hello world

This commit is contained in:
2024-01-02 21:10:26 +08:00
commit 01c45a9669
659 changed files with 39674 additions and 0 deletions

1
.env.development Normal file
View File

@@ -0,0 +1 @@
VITE_API_BASE_URL= 'http://localhost:8000'

1
.env.production Normal file
View File

@@ -0,0 +1 @@
VITE_API_BASE_URL= 'https://api.charles7c.top'

3
.eslintignore Normal file
View File

@@ -0,0 +1,3 @@
/*.json
/*.js
dist

72
.eslintrc.js Normal file
View File

@@ -0,0 +1,72 @@
// eslint-disable-next-line @typescript-eslint/no-var-requires
const path = require('path');
module.exports = {
root: true,
parser: 'vue-eslint-parser',
parserOptions: {
// Parser that checks the content of the <script> tag
parser: '@typescript-eslint/parser',
sourceType: 'module',
ecmaVersion: 2020,
ecmaFeatures: {
jsx: true,
},
},
env: {
'browser': true,
'node': true,
'vue/setup-compiler-macros': true,
},
plugins: ['@typescript-eslint'],
extends: [
// Airbnb JavaScript Style Guide https://github.com/airbnb/javascript
'airbnb-base',
'plugin:@typescript-eslint/recommended',
'plugin:import/recommended',
'plugin:import/typescript',
'plugin:vue/vue3-recommended',
'plugin:prettier/recommended',
],
settings: {
'import/resolver': {
typescript: {
project: path.resolve(__dirname, './tsconfig.json'),
},
},
},
rules: {
'import/prefer-default-export': 'off',
'prettier/prettier': 1,
// Vue: Recommended rules to be closed or modify
'vue/require-default-prop': 0,
'vue/singleline-html-element-content-newline': 0,
'vue/max-attributes-per-line': 0,
// Vue: Add extra rules
'vue/custom-event-name-casing': [2, 'camelCase'],
'vue/no-v-text': 1,
'vue/padding-line-between-blocks': 1,
'vue/require-direct-export': 1,
'vue/multi-word-component-names': 0,
// Allow @ts-ignore comment
'@typescript-eslint/ban-ts-comment': 0,
'@typescript-eslint/no-unused-vars': 1,
'@typescript-eslint/no-empty-function': 1,
'@typescript-eslint/no-explicit-any': 0,
'import/extensions': [
2,
'ignorePackages',
{
js: 'never',
jsx: 'never',
ts: 'never',
tsx: 'never',
},
],
'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0,
'no-param-reassign': 0,
'prefer-regex-literals': 0,
'import/no-extraneous-dependencies': 0,
'camelcase': 'off',
},
};

View File

@@ -0,0 +1,46 @@
<!--
感谢您使用 ContiNew Admin请您花些时间填写这份 Issue 调查,非常感谢您的反馈!
-->
<!-- 在 [] 中输入 x 来勾选) -->
## Issue 类型
- [ ] 缺陷报告Bug
- [ ] 需求建议Feature
## Issue 描述
<!-- 清楚而简洁地描述您遇到的问题。例如:在使用 xxx 功能时发现 xxx 问题。另外,非常欢迎您对此 Issue 提交 PR。 -->
## 复现步骤(如果是提交 Feature Issue请删除本项
<!-- 条理清晰的步骤或演示视频可以帮助快速定位问题。例如1、xxx; 2、xxx; -->
## 预期结果(如果是提交 Feature Issue请删除本项
<!-- 清楚而简洁地描述您期望的结果。 -->
## 环境信息(如果是提交 Feature Issue请删除本项
<!-- 描述清楚您所使用的相关环境例如JDK 版本1.8.0_202框架版本v1.0.0;其他可能与该 issue 相关的依赖版本。 -->
## 解决方案(如果是提交 Bug Issue请删除本项
<!-- 清楚而简洁地描述您想要的解决方案。 -->
## 替代方案(如果是提交 Bug Issue请删除本项
<!-- 清楚而简洁地描述您考虑过的任何替代解决方案或功能。 -->
## 额外补充
<!-- Bug添加您的完整报错信息或屏幕截图以及一切能帮助定位问题的信息。 -->
<!-- Feature添加您在其他框架或场景遇见的效果截图或链接以及一切能帮助理解 Feature 的信息。 -->
## 提交前确认
<!-- 在提交 issue 之前,请确保执行过以下操作。 -->
- [ ] 阅读[文档](https://doc.charles7c.top/admin/other/faq.html)
- [ ] 搜索是否有其他人提交过类似的 issue如果对应 issue 尚未解决,您可以先订阅关注该 issue为了方便后来者查找问题解决方法请尽量避免创建重复的 issue

View File

@@ -0,0 +1,53 @@
<!--
非常感谢您的 PR在提交之前请务必确保您 PR 的代码经过了完整测试,并且通过了代码规范检查。
-->
<!-- 在 [] 中输入 x 来勾选) -->
## PR 类型
<!-- 您的 PR 引入了哪种类型的变更? -->
<!-- 只支持选择一种类型,如果有多种类型,可以在更新日志中增加 “类型” 列。 -->
- [ ] 新 feature
- [ ] Bug 修复
- [ ] 功能增强
- [ ] 文档变更
- [ ] 代码样式变更
- [ ] 重构
- [ ] 性能改进
- [ ] 单元测试
- [ ] CI/CD
- [ ] 其他
## PR 目的
<!-- 描述一下您的 PR 解决了什么问题。如果可以,请链接到相关 issues。 -->
## 解决方案
<!-- 详细描述您是如何解决的问题 -->
## PR 测试
<!-- 如果可以,请为您的 PR 添加或更新单元测试。 -->
<!-- 请描述一下您是如何测试 PR 的。例如:创建/更新单元测试或添加相关的截图。 -->
## Changelog
| 模块 | Changelog | Related issues |
|-----|-----------| -------------- |
| | | |
<!-- 如果有多种类型的变更,可以在变更日志表中增加 “类型” 列,该列的值与上方 “PR 类型” 相同。 -->
<!-- Related issues 格式为 fixes #<issue号>,或者 resolves #<issue号>。 -->
## 其他信息
<!-- 请描述一下还有哪些注意事项。例如:如果引入了一个不向下兼容的变更,请描述其影响。 -->
## 提交前确认
- [ ] PR 代码经过了完整测试,并且通过了代码规范检查
- [ ] 已经完整填写 Changelog并链接到了相关 issues
- [ ] PR 代码将要提交到 dev 分支

57
.github/ISSUE_TEMPLATE/bug_report.yml vendored Normal file
View File

@@ -0,0 +1,57 @@
name: "\U0001F41E Bug 报告"
description: Create a report to help us improve
labels: ['bug: pending triage']
body:
- type: markdown
attributes:
value: |
感谢您使用 ContiNew Admin请您花些时间填写这份 Bug 报告。
- type: textarea
id: bug-description
attributes:
label: Bug 描述
description: 清楚而简洁地描述您遇到的 Bug。另外非常欢迎您对此 Bug 提交 PR。
placeholder: 例如:在使用 xxx 功能时出现异常
validations:
required: true
- type: textarea
id: reproduction
attributes:
label: 复现步骤
description: 条理清晰的步骤或演示视频可以帮助快速定位问题。
placeholder: 例如1、xxx; 2、xxx;
validations:
required: true
- type: textarea
id: expected
attributes:
label: 预期结果
description: 清楚而简洁地描述您期望的结果。
placeholder: 预期结果
validations:
required: true
- type: textarea
id: environment-info
attributes:
label: 环境信息
description: 描述清楚您所使用的相关环境例如JDK 版本1.8.0_202框架版本v1.0.0;其他可能与该 issue 相关的依赖版本。
placeholder: JDK 版本, 框架版本等
validations:
required: true
- type: textarea
id: additional-context
attributes:
label: 额外补充
description: 添加您的完整报错信息或屏幕截图,以及一切能帮助定位问题的信息。
- type: checkboxes
id: checkboxes
attributes:
label: 确认
description: 在提交 issue 之前,请确保执行过以下操作。
options:
- label: 阅读[文档](https://doc.charles7c.top/admin/other/faq.html)
required: true
- label: 根据报错信息百度或 Google 一下
required: true
- label: 搜索是否有其他人提交过类似的 issue如果对应 issue 尚未解决,您可以先订阅关注该 issue为了方便后来者查找问题解决方法请尽量避免创建重复的 issue
required: true

View File

@@ -0,0 +1,42 @@
name: "\U0001F680 新 Feature 建议"
description: Suggest an idea for this project
body:
- type: markdown
attributes:
value: |
感谢您使用 ContiNew Admin请您花些时间填写这份 Feature 调查。
- type: textarea
id: feature-description
attributes:
label: Feature 描述
description: 清楚而简洁地描述您的 Feature。另外非常欢迎您对此 Feature 提交 PR。
placeholder: 例如:我希望增加 xxx 功能;现有的 xxx 功能不好用...
validations:
required: true
- type: textarea
id: suggested-solution
attributes:
label: 描述一下您想要的解决方案
description: 清楚而简洁地描述您想要的解决方案。
validations:
required: true
- type: textarea
id: alternative
attributes:
label: 描述一下您考虑过的替代方案
description: 清楚而简洁地描述您考虑过的任何替代解决方案或功能。
- type: textarea
id: additional-context
attributes:
label: 额外补充
description: 添加您在其他框架或场景遇见的效果截图或链接,以及一切能帮助理解 Feature 的信息。
- type: checkboxes
id: checkboxes
attributes:
label: 确认
description: 在提交 issue 之前,请确保执行过以下操作。
options:
- label: 阅读[文档](https://doc.charles7c.top/admin/intro/require.html)
required: true
- label: 搜索是否有其他人提交过类似的 issue如果对应 issue 尚未解决,您可以先订阅关注该 issue为了方便后来者查找问题解决方法请尽量避免创建重复的 issue
required: true

53
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View File

@@ -0,0 +1,53 @@
<!--
非常感谢您的 PR在提交之前请务必确保您 PR 的代码经过了完整测试,并且通过了代码规范检查。
-->
<!-- 在 [] 中输入 x 来勾选) -->
## PR 类型
<!-- 您的 PR 引入了哪种类型的变更? -->
<!-- 只支持选择一种类型,如果有多种类型,可以在更新日志中增加 “类型” 列。 -->
- [ ] 新 feature
- [ ] Bug 修复
- [ ] 功能增强
- [ ] 文档变更
- [ ] 代码样式变更
- [ ] 重构
- [ ] 性能改进
- [ ] 单元测试
- [ ] CI/CD
- [ ] 其他
## PR 目的
<!-- 描述一下您的 PR 解决了什么问题。如果可以,请链接到相关 issues。 -->
## 解决方案
<!-- 详细描述您是如何解决的问题 -->
## PR 测试
<!-- 如果可以,请为您的 PR 添加或更新单元测试。 -->
<!-- 请描述一下您是如何测试 PR 的。例如:创建/更新单元测试或添加相关的截图。 -->
## Changelog
| 模块 | Changelog | Related issues |
|-----|-----------| -------------- |
| | | |
<!-- 如果有多种类型的变更,可以在变更日志表中增加 “类型” 列,该列的值与上方 “PR 类型” 相同。 -->
<!-- Related issues 格式为 Closes #<issue号>,或者 Fixes #<issue号>,或者 Resolves #<issue号>。 -->
## 其他信息
<!-- 请描述一下还有哪些注意事项。例如:如果引入了一个不向下兼容的变更,请描述其影响。 -->
## 提交前确认
- [ ] PR 代码经过了完整测试,并且通过了代码规范检查
- [ ] 已经完整填写 Changelog并链接到了相关 issues
- [ ] PR 代码将要提交到 dev 分支

56
.github/workflows/deploy.yml vendored Normal file
View File

@@ -0,0 +1,56 @@
name: Deploy
on:
# 推送时执行
push:
branches: [dev]
# 可手动执行
workflow_dispatch:
jobs:
deploy-web:
runs-on: ubuntu-latest
steps:
# 1、检出源码
- name: Checkout
uses: actions/checkout@master
# 2、安装 PNPM
- name: Setup PNPM
uses: pnpm/action-setup@v2
with:
version: latest
# 3、安装 Node 环境
- name: Setup Node
uses: actions/setup-node@v3
with:
node-version: 16
cache: pnpm
cache-dependency-path: ./pnpm-lock.yaml
# 4、安装依赖
- name: Install Dependencies
run: pnpm i --frozen-lockfile
# 5、打包
- name: Build
run: pnpm build
# 6、拷贝到服务器
- name: Copy
uses: garygrossgarten/github-action-scp@release
with:
host: ${{ secrets.SERVER_HOST }}
port: ${{ secrets.SERVER_PORT }}
username: ${{ secrets.SERVER_USERNAME }}
password: ${{ secrets.SERVER_PASSWORD }}
local: ./dist
remote: /docker/continew-admin/tmp
# 7、重启 Nginx
- name: Restart
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.SERVER_HOST }}
port: ${{ secrets.SERVER_PORT }}
username: ${{ secrets.SERVER_USERNAME }}
password: ${{ secrets.SERVER_PASSWORD }}
script: |
rm -rf /docker/continew-admin/html/*
mv /docker/continew-admin/tmp/* /docker/continew-admin/html
docker restart nginx

20
.gitignore vendored Normal file
View File

@@ -0,0 +1,20 @@
.DS_Store
node_modules/
dist/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
**/*.log
tests/**/coverage/
tests/e2e/reports
selenium-debug.log
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.local

7
.prettierignore Normal file
View File

@@ -0,0 +1,7 @@
/dist/*
.local
.output.js
/node_modules/**
**/*.svg
**/*.sh

10
.prettierrc.js Normal file
View File

@@ -0,0 +1,10 @@
module.exports = {
tabWidth: 2,
semi: true,
printWidth: 80,
singleQuote: true,
quoteProps: 'consistent',
htmlWhitespaceSensitivity: 'strict',
vueIndentScriptAndStyle: true,
endOfLine: 'auto',
};

30
.stylelintrc.js Normal file
View File

@@ -0,0 +1,30 @@
module.exports = {
extends: [
'stylelint-config-standard',
'stylelint-config-rational-order',
'stylelint-config-prettier',
'stylelint-config-recommended-vue',
],
defaultSeverity: 'warning',
plugins: ['stylelint-order'],
rules: {
'at-rule-no-unknown': [
true,
{
ignoreAtRules: ['plugin'],
},
],
'rule-empty-line-before': [
'always',
{
except: ['after-single-line-comment', 'first-nested'],
},
],
'selector-pseudo-class-no-unknown': [
true,
{
ignorePseudoClasses: ['deep'],
},
],
},
};

202
LICENSE Normal file
View File

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

191
README.md Normal file
View File

@@ -0,0 +1,191 @@
# ContiNew UI
<a href="https://github.com/Charles7c/continew-admin-ui/blob/dev/LICENSE" target="_blank">
<img src="https://img.shields.io/badge/License-Apache--2.0-blue.svg" alt="License" />
</a>
<a href="https://github.com/Charles7c/continew-admin-ui" target="_blank">
<img src="https://img.shields.io/badge/RELEASE-v2.2.0-%23ff3f59.svg" alt="Release" />
</a>
<a href="https://github.com/Charles7c/continew-admin-ui" target="_blank">
<img src="https://img.shields.io/github/stars/Charles7c/continew-admin-ui?style=social" alt="GitHub stars" />
</a>
<a href="https://github.com/Charles7c/continew-admin-ui" target="_blank">
<img src="https://img.shields.io/github/forks/Charles7c/continew-admin-ui?style=social" alt="GitHub forks" />
</a>
<a href="https://gitee.com/Charles7c/continew-admin-ui" target="_blank">
<img src="https://gitee.com/Charles7c/continew-admin-ui/badge/star.svg?theme=white" alt="Gitee stars" />
</a>
<a href="https://gitee.com/Charles7c/continew-admin-ui" target="_blank">
<img src="https://gitee.com/Charles7c/continew-admin-ui/badge/fork.svg?theme=white" alt="Gitee forks" />
</a>
<a href="https://github.com/Charles7c/continew-admin-ui" target="_blank">
<img src="https://img.shields.io/badge/Vue-3.3.7-%236CB52D.svg" alt="Release" />
</a>
<a href="https://github.com/Charles7c/continew-admin-ui" target="_blank">
<img src="https://img.shields.io/badge/Arco Design Vue-2.53.3-%236CB52D.svg" alt="Release" />
</a>
<a href="https://github.com/Charles7c/continew-admin-ui" target="_blank">
<img src="https://img.shields.io/badge/Vite-4.5.1-%236CB52D.svg" alt="Release" />
</a>
<a href="https://github.com/Charles7c/continew-admin-ui" target="_blank">
<img src="https://img.shields.io/badge/TypeScript-5.3.3-%236CB52D.svg" alt="Release" />
</a>
📚 [在线文档](https://doc.charles7c.top) | 🚀 [演示地址](https://cnadmin.charles7c.top)(账号/密码admin/admin123
## 简介
基于 Arco Design Pro 前端模板开发的 ContiNew Admin 前端适配项目。
ContiNew Admin Continue New Admin中后台管理框架/脚手架持续以最新流行技术栈构建拥抱变化迭代优化。依托开源协作模式提升技术透明度、放大集体智慧、共创优秀实践源源不断地为企业级项目开发提供助力。当前采用的技术栈Spring Boot3Java17、Vue3 & Arco Design、Sa-Token、MyBatis Plus、Redisson、Liquibase、JustAuth、Easy Excel、Hutool、TypeScript、Vite4 等。
## 项目源码
| 开源平台 | 前端源码地址 | 后端源码地址 |
| ------------- | ---------------------------------------------- | ------------------------------------------- |
| GitHub | https://github.com/Charles7c/continew-admin-ui | https://github.com/Charles7c/continew-admin |
| Gitee码云 | https://gitee.com/Charles7c/continew-admin-ui | https://gitee.com/Charles7c/continew-admin |
## 系统功能
> **Note**
> 更多功能和优化正在赶来💦,最新项目计划、进展请进群或关注 [任务清单](https://doc.charles7c.top/admin/intro/require.html#任务清单) 和 [更新日志](https://doc.charles7c.top/admin/other/changelog.html)。
- 个人中心:支持基础信息修改、安全设置(密码修改、邮箱绑定、手机号绑定、第三方账号绑定/解绑)、头像裁剪上传、个人操作日志查看
- 用户管理:提供用户的相关配置,新增用户后,默认密码为 123456
- 部门管理:可配置系统组织架构,树形表格展示
- 角色管理:对权限与菜单进行分配,可根据部门设置角色的数据权限
- 菜单管理:已实现菜单动态路由,后端可配置化,支持多级菜单
- 公告管理:提供公告的发布、查看和删除等功能。管理员可以在后台发布公告,并可以设置公告的生效时间、终止时间,以 markdown-it 为内核渲染 Markdown 格式内容显示
- 消息管理:提供消息查看、标记已读、全部已读、删除等功能(适配对接导航栏站内信功能)
- 字典管理:提供对系统公用数据字典的维护,例如:公告类型,支持字典标签背景色和排序等配置
- 文件管理:提供文件上传、下载、预览(目前支持图片、音视频)、重命名、切换视图(列表、网格)等功能
- 存储库管理:提供文件存储库新增、编辑、删除、导出等功能
- 系统配置提供修改系统标题、Logo、favicon 等基础配置功能,以方便用户系统与其自身品牌形象保持一致(暂未开放高级配置)
- 代码生成:提供根据数据库表自动生成相应的前后端 CRUD 代码的功能
- 在线用户:管理当前登录用户,可一键踢下线
- 日志管理:提供在线用户监控、登录日志监控、操作日志监控和系统日志监控等监控功能
## 快速开始
> **Note**
> 更详细的流程,请查看在线文档[《快速开始》](https://doc.charles7c.top/admin/intro/quick-start.html#%E5%89%8D%E7%AB%AF)。
```bash
# 1.克隆本项目
git clone https://github.com/Charles7c/continew-admin.git
# 2.在 IDEVisual Studio Code/WebStorm中打开前端项目 continew-admin-ui
# 3.安装 pnpm配置淘宝源
npm install -g pnpm
pnpm config set registry https://registry.npm.taobao.org
# 4.安装依赖
pnpm i
# 5.启动程序
# 5.1 启动成功:访问 http://localhost:5173/
pnpm dev
# 6.部署
# 6.1 Docker 部署
# 6.1.1 服务器安装好 docker 及 docker-compose参考https://blog.charles7c.top/categories/fragments/2022/10/31/CentOS%E5%AE%89%E8%A3%85Docker
# 6.1.2 执行 pnpm build 进行项目打包,将 dist 目录下的所有文件放到 /docker/continew-admin/html 目录下
# 6.1.3 将 docker 目录上传到服务器 / 目录下并授权chmod -R 777 /docker
# 6.1.4 修改 docker-compose.yml 中的 MySQL 配置、Redis 配置、continew-admin-server 配置、Nginx 配置
# 6.1.5 执行 docker-compose up -d 创建并后台运行所有容器
# 6.2 其他方式部署
```
## 核心技术栈
| 名称 | 版本 | 简介 |
| :----------------------------------------------------------- | :----- | :----------------------------------------------------------- |
| <a href="https://cn.vuejs.org/" target="_blank">Vue</a> | 3.3.7 | 渐进式 JavaScript 框架,易学易用,性能出色,适用场景丰富的 Web 前端框架。 |
| <a href="https://arco.design/vue/docs/start" target="_blank">Arco Design</a> | 2.53.3 | 字节跳动推出的前端 UI 框架,年轻化的色彩和组件设计。 |
| <a href="https://www.typescriptlang.org/zh/" target="_blank">TypeScript</a> | 5.3.3 | TypeScript 是微软开发的一个开源的编程语言,通过在 JavaScript 的基础上添加静态类型定义构建而成。 |
| <a href="https://cn.vitejs.dev/" target="_blank">Vite</a> | 4.5.1 | 下一代的前端工具链,为开发提供极速响应。 |
## 项目结构
```bash
continew-admin-ui
├─ config # 全局 Vite 配置
├─ public # 公共静态资源favicon.ico、logo.svg
├─ src
│ ├─ api # 请求接口
│ │ ├─ demo # 示例模块
│ │ ├─ auth # 认证模块
│ │ ├─ common # 公共模块
│ │ ├─ monitor # 系统监控模块
│ │ ├─ system # 系统管理模块
│ │ └─ tool # 系统工具模块
│ ├─ assets # 静态资源
│ │ ├─ icons # 图标资源
│ │ ├─ images # 图片资源
│ │ └─ style # 样式资源
│ ├─ components # 通用业务组件
│ ├─ config # 全局配置(包含 echarts 主题)
│ │ └─ settings.json # 配置文件
│ ├─ directives # 指令集(如需,可自行补充)
│ ├─ hooks # 全局 hooks
│ ├─ layout # 布局
│ ├─ locale # 国际化语言包
│ ├─ mock # 模拟数据
│ ├─ router # 路由配置
│ ├─ store # 状态管理中心
│ ├─ types # TypeScript 类型
│ ├─ utils # 工具库mock 全局开启/关闭)
│ ├─ views # 页面模板
│ │ ├─ demo # Arco Design 相关示例模块
│ │ ├─ dashboard # 仪表盘模块
│ │ ├─ login # 登录模块
│ │ ├─ monitor # 系统监控模块
│ │ │ ├─ log # 日志管理
│ │ │ │ ├─ login # 登录日志
│ │ │ │ ├─ operation # 操作日志
│ │ │ │ └─ system # 系统日志
│ │ │ └─ online # 在线用户
│ │ └─ system # 系统管理模块
│ │ ├─ announcement # 公告管理
│ │ ├─ config # 系统配置
│ │ ├─ dept # 部门管理
│ │ ├─ dict # 字典管理
│ │ ├─ menu # 菜单管理
│ │ ├─ message # 消息管理
│ │ ├─ role # 角色管理
│ │ └─ user # 用户模块
│ │ └─ center # 个人中心
│ ├─ App.vue
│ └─ main.ts # 入口文件
├─ .env.development
├─ .env.production
├─ index.html
├─ package.json
└─ tsconfig.json
```
## 鸣谢
### 鸣谢
感谢参与贡献的每一位小伙伴🥰
<a href="https://github.com/Charles7c/continew-admin-ui/graphs/contributors">
<img src="https://contrib.rocks/image?repo=Charles7c/continew-admin-ui" />
</a>
### 特别鸣谢
- 感谢 <a href="https://www.jetbrains.com/" target="_blank">JetBrains</a> 提供的 <a href="https://www.jetbrains.com/shop/eform/opensource" target="_blank">非商业开源软件开发授权</a>
- 感谢 <a href="http://pro.arco.design/" target="_blank">Arco Design Pro</a> 开箱即用的中后台前端解决方案
- 感谢 [Gi Admin Pro](https://gitee.com/lin0716/gi-demo),致敬各位作者为开源前端模板领域作出的贡献
- e.g. 扩展于 Gi Admin Pro 项目开源的文件管理组件
- 感谢项目使用或未使用到的每一款开源组件,致敬各位开源先驱 :fire:
## License
- 遵循 <a href="https://github.com/Charles7c/continew-admin-ui/blob/dev/LICENSE" target="_blank">Apache-2.0</a> 开源许可协议
- Copyright © 2022-present <a href="https://blog.charles7c.top" target="_blank">Charles7c</a>

3
babel.config.js Normal file
View File

@@ -0,0 +1,3 @@
module.exports = {
plugins: ['@vue/babel-plugin-jsx'],
};

3
commitlint.config.js Normal file
View File

@@ -0,0 +1,3 @@
module.exports = {
extends: ['@commitlint/config-conventional'],
};

15
components.d.ts vendored Normal file
View File

@@ -0,0 +1,15 @@
/* eslint-disable */
/* prettier-ignore */
// @ts-nocheck
// Generated by unplugin-vue-components
// Read more: https://github.com/vuejs/core/pull/3399
import '@vue/runtime-core'
export {}
declare module '@vue/runtime-core' {
export interface GlobalComponents {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
}

View File

@@ -0,0 +1,19 @@
/**
* If you use the template method for development, you can use the unplugin-vue-components plugin to enable on-demand loading support.
* 按需引入
* https://github.com/antfu/unplugin-vue-components
* https://arco.design/vue/docs/start
* Although the Pro project is full of imported components, this plugin will be used by default.
* 虽然Pro项目中是全量引入组件但此插件会默认使用。
*/
import Components from 'unplugin-vue-components/vite';
import { ArcoResolver } from 'unplugin-vue-components/resolvers';
export default function configArcoResolverPlugin() {
const arcoResolverPlugin = Components({
dirs: [], // Avoid parsing src/components. 避免解析到src/components
deep: false,
resolvers: [ArcoResolver()],
});
return arcoResolverPlugin;
}

View File

@@ -0,0 +1,12 @@
/**
* Theme import
* 样式按需引入
* https://github.com/arco-design/arco-plugins/blob/main/packages/plugin-vite-vue/README.md
* https://arco.design/vue/docs/start
*/
import { vitePluginForArco } from '@arco-plugins/vite-vue';
export default function configArcoStyleImportPlugin() {
const arcoResolverPlugin = vitePluginForArco({});
return arcoResolverPlugin;
}

34
config/plugin/compress.ts Normal file
View File

@@ -0,0 +1,34 @@
/**
* Used to package and output gzip. Note that this does not work properly in Vite, the specific reason is still being investigated
* gzip压缩
* https://github.com/anncwb/vite-plugin-compression
*/
import type { Plugin } from 'vite';
import compressPlugin from 'vite-plugin-compression';
export default function configCompressPlugin(
compress: 'gzip' | 'brotli',
deleteOriginFile = false
): Plugin | Plugin[] {
const plugins: Plugin[] = [];
if (compress === 'gzip') {
plugins.push(
compressPlugin({
ext: '.gz',
deleteOriginFile,
})
);
}
if (compress === 'brotli') {
plugins.push(
compressPlugin({
ext: '.br',
algorithm: 'brotliCompress',
deleteOriginFile,
})
);
}
return plugins;
}

37
config/plugin/imagemin.ts Normal file
View File

@@ -0,0 +1,37 @@
/**
* Image resource files used to compress the output of the production environment
* 图片压缩
* https://github.com/anncwb/vite-plugin-imagemin
*/
import viteImagemin from 'vite-plugin-imagemin';
export default function configImageminPlugin() {
const imageminPlugin = viteImagemin({
gifsicle: {
optimizationLevel: 7,
interlaced: false,
},
optipng: {
optimizationLevel: 7,
},
mozjpeg: {
quality: 20,
},
pngquant: {
quality: [0.8, 0.9],
speed: 4,
},
svgo: {
plugins: [
{
name: 'removeViewBox',
},
{
name: 'removeEmptyAttrs',
active: false,
},
],
},
});
return imageminPlugin;
}

10
config/plugin/svg-icon.ts Normal file
View File

@@ -0,0 +1,10 @@
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons';
import path from 'path';
export default function createSvgIcon(isBuild: boolean) {
return createSvgIconsPlugin({
iconDirs: [path.resolve(process.cwd(), 'src/assets/icons/svg')],
symbolId: 'icon-[dir]-[name]',
svgoOptions: isBuild,
});
}

View File

@@ -0,0 +1,18 @@
/**
* Generation packaging analysis
* 生成打包分析
*/
import visualizer from 'rollup-plugin-visualizer';
import { isReportMode } from '../utils';
export default function configVisualizerPlugin() {
if (isReportMode()) {
return visualizer({
filename: './node_modules/.cache/visualizer/stats.html',
open: true,
gzipSize: true,
brotliSize: true,
});
}
return [];
}

9
config/utils/index.ts Normal file
View File

@@ -0,0 +1,9 @@
/**
* Whether to generate package preview
* 是否生成打包报告
*/
export default {};
export function isReportMode(): boolean {
return process.env.REPORT === 'true';
}

View File

@@ -0,0 +1,51 @@
import { resolve } from 'path';
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import vueJsx from '@vitejs/plugin-vue-jsx';
import svgLoader from 'vite-svg-loader';
// import configArcoStyleImportPlugin from './plugin/arcoStyleImport';
export default defineConfig({
plugins: [
vue(),
vueJsx(),
svgLoader({ svgoConfig: {} }),
// configArcoStyleImportPlugin(),
],
resolve: {
alias: [
{
find: '@',
replacement: resolve(__dirname, '../src'),
},
{
find: 'assets',
replacement: resolve(__dirname, '../src/assets'),
},
{
find: 'vue-i18n',
replacement: 'vue-i18n/dist/vue-i18n.cjs.js', // Resolve the i18n warning issue
},
{
find: 'vue',
replacement: 'vue/dist/vue.esm-bundler.js', // compile template
},
],
extensions: ['.ts', '.js'],
},
define: {
'process.env': {},
},
css: {
preprocessorOptions: {
less: {
modifyVars: {
hack: `true; @import (reference) "${resolve(
'src/assets/style/breakpoint.less'
)}";`,
},
javascriptEnabled: true,
},
},
},
});

25
config/vite.config.dev.ts Normal file
View File

@@ -0,0 +1,25 @@
import { mergeConfig } from 'vite';
import eslint from 'vite-plugin-eslint';
import baseConfig from './vite.config.base';
import createSvgIcon from './plugin/svg-icon';
export default mergeConfig(
{
mode: 'development',
server: {
open: true,
fs: {
strict: true,
},
},
plugins: [
eslint({
cache: false,
include: ['src/**/*.ts', 'src/**/*.tsx', 'src/**/*.vue'],
exclude: ['node_modules'],
}),
createSvgIcon(false),
],
},
baseConfig
);

View File

@@ -0,0 +1,33 @@
import { mergeConfig } from 'vite';
import baseConfig from './vite.config.base';
import configCompressPlugin from './plugin/compress';
import configVisualizerPlugin from './plugin/visualizer';
// import configArcoResolverPlugin from './plugin/arcoResolver';
import configImageminPlugin from './plugin/imagemin';
import createSvgIcon from './plugin/svg-icon';
export default mergeConfig(
{
mode: 'production',
plugins: [
configCompressPlugin('gzip'),
configVisualizerPlugin(),
// configArcoResolverPlugin(),
configImageminPlugin(),
createSvgIcon(true),
],
build: {
rollupOptions: {
output: {
manualChunks: {
arco: ['@arco-design/web-vue'],
chart: ['echarts', 'vue-echarts'],
vue: ['vue', 'vue-router', 'pinia', '@vueuse/core', 'vue-i18n'],
},
},
},
chunkSizeWarningLimit: 2000,
},
},
baseConfig
);

31
index.html Normal file
View File

@@ -0,0 +1,31 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<title></title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
<script>
/* 百度统计代码 */
var _hmt = _hmt || [];
(function() {
var hm = document.createElement("script");
hm.src = "https://hm.baidu.com/hm.js?b0d02b5db5f47cd1ecb885dd33781f21";
var s = document.getElementsByTagName("script")[0];
s.parentNode.insertBefore(hm, s);
/* 百度统计 => 规则设置 => 单页设置 => 启用单页应用数据统计 */
_hmt.push(['_requirePlugin', 'UrlChangeTracker', {
shouldTrackUrlChange: function (newPath, oldPath) {
return newPath && oldPath;
}}
]);
})();
</script>
</body>
</html>

113
package.json Normal file
View File

@@ -0,0 +1,113 @@
{
"name": "continew-admin-ui",
"description": "ContiNew Admin 中后台管理框架Continue New Admin持续以最新流行技术栈构建拥抱变化迭代优化。",
"version": "2.2.0",
"private": true,
"author": "Charles7c",
"license": "Apache-2.0",
"scripts": {
"dev": "vite --host --config ./config/vite.config.dev.ts",
"build": "vue-tsc --noEmit && vite build --config ./config/vite.config.prod.ts",
"report": "cross-env REPORT=true npm run build",
"preview": "npm run build && vite preview --host",
"type:check": "vue-tsc --noEmit --skipLibCheck",
"lint": "eslint . --ext .vue,.js,.ts,.jsx,.tsx --fix",
"lint-staged": "npx lint-staged"
},
"lint-staged": {
"*.{js,ts,jsx,tsx}": [
"prettier --write",
"eslint --fix"
],
"*.vue": [
"stylelint --fix",
"prettier --write",
"eslint --fix"
],
"*.{less,css}": [
"stylelint --fix",
"prettier --write"
]
},
"dependencies": {
"@arco-design/web-vue": "^2.53.3",
"@codemirror/lang-java": "^6.0.1",
"@codemirror/lang-javascript": "^6.2.1",
"@kangc/v-md-editor": "^2.3.18",
"@vueuse/core": "^10.7.0",
"axios": "^0.24.0",
"codemirror": "^6.0.1",
"crypto-js": "^4.2.0",
"dayjs": "^1.11.10",
"echarts": "^5.4.3",
"highlight.js": "^11.9.0",
"jsencrypt": "^3.3.2",
"lodash": "^4.17.21",
"mitt": "^3.0.1",
"nprogress": "^0.2.0",
"pinia": "^2.1.7",
"query-string": "^8.1.0",
"sortablejs": "^1.15.1",
"v-viewer": "^3.0.10",
"viewerjs": "^1.11.6",
"vue": "3.3.7",
"vue-codemirror": "^6.1.1",
"vue-cropper": "^1.1.1",
"vue-echarts": "^6.6.5",
"vue-i18n": "^9.8.0",
"vue-json-pretty": "^2.3.0",
"vue-router": "^4.2.5",
"vue3-colorpicker": "^2.2.3",
"xgplayer": "^2.31.6"
},
"devDependencies": {
"@arco-plugins/vite-vue": "^1.4.5",
"@commitlint/cli": "^18.4.3",
"@commitlint/config-conventional": "^18.4.3",
"@types/crypto-js": "^4.2.1",
"@types/lodash": "^4.14.202",
"@types/mockjs": "^1.0.10",
"@types/nprogress": "^0.2.3",
"@types/sortablejs": "^1.15.7",
"@typescript-eslint/eslint-plugin": "^6.15.0",
"@typescript-eslint/parser": "^6.15.0",
"@vitejs/plugin-vue": "^4.5.2",
"@vitejs/plugin-vue-jsx": "^3.1.0",
"@vue/babel-plugin-jsx": "^1.1.5",
"consola": "^3.2.3",
"cross-env": "^7.0.3",
"eslint": "^8.56.0",
"eslint-config-airbnb-base": "^15.0.0",
"eslint-config-prettier": "^9.1.0",
"eslint-import-resolver-typescript": "^3.6.1",
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-prettier": "^5.1.2",
"eslint-plugin-vue": "^9.19.2",
"less": "^4.2.0",
"lint-staged": "^15.2.0",
"mockjs": "^1.1.0",
"postcss-html": "^1.5.0",
"prettier": "^3.1.1",
"rollup": "^4.9.1",
"rollup-plugin-visualizer": "^5.11.0",
"sass": "^1.69.5",
"stylelint": "^16.0.2",
"stylelint-config-prettier": "^9.0.5",
"stylelint-config-rational-order": "^0.1.2",
"stylelint-config-recommended-vue": "^1.5.0",
"stylelint-config-standard": "^35.0.0",
"stylelint-order": "^6.0.4",
"typescript": "^5.3.3",
"unplugin-vue-components": "^0.26.0",
"vite": "^4.5.1",
"vite-plugin-compression": "^0.5.1",
"vite-plugin-eslint": "^1.8.1",
"vite-plugin-imagemin": "^0.6.1",
"vite-plugin-svg-icons": "^2.0.1",
"vite-svg-loader": "^4.0.0",
"vue-tsc": "^1.8.26"
},
"engines": {
"node": ">=14.0.0"
}
}

10641
pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

9
public/logo.svg Normal file
View File

@@ -0,0 +1,9 @@
<svg width="33" height="33" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 174.8 204">
<path fill="#307AF2" d="M86.7,0l88,51v.2l-16.3,9.4v-.2L86.7,18.9Zm71.8,143.5,16.3,9.4v.2L86.8,204h0l-16.3-9.4,16.3-9.4h0l71.7-41.5v-.2Z"/>
<path fill="#12D2AC" d="M16.3,143.5v.2L58,167.8l-16.3,9.4L0,153.1v-.2Z"/>
<path fill="#12D2AC" d="M104.1,93,15.9,143.8l-.2-.1V124.9l.2.1L87.7,83.6,104.1,93Z"/>
<path fill="#0057FE" d="M88.1,0,.1,51v.2l16.3,9.4v-.2L88.1,18.9Z"/>
<path fill="#307AF2" d="M.1,50.9.2,152.6l.2.1,16.3-9.4-.2-.1-.1-82.9L.1,50.9Z"/>
<path fill="#0057FE" d="M174.7,50.9l-.1,101.7-.2.1-16.3-9.4.2-.1.1-82.9Z"/>
<path fill="#12D2AC" d="M41.7,158.5l16.1,9.4,100.6-58.7V90.4Z"/>
</svg>

After

Width:  |  Height:  |  Size: 683 B

2
public/robots.txt Normal file
View File

@@ -0,0 +1,2 @@
User-agent: *
Disallow: /

26
src/App.vue Normal file
View File

@@ -0,0 +1,26 @@
<template>
<a-config-provider :locale="locale">
<router-view />
<global-setting />
</a-config-provider>
</template>
<script lang="ts" setup>
import { computed } from 'vue';
import enUS from '@arco-design/web-vue/es/locale/lang/en-us';
import zhCN from '@arco-design/web-vue/es/locale/lang/zh-cn';
import GlobalSetting from '@/components/global-setting/index.vue';
import useLocale from '@/hooks/locale';
const { currentLocale } = useLocale();
const locale = computed(() => {
switch (currentLocale.value) {
case 'zh-CN':
return zhCN;
case 'en-US':
return enUS;
default:
return enUS;
}
});
</script>

58
src/api/auth/index.ts Normal file
View File

@@ -0,0 +1,58 @@
import axios from 'axios';
import type { RouteRecordNormalized } from 'vue-router';
import { UserState } from '@/store/modules/user/types';
const BASE_URL = '/auth';
export interface AccountLoginReq {
username?: string;
password?: string;
captcha: string;
uuid?: string;
}
export interface LoginRes {
token: string;
}
export function accountLogin(req: AccountLoginReq) {
return axios.post<LoginRes>(`${BASE_URL}/account`, req);
}
export interface EmailLoginReq {
email: string;
captcha: string;
}
export function emailLogin(req: EmailLoginReq) {
return axios.post<LoginRes>(`${BASE_URL}/email`, req);
}
export interface PhoneLoginReq {
phone: string;
captcha: string;
}
export function phoneLogin(req: PhoneLoginReq) {
return axios.post<LoginRes>(`${BASE_URL}/phone`, req);
}
export function logout() {
return axios.post(`${BASE_URL}/logout`);
}
export function getUserInfo() {
return axios.get<UserState>(`${BASE_URL}/user/info`);
}
export function listRoute() {
return axios.get<RouteRecordNormalized[]>(`${BASE_URL}/route`);
}
export function socialAuth(source: string) {
return axios.get<string>(`/oauth/${source}`);
}
export function socialLogin(source: string, req: any) {
return axios.post<LoginRes>(`/oauth/${source}`, req);
}

63
src/api/common/captcha.ts Normal file
View File

@@ -0,0 +1,63 @@
import axios from 'axios';
import qs from 'query-string';
const BASE_URL = '/captcha';
export interface ImageCaptchaRes {
uuid: string;
img: string;
}
export interface BehaviorCaptchaRes {
originalImageBase64: string;
point: {
x: number;
y: number;
};
jigsawImageBase64: string;
token: string;
secretKey: string;
}
export interface BehaviorCaptchaReq {
captchaType?: string;
captchaVerification?: string;
clientUid?: string;
}
export interface CheckBehaviorCaptchaRes {
repCode: string;
repMsg: string;
}
export function getImageCaptcha() {
return axios.get<ImageCaptchaRes>(`${BASE_URL}/img`);
}
export function getMailCaptcha(email: string) {
return axios.get(`${BASE_URL}/mail?email=${email}`);
}
export function getSmsCaptcha(
phone: string,
behaviorCaptcha: BehaviorCaptchaReq,
) {
return axios.get(
`${BASE_URL}/sms?phone=${phone}&captchaVerification=${encodeURIComponent(
behaviorCaptcha.captchaVerification || '',
)}`,
);
}
export function getBehaviorCaptcha(params: any) {
return axios.get<BehaviorCaptchaRes>(`${BASE_URL}/behavior`, {
params,
paramsSerializer: (obj) => {
return qs.stringify(obj);
},
});
}
export function checkBehaviorCaptcha(params: any) {
return axios.post<CheckBehaviorCaptchaRes>(`${BASE_URL}/behavior`, params);
}

View File

@@ -0,0 +1,65 @@
import axios from 'axios';
const BASE_URL = '/dashboard';
export interface DashboardTotalRecord {
pvCount: number;
ipCount: number;
todayPvCount: number;
newPvFromYesterday: number;
}
export interface DashboardAccessTrendRecord {
date: string;
pvCount: number;
ipCount: number;
}
export interface DashboardPopularModuleRecord {
module: string;
pvCount: number;
newPvFromYesterday: number;
}
export interface DashboardGeoDistributionRecord {
locations: string[];
locationIpStatistics: [];
}
export interface DashboardAnnouncementRecord {
id: number;
title: string;
type: number;
}
export interface DashboardRecentlyVisitedRecord {
title?: string;
path: string;
icon?: string;
}
export function getTotal() {
return axios.get<DashboardTotalRecord>(`${BASE_URL}/total`);
}
export function listAccessTrend(days: number) {
return axios.get<DashboardAccessTrendRecord[]>(
`${BASE_URL}/access/trend/${days}`,
);
}
export function listPopularModule() {
return axios.get<DashboardPopularModuleRecord[]>(
`${BASE_URL}/popular/module`,
);
}
export function getGeoDistribution() {
return axios.get<DashboardGeoDistributionRecord>(
`${BASE_URL}/geo/distribution`,
);
}
export function listAnnouncement() {
return axios.get<DashboardAnnouncementRecord[]>(`${BASE_URL}/announcement`);
}

54
src/api/common/index.ts Normal file
View File

@@ -0,0 +1,54 @@
import axios from 'axios';
import qs from 'query-string';
import { ListParam as DeptParam } from '@/api/system/dept';
import { ListParam as MenuParam } from '@/api/system/menu';
import { ListParam as RoleParam } from '@/api/system/role';
import { ListParam as OptionParam } from '@/api/system/config';
import { TreeNodeData } from '@arco-design/web-vue';
import { LabelValueState } from '@/store/modules/dict/types';
const BASE_URL = '/common';
export function listDeptTree(params: DeptParam) {
return axios.get<TreeNodeData[]>(`${BASE_URL}/tree/dept`, {
params,
paramsSerializer: (obj) => {
return qs.stringify(obj);
},
});
}
export function listMenuTree(params: MenuParam) {
return axios.get<TreeNodeData[]>(`${BASE_URL}/tree/menu`, {
params,
paramsSerializer: (obj) => {
return qs.stringify(obj);
},
});
}
export function listRoleDict(params: RoleParam) {
return axios.get<LabelValueState[]>(`${BASE_URL}/dict/role`, {
params,
paramsSerializer: (obj) => {
return qs.stringify(obj);
},
});
}
export function listDict(code: string) {
return axios.get<LabelValueState[]>(`${BASE_URL}/dict/${code}`);
}
export function listOption(params: OptionParam) {
return axios.get<LabelValueState[]>(`${BASE_URL}/option`, {
params,
paramsSerializer: (obj) => {
return qs.stringify(obj);
},
});
}
export function upload(data: FormData) {
return axios.post(`${BASE_URL}/file`, data);
}

21
src/api/demo/form.ts Normal file
View File

@@ -0,0 +1,21 @@
import axios from 'axios';
export interface BaseInfoModel {
activityName: string;
channelType: string;
promotionTime: string[];
promoteLink: string;
}
export interface ChannelInfoModel {
advertisingSource: string;
advertisingMedia: string;
keyword: string[];
pushNotify: boolean;
advertisingContent: string;
}
export type UnitChannelModel = BaseInfoModel & ChannelInfoModel;
export function submitChannelForm(data: UnitChannelModel) {
return axios.post('/api/channel-form/submit', { data });
}

56
src/api/demo/list.ts Normal file
View File

@@ -0,0 +1,56 @@
import axios from 'axios';
import qs from 'query-string';
import type { DescData } from '@arco-design/web-vue/es/descriptions/interface';
export interface PolicyRecord {
id: string;
number: number;
name: string;
contentType: 'img' | 'horizontalVideo' | 'verticalVideo';
filterType: 'artificial' | 'rules';
count: number;
status: 'online' | 'offline';
createdTime: string;
}
export interface PolicyParams extends Partial<PolicyRecord> {
current: number;
pageSize: number;
}
export interface PolicyListRes {
list: PolicyRecord[];
total: number;
}
export function queryPolicyList(params: PolicyParams) {
return axios.get<PolicyListRes>('/api/list/policy', {
params,
paramsSerializer: (obj) => {
return qs.stringify(obj);
},
});
}
export interface ServiceRecord {
id: number;
title: string;
description: string;
name?: string;
actionType?: string;
icon?: string;
data?: DescData[];
enable?: boolean;
expires?: boolean;
}
export function queryInspectionList() {
return axios.get('/api/list/quality-inspection');
}
export function queryTheServiceList() {
return axios.get('/api/list/the-service');
}
export function queryRulesPresetList() {
return axios.get('/api/list/rules-preset');
}

38
src/api/demo/message.ts Normal file
View File

@@ -0,0 +1,38 @@
import axios from 'axios';
export interface MessageRecord {
id: number;
type: string;
title: string;
subTitle: string;
avatar?: string;
content: string;
time: string;
status: 0 | 1;
messageType?: number;
}
export type MessageListType = MessageRecord[];
export function queryMessageList() {
return axios.get<MessageListType>('/api/message/list');
}
interface MessageStatus {
ids: number[];
}
export function setMessageStatus(data: MessageStatus) {
return axios.post<MessageListType>('/api/message/read', data);
}
export interface ChatRecord {
id: number;
username: string;
content: string;
time: string;
isCollect: boolean;
}
export function queryChatList() {
return axios.get<ChatRecord[]>('/api/chat/list');
}

49
src/api/demo/profile.ts Normal file
View File

@@ -0,0 +1,49 @@
import axios from 'axios';
export interface ProfileBasicRes {
status: number;
video: {
mode: string;
acquisition: {
resolution: string;
frameRate: number;
};
encoding: {
resolution: string;
rate: {
min: number;
max: number;
default: number;
};
frameRate: number;
profile: string;
};
};
audio: {
mode: string;
acquisition: {
channels: number;
};
encoding: {
channels: number;
rate: number;
profile: string;
};
};
}
export function queryProfileBasic() {
return axios.get<ProfileBasicRes>('/api/profile/basic');
}
export type operationLogRes = Array<{
key: string;
contentNumber: string;
updateContent: string;
status: number;
updateTime: string;
}>;
export function queryOperationLog() {
return axios.get<operationLogRes>('/api/operation/log');
}

View File

@@ -0,0 +1,73 @@
import axios from 'axios';
import { GeneralChart } from '@/types/global';
export interface ChartDataRecord {
x: string;
y: number;
name: string;
}
export interface DataChainGrowth {
quota: string;
}
export interface DataChainGrowthRes {
count: number;
growth: number;
chartData: {
xAxis: string[];
data: { name: string; value: number[] };
};
}
export function queryDataChainGrowth(data: DataChainGrowth) {
return axios.post<DataChainGrowthRes>('/api/data-chain-growth', data);
}
export interface PopularAuthorRes {
list: {
ranking: number;
author: string;
contentCount: number;
clickCount: number;
}[];
}
export function queryPopularAuthor() {
return axios.get<PopularAuthorRes>('/api/popular-author/list');
}
export interface ContentPublishRecord {
x: string[];
y: number[];
name: string;
}
export function queryContentPublish() {
return axios.get<ContentPublishRecord[]>('/api/content-publish');
}
export function queryContentPeriodAnalysis() {
return axios.get<GeneralChart>('/api/content-period-analysis');
}
export interface PublicOpinionAnalysis {
quota: string;
}
export interface PublicOpinionAnalysisRes {
count: number;
growth: number;
chartData: ChartDataRecord[];
}
export function queryPublicOpinionAnalysis(data: DataChainGrowth) {
return axios.post<PublicOpinionAnalysisRes>(
'/api/public-opinion-analysis',
data
);
}
export interface DataOverviewRes {
xAxis: string[];
data: Array<{ name: string; value: number[]; count: number }>;
}
export function queryDataOverview() {
return axios.get<DataOverviewRes>('/api/data-overview');
}

107
src/api/monitor/log.ts Normal file
View File

@@ -0,0 +1,107 @@
import axios from 'axios';
import qs from 'query-string';
const BASE_URL = '/monitor/log';
export interface LogRecord {
id?: number;
ip: string;
address: string;
browser: string;
os: string;
createTime: string;
}
export interface LoginLogRecord extends LogRecord {
description: string;
status: number;
errorMsg: string;
createUserString: string;
}
export interface OperationLogRecord extends LogRecord {
module: string;
description: string;
status: number;
errorMsgString: string;
createUserString: string;
}
export interface SystemLogRecord extends LogRecord {
statusCode: number;
requestMethod: string;
requestUrl: string;
timeTaken: number;
}
export interface SystemLogDetailRecord extends SystemLogRecord {
requestHeaders: string;
requestBody: string;
responseHeaders: string;
responseBody: string;
}
export interface LoginLogParam extends Partial<LoginLogRecord> {
page: number;
size: number;
sort: Array<string>;
}
export interface LoginLogListRes {
list: LoginLogRecord[];
total: number;
}
export function listLoginLog(params: LoginLogParam) {
return axios.get<LoginLogListRes>(`${BASE_URL}/login`, {
params,
paramsSerializer: (obj) => {
return qs.stringify(obj);
},
});
}
export interface OperationLogParam extends Partial<OperationLogRecord> {
page: number;
size: number;
sort: Array<string>;
uid?: string;
}
export interface OperationLogListRes {
list: OperationLogRecord[];
total: number;
}
export function listOperationLog(params: OperationLogParam) {
return axios.get<OperationLogListRes>(`${BASE_URL}/operation`, {
params,
paramsSerializer: (obj) => {
return qs.stringify(obj);
},
});
}
export interface SystemLogParam extends Partial<SystemLogRecord> {
page: number;
size: number;
sort: Array<string>;
}
export interface SystemLogListRes {
list: SystemLogRecord[];
total: number;
}
export function listSystemLog(params: SystemLogParam) {
return axios.get<SystemLogListRes>(`${BASE_URL}/system`, {
params,
paramsSerializer: (obj) => {
return qs.stringify(obj);
},
});
}
export function getSystemLog(id: number) {
return axios.get<SystemLogDetailRecord>(`${BASE_URL}/system/${id}`);
}

39
src/api/monitor/online.ts Normal file
View File

@@ -0,0 +1,39 @@
import axios from 'axios';
import qs from 'query-string';
const BASE_URL = '/monitor/online/user';
export interface DataRecord {
token: string;
username: string;
nickname: string;
ip: string;
address: string;
browser: string;
os: string;
loginTime: string;
}
export interface ListParam extends Partial<DataRecord> {
page: number;
size: number;
sort: Array<string>;
}
export interface ListRes {
list: DataRecord[];
total: number;
}
export function list(params: ListParam) {
return axios.get<ListRes>(BASE_URL, {
params,
paramsSerializer: (obj) => {
return qs.stringify(obj);
},
});
}
export function kickout(token: string) {
return axios.delete(`${BASE_URL}/${token}`);
}

View File

@@ -0,0 +1,59 @@
import axios from 'axios';
import qs from 'query-string';
const BASE_URL = '/system/announcement';
export interface DataRecord {
id?: number;
title?: string;
content?: string;
status?: number;
type?: string;
effectiveTime?: string;
terminateTime?: string;
createUser?: string;
createTime?: string;
updateUser?: string;
updateTime?: string;
createUserString?: string;
updateUserString?: string;
}
export interface ListParam {
title?: string;
status?: number;
type?: string;
page?: number;
size?: number;
sort?: Array<string>;
}
export interface ListRes {
list: DataRecord[];
total: number;
}
export function list(params: ListParam) {
return axios.get<ListRes>(`${BASE_URL}`, {
params,
paramsSerializer: (obj) => {
return qs.stringify(obj);
},
});
}
export function get(id: number) {
return axios.get<DataRecord>(`${BASE_URL}/${id}`);
}
export function add(req: DataRecord) {
return axios.post(BASE_URL, req);
}
export function update(req: DataRecord, id: number) {
return axios.put(`${BASE_URL}/${id}`, req);
}
export function del(ids: number | Array<number>) {
return axios.delete(`${BASE_URL}/${ids}`);
}

39
src/api/system/config.ts Normal file
View File

@@ -0,0 +1,39 @@
import axios from 'axios';
import qs from 'query-string';
const BASE_URL = '/system/option';
export interface BasicConfigRecord {
site_title?: string;
site_copyright?: string;
site_logo?: string;
site_favicon?: string;
}
export interface DataRecord {
name?: string;
code: string;
value: string;
description?: string;
}
export interface ListParam {
code?: Array<string>;
}
export function list(params: ListParam) {
return axios.get<DataRecord[]>(`${BASE_URL}`, {
params,
paramsSerializer: (obj) => {
return qs.stringify(obj);
},
});
}
export function save(req: DataRecord[]) {
return axios.patch(`${BASE_URL}`, req);
}
export function resetValue(params: ListParam) {
return axios.patch(`${BASE_URL}/value`, params);
}

51
src/api/system/dept.ts Normal file
View File

@@ -0,0 +1,51 @@
import axios from 'axios';
import qs from 'query-string';
const BASE_URL = '/system/dept';
export interface DataRecord {
id?: number;
name?: string;
parentId?: number;
description?: string;
sort?: number;
status?: number;
isSystem?: boolean;
createUserString?: string;
createTime?: string;
updateUserString?: string;
updateTime?: string;
children?: Array<DataRecord>;
parentName?: string;
disabled?: boolean;
}
export interface ListParam {
name?: string;
status?: number;
}
export function list(params: ListParam) {
return axios.get<DataRecord[]>(`${BASE_URL}/tree`, {
params,
paramsSerializer: (obj) => {
return qs.stringify(obj);
},
});
}
export function get(id: number) {
return axios.get<DataRecord>(`${BASE_URL}/${id}`);
}
export function add(req: DataRecord) {
return axios.post(BASE_URL, req);
}
export function update(req: DataRecord, id: number) {
return axios.put(`${BASE_URL}/${id}`, req);
}
export function del(ids: number | Array<number>) {
return axios.delete(`${BASE_URL}/${ids}`);
}

View File

@@ -0,0 +1,57 @@
import axios from 'axios';
import qs from 'query-string';
const BASE_URL = '/system/dict/item';
export interface DataRecord {
id?: number;
label?: string;
value?: string;
color?: string;
sort?: number;
description?: string;
dictId?: number;
createUser?: string;
createTime?: string;
updateUser?: string;
updateTime?: string;
createUserString?: string;
updateUserString?: string;
}
export interface ListParam {
dictId?: number;
page?: number;
size?: number;
sort?: Array<string>;
}
export interface ListRes {
list: DataRecord[];
total: number;
}
export function list(params: ListParam) {
return axios.get<ListRes>(`${BASE_URL}`, {
params,
paramsSerializer: (obj) => {
return qs.stringify(obj);
},
});
}
export function get(id: number) {
return axios.get<DataRecord>(`${BASE_URL}/${id}`);
}
export function add(req: DataRecord) {
return axios.post(BASE_URL, req);
}
export function update(req: DataRecord, id: number) {
return axios.put(`${BASE_URL}/${id}`, req);
}
export function del(ids: number | Array<number>) {
return axios.delete(`${BASE_URL}/${ids}`);
}

55
src/api/system/dict.ts Normal file
View File

@@ -0,0 +1,55 @@
import axios from 'axios';
import qs from 'query-string';
const BASE_URL = '/system/dict';
export interface DataRecord {
id?: number;
name?: string;
code?: string;
description?: string;
isSystem?: boolean;
createUser?: string;
createTime?: string;
updateUser?: string;
updateTime?: string;
createUserString?: string;
updateUserString?: string;
}
export interface ListParam {
name?: string;
page?: number;
size?: number;
sort?: Array<string>;
}
export interface ListRes {
list: DataRecord[];
total: number;
}
export function list(params: ListParam) {
return axios.get<ListRes>(`${BASE_URL}`, {
params,
paramsSerializer: (obj) => {
return qs.stringify(obj);
},
});
}
export function get(id: number) {
return axios.get<DataRecord>(`${BASE_URL}/${id}`);
}
export function add(req: DataRecord) {
return axios.post(BASE_URL, req);
}
export function update(req: DataRecord, id: number) {
return axios.put(`${BASE_URL}/${id}`, req);
}
export function del(ids: number | Array<number>) {
return axios.delete(`${BASE_URL}/${ids}`);
}

47
src/api/system/file.ts Normal file
View File

@@ -0,0 +1,47 @@
import axios from 'axios';
import qs from 'query-string';
const BASE_URL = '/system/file';
export interface FileItem {
id: string;
name: string;
size: number;
url: string;
extension: string;
type?: string;
storageId?: string;
createUser?: string;
createTime?: string;
updateUser?: string;
updateTime: string;
createUserString?: string;
updateUserString?: string;
}
export interface ListParam {
name?: string;
type?: string;
sort?: Array<string>;
}
export function list(params: ListParam) {
return axios.get<FileItem[]>(`${BASE_URL}/list`, {
params,
paramsSerializer: (obj) => {
return qs.stringify(obj);
},
});
}
export interface FileItemUpdate {
name: string;
}
export function update(req: FileItemUpdate, id: string) {
return axios.put(`${BASE_URL}/${id}`, req);
}
export function del(ids: string | Array<string>) {
return axios.delete(`${BASE_URL}/${ids}`);
}

57
src/api/system/menu.ts Normal file
View File

@@ -0,0 +1,57 @@
import axios from 'axios';
import qs from 'query-string';
const BASE_URL = '/system/menu';
export interface DataRecord {
id?: number;
title?: string;
parentId?: number;
type?: number;
path?: string;
name?: string;
component?: string;
icon?: string;
isExternal?: boolean;
isCache?: boolean;
isHidden?: boolean;
permission?: string;
sort?: number;
status?: number;
createUserString?: string;
createTime?: string;
updateUserString?: string;
updateTime?: string;
children?: Array<DataRecord>;
parentName?: string;
}
export interface ListParam {
name?: string;
status?: number;
}
export function list(params: ListParam) {
return axios.get<DataRecord[]>(`${BASE_URL}/tree`, {
params,
paramsSerializer: (obj) => {
return qs.stringify(obj);
},
});
}
export function get(id: number) {
return axios.get<DataRecord>(`${BASE_URL}/${id}`);
}
export function add(req: DataRecord) {
return axios.post(BASE_URL, req);
}
export function update(req: DataRecord, id: number) {
return axios.put(`${BASE_URL}/${id}`, req);
}
export function del(ids: number | Array<number>) {
return axios.delete(`${BASE_URL}/${ids}`);
}

60
src/api/system/message.ts Normal file
View File

@@ -0,0 +1,60 @@
import axios from 'axios';
import qs from 'query-string';
const BASE_URL = '/system/message';
export interface DataRecord {
id: number;
title: string;
content: string;
type: number;
createUserString?: string;
createTime: string;
isRead: boolean;
readTime: string;
}
export interface ListParam {
title?: string;
type?: number;
isRead?: boolean;
page?: number;
size?: number;
sort?: Array<string>;
}
export interface ListRes {
list: DataRecord[];
total: number;
}
export function list(params: ListParam) {
return axios.get<ListRes>(`${BASE_URL}`, {
params,
paramsSerializer: (obj) => {
return qs.stringify(obj);
},
});
}
export function del(ids: number | Array<number>) {
return axios.delete(`${BASE_URL}/${ids}`);
}
export function read(ids: Array<number>) {
return axios.patch(`${BASE_URL}/read?ids=${ids}`);
}
export interface MessageTypeUnreadRes {
type: number;
count: number;
}
export interface MessageUnreadRes {
total: number;
details: MessageTypeUnreadRes[];
}
export function countUnread(detail: boolean) {
return axios.get<MessageUnreadRes>(`${BASE_URL}/unread?detail=${detail}`);
}

60
src/api/system/role.ts Normal file
View File

@@ -0,0 +1,60 @@
import axios from 'axios';
import qs from 'query-string';
const BASE_URL = '/system/role';
export interface DataRecord {
id?: number;
name?: string;
code?: string;
sort?: number;
description?: string;
menuIds?: Array<number>;
dataScope?: number;
deptIds?: Array<number>;
status?: number;
isSystem?: boolean;
createUserString?: string;
createTime?: string;
updateUserString?: string;
updateTime?: string;
disabled?: boolean;
}
export interface ListParam {
name?: string;
status?: number;
page?: number;
size?: number;
sort?: Array<string>;
}
export interface ListRes {
list: DataRecord[];
total: number;
}
export function list(params: ListParam) {
return axios.get<ListRes>(`${BASE_URL}`, {
params,
paramsSerializer: (obj) => {
return qs.stringify(obj);
},
});
}
export function get(id: number) {
return axios.get<DataRecord>(`${BASE_URL}/${id}`);
}
export function add(req: DataRecord) {
return axios.post(BASE_URL, req);
}
export function update(req: DataRecord, id: number) {
return axios.put(`${BASE_URL}/${id}`, req);
}
export function del(ids: number | Array<number>) {
return axios.delete(`${BASE_URL}/${ids}`);
}

64
src/api/system/storage.ts Normal file
View File

@@ -0,0 +1,64 @@
import axios from 'axios';
import qs from 'query-string';
const BASE_URL = '/system/storage';
export interface DataRecord {
id?: number;
name?: string;
code?: string;
type?: number;
accessKey?: string;
secretKey?: string;
endpoint?: string;
bucketName?: string;
domain?: string;
description?: string;
isDefault?: boolean;
sort?: number;
status?: number;
createUser?: string;
createTime?: string;
updateUser?: string;
updateTime?: string;
createUserString?: string;
updateUserString?: string;
}
export interface ListParam {
name?: string;
status?: string;
page?: number;
size?: number;
sort?: Array<string>;
}
export interface PageRes<T> {
total: number;
list: T;
}
export function list(params: ListParam) {
return axios.get<PageRes<DataRecord[]>>(`${BASE_URL}`, {
params,
paramsSerializer: (obj) => {
return qs.stringify(obj);
},
});
}
export function get(id: number) {
return axios.get<DataRecord>(`${BASE_URL}/${id}`);
}
export function add(req: DataRecord) {
return axios.post(BASE_URL, req);
}
export function update(req: DataRecord, id: number) {
return axios.put(`${BASE_URL}/${id}`, req);
}
export function del(ids: number | Array<number>) {
return axios.delete(`${BASE_URL}/${ids}`);
}

View File

@@ -0,0 +1,86 @@
import axios from 'axios';
const BASE_URL = '/system/user';
export interface BasicInfoModel {
username: string;
nickname: string;
gender: number;
}
export interface AvatarRes {
avatar: string;
}
export interface cropperOptions {
autoCrop: boolean; // 是否默认生成截图框
autoCropWidth: number; // 默认生成截图框宽度
autoCropHeight: number; // 默认生成截图框高度
canMove: boolean; // 上传图片是否可以移动 (默认:true)
centerBox: boolean; // 截图框是否被限制在图片里面 (默认:false)
full: boolean; // 是否输出原图比例的截图 选true生成的图片会非常大 (默认:false)
fixed: boolean; // 是否开启截图框宽高固定比例 (默认:false)
fixedBox: boolean; // 固定截图框大小 不允许改变
img: string | ArrayBuffer | null; // 裁剪图片的地址
outputSize: number; // 裁剪生成图片的质量 (默认:1)
outputType: string; // 默认生成截图为PNG格式
}
export function uploadAvatar(data: FormData) {
return axios.post<AvatarRes>(`${BASE_URL}/avatar`, data);
}
export interface UserBasicInfoUpdateReq {
nickname: string;
gender: number;
}
export function updateBasicInfo(req: UserBasicInfoUpdateReq) {
return axios.patch(`${BASE_URL}/basic/info`, req);
}
export interface UserPasswordUpdateReq {
oldPassword: string;
newPassword: string;
}
export function updatePassword(req: UserPasswordUpdateReq) {
return axios.patch(`${BASE_URL}/password`, req);
}
export interface UserPhoneUpdateReq {
newPhone: string;
captcha: string;
currentPassword: string;
}
export function updatePhone(req: UserPhoneUpdateReq) {
return axios.patch(`${BASE_URL}/phone`, req);
}
export interface UserEmailUpdateReq {
newEmail: string;
captcha: string;
currentPassword: string;
}
export function updateEmail(req: UserEmailUpdateReq) {
return axios.patch(`${BASE_URL}/email`, req);
}
export interface UserSocialBindRecord {
source: string;
description: string;
}
export function listSocial() {
return axios.get<UserSocialBindRecord[]>(`${BASE_URL}/social`);
}
export function bindSocial(source: string, req: any) {
return axios.post(`${BASE_URL}/social/${source}`, req);
}
export function unbindSocial(source: string) {
return axios.delete(`${BASE_URL}/social/${source}`);
}

77
src/api/system/user.ts Normal file
View File

@@ -0,0 +1,77 @@
import axios from 'axios';
import qs from 'query-string';
const BASE_URL = '/system/user';
export interface DataRecord {
id?: number;
username?: string;
nickname?: string;
gender?: number;
email?: string;
phone?: string;
description?: string;
status?: number;
isSystem?: boolean;
pwdResetTime?: string;
createUserString?: string;
createTime?: string;
updateUserString?: string;
updateTime?: string;
deptId?: number;
deptName?: string;
roleIds?: Array<number>;
roleNames?: Array<string>;
disabled?: boolean;
}
export interface ListParam {
username?: string;
status?: number;
createTime?: Array<string>;
page?: number;
size?: number;
sort?: Array<string>;
}
export interface ListRes {
list: DataRecord[];
total: number;
}
export function list(params: ListParam) {
return axios.get<ListRes>(`${BASE_URL}`, {
params,
paramsSerializer: (obj) => {
return qs.stringify(obj);
},
});
}
export function get(id: number) {
return axios.get<DataRecord>(`${BASE_URL}/${id}`);
}
export function add(req: DataRecord) {
return axios.post(BASE_URL, req);
}
export function update(req: DataRecord, id: number) {
return axios.put(`${BASE_URL}/${id}`, req);
}
export function del(ids: number | Array<number>) {
return axios.delete(`${BASE_URL}/${ids}`);
}
export function resetPassword(id: number) {
return axios.patch(`${BASE_URL}/${id}/password`);
}
export interface UpdateUserRoleReq {
roleIds?: Array<number>;
}
export function updateUserRole(req: UpdateUserRoleReq, id: number) {
return axios.patch(`${BASE_URL}/${id}/role`, req);
}

92
src/api/tool/generator.ts Normal file
View File

@@ -0,0 +1,92 @@
import axios from 'axios';
import qs from 'query-string';
const BASE_URL = '/tool/generator';
export interface TableRecord {
tableName: string;
comment?: string;
engine: string;
charset: string;
createTime?: string;
isConfiged: boolean;
}
export interface TableParam {
tableName?: string;
}
export interface TableListRes {
list: TableRecord[];
total: number;
}
export function listTable(params: TableParam) {
return axios.get<TableListRes>(`${BASE_URL}/table`, {
params,
paramsSerializer: (obj) => {
return qs.stringify(obj);
},
});
}
export interface FieldConfigRecord {
tableName: string;
columnName: string;
columnType: string;
fieldName: string;
fieldType: string;
comment: string;
isRequired: boolean;
showInList: boolean;
showInForm: boolean;
showInQuery: boolean;
formType: string;
queryType: string;
createTime?: string;
}
export function listFieldConfig(tableName: string, requireSync: boolean) {
return axios.get<FieldConfigRecord[]>(
`${BASE_URL}/field/${tableName}?requireSync=${requireSync}`,
);
}
export interface GenConfigRecord {
tableName: string;
moduleName: string;
packageName: string;
frontendPath: string;
businessName: string;
author: string;
tablePrefix: string;
isOverride: boolean;
createTime?: string;
updateTime?: string;
}
export function getGenConfig(tableName: string) {
return axios.get<GenConfigRecord>(`${BASE_URL}/config/${tableName}`);
}
export interface GeneratorConfigRecord {
genConfig: GenConfigRecord;
fieldConfigs: FieldConfigRecord[];
}
export function saveConfig(tableName: string, req: GeneratorConfigRecord) {
return axios.post(`${BASE_URL}/config/${tableName}`, req);
}
export interface GeneratePreviewRecord {
fileName: string;
content: string;
}
export function preview(tableName: string) {
return axios.get<GeneratePreviewRecord[]>(`${BASE_URL}/preview/${tableName}`);
}
export function generate(tableName: string) {
return axios.post(`${BASE_URL}/${tableName}`);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

@@ -0,0 +1 @@
<svg width="24" height="24" viewBox="0 0 48 48" fill="none" stroke="currentColor"><path fill-rule="evenodd" clip-rule="evenodd" d="M43 42a1 1 0 011 1v2a1 1 0 01-1 1H29a1 1 0 01-1-1v-2a1 1 0 011-1h14zM24.05 26a1 1 0 01.993.883l.007.117v2a1 1 0 01-.884.993L24.05 30H16c-3.73 0-6.86 2.55-7.75 6a8.294 8.294 0 00-.24 1.588L8 38v2h16.05a1 1 0 01.993.883l.007.117v2a1 1 0 01-.884.993L24.05 44H6a2.003 2.003 0 01-1.994-1.85L4 42v-4c0-6.525 5.206-11.834 11.695-11.996L16 26h8.05zM43 34a1 1 0 011 1v2a1 1 0 01-1 1H29a1 1 0 01-1-1v-2a1 1 0 011-1h14zm0-8a1 1 0 011 1v2a1 1 0 01-1 1H29a1 1 0 01-1-1v-2a1 1 0 011-1h14zM21 3c5.52 0 10 4.477 10 10s-4.48 10-10 10-10-4.477-10-10S15.48 3 21 3zm0 4c-3.31 0-6 2.686-6 6s2.69 6 6 6 6-2.686 6-6-2.69-6-6-6z" fill="currentColor"/></svg>

After

Width:  |  Height:  |  Size: 764 B

View File

@@ -0,0 +1 @@
<svg width="48" height="48" viewBox="0 0 32 32" fill="currentColor"><path fill-rule="evenodd" clip-rule="evenodd" d="M25.947 7.967a.835.835 0 00-1.17-.764L6.378 15.28a.665.665 0 01-.534-1.218l18.397-8.077a2.165 2.165 0 013.035 1.982v15.508a2.165 2.165 0 01-2.952 2.017L5.87 18.29a.665.665 0 01.484-1.239l18.455 7.202a.835.835 0 001.138-.778V7.967z" fill="currentColor"/><path fill-rule="evenodd" clip-rule="evenodd" d="M18.407 23.399a4.665 4.665 0 01-8.672-3.444l.038-.096a.665.665 0 111.236.491l-.038.096a3.335 3.335 0 006.2 2.462l.037-.096a.665.665 0 011.237.491l-.038.096zM5.734 11.306c.368 0 .665.297.665.665v8.5a.665.665 0 01-1.33 0v-8.5c0-.368.298-.665.665-.665z" fill="currentColor"/></svg>

After

Width:  |  Height:  |  Size: 697 B

View File

@@ -0,0 +1 @@
<svg viewBox="0 0 48 48" fill="none" stroke="currentColor" stroke-width="4"><path d="M44 9H4m38 20H6m28-10H14m20 20H14"/></svg>

After

Width:  |  Height:  |  Size: 127 B

View File

@@ -0,0 +1 @@
<svg viewBox="0 0 48 48" fill="none" stroke="currentColor" stroke-width="4"><path d="M44 9H4m36 20H4m21-10H4m21 20H4"/></svg>

After

Width:  |  Height:  |  Size: 125 B

View File

@@ -0,0 +1 @@
<svg viewBox="0 0 48 48" fill="none" stroke="currentColor" stroke-width="4"><path d="M4 9h40M8 29h36M23 19h21M23 39h21"/></svg>

After

Width:  |  Height:  |  Size: 127 B

View File

@@ -0,0 +1 @@
<svg width="48" height="48" viewBox="0 0 48 48" fill="currentColor"><path fill-rule="evenodd" clip-rule="evenodd" d="M25.657 3.69l.168.113L35.783 11H42a2 2 0 012 2v28a2 2 0 01-2 2H6a2 2 0 01-2-2V13a2 2 0 012-2h6.352l9.958-7.197a3 3 0 013.347-.114zM40 15H8v24h32V15zM25 30a1 1 0 00-1-1H13a1 1 0 00-1 1v2a1 1 0 001 1h11a1 1 0 001-1v-2zm10-9a1 1 0 011 1v2a1 1 0 01-1 1H13a1 1 0 01-1-1v-2a1 1 0 011-1h22zM19.18 11l4.888-3.532L28.954 11H19.18z" fill="currentColor"/></svg>

After

Width:  |  Height:  |  Size: 467 B

View File

@@ -0,0 +1 @@
<svg width="48" height="48" viewBox="0 0 28 28" fill="currentColor"><path fill-rule="evenodd" clip-rule="evenodd" d="M16.333 5.333a6 6 0 00-3.246 11.047c-2.308.562-4.32 1.746-6.025 3.548a2.667 2.667 0 00-.729 1.832v1.073l.006.165a2.5 2.5 0 002.494 2.335h7.642l.099-.007A.667.667 0 0016.475 24H8.833l-.127-.007a1.167 1.167 0 01-1.04-1.16V21.76l.009-.145c.031-.287.156-.558.356-.77 2.217-2.346 4.988-3.512 8.354-3.512h.026a6 6 0 00-.078-12zm0 1.334a4.667 4.667 0 110 9.333 4.667 4.667 0 010-9.333zm8.714 12.214a.667.667 0 00-1.008-.868l-4.1 4.1-2.138-2.139-.075-.064a.667.667 0 00-.868 1.007l2.61 2.61a.667.667 0 00.943 0l4.571-4.571.065-.075z" fill="currentColor"/></svg>

After

Width:  |  Height:  |  Size: 670 B

View File

@@ -0,0 +1 @@
<svg viewBox="0 0 48 48" fill="none" stroke="currentColor" stroke-width="4"><path stroke="#4E5969" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" d="M7 7h13v13H7zM28 7h13v13H28zM7 28h13v13H7zM28 28h13v13H28z"/></svg>

After

Width:  |  Height:  |  Size: 233 B

View File

@@ -0,0 +1 @@
<svg viewBox="0 0 48 48" fill="none" stroke="currentColor" stroke-width="4"><rect x="9" y="18" width="30" height="22" rx="1"/><path d="M6 9a1 1 0 011-1h34a1 1 0 011 1v8a1 1 0 01-1 1H7a1 1 0 01-1-1V9zM19 27h10"/></svg>

After

Width:  |  Height:  |  Size: 217 B

View File

@@ -0,0 +1 @@
<svg viewBox="0 0 48 48" fill="none" stroke="currentColor" stroke-width="4"><path d="M11.27 27.728l12.728 12.728 12.728-12.728M24 5v34.295"/></svg>

After

Width:  |  Height:  |  Size: 147 B

View File

@@ -0,0 +1 @@
<svg viewBox="0 0 48 48" fill="none" stroke="currentColor" stroke-width="4"><path d="M24.008 41.99a.01.01 0 01-.016 0l-9.978-11.974A.01.01 0 0114.02 30H33.98a.01.01 0 01.007.016l-9.978 11.975z"/><path d="M24 42L14 30h20L24 42z" fill="#4E5969"/><path stroke="#4E5969" stroke-width="4" d="M22 6h4v26h-4z"/><path fill="#4E5969" d="M22 6h4v26h-4z"/></svg>

After

Width:  |  Height:  |  Size: 351 B

View File

@@ -0,0 +1 @@
<svg viewBox="0 0 48 48" fill="none" stroke="currentColor" stroke-width="4"><path d="M20.272 11.27L7.544 23.998l12.728 12.728M43 24H8.705"/></svg>

After

Width:  |  Height:  |  Size: 146 B

View File

@@ -0,0 +1 @@
<svg viewBox="0 0 48 48" fill="none" stroke="currentColor" stroke-width="4"><path d="M27.728 11.27l12.728 12.728-12.728 12.728M5 24h34.295"/></svg>

After

Width:  |  Height:  |  Size: 147 B

View File

@@ -0,0 +1 @@
<svg viewBox="0 0 48 48" fill="none" stroke="currentColor" stroke-width="4"><path d="M23.992 6.01a.01.01 0 01.016 0l9.978 11.974a.01.01 0 01-.007.016H14.02a.01.01 0 01-.007-.016l9.978-11.975z"/><path d="M24 6l10 12H14L24 6z" fill="#4E5969"/><path stroke="#4E5969" stroke-width="4" d="M26 42h-4V16h4z"/><path fill="#4E5969" d="M26 42h-4V16h4z"/></svg>

After

Width:  |  Height:  |  Size: 350 B

View File

@@ -0,0 +1 @@
<svg viewBox="0 0 48 48" fill="none" stroke="currentColor" stroke-width="4"><path d="M11.27 20.272L23.998 7.544l12.728 12.728M24 43V8.705"/></svg>

After

Width:  |  Height:  |  Size: 146 B

View File

@@ -0,0 +1 @@
<svg viewBox="0 0 48 48" fill="none" stroke="currentColor" stroke-width="4"><path d="M31 23a7 7 0 11-14 0 7 7 0 0114 0zm0 0c0 3.038 2.462 6.5 5.5 6.5A5.5 5.5 0 0042 24c0-9.941-8.059-18-18-18S6 14.059 6 24s8.059 18 18 18c4.244 0 8.145-1.469 11.222-3.925"/></svg>

After

Width:  |  Height:  |  Size: 261 B

View File

@@ -0,0 +1 @@
<svg viewBox="0 0 48 48" fill="none" stroke="currentColor" stroke-width="4"><path d="M29.037 15.236s-9.174 9.267-11.48 11.594c-2.305 2.327-1.646 4.987-.329 6.316 1.317 1.33 3.994 1.953 6.258-.332L37.32 18.851c3.623-3.657 2.092-8.492 0-10.639-2.093-2.147-6.916-3.657-10.54 0L11.3 23.838c-3.623 3.657-3.953 10.638.329 14.96 4.282 4.322 11.115 4.105 14.821.333 3.706-3.773 8.74-8.822 11.224-11.33"/></svg>

After

Width:  |  Height:  |  Size: 402 B

View File

@@ -0,0 +1 @@
<svg viewBox="0 0 48 48" fill="none" stroke="currentColor" stroke-width="4"><path d="M38.293 36.293L26.707 24.707a1 1 0 010-1.414l11.586-11.586c.63-.63 1.707-.184 1.707.707v23.172c0 .89-1.077 1.337-1.707.707zM21 12.414v23.172c0 .89-1.077 1.337-1.707.707L7.707 24.707a1 1 0 010-1.414l11.586-11.586c.63-.63 1.707-.184 1.707.707z"/></svg>

After

Width:  |  Height:  |  Size: 335 B

View File

@@ -0,0 +1 @@
<svg viewBox="0 0 48 48" fill="none" stroke="currentColor" stroke-width="4"><path d="M41 7H29v34h12V7ZM29 18H18v23h11V18ZM18 29H7v12h11V29Z"></path></svg>

After

Width:  |  Height:  |  Size: 154 B

View File

@@ -0,0 +1 @@
<svg width="24" height="24" viewBox="0 0 48 48" fill="currentColor"><path fill-rule="evenodd" clip-rule="evenodd" d="M23.5 5C33.717 5 42 13.283 42 23.5c0 4.388-1.528 8.42-4.08 11.59l5.808 5.81a1 1 0 010 1.414l-1.414 1.414a1 1 0 01-1.414 0l-5.81-5.808A18.422 18.422 0 0123.5 42C13.283 42 5 33.717 5 23.5S13.283 5 23.5 5zm0 4C15.492 9 9 15.492 9 23.5S15.492 38 23.5 38 38 31.508 38 23.5 31.508 9 23.5 9zm7.832 6.391l1.732 1a1 1 0 01.366 1.366l-5.5 9.526a1 1 0 01-1.261.419l-.105-.053-5.196-3-4 6.928a1 1 0 01-1.366.366l-1.732-1a1 1 0 01-.366-1.366l5.5-9.526a1 1 0 011.366-.366l5.196 3 4-6.928a1 1 0 011.366-.366z" fill="currentColor"/></svg>

After

Width:  |  Height:  |  Size: 639 B

View File

@@ -0,0 +1 @@
<svg viewBox="0 0 48 48" fill="none" stroke="currentColor" stroke-width="4"><path d="M19 5.25L22.75 9m0 0l12.043 12.043a1 1 0 010 1.414L32 25.25 21.221 36.029a1 1 0 01-1.428-.014L9.443 25.25l-.763-.793a1 1 0 01.013-1.4L22.75 9zM6 42h36"/><path d="M11.791 25.25c-.881 0-1.332 1.058-.72 1.693l8.722 9.072a1 1 0 001.428.014L32 25.25H11.791z" fill="#4E5969"/><path fill-rule="evenodd" clip-rule="evenodd" d="M40.013 29.812L37.201 27l-2.812 2.812a4 4 0 105.624 0z" fill="#4E5969"/></svg>

After

Width:  |  Height:  |  Size: 482 B

View File

@@ -0,0 +1 @@
<svg viewBox="0 0 48 48" fill="none" stroke="currentColor" stroke-width="4"><path d="M13 24h12a8 8 0 100-16H13.2a.2.2 0 00-.2.2V24zm0 0h16a8 8 0 110 16H13.2a.2.2 0 01-.2-.2V24z"/></svg>

After

Width:  |  Height:  |  Size: 185 B

View File

@@ -0,0 +1 @@
<svg viewBox="0 0 48 48" fill="none" stroke="currentColor" stroke-width="4"><path d="M24 13L7 7v28l17 6 17-6V7l-17 6zm0 0v27.5M19 18l-7-2.5M19 25l-7-2.5M19 32l-7-2.5M29 18l7-2.5M29 25l7-2.5M29 32l7-2.5" stroke="#4E5969" stroke-width="4" stroke-linejoin="round"/></svg>

After

Width:  |  Height:  |  Size: 268 B

View File

@@ -0,0 +1 @@
<svg viewBox="0 0 48 48" fill="none" stroke="currentColor" stroke-width="4"><path d="M16 16h16M16 24h8"></path><path d="M24 41H8V6h32v17"></path><path d="M30 29h11v13l-5.5-3.5L30 42V29Z"/></svg>

After

Width:  |  Height:  |  Size: 194 B

View File

@@ -0,0 +1 @@
<svg viewBox="0 0 48 48" fill="none" stroke="currentColor" stroke-width="4"><path d="M19 10a4 4 0 11-8 0 4 4 0 018 0zM38 10a4 4 0 11-8 0 4 4 0 018 0zM19 38a4 4 0 11-8 0 4 4 0 018 0zM15 15v15m0 3.5V30m0 0c0-5 19-7 19-15"/></svg>

After

Width:  |  Height:  |  Size: 227 B

View File

@@ -0,0 +1 @@
<svg viewBox="0 0 48 48" fill="none" stroke="currentColor" stroke-width="4"><path d="M33 13h7a1 1 0 011 1v12.14a1 1 0 01-.85.99l-21.3 3.24a1 1 0 00-.85.99V43"/><path d="M7 18V8c0-.552.444-1 .997-1H32.01c.552 0 .99.447.99 1v10.002A.998.998 0 0132 19H8a1 1 0 01-1-1z"/></svg>

After

Width:  |  Height:  |  Size: 273 B

View File

@@ -0,0 +1 @@
<svg viewBox="0 0 48 48" fill="none" stroke="currentColor" stroke-width="4"><path d="M35 27h8M5 27h8m0-9h22v13c0 6.075-4.925 11-11 11s-11-4.925-11-11V18z" stroke="#4E5969" stroke-width="4" stroke-linejoin="round"/><path d="M7 42v-.5a6.5 6.5 0 016.5-6.5M7 42v-.5M41 42v-.5a6.5 6.5 0 00-6.5-6.5M13 18h22M7 14a4 4 0 004 4h26a4 4 0 004-4M24 42V23M17 14a7 7 0 1114 0" stroke="#4E5969" stroke-width="4" stroke-linejoin="round"/></svg>

After

Width:  |  Height:  |  Size: 428 B

View File

@@ -0,0 +1 @@
<svg viewBox="0 0 48 48" fill="none" stroke="currentColor" stroke-width="4"><path d="M30.8 32.465c.585-2.576 2.231-4.75 3.77-6.897A12.94 12.94 0 0037 18c0-7.18-5.82-13-13-13s-13 5.82-13 13c0 2.823.9 5.437 2.43 7.568 1.539 2.147 3.185 4.32 3.77 6.897l.623 2.756A1 1 0 0018.8 36H29.2a1 1 0 00.976-.779l.624-2.756zM17 42h14"/></svg>

After

Width:  |  Height:  |  Size: 329 B

View File

@@ -0,0 +1 @@
<svg viewBox="0 0 48 48" fill="none" stroke="currentColor" stroke-width="4"><path d="M7 22h34M8 41h32a1 1 0 001-1V10a1 1 0 00-1-1H8a1 1 0 00-1 1v30a1 1 0 001 1zM34 5v8M14 5v8"/></svg>

After

Width:  |  Height:  |  Size: 183 B

View File

@@ -0,0 +1 @@
<svg viewBox="0 0 48 48" fill="none" stroke="currentColor" stroke-width="4"><path d="M7 22h34V10a1 1 0 00-1-1H8a1 1 0 00-1 1v30a1 1 0 001 1h18M34 5v8M14 5v8"/><path fill-rule="evenodd" clip-rule="evenodd" d="M36 44a9 9 0 100-18 9 9 0 000 18zm1.5-9.75V29h-3v8.25H42v-3h-4.5z" fill="#4E5969"/></svg>

After

Width:  |  Height:  |  Size: 297 B

View File

@@ -0,0 +1 @@
<svg viewBox="0 0 48 48" fill="none" stroke="currentColor" stroke-width="4"><path d="M6 13a1 1 0 011-1h34a1 1 0 011 1v26a1 1 0 01-1 1H7a1 1 0 01-1-1V13z"/><path d="M31 26a7 7 0 11-14 0 7 7 0 0114 0zM33 12l-1.862-3.724A.5.5 0 0030.691 8H17.309a.5.5 0 00-.447.276L15 12"/></svg>

After

Width:  |  Height:  |  Size: 276 B

View File

@@ -0,0 +1 @@
<svg viewBox="0 0 48 48" fill="none" stroke="currentColor" stroke-width="4"><path d="M24.937 34.829a1.2 1.2 0 01-1.874 0L9.56 17.949C8.93 17.165 9.49 16 10.497 16h27.006c1.007 0 1.566 1.164.937 1.95L24.937 34.829z" fill="#4E5969"/></svg>

After

Width:  |  Height:  |  Size: 237 B

View File

@@ -0,0 +1 @@
<svg viewBox="0 0 48 48" fill="none" stroke="currentColor" stroke-width="4"><path d="M13.171 24.937a1.2 1.2 0 010-1.874L30.051 9.56c.785-.629 1.949-.07 1.949.937v27.006c0 1.007-1.164 1.566-1.95.937L13.171 24.937z" fill="#4E5969"/></svg>

After

Width:  |  Height:  |  Size: 236 B

View File

@@ -0,0 +1 @@
<svg viewBox="0 0 48 48" fill="none" stroke="currentColor" stroke-width="4"><path d="M34.829 23.063c.6.48.6 1.394 0 1.874L17.949 38.44c-.785.629-1.949.07-1.949-.937V10.497c0-1.006 1.164-1.566 1.95-.937l16.879 13.503z" fill="#4E5969"/></svg>

After

Width:  |  Height:  |  Size: 240 B

View File

@@ -0,0 +1 @@
<svg viewBox="0 0 48 48" fill="none" stroke="currentColor" stroke-width="4"><path d="M23.063 13.171a1.2 1.2 0 011.874 0l13.503 16.88c.629.785.07 1.949-.937 1.949H10.497c-1.006 0-1.566-1.164-.937-1.95l13.503-16.879z" fill="#4E5969"/></svg>

After

Width:  |  Height:  |  Size: 238 B

View File

@@ -0,0 +1 @@
<svg viewBox="0 0 48 48" fill="none" stroke="currentColor" stroke-width="4"><path d="M42 24c0 9.941-8.059 18-18 18S6 33.941 6 24 14.059 6 24 6s18 8.059 18 18z" fill="#4E5969"/><path d="M15 22l7 7 11.5-11.5" stroke="#fff" stroke-width="4"/></svg>

After

Width:  |  Height:  |  Size: 245 B

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