[
  {
    "path": ".gitignore",
    "content": "######################################################################\n# Build Tools\n\n.gradle\n/build/\n!gradle/wrapper/gradle-wrapper.jar\n\ntarget/\n!.mvn/wrapper/maven-wrapper.jar\n\n######################################################################\n# IDE\n\n### STS ###\n.apt_generated\n.classpath\n.factorypath\n.project\n.settings\n.springBeans\n\n### IntelliJ IDEA ###\n.idea\n*.iws\n*.iml\n*.ipr\n\n### NetBeans ###\nnbproject/private/\nbuild/*\nnbbuild/\ndist/\nnbdist/\n.nb-gradle/\n\n######################################################################\n# Others\n*.log\n*.xml.versionsBackup\n*.swp\n\n!*/build/*.java\n!*/build/*.html\n!*/build/*.xml\n"
  },
  {
    "path": "LICENSE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entisty authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "README.md",
    "content": "# 格姗导航网站\n\n一个基于 Spring Boot、MyBatis Plus、Vue3、Element Plus 前后端分离的导航网站系统，在线演示地址：[https://gesdh.cn](https://gesdh.cn)。由于工作和个人能力原因，部分技术都是边学习边开发，特别是前端（工作中是后端开发），基本上不熟悉，代码质量和设计，请大家多多指教\n\n欢迎加群一起学习交流技术。\n\nQQ 群交流：673465233\n\n微信群：添加我微信：geshanintell，备注：格姗导航\n\n码云下载地址（国内推荐）：[https://gitee.com/geshanzsq/geshanzsq-nav](https://gitee.com/geshanzsq/geshanzsq-nav)\n\nGithub 下载地址：[https://github.com/geshanzsq/geshanzsq-nav](https://github.com/geshanzsq/geshanzsq-nav)\n\n## 简介\n\n本项目是一个网站导航，网站内容均由[小格子](https://geshanzsq.com/)本人收集并整理。前端基于 Vue3，后端使用 Spring Boot。目前只是基础版，没有开源自定义导航，大家可以根据此开源项目进行自定义导航开发。如果对自定义导航感兴趣，可联系小格子购买自定义导航源码\n\n- 前端采用 Vue3、Element Plus。\n- 后端采用 Spring Boot、MyBatis Plus、Spring Security、Redis。\n- 后台管理支持加载动态权限菜单，权限修改立即生效，不用再退出重新登录。\n- 高效率开发，只需要简单的 @Query 注解即可实现分页和列表接口。\n\n## 内置功能\n\n1. 用户管理：用户是整个系统操作人，主要完成系统用户配置\n2. 角色管理：配置角色菜单、分配用户角色等。\n3. 菜单管理：配置系统菜单、按钮权限标识、关联 API 等。\n4. 数据字典：系统中经常使用的一些较为固定的数据进行维护。\n5. API 管理：后端所有接口地址、请求方式等。\n6. 参数配置：系统动态配置常用参数。\n7. 登录日志：系统登录日志记录查询。\n8. 操作日志：系统操作日志记录和查询。\n9. 导航管理：分类管理、网站管理、评论管理\n\n## 运行\n\n前端：需要搭建 Vue 脚手架环境，如：安装 Node.js、Vue-cli 。\n\n后端：需要搭建 Java 开发环境。此外，还需要安装 MySQL 数据库（推荐 MySQL 8）、Redis。\n\n#### 相关环境版本：\n\n| 软件名称 | 版本号    |\n| -------- | --------- |\n| Node     | 16.16.0   |\n| NPM      | 6.14.15   |\n| JDK      | 1.8.0_202 |\n| MySQL    | 8.0.17    |\n| Redis    | 5.0.9     |\n| Nginx    | 1.9.9     |\n\n### 项目目录结构说明\n\n```yml\ngeshanzsq-nav-admin: 后台管理模块\n    geshanzsq-nav-admin-application: 后台管理应用启动和导航业务\n    geshanzsq-nav-admin-system: 后台管理的系统管理模块\ngeshanzsq-nav-common: 通用模块\n    geshanzsq-nav-common-core: 核心通用模块，包含一些通用工具类\n    geshanzsq-nav-common-framework: 通用框架模块\n    geshanzsq-nav-common-rate-limiter: 通用限流模块\n    geshanzsq-nav-common-log: 通用日志模块\n    geshanzsq-nav-common-redis: 通用缓存模块\n    geshanzsq-nav-common-security: 通用安全框架模块\n    geshanzsq-nav-common-swagger: 通用接口文档模块\nvue-geshanzsq-nav: 前端模块\n```\n\n#### 前端运行\n\n```bash\n# 进入项目目录\ncd vue-geshanzsq-nav\n\n# 安装依赖\nnpm install\n\n# 建议不要直接使用 cnpm 安装依赖，会有各种诡异的 bug。可以通过如下操作解决 npm 下载速度慢的问题\nnpm install --registry=https://registry.npm.taobao.org\n\n# 启动服务\nnpm run serve\n```\n\n浏览器访问 [http://127.0.0.1:8023](http://127.0.0.1:8023)\n\n#### 后端运行\n\n新建数据库，导入 `sql/geshanzsq_nav.sql ` 数据库脚本\n\n在 `geshanzsq-nav/geshanzsq-nav-admin/geshanzsq-nav-admin-application`应用启动模块下，有三个配置文件：\n\n| application.yml            | application-dev.yml                               | application-prod.yml                              |\n| -------------------------- | ------------------------------------------------- | ------------------------------------------------- |\n| 主要配置文件，配置公共信息 | 开发环境，配置 MySQL 数据库、Redis 缓存、文件路径 | 生产环境，配置 MySQL 数据库、Redis 缓存、文件路径 |\n\n上述配置完成后，启动后台管理 `geshanzsq-nav-admin-application` 应用\n\n在开发环境的 application-dev.yml 配置文件中，配置 MySQL 数据库连接信息、Redis 缓存、网站初始化图片。\n\n![导航网站-数据库配置信息](https://geshanzsq.com/geshanzsq-file/profile/image/2023/05/22/02de4f3e-7e5c-4370-acea-3701c243759d.png)\n\n![导航网站-文件上传配置信息](https://geshanzsq.com/geshanzsq-file/profile/image/2023/05/22/a514cec3-084d-41b2-ab64-1585c962a8ba.png)\n\n**注：** 需要把目录【网站图片】下的 profile.zip 解压到配置文件的文件路径中，否则内置的网站数据加载不出图片。比如我的是：`D:/data/geshanzsq-nav/profile`，解压后的文件路径如图所示，如果网站图片加载失败，请检查图片路径是否正确。\n\n![导航网站-图片路径](https://geshanzsq.com/geshanzsq-file/profile/image/2023/05/22/cd09da46-c2a8-476e-908c-ae75998c7ef9.png)\n\n##### 生产环境\n\n在 application.yml 配置文件中，把 dev 改为 prod；并在 application-prod.yml 配置对应的 MySQL 数据库、Redis 环境、文件路径。\n\n### 后端技术\n\n| 技术            | 说明                       | 官网链接                                                     |\n| --------------- | -------------------------- | ------------------------------------------------------------ |\n| Spring Boot     | MVC 框架                   | [https://spring.io/projects/spring-boot](https://spring.io/projects/spring-boot) |\n| Spring Security | 认证和授权安全框架         | [https://spring.io/projects/spring-security](https://spring.io/projects/spring-security) |\n| MyBatis Plus    | ORM 框架                   | [https://mp.baomidou.com](https://mp.baomidou.com)           |\n| Knife4j         | 接口文档管理框架           | [https://doc.xiaominfo.com](https://doc.xiaominfo.com)       |\n| Redis           | 缓存框架                   | [https://redis.io](https://redis.io)                         |\n| Lombok          | 对象封装工具               | [https://github.com/projectlombok/lombok](https://github.com/projectlombok/lombok) |\n| Nginx           | Http 和反向代理 Web 服务器 | [http://nginx.org](http://nginx.org)                         |\n\n### 前端技术\n\n| 说明                   | 官网                                                         |\n| ---------------------- | ------------------------------------------------------------ |\n| 前端框架               | [https://vuejs.org](https://vuejs.org)                       |\n| 路由框架               | [https://router.vuejs.org](https://router.vuejs.org)         |\n| 全局状态管理框架       | [https://vuex.vuejs.org](https://vuex.vuejs.org)             |\n| 前端 Element Plus 框架 | [https://element-plus.gitee.io](https://element-plus.gitee.io) |\n| 前端 Http 框架         | [https://github.com/axios/axios](https://github.com/axios/axios) |\n| 富文本编辑器           | [https://www.wangeditor.com](https://www.wangeditor.com)     |\n| 代码语法高亮插件       | [https://github.com/highlightjs/highlight.js](https://github.com/highlightjs/highlight.js) |\n\n## 使用\n\n导航首页：[http://127.0.0.1:8023](http://127.0.0.1:8023)\n\n登录地址：[http://127.0.0.1:8023/login](http://127.0.0.1:8023.login)\n\n默认用户：admin\n\n默认密码：123456\n\n## 演示图\n\n![导航网站-首页.png](https://geshanzsq.com/geshanzsq-file/profile/image/2023/05/22/32f77495-f788-4889-acb1-417760f89f4c.png)\n\n![导航网站-首页-格式转换.png](https://geshanzsq.com/geshanzsq-file/profile/image/2023/05/22/558ab7c3-3703-43c9-af3b-fd7ab00b57aa.png)\n\n![导航网站-分类管理.png](https://geshanzsq.com/geshanzsq-file/profile/image/2023/05/22/84a02a68-5b1e-4429-9f5f-f09d8c4f00bc.png)\n\n![导航网站-网站管理.png](https://geshanzsq.com/geshanzsq-file/profile/image/2023/05/22/d9f336e5-1454-459e-9431-3d50fa582e13.png)\n\n![导航网站-网站配置.png](https://geshanzsq.com/geshanzsq-file/profile/image/2023/05/22/3efd39b9-6cc5-453e-9a64-e07e6f8ad1de.png)\n\n![导航网站-接口文档.png](https://geshanzsq.com/geshanzsq-file/profile/image/2023/05/22/b001cf3c-b053-45b0-b84f-d6dac1ae878b.png)\n\n## 项目部署\n\n最近有挺多小伙伴不知道如何部署到服务器，小格子给大家简单的介绍一下。\n\n### 安装相关环境\n\n首先需要有一台服务器（建议 Linux 系统），安装 JDK1.8、MySQL 数据库、Redis 缓存、Nginx 代理。不会安装的请看这几篇文章。\n\n[Linux 安装 MySQL 8 数据库](https://geshanzsq.com/article/34)\n\n[Linux 安装 Nginx 代理](https://geshanzsq.com/article/65)\n\n前端打包后会生成 dist，上传到服务器对应目录。打包命令：\n\n```shell\nnpm run build\n```\n\n后端打包为 Jar 包，上传到服务器对应目录并执行下面命令：\n\n```shell\nnohup java -jar geshanzsq-nav-admin.jar &\n```\n\n### Nginx 代理配置\n\n为了保证前端和后端不存在跨域问题，需要部署在 Nginx 进行配置：\n\n```nginx\nserver {\n    listen       80;\n    server_name  localhost;\n    \n    # 前端项目配置\n    location / {\n        # root 后面为项目存在目录\n        root /data/project/geshanzsq-nav/dist;\n        index index.html index.htm;\n        try_files $uri $uri/ /index.html;\n   }\n    \n   # 后端项目配置\n   location /geshanzsq-nav-api/ {\n        # 项目 IP 地址和端口，如果不在一台服务器，请填写对应的 IP；如果后端端口修改后，请填写修改后的端口\n        proxy_pass http://127.0.0.1:8083/geshanzsq-nav-api/;\n        proxy_set_header Host $host;\n        proxy_set_header X-Real-IP $remote_addr;\n        proxy_set_header REMOTE-HOST $remote_addr;\n        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n  } \n}\n\n```\n\n### Docker 部署\n现在流行 Docker 部署，有很多小伙伴叫小格子整理一下 Docker 部署，下面以 Linux 系统为例，给大家简单的说明\n#### Docker 安装\n网上有很多安装教程，大家自行安装即可\n#### 项目启动\n把 docker 目录压缩上传到服务器并解压\n1. `docker/project/file/profile/profile.zip` 图片文件解压\n2. 如果修改了前端代码，自行打包 `dist.zip` 到 `docker/frontend/dist` 目录并解压\n3. 如果修改了后端代码，自行打包 `geshanzsq-nav-admin-application.jar` 到 `docker/project/backend`\n4. `docker` 目录提供了一键启动脚本 `start.sh`，执行 `sh start.sh` 可直接安装并启动 mysql、redis、nginx、后端、前端。同时提供了单独启动脚本，每个目录均有 `start.sh` 脚本。\n\n## License\n\nCopyright © 2020-2023 **[格姗导航](https://gesdh.cn)** Released under the **Apache-2.0**.\n\n> 注：本导航开源的目的是大家能够在本站的基础之上有所启发，做出更多新的东西，比如自定义导航。并不是让大家照搬所有代码。 如果你使用这个开源项目，请**注明**本项目开源地址。\n\n## 参与贡献\n\n开源项目离不开大家的支持与贡献，如果您有好的想法，或者遇到一些 **BUG** 并修复了，欢迎小伙伴们提交 **Pull Request** 参与开源贡献。代码贡献步骤：\n\n1. **fork** 本项目到自己的 **repo**\n2. 把 **fork** 过去的项目也就是你仓库中的项目 **clone** 到你的本地\n3. 修改代码\n4. **commit** 后 **push** 到自己的库\n5. 发起**PR**（ **pull request**） 请求，提交到 **master** 分支\n6. 等待小格子合并\n\n## 自定义导航\n\n自定义导航源码限时优惠中，功能强大，支持自定义导航、分享、第三方账号登录、自定义顶部链接、主题配置、简约版、访问记录、评论管理、分享管理、友情链接、网站配置、登录配置等功能，有需要的可联系小格子，下面是部分页面截图。\n\n![自定义导航-首页.png](https://geshanzsq.com/geshanzsq-file/profile/image/2023/07/12/531a07f7-5590-4f15-bf33-d2236d6c5e41.png)\n\n![自定义导航-主题设置.png](https://geshanzsq.com/geshanzsq-file/profile/image/2023/07/12/387ab999-8af1-44f3-b6a8-04c640444bb7.png)\n\n![自定义导航-后台首页.png](https://geshanzsq.com/geshanzsq-file/profile/image/2023/07/12/9fd1c241-903c-450d-8d32-7da3f7a8898c.png)\n\n![自定义导航-访问统计.png](https://geshanzsq.com/geshanzsq-file/profile/image/2023/07/12/9db24b35-293d-4d80-8ca4-b940bb182b7b.png)\n\n![自定义导航-分享管理.png](https://geshanzsq.com/geshanzsq-file/profile/image/2023/07/12/4c87c459-c267-4899-a8a7-d356af6e91af.png)\n\n![自定义导航-顶部配置.png](https://geshanzsq.com/geshanzsq-file/profile/image/2023/07/12/bb3f5616-05d1-4fd1-a86e-d736b5750829.png)\n\n![自定义导航-登录配置.png](https://geshanzsq.com/geshanzsq-file/profile/image/2023/07/12/41c053a7-d25f-4d07-ae24-9f664a1c464c.png)\n\n![自定义导航-网站配置.png](https://geshanzsq.com/geshanzsq-file/profile/image/2023/07/12/aad69891-5744-4f3e-8dd4-87a904d71135.png)\n\n## 感谢\n\n若依后台管理系统：[RuoYi-Vue](https://gitee.com/y_project/RuoYi-Vue)\n\n蘑菇博客：[mogu_blog](https://gitee.com/moxi159753/mogu_blog_v2)\n"
  },
  {
    "path": "docker/mysql/conf.d/my.cnf",
    "content": "###### [client]配置模块 ######\n[client]\ndefault-character-set=utf8mb4\nsocket=/var/lib/mysql/mysql.sock\n\n###### [mysql]配置模块 ######\n[mysql]\n# 设置MySQL客户端默认字符集\ndefault-character-set=utf8mb4\nsocket=/var/lib/mysql/mysql.sock\n\n###### [mysqld]配置模块 ######\n[mysqld]\nport=3306\nuser=mysql\n# 设置sql模式 sql_mode模式引起的分组查询出现*this is incompatible with sql_mode=only_full_group_by，这里最好剔除ONLY_FULL_GROUP_BY\nsql_mode=STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION\ndatadir=/var/lib/mysql\nsocket=/var/lib/mysql/mysql.sock\nserver-id = 1\n\n# MySQL8 的密码认证插件 如果不设置低版本navicat无法连接\ndefault_authentication_plugin=mysql_native_password\n\n# 禁用符号链接以防止各种安全风险\nsymbolic-links=0\n\n# 允许最大连接数\nmax_connections=1000\n\n# 服务端使用的字符集默认为8比特编码的latin1字符集\ncharacter-set-server=utf8mb4\n\n# 创建新表时将使用的默认存储引擎\ndefault-storage-engine=INNODB\n\n# 0: 表名将按指定方式存储，并且比较区分大小写;\n# 1: 表名以小写形式存储在磁盘上，比较不区分大小写；\nlower_case_table_names=0\n\nmax_allowed_packet=16M \n\n# 设置时区\ndefault-time_zone='+8:00'\n\n"
  },
  {
    "path": "docker/mysql/docker-compose.yml",
    "content": "version: '3'\nservices:\n  mysql: # 服务名称\n    image: mysql:8.0.18 # 或其它mysql版本\n    container_name: mysql8 # 容器名称\n    environment:\n      - MYSQL_ROOT_PASSWORD=gesdh.cn@MySQL8 # root用户密码\n      - MYSQL_DATABASE=geshanzsq_nav  # 自动创建数据库\n      - MYSQL_USER=geshanzsq_nav    # 自动创建用户\n      - MYSQL_PASSWORD=gesdh.cn@MySQL8 # 用户密码\n#      - TZ=Asia/Shanghai # 设置容器时区 我这里通过下面挂载方式同步的宿主机时区和时间了,这里忽略\n    volumes:\n      - ./log:/var/log/mysql # 映射日志目录，宿主机:容器\n      - ./data:/var/lib/mysql # 映射数据目录，宿主机:容器\n      - ./conf.d:/etc/mysql/conf.d # 映射配置目录，宿主机:容器\n      - ./geshanzsq_nav.sql:/docker-entrypoint-initdb.d/geshanzsq_nav.sql # 挂载SQL文件到初始化目录\n      - /etc/localtime:/etc/localtime:ro # 让容器的时钟与宿主机时钟同步，避免时间的问题，ro是read only的意思，就是只读。\n    ports:\n      - 3306:3306 # 指定宿主机端口与容器端口映射关系，宿主机:容器\n    restart: always # 容器随docker启动自启\n"
  },
  {
    "path": "docker/mysql/geshanzsq_nav.sql",
    "content": "/*\n Navicat Premium Data Transfer\n\n Source Server         : localhost\n Source Server Type    : MySQL\n Source Server Version : 80017 (8.0.17)\n Source Host           : localhost:3306\n Source Schema         : geshanzsq_nav\n\n Target Server Type    : MySQL\n Target Server Version : 80017 (8.0.17)\n File Encoding         : 65001\n\n Date: 22/05/2023 20:09:21\n*/\n\nSET NAMES utf8mb4;\nSET FOREIGN_KEY_CHECKS = 0;\n\n-- ----------------------------\n-- Table structure for log_login\n-- ----------------------------\nDROP TABLE IF EXISTS `log_login`;\nCREATE TABLE `log_login`  (\n  `id` bigint(20) NOT NULL COMMENT '登录日志 id',\n  `username` varchar(50) NULL DEFAULT '' COMMENT '登录用户名',\n  `fk_user_id` bigint(20) NOT NULL COMMENT '用户 id',\n  `ip_address` varchar(200) NULL DEFAULT NULL COMMENT '登录 ip 地址',\n  `login_location` varchar(255) NULL DEFAULT '' COMMENT '登录位置',\n  `browser_type` varchar(50) NULL DEFAULT '' COMMENT '浏览器类型',\n  `operate_system` varchar(50) NULL DEFAULT '' COMMENT '操作系统',\n  `status` tinyint(1) NULL DEFAULT 1 COMMENT '登录状态（1 成功，2 失败）',\n  `hint_message` varchar(255) NULL DEFAULT '' COMMENT '提示消息',\n  `gmt_login` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '登录时间',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB COMMENT = '登录日志' ROW_FORMAT = DYNAMIC;\n\n-- ----------------------------\n-- Records of log_login\n-- ----------------------------\n\n-- ----------------------------\n-- Table structure for log_operation\n-- ----------------------------\nDROP TABLE IF EXISTS `log_operation`;\nCREATE TABLE `log_operation`  (\n  `id` bigint(20) NOT NULL COMMENT '操作日志 id',\n  `module_name` varchar(50) NULL DEFAULT '' COMMENT '模块名称',\n  `business_type` tinyint(1) NULL DEFAULT 1 COMMENT '业务类型（1 其它，2 新增，3 修改，4 删除）',\n  `operate_type` tinyint(1) NULL DEFAULT 1 COMMENT '操作类型（1 其它，2 后台用户，3 手机端用户，4 博客用户）',\n  `fk_user_id` bigint(20) NULL DEFAULT NULL COMMENT '操作用户 id',\n  `request_method` varchar(10) NULL DEFAULT '' COMMENT '请求方式',\n  `class_method` varchar(255) NULL DEFAULT '' COMMENT '类方法',\n  `request_url` varchar(255) NULL DEFAULT '' COMMENT '请求地址',\n  `ip_address` varchar(200) NULL DEFAULT '' COMMENT '操作 ip 地址',\n  `operate_location` varchar(255) NULL DEFAULT '' COMMENT '操作位置',\n  `request_param` longtext NULL COMMENT '请求参数',\n  `return_result` longtext NULL COMMENT '返回结果',\n  `status` tinyint(1) NULL DEFAULT 1 COMMENT '登录状态（1 成功，2 异常）',\n  `gmt_operate` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '操作时间',\n  `error_message` longtext NULL COMMENT '错误消息',\n  `browser_type` varchar(50) NULL DEFAULT '' COMMENT '浏览器类型',\n  `operate_system` varchar(50) NULL DEFAULT '' COMMENT '操作系统',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB COMMENT = '操作日志' ROW_FORMAT = DYNAMIC;\n\n-- ----------------------------\n-- Records of log_operation\n-- ----------------------------\n\n-- ----------------------------\n-- Table structure for nav_category\n-- ----------------------------\nDROP TABLE IF EXISTS `nav_category`;\nCREATE TABLE `nav_category`  (\n  `id` bigint(20) NOT NULL COMMENT '分类 id',\n  `parent_id` bigint(20) NOT NULL DEFAULT 0 COMMENT '父级id',\n  `sort` int(11) NULL DEFAULT 0 COMMENT '排序',\n  `category_name` varchar(50) NULL DEFAULT '' COMMENT '分类名称',\n  `category_icon` varchar(20) NULL DEFAULT '' COMMENT '分类图标',\n  `gmt_create` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `gmt_modify` datetime NULL DEFAULT NULL COMMENT '修改时间',\n  `fk_create_user_id` bigint(20) NULL DEFAULT NULL COMMENT '创建人用户 id',\n  `fk_modify_user_id` bigint(20) NULL DEFAULT NULL COMMENT '修改人用户 id',\n  `status` tinyint(1) NULL DEFAULT 1 COMMENT '状态（1 正常，2 停用）',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB COMMENT = '导航分类' ROW_FORMAT = Dynamic;\n\n-- ----------------------------\n-- Records of nav_category\n-- ----------------------------\nINSERT INTO `nav_category` VALUES (150725316141100103, 0, 30, '资源搜索', 'search', '2020-07-10 23:58:54', '2021-03-27 00:27:58', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141100104, 0, 40, '在线工具', 'cogs', '2020-07-25 19:39:39', '2021-03-27 00:27:49', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141100105, 0, 60, '学习教程', 'study', '2020-07-25 19:39:39', '2021-03-27 00:27:30', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141100106, 0, 60, '软件下载', 'software', '2020-07-25 19:39:39', '2021-03-27 00:27:24', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141100107, 0, 70, '休闲娱乐', 'music', '2020-07-25 19:39:39', '2021-03-27 00:27:21', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141100108, 0, 80, '灵感采集', 'Light-Bulb', '2020-07-25 21:41:31', '2021-03-27 00:27:17', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141100109, 0, 90, '素材资源', 'international', '2020-07-25 21:41:31', '2021-03-27 00:27:13', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141101001, 150725316141100103, 1, '网盘搜索', 'chart', '2020-07-11 00:02:09', '2020-07-11 11:35:42', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141101002, 150725316141100103, 2, '电子书搜索', 'code', '2020-07-11 10:46:40', '2020-07-11 11:35:46', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141101004, 0, 1, '格式转换', 'lock', '2020-07-25 21:30:22', '2021-03-27 00:22:23', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141101005, 150725316141100104, 2, '语言翻译', 'component', '2020-07-25 21:30:22', '2020-07-25 21:31:18', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141101006, 0, 3, '图标素材', 'component', '2020-07-25 21:30:22', '2021-03-27 00:28:42', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141101007, 150725316141100105, 1, '博客网站', 'wechat', '2020-07-25 21:43:34', NULL, 1, NULL, 1);\nINSERT INTO `nav_category` VALUES (150725316141101008, 150725316141100106, 1, 'Windows', 'tool', '2020-07-25 21:44:10', NULL, 1, NULL, 1);\nINSERT INTO `nav_category` VALUES (150725316141101009, 150725316141100106, 2, 'Android', 'upload', '2020-07-25 21:44:36', NULL, 1, NULL, 1);\nINSERT INTO `nav_category` VALUES (150725316141101010, 150725316141100106, 3, 'Apple|Mac', 'time', '2020-07-25 21:44:52', '2020-08-18 22:46:22', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141101011, 150725316141100107, 1, '影视', 'icon', '2020-07-25 21:45:44', NULL, 1, NULL, 1);\nINSERT INTO `nav_category` VALUES (150725316141101012, 150725316141100107, 2, '音乐', 'people', '2020-07-25 21:45:58', NULL, 1, NULL, 1);\nINSERT INTO `nav_category` VALUES (150725316141101013, 150725316141100104, 3, '图形创意', 'dict', '2020-08-01 15:06:54', NULL, 1, NULL, 1);\nINSERT INTO `nav_category` VALUES (150725316141101014, 150725316141100104, 4, '界面设计', 'select', '2020-08-01 15:08:33', '2023-05-07 18:24:41', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141101015, 150725316141100104, 5, '交互动效', 'tool', '2020-08-01 15:13:19', '2023-05-07 18:24:51', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141101016, 150725316141100104, 6, '在线配色', 'time', '2020-08-01 15:15:46', '2023-05-07 18:25:00', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141101017, 150725316141100104, 7, '其他工具', 'tag', '2020-08-01 15:15:58', '2023-05-07 18:25:09', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141101018, 150725316141100104, 8, 'Chrome插件', 'clipboard', '2020-08-01 15:16:14', '2023-05-07 18:25:19', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141101019, 150725316141100108, 1, '发现产品', 'build', '2020-08-01 15:23:57', '2023-05-07 18:28:31', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141101020, 150725316141100108, 2, '界面灵感', 'clipboard', '2020-08-01 15:24:11', '2023-05-07 18:28:40', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141101021, 150725316141100108, 3, '网页灵感', 'druid', '2020-08-01 15:24:34', '2023-05-07 18:28:48', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141101022, 150725316141100109, 2, 'LOGO设计', 'clipboard', '2020-08-01 15:35:28', '2023-05-07 18:29:10', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141101023, 150725316141100109, 3, '平面素材', 'drag', '2020-08-01 15:35:55', '2023-05-07 18:29:19', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141101024, 150725316141100109, 4, 'UI资源', 'example', '2020-08-01 15:36:05', '2023-05-07 18:29:26', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141101025, 150725316141100109, 5, 'Sketch资源', 'form', '2020-08-01 15:36:17', '2023-05-07 18:29:31', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141101026, 150725316141100109, 6, '字体资源', 'international', '2020-08-01 15:36:40', '2023-05-07 18:29:36', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141101027, 150725316141100109, 7, 'Mockup', 'monitor', '2020-08-01 15:37:03', '2023-05-07 18:29:44', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141101028, 150725316141100109, 8, '摄影图库', 'post', '2020-08-01 15:37:24', '2023-05-07 18:29:50', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141101029, 0, 10, 'PPT资源', 'theme', '2020-08-01 15:37:35', '2021-03-27 00:28:21', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141101030, 150725316141100105, 4, '后端学习', 'textarea', '2020-08-08 15:06:09', '2023-05-07 18:26:50', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141101031, 150725316141100105, 3, '前端学习', 'rate', '2020-08-08 15:07:26', '2023-05-07 18:27:03', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141101032, 150725316141100105, 2, '个人博客', 'Light-Bulb', '2020-08-08 15:19:13', '2023-05-07 18:27:13', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141101034, 0, 100, '自媒体', 'select', '2020-08-11 17:44:07', '2021-03-27 00:27:08', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141101035, 150725316141101034, 1, '运营平台', 'theme', '2020-08-11 17:44:31', '2023-05-07 18:30:22', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141101036, 0, 2, '图片素材', 'clipboard', '2020-08-11 20:12:23', '2021-03-27 00:30:29', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141101037, 0, 12, '桌面壁纸', 'computer', '2020-08-19 19:39:19', '2021-03-27 00:29:55', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141101039, 150725316141101034, 2, '排版工具', 'shopping', '2020-08-20 11:02:52', '2023-05-07 18:30:31', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141101040, 150725316141100104, 9, 'Github加速下载', 'chart', '2020-08-31 22:51:30', '2023-05-07 18:25:28', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141101042, 150725316141100105, 5, '开源项目', 'article', '2020-09-16 14:37:04', '2023-05-07 18:27:22', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141101043, 150725316141100104, 11, '电视直播', 'online', '2020-09-20 14:54:03', '2023-05-07 18:25:45', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141101046, 150725316141100106, 0, '软件博客', 'software', '2020-10-16 21:41:47', '2023-05-07 18:22:23', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141101047, 150725316141100107, 3, '游戏资源', 'menu', '2020-10-16 22:59:20', '2023-05-07 18:28:20', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141101048, 150725316141101034, 4, '视频制作', 'movie', '2021-01-07 19:56:03', '2023-05-07 18:30:39', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141101049, 150725316141100104, 12, '免费图床', 'excel', '2021-01-12 16:28:22', '2023-05-07 18:26:00', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141101050, 150725316141100104, 13, '办公协作', 'swagger', '2021-01-18 10:27:16', '2023-05-07 18:26:22', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141101051, 150725316141101034, 5, '数据分析', 'dict', '2021-02-01 14:34:50', '2023-05-07 18:30:45', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141101052, 0, 0, '热门导航', 'cc-thumbs-up', '2021-03-27 00:34:10', NULL, 1, NULL, 1);\n\n-- ----------------------------\n-- Table structure for nav_comment\n-- ----------------------------\nDROP TABLE IF EXISTS `nav_comment`;\nCREATE TABLE `nav_comment`  (\n  `id` bigint(20) NOT NULL COMMENT '评论 id',\n  `parent_id` bigint(20) NULL DEFAULT 0 COMMENT '上级 id',\n  `has_sticky` tinyint(1) NULL DEFAULT 2 COMMENT '是否置顶（1 是，2 否） ',\n  `comment_content` longtext NOT NULL COMMENT '评论内容',\n  `nick_name` varchar(30) NULL DEFAULT '' COMMENT '昵称',\n  `email` varchar(50) NULL DEFAULT '' COMMENT '邮箱',\n  `remark` varchar(255) NULL DEFAULT '' COMMENT '备注',\n  `status` tinyint(1) NULL DEFAULT 1 COMMENT '状态（1 待审核，2 通过，3 驳回）',\n  `fk_create_user_id` bigint(20) NULL DEFAULT NULL COMMENT '创建人用户 id',\n  `gmt_create` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `fk_modify_user_id` bigint(20) NULL DEFAULT NULL COMMENT '修改人用户 id',\n  `gmt_modify` datetime NULL DEFAULT NULL COMMENT '修改时间',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB COMMENT = '评论表' ROW_FORMAT = DYNAMIC;\n\n-- ----------------------------\n-- Records of nav_comment\n-- ----------------------------\n\n-- ----------------------------\n-- Table structure for nav_picture\n-- ----------------------------\nDROP TABLE IF EXISTS `nav_picture`;\nCREATE TABLE `nav_picture`  (\n  `id` bigint(20) NOT NULL COMMENT '图片 id',\n  `picture_original_name` varchar(200) NULL DEFAULT '' COMMENT '图片原名称',\n  `picture_path` varchar(500) NULL DEFAULT '' COMMENT '图片路径',\n  `picture_md5` varchar(100) NULL DEFAULT '' COMMENT '图片 md5',\n  `fk_create_user_id` bigint(20) NULL DEFAULT NULL COMMENT '创建人用户 id',\n  `gmt_create` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `fk_modify_user_id` bigint(20) NULL DEFAULT NULL COMMENT '修改人用户 id',\n  `gmt_modify` datetime NULL DEFAULT NULL COMMENT '修改时间',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB COMMENT = '导航图片' ROW_FORMAT = DYNAMIC;\n\n-- ----------------------------\n-- Records of nav_picture\n-- ----------------------------\n\n-- ----------------------------\n-- Table structure for nav_site\n-- ----------------------------\nDROP TABLE IF EXISTS `nav_site`;\nCREATE TABLE `nav_site`  (\n  `id` bigint(20) NOT NULL COMMENT '网站 id',\n  `fk_category_id` bigint(20) NULL DEFAULT NULL COMMENT '分类 id',\n  `site_name` varchar(50) NULL DEFAULT NULL COMMENT '网站名称',\n  `site_path` varchar(100) NULL DEFAULT NULL COMMENT '网站图片路径',\n  `site_description` varchar(300) NULL DEFAULT NULL COMMENT '网站描述',\n  `site_url` varchar(300) NULL DEFAULT NULL COMMENT '网站地址',\n  `sort` int(11) NULL DEFAULT 0 COMMENT '排序',\n  `click_count` int(11) NULL DEFAULT 0 COMMENT '点击量',\n  `gmt_create` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `gmt_modify` datetime NULL DEFAULT NULL COMMENT '修改时间',\n  `fk_create_user_id` bigint(20) NULL DEFAULT NULL COMMENT '创建人用户 id',\n  `fk_modify_user_id` bigint(20) NULL DEFAULT NULL COMMENT '修改人用户 id',\n  `status` tinyint(1) NULL DEFAULT 1 COMMENT '状态（1 正常，2 停用）',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB COMMENT = '用户导航网站信息' ROW_FORMAT = Dynamic;\n\n-- ----------------------------\n-- Records of nav_site\n-- ----------------------------\nINSERT INTO `nav_site` VALUES (150725316141100003, 150725316141101001, '小昭来啦', '/profile/site/system/b27e89c97230454302cbb24a115c3577.ico', '网盘搜索', 'https://www.xiaozhaolaila.com', 2, 0, '2020-07-30 22:18:26', NULL, NULL, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100004, 150725316141101001, '小猪快盘', '/profile/site/system/16ad1dd9-810c-44e0-b553-5ee7a155aa35.png', '有你更方便', 'https://www.xiaokesoso.com/', 3, 0, '2020-07-30 22:18:26', '2022-11-07 19:46:34', NULL, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100006, 150725316141101001, '小不点搜索', '/profile/site/system/7f0c4a571333ea397c688b7fc0ba7191.jpg', '好资源一网打尽', 'https://www.xiaoso.net', 5, 0, '2020-07-30 22:18:26', '2020-07-30 23:16:32', NULL, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100007, 150725316141101001, '56网盘搜索', '/profile/site/system/7dc0f592b6ac032ca20e3d29c2daf2dd.ico', '网盘搜索', 'https://www.56wangpan.com', 6, 0, '2020-07-30 22:18:26', '2020-07-30 23:17:37', NULL, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100008, 150725316141101001, '大圣盘', '/profile/site/system/b054e2e190eb787aedcef330c4a78e1e.png', '网盘搜索，就用大圣盘 - 最好用的百度网盘搜索引擎', 'https://www.dashengpan.com', 7, 0, '2020-07-30 22:18:26', '2020-07-30 23:20:10', NULL, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100016, 150725316141101002, '点点文档搜索', '/profile/site/system/a35d2046-f0c9-4a94-b007-79322b05c5a7.jpg', '专注专业文档搜索', 'https://www.torrent.org.cn/bd', 1, 0, '2020-08-01 14:24:18', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100017, 150725316141101002, 'Telegram书籍搜索', '/profile/site/system/d51adf1b-6baa-4aaf-90e7-d444309ce737.png', '免费电子书搜索', 'https://bks.thefuture.top/', 2, 0, '2020-08-01 14:24:18', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100019, 150725316141101002, '鸠摩搜索', '/profile/site/system/69d815f7-ff4f-49ba-b294-535fd1f5ddbf.png', '强大的电子书搜索', 'https://www.jiumodiary.com/', 4, 0, '2020-08-01 14:24:18', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100020, 150725316141101002, 'kindle 漫画', '/profile/site/system/9ff2df64-77bd-4c12-8e6d-92d28ae264df.ico', '漫画', 'https://volmoe.com/', 5, 0, '2020-08-01 14:24:18', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100021, 150725316141101002, '中华珍宝馆', '/profile/site/system/857063b3-ea38-4eb8-8c29-d372ba2740a6.png', '中华珍宝馆', 'https://ltfc.net/', 6, 0, '2020-08-01 14:24:18', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100022, 150725316141101002, '国图文津', '/profile/site/system/c356a6b5-f8f4-4857-9142-732d5defd004.png', '文津搜索', 'http://find.nlc.cn', 7, 0, '2020-08-01 14:24:18', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100023, 150725316141101002, '书格', '/profile/site/system/a60e1981c4c4a14e5469b9c2e8a16cac.png', '搜索资源集', 'https://new.shuge.org/', 8, 0, '2020-08-01 14:24:18', '2020-08-08 14:50:00', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100024, 150725316141101002, '古籍馆', '/profile/site/system/5350f9dd-1818-4b8b-84b3-cf926d5c7cc2.ico', '中國最大的古籍圖書館，在线搜索', 'https://www.gujiguan.com', 9, 0, '2020-08-01 14:24:18', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100025, 150725316141101002, 'Gutenberg', '/profile/site/system/5b2c64cb-a793-4bd9-a69a-e6535cdd36bc.png', ' 无版权书籍在线搜索与下载，外文版', 'http://www.gutenberg.org', 10, 0, '2020-08-01 14:24:18', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100028, 150725316141101002, '书栈网', '/profile/site/system/2e2d77a6-3c44-4de0-a5eb-a6d2b754a731.png', '程序员IT互联网开源编程书籍免费阅读与下载，取之于猿用之于猿', 'https://www.bookstack.cn', 13, 0, '2020-08-01 14:24:18', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100029, 150725316141101002, '码农之家', '/profile/site/system/54f8a778eaaa63550b63e8a051484d43.ico', '计算机电子书下载网-编程pdf电子书下载', 'https://www.xz577.com', 14, 0, '2020-08-01 14:24:18', '2020-08-06 13:53:33', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100030, 150725316141101002, '书享家', '/profile/site/system/6737d6da-43f3-4b67-b528-5aa58e77e86d.png', '电子书下载导航', 'http://shuxiangjia.cn', 15, 0, '2020-08-01 14:24:18', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100031, 150725316141101005, '有道翻译', '/profile/site/system/316e6dde-d4a6-4590-9d0a-e082b40a4876.ico', '免费、及时的多语种在线翻译', 'http://fanyi.youdao.com/', 1, 0, '2020-08-01 14:41:43', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100032, 150725316141101005, 'Google 翻译', '/profile/site/system/c9f49602-6db2-488c-994c-45524e499090.ico', 'Google 在线翻译', 'https://translate.google.cn/', 2, 0, '2020-08-01 14:41:43', '2021-01-13 15:29:39', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100033, 150725316141101005, '百度翻译', '/profile/site/system/b4354f2c-a638-4e25-9269-f572af38318c.jpg', '百度在线翻译', 'https://fanyi.baidu.com/translate', 3, 0, '2020-08-01 14:41:43', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100034, 150725316141101005, '必应翻译', '/profile/site/system/e877f478-17d8-4549-9858-cb5edd5ab4f2.ico', '微软在线翻译', 'https://cn.bing.com/translator', 4, 0, '2020-08-01 14:41:43', '2021-01-13 15:29:46', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100035, 150725316141101008, '腾讯软件中心', '/profile/site/system/3d244575922fd6f3f251cf2326901817.ico', '不错的软件商店', 'https://pc.qq.com/', 1, 0, '2020-08-01 14:48:15', '2020-08-06 13:43:40', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100036, 150725316141101008, 'MSDN', '/profile/site/system/e377b1038d9b1de65faa974cd0f1697d.ico', '原版系统镜像下载', 'https://msdn.itellyou.cn', 2, 0, '2020-08-01 14:48:15', '2020-08-06 13:44:22', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100037, 150725316141101008, '致美化', '/profile/site/system/095e81f7aabe0a02d59e260b5a006181.ico', '桌面美化交流平台', 'https://zhutix.com', 3, 0, '2020-08-01 14:48:15', '2020-08-06 13:45:11', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100038, 150725316141101046, '大眼仔旭', '/profile/site/system/e9b2f20ae9880fb20b98a07a309b96c1.ico', '专注视频剪辑、解压、录屏、思维导图等办公软件分享', 'http://www.dayanzai.me', 2, 0, '2020-08-01 14:48:15', '2020-10-16 21:45:14', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100039, 150725316141101046, '爱绿软', '/profile/site/system/161d313406154438d4319655b0892cd8.png', '收集分享各类有趣好玩的绿色软件', 'https://ilvruan.com', 3, 0, '2020-08-01 14:48:15', '2020-10-16 21:45:18', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100040, 150725316141101046, '异次元软件站', '/profile/site/system/5edb929d595ea22f900244e4985e4cb2.ico', '异次元软件世界 - 软件改变生活', 'https://www.iplaysoft.com', 4, 0, '2020-08-01 14:48:15', '2020-10-16 21:45:22', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100041, 150725316141101046, '吾爱破解', '/profile/site/system/25ef62ba415682c09a4a0a2dea4cbf67.ico', '软件交流社区', 'https://www.52pojie.cn', 0, 0, '2020-08-01 14:48:15', '2020-10-16 21:45:05', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100042, 150725316141101009, '魔趣 ROM', '/profile/site/system/46363edb-dafa-451a-bb0b-74030679e0e1.png', '可能是中国最好的安卓开源系统', 'https://www.mokeedev.com', 1, 0, '2020-08-01 14:49:28', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100043, 150725316141101009, 'Xposed框架中文站', '/profile/site/system/29575a8c-5830-4310-b04c-1ddb978dcb3f.png', '超多Xposed框架模块介绍与下载', 'http://xposed.appkg.com', 2, 0, '2020-08-01 14:49:28', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100044, 150725316141101009, '观道', '/profile/site/system/1b3a7fddd824ac8f01e5c0d400875808.ico', '国外App下载_App下载界的一股清流', 'http://www.guandao.cc', 3, 0, '2020-08-01 14:49:28', '2020-08-06 13:51:36', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100045, 150725316141101009, '豌豆荚', '/profile/site/system/e0ccaa1741577bac575a049479df805e.ico', '海量安卓APP应用与游戏免费下载', 'https://www.wandoujia.com', 4, 0, '2020-08-01 14:49:28', '2020-08-06 13:50:56', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100046, 150725316141101009, 'TapTap', '/profile/site/system/0f03874c92974facb1706e69c54f94d4.png', '发现好游戏', 'https://www.taptap.com', 5, 0, '2020-08-01 14:49:28', '2020-08-06 13:52:24', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100047, 150725316141101010, '腾讯柠檬精选', '/profile/site/system/25206276f20af4103ea56a75be6c0802.png', '收录免费、正版、好用的Mac应用及产品', 'https://lemon.qq.com/lab/', 1, 0, '2020-08-01 14:50:21', '2020-08-06 13:49:34', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100048, 150725316141101010, '每日限免', '/profile/site/system/5d4d23c3-7135-4f82-b946-6c10f3a11f84.png', '每日精品限免_促销应用', 'https://app.so/xianmian', 2, 0, '2020-08-01 14:50:23', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100052, 150725316141101011, '腾讯视频', '/profile/site/system/fe2b466d9fe6bed552c0adcaac1c2813.ico', '中国领先的在线视频媒体平台,海量高清视频在线观看', 'https://v.qq.com', 4, 0, '2020-08-01 14:51:38', '2020-08-06 13:42:24', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100053, 150725316141101011, '爱奇艺', '/profile/site/system/b9555fb5-e19c-4547-a542-eb55e4a6bd72.png', '在线视频网站-海量正版高清视频在线观看', 'https://www.iqiyi.com', 5, 0, '2020-08-01 14:51:38', '2021-01-13 15:21:57', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100054, 150725316141101011, '优酷', '/profile/site/system/e4e0ee80c4466950ae51cecdd28080e1.png', '这世界很酷', 'https://www.youku.com', 6, 0, '2020-08-01 14:51:38', '2020-08-06 13:39:48', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100055, 150725316141101012, '疯狂音乐', '/profile/site/system/e1ace6a5-b279-49a9-8f1e-afb98ab97956.ico', '音乐聚集平台，支持国内大部分音乐平台', 'http://music.ifkdy.com', 1, 0, '2020-08-01 14:52:55', '2021-01-13 15:26:30', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100059, 150725316141101012, 'QQ音乐', '/profile/site/system/6dfcb9895c4d834dac1bc7256a54a9b8.ico', '千万正版音乐海量无损曲库新歌热歌天天畅听的高品质音乐平台', 'https://y.qq.com', 5, 0, '2020-08-01 14:52:55', '2020-08-04 14:48:12', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100060, 150725316141101012, '网易云音乐', '/profile/site/system/21f3ff494c85b7adbd62f6308651e4cf.png', '专注于发现与分享的音乐产品，依托专业音乐人、DJ、好友推荐及社交功能，为用户打造全新的音乐生活', 'https://music.163.com', 6, 0, '2020-08-01 14:52:55', '2020-08-04 14:49:42', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100061, 150725316141101012, '酷狗音乐', '/profile/site/system/0e8a8d31be0727fcd077c9df5e6a9ba9.ico', '就是歌多', 'https://www.kugou.com', 7, 0, '2020-08-01 14:52:55', '2020-08-04 14:52:05', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100062, 150725316141101012, '虾米音乐', '/profile/site/system/3d4c138efe8f0c64cf3a94c17c3344b7.png', '发现音乐新世界', 'https://www.xiami.com', 8, 0, '2020-08-01 14:52:55', '2020-08-05 00:19:33', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100063, 150725316141101004, '在线格式转换', '/profile/site/system/95ed0b48-8dbc-4407-a52a-2541bc16f054.jpg', '万能且免费的格式转换，支持各种格式。', 'https://www.alltoall.net', 1, 0, '2020-08-01 14:54:25', '2020-08-11 23:28:53', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100064, 150725316141101032, '良许', '/profile/site/system/0fcd5b42-a5c6-4023-86e3-97448d7f20a2.ico', 'Linux系统常用命令教学,shell脚本入门学习', 'https://www.lxlinux.net', 9, 0, '2020-08-01 14:56:49', '2021-01-13 15:33:05', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100065, 150725316141101032, '程序猿圈', '/profile/site/system/a198ddc1-8b73-4900-93b4-87fda77fb50f.jpg', '程序员在线学习站点', 'https://www.cxyquan.com/', 2, 0, '2020-08-01 14:56:49', '2020-08-08 15:19:47', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100066, 150725316141101032, '纯洁的微笑', '/profile/site/system/bee2f3c7-4fb8-4b06-8ba3-c678585d5b54.jpg', '分享技术，品味人生', 'http://ityouknow.com/', 3, 0, '2020-08-01 14:56:49', '2020-08-08 15:19:56', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100067, 150725316141101032, '江南一点雨', '/profile/site/system/063a8d1d-1e81-4c0d-9c43-53cc8a873d7a.jpg', '技术文章分享', 'http://www.javaboy.org', 4, 0, '2020-08-01 14:56:49', '2020-08-08 15:20:07', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100068, 150725316141101032, '程序猿DD', '/profile/site/system/9625e95b-8a7b-4548-8e57-5a191b306f2f.jpg', 'Java Spring Boot Spring Cloud 最新干货分享', 'http://blog.didispace.com', 5, 0, '2020-08-01 14:56:49', '2020-08-08 15:20:11', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100069, 150725316141101006, 'Iconfinder', '/profile/site/system/e3325f68179436ccfc25b9f0ffff5a39.png', '2,100,000+个免费和高级矢量图标。', 'https://www.iconfinder.com', 1, 0, '2020-08-01 14:58:10', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100070, 150725316141101006, 'iconfont', '/profile/site/system/e1f63337915f79f8bcad1952adb9f6e1.png', '阿里巴巴矢量图标库。', 'http://www.iconfont.cn/', 2, 0, '2020-08-01 14:58:10', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100071, 150725316141101006, 'iconmonstr', '/profile/site/system/afd4885651455f12dcac4f214460dd99.png', '您的下一个项目的免费简单图标。', 'https://iconmonstr.com/', 3, 0, '2020-08-01 14:58:11', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100072, 150725316141101006, 'FindIcons', '/profile/site/system/0171a46b0f643752aa90aa314a22a546.png', '搜索300,000个免费图标。', 'https://findicons.com/', 4, 0, '2020-08-01 14:58:11', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100073, 150725316141101006, 'Icon Archive', '/profile/site/system/40c43a8932f24370cf456789b2ab51db.png', '搜索590,912个免费图标。', 'http://www.iconarchive.com/', 5, 0, '2020-08-01 14:58:11', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100074, 150725316141101006, 'IcoMoonApp', '/profile/site/system/d19c97ead3760f1b70efa4ee9ad6859c.png', '图标字体，SVG，PDF和PNG生成器。', 'https://icomoon.io/app/', 6, 0, '2020-08-01 14:58:11', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100075, 150725316141101006, 'easyicon', '/profile/site/system/34b4382075e047c6d1456f8fe591a1ef.png', 'PNG、ICO、ICNS格式图标搜索、图标下载服务。', 'http://www.easyicon.net/', 7, 0, '2020-08-01 14:58:11', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100076, 150725316141101006, 'flaticon', '/profile/site/system/582cf7361a0b4f444628c68b98e5cfc7.png', '634,000+免费矢量图标为SVG，PSD，PNG，EPS格式或图标字体。', 'https://www.flaticon.com/', 8, 0, '2020-08-01 14:58:11', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100077, 150725316141101006, 'UICloud', '/profile/site/system/f9840e127d500449da1c5c721f3634c3.png', '世界上最大的用户界面设计数据库。', 'http://ui-cloud.com/', 9, 0, '2020-08-01 14:58:11', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100078, 150725316141101006, 'Material icons', '/profile/site/system/32a037ffbdd2f97f6f6e62e56321c519.png', '可访问900多种材质系统图标，这些图标以各种大小和密度提供，并且可以作为网络字体使用。', 'https://material.io/icons/', 10, 0, '2020-08-01 14:58:11', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100079, 150725316141101006, 'Font Awesome Icon', '/profile/site/system/88440b8b0d5dc43a3f766670e2d11746.png', '惊人的675个图标的完整集合。', 'https://fontawesome.com/', 11, 0, '2020-08-01 14:58:11', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100080, 150725316141101006, 'ion icons', '/profile/site/system/6d0fd0bf35549f6d61037bd86e2ca242.png', 'Ionic Framework的高级图标字体。', 'http://ionicons.com/', 12, 0, '2020-08-01 14:58:11', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100081, 150725316141101006, 'Simpleline Icons', '/profile/site/system/acf446f1af754f863260cc10dd8d546e.png', '简单的线条图标包。', 'http://simplelineicons.com/', 13, 0, '2020-08-01 14:58:11', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100082, 150725316141101013, 'photoshop', '/profile/site/system/e7117e80fdb340589bc8969900e2af61.png', 'Photoshop不需要解释', 'https://www.adobe.com/cn/products/photoshop.html', 1, 0, '2020-08-01 15:07:48', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100083, 150725316141101013, 'Affinity Designer', '/profile/site/system/290add1cdb3cdb80e6e30af137d48525.png', '专业创意软件', 'https://affinity.serif.com/', 2, 0, '2020-08-01 15:07:48', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100084, 150725316141101013, 'Illustrator', '/profile/site/system/6882fdb094820bae95054ea1c38a3baf.png', '矢量图形和插图。', 'https://www.adobe.com/cn/products/illustrator/', 3, 0, '2020-08-01 15:07:48', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100085, 150725316141101013, 'indesign', '/profile/site/system/4db54894b6751e253212a690dada0df8.png', '页面设计、布局和出版。', 'http://www.adobe.com/cn/products/indesign.html', 4, 0, '2020-08-01 15:07:48', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100086, 150725316141101013, 'cinema-4d', '/profile/site/system/ac0344f03fc1e59b4144fef92a12e211.png', 'Cinema 4D是所有想要快速，轻松地获得惊人效果的3D艺术家的理想选择。', 'https://www.maxon.net/en/products/cinema-4d/overview/', 5, 0, '2020-08-01 15:07:49', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100087, 150725316141101013, '3ds-max', '/profile/site/system/3aacac5d23583cdc250a970a0e30a9aa.png', '3D建模，动画和渲染软件', 'https://www.autodesk.com/products/3ds-max/overview', 6, 0, '2020-08-01 15:07:49', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100088, 150725316141101013, 'Blender', '/profile/site/system/5d61addac4350caee364f0a3e850a3f7.png', 'Blender是免费的开源3D创建套件。', 'https://www.blender.org/', 7, 0, '2020-08-01 15:07:49', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100089, 150725316141101014, 'Sketch', '/profile/site/system/75cc5b5775361d5f0b471b706a115403.png', '数字设计工具包', 'https://sketchapp.com/', 1, 0, '2020-08-01 15:09:23', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100090, 150725316141101014, 'Adobe XD', '/profile/site/system/9eda46042e2ad058951fa4e4bb3a9957.png', '引入Adobe XD。 设计。 原型。 经验。', 'http://www.adobe.com/products/xd.html', 2, 0, '2020-08-01 15:09:23', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100091, 150725316141101014, 'invisionapp', '/profile/site/system/7d5620f1b4fd85c4a7ea4733deed8823.png', '强大的设计原型工具', 'https://www.invisionapp.com/', 3, 0, '2020-08-01 15:09:23', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100092, 150725316141101014, 'marvelapp', '/profile/site/system/28ec81158c67d9783afccf8fcd1bbee6.png', '简单的设计，原型制作和协作', 'https://marvelapp.com/', 4, 0, '2020-08-01 15:09:23', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100093, 150725316141101014, 'Muse CC', '/profile/site/system/612cf530fe80b5f28dc826c4384087bf.png', '无需利用编码即可进行网站设计。', 'https://creative.adobe.com/zh-cn/products/download/muse', 5, 0, '2020-08-01 15:09:23', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100094, 150725316141101014, 'figma', '/profile/site/system/0623aab0bc72437206deca9d4c55df1c.png', '使用Figma一站式设计，制作原型并收集反馈。', 'https://www.figma.com/', 6, 0, '2020-08-01 15:09:23', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100095, 150725316141101015, 'Adobe After Effects CC', '/profile/site/system/01306292e590f37b934785ed67288f80.png', '电影般的视觉效果和动态图形。', 'https://www.adobe.com/cn/products/aftereffects/', 1, 0, '2020-08-01 15:14:56', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100096, 150725316141101015, 'principle', '/profile/site/system/485a410f2076ad20856199caa300f548.png', '激发您的想法，设计更好的应用程序', 'http://principleformac.com/', 2, 0, '2020-08-01 15:14:56', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100097, 150725316141101015, 'flinto', '/profile/site/system/f655d57c7d752c429510e0f649d69666.png', 'Flinto是Mac应用程序，世界各地的顶级设计师都使用它来创建其应用程序设计的交互式动画原型。', 'https://www.flinto.com/', 3, 0, '2020-08-01 15:14:56', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100098, 150725316141101015, 'framer', '/profile/site/system/e64c3cd0283a3bf6a75c9c4ba821049d.png', '从详细的图标到高保真的交互，一切都设计在一个地方。', 'https://framer.com/', 4, 0, '2020-08-01 15:14:56', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100099, 150725316141101015, 'ProtoPie', '/profile/site/system/bdbb0e0485d816b88c75c9276d273873.png', '高保真交互原型设计', 'http://www.protopie.cn/', 5, 0, '2020-08-01 15:14:56', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100100, 150725316141101016, 'khroma', '/profile/site/system/8b158c18b49f0160100086bfcdbe158d.png', 'Khroma是发现，搜索和保存要使用的颜色组合的最快方法。', 'http://khroma.co/generator/', 1, 0, '2020-08-01 15:16:58', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100101, 150725316141101016, 'uigradients', '/profile/site/system/9842ff5c221d6411444d3c13660ba097.png', '美丽的彩色渐变', 'https://uigradients.com', 2, 0, '2020-08-01 15:16:58', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100102, 150725316141101016, 'gradients', '/profile/site/system/64d0ac5dc78b65d83ba500df5b1eab30.png', '为设计师和开发人员设计的渐变色', 'http://gradients.io/', 3, 0, '2020-08-01 15:16:58', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100103, 150725316141101016, 'Coolest', '/profile/site/system/9b77eaad5ef27823b9feb3f765b9d593.png', '最酷的精选渐变色为您的下一个超级⚡惊人的东西', 'https://webkul.github.io/coolhue/', 4, 0, '2020-08-01 15:16:58', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100104, 150725316141101016, 'webgradients', '/profile/site/system/49bfc25c217107d7209eea098ad0307c.png', 'WebGradients是180个线性渐变的免费集合，您可以将其用作网站任何部分的内容背景。', 'https://webgradients.com/', 5, 0, '2020-08-01 15:16:58', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100105, 150725316141101016, 'grabient', '/profile/site/system/8ab1a1044ef9bc5c306c60b81d83b0a2.png', '2017 Grabient展开', 'https://www.grabient.com/', 6, 0, '2020-08-01 15:16:58', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100106, 150725316141101016, 'thedayscolor', '/profile/site/system/6e63366cb896fa19e204cf6b95691062.png', '日常色彩摘要', 'http://www.thedayscolor.com/', 7, 0, '2020-08-01 15:16:58', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100107, 150725316141101016, 'flatuicolors', '/profile/site/system/0b6e14ae22ff962a96ad66de4fc86aff.png', '从平面UI主题复制粘贴调色板', 'http://flatuicolors.com/', 8, 0, '2020-08-01 15:16:58', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100108, 150725316141101016, 'coolors', '/profile/site/system/9176968478c5c42ed20bce8b69f25bf6.png', '超快速配色方案生成器！', 'https://coolors.co/', 9, 0, '2020-08-01 15:16:58', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100109, 150725316141101016, 'Adobe Color CC', '/profile/site/system/ff4d69bedb642bd132297ed22018369b.png', '使用色轮创建配色方案或浏览“颜色”社区中的数千种颜色组合。', 'https://color.adobe.com/zh/create/color-wheel', 10, 0, '2020-08-01 15:16:58', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100110, 150725316141101016, 'flatuicolorpicker', '/profile/site/system/2faf82318597d846e9522c5f52500031.png', 'UI设计的最佳平面颜色', 'http://www.flatuicolorpicker.com/', 11, 0, '2020-08-01 15:16:58', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100111, 150725316141101016, 'trianglify', '/profile/site/system/88261a86b35e5b015bbe35ab9141bc8f.png', '三角生成器', 'http://qrohlf.com/trianglify-generator/', 12, 0, '2020-08-01 15:16:58', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100112, 150725316141101016, 'klart', '/profile/site/system/c51065aaec56c7c65aafd40f4797dba0.png', '每周都会为您的收件箱提供漂亮的颜色和设计', 'https://klart.co/colors/', 13, 0, '2020-08-01 15:16:58', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100113, 150725316141101016, 'vanschneider', '/profile/site/system/4690e9281c23d5fc9df0e2cfbe018edd.png', '“颜色声明”由Tobias van Schneider于2012年创建，目标是为我的未来项目收集和组合独特的颜色。', 'http://www.vanschneider.com/colors', 14, 0, '2020-08-01 15:16:58', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100114, 150725316141101017, 'tinypng', '/profile/site/system/9344c4d9769745c1e63d8f1e7b2f3f25.png', '通过质量和文件大小的完美平衡来优化图像。', 'https://tinypng.com/', 1, 0, '2020-08-01 15:17:33', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100115, 150725316141101017, 'Android 9 patch', '/profile/site/system/dc5b75e3455673384a8f738429789d4b.png', 'Android 9补丁阴影生成器完全可自定义阴影', 'http://inloop.github.io/shadow4android/', 2, 0, '2020-08-01 15:17:33', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100116, 150725316141101017, 'screen sizes', '/profile/site/system/d79583290bc400c0e8a2629d0e7f9f63.png', '流行设备的视口大小和像素密度', 'http://screensiz.es/', 3, 0, '2020-08-01 15:17:33', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100117, 150725316141101017, 'svgomg', '/profile/site/system/f573dc81e4689cb9ce482f35a6fb82f1.png', 'SVG在线压缩平台', 'https://jakearchibald.github.io/svgomg/', 4, 0, '2020-08-01 15:17:33', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100118, 150725316141101017, '稿定抠图', '/profile/site/system/0a6b1b1ea1d5ca5cb49e8cf95470a3b5.png', '免费在线抠图软件,图片快速换背景-抠白底图', 'https://www.gaoding.com', 5, 0, '2020-08-01 15:17:33', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100119, 150725316141101017, '中文转换英文首字母', '/profile/site/system/0fcf02f7a6dc850af65961ce29e4b4cd.png', '在线中文转换英文首字母', 'http://tool.geshanzsq.com/chinese', 6, 0, '2020-08-01 15:17:33', '2020-08-07 10:54:12', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100120, 150725316141101017, 'php数组转Json', '/profile/site/system/9a1139692d3d565d5bb15c816548f13b.png', '在线php数组转Json', 'http://tool.geshanzsq.com/phpToJson', 7, 0, '2020-08-01 15:17:33', '2020-08-07 10:53:01', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100121, 150725316141101017, '在线工具', '/profile/site/system/a2143416-fe6d-4cd5-8a58-bc9ab3911a6a.jpg', '程序员的工具箱', 'https://tool.lu', 8, 0, '2020-08-01 15:17:33', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100122, 150725316141101018, 'wappalyzer', '/profile/site/system/60696fcbba523de88eca68121dee7be7.png', '识别网站上的技术', 'https://www.wappalyzer.com/', 1, 0, '2020-08-01 15:19:02', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100123, 150725316141101018, 'Panda', '/profile/site/system/35e1bbf29c1116cb1dbb703b52ea2ae9.png', '专为提高生产力而打造的智能新闻阅读器。', 'http://usepanda.com/', 2, 0, '2020-08-01 15:19:02', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100124, 150725316141101018, 'sizzy', '/profile/site/system/5ca0b65bcc3606640ba1b4aadd25c7df.png', '快速开发响应式网站的工具', 'https://sizzy.co/', 3, 0, '2020-08-01 15:19:02', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100125, 150725316141101018, 'csspeeper', '/profile/site/system/84fedbd61bf8c93726b713bae36a88ae.png', '专为设计师量身定制的智能CSS查看器。', 'https://csspeeper.com/', 4, 0, '2020-08-01 15:19:02', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100126, 150725316141101018, 'insight', '/profile/site/system/fc5a318293079a2674f1d92f3dce7650.png', '云端上类似IDE的代码搜索和导航', 'http://insight.io/', 5, 0, '2020-08-01 15:19:02', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100127, 150725316141101018, 'mustsee', '/profile/site/system/709ff744a41559fa06b8e8dc199206a3.png', '在每个打开的标签页中发现世界上最美丽的地方。', 'http://mustsee.earth/', 6, 0, '2020-08-01 15:19:02', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100128, 150725316141101019, 'Producthunt', '/profile/site/system/a1cc88fa0a3bf74349ba9c08a67abdc7.png', '发现新鲜有趣的产品。', 'https://www.producthunt.com/', 1, 0, '2020-08-01 15:25:50', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100129, 150725316141101019, 'NEXT', '/profile/site/system/0e2b6c9a5afd4f83d2e22632b08f56ef.png', '不错过任何一个新产品。', 'http://next.36kr.com/posts', 2, 0, '2020-08-01 15:25:50', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100130, 150725316141101019, '少数派', '/profile/site/system/2d01ac82bb496b607c43e7b2b29cd069.png', '高品质数字消费指南。', 'https://sspai.com/', 3, 0, '2020-08-01 15:25:51', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100131, 150725316141101019, '利器', '/profile/site/system/98e2ee62a90b6243630f4aa479b4b47b.png', '创造者和他们的工具。', 'http://liqi.io/', 4, 0, '2020-08-01 15:25:51', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100132, 150725316141101019, 'Today', '/profile/site/system/1726189292537c3a2733ebc673b7f1d6.png', '为身边的新产品喝彩。', 'http://today.itjuzi.com/', 5, 0, '2020-08-01 15:25:51', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100133, 150725316141101019, '小众软件', '/profile/site/system/76b49053ce87ab3c7419c7cdf6fa4f07.png', '在这里发现更多有趣的应用。', 'https://faxian.appinn.com', 6, 0, '2020-08-01 15:25:51', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100134, 150725316141101020, 'Pttrns', '/profile/site/system/1c759dc53774e5758a31fee0401e213a.png', '查看最佳的设计模式，资源，移动应用程序和灵感集合', 'https://www.pttrns.com/', 1, 0, '2020-08-01 15:26:33', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100135, 150725316141101020, 'Collect UI', '/profile/site/system/7e802c77c7bb6c85c1e2bb608a4a13cd.png', '从每日ui档案库及其他收集的每日灵感。', 'http://collectui.com/', 2, 0, '2020-08-01 15:26:33', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100136, 150725316141101020, 'UI uigreat', '/profile/site/system/d140fe4bd548f273ddd00f35e1ad5ff5.png', 'APP界面截图参考。', 'http://ui.uigreat.com/', 3, 0, '2020-08-01 15:26:33', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100137, 150725316141101020, 'Android Niceties', '/profile/site/system/8b6e0af7df3a5d77a14e41a0f5f36dc5.png', '屏幕快照集合，其中包含一些最漂亮的Android应用程序。', 'http://androidniceties.tumblr.com/', 4, 0, '2020-08-01 15:26:33', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100138, 150725316141101021, 'Awwwards', '/profile/site/system/bdd6c88417790c97de2c2d0643cc602c.png', '互联网设计、创意和创新奖', 'https://www.awwwards.com/', 1, 0, '2020-08-01 15:27:02', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100139, 150725316141101021, 'CSS Design Awards', '/profile/site/system/481219fe4285f1f4ec1311acce7deb06.png', '网站奖项与灵感-CSS Gallery', 'https://www.cssdesignawards.com/', 2, 0, '2020-08-01 15:27:02', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100140, 150725316141101021, 'The FWA', '/profile/site/system/8fe5280ff7dc3012fb88781dd9ff4b93.png', 'FWA-自2000年以来每天都在展示创新', 'https://thefwa.com/', 3, 0, '2020-08-01 15:27:02', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100141, 150725316141101021, 'Ecommercefolio', '/profile/site/system/950d52c71e28f1c9ed964732d6ed18fd.png', '只有最佳电子商务设计灵感。', 'http://www.ecommercefolio.com/', 4, 0, '2020-08-01 15:27:02', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100142, 150725316141101021, 'Lapa', '/profile/site/system/1824678ec13d01b76df47fc5975178fa.png', '最佳登陆页面设计灵感来自网络。', 'http://www.lapa.ninja/', 5, 0, '2020-08-01 15:27:02', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100143, 150725316141101021, 'Reeoo', '/profile/site/system/5205b768b9b640bfada244ce9d15318d.png', '网页设计灵感和网站库。', 'http://reeoo.com/', 6, 0, '2020-08-01 15:27:02', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100144, 150725316141101021, 'Designmunk', '/profile/site/system/31de409b71235b76d605e98293d68cb3.png', '最佳首页设计灵感。', 'https://designmunk.com/', 7, 0, '2020-08-01 15:27:02', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100145, 150725316141101021, 'Best Websites Gallery', '/profile/site/system/862823249aa701d8bc8af16ae98f1e3a.png', '网站展示灵感| 最佳网站画廊。', 'https://bestwebsite.gallery/', 8, 0, '2020-08-01 15:27:02', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100146, 150725316141101021, 'Pages', '/profile/site/system/90fd20bd3e7ae7c7fe37ea689dcca32c.png', '最佳网页的精选目录。', 'http://www.pages.xyz/', 9, 0, '2020-08-01 15:27:02', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100147, 150725316141101021, 'SiteSee', '/profile/site/system/da24d08a597456be98191b4a08eff4d6.png', '精选的精美，现代网站集的画廊。', 'https://sitesee.co/', 10, 0, '2020-08-01 15:27:02', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100148, 150725316141101021, 'Site Inspire', '/profile/site/system/c15c9017ad6874faae0df64bd969115b.png', '一个CSS画廊和最佳网页设计灵感的展示。', 'https://www.siteinspire.com/', 11, 0, '2020-08-01 15:27:02', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100149, 150725316141101021, 'WebInspiration', '/profile/site/system/f8fe63594e2083755086ee294b036108.png', '网页设计欣赏,全球顶级网页设计。', 'http://web.uedna.com/', 12, 0, '2020-08-01 15:27:02', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100150, 150725316141101021, 'navnav', '/profile/site/system/86b9e596068f2a71d2a2444733a4094e.png', '来自网络的大量CSS，jQuery和JavaScript响应式导航示例，演示和教程。', 'http://navnav.co/', 13, 0, '2020-08-01 15:27:02', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100151, 150725316141101021, 'Really Good UX', '/profile/site/system/948b0f5b62e59ef0a97edf4b9a51c404.png', '屏幕截图库和非常好的UX示例。 带给你的。', 'https://www.reallygoodux.io/', 14, 0, '2020-08-01 15:27:02', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100152, 150725316141101022, 'Dribbble', '/profile/site/system/7db1257f40b9b04482744387a00b359d.png', '全球UI设计师作品分享平台。', 'https://dribbble.com/', 1, 0, '2020-08-01 15:38:39', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100153, 150725316141101022, 'Iconsfeed', '/profile/site/system/aee21da67d9771c2ebf3f6779afc9649.png', 'iOS图标库。', 'http://www.iconsfeed.com/', 2, 0, '2020-08-01 15:38:39', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100154, 150725316141101022, 'iOS Icon Gallery', '/profile/site/system/98c9f52ede06a56532d5d16afda9d570.png', '展示来自iOS App Store的精美图标设计。', 'http://iosicongallery.com/', 3, 0, '2020-08-01 15:38:39', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100155, 150725316141101022, 'World Vector Logo', '/profile/site/system/c8da40d11f726d974293efc40c9acfb5.png', '免费下载品牌徽标。', 'https://worldvectorlogo.com/', 4, 0, '2020-08-01 15:38:40', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100156, 150725316141101022, 'Instant Logo Search', '/profile/site/system/907f35950eae72526a314306cc59efa7.png', '立即搜索和下载数千个徽标。', 'http://instantlogosearch.com/', 5, 0, '2020-08-01 15:38:40', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100157, 150725316141101023, 'freepik', '/profile/site/system/6a96564b2d100bad3674db5e56794a97.png', '超过一百万的免费矢量，PSD，照片和免费图标。', 'https://www.freepik.com/', 1, 0, '2020-08-01 15:39:26', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100158, 150725316141101023, 'wallhalla', '/profile/site/system/9354f99621e8530d0f996fe4b96ad2c3.png', '在一处找到用于台式机和手机的超酷优质壁纸。', 'https://wallhalla.com/', 2, 0, '2020-08-01 15:39:26', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100159, 150725316141101023, '365PSD', '/profile/site/system/13b10a2f9388e83101d7a35b83ff28bc.png', '免费PSD和图形，插图。', 'https://365psd.com/', 3, 0, '2020-08-01 15:39:26', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100160, 150725316141101023, 'Medialoot', '/profile/site/system/b117eb768a44d662ded91d1f0a9cb1c2.png', '免费和高级设计资源-Medialoot。', 'https://medialoot.com/', 4, 0, '2020-08-01 15:39:26', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100161, 150725316141101023, '千图网', '/profile/site/system/9a24027c0e9d498efb4ad88a330882f8.png', '专注免费设计素材下载的网站。', 'http://www.58pic.com/', 5, 0, '2020-08-01 15:39:26', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100162, 150725316141101023, '千库网', '/profile/site/system/15ffa7b3a5cab15c7d23d402be12cc4c.png', '免费png图片背景素材下载。', 'http://588ku.com/', 6, 0, '2020-08-01 15:39:26', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100163, 150725316141101023, '我图网', '/profile/site/system/a887a255bbe7fe994e0479ae988372c7.png', '我图网,提供图片素材及模板下载,专注正版设计作品交易。', 'http://www.ooopic.com/', 7, 0, '2020-08-01 15:39:26', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100164, 150725316141101023, '90设计', '/profile/site/system/c510c1946d6191a98c6fd3b08ca720ec.png', '电商设计（淘宝美工）千图免费淘宝素材库。', 'http://90sheji.com/', 8, 0, '2020-08-01 15:39:26', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100165, 150725316141101023, '昵图网', '/profile/site/system/d4fba2a16c7a1692ea21f4f0a8ae7672.png', '原创素材共享平台。', 'http://www.nipic.com/', 9, 0, '2020-08-01 15:39:26', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100166, 150725316141101023, '懒人图库', '/profile/site/system/a7e5f98173ea111df83da146a86436a1.png', '懒人图库专注于提供网页素材下载。', 'http://www.lanrentuku.com/', 10, 0, '2020-08-01 15:39:26', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100167, 150725316141101023, '素材搜索', '/profile/site/system/cbca7fabfd7c6d1b117547466bc564ad.png', '设计素材搜索聚合。', 'http://so.ui001.com/', 11, 0, '2020-08-01 15:39:26', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100168, 150725316141101023, 'PS饭团网', '/profile/site/system/7ffad2eac8cbad395d33914344d3aa0a.png', '不一样的设计素材库！让自己的设计与众不同！', 'http://psefan.com/', 12, 0, '2020-08-01 15:39:26', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100169, 150725316141101023, '素材中国', '/profile/site/system/ced6b2a53069c7d360ba78706244081f.png', '免费素材共享平台。', 'http://www.sccnn.com/', 13, 0, '2020-08-01 15:39:26', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100170, 150725316141101024, 'UI中国', '/profile/site/system/9458ececbfeea651b5e871179f245ce5.png', '图形交互与界面设计交流、作品展示、学习平台。', 'http://www.ui.cn/', 1, 0, '2020-08-01 15:39:53', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100171, 150725316141101024, 'Freebiesbug', '/profile/site/system/4288052485ced84952e206a4acfb92ad.png', '网页设计人员和开发人员的精选资源不断更新。', 'https://freebiesbug.com/', 2, 0, '2020-08-01 15:39:53', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100172, 150725316141101024, 'Freebie Supply', '/profile/site/system/2ae393ad916a108ba20d21a8b907477e.png', '设计师的免费资源。', 'https://freebiesupply.com/', 3, 0, '2020-08-01 15:39:53', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100173, 150725316141101024, '云瑞', '/profile/site/system/528f9304b0dab49f5fe2426d4d9d047c.png', '优秀设计资源的分享网站。', 'https://www.yrucd.com/', 4, 0, '2020-08-01 15:39:53', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100174, 150725316141101024, 'Designmodo', '/profile/site/system/2061e0ccebfbb1a94a28d86237589b23.png', '网页设计博客和商店。', 'https://designmodo.com/', 5, 0, '2020-08-01 15:39:53', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100175, 150725316141101024, '稀土区', '/profile/site/system/4f5171443ad0ec6b13b7f96b8cead4bd.png', '优质设计开发资源分享。', 'https://xituqu.com/', 6, 0, '2020-08-01 15:39:53', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100176, 150725316141101024, 'ui8', '/profile/site/system/b815917aad63f449a96900979a16eb4e.png', 'UI套件，线框套件，模板，图标等。', 'https://ui8.net/', 7, 0, '2020-08-01 15:39:53', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100177, 150725316141101024, 'uplabs', '/profile/site/system/42d3d29c62a19e8d4ca6395586d79ee7.png', '产品设计师和开发人员的日常资源。', 'https://www.uplabs.com/', 8, 0, '2020-08-01 15:39:53', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100178, 150725316141101024, 'UIkit.me', '/profile/site/system/13b4ac8efdc9f92e52e7f271b8034b24.png', '最便捷新鲜的uikit资源下载网站。', 'http://www.uikit.me/', 9, 0, '2020-08-01 15:39:53', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100179, 150725316141101024, 'Fribbble', '/profile/site/system/04c1b51de97ceac330358fa7d1685034.png', 'Dribbblers提供了免费的PSD文件和其他免费设计资源。', 'http://www.fribbble.com/', 10, 0, '2020-08-01 15:39:53', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100180, 150725316141101024, 'PrincipleRepo', '/profile/site/system/e3e93f407dcc94461bea06979e89e4a2.png', '免费的高质量原则资源。', 'http://principlerepo.com/', 11, 0, '2020-08-01 15:39:53', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100181, 150725316141101025, 'Sketch', '/profile/site/system/ff6a2f8afaeb91004416c96788f9da95.png', '数字设计工具包。', 'https://sketchapp.com/', 1, 0, '2020-08-01 15:40:26', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100182, 150725316141101025, 'Sketch Measure', '/profile/site/system/69bf814d311d932ea13b746ffc1f9f54.png', '友好的用户界面为您提供了一种更直观的标记方式。', 'http://utom.design/measure/', 2, 0, '2020-08-01 15:40:26', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100183, 150725316141101025, 'Sketch App Sources', '/profile/site/system/ccf82c5a27a285ba63cf3c4ff8964a25.png', '免费的设计资源和插件-图标，UI套件，线框，iOS，Android草图模板', 'https://www.sketchappsources.com/', 3, 0, '2020-08-01 15:40:26', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100184, 150725316141101025, 'Sketch.im', '/profile/site/system/6055276a55b62db423c2b060d3f6b044.png', 'Sketch 相关资源汇聚。', 'http://www.sketch.im/', 4, 0, '2020-08-01 15:40:26', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100185, 150725316141101025, 'Sketch Hunt', '/profile/site/system/c4bf007a61d761db1f895672a2519cd0.png', 'Sketch Hunt是一个独立的博客，为Sketch应用程序的爱好者分享学习，插件和设计工具方面的瑰宝。', 'http://sketchhunt.com/', 5, 0, '2020-08-01 15:40:26', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100186, 150725316141101025, 'Sketch中文网', '/profile/site/system/a26a90da0d304cd3502cdb53e85995b9.png', '分享最新的Sketch中文手册。', 'http://www.sketchcn.com/', 6, 0, '2020-08-01 15:40:26', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100187, 150725316141101025, 'Awesome Sketch Plugins', '/profile/site/system/255cf49affef2fbaba8cd15c3e7329b5.png', '真正有用的Sketch插件的集合。', 'https://awesome-sket.ch/', 7, 0, '2020-08-01 15:40:26', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100188, 150725316141101025, 'Sketchcasts', '/profile/site/system/fa8c8b179ab48ad61e61a18d1720e019.png', '学习素描通过每周的视频教程来训练您的设计技能', 'https://www.sketchcasts.net/', 8, 0, '2020-08-01 15:40:26', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100189, 150725316141101026, 'Google Font', '/profile/site/system/91c604a4ca98b1bb5719e04c80043419.png', '通过出色的排版，使网络更加美观，快速和开放。', 'https://fonts.google.com/', 1, 0, '2020-08-01 15:40:55', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100190, 150725316141101026, 'Typekit', '/profile/site/system/7dbc17741d30274995615612dc1d075f.png', '来自世界上最好的铸造厂的高质量字体。', 'https://typekit.com/', 2, 0, '2020-08-01 15:40:55', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100191, 150725316141101026, '方正字库', '/profile/site/system/8ffeec1d3ad96a39dd4ede9794756b87.png', '方正字库官方网站。', 'http://www.foundertype.com/', 3, 0, '2020-08-01 15:40:55', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100192, 150725316141101026, '字体传奇网', '/profile/site/system/d5fc1ea541fe215ae449a0ae27a09a76.png', '中国首个字体品牌设计师交流网。', 'http://ziticq.com/', 4, 0, '2020-08-01 15:40:55', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100193, 150725316141101026, '私藏字体', '/profile/site/system/125e538efd8b3ea68d3655cb81ccc06f.png', '优质字体免费下载站。', 'http://sicangziti.com/', 5, 0, '2020-08-01 15:40:55', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100194, 150725316141101026, 'Fontsquirrel', '/profile/site/system/94684e5203623eb5540a4b5a0e0b70b0.png', '图形设计师的免费字体。', 'https://www.fontsquirrel.com/', 6, 0, '2020-08-01 15:40:55', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100195, 150725316141101026, 'Urban Fonts', '/profile/site/system/40139afeda032d1a3cd54459d065b31b.png', '下载免费字体和免费Dingbats。', 'https://www.urbanfonts.com/', 7, 0, '2020-08-01 15:40:55', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100196, 150725316141101026, 'Lost Type', '/profile/site/system/cfe0904ec3e37914be51687a2b15f5cf.png', '失物招领是一个协作数字类型代工厂。', 'http://www.losttype.com/', 8, 0, '2020-08-01 15:40:55', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100197, 150725316141101026, 'FONTS2U', '/profile/site/system/19bd844dc123066620d1f6fda7287e48.png', '为Windows和Macintosh下载免费字体。', 'https://fonts2u.com/', 9, 0, '2020-08-01 15:40:55', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100198, 150725316141101026, 'Fontex', '/profile/site/system/1576a7303fb2fa3839016a599418203b.png', '免费字体下载+高级字体。', 'http://www.fontex.org/', 10, 0, '2020-08-01 15:40:55', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100199, 150725316141101026, 'FontM', '/profile/site/system/5ee459b3c52027eb8dcd9c8c6e9266a0.png', '免费字体。', 'http://fontm.com/', 11, 0, '2020-08-01 15:40:55', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100200, 150725316141101026, 'My Fonts', '/profile/site/system/22572b2d9b38ea02f173074d59acf334.png', '用于印刷，产品和屏幕的字体。', 'http://www.myfonts.com/', 12, 0, '2020-08-01 15:40:55', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100201, 150725316141101026, 'Da Font', '/profile/site/system/d0478f80b89bff215437714e62c6d997.png', '可免费下载的字体的存档。', 'https://www.dafont.com/', 13, 0, '2020-08-01 15:40:55', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100202, 150725316141101026, 'OnlineWebFonts', '/profile/site/system/bccc59c04f6f283a63430700273ffdee.png', '适用于Windows和Mac的WEB免费字体/免费下载字体。', 'https://www.onlinewebfonts.com/', 14, 0, '2020-08-01 15:40:55', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100203, 150725316141101026, 'Abstract Fonts', '/profile/site/system/fbedc66f865056e650a036f042625057.png', '抽象字体（13,866个免费字体）。', 'http://www.abstractfonts.com/', 15, 0, '2020-08-01 15:40:55', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100204, 150725316141101027, 'MockupZone', '/profile/site/system/75e599ff2f061dc25fa272de94045ca9.png', 'Mockup Zone是一个在线商店，您可以在其中找到免费的高级PSD样机文件，以专业的方式展示您的设计。', 'https://mockup.zone/', 1, 0, '2020-08-01 15:41:42', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100205, 150725316141101027, 'Dunnnk', '/profile/site/system/7949d12743b95779412dd8673c324164.png', '免费生成产品模型。', 'http://dunnnk.com/', 2, 0, '2020-08-01 15:41:42', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100206, 150725316141101027, 'Graphberry', '/profile/site/system/1216f3642b463e7e9d493e4d00506566.png', '免费设计资源，样机，PSD Web模板，图标。', 'http://www.graphberry.com/', 3, 0, '2020-08-01 15:41:42', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100207, 150725316141101027, 'Threed', '/profile/site/system/252114418dc086100cd58d10035a9436.png', '直接在浏览器中生成3D模型', 'http://threed.io/', 4, 0, '2020-08-01 15:41:42', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100208, 150725316141101027, 'Mockup World', '/profile/site/system/c94ee98e4ada29c0916888820da31744.png', '网上最好的免费模型', 'https://free.lstore.graphics/', 5, 0, '2020-08-01 15:41:42', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100209, 150725316141101027, 'Lstore', '/profile/site/system/965f25d08ae3cd33fab21d764a514967.png', '面向设计师和开发人员的独家令人兴奋的免费赠品。', 'https://free.lstore.graphics/', 6, 0, '2020-08-01 15:41:42', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100210, 150725316141101027, 'pixeden', '/profile/site/system/f8b5261bc1d5e5189b9c1216a6de8b3b.png', '免费的网络资源和图形设计模板。', 'https://www.pixeden.com/', 7, 0, '2020-08-01 15:41:42', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100211, 150725316141101027, 'For Graphic TM', '/profile/site/system/20fceec1b9dd6c1183ad73a90becce7f.png', '适用于图形设计师的高质量PSD样机。', 'http://forgraphictm.com/', 8, 0, '2020-08-01 15:41:42', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100212, 150725316141101028, 'Unsplash', '/profile/site/system/72880b02dbea40fd84472abc05e6d23b.png', '漂亮的免费照片。', 'https://unsplash.com/', 1, 0, '2020-08-01 15:42:16', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100213, 150725316141101028, 'visualhunt', '/profile/site/system/b2a1a1e4c043858ac2411f02b9236ff3.png', '100％免费的高质量照片。', 'https://visualhunt.com/', 2, 0, '2020-08-01 15:42:16', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100214, 150725316141101028, 'librestock', '/profile/site/system/94c5305f78dfadb241f9edcf3d9b870d.png', '65,084高品质的照片，随您想要。', 'https://librestock.com/', 3, 0, '2020-08-01 15:42:16', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100215, 150725316141101028, 'pixabay', '/profile/site/system/310cb7b52774323c7fdffe67aa0f12aa.png', '可在任何地方使用的免费图片和视频。', 'https://pixabay.com/', 4, 0, '2020-08-01 15:42:16', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100216, 150725316141101028, 'SplitShire', '/profile/site/system/0e9933021af7cc4714e900c247010b30.png', '免费图片和视频供商业使用。', 'https://www.splitshire.com/', 5, 0, '2020-08-01 15:42:16', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100217, 150725316141101028, 'StockSnap', '/profile/site/system/fabf86558eb3a7c943c124f7f62f3542.png', '美丽的免费图片素材。', 'https://stocksnap.io/', 6, 0, '2020-08-01 15:42:16', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100218, 150725316141101028, 'albumarium', '/profile/site/system/de8b7f26a21ea0b781f93a3163341731.png', '查找和分享精美图像的最佳场所。', 'http://albumarium.com/', 7, 0, '2020-08-01 15:42:16', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100219, 150725316141101028, 'myphotopack', '/profile/site/system/80d85ea59d293bd43731a890f63c5dc9.png', '专门为您提供的免费照片包。 每个月。', 'https://myphotopack.com/', 8, 0, '2020-08-01 15:42:16', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100220, 150725316141101028, 'Notaselfie', '/profile/site/system/eb5f9a9661e582883c9d3128bb9b4482.png', '一路上发生的照片。 您可以随时使用图像。 玩得开心！', 'http://notaselfie.com/', 9, 0, '2020-08-01 15:42:16', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100221, 150725316141101028, 'papers', '/profile/site/system/3a6396ba24d253502f40432751a11b07.png', '每小时都有墙纸！手工收集', 'http://papers.co/', 10, 0, '2020-08-01 15:42:16', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100222, 150725316141101028, 'stokpic', '/profile/site/system/9dce238279b24893eaa20a99fba802ea.png', '免费图片供商业使用。', 'http://stokpic.com/', 11, 0, '2020-08-01 15:42:16', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100223, 150725316141101028, '55mm', '/profile/site/system/dd8adcbc65cc20e8fb6d6335fd57814a.png', '使用我们的免费照片讲述您的故事！', 'https://55mm.co/visuals', 12, 0, '2020-08-01 15:42:16', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100224, 150725316141101028, 'thestocks', '/profile/site/system/2be533b5b00139b9022f09604f3bd136.png', '使用我们的免费照片讲述您的故事！', 'http://thestocks.im/', 13, 0, '2020-08-01 15:42:16', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100225, 150725316141101028, 'freenaturestock', '/profile/site/system/85c87259ac26b4f48b084066b9e3ec8e.png', '面向设计师和开发人员的独家令人兴奋的免费赠品。', 'http://freenaturestock.com/', 14, 0, '2020-08-01 15:42:16', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100226, 150725316141101028, 'negativespace', '/profile/site/system/9b470b26c5e7e6604f3f17d2fe518af7.png', '美丽，高分辨率免费图片素材。', 'https://negativespace.co/', 15, 0, '2020-08-01 15:42:16', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100227, 150725316141101028, 'gratisography', '/profile/site/system/37a9bff7f4d756e7b227ef295aa5ff82.png', '免费的高分辨率图片，可用于您的个人和商业项目，不受版权限制。', 'https://gratisography.com/', 16, 0, '2020-08-01 15:42:16', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100228, 150725316141101028, 'imcreator', '/profile/site/system/568ae371ba49ce83463d5833af6a8e88.png', '精选的免费网页设计资源集合，全部用于商业用途。', 'http://imcreator.com/free', 17, 0, '2020-08-01 15:42:16', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100229, 150725316141101028, 'lifeofpix', '/profile/site/system/94bf5d51c1367552f337610dbc6aa44b.png', '免费高分辨率摄影', 'http://www.lifeofpix.com/', 18, 0, '2020-08-01 15:42:16', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100230, 150725316141101028, 'skitterphoto', '/profile/site/system/23663c43cb7025f3bf36e9733bea6171.png', '创意专业人士的免费图片素材', 'https://skitterphoto.com/', 19, 0, '2020-08-01 15:42:16', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100231, 150725316141101028, 'mmtstock', '/profile/site/system/d8d5768d2dc63763480478ae25aa176a.png', '免费商业照片', 'https://mmtstock.com/', 20, 0, '2020-08-01 15:42:16', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100232, 150725316141101028, 'magdeleine', '/profile/site/system/12ca6edef00d1d897eb28c4a8e2f8915.png', '精选免费照片供您启发', 'https://magdeleine.co/browse/', 21, 0, '2020-08-01 15:42:16', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100233, 150725316141101028, 'jeshoots', '/profile/site/system/a016e8d2ae3ee88f0ec136440e92fca8.png', '新的免费照片和样机进入您的收件箱！', 'http://jeshoots.com/', 22, 0, '2020-08-01 15:42:16', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100234, 150725316141101028, 'hdwallpapers', '/profile/site/system/74db036ddf1bbfc49a22a5a6dcd392ab.png', '高清壁纸和桌面背景', 'https://www.hdwallpapers.net', 23, 0, '2020-08-01 15:42:16', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100235, 150725316141101028, 'publicdomainarchive', '/profile/site/system/3c7427a4bab6bb40c12a77014f809a2a.png', '新的100％免费图片。 每一个 单。 周。', 'http://publicdomainarchive.com/', 24, 0, '2020-08-01 15:42:16', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100236, 150725316141101029, 'OfficePLUS', '/profile/site/system/4773ef0cfcf8c9fd158fc7db0bc2cf0b.png', 'OfficePLUS，微软Office官方在线模板网站！', 'http://www.officeplus.cn/Template/Home.shtml', 1, 0, '2020-08-01 15:43:15', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100237, 150725316141101029, '优品PPT', '/profile/site/system/b1d803179735ea628d1d914c63c0b9f7.png', '高质量的模版，而且还有PPT图表，PPT背景图等资源', 'http://www.ypppt.com/', 2, 0, '2020-08-01 15:43:15', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100239, 150725316141101029, 'PPTMind', '/profile/site/system/857bb0f6927c2a8c246653cb41136ce7.png', '分享高端ppt模板与keynote模板的数字作品交易平台', 'http://www.pptmind.com/', 4, 0, '2020-08-01 15:43:15', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100240, 150725316141101029, 'tretars', '/profile/site/system/14a77db5ab4af0ba947b1e1707295c5d.png', '网上最好的免费模型', 'http://www.tretars.com/ppt-templates', 5, 0, '2020-08-01 15:43:15', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100241, 150725316141101029, '5百丁', '/profile/site/system/3f735ae4b6e18cd6cff3965661289aac.png', '中国领先的PPT模板共享平台', 'http://ppt.500d.me/', 6, 0, '2020-08-01 15:43:15', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100276, 150725316141101004, 'PDF Candy', '/profile/site/system/0b897cff5f1f2d98efd8dc7ea69b38ae.ico', '一个免费在线的PDF编辑网站。包含了几十种PDF小工具，可以Word与PDF互转、Excel表格与PDF互转、图片与PDF互转等各种实用功能。', 'https://pdfcandy.com/cn/', 2, 0, '2020-08-05 13:28:17', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100277, 150725316141101004, 'LightPDF', '/profile/site/system/6281eb27f1a6c6f07d56588e5e4849ae.png', '一个只需要一步，即可解决PDF所有问题的免费PDF工具!', 'https://lightpdf.com/zh/', 3, 0, '2020-08-05 13:30:31', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100278, 150725316141101004, 'SmallPDF', '/profile/site/system/341475474199e2cf28ce10308fcad860.png', '轻松玩转PDF，功能一应俱全、简单好用的线上 PDF 工具', 'https://smallpdf.com/cn', 4, 0, '2020-08-05 13:31:49', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100279, 150725316141101004, 'HiPDF', '/profile/site/system/3cac096ab252280f24a70ca5550a0287.ico', '一站式在线PDF解决方案的网站。', 'https://www.hipdf.cn/', 5, 0, '2020-08-05 13:33:37', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100280, 150725316141101004, 'iLovePDF', '/profile/site/system/cc239ae8c064a5bbb65318fb40e02cdf.png', '完全免费、易于使用、丰富的PDF处理工具，包括：合并、拆分、压缩、转换、旋转和解锁PDF文件，以及给PDF文件添加水印的工具等。仅需几秒钟即可完成。', 'https://www.ilovepdf.com/zh-cn', 6, 0, '2020-08-05 13:35:10', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100281, 150725316141101007, 'CSDN', '/profile/site/system/0b91790e27fe8cd3941fc6e6ee57ee4a.jpg', '专业开发者社区', 'https://www.csdn.net/', 2, 0, '2020-08-08 15:12:00', '2020-08-08 15:20:24', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100282, 150725316141101007, '博客园', '/profile/site/system/2ec8affefca78704574f1ba17fe070f2.png', '开发者的网上家园', 'https://www.cnblogs.com/', 1, 0, '2020-08-08 15:13:36', '2020-08-08 15:20:20', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100283, 150725316141101007, '思否', '/profile/site/system/570d66cff03484e48b05d6f02db9dfe4.ico', ' 在 SegmentFault，学习技能、解决问题。', 'https://segmentfault.com/', 4, 0, '2020-08-08 15:16:50', '2020-08-08 15:20:39', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100284, 150725316141101007, '掘金', '/profile/site/system/dcc82ea8e38e1bd994cabb18b8eb4e32.ico', '一个帮助开发者成长的社区。', 'https://juejin.im/', 3, 0, '2020-08-08 15:18:11', '2020-08-08 15:20:35', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100285, 150725316141101031, 'Vue', '/profile/site/system/5ebfa247ff3e3cbcb1ada1c5a9b2506b.png', '渐进式 JavaScript 框架。', 'https://cn.vuejs.org/', 1, 0, '2020-08-08 15:21:55', '2020-08-08 15:23:40', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100286, 150725316141101031, 'React', '/profile/site/system/9e76a67ae80210b18e4db37b45a2574a.ico', '用于构建用户界面的 JavaScript 库。', 'https://react.docschina.org/', 5, 0, '2020-08-08 15:23:35', '2022-06-11 17:30:58', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100287, 150725316141101031, 'Angular', '/profile/site/system/ed0f155ed1b6acc4e6f307543883aa7e.ico', '一套框架，多种平台 移动端 &amp; 桌面端。', 'https://angular.cn/', 6, 0, '2020-08-08 15:25:11', '2022-06-11 17:31:14', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100289, 150725316141101031, 'Element', '/profile/site/system/d636a118f9f69184a730ffa3082fdb80.ico', '一套为开发者、设计师和产品经理准备的基于 Vue 2.0 的桌面端组件库', 'https://element.eleme.cn/', 2, 0, '2020-08-08 15:30:27', '2022-06-11 17:30:38', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100290, 150725316141101031, 'vue-element-admin', '/profile/site/system/d91882f77429ded326a30bda549c329d.png', '一个后台前端解决方案，它基于 vue 和 element-ui 实现。', 'https://panjiachen.gitee.io/vue-element-admin-site/zh/', 4, 0, '2020-08-08 15:32:01', '2022-06-11 17:31:05', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100291, 150725316141101007, '知乎', '/profile/site/system/02090c1f2ff7d580395b7dfe44ba066c.ico', '有问题，上知乎。知乎，可信赖的问答社区，以让每个人高效获得可信赖的解答为使命。', 'https://www.zhihu.com', 5, 0, '2020-08-08 15:34:36', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100292, 150725316141101030, 'Spring', '/profile/site/system/39caf5180b7d84ff2f277b25ca10f6ce.png', 'Spring 框架是一个开源的 Java 平台，它为容易而快速的开发出耐用的 Java 应用程序提供了全面的基础设施。', 'https://spring.io/', 1, 0, '2020-08-08 15:37:04', '2020-08-08 15:38:49', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100293, 150725316141101030, 'MyBatis-Plus', '/profile/site/system/4eb015ca9291887d648fc075f76303ac.png', '为简化开发而生。', 'https://mp.baomidou.com/', 2, 0, '2020-08-08 15:40:12', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100294, 150725316141101030, '力扣 LeetCode', '/profile/site/system/3b0dcb135f4c9a960b385170ce5f3946.png', '全球极客挚爱的技术成长平台。海量技术面试题库,拥有算法、数据结构、系统设计等 1000+题目,帮助你高效提升编程技能,轻松拿下世界 IT 名企 Dream Offer。', 'https://leetcode-cn.com/', 3, 0, '2020-08-08 15:44:12', '2021-01-15 10:49:27', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100295, 150725316141101004, 'Convertio 文件转换器', '/profile/site/system/b5d554962ba1cc493607a32f5328787d.png', '超级强大的文件格式转化器，视频、图片、字体、音频通通都可以。', 'https://convertio.co/zh/', 7, 0, '2020-08-09 13:01:13', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100296, 150725316141101007, '菜鸟教程', '/profile/site/system/b66656c08273c4761073eab6ae59b1ca.ico', '学的不仅是技术，更是梦想！', 'https://www.runoob.com/', 6, 0, '2020-08-09 13:08:03', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100297, 150725316141101031, 'Bootstrap', '/profile/site/system/5285612ea8533aa78c30bfa382171077.ico', '简洁、直观、强悍的前端开发框架，让web开发更迅速、简单。', 'https://www.bootcss.com/', 6, 0, '2020-08-09 13:09:42', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100298, 150725316141101007, 'W3School', '/profile/site/system/4d2b466766140ef66ab6e4e92f73383d.png', '在 W3School，你可以找到你所需要的所有的网站建设教程。', 'https://www.w3school.com.cn/', 7, 0, '2020-08-09 13:14:17', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100299, 150725316141101032, '五分钟学算法', '/profile/site/system/4356bd8feb9ae4b14a75fa81b79a6d2a.png', '一个不错的算法网站！', 'https://www.cxyxiaowu.com/', 6, 0, '2020-08-09 13:15:18', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100300, 150725316141101001, '凌风云搜索', '/profile/site/system/ed2c1e745ad694bbe206dd93c0ff8ea1.jpg', '专注于互联网免费资源的大数据搜索，网盘搜索，云盘资源等', 'https://www.lingfengyun.com/', 9, 0, '2020-08-09 21:53:55', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100303, 150725316141101001, '罗马盘', '/profile/site/system/69d01f6bbf50666a7f5bb36c8e59fcbc.ico', '百度网盘搜索引擎 自动更新网络共享资源', 'https://www.luomapan.com/', 11, 0, '2020-08-09 22:03:54', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100304, 150725316141101001, 'bdyso', '/profile/site/system/b6494b1345e86d25a090af4e96e084b2.ico', '百度网盘资源搜索与分享', 'http://www.bdyso.com/', 12, 0, '2020-08-09 22:05:30', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100306, 150725316141101035, '微信公众号', '/profile/site/system/eb7f53364e44272c8a2ec83d0e83df43.png', '再小的个体，也有自己的品牌。', 'https://mp.weixin.qq.com/', 1, 0, '2020-08-11 17:47:03', '2020-08-11 17:52:22', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100307, 150725316141101035, '头条号', '/profile/site/system/0db580c9e8afe1e543c120a8e026be05.png', '今日头条推出的开放的内容创作与分发平台。', 'https://mp.toutiao.com', 2, 0, '2020-08-11 17:49:28', '2020-08-11 17:51:26', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100308, 150725316141101035, '知乎', '/profile/site/system/02090c1f2ff7d580395b7dfe44ba066c.ico', '有问题，上知乎。知乎，可信赖的问答社区，以让每个人高效获得可信赖的解答为使命。', 'https://www.zhihu.com', 5, 0, '2020-08-11 19:58:24', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100309, 150725316141101035, 'CSDN', '/profile/site/system/eb7c0916-0c5f-4aad-ba1e-6eb7a076ce41.ico', '专业开发者社区', 'https://www.csdn.net/', 6, 0, '2020-08-08 15:12:00', '2021-01-13 15:37:46', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100310, 150725316141101035, '博客园', '/profile/site/system/2ec8affefca78704574f1ba17fe070f2.png', '开发者的网上家园', 'https://www.cnblogs.com/', 7, 0, '2020-08-08 15:13:36', '2020-08-08 15:20:20', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100311, 150725316141101035, '思否', '/profile/site/system/570d66cff03484e48b05d6f02db9dfe4.ico', ' 在 SegmentFault，学习技能、解决问题。', 'https://segmentfault.com/', 8, 0, '2020-08-08 15:16:50', '2020-08-08 15:20:39', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100312, 150725316141101035, '掘金', '/profile/site/system/dcc82ea8e38e1bd994cabb18b8eb4e32.ico', '一个帮助开发者成长的社区。', 'https://juejin.im/', 9, 0, '2020-08-08 15:18:11', '2020-08-08 15:20:35', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100313, 150725316141101035, '企鹅号', '/profile/site/system/7c7b65475eddc144e90e6e9e059b75a7.png', '让世界看到你', 'https://om.qq.com/', 4, 0, '2020-08-11 20:03:20', '2020-08-11 20:07:10', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100314, 150725316141101035, '微博号', '/profile/site/system/0457b6128c225591f7b04dd23eaf4445.ico', '为创作者提供更优质的创作环境，帮助原创作者打造专属的个人品牌。', 'https://me.weibo.com/', 4, 0, '2020-08-11 20:05:49', '2020-08-11 20:06:30', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100315, 150725316141101035, '简书', '/profile/site/system/f8e210bc-a44e-49ad-a177-c9a8e2dcc409.ico', '一个优质的创作社区，在这里，你可以任性地创作。', 'https://www.jianshu.com/', 10, 0, '2020-08-11 20:08:35', '2021-01-13 15:37:33', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100316, 150725316141101035, '百家号', '/profile/site/system/ebb9886c7983ded7a1867941643de37f.png', '从这里影响世界。', 'https://baijiahao.baidu.com/', 4, 0, '2020-08-11 20:11:07', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100317, 150725316141101036, 'Unsplash', '/profile/site/system/09b7b39944a6893fbffa2842c0951468.png', '美丽的免费图片图片', 'https://unsplash.com/', 2, 0, '2020-08-11 20:15:40', '2021-04-22 21:36:24', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100318, 150725316141101036, 'Pexels', '/profile/site/system/5116557ff603b6d0cc4ba5270588684e.png', '免费图片', 'https://www.pexels.com/', 2, 0, '2020-08-11 20:17:44', '2020-08-11 20:23:35', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100319, 150725316141101036, 'UnDraw', '/profile/site/system/fb631125f144e2a6fe14120c2f4bd53f.png', '带有开源插图的设计项目，可满足您可以想象和创建的任何想法。', 'https://undraw.co/illustrations', 3, 0, '2020-08-11 20:25:06', '2021-03-27 12:49:55', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100320, 150725316141101036, 'Pixabay', '/profile/site/system/3846a4474d17612128f18f51f3487fd9.png', '我们的才华横溢的社区分享了超过210万张免版税的图片。', 'https://pixabay.com', 4, 0, '2020-08-11 20:28:06', '2021-03-15 23:35:03', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100321, 150725316141101036, 'Iconfinder', '/profile/site/system/e3325f68179436ccfc25b9f0ffff5a39.png', '2,100,000+个免费和高级矢量图标。', 'https://www.iconfinder.com', 5, 0, '2020-08-01 14:58:10', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100322, 150725316141101036, 'iconfont', '/profile/site/system/e1f63337915f79f8bcad1952adb9f6e1.png', '阿里巴巴矢量图标库。', 'http://www.iconfont.cn/', 6, 0, '2020-08-01 14:58:10', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100323, 150725316141101036, 'iconmonstr', '/profile/site/system/afd4885651455f12dcac4f214460dd99.png', '您的下一个项目的免费简单图标。', 'https://iconmonstr.com/', 7, 0, '2020-08-01 14:58:11', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100324, 150725316141101036, 'FindIcons', '/profile/site/system/0171a46b0f643752aa90aa314a22a546.png', '搜索300,000个免费图标。', 'https://findicons.com/', 8, 0, '2020-08-01 14:58:11', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100325, 150725316141101036, 'Icon Archive', '/profile/site/system/40c43a8932f24370cf456789b2ab51db.png', '搜索590,912个免费图标。', 'http://www.iconarchive.com/', 9, 0, '2020-08-01 14:58:11', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100326, 150725316141101036, 'IcoMoonApp', '/profile/site/system/d19c97ead3760f1b70efa4ee9ad6859c.png', '图标字体，SVG，PDF, PNG生成器。', 'https://icomoon.io/app/', 10, 0, '2020-08-01 14:58:11', '2020-08-11 20:36:42', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100327, 150725316141101036, 'easyicon', '/profile/site/system/34b4382075e047c6d1456f8fe591a1ef.png', 'PNG、ICO、ICNS格式图标搜索、图标下载服务。', 'http://www.easyicon.net/', 11, 0, '2020-08-01 14:58:11', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100328, 150725316141101036, 'flaticon', '/profile/site/system/582cf7361a0b4f444628c68b98e5cfc7.png', '634,000+免费矢量图标为SVG，PSD，PNG，EPS格式或图标字体。', 'https://www.flaticon.com/', 12, 0, '2020-08-01 14:58:11', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100329, 150725316141101036, 'UICloud', '/profile/site/system/f9840e127d500449da1c5c721f3634c3.png', '世界上最大的用户界面设计数据库。', 'http://ui-cloud.com/', 13, 0, '2020-08-01 14:58:11', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100330, 150725316141101036, 'Font Awesome Icon', '/profile/site/system/88440b8b0d5dc43a3f766670e2d11746.png', '惊人的675个图标的完整集合。', 'https://fontawesome.com/', 14, 0, '2020-08-01 14:58:11', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100331, 150725316141101036, 'ion icons', '/profile/site/system/6d0fd0bf35549f6d61037bd86e2ca242.png', 'Ionic Framework的高级图标字体。', 'http://ionicons.com/', 15, 0, '2020-08-01 14:58:11', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100332, 150725316141101036, 'Simpleline Icons', '/profile/site/system/acf446f1af754f863260cc10dd8d546e.png', '简单的线条图标包。', 'http://simplelineicons.com/', 16, 0, '2020-08-01 14:58:11', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100334, 150725316141101032, 'Java知音', '/profile/site/system/bbe364f9-0c94-4f88-a4ad-2b590064d624.jpeg', '一个专注于Java技术分享的网站', 'https://www.javazhiyin.com/', 7, 0, '2020-08-18 21:50:19', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100335, 150725316141101010, 'MacWk', '/profile/site/system/17a92b31-c9da-4dee-a04c-4ecf9cd92933.jpeg', '无广告，无后门，安全！', 'https://macwk.com/', 3, 0, '2020-08-18 21:54:00', '2020-08-18 21:54:34', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100340, 150725316141101002, '知轩藏书', '/profile/site/system/2e3e22f2-cd93-45b3-8278-604c483ed70e.jpeg', '玄幻小说排行榜精校-校对全本TXT小说下载网', 'http://zxcs.info/', 20, 0, '2020-08-19 00:20:24', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100341, 150725316141101037, 'WallHaven', '/profile/site/system/ff5238ff-e030-4f55-a6fb-ba49fd772ea8.jpeg', '种类多，壁纸好看！', 'https://wallhaven.cc/', 1, 0, '2020-08-19 19:42:33', '2020-09-20 20:28:40', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100342, 150725316141101037, 'Wallls', '/profile/site/system/3267d11e-db69-4dde-80fb-e3fef5d7ae6f.jpeg', '您从未见过的壁纸。', 'http://wallls.com/', 2, 0, '2020-08-19 19:44:48', '2020-09-20 20:28:47', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100344, 150725316141101037, 'SimpleDesktops', '/profile/site/system/1687352f-29f4-4eea-a61e-a0eeded626f1.jpeg', '简单', 'http://simpledesktops.com/', 4, 0, '2020-08-19 19:52:00', '2020-09-20 20:29:03', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100345, 150725316141101037, '彼岸桌面', '/profile/site/system/bd5b21c3-3bf1-47cc-b25f-36b4aa38180d.jpeg', '美丽精致的壁纸，免费提供风景、日历、美女、动漫、汽车、花卉、节日、动物、游戏、qq、阿狸等唯美、可爱、好看的壁纸，下载您所需要的壁纸', 'http://www.netbian.com/', 1, 0, '2020-08-19 19:57:41', '2020-12-03 13:37:09', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100346, 150725316141101001, '猪猪盘', '/profile/site/system/04e0b7e4-2569-49bf-8629-b7a503dd8fe2.png', '索引1亿+网盘资源', 'http://www.zhuzhupan.com/', 15, 0, '2020-08-19 20:13:29', '2020-09-20 20:25:48', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100348, 150725316141101001, '小白盘', '/profile/site/system/f04b12a5-3a1d-4af2-9672-08a0e5458bf2.jpeg', '网盘搜索', 'https://www.xiaobaipan.com/', 17, 0, '2020-08-19 20:16:51', '2020-08-19 20:18:03', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100352, 150725316141101004, '快用工具', '/profile/site/system/c5a1b819-a2ac-40a5-8aba-4b2204b8e115.jpeg', '提供各种优质、快捷、易用的在线工具，无需下载安装即可使用。', 'https://fastools.cn/', 1, 0, '2020-08-20 10:43:46', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100353, 150725316141101037, '极简壁纸', '/profile/site/system/7dcc6100-d451-4d78-915e-ea68c166d5cd.jpeg', '超高清电脑桌面壁纸美图；每天更新海量 4K 电脑壁纸，9012年最潮的壁纸网站，壁纸包括美女、二次元、自然风景。', 'https://bz.zzzmh.cn/', 1, 0, '2020-08-20 10:50:21', '2020-12-03 13:37:05', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100354, 150725316141101039, '135编辑器', '/profile/site/system/37ddd142-39c9-447c-b116-06f2e25878a0.jpeg', '提供丰富的样式库，支持插入排版、秒刷排版、一键排版等。', 'http://www.135editor.com/', 1, 0, '2020-08-20 11:05:19', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100355, 150725316141101039, '秀米', '/profile/site/system/db7eb18d-abdc-4979-bad3-2df745827332.jpeg', '素材顺应时下的审美，质量高，以布局的概念来进行不同组件的排版。', 'https://xiumi.us', 2, 0, '2020-08-20 11:12:34', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100356, 150725316141101039, '96微信编辑器', '/profile/site/system/849bb7d5-0616-478a-bada-40c3ad70a35f.jpeg', '大量精选素材、可以制作动态二维码、超多色值推荐、表情符号等。', 'https://bj.96weixin.com/', 3, 0, '2020-08-20 11:13:58', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100357, 150725316141101039, 'i排版', '/profile/site/system/1f8b4cd3-f277-42e8-b84e-2236c1a5aa82.ico', '偏清新文艺风，编辑界面较干净，容易上手，支持各种文本格式样式。', 'http://ipaiban.com/', 4, 0, '2020-08-20 11:18:13', '2021-01-13 15:40:07', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100358, 150725316141101039, '新榜编辑器', '/profile/site/system/61ed7aaf-2edd-4877-9d2a-4635a22b49e1.jpeg', '海量在线图片搜索、一键同步多平台，大量爆文可供参考。', 'https://edit.newrank.cn/', 5, 0, '2020-08-20 11:19:12', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100359, 150725316141101039, 'Markdown Nice', '/profile/site/system/a4db5eb7-7b52-4278-8a03-d147c817e494.jpeg', '一款在线，支持自定义样式的微信 Markdown 排版工具；现支持微信公众号、知乎、开源中国、稀土掘金、博客园和 CSDN 等一系列平台。另外，支持变更不同主题风格、格式化（微信外链转脚注、中英文间带空格）等。', 'https://www.mdnice.com/', 0, 0, '2020-08-20 11:22:19', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100360, 150725316141101039, '壹伴', '/profile/site/system/2632d2f2-c7bc-4278-90c4-ae75221e167a.jpeg', '更好用的微信编辑器，但不止于此。你可以使用壹伴小插件来高效地排版、修图、找素材、回消息和导出数据，50万公众号运营者的共同选择', 'https://yiban.io/invitation?invite_code=YE1H00', 6, 0, '2020-08-22 10:29:29', '2021-02-24 22:32:42', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100361, 150725316141101036, '创客贴', '/profile/site/system/3808ba9d-c655-42c9-b56f-cbdc04169be2.jpeg', '提供了15万+精品设计模板，120万+图片素材，涵盖营销海报、新媒体配图、印刷物料、PPT、简历等办公文档、电商设计、定制设计等百余种设计场景，选择喜爱的模板，AI智能生成设计，设计不求人。', 'https://www.chuangkit.com/', 1, 0, '2020-08-22 10:33:27', '2021-04-22 21:36:17', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100362, 150725316141101036, '懒设计', '/profile/site/system/bb76267f-1f2f-47c5-8c63-b4c136d58386.jpeg', '全球最受欢迎的平面设计工具和在线平面设计软件之一,提供海量海报、邀请函、贺卡、banner、logo、名片等免费设计素材和模板,可在线一键稿定设计印刷', 'https://www.fotor.com.cn/', 1, 0, '2020-08-22 10:39:01', '2021-04-22 21:36:12', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100363, 150725316141101011, '磁力熊', '/profile/site/system/75babf0b-ac99-4b61-8de0-b500f06398ff.jpeg', '1080P高清电影磁力迅雷下载,豆瓣Top250及豆瓣高分电影1080P高清磁力下载。', 'https://www.cilixiong.com/', 7, 0, '2020-08-22 18:25:13', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100366, 150725316141101011, '神马影院', '/profile/site/system/8ecdec9c-7802-4007-a30c-078fa7977a27.jpeg', '资源丰富，播放流畅缓冲快！', 'https://www.shenma4480.com/', 10, 0, '2020-08-22 18:29:46', '2020-08-22 18:37:47', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100367, 150725316141101011, '哔嘀影视', '/profile/site/system/f32be96e-14c9-41cb-9217-ea8a28948c65.jpeg', '在线观看，支持百度云，电驴，磁力链接下载', 'https://bde4.com/', 11, 0, '2020-08-22 18:31:14', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100368, 150725316141101011, 'NO视频', '/profile/site/system/bf08096d-7cb2-45e2-a00c-3efe03f0e84b.jpeg', '欧美、日韩、港台影视', 'https://www.novipnoad.com/', 12, 0, '2020-08-22 18:32:42', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100371, 150725316141101011, '南柯电影', '/profile/site/system/1687352f-29f4-4eea-a61e-a0eeded626f1.jpeg', '无广告，速度快！', 'https://www.nkdyw.com/', 15, 0, '2020-08-22 18:36:51', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100376, 150725316141101012, 'MyFree MP3', '/profile/site/system/3267d11e-db69-4dde-80fb-e3fef5d7ae6f.jpeg', '支持音乐在线试听、下载，以及无损音质的下载！', 'http://tool.liumingye.cn/music/', 9, 0, '2020-08-22 18:46:10', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100378, 150725316141101031, 'Ant Design', '/profile/site/system/6df56693-ffd1-4470-b905-0986f1236deb.jpeg', '一套企业级 UI 设计语言和 React 组件库', 'https://ant.design/index-cn', 7, 0, '2020-08-24 21:44:54', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100379, 150725316141101030, 'knife4j', '/profile/site/system/c74470de-7f78-4894-a475-522fa6774170.jpeg', '为Java MVC框架集成Swagger生成Api文档的增强解决方案 ', 'https://doc.xiaominfo.com/', 4, 0, '2020-08-25 19:39:38', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100380, 150725316141101040, 'GitHub 加速下载', '/profile/site/system/github.png', '一个对于 GitHub.com 的镜像加速器。我们使用开放资源为 GitHub 加速。', 'http://toolwa.com/github/', 1, 0, '2020-08-31 22:52:30', '2020-09-25 08:42:27', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100381, 150725316141101040, '下载', '/profile/site/system/github.png', '不需要购买，直接下载！', 'https://d.serctl.com/', 2, 0, '2020-08-31 23:01:16', '2020-09-25 08:43:52', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100382, 150725316141101040, 'GitHub 文件加速', '/profile/site/system/github.png', '直接走本服务器 CN2 GIA 线路 . 大多数情况下体验更佳！', 'https://g.ioiox.com/', 3, 0, '2020-08-31 23:01:46', '2020-09-25 08:44:40', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100383, 150725316141101040, 'GitHub代下', '/profile/site/system/github.png', '代下服务，永久免费！', 'http://gitd.cc/', 4, 0, '2020-08-31 23:03:00', '2020-09-25 08:45:21', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100384, 150725316141101040, 'GitHub加速链接生成工具', '/profile/site/system/github.png', '利用ucloud提供的GlobalSSH功能，对ssh协议数据进行加速！', 'https://github.zhlh6.cn/', 5, 0, '2020-08-31 23:04:04', '2020-09-25 08:46:18', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100385, 150725316141101017, 'SQL转Java', '/profile/site/system/3804cbf8-bb0e-437f-890e-7db6f6f778e0.jpeg', 'SQL转Java JPA、MYBATIS实现类代码生成平台。', 'https://java.bejson.com/generator/', 9, 0, '2020-09-07 10:12:34', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100386, 150725316141101011, 'M3U8播放器', '/profile/site/system/8b13f949-cc19-4882-aa8c-f236fbc04b84.jpeg', '电影、美剧、韩剧、漫画的直链', 'https://www.m3u8play.com/', 20, 0, '2020-09-08 20:33:58', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100387, 150725316141101017, '免费在线语音转文字', '/profile/site/system/bb15ab1d-410b-4242-ab81-50c1b6f1a81b.jpeg', '语音转文字，做纪录或者文字稿等', 'https://beecut.cn/speech-to-text-online', 10, 0, '2020-09-09 15:50:33', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100388, 150725316141101037, 'Wallpaper', '/profile/site/system/f32be96e-14c9-41cb-9217-ea8a28948c65.jpeg', '设计，室内设计，建筑，时尚，艺术', 'https://wall.alphacoders.com/?lang=Chinese', 7, 0, '2020-09-09 23:40:13', '2020-12-11 09:07:39', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100389, 150725316141101035, '视频号助手', '/profile/site/system/wxshipinghao.ico', '微信视频号助手', 'https://channels.weixin.qq.com/', 11, 0, '2020-09-11 20:52:11', '2020-09-11 20:55:03', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100390, 150725316141101005, 'DeepL翻译', '/profile/site/system/1cba9c99-73ef-422f-87ef-2e541bf82d1b.png', '将一段文字翻译到尽可能的通顺和便于理解，甚至是俚语、方言、名言名句、古诗词等内容都可以几乎没有任何语病的翻译出来。', 'https://www.deepl.com/translator', 5, 0, '2020-09-12 15:58:24', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100391, 150725316141101002, '搬书匠', '/profile/site/system/a7e8a0e2-ffe5-417c-bddf-43921d07554b.png', '编程书籍的好网站', 'http://www.banshujiang.cn/', 21, 0, '2020-09-13 14:43:50', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100392, 150725316141101035, '网易见外', '/profile/site/system/c6348b72-41e6-4237-9122-33b4c52cdf3e.png', '由人工智能事业部研发,是一个集视频听翻、直播听翻、语音转写、文档直翻功能为一体的AI智能语音转写听翻平台。', 'https://jianwai.youdao.com/', 10, 0, '2020-09-15 00:24:00', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100394, 150725316141101042, '若依 / RuoYi-Vue', '/profile/site/system/35196237-8319-4204-a844-bc06b4409a39.png', '基于SpringBoot，Spring Security，JWT，Vue & Element 的前后端分离权限管理系统 ', 'https://gitee.com/y_project/RuoYi-Vue', 1, 0, '2020-09-16 14:39:29', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100395, 150725316141101042, '若依 / RuoYi-Cloud', '/profile/site/system/35196237-8319-4204-a844-bc06b4409a39.png', '基于Spring Boot、Spring Cloud & Alibaba、OAuth2的分布式微服务架构权限管理系统 ', 'https://gitee.com/y_project/RuoYi-Cloud', 2, 0, '2020-09-16 14:41:32', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100396, 150725316141101042, '陌溪 / 蘑菇博客', '/profile/site/system/3247f776-f5a8-452a-998a-ebf411948607.png', '一个基于微服务架构的前后端分离博客系统。', 'https://gitee.com/moxi159753/mogu_blog_v2', 3, 0, '2020-09-16 14:43:45', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100397, 150725316141101042, 'FEBS-Cloud', '/profile/site/system/35fcf5e6-8980-4b4d-9da4-564b3bd3f333.png', '基于Spring Cloud Hoxton.RELEASE、Spring Cloud OAuth2 & Spring Cloud Alibaba & Element 微服务权限系统，开箱即用。', 'https://github.com/febsteam/FEBS-Cloud', 4, 0, '2020-09-16 14:46:37', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100398, 150725316141101042, '慕容若冰 / spring-microservice-exam', '/profile/site/system/15afba18-f1c2-4313-b664-ba8943a333a4.png', '硕果云，基于Spring Cloud搭建的新一代微服务教学管理平台，提供多租户、权限管理、考试、练习等功能，题型支持单选题、多选题、不定项选择题、判断题、简答题，二维码分享，移动端答题等 ', 'https://gitee.com/wells2333/spring-microservice-exam', 5, 0, '2020-09-16 14:48:27', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100399, 150725316141101032, '蘑菇博客', '/profile/site/system/3247f776-f5a8-452a-998a-ebf411948607.png', '专注于技术分享的博客平台', 'http://www.moguit.cn', 8, 0, '2020-09-16 14:54:32', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100400, 150725316141101035, 'QQ公众平台', '/profile/site/system/7424959f-6ada-48f9-9077-a7e0477e6e3d.png', 'QQ公众平台，为解决个人，企业，组织在QQ平台上的业务服务与用户管理提供实用的服务工具平台。', 'https://mp.qq.com', 14, 0, '2020-09-18 09:52:23', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100401, 150725316141101035, '抖音短视频', '/profile/site/system/b5541655-6a6f-47b9-8698-9bab021fa846.ico', '抖音短视频，一个旨在帮助大众用户表达自我，记录美好生活的短视频分享平台。为用户创造丰富多样的玩法，让用户在生活中轻松快速产出优质短视频。', 'https://www.douyin.com/', 15, 0, '2020-09-18 09:54:01', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100402, 150725316141101017, 'GitMind', '/profile/site/system/d3799599-6413-4944-a6f4-241e5c54c51c.png', '免费在线思维导图软件，简化逻辑梳理，集思广益，释放创造力在线脑图、思维导图、流程图、工业设计、工程管理，一图涵千面 ', 'https://gitmind.cn/', 11, 0, '2020-09-20 14:33:02', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100403, 150725316141101004, 'DocSmall', '/profile/site/system/380cb37a-370d-40df-8807-5c318e5310f2.png', '免费在线图片压缩、GIF压缩工具、PDF压缩工具、PDF合并工具、PDF分割工具', 'https://docsmall.com/', 8, 0, '2020-09-20 14:36:33', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100404, 150725316141101017, 'Dimmy', '/profile/site/system/12d7985a-2fc7-49b7-b6e6-55312ac1c550.png', '手机电脑等设备的展示模型，可以让你的图片放在电脑、手机、ipad等模型中展示，图片档次大大提升。', 'https://dimmy.club/', 12, 0, '2020-09-20 14:38:10', '2020-09-20 14:42:26', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100405, 150725316141101017, 'BrowserFrame', '/profile/site/system/251a15a3-811e-4359-98e2-f03b3319240e.ico', '浏览器展示模型工具', 'http://browserframe.com/', 13, 0, '2020-09-20 14:39:26', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100406, 150725316141101017, 'Flourish', '/profile/site/system/3416f7ae-1e74-45ef-b03d-4dfa730f1105.png', ' 数据可视化工具，快速地把表格数据转换为各种各样好看的图表，并且还支持动态可视化。', 'https://flourish.studio/', 14, 0, '2020-09-20 14:41:21', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100407, 150725316141101017, 'RemoveBg', '/profile/site/system/b2377b53-291b-4129-bdbe-68cb3a1fa7c3.png', '抠图神器，消除图片中的背景。', 'https://www.remove.bg/zh', 15, 0, '2020-09-20 14:44:42', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100408, 150725316141101018, 'Crx4Chrome', '/profile/site/system/af48b33f-ab08-4e71-a4de-32af6b930860.ico', ' Chrome浏览器插件站', 'https://www.crx4chrome.com/', 7, 0, '2020-09-20 14:46:03', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100409, 150725316141101043, '易视网', '/profile/site/system/e393d0d3-5a07-4c3a-a648-b5fa63b55fea.ico', '直播网络电视直播在线观看', 'https://www.cietv.com/', 1, 0, '2020-09-20 14:55:21', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100410, 150725316141101043, '好趣网', '/profile/site/system/f6ce09df-432f-4903-b308-99f65ab9504b.png', '2000套高清网络电视直播在线观看', 'http://tv.haoqu99.com/', 2, 0, '2020-09-20 14:56:41', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100411, 150725316141101042, 'EasyCaptcha', '/profile/site/system/c85348a4-7b34-488c-bd95-8b7b106f793d.png', 'Java图形验证码，支持gif、中文、算术等类型，可用于Java Web、JavaSE等项目。', 'https://gitee.com/ele-admin/EasyCaptcha', 6, 0, '2020-09-20 15:59:37', '2022-05-25 23:25:00', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100412, 150725316141101017, '短视频解析', '/profile/site/system/b0ffc6e6-6b42-4f5a-a221-44d9ff2c3ee7.png', '支持解析快手、抖音、Youtube、Tiktok、火山、今日头条、西瓜视频、皮皮虾、小咖秀、趣多拍、微视、美拍、秒拍、网易云、TikTok、哔哩哔哩、陌陌、映客、迅雷、阳光宽频、全民 K 歌、刷宝、WIDE 短视频、小红书、等平台的视频，而且解析出来的网站视频没有水印。', 'http://www.dspjx.com/', 16, 0, '2020-09-23 23:57:07', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100413, 150725316141101008, '微 PE 工具箱', '/profile/site/system/93975589-a3c2-4540-9893-69a0149d6435.png', '微 PE 工具箱 就是一款常用的 Windows PE 工具包，支持 Windows 10，提供了 32/64 位版本，并且支持 NVME 硬盘。', 'http://www.wepe.com.cn/', 8, 0, '2020-09-23 23:59:35', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100414, 150725316141101017, 'GeoGebra', '/profile/site/system/68292c53-cd3e-4a4a-9a4e-7d67359fb1e6.png', '一款结合几何、代数与微积分的免费动态数学软件，也支持在线直接绘图 /计算。', 'https://www.geogebra.org', 17, 0, '2020-09-24 00:01:36', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100416, 150725316141101017, 'Maven 仓库', '/profile/site/system/4242630d-0216-4950-9646-d3ee647ba8e3.png', 'Jar 下载，Jar 依赖引用', 'https://mvnrepository.com/', 19, 0, '2020-09-24 08:41:01', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100418, 150725316141101004, '萝卜工坊', '/profile/site/system/a970e871-9f79-4caa-a09e-df2fb7c9ddc2.png', '快速转换模拟手写字体文档，让打印出的字看起来像手写的 一个软件在线解决文字抄写烦恼', 'http://www.beautifulcarrot.com/', 9, 0, '2020-09-24 08:52:18', '2020-09-24 08:53:58', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100419, 150725316141101004, '二维码解码器', '/profile/site/system/989df62a-e0e5-468f-8fc7-f4b766d27ddd.png', '在线二维码解析和生成', 'https://jiema.wwei.cn/', 10, 0, '2020-09-24 08:57:01', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100420, 150725316141101004, 'PickFrom', '/profile/site/system/12736188-5677-410f-a628-1bab12ab00d9.ico', '一站式视频剪辑平台，让工作更简单。', 'https://zh.pickfrom.net/', 11, 0, '2020-09-24 08:59:06', '2020-09-24 08:59:32', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100421, 150725316141101004, '转字体', '/profile/site/system/89d9be4b-cb72-45ac-a30d-def2c1e30e1c.ico', '简体字繁体字互转', 'https://www.aies.cn/', 12, 0, '2020-09-24 09:02:38', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100422, 150725316141101040, 'GitHub 文件加速', '/profile/site/system/github.png', '利用 Cloudflare Workers 对 github release 、archive 以及项目文件进行加速，部署无需服务器且自带CDN。', 'https://gh.api.99988866.xyz/', 6, 0, '2020-09-25 08:41:24', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100427, 150725316141101037, '美桌网', '/profile/site/system/7510faa4-8159-49be-ae55-02629202fbe3.ico', '陪你下载生活的美！', 'http://www.win4000.com/', 8, 0, '2020-09-29 14:43:48', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100428, 150725316141101037, '回车桌面', '/profile/site/system/d40daccb-e4d7-4f34-a909-0341e49cd238.ico', '动画、漫画、卡通、锁屏图片、高清手机壁纸！', 'https://www.enterdesk.com/', 9, 0, '2020-09-29 14:45:43', '2020-09-29 14:47:00', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100429, 150725316141101037, '全面屏壁纸', '/profile/site/system/1ab3ecc5-ebc6-474a-814e-fa57b3781edb.ico', '专为全面屏和刘海屏手机适配的2K超高清壁纸网站！', 'http://m.bcoderss.com/', 10, 0, '2020-09-29 14:48:48', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100430, 150725316141101011, '无限影视网', '/profile/site/system/2c0a0e63-e07a-4344-aa11-e461e9616fee.png', '百万影片任你搜索！', 'https://www.wxtv.net/', 21, 0, '2020-09-30 14:36:33', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100432, 150725316141101008, '不忘初心', '/profile/site/system/9b57a3c2-adc0-4dc9-a3d4-8d573be1fce0.png', '精简版系统官网。从心出发，专注精简系统！', 'https://www.pc521.net/', 10, 0, '2020-10-09 16:56:49', '2021-01-27 10:09:41', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100433, 150725316141101036, '免费版权图片', '/profile/site/system/19e0d9d2-cf36-4046-a2d3-80a028b789a3.jpg', '一键搜索多家免版权图库，再也不用担心商用图片侵权了 ！', 'https://www.logosc.cn/so/', 17, 0, '2020-10-09 17:25:56', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100434, 150725316141101036, 'ColorHub', '/profile/site/system/a51c8e1f-bc7b-4b91-8fcb-9511cc234682.png', '高清无版权图片，个人和商业用途免费！', 'https://colorhub.me/', 18, 0, '2020-10-09 17:28:50', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100435, 150725316141101036, 'Hippopx', '/profile/site/system/c44b31d2-6a73-4879-84da-6a2e909d8a8f.ico', '基于CC0协议的免版权图库！', 'https://www.hippopx.com/zh/', 19, 0, '2020-10-09 17:30:41', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100436, 150725316141101036, 'FreePik', '/profile/site/system/45c6ebba-1fa4-4728-8210-9f9218ecc6cd.jpg', '查找免费矢量，图库照片，PSD和图标！', 'https://www.freepik.com/', 20, 0, '2020-10-09 17:34:34', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100438, 150725316141101011, '云播TV', '/profile/site/system/e7d75bda-6468-48f9-9dee-84a7b304b420.png', '一个单纯不做作的电影站！', 'https://www.yunbtv.net/', 24, 0, '2020-10-09 23:47:22', '2022-05-07 23:30:12', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100440, 150725316141101011, '音范丝', '/profile/site/system/e8eb094d-efb6-40b9-886e-3ab03871950d.ico', '高清无水印，影音集、精选4K蓝光原盘下载，顶级收藏！', 'http://www.yinfans.me/', 26, 0, '2020-10-12 14:14:54', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100441, 150725316141101011, '腾讯视频WeTV', '/profile/site/system/fe2b466d9fe6bed552c0adcaac1c2813.ico', '腾讯视频海外版，无广告，而且可以免费观看1080P！', 'https://wetv.vip/zh-cn', 4, 0, '2020-10-12 20:45:11', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100442, 150725316141101017, '千千秀字', '/profile/site/system/fbdf9a0d-88d9-4fcc-88cc-c63a49ea1eab.png', '提供文字翻译、字体转换、字效生成等在线服务的同时，也关注着文字的历史和文字的各行应用。', 'https://www.qqxiuzi.cn/', 20, 0, '2020-10-12 23:10:47', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100443, 150725316141101002, '搜韵', '/profile/site/system/6b06e347-bfe9-4230-92e4-d4caba9b6747.png', '诗词门户网站，可检索，分经史子集和四库之外等，图像清晰，速度快，非常便捷', 'https://sou-yun.cn/', 22, 0, '2020-10-16 19:08:14', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100445, 150725316141101046, '老殁', '/profile/site/system/22a74e7f-c58c-4914-86d6-4f557f572df9.png', '免费推荐优秀软件', 'https://www.mpyit.com', 1, 0, '2020-10-16 21:43:27', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100446, 150725316141101046, '果核剥壳', '/profile/site/system/1796f330-4e4f-49e6-819b-9810fca40735.png', '还原软件的本质，纯净软件分享，守住互联网最后的一片净土', 'https://www.ghpym.com', 5, 0, '2020-10-16 21:49:17', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100447, 150725316141101046, '423Down', '/profile/site/system/e005555a-a43c-423a-b74d-375b3867e442.ico', '有品质的电脑软件、Android软件分享博客', 'https://www.423down.com', 6, 0, '2020-10-16 21:51:13', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100448, 150725316141101046, '心海e站', '/profile/site/system/69c513c7-6150-4f58-bbf6-f866718b9238.jpg', '发布由烈火汉化的一些实用的软件，全部免费，杜绝广告！', 'https://hrtsea.com', 7, 0, '2020-10-16 21:53:50', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100449, 150725316141101046, '落尘之木', '/profile/site/system/292ba100-2d1a-4f63-acde-9c0f2c02bfe1.png', '分享互联网优秀软件、电脑经验、技术交流、IT类', 'https://www.luochenzhimu.com', 8, 0, '2020-10-16 21:55:28', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100450, 150725316141101046, '易破解', '/profile/site/system/ef2b424c-cd7a-43fd-95dd-b6093cdf8854.ico', '给你所需要的内容', 'https://www.ypojie.com', 9, 0, '2020-10-16 21:56:42', '2021-01-13 15:34:19', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100451, 150725316141101046, 'QQ前线乐园', '/profile/site/system/1cef9d6a-2f6d-42a3-b133-a59ccea947eb.png', '专注于分享，分享好资源。', 'https://www.yijingying.com', 10, 0, '2020-10-16 21:58:21', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100452, 150725316141101046, '风刑软件站', '/profile/site/system/17597510-396f-41a5-abf2-41c847c0fa62.png', '一个满载优秀、严谨、开放的软件下载平台', 'https://www.wsf1234.com', 11, 0, '2020-10-16 22:01:28', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100453, 150725316141101046, '软件缘', '/profile/site/system/172e5128-b12a-45b9-8de2-cbf906d53164.png', '软件缘 - 精品绿软，品鉴独特', 'https://www.appcgn.com', 12, 0, '2020-10-16 22:03:19', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100454, 150725316141101010, 'XClient', '/profile/site/system/12f29237-4138-4c75-815e-58e660c4a8c0.ico', '精品MAC应用分享', 'https://xclient.info', 4, 0, '2020-10-16 22:05:32', '2021-01-13 15:35:24', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100455, 150725316141101046, '孤独求软', '/profile/site/system/41f3a0c6-f433-45db-8a55-4c8a00b28e8a.ico', '常用软件一站齐全', 'http://www.dugubest.com', 13, 0, '2020-10-16 22:06:56', '2021-01-13 15:34:39', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100456, 150725316141101010, '史蒂芬周的博客', '/profile/site/system/8b57d624-b4e1-48e3-8307-0c10b393ccb9.ico', '软硬兼施，娱乐共享。', 'http://www.sdifen.com', 5, 0, '2020-10-16 22:08:45', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100457, 150725316141101010, '苹果软件盒子', '/profile/site/system/2799a66d-646d-445f-87b8-b7335826b812.png', '分享优质 Mac', 'https://www.macappbox.com', 6, 0, '2020-10-16 22:10:15', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100458, 150725316141101046, 'Extfans 扩展迷', '/profile/site/system/bc0605a7-3a50-4840-b37e-d1ac8386ae84.ico', '好用的浏览器插件推荐', 'https://www.extfans.com', 14, 0, '2020-10-16 22:57:50', '2021-01-13 15:34:45', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100459, 150725316141101047, 'Golink加速器', '/profile/site/system/1432bb7a-5e20-446e-9ba7-72856d445abb.ico', '国内首款免费游戏加速器', 'https://www.golink.com', 1, 0, '2020-10-16 23:01:15', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100460, 150725316141101047, '流星游戏加速器', '/profile/site/system/c4597214-2353-414c-b9a2-af8d0ce250d2.ico', '真免费,为痛快!', 'https://www.liuxing.com', 2, 0, '2020-10-16 23:02:15', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100461, 150725316141101047, '奇妙网游加速器', '/profile/site/system/f00c3d4a-e411-42d5-9b26-fda14cc9dd68.png', '真正免费，低延时，真专线，真稳定，真好用', 'https://www.qimiao.com', 3, 0, '2020-10-16 23:03:41', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100463, 150725316141101047, '小霸王', '/profile/site/system/65fcca63-5610-4e2d-8ab9-9084c8b68e58.png', '找回童年的快乐 ', 'https://www.yikm.net', 5, 0, '2020-10-16 23:08:38', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100464, 150725316141101047, '侠聚网', '/profile/site/system/6b430ae2-c72b-45ac-829d-ea3ecd5551ee.png', '免费的Android游戏修改神器，内置海量游戏下载', 'http://www.huluxia.com', 6, 0, '2020-10-16 23:09:48', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100465, 150725316141101047, '骑士下载', '/profile/site/system/193635d3-0fff-4886-ac47-e801ec314ed1.png', '好玩的安卓游戏免费下载', 'https://www.vqs.com', 7, 0, '2020-10-16 23:11:23', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100466, 150725316141101047, 'TapTap', '/profile/site/system/6558f840-b408-4ee1-a019-9514d107c7f8.ico', '推荐高质量好玩的手机游戏', 'https://www.taptap.com', 8, 0, '2020-10-16 23:12:31', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100471, 150725316141101011, '思古影视', '/profile/site/system/5c8372f4-bd97-4f95-a927-665675f1b8e1.ico', '高清免费VIP视频在线解析', 'https://www.sigu.cc/', 28, 0, '2020-10-20 21:32:59', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100472, 150725316141101017, '文档免费下载', '/profile/site/system/7c70855c-a7c2-4377-8e4b-8837a688b43b.ico', '从此,下载百度文库文档变得简单', 'https://doc.chaney.top/', 21, 0, '2020-10-25 11:23:39', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100473, 150725316141101009, 'ROM乐园', '/profile/site/system/34cfc67c-7490-4f5f-b7fb-a0b1f3d812a7.jpeg', '专注于打造全网优质特色ROM刷机包下载官方网站', 'http://www.romleyuan.com', 6, 0, '2020-10-27 23:23:28', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100477, 150725316141101032, '格姗知识圈', '/profile/site/system/logo.jpg', '我的博客网站，专注于技术分享、实用工具与技巧的博客平台！', 'https://geshanzsq.com', 1, 0, '2020-11-29 17:13:06', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100478, 150725316141101037, '人工桌面', '/profile/site/system/3982b997-0e85-4573-bb8c-ccf4db4bf83d.ico', '简洁又可爱的萌妹桌面软件。绝对是美女帅哥、宅男宅女的喜爱。', 'https://lumi.mihoyo.com/#/', 0, 0, '2020-12-03 13:39:48', '2020-12-03 13:39:55', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100479, 150725316141101037, '故宫壁纸', '/profile/site/system/6dd9f9cc-d49e-4b32-abcd-5eba13580739.ico', '将历史的精彩收集到自己的手中', 'https://www.dpm.org.cn/lights/royal.html', 11, 0, '2020-12-11 09:10:22', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100480, 150725316141101037, 'CGWallpapers', '/profile/site/system/0e5e058c-bf93-4c40-b327-b4064a023560.ico', ' 游戏CG壁纸站，超细腻，真假难分', 'https://www.cgwallpapers.com/', 12, 0, '2020-12-11 09:12:58', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100481, 150725316141101042, '格姗导航 / geshanzsq-nav', '/profile/site/system/logo.jpg', '一个基于 Spring Boot + Vue 前后端分离的导航网站！', 'https://gitee.com/geshanzsq/geshanzsq-nav', 0, 0, '2021-01-05 17:15:46', '2022-11-03 21:45:09', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100483, 150725316141101048, '剪映', '/profile/site/system/3bcc9224-2291-4f5c-8675-e10c1c0f570d.jpeg', '轻而易剪，上演大幕。', 'https://lv.ulikecam.com/', 1, 0, '2021-01-07 20:05:59', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100484, 150725316141101048, 'InShot', '/profile/site/system/9cdf91f4-530d-49b2-bdf1-1d4b4af68c8a.ico', '专业视频剪辑工具，风靡全球。', 'http://inshotapp.com/', 2, 0, '2021-01-07 20:06:02', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100485, 150725316141101048, '来画', '/profile/site/system/544f582c-1fa5-41e4-a5c5-65a1b1d9503d.ico', '像做PPT一样做视频', 'https://www.laihua.com/', 3, 0, '2021-01-07 20:06:05', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100486, 150725316141101048, 'VUE VLOG', '/profile/site/system/d725ef43-e3c5-42a9-9289-b8b6cc91137d.ico', '用 Vlog 记录生活', 'https://vuevideo.net/', 4, 0, '2021-01-07 20:06:07', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100487, 150725316141101048, '字说', '/profile/site/system/410577d5-312c-4674-8bf1-ef5177b194b3.ico', '字说  文字动画视频制作神器', 'https://h5.zishuovideo.com/', 5, 0, '2021-01-07 20:06:10', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100488, 150725316141101048, '轻剪辑', '/profile/site/system/e394ad53-a0a7-4929-b20c-c0134f4d436d.ico', '在线视频剪辑神器 无需软件下载 1分钟轻松制作精彩视频', 'https://e.chuanying520.com/', 6, 0, '2021-01-07 20:06:12', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100489, 150725316141101048, '万兴喵影', '/profile/site/system/62f04881-b9fc-42fb-bf75-c14eaba90672.png', '风靡全球的国产剪辑神器 用心剪辑美好生活', 'https://miao.wondershare.cn/', 7, 0, '2021-01-07 20:06:14', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100490, 150725316141101048, 'Animaker', '/profile/site/system/24044bfd-1173-49de-aede-11e2d5592a77.jpeg', '超好用的动画短视频工具', 'https://www.animaker.com/', 8, 0, '2021-01-07 20:06:16', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100491, 150725316141101048, '飞推', '/profile/site/system/3a1d5950-9e7a-49ef-a68c-a304f0a450f1.png', '创意视频制作神器 视频制作从未如此简单!', 'https://www.qutui360.com/', 9, 0, '2021-01-07 20:06:18', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100492, 150725316141101048, 'Premiere', '/profile/site/system/17f4dcb9-4881-4e4b-83d9-9da269d0af2e.ico', '始终更胜一筹的视频编辑', 'https://www.adobe.com/cn/products/premiere.html', 10, 0, '2021-01-07 20:06:20', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100493, 150725316141101048, 'After Effects', '/profile/site/system/17f4dcb9-4881-4e4b-83d9-9da269d0af2e.ico', '制作气势恢宏的大场面', 'https://www.adobe.com/cn/products/aftereffects.html', 11, 0, '2021-01-07 20:06:22', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100494, 150725316141101048, '爱剪辑', '/profile/site/system/910ecf63-8077-427b-987b-98a1f206cc62.ico', '全民流行的视频剪辑软件', 'http://www.ijianji.com/', 12, 0, '2021-01-07 20:06:24', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100495, 150725316141101048, '快剪辑', '/profile/site/system/929c3691-b128-4561-b836-2664bcee50a3.ico', '一触即发，分享你的创意灵感', 'https://kuai.360.cn/home.html', 13, 0, '2021-01-07 20:06:27', '2021-01-07 20:06:50', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100496, 150725316141101035, '知识星球', '/profile/site/system/b5fa8f88-5312-4fb6-8183-af53a748f0a1.ico', '深度连接铁杆粉丝，运营高品质社群，知识变现的工具。', 'https://www.zsxq.com', 17, 0, '2021-01-11 17:54:25', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100497, 150725316141101049, '公益图床', '/profile/site/system/a9532e0c-b22c-4c15-9441-b3293465a7a8.png', '国内图床，速度飞快，快人一步。', 'https://sbimg.cn', 1, 0, '2021-01-12 16:32:07', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100498, 150725316141101049, 'SM.MS图床', '/profile/site/system/de98806e-8b41-4351-a8c5-9f307a4ad1d3.ico', '免费用户无香港节点，速度较慢', 'https://sm.ms', 2, 0, '2021-01-12 16:40:09', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100499, 150725316141101049, '路过图床', '/profile/site/system/13322f82-052a-496e-8d14-39d678745466.png', '免费图片上传，高速稳定的图片上传和外链服务。', 'https://imgchr.com/', 3, 0, '2021-01-12 16:41:10', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100500, 150725316141101030, '极客时间', '/profile/site/system/5ab40821-6da7-4c81-8c56-62ba2598a9ab.jpg', '轻松学习，高效学习！', 'https://time.geekbang.org', 5, 0, '2021-01-13 11:16:54', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100501, 150725316141101031, '极客时间', '/profile/site/system/5ab40821-6da7-4c81-8c56-62ba2598a9ab.jpg', '轻松学习，高效学习', 'https://time.geekbang.org', 8, 0, '2021-01-13 11:17:53', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100502, 150725316141101011, '芒果TV', '/profile/site/system/ef97cc86-01f6-4109-a23c-5613099d0d67.ico', '天生青春', 'https://www.mgtv.com', 6, 0, '2021-01-13 15:25:40', '2021-01-13 15:25:55', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100504, 150725316141101030, '码云 Gitee', '/profile/site/system/ec70a38c-e700-4181-a368-c1b9a12d09b9.ico', '基于 Git 的代码托管和研发协作平台。帮助开发者/团队/企业更好地管理代码，让软件研发更高效', 'https://gitee.com/', 7, 0, '2021-01-15 10:52:50', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100505, 150725316141101030, 'Github', '/profile/site/system/a81a692b-999c-4018-ba1b-9e0156b30af9.svg', '数以百万计的开发人员和公司在github上构建、发布和维护他们的软件，github是世界上最大、最先进的开发平台。', 'https://github.com', 8, 0, '2021-01-15 10:54:32', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100506, 150725316141101035, '网易号', '/profile/site/system/5956e442-9f8b-4516-9dcc-19ddcf06f7af.png', '媒体开放平台。', 'http://mp.163.com', 18, 0, '2021-01-18 10:16:24', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100507, 150725316141101035, '大鱼号', '/profile/site/system/6469fa53-6822-4f50-aca2-a23c7d41acce.ico', '一点接入，多点分发。移动世界，无所不达', 'https://mp.dayu.com', 19, 0, '2021-01-18 10:19:32', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100508, 150725316141101035, '一点资讯', '/profile/site/system/7bccd2ec-e385-497d-a1f8-655a7a8a0197.ico', '阅不同，更有趣', 'http://www.yidianzixun.com', 20, 0, '2021-01-18 10:20:54', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100509, 150725316141101035, '搜狐号', '/profile/site/system/b6021b69-2035-4f9c-8e1e-ea54c1d74817.ico', '再小的个体，也能获取影响力', 'https://mp.sohu.com/mpfe/v3/login', 21, 0, '2021-01-18 10:22:30', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100510, 150725316141101050, '印象笔记', '/profile/site/system/4e5ed731-1d90-4586-baff-c3bb89d4be8b.ico', '工作必备效率应用', 'https://www.yinxiang.com/', 1, 0, '2021-01-18 10:28:44', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100511, 150725316141101050, '有道云笔记', '/profile/site/system/43f327f6-631b-4280-a7af-a01ed8c505b6.ico', '5000万用户的选择', 'http://note.youdao.com', 2, 0, '2021-01-18 10:29:24', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100512, 150725316141101050, 'Tower', '/profile/site/system/136c7f93-cbc7-4daa-9edc-73840b2c2b6e.ico', '提升协作效率，打造高效团队', 'https://tower.im', 3, 0, '2021-01-18 10:29:58', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100513, 150725316141101050, '为知笔记', '/profile/site/system/09f19706-7536-4cdd-aeaa-97d64c89a9b4.ico', '一键收藏、全端全文检索、多级目录、Markdown', 'http://www.wiz.cn', 4, 0, '2021-01-18 10:31:26', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100514, 150725316141101050, '石墨文档', '/profile/site/system/9412b0ba-72eb-4751-a1ab-1517c9678eb9.ico', '企业在线协同办公系统平台,支持云端多人在线协作编辑文档和表格', 'https://shimo.im', 5, 0, '2021-01-18 10:32:00', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100515, 150725316141101050, '锤子便签', '/profile/site/system/e6cc2bbc-e2fb-4cd5-a3cd-b828715fb834.ico', '可能是史上最漂亮的便签应用，你或许会因它重新喜欢上记录和表达', 'https://www.smartisan.com/apps/#/notes', 6, 0, '2021-01-18 10:32:34', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100516, 150725316141101050, '腾讯文档', '/profile/site/system/d9222fdb-cd48-43bf-bc1e-b40a79bc8eb2.ico', '支持多人在线编辑Word、Excel和PPT文档', 'https://docs.qq.com', 7, 0, '2021-01-18 10:33:28', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100517, 150725316141101050, 'Teambition', '/profile/site/system/cd0cb55f-c569-40cf-831e-a4a3f73c6dff.ico', '一套聪明好用的日常工具，包含项目、待办、网盘、文档、日历等丰富应用，帮助你把想法变成现实，使用起来爱不释手。', 'https://www.teambition.com', 8, 0, '2021-01-18 10:34:08', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100518, 150725316141101050, 'Google Docs', '/profile/site/system/1a7be2d2-a468-4ce1-8cd9-9243f265b598.jpeg', '谷歌在线文档', 'https://docs.google.com', 9, 0, '2021-01-18 10:37:08', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100519, 150725316141101050, 'WPS云文档', '/profile/site/system/28edf917-58d1-4739-922f-27f9f9c889f0.ico', '多人实时协作的在线Office', 'https://www.kdocs.cn', 10, 0, '2021-01-18 10:38:17', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100520, 150725316141101030, 'JustAuth', '/profile/site/system/4473999c-1dee-4b47-bbff-155b1785085e.png', '史上最全的整合第三方登录的开源库', 'https://www.justauth.cn/', 9, 0, '2021-01-18 17:03:08', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100521, 150725316141101029, 'PPT超级市场', '/profile/site/system/334c508a-a599-4a25-8e2b-e037de63dba6.ico', '完全免费的PPT模板下载网站。量都是极高，并且非常精美。', 'http://ppt.sotary.com/web/wxapp/index.html', 7, 0, '2021-01-20 11:02:53', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100522, 150725316141101046, '原版系统', '/profile/site/system/3d2a5e29-be0a-4cdb-826d-e063e051a79a.ico', '提供可靠的原版软件。十二年的专注和积累，初心未改，打造下一个里程碑。 ', 'https://next.itellyou.cn/', 0, 0, '2021-01-22 11:02:37', '2021-01-22 11:03:29', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100523, 150725316141101012, 'HiFiNi - 音乐磁场', '/profile/site/system/fdd671e9-b241-462f-a259-d8d7d5a2596e.png', '一个由音乐爱好者维护的分享平台', 'https://www.hifini.com/', 11, 0, '2021-01-25 16:01:45', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100524, 150725316141101037, '必应壁纸', '/profile/site/system/ac326879-5ed4-4ef7-9476-00793db5a0a3.png', '超高质量的必应壁纸4K无水印下载', 'https://www.todaybing.com/', 13, 0, '2021-01-29 14:36:42', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100525, 150725316141101051, '火烧云数据', '/profile/site/system/25f491c8-2164-4d18-aeca-c6aa2ccbc8a7.png', '专业的B站第三方大数据分析平台，助力UP主/MCN 快速涨粉，商业变现；助力品牌方/广告公司洞察竞品投放情报，匹配优质UP主，投放更精准出效', 'http://www.hsydata.com/home/index', 1, 0, '2021-02-01 14:35:32', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100526, 150725316141101035, 'B站创作中心', '/profile/site/system/179f1fce-c229-465c-83b8-0bb2caa9eb2c.ico', '国内知名的视频弹幕网站，这里有最及时的动漫新番，最棒的ACG氛围，最有创意的Up主。大家可以在这里找到许多欢乐。', 'https://member.bilibili.com/', 16, 0, '2021-02-01 15:03:18', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100527, 150725316141101035, '大风号', '/profile/site/system/839113b2-755e-422c-9130-ae6544bc1cb7.png', '好内容随风直达', 'https://fhh.ifeng.com/', 22, 0, '2021-02-01 15:09:36', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100528, 150725316141101029, '第一PPT', '/profile/site/system/a6c796a7-9341-4103-b033-49e99db328f7.png', 'PPT模版免费下载', 'http://www.1ppt.com/', 8, 0, '2021-02-02 09:12:17', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100529, 150725316141101029, '51PPT', '/profile/site/system/bb66b865-a667-46b1-acf2-8b8c6da6ec87.png', '幻灯片演示模板及素材下载', 'http://www.51pptmoban.com/', 9, 0, '2021-02-02 09:14:05', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100530, 150725316141101029, '叮当设计', '/profile/site/system/3c5526a5-70fc-4f67-9ad5-65e301991399.png', '分享优秀设计资源，全部免费下载', 'https://www.dingdangsheji.com/', 10, 0, '2021-02-02 09:15:24', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100531, 150725316141101048, 'SubPlayer', '/profile/site/system/6cee691b-9b0d-4a14-b755-ed9540e92bc0.ico', '字幕在线调整，小巧实用性非常强的网站', 'https://subplayer.js.org/', 14, 0, '2021-03-02 15:53:46', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100532, 150725316141101012, '在线音乐播放', '/profile/site/system/87ace2f3-0c6f-455f-97bc-789f0b00f846.png', '可以在线听，可以直接下载。麻雀虽小，五脏俱全。', 'https://www.binye.xyz/Music/', 12, 0, '2021-03-05 17:54:33', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100534, 150725316141101051, 'Versus', '/profile/site/system/a43adc92-08cd-4b2b-af7e-667e216efa08.png', '万物皆可对比。移动电话、城市、显示卡、大学及及其他', 'https://versus.com/cn', 2, 0, '2021-03-21 19:23:36', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100535, 150725316141101012, '52无损音乐', '/profile/site/system/2cb30771-c5bc-4519-a772-b47abb64c085.png', '无损音乐下载_FLAC_WAV_高品质格式无损音乐免费下载', 'http://www.52flac.com/', 13, 0, '2021-03-23 23:48:01', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100536, 150725316141101012, '炫音音乐论坛', '/profile/site/system/325b7386-aefc-41ee-be4a-63a228de00fc.png', '总有一种声音能打动你', 'https://bbs.musicool.cn/', 14, 0, '2021-03-23 23:49:54', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100538, 150725316141101052, '搜图导航', '/profile/site/system/09fbdff3-6b8e-4c39-ac01-d3356098529b.png', '解决您的搜图需求，包含大量图片网站', 'https://gesdh.cn/share/st', 2, 0, '2021-03-27 00:38:11', '2023-05-19 21:05:00', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100539, 150725316141101052, '自媒体导航', '/profile/site/system/3a9c73f6-0656-40b8-9789-f5f0f55be1f5.png', '自媒体专业导航，包含运营平台、排版工具、图片素材等', 'https://gesdh.cn/share/S8F3C', 3, 0, '2021-03-27 00:41:37', '2023-05-19 21:05:00', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100540, 150725316141101052, '视频号导航', '/profile/site/system/12448992-5513-4826-836c-afa86cb16d3e.png', '视频号相关网站，包括玩家必备、数据分析等', 'https://gesdh.cn/share/shipinhao', 4, 0, '2021-03-27 00:44:04', '2023-05-19 21:05:00', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100541, 150725316141101052, '视频创作导航', '/profile/site/system/dbbe4e57-13aa-4d16-b863-d4c0e74a83c5.png', '好用又专业的视频制作导航网站', 'https://gesdh.cn/share/sp', 5, 0, '2021-03-27 00:45:42', '2023-05-19 21:05:00', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100542, 150725316141101052, '创业神器导航', '/profile/site/system/801ef7f2-28af-4f99-8f52-db7d138d4235.png', '分享创业资源和互联网工具', 'https://gesdh.cn/share/cysq', 7, 0, '2021-03-27 00:51:44', '2023-05-19 21:05:00', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100543, 150725316141101052, '排行榜导航', '/profile/site/system/69a024f8-cada-46c4-9150-fecb932115d0.png', '各类榜单排名大全，包括热搜榜、热议榜等', 'https://gesdh.cn/share/phb', 6, 1, '2021-03-27 00:55:01', '2023-05-19 21:05:00', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100544, 150725316141101052, '运营辅助工具导航', '/profile/site/system/46e442b9-e4cc-43f5-8997-89bccd50b803.png', '分享运营相关网站', 'https://gesdh.cn/share/yyfzgj', 8, 0, '2021-03-27 00:56:26', '2023-05-19 21:05:00', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100545, 150725316141101052, '程序员导航', '/profile/site/system/f741cece-de4a-4e46-a237-07a0ad90c303.png', '程序员自用网站，包括常用推荐、学习教程等', 'https://gesdh.cn/share/6hMpM', 9, 0, '2021-03-27 00:58:32', '2023-05-19 21:05:00', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100546, 150725316141101004, 'PDF 派', '/profile/site/system/2ececa01-18ea-45f5-a168-8421def8ff3d.ico', '几十个强大的PDF在线工具，永久免费，没有注册入口，人人都是VIP！', 'https://www.pdfpai.com/', 8, 0, '2021-04-20 11:42:52', '2021-04-20 11:43:17', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100547, 150725316141101029, '微软 PPT', '/profile/site/system/2bd1b9b8-e0be-4107-8fe5-3550a619fe1d.ico', 'Office 模板和主题，使用 Microsoft 模板创建更多内容', 'https://templates.office.com/', 11, 0, '2021-04-20 11:45:53', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100548, 150725316141101029, 'Slidesgo', '/profile/site/system/24475701-da5f-4aa3-852e-033f55668ed9.png', '免费的谷歌幻灯片和PowerPoint模板，让你的演讲更精彩', 'https://slidesgo.com/', 12, 0, '2021-04-20 11:47:41', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100549, 150725316141101029, 'Hislide', '/profile/site/system/13f71e2b-7687-47ea-861a-ccf4caf2f60b.png', '免费的PowerPoint，谷歌幻灯片，Keynote模板', 'https://hislide.io/', 13, 0, '2021-04-20 11:48:58', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100550, 150725316141101029, 'Rrslide', '/profile/site/system/efad445e-1b5c-4e4c-9afc-0b6b21972b05.png', '下载免费的ppt模板，基调，主题和更多_ RRSlide _最畅销的演示模板市场', 'https://rrslide.com/', 14, 0, '2021-04-20 11:51:32', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100553, 150725316141101036, '图司机', '/profile/site/system/0478f006-ff16-4504-963f-8b28fd24af6d.png', '免费图片在线PS编辑器，10秒搞定平面设计！', 'https://www.tusij.com/', 0, 0, '2021-04-22 21:32:27', '2021-04-22 21:32:35', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100554, 150725316141101050, 'ProcessOn免费在线作图', '/profile/site/system/25fc6979-4a05-49ab-a9dc-3cc0daa76e0c.ico', '在线作图工具的聚合平台， 它可以在线画流程图、思维导图、UI原型图、UML、网络拓扑图、组织结构图等等， 您无需担心下载和更新的问题，不管Mac还是Windows，一个浏览器就可以随时随地的发挥创意，规划工作', 'https://processon.com', 11, 0, '2021-05-13 23:22:40', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100555, 150725316141101042, 'JustAuth', '/profile/site/system/fdf62f71-41e8-4454-8dbd-f1272ed9eac5.png', '小而全而美的第三方登录开源组件。目前已支持Github、Gitee、微博、钉钉、百度、Coding、腾讯云开发者平台、OSChina、支付宝、QQ、微信、淘宝、Google、Facebook、抖音、领英、小米、微软、今日头条、Teambition、StackOverflow、Pinterest、人人、华为、企业微信、酷家乐、Gitlab、美团、饿了么、推特、飞书、京东、阿里云、喜马拉雅、Amazon、Slack和 Line 等第三方平台的授权登录。', 'https://gitee.com/yadong.zhang/JustAuth', 8, 0, '2021-05-31 23:11:03', '2021-05-31 23:11:52', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100556, 150725316141101050, '语雀', '/profile/site/system/6da5aba9-a474-4fb4-82b0-880cfc32237e.png', '专业的云端知识库，个人笔记与知识创作，团队协同与知识沉淀。', 'https://www.yuque.com/', 12, 0, '2021-06-05 10:20:20', '2021-06-12 14:32:16', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100557, 150725316141101048, '爱给网', '/profile/site/system/fec4eb53-e5d2-4cb5-a16e-5ed41e4cc1d4.ico', '音效配乐_3D模型_视频素材_游戏素材。中国最大的数字娱乐免费素材下载网站,免费提供免费的音效配乐|3D模型|视频|游戏素材资源下载。', 'https://www.aigei.com/', 15, 0, '2021-07-25 11:42:08', '2021-07-25 11:44:39', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100558, 150725316141101017, 'IT工具网', '/profile/site/system/a3ccbeef-5ba8-46d3-bf9b-07cc90aa202c.ico', '在线实用工具_代码工具_写作辅助工具', 'https://www.coder.work', 23, 0, '2021-08-02 21:45:14', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100559, 150725316141101037, '电脑壁纸', '/profile/site/system/25fd04c8-3419-4919-ac66-8c6dd822898e.png', '一个自采集壁纸站', 'http://lab.mkblog.cn/wallpaper/', 14, 0, '2021-08-11 22:57:13', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100560, 150725316141101037, '3G壁纸', '/profile/site/system/628bf055-387d-49a5-b403-5292f79354be.png', '电脑壁纸专家', 'https://desk.3gbizhi.com/', 15, 0, '2021-08-11 23:02:53', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100562, 150725316141101048, '混剪侠 预告片世界', '/profile/site/system/e9b3cfca-739b-427b-b52b-bdb6899b2945.png', '最新电影预告片免费下载', 'https://www.yugaopian.cn/', 16, 0, '2021-09-07 00:05:24', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100563, 150725316141101042, 'vue-admin-beautiful', '/profile/site/system/4864e82c-69e2-4868-8501-0c9a06ff56c6.png', '国内首个基于vue3.0的开源admin项目，同时支持电脑，手机，平板', 'https://gitee.com/chu1204505056/vue-admin-beautiful', 9, 0, '2021-09-27 07:55:13', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100566, 150725316141101001, '奈斯搜索', '/profile/site/system/2c3be62e-9584-4db0-9940-509f13f9d0fc.png', '资源超丰富的阿里云盘资源搜索引擎', 'https://www.niceso.fun/', 21, 0, '2021-12-10 22:10:33', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100567, 150725316141101004, 'PDF24 Tools', '/profile/site/system/0d8de034-7e9e-4e7b-8f87-1679cc5cdc55.svg', '免费且易于使用的在线PDF工具，不限制文件大小。', 'https://tools.pdf24.org/zh/', 13, 0, '2022-01-16 20:31:31', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100568, 150725316141101011, '视中心影院', '/profile/site/system/84734031-d40d-40d1-b30c-c13fa29a65f1.png', '全新电视剧,全新动漫,全新综艺节目排行榜,免费在线观看全网电影,动作片,喜剧片,爱情片,搞笑片等全新电影,更多电影高清在线观看尽在视中心影院', 'https://www.mhz8.com/', 30, 0, '2022-03-13 15:03:21', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100569, 150725316141101042, 'vue-admin-better', '/profile/site/system/4864e82c-69e2-4868-8501-0c9a06ff56c6.png', '国内首个基于vue3.0的开源admin项目，同时支持电脑，手机，平板', 'https://github.com/chuzhixin/vue-admin-better', 10, 0, '2022-03-14 23:26:42', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100570, 150725316141101031, 'Element Plus', '/profile/site/system/6a17c0cb-36ad-4029-9570-0b272a07055d.svg', '基于 Vue 3，面向设计师和开发者的组件库', 'https://element-plus.gitee.io/zh-CN/', 3, 0, '2022-06-11 17:30:09', '2022-06-11 17:30:44', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100571, 150725316141101042, '格姗知识圈 / geshanzsq-blog', '/profile/site/system/logo.jpg', '一个基于 Spring Boot、Spring Security、Vue3、Element Plus 的前后端分离的博客网站！', 'https://gitee.com/geshanzsq/geshanzsq-blog', 0, 0, '2022-11-03 21:43:34', '2022-11-03 21:44:25', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100572, 150725316141101017, 'Wormhole', '/profile/site/system/4bef67c6-4105-48ea-8974-b5e565cb6179.png', '一个没有任何限制的文件传输网站，打开就可以选择上传需要临时保存的文件或文件夹，一次可以免费上传10GB以内的大小文件。', 'https://wormhole.app', 24, 0, '2023-01-09 20:26:45', '2023-01-09 20:27:23', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100573, 150725316141101048, 'Hotbox', '/profile/site/system/c4fc3995-0b5d-44f2-8220-8e40db0324e7.png', '一个完全免费的视频下载网站，能够下载多个平台的视频文件，并且还支持自定义选择视频清晰度以及单独的音频下载。', 'https://www.hotbox.fun', 17, 0, '2023-01-09 20:29:53', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100574, 150725316141101048, 'DeepL', '/profile/site/system/4117453c-4615-4245-a59f-f9e03b38b1d6.png', '一个非常好用的在线文字、文档翻译网站，下载的文档文献资料都能一键翻译成需要的语种，支持翻译上百种语种', 'https://www.deepl.com/translator', 18, 0, '2023-01-09 20:31:55', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100575, 150725316141101004, '贴图士', '/profile/site/system/3a981b05-bd12-426a-8094-91eeff7e6e49.png', '一个免费的视频格式转换以及图片压缩网站。打开页面会看到多种图片压缩格式、视频转GIF、GIF合并、GIF裁剪等功能。', 'https://www.tutieshi.com/video/', 14, 0, '2023-01-09 20:33:36', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100576, 150725316141101008, 'Latest10', '/profile/site/system/804cc2a0-afcc-4984-b3b4-dfd6154e527b.png', '获取最新的系统镜像', 'https://latest10.win/', 11, 0, '2023-02-18 19:47:07', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100577, 150725316141101008, '极简系统', '/profile/site/system/67a5a719-0b09-4807-9185-535b7b4ce107.ico', '最纯净的系统下载平台', 'https://www.sysmini.com/', 12, 0, '2023-02-18 19:48:03', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100578, 150725316141101008, '微软官方系统', '/profile/site/system/b066263b-a469-4fa1-a1dc-df8f86869eb8.png', '正版官方下载', 'https://www.microsoft.com/zh-cn/software-download/', 13, 1, '2023-02-18 19:49:50', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100579, 150725316141101048, 'VideoFk', '/profile/site/system/7adb50a1-6511-438f-b713-2662bc747155.png', '在线视频下载，下载视频转换为 MP4 最佳网站以从All在线下载视频', 'https://www.videofk.com/', 19, 0, '2023-02-27 23:23:09', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100580, 150725316141101052, 'AI 导航', '/profile/site/system/65da39e3-2d3b-4e7c-8590-73ac52b2fbeb.png', '超全的国内外 AI 应用', 'https://gesdh.cn/share/ai', 1, 0, '2023-03-31 00:40:32', '2023-05-19 21:05:00', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100581, 150725316141101035, '抖音创作服务平台', '/profile/site/system/da278792-b8ec-4073-8cd4-660fc24acb7b.ico', '抖音创作服务平台', 'https://creator.douyin.com/', 23, 0, '2023-04-05 22:21:33', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (301450632282221536, 150725316141101050, '飞书文档', '/profile/site/system/2023/05/15/8877512b-ad2c-4690-800b-fc5121dda9d6.jpeg', '新一代高效协作工具,融合了在线文档和协同文档的所有功能,不仅能插入在线表格,将数据表转换成看板,还能用思维笔记,将思考路径可视化,更有丰富模板满足多场景创作需求。', 'https://bqfeun1dwu8.feishu.cn', 13, 0, '2023-05-15 22:08:36', NULL, 1, NULL, 1);\n\n-- ----------------------------\n-- Table structure for nav_site_config\n-- ----------------------------\nDROP TABLE IF EXISTS `nav_site_config`;\nCREATE TABLE `nav_site_config`  (\n  `id` bigint(20) NOT NULL COMMENT '配置 id',\n  `about_site_description` varchar(500) NULL DEFAULT '' COMMENT '关于本站描述',\n  `about_site_email` varchar(50) NULL DEFAULT '' COMMENT '关于本站邮箱',\n  `about_site_content` longtext NULL COMMENT '关于本站内容',\n  `about_site_visit_count` int(11) NULL DEFAULT 0 COMMENT '关于本站访问量',\n  `fk_create_user_id` bigint(20) NULL DEFAULT NULL COMMENT '创建人用户 id',\n  `gmt_create` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `fk_modify_user_id` bigint(20) NULL DEFAULT NULL COMMENT '修改人用户 id',\n  `gmt_modify` datetime NULL DEFAULT NULL COMMENT '修改时间',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB COMMENT = '网站配置' ROW_FORMAT = DYNAMIC;\n\n-- ----------------------------\n-- Records of nav_site_config\n-- ----------------------------\nINSERT INTO `nav_site_config` VALUES (152819881111191552, '这个导航网站来源于格姗导航的开源项目。如果你有更好的想法，可以通过左边的邮箱与我联系。如果喜欢本站，可以分享给其他人，或者设置为主页，这是对我最大的支持！\\'', '497301391@qq.com', '<p>这是一个导航网站,收入了大部分常用的网站，希望能够解决到你频繁收藏网站的烦恼！</p><p>显然，这是一个开源项目，主要放一些自己经常用到的网站。</p><p>开源项目来源：<a href=\\\"\\\\\\\" target=\\\"\\\\\\\">https://gitee.com/geshanzsq/geshanzsq-nav</a></p>', 10, 43728307660783616, '2023-05-21 16:51:39', 43728307660783616, '2023-05-21 16:58:51');\n\n-- ----------------------------\n-- Table structure for sys_api\n-- ----------------------------\nDROP TABLE IF EXISTS `sys_api`;\nCREATE TABLE `sys_api`  (\n  `id` bigint(20) NOT NULL COMMENT '接口 id',\n  `api_name` varchar(50) NULL DEFAULT '' COMMENT '接口名称',\n  `api_url` varchar(255) NULL DEFAULT '' COMMENT '接口地址',\n  `api_method` varchar(10) NULL DEFAULT '' COMMENT '接口请求方式（如：get，post）',\n  `fk_api_category_id` varchar(25) NULL DEFAULT '' COMMENT '所属分类 id',\n  `sort` int(11) NULL DEFAULT 0 COMMENT '排序',\n  `status` tinyint(1) NULL DEFAULT 1 COMMENT '状态（1 正常，2 停用）',\n  `remark` varchar(500) NULL DEFAULT '' COMMENT '备注',\n  `fk_create_user_id` bigint(20) NULL DEFAULT NULL COMMENT '创建人用户 id',\n  `gmt_create` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `fk_modify_user_id` bigint(20) NULL DEFAULT NULL COMMENT '修改人用户 id',\n  `gmt_modify` datetime NULL DEFAULT NULL COMMENT '修改时间',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB COMMENT = '系统接口' ROW_FORMAT = DYNAMIC;\n\n-- ----------------------------\n-- Records of sys_api\n-- ----------------------------\nINSERT INTO `sys_api` VALUES (43759552742555648, '分页列表', '/system/user/page', 'GET', '43757041415618560', 1, 1, '', 43728307660783616, '2022-07-24 18:04:50', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43759676889759744, '详情', '/system/user/getById/*', 'GET', '43757041415618560', 2, 1, '', 43728307660783616, '2022-07-24 18:05:20', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43761152815005696, '新增', '/system/user', 'POST', '43757041415618560', 3, 1, '', 43728307660783616, '2022-07-24 18:11:12', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43761187950690304, '修改', '/system/user', 'PUT', '43757041415618560', 4, 1, '', 43728307660783616, '2022-07-24 18:11:20', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43761278916755456, '删除', '/system/user/delete/*', 'DELETE', '43757041415618560', 5, 1, '', 43728307660783616, '2022-07-24 18:11:42', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43762647782391808, '重置密码', '/system/user/resetPassword', 'PUT', '43757041415618560', 6, 1, '', 43728307660783616, '2022-07-24 18:17:08', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43762807694426112, '分页列表', '/system/role/page', 'GET', '43757069869776896', 1, 1, '', 43728307660783616, '2022-07-24 18:17:46', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43762858923655168, '详情', '/system/role/getById/*', 'GET', '43757069869776896', 2, 1, '', 43728307660783616, '2022-07-24 18:17:59', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43763188797276160, '新增', '/system/role', 'POST', '43757069869776896', 3, 1, '', 43728307660783616, '2022-07-24 18:19:17', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43763232950714368, '修改', '/system/role', 'PUT', '43757069869776896', 4, 1, '', 43728307660783616, '2022-07-24 18:19:28', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43763311845572608, '删除', '/system/role/delete/*', 'DELETE', '43757069869776896', 5, 1, '', 43728307660783616, '2022-07-24 18:19:47', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43763442175180800, '获取最大排序', '/system/role/getMaxSort', 'GET', '43757069869776896', 6, 1, '', 43728307660783616, '2022-07-24 18:20:18', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43763523695673344, '已分配用户分页', '/system/role/auth/user/page', 'GET', '43757069869776896', 7, 1, '', 43728307660783616, '2022-07-24 18:20:37', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43763593107210240, '未分配用户分页', '/system/role/auth/user/not/page', 'GET', '43757069869776896', 8, 1, '', 43728307660783616, '2022-07-24 18:20:54', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43763693447544832, '授权用户', '/system/role/auth/user', 'POST', '43757069869776896', 9, 1, '', 43728307660783616, '2022-07-24 18:21:18', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43763818857234432, '取消授权', '/system/role/auth/user/delete', 'DELETE', '43757069869776896', 10, 1, '', 43728307660783616, '2022-07-24 18:21:48', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43763961681674240, '列表', '/system/menu/list', 'GET', '43758191430860800', 1, 1, '', 43728307660783616, '2022-07-24 18:22:22', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43764022897541120, '树形', '/system/menu/tree', 'GET', '43758191430860800', 2, 1, '', 43728307660783616, '2022-07-24 18:22:36', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43764081659740160, '详情', '/system/menu/getById/*', 'GET', '43758191430860800', 3, 1, '', 43728307660783616, '2022-07-24 18:22:50', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43764130418524160, '新增', '/system/menu', 'POST', '43758191430860800', 4, 1, '', 43728307660783616, '2022-07-24 18:23:02', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43764160827228160, '修改', '/system/menu', 'PUT', '43758191430860800', 5, 1, '', 43728307660783616, '2022-07-24 18:23:09', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43764235854938112, '删除', '/system/menu/delete/*', 'DELETE', '43758191430860800', 6, 1, '', 43728307660783616, '2022-07-24 18:23:27', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43764326162497536, '获取最大排序', '/system/menu/getMaxSortByParentId', 'GET', '43758191430860800', 7, 1, '', 43728307660783616, '2022-07-24 18:23:48', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43764505833897984, '分页列表', '/system/dictionary/page', 'GET', '43758342685851648', 1, 1, '', 43728307660783616, '2022-07-24 18:24:31', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43764571281817600, '列表', '/system/dictionary/list', 'GET', '43758342685851648', 2, 1, '', 43728307660783616, '2022-07-24 18:24:47', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43764610037186560, '新增', '/system/dictionary', 'POST', '43758342685851648', 3, 1, '', 43728307660783616, '2022-07-24 18:24:56', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43764642895364096, '修改', '/system/dictionary', 'PUT', '43758342685851648', 5, 1, '', 43728307660783616, '2022-07-24 18:25:04', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43764689900929024, '删除', '/system/dictionary/delete/*', 'DELETE', '43758342685851648', 6, 1, '', 43728307660783616, '2022-07-24 18:25:15', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43764765633282048, '获取最大排序', '/system/dictionary/getMaxSort', 'GET', '43758342685851648', 7, 1, '', 43728307660783616, '2022-07-24 18:25:33', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43764977332387840, '分页列表', '/system/dictionary/data/page', 'GET', '43758386155618304', 1, 1, '', 43728307660783616, '2022-07-24 18:26:24', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43765061612732416, '详情', '/system/dictionary/data/getById/*', 'GET', '43758386155618304', 2, 1, '', 43728307660783616, '2022-07-24 18:26:44', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43765127371030528, '新增', '/system/dictionary/data', 'POST', '43758386155618304', 3, 1, '', 43728307660783616, '2022-07-24 18:26:59', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43765165673414656, '修改', '/system/dictionary/data', 'PUT', '43758386155618304', 4, 1, '', 43728307660783616, '2022-07-24 18:27:09', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43765212116942848, '删除', '/system/dictionary/data/delete/*', 'DELETE', '43758386155618304', 5, 1, '', 43728307660783616, '2022-07-24 18:27:20', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43765276512092160, '获取最大排序', '/system/dictionary/data/getMaxSortByDictionaryId', 'GET', '43758386155618304', 6, 1, '', 43728307660783616, '2022-07-24 18:27:35', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43765395617742848, '分页列表', '/system/api/category/page', 'GET', '43758515155632128', 1, 1, '', 43728307660783616, '2022-07-24 18:28:03', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43765435992113152, '列表', '/system/api/category/list', 'GET', '43758515155632128', 2, 1, '', 43728307660783616, '2022-07-24 18:28:13', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43765499997192192, '详情', '/system/api/category/getById/*', 'GET', '43758515155632128', 3, 1, '', 43728307660783616, '2022-07-24 18:28:28', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43765537020313600, '新增', '/system/api/category', 'POST', '43758515155632128', 4, 1, '', 43728307660783616, '2022-07-24 18:28:37', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43765569287094272, '修改', '/system/api/category', 'PUT', '43758515155632128', 5, 1, '', 43728307660783616, '2022-07-24 18:28:45', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43765629844455424, '删除', '/system/api/category/delete/*', 'DELETE', '43758515155632128', 6, 1, '', 43728307660783616, '2022-07-24 18:28:59', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43765696206733312, '获取最大排序', '/system/api/category/getMaxSort', 'GET', '43758515155632128', 7, 1, '', 43728307660783616, '2022-07-24 18:29:15', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43765773721665536, '分页列表', '/system/api/page', 'GET', '43758723595763712', 1, 1, '', 43728307660783616, '2022-07-24 18:29:34', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43765827249373184, '详情', '/system/api/getById/*', 'GET', '43758723595763712', 2, 1, '', 43728307660783616, '2022-07-24 18:29:46', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43766245937381376, '新增', '/system/api', 'POST', '43758723595763712', 3, 1, '', 43728307660783616, '2022-07-24 18:31:26', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43766278116081664, '修改', '/system/api', 'PUT', '43758723595763712', 4, 1, '', 43728307660783616, '2022-07-24 18:31:34', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43766323594919936, '删除', '/system/api/delete/*', 'DELETE', '43758723595763712', 5, 1, '', 43728307660783616, '2022-07-24 18:31:45', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43766380444516352, '获取最大排序', '/system/api/getMaxSortByCategoryId', 'GET', '43758723595763712', 6, 1, '', 43728307660783616, '2022-07-24 18:31:58', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43766697940746240, '分页列表', '/system/log/login/page', 'GET', '43766546228576256', 1, 1, '', 43728307660783616, '2022-07-24 18:33:14', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43766812147449856, '分页列表', '/system/log/operation/page', 'GET', '43766567304953856', 1, 1, '', 43728307660783616, '2022-07-24 18:33:41', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43766869047377920, '详情', '/system/log/operation/getById/*', 'GET', '43766567304953856', 2, 1, '', 43728307660783616, '2022-07-24 18:33:55', NULL, NULL);\nINSERT INTO `sys_api` VALUES (45287586729558016, '详情', '/system/dictionary/getById/*', 'GET', '43758342685851648', 4, 1, '', 43728307660783616, '2022-07-28 23:16:42', NULL, NULL);\nINSERT INTO `sys_api` VALUES (52141509008424960, '分页列表', '/system/param/page', 'GET', '52133147512406016', 1, 1, '', 43728307660783616, '2022-08-16 21:11:44', NULL, NULL);\nINSERT INTO `sys_api` VALUES (52141659554578432, '详情', '/system/param/getById/*', 'GET', '52133147512406016', 2, 1, '', 43728307660783616, '2022-08-16 21:12:20', NULL, NULL);\nINSERT INTO `sys_api` VALUES (52141709437435904, '新增', '/system/param', 'POST', '52133147512406016', 3, 1, '', 43728307660783616, '2022-08-16 21:12:32', NULL, NULL);\nINSERT INTO `sys_api` VALUES (52141758401740800, '修改', '/system/param', 'PUT', '52133147512406016', 4, 1, '', 43728307660783616, '2022-08-16 21:12:44', NULL, NULL);\nINSERT INTO `sys_api` VALUES (52141803498897408, '删除', '/system/param/delete/*', 'DELETE', '52133147512406016', 5, 1, '', 43728307660783616, '2022-08-16 21:12:55', NULL, NULL);\nINSERT INTO `sys_api` VALUES (52141938496765952, '获取最大排序', '/system/param/getMaxSort', 'GET', '52133147512406016', 6, 1, '', 43728307660783616, '2022-08-16 21:13:27', NULL, NULL);\nINSERT INTO `sys_api` VALUES (86832919972151297, '列表', '/nav/category/list', 'GET', '86832919972151296', 1, 1, '', 43728307660783616, '2022-11-20 14:43:41', NULL, NULL);\nINSERT INTO `sys_api` VALUES (86832919972151298, '详情', '/nav/category/getById/*', 'GET', '86832919972151296', 2, 1, '', 43728307660783616, '2022-11-20 14:43:41', NULL, NULL);\nINSERT INTO `sys_api` VALUES (86832919972151299, '新增', '/nav/category', 'POST', '86832919972151296', 3, 1, '', 43728307660783616, '2022-11-20 14:43:41', NULL, NULL);\nINSERT INTO `sys_api` VALUES (86832919972151300, '修改', '/nav/category', 'PUT', '86832919972151296', 4, 1, '', 43728307660783616, '2022-11-20 14:43:41', NULL, NULL);\nINSERT INTO `sys_api` VALUES (86832919972151301, '删除', '/nav/category/delete/*', 'DELETE', '86832919972151296', 5, 1, '', 43728307660783616, '2022-11-20 14:43:41', NULL, NULL);\nINSERT INTO `sys_api` VALUES (86832919972151302, '获取最大排序', '/nav/category/getMaxSortByParentId', 'GET', '86832919972151296', 6, 1, '', 43728307660783616, '2022-11-20 14:43:41', NULL, NULL);\nINSERT INTO `sys_api` VALUES (86859268749262849, '分页列表', '/nav/site/page', 'GET', '86859268749262848', 1, 1, '', 43728307660783616, '2022-11-20 16:27:55', NULL, NULL);\nINSERT INTO `sys_api` VALUES (86859268749262850, '详情', '/nav/site/getById/*', 'GET', '86859268749262848', 2, 1, '', 43728307660783616, '2022-11-20 16:27:55', NULL, NULL);\nINSERT INTO `sys_api` VALUES (86859268749262851, '新增', '/nav/site', 'POST', '86859268749262848', 3, 1, '', 43728307660783616, '2022-11-20 16:27:55', NULL, NULL);\nINSERT INTO `sys_api` VALUES (86859268749262852, '修改', '/nav/site', 'PUT', '86859268749262848', 4, 1, '', 43728307660783616, '2022-11-20 16:27:55', NULL, NULL);\nINSERT INTO `sys_api` VALUES (86859268749262853, '删除', '/nav/site/delete/*', 'DELETE', '86859268749262848', 5, 1, '', 43728307660783616, '2022-11-20 16:27:55', NULL, NULL);\nINSERT INTO `sys_api` VALUES (86859268749262854, '获取最大排序', '/nav/site/getMaxSort/', 'GET', '86859268749262848', 6, 1, '', 43728307660783616, '2022-11-20 16:27:55', NULL, NULL);\nINSERT INTO `sys_api` VALUES (92732732718710784, '分页列表', '/nav/comment/page', 'GET', '92732554183966720', 1, 1, '', 43728307660783616, '2022-12-06 21:26:46', NULL, NULL);\nINSERT INTO `sys_api` VALUES (92732865405517824, '通过', '/nav/comment/pass/*', 'PUT', '92732554183966720', 2, 1, '', 43728307660783616, '2022-12-06 21:27:18', NULL, NULL);\nINSERT INTO `sys_api` VALUES (92732919084220416, '驳回', '/nav/comment/reject', 'PUT', '92732554183966720', 3, 1, '', 43728307660783616, '2022-12-06 21:27:31', NULL, NULL);\nINSERT INTO `sys_api` VALUES (92733072901931008, '置顶', '/nav/comment/sticky/*', 'PUT', '92732554183966720', 4, 1, '', 43728307660783616, '2022-12-06 21:28:07', NULL, NULL);\nINSERT INTO `sys_api` VALUES (92733166082588672, '取消置顶', '/nav/comment/cancelSticky/*', 'PUT', '92732554183966720', 5, 1, '', 43728307660783616, '2022-12-06 21:28:30', NULL, NULL);\nINSERT INTO `sys_api` VALUES (92733276258566144, '删除', '/nav/comment/delete/*', 'DELETE', '92732554183966720', 6, 1, '', 43728307660783616, '2022-12-06 21:28:56', NULL, NULL);\nINSERT INTO `sys_api` VALUES (94456256298745856, '获取配置', '/nav/config/getConfig', 'GET', '94456130222161920', 1, 1, '', 43728307660783616, '2022-12-11 15:35:26', NULL, NULL);\nINSERT INTO `sys_api` VALUES (94456330005250048, '修改', '/nav/config', 'PUT', '94456130222161920', 2, 1, '', 43728307660783616, '2022-12-11 15:35:44', NULL, NULL);\n\n-- ----------------------------\n-- Table structure for sys_api_category\n-- ----------------------------\nDROP TABLE IF EXISTS `sys_api_category`;\nCREATE TABLE `sys_api_category`  (\n  `id` bigint(20) NOT NULL COMMENT '接口分类 id',\n  `category_name` varchar(50) NULL DEFAULT '' COMMENT '分类名称',\n  `sort` int(11) NULL DEFAULT 0 COMMENT '排序',\n  `status` tinyint(1) NULL DEFAULT 1 COMMENT '状态（1 正常，2 停用）',\n  `remark` varchar(500) NULL DEFAULT '' COMMENT '备注',\n  `fk_create_user_id` bigint(20) NULL DEFAULT NULL COMMENT '创建人用户 id',\n  `gmt_create` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `fk_modify_user_id` bigint(20) NULL DEFAULT NULL COMMENT '修改人用户 id',\n  `gmt_modify` datetime NULL DEFAULT NULL COMMENT '修改时间',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB COMMENT = '系统接口分类' ROW_FORMAT = DYNAMIC;\n\n-- ----------------------------\n-- Records of sys_api_category\n-- ----------------------------\nINSERT INTO `sys_api_category` VALUES (43757041415618560, '用户管理', 1, 1, '', 43728307660783616, '2022-07-24 17:54:52', NULL, NULL);\nINSERT INTO `sys_api_category` VALUES (43757069869776896, '角色管理', 2, 1, '', 43728307660783616, '2022-07-24 17:54:58', NULL, NULL);\nINSERT INTO `sys_api_category` VALUES (43758191430860800, '菜单管理', 3, 1, '', 43728307660783616, '2022-07-24 17:59:26', NULL, NULL);\nINSERT INTO `sys_api_category` VALUES (43758342685851648, '数据字典', 4, 1, '', 43728307660783616, '2022-07-24 18:00:02', NULL, NULL);\nINSERT INTO `sys_api_category` VALUES (43758386155618304, '数据字典数据', 5, 1, '', 43728307660783616, '2022-07-24 18:00:12', NULL, NULL);\nINSERT INTO `sys_api_category` VALUES (43758515155632128, 'API 接口分类', 6, 1, '', 43728307660783616, '2022-07-24 18:00:43', NULL, NULL);\nINSERT INTO `sys_api_category` VALUES (43758723595763712, 'API 接口', 7, 1, '\\n', 43728307660783616, '2022-07-24 18:01:33', NULL, NULL);\nINSERT INTO `sys_api_category` VALUES (43766546228576256, '登录日志', 9, 1, '', 43728307660783616, '2022-07-24 18:32:38', NULL, NULL);\nINSERT INTO `sys_api_category` VALUES (43766567304953856, '操作日志', 10, 1, '', 43728307660783616, '2022-07-24 18:32:43', NULL, NULL);\nINSERT INTO `sys_api_category` VALUES (52133147512406016, '参数配置', 8, 1, '', 43728307660783616, '2022-08-16 20:38:31', NULL, NULL);\nINSERT INTO `sys_api_category` VALUES (86832919972151296, '导航分类管理', 11, 1, '', 43728307660783616, '2022-11-20 14:43:41', NULL, NULL);\nINSERT INTO `sys_api_category` VALUES (86859268749262848, '导航网站管理', 12, 1, '', 43728307660783616, '2022-11-20 16:27:55', NULL, NULL);\nINSERT INTO `sys_api_category` VALUES (92732554183966720, '评论管理', 13, 1, '', 43728307660783616, '2022-12-06 21:26:04', NULL, NULL);\nINSERT INTO `sys_api_category` VALUES (94456130222161920, '网站配置', 16, 1, '', 43728307660783616, '2022-12-11 15:34:56', NULL, NULL);\n\n-- ----------------------------\n-- Table structure for sys_api_menu\n-- ----------------------------\nDROP TABLE IF EXISTS `sys_api_menu`;\nCREATE TABLE `sys_api_menu`  (\n  `id` bigint(20) NOT NULL COMMENT '接口菜单 id',\n  `fk_api_id` bigint(20) NOT NULL COMMENT '接口 id',\n  `fk_menu_id` bigint(20) NOT NULL COMMENT '菜单 id',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB COMMENT = '系统接口菜单' ROW_FORMAT = DYNAMIC;\n\n-- ----------------------------\n-- Records of sys_api_menu\n-- ----------------------------\nINSERT INTO `sys_api_menu` VALUES (44934431223316480, 43759552742555648, 43738261293629440);\nINSERT INTO `sys_api_menu` VALUES (44941778175918080, 43761152815005696, 43779604086784000);\nINSERT INTO `sys_api_menu` VALUES (44942911996952576, 43759676889759744, 43779675532558336);\nINSERT INTO `sys_api_menu` VALUES (44942911996952577, 43761187950690304, 43779675532558336);\nINSERT INTO `sys_api_menu` VALUES (44943459030663168, 43761278916755456, 43779734387032064);\nINSERT INTO `sys_api_menu` VALUES (44943477556903936, 43762647782391808, 43779823738290176);\nINSERT INTO `sys_api_menu` VALUES (44943667110084608, 43762807694426112, 43738420110950400);\nINSERT INTO `sys_api_menu` VALUES (44943839235932160, 43762858923655168, 43780015896133632);\nINSERT INTO `sys_api_menu` VALUES (44943839235932161, 43763232950714368, 43780015896133632);\nINSERT INTO `sys_api_menu` VALUES (44943888078602240, 43763311845572608, 43780063644090368);\nINSERT INTO `sys_api_menu` VALUES (44943942797492224, 43763188797276160, 43779962246791168);\nINSERT INTO `sys_api_menu` VALUES (44943942797492225, 43763442175180800, 43779962246791168);\nINSERT INTO `sys_api_menu` VALUES (45286549805334528, 43763523695673344, 43780156136882176);\nINSERT INTO `sys_api_menu` VALUES (45286549805334529, 43763593107210240, 43780156136882176);\nINSERT INTO `sys_api_menu` VALUES (45286549805334530, 43763693447544832, 43780156136882176);\nINSERT INTO `sys_api_menu` VALUES (45286600837431296, 43763818857234432, 43780326794723328);\nINSERT INTO `sys_api_menu` VALUES (45286731506778112, 43763961681674240, 43738641683447808);\nINSERT INTO `sys_api_menu` VALUES (45286823873740800, 43764022897541120, 43780571997929472);\nINSERT INTO `sys_api_menu` VALUES (45286823873740801, 43764081659740160, 43780571997929472);\nINSERT INTO `sys_api_menu` VALUES (45286823873740802, 43764160827228160, 43780571997929472);\nINSERT INTO `sys_api_menu` VALUES (45286823873740803, 43764326162497536, 43780571997929472);\nINSERT INTO `sys_api_menu` VALUES (45286924520259584, 43764022897541120, 43780526653308928);\nINSERT INTO `sys_api_menu` VALUES (45286924520259585, 43764130418524160, 43780526653308928);\nINSERT INTO `sys_api_menu` VALUES (45286924520259586, 43764326162497536, 43780526653308928);\nINSERT INTO `sys_api_menu` VALUES (45286959903408128, 43764235854938112, 43780625399808000);\nINSERT INTO `sys_api_menu` VALUES (45287084751060992, 43765435992113152, 43780699760623616);\nINSERT INTO `sys_api_menu` VALUES (45287084751060993, 43765773721665536, 43780699760623616);\nINSERT INTO `sys_api_menu` VALUES (45287155194396672, 43764505833897984, 43738896395141120);\nINSERT INTO `sys_api_menu` VALUES (45287205698011136, 43764610037186560, 43780957378969600);\nINSERT INTO `sys_api_menu` VALUES (45287205698011137, 43764765633282048, 43780957378969600);\nINSERT INTO `sys_api_menu` VALUES (45287965244522496, 43764642895364096, 43781009912627200);\nINSERT INTO `sys_api_menu` VALUES (45287965244522497, 43764765633282048, 43781009912627200);\nINSERT INTO `sys_api_menu` VALUES (45287965244522498, 45287586729558016, 43781009912627200);\nINSERT INTO `sys_api_menu` VALUES (45288010920493056, 43764689900929024, 43781060101668864);\nINSERT INTO `sys_api_menu` VALUES (45288359009976320, 43765127371030528, 43781756515516416);\nINSERT INTO `sys_api_menu` VALUES (45288359009976321, 43765276512092160, 43781756515516416);\nINSERT INTO `sys_api_menu` VALUES (45288394263101440, 43765061612732416, 43781829500600320);\nINSERT INTO `sys_api_menu` VALUES (45288394263101441, 43765165673414656, 43781829500600320);\nINSERT INTO `sys_api_menu` VALUES (45288394263101442, 43765276512092160, 43781829500600320);\nINSERT INTO `sys_api_menu` VALUES (45288418313240576, 43765212116942848, 43781877487632384);\nINSERT INTO `sys_api_menu` VALUES (45288563960446976, 43764571281817600, 43781692732735488);\nINSERT INTO `sys_api_menu` VALUES (45288563960446977, 43764977332387840, 43781692732735488);\nINSERT INTO `sys_api_menu` VALUES (45288684953534464, 43765395617742848, 43739074015526912);\nINSERT INTO `sys_api_menu` VALUES (45288725575368704, 43765537020313600, 43820727559782400);\nINSERT INTO `sys_api_menu` VALUES (45288725575368705, 43765696206733312, 43820727559782400);\nINSERT INTO `sys_api_menu` VALUES (45288799676137472, 43765499997192192, 43820963283861504);\nINSERT INTO `sys_api_menu` VALUES (45288799676137473, 43765569287094272, 43820963283861504);\nINSERT INTO `sys_api_menu` VALUES (45288799676137474, 43765696206733312, 43820963283861504);\nINSERT INTO `sys_api_menu` VALUES (45288869704237056, 43765629844455424, 43821061610930176);\nINSERT INTO `sys_api_menu` VALUES (45288915245989888, 43765435992113152, 43821227462098944);\nINSERT INTO `sys_api_menu` VALUES (45288915245989889, 43765773721665536, 43821227462098944);\nINSERT INTO `sys_api_menu` VALUES (45288959261016064, 43766245937381376, 43821354134274048);\nINSERT INTO `sys_api_menu` VALUES (45288959261016065, 43766380444516352, 43821354134274048);\nINSERT INTO `sys_api_menu` VALUES (45288998154797056, 43765827249373184, 43821418193879040);\nINSERT INTO `sys_api_menu` VALUES (45288998154797057, 43766278116081664, 43821418193879040);\nINSERT INTO `sys_api_menu` VALUES (45288998154797058, 43766380444516352, 43821418193879040);\nINSERT INTO `sys_api_menu` VALUES (45289037971324928, 43766323594919936, 43821478692519936);\nINSERT INTO `sys_api_menu` VALUES (45289082435141632, 43766697940746240, 43746395802304512);\nINSERT INTO `sys_api_menu` VALUES (45289104912416768, 43766812147449856, 43746859121901568);\nINSERT INTO `sys_api_menu` VALUES (45289104912416769, 43766869047377920, 43746859121901568);\nINSERT INTO `sys_api_menu` VALUES (52142019916595200, 52141509008424960, 52073868344426496);\nINSERT INTO `sys_api_menu` VALUES (52142059640848384, 52141709437435904, 52132922030817280);\nINSERT INTO `sys_api_menu` VALUES (52142059640848385, 52141938496765952, 52132922030817280);\nINSERT INTO `sys_api_menu` VALUES (52142106021462016, 52141659554578432, 52133024904511488);\nINSERT INTO `sys_api_menu` VALUES (52142106025656320, 52141758401740800, 52133024904511488);\nINSERT INTO `sys_api_menu` VALUES (52142106025656321, 52141938496765952, 52133024904511488);\nINSERT INTO `sys_api_menu` VALUES (52142131011125248, 52141803498897408, 52133076263763968);\nINSERT INTO `sys_api_menu` VALUES (86832919972151307, 86832919972151297, 86832919972151303);\nINSERT INTO `sys_api_menu` VALUES (86832919972151308, 86832919972151299, 86832919972151304);\nINSERT INTO `sys_api_menu` VALUES (86832919972151309, 86832919972151300, 86832919972151305);\nINSERT INTO `sys_api_menu` VALUES (86832919972151310, 86832919972151298, 86832919972151305);\nINSERT INTO `sys_api_menu` VALUES (86832919972151311, 86832919972151301, 86832919972151306);\nINSERT INTO `sys_api_menu` VALUES (86832919972151312, 86832919972151302, 86832919972151304);\nINSERT INTO `sys_api_menu` VALUES (86832919972151313, 86832919972151302, 86832919972151305);\nINSERT INTO `sys_api_menu` VALUES (86859268749262859, 86859268749262849, 86859268749262855);\nINSERT INTO `sys_api_menu` VALUES (86859268749262860, 86859268749262851, 86859268749262856);\nINSERT INTO `sys_api_menu` VALUES (86859268749262861, 86859268749262852, 86859268749262857);\nINSERT INTO `sys_api_menu` VALUES (86859268749262862, 86859268749262850, 86859268749262857);\nINSERT INTO `sys_api_menu` VALUES (86859268749262863, 86859268749262853, 86859268749262858);\nINSERT INTO `sys_api_menu` VALUES (86859268749262864, 86859268749262854, 86859268749262856);\nINSERT INTO `sys_api_menu` VALUES (86859268749262865, 86859268749262854, 86859268749262857);\nINSERT INTO `sys_api_menu` VALUES (92733359674884096, 92732732718710784, 90179495205535744);\nINSERT INTO `sys_api_menu` VALUES (92733462183673856, 92732865405517824, 92733418365779968);\nINSERT INTO `sys_api_menu` VALUES (92733623991533568, 92732919084220416, 92733594673348608);\nINSERT INTO `sys_api_menu` VALUES (92733755071922176, 92733072901931008, 92733726269636608);\nINSERT INTO `sys_api_menu` VALUES (92733896281554944, 92733166082588672, 92733853243801600);\nINSERT INTO `sys_api_menu` VALUES (92734000786833408, 92733276258566144, 92733965122666496);\nINSERT INTO `sys_api_menu` VALUES (94456518971228160, 94456256298745856, 94456071543848960);\nINSERT INTO `sys_api_menu` VALUES (94456548784340992, 94456330005250048, 94456484632461312);\n\n-- ----------------------------\n-- Table structure for sys_dictionary\n-- ----------------------------\nDROP TABLE IF EXISTS `sys_dictionary`;\nCREATE TABLE `sys_dictionary`  (\n  `id` bigint(20) NOT NULL COMMENT '字典 id',\n  `dictionary_name` varchar(50) NULL DEFAULT '' COMMENT '字典名称',\n  `dictionary_code` varchar(50) NULL DEFAULT '' COMMENT '字典编码',\n  `sort` int(11) NULL DEFAULT 0 COMMENT '排序',\n  `status` tinyint(1) NULL DEFAULT 1 COMMENT '状态（1 正常，2 停用）',\n  `remark` varchar(500) NULL DEFAULT '' COMMENT '备注',\n  `fk_create_user_id` bigint(20) NULL DEFAULT NULL COMMENT '创建人用户 id',\n  `gmt_create` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `fk_modify_user_id` bigint(20) NULL DEFAULT NULL COMMENT '修改人用户 id',\n  `gmt_modify` datetime NULL DEFAULT NULL COMMENT '修改时间',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB COMMENT = '系统字典' ROW_FORMAT = DYNAMIC;\n\n-- ----------------------------\n-- Records of sys_dictionary\n-- ----------------------------\nINSERT INTO `sys_dictionary` VALUES (43739630905851904, '通用状态', 'commonStatus', 1, 1, '', 43728307660783616, '2022-07-24 16:45:41', NULL, NULL);\nINSERT INTO `sys_dictionary` VALUES (43739903539806208, '用户类型', 'systemUserType', 2, 1, '', 43728307660783616, '2022-07-24 16:46:46', NULL, NULL);\nINSERT INTO `sys_dictionary` VALUES (43740085404827648, '用户性别', 'systemUserSex', 3, 1, '', 43728307660783616, '2022-07-24 16:47:29', NULL, NULL);\nINSERT INTO `sys_dictionary` VALUES (43740399080046592, '菜单是否缓存', 'systemMenuIsCache', 4, 1, '', 43728307660783616, '2022-07-24 16:48:44', NULL, NULL);\nINSERT INTO `sys_dictionary` VALUES (43740514784116736, '菜单是否外链', 'systemMenuIsFrame', 5, 1, '', 43728307660783616, '2022-07-24 16:49:11', NULL, NULL);\nINSERT INTO `sys_dictionary` VALUES (43740632493064192, '菜单显示状态', 'systemMenuShowStatus', 6, 1, '', 43728307660783616, '2022-07-24 16:49:39', NULL, NULL);\nINSERT INTO `sys_dictionary` VALUES (43741186304770048, '数据字典样式类型', 'dictionaryClassType', 7, 1, '', 43728307660783616, '2022-07-24 16:51:51', NULL, NULL);\nINSERT INTO `sys_dictionary` VALUES (43741186479310919, 'API 请求方式', 'apiRequestMethod', 8, 1, '', 43728307660783616, '2022-07-24 18:07:28', NULL, NULL);\nINSERT INTO `sys_dictionary` VALUES (43747174940409856, '登录日志状态', 'logLoginStatus', 9, 1, '', 43728307660783616, '2022-07-24 17:15:39', NULL, NULL);\nINSERT INTO `sys_dictionary` VALUES (43749175543726080, '操作日志业务类型', 'logOperationBusinessType', 10, 1, '', 43728307660783616, '2022-07-24 17:23:36', NULL, NULL);\nINSERT INTO `sys_dictionary` VALUES (43749667023880192, '操作日志操作类型', 'logOperationType', 11, 1, '', 43728307660783616, '2022-07-24 17:25:33', NULL, NULL);\nINSERT INTO `sys_dictionary` VALUES (43831966709055488, '操作日志状态', 'logOperationStatus', 12, 1, '', 43728307660783616, '2022-07-24 22:52:35', NULL, NULL);\nINSERT INTO `sys_dictionary` VALUES (52090055367327744, '系统参数配置类型', 'sysParamType', 13, 1, '', 43728307660783616, '2022-08-16 17:47:17', NULL, NULL);\nINSERT INTO `sys_dictionary` VALUES (78246041425543168, '通用是否', 'yesNo', 1, 1, '', 43728307660783616, '2022-10-27 22:01:50', NULL, NULL);\nINSERT INTO `sys_dictionary` VALUES (102543613031874560, '评论状态', 'navCommentStatus', 14, 1, '', 43728307660783616, '2023-01-02 23:11:43', NULL, NULL);\n\n-- ----------------------------\n-- Table structure for sys_dictionary_data\n-- ----------------------------\nDROP TABLE IF EXISTS `sys_dictionary_data`;\nCREATE TABLE `sys_dictionary_data`  (\n  `id` bigint(20) NOT NULL COMMENT '字典数据 id',\n  `dictionary_label` varchar(50) NULL DEFAULT '' COMMENT '字典标签',\n  `dictionary_value` varchar(100) NULL DEFAULT '' COMMENT '字典值',\n  `fk_dictionary_id` varchar(25) NULL DEFAULT NULL COMMENT '所属字典 id',\n  `class_type` varchar(100) NULL DEFAULT '' COMMENT '样式类型（primary，success等）',\n  `sort` int(11) NULL DEFAULT 0 COMMENT '排序',\n  `status` tinyint(1) NULL DEFAULT 1 COMMENT '状态（1 正常，2 停用）',\n  `remark` varchar(500) NULL DEFAULT '' COMMENT '备注',\n  `fk_create_user_id` bigint(20) NULL DEFAULT NULL COMMENT '创建人用户 id',\n  `gmt_create` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `fk_modify_user_id` bigint(20) NULL DEFAULT NULL COMMENT '修改用户 id',\n  `gmt_modify` datetime NULL DEFAULT NULL COMMENT '修改时间',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB COMMENT = '系统字典数据' ROW_FORMAT = DYNAMIC;\n\n-- ----------------------------\n-- Records of sys_dictionary_data\n-- ----------------------------\nINSERT INTO `sys_dictionary_data` VALUES (43739723671273472, '正常', '1', '43739630905851904', 'primary', 1, 1, '', 43728307660783616, '2022-07-24 16:46:03', NULL, NULL);\nINSERT INTO `sys_dictionary_data` VALUES (43739751852802048, '停用', '2', '43739630905851904', 'danger', 2, 1, '', 43728307660783616, '2022-07-24 16:46:09', NULL, NULL);\nINSERT INTO `sys_dictionary_data` VALUES (43744355986440192, '后台用户', '1', '43739903539806208', 'primary', 1, 1, '', 43728307660783616, '2022-07-24 17:04:27', NULL, NULL);\nINSERT INTO `sys_dictionary_data` VALUES (43744417940504576, '博客用户', '2', '43739903539806208', 'warning', 2, 1, '', 43728307660783616, '2022-07-24 17:04:42', NULL, NULL);\nINSERT INTO `sys_dictionary_data` VALUES (43744494792736768, '保密', '1', '43740085404827648', 'primary', 1, 1, '', 43728307660783616, '2022-07-24 17:05:00', NULL, NULL);\nINSERT INTO `sys_dictionary_data` VALUES (43744558772649984, '男', '2', '43740085404827648', 'success', 2, 1, '', 43728307660783616, '2022-07-24 17:05:16', NULL, NULL);\nINSERT INTO `sys_dictionary_data` VALUES (43744612069670912, '女', '3', '43740085404827648', 'warning', 3, 1, '', 43728307660783616, '2022-07-24 17:05:28', NULL, NULL);\nINSERT INTO `sys_dictionary_data` VALUES (43745015746265088, '缓存', '1', '43740399080046592', 'primary', 1, 1, '', 43728307660783616, '2022-07-24 17:07:04', NULL, NULL);\nINSERT INTO `sys_dictionary_data` VALUES (43745054946230272, '不缓存', '2', '43740399080046592', 'success', 2, 1, '', 43728307660783616, '2022-07-24 17:07:14', NULL, NULL);\nINSERT INTO `sys_dictionary_data` VALUES (43745129718087680, '是', '1', '43740514784116736', 'primary', 1, 1, '', 43728307660783616, '2022-07-24 17:07:32', NULL, NULL);\nINSERT INTO `sys_dictionary_data` VALUES (43745161041149952, '否', '2', '43740514784116736', 'success', 2, 1, '', 43728307660783616, '2022-07-24 17:07:39', NULL, NULL);\nINSERT INTO `sys_dictionary_data` VALUES (43745249540964352, '显示', '1', '43740632493064192', 'primary', 1, 1, '', 43728307660783616, '2022-07-24 17:08:00', NULL, NULL);\nINSERT INTO `sys_dictionary_data` VALUES (43745280234881024, '隐藏', '2', '43740632493064192', 'success', 2, 1, '', 43728307660783616, '2022-07-24 17:08:08', NULL, NULL);\nINSERT INTO `sys_dictionary_data` VALUES (43745438393696256, '默认(default)', 'default', '43741186304770048', 'default', 1, 1, '', 43728307660783616, '2022-07-24 17:08:45', NULL, NULL);\nINSERT INTO `sys_dictionary_data` VALUES (43745535164678144, '主要(primary)', 'primary', '43741186304770048', 'primary', 2, 1, '', 43728307660783616, '2022-07-24 17:09:08', NULL, NULL);\nINSERT INTO `sys_dictionary_data` VALUES (43745625509986304, '成功(success)', 'success', '43741186304770048', 'success', 3, 1, '', 43728307660783616, '2022-07-24 17:09:30', NULL, NULL);\nINSERT INTO `sys_dictionary_data` VALUES (43745706313252864, '信息(info)', 'info', '43741186304770048', 'info', 4, 1, '', 43728307660783616, '2022-07-24 17:09:49', NULL, NULL);\nINSERT INTO `sys_dictionary_data` VALUES (43745788475473920, '警告(warning)', 'warning', '43741186304770048', 'warning', 5, 1, '', 43728307660783616, '2022-07-24 17:10:09', NULL, NULL);\nINSERT INTO `sys_dictionary_data` VALUES (43745861963874304, '危险(danger)', 'danger', '43741186304770048', 'danger', 6, 1, '', 43728307660783616, '2022-07-24 17:10:26', NULL, NULL);\nINSERT INTO `sys_dictionary_data` VALUES (43747219504889856, '成功', '1', '43747174940409856', 'success', 1, 1, '', 43728307660783616, '2022-07-24 17:15:50', NULL, NULL);\nINSERT INTO `sys_dictionary_data` VALUES (43747249070538752, '失败', '2', '43747174940409856', 'danger', 2, 1, '', 43728307660783616, '2022-07-24 17:15:57', NULL, NULL);\nINSERT INTO `sys_dictionary_data` VALUES (43749310080221184, '其他', '1', '43749175543726080', 'primary', 1, 1, '', 43728307660783616, '2022-07-24 17:24:08', NULL, NULL);\nINSERT INTO `sys_dictionary_data` VALUES (43749355009605632, '新增', '2', '43749175543726080', 'info', 2, 1, '', 43728307660783616, '2022-07-24 17:24:19', NULL, NULL);\nINSERT INTO `sys_dictionary_data` VALUES (43749395618856960, '修改', '3', '43749175543726080', 'warning', 3, 1, '', 43728307660783616, '2022-07-24 17:24:29', NULL, NULL);\nINSERT INTO `sys_dictionary_data` VALUES (43749433191432192, '删除', '4', '43749175543726080', 'danger', 4, 1, '', 43728307660783616, '2022-07-24 17:24:38', NULL, NULL);\nINSERT INTO `sys_dictionary_data` VALUES (43749748162691072, '其他', '1', '43749667023880192', 'default', 1, 1, '', 43728307660783616, '2022-07-24 17:25:53', NULL, NULL);\nINSERT INTO `sys_dictionary_data` VALUES (43749792488095744, '后台用户', '2', '43749667023880192', 'primary', 2, 1, '', 43728307660783616, '2022-07-24 17:26:03', NULL, NULL);\nINSERT INTO `sys_dictionary_data` VALUES (43749847500587008, '手机端用户', '3', '43749667023880192', 'info', 3, 1, '', 43728307660783616, '2022-07-24 17:26:16', NULL, NULL);\nINSERT INTO `sys_dictionary_data` VALUES (43749905755275264, '博客用户', '4', '43749667023880192', 'warning', 4, 1, '', 43728307660783616, '2022-07-24 17:26:30', NULL, NULL);\nINSERT INTO `sys_dictionary_data` VALUES (43760754016387072, 'GET', 'GET', '43741186479310919', 'primary', 1, 1, '', 43728307660783616, '2022-07-24 18:09:37', NULL, NULL);\nINSERT INTO `sys_dictionary_data` VALUES (43760839513079808, 'POST', 'POST', '43741186479310919', 'info', 2, 1, '', 43728307660783616, '2022-07-24 18:09:57', NULL, NULL);\nINSERT INTO `sys_dictionary_data` VALUES (43760910166130688, 'PUT', 'PUT', '43741186479310919', 'warning', 3, 1, '', 43728307660783616, '2022-07-24 18:10:14', NULL, NULL);\nINSERT INTO `sys_dictionary_data` VALUES (43760951937204224, 'DELETE', 'DELETE', '43741186479310919', 'danger', 4, 1, '', 43728307660783616, '2022-07-24 18:10:24', NULL, NULL);\nINSERT INTO `sys_dictionary_data` VALUES (43832012938674176, '成功', '1', '43831966709055488', 'primary', 1, 1, '', 43728307660783616, '2022-07-24 22:52:46', NULL, NULL);\nINSERT INTO `sys_dictionary_data` VALUES (43832045444530176, '异常', '2', '43831966709055488', 'danger', 2, 1, '', 43728307660783616, '2022-07-24 22:52:54', NULL, NULL);\nINSERT INTO `sys_dictionary_data` VALUES (52090134002139136, '系统内置', '1', '52090055367327744', 'primary', 1, 1, '', 43728307660783616, '2022-08-16 17:47:36', NULL, NULL);\nINSERT INTO `sys_dictionary_data` VALUES (67409162056761344, '导航管理', '2', '52090055367327744', 'info', 2, 1, '', 43728307660783616, '2022-09-28 00:19:57', 43728307660783616, '2022-12-10 23:13:20');\nINSERT INTO `sys_dictionary_data` VALUES (78246089743925248, '是', '1', '78246041425543168', 'primary', 1, 1, '', 43728307660783616, '2022-10-27 22:02:02', NULL, NULL);\nINSERT INTO `sys_dictionary_data` VALUES (78246125886242816, '否', '2', '78246041425543168', 'danger', 2, 1, '', 43728307660783616, '2022-10-27 22:02:10', NULL, NULL);\nINSERT INTO `sys_dictionary_data` VALUES (102543668505739264, '待审核', '1', '102543613031874560', 'primary', 1, 1, '', 43728307660783616, '2023-01-02 23:11:56', NULL, NULL);\nINSERT INTO `sys_dictionary_data` VALUES (102543729629331456, '已通过', '2', '102543613031874560', 'success', 2, 1, '', 43728307660783616, '2023-01-02 23:12:10', NULL, NULL);\nINSERT INTO `sys_dictionary_data` VALUES (102543767784914944, '已驳回', '3', '102543613031874560', 'danger', 3, 1, '', 43728307660783616, '2023-01-02 23:12:19', NULL, NULL);\nINSERT INTO `sys_dictionary_data` VALUES (143468816637100032, '主题配置', '3', '52090055367327744', 'warning', 3, 1, '', 43728307660783616, '2023-04-25 21:33:51', 43728307660783616, '2023-04-25 21:34:08');\n\n-- ----------------------------\n-- Table structure for sys_menu\n-- ----------------------------\nDROP TABLE IF EXISTS `sys_menu`;\nCREATE TABLE `sys_menu`  (\n  `id` bigint(20) NOT NULL COMMENT '菜单 id',\n  `menu_name` varchar(50) NOT NULL COMMENT '菜单名称',\n  `parent_id` bigint(20) NULL DEFAULT 0 COMMENT '父菜单ID',\n  `sort` int(11) NULL DEFAULT 0 COMMENT '排序',\n  `menu_type` char(1) NULL DEFAULT '' COMMENT '菜单类型（D 目录，M 菜单，B 按钮）',\n  `permission_code` varchar(100) NULL DEFAULT '' COMMENT '权限标识',\n  `router_url` varchar(200) NULL DEFAULT '' COMMENT '路由地址',\n  `component_path` varchar(255) NULL DEFAULT '' COMMENT '组件路径',\n  `router_param` varchar(255) NULL DEFAULT '' COMMENT '路由参数',\n  `has_frame` tinyint(1) NULL DEFAULT 2 COMMENT '是否为外链（1是，2否）',\n  `has_cache` tinyint(1) NULL DEFAULT 1 COMMENT '是否缓存（1缓存，2不缓存）',\n  `has_permission` tinyint(1) NULL DEFAULT 1 COMMENT '是否需要权限（1 是，2 否）',\n  `menu_icon` varchar(100) NULL DEFAULT '' COMMENT '菜单图标',\n  `show_status` tinyint(1) NULL DEFAULT 1 COMMENT '显示状态（1显示，2隐藏）',\n  `status` tinyint(1) NULL DEFAULT 1 COMMENT '状态（1 正常，2 停用）',\n  `fk_create_user_id` bigint(20) NULL DEFAULT NULL COMMENT '创建人用户 id',\n  `gmt_create` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `fk_modify_user_id` bigint(20) NULL DEFAULT NULL COMMENT '修改人用户 id',\n  `gmt_modify` datetime NULL DEFAULT NULL COMMENT '修改时间',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB COMMENT = '系统菜单' ROW_FORMAT = DYNAMIC;\n\n-- ----------------------------\n-- Records of sys_menu\n-- ----------------------------\nINSERT INTO `sys_menu` VALUES (43737833021636608, '系统管理', 0, 1, 'D', NULL, 'system', NULL, NULL, 2, 1, 1, 'system', 1, 1, 43728307660783616, '2022-07-24 16:38:32', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (43738261293629440, '用户管理', 43737833021636608, 1, 'M', 'system:user:page', 'user', 'system/user/index', NULL, 2, 1, 1, 'user', 1, 1, 43728307660783616, '2022-07-24 16:40:14', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (43738420110950400, '角色管理', 43737833021636608, 2, 'M', 'system:role:page', 'role', 'system/role/index', NULL, 2, 1, 1, 'peoples', 1, 1, 43728307660783616, '2022-07-24 16:40:52', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (43738641683447808, '菜单管理', 43737833021636608, 3, 'M', 'system:menu:list', 'menu', 'system/menu/index', NULL, 2, 1, 1, 'menu', 1, 1, 43728307660783616, '2022-07-24 16:41:45', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (43738896395141120, '数据字典', 43737833021636608, 4, 'M', 'system:dictionary:page', 'dictionary', 'system/dictionary/index', NULL, 2, 1, 1, 'dict', 1, 1, 43728307660783616, '2022-07-24 16:42:46', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (43739074015526912, 'API 管理', 43737833021636608, 5, 'M', 'system:apiCatetory:page', 'api', 'system/api/category', NULL, 2, 1, 1, 'documentation', 1, 1, 43728307660783616, '2022-07-24 16:43:28', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (43746238872420352, '日志审计', 0, 3, 'D', '', 'log', '', '', 2, 1, 1, 'edit', 1, 1, 43728307660783616, '2022-07-24 17:11:56', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (43746395802304512, '登录日志', 43746238872420352, 1, 'M', 'log:login:page', 'login', 'system/log/login/index', '', 2, 1, 1, 'logininfor', 1, 1, 43728307660783616, '2022-07-24 17:12:34', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (43746859121901568, '操作日志', 43746238872420352, 2, 'M', 'log:operation:page', 'operation', 'system/log/operation/index', '', 2, 1, 1, 'form', 1, 1, 43728307660783616, '2022-07-24 17:14:24', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (43779604086784000, '新增', 43738261293629440, 1, 'B', 'system:user:add', '', 'system/log/operation/index', '', 2, 1, 1, '', 1, 1, 43728307660783616, '2022-07-24 19:24:31', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (43779675532558336, '修改', 43738261293629440, 2, 'B', 'system:user:update', '', 'system/log/operation/index', '', 2, 1, 1, '', 1, 1, 43728307660783616, '2022-07-24 19:24:48', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (43779734387032064, '删除', 43738261293629440, 3, 'B', 'system:user:delete', '', 'system/log/operation/index', '', 2, 1, 1, '', 1, 1, 43728307660783616, '2022-07-24 19:25:02', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (43779823738290176, '重置密码', 43738261293629440, 4, 'B', 'system:user:resetPassword', '', 'system/log/operation/index', '', 2, 1, 1, '', 1, 1, 43728307660783616, '2022-07-24 19:25:23', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (43779962246791168, '新增', 43738420110950400, 1, 'B', 'system:role:add', '', '', '', 2, 1, 1, '', 1, 1, 43728307660783616, '2022-07-24 19:25:56', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (43780015896133632, '修改', 43738420110950400, 2, 'B', 'system:role:update', '', '', '', 2, 1, 1, '', 1, 1, 43728307660783616, '2022-07-24 19:26:09', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (43780063644090368, '删除', 43738420110950400, 3, 'B', 'system:role:delete', '', '', '', 2, 1, 1, '', 1, 1, 43728307660783616, '2022-07-24 19:26:21', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (43780156136882176, '分配用户', 43738420110950400, 4, 'B', 'system:role:authUser', '', '', '', 2, 1, 1, '', 1, 1, 43728307660783616, '2022-07-24 19:26:43', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (43780326794723328, '取消用户授权', 43738420110950400, 5, 'B', 'system:user:deleteAuthUser', '', '', '', 2, 1, 1, '', 1, 1, 43728307660783616, '2022-07-24 19:27:23', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (43780526653308928, '新增', 43738641683447808, 1, 'B', 'system:menu:add', '', '', '', 2, 1, 1, '', 1, 1, 43728307660783616, '2022-07-24 19:28:11', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (43780571997929472, '修改', 43738641683447808, 2, 'B', 'system:menu:update', '', '', '', 2, 1, 1, '', 1, 1, 43728307660783616, '2022-07-24 19:28:22', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (43780625399808000, '删除', 43738641683447808, 3, 'B', 'system:menu:delete', '', '', '', 2, 1, 1, '', 1, 1, 43728307660783616, '2022-07-24 19:28:35', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (43780699760623616, '分配 API', 43738641683447808, 4, 'B', 'system:menu:allocateApi', '', '', '', 2, 1, 1, '', 1, 1, 43728307660783616, '2022-07-24 19:28:52', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (43780957378969600, '新增', 43738896395141120, 1, 'B', 'system:dictionary:add', '', '', '', 2, 1, 1, '', 1, 1, 43728307660783616, '2022-07-24 19:29:54', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (43781009912627200, '修改', 43738896395141120, 2, 'B', 'system:dictionary:update', '', '', '', 2, 1, 1, '', 1, 1, 43728307660783616, '2022-07-24 19:30:06', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (43781060101668864, '删除', 43738896395141120, 3, 'B', 'system:dictionary:delete', '', '', '', 2, 1, 1, '', 1, 1, 43728307660783616, '2022-07-24 19:30:18', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (43781692732735488, '分配数据', 43738896395141120, 4, 'B', 'system:dictionary:data:page', '', '', '', 2, 1, 1, '', 1, 1, 43728307660783616, '2022-07-24 19:32:49', 43728307660783616, '2022-09-12 16:32:11');\nINSERT INTO `sys_menu` VALUES (43781756515516416, '数据新增', 43738896395141120, 5, 'B', 'system:dictionaryData:add', '', '', '', 2, 1, 1, '', 1, 1, 43728307660783616, '2022-07-24 19:33:04', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (43781829500600320, '数据修改', 43738896395141120, 6, 'B', 'system:dictionary:data:update', '', '', '', 2, 1, 1, '', 1, 1, 43728307660783616, '2022-07-24 19:33:22', 43728307660783616, '2022-09-12 16:32:18');\nINSERT INTO `sys_menu` VALUES (43781877487632384, '数据删除', 43738896395141120, 7, 'B', 'system:dictionary:data:delete', '', '', '', 2, 1, 1, '', 1, 1, 43728307660783616, '2022-07-24 19:33:33', 43728307660783616, '2022-09-12 16:32:23');\nINSERT INTO `sys_menu` VALUES (43820727559782400, '分类新增', 43739074015526912, 1, 'B', 'system:apiCategory:add', '', '', '', 2, 1, 1, '', 1, 1, 43728307660783616, '2022-07-24 22:07:56', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (43820963283861504, '分类修改', 43739074015526912, 2, 'B', 'system:apiCategory:update', '', '', '', 2, 1, 1, '', 1, 1, 43728307660783616, '2022-07-24 22:08:52', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (43821061610930176, '分类删除', 43739074015526912, 3, 'B', 'system:apiCategory:delete', '', '', '', 2, 1, 1, '', 1, 1, 43728307660783616, '2022-07-24 22:09:15', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (43821227462098944, '分配数据', 43739074015526912, 4, 'B', 'system:api:page', '', '', '', 2, 1, 1, '', 1, 1, 43728307660783616, '2022-07-24 22:09:55', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (43821354134274048, '数据新增', 43739074015526912, 5, 'B', 'system:api:add', '', '', '', 2, 1, 1, '', 1, 1, 43728307660783616, '2022-07-24 22:10:25', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (43821418193879040, '数据修改', 43739074015526912, 6, 'B', 'system:api:update', '', '', '', 2, 1, 1, '', 1, 1, 43728307660783616, '2022-07-24 22:10:40', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (43821478692519936, '数据删除', 43739074015526912, 7, 'B', 'system:api:delete', '', '', '', 2, 1, 1, '', 1, 1, 43728307660783616, '2022-07-24 22:10:55', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (52073868344426496, '参数配置', 43737833021636608, 6, 'M', 'system:param:page', 'param', 'system/param/index', '', 2, 1, 1, 'edit', 1, 1, 43728307660783616, '2022-08-16 16:42:58', 43728307660783616, '2022-08-16 16:59:24');\nINSERT INTO `sys_menu` VALUES (52132922030817280, '新增', 52073868344426496, 1, 'B', 'system:param:add', '', '', '', 2, 1, 1, '', 1, 1, 43728307660783616, '2022-08-16 20:37:37', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (52133024904511488, '修改', 52073868344426496, 2, 'B', 'system:param:update', '', '', '', 2, 1, 1, '', 1, 1, 43728307660783616, '2022-08-16 20:38:02', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (52133076263763968, '删除', 52073868344426496, 3, 'B', 'system:param:delete', '', '', '', 2, 1, 1, '', 1, 1, 43728307660783616, '2022-08-16 20:38:14', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (86832919972151303, '分类管理', 86833558454272000, 1, 'M', 'nav:category:page', 'category', 'nav/category/index', '', 2, 1, 1, 'heart', 1, 1, 43728307660783616, '2022-11-20 14:43:41', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (86832919972151304, '新增', 86832919972151303, 1, 'B', 'nav:category:add', '', '', '', 2, 1, 1, '', 1, 1, 43728307660783616, '2022-11-20 14:43:41', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (86832919972151305, '修改', 86832919972151303, 2, 'B', 'nav:category:update', '', '', '', 2, 1, 1, '', 1, 1, 43728307660783616, '2022-11-20 14:43:41', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (86832919972151306, '删除', 86832919972151303, 3, 'B', 'nav:category:delete', '', '', '', 2, 1, 1, '', 1, 1, 43728307660783616, '2022-11-20 14:43:41', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (86833558454272000, '导航管理', 0, 4, 'D', '', 'nav', '', '', 2, 1, 1, 'computer', 1, 1, 43728307660783616, '2022-11-20 14:45:34', 43728307660783616, '2023-05-21 16:34:42');\nINSERT INTO `sys_menu` VALUES (86859268749262855, '网站管理', 86833558454272000, 2, 'M', 'nav:site:page', 'site', 'nav/site/index', '', 2, 1, 1, 'star', 1, 1, 43728307660783616, '2022-11-20 16:27:55', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (86859268749262856, '新增', 86859268749262855, 1, 'B', 'nav:site:add', '', '', '', 2, 1, 1, '', 1, 1, 43728307660783616, '2022-11-20 16:27:55', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (86859268749262857, '修改', 86859268749262855, 2, 'B', 'nav:site:update', '', '', '', 2, 1, 1, '', 1, 1, 43728307660783616, '2022-11-20 16:27:55', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (86859268749262858, '删除', 86859268749262855, 3, 'B', 'nav:site:delete', '', '', '', 2, 1, 1, '', 1, 1, 43728307660783616, '2022-11-20 16:27:55', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (90179495205535744, '评论管理', 86833558454272000, 3, 'M', 'nav:comment:page', 'comment', 'nav/comment/index', '', 2, 1, 1, 'message', 1, 1, 43728307660783616, '2022-11-29 20:21:07', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (92733418365779968, '通过', 90179495205535744, 1, 'B', 'nav:comment:pass', '', '', '', 2, 1, 1, '', 1, 1, 43728307660783616, '2022-12-06 21:29:30', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (92733594673348608, '驳回', 90179495205535744, 2, 'B', 'nav:comment:reject', '', '', '', 2, 1, 1, '', 1, 1, 43728307660783616, '2022-12-06 21:30:12', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (92733726269636608, '置顶', 90179495205535744, 3, 'B', 'nav:comment:sticky', '', '', '', 2, 1, 1, '', 1, 1, 43728307660783616, '2022-12-06 21:30:43', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (92733853243801600, '取消置顶', 90179495205535744, 4, 'B', 'nav:comment:sticky:cancle', '', '', '', 2, 1, 1, '', 1, 1, 43728307660783616, '2022-12-06 21:31:14', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (92733965122666496, '删除', 90179495205535744, 5, 'B', 'nav:comment:delete', '', '', '', 2, 1, 1, '', 1, 1, 43728307660783616, '2022-12-06 21:31:40', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (94456071543848960, '网站配置', 86833558454272000, 6, 'M', 'nav:config:get', 'config', 'nav/config/index', '', 2, 1, 1, 'website', 1, 1, 43728307660783616, '2022-12-11 15:34:42', 43728307660783616, '2022-12-11 15:37:21');\nINSERT INTO `sys_menu` VALUES (94456484632461312, '修改', 94456071543848960, 1, 'B', 'nav:config:update', '', '', '', 2, 1, 1, '', 1, 1, 43728307660783616, '2022-12-11 15:36:21', NULL, NULL);\n\n-- ----------------------------\n-- Table structure for sys_param\n-- ----------------------------\nDROP TABLE IF EXISTS `sys_param`;\nCREATE TABLE `sys_param`  (\n  `id` bigint(20) NOT NULL COMMENT '参数 id',\n  `param_name` varchar(100) NULL DEFAULT '' COMMENT '参数名称',\n  `param_key` varchar(100) NULL DEFAULT '' COMMENT '参数键',\n  `param_value` varchar(200) NULL DEFAULT '' COMMENT '参数值',\n  `param_type` tinyint(1) NULL DEFAULT NULL COMMENT '参数类型（1 系统参数）',\n  `sort` int(11) NULL DEFAULT 0 COMMENT '排序',\n  `remark` varchar(500) NULL DEFAULT '' COMMENT '备注',\n  `fk_create_user_id` bigint(20) NULL DEFAULT NULL COMMENT '创建人用户 id',\n  `gmt_create` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `fk_modify_user_id` bigint(20) NULL DEFAULT NULL COMMENT '修改人用户 id',\n  `gmt_modify` datetime NULL DEFAULT NULL COMMENT '修改时间',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB COMMENT = '参数配置' ROW_FORMAT = DYNAMIC;\n\n-- ----------------------------\n-- Records of sys_param\n-- ----------------------------\nINSERT INTO `sys_param` VALUES (52091717515476992, '默认密码', 'SYS_DEFAULT_PASSWORD', 'gesdh@2022', 1, 1, '默认密码', 43728307660783616, '2022-08-16 17:53:53', 43728307660783616, '2022-12-10 23:22:55');\nINSERT INTO `sys_param` VALUES (70810422013329408, '评论开启', 'NAV_COMMENT_OPEN', 'true', 2, 2, '评论是否开启', 43728307660783616, '2022-10-07 09:35:20', 43728307660783616, '2023-04-15 00:55:55');\nINSERT INTO `sys_param` VALUES (140123273550626816, '评论无登录开启', 'NAV_COMMENT_NOT_LOGIN_OPEN', 'true', 1, 6, '是否开启不用登录就能提交评论', 43728307660783616, '2023-04-16 15:59:52', 43728307660783616, '2023-04-16 16:23:53');\n\n-- ----------------------------\n-- Table structure for sys_role\n-- ----------------------------\nDROP TABLE IF EXISTS `sys_role`;\nCREATE TABLE `sys_role`  (\n  `id` bigint(20) NOT NULL COMMENT '角色 id',\n  `role_name` varchar(30) NOT NULL COMMENT '角色名称',\n  `role_code` varchar(100) NOT NULL COMMENT '角色编码',\n  `sort` int(11) NULL DEFAULT 0 COMMENT '排序',\n  `status` tinyint(1) NULL DEFAULT 1 COMMENT '状态（1 正常，2 停用）',\n  `remark` varchar(500) NULL DEFAULT '' COMMENT '备注',\n  `fk_create_user_id` bigint(20) NULL DEFAULT NULL COMMENT '创建人用户 id',\n  `gmt_create` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `fk_modify_user_id` bigint(20) NULL DEFAULT NULL COMMENT '修改人用户 id',\n  `gmt_modify` datetime NULL DEFAULT NULL COMMENT '修改时间',\n  PRIMARY KEY (`id`) USING BTREE,\n  UNIQUE INDEX `unique_role_code`(`role_code` ASC) USING BTREE COMMENT '角色编码唯一索引'\n) ENGINE = InnoDB COMMENT = '系统角色' ROW_FORMAT = DYNAMIC;\n\n-- ----------------------------\n-- Records of sys_role\n-- ----------------------------\nINSERT INTO `sys_role` VALUES (43735562707795968, '超级管理员', 'superAdmin', 1, 1, '', 43728307660783616, '2022-07-24 16:29:31', NULL, NULL);\nINSERT INTO `sys_role` VALUES (43767332559912960, '普通角色', 'commonRole', 2, 1, '', 43728307660783616, '2022-07-24 18:35:45', 43728307660783616, '2022-10-31 00:02:36');\n\n-- ----------------------------\n-- Table structure for sys_role_menu\n-- ----------------------------\nDROP TABLE IF EXISTS `sys_role_menu`;\nCREATE TABLE `sys_role_menu`  (\n  `id` bigint(20) NOT NULL COMMENT '角色菜单 id',\n  `fk_role_id` bigint(20) NOT NULL COMMENT '角色 id',\n  `fk_menu_id` bigint(20) NOT NULL COMMENT '菜单 id',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB COMMENT = '系统角色菜单' ROW_FORMAT = DYNAMIC;\n\n-- ----------------------------\n-- Records of sys_role_menu\n-- ----------------------------\nINSERT INTO `sys_role_menu` VALUES (86492067173236736, 43767332559912960, 43737833021636608);\nINSERT INTO `sys_role_menu` VALUES (86492067177431040, 43767332559912960, 43738261293629440);\nINSERT INTO `sys_role_menu` VALUES (86492067177431041, 43767332559912960, 43779604086784000);\nINSERT INTO `sys_role_menu` VALUES (86492067177431042, 43767332559912960, 43779675532558336);\nINSERT INTO `sys_role_menu` VALUES (86492067181625344, 43767332559912960, 43779734387032064);\nINSERT INTO `sys_role_menu` VALUES (86492067181625345, 43767332559912960, 43779823738290176);\nINSERT INTO `sys_role_menu` VALUES (86492067185819648, 43767332559912960, 43738420110950400);\nINSERT INTO `sys_role_menu` VALUES (86492067185819649, 43767332559912960, 43779962246791168);\nINSERT INTO `sys_role_menu` VALUES (86492067185819650, 43767332559912960, 43780015896133632);\nINSERT INTO `sys_role_menu` VALUES (86492067185819651, 43767332559912960, 43780063644090368);\nINSERT INTO `sys_role_menu` VALUES (86492067190013952, 43767332559912960, 43780156136882176);\nINSERT INTO `sys_role_menu` VALUES (86492067190013953, 43767332559912960, 43780326794723328);\nINSERT INTO `sys_role_menu` VALUES (86492067190013954, 43767332559912960, 43738641683447808);\nINSERT INTO `sys_role_menu` VALUES (86492067190013955, 43767332559912960, 43780526653308928);\nINSERT INTO `sys_role_menu` VALUES (86492067190013956, 43767332559912960, 43780571997929472);\nINSERT INTO `sys_role_menu` VALUES (86492067194208256, 43767332559912960, 43780625399808000);\nINSERT INTO `sys_role_menu` VALUES (86492067194208257, 43767332559912960, 43780699760623616);\nINSERT INTO `sys_role_menu` VALUES (86492067194208258, 43767332559912960, 43738896395141120);\nINSERT INTO `sys_role_menu` VALUES (86492067194208259, 43767332559912960, 43780957378969600);\nINSERT INTO `sys_role_menu` VALUES (86492067198402560, 43767332559912960, 43781009912627200);\nINSERT INTO `sys_role_menu` VALUES (86492067198402561, 43767332559912960, 43781060101668864);\nINSERT INTO `sys_role_menu` VALUES (86492067198402562, 43767332559912960, 43781692732735488);\nINSERT INTO `sys_role_menu` VALUES (86492067198402563, 43767332559912960, 43781756515516416);\nINSERT INTO `sys_role_menu` VALUES (86492067198402564, 43767332559912960, 43781829500600320);\nINSERT INTO `sys_role_menu` VALUES (86492067202596864, 43767332559912960, 43781877487632384);\nINSERT INTO `sys_role_menu` VALUES (86492067202596865, 43767332559912960, 43739074015526912);\nINSERT INTO `sys_role_menu` VALUES (86492067202596866, 43767332559912960, 43820727559782400);\nINSERT INTO `sys_role_menu` VALUES (86492067202596867, 43767332559912960, 43820963283861504);\nINSERT INTO `sys_role_menu` VALUES (86492067206791168, 43767332559912960, 43821061610930176);\nINSERT INTO `sys_role_menu` VALUES (86492067206791169, 43767332559912960, 43821227462098944);\nINSERT INTO `sys_role_menu` VALUES (86492067206791170, 43767332559912960, 43821354134274048);\nINSERT INTO `sys_role_menu` VALUES (86492067206791171, 43767332559912960, 43821418193879040);\nINSERT INTO `sys_role_menu` VALUES (86492067206791172, 43767332559912960, 43821478692519936);\nINSERT INTO `sys_role_menu` VALUES (86492067210985472, 43767332559912960, 52073868344426496);\nINSERT INTO `sys_role_menu` VALUES (86492067210985473, 43767332559912960, 52132922030817280);\nINSERT INTO `sys_role_menu` VALUES (86492067210985474, 43767332559912960, 52133024904511488);\nINSERT INTO `sys_role_menu` VALUES (86492067210985475, 43767332559912960, 52133076263763968);\nINSERT INTO `sys_role_menu` VALUES (86492067215179776, 43767332559912960, 86489144947113984);\nINSERT INTO `sys_role_menu` VALUES (86492067215179777, 43767332559912960, 86489393493180416);\nINSERT INTO `sys_role_menu` VALUES (86492067215179778, 43767332559912960, 86490122438049792);\nINSERT INTO `sys_role_menu` VALUES (86492067215179779, 43767332559912960, 86490199768432640);\nINSERT INTO `sys_role_menu` VALUES (86492067215179780, 43767332559912960, 86490267254784000);\nINSERT INTO `sys_role_menu` VALUES (86492067219374080, 43767332559912960, 86490311785709568);\nINSERT INTO `sys_role_menu` VALUES (86492067219374081, 43767332559912960, 86490372783472640);\nINSERT INTO `sys_role_menu` VALUES (86492067219374082, 43767332559912960, 86490430765531136);\nINSERT INTO `sys_role_menu` VALUES (86492067223568384, 43767332559912960, 43746238872420352);\nINSERT INTO `sys_role_menu` VALUES (86492067223568385, 43767332559912960, 43746395802304512);\nINSERT INTO `sys_role_menu` VALUES (86492067223568386, 43767332559912960, 43746859121901568);\nINSERT INTO `sys_role_menu` VALUES (86492067223568387, 43767332559912960, 52099144042414080);\nINSERT INTO `sys_role_menu` VALUES (86492067223568388, 43767332559912960, 52099447332536320);\nINSERT INTO `sys_role_menu` VALUES (86492067227762688, 43767332559912960, 52099788358811648);\nINSERT INTO `sys_role_menu` VALUES (86492067227762689, 43767332559912960, 52099936145113088);\nINSERT INTO `sys_role_menu` VALUES (86492067227762690, 43767332559912960, 52100202487611392);\nINSERT INTO `sys_role_menu` VALUES (86492067227762691, 43767332559912960, 52100286625349632);\nINSERT INTO `sys_role_menu` VALUES (86492067231956992, 43767332559912960, 52114381915291648);\nINSERT INTO `sys_role_menu` VALUES (86492067231956993, 43767332559912960, 52114517684912128);\nINSERT INTO `sys_role_menu` VALUES (86492067231956994, 43767332559912960, 52114568226275328);\nINSERT INTO `sys_role_menu` VALUES (86492067231956995, 43767332559912960, 52114614602694656);\nINSERT INTO `sys_role_menu` VALUES (86492067236151296, 43767332559912960, 52114878701240320);\nINSERT INTO `sys_role_menu` VALUES (86492067236151297, 43767332559912960, 52114929196466176);\nINSERT INTO `sys_role_menu` VALUES (86492067236151298, 43767332559912960, 52114968178327552);\nINSERT INTO `sys_role_menu` VALUES (86492067236151299, 43767332559912960, 52115022117076992);\nINSERT INTO `sys_role_menu` VALUES (86492067236151300, 43767332559912960, 55073494739714048);\nINSERT INTO `sys_role_menu` VALUES (86492067240345600, 43767332559912960, 61851202979102720);\nINSERT INTO `sys_role_menu` VALUES (86492067240345601, 43767332559912960, 61851247690383360);\nINSERT INTO `sys_role_menu` VALUES (86492067240345602, 43767332559912960, 61851299972382720);\nINSERT INTO `sys_role_menu` VALUES (86492067240345603, 43767332559912960, 61851660971933696);\nINSERT INTO `sys_role_menu` VALUES (86492067244539904, 43767332559912960, 61851798276669440);\nINSERT INTO `sys_role_menu` VALUES (86492067244539905, 43767332559912960, 61851972713578496);\nINSERT INTO `sys_role_menu` VALUES (86492067244539906, 43767332559912960, 70575761538416640);\nINSERT INTO `sys_role_menu` VALUES (86492067244539907, 43767332559912960, 70591784899575808);\nINSERT INTO `sys_role_menu` VALUES (86492067244539908, 43767332559912960, 70591842319597568);\nINSERT INTO `sys_role_menu` VALUES (86492067248734208, 43767332559912960, 70591937538686976);\nINSERT INTO `sys_role_menu` VALUES (86492067248734209, 43767332559912960, 77878458918633472);\nINSERT INTO `sys_role_menu` VALUES (86492067248734210, 43767332559912960, 78232604175761408);\nINSERT INTO `sys_role_menu` VALUES (86492067248734211, 43767332559912960, 78232661448982528);\nINSERT INTO `sys_role_menu` VALUES (86492067252928512, 43767332559912960, 78243661845889024);\nINSERT INTO `sys_role_menu` VALUES (86492067252928513, 43767332559912960, 78261905201823744);\nINSERT INTO `sys_role_menu` VALUES (86492067252928514, 43767332559912960, 78261973308932096);\nINSERT INTO `sys_role_menu` VALUES (86492067252928515, 43767332559912960, 78262028635996160);\nINSERT INTO `sys_role_menu` VALUES (86492067257122816, 43767332559912960, 78582377730801664);\nINSERT INTO `sys_role_menu` VALUES (86492067257122817, 43767332559912960, 78594439798325248);\nINSERT INTO `sys_role_menu` VALUES (86492067257122818, 43767332559912960, 78595081065463808);\nINSERT INTO `sys_role_menu` VALUES (104307851295457280, 104307851257708544, 104307285068611584);\n\n-- ----------------------------\n-- Table structure for sys_user\n-- ----------------------------\nDROP TABLE IF EXISTS `sys_user`;\nCREATE TABLE `sys_user`  (\n  `id` bigint(20) NOT NULL COMMENT '用户 id',\n  `username` varchar(40) NOT NULL DEFAULT '' COMMENT '用户名',\n  `nick_name` varchar(40) NULL DEFAULT '' COMMENT '昵称',\n  `password` varchar(100) NULL DEFAULT '' COMMENT '密码',\n  `sex` tinyint(1) NULL DEFAULT 1 COMMENT '用户性别（1 保密，2 男，3 女）',\n  `user_type` tinyint(1) NULL DEFAULT 1 COMMENT '用户类型（1 后台用户，2 博客用户）',\n  `email` varchar(50) NULL DEFAULT '' COMMENT '邮箱',\n  `mobile_phone` varchar(11) NULL DEFAULT '' COMMENT '手机号码',\n  `avatar` varchar(100) NULL DEFAULT '' COMMENT '头像地址',\n  `status` tinyint(1) NULL DEFAULT 1 COMMENT '状态（1 正常，2 停用）',\n  `remark` varchar(500) NULL DEFAULT '' COMMENT '备注',\n  `del_flag` tinyint(1) NULL DEFAULT 1 COMMENT '删除标志（1 未删除，2 已删除）',\n  `fk_create_user_id` bigint(20) NULL DEFAULT NULL COMMENT '创建人用户 id',\n  `gmt_create` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `fk_modify_user_id` bigint(20) NULL DEFAULT NULL COMMENT '修改人用户 id',\n  `gmt_modify` datetime NULL DEFAULT NULL COMMENT '修改时间',\n  PRIMARY KEY (`id`) USING BTREE,\n  UNIQUE INDEX `unique_username`(`username` ASC) USING BTREE COMMENT '用户名唯一索引'\n) ENGINE = InnoDB COMMENT = '系统用户' ROW_FORMAT = DYNAMIC;\n\n-- ----------------------------\n-- Records of sys_user\n-- ----------------------------\nINSERT INTO `sys_user` VALUES (43728307660783616, 'admin', '管理员', '$2a$10$uYSATn7m4cmLFtr4uz7rs.W8a0s3EbwZJCyGnthUrZacMgKh4GoIG', 1, 1, '497301391@qq.com', '', '', 1, '我是管理员', 1, 43728307660783616, '2022-07-24 16:02:02', 43728307660783616, '2023-01-04 22:44:48');\nINSERT INTO `sys_user` VALUES (43767104519798784, 'geshanzsq', '格姗知识圈', '$2a$10$rm5jwWc6lRM11Q.OOEH2T.t41GnkzITcvVAI19LdAicn5/AJdD0im', 3, 1, '497301391@qq.com', '', '', 1, '', 1, 43728307660783616, '2022-07-24 18:34:51', 43728307660783616, '2022-08-15 00:03:28');\nINSERT INTO `sys_user` VALUES (43767196689629184, 'xgz', '小格子', '$2a$10$mh3A6xcNuwM6zfjQgQ/xHOmilj92Vj1JWbiB9VTktUD7zccynLD02', 2, 1, '497301391@qq.com', '', '', 1, '', 1, 43728307660783616, '2022-07-24 18:35:13', NULL, NULL);\n\n-- ----------------------------\n-- Table structure for sys_user_role\n-- ----------------------------\nDROP TABLE IF EXISTS `sys_user_role`;\nCREATE TABLE `sys_user_role`  (\n  `id` bigint(20) NOT NULL COMMENT '用户角色 id',\n  `fk_user_id` bigint(20) NOT NULL COMMENT '用户 id',\n  `fk_role_id` bigint(20) NOT NULL COMMENT '角色 id',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB COMMENT = '系统用户角色' ROW_FORMAT = DYNAMIC;\n\n-- ----------------------------\n-- Records of sys_user_role\n-- ----------------------------\nINSERT INTO `sys_user_role` VALUES (43737619212795904, 43728307660783616, 43735562707795968);\nINSERT INTO `sys_user_role` VALUES (43767363438379008, 43767104519798784, 43767332559912960);\nINSERT INTO `sys_user_role` VALUES (43767363438379009, 43767196689629184, 43767332559912960);\n\nSET FOREIGN_KEY_CHECKS = 1;\n"
  },
  {
    "path": "docker/nginx/conf.d/gesdh.cn.conf",
    "content": "server {\n    listen       80;\n    server_name  localhost;\n\n    # 前端项目，代理到 docker 容器\n    location / {\n        proxy_pass http://127.0.0.1:8023;\n    }\n\n    # 后端项目配置\n    location /geshanzsq-nav-api/ {\n        # 项目 IP 地址和端口，如果不在一台服务器，请填写对应的 IP；如果后端端口修改后，请填写修改后的端口\n        proxy_pass http://127.0.0.1:8083/geshanzsq-nav-api/;\n        proxy_set_header Host $host;\n        proxy_set_header X-Real-IP $remote_addr;\n        proxy_set_header REMOTE-HOST $remote_addr;\n        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n    }\n}"
  },
  {
    "path": "docker/nginx/docker-compose.yml",
    "content": "version: '3'\nservices:\n  nginx:\n    restart: always\n    container_name: nginx\n    image: nginx\n    # 使用宿主机网络\n    # network_mode: \"host\"\n    ports:\n      - 80:80\n      - 443:443\n    volumes:\n      - ./html:/usr/share/nginx/html\n      - ./www:/var/www\n      - ./logs:/var/log/nginx\n      # 有可能会出现不能挂载，这个时候用手动拷贝配置文件就行\n      - ./nginx.conf/:/etc/nginx/nginx.conf\n      - ./cert:/etc/nginx/cert\n      - ./conf.d:/etc/nginx/conf.d\n    environment:\n      - NGINX_PORT=80\n      - TZ=Asia/Shanghai\n    privileged: true"
  },
  {
    "path": "docker/nginx/nginx.conf",
    "content": "\n#user  nobody;\nworker_processes  1;\n\n#error_log  logs/error.log;\n#error_log  logs/error.log  notice;\n#error_log  logs/error.log  info;\n\n#pid        logs/nginx.pid;\n\n\nevents {\n    worker_connections  1024;\n}\n\n\nhttp {\n    include       mime.types;\n    default_type  application/octet-stream;\n\n    #log_format  main  '$remote_addr - $remote_user [$time_local] \"$request\" '\n    #                  '$status $body_bytes_sent \"$http_referer\" '\n    #                  '\"$http_user_agent\" \"$http_x_forwarded_for\"';\n\n    #access_log  logs/access.log  main;\n\n    sendfile        on;\n    #tcp_nopush     on;\n\n    #keepalive_timeout  0;\n    keepalive_timeout  65;\n\n    #gzip  on;\n    # 开启gzip\n    gzip  on;\n    # 启用gzip压缩的最小文件；小于设置值的文件将不会被压缩\n    gzip_min_length 1k;\n    # gzip 压缩级别 1-10\n    gzip_comp_level 2;\n    # 进行压缩的文件类型\n    gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png;\n    # 是否在http header中添加Vary: Accept-Encoding，建议开启\n    gzip_vary on;\n\n    # 注意要添加这一行\n    include ./conf.d/*.conf;\n\n    server {\n        listen       80;\n        server_name  localhost;\n\n        #charset koi8-r;\n\n        #access_log  logs/host.access.log  main;\n\n        location / {\n            root   html;\n            index  index.html index.htm;\n        }\n\n        #error_page  404              /404.html;\n\n        # redirect server error pages to the static page /50x.html\n        #\n        error_page   500 502 503 504  /50x.html;\n        location = /50x.html {\n            root   html;\n        }\n\n        # proxy the PHP scripts to Apache listening on 127.0.0.1:80\n        #\n        #location ~ \\.php$ {\n        #    proxy_pass   http://127.0.0.1;\n        #}\n\n        # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000\n        #\n        #location ~ \\.php$ {\n        #    root           html;\n        #    fastcgi_pass   127.0.0.1:9000;\n        #    fastcgi_index  index.php;\n        #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;\n        #    include        fastcgi_params;\n        #}\n\n        # deny access to .htaccess files, if Apache's document root\n        # concurs with nginx's one\n        #\n        #location ~ /\\.ht {\n        #    deny  all;\n        #}\n    }\n\n\n    # another virtual host using mix of IP-, name-, and port-based configuration\n    #\n    #server {\n    #    listen       8000;\n    #    listen       somename:8080;\n    #    server_name  somename  alias  another.alias;\n\n    #    location / {\n    #        root   html;\n    #        index  index.html index.htm;\n    #    }\n    #}\n\n\n    # HTTPS server\n    #\n    #server {\n    #    listen       443 ssl;\n    #    server_name  localhost;\n\n    #    ssl_certificate      cert.pem;\n    #    ssl_certificate_key  cert.key;\n\n    #    ssl_session_cache    shared:SSL:1m;\n    #    ssl_session_timeout  5m;\n\n    #    ssl_ciphers  HIGH:!aNULL:!MD5;\n    #    ssl_prefer_server_ciphers  on;\n\n    #    location / {\n    #        root   html;\n    #        index  index.html index.htm;\n    #    }\n    #}\n\n}\n"
  },
  {
    "path": "docker/project/backend/Dockerfile",
    "content": "# AdoptOpenJDK 停止发布 OpenJDK 二进制，而 Eclipse Temurin 是它的延伸，提供更好的稳定性\nFROM eclipse-temurin:8-jre\n\n# 创建一个目录用于存放应用\nRUN mkdir /app\n\n# 将构建的 JAR 文件复制到容器中\nCOPY geshanzsq-nav-admin-application.jar /app/app.jar\n\n# 指定工作目录\nWORKDIR /app\n\n# 设置默认环境变量（可以被docker-compose覆盖）\nENV SPRING_PROFILES_ACTIVE=prod\n\n# 暴露应用运行的端口（与你的application.properties中配置的端口一致）\nEXPOSE 8083\n\n# 启动应用\nENTRYPOINT [\"java\", \"-jar\", \"app.jar\"]"
  },
  {
    "path": "docker/project/backend/application-prod.yml",
    "content": "# 数据库配置\nspring:\n  datasource:\n    driver-class-name: com.mysql.cj.jdbc.Driver\n    url: jdbc:mysql://127.0.0.1:3306/geshanzsq_nav?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8\n    username: root\n    password: gesdh.cn@MySQL8\n    type: com.zaxxer.hikari.HikariDataSource\n    # 连接池配置\n    hikari:\n      # 最小连接池数量\n      minimum-idle: 5\n      # 最大连接池数量\n      maximum-pool-size: 10\n      # 配置获取连接等待超时的时间\n      connection-timeout: 60000\n      # 配置间隔多久才进行一次检测，检测需要关闭的空闲连接，单位是毫秒\n      validation-timeout: 60000\n      # 配置一个连接在池中最大生存的时间，单位是毫秒\n      max-lifetime: 900000\n      # 配置一个连接在池中最小生存的时间，单位是毫秒\n      idle-timeout: 300000\n  # redis 配置\n  redis:\n    # 地址\n    host: 127.0.0.1\n    # 端口，默认为6379\n    port: 6379\n    password: gesdh.cn@redis\n    # 连接超时时间\n    timeout: 10s\n    lettuce:\n      pool:\n        # 连接池中的最小空闲连接\n        min-idle: 0\n        # 连接池中的最大空闲连接\n        max-idle: 5\n        # 连接池的最大数据库连接数\n        max-active: 5\n        # #连接池最大阻塞等待时间（使用负值表示没有限制）\n        max-wait: -1ms\n    database: 6\n\n# 日志配置\nlogging:\n  level:\n    com.geshanzsq: info\n    org.springframework: info\n\n# 接口文档配置\nswagger:\n  # 是否启用，生产环境建议不启用，以免所有接口暴露\n  enable: false\n\n# 线程池配置\nthread:\n  # 核心线程池大小\n  core-pool-size: 50\n  # 最大可创建的线程数\n  max-pool-size: 200\n  # 队列最大长度\n  queueCapacity: 1000\n  # 线程池维护线程所允许的空闲时间\n  keepAliveSeconds: 300\n\n# 文件上传配置\nfile-upload:\n  # 上传基本路径\n  base-path: /data/file/geshanzsq-nav/profile\n  # 文件域名地址\n  domain:"
  },
  {
    "path": "docker/project/backend/docker-compose.yml",
    "content": "version: '3'\n\nservices:\n  gesdh-cn-backend:\n    container_name: gesdh-cn-backend\n    # 最终生成的镜像名称\n    image: gesdh-cn-backend:latest\n    build:\n      # 使用当前目录作为构建上下文\n      context: .\n      # 指定 Dockerfile 路径\n      dockerfile: Dockerfile\n    ports:\n      - \"8083:8083\"\n    restart: always\n    # 使用宿主机网络\n    network_mode: \"host\"\n    environment:\n      - TZ=Asia/Shanghai\n    volumes:\n      - ./application-prod.yml:/app/application-prod.yml\n      - ./logs:/app/logs\n      - ../file/profile:/data/file/geshanzsq-nav/profile"
  },
  {
    "path": "docker/project/backend/start.sh",
    "content": "#!/bin/bash\ndocker-compose up -d --build\ndocker logs -f gesdh-cn-backend\n"
  },
  {
    "path": "docker/project/frontend/Dockerfile",
    "content": "FROM nginx:alpine\nCOPY dist/ /usr/share/nginx/html/\nCOPY ./nginx/nginx.conf /etc/nginx/conf.d/default.conf\nEXPOSE 80"
  },
  {
    "path": "docker/project/frontend/docker-compose.yml",
    "content": "version: '3'\nservices:\n  gesdh-cn-frontend:\n    container_name: gesdh-cn-frontend\n    # 最终生成的镜像名称\n    image: gesdh-cn-frontend:latest\n    build:\n      # 使用当前目录作为构建上下文\n      context: .\n      # 指定 Dockerfile 路径\n      dockerfile: Dockerfile\n    volumes:\n      - ./dist:/usr/share/nginx/html\n      - ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf\n    ports:\n      - \"8023:80\"\n    restart: always"
  },
  {
    "path": "docker/project/frontend/nginx/nginx.conf",
    "content": "server {\n    listen       80;\n    server_name  localhost;\n\n    location / {\n        root   /usr/share/nginx/html;\n        index  index.html index.htm;\n        try_files $uri $uri/ /index.html;\n    }\n\n    error_page   500 502 503 504  /50x.html;\n    location = /50x.html {\n        root   /usr/share/nginx/html;\n    }\n}"
  },
  {
    "path": "docker/project/frontend/start.sh",
    "content": "#!/bin/bash\ndocker-compose up -d --build\ndocker logs -f gesdh-cn-frontend\n"
  },
  {
    "path": "docker/redis/docker-compose.yml",
    "content": "version: '3'\nservices:\n  redis:\n    image: redis:latest\n    container_name: redis\n    restart: always\n    ports:\n      - '6379:6379'\n    volumes:\n      - ./data:/data\n      - ./redis.conf:/usr/local/etc/redis/redis.conf\n      - ./logs:/logs\n    #配置文件启动\n    command: redis-server /usr/local/etc/redis/redis.conf\n"
  },
  {
    "path": "docker/redis/redis.conf",
    "content": "# Redis 服务器的端口号（默认：6379）\nport 6379\n\n# 绑定的 IP 地址，如果设置为 127.0.0.1，则只能本地访问；若设置为 0.0.0.0，则监听所有接口（默认：127.0.0.1）\nbind 0.0.0.0\n\n# 设置密码，客户端连接时需要提供密码才能进行操作，如果不设置密码，可以注释掉此行（默认：无）\n# requirepass foobared\nrequirepass gesdh.cn@redis\n\n# 设置在客户端闲置一段时间后关闭连接，单位为秒（默认：0，表示禁用）\n# timeout 0\n\n# 是否以守护进程（daemon）模式运行，默认为 \"no\"，设置为 \"yes\" 后 Redis 会在后台运行\ndaemonize no\n\n# 设置日志级别（默认：notice）。可以是 debug、verbose、notice、warning\nloglevel notice\n\n# 设置日志文件的路径（默认：空字符串），如果不设置，日志会输出到标准输出\nlogfile \"\"\n\n# 设置数据库数量（默认：16），Redis 使用数据库索引从 0 到 15\ndatabases 16\n\n# 是否启用 AOF 持久化，默认为 \"no\"。如果设置为 \"yes\"，将在每个写操作执行时将其追加到文件中\nappendonly no\n\n# 设置 AOF 持久化的文件路径（默认：appendonly.aof）\n# appendfilename \"appendonly.aof\"\n\n# AOF 持久化模式，默认为 \"always\"。可以是 always、everysec 或 no\n# always：每个写操作都立即同步到磁盘\n# everysec：每秒钟同步一次到磁盘\n# no：完全依赖操作系统的行为，可能会丢失数据，但性能最高\n# appendfsync always\n\n# 设置是否在后台进行 AOF 文件重写，默认为 \"no\"\n# auto-aof-rewrite-on-rewrite no\n\n# 设置 AOF 文件重写触发时，原 AOF 文件大小与新 AOF 文件大小之间的比率（默认：100）\n# auto-aof-rewrite-percentage 100\n\n# 设置是否开启 RDB 持久化，默认为 \"yes\"。如果设置为 \"no\"，禁用 RDB 持久化功能\nsave 900 1\nsave 300 10\nsave 60 10000\n\n# 设置 RDB 持久化文件的名称（默认：dump.rdb）\n# dbfilename dump.rdb\n\n# 设置 RDB 持久化文件的保存路径，默认保存在当前目录\n# dir ./\n\n# 设置是否开启对主从同步的支持，默认为 \"no\"\n# slaveof <masterip> <masterport>\n\n# 设置主从同步时是否进行数据完整性校验，默认为 \"yes\"\n# repl-diskless-sync no\n\n# 设置在复制时是否进行异步复制，默认为 \"yes\"，可以加快复制速度，但会增加数据丢失的风险\n# repl-backlog-size 1mb\n\n# 设置是否开启集群模式（cluster mode），默认为 \"no\"\n# cluster-enabled no\n\n# 设置集群中的节点超时时间（默认：15000毫秒）\n# cluster-node-timeout 15000\n\n# 设置集群中节点间通信使用的端口号（默认：0）\n# cluster-announce-port 0\n\n# 设置集群中节点间通信使用的 IP 地址\n# cluster-announce-ip 127.0.0.1\n\n# 设置是否开启慢查询日志，默认为 \"no\"\n# slowlog-log-slower-than 10000\n\n# 设置慢查询日志的最大长度，默认为 128\n# slowlog-max-len 128\n\n# 设置每秒最大处理的写入命令数量，用于保护 Redis 服务器不被超负荷写入（默认：0，表示不限制）\n# maxclients 10000\n\n# 设置最大连接客户端数量（默认：10000，0 表示不限制）\n# maxmemory <bytes>\n\n# 设置最大使用内存的策略（默认：noeviction）。可以是 volatile-lru、allkeys-lru、volatile-random、allkeys-random、volatile-ttl 或 noeviction\n# maxmemory-policy noeviction\n\n# 设置允许最大使用内存的比例（默认：0），设置为 0 表示禁用\n# maxmemory-samples 5\n"
  },
  {
    "path": "docker/start.sh",
    "content": "#!/bin/bash\n\n# 进入各目录并启动服务\ncd mysql && docker-compose up -d && cd ..\necho \"MySQL8 启动成功\"\ncd redis && docker-compose up -d && cd ..\necho \"Redis 启动成功\"\ncd nginx && docker-compose up -d && cd ..\necho \"Nginx 启动成功\"\ncd project/frontend && docker-compose up -d --build && cd ../../\necho \"格姗导航前端 启动成功\"\ncd project/backend && docker-compose up -d --build && cd ../../\necho \"格姗导航后端 启动成功\"\necho \"所有服务启动成功\"\n# 查看后端启动日志\necho \"格姗导航后端启动日志......\"\ndocker logs -f gesdh-cn-backend\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>geshanzsq-nav-admin</artifactId>\n        <groupId>com.geshanzsq</groupId>\n        <version>2.0.0</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>geshanzsq-nav-admin-application</artifactId>\n    <name>geshanzsq-nav-admin-application</name>\n    <description>后台启动模块</description>\n    <version>2.0.0</version>\n\n    <dependencies>\n        <!-- 系统模块 -->\n        <dependency>\n            <groupId>com.geshanzsq</groupId>\n            <artifactId>geshanzsq-nav-admin-system</artifactId>\n        </dependency>\n\n        <!-- 通用限流模块 -->\n        <dependency>\n            <groupId>com.geshanzsq</groupId>\n            <artifactId>geshanzsq-nav-common-rate-limiter</artifactId>\n        </dependency>\n\n        <!-- MySQL 数据库 -->\n        <dependency>\n            <groupId>mysql</groupId>\n            <artifactId>mysql-connector-java</artifactId>\n        </dependency>\n\n        <!-- easy-captcha 验证码 -->\n        <dependency>\n            <groupId>com.github.whvcse</groupId>\n            <artifactId>easy-captcha</artifactId>\n        </dependency>\n\n        <!-- 数据库密码加密 -->\n        <dependency>\n            <groupId>com.github.ulisesbocchio</groupId>\n            <artifactId>jasypt-spring-boot-starter</artifactId>\n        </dependency>\n\n        <!-- html 解析 -->\n        <dependency>\n            <groupId>org.jsoup</groupId>\n            <artifactId>jsoup</artifactId>\n        </dependency>\n\n        <!-- JustAuth 第三方登录模块-->\n        <dependency>\n            <groupId>me.zhyd.oauth</groupId>\n            <artifactId>JustAuth</artifactId>\n        </dependency>\n\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n                <version>${spring.boot.version}</version>\n                <executions>\n                    <execution>\n                        <goals>\n                            <goal>repackage</goal>\n                        </goals>\n                    </execution>\n                </executions>\n            </plugin>\n        </plugins>\n        <finalName>${project.artifactId}</finalName>\n    </build>\n\n\n</project>"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/GeshanzsqNavAdminApplication.java",
    "content": "package com.geshanzsq.admin;\n\nimport org.mybatis.spring.annotation.MapperScan;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n/**\n * 后台启动类\n *\n * @author geshanzsq\n * @date 2022/3/14\n */\n@SpringBootApplication(scanBasePackages = \"com.geshanzsq\")\n@MapperScan(\"com.geshanzsq.**.mapper\")\npublic class GeshanzsqNavAdminApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(GeshanzsqNavAdminApplication.class, args);\n        System.out.println(\"(♥◠‿◠)ﾉﾞ  格姗导航启动成功   ლ(´ڡ`ლ)ﾞ \");\n    }\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/client/nav/constant/NavClientConstant.java",
    "content": "package com.geshanzsq.admin.client.nav.constant;\n\n/**\n * 导航客户端常量\n *\n * @author geshanzsq\n * @date 2023/1/7\n */\npublic class NavClientConstant {\n\n    /**\n     * 分类前缀\n     */\n    public static final String CATEGORY_CACHE_PREFIX = \"nav:client:category\";\n\n    /**\n     * 网站前缀\n     */\n    public static final String SITE_CACHE_PREFIX = \"nav:client:site\";\n\n    /**\n     * 用户分类前缀\n     */\n    public static final String USER_CATEGORY_CACHE_PREFIX = \"nav:client:user:category:\";\n\n    /**\n     * 用户网站前缀\n     */\n    public static final String USER_SITE_CACHE_PREFIX = \"nav:client:user:site:\";\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/client/nav/controller/ClientNavCommentController.java",
    "content": "package com.geshanzsq.admin.client.nav.controller;\n\nimport com.geshanzsq.admin.client.nav.dto.ClientNavCommentAddDTO;\nimport com.geshanzsq.admin.client.nav.vo.ClientNavCommentTreeVO;\nimport com.geshanzsq.admin.nav.comment.service.NavCommentService;\nimport com.geshanzsq.common.core.web.response.ResponseResult;\nimport com.geshanzsq.common.rate.limiter.annotation.RateLimiter;\nimport com.geshanzsq.common.rate.limiter.enums.RateLimiterType;\nimport io.swagger.annotations.Api;\nimport io.swagger.annotations.ApiOperation;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.validation.Valid;\nimport java.util.List;\n\n/**\n * 客户端评论\n *\n * @author geshanzsq\n * @date 2023/4/15\n */\n@Api(tags = \"客户端评论\")\n@RestController\n@RequestMapping(\"/client/nav/comment\")\npublic class ClientNavCommentController {\n\n    @Autowired\n    private NavCommentService navCommentService;\n\n    @ApiOperation(\"开启状态\")\n    @GetMapping(\"/getOpenStatus\")\n    public ResponseResult<Boolean> getOpenStatus() {\n        return ResponseResult.success(navCommentService.getOpenStatus());\n    }\n\n    @ApiOperation(\"树形列表\")\n    @GetMapping(\"/tree\")\n    public ResponseResult<List<ClientNavCommentTreeVO>> tree() {\n        List<ClientNavCommentTreeVO> list = navCommentService.treeClient();\n        return ResponseResult.success(list);\n    }\n\n    @ApiOperation(\"提交评论\")\n    @PostMapping\n    @RateLimiter(type = RateLimiterType.IP, count = 1)\n    public ResponseResult add(@Valid @RequestBody ClientNavCommentAddDTO addDTO) {\n        navCommentService.add(addDTO);\n        return ResponseResult.success();\n    }\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/client/nav/controller/ClientNavController.java",
    "content": "package com.geshanzsq.admin.client.nav.controller;\n\nimport com.geshanzsq.admin.client.nav.service.ClientNavService;\nimport com.geshanzsq.admin.client.nav.vo.NavCategoryClientVO;\nimport com.geshanzsq.admin.client.nav.vo.NavClientListVO;\nimport com.geshanzsq.admin.nav.site.service.NavSiteService;\nimport com.geshanzsq.admin.nav.site.vo.NavSiteLatestCollectVO;\nimport com.geshanzsq.common.core.web.response.ResponseResult;\nimport com.geshanzsq.framework.security.util.SecurityUtils;\nimport io.swagger.annotations.Api;\nimport io.swagger.annotations.ApiOperation;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.util.List;\n\n/**\n * 客户端导航\n *\n * @author geshanzsq\n * @date 2023/1/8\n */\n@Api(tags = \"客户端导航\")\n@RestController\n@RequestMapping(\"/client/nav\")\npublic class ClientNavController {\n\n    @Autowired\n    private ClientNavService clientNavService;\n\n    @ApiOperation(\"分类网站列表\")\n    @GetMapping(\"/category/site/list\")\n    public ResponseResult<NavClientListVO> categorySiteList() {\n        NavClientListVO listVO = clientNavService.categorySiteList();\n        return ResponseResult.success(listVO);\n    }\n\n    @ApiOperation(\"分类列表\")\n    @GetMapping(\"/category/list\")\n    public ResponseResult<NavClientListVO> categoryList() {\n        List<NavCategoryClientVO> list = clientNavService.categoryList();\n        return ResponseResult.success(list);\n    }\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/client/nav/dto/ClientNavCommentAddDTO.java",
    "content": "package com.geshanzsq.admin.client.nav.dto;\n\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\nimport javax.validation.constraints.Email;\nimport javax.validation.constraints.NotBlank;\nimport java.io.Serializable;\n\n/**\n * 提交评论\n *\n * @author geshanzsq\n * @date 2023/4/15\n */\n@Data\n@ApiModel(\"评论\")\npublic class ClientNavCommentAddDTO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(\"上级 id\")\n    private Long parentId;\n\n    @ApiModelProperty(value = \"评论内容\", required = true)\n    @NotBlank(message = \"评论内容不能为空\")\n    private String commentContent;\n\n    @ApiModelProperty(\"昵称\")\n    private String nickName;\n\n    @ApiModelProperty(\"邮箱\")\n    @Email(message = \"邮箱不正确\")\n    private String email;\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/client/nav/mapstruct/ClientNavConverter.java",
    "content": "package com.geshanzsq.admin.client.nav.mapstruct;\n\nimport com.geshanzsq.admin.client.nav.vo.NavCategoryClientVO;\nimport com.geshanzsq.admin.nav.category.po.NavCategory;\nimport org.mapstruct.Mapper;\nimport org.mapstruct.factory.Mappers;\n\nimport java.util.List;\n\n/**\n * 导航客户端对象转换\n *\n * @author geshanzsq\n * @date 2023/1/8\n */\n@Mapper\npublic interface ClientNavConverter {\n\n    ClientNavConverter INSTANCE = Mappers.getMapper(ClientNavConverter.class);\n\n    List<NavCategoryClientVO> convertCategory(List<NavCategory> list);\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/client/nav/service/ClientNavService.java",
    "content": "package com.geshanzsq.admin.client.nav.service;\n\nimport com.geshanzsq.admin.client.nav.vo.NavCategoryClientVO;\nimport com.geshanzsq.admin.client.nav.vo.NavClientListVO;\n\nimport java.util.List;\n\n/**\n * 导航客户端\n *\n * @author geshanzsq\n * @date 2023/1/8\n */\npublic interface ClientNavService {\n\n    /**\n     * 分类网站列表\n     */\n    NavClientListVO categorySiteList();\n\n    /**\n     * 分类列表\n     */\n    List<NavCategoryClientVO> categoryList();\n\n    /**\n     * 清除缓存\n     */\n    void removeCache();\n\n    /**\n     * 站内分类网站搜索列表\n     */\n    NavClientListVO categorySiteSearchList(String searchContent);\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/client/nav/service/impl/ClientNavServiceImpl.java",
    "content": "package com.geshanzsq.admin.client.nav.service.impl;\n\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.geshanzsq.admin.client.nav.constant.NavClientConstant;\nimport com.geshanzsq.admin.client.nav.mapstruct.ClientNavConverter;\nimport com.geshanzsq.admin.client.nav.service.ClientNavService;\nimport com.geshanzsq.admin.client.nav.util.NavClientCategoryUtils;\nimport com.geshanzsq.admin.client.nav.util.NavClientUtils;\nimport com.geshanzsq.admin.client.nav.vo.NavCategoryClientVO;\nimport com.geshanzsq.admin.client.nav.vo.NavCategorySiteClientVO;\nimport com.geshanzsq.admin.client.nav.vo.NavClientListVO;\nimport com.geshanzsq.admin.nav.category.mapper.NavCategoryMapper;\nimport com.geshanzsq.admin.nav.category.mapstruct.NavCategoryConverter;\nimport com.geshanzsq.admin.nav.category.po.NavCategory;\nimport com.geshanzsq.admin.nav.site.mapper.NavSiteMapper;\nimport com.geshanzsq.admin.nav.site.mapstruct.NavSiteConverter;\nimport com.geshanzsq.admin.nav.site.po.NavSite;\nimport com.geshanzsq.admin.nav.site.vo.NavSiteClientVO;\nimport com.geshanzsq.common.core.enums.CommonStatus;\nimport com.geshanzsq.common.redis.service.RedisService;\nimport org.apache.commons.lang3.StringUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\nimport org.springframework.util.CollectionUtils;\n\nimport java.util.*;\nimport java.util.concurrent.TimeUnit;\nimport java.util.stream.Collectors;\n\n/**\n * 导航工具类\n *\n * @author geshanzsq\n * @date 2023/1/8\n */\n@Service\npublic class ClientNavServiceImpl implements ClientNavService {\n\n    @Autowired\n    private NavCategoryMapper navCategoryMapper;\n    @Autowired\n    private NavSiteMapper navSiteMapper;\n\n    @Autowired\n    private RedisService redisService;\n\n    /**\n     * 分类网站列表\n     */\n    @Override\n    public NavClientListVO categorySiteList() {\n        // 从缓存获取分类数据\n        List<NavCategoryClientVO> categories = redisService.get(NavClientConstant.CATEGORY_CACHE_PREFIX);\n        if (categories == null) {\n            // 从数据库获取\n            return buildCategoryAndSiteList();\n        }\n\n        // 从缓存获取分类网站数据\n        List<NavCategorySiteClientVO> siteList = redisService.get(NavClientConstant.SITE_CACHE_PREFIX);\n        if (siteList == null) {\n            return buildCategoryAndSiteList();\n        }\n\n        // 分类数据和分类网站数据都是从缓存获取\n        return NavClientListVO.builder().categories(categories).sites(siteList).build();\n    }\n\n\n    /**\n     * 分类列表\n     */\n    @Override\n    public List<NavCategoryClientVO> categoryList() {\n        // 从缓存获取分类数据\n        List<NavCategoryClientVO> categories = redisService.get(NavClientConstant.CATEGORY_CACHE_PREFIX);\n        if (CollectionUtils.isEmpty(categories)) {\n            // 从数据库获取\n            categories = new ArrayList<>();\n            List<NavCategorySiteClientVO> siteList = new ArrayList<>();\n            buildCategoryAndSiteList();\n        }\n        return categories;\n    }\n\n    /**\n     * 清除缓存\n     */\n    @Override\n    public void removeCache() {\n        // 清除缓存，等后续访问时会自动从数据库获取\n        redisService.delete(NavClientConstant.CATEGORY_CACHE_PREFIX);\n        redisService.delete(NavClientConstant.SITE_CACHE_PREFIX);\n    }\n\n    /**\n     * 站内分类网站搜索列表\n     */\n    @Override\n    public NavClientListVO categorySiteSearchList(String searchContent) {\n        NavClientListVO clientListVO = new NavClientListVO();\n        if (StringUtils.isBlank(searchContent)) {\n            return clientListVO;\n        }\n\n        // 查询网站数据\n        LambdaQueryWrapper<NavSite> siteWrapper = new LambdaQueryWrapper<>();\n        siteWrapper.eq(NavSite::getStatus, CommonStatus.NORMAL.code);\n        siteWrapper.and(wrapper -> {\n            wrapper.or().like(NavSite::getSiteName, searchContent);\n            wrapper.or().like(NavSite::getSiteDescription, searchContent);\n        });\n        siteWrapper.orderByAsc(NavSite::getCategoryId, NavSite::getSort);\n        siteWrapper.select(NavSite::getId, NavSite::getCategoryId, NavSite::getSiteName, NavSite::getSitePath,\n                NavSite::getSiteDescription, NavSite::getSiteUrl);\n        List<NavSite> siteSearchList = navSiteMapper.selectList(siteWrapper);\n        if (CollectionUtils.isEmpty(siteSearchList)) {\n            return clientListVO;\n        }\n\n        // 通过分类 ids 获取所有上级分类\n        Set<Long> categoryIds = siteSearchList.stream().map(NavSite::getCategoryId).collect(Collectors.toSet());\n        List<NavCategory> navCategoryList = new ArrayList<>();\n        getCategoryParent(categoryIds, navCategoryList);\n\n        // 构建树形结构\n        List<NavCategoryClientVO> categories = NavClientCategoryUtils.buildTree(\n                ClientNavConverter.INSTANCE.convertCategory(navCategoryList), 0L);\n\n        // 获取最后一级分类\n        List<NavCategoryClientVO> lastLevelCategoryList = new ArrayList<>();\n        NavClientCategoryUtils.buildLastLevelCategoryList(categories, lastLevelCategoryList);\n        // 最后一级为空，直接返回\n        if (CollectionUtils.isEmpty(lastLevelCategoryList)) {\n            return clientListVO;\n        }\n\n        List<NavSiteClientVO> siteList = NavSiteConverter.INSTANCE.convertCilent(siteSearchList);\n\n        // 构建分类下的网站\n        List<NavCategorySiteClientVO> categorySiteList = NavCategoryConverter.INSTANCE.convertSiteClient(lastLevelCategoryList);\n        categorySiteList.parallelStream().forEach(category -> {\n            List<NavSiteClientVO> siteClientList = siteList.stream().filter(\n                    site -> category.getId().equals(site.getCategoryId())).collect(Collectors.toList());\n            category.setSites(siteClientList);\n        });\n\n        // 过滤没有网站的分类网站\n        categorySiteList = categorySiteList.stream().filter(item -> !CollectionUtils.isEmpty(item.getSites())).collect(Collectors.toList());\n\n        // 过滤没有网站的分类\n        NavClientUtils.filterCategoryNotSite(categories, categorySiteList);\n\n        clientListVO.setCategories(categories);\n        clientListVO.setSites(categorySiteList);\n        return clientListVO;\n    }\n\n\n    /**\n     * 通过分类 ids 获取所有上级分类\n     *\n     * @param categoryIds 分类 ids\n     * @param categoryList 保存的分类\n     */\n    private void getCategoryParent(Set<Long> categoryIds, List<NavCategory> categoryList) {\n        if (CollectionUtils.isEmpty(categoryIds)) {\n            return;\n        }\n        // 获取分类\n        LambdaQueryWrapper<NavCategory> categoryWrapper = new LambdaQueryWrapper<>();\n        categoryWrapper.in(NavCategory::getId, categoryIds);\n        categoryWrapper.eq(NavCategory::getStatus, CommonStatus.NORMAL.code);\n        categoryWrapper.orderByAsc(NavCategory::getSort);\n        List<NavCategory> childrenList = navCategoryMapper.selectList(categoryWrapper);\n        if (CollectionUtils.isEmpty(childrenList)) {\n            return;\n        }\n        categoryList.addAll(childrenList);\n        categoryIds = childrenList.stream().filter(category -> category.getParentId() != null && category.getParentId() > 0)\n                .map(NavCategory::getParentId).collect(Collectors.toSet());\n        // 递归获取父分类\n        getCategoryParent(categoryIds, categoryList);\n    }\n\n    /**\n     * 构建分类和网站数据\n     */\n    private NavClientListVO buildCategoryAndSiteList() {\n        // 获取分类列表并构建树形结构\n        LambdaQueryWrapper<NavCategory> categoryWrapper = new LambdaQueryWrapper<>();\n        categoryWrapper.eq(NavCategory::getStatus, CommonStatus.NORMAL.code);\n        categoryWrapper.orderByAsc(NavCategory::getSort);\n        // 构建树形结构\n        List<NavCategoryClientVO> categories = NavClientCategoryUtils.buildTree(\n                ClientNavConverter.INSTANCE.convertCategory(navCategoryMapper.selectList(categoryWrapper)), 0L);\n\n        // 获取最后一级分类\n        List<NavCategoryClientVO> lastLevelCategoryList = new ArrayList<>();\n        NavClientCategoryUtils.buildLastLevelCategoryList(categories, lastLevelCategoryList);\n        // 最后一级为空，更新缓存为空\n        if (CollectionUtils.isEmpty(lastLevelCategoryList)) {\n            setCategoryCache(new ArrayList<>());\n            setSiteCache(new ArrayList<>());\n            return NavClientListVO.builder().categories(new ArrayList<>()).sites(new ArrayList<>()).build();\n        }\n        // 获取最后一级分类所有 id\n        Set<Long> categoryIds = lastLevelCategoryList.parallelStream().map(c -> c.getId()).collect(Collectors.toSet());\n\n        // 获取网站数据\n        LambdaQueryWrapper<NavSite> siteWrapper = new LambdaQueryWrapper<>();\n        siteWrapper.in(NavSite::getCategoryId, categoryIds);\n        siteWrapper.eq(NavSite::getStatus, CommonStatus.NORMAL.code);\n        siteWrapper.orderByAsc(NavSite::getCategoryId, NavSite::getSort);\n        siteWrapper.select(NavSite::getId, NavSite::getCategoryId, NavSite::getSiteName, NavSite::getSitePath,\n                NavSite::getSiteDescription, NavSite::getSiteUrl);\n        List<NavSiteClientVO> siteList = NavSiteConverter.INSTANCE.convertCilent(navSiteMapper.selectList(siteWrapper));\n\n        // 构建分类下的网站\n        List<NavCategorySiteClientVO> categorySiteList = NavCategoryConverter.INSTANCE.convertSiteClient(lastLevelCategoryList);\n        categorySiteList.parallelStream().forEach(category -> {\n            List<NavSiteClientVO> siteClientList = siteList.stream().filter(\n                    site -> category.getId().equals(site.getCategoryId())).collect(Collectors.toList());\n            category.setSites(siteClientList);\n        });\n\n        // 过滤没有网站的分类网站\n        categorySiteList = categorySiteList.stream().filter(item -> !CollectionUtils.isEmpty(item.getSites())).collect(Collectors.toList());\n\n        // 过滤没有网站的分类\n        NavClientUtils.filterCategoryNotSite(categories, categorySiteList);\n\n        // 存入缓存\n        setCategoryCache(categories);\n        setSiteCache(categorySiteList);\n\n        NavClientListVO clientListVO = NavClientListVO.builder().categories(categories).sites(categorySiteList).build();\n        return clientListVO;\n    }\n\n\n    /**\n     * 设置分类缓存\n     */\n    private void setCategoryCache(List<NavCategoryClientVO> list) {\n        // 存入缓存，默认为 7 天\n        redisService.set(NavClientConstant.CATEGORY_CACHE_PREFIX, list, 7, TimeUnit.DAYS);\n    }\n\n    /**\n     * 设置网站缓存\n     */\n    private void setSiteCache(List<NavCategorySiteClientVO> list) {\n        // 存入缓存，默认为 7 天\n        redisService.set(NavClientConstant.SITE_CACHE_PREFIX, list, 7, TimeUnit.DAYS);\n    }\n\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/client/nav/util/NavClientCategoryUtils.java",
    "content": "package com.geshanzsq.admin.client.nav.util;\n\nimport com.geshanzsq.admin.client.nav.vo.NavCategoryClientVO;\nimport org.springframework.util.CollectionUtils;\n\nimport java.util.ArrayList;\nimport java.util.Iterator;\nimport java.util.List;\n\n/**\n * 客户端分类工具类\n *\n * @author geshanzsq\n * @date 2023/1/8\n */\npublic class NavClientCategoryUtils {\n\n    /**\n     * 构建最后一级分类\n     * @param categoryList 分类数据\n     * @param lastLevelCategoryList 最后一级数据\n     */\n    public static void buildLastLevelCategoryList(List<NavCategoryClientVO> categoryList, List<NavCategoryClientVO> lastLevelCategoryList) {\n        categoryList.stream().forEach(category -> {\n            if (CollectionUtils.isEmpty(category.getChildren())) {\n                // 最后一级\n                lastLevelCategoryList.add(category);\n            } else {\n                // 不是最后一级，递归\n                buildLastLevelCategoryList(category.getChildren(), lastLevelCategoryList);\n            }\n        });\n    }\n\n    /**\n     * 根据父节点的 id 获取所有子节点\n     *\n     * @param allList 所有数据\n     * @param parentId 传入的父节点 id\n     */\n    public static List<NavCategoryClientVO> buildTree(List<NavCategoryClientVO> allList, Long parentId) {\n        List<NavCategoryClientVO> returnList = new ArrayList<>();\n        for (NavCategoryClientVO category : allList) {\n            // 根据传入的某个父节点 id，遍历该父节点的所有子节点\n            if (parentId != null && parentId.equals(category.getParentId())) {\n                // 递归\n                recursive(allList, category);\n                returnList.add(category);\n            }\n        }\n        return returnList;\n    }\n\n    /**\n     * 递归列表\n     *\n     * @param allList\n     * @param category 当前分类\n     */\n    private static void recursive(List<NavCategoryClientVO> allList, NavCategoryClientVO category) {\n        // 得到子节点列表\n        List<NavCategoryClientVO> childList = getChildList(allList, category);\n        category.setChildren(childList);\n        for (NavCategoryClientVO tChild : childList) {\n            if (hasChild(allList, tChild)) {\n                recursive(allList, tChild);\n            }\n        }\n    }\n\n    /**\n     * 判断是否有子节点\n     */\n    private static boolean hasChild(List<NavCategoryClientVO> allList, NavCategoryClientVO category) {\n        return getChildList(allList, category).size() > 0;\n    }\n\n    /**\n     * 得到子节点列表\n     */\n    private static List<NavCategoryClientVO> getChildList(List<NavCategoryClientVO> allList, NavCategoryClientVO category) {\n        List<NavCategoryClientVO> childList = new ArrayList<>();\n        Iterator<NavCategoryClientVO> it = allList.iterator();\n        while (it.hasNext()) {\n            NavCategoryClientVO child = it.next();\n            if (category.getId().equals(child.getParentId())) {\n                childList.add(child);\n            }\n        }\n        return childList;\n    }\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/client/nav/util/NavClientUtils.java",
    "content": "package com.geshanzsq.admin.client.nav.util;\n\nimport com.geshanzsq.admin.client.nav.vo.NavCategoryClientVO;\nimport com.geshanzsq.admin.client.nav.vo.NavCategorySiteClientVO;\nimport org.springframework.util.CollectionUtils;\nimport java.util.List;\n\n/**\n * 导航客户端工具类\n *\n * @author geshanzsq\n * @date 2023/1/8\n */\npublic class NavClientUtils {\n\n    /**\n     * 过滤没有网站的分类\n     */\n    public static void filterCategoryNotSite(List<NavCategoryClientVO> categoryList, List<NavCategorySiteClientVO> siteList) {\n        if (CollectionUtils.isEmpty(categoryList) || CollectionUtils.isEmpty(siteList)) {\n            return;\n        }\n\n        // 获取深度\n        int deep = 0;\n        for (NavCategoryClientVO category: categoryList) {\n            int childrenDeep = getCategoryDeep(category, 0);\n            deep = childrenDeep > deep ? childrenDeep : deep;\n        }\n\n        // 依次剔除最后一级没有网站的菜单\n        for (int i = 1; i <= deep; i++) {\n            filterNoSite(categoryList,siteList);\n        }\n    }\n\n    /**\n     * 获取分类深度\n     */\n    private static int getCategoryDeep(NavCategoryClientVO category, int deep) {\n        // 没有子分类，深度加 1\n        if (CollectionUtils.isEmpty(category.getChildren())) {\n            return deep + 1;\n        }\n\n        int deepChildren = deep;\n        for (NavCategoryClientVO children: category.getChildren()) {\n            int child = getCategoryDeep(children, deep + 1);\n            deepChildren = child > deepChildren ? child : deepChildren;\n        }\n        return deepChildren;\n\n    }\n\n    /**\n     * 依次剔除最后一级没有网站的菜单\n     */\n    private static void filterNoSite(List<NavCategoryClientVO> categoryList,List<NavCategorySiteClientVO> siteList) {\n        for (int t = 0 ; t < categoryList.size(); t++) {\n            NavCategoryClientVO category = categoryList.get(t);\n            if (CollectionUtils.isEmpty(category.getChildren())) {\n                NavCategorySiteClientVO site = siteList.parallelStream()\n                        .filter(s -> category.getId().equals(s.getId())).findFirst().orElse(null);\n                // 不存在，移除\n                if (site == null) {\n                    categoryList.remove(t--);\n                }\n            } else {\n                filterNoSite(category.getChildren(),siteList);\n            }\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/client/nav/vo/ClientNavCommentTreeVO.java",
    "content": "package com.geshanzsq.admin.client.nav.vo;\n\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\nimport java.io.Serializable;\nimport java.util.Date;\nimport java.util.List;\n\n/**\n * 评论\n *\n * @author geshanzsq\n * @date 2023/4/15\n */\n@Data\n@ApiModel(\"评论\")\npublic class ClientNavCommentTreeVO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(\"评论 id\")\n    private Long id;\n\n    @ApiModelProperty(\"上级 id\")\n    private Long parentId;\n\n    @ApiModelProperty(\"是否置顶（1 是，2 否）\")\n    private Integer hasSticky;\n\n    @ApiModelProperty(\"评论内容\")\n    private String commentContent;\n\n    @ApiModelProperty(\"昵称\")\n    private String nickName;\n\n    @ApiModelProperty(\"创建人用户 id\")\n    private Long createUserId;\n\n    @ApiModelProperty(\"创建时间\")\n    private Date gmtCreate;\n\n    @ApiModelProperty(\"是否为站长\")\n    private Boolean webMaster;\n\n    @ApiModelProperty(\"子评论\")\n    private List<ClientNavCommentTreeVO> children;\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/client/nav/vo/NavCategoryClientVO.java",
    "content": "package com.geshanzsq.admin.client.nav.vo;\n\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\nimport java.io.Serializable;\nimport java.util.List;\n\n/**\n * 导航分类客户端列表\n *\n * @author geshanzsq\n * @date 2023/1/7\n */\n@Data\n@ApiModel(\"导航分类客户端列表\")\npublic class NavCategoryClientVO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(\"分类 id\")\n    private Long id;\n\n    @ApiModelProperty(\"上级 id\")\n    private Long parentId;\n\n    @ApiModelProperty(\"分类名称\")\n    private String categoryName;\n\n    @ApiModelProperty(\"分类图标\")\n    private String categoryIcon;\n\n    @ApiModelProperty(\"子分类\")\n    private List<NavCategoryClientVO> children;\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/client/nav/vo/NavCategorySiteClientVO.java",
    "content": "package com.geshanzsq.admin.client.nav.vo;\n\nimport com.geshanzsq.admin.nav.site.vo.NavSiteClientVO;\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.io.Serializable;\nimport java.util.List;\n\n/**\n * 导航分类网站客户端\n *\n * @author geshanzsq\n * @date 2023/1/8\n */\n@Data\n@Builder\n@AllArgsConstructor\n@NoArgsConstructor\n@ApiModel(\"导航分类网站客户端\")\npublic class NavCategorySiteClientVO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(\"分类 id\")\n    private Long id;\n\n    @ApiModelProperty(\"分类名称\")\n    private String categoryName;\n\n    @ApiModelProperty(\"网站\")\n    private List<NavSiteClientVO> sites;\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/client/nav/vo/NavClientListVO.java",
    "content": "package com.geshanzsq.admin.client.nav.vo;\n\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.io.Serializable;\nimport java.util.List;\n\n/**\n * 导航客户端\n *\n * @author geshanzsq\n * @date 2023/1/7\n */\n@Data\n@Builder\n@ApiModel(\"导航客户端\")\n@AllArgsConstructor\n@NoArgsConstructor\npublic class NavClientListVO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(\"分类\")\n    private List<NavCategoryClientVO> categories;\n\n    @ApiModelProperty(\"网站\")\n    private List<NavCategorySiteClientVO> sites;\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/client/search/controller/ClientSearchController.java",
    "content": "package com.geshanzsq.admin.client.search.controller;\n\nimport com.geshanzsq.admin.client.nav.service.ClientNavService;\nimport com.geshanzsq.admin.client.nav.vo.NavClientListVO;\nimport com.geshanzsq.admin.client.search.vo.NavClientSiteSearchVO;\nimport com.geshanzsq.admin.nav.site.service.NavSiteService;\nimport com.geshanzsq.common.core.web.response.ResponseResult;\nimport io.swagger.annotations.Api;\nimport io.swagger.annotations.ApiOperation;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.util.List;\n\n/**\n * 客户端搜索\n *\n * @author geshanzsq\n * @date 2023/4/7\n */\n@Api(tags = \"客户端搜索\")\n@RestController\n@RequestMapping(\"/client/search\")\npublic class ClientSearchController {\n\n    @Autowired\n    private NavSiteService navSiteService;\n    @Autowired\n    private ClientNavService clientNavService;\n\n    @ApiOperation(\"站内网站搜索\")\n    @GetMapping(\"/site/list\")\n    public ResponseResult<List<NavClientSiteSearchVO>> siteSearch(String searchContent) {\n        List<NavClientSiteSearchVO> list = navSiteService.siteSearch(searchContent);\n        return ResponseResult.success(list);\n    }\n\n    @ApiOperation(\"站内分类网站搜索列表\")\n    @GetMapping(\"/category/site/list\")\n    public ResponseResult<NavClientListVO> categorySiteSearchList(String searchContent) {\n        NavClientListVO listVO = clientNavService.categorySiteSearchList(searchContent);\n        return ResponseResult.success(listVO);\n    }\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/client/search/vo/NavClientSiteSearchVO.java",
    "content": "package com.geshanzsq.admin.client.search.vo;\n\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\nimport java.io.Serializable;\n\n/**\n * 客户端网站\n *\n * @author geshanzsq\n * @date 2023/4/7\n */\n@Data\n@ApiModel(\"客户端网站\")\npublic class NavClientSiteSearchVO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(\"网站 id\")\n    private Long id;\n\n    @ApiModelProperty(\"分类名称\")\n    private String categoryName;\n\n    @ApiModelProperty(\"网站名称\")\n    private String siteName;\n\n    @ApiModelProperty(\"网站地址\")\n    private String siteUrl;\n\n    @ApiModelProperty(\"网站描述\")\n    private String siteDescription;\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/core/bing/controller/BingController.java",
    "content": "package com.geshanzsq.admin.core.bing.controller;\n\nimport com.geshanzsq.admin.core.bing.util.BingUtils;\nimport com.geshanzsq.admin.core.bing.vo.BingImageVO;\nimport com.geshanzsq.common.core.web.response.ResponseResult;\nimport com.geshanzsq.common.framework.web.controller.BaseController;\nimport io.swagger.annotations.Api;\nimport io.swagger.annotations.ApiOperation;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * 微软 Bing 图片下载\n *\n * @author geshanzsq\n * @date 2022/5/14\n */\n@RestController\n@RequestMapping(\"/bing\")\n@Api(tags = \"微软 Bing\")\npublic class BingController extends BaseController {\n\n\n    @ApiOperation(\"获取 Bing 壁纸\")\n    @GetMapping(\"/getBingImage\")\n    public ResponseResult<BingImageVO> getBingImage() {\n        String bingOneImage = BingUtils.getBingOneImage();\n        BingImageVO bingImageVO = new BingImageVO(bingOneImage);\n        return ResponseResult.success(bingImageVO);\n    }\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/core/bing/util/BingUtils.java",
    "content": "package com.geshanzsq.admin.core.bing.util;\n\nimport com.alibaba.fastjson.JSON;\nimport com.alibaba.fastjson.JSONObject;\nimport com.geshanzsq.common.core.util.http.HttpUtils;\n\n/**\n * 微软 Bing 工具类\n *\n * @author geshanzsq\n * @date 2022/5/14\n */\npublic class BingUtils {\n\n    private static final String BING_URL = \"https://cn.bing.com/HPImageArchive.aspx?format=js&idx=0&n=1&mkt=zh-CN\";\n\n    /**\n     * 获取Bing图片\n     * @return\n     */\n    public static String getBingOneImage() {\n        String resultStr = HttpUtils.sendGet(BING_URL,null);\n        JSONObject jsonObject = JSON.parseObject(resultStr);\n        JSONObject imageObject = jsonObject.getJSONArray(\"images\").getJSONObject(0);\n        return \"https://cn.bing.com\" + imageObject.getString(\"url\");\n    }\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/core/bing/vo/BingImageVO.java",
    "content": "package com.geshanzsq.admin.core.bing.vo;\n\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\nimport java.io.Serializable;\n\n/**\n * 微软 Bing 图片\n *\n * @author geshanzsq\n * @date 2022/5/14\n */\n@Data\n@ApiModel(\"微软 Bing 图片\")\npublic class BingImageVO implements Serializable {\n\n    private static final Long serialVersionUID = 1L;\n\n    @ApiModelProperty(\"图片链接\")\n    private String imageUrl;\n\n    public BingImageVO(String imageUrl) {\n        this.imageUrl = imageUrl;\n    }\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/core/captcha/constant/CaptchaConstant.java",
    "content": "package com.geshanzsq.admin.core.captcha.constant;\n\n/**\n * 验证码常量\n *\n * @author geshanzsq\n * @date 2022/6/12\n */\npublic class CaptchaConstant {\n\n    /**\n     * 验证码 redis key\n     */\n    public static final String CAPTCHA_CODE_KEY = \"captcha:codes:\";\n\n    /**\n     * 验证码有效期（分钟）\n     */\n    public static final Integer CAPTCHA_EXPIRATION = 2;\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/core/captcha/controller/CaptchaController.java",
    "content": "package com.geshanzsq.admin.core.captcha.controller;\n\nimport com.geshanzsq.admin.core.captcha.constant.CaptchaConstant;\nimport com.geshanzsq.admin.core.captcha.vo.CaptchaImageVO;\nimport com.geshanzsq.common.core.util.id.IdUtils;\nimport com.geshanzsq.common.core.web.response.ResponseResult;\nimport com.geshanzsq.common.framework.web.controller.BaseController;\nimport com.geshanzsq.common.rate.limiter.annotation.RateLimiter;\nimport com.geshanzsq.common.rate.limiter.enums.RateLimiterType;\nimport com.geshanzsq.common.redis.service.RedisService;\nimport com.wf.captcha.SpecCaptcha;\nimport io.swagger.annotations.Api;\nimport io.swagger.annotations.ApiOperation;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.util.concurrent.TimeUnit;\n\n/**\n * 验证码\n * @author geshanzsq\n * @date 2022/5/2\n */\n@RestController\n@Api(tags = \"验证码\")\npublic class CaptchaController extends BaseController {\n\n    @Autowired\n    private RedisService redisService;\n\n    @ApiOperation(\"获取验证码\")\n    @GetMapping(\"/getCaptchaImage\")\n    @RateLimiter(type = RateLimiterType.IP, hintMessage = \"验证码获取频繁，请稍后再试\")\n    public ResponseResult<CaptchaImageVO> getCode() {\n        String uuid = IdUtils.simpleUUID();\n        String verifyKey = CaptchaConstant.CAPTCHA_CODE_KEY + uuid;\n        SpecCaptcha specCaptcha = new SpecCaptcha(105, 38, 4);\n        String code = specCaptcha.text();\n        // 保存验证码信息\n        redisService.set(verifyKey, code, CaptchaConstant.CAPTCHA_EXPIRATION, TimeUnit.MINUTES);\n        CaptchaImageVO captchaImageVO = new CaptchaImageVO();\n        captchaImageVO.setUuid(uuid);\n        captchaImageVO.setImage(specCaptcha.toBase64());\n        return ResponseResult.success(captchaImageVO);\n    }\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/core/captcha/vo/CaptchaImageVO.java",
    "content": "package com.geshanzsq.admin.core.captcha.vo;\n\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\nimport java.io.Serializable;\n\n/**\n * 验证码\n *\n * @author geshanzsq\n * @date 2022/5/2\n */\n@Data\n@ApiModel(\"验证码\")\npublic class CaptchaImageVO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(\"验证码唯一标识\")\n    private String uuid;\n\n    @ApiModelProperty(\"验证码图片\")\n    private String image;\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/core/config/InsertUpdateMyBatisInterceptorConfig.java",
    "content": "package com.geshanzsq.admin.core.config;\n\nimport com.geshanzsq.common.framework.mybatis.plugin.interceptor.InsertUpdateMyBatisInterceptor;\nimport com.geshanzsq.framework.security.util.SecurityUtils;\nimport org.apache.ibatis.executor.Executor;\nimport org.apache.ibatis.mapping.MappedStatement;\nimport org.apache.ibatis.plugin.Interceptor;\nimport org.apache.ibatis.plugin.Intercepts;\nimport org.apache.ibatis.plugin.Invocation;\nimport org.apache.ibatis.plugin.Signature;\nimport org.springframework.stereotype.Component;\n\n/**\n * MyBatis 插入更新拦截器配置\n *\n * @author geshanzsq\n * @date 2022/8/7\n */\n@Intercepts({\n        @Signature(type = Executor.class, method = \"update\", args = {\n                MappedStatement.class, Object.class\n        })\n})\n@Component\npublic class InsertUpdateMyBatisInterceptorConfig implements Interceptor {\n    @Override\n    public Object intercept(Invocation invocation) throws Throwable {\n        // 当前用户 id\n        Long currentUserId = null;\n        try {\n            currentUserId = SecurityUtils.getUserId();\n        } catch (Exception e) {\n        }\n        // 调用拦截器\n        return InsertUpdateMyBatisInterceptor.intercept(invocation, currentUserId);\n    }\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/core/config/MyBatisPlusConfig.java",
    "content": "package com.geshanzsq.admin.core.config;\n\nimport com.baomidou.mybatisplus.annotation.DbType;\nimport com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;\nimport com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n\n/**\n * MyBatis Plus 配置\n *\n * @author geshanzsq\n * @date 2022/8/14\n */\n@Configuration\npublic class MyBatisPlusConfig {\n\n    /**\n     * 拦截器配置\n     */\n    @Bean\n    public MybatisPlusInterceptor mybatisPlusInterceptor() {\n        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();\n        // 分页插件\n        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));\n        return interceptor;\n    }\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/core/config/XssFilterConfig.java",
    "content": "package com.geshanzsq.admin.core.config;\n\nimport com.geshanzsq.common.core.filter.XssFilter;\nimport com.geshanzsq.common.core.util.string.StrUtils;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;\nimport org.springframework.boot.web.servlet.FilterRegistrationBean;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.util.CollectionUtils;\nimport javax.servlet.DispatcherType;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * XSS Filter 配置\n *\n * @author geshanzsq\n * @date 2023/5/3\n */\n@Configuration\npublic class XssFilterConfig {\n\n    @Value(\"${xss.excludes}\")\n    private List<String> excludes;\n\n    @Value(\"${xss.urlPatterns}\")\n    private List<String> urlPatterns;\n\n    @SuppressWarnings({\"rawtypes\", \"unchecked\"})\n    @Bean\n    @ConditionalOnProperty(value = \"xss.enabled\", havingValue = \"true\")\n    public FilterRegistrationBean xssFilterRegistration() {\n        FilterRegistrationBean registration = new FilterRegistrationBean();\n        registration.setDispatcherTypes(DispatcherType.REQUEST);\n        registration.setFilter(new XssFilter());\n        registration.addUrlPatterns(CollectionUtils.isEmpty(urlPatterns) ? new String[]{} : urlPatterns.toArray(new String[urlPatterns.size()]));\n        registration.setName(\"xssFilter\");\n        registration.setOrder(FilterRegistrationBean.HIGHEST_PRECEDENCE);\n        Map<String, String> initParameters = new HashMap<String, String>();\n        initParameters.put(\"excludes\", CollectionUtils.isEmpty(excludes) ? \"\" : StrUtils.join(excludes, \",\"));\n        registration.setInitParameters(initParameters);\n        return registration;\n    }\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/core/exception/GlobalExceptionHandler.java",
    "content": "package com.geshanzsq.admin.core.exception;\n\nimport com.geshanzsq.common.core.constant.HttpStatus;\nimport com.geshanzsq.common.core.exception.BaseException;\nimport com.geshanzsq.common.core.exception.ParamException;\nimport com.geshanzsq.common.core.exception.ServiceException;\nimport com.geshanzsq.common.core.util.message.MessageUtils;\nimport com.geshanzsq.common.core.web.response.ResponseResult;\nimport com.geshanzsq.common.rate.limiter.exception.RateLimiterException;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.validation.BindException;\nimport org.springframework.web.HttpRequestMethodNotSupportedException;\nimport org.springframework.web.bind.MethodArgumentNotValidException;\nimport org.springframework.web.bind.annotation.ExceptionHandler;\nimport org.springframework.web.bind.annotation.RestControllerAdvice;\n\nimport javax.servlet.http.HttpServletRequest;\n\n/**\n * 全局异常处理\n *\n * @author geshanzsq\n * @date 2022/3/26\n */\n@RestControllerAdvice\npublic class GlobalExceptionHandler {\n\n    private static Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);\n\n    /**\n     * 请求参数校验不通过，兼容不通注解\n     */\n    @ExceptionHandler(MethodArgumentNotValidException.class)\n    public ResponseResult methodArgumentNotValidException(MethodArgumentNotValidException e) {\n        String message = e.getBindingResult().getAllErrors().get(0).getDefaultMessage();\n        return ResponseResult.fail(HttpStatus.BAD_PARAM, message);\n    }\n\n    /**\n     * 请求参数校验不通过，兼容不通注解\n     */\n    @ExceptionHandler(BindException.class)\n    public ResponseResult handleBindException(BindException e) {\n        String message = e.getAllErrors().get(0).getDefaultMessage();\n        return ResponseResult.fail(HttpStatus.BAD_PARAM, message);\n    }\n\n    /**\n     * 参数异常\n     */\n    @ExceptionHandler(ParamException.class)\n    public ResponseResult paramException(ParamException e) {\n        return ResponseResult.fail(HttpStatus.BAD_PARAM, e.getMessage());\n    }\n\n    /**\n     * 请求方式不支持\n     */\n    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)\n    public ResponseResult handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException e, HttpServletRequest request) {\n        String requestURI = request.getRequestURI();\n        log.error(\"请求地址 {},不支持 {} 请求\", requestURI, e.getMethod());\n        return ResponseResult.fail(HttpStatus.BAD_METHOD, MessageUtils.message(\"bad.request.method\"));\n    }\n\n    /**\n     * 无权限访问\n     */\n    @ExceptionHandler(AccessDeniedException.class)\n    public ResponseResult accessDeniedException(AccessDeniedException e) {\n        log.error(e.getMessage());\n        return ResponseResult.fail(HttpStatus.FORBIDDEN, MessageUtils.message(\"security.forbidden\"));\n    }\n\n    /**\n     * 服务异常\n     */\n    @ExceptionHandler(ServiceException.class)\n    public ResponseResult serviceException(ServiceException e) {\n        e.printStackTrace();\n        return ResponseResult.fail(e.getCode(), e.getMessage());\n    }\n\n    /**\n     * 自定义基本异常\n     */\n    @ExceptionHandler(BaseException.class)\n    public ResponseResult baseException(BaseException e) {\n        log.error(\"所属模块：{}，异常信息：{}\", e.getModule(), e);\n        return ResponseResult.fail(e.getCode(), e.getMessage());\n    }\n\n    /**\n     * 限流\n     */\n    @ExceptionHandler(RateLimiterException.class)\n    public ResponseResult rateLimiterException(RateLimiterException e) {\n        return ResponseResult.fail(HttpStatus.FORBIDDEN, e.getMessage());\n    }\n\n    /**\n     * 其他异常\n     */\n    @ExceptionHandler(Exception.class)\n    public ResponseResult handleException(Exception e) {\n        e.printStackTrace();\n        return ResponseResult.fail(MessageUtils.message(\"system.exception\"));\n    }\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/core/security/auth/controller/AuthController.java",
    "content": "package com.geshanzsq.admin.core.security.auth.controller;\n\nimport com.geshanzsq.admin.core.security.auth.dto.AuthLoginDTO;\nimport com.geshanzsq.admin.core.security.auth.service.AuthService;\nimport com.geshanzsq.admin.core.security.auth.vo.AuthLoginVO;\nimport com.geshanzsq.common.core.web.response.ResponseResult;\nimport com.geshanzsq.common.framework.web.controller.BaseController;\nimport io.swagger.annotations.Api;\nimport io.swagger.annotations.ApiOperation;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.validation.Valid;\n\n/**\n * 认证中心\n *\n * @author geshanzsq\n * @date 2022/5/2\n */\n@RestController\n@Api(tags = \"认证中心\")\npublic class AuthController extends BaseController {\n\n    @Autowired\n    private AuthService authService;\n\n    @PostMapping(\"/login\")\n    @ApiOperation(\"登录\")\n    public ResponseResult<AuthLoginVO> login(@Valid @RequestBody AuthLoginDTO authLoginDTO) {\n        AuthLoginVO authLoginVO = authService.login(authLoginDTO);\n        return ResponseResult.success(authLoginVO);\n    }\n\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/core/security/auth/dto/AuthLoginDTO.java",
    "content": "package com.geshanzsq.admin.core.security.auth.dto;\n\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotBlank;\nimport java.io.Serializable;\n\n/**\n * 登录\n *\n * @author geshanzsq\n * @date 2022/5/3\n */\n@Data\n@ApiModel(\"登录\")\npublic class AuthLoginDTO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(value = \"用户名\", required = true)\n    @NotBlank(message = \"用户名不能为空\")\n    private String username;\n\n    @ApiModelProperty(value = \"密码\", required = true)\n    @NotBlank(message = \"密码不能为空\")\n    private String password;\n\n    @ApiModelProperty(value = \"验证码唯一标识\", required = true)\n    @NotBlank(message = \"验证码唯一标识不能为空\")\n    private String uuid = \"\";\n\n    @ApiModelProperty(value = \"验证码\", required = true)\n    @NotBlank(message = \"验证码不能为空\")\n    private String code;\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/core/security/auth/filter/TokenAuthenticationFilter.java",
    "content": "package com.geshanzsq.admin.core.security.auth.filter;\n\nimport com.geshanzsq.admin.system.user.service.SysUserService;\nimport com.geshanzsq.framework.security.domain.LoginUserDetail;\nimport com.geshanzsq.framework.security.service.TokenService;\nimport com.geshanzsq.framework.security.util.SecurityUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.web.authentication.WebAuthenticationDetailsSource;\nimport org.springframework.stereotype.Component;\nimport org.springframework.web.filter.OncePerRequestFilter;\n\nimport javax.servlet.FilterChain;\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\n\n/**\n * 令牌认证过滤器，验证令牌有效性\n *\n * @author geshanzsq\n * @date 2022/3/23\n */\n@Component\npublic class TokenAuthenticationFilter extends OncePerRequestFilter {\n\n    @Autowired\n    private TokenService tokenService;\n    @Autowired\n    private SysUserService sysUserService;\n\n    @Override\n    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {\n        LoginUserDetail loginUser = tokenService.getLoginUser(request);\n        if (loginUser == null) {\n            chain.doFilter(request, response);\n            return;\n        }\n\n        // 令牌未过期，设置 Spring Security\n        String token = tokenService.getToken();\n        // 是否需要刷新权限，当权限发生变动时，不刷新就能生效\n        boolean hasRefreshPermission = tokenService.hasRefreshPermission(loginUser.getUserId(), token);\n        if (hasRefreshPermission) {\n            // 重新获取用户信息\n            loginUser = sysUserService.getLoginUserByUsername(loginUser.getUsername());\n            // 设置不需要刷新权限\n            tokenService.setRefreshPermission(loginUser.getUserId(), token,false);\n        }\n\n        // 如果需要刷新权限，或者 Authentication 为空，则重新赋值用户信息\n        if (hasRefreshPermission || SecurityUtils.getAuthentication() == null) {\n            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());\n            authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));\n            SecurityContextHolder.getContext().setAuthentication(authenticationToken);\n        }\n\n        chain.doFilter(request, response);\n    }\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/core/security/auth/service/AuthService.java",
    "content": "package com.geshanzsq.admin.core.security.auth.service;\n\nimport com.geshanzsq.admin.core.security.auth.dto.AuthLoginDTO;\nimport com.geshanzsq.admin.core.security.auth.vo.AuthLoginVO;\n\n/**\n * 认证中心\n *\n * @author geshanzsq\n * @date 2022/5/3\n */\npublic interface AuthService {\n\n    /**\n     * 登录\n     */\n    AuthLoginVO login(AuthLoginDTO authLoginDTO);\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/core/security/auth/service/impl/AuthServiceImpl.java",
    "content": "package com.geshanzsq.admin.core.security.auth.service.impl;\n\nimport com.geshanzsq.admin.core.captcha.constant.CaptchaConstant;\nimport com.geshanzsq.admin.core.security.auth.dto.AuthLoginDTO;\nimport com.geshanzsq.admin.core.security.auth.service.AuthService;\nimport com.geshanzsq.admin.core.security.auth.vo.AuthLoginVO;\nimport com.geshanzsq.admin.system.log.login.enums.LogLoginStatus;\nimport com.geshanzsq.admin.system.log.login.factory.LogLoginAsyncFactory;\nimport com.geshanzsq.admin.system.user.service.SysUserService;\nimport com.geshanzsq.common.core.exception.ServiceException;\nimport com.geshanzsq.common.core.util.message.MessageUtils;\nimport com.geshanzsq.common.framework.manager.AsyncManager;\nimport com.geshanzsq.common.redis.service.RedisService;\nimport com.geshanzsq.framework.security.domain.LoginUserDetail;\nimport com.geshanzsq.framework.security.service.TokenService;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.authentication.BadCredentialsException;\nimport org.springframework.security.authentication.InternalAuthenticationServiceException;\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.stereotype.Service;\n\n/**\n * 认证中心\n *\n * @author geshanzsq\n * @date 2022/5/3\n */\n@Service\npublic class AuthServiceImpl implements AuthService {\n\n    @Autowired\n    private AuthenticationManager authenticationManager;\n\n    @Autowired\n    private TokenService tokenService;\n    @Autowired\n    private SysUserService sysUserService;\n    @Autowired\n    private RedisService redisService;\n\n    /**\n     * 登录\n     */\n    @Override\n    public AuthLoginVO login(AuthLoginDTO authLoginDTO) {\n        // 校验登录验证码\n        verifyLoginCode(authLoginDTO);\n\n        Authentication authentication = null;\n        // 用户名\n        String username = authLoginDTO.getUsername();\n        // 密码\n        String password = authLoginDTO.getPassword();\n\n        try {\n            // 该方法会调用 UserDetailsServiceImpl.loadUserByUsername\n            authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));\n        } catch (Exception e) {\n            if (e instanceof BadCredentialsException) {\n                String message = MessageUtils.message(\"login.username.password.not.match\");\n                // 异步记录登录日志\n                AsyncManager.me().execute(LogLoginAsyncFactory.add(username, LogLoginStatus.FAIL.code, message));\n                throw new ServiceException(message);\n            } else if (e instanceof InternalAuthenticationServiceException) {\n                String message = e.getMessage();\n                // 异步记录登录日志\n                AsyncManager.me().execute(LogLoginAsyncFactory.add(username, LogLoginStatus.FAIL.code, message));\n                throw new ServiceException(message);\n            } else {\n                String message = e.getMessage();\n                // 异步记录登录日志\n                AsyncManager.me().execute(LogLoginAsyncFactory.add(username, LogLoginStatus.FAIL.code, message));\n                throw new RuntimeException(message);\n            }\n        }\n\n        // 获取用户\n        LoginUserDetail loginUserDetail = (LoginUserDetail) authentication.getPrincipal();\n        // 设置用户权限\n        sysUserService.setLoginUserPermission(loginUserDetail);\n        // 创建令牌\n        String token = tokenService.createLoginUser(loginUserDetail);\n        AuthLoginVO authLoginVO = new AuthLoginVO();\n        authLoginVO.setToken(token);\n        // 异步记录登录日志\n        AsyncManager.me().execute(LogLoginAsyncFactory.add(loginUserDetail.getUserId(), username, LogLoginStatus.SUCCESS.code,\n                MessageUtils.message(\"login.success\")));\n        return authLoginVO;\n    }\n\n    /**\n     * 校验登录验证码\n     */\n    private void verifyLoginCode(AuthLoginDTO authLoginDTO) {\n        String verifyKey = CaptchaConstant.CAPTCHA_CODE_KEY + authLoginDTO.getUuid();\n        String captcha = redisService.get(verifyKey);\n        redisService.delete(verifyKey);\n        if (captcha == null) {\n            // 异步记录登录日志\n            String message = MessageUtils.message(\"login.captcha.expire\");\n            AsyncManager.me().execute(LogLoginAsyncFactory.add(\n                    authLoginDTO.getUsername(), LogLoginStatus.FAIL.code, message));\n            throw new ServiceException(message);\n        }\n        if (!authLoginDTO.getCode().equalsIgnoreCase(captcha)) {\n            String message = MessageUtils.message(\"login.captcha.error\");\n            // 异步记录登录日志\n            AsyncManager.me().execute(LogLoginAsyncFactory.add(\n                    authLoginDTO.getUsername(), LogLoginStatus.FAIL.code, message));\n            throw new ServiceException(message);\n        }\n    }\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/core/security/auth/service/impl/UserDetailsServiceImpl.java",
    "content": "package com.geshanzsq.admin.core.security.auth.service.impl;\n\nimport com.geshanzsq.admin.system.user.service.SysUserService;\nimport com.geshanzsq.common.core.exception.ServiceException;\nimport com.geshanzsq.common.core.util.message.MessageUtils;\nimport com.geshanzsq.framework.security.domain.LoginUserDetail;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.core.userdetails.UsernameNotFoundException;\nimport org.springframework.stereotype.Service;\n\n/**\n * 用户验证处理\n *\n * @author geshanzsq\n * @date 2022/3/24\n */\n@Service\npublic class UserDetailsServiceImpl implements UserDetailsService {\n\n    @Autowired\n    private SysUserService sysUserService;\n\n    @Override\n    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {\n        // 获取登录用户\n        LoginUserDetail loginUserDetail = sysUserService.getLoginUserByUsername(username);\n        if (loginUserDetail == null) {\n            throw new ServiceException(MessageUtils.message(\"login.username.password.not.match\"));\n        }\n        return loginUserDetail;\n    }\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/core/security/auth/vo/AuthLoginVO.java",
    "content": "package com.geshanzsq.admin.core.security.auth.vo;\n\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\nimport java.io.Serializable;\n\n/**\n * 登录\n *\n * @author geshanzsq\n * @date 2022/5/3\n */\n@Data\n@ApiModel(\"登录\")\npublic class AuthLoginVO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(\"令牌\")\n    private String token;\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/core/security/config/SecurityConfig.java",
    "content": "package com.geshanzsq.admin.core.security.config;\n\nimport com.geshanzsq.admin.core.security.auth.filter.TokenAuthenticationFilter;\nimport com.geshanzsq.common.framework.file.property.FileUploadProperty;\nimport com.geshanzsq.framework.security.handler.AuthenticationEntryPointImpl;\nimport com.geshanzsq.framework.security.handler.LogoutSuccessHandlerImpl;\nimport com.geshanzsq.framework.security.property.SecurityProperty;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;\nimport org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;\nimport org.springframework.security.config.http.SessionCreationPolicy;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;\nimport org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;\n\n/**\n * Spring Security 配置\n *\n * @author geshanzsq\n * @date 2022/3/19\n */\n@Configuration\n@EnableGlobalMethodSecurity(prePostEnabled = true)\npublic class SecurityConfig extends WebSecurityConfigurerAdapter {\n\n    /**\n     * 自定义登录认证\n     */\n    @Autowired\n    private UserDetailsService userDetailsService;\n\n    /**\n     * 认证失败处理\n     */\n    @Autowired\n    private AuthenticationEntryPointImpl authenticationEntryPoint;\n\n    /**\n     * 自定义退出\n     */\n    @Autowired\n    private LogoutSuccessHandlerImpl logoutSuccessHandler;\n\n    /**\n     * 令牌认证\n     */\n    @Autowired\n    private TokenAuthenticationFilter tokenAuthenticationFilter;\n\n    /**\n     * 安全配置\n     */\n    @Autowired\n    private SecurityProperty securityProperty;\n\n    @Autowired\n    private FileUploadProperty fileUploadProperty;\n\n\n    /**\n     * httpSecurity 配置\n     *\n     */\n    @Override\n    protected void configure(HttpSecurity httpSecurity) throws Exception {\n        httpSecurity\n                // CSRF禁用，因为不使用session\n                .csrf().disable()\n                // 认证失败处理\n                .exceptionHandling().authenticationEntryPoint(authenticationEntryPoint)\n                .and()\n                // 基于 token，不需要 session\n                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)\n                .and()\n                // 过滤请求\n                .authorizeRequests()\n                // 不用登录就能访问\n                .antMatchers(securityProperty.getNotLoginUrls()).permitAll()\n                .antMatchers(\n                        HttpMethod.GET,\n                        \"/*.html\",\n                        \"/**/*.html\",\n                        \"/**/*.css\",\n                        \"/**/*.js\"\n                ).permitAll()\n                .antMatchers(\"/swagger-ui.html\").permitAll()\n                .antMatchers(\"/swagger-resources/**\").permitAll()\n                .antMatchers(\"/webjars/**\").permitAll()\n                .antMatchers(\"/*/api-docs\").permitAll()\n                .antMatchers(\"/doc.html\").permitAll()\n                // 文件预览\n                .antMatchers(fileUploadProperty.getMapPrefix() + \"/**\").permitAll()\n                // 除了上面，都需要认证\n                .anyRequest().authenticated()\n                .and()\n                // 禁止 iframe 调用\n                .headers().frameOptions().disable();\n        // 自定义退出处理\n        httpSecurity.logout().logoutUrl(\"/logout\").logoutSuccessHandler(logoutSuccessHandler);\n        // 令牌认证\n        httpSecurity.addFilterBefore(tokenAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);\n    }\n\n    /**\n     * 身份认证\n     * @param auth\n     * @throws Exception\n     */\n    @Override\n    protected void configure(AuthenticationManagerBuilder auth) throws Exception {\n        auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());\n    }\n\n    /**\n     * 解决无法直接注入 AuthenticationManager\n     */\n    @Bean\n    @Override\n    public AuthenticationManager authenticationManagerBean() throws Exception {\n        return super.authenticationManagerBean();\n    }\n\n    /**\n     * 哈希加密实现\n     */\n    @Bean\n    public BCryptPasswordEncoder bCryptPasswordEncoder() {\n        return new BCryptPasswordEncoder();\n    }\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/nav/category/controller/NavCategoryController.java",
    "content": "package com.geshanzsq.admin.nav.category.controller;\n\nimport com.geshanzsq.admin.nav.category.dto.NavCategoryAddDTO;\nimport com.geshanzsq.admin.nav.category.dto.NavCategoryListDTO;\nimport com.geshanzsq.admin.nav.category.dto.NavCategoryUpdateDTO;\nimport com.geshanzsq.admin.nav.category.mapstruct.NavCategoryConverter;\nimport com.geshanzsq.admin.nav.category.po.NavCategory;\nimport com.geshanzsq.admin.nav.category.service.NavCategoryService;\nimport com.geshanzsq.admin.nav.category.vo.NavCategoryTreeVO;\nimport com.geshanzsq.admin.nav.category.vo.NavCategoryVO;\nimport com.geshanzsq.common.core.web.response.ResponseResult;\nimport com.geshanzsq.common.framework.web.controller.BaseController;\nimport com.geshanzsq.common.log.annotation.Log;\nimport com.geshanzsq.common.log.enums.BusinessType;\nimport io.swagger.annotations.Api;\nimport io.swagger.annotations.ApiOperation;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.validation.Valid;\nimport java.util.List;\n\n/**\n * 导航分类\n *\n * @author geshanzsq\n * @date 2022/11/20\n */\n@Api(tags = \"导航分类\")\n@RestController\n@RequestMapping(\"/nav/category\")\npublic class NavCategoryController extends BaseController {\n\n    @Autowired\n    private NavCategoryService navCategoryService;\n\n    @ApiOperation(\"列表\")\n    @GetMapping(\"/list\")\n    @PreAuthorize(\"@auth.hasUrl()\")\n    public ResponseResult<List<NavCategoryVO>> list(NavCategoryListDTO listDTO) {\n        List<NavCategory> list = navCategoryService.list(listDTO);\n        return ResponseResult.success(NavCategoryConverter.INSTANCE.convert(list));\n    }\n\n    @ApiOperation(\"树形\")\n    @GetMapping(\"/tree\")\n    public ResponseResult<List<NavCategoryTreeVO>> tree() {\n        List<NavCategoryTreeVO> list = navCategoryService.tree();\n        return ResponseResult.success(list);\n    }\n\n    @GetMapping(\"/getById/{id}\")\n    @ApiOperation(\"详情\")\n    @PreAuthorize(\"@auth.hasUrl()\")\n    public ResponseResult<NavCategoryVO> getById(@PathVariable Long id) {\n        return ResponseResult.success(NavCategoryConverter.INSTANCE.convert(navCategoryService.getById(id)));\n    }\n\n    @PostMapping\n    @ApiOperation(\"新增\")\n    @PreAuthorize(\"@auth.hasUrl()\")\n    @Log(moduleName = \"导航分类\", businessType = BusinessType.ADD)\n    public ResponseResult add(@Valid @RequestBody NavCategoryAddDTO addDTO) {\n        NavCategory navCategory = NavCategoryConverter.INSTANCE.convert(addDTO);\n        navCategoryService.save(navCategory);\n        return ResponseResult.success();\n    }\n\n    @PutMapping\n    @ApiOperation(\"修改\")\n    @PreAuthorize(\"@auth.hasUrl()\")\n    @Log(moduleName = \"导航分类\", businessType = BusinessType.UPDATE)\n    public ResponseResult update(@Valid @RequestBody NavCategoryUpdateDTO updateDTO) {\n        navCategoryService.updateById(updateDTO);\n        return ResponseResult.success();\n    }\n\n    @DeleteMapping(\"/delete/{id}\")\n    @ApiOperation(\"删除\")\n    @PreAuthorize(\"@auth.hasUrl()\")\n    @Log(moduleName = \"导航分类\", businessType = BusinessType.DELETE)\n    public ResponseResult delete(@PathVariable Long id) {\n        navCategoryService.remove(id);\n        return ResponseResult.success();\n    }\n\n    @GetMapping(\"/getMaxSortByParentId\")\n    @ApiOperation(\"获取最大排序\")\n    @PreAuthorize(\"@auth.hasUrl()\")\n    public ResponseResult<Integer> getMaxSortByParentId(Long parentId) {\n        Integer maxSort = navCategoryService.getMaxSortByParentId(parentId);\n        return ResponseResult.success(maxSort);\n    }\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/nav/category/dto/NavCategoryAddDTO.java",
    "content": "package com.geshanzsq.admin.nav.category.dto;\n\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotBlank;\nimport javax.validation.constraints.NotNull;\nimport java.io.Serializable;\n\n/**\n * 导航分类新增\n *\n * @author geshanzsq\n * @date 2022/11/20\n */\n@Data\n@ApiModel(\"导航分类新增\")\npublic class NavCategoryAddDTO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(value = \"上级分类 id\", required = true)\n    @NotNull(message = \"上级分类不能为空\")\n    private Long parentId;\n\n    @ApiModelProperty(value = \"排序\", required = true)\n    @NotNull(message = \"排序不能为空\")\n    private Integer sort;\n\n    @ApiModelProperty(value = \"分类名称\", required = true)\n    @NotBlank(message = \"分类名称不能为空\")\n    private String categoryName;\n\n    @ApiModelProperty(\"分类图标\")\n    private String categoryIcon;\n\n    @ApiModelProperty(value = \"状态（1 正常，2 停用）\", required = true)\n    @NotNull(message = \"状态（1 正常，2 停用）不能为空\")\n    private Integer status;\n\n}"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/nav/category/dto/NavCategoryListDTO.java",
    "content": "package com.geshanzsq.admin.nav.category.dto;\n\nimport com.geshanzsq.common.framework.mybatis.plugin.annotation.Query;\nimport com.geshanzsq.common.framework.mybatis.plugin.enums.QueryWay;\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\nimport java.io.Serializable;\n\n/**\n * 导航分类分页\n *\n * @author geshanzsq\n * @date 2022/11/20\n */\n@Data\n@ApiModel(\"导航分类分页\")\npublic class NavCategoryListDTO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(\"分类名称\")\n    @Query(QueryWay.LIKE)\n    private String categoryName;\n\n    @ApiModelProperty(\"状态（1 正常，2 停用）\")\n    private Integer status;\n\n    @ApiModelProperty(value = \"排序列，多个用逗号分开\", hidden = true)\n    @Query(ignore = true)\n    private String orderColumn;\n\n    @ApiModelProperty(value = \"排序类型(asc 或 desc)，多个用逗号分开\", hidden = true)\n    @Query(ignore = true)\n    private String orderType;\n\n}"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/nav/category/dto/NavCategoryUpdateDTO.java",
    "content": "package com.geshanzsq.admin.nav.category.dto;\n\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotBlank;\nimport javax.validation.constraints.NotNull;\nimport java.io.Serializable;\n\n/**\n * 导航分类更新\n *\n * @author geshanzsq\n * @date 2022/11/20\n */\n@Data\n@ApiModel(\"导航分类更新\")\npublic class NavCategoryUpdateDTO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(value = \"分类 id\", required = true)\n    @NotNull(message = \"分类 id不能为空\")\n    private Long id;\n\n    @ApiModelProperty(value = \"上级分类 id\", required = true)\n    @NotNull(message = \"上级分类不能为空\")\n    private Long parentId;\n\n    @ApiModelProperty(value = \"排序\", required = true)\n    @NotNull(message = \"排序不能为空\")\n    private Integer sort;\n\n    @ApiModelProperty(value = \"分类名称\", required = true)\n    @NotBlank(message = \"分类名称不能为空\")\n    private String categoryName;\n\n    @ApiModelProperty(\"分类图标\")\n    private String categoryIcon;\n\n    @ApiModelProperty(value = \"状态（1 正常，2 停用）\", required = true)\n    @NotNull(message = \"状态（1 正常，2 停用）不能为空\")\n    private Integer status;\n\n}"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/nav/category/mapper/NavCategoryMapper.java",
    "content": "package com.geshanzsq.admin.nav.category.mapper;\n\nimport com.geshanzsq.admin.nav.category.po.NavCategory;\nimport com.geshanzsq.common.framework.web.mapper.BaseMapperPlus;\nimport org.apache.ibatis.annotations.Mapper;\n\n/**\n * 导航分类\n *\n * @author geshanzsq\n * @date 2022/11/20\n */\n@Mapper\npublic interface NavCategoryMapper extends BaseMapperPlus<NavCategory> {\n\n    /**\n     * 获取最大排序\n     * @param parentId 上级分类 id\n     */\n    Integer selectMaxSortByParentId(Long parentId);\n\n}"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/nav/category/mapstrcut/NavCategoryConverter.java",
    "content": "package com.geshanzsq.admin.nav.category.mapstruct;\n\nimport com.geshanzsq.admin.client.nav.vo.NavCategoryClientVO;\nimport com.geshanzsq.admin.client.nav.vo.NavCategorySiteClientVO;\nimport com.geshanzsq.admin.nav.category.dto.NavCategoryAddDTO;\nimport com.geshanzsq.admin.nav.category.dto.NavCategoryUpdateDTO;\nimport com.geshanzsq.admin.nav.category.po.NavCategory;\nimport com.geshanzsq.admin.nav.category.vo.NavCategoryTreeVO;\nimport com.geshanzsq.admin.nav.category.vo.NavCategoryVO;\nimport org.mapstruct.Mapper;\nimport org.mapstruct.factory.Mappers;\n\nimport java.util.List;\n\n/**\n * 导航分类对象转换\n *\n * @author geshanzsq\n * @date 2022/11/20\n */\n@Mapper\npublic interface NavCategoryConverter {\n\n    NavCategoryConverter INSTANCE = Mappers.getMapper(NavCategoryConverter.class);\n\n    List<NavCategoryVO> convert(List<NavCategory> list);\n\n    List<NavCategoryTreeVO> convertTree(List<NavCategory> list);\n\n    NavCategoryVO convert(NavCategory navCategory);\n\n    NavCategory convert(NavCategoryAddDTO addDTO);\n\n    NavCategory convert(NavCategoryUpdateDTO updateDTO);\n\n    List<NavCategoryClientVO> convertClient(List<NavCategoryTreeVO> list);\n\n    List<NavCategorySiteClientVO> convertSiteClient(List<NavCategoryClientVO> list);\n\n}"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/nav/category/po/NavCategory.java",
    "content": "package com.geshanzsq.admin.nav.category.po;\n\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport lombok.Data;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\n/**\n * 导航分类\n *\n * @author geshanzsq\n * @date 2022/11/20\n */\n@Data\npublic class NavCategory implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * 分类 id\n    */\n    @TableId\n    private Long id;\n\n    /**\n     * 上级 id\n    */\n    private Long parentId;\n\n    /**\n     * 排序\n    */\n    private Integer sort;\n\n    /**\n     * 分类名称\n    */\n    private String categoryName;\n\n    /**\n     * 分类图标\n    */\n    private String categoryIcon;\n\n    /**\n     * 状态（1 正常，2 停用）\n    */\n    private Integer status;\n\n    /**\n     * 创建人用户 id\n    */\n    @TableField(\"fk_create_user_id\")\n    private Long createUserId;\n\n    /**\n     * 创建时间\n    */\n    private Date gmtCreate;\n\n    /**\n     * 修改人用户 id\n    */\n    @TableField(\"fk_modify_user_id\")\n    private Long modifyUserId;\n\n    /**\n     * 修改时间\n    */\n    private Date gmtModify;\n\n}"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/nav/category/service/NavCategoryService.java",
    "content": "package com.geshanzsq.admin.nav.category.service;\n\nimport com.geshanzsq.admin.nav.category.dto.NavCategoryUpdateDTO;\nimport com.geshanzsq.admin.nav.category.po.NavCategory;\nimport com.geshanzsq.admin.nav.category.vo.NavCategoryTreeVO;\nimport com.geshanzsq.common.framework.web.service.BaseService;\n\nimport java.util.List;\n\n/**\n * 导航分类\n *\n * @author geshanzsq\n * @date 2022/11/20\n */\npublic interface NavCategoryService extends BaseService<NavCategory> {\n\n    /**\n     * 获取最大排序\n     * @param parentId 上级分类 id\n     */\n    Integer getMaxSortByParentId(Long parentId);\n\n    /**\n     * 树形\n     */\n    List<NavCategoryTreeVO> tree();\n\n    /**\n     * 树形\n     */\n    List<NavCategoryTreeVO> treeByIds(List<Long> ids);\n\n    /**\n     * 更新\n     */\n    void updateById(NavCategoryUpdateDTO updateDTO);\n\n    /**\n     * 删除\n     */\n    void remove(Long id);\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/nav/category/service/impl/NavCategoryServiceImpl.java",
    "content": "package com.geshanzsq.admin.nav.category.service.impl;\n\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.geshanzsq.admin.client.nav.service.ClientNavService;\nimport com.geshanzsq.admin.nav.category.dto.NavCategoryUpdateDTO;\nimport com.geshanzsq.admin.nav.category.mapper.NavCategoryMapper;\nimport com.geshanzsq.admin.nav.category.mapstruct.NavCategoryConverter;\nimport com.geshanzsq.admin.nav.category.po.NavCategory;\nimport com.geshanzsq.admin.nav.category.service.NavCategoryService;\nimport com.geshanzsq.admin.nav.category.vo.NavCategoryTreeVO;\nimport com.geshanzsq.admin.nav.site.mapper.NavSiteMapper;\nimport com.geshanzsq.admin.nav.site.po.NavSite;\nimport com.geshanzsq.common.core.enums.CommonStatus;\nimport com.geshanzsq.common.core.enums.YesNo;\nimport com.geshanzsq.common.core.exception.ParamException;\nimport com.geshanzsq.common.core.util.message.MessageUtils;\nimport com.geshanzsq.common.core.util.string.StrUtils;\nimport com.geshanzsq.common.framework.manager.AsyncManager;\nimport com.geshanzsq.common.framework.web.service.impl.BaseServiceImpl;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\nimport org.springframework.util.CollectionUtils;\n\nimport java.util.ArrayList;\nimport java.util.Iterator;\nimport java.util.List;\n\n/**\n * 导航分类\n *\n * @author geshanzsq\n * @date 2022/11/20\n */\n@Service\npublic class NavCategoryServiceImpl extends BaseServiceImpl<NavCategoryMapper, NavCategory> implements NavCategoryService {\n\n    @Autowired\n    private NavCategoryMapper navCategoryMapper;\n    @Autowired\n    private NavSiteMapper navSiteMapper;\n\n    @Autowired\n    private ClientNavService clientNavService;\n\n    /**\n     * 获取最大排序\n     * @param parentId 上级分类 id\n     */\n    @Override\n    public Integer getMaxSortByParentId(Long parentId) {\n        return navCategoryMapper.selectMaxSortByParentId(parentId == null ? 0 : parentId);\n    }\n\n    /**\n     * 树形\n     */\n    @Override\n    public List<NavCategoryTreeVO> tree() {\n        // 获取列表数据\n        LambdaQueryWrapper<NavCategory> wrapper = new LambdaQueryWrapper<>();\n        wrapper.eq(NavCategory::getStatus, YesNo.YES.code);\n        wrapper.orderByAsc(NavCategory::getSort);\n        List<NavCategory> list = navCategoryMapper.selectList(wrapper);\n        List<NavCategoryTreeVO> categoryVOList = NavCategoryConverter.INSTANCE.convertTree(list);\n\n        // 构造树形结构\n        return buildTree(categoryVOList, 0L);\n    }\n\n    /**\n     * 树形\n     */\n    @Override\n    public List<NavCategoryTreeVO> treeByIds(List<Long> ids) {\n        if (CollectionUtils.isEmpty(ids)) {\n            return new ArrayList<>();\n        }\n        LambdaQueryWrapper<NavCategory> wrapper = new LambdaQueryWrapper<>();\n        wrapper.in(NavCategory::getId, ids);\n        wrapper.eq(NavCategory::getStatus, CommonStatus.NORMAL.code);\n\n        List<NavCategory> list = navCategoryMapper.selectList(wrapper);\n        List<NavCategoryTreeVO> categoryVOList = NavCategoryConverter.INSTANCE.convertTree(list);\n        // 构造树形结构\n        return buildTree(categoryVOList, 0L);\n    }\n\n    /**\n     * 更新\n     */\n    @Override\n    public void updateById(NavCategoryUpdateDTO updateDTO) {\n        if (updateDTO.getId().equals(updateDTO.getParentId())) {\n            throw new ParamException(MessageUtils.message(\"nav.category.id.parent.id\"));\n        }\n        NavCategory navCategory = NavCategoryConverter.INSTANCE.convert(updateDTO);\n        navCategoryMapper.updateById(navCategory);\n        // 清除缓存，等后续访问时会自动从数据库获取\n        AsyncManager.me().execute(() -> clientNavService.removeCache());\n    }\n\n    /**\n     * 删除\n     */\n    @Transactional(rollbackFor = Exception.class)\n    @Override\n    public void remove(Long id) {\n        if (id == null) {\n            return;\n        }\n        // 判断是否有下级分类，如果有，则不能删除\n        LambdaQueryWrapper<NavCategory> wrapper = new LambdaQueryWrapper<>();\n        wrapper.eq(NavCategory::getParentId, id);\n        Long count = navCategoryMapper.selectCount(wrapper);\n        if (count > 0) {\n            throw new ParamException(MessageUtils.message(\"nav.category.has.child\"));\n        }\n\n        // 删除分类数据\n        navCategoryMapper.deleteById(id);\n\n        // 删除分类和网站对应关系\n        LambdaQueryWrapper<NavSite> amWrapper = new LambdaQueryWrapper<>();\n        amWrapper.eq(NavSite::getCategoryId, id);\n        navSiteMapper.delete(amWrapper);\n\n        // 清除缓存，等后续访问时会自动从数据库获取\n        AsyncManager.me().execute(() -> clientNavService.removeCache());\n    }\n\n    /**\n     * 根据父节点的 id 获取所有子节点\n     *\n     * @param allList 所有数据\n     * @param parentId 传入的父节点 id\n     */\n    public List<NavCategoryTreeVO> buildTree(List<NavCategoryTreeVO> allList, Long parentId) {\n        List<NavCategoryTreeVO> returnList = new ArrayList<>();\n        for (NavCategoryTreeVO category : allList) {\n            // 根据传入的某个父节点 id，遍历该父节点的所有子节点\n            if (parentId != null && parentId.equals(category.getParentId())) {\n                // 递归\n                recursive(allList, category);\n                returnList.add(category);\n            }\n        }\n        return returnList;\n    }\n\n    /**\n     * 递归列表\n     *\n     * @param allList\n     * @param category 当前分类\n     */\n    private void recursive(List<NavCategoryTreeVO> allList, NavCategoryTreeVO category) {\n        // 得到子节点列表\n        List<NavCategoryTreeVO> childList = getChildList(allList, category);\n        category.setChildren(childList);\n        for (NavCategoryTreeVO tChild : childList) {\n            if (hasChild(allList, tChild)) {\n                recursive(allList, tChild);\n            }\n        }\n    }\n\n    /**\n     * 判断是否有子节点\n     */\n    private boolean hasChild(List<NavCategoryTreeVO> allList, NavCategoryTreeVO category) {\n        return getChildList(allList, category).size() > 0;\n    }\n\n    /**\n     * 得到子节点列表\n     */\n    private List<NavCategoryTreeVO> getChildList(List<NavCategoryTreeVO> allList, NavCategoryTreeVO category) {\n        List<NavCategoryTreeVO> childList = new ArrayList<>();\n        Iterator<NavCategoryTreeVO> it = allList.iterator();\n        while (it.hasNext()) {\n            NavCategoryTreeVO child = it.next();\n            if (category.getId().equals(child.getParentId())) {\n                childList.add(child);\n            }\n        }\n        return childList;\n    }\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/nav/category/vo/NavCategoryTreeVO.java",
    "content": "package com.geshanzsq.admin.nav.category.vo;\n\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\nimport java.io.Serializable;\nimport java.util.List;\n\n/**\n * 导航分类树形\n *\n * @author geshanzsq\n * @date 2022/11/20\n */\n@Data\n@ApiModel(\"导航分类树形\")\npublic class NavCategoryTreeVO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(\"分类 id\")\n    private Long id;\n\n    @ApiModelProperty(\"上级 id\")\n    private Long parentId;\n\n    @ApiModelProperty(\"分类名称\")\n    private String categoryName;\n\n    @ApiModelProperty(\"分类图标\")\n    private String categoryIcon;\n\n    @ApiModelProperty(\"排序\")\n    private Integer sort;\n\n    @ApiModelProperty(\"子分类\")\n    private List<NavCategoryTreeVO> children;\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/nav/category/vo/NavCategoryVO.java",
    "content": "package com.geshanzsq.admin.nav.category.vo;\n\nimport com.geshanzsq.common.core.web.vo.BaseVO;\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\nimport java.io.Serializable;\n\n/**\n * 导航分类\n *\n * @author geshanzsq\n * @date 2022/11/20\n */\n@Data\n@ApiModel(\"导航分类\")\npublic class NavCategoryVO extends BaseVO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(\"分类 id\")\n    private Long id;\n\n    @ApiModelProperty(\"上级 id\")\n    private Long parentId;\n\n    @ApiModelProperty(\"排序\")\n    private Integer sort;\n\n    @ApiModelProperty(\"分类名称\")\n    private String categoryName;\n\n    @ApiModelProperty(\"分类图标\")\n    private String categoryIcon;\n\n    @ApiModelProperty(\"状态（1 正常，2 停用）\")\n    private Integer status;\n\n}"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/nav/comment/constant/NavCommentConstant.java",
    "content": "package com.geshanzsq.admin.nav.comment.constant;\n\n/**\n * 评论常量\n *\n * @author geshanzsq\n * @date 2023/4/15\n */\npublic class NavCommentConstant {\n\n    /**\n     * 开启状态\n     */\n    public static final String NAV_COMMENT_OPEN = \"NAV_COMMENT_OPEN\";\n\n    /**\n     * 是否开启不用登录就能提交评论\n     */\n    public static final String NAV_COMMENT_NOT_LOGIN_OPEN = \"NAV_COMMENT_NOT_LOGIN_OPEN\";\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/nav/comment/controller/NavCommentController.java",
    "content": "package com.geshanzsq.admin.nav.comment.controller;\n\nimport com.geshanzsq.admin.nav.comment.dto.NavCommentPageDTO;\nimport com.geshanzsq.admin.nav.comment.dto.NavCommentRejectDTO;\nimport com.geshanzsq.admin.nav.comment.mapstruct.NavCommentConverter;\nimport com.geshanzsq.admin.nav.comment.po.NavComment;\nimport com.geshanzsq.admin.nav.comment.service.NavCommentService;\nimport com.geshanzsq.admin.nav.comment.vo.NavCommentVO;\nimport com.geshanzsq.common.core.enums.YesNo;\nimport com.geshanzsq.common.core.web.response.ResponseResult;\nimport com.geshanzsq.common.framework.mybatis.page.vo.PageVO;\nimport com.geshanzsq.common.log.annotation.Log;\nimport com.geshanzsq.common.log.enums.BusinessType;\nimport com.geshanzsq.framework.security.util.SecurityUtils;\nimport io.swagger.annotations.Api;\nimport io.swagger.annotations.ApiOperation;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.validation.Valid;\nimport java.util.Arrays;\n\n/**\n * 评论\n *\n * @author geshanzsq\n * @date 2022/11/29\n */\n@Api(tags = \"评论\")\n@RestController\n@RequestMapping(\"/nav/comment\")\npublic class NavCommentController {\n\n    @Autowired\n    private NavCommentService navCommentService;\n\n    @ApiOperation(\"分页列表\")\n    @GetMapping(\"/page\")\n    @PreAuthorize(\"@auth.hasUrl()\")\n    public ResponseResult<PageVO<NavCommentVO>> page(NavCommentPageDTO pageDTO) {\n        PageVO<NavCommentVO> pageVO = navCommentService.pageList(pageDTO);\n        return ResponseResult.success(pageVO);\n    }\n\n\n    @ApiOperation(\"通过\")\n    @PutMapping(\"/pass/{ids}\")\n    @PreAuthorize(\"@auth.hasUrl()\")\n    @Log(moduleName = \"评论-通过\", businessType = BusinessType.UPDATE)\n    public ResponseResult pass(@PathVariable Long[] ids) {\n        navCommentService.pass(ids);\n        return ResponseResult.success();\n    }\n\n    @ApiOperation(\"驳回\")\n    @PutMapping(\"/reject\")\n    @PreAuthorize(\"@auth.hasUrl()\")\n    @Log(moduleName = \"评论-驳回\", businessType = BusinessType.UPDATE)\n    public ResponseResult reject(@Valid @RequestBody NavCommentRejectDTO rejectDTO) {\n        navCommentService.reject(rejectDTO);\n        return ResponseResult.success();\n    }\n\n    @ApiOperation(\"置顶\")\n    @PutMapping(\"/sticky/{id}\")\n    @PreAuthorize(\"@auth.hasUrl()\")\n    @Log(moduleName = \"评论-置顶\", businessType = BusinessType.UPDATE)\n    public ResponseResult pass(@PathVariable Long id) {\n        navCommentService.updateSticky(id, YesNo.YES.code);\n        return ResponseResult.success();\n    }\n\n    @ApiOperation(\"取消置顶\")\n    @PutMapping(\"/cancelSticky/{id}\")\n    @PreAuthorize(\"@auth.hasUrl()\")\n    @Log(moduleName = \"评论-取消置顶\", businessType = BusinessType.UPDATE)\n    public ResponseResult cancelSticky(@PathVariable Long id) {\n        navCommentService.updateSticky(id, YesNo.NO.code);\n        return ResponseResult.success();\n    }\n\n    @DeleteMapping(\"/delete/{ids}\")\n    @ApiOperation(\"删除\")\n    @PreAuthorize(\"@auth.hasUrl()\")\n    @Log(moduleName = \"评论\", businessType = BusinessType.DELETE)\n    public ResponseResult delete(@PathVariable Long[] ids) {\n        navCommentService.removeByIds(Arrays.asList(ids));\n        return ResponseResult.success();\n    }\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/nav/comment/dto/NavCommentPageDTO.java",
    "content": "package com.geshanzsq.admin.nav.comment.dto;\n\nimport com.geshanzsq.common.framework.mybatis.page.dto.PageDTO;\nimport com.geshanzsq.common.framework.mybatis.plugin.annotation.Query;\nimport com.geshanzsq.common.framework.mybatis.plugin.enums.QueryWay;\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\nimport java.io.Serializable;\n\n/**\n * 评论分页\n *\n * @author geshanzsq\n * @date 2022/11/29\n */\n@Data\n@ApiModel(\"评论分页\")\npublic class NavCommentPageDTO extends PageDTO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(\"评论内容\")\n    @Query(QueryWay.LIKE)\n    private String commentContent;\n\n    @ApiModelProperty(\"状态（1 待审核，2 通过，3 驳回）\")\n    private Integer status;\n\n    @ApiModelProperty(value = \"用户 id\", hidden = true)\n    private Long createUserId;\n\n}"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/nav/comment/dto/NavCommentRejectDTO.java",
    "content": "package com.geshanzsq.admin.nav.comment.dto;\n\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotEmpty;\nimport java.io.Serializable;\nimport java.util.List;\n\n/**\n * 评论驳回\n *\n * @author geshanzsq\n * @date 2022/11/30\n */\n@Data\n@ApiModel(\"评论驳回\")\npublic class NavCommentRejectDTO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(\"评论 ids\")\n    @NotEmpty(message = \"评论 ids 不能为空\")\n    private List<Long> ids;\n\n    @ApiModelProperty(\"备注(驳回原因)\")\n    private String remark;\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/nav/comment/enums/NavCommentStatus.java",
    "content": "package com.geshanzsq.admin.nav.comment.enums;\n\n/**\n * 评论状态\n *\n * @author geshanzsq\n * @date 2022/11/30\n */\npublic enum NavCommentStatus {\n\n    /**\n     * 审核中\n     */\n    AUDIT(1),\n\n    /**\n     * 已通过\n     */\n    PASS(2),\n\n    /**\n     * 已拒绝\n     */\n    REJECT(3)\n\n    ;\n\n    public final Integer code;\n\n    NavCommentStatus(Integer code) {\n        this.code = code;\n    }\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/nav/comment/mapper/NavCommentMapper.java",
    "content": "package com.geshanzsq.admin.nav.comment.mapper;\n\nimport com.geshanzsq.admin.nav.comment.po.NavComment;\nimport com.geshanzsq.common.framework.web.mapper.BaseMapperPlus;\nimport org.apache.ibatis.annotations.Mapper;\n\n/**\n * 评论\n *\n * @author geshanzsq\n * @date 2022/11/29\n */\n@Mapper\npublic interface NavCommentMapper extends BaseMapperPlus<NavComment> {\n\n\n}"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/nav/comment/mapstruct/NavCommentConverter.java",
    "content": "package com.geshanzsq.admin.nav.comment.mapstruct;\n\nimport com.geshanzsq.admin.client.nav.dto.ClientNavCommentAddDTO;\nimport com.geshanzsq.admin.client.nav.vo.ClientNavCommentTreeVO;\nimport com.geshanzsq.admin.nav.comment.po.NavComment;\nimport com.geshanzsq.admin.nav.comment.vo.NavCommentVO;\nimport com.geshanzsq.common.framework.mybatis.page.vo.PageVO;\nimport org.mapstruct.Mapper;\nimport org.mapstruct.factory.Mappers;\n\nimport java.util.List;\n\n/**\n * 评论对象转换\n *\n * @author geshanzsq\n * @date 2022/11/29\n */\n@Mapper\npublic interface NavCommentConverter {\n\n    NavCommentConverter INSTANCE = Mappers.getMapper(NavCommentConverter.class);\n\n    PageVO<NavCommentVO> convert(PageVO<NavComment> pageVO);\n\n    NavCommentVO convert(NavComment navComment);\n\n    List<ClientNavCommentTreeVO> convertClient(List<NavComment> list);\n\n    NavComment convert(ClientNavCommentAddDTO addDTO);\n\n}"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/nav/comment/po/NavComment.java",
    "content": "package com.geshanzsq.admin.nav.comment.po;\n\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport lombok.Data;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\n/**\n * 评论\n *\n * @author geshanzsq\n * @date 2022/11/29\n */\n@Data\npublic class NavComment implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * 评论 id\n    */\n    @TableId\n    private Long id;\n\n    /**\n     * 上级 id\n    */\n    private Long parentId;\n\n    /**\n     * 是否置顶（1 是，2 否）\n    */\n    private Integer hasSticky;\n\n    /**\n     * 评论内容\n    */\n    private String commentContent;\n\n    /**\n     * 昵称\n    */\n    private String nickName;\n\n    /**\n     * 邮箱\n    */\n    private String email;\n\n    /**\n     * 备注\n    */\n    private String remark;\n\n    /**\n     * 状态（1 待审核，2 通过，3 驳回）\n    */\n    private Integer status;\n\n    /**\n     * 创建人用户 id\n    */\n    @TableField(\"fk_create_user_id\")\n    private Long createUserId;\n\n    /**\n     * 创建时间\n    */\n    private Date gmtCreate;\n\n    /**\n     * 修改人用户 id\n    */\n    @TableField(\"fk_modify_user_id\")\n    private Long modifyUserId;\n\n    /**\n     * 修改时间\n    */\n    private Date gmtModify;\n\n}"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/nav/comment/service/NavCommentService.java",
    "content": "package com.geshanzsq.admin.nav.comment.service;\n\nimport com.geshanzsq.admin.client.nav.dto.ClientNavCommentAddDTO;\nimport com.geshanzsq.admin.client.nav.vo.ClientNavCommentTreeVO;\nimport com.geshanzsq.admin.nav.comment.dto.NavCommentPageDTO;\nimport com.geshanzsq.admin.nav.comment.dto.NavCommentRejectDTO;\nimport com.geshanzsq.admin.nav.comment.po.NavComment;\nimport com.geshanzsq.admin.nav.comment.vo.NavCommentVO;\nimport com.geshanzsq.common.framework.mybatis.page.vo.PageVO;\nimport com.geshanzsq.common.framework.web.service.BaseService;\n\nimport java.util.List;\n\n/**\n * 评论\n *\n * @author geshanzsq\n * @date 2022/11/29\n */\npublic interface NavCommentService extends BaseService<NavComment> {\n\n    /**\n     * 分页列表\n     */\n    PageVO<NavCommentVO> pageList(NavCommentPageDTO pageDTO);\n\n    /**\n     * 通过\n     */\n    void pass(Long[] ids);\n\n    /**\n     * 驳回\n     */\n    void reject(NavCommentRejectDTO rejectDTO);\n\n    /**\n     * 更新置顶\n     *\n     * @param id 评论 id\n     * @param hasSticky 是否置顶\n     */\n    void updateSticky(Long id, Integer hasSticky);\n\n    /**\n     * 获取评论开启状态\n     */\n    boolean getOpenStatus();\n\n    /**\n     * 是否开启不用登录就能提交评论\n     */\n    boolean getNotLoginOpenStatus();\n\n    /**\n     * 树形列表\n     */\n    List<ClientNavCommentTreeVO> treeClient();\n\n    /**\n     * 新增\n     */\n    void add(ClientNavCommentAddDTO addDTO);\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/nav/comment/service/impl/NavCommentServiceImpl.java",
    "content": "package com.geshanzsq.admin.nav.comment.service.impl;\n\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;\nimport com.geshanzsq.admin.client.nav.dto.ClientNavCommentAddDTO;\nimport com.geshanzsq.admin.client.nav.vo.ClientNavCommentTreeVO;\nimport com.geshanzsq.admin.nav.category.vo.NavCategoryTreeVO;\nimport com.geshanzsq.admin.nav.comment.constant.NavCommentConstant;\nimport com.geshanzsq.admin.nav.comment.dto.NavCommentPageDTO;\nimport com.geshanzsq.admin.nav.comment.dto.NavCommentRejectDTO;\nimport com.geshanzsq.admin.nav.comment.enums.NavCommentStatus;\nimport com.geshanzsq.admin.nav.comment.mapper.NavCommentMapper;\nimport com.geshanzsq.admin.nav.comment.mapstruct.NavCommentConverter;\nimport com.geshanzsq.admin.nav.comment.po.NavComment;\nimport com.geshanzsq.admin.nav.comment.service.NavCommentService;\nimport com.geshanzsq.admin.nav.comment.vo.NavCommentVO;\nimport com.geshanzsq.admin.system.param.service.SysParamService;\nimport com.geshanzsq.admin.system.user.mapper.SysUserRoleMapper;\nimport com.geshanzsq.admin.system.user.service.SysUserService;\nimport com.geshanzsq.admin.system.user.vo.SysUserVO;\nimport com.geshanzsq.common.core.constant.HttpStatus;\nimport com.geshanzsq.common.core.exception.BaseException;\nimport com.geshanzsq.common.core.exception.ParamException;\nimport com.geshanzsq.common.core.util.message.MessageUtils;\nimport com.geshanzsq.common.core.util.string.StrUtils;\nimport com.geshanzsq.common.framework.mybatis.page.vo.PageVO;\nimport com.geshanzsq.common.framework.web.service.impl.BaseServiceImpl;\nimport com.geshanzsq.framework.security.constant.SecurityConstant;\nimport com.geshanzsq.framework.security.domain.LoginUserDetail;\nimport com.geshanzsq.framework.security.util.SecurityUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\nimport org.springframework.util.CollectionUtils;\n\nimport java.util.ArrayList;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.stream.Collectors;\n\n/**\n * 评论\n *\n * @author geshanzsq\n * @date 2022/11/29\n */\n@Service\npublic class NavCommentServiceImpl extends BaseServiceImpl<NavCommentMapper, NavComment> implements NavCommentService {\n\n    @Autowired\n    private NavCommentMapper navCommentMapper;\n    @Autowired\n    private SysUserRoleMapper sysUserRoleMapper;\n\n    @Autowired\n    private SysUserService sysUserService;\n    @Autowired\n    private SysParamService sysParamService;\n\n\n    /**\n     * 分页列表\n     */\n    @Override\n    public PageVO<NavCommentVO> pageList(NavCommentPageDTO pageDTO) {\n        PageVO<NavCommentVO> pageVO = NavCommentConverter.INSTANCE.convert(super.page(pageDTO));\n        List<NavCommentVO> list = pageVO.getList();\n        if (CollectionUtils.isEmpty(list)) {\n            return pageVO;\n        }\n        // 获取对应的评论人信息\n        List<Long> userIds = list.parallelStream().filter(c -> c.getCreateUserId() != null)\n                .map(c -> c.getCreateUserId()).collect(Collectors.toList());\n        List<SysUserVO> userList = sysUserService.getUsernameAndNickNameByUserIds(userIds);\n        // 构造用户信息\n        list.forEach(comment -> {\n            SysUserVO user = userList.parallelStream().filter(u -> u.getId().equals(comment.getCreateUserId()))\n                    .findFirst().orElse(null);\n            if (user != null) {\n                comment.setNickName(user.getNickName());\n                comment.setUsername(user.getUsername());\n            }\n        });\n\n        return pageVO;\n    }\n\n    /**\n     * 通过\n     */\n    @Override\n    public void pass(Long[] ids) {\n        if (ids.length == 0) {\n            return;\n        }\n        NavComment navComment = new NavComment();\n        navComment.setStatus(NavCommentStatus.PASS.code);\n        LambdaUpdateWrapper<NavComment> wrapper = new LambdaUpdateWrapper<>();\n        wrapper.in(NavComment::getId, ids);\n        wrapper.ne(NavComment::getStatus, NavCommentStatus.PASS.code);\n        navCommentMapper.update(navComment, wrapper);\n    }\n\n    /**\n     * 驳回\n     */\n    @Override\n    public void reject(NavCommentRejectDTO rejectDTO) {\n        if (CollectionUtils.isEmpty(rejectDTO.getIds())) {\n            return;\n        }\n        NavComment navComment = new NavComment();\n        navComment.setStatus(NavCommentStatus.REJECT.code);\n        navComment.setRemark(rejectDTO.getRemark());\n        LambdaUpdateWrapper<NavComment> wrapper = new LambdaUpdateWrapper<>();\n        wrapper.in(NavComment::getId, rejectDTO.getIds());\n        wrapper.ne(NavComment::getStatus, NavCommentStatus.REJECT.code);\n        navCommentMapper.update(navComment, wrapper);\n    }\n\n    /**\n     * 更新置顶\n     *\n     * @param id 评论 id\n     * @param hasSticky 是否置顶\n     */\n    @Override\n    public void updateSticky(Long id, Integer hasSticky) {\n        NavComment navComment = new NavComment();\n        navComment.setHasSticky(hasSticky);\n        LambdaUpdateWrapper<NavComment> wrapper = new LambdaUpdateWrapper<>();\n        wrapper.eq(NavComment::getId, id);\n        wrapper.ne(NavComment::getHasSticky, hasSticky);\n        navCommentMapper.update(navComment, wrapper);\n    }\n\n    /**\n     * 获取评论开启状态\n     */\n    @Override\n    public boolean getOpenStatus() {\n        String status = sysParamService.getParamValueByKey(NavCommentConstant.NAV_COMMENT_OPEN);\n        if (StrUtils.isNotBlank(status) && Boolean.TRUE.equals(Boolean.valueOf(status))) {\n            return true;\n        }\n        return false;\n    }\n\n    /**\n     * 是否开启不用登录就能提交评论\n     */\n    @Override\n    public boolean getNotLoginOpenStatus() {\n        String status = sysParamService.getParamValueByKey(NavCommentConstant.NAV_COMMENT_NOT_LOGIN_OPEN);\n        if (StrUtils.isNotBlank(status) && Boolean.TRUE.equals(Boolean.valueOf(status))) {\n            return true;\n        }\n        return false;\n    }\n\n    /**\n     * 树形列表\n     */\n    @Override\n    public List<ClientNavCommentTreeVO> treeClient() {\n        // 如果未开启，不查询数据\n        if (!getOpenStatus()) {\n            return new ArrayList<>();\n        }\n        LambdaQueryWrapper<NavComment> wrapper = new LambdaQueryWrapper<>();\n        wrapper.eq(NavComment::getStatus, NavCommentStatus.PASS.code);\n        wrapper.orderByAsc(NavComment::getHasSticky).orderByDesc(NavComment::getGmtCreate);\n        List<ClientNavCommentTreeVO> list = NavCommentConverter.INSTANCE.convertClient(navCommentMapper.selectList(wrapper));\n\n        // 获取对应的评论人信息\n        List<Long> userIds = list.parallelStream().filter(c -> c.getCreateUserId() != null)\n                .map(c -> c.getCreateUserId()).collect(Collectors.toList());\n        Map<Long, SysUserVO> userMap = sysUserService.getUsernameAndNickNameByUserIds(userIds)\n                .stream().collect(Collectors.toMap(SysUserVO::getId, u -> u));\n\n        // 获取超级管理员用户，用于判断是否为站长\n        List<Long> superUserIds = sysUserRoleMapper.selectUserIdByRoleCode(SecurityConstant.SUPER_ADMIN_ROLE_CODE);\n\n        // 构造用户信息\n        list.forEach(comment -> {\n            SysUserVO user = userMap.get(comment.getCreateUserId());\n            if (user != null) {\n                comment.setNickName(user.getNickName());\n            }\n            comment.setWebMaster((superUserIds.stream().filter(superUserId -> superUserId.equals(comment.getCreateUserId()))\n                    .findFirst().orElse(null)) != null);\n\n        });\n\n        // 构造树形结构\n        return buildTree(list, 0L);\n    }\n\n    /**\n     * 新增\n     */\n    @Override\n    public void add(ClientNavCommentAddDTO addDTO) {\n        NavComment navComment = NavCommentConverter.INSTANCE.convert(addDTO);\n        try {\n            LoginUserDetail loginUser = SecurityUtils.getLoginUser();\n            navComment.setCreateUserId(loginUser.getUserId());\n            navComment.setNickName(\"\");\n            navComment.setEmail(\"\");\n            // 如果为超级管理员，则默认通过，否则为审核中\n            Integer status = loginUser.getRoleCodes().contains(SecurityConstant.SUPER_ADMIN_ROLE_CODE)\n                    ? NavCommentStatus.PASS.code : NavCommentStatus.AUDIT.code;\n            navComment.setStatus(status);\n        } catch (Exception e) {\n            // 是否开启不用登录就能提交评论\n            boolean notLoginOpenStatus = getNotLoginOpenStatus();\n            if (!notLoginOpenStatus) {\n                throw new BaseException(HttpStatus.UNAUTHORIZED, MessageUtils.message(\"security.not.login\"));\n            }\n            // 校验参数\n            if (StrUtils.isBlank(addDTO.getEmail())) {\n                throw new ParamException(MessageUtils.message(\"nav.comment.email.not.null\"));\n            }\n            if (StrUtils.isBlank(addDTO.getNickName())) {\n                throw new ParamException(MessageUtils.message(\"nav.comment.nick.name.not.null\"));\n            }\n        }\n        navCommentMapper.insert(navComment);\n    }\n\n    /**\n     * 根据父节点的 id 获取所有子节点\n     *\n     * @param allList 所有数据\n     * @param parentId 传入的父节点 id\n     */\n    public List<ClientNavCommentTreeVO> buildTree(List<ClientNavCommentTreeVO> allList, Long parentId) {\n        List<ClientNavCommentTreeVO> returnList = new ArrayList<>();\n        for (ClientNavCommentTreeVO comment : allList) {\n            // 根据传入的某个父节点 id，遍历该父节点的所有子节点\n            if (parentId != null && parentId.equals(comment.getParentId())) {\n                // 递归\n                recursive(allList, comment);\n                returnList.add(comment);\n            }\n        }\n        return returnList;\n    }\n\n    /**\n     * 递归列表\n     *\n     * @param allList\n     * @param comment 当前评论\n     */\n    private void recursive(List<ClientNavCommentTreeVO> allList, ClientNavCommentTreeVO comment) {\n        // 得到子节点列表\n        List<ClientNavCommentTreeVO> childList = getChildList(allList, comment);\n        comment.setChildren(childList);\n        for (ClientNavCommentTreeVO tChild : childList) {\n            if (hasChild(allList, tChild)) {\n                recursive(allList, tChild);\n            }\n        }\n    }\n\n    /**\n     * 判断是否有子节点\n     */\n    private boolean hasChild(List<ClientNavCommentTreeVO> allList, ClientNavCommentTreeVO comment) {\n        return getChildList(allList, comment).size() > 0;\n    }\n\n    /**\n     * 得到子节点列表\n     */\n    private List<ClientNavCommentTreeVO> getChildList(List<ClientNavCommentTreeVO> allList, ClientNavCommentTreeVO comment) {\n        List<ClientNavCommentTreeVO> childList = new ArrayList<>();\n        Iterator<ClientNavCommentTreeVO> it = allList.iterator();\n        while (it.hasNext()) {\n            ClientNavCommentTreeVO child = it.next();\n            if (comment.getId().equals(child.getParentId())) {\n                childList.add(child);\n            }\n        }\n        return childList;\n    }\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/nav/comment/vo/NavCommentVO.java",
    "content": "package com.geshanzsq.admin.nav.comment.vo;\n\nimport com.geshanzsq.common.core.web.vo.BaseVO;\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\nimport java.io.Serializable;\n\n/**\n * 评论\n *\n * @author geshanzsq\n * @date 2022/11/29\n */\n@Data\n@ApiModel(\"评论\")\npublic class NavCommentVO extends BaseVO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(\"评论 id\")\n    private Long id;\n\n    @ApiModelProperty(\"上级 id\")\n    private Long parentId;\n\n    @ApiModelProperty(\"是否置顶（1 是，2 否）\")\n    private Integer hasSticky;\n\n    @ApiModelProperty(\"评论内容\")\n    private String commentContent;\n\n    @ApiModelProperty(\"昵称\")\n    private String nickName;\n\n    @ApiModelProperty(\"邮箱\")\n    private String email;\n\n    @ApiModelProperty(\"备注\")\n    private String remark;\n\n    @ApiModelProperty(\"状态（1 待审核，2 通过，3 驳回）\")\n    private Integer status;\n\n    @ApiModelProperty(\"用户名\")\n    private String username;\n\n}"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/nav/config/controller/NavSiteConfigController.java",
    "content": "package com.geshanzsq.admin.nav.config.controller;\n\nimport com.geshanzsq.admin.nav.config.dto.NavSiteConfigUpdateDTO;\nimport com.geshanzsq.admin.nav.config.mapstruct.NavSiteConfigConverter;\nimport com.geshanzsq.admin.nav.config.po.NavSiteConfig;\nimport com.geshanzsq.admin.nav.config.service.NavSiteConfigService;\nimport com.geshanzsq.admin.nav.config.vo.NavSiteConfigAboutVO;\nimport com.geshanzsq.admin.nav.config.vo.NavSiteConfigVO;\nimport com.geshanzsq.common.core.web.response.ResponseResult;\nimport com.geshanzsq.common.log.annotation.Log;\nimport com.geshanzsq.common.log.enums.BusinessType;\nimport io.swagger.annotations.Api;\nimport io.swagger.annotations.ApiOperation;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.validation.Valid;\n\n/**\n * 网站配置\n *\n * @author geshanzsq\n * @date 2022/12/11\n */\n@Api(tags = \"网站配置\")\n@RestController\n@RequestMapping(\"/nav/config\")\npublic class NavSiteConfigController {\n\n    @Autowired\n    private NavSiteConfigService navSiteConfigService;\n\n    @GetMapping(\"/getConfig\")\n    @ApiOperation(\"获取配置\")\n    @PreAuthorize(\"@auth.hasUrl()\")\n    public ResponseResult<NavSiteConfigVO> getConfig() {\n        NavSiteConfigVO navSiteConfigVO = navSiteConfigService.getConfig();\n        return ResponseResult.success(navSiteConfigVO);\n    }\n\n    @PutMapping\n    @ApiOperation(\"修改\")\n    @PreAuthorize(\"@auth.hasUrl()\")\n    @Log(moduleName = \"网站配置\", businessType = BusinessType.UPDATE)\n    public ResponseResult<Long> update(@Valid @RequestBody NavSiteConfigUpdateDTO updateDTO) {\n        navSiteConfigService.updateById(updateDTO);\n        return ResponseResult.success(updateDTO.getId());\n    }\n\n    @ApiOperation(\"关于本站\")\n    @GetMapping(\"/about\")\n    public ResponseResult<NavSiteConfigAboutVO> about() {\n        NavSiteConfigAboutVO navSiteConfigAboutVO = navSiteConfigService.about();\n        return ResponseResult.success(navSiteConfigAboutVO);\n    }\n\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/nav/config/dto/NavSiteConfigUpdateDTO.java",
    "content": "package com.geshanzsq.admin.nav.config.dto;\n\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\nimport java.io.Serializable;\n\n/**\n * 网站配置更新\n *\n * @author geshanzsq\n * @date 2022/12/11\n */\n@Data\n@ApiModel(\"网站配置更新\")\npublic class NavSiteConfigUpdateDTO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(value = \"配置 id\")\n    private Long id;\n\n    @ApiModelProperty(\"关于本站描述\")\n    private String aboutSiteDescription;\n\n    @ApiModelProperty(\"关于本站邮箱\")\n    private String aboutSiteEmail;\n\n    @ApiModelProperty(\"关于本站内容\")\n    private String aboutSiteContent;\n\n}"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/nav/config/mapper/NavSiteConfigMapper.java",
    "content": "package com.geshanzsq.admin.nav.config.mapper;\n\nimport com.geshanzsq.admin.nav.config.po.NavSiteConfig;\nimport com.geshanzsq.common.framework.web.mapper.BaseMapperPlus;\nimport org.apache.ibatis.annotations.Mapper;\n\n/**\n * 网站配置\n *\n * @author geshanzsq\n * @date 2022/12/11\n */\n@Mapper\npublic interface NavSiteConfigMapper extends BaseMapperPlus<NavSiteConfig> {\n\n    /**\n     * 更新访问量\n     */\n    void updateVisitCount(Long id);\n}"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/nav/config/mapstruct/NavSiteConfigConverter.java",
    "content": "package com.geshanzsq.admin.nav.config.mapstruct;\n\nimport com.geshanzsq.admin.nav.config.dto.NavSiteConfigUpdateDTO;\nimport com.geshanzsq.admin.nav.config.po.NavSiteConfig;\nimport com.geshanzsq.admin.nav.config.vo.NavSiteConfigAboutVO;\nimport com.geshanzsq.admin.nav.config.vo.NavSiteConfigVO;\nimport com.geshanzsq.common.framework.mybatis.page.vo.PageVO;\nimport org.mapstruct.Mapper;\nimport org.mapstruct.factory.Mappers;\n\n/**\n * 网站配置对象转换\n *\n * @author geshanzsq\n * @date 2022/12/11\n */\n@Mapper\npublic interface NavSiteConfigConverter {\n\n    NavSiteConfigConverter INSTANCE = Mappers.getMapper(NavSiteConfigConverter.class);\n\n    PageVO<NavSiteConfigVO> convert(PageVO<NavSiteConfig> pageVO);\n\n    NavSiteConfigVO convert(NavSiteConfig navSiteConfig);\n\n    NavSiteConfig convert(NavSiteConfigUpdateDTO updateDTO);\n\n    NavSiteConfigAboutVO convertAbout(NavSiteConfig navSiteConfig);\n\n}"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/nav/config/po/NavSiteConfig.java",
    "content": "package com.geshanzsq.admin.nav.config.po;\n\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport lombok.Data;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\n/**\n * 网站配置\n *\n * @author geshanzsq\n * @date 2022/12/11\n */\n@Data\npublic class NavSiteConfig implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * 配置 id\n    */\n    @TableId\n    private Long id;\n\n    /**\n     * 关于本站描述\n    */\n    private String aboutSiteDescription;\n\n    /**\n     * 关于本站邮箱\n    */\n    private String aboutSiteEmail;\n\n    /**\n     * 关于本站内容\n    */\n    private String aboutSiteContent;\n\n    /**\n     * 关于本站访问量\n    */\n    private Integer aboutSiteVisitCount;\n\n    /**\n     * 创建人用户 id\n    */\n    @TableField(\"fk_create_user_id\")\n    private Long createUserId;\n\n    /**\n     * 创建时间\n    */\n    private Date gmtCreate;\n\n    /**\n     * 修改人用户 id\n    */\n    @TableField(\"fk_modify_user_id\")\n    private Long modifyUserId;\n\n    /**\n     * 修改时间\n    */\n    private Date gmtModify;\n\n}"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/nav/config/service/NavSiteConfigService.java",
    "content": "package com.geshanzsq.admin.nav.config.service;\n\nimport com.geshanzsq.admin.nav.config.dto.NavSiteConfigUpdateDTO;\nimport com.geshanzsq.admin.nav.config.po.NavSiteConfig;\nimport com.geshanzsq.admin.nav.config.vo.NavSiteConfigAboutVO;\nimport com.geshanzsq.admin.nav.config.vo.NavSiteConfigVO;\nimport com.geshanzsq.common.framework.web.service.BaseService;\n\n/**\n * 网站配置\n *\n * @author geshanzsq\n * @date 2022/12/11\n */\npublic interface NavSiteConfigService extends BaseService<NavSiteConfig> {\n\n\n    /**\n     * 获取配置\n     */\n    NavSiteConfigVO getConfig();\n\n    /**\n     * 更新\n     */\n    void updateById(NavSiteConfigUpdateDTO updateDTO);\n\n\n    /**\n     * 关于本站\n     */\n    NavSiteConfigAboutVO about();\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/nav/config/service/impl/NavSiteConfigServiceImpl.java",
    "content": "package com.geshanzsq.admin.nav.config.service.impl;\n\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.geshanzsq.admin.nav.config.dto.NavSiteConfigUpdateDTO;\nimport com.geshanzsq.admin.nav.config.mapper.NavSiteConfigMapper;\nimport com.geshanzsq.admin.nav.config.mapstruct.NavSiteConfigConverter;\nimport com.geshanzsq.admin.nav.config.po.NavSiteConfig;\nimport com.geshanzsq.admin.nav.config.service.NavSiteConfigService;\nimport com.geshanzsq.admin.nav.config.vo.NavSiteConfigAboutVO;\nimport com.geshanzsq.admin.nav.config.vo.NavSiteConfigVO;\nimport com.geshanzsq.admin.system.user.mapper.SysUserMapper;\nimport com.geshanzsq.admin.system.user.po.SysUser;\nimport com.geshanzsq.common.framework.manager.AsyncManager;\nimport com.geshanzsq.common.framework.web.service.impl.BaseServiceImpl;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\n/**\n * 网站配置\n *\n * @author geshanzsq\n * @date 2022/12/11\n */\n@Service\npublic class NavSiteConfigServiceImpl extends BaseServiceImpl<NavSiteConfigMapper, NavSiteConfig> implements NavSiteConfigService {\n\n    @Autowired\n    private NavSiteConfigMapper navSiteConfigMapper;\n    @Autowired\n    private SysUserMapper sysUserMapper;\n\n\n    /**\n     * 获取配置\n     */\n    @Override\n    public NavSiteConfigVO getConfig() {\n        LambdaQueryWrapper<NavSiteConfig> wrapper = new LambdaQueryWrapper<>();\n        wrapper.last(\" limit 1\");\n        NavSiteConfig navSiteConfig = navSiteConfigMapper.selectOne(wrapper);\n        return NavSiteConfigConverter.INSTANCE.convert(navSiteConfig);\n    }\n\n    /**\n     * 更新\n     */\n    @Override\n    public void updateById(NavSiteConfigUpdateDTO updateDTO) {\n        NavSiteConfig navSiteConfig = NavSiteConfigConverter.INSTANCE.convert(updateDTO);\n        if (navSiteConfig.getId() == null) {\n            navSiteConfigMapper.insert(navSiteConfig);\n        } else {\n            navSiteConfigMapper.updateById(navSiteConfig);\n        }\n        updateDTO.setId(navSiteConfig.getId());\n    }\n\n    /**\n     * 关于本站\n     */\n    @Override\n    public NavSiteConfigAboutVO about() {\n        LambdaQueryWrapper<NavSiteConfig> wrapper = new LambdaQueryWrapper<>();\n        wrapper.last(\" limit 1\");\n        NavSiteConfig navSiteConfig = navSiteConfigMapper.selectOne(wrapper);\n        if (navSiteConfig == null) {\n            return null;\n        }\n\n        NavSiteConfigAboutVO navSiteConfigAboutVO = NavSiteConfigConverter.INSTANCE.convertAbout(navSiteConfig);\n\n        // 获取作者\n        LambdaQueryWrapper<SysUser> userWrapper = new LambdaQueryWrapper<>();\n        userWrapper.eq(SysUser::getId, navSiteConfig.getCreateUserId());\n        userWrapper.select(SysUser::getNickName, SysUser::getAvatar);\n        SysUser sysUser = sysUserMapper.selectOne(userWrapper);\n        if (sysUser != null) {\n            navSiteConfigAboutVO.setNickName(sysUser.getNickName());\n            navSiteConfigAboutVO.setAvatar(sysUser.getAvatar());\n        }\n\n        // 更新访问量\n        AsyncManager.me().execute(() -> navSiteConfigMapper.updateVisitCount(navSiteConfig.getId()));\n\n        return navSiteConfigAboutVO;\n    }\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/nav/config/vo/NavSiteConfigAboutVO.java",
    "content": "package com.geshanzsq.admin.nav.config.vo;\n\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\nimport java.io.Serializable;\n\n/**\n * 关于本站\n *\n * @author geshanzsq\n * @date 2023/4/13\n */\n@Data\n@ApiModel(\"关于本站\")\npublic class NavSiteConfigAboutVO  implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(\"关于本站描述\")\n    private String aboutSiteDescription;\n\n    @ApiModelProperty(\"关于本站邮箱\")\n    private String aboutSiteEmail;\n\n    @ApiModelProperty(\"关于本站内容\")\n    private String aboutSiteContent;\n\n    @ApiModelProperty(\"站长昵称\")\n    private String nickName;\n\n    @ApiModelProperty(\"站长头像\")\n    private String avatar;\n\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/nav/config/vo/NavSiteConfigVO.java",
    "content": "package com.geshanzsq.admin.nav.config.vo;\n\nimport com.geshanzsq.common.core.web.vo.BaseVO;\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\nimport java.io.Serializable;\n\n/**\n * 网站配置\n *\n * @author geshanzsq\n * @date 2022/12/11\n */\n@Data\n@ApiModel(\"网站配置\")\npublic class NavSiteConfigVO extends BaseVO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(\"配置 id\")\n    private Long id;\n\n    @ApiModelProperty(\"关于本站描述\")\n    private String aboutSiteDescription;\n\n    @ApiModelProperty(\"关于本站邮箱\")\n    private String aboutSiteEmail;\n\n    @ApiModelProperty(\"关于本站内容\")\n    private String aboutSiteContent;\n\n    @ApiModelProperty(\"关于本站访问量\")\n    private Integer aboutSiteVisitCount;\n\n}"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/nav/index/controller/NavIndexController.java",
    "content": "package com.geshanzsq.admin.nav.index.controller;\n\nimport com.geshanzsq.admin.nav.index.service.NavIndexService;\nimport com.geshanzsq.admin.nav.index.vo.NavIndexStatisticsVO;\nimport com.geshanzsq.common.core.web.response.ResponseResult;\nimport io.swagger.annotations.Api;\nimport io.swagger.annotations.ApiOperation;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * 后台首页展示\n *\n * @author geshanzsq\n * @date 2023/5/2\n */\n@RestController\n@RequestMapping(\"/nav/index\")\n@Api(tags = \"后台首页\")\npublic class NavIndexController {\n\n    @Autowired\n    private NavIndexService navIndexService;\n\n    @ApiOperation(\"获取统计数\")\n    @GetMapping(\"/getStatistics\")\n    public ResponseResult<NavIndexStatisticsVO> getStatistics() {\n        NavIndexStatisticsVO statisticsVO = navIndexService.getStatistics();\n        return ResponseResult.success(statisticsVO);\n    }\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/nav/index/service/NavIndexService.java",
    "content": "package com.geshanzsq.admin.nav.index.service;\n\nimport com.geshanzsq.admin.nav.index.vo.NavIndexStatisticsVO;\n\n/**\n * 后台首页\n *\n * @author geshanzsq\n * @date 2023/5/2\n */\npublic interface NavIndexService {\n\n    /**\n     * 获取统计数\n     */\n    NavIndexStatisticsVO getStatistics();\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/nav/index/service/impl/NavIndexServiceImpl.java",
    "content": "package com.geshanzsq.admin.nav.index.service.impl;\n\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.geshanzsq.admin.nav.category.mapper.NavCategoryMapper;\nimport com.geshanzsq.admin.nav.index.service.NavIndexService;\nimport com.geshanzsq.admin.nav.index.vo.NavIndexStatisticsVO;\nimport com.geshanzsq.admin.nav.site.mapper.NavSiteMapper;\nimport com.geshanzsq.admin.system.user.mapper.SysUserMapper;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\n/**\n * 后台首页\n *\n * @author geshanzsq\n * @date 2023/5/2\n */\n@Service\npublic class NavIndexServiceImpl implements NavIndexService {\n\n    @Autowired\n    private NavCategoryMapper navCategoryMapper;\n    @Autowired\n    private NavSiteMapper navSiteMapper;\n\n    /**\n     * 获取统计数\n     */\n    @Override\n    public NavIndexStatisticsVO getStatistics() {\n        NavIndexStatisticsVO statisticsVO = new NavIndexStatisticsVO();\n\n        // 获取分类数\n        statisticsVO.setCategoryCount(navCategoryMapper.selectCount(new LambdaQueryWrapper<>()));\n        // 获取网站数\n        statisticsVO.setSiteCount(navSiteMapper.selectCount(new LambdaQueryWrapper<>()));\n        // 获取网站点击量信息\n        statisticsVO.setSiteClickCount(navSiteMapper.selectClickCount());\n\n        return statisticsVO;\n    }\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/nav/index/vo/NavIndexStatisticsVO.java",
    "content": "package com.geshanzsq.admin.nav.index.vo;\n\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\nimport java.io.Serializable;\n\n/**\n * 首页统计\n *\n * @author geshanzsq\n * @date 2023/5/2\n */\n@Data\n@ApiModel(\"首页统计\")\npublic class NavIndexStatisticsVO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(\"分类数\")\n    private Long categoryCount;\n\n    @ApiModelProperty(\"网站数\")\n    private Long siteCount;\n\n    @ApiModelProperty(\"网站点击数\")\n    private Long siteClickCount;\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/nav/picture/constant/NavPictureConstant.java",
    "content": "package com.geshanzsq.admin.nav.picture.constant;\n\n/**\n * 导航图片\n *\n * @author geshanzsq\n * @date 2022/12/11\n */\npublic class NavPictureConstant {\n\n    /**\n     * 系统导航网站图片前缀\n     */\n    public final static String SYSTEM_SITE_PREFIX = \"site/system\";\n\n    /**\n     * 网站配置图片前缀\n     */\n    public final static String SITE_CONFIG_PREFIX = \"config\";\n\n    /**\n     * 用户头像图片前缀\n     */\n    public final static String AVATAR_PREFIX = \"avatar\";\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/nav/picture/controller/NavPictureController.java",
    "content": "package com.geshanzsq.admin.nav.picture.controller;\n\nimport com.geshanzsq.admin.nav.picture.constant.NavPictureConstant;\nimport com.geshanzsq.admin.nav.picture.po.NavPicture;\nimport com.geshanzsq.admin.nav.picture.service.NavPictureService;\nimport com.geshanzsq.admin.nav.picture.vo.NavPictureUploadVO;\nimport com.geshanzsq.admin.system.user.service.SysUserService;\nimport com.geshanzsq.common.core.web.response.ResponseResult;\nimport com.geshanzsq.common.framework.web.controller.BaseController;\nimport com.geshanzsq.common.log.annotation.Log;\nimport com.geshanzsq.common.log.enums.BusinessType;\nimport com.geshanzsq.framework.security.util.SecurityUtils;\nimport io.swagger.annotations.Api;\nimport io.swagger.annotations.ApiOperation;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.multipart.MultipartFile;\n\n/**\n * 导航图片\n *\n * @author geshanzsq\n * @date 2022/12/11\n */\n@Api(tags = \"导航图片\")\n@RestController\n@RequestMapping(\"/nav/picture\")\npublic class NavPictureController extends BaseController {\n\n    @Autowired\n    private NavPictureService navPictureService;\n    @Autowired\n    private SysUserService sysUserService;\n\n    @ApiOperation(\"上传\")\n    @PostMapping(\"/upload\")\n    @PreAuthorize(\"@auth.hasUrl()\")\n    public ResponseResult<NavPictureUploadVO> upload(MultipartFile file) {\n        NavPicture navPicture = navPictureService.getUploadFilePath(NavPictureConstant.SITE_CONFIG_PREFIX, file);\n        return ResponseResult.success(new NavPictureUploadVO(navPicture.getPictureOriginalName(), navPicture.getPicturePath()));\n    }\n\n    @ApiOperation(\"系统网站上传\")\n    @PostMapping(\"/site/upload\")\n    @PreAuthorize(\"@auth.hasUrl()\")\n    @Log(moduleName = \"导航图片-系统网站上传\", businessType = BusinessType.ADD, isSaveRequestData = false)\n    public ResponseResult<NavPictureUploadVO> uploadSite(MultipartFile file) {\n        NavPicture navPicture = navPictureService.getUploadFilePath(NavPictureConstant.SYSTEM_SITE_PREFIX, file);\n        return ResponseResult.success(new NavPictureUploadVO(navPicture.getPictureOriginalName(), navPicture.getPicturePath()));\n    }\n\n    @ApiOperation(\"用户头像上传\")\n    @PostMapping(\"/upload/avatar\")\n    @Log(moduleName = \"导航图片-用户头像上传\", businessType = BusinessType.ADD, isSaveRequestData = false)\n    public ResponseResult<NavPictureUploadVO> uploadAvatar(MultipartFile file) {\n        NavPicture navPicture = navPictureService.getUploadFilePath(NavPictureConstant.AVATAR_PREFIX, file);\n        sysUserService.updateAvatarById(SecurityUtils.getUserId(), navPicture.getPicturePath());\n        return ResponseResult.success(new NavPictureUploadVO(navPicture.getPictureOriginalName(), navPicture.getPicturePath()));\n    }\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/nav/picture/mapper/NavPictureMapper.java",
    "content": "package com.geshanzsq.admin.nav.picture.mapper;\n\nimport com.geshanzsq.admin.nav.picture.po.NavPicture;\nimport com.geshanzsq.common.framework.web.mapper.BaseMapperPlus;\nimport org.apache.ibatis.annotations.Mapper;\n\n/**\n * 导航图片\n *\n * @author geshanzsq\n * @date 2022/11/26\n */\n@Mapper\npublic interface NavPictureMapper extends BaseMapperPlus<NavPicture> {\n\n\n}"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/nav/picture/mapstrcut/NavPictureConverter.java",
    "content": "package com.geshanzsq.admin.nav.picture.mapstrcut;\n\nimport org.mapstruct.Mapper;\nimport org.mapstruct.factory.Mappers;\n\n/**\n * 导航图片对象转换\n *\n * @author geshanzsq\n * @date 2022/11/26\n */\n@Mapper\npublic interface NavPictureConverter {\n\n    NavPictureConverter INSTANCE = Mappers.getMapper(NavPictureConverter.class);\n\n}"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/nav/picture/po/NavPicture.java",
    "content": "package com.geshanzsq.admin.nav.picture.po;\n\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport lombok.Data;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\n/**\n * 导航图片\n *\n * @author geshanzsq\n * @date 2022/11/26\n */\n@Data\npublic class NavPicture implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * 图片 id\n    */\n    @TableId\n    private Long id;\n\n    /**\n     * 图片原名称\n    */\n    private String pictureOriginalName;\n\n    /**\n     * 图片路径\n    */\n    private String picturePath;\n\n    /**\n     * 图片 md5\n    */\n    private String pictureMd5;\n\n    /**\n     * 创建人用户 id\n    */\n    @TableField(\"fk_create_user_id\")\n    private Long createUserId;\n\n    /**\n     * 创建时间\n    */\n    private Date gmtCreate;\n\n    /**\n     * 修改人用户 id\n    */\n    @TableField(\"fk_modify_user_id\")\n    private Long modifyUserId;\n\n    /**\n     * 修改时间\n    */\n    private Date gmtModify;\n\n}"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/nav/picture/service/NavPictureService.java",
    "content": "package com.geshanzsq.admin.nav.picture.service;\n\nimport com.geshanzsq.admin.nav.picture.po.NavPicture;\nimport com.geshanzsq.common.framework.web.service.BaseService;\nimport org.springframework.web.multipart.MultipartFile;\n\n/**\n * 导航图片\n *\n * @author geshanzsq\n * @date 2022/11/26\n */\npublic interface NavPictureService extends BaseService<NavPicture> {\n\n    /**\n     * 获取图片上传路径\n     *\n     * @param modulePath 模块路径\n     * @param file 文件\n     */\n    NavPicture getUploadFilePath(String modulePath, MultipartFile file);\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/nav/picture/service/impl/NavPictureServiceImpl.java",
    "content": "package com.geshanzsq.admin.nav.picture.service.impl;\n\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.geshanzsq.admin.nav.picture.mapper.NavPictureMapper;\nimport com.geshanzsq.admin.nav.picture.po.NavPicture;\nimport com.geshanzsq.admin.nav.picture.service.NavPictureService;\nimport com.geshanzsq.common.framework.file.service.FileService;\nimport com.geshanzsq.common.framework.file.util.FileUploadUtils;\nimport com.geshanzsq.common.framework.web.service.impl.BaseServiceImpl;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\nimport org.springframework.util.CollectionUtils;\nimport org.springframework.web.multipart.MultipartFile;\n\nimport java.io.IOException;\nimport java.util.List;\n\n/**\n * 导航图片\n *\n * @author geshanzsq\n * @date 2022/11/26\n */\n@Service\npublic class NavPictureServiceImpl extends BaseServiceImpl<NavPictureMapper, NavPicture> implements NavPictureService {\n\n    @Autowired\n    private NavPictureMapper navPictureMapper;\n\n    @Autowired\n    private FileService fileService;\n\n\n    /**\n     * 获取图片上传路径\n     *\n     * @param modulePath 模块路径\n     * @param file 文件\n     * @return\n     * @throws IOException\n     */\n    @Override\n    public NavPicture getUploadFilePath(String modulePath, MultipartFile file) {\n        // 获取文件的 md5\n        String fileMd5 = FileUploadUtils.getFileMd5(file);\n        // 判断数据库是否存在此文件，如果存在，则直接返回，否则上传文件\n        LambdaQueryWrapper<NavPicture> wrapper = new LambdaQueryWrapper<>();\n        wrapper.eq(NavPicture::getPictureMd5, fileMd5);\n        wrapper.select(NavPicture::getPicturePath);\n        List<NavPicture> pictureList = navPictureMapper.selectList(wrapper);\n        if (!CollectionUtils.isEmpty(pictureList)) {\n            return pictureList.get(0);\n        }\n\n        // 上传文件\n        String filePath = fileService.uploadPicture(modulePath, file);\n\n        // 图片信息插入数据库\n        NavPicture navPicture = new NavPicture();\n        navPicture.setPictureOriginalName(file.getOriginalFilename());\n        navPicture.setPicturePath(filePath);\n        navPicture.setPictureMd5(fileMd5);\n        navPictureMapper.insert(navPicture);\n\n        return navPicture;\n    }\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/nav/picture/vo/NavPictureUploadVO.java",
    "content": "package com.geshanzsq.admin.nav.picture.vo;\n\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.io.Serializable;\n\n/**\n * 图片上传\n *\n * @author geshanzsq\n * @date 2022/12/11\n */\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\n@ApiModel(\"图片上传\")\npublic class NavPictureUploadVO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(\"文件名称\")\n    private String fileName;\n\n    @ApiModelProperty(\"图片路径\")\n    private String filePath;\n\n}"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/nav/site/constant/NavSiteClientConstant.java",
    "content": "package com.geshanzsq.admin.nav.site.constant;\n\n/**\n * 导航网站常量\n *\n * @author geshanzsq\n * @date 2023/1/7\n */\npublic class NavSiteClientConstant {\n\n    /**\n     * 客户端缓存前缀\n     */\n    public static final String CLIENT_CACHE_PREFIX = \"nav:client:site\";\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/nav/site/controller/NavSiteController.java",
    "content": "package com.geshanzsq.admin.nav.site.controller;\n\nimport com.geshanzsq.admin.nav.site.dto.NavSiteAddDTO;\nimport com.geshanzsq.admin.nav.site.dto.NavSitePageDTO;\nimport com.geshanzsq.admin.nav.site.dto.NavSiteUpdateDTO;\nimport com.geshanzsq.admin.nav.site.mapstruct.NavSiteConverter;\nimport com.geshanzsq.admin.nav.site.service.NavSiteService;\nimport com.geshanzsq.admin.nav.site.vo.NavSiteVO;\nimport com.geshanzsq.common.core.web.response.ResponseResult;\nimport com.geshanzsq.common.framework.mybatis.page.vo.PageVO;\nimport com.geshanzsq.common.log.annotation.Log;\nimport com.geshanzsq.common.log.enums.BusinessType;\nimport io.swagger.annotations.Api;\nimport io.swagger.annotations.ApiOperation;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.validation.Valid;\n\n/**\n * 导航网站\n *\n * @author geshanzsq\n * @date 2022/11/20\n */\n@Api(tags = \"导航网站\")\n@RestController\n@RequestMapping(\"/nav/site\")\npublic class NavSiteController {\n\n    @Autowired\n    private NavSiteService navSiteService;\n\n    @ApiOperation(\"分页列表\")\n    @GetMapping(\"/page\")\n    @PreAuthorize(\"@auth.hasUrl()\")\n    public ResponseResult<PageVO<NavSiteVO>> page(NavSitePageDTO pageDTO) {\n        PageVO<NavSiteVO> pageVO = navSiteService.pageList(pageDTO);\n        return ResponseResult.success(pageVO);\n    }\n\n    @GetMapping(\"/getById/{id}\")\n    @ApiOperation(\"详情\")\n    @PreAuthorize(\"@auth.hasUrl()\")\n    public ResponseResult<NavSiteVO> getById(@PathVariable Long id) {\n        return ResponseResult.success(NavSiteConverter.INSTANCE.convert(navSiteService.getById(id)));\n    }\n\n    @PostMapping\n    @ApiOperation(\"新增\")\n    @PreAuthorize(\"@auth.hasUrl()\")\n    @Log(moduleName = \"导航网站\", businessType = BusinessType.ADD)\n    public ResponseResult add(@Valid @RequestBody NavSiteAddDTO addDTO) {\n        navSiteService.save(addDTO);\n        return ResponseResult.success();\n    }\n\n    @PutMapping\n    @ApiOperation(\"修改\")\n    @PreAuthorize(\"@auth.hasUrl()\")\n    @Log(moduleName = \"导航网站\", businessType = BusinessType.UPDATE)\n    public ResponseResult update(@Valid @RequestBody NavSiteUpdateDTO updateDTO) {\n        navSiteService.updateById(updateDTO);\n        return ResponseResult.success();\n    }\n\n    @DeleteMapping(\"/delete/{ids}\")\n    @ApiOperation(\"删除\")\n    @PreAuthorize(\"@auth.hasUrl()\")\n    @Log(moduleName = \"导航网站\", businessType = BusinessType.DELETE)\n    public ResponseResult delete(@PathVariable Long[] ids) {\n        navSiteService.removeByIds(ids);\n        return ResponseResult.success();\n    }\n\n    @GetMapping(\"/getMaxSortByCategoryId\")\n    @ApiOperation(\"获取最大排序\")\n    @PreAuthorize(\"@auth.hasUrl()\")\n    public ResponseResult<Integer> getMaxSortByCategoryId(Long categoryId) {\n        Integer maxSort = navSiteService.getMaxSortByCategoryId(categoryId);\n        return ResponseResult.success(maxSort);\n    }\n\n    @ApiOperation(\"更新点击量\")\n    @PutMapping(\"/updateClickCount/{id}\")\n    public ResponseResult updateClickCount(@PathVariable Long id) {\n        navSiteService.updateClickCountById(id);\n        return ResponseResult.success();\n    }\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/nav/site/dto/NavSiteAddDTO.java",
    "content": "package com.geshanzsq.admin.nav.site.dto;\n\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotBlank;\nimport javax.validation.constraints.NotNull;\nimport java.io.Serializable;\n\n/**\n * 导航网站新增\n *\n * @author geshanzsq\n * @date 2022/11/20\n */\n@Data\n@ApiModel(\"导航网站新增\")\npublic class NavSiteAddDTO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(value = \"分类 id\", required = true)\n    @NotNull(message = \"分类 id不能为空\")\n    private Long categoryId;\n\n    @ApiModelProperty(value = \"网站名称\", required = true)\n    @NotBlank(message = \"网站名称不能为空\")\n    private String siteName;\n\n    @ApiModelProperty(value = \"网站图片路径\", required = true)\n    @NotBlank(message = \"网站图片路径不能为空\")\n    private String sitePath;\n\n    @ApiModelProperty(value = \"网站描述\", required = true)\n    @NotBlank(message = \"网站描述不能为空\")\n    private String siteDescription;\n\n    @ApiModelProperty(value = \"网站地址\", required = true)\n    @NotBlank(message = \"网站地址不能为空\")\n    private String siteUrl;\n\n    @ApiModelProperty(value = \"排序\", required = true)\n    @NotNull(message = \"排序不能为空\")\n    private Integer sort;\n\n    @ApiModelProperty(\"点击量\")\n    private Integer clickCount;\n\n    @ApiModelProperty(value = \"状态（1 正常，2 停用）\", required = true)\n    @NotNull(message = \"状态（1 正常，2 停用）不能为空\")\n    private Integer status;\n\n}"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/nav/site/dto/NavSiteListUserImportDTO.java",
    "content": "package com.geshanzsq.admin.nav.site.dto;\n\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\nimport java.io.Serializable;\n\n/**\n * 用户查询列表\n *\n * @author geshanzsq\n * @date 2022/12/25\n */\n@Data\n@ApiModel(\"用户查询列表\")\npublic class NavSiteListUserImportDTO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(\"分类 id\")\n    private Long categoryId;\n\n    @ApiModelProperty(\"网站名称\")\n    private String siteName;\n\n    @ApiModelProperty(value = \"状态（1 正常，2 停用）\", hidden = true)\n    private Integer status;\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/nav/site/dto/NavSitePageDTO.java",
    "content": "package com.geshanzsq.admin.nav.site.dto;\n\nimport com.geshanzsq.common.framework.mybatis.page.dto.PageDTO;\nimport com.geshanzsq.common.framework.mybatis.plugin.annotation.Query;\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\nimport java.io.Serializable;\n\n/**\n * 导航网站分页\n *\n * @author geshanzsq\n * @date 2022/11/20\n */\n@Data\n@ApiModel(\"导航网站分页\")\npublic class NavSitePageDTO extends PageDTO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(\"分类 id\")\n    private Long categoryId;\n\n    @ApiModelProperty(\"网站名称\")\n    @Query(ignore = true)\n    private String siteNameDescription;\n\n    @ApiModelProperty(\"状态（1 正常，2 停用）\")\n    private Integer status;\n\n}"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/nav/site/dto/NavSiteUpdateDTO.java",
    "content": "package com.geshanzsq.admin.nav.site.dto;\n\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotBlank;\nimport javax.validation.constraints.NotNull;\nimport java.io.Serializable;\n\n/**\n * 导航网站更新\n *\n * @author geshanzsq\n * @date 2022/11/20\n */\n@Data\n@ApiModel(\"导航网站更新\")\npublic class NavSiteUpdateDTO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(value = \"网站 id\", required = true)\n    @NotNull(message = \"网站 id不能为空\")\n    private Long id;\n\n    @ApiModelProperty(value = \"分类 id\", required = true)\n    @NotNull(message = \"分类 id不能为空\")\n    private Long categoryId;\n\n    @ApiModelProperty(value = \"网站名称\", required = true)\n    @NotBlank(message = \"网站名称不能为空\")\n    private String siteName;\n\n    @ApiModelProperty(value = \"网站图片路径\", required = true)\n    @NotBlank(message = \"网站图片路径不能为空\")\n    private String sitePath;\n\n    @ApiModelProperty(value = \"网站描述\", required = true)\n    @NotBlank(message = \"网站描述不能为空\")\n    private String siteDescription;\n\n    @ApiModelProperty(value = \"网站地址\", required = true)\n    @NotBlank(message = \"网站地址不能为空\")\n    private String siteUrl;\n\n    @ApiModelProperty(value = \"排序\", required = true)\n    @NotNull(message = \"排序不能为空\")\n    private Integer sort;\n\n    @ApiModelProperty(value = \"状态（1 正常，2 停用）\", required = true)\n    @NotNull(message = \"状态（1 正常，2 停用）不能为空\")\n    private Integer status;\n\n}"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/nav/site/dto/NavSiteUpdateSortDTO.java",
    "content": "package com.geshanzsq.admin.nav.site.dto;\n\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\nimport java.io.Serializable;\n\n/**\n * 更新排序\n *\n * @author geshanzsq\n * @date 2022/12/25\n */\n@Data\n@ApiModel(\"更新排序\")\npublic class NavSiteUpdateSortDTO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(value = \"网站 id\", required = true)\n    @NotNull(message = \"网站 id不能为空\")\n    private Long id;\n\n    @ApiModelProperty(value = \"排序\", required = true)\n    @NotNull(message = \"排序不能为空\")\n    private Integer sort;\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/nav/site/mapper/NavSiteMapper.java",
    "content": "package com.geshanzsq.admin.nav.site.mapper;\n\nimport com.geshanzsq.admin.client.search.vo.NavClientSiteSearchVO;\nimport com.geshanzsq.admin.nav.site.dto.NavSiteListUserImportDTO;\nimport com.geshanzsq.admin.nav.site.po.NavSite;\nimport com.geshanzsq.admin.nav.site.vo.NavSiteVO;\nimport com.geshanzsq.common.framework.web.mapper.BaseMapperPlus;\nimport org.apache.ibatis.annotations.Mapper;\nimport org.apache.ibatis.annotations.Param;\n\nimport java.util.List;\n\n/**\n * 导航网站\n *\n * @author geshanzsq\n * @date 2022/11/20\n */\n@Mapper\npublic interface NavSiteMapper extends BaseMapperPlus<NavSite> {\n\n    /**\n     * 获取最大排序\n     */\n    Integer selectMaxSortByCategoryId(@Param(\"categoryId\") Long categoryId);\n\n    /**\n     * 更新点击量\n     */\n    void updateClickCountById(@Param(\"id\") Long id);\n\n    /**\n     * 查询列表\n     */\n    List<NavSiteVO> selectListByUserImport(NavSiteListUserImportDTO userImportDTO);\n\n    /**\n     * 站內网站搜索\n     */\n    List<NavClientSiteSearchVO> selectSiteSearchList(@Param(\"searchContent\") String searchContent, @Param(\"status\") Integer status);\n\n    /**\n     * 获取点击量\n     */\n    Long selectClickCount();\n}"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/nav/site/mapstrcut/NavSiteConverter.java",
    "content": "package com.geshanzsq.admin.nav.site.mapstruct;\n\nimport com.geshanzsq.admin.nav.site.dto.NavSiteAddDTO;\nimport com.geshanzsq.admin.nav.site.dto.NavSiteUpdateDTO;\nimport com.geshanzsq.admin.nav.site.dto.NavSiteUpdateSortDTO;\nimport com.geshanzsq.admin.nav.site.po.NavSite;\nimport com.geshanzsq.admin.nav.site.vo.NavSiteClientVO;\nimport com.geshanzsq.admin.nav.site.vo.NavSiteLatestCollectVO;\nimport com.geshanzsq.admin.nav.site.vo.NavSiteMatchVO;\nimport com.geshanzsq.admin.nav.site.vo.NavSiteVO;\nimport com.geshanzsq.common.framework.mybatis.page.vo.PageVO;\nimport org.mapstruct.Mapper;\nimport org.mapstruct.factory.Mappers;\n\nimport java.util.List;\n\n/**\n * 导航网站对象转换\n *\n * @author geshanzsq\n * @date 2022/11/20\n */\n@Mapper\npublic interface NavSiteConverter {\n\n    NavSiteConverter INSTANCE = Mappers.getMapper(NavSiteConverter.class);\n\n    PageVO<NavSiteVO> convert(PageVO<NavSite> pageVO);\n\n    NavSiteVO convert(NavSite navSite);\n\n    NavSite convert(NavSiteAddDTO addDTO);\n\n    NavSite convert(NavSiteUpdateDTO updateDTO);\n\n    NavSiteMatchVO convertMatch(NavSite navSite);\n\n    List<NavSite> convert(List<NavSiteUpdateSortDTO> list);\n\n    List<NavSiteClientVO> convertCilent(List<NavSite> list);\n\n    List<NavSiteLatestCollectVO> convertCollect(List<NavSite> list);\n\n}"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/nav/site/po/NavSite.java",
    "content": "package com.geshanzsq.admin.nav.site.po;\n\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport lombok.Data;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\n/**\n * 导航网站\n *\n * @author geshanzsq\n * @date 2022/11/20\n */\n@Data\npublic class NavSite implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * 网站 id\n    */\n    @TableId\n    private Long id;\n\n    /**\n     * 分类 id\n    */\n    @TableField(\"fk_category_id\")\n    private Long categoryId;\n\n    /**\n     * 网站名称\n    */\n    private String siteName;\n\n    /**\n     * 网站图片路径\n    */\n    private String sitePath;\n\n    /**\n     * 网站描述\n    */\n    private String siteDescription;\n\n    /**\n     * 网站地址\n    */\n    private String siteUrl;\n\n    /**\n     * 排序\n    */\n    private Integer sort;\n\n    /**\n     * 点击量\n    */\n    private Integer clickCount;\n\n    /**\n     * 状态（1 正常，2 停用）\n    */\n    private Integer status;\n\n    /**\n     * 创建人用户 id\n    */\n    @TableField(\"fk_create_user_id\")\n    private Long createUserId;\n\n    /**\n     * 创建时间\n    */\n    private Date gmtCreate;\n\n    /**\n     * 修改人用户 id\n    */\n    @TableField(\"fk_modify_user_id\")\n    private Long modifyUserId;\n\n    /**\n     * 修改时间\n    */\n    private Date gmtModify;\n\n}"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/nav/site/service/NavSiteService.java",
    "content": "package com.geshanzsq.admin.nav.site.service;\n\nimport com.geshanzsq.admin.client.search.vo.NavClientSiteSearchVO;\nimport com.geshanzsq.admin.nav.site.dto.*;\nimport com.geshanzsq.admin.nav.site.po.NavSite;\nimport com.geshanzsq.admin.nav.site.vo.NavSiteLatestCollectVO;\nimport com.geshanzsq.admin.nav.site.vo.NavSiteMatchVO;\nimport com.geshanzsq.admin.nav.site.vo.NavSiteVO;\nimport com.geshanzsq.common.framework.mybatis.page.vo.PageVO;\nimport com.geshanzsq.common.framework.web.service.BaseService;\n\nimport java.util.List;\nimport java.util.Set;\n\n/**\n * 导航网站\n *\n * @author geshanzsq\n * @date 2022/11/20\n */\npublic interface NavSiteService extends BaseService<NavSite> {\n\n    /**\n     * 分页列表\n     */\n    PageVO<NavSiteVO> pageList(NavSitePageDTO pageDTO);\n\n    /**\n     * 新增\n     */\n    void save(NavSiteAddDTO addDTO);\n\n    /**\n     * 修改\n     */\n    void updateById(NavSiteUpdateDTO updateDTO);\n\n    /**\n     * 删除\n     */\n    void removeByIds(Long[] ids);\n\n    /**\n     * 获取最大排序\n     */\n    Integer getMaxSortByCategoryId(Long categoryId);\n\n    /**\n     * 更新点击量\n     */\n    void updateClickCountById(Long id);\n\n\n    /**\n     * 站內网站搜索\n     */\n    List<NavClientSiteSearchVO> siteSearch(String searchContent);\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/nav/site/service/impl/NavSiteServiceImpl.java",
    "content": "package com.geshanzsq.admin.nav.site.service.impl;\n\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.geshanzsq.admin.client.nav.service.ClientNavService;\nimport com.geshanzsq.admin.client.search.vo.NavClientSiteSearchVO;\nimport com.geshanzsq.admin.nav.category.mapper.NavCategoryMapper;\nimport com.geshanzsq.admin.nav.category.po.NavCategory;\nimport com.geshanzsq.admin.nav.site.dto.NavSiteAddDTO;\nimport com.geshanzsq.admin.nav.site.dto.NavSitePageDTO;\nimport com.geshanzsq.admin.nav.site.dto.NavSiteUpdateDTO;\nimport com.geshanzsq.admin.nav.site.mapper.NavSiteMapper;\nimport com.geshanzsq.admin.nav.site.mapstruct.NavSiteConverter;\nimport com.geshanzsq.admin.nav.site.po.NavSite;\nimport com.geshanzsq.admin.nav.site.service.NavSiteService;\nimport com.geshanzsq.admin.nav.site.vo.NavSiteVO;\nimport com.geshanzsq.common.core.enums.CommonStatus;\nimport com.geshanzsq.common.core.util.string.StrUtils;\nimport com.geshanzsq.common.framework.manager.AsyncManager;\nimport com.geshanzsq.common.framework.mybatis.page.vo.PageVO;\nimport com.geshanzsq.common.framework.mybatis.plugin.query.QueryWrapperPlus;\nimport com.geshanzsq.common.framework.web.service.impl.BaseServiceImpl;\nimport org.apache.commons.lang3.StringUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\nimport org.springframework.util.CollectionUtils;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\n/**\n * 导航网站\n *\n * @author geshanzsq\n * @date 2022/11/20\n */\n@Service\npublic class NavSiteServiceImpl extends BaseServiceImpl<NavSiteMapper, NavSite> implements NavSiteService {\n\n    private final Logger log = LoggerFactory.getLogger(NavSiteServiceImpl.class);\n\n    @Autowired\n    private NavSiteMapper navSiteMapper;\n    @Autowired\n    private NavCategoryMapper navCategoryMapper;\n    @Autowired\n    private ClientNavService clientNavService;\n\n    /**\n     * 分页列表\n     */\n    @Override\n    public PageVO<NavSiteVO> pageList(NavSitePageDTO pageDTO) {\n        QueryWrapperPlus<NavSite> wrapper = buildQueryWrapper(pageDTO);\n        if (StrUtils.isNotBlank(pageDTO.getSiteNameDescription())) {\n            wrapper.lambda().and(w -> {\n                w.like(NavSite::getSiteName, pageDTO.getSiteNameDescription())\n                        .or()\n                        .like(NavSite::getSiteDescription, pageDTO.getSiteNameDescription());\n            });\n        }\n        PageVO<NavSiteVO> pageVO = NavSiteConverter.INSTANCE.convert(navSiteMapper.selectPage(pageDTO, wrapper));\n        // 获取对应的分类名称\n        buildCategoryName(pageVO.getList());\n        return pageVO;\n    }\n\n    /**\n     * 新增\n     */\n    @Override\n    public void save(NavSiteAddDTO addDTO) {\n        NavSite navSite = NavSiteConverter.INSTANCE.convert(addDTO);\n        navSiteMapper.insert(navSite);\n        // 清除缓存，等后续访问时会自动从数据库获取\n        AsyncManager.me().execute(() -> clientNavService.removeCache());\n    }\n\n    /**\n     * 修改\n     */\n    @Override\n    public void updateById(NavSiteUpdateDTO updateDTO) {\n        NavSite navSite = NavSiteConverter.INSTANCE.convert(updateDTO);\n        navSiteMapper.updateById(navSite);\n        // 清除缓存，等后续访问时会自动从数据库获取\n        AsyncManager.me().execute(() -> clientNavService.removeCache());\n    }\n\n    /**\n     * 删除\n     */\n    @Override\n    public void removeByIds(Long[] ids) {\n        super.removeByIds(Arrays.asList(ids));\n        // 清除缓存，等后续访问时会自动从数据库获取\n        AsyncManager.me().execute(() -> clientNavService.removeCache());\n    }\n\n    /**\n     * 获取最大排序\n     */\n    @Override\n    public Integer getMaxSortByCategoryId(Long categoryId) {\n        return navSiteMapper.selectMaxSortByCategoryId(categoryId);\n    }\n\n    /**\n     * 更新点击量\n     */\n    @Override\n    public void updateClickCountById(Long id) {\n        navSiteMapper.updateClickCountById(id);\n    }\n\n    /**\n     * 站內网站搜索\n     */\n    @Override\n    public List<NavClientSiteSearchVO> siteSearch(String searchContent) {\n        if (StringUtils.isBlank(searchContent)) {\n            return new ArrayList<>();\n        }\n        return navSiteMapper.selectSiteSearchList(searchContent, CommonStatus.NORMAL.code);\n    }\n\n    /**\n     * 构造分类名称\n     */\n    private void buildCategoryName(List<NavSiteVO> list) {\n        if (CollectionUtils.isEmpty(list)) {\n            return;\n        }\n        Set<Long> categoryIds = list.parallelStream().map(s -> s.getCategoryId()).collect(Collectors.toSet());\n        LambdaQueryWrapper<NavCategory> categoryWrapper = new LambdaQueryWrapper<>();\n        categoryWrapper.in(NavCategory::getId, categoryIds);\n        categoryWrapper.select(NavCategory::getId, NavCategory::getCategoryName);\n        List<NavCategory> categoryList = navCategoryMapper.selectList(categoryWrapper);\n        list.forEach(site -> {\n            NavCategory category = categoryList.parallelStream().filter(c -> c.getId().equals(site.getCategoryId()))\n                    .findFirst().orElse(null);\n            if (category != null) {\n                site.setCategoryName(category.getCategoryName());\n            }\n        });\n    }\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/nav/site/vo/NavSiteClientVO.java",
    "content": "package com.geshanzsq.admin.nav.site.vo;\n\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\nimport java.io.Serializable;\n\n/**\n * 导航网站客户端\n *\n * @author geshanzsq\n * @date 2023/1/7\n */\n@Data\n@ApiModel(\"导航网站客户端\")\npublic class NavSiteClientVO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(\"网站 id\")\n    private Long id;\n\n    @ApiModelProperty(\"分类 id\")\n    private Long categoryId;\n\n    @ApiModelProperty(\"网站名称\")\n    private String siteName;\n\n    @ApiModelProperty(\"网站图片路径\")\n    private String sitePath;\n\n    @ApiModelProperty(\"网站描述\")\n    private String siteDescription;\n\n    @ApiModelProperty(\"网站地址\")\n    private String siteUrl;\n\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/nav/site/vo/NavSiteLatestCollectVO.java",
    "content": "package com.geshanzsq.admin.nav.site.vo;\n\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\n/**\n * 最新收录\n *\n * @author geshanzsq\n * @date 2023/4/13\n */\n@Data\n@ApiModel(\"最新收录\")\npublic class NavSiteLatestCollectVO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(\"网站 id\")\n    private Long id;\n\n    @ApiModelProperty(\"网站图片路径\")\n    private String sitePath;\n\n    @ApiModelProperty(\"网站名称\")\n    private String siteName;\n\n    @ApiModelProperty(\"网站描述\")\n    private String siteDescription;\n\n    @ApiModelProperty(\"网站地址\")\n    private String siteUrl;\n\n    @ApiModelProperty(\"创建时间\")\n    private Date gmtCreate;\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/nav/site/vo/NavSiteMatchVO.java",
    "content": "package com.geshanzsq.admin.nav.site.vo;\n\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\nimport java.io.Serializable;\n\n/**\n * 导航网站匹配\n *\n * @author geshanzsq\n * @date 2022/11/20\n */\n@Data\n@ApiModel(\"导航网站匹配\")\npublic class NavSiteMatchVO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(\"网站名称\")\n    private String siteName;\n\n    @ApiModelProperty(\"网站图片路径\")\n    private String sitePath;\n\n    @ApiModelProperty(\"网站描述\")\n    private String siteDescription;\n\n}"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/java/com/geshanzsq/admin/nav/site/vo/NavSiteVO.java",
    "content": "package com.geshanzsq.admin.nav.site.vo;\n\nimport com.geshanzsq.common.core.web.vo.BaseVO;\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\nimport java.io.Serializable;\n\n/**\n * 导航网站\n *\n * @author geshanzsq\n * @date 2022/11/20\n */\n@Data\n@ApiModel(\"导航网站\")\npublic class NavSiteVO extends BaseVO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(\"网站 id\")\n    private Long id;\n\n    @ApiModelProperty(\"分类 id\")\n    private Long categoryId;\n\n    @ApiModelProperty(\"网站名称\")\n    private String siteName;\n\n    @ApiModelProperty(\"网站图片路径\")\n    private String sitePath;\n\n    @ApiModelProperty(\"网站描述\")\n    private String siteDescription;\n\n    @ApiModelProperty(\"网站地址\")\n    private String siteUrl;\n\n    @ApiModelProperty(\"排序\")\n    private Integer sort;\n\n    @ApiModelProperty(\"点击量\")\n    private Integer clickCount;\n\n    @ApiModelProperty(\"状态（1 正常，2 停用）\")\n    private Integer status;\n\n    @ApiModelProperty(\"分类名称\")\n    private String categoryName;\n\n}"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/resources/application-dev.yml",
    "content": "# 数据库配置\nspring:\n  datasource:\n    driver-class-name: com.mysql.cj.jdbc.Driver\n    url: jdbc:mysql://localhost:3306/geshanzsq_nav?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8\n    username: root\n    password: root\n    type: com.zaxxer.hikari.HikariDataSource\n    # 连接池配置\n    hikari:\n      # 最小连接池数量\n      minimum-idle: 5\n      # 最大连接池数量\n      maximum-pool-size: 10\n      # 配置获取连接等待超时的时间\n      connection-timeout: 60000\n      # 配置间隔多久才进行一次检测，检测需要关闭的空闲连接，单位是毫秒\n      validation-timeout: 60000\n      # 配置一个连接在池中最大生存的时间，单位是毫秒\n      max-lifetime: 900000\n      # 配置一个连接在池中最小生存的时间，单位是毫秒\n      idle-timeout: 300000\n  # redis 配置\n  redis:\n    # 地址\n    host: localhost\n    # 端口，默认为6379\n    port: 6379\n    # 密码\n    password:\n    # 连接超时时间\n    timeout: 10s\n    lettuce:\n      pool:\n        # 连接池中的最小空闲连接\n        min-idle: 0\n        # 连接池中的最大空闲连接\n        max-idle: 5\n        # 连接池的最大数据库连接数\n        max-active: 5\n        # #连接池最大阻塞等待时间（使用负值表示没有限制）\n        max-wait: -1ms\n    database: 6\n# 日志配置\nlogging:\n  level:\n    com.geshanzsq: debug\n    org.springframework: warn\n\n# 接口文档配置\nswagger:\n  # 是否启用，开发和测试环境建议开启\n  enable: true\n\n# 线程池配置\nthread:\n  # 核心线程池大小\n  core-pool-size: 50\n  # 最大可创建的线程数\n  max-pool-size: 200\n  # 队列最大长度\n  queueCapacity: 1000\n  # 线程池维护线程所允许的空闲时间\n  keepAliveSeconds: 300\n\n# 文件上传配置\nfile-upload:\n  # 上传基本路径\n  base-path: D:/data/geshanzsq-nav/profile\n  # 文件域名地址，可不填写，如填写，图片以域名加上传路径拼接访问\n  domain:"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/resources/application-prod.yml",
    "content": "# 数据库配置\nspring:\n  datasource:\n    driver-class-name: com.mysql.cj.jdbc.Driver\n    url: jdbc:mysql://127.0.0.1:3306/geshanzsq_nav?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8\n    username: root\n    password: root\n    type: com.zaxxer.hikari.HikariDataSource\n    # 连接池配置\n    hikari:\n      # 最小连接池数量\n      minimum-idle: 5\n      # 最大连接池数量\n      maximum-pool-size: 10\n      # 配置获取连接等待超时的时间\n      connection-timeout: 60000\n      # 配置间隔多久才进行一次检测，检测需要关闭的空闲连接，单位是毫秒\n      validation-timeout: 60000\n      # 配置一个连接在池中最大生存的时间，单位是毫秒\n      max-lifetime: 900000\n      # 配置一个连接在池中最小生存的时间，单位是毫秒\n      idle-timeout: 300000\n  # redis 配置\n  redis:\n    # 地址\n    host: 127.0.0.1\n    # 端口，默认为6379\n    port: 6379\n    # 连接超时时间\n    timeout: 10s\n    lettuce:\n      pool:\n        # 连接池中的最小空闲连接\n        min-idle: 0\n        # 连接池中的最大空闲连接\n        max-idle: 5\n        # 连接池的最大数据库连接数\n        max-active: 5\n        # #连接池最大阻塞等待时间（使用负值表示没有限制）\n        max-wait: -1ms\n    database: 6\n\n# 日志配置\nlogging:\n  level:\n    com.geshanzsq: info\n    org.springframework: info\n\n# 接口文档配置\nswagger:\n  # 是否启用，生产环境建议不启用，以免所有接口暴露\n  enable: false\n\n# 线程池配置\nthread:\n  # 核心线程池大小\n  core-pool-size: 50\n  # 最大可创建的线程数\n  max-pool-size: 200\n  # 队列最大长度\n  queueCapacity: 1000\n  # 线程池维护线程所允许的空闲时间\n  keepAliveSeconds: 300\n\n# 文件上传配置\nfile-upload:\n  # 上传基本路径\n  base-path: /data/file/geshanzsq-nav/profile\n  # 文件域名地址\n  domain:"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/resources/application.yml",
    "content": "# 服务配置\nserver:\n  # 端口\n  port: 8083\n  servlet:\n    # 应用访问路径\n    context-path: /geshanzsq-nav-api\n  tomcat:\n    # tomcat uri 编码配置\n    uri-encoding: UTF-8\n\n# spring 配置\nspring:\n  profiles:\n    active: dev\n  # 资源信息\n  messages:\n    # 国际化资源配置文件路径\n    basename: i18n/message\n    encoding: utf-8\n  application:\n    name: geshanzsq-nav-admin-application\n  servlet:\n    multipart:\n      # 文件上传最大大小\n      max-file-size: 10MB\n      # 设置总上传的文件大小\n      max-request-size: 20MB\n\n# MyBatis Plus 配置\nmybatis-plus:\n  global-config:\n    db-config:\n      # 配置逻辑删除\n      logic-delete-value: 2\n      logic-not-delete-value: 1\n      # 逻辑删除字段\n      logic-delete-field: delFlag\n  mapper-locations: classpath*:/mapper/**/*Mapper.xml\n\n# token 令牌配置\ntoken:\n  # 自定义令牌标识\n  header: Authorization\n  # 令牌有效期，默认 1 天, 3600*24*1\n  expire-time: 86400\n  # 令牌参数\n  token-param: token\n  # 令牌前缀\n  token-prefix: Bearer\n  # 存入 redis 前缀\n  token-redis-prefix: \"token:admin:\"\n  # 刷新权限 redis 前缀\n  permission-refresh-redis-prefix: \"permission:refresh:admin:\"\n\n# 分页配置\npage:\n  # 默认分页记录数\n  default-page-size: 10\n  # 最大分页记录数\n  max-page-size: 200\n\n# 文件上传配置\nfile-upload:\n  # 文件名称最大长度\n  name-max-length: 200\n  # 文件映射前缀\n  map-prefix: /profile\n\n# 接口文档配置\nswagger:\n  title: 格姗导航接口文档\n  description: 包含用户管理、菜单管理、角色管理、数据字典、API 管理、参数配置、登录日志、操作日志、导航分类管理、网站管理等功能\n  author: geshanzsq\n  url: http://gesdh.cn\n  email: 497301391@qq.com\n  version: 2.0.0\n\n# 防止XSS攻击\nxss:\n  # 过滤开关\n  enabled: true\n  # 排除链接（多个用逗号分隔）\n  excludes: /nav/config/\n  # 匹配链接\n  urlPatterns: /system/*,/nav/*\n\n# 安全配置\nsecurity:\n  # 不用登录就能访问\n  not-login-urls:\n    - /login\n    - /getCaptchaImage\n    - /bing/getBingImage\n    - /nav/site/updateClickCount/*\n    - /client/nav/category/site/list\n    - /client/nav/category/list\n    - /client/nav/comment/**\n    - /client/search/**\n    - /nav/config/about"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/resources/i18n/message.properties",
    "content": "# 操作\noperate.success=操作成功\noperate.fail=操作失败\n\n# 系统异常\nsystem.exception=系统错误，请联系管理员\nbad.request.method=请求接口方式不支持\n\n# 安全框架\nsecurity.not.login=登录后才能访问，请先登录\nsecurity.login.expire=登录已过期，请重新登录\nsecurity.forbidden=您没有权限访问\n\n# 登录\nlogin.username.password.not.match=用户名或密码不正确\nlogin.captcha.expire=验证码已失效\nlogin.captcha.error=验证码不正确\nlogin.success=登录成功\nlogin.decrypt.fail=账号密码参数解密失败\n# 第三方登录\nthird.login.not.support=当前登录方式不支持\n\n# 用户\nuser.info.fail=获取用户信息异常\nuser.username.exist=用户名已存在\nreset.user.password.old.not.null=请输入旧密码\nreset.user.password.old.not.matches=旧密码不匹配\nreset.user.password.old.not.equal.new=旧密码不能与新密码一致\n\n# 菜单\nmenu.has.child=存在子菜单,不允许删除\nmenu.not.exist=菜单不存在\n\n# 角色\nrole.code.exist=角色编码已存在\nrole.super.admin.not.operation=超级管理员角色不允许操作\nrole.not.exist=角色不存在\nrole.auth.user.not.exit=所选用户均不存在或已删除，请重新选择\nrole.is.associate.user=所选角色已分配用户，请先取消分配用户再删除角色\n\n# 接口\napi.category.allocated=当前分类下有关联 Api，不允许删除\n\n# 字典\ndictionary.exist.data=所选部分字典存在字典数据，不允许删除\n\n# 文件上传\nfile.upload.file.name.max.length=文件名称超过最大长度 {}，请重新命名文件名称后再上传\nfile.upload.file.size.max=文件大小超过限制\nfile.upload.file.type.not.allow=文件类型不支持上传\nfile.upload.fail=文件上传失败\n\n# 导航分类\nnav.category.has.child=存在子分类,不允许删除\nnav.category.id.parent.id=上级分类不能为当前分类\n\n# 导航网站\nnav.site.collect.site.url.not.empty=网站链接不能为空\nnav.site.collect.fail=网站图片采集失败，请检查网站地址是否正确，或手动上传图片\nnav.site.not.match=暂无匹配的网站\n\n# 导航分类用户\nnav.category.user.id.parent.id=上级分类不能为当前分类\nnav.category.user.collect.share.me=不能采集自己分享的分类和网站\n\n\n# 导航网站用户\nnav.user.site.is.import=当前网站已经导入过当前所属分类，请选择其他所属分类或其他网站\n\n# 导航分享链接\nnav.share.url.exist=分享链接已存在，请重新输入其他链接\nnav.share.not.exist.or.disable=分享链接不存在或已关闭分享\nnav.share.password.not.null=请输入分享密码\nnav.share.password.not.correct=分享密码不正确\nnav.share.allow.one=分享链接只允许访问一次，当前访问次数已达上限\nnav.share.sync.site.category.not.exist=同步到网站的分类不存在\nnav.share.sync.site.category.done=当前分类下已添加过当前网站\n\n# 导航评论\nnav.comment.email.not.null=邮箱不能为空\nnav.comment.nick.name.not.null=昵称不能为空"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/resources/logback-spring.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<configuration>\n\n    <!-- 日志依赖的渲染类 -->\n    <conversionRule conversionWord=\"clr\" converterClass=\"org.springframework.boot.logging.logback.ColorConverter\" />\n    <conversionRule conversionWord=\"wex\" converterClass=\"org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter\" />\n    <!-- 日志格式 -->\n    <property name=\"CONSOLE_LOG_PATTERN\" value=\"%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(%5p) %clr(${PID:- }) [%t]{magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n%wex\"/>\n    <property name=\"FILE_LOG_PATTERN\" value=\"%d{yyyy-MM-dd HH:mm:ss.SSS} %5p ${PID:- } --- [%t] %-40.40logger{39} : %m%n%wex\"/>\n\n    <!-- 控制台输出 -->\n    <appender name=\"CONSOLE\" class=\"ch.qos.logback.core.ConsoleAppender\">\n        <encoder class=\"ch.qos.logback.classic.encoder.PatternLayoutEncoder\">\n            <!--格式化输出：%d表示日期，%thread表示线程名，%-5level：级别从左显示5个字符宽度%msg：日志消息，%n是换行符-->\n            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>\n        </encoder>\n    </appender>\n\n    <!-- 按照每天生成日志文件 -->\n    <appender name=\"FILE\" class=\"ch.qos.logback.core.rolling.RollingFileAppender\">\n        <rollingPolicy class=\"ch.qos.logback.core.rolling.TimeBasedRollingPolicy\">\n            <!--日志文件输出的文件名-->\n            <FileNamePattern>./logs/geshanzsq-nav-%d{yyyy-MM-dd}-%i.log</FileNamePattern>\n            <timeBasedFileNamingAndTriggeringPolicy class=\"ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP\">\n                <!-- 每个日志文件最大大小 -->\n                <maxFileSize>100MB</maxFileSize>\n            </timeBasedFileNamingAndTriggeringPolicy>\n            <!--日志文件保留天数-->\n            <MaxHistory>60</MaxHistory>\n        </rollingPolicy>\n        <encoder class=\"ch.qos.logback.classic.encoder.PatternLayoutEncoder\">\n            <!--格式化输出：%d表示日期，%thread表示线程名，%-5level：级别从左显示5个字符宽度%msg：日志消息，%n是换行符-->\n            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>\n        </encoder>\n    </appender>\n\n    <!-- 日志输出级别 -->\n    <root level=\"INFO\">\n        <appender-ref ref=\"CONSOLE\"/>\n        <appender-ref ref=\"FILE\"/>\n    </root>\n</configuration>\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/resources/mapper/nav/category/NavCategoryMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\" >\n<mapper namespace=\"com.geshanzsq.admin.nav.category.mapper.NavCategoryMapper\">\n\n    <resultMap id=\"BaseResultMap\" type=\"com.geshanzsq.admin.nav.category.vo.NavCategoryVO\" >\n        <result column=\"id\" property=\"id\" />\n        <result column=\"parent_id\" property=\"parentId\" />\n        <result column=\"sort\" property=\"sort\" />\n        <result column=\"category_name\" property=\"categoryName\" />\n        <result column=\"category_icon\" property=\"categoryIcon\" />\n        <result column=\"status\" property=\"status\" />\n        <result column=\"fk_create_user_id\" property=\"createUserId\" />\n        <result column=\"gmt_create\" property=\"gmtCreate\" />\n        <result column=\"modify_user_id\" property=\"modifyUserId\" />\n        <result column=\"gmt_modify\" property=\"gmtModify\" />\n    </resultMap>\n\n    <!-- 获取最大排序 -->\n    <select id=\"selectMaxSortByParentId\" parameterType=\"Long\" resultType=\"java.lang.Integer\">\n        select max(sort) from nav_category where parent_id = #{parentId}\n    </select>\n</mapper>"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/resources/mapper/nav/config/NavSiteConfigMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\" >\n<mapper namespace=\"com.geshanzsq.admin.nav.config.mapper.NavSiteConfigMapper\">\n\n    <!-- 更新访问量 -->\n    <update id=\"updateVisitCount\">\n        update nav_site_config set about_site_visit_count = about_site_visit_count + 1 where id = #{id}\n    </update>\n</mapper>"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-application/src/main/resources/mapper/nav/site/NavSiteMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\" >\n<mapper namespace=\"com.geshanzsq.admin.nav.site.mapper.NavSiteMapper\">\n\n    <resultMap id=\"BaseResultMap\" type=\"com.geshanzsq.admin.nav.site.vo.NavSiteVO\" >\n        <result column=\"id\" property=\"id\" />\n        <result column=\"fk_category_id\" property=\"categoryId\" />\n        <result column=\"site_name\" property=\"siteName\" />\n        <result column=\"site_path\" property=\"sitePath\" />\n        <result column=\"site_description\" property=\"siteDescription\" />\n        <result column=\"site_url\" property=\"siteUrl\" />\n        <result column=\"sort\" property=\"sort\" />\n        <result column=\"click_count\" property=\"clickCount\" />\n        <result column=\"status\" property=\"status\" />\n        <result column=\"fk_create_user_id\" property=\"createUserId\" />\n        <result column=\"gmt_create\" property=\"gmtCreate\" />\n        <result column=\"modify_user_id\" property=\"modifyUserId\" />\n        <result column=\"gmt_modify\" property=\"gmtModify\" />\n    </resultMap>\n\n    <resultMap id=\"NavClientSiteSearchMap\" type=\"com.geshanzsq.admin.client.search.vo.NavClientSiteSearchVO\">\n        <result column=\"id\" property=\"id\" />\n        <result column=\"category_name\" property=\"categoryName\" />\n        <result column=\"site_name\" property=\"siteName\" />\n        <result column=\"site_url\" property=\"siteUrl\" />\n        <result column=\"site_description\" property=\"siteDescription\" />\n    </resultMap>\n\n    <!-- 获取最大排序 -->\n    <select id=\"selectMaxSortByCategoryId\" resultType=\"Integer\">\n        select max(sort) from nav_site\n        <where>\n            <if test=\"categoryId != null\">\n                and fk_category_id = #{categoryId}\n            </if>\n        </where>\n    </select>\n\n    <!-- 更新点击量 -->\n    <update id=\"updateClickCountById\">\n        update nav_site set click_count = click_count + 1 where id = #{id}\n    </update>\n\n    <!-- 查询列表 -->\n    <select id=\"selectListByUserImport\" resultType=\"com.geshanzsq.admin.nav.site.dto.NavSiteListUserImportDTO\" resultMap=\"BaseResultMap\">\n        select id,\n            fk_category_id,\n            site_name,\n            site_path,\n            site_description,\n            site_url\n        from nav_site\n        where status = #{status}\n        <choose>\n            <when test=\"categoryId != null and categoryId == 0\">\n                <!-- 只查前 20 条记录 -->\n                order by gmt_create desc\n                limit 20\n            </when>\n            <otherwise>\n                <if test=\"categoryId != null\">\n                    and fk_category_id = #{categoryId}\n                </if>\n                <if test=\"siteName != null and siteName != ''\">\n                    and (\n                    site_name like concat('%', #{siteName}, '%')\n                    or site_description like concat('%', #{siteName}, '%')\n                    or site_url like concat('%', #{siteName}, '%')\n                    )\n                </if>\n                order by\n                    fk_category_id asc, sort asc\n            </otherwise>\n        </choose>\n    </select>\n\n    <!-- 站內网站搜索 -->\n    <select id=\"selectSiteSearchList\" resultMap=\"NavClientSiteSearchMap\">\n        select s.id, s.site_name, s.site_url, s.site_description, c.category_name from nav_site s\n        inner join nav_category c on s.fk_category_id = c.id\n        where s.status = #{status}\n        and (s.site_name like concat('%', #{searchContent}, '%') or s.site_description like concat('%', #{searchContent}, '%'))\n    </select>\n\n    <!-- 获取点击量 -->\n    <select id=\"selectClickCount\" resultType=\"java.lang.Long\">\n        select sum(click_count) from nav_site\n    </select>\n</mapper>"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>geshanzsq-nav-admin</artifactId>\n        <groupId>com.geshanzsq</groupId>\n        <version>2.0.0</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>geshanzsq-nav-admin-system</artifactId>\n    <name>geshanzsq-nav-admin-system</name>\n    <description>系统模块</description>\n    <version>2.0.0</version>\n\n    <dependencies>\n\n        <!-- Swagger 接口文档 -->\n        <dependency>\n            <groupId>com.geshanzsq</groupId>\n            <artifactId>geshanzsq-nav-common-swagger</artifactId>\n        </dependency>\n\n        <!-- 日志模块 -->\n        <dependency>\n            <groupId>com.geshanzsq</groupId>\n            <artifactId>geshanzsq-nav-common-log</artifactId>\n        </dependency>\n\n    </dependencies>\n\n\n</project>"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/api/controller/SysApiCategoryController.java",
    "content": "package com.geshanzsq.admin.system.api.controller;\n\nimport com.geshanzsq.admin.system.api.dto.SysApiCategoryAddDTO;\nimport com.geshanzsq.admin.system.api.dto.SysApiCategoryPageDTO;\nimport com.geshanzsq.admin.system.api.dto.SysApiCategoryUpdateDTO;\nimport com.geshanzsq.admin.system.api.mapstruct.SysApiCategoryConverter;\nimport com.geshanzsq.admin.system.api.po.SysApiCategory;\nimport com.geshanzsq.admin.system.api.service.SysApiCategoryService;\nimport com.geshanzsq.admin.system.api.vo.SysApiCategoryVO;\nimport com.geshanzsq.common.core.web.response.ResponseResult;\nimport com.geshanzsq.common.framework.mybatis.page.vo.PageVO;\nimport com.geshanzsq.common.framework.web.controller.BaseController;\nimport com.geshanzsq.common.log.annotation.Log;\nimport com.geshanzsq.common.log.enums.BusinessType;\nimport io.swagger.annotations.Api;\nimport io.swagger.annotations.ApiOperation;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.validation.Valid;\nimport java.util.List;\n\n/**\n * 系统接口分类\n *\n * @author geshanzsq\n * @date 2022/6/24\n */\n@Api(tags = \"系统接口分类\")\n@RestController\n@RequestMapping(\"/system/api/category\")\npublic class SysApiCategoryController extends BaseController {\n\n    @Autowired\n    private SysApiCategoryService sysApiCategoryService;\n\n    @ApiOperation(\"分页列表\")\n    @GetMapping(\"/page\")\n    @PreAuthorize(\"@auth.hasUrl()\")\n    public ResponseResult<PageVO<SysApiCategoryVO>> page(SysApiCategoryPageDTO pageDTO) {\n        pageDTO.setOrderColumn(\"sort,id\");\n        PageVO<SysApiCategory> pageVO = sysApiCategoryService.page(pageDTO);\n        return ResponseResult.success(SysApiCategoryConverter.INSTANCE.convert(pageVO));\n    }\n\n    @ApiOperation(\"列表\")\n    @GetMapping(\"/list\")\n    @PreAuthorize(\"@auth.hasUrl()\")\n    public ResponseResult<List<SysApiCategoryVO>> list() {\n        return ResponseResult.success(sysApiCategoryService.listSort());\n    }\n\n    @GetMapping(\"/getById/{id}\")\n    @ApiOperation(\"详情\")\n    @PreAuthorize(\"@auth.hasUrl()\")\n    public ResponseResult<SysApiCategoryVO> getById(@PathVariable Long id) {\n        return ResponseResult.success(SysApiCategoryConverter.INSTANCE.convert(sysApiCategoryService.getById(id)));\n    }\n\n    @PostMapping()\n    @ApiOperation(\"新增\")\n    @PreAuthorize(\"@auth.hasUrl()\")\n    @Log(moduleName = \"系统接口分类\", businessType = BusinessType.ADD)\n    public ResponseResult add(@Valid @RequestBody SysApiCategoryAddDTO addDTO) {\n        SysApiCategory sysApiCategory = SysApiCategoryConverter.INSTANCE.convert(addDTO);\n        sysApiCategoryService.save(sysApiCategory);\n        return ResponseResult.success();\n    }\n\n    @PutMapping()\n    @ApiOperation(\"修改\")\n    @PreAuthorize(\"@auth.hasUrl()\")\n    @Log(moduleName = \"系统接口分类\", businessType = BusinessType.UPDATE)\n    public ResponseResult update(@Valid @RequestBody SysApiCategoryUpdateDTO updateDTO) {\n        SysApiCategory sysApiCategory = SysApiCategoryConverter.INSTANCE.convert(updateDTO);\n        sysApiCategoryService.updateById(sysApiCategory);\n        return ResponseResult.success();\n    }\n\n    @DeleteMapping(\"/delete/{ids}\")\n    @ApiOperation(\"删除\")\n    @PreAuthorize(\"@auth.hasUrl()\")\n    @Log(moduleName = \"系统接口分类\", businessType = BusinessType.DELETE)\n    public ResponseResult delete(@PathVariable Long[] ids) {\n        sysApiCategoryService.removeByIds(ids);\n        return ResponseResult.success();\n    }\n\n    @GetMapping(\"/getMaxSort\")\n    @ApiOperation(\"获取最大排序\")\n    @PreAuthorize(\"@auth.hasUrl()\")\n    public ResponseResult<Integer> getMaxSort() {\n        Integer maxSort = sysApiCategoryService.getMaxSort();\n        return ResponseResult.success(maxSort);\n    }\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/api/controller/SysApiController.java",
    "content": "package com.geshanzsq.admin.system.api.controller;\n\nimport com.geshanzsq.admin.system.api.dto.SysApiAddDTO;\nimport com.geshanzsq.admin.system.api.dto.SysApiPageDTO;\nimport com.geshanzsq.admin.system.api.dto.SysApiUpdateDTO;\nimport com.geshanzsq.admin.system.api.mapstruct.SysApiConverter;\nimport com.geshanzsq.admin.system.api.po.SysApi;\nimport com.geshanzsq.admin.system.api.service.SysApiService;\nimport com.geshanzsq.admin.system.api.vo.SysApiVO;\nimport com.geshanzsq.common.core.web.response.ResponseResult;\nimport com.geshanzsq.common.framework.mybatis.page.vo.PageVO;\nimport com.geshanzsq.common.framework.web.controller.BaseController;\nimport com.geshanzsq.common.log.annotation.Log;\nimport com.geshanzsq.common.log.enums.BusinessType;\nimport io.swagger.annotations.Api;\nimport io.swagger.annotations.ApiOperation;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.validation.Valid;\n\n/**\n * 系统接口\n *\n * @author geshanzsq\n * @date 2022/6/25\n */\n@Api(tags = \"系统接口\")\n@RestController\n@RequestMapping(\"/system/api\")\npublic class SysApiController extends BaseController {\n\n    @Autowired\n    private SysApiService sysApiService;\n\n    @ApiOperation(\"分页列表\")\n    @GetMapping(\"/page\")\n    @PreAuthorize(\"@auth.hasUrl()\")\n    public ResponseResult<PageVO<SysApiVO>> page(@Valid SysApiPageDTO pageDTO) {\n        pageDTO.setOrderColumn(\"sort,id\");\n        PageVO<SysApi> pageVO = sysApiService.page(pageDTO);\n        return ResponseResult.success(SysApiConverter.INSTANCE.convert(pageVO));\n    }\n\n    @GetMapping(\"/getById/{id}\")\n    @ApiOperation(\"详情\")\n    @PreAuthorize(\"@auth.hasUrl()\")\n    public ResponseResult<SysApiVO> getById(@PathVariable Long id) {\n        return ResponseResult.success(SysApiConverter.INSTANCE.convert(sysApiService.getById(id)));\n    }\n\n    @PostMapping()\n    @ApiOperation(\"新增\")\n    @PreAuthorize(\"@auth.hasUrl()\")\n    @Log(moduleName = \"系统接口\", businessType = BusinessType.ADD)\n    public ResponseResult add(@Valid @RequestBody SysApiAddDTO addDTO) {\n        SysApi sysApi = SysApiConverter.INSTANCE.convert(addDTO);\n        sysApiService.save(sysApi);\n        return ResponseResult.success();\n    }\n\n    @PutMapping()\n    @ApiOperation(\"修改\")\n    @PreAuthorize(\"@auth.hasUrl()\")\n    @Log(moduleName = \"系统接口\", businessType = BusinessType.UPDATE)\n    public ResponseResult update(@Valid @RequestBody SysApiUpdateDTO updateDTO) {\n        SysApi sysApi = SysApiConverter.INSTANCE.convert(updateDTO);\n        sysApiService.updateById(sysApi);\n        return ResponseResult.success();\n    }\n\n    @DeleteMapping(\"/delete/{ids}\")\n    @ApiOperation(\"删除\")\n    @PreAuthorize(\"@auth.hasUrl()\")\n    @Log(moduleName = \"系统接口\", businessType = BusinessType.DELETE)\n    public ResponseResult delete(@PathVariable Long[] ids) {\n        sysApiService.removeByIds(ids);\n        return ResponseResult.success();\n    }\n\n    @GetMapping(\"/getMaxSortByCategoryId\")\n    @ApiOperation(\"获取最大排序\")\n    @PreAuthorize(\"@auth.hasUrl()\")\n    public ResponseResult<Integer> getMaxSortByCategoryId(Long apiCategoryId) {\n        Integer maxSort = sysApiService.getMaxSortByCategoryId(apiCategoryId);\n        return ResponseResult.success(maxSort);\n    }\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/api/dto/SysApiAddDTO.java",
    "content": "package com.geshanzsq.admin.system.api.dto;\n\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotBlank;\nimport javax.validation.constraints.NotNull;\nimport java.io.Serializable;\n\n/**\n * 接口新增\n *\n * @author geshanzsq\n * @date 2022/6/26\n */\n@Data\n@ApiModel(\"接口新增\")\npublic class SysApiAddDTO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(value = \"接口名称\", required = true)\n    @NotBlank(message = \"接口名称不能为空\")\n    private String apiName;\n\n    @ApiModelProperty(value = \"接口地址\", required = true)\n    @NotBlank(message = \"接口地址不能为空\")\n    private String apiUrl;\n\n    @ApiModelProperty(value = \"接口请求方式（如：get，post）\", required = true)\n    @NotBlank(message = \"接口请求方式不能为空\")\n    private String apiMethod;\n\n    @ApiModelProperty(value = \"所属分类\", required = true)\n    @NotNull(message = \"所属分类不能为空\")\n    private Long apiCategoryId;\n\n    @ApiModelProperty(value = \"排序\", required = true)\n    @NotNull(message = \"排序不能为空\")\n    private Integer sort;\n\n    @ApiModelProperty(value = \"状态（1 正常，2 停用）\", required = true)\n    @NotNull(message = \"状态不能为空\")\n    private Integer status;\n\n    @ApiModelProperty(\"备注\")\n    private String remark;\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/api/dto/SysApiCategoryAddDTO.java",
    "content": "package com.geshanzsq.admin.system.api.dto;\n\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotBlank;\nimport javax.validation.constraints.NotNull;\nimport java.io.Serializable;\n\n/**\n * 接口分类新增\n *\n * @author geshanzsq\n * @date 2022/6/24\n */\n@Data\n@ApiModel(\"接口分类新增\")\npublic class SysApiCategoryAddDTO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(value = \"接口分类名称\", required = true)\n    @NotBlank(message = \"接口分类名称不能为空\")\n    private String categoryName;\n\n    @ApiModelProperty(value = \"排序\", required = true)\n    @NotNull(message = \"排序不能为空\")\n    private Integer sort;\n\n    @ApiModelProperty(value = \"状态（1 正常，2 停用）\", required = true)\n    @NotNull(message = \"状态不能为空\")\n    private Integer status;\n\n    @ApiModelProperty(\"备注\")\n    private String remark;\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/api/dto/SysApiCategoryPageDTO.java",
    "content": "package com.geshanzsq.admin.system.api.dto;\n\nimport com.geshanzsq.common.framework.mybatis.page.dto.PageDTO;\nimport com.geshanzsq.common.framework.mybatis.plugin.annotation.Query;\nimport com.geshanzsq.common.framework.mybatis.plugin.enums.QueryWay;\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\nimport java.io.Serializable;\n\n/**\n * 接口分类分页查询\n *\n * @author geshanzsq\n * @date 2022/6/24\n */\n@Data\n@ApiModel(\"接口分类分页\")\npublic class SysApiCategoryPageDTO extends PageDTO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(\"接口分类名称\")\n    @Query(QueryWay.LIKE)\n    private String categoryName;\n\n    @ApiModelProperty(\"状态，1 正常，2 停用\")\n    private Integer status;\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/api/dto/SysApiCategoryUpdateDTO.java",
    "content": "package com.geshanzsq.admin.system.api.dto;\n\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotBlank;\nimport javax.validation.constraints.NotNull;\nimport java.io.Serializable;\n\n/**\n * 接口分类修改\n *\n * @author geshanzsq\n * @date 2022/6/24\n */\n@Data\n@ApiModel(\"接口分类修改\")\npublic class SysApiCategoryUpdateDTO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(value = \"接口分类 id\", required = true)\n    @NotNull(message = \"接口分类 id 不能为空\")\n    private Long id;\n\n    @ApiModelProperty(value = \"接口分类名称\", required = true)\n    @NotBlank(message = \"接口分类名称不能为空\")\n    private String categoryName;\n\n    @ApiModelProperty(value = \"排序\", required = true)\n    @NotNull(message = \"接口分类名称不能为空\")\n    private Integer sort;\n\n    @ApiModelProperty(value = \"状态（1 正常，2 停用）\", required = true)\n    @NotNull(message = \"状态不能为空\")\n    private Integer status;\n\n    @ApiModelProperty(\"备注\")\n    private String remark;\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/api/dto/SysApiPageDTO.java",
    "content": "package com.geshanzsq.admin.system.api.dto;\n\nimport com.geshanzsq.common.framework.mybatis.page.dto.PageDTO;\nimport com.geshanzsq.common.framework.mybatis.plugin.annotation.Query;\nimport com.geshanzsq.common.framework.mybatis.plugin.enums.QueryWay;\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\nimport java.io.Serializable;\n\n/**\n * 接口分页查询\n *\n * @author geshanzsq\n * @date 2022/6/24\n */\n@Data\n@ApiModel(\"接口分页\")\npublic class SysApiPageDTO extends PageDTO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(\"接口名称\")\n    @Query(QueryWay.LIKE)\n    private String apiName;\n\n    @ApiModelProperty(\"接口地址\")\n    @Query(QueryWay.LIKE)\n    private String apiUrl;\n\n    @ApiModelProperty(\"接口请求方式（如：get，post）\")\n    private String apiMethod;\n\n    @ApiModelProperty(value = \"所属分类\", required = true)\n    @NotNull(message = \"所属分类不能为空\")\n    private Long apiCategoryId;\n\n    @ApiModelProperty(\"状态（1 正常，2 停用）\")\n    private Integer status;\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/api/dto/SysApiUpdateDTO.java",
    "content": "package com.geshanzsq.admin.system.api.dto;\n\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotBlank;\nimport javax.validation.constraints.NotNull;\nimport java.io.Serializable;\n\n/**\n * 接口修改\n *\n * @author geshanzsq\n * @date 2022/6/26\n */\n@Data\n@ApiModel(\"接口修改\")\npublic class SysApiUpdateDTO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(value = \"接口 id\", required = true)\n    @NotNull(message = \"接口 id 不能为空\")\n    private Long id;\n\n    @ApiModelProperty(value = \"接口名称\", required = true)\n    @NotBlank(message = \"接口名称不能为空\")\n    private String apiName;\n\n    @ApiModelProperty(value = \"接口地址\", required = true)\n    @NotBlank(message = \"接口地址不能为空\")\n    private String apiUrl;\n\n    @ApiModelProperty(value = \"接口请求方式（如：get，post）\", required = true)\n    @NotBlank(message = \"接口请求方式不能为空\")\n    private String apiMethod;\n\n    @ApiModelProperty(value = \"所属分类\", required = true)\n    @NotNull(message = \"所属分类不能为空\")\n    private Long apiCategoryId;\n\n    @ApiModelProperty(value = \"排序\", required = true)\n    @NotNull(message = \"排序不能为空\")\n    private Integer sort;\n\n    @ApiModelProperty(value = \"状态（1 正常，2 停用）\", required = true)\n    @NotNull(message = \"状态不能为空\")\n    private Integer status;\n\n    @ApiModelProperty(\"备注\")\n    private String remark;\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/api/mapper/SysApiCategoryMapper.java",
    "content": "package com.geshanzsq.admin.system.api.mapper;\n\nimport com.geshanzsq.admin.system.api.po.SysApiCategory;\nimport com.geshanzsq.common.framework.web.mapper.BaseMapperPlus;\nimport org.apache.ibatis.annotations.Mapper;\n\n/**\n * 系统接口分类\n *\n * @author geshanzsq\n * @date 2022/6/24\n */\n@Mapper\npublic interface SysApiCategoryMapper extends BaseMapperPlus<SysApiCategory> {\n\n    /**\n     * 获取最大排序\n     */\n    Integer selectMaxSort();\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/api/mapper/SysApiMapper.java",
    "content": "package com.geshanzsq.admin.system.api.mapper;\n\nimport com.geshanzsq.admin.system.api.po.SysApi;\nimport com.geshanzsq.admin.system.api.vo.SysApiVO;\nimport com.geshanzsq.admin.system.menu.vo.SysMenuAuthApiVO;\nimport com.geshanzsq.common.framework.web.mapper.BaseMapperPlus;\nimport org.apache.ibatis.annotations.Mapper;\nimport org.apache.ibatis.annotations.Param;\n\nimport java.util.List;\n\n/**\n * 接口\n *\n * @author geshanzsq\n * @date 2022/6/12\n */\n@Mapper\npublic interface SysApiMapper extends BaseMapperPlus<SysApi> {\n\n    /**\n     * 通过菜单 id 列表获取接口\n     * @param menuIds 菜单 id 列表\n     * @param status 状态\n     * @return\n     */\n    List<SysApiVO> getApiByMenuIds(@Param(\"menuIds\") List<Long> menuIds,\n                                   @Param(\"status\") Integer status);\n\n    /**\n     * 获取最大排序\n     * @param apiCategoryId 分类 id\n     */\n    Integer selectMaxSortByCategoryId(Long apiCategoryId);\n\n    /**\n     * 获取分配的 API 接口\n     * @param menuId 菜单 id\n     * @return\n     */\n    List<SysMenuAuthApiVO> selectAuthApiByMenuId(Long menuId);\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/api/mapper/SysApiMenuMapper.java",
    "content": "package com.geshanzsq.admin.system.api.mapper;\n\nimport com.geshanzsq.admin.system.api.po.SysApiMenu;\nimport com.geshanzsq.common.framework.web.mapper.BaseMapperPlus;\nimport org.apache.ibatis.annotations.Mapper;\n\n/**\n * 接口菜单\n *\n * @author geshanzsq\n * @date 2022/6/26\n */\n@Mapper\npublic interface SysApiMenuMapper extends BaseMapperPlus<SysApiMenu> {\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/api/mapstruct/SysApiCategoryConverter.java",
    "content": "package com.geshanzsq.admin.system.api.mapstruct;\n\nimport com.geshanzsq.admin.system.api.dto.SysApiCategoryAddDTO;\nimport com.geshanzsq.admin.system.api.dto.SysApiCategoryUpdateDTO;\nimport com.geshanzsq.admin.system.api.po.SysApiCategory;\nimport com.geshanzsq.admin.system.api.vo.SysApiCategoryVO;\nimport com.geshanzsq.common.framework.mybatis.page.vo.PageVO;\nimport org.mapstruct.Mapper;\nimport org.mapstruct.factory.Mappers;\n\nimport java.util.List;\n\n/**\n * 系统接口对象转换\n *\n * @author geshanzsq\n * @date 2022/6/24\n */\n@Mapper\npublic interface SysApiCategoryConverter {\n\n    SysApiCategoryConverter INSTANCE = Mappers.getMapper(SysApiCategoryConverter.class);\n\n    SysApiCategoryVO convert(SysApiCategory sysApiCategory);\n\n    List<SysApiCategoryVO> convertList(List<SysApiCategory> list);\n\n    PageVO<SysApiCategoryVO> convert(PageVO<SysApiCategory> pageVO);\n\n    SysApiCategory convert(SysApiCategoryAddDTO addDTO);\n\n    SysApiCategory convert(SysApiCategoryUpdateDTO updateDTO);\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/api/mapstruct/SysApiConverter.java",
    "content": "package com.geshanzsq.admin.system.api.mapstruct;\n\nimport com.geshanzsq.admin.system.api.dto.SysApiAddDTO;\nimport com.geshanzsq.admin.system.api.dto.SysApiUpdateDTO;\nimport com.geshanzsq.admin.system.api.po.SysApi;\nimport com.geshanzsq.admin.system.api.vo.SysApiVO;\nimport com.geshanzsq.common.framework.mybatis.page.vo.PageVO;\nimport com.geshanzsq.framework.security.domain.ApiPermission;\nimport org.mapstruct.Mapper;\nimport org.mapstruct.factory.Mappers;\n\nimport java.util.List;\n\n/**\n * 接口对象转换\n *\n * @author geshanzsq\n * @date 2022/6/12\n */\n@Mapper\npublic interface SysApiConverter {\n\n    SysApiConverter INSTANCE = Mappers.getMapper(SysApiConverter.class);\n\n    List<ApiPermission> convertList(List<SysApiVO> list);\n\n    PageVO<SysApiVO> convert(PageVO<SysApi> pageVo);\n\n    SysApiVO convert(SysApi sysApi);\n\n    SysApi convert(SysApiAddDTO addDTO);\n\n    SysApi convert(SysApiUpdateDTO updateDTO);\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/api/po/SysApi.java",
    "content": "package com.geshanzsq.admin.system.api.po;\n\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport lombok.Data;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\n/**\n * 系统接口\n *\n * @author geshanzsq\n * @date 2022/6/12\n */\n@Data\npublic class SysApi implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * 接口 id\n     */\n    @TableId\n    private Long id;\n\n    /**\n     * 接口名称\n     */\n    private String apiName;\n\n    /**\n     * 接口地址\n     */\n    private String apiUrl;\n\n    /**\n     * 接口请求方式（如：get，post）\n     */\n    private String apiMethod;\n\n    /**\n     * 所属分类 id\n     */\n    @TableField(\"fk_api_category_id\")\n    private String apiCategoryId;\n\n    /**\n     * 排序\n     */\n    private Integer sort;\n\n    /**\n     * 状态（1 正常，2 停用）\n     */\n    private Integer status;\n\n    /**\n     * 备注\n     */\n    private String remark;\n\n    /**\n     * 创建时间\n     */\n    private Date gmtCreate;\n\n    /**\n     * 创建人用户 id\n     */\n    @TableField(\"fk_create_user_id\")\n    private Long createUserId;\n\n    /**\n     * 修改时间\n     */\n    private Date gmtModify;\n\n    /**\n     * 修改人用户 id\n     */\n    @TableField(\"fk_modify_user_id\")\n    private Long modifyUserId;\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/api/po/SysApiCategory.java",
    "content": "package com.geshanzsq.admin.system.api.po;\n\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport lombok.Data;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\n/**\n * 系统接口分类\n *\n * @author geshanzsq\n * @date 2022/6/24\n */\n@Data\npublic class SysApiCategory implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * 接口 id\n     */\n    @TableId\n    private Long id;\n\n    /**\n     * 分类名称\n     */\n    private String categoryName;\n\n    /**\n     * 排序\n     */\n    private Integer sort;\n\n    /**\n     * 状态（1 正常，2 停用）\n     */\n    private Integer status;\n\n    /**\n     * 备注\n     */\n    private String remark;\n\n    /**\n     * 创建时间\n     */\n    private Date gmtCreate;\n\n    /**\n     * 创建人用户 id\n     */\n    @TableField(\"fk_create_user_id\")\n    private Long createUserId;\n\n    /**\n     * 修改时间\n     */\n    private Date gmtModify;\n\n    /**\n     * 修改人用户 id\n     */\n    @TableField(\"fk_modify_user_id\")\n    private Long modifyUserId;\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/api/po/SysApiMenu.java",
    "content": "package com.geshanzsq.admin.system.api.po;\n\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport lombok.Data;\n\nimport java.io.Serializable;\n\n/**\n * 接口菜单\n *\n * @author geshanzsq\n * @date 2022/6/26\n */\n@Data\npublic class SysApiMenu implements Serializable {\n\n    private static final Long serialVersionUID = 1L;\n    /**\n     * 接口菜单 id\n     */\n    @TableId\n    private Long id;\n\n    /**\n     * 接口 id\n     */\n    @TableField(\"fk_api_id\")\n    private Long apiId;\n\n    /**\n     * 菜单 id\n     */\n    @TableField(\"fk_menu_id\")\n    private Long menuId;\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/api/service/SysApiCategoryService.java",
    "content": "package com.geshanzsq.admin.system.api.service;\n\nimport com.geshanzsq.admin.system.api.po.SysApiCategory;\nimport com.geshanzsq.admin.system.api.vo.SysApiCategoryVO;\nimport com.geshanzsq.common.framework.web.service.BaseService;\n\nimport java.util.List;\n\n/**\n * 系统接口分类\n *\n * @author geshanzsq\n * @date 2022/6/24\n */\npublic interface SysApiCategoryService extends BaseService<SysApiCategory> {\n\n    /**\n     * 列表排序\n     */\n    List<SysApiCategoryVO> listSort();\n\n    /**\n     * 删除\n     */\n    void removeByIds(Long[] ids);\n\n    /**\n     * 获取最大排序\n     */\n    Integer getMaxSort();\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/api/service/SysApiMenuService.java",
    "content": "package com.geshanzsq.admin.system.api.service;\n\nimport com.geshanzsq.admin.system.api.po.SysApiMenu;\nimport com.geshanzsq.common.framework.web.service.BaseService;\n\n/**\n * 接口的菜单\n *\n * @author geshanzsq\n * @date 2022/6/26\n */\npublic interface SysApiMenuService extends BaseService<SysApiMenu> {\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/api/service/SysApiService.java",
    "content": "package com.geshanzsq.admin.system.api.service;\n\nimport com.geshanzsq.admin.system.api.po.SysApi;\nimport com.geshanzsq.admin.system.api.vo.SysApiVO;\nimport com.geshanzsq.admin.system.menu.vo.SysMenuAuthApiVO;\nimport com.geshanzsq.common.framework.web.service.BaseService;\n\nimport java.util.List;\n\n/**\n * 接口\n *\n * @author geshanzsq\n * @date 2022/6/12\n */\npublic interface SysApiService extends BaseService<SysApi> {\n\n    /**\n     * 通过菜单 id 列表获取接口\n     * @param menuIds 菜单 id 列表\n     * @return\n     */\n    List<SysApiVO> getApiByMenuIds(List<Long> menuIds);\n\n    /**\n     * 删除\n     */\n    void removeByIds(Long[] ids);\n\n    /**\n     * 获取最大排序\n     * @param apiCategoryId 分类 id\n     */\n    Integer getMaxSortByCategoryId(Long apiCategoryId);\n\n    /**\n     * 分配 API\n     * @param menuId 菜单 id\n     * @param apiIds 接口 ids\n     */\n    void authApi(Long menuId, List<Long> apiIds);\n\n    /**\n     * 获取分配的 API 接口\n     * @param menuId 菜单 id\n     * @return\n     */\n    List<SysMenuAuthApiVO> getAuthApiByMenuId(Long menuId);\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/api/service/impl/SysApiCategoryServiceImpl.java",
    "content": "package com.geshanzsq.admin.system.api.service.impl;\n\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.geshanzsq.admin.system.api.mapper.SysApiCategoryMapper;\nimport com.geshanzsq.admin.system.api.mapper.SysApiMapper;\nimport com.geshanzsq.admin.system.api.mapstruct.SysApiCategoryConverter;\nimport com.geshanzsq.admin.system.api.po.SysApi;\nimport com.geshanzsq.admin.system.api.po.SysApiCategory;\nimport com.geshanzsq.admin.system.api.service.SysApiCategoryService;\nimport com.geshanzsq.admin.system.api.vo.SysApiCategoryVO;\nimport com.geshanzsq.common.core.exception.ParamException;\nimport com.geshanzsq.common.core.util.message.MessageUtils;\nimport com.geshanzsq.common.framework.web.service.impl.BaseServiceImpl;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\nimport java.util.Arrays;\nimport java.util.List;\n\n/**\n * 系统接口分类\n *\n * @author geshanzsq\n * @date 2022/6/24\n */\n@Service\npublic class SysApiCategoryServiceImpl extends BaseServiceImpl<SysApiCategoryMapper, SysApiCategory> implements SysApiCategoryService {\n\n    @Autowired\n    private SysApiCategoryMapper sysApiCategoryMapper;\n    @Autowired\n    private SysApiMapper sysApiMapper;\n\n    /**\n     * 列表排序\n     */\n    @Override\n    public List<SysApiCategoryVO> listSort() {\n        LambdaQueryWrapper<SysApiCategory> wrapper = new LambdaQueryWrapper<>();\n        wrapper.orderByAsc(SysApiCategory::getSort, SysApiCategory::getId);\n        return SysApiCategoryConverter.INSTANCE.convertList(sysApiCategoryMapper.selectList(wrapper));\n    }\n\n    /**\n     * 删除\n     */\n    @Override\n    public void removeByIds(Long[] ids) {\n        if (ids.length == 0) {\n            return;\n        }\n        // 判断是否关联接口，如果已关联，不允许删除\n        LambdaQueryWrapper<SysApi> wrapper = new LambdaQueryWrapper<>();\n        wrapper.in(SysApi::getApiCategoryId, ids);\n        if (sysApiMapper.selectCount(wrapper) > 0) {\n            throw new ParamException(MessageUtils.message(\"api.category.allocated\"));\n        }\n        // 删除数据\n        sysApiCategoryMapper.deleteBatchIds(Arrays.asList(ids));\n    }\n\n    /**\n     * 获取最大排序\n     */\n    @Override\n    public Integer getMaxSort() {\n        return sysApiCategoryMapper.selectMaxSort();\n    }\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/api/service/impl/SysApiMenuServiceImpl.java",
    "content": "package com.geshanzsq.admin.system.api.service.impl;\n\nimport com.geshanzsq.admin.system.api.mapper.SysApiMenuMapper;\nimport com.geshanzsq.admin.system.api.po.SysApiMenu;\nimport com.geshanzsq.admin.system.api.service.SysApiMenuService;\nimport com.geshanzsq.common.framework.web.service.impl.BaseServiceImpl;\nimport org.springframework.stereotype.Service;\n\n/**\n * 接口菜单\n *\n * @author geshanzsq\n * @date 2022/6/26\n */\n@Service\npublic class SysApiMenuServiceImpl extends BaseServiceImpl<SysApiMenuMapper, SysApiMenu> implements SysApiMenuService {\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/api/service/impl/SysApiServiceImpl.java",
    "content": "package com.geshanzsq.admin.system.api.service.impl;\n\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.geshanzsq.admin.system.api.mapper.SysApiMapper;\nimport com.geshanzsq.admin.system.api.po.SysApi;\nimport com.geshanzsq.admin.system.api.po.SysApiMenu;\nimport com.geshanzsq.admin.system.api.service.SysApiMenuService;\nimport com.geshanzsq.admin.system.api.service.SysApiService;\nimport com.geshanzsq.admin.system.api.vo.SysApiVO;\nimport com.geshanzsq.admin.system.menu.vo.SysMenuAuthApiVO;\nimport com.geshanzsq.common.core.enums.CommonStatus;\nimport com.geshanzsq.common.framework.web.service.impl.BaseServiceImpl;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\nimport org.springframework.util.CollectionUtils;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\n/**\n * 接口\n *\n * @author geshanzsq\n * @date 2022/6/12\n */\n@Service\npublic class SysApiServiceImpl extends BaseServiceImpl<SysApiMapper, SysApi> implements SysApiService {\n\n    @Autowired\n    private SysApiMapper sysApiMapper;\n\n    @Autowired\n    private SysApiMenuService sysApiMenuService;\n\n    /**\n     * 通过菜单 id 列表获取接口\n     * @param menuIds 菜单 id 列表\n     * @return\n     */\n    @Override\n    public List<SysApiVO> getApiByMenuIds(List<Long> menuIds) {\n        if (CollectionUtils.isEmpty(menuIds)) {\n            return new ArrayList<>();\n        }\n        return sysApiMapper.getApiByMenuIds(menuIds, CommonStatus.NORMAL.code);\n    }\n\n    /**\n     * 删除\n     */\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void removeByIds(Long[] ids) {\n        if (ids.length == 0) {\n            return;\n        }\n        // 删除接口\n        sysApiMapper.deleteBatchIds(Arrays.asList(ids));\n\n        // 删除接口和菜单的对应关系\n        LambdaQueryWrapper<SysApiMenu> amWrapper = new LambdaQueryWrapper<>();\n        amWrapper.in(SysApiMenu::getApiId, ids);\n        sysApiMenuService.remove(amWrapper);\n    }\n\n    /**\n     * 获取最大排序\n     * @param apiCategoryId 分类 id\n     */\n    @Override\n    public Integer getMaxSortByCategoryId(Long apiCategoryId) {\n        return sysApiMapper.selectMaxSortByCategoryId(apiCategoryId);\n    }\n\n    /**\n     * 分配 API\n     * @param menuId 菜单 id\n     * @param apiIds 接口 ids\n     */\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void authApi(Long menuId, List<Long> apiIds) {\n        if (menuId == null) {\n            return;\n        }\n        // 先删除对应关系，然后再添加\n        LambdaQueryWrapper<SysApiMenu> apiMenuWrapper = new LambdaQueryWrapper<>();\n        apiMenuWrapper.eq(SysApiMenu::getMenuId, menuId);\n        sysApiMenuService.remove(apiMenuWrapper);\n\n        if (CollectionUtils.isEmpty(apiIds)) {\n            return;\n        }\n\n        // 添加数据\n        List<SysApiMenu> apiMenus = new ArrayList<>();\n        for (Long apiId : apiIds) {\n            SysApiMenu sysApiMenu = new SysApiMenu();\n            sysApiMenu.setMenuId(menuId);\n            sysApiMenu.setApiId(apiId);\n            apiMenus.add(sysApiMenu);\n        }\n        sysApiMenuService.saveBatch(apiMenus);\n    }\n\n    /**\n     * 获取分配的 API 接口\n     * @param menuId 菜单 id\n     * @return\n     */\n    @Override\n    public List<SysMenuAuthApiVO> getAuthApiByMenuId(Long menuId) {\n        return sysApiMapper.selectAuthApiByMenuId(menuId);\n    }\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/api/vo/SysApiCategoryVO.java",
    "content": "package com.geshanzsq.admin.system.api.vo;\n\nimport com.geshanzsq.common.core.web.vo.BaseVO;\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\nimport java.io.Serializable;\n\n/**\n * 接口分类\n *\n * @author geshanzsq\n * @date 2022/6/24\n */\n@Data\n@ApiModel(\"接口分类\")\npublic class SysApiCategoryVO extends BaseVO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(\"接口分类id\")\n    private Long id;\n\n    @ApiModelProperty(\"接口分类名称\")\n    private String categoryName;\n\n    @ApiModelProperty(\"排序\")\n    private Integer sort;\n\n    @ApiModelProperty(\"状态（1 正常，2 停用）\")\n    private Integer status;\n\n    @ApiModelProperty(\"备注\")\n    private String remark;\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/api/vo/SysApiVO.java",
    "content": "package com.geshanzsq.admin.system.api.vo;\n\nimport com.geshanzsq.common.core.web.vo.BaseVO;\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\nimport java.io.Serializable;\n\n/**\n * 接口\n *\n * @author geshanzsq\n * @date 2022/6/12\n */\n@Data\n@ApiModel(\"接口\")\npublic class SysApiVO extends BaseVO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(\"接口id\")\n    private Long id;\n\n    @ApiModelProperty(\"接口名称\")\n    private String apiName;\n\n    @ApiModelProperty(\"接口地址\")\n    private String apiUrl;\n\n    @ApiModelProperty(\"接口请求方式（如：get，post）\")\n    private String apiMethod;\n\n    @ApiModelProperty(\"所属分类\")\n    private Long apiCategoryId;\n\n    @ApiModelProperty(\"排序\")\n    private Integer sort;\n\n    @ApiModelProperty(\"状态（1 正常，2 停用）\")\n    private Integer status;\n\n    @ApiModelProperty(\"备注\")\n    private String remark;\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/dictionary/controller/SysDictionaryController.java",
    "content": "package com.geshanzsq.admin.system.dictionary.controller;\n\nimport com.geshanzsq.admin.system.dictionary.dto.SysDictionaryAddDTO;\nimport com.geshanzsq.admin.system.dictionary.dto.SysDictionaryPageDTO;\nimport com.geshanzsq.admin.system.dictionary.dto.SysDictionaryUpdateDTO;\nimport com.geshanzsq.admin.system.dictionary.mapstruct.SysDictionaryConverter;\nimport com.geshanzsq.admin.system.dictionary.po.SysDictionary;\nimport com.geshanzsq.admin.system.dictionary.service.SysDictionaryService;\nimport com.geshanzsq.admin.system.dictionary.vo.DictionaryInfoVO;\nimport com.geshanzsq.admin.system.dictionary.vo.SysDictionaryVO;\nimport com.geshanzsq.common.core.web.response.ResponseResult;\nimport com.geshanzsq.common.framework.mybatis.page.vo.PageVO;\nimport com.geshanzsq.common.framework.web.controller.BaseController;\nimport com.geshanzsq.common.log.annotation.Log;\nimport com.geshanzsq.common.log.enums.BusinessType;\nimport io.swagger.annotations.Api;\nimport io.swagger.annotations.ApiOperation;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.validation.Valid;\nimport java.util.List;\n\n/**\n * 系统字典\n *\n * @author geshanzsq\n * @date 2022/6/26\n */\n@Api(tags = \"系统字典\")\n@RestController\n@RequestMapping(\"/system/dictionary\")\npublic class SysDictionaryController extends BaseController {\n\n    @Autowired\n    private SysDictionaryService sysDictionaryService;\n\n    @ApiOperation(\"分页列表\")\n    @GetMapping(\"/page\")\n    @PreAuthorize(\"@auth.hasUrl()\")\n    public ResponseResult<PageVO<SysDictionaryVO>> page(SysDictionaryPageDTO pageDTO) {\n        pageDTO.setOrderColumn(\"sort,id\");\n        PageVO<SysDictionary> pageVO = sysDictionaryService.page(pageDTO);\n        return ResponseResult.success( SysDictionaryConverter.INSTANCE.convert(pageVO));\n    }\n\n    @ApiOperation(\"列表\")\n    @GetMapping(\"/list\")\n    @PreAuthorize(\"@auth.hasUrl()\")\n    public ResponseResult<List<SysDictionaryVO>> list() {\n        return ResponseResult.success(sysDictionaryService.listSort());\n    }\n\n    @GetMapping(\"/getById/{id}\")\n    @ApiOperation(\"详情\")\n    @PreAuthorize(\"@auth.hasUrl()\")\n    public ResponseResult<SysDictionaryVO> getById(@PathVariable Long id) {\n        return ResponseResult.success(SysDictionaryConverter.INSTANCE.convert(sysDictionaryService.getById(id)));\n    }\n\n    @PostMapping()\n    @ApiOperation(\"新增\")\n    @PreAuthorize(\"@auth.hasUrl()\")\n    @Log(moduleName = \"系统字典\", businessType = BusinessType.ADD)\n    public ResponseResult add(@Valid @RequestBody SysDictionaryAddDTO addDTO) {\n        SysDictionary sysDictionary = SysDictionaryConverter.INSTANCE.convert(addDTO);\n        sysDictionaryService.save(sysDictionary);\n        return ResponseResult.success();\n    }\n\n    @PutMapping()\n    @ApiOperation(\"修改\")\n    @PreAuthorize(\"@auth.hasUrl()\")\n    @Log(moduleName = \"系统字典\", businessType = BusinessType.UPDATE)\n    public ResponseResult update(@Valid @RequestBody SysDictionaryUpdateDTO updateDTO) {\n        SysDictionary sysDictionary = SysDictionaryConverter.INSTANCE.convert(updateDTO);\n        sysDictionaryService.updateById(sysDictionary);\n        return ResponseResult.success();\n    }\n\n    @DeleteMapping(\"/delete/{ids}\")\n    @ApiOperation(\"删除\")\n    @PreAuthorize(\"@auth.hasUrl()\")\n    @Log(moduleName = \"系统字典\", businessType = BusinessType.DELETE)\n    public ResponseResult delete(@PathVariable Long[] ids) {\n        sysDictionaryService.removeByIds(ids);\n        return ResponseResult.success();\n    }\n\n    @GetMapping(\"/getMaxSort\")\n    @ApiOperation(\"获取最大排序\")\n    @PreAuthorize(\"@auth.hasUrl()\")\n    public ResponseResult<Integer> getMaxSort() {\n        Integer maxSort = sysDictionaryService.getMaxSort();\n        return ResponseResult.success(maxSort);\n    }\n\n    @ApiOperation(\"获取所有字典详细信息\")\n    @GetMapping(\"/getAllDictionaryInfo\")\n    public ResponseResult<List<DictionaryInfoVO>> getAllDictionaryInfo() {\n        return ResponseResult.success(sysDictionaryService.getAllDictionaryInfo());\n    }\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/dictionary/controller/SysDictionaryDataController.java",
    "content": "package com.geshanzsq.admin.system.dictionary.controller;\n\nimport com.geshanzsq.admin.system.dictionary.dto.SysDictionaryDataAddDTO;\nimport com.geshanzsq.admin.system.dictionary.dto.SysDictionaryDataPageDTO;\nimport com.geshanzsq.admin.system.dictionary.dto.SysDictionaryDataUpdateDTO;\nimport com.geshanzsq.admin.system.dictionary.mapstruct.SysDictionaryDataConverter;\nimport com.geshanzsq.admin.system.dictionary.po.SysDictionaryData;\nimport com.geshanzsq.admin.system.dictionary.service.SysDictionaryDataService;\nimport com.geshanzsq.admin.system.dictionary.vo.SysDictionaryDataVO;\nimport com.geshanzsq.common.core.web.response.ResponseResult;\nimport com.geshanzsq.common.framework.mybatis.page.vo.PageVO;\nimport com.geshanzsq.common.framework.web.controller.BaseController;\nimport com.geshanzsq.common.log.annotation.Log;\nimport com.geshanzsq.common.log.enums.BusinessType;\nimport io.swagger.annotations.Api;\nimport io.swagger.annotations.ApiOperation;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.validation.Valid;\nimport java.util.Arrays;\n\n/**\n * 字典数据\n *\n * @author geshanzsq\n * @date 2022/6/27\n */\n@Api(tags = \"系统字典数据\")\n@RestController\n@RequestMapping(\"/system/dictionary/data\")\npublic class SysDictionaryDataController extends BaseController {\n\n    @Autowired\n    private SysDictionaryDataService sysDictionaryDataService;\n\n    @ApiOperation(\"分页列表\")\n    @GetMapping(\"/page\")\n    @PreAuthorize(\"@auth.hasUrl()\")\n    public ResponseResult<PageVO<SysDictionaryDataVO>> page(@Valid SysDictionaryDataPageDTO pageDTO) {\n        pageDTO.setOrderColumn(\"sort,id\");\n        PageVO<SysDictionaryData> pageVO = sysDictionaryDataService.page(pageDTO);\n        return ResponseResult.success(SysDictionaryDataConverter.INSTANCE.convert(pageVO));\n    }\n\n    @GetMapping(\"/getById/{id}\")\n    @ApiOperation(\"详情\")\n    @PreAuthorize(\"@auth.hasUrl()\")\n    public ResponseResult<SysDictionaryDataVO> getById(@PathVariable Long id) {\n        return ResponseResult.success(SysDictionaryDataConverter.INSTANCE.convert(sysDictionaryDataService.getById(id)));\n    }\n\n    @PostMapping()\n    @ApiOperation(\"新增\")\n    @PreAuthorize(\"@auth.hasUrl()\")\n    @Log(moduleName = \"系统字典数据\", businessType = BusinessType.ADD)\n    public ResponseResult add(@Valid @RequestBody SysDictionaryDataAddDTO addDTO) {\n        SysDictionaryData sysDictionaryData = SysDictionaryDataConverter.INSTANCE.convert(addDTO);\n        sysDictionaryDataService.save(sysDictionaryData);\n        return ResponseResult.success();\n    }\n\n    @PutMapping()\n    @ApiOperation(\"修改\")\n    @PreAuthorize(\"@auth.hasUrl()\")\n    @Log(moduleName = \"系统字典数据\", businessType = BusinessType.UPDATE)\n    public ResponseResult update(@Valid @RequestBody SysDictionaryDataUpdateDTO updateDTO) {\n        SysDictionaryData sysDictionaryData = SysDictionaryDataConverter.INSTANCE.convert(updateDTO);\n        sysDictionaryDataService.updateById(sysDictionaryData);\n        return ResponseResult.success();\n    }\n\n    @DeleteMapping(\"/delete/{ids}\")\n    @ApiOperation(\"删除\")\n    @PreAuthorize(\"@auth.hasUrl()\")\n    @Log(moduleName = \"系统字典数据\", businessType = BusinessType.DELETE)\n    public ResponseResult delete(@PathVariable Long[] ids) {\n        sysDictionaryDataService.removeByIds(Arrays.asList(ids));\n        return ResponseResult.success();\n    }\n\n    @GetMapping(\"/getMaxSortByDictionaryId\")\n    @ApiOperation(\"获取最大排序\")\n    @PreAuthorize(\"@auth.hasUrl()\")\n    public ResponseResult<Integer> getMaxSortByDictionaryId(Long dictionaryId) {\n        Integer maxSort = sysDictionaryDataService.getMaxSortByDictionaryId(dictionaryId);\n        return ResponseResult.success(maxSort);\n    }\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/dictionary/dto/SysDictionaryAddDTO.java",
    "content": "package com.geshanzsq.admin.system.dictionary.dto;\n\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotBlank;\nimport javax.validation.constraints.NotNull;\nimport java.io.Serializable;\n\n/**\n * 字典新增\n *\n * @author geshanzsq\n * @date 2022/6/26\n */\n@Data\n@ApiModel(\"字典新增\")\npublic class SysDictionaryAddDTO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(value = \"字典名称\", required = true)\n    @NotBlank(message = \"字典名称不能为空\")\n    private String dictionaryName;\n\n    @ApiModelProperty(value = \"字典编码\", required = true)\n    @NotBlank(message = \"字典编码不能为空\")\n    private String dictionaryCode;\n\n    @ApiModelProperty(value = \"排序\", required = true)\n    @NotNull(message = \"排序不能为空\")\n    private Integer sort;\n\n    @ApiModelProperty(value = \"状态（1 正常，2 停用）\", required = true)\n    @NotNull(message = \"状态不能为空\")\n    private Integer status;\n\n    @ApiModelProperty(\"备注\")\n    private String remark;\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/dictionary/dto/SysDictionaryDataAddDTO.java",
    "content": "package com.geshanzsq.admin.system.dictionary.dto;\n\nimport com.geshanzsq.common.core.web.vo.BaseVO;\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotBlank;\nimport javax.validation.constraints.NotNull;\nimport java.io.Serializable;\n\n/**\n * 字典数据新增\n *\n * @author geshanzsq\n * @date 2022/6/27\n */\n@Data\n@ApiModel(\"字典数据新增\")\npublic class SysDictionaryDataAddDTO extends BaseVO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(value = \"字典标签\", required = true)\n    @NotBlank(message = \"字典标签不能为空\")\n    private String dictionaryLabel;\n\n    @ApiModelProperty(value = \"字典值\", required = true)\n    @NotBlank(message = \"字典值不能为空\")\n    private String dictionaryValue;\n\n    @ApiModelProperty(value = \"所属字典 id\", required = true)\n    @NotNull(message = \"所属字典不能为空\")\n    private Long dictionaryId;\n\n    @ApiModelProperty(\"样式类型（primary，success等）\")\n    private String classType;\n\n    @ApiModelProperty(value = \"排序\", required = true)\n    @NotNull(message = \"排序不能为空\")\n    private Integer sort;\n\n    @ApiModelProperty(value = \"状态（1 正常，2 停用）\", required = true)\n    @NotNull(message = \"状态不能为空\")\n    private Integer status;\n\n    @ApiModelProperty(\"备注\")\n    private String remark;\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/dictionary/dto/SysDictionaryDataPageDTO.java",
    "content": "package com.geshanzsq.admin.system.dictionary.dto;\n\nimport com.geshanzsq.common.framework.mybatis.page.dto.PageDTO;\nimport com.geshanzsq.common.framework.mybatis.plugin.annotation.Query;\nimport com.geshanzsq.common.framework.mybatis.plugin.enums.QueryWay;\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\nimport java.io.Serializable;\n\n/**\n * 字典数据分页\n *\n * @author geshanzsq\n * @date 2022/6/27\n */\n@Data\n@ApiModel(\"字典数据分页\")\npublic class SysDictionaryDataPageDTO extends PageDTO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(\"字典标签\")\n    @Query(QueryWay.LIKE)\n    private String dictionaryLabel;\n\n    @ApiModelProperty(value = \"所属字典\", required = true)\n    @NotNull(message = \"所属字典不能为空\")\n    private Long dictionaryId;\n\n    @ApiModelProperty(\"状态（1 正常，2 停用）\")\n    private Integer status;\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/dictionary/dto/SysDictionaryDataUpdateDTO.java",
    "content": "package com.geshanzsq.admin.system.dictionary.dto;\n\nimport com.geshanzsq.common.core.web.vo.BaseVO;\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotBlank;\nimport javax.validation.constraints.NotNull;\nimport java.io.Serializable;\n\n/**\n * 字典数据修改\n *\n * @author geshanzsq\n * @date 2022/6/27\n */\n@Data\n@ApiModel(\"字典数据修改\")\npublic class SysDictionaryDataUpdateDTO extends BaseVO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(value = \"字典数据 id\", required = true)\n    @NotNull(message = \"字典数据 id 不能为空\")\n    private Long id;\n\n    @ApiModelProperty(value = \"字典标签\", required = true)\n    @NotBlank(message = \"字典标签不能为空\")\n    private String dictionaryLabel;\n\n    @ApiModelProperty(value = \"字典值\", required = true)\n    @NotBlank(message = \"字典值不能为空\")\n    private String dictionaryValue;\n\n    @ApiModelProperty(value = \"所属字典 id\", required = true)\n    @NotNull(message = \"所属字典不能为空\")\n    private Long dictionaryId;\n\n    @ApiModelProperty(\"样式类型（primary，success等）\")\n    private String classType;\n\n    @ApiModelProperty(value = \"排序\", required = true)\n    @NotNull(message = \"排序不能为空\")\n    private Integer sort;\n\n    @ApiModelProperty(value = \"状态（1 正常，2 停用）\", required = true)\n    @NotNull(message = \"状态不能为空\")\n    private Integer status;\n\n    @ApiModelProperty(\"备注\")\n    private String remark;\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/dictionary/dto/SysDictionaryPageDTO.java",
    "content": "package com.geshanzsq.admin.system.dictionary.dto;\n\nimport com.geshanzsq.common.framework.mybatis.page.dto.PageDTO;\nimport com.geshanzsq.common.framework.mybatis.plugin.annotation.Query;\nimport com.geshanzsq.common.framework.mybatis.plugin.enums.QueryWay;\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\nimport java.io.Serializable;\n\n/**\n * 字典分页\n *\n * @author geshanzsq\n * @date 2022/6/26\n */\n@Data\n@ApiModel(\"字典分页\")\npublic class SysDictionaryPageDTO extends PageDTO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(\"字典名称\")\n    @Query(QueryWay.LIKE)\n    private String dictionaryName;\n\n    @ApiModelProperty(\"字典编码\")\n    @Query(QueryWay.LIKE)\n    private String dictionaryCode;\n\n    @ApiModelProperty(\"状态，1 正常，2 停用\")\n    private Integer status;\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/dictionary/dto/SysDictionaryUpdateDTO.java",
    "content": "package com.geshanzsq.admin.system.dictionary.dto;\n\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotBlank;\nimport javax.validation.constraints.NotNull;\nimport java.io.Serializable;\n\n/**\n * 字典修改\n *\n * @author geshanzsq\n * @date 2022/6/26\n */\n@Data\n@ApiModel(\"字典修改\")\npublic class SysDictionaryUpdateDTO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(value = \"字典 id\", required = true)\n    @NotNull(message = \"字典 id 不能为空\")\n    private Long id;\n\n    @ApiModelProperty(value = \"字典名称\", required = true)\n    @NotBlank(message = \"字典名称不能为空\")\n    private String dictionaryName;\n\n    @ApiModelProperty(value = \"字典编码\", required = true)\n    @NotBlank(message = \"字典编码不能为空\")\n    private String dictionaryCode;\n\n    @ApiModelProperty(value = \"排序\", required = true)\n    @NotNull(message = \"排序不能为空\")\n    private Integer sort;\n\n    @ApiModelProperty(value = \"状态（1 正常，2 停用）\", required = true)\n    @NotNull(message = \"状态不能为空\")\n    private Integer status;\n\n    @ApiModelProperty(\"备注\")\n    private String remark;\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/dictionary/mapper/SysDictionaryDataMapper.java",
    "content": "package com.geshanzsq.admin.system.dictionary.mapper;\n\nimport com.geshanzsq.admin.system.dictionary.po.SysDictionaryData;\nimport com.geshanzsq.common.framework.web.mapper.BaseMapperPlus;\nimport org.apache.ibatis.annotations.Mapper;\n\n/**\n * 字典数据\n *\n * @author geshanzsq\n * @date 2022/6/27\n */\n@Mapper\npublic interface SysDictionaryDataMapper extends BaseMapperPlus<SysDictionaryData> {\n\n    /**\n     * 获取最大排序\n     * @param dictionaryId 字典 id\n     */\n    Integer selectMaxSortByDictionaryId(Long dictionaryId);\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/dictionary/mapper/SysDictionaryMapper.java",
    "content": "package com.geshanzsq.admin.system.dictionary.mapper;\n\nimport com.geshanzsq.admin.system.dictionary.po.SysDictionary;\nimport com.geshanzsq.admin.system.dictionary.vo.DictionaryInfoVO;\nimport com.geshanzsq.common.framework.web.mapper.BaseMapperPlus;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.util.List;\n\n/**\n * 字典\n *\n * @author geshanzsq\n * @date 2022/6/26\n */\n@Mapper\npublic interface SysDictionaryMapper extends BaseMapperPlus<SysDictionary> {\n\n    /**\n     * 获取最大排序\n     */\n    Integer selectMaxSort();\n\n    /**\n     * 获取所有字典详细信息\n     * @param status 状态\n     */\n    List<DictionaryInfoVO> getAllDictionaryInfo(Integer status);\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/dictionary/mapstruct/SysDictionaryConverter.java",
    "content": "package com.geshanzsq.admin.system.dictionary.mapstruct;\n\nimport com.geshanzsq.admin.system.dictionary.dto.SysDictionaryAddDTO;\nimport com.geshanzsq.admin.system.dictionary.dto.SysDictionaryUpdateDTO;\nimport com.geshanzsq.admin.system.dictionary.po.SysDictionary;\nimport com.geshanzsq.admin.system.dictionary.vo.SysDictionaryVO;\nimport com.geshanzsq.common.framework.mybatis.page.vo.PageVO;\nimport org.mapstruct.Mapper;\nimport org.mapstruct.factory.Mappers;\n\nimport java.util.List;\n\n/**\n * 字典对象转换\n *\n * @author geshanzsq\n * @date 2022/6/26\n */\n@Mapper\npublic interface SysDictionaryConverter {\n\n    SysDictionaryConverter INSTANCE = Mappers.getMapper(SysDictionaryConverter.class);\n\n\n    SysDictionaryVO convert(SysDictionary sysDictionary);\n\n    List<SysDictionaryVO> convertList(List<SysDictionary> list);\n\n    PageVO<SysDictionaryVO> convert(PageVO<SysDictionary> pageVo);\n\n    SysDictionary convert(SysDictionaryAddDTO addDTO);\n\n    SysDictionary convert(SysDictionaryUpdateDTO updateDTO);\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/dictionary/mapstruct/SysDictionaryDataConverter.java",
    "content": "package com.geshanzsq.admin.system.dictionary.mapstruct;\n\nimport com.geshanzsq.admin.system.dictionary.dto.SysDictionaryDataAddDTO;\nimport com.geshanzsq.admin.system.dictionary.dto.SysDictionaryDataUpdateDTO;\nimport com.geshanzsq.admin.system.dictionary.po.SysDictionaryData;\nimport com.geshanzsq.admin.system.dictionary.vo.SysDictionaryDataVO;\nimport com.geshanzsq.common.framework.mybatis.page.vo.PageVO;\nimport org.mapstruct.Mapper;\nimport org.mapstruct.factory.Mappers;\n\n/**\n * 字典数据对象转换\n *\n * @author geshanzsq\n * @date 2022/6/27\n */\n@Mapper\npublic interface SysDictionaryDataConverter {\n\n    SysDictionaryDataConverter INSTANCE = Mappers.getMapper(SysDictionaryDataConverter.class);\n\n    SysDictionaryDataVO convert(SysDictionaryData sysDictionaryData);\n\n    PageVO<SysDictionaryDataVO> convert(PageVO<SysDictionaryData> pageVo);\n\n    SysDictionaryData convert(SysDictionaryDataAddDTO addDTO);\n\n    SysDictionaryData convert(SysDictionaryDataUpdateDTO updateDTO);\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/dictionary/po/SysDictionary.java",
    "content": "package com.geshanzsq.admin.system.dictionary.po;\n\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport lombok.Data;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\n/**\n * 系统字典\n *\n * @author geshanzsq\n * @date 2022/6/26\n */\n@Data\npublic class SysDictionary implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * 字典 id\n     */\n    @TableId\n    private Long id;\n\n    /**\n     * 字典名称\n     */\n    private String dictionaryName;\n\n    /**\n     * 字典编码\n     */\n    private String dictionaryCode;\n\n    /**\n     * 排序\n     */\n    private Integer sort;\n\n    /**\n     * 状态（1 正常，2 停用）\n     */\n    private Integer status;\n\n    /**\n     * 备注\n     */\n    private String remark;\n\n    /**\n     * 创建时间\n     */\n    private Date gmtCreate;\n\n    /**\n     * 创建人用户 id\n     */\n    @TableField(\"fk_create_user_id\")\n    private Long createUserId;\n\n    /**\n     * 修改时间\n     */\n    private Date gmtModify;\n\n    /**\n     * 修改人用户 id\n     */\n    @TableField(\"fk_modify_user_id\")\n    private Long modifyUserId;\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/dictionary/po/SysDictionaryData.java",
    "content": "package com.geshanzsq.admin.system.dictionary.po;\n\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport lombok.Data;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\n/**\n * 字典数据\n *\n * @author geshanzsq\n * @date 2022/6/27\n */\n@Data\npublic class SysDictionaryData implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * 字典数据 id\n     */\n    @TableId\n    private Long id;\n\n    /**\n     * 字典标签\n     */\n    private String dictionaryLabel;\n\n    /**\n     * 字典值\n     */\n    private String dictionaryValue;\n\n    /**\n     * 所属字典 id\n     */\n    @TableField(\"fk_dictionary_id\")\n    private String dictionaryId;\n\n    /**\n     * 样式类型（primary，success等）\n     */\n    private String classType;\n\n    /**\n     * 排序\n     */\n    private Integer sort;\n\n    /**\n     * 状态（1 正常，2 停用）\n     */\n    private Integer status;\n\n    /**\n     * 备注\n     */\n    private String remark;\n\n    /**\n     * 创建时间\n     */\n    private Date gmtCreate;\n\n    /**\n     * 创建人用户 id\n     */\n    @TableField(\"fk_create_user_id\")\n    private Long createUserId;\n\n    /**\n     * 修改时间\n     */\n    private Date gmtModify;\n\n    /**\n     * 修改人用户 id\n     */\n    @TableField(\"fk_modify_user_id\")\n    private Long modifyUserId;\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/dictionary/service/SysDictionaryDataService.java",
    "content": "package com.geshanzsq.admin.system.dictionary.service;\n\nimport com.geshanzsq.admin.system.dictionary.po.SysDictionaryData;\nimport com.geshanzsq.common.framework.web.service.BaseService;\n\n/**\n * 字典数据\n *\n * @author geshanzsq\n * @date 2022/6/27\n */\npublic interface SysDictionaryDataService extends BaseService<SysDictionaryData> {\n\n    /**\n     * 获取最大排序\n     * @param dictionaryId 字典 id\n     */\n    Integer getMaxSortByDictionaryId(Long dictionaryId);\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/dictionary/service/SysDictionaryService.java",
    "content": "package com.geshanzsq.admin.system.dictionary.service;\n\nimport com.geshanzsq.admin.system.dictionary.po.SysDictionary;\nimport com.geshanzsq.admin.system.dictionary.vo.DictionaryInfoVO;\nimport com.geshanzsq.admin.system.dictionary.vo.SysDictionaryVO;\nimport com.geshanzsq.common.framework.web.service.BaseService;\n\nimport java.util.List;\n\n/**\n * 字典\n *\n * @author geshanzsq\n * @date 2022/6/26\n */\npublic interface SysDictionaryService extends BaseService<SysDictionary> {\n\n    /**\n     * 列表排序\n     */\n    List<SysDictionaryVO> listSort();\n\n    /**\n     * 删除\n     */\n    void removeByIds(Long[] ids);\n\n    /**\n     * 获取最大排序\n     */\n    Integer getMaxSort();\n\n    /**\n     * 获取所有字典详细信息\n     */\n    List<DictionaryInfoVO> getAllDictionaryInfo();\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/dictionary/service/impl/SysDictionaryDataServiceImpl.java",
    "content": "package com.geshanzsq.admin.system.dictionary.service.impl;\n\nimport com.geshanzsq.admin.system.dictionary.mapper.SysDictionaryDataMapper;\nimport com.geshanzsq.admin.system.dictionary.po.SysDictionaryData;\nimport com.geshanzsq.admin.system.dictionary.service.SysDictionaryDataService;\nimport com.geshanzsq.common.framework.web.service.impl.BaseServiceImpl;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\n/**\n * 字典数据\n *\n * @author geshanzsq\n * @date 2022/6/27\n */\n@Service\npublic class SysDictionaryDataServiceImpl extends BaseServiceImpl<SysDictionaryDataMapper, SysDictionaryData> implements SysDictionaryDataService {\n\n    @Autowired\n    private SysDictionaryDataMapper sysDictionaryDataMapper;\n\n    /**\n     * 获取最大排序\n     * @param dictionaryId 字典 id\n     */\n    @Override\n    public Integer getMaxSortByDictionaryId(Long dictionaryId) {\n        return sysDictionaryDataMapper.selectMaxSortByDictionaryId(dictionaryId);\n    }\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/dictionary/service/impl/SysDictionaryServiceImpl.java",
    "content": "package com.geshanzsq.admin.system.dictionary.service.impl;\n\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.geshanzsq.admin.system.dictionary.mapper.SysDictionaryDataMapper;\nimport com.geshanzsq.admin.system.dictionary.mapper.SysDictionaryMapper;\nimport com.geshanzsq.admin.system.dictionary.mapstruct.SysDictionaryConverter;\nimport com.geshanzsq.admin.system.dictionary.po.SysDictionary;\nimport com.geshanzsq.admin.system.dictionary.po.SysDictionaryData;\nimport com.geshanzsq.admin.system.dictionary.service.SysDictionaryService;\nimport com.geshanzsq.admin.system.dictionary.vo.DictionaryInfoVO;\nimport com.geshanzsq.admin.system.dictionary.vo.SysDictionaryVO;\nimport com.geshanzsq.common.core.enums.CommonStatus;\nimport com.geshanzsq.common.core.exception.ParamException;\nimport com.geshanzsq.common.core.util.message.MessageUtils;\nimport com.geshanzsq.common.framework.web.service.impl.BaseServiceImpl;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\nimport java.util.Arrays;\nimport java.util.List;\n\n/**\n * 字典\n *\n * @author geshanzsq\n * @date 2022/6/26\n */\n@Service\npublic class SysDictionaryServiceImpl extends BaseServiceImpl<SysDictionaryMapper, SysDictionary> implements SysDictionaryService {\n\n    @Autowired\n    private SysDictionaryMapper sysDictionaryMapper;\n    @Autowired\n    private SysDictionaryDataMapper sysDictionaryDataMapper;\n\n    /**\n     * 列表排序\n     */\n    @Override\n    public List<SysDictionaryVO> listSort() {\n        LambdaQueryWrapper<SysDictionary> wrapper = new LambdaQueryWrapper<>();\n        wrapper.orderByAsc(SysDictionary::getSort, SysDictionary::getId);\n        return SysDictionaryConverter.INSTANCE.convertList(sysDictionaryMapper.selectList(wrapper));\n    }\n\n    /**\n     * 删除\n     */\n    @Override\n    public void removeByIds(Long[] ids) {\n        if (ids.length == 0) {\n            return;\n        }\n        // 判断字典是否有字典数据，如果有，则不能删除\n        LambdaQueryWrapper<SysDictionaryData> wrapper = new LambdaQueryWrapper<>();\n        wrapper.in(SysDictionaryData::getDictionaryId, ids);\n        if (sysDictionaryDataMapper.selectCount(wrapper) > 0) {\n            throw new ParamException(MessageUtils.message(\"dictionary.exist.data\"));\n        }\n\n        // 删除数据\n        sysDictionaryMapper.deleteBatchIds(Arrays.asList(ids));\n    }\n\n\n    /**\n     * 获取最大排序\n     */\n    @Override\n    public Integer getMaxSort() {\n        return sysDictionaryMapper.selectMaxSort();\n    }\n\n    /**\n     * 获取所有字典详细信息\n     */\n    @Override\n    public List<DictionaryInfoVO> getAllDictionaryInfo() {\n        return sysDictionaryMapper.getAllDictionaryInfo(CommonStatus.NORMAL.code);\n    }\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/dictionary/vo/DictionaryDataInfoVO.java",
    "content": "package com.geshanzsq.admin.system.dictionary.vo;\n\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\nimport java.io.Serializable;\n\n/**\n * 字典数据详情\n *\n * @author geshanzsq\n * @date 2022/6/27\n */\n@Data\n@ApiModel(\"字典数据详情\")\npublic class DictionaryDataInfoVO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(\"字典数据 id\")\n    private Long id;\n\n    @ApiModelProperty(\"字典标签\")\n    private String dictionaryLabel;\n\n    @ApiModelProperty(\"字典值\")\n    private String dictionaryValue;\n\n    @ApiModelProperty(\"样式类型（primary，success等）\")\n    private String classType;\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/dictionary/vo/DictionaryInfoVO.java",
    "content": "package com.geshanzsq.admin.system.dictionary.vo;\n\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\nimport java.io.Serializable;\nimport java.util.List;\n\n/**\n * 字典详情\n *\n * @author geshanzsq\n * @date 2022/6/27\n */\n@Data\n@ApiModel(\"字典详情\")\npublic class DictionaryInfoVO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(\"字典 id\")\n    private Long id;\n\n    @ApiModelProperty(\"字典名称\")\n    private String dictionaryName;\n\n    @ApiModelProperty(\"字典编码\")\n    private String dictionaryCode;\n\n    @ApiModelProperty(\"字典数据\")\n    private List<DictionaryDataInfoVO> dictionaryDataList;\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/dictionary/vo/SysDictionaryDataVO.java",
    "content": "package com.geshanzsq.admin.system.dictionary.vo;\n\nimport com.geshanzsq.common.core.web.vo.BaseVO;\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\nimport java.io.Serializable;\n\n/**\n * 字典数据\n *\n * @author geshanzsq\n * @date 2022/6/27\n */\n@Data\n@ApiModel(\"字典数据\")\npublic class SysDictionaryDataVO extends BaseVO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(\"字典数据 id\")\n    private Long id;\n\n    @ApiModelProperty(\"字典标签\")\n    private String dictionaryLabel;\n\n    @ApiModelProperty(\"字典值\")\n    private String dictionaryValue;\n\n    @ApiModelProperty(\"所属字典 id\")\n    private String dictionaryId;\n\n    @ApiModelProperty(\"样式类型（primary，success等）\")\n    private String classType;\n\n    @ApiModelProperty(\"排序\")\n    private Integer sort;\n\n    @ApiModelProperty(\"状态（1 正常，2 停用）\")\n    private Integer status;\n\n    @ApiModelProperty(\"备注\")\n    private String remark;\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/dictionary/vo/SysDictionaryVO.java",
    "content": "package com.geshanzsq.admin.system.dictionary.vo;\n\nimport com.geshanzsq.common.core.web.vo.BaseVO;\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\nimport java.io.Serializable;\n\n/**\n * 字典\n *\n * @author geshanzsq\n * @date 2022/6/26\n */\n@Data\n@ApiModel(\"字典\")\npublic class SysDictionaryVO extends BaseVO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(\"字典 id\")\n    private Long id;\n\n    @ApiModelProperty(\"字典名称\")\n    private String dictionaryName;\n\n    @ApiModelProperty(\"字典编码\")\n    private String dictionaryCode;\n\n    @ApiModelProperty(\"排序\")\n    private Integer sort;\n\n    @ApiModelProperty(\"状态（1 正常，2 停用）\")\n    private Integer status;\n\n    @ApiModelProperty(\"备注\")\n    private String remark;\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/log/login/controller/LogLoginController.java",
    "content": "package com.geshanzsq.admin.system.log.login.controller;\n\nimport com.geshanzsq.admin.system.log.login.dto.LogLoginPageDTO;\nimport com.geshanzsq.admin.system.log.login.mapstruct.LogLoginConverter;\nimport com.geshanzsq.admin.system.log.login.po.LogLogin;\nimport com.geshanzsq.admin.system.log.login.service.LogLoginService;\nimport com.geshanzsq.admin.system.log.login.vo.LogLoginVO;\nimport com.geshanzsq.common.core.web.response.ResponseResult;\nimport com.geshanzsq.common.framework.mybatis.page.vo.PageVO;\nimport com.geshanzsq.common.framework.web.controller.BaseController;\nimport io.swagger.annotations.Api;\nimport io.swagger.annotations.ApiOperation;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * 登录日志\n *\n * @author geshanzsq\n * @date 2022/7/3\n */\n@Api(tags = \"登录日志\")\n@RestController\n@RequestMapping(\"/system/log/login\")\npublic class LogLoginController extends BaseController {\n\n    @Autowired\n    private LogLoginService logLoginService;\n\n    @GetMapping(\"/page\")\n    @ApiOperation((\"分页列表\"))\n    @PreAuthorize(\"@auth.hasUrl()\")\n    public ResponseResult<PageVO<LogLoginVO>> page(LogLoginPageDTO pageDto) {\n        PageVO<LogLogin> pageVO = logLoginService.page(pageDto);\n        return ResponseResult.success(LogLoginConverter.INSTANCE.convert(pageVO));\n    }\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/log/login/dto/LogLoginAddDTO.java",
    "content": "package com.geshanzsq.admin.system.log.login.dto;\n\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\n/**\n * 登录日志新增\n *\n * @author geshanzsq\n * @date 2022/7/3\n */\n@Data\n@ApiModel(\"登录日志新增\")\npublic class LogLoginAddDTO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(\"登录用户名\")\n    private String username;\n\n    @ApiModelProperty(\"登录 ip 地址\")\n    private String ipAddress;\n\n    @ApiModelProperty(\"登录位置\")\n    private String loginLocation;\n\n    @ApiModelProperty(\"浏览器类型\")\n    private String browserType;\n\n    @ApiModelProperty(\"操作系统\")\n    private String operateSystem;\n\n    @ApiModelProperty(\"登录状态（1 成功，2 失败）\")\n    private Integer status;\n\n    @ApiModelProperty(\"提示消息\")\n    private String hintMessage;\n\n    @ApiModelProperty(\"登录时间\")\n    private Date gmtLogin;\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/log/login/dto/LogLoginPageDTO.java",
    "content": "package com.geshanzsq.admin.system.log.login.dto;\n\nimport com.geshanzsq.common.framework.mybatis.page.dto.PageDTO;\nimport com.geshanzsq.common.framework.mybatis.plugin.annotation.Query;\nimport com.geshanzsq.common.framework.mybatis.plugin.enums.QueryWay;\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\n/**\n * 登录日志分页查询\n *\n * @author geshanzsq\n * @date 2022/7/3\n */\n@Data\n@ApiModel(\"登录日志分页查询\")\npublic class LogLoginPageDTO extends PageDTO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(\"登录用户名\")\n    @Query(QueryWay.LIKE)\n    private String username;\n\n    @ApiModelProperty(\"登录 ip 地址\")\n    @Query(QueryWay.LIKE)\n    private String ipAddress;\n\n    @ApiModelProperty(\"登录状态（1 成功，2 失败）\")\n    private Integer status;\n\n    @ApiModelProperty(\"开始登录时间\")\n    @DateTimeFormat(pattern = \"yyyy-MM-dd HH:mm:ss\")\n    @Query(value = QueryWay.GE, fieldName = \"gmtLogin\")\n    private Date startGmtLogin;\n\n    @ApiModelProperty(\"结束登录时间\")\n    @DateTimeFormat(pattern = \"yyyy-MM-dd HH:mm:ss\")\n    @Query(value = QueryWay.LE, fieldName = \"gmtLogin\")\n    private Date endGmtLogin;\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/log/login/enums/LogLoginStatus.java",
    "content": "package com.geshanzsq.admin.system.log.login.enums;\n\n/**\n * 登录日志状态\n *\n * @author geshanzsq\n * @date 2022/7/3\n */\npublic enum LogLoginStatus {\n\n    /**\n     * 成功\n     */\n    SUCCESS(1),\n\n    /**\n     * 失败\n     */\n    FAIL(2)\n    ;\n\n    public final Integer code;\n\n\n    LogLoginStatus(Integer code) {\n        this.code = code;\n    }\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/log/login/factory/LogLoginAsyncFactory.java",
    "content": "package com.geshanzsq.admin.system.log.login.factory;\n\nimport com.geshanzsq.admin.system.log.login.po.LogLogin;\nimport com.geshanzsq.admin.system.log.login.service.LogLoginService;\nimport com.geshanzsq.common.core.util.ip.AddressUtils;\nimport com.geshanzsq.common.core.util.ip.IpUtils;\nimport com.geshanzsq.common.core.util.servlet.ServletUtils;\nimport com.geshanzsq.common.core.util.spring.SpringUtils;\nimport eu.bitwalker.useragentutils.UserAgent;\n\nimport java.util.Date;\nimport java.util.TimerTask;\n\n/**\n * 登录日志异步工厂\n *\n * @author geshanzsq\n * @date 2022/7/4\n */\npublic class LogLoginAsyncFactory {\n\n    /**\n     * 记录日志\n     * @param username 用户名\n     * @param status 状态\n     * @param hintMessage 提示消息\n     */\n    public static TimerTask add(String username, Integer status, String hintMessage) {\n        return add(null, username, status, hintMessage);\n    }\n\n    /**\n     * 记录日志\n     * @param userId 用户 id\n     * @param username 用户名\n     * @param status 状态\n     * @param hintMessage 提示消息\n     */\n    public static TimerTask add(Long userId, String username, Integer status, String hintMessage) {\n        // 获取客户端信息\n        UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader(\"User-Agent\"));\n        String ip = IpUtils.getIpAddr(ServletUtils.getRequest());\n        return new TimerTask() {\n            @Override\n            public void run() {\n                String address = AddressUtils.getRealAddressByIP(ip);\n                // 获取客户端操作系统\n                String operateSystem = userAgent.getOperatingSystem().getName();\n                // 获取客户端浏览器\n                String browser = userAgent.getBrowser().getName();\n                // 封装对象\n                LogLogin logLogin = LogLogin.builder()\n                        .userId(userId)\n                        .username(username)\n                        .ipAddress(ip)\n                        .loginLocation(address)\n                        .operateSystem(operateSystem)\n                        .browserType(browser)\n                        .hintMessage(hintMessage)\n                        .status(status)\n                        .gmtLogin(new Date())\n                        .build();\n                SpringUtils.getBean(LogLoginService.class).save(logLogin);\n            }\n        };\n    }\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/log/login/mapper/LogLoginMapper.java",
    "content": "package com.geshanzsq.admin.system.log.login.mapper;\n\nimport com.geshanzsq.admin.system.log.login.po.LogLogin;\nimport com.geshanzsq.common.framework.web.mapper.BaseMapperPlus;\nimport org.apache.ibatis.annotations.Mapper;\n\n/**\n * 登录日志\n *\n * @author geshanzsq\n * @date 2022/7/3\n */\n@Mapper\npublic interface LogLoginMapper extends BaseMapperPlus<LogLogin> {\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/log/login/mapstruct/LogLoginConverter.java",
    "content": "package com.geshanzsq.admin.system.log.login.mapstruct;\n\nimport com.geshanzsq.admin.system.log.login.dto.LogLoginAddDTO;\nimport com.geshanzsq.admin.system.log.login.po.LogLogin;\nimport com.geshanzsq.admin.system.log.login.vo.LogLoginVO;\nimport com.geshanzsq.common.framework.mybatis.page.vo.PageVO;\nimport org.mapstruct.Mapper;\nimport org.mapstruct.factory.Mappers;\n\n/**\n * 登录日志对象转换\n *\n * @author geshanzsq\n * @date 2022/7/3\n */\n@Mapper\npublic interface LogLoginConverter {\n\n    LogLoginConverter INSTANCE = Mappers.getMapper(LogLoginConverter.class);\n\n    LogLoginVO convert(LogLogin logLogin);\n\n    PageVO<LogLoginVO> convert(PageVO<LogLogin> pageVo);\n\n    LogLogin convert(LogLoginAddDTO addDTO);\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/log/login/po/LogLogin.java",
    "content": "package com.geshanzsq.admin.system.log.login.po;\n\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\n/**\n * 登录日志\n *\n * @author geshanzsq\n * @date 2022/7/3\n */\n@Data\n@Builder\n@AllArgsConstructor\n@NoArgsConstructor\npublic class LogLogin implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * 登录日志 id\n     */\n    @TableId\n    private Long id;\n\n    /**\n     * 登录用户名\n     */\n    private String username;\n\n    /**\n     * 用户 id\n     */\n    @TableField(\"fk_user_id\")\n    private Long userId;\n\n    /**\n     * 登录 ip 地址\n     */\n    private String ipAddress;\n\n    /**\n     * 登录位置\n     */\n    private String loginLocation;\n\n    /**\n     * 浏览器类型\n     */\n    private String browserType;\n\n    /**\n     * 操作系统\n     */\n    private String operateSystem;\n\n    /**\n     * 登录状态（1 成功，2 失败）\n     */\n    private Integer status;\n\n    /**\n     * 提示消息\n     */\n    private String hintMessage;\n\n    /**\n     * 登录时间\n     */\n    private Date gmtLogin;\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/log/login/service/LogLoginService.java",
    "content": "package com.geshanzsq.admin.system.log.login.service;\n\nimport com.geshanzsq.admin.system.log.login.po.LogLogin;\nimport com.geshanzsq.common.framework.web.service.BaseService;\n\n/**\n * 登录日志\n *\n * @author geshanzsq\n * @date 2022/7/3\n */\npublic interface LogLoginService extends BaseService<LogLogin> {\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/log/login/service/impl/LogLoginServiceImpl.java",
    "content": "package com.geshanzsq.admin.system.log.login.service.impl;\n\nimport com.geshanzsq.admin.system.log.login.mapper.LogLoginMapper;\nimport com.geshanzsq.admin.system.log.login.po.LogLogin;\nimport com.geshanzsq.admin.system.log.login.service.LogLoginService;\nimport com.geshanzsq.common.framework.web.service.impl.BaseServiceImpl;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\n/**\n * 登录日志\n *\n * @author geshanzsq\n * @date 2022/7/3\n */\n@Service\npublic class LogLoginServiceImpl extends BaseServiceImpl<LogLoginMapper, LogLogin> implements LogLoginService {\n\n    @Autowired\n    private LogLoginMapper logLoginMapper;\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/log/login/vo/LogLoginVO.java",
    "content": "package com.geshanzsq.admin.system.log.login.vo;\n\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\n/**\n * 登录日志\n *\n * @author geshanzsq\n * @date 2022/7/3\n */\n@Data\npublic class LogLoginVO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(\"登录日志 id\")\n    private Long id;\n\n    @ApiModelProperty(\"登录用户名\")\n    private String username;\n\n    @ApiModelProperty(\"登录 ip 地址\")\n    private String ipAddress;\n\n    @ApiModelProperty(\"登录位置\")\n    private String loginLocation;\n\n    @ApiModelProperty(\"浏览器类型\")\n    private String browserType;\n\n    @ApiModelProperty(\"操作系统\")\n    private String operateSystem;\n\n    @ApiModelProperty(\"登录状态（1 成功，2 失败）\")\n    private Integer status;\n\n    @ApiModelProperty(\"提示消息\")\n    private String hintMessage;\n\n    @ApiModelProperty(\"登录时间\")\n    private Date gmtLogin;\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/log/operation/config/LogMqConfig.java",
    "content": "package com.geshanzsq.admin.system.log.operation.config;\n\nimport com.geshanzsq.admin.system.log.operation.mq.LogOperationMq;\nimport com.geshanzsq.common.log.constant.LogConstant;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.data.redis.connection.RedisConnectionFactory;\nimport org.springframework.data.redis.listener.PatternTopic;\nimport org.springframework.data.redis.listener.RedisMessageListenerContainer;\nimport org.springframework.data.redis.listener.adapter.MessageListenerAdapter;\n\n/**\n * 日志消息中心配置，目前采用 Redis 消息队列方式\n *\n * @author geshanzsq\n * @date 2022/7/5\n */\n@Configuration\npublic class LogMqConfig {\n\n    /**\n     * 监听器初始化\n     */\n    @Bean\n    public RedisMessageListenerContainer redisMessageListenerContainer(RedisConnectionFactory connectionFactory,\n                                                                       MessageListenerAdapter messageListenerAdapter) {\n        RedisMessageListenerContainer container = new RedisMessageListenerContainer();\n        container.setConnectionFactory(connectionFactory);\n        container.addMessageListener(messageListenerAdapter, new PatternTopic(LogConstant.LOG_MQ_TOPIC));\n        return container;\n    }\n\n    /**\n     * 绑定消费者的接收方法\n     */\n    @Bean\n    public MessageListenerAdapter messageListenerAdapter(LogOperationMq logOperationMq) {\n        // 接收消息中心方法\n        return new MessageListenerAdapter(logOperationMq, \"consumeMqLog\");\n    }\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/log/operation/controller/LogOperationController.java",
    "content": "package com.geshanzsq.admin.system.log.operation.controller;\n\nimport com.geshanzsq.admin.system.log.operation.dto.LogOperationPageDTO;\nimport com.geshanzsq.admin.system.log.operation.service.LogOperationService;\nimport com.geshanzsq.admin.system.log.operation.vo.LogOperationVo;\nimport com.geshanzsq.common.core.web.response.ResponseResult;\nimport com.geshanzsq.common.framework.mybatis.page.vo.PageVO;\nimport com.geshanzsq.common.framework.web.controller.BaseController;\nimport io.swagger.annotations.Api;\nimport io.swagger.annotations.ApiOperation;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * 操作日志\n *\n * @author geshanzsq\n * @date 2022/7/9\n */\n@Api(tags = \"操作日志\")\n@RestController\n@RequestMapping(\"/system/log/operation\")\npublic class LogOperationController extends BaseController {\n\n    @Autowired\n    private LogOperationService logOperationService;\n\n    @GetMapping(\"/page\")\n    @ApiOperation((\"分页列表\"))\n    @PreAuthorize(\"@auth.hasUrl()\")\n    public ResponseResult<PageVO<LogOperationVo>> page(LogOperationPageDTO pageDTO) {\n        PageVO<LogOperationVo> pageVO = logOperationService.pageList(pageDTO);\n        return ResponseResult.success(pageVO);\n    }\n\n    @GetMapping(\"/getById/{id}\")\n    @ApiOperation(\"详情\")\n    @PreAuthorize(\"@auth.hasUrl()\")\n    public ResponseResult<LogOperationVo> getById(@PathVariable Long id) {\n        LogOperationVo logOperationVo = logOperationService.getLogOperationById(id);\n        return ResponseResult.success(logOperationVo);\n    }\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/log/operation/dto/LogOperationPageDTO.java",
    "content": "package com.geshanzsq.admin.system.log.operation.dto;\n\nimport com.geshanzsq.common.framework.mybatis.page.dto.PageDTO;\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\n/**\n * 操作日志分页查询\n *\n * @author geshanzsq\n * @date 2022/7/9\n */\n@Data\n@ApiModel(\"操作日志分页查询\")\npublic class LogOperationPageDTO extends PageDTO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(\"模块名称\")\n    private String moduleName;\n\n    @ApiModelProperty(\"操作 ip 地址\")\n    private String ipAddress;\n\n    @ApiModelProperty(\"操作用户\")\n    private String operateUser;\n\n    @ApiModelProperty(\"业务类型（1 其它，2 新增，3 修改，4 删除）\")\n    private Integer businessType;\n\n    @ApiModelProperty(\"操作类型（1 其它，2 后台用户，3 手机端用户，4 博客用户）\")\n    private Integer operateType;\n\n    @ApiModelProperty(\"状态（1 成功，2 异常）\")\n    private Integer status;\n\n    @ApiModelProperty(\"开始操作时间\")\n    @DateTimeFormat(pattern = \"yyyy-MM-dd HH:mm:ss\")\n    private Date beginGmtOperate;\n\n    @ApiModelProperty(\"结束操作时间\")\n    @DateTimeFormat(pattern = \"yyyy-MM-dd HH:mm:ss\")\n    private Date endGmtOperate;\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/log/operation/mapper/LogOperationMapper.java",
    "content": "package com.geshanzsq.admin.system.log.operation.mapper;\n\nimport com.geshanzsq.admin.system.log.operation.dto.LogOperationPageDTO;\nimport com.geshanzsq.admin.system.log.operation.po.LogOperation;\nimport com.geshanzsq.admin.system.log.operation.vo.LogOperationVo;\nimport com.geshanzsq.common.framework.web.mapper.BaseMapperPlus;\nimport org.apache.ibatis.annotations.Mapper;\n\nimport java.util.List;\n\n/**\n * 操作日志\n *\n * @author geshanzsq\n * @date 2022/7/5\n */\n@Mapper\npublic interface LogOperationMapper extends BaseMapperPlus<LogOperation> {\n\n    /**\n     * 分页列表\n     */\n    List<LogOperationVo> pageList(LogOperationPageDTO pageDto);\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/log/operation/mapstruct/LogOperationConverter.java",
    "content": "package com.geshanzsq.admin.system.log.operation.mapstruct;\n\nimport com.geshanzsq.admin.system.log.operation.po.LogOperation;\nimport com.geshanzsq.admin.system.log.operation.vo.LogOperationVo;\nimport com.geshanzsq.common.log.dto.LogDTO;\nimport org.mapstruct.Mapper;\nimport org.mapstruct.factory.Mappers;\n\n/**\n * 操作日志转换\n *\n * @author geshanzsq\n * @date 2022/7/9\n */\n@Mapper\npublic interface LogOperationConverter {\n\n    LogOperationConverter INSTANCE = Mappers.getMapper(LogOperationConverter.class);\n\n    LogOperationVo convert(LogOperation logOperation);\n\n    LogOperation convert(LogDTO logDto);\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/log/operation/mq/LogOperationMq.java",
    "content": "package com.geshanzsq.admin.system.log.operation.mq;\n\nimport com.alibaba.fastjson.JSONObject;\nimport com.geshanzsq.admin.system.log.operation.mapstruct.LogOperationConverter;\nimport com.geshanzsq.admin.system.log.operation.po.LogOperation;\nimport com.geshanzsq.admin.system.log.operation.service.LogOperationService;\nimport com.geshanzsq.common.core.util.id.IdWorker;\nimport com.geshanzsq.common.log.dto.LogDTO;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\n\n/**\n * 操作日志消息中心\n *\n * @author geshanzsq\n * @date 2022/7/5\n */\n@Component\npublic class LogOperationMq {\n\n    @Autowired\n    private LogOperationService logOperationService;\n\n    /**\n     * 消费消息中心日志\n     */\n    public void consumeMqLog(String message) {\n        LogDTO logDto = JSONObject.parseObject(message, LogDTO.class);\n        LogOperation logOperation = LogOperationConverter.INSTANCE.convert(logDto);\n        logOperation.setId(IdWorker.nextId());\n        logOperationService.save(logOperation);\n    }\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/log/operation/po/LogOperation.java",
    "content": "package com.geshanzsq.admin.system.log.operation.po;\n\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport lombok.Data;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\n/**\n * 操作日志\n *\n * @author geshanzsq\n * @date 2022/7/4\n */\n@Data\npublic class LogOperation implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * 操作日志 id\n     */\n    @TableId\n    private Long id;\n\n    /**\n     * 模块名称\n     */\n    private String moduleName;\n\n    /**\n     * 业务类型（1 其它，2 新增，3 修改，4 删除）\n     */\n    private Integer businessType;\n\n    /**\n     * 操作类型（1 其它，2 后台用户，3 手机端用户，4 博客用户）\n     */\n    private Integer operateType;\n\n    /**\n     * 操作用户 id\n     */\n    @TableField(\"fk_user_id\")\n    private Long userId;\n\n    /**\n     * 请求方式\n     */\n    private String requestMethod;\n\n    /**\n     * 类方法\n     */\n    private String classMethod;\n\n    /**\n     * 请求地址\n     */\n    private String requestUrl;\n\n    /**\n     * 操作 ip 地址\n     */\n    private String ipAddress;\n\n    /**\n     * 操作位置\n     */\n    private String operateLocation;\n\n    /**\n     * 请求参数\n     */\n    private String requestParam;\n\n    /**\n     * 返回结果\n     */\n    private String returnResult;\n\n    /**\n     * 状态（1 成功，2 异常）\n     */\n    private Integer status;\n\n    /**\n     * 操作时间\n     */\n    private Date gmtOperate;\n\n    /**\n     * 错误消息\n     */\n    private String errorMessage;\n\n    /**\n     * 浏览器类型\n     */\n    private String browserType;\n\n    /**\n     * 操作系统\n     */\n    private String operateSystem;\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/log/operation/service/LogOperationService.java",
    "content": "package com.geshanzsq.admin.system.log.operation.service;\n\nimport com.geshanzsq.admin.system.log.operation.dto.LogOperationPageDTO;\nimport com.geshanzsq.admin.system.log.operation.po.LogOperation;\nimport com.geshanzsq.admin.system.log.operation.vo.LogOperationVo;\nimport com.geshanzsq.common.framework.mybatis.page.vo.PageVO;\nimport com.geshanzsq.common.framework.web.service.BaseService;\n\n/**\n * 操作日志\n *\n * @author geshanzsq\n * @date 2022/7/5\n */\npublic interface LogOperationService extends BaseService<LogOperation> {\n\n    /**\n     * 分页列表\n     */\n    PageVO<LogOperationVo> pageList(LogOperationPageDTO pageDTO);\n\n    /**\n     * 获取详情\n     */\n    LogOperationVo getLogOperationById(Long id);\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/log/operation/service/impl/LogOperationServiceImpl.java",
    "content": "package com.geshanzsq.admin.system.log.operation.service.impl;\n\nimport com.geshanzsq.admin.system.log.operation.dto.LogOperationPageDTO;\nimport com.geshanzsq.admin.system.log.operation.mapper.LogOperationMapper;\nimport com.geshanzsq.admin.system.log.operation.mapstruct.LogOperationConverter;\nimport com.geshanzsq.admin.system.log.operation.po.LogOperation;\nimport com.geshanzsq.admin.system.log.operation.service.LogOperationService;\nimport com.geshanzsq.admin.system.log.operation.vo.LogOperationVo;\nimport com.geshanzsq.admin.system.user.service.SysUserService;\nimport com.geshanzsq.admin.system.user.vo.SysUserVO;\nimport com.geshanzsq.common.framework.mybatis.page.util.PageUtils;\nimport com.geshanzsq.common.framework.mybatis.page.vo.PageVO;\nimport com.geshanzsq.common.framework.web.service.impl.BaseServiceImpl;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\nimport org.springframework.util.CollectionUtils;\n\nimport java.util.Arrays;\nimport java.util.List;\n\n/**\n * 操作日志\n *\n * @author geshanzsq\n * @date 2022/7/5\n */\n@Service\npublic class LogOperationServiceImpl extends BaseServiceImpl<LogOperationMapper, LogOperation> implements LogOperationService {\n\n    @Autowired\n    private LogOperationMapper logOperationMapper;\n    @Autowired\n    private SysUserService sysUserService;\n\n    /**\n     * 分页列表\n     */\n    @Override\n    public PageVO<LogOperationVo> pageList(LogOperationPageDTO pageDTO) {\n        PageUtils.startPage(pageDTO);\n        List<LogOperationVo> list = logOperationMapper.pageList(pageDTO);\n        PageVO<LogOperationVo> pageVO = PageUtils.getPage(list);\n        return pageVO;\n    }\n\n    /**\n     * 获取详情\n     */\n    @Override\n    public LogOperationVo getLogOperationById(Long id) {\n        LogOperation logOperation = logOperationMapper.selectById(id);\n        if (logOperation == null) {\n            return null;\n        }\n        LogOperationVo logOperationVo = LogOperationConverter.INSTANCE.convert(logOperation);\n        // 获取操作人信息\n        List<SysUserVO> userList = sysUserService.getUsernameAndNickNameByUserIds(Arrays.asList(logOperationVo.getUserId()));\n        if (!CollectionUtils.isEmpty(userList)) {\n            SysUserVO sysUserVo = userList.get(0);\n            logOperationVo.setUsername(sysUserVo.getUsername());\n            logOperationVo.setNickName(sysUserVo.getNickName());\n        }\n        return logOperationVo;\n    }\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/log/operation/vo/LogOperationVo.java",
    "content": "package com.geshanzsq.admin.system.log.operation.vo;\n\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\n/**\n * 日志操作\n *\n * @author geshanzsq\n * @date 2022/7/4\n */\n@Data\n@ApiModel(\"日志操作\")\npublic class LogOperationVo implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(\"操作日志 id\")\n    private Long id;\n\n    @ApiModelProperty(\"模块名称\")\n    private String moduleName;\n\n    @ApiModelProperty(\"业务类型（1 其它，2 新增，3 修改，4 删除）\")\n    private Integer businessType;\n\n    @ApiModelProperty(\"操作类型（1 其它，2 后台用户，3 手机端用户，4 博客用户）\")\n    private Integer operateType;\n\n    @ApiModelProperty(\"操作用户 id\")\n    private Long userId;\n\n    @ApiModelProperty(\"请求方式\")\n    private String requestMethod;\n\n    @ApiModelProperty(\"类方法\")\n    private String classMethod;\n\n    @ApiModelProperty(\"请求地址\")\n    private String requestUrl;\n\n    @ApiModelProperty(\"操作 ip 地址\")\n    private String ipAddress;\n\n    @ApiModelProperty(\"操作位置\")\n    private String operateLocation;\n\n    @ApiModelProperty(\"请求参数\")\n    private String requestParam;\n\n    @ApiModelProperty(\"返回结果\")\n    private String returnResult;\n\n    @ApiModelProperty(\"状态（1 成功，2 异常）\")\n    private Integer status;\n\n    @ApiModelProperty(\"操作时间\")\n    private Date gmtOperate;\n\n    @ApiModelProperty(\"错误消息\")\n    private String errorMessage;\n\n    @ApiModelProperty(\"浏览器类型\")\n    private String browserType;\n\n    @ApiModelProperty(\"操作系统\")\n    private String operateSystem;\n\n    @ApiModelProperty(\"操作人用户名\")\n    private String username;\n\n    @ApiModelProperty(\"操作人昵称\")\n    private String nickName;\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/menu/constant/MenuConstant.java",
    "content": "package com.geshanzsq.admin.system.menu.constant;\n\n/**\n * 菜单常量\n *\n * @author geshanzsq\n * @date 2022/6/12\n */\npublic class MenuConstant {\n\n    /**\n     * Layout 组件标识\n     */\n    public final static String LAYOUT = \"Layout\";\n\n    /**\n     * ParentView 组件标识\n     */\n    public final static String PARENT_VIEW = \"ParentView\";\n\n    /**\n     * InnerLink 组件标识\n     */\n    public final static String INNER_LINK = \"InnerLink\";\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/menu/controller/SysMenuController.java",
    "content": "package com.geshanzsq.admin.system.menu.controller;\n\nimport com.geshanzsq.admin.system.api.service.SysApiService;\nimport com.geshanzsq.admin.system.menu.dto.SysMenuAddDTO;\nimport com.geshanzsq.admin.system.menu.dto.SysMenuAuthApiDTO;\nimport com.geshanzsq.admin.system.menu.dto.SysMenuListDTO;\nimport com.geshanzsq.admin.system.menu.dto.SysMenuUpdateDTO;\nimport com.geshanzsq.admin.system.menu.mapstruct.SysMenuConverter;\nimport com.geshanzsq.admin.system.menu.po.SysMenu;\nimport com.geshanzsq.admin.system.menu.service.SysMenuService;\nimport com.geshanzsq.admin.system.menu.vo.SysMenuAuthApiVO;\nimport com.geshanzsq.admin.system.menu.vo.SysMenuVO;\nimport com.geshanzsq.common.core.web.response.ResponseResult;\nimport com.geshanzsq.common.log.annotation.Log;\nimport com.geshanzsq.common.log.enums.BusinessType;\nimport io.swagger.annotations.Api;\nimport io.swagger.annotations.ApiOperation;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.validation.Valid;\nimport java.util.List;\n\n/**\n * 系统菜单\n *\n * @author geshanzsq\n * @date 2022/6/12\n */\n@Api(tags = \"系统菜单\")\n@RestController\n@RequestMapping(\"/system/menu\")\npublic class SysMenuController {\n\n    @Autowired\n    private SysMenuService sysMenuService;\n    @Autowired\n    private SysApiService sysApiService;\n\n    @GetMapping(\"/list\")\n    @ApiOperation(\"列表\")\n    @PreAuthorize(\"@auth.hasUrl()\")\n    public ResponseResult<List<SysMenuVO>> list(SysMenuListDTO sysMenuListDTO) {\n        List<SysMenuVO> list = sysMenuService.getMenuList(sysMenuListDTO);\n        return ResponseResult.success(list);\n    }\n\n    @GetMapping(\"/tree\")\n    @ApiOperation(\"树形\")\n    @PreAuthorize(\"@auth.hasUrl()\")\n    public ResponseResult<List<SysMenuVO>> tree() {\n        List<SysMenuVO> treeList = sysMenuService.getMenuTree();\n        return ResponseResult.success(treeList);\n    }\n\n    @GetMapping(\"/getById/{id}\")\n    @ApiOperation(\"详情\")\n    @PreAuthorize(\"@auth.hasUrl()\")\n    public ResponseResult<SysMenuVO> getById(@PathVariable Long id) {\n        return ResponseResult.success(SysMenuConverter.INSTANCE.convert(sysMenuService.getById(id)));\n    }\n\n    @PostMapping()\n    @ApiOperation(\"新增\")\n    @PreAuthorize(\"@auth.hasUrl()\")\n    @Log(moduleName = \"菜单管理\", businessType = BusinessType.ADD)\n    public ResponseResult add(@Valid @RequestBody SysMenuAddDTO addDTO) {\n        SysMenu sysMenu = SysMenuConverter.INSTANCE.convert(addDTO);\n        sysMenuService.save(sysMenu);\n        return ResponseResult.success();\n    }\n\n    @PutMapping()\n    @ApiOperation(\"修改\")\n    @PreAuthorize(\"@auth.hasUrl()\")\n    @Log(moduleName = \"菜单管理\", businessType = BusinessType.UPDATE)\n    public ResponseResult update(@Valid @RequestBody SysMenuUpdateDTO updateDTO) {\n        SysMenu sysMenu = SysMenuConverter.INSTANCE.convert(updateDTO);\n        sysMenuService.updateById(sysMenu);\n        return ResponseResult.success();\n    }\n\n    @GetMapping(\"/getMaxSortByParentId\")\n    @ApiOperation(\"获取最大排序\")\n    @PreAuthorize(\"@auth.hasUrl()\")\n    public ResponseResult<Integer> getMaxSortByParentId(Long parentId) {\n        Integer maxSort = sysMenuService.getMaxSortByParentId(parentId);\n        return ResponseResult.success(maxSort);\n    }\n\n    @DeleteMapping(\"/delete/{id}\")\n    @ApiOperation(\"删除\")\n    @PreAuthorize(\"@auth.hasUrl()\")\n    @Log(moduleName = \"菜单管理\", businessType = BusinessType.DELETE)\n    public ResponseResult delete(@PathVariable Long id) {\n        sysMenuService.remove(id);\n        return ResponseResult.success();\n    }\n\n    @GetMapping(\"/auth/api/list\")\n    @ApiOperation(\"获取分配的 API 接口\")\n    public ResponseResult<List<SysMenuAuthApiVO>> getApiByMenuId(Long menuId) {\n        List<SysMenuAuthApiVO> list = sysApiService.getAuthApiByMenuId(menuId);\n        return ResponseResult.success(list);\n    }\n\n    @PostMapping(\"/auth/api\")\n    @ApiOperation(\"分配 API\")\n    @PreAuthorize(\"@auth.hasUrl()\")\n    @Log(moduleName = \"菜单管理-分配 API\", businessType = BusinessType.UPDATE)\n    public ResponseResult authApi(@Valid @RequestBody SysMenuAuthApiDTO authApiDto) {\n        sysMenuService.authApi(authApiDto);\n        return ResponseResult.success();\n    }\n\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/menu/dto/SysMenuAddDTO.java",
    "content": "package com.geshanzsq.admin.system.menu.dto;\n\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotBlank;\nimport javax.validation.constraints.NotNull;\nimport java.io.Serializable;\n\n/**\n * 菜单新增\n *\n * @author geshanzsq\n * @date 2022/6/15\n */\n@Data\n@ApiModel(\"菜单新增\")\npublic class SysMenuAddDTO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(value = \"菜单名称\", required = true)\n    @NotBlank(message = \"菜单名称不能为空\")\n    private String menuName;\n\n    @ApiModelProperty(\"上级菜单 id\")\n    private Long parentId;\n\n    @ApiModelProperty(\"菜单图标\")\n    private String menuIcon;\n\n    @ApiModelProperty(value = \"菜单类型（D 目录，M 菜单，B 按钮）\", required = true)\n    @NotBlank(message = \"请选择菜单类型\")\n    private String menuType;\n\n    @ApiModelProperty(\"路由地址\")\n    private String routerUrl;\n\n    @ApiModelProperty(value = \"排序\", required = true)\n    @NotNull(message = \"排序顺序不能为空\")\n    private Integer sort;\n\n    @ApiModelProperty(\"是否为外链（1是 2否）\")\n    private Integer frame;\n\n    @ApiModelProperty(\"是否缓存（1缓存 2不缓存）\")\n    private Integer cache;\n\n    @ApiModelProperty(\"组件路径\")\n    private String componentPath;\n\n    @ApiModelProperty(\"路由参数\")\n    private String routerParam;\n\n    @ApiModelProperty(\"权限标识\")\n    private String permissionCode;\n\n    @ApiModelProperty(\"显示状态（1显示 2隐藏）\")\n    private Integer showStatus;\n\n    @ApiModelProperty(\"状态（1 正常，2 停用）\")\n    private Integer status;\n\n    @ApiModelProperty(\"是否需要权限（（1是，2 否））\")\n    private Integer hasPermission;\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/menu/dto/SysMenuAuthApiDTO.java",
    "content": "package com.geshanzsq.admin.system.menu.dto;\n\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\nimport java.io.Serializable;\nimport java.util.List;\n\n/**\n * 分配 API\n *\n * @author geshanzsq\n * @date 2022/7/27\n */\n@Data\n@ApiModel(\"分配 API\")\npublic class SysMenuAuthApiDTO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(value = \"菜单 id\", required = true)\n    @NotNull(message = \"菜单 id 不能为空\")\n    private Long menuId;\n\n    @ApiModelProperty(\"接口列表\")\n    private List<Long> apiIds;\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/menu/dto/SysMenuListDTO.java",
    "content": "package com.geshanzsq.admin.system.menu.dto;\n\nimport com.geshanzsq.common.framework.mybatis.plugin.annotation.Query;\nimport com.geshanzsq.common.framework.mybatis.plugin.enums.QueryWay;\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\nimport java.io.Serializable;\n\n/**\n * 查询菜单\n *\n * @author geshanzsq\n * @date 2022/6/12\n */\n@Data\n@ApiModel(\"查询菜单\")\npublic class SysMenuListDTO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(\"菜单名称\")\n    @Query(QueryWay.LIKE)\n    private String menuName;\n\n    @ApiModelProperty(\"菜单状态\")\n    private Integer status;\n\n    @ApiModelProperty(value = \"排序列，多个用逗号分开\", hidden = true)\n    @Query(ignore = true)\n    private String orderColumn;\n\n    @ApiModelProperty(value = \"排序类型(asc 或 desc)，多个用逗号分开\", hidden = true)\n    @Query(ignore = true)\n    private String orderType;\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/menu/dto/SysMenuUpdateDTO.java",
    "content": "package com.geshanzsq.admin.system.menu.dto;\n\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotBlank;\nimport javax.validation.constraints.NotNull;\nimport java.io.Serializable;\n\n/**\n * 菜单修改\n *\n * @author geshanzsq\n * @date 2022/6/15\n */\n@Data\n@ApiModel(\"菜单修改\")\npublic class SysMenuUpdateDTO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(value = \"菜单id\", required = true)\n    @NotNull(message = \"菜单id不能为空\")\n    private Long id;\n\n    @ApiModelProperty(value = \"菜单名称\", required = true)\n    @NotBlank(message = \"菜单名称不能为空\")\n    private String menuName;\n\n    @ApiModelProperty(\"上级菜单 id\")\n    private Long parentId;\n\n    @ApiModelProperty(\"菜单图标\")\n    private String menuIcon;\n\n    @ApiModelProperty(value = \"菜单类型（D 目录，M 菜单，B 按钮）\", required = true)\n    @NotBlank(message = \"请选择菜单类型\")\n    private String menuType;\n\n    @ApiModelProperty(\"路由地址\")\n    private String routerUrl;\n\n    @ApiModelProperty(value = \"排序\", required = true)\n    @NotNull(message = \"排序顺序不能为空\")\n    private Integer sort;\n\n    @ApiModelProperty(\"是否为外链（1是 2否）\")\n    private Integer frame;\n\n    @ApiModelProperty(\"是否缓存（1缓存 2不缓存）\")\n    private Integer cache;\n\n    @ApiModelProperty(\"组件路径\")\n    private String componentPath;\n\n    @ApiModelProperty(\"路由参数\")\n    private String routerParam;\n\n    @ApiModelProperty(\"权限标识\")\n    private String permissionCode;\n\n    @ApiModelProperty(\"显示状态（1显示 2隐藏）\")\n    private Integer showStatus;\n\n    @ApiModelProperty(\"状态（1 正常，2 停用）\")\n    private Integer status;\n\n    @ApiModelProperty(\"是否需要权限（（1是，2 否））\")\n    private Integer hasPermission;\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/menu/enums/MenuShowStatus.java",
    "content": "package com.geshanzsq.admin.system.menu.enums;\n\n/**\n * 显示状态\n *\n * @author geshanzsq\n * @date 2022/6/12\n */\npublic enum MenuShowStatus {\n\n    /**\n     * 显示\n     */\n    SHOW(1),\n\n    /**\n     * 隐藏\n     */\n    HIDDEN(2)\n    ;\n\n\n    public final Integer code;\n\n    MenuShowStatus(Integer code) {\n        this.code = code;\n    }\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/menu/enums/MenuStatus.java",
    "content": "package com.geshanzsq.admin.system.menu.enums;\n\n/**\n * 菜单状态\n *\n * @author geshanzsq\n * @date 2022/3/26\n */\npublic enum MenuStatus {\n\n    /**\n     * 正常\n     */\n    NORMAL(1),\n\n    /**\n     * 停用\n     */\n    DISABLE(2)\n    ;\n\n    public final Integer code;\n\n    MenuStatus(Integer code) {\n        this.code = code;\n    }\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/menu/enums/MenuType.java",
    "content": "package com.geshanzsq.admin.system.menu.enums;\n\n/**\n * 菜单类型\n *\n * @author geshanzsq\n * @date 2022/3/26\n */\npublic enum MenuType {\n    /**\n     * 目录\n     */\n    DIRECTORY(\"D\"),\n\n    /**\n     * 菜单\n     */\n    MENU(\"M\"),\n\n    /**\n     * 按钮\n     */\n    BUTTON(\"B\")\n    ;\n\n    public final String code;\n\n    MenuType(String code) {\n        this.code = code;\n    }\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/menu/mapper/SysMenuMapper.java",
    "content": "package com.geshanzsq.admin.system.menu.mapper;\n\nimport com.geshanzsq.admin.system.menu.po.SysMenu;\nimport com.geshanzsq.admin.system.menu.vo.SysMenuVO;\nimport com.geshanzsq.common.framework.web.mapper.BaseMapperPlus;\nimport org.apache.ibatis.annotations.Mapper;\nimport org.apache.ibatis.annotations.Param;\n\nimport java.util.List;\nimport java.util.Set;\n\n/**\n * 系统菜单\n *\n * @author geshanzsq\n * @date 2022/3/26\n */\n@Mapper\npublic interface SysMenuMapper extends BaseMapperPlus<SysMenu> {\n\n    /**\n     * 通过角色 ids 获取菜单\n     * @param roleIds 角色 Ids\n     * @param menuTypes 菜单类型\n     * @param menuStatus 菜单状态\n     */\n    List<SysMenuVO> getMenuByRoleIds(@Param(\"roleIds\") Set<Long> roleIds,\n                                     @Param(\"menuTypes\") List<String> menuTypes,\n                                     @Param(\"menuStatus\") Integer menuStatus);\n\n    /**\n     * 通过用户 id 获取菜单\n     * @param userId 用户 id\n     * @param menuStatus 菜单状态\n     * @param roleStatus 角色状态\n     * @param menuHasPermission 是否需要权限\n     * @param menuTypes 菜单类型\n     * @return\n     */\n    List<SysMenuVO> getMenuByUserId(@Param(\"userId\") Long userId,\n                                    @Param(\"menuStatus\") Integer menuStatus,\n                                    @Param(\"roleStatus\") Integer roleStatus,\n                                    @Param(\"menuHasPermission\") Integer menuHasPermission,\n                                    @Param(\"menuTypes\") List<String> menuTypes);\n\n    /**\n     * 获取最大排序\n     * @param parentId 上级菜单\n     */\n    Integer selectMaxSortByParentId(Long parentId);\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/menu/mapstruct/SysMenuConverter.java",
    "content": "package com.geshanzsq.admin.system.menu.mapstruct;\n\nimport com.geshanzsq.admin.system.menu.dto.SysMenuAddDTO;\nimport com.geshanzsq.admin.system.menu.dto.SysMenuUpdateDTO;\nimport com.geshanzsq.admin.system.menu.po.SysMenu;\nimport com.geshanzsq.admin.system.menu.vo.SysMenuVO;\nimport org.mapstruct.Mapper;\nimport org.mapstruct.factory.Mappers;\n\nimport java.util.List;\n\n/**\n * 菜单对象转换\n *\n * @author geshanzsq\n * @date 2022/6/12\n */\n@Mapper\npublic interface SysMenuConverter {\n\n    SysMenuConverter INSTANCE = Mappers.getMapper(SysMenuConverter.class);\n\n    SysMenuVO convert(SysMenu menus);\n\n    List<SysMenuVO> convertList(List<SysMenu> menus);\n\n    SysMenu convert(SysMenuAddDTO addDto);\n\n    SysMenu convert(SysMenuUpdateDTO updateDto);\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/menu/po/SysMenu.java",
    "content": "package com.geshanzsq.admin.system.menu.po;\n\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport lombok.Data;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\n/**\n *\n * 系统菜单\n *\n * @author geshanzsq\n * @date 2022/3/26\n */\n@Data\npublic class SysMenu implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * 菜单 id\n     */\n    @TableId\n    private Long id;\n\n    /**\n     * 菜单名称\n     */\n    private String menuName;\n\n    /**\n     * 上级菜单 id\n     */\n    private Long parentId;\n\n    /**\n     * 排序\n     */\n    private Integer sort;\n\n    /**\n     * 菜单类型（D 目录，M 菜单，B 按钮）\n     */\n    private String menuType;\n\n    /**\n     * 权限标识\n     */\n    private String permissionCode;\n\n    /**\n     * 路由地址\n     */\n    private String routerUrl;\n\n    /**\n     * 组件路径\n     */\n    private String componentPath;\n\n    /**\n     * 路由参数\n     */\n    private String routerParam;\n\n    /**\n     * 是否为外链（1是，2否）\n     */\n    private Integer hasFrame;\n\n    /**\n     * 是否缓存（1缓存，2不缓存）\n     */\n    private Integer hasCache;\n\n    /**\n     * 是否需要权限（（1是，2 否））\n     */\n    private Integer hasPermission;\n\n    /**\n     * 菜单图标\n     */\n    private String menuIcon;\n\n    /**\n     * 显示状态（1 显示，2 隐藏）\n     */\n    private Integer showStatus;\n\n    /**\n     * 状态（1 正常，2 停用）\n     */\n    private Integer status;\n\n    /**\n     * 创建时间\n     */\n    private Date gmtCreate;\n\n    /**\n     * 创建人用户 id\n     */\n    @TableField(\"fk_create_user_id\")\n    private Long createUserId;\n\n    /**\n     * 修改时间\n     */\n    private Date gmtModify;\n\n    /**\n     * 修改人用户 id\n     */\n    @TableField(\"fk_modify_user_id\")\n    private Long modifyUserId;\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/menu/service/SysMenuService.java",
    "content": "package com.geshanzsq.admin.system.menu.service;\n\nimport com.geshanzsq.admin.system.menu.dto.SysMenuAuthApiDTO;\nimport com.geshanzsq.admin.system.menu.dto.SysMenuListDTO;\nimport com.geshanzsq.admin.system.menu.po.SysMenu;\nimport com.geshanzsq.admin.system.menu.vo.RouterVO;\nimport com.geshanzsq.admin.system.menu.vo.SysMenuVO;\nimport com.geshanzsq.common.framework.web.service.BaseService;\n\nimport java.util.List;\nimport java.util.Set;\n\n/**\n * 系统菜单\n *\n * @author geshanzsq\n * @date 2022/3/26\n */\npublic interface SysMenuService extends BaseService<SysMenu> {\n\n    /**\n     * 通过角色 ids 获取菜单\n     * @param roleIds 角色 ids\n     */\n    List<SysMenuVO> getMenuByRoleIds(Set<Long> roleIds);\n\n    /**\n     * 获取菜单路由\n     */\n    List<RouterVO> getRouters();\n\n    /**\n     * 获取菜单列表\n     */\n    List<SysMenuVO> getMenuList(SysMenuListDTO sysMenuListDTO);\n\n    /**\n     * 菜单树形\n     */\n    List<SysMenuVO> getMenuTree();\n\n    /**\n     * 获取最大排序\n     * @param parentId 上级菜单 id\n     */\n    Integer getMaxSortByParentId(Long parentId);\n\n    /**\n     * 删除\n     * @param id 菜单 id\n     */\n    void remove(Long id);\n\n    /**\n     * 分配 API\n     */\n    void authApi(SysMenuAuthApiDTO authApiDTO);\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/menu/service/impl/SysMenuServiceImpl.java",
    "content": "package com.geshanzsq.admin.system.menu.service.impl;\n\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.geshanzsq.admin.system.api.mapper.SysApiMenuMapper;\nimport com.geshanzsq.admin.system.api.po.SysApi;\nimport com.geshanzsq.admin.system.api.po.SysApiMenu;\nimport com.geshanzsq.admin.system.api.service.SysApiService;\nimport com.geshanzsq.admin.system.menu.constant.MenuConstant;\nimport com.geshanzsq.admin.system.menu.dto.SysMenuAuthApiDTO;\nimport com.geshanzsq.admin.system.menu.dto.SysMenuListDTO;\nimport com.geshanzsq.admin.system.menu.enums.MenuShowStatus;\nimport com.geshanzsq.admin.system.menu.enums.MenuStatus;\nimport com.geshanzsq.admin.system.menu.enums.MenuType;\nimport com.geshanzsq.admin.system.menu.mapper.SysMenuMapper;\nimport com.geshanzsq.admin.system.menu.mapstruct.SysMenuConverter;\nimport com.geshanzsq.admin.system.menu.po.SysMenu;\nimport com.geshanzsq.admin.system.menu.service.SysMenuService;\nimport com.geshanzsq.admin.system.menu.vo.MetaVO;\nimport com.geshanzsq.admin.system.menu.vo.RouterVO;\nimport com.geshanzsq.admin.system.menu.vo.SysMenuVO;\nimport com.geshanzsq.common.core.constant.CommonConstant;\nimport com.geshanzsq.common.core.enums.CommonStatus;\nimport com.geshanzsq.common.core.enums.YesNo;\nimport com.geshanzsq.common.core.exception.ParamException;\nimport com.geshanzsq.common.core.exception.ServiceException;\nimport com.geshanzsq.common.core.util.message.MessageUtils;\nimport com.geshanzsq.common.core.util.string.StrUtils;\nimport com.geshanzsq.common.framework.web.service.impl.BaseServiceImpl;\nimport com.geshanzsq.framework.security.domain.LoginUserDetail;\nimport com.geshanzsq.framework.security.util.SecurityUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\nimport org.springframework.util.CollectionUtils;\n\nimport java.util.*;\nimport java.util.stream.Collectors;\n\n/**\n * 系统菜单\n *\n * @author geshanzsq\n * @date 2022/3/26\n */\n@Service\npublic class SysMenuServiceImpl extends BaseServiceImpl<SysMenuMapper, SysMenu> implements SysMenuService {\n\n    @Autowired\n    private SysMenuMapper sysMenuMapper;\n    @Autowired\n    private SysApiService sysApiService;\n    @Autowired\n    private SysApiMenuMapper sysApiMenuMapper;\n\n    /**\n     * 通过角色 ids 获取菜单\n     * @param roleIds 角色 ids\n     */\n    @Override\n    public List<SysMenuVO> getMenuByRoleIds(Set<Long> roleIds) {\n        if (CollectionUtils.isEmpty(roleIds)) {\n            return new ArrayList<>();\n        }\n\n        // 只获取菜单和按钮\n        List<String> menuTypes = Arrays.asList(MenuType.MENU.code, MenuType.BUTTON.code);\n        return sysMenuMapper.getMenuByRoleIds(roleIds, menuTypes, MenuStatus.NORMAL.code);\n    }\n\n    /**\n     * 获取菜单路由\n     * @return\n     */\n    @Override\n    public List<RouterVO> getRouters() {\n        LoginUserDetail loginUser = SecurityUtils.getLoginUser();\n\n        // 菜单类型\n        List<String> menuTypes = Arrays.asList(MenuType.DIRECTORY.code, MenuType.MENU.code);\n\n        // 1 获取菜单\n        List<SysMenuVO> menuVos = new ArrayList<>();\n        // 管理员，获取所有菜单\n        if (loginUser.isHasSuperAdmin()) {\n            LambdaQueryWrapper<SysMenu> menuWrapper = new LambdaQueryWrapper<>();\n            menuWrapper.eq(SysMenu::getStatus, CommonStatus.NORMAL.code);\n            menuWrapper.in(SysMenu::getMenuType, menuTypes);\n            menuWrapper.orderByAsc(SysMenu::getParentId, SysMenu::getSort);\n            List<SysMenu> menus = sysMenuMapper.selectList(menuWrapper);\n            menuVos = SysMenuConverter.INSTANCE.convertList(menus);\n        } else {\n            // 根据用户获取菜单\n            menuVos = sysMenuMapper.getMenuByUserId(loginUser.getUserId(), CommonStatus.NORMAL.code,\n                    CommonStatus.NORMAL.code, YesNo.NO.code, menuTypes);\n        }\n\n        if (CollectionUtils.isEmpty(menuVos)) {\n            return new ArrayList<>();\n        }\n\n        // 2 构造树形结构\n        List<SysMenuVO> treeMenus = buildMenuTree(menuVos, 0L);\n\n        // 3 构造路由\n        return buildRouters(treeMenus, null);\n    }\n\n    /**\n     * 获取菜单列表\n     */\n    @Override\n    public List<SysMenuVO> getMenuList(SysMenuListDTO sysMenuListDTO) {\n        sysMenuListDTO.setOrderColumn(\"sort,parentId\");\n        List<SysMenu> menus = sysMenuMapper.selectList(sysMenuListDTO);\n        return SysMenuConverter.INSTANCE.convertList(menus);\n    }\n\n    /**\n     * 菜单树形\n     */\n    @Override\n    public List<SysMenuVO> getMenuTree() {\n        List<SysMenuVO> menuList = getMenuList(new SysMenuListDTO());\n        return buildMenuTree(menuList, 0L);\n    }\n\n    /**\n     * 获取最大排序\n     * @param parentId 上级菜单 id\n     */\n    @Override\n    public Integer getMaxSortByParentId(Long parentId) {\n        return sysMenuMapper.selectMaxSortByParentId(parentId == null ? 0 : parentId);\n    }\n\n    /**\n     * 删除\n     * @param id 菜单 id\n     */\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void remove(Long id) {\n        if (id == null) {\n            return;\n        }\n        // 判断是否有下级菜单，如果有，则不能删除\n        LambdaQueryWrapper<SysMenu> wrapper = new LambdaQueryWrapper<>();\n        wrapper.eq(SysMenu::getParentId, id);\n        Long count = sysMenuMapper.selectCount(wrapper);\n        if (count > 0) {\n            throw new ServiceException(MessageUtils.message(\"menu.has.child\"));\n        }\n\n        // 删除菜单数据\n        sysMenuMapper.deleteById(id);\n\n        // 删除菜单和接口对应关系\n        LambdaQueryWrapper<SysApiMenu> amWrapper = new LambdaQueryWrapper<>();\n        amWrapper.eq(SysApiMenu::getMenuId, id);\n        sysApiMenuMapper.delete(amWrapper);\n    }\n\n    /**\n     * 分配 API\n     */\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void authApi(SysMenuAuthApiDTO authApiDto) {\n        // 判断菜单是否存在\n        LambdaQueryWrapper<SysMenu> menuWrapper = new LambdaQueryWrapper<>();\n        menuWrapper.eq(SysMenu::getId, authApiDto.getMenuId());\n        if (sysMenuMapper.selectCount(menuWrapper) == 0) {\n            throw new ParamException(MessageUtils.message(\"menu.not.exist\"));\n        }\n\n        // 过滤不存在的 API\n        if (!CollectionUtils.isEmpty(authApiDto.getApiIds())) {\n            LambdaQueryWrapper<SysApi> apiWrapper = new LambdaQueryWrapper<>();\n            apiWrapper.in(SysApi::getId, authApiDto.getApiIds());\n            apiWrapper.select(SysApi::getId);\n            List<Long> apiIds = sysApiService.list(apiWrapper).stream().map(api -> api.getId()).collect(Collectors.toList());\n            authApiDto.setApiIds(apiIds);\n        }\n\n        // 分配 API\n        sysApiService.authApi(authApiDto.getMenuId(), authApiDto.getApiIds());\n    }\n\n    /**\n     * 根据父节点的 id 获取所有子节点\n     *\n     * @param allList 所有数据\n     * @param parentId 传入的父节点 id\n     * @return String\n     */\n    public List<SysMenuVO> buildMenuTree(List<SysMenuVO> allList, Long parentId) {\n        List<SysMenuVO> returnList = new ArrayList<>();\n        for (SysMenuVO menu : allList) {\n            // 根据传入的某个父节点 id，遍历该父节点的所有子节点\n            if (parentId != null && parentId.equals(menu.getParentId())) {\n                // 递归菜单\n                recursiveMenu(allList, menu);\n                returnList.add(menu);\n            }\n        }\n        return returnList;\n    }\n\n    /**\n     * 递归菜单列表\n     *\n     * @param allList\n     * @param menu 当前菜单\n     */\n    private void recursiveMenu(List<SysMenuVO> allList, SysMenuVO menu) {\n        // 得到子节点列表\n        List<SysMenuVO> childList = getChildList(allList, menu);\n        menu.setChildren(childList);\n        for (SysMenuVO tChild : childList) {\n            if (hasChild(allList, tChild)) {\n                recursiveMenu(allList, tChild);\n            }\n        }\n    }\n\n    /**\n     * 判断是否有子节点\n     */\n    private boolean hasChild(List<SysMenuVO> allList, SysMenuVO menu) {\n        return getChildList(allList, menu).size() > 0;\n    }\n\n    /**\n     * 得到子节点列表\n     */\n    private List<SysMenuVO> getChildList(List<SysMenuVO> allList, SysMenuVO menu) {\n        List<SysMenuVO> childList = new ArrayList<>();\n        Iterator<SysMenuVO> it = allList.iterator();\n        while (it.hasNext()) {\n            SysMenuVO child = it.next();\n            if (menu.getId().equals(child.getParentId())) {\n                childList.add(child);\n            }\n        }\n        return childList;\n    }\n\n    /**\n     * 构建前端路由所需要的菜单\n     * @param menus 菜单列表\n     * @return 路由列表\n     */\n    private List<RouterVO> buildRouters(List<SysMenuVO> menus, SysMenuVO parentMenu) {\n        List<RouterVO> routers = new LinkedList<RouterVO>();\n        for (SysMenuVO menu : menus) {\n            RouterVO router = new RouterVO();\n            router.setHidden(MenuShowStatus.HIDDEN.code.equals(menu.getShowStatus()));\n            router.setName(getRouteName(menu, parentMenu));\n            router.setPath(getRouterPath(menu));\n            router.setComponent(getComponent(menu));\n            router.setQuery(menu.getRouterParam());\n            router.setMeta(new MetaVO(menu.getMenuName(), menu.getMenuIcon(), YesNo.NO.code.equals(menu.getHasCache()), menu.getRouterUrl()));\n            List<SysMenuVO> cMenus = menu.getChildren();\n            if (!CollectionUtils.isEmpty(cMenus) && MenuType.DIRECTORY.code.equals(menu.getMenuType())) {\n                router.setAlwaysShow(true);\n                router.setRedirect(\"noRedirect\");\n                router.setChildren(buildRouters(cMenus, menu));\n            } else if (isMenuFrame(menu)) {\n                router.setMeta(null);\n                List<RouterVO> childrenList = new ArrayList<RouterVO>();\n                RouterVO children = new RouterVO();\n                children.setPath(menu.getRouterUrl());\n                children.setComponent(menu.getComponentPath());\n                children.setName(StrUtils.capitalize(menu.getRouterUrl()));\n                children.setMeta(new MetaVO(menu.getMenuName(), menu.getMenuIcon(), YesNo.NO.code.equals(menu.getHasCache()), menu.getRouterUrl()));\n                children.setQuery(menu.getRouterParam());\n                childrenList.add(children);\n                router.setChildren(childrenList);\n            } else if (isRootMenu(menu.getParentId()) && isInnerLink(menu)) {\n                router.setMeta(new MetaVO(menu.getMenuName(), menu.getMenuIcon()));\n                router.setPath(\"/\");\n                List<RouterVO> childrenList = new ArrayList<RouterVO>();\n                RouterVO children = new RouterVO();\n                String routerPath = innerLinkReplaceEach(menu.getRouterUrl());\n                children.setPath(routerPath);\n                children.setComponent(MenuConstant.INNER_LINK);\n                children.setName(StrUtils.capitalize(routerPath));\n                children.setMeta(new MetaVO(menu.getMenuName(), menu.getMenuIcon(), menu.getRouterUrl()));\n                childrenList.add(children);\n                router.setChildren(childrenList);\n            }\n            routers.add(router);\n        }\n        return routers;\n    }\n\n\n    /**\n     * 获取路由名称\n     * @param menu 菜单信息\n     * @return 路由名称\n     */\n    public String getRouteName(SysMenuVO menu, SysMenuVO parentMenu) {\n        String routerName = (parentMenu != null ? StrUtils.capitalize(parentMenu.getRouterUrl()) : \"\")\n                + StrUtils.capitalize(menu.getRouterUrl());\n        // 非外链并且是一级目录（类型为目录）\n        if (isMenuFrame(menu)) {\n            routerName = StrUtils.EMPTY;\n        }\n        return routerName;\n    }\n\n    /**\n     * 是否为菜单内部跳转\n     * @param menu 菜单信息\n     * @return 结果\n     */\n    public boolean isMenuFrame(SysMenuVO menu) {\n        return isRootMenu(menu.getParentId()) && MenuType.MENU.code.equals(menu.getMenuType())\n                && menu.getHasFrame().equals(YesNo.NO.code);\n    }\n\n    /**\n     * 获取路由地址\n     * @param menu 菜单信息\n     * @return 路由地址\n     */\n    public String getRouterPath(SysMenuVO menu) {\n        String routerPath = menu.getRouterUrl();\n        // 内链打开外网方式\n        if (!isRootMenu(menu.getParentId()) && isInnerLink(menu)) {\n            routerPath = innerLinkReplaceEach(routerPath);\n        }\n        // 非外链并且是一级目录（类型为目录）\n        if (isRootMenu(menu.getParentId()) && MenuType.DIRECTORY.code.equals(menu.getMenuType())\n                && YesNo.NO.code.equals(menu.getHasFrame())) {\n            routerPath = \"/\" + menu.getRouterUrl();\n        } else if (isMenuFrame(menu)) {\n            // 非外链并且是一级目录（类型为菜单）\n            routerPath = \"/\";\n        }\n        return routerPath;\n    }\n\n    /**\n     * 是否为内链组件\n     * @param menu 菜单信息\n     */\n    public boolean isInnerLink(SysMenuVO menu) {\n        return YesNo.NO.code.equals(menu.getHasFrame()) && StrUtils.isHttp(menu.getRouterUrl());\n    }\n\n    /**\n     * 内链域名特殊字符替换\n     */\n    public String innerLinkReplaceEach(String path) {\n        return StrUtils.replaceEach(path, new String[] { CommonConstant.HTTP, CommonConstant.HTTPS },\n                new String[] { \"\", \"\" });\n    }\n\n    /**\n     * 获取组件信息\n     *\n     * @param menu 菜单信息\n     * @return 组件信息\n     */\n    public String getComponent(SysMenuVO menu) {\n        String component = MenuConstant.LAYOUT;\n        if (StrUtils.isNotBlank(menu.getComponentPath()) && !isMenuFrame(menu)) {\n            component = menu.getComponentPath();\n        } else if (StrUtils.isBlank(menu.getComponentPath()) && !isRootMenu(menu.getParentId()) && isInnerLink(menu)) {\n            component = MenuConstant.INNER_LINK;\n        } else if (StrUtils.isBlank(menu.getComponentPath()) && isParentView(menu)) {\n            component = MenuConstant.PARENT_VIEW;\n        }\n        return component;\n    }\n\n    /**\n     * 是否为parent_view组件\n     * @param menu 菜单信息\n     * @return 结果\n     */\n    public boolean isParentView(SysMenuVO menu) {\n        return !isRootMenu(menu.getParentId()) && MenuType.DIRECTORY.code.equals(menu.getMenuType());\n    }\n\n    /**\n     * 是否为根菜单\n     * @param parentId 上级菜单 id\n     * @return\n     */\n    private boolean isRootMenu(Long parentId) {\n        return parentId == null || parentId == 0;\n    }\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/menu/vo/MetaVO.java",
    "content": "package com.geshanzsq.admin.system.menu.vo;\n\nimport com.geshanzsq.common.core.util.string.StrUtils;\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\nimport java.io.Serializable;\n\n/**\n * 路由显示信息\n *\n * @author geshanzsq\n * @date 2022/6/12\n */\n@Data\n@ApiModel(\"路由显示信息\")\npublic class MetaVO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(\"设置该路由在侧边栏和面包屑中展示的名字\")\n    private String title;\n\n    @ApiModelProperty(\"设置该路由的图标，对应路径src/assets/icons/svg\")\n    private String icon;\n\n    @ApiModelProperty(\"设置为true，则不会被 <keep-alive>缓存\")\n    private boolean noCache;\n\n    @ApiModelProperty(\"内链地址（http(s)://开头）\")\n    private String link;\n\n    public MetaVO() {\n    }\n\n    public MetaVO(String title, String icon) {\n        this.title = title;\n        this.icon = icon;\n    }\n\n    public MetaVO(String title, String icon, boolean noCache) {\n        this.title = title;\n        this.icon = icon;\n        this.noCache = noCache;\n    }\n\n    public MetaVO(String title, String icon, String link) {\n        this.title = title;\n        this.icon = icon;\n        this.link = link;\n    }\n\n    public MetaVO(String title, String icon, boolean noCache, String link) {\n        this.title = title;\n        this.icon = icon;\n        this.noCache = noCache;\n        if (StrUtils.isHttp(link)) {\n            this.link = link;\n        }\n    }\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/menu/vo/RouterVO.java",
    "content": "package com.geshanzsq.admin.system.menu.vo;\n\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\nimport java.io.Serializable;\nimport java.util.List;\n\n/**\n * 路由配置信息\n *\n * @author geshanzsq\n * @date 2022/6/12\n */\n@Data\n@ApiModel(\"路由配置信息\")\npublic class RouterVO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(\"路由名字\")\n    private String name;\n\n    @ApiModelProperty(\"路由地址\")\n    private String path;\n\n    @ApiModelProperty(\"是否隐藏路由，当设置 true 的时候该路由不会再侧边栏出现\")\n    private boolean hidden;\n\n    @ApiModelProperty(\"重定向地址，当设置 noRedirect 的时候该路由在面包屑导航中不可被点击\")\n    private String redirect;\n\n    @ApiModelProperty(\"组件地址\")\n    private String component;\n\n\n    @ApiModelProperty(\"路由参数：如 {\\\"id\\\": 1, \\\"name\\\": \\\"geshanzsq\\\"}\")\n    private String query;\n\n    @ApiModelProperty(\"当你一个路由下面的 children 声明的路由大于 1 个时，自动会变成嵌套的模式--如组件页面\")\n    private Boolean alwaysShow;\n\n    @ApiModelProperty(\"其他元素\")\n    private MetaVO meta;\n\n    @ApiModelProperty(\"子路由\")\n    private List<RouterVO> children;\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/menu/vo/SysMenuAuthApiVO.java",
    "content": "package com.geshanzsq.admin.system.menu.vo;\n\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\nimport java.io.Serializable;\n\n/**\n * 菜单分配接口\n *\n * @author geshanzsq\n * @date 2022/7/27\n */\n@Data\n@ApiModel(\"菜单分配接口\")\npublic class SysMenuAuthApiVO implements Serializable {\n\n    private static final Long serialVersionUID = 1L;\n\n    @ApiModelProperty(\"接口 id\")\n    private Long apiId;\n\n    @ApiModelProperty(\"接口名称\")\n    private String apiName;\n\n    @ApiModelProperty(\"接口分类 id\")\n    private Long apiCategoryId;\n\n    @ApiModelProperty(\"接口分类名称\")\n    private String apiCategoryName;\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/menu/vo/SysMenuVO.java",
    "content": "package com.geshanzsq.admin.system.menu.vo;\n\nimport com.geshanzsq.common.core.web.vo.BaseVO;\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * 系统菜单\n *\n * @author geshanzsq\n * @date 2022/3/26\n */\n@ApiModel(\"系统菜单\")\n@Data\npublic class SysMenuVO extends BaseVO implements Serializable {\n\n    private static final Long serialVersionUID = 1L;\n\n    @ApiModelProperty(\"菜单 id\")\n    private Long id;\n\n    @ApiModelProperty(\"菜单名称\")\n    private String menuName;\n\n    @ApiModelProperty(\"上级菜单 id\")\n    private Long parentId;\n\n    @ApiModelProperty(\"排序\")\n    private Integer sort;\n\n    @ApiModelProperty(\"菜单类型（D 目录，M 菜单，B 按钮）\")\n    private String menuType;\n\n    @ApiModelProperty(\"权限标识\")\n    private String permissionCode;\n\n    @ApiModelProperty(\"路由地址\")\n    private String routerUrl;\n\n    @ApiModelProperty(\"组件路径\")\n    private String componentPath;\n\n    @ApiModelProperty(\"路由参数\")\n    private String routerParam;\n\n    @ApiModelProperty(\"是否为外链（1 是，2 否）\")\n    private Integer hasFrame;\n\n    @ApiModelProperty(\"是否缓存（1 缓存，2 不缓存）\")\n    private Integer hasCache;\n\n    @ApiModelProperty(\"是否需要权限（（1是，2 否））\")\n    private Integer hasPermission;\n\n    @ApiModelProperty(\"菜单图标\")\n    private String menuIcon;\n\n    @ApiModelProperty(\"显示状态（1 显示，2 隐藏）\")\n    private Integer showStatus;\n\n    @ApiModelProperty(\"状态（1 正常，2 停用）\")\n    private Integer status;\n\n    @ApiModelProperty(\"子菜单\")\n    private List<SysMenuVO> children = new ArrayList<>();\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/param/constant/SysParamConstant.java",
    "content": "package com.geshanzsq.admin.system.param.constant;\n\n/**\n * 系统参数常量\n *\n * @author geshanzsq\n * @date 2023/4/25\n */\npublic class SysParamConstant {\n\n    /**\n     * 缓存前缀\n     */\n    public final static String CACHE_PREFIX = \"sys:param:\";\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/param/controller/SysParamController.java",
    "content": "package com.geshanzsq.admin.system.param.controller;\n\nimport com.geshanzsq.admin.system.param.dto.SysParamAddDTO;\nimport com.geshanzsq.admin.system.param.dto.SysParamPageDTO;\nimport com.geshanzsq.admin.system.param.dto.SysParamUpdateDTO;\nimport com.geshanzsq.admin.system.param.mapstruct.SysParamConverter;\nimport com.geshanzsq.admin.system.param.po.SysParam;\nimport com.geshanzsq.admin.system.param.service.SysParamService;\nimport com.geshanzsq.admin.system.param.vo.SysParamVO;\nimport com.geshanzsq.common.core.web.response.ResponseResult;\nimport com.geshanzsq.common.framework.mybatis.page.vo.PageVO;\nimport com.geshanzsq.common.framework.web.controller.BaseController;\nimport com.geshanzsq.common.log.annotation.Log;\nimport com.geshanzsq.common.log.enums.BusinessType;\nimport io.swagger.annotations.Api;\nimport io.swagger.annotations.ApiOperation;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.validation.Valid;\nimport java.util.Arrays;\n\n/**\n * 参数配置\n *\n * @author geshanzsq\n * @date 2022/8/16\n */\n@Api(tags = \"参数配置\")\n@RestController\n@RequestMapping(\"/system/param\")\npublic class SysParamController extends BaseController {\n\n    @Autowired\n    private SysParamService sysParamService;\n\n    @ApiOperation(\"分页列表\")\n    @GetMapping(\"/page\")\n    @PreAuthorize(\"@auth.hasUrl()\")\n    public ResponseResult<PageVO<SysParamVO>> page(@Valid SysParamPageDTO pageDTO) {\n        pageDTO.setOrderColumn(\"sort,id\");\n        PageVO<SysParam> pageVO = sysParamService.page(pageDTO);\n        return ResponseResult.success(SysParamConverter.INSTANCE.convert(pageVO));\n    }\n\n    @GetMapping(\"/getById/{id}\")\n    @ApiOperation(\"详情\")\n    @PreAuthorize(\"@auth.hasUrl()\")\n    public ResponseResult<SysParamVO> getById(@PathVariable Long id) {\n        return ResponseResult.success(SysParamConverter.INSTANCE.convert(sysParamService.getById(id)));\n    }\n\n    @PostMapping()\n    @ApiOperation(\"新增\")\n    @PreAuthorize(\"@auth.hasUrl()\")\n    @Log(moduleName = \"参数配置\", businessType = BusinessType.ADD)\n    public ResponseResult add(@Valid @RequestBody SysParamAddDTO addDTO) {\n        sysParamService.save(addDTO);\n        return ResponseResult.success();\n    }\n\n    @PutMapping()\n    @ApiOperation(\"修改\")\n    @PreAuthorize(\"@auth.hasUrl()\")\n    @Log(moduleName = \"参数配置\", businessType = BusinessType.UPDATE)\n    public ResponseResult update(@Valid @RequestBody SysParamUpdateDTO updateDTO) {\n        sysParamService.updateById(updateDTO);\n        return ResponseResult.success();\n    }\n\n    @DeleteMapping(\"/delete/{ids}\")\n    @ApiOperation(\"删除\")\n    @PreAuthorize(\"@auth.hasUrl()\")\n    @Log(moduleName = \"参数配置\", businessType = BusinessType.DELETE)\n    public ResponseResult delete(@PathVariable Long[] ids) {\n        sysParamService.removeByIds(ids);\n        return ResponseResult.success();\n    }\n\n    @GetMapping(\"/getMaxSort\")\n    @ApiOperation(\"获取最大排序\")\n    @PreAuthorize(\"@auth.hasUrl()\")\n    public ResponseResult<Integer> getMaxSort() {\n        Integer maxSort = sysParamService.getMaxSort();\n        return ResponseResult.success(maxSort);\n    }\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/param/dto/SysParamAddDTO.java",
    "content": "package com.geshanzsq.admin.system.param.dto;\n\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotBlank;\nimport java.io.Serializable;\n\n/**\n * 参数配置新增\n *\n * @author geshanzsq\n * @date 2022/8/16\n */\n@Data\n@ApiModel(\"参数配置新增\")\npublic class SysParamAddDTO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(value = \"参数名称\", required = true)\n    @NotBlank(message = \"参数名称不能为空\")\n    private String paramName;\n\n    @ApiModelProperty(value = \"参数键\", required = true)\n    @NotBlank(message = \"参数键不能为空\")\n    private String paramKey;\n\n    @ApiModelProperty(value = \"参数值\", required = true)\n    @NotBlank(message = \"参数值不能为空\")\n    private String paramValue;\n\n    @ApiModelProperty(\"参数类型（1 系统参数）\")\n    private Integer paramType;\n\n    @ApiModelProperty(\"排序\")\n    private Integer sort;\n\n    @ApiModelProperty(\"备注\")\n    private String remark;\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/param/dto/SysParamPageDTO.java",
    "content": "package com.geshanzsq.admin.system.param.dto;\n\nimport com.geshanzsq.common.framework.mybatis.page.dto.PageDTO;\nimport com.geshanzsq.common.framework.mybatis.plugin.annotation.Query;\nimport com.geshanzsq.common.framework.mybatis.plugin.enums.QueryWay;\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\nimport java.io.Serializable;\n\n/**\n * 参数配置分页\n *\n * @author geshanzsq\n * @date 2022/8/16\n */\n@Data\n@ApiModel(\"参数配置分页\")\npublic class SysParamPageDTO extends PageDTO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(\"参数名称\")\n    @Query(QueryWay.LIKE)\n    private String paramName;\n\n    @ApiModelProperty(\"参数键\")\n    @Query(QueryWay.LIKE)\n    private String paramKey;\n\n    @ApiModelProperty(\"参数值\")\n    @Query(QueryWay.LIKE)\n    private String paramValue;\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/param/dto/SysParamUpdateDTO.java",
    "content": "package com.geshanzsq.admin.system.param.dto;\n\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotBlank;\nimport javax.validation.constraints.NotNull;\nimport java.io.Serializable;\n\n/**\n * 参数配置修改\n *\n * @author geshanzsq\n * @date 2022/8/16\n */\n@Data\n@ApiModel(\"参数配置修改\")\npublic class SysParamUpdateDTO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(value = \"参数 id\", required = true)\n    @NotNull(message = \"参数 id 不能为空\")\n    private Long id;\n\n    @ApiModelProperty(value = \"参数名称\", required = true)\n    @NotBlank(message = \"参数名称不能为空\")\n    private String paramName;\n\n    @ApiModelProperty(value = \"参数键\", required = true)\n    @NotBlank(message = \"参数键不能为空\")\n    private String paramKey;\n\n    @ApiModelProperty(value = \"参数值\", required = true)\n    @NotBlank(message = \"参数值不能为空\")\n    private String paramValue;\n\n    @ApiModelProperty(\"参数类型（1 系统参数）\")\n    private Integer paramType;\n\n    @ApiModelProperty(\"排序\")\n    private Integer sort;\n\n    @ApiModelProperty(\"备注\")\n    private String remark;\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/param/mapper/SysParamMapper.java",
    "content": "package com.geshanzsq.admin.system.param.mapper;\n\nimport com.geshanzsq.admin.system.param.po.SysParam;\nimport com.geshanzsq.common.framework.web.mapper.BaseMapperPlus;\nimport org.apache.ibatis.annotations.Mapper;\n\n/**\n * 参数配置\n *\n * @author geshanzsq\n * @date 2022/8/16\n */\n@Mapper\npublic interface SysParamMapper extends BaseMapperPlus<SysParam> {\n\n    /**\n     * 获取最大排序\n     */\n    Integer selectMaxSort();\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/param/mapstruct/SysParamConverter.java",
    "content": "package com.geshanzsq.admin.system.param.mapstruct;\n\nimport com.geshanzsq.admin.system.param.dto.SysParamAddDTO;\nimport com.geshanzsq.admin.system.param.dto.SysParamUpdateDTO;\nimport com.geshanzsq.admin.system.param.po.SysParam;\nimport com.geshanzsq.admin.system.param.vo.SysParamVO;\nimport com.geshanzsq.common.framework.mybatis.page.vo.PageVO;\nimport org.mapstruct.Mapper;\nimport org.mapstruct.factory.Mappers;\n\n/**\n * 参数配置对象转换\n *\n * @author geshanzsq\n * @date 2022/8/16\n */\n@Mapper\npublic interface SysParamConverter {\n\n    SysParamConverter INSTANCE = Mappers.getMapper(SysParamConverter.class);\n\n    PageVO<SysParamVO> convert(PageVO<SysParam> pageVO);\n\n    SysParamVO convert(SysParam sysParam);\n\n    SysParam convert(SysParamAddDTO addDTO);\n\n    SysParam convert(SysParamUpdateDTO updateDTO);\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/param/po/SysParam.java",
    "content": "package com.geshanzsq.admin.system.param.po;\n\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport lombok.Data;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\n/**\n * 参数配置\n *\n * @author geshanzsq\n * @date 2022/8/16\n */\n@Data\npublic class SysParam implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * 参数 id\n     */\n    @TableId\n    private Long id;\n\n    /**\n     * 参数名称\n     */\n    private String paramName;\n\n    /**\n     * 参数键\n     */\n    private String paramKey;\n\n    /**\n     * 参数值\n     */\n    private String paramValue;\n\n    /**\n     * 参数类型（1 系统参数）\n     */\n    private Integer paramType;\n\n    /**\n     * 排序\n     */\n    private Integer sort;\n\n    /**\n     * 备注\n     */\n    private String remark;\n\n    /**\n     * 创建时间\n     */\n    private Date gmtCreate;\n\n    /**\n     * 创建人用户 id\n     */\n    @TableField(\"fk_create_user_id\")\n    private Long createUserId;\n\n    /**\n     * 修改时间\n     */\n    private Date gmtModify;\n\n    /**\n     * 修改人用户 id\n     */\n    @TableField(\"fk_modify_user_id\")\n    private Long modifyUserId;\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/param/service/SysParamService.java",
    "content": "package com.geshanzsq.admin.system.param.service;\n\nimport com.geshanzsq.admin.system.param.dto.SysParamAddDTO;\nimport com.geshanzsq.admin.system.param.dto.SysParamUpdateDTO;\nimport com.geshanzsq.admin.system.param.po.SysParam;\nimport com.geshanzsq.common.framework.web.service.BaseService;\n\n/**\n * 参数配置\n *\n * @author geshanzsq\n * @date 2022/8/16\n */\npublic interface SysParamService extends BaseService<SysParam> {\n\n    /**\n     * 新增\n     */\n    void save(SysParamAddDTO addDTO);\n\n    /**\n     * 更新\n     */\n    void updateById(SysParamUpdateDTO updateDTO);\n\n    /**\n     * 删除\n     */\n    void removeByIds(Long[] ids);\n\n    /**\n     * 获取最大排序\n     */\n    Integer getMaxSort();\n\n    /**\n     * 获取参数值\n     *\n     * @param paramKey 参数键\n     */\n    String getParamValueByKey(String paramKey);\n\n    /**\n     * 清除缓存\n     *\n     * @param paramKey 参数键\n     */\n    void removeCache(String paramKey);\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/param/service/impl/SysParamServiceImpl.java",
    "content": "package com.geshanzsq.admin.system.param.service.impl;\n\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.geshanzsq.admin.system.param.constant.SysParamConstant;\nimport com.geshanzsq.admin.system.param.dto.SysParamAddDTO;\nimport com.geshanzsq.admin.system.param.dto.SysParamUpdateDTO;\nimport com.geshanzsq.admin.system.param.mapper.SysParamMapper;\nimport com.geshanzsq.admin.system.param.mapstruct.SysParamConverter;\nimport com.geshanzsq.admin.system.param.po.SysParam;\nimport com.geshanzsq.admin.system.param.service.SysParamService;\nimport com.geshanzsq.common.core.util.string.StrUtils;\nimport com.geshanzsq.common.framework.web.service.impl.BaseServiceImpl;\nimport com.geshanzsq.common.redis.service.RedisService;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.concurrent.TimeUnit;\n\n/**\n * 参数配置\n *\n * @author geshanzsq\n * @date 2022/8/16\n */\n@Service\npublic class SysParamServiceImpl extends BaseServiceImpl<SysParamMapper, SysParam> implements SysParamService {\n\n    @Autowired\n    private SysParamMapper sysParamMapper;\n\n    @Autowired\n    private RedisService redisService;\n\n    @Override\n    public void save(SysParamAddDTO addDTO) {\n        SysParam sysParam = SysParamConverter.INSTANCE.convert(addDTO);\n        sysParamMapper.insert(sysParam);\n        // 清除缓存\n        removeCache(addDTO.getParamKey());\n    }\n\n    /**\n     * 更新\n     */\n    @Override\n    public void updateById(SysParamUpdateDTO updateDTO) {\n        SysParam oldSysParam = sysParamMapper.selectById(updateDTO.getId());\n        if (oldSysParam == null) {\n            return;\n        }\n        SysParam sysParam = SysParamConverter.INSTANCE.convert(updateDTO);\n        sysParamMapper.updateById(sysParam);\n        // 清除缓存\n        removeCache(oldSysParam.getParamKey());\n    }\n\n\n    /**\n     * 删除\n     */\n    @Override\n    public void removeByIds(Long[] ids) {\n        if (ids == null || ids.length == 0) {\n            return;\n        }\n        sysParamMapper.deleteBatchIds(Arrays.asList(ids));\n        // 清除缓存\n        LambdaQueryWrapper<SysParam> wrapper = new LambdaQueryWrapper<>();\n        wrapper.in(SysParam::getId, ids);\n        wrapper.select(SysParam::getParamKey);\n        List<SysParam> sysParams = sysParamMapper.selectList(wrapper);\n        sysParams.forEach(param -> {\n            removeCache(param.getParamKey());\n        });\n    }\n\n    /**\n     * 获取最大排序\n     */\n    @Override\n    public Integer getMaxSort() {\n        return sysParamMapper.selectMaxSort();\n    }\n\n    /**\n     * 获取参数值\n     *\n     * @param paramKey 参数键\n     */\n    @Override\n    public String getParamValueByKey(String paramKey) {\n        if (StrUtils.isBlank(paramKey)) {\n            return paramKey;\n        }\n        String value = redisService.get(SysParamConstant.CACHE_PREFIX + paramKey);\n        if (value != null) {\n            return value;\n        }\n        // 从数据库获取\n        LambdaQueryWrapper<SysParam> wrapper = new LambdaQueryWrapper<>();\n        wrapper.eq(SysParam::getParamKey, paramKey);\n        SysParam sysParam = sysParamMapper.selectOne(wrapper);\n        value = sysParam == null ? null : sysParam.getParamValue();\n        // 存入缓存，有效期为 3 天\n        redisService.set(SysParamConstant.CACHE_PREFIX + paramKey, value, 3, TimeUnit.DAYS);\n        return value;\n    }\n\n    /**\n     * 清除缓存\n     *\n     * @param paramKey 参数键\n     */\n    @Override\n    public void removeCache(String paramKey) {\n        redisService.delete(SysParamConstant.CACHE_PREFIX + paramKey);\n    }\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/param/vo/SysParamVO.java",
    "content": "package com.geshanzsq.admin.system.param.vo;\n\nimport com.geshanzsq.common.core.web.vo.BaseVO;\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\nimport java.io.Serializable;\n\n/**\n * 参数配置\n *\n * @author geshanzsq\n * @date 2022/8/16\n */\n@Data\n@ApiModel(\"参数配置\")\npublic class SysParamVO extends BaseVO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(\"参数 id\")\n    private Long id;\n\n    @ApiModelProperty(\"参数名称\")\n    private String paramName;\n\n    @ApiModelProperty(\"参数键\")\n    private String paramKey;\n\n    @ApiModelProperty(\"参数值\")\n    private String paramValue;\n\n    @ApiModelProperty(\"参数类型（1 系统参数）\")\n    private Integer paramType;\n\n    @ApiModelProperty(\"排序\")\n    private Integer sort;\n\n    @ApiModelProperty(\"备注\")\n    private String remark;\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/role/controller/SysRoleController.java",
    "content": "package com.geshanzsq.admin.system.role.controller;\n\nimport com.geshanzsq.admin.system.role.dto.*;\nimport com.geshanzsq.admin.system.role.mapstruct.SysRoleConverter;\nimport com.geshanzsq.admin.system.role.po.SysRole;\nimport com.geshanzsq.admin.system.role.service.SysRoleService;\nimport com.geshanzsq.admin.system.role.vo.SysRoleVO;\nimport com.geshanzsq.admin.system.user.vo.SysUserVO;\nimport com.geshanzsq.common.core.web.response.ResponseResult;\nimport com.geshanzsq.common.framework.mybatis.page.vo.PageVO;\nimport com.geshanzsq.common.framework.web.controller.BaseController;\nimport com.geshanzsq.common.log.annotation.Log;\nimport com.geshanzsq.common.log.enums.BusinessType;\nimport io.swagger.annotations.Api;\nimport io.swagger.annotations.ApiOperation;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.validation.Valid;\n\n/**\n * 系统角色\n *\n * @author geshanzsq\n * @date 2022/6/19\n */\n@Api(tags = \"系统角色\")\n@RestController\n@RequestMapping(\"/system/role\")\npublic class SysRoleController extends BaseController {\n\n    @Autowired\n    private SysRoleService sysRoleService;\n\n    @ApiOperation(\"分页列表\")\n    @GetMapping(\"/page\")\n    @PreAuthorize(\"@auth.hasUrl()\")\n    public ResponseResult<PageVO<SysRoleVO>> page(SysRolePageDTO pageDTO) {\n        pageDTO.setOrderColumn(\"sort,id\");\n        PageVO<SysRole> pageVO = sysRoleService.page(pageDTO);\n        return ResponseResult.success(SysRoleConverter.INSTANCE.convert(pageVO));\n    }\n\n    @GetMapping(\"/getById/{id}\")\n    @ApiOperation(\"详情\")\n    @PreAuthorize(\"@auth.hasUrl()\")\n    public ResponseResult<SysRoleVO> getById(@PathVariable Long id) {\n        SysRoleVO sysRoleVo = sysRoleService.getRoleById(id);\n        return ResponseResult.success(sysRoleVo);\n    }\n\n    @PostMapping()\n    @ApiOperation(\"新增\")\n    @PreAuthorize(\"@auth.hasUrl()\")\n    @Log(moduleName = \"角色管理\", businessType = BusinessType.ADD)\n    public ResponseResult add(@Valid @RequestBody SysRoleAddDTO addDTO) {\n        sysRoleService.add(addDTO);\n        return ResponseResult.success();\n    }\n\n    @PutMapping()\n    @ApiOperation(\"修改\")\n    @PreAuthorize(\"@auth.hasUrl()\")\n    @Log(moduleName = \"角色管理\", businessType = BusinessType.UPDATE)\n    public ResponseResult update(@Valid @RequestBody SysRoleUpdateDTO updateDTO) {\n        sysRoleService.update(updateDTO);\n        return ResponseResult.success();\n    }\n\n    @DeleteMapping(\"/delete/{ids}\")\n    @ApiOperation(\"删除\")\n    @PreAuthorize(\"@auth.hasUrl()\")\n    @Log(moduleName = \"角色管理\", businessType = BusinessType.DELETE)\n    public ResponseResult delete(@PathVariable Long[] ids) {\n        sysRoleService.removeByIds(ids);\n        return ResponseResult.success();\n    }\n\n    @GetMapping(\"/getMaxSort\")\n    @ApiOperation(\"获取最大排序\")\n    @PreAuthorize(\"@auth.hasUrl()\")\n    public ResponseResult<Integer> getMaxSort() {\n        Integer maxSort = sysRoleService.getMaxSort();\n        return ResponseResult.success(maxSort);\n    }\n\n    @GetMapping(\"/auth/user/page\")\n    @ApiOperation(\"已分配用户分页\")\n    @PreAuthorize(\"@auth.hasUrl()\")\n    public ResponseResult<SysUserVO> getUserAuthPage(@Valid SysRoleAuthUserPageDTO pageDTO) {\n        PageVO<SysUserVO> pageVO = sysRoleService.getUserAuthPage(pageDTO);\n        return ResponseResult.success(pageVO);\n    }\n\n    @GetMapping(\"/auth/user/not/page\")\n    @ApiOperation(\"未分配用户分页\")\n    @PreAuthorize(\"@auth.hasUrl()\")\n    public ResponseResult<SysUserVO> getUserUnAuthPage(@Valid SysRoleNotAuthUserPageDTO pageDTO) {\n        PageVO<SysUserVO> pageVO = sysRoleService.getUserUnAuthPage(pageDTO);\n        return ResponseResult.success(pageVO);\n    }\n\n    @PostMapping(\"/auth/user\")\n    @ApiOperation(\"授权用户\")\n    @PreAuthorize(\"@auth.hasUrl()\")\n    @Log(moduleName = \"角色管理-授权用户\", businessType = BusinessType.ADD)\n    public ResponseResult authUser(@Valid @RequestBody SysRoleAuthUserDTO authUserDTO) {\n        sysRoleService.authUser(authUserDTO);\n        return ResponseResult.success();\n    }\n\n    @DeleteMapping(\"/auth/user/delete\")\n    @ApiOperation(\"取消授权\")\n    @PreAuthorize(\"@auth.hasUrl()\")\n    @Log(moduleName = \"角色管理-取消授权\", businessType = BusinessType.DELETE)\n    public ResponseResult deleteAuthUser(@Valid @RequestBody SysRoleAuthUserDeleteDTO deleteDTO) {\n        sysRoleService.removeAuthUser(deleteDTO);\n        return ResponseResult.success();\n    }\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/role/dto/SysRoleAddDTO.java",
    "content": "package com.geshanzsq.admin.system.role.dto;\n\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotBlank;\nimport javax.validation.constraints.NotNull;\nimport java.io.Serializable;\nimport java.util.List;\n\n/**\n * 角色新增\n *\n * @author geshanzsq\n * @date 2022/6/19\n */\n@Data\n@ApiModel(\"角色新增\")\npublic class SysRoleAddDTO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(value = \"角色名称\", required = true)\n    @NotBlank(message = \"角色名称不能为空\")\n    private String roleName;\n\n    @ApiModelProperty(value = \"角色编码\", required = true)\n    @NotBlank(message = \"角色编码不能为空\")\n    private String roleCode;\n\n    @ApiModelProperty(value = \"排序\", required = true)\n    @NotNull(message = \"角色编码不能为空\")\n    private Integer sort;\n\n    @ApiModelProperty(value = \"状态（1 正常，2 停用）\", required = true)\n    @NotNull(message = \"状态不能为空\")\n    private Integer status;\n\n    @ApiModelProperty(\"备注\")\n    private String remark;\n\n    @ApiModelProperty(\"关联菜单 ids\")\n    private List<Long> menuIds;\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/role/dto/SysRoleAuthUserDTO.java",
    "content": "package com.geshanzsq.admin.system.role.dto;\n\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\nimport java.io.Serializable;\nimport java.util.List;\n\n/**\n * 授权用户\n *\n * @author geshanzsq\n * @date 2022/7/17\n */\n@Data\n@ApiModel(\"授权用户\")\npublic class SysRoleAuthUserDTO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(value = \"角色 id\", required = true)\n    @NotNull(message = \"角色 id 不能为空\")\n    private Long roleId;\n\n    @ApiModelProperty(value = \"用户 ids\", required = true)\n    @NotEmpty(message = \"用户 ids 不能为空\")\n    private List<Long> userIds;\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/role/dto/SysRoleAuthUserDeleteDTO.java",
    "content": "package com.geshanzsq.admin.system.role.dto;\n\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\nimport java.io.Serializable;\nimport java.util.List;\n\n/**\n * 系统角色取消授权\n *\n * @author geshanzsq\n * @date 2022/7/17\n */\n@Data\n@ApiModel(\"系统角色取消授权\")\npublic class SysRoleAuthUserDeleteDTO implements Serializable {\n\n    private static final Long serialVersionUID = 1L;\n\n    @ApiModelProperty(value = \"角色 id\", required = true)\n    @NotNull(message = \"角色 id 不能为空\")\n    private Long roleId;\n\n    @ApiModelProperty(value = \"用户 ids\", required = true)\n    @NotEmpty(message = \"用户不能为空\")\n    private List<Long> userIds;\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/role/dto/SysRoleAuthUserPageDTO.java",
    "content": "package com.geshanzsq.admin.system.role.dto;\n\nimport com.geshanzsq.common.framework.mybatis.page.dto.PageDTO;\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\nimport java.io.Serializable;\n\n/**\n * 角色分配用户分页查询\n *\n * @author geshanzsq\n * @date 2022/7/17\n */\n@Data\n@ApiModel(\"角色分配用户分页查询\")\npublic class SysRoleAuthUserPageDTO extends PageDTO implements Serializable {\n\n    private static final Long serialVersionUID = 1L;\n\n    @ApiModelProperty(value = \"角色 id\", required = true)\n    @NotNull(message = \"角色 id 不能为空\")\n    private Long roleId;\n\n    @ApiModelProperty(\"用户名\")\n    private String username;\n\n    @ApiModelProperty(\"昵称\")\n    private String nickName;\n\n    @ApiModelProperty(\"状态，1 正常，2 停用\")\n    private Integer status;\n\n    @ApiModelProperty(\"用户类型，1 后台用户，2 博客用户\")\n    private Integer userType;\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/role/dto/SysRoleNotAuthUserPageDTO.java",
    "content": "package com.geshanzsq.admin.system.role.dto;\n\nimport com.geshanzsq.common.framework.mybatis.page.dto.PageDTO;\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotNull;\nimport java.io.Serializable;\n\n/**\n * 角色未分配用户分页查询\n *\n * @author geshanzsq\n * @date 2022/7/17\n */\n@Data\n@ApiModel(\"角色未分配用户分页查询\")\npublic class SysRoleNotAuthUserPageDTO extends PageDTO implements Serializable {\n\n    private static final Long serialVersionUID = 1L;\n\n    @ApiModelProperty(value = \"角色 id\", required = true)\n    @NotNull(message = \"角色 id 不能为空\")\n    private Long roleId;\n\n    @ApiModelProperty(\"用户名\")\n    private String username;\n\n    @ApiModelProperty(\"昵称\")\n    private String nickName;\n\n    @ApiModelProperty(\"状态，1 正常，2 停用\")\n    private Integer status;\n\n    @ApiModelProperty(\"用户类型，1 后台用户，2 博客用户\")\n    private Integer userType;\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/role/dto/SysRolePageDTO.java",
    "content": "package com.geshanzsq.admin.system.role.dto;\n\nimport com.geshanzsq.common.framework.mybatis.page.dto.PageDTO;\nimport com.geshanzsq.common.framework.mybatis.plugin.annotation.Query;\nimport com.geshanzsq.common.framework.mybatis.plugin.enums.QueryWay;\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\nimport java.io.Serializable;\n\n/**\n * 角色分页查询\n *\n * @author geshanzsq\n * @date 2022/6/19\n */\n@Data\n@ApiModel(\"角色分页查询\")\npublic class SysRolePageDTO extends PageDTO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(\"角色名称\")\n    @Query(QueryWay.LIKE)\n    private String roleName;\n\n    @ApiModelProperty(\"角色编码\")\n    @Query(QueryWay.LIKE)\n    private String roleCode;\n\n    @ApiModelProperty(\"状态，1 正常，2 停用\")\n    private Integer status;\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/role/dto/SysRoleUpdateDTO.java",
    "content": "package com.geshanzsq.admin.system.role.dto;\n\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotBlank;\nimport javax.validation.constraints.NotNull;\nimport java.io.Serializable;\nimport java.util.List;\n\n/**\n * 角色修改\n *\n * @author geshanzsq\n * @date 2022/6/19\n */\n@Data\n@ApiModel(\"角色修改\")\npublic class SysRoleUpdateDTO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(value = \"角色 id\", required = true)\n    @NotNull(message = \"角色 id 不能为空\")\n    private Long id;\n\n    @ApiModelProperty(value = \"角色名称\", required = true)\n    @NotBlank(message = \"角色名称不能为空\")\n    private String roleName;\n\n    @ApiModelProperty(value = \"角色编码\", required = true)\n    @NotBlank(message = \"角色编码不能为空\")\n    private String roleCode;\n\n    @ApiModelProperty(value = \"排序\", required = true)\n    @NotNull(message = \"角色编码不能为空\")\n    private Integer sort;\n\n    @ApiModelProperty(value = \"状态（1 正常，2 停用）\", required = true)\n    @NotNull(message = \"状态不能为空\")\n    private Integer status;\n\n    @ApiModelProperty(\"备注\")\n    private String remark;\n\n    @ApiModelProperty(\"关联菜单 ids\")\n    private List<Long> menuIds;\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/role/mapper/SysRoleMapper.java",
    "content": "package com.geshanzsq.admin.system.role.mapper;\n\nimport com.geshanzsq.admin.system.role.po.SysRole;\nimport com.geshanzsq.admin.system.role.vo.SysRoleVO;\nimport com.geshanzsq.common.framework.web.mapper.BaseMapperPlus;\nimport org.apache.ibatis.annotations.Mapper;\nimport org.apache.ibatis.annotations.Param;\n\nimport java.util.List;\n\n/**\n * 系统角色\n *\n * @author geshanzsq\n * @date 2022/3/26\n */\n@Mapper\npublic interface SysRoleMapper extends BaseMapperPlus<SysRole> {\n\n    /**\n     * 通过用户 id 获取角色\n     * @param userId 用户 id\n     * @param status 角色状态\n     */\n    List<SysRoleVO> getRoleByUserIds(@Param(\"userIds\") List<Long> userId, @Param(\"status\") Integer status);\n\n    /**\n     * 获取最大排序\n     */\n    Integer selectMaxSort();\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/role/mapper/SysRoleMenuMapper.java",
    "content": "package com.geshanzsq.admin.system.role.mapper;\n\nimport com.geshanzsq.admin.system.role.po.SysRoleMenu;\nimport com.geshanzsq.common.framework.web.mapper.BaseMapperPlus;\nimport org.apache.ibatis.annotations.Mapper;\n\n/**\n * 角色菜单\n *\n * @author geshanzsq\n * @date 2022/6/22\n */\n@Mapper\npublic interface SysRoleMenuMapper extends BaseMapperPlus<SysRoleMenu> {\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/role/mapstruct/SysRoleConverter.java",
    "content": "package com.geshanzsq.admin.system.role.mapstruct;\n\nimport com.geshanzsq.admin.system.role.dto.SysRoleAddDTO;\nimport com.geshanzsq.admin.system.role.dto.SysRoleAuthUserPageDTO;\nimport com.geshanzsq.admin.system.role.dto.SysRoleNotAuthUserPageDTO;\nimport com.geshanzsq.admin.system.role.dto.SysRoleUpdateDTO;\nimport com.geshanzsq.admin.system.role.po.SysRole;\nimport com.geshanzsq.admin.system.role.vo.SysRoleVO;\nimport com.geshanzsq.admin.system.user.dto.SysUserPageDTO;\nimport com.geshanzsq.common.framework.mybatis.page.vo.PageVO;\nimport org.mapstruct.Mapper;\nimport org.mapstruct.factory.Mappers;\n\n/**\n * 角色对象转换\n *\n * @author geshanzsq\n * @date 2022/6/19\n */\n@Mapper\npublic interface SysRoleConverter {\n\n    SysRoleConverter INSTANCE = Mappers.getMapper(SysRoleConverter.class);\n\n    SysRoleVO convert(SysRole sysRole);\n\n    PageVO<SysRoleVO> convert(PageVO<SysRole> pageVO);\n\n    SysRole convert(SysRoleAddDTO addDTO);\n\n    SysRole convert(SysRoleUpdateDTO updateDTO);\n\n    SysUserPageDTO convert(SysRoleAuthUserPageDTO pageDTO);\n\n    SysUserPageDTO convert(SysRoleNotAuthUserPageDTO pageDTO);\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/role/po/SysRole.java",
    "content": "package com.geshanzsq.admin.system.role.po;\n\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport lombok.Data;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\n/**\n * 系统角色\n *\n * @author geshanzsq\n * @date 2022/3/26\n */\n@Data\npublic class SysRole implements Serializable {\n\n    private static final Long serialVersionUID = 1L;\n\n    /**\n     * 角色 id\n     */\n    @TableId\n    private Long id;\n\n    /**\n     * 角色名称\n     */\n    private String roleName;\n\n    /**\n     * 角色编码\n     */\n    private String roleCode;\n\n    /**\n     * 排序\n     */\n    private Integer sort;\n\n    /**\n     * 状态，1 正常，2 停用\n     */\n    private Integer status;\n\n    /**\n     * 备注\n     */\n    private String remark;\n\n    /**\n     * 创建时间\n     */\n    private Date gmtCreate;\n\n    /**\n     * 创建人用户 id\n     */\n    @TableField(\"fk_create_user_id\")\n    private Long createUserId;\n\n    /**\n     * 修改时间\n     */\n    private Date gmtModify;\n\n    /**\n     * 修改人用户 id\n     */\n    @TableField(\"fk_modify_user_id\")\n    private Long modifyUserId;\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/role/po/SysRoleMenu.java",
    "content": "package com.geshanzsq.admin.system.role.po;\n\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport lombok.Data;\n\nimport java.io.Serializable;\n\n/**\n * 系统角色菜单\n *\n * @author geshanzsq\n * @date 2022/6/22\n */\n@Data\npublic class SysRoleMenu implements Serializable {\n\n    private static final Long serialVersionUID = 1L;\n    /**\n     * 角色菜单 id\n     */\n    @TableId\n    private Long id;\n\n    /**\n     * 角色 id\n     */\n    @TableField(\"fk_role_id\")\n    private Long roleId;\n\n    /**\n     * 菜单 id\n     */\n    @TableField(\"fk_menu_id\")\n    private Long menuId;\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/role/service/SysRoleMenuService.java",
    "content": "package com.geshanzsq.admin.system.role.service;\n\nimport com.geshanzsq.admin.system.role.po.SysRoleMenu;\nimport com.geshanzsq.common.framework.web.service.BaseService;\n\nimport java.util.List;\n\n/**\n * 角色菜单\n *\n * @author geshanzsq\n * @date 2022/6/22\n */\npublic interface SysRoleMenuService extends BaseService<SysRoleMenu> {\n\n    /**\n     * 批量插入\n     * @param roleId 角色 id\n     * @param menuIds 菜单 ids\n     */\n    void saveBatch(Long roleId, List<Long> menuIds);\n\n    /**\n     * 通过角色 id 获取菜单 ids\n     * @param roleId 角色 id\n     * @return\n     */\n    List<Long> getMenuIdsByRoleId(Long roleId);\n\n    /**\n     * 删除角色和菜单关系\n     * @param roleId 角色 id\n     */\n    void deleteByRoleId(Long roleId);\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/role/service/SysRoleService.java",
    "content": "package com.geshanzsq.admin.system.role.service;\n\nimport com.geshanzsq.admin.system.role.dto.*;\nimport com.geshanzsq.admin.system.role.po.SysRole;\nimport com.geshanzsq.admin.system.role.vo.SysRoleVO;\nimport com.geshanzsq.admin.system.user.vo.SysUserVO;\nimport com.geshanzsq.common.framework.mybatis.page.vo.PageVO;\nimport com.geshanzsq.common.framework.web.service.BaseService;\n\nimport java.util.List;\n\n/**\n * 系统角色\n *\n * @author geshanzsq\n * @date 2022/3/26\n */\npublic interface SysRoleService extends BaseService<SysRole> {\n\n    /**\n     * 通过用户 id 获取角色\n     * @param userId 用户 id\n     */\n    List<SysRoleVO> getRoleByUserId(Long userId);\n\n    /**\n     * 通过用户 ids 获取角色\n     * @param userIds 用户 ids\n     */\n    List<SysRoleVO> getRoleByUserIds(List<Long> userIds);\n\n    /**\n     * 新增角色\n     */\n    void add(SysRoleAddDTO addDTO);\n\n    /**\n     * 修改角色\n     */\n    void update(SysRoleUpdateDTO updateDTO);\n\n    /**\n     * 删除角色\n     */\n    void removeByIds(Long[] ids);\n\n    /**\n     * 获取最大排序\n     */\n    Integer getMaxSort();\n\n    /**\n     * 获取角色信息\n     * @param id 角色 id\n     */\n    SysRoleVO getRoleById(Long id);\n\n    /**\n     * 已分配用户分页\n     */\n    PageVO<SysUserVO> getUserAuthPage(SysRoleAuthUserPageDTO pageDTO);\n\n    /**\n     * 未分配用户分页\n     */\n    PageVO<SysUserVO> getUserUnAuthPage(SysRoleNotAuthUserPageDTO pageDTO);\n\n    /**\n     * 授权用户\n     */\n    void authUser(SysRoleAuthUserDTO authUserDTO);\n\n    /**\n     * 取消授权\n     */\n    void removeAuthUser(SysRoleAuthUserDeleteDTO deleteDTO);\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/role/service/impl/SysRoleMenuServiceImpl.java",
    "content": "package com.geshanzsq.admin.system.role.service.impl;\n\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.geshanzsq.admin.system.role.mapper.SysRoleMenuMapper;\nimport com.geshanzsq.admin.system.role.po.SysRoleMenu;\nimport com.geshanzsq.admin.system.role.service.SysRoleMenuService;\nimport com.geshanzsq.common.framework.web.service.impl.BaseServiceImpl;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\nimport org.springframework.util.CollectionUtils;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\n/**\n * 角色菜单\n *\n * @author geshanzsq\n * @date 2022/6/22\n */\n@Service\npublic class SysRoleMenuServiceImpl extends BaseServiceImpl<SysRoleMenuMapper, SysRoleMenu> implements SysRoleMenuService {\n\n    @Autowired\n    private SysRoleMenuMapper sysRoleMenuMapper;\n\n    /**\n     * 批量插入\n     * @param roleId 角色 id\n     * @param menuIds 菜单 ids\n     */\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void saveBatch(Long roleId, List<Long> menuIds) {\n        if (CollectionUtils.isEmpty(menuIds)) {\n            return;\n        }\n        List<SysRoleMenu> roleMenus = new ArrayList<>();\n        for (Long menuId : menuIds) {\n            SysRoleMenu sysRoleMenu = new SysRoleMenu();\n            sysRoleMenu.setMenuId(menuId);\n            sysRoleMenu.setRoleId(roleId);\n            roleMenus.add(sysRoleMenu);\n        }\n        super.saveBatch(roleMenus);\n    }\n\n    /**\n     * 通过角色 id 获取菜单 ids\n     * @param roleId 角色 id\n     */\n    @Override\n    public List<Long> getMenuIdsByRoleId(Long roleId) {\n        if (roleId == null) {\n            return new ArrayList<>();\n        }\n        LambdaQueryWrapper<SysRoleMenu> wrapper = new LambdaQueryWrapper<>();\n        wrapper.eq(SysRoleMenu::getRoleId, roleId);\n        wrapper.select(SysRoleMenu::getMenuId);\n        List<SysRoleMenu> roleMenus = sysRoleMenuMapper.selectList(wrapper);\n        Set<Long> menuIds = roleMenus.stream().map(rm -> rm.getMenuId()).collect(Collectors.toSet());\n        return new ArrayList<>(menuIds);\n    }\n\n    /**\n     * 删除角色和菜单关系\n     * @param roleId 角色 id\n     */\n    @Override\n    public void deleteByRoleId(Long roleId) {\n        if (roleId == null) {\n            return;\n        }\n        LambdaQueryWrapper<SysRoleMenu> wrapper = new LambdaQueryWrapper<>();\n        wrapper.eq(SysRoleMenu::getRoleId, roleId);\n        sysRoleMenuMapper.delete(wrapper);\n    }\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/role/service/impl/SysRoleServiceImpl.java",
    "content": "package com.geshanzsq.admin.system.role.service.impl;\n\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.geshanzsq.admin.system.role.dto.*;\nimport com.geshanzsq.admin.system.role.mapper.SysRoleMapper;\nimport com.geshanzsq.admin.system.role.mapstruct.SysRoleConverter;\nimport com.geshanzsq.admin.system.role.po.SysRole;\nimport com.geshanzsq.admin.system.role.service.SysRoleMenuService;\nimport com.geshanzsq.admin.system.role.service.SysRoleService;\nimport com.geshanzsq.admin.system.role.vo.SysRoleVO;\nimport com.geshanzsq.admin.system.user.dto.SysUserPageDTO;\nimport com.geshanzsq.admin.system.user.po.SysUserRole;\nimport com.geshanzsq.admin.system.user.service.SysUserRoleService;\nimport com.geshanzsq.admin.system.user.service.SysUserService;\nimport com.geshanzsq.admin.system.user.vo.SysUserVO;\nimport com.geshanzsq.common.core.enums.CommonStatus;\nimport com.geshanzsq.common.core.exception.ParamException;\nimport com.geshanzsq.common.core.util.message.MessageUtils;\nimport com.geshanzsq.common.core.util.string.StrUtils;\nimport com.geshanzsq.common.framework.mybatis.page.vo.PageVO;\nimport com.geshanzsq.common.framework.web.service.impl.BaseServiceImpl;\nimport com.geshanzsq.framework.security.constant.SecurityConstant;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\nimport org.springframework.util.CollectionUtils;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\n/**\n * 系统角色\n *\n * @author geshanzsq\n * @date 2022/3/26\n */\n@Service\npublic class SysRoleServiceImpl extends BaseServiceImpl<SysRoleMapper, SysRole> implements SysRoleService {\n\n    @Autowired\n    private SysRoleMapper sysRoleMapper;\n\n    @Autowired\n    private SysRoleMenuService sysRoleMenuService;\n    @Autowired\n    private SysUserService sysUserService;\n    @Autowired\n    private SysUserRoleService sysUserRoleService;\n\n    /**\n     * 通过用户 id 获取角色\n     * @param userId 用户 id\n     */\n    @Override\n    public List<SysRoleVO> getRoleByUserId(Long userId) {\n        return sysRoleMapper.getRoleByUserIds(Arrays.asList(userId), CommonStatus.NORMAL.code);\n    }\n\n    /**\n     * 通过用户 ids 获取角色\n     * @param userIds 用户 ids\n     * @return\n     */\n    @Override\n    public List<SysRoleVO> getRoleByUserIds(List<Long> userIds) {\n        if (CollectionUtils.isEmpty(userIds)) {\n            return new ArrayList<>();\n        }\n        return sysRoleMapper.getRoleByUserIds(userIds, CommonStatus.NORMAL.code);\n    }\n\n    /**\n     * 新增角色\n     */\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void add(SysRoleAddDTO addDTO) {\n        // 判断角色编码是否存在\n        if (isExistRoleCode(addDTO.getRoleCode(), null)) {\n            throw new ParamException(MessageUtils.message(\"role.code.exist\"));\n        }\n\n        // 插入角色信息\n        SysRole sysRole = SysRoleConverter.INSTANCE.convert(addDTO);\n        sysRoleMapper.insert(sysRole);\n\n        // 插入角色和菜单信息\n        sysRoleMenuService.saveBatch(sysRole.getId(), addDTO.getMenuIds());\n    }\n\n    /**\n     * 修改角色\n     */\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void update(SysRoleUpdateDTO updateDTO) {\n        Long roleId = updateDTO.getId();\n\n        // 判断是否为超级管理员角色\n        if (isSuperAdminRoleByRoleIds(Arrays.asList(roleId))) {\n            throw new ParamException(MessageUtils.message(\"role.super.admin.not.operation\"));\n        }\n\n        // 判断角色编码是否存在\n        if (isExistRoleCode(updateDTO.getRoleCode(), roleId)) {\n            throw new ParamException(MessageUtils.message(\"role.code.exist\"));\n        }\n\n        // 更新角色信息\n        SysRole sysRole = SysRoleConverter.INSTANCE.convert(updateDTO);\n        sysRoleMapper.updateById(sysRole);\n\n        // 删除角色和菜单关系\n        sysRoleMenuService.deleteByRoleId(roleId);\n\n        // 插入角色和菜单信息\n        sysRoleMenuService.saveBatch(roleId, updateDTO.getMenuIds());\n    }\n\n    /**\n     * 删除角色\n     */\n    @Transactional(rollbackFor = Exception.class)\n    @Override\n    public void removeByIds(Long[] ids) {\n        if (ids.length == 0) {\n            return;\n        }\n        List<Long> idList = Arrays.asList(ids);\n        // 判断是否为超级管理员角色\n        if (isSuperAdminRoleByRoleIds(idList)) {\n            throw new ParamException(MessageUtils.message(\"role.super.admin.not.operation\"));\n        }\n\n        // 判断角色是否关联用户，如果关联了用户，需先取消关联才能删除\n        if (sysUserRoleService.isAssociateUser(idList)) {\n            throw new ParamException(MessageUtils.message(\"role.is.associate.user\"));\n        }\n\n        // 删除角色\n        sysRoleMapper.deleteBatchIds(idList);\n\n        // 删除角色和用户对应关系，虽然前面已经做了是否关联用户，但是为了避免已删除的用户有关联角色有脏的对应关系，所以需要删除\n        LambdaQueryWrapper<SysUserRole> urWrapper = new LambdaQueryWrapper<>();\n        urWrapper.in(SysUserRole::getRoleId, ids);\n        sysUserRoleService.removeByIds(idList);\n    }\n\n    /**\n     * 获取最大排序\n     */\n    @Override\n    public Integer getMaxSort() {\n        return sysRoleMapper.selectMaxSort();\n    }\n\n    /**\n     * 获取角色信息\n     * @param id 角色 id\n     */\n    @Override\n    public SysRoleVO getRoleById(Long id) {\n        SysRoleVO sysRoleVO = SysRoleConverter.INSTANCE.convert(sysRoleMapper.selectById(id));\n        if (sysRoleVO != null) {\n            // 获取角色对应的菜单 Id\n            sysRoleVO.setMenuIds(sysRoleMenuService.getMenuIdsByRoleId(id));\n        }\n        return sysRoleVO;\n    }\n\n    /**\n     * 已分配用户分页\n     */\n    @Override\n    public PageVO<SysUserVO> getUserAuthPage(SysRoleAuthUserPageDTO pageDTO) {\n        // 获取已分配的用户\n        List<Long> userIds = sysUserRoleService.getUserIdsByRoleId(pageDTO.getRoleId());\n        // 如果获取的用户 id 为空，说明没有关联用户，直接返回\n        if (CollectionUtils.isEmpty(userIds)) {\n            return new PageVO<>();\n        }\n\n        // 查询用户数据\n        SysUserPageDTO sysUserPageDTO = SysRoleConverter.INSTANCE.convert(pageDTO);\n        sysUserPageDTO.setIds(userIds);\n        return sysUserService.pageList(sysUserPageDTO);\n    }\n\n    /**\n     * 未分配用户分页\n     */\n    @Override\n    public PageVO<SysUserVO> getUserUnAuthPage(SysRoleNotAuthUserPageDTO pageDTO) {\n        // 获取已分配的用户\n        List<Long> userIds = sysUserRoleService.getUserIdsByRoleId(pageDTO.getRoleId());\n        // 查询用户数据\n        SysUserPageDTO sysUserPageDTO = SysRoleConverter.INSTANCE.convert(pageDTO);\n        sysUserPageDTO.setNotIds(userIds);\n        return sysUserService.pageList(sysUserPageDTO);\n    }\n\n    /**\n     * 授权用户\n     */\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void authUser(SysRoleAuthUserDTO authUserDto) {\n        Long roleId = authUserDto.getRoleId();\n        // 先判断角色是否存在\n        LambdaQueryWrapper<SysRole> wrapper = new LambdaQueryWrapper<>();\n        wrapper.eq(SysRole::getId, roleId);\n        if (sysRoleMapper.selectCount(wrapper) == 0) {\n            throw new ParamException(MessageUtils.message(\"role.not.exist\"));\n        }\n\n        // 过滤不存在的用户 id\n        List<Long> userIds = sysUserService.filterNotExistUserIds(authUserDto.getUserIds());\n        if (CollectionUtils.isEmpty(userIds)) {\n            throw new ParamException(MessageUtils.message(\"role.auth.user.not.exit\"));\n        }\n\n        // 插入角色和用户直接的关系\n        sysUserRoleService.saveBatch(roleId, userIds);\n    }\n\n    /**\n     * 取消授权\n     */\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void removeAuthUser(SysRoleAuthUserDeleteDTO deleteDto) {\n        sysUserRoleService.remove(deleteDto.getRoleId(), deleteDto.getUserIds());\n    }\n\n    /**\n     * 角色编码是否存在\n     * @param roleCode 角色编码\n     * @param roleId 角色 id，用于区别新增还是编辑\n     * @return\n     */\n    private boolean isExistRoleCode(String roleCode, Long roleId) {\n        if (StrUtils.isBlank(roleCode)) {\n            return false;\n        }\n        LambdaQueryWrapper<SysRole> wrapper = new LambdaQueryWrapper<>();\n        wrapper.eq(SysRole::getRoleCode, roleCode);\n        if (roleId != null) {\n            wrapper.ne(SysRole::getId, roleId);\n        }\n        return sysRoleMapper.selectCount(wrapper) > 0;\n    }\n\n    /**\n     * 判断是否为超级管理员角色\n     * @param roleIds\n     * @return\n     */\n    private boolean isSuperAdminRoleByRoleIds(List<Long> roleIds) {\n        if (CollectionUtils.isEmpty(roleIds)) {\n            return false;\n        }\n        LambdaQueryWrapper<SysRole> wrapper = new LambdaQueryWrapper<>();\n        wrapper.in(SysRole::getId, roleIds);\n        wrapper.eq(SysRole::getRoleCode, SecurityConstant.SUPER_ADMIN_ROLE_CODE);\n        return sysRoleMapper.selectCount(wrapper) > 0;\n    }\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/role/vo/SysRoleVO.java",
    "content": "package com.geshanzsq.admin.system.role.vo;\n\nimport com.geshanzsq.common.core.web.vo.BaseVO;\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\nimport java.io.Serializable;\nimport java.util.List;\n\n/**\n * 系统角色\n *\n * @author geshanzsq\n * @date 2022/3/26\n */\n@ApiModel(\"系统角色\")\n@Data\npublic class SysRoleVO extends BaseVO implements Serializable {\n\n    private static final Long serialVersionUID = 1L;\n\n    @ApiModelProperty(\"角色 id\")\n    private Long id;\n\n    @ApiModelProperty(\"角色名称\")\n    private String roleName;\n\n    @ApiModelProperty(\"角色编码\")\n    private String roleCode;\n\n    @ApiModelProperty(\"排序\")\n    private Integer sort;\n\n    @ApiModelProperty(\"状态，1 正常，2 停用\")\n    private Integer status;\n\n    @ApiModelProperty(\"备注\")\n    private String remark;\n\n    @ApiModelProperty(\"用户 id\")\n    private Long userId;\n\n    @ApiModelProperty(\"关联菜单 ids\")\n    private List<Long> menuIds;\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/user/controller/SysUserController.java",
    "content": "package com.geshanzsq.admin.system.user.controller;\n\nimport com.geshanzsq.admin.system.user.dto.*;\nimport com.geshanzsq.admin.system.user.mapstruct.SysUserConverter;\nimport com.geshanzsq.admin.system.user.po.SysUser;\nimport com.geshanzsq.admin.system.user.service.SysUserService;\nimport com.geshanzsq.admin.system.user.vo.SysUserVO;\nimport com.geshanzsq.common.core.web.response.ResponseResult;\nimport com.geshanzsq.common.framework.mybatis.page.vo.PageVO;\nimport com.geshanzsq.common.framework.web.controller.BaseController;\nimport com.geshanzsq.common.log.annotation.Log;\nimport com.geshanzsq.common.log.enums.BusinessType;\nimport com.geshanzsq.framework.security.util.SecurityUtils;\nimport io.swagger.annotations.Api;\nimport io.swagger.annotations.ApiOperation;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.validation.Valid;\nimport java.util.Arrays;\n\n/**\n * 系统用户\n *\n * @author geshanzsq\n * @date 2022/3/20\n */\n@Api(tags = \"用户管理\")\n@RestController\n@RequestMapping(\"/system/user\")\npublic class SysUserController extends BaseController {\n\n    @Autowired\n    private SysUserService sysUserService;\n\n    @GetMapping(\"/page\")\n    @ApiOperation((\"分页列表\"))\n    @PreAuthorize(\"@auth.hasUrl()\")\n    public ResponseResult<PageVO<SysUserVO>> page(SysUserPageDTO pageDto) {\n        PageVO<SysUserVO> pageVO = sysUserService.pageList(pageDto);\n        return ResponseResult.success(pageVO);\n    }\n\n    @GetMapping(\"/getById/{id}\")\n    @ApiOperation(\"详情\")\n    @PreAuthorize(\"@auth.hasUrl()\")\n    public ResponseResult<SysUserVO> getById(@PathVariable Long id) {\n        SysUser sysUser = sysUserService.getById(id);\n        if (sysUser != null) {\n            sysUser.setPassword(null);\n        }\n        return ResponseResult.success(SysUserConverter.INSTANCE.convert(sysUser));\n    }\n\n    @PostMapping()\n    @ApiOperation(\"新增\")\n    @PreAuthorize(\"@auth.hasUrl()\")\n    @Log(moduleName = \"用户管理\", businessType = BusinessType.ADD)\n    public ResponseResult add(@Valid @RequestBody SysUserAddDTO addDto) {\n        sysUserService.add(addDto);\n        return ResponseResult.success();\n    }\n\n    @PutMapping()\n    @ApiOperation(\"修改\")\n    @PreAuthorize(\"@auth.hasUrl()\")\n    @Log(moduleName = \"用户管理\", businessType = BusinessType.UPDATE)\n    public ResponseResult update(@Valid @RequestBody SysUserUpdateDTO updateDto) {\n        SysUser sysUser = SysUserConverter.INSTANCE.convert(updateDto);\n        sysUserService.updateById(sysUser);\n        return ResponseResult.success();\n    }\n\n    @DeleteMapping(\"/delete/{ids}\")\n    @ApiOperation(\"删除\")\n    @PreAuthorize(\"@auth.hasUrl()\")\n    @Log(moduleName = \"用户管理\", businessType = BusinessType.DELETE)\n    public ResponseResult delete(@PathVariable Long[] ids) {\n        sysUserService.removeByIds(Arrays.asList(ids));\n        return ResponseResult.success();\n    }\n\n    @PutMapping(\"/resetPassword\")\n    @ApiOperation(\"重置密码\")\n    @PreAuthorize(\"@auth.hasUrl()\")\n    @Log(moduleName = \"用户管理-重置密码\", businessType = BusinessType.UPDATE, isSaveRequestData = false)\n    public ResponseResult resetPassword(@Valid @RequestBody SysUserResetPasswordDTO sysUserResetPasswordDto) {\n        sysUserService.resetPassword(sysUserResetPasswordDto);\n        return ResponseResult.success();\n    }\n\n    @PutMapping(\"/userUpdateInfo\")\n    @ApiOperation(\"用户修改信息\")\n    @Log(moduleName = \"用户管理-用户修改信息\", businessType = BusinessType.UPDATE)\n    public ResponseResult userUpdateInfo(@Valid @RequestBody SysUserUpdateInfoDTO updateDto) {\n        SysUser sysUser = SysUserConverter.INSTANCE.convert(updateDto);\n        sysUser.setId(SecurityUtils.getUserId());\n        sysUserService.updateById(sysUser);\n        return ResponseResult.success();\n    }\n\n    @PutMapping(\"/resetUserPassword\")\n    @ApiOperation(\"重置用户密码\")\n    public ResponseResult resetUserPassword(@Valid @RequestBody SysUserResetUserPasswordDTO passwordDTO) {\n        sysUserService.resetUserPassword(passwordDTO);\n        return ResponseResult.success();\n    }\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/user/controller/SysUserInfoController.java",
    "content": "package com.geshanzsq.admin.system.user.controller;\n\nimport com.geshanzsq.admin.system.menu.service.SysMenuService;\nimport com.geshanzsq.admin.system.menu.vo.RouterVO;\nimport com.geshanzsq.admin.system.user.service.SysUserInfoService;\nimport com.geshanzsq.admin.system.user.vo.SysUserVO;\nimport com.geshanzsq.common.core.web.response.ResponseResult;\nimport com.geshanzsq.common.framework.web.controller.BaseController;\nimport io.swagger.annotations.Api;\nimport io.swagger.annotations.ApiOperation;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.util.List;\n\n/**\n * 用户信息\n *\n * @author geshanzsq\n * @date 2022/3/26\n */\n@RestController\n@RequestMapping(\"/user\")\n@Api(tags = \"用户信息\")\npublic class SysUserInfoController extends BaseController {\n\n    @Autowired\n    private SysUserInfoService sysUserInfoService;\n    @Autowired\n    private SysMenuService sysMenuService;\n\n    @ApiOperation(\"获取用户信息\")\n    @GetMapping(\"/getUserInfo\")\n    public ResponseResult<SysUserVO> getUserInfo() {\n        SysUserVO sysUserVo = sysUserInfoService.getUserInfo();\n        return ResponseResult.success(sysUserVo);\n    }\n\n    @ApiOperation(\"获取菜单路由\")\n    @GetMapping(\"/getRouters\")\n    public ResponseResult<List<RouterVO>> getRouters() {\n        List<RouterVO> routers = sysMenuService.getRouters();\n        return ResponseResult.success(routers);\n    }\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/user/dto/SysUserAddDTO.java",
    "content": "package com.geshanzsq.admin.system.user.dto;\n\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotBlank;\nimport java.io.Serializable;\n\n/**\n * 用户修改\n *\n * @author geshanzsq\n * @date 2022/6/18\n */\n@Data\n@ApiModel(\"用户修改\")\npublic class SysUserAddDTO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(value = \"昵称\", required = true)\n    @NotBlank(message = \"昵称不能为空\")\n    private String nickName;\n\n    @ApiModelProperty(value = \"用户名\", required = true)\n    @NotBlank(message = \"用户名不能为空\")\n    private String username;\n\n    @ApiModelProperty(value = \"密码\", required = true)\n    @NotBlank(message = \"密码不能为空\")\n    private String password;\n\n    @ApiModelProperty(\"邮箱\")\n    private String email;\n\n    @ApiModelProperty(\"手机号码\")\n    private String mobilePhone;\n\n    @ApiModelProperty(\"性别，1 保密，2 男，3 女\")\n    private Integer sex;\n\n    @ApiModelProperty(\"状态，1 正常，2 停用\")\n    private Integer status;\n\n    @ApiModelProperty(\"备注\")\n    private String remark;\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/user/dto/SysUserPageDTO.java",
    "content": "package com.geshanzsq.admin.system.user.dto;\n\nimport com.geshanzsq.common.framework.mybatis.page.dto.PageDTO;\nimport com.geshanzsq.common.framework.mybatis.plugin.annotation.Query;\nimport com.geshanzsq.common.framework.mybatis.plugin.enums.QueryWay;\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\nimport org.springframework.format.annotation.DateTimeFormat;\n\nimport java.io.Serializable;\nimport java.util.Date;\nimport java.util.List;\n\n/**\n * 用户分页查询\n *\n * @author geshanzsq\n * @date 2022/3/27\n */\n@Data\n@ApiModel(\"用户分页查询\")\npublic class SysUserPageDTO extends PageDTO implements Serializable {\n\n    private static final Long serialVersionUID = 1L;\n\n    @ApiModelProperty(\"用户名\")\n    @Query(QueryWay.LIKE)\n    private String username;\n\n    @ApiModelProperty(\"昵称\")\n    @Query(QueryWay.LIKE)\n    private String nickName;\n\n    @ApiModelProperty(\"状态，1 正常，2 停用\")\n    private Integer status;\n\n    @ApiModelProperty(\"用户类型，1 后台用户，2 博客用户\")\n    private Integer userType;\n\n    @ApiModelProperty(\"开始创建时间\")\n    @DateTimeFormat(pattern = \"yyyy-MM-dd HH:mm:ss\")\n    @Query(value = QueryWay.GE, fieldName = \"gmtCreate\")\n    private Date beginGmtCreate;\n\n    @ApiModelProperty(\"结束创建时间\")\n    @DateTimeFormat(pattern = \"yyyy-MM-dd HH:mm:ss\")\n    @Query(value = QueryWay.LE, fieldName = \"gmtCreate\")\n    private Date endGmtCreate;\n\n    @ApiModelProperty(value = \"用户 id 集合\", hidden = true)\n    @Query(value = QueryWay.IN, fieldName = \"id\")\n    private List<Long> ids;\n\n    @ApiModelProperty(value = \"排除用户 id 集合\", hidden = true)\n    @Query(value = QueryWay.NOT_IN, fieldName = \"id\")\n    private List<Long> notIds;\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/user/dto/SysUserResetPasswordDTO.java",
    "content": "package com.geshanzsq.admin.system.user.dto;\n\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotBlank;\nimport javax.validation.constraints.NotNull;\nimport java.io.Serializable;\n\n/**\n * 用户重置密码\n *\n * @author geshanzsq\n * @date 2022/6/18\n */\n@Data\n@ApiModel(\"用户重置密码\")\npublic class SysUserResetPasswordDTO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(value = \"用户 id\", required = true)\n    @NotNull(message = \"重置的用户不能为空\")\n    private Long id;\n\n    @ApiModelProperty(value = \"密码\", required = true)\n    @NotBlank(message = \"密码不能为空\")\n    private String password;\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/user/dto/SysUserResetUserPasswordDTO.java",
    "content": "package com.geshanzsq.admin.system.user.dto;\n\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotBlank;\nimport javax.validation.constraints.Size;\nimport java.io.Serializable;\n\n/**\n * 用户重置密码\n *\n * @author geshanzsq\n * @date 2023/1/4\n */\n@Data\n@ApiModel(\"用户重置密码\")\npublic class SysUserResetUserPasswordDTO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(\"旧密码\")\n    private String oldPassword;\n\n    @ApiModelProperty(value = \"新密码\", required = true)\n    @NotBlank(message = \"新密码不能为空\")\n    @Size(min = 6, max = 20, message = \"长度在 6 到 20 个字符\")\n    private String newPassword;\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/user/dto/SysUserUpdateDTO.java",
    "content": "package com.geshanzsq.admin.system.user.dto;\n\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotBlank;\nimport javax.validation.constraints.NotNull;\nimport java.io.Serializable;\n\n/**\n * 用户修改\n *\n * @author geshanzsq\n * @date 2022/6/18\n */\n@Data\n@ApiModel(\"用户修改\")\npublic class SysUserUpdateDTO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(value = \"用户 id\", required = true)\n    @NotNull(message = \"用户 id 不能为空\")\n    private Long id;\n\n    @ApiModelProperty(value = \"用户名\", required = true)\n    @NotBlank(message = \"用户名不能为空\")\n    private String username;\n\n    @ApiModelProperty(value = \"昵称\", required = true)\n    @NotBlank(message = \"昵称 不能为空\")\n    private String nickName;\n\n    @ApiModelProperty(\"邮箱\")\n    private String email;\n\n    @ApiModelProperty(\"手机号码\")\n    private String mobilePhone;\n\n    @ApiModelProperty(\"性别，1 保密，2 男，3 女\")\n    private Integer sex;\n\n    @ApiModelProperty(\"状态，1 正常，2 停用\")\n    private Integer status;\n\n    @ApiModelProperty(\"备注\")\n    private String remark;\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/user/dto/SysUserUpdateInfoDTO.java",
    "content": "package com.geshanzsq.admin.system.user.dto;\n\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\nimport javax.validation.constraints.NotBlank;\nimport java.io.Serializable;\n\n/**\n * 用户修改信息\n *\n * @author geshanzsq\n * @date 2022/6/18\n */\n@Data\n@ApiModel(\"用户修改信息\")\npublic class SysUserUpdateInfoDTO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(value = \"用户名\", required = true)\n    @NotBlank(message = \"用户名不能为空\")\n    private String username;\n\n    @ApiModelProperty(value = \"昵称\", required = true)\n    @NotBlank(message = \"昵称 不能为空\")\n    private String nickName;\n\n    @ApiModelProperty(\"邮箱\")\n    private String email;\n\n    @ApiModelProperty(\"手机号码\")\n    private String mobilePhone;\n\n    @ApiModelProperty(\"性别，1 保密，2 男，3 女\")\n    private Integer sex;\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/user/enums/UserType.java",
    "content": "package com.geshanzsq.admin.system.user.enums;\n\n/**\n * 用户类型\n *\n * @author geshanzsq\n * @date 2022/10/7\n */\npublic enum UserType {\n\n    /**\n     * 后台用户\n     */\n    ADMIN(1),\n\n    /**\n     * 导航用户\n     */\n    NAV(2)\n    ;\n\n    public final Integer code;\n\n    UserType(Integer code) {\n        this.code = code;\n    }\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/user/mapper/SysUserMapper.java",
    "content": "package com.geshanzsq.admin.system.user.mapper;\n\nimport com.geshanzsq.admin.system.user.po.SysUser;\nimport com.geshanzsq.common.framework.web.mapper.BaseMapperPlus;\nimport org.apache.ibatis.annotations.Mapper;\n\n/**\n * 系统用户\n *\n * @author geshanzsq\n * @date 2022/3/20\n */\n@Mapper\npublic interface SysUserMapper extends BaseMapperPlus<SysUser> {\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/user/mapper/SysUserRoleMapper.java",
    "content": "package com.geshanzsq.admin.system.user.mapper;\n\nimport com.geshanzsq.admin.system.user.po.SysUserRole;\nimport com.geshanzsq.common.framework.web.mapper.BaseMapperPlus;\nimport org.apache.ibatis.annotations.Mapper;\nimport org.apache.ibatis.annotations.Param;\n\nimport java.util.List;\n\n/**\n * 系统用户角色\n *\n * @author geshanzsq\n * @date 2022/7/17\n */\n@Mapper\npublic interface SysUserRoleMapper extends BaseMapperPlus<SysUserRole> {\n\n    /**\n     * 获取关联未删除的用户数量\n     * @param roleIds 角色 id 集合\n     * @param delFlag 删除标识\n     * @return\n     */\n    Long selectAssociateNotDeleteUserCount(@Param(\"roleIds\") List<Long> roleIds,\n                                              @Param(\"delFlag\") Integer delFlag);\n\n    /**\n     * 通过角色编码获取用户 id\n     *\n     * @param roleCode 角色编码\n     */\n    List<Long> selectUserIdByRoleCode(@Param(\"roleCode\") String roleCode);\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/user/mapstruct/SysUserConverter.java",
    "content": "package com.geshanzsq.admin.system.user.mapstruct;\n\nimport com.geshanzsq.admin.system.user.dto.SysUserAddDTO;\nimport com.geshanzsq.admin.system.user.dto.SysUserResetPasswordDTO;\nimport com.geshanzsq.admin.system.user.dto.SysUserUpdateDTO;\nimport com.geshanzsq.admin.system.user.dto.SysUserUpdateInfoDTO;\nimport com.geshanzsq.admin.system.user.po.SysUser;\nimport com.geshanzsq.admin.system.user.vo.SysUserVO;\nimport com.geshanzsq.common.framework.mybatis.page.vo.PageVO;\nimport com.geshanzsq.framework.security.domain.LoginUserDetail;\nimport org.mapstruct.Mapper;\nimport org.mapstruct.Mapping;\nimport org.mapstruct.factory.Mappers;\n\nimport java.util.List;\n\n/**\n * 用户对象转换\n *\n * @author geshanzsq\n * @date 2022/3/27\n */\n@Mapper\npublic interface SysUserConverter {\n\n    SysUserConverter INSTANCE = Mappers.getMapper(SysUserConverter.class);\n\n    @Mapping(source = \"id\", target = \"userId\")\n    LoginUserDetail convertDetail(SysUser sysUser);\n\n    SysUserVO convert(SysUser sysUser);\n\n    List<SysUserVO> convertList(List<SysUser> list);\n\n    PageVO<SysUserVO> convert(PageVO<SysUser> pageVo);\n\n    SysUser convert(SysUserAddDTO addDTO);\n\n    SysUser convert(SysUserUpdateDTO updateDTO);\n\n    SysUser convert(SysUserUpdateInfoDTO updateInfoDTO);\n\n    SysUser convert(SysUserResetPasswordDTO sysUserResetPasswordDTO);\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/user/po/SysUser.java",
    "content": "package com.geshanzsq.admin.system.user.po;\n\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport lombok.Data;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\n/**\n * 系统用户\n *\n * @author geshanzsq\n * @date 2022/3/20\n */\n@Data\npublic class SysUser implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * 用户 id\n     */\n    @TableId\n    private Long id;\n\n    /**\n     * 用户名\n     */\n    private String username;\n\n    /**\n     * 昵称\n     */\n    private String nickName;\n\n    /**\n     * 密码\n     */\n    private String password;\n\n    /**\n     * 性别（1 保密，2 男，3 女）\n     */\n    private Integer sex;\n\n    /**\n     * 用户类型（1 后台用户，2 博客用户）\n     */\n    private Integer userType;\n\n    /**\n     * 邮箱\n     */\n    private String email;\n\n    /**\n     * 手机号码\n     */\n    private String mobilePhone;\n\n    /**\n     * 头像\n     */\n    private String avatar;\n\n    /**\n     * 状态（1 正常，2 停用）\n     */\n    private Integer status;\n\n    /**\n     * 备注\n     */\n    private String remark;\n\n    /**\n     * 删除标识（1 未删除，2 已删除）\n     */\n    private Integer delFlag;\n\n    /**\n     * 创建时间\n     */\n    private Date gmtCreate;\n\n    /**\n     * 创建人用户 id\n     */\n    @TableField(\"fk_create_user_id\")\n    private Long createUserId;\n\n    /**\n     * 修改时间\n     */\n    private Date gmtModify;\n\n    /**\n     * 修改人用户 id\n     */\n    @TableField(\"fk_modify_user_id\")\n    private Long modifyUserId;\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/user/po/SysUserRole.java",
    "content": "package com.geshanzsq.admin.system.user.po;\n\nimport com.baomidou.mybatisplus.annotation.TableField;\nimport lombok.Data;\n\nimport java.io.Serializable;\n\n/**\n * 系统用户角色\n *\n * @author geshanzsq\n * @date 2022/7/17\n */\n@Data\npublic class SysUserRole implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * 用户角色 id\n     */\n    private Long id;\n\n    /**\n     * 用户 id\n     */\n    @TableField(\"fk_user_id\")\n    private Long userId;\n\n    /**\n     * 角色 id\n     */\n    @TableField(\"fk_role_id\")\n    private Long roleId;\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/user/service/SysUserInfoService.java",
    "content": "package com.geshanzsq.admin.system.user.service;\n\nimport com.geshanzsq.admin.system.user.vo.SysUserVO;\n\n/**\n * 用户信息\n *\n * @author geshanzsq\n * @date 2022/3/27\n */\npublic interface SysUserInfoService {\n\n    /**\n     * 获取用户信息\n     */\n    SysUserVO getUserInfo();\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/user/service/SysUserRoleService.java",
    "content": "package com.geshanzsq.admin.system.user.service;\n\nimport com.geshanzsq.admin.system.user.po.SysUserRole;\nimport com.geshanzsq.common.framework.web.service.BaseService;\n\nimport java.util.List;\n\n/**\n * 系统用户角色\n *\n * @author geshanzsq\n * @date 2022/7/17\n */\npublic interface SysUserRoleService extends BaseService<SysUserRole> {\n\n    /**\n     * 通过角色 id 获取用户 id 集合\n     * @param roleId 角色 id\n     */\n    List<Long> getUserIdsByRoleId(Long roleId);\n\n    /**\n     * 批量插入角色和用户关系\n     * @param roleId 角色 id\n     * @param userIds 用户 id 集合\n     */\n    void saveBatch(Long roleId, List<Long> userIds);\n\n    /**\n     * 删除\n     * @param roleId 角色 id\n     * @param userIds 用户 id 集合\n     */\n    void remove(Long roleId, List<Long> userIds);\n\n    /**\n     * 是否关联用户\n     * @param roleIds 角色 id 集合\n     */\n    boolean isAssociateUser(List<Long> roleIds);\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/user/service/SysUserService.java",
    "content": "package com.geshanzsq.admin.system.user.service;\n\nimport com.geshanzsq.admin.system.user.dto.SysUserAddDTO;\nimport com.geshanzsq.admin.system.user.dto.SysUserPageDTO;\nimport com.geshanzsq.admin.system.user.dto.SysUserResetPasswordDTO;\nimport com.geshanzsq.admin.system.user.dto.SysUserResetUserPasswordDTO;\nimport com.geshanzsq.admin.system.user.po.SysUser;\nimport com.geshanzsq.admin.system.user.vo.SysUserVO;\nimport com.geshanzsq.common.framework.mybatis.page.vo.PageVO;\nimport com.geshanzsq.common.framework.web.service.BaseService;\nimport com.geshanzsq.framework.security.domain.LoginUserDetail;\n\nimport java.util.List;\n\n/**\n * 系统用户\n *\n * @author geshanzsq\n * @date 2022/3/20\n */\npublic interface SysUserService extends BaseService<SysUser> {\n\n    /**\n     * 获取登录用户\n     * @param username 用户名\n     */\n    LoginUserDetail getLoginUserByUsername(String username);\n\n\n    /**\n     * 设置登录成功后的用户权限\n     */\n    void setLoginUserPermission(LoginUserDetail loginUser);\n\n    /**\n     * 通过用户 ids 获取用户名和昵称\n     * @param userIds 用户 ids\n     */\n    List<SysUserVO> getUsernameAndNickNameByUserIds(List<Long> userIds);\n\n    /**\n     * 分页列表\n     */\n    PageVO<SysUserVO> pageList(SysUserPageDTO pageDTO);\n\n    /**\n     * 新增用户\n     */\n    void add(SysUserAddDTO addDTO);\n\n    /**\n     * 重置密码\n     */\n    void resetPassword(SysUserResetPasswordDTO sysUserResetPasswordDto);\n\n    /**\n     * 过滤不存在的用户 ids\n     * @param ids 用户 id 集合\n     */\n    List<Long> filterNotExistUserIds(List<Long> ids);\n\n    /**\n     * 更新用户头像\n     */\n    void updateAvatarById(Long id, String avatar);\n\n    /**\n     * 重置用户密码\n     */\n    void resetUserPassword(SysUserResetUserPasswordDTO passwordDTO);\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/user/service/impl/SysUserInfoServiceImpl.java",
    "content": "package com.geshanzsq.admin.system.user.service.impl;\n\nimport com.geshanzsq.admin.system.user.mapstruct.SysUserConverter;\nimport com.geshanzsq.admin.system.user.po.SysUser;\nimport com.geshanzsq.admin.system.user.service.SysUserInfoService;\nimport com.geshanzsq.admin.system.user.service.SysUserService;\nimport com.geshanzsq.admin.system.user.vo.SysUserVO;\nimport com.geshanzsq.framework.security.domain.LoginUserDetail;\nimport com.geshanzsq.framework.security.service.TokenService;\nimport com.geshanzsq.framework.security.util.SecurityUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\n/**\n * 用户信息\n *\n * @author geshanzsq\n * @date 2022/3/27\n */\n@Service\npublic class SysUserInfoServiceImpl implements SysUserInfoService {\n\n    @Autowired\n    private SysUserService sysUserService;\n    @Autowired\n    private TokenService tokenService;\n\n    /**\n     * 获取用户信息\n     */\n    @Override\n    public SysUserVO getUserInfo() {\n        LoginUserDetail loginUser = SecurityUtils.getLoginUser();\n\n        // 重新获取用户信息，当有权限发生改变时，不用重新登录就能生效\n        SysUser sysUser = sysUserService.getById(loginUser.getUserId());\n        loginUser = SysUserConverter.INSTANCE.convertDetail(sysUser);\n        // 设置用户权限\n        sysUserService.setLoginUserPermission(loginUser);\n\n        // 重新设置用户权限到 Redis 缓存\n        String token = tokenService.getToken();\n        // 获取令牌有效期\n        Long expireTime = tokenService.getTokenExpireTime(token);\n        tokenService.setLoginUserCache(token, loginUser, expireTime);\n\n        // 设置返回的数据\n        SysUserVO sysUserVo = SysUserConverter.INSTANCE.convert(sysUser);\n        sysUserVo.setPassword(\"\");\n        sysUserVo.setPermissionCodes(loginUser.getPermissionCodes());\n        sysUserVo.setRoleCodes(loginUser.getRoleCodes());\n        return sysUserVo;\n    }\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/user/service/impl/SysUserRoleServiceImpl.java",
    "content": "package com.geshanzsq.admin.system.user.service.impl;\n\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.geshanzsq.admin.system.user.mapper.SysUserRoleMapper;\nimport com.geshanzsq.admin.system.user.po.SysUserRole;\nimport com.geshanzsq.admin.system.user.service.SysUserRoleService;\nimport com.geshanzsq.common.core.enums.DelFlag;\nimport com.geshanzsq.common.framework.web.service.impl.BaseServiceImpl;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\nimport org.springframework.util.CollectionUtils;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\n/**\n * 系统用户角色\n *\n * @author geshanzsq\n * @date 2022/7/17\n */\n@Service\npublic class SysUserRoleServiceImpl extends BaseServiceImpl<SysUserRoleMapper, SysUserRole> implements SysUserRoleService {\n\n    @Autowired\n    private SysUserRoleMapper sysUserRoleMapper;\n\n    /**\n     * 通过角色 id 获取用户 id 集合\n     * @param roleId 角色 id\n     */\n    @Override\n    public List<Long> getUserIdsByRoleId(Long roleId) {\n        if (roleId == null) {\n            return new ArrayList<>();\n        }\n        // 获取已分配的用户\n        LambdaQueryWrapper<SysUserRole> wrapper = new LambdaQueryWrapper();\n        wrapper.eq(SysUserRole::getRoleId, roleId);\n        wrapper.select(SysUserRole::getId, SysUserRole::getUserId);\n        List<SysUserRole> list = sysUserRoleMapper.selectList(wrapper);\n        List<Long> userIds = list.stream().map(SysUserRole::getUserId).collect(Collectors.toList());\n        return userIds;\n    }\n\n    /**\n     * 批量插入角色和用户关系\n     * @param roleId 角色 id\n     * @param userIds 用户 id 集合\n     */\n    @Override\n    public void saveBatch(Long roleId, List<Long> userIds) {\n        if (CollectionUtils.isEmpty(userIds)) {\n            return;\n        }\n        List<SysUserRole> list = new ArrayList<>();\n        for (Long userId : userIds) {\n            SysUserRole sysUserRole = new SysUserRole();\n            sysUserRole.setRoleId(roleId);\n            sysUserRole.setUserId(userId);\n            list.add(sysUserRole);\n        }\n        super.saveBatch(list);\n    }\n\n    /**\n     * 删除\n     * @param roleId 角色 id\n     * @param userIds 用户 id 集合\n     */\n    @Override\n    public void remove(Long roleId, List<Long> userIds) {\n        if (CollectionUtils.isEmpty(userIds)) {\n            return;\n        }\n        LambdaQueryWrapper<SysUserRole> wrapper = new LambdaQueryWrapper();\n        wrapper.eq(SysUserRole::getRoleId, roleId);\n        wrapper.in(SysUserRole::getUserId, userIds);\n        sysUserRoleMapper.delete(wrapper);\n    }\n\n    /**\n     * 是否关联用户\n     * @param roleIds 角色 id 集合\n     */\n    @Override\n    public boolean isAssociateUser(List<Long> roleIds) {\n        if (CollectionUtils.isEmpty(roleIds)) {\n            return false;\n        }\n        return sysUserRoleMapper.selectAssociateNotDeleteUserCount(roleIds, DelFlag.NO_DELETE.code) > 0;\n    }\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/user/service/impl/SysUserServiceImpl.java",
    "content": "package com.geshanzsq.admin.system.user.service.impl;\n\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.geshanzsq.admin.system.api.mapstruct.SysApiConverter;\nimport com.geshanzsq.admin.system.api.service.SysApiService;\nimport com.geshanzsq.admin.system.api.vo.SysApiVO;\nimport com.geshanzsq.admin.system.menu.service.SysMenuService;\nimport com.geshanzsq.admin.system.menu.vo.SysMenuVO;\nimport com.geshanzsq.admin.system.role.service.SysRoleService;\nimport com.geshanzsq.admin.system.role.vo.SysRoleVO;\nimport com.geshanzsq.admin.system.user.dto.SysUserAddDTO;\nimport com.geshanzsq.admin.system.user.dto.SysUserPageDTO;\nimport com.geshanzsq.admin.system.user.dto.SysUserResetPasswordDTO;\nimport com.geshanzsq.admin.system.user.dto.SysUserResetUserPasswordDTO;\nimport com.geshanzsq.admin.system.user.mapper.SysUserMapper;\nimport com.geshanzsq.admin.system.user.mapstruct.SysUserConverter;\nimport com.geshanzsq.admin.system.user.po.SysUser;\nimport com.geshanzsq.admin.system.user.service.SysUserService;\nimport com.geshanzsq.admin.system.user.vo.SysUserVO;\nimport com.geshanzsq.common.core.enums.DelFlag;\nimport com.geshanzsq.common.core.exception.ParamException;\nimport com.geshanzsq.common.core.util.message.MessageUtils;\nimport com.geshanzsq.common.core.util.string.StrUtils;\nimport com.geshanzsq.common.framework.mybatis.page.vo.PageVO;\nimport com.geshanzsq.common.framework.web.service.impl.BaseServiceImpl;\nimport com.geshanzsq.framework.security.constant.SecurityConstant;\nimport com.geshanzsq.framework.security.domain.LoginUserDetail;\nimport com.geshanzsq.framework.security.service.TokenService;\nimport com.geshanzsq.framework.security.util.SecurityUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\nimport org.springframework.util.CollectionUtils;\n\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\n/**\n * 系统用户\n *\n * @author geshanzsq\n * @date 2022/3/20\n */\n@Service\npublic class SysUserServiceImpl extends BaseServiceImpl<SysUserMapper, SysUser> implements SysUserService {\n\n    private static Logger log = LoggerFactory.getLogger(SysUserServiceImpl.class);\n\n    @Autowired\n    private SysUserMapper sysUserMapper;\n\n    @Autowired\n    private SysRoleService sysRoleService;\n    @Autowired\n    private SysMenuService sysMenuService;\n    @Autowired\n    private SysApiService sysApiService;\n    @Autowired\n    private TokenService tokenService;\n\n    /**\n     * 获取登录用户\n     * @param username 用户名\n     */\n    @Override\n    public LoginUserDetail getLoginUserByUsername(String username) {\n        LambdaQueryWrapper<SysUser> wrapper = new LambdaQueryWrapper<>();\n        wrapper.eq(SysUser::getUsername, username);\n        SysUser sysUser = sysUserMapper.selectOne(wrapper);\n        if (sysUser == null) {\n            log.info(\"用户：{}，不存在\", username);\n            return null;\n        }\n        LoginUserDetail loginUserDetail = SysUserConverter.INSTANCE.convertDetail(sysUser);\n        return loginUserDetail;\n    }\n\n    /**\n     * 设置登录成功后的用户权限\n     */\n    @Override\n    public void setLoginUserPermission(LoginUserDetail loginUser) {\n        // 1 获取角色\n        List<SysRoleVO> roles = sysRoleService.getRoleByUserId(loginUser.getUserId());\n        if (CollectionUtils.isEmpty(roles)) {\n            return;\n        }\n\n        // 角色 id\n        Set<Long> roleIds = new HashSet<>();\n        // 角色编码\n        Set<String> roleCodes = new HashSet<>();\n        // 获取角色 id 和角色编码\n        for (SysRoleVO role: roles) {\n            roleIds.add(role.getId());\n            roleCodes.add(role.getRoleCode());\n            // 是否为超级管理员\n            if (SecurityConstant.SUPER_ADMIN_ROLE_CODE.equals(role.getRoleCode())) {\n                loginUser.setHasSuperAdmin(true);\n                Set<String> permissionCodes = new HashSet<>();\n                permissionCodes.add(SecurityConstant.ALL_PERMISSION_CODE);\n                loginUser.setPermissionCodes(permissionCodes);\n            }\n        }\n        loginUser.setRoleCodes(roleCodes);\n\n        // 2 如果为超级管理员，不用获取权限标识和接口地址\n        if (loginUser.isHasSuperAdmin()) {\n            return;\n        }\n\n        // 3 获取菜单权限和接口地址\n        List<SysMenuVO> menus = sysMenuService.getMenuByRoleIds(roleIds);\n        if (CollectionUtils.isEmpty(menus)) {\n            return;\n        }\n\n        // 4 设置权限标识\n        Set<String> permissionCodes = menus.stream()\n                .filter(m -> StrUtils.isNotBlank(m.getPermissionCode()))\n                .map(m -> m.getPermissionCode())\n                .collect(Collectors.toSet());\n        loginUser.setPermissionCodes(permissionCodes);\n\n        // 5 设置接口地址\n        List<Long> menuIds = menus.stream().map(m -> m.getId()).collect(Collectors.toList());\n        List<SysApiVO> apis = sysApiService.getApiByMenuIds(menuIds);\n        loginUser.setApiPermissions(SysApiConverter.INSTANCE.convertList(apis));\n\n    }\n\n    /**\n     * 通过用户 ids 获取用户名和昵称\n     * @param userIds 用户 ids\n     */\n    @Override\n    public List<SysUserVO> getUsernameAndNickNameByUserIds(List<Long> userIds) {\n        if (CollectionUtils.isEmpty(userIds)) {\n            return new ArrayList<>();\n        }\n        LambdaQueryWrapper<SysUser> wrapper = new LambdaQueryWrapper<>();\n        wrapper.in(SysUser::getId, new HashSet<>(userIds));\n        wrapper.select(SysUser::getId, SysUser::getUsername, SysUser::getNickName);\n        List<SysUser> list = sysUserMapper.selectList(wrapper);\n        return SysUserConverter.INSTANCE.convertList(list);\n    }\n\n    /**\n     * 分页列表\n     */\n    @Override\n    public PageVO<SysUserVO> pageList(SysUserPageDTO pageDTO) {\n        pageDTO.setOrderColumn(\"gmtCreate,id\");\n        PageVO<SysUserVO> page = SysUserConverter.INSTANCE.convert(sysUserMapper.selectPage(pageDTO));\n        List<SysUserVO> userList = page.getList();\n        if (CollectionUtils.isEmpty(userList)) {\n            return page;\n        }\n\n        // 获取用户角色\n        List<Long> userIds = userList.stream().map(u -> u.getId()).collect(Collectors.toList());\n        List<SysRoleVO> roles = sysRoleService.getRoleByUserIds(userIds);\n        if (CollectionUtils.isEmpty(roles)) {\n            return page;\n        }\n\n        // 设置用户角色名称\n        for (SysUserVO user : userList) {\n            for (SysRoleVO role : roles) {\n                if (user.getId().equals(role.getUserId())) {\n                    if (user.getRoleNames() == null) {\n                        user.setRoleNames(new HashSet<>());\n                    }\n                    user.getRoleNames().add(role.getRoleName());\n                }\n            }\n        }\n\n        return page;\n    }\n\n    /**\n     * 新增用户\n     */\n    @Override\n    public void add(SysUserAddDTO addDTO) {\n        // 判断用户名是否存在\n        LambdaQueryWrapper<SysUser> wrapper = new LambdaQueryWrapper<>();\n        wrapper.eq(SysUser::getUsername, addDTO.getUsername());\n        Long count = sysUserMapper.selectCount(wrapper);\n        if (count > 0) {\n            throw new ParamException(MessageUtils.message(\"user.username.exist\"));\n        }\n\n        SysUser sysUser = SysUserConverter.INSTANCE.convert(addDTO);\n        sysUser.setPassword(SecurityUtils.encryptPassword(addDTO.getPassword()));\n        sysUserMapper.insert(sysUser);\n\n    }\n\n    /**\n     * 重置密码\n     */\n    @Override\n    public void resetPassword(SysUserResetPasswordDTO sysUserResetPasswordDto) {\n        SysUser sysUser = SysUserConverter.INSTANCE.convert(sysUserResetPasswordDto);\n        sysUser.setPassword(SecurityUtils.encryptPassword(sysUserResetPasswordDto.getPassword()));\n        sysUserMapper.updateById(sysUser);\n    }\n\n    /**\n     * 过滤不存在的用户 ids\n     * @param ids 用户 id 集合\n     */\n    @Override\n    public List<Long> filterNotExistUserIds(List<Long> ids) {\n        if (CollectionUtils.isEmpty(ids)) {\n            return new ArrayList<>();\n        }\n        LambdaQueryWrapper<SysUser> wrapper = new LambdaQueryWrapper<>();\n        wrapper.in(SysUser::getId, ids);\n        wrapper.eq(SysUser::getStatus, DelFlag.NO_DELETE.code);\n        wrapper.select(SysUser::getId);\n        List<SysUser> users = sysUserMapper.selectList(wrapper);\n        return users.stream().map(SysUser::getId).collect(Collectors.toList());\n    }\n\n\n    /**\n     * 更新用户头像\n     */\n    @Override\n    public void updateAvatarById(Long id, String avatar) {\n        SysUser sysUser = new SysUser();\n        sysUser.setId(id);\n        sysUser.setAvatar(avatar);\n        sysUserMapper.updateById(sysUser);\n    }\n\n    /**\n     * 重置用户密码\n     */\n    @Override\n    public void resetUserPassword(SysUserResetUserPasswordDTO passwordDTO) {\n        LoginUserDetail loginUser = SecurityUtils.getLoginUser();\n        // 是否需要输入旧密码\n        if (StrUtils.isNotBlank(loginUser.getPassword()) && StrUtils.isBlank(passwordDTO.getOldPassword())) {\n            throw new ParamException(MessageUtils.message(\"reset.user.password.old.not.null\"));\n        }\n\n        if (StrUtils.isNotBlank(passwordDTO.getOldPassword())) {\n            // 校验旧密码是否一致\n            if (!SecurityUtils.matchesPassword(passwordDTO.getOldPassword(), loginUser.getPassword())) {\n                throw new ParamException(MessageUtils.message(\"reset.user.password.old.not.matches\"));\n            }\n            // 新旧密码是否一致\n            if (SecurityUtils.matchesPassword(passwordDTO.getNewPassword(), loginUser.getPassword())) {\n                throw new ParamException(MessageUtils.message(\"reset.user.password.old.not.equal.new\"));\n            }\n        }\n\n        // 修改密码\n        SysUser sysUser = new SysUser();\n        sysUser.setPassword(SecurityUtils.encryptPassword(passwordDTO.getNewPassword()));\n        sysUser.setId(loginUser.getUserId());\n        sysUserMapper.updateById(sysUser);\n\n        // 设置需要刷新权限\n        tokenService.setNeedRefreshPermission(loginUser.getUserId());\n    }\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/java/com/geshanzsq/admin/system/user/vo/SysUserVO.java",
    "content": "package com.geshanzsq.admin.system.user.vo;\n\nimport com.geshanzsq.common.core.web.vo.BaseVO;\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\nimport java.io.Serializable;\nimport java.util.Set;\n\n/**\n * 系统用户\n *\n * @author geshanzsq\n * @date 2022/3/20\n */\n@Data\n@ApiModel(\"系统用户\")\npublic class SysUserVO extends BaseVO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(\"用户 id\")\n    private Long id;\n\n    @ApiModelProperty(\"用户名\")\n    private String username;\n\n    @ApiModelProperty(\"密码\")\n    private String password;\n\n    @ApiModelProperty(\"昵称\")\n    private String nickName;\n\n    @ApiModelProperty(\"性别，1 保密，2 男，3 女\")\n    private Integer sex;\n\n    @ApiModelProperty(\"用户类型，1 后台用户，2 博客用户\")\n    private Integer userType;\n\n    @ApiModelProperty(\"邮箱\")\n    private String email;\n\n    @ApiModelProperty(\"手机号码\")\n    private String mobilePhone;\n\n    @ApiModelProperty(\"头像\")\n    private String avatar;\n\n    @ApiModelProperty(\"状态，1 正常，2 停用\")\n    private Integer status;\n\n    @ApiModelProperty(\"备注\")\n    private String remark;\n\n    @ApiModelProperty(\"角色编码\")\n    private Set<String> roleCodes;\n\n    @ApiModelProperty(\"权限标识\")\n    private Set<String> permissionCodes;\n\n    @ApiModelProperty(\"角色名称\")\n    private Set<String> roleNames;\n}\n"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/resources/mapper/api/SysApiCategoryMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\" >\n<mapper namespace=\"com.geshanzsq.admin.system.api.mapper.SysApiCategoryMapper\">\n\n    <resultMap id=\"BaseResultMap\" type=\"com.geshanzsq.admin.system.api.vo.SysApiCategoryVO\" >\n        <result column=\"id\" property=\"id\" />\n        <result column=\"category_name\" property=\"categoryName\" />\n        <result column=\"sort\" property=\"sort\" />\n        <result column=\"status\" property=\"status\" />\n        <result column=\"remark\" property=\"remark\" />\n        <result column=\"gmt_time\" property=\"gmtCreate\" />\n        <result column=\"fk_create_user_id\" property=\"createUserId\" />\n        <result column=\"gmt_modify\" property=\"gmtModify\" />\n        <result column=\"fk_modify_user_id\" property=\"modifyUserId\" />\n    </resultMap>\n\n    <!-- 获取最大排序 -->\n    <select id=\"selectMaxSort\" resultType=\"java.lang.Integer\">\n        select max(sort) from sys_api_category\n    </select>\n\n</mapper>"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/resources/mapper/api/SysApiMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\" >\n<mapper namespace=\"com.geshanzsq.admin.system.api.mapper.SysApiMapper\">\n\n    <resultMap id=\"BaseResultMap\" type=\"com.geshanzsq.admin.system.api.vo.SysApiVO\" >\n        <result column=\"id\" property=\"id\" />\n        <result column=\"api_name\" property=\"apiName\" />\n        <result column=\"api_url\" property=\"apiUrl\" />\n        <result column=\"api_method\" property=\"apiMethod\" />\n        <result column=\"fk_api_category_id\" property=\"apiCategoryId\" />\n        <result column=\"sort\" property=\"sort\" />\n        <result column=\"status\" property=\"status\" />\n        <result column=\"remark\" property=\"remark\" />\n        <result column=\"gmt_time\" property=\"gmtCreate\" />\n        <result column=\"fk_create_user_id\" property=\"createUserId\" />\n        <result column=\"gmt_modify\" property=\"gmtModify\" />\n        <result column=\"fk_modify_user_id\" property=\"modifyUserId\" />\n    </resultMap>\n\n    <resultMap id=\"SysMenuAuthApiMap\" type=\"com.geshanzsq.admin.system.menu.vo.SysMenuAuthApiVO\" >\n        <result column=\"api_id\" property=\"apiId\" />\n        <result column=\"api_name\" property=\"apiName\" />\n        <result column=\"fk_api_category_id\" property=\"apiCategoryId\" />\n        <result column=\"api_category_name\" property=\"apiCategoryName\" />\n    </resultMap>\n\n    <!-- 通过菜单 id 列表获取接口 -->\n    <select id=\"getApiByMenuIds\" resultMap=\"BaseResultMap\">\n        select distinct api.api_url, api.api_method from sys_api_menu ar\n        inner join sys_api api on ar.fk_api_id = api.id\n        <where>\n            <if test=\"status != null and status != ''\">\n                and api.status = #{status}\n            </if>\n            <if test=\"menuIds != null and menuIds.size > 0\">\n                and ar.fk_menu_id in\n                <foreach collection=\"menuIds\" item=\"menuId\" open=\"(\" separator=\",\" close=\")\">\n                    #{menuId}\n                </foreach>\n            </if>\n        </where>\n    </select>\n\n    <!-- 获取最大排序 -->\n    <select id=\"selectMaxSortByCategoryId\" parameterType=\"Long\" resultType=\"java.lang.Integer\">\n        select max(sort) from sys_api where fk_api_category_id = #{apiCategoryId}\n    </select>\n\n    <!-- 获取分配的 API 接口 -->\n    <select id=\"selectAuthApiByMenuId\" parameterType=\"Long\" resultMap=\"SysMenuAuthApiMap\">\n        select a.id as api_id, a.api_name, a.fk_api_category_id as api_category_id, ac.category_name as api_category_name\n        from sys_api_menu am\n        inner join sys_api a on am.fk_api_id = a.id\n        left join sys_api_category ac on a.fk_api_category_id = ac.id\n        where am.fk_menu_id = #{menuId}\n    </select>\n\n</mapper>"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/resources/mapper/dictionary/SysDictionaryDataMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\" >\n<mapper namespace=\"com.geshanzsq.admin.system.dictionary.mapper.SysDictionaryDataMapper\">\n\n    <resultMap id=\"BaseResultMap\" type=\"com.geshanzsq.admin.system.dictionary.vo.SysDictionaryDataVO\" >\n        <result column=\"id\" property=\"id\" />\n        <result column=\"dictionary_label\" property=\"dictionaryLabel\" />\n        <result column=\"dictionary_value\" property=\"dictionaryValue\" />\n        <result column=\"fk_dictionary_id\" property=\"dictionaryId\" />\n        <result column=\"class_type\" property=\"classType\" />\n        <result column=\"sort\" property=\"sort\" />\n        <result column=\"status\" property=\"status\" />\n        <result column=\"remark\" property=\"remark\" />\n        <result column=\"gmt_time\" property=\"gmtCreate\" />\n        <result column=\"fk_create_user_id\" property=\"createUserId\" />\n        <result column=\"gmt_modify\" property=\"gmtModify\" />\n        <result column=\"fk_modify_user_id\" property=\"modifyUserId\" />\n    </resultMap>\n\n\n    <!-- 获取最大排序 -->\n    <select id=\"selectMaxSortByDictionaryId\" parameterType=\"Long\" resultType=\"java.lang.Integer\">\n        select max(sort) from sys_dictionary_data where fk_dictionary_id = #{dictionaryId}\n    </select>\n</mapper>"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/resources/mapper/dictionary/SysDictionaryMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\" >\n<mapper namespace=\"com.geshanzsq.admin.system.dictionary.mapper.SysDictionaryMapper\">\n\n    <resultMap id=\"BaseResultMap\" type=\"com.geshanzsq.admin.system.dictionary.vo.SysDictionaryVO\" >\n        <result column=\"id\" property=\"id\" />\n        <result column=\"dictionary_name\" property=\"dictionaryName\" />\n        <result column=\"dictionary_code\" property=\"dictionaryCode\" />\n        <result column=\"sort\" property=\"sort\" />\n        <result column=\"status\" property=\"status\" />\n        <result column=\"remark\" property=\"remark\" />\n        <result column=\"gmt_time\" property=\"gmtCreate\" />\n        <result column=\"fk_create_user_id\" property=\"createUserId\" />\n        <result column=\"gmt_modify\" property=\"gmtModify\" />\n        <result column=\"fk_modify_user_id\" property=\"modifyUserId\" />\n    </resultMap>\n\n    <resultMap id=\"DictionaryInfoMap\" type=\"com.geshanzsq.admin.system.dictionary.vo.DictionaryInfoVO\" >\n        <result column=\"id\" property=\"id\" />\n        <result column=\"dictionary_name\" property=\"dictionaryName\" />\n        <result column=\"dictionary_code\" property=\"dictionaryCode\" />\n        <collection property=\"dictionaryDataList\" javaType=\"java.util.List\" resultMap=\"DictionaryDataInfoMap\"/>\n    </resultMap>\n\n    <resultMap id=\"DictionaryDataInfoMap\" type=\"com.geshanzsq.admin.system.dictionary.vo.DictionaryDataInfoVO\">\n        <result column=\"dictionary_data_id\" property=\"id\" />\n        <result column=\"dictionary_label\" property=\"dictionaryLabel\" />\n        <result column=\"dictionary_value\" property=\"dictionaryValue\" />\n        <result column=\"class_type\" property=\"classType\" />\n    </resultMap>\n\n    <!-- 获取最大排序 -->\n    <select id=\"selectMaxSort\" resultType=\"java.lang.Integer\">\n        select max(sort) from sys_dictionary\n    </select>\n\n    <!-- 获取所有字典详细信息 -->\n    <select id=\"getAllDictionaryInfo\" parameterType=\"Integer\" resultMap=\"DictionaryInfoMap\">\n        select\n            d.id,\n            d.dictionary_name,\n            d.dictionary_code,\n            dd.id as dictionary_data_id,\n            dd.dictionary_label,\n            dd.dictionary_value,\n            dd.class_type\n        from\n            sys_dictionary d\n        inner join sys_dictionary_data dd on d.id = dd.fk_dictionary_id\n        <where>\n            <if test=\"status != null and status != ''\">\n                and d.status = #{status} and dd.status = #{status}\n            </if>\n        </where>\n        order by\n            d.sort asc,\n            d.id asc,\n            dd.sort asc,\n            dd.id asc\n    </select>\n\n</mapper>"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/resources/mapper/log/LogOperationMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n        \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"com.geshanzsq.admin.system.log.operation.mapper.LogOperationMapper\">\n\n    <resultMap id=\"BaseResultMap\" type=\"com.geshanzsq.admin.system.log.operation.vo.LogOperationVo\" >\n        <result column=\"id\" property=\"id\" />\n        <result column=\"module_name\" property=\"moduleName\" />\n        <result column=\"business_type\" property=\"businessType\" />\n        <result column=\"operate_type\" property=\"operateType\" />\n        <result column=\"user_id\" property=\"userId\" />\n        <result column=\"request_method\" property=\"requestMethod\" />\n        <result column=\"class_method\" property=\"classMethod\" />\n        <result column=\"request_url\" property=\"requestUrl\" />\n        <result column=\"ip_address\" property=\"ipAddress\" />\n        <result column=\"operate_location\" property=\"operateLocation\" />\n        <result column=\"request_param\" property=\"requestParam\" />\n        <result column=\"return_result\" property=\"returnResult\" />\n        <result column=\"status\" property=\"status\" />\n        <result column=\"gmt_operate\" property=\"gmtOperate\" />\n        <result column=\"error_message\" property=\"errorMessage\" />\n        <result column=\"username\" property=\"username\" />\n        <result column=\"nick_name\" property=\"nickName\" />\n    </resultMap>\n\n    <!-- 分页列表 -->\n    <select id=\"pageList\" parameterType=\"com.geshanzsq.admin.system.log.operation.dto.LogOperationPageDTO\" resultMap=\"BaseResultMap\">\n        select\n            lo.id,\n            lo.module_name,\n            lo.business_type,\n            lo.operate_type,\n            lo.request_method,\n            lo.class_method,\n            lo.request_url,\n            lo.ip_address,\n            lo.operate_location,\n            lo.status,\n            lo.gmt_operate,\n            su.username,\n            su.nick_name\n        from\n            log_operation lo\n            left join sys_user su on lo.fk_user_id = su.id\n        <where>\n            <if test=\"moduleName != null and moduleName != ''\">\n                and lo.module_name like concat('%', #{moduleName}, '%')\n            </if>\n            <if test=\"ipAddress != null and ipAddress != ''\">\n                and lo.ip_address like concat('%', #{ipAddress}, '%')\n            </if>\n            <if test=\"operateUser != null and operateUser != ''\">\n                and (\n                    su.username like concat('%', #{operateUser}, '%')\n                    or su.nick_name like concat('%', #{operateUser}, '%')\n                )\n            </if>\n            <if test=\"businessType != null and businessType != ''\">\n                and lo.business_type = #{businessType}\n            </if>\n            <if test=\"operateType != null and operateType != ''\">\n                and lo.operate_type = #{operateType}\n            </if>\n            <if test=\"status != null and status != ''\">\n                and lo.status = #{status}\n            </if>\n            <if test=\"beginGmtOperate != null\">\n                and lo.gmt_operate >= #{beginGmtOperate}\n            </if>\n            <if test=\"endGmtOperate != null\">\n                and lo.gmt_operate &lt;= #{endGmtOperate}\n            </if>\n        </where>\n        order by lo.gmt_operate desc, lo.id desc\n    </select>\n\n\n</mapper>"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/resources/mapper/menu/SysMenuMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\" >\n<mapper namespace=\"com.geshanzsq.admin.system.menu.mapper.SysMenuMapper\">\n\n    <resultMap id=\"BaseResultMap\" type=\"com.geshanzsq.admin.system.menu.vo.SysMenuVO\" >\n        <result column=\"id\" property=\"id\" />\n        <result column=\"menu_name\" property=\"menuName\" />\n        <result column=\"parent_id\" property=\"parentId\" />\n        <result column=\"sort\" property=\"sort\" />\n        <result column=\"menu_type\" property=\"menuType\" />\n        <result column=\"permission_code\" property=\"permissionCode\" />\n        <result column=\"router_url\" property=\"routerUrl\" />\n        <result column=\"component_path\" property=\"componentPath\" />\n        <result column=\"router_param\" property=\"routerParam\" />\n        <result column=\"has_frame\" property=\"hasFrame\" />\n        <result column=\"has_cache\" property=\"hasCache\" />\n        <result column=\"menu_icon\" property=\"menuIcon\" />\n        <result column=\"show_status\" property=\"showStatus\" />\n        <result column=\"status\" property=\"status\" />\n        <result column=\"gmt_time\" property=\"gmtCreate\" />\n        <result column=\"fk_create_user_id\" property=\"createUserId\" />\n        <result column=\"gmt_modify\" property=\"gmtModify\" />\n        <result column=\"fk_modify_user_id\" property=\"modifyUserId\" />\n    </resultMap>\n\n    <!-- 通过角色 ids 获取菜单 -->\n    <select id=\"getMenuByRoleIds\" resultMap=\"BaseResultMap\">\n        select m.id, m.permission_code from sys_role_menu rm\n        inner join sys_menu m on rm.fk_menu_id = m.id\n        where m.status = #{menuStatus}\n            and rm.fk_role_id in\n            <foreach collection=\"roleIds\" item=\"roleId\" open=\"(\" separator=\",\" close=\")\">\n                #{roleId}\n            </foreach>\n            and m.menu_type in\n            <foreach collection=\"menuTypes\" item=\"menuType\" open=\"(\" separator=\",\" close=\")\">\n                #{menuType}\n            </foreach>\n    </select>\n\n    <!-- 通过用户 id 获取菜单 -->\n    <select id=\"getMenuByUserId\" resultMap=\"BaseResultMap\">\n        select distinct m.id,\n            m.parent_id,\n            m.menu_name,\n            m.router_url,\n            m.component_path,\n            m.router_param,\n            m.show_status,\n            m.status,\n            m.permission_code,\n            m.has_frame,\n            m.has_cache,\n            m.menu_type,\n            m.menu_icon,\n            m.sort\n\t\tfrom sys_menu m\n        left join sys_role_menu rm on m.id = rm.fk_menu_id\n        left join sys_user_role ur on rm.fk_role_id = ur.fk_role_id\n        left join sys_role ro on ur.fk_role_id = ro.id\n        where ur.fk_user_id = #{userId}\n            and m.status = #{menuStatus}\n            and ro.status = #{roleStatus}\n            and m.menu_type in\n                <foreach collection=\"menuTypes\" item=\"menuType\" open=\"(\" separator=\",\" close=\")\">\n                    #{menuType}\n                </foreach>\n\n        union\n        select distinct m.id,\n            m.parent_id,\n            m.menu_name,\n            m.router_url,\n            m.component_path,\n            m.router_param,\n            m.show_status,\n            m.status,\n            m.permission_code,\n            m.has_frame,\n            m.has_cache,\n            m.menu_type,\n            m.menu_icon,\n            m.sort\n        from sys_menu m\n        where m.has_permission = #{menuHasPermission} and m.status = #{menuStatus}\n            and m.menu_type in\n            <foreach collection=\"menuTypes\" item=\"menuType\" open=\"(\" separator=\",\" close=\")\">\n                #{menuType}\n            </foreach>\n\t\torder by parent_id, sort\n    </select>\n\n    <!-- 获取最大排序 -->\n    <select id=\"selectMaxSortByParentId\" parameterType=\"Long\" resultType=\"java.lang.Integer\">\n        select max(sort) from sys_menu where parent_id = #{parentId}\n    </select>\n\n\n</mapper>"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/resources/mapper/param/SysParamMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\" >\n<mapper namespace=\"com.geshanzsq.admin.system.param.mapper.SysParamMapper\">\n\n    <resultMap id=\"BaseResultMap\" type=\"com.geshanzsq.admin.system.param.vo.SysParamVO\" >\n        <result column=\"id\" property=\"id\" />\n        <result column=\"param_name\" property=\"paramName\" />\n        <result column=\"param_key\" property=\"paramKey\" />\n        <result column=\"param_value\" property=\"paramValue\" />\n        <result column=\"param_type\" property=\"paramType\" />\n        <result column=\"sort\" property=\"sort\" />\n        <result column=\"remark\" property=\"remark\" />\n        <result column=\"gmt_time\" property=\"gmtCreate\" />\n        <result column=\"fk_create_user_id\" property=\"createUserId\" />\n        <result column=\"gmt_modify\" property=\"gmtModify\" />\n        <result column=\"fk_modify_user_id\" property=\"modifyUserId\" />\n    </resultMap>\n\n    <!-- 获取最大排序 -->\n    <select id=\"selectMaxSort\" resultType=\"java.lang.Integer\">\n        select max(sort) from sys_param\n    </select>\n\n</mapper>"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/resources/mapper/role/SysRoleMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\" >\n<mapper namespace=\"com.geshanzsq.admin.system.role.mapper.SysRoleMapper\">\n\n    <resultMap id=\"BaseResultMap\" type=\"com.geshanzsq.admin.system.role.vo.SysRoleVO\" >\n        <result column=\"id\" property=\"id\" />\n        <result column=\"role_name\" property=\"roleName\" />\n        <result column=\"role_code\" property=\"roleCode\" />\n        <result column=\"status\" property=\"status\" />\n        <result column=\"remark\" property=\"remark\" />\n        <result column=\"del_flag\" property=\"delFlag\" />\n        <result column=\"gmt_time\" property=\"gmtCreate\" />\n        <result column=\"fk_create_user_id\" property=\"createUserId\" />\n        <result column=\"gmt_modify\" property=\"gmtModify\" />\n        <result column=\"fk_modify_user_id\" property=\"modifyUserId\" />\n        <result column=\"user_id\" property=\"userId\" />\n    </resultMap>\n\n    <!-- 通过用户 id 获取 -->\n    <select id=\"getRoleByUserIds\" resultMap=\"BaseResultMap\">\n        select r.id, r.role_code, r.role_name, ur.fk_user_id as user_id from sys_user_role ur\n        inner join sys_role r on ur.fk_role_id = r.id\n        where r.status = #{status} and ur.fk_user_id in\n        <foreach collection=\"userIds\" item=\"userId\" open=\"(\" separator=\",\" close=\")\">\n            #{userId}\n        </foreach>\n    </select>\n\n    <!-- 获取最大排序 -->\n    <select id=\"selectMaxSort\" resultType=\"java.lang.Integer\">\n        select max(sort) from sys_role\n    </select>\n</mapper>"
  },
  {
    "path": "geshanzsq-nav-admin/geshanzsq-nav-admin-system/src/main/resources/mapper/user/SysUserRoleMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\" >\n<mapper namespace=\"com.geshanzsq.admin.system.user.mapper.SysUserRoleMapper\">\n\n    <!-- 获取关联未删除的用户数量 -->\n    <select id=\"selectAssociateNotDeleteUserCount\" resultType=\"java.lang.Long\">\n        select count(*) from sys_user_role ur\n        inner join sys_user u on ur.fk_user_id = u.id\n        where u.del_flag = #{delFlag} and ur.fk_role_id in\n        <foreach collection=\"roleIds\" item=\"roleId\" open=\"(\" separator=\",\" close=\")\">\n            #{roleId}\n        </foreach>\n    </select>\n\n    <!-- selectUserIdByRoleCode -->\n    <select id=\"selectUserIdByRoleCode\" resultType=\"java.lang.Long\">\n        select distinct ur.fk_user_id from sys_user_role ur\n        inner join sys_role r on ur.fk_role_id = r.id\n        where r.role_code = #{roleCode}\n    </select>\n</mapper>"
  },
  {
    "path": "geshanzsq-nav-admin/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>geshanzsq-nav</artifactId>\n        <groupId>com.geshanzsq</groupId>\n        <version>2.0.0</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>geshanzsq-nav-admin</artifactId>\n    <name>geshanzsq-nav-admin</name>\n    <description>后台管理</description>\n    <version>2.0.0</version>\n    <packaging>pom</packaging>\n\n    <modules>\n        <module>geshanzsq-nav-admin-application</module>\n        <module>geshanzsq-nav-admin-system</module>\n    </modules>\n\n\n</project>"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-core/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>geshanzsq-nav-common</artifactId>\n        <groupId>com.geshanzsq</groupId>\n        <version>2.0.0</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>geshanzsq-nav-common-core</artifactId>\n    <name>geshanzsq-nav-common-core</name>\n    <description>通用核心模块</description>\n    <version>2.0.0</version>\n\n    <dependencies>\n        <!-- lombok -->\n        <dependency>\n            <groupId>org.projectlombok</groupId>\n            <artifactId>lombok</artifactId>\n        </dependency>\n\n        <!-- yml解析器 -->\n        <dependency>\n            <groupId>org.yaml</groupId>\n            <artifactId>snakeyaml</artifactId>\n        </dependency>\n\n        <!-- 阿里 JSO N解析器 -->\n        <dependency>\n            <groupId>com.alibaba</groupId>\n            <artifactId>fastjson</artifactId>\n        </dependency>\n\n        <!-- Jackson -->\n        <dependency>\n            <groupId>com.fasterxml.jackson.core</groupId>\n            <artifactId>jackson-databind</artifactId>\n        </dependency>\n\n        <!-- huTool 工具 -->\n        <dependency>\n            <groupId>cn.hutool</groupId>\n            <artifactId>hutool-all</artifactId>\n        </dependency>\n\n        <!-- io 常用工具类 -->\n        <dependency>\n            <groupId>commons-io</groupId>\n            <artifactId>commons-io</artifactId>\n        </dependency>\n\n        <!-- MapStruct 对象转换 -->\n        <dependency>\n            <groupId>org.mapstruct</groupId>\n            <artifactId>mapstruct</artifactId>\n        </dependency>\n\n        <!-- 参数校验 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-validation</artifactId>\n        </dependency>\n\n        <!-- knife4j 接口文档 -->\n        <dependency>\n            <groupId>com.github.xiaoymin</groupId>\n            <artifactId>knife4j-spring-boot-starter</artifactId>\n        </dependency>\n\n        <!-- Spring Boot Web -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n    </dependencies>\n\n\n</project>"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-core/src/main/java/com/geshanzsq/common/core/constant/CommonConstant.java",
    "content": "package com.geshanzsq.common.core.constant;\n\n/**\n * 通用常量信息\n *\n * @author geshanzsq\n * @date 2022/5/2\n */\npublic class CommonConstant {\n\n    /**\n     * http请求\n     */\n    public static final String HTTP = \"http://\";\n\n    /**\n     * https请求\n     */\n    public static final String HTTPS = \"https://\";\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-core/src/main/java/com/geshanzsq/common/core/constant/HttpStatus.java",
    "content": "package com.geshanzsq.common.core.constant;\n\n/**\n * 响应状态码\n *\n * @author geshanzsq\n * @date 2022/3/19\n */\npublic class HttpStatus {\n\n    /**\n     * 成功\n     */\n    public static final int SUCCESS = 200;\n\n    /**\n     * 参数列表错误\n     */\n    public static final int BAD_PARAM = 400;\n\n    /**\n     * 未授权、未登录，登录已过期\n     */\n    public static final int UNAUTHORIZED = 401;\n\n    /**\n     * 访问受限、无权限\n     */\n    public static final int FORBIDDEN = 403;\n\n    /**\n     * 资源，服务未找到\n     */\n    public static final int NOT_FOUND = 404;\n\n    /**\n     * 参数不正确，不允许 http 方式\n     */\n    public static final int BAD_METHOD = 405;\n\n    /**\n     * 失败\n     */\n    public static final int FAIL = 500;\n\n\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-core/src/main/java/com/geshanzsq/common/core/enums/CommonStatus.java",
    "content": "package com.geshanzsq.common.core.enums;\n\n/**\n * 通用状态枚举\n *\n * @author geshanzsq\n * @date 2022/6/12\n */\npublic enum CommonStatus {\n\n    /**\n     * 正常\n     */\n    NORMAL(1),\n\n    /**\n     * 停用\n     */\n    DISABLE(2)\n    ;\n\n    public final Integer code;\n\n    CommonStatus(Integer code) {\n        this.code = code;\n    }\n}\n"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-core/src/main/java/com/geshanzsq/common/core/enums/DelFlag.java",
    "content": "package com.geshanzsq.common.core.enums;\n\n/**\n * 删除枚举\n *\n * @author geshanzsq\n * @date 2022/6/18\n */\npublic enum DelFlag {\n\n    /**\n     * 未删除\n     */\n    NO_DELETE(1),\n\n    /**\n     * 已删除\n     */\n    DELETE(2)\n    ;\n\n    public final Integer code;\n\n    DelFlag(Integer code) {\n        this.code = code;\n    }\n}\n"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-core/src/main/java/com/geshanzsq/common/core/enums/OpenStatus.java",
    "content": "package com.geshanzsq.common.core.enums;\n\n/**\n * 开启状态\n *\n * @author geshanzsq\n * @date 2022/10/7\n */\npublic enum OpenStatus {\n    /**\n     * 开始\n     */\n    YES(1),\n\n    /**\n     * 不开启\n     */\n    NO(2)\n    ;\n\n    public final Integer code;\n\n    OpenStatus(Integer code) {\n        this.code = code;\n    }\n}\n"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-core/src/main/java/com/geshanzsq/common/core/enums/YesNo.java",
    "content": "package com.geshanzsq.common.core.enums;\n\n/**\n * 是否枚举\n *\n * @author geshanzsq\n * @date 2022/6/12\n */\npublic enum YesNo {\n\n    /**\n     * 是\n     */\n    YES(1),\n\n    /**\n     * 否\n     */\n    NO(2)\n    ;\n\n    public final Integer code;\n\n    YesNo(Integer code) {\n        this.code = code;\n    }\n}\n"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-core/src/main/java/com/geshanzsq/common/core/exception/BaseException.java",
    "content": "package com.geshanzsq.common.core.exception;\n\nimport com.geshanzsq.common.core.constant.HttpStatus;\nimport lombok.Data;\n\n/**\n * 基本异常\n *\n * @author geshanzsq\n * @date 2022/3/21\n */\n@Data\npublic class BaseException extends RuntimeException {\n\n    /**\n     * 错误码\n     */\n    private int code;\n\n    /**\n     * 错误信息\n     */\n    private String message;\n\n    /**\n     * 所属模块\n     */\n    private String module;\n\n    public BaseException(String message) {\n        super(message);\n        this.code = HttpStatus.FAIL;\n    }\n\n    public BaseException(int code, String message) {\n        super(message);\n        this.code = code;\n    }\n\n    public BaseException(int code, String message, Throwable e) {\n        super(message, e);\n        this.code = code;\n    }\n\n    public BaseException(String message, String module) {\n        super(message);\n        this.module = module;\n    }\n\n    public BaseException(int code, String message, String module) {\n        super(message);\n        this.code = code;\n        this.module = module;\n    }\n\n    public BaseException(int code, String message, String module, Throwable e) {\n        super(message, e);\n        this.code = code;\n        this.module = module;\n    }\n\n    @Override\n    public String getMessage() {\n        return super.getMessage();\n    }\n\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-core/src/main/java/com/geshanzsq/common/core/exception/ParamException.java",
    "content": "package com.geshanzsq.common.core.exception;\n\nimport lombok.Data;\n\n/**\n * 服务异常\n *\n * @author geshanzsq\n * @date 2022/3/21\n */\n@Data\npublic class ParamException extends RuntimeException {\n\n    /**\n     * 错误信息\n     */\n    private String message;\n\n    public ParamException(String message) {\n        super(message);\n        this.message = message;\n    }\n\n    public ParamException(String message, Throwable e) {\n        super(message, e);\n        this.message = message;\n    }\n\n    @Override\n    public String getMessage() {\n        return this.message;\n    }\n}\n"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-core/src/main/java/com/geshanzsq/common/core/exception/ServiceException.java",
    "content": "package com.geshanzsq.common.core.exception;\n\nimport com.geshanzsq.common.core.constant.HttpStatus;\nimport lombok.Data;\n\n/**\n * 服务异常\n *\n * @author geshanzsq\n * @date 2022/3/21\n */\n@Data\npublic class ServiceException extends RuntimeException {\n\n    /**\n     * 错误码\n     */\n    private int code;\n\n    /**\n     * 错误信息\n     */\n    private String message;\n\n    public ServiceException(String message) {\n        super(message);\n        this.code = HttpStatus.FAIL;\n        this.message = message;\n    }\n\n    public ServiceException(int code, String message) {\n        super(message);\n        this.code = code;\n        this.message = message;\n    }\n\n    public ServiceException(String message, Throwable e) {\n        super(message, e);\n        this.code = HttpStatus.FAIL;\n        this.message = message;\n    }\n\n    @Override\n    public String getMessage() {\n        return this.message;\n    }\n}\n"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-core/src/main/java/com/geshanzsq/common/core/filter/XssFilter.java",
    "content": "package com.geshanzsq.common.core.filter;\n\nimport com.geshanzsq.common.core.util.string.StrUtils;\nimport org.springframework.http.HttpMethod;\n\nimport javax.servlet.*;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * 防止 XSS 攻击的过滤器\n *\n * @author geshanzsq\n * @date 2023/5/3\n */\npublic class XssFilter implements Filter {\n\n    /**\n     * 排除链接\n     */\n    public List<String> excludes = new ArrayList<>();\n\n    @Override\n    public void init(FilterConfig filterConfig) {\n        String tempExcludes = filterConfig.getInitParameter(\"excludes\");\n        if (StrUtils.isNotEmpty(tempExcludes)) {\n            String[] url = tempExcludes.split(\",\");\n            for (int i = 0; url != null && i < url.length; i++) {\n                excludes.add(url[i]);\n            }\n        }\n    }\n\n    @Override\n    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)\n            throws IOException, ServletException {\n        HttpServletRequest req = (HttpServletRequest) request;\n        HttpServletResponse resp = (HttpServletResponse) response;\n        if (handleExcludeURL(req, resp)) {\n            chain.doFilter(request, response);\n            return;\n        }\n        XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper((HttpServletRequest) request);\n        chain.doFilter(xssRequest, response);\n    }\n\n    private boolean handleExcludeURL(HttpServletRequest request, HttpServletResponse response) {\n        String url = request.getServletPath();\n        String method = request.getMethod();\n        // GET DELETE 不过滤\n        if (method == null || HttpMethod.GET.matches(method) || HttpMethod.DELETE.matches(method)) {\n            return true;\n        }\n        return StrUtils.matches(url, excludes);\n    }\n\n    @Override\n    public void destroy() {\n\n    }\n}\n"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-core/src/main/java/com/geshanzsq/common/core/filter/XssHttpServletRequestWrapper.java",
    "content": "package com.geshanzsq.common.core.filter;\n\nimport com.geshanzsq.common.core.util.html.EscapeUtil;\nimport com.geshanzsq.common.core.util.string.StrUtils;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.MediaType;\nimport javax.servlet.ReadListener;\nimport javax.servlet.ServletInputStream;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletRequestWrapper;\nimport java.io.ByteArrayInputStream;\nimport java.io.IOException;\nimport org.apache.commons.io.IOUtils;\n\n\n/**\n * XSS 过滤处理\n *\n * @author geshanzsq\n * @date 2023/5/3\n */\npublic class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {\n    /**\n     * @param request\n     */\n    public XssHttpServletRequestWrapper(HttpServletRequest request) {\n        super(request);\n    }\n\n    @Override\n    public String[] getParameterValues(String name) {\n        String[] values = super.getParameterValues(name);\n        if (values != null) {\n            int length = values.length;\n            String[] escapesValues = new String[length];\n            for (int i = 0; i < length; i++) {\n                // 防xss攻击和过滤前后空格\n                escapesValues[i] = EscapeUtil.clean(values[i]).trim();\n            }\n            return escapesValues;\n        }\n        return super.getParameterValues(name);\n    }\n\n    @Override\n    public ServletInputStream getInputStream() throws IOException {\n        // 非json类型，直接返回\n        if (!isJsonRequest()) {\n            return super.getInputStream();\n        }\n\n        // 为空，直接返回\n        String json = IOUtils.toString(super.getInputStream(), \"utf-8\");\n        if (StrUtils.isEmpty(json)) {\n            return super.getInputStream();\n        }\n\n        // xss过滤\n        json = EscapeUtil.clean(json).trim();\n        byte[] jsonBytes = json.getBytes(\"utf-8\");\n        final ByteArrayInputStream bis = new ByteArrayInputStream(jsonBytes);\n        return new ServletInputStream() {\n            @Override\n            public boolean isFinished() {\n                return true;\n            }\n\n            @Override\n            public boolean isReady() {\n                return true;\n            }\n\n            @Override\n            public int available() throws IOException {\n                return jsonBytes.length;\n            }\n\n            @Override\n            public void setReadListener(ReadListener readListener) {\n            }\n\n            @Override\n            public int read() throws IOException {\n                return bis.read();\n            }\n        };\n    }\n\n    /**\n     * 是否是Json请求\n     */\n    public boolean isJsonRequest() {\n        String header = super.getHeader(HttpHeaders.CONTENT_TYPE);\n        return StrUtils.startsWithIgnoreCase(header, MediaType.APPLICATION_JSON_VALUE);\n    }\n}\n"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-core/src/main/java/com/geshanzsq/common/core/util/html/EscapeUtil.java",
    "content": "package com.geshanzsq.common.core.util.html;\n\nimport com.geshanzsq.common.core.util.string.StrUtils;\n\n/**\n * 转义和反转义工具类\n *\n * @author geshanzsq\n * @date 2023/5/3\n */\npublic class EscapeUtil {\n\n    public static final String RE_HTML_MARK = \"(<[^<]*?>)|(<[\\\\s]*?/[^<]*?>)|(<[^<]*?/[\\\\s]*?>)\";\n\n    private static final char[][] TEXT = new char[64][];\n\n    static {\n        for (int i = 0; i < 64; i++) {\n            TEXT[i] = new char[]{(char) i};\n        }\n\n        // special HTML characters\n        // 单引号\n        TEXT['\\''] = \"&#039;\".toCharArray();\n        // 双引号\n        TEXT['\"'] = \"&#34;\".toCharArray();\n        // &符\n        TEXT['&'] = \"&#38;\".toCharArray();\n        // 小于号\n        TEXT['<'] = \"&#60;\".toCharArray();\n        // 大于号\n        TEXT['>'] = \"&#62;\".toCharArray();\n    }\n\n    /**\n     * 转义文本中的HTML字符为安全的字符\n     *\n     * @param text 被转义的文本\n     * @return 转义后的文本\n     */\n    public static String escape(String text) {\n        return encode(text);\n    }\n\n    /**\n     * 还原被转义的HTML特殊字符\n     *\n     * @param content 包含转义符的HTML内容\n     * @return 转换后的字符串\n     */\n    public static String unescape(String content) {\n        return decode(content);\n    }\n\n    /**\n     * 清除所有HTML标签，但是不删除标签内的内容\n     *\n     * @param content 文本\n     * @return 清除标签后的文本\n     */\n    public static String clean(String content) {\n        return new HTMLFilter().filter(content);\n    }\n\n    /**\n     * Escape编码\n     *\n     * @param text 被编码的文本\n     * @return 编码后的字符\n     */\n    private static String encode(String text) {\n        if (StrUtils.isEmpty(text)) {\n            return StrUtils.EMPTY;\n        }\n\n        final StringBuilder tmp = new StringBuilder(text.length() * 6);\n        char c;\n        for (int i = 0; i < text.length(); i++) {\n            c = text.charAt(i);\n            if (c < 256) {\n                tmp.append(\"%\");\n                if (c < 16) {\n                    tmp.append(\"0\");\n                }\n                tmp.append(Integer.toString(c, 16));\n            } else {\n                tmp.append(\"%u\");\n                if (c <= 0xfff) {\n                    // issue#I49JU8@Gitee\n                    tmp.append(\"0\");\n                }\n                tmp.append(Integer.toString(c, 16));\n            }\n        }\n        return tmp.toString();\n    }\n\n    /**\n     * Escape解码\n     *\n     * @param content 被转义的内容\n     * @return 解码后的字符串\n     */\n    public static String decode(String content) {\n        if (StrUtils.isEmpty(content)) {\n            return content;\n        }\n\n        StringBuilder tmp = new StringBuilder(content.length());\n        int lastPos = 0, pos = 0;\n        char ch;\n        while (lastPos < content.length()) {\n            pos = content.indexOf(\"%\", lastPos);\n            if (pos == lastPos) {\n                if (content.charAt(pos + 1) == 'u') {\n                    ch = (char) Integer.parseInt(content.substring(pos + 2, pos + 6), 16);\n                    tmp.append(ch);\n                    lastPos = pos + 6;\n                } else {\n                    ch = (char) Integer.parseInt(content.substring(pos + 1, pos + 3), 16);\n                    tmp.append(ch);\n                    lastPos = pos + 3;\n                }\n            } else {\n                if (pos == -1) {\n                    tmp.append(content.substring(lastPos));\n                    lastPos = content.length();\n                } else {\n                    tmp.append(content.substring(lastPos, pos));\n                    lastPos = pos;\n                }\n            }\n        }\n        return tmp.toString();\n    }\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-core/src/main/java/com/geshanzsq/common/core/util/html/HTMLFilter.java",
    "content": "package com.geshanzsq.common.core.util.html;\n\nimport java.util.*;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.ConcurrentMap;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\n/**\n * HTML 过滤器，用于去除 XSS 漏洞隐患。\n *\n * @author geshanzsq\n * @date 2023/5/3\n */\npublic class HTMLFilter {\n    /**\n     * regex flag union representing /si modifiers in php\n     **/\n    private static final int REGEX_FLAGS_SI = Pattern.CASE_INSENSITIVE | Pattern.DOTALL;\n    private static final Pattern P_COMMENTS = Pattern.compile(\"<!--(.*?)-->\", Pattern.DOTALL);\n    private static final Pattern P_COMMENT = Pattern.compile(\"^!--(.*)--$\", REGEX_FLAGS_SI);\n    private static final Pattern P_TAGS = Pattern.compile(\"<(.*?)>\", Pattern.DOTALL);\n    private static final Pattern P_END_TAG = Pattern.compile(\"^/([a-z0-9]+)\", REGEX_FLAGS_SI);\n    private static final Pattern P_START_TAG = Pattern.compile(\"^([a-z0-9]+)(.*?)(/?)$\", REGEX_FLAGS_SI);\n    private static final Pattern P_QUOTED_ATTRIBUTES = Pattern.compile(\"([a-z0-9]+)=([\\\"'])(.*?)\\\\2\", REGEX_FLAGS_SI);\n    private static final Pattern P_UNQUOTED_ATTRIBUTES = Pattern.compile(\"([a-z0-9]+)(=)([^\\\"\\\\s']+)\", REGEX_FLAGS_SI);\n    private static final Pattern P_PROTOCOL = Pattern.compile(\"^([^:]+):\", REGEX_FLAGS_SI);\n    private static final Pattern P_ENTITY = Pattern.compile(\"&#(\\\\d+);?\");\n    private static final Pattern P_ENTITY_UNICODE = Pattern.compile(\"&#x([0-9a-f]+);?\");\n    private static final Pattern P_ENCODE = Pattern.compile(\"%([0-9a-f]{2});?\");\n    private static final Pattern P_VALID_ENTITIES = Pattern.compile(\"&([^&;]*)(?=(;|&|$))\");\n    private static final Pattern P_VALID_QUOTES = Pattern.compile(\"(>|^)([^<]+?)(<|$)\", Pattern.DOTALL);\n    private static final Pattern P_END_ARROW = Pattern.compile(\"^>\");\n    private static final Pattern P_BODY_TO_END = Pattern.compile(\"<([^>]*?)(?=<|$)\");\n    private static final Pattern P_XML_CONTENT = Pattern.compile(\"(^|>)([^<]*?)(?=>)\");\n    private static final Pattern P_STRAY_LEFT_ARROW = Pattern.compile(\"<([^>]*?)(?=<|$)\");\n    private static final Pattern P_STRAY_RIGHT_ARROW = Pattern.compile(\"(^|>)([^<]*?)(?=>)\");\n    private static final Pattern P_AMP = Pattern.compile(\"&\");\n    private static final Pattern P_QUOTE = Pattern.compile(\"\\\"\");\n    private static final Pattern P_LEFT_ARROW = Pattern.compile(\"<\");\n    private static final Pattern P_RIGHT_ARROW = Pattern.compile(\">\");\n    private static final Pattern P_BOTH_ARROWS = Pattern.compile(\"<>\");\n\n    // @xxx could grow large... maybe use sesat's ReferenceMap\n    private static final ConcurrentMap<String, Pattern> P_REMOVE_PAIR_BLANKS = new ConcurrentHashMap<>();\n    private static final ConcurrentMap<String, Pattern> P_REMOVE_SELF_BLANKS = new ConcurrentHashMap<>();\n\n    /**\n     * set of allowed html elements, along with allowed attributes for each element\n     **/\n    private final Map<String, List<String>> vAllowed;\n    /**\n     * counts of open tags for each (allowable) html element\n     **/\n    private final Map<String, Integer> vTagCounts = new HashMap<>();\n\n    /**\n     * html elements which must always be self-closing (e.g. \"<img />\")\n     **/\n    private final String[] vSelfClosingTags;\n    /**\n     * html elements which must always have separate opening and closing tags (e.g. \"<b></b>\")\n     **/\n    private final String[] vNeedClosingTags;\n    /**\n     * set of disallowed html elements\n     **/\n    private final String[] vDisallowed;\n    /**\n     * attributes which should be checked for valid protocols\n     **/\n    private final String[] vProtocolAtts;\n    /**\n     * allowed protocols\n     **/\n    private final String[] vAllowedProtocols;\n    /**\n     * tags which should be removed if they contain no content (e.g. \"<b></b>\" or \"<b />\")\n     **/\n    private final String[] vRemoveBlanks;\n    /**\n     * entities allowed within html markup\n     **/\n    private final String[] vAllowedEntities;\n    /**\n     * flag determining whether comments are allowed in input String.\n     */\n    private final boolean stripComment;\n    private final boolean encodeQuotes;\n    /**\n     * flag determining whether to try to make tags when presented with \"unbalanced\" angle brackets (e.g. \"<b text </b>\"\n     * becomes \"<b> text </b>\"). If set to false, unbalanced angle brackets will be html escaped.\n     */\n    private final boolean alwaysMakeTags;\n\n    /**\n     * Default constructor.\n     */\n    public HTMLFilter() {\n        vAllowed = new HashMap<>();\n\n        final ArrayList<String> a_atts = new ArrayList<>();\n        a_atts.add(\"href\");\n        a_atts.add(\"target\");\n        vAllowed.put(\"a\", a_atts);\n\n        final ArrayList<String> img_atts = new ArrayList<>();\n        img_atts.add(\"src\");\n        img_atts.add(\"width\");\n        img_atts.add(\"height\");\n        img_atts.add(\"alt\");\n        vAllowed.put(\"img\", img_atts);\n\n        final ArrayList<String> no_atts = new ArrayList<>();\n        vAllowed.put(\"b\", no_atts);\n        vAllowed.put(\"strong\", no_atts);\n        vAllowed.put(\"i\", no_atts);\n        vAllowed.put(\"em\", no_atts);\n\n        vSelfClosingTags = new String[]{\"img\"};\n        vNeedClosingTags = new String[]{\"a\", \"b\", \"strong\", \"i\", \"em\"};\n        vDisallowed = new String[]{};\n        vAllowedProtocols = new String[]{\"http\", \"mailto\", \"https\"}; // no ftp.\n        vProtocolAtts = new String[]{\"src\", \"href\"};\n        vRemoveBlanks = new String[]{\"a\", \"b\", \"strong\", \"i\", \"em\"};\n        vAllowedEntities = new String[]{\"amp\", \"gt\", \"lt\", \"quot\"};\n        stripComment = true;\n        encodeQuotes = true;\n        alwaysMakeTags = false;\n    }\n\n    /**\n     * Map-parameter configurable constructor.\n     *\n     * @param conf map containing configuration. keys match field names.\n     */\n    @SuppressWarnings(\"unchecked\")\n    public HTMLFilter(final Map<String, Object> conf) {\n\n        assert conf.containsKey(\"vAllowed\") : \"configuration requires vAllowed\";\n        assert conf.containsKey(\"vSelfClosingTags\") : \"configuration requires vSelfClosingTags\";\n        assert conf.containsKey(\"vNeedClosingTags\") : \"configuration requires vNeedClosingTags\";\n        assert conf.containsKey(\"vDisallowed\") : \"configuration requires vDisallowed\";\n        assert conf.containsKey(\"vAllowedProtocols\") : \"configuration requires vAllowedProtocols\";\n        assert conf.containsKey(\"vProtocolAtts\") : \"configuration requires vProtocolAtts\";\n        assert conf.containsKey(\"vRemoveBlanks\") : \"configuration requires vRemoveBlanks\";\n        assert conf.containsKey(\"vAllowedEntities\") : \"configuration requires vAllowedEntities\";\n\n        vAllowed = Collections.unmodifiableMap((HashMap<String, List<String>>) conf.get(\"vAllowed\"));\n        vSelfClosingTags = (String[]) conf.get(\"vSelfClosingTags\");\n        vNeedClosingTags = (String[]) conf.get(\"vNeedClosingTags\");\n        vDisallowed = (String[]) conf.get(\"vDisallowed\");\n        vAllowedProtocols = (String[]) conf.get(\"vAllowedProtocols\");\n        vProtocolAtts = (String[]) conf.get(\"vProtocolAtts\");\n        vRemoveBlanks = (String[]) conf.get(\"vRemoveBlanks\");\n        vAllowedEntities = (String[]) conf.get(\"vAllowedEntities\");\n        stripComment = conf.containsKey(\"stripComment\") ? (Boolean) conf.get(\"stripComment\") : true;\n        encodeQuotes = conf.containsKey(\"encodeQuotes\") ? (Boolean) conf.get(\"encodeQuotes\") : true;\n        alwaysMakeTags = conf.containsKey(\"alwaysMakeTags\") ? (Boolean) conf.get(\"alwaysMakeTags\") : true;\n    }\n\n    private void reset() {\n        vTagCounts.clear();\n    }\n\n    // my versions of some PHP library functions\n    public static String chr(final int decimal) {\n        return String.valueOf((char) decimal);\n    }\n\n    public static String htmlSpecialChars(final String s) {\n        String result = s;\n        result = regexReplace(P_AMP, \"&amp;\", result);\n        result = regexReplace(P_QUOTE, \"&quot;\", result);\n        result = regexReplace(P_LEFT_ARROW, \"&lt;\", result);\n        result = regexReplace(P_RIGHT_ARROW, \"&gt;\", result);\n        return result;\n    }\n\n\n    /**\n     * given a user submitted input String, filter out any invalid or restricted html.\n     *\n     * @param input text (i.e. submitted by a user) than may contain html\n     * @return \"clean\" version of input, with only valid, whitelisted html elements allowed\n     */\n    public String filter(final String input) {\n        reset();\n        String s = input;\n\n        s = escapeComments(s);\n\n        s = balanceHTML(s);\n\n        s = checkTags(s);\n\n        s = processRemoveBlanks(s);\n\n        // s = validateEntities(s);\n\n        return s;\n    }\n\n    public boolean isAlwaysMakeTags() {\n        return alwaysMakeTags;\n    }\n\n    public boolean isStripComments() {\n        return stripComment;\n    }\n\n    private String escapeComments(final String s) {\n        final Matcher m = P_COMMENTS.matcher(s);\n        final StringBuffer buf = new StringBuffer();\n        if (m.find()) {\n            final String match = m.group(1); // (.*?)\n            m.appendReplacement(buf, Matcher.quoteReplacement(\"<!--\" + htmlSpecialChars(match) + \"-->\"));\n        }\n        m.appendTail(buf);\n\n        return buf.toString();\n    }\n\n    private String balanceHTML(String s) {\n        if (alwaysMakeTags) {\n            //\n            // try and form html\n            //\n            s = regexReplace(P_END_ARROW, \"\", s);\n            // 不追加结束标签\n            s = regexReplace(P_BODY_TO_END, \"<$1>\", s);\n            s = regexReplace(P_XML_CONTENT, \"$1<$2\", s);\n\n        } else {\n            //\n            // escape stray brackets\n            //\n            s = regexReplace(P_STRAY_LEFT_ARROW, \"&lt;$1\", s);\n            s = regexReplace(P_STRAY_RIGHT_ARROW, \"$1$2&gt;<\", s);\n\n            //\n            // the last regexp causes '<>' entities to appear\n            // (we need to do a lookahead assertion so that the last bracket can\n            // be used in the next pass of the regexp)\n            //\n            s = regexReplace(P_BOTH_ARROWS, \"\", s);\n        }\n\n        return s;\n    }\n\n    private String checkTags(String s) {\n        Matcher m = P_TAGS.matcher(s);\n\n        final StringBuffer buf = new StringBuffer();\n        while (m.find()) {\n            String replaceStr = m.group(1);\n            replaceStr = processTag(replaceStr);\n            m.appendReplacement(buf, Matcher.quoteReplacement(replaceStr));\n        }\n        m.appendTail(buf);\n\n        // these get tallied in processTag\n        // (remember to reset before subsequent calls to filter method)\n        final StringBuilder sBuilder = new StringBuilder(buf.toString());\n        for (String key : vTagCounts.keySet()) {\n            for (int ii = 0; ii < vTagCounts.get(key); ii++) {\n                sBuilder.append(\"</\").append(key).append(\">\");\n            }\n        }\n        s = sBuilder.toString();\n\n        return s;\n    }\n\n    private String processRemoveBlanks(final String s) {\n        String result = s;\n        for (String tag : vRemoveBlanks) {\n            if (!P_REMOVE_PAIR_BLANKS.containsKey(tag)) {\n                P_REMOVE_PAIR_BLANKS.putIfAbsent(tag, Pattern.compile(\"<\" + tag + \"(\\\\s[^>]*)?></\" + tag + \">\"));\n            }\n            result = regexReplace(P_REMOVE_PAIR_BLANKS.get(tag), \"\", result);\n            if (!P_REMOVE_SELF_BLANKS.containsKey(tag)) {\n                P_REMOVE_SELF_BLANKS.putIfAbsent(tag, Pattern.compile(\"<\" + tag + \"(\\\\s[^>]*)?/>\"));\n            }\n            result = regexReplace(P_REMOVE_SELF_BLANKS.get(tag), \"\", result);\n        }\n\n        return result;\n    }\n\n    private static String regexReplace(final Pattern regex_pattern, final String replacement, final String s) {\n        Matcher m = regex_pattern.matcher(s);\n        return m.replaceAll(replacement);\n    }\n\n    private String processTag(final String s) {\n        // ending tags\n        Matcher m = P_END_TAG.matcher(s);\n        if (m.find()) {\n            final String name = m.group(1).toLowerCase();\n            if (allowed(name)) {\n                if (!inArray(name, vSelfClosingTags)) {\n                    if (vTagCounts.containsKey(name)) {\n                        vTagCounts.put(name, vTagCounts.get(name) - 1);\n                        return \"</\" + name + \">\";\n                    }\n                }\n            }\n        }\n\n        // starting tags\n        m = P_START_TAG.matcher(s);\n        if (m.find()) {\n            final String name = m.group(1).toLowerCase();\n            final String body = m.group(2);\n            String ending = m.group(3);\n\n            // debug( \"in a starting tag, name='\" + name + \"'; body='\" + body + \"'; ending='\" + ending + \"'\" );\n            if (allowed(name)) {\n                final StringBuilder params = new StringBuilder();\n\n                final Matcher m2 = P_QUOTED_ATTRIBUTES.matcher(body);\n                final Matcher m3 = P_UNQUOTED_ATTRIBUTES.matcher(body);\n                final List<String> paramNames = new ArrayList<>();\n                final List<String> paramValues = new ArrayList<>();\n                while (m2.find()) {\n                    paramNames.add(m2.group(1)); // ([a-z0-9]+)\n                    paramValues.add(m2.group(3)); // (.*?)\n                }\n                while (m3.find()) {\n                    paramNames.add(m3.group(1)); // ([a-z0-9]+)\n                    paramValues.add(m3.group(3)); // ([^\\\"\\\\s']+)\n                }\n\n                String paramName, paramValue;\n                for (int ii = 0; ii < paramNames.size(); ii++) {\n                    paramName = paramNames.get(ii).toLowerCase();\n                    paramValue = paramValues.get(ii);\n\n                    // debug( \"paramName='\" + paramName + \"'\" );\n                    // debug( \"paramValue='\" + paramValue + \"'\" );\n                    // debug( \"allowed? \" + vAllowed.get( name ).contains( paramName ) );\n\n                    if (allowedAttribute(name, paramName)) {\n                        if (inArray(paramName, vProtocolAtts)) {\n                            paramValue = processParamProtocol(paramValue);\n                        }\n                        params.append(' ').append(paramName).append(\"=\\\\\\\"\").append(paramValue).append(\"\\\\\\\"\");\n                    }\n                }\n\n                if (inArray(name, vSelfClosingTags)) {\n                    ending = \" /\";\n                }\n\n                if (inArray(name, vNeedClosingTags)) {\n                    ending = \"\";\n                }\n\n                if (ending == null || ending.length() < 1) {\n                    if (vTagCounts.containsKey(name)) {\n                        vTagCounts.put(name, vTagCounts.get(name) + 1);\n                    } else {\n                        vTagCounts.put(name, 1);\n                    }\n                } else {\n                    ending = \" /\";\n                }\n                return \"<\" + name + params + ending + \">\";\n            } else {\n                return \"\";\n            }\n        }\n\n        // comments\n        m = P_COMMENT.matcher(s);\n        if (!stripComment && m.find()) {\n            return \"<\" + m.group() + \">\";\n        }\n\n        return \"\";\n    }\n\n    private String processParamProtocol(String s) {\n        s = decodeEntities(s);\n        final Matcher m = P_PROTOCOL.matcher(s);\n        if (m.find()) {\n            final String protocol = m.group(1);\n            if (!inArray(protocol, vAllowedProtocols)) {\n                // bad protocol, turn into local anchor link instead\n                s = \"#\" + s.substring(protocol.length() + 1);\n                if (s.startsWith(\"#//\")) {\n                    s = \"#\" + s.substring(3);\n                }\n            }\n        }\n\n        return s;\n    }\n\n    private String decodeEntities(String s) {\n        StringBuffer buf = new StringBuffer();\n\n        Matcher m = P_ENTITY.matcher(s);\n        while (m.find()) {\n            final String match = m.group(1);\n            final int decimal = Integer.decode(match).intValue();\n            m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal)));\n        }\n        m.appendTail(buf);\n        s = buf.toString();\n\n        buf = new StringBuffer();\n        m = P_ENTITY_UNICODE.matcher(s);\n        while (m.find()) {\n            final String match = m.group(1);\n            final int decimal = Integer.valueOf(match, 16).intValue();\n            m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal)));\n        }\n        m.appendTail(buf);\n        s = buf.toString();\n\n        buf = new StringBuffer();\n        m = P_ENCODE.matcher(s);\n        while (m.find()) {\n            final String match = m.group(1);\n            final int decimal = Integer.valueOf(match, 16).intValue();\n            m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal)));\n        }\n        m.appendTail(buf);\n        s = buf.toString();\n\n        s = validateEntities(s);\n        return s;\n    }\n\n    private String validateEntities(final String s) {\n        StringBuffer buf = new StringBuffer();\n\n        // validate entities throughout the string\n        Matcher m = P_VALID_ENTITIES.matcher(s);\n        while (m.find()) {\n            final String one = m.group(1); // ([^&;]*)\n            final String two = m.group(2); // (?=(;|&|$))\n            m.appendReplacement(buf, Matcher.quoteReplacement(checkEntity(one, two)));\n        }\n        m.appendTail(buf);\n\n        return encodeQuotes(buf.toString());\n    }\n\n    private String encodeQuotes(final String s) {\n        if (encodeQuotes) {\n            StringBuffer buf = new StringBuffer();\n            Matcher m = P_VALID_QUOTES.matcher(s);\n            while (m.find()) {\n                final String one = m.group(1); // (>|^)\n                final String two = m.group(2); // ([^<]+?)\n                final String three = m.group(3); // (<|$)\n                // 不替换双引号为&quot;，防止json格式无效 regexReplace(P_QUOTE, \"&quot;\", two)\n                m.appendReplacement(buf, Matcher.quoteReplacement(one + two + three));\n            }\n            m.appendTail(buf);\n            return buf.toString();\n        } else {\n            return s;\n        }\n    }\n\n    private String checkEntity(final String preamble, final String term) {\n\n        return \";\".equals(term) && isValidEntity(preamble) ? '&' + preamble : \"&amp;\" + preamble;\n    }\n\n    private boolean isValidEntity(final String entity) {\n        return inArray(entity, vAllowedEntities);\n    }\n\n    private static boolean inArray(final String s, final String[] array) {\n        for (String item : array) {\n            if (item != null && item.equals(s)) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    private boolean allowed(final String name) {\n        return (vAllowed.isEmpty() || vAllowed.containsKey(name)) && !inArray(name, vDisallowed);\n    }\n\n    private boolean allowedAttribute(final String name, final String paramName) {\n        return allowed(name) && (vAllowed.isEmpty() || vAllowed.get(name).contains(paramName));\n    }\n}\n"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-core/src/main/java/com/geshanzsq/common/core/util/http/HttpUtils.java",
    "content": "package com.geshanzsq.common.core.util.http;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.io.BufferedReader;\nimport java.io.IOException;\nimport java.io.InputStreamReader;\nimport java.net.ConnectException;\nimport java.net.SocketTimeoutException;\nimport java.net.URL;\nimport java.net.URLConnection;\n\n/**\n * Http 通用工具类\n *\n * @author geshanzsq\n * @date 2022/5/14\n */\npublic class HttpUtils {\n\n    private static final Logger log = LoggerFactory.getLogger(HttpUtils.class);\n\n    /**\n     * 向指定 URL 发送GET方法的请求\n     * @param url 发送请求的 URL\n     * @param param 请求参数，请求参数格式： name1=value1&name2=value2\n     */\n    public static String sendGet(String url, String param) {\n        return sendGet(url, param, \"UTF-8\");\n    }\n\n\n    /**\n     * 向指定 URL 发送GET方法的请求\n     *\n     * @param url 发送请求的 URL\n     * @param param 请求参数，请求参数格式： name1=value1&name2=value2\n     * @param contentType 编码类型\n     */\n    public static String sendGet(String url, String param, String contentType) {\n        StringBuilder result = new StringBuilder();\n        BufferedReader in = null;\n        try {\n            String urlNameString = url + \"?\" + param;\n            URL realUrl = new URL(urlNameString);\n            URLConnection connection = realUrl.openConnection();\n            connection.setRequestProperty(\"accept\", \"*/*\");\n            connection.setRequestProperty(\"connection\", \"Keep-Alive\");\n            connection.setRequestProperty(\"user-agent\", \"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)\");\n            connection.connect();\n            in = new BufferedReader(new InputStreamReader(connection.getInputStream(), contentType));\n            String line;\n            while ((line = in.readLine()) != null) {\n                result.append(line);\n            }\n        } catch (ConnectException e) {\n            log.error(\"调用HttpUtils.sendGet ConnectException, url=\" + url + \",param=\" + param, e);\n        } catch (SocketTimeoutException e) {\n            log.error(\"调用HttpUtils.sendGet SocketTimeoutException, url=\" + url + \",param=\" + param, e);\n        } catch (IOException e) {\n            log.error(\"调用HttpUtils.sendGet IOException, url=\" + url + \",param=\" + param, e);\n        } catch (Exception e) {\n            log.error(\"调用HttpsUtil.sendGet Exception, url=\" + url + \",param=\" + param, e);\n        } finally {\n            try {\n                if (in != null) {\n                    in.close();\n                }\n            } catch (Exception ex) {\n                log.error(\"调用in.close Exception, url=\" + url + \",param=\" + param, ex);\n            }\n        }\n        return result.toString();\n    }\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-core/src/main/java/com/geshanzsq/common/core/util/id/IdUtils.java",
    "content": "package com.geshanzsq.common.core.util.id;\n\nimport java.util.UUID;\n\n/**\n * ID 生成器工具\n *\n * @author geshanzsq\n * @date 2022/5/2\n */\npublic class IdUtils {\n\n    /**\n     * 简化的UUID，去掉了横线\n     */\n    public static String simpleUUID() {\n        return UUID.randomUUID().toString().replaceAll(\"-\", \"\");\n    }\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-core/src/main/java/com/geshanzsq/common/core/util/id/IdWorker.java",
    "content": "package com.geshanzsq.common.core.util.id;\n\n/**\n * 雪花算法 id 生成工具类\n * <p>\n * Twitter开源的分布式 id 生成器，使用雪花算法：\n * SnowFlake 的结构如下(每部分用-分开):\n * 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000\n * 1 位标识，由于 long 基本类型在 Java 中是带符号的，最高位是符号位，正数是 0，负数是 1，所以 id 一般是正数，最高位是 0\n * 41 位时间截(毫秒级)，注意，41 位时间截不是存储当前时间的时间截，而是存储时间截的差值（当前时间截 - 开始时间截)\n * 得到的值），这里的的开始时间截，一般是我们的id生成器开始使用的时间，由我们程序来指定的（如下下面程序 IdWorker 类的 startTime 属性）。41 位的时间截，可以使用 69 年，年 T = (1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69\n * 10 位的数据机器位，可以部署在 1024 个节点，包括 5 位 dataCenterId 和 5 位 workerId\n * 12 位序列，毫秒内的计数，12 位的计数顺序号支持每个节点每毫秒(同一机器，同一时间截)产生 4096 个 ID 序号\n * 加起来刚好 64 位，为一个Long型。\n * SnowFlake 的优点是，整体上按照时间自增排序，并且整个分布式系统内不会产生 ID 碰撞(由数据中心 ID 和机器 ID 作区分)，并且效率较高，经测试，SnowFlake 每秒能够产生 26 万 ID 左右\n *\n * @author geshanzsq\n * @date 2022/3/26\n */\npublic class IdWorker {\n\n    /**\n     * 开始时间截 (2022-03-26)\n     */\n    private static final long startTime = 1648224000000L;\n\n    /**\n     * 机器id所占的位数\n     */\n    private static final long workerIdBits = 5L;\n\n    /**\n     * 数据标识id所占的位数\n     */\n    private static final long dataCenterIdBits = 5L;\n\n    /**\n     * 支持的最大机器id，结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数)\n     */\n    private static final long maxWorkerId = -1L ^ (-1L << workerIdBits);\n\n    /**\n     * 支持的最大数据标识id，结果是31\n     */\n    private static final long maxDataCenterId = -1L ^ (-1L << dataCenterIdBits);\n\n    /**\n     * 序列在id中占的位数\n     */\n    private static final long sequenceBits = 12L;\n\n    /**\n     * 机器ID向左移12位\n     */\n    private static final long workerIdShift = sequenceBits;\n\n    /**\n     * 数据标识id向左移17位(12+5)\n     */\n    private static final long dataCenterIdShift = sequenceBits + workerIdBits;\n\n    /**\n     * 时间截向左移22位(5+5+12)\n     */\n    private static final long timestampLeftShift = sequenceBits + workerIdBits + dataCenterIdBits;\n\n    /**\n     * 生成序列的掩码，这里为4095 (0b111111111111=0xfff=4095)\n     */\n    private static final long sequenceMask = -1L ^ (-1L << sequenceBits);\n\n    /**\n     * 工作机器ID(0~31)\n     */\n    private static long workerId;\n\n    /**\n     * 数据中心ID(0~31)\n     */\n    private static long dataCenterId;\n\n    /**\n     * 毫秒内序列(0~4095)\n     */\n    private static long sequence;\n\n    /**\n     * 上次生成ID的时间截\n     */\n    private static long lastTimestamp = -1L;\n\n\n    public IdWorker() {\n    }\n\n    /**\n     * 构造函数\n     *\n     * @param workerId     工作ID (0~31)\n     * @param dataCenterId 数据中心ID (0~31)\n     */\n    public IdWorker(long workerId, long dataCenterId) {\n        if (workerId > maxWorkerId || workerId < 0) {\n            throw new IllegalArgumentException(String.format(\"worker Id can't be greater than %d or less than 0\", maxWorkerId));\n        }\n        if (dataCenterId > maxDataCenterId || dataCenterId < 0) {\n            throw new IllegalArgumentException(String.format(\"dataCenter Id can't be greater than %d or less than 0\", maxDataCenterId));\n        }\n        IdWorker.workerId = workerId;\n        IdWorker.dataCenterId = dataCenterId;\n    }\n\n    /**\n     * 获得下一个ID (该方法是线程安全的)\n     */\n    public static synchronized long nextId() {\n        long timestamp = timeGen();\n\n        //如果当前时间小于上一次ID生成的时间戳，说明系统时钟回退过这个时候应当抛出异常\n        if (timestamp < lastTimestamp) {\n            throw new RuntimeException(\n                    String.format(\"Clock moved backwards.  Refusing to generate id for %d milliseconds\", lastTimestamp - timestamp));\n        }\n\n        //如果是同一时间生成的，则进行毫秒内序列\n        if (lastTimestamp == timestamp) {\n            sequence = (sequence + 1) & sequenceMask;\n            //毫秒内序列溢出\n            if (sequence == 0) {\n                //阻塞到下一个毫秒,获得新的时间戳\n                timestamp = tilNextMillis(lastTimestamp);\n            }\n        } else {\n            //时间戳改变，毫秒内序列重置\n            sequence = 0L;\n        }\n\n        //上次生成ID的时间截\n        lastTimestamp = timestamp;\n\n        //移位并通过或运算拼到一起组成64位的ID\n        long id = ((timestamp - startTime) << timestampLeftShift)\n                | (dataCenterId << dataCenterIdShift)\n                | (workerId << workerIdShift)\n                | sequence;\n        return id;\n    }\n\n    /**\n     * 阻塞到下一个毫秒，直到获得新的时间戳\n     *\n     * @param lastTimestamp 上次生成ID的时间截\n     * @return 当前时间戳\n     */\n    protected static long tilNextMillis(long lastTimestamp) {\n        long timestamp = timeGen();\n        while (timestamp <= lastTimestamp) {\n            timestamp = timeGen();\n        }\n        return timestamp;\n    }\n\n    /**\n     * 返回以毫秒为单位的当前时间\n     *\n     * @return 当前时间(毫秒)\n     */\n    protected static long timeGen() {\n        return System.currentTimeMillis();\n    }\n\n    public static void main(String[] args) {\n        System.out.println(IdWorker.nextId());\n    }\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-core/src/main/java/com/geshanzsq/common/core/util/ip/AddressUtils.java",
    "content": "package com.geshanzsq.common.core.util.ip;\n\nimport com.alibaba.fastjson.JSON;\nimport com.alibaba.fastjson.JSONObject;\nimport com.geshanzsq.common.core.util.http.HttpUtils;\nimport com.geshanzsq.common.core.util.string.StrUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * 地址工具类\n *\n * @author geshanzsq\n * @date 2022/7/3\n */\npublic class AddressUtils {\n\n    private static final Logger log = LoggerFactory.getLogger(AddressUtils.class);\n\n    /**\n     * IP 地址查询\n     */\n    public static final String IP_URL = \"http://whois.pconline.com.cn/ipJson.jsp\";\n\n    /**\n     * 未知地址\n     */\n    public static final String UNKNOWN = \"XX XX\";\n\n    public static String getRealAddressByIP(String ip) {\n        if (StrUtils.isBlank(ip)) {\n            return \"\";\n        }\n        // 内网不查询\n        if (IpUtils.internalIp(ip)) {\n            return \"内网IP\";\n        }\n        try {\n            String responseStr = HttpUtils.sendGet(IP_URL, \"ip=\" + ip.split(\",\")[0] + \"&json=true\", \"GBK\");\n            if (StrUtils.isBlank(responseStr)) {\n                log.error(\"获取地理位置异常 {}\", ip);\n                return UNKNOWN;\n            }\n            JSONObject obj = JSON.parseObject(responseStr);\n            String region = obj.getString(\"pro\");\n            String city = obj.getString(\"city\");\n            return String.format(\"%s %s\", region, city);\n        } catch (Exception e) {\n            log.error(\"获取地理位置异常 {}\", ip);\n        }\n\n        return UNKNOWN;\n    }\n}\n"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-core/src/main/java/com/geshanzsq/common/core/util/ip/IpUtils.java",
    "content": "package com.geshanzsq.common.core.util.ip;\n\nimport com.geshanzsq.common.core.util.string.StrUtils;\n\nimport javax.servlet.http.HttpServletRequest;\nimport java.net.InetAddress;\nimport java.net.UnknownHostException;\n\n/**\n * 获取 IP 工具类\n *\n * @author geshanzsq\n * @date 2022/7/3\n */\npublic class IpUtils {\n\n    /**\n     * 获取客户端IP\n     * @param request 请求对象\n     * @return IP地址\n     */\n    public static String getIpAddr(HttpServletRequest request) {\n        if (request == null) {\n            return \"unknown\";\n        }\n        String ip = request.getHeader(\"x-forwarded-for\");\n        if (ip == null || ip.length() == 0 || \"unknown\".equalsIgnoreCase(ip)) {\n            ip = request.getHeader(\"Proxy-Client-IP\");\n        }\n        if (ip == null || ip.length() == 0 || \"unknown\".equalsIgnoreCase(ip)) {\n            ip = request.getHeader(\"X-Forwarded-For\");\n        }\n        if (ip == null || ip.length() == 0 || \"unknown\".equalsIgnoreCase(ip)) {\n            ip = request.getHeader(\"WL-Proxy-Client-IP\");\n        }\n        if (ip == null || ip.length() == 0 || \"unknown\".equalsIgnoreCase(ip)) {\n            ip = request.getHeader(\"X-Real-IP\");\n        }\n        if (ip == null || ip.length() == 0 || \"unknown\".equalsIgnoreCase(ip)) {\n            ip = request.getRemoteAddr();\n        }\n        return \"0:0:0:0:0:0:0:1\".equals(ip) ? \"127.0.0.1\" : getMultistageReverseProxyIp(ip);\n    }\n\n    /**\n     * 检查是否为内部 IP 地址\n     * @param ip IP 地址\n     * @return 结果\n     */\n    public static boolean internalIp(String ip) {\n        byte[] addr = textToNumericFormatV4(ip);\n        return internalIp(addr) || \"127.0.0.1\".equals(ip);\n    }\n\n    /**\n     * 检查是否为内部 IP 地址\n     * @param addr byte 地址\n     * @return 结果\n     */\n    private static boolean internalIp(byte[] addr) {\n        if (addr == null || addr.length < 2) {\n            return true;\n        }\n        final byte b0 = addr[0];\n        final byte b1 = addr[1];\n        // 10.x.x.x/8\n        final byte SECTION_1 = 0x0A;\n        // 172.16.x.x/12\n        final byte SECTION_2 = (byte) 0xAC;\n        final byte SECTION_3 = (byte) 0x10;\n        final byte SECTION_4 = (byte) 0x1F;\n        // 192.168.x.x/16\n        final byte SECTION_5 = (byte) 0xC0;\n        final byte SECTION_6 = (byte) 0xA8;\n        switch (b0) {\n            case SECTION_1:\n                return true;\n            case SECTION_2:\n                if (b1 >= SECTION_3 && b1 <= SECTION_4) {\n                    return true;\n                }\n            case SECTION_5:\n                switch (b1) {\n                    case SECTION_6:\n                        return true;\n                }\n            default:\n                return false;\n        }\n    }\n\n    /**\n     * 将 IPv4 地址转换成字节\n     * @param text IPv4 地址\n     * @return byte 字节\n     */\n    public static byte[] textToNumericFormatV4(String text) {\n        if (text.length() == 0) {\n            return null;\n        }\n\n        byte[] bytes = new byte[4];\n        String[] elements = text.split(\"\\\\.\", -1);\n        try {\n            long l;\n            int i;\n            switch (elements.length) {\n                case 1:\n                    l = Long.parseLong(elements[0]);\n                    if ((l < 0L) || (l > 4294967295L)) {\n                        return null;\n                    }\n                    bytes[0] = (byte) (int) (l >> 24 & 0xFF);\n                    bytes[1] = (byte) (int) ((l & 0xFFFFFF) >> 16 & 0xFF);\n                    bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF);\n                    bytes[3] = (byte) (int) (l & 0xFF);\n                    break;\n                case 2:\n                    l = Integer.parseInt(elements[0]);\n                    if ((l < 0L) || (l > 255L)) {\n                        return null;\n                    }\n                    bytes[0] = (byte) (int) (l & 0xFF);\n                    l = Integer.parseInt(elements[1]);\n                    if ((l < 0L) || (l > 16777215L)) {\n                        return null;\n                    }\n                    bytes[1] = (byte) (int) (l >> 16 & 0xFF);\n                    bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF);\n                    bytes[3] = (byte) (int) (l & 0xFF);\n                    break;\n                case 3:\n                    for (i = 0; i < 2; ++i) {\n                        l = Integer.parseInt(elements[i]);\n                        if ((l < 0L) || (l > 255L)) {\n                            return null;\n                        }\n                        bytes[i] = (byte) (int) (l & 0xFF);\n                    }\n                    l = Integer.parseInt(elements[2]);\n                    if ((l < 0L) || (l > 65535L)) {\n                        return null;\n                    }\n                    bytes[2] = (byte) (int) (l >> 8 & 0xFF);\n                    bytes[3] = (byte) (int) (l & 0xFF);\n                    break;\n                case 4:\n                    for (i = 0; i < 4; ++i) {\n                        l = Integer.parseInt(elements[i]);\n                        if ((l < 0L) || (l > 255L)) {\n                            return null;\n                        }\n                        bytes[i] = (byte) (int) (l & 0xFF);\n                    }\n                    break;\n                default:\n                    return null;\n            }\n        } catch (NumberFormatException e) {\n            return null;\n        }\n        return bytes;\n    }\n\n    /**\n     * 获取 IP 地址\n     * @return 本地 IP 地址\n     */\n    public static String getHostIp() {\n        try {\n            return InetAddress.getLocalHost().getHostAddress();\n        } catch (UnknownHostException e) {\n        }\n        return \"127.0.0.1\";\n    }\n\n    /**\n     * 获取主机名\n     * @return 本地主机名\n     */\n    public static String getHostName() {\n        try {\n            return InetAddress.getLocalHost().getHostName();\n        } catch (UnknownHostException e) {\n        }\n        return \"未知\";\n    }\n\n    /**\n     * 从多级反向代理中获得第一个非 unknown IP 地址\n     * @param ip 获得的 IP 地址\n     * @return 第一个非 unknown IP 地址\n     */\n    public static String getMultistageReverseProxyIp(String ip) {\n        // 多级反向代理检测\n        if (ip != null && ip.indexOf(\",\") > 0) {\n            final String[] ips = ip.trim().split(\",\");\n            for (String subIp : ips) {\n                if (false == isUnknown(subIp)) {\n                    ip = subIp;\n                    break;\n                }\n            }\n        }\n        return ip;\n    }\n\n    /**\n     * 检测给定字符串是否为未知，多用于检测 HTTP 请求相关\n     * @param checkString 被检测的字符串\n     * @return 是否未知\n     */\n    public static boolean isUnknown(String checkString) {\n        return StrUtils.isBlank(checkString) || \"unknown\".equalsIgnoreCase(checkString);\n    }\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-core/src/main/java/com/geshanzsq/common/core/util/message/MessageUtils.java",
    "content": "package com.geshanzsq.common.core.util.message;\n\nimport com.geshanzsq.common.core.util.spring.SpringUtils;\nimport org.springframework.context.MessageSource;\nimport org.springframework.context.i18n.LocaleContextHolder;\n\n/**\n * 获取 i18n 资源\n *\n * @author geshanzsq\n * @date 2022/3/23\n */\npublic class MessageUtils {\n\n    /**\n     * 根据消息键和参数获取对应的消息\n     * @param code\n     * @param args\n     */\n    public static String message(String code, Object... args) {\n        MessageSource messageSource = SpringUtils.getBean(MessageSource.class);\n        return messageSource.getMessage(code, args, LocaleContextHolder.getLocale());\n    }\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-core/src/main/java/com/geshanzsq/common/core/util/servlet/ServletUtils.java",
    "content": "package com.geshanzsq.common.core.util.servlet;\n\nimport com.alibaba.fastjson.JSON;\nimport com.geshanzsq.common.core.constant.HttpStatus;\nimport com.geshanzsq.common.core.web.response.ResponseResult;\nimport org.springframework.web.context.request.RequestAttributes;\nimport org.springframework.web.context.request.RequestContextHolder;\nimport org.springframework.web.context.request.ServletRequestAttributes;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\n\n/**\n * 客户端工具类\n *\n * @author geshanzsq\n * @date 2022/3/19\n */\npublic class ServletUtils {\n\n    /**\n     * 渲染到客户端\n     * @param responseResult\n     * @param response\n     */\n    public static void renderString(ResponseResult responseResult, HttpServletResponse response) {\n        renderString(JSON.toJSONString(responseResult), response);\n    }\n\n    /**\n     * 渲染字符到客户端\n     * @param str\n     * @param response\n     */\n    public static void renderString(String str, HttpServletResponse response) {\n        response.setStatus(HttpStatus.SUCCESS);\n        response.setContentType(\"application/json\");\n        response.setCharacterEncoding(\"utf-8\");\n        try {\n            response.getWriter().println(str);\n        } catch (IOException e) {\n            e.printStackTrace();\n        }\n    }\n\n    /**\n     * 获取请求\n     */\n    public static HttpServletRequest getRequest() {\n        return getRequestAttributes().getRequest();\n    }\n\n    /**\n     * 请求属性\n     */\n    public static ServletRequestAttributes getRequestAttributes() {\n        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();\n        return (ServletRequestAttributes) requestAttributes;\n    }\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-core/src/main/java/com/geshanzsq/common/core/util/spring/SpringUtils.java",
    "content": "package com.geshanzsq.common.core.util.spring;\n\nimport org.springframework.beans.BeansException;\nimport org.springframework.beans.factory.config.BeanFactoryPostProcessor;\nimport org.springframework.beans.factory.config.ConfigurableListableBeanFactory;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.ApplicationContextAware;\nimport org.springframework.stereotype.Component;\n\n/**\n * Spring 工具类\n *\n * @author geshanzsq\n * @date 2022/3/23\n */\n@Component\npublic class SpringUtils implements BeanFactoryPostProcessor, ApplicationContextAware {\n\n    /**\n     * bean 工厂\n     */\n    private static ConfigurableListableBeanFactory beanFactory;\n\n    /**\n     * 应用上下文\n     */\n    private static ApplicationContext applicationContext;\n\n    @Override\n    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {\n        SpringUtils.beanFactory = beanFactory;\n    }\n\n    @Override\n    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {\n        SpringUtils.applicationContext = applicationContext;\n    }\n\n    /**\n     * 获取 bean 对象\n     * @param clazz\n     * @param <T>\n     */\n    public static <T> T getBean(Class<T> clazz) {\n        T bean = beanFactory.getBean(clazz);\n        return bean;\n    }\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-core/src/main/java/com/geshanzsq/common/core/util/string/StrUtils.java",
    "content": "package com.geshanzsq.common.core.util.string;\n\nimport com.geshanzsq.common.core.constant.CommonConstant;\nimport org.apache.commons.lang3.StringUtils;\nimport org.springframework.util.AntPathMatcher;\nimport org.springframework.util.CollectionUtils;\n\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Random;\n\n/**\n * 字符串工具类\n *\n * @author geshanzsq\n * @date 2022/3/19\n */\npublic class StrUtils extends StringUtils {\n\n    /**\n     * 左括号\n     */\n    private final static String LEFT_BRACKET = \"{\";\n\n    /**\n     * 右括号\n     */\n    private final static String RIGHT_BRACKET = \"}\";\n\n    /**\n     * 下划线\n     */\n    private final static char UNDERLINE = '_';\n\n    /**\n     * 转换文本, {} 表示占位符，例：\n     * format(\"欢迎访问{blogName}博客，网站地址：{siteUrl}\", \"格姗导航\", \"http://gesdh.cn\")\n     * format(\"欢迎访问{}博客，网站地址：{}\", \"格姗导航\", \"http://gesdh.cn\")\n     *\n     * @param str    需要转换的字符串\n     * @param params 参数\n     */\n    public static String format(String str, Object... params) {\n        if (isBlank(str) || params == null || params.length == 0) {\n            return str;\n        }\n        StringBuilder result = new StringBuilder();\n        // 数组下标\n        int index = 0;\n        while (str.contains(LEFT_BRACKET) && str.contains(RIGHT_BRACKET)) {\n            int start = str.indexOf(LEFT_BRACKET);\n            int end = str.indexOf(RIGHT_BRACKET);\n            // 如果 index 大于 params 数组，说明后续无需替换\n            if (index > (params.length - 1)) {\n                result.append(str);\n                break;\n            }\n            // 如果右 } 大于左 }，直接取出 {，跳过当前，示例：欢迎访问{blogName}博客，}{网站地址：{siteUrl}\n            if (end < start || index > (params.length - 1)) {\n                result.append(str.substring(0, start + 1));\n                str = str.substring(start + 1);\n                continue;\n            }\n            result.append(str.substring(0, start));\n            result.append(params[index]);\n            str = str.substring(end + 1);\n            index++;\n        }\n        result.append(str);\n        return result.toString();\n    }\n\n    /**\n     * 是否为http(s)://开头\n     *\n     * @param link 链接\n     * @return 结果\n     */\n    public static boolean isHttp(String link) {\n        return StringUtils.startsWithAny(link, CommonConstant.HTTP, CommonConstant.HTTPS);\n    }\n\n    /**\n     * 判断是否为空，包含字符串\n     *\n     * @param value 值\n     * @return 是否为空\n     */\n    public static boolean isNullBlank(Object value) {\n        return value == null\n                || (value instanceof String && isBlank(String.valueOf(value)))\n                || (value instanceof Collection && CollectionUtils.isEmpty((Collection) value));\n    }\n\n    /**\n     * 判断是否不为空，包含字符串\n     *\n     * @param value 值\n     * @return 是否不为空\n     */\n    public static boolean isNotNullBlank(Object value) {\n        return !isNullBlank(value);\n    }\n\n    /**\n     * 批量替换前缀\n     *\n     * @param value     需要替换的值\n     * @param matchList 替换内容列表\n     * @return\n     */\n    public static String replacePre(String value, String[] matchList) {\n        String result = value;\n        for (String match : matchList) {\n            if (value.startsWith(match)) {\n                result = value.replaceFirst(match, \"\");\n            }\n        }\n        return result;\n    }\n\n    /**\n     * 下划线转驼峰，如：nick_name > nickName\n     *\n     * @param value 需要转换的值\n     * @return\n     */\n    public static String toCamelCase(String value) {\n        return toCamelCase(value, false);\n    }\n\n    /**\n     * 下划线转驼峰\n     *\n     * @param value            需要转换的值\n     * @param isFirstUpperCase 首字母是否需要大写\n     * @return\n     */\n    public static String toCamelCase(String value, boolean isFirstUpperCase) {\n        if (isBlank(value)) {\n            return value;\n        }\n        StringBuilder result = new StringBuilder();\n        // 是否转为大写\n        boolean upperCase = false;\n        for (int i = 0; i < value.length(); i++) {\n            char c = value.charAt(i);\n            if (UNDERLINE == c) {\n                upperCase = true;\n            } else if (upperCase) {\n                result.append(Character.toUpperCase(c));\n                upperCase = false;\n            } else {\n                result.append(c);\n            }\n        }\n        // 首字母需要大写\n        if (isFirstUpperCase) {\n            return toFirstUpperCase(result.toString());\n        }\n        return result.toString();\n    }\n\n    /**\n     * 数组是否包含指定值\n     *\n     * @param array 数组\n     * @param value 指定值\n     */\n    public static boolean isArrayContains(String[] array, String value) {\n        return Arrays.asList(array).contains(value);\n    }\n\n    /**\n     * 指定值是否已某个值后缀\n     *\n     * @param value 指定值\n     * @param array 数组\n     * @return\n     */\n    public static boolean endsWithIgnoreCase(String value, String[] array) {\n        for (String arr : array) {\n            boolean isEnd = endsWith(value, arr);\n            if (isEnd) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    /**\n     * 首字母转换为大写\n     *\n     * @param value 值\n     * @return\n     */\n    public static String toFirstUpperCase(String value) {\n        if (isBlank(value)) {\n            return value;\n        }\n        return value.substring(0, 1).toUpperCase() + value.substring(1);\n    }\n\n    /**\n     * 获取随机数\n     *\n     * @param str    生成规则的字符串\n     * @param length 生成长度\n     */\n    public static String getRandom(String str, int length) {\n        Random random = new Random();\n        StringBuffer valSb = new StringBuffer();\n        int charLength = str.length();\n        for (int i = 0; i < length; i++) {\n            int index = random.nextInt(charLength);\n            valSb.append(str.charAt(index));\n        }\n        return valSb.toString();\n    }\n\n    /**\n     * 查找指定字符串是否匹配指定字符串列表中的任意一个字符串\n     *\n     * @param str     指定字符串\n     * @param strList 需要检查的字符串数组\n     * @return 是否匹配\n     */\n    public static boolean matches(String str, List<String> strList) {\n        if (CollectionUtils.isEmpty(strList)) {\n            return false;\n        }\n        for (String pattern : strList) {\n            if (isMatch(pattern, str)) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    /**\n     * 判断url是否与规则配置:\n     * ? 表示单个字符;\n     * * 表示一层路径内的任意字符串，不可跨层级;\n     * ** 表示任意层路径;\n     *\n     * @param pattern 匹配规则\n     * @param url     需要匹配的url\n     * @return\n     */\n    public static boolean isMatch(String pattern, String url) {\n        AntPathMatcher matcher = new AntPathMatcher();\n        return matcher.match(pattern, url);\n    }\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-core/src/main/java/com/geshanzsq/common/core/util/thread/Threads.java",
    "content": "package com.geshanzsq.common.core.util.thread;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.util.concurrent.*;\n\n/**\n * 线程工具类\n *\n * @author geshanzsq\n * @date 2022/7/4\n */\npublic class Threads {\n\n    private static final Logger log = LoggerFactory.getLogger(Threads.class);\n\n    /**\n     * sleep等待,单位为毫秒\n     */\n    public static void sleep(long milliseconds) {\n        try {\n            Thread.sleep(milliseconds);\n        } catch (InterruptedException e) {\n            return;\n        }\n    }\n\n    /**\n     * 停止线程池\n     * 先使用shutdown, 停止接收新任务并尝试完成所有已存在任务.\n     * 如果超时, 则调用shutdownNow, 取消在workQueue中Pending的任务,并中断所有阻塞函数.\n     * 如果仍然超時，則強制退出.\n     * 另对在shutdown时线程本身被调用中断做了处理.\n     */\n    public static void shutdownAndAwaitTermination(ExecutorService pool) {\n        if (pool != null && !pool.isShutdown()) {\n            pool.shutdown();\n            try {\n                if (!pool.awaitTermination(120, TimeUnit.SECONDS)) {\n                    pool.shutdownNow();\n                    if (!pool.awaitTermination(120, TimeUnit.SECONDS)) {\n                        log.info(\"Pool did not terminate\");\n                    }\n                }\n            } catch (InterruptedException ie) {\n                pool.shutdownNow();\n                Thread.currentThread().interrupt();\n            }\n        }\n    }\n\n    /**\n     * 打印线程异常信息\n     */\n    public static void printException(Runnable r, Throwable t) {\n        if (t == null && r instanceof Future<?>) {\n            try {\n                Future<?> future = (Future<?>) r;\n                if (future.isDone()) {\n                    future.get();\n                }\n            } catch (CancellationException ce) {\n                t = ce;\n            } catch (ExecutionException ee) {\n                t = ee.getCause();\n            } catch (InterruptedException ie) {\n                Thread.currentThread().interrupt();\n            }\n        }\n        if (t != null) {\n            log.error(t.getMessage(), t);\n        }\n    }\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-core/src/main/java/com/geshanzsq/common/core/web/response/ResponseResult.java",
    "content": "package com.geshanzsq.common.core.web.response;\n\nimport com.geshanzsq.common.core.constant.HttpStatus;\nimport com.geshanzsq.common.core.util.message.MessageUtils;\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Getter;\nimport lombok.Setter;\n\n/**\n * 返回前端统一响应\n *\n * @author geshanzsq\n * @date 2022/3/19\n */\n@ApiModel(\"统一响应\")\n@Getter\n@Setter\npublic class ResponseResult<T> {\n\n    @ApiModelProperty(\"状态码\")\n    private int code;\n\n    @ApiModelProperty(\"提示消息\")\n    private String message;\n\n    @ApiModelProperty(\"数据\")\n    private T data;\n\n    public static ResponseResult success() {\n        return success(null);\n    }\n\n    public static <T> ResponseResult success(T data) {\n        return success(HttpStatus.SUCCESS, MessageUtils.message(\"operate.success\"), data);\n    }\n\n    public static <T> ResponseResult success(String message, T data) {\n        return success(HttpStatus.SUCCESS, message, data);\n    }\n\n    public static <T> ResponseResult success(int code, String message, T data) {\n        return new ResponseResult(code, message, data);\n    }\n\n    public static ResponseResult fail(String message) {\n        return fail(HttpStatus.FAIL, message);\n    }\n\n    public static ResponseResult fail(int code) {\n        return fail(code, MessageUtils.message(\"operate.fail\"));\n    }\n\n    public static ResponseResult fail(int code, String message) {\n        return fail(code, message, null);\n    }\n\n    public static <T> ResponseResult fail(int code, String message, T data) {\n        return new ResponseResult(code, message, data);\n    }\n\n    public ResponseResult(int code, String message, T data) {\n        this.code = code;\n        this.message = message;\n        this.data = data;\n    }\n}\n"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-core/src/main/java/com/geshanzsq/common/core/web/vo/BaseVO.java",
    "content": "package com.geshanzsq.common.core.web.vo;\n\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\n/**\n * 基本返回数据\n *\n * @author geshanzsq\n * @date 2022/6/12\n */\n@Data\n@ApiModel(\"基本返回数据\")\npublic class BaseVO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    @ApiModelProperty(\"创建时间\")\n    private Date gmtCreate;\n\n    @ApiModelProperty(\"创建人用户 id\")\n    private Long createUserId;\n\n    @ApiModelProperty(\"修改时间\")\n    private Date gmtModify;\n\n    @ApiModelProperty(\"修改人用户 id\")\n    private Long modifyUserId;\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-framework/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>geshanzsq-nav-common</artifactId>\n        <groupId>com.geshanzsq</groupId>\n        <version>2.0.0</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>geshanzsq-nav-common-framework</artifactId>\n    <name>geshanzsq-nav-common-framework</name>\n    <description>通用框架模块</description>\n    <version>2.0.0</version>\n\n    <dependencies>\n        <!-- 通用核心模块 -->\n        <dependency>\n            <groupId>com.geshanzsq</groupId>\n            <artifactId>geshanzsq-nav-common-core</artifactId>\n        </dependency>\n\n        <!-- PageHelper 分页插件 -->\n        <dependency>\n            <groupId>com.github.pagehelper</groupId>\n            <artifactId>pagehelper-spring-boot-starter</artifactId>\n        </dependency>\n\n        <!-- MyBatis Plus -->\n        <dependency>\n            <groupId>com.baomidou</groupId>\n            <artifactId>mybatis-plus-boot-starter</artifactId>\n        </dependency>\n\n        <!-- http core -->\n        <dependency>\n            <groupId>org.apache.httpcomponents</groupId>\n            <artifactId>httpcore</artifactId>\n        </dependency>\n\n        <!--文件上传工具类 -->\n        <dependency>\n            <groupId>commons-fileupload</groupId>\n            <artifactId>commons-fileupload</artifactId>\n        </dependency>\n    </dependencies>\n\n\n</project>"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-framework/src/main/java/com/geshanzsq/common/framework/config/ResourceConfig.java",
    "content": "package com.geshanzsq.common.framework.config;\n\nimport com.geshanzsq.common.framework.file.property.FileUploadProperty;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;\nimport org.springframework.web.servlet.config.annotation.WebMvcConfigurer;\n\n/**\n * 资源配置\n *\n * @author geshanzsq\n * @date 2022/9/3\n */\n@Configuration\npublic class ResourceConfig implements WebMvcConfigurer {\n\n    @Autowired\n    private FileUploadProperty fileUploadProperty;\n\n    @Override\n    public void addResourceHandlers(ResourceHandlerRegistry registry) {\n        // 资源文件映射，可通过当前项目就能访问\n        registry.addResourceHandler(fileUploadProperty.getMapPrefix() + \"/**\")\n                .addResourceLocations(\"file:\" + fileUploadProperty.getBasePath() + \"/\");\n    }\n}\n"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-framework/src/main/java/com/geshanzsq/common/framework/config/WebConfig.java",
    "content": "package com.geshanzsq.common.framework.config;\n\nimport cn.hutool.core.date.DatePattern;\nimport com.fasterxml.jackson.databind.DeserializationFeature;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.fasterxml.jackson.databind.module.SimpleModule;\nimport com.fasterxml.jackson.databind.ser.std.ToStringSerializer;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;\nimport org.springframework.web.servlet.config.annotation.WebMvcConfigurer;\n\nimport java.text.SimpleDateFormat;\nimport java.util.TimeZone;\n\n/**\n * Web 配置\n *\n * @author geshanzsq\n * @date 2022/8/7\n */\n@Configuration\npublic class WebConfig implements WebMvcConfigurer {\n\n    @Bean\n    public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {\n        MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();\n        ObjectMapper mapper = new ObjectMapper();\n\n        // 日期格式转换，否则返回前端为 Long\n        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);\n        mapper.setDateFormat(new SimpleDateFormat(DatePattern.NORM_DATETIME_PATTERN));\n        mapper.setTimeZone(TimeZone.getTimeZone(\"GMT+8\"));\n\n        // Long 类型转换为 String 类型，否则导致前端获取数据精度丢失\n        SimpleModule simpleModule = new SimpleModule();\n        simpleModule.addSerializer(Long.class, ToStringSerializer.instance);\n        simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance);\n        mapper.registerModule(simpleModule);\n\n        converter.setObjectMapper(mapper);\n        return converter;\n    }\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-framework/src/main/java/com/geshanzsq/common/framework/file/exception/FileException.java",
    "content": "package com.geshanzsq.common.framework.file.exception;\n\nimport com.geshanzsq.common.core.constant.HttpStatus;\nimport com.geshanzsq.common.core.exception.BaseException;\n\n/**\n * 文件异常\n *\n * @author geshanzsq\n * @date 2022/8/25\n */\npublic class FileException extends BaseException {\n\n    private static final long serialVersionUID = 1L;\n\n    public FileException(String message) {\n        super(HttpStatus.FAIL, message, \"file\");\n    }\n}\n"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-framework/src/main/java/com/geshanzsq/common/framework/file/property/FileUploadProperty.java",
    "content": "package com.geshanzsq.common.framework.file.property;\n\nimport lombok.Data;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.autoconfigure.web.servlet.MultipartProperties;\nimport org.springframework.boot.context.properties.ConfigurationProperties;\nimport org.springframework.context.annotation.Configuration;\n\n/**\n * 文件上传配置\n *\n * @author geshanzsq\n * @date 2022/8/25\n */\n@Data\n@Configuration\n@ConfigurationProperties(prefix = \"file-upload\")\npublic class FileUploadProperty {\n\n    @Autowired\n    private MultipartProperties multipartProperties;\n\n    /**\n     * 文件名称最大长度\n     */\n    private Long nameMaxLength = 200L;\n\n    /**\n     * 文件最大限制\n     */\n    private Long maxSize;\n\n    /**\n     * 上传基本路径\n     */\n    private String basePath;\n\n    /**\n     * 文件映射前缀\n     */\n    private String mapPrefix;\n\n    /**\n     * 文件域名地址\n     */\n    private String domain;\n\n    /**\n     * 图片上传基本路径\n     */\n    public String getFileImageBasePath() {\n        return basePath + \"/image\";\n    }\n\n    public Long getFileMaxSize() {\n        return multipartProperties.getMaxFileSize().toBytes();\n    }\n}\n"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-framework/src/main/java/com/geshanzsq/common/framework/file/service/FileService.java",
    "content": "package com.geshanzsq.common.framework.file.service;\n\nimport org.springframework.web.multipart.MultipartFile;\n\n/**\n * 文件\n *\n * @author geshanzsq\n * @date 2022/9/2\n */\npublic interface FileService {\n\n    /**\n     * 文件上传\n     * @param file 文件\n     * @param allowExtensions 允许上传文件类型\n     * @return 文件全路径\n     */\n    String upload(MultipartFile file, String[] allowExtensions);\n\n    /**\n     * 文件上传\n     * @param file 文件\n     * @return 文件全路径\n     */\n    String upload(MultipartFile file);\n\n    /**\n     * 图片上传\n     * @param file 文件\n     * @return 文件全路径\n     */\n    String uploadImage(MultipartFile file);\n\n    /**\n     * 图片上传\n     *\n     * @param moduleName 模块名\n     * @param file 文件\n     * @return 文件全路径\n     */\n    String uploadPicture(String moduleName, MultipartFile file);\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-framework/src/main/java/com/geshanzsq/common/framework/file/service/impl/FileServiceImpl.java",
    "content": "package com.geshanzsq.common.framework.file.service.impl;\n\nimport cn.hutool.core.lang.UUID;\nimport com.geshanzsq.common.core.util.message.MessageUtils;\nimport com.geshanzsq.common.core.util.string.StrUtils;\nimport com.geshanzsq.common.framework.file.exception.FileException;\nimport com.geshanzsq.common.framework.file.property.FileUploadProperty;\nimport com.geshanzsq.common.framework.file.service.FileService;\nimport com.geshanzsq.common.framework.file.util.FileUploadUtils;\nimport com.geshanzsq.common.framework.file.util.MimeTypeUtils;\nimport org.apache.commons.io.FilenameUtils;\nimport org.apache.commons.lang3.time.DateFormatUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\nimport org.springframework.web.multipart.MultipartFile;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.math.BigInteger;\nimport java.security.MessageDigest;\nimport java.util.Date;\n\n/**\n * 文件\n *\n * @author geshanzsq\n * @date 2022/9/2\n */\n@Service\npublic class FileServiceImpl implements FileService {\n\n    private final static Logger log = LoggerFactory.getLogger(FileServiceImpl.class);\n\n    @Autowired\n    private FileUploadProperty fileUploadProperty;\n\n    /**\n     * 文件上传\n     * @param file 文件\n     * @param allowExtensions 允许上传文件类型\n     * @return 文件全路径\n     */\n    @Override\n    public String upload(MultipartFile file, String[] allowExtensions) {\n        return upload(fileUploadProperty.getBasePath(), file, allowExtensions);\n    }\n\n    /**\n     * 文件上传\n     * @param file 文件\n     * @return 文件全路径\n     */\n    @Override\n    public String upload(MultipartFile file) {\n        return upload(fileUploadProperty.getBasePath(), file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION);\n    }\n\n    /**\n     * 文件上传\n     * @param file 文件\n     * @return 文件全路径\n     */\n    @Override\n    public String uploadImage(MultipartFile file) {\n        return upload(fileUploadProperty.getFileImageBasePath(), file, MimeTypeUtils.IMAGE_EXTENSION);\n    }\n\n    /**\n     * 文件上传\n     * @param baseDirectory 基本目录\n     * @param file 文件\n     * @param allowExtensions 允许上传文件类型\n     * @return 文件全路径\n     */\n    private String upload(String baseDirectory, MultipartFile file, String[] allowExtensions) {\n        // 1 校验文件名称长度\n        int fileNameLength = file.getOriginalFilename().length();\n        if (fileNameLength > fileUploadProperty.getNameMaxLength()) {\n            String message = StrUtils.format(MessageUtils.message(\"file.upload.file.name.max.length\"),\n                    fileUploadProperty.getNameMaxLength());\n            throw new FileException(message);\n        }\n\n        // 2 校验文件大小\n        long fileSize = file.getSize();\n        if (fileSize != -1 && fileSize > fileUploadProperty.getFileMaxSize()) {\n            throw new FileException(MessageUtils.message(\"file.upload.file.size.max\"));\n        }\n\n        // 3 校验文件类型是否符合\n        // 获取文件后缀名\n        String extension = FileUploadUtils.getExtension(file);\n        // 后缀名是否允许\n        boolean isAllowedExtension = FileUploadUtils.isAllowedExtension(extension, allowExtensions);\n        if (!isAllowedExtension) {\n            throw new FileException(MessageUtils.message(\"file.upload.file.type.not.allow\"));\n        }\n\n        // 4 文件上传路径\n        String filePathName = FileUploadUtils.getUploadFilePath(extension);\n        File absoluteFile = null;\n        try {\n            // 4.1 获取上传的真实文件\n             absoluteFile = FileUploadUtils.getAbsoluteFile(baseDirectory, filePathName);\n            // 4.2 将内存文件写入磁盘中\n            file.transferTo(absoluteFile);\n        } catch (IOException e) {\n            log.error(\"文件上传失败：{}\", e.getMessage());\n            throw new FileException(MessageUtils.message(\"file.upload.fail\"));\n        }\n        // 4.3 获取文件映射的地址\n        String fileMapPath = getFileMapPath(baseDirectory, filePathName);\n        return fileMapPath;\n    }\n\n    /**\n     * 图片上传\n     *\n     * @param moduleName 模块名\n     * @param file 文件\n     * @return 文件全路径\n     */\n    @Override\n    public String uploadPicture(String moduleName, MultipartFile file) {\n        return upload(fileUploadProperty.getBasePath() + \"/\" + moduleName, file, MimeTypeUtils.IMAGE_EXTENSION);\n    }\n\n    /**\n     * 获取文件映射的地址\n     * @param baseDirectory 基本路径\n     * @param filePathName 文件路径和名称\n     * @return\n     */\n    private String getFileMapPath(String baseDirectory, String filePathName) {\n        // 文件完整路径\n        String fileFullPath = baseDirectory + filePathName;\n        // 去除基本路径\n        if (StrUtils.startsWith(fileFullPath, fileUploadProperty.getBasePath())) {\n            fileFullPath = fileFullPath.replaceFirst(fileUploadProperty.getBasePath(), \"\");\n        }\n        // 文件域名地址不为空，则拼接域名和路径\n        if (StrUtils.isNotBlank(fileUploadProperty.getDomain())) {\n            return fileUploadProperty.getDomain() + fileFullPath;\n        }\n        // 拼接项目映射地址\n        return fileUploadProperty.getMapPrefix() + fileFullPath;\n    }\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-framework/src/main/java/com/geshanzsq/common/framework/file/util/FileUploadUtils.java",
    "content": "package com.geshanzsq.common.framework.file.util;\n\n\nimport cn.hutool.core.lang.UUID;\nimport com.geshanzsq.common.core.util.string.StrUtils;\nimport org.apache.commons.fileupload.FileItem;\nimport org.apache.commons.fileupload.FileItemFactory;\nimport org.apache.commons.fileupload.disk.DiskFileItemFactory;\nimport org.apache.commons.io.FilenameUtils;\nimport org.apache.commons.lang3.time.DateFormatUtils;\nimport org.apache.http.entity.ContentType;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.web.multipart.MultipartFile;\nimport org.springframework.web.multipart.commons.CommonsMultipartFile;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.math.BigInteger;\nimport java.net.HttpURLConnection;\nimport java.net.URL;\nimport java.security.MessageDigest;\nimport java.util.Date;\n\n/**\n * 文件上传工具类\n *\n * @author geshanzsq\n * @date 2022/11/26\n */\npublic class FileUploadUtils {\n\n    private static final Logger log = LoggerFactory.getLogger(FileUploadUtils.class);\n\n    /**\n     * 后缀名是否允许上传\n     *\n     * @param extension       文件后缀名\n     * @param allowExtensions 允许上传文件类型\n     */\n    public static boolean isAllowedExtension(String extension, String[] allowExtensions) {\n        for (String allowedExtension : allowExtensions) {\n            if (allowedExtension.equalsIgnoreCase(extension)) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    /**\n     * 获取上传的文件路径\n     * @param extension 文件后缀名\n     * @return\n     */\n    public static String getUploadFilePath(String extension) {\n        String filePath = StrUtils.format(\"/{date}/{uuid}.{extension}\",\n                DateFormatUtils.format(new Date(), \"yyyy/MM/dd\"), UUID.fastUUID(), extension);\n        return filePath;\n    }\n\n    /**\n     * 获取真实的上传文件\n     * @param baseDirectory 基本路径\n     * @param filePathName 文件路径和名称\n     */\n    public static File getAbsoluteFile(String baseDirectory, String filePathName) throws IOException {\n        File file = new File(baseDirectory + \"/\" + filePathName);\n        // 目录不存在，新建目录\n        if (!file.getParentFile().exists()) {\n            file.getParentFile().mkdirs();\n        }\n        // 文件不存在，新建文件\n        if (!file.exists()) {\n            file.createNewFile();\n        }\n        return file;\n    }\n\n    /**\n     * url转变为 MultipartFile对象\n     *\n     * @param url\n     * @return\n     * @throws Exception\n     */\n    public static MultipartFile urlToMultipartFile(String url) throws Exception {\n        FileItem item = null;\n        HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();\n        conn.setReadTimeout(5000);\n        conn.setConnectTimeout(5000);\n        conn.addRequestProperty(\"User-Agent\", \"Mozilla/4.0 (compatible; MSIE 6.0;WindowsNT 5.0)\");\n        //设置应用程序要从网络连接读取数据\n        conn.setDoInput(true);\n        conn.setRequestMethod(\"GET\");\n        if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {\n            InputStream is = conn.getInputStream();\n\n            FileItemFactory factory = new DiskFileItemFactory(16, null);\n            String textFieldName = \"pictureFile\";\n            String fileName = getFileNameByUrl(url);\n            item = factory.createItem(textFieldName, ContentType.APPLICATION_OCTET_STREAM.toString(), false, fileName);\n            OutputStream os = item.getOutputStream();\n\n            int bytesRead = 0;\n            byte[] buffer = new byte[8192];\n            while ((bytesRead = is.read(buffer, 0, 8192)) != -1) {\n                os.write(buffer, 0, bytesRead);\n            }\n            os.close();\n            is.close();\n        }\n        return new CommonsMultipartFile(item);\n    }\n\n    /**\n     * 获取文件后缀名\n     */\n    public static String getExtension(MultipartFile file) {\n        String extension = FilenameUtils.getExtension(file.getOriginalFilename());\n        if (StrUtils.isBlank(extension)) {\n            extension = MimeTypeUtils.getExtension(file.getContentType());\n        }\n        return extension;\n    }\n\n    public static String getFileMd5(MultipartFile file) {\n        try {\n            //获取文件的byte信息\n            byte[] uploadBytes = file.getBytes();\n            // 拿到一个MD5转换器\n            MessageDigest md5 = MessageDigest.getInstance(\"MD5\");\n            byte[] digest = md5.digest(uploadBytes);\n            //转换为16进制\n            return new BigInteger(1, digest).toString(16);\n        } catch (Exception e) {\n            log.error(\"获取上传文件 md5 失败：{}\", e.getMessage());\n        }\n        return null;\n    }\n\n    /**\n     * 获取文件名称\n     */\n    private static String getFileNameByUrl(String url) {\n        if (url.lastIndexOf(\"/\") < 0) {\n            return url;\n        }\n        String fileName = url.substring(url.lastIndexOf(\"/\") + 1);\n        if (fileName.lastIndexOf(\"?\") < 0) {\n            return fileName;\n        }\n        fileName = fileName.substring(0, fileName.lastIndexOf(\"?\"));\n        return fileName;\n    }\n\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-framework/src/main/java/com/geshanzsq/common/framework/file/util/MimeTypeUtils.java",
    "content": "package com.geshanzsq.common.framework.file.util;\n\n/**\n * 媒体类型工具类\n *\n * @author geshanzsq\n * @date 2022/8/30\n */\npublic class MimeTypeUtils {\n\n    public static final String IMAGE_PNG = \"image/png\";\n\n    public static final String IMAGE_JPG = \"image/jpg\";\n\n    public static final String IMAGE_JPEG = \"image/jpeg\";\n\n    public static final String IMAGE_BMP = \"image/bmp\";\n\n    public static final String IMAGE_GIF = \"image/gif\";\n\n    public static final String[] IMAGE_EXTENSION = {\"bmp\", \"gif\", \"jpg\", \"jpeg\", \"png\", \"ico\", \"svg\"};\n\n    public static final String[] FLASH_EXTENSION = {\"swf\", \"flv\"};\n\n    public static final String[] MEDIA_EXTENSION = {\"swf\", \"flv\", \"mp3\", \"wav\", \"wma\", \"wmv\", \"mid\", \"avi\", \"mpg\",\n            \"asf\", \"rm\", \"rmvb\"};\n\n    public static final String[] DEFAULT_ALLOWED_EXTENSION = {\n            // 图片\n            \"bmp\", \"gif\", \"jpg\", \"jpeg\", \"png\",\n            // word excel powerpoint\n            \"doc\", \"docx\", \"xls\", \"xlsx\", \"ppt\", \"pptx\", \"html\", \"htm\", \"txt\",\n            // 压缩文件\n            \"rar\", \"zip\", \"gz\", \"bz2\",\n            // pdf\n            \"pdf\"};\n    public static final String[] HTML_EXTENSION = {\"html\", \"htm\"};\n\n    /**\n     * 获取后缀名\n     *\n     * @param fileContentType 文件内容列席\n     * @return\n     */\n    public static String getExtension(String fileContentType) {\n        switch (fileContentType) {\n            case IMAGE_PNG:\n                return \"png\";\n            case IMAGE_JPG:\n                return \"jpg\";\n            case IMAGE_JPEG:\n                return \"jpeg\";\n            case IMAGE_BMP:\n                return \"bmp\";\n            case IMAGE_GIF:\n                return \"gif\";\n            default:\n                return \"\";\n        }\n    }\n}\n"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-framework/src/main/java/com/geshanzsq/common/framework/manager/AsyncManager.java",
    "content": "package com.geshanzsq.common.framework.manager;\n\nimport com.geshanzsq.common.core.util.spring.SpringUtils;\nimport com.geshanzsq.common.core.util.thread.Threads;\n\nimport java.util.TimerTask;\nimport java.util.concurrent.ScheduledExecutorService;\nimport java.util.concurrent.TimeUnit;\n\n/**\n * 异步管理器\n *\n * @author geshanzsq\n * @date 2022/7/4\n */\npublic class AsyncManager {\n\n    /**\n     * 操作延迟10毫秒\n     */\n    private final int OPERATE_DELAY_TIME = 10;\n\n    /**\n     * 异步操作任务调度线程池\n     */\n    private ScheduledExecutorService executor = SpringUtils.getBean(ScheduledExecutorService.class);\n\n    /**\n     * 单例模式\n     */\n    private AsyncManager() {\n    }\n\n    private static AsyncManager me = new AsyncManager();\n\n    public static AsyncManager me() {\n        return me;\n    }\n\n    /**\n     * 执行任务\n     *\n     * @param task 任务\n     */\n    public void execute(TimerTask task) {\n        executor.schedule(task, OPERATE_DELAY_TIME, TimeUnit.MILLISECONDS);\n    }\n\n    /**\n     * 执行任务\n     */\n    public void execute(Runnable runnable) {\n        executor.schedule(runnable, OPERATE_DELAY_TIME, TimeUnit.MILLISECONDS);\n    }\n\n    /**\n     * 停止任务线程池\n     */\n    public void shutdown() {\n        Threads.shutdownAndAwaitTermination(executor);\n    }\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-framework/src/main/java/com/geshanzsq/common/framework/mybatis/page/dto/PageDTO.java",
    "content": "package com.geshanzsq.common.framework.mybatis.page.dto;\n\nimport com.geshanzsq.common.framework.mybatis.plugin.annotation.Query;\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\nimport java.io.Serializable;\n\n/**\n * 分页对象\n *\n * @author geshanzsq\n * @date 2022/3/27\n */\n@ApiModel(\"分页对象\")\n@Data\npublic class PageDTO implements Serializable {\n\n    private static final Long serialVersionUID = 1L;\n\n    @ApiModelProperty(\"每页显示记录数\")\n    @Query(ignore = true)\n    private Long pageSize;\n\n    @ApiModelProperty(\"起始页\")\n    @Query(ignore = true)\n    private Long pageNum;\n\n    @ApiModelProperty(\"排序列，多个用逗号分开\")\n    @Query(ignore = true)\n    private String orderColumn;\n\n    @ApiModelProperty(\"排序类型(asc 或 desc)，多个用逗号分开\")\n    @Query(ignore = true)\n    private String orderType;\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-framework/src/main/java/com/geshanzsq/common/framework/mybatis/page/util/PageUtils.java",
    "content": "package com.geshanzsq.common.framework.mybatis.page.util;\n\nimport com.geshanzsq.common.framework.mybatis.page.dto.PageDTO;\nimport com.geshanzsq.common.framework.mybatis.page.vo.PageVO;\nimport com.geshanzsq.common.framework.mybatis.property.PageProperty;\nimport com.github.pagehelper.PageHelper;\nimport com.github.pagehelper.PageInfo;\n\nimport java.util.List;\n\n/**\n * 分页工具类\n *\n * @author geshanzsq\n * @date 2022/3/27\n */\npublic class PageUtils {\n\n    /**\n     * 开始分页\n     */\n    public static void startPage(PageDTO pageDTO) {\n        startPage(pageDTO.getPageNum(), pageDTO.getPageSize());\n    }\n\n    /**\n     * 开始分页\n     * @param pageNum 起始页\n     * @param pageSize 分页记录数\n     */\n    public static void startPage(Long pageNum, Long pageSize) {\n        if (pageNum == null) {\n            pageNum = 1L;\n        }\n        if (pageSize == null) {\n            pageSize = PageProperty.defaultPageSize;\n        }\n        // 如果超过最大分页数，则设置为最大分页数\n        if (pageSize > PageProperty.maxPageSize) {\n            pageSize = PageProperty.maxPageSize;\n        }\n        PageHelper.startPage(pageNum.intValue(), pageSize.intValue());\n    }\n\n\n    /**\n     * 获取分页信息\n     */\n    public static <T> PageVO<T> getPage(List<T> list) {\n        PageInfo pageInfo = new PageInfo(list);\n        return new PageVO(list, pageInfo.getTotal());\n    }\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-framework/src/main/java/com/geshanzsq/common/framework/mybatis/page/vo/PageVO.java",
    "content": "package com.geshanzsq.common.framework.mybatis.page.vo;\n\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\nimport java.io.Serializable;\nimport java.util.List;\n\n/**\n * 分页对象\n *\n * @author geshanzsq\n * @date 2022/3/27\n */\n@Data\n@ApiModel(\"分页返回\")\n@NoArgsConstructor\n@AllArgsConstructor\npublic class PageVO<T> implements Serializable {\n\n    private static final Long serialVersionUID = 1L;\n\n    @ApiModelProperty(\"数据列表\")\n    private List<T> list;\n\n    @ApiModelProperty(\"总记录数\")\n    private long total;\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-framework/src/main/java/com/geshanzsq/common/framework/mybatis/plugin/annotation/Query.java",
    "content": "package com.geshanzsq.common.framework.mybatis.plugin.annotation;\n\nimport com.geshanzsq.common.framework.mybatis.plugin.enums.QueryWay;\n\nimport java.lang.annotation.*;\n\n/**\n * 查询注解\n *\n * @author geshanzsq\n * @date 2022/11/4\n */\n@Target(ElementType.FIELD)\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\npublic @interface Query {\n\n    /**\n     * 查询方式，默认为相等\n     */\n    QueryWay value() default QueryWay.EQ;\n\n    /**\n     * 属性名，如果当前属性名与 PO 不一致，可指定 PO 的属性名\n     */\n    String fieldName() default \"\";\n\n    /**\n     * 是否忽略查询，如果当前属性不需要查询，则可设置为 true\n     */\n    boolean ignore() default false;\n\n    /**\n     * 是否空查询，当为空时，是否依旧查询，默认不需要查询\n     */\n    boolean empty() default false;\n\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-framework/src/main/java/com/geshanzsq/common/framework/mybatis/plugin/constant/FieldConstant.java",
    "content": "package com.geshanzsq.common.framework.mybatis.plugin.constant;\n\n/**\n * 字段常量\n *\n * @author geshanzsq\n * @date 2022/8/7\n */\npublic class FieldConstant {\n\n    /**\n     * 主键\n     */\n    public static final String ID = \"id\";\n\n    /**\n     * 创建时间\n     */\n    public static final String GMT_CREATE = \"gmtCreate\";\n\n    /**\n     * 创建人用户 id\n     */\n    public static final String CREATE_USER_ID = \"createUserId\";\n\n    /**\n     * 修改时间\n     */\n    public static final String GMT_MODIFY = \"gmtModify\";\n\n    /**\n     * 修改人用户 id\n     */\n    public static final String MODIFY_USER_ID = \"modifyUserId\";\n\n    /**\n     * 排序列，多个用逗号分开\n     */\n    public static final String ORDER_COLUMN = \"orderColumn\";\n\n    /**\n     * 排序类型(asc 或 desc)，多个用逗号分开\n     */\n    public static final String ORDER_TYPE = \"orderType\";\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-framework/src/main/java/com/geshanzsq/common/framework/mybatis/plugin/enums/QueryWay.java",
    "content": "package com.geshanzsq.common.framework.mybatis.plugin.enums;\n\n/**\n * 查询方式枚举\n *\n * @author geshanzsq\n * @date 2022/11/4\n */\npublic enum QueryWay {\n\n    /**\n     * 等于\n     */\n    EQ,\n\n    /**\n     * 不等于\n     */\n    NE,\n\n    /**\n     * 大于\n     */\n    GT,\n\n    /**\n     * 大于等于\n     */\n    GE,\n\n    /**\n     * 小于\n     */\n    LT,\n\n    /**\n     * 小于等于\n     */\n    LE,\n\n    /**\n     * 模糊\n     */\n    LIKE,\n\n    /**\n     * 不模糊\n     */\n    NOT_LIKE,\n\n    /**\n     * 左模糊\n     */\n    LIKE_LEFT,\n\n    /**\n     * 右模糊\n     */\n    LIKE_RIGHT,\n\n    /**\n     * 包含\n     */\n    IN,\n\n    /**\n     * 不包含\n     */\n    NOT_IN\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-framework/src/main/java/com/geshanzsq/common/framework/mybatis/plugin/interceptor/InsertUpdateMyBatisInterceptor.java",
    "content": "package com.geshanzsq.common.framework.mybatis.plugin.interceptor;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport com.geshanzsq.common.core.util.id.IdWorker;\nimport com.geshanzsq.common.framework.mybatis.plugin.constant.FieldConstant;\nimport org.apache.ibatis.binding.MapperMethod;\nimport org.apache.ibatis.mapping.MappedStatement;\nimport org.apache.ibatis.mapping.SqlCommandType;\nimport org.apache.ibatis.plugin.Invocation;\n\nimport java.lang.reflect.Field;\nimport java.util.Date;\nimport java.util.Map;\n\n/**\n * MyBatis 拦截器\n *\n * @author geshanzsq\n * @date 2022/8/7\n */\npublic class InsertUpdateMyBatisInterceptor {\n\n    /**\n     * 调用拦截\n     *\n     * @param invocation    调用器\n     * @param currentUserId 当前用户 id\n     */\n    public static Object intercept(Invocation invocation, Long currentUserId) throws Throwable {\n        MappedStatement statement = (MappedStatement) invocation.getArgs()[0];\n        // 获取执行的 SQL 语句类型\n        SqlCommandType sqlCommandType = statement.getSqlCommandType();\n        // 拦截插入和更新操作，如果不是，则跳过\n        if (!SqlCommandType.INSERT.equals(sqlCommandType) && !SqlCommandType.UPDATE.equals(sqlCommandType)) {\n            return invocation.proceed();\n        }\n\n        // 获取对应的参数类\n        Object parameter = invocation.getArgs()[1];\n        if (parameter == null) {\n            return invocation.proceed();\n        }\n\n        // 获取实体对象，如果属于 ParamMap （单个操作），则从参数 param1 获取，否则为 parameter (批量操作)\n        Object clazz = null;\n        if (parameter instanceof MapperMethod.ParamMap) {\n            // 单个操作\n            String paramKey = \"param1\";\n            String paramEtKey = \"et\";\n            if (((Map) parameter).containsKey(paramKey)) {\n                clazz = ((Map) parameter).get(paramKey);\n            } else if (((Map) parameter).containsKey(paramEtKey)) {\n                clazz = ((Map) parameter).get(paramEtKey);\n            }\n        } else {\n            // 批量操作\n            clazz = parameter;\n        }\n        setParameter(sqlCommandType, clazz, currentUserId);\n\n        return invocation.proceed();\n    }\n\n    /**\n     * 设置参数\n     *\n     * @param sqlCommandType SQL 语句类型\n     * @param clazz          参数对象\n     * @param operateUserId  操作人用户 id\n     */\n    private static void setParameter(SqlCommandType sqlCommandType, Object clazz, Long operateUserId) {\n        if (SqlCommandType.INSERT.equals(sqlCommandType)) {\n            // 插入\n            // 设置主键\n            setParameterIdValue(clazz);\n            // 创建时间\n            setParameterValue(clazz, FieldConstant.GMT_CREATE, new Date());\n            // 创建人用户 id\n            setParameterValue(clazz, FieldConstant.CREATE_USER_ID, operateUserId);\n        } else if (SqlCommandType.UPDATE.equals(sqlCommandType)) {\n            // 修改\n            // 修改时间\n            setParameterValue(clazz, FieldConstant.GMT_MODIFY, new Date());\n            // 修改人用户 id\n            setParameterValue(clazz, FieldConstant.MODIFY_USER_ID, operateUserId);\n        }\n    }\n\n    /**\n     * 设置主键值\n     *\n     * @param clazz 参数对象类\n     */\n    private static void setParameterIdValue(Object clazz) {\n        Field field = null;\n        try {\n            field = clazz.getClass().getDeclaredField(FieldConstant.ID);\n        } catch (NoSuchFieldException e) {\n            return;\n        }\n        // 设置允许访问\n        field.setAccessible(true);\n        try {\n            Object value = field.get(clazz);\n            // 如果值不为空，则不设置\n            if (value != null) {\n                return;\n            }\n            // 值为空，判断是否为自增长\n            TableId tableId = field.getAnnotation(TableId.class);\n            if (tableId != null && tableId.type() != null && IdType.AUTO.getKey() == tableId.type().getKey()) {\n                return;\n            }\n            field.set(clazz, IdWorker.nextId());\n        } catch (IllegalAccessException e) {\n            return;\n        }\n    }\n\n    /**\n     * 设置参数值\n     *\n     * @param clazz     参数对象类\n     * @param fieldName 属性名称\n     * @param value     属性值\n     */\n    private static void setParameterValue(Object clazz, String fieldName, Object value) {\n        Field field = null;\n        try {\n            field = clazz.getClass().getDeclaredField(fieldName);\n        } catch (NoSuchFieldException e) {\n            return;\n        }\n        // 设置允许访问\n        field.setAccessible(true);\n        try {\n            field.set(clazz, value);\n        } catch (IllegalAccessException e) {\n            return;\n        }\n    }\n}\n"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-framework/src/main/java/com/geshanzsq/common/framework/mybatis/plugin/query/LambdaQueryWrapperPlus.java",
    "content": "package com.geshanzsq.common.framework.mybatis.plugin.query;\n\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.toolkit.support.SFunction;\nimport com.geshanzsq.common.core.util.string.StrUtils;\nimport org.springframework.util.CollectionUtils;\n\nimport java.util.Collection;\n\n/**\n * 扩展 LambdaQueryWrapper 类\n * 添加 xxxIf 方法，当值不存在的时候，不要拼接条件\n *\n * @author geshanzsq\n * @date 2022/3/27\n */\npublic class LambdaQueryWrapperPlus<T> extends LambdaQueryWrapper<T> {\n\n    /**\n     * 模糊查询\n     */\n    public LambdaQueryWrapperPlus<T> likeIf(SFunction<T, ?> column, String value) {\n        return StrUtils.isBlank(value) ? this : like(column, value);\n    }\n\n    @Override\n    public LambdaQueryWrapperPlus<T> like(SFunction<T, ?> column, Object value) {\n        return (LambdaQueryWrapperPlus) super.like(column, value);\n    }\n\n    /**\n     * 相等\n     */\n    public LambdaQueryWrapperPlus<T> eqIf(SFunction<T, ?> column, Object value) {\n        return StrUtils.isNullBlank(value) ? this : eq(column, value);\n    }\n\n    @Override\n    public LambdaQueryWrapperPlus<T> eq(SFunction<T, ?> column, Object value) {\n        return (LambdaQueryWrapperPlus) super.eq(column, value);\n    }\n\n    /**\n     * 包含\n     */\n    public LambdaQueryWrapperPlus<T> inIf(SFunction<T, ?> column, Collection<?> values) {\n        return CollectionUtils.isEmpty(values) ? this : in(column, values);\n    }\n\n    @Override\n    public LambdaQueryWrapperPlus<T> in(SFunction<T, ?> column, Collection<?> values) {\n        return (LambdaQueryWrapperPlus) super.in(column, values);\n    }\n\n    /**\n     * 不包含\n     */\n    public LambdaQueryWrapperPlus<T> notInIf(SFunction<T, ?> column, Collection<?> values) {\n        return CollectionUtils.isEmpty(values) ? this : notIn(column, values);\n    }\n\n    @Override\n    public LambdaQueryWrapperPlus<T> notIn(SFunction<T, ?> column, Collection<?> values) {\n        return (LambdaQueryWrapperPlus) super.notIn(column, values);\n    }\n\n    /**\n     * 小于\n     */\n    public LambdaQueryWrapperPlus<T> ltIf(SFunction<T, ?> column, Object value) {\n        return StrUtils.isNullBlank(value) ? this : lt(column, value);\n    }\n\n    @Override\n    public LambdaQueryWrapperPlus<T> lt(SFunction<T, ?> column, Object value) {\n        return (LambdaQueryWrapperPlus) super.lt(column, value);\n    }\n\n    /**\n     * 小于等于\n     */\n    public LambdaQueryWrapperPlus<T> leIf(SFunction<T, ?> column, Object value) {\n        return StrUtils.isNullBlank(value) ? this : le(column, value);\n    }\n\n    @Override\n    public LambdaQueryWrapperPlus<T> le(SFunction<T, ?> column, Object value) {\n        return (LambdaQueryWrapperPlus) super.le(column, value);\n    }\n\n    /**\n     * 大于\n     */\n    public LambdaQueryWrapperPlus<T> gtIf(SFunction<T, ?> column, Object value) {\n        return StrUtils.isNullBlank(value) ? this : gt(column, value);\n    }\n\n    @Override\n    public LambdaQueryWrapperPlus<T> gt(SFunction<T, ?> column, Object value) {\n        return (LambdaQueryWrapperPlus) super.gt(column, value);\n    }\n\n    /**\n     * 大于等于\n     */\n    public LambdaQueryWrapperPlus<T> geIf(SFunction<T, ?> column, Object value) {\n        return StrUtils.isNullBlank(value) ? this : ge(column, value);\n    }\n\n    @Override\n    public LambdaQueryWrapperPlus<T> ge(SFunction<T, ?> column, Object value) {\n        return (LambdaQueryWrapperPlus) super.ge(column, value);\n    }\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-framework/src/main/java/com/geshanzsq/common/framework/mybatis/plugin/query/QueryWrapperPlus.java",
    "content": "package com.geshanzsq.common.framework.mybatis.plugin.query;\n\nimport com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;\nimport com.baomidou.mybatisplus.core.enums.SqlKeyword;\nimport com.baomidou.mybatisplus.core.metadata.TableInfo;\nimport com.baomidou.mybatisplus.core.metadata.TableInfoHelper;\nimport com.baomidou.mybatisplus.core.toolkit.ClassUtils;\nimport com.baomidou.mybatisplus.core.toolkit.ReflectionKit;\nimport com.baomidou.mybatisplus.core.toolkit.support.SFunction;\nimport com.geshanzsq.common.core.exception.ServiceException;\nimport com.geshanzsq.common.core.util.message.MessageUtils;\nimport com.geshanzsq.common.core.util.string.StrUtils;\nimport com.geshanzsq.common.framework.mybatis.plugin.annotation.Query;\nimport com.geshanzsq.common.framework.mybatis.plugin.constant.FieldConstant;\nimport com.geshanzsq.common.framework.mybatis.plugin.enums.QueryWay;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.lang.reflect.Field;\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * 扩展 QueryWrapper 类，提供更多功能\n *\n * @author geshanzsq\n * @date 2022/11/4\n */\npublic class QueryWrapperPlus<T> extends QueryWrapper<T> {\n\n    private static final Logger log = LoggerFactory.getLogger(QueryWrapperPlus.class);\n\n    /**\n     * 排序字段分割标识\n     */\n    private static final String ORDER_BY_COLUMN_SPLIT = \",\";\n\n    /**\n     * 构建 Wrapper 查询条件\n     *\n     * @param clazz         PO 实体类\n     * @param d             DTO 实体参数对象\n     * @param selectColumns 查询返回的列\n     * @return 查询构造器\n     */\n    public <D> QueryWrapperPlus<T> buildQueryWrapper(Class<T> clazz, D d, SFunction<T, ?>... selectColumns) {\n        QueryWrapperPlus queryWrapper = new QueryWrapperPlus();\n        // 构建指定返回的列\n        queryWrapper.lambda().select(selectColumns);\n        if (d == null) {\n            return queryWrapper;\n        }\n\n        // 获取 T 属性和列名\n        TableInfo tableInfo = TableInfoHelper.getTableInfo(clazz);\n        if (tableInfo == null) {\n            log.error(\"没有找到数据表对应的实体类，当前传入的 Clazz：{}\", clazz);\n            throw new ServiceException(MessageUtils.message(\"system.exception\"));\n        }\n\n        // T 属性和列名对应关系\n        Map<String, String> propertyColumnMap = new HashMap<>(tableInfo.getFieldList().size());\n        tableInfo.getFieldList().forEach(table -> {\n            propertyColumnMap.put(table.getProperty(), table.getColumn());\n        });\n        // 主键不在 fieldList 里，需单独获取\n        if (StrUtils.isNotBlank(tableInfo.getKeyProperty())) {\n            propertyColumnMap.put(tableInfo.getKeyProperty(), tableInfo.getKeyColumn());\n        }\n\n        // 获取 d 属性\n        List<Field> fieldList = ReflectionKit.getFieldList(ClassUtils.getUserClass(d.getClass()));\n        for (Field field : fieldList) {\n            field.setAccessible(true);\n            // 获取查询注解\n            Query query = field.getAnnotation(Query.class);\n\n            // 获取查询属性名称\n            String fieldName = getQueryFieldName(query, field);\n            // 是否忽略查询\n            if (query != null && query.ignore()) {\n                continue;\n            }\n\n            // 校验查询属性是否在 T 实体类\n            verifyQueryFieldNameExist(fieldName, propertyColumnMap);\n\n            // 获取查询值\n            Object value = getFieldValue(field, d);\n\n            // 如果为空并且不需要空查询（默认不需要空查询），则跳过此属性查询\n            if (StrUtils.isNullBlank(value) && (query == null || !query.empty())) {\n                continue;\n            }\n\n            // 获取查询属性对应的列\n            String column = propertyColumnMap.get(fieldName);\n            // 构造查询条件\n            buildQueryCondition(queryWrapper, query, column, value);\n        }\n\n        buildOrderByCondition(queryWrapper, propertyColumnMap, fieldList, d);\n\n        return queryWrapper;\n    }\n\n    /**\n     * 获取查询属性名称\n     */\n    private String getQueryFieldName(Query query, Field field) {\n        return query != null && StrUtils.isNotBlank(query.fieldName()) ? query.fieldName() : field.getName();\n    }\n\n    /**\n     * 校验查询属性是否在 T 实体类\n     */\n    private void verifyQueryFieldNameExist(String fieldName, Map<String, String> propertyColumnMap) {\n        if (!propertyColumnMap.containsKey(fieldName)) {\n            String message = StrUtils.format(\"查询条件字段：{fieldName} 不存在\", fieldName);\n            throw new ServiceException(message);\n        }\n    }\n\n    /**\n     * 获取属性值\n     */\n    private <D> Object getFieldValue(Field field, D d) {\n        if (field == null) {\n            return null;\n        }\n        Object value = null;\n        try {\n            value = field.get(d);\n        } catch (IllegalAccessException e) {\n            e.printStackTrace();\n            throw new ServiceException(MessageUtils.message(\"system.exception\"));\n        }\n        return value;\n    }\n\n    /**\n     * 构建查询条件\n     *\n     * @param queryWrapper 查询构造器\n     * @param query 查询注解\n     * @param column 列名称\n     * @param value 值\n     */\n    private void buildQueryCondition(QueryWrapperPlus queryWrapper, Query query, String column, Object value) {\n        // 默认为相等\n        if (query == null || query.value() == null) {\n            queryWrapper.eq(column, value);\n            return;\n        }\n        // 查询方式\n        QueryWay queryWay = query.value();\n        switch (queryWay) {\n            case EQ: {\n                queryWrapper.eq(column, value);\n                break;\n            }\n            case NE: {\n                queryWrapper.ne(column, value);\n                break;\n            }\n            case GT: {\n                queryWrapper.gt(column, value);\n                break;\n            }\n            case GE: {\n                queryWrapper.ge(column, value);\n                break;\n            }\n            case LT: {\n                queryWrapper.lt(column, value);\n                break;\n            }\n            case LE: {\n                queryWrapper.le(column, value);\n                break;\n            }\n            case LIKE: {\n                queryWrapper.like(column, value);\n                break;\n            }\n            case NOT_LIKE: {\n                queryWrapper.notLike(column, value);\n                break;\n            }\n            case LIKE_LEFT: {\n                queryWrapper.likeLeft(column, value);\n                break;\n            }\n            case LIKE_RIGHT: {\n                queryWrapper.likeRight(column, value);\n                break;\n            }\n            case IN: {\n                if (value instanceof Collection) {\n                    Collection coll = (Collection) value;\n                    queryWrapper.in(column, coll);\n                } else {\n                    queryWrapper.in(column, value);\n                }\n                break;\n            }\n            case NOT_IN: {\n                if (value instanceof Collection) {\n                    Collection coll = (Collection) value;\n                    queryWrapper.notIn(column, coll);\n                } else {\n                    queryWrapper.notIn(column, value);\n                }\n                break;\n            }\n            default: {\n                queryWrapper.eq(column, value);\n            }\n        }\n    }\n\n    /**\n     * 构造排序\n     *\n     * @param queryWrapper 查询构造器\n     * @param propertyColumnMap T 属性和列名\n     * @param d 参数\n     * @param fieldList d 属性列表\n     */\n    private <D> void buildOrderByCondition(QueryWrapperPlus queryWrapper, Map<String, String> propertyColumnMap,\n                                           List<Field> fieldList, D d) {\n        // 排序列\n        Field orderColumnField = fieldList.stream().filter(f -> FieldConstant.ORDER_COLUMN.equals(f.getName())).findFirst().orElse(null);\n        // 如果排序字段为空，或者不是 String 类型，不进行排序\n        Object orderColumnObj = getFieldValue(orderColumnField, d);\n        if (!(orderColumnObj instanceof String) || StrUtils.isNullBlank(orderColumnObj)) {\n            return;\n        }\n\n        // 排序类型\n        Field orderTypeField = fieldList.stream().filter(f -> FieldConstant.ORDER_TYPE.equals(f.getName())).findFirst().orElse(null);\n        Object orderTypeObj = getFieldValue(orderTypeField, d);\n\n        String[] orderColumns = orderColumnObj.toString().split(ORDER_BY_COLUMN_SPLIT);\n        String[] orderTypes = orderTypeObj == null ? new String[] {} : orderTypeObj.toString().split(ORDER_BY_COLUMN_SPLIT);\n\n        // 构造排序\n        for (int i = 0; i < orderColumns.length; i++) {\n            String orderColumn = orderColumns[i];\n            if (StrUtils.isBlank(orderColumn)) {\n                continue;\n            }\n            // 校验排序字段是否存在\n            String column = propertyColumnMap.get(orderColumn);\n            if (StrUtils.isBlank(column)) {\n                String message = StrUtils.format(\"排序字段：{}，不存在\", orderColumn);\n                throw new ServiceException(message);\n            }\n\n            // 是否升序排序\n            boolean isAsc = i < orderTypes.length ? SqlKeyword.ASC.getSqlSegment().equalsIgnoreCase(orderTypes[i]) : true;\n            queryWrapper.orderBy(true, isAsc, column);\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-framework/src/main/java/com/geshanzsq/common/framework/mybatis/property/PageProperty.java",
    "content": "package com.geshanzsq.common.framework.mybatis.property;\n\nimport lombok.Getter;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.context.annotation.Configuration;\n\n/**\n * 分页配置\n *\n * @author geshanzsq\n * @date 2022/3/27\n */\n@Getter\n@Configuration\npublic class PageProperty {\n\n    /**\n     * 默认分页记录数\n     */\n    public static Long defaultPageSize;\n\n    /**\n     * 最大分页记录数\n     */\n    public static Long maxPageSize;\n\n    @Value(\"${page.default-page-size}\")\n    public void setDefaultPageSize(Long defaultPageSize) {\n        PageProperty.defaultPageSize = defaultPageSize;\n    }\n\n    @Value(\"${page.max-page-size}\")\n    public void setMaxPageSize(Long maxPageSize) {\n        PageProperty.maxPageSize = maxPageSize;\n    }\n}\n"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-framework/src/main/java/com/geshanzsq/common/framework/mybatis/util/MybatisUtils.java",
    "content": "package com.geshanzsq.common.framework.mybatis.util;\n\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport com.geshanzsq.common.framework.mybatis.page.dto.PageDTO;\nimport com.geshanzsq.common.framework.mybatis.property.PageProperty;\n\n/**\n * MyBatis 工具类\n *\n * @author geshanzsq\n * @date 2022/6/26\n */\npublic class MybatisUtils {\n\n    /**\n     * 构建分页\n     */\n    public static <T> Page<T> buildPage(PageDTO pageDTO) {\n        Long pageNum = null;\n        Long pageSize = null;\n        if (pageDTO != null) {\n            pageNum = pageDTO.getPageNum();\n            pageSize = pageDTO.getPageSize();\n        }\n        return buildPage(pageNum, pageSize);\n    }\n\n    /**\n     * 构建分页\n     */\n    public static <T> Page<T> buildPage(Long pageNum, Long pageSize) {\n        // 如果为空，则设置默认值\n        if (pageNum == null) {\n            pageNum = 1L;\n        }\n        if (pageSize == null) {\n            pageSize = PageProperty.defaultPageSize;\n        }\n\n        // 如果超过最大分页数，则设置为最大分页数\n        if (pageSize > PageProperty.maxPageSize) {\n            pageSize = PageProperty.maxPageSize;\n        }\n        Page<T> page = new Page<>(pageNum, pageSize);\n        return page;\n    }\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-framework/src/main/java/com/geshanzsq/common/framework/thread/config/ThreadPoolConfig.java",
    "content": "package com.geshanzsq.common.framework.thread.config;\n\nimport com.geshanzsq.common.core.util.thread.Threads;\nimport com.geshanzsq.common.framework.thread.property.ThreadPoolProperty;\nimport org.apache.commons.lang3.concurrent.BasicThreadFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;\n\nimport java.util.concurrent.ScheduledExecutorService;\nimport java.util.concurrent.ScheduledThreadPoolExecutor;\nimport java.util.concurrent.ThreadPoolExecutor;\n\n/**\n * 线程配置\n *\n * @author geshanzsq\n * @date 2022/7/4\n */\n@Configuration\npublic class ThreadPoolConfig {\n\n    @Autowired\n    private ThreadPoolProperty threadPoolProperty;\n\n\n    /**\n     * 线程池任务配置\n     */\n    @Bean(name = \"threadPoolTaskExecutor\")\n    public ThreadPoolTaskExecutor threadPoolTaskExecutor() {\n        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();\n        executor.setMaxPoolSize(threadPoolProperty.getMaxPoolSize());\n        executor.setCorePoolSize(threadPoolProperty.getCorePoolSize());\n        executor.setQueueCapacity(threadPoolProperty.getQueueCapacity());\n        executor.setKeepAliveSeconds(threadPoolProperty.getKeepAliveSeconds());\n        // 线程池对拒绝任务(无线程可用)的处理策略\n        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());\n        return executor;\n    }\n\n    /**\n     * 执行周期性或定时任务\n     */\n    @Bean(name = \"scheduledExecutorService\")\n    protected ScheduledExecutorService scheduledExecutorService() {\n        return new ScheduledThreadPoolExecutor(threadPoolProperty.getCorePoolSize(),\n                new BasicThreadFactory.Builder().namingPattern(\"schedule-pool-%d\").daemon(true).build(),\n                new ThreadPoolExecutor.CallerRunsPolicy()) {\n            @Override\n            protected void afterExecute(Runnable r, Throwable t) {\n                super.afterExecute(r, t);\n                Threads.printException(r, t);\n            }\n        };\n    }\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-framework/src/main/java/com/geshanzsq/common/framework/thread/property/ThreadPoolProperty.java",
    "content": "package com.geshanzsq.common.framework.thread.property;\n\nimport lombok.Data;\nimport org.springframework.boot.context.properties.ConfigurationProperties;\nimport org.springframework.context.annotation.Configuration;\n\n/**\n * 线程池相关配置\n *\n * @author geshanzsq\n * @date 2022/7/4\n */\n@Data\n@Configuration\n@ConfigurationProperties(prefix = \"thread\")\npublic class ThreadPoolProperty {\n\n    /**\n     * 核心线程池大小\n     */\n    private int corePoolSize = 50;\n\n    /**\n     * 最大可创建的线程数\n     */\n    private int maxPoolSize = 200;\n\n    /**\n     * 队列最大长度\n     */\n    private int queueCapacity = 1000;\n\n    /**\n     * 线程池维护线程所允许的空闲时间\n     */\n    private int keepAliveSeconds = 300;\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-framework/src/main/java/com/geshanzsq/common/framework/web/controller/BaseController.java",
    "content": "package com.geshanzsq.common.framework.web.controller;\n\n/**\n * Web 层通用数据处理\n *\n * @author geshanzsq\n * @date 2022/3/19\n */\npublic class BaseController {\n\n\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-framework/src/main/java/com/geshanzsq/common/framework/web/mapper/BaseMapperPlus.java",
    "content": "package com.geshanzsq.common.framework.web.mapper;\n\nimport com.baomidou.mybatisplus.core.conditions.Wrapper;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport com.baomidou.mybatisplus.core.toolkit.ReflectionKit;\nimport com.baomidou.mybatisplus.core.toolkit.support.SFunction;\nimport com.geshanzsq.common.framework.mybatis.page.dto.PageDTO;\nimport com.geshanzsq.common.framework.mybatis.page.vo.PageVO;\nimport com.geshanzsq.common.framework.mybatis.plugin.query.QueryWrapperPlus;\nimport com.geshanzsq.common.framework.mybatis.util.MybatisUtils;\nimport org.apache.ibatis.annotations.Param;\n\nimport java.util.List;\n\n/**\n * 在 MyBatis Plus 的 BaseMapper 的基础上拓展，提供更多功能\n *\n * @author geshanzsq\n * @date 2022/6/26\n */\npublic interface BaseMapperPlus<T> extends BaseMapper<T> {\n\n    /**\n     * 查询分页\n     */\n    default PageVO<T> selectPage(PageDTO pageDTO, @Param(\"ew\") Wrapper<T> queryWrapper) {\n        // MyBatis Plus 分页查询\n        IPage<T> myBatisPage = MybatisUtils.buildPage(pageDTO);\n        selectPage(myBatisPage, queryWrapper);\n        // 转换返回值\n        return new PageVO<>(myBatisPage.getRecords(), myBatisPage.getTotal());\n    }\n\n    /**\n     * 查询分页\n     *\n     * @param d             实体类参数对接\n     * @param selectColumns 查询返回的列\n     */\n    default <D> PageVO<T> selectPage(D d, SFunction<T, ?>... selectColumns) {\n        // 构造分页，不能强制转 pageDTO，否则如果没有继承的话，会报错\n        PageDTO pageDTO = d instanceof PageDTO ? (PageDTO) d : null;\n        IPage<T> myBatisPage = MybatisUtils.buildPage(pageDTO);\n        // 查询数据\n        selectPage(myBatisPage, buildQueryWrapper(d, selectColumns));\n        // 转换返回值\n        return new PageVO<>(myBatisPage.getRecords(), myBatisPage.getTotal());\n    }\n\n    /**\n     * 查询列表\n     *\n     * @param d             实体类参数对接\n     * @param selectColumns 查询返回的列\n     */\n    default <D> List<T> selectList(D d, SFunction<T, ?>... selectColumns) {\n        return selectList(buildQueryWrapper(d, selectColumns));\n    }\n\n    /**\n     * 查询单条\n     *\n     * @param d 实体类参数对接\n     */\n    default <D> T selectOne(D d) {\n        return selectOne(buildQueryWrapper(d));\n    }\n\n    /**\n     * 查询总记录数\n     *\n     * @param d 实体类参数对接\n     */\n    default <D> Long selectCount(D d) {\n        return selectCount(buildQueryWrapper(d));\n    }\n\n    /**\n     * 构建 Wrapper 查询条件\n     *\n     * @param d             DTO 实体参数对象\n     * @param selectColumns 查询返回的列\n     * @return 查询构造器\n     */\n    default <D> QueryWrapperPlus<T> buildQueryWrapper(D d, SFunction<T, ?>... selectColumns) {\n        Class<T> entityClass = (Class<T>) ReflectionKit.getSuperClassGenericType(getClass(), BaseMapperPlus.class, 0);\n        return new QueryWrapperPlus<T>().buildQueryWrapper(entityClass, d, selectColumns);\n    }\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-framework/src/main/java/com/geshanzsq/common/framework/web/service/BaseService.java",
    "content": "package com.geshanzsq.common.framework.web.service;\n\nimport com.baomidou.mybatisplus.core.toolkit.support.SFunction;\nimport com.baomidou.mybatisplus.extension.service.IService;\nimport com.geshanzsq.common.framework.mybatis.page.vo.PageVO;\nimport com.geshanzsq.common.framework.mybatis.plugin.query.QueryWrapperPlus;\n\nimport java.util.List;\n\n/**\n * 在 MyBatis Plus 的 IService 的基础上拓展，提供更多功能\n *\n * @author geshanzsq\n * @date 2022/8/16\n */\npublic interface BaseService<T> extends IService<T> {\n\n    /**\n     * 查询分页\n     *\n     * @param d             实体类参数对接\n     * @param selectColumns 查询返回的列\n     */\n    <D> PageVO<T> page(D d, SFunction<T, ?>... selectColumns);\n\n    /**\n     * 查询列表\n     *\n     * @param d             实体类参数对接\n     * @param selectColumns 查询返回的列\n     */\n    default <D> List<T> list(D d, SFunction<T, ?>... selectColumns) {\n        return list(buildQueryWrapper(d, selectColumns));\n    }\n\n    /**\n     * 查询单条\n     *\n     * @param d 实体类参数对接\n     */\n    default <D> T getOne(D d) {\n        return getOne(buildQueryWrapper(d));\n    }\n\n    /**\n     * 查询单条\n     *\n     * @param d       实体类参数对接\n     * @param throwEx 有多个结果是否抛出异常\n     */\n    default <D> T getOne(D d, boolean throwEx) {\n        return getOne(buildQueryWrapper( d), throwEx);\n    }\n\n    /**\n     * 查询总记录数\n     *\n     * @param d 实体类参数对接\n     */\n    default <D> Long count(D d) {\n        return count(buildQueryWrapper(d));\n    }\n\n    /**\n     * 构建 Wrapper 查询条件\n     *\n     * @param d             DTO 实体参数对象\n     * @param selectColumns 查询返回的列\n     * @return 查询构造器\n     */\n    default <D> QueryWrapperPlus<T> buildQueryWrapper(D d, SFunction<T, ?>... selectColumns) {\n        return new QueryWrapperPlus<T>().buildQueryWrapper(getEntityClass(), d, selectColumns);\n    }\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-framework/src/main/java/com/geshanzsq/common/framework/web/service/impl/BaseServiceImpl.java",
    "content": "package com.geshanzsq.common.framework.web.service.impl;\n\nimport com.baomidou.mybatisplus.core.toolkit.support.SFunction;\nimport com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;\nimport com.geshanzsq.common.framework.mybatis.page.vo.PageVO;\nimport com.geshanzsq.common.framework.web.mapper.BaseMapperPlus;\nimport com.geshanzsq.common.framework.web.service.BaseService;\nimport org.springframework.beans.factory.annotation.Autowired;\n\n/**\n * 在 MyBatis Plus 的 ServiceImpl 的基础上拓展，后续提供更多功能\n *\n * @author geshanzsq\n * @date 2022/8/16\n */\npublic class BaseServiceImpl<M extends BaseMapperPlus<T>, T> extends ServiceImpl<M, T> implements BaseService<T> {\n\n    @Autowired\n    private M baseMapper;\n\n    /**\n     * 查询分页\n     *\n     * @param d             实体类参数对接\n     * @param selectColumns 查询返回的列\n     */\n    @Override\n    public <D> PageVO<T> page(D d, SFunction<T, ?>... selectColumns) {\n        return baseMapper.selectPage(d, selectColumns);\n    }\n}\n"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-log/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>geshanzsq-nav-common</artifactId>\n        <groupId>com.geshanzsq</groupId>\n        <version>2.0.0</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>geshanzsq-nav-common-log</artifactId>\n    <name>geshanzsq-nav-common-log</name>\n    <description>日志模块</description>\n    <version>2.0.0</version>\n\n    <dependencies>\n        <!-- 框架模块 -->\n        <dependency>\n            <groupId>com.geshanzsq</groupId>\n            <artifactId>geshanzsq-nav-common-framework</artifactId>\n        </dependency>\n\n        <!-- 安全框架模块 -->\n        <dependency>\n            <groupId>com.geshanzsq</groupId>\n            <artifactId>geshanzsq-nav-common-security</artifactId>\n        </dependency>\n\n        <!-- aop 切面 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-aop</artifactId>\n        </dependency>\n\n        <!-- 解析客户端操作系统、浏览器等 -->\n        <dependency>\n            <groupId>eu.bitwalker</groupId>\n            <artifactId>UserAgentUtils</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-log/src/main/java/com/geshanzsq/common/log/annotation/Log.java",
    "content": "package com.geshanzsq.common.log.annotation;\n\nimport com.geshanzsq.common.log.enums.BusinessType;\nimport com.geshanzsq.common.log.enums.OperateType;\n\nimport java.lang.annotation.*;\n\n/**\n * 自定义日志注解\n *\n * @author geshanzsq\n * @date 2022/7/4\n */\n@Target(ElementType.METHOD)\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\npublic @interface Log {\n\n    /**\n     * 模块名称\n     */\n    String moduleName() default \"\";\n\n    /**\n     * 业务类型\n     */\n    BusinessType businessType() default BusinessType.OTHER;\n\n    /**\n     * 操作类型\n     */\n    OperateType operateType() default OperateType.ADMIN;\n\n    /**\n     * 是否保存请求数据\n     */\n    boolean isSaveRequestData() default true;\n\n    /**\n     * 是否保存响应数据\n     */\n    boolean isSaveResponseData() default true;\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-log/src/main/java/com/geshanzsq/common/log/aspect/LogAspect.java",
    "content": "package com.geshanzsq.common.log.aspect;\n\nimport com.alibaba.fastjson.JSON;\nimport com.geshanzsq.common.core.util.ip.IpUtils;\nimport com.geshanzsq.common.core.util.servlet.ServletUtils;\nimport com.geshanzsq.common.framework.manager.AsyncManager;\nimport com.geshanzsq.common.log.annotation.Log;\nimport com.geshanzsq.common.log.dto.LogDTO;\nimport com.geshanzsq.common.log.enums.LogStatus;\nimport com.geshanzsq.common.log.factory.LogAsyncFactory;\nimport com.geshanzsq.framework.security.util.SecurityUtils;\nimport org.aspectj.lang.JoinPoint;\nimport org.aspectj.lang.annotation.AfterReturning;\nimport org.aspectj.lang.annotation.AfterThrowing;\nimport org.aspectj.lang.annotation.Aspect;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.stereotype.Component;\nimport org.springframework.validation.BindingResult;\nimport org.springframework.web.multipart.MultipartFile;\nimport org.springframework.web.servlet.HandlerMapping;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.util.Collection;\nimport java.util.Date;\nimport java.util.Map;\n\n/**\n * 日志切面\n *\n * @author geshanzsq\n * @date 2022/7/4\n */\n@Aspect\n@Component\npublic class LogAspect {\n\n    private final Logger log = LoggerFactory.getLogger(LogAspect.class);\n\n    /**\n     * 请求完成后处理\n     *\n     * @param joinPoint     切点\n     * @param logAnnotation 日志\n     * @param returnResult  返回结果\n     */\n    @AfterReturning(pointcut = \"@annotation(logAnnotation)\", returning = \"returnResult\")\n    public void afterReturning(JoinPoint joinPoint, Log logAnnotation, Object returnResult) {\n        dealLog(joinPoint, logAnnotation, null, returnResult);\n    }\n\n    /**\n     * 异常之后执行\n     *\n     * @param joinPoint     切点\n     * @param logAnnotation 日志\n     * @param exception     异常\n     */\n    @AfterThrowing(pointcut = \"@annotation(logAnnotation)\", throwing = \"exception\")\n    public void afterThrowing(JoinPoint joinPoint, Log logAnnotation, Exception exception) {\n        dealLog(joinPoint, logAnnotation, exception, null);\n    }\n\n    /**\n     * 处理日志\n     *\n     * @param joinPoint     切点\n     * @param logAnnotation 日志\n     * @param exception     异常\n     * @param returnResult  返回结果\n     */\n    private void dealLog(JoinPoint joinPoint, Log logAnnotation, Exception exception, Object returnResult) {\n        try {\n            LogDTO logDTO = new LogDTO();\n            // 操作时间\n            logDTO.setGmtOperate(new Date());\n\n            try {\n                // 当前用户\n                logDTO.setUserId(SecurityUtils.getUserId());\n            } catch (Exception e) {\n            }\n\n            HttpServletRequest request = ServletUtils.getRequest();\n\n            // ip 地址\n            String ip = IpUtils.getIpAddr(request);\n            logDTO.setIpAddress(ip);\n            logDTO.setRequestUrl(request.getRequestURI());\n\n            // 状态\n            if (exception != null) {\n                logDTO.setStatus(LogStatus.ERROR.code);\n                logDTO.setErrorMessage(exception.getMessage());\n            } else {\n                logDTO.setStatus(LogStatus.SUCCESS.code);\n            }\n\n            // 设置方法名称\n            String className = joinPoint.getTarget().getClass().getName();\n            String methodName = joinPoint.getSignature().getName();\n            logDTO.setClassMethod(className + \".\" + methodName + \"()\");\n\n            // 设置请求方式\n            logDTO.setRequestMethod(ServletUtils.getRequest().getMethod());\n\n            // 处理设置注解上的参数\n            dealAnnotationParam(joinPoint, logAnnotation, logDTO, returnResult);\n\n            // 异步推送到消息中心\n            AsyncManager.me().execute(LogAsyncFactory.addLog(logDTO));\n        } catch (Exception e) {\n            log.error(\"操作日志记录前置通知异常，异常信息：{}\", e.getMessage());\n            e.printStackTrace();\n        }\n\n\n    }\n\n    /**\n     * 处理注解参数\n     *\n     * @param joinPoint     切点\n     * @param logAnnotation 日志\n     * @param logDTO        日志记录\n     * @param returnResult  返回结果\n     */\n    private void dealAnnotationParam(JoinPoint joinPoint, Log logAnnotation, LogDTO logDTO, Object returnResult) {\n        // 设置业务类型\n        logDTO.setBusinessType(logAnnotation.businessType().code);\n        // 设置模块名称\n        logDTO.setModuleName(logAnnotation.moduleName());\n        // 设置操作类型\n        logDTO.setOperateType(logAnnotation.operateType().code);\n        // 是否需要保存请求数据\n        if (logAnnotation.isSaveRequestData()) {\n            // 设置请求参数数据\n            setRequestData(joinPoint, logDTO);\n        }\n        // 是否需要保存响应数据，参数和值\n        if (logAnnotation.isSaveResponseData() && returnResult != null) {\n            logDTO.setReturnResult(JSON.toJSONString(returnResult));\n        }\n    }\n\n    /**\n     * 设置请求参数数据\n     */\n    private void setRequestData(JoinPoint joinPoint, LogDTO logDTO) {\n        String requestMethod = logDTO.getRequestMethod();\n        if (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod)) {\n            String params = argsArrayToString(joinPoint.getArgs());\n            logDTO.setRequestParam(params);\n        } else {\n            Map<?, ?> paramsMap = (Map<?, ?>) ServletUtils.getRequest().getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);\n            logDTO.setRequestParam(paramsMap.toString());\n        }\n    }\n\n    /**\n     * 数组参数转为字符串\n     */\n    private String argsArrayToString(Object[] paramsArray) {\n        if (paramsArray == null) {\n            return \"\";\n        }\n        StringBuilder params = new StringBuilder();\n        for (Object param : paramsArray) {\n            if (param != null && !isFilterObject(param)) {\n                try {\n                    Object jsonObj = JSON.toJSON(param);\n                    params.append(jsonObj.toString());\n                } catch (Exception e) {\n                }\n            }\n        }\n        return params.toString().trim();\n    }\n\n\n    /**\n     * 判断是否需要过滤的对象。\n     *\n     * @param object 对象信息。\n     * @return 如果是需要过滤的对象，则返回true；否则返回false。\n     */\n    @SuppressWarnings(\"rawtypes\")\n    public boolean isFilterObject(final Object object) {\n        Class<?> clazz = object.getClass();\n        if (clazz.isArray()) {\n            return clazz.getComponentType().isAssignableFrom(MultipartFile.class);\n        } else if (Collection.class.isAssignableFrom(clazz)) {\n            Collection collection = (Collection) object;\n            for (Object value : collection) {\n                return value instanceof MultipartFile;\n            }\n        } else if (Map.class.isAssignableFrom(clazz)) {\n            Map map = (Map) object;\n            for (Object value : map.entrySet()) {\n                Map.Entry entry = (Map.Entry) value;\n                return entry.getValue() instanceof MultipartFile;\n            }\n        }\n        return object instanceof MultipartFile || object instanceof HttpServletRequest || object instanceof HttpServletResponse\n                || object instanceof BindingResult;\n    }\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-log/src/main/java/com/geshanzsq/common/log/constant/LogConstant.java",
    "content": "package com.geshanzsq.common.log.constant;\n\n/**\n * 日志常量\n *\n * @author geshanzsq\n * @date 2022/7/5\n */\npublic class LogConstant {\n\n    /**\n     * 日志消息中心主题\n     */\n    public final static String LOG_MQ_TOPIC = \"logMqTopic\";\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-log/src/main/java/com/geshanzsq/common/log/dto/LogDTO.java",
    "content": "package com.geshanzsq.common.log.dto;\n\nimport lombok.Data;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\n/**\n * 日志新增\n *\n * @author geshanzsq\n * @date 2022/7/4\n */\n@Data\npublic class LogDTO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    /**\n     * 模块名称\n     */\n    private String moduleName;\n\n    /**\n     * 业务类型（1 其它，2 新增，3 修改，4 删除）\n     */\n    private Integer businessType;\n\n    /**\n     * 操作类型（1 其它，2 后台用户，3 手机端用户，4 博客用户）\n     */\n    private Integer operateType;\n\n    /**\n     * 操作用户 id\n     */\n    private Long userId;\n\n    /**\n     * 请求方式\n     */\n    private String requestMethod;\n\n    /**\n     * 类方法\n     */\n    private String classMethod;\n\n    /**\n     * 请求地址\n     */\n    private String requestUrl;\n\n    /**\n     * 操作 ip 地址\n     */\n    private String ipAddress;\n\n    /**\n     * 操作位置\n     */\n    private String operateLocation;\n\n    /**\n     * 请求参数\n     */\n    private String requestParam;\n\n    /**\n     * 返回结果\n     */\n    private String returnResult;\n\n    /**\n     * 状态（1 成功，2 异常）\n     */\n    private Integer status;\n\n    /**\n     * 操作时间\n     */\n    private Date gmtOperate;\n\n    /**\n     * 错误消息\n     */\n    private String errorMessage;\n\n    /**\n     * 浏览器类型\n     */\n    private String browserType;\n\n    /**\n     * 操作系统\n     */\n    private String operateSystem;\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-log/src/main/java/com/geshanzsq/common/log/enums/BusinessType.java",
    "content": "package com.geshanzsq.common.log.enums;\n\n/**\n * 业务类型\n *\n * @author geshanzsq\n * @date 2022/7/4\n */\npublic enum BusinessType {\n\n    /**\n     * 其他\n     */\n    OTHER(1),\n\n    /**\n     * 新增\n     */\n    ADD(2),\n\n    /**\n     * 修改\n     */\n    UPDATE(3),\n\n    /**\n     * 删除\n     */\n    DELETE(4)\n    ;\n\n    public final Integer code;\n\n    BusinessType(Integer code) {\n        this.code = code;\n    }\n}\n"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-log/src/main/java/com/geshanzsq/common/log/enums/LogStatus.java",
    "content": "package com.geshanzsq.common.log.enums;\n\n/**\n * 日志状态\n *\n * @author geshanzsq\n * @date 2022/7/5\n */\npublic enum LogStatus {\n    /**\n     * 成功\n     */\n    SUCCESS(1),\n\n    /**\n     * 失败\n     */\n    ERROR(2)\n    ;\n\n    public final Integer code;\n\n    LogStatus(Integer code) {\n        this.code = code;\n    }\n}\n"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-log/src/main/java/com/geshanzsq/common/log/enums/OperateType.java",
    "content": "package com.geshanzsq.common.log.enums;\n\n/**\n * 操作类型\n *\n * @author geshanzsq\n * @date 2022/7/4\n */\npublic enum OperateType {\n    /**\n     * 其他\n     */\n    OTHER(1),\n\n    /**\n     * 后台用户\n     */\n    ADMIN(2),\n\n    /**\n     * 手机端用户\n     */\n    MOBILE(3),\n\n    /**\n     * 导航用户\n     */\n    NAV(4)\n    ;\n\n\n    public final Integer code;\n\n    OperateType(Integer code) {\n        this.code = code;\n    }\n}\n"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-log/src/main/java/com/geshanzsq/common/log/factory/LogAsyncFactory.java",
    "content": "package com.geshanzsq.common.log.factory;\n\nimport com.geshanzsq.common.core.util.ip.AddressUtils;\nimport com.geshanzsq.common.core.util.servlet.ServletUtils;\nimport com.geshanzsq.common.core.util.spring.SpringUtils;\nimport com.geshanzsq.common.log.dto.LogDTO;\nimport com.geshanzsq.common.log.service.LogMqService;\nimport eu.bitwalker.useragentutils.UserAgent;\n\nimport java.util.TimerTask;\n\n/**\n * 日志异步工厂\n *\n * @author geshanzsq\n * @date 2022/7/5\n */\npublic class LogAsyncFactory {\n\n    /**\n     * 添加日志到 Redis 消息队列，以免大量操作影响数据库性能\n     * @param logDTO\n     * @return\n     */\n    public static TimerTask addLog(LogDTO logDTO) {\n        // 获取客户端信息\n        UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader(\"User-Agent\"));\n        return new TimerTask() {\n            @Override\n            public void run() {\n                // 获取客户端操作系统\n                String operateSystem = userAgent.getOperatingSystem().getName();\n                // 获取客户端浏览器\n                String browser = userAgent.getBrowser().getName();\n                logDTO.setBrowserType(browser);\n                logDTO.setOperateSystem(operateSystem);\n                // 获取远程查询操作地点\n                logDTO.setOperateLocation(AddressUtils.getRealAddressByIP(logDTO.getIpAddress()));\n                // 添加到消息队列\n                SpringUtils.getBean(LogMqService.class).addLogToMq(logDTO);\n            }\n        };\n    }\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-log/src/main/java/com/geshanzsq/common/log/service/LogMqService.java",
    "content": "package com.geshanzsq.common.log.service;\n\nimport com.geshanzsq.common.log.dto.LogDTO;\n\n/**\n * 日志消息中心\n *\n * @author geshanzsq\n * @date 2022/7/5\n */\npublic interface LogMqService {\n\n    /**\n     * 添加日志消息中心\n     */\n    void addLogToMq(LogDTO logDTO);\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-log/src/main/java/com/geshanzsq/common/log/service/impl/LogMqServiceImpl.java",
    "content": "package com.geshanzsq.common.log.service.impl;\n\nimport com.alibaba.fastjson.JSONObject;\nimport com.geshanzsq.common.log.constant.LogConstant;\nimport com.geshanzsq.common.log.dto.LogDTO;\nimport com.geshanzsq.common.log.service.LogMqService;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.data.redis.core.StringRedisTemplate;\nimport org.springframework.stereotype.Service;\n\n/**\n * 日志消息中心\n *\n * @author geshanzsq\n * @date 2022/7/5\n */\n@Service\npublic class LogMqServiceImpl implements LogMqService {\n\n    @Autowired\n    private StringRedisTemplate stringRedisTemplate;\n\n    /**\n     * 添加日志消息中心\n     */\n    @Override\n    public void addLogToMq(LogDTO logDTO) {\n        stringRedisTemplate.convertAndSend(LogConstant.LOG_MQ_TOPIC, JSONObject.toJSONString(logDTO));\n    }\n}\n"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-rate-limiter/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>geshanzsq-nav-common</artifactId>\n        <groupId>com.geshanzsq</groupId>\n        <version>2.0.0</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>geshanzsq-nav-common-rate-limiter</artifactId>\n    <name>geshanzsq-nav-common-rate-limiter</name>\n    <description>通用限流模块</description>\n    <version>2.0.0</version>\n\n    <dependencies>\n        <!-- aop 切面 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-aop</artifactId>\n        </dependency>\n\n        <!-- 安全框架模块 -->\n        <dependency>\n            <groupId>com.geshanzsq</groupId>\n            <artifactId>geshanzsq-nav-common-security</artifactId>\n        </dependency>\n    </dependencies>\n\n\n</project>"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-rate-limiter/src/main/java/com/geshanzsq/common/rate/limiter/annotation/RateLimiter.java",
    "content": "package com.geshanzsq.common.rate.limiter.annotation;\n\nimport com.geshanzsq.common.rate.limiter.enums.RateLimiterType;\n\nimport java.lang.annotation.*;\n\n/**\n * 限流\n *\n * @author geshanzsq\n * @date 2023/5/2\n */\n@Target(ElementType.METHOD)\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\npublic @interface RateLimiter {\n\n    /**\n     * 限流 key，如果知道了 key，则限流类型为全局\n     */\n    String key() default \"\";\n\n    /**\n     * 限流类型\n     */\n    RateLimiterType type() default RateLimiterType.DEFAULT;\n\n    /**\n     * 限流时间，单位秒\n     */\n    public int time() default 60;\n\n    /**\n     * 限流次数\n     */\n    public int count() default 10;\n\n    /**\n     * 提示语\n     */\n    String hintMessage() default \"操作频繁，请稍后再试\";\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-rate-limiter/src/main/java/com/geshanzsq/common/rate/limiter/aspect/RateLimiterAspect.java",
    "content": "package com.geshanzsq.common.rate.limiter.aspect;\n\nimport com.geshanzsq.common.core.util.ip.IpUtils;\nimport com.geshanzsq.common.core.util.servlet.ServletUtils;\nimport com.geshanzsq.common.core.util.string.StrUtils;\nimport com.geshanzsq.common.rate.limiter.annotation.RateLimiter;\nimport com.geshanzsq.common.rate.limiter.constant.RateLimitConstant;\nimport com.geshanzsq.common.rate.limiter.enums.RateLimiterType;\nimport com.geshanzsq.common.rate.limiter.exception.RateLimiterException;\nimport com.geshanzsq.common.redis.service.RedisService;\nimport com.geshanzsq.framework.security.util.SecurityUtils;\nimport org.aspectj.lang.JoinPoint;\nimport org.aspectj.lang.annotation.Aspect;\nimport org.aspectj.lang.annotation.Before;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\n\n/**\n * 限流\n *\n * @author geshanzsq\n * @date 2023/5/2\n */\n@Component\n@Aspect\npublic class RateLimiterAspect {\n\n    @Autowired\n    private RedisService redisService;\n\n    @Before(\"@annotation(rateLimiter)\")\n    public void doBefore(JoinPoint joinPoint, RateLimiter rateLimiter) {\n        // 获取限流标识\n        String limitKey = getLimitKey(joinPoint, rateLimiter);\n        boolean isLimit = redisService.limit(limitKey, rateLimiter.time(), rateLimiter.count());\n        if (isLimit) {\n            throw new RateLimiterException(rateLimiter.hintMessage());\n        }\n    }\n\n    /**\n     * 获取限流标识\n     */\n    private String getLimitKey(JoinPoint joinPoint, RateLimiter rateLimiter) {\n        if (StrUtils.isNotBlank(rateLimiter.key())) {\n            return RateLimitConstant.CACHE_PREFIX + rateLimiter.key();\n        }\n        // 获取注解作用域的类名和方法名\n        String className = joinPoint.getTarget().getClass().getName();\n        String methodName = joinPoint.getSignature().getName();\n        String limitKey = RateLimitConstant.CACHE_PREFIX + className.replace(\".\", \":\") + methodName;\n        switch (rateLimiter.type()) {\n            case IP: {\n                // ip 地址\n                String ip = IpUtils.getIpAddr(ServletUtils.getRequest());\n                return limitKey + \":\" + ip.split(\",\")[0];\n            }\n            case USER: {\n                return limitKey + \":\" + SecurityUtils.getUserId();\n            }\n            default: {\n                return limitKey;\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-rate-limiter/src/main/java/com/geshanzsq/common/rate/limiter/constant/RateLimitConstant.java",
    "content": "package com.geshanzsq.common.rate.limiter.constant;\n\n/**\n * 限流常量\n *\n * @author geshanzsq\n * @date 2023/5/2\n */\npublic class RateLimitConstant {\n\n    /**\n     * 限流前缀\n     */\n    public final static String CACHE_PREFIX = \"rate:limiter:\";\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-rate-limiter/src/main/java/com/geshanzsq/common/rate/limiter/enums/RateLimiterType.java",
    "content": "package com.geshanzsq.common.rate.limiter.enums;\n\n/**\n * 限流类型\n *\n * @author geshanzsq\n * @date 2023/5/2\n */\npublic enum RateLimiterType {\n\n    /**\n     * 默认策略全局限流\n     */\n    DEFAULT,\n\n    /**\n     * 根据请求 IP 进行限流\n     */\n    IP,\n\n    /**\n     * 根据用户限流\n     */\n    USER\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-rate-limiter/src/main/java/com/geshanzsq/common/rate/limiter/exception/RateLimiterException.java",
    "content": "package com.geshanzsq.common.rate.limiter.exception;\n\n/**\n * 限流异常\n *\n * @author geshanzsq\n * @date 2023/5/2\n */\npublic class RateLimiterException extends RuntimeException {\n\n    /**\n     * 错误信息\n     */\n    private String message;\n\n\n    public RateLimiterException(String message) {\n        super(message);\n        this.message = message;\n    }\n\n    public RateLimiterException(String message, Throwable e) {\n        super(message, e);\n        this.message = message;\n    }\n\n    @Override\n    public String getMessage() {\n        return this.message;\n    }\n\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-redis/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>geshanzsq-nav-common</artifactId>\n        <groupId>com.geshanzsq</groupId>\n        <version>2.0.0</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>geshanzsq-nav-common-redis</artifactId>\n    <name>geshanzsq-nav-common-redis</name>\n    <description>redis 缓存</description>\n    <version>2.0.0</version>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-data-redis</artifactId>\n        </dependency>\n        <!-- 使用 Redis 缓存需要引用 -->\n        <dependency>\n            <groupId>org.apache.commons</groupId>\n            <artifactId>commons-pool2</artifactId>\n        </dependency>\n\n        <!-- 通用核心模块 -->\n        <dependency>\n            <groupId>com.geshanzsq</groupId>\n            <artifactId>geshanzsq-nav-common-core</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-redis/src/main/java/com/geshanzsq/common/redis/config/FastJson2JsonRedisSerializer.java",
    "content": "package com.geshanzsq.common.redis.config;\n\nimport com.alibaba.fastjson.JSON;\nimport com.alibaba.fastjson.parser.ParserConfig;\nimport com.alibaba.fastjson.serializer.SerializerFeature;\nimport com.fasterxml.jackson.databind.JavaType;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.fasterxml.jackson.databind.type.TypeFactory;\nimport org.springframework.data.redis.serializer.RedisSerializer;\nimport org.springframework.data.redis.serializer.SerializationException;\nimport org.springframework.util.Assert;\n\nimport java.nio.charset.Charset;\n\n/**\n * Redis 使用 FastJson 序列化\n *\n * @author geshanzsq\n * @date 2022/3/26\n */\npublic class FastJson2JsonRedisSerializer<T> implements RedisSerializer<T> {\n\n    @SuppressWarnings(\"unused\")\n    private ObjectMapper objectMapper = new ObjectMapper();\n\n    public static final Charset DEFAULT_CHARSET = Charset.forName(\"UTF-8\");\n\n    private Class<T> clazz;\n\n    static {\n        ParserConfig.getGlobalInstance().setAutoTypeSupport(true);\n    }\n\n    public FastJson2JsonRedisSerializer(Class<T> clazz) {\n        super();\n        this.clazz = clazz;\n    }\n\n    @Override\n    public byte[] serialize(T t) throws SerializationException {\n        if (t == null) {\n            return new byte[0];\n        }\n        return JSON.toJSONString(t, SerializerFeature.WriteClassName).getBytes(DEFAULT_CHARSET);\n    }\n\n    @Override\n    public T deserialize(byte[] bytes) throws SerializationException {\n        if (bytes == null || bytes.length <= 0) {\n            return null;\n        }\n        String str = new String(bytes, DEFAULT_CHARSET);\n        return JSON.parseObject(str, clazz);\n    }\n\n    public void setObjectMapper(ObjectMapper objectMapper) {\n        Assert.notNull(objectMapper, \"'objectMapper' must not be null\");\n        this.objectMapper = objectMapper;\n    }\n\n    protected JavaType getJavaType(Class<?> clazz) {\n        return TypeFactory.defaultInstance().constructType(clazz);\n    }\n}\n"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-redis/src/main/java/com/geshanzsq/common/redis/config/RedisConfig.java",
    "content": "package com.geshanzsq.common.redis.config;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\nimport com.fasterxml.jackson.annotation.PropertyAccessor;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;\nimport org.springframework.cache.annotation.CachingConfigurerSupport;\nimport org.springframework.cache.annotation.EnableCaching;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.data.redis.connection.RedisConnectionFactory;\nimport org.springframework.data.redis.core.RedisTemplate;\nimport org.springframework.data.redis.serializer.StringRedisSerializer;\n\n/**\n * Redis 配置\n *\n * @author geshanzsq\n * @date 2022/3/26\n */\n@Configuration\n@EnableCaching\npublic class RedisConfig extends CachingConfigurerSupport {\n\n    /**\n     * 序列化配置\n     */\n    @Bean\n    @SuppressWarnings(value = { \"unchecked\", \"rawtypes\" })\n    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory) {\n        RedisTemplate<Object, Object> template = new RedisTemplate<>();\n        template.setConnectionFactory(connectionFactory);\n\n        FastJson2JsonRedisSerializer serializer = new FastJson2JsonRedisSerializer(Object.class);\n\n        ObjectMapper mapper = new ObjectMapper();\n        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);\n        mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL,\n                JsonTypeInfo.As.PROPERTY);\n        serializer.setObjectMapper(mapper);\n\n        // 使用StringRedisSerializer来序列化和反序列化redis的key值\n        template.setKeySerializer(new StringRedisSerializer());\n        template.setValueSerializer(serializer);\n\n        // Hash的key也采用StringRedisSerializer的序列化方式\n        template.setHashKeySerializer(new StringRedisSerializer());\n        template.setHashValueSerializer(serializer);\n\n        template.afterPropertiesSet();\n        return template;\n    }\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-redis/src/main/java/com/geshanzsq/common/redis/service/RedisService.java",
    "content": "package com.geshanzsq.common.redis.service;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.data.redis.core.RedisTemplate;\nimport org.springframework.data.redis.core.ValueOperations;\nimport org.springframework.data.redis.core.script.DefaultRedisScript;\nimport org.springframework.stereotype.Service;\n\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.concurrent.TimeUnit;\n\n/**\n * redis 工具类\n *\n * @author geshanzsq\n * @date 2022/3/20\n */\n@Service\npublic class RedisService {\n\n    /**\n     * 限流脚本\n     */\n    private final String LIMIT_SCRIPT = \"local key = KEYS[1]\\n\" +\n            \"local time = tonumber(ARGV[1])\\n\" +\n            \"local count = tonumber(ARGV[2])\\n\" +\n            \"local current = redis.call('get', key);\\n\" +\n            \"if current and tonumber(current) > count then\\n\" +\n            \"    return tonumber(current);\\n\" +\n            \"end\\n\" +\n            \"current = redis.call('incr', key)\\n\" +\n            \"if tonumber(current) == 1 then\\n\" +\n            \"    redis.call('expire', key, time)\\n\" +\n            \"end\\n\" +\n            \"return tonumber(current);\";\n\n    @Autowired\n    private RedisTemplate redisTemplate;\n\n    /**\n     * 设置缓存基本对象\n     * @param key 缓存键\n     * @param value 缓存值\n     */\n    public <T> void set(String key, T value) {\n        redisTemplate.opsForValue().set(key, value);\n    }\n\n    /**\n     * 设置缓存基本对象\n     * @param key 缓存键\n     * @param value 缓存值\n     * @param expire 过期时间\n     * @param timeUnit 时间单位\n     * @param <T>\n     */\n    public <T> void set(String key, T value, long expire, TimeUnit timeUnit) {\n        redisTemplate.opsForValue().set(key, value, expire, timeUnit);\n    }\n\n    /**\n     * 设置缓存有效期\n     * @param key 缓存键\n     * @param expire 过期时间\n     */\n    public boolean expire(String key, long expire) {\n        return redisTemplate.expire(key, expire, TimeUnit.SECONDS);\n    }\n\n    /**\n     * 设置缓存有效期\n     * @param key 缓存键\n     * @param expire 过期时间\n     * @param timeUnit 时间单位\n     */\n    public boolean expire(String key, long expire, TimeUnit timeUnit) {\n        return redisTemplate.expire(key, expire, timeUnit);\n    }\n\n    /**\n     * 获取缓存基本对象\n     * @param key 缓存键\n     */\n    public <T> T get(String key) {\n        ValueOperations<String, T> operation = redisTemplate.opsForValue();\n        return operation.get(key);\n    }\n\n    /**\n     * 通配符获取缓存键\n     * @param pattern 通配符缓存键\n     */\n    public Collection<String> keys(String pattern) {\n        return redisTemplate.keys(pattern);\n    }\n\n    /**\n     * 删除缓存对象\n     * @param key 缓存键\n     */\n    public boolean delete(String key) {\n        return redisTemplate.delete(key);\n    }\n\n    /**\n     * 删除缓存对象\n     * @param collection 多个对象\n     */\n    public long delete(Collection collection) {\n        return redisTemplate.delete(collection);\n    }\n\n    /**\n     * 获取缓存有效期\n     * @param key 缓存键\n     */\n    public Long getExpireTime(String key) {\n        return redisTemplate.opsForValue().getOperations().getExpire(key);\n    }\n\n    /**\n     * 限流\n     *\n     * @param key 缓存键\n     * @param time 限流时间，单位秒\n     * @param count 限流次数\n     */\n    public boolean limit(String key, int time, int count) {\n        DefaultRedisScript<Long> limitScript = new DefaultRedisScript<>(LIMIT_SCRIPT, Long.class);\n        // 当前请求次数\n        Long currentRequestCount = (Long) redisTemplate.execute(limitScript, Arrays.asList(key), time, count);\n        return currentRequestCount > count;\n    }\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-security/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>geshanzsq-nav-common</artifactId>\n        <groupId>com.geshanzsq</groupId>\n        <version>2.0.0</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>geshanzsq-nav-common-security</artifactId>\n    <name>geshanzsq-nav-common-security</name>\n    <description>安全模块</description>\n    <version>2.0.0</version>\n\n\n    <dependencies>\n        <!-- Spring Security -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-security</artifactId>\n        </dependency>\n\n        <!-- 通用核心模块 -->\n        <dependency>\n            <groupId>com.geshanzsq</groupId>\n            <artifactId>geshanzsq-nav-common-core</artifactId>\n        </dependency>\n\n        <!-- Redis 缓存模块 -->\n        <dependency>\n            <groupId>com.geshanzsq</groupId>\n            <artifactId>geshanzsq-nav-common-redis</artifactId>\n        </dependency>\n    </dependencies>\n\n\n</project>"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-security/src/main/java/com/geshanzsq/framework/security/constant/SecurityConstant.java",
    "content": "package com.geshanzsq.framework.security.constant;\n\n/**\n * 常量\n *\n * @author geshanzsq\n * @date 2022/3/23\n */\npublic class SecurityConstant {\n\n    /**\n     * 令牌前缀\n     */\n    public static final String TOKEN_PREFIX = \"token:\";\n\n    /**\n     * 刷新权限前缀\n     */\n    public static final String PERMISSION_REFRESH_PREFIX = \"permission:refresh:\";\n\n    /**\n     * 超级管理员角色编码\n     */\n    public static final String SUPER_ADMIN_ROLE_CODE = \"superAdmin\";\n\n    /**\n     * 所有权限标识\n     */\n    public static final String ALL_PERMISSION_CODE = \"*:*:*\";\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-security/src/main/java/com/geshanzsq/framework/security/domain/ApiPermission.java",
    "content": "package com.geshanzsq.framework.security.domain;\n\nimport lombok.Data;\n\n/**\n * 接口权限\n *\n * @author geshanzsq\n * @date 2022/6/12\n */\n@Data\npublic class ApiPermission {\n\n    /**\n     * 接口地址\n     */\n    private String apiUrl;\n\n    /**\n     * 接口请求方式\n     */\n    private String apiMethod;\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-security/src/main/java/com/geshanzsq/framework/security/domain/LoginUserDetail.java",
    "content": "package com.geshanzsq.framework.security.domain;\n\nimport lombok.Data;\nimport org.springframework.security.core.GrantedAuthority;\nimport org.springframework.security.core.userdetails.UserDetails;\n\nimport java.util.*;\n\n/**\n * 登录用户身份\n *\n * @author geshanzsq\n * @date 2022/3/20\n */\n@Data\npublic class LoginUserDetail implements UserDetails {\n\n    /**\n     * 用户 id\n     */\n    private Long userId;\n\n    /**\n     * 用户名\n     */\n    private String username;\n\n    /**\n     * 密码\n     */\n    private String password;\n\n    /**\n     * 用户登录 token\n     */\n    private String token;\n\n    /**\n     * 登录时间\n     */\n    private long loginTime;\n\n    /**\n     * 过期时间\n     */\n    private long expireTime;\n\n    /**\n     * 权限列表\n     */\n    private Set<String> permissionCodes = new HashSet<>();\n\n    /**\n     * 接口权限列表\n     */\n    private List<ApiPermission> apiPermissions = new ArrayList<>();\n\n    /**\n     * 角色编码\n     */\n    private Set<String> roleCodes = new HashSet<>();\n\n    /**\n     * 是否为超级管理员\n     */\n    private boolean hasSuperAdmin;\n\n    @Override\n    public Collection<? extends GrantedAuthority> getAuthorities() {\n        return null;\n    }\n\n    @Override\n    public String getPassword() {\n        return this.password;\n    }\n\n    @Override\n    public String getUsername() {\n        return this.username;\n    }\n\n    @Override\n    public boolean isAccountNonExpired() {\n        return true;\n    }\n\n    @Override\n    public boolean isAccountNonLocked() {\n        return true;\n    }\n\n    @Override\n    public boolean isCredentialsNonExpired() {\n        return true;\n    }\n\n    @Override\n    public boolean isEnabled() {\n        return true;\n    }\n}\n"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-security/src/main/java/com/geshanzsq/framework/security/handler/AccessDeniedExceptionHandler.java",
    "content": "package com.geshanzsq.framework.security.handler;\n\nimport com.geshanzsq.common.core.constant.HttpStatus;\nimport com.geshanzsq.common.core.util.message.MessageUtils;\nimport com.geshanzsq.common.core.web.response.ResponseResult;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.security.access.AccessDeniedException;\nimport org.springframework.web.bind.annotation.ExceptionHandler;\nimport org.springframework.web.bind.annotation.RestControllerAdvice;\n\n/**\n * 无权限异常全局捕获\n *\n * @author geshanzsq\n * @date 2022/5/8\n */\n@RestControllerAdvice\npublic class AccessDeniedExceptionHandler {\n\n    private final static Logger log = LoggerFactory.getLogger(AccessDeniedExceptionHandler.class);\n\n    @ExceptionHandler(AccessDeniedException.class)\n    public ResponseResult handleAuthorizationException(AccessDeniedException e) {\n        log.error(e.getMessage());\n        return ResponseResult.fail(HttpStatus.FORBIDDEN, MessageUtils.message(\"security.forbidden\"));\n    }\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-security/src/main/java/com/geshanzsq/framework/security/handler/AuthenticationEntryPointImpl.java",
    "content": "package com.geshanzsq.framework.security.handler;\n\nimport com.geshanzsq.common.core.constant.HttpStatus;\nimport com.geshanzsq.common.core.util.message.MessageUtils;\nimport com.geshanzsq.common.core.util.servlet.ServletUtils;\nimport com.geshanzsq.common.core.util.string.StrUtils;\nimport com.geshanzsq.common.core.web.response.ResponseResult;\nimport com.geshanzsq.framework.security.service.TokenService;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.core.AuthenticationException;\nimport org.springframework.security.web.AuthenticationEntryPoint;\nimport org.springframework.stereotype.Component;\n\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\n\n/**\n * 认证失败处理，返回未认证\n *\n * @author geshanzsq\n * @date 2022/3/19\n */\n@Component\npublic class AuthenticationEntryPointImpl implements AuthenticationEntryPoint {\n\n\n    @Autowired\n    private TokenService tokenService;\n\n    @Override\n    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException {\n        int code = HttpStatus.UNAUTHORIZED;\n        // 默认未登录\n        String message = MessageUtils.message(\"security.not.login\");\n        String token = tokenService.getToken();\n        // 如果存在令牌，则提示登录已过期\n        if (StrUtils.isNotBlank(token)) {\n            message = MessageUtils.message(\"security.login.expire\");\n        }\n\n        ServletUtils.renderString(ResponseResult.fail(code, message), response);\n    }\n}\n"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-security/src/main/java/com/geshanzsq/framework/security/handler/LogoutSuccessHandlerImpl.java",
    "content": "package com.geshanzsq.framework.security.handler;\n\nimport com.geshanzsq.common.core.util.servlet.ServletUtils;\nimport com.geshanzsq.common.core.web.response.ResponseResult;\nimport com.geshanzsq.framework.security.domain.LoginUserDetail;\nimport com.geshanzsq.framework.security.service.TokenService;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.web.authentication.logout.LogoutSuccessHandler;\nimport org.springframework.stereotype.Component;\n\nimport javax.servlet.ServletException;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\n\n/**\n * 自定义退出处理\n *\n * @author geshanzsq\n * @date 2022/3/23\n */\n@Component\npublic class LogoutSuccessHandlerImpl implements LogoutSuccessHandler {\n\n    @Autowired\n    private TokenService tokenService;\n\n\n    @Override\n    public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {\n        // 获取当前登录用户\n        LoginUserDetail loginUser = tokenService.getLoginUser(request);\n        if (loginUser != null) {\n            // 删除登录用户\n            tokenService.deleteLoginUser(request, loginUser.getUserId());\n        }\n        ServletUtils.renderString(ResponseResult.success(), response);\n    }\n}\n"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-security/src/main/java/com/geshanzsq/framework/security/property/SecurityProperty.java",
    "content": "package com.geshanzsq.framework.security.property;\n\nimport lombok.Data;\nimport org.springframework.boot.context.properties.ConfigurationProperties;\nimport org.springframework.stereotype.Component;\n\n/**\n * 权限配置\n *\n * @author geshanzsq\n * @date 2022/3/20\n */\n@ConfigurationProperties(prefix = \"security\")\n@Component\n@Data\npublic class SecurityProperty {\n\n    /**\n     * 不用登录就能访问的地址\n     */\n    private String[] notLoginUrls;\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-security/src/main/java/com/geshanzsq/framework/security/property/TokenProperty.java",
    "content": "package com.geshanzsq.framework.security.property;\n\nimport lombok.Data;\nimport org.springframework.boot.context.properties.ConfigurationProperties;\nimport org.springframework.stereotype.Component;\n\n/**\n * token 配置\n *\n * @author geshanzsq\n * @date 2022/3/23\n */\n@Component\n@ConfigurationProperties(prefix = \"token\")\n@Data\npublic class TokenProperty {\n\n    /**\n     * 自定义令牌标识\n     */\n    private String header;\n\n    /**\n     * 令牌有效期，默认为秒\n     */\n    private long expireTime;\n\n    /**\n     * 令牌参数\n     */\n    private String tokenParam;\n\n    /**\n     * 令牌前缀\n     */\n    private String tokenPrefix;\n\n    /**\n     * 存入 redis 前缀\n     */\n    private String tokenRedisPrefix;\n\n    /**\n     * 刷新权限 redis 前缀\n     */\n    private String permissionRefreshRedisPrefix;\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-security/src/main/java/com/geshanzsq/framework/security/service/TokenService.java",
    "content": "package com.geshanzsq.framework.security.service;\n\nimport com.geshanzsq.framework.security.domain.LoginUserDetail;\n\nimport javax.servlet.http.HttpServletRequest;\nimport java.util.List;\n\n/**\n * token 验证处理\n *\n * @author geshanzsq\n * @date 2022/3/23\n */\npublic interface TokenService {\n\n    /**\n     * 创建登录令牌\n     * @param loginUser 登录用户信息\n     */\n    String createLoginUser(LoginUserDetail loginUser);\n\n    /**\n     * 获取登录用户信息\n     */\n    LoginUserDetail getLoginUser();\n\n    /**\n     * 获取登录用户信息\n     * @param request\n     */\n    LoginUserDetail getLoginUser(HttpServletRequest request);\n\n    /**\n     * 删除登录用户\n     * @param request\n     * @param userId\n     */\n    boolean deleteLoginUser(HttpServletRequest request, Long userId);\n\n    /**\n     * 设置刷新权限\n     * @param userId 用户 id\n     * @param token 令牌\n     * @param hasRefresh 是否需要刷新\n     */\n    void setRefreshPermission(Long userId, String token, boolean hasRefresh);\n\n    /**\n     * 设置需要刷新权限\n     * @param userId 用户 id\n     */\n    void setNeedRefreshPermission(Long userId);\n\n    /**\n     * 设置需要刷新权限\n     * @param userIds 用户 id 集合\n     */\n    void setNeedRefreshPermission(List<Long> userIds);\n\n    /**\n     * 是否需要刷新权限\n     * @param userId 用户 id\n     * @param token 令牌\n     */\n    boolean hasRefreshPermission(Long userId, String token);\n\n    /**\n     * 设置用户信息到缓存\n     * @param token 令牌\n     * @param loginUser 用户信息\n     * @param expire 过期时间，单位为秒\n     */\n    void setLoginUserCache(String token, LoginUserDetail loginUser, long expire);\n\n    /**\n     * 获取令牌过期时间\n     * @param token 令牌\n     */\n    Long getTokenExpireTime(String token);\n\n    /**\n     * 获取令牌\n     */\n    String getToken();\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-security/src/main/java/com/geshanzsq/framework/security/service/impl/AuthorizationService.java",
    "content": "package com.geshanzsq.framework.security.service.impl;\n\nimport com.geshanzsq.common.core.util.servlet.ServletUtils;\nimport com.geshanzsq.common.core.util.string.StrUtils;\nimport com.geshanzsq.framework.security.domain.ApiPermission;\nimport com.geshanzsq.framework.security.domain.LoginUserDetail;\nimport com.geshanzsq.framework.security.util.SecurityUtils;\nimport org.springframework.stereotype.Service;\nimport org.springframework.util.AntPathMatcher;\nimport org.springframework.util.CollectionUtils;\n\nimport javax.servlet.http.HttpServletRequest;\nimport java.util.List;\n\n/**\n * Geshanzsq 首创，实现自定义权限，auth 取自 Authorization\n *\n * @author geshanzsq\n * @date 2022/3/26\n */\n@Service(\"auth\")\npublic class AuthorizationService {\n\n    private AntPathMatcher antPathMatcher = new AntPathMatcher();\n\n    /**\n     * 验证是否拥有当前请求地址的访问权限\n     */\n    public boolean hasUrl() {\n        // 请求地址\n        HttpServletRequest request = ServletUtils.getRequest();\n        String requestPath = request.getServletPath();\n        // 当前登录用户\n        LoginUserDetail loginUser = SecurityUtils.getLoginUser();\n\n        // 如果是超级管理员，直接返回\n        if (loginUser.isHasSuperAdmin()) {\n            return true;\n        }\n        // 校验请求权限\n        return verifyUrlPermission(requestPath, loginUser.getApiPermissions());\n    }\n\n    /**\n     * 验证是否有权限标识\n     */\n    public boolean hasPerm(String permissionCode) {\n        if (StrUtils.isBlank(permissionCode)) {\n            return false;\n        }\n\n        // 当前登录用户\n        LoginUserDetail loginUser = SecurityUtils.getLoginUser();\n        // 如果是超级管理员，直接返回\n        if (loginUser.isHasSuperAdmin()) {\n            return true;\n        }\n\n        // 验证权限标识\n        for (String targetPermissionCode : loginUser.getPermissionCodes()) {\n            if (permissionCode.equals(targetPermissionCode)) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    /**\n     * 验证是否有某个角色\n     * @param roleCode\n     */\n    public boolean hasRole(String roleCode) {\n        if (StrUtils.isBlank(roleCode)) {\n            return false;\n        }\n        // 当前登录用户\n        LoginUserDetail loginUser = SecurityUtils.getLoginUser();\n        for (String targetRoleCode : loginUser.getRoleCodes()) {\n            if (roleCode.equals(targetRoleCode)) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n\n    /**\n     * 验证是否包含有请求权限\n     * @param requestPath 请求地址\n     * @param apiPermissions 所拥有的接口权限\n     */\n    private boolean verifyUrlPermission(String requestPath, List<ApiPermission> apiPermissions) {\n        // 没有所拥有的权限\n        if (CollectionUtils.isEmpty(apiPermissions)) {\n            return false;\n        }\n        HttpServletRequest request = ServletUtils.getRequest();\n        // 开始验证\n        for (ApiPermission apiPermission : apiPermissions) {\n            // 权限地址\n            String apiUrl = apiPermission.getApiUrl();\n            String apiMethod = apiPermission.getApiMethod();\n            // 如果地址匹配并且请求方式也一致，直接返回\n            if (antPathMatcher.matchStart(apiUrl, requestPath) && request.getMethod().equalsIgnoreCase(apiMethod)) {\n                return true;\n            }\n\n        }\n        return false;\n    }\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-security/src/main/java/com/geshanzsq/framework/security/service/impl/TokenServiceImpl.java",
    "content": "package com.geshanzsq.framework.security.service.impl;\n\nimport com.geshanzsq.common.core.util.id.IdUtils;\nimport com.geshanzsq.common.core.util.servlet.ServletUtils;\nimport com.geshanzsq.common.core.util.string.StrUtils;\nimport com.geshanzsq.common.redis.service.RedisService;\nimport com.geshanzsq.framework.security.constant.SecurityConstant;\nimport com.geshanzsq.framework.security.domain.LoginUserDetail;\nimport com.geshanzsq.framework.security.property.TokenProperty;\nimport com.geshanzsq.framework.security.service.TokenService;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\nimport org.springframework.util.CollectionUtils;\n\nimport javax.servlet.http.HttpServletRequest;\nimport java.util.Collection;\nimport java.util.Date;\nimport java.util.List;\nimport java.util.concurrent.TimeUnit;\n\n/**\n * token 验证处理\n *\n * @author geshanzsq\n * @date 2022/3/23\n */\n@Service\npublic class TokenServiceImpl implements TokenService {\n    \n    @Autowired\n    private TokenProperty tokenProperty;\n\n    @Autowired\n    private RedisService redisService;\n\n    /**\n     * 创建登录令牌\n     * @param loginUser 登录用户信息\n     */\n    @Override\n    public String createLoginUser(LoginUserDetail loginUser) {\n        // 生成令牌并去除横线\n        String token = IdUtils.simpleUUID();\n        // 设置用户信息到缓存\n        setLoginUserCache(token, loginUser, tokenProperty.getExpireTime());\n        return token;\n    }\n\n    /**\n     * 获取登录用户信息\n     */\n    @Override\n    public LoginUserDetail getLoginUser() {\n        return getLoginUser(ServletUtils.getRequest());\n    }\n\n    /**\n     * 获取登录用户信息\n     * @param request\n     */\n    @Override\n    public LoginUserDetail getLoginUser(HttpServletRequest request) {\n        String token = getToken(request);\n        if (StrUtils.isBlank(token)) {\n            return null;\n        }\n        // 令牌标识\n        String tokenKey = getTokenKey(token);\n        Object loginUserObj = redisService.get(tokenKey);\n        if (loginUserObj == null) {\n            return null;\n        }\n        return (LoginUserDetail) loginUserObj;\n    }\n\n    /**\n     * 删除登录用户\n     * @param request\n     * @param userId\n     */\n    @Override\n    public boolean deleteLoginUser(HttpServletRequest request, Long userId) {\n        String token = getToken(request);\n        // 删除令牌\n        String tokenKey = getTokenKey(token);\n        redisService.delete(tokenKey);\n        // 删除权限标识\n        String refreshPermissionKey = getRefreshPermissionKey(userId, token);\n        redisService.delete(refreshPermissionKey);\n        return true;\n    }\n\n    /**\n     * 设置刷新权限\n     * @param userId 用户 id\n     * @param token 令牌\n     * @param hasRefresh 是否需要刷新\n     */\n    @Override\n    public void setRefreshPermission(Long userId, String token, boolean hasRefresh) {\n        String refreshKey = getRefreshPermissionKey(userId, token);\n        redisService.set(refreshKey, hasRefresh, tokenProperty.getExpireTime(), TimeUnit.SECONDS);\n    }\n\n    /**\n     * 设置需要刷新权限\n     * @param userId 用户 id\n     */\n    @Override\n    public void setNeedRefreshPermission(Long userId) {\n        // 获取当前用户已登录的令牌键\n        String refreshKey = getRefreshPermissionKey(userId, \"*\");\n        Collection<String> refreshKeys = redisService.keys(refreshKey);\n        for (String key : refreshKeys) {\n            redisService.set(key, true, tokenProperty.getExpireTime(), TimeUnit.SECONDS);\n        }\n    }\n\n    /**\n     * 设置需要刷新权限\n     * @param userIds 用户 id 集合\n     */\n    @Override\n    public void setNeedRefreshPermission(List<Long> userIds) {\n        if (CollectionUtils.isEmpty(userIds)) {\n            return;\n        }\n        for (Long userId : userIds) {\n            setNeedRefreshPermission(userId);\n        }\n    }\n\n    /**\n     * 是否需要刷新权限\n     * @param userId 用户 id\n     * @param token 令牌\n     */\n    @Override\n    public boolean hasRefreshPermission(Long userId, String token) {\n        String refreshKey = getRefreshPermissionKey(userId, token);\n        Boolean hasRefresh = redisService.get(refreshKey);\n        return hasRefresh == null ? false : hasRefresh;\n    }\n\n    /**\n     * 设置用户信息到缓存\n     * @param token 令牌\n     * @param loginUser 用户信息\n     * @param expire 过期时间，单位为秒\n     */\n    @Override\n    public void setLoginUserCache(String token, LoginUserDetail loginUser, long expire) {\n        String tokenKey = getTokenKey(token);\n        // 设置登录时间和过期时间\n        Date nowDate = new Date();\n        loginUser.setLoginTime(nowDate.getTime());\n        loginUser.setExpireTime(nowDate.getTime() + expire * 1000);\n        redisService.set(tokenKey, loginUser, expire, TimeUnit.SECONDS);\n        // 设置用户不需要刷新权限，用于后续获取动态权限，当权限发生变动时，不刷新就能生效，刚登录时设置为 false，为后续变动权限做准备，以免设置大量未登录的用户\n        setRefreshPermission(loginUser.getUserId(), token, false);\n    }\n\n    /**\n     * 获取令牌过期时间\n     * @param token 令牌\n     */\n    @Override\n    public Long getTokenExpireTime(String token) {\n        String tokenKey = getTokenKey(token);\n        return redisService.getExpireTime(tokenKey);\n    }\n\n    /**\n     * 获取令牌\n     */\n    @Override\n    public String getToken() {\n        return getToken(ServletUtils.getRequest());\n    }\n\n    /**\n     * 获取令牌\n     * @param request\n     */\n    public String getToken(HttpServletRequest request) {\n        String token = request.getHeader(tokenProperty.getHeader());\n        // 如果请求头为空，则从参数中获取\n        if (StrUtils.isBlank(token)) {\n            token = request.getParameter(tokenProperty.getTokenParam());\n        } else {\n            // 去除前缀\n            token = token.replace(tokenProperty.getTokenPrefix() + \" \", \"\");\n        }\n        return token;\n    }\n\n    /**\n     * 获取令牌标识\n     * @param token 令牌\n     */\n    private String getTokenKey(String token) {\n        String tokenPrefix = StrUtils.isBlank(tokenProperty.getTokenRedisPrefix())\n                ? SecurityConstant.TOKEN_PREFIX : tokenProperty.getTokenRedisPrefix();\n        return tokenPrefix + token;\n    }\n\n    /**\n     * 获取刷新令牌标识\n     * @param userId\n     * @param token\n     */\n    private String getRefreshPermissionKey(Long userId, String token) {\n        String permissionRefreshPrefix = StrUtils.isBlank(tokenProperty.getPermissionRefreshRedisPrefix())\n                ? SecurityConstant.PERMISSION_REFRESH_PREFIX : tokenProperty.getPermissionRefreshRedisPrefix();\n        return permissionRefreshPrefix + userId + \":\" + token;\n    }\n}\n"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-security/src/main/java/com/geshanzsq/framework/security/util/SecurityUtils.java",
    "content": "package com.geshanzsq.framework.security.util;\n\nimport com.geshanzsq.common.core.constant.HttpStatus;\nimport com.geshanzsq.common.core.exception.ServiceException;\nimport com.geshanzsq.common.core.util.message.MessageUtils;\nimport com.geshanzsq.framework.security.domain.LoginUserDetail;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.security.core.context.SecurityContextHolder;\nimport org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;\n\n/**\n * 安全工具类\n *\n * @author geshanzsq\n * @date 2022/3/25\n */\npublic class SecurityUtils {\n\n    /**\n     * 获取用户 id\n     */\n    public static Long getUserId() {\n        return getLoginUser().getUserId();\n    }\n\n    /**\n     * 获取用户名\n     */\n    public static String getUsername() {\n        return getLoginUser().getUsername();\n    }\n\n    /**\n     * 获取登录用户\n     */\n    public static LoginUserDetail getLoginUser() {\n        try {\n            return (LoginUserDetail) getAuthentication().getPrincipal();\n        } catch (Exception e) {\n            throw new ServiceException(HttpStatus.UNAUTHORIZED, MessageUtils.message(\"user.info.fail\"));\n        }\n    }\n\n    /**\n     * 获取 Authentication\n     */\n    public static Authentication getAuthentication() {\n        return SecurityContextHolder.getContext().getAuthentication();\n    }\n\n\n    /**\n     * 生成 BCryptPasswordEncoder 加密密码\n     * @param password\n     */\n    public static String encryptPassword(String password) {\n        BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();\n        return bCryptPasswordEncoder.encode(password);\n    }\n\n    /**\n     * 判断密码是否相同\n     *\n     * @param rawPassword 真实密码\n     * @param encodedPassword 加密后字符\n     * @return 结果\n     */\n    public static boolean matchesPassword(String rawPassword, String encodedPassword) {\n        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();\n        return passwordEncoder.matches(rawPassword, encodedPassword);\n    }\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-swagger/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>geshanzsq-nav-common</artifactId>\n        <groupId>com.geshanzsq</groupId>\n        <version>2.0.0</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>geshanzsq-nav-common-swagger</artifactId>\n    <name>geshanzsq-nav-common-swagger</name>\n    <description>接口文档模块</description>\n    <version>2.0.0</version>\n\n    <dependencies>\n        <!-- knife4j 接口文档 -->\n        <dependency>\n            <groupId>com.github.xiaoymin</groupId>\n            <artifactId>knife4j-spring-boot-starter</artifactId>\n        </dependency>\n\n        <!-- Spring Boot Web -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- lombok -->\n        <dependency>\n            <groupId>org.projectlombok</groupId>\n            <artifactId>lombok</artifactId>\n        </dependency>\n    </dependencies>\n\n\n</project>"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-swagger/src/main/java/com/geshanzsq/common/swagger/config/SwaggerConfig.java",
    "content": "package com.geshanzsq.common.swagger.config;\n\nimport com.geshanzsq.common.swagger.property.SwaggerProperty;\nimport io.swagger.annotations.ApiOperation;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport springfox.documentation.builders.ApiInfoBuilder;\nimport springfox.documentation.builders.RequestHandlerSelectors;\nimport springfox.documentation.service.ApiInfo;\nimport springfox.documentation.service.Contact;\nimport springfox.documentation.spi.DocumentationType;\nimport springfox.documentation.spring.web.plugins.Docket;\nimport springfox.documentation.swagger2.annotations.EnableSwagger2;\n\n/**\n * Knife4j 接口文档配置\n *\n * @author geshanzsq\n * @date 2022/3/16\n */\n@Configuration\n@EnableSwagger2\npublic class SwaggerConfig {\n\n    @Autowired\n    private SwaggerProperty swaggerProperty;\n\n    @Bean(\"defaultApi2\")\n    public Docket defaultApi2() {\n        Docket docket = new Docket(DocumentationType.SWAGGER_2)\n                // 是否启用\n                .enable(swaggerProperty.isEnable())\n                // API 基本信息，展示在文档的页面\n                .apiInfo(apiInfo())\n                // 设置哪些接口暴露给Swagger展示\n                .select()\n                //扫描所有有注解的api，用这种方式更灵活\n                .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))\n                .build();\n\n        return docket;\n    }\n\n    /**\n     * 基本信息\n     */\n    private ApiInfo apiInfo() {\n        // 构建基本信息\n        return new ApiInfoBuilder()\n                // 标题\n                .title(swaggerProperty.getTitle())\n                // 描述\n                .description(swaggerProperty.getDescription())\n                // 作者信息\n                .contact(new Contact(swaggerProperty.getAuthor(), swaggerProperty.getUrl(), swaggerProperty.getEmail()))\n                // 版本\n                .version(swaggerProperty.getVersion())\n                .build();\n    }\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-common/geshanzsq-nav-common-swagger/src/main/java/com/geshanzsq/common/swagger/property/SwaggerProperty.java",
    "content": "package com.geshanzsq.common.swagger.property;\n\nimport lombok.Data;\nimport org.springframework.boot.context.properties.ConfigurationProperties;\nimport org.springframework.context.annotation.Configuration;\n\n/**\n * Knife4j 相关属性配置\n *\n * @author geshanzsq\n * @date 2022/3/17\n */\n@Configuration\n@ConfigurationProperties(prefix = \"swagger\")\n@Data\npublic class SwaggerProperty {\n\n    /**\n     * 标题\n     */\n    private String title;\n\n    /**\n     * 描述\n     */\n    private String description;\n\n    /**\n     * 作者\n     */\n    private String author;\n\n    /**\n     * 地址\n     */\n    private String url;\n\n    /**\n     * 邮箱\n     */\n    private String email;\n\n    /**\n     * 版本号\n     */\n    private String version;\n\n    /**\n     * 是否启用\n     */\n    private boolean enable;\n\n}\n"
  },
  {
    "path": "geshanzsq-nav-common/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>geshanzsq-nav</artifactId>\n        <groupId>com.geshanzsq</groupId>\n        <version>2.0.0</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>geshanzsq-nav-common</artifactId>\n    <name>geshanzsq-nav-common</name>\n    <version>2.0.0</version>\n    <packaging>pom</packaging>\n    <modules>\n        <module>geshanzsq-nav-common-core</module>\n        <module>geshanzsq-nav-common-framework</module>\n        <module>geshanzsq-nav-common-rate-limiter</module>\n        <module>geshanzsq-nav-common-log</module>\n        <module>geshanzsq-nav-common-redis</module>\n        <module>geshanzsq-nav-common-security</module>\n        <module>geshanzsq-nav-common-swagger</module>\n    </modules>\n\n\n</project>"
  },
  {
    "path": "pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>com.geshanzsq</groupId>\n    <artifactId>geshanzsq-nav</artifactId>\n    <packaging>pom</packaging>\n    <version>2.0.0</version>\n    <description>格姗导航</description>\n    <modules>\n        <module>geshanzsq-nav-admin</module>\n        <module>geshanzsq-nav-common</module>\n    </modules>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <java.version>1.8</java.version>\n        <maven.compiler.plugin.version>3.10.1</maven.compiler.plugin.version>\n\n        <geshanzsq.nav.common.core.version>2.0.0</geshanzsq.nav.common.core.version>\n        <geshanzsq.nav.common.rate.limiter.version>2.0.0</geshanzsq.nav.common.rate.limiter.version>\n        <geshanzsq.nav.common.redis.version>2.0.0</geshanzsq.nav.common.redis.version>\n        <geshanzsq.nav.common.swagger.version>2.0.0</geshanzsq.nav.common.swagger.version>\n        <geshanzsq.nav.common.log.version>2.0.0</geshanzsq.nav.common.log.version>\n        <geshanzsq.nav.common.security.version>2.0.0</geshanzsq.nav.common.security.version>\n        <geshanzsq.nav.common.framework.version>2.0.0</geshanzsq.nav.common.framework.version>\n        <geshanzsq.nav.admin.system.version>2.0.0</geshanzsq.nav.admin.system.version>\n\n        <spring.boot.version>2.3.12.RELEASE</spring.boot.version>\n        <knife4j.version>3.0.3</knife4j.version>\n        <mybatis.plus.version>3.5.2</mybatis.plus.version>\n        <fastjson.version>1.2.83</fastjson.version>\n        <mapstruct.version>1.5.0.RC1</mapstruct.version>\n        <lombok.version>1.18.22</lombok.version>\n        <pagehelper.version>1.4.1</pagehelper.version>\n        <easy.captcha.version>1.6.2</easy.captcha.version>\n        <commons.pool2.version>2.11.1</commons.pool2.version>\n        <bitwalker.version>1.21</bitwalker.version>\n        <hutool.all.version>5.8.5</hutool.all.version>\n        <commons.io.version>2.11.0</commons.io.version>\n        <flexmark.version>0.62.2</flexmark.version>\n        <just.auth.version>1.16.5</just.auth.version>\n        <jasypt.version>3.0.4</jasypt.version>\n        <jsoup.version>1.15.3</jsoup.version>\n        <httpcore.version>4.4.15</httpcore.version>\n        <commons.fileupload.version>1.4</commons.fileupload.version>\n        <just.auth.version>1.16.5</just.auth.version>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <!-- 通用核心模块 -->\n            <dependency>\n                <groupId>com.geshanzsq</groupId>\n                <artifactId>geshanzsq-nav-common-core</artifactId>\n                <version>${geshanzsq.nav.common.core.version}</version>\n            </dependency>\n\n            <!-- 通用限流模块 -->\n            <dependency>\n                <groupId>com.geshanzsq</groupId>\n                <artifactId>geshanzsq-nav-common-rate-limiter</artifactId>\n                <version>${geshanzsq.nav.common.rate.limiter.version}</version>\n            </dependency>\n\n            <!-- 通用缓存模块 -->\n            <dependency>\n                <groupId>com.geshanzsq</groupId>\n                <artifactId>geshanzsq-nav-common-redis</artifactId>\n                <version>${geshanzsq.nav.common.redis.version}</version>\n            </dependency>\n\n            <!-- 日志模块 -->\n            <dependency>\n                <groupId>com.geshanzsq</groupId>\n                <artifactId>geshanzsq-nav-common-log</artifactId>\n                <version>${geshanzsq.nav.common.log.version}</version>\n            </dependency>\n\n            <!-- 安全认证模块 -->\n            <dependency>\n                <groupId>com.geshanzsq</groupId>\n                <artifactId>geshanzsq-nav-common-security</artifactId>\n                <version>${geshanzsq.nav.common.security.version}</version>\n            </dependency>\n\n            <!-- 通用接口文档模块 -->\n            <dependency>\n                <groupId>com.geshanzsq</groupId>\n                <artifactId>geshanzsq-nav-common-swagger</artifactId>\n                <version>${geshanzsq.nav.common.swagger.version}</version>\n            </dependency>\n\n            <!-- 通用框架模块 -->\n            <dependency>\n                <groupId>com.geshanzsq</groupId>\n                <artifactId>geshanzsq-nav-common-framework</artifactId>\n                <version>${geshanzsq.nav.common.framework.version}</version>\n            </dependency>\n\n            <!-- 系统模块 -->\n            <dependency>\n                <groupId>com.geshanzsq</groupId>\n                <artifactId>geshanzsq-nav-admin-system</artifactId>\n                <version>${geshanzsq.nav.admin.system.version}</version>\n            </dependency>\n\n            <!-- spring boot-->\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-dependencies</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n\n            <!-- knife4j 接口文档 -->\n            <dependency>\n                <groupId>com.github.xiaoymin</groupId>\n                <artifactId>knife4j-spring-boot-starter</artifactId>\n                <version>${knife4j.version}</version>\n            </dependency>\n\n            <!-- MyBatis Plus -->\n            <dependency>\n                <groupId>com.baomidou</groupId>\n                <artifactId>mybatis-plus-boot-starter</artifactId>\n                <version>${mybatis.plus.version}</version>\n            </dependency>\n\n            <!-- 阿里 JSO N解析器 -->\n            <dependency>\n                <groupId>com.alibaba</groupId>\n                <artifactId>fastjson</artifactId>\n                <version>${fastjson.version}</version>\n            </dependency>\n\n            <!-- MapStruct 对象转换 -->\n            <dependency>\n                <groupId>org.mapstruct</groupId>\n                <artifactId>mapstruct</artifactId>\n                <version>${mapstruct.version}</version>\n            </dependency>\n\n            <!-- PageHelper 分页插件 -->\n            <dependency>\n                <groupId>com.github.pagehelper</groupId>\n                <artifactId>pagehelper-spring-boot-starter</artifactId>\n                <version>${pagehelper.version}</version>\n            </dependency>\n\n            <!-- easy-captcha 验证码 -->\n            <dependency>\n                <groupId>com.github.whvcse</groupId>\n                <artifactId>easy-captcha</artifactId>\n                <version>${easy.captcha.version}</version>\n            </dependency>\n\n            <!-- 使用 Redis 缓存需要引用 -->\n            <dependency>\n                <groupId>org.apache.commons</groupId>\n                <artifactId>commons-pool2</artifactId>\n                <version>${commons.pool2.version}</version>\n            </dependency>\n\n            <!-- 解析客户端操作系统、浏览器等 -->\n            <dependency>\n                <groupId>eu.bitwalker</groupId>\n                <artifactId>UserAgentUtils</artifactId>\n                <version>${bitwalker.version}</version>\n            </dependency>\n\n            <!-- aop 切面 -->\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-aop</artifactId>\n                <version>${spring.boot.version}</version>\n            </dependency>\n\n            <!-- huTool 工具 -->\n            <dependency>\n                <groupId>cn.hutool</groupId>\n                <artifactId>hutool-all</artifactId>\n                <version>${hutool.all.version}</version>\n            </dependency>\n\n            <!-- io 常用工具类 -->\n            <dependency>\n                <groupId>commons-io</groupId>\n                <artifactId>commons-io</artifactId>\n                <version>${commons.io.version}</version>\n            </dependency>\n\n            <!-- Markdown 转 Html -->\n            <dependency>\n                <groupId>com.vladsch.flexmark</groupId>\n                <artifactId>flexmark-all</artifactId>\n                <version>${flexmark.version}</version>\n            </dependency>\n            <!--表格渲染插件-->\n            <dependency>\n                <groupId>com.vladsch.flexmark</groupId>\n                <artifactId>flexmark-ext-tables</artifactId>\n                <version>${flexmark.version}</version>\n            </dependency>\n\n            <!-- JustAuth 第三方登录模块-->\n            <dependency>\n                <groupId>me.zhyd.oauth</groupId>\n                <artifactId>JustAuth</artifactId>\n                <version>${just.auth.version}</version>\n            </dependency>\n\n            <!-- 数据库密码加密 -->\n            <dependency>\n                <groupId>com.github.ulisesbocchio</groupId>\n                <artifactId>jasypt-spring-boot-starter</artifactId>\n                <version>${jasypt.version}</version>\n            </dependency>\n\n            <!-- html 解析 -->\n            <dependency>\n                <groupId>org.jsoup</groupId>\n                <artifactId>jsoup</artifactId>\n                <version>${jsoup.version}</version>\n            </dependency>\n\n            <!-- http core -->\n            <dependency>\n                <groupId>org.apache.httpcomponents</groupId>\n                <artifactId>httpcore</artifactId>\n                <version>${httpcore.version}</version>\n            </dependency>\n\n            <!--文件上传工具类 -->\n            <dependency>\n                <groupId>commons-fileupload</groupId>\n                <artifactId>commons-fileupload</artifactId>\n                <version>${commons.fileupload.version}</version>\n            </dependency>\n\n            <!-- JustAuth 第三方登录模块-->\n            <dependency>\n                <groupId>me.zhyd.oauth</groupId>\n                <artifactId>JustAuth</artifactId>\n                <version>${just.auth.version}</version>\n            </dependency>\n\n        </dependencies>\n    </dependencyManagement>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-compiler-plugin</artifactId>\n                <version>${maven.compiler.plugin.version}</version>\n                <configuration>\n                    <source>${java.version}</source>\n                    <target>${java.version}</target>\n                    <encoding>${project.build.sourceEncoding}</encoding>\n                    <!-- 解决 Lombok + MapStruct 组合 -->\n                    <annotationProcessorPaths>\n                        <path>\n                            <groupId>org.projectlombok</groupId>\n                            <artifactId>lombok</artifactId>\n                            <version>${lombok.version}</version>\n                        </path>\n                        <path>\n                            <groupId>org.mapstruct</groupId>\n                            <artifactId>mapstruct-processor</artifactId>\n                            <version>${mapstruct.version}</version>\n                        </path>\n                    </annotationProcessorPaths>\n                </configuration>\n            </plugin>\n        </plugins>\n    </build>\n\n    <repositories>\n        <repository>\n            <id>public</id>\n            <name>aliyun nexus</name>\n            <url>http://maven.aliyun.com/nexus/content/groups/public/</url>\n            <releases>\n                <enabled>true</enabled>\n            </releases>\n        </repository>\n    </repositories>\n\n    <pluginRepositories>\n        <pluginRepository>\n            <id>public</id>\n            <name>aliyun nexus</name>\n            <url>http://maven.aliyun.com/nexus/content/groups/public/</url>\n            <releases>\n                <enabled>true</enabled>\n            </releases>\n            <snapshots>\n                <enabled>false</enabled>\n            </snapshots>\n        </pluginRepository>\n    </pluginRepositories>\n\n\n</project>"
  },
  {
    "path": "sql/geshanzsq_nav.sql",
    "content": "/*\n Navicat Premium Data Transfer\n\n Source Server         : localhost\n Source Server Type    : MySQL\n Source Server Version : 80017 (8.0.17)\n Source Host           : localhost:3306\n Source Schema         : geshanzsq_nav\n\n Target Server Type    : MySQL\n Target Server Version : 80017 (8.0.17)\n File Encoding         : 65001\n\n Date: 22/05/2023 20:09:21\n*/\n\nSET NAMES utf8mb4;\nSET FOREIGN_KEY_CHECKS = 0;\n\n-- ----------------------------\n-- Table structure for log_login\n-- ----------------------------\nDROP TABLE IF EXISTS `log_login`;\nCREATE TABLE `log_login`  (\n  `id` bigint(20) NOT NULL COMMENT '登录日志 id',\n  `username` varchar(50) NULL DEFAULT '' COMMENT '登录用户名',\n  `fk_user_id` bigint(20) NOT NULL COMMENT '用户 id',\n  `ip_address` varchar(200) NULL DEFAULT NULL COMMENT '登录 ip 地址',\n  `login_location` varchar(255) NULL DEFAULT '' COMMENT '登录位置',\n  `browser_type` varchar(50) NULL DEFAULT '' COMMENT '浏览器类型',\n  `operate_system` varchar(50) NULL DEFAULT '' COMMENT '操作系统',\n  `status` tinyint(1) NULL DEFAULT 1 COMMENT '登录状态（1 成功，2 失败）',\n  `hint_message` varchar(255) NULL DEFAULT '' COMMENT '提示消息',\n  `gmt_login` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '登录时间',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB COMMENT = '登录日志' ROW_FORMAT = DYNAMIC;\n\n-- ----------------------------\n-- Records of log_login\n-- ----------------------------\n\n-- ----------------------------\n-- Table structure for log_operation\n-- ----------------------------\nDROP TABLE IF EXISTS `log_operation`;\nCREATE TABLE `log_operation`  (\n  `id` bigint(20) NOT NULL COMMENT '操作日志 id',\n  `module_name` varchar(50) NULL DEFAULT '' COMMENT '模块名称',\n  `business_type` tinyint(1) NULL DEFAULT 1 COMMENT '业务类型（1 其它，2 新增，3 修改，4 删除）',\n  `operate_type` tinyint(1) NULL DEFAULT 1 COMMENT '操作类型（1 其它，2 后台用户，3 手机端用户，4 博客用户）',\n  `fk_user_id` bigint(20) NULL DEFAULT NULL COMMENT '操作用户 id',\n  `request_method` varchar(10) NULL DEFAULT '' COMMENT '请求方式',\n  `class_method` varchar(255) NULL DEFAULT '' COMMENT '类方法',\n  `request_url` varchar(255) NULL DEFAULT '' COMMENT '请求地址',\n  `ip_address` varchar(200) NULL DEFAULT '' COMMENT '操作 ip 地址',\n  `operate_location` varchar(255) NULL DEFAULT '' COMMENT '操作位置',\n  `request_param` longtext NULL COMMENT '请求参数',\n  `return_result` longtext NULL COMMENT '返回结果',\n  `status` tinyint(1) NULL DEFAULT 1 COMMENT '登录状态（1 成功，2 异常）',\n  `gmt_operate` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '操作时间',\n  `error_message` longtext NULL COMMENT '错误消息',\n  `browser_type` varchar(50) NULL DEFAULT '' COMMENT '浏览器类型',\n  `operate_system` varchar(50) NULL DEFAULT '' COMMENT '操作系统',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB COMMENT = '操作日志' ROW_FORMAT = DYNAMIC;\n\n-- ----------------------------\n-- Records of log_operation\n-- ----------------------------\n\n-- ----------------------------\n-- Table structure for nav_category\n-- ----------------------------\nDROP TABLE IF EXISTS `nav_category`;\nCREATE TABLE `nav_category`  (\n  `id` bigint(20) NOT NULL COMMENT '分类 id',\n  `parent_id` bigint(20) NOT NULL DEFAULT 0 COMMENT '父级id',\n  `sort` int(11) NULL DEFAULT 0 COMMENT '排序',\n  `category_name` varchar(50) NULL DEFAULT '' COMMENT '分类名称',\n  `category_icon` varchar(20) NULL DEFAULT '' COMMENT '分类图标',\n  `gmt_create` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `gmt_modify` datetime NULL DEFAULT NULL COMMENT '修改时间',\n  `fk_create_user_id` bigint(20) NULL DEFAULT NULL COMMENT '创建人用户 id',\n  `fk_modify_user_id` bigint(20) NULL DEFAULT NULL COMMENT '修改人用户 id',\n  `status` tinyint(1) NULL DEFAULT 1 COMMENT '状态（1 正常，2 停用）',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB COMMENT = '导航分类' ROW_FORMAT = Dynamic;\n\n-- ----------------------------\n-- Records of nav_category\n-- ----------------------------\nINSERT INTO `nav_category` VALUES (150725316141100103, 0, 30, '资源搜索', 'search', '2020-07-10 23:58:54', '2021-03-27 00:27:58', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141100104, 0, 40, '在线工具', 'cogs', '2020-07-25 19:39:39', '2021-03-27 00:27:49', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141100105, 0, 60, '学习教程', 'study', '2020-07-25 19:39:39', '2021-03-27 00:27:30', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141100106, 0, 60, '软件下载', 'software', '2020-07-25 19:39:39', '2021-03-27 00:27:24', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141100107, 0, 70, '休闲娱乐', 'music', '2020-07-25 19:39:39', '2021-03-27 00:27:21', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141100108, 0, 80, '灵感采集', 'Light-Bulb', '2020-07-25 21:41:31', '2021-03-27 00:27:17', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141100109, 0, 90, '素材资源', 'international', '2020-07-25 21:41:31', '2021-03-27 00:27:13', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141101001, 150725316141100103, 1, '网盘搜索', 'chart', '2020-07-11 00:02:09', '2020-07-11 11:35:42', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141101002, 150725316141100103, 2, '电子书搜索', 'code', '2020-07-11 10:46:40', '2020-07-11 11:35:46', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141101004, 0, 1, '格式转换', 'lock', '2020-07-25 21:30:22', '2021-03-27 00:22:23', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141101005, 150725316141100104, 2, '语言翻译', 'component', '2020-07-25 21:30:22', '2020-07-25 21:31:18', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141101006, 0, 3, '图标素材', 'component', '2020-07-25 21:30:22', '2021-03-27 00:28:42', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141101007, 150725316141100105, 1, '博客网站', 'wechat', '2020-07-25 21:43:34', NULL, 1, NULL, 1);\nINSERT INTO `nav_category` VALUES (150725316141101008, 150725316141100106, 1, 'Windows', 'tool', '2020-07-25 21:44:10', NULL, 1, NULL, 1);\nINSERT INTO `nav_category` VALUES (150725316141101009, 150725316141100106, 2, 'Android', 'upload', '2020-07-25 21:44:36', NULL, 1, NULL, 1);\nINSERT INTO `nav_category` VALUES (150725316141101010, 150725316141100106, 3, 'Apple|Mac', 'time', '2020-07-25 21:44:52', '2020-08-18 22:46:22', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141101011, 150725316141100107, 1, '影视', 'icon', '2020-07-25 21:45:44', NULL, 1, NULL, 1);\nINSERT INTO `nav_category` VALUES (150725316141101012, 150725316141100107, 2, '音乐', 'people', '2020-07-25 21:45:58', NULL, 1, NULL, 1);\nINSERT INTO `nav_category` VALUES (150725316141101013, 150725316141100104, 3, '图形创意', 'dict', '2020-08-01 15:06:54', NULL, 1, NULL, 1);\nINSERT INTO `nav_category` VALUES (150725316141101014, 150725316141100104, 4, '界面设计', 'select', '2020-08-01 15:08:33', '2023-05-07 18:24:41', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141101015, 150725316141100104, 5, '交互动效', 'tool', '2020-08-01 15:13:19', '2023-05-07 18:24:51', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141101016, 150725316141100104, 6, '在线配色', 'time', '2020-08-01 15:15:46', '2023-05-07 18:25:00', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141101017, 150725316141100104, 7, '其他工具', 'tag', '2020-08-01 15:15:58', '2023-05-07 18:25:09', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141101018, 150725316141100104, 8, 'Chrome插件', 'clipboard', '2020-08-01 15:16:14', '2023-05-07 18:25:19', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141101019, 150725316141100108, 1, '发现产品', 'build', '2020-08-01 15:23:57', '2023-05-07 18:28:31', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141101020, 150725316141100108, 2, '界面灵感', 'clipboard', '2020-08-01 15:24:11', '2023-05-07 18:28:40', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141101021, 150725316141100108, 3, '网页灵感', 'druid', '2020-08-01 15:24:34', '2023-05-07 18:28:48', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141101022, 150725316141100109, 2, 'LOGO设计', 'clipboard', '2020-08-01 15:35:28', '2023-05-07 18:29:10', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141101023, 150725316141100109, 3, '平面素材', 'drag', '2020-08-01 15:35:55', '2023-05-07 18:29:19', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141101024, 150725316141100109, 4, 'UI资源', 'example', '2020-08-01 15:36:05', '2023-05-07 18:29:26', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141101025, 150725316141100109, 5, 'Sketch资源', 'form', '2020-08-01 15:36:17', '2023-05-07 18:29:31', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141101026, 150725316141100109, 6, '字体资源', 'international', '2020-08-01 15:36:40', '2023-05-07 18:29:36', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141101027, 150725316141100109, 7, 'Mockup', 'monitor', '2020-08-01 15:37:03', '2023-05-07 18:29:44', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141101028, 150725316141100109, 8, '摄影图库', 'post', '2020-08-01 15:37:24', '2023-05-07 18:29:50', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141101029, 0, 10, 'PPT资源', 'theme', '2020-08-01 15:37:35', '2021-03-27 00:28:21', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141101030, 150725316141100105, 4, '后端学习', 'textarea', '2020-08-08 15:06:09', '2023-05-07 18:26:50', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141101031, 150725316141100105, 3, '前端学习', 'rate', '2020-08-08 15:07:26', '2023-05-07 18:27:03', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141101032, 150725316141100105, 2, '个人博客', 'Light-Bulb', '2020-08-08 15:19:13', '2023-05-07 18:27:13', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141101034, 0, 100, '自媒体', 'select', '2020-08-11 17:44:07', '2021-03-27 00:27:08', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141101035, 150725316141101034, 1, '运营平台', 'theme', '2020-08-11 17:44:31', '2023-05-07 18:30:22', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141101036, 0, 2, '图片素材', 'clipboard', '2020-08-11 20:12:23', '2021-03-27 00:30:29', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141101037, 0, 12, '桌面壁纸', 'computer', '2020-08-19 19:39:19', '2021-03-27 00:29:55', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141101039, 150725316141101034, 2, '排版工具', 'shopping', '2020-08-20 11:02:52', '2023-05-07 18:30:31', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141101040, 150725316141100104, 9, 'Github加速下载', 'chart', '2020-08-31 22:51:30', '2023-05-07 18:25:28', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141101042, 150725316141100105, 5, '开源项目', 'article', '2020-09-16 14:37:04', '2023-05-07 18:27:22', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141101043, 150725316141100104, 11, '电视直播', 'online', '2020-09-20 14:54:03', '2023-05-07 18:25:45', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141101046, 150725316141100106, 0, '软件博客', 'software', '2020-10-16 21:41:47', '2023-05-07 18:22:23', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141101047, 150725316141100107, 3, '游戏资源', 'menu', '2020-10-16 22:59:20', '2023-05-07 18:28:20', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141101048, 150725316141101034, 4, '视频制作', 'movie', '2021-01-07 19:56:03', '2023-05-07 18:30:39', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141101049, 150725316141100104, 12, '免费图床', 'excel', '2021-01-12 16:28:22', '2023-05-07 18:26:00', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141101050, 150725316141100104, 13, '办公协作', 'swagger', '2021-01-18 10:27:16', '2023-05-07 18:26:22', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141101051, 150725316141101034, 5, '数据分析', 'dict', '2021-02-01 14:34:50', '2023-05-07 18:30:45', 1, 1, 1);\nINSERT INTO `nav_category` VALUES (150725316141101052, 0, 0, '热门导航', 'cc-thumbs-up', '2021-03-27 00:34:10', NULL, 1, NULL, 1);\n\n-- ----------------------------\n-- Table structure for nav_comment\n-- ----------------------------\nDROP TABLE IF EXISTS `nav_comment`;\nCREATE TABLE `nav_comment`  (\n  `id` bigint(20) NOT NULL COMMENT '评论 id',\n  `parent_id` bigint(20) NULL DEFAULT 0 COMMENT '上级 id',\n  `has_sticky` tinyint(1) NULL DEFAULT 2 COMMENT '是否置顶（1 是，2 否） ',\n  `comment_content` longtext NOT NULL COMMENT '评论内容',\n  `nick_name` varchar(30) NULL DEFAULT '' COMMENT '昵称',\n  `email` varchar(50) NULL DEFAULT '' COMMENT '邮箱',\n  `remark` varchar(255) NULL DEFAULT '' COMMENT '备注',\n  `status` tinyint(1) NULL DEFAULT 1 COMMENT '状态（1 待审核，2 通过，3 驳回）',\n  `fk_create_user_id` bigint(20) NULL DEFAULT NULL COMMENT '创建人用户 id',\n  `gmt_create` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `fk_modify_user_id` bigint(20) NULL DEFAULT NULL COMMENT '修改人用户 id',\n  `gmt_modify` datetime NULL DEFAULT NULL COMMENT '修改时间',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB COMMENT = '评论表' ROW_FORMAT = DYNAMIC;\n\n-- ----------------------------\n-- Records of nav_comment\n-- ----------------------------\n\n-- ----------------------------\n-- Table structure for nav_picture\n-- ----------------------------\nDROP TABLE IF EXISTS `nav_picture`;\nCREATE TABLE `nav_picture`  (\n  `id` bigint(20) NOT NULL COMMENT '图片 id',\n  `picture_original_name` varchar(200) NULL DEFAULT '' COMMENT '图片原名称',\n  `picture_path` varchar(500) NULL DEFAULT '' COMMENT '图片路径',\n  `picture_md5` varchar(100) NULL DEFAULT '' COMMENT '图片 md5',\n  `fk_create_user_id` bigint(20) NULL DEFAULT NULL COMMENT '创建人用户 id',\n  `gmt_create` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `fk_modify_user_id` bigint(20) NULL DEFAULT NULL COMMENT '修改人用户 id',\n  `gmt_modify` datetime NULL DEFAULT NULL COMMENT '修改时间',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB COMMENT = '导航图片' ROW_FORMAT = DYNAMIC;\n\n-- ----------------------------\n-- Records of nav_picture\n-- ----------------------------\n\n-- ----------------------------\n-- Table structure for nav_site\n-- ----------------------------\nDROP TABLE IF EXISTS `nav_site`;\nCREATE TABLE `nav_site`  (\n  `id` bigint(20) NOT NULL COMMENT '网站 id',\n  `fk_category_id` bigint(20) NULL DEFAULT NULL COMMENT '分类 id',\n  `site_name` varchar(50) NULL DEFAULT NULL COMMENT '网站名称',\n  `site_path` varchar(100) NULL DEFAULT NULL COMMENT '网站图片路径',\n  `site_description` varchar(300) NULL DEFAULT NULL COMMENT '网站描述',\n  `site_url` varchar(300) NULL DEFAULT NULL COMMENT '网站地址',\n  `sort` int(11) NULL DEFAULT 0 COMMENT '排序',\n  `click_count` int(11) NULL DEFAULT 0 COMMENT '点击量',\n  `gmt_create` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `gmt_modify` datetime NULL DEFAULT NULL COMMENT '修改时间',\n  `fk_create_user_id` bigint(20) NULL DEFAULT NULL COMMENT '创建人用户 id',\n  `fk_modify_user_id` bigint(20) NULL DEFAULT NULL COMMENT '修改人用户 id',\n  `status` tinyint(1) NULL DEFAULT 1 COMMENT '状态（1 正常，2 停用）',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB COMMENT = '用户导航网站信息' ROW_FORMAT = Dynamic;\n\n-- ----------------------------\n-- Records of nav_site\n-- ----------------------------\nINSERT INTO `nav_site` VALUES (150725316141100003, 150725316141101001, '小昭来啦', '/profile/site/system/b27e89c97230454302cbb24a115c3577.ico', '网盘搜索', 'https://www.xiaozhaolaila.com', 2, 0, '2020-07-30 22:18:26', NULL, NULL, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100004, 150725316141101001, '小猪快盘', '/profile/site/system/16ad1dd9-810c-44e0-b553-5ee7a155aa35.png', '有你更方便', 'https://www.xiaokesoso.com/', 3, 0, '2020-07-30 22:18:26', '2022-11-07 19:46:34', NULL, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100006, 150725316141101001, '小不点搜索', '/profile/site/system/7f0c4a571333ea397c688b7fc0ba7191.jpg', '好资源一网打尽', 'https://www.xiaoso.net', 5, 0, '2020-07-30 22:18:26', '2020-07-30 23:16:32', NULL, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100007, 150725316141101001, '56网盘搜索', '/profile/site/system/7dc0f592b6ac032ca20e3d29c2daf2dd.ico', '网盘搜索', 'https://www.56wangpan.com', 6, 0, '2020-07-30 22:18:26', '2020-07-30 23:17:37', NULL, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100008, 150725316141101001, '大圣盘', '/profile/site/system/b054e2e190eb787aedcef330c4a78e1e.png', '网盘搜索，就用大圣盘 - 最好用的百度网盘搜索引擎', 'https://www.dashengpan.com', 7, 0, '2020-07-30 22:18:26', '2020-07-30 23:20:10', NULL, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100016, 150725316141101002, '点点文档搜索', '/profile/site/system/a35d2046-f0c9-4a94-b007-79322b05c5a7.jpg', '专注专业文档搜索', 'https://www.torrent.org.cn/bd', 1, 0, '2020-08-01 14:24:18', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100017, 150725316141101002, 'Telegram书籍搜索', '/profile/site/system/d51adf1b-6baa-4aaf-90e7-d444309ce737.png', '免费电子书搜索', 'https://bks.thefuture.top/', 2, 0, '2020-08-01 14:24:18', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100019, 150725316141101002, '鸠摩搜索', '/profile/site/system/69d815f7-ff4f-49ba-b294-535fd1f5ddbf.png', '强大的电子书搜索', 'https://www.jiumodiary.com/', 4, 0, '2020-08-01 14:24:18', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100020, 150725316141101002, 'kindle 漫画', '/profile/site/system/9ff2df64-77bd-4c12-8e6d-92d28ae264df.ico', '漫画', 'https://volmoe.com/', 5, 0, '2020-08-01 14:24:18', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100021, 150725316141101002, '中华珍宝馆', '/profile/site/system/857063b3-ea38-4eb8-8c29-d372ba2740a6.png', '中华珍宝馆', 'https://ltfc.net/', 6, 0, '2020-08-01 14:24:18', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100022, 150725316141101002, '国图文津', '/profile/site/system/c356a6b5-f8f4-4857-9142-732d5defd004.png', '文津搜索', 'http://find.nlc.cn', 7, 0, '2020-08-01 14:24:18', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100023, 150725316141101002, '书格', '/profile/site/system/a60e1981c4c4a14e5469b9c2e8a16cac.png', '搜索资源集', 'https://new.shuge.org/', 8, 0, '2020-08-01 14:24:18', '2020-08-08 14:50:00', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100024, 150725316141101002, '古籍馆', '/profile/site/system/5350f9dd-1818-4b8b-84b3-cf926d5c7cc2.ico', '中國最大的古籍圖書館，在线搜索', 'https://www.gujiguan.com', 9, 0, '2020-08-01 14:24:18', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100025, 150725316141101002, 'Gutenberg', '/profile/site/system/5b2c64cb-a793-4bd9-a69a-e6535cdd36bc.png', ' 无版权书籍在线搜索与下载，外文版', 'http://www.gutenberg.org', 10, 0, '2020-08-01 14:24:18', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100028, 150725316141101002, '书栈网', '/profile/site/system/2e2d77a6-3c44-4de0-a5eb-a6d2b754a731.png', '程序员IT互联网开源编程书籍免费阅读与下载，取之于猿用之于猿', 'https://www.bookstack.cn', 13, 0, '2020-08-01 14:24:18', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100029, 150725316141101002, '码农之家', '/profile/site/system/54f8a778eaaa63550b63e8a051484d43.ico', '计算机电子书下载网-编程pdf电子书下载', 'https://www.xz577.com', 14, 0, '2020-08-01 14:24:18', '2020-08-06 13:53:33', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100030, 150725316141101002, '书享家', '/profile/site/system/6737d6da-43f3-4b67-b528-5aa58e77e86d.png', '电子书下载导航', 'http://shuxiangjia.cn', 15, 0, '2020-08-01 14:24:18', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100031, 150725316141101005, '有道翻译', '/profile/site/system/316e6dde-d4a6-4590-9d0a-e082b40a4876.ico', '免费、及时的多语种在线翻译', 'http://fanyi.youdao.com/', 1, 0, '2020-08-01 14:41:43', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100032, 150725316141101005, 'Google 翻译', '/profile/site/system/c9f49602-6db2-488c-994c-45524e499090.ico', 'Google 在线翻译', 'https://translate.google.cn/', 2, 0, '2020-08-01 14:41:43', '2021-01-13 15:29:39', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100033, 150725316141101005, '百度翻译', '/profile/site/system/b4354f2c-a638-4e25-9269-f572af38318c.jpg', '百度在线翻译', 'https://fanyi.baidu.com/translate', 3, 0, '2020-08-01 14:41:43', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100034, 150725316141101005, '必应翻译', '/profile/site/system/e877f478-17d8-4549-9858-cb5edd5ab4f2.ico', '微软在线翻译', 'https://cn.bing.com/translator', 4, 0, '2020-08-01 14:41:43', '2021-01-13 15:29:46', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100035, 150725316141101008, '腾讯软件中心', '/profile/site/system/3d244575922fd6f3f251cf2326901817.ico', '不错的软件商店', 'https://pc.qq.com/', 1, 0, '2020-08-01 14:48:15', '2020-08-06 13:43:40', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100036, 150725316141101008, 'MSDN', '/profile/site/system/e377b1038d9b1de65faa974cd0f1697d.ico', '原版系统镜像下载', 'https://msdn.itellyou.cn', 2, 0, '2020-08-01 14:48:15', '2020-08-06 13:44:22', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100037, 150725316141101008, '致美化', '/profile/site/system/095e81f7aabe0a02d59e260b5a006181.ico', '桌面美化交流平台', 'https://zhutix.com', 3, 0, '2020-08-01 14:48:15', '2020-08-06 13:45:11', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100038, 150725316141101046, '大眼仔旭', '/profile/site/system/e9b2f20ae9880fb20b98a07a309b96c1.ico', '专注视频剪辑、解压、录屏、思维导图等办公软件分享', 'http://www.dayanzai.me', 2, 0, '2020-08-01 14:48:15', '2020-10-16 21:45:14', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100039, 150725316141101046, '爱绿软', '/profile/site/system/161d313406154438d4319655b0892cd8.png', '收集分享各类有趣好玩的绿色软件', 'https://ilvruan.com', 3, 0, '2020-08-01 14:48:15', '2020-10-16 21:45:18', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100040, 150725316141101046, '异次元软件站', '/profile/site/system/5edb929d595ea22f900244e4985e4cb2.ico', '异次元软件世界 - 软件改变生活', 'https://www.iplaysoft.com', 4, 0, '2020-08-01 14:48:15', '2020-10-16 21:45:22', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100041, 150725316141101046, '吾爱破解', '/profile/site/system/25ef62ba415682c09a4a0a2dea4cbf67.ico', '软件交流社区', 'https://www.52pojie.cn', 0, 0, '2020-08-01 14:48:15', '2020-10-16 21:45:05', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100042, 150725316141101009, '魔趣 ROM', '/profile/site/system/46363edb-dafa-451a-bb0b-74030679e0e1.png', '可能是中国最好的安卓开源系统', 'https://www.mokeedev.com', 1, 0, '2020-08-01 14:49:28', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100043, 150725316141101009, 'Xposed框架中文站', '/profile/site/system/29575a8c-5830-4310-b04c-1ddb978dcb3f.png', '超多Xposed框架模块介绍与下载', 'http://xposed.appkg.com', 2, 0, '2020-08-01 14:49:28', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100044, 150725316141101009, '观道', '/profile/site/system/1b3a7fddd824ac8f01e5c0d400875808.ico', '国外App下载_App下载界的一股清流', 'http://www.guandao.cc', 3, 0, '2020-08-01 14:49:28', '2020-08-06 13:51:36', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100045, 150725316141101009, '豌豆荚', '/profile/site/system/e0ccaa1741577bac575a049479df805e.ico', '海量安卓APP应用与游戏免费下载', 'https://www.wandoujia.com', 4, 0, '2020-08-01 14:49:28', '2020-08-06 13:50:56', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100046, 150725316141101009, 'TapTap', '/profile/site/system/0f03874c92974facb1706e69c54f94d4.png', '发现好游戏', 'https://www.taptap.com', 5, 0, '2020-08-01 14:49:28', '2020-08-06 13:52:24', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100047, 150725316141101010, '腾讯柠檬精选', '/profile/site/system/25206276f20af4103ea56a75be6c0802.png', '收录免费、正版、好用的Mac应用及产品', 'https://lemon.qq.com/lab/', 1, 0, '2020-08-01 14:50:21', '2020-08-06 13:49:34', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100048, 150725316141101010, '每日限免', '/profile/site/system/5d4d23c3-7135-4f82-b946-6c10f3a11f84.png', '每日精品限免_促销应用', 'https://app.so/xianmian', 2, 0, '2020-08-01 14:50:23', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100052, 150725316141101011, '腾讯视频', '/profile/site/system/fe2b466d9fe6bed552c0adcaac1c2813.ico', '中国领先的在线视频媒体平台,海量高清视频在线观看', 'https://v.qq.com', 4, 0, '2020-08-01 14:51:38', '2020-08-06 13:42:24', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100053, 150725316141101011, '爱奇艺', '/profile/site/system/b9555fb5-e19c-4547-a542-eb55e4a6bd72.png', '在线视频网站-海量正版高清视频在线观看', 'https://www.iqiyi.com', 5, 0, '2020-08-01 14:51:38', '2021-01-13 15:21:57', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100054, 150725316141101011, '优酷', '/profile/site/system/e4e0ee80c4466950ae51cecdd28080e1.png', '这世界很酷', 'https://www.youku.com', 6, 0, '2020-08-01 14:51:38', '2020-08-06 13:39:48', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100055, 150725316141101012, '疯狂音乐', '/profile/site/system/e1ace6a5-b279-49a9-8f1e-afb98ab97956.ico', '音乐聚集平台，支持国内大部分音乐平台', 'http://music.ifkdy.com', 1, 0, '2020-08-01 14:52:55', '2021-01-13 15:26:30', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100059, 150725316141101012, 'QQ音乐', '/profile/site/system/6dfcb9895c4d834dac1bc7256a54a9b8.ico', '千万正版音乐海量无损曲库新歌热歌天天畅听的高品质音乐平台', 'https://y.qq.com', 5, 0, '2020-08-01 14:52:55', '2020-08-04 14:48:12', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100060, 150725316141101012, '网易云音乐', '/profile/site/system/21f3ff494c85b7adbd62f6308651e4cf.png', '专注于发现与分享的音乐产品，依托专业音乐人、DJ、好友推荐及社交功能，为用户打造全新的音乐生活', 'https://music.163.com', 6, 0, '2020-08-01 14:52:55', '2020-08-04 14:49:42', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100061, 150725316141101012, '酷狗音乐', '/profile/site/system/0e8a8d31be0727fcd077c9df5e6a9ba9.ico', '就是歌多', 'https://www.kugou.com', 7, 0, '2020-08-01 14:52:55', '2020-08-04 14:52:05', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100062, 150725316141101012, '虾米音乐', '/profile/site/system/3d4c138efe8f0c64cf3a94c17c3344b7.png', '发现音乐新世界', 'https://www.xiami.com', 8, 0, '2020-08-01 14:52:55', '2020-08-05 00:19:33', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100063, 150725316141101004, '在线格式转换', '/profile/site/system/95ed0b48-8dbc-4407-a52a-2541bc16f054.jpg', '万能且免费的格式转换，支持各种格式。', 'https://www.alltoall.net', 1, 0, '2020-08-01 14:54:25', '2020-08-11 23:28:53', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100064, 150725316141101032, '良许', '/profile/site/system/0fcd5b42-a5c6-4023-86e3-97448d7f20a2.ico', 'Linux系统常用命令教学,shell脚本入门学习', 'https://www.lxlinux.net', 9, 0, '2020-08-01 14:56:49', '2021-01-13 15:33:05', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100065, 150725316141101032, '程序猿圈', '/profile/site/system/a198ddc1-8b73-4900-93b4-87fda77fb50f.jpg', '程序员在线学习站点', 'https://www.cxyquan.com/', 2, 0, '2020-08-01 14:56:49', '2020-08-08 15:19:47', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100066, 150725316141101032, '纯洁的微笑', '/profile/site/system/bee2f3c7-4fb8-4b06-8ba3-c678585d5b54.jpg', '分享技术，品味人生', 'http://ityouknow.com/', 3, 0, '2020-08-01 14:56:49', '2020-08-08 15:19:56', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100067, 150725316141101032, '江南一点雨', '/profile/site/system/063a8d1d-1e81-4c0d-9c43-53cc8a873d7a.jpg', '技术文章分享', 'http://www.javaboy.org', 4, 0, '2020-08-01 14:56:49', '2020-08-08 15:20:07', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100068, 150725316141101032, '程序猿DD', '/profile/site/system/9625e95b-8a7b-4548-8e57-5a191b306f2f.jpg', 'Java Spring Boot Spring Cloud 最新干货分享', 'http://blog.didispace.com', 5, 0, '2020-08-01 14:56:49', '2020-08-08 15:20:11', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100069, 150725316141101006, 'Iconfinder', '/profile/site/system/e3325f68179436ccfc25b9f0ffff5a39.png', '2,100,000+个免费和高级矢量图标。', 'https://www.iconfinder.com', 1, 0, '2020-08-01 14:58:10', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100070, 150725316141101006, 'iconfont', '/profile/site/system/e1f63337915f79f8bcad1952adb9f6e1.png', '阿里巴巴矢量图标库。', 'http://www.iconfont.cn/', 2, 0, '2020-08-01 14:58:10', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100071, 150725316141101006, 'iconmonstr', '/profile/site/system/afd4885651455f12dcac4f214460dd99.png', '您的下一个项目的免费简单图标。', 'https://iconmonstr.com/', 3, 0, '2020-08-01 14:58:11', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100072, 150725316141101006, 'FindIcons', '/profile/site/system/0171a46b0f643752aa90aa314a22a546.png', '搜索300,000个免费图标。', 'https://findicons.com/', 4, 0, '2020-08-01 14:58:11', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100073, 150725316141101006, 'Icon Archive', '/profile/site/system/40c43a8932f24370cf456789b2ab51db.png', '搜索590,912个免费图标。', 'http://www.iconarchive.com/', 5, 0, '2020-08-01 14:58:11', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100074, 150725316141101006, 'IcoMoonApp', '/profile/site/system/d19c97ead3760f1b70efa4ee9ad6859c.png', '图标字体，SVG，PDF和PNG生成器。', 'https://icomoon.io/app/', 6, 0, '2020-08-01 14:58:11', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100075, 150725316141101006, 'easyicon', '/profile/site/system/34b4382075e047c6d1456f8fe591a1ef.png', 'PNG、ICO、ICNS格式图标搜索、图标下载服务。', 'http://www.easyicon.net/', 7, 0, '2020-08-01 14:58:11', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100076, 150725316141101006, 'flaticon', '/profile/site/system/582cf7361a0b4f444628c68b98e5cfc7.png', '634,000+免费矢量图标为SVG，PSD，PNG，EPS格式或图标字体。', 'https://www.flaticon.com/', 8, 0, '2020-08-01 14:58:11', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100077, 150725316141101006, 'UICloud', '/profile/site/system/f9840e127d500449da1c5c721f3634c3.png', '世界上最大的用户界面设计数据库。', 'http://ui-cloud.com/', 9, 0, '2020-08-01 14:58:11', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100078, 150725316141101006, 'Material icons', '/profile/site/system/32a037ffbdd2f97f6f6e62e56321c519.png', '可访问900多种材质系统图标，这些图标以各种大小和密度提供，并且可以作为网络字体使用。', 'https://material.io/icons/', 10, 0, '2020-08-01 14:58:11', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100079, 150725316141101006, 'Font Awesome Icon', '/profile/site/system/88440b8b0d5dc43a3f766670e2d11746.png', '惊人的675个图标的完整集合。', 'https://fontawesome.com/', 11, 0, '2020-08-01 14:58:11', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100080, 150725316141101006, 'ion icons', '/profile/site/system/6d0fd0bf35549f6d61037bd86e2ca242.png', 'Ionic Framework的高级图标字体。', 'http://ionicons.com/', 12, 0, '2020-08-01 14:58:11', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100081, 150725316141101006, 'Simpleline Icons', '/profile/site/system/acf446f1af754f863260cc10dd8d546e.png', '简单的线条图标包。', 'http://simplelineicons.com/', 13, 0, '2020-08-01 14:58:11', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100082, 150725316141101013, 'photoshop', '/profile/site/system/e7117e80fdb340589bc8969900e2af61.png', 'Photoshop不需要解释', 'https://www.adobe.com/cn/products/photoshop.html', 1, 0, '2020-08-01 15:07:48', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100083, 150725316141101013, 'Affinity Designer', '/profile/site/system/290add1cdb3cdb80e6e30af137d48525.png', '专业创意软件', 'https://affinity.serif.com/', 2, 0, '2020-08-01 15:07:48', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100084, 150725316141101013, 'Illustrator', '/profile/site/system/6882fdb094820bae95054ea1c38a3baf.png', '矢量图形和插图。', 'https://www.adobe.com/cn/products/illustrator/', 3, 0, '2020-08-01 15:07:48', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100085, 150725316141101013, 'indesign', '/profile/site/system/4db54894b6751e253212a690dada0df8.png', '页面设计、布局和出版。', 'http://www.adobe.com/cn/products/indesign.html', 4, 0, '2020-08-01 15:07:48', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100086, 150725316141101013, 'cinema-4d', '/profile/site/system/ac0344f03fc1e59b4144fef92a12e211.png', 'Cinema 4D是所有想要快速，轻松地获得惊人效果的3D艺术家的理想选择。', 'https://www.maxon.net/en/products/cinema-4d/overview/', 5, 0, '2020-08-01 15:07:49', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100087, 150725316141101013, '3ds-max', '/profile/site/system/3aacac5d23583cdc250a970a0e30a9aa.png', '3D建模，动画和渲染软件', 'https://www.autodesk.com/products/3ds-max/overview', 6, 0, '2020-08-01 15:07:49', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100088, 150725316141101013, 'Blender', '/profile/site/system/5d61addac4350caee364f0a3e850a3f7.png', 'Blender是免费的开源3D创建套件。', 'https://www.blender.org/', 7, 0, '2020-08-01 15:07:49', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100089, 150725316141101014, 'Sketch', '/profile/site/system/75cc5b5775361d5f0b471b706a115403.png', '数字设计工具包', 'https://sketchapp.com/', 1, 0, '2020-08-01 15:09:23', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100090, 150725316141101014, 'Adobe XD', '/profile/site/system/9eda46042e2ad058951fa4e4bb3a9957.png', '引入Adobe XD。 设计。 原型。 经验。', 'http://www.adobe.com/products/xd.html', 2, 0, '2020-08-01 15:09:23', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100091, 150725316141101014, 'invisionapp', '/profile/site/system/7d5620f1b4fd85c4a7ea4733deed8823.png', '强大的设计原型工具', 'https://www.invisionapp.com/', 3, 0, '2020-08-01 15:09:23', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100092, 150725316141101014, 'marvelapp', '/profile/site/system/28ec81158c67d9783afccf8fcd1bbee6.png', '简单的设计，原型制作和协作', 'https://marvelapp.com/', 4, 0, '2020-08-01 15:09:23', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100093, 150725316141101014, 'Muse CC', '/profile/site/system/612cf530fe80b5f28dc826c4384087bf.png', '无需利用编码即可进行网站设计。', 'https://creative.adobe.com/zh-cn/products/download/muse', 5, 0, '2020-08-01 15:09:23', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100094, 150725316141101014, 'figma', '/profile/site/system/0623aab0bc72437206deca9d4c55df1c.png', '使用Figma一站式设计，制作原型并收集反馈。', 'https://www.figma.com/', 6, 0, '2020-08-01 15:09:23', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100095, 150725316141101015, 'Adobe After Effects CC', '/profile/site/system/01306292e590f37b934785ed67288f80.png', '电影般的视觉效果和动态图形。', 'https://www.adobe.com/cn/products/aftereffects/', 1, 0, '2020-08-01 15:14:56', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100096, 150725316141101015, 'principle', '/profile/site/system/485a410f2076ad20856199caa300f548.png', '激发您的想法，设计更好的应用程序', 'http://principleformac.com/', 2, 0, '2020-08-01 15:14:56', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100097, 150725316141101015, 'flinto', '/profile/site/system/f655d57c7d752c429510e0f649d69666.png', 'Flinto是Mac应用程序，世界各地的顶级设计师都使用它来创建其应用程序设计的交互式动画原型。', 'https://www.flinto.com/', 3, 0, '2020-08-01 15:14:56', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100098, 150725316141101015, 'framer', '/profile/site/system/e64c3cd0283a3bf6a75c9c4ba821049d.png', '从详细的图标到高保真的交互，一切都设计在一个地方。', 'https://framer.com/', 4, 0, '2020-08-01 15:14:56', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100099, 150725316141101015, 'ProtoPie', '/profile/site/system/bdbb0e0485d816b88c75c9276d273873.png', '高保真交互原型设计', 'http://www.protopie.cn/', 5, 0, '2020-08-01 15:14:56', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100100, 150725316141101016, 'khroma', '/profile/site/system/8b158c18b49f0160100086bfcdbe158d.png', 'Khroma是发现，搜索和保存要使用的颜色组合的最快方法。', 'http://khroma.co/generator/', 1, 0, '2020-08-01 15:16:58', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100101, 150725316141101016, 'uigradients', '/profile/site/system/9842ff5c221d6411444d3c13660ba097.png', '美丽的彩色渐变', 'https://uigradients.com', 2, 0, '2020-08-01 15:16:58', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100102, 150725316141101016, 'gradients', '/profile/site/system/64d0ac5dc78b65d83ba500df5b1eab30.png', '为设计师和开发人员设计的渐变色', 'http://gradients.io/', 3, 0, '2020-08-01 15:16:58', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100103, 150725316141101016, 'Coolest', '/profile/site/system/9b77eaad5ef27823b9feb3f765b9d593.png', '最酷的精选渐变色为您的下一个超级⚡惊人的东西', 'https://webkul.github.io/coolhue/', 4, 0, '2020-08-01 15:16:58', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100104, 150725316141101016, 'webgradients', '/profile/site/system/49bfc25c217107d7209eea098ad0307c.png', 'WebGradients是180个线性渐变的免费集合，您可以将其用作网站任何部分的内容背景。', 'https://webgradients.com/', 5, 0, '2020-08-01 15:16:58', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100105, 150725316141101016, 'grabient', '/profile/site/system/8ab1a1044ef9bc5c306c60b81d83b0a2.png', '2017 Grabient展开', 'https://www.grabient.com/', 6, 0, '2020-08-01 15:16:58', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100106, 150725316141101016, 'thedayscolor', '/profile/site/system/6e63366cb896fa19e204cf6b95691062.png', '日常色彩摘要', 'http://www.thedayscolor.com/', 7, 0, '2020-08-01 15:16:58', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100107, 150725316141101016, 'flatuicolors', '/profile/site/system/0b6e14ae22ff962a96ad66de4fc86aff.png', '从平面UI主题复制粘贴调色板', 'http://flatuicolors.com/', 8, 0, '2020-08-01 15:16:58', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100108, 150725316141101016, 'coolors', '/profile/site/system/9176968478c5c42ed20bce8b69f25bf6.png', '超快速配色方案生成器！', 'https://coolors.co/', 9, 0, '2020-08-01 15:16:58', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100109, 150725316141101016, 'Adobe Color CC', '/profile/site/system/ff4d69bedb642bd132297ed22018369b.png', '使用色轮创建配色方案或浏览“颜色”社区中的数千种颜色组合。', 'https://color.adobe.com/zh/create/color-wheel', 10, 0, '2020-08-01 15:16:58', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100110, 150725316141101016, 'flatuicolorpicker', '/profile/site/system/2faf82318597d846e9522c5f52500031.png', 'UI设计的最佳平面颜色', 'http://www.flatuicolorpicker.com/', 11, 0, '2020-08-01 15:16:58', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100111, 150725316141101016, 'trianglify', '/profile/site/system/88261a86b35e5b015bbe35ab9141bc8f.png', '三角生成器', 'http://qrohlf.com/trianglify-generator/', 12, 0, '2020-08-01 15:16:58', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100112, 150725316141101016, 'klart', '/profile/site/system/c51065aaec56c7c65aafd40f4797dba0.png', '每周都会为您的收件箱提供漂亮的颜色和设计', 'https://klart.co/colors/', 13, 0, '2020-08-01 15:16:58', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100113, 150725316141101016, 'vanschneider', '/profile/site/system/4690e9281c23d5fc9df0e2cfbe018edd.png', '“颜色声明”由Tobias van Schneider于2012年创建，目标是为我的未来项目收集和组合独特的颜色。', 'http://www.vanschneider.com/colors', 14, 0, '2020-08-01 15:16:58', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100114, 150725316141101017, 'tinypng', '/profile/site/system/9344c4d9769745c1e63d8f1e7b2f3f25.png', '通过质量和文件大小的完美平衡来优化图像。', 'https://tinypng.com/', 1, 0, '2020-08-01 15:17:33', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100115, 150725316141101017, 'Android 9 patch', '/profile/site/system/dc5b75e3455673384a8f738429789d4b.png', 'Android 9补丁阴影生成器完全可自定义阴影', 'http://inloop.github.io/shadow4android/', 2, 0, '2020-08-01 15:17:33', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100116, 150725316141101017, 'screen sizes', '/profile/site/system/d79583290bc400c0e8a2629d0e7f9f63.png', '流行设备的视口大小和像素密度', 'http://screensiz.es/', 3, 0, '2020-08-01 15:17:33', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100117, 150725316141101017, 'svgomg', '/profile/site/system/f573dc81e4689cb9ce482f35a6fb82f1.png', 'SVG在线压缩平台', 'https://jakearchibald.github.io/svgomg/', 4, 0, '2020-08-01 15:17:33', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100118, 150725316141101017, '稿定抠图', '/profile/site/system/0a6b1b1ea1d5ca5cb49e8cf95470a3b5.png', '免费在线抠图软件,图片快速换背景-抠白底图', 'https://www.gaoding.com', 5, 0, '2020-08-01 15:17:33', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100119, 150725316141101017, '中文转换英文首字母', '/profile/site/system/0fcf02f7a6dc850af65961ce29e4b4cd.png', '在线中文转换英文首字母', 'http://tool.geshanzsq.com/chinese', 6, 0, '2020-08-01 15:17:33', '2020-08-07 10:54:12', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100120, 150725316141101017, 'php数组转Json', '/profile/site/system/9a1139692d3d565d5bb15c816548f13b.png', '在线php数组转Json', 'http://tool.geshanzsq.com/phpToJson', 7, 0, '2020-08-01 15:17:33', '2020-08-07 10:53:01', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100121, 150725316141101017, '在线工具', '/profile/site/system/a2143416-fe6d-4cd5-8a58-bc9ab3911a6a.jpg', '程序员的工具箱', 'https://tool.lu', 8, 0, '2020-08-01 15:17:33', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100122, 150725316141101018, 'wappalyzer', '/profile/site/system/60696fcbba523de88eca68121dee7be7.png', '识别网站上的技术', 'https://www.wappalyzer.com/', 1, 0, '2020-08-01 15:19:02', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100123, 150725316141101018, 'Panda', '/profile/site/system/35e1bbf29c1116cb1dbb703b52ea2ae9.png', '专为提高生产力而打造的智能新闻阅读器。', 'http://usepanda.com/', 2, 0, '2020-08-01 15:19:02', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100124, 150725316141101018, 'sizzy', '/profile/site/system/5ca0b65bcc3606640ba1b4aadd25c7df.png', '快速开发响应式网站的工具', 'https://sizzy.co/', 3, 0, '2020-08-01 15:19:02', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100125, 150725316141101018, 'csspeeper', '/profile/site/system/84fedbd61bf8c93726b713bae36a88ae.png', '专为设计师量身定制的智能CSS查看器。', 'https://csspeeper.com/', 4, 0, '2020-08-01 15:19:02', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100126, 150725316141101018, 'insight', '/profile/site/system/fc5a318293079a2674f1d92f3dce7650.png', '云端上类似IDE的代码搜索和导航', 'http://insight.io/', 5, 0, '2020-08-01 15:19:02', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100127, 150725316141101018, 'mustsee', '/profile/site/system/709ff744a41559fa06b8e8dc199206a3.png', '在每个打开的标签页中发现世界上最美丽的地方。', 'http://mustsee.earth/', 6, 0, '2020-08-01 15:19:02', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100128, 150725316141101019, 'Producthunt', '/profile/site/system/a1cc88fa0a3bf74349ba9c08a67abdc7.png', '发现新鲜有趣的产品。', 'https://www.producthunt.com/', 1, 0, '2020-08-01 15:25:50', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100129, 150725316141101019, 'NEXT', '/profile/site/system/0e2b6c9a5afd4f83d2e22632b08f56ef.png', '不错过任何一个新产品。', 'http://next.36kr.com/posts', 2, 0, '2020-08-01 15:25:50', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100130, 150725316141101019, '少数派', '/profile/site/system/2d01ac82bb496b607c43e7b2b29cd069.png', '高品质数字消费指南。', 'https://sspai.com/', 3, 0, '2020-08-01 15:25:51', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100131, 150725316141101019, '利器', '/profile/site/system/98e2ee62a90b6243630f4aa479b4b47b.png', '创造者和他们的工具。', 'http://liqi.io/', 4, 0, '2020-08-01 15:25:51', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100132, 150725316141101019, 'Today', '/profile/site/system/1726189292537c3a2733ebc673b7f1d6.png', '为身边的新产品喝彩。', 'http://today.itjuzi.com/', 5, 0, '2020-08-01 15:25:51', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100133, 150725316141101019, '小众软件', '/profile/site/system/76b49053ce87ab3c7419c7cdf6fa4f07.png', '在这里发现更多有趣的应用。', 'https://faxian.appinn.com', 6, 0, '2020-08-01 15:25:51', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100134, 150725316141101020, 'Pttrns', '/profile/site/system/1c759dc53774e5758a31fee0401e213a.png', '查看最佳的设计模式，资源，移动应用程序和灵感集合', 'https://www.pttrns.com/', 1, 0, '2020-08-01 15:26:33', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100135, 150725316141101020, 'Collect UI', '/profile/site/system/7e802c77c7bb6c85c1e2bb608a4a13cd.png', '从每日ui档案库及其他收集的每日灵感。', 'http://collectui.com/', 2, 0, '2020-08-01 15:26:33', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100136, 150725316141101020, 'UI uigreat', '/profile/site/system/d140fe4bd548f273ddd00f35e1ad5ff5.png', 'APP界面截图参考。', 'http://ui.uigreat.com/', 3, 0, '2020-08-01 15:26:33', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100137, 150725316141101020, 'Android Niceties', '/profile/site/system/8b6e0af7df3a5d77a14e41a0f5f36dc5.png', '屏幕快照集合，其中包含一些最漂亮的Android应用程序。', 'http://androidniceties.tumblr.com/', 4, 0, '2020-08-01 15:26:33', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100138, 150725316141101021, 'Awwwards', '/profile/site/system/bdd6c88417790c97de2c2d0643cc602c.png', '互联网设计、创意和创新奖', 'https://www.awwwards.com/', 1, 0, '2020-08-01 15:27:02', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100139, 150725316141101021, 'CSS Design Awards', '/profile/site/system/481219fe4285f1f4ec1311acce7deb06.png', '网站奖项与灵感-CSS Gallery', 'https://www.cssdesignawards.com/', 2, 0, '2020-08-01 15:27:02', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100140, 150725316141101021, 'The FWA', '/profile/site/system/8fe5280ff7dc3012fb88781dd9ff4b93.png', 'FWA-自2000年以来每天都在展示创新', 'https://thefwa.com/', 3, 0, '2020-08-01 15:27:02', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100141, 150725316141101021, 'Ecommercefolio', '/profile/site/system/950d52c71e28f1c9ed964732d6ed18fd.png', '只有最佳电子商务设计灵感。', 'http://www.ecommercefolio.com/', 4, 0, '2020-08-01 15:27:02', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100142, 150725316141101021, 'Lapa', '/profile/site/system/1824678ec13d01b76df47fc5975178fa.png', '最佳登陆页面设计灵感来自网络。', 'http://www.lapa.ninja/', 5, 0, '2020-08-01 15:27:02', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100143, 150725316141101021, 'Reeoo', '/profile/site/system/5205b768b9b640bfada244ce9d15318d.png', '网页设计灵感和网站库。', 'http://reeoo.com/', 6, 0, '2020-08-01 15:27:02', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100144, 150725316141101021, 'Designmunk', '/profile/site/system/31de409b71235b76d605e98293d68cb3.png', '最佳首页设计灵感。', 'https://designmunk.com/', 7, 0, '2020-08-01 15:27:02', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100145, 150725316141101021, 'Best Websites Gallery', '/profile/site/system/862823249aa701d8bc8af16ae98f1e3a.png', '网站展示灵感| 最佳网站画廊。', 'https://bestwebsite.gallery/', 8, 0, '2020-08-01 15:27:02', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100146, 150725316141101021, 'Pages', '/profile/site/system/90fd20bd3e7ae7c7fe37ea689dcca32c.png', '最佳网页的精选目录。', 'http://www.pages.xyz/', 9, 0, '2020-08-01 15:27:02', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100147, 150725316141101021, 'SiteSee', '/profile/site/system/da24d08a597456be98191b4a08eff4d6.png', '精选的精美，现代网站集的画廊。', 'https://sitesee.co/', 10, 0, '2020-08-01 15:27:02', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100148, 150725316141101021, 'Site Inspire', '/profile/site/system/c15c9017ad6874faae0df64bd969115b.png', '一个CSS画廊和最佳网页设计灵感的展示。', 'https://www.siteinspire.com/', 11, 0, '2020-08-01 15:27:02', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100149, 150725316141101021, 'WebInspiration', '/profile/site/system/f8fe63594e2083755086ee294b036108.png', '网页设计欣赏,全球顶级网页设计。', 'http://web.uedna.com/', 12, 0, '2020-08-01 15:27:02', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100150, 150725316141101021, 'navnav', '/profile/site/system/86b9e596068f2a71d2a2444733a4094e.png', '来自网络的大量CSS，jQuery和JavaScript响应式导航示例，演示和教程。', 'http://navnav.co/', 13, 0, '2020-08-01 15:27:02', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100151, 150725316141101021, 'Really Good UX', '/profile/site/system/948b0f5b62e59ef0a97edf4b9a51c404.png', '屏幕截图库和非常好的UX示例。 带给你的。', 'https://www.reallygoodux.io/', 14, 0, '2020-08-01 15:27:02', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100152, 150725316141101022, 'Dribbble', '/profile/site/system/7db1257f40b9b04482744387a00b359d.png', '全球UI设计师作品分享平台。', 'https://dribbble.com/', 1, 0, '2020-08-01 15:38:39', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100153, 150725316141101022, 'Iconsfeed', '/profile/site/system/aee21da67d9771c2ebf3f6779afc9649.png', 'iOS图标库。', 'http://www.iconsfeed.com/', 2, 0, '2020-08-01 15:38:39', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100154, 150725316141101022, 'iOS Icon Gallery', '/profile/site/system/98c9f52ede06a56532d5d16afda9d570.png', '展示来自iOS App Store的精美图标设计。', 'http://iosicongallery.com/', 3, 0, '2020-08-01 15:38:39', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100155, 150725316141101022, 'World Vector Logo', '/profile/site/system/c8da40d11f726d974293efc40c9acfb5.png', '免费下载品牌徽标。', 'https://worldvectorlogo.com/', 4, 0, '2020-08-01 15:38:40', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100156, 150725316141101022, 'Instant Logo Search', '/profile/site/system/907f35950eae72526a314306cc59efa7.png', '立即搜索和下载数千个徽标。', 'http://instantlogosearch.com/', 5, 0, '2020-08-01 15:38:40', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100157, 150725316141101023, 'freepik', '/profile/site/system/6a96564b2d100bad3674db5e56794a97.png', '超过一百万的免费矢量，PSD，照片和免费图标。', 'https://www.freepik.com/', 1, 0, '2020-08-01 15:39:26', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100158, 150725316141101023, 'wallhalla', '/profile/site/system/9354f99621e8530d0f996fe4b96ad2c3.png', '在一处找到用于台式机和手机的超酷优质壁纸。', 'https://wallhalla.com/', 2, 0, '2020-08-01 15:39:26', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100159, 150725316141101023, '365PSD', '/profile/site/system/13b10a2f9388e83101d7a35b83ff28bc.png', '免费PSD和图形，插图。', 'https://365psd.com/', 3, 0, '2020-08-01 15:39:26', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100160, 150725316141101023, 'Medialoot', '/profile/site/system/b117eb768a44d662ded91d1f0a9cb1c2.png', '免费和高级设计资源-Medialoot。', 'https://medialoot.com/', 4, 0, '2020-08-01 15:39:26', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100161, 150725316141101023, '千图网', '/profile/site/system/9a24027c0e9d498efb4ad88a330882f8.png', '专注免费设计素材下载的网站。', 'http://www.58pic.com/', 5, 0, '2020-08-01 15:39:26', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100162, 150725316141101023, '千库网', '/profile/site/system/15ffa7b3a5cab15c7d23d402be12cc4c.png', '免费png图片背景素材下载。', 'http://588ku.com/', 6, 0, '2020-08-01 15:39:26', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100163, 150725316141101023, '我图网', '/profile/site/system/a887a255bbe7fe994e0479ae988372c7.png', '我图网,提供图片素材及模板下载,专注正版设计作品交易。', 'http://www.ooopic.com/', 7, 0, '2020-08-01 15:39:26', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100164, 150725316141101023, '90设计', '/profile/site/system/c510c1946d6191a98c6fd3b08ca720ec.png', '电商设计（淘宝美工）千图免费淘宝素材库。', 'http://90sheji.com/', 8, 0, '2020-08-01 15:39:26', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100165, 150725316141101023, '昵图网', '/profile/site/system/d4fba2a16c7a1692ea21f4f0a8ae7672.png', '原创素材共享平台。', 'http://www.nipic.com/', 9, 0, '2020-08-01 15:39:26', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100166, 150725316141101023, '懒人图库', '/profile/site/system/a7e5f98173ea111df83da146a86436a1.png', '懒人图库专注于提供网页素材下载。', 'http://www.lanrentuku.com/', 10, 0, '2020-08-01 15:39:26', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100167, 150725316141101023, '素材搜索', '/profile/site/system/cbca7fabfd7c6d1b117547466bc564ad.png', '设计素材搜索聚合。', 'http://so.ui001.com/', 11, 0, '2020-08-01 15:39:26', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100168, 150725316141101023, 'PS饭团网', '/profile/site/system/7ffad2eac8cbad395d33914344d3aa0a.png', '不一样的设计素材库！让自己的设计与众不同！', 'http://psefan.com/', 12, 0, '2020-08-01 15:39:26', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100169, 150725316141101023, '素材中国', '/profile/site/system/ced6b2a53069c7d360ba78706244081f.png', '免费素材共享平台。', 'http://www.sccnn.com/', 13, 0, '2020-08-01 15:39:26', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100170, 150725316141101024, 'UI中国', '/profile/site/system/9458ececbfeea651b5e871179f245ce5.png', '图形交互与界面设计交流、作品展示、学习平台。', 'http://www.ui.cn/', 1, 0, '2020-08-01 15:39:53', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100171, 150725316141101024, 'Freebiesbug', '/profile/site/system/4288052485ced84952e206a4acfb92ad.png', '网页设计人员和开发人员的精选资源不断更新。', 'https://freebiesbug.com/', 2, 0, '2020-08-01 15:39:53', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100172, 150725316141101024, 'Freebie Supply', '/profile/site/system/2ae393ad916a108ba20d21a8b907477e.png', '设计师的免费资源。', 'https://freebiesupply.com/', 3, 0, '2020-08-01 15:39:53', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100173, 150725316141101024, '云瑞', '/profile/site/system/528f9304b0dab49f5fe2426d4d9d047c.png', '优秀设计资源的分享网站。', 'https://www.yrucd.com/', 4, 0, '2020-08-01 15:39:53', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100174, 150725316141101024, 'Designmodo', '/profile/site/system/2061e0ccebfbb1a94a28d86237589b23.png', '网页设计博客和商店。', 'https://designmodo.com/', 5, 0, '2020-08-01 15:39:53', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100175, 150725316141101024, '稀土区', '/profile/site/system/4f5171443ad0ec6b13b7f96b8cead4bd.png', '优质设计开发资源分享。', 'https://xituqu.com/', 6, 0, '2020-08-01 15:39:53', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100176, 150725316141101024, 'ui8', '/profile/site/system/b815917aad63f449a96900979a16eb4e.png', 'UI套件，线框套件，模板，图标等。', 'https://ui8.net/', 7, 0, '2020-08-01 15:39:53', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100177, 150725316141101024, 'uplabs', '/profile/site/system/42d3d29c62a19e8d4ca6395586d79ee7.png', '产品设计师和开发人员的日常资源。', 'https://www.uplabs.com/', 8, 0, '2020-08-01 15:39:53', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100178, 150725316141101024, 'UIkit.me', '/profile/site/system/13b4ac8efdc9f92e52e7f271b8034b24.png', '最便捷新鲜的uikit资源下载网站。', 'http://www.uikit.me/', 9, 0, '2020-08-01 15:39:53', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100179, 150725316141101024, 'Fribbble', '/profile/site/system/04c1b51de97ceac330358fa7d1685034.png', 'Dribbblers提供了免费的PSD文件和其他免费设计资源。', 'http://www.fribbble.com/', 10, 0, '2020-08-01 15:39:53', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100180, 150725316141101024, 'PrincipleRepo', '/profile/site/system/e3e93f407dcc94461bea06979e89e4a2.png', '免费的高质量原则资源。', 'http://principlerepo.com/', 11, 0, '2020-08-01 15:39:53', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100181, 150725316141101025, 'Sketch', '/profile/site/system/ff6a2f8afaeb91004416c96788f9da95.png', '数字设计工具包。', 'https://sketchapp.com/', 1, 0, '2020-08-01 15:40:26', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100182, 150725316141101025, 'Sketch Measure', '/profile/site/system/69bf814d311d932ea13b746ffc1f9f54.png', '友好的用户界面为您提供了一种更直观的标记方式。', 'http://utom.design/measure/', 2, 0, '2020-08-01 15:40:26', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100183, 150725316141101025, 'Sketch App Sources', '/profile/site/system/ccf82c5a27a285ba63cf3c4ff8964a25.png', '免费的设计资源和插件-图标，UI套件，线框，iOS，Android草图模板', 'https://www.sketchappsources.com/', 3, 0, '2020-08-01 15:40:26', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100184, 150725316141101025, 'Sketch.im', '/profile/site/system/6055276a55b62db423c2b060d3f6b044.png', 'Sketch 相关资源汇聚。', 'http://www.sketch.im/', 4, 0, '2020-08-01 15:40:26', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100185, 150725316141101025, 'Sketch Hunt', '/profile/site/system/c4bf007a61d761db1f895672a2519cd0.png', 'Sketch Hunt是一个独立的博客，为Sketch应用程序的爱好者分享学习，插件和设计工具方面的瑰宝。', 'http://sketchhunt.com/', 5, 0, '2020-08-01 15:40:26', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100186, 150725316141101025, 'Sketch中文网', '/profile/site/system/a26a90da0d304cd3502cdb53e85995b9.png', '分享最新的Sketch中文手册。', 'http://www.sketchcn.com/', 6, 0, '2020-08-01 15:40:26', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100187, 150725316141101025, 'Awesome Sketch Plugins', '/profile/site/system/255cf49affef2fbaba8cd15c3e7329b5.png', '真正有用的Sketch插件的集合。', 'https://awesome-sket.ch/', 7, 0, '2020-08-01 15:40:26', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100188, 150725316141101025, 'Sketchcasts', '/profile/site/system/fa8c8b179ab48ad61e61a18d1720e019.png', '学习素描通过每周的视频教程来训练您的设计技能', 'https://www.sketchcasts.net/', 8, 0, '2020-08-01 15:40:26', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100189, 150725316141101026, 'Google Font', '/profile/site/system/91c604a4ca98b1bb5719e04c80043419.png', '通过出色的排版，使网络更加美观，快速和开放。', 'https://fonts.google.com/', 1, 0, '2020-08-01 15:40:55', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100190, 150725316141101026, 'Typekit', '/profile/site/system/7dbc17741d30274995615612dc1d075f.png', '来自世界上最好的铸造厂的高质量字体。', 'https://typekit.com/', 2, 0, '2020-08-01 15:40:55', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100191, 150725316141101026, '方正字库', '/profile/site/system/8ffeec1d3ad96a39dd4ede9794756b87.png', '方正字库官方网站。', 'http://www.foundertype.com/', 3, 0, '2020-08-01 15:40:55', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100192, 150725316141101026, '字体传奇网', '/profile/site/system/d5fc1ea541fe215ae449a0ae27a09a76.png', '中国首个字体品牌设计师交流网。', 'http://ziticq.com/', 4, 0, '2020-08-01 15:40:55', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100193, 150725316141101026, '私藏字体', '/profile/site/system/125e538efd8b3ea68d3655cb81ccc06f.png', '优质字体免费下载站。', 'http://sicangziti.com/', 5, 0, '2020-08-01 15:40:55', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100194, 150725316141101026, 'Fontsquirrel', '/profile/site/system/94684e5203623eb5540a4b5a0e0b70b0.png', '图形设计师的免费字体。', 'https://www.fontsquirrel.com/', 6, 0, '2020-08-01 15:40:55', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100195, 150725316141101026, 'Urban Fonts', '/profile/site/system/40139afeda032d1a3cd54459d065b31b.png', '下载免费字体和免费Dingbats。', 'https://www.urbanfonts.com/', 7, 0, '2020-08-01 15:40:55', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100196, 150725316141101026, 'Lost Type', '/profile/site/system/cfe0904ec3e37914be51687a2b15f5cf.png', '失物招领是一个协作数字类型代工厂。', 'http://www.losttype.com/', 8, 0, '2020-08-01 15:40:55', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100197, 150725316141101026, 'FONTS2U', '/profile/site/system/19bd844dc123066620d1f6fda7287e48.png', '为Windows和Macintosh下载免费字体。', 'https://fonts2u.com/', 9, 0, '2020-08-01 15:40:55', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100198, 150725316141101026, 'Fontex', '/profile/site/system/1576a7303fb2fa3839016a599418203b.png', '免费字体下载+高级字体。', 'http://www.fontex.org/', 10, 0, '2020-08-01 15:40:55', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100199, 150725316141101026, 'FontM', '/profile/site/system/5ee459b3c52027eb8dcd9c8c6e9266a0.png', '免费字体。', 'http://fontm.com/', 11, 0, '2020-08-01 15:40:55', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100200, 150725316141101026, 'My Fonts', '/profile/site/system/22572b2d9b38ea02f173074d59acf334.png', '用于印刷，产品和屏幕的字体。', 'http://www.myfonts.com/', 12, 0, '2020-08-01 15:40:55', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100201, 150725316141101026, 'Da Font', '/profile/site/system/d0478f80b89bff215437714e62c6d997.png', '可免费下载的字体的存档。', 'https://www.dafont.com/', 13, 0, '2020-08-01 15:40:55', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100202, 150725316141101026, 'OnlineWebFonts', '/profile/site/system/bccc59c04f6f283a63430700273ffdee.png', '适用于Windows和Mac的WEB免费字体/免费下载字体。', 'https://www.onlinewebfonts.com/', 14, 0, '2020-08-01 15:40:55', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100203, 150725316141101026, 'Abstract Fonts', '/profile/site/system/fbedc66f865056e650a036f042625057.png', '抽象字体（13,866个免费字体）。', 'http://www.abstractfonts.com/', 15, 0, '2020-08-01 15:40:55', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100204, 150725316141101027, 'MockupZone', '/profile/site/system/75e599ff2f061dc25fa272de94045ca9.png', 'Mockup Zone是一个在线商店，您可以在其中找到免费的高级PSD样机文件，以专业的方式展示您的设计。', 'https://mockup.zone/', 1, 0, '2020-08-01 15:41:42', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100205, 150725316141101027, 'Dunnnk', '/profile/site/system/7949d12743b95779412dd8673c324164.png', '免费生成产品模型。', 'http://dunnnk.com/', 2, 0, '2020-08-01 15:41:42', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100206, 150725316141101027, 'Graphberry', '/profile/site/system/1216f3642b463e7e9d493e4d00506566.png', '免费设计资源，样机，PSD Web模板，图标。', 'http://www.graphberry.com/', 3, 0, '2020-08-01 15:41:42', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100207, 150725316141101027, 'Threed', '/profile/site/system/252114418dc086100cd58d10035a9436.png', '直接在浏览器中生成3D模型', 'http://threed.io/', 4, 0, '2020-08-01 15:41:42', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100208, 150725316141101027, 'Mockup World', '/profile/site/system/c94ee98e4ada29c0916888820da31744.png', '网上最好的免费模型', 'https://free.lstore.graphics/', 5, 0, '2020-08-01 15:41:42', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100209, 150725316141101027, 'Lstore', '/profile/site/system/965f25d08ae3cd33fab21d764a514967.png', '面向设计师和开发人员的独家令人兴奋的免费赠品。', 'https://free.lstore.graphics/', 6, 0, '2020-08-01 15:41:42', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100210, 150725316141101027, 'pixeden', '/profile/site/system/f8b5261bc1d5e5189b9c1216a6de8b3b.png', '免费的网络资源和图形设计模板。', 'https://www.pixeden.com/', 7, 0, '2020-08-01 15:41:42', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100211, 150725316141101027, 'For Graphic TM', '/profile/site/system/20fceec1b9dd6c1183ad73a90becce7f.png', '适用于图形设计师的高质量PSD样机。', 'http://forgraphictm.com/', 8, 0, '2020-08-01 15:41:42', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100212, 150725316141101028, 'Unsplash', '/profile/site/system/72880b02dbea40fd84472abc05e6d23b.png', '漂亮的免费照片。', 'https://unsplash.com/', 1, 0, '2020-08-01 15:42:16', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100213, 150725316141101028, 'visualhunt', '/profile/site/system/b2a1a1e4c043858ac2411f02b9236ff3.png', '100％免费的高质量照片。', 'https://visualhunt.com/', 2, 0, '2020-08-01 15:42:16', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100214, 150725316141101028, 'librestock', '/profile/site/system/94c5305f78dfadb241f9edcf3d9b870d.png', '65,084高品质的照片，随您想要。', 'https://librestock.com/', 3, 0, '2020-08-01 15:42:16', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100215, 150725316141101028, 'pixabay', '/profile/site/system/310cb7b52774323c7fdffe67aa0f12aa.png', '可在任何地方使用的免费图片和视频。', 'https://pixabay.com/', 4, 0, '2020-08-01 15:42:16', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100216, 150725316141101028, 'SplitShire', '/profile/site/system/0e9933021af7cc4714e900c247010b30.png', '免费图片和视频供商业使用。', 'https://www.splitshire.com/', 5, 0, '2020-08-01 15:42:16', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100217, 150725316141101028, 'StockSnap', '/profile/site/system/fabf86558eb3a7c943c124f7f62f3542.png', '美丽的免费图片素材。', 'https://stocksnap.io/', 6, 0, '2020-08-01 15:42:16', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100218, 150725316141101028, 'albumarium', '/profile/site/system/de8b7f26a21ea0b781f93a3163341731.png', '查找和分享精美图像的最佳场所。', 'http://albumarium.com/', 7, 0, '2020-08-01 15:42:16', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100219, 150725316141101028, 'myphotopack', '/profile/site/system/80d85ea59d293bd43731a890f63c5dc9.png', '专门为您提供的免费照片包。 每个月。', 'https://myphotopack.com/', 8, 0, '2020-08-01 15:42:16', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100220, 150725316141101028, 'Notaselfie', '/profile/site/system/eb5f9a9661e582883c9d3128bb9b4482.png', '一路上发生的照片。 您可以随时使用图像。 玩得开心！', 'http://notaselfie.com/', 9, 0, '2020-08-01 15:42:16', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100221, 150725316141101028, 'papers', '/profile/site/system/3a6396ba24d253502f40432751a11b07.png', '每小时都有墙纸！手工收集', 'http://papers.co/', 10, 0, '2020-08-01 15:42:16', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100222, 150725316141101028, 'stokpic', '/profile/site/system/9dce238279b24893eaa20a99fba802ea.png', '免费图片供商业使用。', 'http://stokpic.com/', 11, 0, '2020-08-01 15:42:16', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100223, 150725316141101028, '55mm', '/profile/site/system/dd8adcbc65cc20e8fb6d6335fd57814a.png', '使用我们的免费照片讲述您的故事！', 'https://55mm.co/visuals', 12, 0, '2020-08-01 15:42:16', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100224, 150725316141101028, 'thestocks', '/profile/site/system/2be533b5b00139b9022f09604f3bd136.png', '使用我们的免费照片讲述您的故事！', 'http://thestocks.im/', 13, 0, '2020-08-01 15:42:16', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100225, 150725316141101028, 'freenaturestock', '/profile/site/system/85c87259ac26b4f48b084066b9e3ec8e.png', '面向设计师和开发人员的独家令人兴奋的免费赠品。', 'http://freenaturestock.com/', 14, 0, '2020-08-01 15:42:16', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100226, 150725316141101028, 'negativespace', '/profile/site/system/9b470b26c5e7e6604f3f17d2fe518af7.png', '美丽，高分辨率免费图片素材。', 'https://negativespace.co/', 15, 0, '2020-08-01 15:42:16', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100227, 150725316141101028, 'gratisography', '/profile/site/system/37a9bff7f4d756e7b227ef295aa5ff82.png', '免费的高分辨率图片，可用于您的个人和商业项目，不受版权限制。', 'https://gratisography.com/', 16, 0, '2020-08-01 15:42:16', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100228, 150725316141101028, 'imcreator', '/profile/site/system/568ae371ba49ce83463d5833af6a8e88.png', '精选的免费网页设计资源集合，全部用于商业用途。', 'http://imcreator.com/free', 17, 0, '2020-08-01 15:42:16', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100229, 150725316141101028, 'lifeofpix', '/profile/site/system/94bf5d51c1367552f337610dbc6aa44b.png', '免费高分辨率摄影', 'http://www.lifeofpix.com/', 18, 0, '2020-08-01 15:42:16', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100230, 150725316141101028, 'skitterphoto', '/profile/site/system/23663c43cb7025f3bf36e9733bea6171.png', '创意专业人士的免费图片素材', 'https://skitterphoto.com/', 19, 0, '2020-08-01 15:42:16', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100231, 150725316141101028, 'mmtstock', '/profile/site/system/d8d5768d2dc63763480478ae25aa176a.png', '免费商业照片', 'https://mmtstock.com/', 20, 0, '2020-08-01 15:42:16', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100232, 150725316141101028, 'magdeleine', '/profile/site/system/12ca6edef00d1d897eb28c4a8e2f8915.png', '精选免费照片供您启发', 'https://magdeleine.co/browse/', 21, 0, '2020-08-01 15:42:16', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100233, 150725316141101028, 'jeshoots', '/profile/site/system/a016e8d2ae3ee88f0ec136440e92fca8.png', '新的免费照片和样机进入您的收件箱！', 'http://jeshoots.com/', 22, 0, '2020-08-01 15:42:16', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100234, 150725316141101028, 'hdwallpapers', '/profile/site/system/74db036ddf1bbfc49a22a5a6dcd392ab.png', '高清壁纸和桌面背景', 'https://www.hdwallpapers.net', 23, 0, '2020-08-01 15:42:16', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100235, 150725316141101028, 'publicdomainarchive', '/profile/site/system/3c7427a4bab6bb40c12a77014f809a2a.png', '新的100％免费图片。 每一个 单。 周。', 'http://publicdomainarchive.com/', 24, 0, '2020-08-01 15:42:16', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100236, 150725316141101029, 'OfficePLUS', '/profile/site/system/4773ef0cfcf8c9fd158fc7db0bc2cf0b.png', 'OfficePLUS，微软Office官方在线模板网站！', 'http://www.officeplus.cn/Template/Home.shtml', 1, 0, '2020-08-01 15:43:15', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100237, 150725316141101029, '优品PPT', '/profile/site/system/b1d803179735ea628d1d914c63c0b9f7.png', '高质量的模版，而且还有PPT图表，PPT背景图等资源', 'http://www.ypppt.com/', 2, 0, '2020-08-01 15:43:15', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100239, 150725316141101029, 'PPTMind', '/profile/site/system/857bb0f6927c2a8c246653cb41136ce7.png', '分享高端ppt模板与keynote模板的数字作品交易平台', 'http://www.pptmind.com/', 4, 0, '2020-08-01 15:43:15', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100240, 150725316141101029, 'tretars', '/profile/site/system/14a77db5ab4af0ba947b1e1707295c5d.png', '网上最好的免费模型', 'http://www.tretars.com/ppt-templates', 5, 0, '2020-08-01 15:43:15', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100241, 150725316141101029, '5百丁', '/profile/site/system/3f735ae4b6e18cd6cff3965661289aac.png', '中国领先的PPT模板共享平台', 'http://ppt.500d.me/', 6, 0, '2020-08-01 15:43:15', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100276, 150725316141101004, 'PDF Candy', '/profile/site/system/0b897cff5f1f2d98efd8dc7ea69b38ae.ico', '一个免费在线的PDF编辑网站。包含了几十种PDF小工具，可以Word与PDF互转、Excel表格与PDF互转、图片与PDF互转等各种实用功能。', 'https://pdfcandy.com/cn/', 2, 0, '2020-08-05 13:28:17', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100277, 150725316141101004, 'LightPDF', '/profile/site/system/6281eb27f1a6c6f07d56588e5e4849ae.png', '一个只需要一步，即可解决PDF所有问题的免费PDF工具!', 'https://lightpdf.com/zh/', 3, 0, '2020-08-05 13:30:31', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100278, 150725316141101004, 'SmallPDF', '/profile/site/system/341475474199e2cf28ce10308fcad860.png', '轻松玩转PDF，功能一应俱全、简单好用的线上 PDF 工具', 'https://smallpdf.com/cn', 4, 0, '2020-08-05 13:31:49', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100279, 150725316141101004, 'HiPDF', '/profile/site/system/3cac096ab252280f24a70ca5550a0287.ico', '一站式在线PDF解决方案的网站。', 'https://www.hipdf.cn/', 5, 0, '2020-08-05 13:33:37', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100280, 150725316141101004, 'iLovePDF', '/profile/site/system/cc239ae8c064a5bbb65318fb40e02cdf.png', '完全免费、易于使用、丰富的PDF处理工具，包括：合并、拆分、压缩、转换、旋转和解锁PDF文件，以及给PDF文件添加水印的工具等。仅需几秒钟即可完成。', 'https://www.ilovepdf.com/zh-cn', 6, 0, '2020-08-05 13:35:10', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100281, 150725316141101007, 'CSDN', '/profile/site/system/0b91790e27fe8cd3941fc6e6ee57ee4a.jpg', '专业开发者社区', 'https://www.csdn.net/', 2, 0, '2020-08-08 15:12:00', '2020-08-08 15:20:24', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100282, 150725316141101007, '博客园', '/profile/site/system/2ec8affefca78704574f1ba17fe070f2.png', '开发者的网上家园', 'https://www.cnblogs.com/', 1, 0, '2020-08-08 15:13:36', '2020-08-08 15:20:20', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100283, 150725316141101007, '思否', '/profile/site/system/570d66cff03484e48b05d6f02db9dfe4.ico', ' 在 SegmentFault，学习技能、解决问题。', 'https://segmentfault.com/', 4, 0, '2020-08-08 15:16:50', '2020-08-08 15:20:39', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100284, 150725316141101007, '掘金', '/profile/site/system/dcc82ea8e38e1bd994cabb18b8eb4e32.ico', '一个帮助开发者成长的社区。', 'https://juejin.im/', 3, 0, '2020-08-08 15:18:11', '2020-08-08 15:20:35', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100285, 150725316141101031, 'Vue', '/profile/site/system/5ebfa247ff3e3cbcb1ada1c5a9b2506b.png', '渐进式 JavaScript 框架。', 'https://cn.vuejs.org/', 1, 0, '2020-08-08 15:21:55', '2020-08-08 15:23:40', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100286, 150725316141101031, 'React', '/profile/site/system/9e76a67ae80210b18e4db37b45a2574a.ico', '用于构建用户界面的 JavaScript 库。', 'https://react.docschina.org/', 5, 0, '2020-08-08 15:23:35', '2022-06-11 17:30:58', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100287, 150725316141101031, 'Angular', '/profile/site/system/ed0f155ed1b6acc4e6f307543883aa7e.ico', '一套框架，多种平台 移动端 &amp; 桌面端。', 'https://angular.cn/', 6, 0, '2020-08-08 15:25:11', '2022-06-11 17:31:14', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100289, 150725316141101031, 'Element', '/profile/site/system/d636a118f9f69184a730ffa3082fdb80.ico', '一套为开发者、设计师和产品经理准备的基于 Vue 2.0 的桌面端组件库', 'https://element.eleme.cn/', 2, 0, '2020-08-08 15:30:27', '2022-06-11 17:30:38', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100290, 150725316141101031, 'vue-element-admin', '/profile/site/system/d91882f77429ded326a30bda549c329d.png', '一个后台前端解决方案，它基于 vue 和 element-ui 实现。', 'https://panjiachen.gitee.io/vue-element-admin-site/zh/', 4, 0, '2020-08-08 15:32:01', '2022-06-11 17:31:05', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100291, 150725316141101007, '知乎', '/profile/site/system/02090c1f2ff7d580395b7dfe44ba066c.ico', '有问题，上知乎。知乎，可信赖的问答社区，以让每个人高效获得可信赖的解答为使命。', 'https://www.zhihu.com', 5, 0, '2020-08-08 15:34:36', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100292, 150725316141101030, 'Spring', '/profile/site/system/39caf5180b7d84ff2f277b25ca10f6ce.png', 'Spring 框架是一个开源的 Java 平台，它为容易而快速的开发出耐用的 Java 应用程序提供了全面的基础设施。', 'https://spring.io/', 1, 0, '2020-08-08 15:37:04', '2020-08-08 15:38:49', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100293, 150725316141101030, 'MyBatis-Plus', '/profile/site/system/4eb015ca9291887d648fc075f76303ac.png', '为简化开发而生。', 'https://mp.baomidou.com/', 2, 0, '2020-08-08 15:40:12', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100294, 150725316141101030, '力扣 LeetCode', '/profile/site/system/3b0dcb135f4c9a960b385170ce5f3946.png', '全球极客挚爱的技术成长平台。海量技术面试题库,拥有算法、数据结构、系统设计等 1000+题目,帮助你高效提升编程技能,轻松拿下世界 IT 名企 Dream Offer。', 'https://leetcode-cn.com/', 3, 0, '2020-08-08 15:44:12', '2021-01-15 10:49:27', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100295, 150725316141101004, 'Convertio 文件转换器', '/profile/site/system/b5d554962ba1cc493607a32f5328787d.png', '超级强大的文件格式转化器，视频、图片、字体、音频通通都可以。', 'https://convertio.co/zh/', 7, 0, '2020-08-09 13:01:13', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100296, 150725316141101007, '菜鸟教程', '/profile/site/system/b66656c08273c4761073eab6ae59b1ca.ico', '学的不仅是技术，更是梦想！', 'https://www.runoob.com/', 6, 0, '2020-08-09 13:08:03', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100297, 150725316141101031, 'Bootstrap', '/profile/site/system/5285612ea8533aa78c30bfa382171077.ico', '简洁、直观、强悍的前端开发框架，让web开发更迅速、简单。', 'https://www.bootcss.com/', 6, 0, '2020-08-09 13:09:42', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100298, 150725316141101007, 'W3School', '/profile/site/system/4d2b466766140ef66ab6e4e92f73383d.png', '在 W3School，你可以找到你所需要的所有的网站建设教程。', 'https://www.w3school.com.cn/', 7, 0, '2020-08-09 13:14:17', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100299, 150725316141101032, '五分钟学算法', '/profile/site/system/4356bd8feb9ae4b14a75fa81b79a6d2a.png', '一个不错的算法网站！', 'https://www.cxyxiaowu.com/', 6, 0, '2020-08-09 13:15:18', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100300, 150725316141101001, '凌风云搜索', '/profile/site/system/ed2c1e745ad694bbe206dd93c0ff8ea1.jpg', '专注于互联网免费资源的大数据搜索，网盘搜索，云盘资源等', 'https://www.lingfengyun.com/', 9, 0, '2020-08-09 21:53:55', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100303, 150725316141101001, '罗马盘', '/profile/site/system/69d01f6bbf50666a7f5bb36c8e59fcbc.ico', '百度网盘搜索引擎 自动更新网络共享资源', 'https://www.luomapan.com/', 11, 0, '2020-08-09 22:03:54', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100304, 150725316141101001, 'bdyso', '/profile/site/system/b6494b1345e86d25a090af4e96e084b2.ico', '百度网盘资源搜索与分享', 'http://www.bdyso.com/', 12, 0, '2020-08-09 22:05:30', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100306, 150725316141101035, '微信公众号', '/profile/site/system/eb7f53364e44272c8a2ec83d0e83df43.png', '再小的个体，也有自己的品牌。', 'https://mp.weixin.qq.com/', 1, 0, '2020-08-11 17:47:03', '2020-08-11 17:52:22', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100307, 150725316141101035, '头条号', '/profile/site/system/0db580c9e8afe1e543c120a8e026be05.png', '今日头条推出的开放的内容创作与分发平台。', 'https://mp.toutiao.com', 2, 0, '2020-08-11 17:49:28', '2020-08-11 17:51:26', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100308, 150725316141101035, '知乎', '/profile/site/system/02090c1f2ff7d580395b7dfe44ba066c.ico', '有问题，上知乎。知乎，可信赖的问答社区，以让每个人高效获得可信赖的解答为使命。', 'https://www.zhihu.com', 5, 0, '2020-08-11 19:58:24', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100309, 150725316141101035, 'CSDN', '/profile/site/system/eb7c0916-0c5f-4aad-ba1e-6eb7a076ce41.ico', '专业开发者社区', 'https://www.csdn.net/', 6, 0, '2020-08-08 15:12:00', '2021-01-13 15:37:46', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100310, 150725316141101035, '博客园', '/profile/site/system/2ec8affefca78704574f1ba17fe070f2.png', '开发者的网上家园', 'https://www.cnblogs.com/', 7, 0, '2020-08-08 15:13:36', '2020-08-08 15:20:20', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100311, 150725316141101035, '思否', '/profile/site/system/570d66cff03484e48b05d6f02db9dfe4.ico', ' 在 SegmentFault，学习技能、解决问题。', 'https://segmentfault.com/', 8, 0, '2020-08-08 15:16:50', '2020-08-08 15:20:39', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100312, 150725316141101035, '掘金', '/profile/site/system/dcc82ea8e38e1bd994cabb18b8eb4e32.ico', '一个帮助开发者成长的社区。', 'https://juejin.im/', 9, 0, '2020-08-08 15:18:11', '2020-08-08 15:20:35', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100313, 150725316141101035, '企鹅号', '/profile/site/system/7c7b65475eddc144e90e6e9e059b75a7.png', '让世界看到你', 'https://om.qq.com/', 4, 0, '2020-08-11 20:03:20', '2020-08-11 20:07:10', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100314, 150725316141101035, '微博号', '/profile/site/system/0457b6128c225591f7b04dd23eaf4445.ico', '为创作者提供更优质的创作环境，帮助原创作者打造专属的个人品牌。', 'https://me.weibo.com/', 4, 0, '2020-08-11 20:05:49', '2020-08-11 20:06:30', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100315, 150725316141101035, '简书', '/profile/site/system/f8e210bc-a44e-49ad-a177-c9a8e2dcc409.ico', '一个优质的创作社区，在这里，你可以任性地创作。', 'https://www.jianshu.com/', 10, 0, '2020-08-11 20:08:35', '2021-01-13 15:37:33', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100316, 150725316141101035, '百家号', '/profile/site/system/ebb9886c7983ded7a1867941643de37f.png', '从这里影响世界。', 'https://baijiahao.baidu.com/', 4, 0, '2020-08-11 20:11:07', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100317, 150725316141101036, 'Unsplash', '/profile/site/system/09b7b39944a6893fbffa2842c0951468.png', '美丽的免费图片图片', 'https://unsplash.com/', 2, 0, '2020-08-11 20:15:40', '2021-04-22 21:36:24', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100318, 150725316141101036, 'Pexels', '/profile/site/system/5116557ff603b6d0cc4ba5270588684e.png', '免费图片', 'https://www.pexels.com/', 2, 0, '2020-08-11 20:17:44', '2020-08-11 20:23:35', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100319, 150725316141101036, 'UnDraw', '/profile/site/system/fb631125f144e2a6fe14120c2f4bd53f.png', '带有开源插图的设计项目，可满足您可以想象和创建的任何想法。', 'https://undraw.co/illustrations', 3, 0, '2020-08-11 20:25:06', '2021-03-27 12:49:55', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100320, 150725316141101036, 'Pixabay', '/profile/site/system/3846a4474d17612128f18f51f3487fd9.png', '我们的才华横溢的社区分享了超过210万张免版税的图片。', 'https://pixabay.com', 4, 0, '2020-08-11 20:28:06', '2021-03-15 23:35:03', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100321, 150725316141101036, 'Iconfinder', '/profile/site/system/e3325f68179436ccfc25b9f0ffff5a39.png', '2,100,000+个免费和高级矢量图标。', 'https://www.iconfinder.com', 5, 0, '2020-08-01 14:58:10', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100322, 150725316141101036, 'iconfont', '/profile/site/system/e1f63337915f79f8bcad1952adb9f6e1.png', '阿里巴巴矢量图标库。', 'http://www.iconfont.cn/', 6, 0, '2020-08-01 14:58:10', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100323, 150725316141101036, 'iconmonstr', '/profile/site/system/afd4885651455f12dcac4f214460dd99.png', '您的下一个项目的免费简单图标。', 'https://iconmonstr.com/', 7, 0, '2020-08-01 14:58:11', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100324, 150725316141101036, 'FindIcons', '/profile/site/system/0171a46b0f643752aa90aa314a22a546.png', '搜索300,000个免费图标。', 'https://findicons.com/', 8, 0, '2020-08-01 14:58:11', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100325, 150725316141101036, 'Icon Archive', '/profile/site/system/40c43a8932f24370cf456789b2ab51db.png', '搜索590,912个免费图标。', 'http://www.iconarchive.com/', 9, 0, '2020-08-01 14:58:11', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100326, 150725316141101036, 'IcoMoonApp', '/profile/site/system/d19c97ead3760f1b70efa4ee9ad6859c.png', '图标字体，SVG，PDF, PNG生成器。', 'https://icomoon.io/app/', 10, 0, '2020-08-01 14:58:11', '2020-08-11 20:36:42', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100327, 150725316141101036, 'easyicon', '/profile/site/system/34b4382075e047c6d1456f8fe591a1ef.png', 'PNG、ICO、ICNS格式图标搜索、图标下载服务。', 'http://www.easyicon.net/', 11, 0, '2020-08-01 14:58:11', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100328, 150725316141101036, 'flaticon', '/profile/site/system/582cf7361a0b4f444628c68b98e5cfc7.png', '634,000+免费矢量图标为SVG，PSD，PNG，EPS格式或图标字体。', 'https://www.flaticon.com/', 12, 0, '2020-08-01 14:58:11', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100329, 150725316141101036, 'UICloud', '/profile/site/system/f9840e127d500449da1c5c721f3634c3.png', '世界上最大的用户界面设计数据库。', 'http://ui-cloud.com/', 13, 0, '2020-08-01 14:58:11', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100330, 150725316141101036, 'Font Awesome Icon', '/profile/site/system/88440b8b0d5dc43a3f766670e2d11746.png', '惊人的675个图标的完整集合。', 'https://fontawesome.com/', 14, 0, '2020-08-01 14:58:11', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100331, 150725316141101036, 'ion icons', '/profile/site/system/6d0fd0bf35549f6d61037bd86e2ca242.png', 'Ionic Framework的高级图标字体。', 'http://ionicons.com/', 15, 0, '2020-08-01 14:58:11', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100332, 150725316141101036, 'Simpleline Icons', '/profile/site/system/acf446f1af754f863260cc10dd8d546e.png', '简单的线条图标包。', 'http://simplelineicons.com/', 16, 0, '2020-08-01 14:58:11', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100334, 150725316141101032, 'Java知音', '/profile/site/system/bbe364f9-0c94-4f88-a4ad-2b590064d624.jpeg', '一个专注于Java技术分享的网站', 'https://www.javazhiyin.com/', 7, 0, '2020-08-18 21:50:19', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100335, 150725316141101010, 'MacWk', '/profile/site/system/17a92b31-c9da-4dee-a04c-4ecf9cd92933.jpeg', '无广告，无后门，安全！', 'https://macwk.com/', 3, 0, '2020-08-18 21:54:00', '2020-08-18 21:54:34', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100340, 150725316141101002, '知轩藏书', '/profile/site/system/2e3e22f2-cd93-45b3-8278-604c483ed70e.jpeg', '玄幻小说排行榜精校-校对全本TXT小说下载网', 'http://zxcs.info/', 20, 0, '2020-08-19 00:20:24', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100341, 150725316141101037, 'WallHaven', '/profile/site/system/ff5238ff-e030-4f55-a6fb-ba49fd772ea8.jpeg', '种类多，壁纸好看！', 'https://wallhaven.cc/', 1, 0, '2020-08-19 19:42:33', '2020-09-20 20:28:40', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100342, 150725316141101037, 'Wallls', '/profile/site/system/3267d11e-db69-4dde-80fb-e3fef5d7ae6f.jpeg', '您从未见过的壁纸。', 'http://wallls.com/', 2, 0, '2020-08-19 19:44:48', '2020-09-20 20:28:47', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100344, 150725316141101037, 'SimpleDesktops', '/profile/site/system/1687352f-29f4-4eea-a61e-a0eeded626f1.jpeg', '简单', 'http://simpledesktops.com/', 4, 0, '2020-08-19 19:52:00', '2020-09-20 20:29:03', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100345, 150725316141101037, '彼岸桌面', '/profile/site/system/bd5b21c3-3bf1-47cc-b25f-36b4aa38180d.jpeg', '美丽精致的壁纸，免费提供风景、日历、美女、动漫、汽车、花卉、节日、动物、游戏、qq、阿狸等唯美、可爱、好看的壁纸，下载您所需要的壁纸', 'http://www.netbian.com/', 1, 0, '2020-08-19 19:57:41', '2020-12-03 13:37:09', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100346, 150725316141101001, '猪猪盘', '/profile/site/system/04e0b7e4-2569-49bf-8629-b7a503dd8fe2.png', '索引1亿+网盘资源', 'http://www.zhuzhupan.com/', 15, 0, '2020-08-19 20:13:29', '2020-09-20 20:25:48', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100348, 150725316141101001, '小白盘', '/profile/site/system/f04b12a5-3a1d-4af2-9672-08a0e5458bf2.jpeg', '网盘搜索', 'https://www.xiaobaipan.com/', 17, 0, '2020-08-19 20:16:51', '2020-08-19 20:18:03', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100352, 150725316141101004, '快用工具', '/profile/site/system/c5a1b819-a2ac-40a5-8aba-4b2204b8e115.jpeg', '提供各种优质、快捷、易用的在线工具，无需下载安装即可使用。', 'https://fastools.cn/', 1, 0, '2020-08-20 10:43:46', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100353, 150725316141101037, '极简壁纸', '/profile/site/system/7dcc6100-d451-4d78-915e-ea68c166d5cd.jpeg', '超高清电脑桌面壁纸美图；每天更新海量 4K 电脑壁纸，9012年最潮的壁纸网站，壁纸包括美女、二次元、自然风景。', 'https://bz.zzzmh.cn/', 1, 0, '2020-08-20 10:50:21', '2020-12-03 13:37:05', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100354, 150725316141101039, '135编辑器', '/profile/site/system/37ddd142-39c9-447c-b116-06f2e25878a0.jpeg', '提供丰富的样式库，支持插入排版、秒刷排版、一键排版等。', 'http://www.135editor.com/', 1, 0, '2020-08-20 11:05:19', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100355, 150725316141101039, '秀米', '/profile/site/system/db7eb18d-abdc-4979-bad3-2df745827332.jpeg', '素材顺应时下的审美，质量高，以布局的概念来进行不同组件的排版。', 'https://xiumi.us', 2, 0, '2020-08-20 11:12:34', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100356, 150725316141101039, '96微信编辑器', '/profile/site/system/849bb7d5-0616-478a-bada-40c3ad70a35f.jpeg', '大量精选素材、可以制作动态二维码、超多色值推荐、表情符号等。', 'https://bj.96weixin.com/', 3, 0, '2020-08-20 11:13:58', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100357, 150725316141101039, 'i排版', '/profile/site/system/1f8b4cd3-f277-42e8-b84e-2236c1a5aa82.ico', '偏清新文艺风，编辑界面较干净，容易上手，支持各种文本格式样式。', 'http://ipaiban.com/', 4, 0, '2020-08-20 11:18:13', '2021-01-13 15:40:07', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100358, 150725316141101039, '新榜编辑器', '/profile/site/system/61ed7aaf-2edd-4877-9d2a-4635a22b49e1.jpeg', '海量在线图片搜索、一键同步多平台，大量爆文可供参考。', 'https://edit.newrank.cn/', 5, 0, '2020-08-20 11:19:12', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100359, 150725316141101039, 'Markdown Nice', '/profile/site/system/a4db5eb7-7b52-4278-8a03-d147c817e494.jpeg', '一款在线，支持自定义样式的微信 Markdown 排版工具；现支持微信公众号、知乎、开源中国、稀土掘金、博客园和 CSDN 等一系列平台。另外，支持变更不同主题风格、格式化（微信外链转脚注、中英文间带空格）等。', 'https://www.mdnice.com/', 0, 0, '2020-08-20 11:22:19', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100360, 150725316141101039, '壹伴', '/profile/site/system/2632d2f2-c7bc-4278-90c4-ae75221e167a.jpeg', '更好用的微信编辑器，但不止于此。你可以使用壹伴小插件来高效地排版、修图、找素材、回消息和导出数据，50万公众号运营者的共同选择', 'https://yiban.io/invitation?invite_code=YE1H00', 6, 0, '2020-08-22 10:29:29', '2021-02-24 22:32:42', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100361, 150725316141101036, '创客贴', '/profile/site/system/3808ba9d-c655-42c9-b56f-cbdc04169be2.jpeg', '提供了15万+精品设计模板，120万+图片素材，涵盖营销海报、新媒体配图、印刷物料、PPT、简历等办公文档、电商设计、定制设计等百余种设计场景，选择喜爱的模板，AI智能生成设计，设计不求人。', 'https://www.chuangkit.com/', 1, 0, '2020-08-22 10:33:27', '2021-04-22 21:36:17', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100362, 150725316141101036, '懒设计', '/profile/site/system/bb76267f-1f2f-47c5-8c63-b4c136d58386.jpeg', '全球最受欢迎的平面设计工具和在线平面设计软件之一,提供海量海报、邀请函、贺卡、banner、logo、名片等免费设计素材和模板,可在线一键稿定设计印刷', 'https://www.fotor.com.cn/', 1, 0, '2020-08-22 10:39:01', '2021-04-22 21:36:12', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100363, 150725316141101011, '磁力熊', '/profile/site/system/75babf0b-ac99-4b61-8de0-b500f06398ff.jpeg', '1080P高清电影磁力迅雷下载,豆瓣Top250及豆瓣高分电影1080P高清磁力下载。', 'https://www.cilixiong.com/', 7, 0, '2020-08-22 18:25:13', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100366, 150725316141101011, '神马影院', '/profile/site/system/8ecdec9c-7802-4007-a30c-078fa7977a27.jpeg', '资源丰富，播放流畅缓冲快！', 'https://www.shenma4480.com/', 10, 0, '2020-08-22 18:29:46', '2020-08-22 18:37:47', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100367, 150725316141101011, '哔嘀影视', '/profile/site/system/f32be96e-14c9-41cb-9217-ea8a28948c65.jpeg', '在线观看，支持百度云，电驴，磁力链接下载', 'https://bde4.com/', 11, 0, '2020-08-22 18:31:14', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100368, 150725316141101011, 'NO视频', '/profile/site/system/bf08096d-7cb2-45e2-a00c-3efe03f0e84b.jpeg', '欧美、日韩、港台影视', 'https://www.novipnoad.com/', 12, 0, '2020-08-22 18:32:42', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100371, 150725316141101011, '南柯电影', '/profile/site/system/1687352f-29f4-4eea-a61e-a0eeded626f1.jpeg', '无广告，速度快！', 'https://www.nkdyw.com/', 15, 0, '2020-08-22 18:36:51', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100376, 150725316141101012, 'MyFree MP3', '/profile/site/system/3267d11e-db69-4dde-80fb-e3fef5d7ae6f.jpeg', '支持音乐在线试听、下载，以及无损音质的下载！', 'http://tool.liumingye.cn/music/', 9, 0, '2020-08-22 18:46:10', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100378, 150725316141101031, 'Ant Design', '/profile/site/system/6df56693-ffd1-4470-b905-0986f1236deb.jpeg', '一套企业级 UI 设计语言和 React 组件库', 'https://ant.design/index-cn', 7, 0, '2020-08-24 21:44:54', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100379, 150725316141101030, 'knife4j', '/profile/site/system/c74470de-7f78-4894-a475-522fa6774170.jpeg', '为Java MVC框架集成Swagger生成Api文档的增强解决方案 ', 'https://doc.xiaominfo.com/', 4, 0, '2020-08-25 19:39:38', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100380, 150725316141101040, 'GitHub 加速下载', '/profile/site/system/github.png', '一个对于 GitHub.com 的镜像加速器。我们使用开放资源为 GitHub 加速。', 'http://toolwa.com/github/', 1, 0, '2020-08-31 22:52:30', '2020-09-25 08:42:27', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100381, 150725316141101040, '下载', '/profile/site/system/github.png', '不需要购买，直接下载！', 'https://d.serctl.com/', 2, 0, '2020-08-31 23:01:16', '2020-09-25 08:43:52', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100382, 150725316141101040, 'GitHub 文件加速', '/profile/site/system/github.png', '直接走本服务器 CN2 GIA 线路 . 大多数情况下体验更佳！', 'https://g.ioiox.com/', 3, 0, '2020-08-31 23:01:46', '2020-09-25 08:44:40', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100383, 150725316141101040, 'GitHub代下', '/profile/site/system/github.png', '代下服务，永久免费！', 'http://gitd.cc/', 4, 0, '2020-08-31 23:03:00', '2020-09-25 08:45:21', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100384, 150725316141101040, 'GitHub加速链接生成工具', '/profile/site/system/github.png', '利用ucloud提供的GlobalSSH功能，对ssh协议数据进行加速！', 'https://github.zhlh6.cn/', 5, 0, '2020-08-31 23:04:04', '2020-09-25 08:46:18', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100385, 150725316141101017, 'SQL转Java', '/profile/site/system/3804cbf8-bb0e-437f-890e-7db6f6f778e0.jpeg', 'SQL转Java JPA、MYBATIS实现类代码生成平台。', 'https://java.bejson.com/generator/', 9, 0, '2020-09-07 10:12:34', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100386, 150725316141101011, 'M3U8播放器', '/profile/site/system/8b13f949-cc19-4882-aa8c-f236fbc04b84.jpeg', '电影、美剧、韩剧、漫画的直链', 'https://www.m3u8play.com/', 20, 0, '2020-09-08 20:33:58', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100387, 150725316141101017, '免费在线语音转文字', '/profile/site/system/bb15ab1d-410b-4242-ab81-50c1b6f1a81b.jpeg', '语音转文字，做纪录或者文字稿等', 'https://beecut.cn/speech-to-text-online', 10, 0, '2020-09-09 15:50:33', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100388, 150725316141101037, 'Wallpaper', '/profile/site/system/f32be96e-14c9-41cb-9217-ea8a28948c65.jpeg', '设计，室内设计，建筑，时尚，艺术', 'https://wall.alphacoders.com/?lang=Chinese', 7, 0, '2020-09-09 23:40:13', '2020-12-11 09:07:39', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100389, 150725316141101035, '视频号助手', '/profile/site/system/wxshipinghao.ico', '微信视频号助手', 'https://channels.weixin.qq.com/', 11, 0, '2020-09-11 20:52:11', '2020-09-11 20:55:03', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100390, 150725316141101005, 'DeepL翻译', '/profile/site/system/1cba9c99-73ef-422f-87ef-2e541bf82d1b.png', '将一段文字翻译到尽可能的通顺和便于理解，甚至是俚语、方言、名言名句、古诗词等内容都可以几乎没有任何语病的翻译出来。', 'https://www.deepl.com/translator', 5, 0, '2020-09-12 15:58:24', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100391, 150725316141101002, '搬书匠', '/profile/site/system/a7e8a0e2-ffe5-417c-bddf-43921d07554b.png', '编程书籍的好网站', 'http://www.banshujiang.cn/', 21, 0, '2020-09-13 14:43:50', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100392, 150725316141101035, '网易见外', '/profile/site/system/c6348b72-41e6-4237-9122-33b4c52cdf3e.png', '由人工智能事业部研发,是一个集视频听翻、直播听翻、语音转写、文档直翻功能为一体的AI智能语音转写听翻平台。', 'https://jianwai.youdao.com/', 10, 0, '2020-09-15 00:24:00', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100394, 150725316141101042, '若依 / RuoYi-Vue', '/profile/site/system/35196237-8319-4204-a844-bc06b4409a39.png', '基于SpringBoot，Spring Security，JWT，Vue & Element 的前后端分离权限管理系统 ', 'https://gitee.com/y_project/RuoYi-Vue', 1, 0, '2020-09-16 14:39:29', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100395, 150725316141101042, '若依 / RuoYi-Cloud', '/profile/site/system/35196237-8319-4204-a844-bc06b4409a39.png', '基于Spring Boot、Spring Cloud & Alibaba、OAuth2的分布式微服务架构权限管理系统 ', 'https://gitee.com/y_project/RuoYi-Cloud', 2, 0, '2020-09-16 14:41:32', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100396, 150725316141101042, '陌溪 / 蘑菇博客', '/profile/site/system/3247f776-f5a8-452a-998a-ebf411948607.png', '一个基于微服务架构的前后端分离博客系统。', 'https://gitee.com/moxi159753/mogu_blog_v2', 3, 0, '2020-09-16 14:43:45', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100397, 150725316141101042, 'FEBS-Cloud', '/profile/site/system/35fcf5e6-8980-4b4d-9da4-564b3bd3f333.png', '基于Spring Cloud Hoxton.RELEASE、Spring Cloud OAuth2 & Spring Cloud Alibaba & Element 微服务权限系统，开箱即用。', 'https://github.com/febsteam/FEBS-Cloud', 4, 0, '2020-09-16 14:46:37', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100398, 150725316141101042, '慕容若冰 / spring-microservice-exam', '/profile/site/system/15afba18-f1c2-4313-b664-ba8943a333a4.png', '硕果云，基于Spring Cloud搭建的新一代微服务教学管理平台，提供多租户、权限管理、考试、练习等功能，题型支持单选题、多选题、不定项选择题、判断题、简答题，二维码分享，移动端答题等 ', 'https://gitee.com/wells2333/spring-microservice-exam', 5, 0, '2020-09-16 14:48:27', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100399, 150725316141101032, '蘑菇博客', '/profile/site/system/3247f776-f5a8-452a-998a-ebf411948607.png', '专注于技术分享的博客平台', 'http://www.moguit.cn', 8, 0, '2020-09-16 14:54:32', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100400, 150725316141101035, 'QQ公众平台', '/profile/site/system/7424959f-6ada-48f9-9077-a7e0477e6e3d.png', 'QQ公众平台，为解决个人，企业，组织在QQ平台上的业务服务与用户管理提供实用的服务工具平台。', 'https://mp.qq.com', 14, 0, '2020-09-18 09:52:23', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100401, 150725316141101035, '抖音短视频', '/profile/site/system/b5541655-6a6f-47b9-8698-9bab021fa846.ico', '抖音短视频，一个旨在帮助大众用户表达自我，记录美好生活的短视频分享平台。为用户创造丰富多样的玩法，让用户在生活中轻松快速产出优质短视频。', 'https://www.douyin.com/', 15, 0, '2020-09-18 09:54:01', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100402, 150725316141101017, 'GitMind', '/profile/site/system/d3799599-6413-4944-a6f4-241e5c54c51c.png', '免费在线思维导图软件，简化逻辑梳理，集思广益，释放创造力在线脑图、思维导图、流程图、工业设计、工程管理，一图涵千面 ', 'https://gitmind.cn/', 11, 0, '2020-09-20 14:33:02', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100403, 150725316141101004, 'DocSmall', '/profile/site/system/380cb37a-370d-40df-8807-5c318e5310f2.png', '免费在线图片压缩、GIF压缩工具、PDF压缩工具、PDF合并工具、PDF分割工具', 'https://docsmall.com/', 8, 0, '2020-09-20 14:36:33', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100404, 150725316141101017, 'Dimmy', '/profile/site/system/12d7985a-2fc7-49b7-b6e6-55312ac1c550.png', '手机电脑等设备的展示模型，可以让你的图片放在电脑、手机、ipad等模型中展示，图片档次大大提升。', 'https://dimmy.club/', 12, 0, '2020-09-20 14:38:10', '2020-09-20 14:42:26', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100405, 150725316141101017, 'BrowserFrame', '/profile/site/system/251a15a3-811e-4359-98e2-f03b3319240e.ico', '浏览器展示模型工具', 'http://browserframe.com/', 13, 0, '2020-09-20 14:39:26', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100406, 150725316141101017, 'Flourish', '/profile/site/system/3416f7ae-1e74-45ef-b03d-4dfa730f1105.png', ' 数据可视化工具，快速地把表格数据转换为各种各样好看的图表，并且还支持动态可视化。', 'https://flourish.studio/', 14, 0, '2020-09-20 14:41:21', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100407, 150725316141101017, 'RemoveBg', '/profile/site/system/b2377b53-291b-4129-bdbe-68cb3a1fa7c3.png', '抠图神器，消除图片中的背景。', 'https://www.remove.bg/zh', 15, 0, '2020-09-20 14:44:42', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100408, 150725316141101018, 'Crx4Chrome', '/profile/site/system/af48b33f-ab08-4e71-a4de-32af6b930860.ico', ' Chrome浏览器插件站', 'https://www.crx4chrome.com/', 7, 0, '2020-09-20 14:46:03', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100409, 150725316141101043, '易视网', '/profile/site/system/e393d0d3-5a07-4c3a-a648-b5fa63b55fea.ico', '直播网络电视直播在线观看', 'https://www.cietv.com/', 1, 0, '2020-09-20 14:55:21', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100410, 150725316141101043, '好趣网', '/profile/site/system/f6ce09df-432f-4903-b308-99f65ab9504b.png', '2000套高清网络电视直播在线观看', 'http://tv.haoqu99.com/', 2, 0, '2020-09-20 14:56:41', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100411, 150725316141101042, 'EasyCaptcha', '/profile/site/system/c85348a4-7b34-488c-bd95-8b7b106f793d.png', 'Java图形验证码，支持gif、中文、算术等类型，可用于Java Web、JavaSE等项目。', 'https://gitee.com/ele-admin/EasyCaptcha', 6, 0, '2020-09-20 15:59:37', '2022-05-25 23:25:00', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100412, 150725316141101017, '短视频解析', '/profile/site/system/b0ffc6e6-6b42-4f5a-a221-44d9ff2c3ee7.png', '支持解析快手、抖音、Youtube、Tiktok、火山、今日头条、西瓜视频、皮皮虾、小咖秀、趣多拍、微视、美拍、秒拍、网易云、TikTok、哔哩哔哩、陌陌、映客、迅雷、阳光宽频、全民 K 歌、刷宝、WIDE 短视频、小红书、等平台的视频，而且解析出来的网站视频没有水印。', 'http://www.dspjx.com/', 16, 0, '2020-09-23 23:57:07', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100413, 150725316141101008, '微 PE 工具箱', '/profile/site/system/93975589-a3c2-4540-9893-69a0149d6435.png', '微 PE 工具箱 就是一款常用的 Windows PE 工具包，支持 Windows 10，提供了 32/64 位版本，并且支持 NVME 硬盘。', 'http://www.wepe.com.cn/', 8, 0, '2020-09-23 23:59:35', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100414, 150725316141101017, 'GeoGebra', '/profile/site/system/68292c53-cd3e-4a4a-9a4e-7d67359fb1e6.png', '一款结合几何、代数与微积分的免费动态数学软件，也支持在线直接绘图 /计算。', 'https://www.geogebra.org', 17, 0, '2020-09-24 00:01:36', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100416, 150725316141101017, 'Maven 仓库', '/profile/site/system/4242630d-0216-4950-9646-d3ee647ba8e3.png', 'Jar 下载，Jar 依赖引用', 'https://mvnrepository.com/', 19, 0, '2020-09-24 08:41:01', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100418, 150725316141101004, '萝卜工坊', '/profile/site/system/a970e871-9f79-4caa-a09e-df2fb7c9ddc2.png', '快速转换模拟手写字体文档，让打印出的字看起来像手写的 一个软件在线解决文字抄写烦恼', 'http://www.beautifulcarrot.com/', 9, 0, '2020-09-24 08:52:18', '2020-09-24 08:53:58', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100419, 150725316141101004, '二维码解码器', '/profile/site/system/989df62a-e0e5-468f-8fc7-f4b766d27ddd.png', '在线二维码解析和生成', 'https://jiema.wwei.cn/', 10, 0, '2020-09-24 08:57:01', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100420, 150725316141101004, 'PickFrom', '/profile/site/system/12736188-5677-410f-a628-1bab12ab00d9.ico', '一站式视频剪辑平台，让工作更简单。', 'https://zh.pickfrom.net/', 11, 0, '2020-09-24 08:59:06', '2020-09-24 08:59:32', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100421, 150725316141101004, '转字体', '/profile/site/system/89d9be4b-cb72-45ac-a30d-def2c1e30e1c.ico', '简体字繁体字互转', 'https://www.aies.cn/', 12, 0, '2020-09-24 09:02:38', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100422, 150725316141101040, 'GitHub 文件加速', '/profile/site/system/github.png', '利用 Cloudflare Workers 对 github release 、archive 以及项目文件进行加速，部署无需服务器且自带CDN。', 'https://gh.api.99988866.xyz/', 6, 0, '2020-09-25 08:41:24', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100427, 150725316141101037, '美桌网', '/profile/site/system/7510faa4-8159-49be-ae55-02629202fbe3.ico', '陪你下载生活的美！', 'http://www.win4000.com/', 8, 0, '2020-09-29 14:43:48', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100428, 150725316141101037, '回车桌面', '/profile/site/system/d40daccb-e4d7-4f34-a909-0341e49cd238.ico', '动画、漫画、卡通、锁屏图片、高清手机壁纸！', 'https://www.enterdesk.com/', 9, 0, '2020-09-29 14:45:43', '2020-09-29 14:47:00', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100429, 150725316141101037, '全面屏壁纸', '/profile/site/system/1ab3ecc5-ebc6-474a-814e-fa57b3781edb.ico', '专为全面屏和刘海屏手机适配的2K超高清壁纸网站！', 'http://m.bcoderss.com/', 10, 0, '2020-09-29 14:48:48', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100430, 150725316141101011, '无限影视网', '/profile/site/system/2c0a0e63-e07a-4344-aa11-e461e9616fee.png', '百万影片任你搜索！', 'https://www.wxtv.net/', 21, 0, '2020-09-30 14:36:33', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100432, 150725316141101008, '不忘初心', '/profile/site/system/9b57a3c2-adc0-4dc9-a3d4-8d573be1fce0.png', '精简版系统官网。从心出发，专注精简系统！', 'https://www.pc521.net/', 10, 0, '2020-10-09 16:56:49', '2021-01-27 10:09:41', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100433, 150725316141101036, '免费版权图片', '/profile/site/system/19e0d9d2-cf36-4046-a2d3-80a028b789a3.jpg', '一键搜索多家免版权图库，再也不用担心商用图片侵权了 ！', 'https://www.logosc.cn/so/', 17, 0, '2020-10-09 17:25:56', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100434, 150725316141101036, 'ColorHub', '/profile/site/system/a51c8e1f-bc7b-4b91-8fcb-9511cc234682.png', '高清无版权图片，个人和商业用途免费！', 'https://colorhub.me/', 18, 0, '2020-10-09 17:28:50', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100435, 150725316141101036, 'Hippopx', '/profile/site/system/c44b31d2-6a73-4879-84da-6a2e909d8a8f.ico', '基于CC0协议的免版权图库！', 'https://www.hippopx.com/zh/', 19, 0, '2020-10-09 17:30:41', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100436, 150725316141101036, 'FreePik', '/profile/site/system/45c6ebba-1fa4-4728-8210-9f9218ecc6cd.jpg', '查找免费矢量，图库照片，PSD和图标！', 'https://www.freepik.com/', 20, 0, '2020-10-09 17:34:34', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100438, 150725316141101011, '云播TV', '/profile/site/system/e7d75bda-6468-48f9-9dee-84a7b304b420.png', '一个单纯不做作的电影站！', 'https://www.yunbtv.net/', 24, 0, '2020-10-09 23:47:22', '2022-05-07 23:30:12', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100440, 150725316141101011, '音范丝', '/profile/site/system/e8eb094d-efb6-40b9-886e-3ab03871950d.ico', '高清无水印，影音集、精选4K蓝光原盘下载，顶级收藏！', 'http://www.yinfans.me/', 26, 0, '2020-10-12 14:14:54', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100441, 150725316141101011, '腾讯视频WeTV', '/profile/site/system/fe2b466d9fe6bed552c0adcaac1c2813.ico', '腾讯视频海外版，无广告，而且可以免费观看1080P！', 'https://wetv.vip/zh-cn', 4, 0, '2020-10-12 20:45:11', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100442, 150725316141101017, '千千秀字', '/profile/site/system/fbdf9a0d-88d9-4fcc-88cc-c63a49ea1eab.png', '提供文字翻译、字体转换、字效生成等在线服务的同时，也关注着文字的历史和文字的各行应用。', 'https://www.qqxiuzi.cn/', 20, 0, '2020-10-12 23:10:47', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100443, 150725316141101002, '搜韵', '/profile/site/system/6b06e347-bfe9-4230-92e4-d4caba9b6747.png', '诗词门户网站，可检索，分经史子集和四库之外等，图像清晰，速度快，非常便捷', 'https://sou-yun.cn/', 22, 0, '2020-10-16 19:08:14', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100445, 150725316141101046, '老殁', '/profile/site/system/22a74e7f-c58c-4914-86d6-4f557f572df9.png', '免费推荐优秀软件', 'https://www.mpyit.com', 1, 0, '2020-10-16 21:43:27', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100446, 150725316141101046, '果核剥壳', '/profile/site/system/1796f330-4e4f-49e6-819b-9810fca40735.png', '还原软件的本质，纯净软件分享，守住互联网最后的一片净土', 'https://www.ghpym.com', 5, 0, '2020-10-16 21:49:17', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100447, 150725316141101046, '423Down', '/profile/site/system/e005555a-a43c-423a-b74d-375b3867e442.ico', '有品质的电脑软件、Android软件分享博客', 'https://www.423down.com', 6, 0, '2020-10-16 21:51:13', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100448, 150725316141101046, '心海e站', '/profile/site/system/69c513c7-6150-4f58-bbf6-f866718b9238.jpg', '发布由烈火汉化的一些实用的软件，全部免费，杜绝广告！', 'https://hrtsea.com', 7, 0, '2020-10-16 21:53:50', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100449, 150725316141101046, '落尘之木', '/profile/site/system/292ba100-2d1a-4f63-acde-9c0f2c02bfe1.png', '分享互联网优秀软件、电脑经验、技术交流、IT类', 'https://www.luochenzhimu.com', 8, 0, '2020-10-16 21:55:28', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100450, 150725316141101046, '易破解', '/profile/site/system/ef2b424c-cd7a-43fd-95dd-b6093cdf8854.ico', '给你所需要的内容', 'https://www.ypojie.com', 9, 0, '2020-10-16 21:56:42', '2021-01-13 15:34:19', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100451, 150725316141101046, 'QQ前线乐园', '/profile/site/system/1cef9d6a-2f6d-42a3-b133-a59ccea947eb.png', '专注于分享，分享好资源。', 'https://www.yijingying.com', 10, 0, '2020-10-16 21:58:21', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100452, 150725316141101046, '风刑软件站', '/profile/site/system/17597510-396f-41a5-abf2-41c847c0fa62.png', '一个满载优秀、严谨、开放的软件下载平台', 'https://www.wsf1234.com', 11, 0, '2020-10-16 22:01:28', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100453, 150725316141101046, '软件缘', '/profile/site/system/172e5128-b12a-45b9-8de2-cbf906d53164.png', '软件缘 - 精品绿软，品鉴独特', 'https://www.appcgn.com', 12, 0, '2020-10-16 22:03:19', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100454, 150725316141101010, 'XClient', '/profile/site/system/12f29237-4138-4c75-815e-58e660c4a8c0.ico', '精品MAC应用分享', 'https://xclient.info', 4, 0, '2020-10-16 22:05:32', '2021-01-13 15:35:24', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100455, 150725316141101046, '孤独求软', '/profile/site/system/41f3a0c6-f433-45db-8a55-4c8a00b28e8a.ico', '常用软件一站齐全', 'http://www.dugubest.com', 13, 0, '2020-10-16 22:06:56', '2021-01-13 15:34:39', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100456, 150725316141101010, '史蒂芬周的博客', '/profile/site/system/8b57d624-b4e1-48e3-8307-0c10b393ccb9.ico', '软硬兼施，娱乐共享。', 'http://www.sdifen.com', 5, 0, '2020-10-16 22:08:45', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100457, 150725316141101010, '苹果软件盒子', '/profile/site/system/2799a66d-646d-445f-87b8-b7335826b812.png', '分享优质 Mac', 'https://www.macappbox.com', 6, 0, '2020-10-16 22:10:15', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100458, 150725316141101046, 'Extfans 扩展迷', '/profile/site/system/bc0605a7-3a50-4840-b37e-d1ac8386ae84.ico', '好用的浏览器插件推荐', 'https://www.extfans.com', 14, 0, '2020-10-16 22:57:50', '2021-01-13 15:34:45', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100459, 150725316141101047, 'Golink加速器', '/profile/site/system/1432bb7a-5e20-446e-9ba7-72856d445abb.ico', '国内首款免费游戏加速器', 'https://www.golink.com', 1, 0, '2020-10-16 23:01:15', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100460, 150725316141101047, '流星游戏加速器', '/profile/site/system/c4597214-2353-414c-b9a2-af8d0ce250d2.ico', '真免费,为痛快!', 'https://www.liuxing.com', 2, 0, '2020-10-16 23:02:15', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100461, 150725316141101047, '奇妙网游加速器', '/profile/site/system/f00c3d4a-e411-42d5-9b26-fda14cc9dd68.png', '真正免费，低延时，真专线，真稳定，真好用', 'https://www.qimiao.com', 3, 0, '2020-10-16 23:03:41', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100463, 150725316141101047, '小霸王', '/profile/site/system/65fcca63-5610-4e2d-8ab9-9084c8b68e58.png', '找回童年的快乐 ', 'https://www.yikm.net', 5, 0, '2020-10-16 23:08:38', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100464, 150725316141101047, '侠聚网', '/profile/site/system/6b430ae2-c72b-45ac-829d-ea3ecd5551ee.png', '免费的Android游戏修改神器，内置海量游戏下载', 'http://www.huluxia.com', 6, 0, '2020-10-16 23:09:48', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100465, 150725316141101047, '骑士下载', '/profile/site/system/193635d3-0fff-4886-ac47-e801ec314ed1.png', '好玩的安卓游戏免费下载', 'https://www.vqs.com', 7, 0, '2020-10-16 23:11:23', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100466, 150725316141101047, 'TapTap', '/profile/site/system/6558f840-b408-4ee1-a019-9514d107c7f8.ico', '推荐高质量好玩的手机游戏', 'https://www.taptap.com', 8, 0, '2020-10-16 23:12:31', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100471, 150725316141101011, '思古影视', '/profile/site/system/5c8372f4-bd97-4f95-a927-665675f1b8e1.ico', '高清免费VIP视频在线解析', 'https://www.sigu.cc/', 28, 0, '2020-10-20 21:32:59', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100472, 150725316141101017, '文档免费下载', '/profile/site/system/7c70855c-a7c2-4377-8e4b-8837a688b43b.ico', '从此,下载百度文库文档变得简单', 'https://doc.chaney.top/', 21, 0, '2020-10-25 11:23:39', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100473, 150725316141101009, 'ROM乐园', '/profile/site/system/34cfc67c-7490-4f5f-b7fb-a0b1f3d812a7.jpeg', '专注于打造全网优质特色ROM刷机包下载官方网站', 'http://www.romleyuan.com', 6, 0, '2020-10-27 23:23:28', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100477, 150725316141101032, '格姗知识圈', '/profile/site/system/logo.jpg', '我的博客网站，专注于技术分享、实用工具与技巧的博客平台！', 'https://geshanzsq.com', 1, 0, '2020-11-29 17:13:06', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100478, 150725316141101037, '人工桌面', '/profile/site/system/3982b997-0e85-4573-bb8c-ccf4db4bf83d.ico', '简洁又可爱的萌妹桌面软件。绝对是美女帅哥、宅男宅女的喜爱。', 'https://lumi.mihoyo.com/#/', 0, 0, '2020-12-03 13:39:48', '2020-12-03 13:39:55', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100479, 150725316141101037, '故宫壁纸', '/profile/site/system/6dd9f9cc-d49e-4b32-abcd-5eba13580739.ico', '将历史的精彩收集到自己的手中', 'https://www.dpm.org.cn/lights/royal.html', 11, 0, '2020-12-11 09:10:22', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100480, 150725316141101037, 'CGWallpapers', '/profile/site/system/0e5e058c-bf93-4c40-b327-b4064a023560.ico', ' 游戏CG壁纸站，超细腻，真假难分', 'https://www.cgwallpapers.com/', 12, 0, '2020-12-11 09:12:58', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100481, 150725316141101042, '格姗导航 / geshanzsq-nav', '/profile/site/system/logo.jpg', '一个基于 Spring Boot + Vue 前后端分离的导航网站！', 'https://gitee.com/geshanzsq/geshanzsq-nav', 0, 0, '2021-01-05 17:15:46', '2022-11-03 21:45:09', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100483, 150725316141101048, '剪映', '/profile/site/system/3bcc9224-2291-4f5c-8675-e10c1c0f570d.jpeg', '轻而易剪，上演大幕。', 'https://lv.ulikecam.com/', 1, 0, '2021-01-07 20:05:59', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100484, 150725316141101048, 'InShot', '/profile/site/system/9cdf91f4-530d-49b2-bdf1-1d4b4af68c8a.ico', '专业视频剪辑工具，风靡全球。', 'http://inshotapp.com/', 2, 0, '2021-01-07 20:06:02', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100485, 150725316141101048, '来画', '/profile/site/system/544f582c-1fa5-41e4-a5c5-65a1b1d9503d.ico', '像做PPT一样做视频', 'https://www.laihua.com/', 3, 0, '2021-01-07 20:06:05', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100486, 150725316141101048, 'VUE VLOG', '/profile/site/system/d725ef43-e3c5-42a9-9289-b8b6cc91137d.ico', '用 Vlog 记录生活', 'https://vuevideo.net/', 4, 0, '2021-01-07 20:06:07', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100487, 150725316141101048, '字说', '/profile/site/system/410577d5-312c-4674-8bf1-ef5177b194b3.ico', '字说  文字动画视频制作神器', 'https://h5.zishuovideo.com/', 5, 0, '2021-01-07 20:06:10', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100488, 150725316141101048, '轻剪辑', '/profile/site/system/e394ad53-a0a7-4929-b20c-c0134f4d436d.ico', '在线视频剪辑神器 无需软件下载 1分钟轻松制作精彩视频', 'https://e.chuanying520.com/', 6, 0, '2021-01-07 20:06:12', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100489, 150725316141101048, '万兴喵影', '/profile/site/system/62f04881-b9fc-42fb-bf75-c14eaba90672.png', '风靡全球的国产剪辑神器 用心剪辑美好生活', 'https://miao.wondershare.cn/', 7, 0, '2021-01-07 20:06:14', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100490, 150725316141101048, 'Animaker', '/profile/site/system/24044bfd-1173-49de-aede-11e2d5592a77.jpeg', '超好用的动画短视频工具', 'https://www.animaker.com/', 8, 0, '2021-01-07 20:06:16', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100491, 150725316141101048, '飞推', '/profile/site/system/3a1d5950-9e7a-49ef-a68c-a304f0a450f1.png', '创意视频制作神器 视频制作从未如此简单!', 'https://www.qutui360.com/', 9, 0, '2021-01-07 20:06:18', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100492, 150725316141101048, 'Premiere', '/profile/site/system/17f4dcb9-4881-4e4b-83d9-9da269d0af2e.ico', '始终更胜一筹的视频编辑', 'https://www.adobe.com/cn/products/premiere.html', 10, 0, '2021-01-07 20:06:20', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100493, 150725316141101048, 'After Effects', '/profile/site/system/17f4dcb9-4881-4e4b-83d9-9da269d0af2e.ico', '制作气势恢宏的大场面', 'https://www.adobe.com/cn/products/aftereffects.html', 11, 0, '2021-01-07 20:06:22', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100494, 150725316141101048, '爱剪辑', '/profile/site/system/910ecf63-8077-427b-987b-98a1f206cc62.ico', '全民流行的视频剪辑软件', 'http://www.ijianji.com/', 12, 0, '2021-01-07 20:06:24', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100495, 150725316141101048, '快剪辑', '/profile/site/system/929c3691-b128-4561-b836-2664bcee50a3.ico', '一触即发，分享你的创意灵感', 'https://kuai.360.cn/home.html', 13, 0, '2021-01-07 20:06:27', '2021-01-07 20:06:50', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100496, 150725316141101035, '知识星球', '/profile/site/system/b5fa8f88-5312-4fb6-8183-af53a748f0a1.ico', '深度连接铁杆粉丝，运营高品质社群，知识变现的工具。', 'https://www.zsxq.com', 17, 0, '2021-01-11 17:54:25', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100497, 150725316141101049, '公益图床', '/profile/site/system/a9532e0c-b22c-4c15-9441-b3293465a7a8.png', '国内图床，速度飞快，快人一步。', 'https://sbimg.cn', 1, 0, '2021-01-12 16:32:07', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100498, 150725316141101049, 'SM.MS图床', '/profile/site/system/de98806e-8b41-4351-a8c5-9f307a4ad1d3.ico', '免费用户无香港节点，速度较慢', 'https://sm.ms', 2, 0, '2021-01-12 16:40:09', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100499, 150725316141101049, '路过图床', '/profile/site/system/13322f82-052a-496e-8d14-39d678745466.png', '免费图片上传，高速稳定的图片上传和外链服务。', 'https://imgchr.com/', 3, 0, '2021-01-12 16:41:10', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100500, 150725316141101030, '极客时间', '/profile/site/system/5ab40821-6da7-4c81-8c56-62ba2598a9ab.jpg', '轻松学习，高效学习！', 'https://time.geekbang.org', 5, 0, '2021-01-13 11:16:54', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100501, 150725316141101031, '极客时间', '/profile/site/system/5ab40821-6da7-4c81-8c56-62ba2598a9ab.jpg', '轻松学习，高效学习', 'https://time.geekbang.org', 8, 0, '2021-01-13 11:17:53', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100502, 150725316141101011, '芒果TV', '/profile/site/system/ef97cc86-01f6-4109-a23c-5613099d0d67.ico', '天生青春', 'https://www.mgtv.com', 6, 0, '2021-01-13 15:25:40', '2021-01-13 15:25:55', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100504, 150725316141101030, '码云 Gitee', '/profile/site/system/ec70a38c-e700-4181-a368-c1b9a12d09b9.ico', '基于 Git 的代码托管和研发协作平台。帮助开发者/团队/企业更好地管理代码，让软件研发更高效', 'https://gitee.com/', 7, 0, '2021-01-15 10:52:50', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100505, 150725316141101030, 'Github', '/profile/site/system/a81a692b-999c-4018-ba1b-9e0156b30af9.svg', '数以百万计的开发人员和公司在github上构建、发布和维护他们的软件，github是世界上最大、最先进的开发平台。', 'https://github.com', 8, 0, '2021-01-15 10:54:32', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100506, 150725316141101035, '网易号', '/profile/site/system/5956e442-9f8b-4516-9dcc-19ddcf06f7af.png', '媒体开放平台。', 'http://mp.163.com', 18, 0, '2021-01-18 10:16:24', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100507, 150725316141101035, '大鱼号', '/profile/site/system/6469fa53-6822-4f50-aca2-a23c7d41acce.ico', '一点接入，多点分发。移动世界，无所不达', 'https://mp.dayu.com', 19, 0, '2021-01-18 10:19:32', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100508, 150725316141101035, '一点资讯', '/profile/site/system/7bccd2ec-e385-497d-a1f8-655a7a8a0197.ico', '阅不同，更有趣', 'http://www.yidianzixun.com', 20, 0, '2021-01-18 10:20:54', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100509, 150725316141101035, '搜狐号', '/profile/site/system/b6021b69-2035-4f9c-8e1e-ea54c1d74817.ico', '再小的个体，也能获取影响力', 'https://mp.sohu.com/mpfe/v3/login', 21, 0, '2021-01-18 10:22:30', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100510, 150725316141101050, '印象笔记', '/profile/site/system/4e5ed731-1d90-4586-baff-c3bb89d4be8b.ico', '工作必备效率应用', 'https://www.yinxiang.com/', 1, 0, '2021-01-18 10:28:44', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100511, 150725316141101050, '有道云笔记', '/profile/site/system/43f327f6-631b-4280-a7af-a01ed8c505b6.ico', '5000万用户的选择', 'http://note.youdao.com', 2, 0, '2021-01-18 10:29:24', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100512, 150725316141101050, 'Tower', '/profile/site/system/136c7f93-cbc7-4daa-9edc-73840b2c2b6e.ico', '提升协作效率，打造高效团队', 'https://tower.im', 3, 0, '2021-01-18 10:29:58', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100513, 150725316141101050, '为知笔记', '/profile/site/system/09f19706-7536-4cdd-aeaa-97d64c89a9b4.ico', '一键收藏、全端全文检索、多级目录、Markdown', 'http://www.wiz.cn', 4, 0, '2021-01-18 10:31:26', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100514, 150725316141101050, '石墨文档', '/profile/site/system/9412b0ba-72eb-4751-a1ab-1517c9678eb9.ico', '企业在线协同办公系统平台,支持云端多人在线协作编辑文档和表格', 'https://shimo.im', 5, 0, '2021-01-18 10:32:00', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100515, 150725316141101050, '锤子便签', '/profile/site/system/e6cc2bbc-e2fb-4cd5-a3cd-b828715fb834.ico', '可能是史上最漂亮的便签应用，你或许会因它重新喜欢上记录和表达', 'https://www.smartisan.com/apps/#/notes', 6, 0, '2021-01-18 10:32:34', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100516, 150725316141101050, '腾讯文档', '/profile/site/system/d9222fdb-cd48-43bf-bc1e-b40a79bc8eb2.ico', '支持多人在线编辑Word、Excel和PPT文档', 'https://docs.qq.com', 7, 0, '2021-01-18 10:33:28', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100517, 150725316141101050, 'Teambition', '/profile/site/system/cd0cb55f-c569-40cf-831e-a4a3f73c6dff.ico', '一套聪明好用的日常工具，包含项目、待办、网盘、文档、日历等丰富应用，帮助你把想法变成现实，使用起来爱不释手。', 'https://www.teambition.com', 8, 0, '2021-01-18 10:34:08', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100518, 150725316141101050, 'Google Docs', '/profile/site/system/1a7be2d2-a468-4ce1-8cd9-9243f265b598.jpeg', '谷歌在线文档', 'https://docs.google.com', 9, 0, '2021-01-18 10:37:08', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100519, 150725316141101050, 'WPS云文档', '/profile/site/system/28edf917-58d1-4739-922f-27f9f9c889f0.ico', '多人实时协作的在线Office', 'https://www.kdocs.cn', 10, 0, '2021-01-18 10:38:17', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100520, 150725316141101030, 'JustAuth', '/profile/site/system/4473999c-1dee-4b47-bbff-155b1785085e.png', '史上最全的整合第三方登录的开源库', 'https://www.justauth.cn/', 9, 0, '2021-01-18 17:03:08', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100521, 150725316141101029, 'PPT超级市场', '/profile/site/system/334c508a-a599-4a25-8e2b-e037de63dba6.ico', '完全免费的PPT模板下载网站。量都是极高，并且非常精美。', 'http://ppt.sotary.com/web/wxapp/index.html', 7, 0, '2021-01-20 11:02:53', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100522, 150725316141101046, '原版系统', '/profile/site/system/3d2a5e29-be0a-4cdb-826d-e063e051a79a.ico', '提供可靠的原版软件。十二年的专注和积累，初心未改，打造下一个里程碑。 ', 'https://next.itellyou.cn/', 0, 0, '2021-01-22 11:02:37', '2021-01-22 11:03:29', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100523, 150725316141101012, 'HiFiNi - 音乐磁场', '/profile/site/system/fdd671e9-b241-462f-a259-d8d7d5a2596e.png', '一个由音乐爱好者维护的分享平台', 'https://www.hifini.com/', 11, 0, '2021-01-25 16:01:45', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100524, 150725316141101037, '必应壁纸', '/profile/site/system/ac326879-5ed4-4ef7-9476-00793db5a0a3.png', '超高质量的必应壁纸4K无水印下载', 'https://www.todaybing.com/', 13, 0, '2021-01-29 14:36:42', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100525, 150725316141101051, '火烧云数据', '/profile/site/system/25f491c8-2164-4d18-aeca-c6aa2ccbc8a7.png', '专业的B站第三方大数据分析平台，助力UP主/MCN 快速涨粉，商业变现；助力品牌方/广告公司洞察竞品投放情报，匹配优质UP主，投放更精准出效', 'http://www.hsydata.com/home/index', 1, 0, '2021-02-01 14:35:32', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100526, 150725316141101035, 'B站创作中心', '/profile/site/system/179f1fce-c229-465c-83b8-0bb2caa9eb2c.ico', '国内知名的视频弹幕网站，这里有最及时的动漫新番，最棒的ACG氛围，最有创意的Up主。大家可以在这里找到许多欢乐。', 'https://member.bilibili.com/', 16, 0, '2021-02-01 15:03:18', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100527, 150725316141101035, '大风号', '/profile/site/system/839113b2-755e-422c-9130-ae6544bc1cb7.png', '好内容随风直达', 'https://fhh.ifeng.com/', 22, 0, '2021-02-01 15:09:36', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100528, 150725316141101029, '第一PPT', '/profile/site/system/a6c796a7-9341-4103-b033-49e99db328f7.png', 'PPT模版免费下载', 'http://www.1ppt.com/', 8, 0, '2021-02-02 09:12:17', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100529, 150725316141101029, '51PPT', '/profile/site/system/bb66b865-a667-46b1-acf2-8b8c6da6ec87.png', '幻灯片演示模板及素材下载', 'http://www.51pptmoban.com/', 9, 0, '2021-02-02 09:14:05', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100530, 150725316141101029, '叮当设计', '/profile/site/system/3c5526a5-70fc-4f67-9ad5-65e301991399.png', '分享优秀设计资源，全部免费下载', 'https://www.dingdangsheji.com/', 10, 0, '2021-02-02 09:15:24', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100531, 150725316141101048, 'SubPlayer', '/profile/site/system/6cee691b-9b0d-4a14-b755-ed9540e92bc0.ico', '字幕在线调整，小巧实用性非常强的网站', 'https://subplayer.js.org/', 14, 0, '2021-03-02 15:53:46', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100532, 150725316141101012, '在线音乐播放', '/profile/site/system/87ace2f3-0c6f-455f-97bc-789f0b00f846.png', '可以在线听，可以直接下载。麻雀虽小，五脏俱全。', 'https://www.binye.xyz/Music/', 12, 0, '2021-03-05 17:54:33', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100534, 150725316141101051, 'Versus', '/profile/site/system/a43adc92-08cd-4b2b-af7e-667e216efa08.png', '万物皆可对比。移动电话、城市、显示卡、大学及及其他', 'https://versus.com/cn', 2, 0, '2021-03-21 19:23:36', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100535, 150725316141101012, '52无损音乐', '/profile/site/system/2cb30771-c5bc-4519-a772-b47abb64c085.png', '无损音乐下载_FLAC_WAV_高品质格式无损音乐免费下载', 'http://www.52flac.com/', 13, 0, '2021-03-23 23:48:01', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100536, 150725316141101012, '炫音音乐论坛', '/profile/site/system/325b7386-aefc-41ee-be4a-63a228de00fc.png', '总有一种声音能打动你', 'https://bbs.musicool.cn/', 14, 0, '2021-03-23 23:49:54', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100538, 150725316141101052, '搜图导航', '/profile/site/system/09fbdff3-6b8e-4c39-ac01-d3356098529b.png', '解决您的搜图需求，包含大量图片网站', 'https://gesdh.cn/share/st', 2, 0, '2021-03-27 00:38:11', '2023-05-19 21:05:00', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100539, 150725316141101052, '自媒体导航', '/profile/site/system/3a9c73f6-0656-40b8-9789-f5f0f55be1f5.png', '自媒体专业导航，包含运营平台、排版工具、图片素材等', 'https://gesdh.cn/share/S8F3C', 3, 0, '2021-03-27 00:41:37', '2023-05-19 21:05:00', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100540, 150725316141101052, '视频号导航', '/profile/site/system/12448992-5513-4826-836c-afa86cb16d3e.png', '视频号相关网站，包括玩家必备、数据分析等', 'https://gesdh.cn/share/shipinhao', 4, 0, '2021-03-27 00:44:04', '2023-05-19 21:05:00', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100541, 150725316141101052, '视频创作导航', '/profile/site/system/dbbe4e57-13aa-4d16-b863-d4c0e74a83c5.png', '好用又专业的视频制作导航网站', 'https://gesdh.cn/share/sp', 5, 0, '2021-03-27 00:45:42', '2023-05-19 21:05:00', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100542, 150725316141101052, '创业神器导航', '/profile/site/system/801ef7f2-28af-4f99-8f52-db7d138d4235.png', '分享创业资源和互联网工具', 'https://gesdh.cn/share/cysq', 7, 0, '2021-03-27 00:51:44', '2023-05-19 21:05:00', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100543, 150725316141101052, '排行榜导航', '/profile/site/system/69a024f8-cada-46c4-9150-fecb932115d0.png', '各类榜单排名大全，包括热搜榜、热议榜等', 'https://gesdh.cn/share/phb', 6, 1, '2021-03-27 00:55:01', '2023-05-19 21:05:00', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100544, 150725316141101052, '运营辅助工具导航', '/profile/site/system/46e442b9-e4cc-43f5-8997-89bccd50b803.png', '分享运营相关网站', 'https://gesdh.cn/share/yyfzgj', 8, 0, '2021-03-27 00:56:26', '2023-05-19 21:05:00', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100545, 150725316141101052, '程序员导航', '/profile/site/system/f741cece-de4a-4e46-a237-07a0ad90c303.png', '程序员自用网站，包括常用推荐、学习教程等', 'https://gesdh.cn/share/6hMpM', 9, 0, '2021-03-27 00:58:32', '2023-05-19 21:05:00', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100546, 150725316141101004, 'PDF 派', '/profile/site/system/2ececa01-18ea-45f5-a168-8421def8ff3d.ico', '几十个强大的PDF在线工具，永久免费，没有注册入口，人人都是VIP！', 'https://www.pdfpai.com/', 8, 0, '2021-04-20 11:42:52', '2021-04-20 11:43:17', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100547, 150725316141101029, '微软 PPT', '/profile/site/system/2bd1b9b8-e0be-4107-8fe5-3550a619fe1d.ico', 'Office 模板和主题，使用 Microsoft 模板创建更多内容', 'https://templates.office.com/', 11, 0, '2021-04-20 11:45:53', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100548, 150725316141101029, 'Slidesgo', '/profile/site/system/24475701-da5f-4aa3-852e-033f55668ed9.png', '免费的谷歌幻灯片和PowerPoint模板，让你的演讲更精彩', 'https://slidesgo.com/', 12, 0, '2021-04-20 11:47:41', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100549, 150725316141101029, 'Hislide', '/profile/site/system/13f71e2b-7687-47ea-861a-ccf4caf2f60b.png', '免费的PowerPoint，谷歌幻灯片，Keynote模板', 'https://hislide.io/', 13, 0, '2021-04-20 11:48:58', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100550, 150725316141101029, 'Rrslide', '/profile/site/system/efad445e-1b5c-4e4c-9afc-0b6b21972b05.png', '下载免费的ppt模板，基调，主题和更多_ RRSlide _最畅销的演示模板市场', 'https://rrslide.com/', 14, 0, '2021-04-20 11:51:32', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100553, 150725316141101036, '图司机', '/profile/site/system/0478f006-ff16-4504-963f-8b28fd24af6d.png', '免费图片在线PS编辑器，10秒搞定平面设计！', 'https://www.tusij.com/', 0, 0, '2021-04-22 21:32:27', '2021-04-22 21:32:35', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100554, 150725316141101050, 'ProcessOn免费在线作图', '/profile/site/system/25fc6979-4a05-49ab-a9dc-3cc0daa76e0c.ico', '在线作图工具的聚合平台， 它可以在线画流程图、思维导图、UI原型图、UML、网络拓扑图、组织结构图等等， 您无需担心下载和更新的问题，不管Mac还是Windows，一个浏览器就可以随时随地的发挥创意，规划工作', 'https://processon.com', 11, 0, '2021-05-13 23:22:40', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100555, 150725316141101042, 'JustAuth', '/profile/site/system/fdf62f71-41e8-4454-8dbd-f1272ed9eac5.png', '小而全而美的第三方登录开源组件。目前已支持Github、Gitee、微博、钉钉、百度、Coding、腾讯云开发者平台、OSChina、支付宝、QQ、微信、淘宝、Google、Facebook、抖音、领英、小米、微软、今日头条、Teambition、StackOverflow、Pinterest、人人、华为、企业微信、酷家乐、Gitlab、美团、饿了么、推特、飞书、京东、阿里云、喜马拉雅、Amazon、Slack和 Line 等第三方平台的授权登录。', 'https://gitee.com/yadong.zhang/JustAuth', 8, 0, '2021-05-31 23:11:03', '2021-05-31 23:11:52', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100556, 150725316141101050, '语雀', '/profile/site/system/6da5aba9-a474-4fb4-82b0-880cfc32237e.png', '专业的云端知识库，个人笔记与知识创作，团队协同与知识沉淀。', 'https://www.yuque.com/', 12, 0, '2021-06-05 10:20:20', '2021-06-12 14:32:16', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100557, 150725316141101048, '爱给网', '/profile/site/system/fec4eb53-e5d2-4cb5-a16e-5ed41e4cc1d4.ico', '音效配乐_3D模型_视频素材_游戏素材。中国最大的数字娱乐免费素材下载网站,免费提供免费的音效配乐|3D模型|视频|游戏素材资源下载。', 'https://www.aigei.com/', 15, 0, '2021-07-25 11:42:08', '2021-07-25 11:44:39', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100558, 150725316141101017, 'IT工具网', '/profile/site/system/a3ccbeef-5ba8-46d3-bf9b-07cc90aa202c.ico', '在线实用工具_代码工具_写作辅助工具', 'https://www.coder.work', 23, 0, '2021-08-02 21:45:14', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100559, 150725316141101037, '电脑壁纸', '/profile/site/system/25fd04c8-3419-4919-ac66-8c6dd822898e.png', '一个自采集壁纸站', 'http://lab.mkblog.cn/wallpaper/', 14, 0, '2021-08-11 22:57:13', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100560, 150725316141101037, '3G壁纸', '/profile/site/system/628bf055-387d-49a5-b403-5292f79354be.png', '电脑壁纸专家', 'https://desk.3gbizhi.com/', 15, 0, '2021-08-11 23:02:53', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100562, 150725316141101048, '混剪侠 预告片世界', '/profile/site/system/e9b3cfca-739b-427b-b52b-bdb6899b2945.png', '最新电影预告片免费下载', 'https://www.yugaopian.cn/', 16, 0, '2021-09-07 00:05:24', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100563, 150725316141101042, 'vue-admin-beautiful', '/profile/site/system/4864e82c-69e2-4868-8501-0c9a06ff56c6.png', '国内首个基于vue3.0的开源admin项目，同时支持电脑，手机，平板', 'https://gitee.com/chu1204505056/vue-admin-beautiful', 9, 0, '2021-09-27 07:55:13', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100566, 150725316141101001, '奈斯搜索', '/profile/site/system/2c3be62e-9584-4db0-9940-509f13f9d0fc.png', '资源超丰富的阿里云盘资源搜索引擎', 'https://www.niceso.fun/', 21, 0, '2021-12-10 22:10:33', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100567, 150725316141101004, 'PDF24 Tools', '/profile/site/system/0d8de034-7e9e-4e7b-8f87-1679cc5cdc55.svg', '免费且易于使用的在线PDF工具，不限制文件大小。', 'https://tools.pdf24.org/zh/', 13, 0, '2022-01-16 20:31:31', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100568, 150725316141101011, '视中心影院', '/profile/site/system/84734031-d40d-40d1-b30c-c13fa29a65f1.png', '全新电视剧,全新动漫,全新综艺节目排行榜,免费在线观看全网电影,动作片,喜剧片,爱情片,搞笑片等全新电影,更多电影高清在线观看尽在视中心影院', 'https://www.mhz8.com/', 30, 0, '2022-03-13 15:03:21', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100569, 150725316141101042, 'vue-admin-better', '/profile/site/system/4864e82c-69e2-4868-8501-0c9a06ff56c6.png', '国内首个基于vue3.0的开源admin项目，同时支持电脑，手机，平板', 'https://github.com/chuzhixin/vue-admin-better', 10, 0, '2022-03-14 23:26:42', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100570, 150725316141101031, 'Element Plus', '/profile/site/system/6a17c0cb-36ad-4029-9570-0b272a07055d.svg', '基于 Vue 3，面向设计师和开发者的组件库', 'https://element-plus.gitee.io/zh-CN/', 3, 0, '2022-06-11 17:30:09', '2022-06-11 17:30:44', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100571, 150725316141101042, '格姗知识圈 / geshanzsq-blog', '/profile/site/system/logo.jpg', '一个基于 Spring Boot、Spring Security、Vue3、Element Plus 的前后端分离的博客网站！', 'https://gitee.com/geshanzsq/geshanzsq-blog', 0, 0, '2022-11-03 21:43:34', '2022-11-03 21:44:25', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100572, 150725316141101017, 'Wormhole', '/profile/site/system/4bef67c6-4105-48ea-8974-b5e565cb6179.png', '一个没有任何限制的文件传输网站，打开就可以选择上传需要临时保存的文件或文件夹，一次可以免费上传10GB以内的大小文件。', 'https://wormhole.app', 24, 0, '2023-01-09 20:26:45', '2023-01-09 20:27:23', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100573, 150725316141101048, 'Hotbox', '/profile/site/system/c4fc3995-0b5d-44f2-8220-8e40db0324e7.png', '一个完全免费的视频下载网站，能够下载多个平台的视频文件，并且还支持自定义选择视频清晰度以及单独的音频下载。', 'https://www.hotbox.fun', 17, 0, '2023-01-09 20:29:53', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100574, 150725316141101048, 'DeepL', '/profile/site/system/4117453c-4615-4245-a59f-f9e03b38b1d6.png', '一个非常好用的在线文字、文档翻译网站，下载的文档文献资料都能一键翻译成需要的语种，支持翻译上百种语种', 'https://www.deepl.com/translator', 18, 0, '2023-01-09 20:31:55', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100575, 150725316141101004, '贴图士', '/profile/site/system/3a981b05-bd12-426a-8094-91eeff7e6e49.png', '一个免费的视频格式转换以及图片压缩网站。打开页面会看到多种图片压缩格式、视频转GIF、GIF合并、GIF裁剪等功能。', 'https://www.tutieshi.com/video/', 14, 0, '2023-01-09 20:33:36', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100576, 150725316141101008, 'Latest10', '/profile/site/system/804cc2a0-afcc-4984-b3b4-dfd6154e527b.png', '获取最新的系统镜像', 'https://latest10.win/', 11, 0, '2023-02-18 19:47:07', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100577, 150725316141101008, '极简系统', '/profile/site/system/67a5a719-0b09-4807-9185-535b7b4ce107.ico', '最纯净的系统下载平台', 'https://www.sysmini.com/', 12, 0, '2023-02-18 19:48:03', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100578, 150725316141101008, '微软官方系统', '/profile/site/system/b066263b-a469-4fa1-a1dc-df8f86869eb8.png', '正版官方下载', 'https://www.microsoft.com/zh-cn/software-download/', 13, 1, '2023-02-18 19:49:50', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100579, 150725316141101048, 'VideoFk', '/profile/site/system/7adb50a1-6511-438f-b713-2662bc747155.png', '在线视频下载，下载视频转换为 MP4 最佳网站以从All在线下载视频', 'https://www.videofk.com/', 19, 0, '2023-02-27 23:23:09', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (150725316141100580, 150725316141101052, 'AI 导航', '/profile/site/system/65da39e3-2d3b-4e7c-8590-73ac52b2fbeb.png', '超全的国内外 AI 应用', 'https://gesdh.cn/share/ai', 1, 0, '2023-03-31 00:40:32', '2023-05-19 21:05:00', 1, 1, 1);\nINSERT INTO `nav_site` VALUES (150725316141100581, 150725316141101035, '抖音创作服务平台', '/profile/site/system/da278792-b8ec-4073-8cd4-660fc24acb7b.ico', '抖音创作服务平台', 'https://creator.douyin.com/', 23, 0, '2023-04-05 22:21:33', NULL, 1, NULL, 1);\nINSERT INTO `nav_site` VALUES (301450632282221536, 150725316141101050, '飞书文档', '/profile/site/system/2023/05/15/8877512b-ad2c-4690-800b-fc5121dda9d6.jpeg', '新一代高效协作工具,融合了在线文档和协同文档的所有功能,不仅能插入在线表格,将数据表转换成看板,还能用思维笔记,将思考路径可视化,更有丰富模板满足多场景创作需求。', 'https://bqfeun1dwu8.feishu.cn', 13, 0, '2023-05-15 22:08:36', NULL, 1, NULL, 1);\n\n-- ----------------------------\n-- Table structure for nav_site_config\n-- ----------------------------\nDROP TABLE IF EXISTS `nav_site_config`;\nCREATE TABLE `nav_site_config`  (\n  `id` bigint(20) NOT NULL COMMENT '配置 id',\n  `about_site_description` varchar(500) NULL DEFAULT '' COMMENT '关于本站描述',\n  `about_site_email` varchar(50) NULL DEFAULT '' COMMENT '关于本站邮箱',\n  `about_site_content` longtext NULL COMMENT '关于本站内容',\n  `about_site_visit_count` int(11) NULL DEFAULT 0 COMMENT '关于本站访问量',\n  `fk_create_user_id` bigint(20) NULL DEFAULT NULL COMMENT '创建人用户 id',\n  `gmt_create` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `fk_modify_user_id` bigint(20) NULL DEFAULT NULL COMMENT '修改人用户 id',\n  `gmt_modify` datetime NULL DEFAULT NULL COMMENT '修改时间',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB COMMENT = '网站配置' ROW_FORMAT = DYNAMIC;\n\n-- ----------------------------\n-- Records of nav_site_config\n-- ----------------------------\nINSERT INTO `nav_site_config` VALUES (152819881111191552, '这个导航网站来源于格姗导航的开源项目。如果你有更好的想法，可以通过左边的邮箱与我联系。如果喜欢本站，可以分享给其他人，或者设置为主页，这是对我最大的支持！\\'', '497301391@qq.com', '<p>这是一个导航网站,收入了大部分常用的网站，希望能够解决到你频繁收藏网站的烦恼！</p><p>显然，这是一个开源项目，主要放一些自己经常用到的网站。</p><p>开源项目来源：<a href=\\\"\\\\\\\" target=\\\"\\\\\\\">https://gitee.com/geshanzsq/geshanzsq-nav</a></p>', 10, 43728307660783616, '2023-05-21 16:51:39', 43728307660783616, '2023-05-21 16:58:51');\n\n-- ----------------------------\n-- Table structure for sys_api\n-- ----------------------------\nDROP TABLE IF EXISTS `sys_api`;\nCREATE TABLE `sys_api`  (\n  `id` bigint(20) NOT NULL COMMENT '接口 id',\n  `api_name` varchar(50) NULL DEFAULT '' COMMENT '接口名称',\n  `api_url` varchar(255) NULL DEFAULT '' COMMENT '接口地址',\n  `api_method` varchar(10) NULL DEFAULT '' COMMENT '接口请求方式（如：get，post）',\n  `fk_api_category_id` varchar(25) NULL DEFAULT '' COMMENT '所属分类 id',\n  `sort` int(11) NULL DEFAULT 0 COMMENT '排序',\n  `status` tinyint(1) NULL DEFAULT 1 COMMENT '状态（1 正常，2 停用）',\n  `remark` varchar(500) NULL DEFAULT '' COMMENT '备注',\n  `fk_create_user_id` bigint(20) NULL DEFAULT NULL COMMENT '创建人用户 id',\n  `gmt_create` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `fk_modify_user_id` bigint(20) NULL DEFAULT NULL COMMENT '修改人用户 id',\n  `gmt_modify` datetime NULL DEFAULT NULL COMMENT '修改时间',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB COMMENT = '系统接口' ROW_FORMAT = DYNAMIC;\n\n-- ----------------------------\n-- Records of sys_api\n-- ----------------------------\nINSERT INTO `sys_api` VALUES (43759552742555648, '分页列表', '/system/user/page', 'GET', '43757041415618560', 1, 1, '', 43728307660783616, '2022-07-24 18:04:50', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43759676889759744, '详情', '/system/user/getById/*', 'GET', '43757041415618560', 2, 1, '', 43728307660783616, '2022-07-24 18:05:20', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43761152815005696, '新增', '/system/user', 'POST', '43757041415618560', 3, 1, '', 43728307660783616, '2022-07-24 18:11:12', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43761187950690304, '修改', '/system/user', 'PUT', '43757041415618560', 4, 1, '', 43728307660783616, '2022-07-24 18:11:20', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43761278916755456, '删除', '/system/user/delete/*', 'DELETE', '43757041415618560', 5, 1, '', 43728307660783616, '2022-07-24 18:11:42', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43762647782391808, '重置密码', '/system/user/resetPassword', 'PUT', '43757041415618560', 6, 1, '', 43728307660783616, '2022-07-24 18:17:08', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43762807694426112, '分页列表', '/system/role/page', 'GET', '43757069869776896', 1, 1, '', 43728307660783616, '2022-07-24 18:17:46', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43762858923655168, '详情', '/system/role/getById/*', 'GET', '43757069869776896', 2, 1, '', 43728307660783616, '2022-07-24 18:17:59', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43763188797276160, '新增', '/system/role', 'POST', '43757069869776896', 3, 1, '', 43728307660783616, '2022-07-24 18:19:17', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43763232950714368, '修改', '/system/role', 'PUT', '43757069869776896', 4, 1, '', 43728307660783616, '2022-07-24 18:19:28', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43763311845572608, '删除', '/system/role/delete/*', 'DELETE', '43757069869776896', 5, 1, '', 43728307660783616, '2022-07-24 18:19:47', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43763442175180800, '获取最大排序', '/system/role/getMaxSort', 'GET', '43757069869776896', 6, 1, '', 43728307660783616, '2022-07-24 18:20:18', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43763523695673344, '已分配用户分页', '/system/role/auth/user/page', 'GET', '43757069869776896', 7, 1, '', 43728307660783616, '2022-07-24 18:20:37', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43763593107210240, '未分配用户分页', '/system/role/auth/user/not/page', 'GET', '43757069869776896', 8, 1, '', 43728307660783616, '2022-07-24 18:20:54', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43763693447544832, '授权用户', '/system/role/auth/user', 'POST', '43757069869776896', 9, 1, '', 43728307660783616, '2022-07-24 18:21:18', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43763818857234432, '取消授权', '/system/role/auth/user/delete', 'DELETE', '43757069869776896', 10, 1, '', 43728307660783616, '2022-07-24 18:21:48', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43763961681674240, '列表', '/system/menu/list', 'GET', '43758191430860800', 1, 1, '', 43728307660783616, '2022-07-24 18:22:22', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43764022897541120, '树形', '/system/menu/tree', 'GET', '43758191430860800', 2, 1, '', 43728307660783616, '2022-07-24 18:22:36', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43764081659740160, '详情', '/system/menu/getById/*', 'GET', '43758191430860800', 3, 1, '', 43728307660783616, '2022-07-24 18:22:50', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43764130418524160, '新增', '/system/menu', 'POST', '43758191430860800', 4, 1, '', 43728307660783616, '2022-07-24 18:23:02', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43764160827228160, '修改', '/system/menu', 'PUT', '43758191430860800', 5, 1, '', 43728307660783616, '2022-07-24 18:23:09', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43764235854938112, '删除', '/system/menu/delete/*', 'DELETE', '43758191430860800', 6, 1, '', 43728307660783616, '2022-07-24 18:23:27', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43764326162497536, '获取最大排序', '/system/menu/getMaxSortByParentId', 'GET', '43758191430860800', 7, 1, '', 43728307660783616, '2022-07-24 18:23:48', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43764505833897984, '分页列表', '/system/dictionary/page', 'GET', '43758342685851648', 1, 1, '', 43728307660783616, '2022-07-24 18:24:31', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43764571281817600, '列表', '/system/dictionary/list', 'GET', '43758342685851648', 2, 1, '', 43728307660783616, '2022-07-24 18:24:47', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43764610037186560, '新增', '/system/dictionary', 'POST', '43758342685851648', 3, 1, '', 43728307660783616, '2022-07-24 18:24:56', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43764642895364096, '修改', '/system/dictionary', 'PUT', '43758342685851648', 5, 1, '', 43728307660783616, '2022-07-24 18:25:04', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43764689900929024, '删除', '/system/dictionary/delete/*', 'DELETE', '43758342685851648', 6, 1, '', 43728307660783616, '2022-07-24 18:25:15', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43764765633282048, '获取最大排序', '/system/dictionary/getMaxSort', 'GET', '43758342685851648', 7, 1, '', 43728307660783616, '2022-07-24 18:25:33', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43764977332387840, '分页列表', '/system/dictionary/data/page', 'GET', '43758386155618304', 1, 1, '', 43728307660783616, '2022-07-24 18:26:24', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43765061612732416, '详情', '/system/dictionary/data/getById/*', 'GET', '43758386155618304', 2, 1, '', 43728307660783616, '2022-07-24 18:26:44', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43765127371030528, '新增', '/system/dictionary/data', 'POST', '43758386155618304', 3, 1, '', 43728307660783616, '2022-07-24 18:26:59', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43765165673414656, '修改', '/system/dictionary/data', 'PUT', '43758386155618304', 4, 1, '', 43728307660783616, '2022-07-24 18:27:09', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43765212116942848, '删除', '/system/dictionary/data/delete/*', 'DELETE', '43758386155618304', 5, 1, '', 43728307660783616, '2022-07-24 18:27:20', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43765276512092160, '获取最大排序', '/system/dictionary/data/getMaxSortByDictionaryId', 'GET', '43758386155618304', 6, 1, '', 43728307660783616, '2022-07-24 18:27:35', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43765395617742848, '分页列表', '/system/api/category/page', 'GET', '43758515155632128', 1, 1, '', 43728307660783616, '2022-07-24 18:28:03', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43765435992113152, '列表', '/system/api/category/list', 'GET', '43758515155632128', 2, 1, '', 43728307660783616, '2022-07-24 18:28:13', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43765499997192192, '详情', '/system/api/category/getById/*', 'GET', '43758515155632128', 3, 1, '', 43728307660783616, '2022-07-24 18:28:28', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43765537020313600, '新增', '/system/api/category', 'POST', '43758515155632128', 4, 1, '', 43728307660783616, '2022-07-24 18:28:37', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43765569287094272, '修改', '/system/api/category', 'PUT', '43758515155632128', 5, 1, '', 43728307660783616, '2022-07-24 18:28:45', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43765629844455424, '删除', '/system/api/category/delete/*', 'DELETE', '43758515155632128', 6, 1, '', 43728307660783616, '2022-07-24 18:28:59', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43765696206733312, '获取最大排序', '/system/api/category/getMaxSort', 'GET', '43758515155632128', 7, 1, '', 43728307660783616, '2022-07-24 18:29:15', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43765773721665536, '分页列表', '/system/api/page', 'GET', '43758723595763712', 1, 1, '', 43728307660783616, '2022-07-24 18:29:34', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43765827249373184, '详情', '/system/api/getById/*', 'GET', '43758723595763712', 2, 1, '', 43728307660783616, '2022-07-24 18:29:46', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43766245937381376, '新增', '/system/api', 'POST', '43758723595763712', 3, 1, '', 43728307660783616, '2022-07-24 18:31:26', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43766278116081664, '修改', '/system/api', 'PUT', '43758723595763712', 4, 1, '', 43728307660783616, '2022-07-24 18:31:34', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43766323594919936, '删除', '/system/api/delete/*', 'DELETE', '43758723595763712', 5, 1, '', 43728307660783616, '2022-07-24 18:31:45', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43766380444516352, '获取最大排序', '/system/api/getMaxSortByCategoryId', 'GET', '43758723595763712', 6, 1, '', 43728307660783616, '2022-07-24 18:31:58', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43766697940746240, '分页列表', '/system/log/login/page', 'GET', '43766546228576256', 1, 1, '', 43728307660783616, '2022-07-24 18:33:14', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43766812147449856, '分页列表', '/system/log/operation/page', 'GET', '43766567304953856', 1, 1, '', 43728307660783616, '2022-07-24 18:33:41', NULL, NULL);\nINSERT INTO `sys_api` VALUES (43766869047377920, '详情', '/system/log/operation/getById/*', 'GET', '43766567304953856', 2, 1, '', 43728307660783616, '2022-07-24 18:33:55', NULL, NULL);\nINSERT INTO `sys_api` VALUES (45287586729558016, '详情', '/system/dictionary/getById/*', 'GET', '43758342685851648', 4, 1, '', 43728307660783616, '2022-07-28 23:16:42', NULL, NULL);\nINSERT INTO `sys_api` VALUES (52141509008424960, '分页列表', '/system/param/page', 'GET', '52133147512406016', 1, 1, '', 43728307660783616, '2022-08-16 21:11:44', NULL, NULL);\nINSERT INTO `sys_api` VALUES (52141659554578432, '详情', '/system/param/getById/*', 'GET', '52133147512406016', 2, 1, '', 43728307660783616, '2022-08-16 21:12:20', NULL, NULL);\nINSERT INTO `sys_api` VALUES (52141709437435904, '新增', '/system/param', 'POST', '52133147512406016', 3, 1, '', 43728307660783616, '2022-08-16 21:12:32', NULL, NULL);\nINSERT INTO `sys_api` VALUES (52141758401740800, '修改', '/system/param', 'PUT', '52133147512406016', 4, 1, '', 43728307660783616, '2022-08-16 21:12:44', NULL, NULL);\nINSERT INTO `sys_api` VALUES (52141803498897408, '删除', '/system/param/delete/*', 'DELETE', '52133147512406016', 5, 1, '', 43728307660783616, '2022-08-16 21:12:55', NULL, NULL);\nINSERT INTO `sys_api` VALUES (52141938496765952, '获取最大排序', '/system/param/getMaxSort', 'GET', '52133147512406016', 6, 1, '', 43728307660783616, '2022-08-16 21:13:27', NULL, NULL);\nINSERT INTO `sys_api` VALUES (86832919972151297, '列表', '/nav/category/list', 'GET', '86832919972151296', 1, 1, '', 43728307660783616, '2022-11-20 14:43:41', NULL, NULL);\nINSERT INTO `sys_api` VALUES (86832919972151298, '详情', '/nav/category/getById/*', 'GET', '86832919972151296', 2, 1, '', 43728307660783616, '2022-11-20 14:43:41', NULL, NULL);\nINSERT INTO `sys_api` VALUES (86832919972151299, '新增', '/nav/category', 'POST', '86832919972151296', 3, 1, '', 43728307660783616, '2022-11-20 14:43:41', NULL, NULL);\nINSERT INTO `sys_api` VALUES (86832919972151300, '修改', '/nav/category', 'PUT', '86832919972151296', 4, 1, '', 43728307660783616, '2022-11-20 14:43:41', NULL, NULL);\nINSERT INTO `sys_api` VALUES (86832919972151301, '删除', '/nav/category/delete/*', 'DELETE', '86832919972151296', 5, 1, '', 43728307660783616, '2022-11-20 14:43:41', NULL, NULL);\nINSERT INTO `sys_api` VALUES (86832919972151302, '获取最大排序', '/nav/category/getMaxSortByParentId', 'GET', '86832919972151296', 6, 1, '', 43728307660783616, '2022-11-20 14:43:41', NULL, NULL);\nINSERT INTO `sys_api` VALUES (86859268749262849, '分页列表', '/nav/site/page', 'GET', '86859268749262848', 1, 1, '', 43728307660783616, '2022-11-20 16:27:55', NULL, NULL);\nINSERT INTO `sys_api` VALUES (86859268749262850, '详情', '/nav/site/getById/*', 'GET', '86859268749262848', 2, 1, '', 43728307660783616, '2022-11-20 16:27:55', NULL, NULL);\nINSERT INTO `sys_api` VALUES (86859268749262851, '新增', '/nav/site', 'POST', '86859268749262848', 3, 1, '', 43728307660783616, '2022-11-20 16:27:55', NULL, NULL);\nINSERT INTO `sys_api` VALUES (86859268749262852, '修改', '/nav/site', 'PUT', '86859268749262848', 4, 1, '', 43728307660783616, '2022-11-20 16:27:55', NULL, NULL);\nINSERT INTO `sys_api` VALUES (86859268749262853, '删除', '/nav/site/delete/*', 'DELETE', '86859268749262848', 5, 1, '', 43728307660783616, '2022-11-20 16:27:55', NULL, NULL);\nINSERT INTO `sys_api` VALUES (86859268749262854, '获取最大排序', '/nav/site/getMaxSort/', 'GET', '86859268749262848', 6, 1, '', 43728307660783616, '2022-11-20 16:27:55', NULL, NULL);\nINSERT INTO `sys_api` VALUES (92732732718710784, '分页列表', '/nav/comment/page', 'GET', '92732554183966720', 1, 1, '', 43728307660783616, '2022-12-06 21:26:46', NULL, NULL);\nINSERT INTO `sys_api` VALUES (92732865405517824, '通过', '/nav/comment/pass/*', 'PUT', '92732554183966720', 2, 1, '', 43728307660783616, '2022-12-06 21:27:18', NULL, NULL);\nINSERT INTO `sys_api` VALUES (92732919084220416, '驳回', '/nav/comment/reject', 'PUT', '92732554183966720', 3, 1, '', 43728307660783616, '2022-12-06 21:27:31', NULL, NULL);\nINSERT INTO `sys_api` VALUES (92733072901931008, '置顶', '/nav/comment/sticky/*', 'PUT', '92732554183966720', 4, 1, '', 43728307660783616, '2022-12-06 21:28:07', NULL, NULL);\nINSERT INTO `sys_api` VALUES (92733166082588672, '取消置顶', '/nav/comment/cancelSticky/*', 'PUT', '92732554183966720', 5, 1, '', 43728307660783616, '2022-12-06 21:28:30', NULL, NULL);\nINSERT INTO `sys_api` VALUES (92733276258566144, '删除', '/nav/comment/delete/*', 'DELETE', '92732554183966720', 6, 1, '', 43728307660783616, '2022-12-06 21:28:56', NULL, NULL);\nINSERT INTO `sys_api` VALUES (94456256298745856, '获取配置', '/nav/config/getConfig', 'GET', '94456130222161920', 1, 1, '', 43728307660783616, '2022-12-11 15:35:26', NULL, NULL);\nINSERT INTO `sys_api` VALUES (94456330005250048, '修改', '/nav/config', 'PUT', '94456130222161920', 2, 1, '', 43728307660783616, '2022-12-11 15:35:44', NULL, NULL);\n\n-- ----------------------------\n-- Table structure for sys_api_category\n-- ----------------------------\nDROP TABLE IF EXISTS `sys_api_category`;\nCREATE TABLE `sys_api_category`  (\n  `id` bigint(20) NOT NULL COMMENT '接口分类 id',\n  `category_name` varchar(50) NULL DEFAULT '' COMMENT '分类名称',\n  `sort` int(11) NULL DEFAULT 0 COMMENT '排序',\n  `status` tinyint(1) NULL DEFAULT 1 COMMENT '状态（1 正常，2 停用）',\n  `remark` varchar(500) NULL DEFAULT '' COMMENT '备注',\n  `fk_create_user_id` bigint(20) NULL DEFAULT NULL COMMENT '创建人用户 id',\n  `gmt_create` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `fk_modify_user_id` bigint(20) NULL DEFAULT NULL COMMENT '修改人用户 id',\n  `gmt_modify` datetime NULL DEFAULT NULL COMMENT '修改时间',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB COMMENT = '系统接口分类' ROW_FORMAT = DYNAMIC;\n\n-- ----------------------------\n-- Records of sys_api_category\n-- ----------------------------\nINSERT INTO `sys_api_category` VALUES (43757041415618560, '用户管理', 1, 1, '', 43728307660783616, '2022-07-24 17:54:52', NULL, NULL);\nINSERT INTO `sys_api_category` VALUES (43757069869776896, '角色管理', 2, 1, '', 43728307660783616, '2022-07-24 17:54:58', NULL, NULL);\nINSERT INTO `sys_api_category` VALUES (43758191430860800, '菜单管理', 3, 1, '', 43728307660783616, '2022-07-24 17:59:26', NULL, NULL);\nINSERT INTO `sys_api_category` VALUES (43758342685851648, '数据字典', 4, 1, '', 43728307660783616, '2022-07-24 18:00:02', NULL, NULL);\nINSERT INTO `sys_api_category` VALUES (43758386155618304, '数据字典数据', 5, 1, '', 43728307660783616, '2022-07-24 18:00:12', NULL, NULL);\nINSERT INTO `sys_api_category` VALUES (43758515155632128, 'API 接口分类', 6, 1, '', 43728307660783616, '2022-07-24 18:00:43', NULL, NULL);\nINSERT INTO `sys_api_category` VALUES (43758723595763712, 'API 接口', 7, 1, '\\n', 43728307660783616, '2022-07-24 18:01:33', NULL, NULL);\nINSERT INTO `sys_api_category` VALUES (43766546228576256, '登录日志', 9, 1, '', 43728307660783616, '2022-07-24 18:32:38', NULL, NULL);\nINSERT INTO `sys_api_category` VALUES (43766567304953856, '操作日志', 10, 1, '', 43728307660783616, '2022-07-24 18:32:43', NULL, NULL);\nINSERT INTO `sys_api_category` VALUES (52133147512406016, '参数配置', 8, 1, '', 43728307660783616, '2022-08-16 20:38:31', NULL, NULL);\nINSERT INTO `sys_api_category` VALUES (86832919972151296, '导航分类管理', 11, 1, '', 43728307660783616, '2022-11-20 14:43:41', NULL, NULL);\nINSERT INTO `sys_api_category` VALUES (86859268749262848, '导航网站管理', 12, 1, '', 43728307660783616, '2022-11-20 16:27:55', NULL, NULL);\nINSERT INTO `sys_api_category` VALUES (92732554183966720, '评论管理', 13, 1, '', 43728307660783616, '2022-12-06 21:26:04', NULL, NULL);\nINSERT INTO `sys_api_category` VALUES (94456130222161920, '网站配置', 16, 1, '', 43728307660783616, '2022-12-11 15:34:56', NULL, NULL);\n\n-- ----------------------------\n-- Table structure for sys_api_menu\n-- ----------------------------\nDROP TABLE IF EXISTS `sys_api_menu`;\nCREATE TABLE `sys_api_menu`  (\n  `id` bigint(20) NOT NULL COMMENT '接口菜单 id',\n  `fk_api_id` bigint(20) NOT NULL COMMENT '接口 id',\n  `fk_menu_id` bigint(20) NOT NULL COMMENT '菜单 id',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB COMMENT = '系统接口菜单' ROW_FORMAT = DYNAMIC;\n\n-- ----------------------------\n-- Records of sys_api_menu\n-- ----------------------------\nINSERT INTO `sys_api_menu` VALUES (44934431223316480, 43759552742555648, 43738261293629440);\nINSERT INTO `sys_api_menu` VALUES (44941778175918080, 43761152815005696, 43779604086784000);\nINSERT INTO `sys_api_menu` VALUES (44942911996952576, 43759676889759744, 43779675532558336);\nINSERT INTO `sys_api_menu` VALUES (44942911996952577, 43761187950690304, 43779675532558336);\nINSERT INTO `sys_api_menu` VALUES (44943459030663168, 43761278916755456, 43779734387032064);\nINSERT INTO `sys_api_menu` VALUES (44943477556903936, 43762647782391808, 43779823738290176);\nINSERT INTO `sys_api_menu` VALUES (44943667110084608, 43762807694426112, 43738420110950400);\nINSERT INTO `sys_api_menu` VALUES (44943839235932160, 43762858923655168, 43780015896133632);\nINSERT INTO `sys_api_menu` VALUES (44943839235932161, 43763232950714368, 43780015896133632);\nINSERT INTO `sys_api_menu` VALUES (44943888078602240, 43763311845572608, 43780063644090368);\nINSERT INTO `sys_api_menu` VALUES (44943942797492224, 43763188797276160, 43779962246791168);\nINSERT INTO `sys_api_menu` VALUES (44943942797492225, 43763442175180800, 43779962246791168);\nINSERT INTO `sys_api_menu` VALUES (45286549805334528, 43763523695673344, 43780156136882176);\nINSERT INTO `sys_api_menu` VALUES (45286549805334529, 43763593107210240, 43780156136882176);\nINSERT INTO `sys_api_menu` VALUES (45286549805334530, 43763693447544832, 43780156136882176);\nINSERT INTO `sys_api_menu` VALUES (45286600837431296, 43763818857234432, 43780326794723328);\nINSERT INTO `sys_api_menu` VALUES (45286731506778112, 43763961681674240, 43738641683447808);\nINSERT INTO `sys_api_menu` VALUES (45286823873740800, 43764022897541120, 43780571997929472);\nINSERT INTO `sys_api_menu` VALUES (45286823873740801, 43764081659740160, 43780571997929472);\nINSERT INTO `sys_api_menu` VALUES (45286823873740802, 43764160827228160, 43780571997929472);\nINSERT INTO `sys_api_menu` VALUES (45286823873740803, 43764326162497536, 43780571997929472);\nINSERT INTO `sys_api_menu` VALUES (45286924520259584, 43764022897541120, 43780526653308928);\nINSERT INTO `sys_api_menu` VALUES (45286924520259585, 43764130418524160, 43780526653308928);\nINSERT INTO `sys_api_menu` VALUES (45286924520259586, 43764326162497536, 43780526653308928);\nINSERT INTO `sys_api_menu` VALUES (45286959903408128, 43764235854938112, 43780625399808000);\nINSERT INTO `sys_api_menu` VALUES (45287084751060992, 43765435992113152, 43780699760623616);\nINSERT INTO `sys_api_menu` VALUES (45287084751060993, 43765773721665536, 43780699760623616);\nINSERT INTO `sys_api_menu` VALUES (45287155194396672, 43764505833897984, 43738896395141120);\nINSERT INTO `sys_api_menu` VALUES (45287205698011136, 43764610037186560, 43780957378969600);\nINSERT INTO `sys_api_menu` VALUES (45287205698011137, 43764765633282048, 43780957378969600);\nINSERT INTO `sys_api_menu` VALUES (45287965244522496, 43764642895364096, 43781009912627200);\nINSERT INTO `sys_api_menu` VALUES (45287965244522497, 43764765633282048, 43781009912627200);\nINSERT INTO `sys_api_menu` VALUES (45287965244522498, 45287586729558016, 43781009912627200);\nINSERT INTO `sys_api_menu` VALUES (45288010920493056, 43764689900929024, 43781060101668864);\nINSERT INTO `sys_api_menu` VALUES (45288359009976320, 43765127371030528, 43781756515516416);\nINSERT INTO `sys_api_menu` VALUES (45288359009976321, 43765276512092160, 43781756515516416);\nINSERT INTO `sys_api_menu` VALUES (45288394263101440, 43765061612732416, 43781829500600320);\nINSERT INTO `sys_api_menu` VALUES (45288394263101441, 43765165673414656, 43781829500600320);\nINSERT INTO `sys_api_menu` VALUES (45288394263101442, 43765276512092160, 43781829500600320);\nINSERT INTO `sys_api_menu` VALUES (45288418313240576, 43765212116942848, 43781877487632384);\nINSERT INTO `sys_api_menu` VALUES (45288563960446976, 43764571281817600, 43781692732735488);\nINSERT INTO `sys_api_menu` VALUES (45288563960446977, 43764977332387840, 43781692732735488);\nINSERT INTO `sys_api_menu` VALUES (45288684953534464, 43765395617742848, 43739074015526912);\nINSERT INTO `sys_api_menu` VALUES (45288725575368704, 43765537020313600, 43820727559782400);\nINSERT INTO `sys_api_menu` VALUES (45288725575368705, 43765696206733312, 43820727559782400);\nINSERT INTO `sys_api_menu` VALUES (45288799676137472, 43765499997192192, 43820963283861504);\nINSERT INTO `sys_api_menu` VALUES (45288799676137473, 43765569287094272, 43820963283861504);\nINSERT INTO `sys_api_menu` VALUES (45288799676137474, 43765696206733312, 43820963283861504);\nINSERT INTO `sys_api_menu` VALUES (45288869704237056, 43765629844455424, 43821061610930176);\nINSERT INTO `sys_api_menu` VALUES (45288915245989888, 43765435992113152, 43821227462098944);\nINSERT INTO `sys_api_menu` VALUES (45288915245989889, 43765773721665536, 43821227462098944);\nINSERT INTO `sys_api_menu` VALUES (45288959261016064, 43766245937381376, 43821354134274048);\nINSERT INTO `sys_api_menu` VALUES (45288959261016065, 43766380444516352, 43821354134274048);\nINSERT INTO `sys_api_menu` VALUES (45288998154797056, 43765827249373184, 43821418193879040);\nINSERT INTO `sys_api_menu` VALUES (45288998154797057, 43766278116081664, 43821418193879040);\nINSERT INTO `sys_api_menu` VALUES (45288998154797058, 43766380444516352, 43821418193879040);\nINSERT INTO `sys_api_menu` VALUES (45289037971324928, 43766323594919936, 43821478692519936);\nINSERT INTO `sys_api_menu` VALUES (45289082435141632, 43766697940746240, 43746395802304512);\nINSERT INTO `sys_api_menu` VALUES (45289104912416768, 43766812147449856, 43746859121901568);\nINSERT INTO `sys_api_menu` VALUES (45289104912416769, 43766869047377920, 43746859121901568);\nINSERT INTO `sys_api_menu` VALUES (52142019916595200, 52141509008424960, 52073868344426496);\nINSERT INTO `sys_api_menu` VALUES (52142059640848384, 52141709437435904, 52132922030817280);\nINSERT INTO `sys_api_menu` VALUES (52142059640848385, 52141938496765952, 52132922030817280);\nINSERT INTO `sys_api_menu` VALUES (52142106021462016, 52141659554578432, 52133024904511488);\nINSERT INTO `sys_api_menu` VALUES (52142106025656320, 52141758401740800, 52133024904511488);\nINSERT INTO `sys_api_menu` VALUES (52142106025656321, 52141938496765952, 52133024904511488);\nINSERT INTO `sys_api_menu` VALUES (52142131011125248, 52141803498897408, 52133076263763968);\nINSERT INTO `sys_api_menu` VALUES (86832919972151307, 86832919972151297, 86832919972151303);\nINSERT INTO `sys_api_menu` VALUES (86832919972151308, 86832919972151299, 86832919972151304);\nINSERT INTO `sys_api_menu` VALUES (86832919972151309, 86832919972151300, 86832919972151305);\nINSERT INTO `sys_api_menu` VALUES (86832919972151310, 86832919972151298, 86832919972151305);\nINSERT INTO `sys_api_menu` VALUES (86832919972151311, 86832919972151301, 86832919972151306);\nINSERT INTO `sys_api_menu` VALUES (86832919972151312, 86832919972151302, 86832919972151304);\nINSERT INTO `sys_api_menu` VALUES (86832919972151313, 86832919972151302, 86832919972151305);\nINSERT INTO `sys_api_menu` VALUES (86859268749262859, 86859268749262849, 86859268749262855);\nINSERT INTO `sys_api_menu` VALUES (86859268749262860, 86859268749262851, 86859268749262856);\nINSERT INTO `sys_api_menu` VALUES (86859268749262861, 86859268749262852, 86859268749262857);\nINSERT INTO `sys_api_menu` VALUES (86859268749262862, 86859268749262850, 86859268749262857);\nINSERT INTO `sys_api_menu` VALUES (86859268749262863, 86859268749262853, 86859268749262858);\nINSERT INTO `sys_api_menu` VALUES (86859268749262864, 86859268749262854, 86859268749262856);\nINSERT INTO `sys_api_menu` VALUES (86859268749262865, 86859268749262854, 86859268749262857);\nINSERT INTO `sys_api_menu` VALUES (92733359674884096, 92732732718710784, 90179495205535744);\nINSERT INTO `sys_api_menu` VALUES (92733462183673856, 92732865405517824, 92733418365779968);\nINSERT INTO `sys_api_menu` VALUES (92733623991533568, 92732919084220416, 92733594673348608);\nINSERT INTO `sys_api_menu` VALUES (92733755071922176, 92733072901931008, 92733726269636608);\nINSERT INTO `sys_api_menu` VALUES (92733896281554944, 92733166082588672, 92733853243801600);\nINSERT INTO `sys_api_menu` VALUES (92734000786833408, 92733276258566144, 92733965122666496);\nINSERT INTO `sys_api_menu` VALUES (94456518971228160, 94456256298745856, 94456071543848960);\nINSERT INTO `sys_api_menu` VALUES (94456548784340992, 94456330005250048, 94456484632461312);\n\n-- ----------------------------\n-- Table structure for sys_dictionary\n-- ----------------------------\nDROP TABLE IF EXISTS `sys_dictionary`;\nCREATE TABLE `sys_dictionary`  (\n  `id` bigint(20) NOT NULL COMMENT '字典 id',\n  `dictionary_name` varchar(50) NULL DEFAULT '' COMMENT '字典名称',\n  `dictionary_code` varchar(50) NULL DEFAULT '' COMMENT '字典编码',\n  `sort` int(11) NULL DEFAULT 0 COMMENT '排序',\n  `status` tinyint(1) NULL DEFAULT 1 COMMENT '状态（1 正常，2 停用）',\n  `remark` varchar(500) NULL DEFAULT '' COMMENT '备注',\n  `fk_create_user_id` bigint(20) NULL DEFAULT NULL COMMENT '创建人用户 id',\n  `gmt_create` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `fk_modify_user_id` bigint(20) NULL DEFAULT NULL COMMENT '修改人用户 id',\n  `gmt_modify` datetime NULL DEFAULT NULL COMMENT '修改时间',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB COMMENT = '系统字典' ROW_FORMAT = DYNAMIC;\n\n-- ----------------------------\n-- Records of sys_dictionary\n-- ----------------------------\nINSERT INTO `sys_dictionary` VALUES (43739630905851904, '通用状态', 'commonStatus', 1, 1, '', 43728307660783616, '2022-07-24 16:45:41', NULL, NULL);\nINSERT INTO `sys_dictionary` VALUES (43739903539806208, '用户类型', 'systemUserType', 2, 1, '', 43728307660783616, '2022-07-24 16:46:46', NULL, NULL);\nINSERT INTO `sys_dictionary` VALUES (43740085404827648, '用户性别', 'systemUserSex', 3, 1, '', 43728307660783616, '2022-07-24 16:47:29', NULL, NULL);\nINSERT INTO `sys_dictionary` VALUES (43740399080046592, '菜单是否缓存', 'systemMenuIsCache', 4, 1, '', 43728307660783616, '2022-07-24 16:48:44', NULL, NULL);\nINSERT INTO `sys_dictionary` VALUES (43740514784116736, '菜单是否外链', 'systemMenuIsFrame', 5, 1, '', 43728307660783616, '2022-07-24 16:49:11', NULL, NULL);\nINSERT INTO `sys_dictionary` VALUES (43740632493064192, '菜单显示状态', 'systemMenuShowStatus', 6, 1, '', 43728307660783616, '2022-07-24 16:49:39', NULL, NULL);\nINSERT INTO `sys_dictionary` VALUES (43741186304770048, '数据字典样式类型', 'dictionaryClassType', 7, 1, '', 43728307660783616, '2022-07-24 16:51:51', NULL, NULL);\nINSERT INTO `sys_dictionary` VALUES (43741186479310919, 'API 请求方式', 'apiRequestMethod', 8, 1, '', 43728307660783616, '2022-07-24 18:07:28', NULL, NULL);\nINSERT INTO `sys_dictionary` VALUES (43747174940409856, '登录日志状态', 'logLoginStatus', 9, 1, '', 43728307660783616, '2022-07-24 17:15:39', NULL, NULL);\nINSERT INTO `sys_dictionary` VALUES (43749175543726080, '操作日志业务类型', 'logOperationBusinessType', 10, 1, '', 43728307660783616, '2022-07-24 17:23:36', NULL, NULL);\nINSERT INTO `sys_dictionary` VALUES (43749667023880192, '操作日志操作类型', 'logOperationType', 11, 1, '', 43728307660783616, '2022-07-24 17:25:33', NULL, NULL);\nINSERT INTO `sys_dictionary` VALUES (43831966709055488, '操作日志状态', 'logOperationStatus', 12, 1, '', 43728307660783616, '2022-07-24 22:52:35', NULL, NULL);\nINSERT INTO `sys_dictionary` VALUES (52090055367327744, '系统参数配置类型', 'sysParamType', 13, 1, '', 43728307660783616, '2022-08-16 17:47:17', NULL, NULL);\nINSERT INTO `sys_dictionary` VALUES (78246041425543168, '通用是否', 'yesNo', 1, 1, '', 43728307660783616, '2022-10-27 22:01:50', NULL, NULL);\nINSERT INTO `sys_dictionary` VALUES (102543613031874560, '评论状态', 'navCommentStatus', 14, 1, '', 43728307660783616, '2023-01-02 23:11:43', NULL, NULL);\n\n-- ----------------------------\n-- Table structure for sys_dictionary_data\n-- ----------------------------\nDROP TABLE IF EXISTS `sys_dictionary_data`;\nCREATE TABLE `sys_dictionary_data`  (\n  `id` bigint(20) NOT NULL COMMENT '字典数据 id',\n  `dictionary_label` varchar(50) NULL DEFAULT '' COMMENT '字典标签',\n  `dictionary_value` varchar(100) NULL DEFAULT '' COMMENT '字典值',\n  `fk_dictionary_id` varchar(25) NULL DEFAULT NULL COMMENT '所属字典 id',\n  `class_type` varchar(100) NULL DEFAULT '' COMMENT '样式类型（primary，success等）',\n  `sort` int(11) NULL DEFAULT 0 COMMENT '排序',\n  `status` tinyint(1) NULL DEFAULT 1 COMMENT '状态（1 正常，2 停用）',\n  `remark` varchar(500) NULL DEFAULT '' COMMENT '备注',\n  `fk_create_user_id` bigint(20) NULL DEFAULT NULL COMMENT '创建人用户 id',\n  `gmt_create` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `fk_modify_user_id` bigint(20) NULL DEFAULT NULL COMMENT '修改用户 id',\n  `gmt_modify` datetime NULL DEFAULT NULL COMMENT '修改时间',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB COMMENT = '系统字典数据' ROW_FORMAT = DYNAMIC;\n\n-- ----------------------------\n-- Records of sys_dictionary_data\n-- ----------------------------\nINSERT INTO `sys_dictionary_data` VALUES (43739723671273472, '正常', '1', '43739630905851904', 'primary', 1, 1, '', 43728307660783616, '2022-07-24 16:46:03', NULL, NULL);\nINSERT INTO `sys_dictionary_data` VALUES (43739751852802048, '停用', '2', '43739630905851904', 'danger', 2, 1, '', 43728307660783616, '2022-07-24 16:46:09', NULL, NULL);\nINSERT INTO `sys_dictionary_data` VALUES (43744355986440192, '后台用户', '1', '43739903539806208', 'primary', 1, 1, '', 43728307660783616, '2022-07-24 17:04:27', NULL, NULL);\nINSERT INTO `sys_dictionary_data` VALUES (43744417940504576, '博客用户', '2', '43739903539806208', 'warning', 2, 1, '', 43728307660783616, '2022-07-24 17:04:42', NULL, NULL);\nINSERT INTO `sys_dictionary_data` VALUES (43744494792736768, '保密', '1', '43740085404827648', 'primary', 1, 1, '', 43728307660783616, '2022-07-24 17:05:00', NULL, NULL);\nINSERT INTO `sys_dictionary_data` VALUES (43744558772649984, '男', '2', '43740085404827648', 'success', 2, 1, '', 43728307660783616, '2022-07-24 17:05:16', NULL, NULL);\nINSERT INTO `sys_dictionary_data` VALUES (43744612069670912, '女', '3', '43740085404827648', 'warning', 3, 1, '', 43728307660783616, '2022-07-24 17:05:28', NULL, NULL);\nINSERT INTO `sys_dictionary_data` VALUES (43745015746265088, '缓存', '1', '43740399080046592', 'primary', 1, 1, '', 43728307660783616, '2022-07-24 17:07:04', NULL, NULL);\nINSERT INTO `sys_dictionary_data` VALUES (43745054946230272, '不缓存', '2', '43740399080046592', 'success', 2, 1, '', 43728307660783616, '2022-07-24 17:07:14', NULL, NULL);\nINSERT INTO `sys_dictionary_data` VALUES (43745129718087680, '是', '1', '43740514784116736', 'primary', 1, 1, '', 43728307660783616, '2022-07-24 17:07:32', NULL, NULL);\nINSERT INTO `sys_dictionary_data` VALUES (43745161041149952, '否', '2', '43740514784116736', 'success', 2, 1, '', 43728307660783616, '2022-07-24 17:07:39', NULL, NULL);\nINSERT INTO `sys_dictionary_data` VALUES (43745249540964352, '显示', '1', '43740632493064192', 'primary', 1, 1, '', 43728307660783616, '2022-07-24 17:08:00', NULL, NULL);\nINSERT INTO `sys_dictionary_data` VALUES (43745280234881024, '隐藏', '2', '43740632493064192', 'success', 2, 1, '', 43728307660783616, '2022-07-24 17:08:08', NULL, NULL);\nINSERT INTO `sys_dictionary_data` VALUES (43745438393696256, '默认(default)', 'default', '43741186304770048', 'default', 1, 1, '', 43728307660783616, '2022-07-24 17:08:45', NULL, NULL);\nINSERT INTO `sys_dictionary_data` VALUES (43745535164678144, '主要(primary)', 'primary', '43741186304770048', 'primary', 2, 1, '', 43728307660783616, '2022-07-24 17:09:08', NULL, NULL);\nINSERT INTO `sys_dictionary_data` VALUES (43745625509986304, '成功(success)', 'success', '43741186304770048', 'success', 3, 1, '', 43728307660783616, '2022-07-24 17:09:30', NULL, NULL);\nINSERT INTO `sys_dictionary_data` VALUES (43745706313252864, '信息(info)', 'info', '43741186304770048', 'info', 4, 1, '', 43728307660783616, '2022-07-24 17:09:49', NULL, NULL);\nINSERT INTO `sys_dictionary_data` VALUES (43745788475473920, '警告(warning)', 'warning', '43741186304770048', 'warning', 5, 1, '', 43728307660783616, '2022-07-24 17:10:09', NULL, NULL);\nINSERT INTO `sys_dictionary_data` VALUES (43745861963874304, '危险(danger)', 'danger', '43741186304770048', 'danger', 6, 1, '', 43728307660783616, '2022-07-24 17:10:26', NULL, NULL);\nINSERT INTO `sys_dictionary_data` VALUES (43747219504889856, '成功', '1', '43747174940409856', 'success', 1, 1, '', 43728307660783616, '2022-07-24 17:15:50', NULL, NULL);\nINSERT INTO `sys_dictionary_data` VALUES (43747249070538752, '失败', '2', '43747174940409856', 'danger', 2, 1, '', 43728307660783616, '2022-07-24 17:15:57', NULL, NULL);\nINSERT INTO `sys_dictionary_data` VALUES (43749310080221184, '其他', '1', '43749175543726080', 'primary', 1, 1, '', 43728307660783616, '2022-07-24 17:24:08', NULL, NULL);\nINSERT INTO `sys_dictionary_data` VALUES (43749355009605632, '新增', '2', '43749175543726080', 'info', 2, 1, '', 43728307660783616, '2022-07-24 17:24:19', NULL, NULL);\nINSERT INTO `sys_dictionary_data` VALUES (43749395618856960, '修改', '3', '43749175543726080', 'warning', 3, 1, '', 43728307660783616, '2022-07-24 17:24:29', NULL, NULL);\nINSERT INTO `sys_dictionary_data` VALUES (43749433191432192, '删除', '4', '43749175543726080', 'danger', 4, 1, '', 43728307660783616, '2022-07-24 17:24:38', NULL, NULL);\nINSERT INTO `sys_dictionary_data` VALUES (43749748162691072, '其他', '1', '43749667023880192', 'default', 1, 1, '', 43728307660783616, '2022-07-24 17:25:53', NULL, NULL);\nINSERT INTO `sys_dictionary_data` VALUES (43749792488095744, '后台用户', '2', '43749667023880192', 'primary', 2, 1, '', 43728307660783616, '2022-07-24 17:26:03', NULL, NULL);\nINSERT INTO `sys_dictionary_data` VALUES (43749847500587008, '手机端用户', '3', '43749667023880192', 'info', 3, 1, '', 43728307660783616, '2022-07-24 17:26:16', NULL, NULL);\nINSERT INTO `sys_dictionary_data` VALUES (43749905755275264, '博客用户', '4', '43749667023880192', 'warning', 4, 1, '', 43728307660783616, '2022-07-24 17:26:30', NULL, NULL);\nINSERT INTO `sys_dictionary_data` VALUES (43760754016387072, 'GET', 'GET', '43741186479310919', 'primary', 1, 1, '', 43728307660783616, '2022-07-24 18:09:37', NULL, NULL);\nINSERT INTO `sys_dictionary_data` VALUES (43760839513079808, 'POST', 'POST', '43741186479310919', 'info', 2, 1, '', 43728307660783616, '2022-07-24 18:09:57', NULL, NULL);\nINSERT INTO `sys_dictionary_data` VALUES (43760910166130688, 'PUT', 'PUT', '43741186479310919', 'warning', 3, 1, '', 43728307660783616, '2022-07-24 18:10:14', NULL, NULL);\nINSERT INTO `sys_dictionary_data` VALUES (43760951937204224, 'DELETE', 'DELETE', '43741186479310919', 'danger', 4, 1, '', 43728307660783616, '2022-07-24 18:10:24', NULL, NULL);\nINSERT INTO `sys_dictionary_data` VALUES (43832012938674176, '成功', '1', '43831966709055488', 'primary', 1, 1, '', 43728307660783616, '2022-07-24 22:52:46', NULL, NULL);\nINSERT INTO `sys_dictionary_data` VALUES (43832045444530176, '异常', '2', '43831966709055488', 'danger', 2, 1, '', 43728307660783616, '2022-07-24 22:52:54', NULL, NULL);\nINSERT INTO `sys_dictionary_data` VALUES (52090134002139136, '系统内置', '1', '52090055367327744', 'primary', 1, 1, '', 43728307660783616, '2022-08-16 17:47:36', NULL, NULL);\nINSERT INTO `sys_dictionary_data` VALUES (67409162056761344, '导航管理', '2', '52090055367327744', 'info', 2, 1, '', 43728307660783616, '2022-09-28 00:19:57', 43728307660783616, '2022-12-10 23:13:20');\nINSERT INTO `sys_dictionary_data` VALUES (78246089743925248, '是', '1', '78246041425543168', 'primary', 1, 1, '', 43728307660783616, '2022-10-27 22:02:02', NULL, NULL);\nINSERT INTO `sys_dictionary_data` VALUES (78246125886242816, '否', '2', '78246041425543168', 'danger', 2, 1, '', 43728307660783616, '2022-10-27 22:02:10', NULL, NULL);\nINSERT INTO `sys_dictionary_data` VALUES (102543668505739264, '待审核', '1', '102543613031874560', 'primary', 1, 1, '', 43728307660783616, '2023-01-02 23:11:56', NULL, NULL);\nINSERT INTO `sys_dictionary_data` VALUES (102543729629331456, '已通过', '2', '102543613031874560', 'success', 2, 1, '', 43728307660783616, '2023-01-02 23:12:10', NULL, NULL);\nINSERT INTO `sys_dictionary_data` VALUES (102543767784914944, '已驳回', '3', '102543613031874560', 'danger', 3, 1, '', 43728307660783616, '2023-01-02 23:12:19', NULL, NULL);\nINSERT INTO `sys_dictionary_data` VALUES (143468816637100032, '主题配置', '3', '52090055367327744', 'warning', 3, 1, '', 43728307660783616, '2023-04-25 21:33:51', 43728307660783616, '2023-04-25 21:34:08');\n\n-- ----------------------------\n-- Table structure for sys_menu\n-- ----------------------------\nDROP TABLE IF EXISTS `sys_menu`;\nCREATE TABLE `sys_menu`  (\n  `id` bigint(20) NOT NULL COMMENT '菜单 id',\n  `menu_name` varchar(50) NOT NULL COMMENT '菜单名称',\n  `parent_id` bigint(20) NULL DEFAULT 0 COMMENT '父菜单ID',\n  `sort` int(11) NULL DEFAULT 0 COMMENT '排序',\n  `menu_type` char(1) NULL DEFAULT '' COMMENT '菜单类型（D 目录，M 菜单，B 按钮）',\n  `permission_code` varchar(100) NULL DEFAULT '' COMMENT '权限标识',\n  `router_url` varchar(200) NULL DEFAULT '' COMMENT '路由地址',\n  `component_path` varchar(255) NULL DEFAULT '' COMMENT '组件路径',\n  `router_param` varchar(255) NULL DEFAULT '' COMMENT '路由参数',\n  `has_frame` tinyint(1) NULL DEFAULT 2 COMMENT '是否为外链（1是，2否）',\n  `has_cache` tinyint(1) NULL DEFAULT 1 COMMENT '是否缓存（1缓存，2不缓存）',\n  `has_permission` tinyint(1) NULL DEFAULT 1 COMMENT '是否需要权限（1 是，2 否）',\n  `menu_icon` varchar(100) NULL DEFAULT '' COMMENT '菜单图标',\n  `show_status` tinyint(1) NULL DEFAULT 1 COMMENT '显示状态（1显示，2隐藏）',\n  `status` tinyint(1) NULL DEFAULT 1 COMMENT '状态（1 正常，2 停用）',\n  `fk_create_user_id` bigint(20) NULL DEFAULT NULL COMMENT '创建人用户 id',\n  `gmt_create` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `fk_modify_user_id` bigint(20) NULL DEFAULT NULL COMMENT '修改人用户 id',\n  `gmt_modify` datetime NULL DEFAULT NULL COMMENT '修改时间',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB COMMENT = '系统菜单' ROW_FORMAT = DYNAMIC;\n\n-- ----------------------------\n-- Records of sys_menu\n-- ----------------------------\nINSERT INTO `sys_menu` VALUES (43737833021636608, '系统管理', 0, 1, 'D', NULL, 'system', NULL, NULL, 2, 1, 1, 'system', 1, 1, 43728307660783616, '2022-07-24 16:38:32', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (43738261293629440, '用户管理', 43737833021636608, 1, 'M', 'system:user:page', 'user', 'system/user/index', NULL, 2, 1, 1, 'user', 1, 1, 43728307660783616, '2022-07-24 16:40:14', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (43738420110950400, '角色管理', 43737833021636608, 2, 'M', 'system:role:page', 'role', 'system/role/index', NULL, 2, 1, 1, 'peoples', 1, 1, 43728307660783616, '2022-07-24 16:40:52', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (43738641683447808, '菜单管理', 43737833021636608, 3, 'M', 'system:menu:list', 'menu', 'system/menu/index', NULL, 2, 1, 1, 'menu', 1, 1, 43728307660783616, '2022-07-24 16:41:45', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (43738896395141120, '数据字典', 43737833021636608, 4, 'M', 'system:dictionary:page', 'dictionary', 'system/dictionary/index', NULL, 2, 1, 1, 'dict', 1, 1, 43728307660783616, '2022-07-24 16:42:46', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (43739074015526912, 'API 管理', 43737833021636608, 5, 'M', 'system:apiCatetory:page', 'api', 'system/api/category', NULL, 2, 1, 1, 'documentation', 1, 1, 43728307660783616, '2022-07-24 16:43:28', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (43746238872420352, '日志审计', 0, 3, 'D', '', 'log', '', '', 2, 1, 1, 'edit', 1, 1, 43728307660783616, '2022-07-24 17:11:56', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (43746395802304512, '登录日志', 43746238872420352, 1, 'M', 'log:login:page', 'login', 'system/log/login/index', '', 2, 1, 1, 'logininfor', 1, 1, 43728307660783616, '2022-07-24 17:12:34', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (43746859121901568, '操作日志', 43746238872420352, 2, 'M', 'log:operation:page', 'operation', 'system/log/operation/index', '', 2, 1, 1, 'form', 1, 1, 43728307660783616, '2022-07-24 17:14:24', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (43779604086784000, '新增', 43738261293629440, 1, 'B', 'system:user:add', '', 'system/log/operation/index', '', 2, 1, 1, '', 1, 1, 43728307660783616, '2022-07-24 19:24:31', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (43779675532558336, '修改', 43738261293629440, 2, 'B', 'system:user:update', '', 'system/log/operation/index', '', 2, 1, 1, '', 1, 1, 43728307660783616, '2022-07-24 19:24:48', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (43779734387032064, '删除', 43738261293629440, 3, 'B', 'system:user:delete', '', 'system/log/operation/index', '', 2, 1, 1, '', 1, 1, 43728307660783616, '2022-07-24 19:25:02', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (43779823738290176, '重置密码', 43738261293629440, 4, 'B', 'system:user:resetPassword', '', 'system/log/operation/index', '', 2, 1, 1, '', 1, 1, 43728307660783616, '2022-07-24 19:25:23', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (43779962246791168, '新增', 43738420110950400, 1, 'B', 'system:role:add', '', '', '', 2, 1, 1, '', 1, 1, 43728307660783616, '2022-07-24 19:25:56', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (43780015896133632, '修改', 43738420110950400, 2, 'B', 'system:role:update', '', '', '', 2, 1, 1, '', 1, 1, 43728307660783616, '2022-07-24 19:26:09', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (43780063644090368, '删除', 43738420110950400, 3, 'B', 'system:role:delete', '', '', '', 2, 1, 1, '', 1, 1, 43728307660783616, '2022-07-24 19:26:21', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (43780156136882176, '分配用户', 43738420110950400, 4, 'B', 'system:role:authUser', '', '', '', 2, 1, 1, '', 1, 1, 43728307660783616, '2022-07-24 19:26:43', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (43780326794723328, '取消用户授权', 43738420110950400, 5, 'B', 'system:user:deleteAuthUser', '', '', '', 2, 1, 1, '', 1, 1, 43728307660783616, '2022-07-24 19:27:23', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (43780526653308928, '新增', 43738641683447808, 1, 'B', 'system:menu:add', '', '', '', 2, 1, 1, '', 1, 1, 43728307660783616, '2022-07-24 19:28:11', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (43780571997929472, '修改', 43738641683447808, 2, 'B', 'system:menu:update', '', '', '', 2, 1, 1, '', 1, 1, 43728307660783616, '2022-07-24 19:28:22', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (43780625399808000, '删除', 43738641683447808, 3, 'B', 'system:menu:delete', '', '', '', 2, 1, 1, '', 1, 1, 43728307660783616, '2022-07-24 19:28:35', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (43780699760623616, '分配 API', 43738641683447808, 4, 'B', 'system:menu:allocateApi', '', '', '', 2, 1, 1, '', 1, 1, 43728307660783616, '2022-07-24 19:28:52', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (43780957378969600, '新增', 43738896395141120, 1, 'B', 'system:dictionary:add', '', '', '', 2, 1, 1, '', 1, 1, 43728307660783616, '2022-07-24 19:29:54', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (43781009912627200, '修改', 43738896395141120, 2, 'B', 'system:dictionary:update', '', '', '', 2, 1, 1, '', 1, 1, 43728307660783616, '2022-07-24 19:30:06', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (43781060101668864, '删除', 43738896395141120, 3, 'B', 'system:dictionary:delete', '', '', '', 2, 1, 1, '', 1, 1, 43728307660783616, '2022-07-24 19:30:18', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (43781692732735488, '分配数据', 43738896395141120, 4, 'B', 'system:dictionary:data:page', '', '', '', 2, 1, 1, '', 1, 1, 43728307660783616, '2022-07-24 19:32:49', 43728307660783616, '2022-09-12 16:32:11');\nINSERT INTO `sys_menu` VALUES (43781756515516416, '数据新增', 43738896395141120, 5, 'B', 'system:dictionaryData:add', '', '', '', 2, 1, 1, '', 1, 1, 43728307660783616, '2022-07-24 19:33:04', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (43781829500600320, '数据修改', 43738896395141120, 6, 'B', 'system:dictionary:data:update', '', '', '', 2, 1, 1, '', 1, 1, 43728307660783616, '2022-07-24 19:33:22', 43728307660783616, '2022-09-12 16:32:18');\nINSERT INTO `sys_menu` VALUES (43781877487632384, '数据删除', 43738896395141120, 7, 'B', 'system:dictionary:data:delete', '', '', '', 2, 1, 1, '', 1, 1, 43728307660783616, '2022-07-24 19:33:33', 43728307660783616, '2022-09-12 16:32:23');\nINSERT INTO `sys_menu` VALUES (43820727559782400, '分类新增', 43739074015526912, 1, 'B', 'system:apiCategory:add', '', '', '', 2, 1, 1, '', 1, 1, 43728307660783616, '2022-07-24 22:07:56', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (43820963283861504, '分类修改', 43739074015526912, 2, 'B', 'system:apiCategory:update', '', '', '', 2, 1, 1, '', 1, 1, 43728307660783616, '2022-07-24 22:08:52', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (43821061610930176, '分类删除', 43739074015526912, 3, 'B', 'system:apiCategory:delete', '', '', '', 2, 1, 1, '', 1, 1, 43728307660783616, '2022-07-24 22:09:15', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (43821227462098944, '分配数据', 43739074015526912, 4, 'B', 'system:api:page', '', '', '', 2, 1, 1, '', 1, 1, 43728307660783616, '2022-07-24 22:09:55', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (43821354134274048, '数据新增', 43739074015526912, 5, 'B', 'system:api:add', '', '', '', 2, 1, 1, '', 1, 1, 43728307660783616, '2022-07-24 22:10:25', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (43821418193879040, '数据修改', 43739074015526912, 6, 'B', 'system:api:update', '', '', '', 2, 1, 1, '', 1, 1, 43728307660783616, '2022-07-24 22:10:40', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (43821478692519936, '数据删除', 43739074015526912, 7, 'B', 'system:api:delete', '', '', '', 2, 1, 1, '', 1, 1, 43728307660783616, '2022-07-24 22:10:55', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (52073868344426496, '参数配置', 43737833021636608, 6, 'M', 'system:param:page', 'param', 'system/param/index', '', 2, 1, 1, 'edit', 1, 1, 43728307660783616, '2022-08-16 16:42:58', 43728307660783616, '2022-08-16 16:59:24');\nINSERT INTO `sys_menu` VALUES (52132922030817280, '新增', 52073868344426496, 1, 'B', 'system:param:add', '', '', '', 2, 1, 1, '', 1, 1, 43728307660783616, '2022-08-16 20:37:37', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (52133024904511488, '修改', 52073868344426496, 2, 'B', 'system:param:update', '', '', '', 2, 1, 1, '', 1, 1, 43728307660783616, '2022-08-16 20:38:02', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (52133076263763968, '删除', 52073868344426496, 3, 'B', 'system:param:delete', '', '', '', 2, 1, 1, '', 1, 1, 43728307660783616, '2022-08-16 20:38:14', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (86832919972151303, '分类管理', 86833558454272000, 1, 'M', 'nav:category:page', 'category', 'nav/category/index', '', 2, 1, 1, 'heart', 1, 1, 43728307660783616, '2022-11-20 14:43:41', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (86832919972151304, '新增', 86832919972151303, 1, 'B', 'nav:category:add', '', '', '', 2, 1, 1, '', 1, 1, 43728307660783616, '2022-11-20 14:43:41', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (86832919972151305, '修改', 86832919972151303, 2, 'B', 'nav:category:update', '', '', '', 2, 1, 1, '', 1, 1, 43728307660783616, '2022-11-20 14:43:41', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (86832919972151306, '删除', 86832919972151303, 3, 'B', 'nav:category:delete', '', '', '', 2, 1, 1, '', 1, 1, 43728307660783616, '2022-11-20 14:43:41', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (86833558454272000, '导航管理', 0, 4, 'D', '', 'nav', '', '', 2, 1, 1, 'computer', 1, 1, 43728307660783616, '2022-11-20 14:45:34', 43728307660783616, '2023-05-21 16:34:42');\nINSERT INTO `sys_menu` VALUES (86859268749262855, '网站管理', 86833558454272000, 2, 'M', 'nav:site:page', 'site', 'nav/site/index', '', 2, 1, 1, 'star', 1, 1, 43728307660783616, '2022-11-20 16:27:55', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (86859268749262856, '新增', 86859268749262855, 1, 'B', 'nav:site:add', '', '', '', 2, 1, 1, '', 1, 1, 43728307660783616, '2022-11-20 16:27:55', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (86859268749262857, '修改', 86859268749262855, 2, 'B', 'nav:site:update', '', '', '', 2, 1, 1, '', 1, 1, 43728307660783616, '2022-11-20 16:27:55', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (86859268749262858, '删除', 86859268749262855, 3, 'B', 'nav:site:delete', '', '', '', 2, 1, 1, '', 1, 1, 43728307660783616, '2022-11-20 16:27:55', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (90179495205535744, '评论管理', 86833558454272000, 3, 'M', 'nav:comment:page', 'comment', 'nav/comment/index', '', 2, 1, 1, 'message', 1, 1, 43728307660783616, '2022-11-29 20:21:07', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (92733418365779968, '通过', 90179495205535744, 1, 'B', 'nav:comment:pass', '', '', '', 2, 1, 1, '', 1, 1, 43728307660783616, '2022-12-06 21:29:30', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (92733594673348608, '驳回', 90179495205535744, 2, 'B', 'nav:comment:reject', '', '', '', 2, 1, 1, '', 1, 1, 43728307660783616, '2022-12-06 21:30:12', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (92733726269636608, '置顶', 90179495205535744, 3, 'B', 'nav:comment:sticky', '', '', '', 2, 1, 1, '', 1, 1, 43728307660783616, '2022-12-06 21:30:43', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (92733853243801600, '取消置顶', 90179495205535744, 4, 'B', 'nav:comment:sticky:cancle', '', '', '', 2, 1, 1, '', 1, 1, 43728307660783616, '2022-12-06 21:31:14', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (92733965122666496, '删除', 90179495205535744, 5, 'B', 'nav:comment:delete', '', '', '', 2, 1, 1, '', 1, 1, 43728307660783616, '2022-12-06 21:31:40', NULL, NULL);\nINSERT INTO `sys_menu` VALUES (94456071543848960, '网站配置', 86833558454272000, 6, 'M', 'nav:config:get', 'config', 'nav/config/index', '', 2, 1, 1, 'website', 1, 1, 43728307660783616, '2022-12-11 15:34:42', 43728307660783616, '2022-12-11 15:37:21');\nINSERT INTO `sys_menu` VALUES (94456484632461312, '修改', 94456071543848960, 1, 'B', 'nav:config:update', '', '', '', 2, 1, 1, '', 1, 1, 43728307660783616, '2022-12-11 15:36:21', NULL, NULL);\n\n-- ----------------------------\n-- Table structure for sys_param\n-- ----------------------------\nDROP TABLE IF EXISTS `sys_param`;\nCREATE TABLE `sys_param`  (\n  `id` bigint(20) NOT NULL COMMENT '参数 id',\n  `param_name` varchar(100) NULL DEFAULT '' COMMENT '参数名称',\n  `param_key` varchar(100) NULL DEFAULT '' COMMENT '参数键',\n  `param_value` varchar(200) NULL DEFAULT '' COMMENT '参数值',\n  `param_type` tinyint(1) NULL DEFAULT NULL COMMENT '参数类型（1 系统参数）',\n  `sort` int(11) NULL DEFAULT 0 COMMENT '排序',\n  `remark` varchar(500) NULL DEFAULT '' COMMENT '备注',\n  `fk_create_user_id` bigint(20) NULL DEFAULT NULL COMMENT '创建人用户 id',\n  `gmt_create` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `fk_modify_user_id` bigint(20) NULL DEFAULT NULL COMMENT '修改人用户 id',\n  `gmt_modify` datetime NULL DEFAULT NULL COMMENT '修改时间',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB COMMENT = '参数配置' ROW_FORMAT = DYNAMIC;\n\n-- ----------------------------\n-- Records of sys_param\n-- ----------------------------\nINSERT INTO `sys_param` VALUES (52091717515476992, '默认密码', 'SYS_DEFAULT_PASSWORD', 'gesdh@2022', 1, 1, '默认密码', 43728307660783616, '2022-08-16 17:53:53', 43728307660783616, '2022-12-10 23:22:55');\nINSERT INTO `sys_param` VALUES (70810422013329408, '评论开启', 'NAV_COMMENT_OPEN', 'true', 2, 2, '评论是否开启', 43728307660783616, '2022-10-07 09:35:20', 43728307660783616, '2023-04-15 00:55:55');\nINSERT INTO `sys_param` VALUES (140123273550626816, '评论无登录开启', 'NAV_COMMENT_NOT_LOGIN_OPEN', 'true', 1, 6, '是否开启不用登录就能提交评论', 43728307660783616, '2023-04-16 15:59:52', 43728307660783616, '2023-04-16 16:23:53');\n\n-- ----------------------------\n-- Table structure for sys_role\n-- ----------------------------\nDROP TABLE IF EXISTS `sys_role`;\nCREATE TABLE `sys_role`  (\n  `id` bigint(20) NOT NULL COMMENT '角色 id',\n  `role_name` varchar(30) NOT NULL COMMENT '角色名称',\n  `role_code` varchar(100) NOT NULL COMMENT '角色编码',\n  `sort` int(11) NULL DEFAULT 0 COMMENT '排序',\n  `status` tinyint(1) NULL DEFAULT 1 COMMENT '状态（1 正常，2 停用）',\n  `remark` varchar(500) NULL DEFAULT '' COMMENT '备注',\n  `fk_create_user_id` bigint(20) NULL DEFAULT NULL COMMENT '创建人用户 id',\n  `gmt_create` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `fk_modify_user_id` bigint(20) NULL DEFAULT NULL COMMENT '修改人用户 id',\n  `gmt_modify` datetime NULL DEFAULT NULL COMMENT '修改时间',\n  PRIMARY KEY (`id`) USING BTREE,\n  UNIQUE INDEX `unique_role_code`(`role_code` ASC) USING BTREE COMMENT '角色编码唯一索引'\n) ENGINE = InnoDB COMMENT = '系统角色' ROW_FORMAT = DYNAMIC;\n\n-- ----------------------------\n-- Records of sys_role\n-- ----------------------------\nINSERT INTO `sys_role` VALUES (43735562707795968, '超级管理员', 'superAdmin', 1, 1, '', 43728307660783616, '2022-07-24 16:29:31', NULL, NULL);\nINSERT INTO `sys_role` VALUES (43767332559912960, '普通角色', 'commonRole', 2, 1, '', 43728307660783616, '2022-07-24 18:35:45', 43728307660783616, '2022-10-31 00:02:36');\n\n-- ----------------------------\n-- Table structure for sys_role_menu\n-- ----------------------------\nDROP TABLE IF EXISTS `sys_role_menu`;\nCREATE TABLE `sys_role_menu`  (\n  `id` bigint(20) NOT NULL COMMENT '角色菜单 id',\n  `fk_role_id` bigint(20) NOT NULL COMMENT '角色 id',\n  `fk_menu_id` bigint(20) NOT NULL COMMENT '菜单 id',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB COMMENT = '系统角色菜单' ROW_FORMAT = DYNAMIC;\n\n-- ----------------------------\n-- Records of sys_role_menu\n-- ----------------------------\nINSERT INTO `sys_role_menu` VALUES (86492067173236736, 43767332559912960, 43737833021636608);\nINSERT INTO `sys_role_menu` VALUES (86492067177431040, 43767332559912960, 43738261293629440);\nINSERT INTO `sys_role_menu` VALUES (86492067177431041, 43767332559912960, 43779604086784000);\nINSERT INTO `sys_role_menu` VALUES (86492067177431042, 43767332559912960, 43779675532558336);\nINSERT INTO `sys_role_menu` VALUES (86492067181625344, 43767332559912960, 43779734387032064);\nINSERT INTO `sys_role_menu` VALUES (86492067181625345, 43767332559912960, 43779823738290176);\nINSERT INTO `sys_role_menu` VALUES (86492067185819648, 43767332559912960, 43738420110950400);\nINSERT INTO `sys_role_menu` VALUES (86492067185819649, 43767332559912960, 43779962246791168);\nINSERT INTO `sys_role_menu` VALUES (86492067185819650, 43767332559912960, 43780015896133632);\nINSERT INTO `sys_role_menu` VALUES (86492067185819651, 43767332559912960, 43780063644090368);\nINSERT INTO `sys_role_menu` VALUES (86492067190013952, 43767332559912960, 43780156136882176);\nINSERT INTO `sys_role_menu` VALUES (86492067190013953, 43767332559912960, 43780326794723328);\nINSERT INTO `sys_role_menu` VALUES (86492067190013954, 43767332559912960, 43738641683447808);\nINSERT INTO `sys_role_menu` VALUES (86492067190013955, 43767332559912960, 43780526653308928);\nINSERT INTO `sys_role_menu` VALUES (86492067190013956, 43767332559912960, 43780571997929472);\nINSERT INTO `sys_role_menu` VALUES (86492067194208256, 43767332559912960, 43780625399808000);\nINSERT INTO `sys_role_menu` VALUES (86492067194208257, 43767332559912960, 43780699760623616);\nINSERT INTO `sys_role_menu` VALUES (86492067194208258, 43767332559912960, 43738896395141120);\nINSERT INTO `sys_role_menu` VALUES (86492067194208259, 43767332559912960, 43780957378969600);\nINSERT INTO `sys_role_menu` VALUES (86492067198402560, 43767332559912960, 43781009912627200);\nINSERT INTO `sys_role_menu` VALUES (86492067198402561, 43767332559912960, 43781060101668864);\nINSERT INTO `sys_role_menu` VALUES (86492067198402562, 43767332559912960, 43781692732735488);\nINSERT INTO `sys_role_menu` VALUES (86492067198402563, 43767332559912960, 43781756515516416);\nINSERT INTO `sys_role_menu` VALUES (86492067198402564, 43767332559912960, 43781829500600320);\nINSERT INTO `sys_role_menu` VALUES (86492067202596864, 43767332559912960, 43781877487632384);\nINSERT INTO `sys_role_menu` VALUES (86492067202596865, 43767332559912960, 43739074015526912);\nINSERT INTO `sys_role_menu` VALUES (86492067202596866, 43767332559912960, 43820727559782400);\nINSERT INTO `sys_role_menu` VALUES (86492067202596867, 43767332559912960, 43820963283861504);\nINSERT INTO `sys_role_menu` VALUES (86492067206791168, 43767332559912960, 43821061610930176);\nINSERT INTO `sys_role_menu` VALUES (86492067206791169, 43767332559912960, 43821227462098944);\nINSERT INTO `sys_role_menu` VALUES (86492067206791170, 43767332559912960, 43821354134274048);\nINSERT INTO `sys_role_menu` VALUES (86492067206791171, 43767332559912960, 43821418193879040);\nINSERT INTO `sys_role_menu` VALUES (86492067206791172, 43767332559912960, 43821478692519936);\nINSERT INTO `sys_role_menu` VALUES (86492067210985472, 43767332559912960, 52073868344426496);\nINSERT INTO `sys_role_menu` VALUES (86492067210985473, 43767332559912960, 52132922030817280);\nINSERT INTO `sys_role_menu` VALUES (86492067210985474, 43767332559912960, 52133024904511488);\nINSERT INTO `sys_role_menu` VALUES (86492067210985475, 43767332559912960, 52133076263763968);\nINSERT INTO `sys_role_menu` VALUES (86492067215179776, 43767332559912960, 86489144947113984);\nINSERT INTO `sys_role_menu` VALUES (86492067215179777, 43767332559912960, 86489393493180416);\nINSERT INTO `sys_role_menu` VALUES (86492067215179778, 43767332559912960, 86490122438049792);\nINSERT INTO `sys_role_menu` VALUES (86492067215179779, 43767332559912960, 86490199768432640);\nINSERT INTO `sys_role_menu` VALUES (86492067215179780, 43767332559912960, 86490267254784000);\nINSERT INTO `sys_role_menu` VALUES (86492067219374080, 43767332559912960, 86490311785709568);\nINSERT INTO `sys_role_menu` VALUES (86492067219374081, 43767332559912960, 86490372783472640);\nINSERT INTO `sys_role_menu` VALUES (86492067219374082, 43767332559912960, 86490430765531136);\nINSERT INTO `sys_role_menu` VALUES (86492067223568384, 43767332559912960, 43746238872420352);\nINSERT INTO `sys_role_menu` VALUES (86492067223568385, 43767332559912960, 43746395802304512);\nINSERT INTO `sys_role_menu` VALUES (86492067223568386, 43767332559912960, 43746859121901568);\nINSERT INTO `sys_role_menu` VALUES (86492067223568387, 43767332559912960, 52099144042414080);\nINSERT INTO `sys_role_menu` VALUES (86492067223568388, 43767332559912960, 52099447332536320);\nINSERT INTO `sys_role_menu` VALUES (86492067227762688, 43767332559912960, 52099788358811648);\nINSERT INTO `sys_role_menu` VALUES (86492067227762689, 43767332559912960, 52099936145113088);\nINSERT INTO `sys_role_menu` VALUES (86492067227762690, 43767332559912960, 52100202487611392);\nINSERT INTO `sys_role_menu` VALUES (86492067227762691, 43767332559912960, 52100286625349632);\nINSERT INTO `sys_role_menu` VALUES (86492067231956992, 43767332559912960, 52114381915291648);\nINSERT INTO `sys_role_menu` VALUES (86492067231956993, 43767332559912960, 52114517684912128);\nINSERT INTO `sys_role_menu` VALUES (86492067231956994, 43767332559912960, 52114568226275328);\nINSERT INTO `sys_role_menu` VALUES (86492067231956995, 43767332559912960, 52114614602694656);\nINSERT INTO `sys_role_menu` VALUES (86492067236151296, 43767332559912960, 52114878701240320);\nINSERT INTO `sys_role_menu` VALUES (86492067236151297, 43767332559912960, 52114929196466176);\nINSERT INTO `sys_role_menu` VALUES (86492067236151298, 43767332559912960, 52114968178327552);\nINSERT INTO `sys_role_menu` VALUES (86492067236151299, 43767332559912960, 52115022117076992);\nINSERT INTO `sys_role_menu` VALUES (86492067236151300, 43767332559912960, 55073494739714048);\nINSERT INTO `sys_role_menu` VALUES (86492067240345600, 43767332559912960, 61851202979102720);\nINSERT INTO `sys_role_menu` VALUES (86492067240345601, 43767332559912960, 61851247690383360);\nINSERT INTO `sys_role_menu` VALUES (86492067240345602, 43767332559912960, 61851299972382720);\nINSERT INTO `sys_role_menu` VALUES (86492067240345603, 43767332559912960, 61851660971933696);\nINSERT INTO `sys_role_menu` VALUES (86492067244539904, 43767332559912960, 61851798276669440);\nINSERT INTO `sys_role_menu` VALUES (86492067244539905, 43767332559912960, 61851972713578496);\nINSERT INTO `sys_role_menu` VALUES (86492067244539906, 43767332559912960, 70575761538416640);\nINSERT INTO `sys_role_menu` VALUES (86492067244539907, 43767332559912960, 70591784899575808);\nINSERT INTO `sys_role_menu` VALUES (86492067244539908, 43767332559912960, 70591842319597568);\nINSERT INTO `sys_role_menu` VALUES (86492067248734208, 43767332559912960, 70591937538686976);\nINSERT INTO `sys_role_menu` VALUES (86492067248734209, 43767332559912960, 77878458918633472);\nINSERT INTO `sys_role_menu` VALUES (86492067248734210, 43767332559912960, 78232604175761408);\nINSERT INTO `sys_role_menu` VALUES (86492067248734211, 43767332559912960, 78232661448982528);\nINSERT INTO `sys_role_menu` VALUES (86492067252928512, 43767332559912960, 78243661845889024);\nINSERT INTO `sys_role_menu` VALUES (86492067252928513, 43767332559912960, 78261905201823744);\nINSERT INTO `sys_role_menu` VALUES (86492067252928514, 43767332559912960, 78261973308932096);\nINSERT INTO `sys_role_menu` VALUES (86492067252928515, 43767332559912960, 78262028635996160);\nINSERT INTO `sys_role_menu` VALUES (86492067257122816, 43767332559912960, 78582377730801664);\nINSERT INTO `sys_role_menu` VALUES (86492067257122817, 43767332559912960, 78594439798325248);\nINSERT INTO `sys_role_menu` VALUES (86492067257122818, 43767332559912960, 78595081065463808);\nINSERT INTO `sys_role_menu` VALUES (104307851295457280, 104307851257708544, 104307285068611584);\n\n-- ----------------------------\n-- Table structure for sys_user\n-- ----------------------------\nDROP TABLE IF EXISTS `sys_user`;\nCREATE TABLE `sys_user`  (\n  `id` bigint(20) NOT NULL COMMENT '用户 id',\n  `username` varchar(40) NOT NULL DEFAULT '' COMMENT '用户名',\n  `nick_name` varchar(40) NULL DEFAULT '' COMMENT '昵称',\n  `password` varchar(100) NULL DEFAULT '' COMMENT '密码',\n  `sex` tinyint(1) NULL DEFAULT 1 COMMENT '用户性别（1 保密，2 男，3 女）',\n  `user_type` tinyint(1) NULL DEFAULT 1 COMMENT '用户类型（1 后台用户，2 博客用户）',\n  `email` varchar(50) NULL DEFAULT '' COMMENT '邮箱',\n  `mobile_phone` varchar(11) NULL DEFAULT '' COMMENT '手机号码',\n  `avatar` varchar(100) NULL DEFAULT '' COMMENT '头像地址',\n  `status` tinyint(1) NULL DEFAULT 1 COMMENT '状态（1 正常，2 停用）',\n  `remark` varchar(500) NULL DEFAULT '' COMMENT '备注',\n  `del_flag` tinyint(1) NULL DEFAULT 1 COMMENT '删除标志（1 未删除，2 已删除）',\n  `fk_create_user_id` bigint(20) NULL DEFAULT NULL COMMENT '创建人用户 id',\n  `gmt_create` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',\n  `fk_modify_user_id` bigint(20) NULL DEFAULT NULL COMMENT '修改人用户 id',\n  `gmt_modify` datetime NULL DEFAULT NULL COMMENT '修改时间',\n  PRIMARY KEY (`id`) USING BTREE,\n  UNIQUE INDEX `unique_username`(`username` ASC) USING BTREE COMMENT '用户名唯一索引'\n) ENGINE = InnoDB COMMENT = '系统用户' ROW_FORMAT = DYNAMIC;\n\n-- ----------------------------\n-- Records of sys_user\n-- ----------------------------\nINSERT INTO `sys_user` VALUES (43728307660783616, 'admin', '管理员', '$2a$10$uYSATn7m4cmLFtr4uz7rs.W8a0s3EbwZJCyGnthUrZacMgKh4GoIG', 1, 1, '497301391@qq.com', '', '', 1, '我是管理员', 1, 43728307660783616, '2022-07-24 16:02:02', 43728307660783616, '2023-01-04 22:44:48');\nINSERT INTO `sys_user` VALUES (43767104519798784, 'geshanzsq', '格姗知识圈', '$2a$10$rm5jwWc6lRM11Q.OOEH2T.t41GnkzITcvVAI19LdAicn5/AJdD0im', 3, 1, '497301391@qq.com', '', '', 1, '', 1, 43728307660783616, '2022-07-24 18:34:51', 43728307660783616, '2022-08-15 00:03:28');\nINSERT INTO `sys_user` VALUES (43767196689629184, 'xgz', '小格子', '$2a$10$mh3A6xcNuwM6zfjQgQ/xHOmilj92Vj1JWbiB9VTktUD7zccynLD02', 2, 1, '497301391@qq.com', '', '', 1, '', 1, 43728307660783616, '2022-07-24 18:35:13', NULL, NULL);\n\n-- ----------------------------\n-- Table structure for sys_user_role\n-- ----------------------------\nDROP TABLE IF EXISTS `sys_user_role`;\nCREATE TABLE `sys_user_role`  (\n  `id` bigint(20) NOT NULL COMMENT '用户角色 id',\n  `fk_user_id` bigint(20) NOT NULL COMMENT '用户 id',\n  `fk_role_id` bigint(20) NOT NULL COMMENT '角色 id',\n  PRIMARY KEY (`id`) USING BTREE\n) ENGINE = InnoDB COMMENT = '系统用户角色' ROW_FORMAT = DYNAMIC;\n\n-- ----------------------------\n-- Records of sys_user_role\n-- ----------------------------\nINSERT INTO `sys_user_role` VALUES (43737619212795904, 43728307660783616, 43735562707795968);\nINSERT INTO `sys_user_role` VALUES (43767363438379008, 43767104519798784, 43767332559912960);\nINSERT INTO `sys_user_role` VALUES (43767363438379009, 43767196689629184, 43767332559912960);\n\nSET FOREIGN_KEY_CHECKS = 1;\n"
  },
  {
    "path": "vue-geshanzsq-nav/.browserslistrc",
    "content": "> 1%\nlast 2 versions\nnot dead\nnot ie 11\n"
  },
  {
    "path": "vue-geshanzsq-nav/.editorconfig",
    "content": "[*.{js,jsx,ts,tsx,vue}]\nindent_style = space\nindent_size = 2\ntrim_trailing_whitespace = true\ninsert_final_newline = true\n"
  },
  {
    "path": "vue-geshanzsq-nav/.eslintrc.js",
    "content": "module.exports = {\n  root: true,\n  env: {\n    node: true\n  },\n  extends: ['plugin:vue/vue3-essential', '@vue/standard'],\n  parserOptions: {\n    parser: '@babel/eslint-parser'\n  },\n  rules: {\n    'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',\n    'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',\n    'space-before-function-paren': 'off',\n    'vue/multi-word-component-names': 'off'\n  },\n  globals: {\n    defineProps: 'readonly',\n    defineEmits: 'readonly',\n    defineExpose: 'readonly',\n    withDefaults: 'readonly'\n  }\n}\n"
  },
  {
    "path": "vue-geshanzsq-nav/.gitignore",
    "content": ".DS_Store\nnode_modules\n/dist\n\n\n# local env files\n.env.local\n.env.*.local\n\n# Log files\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\npnpm-debug.log*\n\n# Editor directories and files\n.idea\n.vscode\n*.suo\n*.ntvs*\n*.njsproj\n*.sln\n*.sw?\n\npackage-lock.json\nyarn.lock"
  },
  {
    "path": "vue-geshanzsq-nav/.prettierrc",
    "content": "{\n  \"semi\": false,\n  \"singleQuote\": true,\n  \"trailingComma\": \"none\",\n  \"no-unused-vars\": \"off\"\n}\n"
  },
  {
    "path": "vue-geshanzsq-nav/README.md",
    "content": "## 安装依赖\n\n```bash\nnpm install\n```\n\n## 启动服务\n\n```bash\nnpm run serve\n```\n\n浏览器访问 [http://localhost:8823](http://localhost:8823)\n\n## 发布构建\n\n```bash\nnpm run build\n```\n"
  },
  {
    "path": "vue-geshanzsq-nav/babel.config.js",
    "content": "module.exports = {\n  presets: [\n    '@vue/cli-plugin-babel/preset'\n  ]\n}\n"
  },
  {
    "path": "vue-geshanzsq-nav/jsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"es5\",\n    \"module\": \"esnext\",\n    \"baseUrl\": \"./\",\n    \"moduleResolution\": \"node\",\n    \"paths\": {\n      \"@/*\": [\n        \"src/*\"\n      ]\n    },\n    \"lib\": [\n      \"esnext\",\n      \"dom\",\n      \"dom.iterable\",\n      \"scripthost\"\n    ]\n  }\n}\n"
  },
  {
    "path": "vue-geshanzsq-nav/lint-staged.config.js",
    "content": "module.exports = {\n  '*.{js,jsx,vue}': 'vue-cli-service lint'\n}\n"
  },
  {
    "path": "vue-geshanzsq-nav/package.json",
    "content": "{\n  \"name\": \"vue-geshanzsq-nav\",\n  \"version\": \"2.0.0\",\n  \"description\": \"格姗导航\",\n  \"author\": \"geshanzsq\",\n  \"private\": true,\n  \"scripts\": {\n    \"serve\": \"vue-cli-service serve\",\n    \"build\": \"vue-cli-service build\",\n    \"lint\": \"vue-cli-service lint\"\n  },\n  \"dependencies\": {\n    \"@highlightjs/vue-plugin\": \"^2.1.0\",\n    \"@vueuse/core\": \"^8.6.0\",\n    \"@wangeditor/editor\": \"^5.1.15\",\n    \"@wangeditor/editor-for-vue\": \"^5.1.12\",\n    \"axios\": \"^0.27.2\",\n    \"core-js\": \"^3.8.3\",\n    \"element-plus\": \"^2.3.7\",\n    \"file-saver\": \"^2.0.5\",\n    \"fuse.js\": \"^6.6.2\",\n    \"highlight.js\": \"^11.6.0\",\n    \"js-cookie\": \"^3.0.1\",\n    \"jsonp\": \"^0.2.1\",\n    \"nprogress\": \"^0.2.0\",\n    \"save\": \"^2.5.0\",\n    \"svg-sprite-loader\": \"^6.0.11\",\n    \"vue\": \"^3.2.37\",\n    \"vue-cropper\": \"^1.0.3\",\n    \"vue-router\": \"^4.0.16\",\n    \"vuex\": \"^4.0.2\"\n  },\n  \"devDependencies\": {\n    \"@babel/core\": \"^7.12.16\",\n    \"@babel/eslint-parser\": \"^7.12.16\",\n    \"@vue/cli-plugin-babel\": \"~5.0.0\",\n    \"@vue/cli-plugin-eslint\": \"~5.0.0\",\n    \"@vue/cli-plugin-router\": \"~5.0.0\",\n    \"@vue/cli-plugin-vuex\": \"~5.0.0\",\n    \"@vue/cli-service\": \"~5.0.0\",\n    \"@vue/eslint-config-standard\": \"^6.1.0\",\n    \"compression-webpack-plugin\": \"^10.0.0\",\n    \"eslint\": \"^7.32.0\",\n    \"eslint-plugin-import\": \"^2.25.3\",\n    \"eslint-plugin-node\": \"^11.1.0\",\n    \"eslint-plugin-promise\": \"^5.1.0\",\n    \"eslint-plugin-vue\": \"^8.0.3\",\n    \"lint-staged\": \"^11.1.2\",\n    \"postcss\": \"^8.4.24\",\n    \"sass\": \"^1.32.7\",\n    \"sass-loader\": \"^12.0.0\",\n    \"vue-cli-plugin-element-plus\": \"~0.0.13\"\n  },\n  \"gitHooks\": {\n    \"pre-commit\": \"lint-staged\"\n  }\n}\n"
  },
  {
    "path": "vue-geshanzsq-nav/public/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"zh-CN\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge,chrome=1\" />\n    <meta\n      name=\"viewport\"\n      content=\"width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no\"\n    />\n    <meta name=\"keywords\" content=\"<%= VUE_APP_SITE_KEYWORDS %>\">\n    <meta name=\"description\" content=\"<%= VUE_APP_SITE_DESCRIPTION %>\">\n    <link rel=\"icon\" href=\"<%= BASE_URL %>favicon.ico\" />\n    <title><%= VUE_APP_SITE_TITLE %></title>\n  </head>\n  <style>\n    html,\n    body,\n    #app {\n      height: 100%;\n      margin: 0px;\n      padding: 0px;\n    }\n    .chromeframe {\n      margin: 0.2em 0;\n      background: #ccc;\n      color: #000;\n      padding: 0.2em 0;\n    }\n\n    #loader-wrapper {\n      position: fixed;\n      top: 0;\n      left: 0;\n      width: 100%;\n      height: 100%;\n      z-index: 999999;\n    }\n\n    #loader {\n      display: block;\n      position: relative;\n      left: 50%;\n      top: 50%;\n      width: 150px;\n      height: 150px;\n      margin: -75px 0 0 -75px;\n      border-radius: 50%;\n      border: 3px solid transparent;\n      border-top-color: #97b0fc;\n      -webkit-animation: spin 2s linear infinite;\n      -ms-animation: spin 2s linear infinite;\n      -moz-animation: spin 2s linear infinite;\n      -o-animation: spin 2s linear infinite;\n      animation: spin 2s linear infinite;\n      z-index: 1001;\n    }\n\n    #loader:before {\n      content: \"\";\n      position: absolute;\n      top: 5px;\n      left: 5px;\n      right: 5px;\n      bottom: 5px;\n      border-radius: 50%;\n      border: 3px solid transparent;\n      border-top-color: #00fcbd;\n      -webkit-animation: spin 3s linear infinite;\n      -moz-animation: spin 3s linear infinite;\n      -o-animation: spin 3s linear infinite;\n      -ms-animation: spin 3s linear infinite;\n      animation: spin 3s linear infinite;\n    }\n\n    #loader:after {\n      content: \"\";\n      position: absolute;\n      top: 15px;\n      left: 15px;\n      right: 15px;\n      bottom: 15px;\n      border-radius: 50%;\n      border: 3px solid transparent;\n      border-top-color: #FF805F;\n      -moz-animation: spin 1.5s linear infinite;\n      -o-animation: spin 1.5s linear infinite;\n      -ms-animation: spin 1.5s linear infinite;\n      -webkit-animation: spin 1.5s linear infinite;\n      animation: spin 1.5s linear infinite;\n    }\n\n\n    @-webkit-keyframes spin {\n      0% {\n        -webkit-transform: rotate(0deg);\n        -ms-transform: rotate(0deg);\n        transform: rotate(0deg);\n      }\n      100% {\n        -webkit-transform: rotate(360deg);\n        -ms-transform: rotate(360deg);\n        transform: rotate(360deg);\n      }\n    }\n\n    @keyframes spin {\n      0% {\n        -webkit-transform: rotate(0deg);\n        -ms-transform: rotate(0deg);\n        transform: rotate(0deg);\n      }\n      100% {\n        -webkit-transform: rotate(360deg);\n        -ms-transform: rotate(360deg);\n        transform: rotate(360deg);\n      }\n    }\n\n\n    #loader-wrapper .loader-section {\n      position: fixed;\n      top: 0;\n      width: 51%;\n      height: 100%;\n      background: #20293a;\n      z-index: 1000;\n      -webkit-transform: translateX(0);\n      -ms-transform: translateX(0);\n      transform: translateX(0);\n    }\n\n    #loader-wrapper .loader-section.section-left {\n      left: 0;\n    }\n\n    #loader-wrapper .loader-section.section-right {\n      right: 0;\n    }\n\n\n    .loaded #loader-wrapper .loader-section.section-left {\n      -webkit-transform: translateX(-100%);\n      -ms-transform: translateX(-100%);\n      transform: translateX(-100%);\n      -webkit-transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);\n      transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);\n    }\n\n    .loaded #loader-wrapper .loader-section.section-right {\n      -webkit-transform: translateX(100%);\n      -ms-transform: translateX(100%);\n      transform: translateX(100%);\n      -webkit-transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);\n      transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);\n    }\n\n    .loaded #loader {\n      opacity: 0;\n      -webkit-transition: all 0.3s ease-out;\n      transition: all 0.3s ease-out;\n    }\n\n    .loaded #loader-wrapper {\n      visibility: hidden;\n      -webkit-transform: translateY(-100%);\n      -ms-transform: translateY(-100%);\n      transform: translateY(-100%);\n      -webkit-transition: all 0.3s 1s ease-out;\n      transition: all 0.3s 1s ease-out;\n    }\n\n    .no-js #loader-wrapper {\n      display: none;\n    }\n\n    .no-js h1 {\n      color: #222222;\n    }\n\n    #loader-wrapper .load_title {\n      font-family: 'Open Sans';\n      color: #FFF;\n      font-size: 19px;\n      width: 100%;\n      text-align: center;\n      z-index: 9999999999999;\n      position: absolute;\n      top: 60%;\n      opacity: 1;\n      line-height: 30px;\n    }\n\n    #loader-wrapper .load_title span {\n      font-weight: normal;\n      font-style: italic;\n      font-size: 13px;\n      color: #FFF;\n      opacity: 0.5;\n    }\n  </style>\n</head>\n<body>\n<div id=\"app\">\n  <div id=\"loader-wrapper\">\n    <div id=\"loader\"></div>\n    <div class=\"loader-section section-left\"></div>\n    <div class=\"loader-section section-right\"></div>\n    <div class=\"load_title\"><%= VUE_APP_SITE_LOADING %></div>\n  </div>\n</div>\n</body>\n</html>\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/App.vue",
    "content": "<template>\n  <router-view />\n</template>\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/api/auth/login.js",
    "content": "import request from '@/utils/request'\n\n// 登录\nexport function login(data) {\n  return request({\n    url: '/login',\n    method: 'post',\n    data\n  })\n}\n\n// 获取验证码\nexport function getCaptchaImage() {\n  return request({\n    url: '/getCaptchaImage',\n    method: 'get'\n  })\n}\n\n// 退出登录\nexport function logout() {\n  return request({\n    url: '/logout',\n    method: 'post'\n  })\n}\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/api/auth/user.js",
    "content": "import request from '@/utils/request'\n\n// 获取用户信息\nexport function getUserInfo() {\n  return request({\n    url: '/user/getUserInfo',\n    method: 'get'\n  })\n}\n\n// 获取路由\nexport const getRouters = () => {\n  return request({\n    url: '/user/getRouters',\n    method: 'get'\n  })\n}\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/api/client/comment.js",
    "content": "import request from '@/utils/request'\n\n/**\n * 获取评论开启状态\n */\nexport function getOpenStatus() {\n  return request({\n    url: '/client/nav/comment/getOpenStatus',\n    method: 'get'\n  })\n}\n\n/**\n * 获取评论树形结构\n */\nexport function getTree() {\n  return request({\n    url: '/client/nav/comment/tree',\n    method: 'get'\n  })\n}\n\n/**\n * 提交评论\n */\nexport function add(data) {\n  return request({\n    url: '/client/nav/comment',\n    method: 'post',\n    data\n  })\n}\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/api/client/nav.js",
    "content": "import request from '@/utils/request'\n\n/**\n * 分类网站列表\n */\nexport function categorySiteList() {\n  return request({\n    url: '/client/nav/category/site/list',\n    method: 'get'\n  })\n}\n\n/**\n * 分类列表\n */\nexport function categoryList() {\n  return request({\n    url: '/client/nav/category/list',\n    method: 'get'\n  })\n}\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/api/client/search.js",
    "content": "import request from '@/utils/request'\n\nexport function siteList(searchContent) {\n  return request({\n    url: `/client/search/site/list?searchContent=${searchContent}`,\n    method: 'get'\n  })\n}\n\nexport function categorySiteList(searchContent) {\n  return request({\n    url: `/client/search/category/site/list?searchContent=${searchContent}`,\n    method: 'get'\n  })\n}\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/api/common/bing.js",
    "content": "import request from '@/utils/request'\n\n// 获取微软Bing图片\nexport function getBingImage() {\n  return request({\n    url: '/bing/getBingImage',\n    method: 'get'\n  })\n}\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/api/nav/category.js",
    "content": "import request from '@/utils/request'\n\nexport function list(params) {\n  return request({\n    url: '/nav/category/list',\n    method: 'get',\n    params\n  })\n}\n\nexport function tree(params) {\n  return request({\n    url: '/nav/category/tree',\n    method: 'get',\n    params\n  })\n}\n\nexport function getById(id) {\n  return request({\n    url: `/nav/category/getById/${id}`,\n    method: 'get'\n  })\n}\n\nexport function add(data) {\n  return request({\n    url: 'nav/category/',\n    method: 'post',\n    data\n  })\n}\n\nexport function update(data) {\n  return request({\n    url: '/nav/category/',\n    method: 'put',\n    data\n  })\n}\n\nexport function remove(ids) {\n  return request({\n    url: `/nav/category/delete/${ids}`,\n    method: 'delete'\n  })\n}\n\nexport function getMaxSortByParentId(parentId) {\n  return request({\n    url: '/nav/category/getMaxSortByParentId',\n    method: 'get',\n    params: {\n      parentId\n    }\n  })\n}\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/api/nav/comment.js",
    "content": "import request from '@/utils/request'\n\nexport function page(params) {\n  return request({\n    url: '/nav/comment/page',\n    method: 'get',\n    params\n  })\n}\n\nexport function pass(ids) {\n  return request({\n    url: `/nav/comment/pass/${ids}`,\n    method: 'put'\n  })\n}\n\nexport function reject(data) {\n  return request({\n    url: '/nav/comment/reject',\n    method: 'put',\n    data\n  })\n}\n\nexport function sticky(id) {\n  return request({\n    url: `/nav/comment/sticky/${id}`,\n    method: 'put'\n  })\n}\n\nexport function cancleSticky(id) {\n  return request({\n    url: `/nav/comment/cancelSticky/${id}`,\n    method: 'put'\n  })\n}\n\nexport function remove(ids) {\n  return request({\n    url: `/nav/comment/delete/${ids}`,\n    method: 'delete'\n  })\n}\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/api/nav/config.js",
    "content": "import request from '@/utils/request'\n\nexport function getConfig() {\n  return request({\n    url: '/nav/config/getConfig',\n    method: 'get'\n  })\n}\n\nexport function update(data) {\n  return request({\n    url: '/nav/config/',\n    method: 'put',\n    data\n  })\n}\n\nexport function getAbout() {\n  return request({\n    url: '/nav/config/about',\n    method: 'get'\n  })\n}\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/api/nav/index.js",
    "content": "import request from '@/utils/request'\n\nexport function getStatistics() {\n  return request({\n    url: '/nav/index/getStatistics',\n    method: 'get'\n  })\n}\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/api/nav/picture.js",
    "content": "import request from '@/utils/request'\n\n/**\n * 上传网站\n */\nexport function uploadSite(data) {\n  return request({\n    url: '/nav/picture/site/upload',\n    method: 'post',\n    data: data\n  })\n}\n\n/**\n * 上传头像\n */\nexport function uploadAvavar(data) {\n  return request({\n    url: '/nav/picture/upload/avatar',\n    method: 'post',\n    data: data\n  })\n}\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/api/nav/site.js",
    "content": "import request from '@/utils/request'\n\nexport function page(params) {\n  return request({\n    url: '/nav/site/page',\n    method: 'get',\n    params\n  })\n}\n\nexport function getById(id) {\n  return request({\n    url: `/nav/site/getById/${id}`,\n    method: 'get'\n  })\n}\n\nexport function add(data) {\n  return request({\n    url: 'nav/site/',\n    method: 'post',\n    data\n  })\n}\n\nexport function update(data) {\n  return request({\n    url: '/nav/site/',\n    method: 'put',\n    data\n  })\n}\n\nexport function remove(ids) {\n  return request({\n    url: `/nav/site/delete/${ids}`,\n    method: 'delete'\n  })\n}\n\nexport function getMaxSortByCategoryId(categoryId) {\n  return request({\n    url: '/nav/site/getMaxSortByCategoryId',\n    method: 'get',\n    params: {\n      categoryId\n    }\n  })\n}\n\n/**\n * 更新点击量\n */\nexport function updateClickCount(id) {\n  return request({\n    url: `/nav/site/updateClickCount/${id}`,\n    method: 'put'\n  })\n}\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/api/system/api/api.js",
    "content": "import request from '@/utils/request'\n\nexport function page(params) {\n  return request({\n    url: '/system/api/page',\n    method: 'get',\n    params\n  })\n}\n\nexport function getById(id) {\n  return request({\n    url: `/system/api/getById/${id}`,\n    method: 'get'\n  })\n}\n\nexport function add(data) {\n  return request({\n    url: '/system/api',\n    method: 'post',\n    data\n  })\n}\n\nexport function update(data) {\n  return request({\n    url: '/system/api',\n    method: 'put',\n    data\n  })\n}\n\nexport function remove(ids) {\n  return request({\n    url: `/system/api/delete/${ids}`,\n    method: 'delete'\n  })\n}\n\nexport function getMaxSortByCategoryId(apiCategoryId) {\n  return request({\n    url: '/system/api/getMaxSortByCategoryId',\n    method: 'get',\n    params: {\n      apiCategoryId\n    }\n  })\n}\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/api/system/api/category.js",
    "content": "import request from '@/utils/request'\n\nexport function page(params) {\n  return request({\n    url: '/system/api/category/page',\n    method: 'get',\n    params\n  })\n}\n\nexport function list() {\n  return request({\n    url: '/system/api/category/list',\n    method: 'get'\n  })\n}\n\nexport function getById(id) {\n  return request({\n    url: `/system/api/category/getById/${id}`,\n    method: 'get'\n  })\n}\n\nexport function add(data) {\n  return request({\n    url: '/system/api/category',\n    method: 'post',\n    data\n  })\n}\n\nexport function update(data) {\n  return request({\n    url: '/system/api/category',\n    method: 'put',\n    data\n  })\n}\n\nexport function remove(ids) {\n  return request({\n    url: `/system/api/category/delete/${ids}`,\n    method: 'delete'\n  })\n}\n\nexport function getMaxSort() {\n  return request({\n    url: '/system/api/category/getMaxSort',\n    method: 'get'\n  })\n}\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/api/system/dictionary/data.js",
    "content": "import request from '@/utils/request'\n\nexport function page(params) {\n  return request({\n    url: '/system/dictionary/data/page',\n    method: 'get',\n    params\n  })\n}\n\nexport function getById(id) {\n  return request({\n    url: `/system/dictionary/data/getById/${id}`,\n    method: 'get'\n  })\n}\n\nexport function add(data) {\n  return request({\n    url: '/system/dictionary/data',\n    method: 'post',\n    data\n  })\n}\n\nexport function update(data) {\n  return request({\n    url: '/system/dictionary/data',\n    method: 'put',\n    data\n  })\n}\n\nexport function remove(ids) {\n  return request({\n    url: `/system/dictionary/data/delete/${ids}`,\n    method: 'delete'\n  })\n}\n\nexport function getMaxSortByDictionaryId(dictionaryId) {\n  return request({\n    url: '/system/dictionary/data/getMaxSortByDictionaryId',\n    method: 'get',\n    params: {\n      dictionaryId\n    }\n  })\n}\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/api/system/dictionary/dictionary.js",
    "content": "import request from '@/utils/request'\n\nexport function page(params) {\n  return request({\n    url: '/system/dictionary/page',\n    method: 'get',\n    params\n  })\n}\n\nexport function list() {\n  return request({\n    url: '/system/dictionary/list',\n    method: 'get'\n  })\n}\n\nexport function getById(id) {\n  return request({\n    url: `/system/dictionary/getById/${id}`,\n    method: 'get'\n  })\n}\n\nexport function add(data) {\n  return request({\n    url: '/system/dictionary',\n    method: 'post',\n    data\n  })\n}\n\nexport function update(data) {\n  return request({\n    url: '/system/dictionary',\n    method: 'put',\n    data\n  })\n}\n\nexport function remove(ids) {\n  return request({\n    url: `/system/dictionary/delete/${ids}`,\n    method: 'delete'\n  })\n}\n\nexport function getMaxSort() {\n  return request({\n    url: '/system/dictionary/getMaxSort',\n    method: 'get'\n  })\n}\n\nexport function getAllDictionaryInfo() {\n  return request({\n    url: '/system/dictionary/getAllDictionaryInfo',\n    method: 'get'\n  })\n}\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/api/system/log/login.js",
    "content": "import request from '@/utils/request'\n\nexport function page(params) {\n  return request({\n    url: '/system/log/login/page',\n    method: 'get',\n    params\n  })\n}\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/api/system/log/operation.js",
    "content": "import request from '@/utils/request'\n\nexport function page(params) {\n  return request({\n    url: '/system/log/operation/page',\n    method: 'get',\n    params\n  })\n}\n\nexport function getById(id) {\n  return request({\n    url: `/system/log/operation/getById/${id}`,\n    method: 'get'\n  })\n}\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/api/system/menu.js",
    "content": "import request from '@/utils/request'\n\nexport function list(params) {\n  return request({\n    url: '/system/menu/list',\n    method: 'get',\n    params\n  })\n}\n\nexport function tree() {\n  return request({\n    url: '/system/menu/tree',\n    method: 'get'\n  })\n}\n\nexport function getById(id) {\n  return request({\n    url: `/system/menu/getById/${id}`,\n    method: 'get'\n  })\n}\n\nexport function getMaxSortByParentId(parentId) {\n  return request({\n    url: '/system/menu/getMaxSortByParentId',\n    method: 'get',\n    params: {\n      parentId\n    }\n  })\n}\n\nexport function add(data) {\n  return request({\n    url: '/system/menu',\n    method: 'post',\n    data\n  })\n}\n\nexport function update(data) {\n  return request({\n    url: '/system/menu',\n    method: 'put',\n    data\n  })\n}\n\nexport function remove(id) {\n  return request({\n    url: `/system/menu/delete/${id}`,\n    method: 'delete'\n  })\n}\n\nexport function authApiList(params) {\n  return request({\n    url: '/system/menu/auth/api/list',\n    method: 'get',\n    params\n  })\n}\n\nexport function authApi(data) {\n  return request({\n    url: '/system/menu/auth/api',\n    method: 'post',\n    data\n  })\n}\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/api/system/param.js",
    "content": "import request from '@/utils/request'\n\nexport function page(params) {\n  return request({\n    url: '/system/param/page',\n    method: 'get',\n    params\n  })\n}\n\nexport function getById(id) {\n  return request({\n    url: `/system/param/getById/${id}`,\n    method: 'get'\n  })\n}\n\nexport function add(data) {\n  return request({\n    url: '/system/param',\n    method: 'post',\n    data\n  })\n}\n\nexport function update(data) {\n  return request({\n    url: '/system/param',\n    method: 'put',\n    data\n  })\n}\n\nexport function remove(ids) {\n  return request({\n    url: `/system/param/delete/${ids}`,\n    method: 'delete'\n  })\n}\n\nexport function getMaxSort() {\n  return request({\n    url: '/system/param/getMaxSort',\n    method: 'get'\n  })\n}\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/api/system/role.js",
    "content": "import request from '@/utils/request'\n\nexport function page(params) {\n  return request({\n    url: '/system/role/page',\n    method: 'get',\n    params\n  })\n}\n\nexport function getById(id) {\n  return request({\n    url: `/system/role/getById/${id}`,\n    method: 'get'\n  })\n}\n\nexport function add(data) {\n  return request({\n    url: '/system/role',\n    method: 'post',\n    data\n  })\n}\n\nexport function update(data) {\n  return request({\n    url: '/system/role',\n    method: 'put',\n    data\n  })\n}\n\nexport function remove(ids) {\n  return request({\n    url: `/system/role/delete/${ids}`,\n    method: 'delete'\n  })\n}\n\nexport function getMaxSort() {\n  return request({\n    url: '/system/role/getMaxSort',\n    method: 'get'\n  })\n}\n\nexport function userAuthPage(params) {\n  return request({\n    url: '/system/role/auth/user/page',\n    method: 'get',\n    params\n  })\n}\n\nexport function userUnAuthPage(params) {\n  return request({\n    url: '/system/role/auth/user/not/page',\n    method: 'get',\n    params\n  })\n}\n\nexport function authUser(data) {\n  return request({\n    url: '/system/role/auth/user',\n    method: 'post',\n    data\n  })\n}\n\nexport function removeAuthUser(data) {\n  return request({\n    url: '/system/role/auth/user/delete',\n    method: 'delete',\n    data\n  })\n}\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/api/system/user.js",
    "content": "import request from '@/utils/request'\n\nexport function page(params) {\n  return request({\n    url: '/system/user/page',\n    method: 'get',\n    params\n  })\n}\n\nexport function getById(id) {\n  return request({\n    url: `/system/user/getById/${id}`,\n    method: 'get'\n  })\n}\n\nexport function add(data) {\n  return request({\n    url: '/system/user',\n    method: 'post',\n    data\n  })\n}\n\nexport function update(data) {\n  return request({\n    url: '/system/user',\n    method: 'put',\n    data\n  })\n}\n\nexport function remove(ids) {\n  return request({\n    url: `/system/user/delete/${ids}`,\n    method: 'delete'\n  })\n}\n\nexport function resetPassword(data) {\n  return request({\n    url: '/system/user/resetPassword',\n    method: 'put',\n    data\n  })\n}\n\nexport function userUpdateInfo(data) {\n  return request({\n    url: '/system/user/userUpdateInfo',\n    method: 'put',\n    data\n  })\n}\n\nexport function resetUserPassword(data) {\n  return request({\n    url: '/system/user/resetUserPassword',\n    method: 'put',\n    data\n  })\n}\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/assets/styles/btn.scss",
    "content": "@import './variables.module.scss';\n\n@mixin colorBtn($color) {\n  background: $color;\n\n  &:hover {\n    color: $color;\n\n    &:before,\n    &:after {\n      background: $color;\n    }\n  }\n}\n\n.blue-btn {\n  @include colorBtn($blue);\n}\n\n.light-blue-btn {\n  @include colorBtn($light-blue);\n}\n\n.red-btn {\n  @include colorBtn($red);\n}\n\n.pink-btn {\n  @include colorBtn($pink);\n}\n\n.green-btn {\n  @include colorBtn($green);\n}\n\n.tiffany-btn {\n  @include colorBtn($tiffany);\n}\n\n.yellow-btn {\n  @include colorBtn($yellow);\n}\n\n.pan-btn {\n  font-size: 14px;\n  color: #fff;\n  padding: 14px 36px;\n  border-radius: 8px;\n  border: none;\n  outline: none;\n  transition: 600ms ease all;\n  position: relative;\n  display: inline-block;\n\n  &:hover {\n    background: #fff;\n\n    &:before,\n    &:after {\n      width: 100%;\n      transition: 600ms ease all;\n    }\n  }\n\n  &:before,\n  &:after {\n    content: '';\n    position: absolute;\n    top: 0;\n    right: 0;\n    height: 2px;\n    width: 0;\n    transition: 400ms ease all;\n  }\n\n  &::after {\n    right: inherit;\n    top: inherit;\n    left: 0;\n    bottom: 0;\n  }\n}\n\n.custom-button {\n  display: inline-block;\n  line-height: 1;\n  white-space: nowrap;\n  cursor: pointer;\n  background: #fff;\n  color: #fff;\n  -webkit-appearance: none;\n  text-align: center;\n  box-sizing: border-box;\n  outline: 0;\n  margin: 0;\n  padding: 10px 15px;\n  font-size: 14px;\n  border-radius: 4px;\n}\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/assets/styles/element-plus-ui.scss",
    "content": "// cover some element-ui styles\n\n.el-breadcrumb__inner,\n.el-breadcrumb__inner a {\n  font-weight: 400 !important;\n}\n\n.el-upload {\n  input[type='file'] {\n    display: none !important;\n  }\n}\n\n.el-upload__input {\n  display: none;\n}\n\n.cell {\n  .el-tag {\n    margin-right: 0px;\n  }\n}\n\n.small-padding {\n  .cell {\n    padding-left: 5px;\n    padding-right: 5px;\n  }\n}\n\n.fixed-width {\n  .el-button--mini {\n    padding: 7px 10px;\n    width: 60px;\n  }\n}\n\n.status-col {\n  .cell {\n    padding: 0 10px;\n    text-align: center;\n\n    .el-tag {\n      margin-right: 0px;\n    }\n  }\n}\n\n// to fixed https://github.com/ElemeFE/element/issues/2461\n.el-dialog {\n  transform: none;\n  left: 0;\n  position: relative;\n  margin: 0 auto;\n}\n\n// refine element ui upload\n.upload-container {\n  .el-upload {\n    width: 100%;\n\n    .el-upload-dragger {\n      width: 100%;\n      height: 200px;\n    }\n  }\n}\n\n// dropdown\n.el-dropdown-menu {\n  a {\n    display: block;\n  }\n}\n\n// fix date-picker ui bug in filter-item\n.el-range-editor.el-input__inner {\n  display: inline-flex !important;\n}\n\n// to fix el-date-picker css style\n.el-range-separator {\n  box-sizing: content-box;\n}\n\n.el-menu--collapse\n  > div\n  > .el-submenu\n  > .el-submenu__title\n  .el-submenu__icon-arrow {\n  display: none;\n}\n\n.el-dropdown .el-dropdown-link {\n  color: var(--el-color-primary) !important;\n}\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/assets/styles/geshanzsq.scss",
    "content": "/**\n * 通用css样式布局处理\n */\n\n/** 基础通用 **/\n.pt5 {\n  padding-top: 5px;\n}\n.pr5 {\n  padding-right: 5px;\n}\n.pb5 {\n  padding-bottom: 5px;\n}\n.mt5 {\n  margin-top: 5px;\n}\n.mr5 {\n  margin-right: 5px;\n}\n.mb5 {\n  margin-bottom: 5px;\n}\n.mb8 {\n  margin-bottom: 8px;\n}\n.ml5 {\n  margin-left: 5px;\n}\n.mt10 {\n  margin-top: 10px;\n}\n.mr10 {\n  margin-right: 10px;\n}\n.mb10 {\n  margin-bottom: 10px;\n}\n.ml10 {\n  margin-left: 10px;\n}\n.mb15 {\n  margin-bottom: 15px;\n}\n.mt20 {\n  margin-top: 20px;\n}\n.mr20 {\n  margin-right: 20px;\n}\n.mb20 {\n  margin-bottom: 20px;\n}\n.ml20 {\n  margin-left: 20px;\n}\n\n.h1,\n.h2,\n.h3,\n.h4,\n.h5,\n.h6,\nh1,\nh2,\nh3,\nh4,\nh5,\nh6 {\n  font-family: inherit;\n  font-weight: 500;\n  line-height: 1.1;\n  color: inherit;\n}\n\n.el-form .el-form-item__label {\n  font-weight: 700;\n}\n.el-dialog:not(.is-fullscreen) {\n  margin-top: 6vh !important;\n}\n\n.el-dialog.scrollbar .el-dialog__body {\n  overflow: auto;\n  overflow-x: hidden;\n  max-height: 70vh;\n  padding: 10px 20px 0;\n}\n\n.el-table {\n  .el-table__header-wrapper,\n  .el-table__fixed-header-wrapper {\n    th {\n      word-break: break-word;\n      background-color: #f8f8f9 !important;\n      color: #515a6e;\n      height: 40px !important;\n      font-size: 13px;\n    }\n  }\n  .el-table__body-wrapper {\n    .el-button [class*='el-icon-'] + span {\n      margin-left: 1px;\n    }\n  }\n}\n\n/** 表单布局 **/\n.form-header {\n  font-size: 15px;\n  color: #6379bb;\n  border-bottom: 1px solid #ddd;\n  margin: 8px 10px 25px 10px;\n  padding-bottom: 5px;\n}\n\n/** 表格布局 **/\n.pagination-container {\n  // position: relative;\n  height: 25px;\n  margin-bottom: 10px;\n  margin-top: 15px;\n  padding: 10px 20px !important;\n}\n\n/* tree border */\n.tree-border {\n  margin-top: 5px;\n  border: 1px solid #e5e6e7;\n  background: #ffffff none;\n  border-radius: 4px;\n  width: 100%;\n}\n\n.pagination-container .el-pagination {\n  right: 0;\n  position: absolute;\n}\n\n@media (max-width: 768px) {\n  .pagination-container .el-pagination > .el-pagination__jump {\n    display: none !important;\n  }\n  .pagination-container .el-pagination > .el-pagination__sizes {\n    display: none !important;\n  }\n}\n\n.el-table .fixed-width .el-button--small {\n  padding-left: 0;\n  padding-right: 0;\n  width: inherit;\n}\n\n/** 表格更多操作下拉样式 */\n.el-table .el-dropdown-link {\n  cursor: pointer;\n  color: #409eff;\n  margin-left: 10px;\n}\n\n.el-table .el-dropdown,\n.el-icon-arrow-down {\n  font-size: 12px;\n}\n\n.el-tree-node__content > .el-checkbox {\n  margin-right: 8px;\n}\n\n.list-group-striped > .list-group-item {\n  border-left: 0;\n  border-right: 0;\n  border-radius: 0;\n  padding-left: 0;\n  padding-right: 0;\n}\n\n.list-group {\n  padding-left: 0px;\n  list-style: none;\n}\n\n.list-group-item {\n  border-bottom: 1px solid #e7eaec;\n  border-top: 1px solid #e7eaec;\n  margin-bottom: -1px;\n  padding: 11px 0px;\n  font-size: 13px;\n}\n\n.pull-right {\n  float: right !important;\n}\n\n.el-card__header {\n  padding: 14px 15px 7px !important;\n  min-height: 40px;\n}\n\n.el-card__body {\n  padding: 15px 20px 20px 20px !important;\n}\n\n.card-box {\n  padding-right: 15px;\n  padding-left: 15px;\n  margin-bottom: 10px;\n}\n\n/* button color */\n.el-button--cyan.is-active,\n.el-button--cyan:active {\n  background: #20b2aa;\n  border-color: #20b2aa;\n  color: #ffffff;\n}\n\n.el-button--cyan:focus,\n.el-button--cyan:hover {\n  background: #48d1cc;\n  border-color: #48d1cc;\n  color: #ffffff;\n}\n\n.el-button--cyan {\n  background-color: #20b2aa;\n  border-color: #20b2aa;\n  color: #ffffff;\n}\n\n/* text color */\n.text-navy {\n  color: #1ab394;\n}\n\n.text-primary {\n  color: inherit;\n}\n\n.text-success {\n  color: #1c84c6;\n}\n\n.text-info {\n  color: #23c6c8;\n}\n\n.text-warning {\n  color: #f8ac59;\n}\n\n.text-danger {\n  color: #ed5565;\n}\n\n.text-muted {\n  color: #888888;\n}\n\n/* image */\n.img-circle {\n  border-radius: 50%;\n}\n\n.img-lg {\n  width: 120px;\n  height: 120px;\n}\n\n.avatar-upload-preview {\n  position: absolute;\n  top: 50%;\n  transform: translate(50%, -50%);\n  width: 200px;\n  height: 200px;\n  border-radius: 50%;\n  box-shadow: 0 0 4px #ccc;\n  overflow: hidden;\n}\n\n/* 拖拽列样式 */\n.sortable-ghost {\n  opacity: 0.8;\n  color: #fff !important;\n  background: #42b983 !important;\n}\n\n/* 表格右侧工具栏样式 */\n.top-right-btn {\n  margin-left: auto;\n}\n\n/** 自定义全局样式 */\n/* 网站卡片样式 */\n.site-card-edit {\n  position: absolute;\n  height: 30px;\n  width: 30px;\n  border-radius: 15px;\n  line-height: 30px;\n  text-align: center;\n  font-size: 22px;\n  color: var(--el-color-white);\n  background-color: var(--el-color-success);\n  top: 35%;\n  left: 45%;\n  z-index: 1;\n  cursor: pointer;\n}\n.site-card-close {\n  position: absolute;\n  height: 30px;\n  width: 30px;\n  border-radius: 15px;\n  line-height: 30px;\n  text-align: center;\n  font-size: 18px;\n  color: var(--el-color-white);\n  background-color: var(--el-color-danger);\n  top: -5px;\n  right: -5px;\n  cursor: pointer;\n  z-index: 1;\n}\n\n.site-card-status-disable {\n  text-align: center;\n  transform: rotate(45deg);\n  position: absolute;\n  top: 10px;\n  right: 0px;\n  width: 60px;\n  font-size: 14px;\n  color: var(--el-color-white);\n  background-color: var(--el-color-info);\n}\n\n.site-card-mouse {\n  opacity: 0.6;\n}\n\n// 图片裁剪后显示\n.site-image-cropper {\n  border-radius: 50%;\n  width: 120px;\n  height: 120px;\n}\n\n// 图片上传预览\n.image-upload-preview {\n  position: absolute;\n  top: 50%;\n  transform: translate(50%, -50%);\n  width: 200px;\n  height: 200px;\n  border-radius: 50%;\n  box-shadow: 0 0 4px #ccc;\n  overflow: hidden;\n}\n\n// 网站图片表单文章提示\n.site-form-path-tip {\n  color: var(--el-color-gray);\n  font-size: 12px;\n  margin-top: -10px;\n}\n\n// 网站卡片拖动样式\n\n.site-card-col {\n  margin-bottom: 15px;\n  position: relative;\n  .site-card {\n    .card-footer-icon {\n      color: #979898;\n      font-size: 14px;\n      margin: 0 15px 5px 0;\n      .title {\n        margin-left: 3px;\n      }\n    }\n    .belong-category {\n      display: -webkit-box;\n      -webkit-box-orient: vertical;\n      -webkit-line-clamp: 1;\n      overflow: hidden;\n      font-size: 13px;\n      margin-top: 5px;\n      color: #979898;\n    }\n  }\n\n  .card-add {\n    text-align: center;\n    cursor: pointer;\n    .icon {\n      height: 26px;\n      width: 26px;\n      border-radius: 13px;\n      line-height: 26px;\n      border: 1px solid #7f7d7d;\n      color: #7f7d7d;\n      font-weight: bold;\n      text-align: center;\n      vertical-align: middle;\n      margin: 18px auto;\n    }\n    .icon-no-description {\n      margin: 12px auto;\n    }\n  }\n}\n\n\n.client-category-title {\n  cursor: pointer;\n}\n\n.client-category-icon {\n  margin-right: 7px;\n  font-size: 18px;\n}\n.client-category-name {\n  color: #555;\n  font-size: 17px;\n  font-weight: normal;\n}\n.client-category-edit-icon {\n  margin-left: 5px;\n}\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/assets/styles/index.scss",
    "content": "@import './btn.scss';\n@import './element-plus-ui.scss';\n@import './geshanzsq.scss';\n@import './mixin.scss';\n@import './sidebar.scss';\n@import './variables.module.scss';\n\n@import './themes/blue-black.scss';\n@import './themes/blue-white.scss';\n\nbody {\n  height: 100%;\n  margin: 0;\n  -moz-osx-font-smoothing: grayscale;\n  -webkit-font-smoothing: antialiased;\n  text-rendering: optimizeLegibility;\n  font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB,\n    Microsoft YaHei, Arial, sans-serif;\n}\n\nlabel {\n  font-weight: 700;\n}\n\nhtml {\n  height: 100%;\n  box-sizing: border-box;\n}\n\n#app {\n  height: 100%;\n}\n\n*,\n*:before,\n*:after {\n  box-sizing: inherit;\n}\n\n.no-padding {\n  padding: 0px !important;\n}\n\n.padding-content {\n  padding: 4px 0;\n}\n\na:focus,\na:active {\n  outline: none;\n}\n\na,\na:focus,\na:hover {\n  cursor: pointer;\n  color: inherit;\n  text-decoration: none;\n}\n\ndiv:focus {\n  outline: none;\n}\n\n.fr {\n  float: right;\n}\n\n.fl {\n  float: left;\n}\n\n.pr-5 {\n  padding-right: 5px;\n}\n\n.pl-5 {\n  padding-left: 5px;\n}\n\n.block {\n  display: block;\n}\n\n.pointer {\n  cursor: pointer;\n}\n\n.inlineBlock {\n  display: block;\n}\n\n.clearfix {\n  &:after {\n    visibility: hidden;\n    display: block;\n    font-size: 0;\n    content: ' ';\n    clear: both;\n    height: 0;\n  }\n}\n\naside {\n  background: #eef1f6;\n  padding: 8px 24px;\n  margin-bottom: 20px;\n  border-radius: 2px;\n  display: block;\n  line-height: 32px;\n  font-size: 16px;\n  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,\n    Ubuntu, Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;\n  color: #2c3e50;\n  -webkit-font-smoothing: antialiased;\n  -moz-osx-font-smoothing: grayscale;\n\n  a {\n    color: #337ab7;\n    cursor: pointer;\n\n    &:hover {\n      color: rgb(32, 160, 255);\n    }\n  }\n}\n\n//main-container全局样式\n.app-container {\n  padding: 20px;\n}\n\n.components-container {\n  margin: 30px 50px;\n  position: relative;\n}\n\n.pagination-container {\n  margin-top: 30px;\n}\n\n.text-center {\n  text-align: center;\n}\n\n.sub-navbar {\n  height: 50px;\n  line-height: 50px;\n  position: relative;\n  width: 100%;\n  text-align: right;\n  padding-right: 20px;\n  transition: 600ms ease position;\n  background: linear-gradient(\n    90deg,\n    rgba(32, 182, 249, 1) 0%,\n    rgba(32, 182, 249, 1) 0%,\n    rgba(33, 120, 241, 1) 100%,\n    rgba(33, 120, 241, 1) 100%\n  );\n\n  .subtitle {\n    font-size: 20px;\n    color: #fff;\n  }\n\n  &.draft {\n    background: #d0d0d0;\n  }\n\n  &.deleted {\n    background: #d0d0d0;\n  }\n}\n\n.link-type,\n.link-type:focus {\n  color: #337ab7;\n  cursor: pointer;\n\n  &:hover {\n    color: rgb(32, 160, 255);\n  }\n}\n\n.filter-container {\n  padding-bottom: 10px;\n\n  .filter-item {\n    display: inline-block;\n    vertical-align: middle;\n    margin-bottom: 10px;\n  }\n}\n\n//refine vue-multiselect plugin\n.multiselect {\n  line-height: 16px;\n}\n\n.multiselect--active {\n  z-index: 1000 !important;\n}\n\n// 定义滚动条高宽及背景 高宽分别对应横竖滚动条的尺寸\n::-webkit-scrollbar {\n  width: 5px;\n  height: 5px;\n}\n// 定义滚动条轨道 内阴影+圆角\n::-webkit-scrollbar-track {\n  -webkit-box-shadow: inset 0 0 5px rgba(139, 140, 142, 0.5);\n  border-radius: 10px;\n  background-color: white;\n}\n// 定义滑块 内阴影+圆角\n::-webkit-scrollbar-thumb {\n  border-radius: 5px;\n  -webkit-box-shadow: inset 0 0 5px rgba(139, 140, 142, 0.5);\n  background-color: rgba(144, 147, 153, 0.5);\n}\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/assets/styles/mixin.scss",
    "content": "@mixin clearfix {\n  &:after {\n    content: \"\";\n    display: table;\n    clear: both;\n  }\n}\n\n@mixin scrollBar {\n  &::-webkit-scrollbar-track-piece {\n    background: #d3dce6;\n  }\n\n  &::-webkit-scrollbar {\n    width: 6px;\n  }\n\n  &::-webkit-scrollbar-thumb {\n    background: #99a9bf;\n    border-radius: 20px;\n  }\n}\n\n@mixin relative {\n  position: relative;\n  width: 100%;\n  height: 100%;\n}\n\n@mixin pct($pct) {\n  width: #{$pct};\n  position: relative;\n  margin: 0 auto;\n}\n\n@mixin triangle($width, $height, $color, $direction) {\n  $width: $width/2;\n  $color-border-style: $height solid $color;\n  $transparent-border-style: $width solid transparent;\n  height: 0;\n  width: 0;\n\n  @if $direction==up {\n    border-bottom: $color-border-style;\n    border-left: $transparent-border-style;\n    border-right: $transparent-border-style;\n  }\n\n  @else if $direction==right {\n    border-left: $color-border-style;\n    border-top: $transparent-border-style;\n    border-bottom: $transparent-border-style;\n  }\n\n  @else if $direction==down {\n    border-top: $color-border-style;\n    border-left: $transparent-border-style;\n    border-right: $transparent-border-style;\n  }\n\n  @else if $direction==left {\n    border-right: $color-border-style;\n    border-top: $transparent-border-style;\n    border-bottom: $transparent-border-style;\n  }\n}\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/assets/styles/sidebar.scss",
    "content": "#app {\n  .main-container {\n    min-height: 100%;\n    transition: margin-left 0.28s;\n    margin-left: $base-sidebar-width;\n    position: relative;\n  }\n\n  .sidebarHide {\n    margin-left: 0 !important;\n  }\n\n  .sidebar-container {\n    -webkit-transition: width 0.28s;\n    transition: width 0.28s;\n    width: $base-sidebar-width !important;\n    background-color: $base-menu-background;\n    height: 100%;\n    position: fixed;\n    font-size: 0px;\n    top: 0;\n    bottom: 0;\n    left: 0;\n    z-index: 1001;\n    overflow: hidden;\n    -webkit-box-shadow: 2px 0 6px rgba(29, 35, 41, 0.05);\n    box-shadow: 2px 0 6px rgba(29, 35, 41, 0.05);\n\n    // reset element-ui css\n    .horizontal-collapse-transition {\n      transition: 0s width ease-in-out, 0s padding-left ease-in-out,\n        0s padding-right ease-in-out;\n    }\n\n    .scrollbar-wrapper {\n      overflow-x: hidden !important;\n    }\n\n    .el-scrollbar__bar.is-vertical {\n      right: 0px;\n    }\n\n    .el-scrollbar {\n      height: 100%;\n    }\n\n    &.has-logo {\n      .el-scrollbar {\n        height: calc(100% - 50px);\n      }\n    }\n\n    .is-horizontal {\n      display: none;\n    }\n\n    a {\n      display: inline-block;\n      width: 100%;\n      overflow: hidden;\n    }\n\n    .svg-icon {\n      margin-right: 16px;\n    }\n\n    .el-menu {\n      border: none;\n      height: 100%;\n      width: 100% !important;\n    }\n\n    .el-menu-item,\n    .menu-title {\n      overflow: hidden !important;\n      text-overflow: ellipsis !important;\n      white-space: nowrap !important;\n    }\n\n    .el-menu-item .el-menu-tooltip__trigger {\n      display: inline-block !important;\n    }\n\n    // menu hover\n    .sub-menu-title-noDropdown,\n    .el-sub-menu__title {\n      &:hover {\n        background-color: rgba(0, 0, 0, 0.06) !important;\n      }\n    }\n\n    & .theme-dark .is-active > .el-sub-menu__title {\n      color: $base-menu-color-active !important;\n    }\n\n    & .nest-menu .el-sub-menu > .el-sub-menu__title,\n    & .el-sub-menu .el-menu-item {\n      min-width: $base-sidebar-width !important;\n\n      &:hover {\n        background-color: rgba(0, 0, 0, 0.06) !important;\n      }\n    }\n\n    & .theme-dark .nest-menu .el-sub-menu > .el-sub-menu__title,\n    & .theme-dark .el-sub-menu .el-menu-item {\n      background-color: $base-sub-menu-background !important;\n\n      &:hover {\n        background-color: $base-sub-menu-hover !important;\n      }\n    }\n  }\n\n  .hideSidebar {\n    .sidebar-container {\n      width: 54px !important;\n    }\n\n    .main-container {\n      margin-left: 54px;\n    }\n\n    .sub-menu-title-noDropdown {\n      padding: 0 !important;\n      position: relative;\n\n      .el-tooltip {\n        padding: 0 !important;\n\n        .svg-icon {\n          margin-left: 20px;\n        }\n      }\n    }\n\n    .el-sub-menu {\n      overflow: hidden;\n\n      & > .el-sub-menu__title {\n        padding: 0 !important;\n\n        .svg-icon {\n          margin-left: 20px;\n        }\n      }\n    }\n\n    .el-menu--collapse {\n      .el-sub-menu {\n        & > .el-sub-menu__title {\n          & > span {\n            height: 0;\n            width: 0;\n            overflow: hidden;\n            visibility: hidden;\n            display: inline-block;\n          }\n          & > i {\n            height: 0;\n            width: 0;\n            overflow: hidden;\n            visibility: hidden;\n            display: inline-block;\n          }\n        }\n      }\n    }\n  }\n\n  .el-menu--collapse .el-menu .el-sub-menu {\n    min-width: $base-sidebar-width !important;\n  }\n\n  // mobile responsive\n  .mobile {\n    .main-container {\n      margin-left: 0px;\n    }\n\n    .sidebar-container {\n      transition: transform 0.28s;\n      width: $base-sidebar-width !important;\n    }\n\n    &.hideSidebar {\n      .sidebar-container {\n        pointer-events: none;\n        transition-duration: 0.3s;\n        transform: translate3d(-$base-sidebar-width, 0, 0);\n      }\n    }\n  }\n\n  .withoutAnimation {\n    .main-container,\n    .sidebar-container {\n      transition: none;\n    }\n  }\n}\n\n// when menu collapsed\n.el-menu--vertical {\n  & > .el-menu {\n    .svg-icon {\n      margin-right: 16px;\n    }\n  }\n\n  .nest-menu .el-sub-menu > .el-sub-menu__title,\n  .el-menu-item {\n    &:hover {\n      // you can use $sub-menuHover\n      background-color: rgba(0, 0, 0, 0.06) !important;\n    }\n  }\n\n  // the scroll bar appears when the sub-menu is too long\n  > .el-menu--popup {\n    max-height: 100vh;\n    overflow-y: auto;\n\n    &::-webkit-scrollbar-track-piece {\n      background: #d3dce6;\n    }\n\n    &::-webkit-scrollbar {\n      width: 6px;\n    }\n\n    &::-webkit-scrollbar-thumb {\n      background: #99a9bf;\n      border-radius: 20px;\n    }\n  }\n}\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/assets/styles/themes/blue-black.scss",
    "content": "// 侧边栏背景颜色\n$sidebar-background-color: #282c34;\n// 菜单选中颜色\n$menu-item-active-background-color: var(--el-color-primary);\n$menu-item-active-text-color: #fff;\n// 菜单文字颜色\n$menu-item-text-color: #fff;\n// logo 标题颜色\n$logo-title: #fff;\n\n.geshan-theme-blue-black {\n  .sidebar-container {\n    background-color: $sidebar-background-color !important;\n\n    .logo-container {\n      .title {\n        color: $logo-title;\n      }\n    }\n\n    .el-menu {\n      background-color: $sidebar-background-color !important;\n      .el-sub-menu__title,\n      .el-menu-item,\n      i {\n        color: $menu-item-text-color !important;\n      }\n    }\n\n    .el-menu-item.is-active,\n    .el-sub-menu__title.is-active,\n    .el-menu-item:hover,\n    .el-sub-menu__title:hover {\n      i {\n        color: $menu-item-active-text-color !important;\n      }\n      color: $menu-item-active-text-color !important;\n      background-color: $menu-item-active-background-color !important;\n    }\n  }\n}\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/assets/styles/themes/blue-white.scss",
    "content": "// 侧边栏背景颜色\n$sidebar-background-color: #fff;\n// 菜单选中颜色\n$menu-item-active-background-color: var(--el-color-primary);\n$menu-item-active-text-color: var(--el-color-primary);\n\n.geshan-theme-blue-white {\n  .sidebar-container {\n    background-color: $sidebar-background-color !important;\n\n    .logo-container {\n      .title {\n        color: #515a6e !important;\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/assets/styles/variables.module.scss",
    "content": "// 基础颜色\n$blue: #324157;\n$light-blue: #3a71a8;\n$red: #c03639;\n$pink: #e65d6e;\n$green: #30b08f;\n$tiffany: #4ab7bd;\n$yellow: #fec171;\n$panGreen: #30b08f;\n\n$--color-primary: #409eff;\n$--color-success: #67c23a;\n$--color-warning: #e6a23c;\n$--color-danger: #f56c6c;\n$--color-info: #909399;\n\n$base-sidebar-width: 200px;\n\n// 默认菜单主题风格\n$base-menu-color: #bfcbd9;\n// $base-menu-color: #fff;\n$base-menu-color-active: #f4f4f5;\n$base-menu-background: #304156;\n// $base-menu-background: #282c34;\n$base-logo-title-color: #ffffff;\n\n$base-menu-light-color: rgba(0, 0, 0, 0.7);\n$base-menu-light-background: #ffffff;\n$base-logo-light-title-color: #001529;\n$base-sub-menu-background: #1f2d3d;\n$base-sub-menu-hover: #001528;\n// $base-sub-menu-background: #282c34;\n// $base-sub-menu-hover: #1890ff;\n\n// 自定义暗色菜单风格\n/**\n$base-menu-color:hsla(0,0%,100%,.65);\n$base-menu-color-active:#fff;\n$base-menu-background:#001529;\n$base-logo-title-color: #ffffff;\n\n$base-menu-light-color:rgba(0,0,0,.70);\n$base-menu-light-background:#ffffff;\n$base-logo-light-title-color: #001529;\n\n$base-sub-menu-background:#000c17;\n$base-sub-menu-hover:#001528;\n*/\n\n// 导出配置\n:export {\n  menuColor: $base-menu-color;\n  menuLightColor: $base-menu-light-color;\n  menuColorActive: $base-menu-color-active;\n  menuBackground: $base-menu-background;\n  menuLightBackground: $base-menu-light-background;\n  subMenuBackground: $base-sub-menu-background;\n  subMenuHover: $base-sub-menu-hover;\n  sideBarWidth: $base-sidebar-width;\n  logoTitleColor: $base-logo-title-color;\n  logoLightTitleColor: $base-logo-light-title-color;\n  primaryColor: $--color-primary;\n  successColor: $--color-success;\n  dangerColor: $--color-danger;\n  infoColor: $--color-info;\n  warningColor: $--color-warning;\n}\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/components/Breadcrumb/index.vue",
    "content": "<template>\n  <el-breadcrumb class=\"app-breadcrumb\" separator=\"/\">\n    <transition-group name=\"breadcrumb\">\n      <el-breadcrumb-item v-for=\"(item, index) in levelList\" :key=\"item.path\">\n        <span\n          v-if=\"item.redirect === 'noRedirect' || index == levelList.length - 1\"\n          class=\"no-redirect\"\n          >{{ item.meta.title }}</span\n        >\n        <a v-else @click.prevent=\"handleLink(item)\">{{ item.meta.title }}</a>\n      </el-breadcrumb-item>\n    </transition-group>\n  </el-breadcrumb>\n</template>\n\n<script setup>\nimport { ref, watchEffect } from 'vue'\nimport { useRoute, useRouter } from 'vue-router'\n\nconst route = useRoute()\nconst router = useRouter()\nconst levelList = ref([])\n\nfunction getBreadcrumb() {\n  // only show routes with meta.title\n  let matched = route.matched.filter((item) => item.meta && item.meta.title)\n  const first = matched[0]\n  // 判断是否为首页\n  if (!isDashboard(first)) {\n    matched = [{ path: '/index', meta: { title: '首页' } }].concat(matched)\n  }\n\n  levelList.value = matched.filter(\n    (item) => item.meta && item.meta.title && item.meta.breadcrumb !== false\n  )\n}\nfunction isDashboard(route) {\n  const name = route && route.name\n  if (!name) {\n    return false\n  }\n  return name.trim() === 'Index'\n}\nfunction handleLink(item) {\n  const { redirect, path } = item\n  if (redirect) {\n    router.push(redirect)\n    return\n  }\n  router.push(path)\n}\n\nwatchEffect(() => {\n  // if you go to the redirect page, do not update the breadcrumbs\n  if (route.path.startsWith('/redirect/')) {\n    return\n  }\n  getBreadcrumb()\n})\ngetBreadcrumb()\n</script>\n\n<style lang=\"scss\" scoped>\n.app-breadcrumb.el-breadcrumb {\n  display: inline-block;\n  font-size: 14px;\n  line-height: 50px;\n  margin-left: 8px;\n\n  .no-redirect {\n    color: #97a8be;\n    cursor: text;\n  }\n}\n</style>\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/components/DictionaryOption/index.vue",
    "content": "<template>\n  <div>\n    <el-option\n      :key=\"dictionary.id\"\n      :label=\"dictionary.dictionaryLabel\"\n      :value=\"dictionary.dictionaryValue\"\n      v-for=\"dictionary in dictionaryDataList\"\n    />\n  </div>\n</template>\n<script setup>\nimport { ref } from 'vue'\n\nimport { getDictionary } from '@/utils/geshanzsq'\n\nconst props = defineProps({\n  // 字典编码\n  code: {\n    type: String,\n    required: true\n  }\n})\n\nconst dictionaryDataList = ref([])\n\nfunction getDictionaryDataList() {\n  dictionaryDataList.value = getDictionary(props.code)\n}\n\ngetDictionaryDataList()\n</script>\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/components/DictionaryRadio/index.vue",
    "content": "<template>\n  <div>\n    <el-radio\n      :key=\"dictionary.id\"\n      :label=\"dictionary.dictionaryValue\"\n      v-for=\"dictionary in dictionaryDataList\"\n      >{{ dictionary.dictionaryLabel }}</el-radio\n    >\n  </div>\n</template>\n<script setup>\nimport { ref } from 'vue'\n\nimport { getDictionary } from '@/utils/geshanzsq'\n\nconst props = defineProps({\n  // 字典编码\n  code: {\n    type: String,\n    required: true\n  }\n})\n\nconst dictionaryDataList = ref([])\n\nfunction getDictionaryDataList() {\n  dictionaryDataList.value = getDictionary(props.code)\n}\n\ngetDictionaryDataList()\n</script>\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/components/DictionaryTag/index.vue",
    "content": "<template>\n  <span v-if=\"type === '' || type === 'default'\">{{ label }}</span>\n  <el-tag v-else :effect=\"effect\" :type=\"type\" :round=\"round\" :size=\"size\">{{\n    label\n  }}</el-tag>\n</template>\n<script setup>\nimport { ref, watch } from 'vue'\nimport { getDictionaryData } from '@/utils/geshanzsq'\n\nconst props = defineProps({\n  // 字典编码\n  code: {\n    type: String,\n    required: true\n  },\n  // 字典值\n  value: {\n    type: [Number, String, Array]\n  },\n  // 主题\n  effect: {\n    type: String,\n    default: 'light'\n  },\n  // 是否圆角\n  round: {\n    type: Boolean,\n    default: false\n  },\n  // 大小\n  size: {\n    type: String,\n    default: 'default'\n  }\n})\n\n// 显示内容\nconst label = ref('')\n// 类型\nconst type = ref('')\n\n/**\n * 监听变化，否则数据有变动不会生效\n */\nwatch(props, (nweProps, oldProps) => {\n  getLabel()\n})\n\n/**\n * 获取标签\n */\nfunction getLabel() {\n  const dictionaryData = getDictionaryData(props.code, props.value)\n  if (dictionaryData !== undefined) {\n    label.value = dictionaryData.dictionaryLabel\n    type.value = dictionaryData.classType\n  }\n}\n\ngetLabel()\n</script>\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/components/Hamburger/index.vue",
    "content": "<template>\n  <div style=\"padding: 0 15px\" @click=\"toggleClick\">\n    <svg\n      :class=\"{ 'is-active': isActive }\"\n      class=\"hamburger\"\n      viewBox=\"0 0 1024 1024\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      width=\"64\"\n      height=\"64\"\n    >\n      <path\n        d=\"M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM142.4 642.1L298.7 519a8.84 8.84 0 0 0 0-13.9L142.4 381.9c-5.8-4.6-14.4-.5-14.4 6.9v246.3a8.9 8.9 0 0 0 14.4 7z\"\n      />\n    </svg>\n  </div>\n</template>\n\n<script setup>\ndefineProps({\n  isActive: {\n    type: Boolean,\n    default: false\n  }\n})\n\nconst emit = defineEmits(null)\nconst toggleClick = () => {\n  emit('toggleClick')\n}\n</script>\n\n<style scoped>\n.hamburger {\n  display: inline-block;\n  vertical-align: middle;\n  width: 20px;\n  height: 20px;\n}\n\n.hamburger.is-active {\n  transform: rotate(180deg);\n}\n</style>\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/components/HeaderSearch/index.vue",
    "content": "<template>\n  <div :class=\"{ show: show }\" class=\"header-search\">\n    <svg-icon class-name=\"search-icon\" icon-name=\"search\" @click.stop=\"click\" />\n    <el-select\n      ref=\"headerSearchSelectRef\"\n      v-model=\"search\"\n      :remote-method=\"querySearch\"\n      filterable\n      default-first-option\n      remote\n      placeholder=\"搜索\"\n      class=\"header-search-select\"\n      @change=\"change\"\n    >\n      <el-option\n        v-for=\"option in options\"\n        :key=\"option.item.path\"\n        :value=\"option.item\"\n        :label=\"option.item.title.join(' > ')\"\n      />\n    </el-select>\n  </div>\n</template>\n\n<script setup>\nimport Fuse from 'fuse.js'\nimport { getNormalPath } from '@/utils/geshanzsq'\nimport { isHttp } from '@/utils/validate'\nimport { computed, nextTick, onMounted, ref, watch, watchEffect } from 'vue'\nimport { useStore } from 'vuex'\nimport { useRouter } from 'vue-router'\n\nconst search = ref('')\nconst options = ref([])\nconst searchPool = ref([])\nconst show = ref(false)\nconst fuse = ref(undefined)\nconst headerSearchSelectRef = ref(null)\nconst store = useStore()\nconst router = useRouter()\nconst routes = computed(() => store.getters['permission/routes'])\n\nfunction click() {\n  show.value = !show.value\n  if (show.value) {\n    headerSearchSelectRef.value && headerSearchSelectRef.value.focus()\n  }\n}\nfunction close() {\n  headerSearchSelectRef.value && headerSearchSelectRef.value.blur()\n  options.value = []\n  show.value = false\n}\nfunction change(val) {\n  const path = val.path\n  if (isHttp(path)) {\n    // http(s):// 路径新窗口打开\n    const pindex = path.indexOf('http')\n    window.open(path.substr(pindex, path.length), '_blank')\n  } else {\n    router.push(path)\n  }\n\n  search.value = ''\n  options.value = []\n  nextTick(() => {\n    show.value = false\n  })\n}\nfunction initFuse(list) {\n  fuse.value = new Fuse(list, {\n    shouldSort: true,\n    threshold: 0.4,\n    location: 0,\n    distance: 100,\n    maxPatternLength: 32,\n    minMatchCharLength: 1,\n    keys: [\n      {\n        name: 'title',\n        weight: 0.7\n      },\n      {\n        name: 'path',\n        weight: 0.3\n      }\n    ]\n  })\n}\n// Filter out the routes that can be displayed in the sidebar\n// And generate the internationalized title\nfunction generateRoutes(routes, basePath = '', prefixTitle = []) {\n  let res = []\n  if (routes === undefined) {\n    return res\n  }\n\n  for (const r of routes) {\n    // skip hidden router\n    if (r.hidden) {\n      continue\n    }\n    const p = r.path.length > 0 && r.path[0] === '/' ? r.path : '/' + r.path\n    const data = {\n      path: !isHttp(r.path) ? getNormalPath(basePath + p) : r.path,\n      title: [...prefixTitle]\n    }\n\n    if (r.meta && r.meta.title) {\n      data.title = [...data.title, r.meta.title]\n\n      if (r.redirect !== 'noRedirect') {\n        // only push the routes with title\n        // special case: need to exclude parent router without redirect\n        res.push(data)\n      }\n    }\n\n    // recursive child routes\n    if (r.children) {\n      const tempRoutes = generateRoutes(r.children, data.path, data.title)\n      if (tempRoutes.length >= 1) {\n        res = [...res, ...tempRoutes]\n      }\n    }\n  }\n  return res\n}\nfunction querySearch(query) {\n  if (query !== '') {\n    options.value = fuse.value.search(query)\n  } else {\n    options.value = []\n  }\n}\n\nonMounted(() => {\n  searchPool.value = generateRoutes(routes.value)\n})\n\nwatchEffect(() => {\n  searchPool.value = generateRoutes(routes.value)\n})\n\nwatch(show, (value) => {\n  if (value) {\n    document.body.addEventListener('click', close)\n  } else {\n    document.body.removeEventListener('click', close)\n  }\n})\n\nwatch(searchPool, (list) => {\n  initFuse(list)\n})\n</script>\n\n<style lang=\"scss\" scoped>\n.header-search {\n  font-size: 0 !important;\n\n  .search-icon {\n    cursor: pointer;\n    font-size: 18px;\n    vertical-align: middle;\n  }\n\n  .header-search-select {\n    font-size: 18px;\n    transition: width 0.2s;\n    width: 0;\n    overflow: hidden;\n    background: transparent;\n    border-radius: 0;\n    display: inline-block;\n    vertical-align: middle;\n\n    :deep(.el-input__inner) {\n      border-radius: 0;\n      border: 0;\n      padding-left: 0;\n      padding-right: 0;\n      box-shadow: none !important;\n      border-bottom: 1px solid #d9d9d9;\n      vertical-align: middle;\n    }\n  }\n\n  &.show {\n    .header-search-select {\n      width: 210px;\n      margin-left: 10px;\n    }\n  }\n}\n</style>\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/components/IconSelect/index.vue",
    "content": "<template>\n  <div class=\"icon-body\">\n    <el-input\n      v-model=\"iconName\"\n      style=\"position: relative\"\n      clearable\n      placeholder=\"请输入图标名称\"\n      @clear=\"filterIcons\"\n      @input=\"filterIcons\"\n    >\n      <template #suffix>\n        <el-icon><Search /></el-icon>\n      </template>\n    </el-input>\n    <div class=\"icon-list\">\n      <div\n        v-for=\"(item, index) in iconList\"\n        :key=\"index\"\n        @click=\"selectedIcon(item)\"\n      >\n        <svg-icon :icon-name=\"item\" style=\"height: 30px; width: 16px\" />\n        <span>{{ item }}</span>\n      </div>\n    </div>\n  </div>\n</template>\n\n<script setup>\nimport { ref } from 'vue'\nimport icons from './requireIcons'\n\nconst iconName = ref('')\nconst iconList = ref(icons)\nconst emit = defineEmits(null)\n\nfunction filterIcons() {\n  iconList.value = icons\n  if (iconName.value) {\n    iconList.value = icons.filter((item) => item.indexOf(iconName.value) !== -1)\n  }\n}\n\nfunction selectedIcon(name) {\n  emit('selected', name)\n}\n\nfunction reset() {\n  iconName.value = ''\n  iconList.value = icons\n}\n\ndefineExpose({\n  reset\n})\n</script>\n\n<style lang=\"scss\" scoped>\n.icon-body {\n  width: 100%;\n  padding: 10px;\n  .icon-list {\n    height: 200px;\n    overflow-y: scroll;\n    div {\n      height: 30px;\n      line-height: 30px;\n      margin-bottom: -5px;\n      cursor: pointer;\n      width: 33%;\n      float: left;\n    }\n    span {\n      display: inline-block;\n      vertical-align: -0.15em;\n      fill: currentColor;\n      overflow: hidden;\n    }\n  }\n}\n</style>\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/components/IconSelect/requireIcons.js",
    "content": "const icons = []\nconst svgRequire = require.context('@/assets/icons/svg', false, /\\.svg$/)\n\nsvgRequire.keys().forEach((svgIcon) => {\n  const path = svgIcon.split('./')[1].split('.svg')[0]\n  icons.push(path)\n})\n\nexport default icons\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/components/Pagination/index.vue",
    "content": "<template>\n  <div :class=\"{ hidden: hidden }\" class=\"pagination-container\">\n    <el-pagination\n      :background=\"background\"\n      v-model:current-page=\"currentPage\"\n      v-model:page-size=\"pageSize\"\n      :layout=\"layout\"\n      :page-sizes=\"pageSizes\"\n      :pager-count=\"pagerCount\"\n      :total=\"total\"\n      @size-change=\"handleSizeChange\"\n      @current-change=\"handleCurrentChange\"\n    />\n  </div>\n</template>\n\n<script setup>\nimport { computed } from 'vue'\n\nimport { scrollTo } from '@/utils/scroll-to'\n\nconst props = defineProps({\n  total: {\n    required: true,\n    type: Number\n  },\n  page: {\n    type: Number,\n    default: 1\n  },\n  limit: {\n    type: Number,\n    default: 20\n  },\n  pageSizes: {\n    type: Array,\n    default() {\n      return [10, 20, 30, 50]\n    }\n  },\n  // 移动端页码按钮的数量端默认值5\n  pagerCount: {\n    type: Number,\n    default: document.body.clientWidth < 992 ? 5 : 7\n  },\n  layout: {\n    type: String,\n    default: 'total, sizes, prev, pager, next, jumper'\n  },\n  background: {\n    type: Boolean,\n    default: true\n  },\n  autoScroll: {\n    type: Boolean,\n    default: true\n  },\n  hidden: {\n    type: Boolean,\n    default: false\n  }\n})\n\nconst emit = defineEmits(null)\nconst currentPage = computed({\n  get() {\n    return props.page\n  },\n  set(val) {\n    emit('update:page', val)\n  }\n})\nconst pageSize = computed({\n  get() {\n    return props.limit\n  },\n  set(val) {\n    emit('update:limit', val)\n  }\n})\nfunction handleSizeChange(val) {\n  if (currentPage.value * val > props.total) {\n    currentPage.value = 1\n  }\n  emit('pagination', { page: currentPage.value, limit: val })\n  if (props.autoScroll) {\n    scrollTo(0, 800)\n  }\n}\nfunction handleCurrentChange(val) {\n  emit('pagination', { page: val, limit: pageSize.value })\n  if (props.autoScroll) {\n    scrollTo(0, 800)\n  }\n}\n</script>\n\n<style scoped>\n.pagination-container {\n  background: #fff;\n  padding: 32px 16px;\n}\n.pagination-container.hidden {\n  display: none;\n}\n</style>\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/components/ParentView/index.vue",
    "content": "<template>\n  <router-view />\n</template>\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/components/Screenfull/index.vue",
    "content": "<template>\n  <div>\n    <svg-icon\n      :icon-name=\"isFullscreen ? 'exit-fullscreen' : 'fullscreen'\"\n      @click=\"toggle\"\n    />\n  </div>\n</template>\n\n<script setup>\nimport { useFullscreen } from '@vueuse/core'\n\nconst { isFullscreen, toggle } = useFullscreen()\n</script>\n\n<style lang=\"scss\" scoped>\n.screenfull-svg {\n  display: inline-block;\n  cursor: pointer;\n  fill: #5a5e66;\n  width: 20px;\n  height: 20px;\n  vertical-align: 10px;\n}\n</style>\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/components/SiteCard/index.vue",
    "content": "<template>\n  <el-tooltip\n    :content=\"site.siteDescription\"\n    :visible-arrow=\"false\"\n    :disabled=\"\n      site.siteDescription == undefined ||\n      site.siteDescription === '' ||\n      disabledTip ||\n      !showSiteDescription\n    \"\n    placement=\"bottom\"\n    effect=\"light\"\n  >\n    <el-card shadow=\"hover\" class=\"site-card\" @click=\"handleSiteCard\">\n      <el-container>\n        <el-aside class=\"site-card-aside\">\n          <el-image\n            loading=\"lazy\"\n            class=\"site-card-logo\"\n            :src=\"getPictureShowUrl(site.sitePath)\"\n          >\n            <template #error>\n              <el-image\n                loading=\"lazy\"\n                class=\"site-card-logo\"\n                :src=\"getPictureShowUrl(defaultSiteImage)\"\n              />\n            </template>\n          </el-image>\n        </el-aside>\n        <el-main class=\"site-card-main\">\n          <a\n            class=\"site-title\"\n            :class=\"showSiteDescription ? '' : 'site-title-no-description'\"\n            ><strong>{{ site.siteName }}</strong></a\n          >\n          <div class=\"site-description\" v-if=\"showSiteDescription\">\n            {{ site.siteDescription }}\n          </div>\n        </el-main>\n        <slot name=\"container-footer\"></slot>\n      </el-container>\n      <slot name=\"card-footer\"></slot>\n    </el-card>\n  </el-tooltip>\n</template>\n<script setup>\nimport { computed, ref } from 'vue'\nimport { useStore } from 'vuex'\n\nimport { updateClickCount } from '@/api/nav/site'\nimport { openSite } from '@/utils/geshanzsq'\nconst props = defineProps({\n  // 网站信息\n  site: {\n    type: Object\n  },\n  // 改变标题颜色\n  changeTitleColor: {\n    type: Boolean,\n    default: true\n  },\n  // 是否打开网站\n  openSite: {\n    type: Boolean,\n    default: true\n  },\n  // 是否开启描述提示\n  disabledTip: {\n    type: Boolean,\n    default: true\n  }\n})\n\nconst store = useStore()\n\n// 默认图片\nconst defaultSiteImage = ref('/profile/site/system/logo.jpg')\nconst showSiteDescription = computed(\n  () => store.getters['settings/showSiteDescription']\n)\n\n/**\n * 网站卡片点击事件\n */\nfunction handleSiteCard() {\n  if (!props.openSite) {\n    return\n  }\n  openSite(props.site.siteUrl)\n  updateClickCount(props.site.id)\n}\n</script>\n<style lang=\"scss\" scoped>\n.site-card {\n  cursor: pointer;\n  .site-card-aside {\n    background-color: transparent;\n    width: 40px;\n    overflow: hidden;\n    padding: 0px;\n    margin: 0px;\n    text-align: center;\n    .site-card-logo {\n      border-radius: 50%;\n      width: 40px;\n      height: 40px;\n    }\n  }\n  .site-card-main {\n    padding: 0;\n    margin-left: 5px;\n\n    .site-title {\n      display: -webkit-box;\n      -webkit-box-orient: vertical;\n      -webkit-line-clamp: 1;\n      overflow: hidden;\n      font-size: 15px;\n      color: #373e4a;\n    }\n    .site-title-no-description {\n      padding-top: 10px;\n    }\n    .site-description {\n      display: -webkit-box;\n      -webkit-box-orient: vertical;\n      -webkit-line-clamp: 2;\n      overflow: hidden;\n      margin-top: 5px;\n      font-size: 14px;\n      height: 39px;\n      color: #979898;\n    }\n  }\n}\n</style>\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/components/SizeSelect/index.vue",
    "content": "<template>\n  <div>\n    <el-dropdown trigger=\"click\" @command=\"handleSetSize\">\n      <div class=\"size-icon--style\">\n        <svg-icon class-name=\"size-icon\" icon-name=\"size\" />\n      </div>\n      <template #dropdown>\n        <el-dropdown-menu>\n          <el-dropdown-item\n            v-for=\"item of sizeOptions\"\n            :key=\"item.value\"\n            :disabled=\"size === item.value\"\n            :command=\"item.value\"\n          >\n            {{ item.label }}\n          </el-dropdown-item>\n        </el-dropdown-menu>\n      </template>\n    </el-dropdown>\n  </div>\n</template>\n\n<script setup>\nimport { computed, getCurrentInstance, ref } from 'vue'\nimport { useStore } from 'vuex'\n\nconst store = useStore()\nconst size = computed(() => store.getters['app/size'])\nconst { proxy } = getCurrentInstance()\nconst sizeOptions = ref([\n  { label: '较大', value: 'large' },\n  { label: '默认', value: 'default' },\n  { label: '稍小', value: 'small' }\n])\n\nfunction handleSetSize(size) {\n  proxy.$modal.loading('正在设置布局大小，请稍候...')\n  store.dispatch('app/setSize', size)\n  setTimeout(() => {\n    window.location.reload()\n  }, 1000)\n}\n</script>\n\n<style lang=\"scss\" scoped>\n.size-icon--style {\n  font-size: 18px;\n  line-height: 50px;\n  padding-right: 7px;\n}\n</style>\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/components/SvgIcon/index.vue",
    "content": "<template>\n  <div\n    v-if=\"hasExternal\"\n    :style=\"styleExternalIcon\"\n    class=\"svg-external-icon svg-icon\"\n  />\n  <svg v-else :class=\"svgClass\" aria-hidden=\"true\">\n    <use :xlink:href=\"internalIconName\" />\n  </svg>\n</template>\n<script setup>\nimport { computed } from 'vue'\nimport { isExternal } from '@/utils/validate'\nconst props = defineProps({\n  // icon 图标\n  iconName: {\n    type: String,\n    required: true\n  },\n  // 图标类名\n  className: {\n    type: String,\n    default: ''\n  }\n})\n\n/**\n * 判断是否为外部资源\n */\nconst hasExternal = computed(() => isExternal(props.iconName))\n\n/**\n * 外部图标样式\n */\nconst styleExternalIcon = computed(() => ({\n  mask: `url(${props.iconName}) no-repeat 50% 50%`,\n  '-webkit-mask': `url(${props.iconName}) no-repeat 50% 50%`\n}))\n\n/**\n * 内部图标\n */\nconst internalIconName = computed(() => `#icon-${props.iconName}`)\n\n/**\n * 样式设置\n */\nconst svgClass = computed(() =>\n  props.className ? 'svg-icon ' + props.className : 'svg-icon'\n)\n</script>\n\n<style scoped>\n.svg-icon {\n  width: 1em;\n  height: 1em;\n  vertical-align: -0.15em;\n  fill: currentColor;\n  overflow: hidden;\n}\n\n.svg-external-icon {\n  background-color: currentColor;\n  mask-size: cover !important;\n  display: inline-block;\n}\n</style>\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/components/SvgIcon/svgicon.js",
    "content": "import * as components from '@element-plus/icons-vue'\n\nexport default {\n  install: (app) => {\n    for (const key in components) {\n      const componentConfig = components[key]\n      app.component(componentConfig.name, componentConfig)\n    }\n  }\n}\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/components/TopNav/index.vue",
    "content": "<template>\n  <el-menu\n    :default-active=\"activeMenu\"\n    mode=\"horizontal\"\n    @select=\"handleSelect\"\n  >\n    <template v-for=\"(item, index) in topMenus\">\n      <el-menu-item\n        :style=\"{ '--theme': theme }\"\n        :index=\"item.path\"\n        :key=\"index\"\n        v-if=\"index < visibleNumber\"\n        ><svg-icon :icon-name=\"item.meta.icon\" />\n        {{ item.meta.title }}</el-menu-item\n      >\n    </template>\n\n    <!-- 顶部菜单超出数量折叠 -->\n    <el-sub-menu\n      :style=\"{ '--theme': theme }\"\n      index=\"more\"\n      v-if=\"topMenus.length > visibleNumber\"\n    >\n      <template #title>更多菜单</template>\n      <template v-for=\"(item, index) in topMenus\">\n        <el-menu-item\n          :index=\"item.path\"\n          :key=\"index\"\n          v-if=\"index >= visibleNumber\"\n          ><svg-icon :icon-name=\"item.meta.icon\" />\n          {{ item.meta.title }}</el-menu-item\n        >\n      </template>\n    </el-sub-menu>\n  </el-menu>\n</template>\n\n<script setup>\nimport { constantRoutes } from '@/router'\nimport { isHttp } from '@/utils/validate'\nimport { computed, onBeforeUnmount, onMounted, ref } from 'vue'\nimport { useRoute, useRouter } from 'vue-router'\nimport { useStore } from 'vuex'\n\n// 顶部栏初始数\nconst visibleNumber = ref(null)\n// 当前激活菜单的 index\nconst currentIndex = ref(null)\n// 隐藏侧边栏路由\nconst hideList = ['/index', '/user/profile']\n\nconst store = useStore()\nconst route = useRoute()\nconst router = useRouter()\n\n// 主题颜色\nconst theme = computed(() => store.getters['settings/theme'])\n// 所有的路由信息\nconst routers = computed(() => store.getters['permission/topbarRouters'])\n\n// 顶部显示菜单\nconst topMenus = computed(() => {\n  const topMenus = []\n  routers.value.forEach((menu) => {\n    if (menu.hidden !== true) {\n      // 兼容顶部栏一级菜单内部跳转\n      if (menu.path === '/') {\n        topMenus.push(menu.children[0])\n      } else {\n        topMenus.push(menu)\n      }\n    }\n  })\n  return topMenus\n})\n\n// 设置子路由\nconst childrenMenus = computed(() => {\n  const childrenMenus = []\n  routers.value.forEach((router) => {\n    for (const item in router.children) {\n      if (router.children[item].parentPath === undefined) {\n        if (router.path === '/') {\n          router.children[item].path = '/' + router.children[item].path\n        } else {\n          if (!isHttp(router.children[item].path)) {\n            router.children[item].path =\n              router.path + '/' + router.children[item].path\n          }\n        }\n        router.children[item].parentPath = router.path\n      }\n      childrenMenus.push(router.children[item])\n    }\n  })\n  return constantRoutes.concat(childrenMenus)\n})\n\n// 默认激活的菜单\nconst activeMenu = computed(() => {\n  const path = route.path\n  let activePath = path\n  if (\n    path !== undefined &&\n    path.lastIndexOf('/') > 0 &&\n    hideList.indexOf(path) === -1\n  ) {\n    const tmpPath = path.substring(1, path.length)\n    activePath = '/' + tmpPath.substring(0, tmpPath.indexOf('/'))\n    store.dispatch('app/toggleSideBarHide', false)\n  } else if (!route.children) {\n    activePath = path\n    store.dispatch('app/toggleSideBarHide', true)\n  }\n  activeRoutes(activePath)\n  return activePath\n})\n\nfunction setVisibleNumber() {\n  const width = document.body.getBoundingClientRect().width / 3\n  visibleNumber.value = parseInt(width / 85)\n}\n\nfunction handleSelect(key, keyPath) {\n  currentIndex.value = key\n  const route = routers.value.find((item) => item.path === key)\n  if (isHttp(key)) {\n    // http(s):// 路径新窗口打开\n    window.open(key, '_blank')\n  } else if (!route || !route.children) {\n    // 没有子路由路径内部打开\n    router.push({ path: key })\n    store.dispatch('app/toggleSideBarHide', true)\n  } else {\n    // 显示左侧联动菜单\n    activeRoutes(key)\n    store.dispatch('app/toggleSideBarHide', false)\n  }\n}\n\nfunction activeRoutes(key) {\n  const routes = []\n  if (childrenMenus.value && childrenMenus.value.length > 0) {\n    childrenMenus.value.forEach((item) => {\n      if (key === item.parentPath || (key === 'index' && item.path === '')) {\n        routes.push(item)\n      }\n    })\n  }\n  if (routes.length > 0) {\n    store.commit('permission/setSidebarRouters', routes)\n  }\n  return routes\n}\n\nonMounted(() => {\n  window.addEventListener('resize', setVisibleNumber)\n})\nonBeforeUnmount(() => {\n  window.removeEventListener('resize', setVisibleNumber)\n})\n\nonMounted(() => {\n  setVisibleNumber()\n})\n</script>\n\n<style lang=\"scss\">\n.topmenu-container.el-menu--horizontal > .el-menu-item {\n  float: left;\n  height: 50px !important;\n  line-height: 50px !important;\n  color: #999093 !important;\n  padding: 0 5px !important;\n  margin: 0 10px !important;\n}\n\n.topmenu-container.el-menu--horizontal > .el-menu-item.is-active,\n.el-menu--horizontal > .el-sub-menu.is-active .el-submenu__title {\n  border-bottom: 2px solid #{'var(--theme)'} !important;\n  color: #303133;\n}\n\n/* sub-menu item */\n.topmenu-container.el-menu--horizontal > .el-sub-menu .el-sub-menu__title {\n  float: left;\n  height: 50px !important;\n  line-height: 50px !important;\n  color: #999093 !important;\n  padding: 0 5px !important;\n  margin: 0 10px !important;\n}\n</style>\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/components/WangEditor/index.vue",
    "content": "<template>\n  <div class=\"wang-editor-container\">\n    <Toolbar\n      class=\"wang-toolbar\"\n      :editor=\"editorRef\"\n      :defaultConfig=\"toolbarConfig\"\n      :mode=\"mode\"\n    />\n    <Editor\n      class=\"wang-content\"\n      v-model=\"content\"\n      :defaultConfig=\"editorConfig\"\n      :mode=\"mode\"\n      @onCreated=\"handleCreated\"\n    />\n  </div>\n</template>\n<script setup>\nimport '@wangeditor/editor/dist/css/style.css' // 引入 css\n\nimport { computed, onBeforeUnmount, ref, shallowRef } from 'vue'\nimport { Editor, Toolbar } from '@wangeditor/editor-for-vue'\nimport { getToken } from '@/utils/auth'\nimport { tokenConfig } from '@/config/network.config'\nimport { getPictureShowUrl } from '@/utils/geshanzsq'\nconst props = defineProps({\n  // 内容\n  modelValue: {\n    type: String,\n    default: ''\n  }\n})\n\n// 编辑器实例，必须用 shallowRef\nconst editorRef = shallowRef()\n\n// 内容\nconst emit = defineEmits(null)\nconst content = computed({\n  get() {\n    return props.modelValue\n  },\n  set(value) {\n    // 子组件传值给父组件\n    emit('update:modelValue', value)\n  }\n})\n\nconst toolbarConfig = ref({})\nconst editorConfig = ref({\n  placeholder: '请输入内容...',\n  MENU_CONF: {\n    uploadImage: {\n      server: process.env.VUE_APP_BASE_API + '/nav/picture/upload',\n      fieldName: 'file',\n      headers: {\n        Authorization: tokenConfig.prefix + getToken()\n      },\n      // 由于上传成功后格式不符合编辑器要求，所以自定义实现\n      customInsert(res, insertFn) {\n        const {\n          code,\n          data: { fileName, filePath }\n        } = res\n        if (code === 200) {\n          const url = getPictureShowUrl(filePath)\n          insertFn(url, fileName, url)\n        }\n      }\n    }\n  }\n})\n\n// 组件销毁时，也及时销毁编辑器\nonBeforeUnmount(() => {\n  const editor = editorRef.value\n  if (editor == null) return\n  editor.destroy()\n})\n\nconst handleCreated = (editor) => {\n  // 记录 editor 实例，重要！\n  editorRef.value = editor\n}\n\n/**\n * 插入图片\n */\nfunction insertPicture(url) {\n  const src = getPictureShowUrl(url)\n  editorRef.value.dangerouslyInsertHtml(`<img src=\"${src}\">`)\n}\n\ndefineExpose({\n  insertPicture\n})\n</script>\n<style scoped lang=\"scss\">\n.wang-editor-container {\n  width: 100%;\n  border: 1px solid #ccc;\n\n  &.w-e-full-screen-container {\n    z-index: 9999 !important;\n  }\n\n  .wang-toolbar {\n    border-bottom: 1px solid #ccc;\n  }\n  .wang-content {\n    height: 400px !important;\n    overflow-y: hidden;\n  }\n}\n</style>\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/components/Weather/index.vue",
    "content": "<template>\n  <div v-if=\"show\">\n    <iframe\n      scrolling=\"no\"\n      src=\"https://tianqiapi.com/api.php?style=tm&skin=pitaya\"\n      frameborder=\"0\"\n      width=\"270\"\n      height=\"24\"\n      allowtransparency=\"true\"\n    ></iframe>\n  </div>\n</template>\n<script setup>\ndefineProps({\n  show: {\n    type: Boolean,\n    default: true\n  }\n})\n</script>\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/config/index.js",
    "content": "/**\n * 子配置导出\n */\nconst setting = require('./setting.config')\nconst network = require('./network.config')\nconst themeCli = require('./theme.config')\nconst vueCli = require('./vue.cli.config')\nmodule.exports = {\n  ...setting,\n  ...network,\n  ...themeCli,\n  ...vueCli\n}\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/config/network.config.js",
    "content": "/**\n * 网络配置\n */\nmodule.exports = {\n  // 配后端数据的接收方式application/json;charset=UTF-8 或 application/x-www-form-urlencoded;charset=UTF-8\n  contentType: 'application/json;charset=UTF-8',\n  // 最长请求时间\n  requestTimeout: 10000,\n  // 操作正常code，支持String、Array、int多种类型\n  successCode: [200, 0, '200', '0'],\n  // 数据状态的字段名称\n  statusName: 'code',\n  // 状态信息的字段名称\n  messageName: 'message',\n  // token 令牌配置\n  tokenConfig: {\n    // 自定义令牌标识\n    header: 'Authorization',\n    // 令牌前缀\n    prefix: 'Bearer ',\n    // 过期时间，单位为天数\n    expireTime: 30\n  },\n  // 错误码内容提示\n  errorCode: {\n    401: '认证失败，无法访问系统资源',\n    403: '当前操作没有权限',\n    404: '访问资源不存在'\n  }\n}\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/config/setting.config.js",
    "content": "/**\n * 导出通用配置\n */\nmodule.exports = {\n  // 标题，此项修改后需要重启项目\n  title: '格姗导航',\n  // 网站标题\n  siteTitle: '格姗导航 - 可自定义的简洁导航网站',\n  // 网站加载时显示的内容\n  siteLoading: '正在为您加载系统资源，请耐心等待',\n  // 网站描述\n  siteDescription:\n    '格姗导航(gesdh.cn)是一个可自定义、简洁又好用的导航网站，国内屈指可数的自定义导航，用户可以自定义导航、布局设置等。收录大量各类优秀网站，包含热门导航、格式转换、图片素材、PPT资源、桌面壁纸、资源搜索、在线工具、素材资源、自媒体等。网站大全就选格姗导航！',\n  // 网站关键字\n  siteKeywords:\n    '格姗导航,导航,导航网站,简洁,简洁导航,好用的导航网站,网站大全,格姗,知识圈,自定义导航',\n  // 白名单配置，不用令牌就能访问\n  whileList: ['/', '/login', '/about'],\n  // 超级管理员\n  superAdmin: 'superAdmin',\n  // 所有权限\n  allPermission: '*:*:*',\n  // 备案号\n  recordTitle: ''\n}\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/config/theme.config.js",
    "content": "/**\n * @description 主题配置\n */\nmodule.exports = {\n  /**\n   * 侧边栏主题 深色主题theme-dark，浅色主题theme-light\n   */\n  sideTheme: 'theme-dark',\n  /**\n   * 是否系统布局配置\n   */\n  showSettings: false,\n\n  /**\n   * 是否显示 tagsView\n   */\n  tagsView: true,\n\n  /**\n   * 是否固定头部\n   */\n  fixedHeader: true,\n\n  /**\n   * 是否显示logo\n   */\n  sidebarLogo: true,\n\n  /**\n   * 是否显示动态标题\n   */\n  dynamicTitle: true,\n\n  /**\n   * 显示搜索\n   */\n  searchOpen: true,\n\n  /**\n   * 显示天气\n   */\n  weatherOpen: true,\n\n  /**\n   * 是否显示网站描述\n   */\n  showSiteDescription: true,\n\n  /**\n   * 每行显示数量\n   */\n  showCount: 4,\n\n  /**\n   * 百度统计代码链接\n   */\n  baiduStatisticsUrl: '',\n\n  /**\n   * @type {string | array} 'production' | ['production', 'development']\n   * @description Need show err logs component.\n   * The default is only used in the production env\n   * If you want to also use it in dev, you can pass ['production', 'development']\n   */\n  errorLog: 'production'\n}\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/config/vue.cli.config.js",
    "content": "/**\n * vue/cli配置，以下任意一个配置修改都需要重启项目\n */\nmodule.exports = {\n  // 开发环境端口号\n  devPort: 8023,\n  // 开发环境，启动完成后是否自动打开浏览器\n  devOpenBrowser: false,\n  // 是否开启 Gzip 打包\n  buildGzip: true\n}\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/directive/index.js",
    "content": "import hasRole from './permission/hasRole'\nimport hasPermission from './permission/hasPermission'\n\nexport default function directive(app) {\n  app.directive('role', hasRole)\n  app.directive('permission', hasPermission)\n}\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/directive/permission/hasPermission.js",
    "content": "/**\n * v-permission 操作权限处理\n */\nimport store from '@/store'\n\nexport default {\n  mounted(el, binding, vnode) {\n    const { value } = binding\n    const allPermission = '*:*:*'\n    const permissions = store.getters['user/permissionCodes']\n\n    if (value && value instanceof Array && value.length > 0) {\n      const permissionFlag = value\n\n      const hasPermissions = permissions.some((permission) => {\n        return (\n          allPermission === permission || permissionFlag.includes(permission)\n        )\n      })\n\n      if (!hasPermissions) {\n        el.parentNode && el.parentNode.removeChild(el)\n      }\n    } else {\n      throw new Error('请设置操作权限标签值')\n    }\n  }\n}\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/directive/permission/hasRole.js",
    "content": "/**\n * v-hasRole 角色权限处理\n */\n\nimport store from '@/store'\nimport { superAdmin } from '@/config/setting.config'\n\nexport default {\n  mounted(el, binding, vnode) {\n    const { value } = binding\n    const roles = store.getters['user/roleCodes']\n\n    if (value && value instanceof Array && value.length > 0) {\n      const roleFlag = value\n      const hasRole = roles.some((role) => {\n        return superAdmin === role || roleFlag.includes(role)\n      })\n\n      if (!hasRole) {\n        el.parentNode && el.parentNode.removeChild(el)\n      }\n    } else {\n      throw new Error('请设置角色权限标签值\"')\n    }\n  }\n}\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/layout/client/components/FooterBottom/index.vue",
    "content": "<template>\n  <div>\n    <el-divider></el-divider>\n    <div class=\"footer-beian\">\n      Copyright &copy; 2020 <a href=\"/\">{{ title }}</a\n      >.保留所有权利\n      <a href=\"http://beian.miit.gov.cn/\" target=\"_blank\">\n        {{ recordTitle }}\n      </a>\n    </div>\n  </div>\n</template>\n<script setup>\nimport { title, recordTitle } from '@/config/setting.config'\n</script>\n\n<style scoped lang=\"scss\">\n.footer-beian {\n  text-align: center;\n  font-size: 13px;\n  color: #a4a4a4;\n  padding-bottom: 20px;\n}\n</style>\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/layout/client/components/Navbar/index.vue",
    "content": "<template>\n  <div class=\"navbar\">\n    <hamburger\n      id=\"hamburger-container\"\n      :is-active=\"sidebar.opened\"\n      class=\"hamburger-container\"\n      @toggleClick=\"toggleSideBar\"\n    />\n\n    <weather v-if=\"weatherOpen && !isMobile\" class=\"navbar-weather\" />\n\n    <div class=\"right-menu\">\n      <a href=\"https://gitee.com/geshanzsq/geshanzsq-nav\" target=\"_blank\">\n        <svg-icon icon-name=\"gitee\" class=\"source-code\" />\n      </a>\n      <a href=\"https://github.com/geshanzsq/geshanzsq-nav\" target=\"_blank\">\n        <svg-icon icon-name=\"github\" class=\"source-code\" />\n      </a>\n    </div>\n  </div>\n</template>\n\n<script setup>\nimport { useStore } from 'vuex'\nimport { computed } from 'vue'\n\nimport Hamburger from '@/components/Hamburger'\nimport Weather from '@/components/Weather'\n\nconst store = useStore()\nconst sidebar = computed(() => store.getters['app/sidebar'])\nconst weatherOpen = computed(() => store.getters['settings/weatherOpen'])\n\nfunction toggleSideBar() {\n  store.dispatch('app/toggleSideBar')\n}\n</script>\n\n<style lang=\"scss\" scoped>\n.navbar {\n  height: 50px;\n  overflow: hidden;\n  position: relative;\n  background: #fff;\n  box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);\n\n  .hamburger-container {\n    line-height: 46px;\n    height: 100%;\n    float: left;\n    cursor: pointer;\n    transition: background 0.3s;\n    -webkit-tap-highlight-color: transparent;\n\n    &:hover {\n      background: rgba(0, 0, 0, 0.025);\n    }\n  }\n\n  .navbar-weather {\n    float: left;\n    margin: 15px;\n  }\n\n  .right-menu {\n    float: right;\n    height: 100%;\n    line-height: 50px;\n    display: flex;\n\n    .source-code {\n      margin-right: 10px;\n      display: inline-block;\n      font-size: 30px;\n      height: 100%;\n      vertical-align: text-bottom;\n    }\n  }\n}\n</style>\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/layout/client/components/Search/index.vue",
    "content": "<template>\n  <div class=\"search\">\n    <el-tabs type=\"border-card\" v-model=\"searchType\">\n      <el-tab-pane label=\"百度\" :name=\"baidu.value\">\n        <el-autocomplete\n          v-model=\"content\"\n          placeholder=\"百度一下\"\n          prefix-icon=\"Search\"\n          style=\"width: 100%\"\n          size=\"large\"\n          class=\"search-input\"\n          @select=\"handleSearchUrl(baidu.searchUrl)\"\n          @keyup.enter=\"handleSearchUrl(baidu.searchUrl)\"\n          :fetch-suggestions=\"handleSearchData\"\n        >\n          <template #append>\n            <el-button type=\"primary\" @click=\"handleSearchUrl(baidu.searchUrl)\"\n              >搜索</el-button\n            >\n          </template>\n        </el-autocomplete>\n      </el-tab-pane>\n      <el-tab-pane label=\"Bing\" :name=\"bing.value\">\n        <el-autocomplete\n          v-model=\"content\"\n          placeholder=\"微软 Bing 搜索\"\n          prefix-icon=\"Search\"\n          style=\"width: 100%\"\n          size=\"large\"\n          class=\"search-input\"\n          @select=\"handleSearchUrl(bing.searchUrl)\"\n          @keyup.enter=\"handleSearchUrl(bing.searchUrl)\"\n          :fetch-suggestions=\"handleSearchData\"\n        >\n          <template #append>\n            <el-button type=\"primary\" @click=\"handleSearchUrl(bing.searchUrl)\"\n              >搜索</el-button\n            >\n          </template>\n        </el-autocomplete>\n      </el-tab-pane>\n      <el-tab-pane :name=\"google.value\">\n        <template #label>\n          <span style=\"color: #3b83fa\">G</span>\n          <span style=\"color: #f3442c\">o</span>\n          <span style=\"color: #ffc300\">o</span>\n          <span style=\"color: #4696f8\">g</span>\n          <span style=\"color: #2cab4e\">l</span>\n          <span style=\"color: #f54231\">e</span>\n        </template>\n        <el-autocomplete\n          v-model=\"content\"\n          placeholder=\"Google 搜索\"\n          prefix-icon=\"Search\"\n          style=\"width: 100%\"\n          size=\"large\"\n          class=\"search-input\"\n          @select=\"handleSearchUrl(google.searchUrl)\"\n          @keyup.enter=\"handleSearchUrl(google.searchUrl)\"\n          :fetch-suggestions=\"handleSearchData\"\n        >\n          <template #append>\n            <el-button type=\"primary\" @click=\"handleSearchUrl(google.searchUrl)\"\n              >搜索</el-button\n            >\n          </template>\n        </el-autocomplete>\n      </el-tab-pane>\n      <el-tab-pane :name=\"baiduDev.value\">\n        <template #label>\n          <span style=\"color: #19b955\">开发者</span>\n        </template>\n        <el-autocomplete\n          v-model=\"content\"\n          placeholder=\"百度开发者搜索，无任何广告\"\n          prefix-icon=\"Search\"\n          style=\"width: 100%\"\n          size=\"large\"\n          class=\"search-input\"\n          @select=\"handleSearchUrl(baiduDev.searchUrl)\"\n          @keyup.enter=\"handleSearchUrl(baiduDev.searchUrl)\"\n          :fetch-suggestions=\"handleSearchData\"\n        >\n          <template #append>\n            <el-button\n              type=\"primary\"\n              @click=\"handleSearchUrl(baiduDev.searchUrl)\"\n              >搜索</el-button\n            >\n          </template>\n        </el-autocomplete>\n      </el-tab-pane>\n      <el-tab-pane :name=\"magi.value\">\n        <template #label>\n          <span style=\"color: #19b955\">Magi</span>\n        </template>\n        <el-autocomplete\n          v-model=\"content\"\n          placeholder=\"Magi 搜索\"\n          prefix-icon=\"Search\"\n          style=\"width: 100%\"\n          size=\"large\"\n          class=\"search-input\"\n          @select=\"handleSearchUrl(magi.searchUrl)\"\n          @keyup.enter=\"handleSearchUrl(magi.searchUrl)\"\n          :fetch-suggestions=\"handleSearchData\"\n        >\n          <template #append>\n            <el-button type=\"primary\" @click=\"handleSearchUrl(magi.searchUrl)\"\n              >搜索</el-button\n            >\n          </template>\n        </el-autocomplete>\n      </el-tab-pane>\n      <el-tab-pane :name=\"haosou.value\">\n        <template #label>\n          <span style=\"color: #19b955\">360</span>\n        </template>\n        <el-autocomplete\n          v-model=\"content\"\n          placeholder=\"360 好搜\"\n          prefix-icon=\"Search\"\n          style=\"width: 100%\"\n          size=\"large\"\n          class=\"search-input\"\n          @select=\"handleSearchUrl(haosou.searchUrl)\"\n          @keyup.enter=\"handleSearchUrl(haosou.searchUrl)\"\n          :fetch-suggestions=\"handleSearchData\"\n        >\n          <template #append>\n            <el-button type=\"primary\" @click=\"handleSearchUrl(haosou.searchUrl)\"\n              >搜索</el-button\n            >\n          </template>\n        </el-autocomplete>\n      </el-tab-pane>\n      <el-tab-pane :name=\"sougou.value\">\n        <template #label>\n          <span style=\"color: #ff5943\">搜狗</span>\n        </template>\n        <el-autocomplete\n          v-model=\"content\"\n          placeholder=\"搜狗搜索\"\n          prefix-icon=\"Search\"\n          style=\"width: 100%\"\n          size=\"large\"\n          class=\"search-input\"\n          @select=\"handleSearchUrl(sougou.searchUrl)\"\n          @keyup.enter=\"handleSearchUrl(sougou.searchUrl)\"\n          :fetch-suggestions=\"handleSearchData\"\n        >\n          <template #append>\n            <el-button type=\"primary\" @click=\"handleSearchUrl(sougou.searchUrl)\"\n              >搜索</el-button\n            >\n          </template>\n        </el-autocomplete>\n      </el-tab-pane>\n      <el-tab-pane label=\"多吉\" :name=\"doge.value\">\n        <el-autocomplete\n          v-model=\"content\"\n          placeholder=\"DogeDoge 检索\"\n          prefix-icon=\"Search\"\n          style=\"width: 100%\"\n          size=\"large\"\n          class=\"search-input\"\n          @select=\"handleSearchUrl(doge.searchUrl)\"\n          @keyup.enter=\"handleSearchUrl(doge.searchUrl)\"\n          :fetch-suggestions=\"handleSearchData\"\n        >\n          <template #append>\n            <el-button type=\"primary\" @click=\"handleSearchUrl(doge.searchUrl)\"\n              >搜索</el-button\n            >\n          </template>\n        </el-autocomplete>\n      </el-tab-pane>\n      <el-tab-pane label=\"站内\" name=\"site\">\n        <el-autocomplete\n          v-model=\"content\"\n          placeholder=\"站内搜索，支持名称和描述\"\n          prefix-icon=\"Search\"\n          style=\"width: 100%\"\n          size=\"large\"\n          class=\"search-input\"\n          @select=\"handleSelectSearchSiteUrl\"\n          @keyup.enter=\"handleSearchUrl('/search/')\"\n          :fetch-suggestions=\"handleSearchSiteData\"\n        >\n          <template #append>\n            <el-button type=\"primary\" @click=\"handleSearchUrl('/search/')\"\n              >搜索</el-button\n            >\n          </template>\n        </el-autocomplete>\n      </el-tab-pane>\n    </el-tabs>\n  </div>\n</template>\n<script setup>\nimport { getCurrentInstance, ref } from 'vue'\nimport jsonp from 'jsonp'\n\nimport { siteList } from '@/api/client/search'\nimport { updateClickCount } from '@/api/nav/site'\nimport { openSite } from '@/utils/geshanzsq'\n\nconst { proxy } = getCurrentInstance()\n\nconst searchType = ref('baidu')\nconst content = ref()\nconst options = ref({\n  timeout: 10000,\n  name: 'searchCallback'\n})\n\nconst baidu = ref({\n  value: 'baidu',\n  searchUrl: 'https://www.baidu.com/s?wd=',\n  queryUrl: 'https://sp0.baidu.com/5a1Fazu8AA54nxGko9WTAnF6hhy/su?wd='\n})\nconst bing = ref({\n  value: 'bing',\n  searchUrl: 'https://cn.bing.com/search?q=',\n  queryUrl: 'https://api.bing.com/qsonhs.aspx?type=cb&q='\n})\nconst google = ref({\n  value: 'google',\n  searchUrl: 'https://www.google.com/search?q=',\n  queryUrl: 'https://www.google.com/complete/search?client=youtube&q='\n})\n// 未实现\nconst baiduDev = ref({\n  value: 'baiduDev',\n  searchUrl: 'https://kaifa.baidu.com/searchPage?wd='\n  // queryUrl: 'https://kaifa.baidu.com/rest/v1/recommend/suggests?wd='\n})\n// 未实现\nconst magi = ref({\n  value: 'magi',\n  searchUrl: 'https://magi.com/search?q='\n  // queryUrl: 'https://magi.com/suggest?q='\n})\nconst haosou = ref({\n  value: 'haosou',\n  searchUrl: 'https://www.so.com/s?q=',\n  queryUrl: 'https://sug.so.360.cn/suggest?word='\n})\nconst sougou = ref({\n  value: 'sougou',\n  searchUrl: 'https://www.sogou.com/web?query='\n  // queryUrl: 'https://www.sogou.com/suggnew/ajajjson?type=web&key='\n})\n// 未实现\nconst doge = ref({\n  value: 'doge',\n  searchUrl: 'https://www.dogedoge.com/results?q='\n  // queryUrl: 'https://www.dogedoge.com/sugg/'\n})\n\n/**\n * 获取搜索推荐的内容\n */\nfunction handleSearchData(searchContent, cb) {\n  if (searchContent === '') {\n    const result = []\n    cb(result)\n    return\n  }\n\n  let url = ''\n  if (searchType.value === doge.value.value) {\n    // 多吉搜索\n    url = doge.value.queryUrl + searchContent\n    const result = []\n    cb(result)\n    return\n  } else {\n    // 其他搜索\n    const queryUrl = getSearchDataUrl()\n    // 没有搜索提示\n    if (!queryUrl) {\n      const result = []\n      cb(result)\n      return\n    }\n    url = queryUrl + searchContent + '&cb=searchCallback'\n  }\n  jsonp(url, options.value, (responseData) => {\n    if (responseData.message === 'Timeout') {\n      const result = []\n      cb(result)\n      return\n    }\n    setSearchData(responseData, cb)\n  })\n\n  /**\n   * 回调\n   */\n  window.searchCallback = (responseData) => {\n    setSearchData(responseData, cb)\n  }\n}\n\n/**\n * 获取查询数据的地址\n */\nfunction getSearchDataUrl() {\n  switch (searchType.value) {\n    case baidu.value.value:\n      return baidu.value.queryUrl\n    case bing.value.value:\n      return bing.value.queryUrl\n    case google.value.value:\n      return google.value.queryUrl\n    case baiduDev.value.value:\n      return baiduDev.value.queryUrl\n    case magi.value.value:\n      return magi.value.queryUrl\n    case haosou.value.value:\n      return haosou.value.queryUrl\n    case sougou.value.value:\n      return sougou.value.queryUrl\n    case doge.value.value:\n      return doge.value.queryUrl\n  }\n  return ''\n}\n\n/**\n * 设置搜索推荐的内容\n */\nfunction setSearchData(responseData, cb) {\n  console.log(responseData)\n  const searchList = []\n  switch (searchType.value) {\n    // 百度\n    case baidu.value.value: {\n      responseData.s.forEach((s) => {\n        searchList.push({ value: s })\n      })\n      break\n    }\n    // 微软 Bing\n    case bing.value.value: {\n      responseData.AS.Results.forEach((r) => {\n        r.Suggests.forEach((s) => {\n          searchList.push({ value: s.Txt })\n        })\n      })\n      break\n    }\n    // google\n    case google.value.value: {\n      if (responseData.length < 2) {\n        break\n      }\n      responseData[1].forEach((s) => {\n        this.searchList.push({ value: s[0] })\n      })\n      break\n    }\n    // 360 好搜\n    case haosou.value.value: {\n      responseData.result.forEach((s) => {\n        searchList.push({ value: s.word })\n      })\n      break\n    }\n    // 搜狗\n    case sougou.value.value: {\n      if (responseData.length < 2) {\n        break\n      }\n      responseData[1].forEach((s) => {\n        searchList.push({ value: s })\n      })\n      break\n    }\n    // 多吉\n    case doge.value.value: {\n      responseData.forEach((s) => {\n        searchList.push({ value: s })\n      })\n      break\n    }\n  }\n  cb(searchList)\n}\n\n/**\n * 搜索按钮或选择事件\n */\nfunction handleSearchUrl(url) {\n  if (!content.value) {\n    proxy.$modal.msgWarning('请输入需要搜索的内容')\n    return\n  }\n  openSite(url + content.value)\n}\n\n/**\n * 点击站内搜索的网站\n */\nfunction handleSelectSearchSiteUrl(site) {\n  // 设置输入框内容为之前输入的内容，否则点击后会取 value 值\n  content.value = site.searchContent\n  openSite(site.siteUrl)\n  // 更新点击量\n  updateClickCount(site.id)\n}\n\n/**\n * 搜索站内网站\n */\nasync function handleSearchSiteData(searchContent, cb) {\n  const searchList = []\n  if (!searchContent) {\n    cb(searchList)\n    return\n  }\n  const { data } = await siteList(content.value)\n  data.forEach((site) => {\n    searchList.push({\n      id: site.id,\n      searchContent: searchContent,\n      siteUrl: site.siteUrl,\n      value:\n        '【' +\n        site.categoryName +\n        '】' +\n        site.siteName +\n        '-' +\n        site.siteDescription\n    })\n  })\n  cb(searchList)\n}\n</script>\n<style lang=\"scss\" scoped></style>\n\n<style lang=\"scss\">\n.search {\n  .search-input {\n    .el-input-group__append {\n      background-color: var(--el-color-primary);\n      color: var(--el-color-white);\n      border-radius: var(--el-border-radius-base);\n      width: 70px;\n    }\n  }\n}\n</style>\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/layout/client/components/Sidebar/Link.vue",
    "content": "<template>\n  <component :is=\"type\" v-bind=\"linkProps()\">\n    <slot />\n  </component>\n</template>\n\n<script setup>\nimport { isExternal } from '@/utils/validate'\nimport { computed } from '@vue/runtime-core'\n\nconst props = defineProps({\n  to: {\n    type: [String, Object],\n    required: true\n  }\n})\n\nconst isExt = computed(() => {\n  return isExternal(props.to)\n})\n\nconst type = computed(() => {\n  if (isExt.value) {\n    return 'a'\n  }\n  return 'router-link'\n})\n\nfunction linkProps() {\n  if (isExt.value) {\n    return {\n      href: props.to,\n      target: '_blank',\n      rel: 'noopener'\n    }\n  }\n  return {\n    to: props.to\n  }\n}\n</script>\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/layout/client/components/Sidebar/Logo.vue",
    "content": "<template>\n  <div\n    class=\"sidebar-logo-container\"\n    :class=\"{ collapse: collapse }\"\n    :style=\"{\n      backgroundColor:\n        sideTheme === 'theme-dark'\n          ? variables.menuBackground\n          : variables.menuLightBackground\n    }\"\n  >\n    <transition name=\"sidebarLogoFade\">\n      <router-link\n        v-if=\"collapse\"\n        key=\"collapse\"\n        class=\"sidebar-logo-link\"\n        to=\"/\"\n      >\n        <img v-if=\"logo\" :src=\"logo\" class=\"sidebar-logo\" />\n        <h1\n          v-else\n          class=\"sidebar-title\"\n          :style=\"{\n            color:\n              sideTheme === 'theme-dark'\n                ? variables.logoTitleColor\n                : variables.logoLightTitleColor\n          }\"\n        >\n          {{ title }}\n        </h1>\n      </router-link>\n      <router-link v-else key=\"expand\" class=\"sidebar-logo-link\" to=\"/\">\n        <img v-if=\"logo\" :src=\"logo\" class=\"sidebar-logo\" />\n        <h1\n          class=\"sidebar-title\"\n          :style=\"{\n            color:\n              sideTheme === 'theme-dark'\n                ? variables.logoTitleColor\n                : variables.logoLightTitleColor\n          }\"\n        >\n          {{ title }}\n        </h1>\n      </router-link>\n    </transition>\n  </div>\n</template>\n\n<script setup>\nimport variables from '@/assets/styles/variables.module.scss'\nimport logo from '@/assets/logo/logo.png'\nimport { useStore } from 'vuex'\nimport { computed } from '@vue/runtime-core'\nimport { title } from '@/config/setting.config'\n\ndefineProps({\n  collapse: {\n    type: Boolean,\n    required: true\n  }\n})\n\nconst store = useStore()\nconst sideTheme = computed(() => store.getters['settings/sideTheme'])\n</script>\n\n<style lang=\"scss\" scoped>\n.sidebarLogoFade-enter-active {\n  transition: opacity 1.5s;\n}\n\n.sidebarLogoFade-enter,\n.sidebarLogoFade-leave-to {\n  opacity: 0;\n}\n\n.sidebar-logo-container {\n  position: relative;\n  width: 100%;\n  height: 50px;\n  line-height: 50px;\n  background: #2b2f3a;\n  text-align: center;\n  overflow: hidden;\n\n  & .sidebar-logo-link {\n    height: 100%;\n    width: 100%;\n\n    & .sidebar-logo {\n      width: 32px;\n      height: 32px;\n      vertical-align: middle;\n      margin-right: 12px;\n    }\n\n    & .sidebar-title {\n      display: inline-block;\n      margin: 0;\n      color: #fff;\n      font-weight: 600;\n      line-height: 50px;\n      font-size: 25px;\n      font-family: Avenir, Helvetica Neue, Arial, Helvetica, sans-serif;\n      vertical-align: middle;\n    }\n  }\n\n  &.collapse {\n    .sidebar-logo {\n      margin-right: 0px;\n    }\n  }\n}\n</style>\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/layout/client/components/Sidebar/SidebarItem.vue",
    "content": "<template>\n  <div>\n    <template v-if=\"!hasChild(item)\">\n      <el-menu-item\n        :class=\"{ 'submenu-title-noDropdown': !isNest }\"\n        :index=\"item.id\"\n        @click=\"handlePosition('category-' + item.id)\"\n      >\n        <svg-icon :icon-name=\"item.categoryIcon\" />\n        <template #title\n          ><span class=\"menu-title\">{{ item.categoryName }}</span></template\n        >\n      </el-menu-item>\n    </template>\n\n    <el-sub-menu v-else ref=\"subMenu\" popper-append-to-body :index=\"item.id\">\n      <template #title>\n        <svg-icon :icon-name=\"item.categoryIcon\" />\n        <span class=\"menu-title\">{{ item.categoryName }}</span>\n      </template>\n\n      <sidebar-item\n        v-for=\"child in item.children\"\n        :key=\"child.id\"\n        :is-nest=\"true\"\n        :item=\"child\"\n        class=\"nest-menu\"\n      />\n    </el-sub-menu>\n  </div>\n</template>\n\n<script setup>\nimport { computed } from 'vue'\nimport { useRouter } from 'vue-router'\nimport { useStore } from 'vuex'\n\nimport { scrollTo } from '@/utils/scroll-to'\n\ndefineProps({\n  // route object\n  item: {\n    type: Object,\n    required: true\n  }\n})\n\nconst store = useStore()\nconst router = useRouter()\n\n// 是否移动端\nconst isMobile = computed(() => store.getters['app/isMobile'])\n// 是否固定头部\nconst fixedHeader = computed(() => store.getters['settings/fixedHeader'])\n\n/**\n * 是否有子分类\n */\nfunction hasChild(category) {\n  if (\n    category.children === null ||\n    category.children === undefined ||\n    category.children.length === 0\n  ) {\n    return false\n  }\n  return true\n}\n\n/**\n * 瞄点定位\n */\nfunction handlePosition(id) {\n  try {\n    const e = document.querySelector('#' + id)\n    const top = e.offsetTop - 60\n    const topTime = 400\n    // 移动端\n    if (isMobile.value) {\n      store.dispatch('app/closeSideBar', { withoutAnimation: false })\n      scrollTo(top, topTime)\n    } else if (!fixedHeader.value) {\n      // 不是移动端，且头部不固定时，使用这个动画比较好\n      e.scrollIntoView({ behavior: 'smooth' })\n    } else {\n      // 不是移动端并且固定时，\n      const top = e.offsetTop - 60\n      scrollTo(top, topTime)\n    }\n  } catch (e) {\n    console.error(e)\n    router.push('/')\n  }\n}\n</script>\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/layout/client/components/Sidebar/index.vue",
    "content": "<template>\n  <div\n    :class=\"{ 'has-logo': showLogo }\"\n    :style=\"{\n      backgroundColor:\n        sideTheme === 'theme-dark'\n          ? variables.menuBackground\n          : variables.menuLightBackground\n    }\"\n  >\n    <logo v-if=\"showLogo\" :collapse=\"isCollapse\" />\n    <el-scrollbar :class=\"sideTheme\" wrap-class=\"scrollbar-wrapper\">\n      <el-menu\n        :collapse=\"isCollapse\"\n        :background-color=\"\n          sideTheme === 'theme-dark'\n            ? variables.menuBackground\n            : variables.menuLightBackground\n        \"\n        :text-color=\"\n          sideTheme === 'theme-dark'\n            ? variables.menuColor\n            : variables.menuLightColor\n        \"\n        :unique-opened=\"true\"\n        :active-text-color=\"theme\"\n        :collapse-transition=\"false\"\n        mode=\"vertical\"\n      >\n        <sidebar-item\n          v-for=\"category in categories\"\n          :key=\"category.id\"\n          :item=\"category\"\n        />\n\n        <router-link to=\"/about\" target=\"_blank\">\n          <el-menu-item index=\"about\">\n            <svg-icon icon-name=\"heart\" />\n            <template #title><span class=\"menu-title\">关于本站</span></template>\n          </el-menu-item>\n        </router-link>\n      </el-menu>\n    </el-scrollbar>\n  </div>\n</template>\n\n<script setup>\nimport Logo from './Logo'\nimport SidebarItem from './SidebarItem'\nimport variables from '@/assets/styles/variables.module.scss'\nimport { useStore } from 'vuex'\nimport { computed } from '@vue/runtime-core'\n\nconst store = useStore()\n\ndefineProps({\n  categories: {\n    type: Array,\n    require: true\n  }\n})\n\nconst showLogo = computed(() => store.getters['settings/sidebarLogo'])\nconst sideTheme = computed(() => store.getters['settings/sideTheme'])\nconst theme = computed(() => store.getters['settings/theme'])\nconst sidebar = store.getters['app/sidebar']\nconst isCollapse = computed(() => !sidebar.opened)\n</script>\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/layout/client/index.vue",
    "content": "<template>\n  <div\n    :class=\"classObj\"\n    class=\"app-wrapper\"\n    :style=\"{ '--current-color': theme }\"\n  >\n    <div\n      v-if=\"device === 'mobile' && sidebarConfig.opened\"\n      class=\"drawer-bg\"\n      @click=\"handleClickOutside\"\n    />\n    <!-- 侧边栏 -->\n    <sidebar\n      v-if=\"!sidebarConfig.hide\"\n      class=\"sidebar-container\"\n      :categories=\"categories\"\n    />\n    <div :class=\"{ sidebarHide: sidebarConfig.hide }\" class=\"main-container\">\n      <div :class=\"{ 'fixed-header': fixedHeader }\">\n        <!-- 头部 -->\n        <navbar @setLayout=\"setLayout\" />\n      </div>\n      <!-- 中间内容 -->\n      <div class=\"app-main\">\n        <slot name=\"app-main\"></slot>\n        <!-- 底部友情链接 -->\n        <footer-bottom />\n      </div>\n    </div>\n\n    <!-- 右下角回到顶部 -->\n    <el-backtop :right=\"20\" :bottom=\"20\" />\n    <!-- 右下角搜索图标 -->\n    <el-backtop v-if=\"searchOpen\" :right=\"20\" :bottom=\"65\">\n      <div class=\"search-top\" @click.stop=\"handleSearch\">\n        <el-icon><Search /></el-icon>\n      </div>\n    </el-backtop>\n\n    <!-- 弹出搜索对话框 -->\n    <el-dialog\n      v-model=\"searchVisible\"\n      :width=\"isMobile ? '100%' : '60%'\"\n      center\n    >\n      <third-search />\n    </el-dialog>\n\n    <settings ref=\"settingRef\" />\n  </div>\n</template>\n<script setup>\nimport { computed, ref, watchEffect } from '@vue/runtime-core'\nimport { useWindowSize } from '@vueuse/core'\nimport { useStore } from 'vuex'\n\nimport Settings from '@/layout/components/Settings'\nimport Navbar from './components/Navbar'\nimport Sidebar from './components/Sidebar'\nimport ThirdSearch from './components/Search'\nimport FooterBottom from './components/FooterBottom'\n\ndefineProps({\n  categories: {\n    type: Array,\n    require: true\n  }\n})\n\nconst store = useStore()\n\nconst theme = computed(() => store.getters['settings/theme'])\nconst sidebarConfig = computed(() => store.getters['app/sidebar'])\nconst device = computed(() => store.getters['app/device'])\nconst fixedHeader = computed(() => store.getters['settings/fixedHeader'])\nconst classObj = computed(() => ({\n  hideSidebar: !sidebarConfig.value.opened,\n  openSidebar: sidebarConfig.value.opened,\n  withoutAnimation: sidebarConfig.value.withoutAnimation,\n  mobile: device.value === 'mobile'\n}))\nconst searchOpen = computed(() => store.getters['settings/searchOpen'])\n// 是否移动端\nconst isMobile = computed(() => store.getters['app/isMobile'])\nconst searchVisible = ref(false)\n\nconst { width } = useWindowSize()\n// 参考 Bootstrap 的响应式设计\nconst defaultWidth = 992\n\nwatchEffect(() => {\n  if (device.value === 'mobile' && sidebarConfig.value.opened) {\n    store.dispatch('app/closeSideBar', { withoutAnimation: false })\n  }\n  if (width.value - 1 < defaultWidth) {\n    store.dispatch('app/toggleDevice', 'mobile')\n    store.dispatch('app/closeSideBar', { withoutAnimation: true })\n  } else {\n    store.dispatch('app/toggleDevice', 'desktop')\n  }\n})\n\nfunction handleClickOutside() {\n  store.dispatch('app/closeSideBar', { withoutAnimation: false })\n}\n\nconst settingRef = ref(null)\nfunction setLayout() {\n  settingRef.value.openSetting()\n}\n\n/**\n * 右下角点击搜索\n */\nfunction handleSearch() {\n  searchVisible.value = true\n}\n</script>\n\n<style lang=\"scss\" scoped>\n@import '@/assets/styles/mixin.scss';\n@import '@/assets/styles/variables.module.scss';\n\n.app-wrapper {\n  @include clearfix;\n  position: relative;\n  height: 100%;\n  width: 100%;\n\n  &.mobile.openSidebar {\n    position: fixed;\n    top: 0;\n  }\n\n  .app-main {\n    /* 50= navbar  50  */\n    min-height: calc(100vh - 50px);\n    width: 100%;\n    position: relative;\n    overflow: hidden;\n  }\n\n  .fixed-header + .app-main {\n    padding-top: 50px;\n  }\n}\n\n.drawer-bg {\n  background: #000;\n  opacity: 0.3;\n  width: 100%;\n  top: 0;\n  height: 100%;\n  position: absolute;\n  z-index: 999;\n}\n\n.fixed-header {\n  position: fixed;\n  top: 0;\n  right: 0;\n  z-index: 9;\n  width: calc(100% - #{$base-sidebar-width});\n  transition: width 0.28s;\n}\n\n.hideSidebar .fixed-header {\n  width: calc(100% - 54px);\n}\n\n.sidebarHide .fixed-header {\n  width: 100%;\n}\n\n.mobile .fixed-header {\n  width: 100%;\n}\n\n.search-top {\n  width: 40px;\n  height: 40px;\n  line-height: 40px;\n  text-align: center;\n}\n</style>\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/layout/components/AppMain.vue",
    "content": "<template>\n  <section class=\"app-main\">\n    <router-view v-slot=\"{ Component, route }\">\n      <transition name=\"fade-transform\" mode=\"out-in\">\n        <keep-alive :include=\"cachedViews\">\n          <component :is=\"Component\" :key=\"route.path\" />\n        </keep-alive>\n      </transition>\n    </router-view>\n  </section>\n</template>\n\n<script setup>\nimport { computed } from '@vue/runtime-core'\nimport { useRoute } from 'vue-router'\nimport { useStore } from 'vuex'\n\nconst store = useStore()\nconst route = useRoute()\nstore.dispatch('tagsView/addCachedView', route)\nconst cachedViews = computed(() => {\n  return store.getters['tagsView/cachedViews']\n})\n</script>\n\n<style lang=\"scss\" scoped>\n.app-main {\n  /* 50= navbar  50  */\n  min-height: calc(100vh - 50px);\n  width: 100%;\n  position: relative;\n  overflow: hidden;\n}\n\n.fixed-header + .app-main {\n  padding-top: 50px;\n}\n\n.hasTagsView {\n  .app-main {\n    /* 84 = navbar + tags-view = 50 + 34 */\n    min-height: calc(100vh - 84px);\n  }\n\n  .fixed-header + .app-main {\n    padding-top: 84px;\n  }\n}\n</style>\n\n<style lang=\"scss\">\n// fix css style bug in open el-dialog\n.el-popup-parent--hidden {\n  .fixed-header {\n    padding-right: 17px;\n  }\n}\n</style>\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/layout/components/InnerLink/index.vue",
    "content": "<script>\nimport { h } from 'vue'\nimport { useRoute } from 'vue-router'\n\nexport default {\n  setup() {\n    const route = useRoute()\n    const link = route.meta.link\n    if (link === '') {\n      return '404'\n    }\n    const url = link\n    const height = document.documentElement.clientHeight - 94.5 + 'px'\n    const style = { height: height }\n\n    // 返回渲染函数\n    return () =>\n      h(\n        'div',\n        {\n          style: style\n        },\n        h('iframe', {\n          src: url,\n          frameborder: 'no',\n          width: '100%',\n          height: '100%',\n          scrolling: 'auto'\n        })\n      )\n  }\n}\n</script>\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/layout/components/Navbar/index.vue",
    "content": "<template>\n  <div class=\"navbar\">\n    <hamburger\n      id=\"hamburger-container\"\n      :is-active=\"sidebar.opened\"\n      class=\"hamburger-container\"\n      @toggleClick=\"toggleSideBar\"\n    />\n    <breadcrumb\n      id=\"breadcrumb-container\"\n      class=\"breadcrumb-container\"\n      v-if=\"!$store.state.settings.topNav\"\n    />\n    <top-nav\n      id=\"topmenu-container\"\n      class=\"topmenu-container\"\n      v-if=\"$store.state.settings.topNav\"\n    />\n\n    <div class=\"right-menu\">\n      <template v-if=\"device !== 'mobile'\">\n        <header-search id=\"header-search\" class=\"right-menu-item\" />\n\n        <screenfull id=\"screenfull\" class=\"right-menu-item hover-effect\" />\n\n        <el-tooltip content=\"布局大小\" effect=\"dark\" placement=\"bottom\">\n          <size-select id=\"size-select\" class=\"right-menu-item hover-effect\" />\n        </el-tooltip>\n      </template>\n      <div class=\"avatar-container\">\n        <el-dropdown\n          @command=\"handleCommand\"\n          class=\"right-menu-item hover-effect\"\n          trigger=\"click\"\n        >\n          <div class=\"avatar-wrapper\">\n            <img :src=\"avatar\" class=\"user-avatar\" />\n            <el-icon><caret-bottom /></el-icon>\n          </div>\n          <template #dropdown>\n            <el-dropdown-menu>\n              <router-link to=\"/user/profile\">\n                <el-dropdown-item>个人中心</el-dropdown-item>\n              </router-link>\n              <el-dropdown-item command=\"setLayout\">\n                <span>布局设置</span>\n              </el-dropdown-item>\n              <el-dropdown-item divided command=\"logout\">\n                <span>退出登录</span>\n              </el-dropdown-item>\n            </el-dropdown-menu>\n          </template>\n        </el-dropdown>\n      </div>\n    </div>\n  </div>\n</template>\n\n<script setup>\nimport { ElMessageBox } from 'element-plus'\nimport { useStore } from 'vuex'\nimport { computed } from 'vue'\nimport Breadcrumb from '@/components/Breadcrumb'\nimport TopNav from '@/components/TopNav'\nimport Hamburger from '@/components/Hamburger'\nimport Screenfull from '@/components/Screenfull'\nimport SizeSelect from '@/components/SizeSelect'\nimport HeaderSearch from '@/components/HeaderSearch'\n\nconst store = useStore()\nconst avatar = computed(() => store.getters['user/avatar'])\nconst sidebar = computed(() => store.getters['app/sidebar'])\nconst device = computed(() => store.getters['app/device'])\n\nfunction toggleSideBar() {\n  store.dispatch('app/toggleSideBar')\n}\n\nfunction handleCommand(command) {\n  switch (command) {\n    case 'setLayout':\n      setLayout()\n      break\n    case 'logout':\n      logout()\n      break\n    default:\n      break\n  }\n}\n\nfunction logout() {\n  ElMessageBox.confirm('确定注销并退出系统吗？', '提示', {\n    confirmButtonText: '确定',\n    cancelButtonText: '取消',\n    type: 'warning'\n  })\n    .then(() => {\n      store.dispatch('user/logout').then(() => {\n        location.href = '/login'\n      })\n    })\n    .catch(() => {})\n}\n\nconst emits = defineEmits(null)\nfunction setLayout() {\n  emits('setLayout')\n}\n</script>\n\n<style lang=\"scss\" scoped>\n.navbar {\n  height: 50px;\n  overflow: hidden;\n  position: relative;\n  background: #fff;\n  box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);\n\n  .hamburger-container {\n    line-height: 46px;\n    height: 100%;\n    float: left;\n    cursor: pointer;\n    transition: background 0.3s;\n    -webkit-tap-highlight-color: transparent;\n\n    &:hover {\n      background: rgba(0, 0, 0, 0.025);\n    }\n  }\n\n  .breadcrumb-container {\n    float: left;\n  }\n\n  .topmenu-container {\n    position: absolute;\n    left: 50px;\n  }\n\n  .errLog-container {\n    display: inline-block;\n    vertical-align: top;\n  }\n\n  .right-menu {\n    float: right;\n    height: 100%;\n    line-height: 50px;\n    display: flex;\n\n    &:focus {\n      outline: none;\n    }\n\n    .right-menu-item {\n      display: inline-block;\n      padding: 0 8px;\n      height: 100%;\n      font-size: 18px;\n      color: #5a5e66;\n      vertical-align: text-bottom;\n\n      &.hover-effect {\n        cursor: pointer;\n        transition: background 0.3s;\n\n        &:hover {\n          background: rgba(0, 0, 0, 0.025);\n        }\n      }\n    }\n\n    .avatar-container {\n      margin-right: 40px;\n\n      .avatar-wrapper {\n        margin-top: 5px;\n        position: relative;\n\n        .user-avatar {\n          cursor: pointer;\n          width: 40px;\n          height: 40px;\n          border-radius: 10px;\n        }\n\n        i {\n          cursor: pointer;\n          position: absolute;\n          right: -20px;\n          top: 25px;\n          font-size: 12px;\n        }\n      }\n    }\n  }\n}\n</style>\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/layout/components/Settings/index.vue",
    "content": "<template>\n  <el-drawer\n    v-model=\"showSettings\"\n    :withHeader=\"false\"\n    direction=\"rtl\"\n    size=\"300px\"\n  >\n    <div class=\"setting-drawer-title\">\n      <h3 class=\"drawer-title\">主题风格设置</h3>\n    </div>\n    <div class=\"setting-drawer-block-checbox\">\n      <div\n        class=\"setting-drawer-block-checbox-item\"\n        @click=\"handleTheme('theme-dark')\"\n      >\n        <img src=\"@/assets/images/dark.svg\" alt=\"dark\" />\n        <div\n          v-if=\"sideTheme === 'theme-dark'\"\n          class=\"setting-drawer-block-checbox-selectIcon\"\n          style=\"display: block\"\n        >\n          <i aria-label=\"图标: check\" class=\"anticon anticon-check\">\n            <svg\n              viewBox=\"64 64 896 896\"\n              data-icon=\"check\"\n              width=\"1em\"\n              height=\"1em\"\n              :fill=\"theme\"\n              aria-hidden=\"true\"\n              focusable=\"false\"\n              class\n            >\n              <path\n                d=\"M912 190h-69.9c-9.8 0-19.1 4.5-25.1 12.2L404.7 724.5 207 474a32 32 0 0 0-25.1-12.2H112c-6.7 0-10.4 7.7-6.3 12.9l273.9 347c12.8 16.2 37.4 16.2 50.3 0l488.4-618.9c4.1-5.1.4-12.8-6.3-12.8z\"\n              />\n            </svg>\n          </i>\n        </div>\n      </div>\n      <div\n        class=\"setting-drawer-block-checbox-item\"\n        @click=\"handleTheme('theme-light')\"\n      >\n        <img src=\"@/assets/images/light.svg\" alt=\"light\" />\n        <div\n          v-if=\"sideTheme === 'theme-light'\"\n          class=\"setting-drawer-block-checbox-selectIcon\"\n          style=\"display: block\"\n        >\n          <i aria-label=\"图标: check\" class=\"anticon anticon-check\">\n            <svg\n              viewBox=\"64 64 896 896\"\n              data-icon=\"check\"\n              width=\"1em\"\n              height=\"1em\"\n              :fill=\"theme\"\n              aria-hidden=\"true\"\n              focusable=\"false\"\n              class\n            >\n              <path\n                d=\"M912 190h-69.9c-9.8 0-19.1 4.5-25.1 12.2L404.7 724.5 207 474a32 32 0 0 0-25.1-12.2H112c-6.7 0-10.4 7.7-6.3 12.9l273.9 347c12.8 16.2 37.4 16.2 50.3 0l488.4-618.9c4.1-5.1.4-12.8-6.3-12.8z\"\n              />\n            </svg>\n          </i>\n        </div>\n      </div>\n    </div>\n    <div class=\"drawer-item\">\n      <span>主题颜色</span>\n      <span class=\"comp-style\">\n        <el-color-picker\n          v-model=\"theme\"\n          :predefine=\"predefineColors\"\n          @change=\"themeChange\"\n        />\n      </span>\n    </div>\n    <el-divider />\n\n    <h3 class=\"drawer-title\">系统布局配置</h3>\n\n    <div class=\"drawer-item\">\n      <span>开启 TopNav</span>\n      <span class=\"comp-style\">\n        <el-switch v-model=\"topNav\" class=\"drawer-switch\" />\n      </span>\n    </div>\n\n    <div class=\"drawer-item\">\n      <span>开启 Tags-Views</span>\n      <span class=\"comp-style\">\n        <el-switch v-model=\"tagsView\" class=\"drawer-switch\" />\n      </span>\n    </div>\n\n    <div class=\"drawer-item\">\n      <span>固定 Header</span>\n      <span class=\"comp-style\">\n        <el-switch v-model=\"fixedHeader\" class=\"drawer-switch\" />\n      </span>\n    </div>\n\n    <div class=\"drawer-item\">\n      <span>显示 Logo</span>\n      <span class=\"comp-style\">\n        <el-switch v-model=\"sidebarLogo\" class=\"drawer-switch\" />\n      </span>\n    </div>\n\n    <div class=\"drawer-item\">\n      <span>动态标题</span>\n      <span class=\"comp-style\">\n        <el-switch v-model=\"dynamicTitle\" class=\"drawer-switch\" />\n      </span>\n    </div>\n\n    <el-divider />\n\n    <el-button type=\"primary\" plain icon=\"DocumentAdd\" @click=\"saveSetting\"\n      >保存配置</el-button\n    >\n    <el-button plain icon=\"Refresh\" @click=\"resetSetting\">重置配置</el-button>\n  </el-drawer>\n</template>\n\n<script setup>\nimport { useDynamicTitle } from '@/utils/dynamicTitle'\nimport { ref } from 'vue'\nimport { computed, getCurrentInstance } from '@vue/runtime-core'\nimport { useStore } from 'vuex'\n\nconst { proxy } = getCurrentInstance()\nconst store = useStore()\nconst showSettings = ref(false)\nconst theme = ref(store.getters['settings/theme'])\nconst sideTheme = ref(store.getters['settings/sideTheme'])\n\nconst predefineColors = ref([\n  '#409EFF',\n  '#ff4500',\n  '#ff8c00',\n  '#ffd700',\n  '#90ee90',\n  '#00ced1',\n  '#1e90ff',\n  '#c71585'\n])\n\n// 是否需要 topnav\nconst topNav = computed({\n  get: () => store.getters['settings/topNav'],\n  set: (val) => {\n    store.dispatch('settings/changeSetting', {\n      key: 'topNav',\n      value: val\n    })\n    if (!val) {\n      store.dispatch('app/toggleSideBarHide', false)\n      store.commit(\n        'permission/setSidebarRouters',\n        store.getters['permission/defaultRoutes']\n      )\n    }\n  }\n})\n\n// 是否需要 tagview\nconst tagsView = computed({\n  get: () => store.getters['settings/tagsView'],\n  set: (val) => {\n    store.dispatch('settings/changeSetting', {\n      key: 'tagsView',\n      value: val\n    })\n  }\n})\n\n// 是否需要固定头部\nconst fixedHeader = computed({\n  get: () => store.getters['settings/fixedHeader'],\n  set: (val) => {\n    store.dispatch('settings/changeSetting', {\n      key: 'fixedHeader',\n      value: val\n    })\n  }\n})\n\n// 是否需要侧边栏的 logo\nconst sidebarLogo = computed({\n  get: () => store.getters['settings/sidebarLogo'],\n  set: (val) => {\n    store.dispatch('settings/changeSetting', {\n      key: 'sidebarLogo',\n      value: val\n    })\n  }\n})\n\n// 是否需要侧边栏的动态网页的 title\nconst dynamicTitle = computed({\n  get: () => store.getters['settings/dynamicTitle'],\n  set: (val) => {\n    store.dispatch('settings/changeSetting', {\n      key: 'dynamicTitle',\n      value: val\n    })\n    // 动态设置网页标题\n    useDynamicTitle()\n  }\n})\n\nfunction themeChange(val) {\n  store.dispatch('settings/changeSetting', {\n    key: 'theme',\n    value: val\n  })\n  theme.value = val\n}\nfunction handleTheme(val) {\n  store.dispatch('settings/changeSetting', {\n    key: 'sideTheme',\n    value: val\n  })\n  sideTheme.value = val\n}\nfunction saveSetting() {\n  proxy.$modal.loading('正在保存到本地，请稍候...')\n  const layoutSetting = {\n    topNav: store.getters['settings/topNav'],\n    tagsView: store.getters['settings/tagsView'],\n    fixedHeader: store.getters['settings/fixedHeader'],\n    sidebarLogo: store.getters['settings/sidebarLogo'],\n    dynamicTitle: store.getters['settings/dynamicTitle'],\n    sideTheme: store.getters['settings/sideTheme'],\n    theme: store.getters['settings/theme']\n  }\n  localStorage.setItem('layout-setting', JSON.stringify(layoutSetting))\n  setTimeout(proxy.$modal.closeLoading(), 1000)\n}\nfunction resetSetting() {\n  proxy.$modal.loading('正在清除设置缓存并刷新，请稍候...')\n  localStorage.removeItem('layout-setting')\n  setTimeout(() => {\n    window.location.reload()\n  }, 1000)\n}\nfunction openSetting() {\n  showSettings.value = true\n}\n\ndefineExpose({\n  openSetting\n})\n</script>\n\n<style lang=\"scss\" scoped>\n.setting-drawer-title {\n  margin-bottom: 12px;\n  color: rgba(0, 0, 0, 0.85);\n  line-height: 22px;\n  font-weight: bold;\n  .drawer-title {\n    font-size: 14px;\n  }\n}\n.setting-drawer-block-checbox {\n  display: flex;\n  justify-content: flex-start;\n  align-items: center;\n  margin-top: 10px;\n  margin-bottom: 20px;\n\n  .setting-drawer-block-checbox-item {\n    position: relative;\n    margin-right: 16px;\n    border-radius: 2px;\n    cursor: pointer;\n\n    img {\n      width: 48px;\n      height: 48px;\n    }\n\n    .custom-img {\n      width: 48px;\n      height: 38px;\n      border-radius: 5px;\n      box-shadow: 1px 1px 2px #898484;\n    }\n\n    .setting-drawer-block-checbox-selectIcon {\n      position: absolute;\n      top: 0;\n      right: 0;\n      width: 100%;\n      height: 100%;\n      padding-top: 15px;\n      padding-left: 24px;\n      color: #1890ff;\n      font-weight: 700;\n      font-size: 14px;\n    }\n  }\n}\n\n.drawer-item {\n  color: rgba(0, 0, 0, 0.65);\n  padding: 12px 0;\n  font-size: 14px;\n\n  .comp-style {\n    float: right;\n    margin: -3px 8px 0px 0px;\n  }\n}\n</style>\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/layout/components/Sidebar/Link.vue",
    "content": "<template>\n  <component :is=\"type\" v-bind=\"linkProps()\">\n    <slot />\n  </component>\n</template>\n\n<script setup>\nimport { isExternal } from '@/utils/validate'\nimport { computed } from '@vue/runtime-core'\n\nconst props = defineProps({\n  to: {\n    type: [String, Object],\n    required: true\n  }\n})\n\nconst isExt = computed(() => {\n  return isExternal(props.to)\n})\n\nconst type = computed(() => {\n  if (isExt.value) {\n    return 'a'\n  }\n  return 'router-link'\n})\n\nfunction linkProps() {\n  if (isExt.value) {\n    return {\n      href: props.to,\n      target: '_blank',\n      rel: 'noopener'\n    }\n  }\n  return {\n    to: props.to\n  }\n}\n</script>\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/layout/components/Sidebar/Logo.vue",
    "content": "<template>\n  <div\n    class=\"sidebar-logo-container\"\n    :class=\"{ collapse: collapse }\"\n    :style=\"{\n      backgroundColor:\n        sideTheme === 'theme-dark'\n          ? variables.menuBackground\n          : variables.menuLightBackground\n    }\"\n  >\n    <transition name=\"sidebarLogoFade\">\n      <router-link\n        v-if=\"collapse\"\n        key=\"collapse\"\n        class=\"sidebar-logo-link\"\n        to=\"/\"\n      >\n        <img v-if=\"logo\" :src=\"logo\" class=\"sidebar-logo\" />\n        <h1\n          v-else\n          class=\"sidebar-title\"\n          :style=\"{\n            color:\n              sideTheme === 'theme-dark'\n                ? variables.logoTitleColor\n                : variables.logoLightTitleColor\n          }\"\n        >\n          {{ title }}\n        </h1>\n      </router-link>\n      <router-link v-else key=\"expand\" class=\"sidebar-logo-link\" to=\"/\">\n        <img v-if=\"logo\" :src=\"logo\" class=\"sidebar-logo\" />\n        <h1\n          class=\"sidebar-title\"\n          :style=\"{\n            color:\n              sideTheme === 'theme-dark'\n                ? variables.logoTitleColor\n                : variables.logoLightTitleColor\n          }\"\n        >\n          {{ title }}\n        </h1>\n      </router-link>\n    </transition>\n  </div>\n</template>\n\n<script setup>\nimport variables from '@/assets/styles/variables.module.scss'\nimport logo from '@/assets/logo/logo.png'\nimport { useStore } from 'vuex'\nimport { computed } from '@vue/runtime-core'\nimport { title } from '@/config/setting.config'\n\ndefineProps({\n  collapse: {\n    type: Boolean,\n    required: true\n  }\n})\n\nconst store = useStore()\nconst sideTheme = computed(() => store.getters['settings/sideTheme'])\n</script>\n\n<style lang=\"scss\" scoped>\n.sidebarLogoFade-enter-active {\n  transition: opacity 1.5s;\n}\n\n.sidebarLogoFade-enter,\n.sidebarLogoFade-leave-to {\n  opacity: 0;\n}\n\n.sidebar-logo-container {\n  position: relative;\n  width: 100%;\n  height: 50px;\n  line-height: 50px;\n  background: #2b2f3a;\n  text-align: center;\n  overflow: hidden;\n\n  & .sidebar-logo-link {\n    height: 100%;\n    width: 100%;\n\n    & .sidebar-logo {\n      width: 32px;\n      height: 32px;\n      vertical-align: middle;\n      margin-right: 12px;\n    }\n\n    & .sidebar-title {\n      display: inline-block;\n      margin: 0;\n      color: #fff;\n      font-weight: 600;\n      line-height: 50px;\n      font-size: 25px;\n      font-family: Avenir, Helvetica Neue, Arial, Helvetica, sans-serif;\n      vertical-align: middle;\n    }\n  }\n\n  &.collapse {\n    .sidebar-logo {\n      margin-right: 0px;\n    }\n  }\n}\n</style>\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/layout/components/Sidebar/SidebarItem.vue",
    "content": "<template>\n  <div v-if=\"!item.hidden\">\n    <template\n      v-if=\"\n        hasOneShowingChild(item.children, item) &&\n        (!onlyOneChild.children || onlyOneChild.noShowingChildren) &&\n        !item.alwaysShow\n      \"\n    >\n      <app-link\n        v-if=\"onlyOneChild.meta\"\n        :to=\"resolvePath(onlyOneChild.path, onlyOneChild.query)\"\n      >\n        <el-menu-item\n          :index=\"resolvePath(onlyOneChild.path)\"\n          :class=\"{ 'submenu-title-noDropdown': !isNest }\"\n        >\n          <svg-icon\n            :icon-name=\"onlyOneChild.meta.icon || (item.meta && item.meta.icon)\"\n          />\n          <template #title\n            ><span\n              class=\"menu-title\"\n              :title=\"hasTitle(onlyOneChild.meta.title)\"\n              >{{ onlyOneChild.meta.title }}</span\n            ></template\n          >\n        </el-menu-item>\n      </app-link>\n    </template>\n\n    <el-sub-menu\n      v-else\n      ref=\"subMenu\"\n      :index=\"resolvePath(item.path)\"\n      popper-append-to-body\n    >\n      <template v-if=\"item.meta\" #title>\n        <svg-icon :icon-name=\"item.meta && item.meta.icon\" />\n        <span class=\"menu-title\" :title=\"hasTitle(item.meta.title)\">{{\n          item.meta.title\n        }}</span>\n      </template>\n\n      <sidebar-item\n        v-for=\"child in item.children\"\n        :key=\"child.path\"\n        :is-nest=\"true\"\n        :item=\"child\"\n        :base-path=\"resolvePath(child.path)\"\n        class=\"nest-menu\"\n      />\n    </el-sub-menu>\n  </div>\n</template>\n\n<script setup>\nimport { isExternal } from '@/utils/validate'\nimport AppLink from './Link'\nimport { getNormalPath } from '@/utils/geshanzsq'\nimport { ref } from 'vue'\n\nconst props = defineProps({\n  // route object\n  item: {\n    type: Object,\n    required: true\n  },\n  isNest: {\n    type: Boolean,\n    default: false\n  },\n  basePath: {\n    type: String,\n    default: ''\n  }\n})\n\nconst onlyOneChild = ref({})\n\nfunction hasOneShowingChild(children = [], parent) {\n  if (!children) {\n    children = []\n  }\n  const showingChildren = children.filter((item) => {\n    if (item.hidden) {\n      return false\n    } else {\n      // Temp set(will be used if only has one showing child)\n      onlyOneChild.value = item\n      return true\n    }\n  })\n\n  // When there is only one child router, the child router is displayed by default\n  if (showingChildren.length === 1) {\n    return true\n  }\n\n  // Show parent if there are no child router to display\n  if (showingChildren.length === 0) {\n    onlyOneChild.value = { ...parent, path: '', noShowingChildren: true }\n    return true\n  }\n\n  return false\n}\n\nfunction resolvePath(routePath, routeQuery) {\n  if (isExternal(routePath)) {\n    return routePath\n  }\n  if (isExternal(props.basePath)) {\n    return props.basePath\n  }\n  if (routeQuery) {\n    const query = JSON.parse(routeQuery)\n    return {\n      path: getNormalPath(props.basePath + '/' + routePath),\n      query: query\n    }\n  }\n  return getNormalPath(props.basePath + '/' + routePath)\n}\n\nfunction hasTitle(title) {\n  if (title.length > 5) {\n    return title\n  } else {\n    return ''\n  }\n}\n</script>\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/layout/components/Sidebar/index.vue",
    "content": "<template>\n  <div\n    :class=\"{ 'has-logo': showLogo }\"\n    :style=\"{\n      backgroundColor:\n        sideTheme === 'theme-dark'\n          ? variables.menuBackground\n          : variables.menuLightBackground\n    }\"\n  >\n    <logo v-if=\"showLogo\" :collapse=\"isCollapse\" />\n    <el-scrollbar :class=\"sideTheme\" wrap-class=\"scrollbar-wrapper\">\n      <el-menu\n        :default-active=\"activeMenu\"\n        :collapse=\"isCollapse\"\n        :background-color=\"\n          sideTheme === 'theme-dark'\n            ? variables.menuBackground\n            : variables.menuLightBackground\n        \"\n        :text-color=\"\n          sideTheme === 'theme-dark'\n            ? variables.menuColor\n            : variables.menuLightColor\n        \"\n        :unique-opened=\"true\"\n        :active-text-color=\"theme\"\n        :collapse-transition=\"false\"\n        mode=\"vertical\"\n      >\n        <sidebar-item\n          v-for=\"(route, index) in sidebarRouters\"\n          :key=\"route.path + index\"\n          :item=\"route\"\n          :base-path=\"route.path\"\n        />\n      </el-menu>\n    </el-scrollbar>\n  </div>\n</template>\n\n<script setup>\nimport Logo from './Logo'\nimport SidebarItem from './SidebarItem'\nimport variables from '@/assets/styles/variables.module.scss'\nimport { useRoute } from 'vue-router'\nimport { useStore } from 'vuex'\nimport { computed } from '@vue/runtime-core'\n\nconst route = useRoute()\nconst store = useStore()\n\nconst sidebarRouters = computed(\n  () => store.getters['permission/sidebarRouters']\n)\nconst showLogo = computed(() => store.getters['settings/sidebarLogo'])\nconst sideTheme = computed(() => store.getters['settings/sideTheme'])\nconst theme = computed(() => store.getters['settings/theme'])\nconst sidebar = store.getters['app/sidebar']\nconst isCollapse = computed(() => !sidebar.opened)\n\nconst activeMenu = computed(() => {\n  const { meta, path } = route\n  // if set path, the sidebar will highlight the path you set\n  if (meta.activeMenu) {\n    return meta.activeMenu\n  }\n  return path\n})\n</script>\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/layout/components/TagsView/ScrollPane.vue",
    "content": "<template>\n  <el-scrollbar\n    ref=\"scrollContainer\"\n    :vertical=\"false\"\n    class=\"scroll-container\"\n    @wheel.prevent=\"handleScroll\"\n  >\n    <slot />\n  </el-scrollbar>\n</template>\n\n<script setup>\nimport {\n  getCurrentInstance,\n  onBeforeUnmount,\n  onMounted,\n  ref,\n  computed,\n  defineEmits\n} from 'vue'\nimport { useStore } from 'vuex'\n\nconst tagAndTagSpacing = ref(4)\nconst { proxy } = getCurrentInstance()\n\nconst scrollWrapper = computed(() => proxy.$refs.scrollContainer.$refs.wrapRef)\n\nonMounted(() => {\n  scrollWrapper.value.addEventListener('scroll', emitScroll, true)\n})\nonBeforeUnmount(() => {\n  scrollWrapper.value.removeEventListener('scroll', emitScroll)\n})\n\nfunction handleScroll(e) {\n  const eventDelta = e.wheelDelta || -e.deltaY * 40\n  const $scrollWrapper = scrollWrapper.value\n  $scrollWrapper.scrollLeft = $scrollWrapper.scrollLeft + eventDelta / 4\n}\nconst emitScroll = () => {\n  defineEmits('scroll')\n}\n\nconst store = useStore()\nconst visitedViews = computed(() => store.getters['tagsView/visitedViews'])\n\nfunction moveToTarget(currentTag) {\n  const $container = proxy.$refs.scrollContainer.$el\n  const $containerWidth = $container.offsetWidth\n  const $scrollWrapper = scrollWrapper.value\n\n  let firstTag = null\n  let lastTag = null\n\n  // find first tag and last tag\n  if (visitedViews.value.length > 0) {\n    firstTag = visitedViews.value[0]\n    lastTag = visitedViews.value[visitedViews.value.length - 1]\n  }\n\n  if (firstTag === currentTag) {\n    $scrollWrapper.scrollLeft = 0\n  } else if (lastTag === currentTag) {\n    $scrollWrapper.scrollLeft = $scrollWrapper.scrollWidth - $containerWidth\n  } else {\n    const tagListDom = document.getElementsByClassName('tags-view-item')\n    const currentIndex = visitedViews.value.findIndex(\n      (item) => item === currentTag\n    )\n    let prevTag = null\n    let nextTag = null\n    for (const k in tagListDom) {\n      if (k !== 'length' && Object.hasOwnProperty.call(tagListDom, k)) {\n        if (\n          tagListDom[k].dataset.path ===\n          visitedViews.value[currentIndex - 1].path\n        ) {\n          prevTag = tagListDom[k]\n        }\n        if (\n          tagListDom[k].dataset.path ===\n          visitedViews.value[currentIndex + 1].path\n        ) {\n          nextTag = tagListDom[k]\n        }\n      }\n    }\n\n    // the tag's offsetLeft after of nextTag\n    const afterNextTagOffsetLeft =\n      nextTag.offsetLeft + nextTag.offsetWidth + tagAndTagSpacing.value\n\n    // the tag's offsetLeft before of prevTag\n    const beforePrevTagOffsetLeft = prevTag.offsetLeft - tagAndTagSpacing.value\n    if (afterNextTagOffsetLeft > $scrollWrapper.scrollLeft + $containerWidth) {\n      $scrollWrapper.scrollLeft = afterNextTagOffsetLeft - $containerWidth\n    } else if (beforePrevTagOffsetLeft < $scrollWrapper.scrollLeft) {\n      $scrollWrapper.scrollLeft = beforePrevTagOffsetLeft\n    }\n  }\n}\n\ndefineExpose({\n  moveToTarget\n})\n</script>\n\n<style lang=\"scss\" scoped>\n.scroll-container {\n  white-space: nowrap;\n  position: relative;\n  overflow: hidden;\n  width: 100%;\n  :deep(.el-scrollbar__bar) {\n    bottom: 0px;\n  }\n  :deep(.el-scrollbar__wrap) {\n    height: 49px;\n  }\n}\n</style>\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/layout/components/TagsView/index.vue",
    "content": "<template>\n  <div id=\"tags-view-container\" class=\"tags-view-container\">\n    <scroll-pane\n      ref=\"scrollPaneRef\"\n      class=\"tags-view-wrapper\"\n      @scroll=\"handleScroll\"\n    >\n      <router-link\n        v-for=\"tag in visitedViews\"\n        :key=\"tag.path\"\n        :data-path=\"tag.path\"\n        :class=\"isActive(tag) ? 'active' : ''\"\n        :to=\"{ path: tag.path, query: tag.query, fullPath: tag.fullPath }\"\n        class=\"tags-view-item\"\n        :style=\"activeStyle(tag)\"\n        @click.middle=\"!isAffix(tag) ? closeSelectedTag(tag) : ''\"\n        @contextmenu.prevent=\"openMenu(tag, $event)\"\n      >\n        {{ tag.title }}\n        <span v-if=\"!isAffix(tag)\" @click.prevent.stop=\"closeSelectedTag(tag)\">\n          <close\n            class=\"el-icon-close\"\n            style=\"width: 1em; height: 1em; vertical-align: middle\"\n          />\n        </span>\n      </router-link>\n    </scroll-pane>\n    <ul\n      v-show=\"visible\"\n      :style=\"{ left: left + 'px', top: top + 'px' }\"\n      class=\"contextmenu\"\n    >\n      <li @click=\"refreshSelectedTag(selectedTag)\">\n        <refresh-right style=\"width: 1em; height: 1em\" /> 刷新页面\n      </li>\n      <li v-if=\"!isAffix(selectedTag)\" @click=\"closeSelectedTag(selectedTag)\">\n        <close style=\"width: 1em; height: 1em\" /> 关闭当前\n      </li>\n      <li @click=\"closeOthersTags\">\n        <circle-close style=\"width: 1em; height: 1em\" /> 关闭其他\n      </li>\n      <li v-if=\"!isFirstView()\" @click=\"closeLeftTags\">\n        <back style=\"width: 1em; height: 1em\" /> 关闭左侧\n      </li>\n      <li v-if=\"!isLastView()\" @click=\"closeRightTags\">\n        <right style=\"width: 1em; height: 1em\" /> 关闭右侧\n      </li>\n      <li @click=\"closeAllTags(selectedTag)\">\n        <circle-close style=\"width: 1em; height: 1em\" /> 全部关闭\n      </li>\n    </ul>\n  </div>\n</template>\n\n<script setup>\nimport {\n  computed,\n  getCurrentInstance,\n  nextTick,\n  onMounted,\n  ref,\n  watch\n} from 'vue'\nimport { useRoute, useRouter } from 'vue-router'\nimport { useStore } from 'vuex'\n\nimport ScrollPane from './ScrollPane'\nimport { getNormalPath } from '@/utils/geshanzsq'\n\nconst visible = ref(false)\nconst top = ref(0)\nconst left = ref(0)\nconst selectedTag = ref({})\nconst affixTags = ref([])\nconst scrollPaneRef = ref(null)\n\nconst { proxy } = getCurrentInstance()\nconst route = useRoute()\nconst router = useRouter()\nconst store = useStore()\n\nconst visitedViews = computed(() => store.getters['tagsView/visitedViews'])\nconst routes = computed(() => store.getters['permission/routes'])\nconst theme = computed(() => store.getters['settings/theme'])\n\nwatch(route, () => {\n  addTags()\n  moveToCurrentTag()\n})\nwatch(visible, (value) => {\n  if (value) {\n    document.body.addEventListener('click', closeMenu)\n  } else {\n    document.body.removeEventListener('click', closeMenu)\n  }\n})\nonMounted(() => {\n  initTags()\n  addTags()\n})\n\nfunction isActive(r) {\n  return r.path === route.path\n}\nfunction activeStyle(tag) {\n  if (!isActive(tag)) return {}\n  return {\n    'background-color': theme.value,\n    'border-color': theme.value\n  }\n}\nfunction isAffix(tag) {\n  return tag.meta && tag.meta.affix\n}\nfunction isFirstView() {\n  try {\n    return (\n      selectedTag.value.fullPath === '/index' ||\n      selectedTag.value.fullPath === visitedViews.value[1].fullPath\n    )\n  } catch (err) {\n    return false\n  }\n}\nfunction isLastView() {\n  try {\n    return (\n      selectedTag.value.fullPath ===\n      visitedViews.value[visitedViews.value.length - 1].fullPath\n    )\n  } catch (err) {\n    return false\n  }\n}\nfunction filterAffixTags(routes, basePath = '') {\n  let tags = []\n  routes.forEach((route) => {\n    if (route.meta && route.meta.affix) {\n      const tagPath = getNormalPath(basePath + '/' + route.path)\n      tags.push({\n        fullPath: tagPath,\n        path: tagPath,\n        name: route.name,\n        meta: { ...route.meta }\n      })\n    }\n    if (route.children) {\n      const tempTags = filterAffixTags(route.children, route.path)\n      if (tempTags.length >= 1) {\n        tags = [...tags, ...tempTags]\n      }\n    }\n  })\n  return tags\n}\nfunction initTags() {\n  const res = filterAffixTags(routes.value)\n  affixTags.value = res\n  for (const tag of res) {\n    // Must have tag name\n    if (tag.name) {\n      store.dispatch('tagsView/addVisitedView', tag)\n    }\n  }\n}\nfunction addTags() {\n  const { name } = route\n  if (name) {\n    store.dispatch('tagsView/addView', route)\n    if (route.meta.link) {\n      store.dispatch('tagsView/addIframeView', route)\n    }\n  }\n  return false\n}\nfunction moveToCurrentTag() {\n  nextTick(() => {\n    for (const r of visitedViews.value) {\n      if (r.path === route.path) {\n        scrollPaneRef.value.moveToTarget(r)\n        // when query is different then update\n        if (r.fullPath !== route.fullPath) {\n          store.dispatch('tagsView/updateVisitedView', route)\n        }\n      }\n    }\n  })\n}\nfunction refreshSelectedTag(view) {\n  proxy.$tab.refreshPage(view)\n  if (route.meta.link) {\n    store.dispatch('tagsView/delIframeView', route)\n  }\n}\nfunction closeSelectedTag(view) {\n  proxy.$tab.closePage(view).then(({ visitedViews }) => {\n    if (isActive(view)) {\n      toLastView(visitedViews, view)\n    }\n  })\n}\nfunction closeRightTags() {\n  proxy.$tab.closeRightPage(selectedTag.value).then((visitedViews) => {\n    if (!visitedViews.find((i) => i.fullPath === route.fullPath)) {\n      toLastView(visitedViews)\n    }\n  })\n}\nfunction closeLeftTags() {\n  proxy.$tab.closeLeftPage(selectedTag.value).then((visitedViews) => {\n    if (!visitedViews.find((i) => i.fullPath === route.fullPath)) {\n      toLastView(visitedViews)\n    }\n  })\n}\nfunction closeOthersTags() {\n  router.push(selectedTag.value).catch(() => {})\n  proxy.$tab.closeOtherPage(selectedTag.value).then(() => {\n    moveToCurrentTag()\n  })\n}\nfunction closeAllTags(view) {\n  proxy.$tab.closeAllPage().then(({ visitedViews }) => {\n    if (affixTags.value.some((tag) => tag.path === route.path)) {\n      return\n    }\n    toLastView(visitedViews, view)\n  })\n}\nfunction toLastView(visitedViews, view) {\n  const latestView = visitedViews.slice(-1)[0]\n  if (latestView) {\n    router.push(latestView.fullPath)\n  } else {\n    // now the default is to redirect to the home page if there is no tags-view,\n    // you can adjust it according to your needs.\n    if (view.name === 'Dashboard') {\n      // to reload home page\n      router.replace({ path: '/redirect' + view.fullPath })\n    } else {\n      router.push('/')\n    }\n  }\n}\nfunction openMenu(tag, e) {\n  const menuMinWidth = 105\n  const offsetLeft = proxy.$el.getBoundingClientRect().left // container margin left\n  const offsetWidth = proxy.$el.offsetWidth // container width\n  const maxLeft = offsetWidth - menuMinWidth // left boundary\n  const l = e.clientX - offsetLeft + 15 // 15: margin right\n\n  if (l > maxLeft) {\n    left.value = maxLeft\n  } else {\n    left.value = l\n  }\n\n  top.value = e.clientY\n  visible.value = true\n  selectedTag.value = tag\n}\nfunction closeMenu() {\n  visible.value = false\n}\nfunction handleScroll() {\n  closeMenu()\n}\n</script>\n\n<style lang=\"scss\" scoped>\n.tags-view-container {\n  height: 34px;\n  width: 100%;\n  background: #fff;\n  border-bottom: 1px solid #d8dce5;\n  box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12), 0 0 3px 0 rgba(0, 0, 0, 0.04);\n  .tags-view-wrapper {\n    .tags-view-item {\n      display: inline-block;\n      position: relative;\n      cursor: pointer;\n      height: 26px;\n      line-height: 26px;\n      border: 1px solid #d8dce5;\n      color: #495060;\n      background: #fff;\n      padding: 0 8px;\n      font-size: 12px;\n      margin-left: 5px;\n      margin-top: 4px;\n      &:first-of-type {\n        margin-left: 15px;\n      }\n      &:last-of-type {\n        margin-right: 15px;\n      }\n      &.active {\n        background-color: #42b983;\n        color: #fff;\n        border-color: #42b983;\n        &::before {\n          content: '';\n          background: #fff;\n          display: inline-block;\n          width: 8px;\n          height: 8px;\n          border-radius: 50%;\n          position: relative;\n          margin-right: 2px;\n        }\n      }\n    }\n  }\n  .contextmenu {\n    margin: 0;\n    background: #fff;\n    z-index: 3000;\n    position: absolute;\n    list-style-type: none;\n    padding: 5px 0;\n    border-radius: 4px;\n    font-size: 12px;\n    font-weight: 400;\n    color: #333;\n    box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, 0.3);\n    li {\n      margin: 0;\n      padding: 7px 16px;\n      cursor: pointer;\n      &:hover {\n        background: #eee;\n      }\n    }\n  }\n}\n</style>\n\n<style lang=\"scss\">\n//reset element css of el-icon-close\n.tags-view-wrapper {\n  .tags-view-item {\n    .el-icon-close {\n      width: 16px;\n      height: 16px;\n      vertical-align: 2px;\n      border-radius: 50%;\n      text-align: center;\n      transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);\n      transform-origin: 100% 50%;\n      &:before {\n        transform: scale(0.6);\n        display: inline-block;\n        vertical-align: -3px;\n      }\n      &:hover {\n        background-color: #b4bccc;\n        color: #fff;\n        width: 12px !important;\n        height: 12px !important;\n      }\n    }\n  }\n}\n</style>\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/layout/components/index.js",
    "content": "export { default as AppMain } from './AppMain'\nexport { default as Navbar } from './Navbar'\nexport { default as Settings } from './Settings'\nexport { default as TagsView } from './TagsView/index.vue'\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/layout/index.vue",
    "content": "<template>\n  <div\n    :class=\"classObj\"\n    class=\"app-wrapper\"\n    :style=\"{ '--current-color': theme }\"\n  >\n    <div\n      v-if=\"device === 'mobile' && sidebarConfig.opened\"\n      class=\"drawer-bg\"\n      @click=\"handleClickOutside\"\n    />\n    <sidebar v-if=\"!sidebarConfig.hide\" class=\"sidebar-container\" />\n    <div\n      :class=\"{ hasTagsView: needTagsView, sidebarHide: sidebarConfig.hide }\"\n      class=\"main-container\"\n    >\n      <div :class=\"{ 'fixed-header': fixedHeader }\">\n        <navbar @setLayout=\"setLayout\" />\n        <tags-view v-if=\"needTagsView\" />\n      </div>\n      <app-main />\n      <settings ref=\"settingRef\" />\n    </div>\n  </div>\n</template>\n<script setup>\nimport { computed, ref, watchEffect } from '@vue/runtime-core'\nimport { useWindowSize } from '@vueuse/core'\nimport { useStore } from 'vuex'\n\nimport Navbar from './components/Navbar'\nimport Sidebar from './components/Sidebar'\nimport Settings from './components/Settings'\nimport AppMain from './components/AppMain.vue'\nimport TagsView from './components/TagsView'\n\nconst store = useStore()\n\nconst theme = computed(() => store.getters['settings/theme'])\nconst sidebarConfig = computed(() => store.getters['app/sidebar'])\nconst device = computed(() => store.getters['app/device'])\nconst needTagsView = computed(() => store.getters['settings/tagsView'])\nconst fixedHeader = computed(() => store.getters['settings/fixedHeader'])\nconst classObj = computed(() => ({\n  hideSidebar: !sidebarConfig.value.opened,\n  openSidebar: sidebarConfig.value.opened,\n  withoutAnimation: sidebarConfig.value.withoutAnimation,\n  mobile: device.value === 'mobile'\n}))\n\nconst { width } = useWindowSize()\n// 参考 Bootstrap 的响应式设计\nconst defaultWidth = 992\n\nwatchEffect(() => {\n  if (device.value === 'mobile' && sidebarConfig.value.opened) {\n    store.dispatch('app/closeSideBar', { withoutAnimation: false })\n  }\n  if (width.value - 1 < defaultWidth) {\n    store.dispatch('app/toggleDevice', 'mobile')\n    store.dispatch('app/closeSideBar', { withoutAnimation: true })\n  } else {\n    store.dispatch('app/toggleDevice', 'desktop')\n  }\n})\n\nfunction handleClickOutside() {\n  store.dispatch('app/closeSideBar', { withoutAnimation: false })\n}\n\nconst settingRef = ref(null)\nfunction setLayout() {\n  settingRef.value.openSetting()\n}\n</script>\n\n<style lang=\"scss\" scoped>\n@import '@/assets/styles/mixin.scss';\n@import '@/assets/styles/variables.module.scss';\n\n.app-wrapper {\n  @include clearfix;\n  position: relative;\n  height: 100%;\n  width: 100%;\n\n  &.mobile.openSidebar {\n    position: fixed;\n    top: 0;\n  }\n}\n\n.drawer-bg {\n  background: #000;\n  opacity: 0.3;\n  width: 100%;\n  top: 0;\n  height: 100%;\n  position: absolute;\n  z-index: 999;\n}\n\n.fixed-header {\n  position: fixed;\n  top: 0;\n  right: 0;\n  z-index: 9;\n  width: calc(100% - #{$base-sidebar-width});\n  transition: width 0.28s;\n}\n\n.hideSidebar .fixed-header {\n  width: calc(100% - 54px);\n}\n\n.sidebarHide .fixed-header {\n  width: 100%;\n}\n\n.mobile .fixed-header {\n  width: 100%;\n}\n</style>\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/main.js",
    "content": "import { createApp } from 'vue'\n\nimport Cookies from 'js-cookie'\n\nimport App from './App.vue'\nimport router from './router'\nimport store from './store'\n\nimport ElementPlus from 'element-plus'\nimport 'element-plus/dist/index.css'\n// 中文语言\nimport locale from 'element-plus/es/locale/lang/zh-cn'\n\n// 导入 svgIcon\nimport svgIcon from '@/plugins/svg-icon'\nimport elementIcons from '@/components/SvgIcon/svgicon'\n\n// 代码高亮文件引入\nimport hljs from 'highlight.js'\n// 样式文件,这里我选的是sublime样式，文件里面还有其他样式可供选择\nimport 'highlight.js/styles/monokai-sublime.css'\n\n// 全局样式\nimport '@/assets/styles/index.scss'\n\n// 插件\nimport plugins from './plugins'\n\n// 指令\nimport directive from './directive'\n\n// 导航守卫\nimport './permission'\n\nimport {\n  parseTime,\n  resetForm,\n  addDateRange,\n  handleTree,\n  getDictionaryLabel,\n  getPictureShowUrl,\n  getHost\n} from '@/utils/geshanzsq'\n\n// 分页组件\nimport Pagination from '@/components/Pagination'\n\n// 字典组件\nimport DictionaryTag from '@/components/DictionaryTag'\nimport DictionaryOption from '@/components/DictionaryOption'\nimport DictionaryRadio from '@/components/DictionaryRadio'\n\nconst app = createApp(App)\n\ndirective(app)\n\n// 全局方法挂载\napp.config.globalProperties.parseTime = parseTime\napp.config.globalProperties.resetForm = resetForm\napp.config.globalProperties.handleTree = handleTree\napp.config.globalProperties.addDateRange = addDateRange\napp.config.globalProperties.getDictionaryLabel = getDictionaryLabel\napp.config.globalProperties.getPictureShowUrl = getPictureShowUrl\napp.config.globalProperties.getHost = getHost\n\n// 使用element-plus 并且设置全局的大小\napp.use(ElementPlus, {\n  locale: locale,\n  // 支持 large、default、small\n  size: Cookies.get('size') || 'default'\n})\n\n// 自定义一个代码高亮指令\napp.directive('highlight', function (el) {\n  const blocks = el.querySelectorAll('pre code')\n  blocks.forEach((block) => {\n    hljs.highlightBlock(block)\n  })\n})\n\napp.use(plugins)\n\n// 全局组件挂载\napp.component('Pagination', Pagination)\napp.component('DictionaryTag', DictionaryTag)\napp.component('DictionaryOption', DictionaryOption)\napp.component('DictionaryRadio', DictionaryRadio)\n\napp.use(svgIcon)\napp.use(elementIcons)\napp.use(store).use(router).mount('#app')\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/permission.js",
    "content": "import NProgress from 'nprogress'\nimport 'nprogress/nprogress.css'\nimport router from '@/router'\nimport store from '@/store'\nimport { whileList } from '@/config/setting.config'\nimport { baiduStatisticsUrl } from '@/config/theme.config'\n\nimport { isHttp } from '@/utils/validate'\nimport { useDynamicTitle } from '@/utils/dynamicTitle'\n\nNProgress.configure({ showSpinner: false })\n\nrouter.beforeEach(async (to, from, next) => {\n  NProgress.start()\n  useDynamicTitle(to.meta.title)\n  const token = store.getters['user/token']\n  // 已登录\n  if (token) {\n    // 如果已登录又访问登录页，直接跳转到首页\n    if (to.path === '/login') {\n      next(to.query.redirect || '/')\n    } else {\n      if (store.getters['user/username'].length === 0) {\n        try {\n          // 判断当前用户是否已拉取完用户信息\n          await store.dispatch('user/getUserInfo')\n          // 获取字典数据\n          await store.dispatch('dictionary/getAllDictionaryInfo')\n          // 根据 roles 权限生成可访问的路由表\n          await store.dispatch('permission/generateRoutes')\n          // 根据roles权限生成可访问的路由表\n          const accessRoutes = store.getters['permission/addRoutes']\n          accessRoutes.forEach((route) => {\n            // 动态添加可访问路由表\n            if (!isHttp(route.path)) {\n              router.addRoute(route)\n            }\n          })\n          // hack方法 确保addRoutes已完成\n          next({ ...to, replace: true })\n        } catch (error) {\n          console.log(error)\n          NProgress.done()\n          await store.dispatch('user/logout')\n          next(`/login?redirect=${to.fullPath}`)\n        }\n      } else {\n        next()\n      }\n    }\n  } else {\n    // 未登录\n    if (\n      whileList.indexOf(to.path) !== -1 ||\n      to.path.indexOf('/search/') !== -1\n    ) {\n      // 在免登录白名单，直接进入\n      next()\n    } else {\n      // 否则全部重定向到登录页，判断是否为首页，如果是首页，去除 redirect 参数\n      if (to.path === '/') {\n        next('/login')\n      } else {\n        next(`/login?redirect=${to.fullPath}`)\n      }\n    }\n  }\n  NProgress.done()\n})\n\nrouter.afterEach(() => {\n  NProgress.done()\n  // 判断百度统计的链接地址是否为空，或者不是生产环境，不需要统计\n  if (!baiduStatisticsUrl || process.env.NODE_ENV !== 'production') {\n    NProgress.done()\n    return\n  }\n\n  try {\n    setTimeout(() => {\n      // 每次执行前，先移除上次插入的代码\n      document.getElementById('baidu_tj') &&\n        document.getElementById('baidu_tj').remove()\n      const hm = document.createElement('script')\n      hm.src = baiduStatisticsUrl\n      hm.id = 'baidu_tj'\n      const s = document.getElementsByTagName('script')[0]\n      s.parentNode.insertBefore(hm, s)\n    }, 0)\n  } catch (e) {\n    console.log(e)\n  }\n  NProgress.done()\n})\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/plugins/auth.js",
    "content": "import store from '@/store'\n\nimport { superAdmin, allPermission } from '@/config/setting.config'\n\nfunction authPermission(permission) {\n  const permissions = store.getters && store.getters['user/permissionCodes']\n  if (permission && permission.length > 0) {\n    return permissions.some((v) => {\n      return allPermission === v || v === permission\n    })\n  }\n  return false\n}\n\nfunction authRole(role) {\n  const roles = store.getters && store.getters['user/roleCodes']\n  if (role && role.length > 0) {\n    return roles.some((v) => {\n      return superAdmin === v || v === role\n    })\n  } else {\n    return false\n  }\n}\n\nexport default {\n  // 验证用户是否具备某权限\n  hasPermi(permission) {\n    return authPermission(permission)\n  },\n  // 验证用户是否含有指定权限，只需包含其中一个\n  hasPermiOr(permissions) {\n    return permissions.some((item) => {\n      return authPermission(item)\n    })\n  },\n  // 验证用户是否含有指定权限，必须全部拥有\n  hasPermiAnd(permissions) {\n    return permissions.every((item) => {\n      return authPermission(item)\n    })\n  },\n  // 验证用户是否具备某角色\n  hasRole(role) {\n    return authRole(role)\n  },\n  // 验证用户是否含有指定角色，只需包含其中一个\n  hasRoleOr(roles) {\n    return roles.some((item) => {\n      return authRole(item)\n    })\n  },\n  // 验证用户是否含有指定角色，必须全部拥有\n  hasRoleAnd(roles) {\n    return roles.every((item) => {\n      return authRole(item)\n    })\n  }\n}\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/plugins/cache.js",
    "content": "const sessionCache = {\n  set(key, value) {\n    if (!sessionStorage) {\n      return\n    }\n    if (key != null && value != null) {\n      sessionStorage.setItem(key, value)\n    }\n  },\n  get(key) {\n    if (!sessionStorage) {\n      return null\n    }\n    if (key == null) {\n      return null\n    }\n    return sessionStorage.getItem(key)\n  },\n  setJSON(key, jsonValue) {\n    if (jsonValue != null) {\n      this.set(key, JSON.stringify(jsonValue))\n    }\n  },\n  getJSON(key) {\n    const value = this.get(key)\n    if (value != null) {\n      return JSON.parse(value)\n    }\n  },\n  remove(key) {\n    sessionStorage.removeItem(key)\n  }\n}\nconst localCache = {\n  set(key, value) {\n    if (!localStorage) {\n      return\n    }\n    if (key != null && value != null) {\n      localStorage.setItem(key, value)\n    }\n  },\n  get(key) {\n    if (!localStorage) {\n      return null\n    }\n    if (key == null) {\n      return null\n    }\n    return localStorage.getItem(key)\n  },\n  setJSON(key, jsonValue) {\n    if (jsonValue != null) {\n      this.set(key, JSON.stringify(jsonValue))\n    }\n  },\n  getJSON(key) {\n    const value = this.get(key)\n    if (value != null) {\n      return JSON.parse(value)\n    }\n  },\n  remove(key) {\n    localStorage.removeItem(key)\n  }\n}\n\nexport default {\n  /**\n   * 会话级缓存\n   */\n  session: sessionCache,\n  /**\n   * 本地缓存\n   */\n  local: localCache\n}\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/plugins/index.js",
    "content": "import modal from './modal'\nimport tab from './tab'\n\nexport default function installPlugins(app) {\n  // 模态框对象\n  app.config.globalProperties.$modal = modal\n  app.config.globalProperties.$tab = tab\n}\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/plugins/modal.js",
    "content": "import {\n  ElMessage,\n  ElMessageBox,\n  ElNotification,\n  ElLoading\n} from 'element-plus'\n\nlet loadingInstance\n\nexport default {\n  // 消息提示\n  msg(content) {\n    ElMessage.info(content)\n  },\n  // 错误消息\n  msgError(content) {\n    ElMessage.error(content)\n  },\n  // 成功消息\n  msgSuccess(content) {\n    ElMessage.success(content)\n  },\n  // 警告消息\n  msgWarning(content) {\n    ElMessage.warning(content)\n  },\n  // 弹出提示\n  alert(content) {\n    ElMessageBox.alert(content, '系统提示')\n  },\n  // 错误提示\n  alertError(content) {\n    ElMessageBox.alert(content, '系统提示', { type: 'error' })\n  },\n  // 成功提示\n  alertSuccess(content) {\n    ElMessageBox.alert(content, '系统提示', { type: 'success' })\n  },\n  // 警告提示\n  alertWarning(content) {\n    ElMessageBox.alert(content, '系统提示', { type: 'warning' })\n  },\n  // 通知提示\n  notify(content) {\n    ElNotification.info(content)\n  },\n  // 错误通知\n  notifyError(content) {\n    ElNotification.error(content)\n  },\n  // 成功通知\n  notifySuccess(content) {\n    ElNotification.success(content)\n  },\n  // 警告通知\n  notifyWarning(content) {\n    ElNotification.warning(content)\n  },\n  // 确认窗体\n  confirm(content) {\n    return ElMessageBox.confirm(content, '系统提示', {\n      confirmButtonText: '确定',\n      cancelButtonText: '取消',\n      type: 'warning'\n    })\n  },\n  // 提交内容\n  prompt(content) {\n    return ElMessageBox.prompt(content, '系统提示', {\n      confirmButtonText: '确定',\n      cancelButtonText: '取消',\n      type: 'warning'\n    })\n  },\n  // 打开遮罩层\n  loading(content) {\n    loadingInstance = ElLoading.service({\n      lock: true,\n      text: content,\n      background: 'rgba(0, 0, 0, 0.7)'\n    })\n  },\n  // 关闭遮罩层\n  closeLoading() {\n    loadingInstance.close()\n  }\n}\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/plugins/svg-icon.js",
    "content": "import SvgIcon from '@/components/SvgIcon'\n// 导入所有的 svg 图标\nconst svgRequire = require.context('@/assets/icons/svg', false, /\\.svg$/)\nsvgRequire.keys().forEach((svgIcon) => svgRequire(svgIcon))\n\n// SvgIcon 全局注册\nexport default (app) => {\n  app.component('svg-icon', SvgIcon)\n}\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/plugins/tab.js",
    "content": "import store from '@/store'\nimport router from '@/router'\n\nexport default {\n  // 刷新当前tab页签\n  refreshPage(obj) {\n    const { path, query, matched } = router.currentRoute.value\n    if (obj === undefined) {\n      matched.forEach((m) => {\n        if (m.components && m.components.default && m.components.default.name) {\n          if (!['Layout', 'ParentView'].includes(m.components.default.name)) {\n            obj = { name: m.components.default.name, path: path, query: query }\n          }\n        }\n      })\n    }\n    return store.dispatch('tagsView/delCachedView', obj).then(() => {\n      const { path, query } = obj\n      router.replace({\n        path: '/redirect' + path,\n        query: query\n      })\n    })\n  },\n  // 关闭当前tab页签，打开新页签\n  closeOpenPage(obj) {\n    store.dispatch('tagsView/delView', router.currentRoute.value)\n    if (obj !== undefined) {\n      return router.push(obj)\n    }\n  },\n  // 关闭指定tab页签\n  closePage(obj) {\n    if (obj === undefined) {\n      return store\n        .dispatch('tagsView/delView', router.currentRoute.value)\n        .then(({ lastPath }) => {\n          return router.push(lastPath || '/index')\n        })\n    }\n    return store.dispatch('tagsView/delView', obj)\n  },\n  // 关闭所有tab页签\n  closeAllPage() {\n    return store.dispatch('tagsView/delAllViews')\n  },\n  // 关闭左侧tab页签\n  closeLeftPage(obj) {\n    return store.dispatch(\n      'tagsView/delLeftTags',\n      obj || router.currentRoute.value\n    )\n  },\n  // 关闭右侧tab页签\n  closeRightPage(obj) {\n    return store.dispatch(\n      'tagsView/delRightTags',\n      obj || router.currentRoute.value\n    )\n  },\n  // 关闭其他tab页签\n  closeOtherPage(obj) {\n    return store.dispatch(\n      'tagsView/delOthersViews',\n      obj || router.currentRoute.value\n    )\n  },\n  // 打开tab页签\n  openPage(url) {\n    return router.push(url)\n  },\n  // 修改tab页签\n  updatePage(obj) {\n    return store.dispatch('tagsView/updateVisitedView', obj)\n  }\n}\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/router/index.js",
    "content": "import { createRouter, createWebHistory } from 'vue-router'\nimport Layout from '@/layout'\n\n/**\n * 公共路由\n */\nexport const constantRoutes = [\n  {\n    path: '/login',\n    component: () => import('@/views/login')\n  },\n  {\n    path: '/',\n    component: () => import('@/views/client/index'),\n    hidden: true\n  },\n  {\n    path: '/search/:searchContent',\n    component: () => import('@/views/client/search'),\n    hidden: true,\n    meta: { title: '搜索' }\n  },\n  {\n    path: '/about',\n    component: () => import('@/views/client/about'),\n    hidden: true,\n    meta: { title: '关于本站' }\n  },\n  {\n    path: '/index',\n    component: Layout,\n    redirect: '/index',\n    children: [\n      {\n        path: '',\n        component: () => import('@/views/index'),\n        name: 'Index',\n        meta: { title: '首页', icon: 'dashboard', affix: true }\n      }\n    ]\n  },\n  {\n    path: '/user',\n    component: Layout,\n    hidden: true,\n    children: [\n      {\n        path: 'profile',\n        component: () => import('@/views/system/user/profile'),\n        name: 'Profile',\n        meta: { title: '个人中心', icon: 'user' }\n      }\n    ]\n  },\n  {\n    path: '/:pathMatch(.*)*',\n    component: () => import('@/views/error/404'),\n    hidden: true\n  },\n  {\n    path: '/403',\n    component: () => import('@/views/error/403'),\n    hidden: true\n  }\n]\n\n/**\n * 动态路由，基于用户权限动态去加载\n */\nexport const dynamicRoutes = [\n  {\n    path: '/system/role-auth',\n    component: Layout,\n    hidden: true,\n    permissions: ['system:role:authUser'],\n    children: [\n      {\n        path: 'user/:id',\n        component: () => import('@/views/system/role/authUser'),\n        name: 'AuthUser',\n        meta: { title: '分配用户角色', activeMenu: '/system/role' }\n      }\n    ]\n  },\n  {\n    path: '/system/api/data/',\n    component: Layout,\n    hidden: true,\n    permissions: ['system:api:page'],\n    children: [\n      {\n        path: ':id',\n        component: () => import('@/views/system/api'),\n        name: 'APIData',\n        meta: { title: 'API 接口数据', activeMenu: '/system/api' }\n      }\n    ]\n  },\n  {\n    path: '/system/dictionary/data',\n    component: Layout,\n    hidden: true,\n    permissions: ['system:dictionary:data:page'],\n    children: [\n      {\n        path: ':id',\n        component: () => import('@/views/system/dictionary/data'),\n        name: 'DictionaryData',\n        meta: { title: '字典数据', activeMenu: '/system/dictionary' }\n      }\n    ]\n  }\n]\n\nconst router = createRouter({\n  history: createWebHistory(process.env.BASE_URL),\n  routes: constantRoutes\n})\n\nexport default router\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/store/index.js",
    "content": "/**\n *  导入所有 vuex 模块，自动加入 namespaced:true，用于解决 vuex 命名冲突，请勿修改。\n */\nimport { createStore } from 'vuex'\n\nconst modules = {}\nconst files = require.context('./modules', false, /\\.js$/)\nfiles.keys().forEach((key) => {\n  modules[key.replace(/(modules|\\/|\\.|js)/g, '')] = {\n    ...files(key).default,\n    namespaced: true\n  }\n})\n\nconst store = createStore({ modules })\n\nexport function setupStore(app) {\n  app.use(store)\n}\n\nexport default store\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/store/modules/app.js",
    "content": "import Cookies from 'js-cookie'\n\nconst getters = {\n  sidebar: (state) => state.sidebar,\n  device: (state) => state.device,\n  isMobile: (state) => state.device !== 'desktop',\n  size: (state) => state.size\n}\n\nconst state = {\n  sidebar: {\n    opened: Cookies.get('sidebarStatus')\n      ? !!+Cookies.get('sidebarStatus')\n      : true,\n    withoutAnimation: false,\n    hide: false\n  },\n  device: 'desktop',\n  size: Cookies.get('size') || 'default'\n}\n\nconst mutations = {\n  toggleSideBar: (state) => {\n    if (state.sidebar.hide) {\n      return false\n    }\n    state.sidebar.opened = !state.sidebar.opened\n    state.sidebar.withoutAnimation = false\n    if (state.sidebar.opened) {\n      Cookies.set('sidebarStatus', 1)\n    } else {\n      Cookies.set('sidebarStatus', 0)\n    }\n  },\n  closeSideBar: (state, withoutAnimation) => {\n    Cookies.set('sidebarStatus', 0)\n    state.sidebar.opened = false\n    state.sidebar.withoutAnimation = withoutAnimation\n  },\n  toggleDevice: (state, device) => {\n    state.device = device\n  },\n  setSize: (state, size) => {\n    state.size = size\n    Cookies.set('size', size)\n  },\n  toggleSideBarHide: (state, status) => {\n    state.sidebar.hide = status\n  }\n}\n\nconst actions = {\n  toggleSideBar({ commit }) {\n    commit('toggleSideBar')\n  },\n  closeSideBar({ commit }, { withoutAnimation }) {\n    commit('closeSideBar', withoutAnimation)\n  },\n  toggleDevice({ commit }, device) {\n    commit('toggleDevice', device)\n  },\n  setSize({ commit }, size) {\n    commit('setSize', size)\n  },\n  toggleSideBarHide({ commit }, status) {\n    commit('toggleSideBarHide', status)\n  }\n}\n\nexport default {\n  namespaced: true,\n  getters,\n  state,\n  mutations,\n  actions\n}\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/store/modules/dictionary.js",
    "content": "import { getAllDictionaryInfo } from '@/api/system/dictionary/dictionary'\nconst getters = {\n  allDictionaryInfo: (state) => state.allDictionaryInfo\n}\n\nconst state = {\n  allDictionaryInfo: []\n}\n\nconst mutations = {\n  setAllDictionaryInfo: (state, allDictionaryInfo) => {\n    state.allDictionaryInfo = allDictionaryInfo\n  }\n}\n\nconst actions = {\n  // 获取字典数据\n  async getAllDictionaryInfo({ commit }) {\n    const { data } = await getAllDictionaryInfo()\n    commit('setAllDictionaryInfo', data)\n  }\n}\n\nexport default {\n  namespaced: true,\n  getters,\n  state,\n  mutations,\n  actions\n}\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/store/modules/permission.js",
    "content": "import auth from '@/plugins/auth'\nimport router, { constantRoutes, dynamicRoutes } from '@/router'\nimport { getRouters } from '@/api/auth/user'\nimport Layout from '@/layout/index'\nimport ParentView from '@/components/ParentView'\nimport InnerLink from '@/layout/components/InnerLink'\n\nconst getters = {\n  routes: (state) => state.routes,\n  addRoutes: (state) => state.addRoutes,\n  defaultRoutes: (state) => state.defaultRoutes,\n  topbarRouters: (state) => state.topbarRouters,\n  sidebarRouters: (state) => state.sidebarRouters\n}\n\nconst state = {\n  routes: [],\n  addRoutes: [],\n  defaultRoutes: [],\n  topbarRouters: [],\n  sidebarRouters: []\n}\n\nconst mutations = {\n  setRouters: (state, routes) => {\n    state.addRoutes = routes\n    state.routes = constantRoutes.concat(routes)\n  },\n  setDefaultRouters: (state, routes) => {\n    state.defaultRoutes = constantRoutes.concat(routes)\n  },\n  setTopbarRouters: (state, routes) => {\n    state.topbarRouters = routes\n  },\n  setSidebarRouters: (state, routes) => {\n    state.sidebarRouters = routes\n  }\n}\n\nconst actions = {\n  // 生成路由\n  async generateRoutes({ commit, state }) {\n    // 向后端请求路由数据\n    const { data } = await getRouters()\n    const sdata = JSON.parse(JSON.stringify(data))\n    const rdata = JSON.parse(JSON.stringify(data))\n    const defaultData = JSON.parse(JSON.stringify(data))\n    const sidebarRoutes = filterAsyncRouter(sdata)\n    const rewriteRoutes = filterAsyncRouter(rdata, false, true)\n    const defaultRoutes = filterAsyncRouter(defaultData)\n    // 动态路由\n    const asyncRoutes = filterDynamicRoutes(dynamicRoutes)\n    asyncRoutes.forEach((route) => {\n      router.addRoute(route)\n    })\n    commit('setRouters', rewriteRoutes)\n    commit('setSidebarRouters', constantRoutes.concat(sidebarRoutes))\n    commit('setDefaultRouters', sidebarRoutes)\n    commit('setTopbarRouters', defaultRoutes)\n  }\n}\n\n// 遍历后台传来的路由字符串，转换为组件对象\nfunction filterAsyncRouter(asyncRouterMap, lastRouter = false, type = false) {\n  return asyncRouterMap.filter((route) => {\n    if (type && route.children) {\n      route.children = filterChildren(route.children)\n    }\n    if (route.component) {\n      // Layout ParentView 组件特殊处理\n      if (route.component === 'Layout') {\n        route.component = Layout\n      } else if (route.component === 'ParentView') {\n        route.component = ParentView\n      } else if (route.component === 'InnerLink') {\n        route.component = InnerLink\n      } else {\n        route.component = loadView(route.component)\n      }\n    }\n    if (route.children != null && route.children && route.children.length) {\n      route.children = filterAsyncRouter(route.children, route, type)\n    } else {\n      delete route.children\n      delete route.redirect\n    }\n    return true\n  })\n}\n\nfunction filterChildren(childrenMap, lastRouter = false) {\n  let children = []\n  childrenMap.forEach((el, index) => {\n    if (el.children && el.children.length) {\n      if (el.component === 'ParentView' && !lastRouter) {\n        el.children.forEach((c) => {\n          c.path = el.path + '/' + c.path\n          if (c.children && c.children.length) {\n            children = children.concat(filterChildren(c.children, c))\n            return\n          }\n          children.push(c)\n        })\n        return\n      }\n    }\n    if (lastRouter) {\n      el.path = lastRouter.path + '/' + el.path\n    }\n    children = children.concat(el)\n  })\n  return children\n}\n\n/**\n * 动态路由遍历，验证是否具备权限\n */\nexport function filterDynamicRoutes(routes) {\n  const res = []\n  routes.forEach((route) => {\n    if (route.permissions) {\n      if (auth.hasPermiOr(route.permissions)) {\n        res.push(route)\n      }\n    } else if (route.roles) {\n      if (auth.hasRoleOr(route.roles)) {\n        res.push(route)\n      }\n    }\n  })\n  return res\n}\n\n// 路由懒加载\nexport const loadView = (view) => {\n  // return (resolve) => require([`@/views/${view}`], resolve)\n  return () => require.ensure([], (require) => require(`@/views/${view}`))\n}\n\nexport default { namespaced: true, state, getters, mutations, actions }\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/store/modules/settings.js",
    "content": "import {\n  sideTheme,\n  showSettings,\n  topNav,\n  tagsView,\n  fixedHeader,\n  sidebarLogo,\n  dynamicTitle,\n  searchOpen,\n  weatherOpen,\n  showSiteDescription,\n  showCount\n} from '@/config/theme.config'\nimport { title } from '@/config/setting.config'\n\nconst storageSetting = JSON.parse(localStorage.getItem('layout-setting')) || ''\n\nconst getters = {\n  title: (state) => state.title,\n  themeColor: (state) => state.themeColor,\n  sideTheme: (state) => state.sideTheme,\n  showSettings: (state) => state.showSettings,\n  topNav: (state) => state.topNav,\n  tagsView: (state) => state.tagsView,\n  fixedHeader: (state) => state.fixedHeader,\n  sidebarLogo: (state) => state.sidebarLogo,\n  dynamicTitle: (state) => state.dynamicTitle,\n  searchOpen: (state) => state.searchOpen,\n  weatherOpen: (state) => state.weatherOpen,\n  showSiteDescription: (state) => state.showSiteDescription,\n  showCount: (state) => state.showCount\n}\n\nconst state = {\n  title: title,\n  themeColor: storageSetting.themeColor || '#409EFF',\n  sideTheme: storageSetting.sideTheme || sideTheme,\n  showSettings: showSettings,\n  topNav: storageSetting.topNav === undefined ? topNav : storageSetting.topNav,\n  tagsView:\n    storageSetting.tagsView === undefined ? tagsView : storageSetting.tagsView,\n  fixedHeader:\n    storageSetting.fixedHeader === undefined\n      ? fixedHeader\n      : storageSetting.fixedHeader,\n  sidebarLogo:\n    storageSetting.sidebarLogo === undefined\n      ? sidebarLogo\n      : storageSetting.sidebarLogo,\n  dynamicTitle:\n    storageSetting.dynamicTitle === undefined\n      ? dynamicTitle\n      : storageSetting.dynamicTitle,\n  searchOpen: searchOpen,\n  weatherOpen: weatherOpen,\n  showSiteDescription: showSiteDescription,\n  showCount: showCount\n}\nconst mutations = {\n  changeSetting: (state, { key, value }) => {\n    if (Object.prototype.hasOwnProperty.call(state, key)) {\n      state[key] = value\n    }\n  },\n  setSearchOpen: (state, searchOpen) => {\n    state.searchOpen = searchOpen\n  },\n  setWeatherOpen: (state, weatherOpen) => {\n    state.weatherOpen = weatherOpen\n  },\n  setShowSiteDescription: (state, showSiteDescription) => {\n    state.showSiteDescription = showSiteDescription\n  },\n  setShowCount: (state, showCount) => {\n    state.showCount = showCount\n  }\n}\n\nconst actions = {\n  // 修改布局设置\n  changeSetting({ commit }, data) {\n    commit('changeSetting', data)\n  },\n  // 修改搜索开启\n  setSearchOpen({ commit }, data) {\n    commit('setSearchOpen', data)\n  },\n  // 修改描述开启\n  setShowSiteDescription({ commit }, data) {\n    commit('setShowSiteDescription', data)\n  },\n  // 修改显示数量\n  setShowCount({ commit }, data) {\n    commit('setShowCount', data)\n  }\n}\n\nexport default {\n  namespaced: true,\n  getters,\n  state,\n  mutations,\n  actions\n}\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/store/modules/tagsView.js",
    "content": "const getters = {\n  visitedViews: (state) => state.visitedViews,\n  cachedViews: (state) => state.cachedViews\n}\n\nconst state = {\n  visitedViews: [],\n  cachedViews: []\n}\n\nconst mutations = {\n  addVisitedView: (state, view) => {\n    if (state.visitedViews.some((v) => v.path === view.path)) return\n    state.visitedViews.push(\n      Object.assign({}, view, {\n        title: view.meta.title || 'no-name'\n      })\n    )\n  },\n  addCachedView: (state, view) => {\n    if (state.cachedViews.includes(view.name)) return\n    if (!view.meta.noCache) {\n      state.cachedViews.push(view.name)\n    }\n  },\n\n  delVisitedView: (state, view) => {\n    for (const [i, v] of state.visitedViews.entries()) {\n      if (v.path === view.path) {\n        state.visitedViews.splice(i, 1)\n        break\n      }\n    }\n  },\n  delCachdeView: (state, view) => {\n    const index = state.cachedViews.indexOf(view.name)\n    index > -1 && state.cachedViews.splice(index, 1)\n  },\n\n  delOthersVisitedViews: (state, view) => {\n    state.visitedViews = state.visitedViews.filter((v) => {\n      return v.meta.affix || v.path === view.path\n    })\n  },\n\n  delOthersCachedViews: (state, view) => {\n    const index = state.cachedViews.indexOf(view.name)\n    if (index > -1) {\n      state.cachedViews = state.cachedViews.slice(index, index + 1)\n    } else {\n      state.cachedViews = []\n    }\n  },\n\n  delAllVisitedViews: (state) => {\n    // keep affix tags\n    const affixTags = state.visitedViews.filter((tag) => tag.meta.affix)\n    state.visitedViews = affixTags\n  },\n  delAllCachedViews: (state) => {\n    state.cachedViews = []\n  },\n\n  updateVisitedView: (state, view) => {\n    for (let v of state.visitedViews) {\n      if (v.path === view.path) {\n        v = Object.assign(v, view)\n        break\n      }\n    }\n  },\n\n  delRightTags: (state, view) => {\n    const index = state.visitedViews.findIndex((v) => v.path === view.path)\n    if (index === -1) {\n      return\n    }\n    state.visitedViews = state.visitedViews.filter((item, idx) => {\n      if (idx <= index || (item.meta && item.meta.affix)) {\n        return true\n      }\n      const i = state.cachedViews.indexOf(item.name)\n      if (i > -1) {\n        state.cachedViews.splice(i, 1)\n      }\n      return false\n    })\n  },\n\n  delLeftTags: (state, view) => {\n    const index = state.visitedViews.findIndex((v) => v.path === view.path)\n    if (index === -1) {\n      return\n    }\n    state.visitedViews = state.visitedViews.filter((item, idx) => {\n      if (idx >= index || (item.meta && item.meta.affix)) {\n        return true\n      }\n      const i = state.cachedViews.indexOf(item.name)\n      if (i > -1) {\n        state.cachedViews.splice(i, 1)\n      }\n      return false\n    })\n  }\n}\n\nconst actions = {\n  addView({ dispatch }, view) {\n    dispatch('addVisitedView', view)\n    dispatch('addCachedView', view)\n  },\n  addVisitedView({ commit }, view) {\n    commit('addVisitedView', view)\n  },\n  addCachedView({ commit }, view) {\n    commit('addCachedView', view)\n  },\n\n  delView({ dispatch, state }, view) {\n    return new Promise((resolve) => {\n      dispatch('delVisitedView', view)\n      dispatch('delCachedView', view)\n      resolve({\n        visitedViews: [...state.visitedViews],\n        cachedViews: [...state.cachedViews]\n      })\n    })\n  },\n  delVisitedView({ commit, state }, view) {\n    return new Promise((resolve) => {\n      commit('delVisitedView', view)\n      resolve([...state.visitedViews])\n    })\n  },\n  delCachedView({ commit, state }, view) {\n    return new Promise((resolve) => {\n      commit('delCachedView', view)\n      resolve([...state.cachedViews])\n    })\n  },\n\n  delOthersViews({ dispatch, state }, view) {\n    return new Promise((resolve) => {\n      dispatch('delOthersVisitedViews', view)\n      dispatch('delOthersCachedViews', view)\n      resolve({\n        visitedViews: [...state.visitedViews],\n        cachedViews: [...state.cachedViews]\n      })\n    })\n  },\n  delOthersVisitedViews({ commit, state }, view) {\n    return new Promise((resolve) => {\n      commit('delOthersVisitedViews', view)\n      resolve([...state.visitedViews])\n    })\n  },\n  delOthersCachedViews({ commit, state }, view) {\n    return new Promise((resolve) => {\n      commit('delOthersCachedViews', view)\n      resolve([...state.cachedViews])\n    })\n  },\n\n  delAllViews({ dispatch, state }, view) {\n    return new Promise((resolve) => {\n      dispatch('delAllVisitedViews', view)\n      dispatch('delAllCachedViews', view)\n      resolve({\n        visitedViews: [...state.visitedViews],\n        cachedViews: [...state.cachedViews]\n      })\n    })\n  },\n  delAllVisitedViews({ commit, state }) {\n    return new Promise((resolve) => {\n      commit('delAllVisitedViews')\n      resolve([...state.visitedViews])\n    })\n  },\n  delAllCachedViews({ commit, state }) {\n    return new Promise((resolve) => {\n      commit('delAllCachedViews')\n      resolve([...state.cachedViews])\n    })\n  },\n\n  updateVisitedView({ commit }, view) {\n    commit('updateVisitedView', view)\n  },\n\n  delRightTags({ commit }, view) {\n    return new Promise((resolve) => {\n      commit('delRightTags', view)\n      resolve([...state.visitedViews])\n    })\n  },\n\n  delLeftTags({ commit }, view) {\n    return new Promise((resolve) => {\n      commit('delLeftTags', view)\n      resolve([...state.visitedViews])\n    })\n  }\n}\n\nexport default {\n  namespaced: true,\n  getters,\n  state,\n  mutations,\n  actions\n}\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/store/modules/user.js",
    "content": "import defaultAvatar from '@/assets/images/profile.jpg'\nimport { login, logout } from '@/api/auth/login'\nimport { getUserInfo } from '@/api/auth/user'\nimport { getToken, setToken, removeToken } from '@/utils/auth'\nimport { getPictureShowUrl } from '@/utils/geshanzsq'\n\nconst state = {\n  token: getToken(),\n  username: '',\n  avatar: '',\n  roleCodes: [],\n  permissionCodes: []\n}\n\nconst getters = {\n  token: (state) => state.token,\n  username: (state) => state.username,\n  avatar: (state) => state.avatar,\n  roleCodes: (state) => state.roleCodes,\n  permissionCodes: (state) => state.permissionCodes\n}\n\nconst mutations = {\n  setToken: (state, token) => {\n    state.token = token\n  },\n  setUsername: (state, username) => {\n    state.username = username\n  },\n  setAvatar: (state, avatar) => {\n    state.avatar =\n      avatar === null || avatar === ''\n        ? defaultAvatar\n        : getPictureShowUrl(avatar)\n  },\n  setRoleCodes: (state, roleCodes) => {\n    state.roleCodes = roleCodes\n  },\n  setPermissionCodes: (state, permissionCodes) => {\n    if (permissionCodes) {\n      state.permissionCodes = permissionCodes\n    } else {\n      state.permissionCodes = []\n    }\n  }\n}\n\nconst actions = {\n  // 登录\n  login({ commit }, userInfo) {\n    const { username, password, uuid, code } = userInfo\n    return new Promise((resolve, reject) => {\n      login({\n        username,\n        password,\n        uuid,\n        code\n      })\n        .then((response) => {\n          const {\n            data: { token }\n          } = response\n          setToken(token)\n          commit('setToken', token)\n          resolve()\n        })\n        .catch((error) => {\n          reject(error)\n        })\n    })\n  },\n\n  // 获取用户信息\n  async getUserInfo({ commit }) {\n    const {\n      data: { username, avatar, roleCodes, permissionCodes }\n    } = await getUserInfo()\n    commit('setUsername', username)\n    commit('setRoleCodes', roleCodes)\n    commit('setPermissionCodes', permissionCodes)\n    commit('setAvatar', avatar)\n  },\n\n  // 退出登录\n  async logout({ commit }) {\n    try {\n      await logout()\n    } catch (error) {\n      console.log(error)\n    }\n\n    commit('setToken', '')\n    commit('setRoleCodes', [])\n    removeToken()\n  },\n\n  // 设置头像\n  setAvatar({ commit }, data) {\n    commit('setAvatar', data)\n  },\n  // 设置 token\n  setToken({ commit }, data) {\n    commit('setToken', data)\n  }\n}\n\nexport default { namespaced: true, state, getters, mutations, actions }\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/utils/auth.js",
    "content": "import Cookies from 'js-cookie'\nimport { tokenConfig } from '@/config/network.config'\nconst TokenKey = tokenConfig.header\n\nexport function getToken() {\n  return Cookies.get(TokenKey)\n}\n\nexport function setToken(token) {\n  return Cookies.set(TokenKey, token, { expires: tokenConfig.expireTime })\n}\n\nexport function removeToken() {\n  return Cookies.remove(TokenKey)\n}\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/utils/download.js",
    "content": "import { saveAs } from 'file-saver'\n\nexport function zip(data, zipName) {\n  const blob = new Blob([data], { type: 'application/zip' })\n  saveAs(blob, zipName)\n}\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/utils/dynamicTitle.js",
    "content": "import store from '@/store'\nimport { siteTitle } from '@/config/setting.config'\n\n/**\n * 动态修改标题\n */\nexport function useDynamicTitle(metaTitle) {\n  if (store.getters['settings/dynamicTitle'] && metaTitle) {\n    document.title = metaTitle + ' - ' + siteTitle\n  } else {\n    document.title = siteTitle\n  }\n}\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/utils/geshanzsq.js",
    "content": "/**\n * 通用js方法封装处理\n */\n\nimport store from '@/store'\nimport { isHttp } from './validate'\n\n// @description 格式化时间\nexport function parseTime(time, cFormat) {\n  if (time === undefined || time === '' || time === null) {\n    return ''\n  }\n  if (arguments.length === 0) {\n    return null\n  }\n  const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}'\n  let date\n  if (typeof time === 'object') {\n    date = time\n  } else {\n    if (typeof time === 'string' && /^[0-9]+$/.test(time)) {\n      time = parseInt(time)\n    }\n    if (typeof time === 'number' && time.toString().length === 10) {\n      time = time * 1000\n    }\n    date = new Date(time)\n  }\n  const formatObj = {\n    y: date.getFullYear(),\n    m: date.getMonth() + 1,\n    d: date.getDate(),\n    h: date.getHours(),\n    i: date.getMinutes(),\n    s: date.getSeconds(),\n    a: date.getDay()\n  }\n  return format.replace(/{([ymdhisa])+}/g, (result, key) => {\n    let value = formatObj[key]\n    if (key === 'a') {\n      return ['日', '一', '二', '三', '四', '五', '六'][value]\n    }\n    if (result.length > 0 && value < 10) {\n      value = '0' + value\n    }\n    return value || 0\n  })\n}\n\n// 格式化时间\nexport function formatTime(time, option) {\n  if (('' + time).length === 10) {\n    time = parseInt(time) * 1000\n  } else {\n    time = +time\n  }\n  const d = new Date(time)\n  const now = Date.now()\n\n  const diff = (now - d) / 1000\n\n  if (diff < 30) {\n    return '刚刚'\n  } else if (diff < 3600) {\n    // less 1 hour\n    return Math.ceil(diff / 60) + '分钟前'\n  } else if (diff < 3600 * 24) {\n    return Math.ceil(diff / 3600) + '小时前'\n  } else if (diff < 3600 * 24 * 2) {\n    return '1天前'\n  }\n  if (option) {\n    return parseTime(time, option)\n  } else {\n    return (\n      d.getMonth() +\n      1 +\n      '月' +\n      d.getDate() +\n      '日' +\n      d.getHours() +\n      '时' +\n      d.getMinutes() +\n      '分'\n    )\n  }\n}\n\n// 表单重置\nexport function resetForm(refName) {\n  if (this.$refs[refName]) {\n    this.$refs[refName].resetFields()\n  }\n}\n\n// 添加日期范围\nexport function addDateRange(params, dateRange, propName) {\n  const search = params\n  search.params =\n    typeof search.params === 'object' &&\n    search.params !== null &&\n    !Array.isArray(search.params)\n      ? search.params\n      : {}\n  dateRange = Array.isArray(dateRange) ? dateRange : []\n  if (typeof propName === 'undefined') {\n    search.params.beginTime = dateRange[0]\n    search.params.endTime = dateRange[1]\n  } else {\n    search.params['begin' + propName] = dateRange[0]\n    search.params['end' + propName] = dateRange[1]\n  }\n  return search\n}\n\n/**\n * 获取数据字典标签\n * @param {*} dictionaryCode 字典编码\n * @param {*} value 字典值\n */\nexport function getDictionaryLabel(dictionaryCode, value) {\n  const dictionaryData = getDictionaryData(dictionaryCode, value)\n  if (dictionaryData === undefined) {\n    return undefined\n  }\n  return dictionaryData.dictionaryLabel\n}\n\n/**\n * 获取数据字典数据\n * @param {*} dictionaryCode 字典编码\n * @param {*} value 字典值\n */\nexport function getDictionaryData(dictionaryCode, value) {\n  if (value === undefined) {\n    return undefined\n  }\n  // 获取数据字典\n  const dictionary = getDictionary(dictionaryCode)\n  if (dictionary.length === 0) {\n    return undefined\n  }\n  for (const dictionaryData of dictionary) {\n    if (dictionaryData.dictionaryValue === value + '') {\n      return dictionaryData\n    }\n  }\n  return undefined\n}\n\n/**\n * 获取数据字典\n * @param {*} dictionaryCode 字典编码\n */\nexport function getDictionary(dictionaryCode) {\n  if (dictionaryCode === undefined) {\n    return []\n  }\n  const dictionaryInfoList = store.getters['dictionary/allDictionaryInfo']\n  for (const dictionary of dictionaryInfoList) {\n    if (dictionaryCode === dictionary.dictionaryCode) {\n      return dictionary.dictionaryDataList\n    }\n  }\n  return []\n}\n\n// 字符串格式化(%s )\nexport function sprintf(str) {\n  const args = arguments\n  let flag = true\n  let i = 1\n  str = str.replace(/%s/g, function () {\n    const arg = args[i++]\n    if (typeof arg === 'undefined') {\n      flag = false\n      return ''\n    }\n    return arg\n  })\n  return flag ? str : ''\n}\n\n// 转换字符串，undefined,null等转化为\"\"\nexport function parseStrEmpty(str) {\n  if (!str || str === 'undefined' || str === 'null') {\n    return ''\n  }\n  return str\n}\n\n// 数据合并\nexport function mergeRecursive(source, target) {\n  for (const p in target) {\n    try {\n      if (target[p].constructor === Object) {\n        source[p] = mergeRecursive(source[p], target[p])\n      } else {\n        source[p] = target[p]\n      }\n    } catch (e) {\n      source[p] = target[p]\n    }\n  }\n  return source\n}\n\n/**\n * 构造树型结构数据\n * @param {*} data 数据源\n * @param {*} id id字段 默认 'id'\n * @param {*} parentId 父节点字段 默认 'parentId'\n * @param {*} children 孩子节点字段 默认 'children'\n */\nexport function handleTree(data, id, parentId, children) {\n  const config = {\n    id: id || 'id',\n    parentId: parentId || 'parentId',\n    childrenList: children || 'children'\n  }\n\n  const childrenListMap = {}\n  const nodeIds = {}\n  const tree = []\n\n  for (const d of data) {\n    const parentId = d[config.parentId]\n    if (childrenListMap[parentId] == null) {\n      childrenListMap[parentId] = []\n    }\n    nodeIds[d[config.id]] = d\n    childrenListMap[parentId].push(d)\n  }\n\n  for (const d of data) {\n    const parentId = d[config.parentId]\n    if (nodeIds[parentId] == null) {\n      tree.push(d)\n    }\n  }\n\n  for (const t of tree) {\n    adaptToChildrenList(t)\n  }\n\n  function adaptToChildrenList(o) {\n    if (childrenListMap[o[config.id]] !== null) {\n      o[config.childrenList] = childrenListMap[o[config.id]]\n    }\n    if (o[config.childrenList]) {\n      for (const c of o[config.childrenList]) {\n        adaptToChildrenList(c)\n      }\n    }\n  }\n  return tree\n}\n\n/**\n * 参数处理\n * @param {*} params  参数\n */\nexport function tansParams(params) {\n  let result = ''\n  for (const propName of Object.keys(params)) {\n    const value = params[propName]\n    const part = encodeURIComponent(propName) + '='\n    if (value !== null && typeof value !== 'undefined') {\n      if (typeof value === 'object') {\n        for (const key of Object.keys(value)) {\n          if (value[key] !== null && typeof value[key] !== 'undefined') {\n            const params = propName + '[' + key + ']'\n            const subPart = encodeURIComponent(params) + '='\n            result += subPart + encodeURIComponent(value[key]) + '&'\n          }\n        }\n      } else {\n        result += part + encodeURIComponent(value) + '&'\n      }\n    }\n  }\n  return result\n}\n\n// 返回项目路径\nexport function getNormalPath(p) {\n  if (p.length === 0 || !p || p === 'undefined') {\n    return p\n  }\n  const res = p.replace('//', '/')\n  if (res[res.length - 1] === '/') {\n    return res.slice(0, res.length - 1)\n  }\n  return res\n}\n\n// 验证是否为blob格式\nexport async function blobValidate(data) {\n  try {\n    const text = await data.text()\n    JSON.parse(text)\n    return false\n  } catch (error) {\n    return true\n  }\n}\n\n/**\n * 复制文字到剪切板\n * @param text\n */\nexport function copyText(text) {\n  const oInput = document.createElement('input')\n  oInput.value = text\n  document.body.appendChild(oInput)\n  oInput.select() // 选择对象\n  document.execCommand('Copy') // 执行浏览器复制命令\n  oInput.className = 'oInput'\n  oInput.style.display = 'none'\n}\n\n/**\n * 获取图片显示链接\n */\nexport function getPictureShowUrl(picturePath, isProjectImage) {\n  if (!picturePath || picturePath.indexOf('data:image') !== -1) {\n    return picturePath\n  }\n  return isHttp(picturePath) || isProjectImage\n    ? picturePath\n    : process.env.VUE_APP_BASE_FILE + picturePath\n}\n\n/**\n * 获取主机域名\n */\nexport function getHost() {\n  return window.location.protocol + '//' + window.location.host\n}\n\n/**\n * 打开网站\n */\nexport function openSite(siteUrl) {\n  const host = window.location.host\n  // 如果跳转网站为当前网站，不加 ref 后缀\n  if (\n    siteUrl.indexOf('http://' + host) === -1 ||\n    siteUrl.indexOf('https://' + host) === -1\n  ) {\n    window.open(siteUrl, '_blank')\n    return\n  }\n  if (siteUrl.lastIndexOf('?') > 0) {\n    siteUrl = siteUrl + '&ref=' + host\n  } else {\n    siteUrl = siteUrl + '?ref=' + host\n  }\n  window.open(siteUrl, '_blank')\n}\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/utils/request.js",
    "content": "import axios from 'axios'\nimport { ElMessage, ElMessageBox, ElNotification } from 'element-plus'\nimport qs from 'qs'\nimport store from '@/store'\n\nimport {\n  contentType,\n  requestTimeout,\n  statusName,\n  messageName,\n  tokenConfig\n} from '@/config/network.config'\nimport router from '@/router'\n\n/**\n * axios初始化\n */\nconst service = axios.create({\n  baseURL: process.env.VUE_APP_BASE_API,\n  timeout: requestTimeout,\n  headers: {\n    'Content-Type': contentType\n  }\n})\n\n/**\n * axios请求拦截器\n */\nservice.interceptors.request.use(\n  (config) => {\n    const token = store.getters['user/token']\n    if (token) {\n      config.headers[tokenConfig.header] = tokenConfig.prefix + token\n    }\n    if (\n      config.data &&\n      config.headers['Content-Type'] ===\n        'application/x-www-form-urlencoded;charset=UTF-8'\n    ) {\n      config.data = qs.stringify(config.data)\n    }\n    return config\n  },\n  (error) => {\n    console.log(error)\n    return Promise.reject(error)\n  }\n)\n\n/**\n * axios响应拦截器\n */\nservice.interceptors.response.use(\n  (response) => {\n    // 二进制数据则直接返回\n    if (\n      response.request.responseType === 'blob' ||\n      response.request.responseType === 'arraybuffer'\n    ) {\n      return response.data\n    }\n    // 未设置状态码则默认成功状态\n    const code = response.data[statusName] || 200\n    // 若 data.message 存在，覆盖默认提醒消息\n    const message =\n      response.data[messageName] || '系统未知错误，请反馈给相关人员'\n    // 异常处理\n    switch (code) {\n      case 200: {\n        return Promise.resolve(response.data)\n      }\n      case 401: {\n        ElMessageBox.confirm(\n          '登录状态已过期，您可以继续留在该页面，或者重新登录',\n          '系统提示',\n          {\n            confirmButtonText: '重新登录',\n            cancelButtonText: '取消',\n            type: 'warning'\n          }\n        )\n          .then(() => {\n            store.dispatch('user/logout').then(() => {\n              location.href = router.currentRoute.value.fullPath\n            })\n          })\n          .catch(() => {})\n        return Promise.reject(message)\n      }\n      case 500: {\n        ElMessage({\n          message: message,\n          type: 'error'\n        })\n        return Promise.reject(new Error(message))\n      }\n      default: {\n        ElNotification.error({\n          title: message\n        })\n        return Promise.reject(new Error(message))\n      }\n    }\n  },\n  (error) => {\n    console.log(error)\n    let { message } = error\n    if (message === 'Network Error') {\n      message = '后端接口连接异常'\n    } else if (message.includes('timeout')) {\n      message = '系统接口请求超时'\n    } else if (message.includes('Request failed with status code')) {\n      message = '系统接口' + message.substr(message.length - 3) + '异常'\n    }\n    ElMessage({\n      message: message,\n      type: 'error',\n      duration: 5 * 1000\n    })\n    return Promise.reject(error)\n  }\n)\n\nexport default service\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/utils/scroll-to.js",
    "content": "Math.easeInOutQuad = function (t, b, c, d) {\n  t /= d / 2\n  if (t < 1) {\n    return (c / 2) * t * t + b\n  }\n  t--\n  return (-c / 2) * (t * (t - 2) - 1) + b\n}\n\n// requestAnimationFrame for Smart Animating http://goo.gl/sx5sts\nconst requestAnimFrame = (function () {\n  return (\n    window.requestAnimationFrame ||\n    window.webkitRequestAnimationFrame ||\n    window.mozRequestAnimationFrame ||\n    function (callback) {\n      window.setTimeout(callback, 1000 / 60)\n    }\n  )\n})()\n\n/**\n * Because it's so fucking difficult to detect the scrolling element, just move them all\n * @param {number} amount\n */\nfunction move(amount) {\n  document.documentElement.scrollTop = amount\n  document.body.parentNode.scrollTop = amount\n  document.body.scrollTop = amount\n}\n\nfunction position() {\n  return (\n    document.documentElement.scrollTop ||\n    document.body.parentNode.scrollTop ||\n    document.body.scrollTop\n  )\n}\n\n/**\n * @param {number} to\n * @param {number} duration\n * @param {Function} callback\n */\nexport function scrollTo(to, duration, callback) {\n  const start = position()\n  const change = to - start\n  const increment = 20\n  let currentTime = 0\n  duration = typeof duration === 'undefined' ? 500 : duration\n  const animateScroll = function () {\n    // increment the time\n    currentTime += increment\n    // find the value with the quadratic in-out easing function\n    const val = Math.easeInOutQuad(currentTime, start, change, duration)\n    // move the document.body\n    move(val)\n    // do the animation unless its over\n    if (currentTime < duration) {\n      requestAnimFrame(animateScroll)\n    } else {\n      if (callback && typeof callback === 'function') {\n        // the animation is done so lets callback\n        callback()\n      }\n    }\n  }\n  animateScroll()\n}\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/utils/validate.js",
    "content": "/**\n * 是否为外部资源\n */\nexport function isExternal(path) {\n  return /^(https?:|mailto:|tel:)/.test(path)\n}\n\n/**\n * 判断是否是数组\n */\nexport function isArray(arg) {\n  if (typeof Array.isArray === 'undefined') {\n    return Object.prototype.toString.call(arg) === '[object Array]'\n  }\n  return Array.isArray(arg)\n}\n\n/**\n * 判断 url 是否是 http 或 https\n */\nexport function isHttp(url) {\n  if (!url) {\n    return false\n  }\n  return url.indexOf('http://') !== -1 || url.indexOf('https://') !== -1\n}\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/views/client/about/components/Comment.vue",
    "content": "<template>\n  <div class=\"comment\" v-loading=\"loading\">\n    <div class=\"total\">\n      <el-icon class=\"message-icon\"><Comment /></el-icon>\n      <span>{{ dataList.length }} 条评论</span>\n    </div>\n\n    <!-- 发布评论框 -->\n    <div id=\"publish-comment\">\n      <send-comment\n        :parent-id=\"parentId\"\n        @cancel=\"handleCancel\"\n        @success=\"getData\"\n      />\n    </div>\n\n    <div v-if=\"dataList.length > 0\" class=\"main-comment\">\n      <div\n        v-for=\"comment in dataList\"\n        :key=\"comment.id\"\n        :class=\"comment.parentId > 0 ? 'parent-comment' : ''\"\n        :id=\"'comment-' + comment.id\"\n      >\n        <el-container>\n          <el-aside class=\"aside\">\n            <el-image\n              class=\"avatar\"\n              lazy\n              :src=\"\n                comment.avatar\n                  ? getPictureShowUrl(comment.avatar)\n                  : userDefaultAvatar\n              \"\n            >\n              <template #error>\n                <div class=\"image-slot\">\n                  <el-image class=\"avatar\" lazy :src=\"userDefaultAvatar\">\n                  </el-image>\n                </div>\n              </template>\n            </el-image>\n          </el-aside>\n\n          <el-main class=\"main\">\n            <div class=\"nick-name\">\n              <el-tag\n                type=\"primary\"\n                effect=\"dark\"\n                class=\"sticky\"\n                v-if=\"comment.hasSticky == 1\"\n                >置顶</el-tag\n              >\n              <span>{{ comment.nickName }}</span>\n              <el-tag\n                type=\"error\"\n                class=\"user-web-master\"\n                v-if=\"comment.webMaster\"\n              >\n                <span>站长</span>\n              </el-tag>\n              <el-tag\n                type=\"error\"\n                class=\"user-pro\"\n                v-if=\"!comment.webMaster && comment.createUserId\"\n              >\n                <span>PRO</span>\n              </el-tag>\n            </div>\n            <div\n              v-if=\"comment.parentId > 0\"\n              class=\"parent-content\"\n              @click=\"handlePosition('comment-' + comment.parent.id)\"\n            >\n              回复：\n              <span v-html=\"comment.parent.commentContent\"></span>\n            </div>\n            <div\n              v-html=\"comment.commentContent\"\n              v-highlight\n              class=\"content\"\n            ></div>\n            <div class=\"comment-time\">\n              <span class=\"time\">{{ parseTime(comment.gmtCreate) }}</span>\n              <span class=\"reply\" @click=\"handleReply(comment.id)\">回复</span>\n            </div>\n            <send\n              v-if=\"comment.id === parentId\"\n              :article-id=\"articleId\"\n              :parent-id=\"parentId\"\n              @cancel=\"handleCancelPublish\"\n              @success=\"handleSuccessPublish\"\n            />\n            <el-divider />\n          </el-main>\n        </el-container>\n      </div>\n    </div>\n    <el-empty v-else description=\"还没有评论，赶紧去评论抢占沙发吧！\" />\n  </div>\n</template>\n<script setup>\nimport { ref } from 'vue'\nimport { getTree } from '@/api/client/comment'\nimport { scrollTo } from '@/utils/scroll-to'\n\nimport SendComment from './SendComment'\n\nimport userDefaultAvatar from '@/assets/images/profile.jpg'\n\nconst loading = ref(true)\nconst dataList = ref([])\nconst parentId = ref(undefined)\n\nasync function getData() {\n  loading.value = true\n  const { data } = await getTree()\n  dataList.value = []\n  data.forEach((comment) => {\n    dataList.value.push(comment)\n    recursiveComment(comment, comment.children)\n  })\n  loading.value = false\n}\n\n/**\n * 递归添加评论\n */\nfunction recursiveComment(parentData, data) {\n  if (!(data instanceof Array)) {\n    return\n  }\n  data.forEach((comment) => {\n    comment.parent = parentData\n    dataList.value.push(comment)\n    if (comment.children instanceof Array && comment.children.length > 0) {\n      recursiveComment(comment, comment.children)\n    }\n  })\n}\n/**\n * 描点定位到上级评论\n */\nfunction handlePosition(parentId) {\n  const e = document.querySelector('#' + parentId)\n  scrollTo(e.offsetTop - 50, 500)\n}\n\n/**\n * 点击回复事件\n */\nfunction handleReply(id) {\n  parentId.value = id\n  const e = document.querySelector('#publish-comment')\n  scrollTo(e.offsetTop - 20, 500)\n}\n\n/**\n * 取消发布按钮\n */\nfunction handleCancel() {\n  parentId.value = 0\n}\n\ngetData()\n</script>\n<style scoped lang=\"scss\">\n.comment {\n  margin-top: 30px;\n  .total {\n    font-size: 20px;\n    display: flex;\n    align-items: center;\n    .message-icon {\n      font-size: 22px;\n    }\n  }\n\n  #publish-comment {\n    margin-top: 20px;\n    margin-bottom: 20px;\n  }\n\n  .main-comment {\n    .parent-comment {\n      margin-left: 50px;\n    }\n\n    .aside {\n      background-color: white !important;\n      width: 50px !important;\n      height: 50px !important;\n      padding: 0 !important;\n      overflow: hidden;\n      text-align: center !important;\n      border-radius: 50%;\n\n      .avatar {\n        border-radius: 50%;\n        width: 50px;\n        height: 50px;\n      }\n    }\n\n    .main {\n      padding: 0 !important;\n      margin-left: 20px !important;\n      .nick-name {\n        font-size: 18px;\n        font-weight: bold;\n        .sticky {\n          margin-right: 5px;\n        }\n        .user-web-master {\n          font-size: 13px;\n          margin-left: 5px;\n        }\n        .user-pro {\n          cursor: pointer;\n          margin-left: 5px;\n          color: white;\n          background: linear-gradient(\n            to right,\n            #409eff,\n            rgba(172, 81, 252, 0.96),\n            #e6a23c\n          );\n        }\n        .user-pro:hover {\n          background: linear-gradient(\n            to right,\n            rgba(172, 81, 252, 0.96),\n            #e6a23c,\n            #409eff\n          );\n        }\n      }\n      .content {\n        margin-top: 10px;\n      }\n\n      /*设置图片，以防过大*/\n      .content >>> img {\n        max-width: 100%;\n        height: auto;\n        cursor: pointer;\n      }\n\n      .parent-content {\n        margin-top: 10px;\n        color: var(--el-text-color-secondary);\n        font-size: var(--el-font-size-small);\n        display: -webkit-box;\n        -webkit-box-orient: vertical;\n        -webkit-line-clamp: 2;\n        overflow: hidden;\n        cursor: pointer;\n      }\n\n      .comment-time {\n        font-size: 14px;\n        margin-top: 10px;\n        .time {\n          color: var(--el-text-color-secondary);\n          font-size: var(--el-font-size-small);\n        }\n        .reply {\n          float: right;\n          cursor: pointer;\n        }\n      }\n    }\n  }\n}\n</style>\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/views/client/about/components/SendComment.vue",
    "content": "<template>\n  <el-form\n    :class=\"editForm.parentId > 0 ? 'parent-send-comment' : ''\"\n    :model=\"editForm\"\n    ref=\"editFormRef\"\n  >\n    <el-form-item prop=\"commentContent\">\n      <el-input\n        type=\"textarea\"\n        :rows=\"4\"\n        :placeholder=\"placeholder\"\n        v-model=\"editForm.commentContent\"\n      />\n    </el-form-item>\n    <el-row :gutter=\"24\" v-if=\"!isLogin\">\n      <el-col :xs=\"24\" :sm=\"12\">\n        <el-form-item prop=\"nickName\">\n          <el-input v-model=\"editForm.nickName\" placeholder=\"昵称\">\n            <template #prefix>\n              <el-icon><User /></el-icon>\n            </template>\n          </el-input>\n        </el-form-item>\n      </el-col>\n      <el-col :xs=\"24\" :sm=\"12\">\n        <el-form-item prop=\"email\">\n          <el-input v-model=\"editForm.email\" placeholder=\"邮箱\">\n            <template #prefix>\n              <el-icon><Message /></el-icon>\n            </template>\n          </el-input>\n        </el-form-item>\n      </el-col>\n    </el-row>\n    <div class=\"operation-parent\">\n      <div class=\"operation\">\n        <el-button\n          type=\"danger\"\n          v-if=\"editForm.parentId > 0\"\n          @click=\"handleCancelPublish\"\n          >再想想</el-button\n        >\n        <el-button type=\"primary\" @click=\"handlePublish\" :loading=\"loading\">\n          发表\n        </el-button>\n      </div>\n    </div>\n  </el-form>\n</template>\n<script setup>\nimport { computed, getCurrentInstance, ref, watch } from 'vue'\n\nimport { getToken } from '@/utils/auth'\nimport { add } from '@/api/client/comment'\n\nconst props = defineProps({\n  parentId: {\n    type: Number || String,\n    default: 0\n  },\n  placeholder: {\n    type: String,\n    default: '既然来了，那就留些足迹吧！'\n  }\n})\n\nconst { proxy } = getCurrentInstance()\nconst emit = defineEmits(null)\n\nconst editForm = ref({\n  parentId: props.parentId,\n  commentContent: undefined,\n  nickName: undefined,\n  email: undefined\n})\nconst loading = ref(false)\nconst isLogin = computed(() => getToken())\n\nwatch(props, (newProps) => {\n  editForm.value.parentId = newProps.parentId\n})\n\n/**\n * 取消发表\n */\nfunction handleCancelPublish() {\n  editForm.value.parentId = 0\n  emit('cancel')\n}\n\n/**\n * 发表评论\n */\nasync function handlePublish() {\n  if (!editForm.value.commentContent) {\n    proxy.$modal.msgError('请输入评论内容')\n    return\n  }\n  try {\n    loading.value = true\n    await add(editForm.value)\n    proxy.$modal.msgSuccess('发表成功')\n    loading.value = false\n    emit('success')\n    reset()\n  } catch (err) {\n    loading.value = false\n  }\n}\n\n/**\n * 重置\n */\nfunction reset() {\n  proxy.resetForm('editFormRef')\n}\n</script>\n<style lang=\"scss\" scoped>\n.parent-send-comment {\n  margin-top: 10px;\n}\n\n/* 清除高度坍塌 */\n.operation-parent:after {\n  content: '';\n  display: block;\n  clear: both;\n  width: 0;\n  height: 0;\n}\n\n.operation {\n  margin-top: 10px;\n  margin-bottom: 10px;\n  float: right;\n}\n</style>\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/views/client/about/index.vue",
    "content": "<template>\n  <Client :categories=\"categories\">\n    <template #app-main>\n      <div v-loading=\"loading\" class=\"app-container about\">\n        <el-card class=\"card\" :style=\"{ width: isMobile ? '100%' : '90%' }\">\n          <div\n            v-highlight\n            class=\"content\"\n            v-html=\"aboutConfig.aboutSiteContent\"\n          ></div>\n          <!-- 关于站长 -->\n          <h4>关于站长</h4>\n          <el-row :gutter=\"24\">\n            <el-col :sm=\"6\" :sx=\"24\">\n              <el-card class=\"author-card\">\n                <el-container>\n                  <el-aside class=\"author-aside\">\n                    <el-image\n                      class=\"about-author-avatar\"\n                      :src=\"\n                        aboutConfig.avatar\n                          ? getPictureShowUrl(aboutConfig.avatar)\n                          : getPictureShowUrl(defaultSiteImage)\n                      \"\n                    >\n                      <template #error>\n                        <el-image\n                          class=\"about-author-avatar\"\n                          :src=\"getPictureShowUrl(defaultSiteImage)\"\n                        />\n                      </template>\n                    </el-image>\n                  </el-aside>\n                  <el-main class=\"author-main\">\n                    <a class=\"nick-name\"\n                      ><strong>{{ aboutConfig.nickName }}</strong></a\n                    >\n                    <p class=\"email\">\n                      {{ aboutConfig.aboutSiteEmail }}\n                    </p>\n                  </el-main>\n                </el-container>\n                <div></div>\n              </el-card>\n            </el-col>\n            <el-col :sm=\"18\" :sx=\"24\">\n              <div class=\"description\">\n                {{ aboutConfig.aboutSiteDescription }}\n              </div>\n            </el-col>\n          </el-row>\n\n          <!-- 评论 -->\n          <Comment v-if=\"commentOpenStatus\" />\n        </el-card>\n      </div>\n    </template>\n  </Client>\n</template>\n<script setup>\nimport { computed, ref } from 'vue'\nimport { useStore } from 'vuex'\n\nimport { categoryList } from '@/api/client/nav'\nimport { getAbout } from '@/api/nav/config'\nimport { getOpenStatus } from '@/api/client/comment'\n\nimport Client from '@/layout/client'\nimport Comment from './components/Comment'\n\nconst store = useStore()\n\nconst loading = ref(true)\nconst categories = ref([])\nconst aboutConfig = ref({\n  aboutSiteContent: undefined\n})\nconst commentOpenStatus = ref(false)\n// 是否移动端\nconst isMobile = computed(() => store.getters['app/isMobile'])\n// 默认图片\nconst defaultSiteImage = ref('/profile/site/system/logo.jpg')\n\n/**\n * 获取分类\n */\nasync function getCategories() {\n  const { data } = await categoryList()\n  categories.value = data\n}\n\n/**\n * 获取最新收录网站\n */\nasync function getData() {\n  loading.value = true\n  const { data } = await getAbout()\n  if (data) {\n    aboutConfig.value = data\n  }\n  loading.value = false\n}\n\n/**\n * 获取评论开启状态\n */\nasync function getCommentOpenStatus() {\n  const { data } = await getOpenStatus()\n  commentOpenStatus.value = data\n}\n\ngetCategories()\ngetData()\ngetCommentOpenStatus()\n</script>\n<style scoped lang=\"scss\">\n.about {\n  .card {\n    margin: auto;\n\n    /*设置图片，以防过大*/\n    .content >>> img {\n      max-width: 100%;\n      height: auto;\n    }\n\n    .author-card {\n      height: 80px;\n      .author-aside {\n        background-color: white;\n        width: 40px;\n        padding: 0;\n        text-align: center;\n\n        .about-author-avatar {\n          border-radius: 20px;\n          width: 40px;\n          height: 40px;\n        }\n      }\n      .author-main {\n        padding: 0;\n        margin-left: 5px;\n\n        .nick-name {\n          font-size: 14px;\n        }\n        .email {\n          font-size: 14px;\n          color: #979898;\n          margin-top: 5px;\n        }\n      }\n    }\n    .description {\n      border-left: 5px solid #eee;\n      padding: 9px 18px;\n      color: #979898;\n    }\n  }\n}\n</style>\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/views/client/index/index.vue",
    "content": "<template>\n  <Client :categories=\"categoryList\">\n    <template #app-main>\n      <div class=\"app-container\">\n        <el-row :gutter=\"24\" type=\"flex\" justify=\"center\" v-if=\"searchOpen\">\n          <el-col :sm=\"16\" :sx=\"24\">\n            <search class=\"search\" />\n          </el-col>\n        </el-row>\n        <div v-loading=\"loading\" element-loading-text=\"数据加载中，请稍等...\">\n          <div v-for=\"categorySite in siteList\" :key=\"categorySite.id\">\n            <h4\n              :id=\"'category-' + categorySite.id\"\n              :ref=\"'category-' + categorySite.id\"\n            >\n              <svg-icon icon-name=\"tag\" class=\"client-category-icon\" />\n              <span class=\"client-category-name\">{{\n                categorySite.categoryName\n              }}</span>\n            </h4>\n            <el-row :gutter=\"24\">\n              <el-col\n                :sm=\"24 / showCount\"\n                :sx=\"24\"\n                class=\"mb15\"\n                v-for=\"site in categorySite.sites\"\n                :key=\"site.id\"\n              >\n                <site-card :site=\"site\" :disabled-tip=\"false\" />\n              </el-col>\n            </el-row>\n          </div>\n        </div>\n      </div>\n    </template>\n  </Client>\n</template>\n<script setup>\nimport { ref, computed } from 'vue'\nimport { useStore } from 'vuex'\n\nimport Client from '@/layout/client'\nimport SiteCard from '@/components/SiteCard'\nimport Search from '@/layout/client/components/Search'\n\nimport { categorySiteList } from '@/api/client/nav'\n\nconst store = useStore()\n\nconst searchOpen = computed(() => store.getters['settings/searchOpen'])\nconst showCount = computed(() => store.getters['settings/showCount'])\n\nconst loading = ref(true)\nconst categoryList = ref([])\nconst siteList = ref([])\n\n/**\n * 获取数据\n */\nasync function getData() {\n  loading.value = true\n  const {\n    data: { categories, sites }\n  } = await categorySiteList()\n  categoryList.value = categories\n\n  siteList.value = sites\n  loading.value = false\n}\n\ngetData()\n</script>\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/views/client/search/index.vue",
    "content": "<template>\n  <Client :categories=\"categoryList\" class=\"app-container\">\n    <template #app-main>\n      <el-row :gutter=\"24\" type=\"flex\" justify=\"center\">\n        <el-col :sm=\"16\" :sx=\"24\">\n          <search class=\"search\" />\n        </el-col>\n      </el-row>\n      <div v-loading=\"loading\">\n        <div v-for=\"categorySite in siteList\" :key=\"categorySite.id\">\n          <h4\n            :id=\"'category-' + categorySite.id\"\n            :ref=\"'category-' + categorySite.id\"\n          >\n            <svg-icon icon-name=\"tag\" class=\"client-category-icon\" />\n            <span class=\"client-category-name\">{{\n              categorySite.categoryName\n            }}</span>\n          </h4>\n          <el-row :gutter=\"24\">\n            <el-col\n              :sm=\"6\"\n              :sx=\"24\"\n              class=\"mb15\"\n              v-for=\"site in categorySite.sites\"\n              :key=\"site.id\"\n            >\n              <site-card :site=\"site\" />\n            </el-col>\n          </el-row>\n        </div>\n      </div>\n    </template>\n  </Client>\n</template>\n<script setup>\nimport { ref } from 'vue'\nimport { useRouter } from 'vue-router'\n\nimport Client from '@/layout/client'\nimport SiteCard from '@/components/SiteCard'\nimport Search from '@/layout/client/components/Search'\n\nimport { categorySiteList } from '@/api/client/search'\n\nconst router = useRouter()\n\nconst loading = ref(true)\nconst categoryList = ref([])\nconst siteList = ref([])\nconst searchContent = ref(router.currentRoute.value.params.searchContent)\n\n/**\n * 获取数据\n */\nasync function getData() {\n  loading.value = true\n  const {\n    data: { categories, sites }\n  } = await categorySiteList(searchContent.value)\n  categoryList.value = categories\n  siteList.value = sites\n  loading.value = false\n}\n\ngetData()\n</script>\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/views/error/403.vue",
    "content": "<template>\n  <div class=\"errPage-container\">\n    <el-button icon=\"arrow-left\" class=\"pan-back-btn\" @click=\"back\">\n      返回\n    </el-button>\n    <el-row>\n      <el-col :span=\"12\">\n        <h1 class=\"text-jumbo text-ginormous\">403错误!</h1>\n        <h2>您没有访问权限！</h2>\n        <h6>对不起，您没有访问权限，请不要进行非法操作！您可以返回主页面</h6>\n        <ul class=\"list-unstyled\">\n          <li class=\"link-type\">\n            <router-link to=\"/\"> 回首页 </router-link>\n          </li>\n        </ul>\n      </el-col>\n      <el-col :span=\"12\">\n        <img\n          :src=\"errGif\"\n          width=\"313\"\n          height=\"428\"\n          alt=\"Girl has dropped her ice cream.\"\n        />\n      </el-col>\n    </el-row>\n  </div>\n</template>\n\n<script setup>\nimport errImage from '@/assets/images/error/403.gif'\nimport { getCurrentInstance, ref } from '@vue/runtime-core'\n\nconst { proxy } = getCurrentInstance()\n\nconst errGif = ref(errImage + '?' + +new Date())\n\nfunction back() {\n  if (proxy.$route.query.noGoBack) {\n    proxy.$router.push({ path: '/' })\n  } else {\n    proxy.$router.go(-1)\n  }\n}\n</script>\n\n<style lang=\"scss\" scoped>\n.errPage-container {\n  width: 800px;\n  max-width: 100%;\n  margin: 100px auto;\n  .pan-back-btn {\n    background: #008489;\n    color: #fff;\n    border: none !important;\n  }\n  .pan-gif {\n    margin: 0 auto;\n    display: block;\n  }\n  .pan-img {\n    display: block;\n    margin: 0 auto;\n    width: 100%;\n  }\n  .text-jumbo {\n    font-size: 60px;\n    font-weight: 700;\n    color: #484848;\n  }\n  .list-unstyled {\n    font-size: 14px;\n    li {\n      padding-bottom: 5px;\n    }\n    a {\n      color: #008489;\n      text-decoration: none;\n      &:hover {\n        text-decoration: underline;\n      }\n    }\n  }\n}\n</style>\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/views/error/404.vue",
    "content": "<template>\n  <div class=\"wscn-http404-container\">\n    <div class=\"wscn-http404\">\n      <div class=\"pic-404\">\n        <img\n          class=\"pic-404__parent\"\n          src=\"@/assets/images/error/404.png\"\n          alt=\"404\"\n        />\n        <img\n          class=\"pic-404__child left\"\n          src=\"@/assets/images/error/404-cloud.png\"\n          alt=\"404\"\n        />\n        <img\n          class=\"pic-404__child mid\"\n          src=\"@/assets/images/error/404-cloud.png\"\n          alt=\"404\"\n        />\n        <img\n          class=\"pic-404__child right\"\n          src=\"@/assets/images/error/404-cloud.png\"\n          alt=\"404\"\n        />\n      </div>\n      <div class=\"bullshit\">\n        <div class=\"bullshit__oops\">404错误!</div>\n        <div class=\"bullshit__headline\">\n          {{ message }}\n        </div>\n        <div class=\"bullshit__info\">\n          对不起，您正在寻找的页面不存在。尝试检查URL的错误，然后按浏览器上的刷新按钮或尝试在我们的应用程序中找到其他内容。\n        </div>\n        <router-link to=\"/\" class=\"bullshit__return-home\">\n          返回首页\n        </router-link>\n      </div>\n    </div>\n  </div>\n</template>\n\n<script setup>\nimport { computed } from '@vue/reactivity'\n\nconst message = computed(() => {\n  return '找不到网页！'\n})\n</script>\n\n<style lang=\"scss\" scoped>\n.wscn-http404-container {\n  transform: translate(-50%, -50%);\n  position: absolute;\n  top: 40%;\n  left: 50%;\n}\n.wscn-http404 {\n  position: relative;\n  width: 1200px;\n  padding: 0 50px;\n  overflow: hidden;\n  .pic-404 {\n    position: relative;\n    float: left;\n    width: 600px;\n    overflow: hidden;\n    &__parent {\n      width: 100%;\n    }\n    &__child {\n      position: absolute;\n      &.left {\n        width: 80px;\n        top: 17px;\n        left: 220px;\n        opacity: 0;\n        animation-name: cloudLeft;\n        animation-duration: 2s;\n        animation-timing-function: linear;\n        animation-fill-mode: forwards;\n        animation-delay: 1s;\n      }\n      &.mid {\n        width: 46px;\n        top: 10px;\n        left: 420px;\n        opacity: 0;\n        animation-name: cloudMid;\n        animation-duration: 2s;\n        animation-timing-function: linear;\n        animation-fill-mode: forwards;\n        animation-delay: 1.2s;\n      }\n      &.right {\n        width: 62px;\n        top: 100px;\n        left: 500px;\n        opacity: 0;\n        animation-name: cloudRight;\n        animation-duration: 2s;\n        animation-timing-function: linear;\n        animation-fill-mode: forwards;\n        animation-delay: 1s;\n      }\n      @keyframes cloudLeft {\n        0% {\n          top: 17px;\n          left: 220px;\n          opacity: 0;\n        }\n        20% {\n          top: 33px;\n          left: 188px;\n          opacity: 1;\n        }\n        80% {\n          top: 81px;\n          left: 92px;\n          opacity: 1;\n        }\n        100% {\n          top: 97px;\n          left: 60px;\n          opacity: 0;\n        }\n      }\n      @keyframes cloudMid {\n        0% {\n          top: 10px;\n          left: 420px;\n          opacity: 0;\n        }\n        20% {\n          top: 40px;\n          left: 360px;\n          opacity: 1;\n        }\n        70% {\n          top: 130px;\n          left: 180px;\n          opacity: 1;\n        }\n        100% {\n          top: 160px;\n          left: 120px;\n          opacity: 0;\n        }\n      }\n      @keyframes cloudRight {\n        0% {\n          top: 100px;\n          left: 500px;\n          opacity: 0;\n        }\n        20% {\n          top: 120px;\n          left: 460px;\n          opacity: 1;\n        }\n        80% {\n          top: 180px;\n          left: 340px;\n          opacity: 1;\n        }\n        100% {\n          top: 200px;\n          left: 300px;\n          opacity: 0;\n        }\n      }\n    }\n  }\n  .bullshit {\n    position: relative;\n    float: left;\n    width: 300px;\n    padding: 30px 0;\n    overflow: hidden;\n    &__oops {\n      font-size: 32px;\n      font-weight: bold;\n      line-height: 40px;\n      color: #1482f0;\n      opacity: 0;\n      margin-bottom: 20px;\n      animation-name: slideUp;\n      animation-duration: 0.5s;\n      animation-fill-mode: forwards;\n    }\n    &__headline {\n      font-size: 20px;\n      line-height: 24px;\n      color: #222;\n      font-weight: bold;\n      opacity: 0;\n      margin-bottom: 10px;\n      animation-name: slideUp;\n      animation-duration: 0.5s;\n      animation-delay: 0.1s;\n      animation-fill-mode: forwards;\n    }\n    &__info {\n      font-size: 13px;\n      line-height: 21px;\n      color: grey;\n      opacity: 0;\n      margin-bottom: 30px;\n      animation-name: slideUp;\n      animation-duration: 0.5s;\n      animation-delay: 0.2s;\n      animation-fill-mode: forwards;\n    }\n    &__return-home {\n      display: block;\n      float: left;\n      width: 110px;\n      height: 36px;\n      background: #1482f0;\n      border-radius: 100px;\n      text-align: center;\n      color: #ffffff;\n      opacity: 0;\n      font-size: 14px;\n      line-height: 36px;\n      cursor: pointer;\n      animation-name: slideUp;\n      animation-duration: 0.5s;\n      animation-delay: 0.3s;\n      animation-fill-mode: forwards;\n    }\n    @keyframes slideUp {\n      0% {\n        transform: translateY(60px);\n        opacity: 0;\n      }\n      100% {\n        transform: translateY(0);\n        opacity: 1;\n      }\n    }\n  }\n}\n</style>\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/views/index/index.vue",
    "content": "<template>\n  <div class=\"app-container\">\n    <el-row :gutter=\"24\" class=\"panel-group\">\n      <el-col :sm=\"6\" :xs=\"24\">\n        <router-link to=\"/nav/category\">\n          <div class=\"card-panel\">\n            <div class=\"card-panel-icon-wrapper icon-message\">\n              <svg-icon icon-name=\"menu\" class-name=\"card-panel-icon\" />\n            </div>\n            <div class=\"card-panel-description\">\n              <div class=\"card-panel-text\">系统网站分类数</div>\n              <div class=\"card-panel-num\">{{ dataCount.categoryCount }}</div>\n            </div>\n          </div>\n        </router-link>\n      </el-col>\n      <el-col :sm=\"6\" :xs=\"24\">\n        <router-link to=\"/nav/site\">\n          <div class=\"card-panel\">\n            <div class=\"card-panel-icon-wrapper icon-money\">\n              <svg-icon icon-name=\"website\" class-name=\"card-panel-icon\" />\n            </div>\n            <div class=\"card-panel-description\">\n              <div class=\"card-panel-text\">系统网站数</div>\n              <div class=\"card-panel-num\">{{ dataCount.siteCount }}</div>\n            </div>\n          </div>\n        </router-link>\n      </el-col>\n      <el-col :sm=\"6\" :xs=\"24\">\n        <router-link to=\"/nav/site\">\n          <div class=\"card-panel\">\n            <div class=\"card-panel-icon-wrapper icon-shopping\">\n              <svg-icon icon-name=\"click\" class-name=\"card-panel-icon\" />\n            </div>\n            <div class=\"card-panel-description\">\n              <div class=\"card-panel-text\">系统网站点击量数</div>\n              <div class=\"card-panel-num\">{{ dataCount.siteClickCount }}</div>\n            </div>\n          </div>\n        </router-link>\n      </el-col>\n    </el-row>\n  </div>\n</template>\n<script setup>\nimport { ref } from 'vue'\n\nimport { getStatistics } from '@/api/nav/index'\n\nconst loading = ref(true)\nconst dataCount = ref({\n  categoryCount: 0,\n  siteCount: 0,\n  siteClickCount: 0\n})\n\n/**\n * 获取数据\n */\nasync function getData() {\n  loading.value = true\n  const { data } = await getStatistics()\n  dataCount.value = data\n  loading.value = false\n}\n\ngetData()\n</script>\n\n<style lang=\"scss\" scoped>\n.panel-group {\n  margin-top: 18px;\n\n  .card-panel-col {\n    margin-bottom: 32px;\n  }\n\n  .card-panel {\n    height: 108px;\n    cursor: pointer;\n    font-size: 12px;\n    position: relative;\n    overflow: hidden;\n    color: #666;\n    background: #fff;\n    box-shadow: 4px 4px 40px rgba(0, 0, 0, 0.05);\n    border-color: rgba(0, 0, 0, 0.05);\n\n    &:hover {\n      .card-panel-icon-wrapper {\n        color: #fff;\n      }\n\n      .icon-people {\n        background: #40c9c6;\n      }\n\n      .icon-message {\n        background: #36a3f7;\n      }\n\n      .icon-money {\n        background: #f4516c;\n      }\n\n      .icon-shopping {\n        background: #34bfa3;\n      }\n    }\n\n    .icon-people {\n      color: #40c9c6;\n    }\n\n    .icon-message {\n      color: #36a3f7;\n    }\n\n    .icon-money {\n      color: #f4516c;\n    }\n\n    .icon-shopping {\n      color: #34bfa3;\n    }\n\n    .card-panel-icon-wrapper {\n      float: left;\n      margin: 14px 0 0 14px;\n      padding: 16px;\n      transition: all 0.38s ease-out;\n      border-radius: 6px;\n    }\n\n    .card-panel-icon {\n      float: left;\n      font-size: 48px;\n    }\n\n    .card-panel-description {\n      float: right;\n      font-weight: bold;\n      margin: 26px;\n      margin-left: 0px;\n\n      .card-panel-text {\n        line-height: 18px;\n        color: rgba(0, 0, 0, 0.45);\n        font-size: 16px;\n        margin-bottom: 12px;\n      }\n\n      .card-panel-num {\n        font-size: 20px;\n      }\n    }\n  }\n}\n\n@media (max-width: 550px) {\n  .card-panel-description {\n    display: none;\n  }\n\n  .card-panel-icon-wrapper {\n    float: none !important;\n    width: 100%;\n    height: 100%;\n    margin: 0 !important;\n\n    .svg-icon {\n      display: block;\n      margin: 14px auto !important;\n      float: none !important;\n    }\n  }\n}\n</style>\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/views/login/index.vue",
    "content": "<template>\n  <div\n    class=\"login-container\"\n    :style=\"{ backgroundImage: 'url(' + backgroundImage + ')' }\"\n  >\n    <el-form\n      class=\"login-form\"\n      :model=\"loginForm\"\n      :rules=\"loginRules\"\n      ref=\"loginRef\"\n    >\n      <h3 class=\"title\">{{ title }}</h3>\n      <el-form-item prop=\"username\">\n        <el-input\n          v-model=\"loginForm.username\"\n          type=\"text\"\n          auto-complete=\"off\"\n          placeholder=\"用户名\"\n        >\n          <template #prefix>\n            <svg-icon icon-name=\"user\" class=\"el-input__icon input-icon\" />\n          </template>\n        </el-input>\n      </el-form-item>\n      <el-tooltip\n        v-model:visible=\"capsTooltip\"\n        content=\"大写已开启\"\n        placement=\"right\"\n      >\n        <el-form-item prop=\"password\">\n          <el-input\n            v-model=\"loginForm.password\"\n            :type=\"passwordType\"\n            auto-complete=\"off\"\n            placeholder=\"密码\"\n            @keyup=\"checkCapslock\"\n            @blur=\"capsTooltip = false\"\n            @keyup.enter=\"handleLogin\"\n          >\n            <template #prefix>\n              <svg-icon\n                icon-name=\"password\"\n                class=\"el-input__icon input-icon\"\n              />\n            </template>\n          </el-input>\n          <span class=\"show-password\" @click=\"handleShowPassword\">\n            <svg-icon\n              :icon-name=\"passwordType === 'password' ? 'eye' : 'eye-open'\"\n            />\n          </span>\n        </el-form-item>\n      </el-tooltip>\n      <el-form-item prop=\"code\">\n        <el-input\n          v-model=\"loginForm.code\"\n          auto-complete=\"off\"\n          placeholder=\"验证码\"\n          class=\"code-input\"\n          @keyup.enter=\"handleLogin\"\n        >\n          <template #prefix>\n            <svg-icon icon-name=\"validCode\" class=\"el-input__icon input-icon\" />\n          </template>\n        </el-input>\n        <div class=\"login-code\" v-loading=\"loadingCodeImage\">\n          <img :src=\"codeImageUrl\" @click=\"getCode\" class=\"login-code-img\" />\n        </div>\n      </el-form-item>\n      <el-form-item style=\"width: 100%\">\n        <el-button\n          :loading=\"loading\"\n          size=\"medium\"\n          type=\"primary\"\n          class=\"login-button\"\n          @click.prevent=\"handleLogin\"\n        >\n          <span v-if=\"!loading\">登 录</span>\n          <span v-else>登 录 中...</span>\n        </el-button>\n      </el-form-item>\n    </el-form>\n  </div>\n</template>\n\n<script setup>\nimport { ref, getCurrentInstance } from 'vue'\nimport { useStore } from 'vuex'\nimport { useRouter } from 'vue-router'\n\nimport { title } from '@/config'\nimport LoginBackground from '@/assets/images/login-background.jpg'\n\nimport { getCaptchaImage } from '@/api/auth/login'\nimport { getBingImage } from '@/api/common/bing'\n\nconst { proxy } = getCurrentInstance()\nconst store = useStore()\nconst router = useRouter()\n\nconst loginForm = ref({\n  username: '',\n  password: '',\n  code: '',\n  uuid: ''\n})\n\n// 大写提示\nconst capsTooltip = ref(false)\n/**\n * 检测是否为大写\n */\nfunction checkCapslock(e) {\n  const { key } = e\n  capsTooltip.value = key && key.length === 1 && key >= 'A' && key <= 'Z'\n}\n\n// 密码类型\nconst passwordType = ref('password')\n/**\n * 是否显示密码\n */\nfunction handleShowPassword() {\n  passwordType.value = passwordType.value === 'password' ? 'text' : 'password'\n}\n\n// 验证码路径\nconst codeImageUrl = ref('')\n// 验证码路径\nconst loadingCodeImage = ref(true)\n// 获取验证码\nasync function getCode() {\n  loadingCodeImage.value = true\n  try {\n    const {\n      data: { uuid, image }\n    } = await getCaptchaImage()\n    loginForm.value.uuid = uuid\n    codeImageUrl.value = image\n  } finally {\n    loadingCodeImage.value = false\n  }\n}\n\n// 验证规则\nconst loginRules = ref({\n  username: [{ required: true, trigger: 'blur', message: '用户名不能为空' }],\n  password: [\n    { required: true, trigger: 'blur', validator: validatePassword() }\n  ],\n  code: [{ required: true, trigger: 'blur', message: '验证码不能为空' }]\n})\n// 密码验证规则\nfunction validatePassword() {\n  return (rule, value, callback) => {\n    if (!value) {\n      callback(new Error('密码不能为空'))\n    } else if (value.length < 6) {\n      callback(new Error('密码不能少于6位'))\n    } else {\n      callback()\n    }\n  }\n}\n\n// 登录加载\nconst loading = ref(false)\n// 跳转地址\nconst redirect = ref(router.currentRoute.value.query.redirect)\n// 登录\nfunction handleLogin() {\n  proxy.$refs.loginRef.validate((valid) => {\n    if (!valid) {\n      return\n    }\n    loading.value = true\n    // 调用action的登录方法\n    store\n      .dispatch('user/login', loginForm.value)\n      .then(() => {\n        router.push(redirect.value || '/index')\n      })\n      .catch(() => {\n        // 重新获取验证码\n        getCode()\n      })\n      .finally(() => {\n        loading.value = false\n      })\n  })\n}\n\n// 背景图片\nconst backgroundImage = ref(LoginBackground)\n// 获取背景图片\nasync function getBackgroundImage() {\n  const {\n    data: { imageUrl }\n  } = await getBingImage()\n  backgroundImage.value = imageUrl\n}\n\ngetCode()\ngetBackgroundImage()\n</script>\n\n<style lang=\"scss\" scoped>\n.login-container {\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  height: 100%;\n  background-size: cover;\n  background-color: #2d3a4b;\n  background-image: url('~@/assets/images/login-background.jpg');\n\n  .title {\n    margin: 0px auto 30px auto;\n    text-align: center;\n    color: #707070;\n  }\n\n  .login-form {\n    border-radius: 6px;\n    background: #ffffff;\n    width: 400px;\n    padding: 25px 25px 5px 25px;\n    .el-input {\n      height: 38px;\n      input {\n        height: 38px;\n      }\n      .input-icon {\n        height: 39px;\n        width: 14px;\n        margin-left: 2px;\n      }\n    }\n    .show-password {\n      height: 39px;\n      width: 14px;\n      position: absolute;\n      right: 10px;\n      font-size: 16px;\n      color: #889aa4;\n      cursor: pointer;\n      user-select: none;\n    }\n    .code-input {\n      width: 63%;\n    }\n    .login-code {\n      width: 37%;\n      height: 38px;\n      float: right;\n      text-align: center;\n      .login-code-img {\n        cursor: pointer;\n        vertical-align: middle;\n      }\n    }\n    .login-button {\n      width: 100%;\n    }\n  }\n}\n</style>\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/views/nav/category/components/CategoryEdit.vue",
    "content": "<template>\n  <el-dialog\n    v-model=\"visible\"\n    :title=\"title\"\n    append-to-body\n    width=\"680px\"\n    :before-close=\"close\"\n  >\n    <el-form\n      :model=\"editForm\"\n      ref=\"editFormRef\"\n      label-width=\"100px\"\n      :rules=\"rules\"\n    >\n      <el-form-item label=\"上级分类\" prop=\"parentId\">\n        <el-tree-select\n          style=\"width: 100%\"\n          v-model=\"editForm.parentId\"\n          :data=\"categoryTree\"\n          :props=\"{ value: 'id', label: 'categoryName', children: 'children' }\"\n          value-key=\"id\"\n          placeholder=\"选择上级分类\"\n          check-strictly\n          filterable\n          :render-after-expand=\"false\"\n          @keyup.enter=\"handleSave\"\n        />\n      </el-form-item>\n      <el-form-item label=\"分类图标\" prop=\"categoryIcon\">\n        <el-popover\n          placement=\"bottom-start\"\n          :width=\"540\"\n          v-model:visible=\"showChooseIcon\"\n          trigger=\"click\"\n          @keyup.enter=\"handleSave\"\n          @show=\"showSelectIcon\"\n        >\n          <template #reference>\n            <el-input\n              v-model=\"editForm.categoryIcon\"\n              placeholder=\"点击选择图标\"\n              @click=\"showSelectIcon\"\n              @keyup.enter=\"handleSave\"\n              readonly\n            >\n              <template #prefix>\n                <svg-icon\n                  v-if=\"editForm.categoryIcon\"\n                  :icon-name=\"editForm.categoryIcon\"\n                  class=\"el-input__icon\"\n                  style=\"height: 32px; width: 16px\"\n                />\n                <el-icon v-else style=\"height: 32px; width: 16px\"\n                  ><search\n                /></el-icon>\n              </template>\n            </el-input>\n          </template>\n          <icon-select ref=\"iconSelectRef\" @selected=\"selected\" />\n        </el-popover>\n      </el-form-item>\n\n      <el-form-item label=\"分类名称\" prop=\"categoryName\">\n        <el-input\n          v-model=\"editForm.categoryName\"\n          placeholder=\"请输入分类名称\"\n          @keyup.enter=\"handleSave\"\n        />\n      </el-form-item>\n      <el-form-item label=\"排序\" prop=\"sort\">\n        <el-input-number\n          v-model=\"editForm.sort\"\n          controls-position=\"right\"\n          :min=\"0\"\n          @keyup.enter=\"handleSave\"\n        />\n      </el-form-item>\n\n      <el-form-item label=\"状态\" prop=\"status\">\n        <el-radio-group v-model=\"editForm.status\" @keyup.enter=\"handleSave\">\n          <dictionary-radio code=\"commonStatus\" />\n        </el-radio-group>\n      </el-form-item>\n    </el-form>\n\n    <template #footer>\n      <div class=\"dialog-footer\">\n        <el-button @click=\"close\">取 消</el-button>\n        <el-button type=\"primary\" @click=\"handleSave\" :loading=\"editLoading\"\n          >确 定</el-button\n        >\n      </div>\n    </template>\n  </el-dialog>\n</template>\n\n<script setup>\nimport { getCurrentInstance, ref } from 'vue'\n\nimport IconSelect from '@/components/IconSelect'\n\nimport {\n  tree,\n  getById,\n  getMaxSortByParentId,\n  add,\n  update\n} from '@/api/nav/category'\n\nconst { proxy } = getCurrentInstance()\n\nconst visible = ref(false)\nconst title = ref('')\nconst categoryTree = ref([])\nconst editLoading = ref(false)\nconst editForm = ref({\n  id: undefined,\n  categoryName: undefined,\n  parentId: '0',\n  status: '1',\n  sort: 0\n})\n\nconst rules = ref({\n  categoryName: [\n    { required: true, message: '分类名称不能为空', trigger: 'blur' }\n  ],\n  parentId: [{ required: true, message: '上级分类不能为空', trigger: 'blur' }],\n  sort: [{ required: true, message: '排序不能为空', trigger: 'blur' }],\n  status: [{ required: true, message: '状态不能为空', trigger: 'blur' }]\n})\n\nconst showChooseIcon = ref(false)\nconst iconSelectRef = ref(null)\n\n/**\n * 显示弹窗\n */\nasync function show(id, parentId) {\n  visible.value = true\n  if (id) {\n    title.value = '修改分类'\n    // 获取菜单信息\n    const { data } = await getById(id)\n    editForm.value = data\n    editForm.value.status = editForm.value.status + ''\n  } else {\n    title.value = '新增分类'\n    // 获取排序最大值\n    const { data } = await getMaxSortByParentId(parentId)\n    editForm.value.sort = data ? data + 1 : 1\n    editForm.value.id = undefined\n    editForm.value.parentId = parentId\n  }\n  getTree()\n}\n\n/**\n * 获取分类树\n */\nasync function getTree() {\n  const { data } = await tree()\n  categoryTree.value = [{ id: '0', categoryName: '根分类', children: data }]\n}\n\n/**\n * 展示下拉图标\n */\nfunction showSelectIcon() {\n  iconSelectRef.value.reset()\n  showChooseIcon.value = true\n}\n/**\n * 选择图标\n */\nfunction selected(name) {\n  editForm.value.categoryIcon = name\n  showChooseIcon.value = false\n}\n\n/**\n * 关闭\n */\nfunction close() {\n  visible.value = false\n  showChooseIcon.value = false\n  reset()\n}\n\n/**\n * 重置\n */\nfunction reset() {\n  proxy.resetForm('editFormRef')\n}\n\n/**\n * 提交数据\n */\nconst emit = defineEmits(null)\nfunction handleSave() {\n  proxy.$refs.editFormRef.validate(async (valid) => {\n    if (valid) {\n      editLoading.value = true\n      // 修改\n      if (editForm.value.id) {\n        try {\n          await update(editForm.value)\n          proxy.$modal.msgSuccess('修改分类成功')\n        } finally {\n          editLoading.value = false\n        }\n      } else {\n        // 新增\n        try {\n          await add(editForm.value)\n          proxy.$modal.msgSuccess('新增分类成功')\n        } finally {\n          editLoading.value = false\n        }\n      }\n      emit('fetch-data')\n      close()\n    }\n  })\n}\n\ndefineExpose({\n  show\n})\n</script>\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/views/nav/category/index.vue",
    "content": "<template>\n  <div class=\"app-container\">\n    <el-form\n      :model=\"queryParams\"\n      ref=\"queryFormRef\"\n      :inline=\"true\"\n      label-width=\"68px\"\n    >\n      <el-form-item label=\"分类名称\" prop=\"categoryName\">\n        <el-input\n          v-model=\"queryParams.categoryName\"\n          placeholder=\"请输入分类名称\"\n          clearable\n          style=\"width: 240px\"\n          @keyup.enter=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"状态\" prop=\"status\">\n        <el-select\n          v-model=\"queryParams.status\"\n          placeholder=\"分类状态\"\n          @change=\"handleQuery\"\n          clearable\n          style=\"width: 240px\"\n        >\n          <dictionary-option code=\"commonStatus\" />\n        </el-select>\n      </el-form-item>\n      <el-form-item>\n        <el-button type=\"primary\" icon=\"Search\" @click=\"handleQuery\"\n          >搜索</el-button\n        >\n        <el-button icon=\"Refresh\" @click=\"handleResetQuery\">重置</el-button>\n      </el-form-item>\n    </el-form>\n\n    <el-row class=\"mb8\">\n      <el-col>\n        <el-button\n          type=\"primary\"\n          icon=\"Plus\"\n          @click=\"handleAdd\"\n          v-permission=\"['nav:category:add']\"\n          >新增</el-button\n        >\n      </el-col>\n    </el-row>\n\n    <el-table\n      border\n      v-loading=\"loading\"\n      :data=\"dataList\"\n      row-key=\"id\"\n      :tree-props=\"{ children: 'children', hasChildren: 'hasChildren' }\"\n    >\n      <el-table-column\n        prop=\"categoryName\"\n        label=\"分类名称\"\n        :show-overflow-tooltip=\"true\"\n        width=\"160\"\n      ></el-table-column>\n      <el-table-column\n        prop=\"categoryIcon\"\n        label=\"图标\"\n        align=\"center\"\n        width=\"100\"\n      >\n        <template #default=\"{ row }\">\n          <svg-icon :icon-name=\"row.categoryIcon\" />\n        </template>\n      </el-table-column>\n      <el-table-column\n        prop=\"sort\"\n        label=\"排序\"\n        width=\"60\"\n        align=\"center\"\n      ></el-table-column>\n      <el-table-column label=\"状态\" align=\"center\" prop=\"status\" width=\"100\">\n        <template #default=\"{ row }\">\n          <dictionary-tag\n            code=\"commonStatus\"\n            :value=\"row.status\"\n          ></dictionary-tag>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"创建时间\" align=\"center\" prop=\"gmtCreate\">\n        <template #default=\"{ row }\">\n          <span>{{ parseTime(row.gmtCreate) }}</span>\n        </template>\n      </el-table-column>\n      <el-table-column\n        label=\"操作\"\n        align=\"center\"\n        width=\"300\"\n        class-name=\"small-padding fixed-width\"\n      >\n        <template #default=\"{ row }\">\n          <el-button\n            type=\"text\"\n            icon=\"Edit\"\n            @click=\"handleUpdate(row)\"\n            v-permission=\"['nav:category:update']\"\n            >修改</el-button\n          >\n          <el-button\n            type=\"text\"\n            icon=\"Plus\"\n            @click=\"handleAdd(row)\"\n            v-permission=\"['nav:category:add']\"\n            >新增</el-button\n          >\n          <el-button\n            type=\"text\"\n            icon=\"Delete\"\n            @click=\"handleDelete(row)\"\n            v-permission=\"['nav:category:delete']\"\n            >删除</el-button\n          >\n        </template>\n      </el-table-column>\n    </el-table>\n\n    <!-- 新增和编辑 -->\n    <edit @fetch-data=\"handleQuery\" ref=\"editRef\"></edit>\n  </div>\n</template>\n<script setup>\nimport { getCurrentInstance, ref } from 'vue'\n\nimport Edit from './components/CategoryEdit'\n\nimport { list, remove } from '@/api/nav/category'\n\nconst { proxy } = getCurrentInstance()\n\nconst queryParams = ref({\n  categoryName: undefined,\n  status: undefined,\n  orderColumn: 'sort',\n  orderType: 'asc'\n})\n\nconst loading = ref(true)\nconst dataList = ref([])\n\n/**\n * 获取分类树\n */\nasync function getTree() {\n  loading.value = true\n  const { data } = await list(queryParams.value)\n  dataList.value = proxy.handleTree(data)\n  loading.value = false\n}\n\n/**\n * 搜索\n */\nfunction handleQuery() {\n  getTree()\n}\n\n/**\n * 重置\n */\nfunction handleResetQuery() {\n  proxy.resetForm('queryFormRef')\n  handleQuery()\n}\n\n/**\n * 新增\n */\nfunction handleAdd(row) {\n  const parentId = row ? row.id : undefined\n  proxy.$refs.editRef.show(undefined, parentId)\n}\n\n/**\n * 修改\n */\nfunction handleUpdate(row) {\n  proxy.$refs.editRef.show(row.id, row.id)\n}\n\n/**\n * 删除\n */\nasync function handleDelete(row) {\n  proxy.$modal\n    .confirm(\n      '是否确认删除分类名称为【' +\n        row.categoryName +\n        '】的数据项？该分类下的所有网站都会删除'\n    )\n    .then(async function () {\n      await remove(row.id)\n      proxy.$modal.msgSuccess('删除分类成功')\n      handleQuery()\n    })\n}\n\n/**\n * 初始化\n */\ngetTree()\n</script>\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/views/nav/comment/components/CommentReject.vue",
    "content": "<template>\n  <el-dialog\n    v-model=\"visible\"\n    title=\"驳回评论\"\n    width=\"600px\"\n    append-to-body\n    :before-close=\"close\"\n  >\n    <el-form :model=\"editForm\" ref=\"editFormRef\">\n      <el-form-item prop=\"remark\">\n        <el-input\n          v-model=\"editForm.remark\"\n          type=\"textarea\"\n          rows=\"3\"\n          placeholder=\"请输入驳回原因\"\n        ></el-input>\n      </el-form-item>\n    </el-form>\n\n    <template #footer>\n      <div class=\"dialog-footer\">\n        <el-button @click=\"close\">取 消</el-button>\n        <el-button type=\"primary\" @click=\"handleSave\" :loading=\"editLoading\"\n          >确 定</el-button\n        >\n      </div>\n    </template>\n  </el-dialog>\n</template>\n<script setup>\nimport { getCurrentInstance, ref } from 'vue'\n\nimport { reject } from '@/api/nav/comment'\n\nconst { proxy } = getCurrentInstance()\n\nconst visible = ref(false)\nconst editLoading = ref(false)\nconst editForm = ref({\n  ids: undefined,\n  remark: undefined\n})\n\n/**\n * 显示弹窗\n */\nasync function show(ids) {\n  visible.value = true\n  editForm.value.ids = ids\n}\n\n/**\n * 关闭\n */\nfunction close() {\n  visible.value = false\n  reset()\n}\n\n/**\n * 重置\n */\nfunction reset() {\n  proxy.resetForm('editFormRef')\n}\n\n/**\n * 提交数据\n */\nconst emit = defineEmits(null)\nfunction handleSave() {\n  proxy.$refs.editFormRef.validate(async (valid) => {\n    if (valid) {\n      try {\n        editLoading.value = true\n        await reject(editForm.value)\n        proxy.$modal.msgSuccess('驳回评论成功')\n        emit('fetch-data')\n        close()\n      } finally {\n        editLoading.value = false\n      }\n    }\n  })\n}\n\ndefineExpose({\n  show\n})\n</script>\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/views/nav/comment/index.vue",
    "content": "<template>\n  <div class=\"app-container\">\n    <el-form\n      :model=\"queryParams\"\n      ref=\"queryFormRef\"\n      :inline=\"true\"\n      label-width=\"68px\"\n    >\n      <el-form-item label=\"评论内容\" prop=\"commentContent\">\n        <el-input\n          v-model=\"queryParams.commentContent\"\n          placeholder=\"请输入评论内容\"\n          clearable\n          style=\"width: 240px\"\n          @keyup.enter=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"状态\" prop=\"status\">\n        <el-select\n          style=\"width: 240px\"\n          v-model=\"queryParams.status\"\n          placeholder=\"请选择状态\"\n          @change=\"handleQuery\"\n          clearable\n        >\n          <dictionary-option code=\"navCommentStatus\" />\n        </el-select>\n      </el-form-item>\n      <el-form-item>\n        <el-button type=\"primary\" icon=\"Search\" @click=\"handleQuery\"\n          >搜索</el-button\n        >\n        <el-button icon=\"Refresh\" @click=\"handleResetQuery\">重置</el-button>\n      </el-form-item>\n    </el-form>\n    <el-row :gutter=\"10\" class=\"mb8\">\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"primary\"\n          icon=\"Check\"\n          @click=\"handlePass\"\n          v-permission=\"['nav:comment:pass']\"\n          >通过</el-button\n        >\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"warning\"\n          icon=\"Close\"\n          @click=\"handleReject\"\n          v-permission=\"['nav:comment:reject']\"\n          >驳回</el-button\n        >\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"danger\"\n          icon=\"Delete\"\n          :disabled=\"multiple\"\n          @click=\"handleDelete\"\n          v-permission=\"['nav:comment:delete']\"\n          >删除</el-button\n        >\n      </el-col>\n    </el-row>\n\n    <el-table\n      border\n      v-loading=\"loading\"\n      :data=\"dataList\"\n      @selection-change=\"handleSelectionChange\"\n    >\n      <el-table-column type=\"selection\" width=\"55\" align=\"center\" />\n      <el-table-column label=\"评论编号\" align=\"center\" prop=\"id\" width=\"180\" />\n\n      <el-table-column label=\"评论内容\" prop=\"commentContent\" />\n      <el-table-column label=\"评论人\">\n        <template #default=\"{ row }\">\n          <span v-if=\"row.createUserId\"\n            >{{ row.nickName }}({{ row.username }})</span\n          >\n        </template>\n      </el-table-column>\n      <el-table-column label=\"昵称\">\n        <template #default=\"{ row }\">\n          <span v-if=\"!row.createUserId\">{{ row.nickName }}</span>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"邮箱\" prop=\"email\" />\n      <el-table-column label=\"备注\" prop=\"remark\" />\n      <el-table-column label=\"创建时间\" align=\"center\">\n        <template #default=\"{ row }\">\n          <span>{{ parseTime(row.gmtCreate) }}</span>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"审核时间\" align=\"center\">\n        <template #default=\"{ row }\">\n          <span>{{ parseTime(row.gmtModify) }}</span>\n        </template>\n      </el-table-column>\n      <el-table-column\n        label=\"状态\"\n        prop=\"status\"\n        width=\"100\"\n        align=\"center\"\n        :show-overflow-tooltip=\"true\"\n      >\n        <template #default=\"{ row }\">\n          <dictionary-tag\n            code=\"navCommentStatus\"\n            :value=\"row.status\"\n          ></dictionary-tag>\n        </template>\n      </el-table-column>\n      <el-table-column\n        label=\"是否置顶\"\n        prop=\"hasSticky\"\n        width=\"100\"\n        align=\"center\"\n        :show-overflow-tooltip=\"true\"\n      >\n        <template #default=\"{ row }\">\n          <dictionary-tag code=\"yesNo\" :value=\"row.hasSticky\"></dictionary-tag>\n        </template>\n      </el-table-column>\n      <el-table-column\n        label=\"操作\"\n        class-name=\"small-padding fixed-width\"\n        width=\"200\"\n      >\n        <template #default=\"{ row }\">\n          <el-button\n            type=\"text\"\n            icon=\"Check\"\n            @click=\"handlePass(row)\"\n            v-permission=\"['nav:comment:pass']\"\n            >通过</el-button\n          >\n          <el-button\n            type=\"text\"\n            icon=\"Close\"\n            @click=\"handleReject(row)\"\n            v-permission=\"['nav:comment:reject']\"\n            >驳回</el-button\n          >\n          <el-button\n            type=\"text\"\n            icon=\"Delete\"\n            @click=\"handleDelete(row)\"\n            v-permission=\"['nav:comment:delete']\"\n            >删除</el-button\n          >\n          <el-button\n            type=\"text\"\n            icon=\"Top\"\n            @click=\"handleSticky(row)\"\n            v-permission=\"['nav:comment:sticky']\"\n            >置顶</el-button\n          >\n          <el-button\n            type=\"text\"\n            icon=\"Bottom\"\n            @click=\"handleCancelSticky(row)\"\n            v-permission=\"['nav:comment:sticky:cancle']\"\n            >取消置顶</el-button\n          >\n        </template>\n      </el-table-column>\n    </el-table>\n\n    <pagination\n      v-show=\"totalCount > 0\"\n      :total=\"totalCount\"\n      v-model:page=\"queryParams.pageNum\"\n      v-model:limit=\"queryParams.pageSize\"\n      @pagination=\"getPage\"\n    />\n\n    <!-- 驳回 -->\n    <comment-reject @fetch-data=\"handleQuery\" ref=\"rejectRef\" />\n  </div>\n</template>\n<script setup>\nimport { getCurrentInstance, ref } from 'vue'\n\nimport CommentReject from './components/CommentReject'\n\nimport { page, pass, sticky, cancleSticky, remove } from '@/api/nav/comment'\n\nconst { proxy } = getCurrentInstance()\n\nconst loading = ref(true)\nconst dataList = ref([])\nconst totalCount = ref(0)\nconst ids = ref([])\nconst multiple = ref(true)\n\nconst queryParams = ref({\n  pageNum: 1,\n  pageSize: 10,\n  commentContent: undefined,\n  status: '1',\n  orderColumn: 'hasSticky,gmtCreate,id',\n  orderType: 'asc,desc,desc'\n})\n\n/**\n * 获取数据\n */\nasync function getPage() {\n  loading.value = true\n  const {\n    data: { total, list }\n  } = await page(queryParams.value)\n  totalCount.value = parseInt(total)\n  dataList.value = list\n  loading.value = false\n}\n\n/**\n * 搜索\n */\nfunction handleQuery() {\n  getPage()\n}\n\n/**\n * 重置\n */\nfunction handleResetQuery() {\n  proxy.resetForm('queryFormRef')\n  handleQuery()\n}\n\n/**\n * 选择条数\n */\nfunction handleSelectionChange(selection) {\n  ids.value = selection.map((item) => item.id)\n  multiple.value = !selection.length\n}\n\n/**\n * 通过\n */\nasync function handlePass(row) {\n  const passIds = row.id || ids.value\n  if (passIds.length === 0) {\n    proxy.$modal.msgError('请选择需要通过的评论')\n    return\n  }\n  await pass(passIds)\n  proxy.$modal.msgSuccess('通过评论成功')\n  handleQuery()\n}\n\n/**\n * 驳回\n */\nasync function handleReject(row) {\n  let rejectIds = []\n  if (row.id) {\n    rejectIds.push(row.id)\n  } else {\n    rejectIds = ids.value\n  }\n  if (rejectIds.length === 0) {\n    proxy.$modal.msgError('请选择需要驳回的评论')\n    return\n  }\n  proxy.$refs.rejectRef.show(rejectIds)\n}\n\n/**\n * 置顶\n */\nasync function handleSticky(row) {\n  await sticky(row.id)\n  proxy.$modal.msgSuccess('置顶评论成功')\n  handleQuery()\n}\n\n/**\n * 取消置顶\n */\nasync function handleCancelSticky(row) {\n  await cancleSticky(row.id)\n  proxy.$modal.msgSuccess('取消置顶评论成功')\n  handleQuery()\n}\n\n/**\n * 删除按钮操作\n */\nasync function handleDelete(row) {\n  const deleteIds = row.id || ids.value\n  proxy.$modal\n    .confirm('是否确认删除评论编号为【' + deleteIds + '】的数据项?')\n    .then(async function () {\n      await remove(deleteIds)\n      proxy.$modal.msgSuccess('删除评论成功')\n      handleQuery()\n    })\n}\n\n/**\n * 初始化\n */\ngetPage()\n</script>\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/views/nav/components/SitePicture.vue",
    "content": "<template>\n  <div>\n    <el-image\n      :src=\"getPictureShowUrl(options.img)\"\n      class=\"site-image-cropper\"\n      @click=\"handleEditCropper\"\n    >\n      <template #error>\n        <el-image\n          class=\"site-card-logo\"\n          :src=\"getPictureShowUrl(defaultSiteImage)\"\n          @click=\"handleEditCropper\"\n        />\n      </template>\n    </el-image>\n\n    <el-dialog\n      v-model=\"visible\"\n      :title=\"title\"\n      width=\"800px\"\n      append-to-body\n      @opened=\"modalOpened\"\n      :before-close=\"beforeClose\"\n    >\n      <el-row>\n        <el-col :xs=\"24\" :md=\"12\" :style=\"{ height: '350px' }\">\n          <vue-cropper\n            ref=\"cropperRef\"\n            :img=\"getPictureShowUrl(options.img)\"\n            :info=\"true\"\n            :autoCrop=\"options.autoCrop\"\n            :autoCropWidth=\"options.autoCropWidth\"\n            :autoCropHeight=\"options.autoCropHeight\"\n            :fixedBox=\"options.fixedBox\"\n            @realTime=\"realTime\"\n            v-if=\"visibleCropper\"\n          />\n        </el-col>\n        <el-col :xs=\"24\" :md=\"12\" :style=\"{ height: '350px' }\">\n          <div class=\"image-upload-preview\">\n            <img\n              :src=\"getPictureShowUrl(options.previews.url)\"\n              :style=\"options.previews.img\"\n            />\n          </div>\n        </el-col>\n      </el-row>\n      <br />\n      <el-row>\n        <el-col :lg=\"2\" :md=\"2\">\n          <el-upload\n            action=\"#\"\n            accept=\".jpg,.png,.jpeg,.ico,.gif,.svg.tiff\"\n            :http-request=\"requestUpload\"\n            :show-file-list=\"false\"\n            :before-upload=\"beforeUpload\"\n          >\n            <el-button>\n              <span class=\"upload-btn\">上传</span>\n              <el-icon><UploadFilled /></el-icon>\n            </el-button>\n          </el-upload>\n        </el-col>\n        <el-col :lg=\"{ span: 1, offset: 2 }\" :md=\"2\">\n          <el-button icon=\"Plus\" @click=\"changeScale(1)\"></el-button>\n        </el-col>\n        <el-col :lg=\"{ span: 1, offset: 1 }\" :md=\"2\">\n          <el-button icon=\"Minus\" @click=\"changeScale(-1)\"></el-button>\n        </el-col>\n        <el-col :lg=\"{ span: 1, offset: 1 }\" :md=\"2\">\n          <el-button icon=\"RefreshLeft\" @click=\"rotateLeft()\"></el-button>\n        </el-col>\n        <el-col :lg=\"{ span: 1, offset: 1 }\" :md=\"2\">\n          <el-button icon=\"RefreshRight\" @click=\"rotateRight()\"></el-button>\n        </el-col>\n        <el-col :lg=\"{ span: 2, offset: 6 }\" :md=\"2\">\n          <el-button type=\"primary\" @click=\"handleSave\" :loading=\"editLoading\"\n            >提 交</el-button\n          >\n        </el-col>\n      </el-row>\n    </el-dialog>\n  </div>\n</template>\n<script setup>\nimport { getCurrentInstance, ref, watch } from 'vue'\nimport 'vue-cropper/dist/index.css'\nimport { VueCropper } from 'vue-cropper'\n\nimport { uploadSite } from '@/api/nav/picture'\n\nconst props = defineProps({\n  // 图片路径\n  path: {\n    type: String,\n    require: true\n  },\n  // 标题\n  title: {\n    type: String,\n    default: '修改图片'\n  }\n})\n\nconst { proxy } = getCurrentInstance()\n\nconst visible = ref(false)\nconst visibleCropper = ref(false)\nconst editLoading = ref(false)\n// 默认图片\nconst defaultSiteImage = ref('/profile/site/system/logo.jpg')\n\nconst options = ref({\n  // 裁剪图片的地址\n  img: props.path,\n  // 裁剪生成图片的质量\n  outputSize: 1,\n  // 裁剪生成图片的格式\n  outputType: 'png',\n  // 是否默认生成截图框\n  autoCrop: true,\n  // 默认生成截图框宽度\n  autoCropWidth: 200,\n  // 默认生成截图框高度\n  autoCropHeight: 200,\n  // 固定截图框大小 不允许改变\n  fixedBox: true,\n  // 预览数据\n  previews: {}\n})\n\n/**\n * 编辑图片显示\n */\nfunction handleEditCropper() {\n  visible.value = true\n}\n\n/**\n * 打开弹出层结束时的回调\n */\nfunction modalOpened() {\n  visibleCropper.value = true\n}\n\n/**\n * 手动关闭\n */\nfunction beforeClose() {\n  close()\n  options.value.img = props.path\n}\n\n/**\n * 关闭\n */\nfunction close() {\n  visible.value = false\n  visibleCropper.value = false\n}\n\n/**\n * 实时预览\n */\nfunction realTime(data) {\n  options.value.previews = data\n}\n\n/**\n * 向左旋转\n */\nfunction rotateLeft() {\n  proxy.$refs.cropperRef.rotateLeft()\n}\n\n/**\n * 向右旋转\n */\nfunction rotateRight() {\n  proxy.$refs.cropperRef.rotateRight()\n}\n\n/**\n * 图片缩放\n */\nfunction changeScale(num) {\n  num = num || 1\n  proxy.$refs.cropperRef.changeScale(num)\n}\n\n/**\n * 上传预处理\n */\nfunction beforeUpload(file) {\n  if (file.type.indexOf('image/') === -1) {\n    proxy.$modal.msgError(\n      '文件格式错误，请上传图片类型,如：JPG，PNG后缀的文件。'\n    )\n  } else {\n    const reader = new FileReader()\n    reader.readAsDataURL(file)\n    reader.onload = () => {\n      options.value.img = reader.result\n    }\n  }\n}\n/**\n * 上传图片\n */\nconst emit = defineEmits(null)\nfunction handleSave() {\n  proxy.$refs.cropperRef.getCropBlob(async (data) => {\n    const formData = new FormData()\n    formData.append('file', data)\n    editLoading.value = true\n    try {\n      const {\n        data: { filePath }\n      } = await uploadSite(formData)\n      const path = filePath\n      options.value.img = path\n      proxy.$modal.msgSuccess('上传成功')\n      emit('success', path)\n      close()\n    } finally {\n      editLoading.value = false\n    }\n  })\n}\n\nwatch(\n  () => props.path,\n  (newVal, oldVal) => {\n    options.value.img = newVal\n  }\n)\n</script>\n<style lang=\"scss\" scoped>\n.upload-btn {\n  margin-right: 10px;\n}\n</style>\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/views/nav/config/index.vue",
    "content": "<template>\n  <div class=\"app-container\">\n    <el-tabs type=\"border-card\">\n      <el-tab-pane label=\"关于本站\">\n        <el-form\n          v-loading=\"loading\"\n          :model=\"editForm\"\n          ref=\"editFormRef\"\n          label-width=\"80px\"\n          :rules=\"rules\"\n        >\n          <el-form-item label=\"邮箱\" prop=\"aboutSiteEmail\">\n            <el-input\n              v-model=\"editForm.aboutSiteEmail\"\n              placeholder=\"请输入邮箱\"\n            >\n            </el-input>\n          </el-form-item>\n          <el-form-item label=\"网站说明\" prop=\"aboutSiteDescription\">\n            <el-input\n              v-model=\"editForm.aboutSiteDescription\"\n              placeholder=\"请输入网站说明\"\n            >\n            </el-input>\n          </el-form-item>\n          <el-form-item label=\"内容\" prop=\"aboutSiteContent\">\n            <wang-editor v-model=\"editForm.aboutSiteContent\" />\n          </el-form-item>\n          <el-form-item>\n            <el-button\n              type=\"primary\"\n              v-permission=\"['nav:config:update']\"\n              class=\"button-save\"\n              @click=\"handleSave\"\n              :loading=\"editLoading\"\n              >保存</el-button\n            >\n          </el-form-item>\n        </el-form>\n      </el-tab-pane>\n    </el-tabs>\n  </div>\n</template>\n<script setup>\nimport { getCurrentInstance, ref } from 'vue'\nimport WangEditor from '@/components/WangEditor'\n\nimport { getConfig, update } from '@/api/nav/config'\n\nconst { proxy } = getCurrentInstance()\n\nconst loading = ref(true)\nconst editLoading = ref(false)\nconst editForm = ref({\n  id: undefined,\n  aboutSiteEmail: undefined,\n  aboutSiteDescription: undefined,\n  aboutSiteContent: undefined\n})\n\n/**\n * 获取数据\n */\nasync function getData() {\n  loading.value = true\n  const { data } = await getConfig()\n  if (data) {\n    const { id, aboutSiteEmail, aboutSiteDescription, aboutSiteContent } = data\n    editForm.value.id = id\n    editForm.value.aboutSiteEmail = aboutSiteEmail\n    editForm.value.aboutSiteDescription = aboutSiteDescription\n    editForm.value.aboutSiteContent = aboutSiteContent\n  }\n  loading.value = false\n}\n\n/**\n * 提交数据\n */\nfunction handleSave() {\n  proxy.$refs.editFormRef.validate(async (valid) => {\n    if (valid) {\n      editLoading.value = true\n      try {\n        // 修改\n        const { data } = await update(editForm.value)\n        proxy.$modal.msgSuccess('保存成功')\n        editForm.value.id = data\n      } finally {\n        editLoading.value = false\n      }\n    }\n  })\n}\n\ngetData()\n</script>\n\n<style lang=\"scss\" scoped>\n.app-container {\n  position: relative;\n  .button-save {\n    position: absolute;\n    right: 10px;\n    margin-top: 15px;\n  }\n}\n</style>\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/views/nav/site/components/SiteEdit.vue",
    "content": "<template>\n  <el-dialog\n    v-model=\"visible\"\n    :title=\"title\"\n    append-to-body\n    width=\"500px\"\n    :before-close=\"close\"\n  >\n    <el-form\n      :model=\"editForm\"\n      ref=\"editFormRef\"\n      label-width=\"80px\"\n      :rules=\"rules\"\n    >\n      <el-form-item label=\"所属分类\" prop=\"categoryId\">\n        <el-tree-select\n          style=\"width: 100%\"\n          v-model=\"editForm.categoryId\"\n          :data=\"categoryTree\"\n          :props=\"{ value: 'id', label: 'categoryName', children: 'children' }\"\n          value-key=\"id\"\n          placeholder=\"请选择所属分类\"\n          filterable\n          :render-after-expand=\"false\"\n          @keyup.enter=\"handleSave\"\n        />\n      </el-form-item>\n      <el-form-item label=\"网站名称\" prop=\"siteName\">\n        <el-input\n          v-model=\"editForm.siteName\"\n          placeholder=\"请输入网站名称\"\n          @keyup.enter=\"handleSave\"\n        />\n      </el-form-item>\n      <el-form-item label=\"网站描述\" prop=\"siteDescription\">\n        <el-input\n          v-model=\"editForm.siteDescription\"\n          placeholder=\"请输入网站描述\"\n          @keyup.enter=\"handleSave\"\n        />\n      </el-form-item>\n      <el-form-item label=\"网站地址\" prop=\"siteUrl\">\n        <el-input\n          v-model=\"editForm.siteUrl\"\n          placeholder=\"请输入网站地址\"\n          @keyup.enter=\"handleSave\"\n        />\n      </el-form-item>\n      <el-form-item label=\"排序\" prop=\"sort\">\n        <el-input-number\n          v-model=\"editForm.sort\"\n          controls-position=\"right\"\n          :min=\"0\"\n          @keyup.enter=\"handleSave\"\n        />\n      </el-form-item>\n      <el-form-item label=\"网站图片\" prop=\"sitePath\">\n        <el-row>\n          <el-col :span=\"24\">\n            <site-picture\n              :path=\"editForm.sitePath\"\n              @success=\"handleUploadSitePicture\"\n            />\n          </el-col>\n          <el-col :span=\"24\">\n            <div class=\"site-form-path-tip\">\n              提示：点击图片可进行裁剪和上传，点击采集自动获取该网站的图标\n            </div>\n            <div class=\"site-form-path-tip\">\n              输入网站地址，点击自动匹配即可匹配网站相关内容\n            </div>\n          </el-col>\n        </el-row>\n      </el-form-item>\n      <el-form-item label=\"状态\" prop=\"status\">\n        <el-radio-group v-model=\"editForm.status\" @keyup.enter=\"handleSave\">\n          <dictionary-radio code=\"commonStatus\" />\n        </el-radio-group>\n      </el-form-item>\n    </el-form>\n    <template #footer>\n      <div class=\"dialog-footer\">\n        <el-button @click=\"close\">取 消</el-button>\n        <el-button type=\"primary\" @click=\"handleSave\" :loading=\"editLoading\"\n          >确 定</el-button\n        >\n      </div>\n    </template>\n  </el-dialog>\n</template>\n<script setup>\nimport { getCurrentInstance, ref } from 'vue'\n\nimport SitePicture from '@/views/nav/components/SitePicture'\n\nimport { tree } from '@/api/nav/category'\nimport { add, getById, update, getMaxSortByCategoryId } from '@/api/nav/site'\n\nconst { proxy } = getCurrentInstance()\n\nconst visible = ref(false)\nconst title = ref('')\nconst categoryTree = ref([])\nconst editLoading = ref(false)\n// 默认图片\nconst defaultSiteImage = ref('/profile/site/system/logo.jpg')\nconst editForm = ref({\n  id: undefined,\n  categoryId: undefined,\n  siteName: undefined,\n  status: '1',\n  sort: 0,\n  sitePath: undefined\n})\n\nconst rules = ref({\n  categoryId: [\n    { required: true, message: '所属分类不能为空', trigger: 'blur' }\n  ],\n  siteName: [{ required: true, message: '网站名称不能为空', trigger: 'blur' }],\n  siteUrl: [{ required: true, message: '网站链接不能为空', trigger: 'blur' }],\n  sort: [{ required: true, message: '排序不能为空', trigger: 'blur' }],\n  status: [{ required: true, message: '状态不能为空', trigger: 'blur' }]\n})\n\n/**\n * 显示弹窗\n */\nasync function show(id, categoryId) {\n  visible.value = true\n  editForm.value.categoryId = categoryId\n  if (id) {\n    title.value = '修改网站'\n    // 获取菜单信息\n    const { data } = await getById(id)\n    editForm.value = data\n    editForm.value.status = editForm.value.status + ''\n  } else {\n    title.value = '新增网站'\n    editForm.value.sitePath = defaultSiteImage.value\n    // 获取排序最大值\n    const { data } = await getMaxSortByCategoryId(editForm.value.categoryId)\n    editForm.value.sort = data ? data + 1 : 1\n    editForm.value.id = undefined\n  }\n  getTree()\n}\n\n/**\n * 获取分类树\n */\nasync function getTree() {\n  const { data } = await tree()\n  categoryTree.value = data\n}\n\n/**\n * 关闭\n */\nfunction close() {\n  visible.value = false\n  reset()\n}\n\n/**\n * 重置\n */\nfunction reset() {\n  proxy.resetForm('editFormRef')\n}\n\n/**\n * 图片裁剪上传成功回调\n */\nfunction handleUploadSitePicture(filePath) {\n  editForm.value.sitePath = filePath\n}\n\n/**\n * 提交数据\n */\nconst emit = defineEmits(null)\nfunction handleSave() {\n  proxy.$refs.editFormRef.validate(async (valid) => {\n    if (valid) {\n      editLoading.value = true\n      // 修改\n      if (editForm.value.id) {\n        try {\n          await update(editForm.value)\n          proxy.$modal.msgSuccess('修改网站成功')\n        } finally {\n          editLoading.value = false\n        }\n      } else {\n        // 新增\n        try {\n          await add(editForm.value)\n          proxy.$modal.msgSuccess('新增网站成功')\n        } finally {\n          editLoading.value = false\n        }\n      }\n      emit('fetch-data')\n      close()\n    }\n  })\n}\n\ndefineExpose({\n  show\n})\n</script>\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/views/nav/site/index.vue",
    "content": "<template>\n  <div class=\"app-container\">\n    <el-form\n      :model=\"queryParams\"\n      ref=\"queryFormRef\"\n      :inline=\"true\"\n      label-width=\"68px\"\n    >\n      <el-form-item label=\"名称描述\" prop=\"siteNameDescription\">\n        <el-input\n          v-model=\"queryParams.siteNameDescription\"\n          placeholder=\"请输入网站名称或描述\"\n          clearable\n          style=\"width: 240px\"\n          @keyup.enter=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"所属分类\" prop=\"categoryId\">\n        <el-tree-select\n          v-model=\"queryParams.categoryId\"\n          :data=\"categoryTree\"\n          :props=\"{ value: 'id', label: 'categoryName', children: 'children' }\"\n          value-key=\"id\"\n          placeholder=\"选择所属分类\"\n          @change=\"handleQuery\"\n          filterable\n          clearable\n        />\n      </el-form-item>\n      <el-form-item label=\"状态\" prop=\"status\">\n        <el-select\n          v-model=\"queryParams.status\"\n          placeholder=\"网站状态\"\n          @change=\"handleQuery\"\n          clearable\n          style=\"width: 240px\"\n        >\n          <dictionary-option code=\"commonStatus\" />\n        </el-select>\n      </el-form-item>\n      <el-form-item>\n        <el-button type=\"primary\" icon=\"Search\" @click=\"handleQuery\"\n          >搜索</el-button\n        >\n        <el-button icon=\"Refresh\" @click=\"handleResetQuery\">重置</el-button>\n      </el-form-item>\n    </el-form>\n\n    <el-row :gutter=\"10\" class=\"mb8\">\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"primary\"\n          icon=\"Plus\"\n          @click=\"handleAdd\"\n          v-permission=\"['system:user:add']\"\n          >新增</el-button\n        >\n      </el-col>\n    </el-row>\n\n    <div v-loading=\"loading\">\n      <el-row :gutter=\"24\">\n        <el-col\n          :sm=\"24 / showCount\"\n          :sx=\"24\"\n          class=\"site-card-col\"\n          v-for=\"site in dataList\"\n          :key=\"site.id\"\n          @mouseenter=\"handleSiteCardMouseEnter(site.id)\"\n          @mouseleave=\"handleSiteCardMouseLeave\"\n        >\n          <div v-show=\"isMouseSite(site.id)\">\n            <el-icon class=\"site-card-edit\" @click=\"handleUpdate(site)\">\n              <Edit />\n            </el-icon>\n            <el-icon class=\"site-card-close\" @click=\"handleDelete(site)\">\n              <Close />\n            </el-icon>\n          </div>\n          <div :class=\"isMouseSite(site.id) ? 'site-card-mouse' : ''\">\n            <site-card :site=\"site\" class=\"site-card\">\n              <template #card-footer>\n                <span class=\"card-footer-icon\">\n                  <el-icon><View /></el-icon>\n                  <span class=\"title\">{{ site.clickCount }}</span>\n                </span>\n                <span class=\"card-footer-icon\">\n                  <el-icon><Sort /></el-icon>\n                  <span class=\"title\">{{ site.sort }}</span>\n                </span>\n                <div class=\"belong-category\">\n                  所属分类：{{ site.categoryName }}\n                </div>\n                <div class=\"site-card-status-disable\" v-if=\"site.status === 2\">\n                  停 用\n                </div>\n              </template>\n            </site-card>\n          </div>\n        </el-col>\n      </el-row>\n    </div>\n\n    <el-empty v-if=\"totalCount == 0\" description=\"暂无数据\" />\n\n    <pagination\n      v-show=\"totalCount > 0\"\n      :total=\"totalCount\"\n      v-model:page=\"queryParams.pageNum\"\n      v-model:limit=\"queryParams.pageSize\"\n      @pagination=\"getPage\"\n      :page-sizes=\"[8, 12, 16, 20]\"\n    />\n\n    <!-- 新增和编辑 -->\n    <site-edit @fetch-data=\"handleQuery\" ref=\"editRef\"></site-edit>\n  </div>\n</template>\n<script setup>\nimport { computed, getCurrentInstance, ref } from 'vue'\nimport { useStore } from 'vuex'\n\nimport { page, remove } from '@/api/nav/site'\nimport { tree } from '@/api/nav/category'\n\nimport SiteEdit from './components/SiteEdit'\nimport SiteCard from '@/components/SiteCard'\n\nconst { proxy } = getCurrentInstance()\nconst store = useStore()\n\nconst queryParams = ref({\n  pageNum: 1,\n  pageSize: 8,\n  categoryId: undefined,\n  siteNameDescription: undefined,\n  status: undefined,\n  orderColumn: 'categoryId,sort,id',\n  orderType: 'asc,asc,asc'\n})\nconst loading = ref(true)\nconst dataList = ref([])\nconst totalCount = ref(0)\nconst categoryTree = ref([])\nconst id = ref()\nconst showCount = computed(() => store.getters['settings/showCount'])\n\n/**\n * 搜索\n */\nfunction handleQuery() {\n  getPage()\n}\n\n/**\n * 重置\n */\nfunction handleResetQuery() {\n  proxy.resetForm('queryFormRef')\n  handleQuery()\n}\n\n/**\n * 获取数据\n */\nasync function getPage() {\n  loading.value = true\n  const {\n    data: { total, list }\n  } = await page(queryParams.value)\n  totalCount.value = parseInt(total)\n  dataList.value = list\n  loading.value = false\n}\n\n/**\n * 获取分类树\n */\nasync function getCategoryTree() {\n  const { data } = await tree()\n  categoryTree.value = data\n}\n\n/**\n * 鼠标放入网站中\n */\nfunction handleSiteCardMouseEnter(siteId) {\n  id.value = siteId\n}\n\n/**\n * 鼠标离开网站中\n */\nfunction handleSiteCardMouseLeave() {\n  id.value = undefined\n}\n\n/**\n * 鼠标是否在当前网站\n */\nfunction isMouseSite(siteId) {\n  return id.value === siteId\n}\n\n/**\n * 新增\n */\nfunction handleAdd(row) {\n  proxy.$refs.editRef.show(undefined, queryParams.value.categoryId)\n}\n\n/**\n * 修改\n */\nfunction handleUpdate(row) {\n  proxy.$refs.editRef.show(row.id, queryParams.value.categoryId)\n}\n\n/**\n * 删除\n */\nasync function handleDelete(row) {\n  proxy.$modal\n    .confirm('是否确认删除网站名称为【' + row.siteName + '】的数据项？')\n    .then(async function () {\n      await remove(row.id)\n      proxy.$modal.msgSuccess('删除网站成功')\n      handleQuery()\n    })\n}\n\ngetPage()\ngetCategoryTree()\n</script>\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/views/system/api/category.vue",
    "content": "<template>\n  <div class=\"app-container\">\n    <el-form\n      :model=\"queryParams\"\n      ref=\"queryFormRef\"\n      :inline=\"true\"\n      label-width=\"68px\"\n    >\n      <el-form-item label=\"分类名称\" prop=\"categoryName\">\n        <el-input\n          v-model=\"queryParams.categoryName\"\n          placeholder=\"请输入接口分类名称\"\n          clearable\n          style=\"width: 240px\"\n          @keyup.enter=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"状态\" prop=\"status\">\n        <el-select\n          v-model=\"queryParams.status\"\n          placeholder=\"分类状态\"\n          @change=\"handleQuery\"\n          clearable\n        >\n          <dictionary-option code=\"commonStatus\" /> </el-select\n      ></el-form-item>\n      <el-form-item>\n        <el-button type=\"primary\" icon=\"Search\" @click=\"handleQuery\"\n          >搜索</el-button\n        >\n        <el-button icon=\"Refresh\" @click=\"handleResetQuery\">重置</el-button>\n      </el-form-item>\n    </el-form>\n    <el-row :gutter=\"10\" class=\"mb8\">\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"primary\"\n          icon=\"Plus\"\n          @click=\"handleAdd\"\n          v-permission=\"['system:apiCategory:add']\"\n          >新增</el-button\n        >\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"warning\"\n          icon=\"Edit\"\n          :disabled=\"single\"\n          @click=\"handleUpdate\"\n          v-permission=\"['system:apiCategory:update']\"\n          >修改</el-button\n        >\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"danger\"\n          icon=\"Delete\"\n          :disabled=\"multiple\"\n          @click=\"handleDelete\"\n          v-permission=\"['system:apiCategory:delete']\"\n          >删除</el-button\n        >\n      </el-col>\n    </el-row>\n\n    <el-table\n      border\n      v-loading=\"loading\"\n      :data=\"dataList\"\n      @selection-change=\"handleSelectionChange\"\n    >\n      <el-table-column type=\"selection\" width=\"55\" align=\"center\" />\n      <el-table-column label=\"分类编号\" prop=\"id\" width=\"180\" align=\"center\" />\n      <el-table-column\n        label=\"分类名称\"\n        prop=\"categoryName\"\n        :show-overflow-tooltip=\"true\"\n        align=\"center\"\n      >\n        <template #default=\"{ row }\">\n          <router-link :to=\"'/system/api/data/' + row.id\" class=\"link-type\">\n            <span>{{ row.categoryName }}</span>\n          </router-link>\n        </template>\n      </el-table-column>\n      <el-table-column\n        label=\"显示顺序\"\n        prop=\"sort\"\n        width=\"100\"\n        align=\"center\"\n      />\n      <el-table-column label=\"状态\" align=\"center\" prop=\"status\" width=\"100\">\n        <template #default=\"{ row }\">\n          <dictionary-tag\n            code=\"commonStatus\"\n            :value=\"row.status\"\n          ></dictionary-tag>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"备注\" align=\"center\" prop=\"remark\" />\n      <el-table-column\n        label=\"创建时间\"\n        align=\"center\"\n        prop=\"gmtCreate\"\n        width=\"160\"\n      >\n        <template #default=\"{ row }\">\n          <span>{{ parseTime(row.gmtCreate) }}</span>\n        </template>\n      </el-table-column>\n      <el-table-column\n        label=\"操作\"\n        align=\"center\"\n        class-name=\"small-padding fixed-width\"\n        width=\"160\"\n      >\n        <template #default=\"{ row }\">\n          <el-button\n            type=\"text\"\n            icon=\"Edit\"\n            @click=\"handleUpdate(row)\"\n            v-permission=\"['system:apiCategory:update']\"\n            >修改</el-button\n          >\n          <el-button\n            type=\"text\"\n            icon=\"Delete\"\n            @click=\"handleDelete(row)\"\n            v-permission=\"['system:apiCategory:delete']\"\n            >删除</el-button\n          >\n        </template>\n      </el-table-column>\n    </el-table>\n\n    <pagination\n      v-show=\"totalCount > 0\"\n      :total=\"totalCount\"\n      v-model:page=\"queryParams.pageNum\"\n      v-model:limit=\"queryParams.pageSize\"\n      @pagination=\"getPage\"\n    />\n\n    <!-- 新增和编辑 -->\n    <edit @fetch-data=\"handleQuery\" ref=\"editRef\"></edit>\n  </div>\n</template>\n<script setup>\nimport { getCurrentInstance, ref } from 'vue'\n\nimport Edit from './components/ApiCategoryEdit'\n\nimport { page, remove } from '@/api/system/api/category'\n\nconst { proxy } = getCurrentInstance()\n\nconst loading = ref(true)\nconst dataList = ref([])\nconst totalCount = ref(0)\nconst ids = ref([])\nconst single = ref(true)\nconst multiple = ref(true)\n\nconst queryParams = ref({\n  pageNum: 1,\n  pageSize: 10,\n  categoryName: undefined,\n  status: undefined\n})\n\n/**\n * 获取数据\n */\nasync function getPage() {\n  loading.value = true\n  const {\n    data: { total, list }\n  } = await page(queryParams.value)\n  totalCount.value = parseInt(total)\n  dataList.value = list\n  loading.value = false\n}\n\n/**\n * 搜索\n */\nfunction handleQuery() {\n  getPage()\n}\n\n/**\n * 重置\n */\nfunction handleResetQuery() {\n  proxy.resetForm('queryFormRef')\n  handleQuery()\n}\n\n/**\n * 选择条数\n */\nfunction handleSelectionChange(selection) {\n  ids.value = selection.map((item) => item.id)\n  single.value = selection.length !== 1\n  multiple.value = !selection.length\n}\n\n/**\n * 新增\n */\nfunction handleAdd() {\n  proxy.$refs.editRef.show(undefined)\n}\n\n/**\n * 修改\n */\nfunction handleUpdate(row) {\n  const id = row.id || ids.value\n  proxy.$refs.editRef.show(id)\n}\n\n/**\n * 删除按钮操作\n */\nasync function handleDelete(row) {\n  const deleteIds = row.id || ids.value\n  proxy.$modal\n    .confirm('是否确认删除分类编号为【' + deleteIds + '】的数据项?')\n    .then(async function () {\n      await remove(deleteIds)\n      proxy.$modal.msgSuccess('删除分类成功')\n      handleQuery()\n    })\n}\n\n/**\n * 初始化\n */\ngetPage()\n</script>\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/views/system/api/components/ApiCategoryEdit.vue",
    "content": "<template>\n  <el-dialog\n    v-model=\"visible\"\n    :title=\"title\"\n    width=\"600px\"\n    append-to-body\n    :before-close=\"close\"\n  >\n    <el-form\n      :model=\"editForm\"\n      ref=\"editFormRef\"\n      label-width=\"80px\"\n      :rules=\"rules\"\n    >\n      <el-form-item label=\"分类名称\" prop=\"categoryName\">\n        <el-input\n          v-model=\"editForm.categoryName\"\n          placeholder=\"请输入分类名称\"\n          @keyup.enter=\"handleSave\"\n        />\n      </el-form-item>\n      <el-form-item label=\"顺序\" prop=\"sort\">\n        <el-input-number\n          v-model=\"editForm.sort\"\n          controls-position=\"right\"\n          :min=\"0\"\n        />\n      </el-form-item>\n      <el-form-item label=\"状态\" prop=\"status\">\n        <el-radio-group v-model=\"editForm.status\">\n          <dictionary-radio code=\"commonStatus\" />\n        </el-radio-group>\n      </el-form-item>\n\n      <el-form-item label=\"备注\" prop=\"remark\">\n        <el-input\n          v-model=\"editForm.remark\"\n          type=\"textarea\"\n          placeholder=\"请输入内容\"\n        ></el-input>\n      </el-form-item>\n    </el-form>\n\n    <template #footer>\n      <div class=\"dialog-footer\">\n        <el-button @click=\"close\">取 消</el-button>\n        <el-button type=\"primary\" @click=\"handleSave\">确 定</el-button>\n      </div>\n    </template>\n  </el-dialog>\n</template>\n<script setup>\nimport { getCurrentInstance, ref } from 'vue'\n\nimport { getById, add, update, getMaxSort } from '@/api/system/api/category'\n\nconst { proxy } = getCurrentInstance()\n\nconst visible = ref(false)\nconst title = ref('')\nconst rules = ref({\n  categoryName: [\n    { required: true, message: '分类名称不能为空', trigger: 'blur' }\n  ],\n  sort: [{ required: true, message: '顺序不能为空', trigger: 'blur' }],\n  status: [{ required: true, message: '状态不能为空', trigger: 'blur' }]\n})\nconst editForm = ref({\n  sort: 1,\n  status: '1',\n  menuIds: []\n})\n\n/**\n * 显示弹窗\n */\nasync function show(id) {\n  visible.value = true\n  if (id) {\n    title.value = '修改分类'\n    // 获取分类信息\n    const { data } = await getById(id)\n    editForm.value = data\n    editForm.value.status = editForm.value.status + ''\n  } else {\n    title.value = '新增分类'\n    editForm.value.id = undefined\n    const { data } = await getMaxSort()\n    editForm.value.sort = data ? data + 1 : 1\n  }\n}\n\n/**\n * 关闭\n */\nfunction close() {\n  visible.value = false\n  reset()\n}\n\n/**\n * 重置\n */\nfunction reset() {\n  proxy.resetForm('editFormRef')\n}\n\n/**\n * 提交数据\n */\nconst emit = defineEmits(null)\nfunction handleSave() {\n  proxy.$refs.editFormRef.validate(async (valid) => {\n    if (valid) {\n      // 修改\n      if (editForm.value.id) {\n        await update(editForm.value)\n        proxy.$modal.msgSuccess('修改分类成功')\n      } else {\n        // 新增\n        await add(editForm.value)\n        proxy.$modal.msgSuccess('新增分类成功')\n      }\n      emit('fetch-data')\n      close()\n    }\n  })\n}\n\ndefineExpose({\n  show\n})\n</script>\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/views/system/api/components/ApiEdit.vue",
    "content": "<template>\n  <el-dialog\n    v-model=\"visible\"\n    :title=\"title\"\n    width=\"600px\"\n    append-to-body\n    :before-close=\"close\"\n  >\n    <el-form\n      :model=\"editForm\"\n      ref=\"editFormRef\"\n      label-width=\"80px\"\n      :rules=\"rules\"\n    >\n      <el-form-item label=\"所属分类\">\n        <el-input v-model=\"apiCategoryName\" disabled />\n      </el-form-item>\n      <el-form-item label=\"接口名称\" prop=\"apiName\">\n        <el-input\n          v-model=\"editForm.apiName\"\n          placeholder=\"请输入接口名称\"\n          @keyup.enter=\"handleSave\"\n        />\n      </el-form-item>\n      <el-form-item label=\"接口地址\" prop=\"apiUrl\">\n        <el-input\n          v-model=\"editForm.apiUrl\"\n          placeholder=\"请输入接口地址\"\n          @keyup.enter=\"handleSave\"\n        />\n      </el-form-item>\n      <el-form-item label=\"请求方式\" prop=\"apiMethod\">\n        <el-select v-model=\"editForm.apiMethod\" placeholder=\"请求方式\">\n          <dictionary-option code=\"apiRequestMethod\" />\n        </el-select>\n      </el-form-item>\n      <el-form-item label=\"顺序\" prop=\"sort\">\n        <el-input-number\n          v-model=\"editForm.sort\"\n          controls-position=\"right\"\n          :min=\"0\"\n        />\n      </el-form-item>\n      <el-form-item label=\"状态\" prop=\"status\">\n        <el-radio-group v-model=\"editForm.status\">\n          <dictionary-radio code=\"commonStatus\" />\n        </el-radio-group>\n      </el-form-item>\n\n      <el-form-item label=\"备注\" prop=\"remark\">\n        <el-input\n          v-model=\"editForm.remark\"\n          type=\"textarea\"\n          placeholder=\"请输入内容\"\n        ></el-input>\n      </el-form-item>\n    </el-form>\n\n    <template #footer>\n      <div class=\"dialog-footer\">\n        <el-button @click=\"close\">取 消</el-button>\n        <el-button type=\"primary\" @click=\"handleSave\">确 定</el-button>\n      </div>\n    </template>\n  </el-dialog>\n</template>\n<script setup>\nimport { getCurrentInstance, ref } from 'vue'\n\nimport {\n  getById,\n  add,\n  update,\n  getMaxSortByCategoryId\n} from '@/api/system/api/api'\n\nconst { proxy } = getCurrentInstance()\n\nconst visible = ref(false)\nconst title = ref('')\nconst rules = ref({\n  apiName: [{ required: true, message: '接口名称不能为空', trigger: 'blur' }],\n  apiUrl: [{ required: true, message: '接口地址不能为空', trigger: 'blur' }],\n  apiMethod: [{ required: true, message: '请选择请求方式', trigger: 'blur' }],\n  sort: [{ required: true, message: '顺序不能为空', trigger: 'blur' }],\n  status: [{ required: true, message: '状态不能为空', trigger: 'blur' }]\n})\nconst editForm = ref({\n  sort: 1,\n  status: '1',\n  apiCategoryId: undefined,\n  apiMethod: 'GET'\n})\nconst apiCategoryName = ref('')\n\n/**\n * 显示弹窗\n */\nasync function show(id, category) {\n  visible.value = true\n  editForm.value.apiCategoryId = category.id\n  apiCategoryName.value = category.categoryName\n  if (id) {\n    title.value = '修改接口'\n    // 获取接口信息\n    const { data } = await getById(id)\n    editForm.value = data\n    editForm.value.status = editForm.value.status + ''\n  } else {\n    title.value = '新增接口'\n    editForm.value.id = undefined\n    const { data } = await getMaxSortByCategoryId(editForm.value.apiCategoryId)\n    editForm.value.sort = data ? data + 1 : 1\n  }\n}\n\n/**\n * 关闭\n */\nfunction close() {\n  visible.value = false\n  reset()\n}\n\n/**\n * 重置\n */\nfunction reset() {\n  proxy.resetForm('editFormRef')\n}\n\n/**\n * 提交数据\n */\nconst emit = defineEmits(null)\nfunction handleSave() {\n  proxy.$refs.editFormRef.validate(async (valid) => {\n    if (valid) {\n      // 修改\n      if (editForm.value.id) {\n        await update(editForm.value)\n        proxy.$modal.msgSuccess('修改接口成功')\n      } else {\n        // 新增\n        await add(editForm.value)\n        proxy.$modal.msgSuccess('新增接口成功')\n      }\n      emit('fetch-data')\n      close()\n    }\n  })\n}\n\ndefineExpose({\n  show\n})\n</script>\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/views/system/api/index.vue",
    "content": "<template>\n  <div class=\"app-container\">\n    <el-form\n      :model=\"queryParams\"\n      ref=\"queryFormRef\"\n      :inline=\"true\"\n      label-width=\"68px\"\n    >\n      <el-form-item label=\"接口分类\" prop=\"apiCategoryId\">\n        <el-select\n          style=\"width: 240px\"\n          v-model=\"queryParams.apiCategoryId\"\n          placeholder=\"所属分类\"\n          @change=\"handleQuery\"\n        >\n          <el-option\n            v-for=\"(category, index) in dataCategoryList\"\n            :key=\"index\"\n            :label=\"category.categoryName\"\n            :value=\"category.id\"\n          />\n        </el-select>\n      </el-form-item>\n      <el-form-item label=\"接口名称\" prop=\"apiName\">\n        <el-input\n          v-model=\"queryParams.apiName\"\n          placeholder=\"请输入接口名称\"\n          clearable\n          style=\"width: 240px\"\n          @keyup.enter=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"接口地址\" prop=\"apiUrl\">\n        <el-input\n          v-model=\"queryParams.apiUrl\"\n          placeholder=\"请输入接口地址\"\n          clearable\n          style=\"width: 240px\"\n          @keyup.enter=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"请求方式\" prop=\"apiMethod\">\n        <el-select\n          v-model=\"queryParams.apiMethod\"\n          placeholder=\"请求方式\"\n          @change=\"handleQuery\"\n          style=\"width: 240px\"\n          clearable\n        >\n          <dictionary-option code=\"apiRequestMethod\" />\n        </el-select>\n      </el-form-item>\n      <el-form-item label=\"状态\" prop=\"status\">\n        <el-select\n          style=\"width: 240px\"\n          v-model=\"queryParams.status\"\n          placeholder=\"分类状态\"\n          @change=\"handleQuery\"\n          clearable\n        >\n          <dictionary-option code=\"commonStatus\" /> </el-select\n      ></el-form-item>\n      <el-form-item>\n        <el-button type=\"primary\" icon=\"Search\" @click=\"handleQuery\"\n          >搜索</el-button\n        >\n        <el-button icon=\"Refresh\" @click=\"handleResetQuery\">重置</el-button>\n      </el-form-item>\n    </el-form>\n    <el-row :gutter=\"10\" class=\"mb8\">\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"primary\"\n          icon=\"Plus\"\n          @click=\"handleAdd\"\n          v-permission=\"['system:api:add']\"\n          >新增</el-button\n        >\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"warning\"\n          icon=\"Edit\"\n          :disabled=\"single\"\n          @click=\"handleUpdate\"\n          v-permission=\"['system:api:update']\"\n          >修改</el-button\n        >\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"danger\"\n          icon=\"Delete\"\n          :disabled=\"multiple\"\n          @click=\"handleDelete\"\n          v-permission=\"['system:api:delete']\"\n          >删除</el-button\n        >\n      </el-col>\n    </el-row>\n\n    <el-table\n      border\n      v-loading=\"loading\"\n      :data=\"dataList\"\n      @selection-change=\"handleSelectionChange\"\n    >\n      <el-table-column type=\"selection\" width=\"55\" align=\"center\" />\n      <el-table-column label=\"接口编号\" prop=\"id\" width=\"180\" align=\"center\" />\n      <el-table-column\n        label=\"接口名称\"\n        prop=\"apiName\"\n        :show-overflow-tooltip=\"true\"\n      />\n      <el-table-column\n        label=\"接口地址\"\n        prop=\"apiUrl\"\n        :show-overflow-tooltip=\"true\"\n      />\n      <el-table-column label=\"请求方式\" prop=\"apiMethod\" width=\"100\">\n        <template #default=\"{ row }\">\n          <dictionary-tag\n            code=\"apiRequestMethod\"\n            :value=\"row.apiMethod\"\n          ></dictionary-tag>\n        </template>\n      </el-table-column>\n      <el-table-column\n        label=\"显示顺序\"\n        prop=\"sort\"\n        width=\"100\"\n        align=\"center\"\n      />\n      <el-table-column label=\"状态\" align=\"center\" prop=\"status\" width=\"100\">\n        <template #default=\"{ row }\">\n          <dictionary-tag\n            code=\"commonStatus\"\n            :value=\"row.status\"\n          ></dictionary-tag>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"备注\" align=\"center\" prop=\"remark\" />\n      <el-table-column\n        label=\"创建时间\"\n        align=\"center\"\n        prop=\"gmtCreate\"\n        width=\"160\"\n      >\n        <template #default=\"{ row }\">\n          <span>{{ parseTime(row.gmtCreate) }}</span>\n        </template>\n      </el-table-column>\n      <el-table-column\n        label=\"操作\"\n        align=\"center\"\n        class-name=\"small-padding fixed-width\"\n        width=\"160\"\n      >\n        <template #default=\"{ row }\">\n          <el-button\n            type=\"text\"\n            icon=\"Edit\"\n            @click=\"handleUpdate(row)\"\n            v-permission=\"['system:api:update']\"\n            >修改</el-button\n          >\n          <el-button\n            type=\"text\"\n            icon=\"Delete\"\n            @click=\"handleDelete(row)\"\n            v-permission=\"['system:api:delete']\"\n            >删除</el-button\n          >\n        </template>\n      </el-table-column>\n    </el-table>\n\n    <pagination\n      v-show=\"totalCount > 0\"\n      :total=\"totalCount\"\n      v-model:page=\"queryParams.pageNum\"\n      v-model:limit=\"queryParams.pageSize\"\n      @pagination=\"getPage\"\n    />\n\n    <!-- 新增和编辑 -->\n    <edit @fetch-data=\"handleQuery\" ref=\"editRef\"></edit>\n  </div>\n</template>\n<script setup>\nimport { getCurrentInstance, ref } from 'vue'\n\nimport Edit from './components/ApiEdit'\n\nimport { page, remove } from '@/api/system/api/api'\nimport { list as listCategory } from '@/api/system/api/category'\n\nimport { useRouter } from 'vue-router'\n\nconst { proxy } = getCurrentInstance()\n\nconst router = useRouter()\n\nconst loading = ref(true)\nconst dataList = ref([])\nconst totalCount = ref(0)\nconst dataCategoryList = ref([])\nconst ids = ref([])\nconst single = ref(true)\nconst multiple = ref(true)\n\nconst queryParams = ref({\n  pageNum: 1,\n  pageSize: 10,\n  apiCategoryId: router.currentRoute.value.params.id,\n  apiName: undefined,\n  status: undefined\n})\n\n/**\n * 获取数据\n */\nasync function getPage() {\n  loading.value = true\n  const {\n    data: { total, list }\n  } = await page(queryParams.value)\n  totalCount.value = parseInt(total)\n  dataList.value = list\n  loading.value = false\n}\n\n/**\n * 获取分类列表\n */\nasync function getCategoryList() {\n  const { data } = await listCategory()\n  dataCategoryList.value = data\n}\n\n/**\n * 搜索\n */\nfunction handleQuery() {\n  getPage()\n}\n\n/**\n * 重置\n */\nfunction handleResetQuery() {\n  proxy.resetForm('queryFormRef')\n  handleQuery()\n}\n\n/**\n * 选择条数\n */\nfunction handleSelectionChange(selection) {\n  ids.value = selection.map((item) => item.id)\n  single.value = selection.length !== 1\n  multiple.value = !selection.length\n}\n\n/**\n * 新增\n */\nfunction handleAdd() {\n  const category = dataCategoryList.value.filter(\n    (item) => queryParams.value.apiCategoryId === item.id\n  )\n  proxy.$refs.editRef.show(undefined, category[0])\n}\n\n/**\n * 修改\n */\nfunction handleUpdate(row) {\n  const id = row.id || ids.value\n  const category = dataCategoryList.value.filter(\n    (item) => queryParams.value.apiCategoryId === item.id\n  )\n  proxy.$refs.editRef.show(id, category[0])\n}\n\n/**\n * 删除按钮操作\n */\nasync function handleDelete(row) {\n  const deleteIds = row.id || ids.value\n  proxy.$modal\n    .confirm('是否确认删除接口编号为【' + deleteIds + '】的数据项?')\n    .then(async function () {\n      await remove(deleteIds)\n      proxy.$modal.msgSuccess('删除接口成功')\n      handleQuery()\n    })\n}\n\n/**\n * 初始化\n */\ngetPage()\ngetCategoryList()\n</script>\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/views/system/dictionary/components/DictionaryDataEdit.vue",
    "content": "<template>\n  <el-dialog\n    v-model=\"visible\"\n    :title=\"title\"\n    width=\"600px\"\n    append-to-body\n    :before-close=\"close\"\n  >\n    <el-form\n      :model=\"editForm\"\n      ref=\"editFormRef\"\n      label-width=\"80px\"\n      :rules=\"rules\"\n    >\n      <el-form-item label=\"所属字典\">\n        <el-input v-model=\"dictionaryName\" disabled />\n      </el-form-item>\n      <el-form-item label=\"字典标签\" prop=\"dictionaryLabel\">\n        <el-input\n          v-model=\"editForm.dictionaryLabel\"\n          placeholder=\"请输入字典标签\"\n        />\n      </el-form-item>\n      <el-form-item label=\"字典值\" prop=\"dictionaryValue\">\n        <el-input\n          v-model=\"editForm.dictionaryValue\"\n          placeholder=\"请输入字典值\"\n        />\n      </el-form-item>\n      <el-form-item label=\"样式类型\" prop=\"classType\">\n        <el-select\n          v-model=\"editForm.classType\"\n          placeholder=\"样式类型\"\n          clearable\n        >\n          <dictionary-option code=\"dictionaryClassType\" />\n        </el-select>\n      </el-form-item>\n      <el-form-item label=\"顺序\" prop=\"sort\">\n        <el-input-number\n          v-model=\"editForm.sort\"\n          controls-position=\"right\"\n          :min=\"0\"\n        />\n      </el-form-item>\n      <el-form-item label=\"状态\" prop=\"status\">\n        <el-radio-group v-model=\"editForm.status\">\n          <dictionary-radio code=\"commonStatus\" />\n        </el-radio-group>\n      </el-form-item>\n\n      <el-form-item label=\"备注\" prop=\"remark\">\n        <el-input\n          v-model=\"editForm.remark\"\n          type=\"textarea\"\n          placeholder=\"请输入内容\"\n        ></el-input>\n      </el-form-item>\n    </el-form>\n\n    <template #footer>\n      <div class=\"dialog-footer\">\n        <el-button @click=\"close\">取 消</el-button>\n        <el-button type=\"primary\" @click=\"handleSave\">确 定</el-button>\n      </div>\n    </template>\n  </el-dialog>\n</template>\n<script setup>\nimport { getCurrentInstance, ref } from 'vue'\n\nimport {\n  getById,\n  add,\n  update,\n  getMaxSortByDictionaryId\n} from '@/api/system/dictionary/data'\n\nconst { proxy } = getCurrentInstance()\n\nconst visible = ref(false)\nconst title = ref('')\nconst rules = ref({\n  dictionaryLabel: [\n    { required: true, message: '字典标签不能为空', trigger: 'blur' }\n  ],\n  dictionaryValue: [\n    { required: true, message: '字典值不能为空', trigger: 'blur' }\n  ],\n  sort: [{ required: true, message: '顺序不能为空', trigger: 'blur' }],\n  status: [{ required: true, message: '状态不能为空', trigger: 'blur' }]\n})\nconst editForm = ref({\n  sort: 1,\n  status: '1',\n  dictionaryId: undefined,\n  classType: undefined\n})\nconst dictionaryName = ref('')\n\n/**\n * 显示弹窗\n */\nasync function show(id, dictionary) {\n  visible.value = true\n  editForm.value.dictionaryId = dictionary.id\n  dictionaryName.value = dictionary.dictionaryName\n  if (id) {\n    title.value = '修改字典数据'\n    // 获取字典数据\n    const { data } = await getById(id)\n    editForm.value = data\n    editForm.value.status = editForm.value.status + ''\n  } else {\n    title.value = '新增字典数据'\n    editForm.value.id = undefined\n    const { data } = await getMaxSortByDictionaryId(editForm.value.dictionaryId)\n    editForm.value.sort = data ? data + 1 : 1\n  }\n}\n\n/**\n * 关闭\n */\nfunction close() {\n  visible.value = false\n  reset()\n}\n\n/**\n * 重置\n */\nfunction reset() {\n  proxy.resetForm('editFormRef')\n}\n\n/**\n * 提交数据\n */\nconst emit = defineEmits(null)\nfunction handleSave() {\n  proxy.$refs.editFormRef.validate(async (valid) => {\n    if (valid) {\n      // 修改\n      if (editForm.value.id) {\n        await update(editForm.value)\n        proxy.$modal.msgSuccess('修改字典数据成功')\n      } else {\n        // 新增\n        await add(editForm.value)\n        proxy.$modal.msgSuccess('新增字典数据成功')\n      }\n      emit('fetch-data')\n      close()\n    }\n  })\n}\n\ndefineExpose({\n  show\n})\n</script>\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/views/system/dictionary/components/DictionaryEdit.vue",
    "content": "<template>\n  <el-dialog\n    v-model=\"visible\"\n    :title=\"title\"\n    width=\"600px\"\n    append-to-body\n    :before-close=\"close\"\n  >\n    <el-form\n      :model=\"editForm\"\n      ref=\"editFormRef\"\n      label-width=\"80px\"\n      :rules=\"rules\"\n    >\n      <el-form-item label=\"字典名称\" prop=\"dictionaryName\">\n        <el-input\n          v-model=\"editForm.dictionaryName\"\n          placeholder=\"请输入字典名称\"\n        />\n      </el-form-item>\n      <el-form-item label=\"字典编码\" prop=\"dictionaryCode\">\n        <el-input\n          v-model=\"editForm.dictionaryCode\"\n          placeholder=\"请输入字典编码\"\n        />\n      </el-form-item>\n      <el-form-item label=\"顺序\" prop=\"sort\">\n        <el-input-number\n          v-model=\"editForm.sort\"\n          controls-position=\"right\"\n          :min=\"0\"\n        />\n      </el-form-item>\n      <el-form-item label=\"状态\" prop=\"status\">\n        <el-radio-group v-model=\"editForm.status\">\n          <dictionary-radio code=\"commonStatus\" />\n        </el-radio-group>\n      </el-form-item>\n\n      <el-form-item label=\"备注\" prop=\"remark\">\n        <el-input\n          v-model=\"editForm.remark\"\n          type=\"textarea\"\n          placeholder=\"请输入内容\"\n        ></el-input>\n      </el-form-item>\n    </el-form>\n\n    <template #footer>\n      <div class=\"dialog-footer\">\n        <el-button @click=\"close\">取 消</el-button>\n        <el-button type=\"primary\" @click=\"handleSave\">确 定</el-button>\n      </div>\n    </template>\n  </el-dialog>\n</template>\n<script setup>\nimport { getCurrentInstance, ref } from 'vue'\n\nimport {\n  getById,\n  add,\n  update,\n  getMaxSort\n} from '@/api/system/dictionary/dictionary'\n\nconst { proxy } = getCurrentInstance()\n\nconst visible = ref(false)\nconst title = ref('')\nconst rules = ref({\n  dictionaryName: [\n    { required: true, message: '字典名称不能为空', trigger: 'blur' }\n  ],\n  dictionaryCode: [\n    { required: true, message: '字典编码不能为空', trigger: 'blur' }\n  ],\n  sort: [{ required: true, message: '顺序不能为空', trigger: 'blur' }],\n  status: [{ required: true, message: '状态不能为空', trigger: 'blur' }]\n})\nconst editForm = ref({\n  sort: 1,\n  status: '1',\n  menuIds: []\n})\n\n/**\n * 显示弹窗\n */\nasync function show(id) {\n  visible.value = true\n  if (id) {\n    title.value = '修改字典'\n    // 获取字典信息\n    const { data } = await getById(id)\n    editForm.value = data\n    editForm.value.status = editForm.value.status + ''\n  } else {\n    title.value = '新增字典'\n    editForm.value.id = undefined\n    const { data } = await getMaxSort()\n    editForm.value.sort = data ? data + 1 : 1\n  }\n}\n\n/**\n * 关闭\n */\nfunction close() {\n  visible.value = false\n  reset()\n}\n\n/**\n * 重置\n */\nfunction reset() {\n  proxy.resetForm('editFormRef')\n}\n\n/**\n * 提交数据\n */\nconst emit = defineEmits(null)\nfunction handleSave() {\n  proxy.$refs.editFormRef.validate(async (valid) => {\n    if (valid) {\n      // 修改\n      if (editForm.value.id) {\n        await update(editForm.value)\n        proxy.$modal.msgSuccess('修改字典成功')\n      } else {\n        // 新增\n        await add(editForm.value)\n        proxy.$modal.msgSuccess('新增字典成功')\n      }\n      emit('fetch-data')\n      close()\n    }\n  })\n}\n\ndefineExpose({\n  show\n})\n</script>\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/views/system/dictionary/data.vue",
    "content": "<template>\n  <div class=\"app-container\">\n    <el-form\n      :model=\"queryParams\"\n      ref=\"queryFormRef\"\n      :inline=\"true\"\n      label-width=\"68px\"\n    >\n      <el-form-item label=\"所属字典\" prop=\"dictionaryId\">\n        <el-select\n          style=\"width: 240px\"\n          v-model=\"queryParams.dictionaryId\"\n          placeholder=\"所属字典\"\n          @change=\"handleQuery\"\n        >\n          <el-option\n            v-for=\"(dictionary, index) in dataDictionaryList\"\n            :key=\"index\"\n            :label=\"dictionary.dictionaryName\"\n            :value=\"dictionary.id\"\n          />\n        </el-select>\n      </el-form-item>\n      <el-form-item label=\"字典标签\" prop=\"dictionaryLabel\">\n        <el-input\n          v-model=\"queryParams.dictionaryLabel\"\n          placeholder=\"请输入字典标签\"\n          clearable\n          style=\"width: 240px\"\n          @keyup.enter=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"状态\" prop=\"status\">\n        <el-select\n          style=\"width: 240px\"\n          v-model=\"queryParams.status\"\n          placeholder=\"字典数据状态\"\n          @change=\"handleQuery\"\n          clearable\n        >\n          <dictionary-option code=\"commonStatus\" /></el-select\n      ></el-form-item>\n      <el-form-item>\n        <el-button type=\"primary\" icon=\"Search\" @click=\"handleQuery\"\n          >搜索</el-button\n        >\n        <el-button icon=\"Refresh\" @click=\"handleResetQuery\">重置</el-button>\n      </el-form-item>\n    </el-form>\n    <el-row :gutter=\"10\" class=\"mb8\">\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"primary\"\n          icon=\"Plus\"\n          @click=\"handleAdd\"\n          v-permission=\"['system:dictionary:data:add']\"\n          >新增</el-button\n        >\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"warning\"\n          icon=\"Edit\"\n          :disabled=\"single\"\n          @click=\"handleUpdate\"\n          v-permission=\"['system:dictionary:data:update']\"\n          >修改</el-button\n        >\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"danger\"\n          icon=\"Delete\"\n          :disabled=\"multiple\"\n          @click=\"handleDelete\"\n          v-permission=\"['system:dictionary:data:delete']\"\n          >删除</el-button\n        >\n      </el-col>\n    </el-row>\n\n    <el-table\n      border\n      v-loading=\"loading\"\n      :data=\"dataList\"\n      @selection-change=\"handleSelectionChange\"\n    >\n      <el-table-column type=\"selection\" width=\"55\" align=\"center\" />\n      <el-table-column\n        label=\"字典数据编号\"\n        prop=\"id\"\n        width=\"180\"\n        align=\"center\"\n      />\n      <el-table-column\n        label=\"字典标签\"\n        prop=\"dictionaryLabel\"\n        :show-overflow-tooltip=\"true\"\n      />\n      <el-table-column\n        label=\"字典值\"\n        prop=\"dictionaryValue\"\n        :show-overflow-tooltip=\"true\"\n      />\n      <el-table-column\n        label=\"样式类型\"\n        prop=\"classType\"\n        :show-overflow-tooltip=\"true\"\n      >\n        <template #default=\"{ row }\">\n          <dictionary-tag\n            code=\"dictionaryClassType\"\n            :value=\"row.classType\"\n          ></dictionary-tag>\n        </template>\n      </el-table-column>\n      <el-table-column\n        label=\"显示顺序\"\n        prop=\"sort\"\n        width=\"100\"\n        align=\"center\"\n      />\n      <el-table-column label=\"状态\" align=\"center\" prop=\"status\" width=\"100\">\n        <template #default=\"{ row }\">\n          <dictionary-tag\n            code=\"commonStatus\"\n            :value=\"row.status\"\n          ></dictionary-tag>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"备注\" align=\"center\" prop=\"remark\" />\n      <el-table-column\n        label=\"创建时间\"\n        align=\"center\"\n        prop=\"gmtCreate\"\n        width=\"160\"\n      >\n        <template #default=\"{ row }\">\n          <span>{{ parseTime(row.gmtCreate) }}</span>\n        </template>\n      </el-table-column>\n      <el-table-column\n        label=\"操作\"\n        align=\"center\"\n        class-name=\"small-padding fixed-width\"\n        width=\"160\"\n      >\n        <template #default=\"{ row }\">\n          <el-button\n            type=\"text\"\n            icon=\"Edit\"\n            @click=\"handleUpdate(row)\"\n            v-permission=\"['system:dictionary:data:update']\"\n            >修改</el-button\n          >\n          <el-button\n            type=\"text\"\n            icon=\"Delete\"\n            @click=\"handleDelete(row)\"\n            v-permission=\"['system:dictionary:data:delete']\"\n            >删除</el-button\n          >\n        </template>\n      </el-table-column>\n    </el-table>\n\n    <pagination\n      v-show=\"totalCount > 0\"\n      :total=\"totalCount\"\n      v-model:page=\"queryParams.pageNum\"\n      v-model:limit=\"queryParams.pageSize\"\n      @pagination=\"getPage\"\n    />\n\n    <!-- 新增和编辑 -->\n    <edit @fetch-data=\"handleQuery\" ref=\"editRef\"></edit>\n  </div>\n</template>\n<script setup>\nimport { getCurrentInstance, ref } from 'vue'\n\nimport Edit from './components/DictionaryDataEdit'\n\nimport { page, remove } from '@/api/system/dictionary/data'\nimport { list as listDictionary } from '@/api/system/dictionary/dictionary'\n\nimport { useRouter } from 'vue-router'\n\nconst { proxy } = getCurrentInstance()\n\nconst router = useRouter()\n\nconst loading = ref(true)\nconst dataList = ref([])\nconst totalCount = ref(0)\nconst dataDictionaryList = ref([])\nconst ids = ref([])\nconst single = ref(true)\nconst multiple = ref(true)\n\nconst queryParams = ref({\n  pageNum: 1,\n  pageSize: 10,\n  dictionaryId: router.currentRoute.value.params.id,\n  dictionaryName: undefined,\n  status: undefined\n})\n\n/**\n * 获取数据\n */\nasync function getPage() {\n  loading.value = true\n  const {\n    data: { total, list }\n  } = await page(queryParams.value)\n  totalCount.value = parseInt(total)\n  dataList.value = list\n  loading.value = false\n}\n\n/**\n * 获取分类列表\n */\nasync function getDictionaryList() {\n  const { data } = await listDictionary()\n  dataDictionaryList.value = data\n}\n\n/**\n * 搜索\n */\nfunction handleQuery() {\n  getPage()\n}\n\n/**\n * 重置\n */\nfunction handleResetQuery() {\n  proxy.resetForm('queryFormRef')\n  handleQuery()\n}\n\n/**\n * 选择条数\n */\nfunction handleSelectionChange(selection) {\n  ids.value = selection.map((item) => item.id)\n  single.value = selection.length !== 1\n  multiple.value = !selection.length\n}\n\n/**\n * 新增\n */\nfunction handleAdd() {\n  const dictionary = dataDictionaryList.value.filter(\n    (item) => queryParams.value.dictionaryId === item.id\n  )\n  proxy.$refs.editRef.show(undefined, dictionary[0])\n}\n\n/**\n * 修改\n */\nfunction handleUpdate(row) {\n  const id = row.id || ids.value\n  const dictionary = dataDictionaryList.value.filter(\n    (item) => queryParams.value.dictionaryId === item.id\n  )\n  proxy.$refs.editRef.show(id, dictionary[0])\n}\n\n/**\n * 删除按钮操作\n */\nasync function handleDelete(row) {\n  const deleteIds = row.id || ids.value\n  proxy.$modal\n    .confirm('是否确认删除字典数据编号为【' + deleteIds + '】的数据项?')\n    .then(async function () {\n      await remove(deleteIds)\n      proxy.$modal.msgSuccess('删除字典数据成功')\n      handleQuery()\n    })\n}\n\n/**\n * 初始化\n */\ngetPage()\ngetDictionaryList()\n</script>\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/views/system/dictionary/index.vue",
    "content": "<template>\n  <div class=\"app-container\">\n    <el-form\n      :model=\"queryParams\"\n      ref=\"queryFormRef\"\n      :inline=\"true\"\n      label-width=\"68px\"\n    >\n      <el-form-item label=\"字典名称\" prop=\"dictionaryName\">\n        <el-input\n          v-model=\"queryParams.dictionaryName\"\n          placeholder=\"请输入字典名称\"\n          clearable\n          style=\"width: 240px\"\n          @keyup.enter=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"字典编码\" prop=\"dictionaryCode\">\n        <el-input\n          v-model=\"queryParams.dictionaryCode\"\n          placeholder=\"请输入字典编码\"\n          clearable\n          style=\"width: 240px\"\n          @keyup.enter=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"状态\" prop=\"status\">\n        <el-select\n          v-model=\"queryParams.status\"\n          placeholder=\"字典状态\"\n          @change=\"handleQuery\"\n          clearable\n        >\n          <dictionary-option code=\"commonStatus\" /> </el-select\n      ></el-form-item>\n      <el-form-item>\n        <el-button type=\"primary\" icon=\"Search\" @click=\"handleQuery\"\n          >搜索</el-button\n        >\n        <el-button icon=\"Refresh\" @click=\"handleResetQuery\">重置</el-button>\n      </el-form-item>\n    </el-form>\n    <el-row :gutter=\"10\" class=\"mb8\">\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"primary\"\n          icon=\"Plus\"\n          @click=\"handleAdd\"\n          v-permission=\"['system:dictionary:add']\"\n          >新增</el-button\n        >\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"warning\"\n          icon=\"Edit\"\n          :disabled=\"single\"\n          @click=\"handleUpdate\"\n          v-permission=\"['system:dictionary:update']\"\n          >修改</el-button\n        >\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"danger\"\n          icon=\"Delete\"\n          :disabled=\"multiple\"\n          @click=\"handleDelete\"\n          v-permission=\"['system:dictionary:delete']\"\n          >删除</el-button\n        >\n      </el-col>\n    </el-row>\n\n    <el-table\n      border\n      v-loading=\"loading\"\n      :data=\"dataList\"\n      @selection-change=\"handleSelectionChange\"\n    >\n      <el-table-column type=\"selection\" width=\"55\" align=\"center\" />\n      <el-table-column label=\"字典编号\" prop=\"id\" width=\"180\" align=\"center\" />\n      <el-table-column\n        label=\"字典名称\"\n        prop=\"dictionaryName\"\n        :show-overflow-tooltip=\"true\"\n      />\n      <el-table-column\n        label=\"字典编码\"\n        prop=\"dictionaryCode\"\n        :show-overflow-tooltip=\"true\"\n      >\n        <template #default=\"{ row }\">\n          <router-link\n            :to=\"'/system/dictionary/data/' + row.id\"\n            class=\"link-type\"\n          >\n            <span>{{ row.dictionaryCode }}</span>\n          </router-link>\n        </template>\n      </el-table-column>\n      <el-table-column\n        label=\"显示顺序\"\n        prop=\"sort\"\n        width=\"100\"\n        align=\"center\"\n      />\n      <el-table-column label=\"状态\" align=\"center\" prop=\"status\" width=\"100\">\n        <template #default=\"{ row }\">\n          <dictionary-tag\n            code=\"commonStatus\"\n            :value=\"row.status\"\n          ></dictionary-tag>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"备注\" align=\"center\" prop=\"remark\" />\n      <el-table-column\n        label=\"创建时间\"\n        align=\"center\"\n        prop=\"gmtCreate\"\n        width=\"160\"\n      >\n        <template #default=\"{ row }\">\n          <span>{{ parseTime(row.gmtCreate) }}</span>\n        </template>\n      </el-table-column>\n      <el-table-column\n        label=\"操作\"\n        align=\"center\"\n        class-name=\"small-padding fixed-width\"\n        width=\"160\"\n      >\n        <template #default=\"{ row }\">\n          <el-button\n            type=\"text\"\n            icon=\"Edit\"\n            @click=\"handleUpdate(row)\"\n            v-permission=\"['system:dictionary:update']\"\n            >修改</el-button\n          >\n          <el-button\n            type=\"text\"\n            icon=\"Delete\"\n            @click=\"handleDelete(row)\"\n            v-permission=\"['system:dictionary:delete']\"\n            >删除</el-button\n          >\n        </template>\n      </el-table-column>\n    </el-table>\n\n    <pagination\n      v-show=\"totalCount > 0\"\n      :total=\"totalCount\"\n      v-model:page=\"queryParams.pageNum\"\n      v-model:limit=\"queryParams.pageSize\"\n      @pagination=\"getPage\"\n    />\n\n    <!-- 新增和编辑 -->\n    <edit @fetch-data=\"handleQuery\" ref=\"editRef\"></edit>\n  </div>\n</template>\n<script setup>\nimport { getCurrentInstance, ref } from 'vue'\n\nimport Edit from './components/DictionaryEdit'\n\nimport { page, remove } from '@/api/system/dictionary/dictionary'\n\nconst { proxy } = getCurrentInstance()\n\nconst loading = ref(true)\nconst dataList = ref([])\nconst totalCount = ref(0)\nconst ids = ref([])\nconst single = ref(true)\nconst multiple = ref(true)\n\nconst queryParams = ref({\n  pageNum: 1,\n  pageSize: 10,\n  dictionaryName: undefined,\n  dictionaryCode: undefined,\n  status: undefined\n})\n\n/**\n * 获取数据\n */\nasync function getPage() {\n  loading.value = true\n  const {\n    data: { total, list }\n  } = await page(queryParams.value)\n  totalCount.value = parseInt(total)\n  dataList.value = list\n  loading.value = false\n}\n\n/**\n * 搜索\n */\nfunction handleQuery() {\n  getPage()\n}\n\n/**\n * 重置\n */\nfunction handleResetQuery() {\n  proxy.resetForm('queryFormRef')\n  handleQuery()\n}\n\n/**\n * 选择条数\n */\nfunction handleSelectionChange(selection) {\n  ids.value = selection.map((item) => item.id)\n  single.value = selection.length !== 1\n  multiple.value = !selection.length\n}\n\n/**\n * 新增\n */\nfunction handleAdd() {\n  proxy.$refs.editRef.show(undefined)\n}\n\n/**\n * 修改\n */\nfunction handleUpdate(row) {\n  const id = row.id || ids.value\n  proxy.$refs.editRef.show(id)\n}\n\n/**\n * 删除按钮操作\n */\nasync function handleDelete(row) {\n  const deleteIds = row.id || ids.value\n  proxy.$modal\n    .confirm('是否确认删除字典编号为【' + deleteIds + '】的数据项?')\n    .then(async function () {\n      await remove(deleteIds)\n      proxy.$modal.msgSuccess('删除字典成功')\n      handleQuery()\n    })\n}\n\n/**\n * 初始化\n */\ngetPage()\n</script>\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/views/system/log/login/index.vue",
    "content": "<template>\n  <div class=\"app-container\">\n    <el-form\n      :model=\"queryParams\"\n      ref=\"queryFormRef\"\n      :inline=\"true\"\n      label-width=\"68px\"\n    >\n      <el-form-item label=\"IP 地址\" prop=\"ipAddress\">\n        <el-input\n          v-model=\"queryParams.ipAddress\"\n          placeholder=\"请输入 IP 地址\"\n          clearable\n          style=\"width: 240px\"\n          @keyup.enter=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"用户名\" prop=\"username\">\n        <el-input\n          v-model=\"queryParams.username\"\n          placeholder=\"请输入用户名\"\n          clearable\n          style=\"width: 240px\"\n          @keyup.enter=\"handleQuery\"\n        />\n      </el-form-item>\n\n      <el-form-item label=\"状态\" prop=\"status\">\n        <el-select\n          v-model=\"queryParams.status\"\n          placeholder=\"登录状态\"\n          @change=\"handleQuery\"\n          clearable\n          style=\"width: 240px\"\n        >\n          <dictionary-option code=\"logLoginStatus\" />\n        </el-select>\n      </el-form-item>\n      <el-form-item label=\"登录时间\" style=\"width: 308px\">\n        <el-date-picker\n          v-model=\"dateRange\"\n          value-format=\"YYYY-MM-DD\"\n          format=\"YYYY-MM-DD\"\n          type=\"daterange\"\n          range-separator=\"-\"\n          start-placeholder=\"开始日期\"\n          end-placeholder=\"结束日期\"\n        ></el-date-picker>\n      </el-form-item>\n      <el-form-item>\n        <el-button type=\"primary\" icon=\"Search\" @click=\"handleQuery\"\n          >搜索</el-button\n        >\n        <el-button icon=\"Refresh\" @click=\"handleResetQuery\">重置</el-button>\n      </el-form-item>\n    </el-form>\n\n    <el-table\n      v-loading=\"loading\"\n      :data=\"dataList\"\n      border\n      @selection-change=\"handleSelectionChange\"\n    >\n      <el-table-column label=\"日志编号\" align=\"center\" prop=\"id\" width=\"180\" />\n      <el-table-column\n        label=\"用户名\"\n        prop=\"username\"\n        :show-overflow-tooltip=\"true\"\n      />\n      <el-table-column\n        label=\"登录 IP 地址\"\n        prop=\"ipAddress\"\n        :show-overflow-tooltip=\"true\"\n      />\n      <el-table-column\n        label=\"登录位置\"\n        prop=\"loginLocation\"\n        :show-overflow-tooltip=\"true\"\n      />\n      <el-table-column\n        label=\"操作系统\"\n        prop=\"operateSystem\"\n        :show-overflow-tooltip=\"true\"\n      />\n      <el-table-column\n        label=\"浏览器\"\n        prop=\"browserType\"\n        :show-overflow-tooltip=\"true\"\n      />\n      <el-table-column\n        label=\"登录状态\"\n        align=\"center\"\n        prop=\"userType\"\n        :show-overflow-tooltip=\"true\"\n      >\n        <template #default=\"{ row }\">\n          <dictionary-tag\n            code=\"logLoginStatus\"\n            :value=\"row.status\"\n          ></dictionary-tag>\n        </template>\n      </el-table-column>\n      <el-table-column\n        label=\"提示消息\"\n        prop=\"hintMessage\"\n        :show-overflow-tooltip=\"true\"\n      />\n\n      <el-table-column label=\"登录时间\" align=\"center\" width=\"160\">\n        <template #default=\"{ row }\">\n          <span>{{ parseTime(row.gmtLogin) }}</span>\n        </template>\n      </el-table-column>\n    </el-table>\n\n    <pagination\n      v-show=\"totalCount > 0\"\n      :total=\"totalCount\"\n      v-model:page=\"queryParams.pageNum\"\n      v-model:limit=\"queryParams.pageSize\"\n      @pagination=\"getPage\"\n    />\n  </div>\n</template>\n<script setup>\nimport { getCurrentInstance, ref } from 'vue'\n\nimport { page } from '@/api/system/log/login'\n\nconst { proxy } = getCurrentInstance()\n\nconst queryParams = ref({\n  pageNum: 1,\n  pageSize: 10,\n  username: undefined,\n  ipAddress: undefined,\n  status: undefined,\n  beginGmtLogin: undefined,\n  endGmtLogin: undefined,\n  orderColumn: 'gmtLogin,id',\n  orderType: 'desc,desc'\n})\n\nconst loading = ref(true)\nconst dataList = ref([])\nconst dateRange = ref([])\nconst totalCount = ref(0)\n\n/**\n * 搜索\n */\nfunction handleQuery() {\n  getPage()\n}\n\n/**\n * 重置\n */\nfunction handleResetQuery() {\n  proxy.resetForm('queryFormRef')\n  dateRange.value = undefined\n  handleQuery()\n}\n\n/**\n * 获取数据\n */\nasync function getPage() {\n  loading.value = true\n  queryParams.value.beginGmtLogin = undefined\n  queryParams.value.endGmtLogin = undefined\n  if (Array.isArray(dateRange.value) && dateRange.value.length === 2) {\n    queryParams.value.beginGmtLogin = dateRange.value[0] + ' 00:00:00'\n    queryParams.value.endGmtLogin = dateRange.value[1] + ' 23:59:59'\n  }\n  const {\n    data: { total, list }\n  } = await page(queryParams.value)\n  totalCount.value = parseInt(total)\n  dataList.value = list\n  loading.value = false\n}\n\n/**\n * 初始化\n */\ngetPage()\n</script>\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/views/system/log/operation/components/LogOperationDetail.vue",
    "content": "<template>\n  <el-dialog\n    v-model=\"visible\"\n    title=\"操作日志详情\"\n    width=\"60%\"\n    append-to-body\n    :before-close=\"close\"\n  >\n    <el-form :model=\"editForm\" label-width=\"100px\">\n      <el-row>\n        <el-col :span=\"12\">\n          <el-form-item label=\"模块名称：\"\n            >{{ editForm.moduleName }} /\n            {{\n              getDictionaryLabel(\n                'logOperationBusinessType',\n                editForm.businessType\n              )\n            }}</el-form-item\n          >\n        </el-col>\n        <el-col :span=\"12\">\n          <el-form-item label=\"操作用户：\"\n            >{{ editForm.nickName }}({{ editForm.username }})</el-form-item\n          >\n        </el-col>\n        <el-col :span=\"12\">\n          <el-form-item label=\"请求地址：\">{{\n            editForm.requestUrl\n          }}</el-form-item>\n        </el-col>\n        <el-col :span=\"12\">\n          <el-form-item label=\"请求方式：\">{{\n            editForm.requestMethod\n          }}</el-form-item>\n        </el-col>\n        <el-col :span=\"24\">\n          <el-form-item label=\"类方法：\">{{\n            editForm.classMethod\n          }}</el-form-item>\n        </el-col>\n        <el-col :span=\"24\">\n          <el-form-item label=\"请求参数：\">{{\n            editForm.requestParam\n          }}</el-form-item>\n        </el-col>\n        <el-col :span=\"24\">\n          <el-form-item label=\"返回结果：\">{{\n            editForm.returnResult\n          }}</el-form-item>\n        </el-col>\n        <el-col :span=\"24\">\n          <el-form-item label=\"错误信息：\">{{\n            editForm.errorMessage\n          }}</el-form-item>\n        </el-col>\n        <el-col :span=\"12\">\n          <el-form-item label=\"操作 IP ：\"\n            >{{ editForm.ipAddress }}\n          </el-form-item>\n        </el-col>\n        <el-col :span=\"12\">\n          <el-form-item label=\"操作位置：\"\n            >{{ editForm.operateLocation }}\n          </el-form-item>\n        </el-col>\n        <el-col :span=\"12\">\n          <el-form-item label=\"浏览器：\"\n            >{{ editForm.browserType }}\n          </el-form-item>\n        </el-col>\n        <el-col :span=\"12\">\n          <el-form-item label=\"操作系统：\"\n            >{{ editForm.operateSystem }}\n          </el-form-item>\n        </el-col>\n        <el-col :span=\"12\">\n          <el-form-item label=\"操作时间：\">{{\n            parseTime(editForm.gmtOperate)\n          }}</el-form-item>\n        </el-col>\n        <el-col :span=\"12\">\n          <el-form-item label=\"操作状态：\"\n            ><dictionary-tag\n              code=\"logOperationStatus\"\n              :value=\"editForm.status\"\n            ></dictionary-tag\n          ></el-form-item>\n        </el-col>\n      </el-row>\n    </el-form>\n\n    <template #footer>\n      <div class=\"dialog-footer\">\n        <el-button @click=\"close\" type=\"primary\">关闭</el-button>\n      </div>\n    </template>\n  </el-dialog>\n</template>\n<script setup>\nimport { ref } from 'vue'\n\nimport { getById } from '@/api/system/log/operation'\n\nconst visible = ref(false)\nconst editForm = ref({})\n\n/**\n * 显示弹窗\n */\nasync function show(id) {\n  visible.value = true\n  const { data } = await getById(id)\n  editForm.value = data\n}\n\n/**\n * 关闭\n */\nfunction close() {\n  visible.value = false\n}\n\ndefineExpose({\n  show\n})\n</script>\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/views/system/log/operation/index.vue",
    "content": "<template>\n  <div class=\"app-container\">\n    <el-form\n      :model=\"queryParams\"\n      ref=\"queryFormRef\"\n      :inline=\"true\"\n      label-width=\"68px\"\n    >\n      <el-form-item label=\"模块名称\" prop=\"moduleName\">\n        <el-input\n          v-model=\"queryParams.moduleName\"\n          placeholder=\"请输入模块名称\"\n          clearable\n          style=\"width: 240px\"\n          @keyup.enter=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"IP 地址\" prop=\"ipAddress\">\n        <el-input\n          v-model=\"queryParams.ipAddress\"\n          placeholder=\"请输入 IP 地址\"\n          clearable\n          style=\"width: 240px\"\n          @keyup.enter=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"操作用户\" prop=\"operateUser\">\n        <el-input\n          v-model=\"queryParams.operateUser\"\n          placeholder=\"请输入用户名或昵称\"\n          clearable\n          style=\"width: 240px\"\n          @keyup.enter=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"业务类型\" prop=\"businessType\">\n        <el-select\n          v-model=\"queryParams.businessType\"\n          placeholder=\"业务类型\"\n          @change=\"handleQuery\"\n          clearable\n          style=\"width: 240px\"\n        >\n          <dictionary-option code=\"logOperationBusinessType\" />\n        </el-select>\n      </el-form-item>\n      <el-form-item label=\"操作类型\" prop=\"operateType\">\n        <el-select\n          v-model=\"queryParams.operateType\"\n          placeholder=\"操作类型\"\n          @change=\"handleQuery\"\n          clearable\n          style=\"width: 240px\"\n        >\n          <dictionary-option code=\"logOperationType\" />\n        </el-select>\n      </el-form-item>\n      <el-form-item label=\"状态\" prop=\"status\">\n        <el-select\n          v-model=\"queryParams.status\"\n          placeholder=\"操作状态\"\n          @change=\"handleQuery\"\n          clearable\n          style=\"width: 240px\"\n        >\n          <dictionary-option code=\"logOperationStatus\" />\n        </el-select>\n      </el-form-item>\n      <el-form-item label=\"操作时间\" style=\"width: 308px\">\n        <el-date-picker\n          v-model=\"dateRange\"\n          value-format=\"YYYY-MM-DD\"\n          format=\"YYYY-MM-DD\"\n          type=\"daterange\"\n          range-separator=\"-\"\n          start-placeholder=\"开始日期\"\n          end-placeholder=\"结束日期\"\n        ></el-date-picker>\n      </el-form-item>\n      <el-form-item>\n        <el-button type=\"primary\" icon=\"Search\" @click=\"handleQuery\"\n          >搜索</el-button\n        >\n        <el-button icon=\"Refresh\" @click=\"handleResetQuery\">重置</el-button>\n      </el-form-item>\n    </el-form>\n\n    <el-table\n      v-loading=\"loading\"\n      :data=\"dataList\"\n      border\n      @selection-change=\"handleSelectionChange\"\n    >\n      <el-table-column label=\"日志编号\" align=\"center\" prop=\"id\" width=\"180\" />\n      <el-table-column\n        label=\"模块名称\"\n        prop=\"moduleName\"\n        :show-overflow-tooltip=\"true\"\n      />\n      <el-table-column\n        label=\"业务类型\"\n        prop=\"businessType\"\n        :show-overflow-tooltip=\"true\"\n      >\n        <template #default=\"{ row }\">\n          <dictionary-tag\n            code=\"logOperationBusinessType\"\n            :value=\"row.businessType\"\n          ></dictionary-tag>\n        </template>\n      </el-table-column>\n      <el-table-column\n        label=\"操作类型\"\n        prop=\"operateType\"\n        :show-overflow-tooltip=\"true\"\n      >\n        <template #default=\"{ row }\">\n          <dictionary-tag\n            code=\"logOperationType\"\n            :value=\"row.operateType\"\n          ></dictionary-tag>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"操作用户\" :show-overflow-tooltip=\"true\">\n        <template #default=\"{ row }\">\n          <span>{{ row.nickName }}({{ row.username }})</span>\n        </template>\n      </el-table-column>\n      <el-table-column\n        label=\"操作 IP 地址\"\n        prop=\"ipAddress\"\n        :show-overflow-tooltip=\"true\"\n      />\n      <el-table-column\n        label=\"操作位置\"\n        prop=\"operateLocation\"\n        :show-overflow-tooltip=\"true\"\n      />\n      <el-table-column\n        label=\"状态\"\n        align=\"center\"\n        prop=\"status\"\n        :show-overflow-tooltip=\"true\"\n      >\n        <template #default=\"{ row }\">\n          <dictionary-tag\n            code=\"logOperationStatus\"\n            :value=\"row.status\"\n          ></dictionary-tag>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"操作时间\" align=\"center\" width=\"160\">\n        <template #default=\"{ row }\">\n          <span>{{ parseTime(row.gmtOperate) }}</span>\n        </template>\n      </el-table-column>\n      <el-table-column\n        label=\"操作\"\n        align=\"center\"\n        class-name=\"small-padding fixed-width\"\n        width=\"220\"\n      >\n        <template #default=\"{ row }\">\n          <el-button type=\"text\" icon=\"View\" @click=\"handleDetail(row)\"\n            >详情</el-button\n          >\n        </template>\n      </el-table-column>\n    </el-table>\n\n    <pagination\n      v-show=\"totalCount > 0\"\n      :total=\"totalCount\"\n      v-model:page=\"queryParams.pageNum\"\n      v-model:limit=\"queryParams.pageSize\"\n      @pagination=\"getPage\"\n    />\n\n    <!-- 详情 -->\n    <detail @fetch-data=\"handleQuery\" ref=\"detailRef\"></detail>\n  </div>\n</template>\n<script setup>\nimport { getCurrentInstance, ref } from 'vue'\n\nimport { page } from '@/api/system/log/operation'\n\nimport detail from './components/LogOperationDetail'\n\nconst { proxy } = getCurrentInstance()\n\nconst queryParams = ref({\n  pageNum: 1,\n  pageSize: 10,\n  operateUser: undefined,\n  ipAddress: undefined,\n  status: undefined,\n  beginGmtOperate: undefined,\n  endGmtOperate: undefined\n})\n\nconst loading = ref(true)\nconst dataList = ref([])\nconst dateRange = ref([])\nconst totalCount = ref(0)\n\n/**\n * 搜索\n */\nfunction handleQuery() {\n  getPage()\n}\n\n/**\n * 重置\n */\nfunction handleResetQuery() {\n  proxy.resetForm('queryFormRef')\n  dateRange.value = undefined\n  handleQuery()\n}\n\n/**\n * 获取数据\n */\nasync function getPage() {\n  loading.value = true\n  queryParams.value.beginGmtOperate = undefined\n  queryParams.value.endGmtOperate = undefined\n  if (Array.isArray(dateRange.value) && dateRange.value.length === 2) {\n    queryParams.value.beginGmtOperate = dateRange.value[0] + ' 00:00:00'\n    queryParams.value.endGmtOperate = dateRange.value[1] + ' 23:59:59'\n  }\n  const {\n    data: { total, list }\n  } = await page(queryParams.value)\n  totalCount.value = parseInt(total)\n  dataList.value = list\n  loading.value = false\n}\n/**\n * 详情\n */\nfunction handleDetail(row) {\n  proxy.$refs.detailRef.show(row.id)\n}\n\n/**\n * 初始化\n */\ngetPage()\n</script>\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/views/system/menu/components/MenuEdit.vue",
    "content": "<template>\n  <el-dialog\n    v-model=\"visible\"\n    :title=\"title\"\n    append-to-body\n    width=\"680px\"\n    :before-close=\"close\"\n  >\n    <el-form\n      :model=\"editForm\"\n      ref=\"editFormRef\"\n      label-width=\"100px\"\n      :rules=\"rules\"\n    >\n      <el-row>\n        <el-col :span=\"24\">\n          <el-form-item label=\"上级菜单\" prop=\"parentId\">\n            <el-tree-select\n              style=\"width: 100%\"\n              v-model=\"editForm.parentId\"\n              :data=\"menuTree\"\n              :props=\"{ value: 'id', label: 'menuName', children: 'children' }\"\n              value-key=\"id\"\n              placeholder=\"选择上级菜单\"\n              check-strictly\n              :render-after-expand=\"false\"\n            />\n          </el-form-item>\n        </el-col>\n        <el-col :span=\"24\" v-if=\"editForm.menuType != 'B'\">\n          <el-form-item label=\"菜单图标\" prop=\"menuIcon\">\n            <el-popover\n              placement=\"bottom-start\"\n              :width=\"540\"\n              v-model:visible=\"showChooseIcon\"\n              trigger=\"click\"\n              @show=\"showSelectIcon\"\n            >\n              <template #reference>\n                <el-input\n                  v-model=\"editForm.menuIcon\"\n                  placeholder=\"点击选择图标\"\n                  @click=\"showSelectIcon\"\n                  readonly\n                >\n                  <template #prefix>\n                    <svg-icon\n                      v-if=\"editForm.menuIcon\"\n                      :icon-name=\"editForm.menuIcon\"\n                      class=\"el-input__icon\"\n                      style=\"height: 32px; width: 16px\"\n                    />\n                    <el-icon v-else style=\"height: 32px; width: 16px\"\n                      ><search\n                    /></el-icon>\n                  </template>\n                </el-input>\n              </template>\n              <icon-select ref=\"iconSelectRef\" @selected=\"selected\" />\n            </el-popover>\n          </el-form-item>\n        </el-col>\n        <el-col :span=\"24\">\n          <el-form-item label=\"菜单类型\" prop=\"menuType\">\n            <el-radio-group v-model=\"editForm.menuType\">\n              <el-radio label=\"D\">目录</el-radio>\n              <el-radio label=\"M\">菜单</el-radio>\n              <el-radio label=\"B\">按钮</el-radio>\n            </el-radio-group>\n          </el-form-item>\n        </el-col>\n        <el-col :span=\"12\">\n          <el-form-item label=\"菜单名称\" prop=\"menuName\">\n            <el-input\n              v-model=\"editForm.menuName\"\n              placeholder=\"请输入菜单名称\"\n            />\n          </el-form-item>\n        </el-col>\n        <el-col :span=\"12\">\n          <el-form-item label=\"显示排序\" prop=\"sort\">\n            <el-input-number\n              v-model=\"editForm.sort\"\n              controls-position=\"right\"\n              :min=\"0\"\n            />\n          </el-form-item>\n        </el-col>\n        <el-col :span=\"12\" v-if=\"editForm.menuType != 'B'\">\n          <el-form-item>\n            <template #label>\n              <span>\n                <el-tooltip\n                  content=\"选择是外链则路由地址需要以`http(s)://`开头\"\n                  placement=\"top\"\n                >\n                  <el-icon>\n                    <QuestionFilled />\n                  </el-icon> </el-tooltip\n                >是否外链\n              </span>\n            </template>\n            <el-radio-group v-model=\"editForm.hasFrame\">\n              <dictionary-radio code=\"systemMenuIsFrame\" />\n            </el-radio-group>\n          </el-form-item>\n        </el-col>\n        <el-col :span=\"12\" v-if=\"editForm.menuType != 'B'\">\n          <el-form-item prop=\"routerUrl\">\n            <template #label>\n              <span>\n                <el-tooltip\n                  content=\"访问的路由地址，如：`user`，如外网地址需内链访问则以`http(s)://`开头\"\n                  placement=\"top\"\n                >\n                  <el-icon><question-filled /></el-icon>\n                </el-tooltip>\n                路由地址\n              </span>\n            </template>\n            <el-input\n              v-model=\"editForm.routerUrl\"\n              placeholder=\"请输入路由地址\"\n            />\n          </el-form-item>\n        </el-col>\n        <el-col :span=\"12\" v-if=\"editForm.menuType == 'M'\">\n          <el-form-item prop=\"componentPath\">\n            <template #label>\n              <span>\n                <el-tooltip\n                  content=\"访问的组件路径，如：`system/user/index`，默认在`views`目录下\"\n                  placement=\"top\"\n                >\n                  <el-icon><question-filled /></el-icon>\n                </el-tooltip>\n                组件路径\n              </span>\n            </template>\n            <el-input\n              v-model=\"editForm.componentPath\"\n              placeholder=\"请输入组件路径\"\n            />\n          </el-form-item>\n        </el-col>\n        <el-col :span=\"12\" v-if=\"editForm.menuType != 'D'\">\n          <el-form-item prop=\"permissionCode\">\n            <el-input\n              v-model=\"editForm.permissionCode\"\n              placeholder=\"请输入权限标识\"\n              maxlength=\"100\"\n            />\n            <template #label>\n              <span>\n                <el-tooltip content=\"控制器中定义的权限字符\" placement=\"top\">\n                  <el-icon><question-filled /></el-icon>\n                </el-tooltip>\n                权限字符\n              </span>\n            </template>\n          </el-form-item>\n        </el-col>\n        <el-col :span=\"12\" v-if=\"editForm.menuType == 'M'\">\n          <el-form-item prop=\"routerParam\">\n            <el-input\n              v-model=\"editForm.routerParam\"\n              placeholder=\"请输入路由参数\"\n              maxlength=\"255\"\n            />\n            <template #label>\n              <span>\n                <el-tooltip\n                  content='访问路由的默认传递参数，如：`{\"id\": \"M1\", \"name\": \"geshanzsq\"}`'\n                  placement=\"top\"\n                >\n                  <el-icon><question-filled /></el-icon>\n                </el-tooltip>\n                路由参数\n              </span>\n            </template>\n          </el-form-item>\n        </el-col>\n        <el-col :span=\"12\" v-if=\"editForm.menuType == 'M'\">\n          <el-form-item prop=\"hasCache\">\n            <template #label>\n              <span>\n                <el-tooltip\n                  content=\"选择是则会被`keep-alive`缓存，需要匹配组件的`name`和地址保持一致\"\n                  placement=\"top\"\n                >\n                  <el-icon><question-filled /></el-icon>\n                </el-tooltip>\n                是否缓存\n              </span>\n            </template>\n            <el-radio-group v-model=\"editForm.hasCache\">\n              <dictionary-radio code=\"systemMenuIsCache\" />\n            </el-radio-group>\n          </el-form-item>\n        </el-col>\n        <el-col :span=\"12\" v-if=\"editForm.menuType != 'B'\">\n          <el-form-item prop=\"showStatus\">\n            <template #label>\n              <span>\n                <el-tooltip\n                  content=\"选择隐藏则路由将不会出现在侧边栏，但仍然可以访问\"\n                  placement=\"top\"\n                >\n                  <el-icon><question-filled /></el-icon>\n                </el-tooltip>\n                显示状态\n              </span>\n            </template>\n            <el-radio-group v-model=\"editForm.showStatus\">\n              <dictionary-radio code=\"systemMenuShowStatus\" />\n            </el-radio-group>\n          </el-form-item>\n        </el-col>\n        <el-col :span=\"12\" v-if=\"editForm.menuType != 'B'\">\n          <el-form-item prop=\"status\">\n            <template #label>\n              <span>\n                <el-tooltip\n                  content=\"选择停用则路由将不会出现在侧边栏，也不能被访问\"\n                  placement=\"top\"\n                >\n                  <el-icon><question-filled /></el-icon>\n                </el-tooltip>\n                菜单状态\n              </span>\n            </template>\n            <el-radio-group v-model=\"editForm.status\">\n              <dictionary-radio code=\"commonStatus\" />\n            </el-radio-group>\n          </el-form-item>\n        </el-col>\n        <el-col :span=\"12\" v-if=\"editForm.menuType != 'B'\">\n          <el-form-item prop=\"hasPermission\">\n            <template #label>\n              <span>\n                <el-tooltip\n                  content=\"是否需要权限才能访问，如果为否，则该菜单不用分配就能显示\"\n                  placement=\"top\"\n                >\n                  <el-icon><question-filled /></el-icon>\n                </el-tooltip>\n                需要权限\n              </span>\n            </template>\n            <el-radio-group v-model=\"editForm.hasPermission\">\n              <dictionary-radio code=\"yesNo\" />\n            </el-radio-group>\n          </el-form-item>\n        </el-col>\n      </el-row>\n    </el-form>\n\n    <template #footer>\n      <div class=\"dialog-footer\">\n        <el-button @click=\"close\">取 消</el-button>\n        <el-button type=\"primary\" @click=\"handleSave\">确 定</el-button>\n      </div>\n    </template>\n  </el-dialog>\n</template>\n\n<script setup>\nimport { getCurrentInstance, ref } from 'vue'\n\nimport IconSelect from '@/components/IconSelect'\n\nimport {\n  list,\n  getById,\n  getMaxSortByParentId,\n  add,\n  update\n} from '@/api/system/menu'\n\nconst { proxy } = getCurrentInstance()\n\nconst visible = ref(false)\nconst title = ref('')\nconst menuTree = ref([])\nconst editForm = ref({\n  id: undefined,\n  parentId: '',\n  menuType: 'D',\n  hasFrame: '2',\n  hasCache: '1',\n  showStatus: '1',\n  status: '1',\n  hasPermission: '1'\n})\n\nconst rules = ref({\n  menuName: [{ required: true, message: '菜单名称不能为空', trigger: 'blur' }],\n  sort: [{ required: true, message: '菜单顺序不能为空', trigger: 'blur' }],\n  routerUrl: [{ required: true, message: '路由地址不能为空', trigger: 'blur' }]\n})\n\nconst showChooseIcon = ref(false)\nconst iconSelectRef = ref(null)\n\n/**\n * 显示弹窗\n */\nasync function show(id, parentId) {\n  visible.value = true\n  if (id) {\n    title.value = '修改菜单'\n    // 获取菜单信息\n    const { data } = await getById(id)\n    editForm.value = data\n    editForm.value.hasFrame = editForm.value.hasFrame + ''\n    editForm.value.hasCache = editForm.value.hasCache + ''\n    editForm.value.showStatus = editForm.value.showStatus + ''\n    editForm.value.status = editForm.value.status + ''\n    editForm.value.hasPermission = editForm.value.hasPermission + ''\n  } else {\n    title.value = '新增菜单'\n    // 获取排序最大值\n    const { data } = await getMaxSortByParentId(parentId)\n    editForm.value.sort = data ? data + 1 : 1\n    editForm.value.id = undefined\n    editForm.value.parentId = parentId\n  }\n  getTree()\n}\n\n/**\n * 获取菜单树\n */\nasync function getTree() {\n  menuTree.value = []\n  const { data } = await list()\n  const menu = { id: '', menuName: '根菜单', children: [] }\n  menu.children = proxy.handleTree(data)\n  menuTree.value.push(menu)\n}\n\n/**\n * 展示下拉图标\n */\nfunction showSelectIcon() {\n  iconSelectRef.value.reset()\n  showChooseIcon.value = true\n}\n/**\n * 选择图标\n */\nfunction selected(name) {\n  editForm.value.menuIcon = name\n  showChooseIcon.value = false\n}\n\n/**\n * 关闭\n */\nfunction close() {\n  visible.value = false\n  showChooseIcon.value = false\n  reset()\n}\n\n/**\n * 重置\n */\nfunction reset() {\n  proxy.resetForm('editFormRef')\n}\n\n/**\n * 提交数据\n */\nconst emit = defineEmits(null)\nfunction handleSave() {\n  proxy.$refs.editFormRef.validate(async (valid) => {\n    if (valid) {\n      // 修改\n      if (editForm.value.id) {\n        await update(editForm.value)\n        proxy.$modal.msgSuccess('修改菜单成功')\n      } else {\n        // 新增\n        await add(editForm.value)\n        proxy.$modal.msgSuccess('新增菜单成功')\n      }\n      emit('fetch-data')\n      close()\n    }\n  })\n}\n\ndefineExpose({\n  show\n})\n</script>\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/views/system/menu/components/SelectApi.vue",
    "content": "<template>\n  <el-dialog\n    v-model=\"visible\"\n    title=\"分配 API 接口数据\"\n    append-to-body\n    width=\"70%\"\n    :before-close=\"close\"\n  >\n    <div>\n      <span>所选接口数据：</span>\n      <span>\n        <el-tag\n          class=\"api-select-tag\"\n          v-for=\"api in apiMap\"\n          :key=\"api[0]\"\n          closable\n          @close=\"handleCloseTag(api[0])\"\n          >{{ api[1].apiCategoryName }} - {{ api[1].apiName }}</el-tag\n        >\n      </span>\n    </div>\n    <el-tabs\n      v-model=\"queryParams.apiCategoryId\"\n      @tab-change=\"handleApiCategory\"\n    >\n      <el-tab-pane\n        v-for=\"apiCategory in apiCategoryList\"\n        :key=\"apiCategory.id\"\n        :label=\"apiCategory.categoryName\"\n        :name=\"apiCategory.id\"\n      >\n        <!-- 需使用 v-if，否则因为有缓存导致切换 tab 时无法显示已勾选的数据 -->\n        <el-table\n          v-if=\"queryParams.apiCategoryId === apiCategory.id\"\n          border\n          ref=\"apiTableRef\"\n          v-loading=\"loading\"\n          :data=\"apiList\"\n          @select=\"handleSelection\"\n          @select-all=\"handleSelection\"\n        >\n          <el-table-column type=\"selection\" width=\"55\" align=\"center\" />\n          <el-table-column\n            label=\"接口名称\"\n            prop=\"apiName\"\n            :show-overflow-tooltip=\"true\"\n          />\n          <el-table-column\n            label=\"接口地址\"\n            prop=\"apiUrl\"\n            :show-overflow-tooltip=\"true\"\n          />\n          <el-table-column label=\"请求方式\" prop=\"apiMethod\" width=\"100\">\n            <template #default=\"{ row }\">\n              <dictionary-tag\n                code=\"apiRequestMethod\"\n                :value=\"row.apiMethod\"\n              ></dictionary-tag>\n            </template>\n          </el-table-column>\n          <el-table-column\n            label=\"状态\"\n            align=\"center\"\n            prop=\"status\"\n            width=\"100\"\n          >\n            <template #default=\"{ row }\">\n              <dictionary-tag\n                code=\"commonStatus\"\n                :value=\"row.status\"\n              ></dictionary-tag>\n            </template>\n          </el-table-column>\n        </el-table>\n\n        <pagination\n          class=\"pagination\"\n          v-show=\"totalCount > 0\"\n          :total=\"totalCount\"\n          v-model:page=\"queryParams.pageNum\"\n          v-model:limit=\"queryParams.pageSize\"\n          @pagination=\"getPage\"\n        />\n      </el-tab-pane>\n    </el-tabs>\n\n    <template #footer>\n      <div class=\"dialog-footer\">\n        <el-button @click=\"close\">取 消</el-button>\n        <el-button type=\"primary\" @click=\"handleSave\">确 定</el-button>\n      </div>\n    </template>\n  </el-dialog>\n</template>\n<script setup>\nimport { getCurrentInstance, ref } from 'vue'\n\nimport { list as listCateogry } from '@/api/system/api/category'\nimport { page } from '@/api/system/api/api'\nimport { authApiList, authApi } from '@/api/system/menu'\n\nconst { proxy } = getCurrentInstance()\n\nconst visible = ref(false)\n\nconst editForm = ref({\n  menuId: undefined,\n  apiIds: []\n})\nconst loading = ref(true)\nconst apiCategoryList = ref([])\nconst apiMap = ref(new Map())\n\nconst queryParams = ref({\n  pageNum: 1,\n  pageSize: 10,\n  apiCategoryId: undefined\n})\nconst apiCategoryName = ref('')\nconst totalCount = ref(0)\nconst apiList = ref([])\n\n/**\n * 显示弹窗\n */\nasync function show(menuId) {\n  visible.value = true\n  apiMap.value = new Map()\n  editForm.value.menuId = menuId\n  const { data } = await listCateogry()\n  apiCategoryList.value = data\n  await getAuthApiList()\n  if (apiCategoryList.value.length > 0) {\n    queryParams.value.apiCategoryId = apiCategoryList.value[0].id\n    // 获取接口列表数据\n    await getApiCategoryData()\n    // 设置选中列\n    handleSelectRows()\n  }\n}\n\n/**\n * 获取接口数据\n */\nasync function getAuthApiList() {\n  const { data } = await authApiList({ menuId: editForm.value.menuId })\n  if (data == null) {\n    return\n  }\n  for (const api of data) {\n    apiMap.value.set(api.apiId, api)\n  }\n}\n\n/**\n * 点击分类\n */\nasync function handleApiCategory() {\n  // 获取接口列表数据\n  await getApiCategoryData()\n  // 设置选中列\n  handleSelectRows()\n}\n\n/**\n * 获取接口分类\n */\nasync function getApiCategoryData() {\n  // 设置分类名称\n  for (const acRow in apiCategoryList.value) {\n    if (queryParams.value.apiCategoryId === apiCategoryList.value[acRow].id) {\n      apiCategoryName.value = apiCategoryList.value[acRow].categoryName\n      break\n    }\n  }\n  loading.value = true\n  const {\n    data: { total, list }\n  } = await page(queryParams.value)\n  apiList.value = list\n  totalCount.value = parseInt(total)\n  loading.value = false\n}\n\n/**\n * 选择条数\n */\nfunction handleSelection(rows) {\n  // 当前接口数据是否在 rows 中，如果不在，说明需要删除\n  for (const api in apiList.value) {\n    const apiData = apiList.value[api]\n    let hasExist = false\n    for (const row in rows) {\n      if (apiData.id === rows[row].id) {\n        hasExist = true\n        apiMap.value.set(apiData.id, {\n          apiName: apiData.apiName,\n          apiCategoryName: apiCategoryName.value\n        })\n        break\n      }\n    }\n    // 不在，移除\n    if (!hasExist) {\n      apiMap.value.delete(apiData.id)\n    }\n  }\n}\n\n/**\n * 设置选中列\n */\nfunction handleSelectRows() {\n  apiMap.value.forEach((apiRow) => {\n    for (const rowData of apiList.value) {\n      if (rowData.id === apiRow.apiId) {\n        proxy.$refs.apiTableRef[0].toggleRowSelection(rowData, true)\n        break\n      }\n    }\n  })\n}\n\n/**\n * 取消选择数据\n */\nfunction handleCloseTag(id) {\n  apiMap.value.delete(id)\n}\n\n/**\n * 提交数据\n */\nconst emit = defineEmits(null)\nasync function handleSave() {\n  // 获取选择的 API\n  editForm.value.apiIds = []\n  for (const key of apiMap.value.keys()) {\n    editForm.value.apiIds.push(key)\n  }\n  await authApi(editForm.value)\n  proxy.$modal.msgSuccess('分配 API 成功')\n\n  emit('fetch-data')\n  close()\n}\n\n/**\n * 关闭\n */\nfunction close() {\n  visible.value = false\n}\n\ndefineExpose({\n  show\n})\n</script>\n<style scoped>\n.api-select-tag {\n  margin: 5px;\n}\n.pagination {\n  margin-bottom: 20px;\n}\n</style>\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/views/system/menu/index.vue",
    "content": "<template>\n  <div class=\"app-container\">\n    <el-form\n      :model=\"queryParams\"\n      ref=\"queryFormRef\"\n      :inline=\"true\"\n      label-width=\"68px\"\n    >\n      <el-form-item label=\"菜单名称\" prop=\"menuName\">\n        <el-input\n          v-model=\"queryParams.menuName\"\n          placeholder=\"请输入菜单名称\"\n          clearable\n          style=\"width: 240px\"\n          @keyup.enter=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"状态\" prop=\"status\">\n        <el-select\n          v-model=\"queryParams.status\"\n          placeholder=\"菜单状态\"\n          @change=\"handleQuery\"\n          clearable\n          style=\"width: 240px\"\n        >\n          <dictionary-option code=\"commonStatus\" />\n        </el-select>\n      </el-form-item>\n      <el-form-item>\n        <el-button type=\"primary\" icon=\"Search\" @click=\"handleQuery\"\n          >搜索</el-button\n        >\n        <el-button icon=\"Refresh\" @click=\"handleResetQuery\">重置</el-button>\n      </el-form-item>\n    </el-form>\n\n    <el-row class=\"mb8\">\n      <el-col>\n        <el-button\n          type=\"primary\"\n          icon=\"Plus\"\n          @click=\"handleAdd\"\n          v-permission=\"['system:menu:add']\"\n          >新增</el-button\n        >\n      </el-col>\n    </el-row>\n\n    <el-table\n      border\n      v-loading=\"loading\"\n      :data=\"dataList\"\n      row-key=\"id\"\n      :tree-props=\"{ children: 'children', hasChildren: 'hasChildren' }\"\n    >\n      <el-table-column\n        prop=\"menuName\"\n        label=\"菜单名称\"\n        :show-overflow-tooltip=\"true\"\n        width=\"160\"\n      ></el-table-column>\n      <el-table-column prop=\"menuIcon\" label=\"图标\" align=\"center\" width=\"100\">\n        <template #default=\"{ row }\">\n          <svg-icon :icon-name=\"row.menuIcon\" />\n        </template>\n      </el-table-column>\n      <el-table-column\n        prop=\"sort\"\n        label=\"排序\"\n        width=\"60\"\n        align=\"center\"\n      ></el-table-column>\n      <el-table-column\n        prop=\"permissionCode\"\n        label=\"权限标识\"\n        :show-overflow-tooltip=\"true\"\n      ></el-table-column>\n      <el-table-column\n        prop=\"componentPath\"\n        label=\"组件路径\"\n        :show-overflow-tooltip=\"true\"\n      ></el-table-column>\n      <el-table-column prop=\"status\" label=\"状态\" width=\"80\" align=\"center\">\n        <template #default=\"{ row }\">\n          <dictionary-tag\n            code=\"commonStatus\"\n            :value=\"row.status\"\n          ></dictionary-tag>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"创建时间\" align=\"center\" prop=\"gmtCreate\">\n        <template #default=\"{ row }\">\n          <span>{{ parseTime(row.gmtCreate) }}</span>\n        </template>\n      </el-table-column>\n      <el-table-column\n        label=\"操作\"\n        align=\"center\"\n        width=\"300\"\n        class-name=\"small-padding fixed-width\"\n      >\n        <template #default=\"{ row }\">\n          <el-button\n            type=\"text\"\n            icon=\"Edit\"\n            @click=\"handleUpdate(row)\"\n            v-permission=\"['system:menu:update']\"\n            >修改</el-button\n          >\n          <el-button\n            type=\"text\"\n            icon=\"Plus\"\n            @click=\"handleAdd(row)\"\n            v-permission=\"['system:menu:add']\"\n            >新增</el-button\n          >\n          <el-button\n            type=\"text\"\n            icon=\"Delete\"\n            @click=\"handleDelete(row)\"\n            v-permission=\"['system:menu:delete']\"\n            >删除</el-button\n          >\n          <el-button\n            type=\"text\"\n            icon=\"Document\"\n            @click=\"handleSelectApi(row)\"\n            v-permission=\"['system:menu:allocateApi']\"\n            v-if=\"'D' !== row.menuType && row.hasPermission !== 2\"\n            >分配 API</el-button\n          >\n        </template>\n      </el-table-column>\n    </el-table>\n\n    <!-- 新增和编辑 -->\n    <edit @fetch-data=\"handleQuery\" ref=\"editRef\"></edit>\n\n    <!-- 分配 API -->\n    <SelectApi ref=\"selectApiRef\" />\n  </div>\n</template>\n<script setup>\nimport { getCurrentInstance, ref } from 'vue'\n\nimport Edit from './components/MenuEdit'\nimport SelectApi from './components/SelectApi'\n\nimport { list, remove } from '@/api/system/menu'\n\nconst { proxy } = getCurrentInstance()\n\nconst queryParams = ref({\n  menuName: undefined,\n  status: undefined\n})\n\nconst loading = ref(true)\nconst dataList = ref([])\n\n/**\n * 获取菜单树\n */\nasync function getTree() {\n  loading.value = true\n  const { data } = await list(queryParams.value)\n  dataList.value = proxy.handleTree(data)\n  loading.value = false\n}\n\n/**\n * 搜索\n */\nfunction handleQuery() {\n  getTree()\n}\n\n/**\n * 重置\n */\nfunction handleResetQuery() {\n  proxy.resetForm('queryFormRef')\n  handleQuery()\n}\n\n/**\n * 新增\n */\nfunction handleAdd(row) {\n  const parentId = row ? row.id : undefined\n  proxy.$refs.editRef.show(undefined, parentId)\n}\n\n/**\n * 修改\n */\nfunction handleUpdate(row) {\n  proxy.$refs.editRef.show(row.id, row.id)\n}\n\n/**\n * 删除\n */\nasync function handleDelete(row) {\n  proxy.$modal\n    .confirm('是否确认删除名称为【' + row.menuName + '】的数据项?')\n    .then(async function () {\n      await remove(row.id)\n      proxy.$modal.msgSuccess('删除菜单成功')\n      handleQuery()\n    })\n}\n\n/**\n * 分配 Api\n */\nfunction handleSelectApi(row) {\n  proxy.$refs.selectApiRef.show(row.id, row.id)\n}\n\n/**\n * 初始化\n */\ngetTree()\n</script>\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/views/system/param/components/ParamEdit.vue",
    "content": "<template>\n  <el-dialog\n    v-model=\"visible\"\n    :title=\"title\"\n    width=\"600px\"\n    append-to-body\n    :before-close=\"close\"\n  >\n    <el-form\n      :model=\"editForm\"\n      ref=\"editFormRef\"\n      label-width=\"80px\"\n      :rules=\"rules\"\n    >\n      <el-form-item label=\"参数名称\" prop=\"paramName\">\n        <el-input v-model=\"editForm.paramName\" placeholder=\"请输入参数名称\" />\n      </el-form-item>\n      <el-form-item label=\"参数键\" prop=\"paramKey\">\n        <el-input v-model=\"editForm.paramKey\" placeholder=\"请输入参数键\" />\n      </el-form-item>\n      <el-form-item label=\"参数值\" prop=\"paramValue\">\n        <el-input v-model=\"editForm.paramValue\" placeholder=\"请输入参数值\" />\n      </el-form-item>\n      <el-form-item label=\"参数顺序\" prop=\"sort\">\n        <el-input-number\n          v-model=\"editForm.sort\"\n          controls-position=\"right\"\n          :min=\"0\"\n        />\n      </el-form-item>\n      <el-form-item label=\"参数类型\" prop=\"paramType\">\n        <el-radio-group v-model=\"editForm.paramType\">\n          <dictionary-radio code=\"sysParamType\" />\n        </el-radio-group>\n      </el-form-item>\n      <el-form-item label=\"备注\" prop=\"remark\">\n        <el-input\n          v-model=\"editForm.remark\"\n          type=\"textarea\"\n          placeholder=\"请输入内容\"\n        ></el-input>\n      </el-form-item>\n    </el-form>\n\n    <template #footer>\n      <div class=\"dialog-footer\">\n        <el-button @click=\"close\">取 消</el-button>\n        <el-button type=\"primary\" @click=\"handleSave\">确 定</el-button>\n      </div>\n    </template>\n  </el-dialog>\n</template>\n<script setup>\nimport { getCurrentInstance, ref } from 'vue'\n\nimport { getById, add, update, getMaxSort } from '@/api/system/param'\n\nconst { proxy } = getCurrentInstance()\n\nconst visible = ref(false)\nconst title = ref('')\nconst rules = ref({\n  paramName: [{ required: true, message: '参数名称不能为空', trigger: 'blur' }],\n  paramKey: [{ required: true, message: '参数键不能为空', trigger: 'blur' }],\n  paramValue: [{ required: true, message: '参数值不能为空', trigger: 'blur' }],\n  sort: [{ required: true, message: '顺序不能为空', trigger: 'blur' }]\n})\nconst editForm = ref({\n  sort: 1,\n  paramType: '1'\n})\n\n/**\n * 显示弹窗\n */\nasync function show(id) {\n  visible.value = true\n  if (id) {\n    title.value = '修改参数'\n    // 获取角色信息\n    const { data } = await getById(id)\n    editForm.value = data\n    editForm.value.paramType = editForm.value.paramType + ''\n  } else {\n    title.value = '新增参数'\n    editForm.value.id = undefined\n    const { data } = await getMaxSort()\n    editForm.value.sort = data ? data + 1 : 1\n  }\n}\n\n/**\n * 关闭\n */\nfunction close() {\n  visible.value = false\n  reset()\n}\n\n/**\n * 重置\n */\nfunction reset() {\n  proxy.resetForm('editFormRef')\n}\n\n/**\n * 提交数据\n */\nconst emit = defineEmits(null)\nfunction handleSave() {\n  proxy.$refs.editFormRef.validate(async (valid) => {\n    if (valid) {\n      // 修改\n      if (editForm.value.id) {\n        await update(editForm.value)\n        proxy.$modal.msgSuccess('修改参数成功')\n      } else {\n        // 新增\n        await add(editForm.value)\n        proxy.$modal.msgSuccess('新增参数成功')\n      }\n      emit('fetch-data')\n      close()\n    }\n  })\n}\n\ndefineExpose({\n  show\n})\n</script>\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/views/system/param/index.vue",
    "content": "<template>\n  <div class=\"app-container\">\n    <el-form\n      :model=\"queryParams\"\n      ref=\"queryFormRef\"\n      :inline=\"true\"\n      label-width=\"68px\"\n    >\n      <el-form-item label=\"参数名称\" prop=\"paramName\">\n        <el-input\n          v-model=\"queryParams.paramName\"\n          placeholder=\"请输入参数名称\"\n          clearable\n          style=\"width: 240px\"\n          @keyup.enter=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"参数键\" prop=\"paramKey\">\n        <el-input\n          v-model=\"queryParams.paramKey\"\n          placeholder=\"请输入参数键\"\n          clearable\n          style=\"width: 240px\"\n          @keyup.enter=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item>\n        <el-button type=\"primary\" icon=\"Search\" @click=\"handleQuery\"\n          >搜索</el-button\n        >\n        <el-button icon=\"Refresh\" @click=\"handleResetQuery\">重置</el-button>\n      </el-form-item>\n    </el-form>\n    <el-row :gutter=\"10\" class=\"mb8\">\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"primary\"\n          icon=\"Plus\"\n          @click=\"handleAdd\"\n          v-permission=\"['system:param:add']\"\n          >新增</el-button\n        >\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"warning\"\n          icon=\"Edit\"\n          :disabled=\"single\"\n          @click=\"handleUpdate\"\n          v-permission=\"['system:param:update']\"\n          >修改</el-button\n        >\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"danger\"\n          icon=\"Delete\"\n          :disabled=\"multiple\"\n          @click=\"handleDelete\"\n          v-permission=\"['system:param:delete']\"\n          >删除</el-button\n        >\n      </el-col>\n    </el-row>\n\n    <el-table\n      border\n      v-loading=\"loading\"\n      :data=\"dataList\"\n      @selection-change=\"handleSelectionChange\"\n    >\n      <el-table-column type=\"selection\" width=\"55\" align=\"center\" />\n      <el-table-column label=\"参数编号\" prop=\"id\" width=\"180\" align=\"center\" />\n      <el-table-column\n        label=\"参数名称\"\n        prop=\"paramName\"\n        :show-overflow-tooltip=\"true\"\n      />\n      <el-table-column\n        label=\"参数键\"\n        prop=\"paramKey\"\n        :show-overflow-tooltip=\"true\"\n      />\n      <el-table-column\n        label=\"参数值\"\n        prop=\"paramValue\"\n        :show-overflow-tooltip=\"true\"\n      />\n      <el-table-column\n        label=\"参数类型\"\n        align=\"center\"\n        prop=\"paramType\"\n        width=\"100\"\n      >\n        <template #default=\"{ row }\">\n          <dictionary-tag\n            code=\"sysParamType\"\n            :value=\"row.paramType\"\n          ></dictionary-tag>\n        </template>\n      </el-table-column>\n      <el-table-column\n        label=\"显示顺序\"\n        prop=\"sort\"\n        width=\"100\"\n        align=\"center\"\n      />\n      <el-table-column label=\"备注\" align=\"center\" prop=\"remark\" />\n      <el-table-column\n        label=\"创建时间\"\n        align=\"center\"\n        prop=\"gmtCreate\"\n        width=\"160\"\n      >\n        <template #default=\"{ row }\">\n          <span>{{ parseTime(row.gmtCreate) }}</span>\n        </template>\n      </el-table-column>\n      <el-table-column\n        label=\"操作\"\n        align=\"center\"\n        class-name=\"small-padding fixed-width\"\n        width=\"160\"\n      >\n        <template #default=\"{ row }\">\n          <el-button\n            type=\"text\"\n            icon=\"Edit\"\n            @click=\"handleUpdate(row)\"\n            v-permission=\"['system:param:update']\"\n            >修改</el-button\n          >\n          <el-button\n            type=\"text\"\n            icon=\"Delete\"\n            @click=\"handleDelete(row)\"\n            v-permission=\"['system:param:delete']\"\n            >删除</el-button\n          >\n        </template>\n      </el-table-column>\n    </el-table>\n\n    <pagination\n      v-show=\"totalCount > 0\"\n      :total=\"totalCount\"\n      v-model:page=\"queryParams.pageNum\"\n      v-model:limit=\"queryParams.pageSize\"\n      @pagination=\"getPage\"\n    />\n\n    <!-- 新增和编辑 -->\n    <edit @fetch-data=\"handleQuery\" ref=\"editRef\"></edit>\n  </div>\n</template>\n<script setup>\nimport { getCurrentInstance, ref } from 'vue'\n\nimport Edit from './components/ParamEdit'\n\nimport { page, remove } from '@/api/system/param'\n\nconst { proxy } = getCurrentInstance()\n\nconst loading = ref(true)\nconst dataList = ref([])\nconst totalCount = ref(0)\nconst ids = ref([])\nconst single = ref(true)\nconst multiple = ref(true)\n\nconst queryParams = ref({\n  pageNum: 1,\n  pageSize: 10,\n  paramName: undefined,\n  paramKey: undefined\n})\n\n/**\n * 获取数据\n */\nasync function getPage() {\n  loading.value = true\n  const {\n    data: { total, list }\n  } = await page(queryParams.value)\n  totalCount.value = parseInt(total)\n  dataList.value = list\n  loading.value = false\n}\n\n/**\n * 搜索\n */\nfunction handleQuery() {\n  getPage()\n}\n\n/**\n * 重置\n */\nfunction handleResetQuery() {\n  proxy.resetForm('queryFormRef')\n  handleQuery()\n}\n\n/**\n * 选择条数\n */\nfunction handleSelectionChange(selection) {\n  ids.value = selection.map((item) => item.id)\n  single.value = selection.length !== 1\n  multiple.value = !selection.length\n}\n\n/**\n * 新增\n */\nfunction handleAdd() {\n  proxy.$refs.editRef.show(undefined)\n}\n\n/**\n * 修改\n */\nfunction handleUpdate(row) {\n  const id = row.id || ids.value\n  proxy.$refs.editRef.show(id)\n}\n\n/**\n * 删除按钮操作\n */\nasync function handleDelete(row) {\n  const deleteIds = row.id || ids.value\n  proxy.$modal\n    .confirm('是否确认删除参数编号为【' + deleteIds + '】的数据项?')\n    .then(async function () {\n      await remove(deleteIds)\n      proxy.$modal.msgSuccess('删除参数成功')\n      handleQuery()\n    })\n}\n\n/**\n * 初始化\n */\ngetPage()\n</script>\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/views/system/role/authUser.vue",
    "content": "<template>\n  <div class=\"app-container\">\n    <div>当前角色：{{ role.roleName }}（{{ role.roleCode }}）</div>\n    <el-divider />\n    <el-form\n      :model=\"queryParams\"\n      ref=\"queryFormRef\"\n      :inline=\"true\"\n      label-width=\"68px\"\n    >\n      <el-form-item label=\"用户名\" prop=\"username\">\n        <el-input\n          v-model=\"queryParams.username\"\n          placeholder=\"请输入用户名\"\n          clearable\n          style=\"width: 240px\"\n          @keyup.enter=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"昵称\" prop=\"nickName\">\n        <el-input\n          v-model=\"queryParams.nickName\"\n          placeholder=\"请输入用户昵称\"\n          clearable\n          style=\"width: 240px\"\n          @keyup.enter=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"状态\" prop=\"status\">\n        <el-select\n          v-model=\"queryParams.status\"\n          placeholder=\"用户状态\"\n          @change=\"handleQuery\"\n          clearable\n          style=\"width: 240px\"\n        >\n          <dictionary-option code=\"commonStatus\" />\n        </el-select>\n      </el-form-item>\n      <el-form-item label=\"用户类型\" prop=\"userType\">\n        <el-select\n          v-model=\"queryParams.userType\"\n          placeholder=\"用户类型\"\n          @change=\"handleQuery\"\n          clearable\n          style=\"width: 240px\"\n        >\n          <dictionary-option code=\"systemUserType\" />\n        </el-select>\n      </el-form-item>\n      <el-form-item>\n        <el-button type=\"primary\" icon=\"Search\" @click=\"handleQuery\"\n          >搜索</el-button\n        >\n        <el-button icon=\"Refresh\" @click=\"handleResetQuery\">重置</el-button>\n      </el-form-item>\n    </el-form>\n\n    <el-row :gutter=\"10\" class=\"mb8\">\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"primary\"\n          icon=\"Plus\"\n          @click=\"handleAdd\"\n          v-permission=\"['system:role:addAuthUser']\"\n          >授权用户</el-button\n        >\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"danger\"\n          icon=\"Delete\"\n          :disabled=\"multiple\"\n          @click=\"handleDelete\"\n          v-permission=\"['system:user:deleteAuthUser']\"\n          >取消授权</el-button\n        >\n      </el-col>\n    </el-row>\n\n    <el-table\n      v-loading=\"loading\"\n      :data=\"dataList\"\n      border\n      @selection-change=\"handleSelectionChange\"\n    >\n      <el-table-column type=\"selection\" width=\"50\" align=\"center\" />\n      <el-table-column label=\"用户编号\" align=\"center\" prop=\"id\" width=\"180\" />\n      <el-table-column\n        label=\"用户名\"\n        prop=\"username\"\n        :show-overflow-tooltip=\"true\"\n      />\n      <el-table-column\n        label=\"昵称\"\n        prop=\"nickName\"\n        :show-overflow-tooltip=\"true\"\n      />\n      <el-table-column\n        label=\"用户角色\"\n        prop=\"roleNames\"\n        :show-overflow-tooltip=\"true\"\n      />\n      <el-table-column\n        label=\"性别\"\n        align=\"center\"\n        prop=\"sex\"\n        :show-overflow-tooltip=\"true\"\n      >\n        <template #default=\"{ row }\">\n          <dictionary-tag\n            code=\"systemUserSex\"\n            :value=\"row.sex\"\n          ></dictionary-tag>\n        </template>\n      </el-table-column>\n      <el-table-column\n        label=\"用户类型\"\n        align=\"center\"\n        prop=\"userType\"\n        :show-overflow-tooltip=\"true\"\n      >\n        <template #default=\"{ row }\">\n          <dictionary-tag\n            code=\"systemUserType\"\n            :value=\"row.userType\"\n          ></dictionary-tag>\n        </template>\n      </el-table-column>\n      <el-table-column\n        label=\"用户状态\"\n        align=\"center\"\n        prop=\"userType\"\n        :show-overflow-tooltip=\"true\"\n      >\n        <template #default=\"{ row }\">\n          <dictionary-tag\n            code=\"commonStatus\"\n            :value=\"row.status\"\n          ></dictionary-tag>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"创建时间\" align=\"center\" width=\"160\">\n        <template #default=\"{ row }\">\n          <span>{{ parseTime(row.gmtCreate) }}</span>\n        </template>\n      </el-table-column>\n\n      <el-table-column\n        label=\"操作\"\n        align=\"center\"\n        width=\"100\"\n        class-name=\"small-padding fixed-width\"\n      >\n        <template #default=\"{ row }\">\n          <el-button\n            type=\"text\"\n            icon=\"CircleClose\"\n            @click=\"handleDelete(row)\"\n            v-permission=\"['system:user:deleteAuthUser']\"\n            >取消授权</el-button\n          >\n        </template>\n      </el-table-column>\n    </el-table>\n\n    <pagination\n      v-show=\"totalCount > 0\"\n      :total=\"totalCount\"\n      v-model:page=\"queryParams.pageNum\"\n      v-model:limit=\"queryParams.pageSize\"\n      @pagination=\"getPage\"\n    />\n\n    <!-- 选择授权用户 -->\n    <select-auth-user\n      @fetch-data=\"handleQuery\"\n      ref=\"selectAuthUserRef\"\n    ></select-auth-user>\n  </div>\n</template>\n<script setup>\nimport { getCurrentInstance, ref } from 'vue'\nimport { useRouter } from 'vue-router'\n\nimport SelectAuthUser from './components/SelectAuthUser'\n\nimport { getById, userAuthPage, removeAuthUser } from '@/api/system/role'\n\nconst router = useRouter()\n\nconst { proxy } = getCurrentInstance()\n\nconst queryParams = ref({\n  pageNum: 1,\n  pageSize: 10,\n  username: undefined,\n  nickName: undefined,\n  status: undefined,\n  userType: undefined,\n  roleId: router.currentRoute.value.params.id\n})\n\nconst loading = ref(true)\nconst dataList = ref([])\nconst totalCount = ref(0)\nconst ids = ref([])\nconst multiple = ref(true)\nconst role = ref({})\n\n/**\n * 搜索\n */\nfunction handleQuery() {\n  getPage()\n}\n\n/**\n * 重置\n */\nfunction handleResetQuery() {\n  proxy.resetForm('queryFormRef')\n  handleQuery()\n}\n\n/**\n * 获取数据\n */\nasync function getPage() {\n  loading.value = true\n  const {\n    data: { total, list }\n  } = await userAuthPage(queryParams.value)\n  totalCount.value = parseInt(total)\n  dataList.value = list\n  loading.value = false\n}\n\n/**\n * 获取角色信息\n */\nasync function getRole() {\n  const { data } = await getById(queryParams.value.roleId)\n  role.value = data\n}\n\n/**\n * 选择条数\n */\nfunction handleSelectionChange(selection) {\n  ids.value = selection.map((item) => item.id)\n  multiple.value = !selection.length\n}\n\n/**\n * 添加授权用户\n */\nfunction handleAdd() {\n  proxy.$refs.selectAuthUserRef.show(queryParams.value.roleId)\n}\n\n/**\n * 删除按钮操作\n */\nasync function handleDelete(row) {\n  let deleteIds = []\n  if (row.id) {\n    deleteIds.push(row.id)\n  } else {\n    deleteIds = ids.value\n  }\n  proxy.$modal\n    .confirm('是否确认取消授权用户编号为【' + deleteIds + '】的数据项?')\n    .then(async function () {\n      await removeAuthUser({\n        roleId: queryParams.value.roleId,\n        userIds: deleteIds\n      })\n      proxy.$modal.msgSuccess('取消授权用户成功')\n      handleQuery()\n    })\n}\n\n/**\n * 初始化\n */\ngetPage()\ngetRole()\n</script>\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/views/system/role/components/RoleEdit.vue",
    "content": "<template>\n  <el-dialog\n    v-model=\"visible\"\n    :title=\"title\"\n    width=\"600px\"\n    append-to-body\n    :before-close=\"close\"\n  >\n    <el-form\n      :model=\"editForm\"\n      ref=\"editFormRef\"\n      label-width=\"80px\"\n      :rules=\"rules\"\n    >\n      <el-form-item label=\"角色名称\" prop=\"roleName\">\n        <el-input v-model=\"editForm.roleName\" placeholder=\"请输入角色名称\" />\n      </el-form-item>\n      <el-form-item label=\"角色编码\" prop=\"roleCode\">\n        <el-input v-model=\"editForm.roleCode\" placeholder=\"请输入角色标识\" />\n      </el-form-item>\n      <el-form-item label=\"角色顺序\" prop=\"sort\">\n        <el-input-number\n          v-model=\"editForm.sort\"\n          controls-position=\"right\"\n          :min=\"0\"\n        />\n      </el-form-item>\n      <el-form-item label=\"状态\" prop=\"status\">\n        <el-radio-group v-model=\"editForm.status\">\n          <dictionary-radio code=\"commonStatus\" />\n        </el-radio-group>\n      </el-form-item>\n      <el-form-item label=\"菜单权限\">\n        <el-row>\n          <el-col :span=\"24\"\n            ><el-checkbox\n              v-model=\"menuExpand\"\n              @change=\"handleCheckedTreeExpand($event)\"\n              >展开/折叠</el-checkbox\n            >\n            <el-checkbox\n              v-model=\"menuNodeAll\"\n              @change=\"handleCheckedTreeNodeAll($event)\"\n              >全选/全不选</el-checkbox\n            >\n            <el-checkbox\n              v-model=\"menuCheckStrictly\"\n              @change=\"handleCheckedTreeConnect($event)\"\n              >父子联动</el-checkbox\n            ></el-col\n          >\n          <el-col :span=\"24\">\n            <el-tree\n              class=\"tree-border\"\n              :default-expand-all=\"menuExpand\"\n              :data=\"menuTree\"\n              show-checkbox\n              ref=\"menuRef\"\n              node-key=\"id\"\n              :check-strictly=\"!menuCheckStrictly\"\n              :default-checked-keys=\"editForm.menuIds\"\n              @check=\"handleMenuNodeClick\"\n              empty-text=\"加载中，请稍候\"\n              :props=\"{ label: 'menuName', children: 'children' }\"\n            ></el-tree\n          ></el-col>\n        </el-row>\n      </el-form-item>\n      <el-form-item label=\"备注\" prop=\"remark\">\n        <el-input\n          v-model=\"editForm.remark\"\n          type=\"textarea\"\n          placeholder=\"请输入内容\"\n        ></el-input>\n      </el-form-item>\n    </el-form>\n\n    <template #footer>\n      <div class=\"dialog-footer\">\n        <el-button @click=\"close\">取 消</el-button>\n        <el-button type=\"primary\" @click=\"handleSave\">确 定</el-button>\n      </div>\n    </template>\n  </el-dialog>\n</template>\n<script setup>\nimport { getCurrentInstance, ref } from 'vue'\n\nimport { getById, add, update, getMaxSort } from '@/api/system/role'\n\nimport { list } from '@/api/system/menu'\n\nconst { proxy } = getCurrentInstance()\n\nconst visible = ref(false)\nconst title = ref('')\nconst rules = ref({\n  roleName: [{ required: true, message: '角色名称不能为空', trigger: 'blur' }],\n  roleCode: [{ required: true, message: '角色编码不能为空', trigger: 'blur' }],\n  sort: [{ required: true, message: '顺序不能为空', trigger: 'blur' }],\n  status: [{ required: true, message: '状态不能为空', trigger: 'blur' }]\n})\nconst editForm = ref({\n  sort: 1,\n  status: '1',\n  menuIds: []\n})\nconst menuTree = ref([])\n// 菜单数量\nconst menuCount = ref(0)\nconst menuRef = ref(null)\n// 父子联动\nconst menuCheckStrictly = ref(true)\n// 展开/折叠\nconst menuExpand = ref(false)\n// 全选/全不选\nconst menuNodeAll = ref(false)\n\n/**\n * 显示弹窗\n */\nasync function show(id) {\n  visible.value = true\n  if (id) {\n    title.value = '修改角色'\n    // 获取角色信息\n    const { data } = await getById(id)\n    editForm.value = data\n    editForm.value.status = editForm.value.status + ''\n  } else {\n    title.value = '新增角色'\n    editForm.value.id = undefined\n    const { data } = await getMaxSort()\n    editForm.value.sort = data ? data + 1 : 1\n  }\n  getMenuTree()\n  // 设置父子联动，新增时默认父子联动，修改时判断是否全选\n  menuCheckStrictly.value =\n    !id && editForm.value.menuIds.length === menuCount.value\n}\n\n/**\n * 关闭\n */\nfunction close() {\n  visible.value = false\n  menuCheckStrictly.value = true\n  menuExpand.value = false\n  menuNodeAll.value = false\n  reset()\n}\n\n/**\n * 重置\n */\nfunction reset() {\n  proxy.resetForm('editFormRef')\n}\n\n/**\n * 提交数据\n */\nconst emit = defineEmits(null)\nfunction handleSave() {\n  proxy.$refs.editFormRef.validate(async (valid) => {\n    if (valid) {\n      editForm.value.menuIds = getMenuAllCheckedKeys()\n      // 修改\n      if (editForm.value.id) {\n        await update(editForm.value)\n        proxy.$modal.msgSuccess('修改角色成功')\n      } else {\n        // 新增\n        await add(editForm.value)\n        proxy.$modal.msgSuccess('新增角色成功')\n      }\n      emit('fetch-data')\n      close()\n    }\n  })\n}\n\n/**\n * 获取菜单数据\n */\nasync function getMenuTree() {\n  const { data } = await list()\n  menuCount.value = data.length\n  menuTree.value = proxy.handleTree(data)\n}\n\n/**\n * 树权限（展开/折叠）\n */\nfunction handleCheckedTreeExpand(value) {\n  const treeList = menuTree.value\n  for (let i = 0; i < treeList.length; i++) {\n    menuRef.value.store.nodesMap[treeList[i].id].expanded = value\n  }\n}\n\n/**\n * 树权限（全选/全不选）\n */\nfunction handleCheckedTreeNodeAll(value) {\n  menuRef.value.setCheckedNodes(value ? menuTree.value : [])\n}\n\n/**\n * 所有菜单节点数据\n */\nfunction getMenuAllCheckedKeys() {\n  // 目前被选中的菜单节点\n  const checkedKeys = menuRef.value.getCheckedKeys()\n  // 半选中的菜单节点\n  const halfCheckedKeys = menuRef.value.getHalfCheckedKeys()\n  checkedKeys.unshift.apply(checkedKeys, halfCheckedKeys)\n  return checkedKeys\n}\n\n/**\n * 菜单点击事件\n */\nfunction handleMenuNodeClick(data, node) {\n  const checkedKeyLength = node.checkedKeys.length\n  // 去除全选\n  if (\n    (checkedKeyLength === 0 || checkedKeyLength < menuCount.value) &&\n    menuNodeAll.value\n  ) {\n    menuNodeAll.value = false\n    return\n  }\n  // 勾选全选\n  if (checkedKeyLength === menuCount.value && !menuNodeAll.value) {\n    menuNodeAll.value = true\n  }\n}\n\n/** 树权限（父子联动） */\nfunction handleCheckedTreeConnect(value) {\n  menuCheckStrictly.value = !!value\n}\n\ndefineExpose({\n  show\n})\n</script>\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/views/system/role/components/SelectAuthUser.vue",
    "content": "<template>\n  <el-dialog\n    v-model=\"visible\"\n    title=\"选择分配用户\"\n    width=\"70%\"\n    append-to-body\n    :before-close=\"close\"\n  >\n    <el-form\n      :model=\"queryParams\"\n      ref=\"queryFormRef\"\n      :inline=\"true\"\n      label-width=\"68px\"\n    >\n      <el-form-item label=\"用户名\" prop=\"username\">\n        <el-input\n          v-model=\"queryParams.username\"\n          placeholder=\"请输入用户名\"\n          clearable\n          style=\"width: 240px\"\n          @keyup.enter=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"昵称\" prop=\"nickName\">\n        <el-input\n          v-model=\"queryParams.nickName\"\n          placeholder=\"请输入用户昵称\"\n          clearable\n          style=\"width: 240px\"\n          @keyup.enter=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"状态\" prop=\"status\">\n        <el-select\n          v-model=\"queryParams.status\"\n          placeholder=\"用户状态\"\n          @change=\"handleQuery\"\n          clearable\n          style=\"width: 240px\"\n        >\n          <dictionary-option code=\"commonStatus\" />\n        </el-select>\n      </el-form-item>\n      <el-form-item label=\"用户类型\" prop=\"userType\">\n        <el-select\n          v-model=\"queryParams.userType\"\n          placeholder=\"用户类型\"\n          @change=\"handleQuery\"\n          clearable\n          style=\"width: 240px\"\n        >\n          <dictionary-option code=\"systemUserType\" />\n        </el-select>\n      </el-form-item>\n      <el-form-item>\n        <el-button type=\"primary\" icon=\"Search\" @click=\"handleQuery\"\n          >搜索</el-button\n        >\n        <el-button icon=\"Refresh\" @click=\"handleResetQuery\">重置</el-button>\n      </el-form-item>\n    </el-form>\n\n    <el-table\n      v-loading=\"loading\"\n      :data=\"dataList\"\n      border\n      @selection-change=\"handleSelectionChange\"\n    >\n      <el-table-column type=\"selection\" width=\"50\" align=\"center\" />\n      <el-table-column label=\"用户编号\" align=\"center\" prop=\"id\" width=\"180\" />\n      <el-table-column\n        label=\"用户名\"\n        prop=\"username\"\n        :show-overflow-tooltip=\"true\"\n      />\n      <el-table-column\n        label=\"昵称\"\n        prop=\"nickName\"\n        :show-overflow-tooltip=\"true\"\n      />\n      <el-table-column\n        label=\"用户角色\"\n        prop=\"roleNames\"\n        :show-overflow-tooltip=\"true\"\n      />\n      <el-table-column\n        label=\"性别\"\n        align=\"center\"\n        prop=\"sex\"\n        :show-overflow-tooltip=\"true\"\n      >\n        <template #default=\"{ row }\">\n          <dictionary-tag\n            code=\"systemUserSex\"\n            :value=\"row.sex\"\n          ></dictionary-tag>\n        </template>\n      </el-table-column>\n      <el-table-column\n        label=\"用户类型\"\n        align=\"center\"\n        prop=\"userType\"\n        :show-overflow-tooltip=\"true\"\n      >\n        <template #default=\"{ row }\">\n          <dictionary-tag\n            code=\"systemUserType\"\n            :value=\"row.userType\"\n          ></dictionary-tag>\n        </template>\n      </el-table-column>\n      <el-table-column\n        label=\"用户状态\"\n        align=\"center\"\n        prop=\"userType\"\n        :show-overflow-tooltip=\"true\"\n      >\n        <template #default=\"{ row }\">\n          <dictionary-tag\n            code=\"commonStatus\"\n            :value=\"row.status\"\n          ></dictionary-tag>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"创建时间\" align=\"center\" width=\"160\">\n        <template #default=\"{ row }\">\n          <span>{{ parseTime(row.gmtCreate) }}</span>\n        </template>\n      </el-table-column>\n    </el-table>\n\n    <pagination\n      v-show=\"totalCount > 0\"\n      :total=\"totalCount\"\n      v-model:page=\"queryParams.pageNum\"\n      v-model:limit=\"queryParams.pageSize\"\n      @pagination=\"getPage\"\n    />\n\n    <template #footer>\n      <div class=\"dialog-footer\">\n        <el-button @click=\"close\">取 消</el-button>\n        <el-button type=\"primary\" @click=\"handleSave\">确 定</el-button>\n      </div>\n    </template>\n  </el-dialog>\n</template>\n<script setup>\nimport { getCurrentInstance, ref } from 'vue'\n\nimport { userUnAuthPage, authUser } from '@/api/system/role'\n\nconst { proxy } = getCurrentInstance()\n\nconst visible = ref(false)\nconst queryParams = ref({\n  pageNum: 1,\n  pageSize: 10,\n  username: undefined,\n  nickName: undefined,\n  status: undefined,\n  userType: undefined,\n  roleId: undefined\n})\n\nconst loading = ref(true)\nconst dataList = ref([])\nconst totalCount = ref(0)\nconst ids = ref([])\n\n/**\n * 显示弹窗\n */\nasync function show(roleId) {\n  visible.value = true\n  queryParams.value.roleId = roleId\n  handleQuery()\n}\n\n/**\n * 搜索\n */\nasync function handleQuery() {\n  getPage()\n}\n\n/**\n * 分页\n */\nasync function getPage() {\n  loading.value = true\n  const {\n    data: { total, list }\n  } = await userUnAuthPage(queryParams.value)\n  totalCount.value = parseInt(total)\n  dataList.value = list\n  loading.value = false\n}\n\n/**\n * 保存\n */\nconst emit = defineEmits(null)\nasync function handleSave() {\n  if (ids.value.length === 0) {\n    close()\n    return\n  }\n  // 保存\n  const form = {\n    roleId: queryParams.value.roleId,\n    userIds: ids.value\n  }\n  await authUser(form)\n  proxy.$modal.msgSuccess('授权用户成功')\n  emit('fetch-data')\n  close()\n}\n\n/**\n * 关闭\n */\nfunction close() {\n  visible.value = false\n}\n\n/**\n * 选择条数\n */\nfunction handleSelectionChange(selection) {\n  ids.value = selection.map((item) => item.id)\n}\n\ndefineExpose({\n  show\n})\n</script>\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/views/system/role/index.vue",
    "content": "<template>\n  <div class=\"app-container\">\n    <el-form\n      :model=\"queryParams\"\n      ref=\"queryFormRef\"\n      :inline=\"true\"\n      label-width=\"68px\"\n    >\n      <el-form-item label=\"角色名称\" prop=\"roleName\">\n        <el-input\n          v-model=\"queryParams.roleName\"\n          placeholder=\"请输入角色名称\"\n          clearable\n          style=\"width: 240px\"\n          @keyup.enter=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"权限字符\" prop=\"roleCode\">\n        <el-input\n          v-model=\"queryParams.roleCode\"\n          placeholder=\"请输入权限字符\"\n          clearable\n          style=\"width: 240px\"\n          @keyup.enter=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"状态\" prop=\"status\"\n        ><el-select\n          v-model=\"queryParams.status\"\n          placeholder=\"角色状态\"\n          @change=\"handleQuery\"\n          clearable\n        >\n          <dictionary-option code=\"commonStatus\" />\n        </el-select>\n      </el-form-item>\n      <el-form-item>\n        <el-button type=\"primary\" icon=\"Search\" @click=\"handleQuery\"\n          >搜索</el-button\n        >\n        <el-button icon=\"Refresh\" @click=\"handleResetQuery\">重置</el-button>\n      </el-form-item>\n    </el-form>\n    <el-row :gutter=\"10\" class=\"mb8\">\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"primary\"\n          icon=\"Plus\"\n          @click=\"handleAdd\"\n          v-permission=\"['system:role:add']\"\n          >新增</el-button\n        >\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"warning\"\n          icon=\"Edit\"\n          :disabled=\"single\"\n          @click=\"handleUpdate\"\n          v-permission=\"['system:role:update']\"\n          >修改</el-button\n        >\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"danger\"\n          icon=\"Delete\"\n          :disabled=\"multiple\"\n          @click=\"handleDelete\"\n          v-permission=\"['system:role:delete']\"\n          >删除</el-button\n        >\n      </el-col>\n    </el-row>\n\n    <el-table\n      border\n      v-loading=\"loading\"\n      :data=\"dataList\"\n      @selection-change=\"handleSelectionChange\"\n    >\n      <el-table-column type=\"selection\" width=\"55\" align=\"center\" />\n      <el-table-column label=\"角色编号\" prop=\"id\" width=\"180\" align=\"center\" />\n      <el-table-column\n        label=\"角色名称\"\n        prop=\"roleName\"\n        :show-overflow-tooltip=\"true\"\n      />\n      <el-table-column\n        label=\"角色编码\"\n        prop=\"roleCode\"\n        :show-overflow-tooltip=\"true\"\n      />\n      <el-table-column\n        label=\"显示顺序\"\n        prop=\"sort\"\n        width=\"100\"\n        align=\"center\"\n      />\n      <el-table-column label=\"状态\" align=\"center\" prop=\"status\" width=\"100\">\n        <template #default=\"{ row }\">\n          <dictionary-tag\n            code=\"commonStatus\"\n            :value=\"row.status\"\n          ></dictionary-tag>\n        </template>\n      </el-table-column>\n      <el-table-column\n        label=\"创建时间\"\n        align=\"center\"\n        prop=\"gmtCreate\"\n        width=\"160\"\n      >\n        <template #default=\"{ row }\">\n          <span>{{ parseTime(row.gmtCreate) }}</span>\n        </template>\n      </el-table-column>\n      <el-table-column\n        label=\"操作\"\n        align=\"center\"\n        class-name=\"small-padding fixed-width\"\n        width=\"220\"\n      >\n        <template #default=\"{ row }\">\n          <el-button\n            type=\"text\"\n            icon=\"Edit\"\n            @click=\"handleUpdate(row)\"\n            v-permission=\"['system:role:update']\"\n            v-if=\"superAdmin !== row.roleCode\"\n            >修改</el-button\n          >\n          <el-button\n            type=\"text\"\n            icon=\"Delete\"\n            @click=\"handleDelete(row)\"\n            v-permission=\"['system:role:delete']\"\n            v-if=\"superAdmin !== row.roleCode\"\n            >删除</el-button\n          >\n          <el-button\n            type=\"text\"\n            icon=\"User\"\n            @click=\"handleAuthUserRole(row)\"\n            v-permission=\"['system:role:authUser']\"\n            >分配用户</el-button\n          >\n        </template>\n      </el-table-column>\n    </el-table>\n\n    <pagination\n      v-show=\"totalCount > 0\"\n      :total=\"totalCount\"\n      v-model:page=\"queryParams.pageNum\"\n      v-model:limit=\"queryParams.pageSize\"\n      @pagination=\"getPage\"\n    />\n\n    <!-- 新增和编辑 -->\n    <edit @fetch-data=\"handleQuery\" ref=\"editRef\"></edit>\n  </div>\n</template>\n<script setup>\nimport { getCurrentInstance, ref } from 'vue'\n\nimport Edit from './components/RoleEdit'\n\nimport { page, remove } from '@/api/system/role'\nimport { superAdmin } from '@/config/setting.config'\nimport { useRouter } from 'vue-router'\n\nconst { proxy } = getCurrentInstance()\nconst router = useRouter()\n\nconst loading = ref(true)\nconst dataList = ref([])\nconst totalCount = ref(0)\nconst ids = ref([])\nconst single = ref(true)\nconst multiple = ref(true)\n\nconst queryParams = ref({\n  pageNum: 1,\n  pageSize: 10,\n  roleName: undefined,\n  roleCode: undefined,\n  status: undefined\n})\n\n/**\n * 获取数据\n */\nasync function getPage() {\n  loading.value = true\n  const {\n    data: { total, list }\n  } = await page(queryParams.value)\n  totalCount.value = parseInt(total)\n  dataList.value = list\n  loading.value = false\n}\n\n/**\n * 搜索\n */\nfunction handleQuery() {\n  getPage()\n}\n\n/**\n * 重置\n */\nfunction handleResetQuery() {\n  proxy.resetForm('queryFormRef')\n  handleQuery()\n}\n\n/**\n * 选择条数\n */\nfunction handleSelectionChange(selection) {\n  ids.value = selection.map((item) => item.id)\n  single.value = selection.length !== 1\n  multiple.value = !selection.length\n}\n\n/**\n * 新增\n */\nfunction handleAdd() {\n  proxy.$refs.editRef.show(undefined)\n}\n\n/**\n * 修改\n */\nfunction handleUpdate(row) {\n  const id = row.id || ids.value\n  proxy.$refs.editRef.show(id)\n}\n\n/**\n * 删除按钮操作\n */\nasync function handleDelete(row) {\n  const deleteIds = row.id || ids.value\n  proxy.$modal\n    .confirm('是否确认删除角色编号为【' + deleteIds + '】的数据项?')\n    .then(async function () {\n      await remove(deleteIds)\n      proxy.$modal.msgSuccess('删除角色成功')\n      handleQuery()\n    })\n}\n\n/**\n * 分配用户\n */\nfunction handleAuthUserRole(row) {\n  router.push('/system/role-auth/user/' + row.id)\n}\n\n/**\n * 初始化\n */\ngetPage()\n</script>\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/views/system/user/components/UserEdit.vue",
    "content": "<template>\n  <el-dialog\n    v-model=\"visible\"\n    :title=\"title\"\n    width=\"600px\"\n    append-to-body\n    :before-close=\"close\"\n  >\n    <el-form\n      :model=\"editForm\"\n      ref=\"editFormRef\"\n      label-width=\"80px\"\n      :rules=\"rules\"\n    >\n      <el-row>\n        <el-col :span=\"12\">\n          <el-form-item label=\"用户名\" prop=\"username\">\n            <el-input\n              v-model=\"editForm.username\"\n              placeholder=\"请输入用户名\"\n              maxlength=\"30\"\n            />\n          </el-form-item>\n        </el-col>\n        <el-col :span=\"12\">\n          <el-form-item label=\"昵称\" prop=\"nickName\">\n            <el-input\n              v-model=\"editForm.nickName\"\n              placeholder=\"请输入用户昵称\"\n              maxlength=\"30\"\n            />\n          </el-form-item>\n        </el-col>\n      </el-row>\n      <el-row>\n        <el-col :span=\"12\">\n          <el-form-item label=\"手机号码\" prop=\"mobilePhone\">\n            <el-input\n              v-model=\"editForm.mobilePhone\"\n              placeholder=\"请输入手机号码\"\n              maxlength=\"11\"\n            />\n          </el-form-item>\n        </el-col>\n        <el-col :span=\"12\">\n          <el-form-item label=\"邮箱\" prop=\"email\">\n            <el-input\n              v-model=\"editForm.email\"\n              placeholder=\"请输入邮箱\"\n              maxlength=\"50\"\n            />\n          </el-form-item>\n        </el-col>\n      </el-row>\n\n      <el-row>\n        <el-col :span=\"12\">\n          <el-form-item label=\"用户性别\" prop=\"sex\">\n            <el-select v-model=\"editForm.sex\" placeholder=\"请选择\">\n              <dictionary-option code=\"systemUserSex\" />\n            </el-select>\n          </el-form-item>\n        </el-col>\n        <el-col :span=\"12\">\n          <el-form-item label=\"状态\" prop=\"status\">\n            <el-radio-group v-model=\"editForm.status\">\n              <dictionary-radio code=\"commonStatus\" />\n            </el-radio-group>\n          </el-form-item>\n        </el-col>\n      </el-row>\n      <el-row v-if=\"!editForm.id\">\n        <el-col :span=\"12\">\n          <el-form-item label=\"用户密码\" prop=\"password\">\n            <el-input\n              v-model=\"editForm.password\"\n              placeholder=\"请输入用户密码\"\n              type=\"password\"\n              maxlength=\"20\"\n              show-password\n            />\n          </el-form-item>\n        </el-col>\n      </el-row>\n      <el-row>\n        <el-col :span=\"24\">\n          <el-form-item label=\"备注\" prop=\"remark\">\n            <el-input\n              v-model=\"editForm.remark\"\n              type=\"textarea\"\n              placeholder=\"请输入内容\"\n            ></el-input>\n          </el-form-item>\n        </el-col>\n      </el-row>\n    </el-form>\n\n    <template #footer>\n      <div class=\"dialog-footer\">\n        <el-button @click=\"close\">取 消</el-button>\n        <el-button type=\"primary\" @click=\"handleSave\">确 定</el-button>\n      </div>\n    </template>\n  </el-dialog>\n</template>\n<script setup>\nimport { getCurrentInstance, ref } from 'vue'\n\nimport { getById, add, update } from '@/api/system/user'\n\nconst { proxy } = getCurrentInstance()\n\nconst visible = ref(false)\nconst title = ref('')\nconst rules = ref({\n  username: [{ required: true, message: '用户名不能为空', trigger: 'blur' }],\n  nickName: [{ required: true, message: '昵称不能为空', trigger: 'blur' }],\n  password: [{ required: true, message: '密码不能为空', trigger: 'blur' }]\n})\nconst editForm = ref({\n  id: undefined,\n  sex: '1',\n  status: '1'\n})\n\n/**\n * 显示弹窗\n */\nasync function show(id) {\n  visible.value = true\n  if (id) {\n    title.value = '修改用户'\n    // 获取用户信息\n    const { data } = await getById(id)\n    editForm.value = data\n    editForm.value.sex = editForm.value.sex + ''\n    editForm.value.status = editForm.value.status + ''\n  } else {\n    title.value = '新增用户'\n    editForm.value.id = undefined\n  }\n}\n\n/**\n * 关闭\n */\nfunction close() {\n  visible.value = false\n  reset()\n}\n\n/**\n * 重置\n */\nfunction reset() {\n  proxy.resetForm('editFormRef')\n}\n\n/**\n * 提交数据\n */\nconst emit = defineEmits(null)\nfunction handleSave() {\n  proxy.$refs.editFormRef.validate(async (valid) => {\n    if (valid) {\n      // 修改\n      if (editForm.value.id) {\n        await update(editForm.value)\n        proxy.$modal.msgSuccess('修改用户成功')\n      } else {\n        // 新增\n        await add(editForm.value)\n        proxy.$modal.msgSuccess('新增用户成功')\n      }\n      emit('fetch-data')\n      close()\n    }\n  })\n}\n\ndefineExpose({\n  show\n})\n</script>\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/views/system/user/components/UserResetPassword.vue",
    "content": "<template>\n  <el-dialog\n    v-model=\"visible\"\n    title=\"提示\"\n    width=\"400px\"\n    append-to-body\n    :before-close=\"close\"\n  >\n    <div class=\"mb8\">请输入 {{ username }} 的新密码</div>\n    <el-form :model=\"editForm\" ref=\"editFormRef\" :rules=\"rules\">\n      <el-form-item prop=\"password\">\n        <el-input\n          :type=\"passwordType\"\n          v-model=\"editForm.password\"\n          placeholder=\"请输入密码\"\n        >\n        </el-input>\n        <span class=\"show-password\" @click=\"handleShowPassword\">\n          <svg-icon\n            :icon-name=\"passwordType === 'password' ? 'eye' : 'eye-open'\"\n          />\n        </span>\n      </el-form-item>\n    </el-form>\n\n    <template #footer>\n      <div class=\"dialog-footer\">\n        <el-button @click=\"close\">取 消</el-button>\n        <el-button type=\"primary\" @click=\"handleSave\">确 定</el-button>\n      </div>\n    </template>\n  </el-dialog>\n</template>\n<script setup>\nimport { getCurrentInstance, ref } from 'vue'\n\nimport { resetPassword } from '@/api/system/user'\n\nconst { proxy } = getCurrentInstance()\n\nconst visible = ref(false)\nconst username = ref('')\nconst editForm = ref({\n  id: undefined,\n  password: undefined\n})\n\nconst rules = ref({\n  password: [{ required: true, message: '密码不能为空', trigger: 'blur' }]\n})\n\n/**\n * 显示弹窗\n */\nfunction show(row) {\n  visible.value = true\n  editForm.value.id = row.id\n  username.value = row.username\n}\n\n/**\n * 提交数据\n */\nasync function handleSave() {\n  proxy.$refs.editFormRef.validate(async (valid) => {\n    if (valid) {\n      await resetPassword(editForm.value)\n      proxy.$modal.msgSuccess('修改密码成功')\n      close()\n    }\n  })\n}\n\n// 密码类型\nconst passwordType = ref('password')\n/**\n * 是否显示密码\n */\nfunction handleShowPassword() {\n  passwordType.value = passwordType.value === 'password' ? 'text' : 'password'\n}\n\n/**\n * 关闭\n */\nfunction close() {\n  visible.value = false\n  reset()\n}\n\n/**\n * 重置\n */\nfunction reset() {\n  proxy.resetForm('editFormRef')\n}\n\ndefineExpose({\n  show\n})\n</script>\n\n<style scoped>\n.show-password {\n  height: 39px;\n  width: 14px;\n  top: 0px;\n  position: absolute;\n  right: 10px;\n  font-size: 16px;\n  color: #889aa4;\n  cursor: pointer;\n  user-select: none;\n}\n</style>\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/views/system/user/index.vue",
    "content": "<template>\n  <div class=\"app-container\">\n    <el-form\n      :model=\"queryParams\"\n      ref=\"queryFormRef\"\n      :inline=\"true\"\n      label-width=\"68px\"\n    >\n      <el-form-item label=\"用户名\" prop=\"username\">\n        <el-input\n          v-model=\"queryParams.username\"\n          placeholder=\"请输入用户名\"\n          clearable\n          style=\"width: 240px\"\n          @keyup.enter=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"昵称\" prop=\"nickName\">\n        <el-input\n          v-model=\"queryParams.nickName\"\n          placeholder=\"请输入用户昵称\"\n          clearable\n          style=\"width: 240px\"\n          @keyup.enter=\"handleQuery\"\n        />\n      </el-form-item>\n      <el-form-item label=\"状态\" prop=\"status\">\n        <el-select\n          v-model=\"queryParams.status\"\n          placeholder=\"用户状态\"\n          @change=\"handleQuery\"\n          clearable\n          style=\"width: 240px\"\n        >\n          <dictionary-option code=\"commonStatus\" />\n        </el-select>\n      </el-form-item>\n      <el-form-item label=\"用户类型\" prop=\"userType\">\n        <el-select\n          v-model=\"queryParams.userType\"\n          placeholder=\"用户类型\"\n          @change=\"handleQuery\"\n          clearable\n          style=\"width: 240px\"\n        >\n          <dictionary-option code=\"systemUserType\" />\n        </el-select>\n      </el-form-item>\n      <el-form-item label=\"创建时间\" style=\"width: 308px\">\n        <el-date-picker\n          v-model=\"dateRange\"\n          value-format=\"YYYY-MM-DD\"\n          format=\"YYYY-MM-DD\"\n          type=\"daterange\"\n          range-separator=\"-\"\n          start-placeholder=\"开始日期\"\n          end-placeholder=\"结束日期\"\n        ></el-date-picker>\n      </el-form-item>\n      <el-form-item>\n        <el-button type=\"primary\" icon=\"Search\" @click=\"handleQuery\"\n          >搜索</el-button\n        >\n        <el-button icon=\"Refresh\" @click=\"handleResetQuery\">重置</el-button>\n      </el-form-item>\n    </el-form>\n\n    <el-row :gutter=\"10\" class=\"mb8\">\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"primary\"\n          icon=\"Plus\"\n          @click=\"handleAdd\"\n          v-permission=\"['system:user:add']\"\n          >新增</el-button\n        >\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"warning\"\n          icon=\"Edit\"\n          :disabled=\"single\"\n          @click=\"handleUpdate\"\n          v-permission=\"['system:user:update']\"\n          >修改</el-button\n        >\n      </el-col>\n      <el-col :span=\"1.5\">\n        <el-button\n          type=\"danger\"\n          icon=\"Delete\"\n          :disabled=\"multiple\"\n          @click=\"handleDelete\"\n          v-permission=\"['system:user:delete']\"\n          >删除</el-button\n        >\n      </el-col>\n    </el-row>\n\n    <el-table\n      v-loading=\"loading\"\n      :data=\"dataList\"\n      border\n      @selection-change=\"handleSelectionChange\"\n    >\n      <el-table-column type=\"selection\" width=\"50\" align=\"center\" />\n      <el-table-column label=\"用户编号\" align=\"center\" prop=\"id\" width=\"180\" />\n      <el-table-column\n        label=\"用户名\"\n        prop=\"username\"\n        :show-overflow-tooltip=\"true\"\n      />\n      <el-table-column\n        label=\"昵称\"\n        prop=\"nickName\"\n        :show-overflow-tooltip=\"true\"\n      />\n      <el-table-column\n        label=\"用户角色\"\n        prop=\"roleNames\"\n        :show-overflow-tooltip=\"true\"\n      />\n      <el-table-column\n        label=\"邮箱\"\n        prop=\"email\"\n        :show-overflow-tooltip=\"true\"\n      />\n      <el-table-column\n        label=\"手机号码\"\n        align=\"center\"\n        prop=\"mobilePhone\"\n        :show-overflow-tooltip=\"true\"\n      />\n      <el-table-column\n        label=\"性别\"\n        align=\"center\"\n        prop=\"sex\"\n        :show-overflow-tooltip=\"true\"\n      >\n        <template #default=\"{ row }\">\n          <dictionary-tag\n            code=\"systemUserSex\"\n            :value=\"row.sex\"\n          ></dictionary-tag>\n        </template>\n      </el-table-column>\n      <el-table-column\n        label=\"用户类型\"\n        align=\"center\"\n        prop=\"userType\"\n        :show-overflow-tooltip=\"true\"\n      >\n        <template #default=\"{ row }\">\n          <dictionary-tag\n            code=\"systemUserType\"\n            :value=\"row.userType\"\n          ></dictionary-tag>\n        </template>\n      </el-table-column>\n      <el-table-column\n        label=\"用户状态\"\n        align=\"center\"\n        prop=\"userType\"\n        :show-overflow-tooltip=\"true\"\n      >\n        <template #default=\"{ row }\">\n          <dictionary-tag\n            code=\"commonStatus\"\n            :value=\"row.status\"\n          ></dictionary-tag>\n        </template>\n      </el-table-column>\n      <el-table-column label=\"创建时间\" align=\"center\" width=\"160\">\n        <template #default=\"{ row }\">\n          <span>{{ parseTime(row.gmtCreate) }}</span>\n        </template>\n      </el-table-column>\n\n      <el-table-column\n        label=\"操作\"\n        align=\"center\"\n        width=\"220\"\n        class-name=\"small-padding fixed-width\"\n      >\n        <template #default=\"{ row }\">\n          <el-button\n            type=\"text\"\n            icon=\"Edit\"\n            @click=\"handleUpdate(row)\"\n            v-permission=\"['system:user:update']\"\n            >修改</el-button\n          >\n          <el-button\n            type=\"text\"\n            icon=\"Delete\"\n            @click=\"handleDelete(row)\"\n            v-permission=\"['system:user:delete']\"\n            >删除</el-button\n          >\n          <el-button\n            type=\"text\"\n            icon=\"Key\"\n            @click=\"handleResetPassword(row)\"\n            v-permission=\"['system:user:resetPassword']\"\n            >重置密码</el-button\n          >\n        </template>\n      </el-table-column>\n    </el-table>\n\n    <pagination\n      v-show=\"totalCount > 0\"\n      :total=\"totalCount\"\n      v-model:page=\"queryParams.pageNum\"\n      v-model:limit=\"queryParams.pageSize\"\n      @pagination=\"getPage\"\n    />\n\n    <!-- 新增和编辑 -->\n    <edit @fetch-data=\"handleQuery\" ref=\"editRef\"></edit>\n\n    <!-- 重置密码 -->\n    <reset-password ref=\"resetPasswordRef\"></reset-password>\n  </div>\n</template>\n<script setup>\nimport { getCurrentInstance, ref } from 'vue'\n\nimport Edit from './components/UserEdit'\nimport ResetPassword from './components/UserResetPassword'\n\nimport { page, remove } from '@/api/system/user'\n\nconst { proxy } = getCurrentInstance()\n\nconst queryParams = ref({\n  pageNum: 1,\n  pageSize: 10,\n  username: undefined,\n  nickName: undefined,\n  status: undefined,\n  userType: undefined,\n  beginGmtCreate: undefined,\n  endGmtCreate: undefined\n})\n\nconst loading = ref(true)\nconst dataList = ref([])\nconst dateRange = ref([])\nconst totalCount = ref(0)\nconst ids = ref([])\nconst single = ref(true)\nconst multiple = ref(true)\n\n/**\n * 搜索\n */\nfunction handleQuery() {\n  getPage()\n}\n\n/**\n * 重置\n */\nfunction handleResetQuery() {\n  proxy.resetForm('queryFormRef')\n  dateRange.value = undefined\n  handleQuery()\n}\n\n/**\n * 获取数据\n */\nasync function getPage() {\n  loading.value = true\n  queryParams.value.beginGmtCreate = undefined\n  queryParams.value.endGmtCreate = undefined\n  if (Array.isArray(dateRange.value) && dateRange.value.length === 2) {\n    queryParams.value.beginGmtCreate = dateRange.value[0] + ' 00:00:00'\n    queryParams.value.endGmtCreate = dateRange.value[1] + ' 23:59:59'\n  }\n  const {\n    data: { total, list }\n  } = await page(queryParams.value)\n  totalCount.value = parseInt(total)\n  dataList.value = list\n  loading.value = false\n}\n\n/**\n * 选择条数\n */\nfunction handleSelectionChange(selection) {\n  ids.value = selection.map((item) => item.id)\n  single.value = selection.length !== 1\n  multiple.value = !selection.length\n}\n\n/**\n * 新增\n */\nfunction handleAdd() {\n  proxy.$refs.editRef.show(undefined)\n}\n\n/**\n * 修改\n */\nfunction handleUpdate(row) {\n  const id = row.id || ids.value\n  proxy.$refs.editRef.show(id)\n}\n\n/**\n * 删除按钮操作\n */\nasync function handleDelete(row) {\n  const deleteIds = row.id || ids.value\n  proxy.$modal\n    .confirm('是否确认删除用户编号为【' + deleteIds + '】的数据项?')\n    .then(async function () {\n      await remove(deleteIds)\n      proxy.$modal.msgSuccess('删除用户成功')\n      handleQuery()\n    })\n}\n\n/**\n * 重置密码\n */\nfunction handleResetPassword(row) {\n  proxy.$refs.resetPasswordRef.show(row)\n}\n\n/**\n * 初始化\n */\ngetPage()\n</script>\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/views/system/user/profile/UpdateUserInfo.vue",
    "content": "<template>\n  <el-card>\n    <template #header>\n      <div>\n        <span>基本资料</span>\n      </div>\n    </template>\n    <el-tabs v-model=\"activeTab\">\n      <el-tab-pane label=\"基本资料\" name=\"updateUser\">\n        <el-form\n          :model=\"editForm\"\n          ref=\"editFormRef\"\n          label-width=\"80px\"\n          :rules=\"rules\"\n        >\n          <el-form-item label=\"用户名\" prop=\"username\">\n            <el-input\n              v-model=\"editForm.username\"\n              placeholder=\"请输入用户名\"\n              maxlength=\"30\"\n            />\n          </el-form-item>\n          <el-form-item label=\"昵称\" prop=\"nickName\">\n            <el-input\n              v-model=\"editForm.nickName\"\n              placeholder=\"请输入用户昵称\"\n              maxlength=\"30\"\n            />\n          </el-form-item>\n          <el-form-item label=\"手机号码\" prop=\"mobilePhone\">\n            <el-input\n              v-model=\"editForm.mobilePhone\"\n              placeholder=\"请输入手机号码\"\n              maxlength=\"11\"\n            />\n          </el-form-item>\n          <el-form-item label=\"邮箱\" prop=\"email\">\n            <el-input\n              v-model=\"editForm.email\"\n              placeholder=\"请输入邮箱\"\n              maxlength=\"50\"\n            />\n          </el-form-item>\n          <el-form-item label=\"用户性别\" prop=\"sex\">\n            <el-radio-group v-model=\"editForm.sex\" @keyup.enter=\"handleSave\">\n              <dictionary-radio code=\"systemUserSex\" />\n            </el-radio-group>\n          </el-form-item>\n          <el-form-item>\n            <el-button type=\"primary\" @click=\"handleSave\" :loading=\"editLoading\"\n              >保 存</el-button\n            >\n            <el-button type=\"danger\" @click=\"close\">关 闭</el-button>\n          </el-form-item>\n        </el-form>\n      </el-tab-pane>\n      <el-tab-pane label=\"修改密码\" name=\"updatePassword\">\n        <el-form\n          :model=\"editPasswordForm\"\n          ref=\"editPasswordFormRef\"\n          label-width=\"80px\"\n          class=\"password-form\"\n          :rules=\"passwordRules\"\n        >\n          <el-form-item label=\"旧密码\" prop=\"oldPassword\">\n            <el-input\n              v-model=\"editPasswordForm.oldPassword\"\n              :type=\"passwordType\"\n              auto-complete=\"off\"\n              placeholder=\"旧密码，使用第三方登录并且未重置密码，可不填\"\n              @keyup=\"checkCapslock\"\n              @blur=\"capsTooltip = false\"\n              @keyup.enter=\"handleSavePassword\"\n            />\n            <span class=\"show-password\" @click=\"handleShowPassword\">\n              <svg-icon\n                :icon-name=\"passwordType === 'password' ? 'eye' : 'eye-open'\"\n              />\n            </span>\n          </el-form-item>\n          <el-form-item label=\"新密码\" prop=\"newPassword\">\n            <el-input\n              v-model=\"editPasswordForm.newPassword\"\n              :type=\"passwordType\"\n              auto-complete=\"off\"\n              placeholder=\"请输入新密码\"\n              @keyup=\"checkCapslock\"\n              @blur=\"capsTooltip = false\"\n              @keyup.enter=\"handleSavePassword\"\n            />\n            <span class=\"show-password\" @click=\"handleShowPassword\">\n              <svg-icon\n                :icon-name=\"passwordType === 'password' ? 'eye' : 'eye-open'\"\n              />\n            </span>\n          </el-form-item>\n          <el-form-item label=\"确认密码\" prop=\"confirmPassword\">\n            <el-input\n              v-model=\"editPasswordForm.confirmPassword\"\n              :type=\"passwordType\"\n              auto-complete=\"off\"\n              placeholder=\"请再次输入新密码\"\n              @keyup=\"checkCapslock\"\n              @blur=\"capsTooltip = false\"\n              @keyup.enter=\"handleSavePassword\"\n            />\n            <span class=\"show-password\" @click=\"handleShowPassword\">\n              <svg-icon\n                :icon-name=\"passwordType === 'password' ? 'eye' : 'eye-open'\"\n              />\n            </span>\n          </el-form-item>\n          <el-form-item>\n            <el-button\n              type=\"primary\"\n              @click=\"handleSavePassword\"\n              :loading=\"editPasswordLoading\"\n              >保 存</el-button\n            >\n            <el-button type=\"danger\" @click=\"close\">关 闭</el-button>\n          </el-form-item>\n        </el-form>\n      </el-tab-pane>\n    </el-tabs>\n  </el-card>\n</template>\n<script setup>\nimport { getCurrentInstance, ref, watch } from 'vue'\n\nimport { userUpdateInfo, resetUserPassword } from '@/api/system/user'\n\nconst props = defineProps({\n  user: {\n    type: Object,\n    require: true\n  }\n})\n\nconst { proxy } = getCurrentInstance()\n\nconst activeTab = ref('updateUser')\nconst editLoading = ref(false)\nconst editForm = ref({})\nconst rules = ref({\n  username: [{ required: true, message: '用户名不能为空', trigger: 'blur' }],\n  nickName: [{ required: true, message: '昵称不能为空', trigger: 'blur' }]\n})\n\nconst editPasswordLoading = ref(false)\nconst editPasswordForm = ref({\n  oldPassword: undefined,\n  newPassword: undefined,\n  confirmPassword: undefined\n})\n\n// 校验密码是否相等\nconst checkNewPasswordIsEqual = (rule, value, callback) => {\n  if (editPasswordForm.value.newPassword !== value) {\n    callback(new Error('两次输入的密码不一致'))\n  } else {\n    callback()\n  }\n}\nconst passwordRules = ref({\n  newPassword: [\n    { required: true, message: '新密码不能为空', trigger: 'blur' },\n    { min: 6, max: 20, message: '长度在 6 到 20 个字符', trigger: 'blur' }\n  ],\n  confirmPassword: [\n    { required: true, message: '确认密码不能为空', trigger: 'blur' },\n    { required: true, validator: checkNewPasswordIsEqual, trigger: 'blur' }\n  ]\n})\n\n/**\n * 监听变化，否则数据有变动不会生效\n */\nwatch(props, (nweProps, oldProps) => {\n  const user = nweProps.user\n  editForm.value.username = user.username\n  editForm.value.nickName = user.nickName\n  editForm.value.mobilePhone = user.mobilePhone\n  editForm.value.email = user.email\n  editForm.value.sex = user.sex + ''\n})\n\n/**\n * 保存用户信息\n */\nconst emit = defineEmits(null)\nfunction handleSave() {\n  proxy.$refs.editFormRef.validate(async (valid) => {\n    if (valid) {\n      editLoading.value = true\n      try {\n        await userUpdateInfo(editForm.value)\n        proxy.$modal.msgSuccess('修改基本资料成功')\n        emit('fetch-data')\n      } finally {\n        editLoading.value = false\n      }\n    }\n  })\n}\n\n// 大写提示\nconst capsTooltip = ref(false)\n/**\n * 检测是否为大写\n */\nfunction checkCapslock(e) {\n  const { key } = e\n  capsTooltip.value = key && key.length === 1 && key >= 'A' && key <= 'Z'\n}\n\n// 密码类型\nconst passwordType = ref('password')\n/**\n * 是否显示密码\n */\nfunction handleShowPassword() {\n  passwordType.value = passwordType.value === 'password' ? 'text' : 'password'\n}\n\n/**\n * 修改密码\n */\nfunction handleSavePassword() {\n  proxy.$refs.editPasswordFormRef.validate(async (valid) => {\n    if (valid) {\n      editPasswordLoading.value = true\n      try {\n        await resetUserPassword(editPasswordForm.value)\n        proxy.$modal.msgSuccess('修改密码成功')\n      } finally {\n        editPasswordLoading.value = false\n      }\n    }\n  })\n}\n\n/**\n * 关闭按钮\n */\nfunction close() {\n  proxy.$tab.closePage()\n}\n</script>\n<style lang=\"scss\" scoped>\n.password-form {\n  .el-input {\n    height: 38px;\n    input {\n      height: 38px;\n    }\n    .input-icon {\n      height: 39px;\n      width: 14px;\n      margin-left: 2px;\n    }\n  }\n  .show-password {\n    height: 39px;\n    width: 14px;\n    position: absolute;\n    right: 10px;\n    line-height: 39px;\n    font-size: 16px;\n    color: #889aa4;\n    cursor: pointer;\n    user-select: none;\n  }\n}\n</style>\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/views/system/user/profile/UserAvatar.vue",
    "content": "<template>\n  <div>\n    <img\n      :src=\"getPictureShowUrl(options.img, options.img === defaultAvatar)\"\n      class=\"site-image-cropper\"\n      @click=\"handleEditCropper\"\n    />\n\n    <el-dialog\n      v-model=\"visible\"\n      :title=\"title\"\n      width=\"800px\"\n      append-to-body\n      @opened=\"modalOpened\"\n      :before-close=\"beforeClose\"\n    >\n      <el-row>\n        <el-col :xs=\"24\" :md=\"12\" :style=\"{ height: '350px' }\">\n          <vue-cropper\n            ref=\"cropperRef\"\n            :img=\"getPictureShowUrl(options.img, options.img === defaultAvatar)\"\n            :info=\"true\"\n            :autoCrop=\"options.autoCrop\"\n            :autoCropWidth=\"options.autoCropWidth\"\n            :autoCropHeight=\"options.autoCropHeight\"\n            :fixedBox=\"options.fixedBox\"\n            @realTime=\"realTime\"\n            v-if=\"visibleCropper\"\n          />\n        </el-col>\n        <el-col :xs=\"24\" :md=\"12\" :style=\"{ height: '350px' }\">\n          <div class=\"image-upload-preview\">\n            <img\n              :src=\"getPictureShowUrl(options.previews.url)\"\n              :style=\"options.previews.img\"\n            />\n          </div>\n        </el-col>\n      </el-row>\n      <br />\n      <el-row>\n        <el-col :lg=\"2\" :md=\"2\">\n          <el-upload\n            action=\"#\"\n            accept=\".jpg,.png,.jpeg,.ico,.gif,.svg.tiff\"\n            :http-request=\"requestUpload\"\n            :show-file-list=\"false\"\n            :before-upload=\"beforeUpload\"\n          >\n            <el-button>\n              <span class=\"upload-btn\">上传</span>\n              <el-icon><UploadFilled /></el-icon>\n            </el-button>\n          </el-upload>\n        </el-col>\n        <el-col :lg=\"{ span: 1, offset: 2 }\" :md=\"2\">\n          <el-button icon=\"Plus\" @click=\"changeScale(1)\"></el-button>\n        </el-col>\n        <el-col :lg=\"{ span: 1, offset: 1 }\" :md=\"2\">\n          <el-button icon=\"Minus\" @click=\"changeScale(-1)\"></el-button>\n        </el-col>\n        <el-col :lg=\"{ span: 1, offset: 1 }\" :md=\"2\">\n          <el-button icon=\"RefreshLeft\" @click=\"rotateLeft()\"></el-button>\n        </el-col>\n        <el-col :lg=\"{ span: 1, offset: 1 }\" :md=\"2\">\n          <el-button icon=\"RefreshRight\" @click=\"rotateRight()\"></el-button>\n        </el-col>\n        <el-col :lg=\"{ span: 2, offset: 6 }\" :md=\"2\">\n          <el-button type=\"primary\" @click=\"handleSave\" :loading=\"editLoading\"\n            >提 交</el-button\n          >\n        </el-col>\n      </el-row>\n    </el-dialog>\n  </div>\n</template>\n<script setup>\nimport { getCurrentInstance, ref, watch } from 'vue'\nimport 'vue-cropper/dist/index.css'\nimport { VueCropper } from 'vue-cropper'\n\nimport defaultAvatar from '@/assets/images/profile.jpg'\n\nimport { uploadAvavar } from '@/api/nav/picture'\n\nconst props = defineProps({\n  // 图片路径\n  path: {\n    type: String,\n    require: true\n  },\n  // 标题\n  title: {\n    type: String,\n    default: '上传头像'\n  }\n})\n\nconst { proxy } = getCurrentInstance()\n\nconst visible = ref(false)\nconst visibleCropper = ref(false)\nconst editLoading = ref(false)\n\nconst options = ref({\n  // 裁剪图片的地址\n  img: props.path ? props.path : defaultAvatar,\n  // 裁剪生成图片的质量\n  outputSize: 1,\n  // 裁剪生成图片的格式\n  outputType: 'png',\n  // 是否默认生成截图框\n  autoCrop: true,\n  // 默认生成截图框宽度\n  autoCropWidth: 200,\n  // 默认生成截图框高度\n  autoCropHeight: 200,\n  // 固定截图框大小 不允许改变\n  fixedBox: true,\n  // 预览数据\n  previews: {}\n})\n\n/**\n * 编辑图片显示\n */\nfunction handleEditCropper() {\n  visible.value = true\n}\n\n/**\n * 打开弹出层结束时的回调\n */\nfunction modalOpened() {\n  visibleCropper.value = true\n}\n\n/**\n * 手动关闭\n */\nfunction beforeClose() {\n  close()\n  options.value.img = props.path\n}\n\n/**\n * 关闭\n */\nfunction close() {\n  visible.value = false\n  visibleCropper.value = false\n}\n\n/**\n * 实时预览\n */\nfunction realTime(data) {\n  options.value.previews = data\n}\n\n/**\n * 向左旋转\n */\nfunction rotateLeft() {\n  proxy.$refs.cropperRef.rotateLeft()\n}\n\n/**\n * 向右旋转\n */\nfunction rotateRight() {\n  proxy.$refs.cropperRef.rotateRight()\n}\n\n/**\n * 图片缩放\n */\nfunction changeScale(num) {\n  num = num || 1\n  proxy.$refs.cropperRef.changeScale(num)\n}\n\n/**\n * 上传预处理\n */\nfunction beforeUpload(file) {\n  if (file.type.indexOf('image/') === -1) {\n    proxy.$modal.msgError(\n      '文件格式错误，请上传图片类型,如：JPG，PNG后缀的文件。'\n    )\n  } else {\n    const reader = new FileReader()\n    reader.readAsDataURL(file)\n    reader.onload = () => {\n      options.value.img = reader.result\n    }\n  }\n}\n/**\n * 上传图片\n */\nconst emit = defineEmits(null)\nfunction handleSave() {\n  proxy.$refs.cropperRef.getCropBlob(async (data) => {\n    const formData = new FormData()\n    formData.append('file', data)\n    editLoading.value = true\n    try {\n      let path = ''\n      const {\n        data: { filePath }\n      } = await uploadAvavar(formData)\n      path = filePath\n\n      options.value.img = path\n      proxy.$modal.msgSuccess('上传成功')\n      emit('success', path)\n      close()\n    } finally {\n      editLoading.value = false\n    }\n  })\n}\n\nwatch(\n  () => props.path,\n  (newVal, oldVal) => {\n    options.value.img = newVal\n  }\n)\n</script>\n<style lang=\"scss\" scoped>\n.upload-btn {\n  margin-right: 10px;\n}\n</style>\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/views/system/user/profile/UserBaseInfo.vue",
    "content": "<template>\n  <el-card :v-loading=\"loading\">\n    <template #header>\n      <div>\n        <span>个人信息</span>\n      </div>\n    </template>\n\n    <user-avatar\n      :path=\"user.avatar ? user.avatar : defaultAvatar\"\n      @success=\"handleUploadAvatarSuccess\"\n    />\n    <ul class=\"list-group list-group-striped\">\n      <li class=\"list-group-item\">\n        <svg-icon icon-name=\"user\" />用户名\n        <div class=\"pull-right\">{{ user.username }}</div>\n      </li>\n      <li class=\"list-group-item\">\n        <svg-icon icon-name=\"phone\" />手机号码\n        <div class=\"pull-right\">{{ user.mobilePhone }}</div>\n      </li>\n      <li class=\"list-group-item\">\n        <svg-icon icon-name=\"email\" />用户邮箱\n        <div class=\"pull-right\">{{ user.email }}</div>\n      </li>\n      <li class=\"list-group-item\">\n        <svg-icon icon-name=\"date\" />注册时间\n        <div class=\"pull-right\">{{ user.gmtCreate }}</div>\n      </li>\n    </ul>\n  </el-card>\n</template>\n<script setup>\nimport { useStore } from 'vuex'\n\nimport UserAvatar from './UserAvatar'\nimport defaultAvatar from '@/assets/images/profile.jpg'\n\ndefineProps({\n  user: {\n    type: Object,\n    require: true\n  }\n})\n\nconst store = useStore()\n\n/**\n * 头像上传成功\n */\nfunction handleUploadAvatarSuccess(avatar) {\n  store.dispatch('user/setAvatar', avatar)\n}\n</script>\n"
  },
  {
    "path": "vue-geshanzsq-nav/src/views/system/user/profile/index.vue",
    "content": "<template>\n  <div class=\"app-container\">\n    <el-row :gutter=\"20\">\n      <el-col :span=\"8\" :xs=\"24\" :v-loading=\"loading\">\n        <user-base-info :user=\"user\" />\n      </el-col>\n      <el-col :span=\"16\" :xs=\"24\" :v-loading=\"loading\">\n        <update-user-info :user=\"user\" @fetch-data=\"getData\" />\n      </el-col>\n    </el-row>\n  </div>\n</template>\n<script setup>\nimport UserBaseInfo from './UserBaseInfo'\nimport UpdateUserInfo from './UpdateUserInfo'\n\nimport { getUserInfo } from '@/api/auth/user'\nimport { ref } from 'vue'\n\nconst loading = ref(true)\nconst user = ref({})\n\nasync function getData() {\n  loading.value = true\n  try {\n    const { data } = await getUserInfo()\n    user.value = data\n  } finally {\n    loading.value = false\n  }\n}\n\ngetData()\n</script>\n\n<style lang=\"scss\" scoped>\n.card-nav {\n  margin-top: 20px;\n}\n</style>\n"
  },
  {
    "path": "vue-geshanzsq-nav/vue.config.js",
    "content": "const path = require('path')\nfunction resolve(dir) {\n  return path.join(__dirname, dir)\n}\n\nconst { defineConfig } = require('@vue/cli-service')\nconst CompressionPlugin = require('compression-webpack-plugin')\n\nconst { devPort, devOpenBrowser } = require('./src/config/vue.cli.config')\n\nconst {\n  siteTitle,\n  siteDescription,\n  siteKeywords,\n  siteLoading\n} = require('./src/config/setting.config')\n\nprocess.env.VUE_APP_SITE_TITLE = siteTitle\nprocess.env.VUE_APP_SITE_DESCRIPTION = siteDescription\nprocess.env.VUE_APP_SITE_KEYWORDS = siteKeywords\nprocess.env.VUE_APP_SITE_LOADING = siteLoading\n\nmodule.exports = defineConfig({\n  transpileDependencies: true,\n  // webpack-dev-server 相关配置\n  devServer: {\n    port: devPort,\n    open: devOpenBrowser,\n    proxy: {\n      [process.env.VUE_APP_BASE_API]: {\n        target: 'http://localhost:8083/geshanzsq-nav-api',\n        changeOrigin: true,\n        pathRewrite: {\n          ['^' + process.env.VUE_APP_BASE_API]: ''\n        }\n      }\n    },\n    client: {\n      // 编译错误时，取消全屏覆盖（建议关掉）\n      overlay: false\n    }\n  },\n  configureWebpack: {\n    plugins: [\n      new CompressionPlugin({\n        // 使用gzip压缩\n        algorithm: 'gzip',\n        // 匹配文件名\n        test: /\\.js$|\\.html$|\\.css$/,\n        // 压缩后的文件名(保持原文件名，后缀加.gz)\n        filename: '[path][base].gz',\n        // 压缩率小于1才会压缩\n        minRatio: 1,\n        // 对超过10k的数据压缩\n        threshold: 10240,\n        // 是否删除未压缩的源文件，谨慎设置，如果希望提供非gzip的资源，可不设置或者设置为false（比如删除打包后的gz后还可以加载到原始资源文件）\n        deleteOriginalAssets: false\n      })\n    ]\n  },\n  // svg-sprite-loader 设置\n  chainWebpack(config) {\n    config.module.rule('svg').exclude.add(resolve('src/assets/icons')).end()\n    config.module\n      .rule('icons')\n      .test(/\\.svg$/)\n      .include.add(resolve('src/assets/icons'))\n      .end()\n      .use('svg-sprite-loader')\n      .loader('svg-sprite-loader')\n      .options({\n        symbolId: 'icon-[name]'\n      })\n      .end()\n  }\n})\n"
  }
]