Initial commit
22
.editorconfig
Normal file
@ -0,0 +1,22 @@
|
||||
# 告诉EditorConfig插件,这是根文件,不用继续往上查找
|
||||
root = true
|
||||
|
||||
# 匹配全部文件
|
||||
[*]
|
||||
# 设置字符集
|
||||
charset = utf-8
|
||||
# 缩进风格,可选space、tab
|
||||
indent_style = space
|
||||
# 缩进的空格数
|
||||
indent_size = 2
|
||||
# 结尾换行符,可选lf、cr、crlf
|
||||
end_of_line = lf
|
||||
# 在文件结尾插入新行
|
||||
insert_final_newline = true
|
||||
# 删除一行中的前后空格
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
# 匹配md结尾的文件
|
||||
[*.md]
|
||||
insert_final_newline = false
|
||||
trim_trailing_whitespace = false
|
16
.env.development
Normal file
@ -0,0 +1,16 @@
|
||||
# 开发环境配置
|
||||
ENV = 'development'
|
||||
|
||||
VITE_APP_API_HOST = 'http://localhost:8888'
|
||||
|
||||
# 开发环境
|
||||
VITE_APP_BASE_API = '/dev-api'
|
||||
|
||||
# 路由前缀
|
||||
VITE_APP_ROUTER_PREFIX = '/'
|
||||
|
||||
# 默认上传地址
|
||||
VITE_APP_UPLOAD_URL = '/Common/UploadFile'
|
||||
|
||||
#socket API
|
||||
VITE_APP_SOCKET_API = '/msghub'
|
15
.env.production
Normal file
@ -0,0 +1,15 @@
|
||||
# 生产环境配置
|
||||
ENV = 'production'
|
||||
|
||||
|
||||
# 生产环境
|
||||
VITE_APP_BASE_API = '/prod-api'
|
||||
|
||||
# 路由前缀
|
||||
VITE_APP_ROUTER_PREFIX = '/'
|
||||
|
||||
# 默认上传地址
|
||||
VITE_APP_UPLOAD_URL = '/Common/UploadFile'
|
||||
|
||||
#socket API
|
||||
VITE_APP_SOCKET_API = '/msghub'
|
18
.env.staging
Normal file
@ -0,0 +1,18 @@
|
||||
# 测试环境配置
|
||||
ENV = 'staging'
|
||||
|
||||
|
||||
# 测试环境
|
||||
VITE_APP_BASE_API = '/stage-api'
|
||||
|
||||
# 路由前缀
|
||||
VITE_APP_ROUTER_PREFIX = '/'
|
||||
|
||||
# 默认上传地址
|
||||
VITE_APP_UPLOAD_URL = '/Common/UploadFile'
|
||||
|
||||
# 是否在打包时开启压缩,支持 gzip 和 brotli
|
||||
VITE_BUILD_COMPRESS = gzip
|
||||
|
||||
#socket API
|
||||
VITE_APP_SOCKET_API = '/msghub'
|
14
.eslintignore
Normal file
@ -0,0 +1,14 @@
|
||||
# 忽略build目录下类型为js的文件的语法检查
|
||||
build/*.js
|
||||
# 忽略src/assets目录下文件的语法检查
|
||||
src/assets
|
||||
# 忽略public目录下文件的语法检查
|
||||
public
|
||||
# 忽略当前目录下为js的文件的语法检查
|
||||
# *.js
|
||||
# 忽略当前目录下为vue的文件的语法检查
|
||||
# *.vue
|
||||
/node_modules/
|
||||
/vite/
|
||||
.eslintrc.js
|
||||
.prettierrc.js
|
199
.eslintrc.js
Normal file
@ -0,0 +1,199 @@
|
||||
// ESlint 检查配置
|
||||
module.exports = {
|
||||
root: true,
|
||||
parserOptions: {
|
||||
parser: 'babel-eslint',
|
||||
sourceType: 'module'
|
||||
},
|
||||
env: {
|
||||
browser: true,
|
||||
node: true,
|
||||
es6: true,
|
||||
},
|
||||
extends: ['plugin:vue/recommended', 'eslint:recommended'],
|
||||
|
||||
// add your custom rules here
|
||||
//it is base on https://github.com/vuejs/eslint-config-vue
|
||||
rules: {
|
||||
"vue/max-attributes-per-line": [2, {
|
||||
"singleline": 10,
|
||||
"multiline": {
|
||||
"max": 1,
|
||||
"allowFirstLine": false
|
||||
}
|
||||
}],
|
||||
"vue/singleline-html-element-content-newline": "off",
|
||||
"vue/multiline-html-element-content-newline":"off",
|
||||
"vue/name-property-casing": ["error", "PascalCase"],
|
||||
"vue/no-v-html": "off",
|
||||
'accessor-pairs': 2,
|
||||
'arrow-spacing': [2, {
|
||||
'before': true,
|
||||
'after': true
|
||||
}],
|
||||
'block-spacing': [2, 'always'],
|
||||
'brace-style': [2, '1tbs', {
|
||||
'allowSingleLine': true
|
||||
}],
|
||||
'camelcase': [0, {
|
||||
'properties': 'always'
|
||||
}],
|
||||
'comma-dangle': [2, 'never'],
|
||||
'comma-spacing': [2, {
|
||||
'before': false,
|
||||
'after': true
|
||||
}],
|
||||
'comma-style': [2, 'last'],
|
||||
'constructor-super': 2,
|
||||
'curly': [2, 'multi-line'],
|
||||
'dot-location': [2, 'property'],
|
||||
'eol-last': 2,
|
||||
'eqeqeq': ["error", "always", {"null": "ignore"}],
|
||||
'generator-star-spacing': [2, {
|
||||
'before': true,
|
||||
'after': true
|
||||
}],
|
||||
'handle-callback-err': [2, '^(err|error)$'],
|
||||
'indent': [2, 2, {
|
||||
'SwitchCase': 1
|
||||
}],
|
||||
'jsx-quotes': [2, 'prefer-single'],
|
||||
'key-spacing': [2, {
|
||||
'beforeColon': false,
|
||||
'afterColon': true
|
||||
}],
|
||||
'keyword-spacing': [2, {
|
||||
'before': true,
|
||||
'after': true
|
||||
}],
|
||||
'new-cap': [2, {
|
||||
'newIsCap': true,
|
||||
'capIsNew': false
|
||||
}],
|
||||
'new-parens': 2,
|
||||
'no-array-constructor': 2,
|
||||
'no-caller': 2,
|
||||
'no-console': 'off',
|
||||
'no-class-assign': 2,
|
||||
'no-cond-assign': 2,
|
||||
'no-const-assign': 2,
|
||||
'no-control-regex': 0,
|
||||
'no-delete-var': 2,
|
||||
'no-dupe-args': 2,
|
||||
'no-dupe-class-members': 2,
|
||||
'no-dupe-keys': 2,
|
||||
'no-duplicate-case': 2,
|
||||
'no-empty-character-class': 2,
|
||||
'no-empty-pattern': 2,
|
||||
'no-eval': 2,
|
||||
'no-ex-assign': 2,
|
||||
'no-extend-native': 2,
|
||||
'no-extra-bind': 2,
|
||||
'no-extra-boolean-cast': 2,
|
||||
'no-extra-parens': [2, 'functions'],
|
||||
'no-fallthrough': 2,
|
||||
'no-floating-decimal': 2,
|
||||
'no-func-assign': 2,
|
||||
'no-implied-eval': 2,
|
||||
'no-inner-declarations': [2, 'functions'],
|
||||
'no-invalid-regexp': 2,
|
||||
'no-irregular-whitespace': 2,
|
||||
'no-iterator': 2,
|
||||
'no-label-var': 2,
|
||||
'no-labels': [2, {
|
||||
'allowLoop': false,
|
||||
'allowSwitch': false
|
||||
}],
|
||||
'no-lone-blocks': 2,
|
||||
'no-mixed-spaces-and-tabs': 2,
|
||||
'no-multi-spaces': 2,
|
||||
'no-multi-str': 2,
|
||||
'no-multiple-empty-lines': [2, {
|
||||
'max': 1
|
||||
}],
|
||||
'no-native-reassign': 2,
|
||||
'no-negated-in-lhs': 2,
|
||||
'no-new-object': 2,
|
||||
'no-new-require': 2,
|
||||
'no-new-symbol': 2,
|
||||
'no-new-wrappers': 2,
|
||||
'no-obj-calls': 2,
|
||||
'no-octal': 2,
|
||||
'no-octal-escape': 2,
|
||||
'no-path-concat': 2,
|
||||
'no-proto': 2,
|
||||
'no-redeclare': 2,
|
||||
'no-regex-spaces': 2,
|
||||
'no-return-assign': [2, 'except-parens'],
|
||||
'no-self-assign': 2,
|
||||
'no-self-compare': 2,
|
||||
'no-sequences': 2,
|
||||
'no-shadow-restricted-names': 2,
|
||||
'no-spaced-func': 2,
|
||||
'no-sparse-arrays': 2,
|
||||
'no-this-before-super': 2,
|
||||
'no-throw-literal': 2,
|
||||
'no-trailing-spaces': 2,
|
||||
'no-undef': 2,
|
||||
'no-undef-init': 2,
|
||||
'no-unexpected-multiline': 2,
|
||||
'no-unmodified-loop-condition': 2,
|
||||
'no-unneeded-ternary': [2, {
|
||||
'defaultAssignment': false
|
||||
}],
|
||||
'no-unreachable': 2,
|
||||
'no-unsafe-finally': 2,
|
||||
'no-unused-vars': [2, {
|
||||
'vars': 'all',
|
||||
'args': 'none'
|
||||
}],
|
||||
'no-useless-call': 2,
|
||||
'no-useless-computed-key': 2,
|
||||
'no-useless-constructor': 2,
|
||||
'no-useless-escape': 0,
|
||||
'no-whitespace-before-property': 2,
|
||||
'no-with': 2,
|
||||
'one-var': [2, {
|
||||
'initialized': 'never'
|
||||
}],
|
||||
'operator-linebreak': [2, 'after', {
|
||||
'overrides': {
|
||||
'?': 'before',
|
||||
':': 'before'
|
||||
}
|
||||
}],
|
||||
'padded-blocks': [2, 'never'],
|
||||
'quotes': [2, 'single', {
|
||||
'avoidEscape': true,
|
||||
'allowTemplateLiterals': true
|
||||
}],
|
||||
'semi': [2, 'never'],
|
||||
'semi-spacing': [2, {
|
||||
'before': false,
|
||||
'after': true
|
||||
}],
|
||||
'space-before-blocks': [2, 'always'],
|
||||
'space-before-function-paren': [2, 'never'],
|
||||
'space-in-parens': [2, 'never'],
|
||||
'space-infix-ops': 2,
|
||||
'space-unary-ops': [2, {
|
||||
'words': true,
|
||||
'nonwords': false
|
||||
}],
|
||||
'spaced-comment': [2, 'always', {
|
||||
'markers': ['global', 'globals', 'eslint', 'eslint-disable', '*package', '!', ',']
|
||||
}],
|
||||
'template-curly-spacing': [2, 'never'],
|
||||
'use-isnan': 2,
|
||||
'valid-typeof': 2,
|
||||
'wrap-iife': [2, 'any'],
|
||||
'yield-star-spacing': [2, 'both'],
|
||||
'yoda': [2, 'never'],
|
||||
'prefer-const': 2,
|
||||
'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0,
|
||||
'object-curly-spacing': [2, 'always', {
|
||||
objectsInObjects: false
|
||||
}],
|
||||
'array-bracket-spacing': [2, 'never']
|
||||
}
|
||||
}
|
23
.gitignore
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
.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
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.local
|
||||
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
/deploy.js
|
12
.jsbeautifyrc
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"brace_style": "none,preserve-inline",
|
||||
"indent_size": 2,
|
||||
"indent_char": " ",
|
||||
"jslint_happy": true,
|
||||
"unformatted": [
|
||||
""
|
||||
],
|
||||
"css": {
|
||||
"indent_size": 2
|
||||
}
|
||||
}
|
38
.prettierrc.js
Normal file
@ -0,0 +1,38 @@
|
||||
module.exports = {
|
||||
// 超过最大值换行
|
||||
printWidth: 148,
|
||||
// 使用 2 个空格缩进
|
||||
tabWidth: 2,
|
||||
// 不使用缩进符,而使用空格
|
||||
useTabs: false,
|
||||
// 行尾不需要有分号
|
||||
semi: false,
|
||||
// 使用单引号
|
||||
singleQuote: true,
|
||||
// 对象的 key 仅在必要时用引号
|
||||
quoteProps: 'as-needed',
|
||||
// jsx 不使用单引号,而使用双引号
|
||||
jsxSingleQuote: false,
|
||||
// 多行时尽可能打印尾随逗号。(例如,单行数组永远不会出现逗号结尾。) 可选值"<none|es5|all>",默认none
|
||||
trailingComma: 'none',
|
||||
// 在对象,数组括号与文字之间加空格 "{ foo: bar }"
|
||||
bracketSpacing: true,
|
||||
// jsx 标签的反尖括号需要换行
|
||||
jsxBracketSameLine: true,
|
||||
bracketSameLine: true,
|
||||
// 箭头函数,always只有一个参数的时候,也需要括号,'avoid'箭头函数只有一个参数的时候可以忽略括号
|
||||
arrowParens: 'always',
|
||||
// 每个文件格式化的范围是文件的全部内容
|
||||
rangeStart: 0,
|
||||
rangeEnd: Infinity,
|
||||
// 不需要写文件开头的 @prettier
|
||||
requirePragma: false,
|
||||
// 不需要自动在文件开头插入 @prettier
|
||||
insertPragma: false,
|
||||
// 使用默认的折行标准
|
||||
proseWrap: 'preserve',
|
||||
// 根据显示样式决定 html 要不要折行
|
||||
htmlWhitespaceSensitivity: 'css',
|
||||
// 换行符使用 lf 结尾是 可选值"<auto|lf|crlf|cr>"
|
||||
endOfLine: 'auto'
|
||||
}
|
3
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"nuxt.isNuxtApp": false
|
||||
}
|
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2023 791736813@qq.com zr
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
89
README.md
Normal file
@ -0,0 +1,89 @@
|
||||
<h2 align="center"> ZRAdmin.NET后台管理系统</h2>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://gitee.com/izory/ZrAdminNetCore"><img src="https://gitee.com/izory/ZrAdminNetCore/badge/star.svg?theme=dark"></a>
|
||||
<a href="https://gitee.com/izory/ZrAdminNetCore/blob/master/LICENSE"><img src="https://img.shields.io/github/license/mashape/apistatus.svg"></a>
|
||||
</p>
|
||||
|
||||
## 🍟概述
|
||||
* 本仓库为前端技术栈 [Vue3](https://v3.cn.vuejs.org) + [Element Plus](https://element-plus.org/zh-CN) + [Vite](https://cn.vitejs.dev) 版本。
|
||||
* 配套后端代码仓库地址[ZRAdmin.NET](https://gitee.com/izory/ZrAdminNetCore/) 版本。
|
||||
* 前端采用Vue3.0、Element UI Plus、vite、compisition api、Pinia等。
|
||||
* 支持加载动态权限菜单,多方式轻松权限控制
|
||||
* 腾讯云秒杀场:[点我进入](https://curl.qcloud.com/4yEoRquq)。
|
||||
* 腾讯云优惠券:[点我领取](https://curl.qcloud.com/5J4nag8D)。
|
||||
* 七牛云通用云产品优惠券:[点我进入](https://s.qiniu.com/FzEfay)。
|
||||
```
|
||||
如果对您有帮助,您可以点右上角 “Star” 收藏一下 ,这样作者才有继续免费下去的动力,谢谢!~
|
||||
```
|
||||
|
||||
## 🍿在线体验
|
||||
- 官方文档:http://www.izhaorui.cn/doc
|
||||
- 体验地址:http://www.izhaorui.cn/vue3
|
||||
- 管理员:admin
|
||||
- 密 码:123456
|
||||
|
||||
```
|
||||
由于是个人项目,资金有限,体验服是低配,请大家爱惜,轻戳,不胜感激!!!
|
||||
```
|
||||
## 🍁前端运行
|
||||
|
||||
```bash
|
||||
# 克隆项目
|
||||
git clone https://gitee.com/izory/ZRAdmin-vue.git
|
||||
|
||||
# 进入项目目录
|
||||
cd ZRAdmin-vue
|
||||
|
||||
# 安装依赖
|
||||
yarn --registry=https://registry.npm.taobao.org
|
||||
|
||||
# 启动服务
|
||||
yarn dev
|
||||
|
||||
# 构建测试环境 yarn build:stage
|
||||
# 构建生产环境 yarn build:prod
|
||||
# 前端访问地址 http://localhost:8887
|
||||
```
|
||||
|
||||
|
||||
## 🍖内置功能
|
||||
|
||||
1. 用户管理:用户是系统操作者,该功能主要完成系统用户配置。
|
||||
2. 部门管理:配置系统组织机构(公司、部门、小组),树结构展现。
|
||||
3. 岗位管理:配置系统用户所属担任职务。
|
||||
4. 菜单管理:配置系统菜单,操作权限,按钮权限标识等。(支持多语言)
|
||||
5. 角色管理:角色菜单权限分配。
|
||||
6. 字典管理:对系统中经常使用的一些较为固定的数据进行维护。
|
||||
7. 操作日志:系统正常操作日志记录和查询;系统异常信息日志记录和查询。
|
||||
8. 登录日志:系统登录日志记录查询包含登录异常。
|
||||
9. 系统接口:使用swagger生成相关api接口文档。
|
||||
10. 参数设置:常用参数配置
|
||||
11. 发送邮件:可以对多个用户进行发送邮件
|
||||
12. 任务系统:基于Quartz.NET,可以在线(添加、修改、删除、手动执行)任务调度包含执行结果日志。
|
||||
13. 文章管理:可以写文章记录。
|
||||
14. 代码生成:可以一键生成前后端代码(.cs、.vue、.js、SQL文件等),支持下载,自定义配置前端展示控件、让开发更快捷高效。
|
||||
15. 文件管理:可以进行上传文件管理,目前支持上传到本地、阿里云
|
||||
16. 通知管理:系统通知公告信息发布维护
|
||||
17. 参数管理:对系统动态配置常用参数。
|
||||
18. 多语言管理:动态修改多语言翻译
|
||||
|
||||
## 🎉优势
|
||||
|
||||
1. 前台系统不用编写登录、授权、认证模块;只负责编写业务模块即可
|
||||
2. 后台系统无需任何二次开发,直接发布即可使用
|
||||
3. 前台与后台系统分离,分别为不同的系统(域名可独立)
|
||||
4. 全局异常统一处理
|
||||
5. 自定义的代码生成功能
|
||||
6. 国际化
|
||||
|
||||
## 💐 特别鸣谢
|
||||
- 👉Ruoyi.vue:[Ruoyi](http://www.ruoyi.vip/)
|
||||
|
||||
## 🎀捐赠
|
||||
如果这个项目对您有所帮助,请扫下方二维码打赏作者喝杯咖啡。
|
||||
<img src="https://gitee.com/izory/ZrAdminNetCore/raw/master/document/images/pay.jpg"/>
|
||||
|
||||
## 源码地址
|
||||
- [Gitee](https://gitee.com/izory/ZrAdminNetCore/)
|
||||
- [Github](https://github.com/izhaorui/ZrAdmin.NET/)
|
12
bat/build.bat
Normal file
@ -0,0 +1,12 @@
|
||||
@echo off
|
||||
echo.
|
||||
echo [信息] 打包Web工程,生成dist文件。
|
||||
echo.
|
||||
|
||||
%~d0
|
||||
cd %~dp0
|
||||
|
||||
cd ..
|
||||
yarn build:prod
|
||||
|
||||
pause
|
12
bat/package.bat
Normal file
@ -0,0 +1,12 @@
|
||||
@echo off
|
||||
echo.
|
||||
echo [信息] 安装Web工程,生成node_modules文件。
|
||||
echo.
|
||||
|
||||
%~d0
|
||||
cd %~dp0
|
||||
|
||||
cd ..
|
||||
yarn --registry=https://registry.npm.taobao.org
|
||||
|
||||
pause
|
12
bat/run-web.bat
Normal file
@ -0,0 +1,12 @@
|
||||
@echo off
|
||||
echo.
|
||||
echo [信息] 使用 Vite 命令运行 Web 工程。
|
||||
echo.
|
||||
|
||||
%~d0
|
||||
cd %~dp0
|
||||
|
||||
cd ..
|
||||
yarn dev
|
||||
|
||||
pause
|
46
html/ie.html
Normal file
139
index.html
Normal file
@ -0,0 +1,139 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
<meta name="renderer" content="webkit">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
|
||||
<link rel="icon" href="/favicon.ico">
|
||||
<title>阿尔文科技后台</title>
|
||||
<!--[if lt IE 11]><script>window.location.href='/html/ie.html';</script><![endif]-->
|
||||
<style>
|
||||
html,
|
||||
body,
|
||||
#app {
|
||||
height: 100%;
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
.chromeframe {
|
||||
margin: 0.2em 0;
|
||||
background: #ccc;
|
||||
color: #000;
|
||||
padding: 0.2em 0;
|
||||
}
|
||||
|
||||
.first-loading-wrp {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 90vh;
|
||||
min-height: 90vh;
|
||||
}
|
||||
|
||||
.first-loading-wrp>h1 {
|
||||
font-size: 30px;
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
.first-loading-wrp .loading-wrp {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 48px;
|
||||
}
|
||||
|
||||
.dot {
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
display: inline-block;
|
||||
width: 45px;
|
||||
height: 45px;
|
||||
font-size: 64px;
|
||||
transform: rotate(45deg);
|
||||
animation: antRotate 1.2s infinite linear;
|
||||
}
|
||||
|
||||
.dot i {
|
||||
position: absolute;
|
||||
display: block;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
background-color: #1890ff;
|
||||
border-radius: 100%;
|
||||
opacity: 0.3;
|
||||
transform: scale(0.75);
|
||||
transform-origin: 50% 50%;
|
||||
animation: antSpinMove 1s infinite linear alternate;
|
||||
}
|
||||
|
||||
.dot i:nth-child(1) {
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.dot i:nth-child(2) {
|
||||
top: 0;
|
||||
right: 0;
|
||||
-webkit-animation-delay: 0.4s;
|
||||
animation-delay: 0.4s;
|
||||
}
|
||||
|
||||
.dot i:nth-child(3) {
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
-webkit-animation-delay: 0.8s;
|
||||
animation-delay: 0.8s;
|
||||
}
|
||||
|
||||
.dot i:nth-child(4) {
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
-webkit-animation-delay: 1.2s;
|
||||
animation-delay: 1.2s;
|
||||
}
|
||||
|
||||
@keyframes antRotate {
|
||||
to {
|
||||
-webkit-transform: rotate(405deg);
|
||||
transform: rotate(405deg);
|
||||
}
|
||||
}
|
||||
|
||||
@-webkit-keyframes antRotate {
|
||||
to {
|
||||
-webkit-transform: rotate(405deg);
|
||||
transform: rotate(405deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes antSpinMove {
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@-webkit-keyframes antSpinMove {
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app">
|
||||
<div class="first-loading-wrp">
|
||||
<div class="loading-wrp">
|
||||
<span class="dot dot-spin"> <i></i> <i></i> <i></i> <i></i> </span>
|
||||
</div>
|
||||
<h5>Loading...</h5>
|
||||
</div>
|
||||
</div>
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
11
jsconfig.json
Normal file
@ -0,0 +1,11 @@
|
||||
/* 主要作用是vscode导入使用别名时可以自动化提示文件路径 */
|
||||
{
|
||||
"compilerOptions": {
|
||||
"experimentalDecorators": true,
|
||||
"baseUrl": "./",
|
||||
"paths": {
|
||||
"@/*": ["src/*"]
|
||||
}
|
||||
},
|
||||
"exclude": ["node_modules", "dist", ".vscode"]
|
||||
}
|
55
package.json
Normal file
@ -0,0 +1,55 @@
|
||||
{
|
||||
"name": "arw.admin",
|
||||
"version": "3.8.2",
|
||||
"description": "阿尔文后台管理系统",
|
||||
"author": "Aerwen",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build:prod": "vite build",
|
||||
"build:stage": "vite build --mode staging",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://gitee.com/izory/ZrAdminNetCore"
|
||||
},
|
||||
"dependencies": {
|
||||
"@amap/amap-jsapi-loader": "^1.0.1",
|
||||
"@element-plus/icons-vue": "^0.2.7",
|
||||
"@microsoft/signalr": "^6.0.5",
|
||||
"@vueuse/core": "^8.9.4",
|
||||
"@wangeditor/editor": "^5.1.1",
|
||||
"@wangeditor/editor-for-vue": "^5.1.11",
|
||||
"axios": "^0.27.2",
|
||||
"countup.js": "^2.1.0",
|
||||
"echarts": "5.2.2",
|
||||
"element-plus": "^2.3.2",
|
||||
"file-saver": "2.0.5",
|
||||
"fuse.js": "6.4.6",
|
||||
"highlight.js": "^11.5.1",
|
||||
"js-cookie": "3.0.1",
|
||||
"js-md5": "^0.7.3",
|
||||
"jsencrypt": "3.2.1",
|
||||
"md-editor-v3": "^1.11.11",
|
||||
"nprogress": "0.2.0",
|
||||
"pinia": "^2.0.33",
|
||||
"qs": "^6.11.0",
|
||||
"sortablejs": "^1.15.0",
|
||||
"vue": "^3.2.47",
|
||||
"vue-clipboard3": "^2.0.0",
|
||||
"vue-cropper": "1.0.2",
|
||||
"vue-i18n": "^9.2.2",
|
||||
"vue-router": "^4.1.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^4.1.0",
|
||||
"@vue/compiler-sfc": "^3.2.47",
|
||||
"sass": "1.45.0",
|
||||
"unplugin-auto-import": "0.5.3",
|
||||
"vite": "^4.2.1",
|
||||
"vite-plugin-compression": "^0.3.6",
|
||||
"vite-plugin-svg-icons": "1.0.5",
|
||||
"vite-plugin-vue-setup-extend": "^0.4.0"
|
||||
}
|
||||
}
|
BIN
public/favicon.ico
Normal file
After Width: | Height: | Size: 41 KiB |
BIN
public/logo_500.ico
Normal file
After Width: | Height: | Size: 17 KiB |
56
src/App.vue
Normal file
@ -0,0 +1,56 @@
|
||||
<template>
|
||||
<el-config-provider :locale="locale" :size="size">
|
||||
<router-view />
|
||||
</el-config-provider>
|
||||
</template>
|
||||
<script setup>
|
||||
import useUserStore from './store/modules/user'
|
||||
import useAppStore from './store/modules/app'
|
||||
import { ElConfigProvider } from 'element-plus'
|
||||
import zh from 'element-plus/lib/locale/lang/zh-cn' // 中文语言
|
||||
import en from 'element-plus/lib/locale/lang/en' // 英文语言
|
||||
import tw from 'element-plus/lib/locale/lang/zh-tw' //繁体
|
||||
import defaultSettings from '@/settings'
|
||||
const { proxy } = getCurrentInstance()
|
||||
|
||||
const token = computed(() => {
|
||||
return useUserStore().userId
|
||||
})
|
||||
|
||||
const lang = computed(() => {
|
||||
return useAppStore().lang
|
||||
})
|
||||
const locale = ref(zh)
|
||||
const size = ref(defaultSettings.defaultSize)
|
||||
|
||||
size.value = useAppStore().size
|
||||
watch(
|
||||
token,
|
||||
(val) => {
|
||||
if (val) {
|
||||
proxy.signalr.start()
|
||||
}
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
deep: true
|
||||
}
|
||||
)
|
||||
watch(
|
||||
lang,
|
||||
(val) => {
|
||||
if (val == 'zh-cn') {
|
||||
locale.value = zh
|
||||
} else if (val == 'en') {
|
||||
locale.value = en
|
||||
} else if (val == 'zh-tw') {
|
||||
locale.value = tw
|
||||
} else {
|
||||
locale.value = zh
|
||||
}
|
||||
},
|
||||
{
|
||||
immediate: true
|
||||
}
|
||||
)
|
||||
</script>
|
37
src/api/business/customers/customer.js
Normal file
@ -0,0 +1,37 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 小程序客户分页查询列表
|
||||
export function customerList(query) {
|
||||
return request({
|
||||
url: '/business/Customer/getCustomerList',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// 小程序客户新增或修改
|
||||
export function addOrUpdateCustomer(data) {
|
||||
return request({
|
||||
url: '/business/Customer/addOrUpdateCustomer',
|
||||
method: 'post',
|
||||
data: data,
|
||||
})
|
||||
}
|
||||
|
||||
// 小程序客户删除
|
||||
export function delCustomer(ids) {
|
||||
return request({
|
||||
url: '/business/Customer/'+ ids,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 小程序客户导出
|
||||
export function exportCustomer(query) {
|
||||
return request({
|
||||
url: 'business/Customer/export',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
37
src/api/business/payments/payment.js
Normal file
@ -0,0 +1,37 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 支付订单分页查询列表
|
||||
export function paymentList(query) {
|
||||
return request({
|
||||
url: '/business/Payment/getPaymentList',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// 支付订单新增或修改
|
||||
export function addOrUpdatePayment(data) {
|
||||
return request({
|
||||
url: '/business/Payment/addOrUpdatePayment',
|
||||
method: 'post',
|
||||
data: data,
|
||||
})
|
||||
}
|
||||
|
||||
// 支付订单删除
|
||||
export function delPayment(ids) {
|
||||
return request({
|
||||
url: '/business/Payment/'+ ids,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 支付订单导出
|
||||
export function exportPayment(query) {
|
||||
return request({
|
||||
url: 'business/Payment/export',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
23
src/api/common.js
Normal file
@ -0,0 +1,23 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
export function upload(data) {
|
||||
return request({
|
||||
url: '/common/UploadFile',
|
||||
method: 'POST',
|
||||
data: data,
|
||||
headers: { "Content-Type": "multipart/form-data" },
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送邮件
|
||||
* @param {*} data
|
||||
* @returns
|
||||
*/
|
||||
export function sendEmail(data) {
|
||||
return request({
|
||||
url: '/common/SendEmail',
|
||||
method: 'POST',
|
||||
data: data,
|
||||
})
|
||||
}
|
9
src/api/monitor/cache.js
Normal file
@ -0,0 +1,9 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 查询缓存详细
|
||||
export function getCache() {
|
||||
return request({
|
||||
url: '/monitor/cache',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
114
src/api/monitor/job.js
Normal file
@ -0,0 +1,114 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
export function queryTasks(data) {
|
||||
return request({
|
||||
url: '/system/tasks/list',
|
||||
method: 'get',
|
||||
params: data
|
||||
})
|
||||
}
|
||||
|
||||
export function getTasks(id) {
|
||||
return request({
|
||||
url: '/system/tasks/get?id=' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* 获取所有任务
|
||||
* @returns
|
||||
*/
|
||||
export function getAllTasks() {
|
||||
return request({
|
||||
url: '/system/tasks/getAll',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建任务
|
||||
* @param {*} data
|
||||
* @returns
|
||||
*/
|
||||
export function createTasks(data) {
|
||||
return request({
|
||||
url: '/system/tasks/create',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新任务
|
||||
* @param {*} data
|
||||
* @returns
|
||||
*/
|
||||
export function updateTasks(data) {
|
||||
return request({
|
||||
url: '/system/tasks/update',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除任务
|
||||
* @param {*} id
|
||||
* @returns
|
||||
*/
|
||||
export function deleteTasks(id) {
|
||||
return request({
|
||||
url: '/system/tasks/delete?id=' + id,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动任务
|
||||
* @param {*} id
|
||||
* @returns
|
||||
*/
|
||||
export function startTasks(id) {
|
||||
return request({
|
||||
url: '/system/tasks/start?id=' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止任务
|
||||
* @param {*} id
|
||||
* @returns
|
||||
*/
|
||||
export function stopTasks(id) {
|
||||
return request({
|
||||
url: '/system/tasks/stop?id=' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 运行一次
|
||||
* @param {*} id
|
||||
* @returns
|
||||
*/
|
||||
export function runTasks(id) {
|
||||
return request({
|
||||
url: '/system/tasks/run?id=' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
/**
|
||||
* 导出
|
||||
* @returns
|
||||
*/
|
||||
export function exportTasks() {
|
||||
return request({
|
||||
url: '/system/tasks/export',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export default { queryTasks, getTasks, getAllTasks, createTasks, updateTasks, deleteTasks, startTasks, stopTasks, runTasks, exportTasks }
|
35
src/api/monitor/jobLog.js
Normal file
@ -0,0 +1,35 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 查询调度日志列表
|
||||
export function listJobLog(query) {
|
||||
return request({
|
||||
url: '/monitor/jobLog/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 删除调度日志
|
||||
export function delJobLog(jobLogId) {
|
||||
return request({
|
||||
url: '/monitor/jobLog/' + jobLogId,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 清空调度日志
|
||||
export function cleanJobLog() {
|
||||
return request({
|
||||
url: '/monitor/jobLog/clean',
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 导出调度日志
|
||||
export function exportJobLog(query) {
|
||||
return request({
|
||||
url: '/monitor/jobLog/export',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
35
src/api/monitor/logininfor.js
Normal file
@ -0,0 +1,35 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 查询登录日志列表
|
||||
export function list(query) {
|
||||
return request({
|
||||
url: '/monitor/logininfor/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 删除登录日志
|
||||
export function delLogininfor(infoId) {
|
||||
return request({
|
||||
url: '/monitor/logininfor/' + infoId,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 清空登录日志
|
||||
export function cleanLogininfor() {
|
||||
return request({
|
||||
url: '/monitor/logininfor/clean',
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 导出登录日志
|
||||
export function exportLogininfor(query) {
|
||||
return request({
|
||||
url: '/monitor/logininfor/export',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
18
src/api/monitor/online.js
Normal file
@ -0,0 +1,18 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 查询在线用户列表
|
||||
export function listOnline(query) {
|
||||
return request({
|
||||
url: '/monitor/online/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 强退用户
|
||||
export function forceLogout(tokenId) {
|
||||
return request({
|
||||
url: '/monitor/online/' + tokenId,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
35
src/api/monitor/operlog.js
Normal file
@ -0,0 +1,35 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 查询操作日志列表
|
||||
export function list(query) {
|
||||
return request({
|
||||
url: '/monitor/operlog/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 删除操作日志
|
||||
export function delOperlog(operId) {
|
||||
return request({
|
||||
url: '/monitor/operlog/' + operId,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 清空操作日志
|
||||
export function cleanOperlog() {
|
||||
return request({
|
||||
url: '/monitor/operlog/clean',
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 导出操作日志
|
||||
export function exportOperlog(query) {
|
||||
return request({
|
||||
url: '/monitor/operlog/export',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
9
src/api/monitor/server.js
Normal file
@ -0,0 +1,9 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 查询服务器详细
|
||||
export function getServer() {
|
||||
return request({
|
||||
url: '/monitor/server',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
89
src/api/system/commonlang.js
Normal file
@ -0,0 +1,89 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
/**
|
||||
* 多语言配置分页查询
|
||||
* @param {查询条件} data
|
||||
*/
|
||||
export function listCommonLang(query) {
|
||||
return request({
|
||||
url: 'system/CommonLang/list',
|
||||
method: 'get',
|
||||
params: query,
|
||||
})
|
||||
}
|
||||
/**
|
||||
* 多语言配置查询
|
||||
* @param {查询条件} data
|
||||
*/
|
||||
export function listLangByLocale(locale) {
|
||||
return request({
|
||||
url: 'system/CommonLang/list/' + locale,
|
||||
method: 'get',
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增多语言配置
|
||||
* @param data
|
||||
*/
|
||||
export function addCommonLang(data) {
|
||||
return request({
|
||||
url: 'system/CommonLang',
|
||||
method: 'post',
|
||||
data: data,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改多语言配置
|
||||
* @param data
|
||||
*/
|
||||
export function updateCommonLang(data) {
|
||||
return request({
|
||||
url: 'system/CommonLang',
|
||||
method: 'PUT',
|
||||
data: data,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取多语言配置详情
|
||||
* @param {Id}
|
||||
*/
|
||||
export function getCommonLang(id) {
|
||||
return request({
|
||||
url: 'system/CommonLang/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
/**
|
||||
* 获取多语言配置详情
|
||||
* @param {key}
|
||||
*/
|
||||
export function getCommonLangByKey(key) {
|
||||
return request({
|
||||
url: 'system/CommonLang/key/' + key,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 删除多语言配置
|
||||
* @param {主键} pid
|
||||
*/
|
||||
export function delCommonLang(pid) {
|
||||
return request({
|
||||
url: 'system/CommonLang/' + pid,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 导出多语言配置
|
||||
export function exportCommonLang(query) {
|
||||
return request({
|
||||
url: 'system/CommonLang/export',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
69
src/api/system/config.js
Normal file
@ -0,0 +1,69 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 查询参数列表
|
||||
export function listConfig(query) {
|
||||
return request({
|
||||
url: '/system/config/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询参数详细
|
||||
export function getConfig(configId) {
|
||||
return request({
|
||||
url: '/system/config/' + configId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 根据参数键名查询参数值
|
||||
export function getConfigKey(configKey) {
|
||||
return request({
|
||||
url: '/system/config/configKey/' + configKey,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增参数配置
|
||||
export function addConfig(data) {
|
||||
return request({
|
||||
url: '/system/config',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改参数配置
|
||||
export function updateConfig(data) {
|
||||
return request({
|
||||
url: '/system/config',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除参数配置
|
||||
export function delConfig(configId) {
|
||||
return request({
|
||||
url: '/system/config/' + configId,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 刷新参数缓存
|
||||
export function refreshCache() {
|
||||
return request({
|
||||
url: '/system/config/refreshCache',
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 导出参数
|
||||
// export function exportConfig(query) {
|
||||
// return request({
|
||||
// url: '/system/config/export',
|
||||
// method: 'get',
|
||||
// params: query
|
||||
// })
|
||||
// }
|
68
src/api/system/dept.js
Normal file
@ -0,0 +1,68 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 查询部门列表
|
||||
export function listDept(query) {
|
||||
return request({
|
||||
url: '/system/dept/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询部门列表(排除节点)
|
||||
export function listDeptExcludeChild(deptId) {
|
||||
return request({
|
||||
url: '/system/dept/list/exclude/' + deptId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 查询部门详细
|
||||
export function getDept(deptId) {
|
||||
return request({
|
||||
url: '/system/dept/' + deptId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 查询部门下拉树结构
|
||||
export function treeselect() {
|
||||
return request({
|
||||
url: '/system/dept/treeselect',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 根据角色ID查询部门树结构
|
||||
export function roleDeptTreeselect(roleId) {
|
||||
return request({
|
||||
url: '/system/dept/roleDeptTreeselect/' + roleId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增部门
|
||||
export function addDept(data) {
|
||||
return request({
|
||||
url: '/system/dept',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改部门
|
||||
export function updateDept(data) {
|
||||
return request({
|
||||
url: '/system/dept',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除部门
|
||||
export function delDept(deptId) {
|
||||
return request({
|
||||
url: '/system/dept/' + deptId,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
69
src/api/system/dict/data.js
Normal file
@ -0,0 +1,69 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 查询字典数据列表
|
||||
export function listData(query) {
|
||||
return request({
|
||||
url: '/system/dict/data/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询字典数据详细
|
||||
export function getData(dictCode) {
|
||||
return request({
|
||||
url: '/system/dict/data/info/' + dictCode,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 根据字典类型查询字典数据信息
|
||||
export function getDicts(dictType) {
|
||||
if (typeof (dictType) === "object") {
|
||||
return request({
|
||||
url: '/system/dict/data/types',
|
||||
data: dictType,
|
||||
method: 'post'
|
||||
})
|
||||
} else {
|
||||
return request({
|
||||
url: '/system/dict/data/type/' + dictType,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 新增字典数据
|
||||
export function addData(data) {
|
||||
return request({
|
||||
url: '/system/dict/data',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改字典数据
|
||||
export function updateData(data) {
|
||||
return request({
|
||||
url: '/system/dict/data',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除字典数据
|
||||
export function delData(dictCode) {
|
||||
return request({
|
||||
url: '/system/dict/data/' + dictCode,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 导出字典数据
|
||||
export function exportData(query) {
|
||||
return request({
|
||||
url: '/system/dict/data/export',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
69
src/api/system/dict/type.js
Normal file
@ -0,0 +1,69 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 查询字典类型列表
|
||||
export function listType(query) {
|
||||
return request({
|
||||
url: '/system/dict/type/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询字典类型详细
|
||||
export function getType(dictId) {
|
||||
return request({
|
||||
url: '/system/dict/type/' + dictId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增字典类型
|
||||
export function addType(data) {
|
||||
return request({
|
||||
url: '/system/dict/type/edit',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改字典类型
|
||||
export function updateType(data) {
|
||||
return request({
|
||||
url: '/system/dict/type/edit',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除字典类型
|
||||
export function delType(dictId) {
|
||||
return request({
|
||||
url: '/system/dict/type/' + dictId,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 清理参数缓存
|
||||
export function clearCache() {
|
||||
return request({
|
||||
url: '/system/dict/type/clearCache',
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 导出字典类型
|
||||
export function exportType(query) {
|
||||
return request({
|
||||
url: '/system/dict/type/export',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 获取字典选择框列表
|
||||
export function optionselect() {
|
||||
return request({
|
||||
url: '/system/dict/type/optionselect',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
69
src/api/system/login.js
Normal file
@ -0,0 +1,69 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 登录方法
|
||||
export function login(username, password, code, uuid) {
|
||||
const data = {
|
||||
username,
|
||||
password,
|
||||
code,
|
||||
uuid
|
||||
}
|
||||
return request({
|
||||
url: '/login',
|
||||
method: 'POST',
|
||||
data: data,
|
||||
headers: {
|
||||
userName: username
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 获取用户详细信息
|
||||
export function getInfo() {
|
||||
return request({
|
||||
url: '/getInfo',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 退出方法
|
||||
export function logout() {
|
||||
return request({
|
||||
url: '/LogOut',
|
||||
method: 'POST'
|
||||
})
|
||||
}
|
||||
|
||||
// 获取验证码
|
||||
export function getCodeImg() {
|
||||
return request({
|
||||
url: '/captchaImage',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
/**
|
||||
* 注册
|
||||
* @returns
|
||||
*/
|
||||
export function register(data) {
|
||||
return request({
|
||||
url: '/register',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 三方授权回调
|
||||
* @param {*} data
|
||||
* @param {*} params
|
||||
* @returns
|
||||
*/
|
||||
export function oauthCallback(data, params) {
|
||||
return request({
|
||||
url: '/auth/callback',
|
||||
method: 'post',
|
||||
data: data,
|
||||
params: params
|
||||
})
|
||||
}
|
84
src/api/system/menu.js
Normal file
@ -0,0 +1,84 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 查询菜单列表
|
||||
export function listMenu(query) {
|
||||
return request({
|
||||
url: '/system/menu/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
// 查询菜单列表
|
||||
export function listMenuById(menuId) {
|
||||
return request({
|
||||
url: '/system/menu/list/' + menuId,
|
||||
method: 'get',
|
||||
})
|
||||
}
|
||||
// 查询菜单详细
|
||||
export function getMenu(menuId) {
|
||||
return request({
|
||||
url: '/system/menu/' + menuId,
|
||||
method: 'get',
|
||||
})
|
||||
}
|
||||
|
||||
// 查询菜单下拉树结构
|
||||
export function treeselect() {
|
||||
return request({
|
||||
url: '/system/Menu/treeSelect',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 根据角色ID查询菜单下拉树结构
|
||||
export function roleMenuTreeselect(roleId) {
|
||||
return request({
|
||||
url: '/system/menu/roleMenuTreeselect/' + roleId,
|
||||
method: 'get',
|
||||
})
|
||||
}
|
||||
|
||||
// 新增菜单
|
||||
export const addMenu = (data) => {
|
||||
return request({
|
||||
url: '/system/menu/add',
|
||||
method: 'put',
|
||||
data: data,
|
||||
})
|
||||
}
|
||||
|
||||
// 修改菜单
|
||||
export function updateMenu(data) {
|
||||
return request({
|
||||
url: '/system/Menu/edit',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除菜单
|
||||
export function delMenu(menuId) {
|
||||
return request({
|
||||
url: '/system/Menu/' + menuId,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
//排序
|
||||
export function changeMenuSort(data) {
|
||||
return request({
|
||||
url: '/system/Menu/ChangeSort',
|
||||
method: 'GET',
|
||||
params: data
|
||||
})
|
||||
}
|
||||
|
||||
// 获取路由
|
||||
export const getRouters = (query) => {
|
||||
return request({
|
||||
url: '/getRouters',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
61
src/api/system/notice.js
Normal file
@ -0,0 +1,61 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 导航栏查询公告列表
|
||||
export function queryNotice(query) {
|
||||
return request({
|
||||
url: '/system/notice/queryNotice',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询公告列表
|
||||
export function listNotice(query) {
|
||||
return request({
|
||||
url: '/system/notice/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询公告详细
|
||||
export function getNotice(noticeId) {
|
||||
return request({
|
||||
url: '/system/notice/' + noticeId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增公告
|
||||
export function addNotice(data) {
|
||||
return request({
|
||||
url: '/system/notice',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改公告
|
||||
export function updateNotice(data) {
|
||||
return request({
|
||||
url: '/system/notice',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除公告
|
||||
export function delNotice(noticeId) {
|
||||
return request({
|
||||
url: '/system/notice/' + noticeId,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 发送通知公告
|
||||
export function sendNotice(noticeId) {
|
||||
return request({
|
||||
url: '/system/notice/send/' + noticeId,
|
||||
method: 'PUT'
|
||||
})
|
||||
}
|
54
src/api/system/post.js
Normal file
@ -0,0 +1,54 @@
|
||||
import request from '@/utils/request'
|
||||
import { downFile } from '@/utils/request'
|
||||
// 查询岗位列表
|
||||
export function listPost(query) {
|
||||
return request({
|
||||
url: '/system/post/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询岗位详细
|
||||
export function getPost(postId) {
|
||||
return request({
|
||||
url: '/system/post/' + postId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增岗位
|
||||
export function addPost(data) {
|
||||
return request({
|
||||
url: '/system/post',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改岗位
|
||||
export function updatePost(data) {
|
||||
return request({
|
||||
url: '/system/post',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除岗位
|
||||
export function delPost(postId) {
|
||||
return request({
|
||||
url: '/system/post/' + postId,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 导出岗位
|
||||
export async function exportPost(query) {
|
||||
// return request({
|
||||
// url: '/system/post/export',
|
||||
// method: 'get',
|
||||
// params: query
|
||||
// })
|
||||
await downFile('/system/post/export', query)
|
||||
}
|
75
src/api/system/role.js
Normal file
@ -0,0 +1,75 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 查询角色列表
|
||||
export function listRole(query) {
|
||||
return request({
|
||||
url: '/system/role/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询角色详细
|
||||
export function getRole(roleId) {
|
||||
return request({
|
||||
url: '/system/role/' + roleId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增角色
|
||||
export const addRole = (data) => {
|
||||
return request({
|
||||
url: '/system/role/edit',
|
||||
method: 'post',
|
||||
data: data,
|
||||
})
|
||||
}
|
||||
|
||||
// 修改角色
|
||||
export function updateRole(data) {
|
||||
return request({
|
||||
url: '/system/role/edit',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 角色数据权限
|
||||
export function dataScope(data) {
|
||||
return request({
|
||||
url: '/system/role/dataScope',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 角色状态修改
|
||||
export function changeRoleStatus(roleId, status) {
|
||||
const data = {
|
||||
roleId,
|
||||
status
|
||||
}
|
||||
return request({
|
||||
url: '/system/role/changeStatus',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除角色
|
||||
export function delRole(roleId) {
|
||||
return request({
|
||||
url: '/system/role/' + roleId,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 导出角色
|
||||
export function exportRole(query) {
|
||||
return request({
|
||||
url: '/system/role/export',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
70
src/api/system/thirdaccount.js
Normal file
@ -0,0 +1,70 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
/**
|
||||
* 三方账号绑定分页查询
|
||||
* @param {查询条件} data
|
||||
*/
|
||||
export function listThirdAccount(query) {
|
||||
return request({
|
||||
url: 'system/ThirdAccount/list',
|
||||
method: 'get',
|
||||
params: query,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 新增三方账号绑定
|
||||
* @param data
|
||||
*/
|
||||
export function addThirdAccount(data) {
|
||||
return request({
|
||||
url: 'system/ThirdAccount',
|
||||
method: 'post',
|
||||
data: data,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改三方账号绑定
|
||||
* @param data
|
||||
*/
|
||||
export function updateThirdAccount(data) {
|
||||
return request({
|
||||
url: 'system/ThirdAccount',
|
||||
method: 'PUT',
|
||||
data: data,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取三方账号绑定详情
|
||||
* @param {Id}
|
||||
*/
|
||||
export function getThirdAccount(id) {
|
||||
return request({
|
||||
url: 'system/ThirdAccount/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除三方账号绑定
|
||||
* @param {主键} pid
|
||||
*/
|
||||
export function delThirdAccount(pid) {
|
||||
return request({
|
||||
url: 'system/ThirdAccount/' + pid,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 导出三方账号绑定
|
||||
export function exportThirdAccount(query) {
|
||||
return request({
|
||||
url: 'system/ThirdAccount/export',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
130
src/api/system/user.js
Normal file
@ -0,0 +1,130 @@
|
||||
import request from '@/utils/request'
|
||||
import { praseStrZero } from '@/utils/ruoyi'
|
||||
import { downFile } from '@/utils/request'
|
||||
|
||||
// 查询用户列表
|
||||
export function listUser(query) {
|
||||
return request({
|
||||
url: '/system/user/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询用户详细
|
||||
export function getUser(userId) {
|
||||
return request({
|
||||
url: '/system/user/' + praseStrZero(userId),
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增用户
|
||||
export function addUser(data) {
|
||||
return request({
|
||||
url: '/system/user/edit',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改用户
|
||||
export function updateUser(data) {
|
||||
return request({
|
||||
url: '/system/user/edit',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除用户
|
||||
export function delUser(userId) {
|
||||
return request({
|
||||
url: '/system/user/' + userId,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 导出用户
|
||||
export async function exportUser(query) {
|
||||
// return request({
|
||||
// url: '/system/User/export',
|
||||
// method: 'get',
|
||||
// params: query
|
||||
// })
|
||||
await downFile('/system/user/export', { ...query })
|
||||
}
|
||||
|
||||
// 用户密码重置
|
||||
export function resetUserPwd(userId, password) {
|
||||
const data = {
|
||||
userId,
|
||||
password
|
||||
}
|
||||
return request({
|
||||
url: '/system/user/resetPwd',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 用户状态修改
|
||||
export function changeUserStatus(userId, status) {
|
||||
const data = {
|
||||
userId,
|
||||
status
|
||||
}
|
||||
return request({
|
||||
url: '/system/user/changeStatus',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 查询用户个人信息
|
||||
export function getUserProfile() {
|
||||
return request({
|
||||
url: '/system/user/Profile',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 修改用户个人信息
|
||||
export function updateUserProfile(data) {
|
||||
return request({
|
||||
url: '/system/user/profile',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 用户密码重置
|
||||
export function updateUserPwd(oldPassword, newPassword) {
|
||||
const data = {
|
||||
oldPassword,
|
||||
newPassword
|
||||
}
|
||||
return request({
|
||||
url: '/system/user/profile/updatePwd',
|
||||
method: 'put',
|
||||
params: data
|
||||
})
|
||||
}
|
||||
|
||||
// 用户头像上传
|
||||
export function uploadAvatar(data) {
|
||||
return request({
|
||||
url: '/system/user/profile/avatar',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 下载用户导入模板
|
||||
export function importTemplate() {
|
||||
return request({
|
||||
url: '/system/user/importTemplate',
|
||||
method: 'get',
|
||||
responseType: 'blob' //1.首先设置responseType对象格式为 blob:
|
||||
})
|
||||
}
|
37
src/api/system/userRoles.js
Normal file
@ -0,0 +1,37 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 查询角色用户
|
||||
export function getRoleUsers(query) {
|
||||
return request({
|
||||
url: '/system/userRole/list',
|
||||
method: 'get',
|
||||
params: query,
|
||||
})
|
||||
}
|
||||
|
||||
// 添加角色用户
|
||||
export function createRoleUsers(data) {
|
||||
return request({
|
||||
url: '/system/userRole/create',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
// 删除角色用户
|
||||
export function deleteRoleUsers(data) {
|
||||
return request({
|
||||
url: '/system/userRole/delete',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
// 查询角色未添加用户列表
|
||||
export function getExcludeUsers(query) {
|
||||
return request({
|
||||
url: '/system/userRole/getExcludeUsers',
|
||||
method: 'get',
|
||||
params: query,
|
||||
})
|
||||
}
|
||||
|
||||
// export default { getRoleUsers, getExcludeUsers }
|
69
src/api/tool/file.js
Normal file
@ -0,0 +1,69 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
/**
|
||||
* 文件存储分页查询
|
||||
* @param {查询条件} data
|
||||
*/
|
||||
export function listSysfile(query) {
|
||||
return request({
|
||||
url: 'tool/file/list',
|
||||
method: 'get',
|
||||
params: query,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增文件存储
|
||||
* @param data
|
||||
*/
|
||||
export function addSysfile(data) {
|
||||
return request({
|
||||
url: 'tool/file',
|
||||
method: 'post',
|
||||
data: data,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改文件存储
|
||||
* @param data
|
||||
*/
|
||||
export function updateSysfile(data) {
|
||||
return request({
|
||||
url: 'tool/file',
|
||||
method: 'PUT',
|
||||
data: data,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件存储详情
|
||||
* @param {Id}
|
||||
*/
|
||||
export function getSysfile(id) {
|
||||
return request({
|
||||
url: 'tool/file/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除文件存储
|
||||
* @param {主键} pid
|
||||
*/
|
||||
export function delSysfile(pid) {
|
||||
return request({
|
||||
url: 'tool/file/' + pid,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 导出文件存储
|
||||
export function exportSysfile(query) {
|
||||
return request({
|
||||
url: 'tool/file/export',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
114
src/api/tool/gen.js
Normal file
@ -0,0 +1,114 @@
|
||||
import request from '@/utils/request';
|
||||
|
||||
/**
|
||||
* 获取数据库
|
||||
*/
|
||||
export function codeGetDBList() {
|
||||
return request({
|
||||
url: 'tool/gen/getDbList',
|
||||
method: 'get',
|
||||
})
|
||||
}
|
||||
/**
|
||||
* 获取数据库表
|
||||
*/
|
||||
export function listDbTable(data) {
|
||||
return request({
|
||||
url: 'tool/gen/getTableList',
|
||||
method: 'get',
|
||||
params: data,
|
||||
})
|
||||
}
|
||||
/**
|
||||
* 生成代码
|
||||
*/
|
||||
export async function codeGenerator(data) {
|
||||
return await request({
|
||||
url: 'tool/gen/genCode',
|
||||
method: 'POST',
|
||||
data: data,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 前端接口
|
||||
*/
|
||||
export async function codeGeneratorApi(data) {
|
||||
return await request({
|
||||
url: 'tool/gen/genCodeApi',
|
||||
method: 'POST',
|
||||
data: data,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取表格列信息
|
||||
* @param {*} data
|
||||
* @returns
|
||||
*/
|
||||
export function queryColumnInfo(tableId) {
|
||||
return request({
|
||||
url: 'tool/gen/Column/' + tableId,
|
||||
method: 'GET',
|
||||
})
|
||||
}
|
||||
|
||||
// 查询生成表数据
|
||||
export function listTable(params) {
|
||||
return request({
|
||||
url: 'tool/gen/list',
|
||||
method: 'get',
|
||||
params: params
|
||||
})
|
||||
}
|
||||
|
||||
// 查询表详细信息
|
||||
export function getGenTable(tableId) {
|
||||
return request({
|
||||
url: '/tool/gen/' + tableId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 导入表
|
||||
export function importTable(data) {
|
||||
return request({
|
||||
url: '/tool/gen/importTable',
|
||||
method: 'post',
|
||||
params: data
|
||||
})
|
||||
}
|
||||
// 删除表数据
|
||||
export function delTable(tableId) {
|
||||
return request({
|
||||
url: '/tool/gen/' + tableId,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 修改代码生成表信息
|
||||
export function updateGenTable(data) {
|
||||
return request({
|
||||
url: '/tool/gen/',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 预览生成代码
|
||||
export function previewTable(tableId, data) {
|
||||
return request({
|
||||
url: '/tool/gen/preview/' + tableId,
|
||||
method: 'post',
|
||||
params: data
|
||||
})
|
||||
}
|
||||
|
||||
// 同步数据库
|
||||
export function synchDb(tableId, data) {
|
||||
return request({
|
||||
url: '/tool/gen/synchDb/' + tableId,
|
||||
method: 'get',
|
||||
params: data
|
||||
})
|
||||
}
|
BIN
src/assets/401_images/401.gif
Normal file
After Width: | Height: | Size: 160 KiB |
BIN
src/assets/404_images/404.png
Normal file
After Width: | Height: | Size: 96 KiB |
BIN
src/assets/404_images/404_cloud.png
Normal file
After Width: | Height: | Size: 4.7 KiB |
391
src/assets/iconfont/iconfont.css
Normal file
@ -0,0 +1,391 @@
|
||||
@font-face {
|
||||
font-family: "iconfont"; /* Project id 4017520 */
|
||||
src: url('iconfont.woff2?t=1681548759797') format('woff2'),
|
||||
url('iconfont.woff?t=1681548759797') format('woff'),
|
||||
url('iconfont.ttf?t=1681548759797') format('truetype');
|
||||
}
|
||||
|
||||
.iconfont {
|
||||
font-family: "iconfont" !important;
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.icon-tree-table:before {
|
||||
content: "\e637";
|
||||
}
|
||||
|
||||
.icon-unlock:before {
|
||||
content: "\e654";
|
||||
}
|
||||
|
||||
.icon-zujian:before {
|
||||
content: "\e655";
|
||||
}
|
||||
|
||||
.icon-theme:before {
|
||||
content: "\e659";
|
||||
}
|
||||
|
||||
.icon-swagger:before {
|
||||
content: "\e65c";
|
||||
}
|
||||
|
||||
.icon-star:before {
|
||||
content: "\e661";
|
||||
}
|
||||
|
||||
.icon-time:before {
|
||||
content: "\e662";
|
||||
}
|
||||
|
||||
.icon-upload:before {
|
||||
content: "\e663";
|
||||
}
|
||||
|
||||
.icon-weixin:before {
|
||||
content: "\e664";
|
||||
}
|
||||
|
||||
.icon-system:before {
|
||||
content: "\e667";
|
||||
}
|
||||
|
||||
.icon-web:before {
|
||||
content: "\e668";
|
||||
}
|
||||
|
||||
.icon-tree:before {
|
||||
content: "\e669";
|
||||
}
|
||||
|
||||
.icon-user:before {
|
||||
content: "\e66a";
|
||||
}
|
||||
|
||||
.icon-tool:before {
|
||||
content: "\e66b";
|
||||
}
|
||||
|
||||
.icon-zip:before {
|
||||
content: "\e66c";
|
||||
}
|
||||
|
||||
.icon-link:before {
|
||||
content: "\e636";
|
||||
}
|
||||
|
||||
.icon-list:before {
|
||||
content: "\e638";
|
||||
}
|
||||
|
||||
.icon-international:before {
|
||||
content: "\e639";
|
||||
}
|
||||
|
||||
.icon-language:before {
|
||||
content: "\e63a";
|
||||
}
|
||||
|
||||
.icon-index:before {
|
||||
content: "\e63b";
|
||||
}
|
||||
|
||||
.icon-ipvisits:before {
|
||||
content: "\e63c";
|
||||
}
|
||||
|
||||
.icon-money:before {
|
||||
content: "\e63d";
|
||||
}
|
||||
|
||||
.icon-message:before {
|
||||
content: "\e63e";
|
||||
}
|
||||
|
||||
.icon-login:before {
|
||||
content: "\e63f";
|
||||
}
|
||||
|
||||
.icon-lock:before {
|
||||
content: "\e640";
|
||||
}
|
||||
|
||||
.icon-menu:before {
|
||||
content: "\e641";
|
||||
}
|
||||
|
||||
.icon-log:before {
|
||||
content: "\e643";
|
||||
}
|
||||
|
||||
.icon-logininfor:before {
|
||||
content: "\e644";
|
||||
}
|
||||
|
||||
.icon-mnt:before {
|
||||
content: "\e645";
|
||||
}
|
||||
|
||||
.icon-password:before {
|
||||
content: "\e646";
|
||||
}
|
||||
|
||||
.icon-peoples:before {
|
||||
content: "\e647";
|
||||
}
|
||||
|
||||
.icon-post:before {
|
||||
content: "\e648";
|
||||
}
|
||||
|
||||
.icon-permission:before {
|
||||
content: "\e649";
|
||||
}
|
||||
|
||||
.icon-phone:before {
|
||||
content: "\e64a";
|
||||
}
|
||||
|
||||
.icon-people:before {
|
||||
content: "\e64b";
|
||||
}
|
||||
|
||||
.icon-online:before {
|
||||
content: "\e64d";
|
||||
}
|
||||
|
||||
.icon-pdf:before {
|
||||
content: "\e64f";
|
||||
}
|
||||
|
||||
.icon-redis:before {
|
||||
content: "\e650";
|
||||
}
|
||||
|
||||
.icon-size:before {
|
||||
content: "\e651";
|
||||
}
|
||||
|
||||
.icon-search:before {
|
||||
content: "\e652";
|
||||
}
|
||||
|
||||
.icon-server:before {
|
||||
content: "\e653";
|
||||
}
|
||||
|
||||
.icon-select:before {
|
||||
content: "\e656";
|
||||
}
|
||||
|
||||
.icon-question:before {
|
||||
content: "\e657";
|
||||
}
|
||||
|
||||
.icon-rate:before {
|
||||
content: "\e658";
|
||||
}
|
||||
|
||||
.icon-monitor:before {
|
||||
content: "\e65a";
|
||||
}
|
||||
|
||||
.icon-source:before {
|
||||
content: "\e65b";
|
||||
}
|
||||
|
||||
.icon-role:before {
|
||||
content: "\e65d";
|
||||
}
|
||||
|
||||
.icon-shopping:before {
|
||||
content: "\e65e";
|
||||
}
|
||||
|
||||
.icon-skill:before {
|
||||
content: "\e65f";
|
||||
}
|
||||
|
||||
.icon-number:before {
|
||||
content: "\e660";
|
||||
}
|
||||
|
||||
.icon-a-404:before {
|
||||
content: "\e622";
|
||||
}
|
||||
|
||||
.icon-email:before {
|
||||
content: "\e623";
|
||||
}
|
||||
|
||||
.icon-example:before {
|
||||
content: "\e624";
|
||||
}
|
||||
|
||||
.icon-error:before {
|
||||
content: "\e625";
|
||||
}
|
||||
|
||||
.icon-excel:before {
|
||||
content: "\e626";
|
||||
}
|
||||
|
||||
.icon-education:before {
|
||||
content: "\e627";
|
||||
}
|
||||
|
||||
.icon-eye-open:before {
|
||||
content: "\e628";
|
||||
}
|
||||
|
||||
.icon-eye:before {
|
||||
content: "\e629";
|
||||
}
|
||||
|
||||
.icon-github:before {
|
||||
content: "\e62b";
|
||||
}
|
||||
|
||||
.icon-guide:before {
|
||||
content: "\e62c";
|
||||
}
|
||||
|
||||
.icon-gonggao:before {
|
||||
content: "\e62d";
|
||||
}
|
||||
|
||||
.icon-icon1:before {
|
||||
content: "\e62e";
|
||||
}
|
||||
|
||||
.icon-fullscreen:before {
|
||||
content: "\e62f";
|
||||
}
|
||||
|
||||
.icon-icon:before {
|
||||
content: "\e630";
|
||||
}
|
||||
|
||||
.icon-image:before {
|
||||
content: "\e631";
|
||||
}
|
||||
|
||||
.icon-form:before {
|
||||
content: "\e632";
|
||||
}
|
||||
|
||||
.icon-job:before {
|
||||
content: "\e635";
|
||||
}
|
||||
|
||||
.icon-cascader:before {
|
||||
content: "\e603";
|
||||
}
|
||||
|
||||
.icon-alipay:before {
|
||||
content: "\e604";
|
||||
}
|
||||
|
||||
.icon-anq:before {
|
||||
content: "\e605";
|
||||
}
|
||||
|
||||
.icon-backup:before {
|
||||
content: "\e606";
|
||||
}
|
||||
|
||||
.icon-bug:before {
|
||||
content: "\e607";
|
||||
}
|
||||
|
||||
.icon-button:before {
|
||||
content: "\e609";
|
||||
}
|
||||
|
||||
.icon-chain:before {
|
||||
content: "\e60b";
|
||||
}
|
||||
|
||||
.icon-chart:before {
|
||||
content: "\e60c";
|
||||
}
|
||||
|
||||
.icon-checkbox:before {
|
||||
content: "\e60d";
|
||||
}
|
||||
|
||||
.icon-clipboard:before {
|
||||
content: "\e60e";
|
||||
}
|
||||
|
||||
.icon-codeConsole:before {
|
||||
content: "\e60f";
|
||||
}
|
||||
|
||||
.icon-code:before {
|
||||
content: "\e610";
|
||||
}
|
||||
|
||||
.icon-color:before {
|
||||
content: "\e611";
|
||||
}
|
||||
|
||||
.icon-database:before {
|
||||
content: "\e612";
|
||||
}
|
||||
|
||||
.icon-component:before {
|
||||
content: "\e613";
|
||||
}
|
||||
|
||||
.icon-dashboard:before {
|
||||
content: "\e614";
|
||||
}
|
||||
|
||||
.icon-date:before {
|
||||
content: "\e615";
|
||||
}
|
||||
|
||||
.icon-deploy:before {
|
||||
content: "\e616";
|
||||
}
|
||||
|
||||
.icon-develop:before {
|
||||
content: "\e617";
|
||||
}
|
||||
|
||||
.icon-dept:before {
|
||||
content: "\e619";
|
||||
}
|
||||
|
||||
.icon-dictionary:before {
|
||||
content: "\e61a";
|
||||
}
|
||||
|
||||
.icon-documentation:before {
|
||||
content: "\e61b";
|
||||
}
|
||||
|
||||
.icon-doc:before {
|
||||
content: "\e61c";
|
||||
}
|
||||
|
||||
.icon-download:before {
|
||||
content: "\e61d";
|
||||
}
|
||||
|
||||
.icon-dict:before {
|
||||
content: "\e61e";
|
||||
}
|
||||
|
||||
.icon-edit:before {
|
||||
content: "\e620";
|
||||
}
|
||||
|
||||
.icon-app:before {
|
||||
content: "\e602";
|
||||
}
|
||||
|
1
src/assets/iconfont/iconfont.js
Normal file
667
src/assets/iconfont/iconfont.json
Normal file
@ -0,0 +1,667 @@
|
||||
{
|
||||
"id": "4017520",
|
||||
"name": "admin",
|
||||
"font_family": "iconfont",
|
||||
"css_prefix_text": "icon-",
|
||||
"description": "",
|
||||
"glyphs": [
|
||||
{
|
||||
"icon_id": "35076965",
|
||||
"name": "tree-table",
|
||||
"font_class": "tree-table",
|
||||
"unicode": "e637",
|
||||
"unicode_decimal": 58935
|
||||
},
|
||||
{
|
||||
"icon_id": "35076967",
|
||||
"name": "unlock",
|
||||
"font_class": "unlock",
|
||||
"unicode": "e654",
|
||||
"unicode_decimal": 58964
|
||||
},
|
||||
{
|
||||
"icon_id": "35076968",
|
||||
"name": "zujian",
|
||||
"font_class": "zujian",
|
||||
"unicode": "e655",
|
||||
"unicode_decimal": 58965
|
||||
},
|
||||
{
|
||||
"icon_id": "35076969",
|
||||
"name": "theme",
|
||||
"font_class": "theme",
|
||||
"unicode": "e659",
|
||||
"unicode_decimal": 58969
|
||||
},
|
||||
{
|
||||
"icon_id": "35076970",
|
||||
"name": "swagger",
|
||||
"font_class": "swagger",
|
||||
"unicode": "e65c",
|
||||
"unicode_decimal": 58972
|
||||
},
|
||||
{
|
||||
"icon_id": "35076971",
|
||||
"name": "star",
|
||||
"font_class": "star",
|
||||
"unicode": "e661",
|
||||
"unicode_decimal": 58977
|
||||
},
|
||||
{
|
||||
"icon_id": "35076972",
|
||||
"name": "time",
|
||||
"font_class": "time",
|
||||
"unicode": "e662",
|
||||
"unicode_decimal": 58978
|
||||
},
|
||||
{
|
||||
"icon_id": "35076973",
|
||||
"name": "upload",
|
||||
"font_class": "upload",
|
||||
"unicode": "e663",
|
||||
"unicode_decimal": 58979
|
||||
},
|
||||
{
|
||||
"icon_id": "35076974",
|
||||
"name": "weixin",
|
||||
"font_class": "weixin",
|
||||
"unicode": "e664",
|
||||
"unicode_decimal": 58980
|
||||
},
|
||||
{
|
||||
"icon_id": "35076977",
|
||||
"name": "system",
|
||||
"font_class": "system",
|
||||
"unicode": "e667",
|
||||
"unicode_decimal": 58983
|
||||
},
|
||||
{
|
||||
"icon_id": "35076979",
|
||||
"name": "web",
|
||||
"font_class": "web",
|
||||
"unicode": "e668",
|
||||
"unicode_decimal": 58984
|
||||
},
|
||||
{
|
||||
"icon_id": "35076980",
|
||||
"name": "tree",
|
||||
"font_class": "tree",
|
||||
"unicode": "e669",
|
||||
"unicode_decimal": 58985
|
||||
},
|
||||
{
|
||||
"icon_id": "35076981",
|
||||
"name": "user",
|
||||
"font_class": "user",
|
||||
"unicode": "e66a",
|
||||
"unicode_decimal": 58986
|
||||
},
|
||||
{
|
||||
"icon_id": "35076982",
|
||||
"name": "tool",
|
||||
"font_class": "tool",
|
||||
"unicode": "e66b",
|
||||
"unicode_decimal": 58987
|
||||
},
|
||||
{
|
||||
"icon_id": "35076986",
|
||||
"name": "zip",
|
||||
"font_class": "zip",
|
||||
"unicode": "e66c",
|
||||
"unicode_decimal": 58988
|
||||
},
|
||||
{
|
||||
"icon_id": "35076492",
|
||||
"name": "link",
|
||||
"font_class": "link",
|
||||
"unicode": "e636",
|
||||
"unicode_decimal": 58934
|
||||
},
|
||||
{
|
||||
"icon_id": "35076494",
|
||||
"name": "list",
|
||||
"font_class": "list",
|
||||
"unicode": "e638",
|
||||
"unicode_decimal": 58936
|
||||
},
|
||||
{
|
||||
"icon_id": "35076495",
|
||||
"name": "international",
|
||||
"font_class": "international",
|
||||
"unicode": "e639",
|
||||
"unicode_decimal": 58937
|
||||
},
|
||||
{
|
||||
"icon_id": "35076496",
|
||||
"name": "language",
|
||||
"font_class": "language",
|
||||
"unicode": "e63a",
|
||||
"unicode_decimal": 58938
|
||||
},
|
||||
{
|
||||
"icon_id": "35076497",
|
||||
"name": "index",
|
||||
"font_class": "index",
|
||||
"unicode": "e63b",
|
||||
"unicode_decimal": 58939
|
||||
},
|
||||
{
|
||||
"icon_id": "35076498",
|
||||
"name": "ipvisits",
|
||||
"font_class": "ipvisits",
|
||||
"unicode": "e63c",
|
||||
"unicode_decimal": 58940
|
||||
},
|
||||
{
|
||||
"icon_id": "35076499",
|
||||
"name": "money",
|
||||
"font_class": "money",
|
||||
"unicode": "e63d",
|
||||
"unicode_decimal": 58941
|
||||
},
|
||||
{
|
||||
"icon_id": "35076500",
|
||||
"name": "message",
|
||||
"font_class": "message",
|
||||
"unicode": "e63e",
|
||||
"unicode_decimal": 58942
|
||||
},
|
||||
{
|
||||
"icon_id": "35076501",
|
||||
"name": "login",
|
||||
"font_class": "login",
|
||||
"unicode": "e63f",
|
||||
"unicode_decimal": 58943
|
||||
},
|
||||
{
|
||||
"icon_id": "35076502",
|
||||
"name": "lock",
|
||||
"font_class": "lock",
|
||||
"unicode": "e640",
|
||||
"unicode_decimal": 58944
|
||||
},
|
||||
{
|
||||
"icon_id": "35076503",
|
||||
"name": "menu",
|
||||
"font_class": "menu",
|
||||
"unicode": "e641",
|
||||
"unicode_decimal": 58945
|
||||
},
|
||||
{
|
||||
"icon_id": "35076505",
|
||||
"name": "log",
|
||||
"font_class": "log",
|
||||
"unicode": "e643",
|
||||
"unicode_decimal": 58947
|
||||
},
|
||||
{
|
||||
"icon_id": "35076506",
|
||||
"name": "logininfor",
|
||||
"font_class": "logininfor",
|
||||
"unicode": "e644",
|
||||
"unicode_decimal": 58948
|
||||
},
|
||||
{
|
||||
"icon_id": "35076507",
|
||||
"name": "mnt",
|
||||
"font_class": "mnt",
|
||||
"unicode": "e645",
|
||||
"unicode_decimal": 58949
|
||||
},
|
||||
{
|
||||
"icon_id": "35076509",
|
||||
"name": "password",
|
||||
"font_class": "password",
|
||||
"unicode": "e646",
|
||||
"unicode_decimal": 58950
|
||||
},
|
||||
{
|
||||
"icon_id": "35076510",
|
||||
"name": "peoples",
|
||||
"font_class": "peoples",
|
||||
"unicode": "e647",
|
||||
"unicode_decimal": 58951
|
||||
},
|
||||
{
|
||||
"icon_id": "35076511",
|
||||
"name": "post",
|
||||
"font_class": "post",
|
||||
"unicode": "e648",
|
||||
"unicode_decimal": 58952
|
||||
},
|
||||
{
|
||||
"icon_id": "35076512",
|
||||
"name": "permission",
|
||||
"font_class": "permission",
|
||||
"unicode": "e649",
|
||||
"unicode_decimal": 58953
|
||||
},
|
||||
{
|
||||
"icon_id": "35076513",
|
||||
"name": "phone",
|
||||
"font_class": "phone",
|
||||
"unicode": "e64a",
|
||||
"unicode_decimal": 58954
|
||||
},
|
||||
{
|
||||
"icon_id": "35076514",
|
||||
"name": "people",
|
||||
"font_class": "people",
|
||||
"unicode": "e64b",
|
||||
"unicode_decimal": 58955
|
||||
},
|
||||
{
|
||||
"icon_id": "35076516",
|
||||
"name": "online",
|
||||
"font_class": "online",
|
||||
"unicode": "e64d",
|
||||
"unicode_decimal": 58957
|
||||
},
|
||||
{
|
||||
"icon_id": "35076518",
|
||||
"name": "pdf",
|
||||
"font_class": "pdf",
|
||||
"unicode": "e64f",
|
||||
"unicode_decimal": 58959
|
||||
},
|
||||
{
|
||||
"icon_id": "35076519",
|
||||
"name": "redis",
|
||||
"font_class": "redis",
|
||||
"unicode": "e650",
|
||||
"unicode_decimal": 58960
|
||||
},
|
||||
{
|
||||
"icon_id": "35076520",
|
||||
"name": "size",
|
||||
"font_class": "size",
|
||||
"unicode": "e651",
|
||||
"unicode_decimal": 58961
|
||||
},
|
||||
{
|
||||
"icon_id": "35076521",
|
||||
"name": "search",
|
||||
"font_class": "search",
|
||||
"unicode": "e652",
|
||||
"unicode_decimal": 58962
|
||||
},
|
||||
{
|
||||
"icon_id": "35076522",
|
||||
"name": "server",
|
||||
"font_class": "server",
|
||||
"unicode": "e653",
|
||||
"unicode_decimal": 58963
|
||||
},
|
||||
{
|
||||
"icon_id": "35076525",
|
||||
"name": "select",
|
||||
"font_class": "select",
|
||||
"unicode": "e656",
|
||||
"unicode_decimal": 58966
|
||||
},
|
||||
{
|
||||
"icon_id": "35076526",
|
||||
"name": "question",
|
||||
"font_class": "question",
|
||||
"unicode": "e657",
|
||||
"unicode_decimal": 58967
|
||||
},
|
||||
{
|
||||
"icon_id": "35076527",
|
||||
"name": "rate",
|
||||
"font_class": "rate",
|
||||
"unicode": "e658",
|
||||
"unicode_decimal": 58968
|
||||
},
|
||||
{
|
||||
"icon_id": "35076529",
|
||||
"name": "monitor",
|
||||
"font_class": "monitor",
|
||||
"unicode": "e65a",
|
||||
"unicode_decimal": 58970
|
||||
},
|
||||
{
|
||||
"icon_id": "35076530",
|
||||
"name": "source",
|
||||
"font_class": "source",
|
||||
"unicode": "e65b",
|
||||
"unicode_decimal": 58971
|
||||
},
|
||||
{
|
||||
"icon_id": "35076532",
|
||||
"name": "role",
|
||||
"font_class": "role",
|
||||
"unicode": "e65d",
|
||||
"unicode_decimal": 58973
|
||||
},
|
||||
{
|
||||
"icon_id": "35076533",
|
||||
"name": "shopping",
|
||||
"font_class": "shopping",
|
||||
"unicode": "e65e",
|
||||
"unicode_decimal": 58974
|
||||
},
|
||||
{
|
||||
"icon_id": "35076534",
|
||||
"name": "skill",
|
||||
"font_class": "skill",
|
||||
"unicode": "e65f",
|
||||
"unicode_decimal": 58975
|
||||
},
|
||||
{
|
||||
"icon_id": "35076535",
|
||||
"name": "number",
|
||||
"font_class": "number",
|
||||
"unicode": "e660",
|
||||
"unicode_decimal": 58976
|
||||
},
|
||||
{
|
||||
"icon_id": "35076366",
|
||||
"name": "404",
|
||||
"font_class": "a-404",
|
||||
"unicode": "e622",
|
||||
"unicode_decimal": 58914
|
||||
},
|
||||
{
|
||||
"icon_id": "35076470",
|
||||
"name": "email",
|
||||
"font_class": "email",
|
||||
"unicode": "e623",
|
||||
"unicode_decimal": 58915
|
||||
},
|
||||
{
|
||||
"icon_id": "35076471",
|
||||
"name": "example",
|
||||
"font_class": "example",
|
||||
"unicode": "e624",
|
||||
"unicode_decimal": 58916
|
||||
},
|
||||
{
|
||||
"icon_id": "35076472",
|
||||
"name": "error",
|
||||
"font_class": "error",
|
||||
"unicode": "e625",
|
||||
"unicode_decimal": 58917
|
||||
},
|
||||
{
|
||||
"icon_id": "35076473",
|
||||
"name": "excel",
|
||||
"font_class": "excel",
|
||||
"unicode": "e626",
|
||||
"unicode_decimal": 58918
|
||||
},
|
||||
{
|
||||
"icon_id": "35076474",
|
||||
"name": "education",
|
||||
"font_class": "education",
|
||||
"unicode": "e627",
|
||||
"unicode_decimal": 58919
|
||||
},
|
||||
{
|
||||
"icon_id": "35076475",
|
||||
"name": "eye-open",
|
||||
"font_class": "eye-open",
|
||||
"unicode": "e628",
|
||||
"unicode_decimal": 58920
|
||||
},
|
||||
{
|
||||
"icon_id": "35076476",
|
||||
"name": "eye",
|
||||
"font_class": "eye",
|
||||
"unicode": "e629",
|
||||
"unicode_decimal": 58921
|
||||
},
|
||||
{
|
||||
"icon_id": "35076480",
|
||||
"name": "github",
|
||||
"font_class": "github",
|
||||
"unicode": "e62b",
|
||||
"unicode_decimal": 58923
|
||||
},
|
||||
{
|
||||
"icon_id": "35076481",
|
||||
"name": "guide",
|
||||
"font_class": "guide",
|
||||
"unicode": "e62c",
|
||||
"unicode_decimal": 58924
|
||||
},
|
||||
{
|
||||
"icon_id": "35076482",
|
||||
"name": "gonggao",
|
||||
"font_class": "gonggao",
|
||||
"unicode": "e62d",
|
||||
"unicode_decimal": 58925
|
||||
},
|
||||
{
|
||||
"icon_id": "35076483",
|
||||
"name": "icon1",
|
||||
"font_class": "icon1",
|
||||
"unicode": "e62e",
|
||||
"unicode_decimal": 58926
|
||||
},
|
||||
{
|
||||
"icon_id": "35076484",
|
||||
"name": "fullscreen",
|
||||
"font_class": "fullscreen",
|
||||
"unicode": "e62f",
|
||||
"unicode_decimal": 58927
|
||||
},
|
||||
{
|
||||
"icon_id": "35076485",
|
||||
"name": "icon",
|
||||
"font_class": "icon",
|
||||
"unicode": "e630",
|
||||
"unicode_decimal": 58928
|
||||
},
|
||||
{
|
||||
"icon_id": "35076486",
|
||||
"name": "image",
|
||||
"font_class": "image",
|
||||
"unicode": "e631",
|
||||
"unicode_decimal": 58929
|
||||
},
|
||||
{
|
||||
"icon_id": "35076487",
|
||||
"name": "form",
|
||||
"font_class": "form",
|
||||
"unicode": "e632",
|
||||
"unicode_decimal": 58930
|
||||
},
|
||||
{
|
||||
"icon_id": "35076491",
|
||||
"name": "job",
|
||||
"font_class": "job",
|
||||
"unicode": "e635",
|
||||
"unicode_decimal": 58933
|
||||
},
|
||||
{
|
||||
"icon_id": "35076284",
|
||||
"name": "cascader",
|
||||
"font_class": "cascader",
|
||||
"unicode": "e603",
|
||||
"unicode_decimal": 58883
|
||||
},
|
||||
{
|
||||
"icon_id": "35076315",
|
||||
"name": "alipay",
|
||||
"font_class": "alipay",
|
||||
"unicode": "e604",
|
||||
"unicode_decimal": 58884
|
||||
},
|
||||
{
|
||||
"icon_id": "35076316",
|
||||
"name": "anq",
|
||||
"font_class": "anq",
|
||||
"unicode": "e605",
|
||||
"unicode_decimal": 58885
|
||||
},
|
||||
{
|
||||
"icon_id": "35076317",
|
||||
"name": "backup",
|
||||
"font_class": "backup",
|
||||
"unicode": "e606",
|
||||
"unicode_decimal": 58886
|
||||
},
|
||||
{
|
||||
"icon_id": "35076318",
|
||||
"name": "bug",
|
||||
"font_class": "bug",
|
||||
"unicode": "e607",
|
||||
"unicode_decimal": 58887
|
||||
},
|
||||
{
|
||||
"icon_id": "35076321",
|
||||
"name": "button",
|
||||
"font_class": "button",
|
||||
"unicode": "e609",
|
||||
"unicode_decimal": 58889
|
||||
},
|
||||
{
|
||||
"icon_id": "35076323",
|
||||
"name": "chain",
|
||||
"font_class": "chain",
|
||||
"unicode": "e60b",
|
||||
"unicode_decimal": 58891
|
||||
},
|
||||
{
|
||||
"icon_id": "35076324",
|
||||
"name": "chart",
|
||||
"font_class": "chart",
|
||||
"unicode": "e60c",
|
||||
"unicode_decimal": 58892
|
||||
},
|
||||
{
|
||||
"icon_id": "35076325",
|
||||
"name": "checkbox",
|
||||
"font_class": "checkbox",
|
||||
"unicode": "e60d",
|
||||
"unicode_decimal": 58893
|
||||
},
|
||||
{
|
||||
"icon_id": "35076326",
|
||||
"name": "clipboard",
|
||||
"font_class": "clipboard",
|
||||
"unicode": "e60e",
|
||||
"unicode_decimal": 58894
|
||||
},
|
||||
{
|
||||
"icon_id": "35076327",
|
||||
"name": "codeConsole",
|
||||
"font_class": "codeConsole",
|
||||
"unicode": "e60f",
|
||||
"unicode_decimal": 58895
|
||||
},
|
||||
{
|
||||
"icon_id": "35076328",
|
||||
"name": "code",
|
||||
"font_class": "code",
|
||||
"unicode": "e610",
|
||||
"unicode_decimal": 58896
|
||||
},
|
||||
{
|
||||
"icon_id": "35076329",
|
||||
"name": "color",
|
||||
"font_class": "color",
|
||||
"unicode": "e611",
|
||||
"unicode_decimal": 58897
|
||||
},
|
||||
{
|
||||
"icon_id": "35076330",
|
||||
"name": "database",
|
||||
"font_class": "database",
|
||||
"unicode": "e612",
|
||||
"unicode_decimal": 58898
|
||||
},
|
||||
{
|
||||
"icon_id": "35076331",
|
||||
"name": "component",
|
||||
"font_class": "component",
|
||||
"unicode": "e613",
|
||||
"unicode_decimal": 58899
|
||||
},
|
||||
{
|
||||
"icon_id": "35076332",
|
||||
"name": "dashboard",
|
||||
"font_class": "dashboard",
|
||||
"unicode": "e614",
|
||||
"unicode_decimal": 58900
|
||||
},
|
||||
{
|
||||
"icon_id": "35076333",
|
||||
"name": "date",
|
||||
"font_class": "date",
|
||||
"unicode": "e615",
|
||||
"unicode_decimal": 58901
|
||||
},
|
||||
{
|
||||
"icon_id": "35076334",
|
||||
"name": "deploy",
|
||||
"font_class": "deploy",
|
||||
"unicode": "e616",
|
||||
"unicode_decimal": 58902
|
||||
},
|
||||
{
|
||||
"icon_id": "35076335",
|
||||
"name": "develop",
|
||||
"font_class": "develop",
|
||||
"unicode": "e617",
|
||||
"unicode_decimal": 58903
|
||||
},
|
||||
{
|
||||
"icon_id": "35076337",
|
||||
"name": "dept",
|
||||
"font_class": "dept",
|
||||
"unicode": "e619",
|
||||
"unicode_decimal": 58905
|
||||
},
|
||||
{
|
||||
"icon_id": "35076340",
|
||||
"name": "dictionary",
|
||||
"font_class": "dictionary",
|
||||
"unicode": "e61a",
|
||||
"unicode_decimal": 58906
|
||||
},
|
||||
{
|
||||
"icon_id": "35076341",
|
||||
"name": "documentation",
|
||||
"font_class": "documentation",
|
||||
"unicode": "e61b",
|
||||
"unicode_decimal": 58907
|
||||
},
|
||||
{
|
||||
"icon_id": "35076342",
|
||||
"name": "doc",
|
||||
"font_class": "doc",
|
||||
"unicode": "e61c",
|
||||
"unicode_decimal": 58908
|
||||
},
|
||||
{
|
||||
"icon_id": "35076343",
|
||||
"name": "download",
|
||||
"font_class": "download",
|
||||
"unicode": "e61d",
|
||||
"unicode_decimal": 58909
|
||||
},
|
||||
{
|
||||
"icon_id": "35076344",
|
||||
"name": "dict",
|
||||
"font_class": "dict",
|
||||
"unicode": "e61e",
|
||||
"unicode_decimal": 58910
|
||||
},
|
||||
{
|
||||
"icon_id": "35076351",
|
||||
"name": "edit",
|
||||
"font_class": "edit",
|
||||
"unicode": "e620",
|
||||
"unicode_decimal": 58912
|
||||
},
|
||||
{
|
||||
"icon_id": "35075963",
|
||||
"name": "app",
|
||||
"font_class": "app",
|
||||
"unicode": "e602",
|
||||
"unicode_decimal": 58882
|
||||
}
|
||||
]
|
||||
}
|
BIN
src/assets/iconfont/iconfont.ttf
Normal file
BIN
src/assets/iconfont/iconfont.woff
Normal file
BIN
src/assets/iconfont/iconfont.woff2
Normal file
BIN
src/assets/icons/gitee-fill-round.png
Normal file
After Width: | Height: | Size: 6.4 KiB |
BIN
src/assets/icons/github-fill.png
Normal file
After Width: | Height: | Size: 7.5 KiB |
1
src/assets/icons/svg/build.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="1568899741379" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2054" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M960 591.424V368.96c0-0.288 0.16-0.512 0.16-0.768S960 367.68 960 367.424V192a32 32 0 0 0-32-32H96a32 32 0 0 0-32 32v175.424c0 0.288-0.16 0.512-0.16 0.768s0.16 0.48 0.16 0.768v222.464c0 0.288-0.16 0.512-0.16 0.768s0.16 0.48 0.16 0.768V864a32 32 0 0 0 32 32h832a32 32 0 0 0 32-32v-271.04c0-0.288 0.16-0.512 0.16-0.768S960 591.68 960 591.424z m-560-31.232v-160H608v160h-208z m208 64V832h-208v-207.808H608z m-480-224h208v160H128v-160z m544 0h224v160h-224v-160zM896 224v112.192H128V224h768zM128 624.192h208V832H128v-207.808zM672 832v-207.808h224V832h-224z" p-id="2055"></path></svg>
|
After Width: | Height: | Size: 954 B |
1
src/assets/icons/svg/exit-fullscreen.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M49.217 41.329l-.136-35.24c-.06-2.715-2.302-4.345-5.022-4.405h-3.65c-2.712-.06-4.866 2.303-4.806 5.016l.152 19.164-24.151-23.79a6.698 6.698 0 0 0-9.499 0 6.76 6.76 0 0 0 0 9.526l23.93 23.713-18.345.074c-2.712-.069-5.228 1.813-5.64 5.02v3.462c.069 2.721 2.31 4.97 5.022 5.03l35.028-.207c.052.005.087.025.133.025l2.457.054a4.626 4.626 0 0 0 3.436-1.38c.88-.874 1.205-2.096 1.169-3.462l-.262-2.465c0-.048.182-.081.182-.136h.002zm52.523 51.212l18.32-.073c2.713.06 5.224-1.609 5.64-4.815v-3.462c-.068-2.722-2.317-4.97-5.021-5.04l-34.58.21c-.053 0-.086-.021-.138-.021l-2.451-.06a4.64 4.64 0 0 0-3.445 1.381c-.885.868-1.201 2.094-1.174 3.46l.27 2.46c.005.06-.177.095-.177.141l.141 34.697c.069 2.713 2.31 4.338 5.022 4.397l3.45.006c2.705.062 4.867-2.31 4.8-5.026l-.153-18.752 24.151 23.946a6.69 6.69 0 0 0 9.494 0 6.747 6.747 0 0 0 0-9.523L101.74 92.54v.001zM48.125 80.662a4.636 4.636 0 0 0-3.437-1.382l-2.457.06c-.05 0-.082.022-.137.022l-35.025-.21c-2.712.07-4.957 2.318-5.022 5.04v3.462c.409 3.206 2.925 4.874 5.633 4.814l18.554.06-24.132 23.928c-2.62 2.626-2.62 6.89 0 9.524a6.694 6.694 0 0 0 9.496 0l24.155-23.79-.155 18.866c-.06 2.722 2.094 5.093 4.801 5.025h3.65c2.72-.069 4.962-1.685 5.022-4.406l.141-34.956c0-.05-.182-.082-.182-.136l.262-2.46c.03-1.366-.286-2.592-1.166-3.46h-.001zM80.08 47.397a4.62 4.62 0 0 0 3.443 1.374l2.45-.054c.055 0 .088-.02.143-.028l35.08.21c2.712-.062 4.953-2.312 5.021-5.033l.009-3.463c-.417-3.211-2.937-5.084-5.64-5.025l-18.615-.073 23.917-23.715c2.63-2.623 2.63-6.879.008-9.513a6.691 6.691 0 0 0-9.494 0L92.251 26.016l.155-19.312c.065-2.713-2.097-5.085-4.802-5.025h-3.45c-2.713.069-4.954 1.693-5.022 4.406l-.139 35.247c0 .054.18.088.18.136l-.267 2.465c-.028 1.366.288 2.588 1.174 3.463v.001z"/></svg>
|
After Width: | Height: | Size: 1.8 KiB |
1
src/assets/icons/svg/gitee.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="1682510872430" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1825" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><path d="M512 1024C229.222 1024 0 794.778 0 512S229.222 0 512 0s512 229.222 512 512-229.222 512-512 512z m259.149-568.883h-290.74a25.293 25.293 0 0 0-25.292 25.293l-0.026 63.206c0 13.952 11.315 25.293 25.267 25.293h177.024c13.978 0 25.293 11.315 25.293 25.267v12.646a75.853 75.853 0 0 1-75.853 75.853h-240.23a25.293 25.293 0 0 1-25.267-25.293V417.203a75.853 75.853 0 0 1 75.827-75.853h353.946a25.293 25.293 0 0 0 25.267-25.292l0.077-63.207a25.293 25.293 0 0 0-25.268-25.293H417.152a189.62 189.62 0 0 0-189.62 189.645V771.15c0 13.977 11.316 25.293 25.294 25.293h372.94a170.65 170.65 0 0 0 170.65-170.65V480.384a25.293 25.293 0 0 0-25.293-25.267z" fill="#C71D23" p-id="1826"></path></svg>
|
After Width: | Height: | Size: 1010 B |
1
src/assets/icons/svg/validCode.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="1569580729849" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1939" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M513.3 958.5c-142.2 0-397.9-222.1-401.6-440.5V268c1.7-39.6 31.7-72.3 71.1-77.3 49-4.6 97.1-16.5 142.7-35.3 47.8-14 91.9-38.3 129.4-71.1 30.3-24.4 72.9-26.3 105.3-4.6 39.9 30.7 83.8 55.9 130.5 74.6 48.6 14.7 98.2 25.9 148.4 33.7 38.5 7.6 67.1 40.3 69.5 79.5 3.3 84.9 2.5 169.9-2.6 254.7-33.7 281.6-253.7 436.4-392.7 436.3z m-0.1-813.7c-7.2-0.2-14.3 2-20 6.4-39.7 35.2-86.8 61.1-137.7 75.7-46.8 19.2-96.2 31-146.6 35.2-11 3.2-18.8 13-19.5 24.4v230.1c3.5 180.3 223.3 361 323.9 361s287.3-120.2 317.6-360.5c7.3-142.7 0-228.6 0-229.6-1.3-13.3-11-24.3-24-27.3-49.6-7.7-98.6-19-146.5-33.7-46.3-19.5-89.7-45.3-129-76.7-5.8-3.8-12.7-5.5-19.5-4.9l1.3-0.1z" fill="#C6CCDA" p-id="1940"></path><path d="M750.1 428L490.7 673.2c-11.7 11.1-29.5 12.9-43.1 4.2l-6.8-5.8-141.2-149.4c-9.3-9.3-12.7-22.9-9-35.5 3.8-12.6 14.1-22.1 27-24.8 12.9-2.7 26.1 1.9 34.6 11.9L469 597.5l233.7-221c14.6-12.8 36.8-11.6 49.9 2.7 13.2 14.2 11.5 35.3-2.5 48.8" fill="#C6CCDA" p-id="1941"></path></svg>
|
After Width: | Height: | Size: 1.3 KiB |
BIN
src/assets/icons/wechat-fill.png
Normal file
After Width: | Height: | Size: 6.4 KiB |
39
src/assets/images/dark.svg
Normal file
@ -0,0 +1,39 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="52px" height="45px" viewBox="0 0 52 45" version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<defs>
|
||||
<filter x="-9.4%" y="-6.2%" width="118.8%" height="122.5%" filterUnits="objectBoundingBox" id="filter-1">
|
||||
<feOffset dx="0" dy="1" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
|
||||
<feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
|
||||
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.15 0" type="matrix" in="shadowBlurOuter1" result="shadowMatrixOuter1"></feColorMatrix>
|
||||
<feMerge>
|
||||
<feMergeNode in="shadowMatrixOuter1"></feMergeNode>
|
||||
<feMergeNode in="SourceGraphic"></feMergeNode>
|
||||
</feMerge>
|
||||
</filter>
|
||||
<rect id="path-2" x="0" y="0" width="48" height="40" rx="4"></rect>
|
||||
<filter x="-4.2%" y="-2.5%" width="108.3%" height="110.0%" filterUnits="objectBoundingBox" id="filter-4">
|
||||
<feOffset dx="0" dy="1" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
|
||||
<feGaussianBlur stdDeviation="0.5" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
|
||||
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.1 0" type="matrix" in="shadowBlurOuter1"></feColorMatrix>
|
||||
</filter>
|
||||
</defs>
|
||||
<g id="配置面板" width="48" height="40" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="setting-copy-2" width="48" height="40" transform="translate(-1190.000000, -136.000000)">
|
||||
<g id="Group-8" width="48" height="40" transform="translate(1167.000000, 0.000000)">
|
||||
<g id="Group-5-Copy-5" filter="url(#filter-1)" transform="translate(25.000000, 137.000000)">
|
||||
<mask id="mask-3" fill="white">
|
||||
<use xlink:href="#path-2"></use>
|
||||
</mask>
|
||||
<g id="Rectangle-18">
|
||||
<use fill="black" fill-opacity="1" filter="url(#filter-4)" xlink:href="#path-2"></use>
|
||||
<use fill="#F0F2F5" fill-rule="evenodd" xlink:href="#path-2"></use>
|
||||
</g>
|
||||
<rect id="Rectangle-11" fill="#FFFFFF" mask="url(#mask-3)" x="0" y="0" width="48" height="10"></rect>
|
||||
<rect id="Rectangle-18" fill="#303648" mask="url(#mask-3)" x="0" y="0" width="16" height="40"></rect>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.5 KiB |
39
src/assets/images/light.svg
Normal file
@ -0,0 +1,39 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="52px" height="45px" viewBox="0 0 52 45" version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<defs>
|
||||
<filter x="-9.4%" y="-6.2%" width="118.8%" height="122.5%" filterUnits="objectBoundingBox" id="filter-1">
|
||||
<feOffset dx="0" dy="1" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
|
||||
<feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
|
||||
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.15 0" type="matrix" in="shadowBlurOuter1" result="shadowMatrixOuter1"></feColorMatrix>
|
||||
<feMerge>
|
||||
<feMergeNode in="shadowMatrixOuter1"></feMergeNode>
|
||||
<feMergeNode in="SourceGraphic"></feMergeNode>
|
||||
</feMerge>
|
||||
</filter>
|
||||
<rect id="path-2" x="0" y="0" width="48" height="40" rx="4"></rect>
|
||||
<filter x="-4.2%" y="-2.5%" width="108.3%" height="110.0%" filterUnits="objectBoundingBox" id="filter-4">
|
||||
<feOffset dx="0" dy="1" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
|
||||
<feGaussianBlur stdDeviation="0.5" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
|
||||
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.1 0" type="matrix" in="shadowBlurOuter1"></feColorMatrix>
|
||||
</filter>
|
||||
</defs>
|
||||
<g id="配置面板" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="setting-copy-2" transform="translate(-1254.000000, -136.000000)">
|
||||
<g id="Group-8" transform="translate(1167.000000, 0.000000)">
|
||||
<g id="Group-5" filter="url(#filter-1)" transform="translate(89.000000, 137.000000)">
|
||||
<mask id="mask-3" fill="white">
|
||||
<use xlink:href="#path-2"></use>
|
||||
</mask>
|
||||
<g id="Rectangle-18">
|
||||
<use fill="black" fill-opacity="1" filter="url(#filter-4)" xlink:href="#path-2"></use>
|
||||
<use fill="#F0F2F5" fill-rule="evenodd" xlink:href="#path-2"></use>
|
||||
</g>
|
||||
<rect id="Rectangle-18" fill="#FFFFFF" mask="url(#mask-3)" x="0" y="0" width="16" height="40"></rect>
|
||||
<rect id="Rectangle-11" fill="#FFFFFF" mask="url(#mask-3)" x="0" y="0" width="48" height="10"></rect>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.5 KiB |
BIN
src/assets/images/profile.jpg
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
src/assets/logo/icon.png
Normal file
After Width: | Height: | Size: 3.4 KiB |
BIN
src/assets/logo/logo.png
Normal file
After Width: | Height: | Size: 7.5 KiB |
BIN
src/assets/logo/logo_500.png
Normal file
After Width: | Height: | Size: 22 KiB |
99
src/assets/styles/btn.scss
Normal file
@ -0,0 +1,99 @@
|
||||
@import './variables.module.scss';
|
||||
|
||||
@mixin colorBtn($color) {
|
||||
background: $color;
|
||||
|
||||
&:hover {
|
||||
color: $color;
|
||||
|
||||
&:before,
|
||||
&:after {
|
||||
background: $color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.blue-btn {
|
||||
@include colorBtn($blue)
|
||||
}
|
||||
|
||||
.light-blue-btn {
|
||||
@include colorBtn($light-blue)
|
||||
}
|
||||
|
||||
.red-btn {
|
||||
@include colorBtn($red)
|
||||
}
|
||||
|
||||
.pink-btn {
|
||||
@include colorBtn($pink)
|
||||
}
|
||||
|
||||
.green-btn {
|
||||
@include colorBtn($green)
|
||||
}
|
||||
|
||||
.tiffany-btn {
|
||||
@include colorBtn($tiffany)
|
||||
}
|
||||
|
||||
.yellow-btn {
|
||||
@include colorBtn($yellow)
|
||||
}
|
||||
|
||||
.pan-btn {
|
||||
font-size: 14px;
|
||||
color: #fff;
|
||||
padding: 14px 36px;
|
||||
border-radius: 8px;
|
||||
border: none;
|
||||
outline: none;
|
||||
transition: 600ms ease all;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
|
||||
&:hover {
|
||||
background: #fff;
|
||||
|
||||
&:before,
|
||||
&:after {
|
||||
width: 100%;
|
||||
transition: 600ms ease all;
|
||||
}
|
||||
}
|
||||
|
||||
&:before,
|
||||
&:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
height: 2px;
|
||||
width: 0;
|
||||
transition: 400ms ease all;
|
||||
}
|
||||
|
||||
&::after {
|
||||
right: inherit;
|
||||
top: inherit;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.custom-button {
|
||||
display: inline-block;
|
||||
line-height: 1;
|
||||
white-space: nowrap;
|
||||
cursor: pointer;
|
||||
background: #fff;
|
||||
color: #fff;
|
||||
-webkit-appearance: none;
|
||||
text-align: center;
|
||||
box-sizing: border-box;
|
||||
outline: 0;
|
||||
margin: 0;
|
||||
padding: 10px 15px;
|
||||
font-size: 14px;
|
||||
border-radius: 4px;
|
||||
}
|
130
src/assets/styles/element-ui.scss
Normal file
@ -0,0 +1,130 @@
|
||||
// cover some element-ui styles
|
||||
|
||||
.el-upload__input {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.cell {
|
||||
.el-tag {
|
||||
margin-right: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
// table 里面操作按钮样式覆盖
|
||||
.el-table__cell .cell {
|
||||
.el-button.is-text {
|
||||
padding: 8px 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.status-col {
|
||||
.cell {
|
||||
padding: 0 10px;
|
||||
text-align: center;
|
||||
|
||||
.el-tag {
|
||||
margin-right: 0px;
|
||||
}
|
||||
}
|
||||
}
|
||||
// fix 2.2.0 版本label标签不居中
|
||||
// .el-form-item--small .el-form-item__label {
|
||||
// line-height: 32px !important;
|
||||
// }
|
||||
|
||||
// element ui 移动端组件适配
|
||||
.el-icon {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.el-header {
|
||||
--el-header-padding: 0 0px !important;
|
||||
// --el-header-height: 50px !important;
|
||||
}
|
||||
// el 2.2.0 text button
|
||||
.el-button.is-text {
|
||||
color: var(--el-color-primary) !important;
|
||||
}
|
||||
@media screen and (max-width: 500px) {
|
||||
.el-message {
|
||||
min-width: 300px !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 500px) {
|
||||
.el-message-box {
|
||||
width: 300px !important;
|
||||
}
|
||||
|
||||
.el-pagination__jump {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.el-pagination__sizes {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
// dialog
|
||||
@media screen and (max-width: 700px) {
|
||||
.el-dialog {
|
||||
--el-dialog-width: 100% !important;
|
||||
// --el-dialog-margin-top: 0 !important;
|
||||
}
|
||||
// .el-dialog:not(.is-fullscreen) {
|
||||
// margin-top: 0 !important;
|
||||
// }
|
||||
.el-drawer {
|
||||
width: 85% !important;
|
||||
}
|
||||
}
|
||||
|
||||
/** 表格更多操作下拉样式 */
|
||||
.el-table .el-dropdown-link {
|
||||
cursor: pointer;
|
||||
color: #409eff;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.el-table .el-dropdown,
|
||||
.el-icon-arrow-down {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
//适配完毕
|
||||
|
||||
// 隐藏picture-card 上传按钮
|
||||
.hide .el-upload--picture-card {
|
||||
display: none;
|
||||
}
|
||||
|
||||
// 禁止菜单选中
|
||||
.el-sub-menu .el-sub-menu__title span,
|
||||
.el-menu .el-menu-item {
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
// fix 侧边栏二级导航缩小图标消失问题
|
||||
.el-sub-menu__title {
|
||||
padding-right: unset !important;
|
||||
}
|
||||
|
||||
// fix 顶部导航更多样式问题
|
||||
.el-menu--horizontal .el-sub-menu .el-sub-menu__icon-arrow {
|
||||
right: calc(0px - var(--el-menu-base-level-padding)) !important;
|
||||
}
|
||||
|
||||
// 弹出搜索框
|
||||
.header-search-select {
|
||||
.el-select-dropdown__item {
|
||||
height: unset !important;
|
||||
line-height: unset !important;
|
||||
margin-bottom: 5px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
276
src/assets/styles/index.scss
Normal file
@ -0,0 +1,276 @@
|
||||
@import './variables.module.scss';
|
||||
@import './mixin.scss';
|
||||
@import './transition.scss';
|
||||
@import './element-ui.scss';
|
||||
@import './sidebar.scss';
|
||||
@import './btn.scss';
|
||||
@import './waves.scss';
|
||||
|
||||
html,
|
||||
body,
|
||||
#app {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, SimSun, sans-serif;
|
||||
font-weight: 400;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
background-color: var(--base-bg-main);
|
||||
// overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
*,
|
||||
*:before,
|
||||
*:after {
|
||||
box-sizing: inherit;
|
||||
}
|
||||
|
||||
.no-padding {
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
.padding-content {
|
||||
padding: 4px 0;
|
||||
}
|
||||
|
||||
a:focus,
|
||||
a:active {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
a,
|
||||
a:focus,
|
||||
a:hover {
|
||||
cursor: pointer;
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
div:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.fr {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.fl {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.pr-5 {
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
.pl-5 {
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
.block {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.pointer {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.inlineBlock {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.clearfix {
|
||||
&:after {
|
||||
visibility: hidden;
|
||||
display: block;
|
||||
font-size: 0;
|
||||
content: ' ';
|
||||
clear: both;
|
||||
height: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.text-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.link-type,
|
||||
.link-type:focus {
|
||||
color: var(--el-color-primary);
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
// color: rgb(32, 160, 255);
|
||||
opacity: 0.3;
|
||||
}
|
||||
}
|
||||
|
||||
/** 基础通用 **/
|
||||
.pt5 {
|
||||
padding-top: 5px;
|
||||
}
|
||||
|
||||
.pr5 {
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
.pb5 {
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
.pb20 {
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
|
||||
.mt5 {
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.mr5 {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.mb5 {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.mb8 {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.ml5 {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.mt10 {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.mr10 {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.mb10 {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.ml10 {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.mt20 {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.mr20 {
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
.mb20 {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.ml20 {
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
.ml {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.mr {
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.mt {
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
.mb {
|
||||
margin-bottom: auto;
|
||||
}
|
||||
.w20 {
|
||||
width: 20%;
|
||||
}
|
||||
.w100 {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.pull-right {
|
||||
float: right !important;
|
||||
}
|
||||
|
||||
/* text color */
|
||||
.text-navy {
|
||||
color: #1ab394;
|
||||
}
|
||||
.text-pink {
|
||||
color: pink;
|
||||
}
|
||||
.text-primary {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.text-success {
|
||||
color: #1c84c6;
|
||||
}
|
||||
|
||||
.text-info {
|
||||
color: #23c6c8;
|
||||
}
|
||||
|
||||
.text-warning {
|
||||
color: #f8ac59;
|
||||
}
|
||||
|
||||
.text-danger {
|
||||
color: #ff0000;
|
||||
}
|
||||
|
||||
.text-muted {
|
||||
color: #888888;
|
||||
}
|
||||
|
||||
.text-orange {
|
||||
color: #ff7d00;
|
||||
}
|
||||
|
||||
.text-hotpink {
|
||||
color: hotpink;
|
||||
}
|
||||
|
||||
.text-green {
|
||||
color: green;
|
||||
}
|
||||
|
||||
.text-greenyellow {
|
||||
color: greenyellow;
|
||||
}
|
||||
.text-purple {
|
||||
color: #ff00ff;
|
||||
}
|
||||
|
||||
/* image */
|
||||
.img-circle {
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.card-box {
|
||||
padding-right: 15px;
|
||||
padding-left: 15px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
.table-td-thumb {
|
||||
width: 56px;
|
||||
}
|
||||
|
||||
.flex-center {
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
73
src/assets/styles/login.scss
Normal file
@ -0,0 +1,73 @@
|
||||
.login {
|
||||
background: radial-gradient(220% 105% at top center, #1b2947 10%, #4b76a7 40%, #81acae 65%, #f7f7b6);
|
||||
background-attachment: fixed;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
// background-image: url('@/assets/images/login-bg.jpg');
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
.title {
|
||||
margin: 0px auto 30px auto;
|
||||
text-align: center;
|
||||
// color: #fff;
|
||||
}
|
||||
|
||||
.login-form {
|
||||
border-radius: 6px;
|
||||
background: #ffffff;
|
||||
// background-color: hsla(0, 0%, 100%, 0.3);
|
||||
width: var(--base-login-width);
|
||||
padding: 25px 15px 5px 15px;
|
||||
position: relative;
|
||||
|
||||
.input-icon {
|
||||
height: 39px;
|
||||
width: 14px;
|
||||
margin-left: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
.login-tip {
|
||||
font-size: 13px;
|
||||
text-align: center;
|
||||
color: #bfbfbf;
|
||||
}
|
||||
|
||||
.login-code {
|
||||
width: 33%;
|
||||
height: 40px;
|
||||
float: right;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
cursor: pointer;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
.el-login-footer {
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
color: #fff;
|
||||
font-family: Arial;
|
||||
font-size: 12px;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
.login-code-img {
|
||||
height: 40px;
|
||||
padding-left: 12px;
|
||||
}
|
||||
.langSet {
|
||||
position: absolute;
|
||||
right: 20px;
|
||||
top: 10px;
|
||||
}
|
66
src/assets/styles/mixin.scss
Normal file
@ -0,0 +1,66 @@
|
||||
@mixin clearfix {
|
||||
&:after {
|
||||
content: "";
|
||||
display: table;
|
||||
clear: both;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin scrollBar {
|
||||
&::-webkit-scrollbar-track-piece {
|
||||
background: #d3dce6;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: #99a9bf;
|
||||
border-radius: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin relative {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
@mixin pct($pct) {
|
||||
width: #{$pct};
|
||||
position: relative;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
@mixin triangle($width, $height, $color, $direction) {
|
||||
$width: $width/2;
|
||||
$color-border-style: $height solid $color;
|
||||
$transparent-border-style: $width solid transparent;
|
||||
height: 0;
|
||||
width: 0;
|
||||
|
||||
@if $direction==up {
|
||||
border-bottom: $color-border-style;
|
||||
border-left: $transparent-border-style;
|
||||
border-right: $transparent-border-style;
|
||||
}
|
||||
|
||||
@else if $direction==right {
|
||||
border-left: $color-border-style;
|
||||
border-top: $transparent-border-style;
|
||||
border-bottom: $transparent-border-style;
|
||||
}
|
||||
|
||||
@else if $direction==down {
|
||||
border-top: $color-border-style;
|
||||
border-left: $transparent-border-style;
|
||||
border-right: $transparent-border-style;
|
||||
}
|
||||
|
||||
@else if $direction==left {
|
||||
border-right: $color-border-style;
|
||||
border-top: $transparent-border-style;
|
||||
border-bottom: $transparent-border-style;
|
||||
}
|
||||
}
|
106
src/assets/styles/sidebar.scss
Normal file
@ -0,0 +1,106 @@
|
||||
#app {
|
||||
.sidebar {
|
||||
position: relative;
|
||||
overflow-y: hidden;
|
||||
z-index: 1001;
|
||||
transition: width 0.28s ease;
|
||||
background-color: var(--base-menu-background);
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
-webkit-box-shadow: 2px 0 14px rgb(0 21 41 / 10%);
|
||||
box-shadow: 2px 0 14px rgb(0 21 41 / 10%);
|
||||
|
||||
.el-scrollbar__bar.is-vertical {
|
||||
right: 0px;
|
||||
}
|
||||
// 去掉el-menu边框
|
||||
.el-menu {
|
||||
border: none;
|
||||
}
|
||||
|
||||
[class^='el-icon'] {
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
font-size: unset;
|
||||
}
|
||||
}
|
||||
|
||||
// 展开sidebar状态设置svg-icon边距
|
||||
.openSidebar {
|
||||
.sidebar {
|
||||
transform: translate(0);
|
||||
}
|
||||
.sidebar .svg-icon {
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
// 隐藏侧边栏样式
|
||||
.hideSidebar {
|
||||
.el-aside {
|
||||
--el-aside-width: 60px;
|
||||
}
|
||||
// 隐藏箭头
|
||||
.el-sub-menu {
|
||||
overflow: hidden;
|
||||
|
||||
& > .el-sub-menu__title {
|
||||
.el-sub-menu__icon-arrow {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 折叠状态下
|
||||
.el-menu--collapse {
|
||||
[class^='el-icon'] {
|
||||
width: auto;
|
||||
font-size: medium;
|
||||
margin-right: 0;
|
||||
}
|
||||
.el-sub-menu {
|
||||
& > .el-sub-menu__title {
|
||||
& > span {
|
||||
height: 3000px;
|
||||
width: 0;
|
||||
overflow: hidden;
|
||||
visibility: hidden;
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// mobile responsive
|
||||
.mobile {
|
||||
.main-container {
|
||||
margin-left: 0px;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
transition: transform 0.28s;
|
||||
position: fixed;
|
||||
// background: var(--base-menu-background, #fff);
|
||||
}
|
||||
|
||||
&.hideSidebar {
|
||||
.sidebar {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// when menu collapsed
|
||||
.el-menu--vertical {
|
||||
// the scroll bar appears when the subMenu is too long
|
||||
> .el-menu--popup {
|
||||
max-height: 100vh;
|
||||
overflow-y: auto;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
}
|
||||
}
|
48
src/assets/styles/transition.scss
Normal file
@ -0,0 +1,48 @@
|
||||
// global transition css
|
||||
|
||||
/* fade */
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
transition: opacity 0.28s;
|
||||
}
|
||||
|
||||
.fade-enter,
|
||||
.fade-leave-active {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
/* fade-transform */
|
||||
.fade-transform-leave-active,
|
||||
.fade-transform-enter-active {
|
||||
transition: all .5s;
|
||||
}
|
||||
|
||||
.fade-transform-enter {
|
||||
opacity: 0;
|
||||
transform: translateX(-30px);
|
||||
}
|
||||
|
||||
.fade-transform-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateX(30px);
|
||||
}
|
||||
|
||||
/* breadcrumb transition */
|
||||
.breadcrumb-enter-active,
|
||||
.breadcrumb-leave-active {
|
||||
transition: all .5s;
|
||||
}
|
||||
|
||||
.breadcrumb-enter,
|
||||
.breadcrumb-leave-active {
|
||||
opacity: 0;
|
||||
transform: translateX(20px);
|
||||
}
|
||||
|
||||
.breadcrumb-move {
|
||||
transition: all .5s;
|
||||
}
|
||||
|
||||
.breadcrumb-leave-active {
|
||||
position: absolute;
|
||||
}
|
45
src/assets/styles/variables.module.scss
Normal file
@ -0,0 +1,45 @@
|
||||
// base color
|
||||
$blue: #324157;
|
||||
$light-blue: #3a71a8;
|
||||
$red: #c03639;
|
||||
$pink: #e65d6e;
|
||||
$green: #30b08f;
|
||||
$tiffany: #4ab7bd;
|
||||
$yellow: #fec171;
|
||||
$panGreen: #30b08f;
|
||||
|
||||
// 默认菜单主题风格
|
||||
:root {
|
||||
--base-text-color-rgba: rgba(0, 0, 0, 0.85);
|
||||
--base-sidebar-width: 220px;
|
||||
// 左侧菜单宽度
|
||||
--el-aside-width: 220px;
|
||||
//底部高度
|
||||
--base-footer-height: 30px;
|
||||
--base-tags-height: 34px;
|
||||
--base-header-height: 50px;
|
||||
//登录框宽度
|
||||
--base-login-width: 280px;
|
||||
}
|
||||
|
||||
/***侧边栏深色配置***/
|
||||
[data-theme='theme-dark'] {
|
||||
--base-menu-background: #324157;
|
||||
--base-logo-title-color: #ffffff;
|
||||
// // el-ement ui 设置
|
||||
// --el-fill-color-blank: #304156;
|
||||
--el-text-color-primary: #e5eaf3;
|
||||
--el-menu-text-color: var(--el-text-color-primary);
|
||||
}
|
||||
html.dark {
|
||||
/* custom dark bg color */
|
||||
// --el-bg-color: #141414;
|
||||
--base-color-white: #ffffff;
|
||||
--base-text-color-rgba: #ffffff;
|
||||
}
|
||||
html.cafe {
|
||||
filter: sepia(0.9) hue-rotate(315deg) brightness(0.9);
|
||||
}
|
||||
html.contrast {
|
||||
filter: contrast(2);
|
||||
}
|
101
src/assets/styles/waves.scss
Normal file
@ -0,0 +1,101 @@
|
||||
/* Waves v0.6.0
|
||||
* http://fian.my.id/Waves
|
||||
*
|
||||
* Copyright 2014 Alfiana E. Sibuea and other contributors
|
||||
* Released under the MIT license
|
||||
* https://github.com/fians/Waves/blob/master/LICENSE
|
||||
*/
|
||||
.waves-effect {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
overflow: hidden;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
vertical-align: middle;
|
||||
z-index: 1;
|
||||
will-change: opacity, transform;
|
||||
transition: all 0.3s ease-out;
|
||||
}
|
||||
.waves-effect .waves-ripple {
|
||||
position: absolute;
|
||||
border-radius: 50%;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin-top: -10px;
|
||||
margin-left: -10px;
|
||||
opacity: 0;
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
transition: all 0.7s ease-out;
|
||||
transition-property: opacity, -webkit-transform;
|
||||
transition-property: transform, opacity;
|
||||
transition-property: transform, opacity, -webkit-transform;
|
||||
-webkit-transform: scale(0);
|
||||
transform: scale(0);
|
||||
pointer-events: none;
|
||||
}
|
||||
.waves-effect.waves-light .waves-ripple {
|
||||
background-color: rgba(255, 255, 255, 0.45);
|
||||
}
|
||||
.waves-effect.waves-red .waves-ripple {
|
||||
background-color: rgba(244, 67, 54, 0.7);
|
||||
}
|
||||
.waves-effect.waves-yellow .waves-ripple {
|
||||
background-color: rgba(255, 235, 59, 0.7);
|
||||
}
|
||||
.waves-effect.waves-orange .waves-ripple {
|
||||
background-color: rgba(255, 152, 0, 0.7);
|
||||
}
|
||||
.waves-effect.waves-purple .waves-ripple {
|
||||
background-color: rgba(156, 39, 176, 0.7);
|
||||
}
|
||||
.waves-effect.waves-green .waves-ripple {
|
||||
background-color: rgba(76, 175, 80, 0.7);
|
||||
}
|
||||
.waves-effect.waves-teal .waves-ripple {
|
||||
background-color: rgba(0, 150, 136, 0.7);
|
||||
}
|
||||
.waves-effect input[type='button'],
|
||||
.waves-effect input[type='reset'],
|
||||
.waves-effect input[type='submit'] {
|
||||
border: 0;
|
||||
font-style: normal;
|
||||
font-size: inherit;
|
||||
text-transform: inherit;
|
||||
background: none;
|
||||
}
|
||||
.waves-notransition {
|
||||
transition: none !important;
|
||||
}
|
||||
.waves-circle {
|
||||
-webkit-transform: translateZ(0);
|
||||
transform: translateZ(0);
|
||||
-webkit-mask-image: -webkit-radial-gradient(circle, #fff 100%, #000 100%);
|
||||
}
|
||||
.waves-input-wrapper {
|
||||
border-radius: 0.2em;
|
||||
vertical-align: bottom;
|
||||
}
|
||||
.waves-input-wrapper .waves-button-input {
|
||||
position: relative;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 1;
|
||||
}
|
||||
.waves-circle {
|
||||
text-align: center;
|
||||
width: 2.5em;
|
||||
height: 2.5em;
|
||||
line-height: 2.5em;
|
||||
border-radius: 50%;
|
||||
-webkit-mask-image: none;
|
||||
}
|
||||
.waves-block {
|
||||
display: block;
|
||||
}
|
||||
a.waves-effect .waves-ripple {
|
||||
z-index: -1;
|
||||
}
|
64
src/auto-import.d.ts
vendored
Normal file
@ -0,0 +1,64 @@
|
||||
// Generated by 'unplugin-auto-import'
|
||||
// We suggest you to commit this file into source control
|
||||
declare global {
|
||||
const acceptHMRUpdate: typeof import('pinia')['acceptHMRUpdate']
|
||||
const computed: typeof import('vue')['computed']
|
||||
const createApp: typeof import('vue')['createApp']
|
||||
const createPinia: typeof import('pinia')['createPinia']
|
||||
const customRef: typeof import('vue')['customRef']
|
||||
const defineAsyncComponent: typeof import('vue')['defineAsyncComponent']
|
||||
const defineComponent: typeof import('vue')['defineComponent']
|
||||
const defineStore: typeof import('pinia')['defineStore']
|
||||
const effectScope: typeof import('vue')['effectScope']
|
||||
const EffectScope: typeof import('vue')['EffectScope']
|
||||
const getActivePinia: typeof import('pinia')['getActivePinia']
|
||||
const getCurrentInstance: typeof import('vue')['getCurrentInstance']
|
||||
const getCurrentScope: typeof import('vue')['getCurrentScope']
|
||||
const h: typeof import('vue')['h']
|
||||
const inject: typeof import('vue')['inject']
|
||||
const isReadonly: typeof import('vue')['isReadonly']
|
||||
const isRef: typeof import('vue')['isRef']
|
||||
const mapActions: typeof import('pinia')['mapActions']
|
||||
const mapGetters: typeof import('pinia')['mapGetters']
|
||||
const mapState: typeof import('pinia')['mapState']
|
||||
const mapStores: typeof import('pinia')['mapStores']
|
||||
const mapWritableState: typeof import('pinia')['mapWritableState']
|
||||
const markRaw: typeof import('vue')['markRaw']
|
||||
const nextTick: typeof import('vue')['nextTick']
|
||||
const onActivated: typeof import('vue')['onActivated']
|
||||
const onBeforeMount: typeof import('vue')['onBeforeMount']
|
||||
const onBeforeUnmount: typeof import('vue')['onBeforeUnmount']
|
||||
const onBeforeUpdate: typeof import('vue')['onBeforeUpdate']
|
||||
const onDeactivated: typeof import('vue')['onDeactivated']
|
||||
const onErrorCaptured: typeof import('vue')['onErrorCaptured']
|
||||
const onMounted: typeof import('vue')['onMounted']
|
||||
const onRenderTracked: typeof import('vue')['onRenderTracked']
|
||||
const onRenderTriggered: typeof import('vue')['onRenderTriggered']
|
||||
const onScopeDispose: typeof import('vue')['onScopeDispose']
|
||||
const onServerPrefetch: typeof import('vue')['onServerPrefetch']
|
||||
const onUnmounted: typeof import('vue')['onUnmounted']
|
||||
const onUpdated: typeof import('vue')['onUpdated']
|
||||
const provide: typeof import('vue')['provide']
|
||||
const reactive: typeof import('vue')['reactive']
|
||||
const readonly: typeof import('vue')['readonly']
|
||||
const ref: typeof import('vue')['ref']
|
||||
const setActivePinia: typeof import('pinia')['setActivePinia']
|
||||
const setMapStoreSuffix: typeof import('pinia')['setMapStoreSuffix']
|
||||
const shallowReactive: typeof import('vue')['shallowReactive']
|
||||
const shallowReadonly: typeof import('vue')['shallowReadonly']
|
||||
const shallowRef: typeof import('vue')['shallowRef']
|
||||
const storeToRefs: typeof import('pinia')['storeToRefs']
|
||||
const toRaw: typeof import('vue')['toRaw']
|
||||
const toRef: typeof import('vue')['toRef']
|
||||
const toRefs: typeof import('vue')['toRefs']
|
||||
const triggerRef: typeof import('vue')['triggerRef']
|
||||
const unref: typeof import('vue')['unref']
|
||||
const useAttrs: typeof import('vue')['useAttrs']
|
||||
const useCssModule: typeof import('vue')['useCssModule']
|
||||
const useRoute: typeof import('vue-router')['useRoute']
|
||||
const useRouter: typeof import('vue-router')['useRouter']
|
||||
const useSlots: typeof import('vue')['useSlots']
|
||||
const watch: typeof import('vue')['watch']
|
||||
const watchEffect: typeof import('vue')['watchEffect']
|
||||
}
|
||||
export {}
|
72
src/components/Breadcrumb/index.vue
Normal file
@ -0,0 +1,72 @@
|
||||
<template>
|
||||
<el-breadcrumb class="app-breadcrumb" separator="/">
|
||||
<!-- <transition-group name="breadcrumb"> -->
|
||||
<el-breadcrumb-item v-for="(item, index) in levelList" :key="item.path">
|
||||
<span v-if="item.redirect === 'noRedirect' || index == levelList.length - 1" class="no-redirect">{{ item.meta.title }}</span>
|
||||
<span v-else @click.prevent="handleLink(item)" style="cursor: pointer">
|
||||
{{ item.meta.title }}
|
||||
</span>
|
||||
</el-breadcrumb-item>
|
||||
<!-- </transition-group> -->
|
||||
</el-breadcrumb>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const levelList = ref([])
|
||||
|
||||
function getBreadcrumb() {
|
||||
// only show routes with meta.title
|
||||
let matched = route.matched.filter((item) => item.meta && item.meta.title)
|
||||
const first = matched[0]
|
||||
// 判断是否为首页
|
||||
if (!isDashboard(first)) {
|
||||
matched = [{ path: '/index', meta: { title: '首页' } }].concat(matched)
|
||||
}
|
||||
|
||||
levelList.value = matched.filter((item) => item.meta && item.meta.title && item.meta.breadcrumb !== false)
|
||||
}
|
||||
|
||||
function isDashboard(route) {
|
||||
const name = route && route.name
|
||||
if (!name) {
|
||||
return false
|
||||
}
|
||||
return name.trim() === 'Index'
|
||||
}
|
||||
function handleLink(item) {
|
||||
const { redirect, path } = item
|
||||
if (redirect) {
|
||||
router.push(redirect)
|
||||
return
|
||||
}
|
||||
router.push(path)
|
||||
}
|
||||
|
||||
watchEffect(() => {
|
||||
// if you go to the redirect page, do not update the breadcrumbs
|
||||
if (route.path.startsWith('/redirect/')) {
|
||||
return
|
||||
}
|
||||
getBreadcrumb()
|
||||
})
|
||||
getBreadcrumb()
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.app-breadcrumb.el-breadcrumb {
|
||||
display: inline-block;
|
||||
font-size: 14px;
|
||||
line-height: 50px;
|
||||
margin-left: 8px;
|
||||
|
||||
.no-redirect {
|
||||
color: #97a8be;
|
||||
cursor: text;
|
||||
}
|
||||
}
|
||||
.mobile .app-breadcrumb.el-breadcrumb {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
172
src/components/Crontab/day.vue
Normal file
@ -0,0 +1,172 @@
|
||||
<template>
|
||||
<el-form size="small">
|
||||
<el-form-item>
|
||||
<el-radio v-model="radioValue" :label="1"> 日,允许的通配符[, - * ? / L W] </el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model="radioValue" :label="2"> 不指定 </el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model="radioValue" :label="3">
|
||||
周期从
|
||||
<el-input-number v-model="cycle01" :min="1" :max="30" /> - <el-input-number v-model="cycle02" :min="cycle01 + 1" :max="31" /> 日
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model="radioValue" :label="4">
|
||||
从
|
||||
<el-input-number v-model="average01" :min="1" :max="30" /> 号开始,每
|
||||
<el-input-number v-model="average02" :min="1" :max="31 - average01" /> 日执行一次
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model="radioValue" :label="5">
|
||||
每月
|
||||
<el-input-number v-model="workday" :min="1" :max="31" /> 号最近的那个工作日
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model="radioValue" :label="6"> 本月最后一天 </el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model="radioValue" :label="7">
|
||||
指定
|
||||
<el-select clearable v-model="checkboxList" placeholder="可多选" multiple :multiple-limit="10">
|
||||
<el-option v-for="item in 31" :key="item" :label="item" :value="item" />
|
||||
</el-select>
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
<script setup>
|
||||
const emit = defineEmits(['update'])
|
||||
const props = defineProps({
|
||||
cron: {
|
||||
type: Object,
|
||||
default: {
|
||||
second: '*',
|
||||
min: '*',
|
||||
hour: '*',
|
||||
day: '*',
|
||||
month: '*',
|
||||
week: '?',
|
||||
year: ''
|
||||
}
|
||||
},
|
||||
check: {
|
||||
type: Function,
|
||||
default: () => {}
|
||||
}
|
||||
})
|
||||
const radioValue = ref(1)
|
||||
const cycle01 = ref(1)
|
||||
const cycle02 = ref(2)
|
||||
const average01 = ref(1)
|
||||
const average02 = ref(1)
|
||||
const workday = ref(1)
|
||||
const checkboxList = ref([])
|
||||
const checkCopy = ref([1])
|
||||
const cycleTotal = computed(() => {
|
||||
cycle01.value = props.check(cycle01.value, 1, 30)
|
||||
cycle02.value = props.check(cycle02.value, cycle01.value + 1, 31)
|
||||
return cycle01.value + '-' + cycle02.value
|
||||
})
|
||||
const averageTotal = computed(() => {
|
||||
average01.value = props.check(average01.value, 1, 30)
|
||||
average02.value = props.check(average02.value, 1, 31 - average01.value)
|
||||
return average01.value + '/' + average02.value
|
||||
})
|
||||
const workdayTotal = computed(() => {
|
||||
workday.value = props.check(workday.value, 1, 31)
|
||||
return workday.value + 'W'
|
||||
})
|
||||
const checkboxString = computed(() => {
|
||||
return checkboxList.value.join(',')
|
||||
})
|
||||
watch(
|
||||
() => props.cron.day,
|
||||
(value) => changeRadioValue(value)
|
||||
)
|
||||
watch([radioValue, cycleTotal, averageTotal, workdayTotal, checkboxString], () => onRadioChange())
|
||||
function changeRadioValue(value) {
|
||||
if (value === '*') {
|
||||
radioValue.value = 1
|
||||
} else if (value === '?') {
|
||||
radioValue.value = 2
|
||||
} else if (value.indexOf('-') > -1) {
|
||||
const indexArr = value.split('-')
|
||||
cycle01.value = Number(indexArr[0])
|
||||
cycle02.value = Number(indexArr[1])
|
||||
radioValue.value = 3
|
||||
} else if (value.indexOf('/') > -1) {
|
||||
const indexArr = value.split('/')
|
||||
average01.value = Number(indexArr[0])
|
||||
average02.value = Number(indexArr[1])
|
||||
radioValue.value = 4
|
||||
} else if (value.indexOf('W') > -1) {
|
||||
const indexArr = value.split('W')
|
||||
workday.value = Number(indexArr[0])
|
||||
radioValue.value = 5
|
||||
} else if (value === 'L') {
|
||||
radioValue.value = 6
|
||||
} else {
|
||||
checkboxList.value = [...new Set(value.split(',').map((item) => Number(item)))]
|
||||
radioValue.value = 7
|
||||
}
|
||||
}
|
||||
// 单选按钮值变化时
|
||||
function onRadioChange() {
|
||||
if (radioValue.value === 2 && props.cron.week === '?') {
|
||||
emit('update', 'week', '*', 'day')
|
||||
}
|
||||
if (radioValue.value !== 2 && props.cron.week !== '?') {
|
||||
emit('update', 'week', '?', 'day')
|
||||
}
|
||||
switch (radioValue.value) {
|
||||
case 1:
|
||||
emit('update', 'day', '*', 'day')
|
||||
break
|
||||
case 2:
|
||||
emit('update', 'day', '?', 'day')
|
||||
break
|
||||
case 3:
|
||||
emit('update', 'day', cycleTotal.value, 'day')
|
||||
break
|
||||
case 4:
|
||||
emit('update', 'day', averageTotal.value, 'day')
|
||||
break
|
||||
case 5:
|
||||
emit('update', 'day', workdayTotal.value, 'day')
|
||||
break
|
||||
case 6:
|
||||
emit('update', 'day', 'L', 'day')
|
||||
break
|
||||
case 7:
|
||||
if (checkboxList.value.length === 0) {
|
||||
checkboxList.value.push(checkCopy.value[0])
|
||||
} else {
|
||||
checkCopy.value = checkboxList.value
|
||||
}
|
||||
emit('update', 'day', checkboxString.value, 'day')
|
||||
break
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.el-input-number--small,
|
||||
.el-select,
|
||||
.el-select--small {
|
||||
margin: 0 0.2rem;
|
||||
}
|
||||
.el-select,
|
||||
.el-select--small {
|
||||
width: 18.8rem;
|
||||
}
|
||||
</style>
|
129
src/components/Crontab/hour.vue
Normal file
@ -0,0 +1,129 @@
|
||||
<template>
|
||||
<el-form size="small">
|
||||
<el-form-item>
|
||||
<el-radio v-model="radioValue" :label="1"> 小时,允许的通配符[, - * /] </el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model="radioValue" :label="2">
|
||||
周期从
|
||||
<el-input-number v-model="cycle01" :min="0" :max="22" /> - <el-input-number v-model="cycle02" :min="cycle01 + 1" :max="23" /> 时
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model="radioValue" :label="3">
|
||||
从
|
||||
<el-input-number v-model="average01" :min="0" :max="22" /> 时开始,每
|
||||
<el-input-number v-model="average02" :min="1" :max="23 - average01" /> 小时执行一次
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model="radioValue" :label="4">
|
||||
指定
|
||||
<el-select clearable v-model="checkboxList" placeholder="可多选" multiple :multiple-limit="10">
|
||||
<el-option v-for="item in 24" :key="item" :label="item - 1" :value="item - 1" />
|
||||
</el-select>
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const emit = defineEmits(['update'])
|
||||
const props = defineProps({
|
||||
cron: {
|
||||
type: Object,
|
||||
default: {
|
||||
second: '*',
|
||||
min: '*',
|
||||
hour: '*',
|
||||
day: '*',
|
||||
month: '*',
|
||||
week: '?',
|
||||
year: ''
|
||||
}
|
||||
},
|
||||
check: {
|
||||
type: Function,
|
||||
default: () => {}
|
||||
}
|
||||
})
|
||||
const radioValue = ref(1)
|
||||
const cycle01 = ref(0)
|
||||
const cycle02 = ref(1)
|
||||
const average01 = ref(0)
|
||||
const average02 = ref(1)
|
||||
const checkboxList = ref([])
|
||||
const checkCopy = ref([0])
|
||||
const cycleTotal = computed(() => {
|
||||
cycle01.value = props.check(cycle01.value, 0, 22)
|
||||
cycle02.value = props.check(cycle02.value, cycle01.value + 1, 23)
|
||||
return cycle01.value + '-' + cycle02.value
|
||||
})
|
||||
const averageTotal = computed(() => {
|
||||
average01.value = props.check(average01.value, 0, 22)
|
||||
average02.value = props.check(average02.value, 1, 23 - average01.value)
|
||||
return average01.value + '/' + average02.value
|
||||
})
|
||||
const checkboxString = computed(() => {
|
||||
return checkboxList.value.join(',')
|
||||
})
|
||||
watch(
|
||||
() => props.cron.hour,
|
||||
(value) => changeRadioValue(value)
|
||||
)
|
||||
watch([radioValue, cycleTotal, averageTotal, checkboxString], () => onRadioChange())
|
||||
function changeRadioValue(value) {
|
||||
if (value === '*') {
|
||||
radioValue.value = 1
|
||||
} else if (value.indexOf('-') > -1) {
|
||||
const indexArr = value.split('-')
|
||||
cycle01.value = Number(indexArr[0])
|
||||
cycle02.value = Number(indexArr[1])
|
||||
radioValue.value = 2
|
||||
} else if (value.indexOf('/') > -1) {
|
||||
const indexArr = value.split('/')
|
||||
average01.value = Number(indexArr[0])
|
||||
average02.value = Number(indexArr[1])
|
||||
radioValue.value = 3
|
||||
} else {
|
||||
checkboxList.value = [...new Set(value.split(',').map((item) => Number(item)))]
|
||||
radioValue.value = 4
|
||||
}
|
||||
}
|
||||
function onRadioChange() {
|
||||
switch (radioValue.value) {
|
||||
case 1:
|
||||
emit('update', 'hour', '*', 'hour')
|
||||
break
|
||||
case 2:
|
||||
emit('update', 'hour', cycleTotal.value, 'hour')
|
||||
break
|
||||
case 3:
|
||||
emit('update', 'hour', averageTotal.value, 'hour')
|
||||
break
|
||||
case 4:
|
||||
if (checkboxList.value.length === 0) {
|
||||
checkboxList.value.push(checkCopy.value[0])
|
||||
} else {
|
||||
checkCopy.value = checkboxList.value
|
||||
}
|
||||
emit('update', 'hour', checkboxString.value, 'hour')
|
||||
break
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.el-input-number--small,
|
||||
.el-select,
|
||||
.el-select--small {
|
||||
margin: 0 0.2rem;
|
||||
}
|
||||
.el-select,
|
||||
.el-select--small {
|
||||
width: 18.8rem;
|
||||
}
|
||||
</style>
|
280
src/components/Crontab/index.vue
Normal file
@ -0,0 +1,280 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-tabs type="border-card">
|
||||
<el-tab-pane label="秒" v-if="shouldHide('second')">
|
||||
<CrontabSecond @update="updateCrontabValue" :check="checkNumber" :cron="crontabValueObj" ref="cronsecond" />
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="分钟" v-if="shouldHide('min')">
|
||||
<CrontabMin @update="updateCrontabValue" :check="checkNumber" :cron="crontabValueObj" ref="cronmin" />
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="小时" v-if="shouldHide('hour')">
|
||||
<CrontabHour @update="updateCrontabValue" :check="checkNumber" :cron="crontabValueObj" ref="cronhour" />
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="日" v-if="shouldHide('day')">
|
||||
<CrontabDay @update="updateCrontabValue" :check="checkNumber" :cron="crontabValueObj" ref="cronday" />
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="月" v-if="shouldHide('month')">
|
||||
<CrontabMonth @update="updateCrontabValue" :check="checkNumber" :cron="crontabValueObj" ref="cronmonth" />
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="周" v-if="shouldHide('week')">
|
||||
<CrontabWeek @update="updateCrontabValue" :check="checkNumber" :cron="crontabValueObj" ref="cronweek" />
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="年" v-if="shouldHide('year')">
|
||||
<CrontabYear @update="updateCrontabValue" :check="checkNumber" :cron="crontabValueObj" ref="cronyear" />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
|
||||
<div class="popup-main">
|
||||
<div class="popup-result">
|
||||
<p class="title">时间表达式</p>
|
||||
<table>
|
||||
<thead>
|
||||
<th v-for="item of tabTitles" :key="item">{{ item }}</th>
|
||||
<th>Cron 表达式</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
<td>
|
||||
<span v-if="crontabValueObj.second.length < 10">{{ crontabValueObj.second }}</span>
|
||||
<el-tooltip v-else :content="crontabValueObj.second" placement="top"
|
||||
><span>{{ crontabValueObj.second }}</span></el-tooltip
|
||||
>
|
||||
</td>
|
||||
<td>
|
||||
<span v-if="crontabValueObj.min.length < 10">{{ crontabValueObj.min }}</span>
|
||||
<el-tooltip v-else :content="crontabValueObj.min" placement="top"
|
||||
><span>{{ crontabValueObj.min }}</span></el-tooltip
|
||||
>
|
||||
</td>
|
||||
<td>
|
||||
<span v-if="crontabValueObj.hour.length < 10">{{ crontabValueObj.hour }}</span>
|
||||
<el-tooltip v-else :content="crontabValueObj.hour" placement="top"
|
||||
><span>{{ crontabValueObj.hour }}</span></el-tooltip
|
||||
>
|
||||
</td>
|
||||
<td>
|
||||
<span v-if="crontabValueObj.day.length < 10">{{ crontabValueObj.day }}</span>
|
||||
<el-tooltip v-else :content="crontabValueObj.day" placement="top"
|
||||
><span>{{ crontabValueObj.day }}</span></el-tooltip
|
||||
>
|
||||
</td>
|
||||
<td>
|
||||
<span v-if="crontabValueObj.month.length < 10">{{ crontabValueObj.month }}</span>
|
||||
<el-tooltip v-else :content="crontabValueObj.month" placement="top"
|
||||
><span>{{ crontabValueObj.month }}</span></el-tooltip
|
||||
>
|
||||
</td>
|
||||
<td>
|
||||
<span v-if="crontabValueObj.week.length < 10">{{ crontabValueObj.week }}</span>
|
||||
<el-tooltip v-else :content="crontabValueObj.week" placement="top"
|
||||
><span>{{ crontabValueObj.week }}</span></el-tooltip
|
||||
>
|
||||
</td>
|
||||
<td>
|
||||
<span v-if="crontabValueObj.year.length < 10">{{ crontabValueObj.year }}</span>
|
||||
<el-tooltip v-else :content="crontabValueObj.year" placement="top"
|
||||
><span>{{ crontabValueObj.year }}</span></el-tooltip
|
||||
>
|
||||
</td>
|
||||
<td class="result">
|
||||
<span v-if="crontabValueString.length < 90">{{ crontabValueString }}</span>
|
||||
<el-tooltip v-else :content="crontabValueString" placement="top"
|
||||
><span>{{ crontabValueString }}</span></el-tooltip
|
||||
>
|
||||
</td>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<CrontabResult :ex="crontabValueString"></CrontabResult>
|
||||
|
||||
<div class="pop_btn">
|
||||
<el-button type="primary" @click="submitFill">确定</el-button>
|
||||
<el-button type="warning" @click="clearCron">重置</el-button>
|
||||
<el-button @click="hidePopup">取消</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import CrontabSecond from './second.vue'
|
||||
import CrontabMin from './min.vue'
|
||||
import CrontabHour from './hour.vue'
|
||||
import CrontabDay from './day.vue'
|
||||
import CrontabMonth from './month.vue'
|
||||
import CrontabWeek from './week.vue'
|
||||
import CrontabYear from './year.vue'
|
||||
import CrontabResult from './result.vue'
|
||||
const { proxy } = getCurrentInstance()
|
||||
const emit = defineEmits(['hide', 'fill'])
|
||||
const props = defineProps({
|
||||
hideComponent: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
expression: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
})
|
||||
const tabTitles = ref(['秒', '分钟', '小时', '日', '月', '周', '年'])
|
||||
const tabActive = ref(0)
|
||||
const hideComponent = ref([])
|
||||
const expression = ref('')
|
||||
const crontabValueObj = ref({
|
||||
second: '*',
|
||||
min: '*',
|
||||
hour: '*',
|
||||
day: '*',
|
||||
month: '*',
|
||||
week: '?',
|
||||
year: ''
|
||||
})
|
||||
const crontabValueString = computed(() => {
|
||||
const obj = crontabValueObj.value
|
||||
return obj.second + ' ' + obj.min + ' ' + obj.hour + ' ' + obj.day + ' ' + obj.month + ' ' + obj.week + (obj.year === '' ? '' : ' ' + obj.year)
|
||||
})
|
||||
watch(expression, () => resolveExp())
|
||||
function shouldHide(key) {
|
||||
return !(hideComponent.value && hideComponent.value.includes(key))
|
||||
}
|
||||
function resolveExp() {
|
||||
// 反解析 表达式
|
||||
if (expression.value) {
|
||||
const arr = expression.value.split(/\s+/)
|
||||
if (arr.length >= 6) {
|
||||
//6 位以上是合法表达式
|
||||
let obj = {
|
||||
second: arr[0],
|
||||
min: arr[1],
|
||||
hour: arr[2],
|
||||
day: arr[3],
|
||||
month: arr[4],
|
||||
week: arr[5],
|
||||
year: arr[6] ? arr[6] : ''
|
||||
}
|
||||
crontabValueObj.value = {
|
||||
...obj
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 没有传入的表达式 则还原
|
||||
clearCron()
|
||||
}
|
||||
}
|
||||
// tab切换值
|
||||
function tabCheck(index) {
|
||||
tabActive.value = index
|
||||
}
|
||||
// 由子组件触发,更改表达式组成的字段值
|
||||
function updateCrontabValue(name, value, from) {
|
||||
crontabValueObj.value[name] = value
|
||||
}
|
||||
// 表单选项的子组件校验数字格式(通过-props传递)
|
||||
function checkNumber(value, minLimit, maxLimit) {
|
||||
// 检查必须为整数
|
||||
value = Math.floor(value)
|
||||
if (value < minLimit) {
|
||||
value = minLimit
|
||||
} else if (value > maxLimit) {
|
||||
value = maxLimit
|
||||
}
|
||||
return value
|
||||
}
|
||||
// 隐藏弹窗
|
||||
function hidePopup() {
|
||||
emit('hide')
|
||||
}
|
||||
// 填充表达式
|
||||
function submitFill() {
|
||||
emit('fill', crontabValueString.value)
|
||||
hidePopup()
|
||||
}
|
||||
function clearCron() {
|
||||
// 还原选择项
|
||||
crontabValueObj.value = {
|
||||
second: '*',
|
||||
min: '*',
|
||||
hour: '*',
|
||||
day: '*',
|
||||
month: '*',
|
||||
week: '?',
|
||||
year: ''
|
||||
}
|
||||
}
|
||||
onMounted(() => {
|
||||
expression.value = props.expression
|
||||
hideComponent.value = props.hideComponent
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.pop_btn {
|
||||
text-align: center;
|
||||
margin-top: 20px;
|
||||
}
|
||||
.popup-main {
|
||||
position: relative;
|
||||
margin: 10px auto;
|
||||
background: #fff;
|
||||
border-radius: 5px;
|
||||
font-size: 12px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.popup-title {
|
||||
overflow: hidden;
|
||||
line-height: 34px;
|
||||
padding-top: 6px;
|
||||
background: #f2f2f2;
|
||||
}
|
||||
.popup-result {
|
||||
box-sizing: border-box;
|
||||
line-height: 24px;
|
||||
margin: 25px auto;
|
||||
padding: 15px 10px 10px;
|
||||
border: 1px solid #ccc;
|
||||
position: relative;
|
||||
}
|
||||
.popup-result .title {
|
||||
position: absolute;
|
||||
top: -28px;
|
||||
left: 50%;
|
||||
width: 140px;
|
||||
font-size: 14px;
|
||||
margin-left: -70px;
|
||||
text-align: center;
|
||||
line-height: 30px;
|
||||
background: #fff;
|
||||
}
|
||||
.popup-result table {
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.popup-result table td:not(.result) {
|
||||
width: 3.5rem;
|
||||
min-width: 3.5rem;
|
||||
max-width: 3.5rem;
|
||||
}
|
||||
.popup-result table span {
|
||||
display: block;
|
||||
width: 100%;
|
||||
font-family: arial;
|
||||
line-height: 30px;
|
||||
height: 30px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
border: 1px solid #e8e8e8;
|
||||
}
|
||||
.popup-result-scroll {
|
||||
font-size: 12px;
|
||||
line-height: 24px;
|
||||
height: 10em;
|
||||
overflow-y: auto;
|
||||
}
|
||||
</style>
|
128
src/components/Crontab/min.vue
Normal file
@ -0,0 +1,128 @@
|
||||
<template>
|
||||
<el-form size="small">
|
||||
<el-form-item>
|
||||
<el-radio v-model="radioValue" :label="1"> 分钟,允许的通配符[, - * /] </el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model="radioValue" :label="2">
|
||||
周期从
|
||||
<el-input-number v-model="cycle01" :min="0" :max="58" /> - <el-input-number v-model="cycle02" :min="cycle01 + 1" :max="59" /> 分钟
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model="radioValue" :label="3">
|
||||
从
|
||||
<el-input-number v-model="average01" :min="0" :max="58" /> 分钟开始, 每
|
||||
<el-input-number v-model="average02" :min="1" :max="59 - average01" /> 分钟执行一次
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model="radioValue" :label="4">
|
||||
指定
|
||||
<el-select clearable v-model="checkboxList" placeholder="可多选" multiple :multiple-limit="10">
|
||||
<el-option v-for="item in 60" :key="item" :label="item - 1" :value="item - 1" />
|
||||
</el-select>
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
<script setup>
|
||||
const emit = defineEmits(['update'])
|
||||
const props = defineProps({
|
||||
cron: {
|
||||
type: Object,
|
||||
default: {
|
||||
second: '*',
|
||||
min: '*',
|
||||
hour: '*',
|
||||
day: '*',
|
||||
month: '*',
|
||||
week: '?',
|
||||
year: ''
|
||||
}
|
||||
},
|
||||
check: {
|
||||
type: Function,
|
||||
default: () => {}
|
||||
}
|
||||
})
|
||||
const radioValue = ref(1)
|
||||
const cycle01 = ref(0)
|
||||
const cycle02 = ref(1)
|
||||
const average01 = ref(0)
|
||||
const average02 = ref(1)
|
||||
const checkboxList = ref([])
|
||||
const checkCopy = ref([0])
|
||||
const cycleTotal = computed(() => {
|
||||
cycle01.value = props.check(cycle01.value, 0, 58)
|
||||
cycle02.value = props.check(cycle02.value, cycle01.value + 1, 59)
|
||||
return cycle01.value + '-' + cycle02.value
|
||||
})
|
||||
const averageTotal = computed(() => {
|
||||
average01.value = props.check(average01.value, 0, 58)
|
||||
average02.value = props.check(average02.value, 1, 59 - average01.value)
|
||||
return average01.value + '/' + average02.value
|
||||
})
|
||||
const checkboxString = computed(() => {
|
||||
return checkboxList.value.join(',')
|
||||
})
|
||||
watch(
|
||||
() => props.cron.min,
|
||||
(value) => changeRadioValue(value)
|
||||
)
|
||||
watch([radioValue, cycleTotal, averageTotal, checkboxString], () => onRadioChange())
|
||||
function changeRadioValue(value) {
|
||||
if (value === '*') {
|
||||
radioValue.value = 1
|
||||
} else if (value.indexOf('-') > -1) {
|
||||
const indexArr = value.split('-')
|
||||
cycle01.value = Number(indexArr[0])
|
||||
cycle02.value = Number(indexArr[1])
|
||||
radioValue.value = 2
|
||||
} else if (value.indexOf('/') > -1) {
|
||||
const indexArr = value.split('/')
|
||||
average01.value = Number(indexArr[0])
|
||||
average02.value = Number(indexArr[1])
|
||||
radioValue.value = 3
|
||||
} else {
|
||||
checkboxList.value = [...new Set(value.split(',').map((item) => Number(item)))]
|
||||
radioValue.value = 4
|
||||
}
|
||||
}
|
||||
function onRadioChange() {
|
||||
switch (radioValue.value) {
|
||||
case 1:
|
||||
emit('update', 'min', '*', 'min')
|
||||
break
|
||||
case 2:
|
||||
emit('update', 'min', cycleTotal.value, 'min')
|
||||
break
|
||||
case 3:
|
||||
emit('update', 'min', averageTotal.value, 'min')
|
||||
break
|
||||
case 4:
|
||||
if (checkboxList.value.length === 0) {
|
||||
checkboxList.value.push(checkCopy.value[0])
|
||||
} else {
|
||||
checkCopy.value = checkboxList.value
|
||||
}
|
||||
emit('update', 'min', checkboxString.value, 'min')
|
||||
break
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.el-input-number--small,
|
||||
.el-select,
|
||||
.el-select--small {
|
||||
margin: 0 0.2rem;
|
||||
}
|
||||
.el-select,
|
||||
.el-select--small {
|
||||
width: 19.8rem;
|
||||
}
|
||||
</style>
|
143
src/components/Crontab/month.vue
Normal file
@ -0,0 +1,143 @@
|
||||
<template>
|
||||
<el-form size="small">
|
||||
<el-form-item>
|
||||
<el-radio v-model="radioValue" :label="1"> 月,允许的通配符[, - * /] </el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model="radioValue" :label="2">
|
||||
周期从
|
||||
<el-input-number v-model="cycle01" :min="1" :max="11" /> - <el-input-number v-model="cycle02" :min="cycle01 + 1" :max="12" /> 月
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model="radioValue" :label="3">
|
||||
从
|
||||
<el-input-number v-model="average01" :min="1" :max="11" /> 月开始,每
|
||||
<el-input-number v-model="average02" :min="1" :max="12 - average01" /> 月月执行一次
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model="radioValue" :label="4">
|
||||
指定
|
||||
<el-select clearable v-model="checkboxList" placeholder="可多选" multiple :multiple-limit="8">
|
||||
<el-option v-for="item in monthList" :key="item.key" :label="item.value" :value="item.key" />
|
||||
</el-select>
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const emit = defineEmits(['update'])
|
||||
const props = defineProps({
|
||||
cron: {
|
||||
type: Object,
|
||||
default: {
|
||||
second: '*',
|
||||
min: '*',
|
||||
hour: '*',
|
||||
day: '*',
|
||||
month: '*',
|
||||
week: '?',
|
||||
year: ''
|
||||
}
|
||||
},
|
||||
check: {
|
||||
type: Function,
|
||||
default: () => {}
|
||||
}
|
||||
})
|
||||
const radioValue = ref(1)
|
||||
const cycle01 = ref(1)
|
||||
const cycle02 = ref(2)
|
||||
const average01 = ref(1)
|
||||
const average02 = ref(1)
|
||||
const checkboxList = ref([])
|
||||
const checkCopy = ref([1])
|
||||
const monthList = ref([
|
||||
{ key: 1, value: '一月' },
|
||||
{ key: 2, value: '二月' },
|
||||
{ key: 3, value: '三月' },
|
||||
{ key: 4, value: '四月' },
|
||||
{ key: 5, value: '五月' },
|
||||
{ key: 6, value: '六月' },
|
||||
{ key: 7, value: '七月' },
|
||||
{ key: 8, value: '八月' },
|
||||
{ key: 9, value: '九月' },
|
||||
{ key: 10, value: '十月' },
|
||||
{ key: 11, value: '十一月' },
|
||||
{ key: 12, value: '十二月' }
|
||||
])
|
||||
const cycleTotal = computed(() => {
|
||||
cycle01.value = props.check(cycle01.value, 1, 11)
|
||||
cycle02.value = props.check(cycle02.value, cycle01.value + 1, 12)
|
||||
return cycle01.value + '-' + cycle02.value
|
||||
})
|
||||
const averageTotal = computed(() => {
|
||||
average01.value = props.check(average01.value, 1, 11)
|
||||
average02.value = props.check(average02.value, 1, 12 - average01.value)
|
||||
return average01.value + '/' + average02.value
|
||||
})
|
||||
const checkboxString = computed(() => {
|
||||
return checkboxList.value.join(',')
|
||||
})
|
||||
watch(
|
||||
() => props.cron.month,
|
||||
(value) => changeRadioValue(value)
|
||||
)
|
||||
watch([radioValue, cycleTotal, averageTotal, checkboxString], () => onRadioChange())
|
||||
function changeRadioValue(value) {
|
||||
if (value === '*') {
|
||||
radioValue.value = 1
|
||||
} else if (value.indexOf('-') > -1) {
|
||||
const indexArr = value.split('-')
|
||||
cycle01.value = Number(indexArr[0])
|
||||
cycle02.value = Number(indexArr[1])
|
||||
radioValue.value = 2
|
||||
} else if (value.indexOf('/') > -1) {
|
||||
const indexArr = value.split('/')
|
||||
average01.value = Number(indexArr[0])
|
||||
average02.value = Number(indexArr[1])
|
||||
radioValue.value = 3
|
||||
} else {
|
||||
checkboxList.value = [...new Set(value.split(',').map((item) => Number(item)))]
|
||||
radioValue.value = 4
|
||||
}
|
||||
}
|
||||
function onRadioChange() {
|
||||
switch (radioValue.value) {
|
||||
case 1:
|
||||
emit('update', 'month', '*', 'month')
|
||||
break
|
||||
case 2:
|
||||
emit('update', 'month', cycleTotal.value, 'month')
|
||||
break
|
||||
case 3:
|
||||
emit('update', 'month', averageTotal.value, 'month')
|
||||
break
|
||||
case 4:
|
||||
if (checkboxList.value.length === 0) {
|
||||
checkboxList.value.push(checkCopy.value[0])
|
||||
} else {
|
||||
checkCopy.value = checkboxList.value
|
||||
}
|
||||
emit('update', 'month', checkboxString.value, 'month')
|
||||
break
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.el-input-number--small,
|
||||
.el-select,
|
||||
.el-select--small {
|
||||
margin: 0 0.2rem;
|
||||
}
|
||||
.el-select,
|
||||
.el-select--small {
|
||||
width: 18.8rem;
|
||||
}
|
||||
</style>
|
560
src/components/Crontab/result.vue
Normal file
@ -0,0 +1,560 @@
|
||||
<template>
|
||||
<div class="popup-result">
|
||||
<p class="title">最近5次运行时间</p>
|
||||
<ul class="popup-result-scroll">
|
||||
<template v-if="isShow">
|
||||
<li v-for="item in resultList" :key="item">{{ item }}</li>
|
||||
</template>
|
||||
<li v-else>计算结果中...</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const props = defineProps({
|
||||
ex: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
})
|
||||
const dayRule = ref('')
|
||||
const dayRuleSup = ref('')
|
||||
const dateArr = ref([])
|
||||
const resultList = ref([])
|
||||
const isShow = ref(false)
|
||||
watch(
|
||||
() => props.ex,
|
||||
() => expressionChange()
|
||||
)
|
||||
// 表达式值变化时,开始去计算结果
|
||||
function expressionChange() {
|
||||
// 计算开始-隐藏结果
|
||||
isShow.value = false
|
||||
// 获取规则数组[0秒、1分、2时、3日、4月、5星期、6年]
|
||||
let ruleArr = props.ex.split(' ')
|
||||
// 用于记录进入循环的次数
|
||||
let nums = 0
|
||||
// 用于暂时存符号时间规则结果的数组
|
||||
let resultArr = []
|
||||
// 获取当前时间精确至[年、月、日、时、分、秒]
|
||||
let nTime = new Date()
|
||||
let nYear = nTime.getFullYear()
|
||||
let nMonth = nTime.getMonth() + 1
|
||||
let nDay = nTime.getDate()
|
||||
let nHour = nTime.getHours()
|
||||
let nMin = nTime.getMinutes()
|
||||
let nSecond = nTime.getSeconds()
|
||||
// 根据规则获取到近100年可能年数组、月数组等等
|
||||
getSecondArr(ruleArr[0])
|
||||
getMinArr(ruleArr[1])
|
||||
getHourArr(ruleArr[2])
|
||||
getDayArr(ruleArr[3])
|
||||
getMonthArr(ruleArr[4])
|
||||
getWeekArr(ruleArr[5])
|
||||
getYearArr(ruleArr[6], nYear)
|
||||
// 将获取到的数组赋值-方便使用
|
||||
let sDate = dateArr.value[0]
|
||||
let mDate = dateArr.value[1]
|
||||
let hDate = dateArr.value[2]
|
||||
let DDate = dateArr.value[3]
|
||||
let MDate = dateArr.value[4]
|
||||
let YDate = dateArr.value[5]
|
||||
// 获取当前时间在数组中的索引
|
||||
let sIdx = getIndex(sDate, nSecond)
|
||||
let mIdx = getIndex(mDate, nMin)
|
||||
let hIdx = getIndex(hDate, nHour)
|
||||
let DIdx = getIndex(DDate, nDay)
|
||||
let MIdx = getIndex(MDate, nMonth)
|
||||
let YIdx = getIndex(YDate, nYear)
|
||||
// 重置月日时分秒的函数(后面用的比较多)
|
||||
const resetSecond = function () {
|
||||
sIdx = 0
|
||||
nSecond = sDate[sIdx]
|
||||
}
|
||||
const resetMin = function () {
|
||||
mIdx = 0
|
||||
nMin = mDate[mIdx]
|
||||
resetSecond()
|
||||
}
|
||||
const resetHour = function () {
|
||||
hIdx = 0
|
||||
nHour = hDate[hIdx]
|
||||
resetMin()
|
||||
}
|
||||
const resetDay = function () {
|
||||
DIdx = 0
|
||||
nDay = DDate[DIdx]
|
||||
resetHour()
|
||||
}
|
||||
const resetMonth = function () {
|
||||
MIdx = 0
|
||||
nMonth = MDate[MIdx]
|
||||
resetDay()
|
||||
}
|
||||
// 如果当前年份不为数组中当前值
|
||||
if (nYear !== YDate[YIdx]) {
|
||||
resetMonth()
|
||||
}
|
||||
// 如果当前月份不为数组中当前值
|
||||
if (nMonth !== MDate[MIdx]) {
|
||||
resetDay()
|
||||
}
|
||||
// 如果当前“日”不为数组中当前值
|
||||
if (nDay !== DDate[DIdx]) {
|
||||
resetHour()
|
||||
}
|
||||
// 如果当前“时”不为数组中当前值
|
||||
if (nHour !== hDate[hIdx]) {
|
||||
resetMin()
|
||||
}
|
||||
// 如果当前“分”不为数组中当前值
|
||||
if (nMin !== mDate[mIdx]) {
|
||||
resetSecond()
|
||||
}
|
||||
// 循环年份数组
|
||||
goYear: for (let Yi = YIdx; Yi < YDate.length; Yi++) {
|
||||
let YY = YDate[Yi]
|
||||
// 如果到达最大值时
|
||||
if (nMonth > MDate[MDate.length - 1]) {
|
||||
resetMonth()
|
||||
continue
|
||||
}
|
||||
// 循环月份数组
|
||||
goMonth: for (let Mi = MIdx; Mi < MDate.length; Mi++) {
|
||||
// 赋值、方便后面运算
|
||||
let MM = MDate[Mi]
|
||||
MM = MM < 10 ? '0' + MM : MM
|
||||
// 如果到达最大值时
|
||||
if (nDay > DDate[DDate.length - 1]) {
|
||||
resetDay()
|
||||
if (Mi === MDate.length - 1) {
|
||||
resetMonth()
|
||||
continue goYear
|
||||
}
|
||||
continue
|
||||
}
|
||||
// 循环日期数组
|
||||
goDay: for (let Di = DIdx; Di < DDate.length; Di++) {
|
||||
// 赋值、方便后面运算
|
||||
let DD = DDate[Di]
|
||||
let thisDD = DD < 10 ? '0' + DD : DD
|
||||
// 如果到达最大值时
|
||||
if (nHour > hDate[hDate.length - 1]) {
|
||||
resetHour()
|
||||
if (Di === DDate.length - 1) {
|
||||
resetDay()
|
||||
if (Mi === MDate.length - 1) {
|
||||
resetMonth()
|
||||
continue goYear
|
||||
}
|
||||
continue goMonth
|
||||
}
|
||||
continue
|
||||
}
|
||||
// 判断日期的合法性,不合法的话也是跳出当前循环
|
||||
if (
|
||||
checkDate(YY + '-' + MM + '-' + thisDD + ' 00:00:00') !== true &&
|
||||
dayRule.value !== 'workDay' &&
|
||||
dayRule.value !== 'lastWeek' &&
|
||||
dayRule.value !== 'lastDay'
|
||||
) {
|
||||
resetDay()
|
||||
continue goMonth
|
||||
}
|
||||
// 如果日期规则中有值时
|
||||
if (dayRule.value === 'lastDay') {
|
||||
// 如果不是合法日期则需要将前将日期调到合法日期即月末最后一天
|
||||
if (checkDate(YY + '-' + MM + '-' + thisDD + ' 00:00:00') !== true) {
|
||||
while (DD > 0 && checkDate(YY + '-' + MM + '-' + thisDD + ' 00:00:00') !== true) {
|
||||
DD--
|
||||
thisDD = DD < 10 ? '0' + DD : DD
|
||||
}
|
||||
}
|
||||
} else if (dayRule.value === 'workDay') {
|
||||
// 校验并调整如果是2月30号这种日期传进来时需调整至正常月底
|
||||
if (checkDate(YY + '-' + MM + '-' + thisDD + ' 00:00:00') !== true) {
|
||||
while (DD > 0 && checkDate(YY + '-' + MM + '-' + thisDD + ' 00:00:00') !== true) {
|
||||
DD--
|
||||
thisDD = DD < 10 ? '0' + DD : DD
|
||||
}
|
||||
}
|
||||
// 获取达到条件的日期是星期X
|
||||
let thisWeek = formatDate(new Date(YY + '-' + MM + '-' + thisDD + ' 00:00:00'), 'week')
|
||||
// 当星期日时
|
||||
if (thisWeek === 1) {
|
||||
// 先找下一个日,并判断是否为月底
|
||||
DD++
|
||||
thisDD = DD < 10 ? '0' + DD : DD
|
||||
// 判断下一日已经不是合法日期
|
||||
if (checkDate(YY + '-' + MM + '-' + thisDD + ' 00:00:00') !== true) {
|
||||
DD -= 3
|
||||
}
|
||||
} else if (thisWeek === 7) {
|
||||
// 当星期6时只需判断不是1号就可进行操作
|
||||
if (dayRuleSup.value !== 1) {
|
||||
DD--
|
||||
} else {
|
||||
DD += 2
|
||||
}
|
||||
}
|
||||
} else if (dayRule.value === 'weekDay') {
|
||||
// 如果指定了是星期几
|
||||
// 获取当前日期是属于星期几
|
||||
let thisWeek = formatDate(new Date(YY + '-' + MM + '-' + DD + ' 00:00:00'), 'week')
|
||||
// 校验当前星期是否在星期池(dayRuleSup)中
|
||||
if (dayRuleSup.value.indexOf(thisWeek) < 0) {
|
||||
// 如果到达最大值时
|
||||
if (Di === DDate.length - 1) {
|
||||
resetDay()
|
||||
if (Mi === MDate.length - 1) {
|
||||
resetMonth()
|
||||
continue goYear
|
||||
}
|
||||
continue goMonth
|
||||
}
|
||||
continue
|
||||
}
|
||||
} else if (dayRule.value === 'assWeek') {
|
||||
// 如果指定了是第几周的星期几
|
||||
// 获取每月1号是属于星期几
|
||||
let thisWeek = formatDate(new Date(YY + '-' + MM + '-' + DD + ' 00:00:00'), 'week')
|
||||
if (dayRuleSup.value[1] >= thisWeek) {
|
||||
DD = (dayRuleSup.value[0] - 1) * 7 + dayRuleSup.value[1] - thisWeek + 1
|
||||
} else {
|
||||
DD = dayRuleSup.value[0] * 7 + dayRuleSup.value[1] - thisWeek + 1
|
||||
}
|
||||
} else if (dayRule.value === 'lastWeek') {
|
||||
// 如果指定了每月最后一个星期几
|
||||
// 校验并调整如果是2月30号这种日期传进来时需调整至正常月底
|
||||
if (checkDate(YY + '-' + MM + '-' + thisDD + ' 00:00:00') !== true) {
|
||||
while (DD > 0 && checkDate(YY + '-' + MM + '-' + thisDD + ' 00:00:00') !== true) {
|
||||
DD--
|
||||
thisDD = DD < 10 ? '0' + DD : DD
|
||||
}
|
||||
}
|
||||
// 获取月末最后一天是星期几
|
||||
let thisWeek = formatDate(new Date(YY + '-' + MM + '-' + thisDD + ' 00:00:00'), 'week')
|
||||
// 找到要求中最近的那个星期几
|
||||
if (dayRuleSup.value < thisWeek) {
|
||||
DD -= thisWeek - dayRuleSup.value
|
||||
} else if (dayRuleSup.value > thisWeek) {
|
||||
DD -= 7 - (dayRuleSup.value - thisWeek)
|
||||
}
|
||||
}
|
||||
// 判断时间值是否小于10置换成“05”这种格式
|
||||
DD = DD < 10 ? '0' + DD : DD
|
||||
// 循环“时”数组
|
||||
goHour: for (let hi = hIdx; hi < hDate.length; hi++) {
|
||||
let hh = hDate[hi] < 10 ? '0' + hDate[hi] : hDate[hi]
|
||||
// 如果到达最大值时
|
||||
if (nMin > mDate[mDate.length - 1]) {
|
||||
resetMin()
|
||||
if (hi === hDate.length - 1) {
|
||||
resetHour()
|
||||
if (Di === DDate.length - 1) {
|
||||
resetDay()
|
||||
if (Mi === MDate.length - 1) {
|
||||
resetMonth()
|
||||
continue goYear
|
||||
}
|
||||
continue goMonth
|
||||
}
|
||||
continue goDay
|
||||
}
|
||||
continue
|
||||
}
|
||||
// 循环"分"数组
|
||||
goMin: for (let mi = mIdx; mi < mDate.length; mi++) {
|
||||
let mm = mDate[mi] < 10 ? '0' + mDate[mi] : mDate[mi]
|
||||
// 如果到达最大值时
|
||||
if (nSecond > sDate[sDate.length - 1]) {
|
||||
resetSecond()
|
||||
if (mi === mDate.length - 1) {
|
||||
resetMin()
|
||||
if (hi === hDate.length - 1) {
|
||||
resetHour()
|
||||
if (Di === DDate.length - 1) {
|
||||
resetDay()
|
||||
if (Mi === MDate.length - 1) {
|
||||
resetMonth()
|
||||
continue goYear
|
||||
}
|
||||
continue goMonth
|
||||
}
|
||||
continue goDay
|
||||
}
|
||||
continue goHour
|
||||
}
|
||||
continue
|
||||
}
|
||||
// 循环"秒"数组
|
||||
goSecond: for (let si = sIdx; si <= sDate.length - 1; si++) {
|
||||
let ss = sDate[si] < 10 ? '0' + sDate[si] : sDate[si]
|
||||
// 添加当前时间(时间合法性在日期循环时已经判断)
|
||||
if (MM !== '00' && DD !== '00') {
|
||||
resultArr.push(YY + '-' + MM + '-' + DD + ' ' + hh + ':' + mm + ':' + ss)
|
||||
nums++
|
||||
}
|
||||
// 如果条数满了就退出循环
|
||||
if (nums === 5) break goYear
|
||||
// 如果到达最大值时
|
||||
if (si === sDate.length - 1) {
|
||||
resetSecond()
|
||||
if (mi === mDate.length - 1) {
|
||||
resetMin()
|
||||
if (hi === hDate.length - 1) {
|
||||
resetHour()
|
||||
if (Di === DDate.length - 1) {
|
||||
resetDay()
|
||||
if (Mi === MDate.length - 1) {
|
||||
resetMonth()
|
||||
continue goYear
|
||||
}
|
||||
continue goMonth
|
||||
}
|
||||
continue goDay
|
||||
}
|
||||
continue goHour
|
||||
}
|
||||
continue goMin
|
||||
}
|
||||
} //goSecond
|
||||
} //goMin
|
||||
} //goHour
|
||||
} //goDay
|
||||
} //goMonth
|
||||
}
|
||||
// 判断100年内的结果条数
|
||||
if (resultArr.length === 0) {
|
||||
resultList.value = ['没有达到条件的结果!']
|
||||
} else {
|
||||
resultList.value = resultArr
|
||||
if (resultArr.length !== 5) {
|
||||
resultList.value.push('最近100年内只有上面' + resultArr.length + '条结果!')
|
||||
}
|
||||
}
|
||||
// 计算完成-显示结果
|
||||
isShow.value = true
|
||||
}
|
||||
// 用于计算某位数字在数组中的索引
|
||||
function getIndex(arr, value) {
|
||||
if (value <= arr[0] || value > arr[arr.length - 1]) {
|
||||
return 0
|
||||
} else {
|
||||
for (let i = 0; i < arr.length - 1; i++) {
|
||||
if (value > arr[i] && value <= arr[i + 1]) {
|
||||
return i + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 获取"年"数组
|
||||
function getYearArr(rule, year) {
|
||||
dateArr.value[5] = getOrderArr(year, year + 100)
|
||||
if (rule !== undefined) {
|
||||
if (rule.indexOf('-') >= 0) {
|
||||
dateArr.value[5] = getCycleArr(rule, year + 100, false)
|
||||
} else if (rule.indexOf('/') >= 0) {
|
||||
dateArr.value[5] = getAverageArr(rule, year + 100)
|
||||
} else if (rule !== '*') {
|
||||
dateArr.value[5] = getAssignArr(rule)
|
||||
}
|
||||
}
|
||||
}
|
||||
// 获取"月"数组
|
||||
function getMonthArr(rule) {
|
||||
dateArr.value[4] = getOrderArr(1, 12)
|
||||
if (rule.indexOf('-') >= 0) {
|
||||
dateArr.value[4] = getCycleArr(rule, 12, false)
|
||||
} else if (rule.indexOf('/') >= 0) {
|
||||
dateArr.value[4] = getAverageArr(rule, 12)
|
||||
} else if (rule !== '*') {
|
||||
dateArr.value[4] = getAssignArr(rule)
|
||||
}
|
||||
}
|
||||
// 获取"日"数组-主要为日期规则
|
||||
function getWeekArr(rule) {
|
||||
// 只有当日期规则的两个值均为“”时则表达日期是有选项的
|
||||
if (dayRule.value === '' && dayRuleSup.value === '') {
|
||||
if (rule.indexOf('-') >= 0) {
|
||||
dayRule.value = 'weekDay'
|
||||
dayRuleSup.value = getCycleArr(rule, 7, false)
|
||||
} else if (rule.indexOf('#') >= 0) {
|
||||
dayRule.value = 'assWeek'
|
||||
let matchRule = rule.match(/[0-9]{1}/g)
|
||||
dayRuleSup.value = [Number(matchRule[1]), Number(matchRule[0])]
|
||||
dateArr.value[3] = [1]
|
||||
if (dayRuleSup.value[1] === 7) {
|
||||
dayRuleSup.value[1] = 0
|
||||
}
|
||||
} else if (rule.indexOf('L') >= 0) {
|
||||
dayRule.value = 'lastWeek'
|
||||
dayRuleSup.value = Number(rule.match(/[0-9]{1,2}/g)[0])
|
||||
dateArr.value[3] = [31]
|
||||
if (dayRuleSup.value === 7) {
|
||||
dayRuleSup.value = 0
|
||||
}
|
||||
} else if (rule !== '*' && rule !== '?') {
|
||||
dayRule.value = 'weekDay'
|
||||
dayRuleSup.value = getAssignArr(rule)
|
||||
}
|
||||
}
|
||||
}
|
||||
// 获取"日"数组-少量为日期规则
|
||||
function getDayArr(rule) {
|
||||
dateArr.value[3] = getOrderArr(1, 31)
|
||||
dayRule.value = ''
|
||||
dayRuleSup.value = ''
|
||||
if (rule.indexOf('-') >= 0) {
|
||||
dateArr.value[3] = getCycleArr(rule, 31, false)
|
||||
dayRuleSup.value = 'null'
|
||||
} else if (rule.indexOf('/') >= 0) {
|
||||
dateArr.value[3] = getAverageArr(rule, 31)
|
||||
dayRuleSup.value = 'null'
|
||||
} else if (rule.indexOf('W') >= 0) {
|
||||
dayRule.value = 'workDay'
|
||||
dayRuleSup.value = Number(rule.match(/[0-9]{1,2}/g)[0])
|
||||
dateArr.value[3] = [dayRuleSup.value]
|
||||
} else if (rule.indexOf('L') >= 0) {
|
||||
dayRule.value = 'lastDay'
|
||||
dayRuleSup.value = 'null'
|
||||
dateArr.value[3] = [31]
|
||||
} else if (rule !== '*' && rule !== '?') {
|
||||
dateArr.value[3] = getAssignArr(rule)
|
||||
dayRuleSup.value = 'null'
|
||||
} else if (rule === '*') {
|
||||
dayRuleSup.value = 'null'
|
||||
}
|
||||
}
|
||||
// 获取"时"数组
|
||||
function getHourArr(rule) {
|
||||
dateArr.value[2] = getOrderArr(0, 23)
|
||||
if (rule.indexOf('-') >= 0) {
|
||||
dateArr.value[2] = getCycleArr(rule, 24, true)
|
||||
} else if (rule.indexOf('/') >= 0) {
|
||||
dateArr.value[2] = getAverageArr(rule, 23)
|
||||
} else if (rule !== '*') {
|
||||
dateArr.value[2] = getAssignArr(rule)
|
||||
}
|
||||
}
|
||||
// 获取"分"数组
|
||||
function getMinArr(rule) {
|
||||
dateArr.value[1] = getOrderArr(0, 59)
|
||||
if (rule.indexOf('-') >= 0) {
|
||||
dateArr.value[1] = getCycleArr(rule, 60, true)
|
||||
} else if (rule.indexOf('/') >= 0) {
|
||||
dateArr.value[1] = getAverageArr(rule, 59)
|
||||
} else if (rule !== '*') {
|
||||
dateArr.value[1] = getAssignArr(rule)
|
||||
}
|
||||
}
|
||||
// 获取"秒"数组
|
||||
function getSecondArr(rule) {
|
||||
dateArr.value[0] = getOrderArr(0, 59)
|
||||
if (rule.indexOf('-') >= 0) {
|
||||
dateArr.value[0] = getCycleArr(rule, 60, true)
|
||||
} else if (rule.indexOf('/') >= 0) {
|
||||
dateArr.value[0] = getAverageArr(rule, 59)
|
||||
} else if (rule !== '*') {
|
||||
dateArr.value[0] = getAssignArr(rule)
|
||||
}
|
||||
}
|
||||
// 根据传进来的min-max返回一个顺序的数组
|
||||
function getOrderArr(min, max) {
|
||||
let arr = []
|
||||
for (let i = min; i <= max; i++) {
|
||||
arr.push(i)
|
||||
}
|
||||
return arr
|
||||
}
|
||||
// 根据规则中指定的零散值返回一个数组
|
||||
function getAssignArr(rule) {
|
||||
let arr = []
|
||||
let assiginArr = rule.split(',')
|
||||
for (let i = 0; i < assiginArr.length; i++) {
|
||||
arr[i] = Number(assiginArr[i])
|
||||
}
|
||||
arr.sort(compare)
|
||||
return arr
|
||||
}
|
||||
// 根据一定算术规则计算返回一个数组
|
||||
function getAverageArr(rule, limit) {
|
||||
let arr = []
|
||||
let agArr = rule.split('/')
|
||||
let min = Number(agArr[0])
|
||||
let step = Number(agArr[1])
|
||||
while (min <= limit) {
|
||||
arr.push(min)
|
||||
min += step
|
||||
}
|
||||
return arr
|
||||
}
|
||||
// 根据规则返回一个具有周期性的数组
|
||||
function getCycleArr(rule, limit, status) {
|
||||
// status--表示是否从0开始(则从1开始)
|
||||
let arr = []
|
||||
let cycleArr = rule.split('-')
|
||||
let min = Number(cycleArr[0])
|
||||
let max = Number(cycleArr[1])
|
||||
if (min > max) {
|
||||
max += limit
|
||||
}
|
||||
for (let i = min; i <= max; i++) {
|
||||
let add = 0
|
||||
if (status === false && i % limit === 0) {
|
||||
add = limit
|
||||
}
|
||||
arr.push(Math.round((i % limit) + add))
|
||||
}
|
||||
arr.sort(compare)
|
||||
return arr
|
||||
}
|
||||
// 比较数字大小(用于Array.sort)
|
||||
function compare(value1, value2) {
|
||||
if (value2 - value1 > 0) {
|
||||
return -1
|
||||
} else {
|
||||
return 1
|
||||
}
|
||||
}
|
||||
// 格式化日期格式如:2017-9-19 18:04:33
|
||||
function formatDate(value, type) {
|
||||
// 计算日期相关值
|
||||
let time = typeof value == 'number' ? new Date(value) : value
|
||||
let Y = time.getFullYear()
|
||||
let M = time.getMonth() + 1
|
||||
let D = time.getDate()
|
||||
let h = time.getHours()
|
||||
let m = time.getMinutes()
|
||||
let s = time.getSeconds()
|
||||
let week = time.getDay()
|
||||
// 如果传递了type的话
|
||||
if (type === undefined) {
|
||||
return (
|
||||
Y +
|
||||
'-' +
|
||||
(M < 10 ? '0' + M : M) +
|
||||
'-' +
|
||||
(D < 10 ? '0' + D : D) +
|
||||
' ' +
|
||||
(h < 10 ? '0' + h : h) +
|
||||
':' +
|
||||
(m < 10 ? '0' + m : m) +
|
||||
':' +
|
||||
(s < 10 ? '0' + s : s)
|
||||
)
|
||||
} else if (type === 'week') {
|
||||
// 在quartz中 1为星期日
|
||||
return week + 1
|
||||
}
|
||||
}
|
||||
// 检查日期是否存在
|
||||
function checkDate(value) {
|
||||
let time = new Date(value)
|
||||
let format = formatDate(time)
|
||||
return value === format
|
||||
}
|
||||
onMounted(() => {
|
||||
expressionChange()
|
||||
})
|
||||
</script>
|
130
src/components/Crontab/second.vue
Normal file
@ -0,0 +1,130 @@
|
||||
<template>
|
||||
<el-form size="small">
|
||||
<el-form-item>
|
||||
<el-radio v-model="radioValue" :label="1"> 秒,允许的通配符[, - * /] </el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model="radioValue" :label="2">
|
||||
周期从
|
||||
<el-input-number v-model="cycle01" :min="0" :max="58" /> - <el-input-number v-model="cycle02" :min="cycle01 + 1" :max="59" /> 秒
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model="radioValue" :label="3">
|
||||
从
|
||||
<el-input-number v-model="average01" :min="0" :max="58" /> 秒开始,每
|
||||
<el-input-number v-model="average02" :min="1" :max="59 - average01" /> 秒执行一次
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model="radioValue" :label="4">
|
||||
指定
|
||||
<el-select clearable v-model="checkboxList" placeholder="可多选" multiple :multiple-limit="10">
|
||||
<el-option v-for="item in 60" :key="item" :label="item - 1" :value="item - 1" />
|
||||
</el-select>
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const emit = defineEmits(['update'])
|
||||
const props = defineProps({
|
||||
cron: {
|
||||
type: Object,
|
||||
default: {
|
||||
second: '*',
|
||||
min: '*',
|
||||
hour: '*',
|
||||
day: '*',
|
||||
month: '*',
|
||||
week: '?',
|
||||
year: ''
|
||||
}
|
||||
},
|
||||
check: {
|
||||
type: Function,
|
||||
default: () => {}
|
||||
}
|
||||
})
|
||||
const radioValue = ref(1)
|
||||
const cycle01 = ref(0)
|
||||
const cycle02 = ref(1)
|
||||
const average01 = ref(0)
|
||||
const average02 = ref(1)
|
||||
const checkboxList = ref([])
|
||||
const checkCopy = ref([0])
|
||||
const cycleTotal = computed(() => {
|
||||
cycle01.value = props.check(cycle01.value, 0, 58)
|
||||
cycle02.value = props.check(cycle02.value, cycle01.value + 1, 59)
|
||||
return cycle01.value + '-' + cycle02.value
|
||||
})
|
||||
const averageTotal = computed(() => {
|
||||
average01.value = props.check(average01.value, 0, 58)
|
||||
average02.value = props.check(average02.value, 1, 59 - average01.value)
|
||||
return average01.value + '/' + average02.value
|
||||
})
|
||||
const checkboxString = computed(() => {
|
||||
return checkboxList.value.join(',')
|
||||
})
|
||||
watch(
|
||||
() => props.cron.second,
|
||||
(value) => changeRadioValue(value)
|
||||
)
|
||||
watch([radioValue, cycleTotal, averageTotal, checkboxString], () => onRadioChange())
|
||||
function changeRadioValue(value) {
|
||||
if (value === '*') {
|
||||
radioValue.value = 1
|
||||
} else if (value.indexOf('-') > -1) {
|
||||
const indexArr = value.split('-')
|
||||
cycle01.value = Number(indexArr[0])
|
||||
cycle02.value = Number(indexArr[1])
|
||||
radioValue.value = 2
|
||||
} else if (value.indexOf('/') > -1) {
|
||||
const indexArr = value.split('/')
|
||||
average01.value = Number(indexArr[0])
|
||||
average02.value = Number(indexArr[1])
|
||||
radioValue.value = 3
|
||||
} else {
|
||||
checkboxList.value = [...new Set(value.split(',').map((item) => Number(item)))]
|
||||
radioValue.value = 4
|
||||
}
|
||||
}
|
||||
// 单选按钮值变化时
|
||||
function onRadioChange() {
|
||||
switch (radioValue.value) {
|
||||
case 1:
|
||||
emit('update', 'second', '*', 'second')
|
||||
break
|
||||
case 2:
|
||||
emit('update', 'second', cycleTotal.value, 'second')
|
||||
break
|
||||
case 3:
|
||||
emit('update', 'second', averageTotal.value, 'second')
|
||||
break
|
||||
case 4:
|
||||
if (checkboxList.value.length === 0) {
|
||||
checkboxList.value.push(checkCopy.value[0])
|
||||
} else {
|
||||
checkCopy.value = checkboxList.value
|
||||
}
|
||||
emit('update', 'second', checkboxString.value, 'second')
|
||||
break
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.el-input-number--small,
|
||||
.el-select,
|
||||
.el-select--small {
|
||||
margin: 0 0.2rem;
|
||||
}
|
||||
.el-select,
|
||||
.el-select--small {
|
||||
width: 18.8rem;
|
||||
}
|
||||
</style>
|
190
src/components/Crontab/week.vue
Normal file
@ -0,0 +1,190 @@
|
||||
<template>
|
||||
<el-form size="small">
|
||||
<el-form-item>
|
||||
<el-radio v-model="radioValue" :label="1"> 周,允许的通配符[, - * ? / L #] </el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model="radioValue" :label="2"> 不指定 </el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model="radioValue" :label="3">
|
||||
周期从
|
||||
<el-select clearable v-model="cycle01">
|
||||
<el-option v-for="(item, index) of weekList" :key="index" :label="item.value" :value="item.key" :disabled="item.key === 7">{{
|
||||
item.value
|
||||
}}</el-option>
|
||||
</el-select>
|
||||
-
|
||||
<el-select clearable v-model="cycle02">
|
||||
<el-option v-for="(item, index) of weekList" :key="index" :label="item.value" :value="item.key" :disabled="item.key <= cycle01">{{
|
||||
item.value
|
||||
}}</el-option>
|
||||
</el-select>
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model="radioValue" :label="4">
|
||||
第
|
||||
<el-input-number v-model="average01" :min="1" :max="4" /> 周的
|
||||
<el-select clearable v-model="average02">
|
||||
<el-option v-for="item in weekList" :key="item.key" :label="item.value" :value="item.key" />
|
||||
</el-select>
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model="radioValue" :label="5">
|
||||
本月最后一个
|
||||
<el-select clearable v-model="weekday">
|
||||
<el-option v-for="item in weekList" :key="item.key" :label="item.value" :value="item.key" />
|
||||
</el-select>
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model="radioValue" :label="6">
|
||||
指定
|
||||
<el-select class="multiselect" clearable v-model="checkboxList" placeholder="可多选" multiple :multiple-limit="6">
|
||||
<el-option v-for="item in weekList" :key="item.key" :label="item.value" :value="item.key" />
|
||||
</el-select>
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const emit = defineEmits(['update'])
|
||||
const props = defineProps({
|
||||
cron: {
|
||||
type: Object,
|
||||
default: {
|
||||
second: '*',
|
||||
min: '*',
|
||||
hour: '*',
|
||||
day: '*',
|
||||
month: '*',
|
||||
week: '?',
|
||||
year: ''
|
||||
}
|
||||
},
|
||||
check: {
|
||||
type: Function,
|
||||
default: () => {}
|
||||
}
|
||||
})
|
||||
const radioValue = ref(2)
|
||||
const cycle01 = ref(2)
|
||||
const cycle02 = ref(3)
|
||||
const average01 = ref(1)
|
||||
const average02 = ref(2)
|
||||
const weekday = ref(2)
|
||||
const checkboxList = ref([])
|
||||
const checkCopy = ref([2])
|
||||
const weekList = ref([
|
||||
{ key: 1, value: '星期日' },
|
||||
{ key: 2, value: '星期一' },
|
||||
{ key: 3, value: '星期二' },
|
||||
{ key: 4, value: '星期三' },
|
||||
{ key: 5, value: '星期四' },
|
||||
{ key: 6, value: '星期五' },
|
||||
{ key: 7, value: '星期六' }
|
||||
])
|
||||
const cycleTotal = computed(() => {
|
||||
cycle01.value = props.check(cycle01.value, 1, 6)
|
||||
cycle02.value = props.check(cycle02.value, cycle01.value + 1, 7)
|
||||
return cycle01.value + '-' + cycle02.value
|
||||
})
|
||||
const averageTotal = computed(() => {
|
||||
average01.value = props.check(average01.value, 1, 4)
|
||||
average02.value = props.check(average02.value, 1, 7)
|
||||
return average02.value + '#' + average01.value
|
||||
})
|
||||
const weekdayTotal = computed(() => {
|
||||
weekday.value = props.check(weekday.value, 1, 7)
|
||||
return weekday.value + 'L'
|
||||
})
|
||||
const checkboxString = computed(() => {
|
||||
return checkboxList.value.join(',')
|
||||
})
|
||||
watch(
|
||||
() => props.cron.week,
|
||||
(value) => changeRadioValue(value)
|
||||
)
|
||||
watch([radioValue, cycleTotal, averageTotal, weekdayTotal, checkboxString], () => onRadioChange())
|
||||
function changeRadioValue(value) {
|
||||
if (value === '*') {
|
||||
radioValue.value = 1
|
||||
} else if (value === '?') {
|
||||
radioValue.value = 2
|
||||
} else if (value.indexOf('-') > -1) {
|
||||
const indexArr = value.split('-')
|
||||
cycle01.value = Number(indexArr[0])
|
||||
cycle02.value = Number(indexArr[1])
|
||||
radioValue.value = 3
|
||||
} else if (value.indexOf('#') > -1) {
|
||||
const indexArr = value.split('#')
|
||||
average01.value = Number(indexArr[1])
|
||||
average02.value = Number(indexArr[0])
|
||||
radioValue.value = 4
|
||||
} else if (value.indexOf('L') > -1) {
|
||||
const indexArr = value.split('L')
|
||||
weekday.value = Number(indexArr[0])
|
||||
radioValue.value = 5
|
||||
} else {
|
||||
checkboxList.value = [...new Set(value.split(',').map((item) => Number(item)))]
|
||||
radioValue.value = 6
|
||||
}
|
||||
}
|
||||
function onRadioChange() {
|
||||
if (radioValue.value === 2 && props.cron.day === '?') {
|
||||
emit('update', 'day', '*', 'week')
|
||||
}
|
||||
if (radioValue.value !== 2 && props.cron.day !== '?') {
|
||||
emit('update', 'day', '?', 'week')
|
||||
}
|
||||
switch (radioValue.value) {
|
||||
case 1:
|
||||
emit('update', 'week', '*', 'week')
|
||||
break
|
||||
case 2:
|
||||
emit('update', 'week', '?', 'week')
|
||||
break
|
||||
case 3:
|
||||
emit('update', 'week', cycleTotal.value, 'week')
|
||||
break
|
||||
case 4:
|
||||
emit('update', 'week', averageTotal.value, 'week')
|
||||
break
|
||||
case 5:
|
||||
emit('update', 'week', weekdayTotal.value, 'week')
|
||||
break
|
||||
case 6:
|
||||
if (checkboxList.value.length === 0) {
|
||||
checkboxList.value.push(checkCopy.value[0])
|
||||
} else {
|
||||
checkCopy.value = checkboxList.value
|
||||
}
|
||||
emit('update', 'week', checkboxString.value, 'week')
|
||||
break
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.el-input-number--small,
|
||||
.el-select,
|
||||
.el-select--small {
|
||||
margin: 0 0.5rem;
|
||||
}
|
||||
.el-select,
|
||||
.el-select--small {
|
||||
width: 8rem;
|
||||
}
|
||||
.el-select.multiselect,
|
||||
.el-select--small.multiselect {
|
||||
width: 17.8rem;
|
||||
}
|
||||
</style>
|
149
src/components/Crontab/year.vue
Normal file
@ -0,0 +1,149 @@
|
||||
<template>
|
||||
<el-form size="small">
|
||||
<el-form-item>
|
||||
<el-radio :label="1" v-model="radioValue"> 不填,允许的通配符[, - * /] </el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio :label="2" v-model="radioValue"> 每年 </el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio :label="3" v-model="radioValue">
|
||||
周期从
|
||||
<el-input-number v-model="cycle01" :min="fullYear" :max="maxFullYear - 1" /> -
|
||||
<el-input-number v-model="cycle02" :min="cycle01 + 1" :max="maxFullYear" />
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio :label="4" v-model="radioValue">
|
||||
从
|
||||
<el-input-number v-model="average01" :min="fullYear" :max="maxFullYear - 1" /> 年开始,每
|
||||
<el-input-number v-model="average02" :min="1" :max="10" /> 年执行一次
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio :label="5" v-model="radioValue">
|
||||
指定
|
||||
<el-select clearable v-model="checkboxList" placeholder="可多选" multiple :multiple-limit="8">
|
||||
<el-option v-for="item in 9" :key="item" :value="item - 1 + fullYear" :label="item - 1 + fullYear" />
|
||||
</el-select>
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const emit = defineEmits(['update'])
|
||||
const props = defineProps({
|
||||
cron: {
|
||||
type: Object,
|
||||
default: {
|
||||
second: '*',
|
||||
min: '*',
|
||||
hour: '*',
|
||||
day: '*',
|
||||
month: '*',
|
||||
week: '?',
|
||||
year: ''
|
||||
}
|
||||
},
|
||||
check: {
|
||||
type: Function,
|
||||
default: () => {}
|
||||
}
|
||||
})
|
||||
const fullYear = ref(0)
|
||||
const maxFullYear = ref(0)
|
||||
const radioValue = ref(1)
|
||||
const cycle01 = ref(0)
|
||||
const cycle02 = ref(0)
|
||||
const average01 = ref(0)
|
||||
const average02 = ref(1)
|
||||
const checkboxList = ref([])
|
||||
const checkCopy = ref([])
|
||||
const cycleTotal = computed(() => {
|
||||
cycle01.value = props.check(cycle01.value, fullYear.value, maxFullYear.value - 1)
|
||||
cycle02.value = props.check(cycle02.value, cycle01.value + 1, maxFullYear.value)
|
||||
return cycle01.value + '-' + cycle02.value
|
||||
})
|
||||
const averageTotal = computed(() => {
|
||||
average01.value = props.check(average01.value, fullYear.value, maxFullYear.value - 1)
|
||||
average02.value = props.check(average02.value, 1, 10)
|
||||
return average01.value + '/' + average02.value
|
||||
})
|
||||
const checkboxString = computed(() => {
|
||||
return checkboxList.value.join(',')
|
||||
})
|
||||
watch(
|
||||
() => props.cron.year,
|
||||
(value) => changeRadioValue(value)
|
||||
)
|
||||
watch([radioValue, cycleTotal, averageTotal, checkboxString], () => onRadioChange())
|
||||
function changeRadioValue(value) {
|
||||
if (value === '') {
|
||||
radioValue.value = 1
|
||||
} else if (value === '*') {
|
||||
radioValue.value = 2
|
||||
} else if (value.indexOf('-') > -1) {
|
||||
const indexArr = value.split('-')
|
||||
cycle01.value = Number(indexArr[0])
|
||||
cycle02.value = Number(indexArr[1])
|
||||
radioValue.value = 3
|
||||
} else if (value.indexOf('/') > -1) {
|
||||
const indexArr = value.split('#')
|
||||
average01.value = Number(indexArr[1])
|
||||
average02.value = Number(indexArr[0])
|
||||
radioValue.value = 4
|
||||
} else {
|
||||
checkboxList.value = [...new Set(value.split(',').map((item) => Number(item)))]
|
||||
radioValue.value = 5
|
||||
}
|
||||
}
|
||||
function onRadioChange() {
|
||||
switch (radioValue.value) {
|
||||
case 1:
|
||||
emit('update', 'year', '', 'year')
|
||||
break
|
||||
case 2:
|
||||
emit('update', 'year', '*', 'year')
|
||||
break
|
||||
case 3:
|
||||
emit('update', 'year', cycleTotal.value, 'year')
|
||||
break
|
||||
case 4:
|
||||
emit('update', 'year', averageTotal.value, 'year')
|
||||
break
|
||||
case 5:
|
||||
if (checkboxList.value.length === 0) {
|
||||
checkboxList.value.push(checkCopy.value[0])
|
||||
} else {
|
||||
checkCopy.value = checkboxList.value
|
||||
}
|
||||
emit('update', 'year', checkboxString.value, 'year')
|
||||
break
|
||||
}
|
||||
}
|
||||
onMounted(() => {
|
||||
fullYear.value = Number(new Date().getFullYear())
|
||||
maxFullYear.value = fullYear.value + 10
|
||||
cycle01.value = fullYear.value
|
||||
cycle02.value = cycle01.value + 1
|
||||
average01.value = fullYear.value
|
||||
checkCopy.value = [fullYear.value]
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.el-input-number--small,
|
||||
.el-select,
|
||||
.el-select--small {
|
||||
margin: 0 0.2rem;
|
||||
}
|
||||
.el-select,
|
||||
.el-select--small {
|
||||
width: 18.8rem;
|
||||
}
|
||||
</style>
|
46
src/components/DictTag/index.vue
Normal file
@ -0,0 +1,46 @@
|
||||
<template>
|
||||
<template v-for="(item, index) in props.options">
|
||||
<template v-if="values.includes(item.dictValue)">
|
||||
<span v-if="item.listClass == 'default' || item.listClass == ''" :key="item.dictValue" :index="index" :class="item.cssClass">
|
||||
{{ item.dictLabel }} <i v-if="showValue">#{{ item.dictValue }}</i>
|
||||
</span>
|
||||
<el-tag
|
||||
size="small"
|
||||
v-else
|
||||
:disable-transitions="true"
|
||||
:index="index"
|
||||
:type="item.listClass == 'primary' ? '' : item.listClass"
|
||||
:class="item.cssClass">
|
||||
{{ item.dictLabel }}
|
||||
<i v-if="showValue">#{{ item.dictValue }}</i>
|
||||
</el-tag>
|
||||
</template>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const props = defineProps({
|
||||
// 数据
|
||||
options: {
|
||||
type: Array,
|
||||
default: null,
|
||||
},
|
||||
// 当前的值
|
||||
value: [Number, String, Array, Boolean],
|
||||
showValue: false,
|
||||
})
|
||||
|
||||
const values = computed(() => {
|
||||
if (props.value !== null && typeof props.value !== 'undefined') {
|
||||
return Array.isArray(props.value) ? props.value : [String(props.value)]
|
||||
} else {
|
||||
return []
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.el-tag + .el-tag {
|
||||
margin-left: 10px;
|
||||
}
|
||||
</style>
|
90
src/components/Echarts/Gauge.vue
Normal file
@ -0,0 +1,90 @@
|
||||
<template>
|
||||
<div ref="chartRef" :class="className" :style="{ height: height, width: width }" />
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import * as echarts from 'echarts'
|
||||
|
||||
// import("echarts/theme/macarons"); // echarts theme
|
||||
// import { debounce } from '@/utils'
|
||||
import { useDebounceFn } from '@vueuse/core'
|
||||
|
||||
const { proxy } = getCurrentInstance()
|
||||
|
||||
const props = defineProps({
|
||||
name: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
min: {
|
||||
type: [Object, Number],
|
||||
default: 0,
|
||||
},
|
||||
max: {
|
||||
type: [Object, Number],
|
||||
default: 0,
|
||||
},
|
||||
data: {
|
||||
type: Array,
|
||||
default: () => [
|
||||
{
|
||||
value: '',
|
||||
name: '占比',
|
||||
},
|
||||
],
|
||||
},
|
||||
className: {
|
||||
type: String,
|
||||
default: 'chart',
|
||||
},
|
||||
width: {
|
||||
type: String,
|
||||
default: '100%',
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
default: '300px',
|
||||
},
|
||||
})
|
||||
let chart = null
|
||||
const chartRef = ref()
|
||||
|
||||
//重绘图表函数
|
||||
const resizeHandler = () => {
|
||||
chart.resize()
|
||||
}
|
||||
function initChart() {
|
||||
chart = echarts.init(proxy.$refs.chartRef, 'macarons')
|
||||
|
||||
chart.setOption({
|
||||
tooltip: {
|
||||
formatter: '{a} <br/>{b} : {c}',
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: props.name,
|
||||
type: 'gauge',
|
||||
min: props.min,
|
||||
max: props.max,
|
||||
detail: {
|
||||
formatter: '{value}%',
|
||||
},
|
||||
data: props.data,
|
||||
},
|
||||
],
|
||||
})
|
||||
}
|
||||
const debouncedFn = useDebounceFn(() => {
|
||||
// do something
|
||||
resizeHandler()
|
||||
}, 500)
|
||||
|
||||
document.addEventListener('resize', debouncedFn)
|
||||
|
||||
onMounted(() => {
|
||||
initChart()
|
||||
})
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener('resize', debouncedFn)
|
||||
})
|
||||
</script>
|
135
src/components/Editor/index.vue
Normal file
@ -0,0 +1,135 @@
|
||||
<template>
|
||||
<div style="border: 1px solid #ccc">
|
||||
<Toolbar style="border-bottom: 1px solid #ccc" :editor="editorRef" :defaultConfig="toolbarConfig" :mode="mode" />
|
||||
<Editor
|
||||
style="height: 300px; overflow-y: hidden"
|
||||
v-model="valueHtml"
|
||||
:defaultConfig="editorConfig"
|
||||
:mode="mode"
|
||||
@onCreated="handleCreated"
|
||||
@onChange="handleChange" />
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import '@wangeditor/editor/dist/css/style.css' // 引入 css
|
||||
import { onBeforeUnmount, ref, shallowRef } from 'vue'
|
||||
import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
|
||||
import { getToken } from '@/utils/auth'
|
||||
import useUserStore from '@/store/modules/user'
|
||||
export default {
|
||||
components: { Editor, Toolbar },
|
||||
props: {
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: () => '请输入内容',
|
||||
},
|
||||
modelValue: String,
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
const editorRef = shallowRef()
|
||||
const valueHtml = ref(props.modelValue)
|
||||
const toolbarConfig = {}
|
||||
const editorConfig = {
|
||||
MENU_CONF: {},
|
||||
placeholder: props.placeholder,
|
||||
}
|
||||
//上传图片
|
||||
editorConfig.MENU_CONF['uploadImage'] = {
|
||||
server: import.meta.env.VITE_APP_BASE_API + '/common/UploadFile',
|
||||
// form-data fieldName ,默认值 'wangeditor-uploaded-image'
|
||||
fieldName: 'file',
|
||||
// 单个文件的最大体积限制,默认为 2M
|
||||
maxFileSize: 1 * 1024 * 1024, // 1M
|
||||
// 最多可上传几个文件,默认为 100
|
||||
maxNumberOfFiles: 10,
|
||||
// 选择文件时的类型限制,默认为 ['image/*'] 。如不想限制,则设置为 []
|
||||
allowedFileTypes: ['image/*'],
|
||||
// 将 meta 拼接到 url 参数中,默认 false
|
||||
metaWithUrl: false,
|
||||
// 自定义增加 http header
|
||||
headers: {
|
||||
Authorization: 'Bearer ' + getToken(),
|
||||
userid: useUserStore().userId,
|
||||
},
|
||||
// 跨域是否传递 cookie ,默认为 false
|
||||
withCredentials: true,
|
||||
// 超时时间,默认为 10 秒
|
||||
timeout: 5 * 1000, // 5 秒
|
||||
// 自定义插入图片
|
||||
customInsert(res, insertFn) {
|
||||
;-(
|
||||
// 从 res 中找到 url alt href ,然后插图图片
|
||||
insertFn(res.data.url)
|
||||
)
|
||||
},
|
||||
}
|
||||
//上传视频
|
||||
editorConfig.MENU_CONF['uploadVideo'] = {
|
||||
server: import.meta.env.VITE_APP_BASE_API + '/common/UploadFile',
|
||||
// form-data fieldName ,默认值 'wangeditor-uploaded-video'
|
||||
fieldName: 'file',
|
||||
|
||||
// 单个文件的最大体积限制,默认为 10M
|
||||
maxFileSize: 5 * 1024 * 1024, // 5M
|
||||
|
||||
// 最多可上传几个文件,默认为 5
|
||||
maxNumberOfFiles: 3,
|
||||
|
||||
// 选择文件时的类型限制,默认为 ['video/*'] 。如不想限制,则设置为 []
|
||||
allowedFileTypes: ['video/*'],
|
||||
|
||||
// 将 meta 拼接到 url 参数中,默认 false
|
||||
metaWithUrl: false,
|
||||
|
||||
// 自定义增加 http header
|
||||
headers: {
|
||||
Authorization: 'Bearer ' + getToken(),
|
||||
userid: useUserStore().userId,
|
||||
},
|
||||
|
||||
// 跨域是否传递 cookie ,默认为 false
|
||||
withCredentials: true,
|
||||
// 超时时间,默认为 30 秒
|
||||
timeout: 15 * 1000, // 15 秒
|
||||
// 自定义插入视频
|
||||
customInsert(res, insertFn) {
|
||||
;-(
|
||||
// 从 res 中找到 url alt href ,然后插图图片
|
||||
insertFn(res.data.url)
|
||||
)
|
||||
},
|
||||
}
|
||||
onBeforeUnmount(() => {
|
||||
const editor = editorRef.value
|
||||
if (editor == null) return
|
||||
editor.destroy()
|
||||
})
|
||||
const handleCreated = (editor) => {
|
||||
editorRef.value = editor
|
||||
}
|
||||
const handleChange = (editor) => {
|
||||
emit('update:modelValue', editor.getHtml())
|
||||
}
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(value) => {
|
||||
const editor = editorRef.value
|
||||
if (value == undefined) {
|
||||
editor.clear()
|
||||
return
|
||||
}
|
||||
valueHtml.value = value;
|
||||
},
|
||||
)
|
||||
return {
|
||||
editorRef,
|
||||
valueHtml,
|
||||
mode: 'default',
|
||||
toolbarConfig,
|
||||
editorConfig,
|
||||
handleCreated,
|
||||
handleChange,
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
254
src/components/FileUpload/index.vue
Normal file
@ -0,0 +1,254 @@
|
||||
<template>
|
||||
<div class="upload-file">
|
||||
<el-upload
|
||||
multiple
|
||||
:action="uploadFileUrl"
|
||||
:before-upload="handleBeforeUpload"
|
||||
v-model:file-list="fileList"
|
||||
:limit="limit"
|
||||
:on-error="handleUploadError"
|
||||
:on-exceed="handleExceed"
|
||||
:on-success="handleUploadSuccess"
|
||||
:show-file-list="false"
|
||||
:data="uploadData"
|
||||
:drag="drag"
|
||||
:headers="headers"
|
||||
:auto-upload="autoUpload"
|
||||
class="upload-file-uploader"
|
||||
ref="fileUpload">
|
||||
<!-- 拖拽上传 -->
|
||||
<template v-if="drag">
|
||||
<el-icon class="el-icon--upload">
|
||||
<upload-filled />
|
||||
</el-icon>
|
||||
<div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
|
||||
</template>
|
||||
<!-- 上传按钮 -->
|
||||
<el-button type="primary" icon="upload" v-if="!drag">选取文件</el-button>
|
||||
<!-- 上传提示 -->
|
||||
<template #tip>
|
||||
<div class="el-upload__tip" v-if="showTip">
|
||||
请上传
|
||||
<template v-if="fileSize">
|
||||
大小不超过 <b style="color: #f56c6c">{{ fileSize }}MB</b>
|
||||
</template>
|
||||
<template v-if="fileType && fileType.length > 0">
|
||||
格式为 <b style="color: #f56c6c">{{ fileType.join('/') }}</b>
|
||||
</template>
|
||||
的文件
|
||||
</div>
|
||||
</template>
|
||||
</el-upload>
|
||||
<!-- 文件列表 -->
|
||||
<transition-group class="upload-file-list el-upload-list el-upload-list--text" name="el-fade-in-linear" tag="ul">
|
||||
<li :key="file.uid" class="el-upload-list__item ele-upload-list__item-content" v-for="(file, index) in fileList">
|
||||
<el-link :href="`${file.url}`" :underline="false" target="_blank">
|
||||
<svg-icon class-name="doc-icon" name="documentation" />
|
||||
{{ file.name }}
|
||||
</el-link>
|
||||
<div class="ele-upload-list__item-content-action">
|
||||
<el-link :underline="false" @click="handleDelete(index)" type="danger">删除</el-link>
|
||||
</div>
|
||||
</li>
|
||||
</transition-group>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { getToken } from '@/utils/auth'
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: [String, Object, Array],
|
||||
// 数量限制
|
||||
limit: {
|
||||
type: Number,
|
||||
default: 5
|
||||
},
|
||||
// 大小限制(MB)
|
||||
fileSize: {
|
||||
type: Number,
|
||||
default: 5
|
||||
},
|
||||
// 文件类型, 例如['png', 'jpg', 'jpeg']
|
||||
fileType: {
|
||||
type: Array,
|
||||
default: () => ['doc', 'xls', 'ppt', 'txt', 'pdf']
|
||||
},
|
||||
// 是否显示提示
|
||||
isShowTip: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 是否拖拽
|
||||
drag: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 自动上传
|
||||
autoUpload: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 上传携带的参数
|
||||
data: {
|
||||
type: Object
|
||||
}
|
||||
})
|
||||
|
||||
const { proxy } = getCurrentInstance()
|
||||
const emit = defineEmits()
|
||||
const number = ref(0)
|
||||
const uploadList = ref([])
|
||||
const baseUrl = import.meta.env.VITE_APP_BASE_API
|
||||
const uploadFileUrl = ref(baseUrl + import.meta.env.VITE_APP_UPLOAD_URL) // 上传的图片服务器地址
|
||||
const headers = ref({ Authorization: 'Bearer ' + getToken() })
|
||||
const fileList = ref([])
|
||||
const showTip = computed(() => props.isShowTip && (props.fileType || props.fileSize))
|
||||
const uploadData = computed(() => props.data)
|
||||
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(val) => {
|
||||
if (val) {
|
||||
let temp = 1
|
||||
// 首先将值转为数组
|
||||
const list = Array.isArray(val) ? val : props.modelValue.split(',')
|
||||
// 然后将数组转为对象数组
|
||||
fileList.value = list.map((item) => {
|
||||
var fileName = item.slice(item.lastIndexOf('/') + 1)
|
||||
if (typeof item === 'string') {
|
||||
item = { name: fileName, url: item }
|
||||
}
|
||||
item.uid = item.uid || new Date().getTime() + temp++
|
||||
return item
|
||||
})
|
||||
} else {
|
||||
fileList.value = []
|
||||
return []
|
||||
}
|
||||
},
|
||||
{ deep: true, immediate: true }
|
||||
)
|
||||
|
||||
// 上传前校检格式和大小
|
||||
function handleBeforeUpload(file) {
|
||||
// 校检文件类型
|
||||
if (props.fileType.length) {
|
||||
let fileExtension = ''
|
||||
if (file.name.lastIndexOf('.') > -1) {
|
||||
fileExtension = file.name.slice(file.name.lastIndexOf('.') + 1)
|
||||
}
|
||||
const isTypeOk = props.fileType.some((type) => {
|
||||
if (file.type.indexOf(type) > -1) return true
|
||||
if (fileExtension && fileExtension.indexOf(type) > -1) return true
|
||||
return false
|
||||
})
|
||||
if (!isTypeOk) {
|
||||
proxy.$modal.msgError(`文件格式不正确, 请上传${props.fileType.join('/')}格式文件!`)
|
||||
return false
|
||||
}
|
||||
}
|
||||
// 校检文件大小
|
||||
if (props.fileSize) {
|
||||
const isLt = file.size / 1024 / 1024 < props.fileSize
|
||||
if (!isLt) {
|
||||
proxy.$modal.msgError(`上传文件大小不能超过 ${props.fileSize} MB!`)
|
||||
return false
|
||||
}
|
||||
}
|
||||
proxy.$modal.loading('正在上传文件,请稍候...')
|
||||
number.value++
|
||||
return true
|
||||
}
|
||||
|
||||
// 文件个数超出
|
||||
function handleExceed() {
|
||||
proxy.$modal.msgError(`上传文件数量不能超过 ${props.limit} 个!`)
|
||||
}
|
||||
|
||||
// 上传失败
|
||||
function handleUploadError(err) {
|
||||
proxy.$modal.msgError('上传失败')
|
||||
proxy.$modal.closeLoading()
|
||||
}
|
||||
|
||||
// 上传成功回调
|
||||
function handleUploadSuccess(response, uploadFile) {
|
||||
if (response.code != 200) {
|
||||
fileList.value = []
|
||||
proxy.$modal.msgError(`上传失败,原因:${response.msg}!`)
|
||||
proxy.$modal.closeLoading()
|
||||
return
|
||||
}
|
||||
const { fileName, url, fileId } = response.data
|
||||
const tempFile = { name: fileName, url: url }
|
||||
uploadList.value.push(tempFile)
|
||||
if (number.value > 0 && uploadList.value.length === number.value) {
|
||||
fileList.value = fileList.value.filter((f) => f.url !== undefined).concat(uploadList.value)
|
||||
uploadList.value = []
|
||||
number.value = 0
|
||||
emit('update:modelValue', listToString(fileList.value))
|
||||
emit('success', listToString(fileList.value))
|
||||
proxy.$modal.closeLoading()
|
||||
}
|
||||
}
|
||||
|
||||
// 删除文件
|
||||
function handleDelete(index) {
|
||||
fileList.value.splice(index, 1)
|
||||
//emit('update:modelValue', listToString(fileList.value))
|
||||
}
|
||||
|
||||
// 获取文件名称
|
||||
function getFileName(name) {
|
||||
if (name.lastIndexOf('/') > -1) {
|
||||
return name.slice(name.lastIndexOf('/') + 1).toLowerCase()
|
||||
} else {
|
||||
return ''
|
||||
}
|
||||
}
|
||||
|
||||
// 对象转成指定字符串分隔
|
||||
function listToString(list, separator) {
|
||||
let strs = ''
|
||||
separator = separator || ','
|
||||
|
||||
list.forEach((element) => {
|
||||
if (element) {
|
||||
strs += element.url + separator
|
||||
}
|
||||
})
|
||||
return strs != '' ? strs.substr(0, strs.length - 1) : ''
|
||||
}
|
||||
// 手动提交上传
|
||||
function submitUpload() {
|
||||
proxy.$refs.fileUpload.submit()
|
||||
}
|
||||
defineExpose({
|
||||
submitUpload
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.upload-file-uploader {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.upload-file-list .el-upload-list__item {
|
||||
border: 1px solid #e4e7ed;
|
||||
line-height: 2;
|
||||
margin-bottom: 10px;
|
||||
position: relative;
|
||||
}
|
||||
.upload-file-list .ele-upload-list__item-content {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
color: inherit;
|
||||
}
|
||||
.ele-upload-list__item-content-action .el-link {
|
||||
margin-right: 10px;
|
||||
}
|
||||
.doc-icon {
|
||||
margin: 0 10px;
|
||||
}
|
||||
</style>
|
42
src/components/Hamburger/index.vue
Normal file
@ -0,0 +1,42 @@
|
||||
<template>
|
||||
<div style="padding: 0 15px;" @click="toggleClick">
|
||||
<svg
|
||||
:class="{'is-active':isActive}"
|
||||
class="hamburger"
|
||||
viewBox="0 0 1024 1024"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="64"
|
||||
height="64"
|
||||
>
|
||||
<path d="M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM142.4 642.1L298.7 519a8.84 8.84 0 0 0 0-13.9L142.4 381.9c-5.8-4.6-14.4-.5-14.4 6.9v246.3a8.9 8.9 0 0 0 14.4 7z" />
|
||||
</svg>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
defineProps({
|
||||
isActive: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits()
|
||||
const toggleClick = () => {
|
||||
emit('toggleClick');
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.hamburger {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
fill: var(--base-color-white);
|
||||
}
|
||||
|
||||
.hamburger.is-active {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
</style>
|
223
src/components/HeaderSearch/index.vue
Normal file
@ -0,0 +1,223 @@
|
||||
<template>
|
||||
<div class="header-search">
|
||||
<svg-icon class-name="search-icon" name="search" @click.stop="click" />
|
||||
<el-dialog v-model="open" width="500" @close="close">
|
||||
<el-select
|
||||
style="width: 100%"
|
||||
ref="headerSearchSelectRef"
|
||||
size="large"
|
||||
v-model="search"
|
||||
:remote-method="querySearch"
|
||||
filterable
|
||||
default-first-option
|
||||
remote
|
||||
popper-class="header-search-select"
|
||||
placement="bottom"
|
||||
placeholder="菜单搜索,支持标题、URL模糊查询"
|
||||
@change="change">
|
||||
<template #prefix>
|
||||
<el-icon color="#409EFC" class="no-inherit">
|
||||
<Search />
|
||||
</el-icon>
|
||||
</template>
|
||||
<el-option v-for="option in options" :key="option.item.path" :value="option.item" :label="option.item.title.join(' > ')">
|
||||
<span style="float: left">
|
||||
<div>
|
||||
{{ option.item.title.join(' > ') }}
|
||||
<div class="path">{{ option.item.path }}</div>
|
||||
</div>
|
||||
</span>
|
||||
<span style="float: right" @click.stop="handleLove(option.item)"> <svg-icon color="#ccc" name="star" /></span>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import Fuse from 'fuse.js'
|
||||
import { getNormalPath } from '@/utils/ruoyi'
|
||||
import { isHttp } from '@/utils/validate'
|
||||
import usePermissionStore from '@/store/modules/permission'
|
||||
import { findItem, color16 } from '@/utils/ruoyi'
|
||||
const { proxy } = getCurrentInstance()
|
||||
const search = ref('')
|
||||
const options = ref([])
|
||||
const searchPool = ref([])
|
||||
const show = ref(false)
|
||||
const open = ref(false)
|
||||
const fuse = ref(undefined)
|
||||
const headerSearchSelectRef = ref(null)
|
||||
const router = useRouter()
|
||||
const routes = computed(() => usePermissionStore().routes)
|
||||
|
||||
function click() {
|
||||
open.value = !open.value
|
||||
show.value = !show.value
|
||||
if (open.value) {
|
||||
setTimeout(() => {
|
||||
headerSearchSelectRef.value && headerSearchSelectRef.value.focus()
|
||||
}, 1)
|
||||
}
|
||||
}
|
||||
function close() {
|
||||
headerSearchSelectRef.value && headerSearchSelectRef.value.blur()
|
||||
options.value = []
|
||||
show.value = false
|
||||
open.value = false
|
||||
}
|
||||
function change(val) {
|
||||
const path = val.path
|
||||
if (isHttp(path)) {
|
||||
// http(s):// 路径新窗口打开
|
||||
const pindex = path.indexOf('http')
|
||||
window.open(path.substr(pindex, path.length), '_blank')
|
||||
} else {
|
||||
router.push(path)
|
||||
}
|
||||
|
||||
search.value = ''
|
||||
options.value = []
|
||||
nextTick(() => {
|
||||
show.value = false
|
||||
open.value = false
|
||||
})
|
||||
}
|
||||
function initFuse(list) {
|
||||
fuse.value = new Fuse(list, {
|
||||
shouldSort: true,
|
||||
threshold: 0.4,
|
||||
location: 0,
|
||||
distance: 100,
|
||||
minMatchCharLength: 1,
|
||||
keys: [
|
||||
{
|
||||
name: 'title',
|
||||
weight: 0.7
|
||||
},
|
||||
{
|
||||
name: 'path',
|
||||
weight: 0.3
|
||||
}
|
||||
]
|
||||
})
|
||||
}
|
||||
// Filter out the routes that can be displayed in the sidebar
|
||||
// And generate the internationalized title
|
||||
function generateRoutes(routes, basePath = '', prefixTitle = []) {
|
||||
let res = []
|
||||
|
||||
for (const r of routes) {
|
||||
// skip hidden router
|
||||
if (r.hidden) {
|
||||
continue
|
||||
}
|
||||
const p = r.path.length > 0 && r.path[0] === '/' ? r.path : '/' + r.path
|
||||
const data = {
|
||||
path: !isHttp(r.path) ? getNormalPath(basePath + p) : r.path,
|
||||
title: [...prefixTitle],
|
||||
icon: 'menu',
|
||||
menuTitle: ''
|
||||
}
|
||||
|
||||
if (r.meta && r.meta.title) {
|
||||
data.title = [...data.title, r.meta.title]
|
||||
data.icon = r.meta.icon
|
||||
data.menuTitle = r.meta.title
|
||||
if (r.redirect !== 'noRedirect') {
|
||||
// only push the routes with title
|
||||
// special case: need to exclude parent router without redirect
|
||||
res.push(data)
|
||||
}
|
||||
}
|
||||
|
||||
// recursive child routes
|
||||
if (r.children) {
|
||||
const tempRoutes = generateRoutes(r.children, data.path, data.title)
|
||||
if (tempRoutes.length >= 1) {
|
||||
res = [...res, ...tempRoutes]
|
||||
}
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
function querySearch(query) {
|
||||
if (query !== '') {
|
||||
options.value = fuse.value.search(query)
|
||||
} else {
|
||||
options.value = []
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 添加快捷菜单
|
||||
* @param {*} item
|
||||
*/
|
||||
function handleLove(item) {
|
||||
var arraryObjectLocal = proxy.$cache.local.getJSON('commonlyUseMenu') || []
|
||||
|
||||
var len = 12
|
||||
if (arraryObjectLocal.length >= len) {
|
||||
proxy.$modal.msgError(`最多可添加${len}个常用菜单`)
|
||||
return
|
||||
}
|
||||
let index = findItem(arraryObjectLocal, 'path', item.path)
|
||||
if (index <= -1) {
|
||||
arraryObjectLocal.push({ ...item, color: color16() })
|
||||
proxy.$cache.local.setJSON('commonlyUseMenu', arraryObjectLocal)
|
||||
proxy.$modal.msgSuccess('添加成功')
|
||||
usePermissionStore().setCommonlyUsedRoutes()
|
||||
} else {
|
||||
proxy.$modal.msgError('该菜单已存在')
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
searchPool.value = generateRoutes(routes.value)
|
||||
})
|
||||
|
||||
watchEffect(() => {
|
||||
searchPool.value = generateRoutes(routes.value)
|
||||
})
|
||||
|
||||
watch(show, (value) => {
|
||||
if (value) {
|
||||
document.body.addEventListener('click', close)
|
||||
} else {
|
||||
document.body.removeEventListener('click', close)
|
||||
}
|
||||
})
|
||||
|
||||
watch(searchPool, (list) => {
|
||||
initFuse(list)
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.header-search {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
:deep(.el-dialog) {
|
||||
.el-dialog__header {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
--el-dialog-bg-color: #00000;
|
||||
.el-dialog__body {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.search-icon {
|
||||
cursor: pointer;
|
||||
font-size: 18px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
.path {
|
||||
color: #ccc;
|
||||
font-size: 10px;
|
||||
}
|
||||
</style>
|
101
src/components/IconSelect/index.vue
Normal file
@ -0,0 +1,101 @@
|
||||
<template>
|
||||
<div class="icon-body">
|
||||
<el-input v-model="iconName" style="position: relative" clearable placeholder="请输入图标名称" @clear="filterIcons" @input="filterIcons">
|
||||
<template #prefix>
|
||||
<el-icon class="el-input__icon">
|
||||
<search />
|
||||
</el-icon>
|
||||
</template>
|
||||
<template #suffix>
|
||||
<el-icon class="el-input__icon" @click="selectedIcon('')">
|
||||
<delete />
|
||||
</el-icon>
|
||||
</template>
|
||||
</el-input>
|
||||
<el-tabs v-model="activeName">
|
||||
<el-tab-pane label="svg-icon" name="1">
|
||||
<div class="icon-list">
|
||||
<div class="icon-item mb10" v-for="(item, index) in iconList" :key="index" @click="selectedIcon(item, '')">
|
||||
<svg-icon :name="item" style="height: 30px; width: 16px" />
|
||||
<div class="name">{{ item }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="Element-UI Icons" name="2">
|
||||
<div class="icon-list">
|
||||
<div class="icon-item mb10" v-for="item of elementIcons" :key="item" @click="selectedIcon(item, 'ele-')">
|
||||
<svg-icon :name="'ele-' + item" style="height: 30px; width: 16px" />
|
||||
<div class="name">{{ item }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import icons from './requireIcons'
|
||||
import * as elIcons from '@element-plus/icons-vue'
|
||||
|
||||
const elementIcons = ref([])
|
||||
for (const key in elIcons) {
|
||||
elementIcons.value.push(key)
|
||||
}
|
||||
const iconName = ref('')
|
||||
const iconList = ref(icons)
|
||||
const activeName = ref('1')
|
||||
const emit = defineEmits(['selected'])
|
||||
|
||||
function filterIcons() {
|
||||
iconList.value = icons
|
||||
if (iconName.value) {
|
||||
iconList.value = icons.filter((item) => item.indexOf(iconName.value) !== -1)
|
||||
}
|
||||
}
|
||||
|
||||
function selectedIcon(name, prefix) {
|
||||
const iconName = prefix != undefined ? prefix + name : name
|
||||
console.log(iconName)
|
||||
emit('selected', iconName)
|
||||
document.body.click()
|
||||
}
|
||||
|
||||
function reset() {
|
||||
iconName.value = ''
|
||||
iconList.value = icons
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
reset,
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.icon-body {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
.icon-list {
|
||||
overflow-y: scroll;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-around;
|
||||
height: 200px;
|
||||
|
||||
.icon-item {
|
||||
// height: 30px;
|
||||
// line-height: 30px;
|
||||
// margin-bottom: -5px;
|
||||
cursor: pointer;
|
||||
width: 19%;
|
||||
text-align: center;
|
||||
// float: left;
|
||||
}
|
||||
.name {
|
||||
// display: inline-block;
|
||||
// vertical-align: -0.15em;
|
||||
// fill: currentColor;
|
||||
// overflow: hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
14
src/components/IconSelect/requireIcons.js
Normal file
@ -0,0 +1,14 @@
|
||||
let icons = []
|
||||
const modules = import.meta.glob('./../../assets/icons/svg/*.svg')
|
||||
for (const path in modules) {
|
||||
const p = path.split('assets/icons/svg/')[1].split('.svg')[0]
|
||||
icons.push(p)
|
||||
}
|
||||
|
||||
// iconfont
|
||||
import iconList from '@/assets/iconfont/iconfont.json'
|
||||
|
||||
iconList.glyphs.forEach((element) => {
|
||||
icons.push(element.name)
|
||||
})
|
||||
export default icons
|
85
src/components/ImagePreview/index.vue
Normal file
@ -0,0 +1,85 @@
|
||||
<template>
|
||||
<el-image
|
||||
:src="`${realSrc}`"
|
||||
fit="cover"
|
||||
:style="`width:${realWidth};height:${realHeight};`"
|
||||
:preview-src-list="realSrcList"
|
||||
>
|
||||
<template #error>
|
||||
<div class="image-slot">
|
||||
<el-icon><picture-filled /></el-icon>
|
||||
</div>
|
||||
</template>
|
||||
</el-image>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { isExternal } from "@/utils/validate";
|
||||
|
||||
const props = defineProps({
|
||||
src: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
width: {
|
||||
type: [Number, String],
|
||||
default: ""
|
||||
},
|
||||
height: {
|
||||
type: [Number, String],
|
||||
default: ""
|
||||
}
|
||||
});
|
||||
|
||||
const realSrc = computed(() => {
|
||||
let real_src = props.src.split(",")[0];
|
||||
if (isExternal(real_src)) {
|
||||
return real_src;
|
||||
}
|
||||
return import.meta.env.VITE_APP_BASE_API + real_src;
|
||||
});
|
||||
|
||||
const realSrcList = computed(() => {
|
||||
let real_src_list = props.src.split(",");
|
||||
let srcList = [];
|
||||
real_src_list.forEach(item => {
|
||||
if (isExternal(item)) {
|
||||
return srcList.push(item);
|
||||
}
|
||||
return srcList.push(import.meta.env.VITE_APP_BASE_API + item);
|
||||
});
|
||||
return srcList;
|
||||
});
|
||||
|
||||
const realWidth = computed(() =>
|
||||
typeof props.width == "string" ? props.width : `${props.width}px`
|
||||
);
|
||||
|
||||
const realHeight = computed(() =>
|
||||
typeof props.height == "string" ? props.height : `${props.height}px`
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.el-image {
|
||||
border-radius: 5px;
|
||||
background-color: #ebeef5;
|
||||
box-shadow: 0 0 5px 1px #ccc;
|
||||
:deep(.el-image__inner) {
|
||||
transition: all 0.3s;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
transform: scale(1.2);
|
||||
}
|
||||
}
|
||||
:deep(.image-slot) {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
color: #909399;
|
||||
font-size: 30px;
|
||||
}
|
||||
}
|
||||
</style>
|
196
src/components/ImageUpload/index.vue
Normal file
@ -0,0 +1,196 @@
|
||||
<template>
|
||||
<div class="component-upload-image">
|
||||
<el-upload multiple :action="uploadImgUrl" list-type="picture-card" :on-success="handleUploadSuccess"
|
||||
:before-upload="handleBeforeUpload" :limit="limit" :on-error="handleUploadError" :on-exceed="handleExceed"
|
||||
name="file" :data="data" :drag="drag" :on-remove="handleRemove" :show-file-list="true" :headers="headers"
|
||||
v-model:file-list="fileList" :on-preview="handlePictureCardPreview" :class="{ hide: fileList.length >= limit }">
|
||||
<el-icon class="avatar-uploader-icon">
|
||||
<plus />
|
||||
</el-icon>
|
||||
</el-upload>
|
||||
<!-- 上传提示 -->
|
||||
<div class="el-upload__tip" v-if="showTip">
|
||||
请上传
|
||||
<template v-if="fileSize">
|
||||
大小不超过 <b style="color: #f56c6c">{{ fileSize }}MB</b>
|
||||
</template>
|
||||
<template v-if="fileType">
|
||||
格式为 <b style="color: #f56c6c">{{ fileType.join('/') }}</b>
|
||||
</template>
|
||||
的文件
|
||||
</div>
|
||||
|
||||
<el-dialog v-model="dialogVisible" title="预览" width="800px" append-to-body>
|
||||
<img :src="dialogImageUrl" style="display: block; max-width: 100%; margin: 0 auto" />
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { getToken } from '@/utils/auth'
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: [String, Object, Array],
|
||||
// 图片数量限制
|
||||
limit: {
|
||||
type: Number,
|
||||
default: 5,
|
||||
},
|
||||
// 大小限制(MB)
|
||||
fileSize: {
|
||||
type: Number,
|
||||
default: 5,
|
||||
},
|
||||
// 文件类型, 例如['png', 'jpg', 'jpeg']
|
||||
fileType: {
|
||||
type: Array,
|
||||
default: () => ['png', 'jpg', 'jpeg'],
|
||||
},
|
||||
// 是否显示提示
|
||||
isShowTip: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
// 上传携带的参数
|
||||
data: {
|
||||
type: Object,
|
||||
},
|
||||
// 是否可以拖拽
|
||||
drag: {
|
||||
type: Boolean,
|
||||
},
|
||||
})
|
||||
|
||||
const { proxy } = getCurrentInstance()
|
||||
const emit = defineEmits()
|
||||
const number = ref(0)
|
||||
const uploadList = ref([])
|
||||
const dialogImageUrl = ref('')
|
||||
const dialogVisible = ref(false)
|
||||
const baseUrl = import.meta.env.VITE_APP_BASE_API
|
||||
const uploadImgUrl = ref(baseUrl + import.meta.env.VITE_APP_UPLOAD_URL) // 上传的图片服务器地址
|
||||
const headers = ref({ Authorization: 'Bearer ' + getToken() })
|
||||
const fileList = ref([])
|
||||
const showTip = computed(() => props.isShowTip && (props.fileType || props.fileSize))
|
||||
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(val) => {
|
||||
if (val) {
|
||||
// 首先将值转为数组
|
||||
const list = Array.isArray(val) ? val : props.modelValue.split(',')
|
||||
// 然后将数组转为对象数组
|
||||
fileList.value = list.map((item) => {
|
||||
if (typeof item === 'string') {
|
||||
// if (item.indexOf(baseUrl) === -1) {
|
||||
// item = { name: baseUrl + item, url: baseUrl + item }
|
||||
// } else {
|
||||
item = { name: item, url: item }
|
||||
// }
|
||||
}
|
||||
return item
|
||||
})
|
||||
} else {
|
||||
fileList.value = []
|
||||
return []
|
||||
}
|
||||
},
|
||||
{ deep: true, immediate: true },
|
||||
)
|
||||
|
||||
// 删除图片
|
||||
function handleRemove(file, files) {
|
||||
emit('update:modelValue', listToString(fileList.value))
|
||||
}
|
||||
|
||||
// 上传成功回调
|
||||
function handleUploadSuccess(res) {
|
||||
if (res.code != 200) {
|
||||
proxy.$modal.msgError(`上传失败,原因:${res.msg}!`)
|
||||
proxy.$modal.closeLoading()
|
||||
fileList.value = fileList.value.slice(0, fileList.value.length - 1)
|
||||
return
|
||||
}
|
||||
uploadList.value.push({ name: res.data.fileName, url: res.data.url })
|
||||
if (uploadList.value.length === number.value) {
|
||||
fileList.value = fileList.value.filter((f) => f.url !== undefined).concat(uploadList.value)
|
||||
uploadList.value = []
|
||||
number.value = 0
|
||||
emit('update:modelValue', listToString(fileList.value))
|
||||
}
|
||||
proxy.$modal.closeLoading()
|
||||
}
|
||||
|
||||
// 上传前loading加载
|
||||
function handleBeforeUpload(file) {
|
||||
let isImg = false
|
||||
if (props.fileType.length) {
|
||||
let fileExtension = ''
|
||||
if (file.name.lastIndexOf('.') > -1) {
|
||||
fileExtension = file.name.slice(file.name.lastIndexOf('.') + 1)
|
||||
}
|
||||
isImg = props.fileType.some((type) => {
|
||||
if (file.type.indexOf(type) > -1) return true
|
||||
if (fileExtension && fileExtension.indexOf(type) > -1) return true
|
||||
return false
|
||||
})
|
||||
} else {
|
||||
isImg = file.type.indexOf('image') > -1
|
||||
}
|
||||
if (!isImg) {
|
||||
proxy.$modal.msgError(`文件格式不正确, 请上传${props.fileType.join('/')}图片格式文件!`)
|
||||
return false
|
||||
}
|
||||
if (props.fileSize) {
|
||||
const isLt = file.size / 1024 / 1024 < props.fileSize
|
||||
if (!isLt) {
|
||||
proxy.$modal.msgError(`上传头像图片大小不能超过 ${props.fileSize} MB!`)
|
||||
return false
|
||||
}
|
||||
}
|
||||
proxy.$modal.loading('正在上传图片,请稍候...')
|
||||
number.value++
|
||||
}
|
||||
|
||||
// 文件个数超出
|
||||
function handleExceed() {
|
||||
proxy.$modal.msgError(`上传文件数量不能超过 ${props.limit} 个!`)
|
||||
}
|
||||
|
||||
// 上传失败
|
||||
function handleUploadError() {
|
||||
proxy.$modal.msgError('上传图片失败')
|
||||
proxy.$modal.closeLoading()
|
||||
}
|
||||
|
||||
// 预览
|
||||
function handlePictureCardPreview(file) {
|
||||
dialogImageUrl.value = file.url
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
// 对象转成指定字符串分隔
|
||||
function listToString(list, separator) {
|
||||
let strs = ''
|
||||
separator = separator || ','
|
||||
for (let i in list) {
|
||||
if (undefined !== list[i].url && list[i].url.indexOf('blob:') !== 0) {
|
||||
strs += list[i].url + separator
|
||||
}
|
||||
}
|
||||
return strs != '' ? strs.substr(0, strs.length - 1) : ''
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep(.hide .el-upload--picture-card) {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
:deep(.el-upload-dragger) {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
</style>
|