新增前端vue
28
web-vue/.editorconfig
Normal file
@@ -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
|
||||
22
web-vue/.gitee/ISSUE_TEMPLATE.zh-CN.md
Normal file
@@ -0,0 +1,22 @@
|
||||
### 是什么问题、该问题是怎么引起的?
|
||||
|
||||
1.
|
||||
|
||||
### 重现步骤、期望结果、截图、代码
|
||||
|
||||
1.
|
||||
```
|
||||
这里贴你的代码块
|
||||
```
|
||||
|
||||
### 实际结果、报错信息、截图
|
||||
|
||||
1.
|
||||
```
|
||||
这里贴错误信息
|
||||
```
|
||||
|
||||
### 环境版本:
|
||||
|
||||
- 浏览器版本:Chrome xx、Firefox xx、其它
|
||||
- 平台版本:JeeSite 4.x.x、5.x.x(package.json里查看)
|
||||
36
web-vue/.gitignore
vendored
Normal file
@@ -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
|
||||
5
web-vue/.npmrc
Normal file
@@ -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
|
||||
14
web-vue/.prettierignore
Normal file
@@ -0,0 +1,14 @@
|
||||
dist
|
||||
public
|
||||
node_modules
|
||||
|
||||
.local
|
||||
.npmrc
|
||||
.output.js
|
||||
|
||||
*.sh
|
||||
*.md
|
||||
*.svg
|
||||
*.html
|
||||
*.json
|
||||
*-lock.yaml
|
||||
32
web-vue/.prettierrc.mjs
Normal file
@@ -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',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
6
web-vue/.stylelintignore
Normal file
@@ -0,0 +1,6 @@
|
||||
dist
|
||||
public
|
||||
node_modules
|
||||
|
||||
*.sh
|
||||
*.md
|
||||
223
web-vue/LICENSE
Normal file
@@ -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
|
||||
819
web-vue/README.md
Normal file
@@ -0,0 +1,819 @@
|
||||
|
||||
|
||||
<p align="center">
|
||||
<img alt="JeeSite" src="https://jeesite.com/assets/images/logo.png" width="120" height="120" style="margin-bottom: 10px;">
|
||||
</p>
|
||||
<h3 align="center" style="margin:30px 0 30px;font-weight:bold;font-size:30px;">
|
||||
JeeSite Vue3 前端源码<br>
|
||||
使用 Turborepo、Monorepo、pnpm<br>
|
||||
快速构建、模块化、代码复用、高效管理
|
||||
</h3>
|
||||
<p align="center">
|
||||
<a href="https://v3.cn.vuejs.org/" target="__blank"><img alt="TypeScript-Vue3" src="https://img.shields.io/badge/TypeScript-Vue3-green.svg"></a>
|
||||
<a href="https://www.antdv.com/" target="__blank"><img alt="Ant Design Vue-V4.x" src="https://img.shields.io/badge/Ant Design Vue-V4.x-brightgreen.svg"></a>
|
||||
<a href="https://jeesite.com" target="__blank"><img alt="JeeSite-V5.x" src="https://img.shields.io/badge/JeeSite-V5.x-blue.svg"></a>
|
||||
<a href="https://gitee.com/thinkgem/jeesite5" target="__blank"><img alt="star" src="https://gitee.com/thinkgem/jeesite5/badge/star.svg?theme=dark"></a>
|
||||
<a href="https://gitee.com/thinkgem/jeesite-vue" target="__blank"><img alt="star" src="https://gitee.com/thinkgem/jeesite-vue/badge/star.svg?theme=dark"></a>
|
||||
<a href="https://gitee.com/thinkgem/jeesite" target="__blank"><img alt="star" src="https://gitee.com/thinkgem/jeesite/badge/star.svg?theme=gvp"></a>
|
||||
<a href="https://gitcode.com/thinkgem/jeesite" target="__blank"><img alt="star" src="https://gitcode.com/thinkgem/jeesite/star/badge.svg"></a>
|
||||
</p>
|
||||
|
||||
------
|
||||
|
||||
<div align="center">
|
||||
如果你喜欢 JeeSite,请给她一个 ⭐️ Star,您的支持将是我们前行的动力。
|
||||
</div>
|
||||
|
||||
------
|
||||
|
||||
1. 单仓多包 pnpm + Turborepo 涡轮增压,提升编译速度,方便统一管理脚本任务
|
||||
2. 按功能模块进行拆分为不同的包,方便进行团队开发源码管理,可根据需要进行发包
|
||||
3. 模块之间松耦合,单依赖,公共模块,公共组件,公共工具,方便代码复用
|
||||
4. 可方便从传统架构版本,升级到 Monorepo 模块化、分包架构
|
||||
|
||||
## 技术交流
|
||||
|
||||
* 官方网站:<https://jeesite.com>
|
||||
* 使用文档:<https://jeesite.com/docs>
|
||||
* 问题反馈:[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)
|
||||
* 联系我们:<http://s.jeesite.com>
|
||||
* 关注微信公众号,了解最新动态:
|
||||
|
||||
<p style="padding-left:40px">
|
||||
<img alt="JeeSite微信公众号" src="https://jeesite.com/assets/images/mp.png" width="200">
|
||||
</p>
|
||||
|
||||
* QQ 群:`127515876`、`209330483`、`223507718`、`709534275`、`730390092`、`1373527`、`183903863(外包)`
|
||||
* 微信群:如果二维码过期,请尝试点击图片并F5刷新,或者添加客服微信 jeesitex 邀请您进群
|
||||
|
||||
<p style="padding-left:40px"><a href="https://jeesite.com/assets/images/wxg_cur.png" target="_blank">
|
||||
<img alt="JeeSite微信群" src="https://jeesite.com/assets/images/wxg_cur.png" width="200"/></a>
|
||||
</p>
|
||||
|
||||
* 后端源码仓库地址:
|
||||
[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 开源协议,永久免费使用。
|
||||
|
||||
### 架构特点及安全方面的优势:<https://jeesite.com/docs/feature/>
|
||||
|
||||
## Vue 前端简介
|
||||
|
||||
基于 Vue3、Vite、Ant-Design-Vue、TypeScript 和 Vue Vben Admin 等前沿技术栈构建,本软件采用最先进的技术架构,
|
||||
帮助初学者快速上手并融入团队开发。内置组织机构、角色用户、菜单授权、数据权限、系统参数等核心模块,结合强大的组件封装
|
||||
与数据驱动视图设计,为微小、中大型项目提供开箱即用的解决方案和丰富的示例,助力高效开发。
|
||||
|
||||
用户界面专为信息化管理后台量身打造,在界面设计上精益求精,每一处细节都彰显着精致,为用户带来优雅且直观的操作体验。
|
||||
它提供多样化的菜单布局、智能的页签管理、高效的树表操作体验、强大的表格组件、灵活的表单组件设计,具备强大的扩展能力,
|
||||
同时支持黑暗布局风格,为用户提供高效、灵活且美观的操作体验,满足各类管理后台的复杂需求。
|
||||
|
||||
另外我们还支持 Turborepo + Monorepo 快速构建、模块化、代码复用、支持分包开发
|
||||
详见:<https://gitee.com/thinkgem/jeesite-vue/tree/monorepo/>
|
||||
|
||||
## 前端技术特点
|
||||
|
||||
定义众多组件,非常贴心的组件属性及小功能,符合 JeeSite 以往的设计思想,列表和表单以数据驱动视图,
|
||||
极大简化了业务功能开发, 注释分解详见【[源码解析](https://jeesite.com/docs/vue-crud-view)】
|
||||
|
||||
为什么做数据驱动视图?前端向下兼容一直是最大的问题,有了一套相应的标准,会对框架升级帮助很大。
|
||||
比如你可以非常小的成本,业务代码改动非常小的情况下,去升级前端;数据驱动视图可以为未来自定义拖拽表单做更好的铺垫,
|
||||
数据存储结构更清晰化,更利于维护。
|
||||
|
||||
提示:请仔细阅读源码解析,表单视图和列表视图上的注释哦,支持复杂表单以及多表单联合使用。
|
||||
|
||||
## 演示地址
|
||||
|
||||
1. 地址:<http://vue.jeesite.com/>
|
||||
|
||||
## 学习准备
|
||||
|
||||
- [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+,下载地址:<https://nodejs.org>
|
||||
|
||||
```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。
|
||||
|
||||
详见文档:<https://jeesite.com/docs/vue-install-deploy/#部署到正式服务器>
|
||||
|
||||
### 后端服务
|
||||
|
||||
- 安装后台服务 [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。
|
||||
* 更多文档详见:<https://jeesite.com/docs/vue-faq/#常见问题>
|
||||
|
||||
## 软件截图
|
||||
|
||||
<img src="https://oscimg.oschina.net/oscnet/up-db83c334daab05d89a0930d8497816da6a4.png"/>
|
||||
<img src="https://oscimg.oschina.net/oscnet/up-685134ad2a721e0a7818efe4201d476e332.png"/>
|
||||
<img src="https://oscimg.oschina.net/oscnet/up-3ac1f31bdb399431cd0f6af479acc7f2e58.png"/>
|
||||
<img src="https://oscimg.oschina.net/oscnet/up-6a4c84696b589ba2a3bd034c2b9026b40e1.png"/>
|
||||
<img src="https://oscimg.oschina.net/oscnet/up-b85f44704e4a8c1e3a50f1c10e7e413341a.png"/>
|
||||
<img src="https://oscimg.oschina.net/oscnet/up-f03d07d58b351e1f2fc9931ea2dba550429.png"/>
|
||||
<img src="https://oscimg.oschina.net/oscnet/up-a36a005290cb95bc625e0933c867edc7e6f.png"/>
|
||||
|
||||
## 附录
|
||||
|
||||
### 表单视图
|
||||
|
||||
```html
|
||||
<template>
|
||||
<!-- 弹出抽屉组件,如果想改为弹窗,Drawer 换为 Modal 即可快速替换 -->
|
||||
<BasicDrawer
|
||||
v-bind="$attrs" -- 传递来自父组件的属性
|
||||
:showFooter="true" -- 显示弹窗底部按钮组
|
||||
:okAuth="'test:testData:edit'" -- 提交按钮权限,控制按钮是否显示
|
||||
@register="registerDrawer" -- 弹窗后的回调方法
|
||||
@ok="handleSubmit" -- 提交按钮调用方法
|
||||
width="60%" -- 弹窗宽度,支持按比例
|
||||
>
|
||||
<!-- 弹窗标题 -->
|
||||
<template #title>
|
||||
<Icon :icon="getTitle.icon" class="pr-1 m-1" /> -- 图标
|
||||
<span> {{ getTitle.value }} </span> -- 标题名称
|
||||
</template>
|
||||
<!-- 表单组件 -->
|
||||
<BasicForm @register="registerForm">
|
||||
<!-- 定义表单控件插槽、个性化表单控件,如:这是一个表单子表插槽 -->
|
||||
<template #testDataChildList>
|
||||
<BasicTable
|
||||
@register="registerTestDataChildTable"
|
||||
@row-click="handleTestDataChildRowClick"
|
||||
/>
|
||||
<!-- 子表新增按钮 -->
|
||||
<a-button class="mt-2" @click="handleTestDataChildAdd">
|
||||
<Icon icon="i-ant-design:plus-circle-outlined" /> {{ t('新增') }}
|
||||
</a-button>
|
||||
</template>
|
||||
</BasicForm>
|
||||
</BasicDrawer>
|
||||
</template>
|
||||
<!-- script name: 当前组件名称(与路由名一致,如果不一致会页面缓存失效)-->
|
||||
<script lang="ts" setup name="ViewsTestTestDataForm">
|
||||
|
||||
// 导入当前用到的对象,部分省略
|
||||
import { ref, unref, computed } from 'vue';
|
||||
import { officeTreeData } from '/@/api/sys/office';
|
||||
|
||||
// 页面事件定义
|
||||
const emit = defineEmits(['success', 'register']);
|
||||
|
||||
// 国际化方法调用,参数是国际化编码的根路径
|
||||
const { t } = useI18n('test.testData');
|
||||
|
||||
// 消息弹窗方法
|
||||
const { showMessage } = useMessage();
|
||||
|
||||
// 路由meta信息
|
||||
const { meta } = unref(router.currentRoute);
|
||||
|
||||
// 当前页面数据记录
|
||||
const record = ref<Recordable>({});
|
||||
|
||||
// 当前页面标题定义,来自菜单管理定义
|
||||
const getTitle = computed(() => ({
|
||||
icon: meta.icon || 'ant-design:book-outlined',
|
||||
value: record.value.isNewRecord ? t('新增数据') : t('编辑数据'),
|
||||
}));
|
||||
|
||||
// 输入表单控件定义
|
||||
const inputFormSchemas: FormSchema[] = [
|
||||
{
|
||||
label: t('单行文本'), // 控件前面的页签
|
||||
field: 'testInput', // 字段提交参数名
|
||||
component: 'Input', // 控件类型(可自定义,更多查看 componentMap.ts )
|
||||
componentProps: { // 组件属性定义
|
||||
maxlength: 200,
|
||||
},
|
||||
required: true, // 表单验证,是否必填(快速定义)
|
||||
rules: [ // 如果不只是必填,需要通过 rules 定义,举例:
|
||||
{ required: true },
|
||||
{ min: 4, max: 20, message: t('请输入长度在 4 到 20 个字符之间') },
|
||||
{ pattern: /^[\u0391-\uFFE5\w]+$/, message: t('不能输入特殊字符') },
|
||||
{
|
||||
validator(_rule, value) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!value || value === '') return resolve();
|
||||
// 远程验证,访问后台校验数据是否重复
|
||||
checkTestInput(record.value.testInput || '', value)
|
||||
.then((res) => (res ? resolve() : reject(t('数据已存在'))))
|
||||
.catch((err) => reject(err.message || t('验证失败')));
|
||||
});
|
||||
},
|
||||
trigger: 'blur', // 如果是远程验证,可以减少请求频率
|
||||
},
|
||||
],
|
||||
colProps: { lg: 24, md: 24 }, // 栅格布局(遵循 Ant Design 风格)
|
||||
},
|
||||
{
|
||||
label: t('下拉框'),
|
||||
field: 'testSelect',
|
||||
component: 'Select', // 选择框还有 RadioGroup、CheckboxGroup
|
||||
componentProps: {
|
||||
dictType: 'sys_menu_type', // 下拉框选项数据(支持直接指定字典类型)
|
||||
allowClear: true, // 启用空选项,可清空选择
|
||||
mode: 'multiple', // 下拉框模块,启用多选
|
||||
},
|
||||
},
|
||||
{
|
||||
label: t('日期选择'),
|
||||
field: 'testDate',
|
||||
component: 'DatePicker',
|
||||
componentProps: {
|
||||
format: 'YYYY-MM-DD', // 日期选择
|
||||
showTime: false, // 关闭时间选择
|
||||
},
|
||||
},
|
||||
{
|
||||
label: t('日期时间'),
|
||||
field: 'testDatetime',
|
||||
component: 'DatePicker',
|
||||
componentProps: {
|
||||
format: 'YYYY-MM-DD HH:mm', // 日期时间选择
|
||||
showTime: { format: 'HH:mm' }, // 设置时间的格式
|
||||
},
|
||||
},
|
||||
{
|
||||
label: t('用户选择'),
|
||||
field: 'testUser.userCode',
|
||||
fieldLabel: 'testUser.userName', //【支持返回,如下拉框或树选择的节点名】
|
||||
component: 'TreeSelect', // 树选择控件
|
||||
componentProps: {
|
||||
api: officeTreeData, // 数据源 API 定义,支持 ztree 格式
|
||||
params: { isLoadUser: true, userIdPrefix: '' }, // API 参数
|
||||
canSelectParent: false, // 是否允许选择父级
|
||||
allowClear: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
label: t('子表数据'),
|
||||
field: 'testDataChildList',
|
||||
component: 'Input',
|
||||
colProps: { lg: 24, md: 24 },
|
||||
slot: 'testDataChildList', // 指定插槽、个性化控件内容
|
||||
},
|
||||
];
|
||||
|
||||
// 当前表单的参数定义
|
||||
const [registerForm, { resetFields, setFieldsValue, validate }] = useForm({
|
||||
labelWidth: 120, // 控件前面的标签宽度
|
||||
schemas: inputFormSchemas, // 控件定义列表
|
||||
baseColProps: { lg: 12, md: 24 }, // 控件默认栅格布局方式(响应式)
|
||||
});
|
||||
|
||||
// 当前表单子表格定义
|
||||
const [registerTestDataChildTable, testDataChildTable] = useTable({
|
||||
actionColumn: { // 子表的操作列定义
|
||||
width: 60, // 操作列宽度
|
||||
actions: (record: Recordable) => [
|
||||
{
|
||||
icon: 'i-ant-design:delete-outlined',
|
||||
color: 'error',
|
||||
popConfirm: { // 是否需要启用确认框
|
||||
title: '是否确认删除',
|
||||
confirm: handleTestDataChildDelete.bind(this, record),
|
||||
},
|
||||
auth: 'sys:empUser:edit', // 按钮权限(可控制按钮是否显示)
|
||||
},
|
||||
],
|
||||
},
|
||||
rowKey: 'id', // 子表主键名
|
||||
pagination: false,// 关闭分页
|
||||
bordered: true, // 开启表格边框
|
||||
size: 'small', // 单元格间距
|
||||
inset: true, // 是否内嵌(去除一些边距)
|
||||
});
|
||||
|
||||
// 当前表单子表自动定义
|
||||
async function setTestDataChildTableData(_res: Recordable) {
|
||||
testDataChildTable.setColumns([
|
||||
{
|
||||
title: t('单行文本'),
|
||||
dataIndex: 'testInput',
|
||||
width: 230,
|
||||
align: 'left',
|
||||
editRow: true, // 是否启用编辑
|
||||
editComponent: 'Input', // 编辑控件(可自定义,更多查看 componentMap.ts )
|
||||
editRule: true, // 控件验证(是否必填)
|
||||
},
|
||||
{
|
||||
title: t('下拉框'),
|
||||
dataIndex: 'testSelect',
|
||||
width: 130,
|
||||
align: 'left',
|
||||
dictType: 'sys_menu_type', // 指定字典类型,自动显示字典标签
|
||||
editRow: true,
|
||||
editComponent: 'Select',
|
||||
editComponentProps: { // 控件属性
|
||||
dictType: 'sys_menu_type', // 下拉框的字段类型
|
||||
allowClear: true,
|
||||
},
|
||||
editRule: false,
|
||||
},
|
||||
// 更多组件控件不举例了,同表单控件 ...
|
||||
]);
|
||||
// 设定子表数据
|
||||
testDataChildTable.setTableData(record.value.testDataChildList || []);
|
||||
}
|
||||
|
||||
// 点击行,启用编辑
|
||||
function handleTestDataChildRowClick(record: Recordable) {
|
||||
record.onEdit?.(true, false);
|
||||
}
|
||||
|
||||
// 添加编辑行,可指定初始数据
|
||||
function handleTestDataChildAdd() {
|
||||
testDataChildTable.insertTableDataRecord({
|
||||
id: new Date().getTime(),
|
||||
isNewRecord: true,
|
||||
editable: true,
|
||||
});
|
||||
}
|
||||
|
||||
// 删除编辑行方法
|
||||
function handleTestDataChildDelete(record: Recordable) {
|
||||
testDataChildTable.deleteTableDataRecord(record);
|
||||
}
|
||||
|
||||
// 获取子表数据(支持返回删除未提交的数据)
|
||||
async function getTestDataChildList() {
|
||||
let testDataChildListValid = true;
|
||||
let testDataChildList: Recordable[] = [];
|
||||
for (const record of testDataChildTable.getDataSource()) {
|
||||
// 验证控件内容,并取消行的编辑状态(如果验证失败返回false)
|
||||
if (!(await record.onEdit?.(false, true))) {
|
||||
testDataChildListValid = false;
|
||||
}
|
||||
testDataChildList.push({
|
||||
...record,
|
||||
id: !!record.isNewRecord ? '' : record.id,
|
||||
});
|
||||
}
|
||||
for (const record of testDataChildTable.getDelDataSource()) {
|
||||
if (!!record.isNewRecord) continue;
|
||||
testDataChildList.push({
|
||||
...record,
|
||||
status: '1',
|
||||
});
|
||||
}
|
||||
// 子表验证事件,抛出异常消息
|
||||
if (!testDataChildListValid) {
|
||||
throw { errorFields: [{ name: ['testDataChildList'] }] };
|
||||
}
|
||||
return testDataChildList;
|
||||
}
|
||||
|
||||
// 弹窗后的回调事件,进行一些表单数据初始化等操作
|
||||
const [registerDrawer, { setDrawerProps, closeDrawer }] = useDrawerInner(async (data) => {
|
||||
resetFields(); // 重置表单数据
|
||||
setDrawerProps({ loading: true }); // 显示加载框
|
||||
const res = await testDataForm(data); // 查询表单数据
|
||||
record.value = (res.testData || {}) as Recordable;
|
||||
setFieldsValue(record.value); // 设置字段值
|
||||
setTestDataChildTableData(res); // 设置子表数据(没有子表可不写)
|
||||
setDrawerProps({ loading: false }); // 隐藏加载框
|
||||
});
|
||||
|
||||
// 表单提交按钮方法
|
||||
async function handleSubmit() {
|
||||
try {
|
||||
const data = await validate(); // 验证表单,并返回数据
|
||||
setDrawerProps({ confirmLoading: true }); // 显示提交加载中
|
||||
// 设置提交的参数(QueryString,后台 Controller 的 get 接受)
|
||||
const params: any = {
|
||||
isNewRecord: record.value.isNewRecord,
|
||||
id: record.value.id,
|
||||
};
|
||||
// 获取并设置子表数据
|
||||
data.testDataChildList = await getTestDataChildList();
|
||||
// console.log('submit', params, data, record);
|
||||
// 将数据提交给后台(如果失败跳转到 catch)
|
||||
const res = await testDataSave(params, data);
|
||||
showMessage(res.message); // 显示提交结果
|
||||
setTimeout(closeDrawer); // 隐藏抽屉弹窗
|
||||
emit('success', data); // 触发事件,列表数据刷新
|
||||
} catch (error: any) {
|
||||
if (error && error.errorFields) {
|
||||
showMessage(t('您填写的信息有误,请根据提示修正。'));
|
||||
}
|
||||
console.log('error', error);
|
||||
} finally {
|
||||
setDrawerProps({ confirmLoading: false }); // 隐藏提交加载中
|
||||
}
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
### 列表视图
|
||||
|
||||
```html
|
||||
<template>
|
||||
<div>
|
||||
<!-- 表格组件 -->
|
||||
<BasicTable @register="registerTable">
|
||||
<!-- 表格标题插槽 -->
|
||||
<template #tableTitle>
|
||||
<Icon :icon="getTitle.icon" class="m-1 pr-1" />
|
||||
<span> {{ getTitle.value }} </span>
|
||||
</template>
|
||||
<!-- 表格右侧按钮插槽,其中 v-auth 是按钮权限控制 -->
|
||||
<template #toolbar>
|
||||
<a-button type="primary" @click="handleForm({})" v-auth="'test:testData:edit'">
|
||||
<Icon icon="i-fluent:add-12-filled" /> {{ t('新增') }}
|
||||
</a-button>
|
||||
</template>
|
||||
<!-- 首列插槽 -->
|
||||
<template #firstColumn="{ record }">
|
||||
<a @click="handleForm({ id: record.id })">
|
||||
{{ record.testInput }}
|
||||
</a>
|
||||
</template>
|
||||
</BasicTable>
|
||||
<!-- 点击表格行进入的输入表单弹窗 -->
|
||||
<InputForm @register="registerDrawer" @success="handleSuccess" />
|
||||
</div>
|
||||
</template>
|
||||
<!-- script name: 当前组件名称(与路由名一致,如果不一致会页面缓存失效)-->
|
||||
<script lang="ts" setup name="ViewsTestTestDataList">
|
||||
|
||||
// 导入当前用到的对象,部分省略
|
||||
import InputForm from './form.vue';
|
||||
|
||||
// 国际化方法调用,参数是国际化编码的根路径
|
||||
const { t } = useI18n('test.testData');
|
||||
|
||||
// 消息弹窗方法
|
||||
const { showMessage } = useMessage();
|
||||
|
||||
// 路由meta信息
|
||||
const { meta } = unref(router.currentRoute);
|
||||
|
||||
// 当前页面标题定义,来自菜单管理定义
|
||||
const getTitle = {
|
||||
icon: meta.icon || 'ant-design:book-outlined',
|
||||
value: meta.title || t('数据管理'),
|
||||
};
|
||||
|
||||
// 表格搜索表单控件定义
|
||||
const searchForm: FormProps = {
|
||||
baseColProps: { lg: 6, md: 8 }, // 表单栅格布局
|
||||
labelWidth: 90, // 表单标签宽度
|
||||
schemas: [
|
||||
{
|
||||
label: t('单行文本'), // 表单标签
|
||||
field: 'testInput', // 字段提交参数名
|
||||
component: 'Input', // 表单控件
|
||||
},
|
||||
{
|
||||
label: t('下拉框'),
|
||||
field: 'testSelect',
|
||||
component: 'Select', // 选择框还有 RadioGroup、CheckboxGroup
|
||||
componentProps: {
|
||||
dictType: 'sys_menu_type', // 下拉框选项数据(支持直接指定字典类型)
|
||||
allowClear: true, // 启用空选项,可清空选择
|
||||
mode: 'multiple', // 下拉框模块,启用多选
|
||||
},
|
||||
},
|
||||
// 更多控件,再次不展示了,和上一节表单视图一致
|
||||
],
|
||||
};
|
||||
|
||||
// 表格列定义
|
||||
const tableColumns: BasicColumn[] = [
|
||||
{
|
||||
title: t('单行文本'), // 表头标题
|
||||
dataIndex: 'testInput', // 表列实体属性名
|
||||
key: 'a.test_input', // 排序数据库字段名
|
||||
sorter: true, // 点击表头是否可排序
|
||||
width: 230, // 列宽
|
||||
align: 'left', // 列的对齐方式
|
||||
// 个性化列,可定义插槽(如样式,增加控件等)
|
||||
slot: 'firstColumn',
|
||||
},
|
||||
{
|
||||
title: t('下拉框'),
|
||||
dataIndex: 'testSelect',
|
||||
key: 'a.test_select',
|
||||
sorter: true,
|
||||
width: 130,
|
||||
align: 'center',
|
||||
dictType: 'sys_menu_type', // 字典列,快速显示字典标签
|
||||
},
|
||||
];
|
||||
|
||||
// 表格操作列定义
|
||||
const actionColumn: BasicColumn = {
|
||||
width: 160, // 操作列宽
|
||||
actions: (record: Recordable) => [
|
||||
{
|
||||
icon: 'i-clarity:note-edit-line',
|
||||
title: t('编辑数据'),
|
||||
onClick: handleForm.bind(this, { id: record.id }),
|
||||
// 按钮权限控制,指定权限字符串
|
||||
auth: 'test:testData:edit',
|
||||
},
|
||||
{
|
||||
icon: 'i-ant-design:stop-outlined',
|
||||
color: 'error',
|
||||
title: t('停用数据'),
|
||||
// 是否需要启用确认框
|
||||
popConfirm: {
|
||||
title: t('是否确认停用数据'),
|
||||
confirm: handleDisable.bind(this, { id: record.id }),
|
||||
},
|
||||
// 按钮权限控制,指定权限字符串
|
||||
auth: 'test:testData:edit',
|
||||
// 控制按钮是否显示(区别:show 是显示或隐藏;ifShow 是显示或移除)
|
||||
show: () => record.status === '0',
|
||||
ifShow: () => record.status === '0',
|
||||
},
|
||||
],
|
||||
// 操作列更多按钮定义
|
||||
dropDownActions: (record: Recordable) => [
|
||||
{
|
||||
icon: 'i-ant-design:reload-outlined',
|
||||
label: t('重置密码'),
|
||||
onClick: handleResetpwd.bind(this, { userCode: record.userCode }),
|
||||
auth: 'sys:empUser:resetpwd',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
// 点击首列或编辑按钮是的抽屉弹窗定义
|
||||
const [registerDrawer, { openDrawer }] = useDrawer();
|
||||
|
||||
// 表格定义
|
||||
const [registerTable, { reload }] = useTable({
|
||||
api: testDataListData, // 表格数据源 API
|
||||
beforeFetch: (params) => {
|
||||
return params; // API 提交之前的参数修改
|
||||
},
|
||||
columns: tableColumns, // 表格列
|
||||
actionColumn: actionColumn,// 操作列
|
||||
formConfig: searchForm, // 搜索表单
|
||||
showTableSetting: true, // 是否显示右上角的设置按钮
|
||||
useSearchForm: true, // 是否显示搜索表单
|
||||
canResize: true, // 是否自适应表单高度
|
||||
});
|
||||
|
||||
// 弹窗操作方法
|
||||
function handleForm(record: Recordable) {
|
||||
openDrawer(true, record);
|
||||
}
|
||||
|
||||
// 操作列停用按钮方法
|
||||
async function handleDisable(record: Recordable) {
|
||||
const res = await testDataDisable(record);
|
||||
showMessage(res.message);
|
||||
handleSuccess();
|
||||
}
|
||||
|
||||
// 刷新表格数据(含表单回调)
|
||||
function handleSuccess() {
|
||||
reload();
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
## 更多介绍
|
||||
|
||||
* 架构特点:<https://jeesite.com/docs/feature/>
|
||||
* 内置功能:<https://jeesite.com/docs/function/>
|
||||
* 目录结构:<https://jeesite.com/docs/catalog/>
|
||||
* 参数配置:<https://jeesite.com/docs/config/>
|
||||
* 开发规范:<https://jeesite.com/docs/standard/>
|
||||
* 数表设计:<https://jeesite.com/docs/treetable/>
|
||||
|
||||
## 学习文档
|
||||
|
||||
* 库表生成、代码生成:<https://jeesite.com/docs/code-gen/>
|
||||
* 菜单权限、按钮权限:<https://jeesite.com/docs/permi-shiro/>
|
||||
* 数据权限、库事务:<https://jeesite.com/docs/service-datascope/#数据权限>
|
||||
* 表结构、数据字典:<https://jeesite.com/docs/code-gen/#表结构数据字典>
|
||||
* 持久层框架、SQL:<https://jeesite.com/docs/dao-mybatis/>
|
||||
* 后端常用工具:<https://jeesite.com/docs/sys-utils/>
|
||||
|
||||
**分离版**
|
||||
|
||||
* 版本介绍:<https://jeesite.com/docs/jeesite-vue/>
|
||||
* 源码解析:<https://jeesite.com/docs/vue-crud-view/>
|
||||
* 表单组件:<https://jeesite.com/docs/vue-basic-form/>
|
||||
* 表格组件:<https://jeesite.com/docs/vue-basic-table/>
|
||||
* 参数配置:<https://jeesite.com/docs/vue-settings/>
|
||||
* 常用组件:<https://jeesite.com/docs/vue-comp/>
|
||||
* 前端权限:<https://jeesite.com/docs/vue-auth/>
|
||||
* 图标组件:<https://jeesite.com/docs/vue-icon/>
|
||||
* 前端样式库:<https://jeesite.com/docs/vue-style/>
|
||||
* 多语言国际化:<https://jeesite.com/docs/vue-i18n/>
|
||||
|
||||
**经典版**
|
||||
|
||||
* 表单组件:<https://jeesite.com/docs/views-beetl/>
|
||||
* 表格组件:<https://jeesite.com/docs/datagrid/>
|
||||
* 常用工具:<https://jeesite.com/docs/jeesite-js/>
|
||||
* 自定义主题:<https://jeesite.com/docs/custom-views/>
|
||||
|
||||
## 更多文档
|
||||
|
||||
* AI、CMS、RAG、Tool、MCP 人工智能助手:<https://jeesite.com/docs/ai-cms>
|
||||
* BPM 业务流程引擎(Flowable):<http://jeesite.com/docs/bpm/>
|
||||
* CMS 多站点内容管理模块:<https://jeesite.com/docs/cms/>
|
||||
|
||||
* 消息推送消息提醒:<https://jeesite.com/docs/msg-push-use/>
|
||||
* 对象存储模块:<https://jeesite.com/docs/oss-client>
|
||||
* 单点登录模块:<https://jeesite.com/docs/sso-cas>
|
||||
* 在线任务调度:<https://jeesite.com/docs/job/>
|
||||
|
||||
* 大屏设计器:<https://jeesite.com/docs/visual/>
|
||||
* 报表设计器:<https://jeesite.com/docs/ureport/>
|
||||
* 文件管理分享:<https://jeesite.com/docs/filemanager/>
|
||||
* 文件在线预览:<https://jeesite.com/docs/filepreview/>
|
||||
|
||||
* 集群、高可用架构:<https://jeesite.com/docs/cluster/>
|
||||
* SaaS 多租户架构:<https://jeesite.com/docs/saas-corp-use/>
|
||||
* 读写分离分片分表:<https://jeesite.com/docs/sharding/>
|
||||
* Spring监控系统:<https://jeesite.com/docs/webadmin/>
|
||||
* 分布式跨应用事务:<https://jeesite.com/docs/seata/>
|
||||
* 追踪系统集成:<https://jeesite.com/docs/skywalking/>
|
||||
* ELK 日志收集:<https://jeesite.com/docs/elk-log/>
|
||||
|
||||
* MybatisPlus: <https://gitee.com/thinkgem/jeesite-mybatisplus>
|
||||
* 接口快速开发:<https://gitee.com/thinkgem/jeesite-magic-api>
|
||||
* 内外网中间件:<https://my.oschina.net/thinkgem/blog/4624519>
|
||||
* 统一认证平台:<https://jeesite.com/docs/oauth2-server>
|
||||
|
||||
## 授权许可协议条款
|
||||
|
||||
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 帮助了您,请为我们点赞。支持我们,您可以获得更多回馈,我们会把公益事业做的更好,开放更多资源,回报社区和社会。请给我们一些动力吧,在此非常感谢已支持我们的朋友!
|
||||
* **联系我们**:请访问技术支持与服务页面:<http://s.jeesite.com>
|
||||
|
||||
## 专业版增加的功能
|
||||
|
||||
1. 主题标签页的三种风格自由切换
|
||||
2. 业务流程、流程设计、流程办理
|
||||
3. 文件管理、上传秒传、文件预览
|
||||
4. 高级折叠表单和个性化本地存储
|
||||
5. 表格个性化设置参数本地存储
|
||||
6. 租户管理功能、租户切换
|
||||
7. 动态设置页面字体大小
|
||||
8. 页签右键,在新窗口打开
|
||||
9. 消息推送、消息提醒
|
||||
10. 语言国际化、本地化
|
||||
11. 快速升级到 Monorepo 脚本
|
||||
12. 更多功能详见文档
|
||||
9
web-vue/bin/build.bat
Normal file
@@ -0,0 +1,9 @@
|
||||
@echo off
|
||||
%~d0
|
||||
cd %~dp0
|
||||
|
||||
cd..
|
||||
npm run build
|
||||
|
||||
cd bin
|
||||
pause
|
||||
5
web-vue/bin/build.sh
Normal file
@@ -0,0 +1,5 @@
|
||||
#!/bin/sh
|
||||
cd ..
|
||||
pnpm build
|
||||
|
||||
cd bin
|
||||
9
web-vue/bin/install.bat
Normal file
@@ -0,0 +1,9 @@
|
||||
@echo off
|
||||
%~d0
|
||||
cd %~dp0
|
||||
|
||||
cd..
|
||||
npm run install
|
||||
|
||||
cd bin
|
||||
pause
|
||||
5
web-vue/bin/install.sh
Normal file
@@ -0,0 +1,5 @@
|
||||
#!/bin/sh
|
||||
cd..
|
||||
pnpm install
|
||||
|
||||
cd bin
|
||||
9
web-vue/bin/startup.bat
Normal file
@@ -0,0 +1,9 @@
|
||||
@echo off
|
||||
%~d0
|
||||
cd %~dp0
|
||||
|
||||
cd..
|
||||
pnpm dev
|
||||
|
||||
cd bin
|
||||
pause
|
||||
5
web-vue/bin/startup.sh
Normal file
@@ -0,0 +1,5 @@
|
||||
#!/bin/sh
|
||||
cd ..
|
||||
pnpm dev
|
||||
|
||||
cd bin
|
||||
177
web-vue/eslint.config.mjs
Normal file
@@ -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,
|
||||
'<template>': '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',
|
||||
},
|
||||
},
|
||||
];
|
||||
119
web-vue/package.json
Normal file
@@ -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"
|
||||
}
|
||||
23
web-vue/packages/assets/README.md
Normal file
@@ -0,0 +1,23 @@
|
||||
|
||||
- 官方网站:<https://jeesite.com>
|
||||
- 使用文档:<https://jeesite.com/docs>
|
||||
- 后端代码:<https://gitee.com/thinkgem/jeesite5>
|
||||
- 前端代码:<https://gitee.com/thinkgem/jeesite-vue>
|
||||
|
||||
------
|
||||
|
||||
<div align="center">
|
||||
如果你喜欢 JeeSite,请给她一个 ⭐️ Star,您的支持将是我们前行的动力。
|
||||
</div>
|
||||
|
||||
------
|
||||
|
||||
- 问题反馈:<https://gitee.com/thinkgem/jeesite-vue/issues> [【新手必读】](https://gitee.com/thinkgem/jeesite5/issues/I18ARR)
|
||||
- 需求收集:<https://gitee.com/thinkgem/jeesite-vue/issues/new>
|
||||
- QQ 群:`127515876`、`209330483`、`223507718`、`709534275`、`730390092`、`1373527`、`183903863(外包)`
|
||||
- 微信群:添加客服微信 <http://s.jeesite.com> 邀请您进群
|
||||
- 关注微信公众号,了解最新动态:
|
||||
|
||||
<p style="padding-left:40px">
|
||||
<img alt="JeeSite微信公众号" src="https://jeesite.com/assets/images/mp.png" width="220" height="220">
|
||||
</p>
|
||||
16
web-vue/packages/assets/icons/moon.svg
Normal file
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 499.712 499.712" style="enable-background: new 0 0 499.712 499.712;" xml:space="preserve">
|
||||
<path style="fill: #FFD93B;" d="M146.88,375.528c126.272,0,228.624-102.368,228.624-228.64c0-55.952-20.16-107.136-53.52-146.88
|
||||
C425.056,33.096,499.696,129.64,499.696,243.704c0,141.392-114.608,256-256,256c-114.064,0-210.608-74.64-243.696-177.712
|
||||
C39.744,355.368,90.944,375.528,146.88,375.528z"/>
|
||||
<path style="fill: #F4C534;" d="M401.92,42.776c34.24,43.504,54.816,98.272,54.816,157.952c0,141.392-114.608,256-256,256
|
||||
c-59.68,0-114.448-20.576-157.952-54.816c46.848,59.472,119.344,97.792,200.928,97.792c141.392,0,256-114.608,256-256
|
||||
C499.712,162.12,461.392,89.64,401.92,42.776z"/>
|
||||
<g>
|
||||
<polygon style="fill: #FFD83B;" points="128.128,99.944 154.496,153.4 213.472,161.96 170.8,203.56 180.864,262.296
|
||||
128.128,234.568 75.376,262.296 85.44,203.56 42.768,161.96 101.744,153.4"/>
|
||||
<polygon style="fill: #FFD83B;" points="276.864,82.84 290.528,110.552 321.104,114.984 298.976,136.552 304.208,166.984
|
||||
276.864,152.616 249.52,166.984 254.752,136.552 232.624,114.984 263.2,110.552"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
42
web-vue/packages/assets/icons/sun.svg
Normal file
@@ -0,0 +1,42 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 60 60" style="enable-background: new 0 0 60 60;" xml:space="preserve">
|
||||
<g>
|
||||
<path style="fill: #F0C419;" d="M30,0c-0.552,0-1,0.448-1,1v6c0,0.552,0.448,1,1,1s1-0.448,1-1V1C31,0.448,30.552,0,30,0z"/>
|
||||
<path style="fill: #F0C419;" d="M30,52c-0.552,0-1,0.448-1,1v6c0,0.552,0.448,1,1,1s1-0.448,1-1v-6C31,52.448,30.552,52,30,52z"/>
|
||||
<path style="fill: #F0C419;" d="M59,29h-6c-0.552,0-1,0.448-1,1s0.448,1,1,1h6c0.552,0,1-0.448,1-1S59.552,29,59,29z"/>
|
||||
<path style="fill: #F0C419;" d="M8,30c0-0.552-0.448-1-1-1H1c-0.552,0-1,0.448-1,1s0.448,1,1,1h6C7.552,31,8,30.552,8,30z"/>
|
||||
<path style="fill: #F0C419;" d="M46.264,14.736c0.256,0,0.512-0.098,0.707-0.293l5.736-5.736c0.391-0.391,0.391-1.023,0-1.414
|
||||
s-1.023-0.391-1.414,0l-5.736,5.736c-0.391,0.391-0.391,1.023,0,1.414C45.752,14.639,46.008,14.736,46.264,14.736z"/>
|
||||
<path style="fill: #F0C419;" d="M13.029,45.557l-5.736,5.736c-0.391,0.391-0.391,1.023,0,1.414C7.488,52.902,7.744,53,8,53
|
||||
s0.512-0.098,0.707-0.293l5.736-5.736c0.391-0.391,0.391-1.023,0-1.414S13.42,45.166,13.029,45.557z"/>
|
||||
<path style="fill: #F0C419;" d="M46.971,45.557c-0.391-0.391-1.023-0.391-1.414,0s-0.391,1.023,0,1.414l5.736,5.736
|
||||
C51.488,52.902,51.744,53,52,53s0.512-0.098,0.707-0.293c0.391-0.391,0.391-1.023,0-1.414L46.971,45.557z"/>
|
||||
<path style="fill: #F0C419;" d="M8.707,7.293c-0.391-0.391-1.023-0.391-1.414,0s-0.391,1.023,0,1.414l5.736,5.736
|
||||
c0.195,0.195,0.451,0.293,0.707,0.293s0.512-0.098,0.707-0.293c0.391-0.391,0.391-1.023,0-1.414L8.707,7.293z"/>
|
||||
<path style="fill: #F0C419;" d="M50.251,21.404c0.162,0.381,0.532,0.61,0.921,0.61c0.13,0,0.263-0.026,0.39-0.08l2.762-1.172
|
||||
c0.508-0.216,0.746-0.803,0.53-1.311s-0.804-0.746-1.311-0.53l-2.762,1.172C50.272,20.309,50.035,20.896,50.251,21.404z"/>
|
||||
<path style="fill: #F0C419;" d="M9.749,38.596c-0.216-0.508-0.803-0.746-1.311-0.53l-2.762,1.172
|
||||
c-0.508,0.216-0.746,0.803-0.53,1.311c0.162,0.381,0.532,0.61,0.921,0.61c0.13,0,0.263-0.026,0.39-0.08l2.762-1.172
|
||||
C9.728,39.691,9.965,39.104,9.749,38.596z"/>
|
||||
<path style="fill: #F0C419;" d="M54.481,38.813L51.7,37.688c-0.511-0.207-1.095,0.041-1.302,0.553
|
||||
c-0.207,0.512,0.041,1.095,0.553,1.302l2.782,1.124c0.123,0.049,0.25,0.073,0.374,0.073c0.396,0,0.771-0.236,0.928-0.626
|
||||
C55.241,39.603,54.994,39.02,54.481,38.813z"/>
|
||||
<path style="fill: #F0C419;" d="M5.519,21.188L8.3,22.312c0.123,0.049,0.25,0.073,0.374,0.073c0.396,0,0.771-0.236,0.928-0.626
|
||||
c0.207-0.512-0.041-1.095-0.553-1.302l-2.782-1.124c-0.513-0.207-1.095,0.04-1.302,0.553C4.759,20.397,5.006,20.98,5.519,21.188z"
|
||||
/>
|
||||
<path style="fill: #F0C419;" d="M39.907,50.781c-0.216-0.508-0.803-0.745-1.311-0.53c-0.508,0.216-0.746,0.803-0.53,1.311
|
||||
l1.172,2.762c0.162,0.381,0.532,0.61,0.921,0.61c0.13,0,0.263-0.026,0.39-0.08c0.508-0.216,0.746-0.803,0.53-1.311L39.907,50.781z"
|
||||
/>
|
||||
<path style="fill: #F0C419;" d="M21.014,9.829c0.13,0,0.263-0.026,0.39-0.08c0.508-0.216,0.746-0.803,0.53-1.311l-1.172-2.762
|
||||
c-0.215-0.509-0.802-0.747-1.311-0.53c-0.508,0.216-0.746,0.803-0.53,1.311l1.172,2.762C20.254,9.6,20.625,9.829,21.014,9.829z"/>
|
||||
<path style="fill: #F0C419;" d="M21.759,50.398c-0.511-0.205-1.095,0.04-1.302,0.553l-1.124,2.782
|
||||
c-0.207,0.512,0.041,1.095,0.553,1.302c0.123,0.049,0.25,0.073,0.374,0.073c0.396,0,0.771-0.236,0.928-0.626l1.124-2.782
|
||||
C22.519,51.188,22.271,50.605,21.759,50.398z"/>
|
||||
<path style="fill: #F0C419;" d="M38.615,9.675c0.396,0,0.771-0.236,0.928-0.626l1.124-2.782c0.207-0.512-0.041-1.095-0.553-1.302
|
||||
c-0.511-0.207-1.095,0.041-1.302,0.553L37.688,8.3c-0.207,0.512,0.041,1.095,0.553,1.302C38.364,9.651,38.491,9.675,38.615,9.675z"
|
||||
/>
|
||||
</g>
|
||||
<circle style="fill: #F0C419;" cx="30" cy="30" r="20"/>
|
||||
<circle style="fill: #EDE21B;" cx="30" cy="30" r="15"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.7 KiB |
BIN
web-vue/packages/assets/images/header.jpg
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
web-vue/packages/assets/images/logo.png
Normal file
|
After Width: | Height: | Size: 7.2 KiB |
BIN
web-vue/packages/assets/images/mp.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
23
web-vue/packages/assets/package.json
Normal file
@@ -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"
|
||||
}
|
||||
}
|
||||
1
web-vue/packages/assets/svg/illustration.svg
Normal file
|
After Width: | Height: | Size: 54 KiB |
19
web-vue/packages/assets/svg/login-bg-dark.svg
Normal file
@@ -0,0 +1,19 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="6395" height="1080" viewBox="0 0 6395 1080">
|
||||
<defs>
|
||||
<clipPath id="clip-path">
|
||||
<rect id="Rectangle_73" data-name="Rectangle 73" width="6395" height="1079" transform="translate(-5391)" fill="#fff"/>
|
||||
</clipPath>
|
||||
<linearGradient id="linear-gradient" x1="0.631" y1="0.5" x2="0.958" y2="0.488" gradientUnits="objectBoundingBox">
|
||||
<stop offset="0" stop-color="#2e364a"/>
|
||||
<stop offset="1" stop-color="#2c344a"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<g id="Web_1920_1" data-name="Web 1920 – 1" clip-path="url(#clip-Web_1920_1)">
|
||||
<g id="Mask_Group_1" data-name="Mask Group 1" transform="translate(5391)" clip-path="url(#clip-path)">
|
||||
<g id="Group_118" data-name="Group 118" transform="translate(-419.333 -1.126)">
|
||||
<path id="Path_142" data-name="Path 142" d="M6271.734-6.176s-222.478,187.809-55.349,583.254c44.957,106.375,81.514,205.964,84.521,277,8.164,192.764-156.046,268.564-156.046,268.564l-653.53-26.8L5475.065-21.625Z" transform="translate(-4876.383)" fill="#2d3750"/>
|
||||
<path id="Union_6" data-name="Union 6" d="M-2631.1,1081.8v-1.6H-8230.9V.022h5599.8V0h759.7s-187.845,197.448-91.626,488.844c49.167,148.9,96.309,256.289,104.683,362.118,7.979,100.852-57.98,201.711-168.644,254.286-65.858,31.29-144.552,42.382-223.028,42.383C-2441.2,1147.632-2631.1,1081.8-2631.1,1081.8Z" transform="translate(3259.524 0.803)" fill="url(#linear-gradient)"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
17
web-vue/packages/assets/svg/login-bg.svg
Normal file
@@ -0,0 +1,17 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="6395" height="1079" viewBox="0 0 6395 1079">
|
||||
<defs>
|
||||
<clipPath id="clip-path">
|
||||
<rect width="6395" height="1079" transform="translate(-5391)" fill="#fff"/>
|
||||
</clipPath>
|
||||
<linearGradient id="linear-gradient" x1="0.747" y1="0.222" x2="0.973" y2="0.807" gradientUnits="objectBoundingBox">
|
||||
<stop offset="0" stop-color="#1e58cc"/>
|
||||
<stop offset="1" stop-color="#1951be"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<g id="Mask_Group_1" data-name="Mask Group 1" transform="translate(5391)" clip-path="url(#clip-path)">
|
||||
<g id="Group_118" data-name="Group 118" transform="translate(-419.333 -1.126)">
|
||||
<path id="Path_142" data-name="Path 142" d="M6271.734-6.176s-222.478,187.809-55.349,583.254c44.957,106.375,81.514,205.964,84.521,277,8.164,192.764-156.046,268.564-156.046,268.564l-653.53-26.8L5475.065-21.625Z" transform="translate(-4876.383 0)" fill="#eaecf3"/>
|
||||
<path id="Union_6" data-name="Union 6" d="M-2631.1,1081.8v-1.6H-8230.9V.022H-2631.1V0H-1871.4s-187.845,197.448-91.626,488.844c49.167,148.9,96.309,256.289,104.683,362.118,7.979,100.852-57.98,201.711-168.644,254.286-65.858,31.29-144.552,42.382-223.028,42.383C-2441.2,1147.632-2631.1,1081.8-2631.1,1081.8Z" transform="translate(3259.524 0.803)" fill="url(#linear-gradient)"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
98
web-vue/packages/assets/svg/login-box-bg.svg
Normal file
@@ -0,0 +1,98 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="a622e68e-7a65-46e9-94a9-d455de519afc" data-name="Layer 1" width="971.44" height="502" viewBox="0 0 971.44 502">
|
||||
<defs>
|
||||
<linearGradient id="341b0e5e-a21f-44db-b85f-76180f33f0d3" x1="599.5" y1="668.05" x2="599.5" y2="199" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" stop-color="gray" stop-opacity="0.25"/>
|
||||
<stop offset="0.54" stop-color="gray" stop-opacity="0.12"/>
|
||||
<stop offset="1" stop-color="gray" stop-opacity="0.1"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="9c19d1ba-0c1d-4cca-8c15-e6f3831a5e67" x1="485.72" y1="258.88" x2="485.72" y2="71.12" xlink:href="#341b0e5e-a21f-44db-b85f-76180f33f0d3"/>
|
||||
<linearGradient id="fe76f7c7-2126-4e48-920d-21143a22d340" x1="132" y1="515" x2="303" y2="515" xlink:href="#341b0e5e-a21f-44db-b85f-76180f33f0d3"/>
|
||||
<linearGradient id="2cf89a04-5a05-413b-983a-d2bc296cbb5e" x1="933" y1="568.28" x2="1031" y2="568.28" xlink:href="#341b0e5e-a21f-44db-b85f-76180f33f0d3"/>
|
||||
</defs>
|
||||
<title>responsive</title>
|
||||
<g opacity="0.7">
|
||||
<path d="M852.69,199H346.31A16.37,16.37,0,0,0,330,215.42V563.94a16.37,16.37,0,0,0,16.31,16.42H520.47v60.16h-7.94a8.3,8.3,0,0,0-8.27,8.33v12.07h16.21v7.14H678.53v-7.14h16.21V648.85a8.3,8.3,0,0,0-8.27-8.33H679V640h-.51V580.36H852.69A16.37,16.37,0,0,0,869,563.94V215.42A16.37,16.37,0,0,0,852.69,199Z" transform="translate(-114.28 -199)" fill="url(#341b0e5e-a21f-44db-b85f-76180f33f0d3)"/>
|
||||
</g>
|
||||
<rect x="407.72" y="371" width="156" height="92" fill="#bdbdbd"/>
|
||||
<g opacity="0.1">
|
||||
<path d="M525.07,579H675.24c1.81-7.87,3.26-13,3.26-13h-157S523.11,571.11,525.07,579Z" transform="translate(-114.28 -199)"/>
|
||||
</g>
|
||||
<path d="M235.82,3h499.8a16.1,16.1,0,0,1,16.1,16.1V327a0,0,0,0,1,0,0h-532a0,0,0,0,1,0,0V19.1A16.1,16.1,0,0,1,235.82,3Z" fill="#535461"/>
|
||||
<path d="M849.9,576H350.1A16.1,16.1,0,0,1,334,559.9V526H866v33.9A16.1,16.1,0,0,1,849.9,576Z" transform="translate(-114.28 -199)" fill="#bdbdbd"/>
|
||||
<circle cx="485.72" cy="352" r="9" fill="#535461"/>
|
||||
<path d="M399.89,436H571.55a8.17,8.17,0,0,1,8.17,8.17V456a0,0,0,0,1,0,0h-188a0,0,0,0,1,0,0V444.17A8.17,8.17,0,0,1,399.89,436Z" fill="#bdbdbd"/>
|
||||
<g opacity="0.5">
|
||||
<rect x="320.72" y="71.12" width="330" height="187.76" rx="4.5" ry="4.5" fill="url(#9c19d1ba-0c1d-4cca-8c15-e6f3831a5e67)"/>
|
||||
</g>
|
||||
<rect x="324.95" y="72.5" width="321.54" height="183.96" rx="4.5" ry="4.5" fill="#fff"/>
|
||||
<g opacity="0.5">
|
||||
<rect x="414.52" y="98.91" width="35.44" height="31.9" rx="4.5" ry="4.5" fill="#0960bd"/>
|
||||
</g>
|
||||
<rect x="460.59" y="98.91" width="95.69" height="3.54" rx="1.77" ry="1.77" fill="#e0e0e0"/>
|
||||
<rect x="460.59" y="109.55" width="79.54" height="3.54" rx="1.77" ry="1.77" fill="#e0e0e0"/>
|
||||
<g opacity="0.5">
|
||||
<rect x="414.52" y="148.53" width="35.44" height="31.9" rx="4.5" ry="4.5" fill="#0960bd"/>
|
||||
</g>
|
||||
<rect x="460.59" y="148.53" width="95.69" height="3.54" rx="1.77" ry="1.77" fill="#e0e0e0"/>
|
||||
<rect x="460.59" y="159.16" width="95.69" height="3.54" rx="1.77" ry="1.77" fill="#e0e0e0"/>
|
||||
<g opacity="0.5">
|
||||
<rect x="414.52" y="198.15" width="35.44" height="31.9" rx="4.5" ry="4.5" fill="#0960bd"/>
|
||||
</g>
|
||||
<rect x="460.59" y="198.15" width="95.69" height="3.54" rx="1.77" ry="1.77" fill="#e0e0e0"/>
|
||||
<rect x="460.59" y="208.78" width="96.33" height="3.54" rx="1.59" ry="1.59" fill="#e0e0e0"/>
|
||||
<line x1="485.72" y1="42" x2="485.72" y2="20" stroke="#0960bd" stroke-miterlimit="10" stroke-width="2"/>
|
||||
<line x1="485.72" y1="79" x2="485.72" y2="50.13" stroke="#0960bd" stroke-miterlimit="10" stroke-width="2"/>
|
||||
<circle cx="485.72" cy="79" r="4" fill="#0960bd"/>
|
||||
<circle cx="485.72" cy="46" r="4" fill="none" stroke="#fff" stroke-miterlimit="10"/>
|
||||
<line x1="485.72" y1="42" x2="485.72" y2="20" stroke="#0960bd" stroke-miterlimit="10" stroke-width="2"/>
|
||||
<line x1="485.72" y1="79" x2="485.72" y2="50.13" stroke="#0960bd" stroke-miterlimit="10" stroke-width="2"/>
|
||||
<circle cx="485.72" cy="79" r="4" fill="#0960bd"/>
|
||||
<circle cx="485.72" cy="46" r="4" fill="none" stroke="#fff" stroke-miterlimit="10"/>
|
||||
<line x1="485.72" y1="279" x2="485.72" y2="310" stroke="#0960bd" stroke-miterlimit="10" stroke-width="2"/>
|
||||
<line x1="485.72" y1="251" x2="485.72" y2="279.87" stroke="#0960bd" stroke-miterlimit="10" stroke-width="2"/>
|
||||
<circle cx="485.72" cy="251" r="4" fill="#0960bd"/>
|
||||
<line x1="305.72" y1="168.5" x2="274.22" y2="168.5" stroke="#0960bd" stroke-miterlimit="10" stroke-width="2"/>
|
||||
<line x1="333.22" y1="168.5" x2="304.35" y2="168.5" stroke="#0960bd" stroke-miterlimit="10" stroke-width="2"/>
|
||||
<circle cx="333.22" cy="168.5" r="4" fill="#0960bd"/>
|
||||
<g opacity="0.1">
|
||||
<rect x="408.22" y="435.5" width="156" height="3"/>
|
||||
</g>
|
||||
<g opacity="0.7">
|
||||
<path d="M293.48,566.06H221.08l1-8.14c20.46-18.37,33.69-67.31,33.69-67.31a6.78,6.78,0,0,0-.87.18c-12,2.42-20.54,7.35-26.51,13.28l2.54-21.66c37.8-8.14,52.79-58.14,52.79-58.14-24.12,5.35-39.16,13.63-48.5,21.49l3.72-31.82c25.56,8.77,52-37.82,52-37.82l-1-.21.5-.32-.76.27c-28.25-6.09-43.35,10.06-48.25,16.77l.37-3.12q-1.12,3-2.18,5.88h0l0,.08q-3,8.13-5.49,16.06l0,0h0q-2.17,6.77-4.06,13.4l0-.06s-1.17-28.46-31.18-35.95c0,0,3.15,62.07,26.93,51.91h0c-2.2,9-4,17.66-5.56,26.07h0q-1.49,8.21-2.6,16l-.14.16.14-.12-.06.41v0h0q-1,7.07-1.7,13.78c.46-8.62-1.11-33.52-30.45-56.92,0,0-39,68.54,27.5,82,.15.13.3.26.44.38l-.1-.31.6.13.27-3.52a369.39,369.39,0,0,0,.23,44.1h0c.07,1,.14,2,.21,2.95H141.37c-27.94,57.79,15.52,89.46,15.52,89.46h120C323.49,596.66,293.48,566.06,293.48,566.06Zm-78-65.68h0v0Z" transform="translate(-114.28 -199)" fill="url(#fe76f7c7-2126-4e48-920d-21143a22d340)"/>
|
||||
</g>
|
||||
<path d="M217,588s-19-83,23-190" transform="translate(-114.28 -199)" fill="none" stroke="#535461" stroke-miterlimit="10" stroke-width="3" opacity="0.6"/>
|
||||
<path d="M143,563H290s29,37-16,92H158S116,617,143,563Z" transform="translate(-114.28 -199)" fill="#a76611"/>
|
||||
<path d="M237.89,403.5s14.61-26,49.61-18c0,0-28.93,49.26-55,33.13Z" transform="translate(-114.28 -199)" fill="#4db6ac"/>
|
||||
<path d="M228.63,431.09S227.5,404.5,198.5,397.5c0,0,3,58,26,48.5Z" transform="translate(-114.28 -199)" fill="#4db6ac"/>
|
||||
<path d="M219.15,470.36s5.35-27.86,61.35-39.86c0,0-17.86,57.62-63.93,55.31Z" transform="translate(-114.28 -199)" fill="#4db6ac"/>
|
||||
<path d="M214.61,501.63s5.89-29.13-29.11-56.13c0,0-38,64.67,27.48,76.83Z" transform="translate(-114.28 -199)" fill="#4db6ac"/>
|
||||
<path d="M213.56,541.67S209.5,500.5,253.5,492.5c0,0-16.07,57.49-40,67.74Z" transform="translate(-114.28 -199)" fill="#4db6ac"/>
|
||||
<path d="M233,419s38-29,54-34" transform="translate(-114.28 -199)" fill="none" stroke="#535461" stroke-miterlimit="10" opacity="0.3"/>
|
||||
<path d="M216.5,485.5s46-49,64-55" transform="translate(-114.28 -199)" fill="none" stroke="#535461" stroke-miterlimit="10" opacity="0.3"/>
|
||||
<path d="M198.5,397.5s28,38,26,48" transform="translate(-114.28 -199)" fill="none" stroke="#535461" stroke-miterlimit="10" opacity="0.3"/>
|
||||
<path d="M185.5,445.5s15,68,27,77" transform="translate(-114.28 -199)" fill="none" stroke="#535461" stroke-miterlimit="10" opacity="0.3"/>
|
||||
<path d="M213.5,560.5s24-66,40-68" transform="translate(-114.28 -199)" fill="none" stroke="#535461" stroke-miterlimit="10" opacity="0.3"/>
|
||||
<g opacity="0.1">
|
||||
<path d="M290,563H143c-.33.67-.65,1.34-1,2H285s28.29,36.11-14.4,90H274C319,600,290,563,290,563Z" transform="translate(-114.28 -199)"/>
|
||||
</g>
|
||||
<rect y="455.6" width="971.44" height="32.93" fill="#e0e0e0"/>
|
||||
<rect x="41.16" y="488.53" width="889.11" height="13.47" fill="#e0e0e0"/>
|
||||
<rect x="41.16" y="488.53" width="889.11" height="4.49" opacity="0.1"/>
|
||||
<line x1="690.22" y1="168.5" x2="696.22" y2="168.5" stroke="#0960bd" stroke-miterlimit="10" stroke-width="2"/>
|
||||
<line x1="637.22" y1="168.5" x2="682.1" y2="168.5" stroke="#0960bd" stroke-miterlimit="10" stroke-width="2"/>
|
||||
<circle cx="637.22" cy="168.5" r="4" fill="#0960bd"/>
|
||||
<circle cx="686.22" cy="168.5" r="4" fill="none" stroke="#fff" stroke-miterlimit="10"/>
|
||||
<g opacity="0.7">
|
||||
<path d="M1027,643.88l.1-.15q.31-.48.61-1l.11-.19q.29-.49.55-1l.09-.17c.2-.39.39-.78.56-1.19h0a23.79,23.79,0,0,0,.94-2.51l.1-.33c.09-.31.18-.62.26-.93l.1-.44q.1-.42.18-.85c0-.16.06-.32.09-.48s.09-.56.13-.85,0-.33.06-.49.06-.61.08-.92c0-.14,0-.29,0-.43,0-.45,0-.91,0-1.36V548h-13.85V507.52h-17V548H988.39V489.86h-17V548H965V481.55h-17V548H933V630.6c0,13.48,11.21,24.4,25,24.4H1006a25.19,25.19,0,0,0,20.24-10.06l0,0Q1026.61,644.41,1027,643.88Z" transform="translate(-114.28 -199)" fill="url(#2cf89a04-5a05-413b-983a-d2bc296cbb5e)"/>
|
||||
</g>
|
||||
<rect x="835.72" y="321" width="16" height="100" fill="#535461"/>
|
||||
<rect x="835.72" y="288" width="16" height="33" fill="#3ad29f"/>
|
||||
<rect x="857.72" y="329" width="16" height="100" fill="#535461"/>
|
||||
<rect x="857.72" y="296" width="16" height="33" fill="#4d8af0"/>
|
||||
<rect x="884.72" y="346" width="16" height="100" fill="#535461"/>
|
||||
<rect x="884.72" y="313" width="16" height="33" fill="#f55f44"/>
|
||||
<path d="M821.72,352h92a0,0,0,0,1,0,0v79.5a23.5,23.5,0,0,1-23.5,23.5h-45a23.5,23.5,0,0,1-23.5-23.5V352A0,0,0,0,1,821.72,352Z" fill="#0a949e"/>
|
||||
<g opacity="0.1">
|
||||
<path d="M936,551v4h88v79.5a23.39,23.39,0,0,1-5,14.49,23.45,23.45,0,0,0,9-18.49V551Z" transform="translate(-114.28 -199)"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 9.0 KiB |
1
web-vue/packages/assets/svg/net-error.svg
Normal file
|
After Width: | Height: | Size: 25 KiB |
1
web-vue/packages/assets/svg/no-data.svg
Normal file
|
After Width: | Height: | Size: 10 KiB |
1
web-vue/packages/assets/svg/preview/p-rotate.svg
Normal file
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1595306944988" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1820" xmlns:xlink="http://www.w3.org/1999/xlink" width="48" height="48"><defs><style type="text/css"></style></defs><path d="M1464.3 279.7" p-id="1821" fill="#ffffff"></path><path d="M512 960c-60.5 0-119.1-11.9-174.4-35.2-53.4-22.6-101.3-54.9-142.4-96s-73.4-89-96-142.4C75.9 631.1 64 572.5 64 512s11.9-119.1 35.2-174.4c22.6-53.4 54.9-101.3 96-142.4s89-73.4 142.4-96C392.9 75.9 451.5 64 512 64s119.1 11.9 174.4 35.2c53.4 22.6 101.3 54.9 142.4 96s73.4 89 96 142.4C948.1 392.9 960 451.5 960 512c0 19.1-15.5 34.6-34.6 34.6s-34.6-15.5-34.6-34.6c0-51.2-10-100.8-29.8-147.4-19.1-45.1-46.4-85.6-81.2-120.4C745 209.4 704.5 182 659.4 163c-46.7-19.7-96.3-29.8-147.4-29.8-51.2 0-100.8 10-147.4 29.8-45.1 19.1-85.6 46.4-120.4 81.2S182 319.5 163 364.6c-19.7 46.7-29.8 96.3-29.8 147.4 0 51.2 10 100.8 29.8 147.4 19.1 45.1 46.4 85.6 81.2 120.4C279 814.6 319.5 842 364.6 861c46.7 19.7 96.3 29.8 147.4 29.8 64.6 0 128.4-16.5 184.4-47.8 54.4-30.4 100.9-74.1 134.6-126.6 10.3-16.1 31.7-20.8 47.8-10.4 16.1 10.3 20.8 31.7 10.4 47.8-39.8 62-94.8 113.7-159.1 149.6-66.2 37-141.7 56.6-218.1 56.6z" p-id="1822" fill="#ffffff"></path><path d="M924 552c-19.8 0-36-16.2-36-36V228c0-19.8 16.2-36 36-36s36 16.2 36 36v288c0 19.8-16.2 36-36 36zM275.4 575.5c9.5-2.5 19.1 2.9 22.3 12.2 3.5 10.2 9.9 17.7 19.1 22.6 7.1 3.9 15.1 5.8 24 5.8 16.6 0 30.8-6.9 42.5-20.8 11.7-13.8 20-32.7 24.9-75.1-7.7 12.2-17.3 20.8-28.7 25.8-11.4 5-23.7 7.4-36.8 7.4-26.7 0-47.7-8.3-63.3-24.9-15.5-16.6-23.3-37.9-23.3-64.1 0-25.1 7.7-47.1 23-66.2 15.3-19 37.9-28.6 67.8-28.6 40.3 0 68.1 18.1 83.4 54.4 8.5 19.9 12.7 44.9 12.7 74.9 0 33.8-5.1 63.8-15.3 89.9-16.9 43.5-45.5 65.2-85.8 65.2-27 0-47.6-7.1-61.6-21.2-10-10.1-16.4-22-19.3-35.8-2-9.6 4-19.1 13.5-21.6l0.9 0.1z m103-74.4c9.4-7.5 14.1-20.6 14.1-39.3 0-16.8-4.2-29.3-12.7-37.5S360.6 412 347.5 412c-14 0-25.2 4.7-33.4 14.1-8.2 9.4-12.4 22-12.4 37.7 0 14.9 3.6 26.7 10.9 35.5 7.2 8.8 18.8 13.1 34.6 13.1 11.4 0 21.8-3.8 31.2-11.3zM646.6 414.4c12.4 22.8 18.5 54 18.5 93.7 0 37.6-5.6 68.7-16.8 93.3-16.2 35.3-42.8 52.9-79.6 52.9-33.2 0-57.9-14.4-74.2-43.3-13.5-24.1-20.3-56.4-20.3-97 0-31.4 4.1-58.4 12.2-80.9 15.2-42 42.7-63 82.5-63 35.9 0 61.8 14.8 77.7 44.3z m-40.2 173.3c9.4-13.9 14-39.9 14-78 0-27.4-3.4-50-10.1-67.7-6.8-17.7-19.9-26.6-39.4-26.6-17.9 0-31 8.4-39.3 25.2-8.3 16.8-12.4 41.6-12.4 74.3 0 24.6 2.6 44.4 7.9 59.4 8.1 22.8 22 34.3 41.6 34.3 15.7 0 28.3-7 37.7-20.9zM803.3 387.2c11.2 11.3 16.8 25 16.8 41.2 0 16.7-5.8 30.7-17.5 41.8C791 481.4 777.4 487 762 487c-17.1 0-31.2-5.8-42.1-17.4-10.9-11.6-16.4-25.1-16.4-40.6 0-16.5 5.8-30.4 17.3-41.7 11.5-11.3 25.3-17 41.2-17 16.3 0 30.1 5.7 41.3 16.9zM739.5 451c6.2 6.2 13.7 9.3 22.5 9.3 8.4 0 15.8-3.1 22.1-9.3 6.3-6.2 9.4-13.7 9.4-22.6 0-8.5-3.1-15.9-9.3-22.1-6.2-6.2-13.6-9.3-22.2-9.3s-16.1 3.1-22.4 9.3c-6.3 6.2-9.4 13.7-9.4 22.6-0.1 8.4 3 15.8 9.3 22.1z" p-id="1823" fill="#ffffff"></path></svg>
|
||||
|
After Width: | Height: | Size: 3.0 KiB |
1
web-vue/packages/assets/svg/preview/resume.svg
Normal file
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1595307154239" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7317" xmlns:xlink="http://www.w3.org/1999/xlink" width="48" height="48"><defs><style type="text/css"></style></defs><path d="M316 672h60c4.4 0 8-3.6 8-8V360c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v304c0 4.4 3.6 8 8 8zM512 622c22.1 0 40-17.9 40-39 0-23.1-17.9-41-40-41s-40 17.9-40 41c0 21.1 17.9 39 40 39zM512 482c22.1 0 40-17.9 40-39 0-23.1-17.9-41-40-41s-40 17.9-40 41c0 21.1 17.9 39 40 39z" p-id="7318" fill="#ffffff"></path><path d="M880 112H144c-17.7 0-32 14.3-32 32v736c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V144c0-17.7-14.3-32-32-32z m-40 728H184V184h656v656z" p-id="7319" fill="#ffffff"></path><path d="M648 672h60c4.4 0 8-3.6 8-8V360c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v304c0 4.4 3.6 8 8 8z" p-id="7320" fill="#ffffff"></path></svg>
|
||||
|
After Width: | Height: | Size: 996 B |
1
web-vue/packages/assets/svg/preview/scale.svg
Normal file
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1595307195033" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8116" xmlns:xlink="http://www.w3.org/1999/xlink" width="48" height="48"><defs><style type="text/css"></style></defs><path d="M887.081 904.791a25.8 25.8 0 0 1-18.376-7.619L705.618 734.075l-4.163 3.369c-58.255 47.18-131.522 73.16-206.32 73.16-181.07 0-328.377-147.308-328.377-328.367 0-181.068 147.308-328.376 328.377-328.376 181.063 0 328.376 147.308 328.376 328.376 0 77.072-27.412 152.07-77.169 211.17l-3.522 4.173 162.719 162.744a25.846 25.846 0 0 1 7.639 18.432 26.081 26.081 0 0 1-26.051 26.045l-0.046-0.01zM495.13 205.957c-152.336 0-276.27 123.935-276.27 276.27 0 152.33 123.934 276.27 276.27 276.27 152.34 0 276.275-123.94 276.275-276.27 0-152.335-123.935-276.27-276.275-276.27z" fill="#ffffff" p-id="8117"></path><path d="M626.545 508.355h-262.83a26.127 26.127 0 0 1 0-52.255h262.83a26.127 26.127 0 0 1 0 52.255z" fill="#ffffff" p-id="8118"></path><path d="M495.13 639.77a26.127 26.127 0 0 1-26.128-26.128v-262.83a26.127 26.127 0 0 1 52.255 0v262.835a26.127 26.127 0 0 1-26.127 26.123z" fill="#ffffff" p-id="8119"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
1
web-vue/packages/assets/svg/preview/unrotate.svg
Normal file
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1595306911635" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1352" width="48" height="48" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><style type="text/css"></style></defs><path d="M924.8 337.6c-22.6-53.4-54.9-101.3-96-142.4s-89-73.4-142.4-96C631.1 75.9 572.5 64 512 64S392.9 75.9 337.6 99.2c-53.4 22.6-101.3 54.9-142.4 96-22.4 22.4-42.2 46.8-59.2 73.1V228c0-19.8-16.2-36-36-36s-36 16.2-36 36v288c0 19.8 16.2 36 36 36s36-16.2 36-36v-50.2c4.2-34.8 13.2-68.7 27-101.2 19.1-45.1 46.4-85.6 81.2-120.4C279 209.4 319.5 182 364.6 163c46.7-19.7 96.3-29.8 147.4-29.8 51.2 0 100.8 10 147.4 29.8 45.1 19.1 85.6 46.4 120.4 81.2C814.6 279 842 319.5 861 364.6c19.7 46.7 29.8 96.3 29.8 147.4 0 51.2-10 100.8-29.8 147.4-19.1 45.1-46.4 85.6-81.2 120.4C745 814.6 704.5 842 659.4 861c-46.7 19.7-96.3 29.8-147.4 29.8-64.6 0-128.4-16.5-184.4-47.8-54.4-30.4-100.9-74.1-134.6-126.6-10.3-16.1-31.7-20.8-47.8-10.4-16.1 10.3-20.8 31.7-10.4 47.8 39.8 62 94.8 113.7 159.1 149.6 66.2 37 141.7 56.6 218.1 56.6 60.5 0 119.1-11.9 174.4-35.2 53.4-22.6 101.3-54.9 142.4-96 41.1-41.1 73.4-89 96-142.4C948.1 631.1 960 572.5 960 512s-11.9-119.1-35.2-174.4z" p-id="1353" fill="#ffffff"></path><path d="M275.4 575.5c9.5-2.5 19.1 2.9 22.3 12.2 3.5 10.2 9.9 17.7 19.1 22.6 7.1 3.9 15.1 5.8 24 5.8 16.6 0 30.8-6.9 42.5-20.8 11.7-13.8 20-32.7 24.9-75.1-7.7 12.2-17.3 20.8-28.7 25.8-11.4 5-23.7 7.4-36.8 7.4-26.7 0-47.7-8.3-63.3-24.9-15.5-16.6-23.3-37.9-23.3-64.1 0-25.1 7.7-47.1 23-66.2 15.3-19 37.9-28.6 67.8-28.6 40.3 0 68.1 18.1 83.4 54.4 8.5 19.9 12.7 44.9 12.7 74.9 0 33.8-5.1 63.8-15.3 89.9-16.9 43.5-45.5 65.2-85.8 65.2-27 0-47.6-7.1-61.6-21.2-10-10.1-16.4-22-19.3-35.8-2-9.6 4-19.1 13.5-21.6l0.9 0.1z m103-74.4c9.4-7.5 14.1-20.6 14.1-39.3 0-16.8-4.2-29.3-12.7-37.5S360.6 412 347.5 412c-14 0-25.2 4.7-33.4 14.1-8.2 9.4-12.4 22-12.4 37.7 0 14.9 3.6 26.7 10.9 35.5 7.2 8.8 18.8 13.1 34.6 13.1 11.4 0 21.8-3.8 31.2-11.3zM646.6 414.4c12.4 22.8 18.5 54 18.5 93.7 0 37.6-5.6 68.7-16.8 93.3-16.2 35.3-42.8 52.9-79.6 52.9-33.2 0-57.9-14.4-74.2-43.3-13.5-24.1-20.3-56.4-20.3-97 0-31.4 4.1-58.4 12.2-80.9 15.2-42 42.7-63 82.5-63 35.9 0 61.8 14.8 77.7 44.3z m-40.2 173.3c9.4-13.9 14-39.9 14-78 0-27.4-3.4-50-10.1-67.7-6.8-17.7-19.9-26.6-39.4-26.6-17.9 0-31 8.4-39.3 25.2-8.3 16.8-12.4 41.6-12.4 74.3 0 24.6 2.6 44.4 7.9 59.4 8.1 22.8 22 34.3 41.6 34.3 15.7 0 28.3-7 37.7-20.9zM803.3 387.2c11.2 11.3 16.8 25 16.8 41.2 0 16.7-5.8 30.7-17.5 41.8C791 481.4 777.4 487 762 487c-17.1 0-31.2-5.8-42.1-17.4-10.9-11.6-16.4-25.1-16.4-40.6 0-16.5 5.8-30.4 17.3-41.7 11.5-11.3 25.3-17 41.2-17 16.3 0 30.1 5.7 41.3 16.9zM739.5 451c6.2 6.2 13.7 9.3 22.5 9.3 8.4 0 15.8-3.1 22.1-9.3 6.3-6.2 9.4-13.7 9.4-22.6 0-8.5-3.1-15.9-9.3-22.1-6.2-6.2-13.6-9.3-22.2-9.3s-16.1 3.1-22.4 9.3c-6.3 6.2-9.4 13.7-9.4 22.6-0.1 8.4 3 15.8 9.3 22.1z" p-id="1354" fill="#ffffff"></path></svg>
|
||||
|
After Width: | Height: | Size: 2.9 KiB |
1
web-vue/packages/assets/svg/preview/unscale.svg
Normal file
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1595308005241" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="9878" xmlns:xlink="http://www.w3.org/1999/xlink" width="48" height="48"><defs><style type="text/css"></style></defs><path d="M750.3 198.7C598 46.4 351.1 46.4 198.7 198.7s-152.3 399.2 0 551.5C345.1 896.6 578.8 902.3 732 767.3l172.1 172.1 35.4-35.4-172.1-171.9c135-153.2 129.3-387-17.1-533.4z m39.3 403.8c-17.1 42.1-42.2 80-74.7 112.4-32.5 32.5-70.3 57.6-112.4 74.7-40.7 16.5-83.8 24.9-128 24.9s-87.2-8.4-128-24.9c-42.1-17.1-80-42.2-112.4-74.7s-57.6-70.3-74.7-112.4c-16.5-40.7-24.9-83.8-24.9-128s8.4-87.2 24.9-128c17.1-42.1 42.2-80 74.7-112.4s70.3-57.6 112.4-74.7c40.7-16.5 83.8-24.9 128-24.9s87.2 8.4 128 24.9c42.1 17.1 80 42.2 112.4 74.7 32.5 32.5 57.6 70.3 74.7 112.4 16.5 40.7 24.9 83.8 24.9 128s-8.4 87.3-24.9 128zM671 502H271v-50h400v50z" fill="#ffffff" p-id="9879"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
19
web-vue/packages/assets/tsconfig.json
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@jeesite/assets/*": ["./*"]
|
||||
}
|
||||
},
|
||||
"include": [
|
||||
"./**/*.ts",
|
||||
"./**/*.tsx",
|
||||
"./**/*.vue"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"vite.config.ts",
|
||||
"dist"
|
||||
]
|
||||
}
|
||||
23
web-vue/packages/cms/README.md
Normal file
@@ -0,0 +1,23 @@
|
||||
|
||||
- 官方网站:<https://jeesite.com>
|
||||
- 使用文档:<https://jeesite.com/docs>
|
||||
- 后端代码:<https://gitee.com/thinkgem/jeesite5>
|
||||
- 前端代码:<https://gitee.com/thinkgem/jeesite-vue>
|
||||
|
||||
------
|
||||
|
||||
<div align="center">
|
||||
如果你喜欢 JeeSite,请给她一个 ⭐️ Star,您的支持将是我们前行的动力。
|
||||
</div>
|
||||
|
||||
------
|
||||
|
||||
- 问题反馈:<https://gitee.com/thinkgem/jeesite-vue/issues> [【新手必读】](https://gitee.com/thinkgem/jeesite5/issues/I18ARR)
|
||||
- 需求收集:<https://gitee.com/thinkgem/jeesite-vue/issues/new>
|
||||
- QQ 群:`127515876`、`209330483`、`223507718`、`709534275`、`730390092`、`1373527`、`183903863(外包)`
|
||||
- 微信群:添加客服微信 <http://s.jeesite.com> 邀请您进群
|
||||
- 关注微信公众号,了解最新动态:
|
||||
|
||||
<p style="padding-left:40px">
|
||||
<img alt="JeeSite微信公众号" src="https://jeesite.com/assets/images/mp.png" width="220" height="220">
|
||||
</p>
|
||||
38
web-vue/packages/cms/api/cms/chat.ts
Normal file
@@ -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<Recordable[]>({ url: adminPath + '/cms/chat/message', params });
|
||||
|
||||
export const cmsChatList = (params?: Recordable | any) =>
|
||||
defHttp.get<Recordable[]>({ url: adminPath + '/cms/chat/list', params });
|
||||
|
||||
export const cmsChatSave = (params?: Recordable | any) =>
|
||||
defHttp.post<Recordable>({ url: adminPath + '/cms/chat/save', params });
|
||||
|
||||
export const cmsChatDelete = (params?: Recordable | any) =>
|
||||
defHttp.get<Recordable>({ url: adminPath + '/cms/chat/delete', params });
|
||||
|
||||
export const cmsChatStream = (
|
||||
params?: Recordable | any,
|
||||
signal?: GenericAbortSignal,
|
||||
onDownloadProgress?: (progressEvent: AxiosProgressEvent) => void,
|
||||
) =>
|
||||
defHttp.post<Recordable>({
|
||||
url: adminPath + '/cms/chat/stream',
|
||||
params,
|
||||
signal,
|
||||
onDownloadProgress,
|
||||
responseType: 'stream',
|
||||
headers: {
|
||||
'x-ajax': 'event-stream',
|
||||
},
|
||||
});
|
||||
2
web-vue/packages/cms/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
import './node_modules/@jeesite/cms-lib/dist/style.css';
|
||||
export { ChatMessage } from './node_modules/@jeesite/cms-lib/dist';
|
||||
31
web-vue/packages/cms/package.json
Normal file
@@ -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"
|
||||
}
|
||||
}
|
||||
19
web-vue/packages/cms/tsconfig.json
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@jeesite/cms/*": ["./*"]
|
||||
}
|
||||
},
|
||||
"include": [
|
||||
"./**/*.ts",
|
||||
"./**/*.tsx",
|
||||
"./**/*.vue"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"vite.config.ts",
|
||||
"dist"
|
||||
]
|
||||
}
|
||||
240
web-vue/packages/cms/views/cms/chat/index.vue
Normal file
@@ -0,0 +1,240 @@
|
||||
<!--
|
||||
* Copyright (c) 2013-Now http://jeesite.com All rights reserved.
|
||||
* No deletion without permission, or be held responsible to law.
|
||||
* @author ThinkGem
|
||||
-->
|
||||
<template>
|
||||
<PageWrapper :sidebarWidth="230" :contentFullHeight="true">
|
||||
<template #sidebar>
|
||||
<div class="p-2 pt-1">
|
||||
<a-button type="primary" class="w-full" @click="handleAdd" :disabled="loading">
|
||||
<Icon icon="i-ant-design:plus-outlined" /> {{ t('新建对话') }}
|
||||
</a-button>
|
||||
</div>
|
||||
<ScrollContainer class="jeesite-cms-ai p-2 bg-white rounded-2 h-full">
|
||||
<Menu class="jeesite-cms-ai-menu" v-model:selectedKeys="conversationIds" :disabled="loading">
|
||||
<template v-for="(item, index) in chatList" :key="item.id">
|
||||
<Menu.Item @click="handleSelect(item)">
|
||||
<div class="flex justify-end">
|
||||
<span v-if="item.edit" class="flex-1 mr-2">
|
||||
<a-input
|
||||
v-model:value="item.title"
|
||||
size="small"
|
||||
class="mr-2"
|
||||
@blur="handleEdit(item, false, $event)"
|
||||
/>
|
||||
</span>
|
||||
<span v-else class="flex-1 truncate">{{ item.title }}</span>
|
||||
<span v-if="item.id == conversationIds[0]" class="c-gray">
|
||||
<Icon icon="i-ant-design:edit" class="pt-3" @click="handleEdit(item, true)" />
|
||||
<Popconfirm :title="t('是否确认删除该对话吗?')" @confirm="handleDelete(item, index)">
|
||||
<Icon icon="i-ant-design:delete" class="pt-3" />
|
||||
</Popconfirm>
|
||||
</span>
|
||||
</div>
|
||||
</Menu.Item>
|
||||
</template>
|
||||
</Menu>
|
||||
<div class="h-10"></div>
|
||||
</ScrollContainer>
|
||||
</template>
|
||||
<div class="h-full rounded-2 flex flex-col overflow-hidden">
|
||||
<div v-if="messages.length == 0" class="h-[90%] flex justify-center items-center text-center">
|
||||
<div class="text-xl c-gray-4">
|
||||
{{ t('我是你的 AI 助手,我可以帮你解答一些问题') }}
|
||||
<div v-if="userStore.getPageCacheByKey('demoMode')" class="text-sm mt-20 line-height-loose">
|
||||
提示:当前对接的是 DeepSeek 蒸馏过的 7B 超小模型,仅作为演示使用,AI 回答结果可能不够理想,<br />
|
||||
可在自己本地部署,或对接其它大模型。此外当前向量库中只含了几篇关于 jeesite 的文章,<br />
|
||||
知识库文章来源,可进入菜单查看:扩展功能 -> 内容管理 -> 内容发布<br />
|
||||
提问举例:jeesite 简介、jeesite 优势、jeesite 技术栈,体验一下。
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ChatMessage
|
||||
ref="messageRef"
|
||||
v-model:value="messages"
|
||||
:chatStreamApi="cmsChatStream"
|
||||
:conversationId="conversationIds[0]"
|
||||
:inputMessage="inputMessageRef?.value"
|
||||
v-model:loading="loading"
|
||||
/>
|
||||
<div class="pl-14 pr-16 w-full flex justify-end mt-3">
|
||||
<div class="flex-1 rounded-2 p-3 pb-1 bg-white text-[15px] leading-7">
|
||||
<textarea
|
||||
ref="inputMessageRef"
|
||||
class="outline-none no-scrollbar resize-none w-full h-full border-none bg-transparent"
|
||||
rows="1"
|
||||
:placeholder="t('你有什么想知道的,快来问问我,Shift+Enter 换行,Enter 发送。')"
|
||||
@input="handleInput"
|
||||
@keypress="handleEnter"
|
||||
></textarea>
|
||||
</div>
|
||||
<a-button
|
||||
style="width: 110px; height: 100%; margin-left: 10px"
|
||||
type="primary"
|
||||
:loading="loading"
|
||||
@click="handleSend"
|
||||
>
|
||||
<Icon icon="i-fa:send" /> {{ t('发送') }}
|
||||
</a-button>
|
||||
</div>
|
||||
<div class="pt-2 pr-8 c-gray-4 text-xs text-center">
|
||||
{{ t('服务生成的所有内容均由人工智能模型生成,准确和完整性无法保证,不代表我们的态度或观点。') }}
|
||||
</div>
|
||||
</div>
|
||||
</PageWrapper>
|
||||
</template>
|
||||
<script lang="ts" setup name="ViewsCmsChatIndex">
|
||||
import { nextTick, onMounted, ref } from 'vue';
|
||||
import { Menu, Popconfirm } from 'ant-design-vue';
|
||||
import { Icon } from '@jeesite/core/components/Icon';
|
||||
import { useI18n } from '@jeesite/core/hooks/web/useI18n';
|
||||
import { useMessage } from '@jeesite/core/hooks/web/useMessage';
|
||||
import { useUserStore } from '@jeesite/core/store/modules/user';
|
||||
import { PageWrapper } from '@jeesite/core/components/Page';
|
||||
import { ScrollContainer } from '@jeesite/core/components/Container';
|
||||
import { cmsChatDelete, cmsChatList, cmsChatMessage, cmsChatSave, cmsChatStream } from '@jeesite/cms/api/cms/chat';
|
||||
import { ChatMessage } from '@jeesite/cms';
|
||||
|
||||
const { t } = useI18n('cms.chat');
|
||||
const { showMessage } = useMessage();
|
||||
const conversationIds = ref<string[]>([]);
|
||||
const conversationTitle = ref<string>('');
|
||||
const userStore = useUserStore();
|
||||
|
||||
const loading = ref(false);
|
||||
const messageRef = ref<InstanceType<typeof ChatMessage>>();
|
||||
const inputMessageRef = ref<HTMLTextAreaElement>();
|
||||
const messages = ref<Recordable[]>([]);
|
||||
const chatList = ref<Recordable[]>([]);
|
||||
|
||||
onMounted(async () => {
|
||||
chatList.value = await cmsChatList();
|
||||
if (chatList.value.length == 0) {
|
||||
const res = await cmsChatSave();
|
||||
chatList.value.unshift(res);
|
||||
}
|
||||
await nextTick(async () => {
|
||||
setTimeout(async () => {
|
||||
await handleSelect(chatList.value[0]);
|
||||
}, 100);
|
||||
});
|
||||
});
|
||||
|
||||
async function handleAdd() {
|
||||
if (chatList.value.length > 0) {
|
||||
await handleSelect(chatList.value[0]);
|
||||
if (messages.value.length == 0) {
|
||||
showMessage(t('当前已是新对话'));
|
||||
return;
|
||||
}
|
||||
}
|
||||
const res = await cmsChatSave();
|
||||
chatList.value.unshift(res);
|
||||
await handleSelect(chatList.value[0]);
|
||||
}
|
||||
|
||||
async function handleSelect(item: Recordable) {
|
||||
conversationIds.value = [item.id];
|
||||
conversationTitle.value = item.title;
|
||||
messages.value = await cmsChatMessage({ id: item.id });
|
||||
messageRef.value?.scrollBottom();
|
||||
}
|
||||
|
||||
async function handleEdit(item: Recordable, edit: boolean, event?: Event) {
|
||||
item.edit = edit;
|
||||
if (!edit) {
|
||||
const res = await cmsChatSave(item);
|
||||
showMessage(res.message);
|
||||
}
|
||||
}
|
||||
|
||||
async function handleDelete(item: Recordable, idx: number) {
|
||||
const res = await cmsChatDelete({ id: item.id });
|
||||
chatList.value.splice(idx, 1);
|
||||
showMessage(res.message);
|
||||
if (idx == 0 && chatList.value[idx]) {
|
||||
await handleSelect(chatList.value[idx]);
|
||||
} else if (chatList.value[idx - 1]) {
|
||||
await handleSelect(chatList.value[idx - 1]);
|
||||
}
|
||||
}
|
||||
|
||||
function handleInput() {
|
||||
if (inputMessageRef.value) {
|
||||
inputMessageRef.value.style.height = 'auto';
|
||||
const height = inputMessageRef.value.scrollHeight;
|
||||
inputMessageRef.value.style.height = (height > 300 ? 300 : height) + 'px';
|
||||
}
|
||||
}
|
||||
|
||||
function handleEnter(event: KeyboardEvent) {
|
||||
if (event.key === 'Enter' && !event.shiftKey) {
|
||||
event.preventDefault();
|
||||
if (!loading.value) {
|
||||
handleSend();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function handleSend() {
|
||||
if (inputMessageRef.value && inputMessageRef.value.value) {
|
||||
if (!conversationIds.value[0]) {
|
||||
await handleAdd();
|
||||
}
|
||||
loading.value = true;
|
||||
const params = {
|
||||
id: conversationIds.value[0],
|
||||
message: inputMessageRef.value.value,
|
||||
};
|
||||
inputMessageRef.value.value = '';
|
||||
inputMessageRef.value.style.height = 'auto';
|
||||
try {
|
||||
chatList.value
|
||||
.filter((item) => item.id == params.id)
|
||||
.forEach((item) => {
|
||||
if (item.title.startsWith('新对话')) {
|
||||
item.title = params.message.substring(0, 30);
|
||||
cmsChatSave(item);
|
||||
}
|
||||
});
|
||||
await messageRef.value?.sendMessage(params);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
} else {
|
||||
showMessage(t('请填写你的问题'));
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="less">
|
||||
.jeesite-cms-ai {
|
||||
&-menu.ant-menu.ant-menu-light {
|
||||
border-right: 0 !important;
|
||||
padding: 2px !important;
|
||||
|
||||
.ant-menu-item {
|
||||
padding-right: 8px;
|
||||
color: #333 !important;
|
||||
|
||||
&-selected {
|
||||
background-color: #f0f5ff !important;
|
||||
color: #333 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
html[data-theme='dark'] {
|
||||
.jeesite-cms-ai {
|
||||
.ant-menu-light .ant-menu-item {
|
||||
color: #ddd !important;
|
||||
|
||||
&-selected {
|
||||
background-color: #333 !important;
|
||||
color: #ddd !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
23
web-vue/packages/core/README.md
Normal file
@@ -0,0 +1,23 @@
|
||||
|
||||
- 官方网站:<https://jeesite.com>
|
||||
- 使用文档:<https://jeesite.com/docs>
|
||||
- 后端代码:<https://gitee.com/thinkgem/jeesite5>
|
||||
- 前端代码:<https://gitee.com/thinkgem/jeesite-vue>
|
||||
|
||||
------
|
||||
|
||||
<div align="center">
|
||||
如果你喜欢 JeeSite,请给她一个 ⭐️ Star,您的支持将是我们前行的动力。
|
||||
</div>
|
||||
|
||||
------
|
||||
|
||||
- 问题反馈:<https://gitee.com/thinkgem/jeesite-vue/issues> [【新手必读】](https://gitee.com/thinkgem/jeesite5/issues/I18ARR)
|
||||
- 需求收集:<https://gitee.com/thinkgem/jeesite-vue/issues/new>
|
||||
- QQ 群:`127515876`、`209330483`、`223507718`、`709534275`、`730390092`、`1373527`、`183903863(外包)`
|
||||
- 微信群:添加客服微信 <http://s.jeesite.com> 邀请您进群
|
||||
- 关注微信公众号,了解最新动态:
|
||||
|
||||
<p style="padding-left:40px">
|
||||
<img alt="JeeSite微信公众号" src="https://jeesite.com/assets/images/mp.png" width="220" height="220">
|
||||
</p>
|
||||
1
web-vue/packages/core/api/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
// export * from './sys';
|
||||
56
web-vue/packages/core/api/model/baseModel.ts
Normal file
@@ -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<T> {
|
||||
pageNo: number;
|
||||
pageSize: number;
|
||||
orderBy: string;
|
||||
count: number;
|
||||
list: T[];
|
||||
}
|
||||
|
||||
export interface BasicModel<T> extends Result, Recordable {
|
||||
id: string;
|
||||
page: Page<T>;
|
||||
isNewRecord: boolean;
|
||||
dataMap: Map<string, any>;
|
||||
|
||||
createBy?: string;
|
||||
createDate?: string;
|
||||
|
||||
updateBy?: string;
|
||||
updateDate?: string;
|
||||
|
||||
status?: string;
|
||||
}
|
||||
|
||||
export interface TreeModel<T> extends BasicModel<T> {
|
||||
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;
|
||||
}
|
||||
43
web-vue/packages/core/api/msg/msgInner.ts
Normal file
@@ -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<MsgInner> {
|
||||
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<MsgInner>({ url: adminPath + '/msg/msgInner/list', params });
|
||||
|
||||
export const msgInnerListData = (params?: MsgInner | any) =>
|
||||
defHttp.post<Page<MsgInner>>({ url: adminPath + '/msg/msgInner/listData', params });
|
||||
|
||||
export const msgInnerForm = (params?: MsgInner | any) =>
|
||||
defHttp.get<MsgInner>({ url: adminPath + '/msg/msgInner/form', params });
|
||||
|
||||
export const msgInnerView = (params?: MsgInner | any) =>
|
||||
defHttp.get<MsgInner>({ url: adminPath + '/msg/msgInner/view', params });
|
||||
|
||||
export const msgInnerSave = (params?: any, data?: MsgInner | any) =>
|
||||
defHttp.postJson<MsgInner>({ url: adminPath + '/msg/msgInner/save', params, data });
|
||||
|
||||
export const msgInnerDelete = (params?: MsgInner | any) =>
|
||||
defHttp.get<MsgInner>({ url: adminPath + '/msg/msgInner/delete', params });
|
||||
51
web-vue/packages/core/api/sys/account.ts
Normal file
@@ -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' });
|
||||
};
|
||||
38
web-vue/packages/core/api/sys/area.ts
Normal file
@@ -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<Area> {
|
||||
areaCode?: string; // 区域编码
|
||||
areaName?: string; // 区域名称
|
||||
areaType?: string; // 区域类型
|
||||
}
|
||||
|
||||
export const areaList = (params?: Area | any) => defHttp.get<Area>({ url: adminPath + '/sys/area/list', params });
|
||||
|
||||
export const areaListData = (params?: Area | any) =>
|
||||
defHttp.post<Page<Area>>({ url: adminPath + '/sys/area/listPageData', params });
|
||||
|
||||
export const areaForm = (params?: Area | any) => defHttp.get<Area>({ url: adminPath + '/sys/area/form', params });
|
||||
|
||||
export const areaCreateNextNode = (params?: Area | any) =>
|
||||
defHttp.get<Area>({ url: adminPath + '/sys/area/createNextNode', params });
|
||||
|
||||
export const areaSave = (params?: any, data?: Area | any) =>
|
||||
defHttp.postJson<Area>({ url: adminPath + '/sys/area/save', params, data });
|
||||
|
||||
export const areaDisable = (params?: Area | any) => defHttp.get<Area>({ url: adminPath + '/sys/area/disable', params });
|
||||
|
||||
export const areaEnable = (params?: Area | any) => defHttp.get<Area>({ url: adminPath + '/sys/area/enable', params });
|
||||
|
||||
export const areaDelete = (params?: Area | any) => defHttp.get<Area>({ url: adminPath + '/sys/area/delete', params });
|
||||
|
||||
export const areaTreeData = (params?: any) =>
|
||||
defHttp.get<TreeDataModel[]>({ url: adminPath + '/sys/area/treeData', params });
|
||||
46
web-vue/packages/core/api/sys/company.ts
Normal file
@@ -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<Company> {
|
||||
companyCode?: string; // 公司编码
|
||||
viewCode?: string; // 公司代码
|
||||
companyName?: string; // 公司名称
|
||||
fullName?: string; // 公司全称
|
||||
areaCode?: string; // 区域编码
|
||||
extend?: any; // 扩展字段
|
||||
}
|
||||
|
||||
export const companyList = (params?: Company | any) =>
|
||||
defHttp.get<Company>({ url: adminPath + '/sys/company/list', params });
|
||||
|
||||
export const companyListData = (params?: Company | any) =>
|
||||
defHttp.post<Company[]>({ url: adminPath + '/sys/company/listData', params });
|
||||
|
||||
export const companyForm = (params?: Company | any) =>
|
||||
defHttp.get<Company>({ url: adminPath + '/sys/company/form', params });
|
||||
|
||||
export const companyCreateNextNode = (params?: Company | any) =>
|
||||
defHttp.get<Company>({ url: adminPath + '/sys/company/createNextNode', params });
|
||||
|
||||
export const companySave = (params?: any, data?: Company | any) =>
|
||||
defHttp.postJson<Company>({ url: adminPath + '/sys/company/save', params, data });
|
||||
|
||||
export const companyDisable = (params?: Company | any) =>
|
||||
defHttp.get<Company>({ url: adminPath + '/sys/company/disable', params });
|
||||
|
||||
export const companyEnable = (params?: Company | any) =>
|
||||
defHttp.get<Company>({ url: adminPath + '/sys/company/enable', params });
|
||||
|
||||
export const companyDelete = (params?: Company | any) =>
|
||||
defHttp.get<Company>({ url: adminPath + '/sys/company/delete', params });
|
||||
|
||||
export const companyTreeData = (params?: any) =>
|
||||
defHttp.get<TreeDataModel[]>({ url: adminPath + '/sys/company/treeData', params });
|
||||
38
web-vue/packages/core/api/sys/config.ts
Normal file
@@ -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<Config> {
|
||||
configName?: string; // 名称
|
||||
configKey?: string; // 参数键
|
||||
configValue?: string; // 参数值
|
||||
isSys?: string; // 系统内置(1是 0否)
|
||||
}
|
||||
|
||||
export const configList = (params?: Config | any) =>
|
||||
defHttp.get<Config>({ url: adminPath + '/sys/config/list', params });
|
||||
|
||||
export const configListData = (params?: Config | any) =>
|
||||
defHttp.post<Page<Config>>({ url: adminPath + '/sys/config/listData', params });
|
||||
|
||||
export const configForm = (params?: Config | any) =>
|
||||
defHttp.get<Config>({ url: adminPath + '/sys/config/form', params });
|
||||
|
||||
export const checkConfigKey = (oldConfigKey: string, configKey: string) =>
|
||||
defHttp.get<Config>({
|
||||
url: adminPath + '/sys/config/checkConfigKey',
|
||||
params: { oldConfigKey, configKey },
|
||||
});
|
||||
|
||||
export const configSave = (params?: any, data?: Config | any) =>
|
||||
defHttp.postJson<Config>({ url: adminPath + '/sys/config/save', params, data });
|
||||
|
||||
export const configDelete = (params?: Config | any) =>
|
||||
defHttp.get<Config>({ url: adminPath + '/sys/config/delete', params });
|
||||
43
web-vue/packages/core/api/sys/corpAdmin.ts
Normal file
@@ -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<User>({ url: adminPath + '/sys/corpAdmin/list', params });
|
||||
|
||||
export const corpAdminListData = (params?: User | any) =>
|
||||
defHttp.post<Page<User>>({ url: adminPath + '/sys/corpAdmin/listData', params });
|
||||
|
||||
export const corpAdminForm = (params?: User | any) =>
|
||||
defHttp.get<User>({ url: adminPath + '/sys/corpAdmin/form', params });
|
||||
|
||||
export const corpAdminSave = (params?: any, data?: User | any) =>
|
||||
defHttp.postJson<User>({ url: adminPath + '/sys/corpAdmin/save', params, data });
|
||||
|
||||
export const corpAdminDisable = (params?: User | any) =>
|
||||
defHttp.get<User>({ url: adminPath + '/sys/corpAdmin/disable', params });
|
||||
|
||||
export const corpAdminEnable = (params?: User | any) =>
|
||||
defHttp.get<User>({ url: adminPath + '/sys/corpAdmin/enable', params });
|
||||
|
||||
export const corpAdminResetpwd = (params?: User | any) =>
|
||||
defHttp.get<User>({ url: adminPath + '/sys/corpAdmin/resetpwd', params });
|
||||
|
||||
export const corpAdminDelete = (params?: User | any) =>
|
||||
defHttp.get<User>({ url: adminPath + '/sys/corpAdmin/delete', params });
|
||||
|
||||
export const corpAdminTreeData = (params?: any) =>
|
||||
defHttp.get<TreeDataModel[]>({ url: adminPath + '/sys/corpAdmin/treeData', params });
|
||||
|
||||
export const switchCorp = (corpCode: string) =>
|
||||
defHttp.get<User>({
|
||||
url: adminPath + '/sys/corpAdmin/switch/' + corpCode,
|
||||
});
|
||||
55
web-vue/packages/core/api/sys/dictData.ts
Normal file
@@ -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<DictData> {
|
||||
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<DictData>({ url: adminPath + '/sys/dictData/list', params });
|
||||
|
||||
export const dictDataListData = (params?: DictData | any) =>
|
||||
defHttp.post<DictData[]>({ url: adminPath + '/sys/dictData/listData', params });
|
||||
|
||||
export const dictDataForm = (params?: DictData | any) =>
|
||||
defHttp.get<DictData>({ url: adminPath + '/sys/dictData/form', params });
|
||||
|
||||
export const dictDataCreateNextNode = (params?: DictData | any) =>
|
||||
defHttp.get<DictData>({ url: adminPath + '/sys/dictData/createNextNode', params });
|
||||
|
||||
export const dictDataSave = (params?: any, data?: DictData | any) =>
|
||||
defHttp.postJson<DictData>({ url: adminPath + '/sys/dictData/save', params, data });
|
||||
|
||||
export const dictDataDisable = (params?: DictData | any) =>
|
||||
defHttp.get<DictData>({ url: adminPath + '/sys/dictData/disable', params });
|
||||
|
||||
export const dictDataEnable = (params?: DictData | any) =>
|
||||
defHttp.get<DictData>({ url: adminPath + '/sys/dictData/enable', params });
|
||||
|
||||
export const dictDataDelete = (params?: DictData | any) =>
|
||||
defHttp.get<DictData>({ url: adminPath + '/sys/dictData/delete', params });
|
||||
|
||||
export const dictDataTreeData = (params?: any) =>
|
||||
defHttp.get<DictDataTree[]>({ url: adminPath + '/sys/dictData/treeData', params });
|
||||
40
web-vue/packages/core/api/sys/dictType.ts
Normal file
@@ -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<DictType> {
|
||||
dictName?: string; // 字典名称
|
||||
dictType?: string; // 字典类型
|
||||
isSys?: string; // 是否系统字典
|
||||
}
|
||||
|
||||
export const dictTypeList = (params?: DictType | any) =>
|
||||
defHttp.get<DictType>({ url: adminPath + '/sys/dictType/list', params });
|
||||
|
||||
export const dictTypeListData = (params?: DictType | any) =>
|
||||
defHttp.post<Page<DictType>>({ url: adminPath + '/sys/dictType/listData', params });
|
||||
|
||||
export const dictTypeForm = (params?: DictType | any) =>
|
||||
defHttp.get<DictType>({ url: adminPath + '/sys/dictType/form', params });
|
||||
|
||||
export const dictTypeSave = (params?: any, data?: DictType | any) =>
|
||||
defHttp.postJson<DictType>({ url: adminPath + '/sys/dictType/save', params, data });
|
||||
|
||||
export const checkDictType = (oldDictType: string, dictType: string) =>
|
||||
defHttp.get<DictType>({
|
||||
url: adminPath + '/sys/dictType/checkDictType',
|
||||
params: { oldDictType, dictType },
|
||||
});
|
||||
|
||||
export const dictTypeDelete = (params?: DictType | any) =>
|
||||
defHttp.get<DictType>({ url: adminPath + '/sys/dictType/delete', params });
|
||||
|
||||
export const dictTypeTreeData = (params?: any) =>
|
||||
defHttp.get<TreeDataModel[]>({ url: adminPath + '/sys/dictType/treeData', params });
|
||||
96
web-vue/packages/core/api/sys/empUser.ts
Normal file
@@ -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<Employee> {
|
||||
empCode?: string;
|
||||
empNo?: string;
|
||||
empName?: string;
|
||||
empNameEn?: string;
|
||||
office?: Office;
|
||||
company?: Company;
|
||||
}
|
||||
|
||||
export const empUserIndex = (params?: EmpUser | any) =>
|
||||
defHttp.get<EmpUser>({ url: adminPath + '/sys/empUser/index', params });
|
||||
|
||||
export const empUserList = (params?: EmpUser | any) =>
|
||||
defHttp.get<EmpUser>({ url: adminPath + '/sys/empUser/list', params });
|
||||
|
||||
export const empUserListData = (params?: EmpUser | any) =>
|
||||
defHttp.post<Page<EmpUser>>({ url: adminPath + '/sys/empUser/listData', params });
|
||||
|
||||
export const empUserForm = (params?: EmpUser | any) =>
|
||||
defHttp.get<EmpUser>({ url: adminPath + '/sys/empUser/form', params });
|
||||
|
||||
export const empUserSave = (params?: any, data?: EmpUser | any) =>
|
||||
defHttp.postJson<EmpUser>({ url: adminPath + '/sys/empUser/save', params, data });
|
||||
|
||||
export const checkEmpNo = (oldEmpNo: string, empNo: string) =>
|
||||
defHttp.get<EmpUser>({
|
||||
url: adminPath + '/sys/empUser/checkEmpNo',
|
||||
params: { oldEmpNo, 'employee.empNo': empNo },
|
||||
});
|
||||
|
||||
export const empUserImportData = (
|
||||
params: UploadFileParams,
|
||||
onUploadProgress: (progressEvent: AxiosProgressEvent) => void,
|
||||
) =>
|
||||
defHttp.uploadFile<UploadApiResult>(
|
||||
{
|
||||
url: ctxPath + adminPath + '/sys/empUser/importData',
|
||||
onUploadProgress,
|
||||
},
|
||||
params,
|
||||
);
|
||||
|
||||
export const empUserDisable = (params?: EmpUser | any) =>
|
||||
defHttp.get<EmpUser>({ url: adminPath + '/sys/empUser/disable', params });
|
||||
|
||||
export const empUserEnable = (params?: EmpUser | any) =>
|
||||
defHttp.get<EmpUser>({ url: adminPath + '/sys/empUser/enable', params });
|
||||
|
||||
export const resetpwd = (params?: EmpUser | any) =>
|
||||
defHttp.get<EmpUser>({ url: adminPath + '/sys/empUser/resetpwd', params });
|
||||
|
||||
export const empUserDelete = (params?: EmpUser | any) =>
|
||||
defHttp.get<EmpUser>({ url: adminPath + '/sys/empUser/delete', params }, { errorMessageMode: 'none' });
|
||||
|
||||
export const formAuthDataScope = (params?: EmpUser | any) =>
|
||||
defHttp.get<EmpUser>({ url: adminPath + '/sys/empUser/formAuthDataScope', params });
|
||||
|
||||
export const ctrlDataTreeData = (params?: any) => {
|
||||
const { url, ...params2 } = params;
|
||||
return defHttp.get<EmpUser>({ url: adminPath + url, params: params2 });
|
||||
};
|
||||
|
||||
export const saveAuthDataScope = (params?: EmpUser | any) =>
|
||||
defHttp.post<EmpUser>({ url: adminPath + '/sys/empUser/saveAuthDataScope', params });
|
||||
|
||||
export const empUserTreeData = (params?: any) =>
|
||||
defHttp.get<TreeDataModel[]>({ url: adminPath + '/sys/empUser/treeData', params });
|
||||
|
||||
export const empUserOfficeListData = (params?: any) =>
|
||||
defHttp.get<Recordable[]>({ url: adminPath + '/sys/empUser/officeListData', params });
|
||||
|
||||
export const empUserSwitchOffice = (officeCode: string) =>
|
||||
defHttp.get<User>({
|
||||
url: adminPath + '/sys/empUser/switchOffice' + (officeCode ? '/' + officeCode : ''),
|
||||
});
|
||||
18
web-vue/packages/core/api/sys/index.ts
Normal file
@@ -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';
|
||||
38
web-vue/packages/core/api/sys/log.ts
Normal file
@@ -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<Log> {
|
||||
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<Log>({ url: adminPath + '/sys/log/list', params });
|
||||
|
||||
export const logListData = (params?: Log | any) =>
|
||||
defHttp.post<Page<Log>>({ url: adminPath + '/sys/log/listData', params });
|
||||
|
||||
export const logForm = (params?: Log | any) => defHttp.get<Log>({ url: adminPath + '/sys/log/form', params });
|
||||
98
web-vue/packages/core/api/sys/login.ts
Normal file
@@ -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<LoginResult>(
|
||||
{ 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<LoginResult>({ url: adminPath + '/index', timeout: 10 * 1000 }, { errorMessageMode: mode });
|
||||
|
||||
export const authInfoApi = () => defHttp.get<AuthInfo>({ url: adminPath + '/authInfo' });
|
||||
|
||||
export const menuRouteApi = () => defHttp.get<Menu[]>({ url: adminPath + '/menuRoute' });
|
||||
|
||||
export const logoutApi = () => defHttp.get({ url: adminPath + '/logout' });
|
||||
50
web-vue/packages/core/api/sys/menu.ts
Normal file
@@ -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<Menu> {
|
||||
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<Menu>({ url: adminPath + '/sys/menu/index', params });
|
||||
|
||||
export const menuList = (params?: Menu | any) => defHttp.get<Menu>({ url: adminPath + '/sys/menu/list', params });
|
||||
|
||||
export const menuListData = (params?: Menu | any) =>
|
||||
defHttp.post<Menu[]>({ url: adminPath + '/sys/menu/listData', params });
|
||||
|
||||
export const menuForm = (params?: Menu | any) => defHttp.get<Menu>({ url: adminPath + '/sys/menu/form', params });
|
||||
|
||||
export const menuCreateNextNode = (params?: Menu | any) =>
|
||||
defHttp.get<Menu>({ url: adminPath + '/sys/menu/createNextNode', params });
|
||||
|
||||
export const menuSave = (params?: any, data?: Menu | any) =>
|
||||
defHttp.postJson<Menu>({ url: adminPath + '/sys/menu/save', params, data });
|
||||
|
||||
export const menuDisable = (params?: Menu | any) => defHttp.get<Menu>({ url: adminPath + '/sys/menu/disable', params });
|
||||
|
||||
export const menuEnable = (params?: Menu | any) => defHttp.get<Menu>({ url: adminPath + '/sys/menu/enable', params });
|
||||
|
||||
export const menuDelete = (params?: Menu | any) => defHttp.get<Menu>({ url: adminPath + '/sys/menu/delete', params });
|
||||
|
||||
export const menuTreeData = (params?: any) =>
|
||||
defHttp.get<TreeDataModel[]>({ url: adminPath + '/sys/menu/treeData', params });
|
||||
43
web-vue/packages/core/api/sys/module.ts
Normal file
@@ -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<Module> {
|
||||
moduleCode?: string; // 模块编码
|
||||
moduleName?: string; // 模块名称
|
||||
description?: string; // 模块描述
|
||||
mainClassName?: string; // 主类全名
|
||||
currentVersion?: string; // 当前版本
|
||||
upgradeInfo?: string; // 升级信息
|
||||
}
|
||||
|
||||
export const moduleList = (params?: Module | any) =>
|
||||
defHttp.get<Module>({ url: adminPath + '/sys/module/list', params });
|
||||
|
||||
export const moduleListData = (params?: Module | any) =>
|
||||
defHttp.post<Page<Module>>({ url: adminPath + '/sys/module/listData', params });
|
||||
|
||||
export const moduleSelectData = (params?: Module | any) =>
|
||||
defHttp.post<Recordable[]>({ url: adminPath + '/sys/module/selectData', params });
|
||||
|
||||
export const moduleForm = (params?: Module | any) =>
|
||||
defHttp.get<Module>({ url: adminPath + '/sys/module/form', params });
|
||||
|
||||
export const moduleSave = (params?: any, data?: Module | any) =>
|
||||
defHttp.postJson<Module>({ url: adminPath + '/sys/module/save', params, data });
|
||||
|
||||
export const moduleDisable = (params?: Module | any) =>
|
||||
defHttp.get<Module>({ url: adminPath + '/sys/module/disable', params });
|
||||
|
||||
export const moduleEnable = (params?: Module | any) =>
|
||||
defHttp.get<Module>({ url: adminPath + '/sys/module/enable', params });
|
||||
|
||||
export const moduleDelete = (params?: Module | any) =>
|
||||
defHttp.get<Module>({ url: adminPath + '/sys/module/delete', params });
|
||||
68
web-vue/packages/core/api/sys/office.ts
Normal file
@@ -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<Office> {
|
||||
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<Office>({ url: adminPath + '/sys/office/list', params });
|
||||
|
||||
export const officeListData = (params?: Office | any) =>
|
||||
defHttp.post<Office[]>({ url: adminPath + '/sys/office/listData', params });
|
||||
|
||||
export const officeForm = (params?: Office | any) =>
|
||||
defHttp.get<Office>({ url: adminPath + '/sys/office/form', params });
|
||||
|
||||
export const officeCreateNextNode = (params?: Office | any) =>
|
||||
defHttp.get<Office>({ url: adminPath + '/sys/office/createNextNode', params });
|
||||
|
||||
export const officeSave = (params?: any, data?: Office | any) =>
|
||||
defHttp.postJson<Office>({ url: adminPath + '/sys/office/save', params, data });
|
||||
|
||||
export const officeImportData = (
|
||||
params: UploadFileParams,
|
||||
onUploadProgress: (progressEvent: AxiosProgressEvent) => void,
|
||||
) =>
|
||||
defHttp.uploadFile<UploadApiResult>(
|
||||
{
|
||||
url: ctxPath + adminPath + '/sys/office/importData',
|
||||
onUploadProgress,
|
||||
},
|
||||
params,
|
||||
);
|
||||
|
||||
export const officeDisable = (params?: Office | any) =>
|
||||
defHttp.get<Office>({ url: adminPath + '/sys/office/disable', params });
|
||||
|
||||
export const officeEnable = (params?: Office | any) =>
|
||||
defHttp.get<Office>({ url: adminPath + '/sys/office/enable', params });
|
||||
|
||||
export const officeDelete = (params?: Office | any) =>
|
||||
defHttp.get<Office>({ url: adminPath + '/sys/office/delete', params });
|
||||
|
||||
export const officeTreeData = (params?: any) =>
|
||||
defHttp.get<TreeDataModel[]>({ url: adminPath + '/sys/office/treeData', params });
|
||||
34
web-vue/packages/core/api/sys/online.ts
Normal file
@@ -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<Online>({ url: adminPath + '/sys/online/list', params });
|
||||
|
||||
export const onlineListData = (params?: Online | any) =>
|
||||
defHttp.post<Online[]>({ url: adminPath + '/sys/online/listData', params });
|
||||
|
||||
export const onlineTickOut = (params?: Online | any) =>
|
||||
defHttp.post<Result>({ url: adminPath + '/sys/online/tickOut', params });
|
||||
|
||||
export const onlineCount = () =>
|
||||
defHttp.post<any>({ url: adminPath + '/sys/online/count?__notUpdateSession=true' }, { errorMessageMode: 'none' });
|
||||
37
web-vue/packages/core/api/sys/post.ts
Normal file
@@ -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<Post> {
|
||||
postCode?: string; // 岗位编码
|
||||
postName?: string; // 岗位名称
|
||||
postType?: string; // 岗位分类(高管、中层、基层)
|
||||
postSort?: number; // 岗位排序(升序)
|
||||
viewCode?: string; // 岗位代码
|
||||
}
|
||||
|
||||
export const postList = (params?: Post | any) => defHttp.get<Post>({ url: adminPath + '/sys/post/list', params });
|
||||
|
||||
export const postListData = (params?: Post | any) =>
|
||||
defHttp.post<Page<Post>>({ url: adminPath + '/sys/post/listData', params });
|
||||
|
||||
export const postForm = (params?: Post | any) => defHttp.get<Post>({ url: adminPath + '/sys/post/form', params });
|
||||
|
||||
export const postDisable = (params?: Post | any) => defHttp.get<Post>({ url: adminPath + '/sys/post/disable', params });
|
||||
|
||||
export const postEnable = (params?: Post | any) => defHttp.get<Post>({ url: adminPath + '/sys/post/enable', params });
|
||||
|
||||
export const postSave = (params?: any, data?: Post | any) =>
|
||||
defHttp.postJson<Post>({ url: adminPath + '/sys/post/save', params, data });
|
||||
|
||||
export const postDelete = (params?: Post | any) => defHttp.get<Post>({ url: adminPath + '/sys/post/delete', params });
|
||||
|
||||
export const postTreeData = (params?: any) =>
|
||||
defHttp.get<TreeDataModel[]>({ url: adminPath + '/sys/post/treeData', params });
|
||||
77
web-vue/packages/core/api/sys/role.ts
Normal file
@@ -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<Role> {
|
||||
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<Role>({ url: adminPath + '/sys/role/list', params });
|
||||
|
||||
export const roleListData = (params?: Role | any) =>
|
||||
defHttp.post<Page<Role>>({ url: adminPath + '/sys/role/listData', params });
|
||||
|
||||
export const roleForm = (params?: Role | any) => defHttp.get<Role>({ url: adminPath + '/sys/role/form', params });
|
||||
|
||||
export const roleMenuTreeData = (params?: any) =>
|
||||
defHttp.get<Recordable>({ url: adminPath + '/sys/role/menuTreeData', params });
|
||||
|
||||
export const menuTreeDataByRoleCode = (params?: any) =>
|
||||
defHttp.get<Recordable>({ url: adminPath + '/sys/role/menuTreeDataByRoleCode', params });
|
||||
|
||||
export const roleSave = (params?: any, data?: Role | any) =>
|
||||
defHttp.postJson<Role>({ url: adminPath + '/sys/role/save', params, data });
|
||||
|
||||
export const checkRoleName = (oldRoleName: string, roleName: string) =>
|
||||
defHttp.get<Role>({
|
||||
url: adminPath + '/sys/role/checkRoleName',
|
||||
params: { oldRoleName, roleName },
|
||||
});
|
||||
|
||||
export const roleDisable = (params?: Role | any) => defHttp.get<Role>({ url: adminPath + '/sys/role/disable', params });
|
||||
|
||||
export const roleEnable = (params?: Role | any) => defHttp.get<Role>({ url: adminPath + '/sys/role/enable', params });
|
||||
|
||||
export const roleDelete = (params?: Role | any) => defHttp.get<Role>({ url: adminPath + '/sys/role/delete', params });
|
||||
|
||||
export const roleFormAuthDataScope = (params?: Role | any) =>
|
||||
defHttp.get<Role>({ url: adminPath + '/sys/role/formAuthDataScope', params });
|
||||
|
||||
export const roleCtrlDataTreeData = (params?: any) => {
|
||||
const { url, ...params2 } = params;
|
||||
return defHttp.get<Role>({ url: adminPath + url, params: params2 });
|
||||
};
|
||||
|
||||
export const roleSaveAuthDataScope = (params?: Role | any) =>
|
||||
defHttp.post<Role>({ url: adminPath + '/sys/role/saveAuthDataScope', params });
|
||||
|
||||
export const formAuthUser = (params?: Role | any) =>
|
||||
defHttp.get<Role>({ url: adminPath + '/sys/role/formAuthUser', params });
|
||||
|
||||
export const saveAuthUser = (params?: Role | any) =>
|
||||
defHttp.post<Role>({ url: adminPath + '/sys/role/saveAuthUser', params });
|
||||
|
||||
export const deleteAuthUser = (params?: Role | any) =>
|
||||
defHttp.post<Role>({ url: adminPath + '/sys/role/deleteAuthUser', params });
|
||||
|
||||
export const roleTreeData = (params?: any) =>
|
||||
defHttp.get<TreeDataModel[]>({ url: adminPath + '/sys/role/treeData', params });
|
||||
25
web-vue/packages/core/api/sys/secAdmin.ts
Normal file
@@ -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<User>({ url: adminPath + '/sys/secAdmin/list', params });
|
||||
|
||||
export const secAdminListData = (params?: User | any) =>
|
||||
defHttp.post<Page<User>>({ url: adminPath + '/sys/secAdmin/listData', params });
|
||||
|
||||
export const secAdminForm = (params?: User | any) =>
|
||||
defHttp.get<User>({ url: adminPath + '/sys/secAdmin/form', params });
|
||||
|
||||
export const secAdminSave = (params?: any) => defHttp.post<User>({ url: adminPath + '/sys/secAdmin/save', params });
|
||||
|
||||
export const secAdminDelete = (params?: User | any) =>
|
||||
defHttp.get<User>({ url: adminPath + '/sys/secAdmin/delete', params });
|
||||
82
web-vue/packages/core/api/sys/upload.ts
Normal file
@@ -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<FileEntity> {
|
||||
fileId: string;
|
||||
fileMd5: string;
|
||||
filePath: string;
|
||||
fileContentType: string;
|
||||
fileExtension: string;
|
||||
fileSize: number;
|
||||
fileMeta: string;
|
||||
fileMetaMap: any;
|
||||
filePreview: string;
|
||||
}
|
||||
export interface FileUpload extends BasicModel<FileUpload> {
|
||||
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<UploadApiResult>(
|
||||
{
|
||||
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<FileUpload[]>(
|
||||
{ url: apiFileListUrl || ctxPath + adminPath + '/file/fileList', params },
|
||||
{ errorMessageMode: 'none', apiUrl: '', urlPrefix: '' },
|
||||
);
|
||||
|
||||
export const uploadParams = () => defHttp.get<UploadParams>({ url: adminPath + '/file/params' });
|
||||
75
web-vue/packages/core/api/sys/user.ts
Normal file
@@ -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<User> {
|
||||
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<Page<User>>({ url: adminPath + '/sys/user/listData', params });
|
||||
|
||||
export const checkLoginCode = (oldLoginCode: string, loginCode: string) =>
|
||||
defHttp.get<User>({
|
||||
url: adminPath + '/sys/user/checkLoginCode',
|
||||
params: { oldLoginCode, loginCode },
|
||||
});
|
||||
|
||||
export const userInfo = (params?: any) => defHttp.post<User>({ url: adminPath + '/sys/user/info', params });
|
||||
|
||||
export const infoSaveBase = (params?: any) => defHttp.post<User>({ 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<User>({ 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<User>({ url: adminPath + '/sys/user/infoSavePqa', params });
|
||||
};
|
||||
15
web-vue/packages/core/components/Application/index.ts
Normal file
@@ -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);
|
||||
@@ -0,0 +1,79 @@
|
||||
<template>
|
||||
<div v-if="getShowDarkModeToggle" :class="getClass" @click="toggleDarkMode">
|
||||
<div :class="`${prefixCls}-inner`"></div>
|
||||
<Icon icon="i-svg:sun" size="14" />
|
||||
<Icon icon="i-svg:moon" size="14" />
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { computed, unref } from 'vue';
|
||||
import { Icon } from '@jeesite/core/components/Icon';
|
||||
import { useDesign } from '@jeesite/core/hooks/web/useDesign';
|
||||
import { useRootSetting } from '@jeesite/core/hooks/setting/useRootSetting';
|
||||
import { updateHeaderBgColor, updateSidebarBgColor } from '@jeesite/core/logics/theme/updateBackground';
|
||||
import { updateDarkTheme } from '@jeesite/core/logics/theme/dark';
|
||||
import { ThemeEnum } from '@jeesite/core/enums/appEnum';
|
||||
|
||||
const { prefixCls } = useDesign('dark-switch');
|
||||
const { getDarkMode, setDarkMode, getShowDarkModeToggle } = useRootSetting();
|
||||
|
||||
const isDark = computed(() => getDarkMode.value === ThemeEnum.DARK);
|
||||
|
||||
const getClass = computed(() => [
|
||||
prefixCls,
|
||||
{
|
||||
[`${prefixCls}--dark`]: unref(isDark),
|
||||
},
|
||||
]);
|
||||
|
||||
function toggleDarkMode() {
|
||||
const darkMode = getDarkMode.value === ThemeEnum.DARK ? ThemeEnum.LIGHT : ThemeEnum.DARK;
|
||||
setDarkMode(darkMode);
|
||||
updateDarkTheme(darkMode);
|
||||
updateHeaderBgColor();
|
||||
updateSidebarBgColor();
|
||||
}
|
||||
</script>
|
||||
<style lang="less">
|
||||
@prefix-cls: ~'jeesite-dark-switch';
|
||||
|
||||
html[data-theme='dark'] {
|
||||
.@{prefix-cls} {
|
||||
border: 1px solid rgb(196 188 188);
|
||||
}
|
||||
}
|
||||
|
||||
.@{prefix-cls} {
|
||||
z-index: 10 !important;
|
||||
position: relative !important;
|
||||
display: flex;
|
||||
width: 50px;
|
||||
height: 26px;
|
||||
padding: 0 6px;
|
||||
margin-left: auto;
|
||||
cursor: pointer;
|
||||
background-color: #151515;
|
||||
border-radius: 30px;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
&-inner {
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
background-color: #fff;
|
||||
border-radius: 50%;
|
||||
transition:
|
||||
transform 0.5s,
|
||||
background-color 0.5s;
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
&--dark {
|
||||
.@{prefix-cls}-inner {
|
||||
transform: translateX(calc(100% + 2px));
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,76 @@
|
||||
<!--
|
||||
* @Author: Vben
|
||||
* @Description: Multi-language switching component
|
||||
-->
|
||||
<template>
|
||||
<Dropdown
|
||||
placement="bottom"
|
||||
:trigger="['click']"
|
||||
:dropMenuList="localeList"
|
||||
:selectedKeys="selectedKeys"
|
||||
@menu-event="handleMenuEvent"
|
||||
overlayClassName="app-locale-picker-overlay"
|
||||
>
|
||||
<span class="flex cursor-pointer items-center">
|
||||
<Icon icon="i-ion:language" />
|
||||
<span v-if="showText" class="ml-1">{{ getLocaleText }}</span>
|
||||
</span>
|
||||
</Dropdown>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import type { LocaleType } from '@jeesite/types/config';
|
||||
import type { DropMenu } from '@jeesite/core/components/Dropdown';
|
||||
import { ref, watchEffect, unref, computed } from 'vue';
|
||||
import { Dropdown } from '@jeesite/core/components/Dropdown';
|
||||
import { Icon } from '@jeesite/core/components/Icon';
|
||||
import { useLocale } from '@jeesite/core/locales/useLocale';
|
||||
import { localeList } from '@jeesite/core/settings/localeSetting';
|
||||
|
||||
const props = defineProps({
|
||||
/**
|
||||
* Whether to display text
|
||||
*/
|
||||
showText: { type: Boolean, default: true },
|
||||
/**
|
||||
* Whether to refresh the interface when changing
|
||||
*/
|
||||
reload: { type: Boolean },
|
||||
});
|
||||
|
||||
const selectedKeys = ref<string[]>([]);
|
||||
|
||||
const { changeLocale, getLocale } = useLocale();
|
||||
|
||||
const getLocaleText = computed(() => {
|
||||
const key = selectedKeys.value[0];
|
||||
if (!key) {
|
||||
return '';
|
||||
}
|
||||
return localeList.find((item) => item.event === key)?.text;
|
||||
});
|
||||
|
||||
watchEffect(() => {
|
||||
selectedKeys.value = [unref(getLocale)];
|
||||
});
|
||||
|
||||
async function toggleLocale(lang: LocaleType | string) {
|
||||
await changeLocale(lang as LocaleType);
|
||||
selectedKeys.value = [lang as string];
|
||||
props.reload && location.reload();
|
||||
}
|
||||
|
||||
function handleMenuEvent(menu: DropMenu) {
|
||||
if (unref(getLocale) === menu.event) {
|
||||
return;
|
||||
}
|
||||
toggleLocale(menu.event as string);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
.app-locale-picker-overlay {
|
||||
.ant-dropdown-menu-item {
|
||||
min-width: 160px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
98
web-vue/packages/core/components/Application/src/AppLogo.vue
Normal file
@@ -0,0 +1,98 @@
|
||||
<!--
|
||||
* @Author: Vben
|
||||
* @Description: logo component
|
||||
-->
|
||||
<template>
|
||||
<div class="anticon" :class="getAppLogoClass" @click="goHome">
|
||||
<img src="@jeesite/assets/images/logo.png" v-show="!!!showTitle" />
|
||||
<div class="ml-2 truncate md:opacity-100" :class="getTitleClass" v-show="showTitle">
|
||||
{{ getTitle }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { computed, unref } from 'vue';
|
||||
import { useGlobSetting } from '@jeesite/core/hooks/setting';
|
||||
import { useGo } from '@jeesite/core/hooks/web/usePage';
|
||||
import { useMenuSetting } from '@jeesite/core/hooks/setting/useMenuSetting';
|
||||
import { useDesign } from '@jeesite/core/hooks/web/useDesign';
|
||||
import { PageEnum } from '@jeesite/core/enums/pageEnum';
|
||||
import { useUserStore } from '@jeesite/core/store/modules/user';
|
||||
|
||||
const props = defineProps({
|
||||
/**
|
||||
* The theme of the current parent component
|
||||
*/
|
||||
theme: { type: String, validator: (v: string) => ['light', 'dark'].includes(v) },
|
||||
/**
|
||||
* Whether to show title
|
||||
*/
|
||||
showTitle: { type: Boolean, default: true },
|
||||
/**
|
||||
* The title is also displayed when the menu is collapsed
|
||||
*/
|
||||
alwaysShowTitle: { type: Boolean },
|
||||
});
|
||||
|
||||
const { prefixCls } = useDesign('app-logo');
|
||||
const { getCollapsedShowTitle } = useMenuSetting();
|
||||
const userStore = useUserStore();
|
||||
const go = useGo();
|
||||
|
||||
const getTitle = computed(() => {
|
||||
const { title } = useGlobSetting();
|
||||
return userStore.getPageCacheByKey('title', title);
|
||||
});
|
||||
|
||||
const getAppLogoClass = computed(() => [
|
||||
prefixCls,
|
||||
props.theme,
|
||||
{ 'collapsed-show-title': unref(getCollapsedShowTitle) },
|
||||
]);
|
||||
|
||||
const getTitleClass = computed(() => [
|
||||
`${prefixCls}__title`,
|
||||
{
|
||||
'xs:opacity-0': !props.alwaysShowTitle,
|
||||
},
|
||||
]);
|
||||
|
||||
function goHome() {
|
||||
go(userStore.getUserInfo.homePath || PageEnum.BASE_HOME);
|
||||
}
|
||||
</script>
|
||||
<style lang="less">
|
||||
@prefix-cls: ~'jeesite-app-logo';
|
||||
|
||||
.@{prefix-cls} {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-left: 7px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
|
||||
&.light {
|
||||
// border-bottom: 1px solid @border-color-base;
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
&.collapsed-show-title {
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
&.light &__title {
|
||||
color: #555;
|
||||
}
|
||||
|
||||
&.dark &__title {
|
||||
color: @white;
|
||||
}
|
||||
|
||||
&__title {
|
||||
font-size: 20px;
|
||||
// font-weight: bold;
|
||||
font-family: Arial, 'Microsoft YaHei', sans-serif;
|
||||
transition: all 0.5s;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,78 @@
|
||||
<script lang="ts">
|
||||
import { defineComponent, toRefs, ref, unref } from 'vue';
|
||||
import { createAppProviderContext } from './useAppContext';
|
||||
import { createBreakpointListen } from '@jeesite/core/hooks/event/useBreakpoint';
|
||||
// import { useAppStore } from '@jeesite/core/store/modules/app';
|
||||
// import { MenuModeEnum, MenuTypeEnum } from '@jeesite/core/enums/menuEnum';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'AppProvider',
|
||||
inheritAttrs: false,
|
||||
props: {
|
||||
prefixCls: {
|
||||
type: String,
|
||||
default: 'jeesite',
|
||||
},
|
||||
},
|
||||
setup(props, { slots }) {
|
||||
const isMobile = ref(false);
|
||||
// const isSetState = ref(false);
|
||||
// const appStore = useAppStore();
|
||||
|
||||
// Monitor screen breakpoint information changes
|
||||
createBreakpointListen(({ screenMap, sizeEnum, width }) => {
|
||||
const lgWidth = screenMap.get(sizeEnum.LG);
|
||||
if (lgWidth) {
|
||||
isMobile.value = width.value - 1 < lgWidth;
|
||||
}
|
||||
// handleRestoreState();
|
||||
});
|
||||
|
||||
const { prefixCls } = toRefs(props);
|
||||
|
||||
// Inject variables into the global
|
||||
createAppProviderContext({ prefixCls, isMobile });
|
||||
|
||||
// /**
|
||||
// * Used to maintain the state before the window changes
|
||||
// */
|
||||
// function handleRestoreState() {
|
||||
// if (unref(isMobile)) {
|
||||
// if (!unref(isSetState)) {
|
||||
// isSetState.value = true;
|
||||
// const {
|
||||
// menuSetting: {
|
||||
// type: menuType,
|
||||
// mode: menuMode,
|
||||
// collapsed: menuCollapsed,
|
||||
// split: menuSplit,
|
||||
// },
|
||||
// } = appStore.getProjectConfig;
|
||||
// appStore.setProjectConfig({
|
||||
// menuSetting: {
|
||||
// mode: MenuModeEnum.INLINE,
|
||||
// type: MenuTypeEnum.MIX,
|
||||
// split: false,
|
||||
// },
|
||||
// });
|
||||
// appStore.setBeforeMiniInfo({ menuMode, menuCollapsed, menuType, menuSplit });
|
||||
// }
|
||||
// } else {
|
||||
// if (unref(isSetState)) {
|
||||
// isSetState.value = false;
|
||||
// const { menuMode, menuCollapsed, menuType, menuSplit } = appStore.getBeforeMiniInfo;
|
||||
// appStore.setProjectConfig({
|
||||
// menuSetting: {
|
||||
// type: menuType,
|
||||
// mode: menuMode,
|
||||
// collapsed: menuCollapsed,
|
||||
// split: menuSplit,
|
||||
// },
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
return () => slots.default?.();
|
||||
},
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,34 @@
|
||||
<script lang="tsx">
|
||||
import { defineComponent, ref, unref } from 'vue';
|
||||
import { Tooltip } from 'ant-design-vue';
|
||||
import { Icon } from '@jeesite/core/components/Icon';
|
||||
import AppSearchModal from './AppSearchModal.vue';
|
||||
import { useI18n } from '@jeesite/core/hooks/web/useI18n';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'AppSearch',
|
||||
setup() {
|
||||
const showModal = ref(false);
|
||||
const { t } = useI18n();
|
||||
|
||||
function changeModal(show: boolean) {
|
||||
showModal.value = show;
|
||||
}
|
||||
|
||||
return () => {
|
||||
return (
|
||||
<div class="p-1" onClick={changeModal.bind(null, true)}>
|
||||
<Tooltip>
|
||||
{{
|
||||
title: () => t('common.searchText'),
|
||||
// default: () => <SearchOutlined />,
|
||||
default: () => <Icon icon="i-ant-design:search-outlined" />,
|
||||
}}
|
||||
</Tooltip>
|
||||
<AppSearchModal onClose={changeModal.bind(null, false)} open={unref(showModal)} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,58 @@
|
||||
<template>
|
||||
<div :class="`${prefixCls}`">
|
||||
<AppSearchKeyItem :class="`${prefixCls}-item`" icon="i-ant-design:enter-outlined" />
|
||||
<span>{{ t('component.app.toSearch') }}</span>
|
||||
<AppSearchKeyItem :class="`${prefixCls}-item`" icon="i-ion:arrow-up-outline" />
|
||||
<AppSearchKeyItem :class="`${prefixCls}-item`" icon="i-ion:arrow-down-outline" />
|
||||
<span>{{ t('component.app.toNavigate') }}</span>
|
||||
<AppSearchKeyItem :class="`${prefixCls}-item`" icon="i-mdi:keyboard-esc" />
|
||||
<span>{{ t('common.closeText') }}</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import AppSearchKeyItem from './AppSearchKeyItem.vue';
|
||||
import { useDesign } from '@jeesite/core/hooks/web/useDesign';
|
||||
import { useI18n } from '@jeesite/core/hooks/web/useI18n';
|
||||
const { prefixCls } = useDesign('app-search-footer');
|
||||
const { t } = useI18n();
|
||||
</script>
|
||||
<style lang="less">
|
||||
@prefix-cls: ~'jeesite-app-search-footer';
|
||||
|
||||
.@{prefix-cls} {
|
||||
position: relative;
|
||||
display: flex;
|
||||
height: 44px;
|
||||
padding: 0 16px;
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
background-color: @component-background;
|
||||
border-top: 1px solid @border-color-base;
|
||||
border-radius: 0 0 16px 16px;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
|
||||
&-item {
|
||||
display: flex;
|
||||
width: 20px;
|
||||
height: 18px;
|
||||
padding-bottom: 2px;
|
||||
margin-right: 0.4em;
|
||||
background-color: linear-gradient(-225deg, #d5dbe4, #f8f8f8);
|
||||
border-radius: 2px;
|
||||
box-shadow:
|
||||
inset 0 -2px 0 0 #cdcde6,
|
||||
inset 0 0 1px 1px #fff,
|
||||
0 1px 2px 1px rgb(30 35 90 / 40%);
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
&:nth-child(2),
|
||||
&:nth-child(3),
|
||||
&:nth-child(6) {
|
||||
margin-left: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,11 @@
|
||||
<template>
|
||||
<span :class="$attrs.class">
|
||||
<Icon :icon="icon" />
|
||||
</span>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { Icon } from '@jeesite/core/components/Icon';
|
||||
defineProps({
|
||||
icon: String,
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,269 @@
|
||||
<template>
|
||||
<Teleport to="body">
|
||||
<transition name="zoom-fade" mode="out-in">
|
||||
<div :class="getClass" @click.stop v-if="open">
|
||||
<div :class="`${prefixCls}-content`" v-click-outside="handleClose">
|
||||
<div :class="`${prefixCls}-input__wrapper`">
|
||||
<a-input
|
||||
:class="`${prefixCls}-input`"
|
||||
:placeholder="t('common.searchText')"
|
||||
ref="inputRef"
|
||||
allow-clear
|
||||
@change="handleSearch"
|
||||
>
|
||||
<template #prefix>
|
||||
<Icon icon="i-ant-design:search-outlined" class="text-gray-500" />
|
||||
</template>
|
||||
</a-input>
|
||||
<span :class="`${prefixCls}-cancel`" @click="handleClose">
|
||||
{{ t('common.cancelText') }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div :class="`${prefixCls}-not-data`" v-show="getIsNotData">
|
||||
{{ t('component.app.searchNotData') }}
|
||||
</div>
|
||||
|
||||
<ul :class="`${prefixCls}-list`" v-show="!getIsNotData" ref="scrollWrap">
|
||||
<li
|
||||
:ref="setRefs(index)"
|
||||
v-for="(item, index) in searchResult"
|
||||
:key="item.path"
|
||||
:data-index="index"
|
||||
@mouseenter="handleMouseenter"
|
||||
@click="handleEnter"
|
||||
:class="[
|
||||
`${prefixCls}-list__item`,
|
||||
{
|
||||
[`${prefixCls}-list__item--active`]: activeIndex === index,
|
||||
},
|
||||
]"
|
||||
>
|
||||
<div :class="`${prefixCls}-list__item-icon`">
|
||||
<Icon :icon="item.icon || 'mdi:form-select'" :size="20" />
|
||||
</div>
|
||||
<div :class="`${prefixCls}-list__item-text`">
|
||||
{{ item.name }}
|
||||
</div>
|
||||
<div :class="`${prefixCls}-list__item-enter`">
|
||||
<Icon icon="i-ant-design:enter-outlined" :size="20" />
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<AppSearchFooter />
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</Teleport>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, unref, ref, watch, nextTick } from 'vue';
|
||||
import AppSearchFooter from './AppSearchFooter.vue';
|
||||
import { Icon } from '@jeesite/core/components/Icon';
|
||||
// @ts-ignore
|
||||
import vClickOutside from '@jeesite/core/directives/clickOutside';
|
||||
import { useDesign } from '@jeesite/core/hooks/web/useDesign';
|
||||
import { useRefs } from '@jeesite/core/hooks/core/useRefs';
|
||||
import { useMenuSearch } from './useMenuSearch';
|
||||
import { useI18n } from '@jeesite/core/hooks/web/useI18n';
|
||||
import { useAppInject } from '@jeesite/core/hooks/web/useAppInject';
|
||||
|
||||
const props = defineProps({
|
||||
open: { type: Boolean },
|
||||
});
|
||||
|
||||
const emit = defineEmits(['close']);
|
||||
|
||||
const scrollWrap = ref(null);
|
||||
const inputRef = ref<Nullable<HTMLElement>>(null);
|
||||
|
||||
const { t } = useI18n();
|
||||
const { prefixCls } = useDesign('app-search-modal');
|
||||
const { refs, setRefs } = useRefs();
|
||||
const { getIsMobile } = useAppInject();
|
||||
|
||||
const { handleSearch, searchResult, keyword, activeIndex, handleEnter, handleMouseenter } = useMenuSearch(
|
||||
refs,
|
||||
scrollWrap,
|
||||
emit,
|
||||
);
|
||||
|
||||
const getIsNotData = computed(() => !keyword || unref(searchResult).length === 0);
|
||||
|
||||
const getClass = computed(() => {
|
||||
return [
|
||||
prefixCls,
|
||||
{
|
||||
[`${prefixCls}--mobile`]: unref(getIsMobile),
|
||||
},
|
||||
];
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.open,
|
||||
(open: boolean) => {
|
||||
open &&
|
||||
nextTick(() => {
|
||||
unref(inputRef)?.focus();
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
function handleClose() {
|
||||
searchResult.value = [];
|
||||
emit('close');
|
||||
}
|
||||
</script>
|
||||
<style lang="less">
|
||||
@prefix-cls: ~'jeesite-app-search-modal';
|
||||
@footer-prefix-cls: ~'jeesite-app-search-footer';
|
||||
.@{prefix-cls} {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 800;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding-top: 50px;
|
||||
background-color: rgb(0 0 0 / 25%);
|
||||
justify-content: center;
|
||||
|
||||
&--mobile {
|
||||
padding: 0;
|
||||
|
||||
> div {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.@{prefix-cls}-input {
|
||||
width: calc(100% - 38px);
|
||||
}
|
||||
|
||||
.@{prefix-cls}-cancel {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.@{prefix-cls}-content {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.@{footer-prefix-cls} {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.@{prefix-cls}-list {
|
||||
height: calc(100% - 80px);
|
||||
max-height: unset;
|
||||
|
||||
&__item {
|
||||
&-enter {
|
||||
opacity: 0 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-content {
|
||||
position: relative;
|
||||
width: 632px;
|
||||
margin: 0 auto auto;
|
||||
background-color: @component-background;
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 25px 50px -12px rgb(0 0 0 / 25%);
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
&-input__wrapper {
|
||||
display: flex;
|
||||
padding: 14px 14px 0;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&-input {
|
||||
width: 100%;
|
||||
height: 48px;
|
||||
font-size: 1.5em;
|
||||
color: #1c1e21;
|
||||
border-radius: 6px;
|
||||
|
||||
span[role='img'] {
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
|
||||
&-cancel {
|
||||
display: none;
|
||||
font-size: 1em;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
&-not-data {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 100px;
|
||||
font-size: 0.9;
|
||||
color: rgb(150 159 175);
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
&-list {
|
||||
max-height: 472px;
|
||||
padding: 0 14px;
|
||||
padding-bottom: 20px;
|
||||
margin: 0 auto;
|
||||
margin-top: 14px;
|
||||
overflow: auto;
|
||||
|
||||
&__item {
|
||||
position: relative;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 56px;
|
||||
padding-bottom: 4px;
|
||||
padding-left: 14px;
|
||||
margin-top: 8px;
|
||||
font-size: 14px;
|
||||
color: @text-color-base;
|
||||
cursor: pointer;
|
||||
background-color: @component-background;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 1px 3px 0 #d4d9e1;
|
||||
align-items: center;
|
||||
|
||||
> div:first-child,
|
||||
> div:last-child {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&--active {
|
||||
color: #fff;
|
||||
background-color: @primary-color;
|
||||
|
||||
.@{prefix-cls}-list__item-enter {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
&-icon {
|
||||
width: 30px;
|
||||
}
|
||||
|
||||
&-text {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
&-enter {
|
||||
width: 30px;
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -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<HTMLElement[]>, scrollWrap: Ref<ElRef>, emit: EmitType) {
|
||||
const searchResult = ref<SearchResult[]>([]);
|
||||
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 };
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { InjectionKey, Ref } from 'vue';
|
||||
import { createContext, useContext } from '@jeesite/core/hooks/core/useContext';
|
||||
|
||||
export interface AppProviderContextProps {
|
||||
prefixCls: Ref<string>;
|
||||
isMobile: Ref<boolean>;
|
||||
}
|
||||
|
||||
const key: InjectionKey<AppProviderContextProps> = Symbol();
|
||||
|
||||
export function createAppProviderContext(context: AppProviderContextProps) {
|
||||
return createContext<AppProviderContextProps>(context, key);
|
||||
}
|
||||
|
||||
export function useAppProviderContext() {
|
||||
return useContext<AppProviderContextProps>(key);
|
||||
}
|
||||
4
web-vue/packages/core/components/Authority/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
import { withInstall } from '@jeesite/core/utils';
|
||||
import authority from './src/Authority.vue';
|
||||
|
||||
export const Authority = withInstall(authority);
|
||||
45
web-vue/packages/core/components/Authority/src/Authority.vue
Normal file
@@ -0,0 +1,45 @@
|
||||
<!--
|
||||
Access control component for fine-grained access control.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import type { PropType } from 'vue';
|
||||
import { defineComponent } from 'vue';
|
||||
import { RoleEnum } from '@jeesite/core/enums/roleEnum';
|
||||
import { usePermission } from '@jeesite/core/hooks/web/usePermission';
|
||||
import { getSlot } from '@jeesite/core/utils/helper/tsxHelper';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Authority',
|
||||
props: {
|
||||
/**
|
||||
* Specified role is open
|
||||
* When the permission mode is the role mode, the value value can pass the role value.
|
||||
* When the permission mode is background, the value value can pass the code permission value
|
||||
* @default ''
|
||||
*/
|
||||
value: {
|
||||
type: [Number, Array, String] as PropType<RoleEnum | RoleEnum[] | string | string[]>,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
setup(props, { slots }) {
|
||||
const { hasPermission } = usePermission();
|
||||
|
||||
/**
|
||||
* Render role button
|
||||
*/
|
||||
function renderAuth() {
|
||||
const { value } = props;
|
||||
if (!value) {
|
||||
return getSlot(slots);
|
||||
}
|
||||
return hasPermission(value) ? getSlot(slots) : null;
|
||||
}
|
||||
|
||||
return () => {
|
||||
// Role-based value control
|
||||
return renderAuth();
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
8
web-vue/packages/core/components/Basic/index.ts
Normal file
@@ -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);
|
||||
117
web-vue/packages/core/components/Basic/src/BasicArrow.vue
Normal file
@@ -0,0 +1,117 @@
|
||||
<!--
|
||||
* Copyright (c) 2013-Now http://jeesite.com All rights reserved.
|
||||
* No deletion without permission, or be held responsible to law.
|
||||
* @author Vben、ThinkGem
|
||||
-->
|
||||
<template>
|
||||
<span :class="getClass" @click="handleClick" @dblclick="handleDblClick">
|
||||
<Spin v-if="props.loading" size="small" :style="$attrs.iconStyle" />
|
||||
<Icon v-else :icon="getIcon" :style="$attrs.iconStyle" />
|
||||
</span>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { computed } from 'vue';
|
||||
import { Spin } from 'ant-design-vue';
|
||||
import { Icon } from '@jeesite/core/components/Icon';
|
||||
import { useDesign } from '@jeesite/core/hooks/web/useDesign';
|
||||
|
||||
const props = defineProps({
|
||||
/**
|
||||
* Arrow expand state
|
||||
*/
|
||||
expand: { type: Boolean },
|
||||
/**
|
||||
* Arrow up by default
|
||||
*/
|
||||
up: { type: Boolean },
|
||||
/**
|
||||
* Arrow down by default
|
||||
*/
|
||||
down: { type: Boolean },
|
||||
/**
|
||||
* Cancel padding/margin for inline
|
||||
*/
|
||||
inset: { type: Boolean },
|
||||
/**
|
||||
* 是否是叶子节点
|
||||
*/
|
||||
leaf: { type: Boolean },
|
||||
/**
|
||||
* 是否双箭头图标
|
||||
*/
|
||||
double: { type: Boolean },
|
||||
/**
|
||||
* 是否加载状态
|
||||
*/
|
||||
loading: { type: Boolean, defaultValue: false },
|
||||
// eslint check
|
||||
onClick: { type: Function, default: (_e: Event) => {} },
|
||||
onDblclick: { type: Function, default: (_e: Event) => {} },
|
||||
});
|
||||
|
||||
const emit = defineEmits(['click', 'dblclick']);
|
||||
|
||||
const { prefixCls } = useDesign('basic-arrow');
|
||||
|
||||
const getIcon = computed(() => {
|
||||
const { leaf, double } = props;
|
||||
return leaf ? 'i-radix-icons:dot' : double ? 'i-ant-design:double-right-outlined' : 'i-ion:chevron-forward';
|
||||
});
|
||||
|
||||
// get component class
|
||||
const getClass = computed(() => {
|
||||
const { expand, up, down, inset } = props;
|
||||
return [
|
||||
prefixCls,
|
||||
{
|
||||
[`${prefixCls}--active`]: expand,
|
||||
up,
|
||||
inset,
|
||||
down,
|
||||
},
|
||||
];
|
||||
});
|
||||
|
||||
function handleClick(event) {
|
||||
emit('click', event);
|
||||
}
|
||||
|
||||
function handleDblClick(event) {
|
||||
emit('dblclick', event);
|
||||
}
|
||||
</script>
|
||||
<style lang="less">
|
||||
@prefix-cls: ~'jeesite-basic-arrow';
|
||||
|
||||
.@{prefix-cls} {
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
transform: rotate(0deg);
|
||||
transition: all 0.3s ease 0.1s;
|
||||
transform-origin: center center;
|
||||
|
||||
&--active {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
&.inset {
|
||||
line-height: 0;
|
||||
}
|
||||
|
||||
&.up {
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
|
||||
&.down {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
&.up.@{prefix-cls}--active {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
&.down.@{prefix-cls}--active {
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
119
web-vue/packages/core/components/Basic/src/BasicHelp.vue
Normal file
@@ -0,0 +1,119 @@
|
||||
<script lang="tsx">
|
||||
import type { CSSProperties, PropType } from 'vue';
|
||||
import { defineComponent, computed, unref } from 'vue';
|
||||
import { Tooltip } from 'ant-design-vue';
|
||||
import { getPopupContainer } from '@jeesite/core/utils';
|
||||
import { isString, isArray } from '@jeesite/core/utils/is';
|
||||
import { getSlot } from '@jeesite/core/utils/helper/tsxHelper';
|
||||
import { useDesign } from '@jeesite/core/hooks/web/useDesign';
|
||||
import { Icon } from '@jeesite/core/components/Icon';
|
||||
|
||||
const props = {
|
||||
/**
|
||||
* Help text max-width
|
||||
* @default: 600px
|
||||
*/
|
||||
maxWidth: { type: String, default: '600px' },
|
||||
/**
|
||||
* Whether to display the serial number
|
||||
* @default: false
|
||||
*/
|
||||
showIndex: { type: Boolean },
|
||||
/**
|
||||
* Help text font color
|
||||
* @default: #ffffff
|
||||
*/
|
||||
color: { type: String, default: '#ffffff' },
|
||||
/**
|
||||
* Help text font size
|
||||
* @default: 14px
|
||||
*/
|
||||
fontSize: { type: String, default: '14px' },
|
||||
/**
|
||||
* Help text list
|
||||
*/
|
||||
placement: { type: String, default: 'right' },
|
||||
/**
|
||||
* Help text list
|
||||
*/
|
||||
text: { type: [Array, String] as PropType<string[] | string> },
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
name: 'BasicHelp',
|
||||
components: { Icon, Tooltip },
|
||||
props,
|
||||
setup(props, { slots }) {
|
||||
const { prefixCls } = useDesign('basic-help');
|
||||
|
||||
const getTooltipStyle = computed((): CSSProperties => ({ color: props.color, fontSize: props.fontSize }));
|
||||
|
||||
const getOverlayStyle = computed((): CSSProperties => ({ maxWidth: props.maxWidth }));
|
||||
|
||||
function renderTitle() {
|
||||
const textList = props.text;
|
||||
|
||||
if (isString(textList)) {
|
||||
return <p>{textList}</p>;
|
||||
}
|
||||
|
||||
if (isArray(textList)) {
|
||||
return textList.map((text, index) => {
|
||||
return (
|
||||
<p key={text}>
|
||||
<>
|
||||
{props.showIndex ? `${index + 1}. ` : ''}
|
||||
{text}
|
||||
</>
|
||||
</p>
|
||||
);
|
||||
});
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
return () => {
|
||||
return (
|
||||
<Tooltip
|
||||
overlayClassName={`${prefixCls}__wrap`}
|
||||
title={<div style={unref(getTooltipStyle)}>{renderTitle()}</div>}
|
||||
autoAdjustOverflow={true}
|
||||
overlayStyle={unref(getOverlayStyle)}
|
||||
placement={props.placement as 'right'}
|
||||
getPopupContainer={() => getPopupContainer()}
|
||||
>
|
||||
<span class={prefixCls}>{getSlot(slots) || <Icon icon="i-ant-design:question-circle-outlined" />}</span>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<style lang="less">
|
||||
@prefix-cls: ~'jeesite-basic-help';
|
||||
|
||||
.@{prefix-cls} {
|
||||
display: inline-block;
|
||||
font-size: 13px;
|
||||
color: @text-color-help-dark;
|
||||
vertical-align: middle;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
color: @primary-color;
|
||||
}
|
||||
|
||||
&__wrap {
|
||||
p {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ant-form-item-label .@{prefix-cls} {
|
||||
vertical-align: baseline;
|
||||
margin-left: -7px;
|
||||
margin-right: -4px;
|
||||
opacity: 0.8;
|
||||
}
|
||||
</style>
|
||||
76
web-vue/packages/core/components/Basic/src/BasicTitle.vue
Normal file
@@ -0,0 +1,76 @@
|
||||
<template>
|
||||
<span :class="getClass">
|
||||
<slot></slot>
|
||||
<BasicHelp :class="`${prefixCls}-help`" v-if="helpMessage" :text="helpMessage" />
|
||||
</span>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import type { PropType } from 'vue';
|
||||
import { useSlots, computed } from 'vue';
|
||||
import BasicHelp from './BasicHelp.vue';
|
||||
import { useDesign } from '@jeesite/core/hooks/web/useDesign';
|
||||
|
||||
const props = defineProps({
|
||||
/**
|
||||
* Help text list or string
|
||||
* @default: ''
|
||||
*/
|
||||
helpMessage: {
|
||||
type: [String, Array] as PropType<string | string[]>,
|
||||
default: '',
|
||||
},
|
||||
/**
|
||||
* Whether the color block on the left side of the title
|
||||
* @default: false
|
||||
*/
|
||||
span: { type: Boolean },
|
||||
/**
|
||||
* Whether to default the text, that is, not bold
|
||||
* @default: false
|
||||
*/
|
||||
normal: { type: Boolean },
|
||||
});
|
||||
|
||||
const { prefixCls } = useDesign('basic-title');
|
||||
const slots = useSlots();
|
||||
const getClass = computed(() => [
|
||||
prefixCls,
|
||||
{ [`${prefixCls}-show-span`]: props.span && slots.default },
|
||||
{ [`${prefixCls}-normal`]: props.normal },
|
||||
]);
|
||||
</script>
|
||||
<style lang="less">
|
||||
@prefix-cls: ~'jeesite-basic-title';
|
||||
|
||||
.@{prefix-cls} {
|
||||
position: relative;
|
||||
display: flex;
|
||||
padding-left: 3px;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
line-height: 24px;
|
||||
color: fade(@text-color-base, 75);
|
||||
//cursor: pointer;
|
||||
user-select: none;
|
||||
|
||||
&-normal {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
&-show-span::before {
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
left: 0;
|
||||
width: 3px;
|
||||
height: 16px;
|
||||
margin-right: 4px;
|
||||
background-color: @primary-color;
|
||||
content: '';
|
||||
}
|
||||
|
||||
&-help {
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
9
web-vue/packages/core/components/Button/index.ts
Normal file
@@ -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<ExtractPropTypes<typeof buttonProps>>;
|
||||
35
web-vue/packages/core/components/Button/src/BasicButton.vue
Normal file
@@ -0,0 +1,35 @@
|
||||
<template>
|
||||
<Button v-bind="getBindValue" :class="getButtonClass" @click="onClick">
|
||||
<template #default="data">
|
||||
<Icon :icon="preIcon" v-if="preIcon" :size="iconSize" />
|
||||
<slot v-bind="data || {}"></slot>
|
||||
<Icon :icon="postIcon" v-if="postIcon" :size="iconSize" />
|
||||
</template>
|
||||
</Button>
|
||||
</template>
|
||||
<script lang="ts" setup name="AButton">
|
||||
import { computed, unref } from 'vue';
|
||||
import { Button } from 'ant-design-vue';
|
||||
import Icon from '@jeesite/core/components/Icon/src/Icon.vue';
|
||||
import { buttonProps } from './props';
|
||||
import { useAttrs } from '@jeesite/core/hooks/core/useAttrs';
|
||||
|
||||
defineOptions({
|
||||
inheritAttrs: false,
|
||||
});
|
||||
|
||||
const props = defineProps(buttonProps);
|
||||
|
||||
const attrs = useAttrs({ excludeDefaultKeys: false });
|
||||
const getButtonClass = computed(() => {
|
||||
const { color, disabled } = props;
|
||||
return [
|
||||
{
|
||||
[`ant-btn-${color}`]: !!color,
|
||||
[`is-disabled`]: disabled,
|
||||
},
|
||||
];
|
||||
});
|
||||
|
||||
const getBindValue = computed(() => ({ ...unref(attrs), ...props }));
|
||||
</script>
|
||||
@@ -0,0 +1,55 @@
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, h, unref } from 'vue';
|
||||
import BasicButton from './BasicButton.vue';
|
||||
import { Popconfirm } from 'ant-design-vue';
|
||||
import { extendSlots } from '@jeesite/core/utils/helper/tsxHelper';
|
||||
import { omit } from 'lodash-es';
|
||||
import { useAttrs } from '@jeesite/core/hooks/core/useAttrs';
|
||||
import { useI18n } from '@jeesite/core/hooks/web/useI18n';
|
||||
|
||||
const props = {
|
||||
/**
|
||||
* Whether to enable the drop-down menu
|
||||
* @default: true
|
||||
*/
|
||||
enable: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
name: 'PopButton',
|
||||
inheritAttrs: false,
|
||||
props,
|
||||
setup(props, { slots }) {
|
||||
const { t } = useI18n();
|
||||
const attrs = useAttrs();
|
||||
|
||||
// get inherit binding value
|
||||
const getBindValues = computed(() => {
|
||||
return Object.assign(
|
||||
{
|
||||
okText: t('common.okText'),
|
||||
cancelText: t('common.cancelText'),
|
||||
placement: 'left',
|
||||
},
|
||||
{ ...props, ...unref(attrs) },
|
||||
);
|
||||
});
|
||||
|
||||
return () => {
|
||||
const bindValues = omit(unref(getBindValues), 'icon') as Recordable;
|
||||
const btnBind = omit(bindValues, 'title') as Recordable;
|
||||
if (btnBind.disabled) btnBind.color = '';
|
||||
const Button = h(BasicButton, btnBind, extendSlots(slots));
|
||||
|
||||
// If it is not enabled, it is a normal button
|
||||
if (!props.enable) {
|
||||
return Button;
|
||||
}
|
||||
return h(Popconfirm, bindValues, { default: () => Button });
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
19
web-vue/packages/core/components/Button/src/props.ts
Normal file
@@ -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 },
|
||||
};
|
||||
4
web-vue/packages/core/components/CardList/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
import { withInstall } from '@jeesite/core/utils';
|
||||
import cardList from './src/CardList.vue';
|
||||
|
||||
export const CardList = withInstall(cardList);
|
||||
159
web-vue/packages/core/components/CardList/src/CardList.vue
Normal file
@@ -0,0 +1,159 @@
|
||||
<template>
|
||||
<div class="bg-white">
|
||||
<List
|
||||
:grid="{ gutter: 5, xs: 1, sm: 2, md: 4, lg: 4, xl: 6, xxl: 6 }"
|
||||
:data-source="data"
|
||||
:pagination="paginationProp"
|
||||
>
|
||||
<template #header>
|
||||
<div class="flex space-x-2">
|
||||
<BasicForm @register="registerForm" />
|
||||
<slot name="header"></slot>
|
||||
<Tooltip :overlayStyle="{ maxWidth: '500px' }">
|
||||
<template #title>
|
||||
<div class="w-50">每页显示数量</div>
|
||||
<Slider id="slider" class="w-90" v-bind="sliderProp" v-model:value="grid" @change="sliderChange" />
|
||||
</template>
|
||||
<a-button><TableOutlined /></a-button>
|
||||
</Tooltip>
|
||||
<Tooltip @click="fetch">
|
||||
<template #title>刷新</template>
|
||||
<a-button><RedoOutlined /></a-button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</template>
|
||||
<template #renderItem="{ item }">
|
||||
<ListItem style="padding: 10px; margin: 10px 0 0">
|
||||
<Card>
|
||||
<template #actions>
|
||||
<EditOutlined @click="showMessage('你点击了编辑图标')" />
|
||||
<Dropdown
|
||||
:trigger="['hover']"
|
||||
:dropMenuList="[
|
||||
{
|
||||
text: '删除',
|
||||
event: '1',
|
||||
popConfirm: {
|
||||
title: t('是否确认删除'),
|
||||
confirm: handleDelete.bind(null, item.id),
|
||||
},
|
||||
},
|
||||
]"
|
||||
popconfirm
|
||||
>
|
||||
<EllipsisOutlined />
|
||||
</Dropdown>
|
||||
</template>
|
||||
<Avatar :src="getAvatar(item)" />
|
||||
<span class="pl-2">{{ item.userName }}</span>
|
||||
</Card>
|
||||
</ListItem>
|
||||
</template>
|
||||
</List>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
import { EditOutlined, EllipsisOutlined, RedoOutlined, TableOutlined } from '@ant-design/icons-vue';
|
||||
import { List, Card, Image, Typography, Tooltip, Slider, Avatar } from 'ant-design-vue';
|
||||
import { useI18n } from '@jeesite/core/hooks/web/useI18n';
|
||||
import { Dropdown } from '@jeesite/core/components/Dropdown';
|
||||
import { BasicForm, useForm } from '@jeesite/core/components/Form';
|
||||
import { propTypes } from '@jeesite/core/utils/propTypes';
|
||||
import { isFunction } from '@jeesite/core/utils/is';
|
||||
import { useSlider, grid } from './data';
|
||||
import { useGlobSetting } from '@jeesite/core/hooks/setting';
|
||||
import { useMessage } from '@jeesite/core/hooks/web/useMessage';
|
||||
|
||||
const { t } = useI18n();
|
||||
const { showMessage } = useMessage();
|
||||
|
||||
const ListItem = List.Item;
|
||||
|
||||
// 获取slider属性
|
||||
const sliderProp = computed(() => useSlider(1));
|
||||
|
||||
// 组件接收参数
|
||||
const props = defineProps({
|
||||
// 请求API的参数
|
||||
params: propTypes.object.def({}),
|
||||
// api
|
||||
api: propTypes.func,
|
||||
});
|
||||
|
||||
// 暴露内部方法
|
||||
const emit = defineEmits(['getMethod', 'delete']);
|
||||
|
||||
// 数据
|
||||
const data = ref([]);
|
||||
|
||||
// 表单
|
||||
const [registerForm, { validate }] = useForm({
|
||||
schemas: [{ field: 'loginCode', component: 'Input', label: '账号' }],
|
||||
labelWidth: 80,
|
||||
autoSubmitOnEnter: true,
|
||||
showActionButtonGroup: true,
|
||||
submitFunc: handleSubmit,
|
||||
});
|
||||
|
||||
//表单提交
|
||||
async function handleSubmit() {
|
||||
const data = await validate();
|
||||
await fetch(data);
|
||||
}
|
||||
|
||||
function sliderChange(n) {
|
||||
pageSize.value = n;
|
||||
fetch();
|
||||
}
|
||||
|
||||
// 自动请求并暴露内部方法
|
||||
onMounted(() => {
|
||||
fetch();
|
||||
emit('getMethod', fetch);
|
||||
});
|
||||
|
||||
async function fetch(p = {}) {
|
||||
const { api, params } = props;
|
||||
if (api && isFunction(api)) {
|
||||
const res = await api({ ...params, pageNo: page.value, pageSize: pageSize.value, ...p });
|
||||
data.value = res.list;
|
||||
total.value = res.count;
|
||||
}
|
||||
}
|
||||
//分页相关
|
||||
const page = ref(1);
|
||||
const pageSize = ref(36);
|
||||
const total = ref(0);
|
||||
const paginationProp = ref({
|
||||
showSizeChanger: false,
|
||||
showQuickJumper: true,
|
||||
pageSize,
|
||||
current: page,
|
||||
total,
|
||||
showTotal: (total: number) => `总 ${total} 条`,
|
||||
onChange: pageChange,
|
||||
onShowSizeChange: pageSizeChange,
|
||||
});
|
||||
|
||||
function getAvatar(item: Recordable) {
|
||||
const { ctxPath } = useGlobSetting();
|
||||
let url = item.avatarUrl || '/ctxPath/static/images/user1.jpg';
|
||||
url = url.replace('/ctxPath/', ctxPath + '/');
|
||||
return url;
|
||||
}
|
||||
|
||||
function pageChange(p: number, pz: number) {
|
||||
page.value = p;
|
||||
pageSize.value = pz;
|
||||
fetch();
|
||||
}
|
||||
function pageSizeChange(_current, size: number) {
|
||||
pageSize.value = size;
|
||||
fetch();
|
||||
}
|
||||
|
||||
async function handleDelete(id: number) {
|
||||
emit('delete', id);
|
||||
}
|
||||
</script>
|
||||
25
web-vue/packages/core/components/CardList/src/data.ts
Normal file
@@ -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,
|
||||
};
|
||||
};
|
||||
4
web-vue/packages/core/components/ClickOutSide/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
import { withInstall } from '@jeesite/core/utils';
|
||||
import clickOutSide from './src/ClickOutSide.vue';
|
||||
|
||||
export const ClickOutSide = withInstall(clickOutSide);
|
||||
@@ -0,0 +1,19 @@
|
||||
<template>
|
||||
<div ref="wrap">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { onClickOutside } from '@vueuse/core';
|
||||
const emit = defineEmits(['mounted', 'clickOutside']);
|
||||
const wrap = ref<ElRef>(null);
|
||||
|
||||
onClickOutside(wrap, () => {
|
||||
emit('clickOutside');
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
emit('mounted');
|
||||
});
|
||||
</script>
|
||||
8
web-vue/packages/core/components/CodeEditor/index.ts
Normal file
@@ -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';
|
||||
@@ -0,0 +1,59 @@
|
||||
<template>
|
||||
<div class="h-full">
|
||||
<CodeMirrorEditor
|
||||
:value="getValue"
|
||||
@change="handleValueChange"
|
||||
:mode="mode"
|
||||
:readonly="readonly"
|
||||
:bordered="bordered"
|
||||
:config="config"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { computed } from 'vue';
|
||||
import CodeMirrorEditor from './codemirror/CodeMirror.vue';
|
||||
import { isString } from '@jeesite/core/utils/is';
|
||||
import { MODE } from './typing';
|
||||
import type { EditorConfiguration } from 'codemirror';
|
||||
|
||||
const props = defineProps({
|
||||
value: { type: [Object, String] as PropType<Record<string, any> | string> },
|
||||
mode: {
|
||||
type: String as PropType<MODE>,
|
||||
default: MODE.JSON,
|
||||
validator(value: any) {
|
||||
// 这个值必须匹配下列字符串中的一个
|
||||
return Object.values(MODE).includes(value);
|
||||
},
|
||||
},
|
||||
readonly: { type: Boolean },
|
||||
autoFormat: { type: Boolean, default: true },
|
||||
bordered: { type: Boolean, default: false },
|
||||
config: { type: Object as PropType<EditorConfiguration>, default: () => {} },
|
||||
});
|
||||
|
||||
const emit = defineEmits(['change', 'update:value', 'format-error']);
|
||||
|
||||
const getValue = computed(() => {
|
||||
const { value, mode, autoFormat } = props;
|
||||
if (!autoFormat || mode !== MODE.JSON) {
|
||||
return value as string;
|
||||
}
|
||||
let result = value;
|
||||
if (isString(value)) {
|
||||
try {
|
||||
result = JSON.parse(value);
|
||||
} catch (e) {
|
||||
emit('format-error', value);
|
||||
return value as string;
|
||||
}
|
||||
}
|
||||
return JSON.stringify(result, null, 2);
|
||||
});
|
||||
|
||||
function handleValueChange(v) {
|
||||
emit('update:value', v);
|
||||
emit('change', v);
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,132 @@
|
||||
<template>
|
||||
<div
|
||||
class="relative w-full overflow-hidden !h-full"
|
||||
:class="{ 'ant-input': props.bordered, 'css-dev-only-do-not-override-kqecok': props.bordered }"
|
||||
ref="el"
|
||||
></div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { type PropType, ref, onMounted, onUnmounted, watchEffect, watch, unref, nextTick } from 'vue';
|
||||
import { useWindowSizeFn } from '@jeesite/core/hooks/event/useWindowSizeFn';
|
||||
import { useDebounceFn } from '@vueuse/core';
|
||||
import { useAppStore } from '@jeesite/core/store/modules/app';
|
||||
|
||||
import CodeMirror from 'codemirror';
|
||||
import type { EditorConfiguration } from 'codemirror';
|
||||
import { MODE, parserDynamicImport } from './../typing';
|
||||
|
||||
// css
|
||||
import 'codemirror/lib/codemirror.css';
|
||||
import 'codemirror/theme/idea.css';
|
||||
import 'codemirror/theme/material-palenight.css';
|
||||
|
||||
// 代码段折叠功能
|
||||
import 'codemirror/addon/fold/foldgutter.css';
|
||||
import 'codemirror/addon/fold/foldcode.js';
|
||||
import 'codemirror/addon/fold/foldgutter';
|
||||
import 'codemirror/addon/fold/brace-fold';
|
||||
import 'codemirror/addon/fold/comment-fold';
|
||||
import 'codemirror/addon/fold/markdown-fold';
|
||||
import 'codemirror/addon/fold/xml-fold';
|
||||
import 'codemirror/addon/fold/indent-fold';
|
||||
|
||||
const props = defineProps({
|
||||
mode: {
|
||||
type: String as PropType<MODE>,
|
||||
default: MODE.JSON,
|
||||
validator(value: any) {
|
||||
// 这个值必须匹配下列字符串中的一个
|
||||
return Object.values(MODE).includes(value);
|
||||
},
|
||||
},
|
||||
value: { type: String, default: '' },
|
||||
readonly: { type: Boolean, default: false },
|
||||
bordered: { type: Boolean, default: false },
|
||||
config: { type: Object as PropType<EditorConfiguration>, default: () => {} },
|
||||
});
|
||||
|
||||
const emit = defineEmits(['change']);
|
||||
|
||||
const el = ref();
|
||||
let editor: Nullable<CodeMirror.Editor>;
|
||||
|
||||
const debounceRefresh = useDebounceFn(refresh, 100);
|
||||
const appStore = useAppStore();
|
||||
|
||||
watch(
|
||||
() => props.value,
|
||||
async (value) => {
|
||||
await nextTick();
|
||||
const oldValue = editor?.getValue();
|
||||
if (value !== oldValue) {
|
||||
editor?.setValue(value ? value : '');
|
||||
}
|
||||
},
|
||||
{ flush: 'post' },
|
||||
);
|
||||
|
||||
watchEffect(async () => {
|
||||
await parserDynamicImport(props.mode)();
|
||||
editor?.setOption('mode', props.mode);
|
||||
});
|
||||
|
||||
watch(
|
||||
() => appStore.getDarkMode,
|
||||
async () => {
|
||||
setTheme();
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
},
|
||||
);
|
||||
|
||||
function setTheme() {
|
||||
unref(editor)?.setOption('theme', appStore.getDarkMode === 'light' ? 'idea' : 'material-palenight');
|
||||
}
|
||||
|
||||
function refresh() {
|
||||
editor?.refresh();
|
||||
}
|
||||
|
||||
async function init() {
|
||||
const addonOptions = {
|
||||
autoCloseBrackets: true,
|
||||
autoCloseTags: true,
|
||||
foldGutter: true,
|
||||
gutters: ['CodeMirror-lint-markers', 'CodeMirror-linenumbers', 'CodeMirror-foldgutter'],
|
||||
};
|
||||
|
||||
editor = CodeMirror(el.value!, {
|
||||
value: '',
|
||||
mode: props.mode,
|
||||
readOnly: props.readonly,
|
||||
tabSize: 2,
|
||||
theme: 'material-palenight',
|
||||
lineWrapping: true,
|
||||
lineNumbers: true,
|
||||
...addonOptions,
|
||||
...props.config,
|
||||
});
|
||||
editor?.setValue(props.value);
|
||||
setTheme();
|
||||
editor?.on('change', () => {
|
||||
emit('change', editor?.getValue());
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await nextTick();
|
||||
init();
|
||||
useWindowSizeFn(debounceRefresh);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
editor = null;
|
||||
});
|
||||
</script>
|
||||
<style>
|
||||
.CodeMirror {
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,16 @@
|
||||
<template>
|
||||
<vue-json-pretty :path="'res'" :deep="4" :showLength="true" :data="data" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import VueJsonPretty from 'vue-json-pretty';
|
||||
import 'vue-json-pretty/lib/styles.css';
|
||||
|
||||
defineProps({
|
||||
data: Object,
|
||||
});
|
||||
</script>
|
||||
<style lang="less">
|
||||
.vjs-value-string {
|
||||
color: @text-color-base;
|
||||
}
|
||||
</style>
|
||||
247
web-vue/packages/core/components/CodeEditor/src/typing.ts
Normal file
@@ -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<any> {
|
||||
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];
|
||||
}
|
||||
3
web-vue/packages/core/components/CollapseForm/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import CollapseForm from './src/CollapseForm.vue';
|
||||
|
||||
export { CollapseForm };
|
||||
@@ -0,0 +1,155 @@
|
||||
<!--
|
||||
* Copyright (c) 2013-Now http://jeesite.com All rights reserved.
|
||||
* No deletion without permission, or be held responsible to law.
|
||||
* @author ThinkGem
|
||||
-->
|
||||
<template>
|
||||
<div class="jeesite-collapse-form-page">
|
||||
<ScrollContainer ref="contentRef" :style="{ height: contentHeight + 'px' }" v-loading="props.loading">
|
||||
<div v-for="item in configList" :key="item.value">
|
||||
<Collapse
|
||||
:class="item.value"
|
||||
:default-active-key="configList.filter((i) => i.open || true).map((i) => i.value)"
|
||||
>
|
||||
<Collapse.Panel :key="item.value" :header="item.label">
|
||||
<slot :name="item.value"></slot>
|
||||
</Collapse.Panel>
|
||||
</Collapse>
|
||||
</div>
|
||||
</ScrollContainer>
|
||||
<div class="jeesite-collapse-form-actions">
|
||||
<slot v-if="$slots.actions" name="actions"></slot>
|
||||
<template v-else>
|
||||
<a-button type="default" @click="handleClose" v-auth="props.okAuth">
|
||||
<Icon icon="i-ant-design:close-outlined" /> {{ t('common.closeText') }}
|
||||
</a-button>
|
||||
<a-button type="primary" @click="handleSubmit" :loading="props.loading || props.okLoading">
|
||||
<Icon icon="i-ant-design:check-outlined" /> {{ t('common.okText') }}
|
||||
</a-button>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup name="CollapseForm">
|
||||
import { nextTick, ref } from 'vue';
|
||||
import { Collapse } from 'ant-design-vue';
|
||||
import { Icon } from '@jeesite/core/components/Icon';
|
||||
import { useI18n } from '@jeesite/core/hooks/web/useI18n';
|
||||
import { propTypes } from '@jeesite/core/utils/propTypes';
|
||||
import { ScrollContainer } from '@jeesite/core/components/Container';
|
||||
import { useWindowSizeFn } from '@jeesite/core/hooks/event/useWindowSizeFn';
|
||||
import { onMountedOrActivated } from '@jeesite/core/hooks/core/onMountedOrActivated';
|
||||
import { useLayoutHeight } from '@jeesite/core/layouts/default/content/useContentViewHeight';
|
||||
|
||||
const props = defineProps({
|
||||
config: propTypes.array.def([]),
|
||||
okAuth: propTypes.string,
|
||||
loading: propTypes.bool,
|
||||
okLoading: propTypes.bool,
|
||||
});
|
||||
|
||||
const emit = defineEmits(['close', 'ok']);
|
||||
|
||||
const { t } = useI18n();
|
||||
const configList = ref<any[]>(props.config);
|
||||
const contentRef = ref<ComponentRef>();
|
||||
const contentHeight = ref<number>(200);
|
||||
const { headerHeightRef } = useLayoutHeight();
|
||||
|
||||
function calcContentHeight() {
|
||||
const parentElement = contentRef.value?.$el.parentElement;
|
||||
const actionsElement = parentElement?.querySelector('.jeesite-collapse-form-actions');
|
||||
if (parentElement && actionsElement) {
|
||||
contentHeight.value = document.body.clientHeight - headerHeightRef.value - actionsElement.scrollHeight - 32;
|
||||
}
|
||||
}
|
||||
|
||||
onMountedOrActivated(() => {
|
||||
nextTick(() => {
|
||||
calcContentHeight();
|
||||
});
|
||||
});
|
||||
|
||||
useWindowSizeFn(
|
||||
() => {
|
||||
calcContentHeight();
|
||||
},
|
||||
50,
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
function handleClose() {
|
||||
emit('close');
|
||||
}
|
||||
|
||||
function handleSubmit() {
|
||||
emit('ok');
|
||||
}
|
||||
</script>
|
||||
<style lang="less">
|
||||
.jeesite-collapse-form {
|
||||
&-page {
|
||||
.scrollbar {
|
||||
border-radius: 4px !important;
|
||||
|
||||
&__view > div {
|
||||
margin-bottom: 5px;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ant-collapse {
|
||||
border: 0 !important;
|
||||
|
||||
&-item {
|
||||
border: 0 !important;
|
||||
border-radius: 4px !important;
|
||||
}
|
||||
|
||||
&-header {
|
||||
font-size: 16px;
|
||||
padding: 8px 16px !important;
|
||||
border: 0 !important;
|
||||
border-radius: 4px !important;
|
||||
background-color: @component-background;
|
||||
|
||||
.ant-collapse-expand-icon {
|
||||
padding-top: 3px;
|
||||
}
|
||||
}
|
||||
|
||||
&-content {
|
||||
border: 0 !important;
|
||||
padding-top: 5px !important;
|
||||
border-radius: 0 0 4px 4px !important;
|
||||
}
|
||||
|
||||
&-item-active {
|
||||
.ant-collapse-header {
|
||||
border-radius: 4px 4px 0 0 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-actions {
|
||||
padding: 10px;
|
||||
margin-top: 5px;
|
||||
margin-bottom: 0;
|
||||
text-align: center;
|
||||
border-radius: 4px !important;
|
||||
background-color: @component-background;
|
||||
|
||||
.ant-btn {
|
||||
margin-right: 8px;
|
||||
|
||||
&-primary {
|
||||
background: fade(@primary-color, 85);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
10
web-vue/packages/core/components/Container/index.ts
Normal file
@@ -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';
|
||||