diff --git a/web-vue/.editorconfig b/web-vue/.editorconfig
new file mode 100644
index 00000000..392a883a
--- /dev/null
+++ b/web-vue/.editorconfig
@@ -0,0 +1,28 @@
+root = true
+
+[*]
+charset = utf-8
+end_of_line = lf
+insert_final_newline = true
+indent_style = space
+indent_size = 2
+max_line_length = 100
+
+[*.{ts,mts,tsx,vue}]
+ij_typescript_use_chained_calls_group_indents = false
+ij_typescript_use_double_quotes = false
+ij_typescript_use_explicit_js_extension = auto
+ij_typescript_use_import_type = auto
+ij_typescript_use_path_mapping = always
+ij_typescript_use_public_modifier = false
+ij_typescript_use_semicolon_after_statement = true
+
+[*.{yml,yaml,json}]
+indent_style = space
+indent_size = 2
+
+[*.md]
+trim_trailing_whitespace = false
+
+[Makefile]
+indent_style = tab
diff --git a/web-vue/.gitee/ISSUE_TEMPLATE.zh-CN.md b/web-vue/.gitee/ISSUE_TEMPLATE.zh-CN.md
new file mode 100644
index 00000000..e62376c3
--- /dev/null
+++ b/web-vue/.gitee/ISSUE_TEMPLATE.zh-CN.md
@@ -0,0 +1,22 @@
+### 是什么问题、该问题是怎么引起的?
+
+1.
+
+### 重现步骤、期望结果、截图、代码
+
+1.
+```
+这里贴你的代码块
+```
+
+### 实际结果、报错信息、截图
+
+1.
+```
+这里贴错误信息
+```
+
+### 环境版本:
+
+- 浏览器版本:Chrome xx、Firefox xx、其它
+- 平台版本:JeeSite 4.x.x、5.x.x(package.json里查看)
diff --git a/web-vue/.gitignore b/web-vue/.gitignore
new file mode 100644
index 00000000..9599e3ce
--- /dev/null
+++ b/web-vue/.gitignore
@@ -0,0 +1,36 @@
+node_modules
+Thumbs.db
+.DS_Store
+.vercel
+.turbo
+.cache
+dist
+
+upgrade
+
+# Test files
+tests/server/static
+tests/server/static/upload
+coverage
+
+# Local files
+.local
+.env.local
+.env.*.local
+.eslintcache
+
+# Log files
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+vite.config.ts.timestamp*
+
+# IDE files
+.idea
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
+*.iml
diff --git a/web-vue/.npmrc b/web-vue/.npmrc
new file mode 100644
index 00000000..746ac7dd
--- /dev/null
+++ b/web-vue/.npmrc
@@ -0,0 +1,5 @@
+@jeesite:registry=https://maven.jeesite.net/repository/npm-package/
+registry=https://registry.npmmirror.com
+package-manager-strict=false
+auto-install-peers = true
+git-checks=false
diff --git a/web-vue/.prettierignore b/web-vue/.prettierignore
new file mode 100644
index 00000000..136f2fc8
--- /dev/null
+++ b/web-vue/.prettierignore
@@ -0,0 +1,14 @@
+dist
+public
+node_modules
+
+.local
+.npmrc
+.output.js
+
+*.sh
+*.md
+*.svg
+*.html
+*.json
+*-lock.yaml
diff --git a/web-vue/.prettierrc.mjs b/web-vue/.prettierrc.mjs
new file mode 100644
index 00000000..987cd1c4
--- /dev/null
+++ b/web-vue/.prettierrc.mjs
@@ -0,0 +1,32 @@
+/**
+ * Copyright (c) 2013-Now http://jeesite.com All rights reserved.
+ * No deletion without permission, or be held responsible to law.
+ * @author ThinkGem
+ */
+export default {
+ printWidth: 120,
+ tabWidth: 2,
+ useTabs: false,
+ semi: true,
+ vueIndentScriptAndStyle: true,
+ singleQuote: true,
+ quoteProps: 'as-needed',
+ bracketSpacing: true,
+ trailingComma: 'all',
+ jsxSingleQuote: false,
+ arrowParens: 'always',
+ insertPragma: false,
+ requirePragma: false,
+ proseWrap: 'preserve',
+ htmlWhitespaceSensitivity: 'strict',
+ endOfLine: 'auto',
+ plugins: ['prettier-plugin-packagejson'],
+ overrides: [
+ {
+ files: '.*rc',
+ options: {
+ parser: 'json',
+ },
+ },
+ ],
+};
diff --git a/web-vue/.stylelintignore b/web-vue/.stylelintignore
new file mode 100644
index 00000000..9e3d42bb
--- /dev/null
+++ b/web-vue/.stylelintignore
@@ -0,0 +1,6 @@
+dist
+public
+node_modules
+
+*.sh
+*.md
diff --git a/web-vue/LICENSE b/web-vue/LICENSE
new file mode 100644
index 00000000..233ef956
--- /dev/null
+++ b/web-vue/LICENSE
@@ -0,0 +1,223 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright (C) 2013-Now, http://jeesite.com (thinkgem@163.com).
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+============================================================================
+
+授权许可补充协议条款:
+
+1. 基于 Apache License Version 2.0 协议发布,可用于商业项目,但必须遵守以下补充条款。
+2. 不得将本软件应用于危害国家安全、荣誉和利益的行为,不能以任何形式用于非法为目的的行为。
+3. 在延伸的代码中(修改和有源代码衍生的代码中)需要带有原来代码中的协议、版权声明和其他原作者
+ 规定需要包含的说明(请尊重原作者的著作权,不要删除或修改文件中的`Copyright`和`@author`信息)
+ 更不要,全局替换源代码中的 jeesite 或 ThinkGem 等字样,否则你将违反本协议条款承担责任。
+4. 基于本软件的作品,只能使用 JeeSite5 作为后台服务,除外情况不可商用且不允许二次分发或开源。
+5. 您若套用本软件的一些代码或功能参考,请保留源文件中的版权和作者,需要在您的软件介绍明显位置
+ 说明出处,举例:本软件基于 JeeSite Vue 快速开发平台,并附带链接:http://jeesite.com
+6. 任何基于本软件而产生的一切法律纠纷和责任,均于我司无关。
+7. 如果你对本软件有改进,希望可以贡献给我们,共同进步。
+8. 本项目已申请软件著作权,请尊重开源,感谢阅读。
+
+版权所有:济南卓源软件有限公司
+
+官方网址:http://jeesite.com
+
+技术服务:http://s.jeesite.com
diff --git a/web-vue/README.md b/web-vue/README.md
new file mode 100644
index 00000000..6b5e53c9
--- /dev/null
+++ b/web-vue/README.md
@@ -0,0 +1,819 @@
+
+
+
+
+
+
+ JeeSite Vue3 前端源码
+ 使用 Turborepo、Monorepo、pnpm
+ 快速构建、模块化、代码复用、高效管理
+
+
+
+
+
+
+
+
+
+
+
+------
+
+
+ 如果你喜欢 JeeSite,请给她一个 ⭐️ Star,您的支持将是我们前行的动力。
+
+
+------
+
+1. 单仓多包 pnpm + Turborepo 涡轮增压,提升编译速度,方便统一管理脚本任务
+2. 按功能模块进行拆分为不同的包,方便进行团队开发源码管理,可根据需要进行发包
+3. 模块之间松耦合,单依赖,公共模块,公共组件,公共工具,方便代码复用
+4. 可方便从传统架构版本,升级到 Monorepo 模块化、分包架构
+
+## 技术交流
+
+* 官方网站:
+* 使用文档:
+* 问题反馈:[http://jeesite.net](https://gitee.com/thinkgem/jeesite5/issues/new) |
+ [gitcode](https://gitcode.com/thinkgem/jeesite/issues/create/choose) |
+ [新手必读](https://gitee.com/thinkgem/jeesite5/issues/I18ARR)
+* 联系我们:
+* 关注微信公众号,了解最新动态:
+
+
+
+
+
+* QQ 群:`127515876`、`209330483`、`223507718`、`709534275`、`730390092`、`1373527`、`183903863(外包)`
+* 微信群:如果二维码过期,请尝试点击图片并F5刷新,或者添加客服微信 jeesitex 邀请您进群
+
+
+
+
+
+* 后端源码仓库地址:
+ [Gitee](https://gitee.com/thinkgem/jeesite5)、
+ [GitCode](https://gitcode.com/thinkgem/jeesite5)、
+ [GitHub](https://github.com/thinkgem/jeesite5)
+* 前端源码仓库地址:
+ [Gitee](https://gitee.com/thinkgem/jeesite-vue)、
+ [GitCode](https://gitcode.com/thinkgem/jeesite-vue)、
+ [GitHub](https://github.com/thinkgem/jeesite-vue)
+* 源码合集仓库地址:
+ [GVP](https://gitee.com/thinkgem/jeesite/tree/main)、
+ [G-Star](https://gitcode.com/thinkgem/jeesite/tree/main)、
+ [GitHub](https://github.com/thinkgem/jeesite/tree/main)
+
+## 平台介绍
+
+* JeeSite 快速开发平台,低代码,轻量级,不仅仅是一个后台开发框架,它是一个企业级快速开发解决方案,后端基于经典组合 Spring Boot、Shiro、MyBatis,前端采用分离版 Vue3、Vite、Monorepo、Ant Design Vue、TypeScript、Vben Admin 最先进技术栈,或者 Beetl、Bootstrap、AdminLTE 经典开发模式。
+
+* 提供在线数据源管理、数据表建模、代码生成等功能,可自动创建业务模块代码工程和微服务模块代码工程,自动生成前端代码和后端代码;包括核心功能模块如:组织机构、用户、角色、岗位、管理员、权限审计、菜单及按钮权限、数据权限、模块管理、系统参数、字典管理、系统监控、数据监控等;扩展功能如:工作流引擎、内容管理、消息推送、单点登录、第三方登录、在线作业调度、对象存储、可视化数据大屏、报表设计器、在线文件预览、国际化、全文检索、统一认证服务等。
+
+* 本平台采用松耦合设计,真正的轻量级,微内核,快速部署,插件架构,模块增减便捷,支持扩展 SaaS 架构、集群部署、读写分离、分库分表、Spring Cloud 微服务架构;并内置了众多账号安全设置、密码策略、系统访问限制等安全解决方案,支持等保评测。
+
+* 本平台专注于为初级研发人员提供强大的支持,使他们能够高效、快速地开发出复杂的业务功能,同时为中高级人员腾出宝贵的时间,专注于更具战略性和创新性的任务。我们致力于让开发者能够全心投入业务逻辑中,而将繁琐的技术细节交由平台来封装处理。这不仅降低了技术实现的难度,还确保了系统架构的稳定性和安全性,进而帮助企业节省人力成本、缩短项目周期,并提高整体软件的安全性和质量。
+
+* 2013 年发布以来已被广大爱好者用到了企业、政府、医疗、金融、互联网等各个领域中,拥有:精良架构、易于扩展、大众思维的设计模式,工匠精神,用心打磨每一个细节,深入开发者的内心,并荣获开源中国《最受欢迎中国开源软件》多次奖项,期间也帮助了不少刚毕业的大学生,教师作为入门教材,快速的去实践。
+
+* 2019 年换代升级,我们结合了多年总结和经验,以及各方面的应用案例,对架构完成了一次全部重构,也纳入很多新的思想。不管是从开发者模式、底层架构、逻辑处理还是到用户界面,用户交互体验上都有很大的进步,在不忘学习成本、提高开发效率的情况下,安全方面也做和很多工作,包括:身份认证、密码策略、安全审计、日志收集等众多安全选项供您选择。努力为大中小微企业打造全方位企业级快速开发解决方案。
+
+* 2021 年终发布 Vue3 的前后分离版本,使得 JeeSite 拥有同一个后台服务 Web 来支撑分离版和全栈版两套前端技术栈。
+
+* 对接 OpenAPI、Ollama、DeepSeek 等热门 AI 大模型,凭借检索增强生成 RAG 技术,为企业知识库打造专属智能对话。
+
+* 提供大模型 Tool 本地工具调用及 MCP 服务端和客户端工具调用,助力大模型与您的业务深度融合,实现高效交互。
+
+* 支持国产化软件和硬件环境,如国产芯片、操作系统、数据库、中间件、国密算法等。
+
+## 核心优势
+
+* JeeSite 非常易于二次开发,可控性高,整体架构清晰、技术稳定而先进、源代码书写规范、经典技术会的人多、易于维护、易于扩展、安全稳定。
+
+* JeeSite 功能全,知识点非常多,也非常少。因为她使用的都是一些通用的技术,通俗的设计风格,大多数基础知识点,多数人都能掌握,所以每一个 JeeSite 的功能点都非常容易掌握。只要您学会使用这些功能和组件的应用,就可以顺利地完成系统开发了。
+
+* JeeSite 是一个低代码开发平台,具有较高的封装度、扩展性,封装不是限制您去做一些事情,而是在便捷的同时,也具有较好的扩展性,在不具备一些功能的情况下,JeeSite 提供了扩展接口,提供了原生调用方法。
+
+* 大家都在用 Spring,也在学习 Spring 的优点,Spring 提供了较好的扩展性,可又有多少人去修改它的源代码呢,退一步说,大家去修改了 Spring 的源码,反而会对未来升级造成很大困扰,您说不是呢?这样的例子很多,所以不要纠结,我们非常注重这一点,JeeSite 也一样具备强大的扩展性。为你解决升级的困扰。
+
+* 为什么说 JeeSite 比较易于学习?JeeSite 很好的把握了设计的 “度”,避免过度设计的情况。过度设计是在产品设计过程中忽略了产品和用户的实际需求,反而带来了不必要的复杂性,而忽略了系统的学习、开发和维护成本。
+
+------
+
+* 至今 JeeSite 平台架构已经非常稳定,我们持续升级,并不失架构的先进性。
+* JeeSite 精益求精,用心打磨每一个细节,界面 UI 操作便捷,体验性好。
+* JeeSite 是一个专业的平台,是一个可以让您,用着省心的平台。
+* 社区版基于 Apache License 2.0 开源协议,永久免费使用。
+
+### 架构特点及安全方面的优势:
+
+## Vue 前端简介
+
+基于 Vue3、Vite、Ant-Design-Vue、TypeScript 和 Vue Vben Admin 等前沿技术栈构建,本软件采用最先进的技术架构,
+帮助初学者快速上手并融入团队开发。内置组织机构、角色用户、菜单授权、数据权限、系统参数等核心模块,结合强大的组件封装
+与数据驱动视图设计,为微小、中大型项目提供开箱即用的解决方案和丰富的示例,助力高效开发。
+
+用户界面专为信息化管理后台量身打造,在界面设计上精益求精,每一处细节都彰显着精致,为用户带来优雅且直观的操作体验。
+它提供多样化的菜单布局、智能的页签管理、高效的树表操作体验、强大的表格组件、灵活的表单组件设计,具备强大的扩展能力,
+同时支持黑暗布局风格,为用户提供高效、灵活且美观的操作体验,满足各类管理后台的复杂需求。
+
+另外我们还支持 Turborepo + Monorepo 快速构建、模块化、代码复用、支持分包开发
+详见:
+
+## 前端技术特点
+
+定义众多组件,非常贴心的组件属性及小功能,符合 JeeSite 以往的设计思想,列表和表单以数据驱动视图,
+极大简化了业务功能开发, 注释分解详见【[源码解析](https://jeesite.com/docs/vue-crud-view)】
+
+为什么做数据驱动视图?前端向下兼容一直是最大的问题,有了一套相应的标准,会对框架升级帮助很大。
+比如你可以非常小的成本,业务代码改动非常小的情况下,去升级前端;数据驱动视图可以为未来自定义拖拽表单做更好的铺垫,
+数据存储结构更清晰化,更利于维护。
+
+提示:请仔细阅读源码解析,表单视图和列表视图上的注释哦,支持复杂表单以及多表单联合使用。
+
+## 演示地址
+
+1. 地址:
+
+## 学习准备
+
+- [VSCode](https://code.visualstudio.com/) - 推荐 IDE 集成开发工具
+- [Node.js 20](https://nodejs.org/dist/latest-v20.x/) 和 [git](https://git-scm.com/) - 开发环境
+- [Vite](https://vitejs.dev/) - 熟悉 Vite 特性
+- [Vue-v3](https://cn.vuejs.org/) - 熟悉 Vue 基础语法
+- [TypeScript](https://www.typescriptlang.org/) - 熟悉 TS 基本语法
+- [ES6+](http://es6.ruanyifeng.com/) - 熟悉 ES6 基本语法
+- [Vue-Router-v4](https://next.router.vuejs.org/) - 熟悉 vue-router 基本使用
+- [Vue-Vben-Admin](https://jeesite.com/front/vben-admin/) - 熟悉 UI 及表单列表及常用组件使用
+- [Ant-Design-Vue](https://antdv.com/components/overview-cn/) - 熟悉 UI 基本使用
+
+## 安装使用
+
+- 如果没有安装 Node.js 20+,下载地址:
+
+```bash
+# 验证
+node -v
+# 配置国内源
+npm config set registry https://registry.npmmirror.com
+```
+
+- 如果没有安装 Pnpm 执行安装
+
+```bash
+npm i -g pnpm
+# 验证
+pnpm -v
+# 配置国内源
+pnpm config set registry https://registry.npmmirror.com
+```
+
+- 获取源代码
+
+```bash
+git clone https://gitee.com/thinkgem/jeesite-vue.git
+cd jeesite-vue
+```
+注意:不要放到中文或带空格的目录下。
+
+- 安装依赖
+
+```bash
+pnpm install
+```
+
+- 开发环境运行访问(方式一)
+
+```bash
+pnpm dev
+```
+开发环境会加载文件较多,便于调试,请耐心等待。
+
+- 编译打包后运行访问(方式二)
+
+```bash
+pnpm preview
+```
+编译打包后,会整合这些文件,所以访问性能会大大提高,生产环境可以开启 gzip
+
+- 打包发布程序
+
+```bash
+pnpm build
+```
+打包完成后,会在根目录生成 dist 文件夹,发布 nginx。
+
+详见文档:
+
+### 后端服务
+
+- 安装后台服务 [JeeSite v5.x](https://gitee.com/thinkgem/jeesite5/tree/v5.springboot3/)
+- 打开 [.env.development](https://jeesite.com/docs/vue-settings/#env-development-详解) 文件,修改后台接口:
+
+```bash
+# 代理设置,可配置多个,不能换行,格式:[访问接口的根路径, 代理地址, 是否保持Host头]
+# VITE_PROXY = [["/js","https://vue.jeesite.com/js",true]]
+VITE_PROXY = [["/js","http://127.0.0.1:8980/js",false]]
+
+# 访问接口的根路径(例如:https://vue.jeesite.com)
+VITE_GLOB_API_URL =
+
+# 访问接口的前缀,在根路径之后
+VITE_GLOB_API_URL_PREFIX = /js
+```
+
+### 如果您使用的 VSCode 的话,推荐安装以下插件:
+
+* [UnoCSS](https://marketplace.visualstudio.com/items?itemName=antfu.unocss) - UnoCSS 提示插件
+* [Iconify](https://marketplace.visualstudio.com/items?itemName=antfu.iconify) - Iconify 图标插件
+* [I18n-ally](https://marketplace.visualstudio.com/items?itemName=Lokalise.i18n-ally) - i18n 插件
+* [Volar](https://marketplace.visualstudio.com/items?itemName=johnsoncodehk.volar) - Vue3 开发必备(Vetur禁用)
+* [ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) - 脚本代码检查
+* [Prettier](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) - 代码格式化
+* [Stylelint](https://marketplace.visualstudio.com/items?itemName=stylelint.vscode-stylelint) - CSS 格式化
+* [DotENV](https://marketplace.visualstudio.com/items?itemName=mikestead.dotenv) - .env 文件高亮
+
+## 常见问题
+
+* Vue 版本的浏览器支持情况:支持所有现代浏览器,Vue3 已不再支持 IE 浏览器。
+* 为什么使用抽屉作为表单组件,因为抽屉空间更大,可以展示更多内容,且操作更友好。
+* 如何将表单抽屉改为弹窗,替换 list 和 form 页面的 Drawer 为 Modal 即可,V5.6增加了路由表单和弹窗表单的代码生成。
+* 打不开代码生成工具怎么办?提示 404,请检查 .env.development 中的代理配置 VITE_PROXY 最后一个参数(是否保持Host头),本地服务 127.0.0.1 应设置为 false,远程服务设置为 true。
+* 更多文档详见:
+
+## 软件截图
+
+
+
+
+
+
+
+
+
+## 附录
+
+### 表单视图
+
+```html
+
+
+
+
+
+ -- 图标
+ {{ getTitle.value }} -- 标题名称
+
+
+
+
+
+
+
+
+ {{ t('新增') }}
+
+
+
+
+
+
+
+```
+
+### 列表视图
+
+```html
+
+
+
+
+
+```
+
+## 更多介绍
+
+* 架构特点:
+* 内置功能:
+* 目录结构:
+* 参数配置:
+* 开发规范:
+* 数表设计:
+
+## 学习文档
+
+* 库表生成、代码生成:
+* 菜单权限、按钮权限:
+* 数据权限、库事务:
+* 表结构、数据字典:
+* 持久层框架、SQL:
+* 后端常用工具:
+
+**分离版**
+
+* 版本介绍:
+* 源码解析:
+* 表单组件:
+* 表格组件:
+* 参数配置:
+* 常用组件:
+* 前端权限:
+* 图标组件:
+* 前端样式库:
+* 多语言国际化:
+
+**经典版**
+
+* 表单组件:
+* 表格组件:
+* 常用工具:
+* 自定义主题:
+
+## 更多文档
+
+* AI、CMS、RAG、Tool、MCP 人工智能助手:
+* BPM 业务流程引擎(Flowable):
+* CMS 多站点内容管理模块:
+
+* 消息推送消息提醒:
+* 对象存储模块:
+* 单点登录模块:
+* 在线任务调度:
+
+* 大屏设计器:
+* 报表设计器:
+* 文件管理分享:
+* 文件在线预览:
+
+* 集群、高可用架构:
+* SaaS 多租户架构:
+* 读写分离分片分表:
+* Spring监控系统:
+* 分布式跨应用事务:
+* 追踪系统集成:
+* ELK 日志收集:
+
+* MybatisPlus:
+* 接口快速开发:
+* 内外网中间件:
+* 统一认证平台:
+
+## 授权许可协议条款
+
+1. 基于 Apache License Version 2.0 协议发布,可用于商业项目,但必须遵守以下补充条款。
+2. 不得将本软件应用于危害国家安全、荣誉和利益的行为,不能以任何形式用于非法为目的的行为。
+3. 在使用本软件时,由于它集成了众多第三方开源软件,请共同遵守这些开源软件的使用许可条款规定。
+4. 在延伸的代码中(修改和有源代码衍生的代码中)需要带有原来代码中的协议、版权声明和其他原作者
+ 规定需要包含的说明(请尊重原作者的著作权,不要删除或修改文件中的`Copyright`和`@author`信息)
+ 更不要,全局替换源代码中的 jeesite 或 ThinkGem 等字样,否则你将违反本协议条款承担责任。
+5. 基于本软件的作品,只能使用 JeeSite5 作为后台服务,除外情况不可商用且不允许二次分发或开源。
+6. 您若套用本软件的一些代码或功能参考,请保留源文件中的版权和作者,需要在您的软件介绍明显位置
+ 说明出处,举例:本软件基于 JeeSite Vue 快速开发平台,并附带链接:http://jeesite.com
+7. 任何基于本软件而产生的一切法律纠纷和责任,均于我司无关。
+8. 如果你对本软件有改进,希望可以贡献给我们,共同进步。
+9. 本项目已申请软件著作权,请尊重开源,感谢阅读。
+
+## 技术支持与服务
+
+* 本软件免费,我们也提供了相应的收费服务,因为:
+* 没有资金的支撑就很难得到发展,特别是一个好的产品,如果 JeeSite 帮助了您,请为我们点赞。支持我们,您可以获得更多回馈,我们会把公益事业做的更好,开放更多资源,回报社区和社会。请给我们一些动力吧,在此非常感谢已支持我们的朋友!
+* **联系我们**:请访问技术支持与服务页面:
+
+## 专业版增加的功能
+
+1. 主题标签页的三种风格自由切换
+2. 业务流程、流程设计、流程办理
+3. 文件管理、上传秒传、文件预览
+4. 高级折叠表单和个性化本地存储
+5. 表格个性化设置参数本地存储
+6. 租户管理功能、租户切换
+7. 动态设置页面字体大小
+8. 页签右键,在新窗口打开
+9. 消息推送、消息提醒
+10. 语言国际化、本地化
+11. 快速升级到 Monorepo 脚本
+12. 更多功能详见文档
diff --git a/web-vue/bin/build.bat b/web-vue/bin/build.bat
new file mode 100644
index 00000000..d108a701
--- /dev/null
+++ b/web-vue/bin/build.bat
@@ -0,0 +1,9 @@
+@echo off
+%~d0
+cd %~dp0
+
+cd..
+npm run build
+
+cd bin
+pause
\ No newline at end of file
diff --git a/web-vue/bin/build.sh b/web-vue/bin/build.sh
new file mode 100644
index 00000000..961d9d0f
--- /dev/null
+++ b/web-vue/bin/build.sh
@@ -0,0 +1,5 @@
+#!/bin/sh
+cd ..
+pnpm build
+
+cd bin
diff --git a/web-vue/bin/install.bat b/web-vue/bin/install.bat
new file mode 100644
index 00000000..a3616032
--- /dev/null
+++ b/web-vue/bin/install.bat
@@ -0,0 +1,9 @@
+@echo off
+%~d0
+cd %~dp0
+
+cd..
+npm run install
+
+cd bin
+pause
\ No newline at end of file
diff --git a/web-vue/bin/install.sh b/web-vue/bin/install.sh
new file mode 100644
index 00000000..63fdbe92
--- /dev/null
+++ b/web-vue/bin/install.sh
@@ -0,0 +1,5 @@
+#!/bin/sh
+cd..
+pnpm install
+
+cd bin
diff --git a/web-vue/bin/startup.bat b/web-vue/bin/startup.bat
new file mode 100644
index 00000000..4a9ffb73
--- /dev/null
+++ b/web-vue/bin/startup.bat
@@ -0,0 +1,9 @@
+@echo off
+%~d0
+cd %~dp0
+
+cd..
+pnpm dev
+
+cd bin
+pause
\ No newline at end of file
diff --git a/web-vue/bin/startup.sh b/web-vue/bin/startup.sh
new file mode 100644
index 00000000..a7fb752d
--- /dev/null
+++ b/web-vue/bin/startup.sh
@@ -0,0 +1,5 @@
+#!/bin/sh
+cd ..
+pnpm dev
+
+cd bin
diff --git a/web-vue/eslint.config.mjs b/web-vue/eslint.config.mjs
new file mode 100644
index 00000000..8423819f
--- /dev/null
+++ b/web-vue/eslint.config.mjs
@@ -0,0 +1,177 @@
+/**
+ * Copyright (c) 2013-Now http://jeesite.com All rights reserved.
+ * No deletion without permission, or be held responsible to law.
+ * @author ThinkGem
+ */
+import globals from 'globals';
+import vuePlugin from 'eslint-plugin-vue';
+import tsPlugin from '@typescript-eslint/eslint-plugin';
+import prettierPlugin from 'eslint-plugin-prettier';
+import tsParser from '@typescript-eslint/parser';
+import vueParser from 'vue-eslint-parser';
+import babelParser from '@babel/eslint-parser';
+
+export default [
+ // 基础忽略配置
+ {
+ ignores: [
+ '**/node_modules/',
+ '**/dist/',
+ '**/public/',
+ '**/build/',
+ '**/.git/',
+ '**/.vscode/',
+ '**/.idea/',
+ '**/.husky/',
+ '**/.local/',
+ '**/.turbo/',
+ '**/Dockerfile',
+ '**/*.sh',
+ '**/*.md',
+ '**/*.woff',
+ '**/*.ttf',
+ '**/*.d.ts',
+ '**/__snapshots__/',
+ ],
+ },
+
+ // 公共基础配置
+ {
+ languageOptions: {
+ ecmaVersion: 'latest',
+ sourceType: 'module',
+ globals: {
+ ...globals.browser,
+ ...globals.node,
+ defineOptions: 'readonly',
+ },
+ parserOptions: {
+ requireConfigFile: false,
+ },
+ },
+ rules: {
+ // 通用规则
+ 'no-case-declarations': 'off',
+ 'no-extra-boolean-cast': 'off',
+ 'no-undef': 'off',
+ 'space-before-function-paren': 'off',
+ },
+ },
+
+ // Vue 文件配置
+ {
+ files: ['**/*.vue'],
+ plugins: {
+ vue: vuePlugin,
+ },
+ languageOptions: {
+ parser: vueParser,
+ parserOptions: {
+ parser: {
+ ts: tsParser,
+ tsx: tsParser,
+ js: babelParser,
+ '': 'espree',
+ },
+ ecmaFeatures: {
+ jsx: true,
+ },
+ },
+ },
+ rules: {
+ // ...vuePlugin.configs['vue3-recommended'].rules,
+ // 'vue/script-setup-uses-vars': 'error',
+ 'vue/no-reserved-component-names': 'off',
+ 'vue/custom-event-name-casing': 'off',
+ 'vue/attributes-order': 'off',
+ 'vue/one-component-per-file': 'off',
+ 'vue/html-closing-bracket-newline': 'off',
+ 'vue/max-attributes-per-line': 'off',
+ 'vue/multiline-html-element-content-newline': 'off',
+ 'vue/singleline-html-element-content-newline': 'off',
+ 'vue/attribute-hyphenation': 'off',
+ 'vue/require-default-prop': 'off',
+ 'vue/require-explicit-emits': 'off',
+ 'vue/html-self-closing': [
+ 'error',
+ {
+ html: {
+ void: 'always',
+ normal: 'never',
+ component: 'always',
+ },
+ svg: 'always',
+ math: 'always',
+ },
+ ],
+ 'vue/multi-word-component-names': 'off',
+ 'vue/no-v-html': 'off',
+ },
+ },
+
+ // TypeScript 配置
+ {
+ files: ['**/*.ts', '**/*.tsx'],
+ plugins: {
+ '@typescript-eslint': tsPlugin,
+ },
+ languageOptions: {
+ parser: tsParser,
+ parserOptions: {
+ // project: './tsconfig.json',
+ // jsxPragma: 'React',
+ ecmaFeatures: {
+ jsx: true, // 启用 JSX 支持
+ },
+ },
+ },
+ rules: {
+ ...tsPlugin.configs.recommended.rules,
+ '@typescript-eslint/ban-ts-comment': 'off',
+ '@typescript-eslint/ban-types': 'off',
+ '@typescript-eslint/explicit-module-boundary-types': 'off',
+ '@typescript-eslint/no-empty-function': 'off',
+ '@typescript-eslint/no-explicit-any': 'off',
+ '@typescript-eslint/no-non-null-assertion': 'off',
+ '@typescript-eslint/no-unused-expressions': 'off',
+ '@typescript-eslint/no-unused-vars': 'off',
+ '@typescript-eslint/no-use-before-define': 'off',
+ '@typescript-eslint/no-var-requires': 'off',
+ '@typescript-eslint/no-unsafe-function-type': 'off',
+ },
+ },
+
+ // JavaScript 配置
+ {
+ files: ['**/*.js'],
+ languageOptions: {
+ parser: babelParser,
+ parserOptions: {
+ requireConfigFile: false,
+ babelOptions: {
+ presets: ['@babel/preset-env'],
+ plugins: [['@babel/plugin-proposal-decorators', { legacy: true }]],
+ },
+ },
+ },
+ },
+
+ // Prettier 集成配置
+ {
+ plugins: {
+ prettier: prettierPlugin,
+ },
+ rules: {
+ 'prettier/prettier': [
+ 'error',
+ {
+ printWidth: 120,
+ semi: true,
+ singleQuote: true,
+ trailingComma: 'all',
+ },
+ ],
+ 'property-sort-order': 'off',
+ },
+ },
+];
diff --git a/web-vue/package.json b/web-vue/package.json
new file mode 100644
index 00000000..dcf11db3
--- /dev/null
+++ b/web-vue/package.json
@@ -0,0 +1,119 @@
+{
+ "name": "@jeesite/root",
+ "version": "5.14.0",
+ "type": "module",
+ "private": true,
+ "scripts": {
+ "bootstrap": "pnpm install",
+ "dev": "cd web && pnpm run dev",
+ "build": "turbo build --concurrency 20",
+ "build:tomcat": "cd web && pnpm build:tomcat",
+ "build:preview": "pnpm run preview",
+ "report": "cd web && pnpm report",
+ "preview": "cd web && pnpm preview",
+ "preview:dist": "cd web && pnpm preview:dist",
+ "type:check": "turbo type:check --concurrency 20",
+ "lint:eslint": "eslint --cache --max-warnings 0 \"./**/*.{ts,tsx,mjs,vue}\" --fix",
+ "lint:prettier": "prettier --ignore-unknown --check --cache --write \"./**/*.{vue,tsx,less,scss}\"",
+ "lint:stylelint": "stylelint \"./**/*.{vue,less,scss,css}\" --fix --custom-syntax postcss-html --cache --cache-location node_modules/.cache/stylelint/",
+ "lint:all": "pnpm bootstrap && pnpm type:check && pnpm lint:eslint && pnpm lint:prettier && pnpm lint:stylelint",
+ "reinstall:force": "rimraf pnpm-lock.yaml node_modules && pnpm store prune && pnpm i --force && pnpm run dev --force",
+ "reinstall": "turbo uninstall --concurrency 20 && rimraf pnpm-lock.yaml node_modules && pnpm bootstrap",
+ "update": "turbo update --concurrency 20 && ncu -u && pnpm reinstall",
+ "postinstall": "pnpm -r run stub --if-present",
+ "preinstall": "npx only-allow pnpm",
+ "serve": "pnpm dev"
+ },
+ "dependencies": {
+ "@ant-design/colors": "8.0.0",
+ "@ant-design/icons-vue": "7.0.1",
+ "@jeesite/assets": "workspace:*",
+ "@jeesite/cms": "workspace:*",
+ "@jeesite/core": "workspace:*",
+ "@jeesite/dbm": "workspace:*",
+ "@jeesite/dfm": "workspace:*",
+ "@jeesite/test": "workspace:*",
+ "@jeesite/types": "workspace:*",
+ "@jeesite/vite": "workspace:*",
+ "@jeesite/web": "workspace:*",
+ "ant-design-vue": "4.2.6",
+ "axios": "1.12.2",
+ "dayjs": "1.11.18",
+ "lodash-es": "4.17.21",
+ "vue": "3.5.22",
+ "vue-eslint-parser": "10.2.0",
+ "vue-router": "4.5.1",
+ "vue-tsc": "3.1.1",
+ "vue-types": "6.0.0"
+ },
+ "devDependencies": {
+ "@babel/eslint-parser": "7.28.4",
+ "@iconify/json": "2.2.394",
+ "@iconify/utils": "3.0.2",
+ "@stylistic/stylelint-plugin": "4.0.0",
+ "@types/node": "24.7.2",
+ "@typescript-eslint/eslint-plugin": "8.46.0",
+ "@typescript-eslint/parser": "8.46.0",
+ "@unocss/eslint-config": "66.5.3",
+ "@unocss/preset-wind3": "66.5.3",
+ "@vue/compiler-sfc": "3.5.22",
+ "@vue/runtime-core": "3.5.22",
+ "@vue/shared": "3.5.22",
+ "autoprefixer": "10.4.21",
+ "cross-env": "10.1.0",
+ "eslint": "9.37.0",
+ "eslint-config-prettier": "10.1.8",
+ "eslint-plugin-prettier": "5.5.4",
+ "eslint-plugin-vue": "10.5.0",
+ "globals": "16.4.0",
+ "less": "4.4.2",
+ "monaco-editor": "0.54.0",
+ "npm-check-updates": "19.0.0",
+ "pkg-types": "2.3.0",
+ "postcss": "8.5.6",
+ "postcss-html": "1.8.0",
+ "postcss-less": "6.0.0",
+ "prettier": "3.6.2",
+ "prettier-plugin-packagejson": "2.5.19",
+ "rimraf": "6.0.1",
+ "stylelint": "16.25.0",
+ "stylelint-config-recommended": "17.0.0",
+ "stylelint-config-recommended-less": "3.0.1",
+ "stylelint-config-recommended-vue": "1.6.1",
+ "stylelint-config-standard": "39.0.1",
+ "stylelint-config-standard-less": "3.0.1",
+ "stylelint-less": "3.0.1",
+ "stylelint-prettier": "5.0.3",
+ "turbo": "2.5.8",
+ "typescript": "5.9.3",
+ "unocss": "66.5.3",
+ "vite": "7.1.9"
+ },
+ "keywords": [
+ "typescript",
+ "jeesite",
+ "antdv",
+ "vite",
+ "vue"
+ ],
+ "resolutions": {
+ "bin-wrapper": "npm:bin-wrapper-china"
+ },
+ "homepage": "https://jeesite.com",
+ "repository": {
+ "type": "git",
+ "url": "https://gitee.com/thinkgem/jeesite-vue.git"
+ },
+ "bugs": {
+ "url": "https://gitee.com/thinkgem/jeesite-vue/issues"
+ },
+ "author": {
+ "name": "ThinkGem",
+ "email": "thinkgem@163.com",
+ "url": "https://gitee.com/thinkgem"
+ },
+ "engines": {
+ "node": ">=20.19 || >=22.12"
+ },
+ "packageManager": "pnpm@10.18.2"
+}
diff --git a/web-vue/packages/assets/README.md b/web-vue/packages/assets/README.md
new file mode 100644
index 00000000..dd372de4
--- /dev/null
+++ b/web-vue/packages/assets/README.md
@@ -0,0 +1,23 @@
+
+- 官方网站:
+- 使用文档:
+- 后端代码:
+- 前端代码:
+
+------
+
+
+ 如果你喜欢 JeeSite,请给她一个 ⭐️ Star,您的支持将是我们前行的动力。
+
+
+------
+
+- 问题反馈: [【新手必读】](https://gitee.com/thinkgem/jeesite5/issues/I18ARR)
+- 需求收集:
+- QQ 群:`127515876`、`209330483`、`223507718`、`709534275`、`730390092`、`1373527`、`183903863(外包)`
+- 微信群:添加客服微信 邀请您进群
+- 关注微信公众号,了解最新动态:
+
+
+
+
diff --git a/web-vue/packages/assets/icons/moon.svg b/web-vue/packages/assets/icons/moon.svg
new file mode 100644
index 00000000..e6667f0d
--- /dev/null
+++ b/web-vue/packages/assets/icons/moon.svg
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
diff --git a/web-vue/packages/assets/icons/sun.svg b/web-vue/packages/assets/icons/sun.svg
new file mode 100644
index 00000000..a3997cbf
--- /dev/null
+++ b/web-vue/packages/assets/icons/sun.svg
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/web-vue/packages/assets/images/header.jpg b/web-vue/packages/assets/images/header.jpg
new file mode 100644
index 00000000..04863b19
Binary files /dev/null and b/web-vue/packages/assets/images/header.jpg differ
diff --git a/web-vue/packages/assets/images/logo.png b/web-vue/packages/assets/images/logo.png
new file mode 100644
index 00000000..3477d02a
Binary files /dev/null and b/web-vue/packages/assets/images/logo.png differ
diff --git a/web-vue/packages/assets/images/mp.png b/web-vue/packages/assets/images/mp.png
new file mode 100644
index 00000000..635c0a74
Binary files /dev/null and b/web-vue/packages/assets/images/mp.png differ
diff --git a/web-vue/packages/assets/package.json b/web-vue/packages/assets/package.json
new file mode 100644
index 00000000..0fbc855e
--- /dev/null
+++ b/web-vue/packages/assets/package.json
@@ -0,0 +1,23 @@
+{
+ "name": "@jeesite/assets",
+ "version": "5.14.0",
+ "private": true,
+ "type": "module",
+ "scripts": {
+ "uninstall": "rimraf node_modules",
+ "update": "ncu -u"
+ },
+ "homepage": "https://jeesite.com",
+ "repository": {
+ "type": "git",
+ "url": "https://gitee.com/thinkgem/jeesite-vue.git"
+ },
+ "bugs": {
+ "url": "https://gitee.com/thinkgem/jeesite-vue/issues"
+ },
+ "author": {
+ "name": "ThinkGem",
+ "email": "thinkgem@163.com",
+ "url": "https://gitee.com/thinkgem"
+ }
+}
diff --git a/web-vue/packages/assets/svg/illustration.svg b/web-vue/packages/assets/svg/illustration.svg
new file mode 100644
index 00000000..b45215b0
--- /dev/null
+++ b/web-vue/packages/assets/svg/illustration.svg
@@ -0,0 +1 @@
+Asset 336
\ No newline at end of file
diff --git a/web-vue/packages/assets/svg/login-bg-dark.svg b/web-vue/packages/assets/svg/login-bg-dark.svg
new file mode 100644
index 00000000..888da7af
--- /dev/null
+++ b/web-vue/packages/assets/svg/login-bg-dark.svg
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/web-vue/packages/assets/svg/login-bg.svg b/web-vue/packages/assets/svg/login-bg.svg
new file mode 100644
index 00000000..c7c25848
--- /dev/null
+++ b/web-vue/packages/assets/svg/login-bg.svg
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/web-vue/packages/assets/svg/login-box-bg.svg b/web-vue/packages/assets/svg/login-box-bg.svg
new file mode 100644
index 00000000..61fa1e98
--- /dev/null
+++ b/web-vue/packages/assets/svg/login-box-bg.svg
@@ -0,0 +1,98 @@
+
+
+
+
+
+
+
+
+
+
+
+ responsive
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/web-vue/packages/assets/svg/net-error.svg b/web-vue/packages/assets/svg/net-error.svg
new file mode 100644
index 00000000..81f20044
--- /dev/null
+++ b/web-vue/packages/assets/svg/net-error.svg
@@ -0,0 +1 @@
+personal settings
\ No newline at end of file
diff --git a/web-vue/packages/assets/svg/no-data.svg b/web-vue/packages/assets/svg/no-data.svg
new file mode 100644
index 00000000..2b9f2570
--- /dev/null
+++ b/web-vue/packages/assets/svg/no-data.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web-vue/packages/assets/svg/preview/p-rotate.svg b/web-vue/packages/assets/svg/preview/p-rotate.svg
new file mode 100644
index 00000000..5153a816
--- /dev/null
+++ b/web-vue/packages/assets/svg/preview/p-rotate.svg
@@ -0,0 +1 @@
+
diff --git a/web-vue/packages/assets/svg/preview/resume.svg b/web-vue/packages/assets/svg/preview/resume.svg
new file mode 100644
index 00000000..0e86c5f6
--- /dev/null
+++ b/web-vue/packages/assets/svg/preview/resume.svg
@@ -0,0 +1 @@
+
diff --git a/web-vue/packages/assets/svg/preview/scale.svg b/web-vue/packages/assets/svg/preview/scale.svg
new file mode 100644
index 00000000..1f7adaee
--- /dev/null
+++ b/web-vue/packages/assets/svg/preview/scale.svg
@@ -0,0 +1 @@
+
diff --git a/web-vue/packages/assets/svg/preview/unrotate.svg b/web-vue/packages/assets/svg/preview/unrotate.svg
new file mode 100644
index 00000000..e4708be1
--- /dev/null
+++ b/web-vue/packages/assets/svg/preview/unrotate.svg
@@ -0,0 +1 @@
+
diff --git a/web-vue/packages/assets/svg/preview/unscale.svg b/web-vue/packages/assets/svg/preview/unscale.svg
new file mode 100644
index 00000000..1359b34c
--- /dev/null
+++ b/web-vue/packages/assets/svg/preview/unscale.svg
@@ -0,0 +1 @@
+
diff --git a/web-vue/packages/assets/tsconfig.json b/web-vue/packages/assets/tsconfig.json
new file mode 100644
index 00000000..e67c0383
--- /dev/null
+++ b/web-vue/packages/assets/tsconfig.json
@@ -0,0 +1,19 @@
+{
+ "extends": "../../tsconfig.json",
+ "compilerOptions": {
+ "baseUrl": ".",
+ "paths": {
+ "@jeesite/assets/*": ["./*"]
+ }
+ },
+ "include": [
+ "./**/*.ts",
+ "./**/*.tsx",
+ "./**/*.vue"
+ ],
+ "exclude": [
+ "node_modules",
+ "vite.config.ts",
+ "dist"
+ ]
+}
diff --git a/web-vue/packages/cms/README.md b/web-vue/packages/cms/README.md
new file mode 100644
index 00000000..dd372de4
--- /dev/null
+++ b/web-vue/packages/cms/README.md
@@ -0,0 +1,23 @@
+
+- 官方网站:
+- 使用文档:
+- 后端代码:
+- 前端代码:
+
+------
+
+
+ 如果你喜欢 JeeSite,请给她一个 ⭐️ Star,您的支持将是我们前行的动力。
+
+
+------
+
+- 问题反馈: [【新手必读】](https://gitee.com/thinkgem/jeesite5/issues/I18ARR)
+- 需求收集:
+- QQ 群:`127515876`、`209330483`、`223507718`、`709534275`、`730390092`、`1373527`、`183903863(外包)`
+- 微信群:添加客服微信 邀请您进群
+- 关注微信公众号,了解最新动态:
+
+
+
+
diff --git a/web-vue/packages/cms/api/cms/chat.ts b/web-vue/packages/cms/api/cms/chat.ts
new file mode 100644
index 00000000..72a9f508
--- /dev/null
+++ b/web-vue/packages/cms/api/cms/chat.ts
@@ -0,0 +1,38 @@
+/**
+ * Copyright (c) 2013-Now http://jeesite.com All rights reserved.
+ * No deletion without permission, or be held responsible to law.
+ * @author ThinkGem
+ */
+import { defHttp } from '@jeesite/core/utils/http/axios';
+import { useGlobSetting } from '@jeesite/core/hooks/setting';
+import { AxiosProgressEvent, GenericAbortSignal } from 'axios';
+
+const { adminPath } = useGlobSetting();
+
+export const cmsChatMessage = (params?: Recordable | any) =>
+ defHttp.get({ url: adminPath + '/cms/chat/message', params });
+
+export const cmsChatList = (params?: Recordable | any) =>
+ defHttp.get({ url: adminPath + '/cms/chat/list', params });
+
+export const cmsChatSave = (params?: Recordable | any) =>
+ defHttp.post({ url: adminPath + '/cms/chat/save', params });
+
+export const cmsChatDelete = (params?: Recordable | any) =>
+ defHttp.get({ url: adminPath + '/cms/chat/delete', params });
+
+export const cmsChatStream = (
+ params?: Recordable | any,
+ signal?: GenericAbortSignal,
+ onDownloadProgress?: (progressEvent: AxiosProgressEvent) => void,
+) =>
+ defHttp.post({
+ url: adminPath + '/cms/chat/stream',
+ params,
+ signal,
+ onDownloadProgress,
+ responseType: 'stream',
+ headers: {
+ 'x-ajax': 'event-stream',
+ },
+ });
diff --git a/web-vue/packages/cms/index.ts b/web-vue/packages/cms/index.ts
new file mode 100644
index 00000000..dbe5605d
--- /dev/null
+++ b/web-vue/packages/cms/index.ts
@@ -0,0 +1,2 @@
+import './node_modules/@jeesite/cms-lib/dist/style.css';
+export { ChatMessage } from './node_modules/@jeesite/cms-lib/dist';
diff --git a/web-vue/packages/cms/package.json b/web-vue/packages/cms/package.json
new file mode 100644
index 00000000..64ca818d
--- /dev/null
+++ b/web-vue/packages/cms/package.json
@@ -0,0 +1,31 @@
+{
+ "name": "@jeesite/cms",
+ "version": "5.14.0",
+ "private": true,
+ "type": "module",
+ "scripts": {
+ "type:check": "vue-tsc --noEmit --skipLibCheck",
+ "uninstall": "rimraf node_modules",
+ "update": "ncu -u"
+ },
+ "dependencies": {
+ "@jeesite/cms-lib": "5.14.0-rc.1",
+ "qs": "6.14.0"
+ },
+ "devDependencies": {
+ "@types/qs": "6.14.0"
+ },
+ "homepage": "https://jeesite.com",
+ "repository": {
+ "type": "git",
+ "url": "https://gitee.com/thinkgem/jeesite-vue.git"
+ },
+ "bugs": {
+ "url": "https://gitee.com/thinkgem/jeesite-vue/issues"
+ },
+ "author": {
+ "name": "ThinkGem",
+ "email": "thinkgem@163.com",
+ "url": "https://gitee.com/thinkgem"
+ }
+}
diff --git a/web-vue/packages/cms/tsconfig.json b/web-vue/packages/cms/tsconfig.json
new file mode 100644
index 00000000..09cf8a6c
--- /dev/null
+++ b/web-vue/packages/cms/tsconfig.json
@@ -0,0 +1,19 @@
+{
+ "extends": "../../tsconfig.json",
+ "compilerOptions": {
+ "baseUrl": ".",
+ "paths": {
+ "@jeesite/cms/*": ["./*"]
+ }
+ },
+ "include": [
+ "./**/*.ts",
+ "./**/*.tsx",
+ "./**/*.vue"
+ ],
+ "exclude": [
+ "node_modules",
+ "vite.config.ts",
+ "dist"
+ ]
+}
diff --git a/web-vue/packages/cms/views/cms/chat/index.vue b/web-vue/packages/cms/views/cms/chat/index.vue
new file mode 100644
index 00000000..d1969859
--- /dev/null
+++ b/web-vue/packages/cms/views/cms/chat/index.vue
@@ -0,0 +1,240 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ t('我是你的 AI 助手,我可以帮你解答一些问题') }}
+
+ 提示:当前对接的是 DeepSeek 蒸馏过的 7B 超小模型,仅作为演示使用,AI 回答结果可能不够理想,
+ 可在自己本地部署,或对接其它大模型。此外当前向量库中只含了几篇关于 jeesite 的文章,
+ 知识库文章来源,可进入菜单查看:扩展功能 -> 内容管理 -> 内容发布
+ 提问举例:jeesite 简介、jeesite 优势、jeesite 技术栈,体验一下。
+
+
+
+
+
+
+
+
+
+ {{ t('发送') }}
+
+
+
+ {{ t('服务生成的所有内容均由人工智能模型生成,准确和完整性无法保证,不代表我们的态度或观点。') }}
+
+
+
+
+
+
diff --git a/web-vue/packages/core/README.md b/web-vue/packages/core/README.md
new file mode 100644
index 00000000..dd372de4
--- /dev/null
+++ b/web-vue/packages/core/README.md
@@ -0,0 +1,23 @@
+
+- 官方网站:
+- 使用文档:
+- 后端代码:
+- 前端代码:
+
+------
+
+
+ 如果你喜欢 JeeSite,请给她一个 ⭐️ Star,您的支持将是我们前行的动力。
+
+
+------
+
+- 问题反馈: [【新手必读】](https://gitee.com/thinkgem/jeesite5/issues/I18ARR)
+- 需求收集:
+- QQ 群:`127515876`、`209330483`、`223507718`、`709534275`、`730390092`、`1373527`、`183903863(外包)`
+- 微信群:添加客服微信 邀请您进群
+- 关注微信公众号,了解最新动态:
+
+
+
+
diff --git a/web-vue/packages/core/api/index.ts b/web-vue/packages/core/api/index.ts
new file mode 100644
index 00000000..272f5264
--- /dev/null
+++ b/web-vue/packages/core/api/index.ts
@@ -0,0 +1 @@
+// export * from './sys';
diff --git a/web-vue/packages/core/api/model/baseModel.ts b/web-vue/packages/core/api/model/baseModel.ts
new file mode 100644
index 00000000..1ba4f95a
--- /dev/null
+++ b/web-vue/packages/core/api/model/baseModel.ts
@@ -0,0 +1,56 @@
+import type { Result } from '@jeesite/types/axios';
+
+/**
+ * Copyright (c) 2013-Now http://jeesite.com All rights reserved.
+ * No deletion without permission, or be held responsible to law.
+ * @author ThinkGem
+ */
+export interface Page {
+ pageNo: number;
+ pageSize: number;
+ orderBy: string;
+ count: number;
+ list: T[];
+}
+
+export interface BasicModel extends Result, Recordable {
+ id: string;
+ page: Page;
+ isNewRecord: boolean;
+ dataMap: Map;
+
+ createBy?: string;
+ createDate?: string;
+
+ updateBy?: string;
+ updateDate?: string;
+
+ status?: string;
+}
+
+export interface TreeModel extends BasicModel {
+ parentCode?: string; // 父级编码
+ parentCodes?: string; // 所有父级编号
+
+ treeNames?: string; // 全节点名
+
+ treeSort?: string; // 排序号
+ treeSorts?: string; // 所有排序号
+
+ treeLeaf?: string; // 是否叶子节点
+ treeLevel?: number; // 树层次级别(从0开始)
+
+ childList?: T[]; // 子项列表
+
+ isRoot?: boolean; // 是否根节点
+ isTreeLeaf?: boolean; // 是否叶子
+ isLoading?: boolean; // 是否加载中
+}
+
+export interface TreeDataModel {
+ id: string;
+ pId: string;
+ name: string;
+ value?: string;
+ title?: string;
+}
diff --git a/web-vue/packages/core/api/msg/msgInner.ts b/web-vue/packages/core/api/msg/msgInner.ts
new file mode 100644
index 00000000..05a2c481
--- /dev/null
+++ b/web-vue/packages/core/api/msg/msgInner.ts
@@ -0,0 +1,43 @@
+/**
+ * Copyright (c) 2013-Now http://jeesite.com All rights reserved.
+ * No deletion without permission, or be held responsible to law.
+ * @author ThinkGem
+ */
+import { defHttp } from '@jeesite/core/utils/http/axios';
+import { useGlobSetting } from '@jeesite/core/hooks/setting';
+import { BasicModel, Page } from '@jeesite/core/api/model/baseModel';
+
+const { adminPath } = useGlobSetting();
+
+export interface MsgInner extends BasicModel {
+ msgTitle?: string; // 消息标题
+ contentLevel?: string; // 内容级别(1普通 2一般 3紧急)
+ contentType?: string; // 内容类型(1公告 2新闻 3会议 4其它)
+ msgContent?: string; // 消息内容
+ receiveType?: string; // 接受者类型(0全部 1用户 2部门 3角色 4岗位)
+ receiveCodes?: string; // 接受者字符串
+ receiveNames?: string; // 接受者名称字符串
+ sendUserCode?: string; // 发送者用户编码
+ sendUserName?: string; // 发送者用户姓名
+ sendDate?: string; // 发送时间
+ isAttac?: string; // 是否有附件
+ notifyTypes?: string; // 通知类型(PC APP 短信 邮件 微信)多选
+}
+
+export const msgInnerList = (params?: MsgInner | any) =>
+ defHttp.get({ url: adminPath + '/msg/msgInner/list', params });
+
+export const msgInnerListData = (params?: MsgInner | any) =>
+ defHttp.post>({ url: adminPath + '/msg/msgInner/listData', params });
+
+export const msgInnerForm = (params?: MsgInner | any) =>
+ defHttp.get({ url: adminPath + '/msg/msgInner/form', params });
+
+export const msgInnerView = (params?: MsgInner | any) =>
+ defHttp.get({ url: adminPath + '/msg/msgInner/view', params });
+
+export const msgInnerSave = (params?: any, data?: MsgInner | any) =>
+ defHttp.postJson({ url: adminPath + '/msg/msgInner/save', params, data });
+
+export const msgInnerDelete = (params?: MsgInner | any) =>
+ defHttp.get({ url: adminPath + '/msg/msgInner/delete', params });
diff --git a/web-vue/packages/core/api/sys/account.ts b/web-vue/packages/core/api/sys/account.ts
new file mode 100644
index 00000000..e53f8767
--- /dev/null
+++ b/web-vue/packages/core/api/sys/account.ts
@@ -0,0 +1,51 @@
+/**
+ * Copyright (c) 2013-Now http://jeesite.com All rights reserved.
+ * No deletion without permission, or be held responsible to law.
+ * @author ThinkGem
+ */
+import { defHttp } from '@jeesite/core/utils/http/axios';
+import { encryptByBase64 } from '@jeesite/core/utils/cipher';
+
+export const getLoginValidCode = (params?: any) =>
+ defHttp.post({ url: '/account/getLoginValidCode', params }, { errorMessageMode: 'none' });
+
+export const loginByValidCode = (params?: any) =>
+ defHttp.post({ url: '/account/loginByValidCode', params }, { errorMessageMode: 'none' });
+
+export const getFpValidCode = (params?: any) =>
+ defHttp.post({ url: '/account/getFpValidCode', params }, { errorMessageMode: 'none' });
+
+export const getPwdQuestion = (params?: any) =>
+ defHttp.post({ url: '/account/getPwdQuestion', params }, { errorMessageMode: 'none' });
+
+export const savePwdByValidCode = (params?: any) => {
+ if (params.password || params.confirmPassword) {
+ params.password = encryptByBase64(params.password);
+ params.confirmPassword = encryptByBase64(params.confirmPassword);
+ }
+ return defHttp.post({ url: '/account/savePwdByValidCode', params }, { errorMessageMode: 'none' });
+};
+
+export const savePwdByPwdQuestion = (params?: any) => {
+ if (params.pwdQuestionAnswer || params.pwdQuestionAnswer2 || params.pwdQuestionAnswer3) {
+ params.pwdQuestionAnswer = encryptByBase64(params.pwdQuestionAnswer);
+ params.pwdQuestionAnswer2 = encryptByBase64(params.pwdQuestionAnswer2);
+ params.pwdQuestionAnswer3 = encryptByBase64(params.pwdQuestionAnswer3);
+ }
+ if (params.password || params.confirmPassword) {
+ params.password = encryptByBase64(params.password);
+ params.confirmPassword = encryptByBase64(params.confirmPassword);
+ }
+ return defHttp.post({ url: '/account/savePwdByPwdQuestion', params }, { errorMessageMode: 'none' });
+};
+
+export const getRegValidCode = (params?: any) =>
+ defHttp.post({ url: '/account/getRegValidCode', params }, { errorMessageMode: 'none' });
+
+export const saveRegByValidCode = (params?: any) => {
+ if (params.password || params.confirmPassword) {
+ params.password = encryptByBase64(params.password);
+ params.confirmPassword = encryptByBase64(params.confirmPassword);
+ }
+ return defHttp.post({ url: '/account/saveRegByValidCode', params }, { errorMessageMode: 'none' });
+};
diff --git a/web-vue/packages/core/api/sys/area.ts b/web-vue/packages/core/api/sys/area.ts
new file mode 100644
index 00000000..2ebca10e
--- /dev/null
+++ b/web-vue/packages/core/api/sys/area.ts
@@ -0,0 +1,38 @@
+/**
+ * Copyright (c) 2013-Now http://jeesite.com All rights reserved.
+ * No deletion without permission, or be held responsible to law.
+ * @author ThinkGem
+ */
+import { defHttp } from '@jeesite/core/utils/http/axios';
+import { useGlobSetting } from '@jeesite/core/hooks/setting';
+import { TreeDataModel, TreeModel, Page } from '@jeesite/core/api/model/baseModel';
+
+const { adminPath } = useGlobSetting();
+
+export interface Area extends TreeModel {
+ areaCode?: string; // 区域编码
+ areaName?: string; // 区域名称
+ areaType?: string; // 区域类型
+}
+
+export const areaList = (params?: Area | any) => defHttp.get ({ url: adminPath + '/sys/area/list', params });
+
+export const areaListData = (params?: Area | any) =>
+ defHttp.post>({ url: adminPath + '/sys/area/listPageData', params });
+
+export const areaForm = (params?: Area | any) => defHttp.get ({ url: adminPath + '/sys/area/form', params });
+
+export const areaCreateNextNode = (params?: Area | any) =>
+ defHttp.get ({ url: adminPath + '/sys/area/createNextNode', params });
+
+export const areaSave = (params?: any, data?: Area | any) =>
+ defHttp.postJson ({ url: adminPath + '/sys/area/save', params, data });
+
+export const areaDisable = (params?: Area | any) => defHttp.get ({ url: adminPath + '/sys/area/disable', params });
+
+export const areaEnable = (params?: Area | any) => defHttp.get ({ url: adminPath + '/sys/area/enable', params });
+
+export const areaDelete = (params?: Area | any) => defHttp.get ({ url: adminPath + '/sys/area/delete', params });
+
+export const areaTreeData = (params?: any) =>
+ defHttp.get({ url: adminPath + '/sys/area/treeData', params });
diff --git a/web-vue/packages/core/api/sys/company.ts b/web-vue/packages/core/api/sys/company.ts
new file mode 100644
index 00000000..fb72b172
--- /dev/null
+++ b/web-vue/packages/core/api/sys/company.ts
@@ -0,0 +1,46 @@
+/**
+ * Copyright (c) 2013-Now http://jeesite.com All rights reserved.
+ * No deletion without permission, or be held responsible to law.
+ * @author ThinkGem
+ */
+import { defHttp } from '@jeesite/core/utils/http/axios';
+import { useGlobSetting } from '@jeesite/core/hooks/setting';
+import { TreeDataModel, TreeModel } from '@jeesite/core/api/model/baseModel';
+
+const { adminPath } = useGlobSetting();
+
+export interface Company extends TreeModel {
+ companyCode?: string; // 公司编码
+ viewCode?: string; // 公司代码
+ companyName?: string; // 公司名称
+ fullName?: string; // 公司全称
+ areaCode?: string; // 区域编码
+ extend?: any; // 扩展字段
+}
+
+export const companyList = (params?: Company | any) =>
+ defHttp.get({ url: adminPath + '/sys/company/list', params });
+
+export const companyListData = (params?: Company | any) =>
+ defHttp.post({ url: adminPath + '/sys/company/listData', params });
+
+export const companyForm = (params?: Company | any) =>
+ defHttp.get({ url: adminPath + '/sys/company/form', params });
+
+export const companyCreateNextNode = (params?: Company | any) =>
+ defHttp.get({ url: adminPath + '/sys/company/createNextNode', params });
+
+export const companySave = (params?: any, data?: Company | any) =>
+ defHttp.postJson({ url: adminPath + '/sys/company/save', params, data });
+
+export const companyDisable = (params?: Company | any) =>
+ defHttp.get({ url: adminPath + '/sys/company/disable', params });
+
+export const companyEnable = (params?: Company | any) =>
+ defHttp.get({ url: adminPath + '/sys/company/enable', params });
+
+export const companyDelete = (params?: Company | any) =>
+ defHttp.get({ url: adminPath + '/sys/company/delete', params });
+
+export const companyTreeData = (params?: any) =>
+ defHttp.get({ url: adminPath + '/sys/company/treeData', params });
diff --git a/web-vue/packages/core/api/sys/config.ts b/web-vue/packages/core/api/sys/config.ts
new file mode 100644
index 00000000..f3421d29
--- /dev/null
+++ b/web-vue/packages/core/api/sys/config.ts
@@ -0,0 +1,38 @@
+/**
+ * Copyright (c) 2013-Now http://jeesite.com All rights reserved.
+ * No deletion without permission, or be held responsible to law.
+ * @author ThinkGem
+ */
+import { defHttp } from '@jeesite/core/utils/http/axios';
+import { useGlobSetting } from '@jeesite/core/hooks/setting';
+import { BasicModel, Page } from '@jeesite/core/api/model/baseModel';
+
+const { adminPath } = useGlobSetting();
+
+export interface Config extends BasicModel {
+ configName?: string; // 名称
+ configKey?: string; // 参数键
+ configValue?: string; // 参数值
+ isSys?: string; // 系统内置(1是 0否)
+}
+
+export const configList = (params?: Config | any) =>
+ defHttp.get({ url: adminPath + '/sys/config/list', params });
+
+export const configListData = (params?: Config | any) =>
+ defHttp.post>({ url: adminPath + '/sys/config/listData', params });
+
+export const configForm = (params?: Config | any) =>
+ defHttp.get({ url: adminPath + '/sys/config/form', params });
+
+export const checkConfigKey = (oldConfigKey: string, configKey: string) =>
+ defHttp.get({
+ url: adminPath + '/sys/config/checkConfigKey',
+ params: { oldConfigKey, configKey },
+ });
+
+export const configSave = (params?: any, data?: Config | any) =>
+ defHttp.postJson({ url: adminPath + '/sys/config/save', params, data });
+
+export const configDelete = (params?: Config | any) =>
+ defHttp.get({ url: adminPath + '/sys/config/delete', params });
diff --git a/web-vue/packages/core/api/sys/corpAdmin.ts b/web-vue/packages/core/api/sys/corpAdmin.ts
new file mode 100644
index 00000000..69c717ea
--- /dev/null
+++ b/web-vue/packages/core/api/sys/corpAdmin.ts
@@ -0,0 +1,43 @@
+/**
+ * Copyright (c) 2013-Now http://jeesite.com All rights reserved.
+ * No deletion without permission, or be held responsible to law.
+ * @author ThinkGem
+ */
+import { defHttp } from '@jeesite/core/utils/http/axios';
+import { useGlobSetting } from '@jeesite/core/hooks/setting';
+import { Page, TreeDataModel } from '@jeesite/core/api/model/baseModel';
+import { User } from '@jeesite/core/api/sys/user';
+
+const { adminPath } = useGlobSetting();
+
+export const corpAdminList = (params?: User | any) =>
+ defHttp.get({ url: adminPath + '/sys/corpAdmin/list', params });
+
+export const corpAdminListData = (params?: User | any) =>
+ defHttp.post>({ url: adminPath + '/sys/corpAdmin/listData', params });
+
+export const corpAdminForm = (params?: User | any) =>
+ defHttp.get({ url: adminPath + '/sys/corpAdmin/form', params });
+
+export const corpAdminSave = (params?: any, data?: User | any) =>
+ defHttp.postJson({ url: adminPath + '/sys/corpAdmin/save', params, data });
+
+export const corpAdminDisable = (params?: User | any) =>
+ defHttp.get({ url: adminPath + '/sys/corpAdmin/disable', params });
+
+export const corpAdminEnable = (params?: User | any) =>
+ defHttp.get({ url: adminPath + '/sys/corpAdmin/enable', params });
+
+export const corpAdminResetpwd = (params?: User | any) =>
+ defHttp.get({ url: adminPath + '/sys/corpAdmin/resetpwd', params });
+
+export const corpAdminDelete = (params?: User | any) =>
+ defHttp.get({ url: adminPath + '/sys/corpAdmin/delete', params });
+
+export const corpAdminTreeData = (params?: any) =>
+ defHttp.get({ url: adminPath + '/sys/corpAdmin/treeData', params });
+
+export const switchCorp = (corpCode: string) =>
+ defHttp.get({
+ url: adminPath + '/sys/corpAdmin/switch/' + corpCode,
+ });
diff --git a/web-vue/packages/core/api/sys/dictData.ts b/web-vue/packages/core/api/sys/dictData.ts
new file mode 100644
index 00000000..43585801
--- /dev/null
+++ b/web-vue/packages/core/api/sys/dictData.ts
@@ -0,0 +1,55 @@
+/**
+ * Copyright (c) 2013-Now http://jeesite.com All rights reserved.
+ * No deletion without permission, or be held responsible to law.
+ * @author ThinkGem
+ */
+import { defHttp } from '@jeesite/core/utils/http/axios';
+import { useGlobSetting } from '@jeesite/core/hooks/setting';
+import { TreeDataModel, TreeModel } from '@jeesite/core/api/model/baseModel';
+
+const { adminPath } = useGlobSetting();
+
+export interface DictData extends TreeModel {
+ dictCode?: string; // 字典编码
+ dictLabelRaw?: string; // 字典标签
+ dictValue?: string; // 字典键值
+ dictIcon?: string; // 字典图标
+ dictType?: string; // 字典类型
+ isSys?: string; // 系统内置(1是 0否)
+ description?: string; // 字典描述
+ cssStyle?: string; // css样式(如:color:red)
+ cssClass?: string; // css类名(如:red)
+}
+
+export interface DictDataTree extends TreeDataModel {
+ icon?: string; // 字典图标
+ cssStyle?: string; // css样式(如:color:red)
+ cssClass?: string; // css类名(如:red)
+}
+
+export const dictDataList = (params?: DictData | any) =>
+ defHttp.get({ url: adminPath + '/sys/dictData/list', params });
+
+export const dictDataListData = (params?: DictData | any) =>
+ defHttp.post({ url: adminPath + '/sys/dictData/listData', params });
+
+export const dictDataForm = (params?: DictData | any) =>
+ defHttp.get({ url: adminPath + '/sys/dictData/form', params });
+
+export const dictDataCreateNextNode = (params?: DictData | any) =>
+ defHttp.get({ url: adminPath + '/sys/dictData/createNextNode', params });
+
+export const dictDataSave = (params?: any, data?: DictData | any) =>
+ defHttp.postJson({ url: adminPath + '/sys/dictData/save', params, data });
+
+export const dictDataDisable = (params?: DictData | any) =>
+ defHttp.get({ url: adminPath + '/sys/dictData/disable', params });
+
+export const dictDataEnable = (params?: DictData | any) =>
+ defHttp.get({ url: adminPath + '/sys/dictData/enable', params });
+
+export const dictDataDelete = (params?: DictData | any) =>
+ defHttp.get({ url: adminPath + '/sys/dictData/delete', params });
+
+export const dictDataTreeData = (params?: any) =>
+ defHttp.get({ url: adminPath + '/sys/dictData/treeData', params });
diff --git a/web-vue/packages/core/api/sys/dictType.ts b/web-vue/packages/core/api/sys/dictType.ts
new file mode 100644
index 00000000..488439f1
--- /dev/null
+++ b/web-vue/packages/core/api/sys/dictType.ts
@@ -0,0 +1,40 @@
+/**
+ * Copyright (c) 2013-Now http://jeesite.com All rights reserved.
+ * No deletion without permission, or be held responsible to law.
+ * @author ThinkGem
+ */
+import { defHttp } from '@jeesite/core/utils/http/axios';
+import { useGlobSetting } from '@jeesite/core/hooks/setting';
+import { BasicModel, Page, TreeDataModel } from '@jeesite/core/api/model/baseModel';
+
+const { adminPath } = useGlobSetting();
+
+export interface DictType extends BasicModel {
+ dictName?: string; // 字典名称
+ dictType?: string; // 字典类型
+ isSys?: string; // 是否系统字典
+}
+
+export const dictTypeList = (params?: DictType | any) =>
+ defHttp.get({ url: adminPath + '/sys/dictType/list', params });
+
+export const dictTypeListData = (params?: DictType | any) =>
+ defHttp.post>({ url: adminPath + '/sys/dictType/listData', params });
+
+export const dictTypeForm = (params?: DictType | any) =>
+ defHttp.get({ url: adminPath + '/sys/dictType/form', params });
+
+export const dictTypeSave = (params?: any, data?: DictType | any) =>
+ defHttp.postJson({ url: adminPath + '/sys/dictType/save', params, data });
+
+export const checkDictType = (oldDictType: string, dictType: string) =>
+ defHttp.get({
+ url: adminPath + '/sys/dictType/checkDictType',
+ params: { oldDictType, dictType },
+ });
+
+export const dictTypeDelete = (params?: DictType | any) =>
+ defHttp.get({ url: adminPath + '/sys/dictType/delete', params });
+
+export const dictTypeTreeData = (params?: any) =>
+ defHttp.get({ url: adminPath + '/sys/dictType/treeData', params });
diff --git a/web-vue/packages/core/api/sys/empUser.ts b/web-vue/packages/core/api/sys/empUser.ts
new file mode 100644
index 00000000..a19432e9
--- /dev/null
+++ b/web-vue/packages/core/api/sys/empUser.ts
@@ -0,0 +1,96 @@
+/**
+ * Copyright (c) 2013-Now http://jeesite.com All rights reserved.
+ * No deletion without permission, or be held responsible to law.
+ * @author ThinkGem
+ */
+import { defHttp } from '@jeesite/core/utils/http/axios';
+import { useGlobSetting } from '@jeesite/core/hooks/setting';
+import { BasicModel, Page, TreeDataModel } from '@jeesite/core/api/model/baseModel';
+import { User } from '@jeesite/core/api/sys/user';
+import { UploadApiResult } from '@jeesite/core/api/sys/upload';
+import { UploadFileParams } from '@jeesite/types/axios';
+import { AxiosProgressEvent } from 'axios';
+import { Office } from '@jeesite/core/api/sys/office';
+import { Company } from '@jeesite/core/api/sys/company';
+
+const { ctxPath, adminPath } = useGlobSetting();
+
+export interface EmpUser extends User {
+ employee?: Employee;
+}
+
+export interface Employee extends BasicModel {
+ empCode?: string;
+ empNo?: string;
+ empName?: string;
+ empNameEn?: string;
+ office?: Office;
+ company?: Company;
+}
+
+export const empUserIndex = (params?: EmpUser | any) =>
+ defHttp.get({ url: adminPath + '/sys/empUser/index', params });
+
+export const empUserList = (params?: EmpUser | any) =>
+ defHttp.get({ url: adminPath + '/sys/empUser/list', params });
+
+export const empUserListData = (params?: EmpUser | any) =>
+ defHttp.post>({ url: adminPath + '/sys/empUser/listData', params });
+
+export const empUserForm = (params?: EmpUser | any) =>
+ defHttp.get({ url: adminPath + '/sys/empUser/form', params });
+
+export const empUserSave = (params?: any, data?: EmpUser | any) =>
+ defHttp.postJson({ url: adminPath + '/sys/empUser/save', params, data });
+
+export const checkEmpNo = (oldEmpNo: string, empNo: string) =>
+ defHttp.get({
+ url: adminPath + '/sys/empUser/checkEmpNo',
+ params: { oldEmpNo, 'employee.empNo': empNo },
+ });
+
+export const empUserImportData = (
+ params: UploadFileParams,
+ onUploadProgress: (progressEvent: AxiosProgressEvent) => void,
+) =>
+ defHttp.uploadFile(
+ {
+ url: ctxPath + adminPath + '/sys/empUser/importData',
+ onUploadProgress,
+ },
+ params,
+ );
+
+export const empUserDisable = (params?: EmpUser | any) =>
+ defHttp.get({ url: adminPath + '/sys/empUser/disable', params });
+
+export const empUserEnable = (params?: EmpUser | any) =>
+ defHttp.get({ url: adminPath + '/sys/empUser/enable', params });
+
+export const resetpwd = (params?: EmpUser | any) =>
+ defHttp.get({ url: adminPath + '/sys/empUser/resetpwd', params });
+
+export const empUserDelete = (params?: EmpUser | any) =>
+ defHttp.get({ url: adminPath + '/sys/empUser/delete', params }, { errorMessageMode: 'none' });
+
+export const formAuthDataScope = (params?: EmpUser | any) =>
+ defHttp.get({ url: adminPath + '/sys/empUser/formAuthDataScope', params });
+
+export const ctrlDataTreeData = (params?: any) => {
+ const { url, ...params2 } = params;
+ return defHttp.get({ url: adminPath + url, params: params2 });
+};
+
+export const saveAuthDataScope = (params?: EmpUser | any) =>
+ defHttp.post({ url: adminPath + '/sys/empUser/saveAuthDataScope', params });
+
+export const empUserTreeData = (params?: any) =>
+ defHttp.get({ url: adminPath + '/sys/empUser/treeData', params });
+
+export const empUserOfficeListData = (params?: any) =>
+ defHttp.get({ url: adminPath + '/sys/empUser/officeListData', params });
+
+export const empUserSwitchOffice = (officeCode: string) =>
+ defHttp.get({
+ url: adminPath + '/sys/empUser/switchOffice' + (officeCode ? '/' + officeCode : ''),
+ });
diff --git a/web-vue/packages/core/api/sys/index.ts b/web-vue/packages/core/api/sys/index.ts
new file mode 100644
index 00000000..dc1fe055
--- /dev/null
+++ b/web-vue/packages/core/api/sys/index.ts
@@ -0,0 +1,18 @@
+export * from './area';
+export * from './company';
+export * from './config';
+export * from './corpAdmin';
+export * from './dictData';
+export * from './dictType';
+export * from './empUser';
+export * from './log';
+export * from './login';
+export * from './menu';
+export * from './module';
+export * from './office';
+export * from './online';
+export * from './post';
+export * from './role';
+export * from './secAdmin';
+export * from './upload';
+export * from './user';
diff --git a/web-vue/packages/core/api/sys/log.ts b/web-vue/packages/core/api/sys/log.ts
new file mode 100644
index 00000000..fca087ae
--- /dev/null
+++ b/web-vue/packages/core/api/sys/log.ts
@@ -0,0 +1,38 @@
+/**
+ * Copyright (c) 2013-Now http://jeesite.com All rights reserved.
+ * No deletion without permission, or be held responsible to law.
+ * @author ThinkGem
+ */
+import { defHttp } from '@jeesite/core/utils/http/axios';
+import { useGlobSetting } from '@jeesite/core/hooks/setting';
+import { BasicModel, Page } from '@jeesite/core/api/model/baseModel';
+
+const { adminPath } = useGlobSetting();
+
+export interface Log extends BasicModel {
+ logTitle?: string; // 日志标题
+ requestUri?: string; // 请求URI
+ logType?: string; // 日志类型
+ createBy?: string; // 操作用户编码
+ createByName?: string; // 操作用户名称
+ requestMethod?: string; // 操作方式
+ requestParams?: string; // 操作提交的数据
+ diffModifyData?: string; // 新旧数据比较结果
+ bizType?: string; // 业务类型
+ bizKey?: string; // 业务主键
+ remoteAddr?: string; // 客户端IP
+ serverAddr?: string; // 请求服务器地址
+ isException?: string; // 是否异常
+ exceptionInfo?: string; // 异常信息
+ userAgent?: string; // 用户代理
+ deviceName?: string; // 设备名称
+ browserName?: string; // 浏览器名称
+ executeTime?: number; // 响应时间
+}
+
+export const logList = (params?: Log | any) => defHttp.get({ url: adminPath + '/sys/log/list', params });
+
+export const logListData = (params?: Log | any) =>
+ defHttp.post>({ url: adminPath + '/sys/log/listData', params });
+
+export const logForm = (params?: Log | any) => defHttp.get({ url: adminPath + '/sys/log/form', params });
diff --git a/web-vue/packages/core/api/sys/login.ts b/web-vue/packages/core/api/sys/login.ts
new file mode 100644
index 00000000..d1ae93bc
--- /dev/null
+++ b/web-vue/packages/core/api/sys/login.ts
@@ -0,0 +1,98 @@
+/**
+ * Copyright (c) 2013-Now http://jeesite.com All rights reserved.
+ * No deletion without permission, or be held responsible to law.
+ * @author ThinkGem
+ */
+import { defHttp } from '@jeesite/core/utils/http/axios';
+import { UserInfo } from '@jeesite/types/store';
+import { ErrorMessageMode } from '@jeesite/types/axios';
+import { useGlobSetting } from '@jeesite/core/hooks/setting';
+import { encryptByBase64 } from '@jeesite/core/utils/cipher';
+import { Menu } from '@jeesite/core/router/types';
+import { useAppStore } from '@jeesite/core/store/modules/app';
+
+const { adminPath } = useGlobSetting();
+
+export interface LoginParams {
+ username: string;
+ password: string;
+ validCode?: string;
+ rememberMe?: boolean;
+}
+
+export interface LoginResult {
+ result: string;
+ message: string;
+ sessionid: string;
+ isValidCodeLogin: boolean;
+ user: UserInfo;
+ demoMode: boolean;
+ useCorpModel: boolean;
+ currentCorpCode: string;
+ currentCorpName: string;
+ modifyPasswordTip: string;
+ modifyPasswordMsg: string;
+ msgEnabled: boolean;
+ sysCode: string;
+ roleCode: string;
+ postCode: string;
+ title: string;
+ company: string;
+ version: string;
+ year: string;
+ lang: string;
+}
+
+export interface AuthInfo {
+ stringPermissions: string[];
+ roles: string[];
+}
+
+export const loginApi = (params: LoginParams, mode: ErrorMessageMode = 'none') => {
+ params.username = encryptByBase64(params.username);
+ params.password = encryptByBase64(params.password);
+ if (params.validCode) {
+ params.validCode = encryptByBase64(params.validCode);
+ }
+ return defHttp.post(
+ { url: adminPath + '/login', params, timeout: 20 * 1000 },
+ { errorMessageMode: mode },
+ );
+};
+
+export const switchSys = (sysCode: string) => {
+ const params = sysCode ? '/' + sysCode : sysCode;
+ return defHttp.get({ url: adminPath + '/switch' + params });
+};
+
+export const switchRole = (roleCode: string) => {
+ const params = roleCode ? '/' + roleCode : roleCode;
+ return defHttp.get({ url: adminPath + '/switchRole' + params });
+};
+
+export const switchPost = (postCode: string) => {
+ const params = postCode ? '/' + postCode : postCode;
+ return defHttp.get({ url: adminPath + '/switchPost' + params });
+};
+
+export const switchSkin = (name = '') => {
+ if (name == '') {
+ const appStore = useAppStore();
+ if (appStore.getDarkMode === 'dark') {
+ name = 'skin-dark';
+ } else {
+ const themeColor = appStore.getProjectConfig.themeColor;
+ name = themeColor == '#1890ff' ? 'skin-blue-light3' : 'skin-blue3';
+ }
+ }
+ return defHttp.get({ url: adminPath + '/switchSkin/' + name });
+};
+
+export const userInfoApi = (mode: ErrorMessageMode = 'message') =>
+ defHttp.get({ url: adminPath + '/index', timeout: 10 * 1000 }, { errorMessageMode: mode });
+
+export const authInfoApi = () => defHttp.get({ url: adminPath + '/authInfo' });
+
+export const menuRouteApi = () => defHttp.get({ url: adminPath + '/menuRoute' });
+
+export const logoutApi = () => defHttp.get({ url: adminPath + '/logout' });
diff --git a/web-vue/packages/core/api/sys/menu.ts b/web-vue/packages/core/api/sys/menu.ts
new file mode 100644
index 00000000..21ec12b9
--- /dev/null
+++ b/web-vue/packages/core/api/sys/menu.ts
@@ -0,0 +1,50 @@
+/**
+ * Copyright (c) 2013-Now http://jeesite.com All rights reserved.
+ * No deletion without permission, or be held responsible to law.
+ * @author ThinkGem
+ */
+import { defHttp } from '@jeesite/core/utils/http/axios';
+import { useGlobSetting } from '@jeesite/core/hooks/setting';
+import { TreeDataModel, TreeModel } from '@jeesite/core/api/model/baseModel';
+
+const { adminPath } = useGlobSetting();
+
+export interface Menu extends TreeModel {
+ menuCode?: string; // 菜单编码
+ menuNameRaw?: string; // 菜单名称
+ menuType?: string; // 菜单类型(1菜单 2权限)
+ menuUrl?: string; // 菜单链接
+ menuTarget?: string; // 目标窗口
+ menuIcon?: string; // 菜单图标
+ menuColor?: string; // 菜单颜色
+ menuTitle?: string; // 菜单标题
+ permission?: string; // 权限标识
+ weight?: number; // 菜单权重(权重越大,表示菜单的重要性越大)
+ isShow?: string; // 是否显示(1显示 0隐藏)
+ sysCode?: string; // 归属系统(default:主导航菜单、mobileApp:APP菜单)
+ moduleCodes?: string; // 归属模块(多个用逗号隔开)
+}
+
+export const menuIndex = (params?: Menu | any) => defHttp.get({ url: adminPath + '/sys/menu/index', params });
+
+export const menuList = (params?: Menu | any) => defHttp.get({ url: adminPath + '/sys/menu/list', params });
+
+export const menuListData = (params?: Menu | any) =>
+ defHttp.post({ url: adminPath + '/sys/menu/listData', params });
+
+export const menuForm = (params?: Menu | any) => defHttp.get({ url: adminPath + '/sys/menu/form', params });
+
+export const menuCreateNextNode = (params?: Menu | any) =>
+ defHttp.get({ url: adminPath + '/sys/menu/createNextNode', params });
+
+export const menuSave = (params?: any, data?: Menu | any) =>
+ defHttp.postJson({ url: adminPath + '/sys/menu/save', params, data });
+
+export const menuDisable = (params?: Menu | any) => defHttp.get({ url: adminPath + '/sys/menu/disable', params });
+
+export const menuEnable = (params?: Menu | any) => defHttp.get({ url: adminPath + '/sys/menu/enable', params });
+
+export const menuDelete = (params?: Menu | any) => defHttp.get({ url: adminPath + '/sys/menu/delete', params });
+
+export const menuTreeData = (params?: any) =>
+ defHttp.get({ url: adminPath + '/sys/menu/treeData', params });
diff --git a/web-vue/packages/core/api/sys/module.ts b/web-vue/packages/core/api/sys/module.ts
new file mode 100644
index 00000000..c35662e7
--- /dev/null
+++ b/web-vue/packages/core/api/sys/module.ts
@@ -0,0 +1,43 @@
+/**
+ * Copyright (c) 2013-Now http://jeesite.com All rights reserved.
+ * No deletion without permission, or be held responsible to law.
+ * @author ThinkGem
+ */
+import { defHttp } from '@jeesite/core/utils/http/axios';
+import { useGlobSetting } from '@jeesite/core/hooks/setting';
+import { BasicModel, Page } from '@jeesite/core/api/model/baseModel';
+
+const { adminPath } = useGlobSetting();
+
+export interface Module extends BasicModel {
+ moduleCode?: string; // 模块编码
+ moduleName?: string; // 模块名称
+ description?: string; // 模块描述
+ mainClassName?: string; // 主类全名
+ currentVersion?: string; // 当前版本
+ upgradeInfo?: string; // 升级信息
+}
+
+export const moduleList = (params?: Module | any) =>
+ defHttp.get({ url: adminPath + '/sys/module/list', params });
+
+export const moduleListData = (params?: Module | any) =>
+ defHttp.post>({ url: adminPath + '/sys/module/listData', params });
+
+export const moduleSelectData = (params?: Module | any) =>
+ defHttp.post({ url: adminPath + '/sys/module/selectData', params });
+
+export const moduleForm = (params?: Module | any) =>
+ defHttp.get({ url: adminPath + '/sys/module/form', params });
+
+export const moduleSave = (params?: any, data?: Module | any) =>
+ defHttp.postJson({ url: adminPath + '/sys/module/save', params, data });
+
+export const moduleDisable = (params?: Module | any) =>
+ defHttp.get({ url: adminPath + '/sys/module/disable', params });
+
+export const moduleEnable = (params?: Module | any) =>
+ defHttp.get({ url: adminPath + '/sys/module/enable', params });
+
+export const moduleDelete = (params?: Module | any) =>
+ defHttp.get({ url: adminPath + '/sys/module/delete', params });
diff --git a/web-vue/packages/core/api/sys/office.ts b/web-vue/packages/core/api/sys/office.ts
new file mode 100644
index 00000000..c530e39b
--- /dev/null
+++ b/web-vue/packages/core/api/sys/office.ts
@@ -0,0 +1,68 @@
+/**
+ * Copyright (c) 2013-Now http://jeesite.com All rights reserved.
+ * No deletion without permission, or be held responsible to law.
+ * @author ThinkGem
+ */
+import { defHttp } from '@jeesite/core/utils/http/axios';
+import { useGlobSetting } from '@jeesite/core/hooks/setting';
+import { TreeDataModel, TreeModel } from '@jeesite/core/api/model/baseModel';
+import { UploadApiResult } from '@jeesite/core/api/sys/upload';
+import { UploadFileParams } from '@jeesite/types/axios';
+import { AxiosProgressEvent } from 'axios';
+
+const { ctxPath, adminPath } = useGlobSetting();
+
+export interface Office extends TreeModel {
+ officeCode?: string; // 机构编码
+ viewCode?: string; // 机构代码
+ officeName?: string; // 机构名称
+ fullName?: string; // 机构全称
+ officeType?: string; // 机构类型
+ leader?: string; // 负责人
+ phone?: string; // 办公电话
+ address?: string; // 联系地址
+ zipCode?: string; // 邮政编码
+ email?: string; // 电子邮箱
+ extend?: any; // 扩展字段
+
+ companyCode?: string; // 根据公司查询机构,组织机构所属公司
+}
+
+export const officeList = (params?: Office | any) =>
+ defHttp.get({ url: adminPath + '/sys/office/list', params });
+
+export const officeListData = (params?: Office | any) =>
+ defHttp.post({ url: adminPath + '/sys/office/listData', params });
+
+export const officeForm = (params?: Office | any) =>
+ defHttp.get({ url: adminPath + '/sys/office/form', params });
+
+export const officeCreateNextNode = (params?: Office | any) =>
+ defHttp.get({ url: adminPath + '/sys/office/createNextNode', params });
+
+export const officeSave = (params?: any, data?: Office | any) =>
+ defHttp.postJson({ url: adminPath + '/sys/office/save', params, data });
+
+export const officeImportData = (
+ params: UploadFileParams,
+ onUploadProgress: (progressEvent: AxiosProgressEvent) => void,
+) =>
+ defHttp.uploadFile(
+ {
+ url: ctxPath + adminPath + '/sys/office/importData',
+ onUploadProgress,
+ },
+ params,
+ );
+
+export const officeDisable = (params?: Office | any) =>
+ defHttp.get({ url: adminPath + '/sys/office/disable', params });
+
+export const officeEnable = (params?: Office | any) =>
+ defHttp.get({ url: adminPath + '/sys/office/enable', params });
+
+export const officeDelete = (params?: Office | any) =>
+ defHttp.get({ url: adminPath + '/sys/office/delete', params });
+
+export const officeTreeData = (params?: any) =>
+ defHttp.get({ url: adminPath + '/sys/office/treeData', params });
diff --git a/web-vue/packages/core/api/sys/online.ts b/web-vue/packages/core/api/sys/online.ts
new file mode 100644
index 00000000..4da127cc
--- /dev/null
+++ b/web-vue/packages/core/api/sys/online.ts
@@ -0,0 +1,34 @@
+/**
+ * Copyright (c) 2013-Now http://jeesite.com All rights reserved.
+ * No deletion without permission, or be held responsible to law.
+ * @author ThinkGem
+ */
+import { defHttp } from '@jeesite/core/utils/http/axios';
+import { useGlobSetting } from '@jeesite/core/hooks/setting';
+import type { Result } from '@jeesite/types/axios';
+
+const { adminPath } = useGlobSetting();
+
+export interface Online {
+ id?: string;
+ startTimestamp?: string;
+ lastAccessTime?: string;
+ timeout?: string;
+ userCode?: string;
+ userName?: string;
+ userType?: string;
+ deviceType?: string;
+ host?: string;
+}
+
+export const onlineList = (params?: Online | any) =>
+ defHttp.get({ url: adminPath + '/sys/online/list', params });
+
+export const onlineListData = (params?: Online | any) =>
+ defHttp.post({ url: adminPath + '/sys/online/listData', params });
+
+export const onlineTickOut = (params?: Online | any) =>
+ defHttp.post({ url: adminPath + '/sys/online/tickOut', params });
+
+export const onlineCount = () =>
+ defHttp.post({ url: adminPath + '/sys/online/count?__notUpdateSession=true' }, { errorMessageMode: 'none' });
diff --git a/web-vue/packages/core/api/sys/post.ts b/web-vue/packages/core/api/sys/post.ts
new file mode 100644
index 00000000..1cc3e436
--- /dev/null
+++ b/web-vue/packages/core/api/sys/post.ts
@@ -0,0 +1,37 @@
+/**
+ * Copyright (c) 2013-Now http://jeesite.com All rights reserved.
+ * No deletion without permission, or be held responsible to law.
+ * @author ThinkGem
+ */
+import { defHttp } from '@jeesite/core/utils/http/axios';
+import { useGlobSetting } from '@jeesite/core/hooks/setting';
+import { TreeDataModel, BasicModel, Page } from '@jeesite/core/api/model/baseModel';
+
+const { adminPath } = useGlobSetting();
+
+export interface Post extends BasicModel {
+ postCode?: string; // 岗位编码
+ postName?: string; // 岗位名称
+ postType?: string; // 岗位分类(高管、中层、基层)
+ postSort?: number; // 岗位排序(升序)
+ viewCode?: string; // 岗位代码
+}
+
+export const postList = (params?: Post | any) => defHttp.get({ url: adminPath + '/sys/post/list', params });
+
+export const postListData = (params?: Post | any) =>
+ defHttp.post>({ url: adminPath + '/sys/post/listData', params });
+
+export const postForm = (params?: Post | any) => defHttp.get({ url: adminPath + '/sys/post/form', params });
+
+export const postDisable = (params?: Post | any) => defHttp.get({ url: adminPath + '/sys/post/disable', params });
+
+export const postEnable = (params?: Post | any) => defHttp.get({ url: adminPath + '/sys/post/enable', params });
+
+export const postSave = (params?: any, data?: Post | any) =>
+ defHttp.postJson({ url: adminPath + '/sys/post/save', params, data });
+
+export const postDelete = (params?: Post | any) => defHttp.get({ url: adminPath + '/sys/post/delete', params });
+
+export const postTreeData = (params?: any) =>
+ defHttp.get({ url: adminPath + '/sys/post/treeData', params });
diff --git a/web-vue/packages/core/api/sys/role.ts b/web-vue/packages/core/api/sys/role.ts
new file mode 100644
index 00000000..85a6b213
--- /dev/null
+++ b/web-vue/packages/core/api/sys/role.ts
@@ -0,0 +1,77 @@
+/**
+ * Copyright (c) 2013-Now http://jeesite.com All rights reserved.
+ * No deletion without permission, or be held responsible to law.
+ * @author ThinkGem
+ */
+import { defHttp } from '@jeesite/core/utils/http/axios';
+import { useGlobSetting } from '@jeesite/core/hooks/setting';
+import { Page, TreeDataModel, BasicModel } from '@jeesite/core/api/model/baseModel';
+
+const { adminPath } = useGlobSetting();
+
+export interface Role extends BasicModel {
+ roleCode?: string; // 角色编码
+ roleName?: string; // 角色名称
+ roleType?: string; // 角色分类(高管、中层、基层、其它)
+ roleSort?: number; // 角色排序(升序)
+ isSys?: string; // 系统内置(1是 0否)
+ userType?: string; // 用户类型(employee员工 member会员)
+ dataScope?: string; // 数据范围设置(0未设置 1全部数据 2自定义数据)
+ bizScope?: string; // 适应业务范围(不同的功能,不同的数据权限支持)
+ sysCodes?: string; // 包含系统
+ extend?: any; // 扩展字段
+ viewCode?: string; // 角色代码
+
+ userCode?: string; // 根据用户编号查询授权的角色列表
+}
+
+export const roleList = (params?: Role | any) => defHttp.get({ url: adminPath + '/sys/role/list', params });
+
+export const roleListData = (params?: Role | any) =>
+ defHttp.post>({ url: adminPath + '/sys/role/listData', params });
+
+export const roleForm = (params?: Role | any) => defHttp.get({ url: adminPath + '/sys/role/form', params });
+
+export const roleMenuTreeData = (params?: any) =>
+ defHttp.get({ url: adminPath + '/sys/role/menuTreeData', params });
+
+export const menuTreeDataByRoleCode = (params?: any) =>
+ defHttp.get({ url: adminPath + '/sys/role/menuTreeDataByRoleCode', params });
+
+export const roleSave = (params?: any, data?: Role | any) =>
+ defHttp.postJson({ url: adminPath + '/sys/role/save', params, data });
+
+export const checkRoleName = (oldRoleName: string, roleName: string) =>
+ defHttp.get({
+ url: adminPath + '/sys/role/checkRoleName',
+ params: { oldRoleName, roleName },
+ });
+
+export const roleDisable = (params?: Role | any) => defHttp.get({ url: adminPath + '/sys/role/disable', params });
+
+export const roleEnable = (params?: Role | any) => defHttp.get({ url: adminPath + '/sys/role/enable', params });
+
+export const roleDelete = (params?: Role | any) => defHttp.get({ url: adminPath + '/sys/role/delete', params });
+
+export const roleFormAuthDataScope = (params?: Role | any) =>
+ defHttp.get({ url: adminPath + '/sys/role/formAuthDataScope', params });
+
+export const roleCtrlDataTreeData = (params?: any) => {
+ const { url, ...params2 } = params;
+ return defHttp.get({ url: adminPath + url, params: params2 });
+};
+
+export const roleSaveAuthDataScope = (params?: Role | any) =>
+ defHttp.post({ url: adminPath + '/sys/role/saveAuthDataScope', params });
+
+export const formAuthUser = (params?: Role | any) =>
+ defHttp.get({ url: adminPath + '/sys/role/formAuthUser', params });
+
+export const saveAuthUser = (params?: Role | any) =>
+ defHttp.post({ url: adminPath + '/sys/role/saveAuthUser', params });
+
+export const deleteAuthUser = (params?: Role | any) =>
+ defHttp.post({ url: adminPath + '/sys/role/deleteAuthUser', params });
+
+export const roleTreeData = (params?: any) =>
+ defHttp.get({ url: adminPath + '/sys/role/treeData', params });
diff --git a/web-vue/packages/core/api/sys/secAdmin.ts b/web-vue/packages/core/api/sys/secAdmin.ts
new file mode 100644
index 00000000..858e3e70
--- /dev/null
+++ b/web-vue/packages/core/api/sys/secAdmin.ts
@@ -0,0 +1,25 @@
+/**
+ * Copyright (c) 2013-Now http://jeesite.com All rights reserved.
+ * No deletion without permission, or be held responsible to law.
+ * @author ThinkGem
+ */
+import { defHttp } from '@jeesite/core/utils/http/axios';
+import { useGlobSetting } from '@jeesite/core/hooks/setting';
+import { Page } from '@jeesite/core/api/model/baseModel';
+import { User } from '@jeesite/core/api/sys/user';
+
+const { adminPath } = useGlobSetting();
+
+export const secAdminList = (params?: User | any) =>
+ defHttp.get({ url: adminPath + '/sys/secAdmin/list', params });
+
+export const secAdminListData = (params?: User | any) =>
+ defHttp.post>({ url: adminPath + '/sys/secAdmin/listData', params });
+
+export const secAdminForm = (params?: User | any) =>
+ defHttp.get({ url: adminPath + '/sys/secAdmin/form', params });
+
+export const secAdminSave = (params?: any) => defHttp.post({ url: adminPath + '/sys/secAdmin/save', params });
+
+export const secAdminDelete = (params?: User | any) =>
+ defHttp.get({ url: adminPath + '/sys/secAdmin/delete', params });
diff --git a/web-vue/packages/core/api/sys/upload.ts b/web-vue/packages/core/api/sys/upload.ts
new file mode 100644
index 00000000..827a4b23
--- /dev/null
+++ b/web-vue/packages/core/api/sys/upload.ts
@@ -0,0 +1,82 @@
+import { defHttp } from '@jeesite/core/utils/http/axios';
+import { UploadFileParams } from '@jeesite/types/axios';
+import { useGlobSetting } from '@jeesite/core/hooks/setting';
+import { BasicModel } from '@jeesite/core/api/model/baseModel';
+import { AxiosProgressEvent } from 'axios';
+
+const { ctxPath, adminPath } = useGlobSetting();
+
+export interface UploadApiResult {
+ code: string;
+ // url: string;
+ result: string;
+ message: string;
+ fileEntityId: string;
+ fileUploadId: string;
+ fileUpload: FileUpload;
+}
+export interface FileEntity extends BasicModel {
+ fileId: string;
+ fileMd5: string;
+ filePath: string;
+ fileContentType: string;
+ fileExtension: string;
+ fileSize: number;
+ fileMeta: string;
+ fileMetaMap: any;
+ filePreview: string;
+}
+export interface FileUpload extends BasicModel {
+ fileEntity: FileEntity;
+ fileName: string;
+ fileType: string;
+ fileSort: number;
+ bizKey: string;
+ bizType: string;
+ bizKeyIsLike: string;
+ fileUrl?: string;
+}
+export interface UploadParams {
+ maxFileSize: number;
+ imageAllowSuffixes: string;
+ mediaAllowSuffixes: string;
+ fileAllowSuffixes: string;
+ imageMaxWidth?: number;
+ imageMaxHeight?: number;
+ checkmd5?: boolean;
+ chunked?: boolean;
+ chunkSize?: number;
+ threads?: number;
+}
+
+/**
+ * @description: Upload interface
+ */
+export function uploadFile(
+ params: UploadFileParams,
+ onUploadProgress: (progressEvent: ProgressEvent | AxiosProgressEvent) => void,
+ apiUploadUrl?: string,
+) {
+ if (params.file != undefined) {
+ return defHttp.uploadFile(
+ {
+ url: apiUploadUrl || ctxPath + adminPath + '/file/upload',
+ onUploadProgress,
+ },
+ params,
+ );
+ } else {
+ return defHttp.post(
+ { url: apiUploadUrl || ctxPath + adminPath + '/file/upload', params },
+ { errorMessageMode: 'none', apiUrl: '', urlPrefix: '' },
+ );
+ }
+}
+
+export const uploadFileList = (params?: FileUpload | any, apiFileListUrl?: string) =>
+ defHttp.get(
+ { url: apiFileListUrl || ctxPath + adminPath + '/file/fileList', params },
+ { errorMessageMode: 'none', apiUrl: '', urlPrefix: '' },
+ );
+
+export const uploadParams = () => defHttp.get({ url: adminPath + '/file/params' });
diff --git a/web-vue/packages/core/api/sys/user.ts b/web-vue/packages/core/api/sys/user.ts
new file mode 100644
index 00000000..2fbf68c6
--- /dev/null
+++ b/web-vue/packages/core/api/sys/user.ts
@@ -0,0 +1,75 @@
+/**
+ * Copyright (c) 2013-Now http://jeesite.com All rights reserved.
+ * No deletion without permission, or be held responsible to law.
+ * @author ThinkGem
+ */
+import { defHttp } from '@jeesite/core/utils/http/axios';
+import { useGlobSetting } from '@jeesite/core/hooks/setting';
+import { BasicModel, Page } from '@jeesite/core/api/model/baseModel';
+import { encryptByBase64 } from '@jeesite/core/utils/cipher';
+
+const { adminPath } = useGlobSetting();
+
+export interface User extends BasicModel {
+ userCode?: string; // 用户编码
+ loginCode?: string; // 登录账号
+ userName?: string; // 用户昵称
+ password?: string; // 登录密码
+ email?: string; // 电子邮箱
+ mobile?: string; // 手机号码
+ phone?: string; // 办公电话
+ sex?: string; // 用户性别
+ avatar?: string; // 头像路径
+ sign?: string; // 个性签名
+ wxOpenid?: string; // 绑定的微信号
+ mobileImei?: string; // 绑定的手机串号
+ userType?: string; // 用户类型(none未设置 employee员工 member会员)
+ refCode?: string; // 用户类型引用编号
+ refName?: string; // 用户类型引用姓名
+ mgrType?: string; // 管理员类型(0非管理员 1系统管理员 2二级管理员)
+ lastLoginIp?: string; // 最后登陆IP
+ lastLoginDate?: string; // 最后登陆时间
+ freezeDate?: string; // 冻结时间
+ freezeCause?: string; // 冻结原因
+ userWeight?: number; // 用户权重(降序)
+
+ avatarBase64?: string; // 头像Base64数据,修改头像时用
+
+ oldLastLoginIp?: string; // 上次登陆IP
+ oldLastLoginDate?: string; // 上次登陆日期
+
+ roleCode?: string; // 根据角色查询用户条件
+ isAll?: string; // 不过滤数据权限,查询全部用户
+ ctrlPermi?: string; // 权限控制类型(拥有权限、管理权限)
+}
+
+export const userListData = (params?: User | any) =>
+ defHttp.post>({ url: adminPath + '/sys/user/listData', params });
+
+export const checkLoginCode = (oldLoginCode: string, loginCode: string) =>
+ defHttp.get({
+ url: adminPath + '/sys/user/checkLoginCode',
+ params: { oldLoginCode, loginCode },
+ });
+
+export const userInfo = (params?: any) => defHttp.post({ url: adminPath + '/sys/user/info', params });
+
+export const infoSaveBase = (params?: any) => defHttp.post({ url: adminPath + '/sys/user/infoSaveBase', params });
+
+export const infoSavePwd = (params?: any) => {
+ params.oldPassword = encryptByBase64(params.oldPassword);
+ params.newPassword = encryptByBase64(params.newPassword);
+ params.confirmNewPassword = encryptByBase64(params.confirmNewPassword);
+ return defHttp.post({ url: adminPath + '/sys/user/infoSavePwd', params });
+};
+
+export const infoSavePqa = (params?: any) => {
+ params.validPassword = encryptByBase64(params.validPassword);
+ params.oldPwdQuestionAnswer = encryptByBase64(params.oldPwdQuestionAnswer);
+ params.oldPwdQuestionAnswer2 = encryptByBase64(params.oldPwdQuestionAnswer2);
+ params.oldPwdQuestionAnswer3 = encryptByBase64(params.oldPwdQuestionAnswer3);
+ params.pwdQuestionAnswer = encryptByBase64(params.pwdQuestionAnswer);
+ params.pwdQuestionAnswer2 = encryptByBase64(params.pwdQuestionAnswer2);
+ params.pwdQuestionAnswer3 = encryptByBase64(params.pwdQuestionAnswer3);
+ return defHttp.post({ url: adminPath + '/sys/user/infoSavePqa', params });
+};
diff --git a/web-vue/packages/core/components/Application/index.ts b/web-vue/packages/core/components/Application/index.ts
new file mode 100644
index 00000000..e5998e37
--- /dev/null
+++ b/web-vue/packages/core/components/Application/index.ts
@@ -0,0 +1,15 @@
+import { withInstall } from '@jeesite/core/utils';
+
+import appLogo from './src/AppLogo.vue';
+import appProvider from './src/AppProvider.vue';
+import appSearch from './src/search/AppSearch.vue';
+import appLocalePicker from './src/AppLocalePicker.vue';
+import appDarkModeToggle from './src/AppDarkModeToggle.vue';
+
+export { useAppProviderContext } from './src/useAppContext';
+
+export const AppLogo = withInstall(appLogo);
+export const AppProvider = withInstall(appProvider);
+export const AppSearch = withInstall(appSearch);
+export const AppLocalePicker = withInstall(appLocalePicker);
+export const AppDarkModeToggle = withInstall(appDarkModeToggle);
diff --git a/web-vue/packages/core/components/Application/src/AppDarkModeToggle.vue b/web-vue/packages/core/components/Application/src/AppDarkModeToggle.vue
new file mode 100644
index 00000000..c1245dc2
--- /dev/null
+++ b/web-vue/packages/core/components/Application/src/AppDarkModeToggle.vue
@@ -0,0 +1,79 @@
+
+
+
+
+
diff --git a/web-vue/packages/core/components/Application/src/AppLocalePicker.vue b/web-vue/packages/core/components/Application/src/AppLocalePicker.vue
new file mode 100644
index 00000000..3accbccb
--- /dev/null
+++ b/web-vue/packages/core/components/Application/src/AppLocalePicker.vue
@@ -0,0 +1,76 @@
+
+
+
+
+
+ {{ getLocaleText }}
+
+
+
+
+
+
diff --git a/web-vue/packages/core/components/Application/src/AppLogo.vue b/web-vue/packages/core/components/Application/src/AppLogo.vue
new file mode 100644
index 00000000..67181313
--- /dev/null
+++ b/web-vue/packages/core/components/Application/src/AppLogo.vue
@@ -0,0 +1,98 @@
+
+
+
+
+
+ {{ getTitle }}
+
+
+
+
+
diff --git a/web-vue/packages/core/components/Application/src/AppProvider.vue b/web-vue/packages/core/components/Application/src/AppProvider.vue
new file mode 100644
index 00000000..8599519c
--- /dev/null
+++ b/web-vue/packages/core/components/Application/src/AppProvider.vue
@@ -0,0 +1,78 @@
+
diff --git a/web-vue/packages/core/components/Application/src/search/AppSearch.vue b/web-vue/packages/core/components/Application/src/search/AppSearch.vue
new file mode 100644
index 00000000..e4ceed08
--- /dev/null
+++ b/web-vue/packages/core/components/Application/src/search/AppSearch.vue
@@ -0,0 +1,34 @@
+
diff --git a/web-vue/packages/core/components/Application/src/search/AppSearchFooter.vue b/web-vue/packages/core/components/Application/src/search/AppSearchFooter.vue
new file mode 100644
index 00000000..bb0983f0
--- /dev/null
+++ b/web-vue/packages/core/components/Application/src/search/AppSearchFooter.vue
@@ -0,0 +1,58 @@
+
+
+
+
{{ t('component.app.toSearch') }}
+
+
+
{{ t('component.app.toNavigate') }}
+
+
{{ t('common.closeText') }}
+
+
+
+
+
diff --git a/web-vue/packages/core/components/Application/src/search/AppSearchKeyItem.vue b/web-vue/packages/core/components/Application/src/search/AppSearchKeyItem.vue
new file mode 100644
index 00000000..b3b83a30
--- /dev/null
+++ b/web-vue/packages/core/components/Application/src/search/AppSearchKeyItem.vue
@@ -0,0 +1,11 @@
+
+
+
+
+
+
diff --git a/web-vue/packages/core/components/Application/src/search/AppSearchModal.vue b/web-vue/packages/core/components/Application/src/search/AppSearchModal.vue
new file mode 100644
index 00000000..50c21874
--- /dev/null
+++ b/web-vue/packages/core/components/Application/src/search/AppSearchModal.vue
@@ -0,0 +1,269 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ t('common.cancelText') }}
+
+
+
+
+ {{ t('component.app.searchNotData') }}
+
+
+
+
+
+
+
+
+ {{ item.name }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/web-vue/packages/core/components/Application/src/search/useMenuSearch.ts b/web-vue/packages/core/components/Application/src/search/useMenuSearch.ts
new file mode 100644
index 00000000..b3b34ff8
--- /dev/null
+++ b/web-vue/packages/core/components/Application/src/search/useMenuSearch.ts
@@ -0,0 +1,166 @@
+import type { Menu } from '@jeesite/core/router/types';
+import { ref, onBeforeMount, unref, Ref, nextTick } from 'vue';
+import { getMenus } from '@jeesite/core/router/menus';
+import { cloneDeep } from 'lodash-es';
+import { filter, forEach } from '@jeesite/core/utils/helper/treeHelper';
+import { useGo } from '@jeesite/core/hooks/web/usePage';
+import { useScrollTo } from '@jeesite/core/hooks/event/useScrollTo';
+import { onKeyStroke, useDebounceFn } from '@vueuse/core';
+import { useI18n } from '@jeesite/core/hooks/web/useI18n';
+
+export interface SearchResult {
+ name: string;
+ path: string;
+ icon?: string;
+}
+
+// Translate special characters
+function transform(c: string) {
+ const code: string[] = ['$', '(', ')', '*', '+', '.', '[', ']', '?', '\\', '^', '{', '}', '|'];
+ return code.includes(c) ? `\\${c}` : c;
+}
+
+function createSearchReg(key: string) {
+ const keys = [...key].map((item) => transform(item));
+ const str = ['', ...keys, ''].join('.*');
+ return new RegExp(str);
+}
+
+export function useMenuSearch(refs: Ref, scrollWrap: Ref, emit: EmitType) {
+ const searchResult = ref([]);
+ const keyword = ref('');
+ const activeIndex = ref(-1);
+
+ let menuList: Menu[] = [];
+
+ const { t } = useI18n();
+ const go = useGo();
+ const handleSearch = useDebounceFn(search, 200);
+
+ onBeforeMount(async () => {
+ const list = await getMenus();
+ menuList = cloneDeep(list);
+ forEach(menuList, (item) => {
+ item.name = t(item.name);
+ });
+ });
+
+ function search(e: ChangeEvent) {
+ e?.stopPropagation();
+ const key = e.target.value || '';
+ keyword.value = key.trim();
+ if (!key) {
+ searchResult.value = [];
+ return;
+ }
+ const reg = createSearchReg(unref(keyword));
+ const filterMenu = filter(menuList, (item) => {
+ return reg.test(item.name) && !item.hideMenu;
+ });
+ searchResult.value = handlerSearchResult(filterMenu, reg);
+ activeIndex.value = 0;
+ }
+
+ function handlerSearchResult(filterMenu: Menu[], reg: RegExp, parent?: Menu) {
+ const ret: SearchResult[] = [];
+ filterMenu.forEach((item) => {
+ const { name, path, icon, children, hideMenu, meta } = item;
+ if (!hideMenu && reg.test(name) && (!children?.length || meta?.hideChildrenInMenu)) {
+ ret.push({
+ name: parent?.name ? `${parent.name} > ${name}` : name,
+ path,
+ icon,
+ });
+ }
+ if (!meta?.hideChildrenInMenu && Array.isArray(children) && children.length) {
+ ret.push(...handlerSearchResult(children, reg, item));
+ }
+ });
+ return ret;
+ }
+
+ // Activate when the mouse moves to a certain line
+ function handleMouseenter(e: any) {
+ const index = e.target.dataset.index;
+ activeIndex.value = Number(index);
+ }
+
+ // Arrow key up
+ function handleUp() {
+ if (!searchResult.value.length) return;
+ activeIndex.value--;
+ if (activeIndex.value < 0) {
+ activeIndex.value = searchResult.value.length - 1;
+ }
+ handleScroll();
+ }
+
+ // Arrow key down
+ function handleDown() {
+ if (!searchResult.value.length) return;
+ activeIndex.value++;
+ if (activeIndex.value > searchResult.value.length - 1) {
+ activeIndex.value = 0;
+ }
+ handleScroll();
+ }
+
+ // When the keyboard up and down keys move to an invisible place
+ // the scroll bar needs to scroll automatically
+ function handleScroll() {
+ const refList = unref(refs);
+ if (!refList || !Array.isArray(refList) || refList.length === 0 || !unref(scrollWrap)) {
+ return;
+ }
+
+ const index = unref(activeIndex);
+ const currentRef = refList[index];
+ if (!currentRef) {
+ return;
+ }
+ const wrapEl = unref(scrollWrap);
+ if (!wrapEl) {
+ return;
+ }
+ const scrollHeight = currentRef.offsetTop + currentRef.offsetHeight;
+ const wrapHeight = wrapEl.offsetHeight;
+ const { start } = useScrollTo({
+ el: wrapEl,
+ duration: 100,
+ to: scrollHeight - wrapHeight,
+ });
+ start();
+ }
+
+ // enter keyboard event
+ async function handleEnter() {
+ if (!searchResult.value.length) {
+ return;
+ }
+ const result = unref(searchResult);
+ const index = unref(activeIndex);
+ if (result.length === 0 || index < 0) {
+ return;
+ }
+ const to = result[index];
+ handleClose();
+ await nextTick();
+ await go(to.path);
+ }
+
+ // close search modal
+ function handleClose() {
+ searchResult.value = [];
+ emit('close');
+ }
+
+ // enter search
+ onKeyStroke('Enter', handleEnter);
+ // Monitor keyboard arrow keys
+ onKeyStroke('ArrowUp', handleUp);
+ onKeyStroke('ArrowDown', handleDown);
+ // esc close
+ onKeyStroke('Escape', handleClose);
+
+ return { handleSearch, searchResult, keyword, activeIndex, handleMouseenter, handleEnter };
+}
diff --git a/web-vue/packages/core/components/Application/src/useAppContext.ts b/web-vue/packages/core/components/Application/src/useAppContext.ts
new file mode 100644
index 00000000..2f602e1b
--- /dev/null
+++ b/web-vue/packages/core/components/Application/src/useAppContext.ts
@@ -0,0 +1,17 @@
+import { InjectionKey, Ref } from 'vue';
+import { createContext, useContext } from '@jeesite/core/hooks/core/useContext';
+
+export interface AppProviderContextProps {
+ prefixCls: Ref;
+ isMobile: Ref;
+}
+
+const key: InjectionKey = Symbol();
+
+export function createAppProviderContext(context: AppProviderContextProps) {
+ return createContext(context, key);
+}
+
+export function useAppProviderContext() {
+ return useContext(key);
+}
diff --git a/web-vue/packages/core/components/Authority/index.ts b/web-vue/packages/core/components/Authority/index.ts
new file mode 100644
index 00000000..0970ecf5
--- /dev/null
+++ b/web-vue/packages/core/components/Authority/index.ts
@@ -0,0 +1,4 @@
+import { withInstall } from '@jeesite/core/utils';
+import authority from './src/Authority.vue';
+
+export const Authority = withInstall(authority);
diff --git a/web-vue/packages/core/components/Authority/src/Authority.vue b/web-vue/packages/core/components/Authority/src/Authority.vue
new file mode 100644
index 00000000..9ced8801
--- /dev/null
+++ b/web-vue/packages/core/components/Authority/src/Authority.vue
@@ -0,0 +1,45 @@
+
+
diff --git a/web-vue/packages/core/components/Basic/index.ts b/web-vue/packages/core/components/Basic/index.ts
new file mode 100644
index 00000000..fdbf4940
--- /dev/null
+++ b/web-vue/packages/core/components/Basic/index.ts
@@ -0,0 +1,8 @@
+import { withInstall } from '@jeesite/core/utils';
+import basicArrow from './src/BasicArrow.vue';
+import basicTitle from './src/BasicTitle.vue';
+import basicHelp from './src/BasicHelp.vue';
+
+export const BasicArrow = withInstall(basicArrow);
+export const BasicTitle = withInstall(basicTitle);
+export const BasicHelp = withInstall(basicHelp);
diff --git a/web-vue/packages/core/components/Basic/src/BasicArrow.vue b/web-vue/packages/core/components/Basic/src/BasicArrow.vue
new file mode 100644
index 00000000..18a78742
--- /dev/null
+++ b/web-vue/packages/core/components/Basic/src/BasicArrow.vue
@@ -0,0 +1,117 @@
+
+
+
+
+
+
+
+
+
diff --git a/web-vue/packages/core/components/Basic/src/BasicHelp.vue b/web-vue/packages/core/components/Basic/src/BasicHelp.vue
new file mode 100644
index 00000000..da5237c0
--- /dev/null
+++ b/web-vue/packages/core/components/Basic/src/BasicHelp.vue
@@ -0,0 +1,119 @@
+
+
diff --git a/web-vue/packages/core/components/Basic/src/BasicTitle.vue b/web-vue/packages/core/components/Basic/src/BasicTitle.vue
new file mode 100644
index 00000000..4fd95d93
--- /dev/null
+++ b/web-vue/packages/core/components/Basic/src/BasicTitle.vue
@@ -0,0 +1,76 @@
+
+
+
+
+
+
+
+
diff --git a/web-vue/packages/core/components/Button/index.ts b/web-vue/packages/core/components/Button/index.ts
new file mode 100644
index 00000000..5c0a4ffa
--- /dev/null
+++ b/web-vue/packages/core/components/Button/index.ts
@@ -0,0 +1,9 @@
+import { withInstall } from '@jeesite/core/utils';
+import type { ExtractPropTypes } from 'vue';
+import button from './src/BasicButton.vue';
+import popConfirmButton from './src/PopConfirmButton.vue';
+import { buttonProps } from './src/props';
+
+export const Button = withInstall(button);
+export const PopConfirmButton = withInstall(popConfirmButton);
+export declare type ButtonProps = Partial>;
diff --git a/web-vue/packages/core/components/Button/src/BasicButton.vue b/web-vue/packages/core/components/Button/src/BasicButton.vue
new file mode 100644
index 00000000..01f1a42f
--- /dev/null
+++ b/web-vue/packages/core/components/Button/src/BasicButton.vue
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/web-vue/packages/core/components/Button/src/PopConfirmButton.vue b/web-vue/packages/core/components/Button/src/PopConfirmButton.vue
new file mode 100644
index 00000000..b3286b5c
--- /dev/null
+++ b/web-vue/packages/core/components/Button/src/PopConfirmButton.vue
@@ -0,0 +1,55 @@
+
diff --git a/web-vue/packages/core/components/Button/src/props.ts b/web-vue/packages/core/components/Button/src/props.ts
new file mode 100644
index 00000000..d79d378a
--- /dev/null
+++ b/web-vue/packages/core/components/Button/src/props.ts
@@ -0,0 +1,19 @@
+export const buttonProps = {
+ color: { type: String, validator: (v) => ['error', 'warning', 'success', ''].includes(v) },
+ loading: { type: Boolean },
+ disabled: { type: Boolean },
+ /**
+ * Text before icon.
+ */
+ preIcon: { type: String },
+ /**
+ * Text after icon.
+ */
+ postIcon: { type: String },
+ /**
+ * preIcon and postIcon icon size.
+ * @default: 14
+ */
+ iconSize: { type: Number, default: 14 },
+ onClick: { type: Function as PropType<(...args) => any>, default: null },
+};
diff --git a/web-vue/packages/core/components/CardList/index.ts b/web-vue/packages/core/components/CardList/index.ts
new file mode 100644
index 00000000..874790e1
--- /dev/null
+++ b/web-vue/packages/core/components/CardList/index.ts
@@ -0,0 +1,4 @@
+import { withInstall } from '@jeesite/core/utils';
+import cardList from './src/CardList.vue';
+
+export const CardList = withInstall(cardList);
diff --git a/web-vue/packages/core/components/CardList/src/CardList.vue b/web-vue/packages/core/components/CardList/src/CardList.vue
new file mode 100644
index 00000000..b28d707b
--- /dev/null
+++ b/web-vue/packages/core/components/CardList/src/CardList.vue
@@ -0,0 +1,159 @@
+
+
+
+
+
+
+
+
+
+ 每页显示数量
+
+
+
+
+
+ 刷新
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ item.userName }}
+
+
+
+
+
+
+
diff --git a/web-vue/packages/core/components/CardList/src/data.ts b/web-vue/packages/core/components/CardList/src/data.ts
new file mode 100644
index 00000000..34f2e1a8
--- /dev/null
+++ b/web-vue/packages/core/components/CardList/src/data.ts
@@ -0,0 +1,25 @@
+import { ref } from 'vue';
+// 每行个数
+export const grid = ref(20);
+// slider属性
+export const useSlider = (min = 6, max = 20) => {
+ // 每行显示个数滑动条
+ const getMarks = () => {
+ const l = {};
+ for (let i = min; i < max + 1; i++) {
+ l[i] = {
+ style: {
+ color: '#fff',
+ },
+ label: i,
+ };
+ }
+ return l;
+ };
+ return {
+ min,
+ max,
+ marks: getMarks(),
+ step: 1,
+ };
+};
diff --git a/web-vue/packages/core/components/ClickOutSide/index.ts b/web-vue/packages/core/components/ClickOutSide/index.ts
new file mode 100644
index 00000000..54604113
--- /dev/null
+++ b/web-vue/packages/core/components/ClickOutSide/index.ts
@@ -0,0 +1,4 @@
+import { withInstall } from '@jeesite/core/utils';
+import clickOutSide from './src/ClickOutSide.vue';
+
+export const ClickOutSide = withInstall(clickOutSide);
diff --git a/web-vue/packages/core/components/ClickOutSide/src/ClickOutSide.vue b/web-vue/packages/core/components/ClickOutSide/src/ClickOutSide.vue
new file mode 100644
index 00000000..c043cc19
--- /dev/null
+++ b/web-vue/packages/core/components/ClickOutSide/src/ClickOutSide.vue
@@ -0,0 +1,19 @@
+
+
+
+
+
+
diff --git a/web-vue/packages/core/components/CodeEditor/index.ts b/web-vue/packages/core/components/CodeEditor/index.ts
new file mode 100644
index 00000000..8de7672b
--- /dev/null
+++ b/web-vue/packages/core/components/CodeEditor/index.ts
@@ -0,0 +1,8 @@
+import { withInstall } from '@jeesite/core/utils';
+import codeEditor from './src/CodeEditor.vue';
+import jsonPreview from './src/json-preview/JsonPreview.vue';
+
+export const CodeEditor = withInstall(codeEditor);
+export const JsonPreview = withInstall(jsonPreview);
+
+export * from './src/typing';
diff --git a/web-vue/packages/core/components/CodeEditor/src/CodeEditor.vue b/web-vue/packages/core/components/CodeEditor/src/CodeEditor.vue
new file mode 100644
index 00000000..4e1e90fd
--- /dev/null
+++ b/web-vue/packages/core/components/CodeEditor/src/CodeEditor.vue
@@ -0,0 +1,59 @@
+
+
+
+
+
+
diff --git a/web-vue/packages/core/components/CodeEditor/src/codemirror/CodeMirror.vue b/web-vue/packages/core/components/CodeEditor/src/codemirror/CodeMirror.vue
new file mode 100644
index 00000000..87af69ab
--- /dev/null
+++ b/web-vue/packages/core/components/CodeEditor/src/codemirror/CodeMirror.vue
@@ -0,0 +1,132 @@
+
+
+
+
+
+
diff --git a/web-vue/packages/core/components/CodeEditor/src/json-preview/JsonPreview.vue b/web-vue/packages/core/components/CodeEditor/src/json-preview/JsonPreview.vue
new file mode 100644
index 00000000..1164fe8b
--- /dev/null
+++ b/web-vue/packages/core/components/CodeEditor/src/json-preview/JsonPreview.vue
@@ -0,0 +1,16 @@
+
+
+
+
+
diff --git a/web-vue/packages/core/components/CodeEditor/src/typing.ts b/web-vue/packages/core/components/CodeEditor/src/typing.ts
new file mode 100644
index 00000000..b154c79e
--- /dev/null
+++ b/web-vue/packages/core/components/CodeEditor/src/typing.ts
@@ -0,0 +1,247 @@
+export enum MODE {
+ JSON = 'application/json',
+ APL = 'apl',
+ ASCIIARMOR = 'asciiarmor',
+ ASTERISK = 'asterisk',
+ BRAINFUCK = 'brainfuck',
+ CLIKE = 'clike',
+ CLOJURE = 'clojure',
+ CMAKE = 'cmake',
+ COBOL = 'cobol',
+ COFFEESCRIPT = 'coffeescript',
+ COMMONLISP = 'commonlisp',
+ CRYSTAL = 'crystal',
+ CSS = 'css',
+ CYPHER = 'cypher',
+ D = 'd',
+ DART = 'dart',
+ DIFF = 'diff',
+ DJANGO = 'django',
+ DOCKERFILE = 'dockerfile',
+ DTD = 'dtd',
+ DYLAN = 'dylan',
+ EBNF = 'ebnf',
+ ECL = 'ecl',
+ EIFFEL = 'eiffel',
+ ELM = 'elm',
+ ERLANG = 'erlang',
+ FACTOR = 'factor',
+ FCL = 'fcl',
+ FORTH = 'forth',
+ FORTRAN = 'fortran',
+ GAS = 'gas',
+ GFM = 'gfm',
+ GHERKIN = 'gherkin',
+ GO = 'go',
+ GROOVY = 'groovy',
+ HAML = 'haml',
+ HANDLEBARS = 'handlebars',
+ HASKELL = 'haskell',
+ HAXE = 'haxe',
+ HTMLEMBEDDED = 'htmlembedded',
+ HTMLMIXED = 'htmlmixed',
+ HTTP = 'http',
+ IDL = 'idl',
+ JAVASCRIPT = 'javascript',
+ JINJA2 = 'jinja2',
+ JSX = 'jsx',
+ JULIA = 'julia',
+ LIVESCRIPT = 'livescript',
+ LUA = 'lua',
+ MARKDOWN = 'markdown',
+ MATHEMATICA = 'mathematica',
+ MBOX = 'mbox',
+ MIRC = 'mirc',
+ MLLIKE = 'mllike',
+ MODELICA = 'modelica',
+ MSCGEN = 'mscgen',
+ MUMPS = 'mumps',
+ NGINX = 'nginx',
+ NSIS = 'nsis',
+ NTRIPLES = 'ntriples',
+ OCTAVE = 'octave',
+ OZ = 'oz',
+ PASCAL = 'pascal',
+ PEGJS = 'pegjs',
+ PERL = 'perl',
+ PHP = 'php',
+ PIG = 'pig',
+ POWERSHELL = 'powershell',
+ PROPERTIES = 'properties',
+ PROTOBUF = 'protobuf',
+ PUG = 'pug',
+ PUPPET = 'puppet',
+ PYTHON = 'python',
+ Q = 'q',
+ R = 'r',
+ RPM = 'rpm',
+ RST = 'rst',
+ RUBY = 'ruby',
+ RUST = 'rust',
+ SAS = 'sas',
+ SASS = 'sass',
+ SCHEME = 'scheme',
+ SHELL = 'shell',
+ SIEVE = 'sieve',
+ SLIM = 'slim',
+ SMALLTALK = 'smalltalk',
+ SMARTY = 'smarty',
+ SOLR = 'solr',
+ SOY = 'soy',
+ SPARQL = 'sparql',
+ SPREADSHEET = 'spreadsheet',
+ SQL = 'sql',
+ STEX = 'stex',
+ STYLUS = 'stylus',
+ SWIFT = 'swift',
+ TCL = 'tcl',
+ TEXTILE = 'textile',
+ TIDDLYWIKI = 'tiddlywiki',
+ TIKI = 'tiki',
+ TOML = 'toml',
+ TORNADO = 'tornado',
+ TROFF = 'troff',
+ TTCN = 'ttcn',
+ TURTLE = 'turtle',
+ TWIG = 'twig',
+ VB = 'vb',
+ VBSCRIPT = 'vbscript',
+ VELOCITY = 'velocity',
+ VERILOG = 'verilog',
+ VHDL = 'vhdl',
+ VUE = 'vue',
+ WAST = 'wast',
+ WEBIDL = 'webidl',
+ XML = 'xml',
+ XQUERY = 'xquery',
+ YACAS = 'yacas',
+ YAML = 'yaml',
+ Z80 = 'z80',
+}
+/**
+ * @description: DynamicImport codemirror
+ */
+export function parserDynamicImport(str: MODE): () => Promise {
+ const dynamicArray = {
+ // adapt before demo
+ 'application/json': async () => await import('codemirror/mode/javascript/javascript'),
+ apl: async () => await import('codemirror/mode/apl/apl'),
+ asciiarmor: async () => await import('codemirror/mode/asciiarmor/asciiarmor'),
+ asterisk: async () => await import('codemirror/mode/asterisk/asterisk'),
+ brainfuck: async () => await import('codemirror/mode/brainfuck/brainfuck'),
+ clike: async () => await import('codemirror/mode/clike/clike'),
+ clojure: async () => await import('codemirror/mode/clojure/clojure'),
+ cmake: async () => await import('codemirror/mode/cmake/cmake'),
+ cobol: async () => await import('codemirror/mode/cobol/cobol'),
+ coffeescript: async () => await import('codemirror/mode/coffeescript/coffeescript'),
+ commonlisp: async () => await import('codemirror/mode/commonlisp/commonlisp'),
+ crystal: async () => await import('codemirror/mode/crystal/crystal'),
+ css: async () => await import('codemirror/mode/css/css'),
+ cypher: async () => await import('codemirror/mode/cypher/cypher'),
+ d: async () => await import('codemirror/mode/d/d'),
+ dart: async () => await import('codemirror/mode/dart/dart'),
+ diff: async () => await import('codemirror/mode/diff/diff'),
+ django: async () => await import('codemirror/mode/django/django'),
+ dockerfile: async () => await import('codemirror/mode/dockerfile/dockerfile'),
+ dtd: async () => await import('codemirror/mode/dtd/dtd'),
+ dylan: async () => await import('codemirror/mode/dylan/dylan'),
+ ebnf: async () => await import('codemirror/mode/ebnf/ebnf'),
+ ecl: async () => await import('codemirror/mode/ecl/ecl'),
+ eiffel: async () => await import('codemirror/mode/eiffel/eiffel'),
+ elm: async () => await import('codemirror/mode/elm/elm'),
+ erlang: async () => await import('codemirror/mode/erlang/erlang'),
+ factor: async () => await import('codemirror/mode/factor/factor'),
+ fcl: async () => await import('codemirror/mode/fcl/fcl'),
+ forth: async () => await import('codemirror/mode/forth/forth'),
+ fortran: async () => await import('codemirror/mode/fortran/fortran'),
+ gas: async () => await import('codemirror/mode/gas/gas'),
+ gfm: async () => await import('codemirror/mode/gfm/gfm'),
+ gherkin: async () => await import('codemirror/mode/gherkin/gherkin'),
+ go: async () => await import('codemirror/mode/go/go'),
+ groovy: async () => await import('codemirror/mode/groovy/groovy'),
+ haml: async () => await import('codemirror/mode/haml/haml'),
+ handlebars: async () => await import('codemirror/mode/handlebars/handlebars'),
+ haskell: async () => await import('codemirror/mode/haskell/haskell'),
+ haxe: async () => await import('codemirror/mode/haxe/haxe'),
+ htmlembedded: async () => await import('codemirror/mode/htmlembedded/htmlembedded'),
+ htmlmixed: async () => await import('codemirror/mode/htmlmixed/htmlmixed'),
+ http: async () => await import('codemirror/mode/http/http'),
+ idl: async () => await import('codemirror/mode/idl/idl'),
+ javascript: async () => await import('codemirror/mode/javascript/javascript'),
+ jinja2: async () => await import('codemirror/mode/jinja2/jinja2'),
+ jsx: async () => await import('codemirror/mode/jsx/jsx'),
+ julia: async () => await import('codemirror/mode/julia/julia'),
+ livescript: async () => await import('codemirror/mode/livescript/livescript'),
+ lua: async () => await import('codemirror/mode/lua/lua'),
+ markdown: async () => await import('codemirror/mode/markdown/markdown'),
+ mathematica: async () => await import('codemirror/mode/mathematica/mathematica'),
+ mbox: async () => await import('codemirror/mode/mbox/mbox'),
+ mirc: async () => await import('codemirror/mode/mirc/mirc'),
+ mllike: async () => await import('codemirror/mode/mllike/mllike'),
+ modelica: async () => await import('codemirror/mode/modelica/modelica'),
+ mscgen: async () => await import('codemirror/mode/mscgen/mscgen'),
+ mumps: async () => await import('codemirror/mode/mumps/mumps'),
+ nginx: async () => await import('codemirror/mode/nginx/nginx'),
+ nsis: async () => await import('codemirror/mode/nsis/nsis'),
+ ntriples: async () => await import('codemirror/mode/ntriples/ntriples'),
+ octave: async () => await import('codemirror/mode/octave/octave'),
+ oz: async () => await import('codemirror/mode/oz/oz'),
+ pascal: async () => await import('codemirror/mode/pascal/pascal'),
+ pegjs: async () => await import('codemirror/mode/pegjs/pegjs'),
+ perl: async () => await import('codemirror/mode/perl/perl'),
+ php: async () => await import('codemirror/mode/php/php'),
+ pig: async () => await import('codemirror/mode/pig/pig'),
+ powershell: async () => await import('codemirror/mode/powershell/powershell'),
+ properties: async () => await import('codemirror/mode/properties/properties'),
+ protobuf: async () => await import('codemirror/mode/protobuf/protobuf'),
+ pug: async () => await import('codemirror/mode/pug/pug'),
+ puppet: async () => await import('codemirror/mode/puppet/puppet'),
+ python: async () => await import('codemirror/mode/python/python'),
+ q: async () => await import('codemirror/mode/q/q'),
+ r: async () => await import('codemirror/mode/r/r'),
+ rpm: async () => await import('codemirror/mode/rpm/rpm'),
+ rst: async () => await import('codemirror/mode/rst/rst'),
+ ruby: async () => await import('codemirror/mode/ruby/ruby'),
+ rust: async () => await import('codemirror/mode/rust/rust'),
+ sas: async () => await import('codemirror/mode/sas/sas'),
+ sass: async () => await import('codemirror/mode/sass/sass'),
+ scheme: async () => await import('codemirror/mode/scheme/scheme'),
+ shell: async () => await import('codemirror/mode/shell/shell'),
+ sieve: async () => await import('codemirror/mode/sieve/sieve'),
+ slim: async () => await import('codemirror/mode/slim/slim'),
+ smalltalk: async () => await import('codemirror/mode/smalltalk/smalltalk'),
+ smarty: async () => await import('codemirror/mode/smarty/smarty'),
+ solr: async () => await import('codemirror/mode/solr/solr'),
+ soy: async () => await import('codemirror/mode/soy/soy'),
+ sparql: async () => await import('codemirror/mode/sparql/sparql'),
+ spreadsheet: async () => await import('codemirror/mode/spreadsheet/spreadsheet'),
+ sql: async () => await import('codemirror/mode/sql/sql'),
+ stex: async () => await import('codemirror/mode/stex/stex'),
+ stylus: async () => await import('codemirror/mode/stylus/stylus'),
+ swift: async () => await import('codemirror/mode/swift/swift'),
+ tcl: async () => await import('codemirror/mode/tcl/tcl'),
+ textile: async () => await import('codemirror/mode/textile/textile'),
+ tiddlywiki: async () => await import('codemirror/mode/tiddlywiki/tiddlywiki'),
+ tiki: async () => await import('codemirror/mode/tiki/tiki'),
+ toml: async () => await import('codemirror/mode/toml/toml'),
+ tornado: async () => await import('codemirror/mode/tornado/tornado'),
+ troff: async () => await import('codemirror/mode/troff/troff'),
+ ttcn: async () => await import('codemirror/mode/ttcn/ttcn'),
+ turtle: async () => await import('codemirror/mode/turtle/turtle'),
+ twig: async () => await import('codemirror/mode/twig/twig'),
+ vb: async () => await import('codemirror/mode/vb/vb'),
+ vbscript: async () => await import('codemirror/mode/vbscript/vbscript'),
+ velocity: async () => await import('codemirror/mode/velocity/velocity'),
+ verilog: async () => await import('codemirror/mode/verilog/verilog'),
+ vhdl: async () => await import('codemirror/mode/vhdl/vhdl'),
+ vue: async () => await import('codemirror/mode/vue/vue'),
+ wast: async () => await import('codemirror/mode/wast/wast'),
+ webidl: async () => await import('codemirror/mode/webidl/webidl'),
+ xml: async () => await import('codemirror/mode/xml/xml'),
+ xquery: async () => await import('codemirror/mode/xquery/xquery'),
+ yacas: async () => await import('codemirror/mode/yacas/yacas'),
+ yaml: async () => await import('codemirror/mode/yaml/yaml'),
+ z80: async () => await import('codemirror/mode/z80/z80'),
+ };
+ return dynamicArray[str];
+}
diff --git a/web-vue/packages/core/components/CollapseForm/index.ts b/web-vue/packages/core/components/CollapseForm/index.ts
new file mode 100644
index 00000000..56b25504
--- /dev/null
+++ b/web-vue/packages/core/components/CollapseForm/index.ts
@@ -0,0 +1,3 @@
+import CollapseForm from './src/CollapseForm.vue';
+
+export { CollapseForm };
diff --git a/web-vue/packages/core/components/CollapseForm/src/CollapseForm.vue b/web-vue/packages/core/components/CollapseForm/src/CollapseForm.vue
new file mode 100644
index 00000000..ac41846c
--- /dev/null
+++ b/web-vue/packages/core/components/CollapseForm/src/CollapseForm.vue
@@ -0,0 +1,155 @@
+
+
+
+
+
+
diff --git a/web-vue/packages/core/components/Container/index.ts b/web-vue/packages/core/components/Container/index.ts
new file mode 100644
index 00000000..1ac33c22
--- /dev/null
+++ b/web-vue/packages/core/components/Container/index.ts
@@ -0,0 +1,10 @@
+import { withInstall } from '@jeesite/core/utils';
+import collapseContainer from './src/collapse/CollapseContainer.vue';
+import scrollContainer from './src/ScrollContainer.vue';
+import lazyContainer from './src/LazyContainer.vue';
+
+export const CollapseContainer = withInstall(collapseContainer);
+export const ScrollContainer = withInstall(scrollContainer);
+export const LazyContainer = withInstall(lazyContainer);
+
+export * from './src/typing';
diff --git a/web-vue/packages/core/components/Container/src/LazyContainer.vue b/web-vue/packages/core/components/Container/src/LazyContainer.vue
new file mode 100644
index 00000000..63e7d5e6
--- /dev/null
+++ b/web-vue/packages/core/components/Container/src/LazyContainer.vue
@@ -0,0 +1,138 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/web-vue/packages/core/components/Container/src/ScrollContainer.vue b/web-vue/packages/core/components/Container/src/ScrollContainer.vue
new file mode 100644
index 00000000..1f78f822
--- /dev/null
+++ b/web-vue/packages/core/components/Container/src/ScrollContainer.vue
@@ -0,0 +1,85 @@
+
+
+
+
+
+
+
diff --git a/web-vue/packages/core/components/Container/src/collapse/CollapseContainer.vue b/web-vue/packages/core/components/Container/src/collapse/CollapseContainer.vue
new file mode 100644
index 00000000..0b1476da
--- /dev/null
+++ b/web-vue/packages/core/components/Container/src/collapse/CollapseContainer.vue
@@ -0,0 +1,122 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/web-vue/packages/core/components/Container/src/collapse/CollapseHeader.vue b/web-vue/packages/core/components/Container/src/collapse/CollapseHeader.vue
new file mode 100644
index 00000000..92a39273
--- /dev/null
+++ b/web-vue/packages/core/components/Container/src/collapse/CollapseHeader.vue
@@ -0,0 +1,38 @@
+
+
+
+
+ {{ title }}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/web-vue/packages/core/components/Container/src/typing.ts b/web-vue/packages/core/components/Container/src/typing.ts
new file mode 100644
index 00000000..2f2643a3
--- /dev/null
+++ b/web-vue/packages/core/components/Container/src/typing.ts
@@ -0,0 +1,18 @@
+export type ScrollType = 'default' | 'main';
+
+export interface CollapseContainerOptions {
+ canExpand?: boolean;
+ expand?: boolean;
+ title?: string;
+ helpMessage?: Array | string;
+}
+export interface ScrollContainerOptions {
+ enableScroll?: boolean;
+ type?: ScrollType;
+}
+
+export type ScrollActionType = RefType<{
+ scrollBottom: () => void;
+ getScrollWrap: () => Nullable;
+ scrollTo: (top: number) => void;
+}>;
diff --git a/web-vue/packages/core/components/ContextMenu/index.ts b/web-vue/packages/core/components/ContextMenu/index.ts
new file mode 100644
index 00000000..ed294d7b
--- /dev/null
+++ b/web-vue/packages/core/components/ContextMenu/index.ts
@@ -0,0 +1,3 @@
+export { createContextMenu, destroyContextMenu } from './src/createContextMenu';
+
+export * from './src/typing';
diff --git a/web-vue/packages/core/components/ContextMenu/src/ContextMenu.vue b/web-vue/packages/core/components/ContextMenu/src/ContextMenu.vue
new file mode 100644
index 00000000..886d8a6f
--- /dev/null
+++ b/web-vue/packages/core/components/ContextMenu/src/ContextMenu.vue
@@ -0,0 +1,199 @@
+
+
diff --git a/web-vue/packages/core/components/ContextMenu/src/createContextMenu.ts b/web-vue/packages/core/components/ContextMenu/src/createContextMenu.ts
new file mode 100644
index 00000000..43545609
--- /dev/null
+++ b/web-vue/packages/core/components/ContextMenu/src/createContextMenu.ts
@@ -0,0 +1,77 @@
+import contextMenuVue from './ContextMenu.vue';
+import { isClient } from '@jeesite/core/utils/is';
+import { CreateContextOptions, ContextMenuProps } from './typing';
+import { createVNode, render } from 'vue';
+
+const menuManager: {
+ domList: Element[];
+ resolve: Fn;
+} = {
+ domList: [],
+ resolve: () => {},
+};
+
+export const createContextMenu = function (options: CreateContextOptions) {
+ const { event } = options || {};
+
+ event && event?.preventDefault();
+
+ if (!isClient) {
+ return;
+ }
+ return new Promise((resolve) => {
+ const body = document.body;
+
+ const container = document.createElement('div');
+ const propsData: Partial = {};
+ if (options.styles) {
+ propsData.styles = options.styles;
+ }
+
+ if (options.items) {
+ propsData.items = options.items;
+ }
+
+ if (options.event) {
+ propsData.customEvent = event;
+ propsData.axis = { x: event.clientX, y: event.clientY };
+ }
+
+ const vm = createVNode(contextMenuVue, propsData);
+ render(vm, container);
+
+ const handleClick = function () {
+ menuManager.resolve('');
+ };
+
+ menuManager.domList.push(container);
+
+ const remove = function () {
+ menuManager.domList.forEach((dom: Element) => {
+ try {
+ dom && body.removeChild(dom);
+ } catch (error) {
+ /* empty */
+ }
+ });
+ body.removeEventListener('click', handleClick);
+ body.removeEventListener('scroll', handleClick);
+ };
+
+ menuManager.resolve = function (arg) {
+ remove();
+ resolve(arg);
+ };
+ remove();
+ body.appendChild(container);
+ body.addEventListener('click', handleClick);
+ body.addEventListener('scroll', handleClick);
+ });
+};
+
+export const destroyContextMenu = function () {
+ if (menuManager) {
+ menuManager.resolve('');
+ menuManager.domList = [];
+ }
+};
diff --git a/web-vue/packages/core/components/ContextMenu/src/typing.ts b/web-vue/packages/core/components/ContextMenu/src/typing.ts
new file mode 100644
index 00000000..899d36b2
--- /dev/null
+++ b/web-vue/packages/core/components/ContextMenu/src/typing.ts
@@ -0,0 +1,35 @@
+export interface Axis {
+ x: number;
+ y: number;
+}
+
+export interface ContextMenuItem {
+ label: string;
+ icon?: string;
+ disabled?: boolean;
+ handler?: Fn;
+ divider?: boolean;
+ children?: ContextMenuItem[];
+}
+export interface CreateContextOptions {
+ event: MouseEvent;
+ icon?: string;
+ styles?: any;
+ items?: ContextMenuItem[];
+}
+
+export interface ContextMenuProps {
+ event?: MouseEvent;
+ styles?: any;
+ items: ContextMenuItem[];
+ customEvent?: MouseEvent;
+ axis?: Axis;
+ width?: number;
+ showIcon?: boolean;
+}
+
+export interface ItemContentProps {
+ showIcon: boolean | undefined;
+ item: ContextMenuItem;
+ handler: Fn;
+}
diff --git a/web-vue/packages/core/components/CountDown/index.ts b/web-vue/packages/core/components/CountDown/index.ts
new file mode 100644
index 00000000..3ef29325
--- /dev/null
+++ b/web-vue/packages/core/components/CountDown/index.ts
@@ -0,0 +1,6 @@
+import { withInstall } from '@jeesite/core/utils';
+import countButton from './src/CountButton.vue';
+import countdownInput from './src/CountdownInput.vue';
+
+export const CountdownInput = withInstall(countdownInput);
+export const CountButton = withInstall(countButton);
diff --git a/web-vue/packages/core/components/CountDown/src/CountButton.vue b/web-vue/packages/core/components/CountDown/src/CountButton.vue
new file mode 100644
index 00000000..ba082573
--- /dev/null
+++ b/web-vue/packages/core/components/CountDown/src/CountButton.vue
@@ -0,0 +1,62 @@
+
+
+ {{ getButtonText }}
+
+
+
diff --git a/web-vue/packages/core/components/CountDown/src/CountdownInput.vue b/web-vue/packages/core/components/CountDown/src/CountdownInput.vue
new file mode 100644
index 00000000..828d651f
--- /dev/null
+++ b/web-vue/packages/core/components/CountDown/src/CountdownInput.vue
@@ -0,0 +1,58 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/web-vue/packages/core/components/CountDown/src/useCountdown.ts b/web-vue/packages/core/components/CountDown/src/useCountdown.ts
new file mode 100644
index 00000000..316d69a8
--- /dev/null
+++ b/web-vue/packages/core/components/CountDown/src/useCountdown.ts
@@ -0,0 +1,51 @@
+import { ref, unref } from 'vue';
+import { tryOnUnmounted } from '@vueuse/core';
+
+export function useCountdown(count: number) {
+ const currentCount = ref(count);
+
+ const isStart = ref(false);
+
+ let timerId: ReturnType | null;
+
+ function clear() {
+ timerId && window.clearInterval(timerId);
+ }
+
+ function stop() {
+ isStart.value = false;
+ clear();
+ timerId = null;
+ }
+
+ function start() {
+ if (unref(isStart) || !!timerId) {
+ return;
+ }
+ isStart.value = true;
+ timerId = setInterval(() => {
+ if (unref(currentCount) === 1) {
+ stop();
+ currentCount.value = count;
+ } else {
+ currentCount.value -= 1;
+ }
+ }, 1000);
+ }
+
+ function reset() {
+ currentCount.value = count;
+ stop();
+ }
+
+ function restart() {
+ reset();
+ start();
+ }
+
+ tryOnUnmounted(() => {
+ reset();
+ });
+
+ return { start, reset, restart, clear, stop, currentCount, isStart };
+}
diff --git a/web-vue/packages/core/components/CountTo/index.ts b/web-vue/packages/core/components/CountTo/index.ts
new file mode 100644
index 00000000..409e2eb4
--- /dev/null
+++ b/web-vue/packages/core/components/CountTo/index.ts
@@ -0,0 +1,4 @@
+import { withInstall } from '@jeesite/core/utils';
+import countTo from './src/CountTo.vue';
+
+export const CountTo = withInstall(countTo);
diff --git a/web-vue/packages/core/components/CountTo/src/CountTo.vue b/web-vue/packages/core/components/CountTo/src/CountTo.vue
new file mode 100644
index 00000000..f83642ca
--- /dev/null
+++ b/web-vue/packages/core/components/CountTo/src/CountTo.vue
@@ -0,0 +1,110 @@
+
+
+ {{ value }}
+
+
+
diff --git a/web-vue/packages/core/components/Cropper/index.ts b/web-vue/packages/core/components/Cropper/index.ts
new file mode 100644
index 00000000..bdc5f685
--- /dev/null
+++ b/web-vue/packages/core/components/Cropper/index.ts
@@ -0,0 +1,7 @@
+import { withInstall } from '@jeesite/core/utils';
+import cropperImage from './src/Cropper.vue';
+import avatarCropper from './src/CropperAvatar.vue';
+
+export * from './src/typing';
+export const CropperImage = withInstall(cropperImage);
+export const CropperAvatar = withInstall(avatarCropper);
diff --git a/web-vue/packages/core/components/Cropper/src/CopperModal.vue b/web-vue/packages/core/components/Cropper/src/CopperModal.vue
new file mode 100644
index 00000000..d4b6c790
--- /dev/null
+++ b/web-vue/packages/core/components/Cropper/src/CopperModal.vue
@@ -0,0 +1,273 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/web-vue/packages/core/components/Cropper/src/Cropper.vue b/web-vue/packages/core/components/Cropper/src/Cropper.vue
new file mode 100644
index 00000000..0893e279
--- /dev/null
+++ b/web-vue/packages/core/components/Cropper/src/Cropper.vue
@@ -0,0 +1,184 @@
+
+
+
+
+
+
+
diff --git a/web-vue/packages/core/components/Cropper/src/CropperAvatar.vue b/web-vue/packages/core/components/Cropper/src/CropperAvatar.vue
new file mode 100644
index 00000000..413b89f7
--- /dev/null
+++ b/web-vue/packages/core/components/Cropper/src/CropperAvatar.vue
@@ -0,0 +1,145 @@
+
+
+
+
+
+
+
+
+
+ {{ btnText ? btnText : t('component.cropper.selectImage') }}
+
+
+
+
+
+
+
+
diff --git a/web-vue/packages/core/components/Cropper/src/typing.ts b/web-vue/packages/core/components/Cropper/src/typing.ts
new file mode 100644
index 00000000..e76cc6f8
--- /dev/null
+++ b/web-vue/packages/core/components/Cropper/src/typing.ts
@@ -0,0 +1,8 @@
+import type Cropper from 'cropperjs';
+
+export interface CropendResult {
+ imgBase64: string;
+ imgInfo: Cropper.Data;
+}
+
+export type { Cropper };
diff --git a/web-vue/packages/core/components/Description/index.ts b/web-vue/packages/core/components/Description/index.ts
new file mode 100644
index 00000000..9eeda36d
--- /dev/null
+++ b/web-vue/packages/core/components/Description/index.ts
@@ -0,0 +1,6 @@
+import { withInstall } from '@jeesite/core/utils';
+import description from './src/Description.vue';
+
+export * from './src/typing';
+export { useDescription } from './src/useDescription';
+export const Description = withInstall(description);
diff --git a/web-vue/packages/core/components/Description/src/Description.vue b/web-vue/packages/core/components/Description/src/Description.vue
new file mode 100644
index 00000000..ea224f99
--- /dev/null
+++ b/web-vue/packages/core/components/Description/src/Description.vue
@@ -0,0 +1,183 @@
+
diff --git a/web-vue/packages/core/components/Description/src/typing.ts b/web-vue/packages/core/components/Description/src/typing.ts
new file mode 100644
index 00000000..22e06b5d
--- /dev/null
+++ b/web-vue/packages/core/components/Description/src/typing.ts
@@ -0,0 +1,47 @@
+import type { VNode, CSSProperties } from 'vue';
+import type { CollapseContainerOptions } from '@jeesite/core/components/Container';
+import type { DescriptionsProps } from 'ant-design-vue/es/descriptions';
+
+export interface DescItem {
+ labelMinWidth?: number;
+ contentMinWidth?: number;
+ labelStyle?: CSSProperties;
+ field: string;
+ label: string | VNode | JSX.Element;
+ // Merge column
+ span?: number;
+ show?: (...arg: any) => boolean;
+ // render
+ render?: (val: any, data: Recordable) => VNode | undefined | JSX.Element | Element | string | number;
+}
+
+export interface DescriptionProps extends DescriptionsProps {
+ // Whether to include the collapse component
+ useCollapse?: boolean;
+ /**
+ * item configuration
+ * @type DescItem
+ */
+ schema: DescItem[];
+ /**
+ * 数据
+ * @type object
+ */
+ data: Recordable;
+ /**
+ * Built-in CollapseContainer component configuration
+ * @type CollapseContainerOptions
+ */
+ collapseOptions?: CollapseContainerOptions;
+}
+
+export interface DescInstance {
+ setDescProps(descProps: Partial): void;
+}
+
+export type Register = (descInstance: DescInstance) => void;
+
+/**
+ * @description:
+ */
+export type UseDescReturnType = [Register, DescInstance];
diff --git a/web-vue/packages/core/components/Description/src/useDescription.ts b/web-vue/packages/core/components/Description/src/useDescription.ts
new file mode 100644
index 00000000..4ba175be
--- /dev/null
+++ b/web-vue/packages/core/components/Description/src/useDescription.ts
@@ -0,0 +1,28 @@
+import type { DescriptionProps, DescInstance, UseDescReturnType } from './typing';
+import { ref, getCurrentInstance, unref } from 'vue';
+import { isProdMode } from '@jeesite/core/utils/env';
+
+export function useDescription(props?: Partial): UseDescReturnType {
+ if (!getCurrentInstance()) {
+ throw new Error('useDescription() can only be used inside setup() or functional components!');
+ }
+ const desc = ref>(null);
+ const loaded = ref(false);
+
+ function register(instance: DescInstance) {
+ if (unref(loaded) && isProdMode()) {
+ return;
+ }
+ desc.value = instance;
+ props && instance.setDescProps(props);
+ loaded.value = true;
+ }
+
+ const methods: DescInstance = {
+ setDescProps: (descProps: Partial): void => {
+ unref(desc)?.setDescProps(descProps);
+ },
+ };
+
+ return [register, methods];
+}
diff --git a/web-vue/packages/core/components/Dialog/index.ts b/web-vue/packages/core/components/Dialog/index.ts
new file mode 100644
index 00000000..dc2a646d
--- /dev/null
+++ b/web-vue/packages/core/components/Dialog/index.ts
@@ -0,0 +1,5 @@
+import { withInstall } from '@jeesite/core/utils';
+import basicDialog from './src/BasicDialog.vue';
+
+export const BasicDialog = withInstall(basicDialog);
+export type BasicDialogInstance = InstanceType;
diff --git a/web-vue/packages/core/components/Dialog/src/BasicDialog.vue b/web-vue/packages/core/components/Dialog/src/BasicDialog.vue
new file mode 100644
index 00000000..210e078d
--- /dev/null
+++ b/web-vue/packages/core/components/Dialog/src/BasicDialog.vue
@@ -0,0 +1,67 @@
+
+
+
+
+
+
+
+
diff --git a/web-vue/packages/core/components/Dict/index.ts b/web-vue/packages/core/components/Dict/index.ts
new file mode 100644
index 00000000..179e92b7
--- /dev/null
+++ b/web-vue/packages/core/components/Dict/index.ts
@@ -0,0 +1,5 @@
+import { withInstall } from '@jeesite/core/utils';
+import dictLabel from './src/DictLabel.vue';
+
+export * from './src/useDict';
+export const DictLabel = withInstall(dictLabel);
diff --git a/web-vue/packages/core/components/Dict/src/DictLabel.vue b/web-vue/packages/core/components/Dict/src/DictLabel.vue
new file mode 100644
index 00000000..0e6ff1c5
--- /dev/null
+++ b/web-vue/packages/core/components/Dict/src/DictLabel.vue
@@ -0,0 +1,87 @@
+
+
+
+
+
+
+
+ {{ item.name }}
+
+
+
+
+
+
+
+
+
+ {{ item.name }}
+
+
+
+
+
+
+ {{ props.defaultValue || t('未知') }}
+
+
+
+
+
diff --git a/web-vue/packages/core/components/Dict/src/useDict.ts b/web-vue/packages/core/components/Dict/src/useDict.ts
new file mode 100644
index 00000000..4548e5ec
--- /dev/null
+++ b/web-vue/packages/core/components/Dict/src/useDict.ts
@@ -0,0 +1,81 @@
+/**
+ * Copyright (c) 2013-Now http://jeesite.com All rights reserved.
+ * No deletion without permission, or be held responsible to law.
+ * @author ThinkGem
+ */
+import { Ref } from 'vue';
+import { isEmpty } from '@jeesite/core/utils/is';
+import { useI18n } from '@jeesite/core/hooks/web/useI18n';
+import { dictDataTreeData } from '@jeesite/core/api/sys/dictData';
+import { useUserStore } from '@jeesite/core/store/modules/user';
+import { listToTree } from '@jeesite/core/utils/helper/treeHelper';
+
+const { t } = useI18n();
+const userStore = useUserStore();
+
+export function useDict() {
+ const dictListMap = userStore.getPageCacheByKey('dictListMap', {});
+
+ async function initDict(dictTypes: string[] | Set = []) {
+ if (!dictTypes) return;
+ for (const dictType of dictTypes) {
+ if (!dictListMap[dictType]) {
+ // await new Promise((resolve) => setTimeout(resolve, 1000));
+ dictListMap[dictType] = await dictDataTreeData({ dictType: dictType });
+ }
+ }
+ }
+
+ function getDictList(dictType: string): Recordable[] {
+ return dictListMap[dictType] || [];
+ }
+
+ function getDictLabel(dictType: string, value?: string, defaultValue = t('未知')): string {
+ const result: string[] = [];
+ for (const item of getDictList(dictType)) {
+ if (item && (',' + value + ',').includes(',' + item.value + ',')) {
+ result.push(item.name);
+ }
+ }
+ return result.length > 0 ? result.join(',') : defaultValue;
+ }
+
+ async function initGetDictList(dictType: string): Promise {
+ if (isEmpty(dictType)) return [];
+ await initDict([dictType]);
+ return dictListMap[dictType] || [];
+ }
+
+ async function initSelectOptions(optionsRef: Ref, dictType?: string) {
+ if (isEmpty(dictType)) return;
+ await initDict([dictType]);
+ const jeesiteDictList = getDictList(dictType);
+ optionsRef.value = jeesiteDictList
+ .filter((item) => item.pId == '0')
+ .map((item) => ({
+ label: item.name,
+ value: item.value,
+ key: item.id,
+ }));
+ }
+
+ async function initSelectTreeData(treeData: Ref, dictType: string, isListToTree: boolean) {
+ if (isEmpty(dictType)) return;
+ await initDict([dictType]);
+ const jeesiteDictList = getDictList(dictType);
+ if (isListToTree) {
+ treeData.value = listToTree(jeesiteDictList);
+ } else {
+ treeData.value = jeesiteDictList;
+ }
+ }
+
+ return {
+ initDict,
+ getDictList,
+ getDictLabel,
+ initGetDictList,
+ initSelectOptions,
+ initSelectTreeData,
+ };
+}
diff --git a/web-vue/packages/core/components/Drawer/index.ts b/web-vue/packages/core/components/Drawer/index.ts
new file mode 100644
index 00000000..549d4d35
--- /dev/null
+++ b/web-vue/packages/core/components/Drawer/index.ts
@@ -0,0 +1,8 @@
+import { withInstall } from '@jeesite/core/utils';
+import basicDrawer from './src/BasicDrawer.vue';
+
+export const BasicDrawer = withInstall(basicDrawer);
+export type BasicDrawerInstance = InstanceType;
+
+export * from './src/typing';
+export { useDrawer, useDrawerInner } from './src/useDrawer';
diff --git a/web-vue/packages/core/components/Drawer/src/BasicDrawer.vue b/web-vue/packages/core/components/Drawer/src/BasicDrawer.vue
new file mode 100644
index 00000000..6a4f105a
--- /dev/null
+++ b/web-vue/packages/core/components/Drawer/src/BasicDrawer.vue
@@ -0,0 +1,349 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/web-vue/packages/core/components/Drawer/src/components/DrawerFooter.vue b/web-vue/packages/core/components/Drawer/src/components/DrawerFooter.vue
new file mode 100644
index 00000000..fb52c8f2
--- /dev/null
+++ b/web-vue/packages/core/components/Drawer/src/components/DrawerFooter.vue
@@ -0,0 +1,95 @@
+
+
+
+
+
+
+ {{ cancelText || (getOkAuth && showOkBtn ? t('common.cancelText') : t('common.closeText')) }}
+
+
+
+
+ {{ okText || t('common.okText') }}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/web-vue/packages/core/components/Drawer/src/components/DrawerHeader.vue b/web-vue/packages/core/components/Drawer/src/components/DrawerHeader.vue
new file mode 100644
index 00000000..e90009b9
--- /dev/null
+++ b/web-vue/packages/core/components/Drawer/src/components/DrawerHeader.vue
@@ -0,0 +1,74 @@
+
+
+
+ {{ !$slots.title ? title : '' }}
+
+
+
+
+
+
+
+ {{ title }}
+
+
+
+
+
+
+
+
+
+
diff --git a/web-vue/packages/core/components/Drawer/src/props.ts b/web-vue/packages/core/components/Drawer/src/props.ts
new file mode 100644
index 00000000..a2a4e137
--- /dev/null
+++ b/web-vue/packages/core/components/Drawer/src/props.ts
@@ -0,0 +1,50 @@
+import type { PropType } from 'vue';
+
+export const footerProps = {
+ confirmLoading: { type: Boolean },
+ /**
+ * @description: Show close button
+ */
+ showCancelBtn: { type: Boolean, default: true },
+ cancelButtonProps: Object as PropType,
+ cancelText: { type: String },
+ /**
+ * @description: Show confirmation button
+ */
+ showOkBtn: { type: Boolean, default: true },
+ okButtonProps: Object as PropType,
+ okText: { type: String },
+ okType: { type: String, default: 'primary' },
+ okAuth: { type: String },
+ showFooter: { type: Boolean },
+ footerHeight: {
+ type: [String, Number] as PropType,
+ default: 60,
+ },
+};
+
+export const basicProps = {
+ isDetail: { type: Boolean },
+ title: { type: String, default: '' },
+ loadingText: { type: String },
+ showDetailBack: { type: Boolean, default: true },
+ open: { type: Boolean },
+ loading: { type: Boolean },
+ maskClosable: { type: Boolean, default: true },
+ getContainer: {
+ type: [Object, String] as PropType,
+ },
+ closeFunc: {
+ type: [Function, Object] as PropType,
+ default: null,
+ },
+ destroyOnClose: { type: Boolean },
+ wrapClassName: { type: String },
+ // 是否允许拖拽调整抽屉宽度
+ widthResize: { type: Boolean, default: true },
+ ...footerProps,
+ // eslint check
+ width: { type: [Number, String] },
+ mask: { type: Boolean, default: true },
+ maskStyle: { type: Object },
+};
diff --git a/web-vue/packages/core/components/Drawer/src/typing.ts b/web-vue/packages/core/components/Drawer/src/typing.ts
new file mode 100644
index 00000000..ddd73634
--- /dev/null
+++ b/web-vue/packages/core/components/Drawer/src/typing.ts
@@ -0,0 +1,198 @@
+import type { ButtonProps } from 'ant-design-vue/lib/button/buttonTypes';
+import type { CSSProperties, VNodeChild, ComputedRef } from 'vue';
+import type { ScrollContainerOptions } from '@jeesite/core/components/Container';
+
+export interface DrawerInstance {
+ getDrawerProps: () => Partial;
+ setDrawerProps: (props: Partial) => void;
+ emitOpen?: (open: boolean, uid: number) => void;
+}
+
+export interface ReturnMethods extends DrawerInstance {
+ openDrawer: (open?: boolean, data?: T, openOnSet?: boolean) => void;
+ closeDrawer: () => void;
+ getOpen?: ComputedRef;
+ setDrawerData: (data: any) => void;
+}
+
+export type RegisterFn = (drawerInstance: DrawerInstance, uuid: number) => void;
+
+export interface ReturnInnerMethods extends DrawerInstance {
+ closeDrawer: () => void;
+ changeLoading: (loading: boolean) => void;
+ changeOkLoading: (loading: boolean) => void;
+ getOpen?: ComputedRef;
+}
+
+export type UseDrawerReturnType = [RegisterFn, ReturnMethods];
+
+export type UseDrawerInnerReturnType = [RegisterFn, ReturnInnerMethods];
+
+export interface DrawerFooterProps {
+ showOkBtn: boolean;
+ showCancelBtn: boolean;
+ /**
+ * Text of the Cancel button
+ * @default 'cancel'
+ * @type string
+ */
+ cancelText: string;
+ /**
+ * Text of the OK button
+ * @default 'OK'
+ * @type string
+ */
+ okText: string;
+
+ /**
+ * Button type of the OK button
+ * @default 'primary'
+ * @type string
+ */
+ okType: 'primary' | 'danger' | 'dashed' | 'ghost' | 'default';
+ okAuth: string;
+ /**
+ * The ok button props, follow jsx rules
+ * @type object
+ */
+ okButtonProps: { props: ButtonProps; on: any };
+
+ /**
+ * The cancel button props, follow jsx rules
+ * @type object
+ */
+ cancelButtonProps: { props: ButtonProps; on: any };
+ /**
+ * Whether to apply loading visual effect for OK button or not
+ * @default false
+ * @type boolean
+ */
+ confirmLoading: boolean;
+
+ showFooter: boolean;
+ footerHeight: string | number;
+}
+export interface DrawerProps extends DrawerFooterProps {
+ isDetail?: boolean;
+ loading?: boolean;
+ showDetailBack?: boolean;
+ open?: boolean;
+ /**
+ * Built-in ScrollContainer component configuration
+ * @type ScrollContainerOptions
+ */
+ scrollOptions?: ScrollContainerOptions;
+ closeFunc?: () => Promise;
+ triggerWindowResize?: boolean;
+ /**
+ * Whether a close (x) button is open on top right of the Drawer dialog or not.
+ * @default true
+ * @type boolean
+ */
+ closable?: boolean;
+
+ /**
+ * Whether to unmount child components on closing drawer or not.
+ * @default false
+ * @type boolean
+ */
+ destroyOnClose?: boolean;
+
+ /**
+ * Return the mounted node for Drawer.
+ * @default 'body'
+ * @type any ( HTMLElement| () => HTMLElement | string)
+ */
+ getContainer?: string | false | HTMLElement | (() => HTMLElement);
+
+ /**
+ * Whether to show mask or not.
+ * @default true
+ * @type boolean
+ */
+ mask?: boolean;
+
+ /**
+ * Clicking on the mask (area outside the Drawer) to close the Drawer or not.
+ * @default true
+ * @type boolean
+ */
+ maskClosable?: boolean;
+
+ /**
+ * Style for Drawer's mask element.
+ * @default {}
+ * @type object
+ */
+ maskStyle?: CSSProperties;
+
+ /**
+ * The title for Drawer.
+ * @type any (string | slot)
+ */
+ title?: VNodeChild | JSX.Element | any;
+
+ /**
+ * The class name of the container of the Drawer dialog.
+ * @type string
+ */
+ //wrapClassName?: string;
+ class?: string;
+
+ /**
+ * Style of wrapper element which **contains mask** compare to `drawerStyle`
+ * @type object
+ */
+ wrapStyle?: CSSProperties;
+
+ /**
+ * Style of the popup layer element
+ * @type object
+ */
+ drawerStyle?: CSSProperties;
+
+ /**
+ * Style of floating layer, typically used for adjusting its position.
+ * @type object
+ */
+ bodyStyle?: CSSProperties;
+ headerStyle?: CSSProperties;
+
+ /**
+ * Width of the Drawer dialog.
+ * @default 256
+ * @type string | number
+ */
+ width?: string | number;
+
+ /**
+ * placement is top or bottom, height of the Drawer dialog.
+ * @type string | number
+ */
+ height?: string | number;
+
+ /**
+ * The z-index of the Drawer.
+ * @default 1000
+ * @type number
+ */
+ zIndex?: number;
+
+ /**
+ * The placement of the Drawer.
+ * @default 'right'
+ * @type string
+ */
+ placement?: 'top' | 'right' | 'bottom' | 'left';
+ afterOpenChange?: (open?: boolean) => void;
+ keyboard?: boolean;
+ /**
+ * Specify a callback that will be called when a user clicks mask, close button or Cancel button.
+ */
+ onClose?: (e?: Event) => void;
+}
+export interface DrawerActionType {
+ scrollBottom: () => void;
+ scrollTo: (to: number) => void;
+ getScrollWrap: () => Element | null;
+}
diff --git a/web-vue/packages/core/components/Drawer/src/useDrawer.ts b/web-vue/packages/core/components/Drawer/src/useDrawer.ts
new file mode 100644
index 00000000..3d9ae261
--- /dev/null
+++ b/web-vue/packages/core/components/Drawer/src/useDrawer.ts
@@ -0,0 +1,170 @@
+import type {
+ UseDrawerReturnType,
+ DrawerInstance,
+ ReturnMethods,
+ DrawerProps,
+ UseDrawerInnerReturnType,
+} from './typing';
+
+import { ref, onUnmounted, unref, getCurrentInstance, reactive, watchEffect, nextTick, toRaw, computed } from 'vue';
+import { isProdMode } from '@jeesite/core/utils/env';
+import { isFunction } from '@jeesite/core/utils/is';
+import { isEqual } from 'lodash-es';
+import { tryOnUnmounted } from '@vueuse/core';
+import { error } from '@jeesite/core/utils/log';
+
+const dataTransfer = reactive({});
+
+const openData = reactive<{ [key: number]: boolean }>({});
+
+/**
+ * @description: Applicable to separate drawer and call outside
+ */
+export function useDrawer(): UseDrawerReturnType {
+ const drawer = ref(null);
+ const loaded = ref>(false);
+ const uid = ref(0);
+
+ function register(drawerInstance: DrawerInstance, uuid: number) {
+ if (!getCurrentInstance()) {
+ throw new Error('useDrawer() can only be used inside setup() or functional components!');
+ }
+ uid.value = uuid;
+ isProdMode() &&
+ onUnmounted(() => {
+ drawer.value = null;
+ loaded.value = false;
+ dataTransfer[unref(uid)] = null;
+ });
+ if (unref(loaded) && isProdMode() && drawerInstance === unref(drawer)) return;
+
+ drawer.value = drawerInstance;
+ loaded.value = true;
+
+ drawerInstance.emitOpen = (open: boolean, uid: number) => {
+ openData[uid] = open;
+ };
+ }
+
+ const getInstance = () => {
+ const instance = unref(drawer);
+ if (!instance) {
+ error('useDrawer instance is undefined!');
+ }
+ return instance;
+ };
+
+ const methods: ReturnMethods = {
+ getDrawerProps: (): Partial => {
+ return getInstance()?.getDrawerProps() || {};
+ },
+
+ setDrawerProps: (props: Partial): void => {
+ getInstance()?.setDrawerProps(props);
+ },
+
+ getOpen: computed((): boolean => {
+ return openData[~~unref(uid)];
+ }),
+
+ openDrawer: (open = true, data?: T, openOnSet = true): void => {
+ getInstance()?.setDrawerProps({
+ open: open,
+ });
+
+ if (!data) return;
+ const id = unref(uid);
+ if (openOnSet) {
+ dataTransfer[id] = null;
+ dataTransfer[id] = toRaw(data);
+ return;
+ }
+ const equal = isEqual(toRaw(dataTransfer[id]), toRaw(data));
+ if (!equal) {
+ dataTransfer[id] = toRaw(data);
+ }
+ },
+
+ closeDrawer: () => {
+ getInstance()?.setDrawerProps({ open: false });
+ },
+
+ setDrawerData: (data: any) => {
+ if (!data) return;
+ nextTick(() => {
+ setTimeout(() => {
+ const id = unref(uid);
+ dataTransfer[id] = null;
+ dataTransfer[id] = toRaw(data);
+ return;
+ }, 100);
+ });
+ },
+ };
+
+ return [register, methods];
+}
+
+export const useDrawerInner = (callbackFn?: Fn): UseDrawerInnerReturnType => {
+ const drawerInstanceRef = ref>(null);
+ const currentInstance = getCurrentInstance();
+ const uidRef = ref(0);
+
+ const getInstance = () => {
+ const instance = unref(drawerInstanceRef);
+ if (!instance) {
+ error('useDrawerInner instance is undefined!');
+ return;
+ }
+ return instance;
+ };
+
+ const register = (modalInstance: DrawerInstance, uuid: number) => {
+ isProdMode() &&
+ tryOnUnmounted(() => {
+ drawerInstanceRef.value = null;
+ });
+
+ uidRef.value = uuid;
+ drawerInstanceRef.value = modalInstance;
+ currentInstance?.emit('register', modalInstance, uuid);
+ };
+
+ watchEffect(() => {
+ const data = dataTransfer[unref(uidRef)];
+ if (!data) return;
+ if (!callbackFn || !isFunction(callbackFn)) return;
+ nextTick(() => {
+ callbackFn(data);
+ });
+ });
+
+ return [
+ register,
+ {
+ changeLoading: (loading = true) => {
+ getInstance()?.setDrawerProps({ loading });
+ },
+
+ changeOkLoading: (loading = true) => {
+ getInstance()?.setDrawerProps({ confirmLoading: loading });
+ },
+
+ getOpen: computed((): boolean => {
+ return openData[~~unref(uidRef)];
+ }),
+
+ closeDrawer: () => {
+ getInstance()?.setDrawerProps({ open: false });
+ },
+
+ getDrawerProps: (): Partial => {
+ return getInstance()?.getDrawerProps() || {};
+ },
+
+ setDrawerProps: (props: Partial) => {
+ getInstance()?.setDrawerProps(props);
+ },
+ },
+ ];
+};
diff --git a/web-vue/packages/core/components/Dropdown/index.ts b/web-vue/packages/core/components/Dropdown/index.ts
new file mode 100644
index 00000000..4c7379e8
--- /dev/null
+++ b/web-vue/packages/core/components/Dropdown/index.ts
@@ -0,0 +1,5 @@
+import { withInstall } from '@jeesite/core/utils';
+import dropdown from './src/Dropdown.vue';
+
+export * from './src/typing';
+export const Dropdown = withInstall(dropdown);
diff --git a/web-vue/packages/core/components/Dropdown/src/Dropdown.vue b/web-vue/packages/core/components/Dropdown/src/Dropdown.vue
new file mode 100644
index 00000000..486e4d6e
--- /dev/null
+++ b/web-vue/packages/core/components/Dropdown/src/Dropdown.vue
@@ -0,0 +1,87 @@
+
+