feat: first-commit
@@ -0,0 +1,15 @@
|
||||
# @see: http://editorconfig.org
|
||||
|
||||
root = true
|
||||
|
||||
[*] # 表示所有文件适用
|
||||
charset = utf-8 # 设置文件字符集为 utf-8
|
||||
end_of_line = lf # 控制换行类型(lf | cr | crlf)
|
||||
insert_final_newline = true # 始终在文件末尾插入一个新行
|
||||
indent_style = tab # 缩进风格(tab | space)
|
||||
indent_size = 2 # 缩进大小
|
||||
max_line_length = 130 # 最大行长度
|
||||
|
||||
[*.md] # 表示仅 md 文件适用以下规则
|
||||
max_line_length = off # 关闭最大行长度限制
|
||||
trim_trailing_whitespace = false # 关闭末尾空格修剪
|
||||
@@ -0,0 +1,18 @@
|
||||
# title
|
||||
VITE_GLOB_APP_TITLE = '1Panel'
|
||||
|
||||
# port
|
||||
VITE_PORT = 4004
|
||||
|
||||
# open 运行 npm run dev 时自动打开浏览器
|
||||
VITE_OPEN = true
|
||||
|
||||
# 是否生成包预览文件
|
||||
VITE_REPORT = false
|
||||
|
||||
# 是否开启gzip压缩
|
||||
VITE_BUILD_GZIP = false
|
||||
|
||||
# 是否删除生产环境 console
|
||||
VITE_DROP_CONSOLE = true
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
# 本地环境
|
||||
NODE_ENV = 'development'
|
||||
|
||||
# 本地环境接口地址
|
||||
VITE_API_URL = '/api/v1'
|
||||
@@ -0,0 +1,5 @@
|
||||
# 线上环境
|
||||
NODE_ENV = "production"
|
||||
|
||||
# 线上环境接口地址(easymock)
|
||||
VITE_API_URL = "https://mock.mengxuegu.com/mock/629d727e6163854a32e8307e"
|
||||
@@ -0,0 +1,17 @@
|
||||
*.sh
|
||||
node_modules
|
||||
*.md
|
||||
*.woff
|
||||
*.ttf
|
||||
.vscode
|
||||
.idea
|
||||
dist
|
||||
/public
|
||||
/docs
|
||||
.husky
|
||||
.local
|
||||
/bin
|
||||
.eslintrc.js
|
||||
.prettierrc.js
|
||||
/src/mock/*
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
// @see: http://eslint.cn
|
||||
|
||||
module.exports = {
|
||||
root: true,
|
||||
env: {
|
||||
browser: true,
|
||||
node: true,
|
||||
es6: true,
|
||||
},
|
||||
/* 指定如何解析语法 */
|
||||
parser: 'vue-eslint-parser',
|
||||
/* 优先级低于 parse 的语法解析配置 */
|
||||
parserOptions: {
|
||||
parser: '@typescript-eslint/parser',
|
||||
ecmaVersion: 2020,
|
||||
sourceType: 'module',
|
||||
jsxPragma: 'React',
|
||||
ecmaFeatures: {
|
||||
jsx: true,
|
||||
},
|
||||
},
|
||||
/* 继承某些已有的规则 */
|
||||
extends: [
|
||||
'plugin:vue/vue3-recommended',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'prettier',
|
||||
'plugin:prettier/recommended',
|
||||
],
|
||||
/*
|
||||
* "off" 或 0 ==> 关闭规则
|
||||
* "warn" 或 1 ==> 打开的规则作为警告(不影响代码执行)
|
||||
* "error" 或 2 ==> 规则作为一个错误(代码不能执行,界面报错)
|
||||
*/
|
||||
rules: {
|
||||
// eslint (http://eslint.cn/docs/rules)
|
||||
'no-var': 'error', // 要求使用 let 或 const 而不是 var
|
||||
'no-multiple-empty-lines': ['error', { max: 1 }], // 不允许多个空行
|
||||
'no-use-before-define': 'off', // 禁止在 函数/类/变量 定义之前使用它们
|
||||
'prefer-const': 'off', // 此规则旨在标记使用 let 关键字声明但在初始分配后从未重新分配的变量,要求使用 const
|
||||
'no-irregular-whitespace': 'off', // 禁止不规则的空白
|
||||
|
||||
// typeScript (https://typescript-eslint.io/rules)
|
||||
'@typescript-eslint/no-unused-vars': 'error', // 禁止定义未使用的变量
|
||||
'@typescript-eslint/no-inferrable-types': 'off', // 可以轻松推断的显式类型可能会增加不必要的冗长
|
||||
'@typescript-eslint/no-namespace': 'off', // 禁止使用自定义 TypeScript 模块和命名空间。
|
||||
'@typescript-eslint/no-explicit-any': 'off', // 禁止使用 any 类型
|
||||
'@typescript-eslint/ban-ts-ignore': 'off', // 禁止使用 @ts-ignore
|
||||
'@typescript-eslint/ban-types': 'off', // 禁止使用特定类型
|
||||
'@typescript-eslint/explicit-function-return-type': 'off', // 不允许对初始化为数字、字符串或布尔值的变量或参数进行显式类型声明
|
||||
'@typescript-eslint/no-var-requires': 'off', // 不允许在 import 语句中使用 require 语句
|
||||
'@typescript-eslint/no-empty-function': 'off', // 禁止空函数
|
||||
'@typescript-eslint/no-use-before-define': 'off', // 禁止在变量定义之前使用它们
|
||||
'@typescript-eslint/ban-ts-comment': 'off', // 禁止 @ts-<directive> 使用注释或要求在指令后进行描述
|
||||
'@typescript-eslint/no-non-null-assertion': 'off', // 不允许使用后缀运算符的非空断言(!)
|
||||
'@typescript-eslint/explicit-module-boundary-types': 'off', // 要求导出函数和类的公共类方法的显式返回和参数类型
|
||||
|
||||
// vue (https://eslint.vuejs.org/rules)
|
||||
'vue/no-v-html': 'off', // 禁止使用 v-html
|
||||
'vue/script-setup-uses-vars': 'error', // 防止<script setup>使用的变量<template>被标记为未使用,此规则仅在启用该no-unused-vars规则时有效。
|
||||
'vue/v-slot-style': 'error', // 强制执行 v-slot 指令样式
|
||||
'vue/no-mutating-props': 'off', // 不允许组件 prop的改变(明天找原因)
|
||||
'vue/custom-event-name-casing': 'off', // 为自定义事件名称强制使用特定大小写
|
||||
'vue/attributes-order': 'off', // vue api使用顺序,强制执行属性顺序
|
||||
'vue/one-component-per-file': 'off', // 强制每个组件都应该在自己的文件中
|
||||
'vue/html-closing-bracket-newline': 'off', // 在标签的右括号之前要求或禁止换行
|
||||
'vue/max-attributes-per-line': 'off', // 强制每行的最大属性数
|
||||
'vue/multiline-html-element-content-newline': 'off', // 在多行元素的内容之前和之后需要换行符
|
||||
'vue/singleline-html-element-content-newline': 'off', // 在单行元素的内容之前和之后需要换行符
|
||||
'vue/attribute-hyphenation': 'off', // 对模板中的自定义组件强制执行属性命名样式
|
||||
'vue/require-default-prop': 'off', // 此规则要求为每个 prop 为必填时,必须提供默认值
|
||||
'vue/multi-word-component-names': 'off', // 要求组件名称始终为 “-” 链接的单词
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,27 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
stats.html
|
||||
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
!.vscode/settings.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
@@ -0,0 +1,8 @@
|
||||
/dist/*
|
||||
.local
|
||||
/node_modules/**
|
||||
|
||||
**/*.svg
|
||||
**/*.sh
|
||||
|
||||
/public/*
|
||||
@@ -0,0 +1,37 @@
|
||||
module.exports = {
|
||||
// 一行最多 120 字符
|
||||
printWidth: 120,
|
||||
// 使用 4 个空格缩进
|
||||
tabWidth: 4,
|
||||
// 不使用 tab 缩进,而使用空格
|
||||
useTabs: false,
|
||||
// 行尾需要有分号
|
||||
semi: true,
|
||||
// 使用单引号代替双引号
|
||||
singleQuote: true,
|
||||
// 对象的 key 仅在必要时用引号
|
||||
quoteProps: 'as-needed',
|
||||
// jsx 不使用单引号,而使用双引号
|
||||
jsxSingleQuote: false,
|
||||
// 末尾使用逗号
|
||||
trailingComma: 'all',
|
||||
// 大括号内的首尾需要空格 { foo: bar }
|
||||
bracketSpacing: true,
|
||||
// jsx 标签的反尖括号需要换行
|
||||
jsxBracketSameLine: false,
|
||||
// 箭头函数,只有一个参数的时候,也需要括号
|
||||
arrowParens: 'always',
|
||||
// 每个文件格式化的范围是文件的全部内容
|
||||
rangeStart: 0,
|
||||
rangeEnd: Infinity,
|
||||
// 不需要写文件开头的 @prettier
|
||||
requirePragma: false,
|
||||
// 不需要自动在文件开头插入 @prettier
|
||||
insertPragma: false,
|
||||
// 使用默认的折行标准
|
||||
proseWrap: 'preserve',
|
||||
// 根据显示样式决定 html 要不要折行
|
||||
htmlWhitespaceSensitivity: 'css',
|
||||
// 换行符使用 lf
|
||||
endOfLine: 'auto',
|
||||
};
|
||||
@@ -0,0 +1,3 @@
|
||||
/dist/*
|
||||
/public/*
|
||||
public/*
|
||||
@@ -0,0 +1,46 @@
|
||||
// @see: https://stylelint.io
|
||||
|
||||
module.exports = {
|
||||
/* 继承某些已有的规则 */
|
||||
extends: [
|
||||
'stylelint-config-standard', // 配置stylelint拓展插件
|
||||
'stylelint-config-html/vue', // 配置 vue 中 template 样式格式化
|
||||
'stylelint-config-standard-scss', // 配置stylelint scss插件
|
||||
'stylelint-config-recommended-vue/scss', // 配置 vue 中 scss 样式格式化
|
||||
'stylelint-config-recess-order', // 配置stylelint css属性书写顺序插件,
|
||||
'stylelint-config-prettier', // 配置stylelint和prettier兼容
|
||||
],
|
||||
overrides: [
|
||||
// 扫描 .vue/html 文件中的<style>标签内的样式
|
||||
{
|
||||
files: ['**/*.{vue,html}'],
|
||||
customSyntax: 'postcss-html',
|
||||
},
|
||||
],
|
||||
/**
|
||||
* null => 关闭该规则
|
||||
*/
|
||||
rules: {
|
||||
'no-descending-specificity': null, // 禁止在具有较高优先级的选择器后出现被其覆盖的较低优先级的选择器
|
||||
'function-url-quotes': 'always', // 要求或禁止 URL 的引号 "always(必须加上引号)"|"never(没有引号)"
|
||||
'string-quotes': 'double', // 指定字符串使用单引号或双引号
|
||||
'unit-case': null, // 指定单位的大小写 "lower(全小写)"|"upper(全大写)"
|
||||
'color-hex-case': 'lower', // 指定 16 进制颜色的大小写 "lower(全小写)"|"upper(全大写)"
|
||||
'color-hex-length': 'long', // 指定 16 进制颜色的简写或扩写 "short(16进制简写)"|"long(16进制扩写)"
|
||||
'rule-empty-line-before': 'never', // 要求或禁止在规则之前的空行 "always(规则之前必须始终有一个空行)"|"never(规则前绝不能有空行)"|"always-multi-line(多行规则之前必须始终有一个空行)"|"never-multi-line(多行规则之前绝不能有空行。)"
|
||||
'font-family-no-missing-generic-family-keyword': null, // 禁止在字体族名称列表中缺少通用字体族关键字
|
||||
'block-opening-brace-space-before': 'always', // 要求在块的开大括号之前必须有一个空格或不能有空白符 "always(大括号前必须始终有一个空格)"|"never(左大括号之前绝不能有空格)"|"always-single-line(在单行块中的左大括号之前必须始终有一个空格)"|"never-single-line(在单行块中的左大括号之前绝不能有空格)"|"always-multi-line(在多行块中,左大括号之前必须始终有一个空格)"|"never-multi-line(多行块中的左大括号之前绝不能有空格)"
|
||||
'property-no-unknown': null, // 禁止未知的属性(true 为不允许)
|
||||
'no-empty-source': null, // 禁止空源码
|
||||
'declaration-block-trailing-semicolon': null, // 要求或不允许在声明块中使用尾随分号 string:"always(必须始终有一个尾随分号)"|"never(不得有尾随分号)"
|
||||
'selector-class-pattern': null, // 强制选择器类名的格式
|
||||
'scss/at-import-partial-extension': null, // 解决不能引入scss文件
|
||||
'value-no-vendor-prefix': null, // 关闭 vendor-prefix(为了解决多行省略 -webkit-box)
|
||||
'selector-pseudo-class-no-unknown': [
|
||||
true,
|
||||
{
|
||||
ignorePseudoClasses: ['global', 'v-deep', 'deep'],
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"vue.volar",
|
||||
"hollowtree.vue-snippets",
|
||||
"vue.vscode-typescript-vue-plugin",
|
||||
"stylelint.vscode-stylelint",
|
||||
"dbaeumer.vscode-eslint",
|
||||
"editorconfig.editorconfig",
|
||||
"esbenp.prettier-vscode"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
Copyright (c) 2022 Halsey
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
PERFORMANCE OF THIS SOFTWARE.
|
||||
@@ -0,0 +1 @@
|
||||
1Panel 前端
|
||||
@@ -0,0 +1,544 @@
|
||||
# 项目规范文档 📚
|
||||
|
||||
## 一、项目文件、组件命名规范
|
||||
|
||||
> **完全采用 Vue 官方推荐的风格指南,请务必查看 💢**
|
||||
>
|
||||
> **Link:** https://v3.cn.vuejs.org/style-guide
|
||||
|
||||
## 二、代码格式化工具(Prettier)
|
||||
|
||||
### 1、下载安装 prettier:
|
||||
|
||||
```text
|
||||
npm install prettier
|
||||
```
|
||||
|
||||
### 2、安装 Vscode 插件(Prettier):
|
||||
|
||||

|
||||
|
||||
### 3、配置 Prettier:
|
||||
|
||||
```javascript
|
||||
// @see: https://www.prettier.cn
|
||||
|
||||
module.exports = {
|
||||
// 超过最大值换行
|
||||
printWidth: 130,
|
||||
// 缩进字节数
|
||||
tabWidth: 2,
|
||||
// 使用制表符而不是空格缩进行
|
||||
useTabs: true,
|
||||
// 结尾不用分号(true有,false没有)
|
||||
semi: true,
|
||||
// 使用单引号(true单双引号,false双引号)
|
||||
singleQuote: false,
|
||||
// 更改引用对象属性的时间 可选值"<as-needed|consistent|preserve>"
|
||||
quoteProps: 'as-needed',
|
||||
// 在对象,数组括号与文字之间加空格 "{ foo: bar }"
|
||||
bracketSpacing: true,
|
||||
// 多行时尽可能打印尾随逗号。(例如,单行数组永远不会出现逗号结尾。) 可选值"<none|es5|all>",默认none
|
||||
trailingComma: 'none',
|
||||
// 在JSX中使用单引号而不是双引号
|
||||
jsxSingleQuote: false,
|
||||
// (x) => {} 箭头函数参数只有一个时是否要有小括号。avoid:省略括号 ,always:不省略括号
|
||||
arrowParens: 'avoid',
|
||||
// 如果文件顶部已经有一个 doclock,这个选项将新建一行注释,并打上@format标记。
|
||||
insertPragma: false,
|
||||
// 指定要使用的解析器,不需要写文件开头的 @prettier
|
||||
requirePragma: false,
|
||||
// 默认值。因为使用了一些折行敏感型的渲染器(如GitHub comment)而按照markdown文本样式进行折行
|
||||
proseWrap: 'preserve',
|
||||
// 在html中空格是否是敏感的 "css" - 遵守CSS显示属性的默认值, "strict" - 空格被认为是敏感的 ,"ignore" - 空格被认为是不敏感的
|
||||
htmlWhitespaceSensitivity: 'css',
|
||||
// 换行符使用 lf 结尾是 可选值"<auto|lf|crlf|cr>"
|
||||
endOfLine: 'auto',
|
||||
// 这两个选项可用于格式化以给定字符偏移量(分别包括和不包括)开始和结束的代码
|
||||
rangeStart: 0,
|
||||
rangeEnd: Infinity,
|
||||
// Vue文件脚本和样式标签缩进
|
||||
vueIndentScriptAndStyle: false,
|
||||
};
|
||||
```
|
||||
|
||||
## 三、代码规范工具(ESLint)
|
||||
|
||||
### 1、安装 ESLint 相关插件:
|
||||
|
||||
```text
|
||||
npm install eslint eslint-config-prettier eslint-plugin-prettier eslint-plugin-vue @typescript-eslint/eslint-plugin @typescript-eslint/parser -D
|
||||
```
|
||||
|
||||
| 依赖 | 作用描述 |
|
||||
| :------------------------------: | :------------------------------------------------------------------: |
|
||||
| eslint | ESLint 核心库 |
|
||||
| eslint-config-prettier | 关掉所有和 Prettier 冲突的 ESLint 的配置 |
|
||||
| eslint-plugin-prettier | 将 Prettier 的 rules 以插件的形式加入到 ESLint 里面 |
|
||||
| eslint-plugin-vue | 为 Vue 使用 ESlint 的插件 |
|
||||
| @typescript-eslint/eslint-plugin | ESLint 插件,包含了各类定义好的检测 TypeScript 代码的规范 |
|
||||
| @typescript-eslint/parser | ESLint 的解析器,用于解析 TypeScript,从而检查和规范 TypeScript 代码 |
|
||||
|
||||
### 2、安装 Vscode 插件(ESLint、TSLint):
|
||||
|
||||
- **ESLint:**
|
||||
|
||||

|
||||
|
||||
### 3、配置 ESLint:
|
||||
|
||||
```javascript
|
||||
// @see: http://eslint.cn
|
||||
|
||||
module.exports = {
|
||||
root: true,
|
||||
env: {
|
||||
browser: true,
|
||||
node: true,
|
||||
es6: true,
|
||||
},
|
||||
/* 指定如何解析语法 */
|
||||
parser: 'vue-eslint-parser',
|
||||
/* 优先级低于 parse 的语法解析配置 */
|
||||
parserOptions: {
|
||||
parser: '@typescript-eslint/parser',
|
||||
ecmaVersion: 2020,
|
||||
sourceType: 'module',
|
||||
jsxPragma: 'React',
|
||||
ecmaFeatures: {
|
||||
jsx: true,
|
||||
},
|
||||
},
|
||||
/* 继承某些已有的规则 */
|
||||
extends: [
|
||||
'plugin:vue/vue3-recommended',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'prettier',
|
||||
'plugin:prettier/recommended',
|
||||
],
|
||||
/*
|
||||
* "off" 或 0 ==> 关闭规则
|
||||
* "warn" 或 1 ==> 打开的规则作为警告(不影响代码执行)
|
||||
* "error" 或 2 ==> 规则作为一个错误(代码不能执行,界面报错)
|
||||
*/
|
||||
rules: {
|
||||
// eslint (http://eslint.cn/docs/rules)
|
||||
'no-var': 'error', // 要求使用 let 或 const 而不是 var
|
||||
'no-multiple-empty-lines': ['error', { max: 1 }], // 不允许多个空行
|
||||
'no-use-before-define': 'off', // 禁止在 函数/类/变量 定义之前使用它们
|
||||
'prefer-const': 'off', // 此规则旨在标记使用 let 关键字声明但在初始分配后从未重新分配的变量,要求使用 const
|
||||
'no-irregular-whitespace': 'off', // 禁止不规则的空白
|
||||
|
||||
// typeScript (https://typescript-eslint.io/rules)
|
||||
'@typescript-eslint/no-unused-vars': 'error', // 禁止定义未使用的变量
|
||||
'@typescript-eslint/no-inferrable-types': 'off', // 可以轻松推断的显式类型可能会增加不必要的冗长
|
||||
'@typescript-eslint/no-namespace': 'off', // 禁止使用自定义 TypeScript 模块和命名空间。
|
||||
'@typescript-eslint/no-explicit-any': 'off', // 禁止使用 any 类型
|
||||
'@typescript-eslint/ban-ts-ignore': 'off', // 禁止使用 @ts-ignore
|
||||
'@typescript-eslint/ban-types': 'off', // 禁止使用特定类型
|
||||
'@typescript-eslint/explicit-function-return-type': 'off', // 不允许对初始化为数字、字符串或布尔值的变量或参数进行显式类型声明
|
||||
'@typescript-eslint/no-var-requires': 'off', // 不允许在 import 语句中使用 require 语句
|
||||
'@typescript-eslint/no-empty-function': 'off', // 禁止空函数
|
||||
'@typescript-eslint/no-use-before-define': 'off', // 禁止在变量定义之前使用它们
|
||||
'@typescript-eslint/ban-ts-comment': 'off', // 禁止 @ts-<directive> 使用注释或要求在指令后进行描述
|
||||
'@typescript-eslint/no-non-null-assertion': 'off', // 不允许使用后缀运算符的非空断言(!)
|
||||
'@typescript-eslint/explicit-module-boundary-types': 'off', // 要求导出函数和类的公共类方法的显式返回和参数类型
|
||||
|
||||
// vue (https://eslint.vuejs.org/rules)
|
||||
'vue/script-setup-uses-vars': 'error', // 防止<script setup>使用的变量<template>被标记为未使用,此规则仅在启用该no-unused-vars规则时有效。
|
||||
'vue/v-slot-style': 'error', // 强制执行 v-slot 指令样式
|
||||
'vue/no-mutating-props': 'off', // 不允许组件 prop的改变(明天找原因)
|
||||
'vue/custom-event-name-casing': 'off', // 为自定义事件名称强制使用特定大小写
|
||||
'vue/attributes-order': 'off', // vue api使用顺序,强制执行属性顺序
|
||||
'vue/one-component-per-file': 'off', // 强制每个组件都应该在自己的文件中
|
||||
'vue/html-closing-bracket-newline': 'off', // 在标签的右括号之前要求或禁止换行
|
||||
'vue/max-attributes-per-line': 'off', // 强制每行的最大属性数
|
||||
'vue/multiline-html-element-content-newline': 'off', // 在多行元素的内容之前和之后需要换行符
|
||||
'vue/singleline-html-element-content-newline': 'off', // 在单行元素的内容之前和之后需要换行符
|
||||
'vue/attribute-hyphenation': 'off', // 对模板中的自定义组件强制执行属性命名样式
|
||||
'vue/require-default-prop': 'off', // 此规则要求为每个 prop 为必填时,必须提供默认值
|
||||
'vue/multi-word-component-names': 'off', // 要求组件名称始终为 “-” 链接的单词
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
## 四、样式规范工具(StyleLint)
|
||||
|
||||
### 1、安装 StyleLint 相关插件:
|
||||
|
||||
```text
|
||||
npm i stylelint stylelint-config-html stylelint-config-recommended-scss stylelint-config-recommended-vue stylelint-config-standard stylelint-config-standard-scss stylelint-config-recess-order postcss postcss-html stylelint-config-prettier -D
|
||||
```
|
||||
|
||||
| 依赖 | 作用描述 |
|
||||
| :-------------------------------: | :----------------------------------------------------------------------------------------------------------------------------------------------: |
|
||||
| stylelint | stylelint 核心库 |
|
||||
| stylelint-config-html | Stylelint 的可共享 HTML(和类似 HTML)配置,捆绑 postcss-html 并对其进行配置。 |
|
||||
| stylelint-config-recommended-scss | 扩展 stylelint-config-recommended 共享配置,并为 SCSS 配置其规则 |
|
||||
| stylelint-config-recommended-vue | 扩展 stylelint-config-recommended 共享配置,并为 Vue 配置其规则 |
|
||||
| stylelint-config-standard | 打开额外的规则来执行在规范和一些 CSS 样式指南中发现的通用约定,包括:惯用 CSS 原则,谷歌的 CSS 样式指南,Airbnb 的样式指南,和 @mdo 的代码指南。 |
|
||||
| stylelint-config-standard-scss | 扩展 stylelint-config-standard 共享配置,并为 SCSS 配置其规则 |
|
||||
| postcss | postcss-html 的依赖包 |
|
||||
| postcss-html | 用于解析 HTML(和类似 HTML)的 PostCSS 语法 |
|
||||
| stylelint-config-recess-order | 属性的排序(插件包) |
|
||||
| stylelint-config-prettier | 关闭所有不必要的或可能与 Prettier 冲突的规则 |
|
||||
|
||||
### 2、安装 Vscode 插件(Stylelint):
|
||||
|
||||

|
||||
|
||||
### 3、在目录的 .vscode 文件中新建 settings.json:
|
||||
|
||||
```json
|
||||
{
|
||||
"editor.formatOnSave": true,
|
||||
"stylelint.enable": true,
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.stylelint": true
|
||||
},
|
||||
"stylelint.validate": ["css", "less", "postcss", "scss", "vue", "sass", "html"],
|
||||
"files.eol": "\n"
|
||||
}
|
||||
```
|
||||
|
||||
> 😎 也可以在 vscode 中全局配置上述 json 代码 😎
|
||||
|
||||
### 4、配置 stylelint.config.js
|
||||
|
||||
```javascript
|
||||
// @see: https://stylelint.io
|
||||
|
||||
module.exports = {
|
||||
/* 继承某些已有的规则 */
|
||||
extends: [
|
||||
'stylelint-config-standard', // 配置stylelint拓展插件
|
||||
'stylelint-config-html/vue', // 配置 vue 中 template 样式格式化
|
||||
'stylelint-config-standard-scss', // 配置stylelint scss插件
|
||||
'stylelint-config-recommended-vue/scss', // 配置 vue 中 scss 样式格式化
|
||||
'stylelint-config-recess-order', // 配置stylelint css属性书写顺序插件,
|
||||
'stylelint-config-prettier', // 配置stylelint和prettier兼容
|
||||
],
|
||||
overrides: [
|
||||
// 扫描 .vue/html 文件中的<style>标签内的样式
|
||||
{
|
||||
files: ['**/*.{vue,html}'],
|
||||
customSyntax: 'postcss-html',
|
||||
},
|
||||
],
|
||||
/**
|
||||
* null => 关闭该规则
|
||||
*/
|
||||
rules: {
|
||||
'no-descending-specificity': null, // 禁止在具有较高优先级的选择器后出现被其覆盖的较低优先级的选择器
|
||||
'function-url-quotes': 'always', // 要求或禁止 URL 的引号 "always(必须加上引号)"|"never(没有引号)"
|
||||
'string-quotes': 'double', // 指定字符串使用单引号或双引号
|
||||
'unit-case': null, // 指定单位的大小写 "lower(全小写)"|"upper(全大写)"
|
||||
'color-hex-case': 'lower', // 指定 16 进制颜色的大小写 "lower(全小写)"|"upper(全大写)"
|
||||
'color-hex-length': 'long', // 指定 16 进制颜色的简写或扩写 "short(16进制简写)"|"long(16进制扩写)"
|
||||
'rule-empty-line-before': 'never', // 要求或禁止在规则之前的空行 "always(规则之前必须始终有一个空行)"|"never(规则前绝不能有空行)"|"always-multi-line(多行规则之前必须始终有一个空行)"|"never-multi-line(多行规则之前绝不能有空行。)"
|
||||
'font-family-no-missing-generic-family-keyword': null, // 禁止在字体族名称列表中缺少通用字体族关键字
|
||||
'block-opening-brace-space-before': 'always', // 要求在块的开大括号之前必须有一个空格或不能有空白符 "always(大括号前必须始终有一个空格)"|"never(左大括号之前绝不能有空格)"|"always-single-line(在单行块中的左大括号之前必须始终有一个空格)"|"never-single-line(在单行块中的左大括号之前绝不能有空格)"|"always-multi-line(在多行块中,左大括号之前必须始终有一个空格)"|"never-multi-line(多行块中的左大括号之前绝不能有空格)"
|
||||
'property-no-unknown': null, // 禁止未知的属性(true 为不允许)
|
||||
'no-empty-source': null, // 禁止空源码
|
||||
'declaration-block-trailing-semicolon': null, // 要求或不允许在声明块中使用尾随分号 string:"always(必须始终有一个尾随分号)"|"never(不得有尾随分号)"
|
||||
'selector-class-pattern': null, // 强制选择器类名的格式
|
||||
'scss/at-import-partial-extension': null, // 解决不能引入scss文件
|
||||
'value-no-vendor-prefix': null, // 关闭 vendor-prefix(为了解决多行省略 -webkit-box)
|
||||
'selector-pseudo-class-no-unknown': [
|
||||
true,
|
||||
{
|
||||
ignorePseudoClasses: ['global', 'v-deep', 'deep'],
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
## 五、EditorConfig 配置
|
||||
|
||||
### 1、简介
|
||||
|
||||
> **EditorConfig** 帮助开发人员在 **不同的编辑器** 和 **IDE** 之间定义和维护一致的编码样式。
|
||||
|
||||
### 2、安装 VsCode 插件(EditorConfig ):
|
||||
|
||||

|
||||
|
||||
### 3、配置 EditorConfig:
|
||||
|
||||
```javascript
|
||||
# http://editorconfig.org
|
||||
root = true
|
||||
|
||||
[*] # 表示所有文件适用
|
||||
charset = utf-8 # 设置文件字符集为 utf-8
|
||||
end_of_line = lf # 控制换行类型(lf | cr | crlf)
|
||||
insert_final_newline = true # 始终在文件末尾插入一个新行
|
||||
indent_style = tab # 缩进风格(tab | space)
|
||||
indent_size = 2 # 缩进大小
|
||||
max_line_length = 130 # 最大行长度
|
||||
|
||||
[*.md] # 表示仅 md 文件适用以下规则
|
||||
max_line_length = off # 关闭最大行长度限制
|
||||
trim_trailing_whitespace = false # 关闭末尾空格修剪
|
||||
```
|
||||
|
||||
## 六、Git 流程规范配置
|
||||
|
||||
| 依赖 | 作用描述 |
|
||||
| :-----------------------------: | :----------------------------------------------------------------------------: |
|
||||
| husky | 操作 **git** 钩子的工具(在 **git xx** 之前执行某些命令) |
|
||||
| lint-staged | 在提交之前进行 **eslint** 校验,并使用 **prettier** 格式化本地暂存区的代码, |
|
||||
| @commitlint/cli | 校验 **git commit** 信息是否符合规范,保证团队的一致性 |
|
||||
| @commitlint/config-conventional | **Anglar** 的提交规范 |
|
||||
| commitizen | 基于 **Node.js** 的 **git commit** 命令行工具,生成标准化的 **commit message** |
|
||||
| cz-git | 一款工程性更强,轻量级,高度自定义,标准输出格式的 **commitize** 适配器 |
|
||||
|
||||
### 1、husky(操作 git 钩子的工具):
|
||||
|
||||
> **安装:**
|
||||
|
||||
```text
|
||||
npm install husky -D
|
||||
```
|
||||
|
||||
> **使用(为了添加.husky 文件夹):**
|
||||
|
||||
```text
|
||||
# 编辑 package.json > prepare 脚本并运行一次
|
||||
|
||||
npm set-script prepare "husky install"
|
||||
npm run prepare
|
||||
```
|
||||
|
||||
### 2、 lint-staged(本地暂存代码检查工具)
|
||||
|
||||
> **安装:**
|
||||
|
||||
```text
|
||||
npm install lint-staged --D
|
||||
```
|
||||
|
||||
> **添加 ESlint Hook(在.husky 文件夹下添加 pre-commit 文件):**
|
||||
>
|
||||
> **作用:通过钩子函数,判断提交的代码是否符合规范,并使用 prettier 格式化代码**
|
||||
|
||||
```text
|
||||
npx husky add .husky/pre-commit "npm run lint:lint-staged"
|
||||
```
|
||||
|
||||
> 新增 **lint-staged.config.js** 文件:
|
||||
|
||||
```text
|
||||
module.exports = {
|
||||
"*.{js,jsx,ts,tsx}": ["eslint --fix", "prettier --write"],
|
||||
"{!(package)*.json,*.code-snippets,.!(browserslist)*rc}": ["prettier --write--parser json"],
|
||||
"package.json": ["prettier --write"],
|
||||
"*.vue": ["eslint --fix", "prettier --write", "stylelint --fix"],
|
||||
"*.{scss,less,styl,html}": ["stylelint --fix", "prettier --write"],
|
||||
"*.md": ["prettier --write"]
|
||||
};
|
||||
```
|
||||
|
||||
### 3、commitlint(commit 信息校验工具,不符合则报错)
|
||||
|
||||
> **安装:**
|
||||
|
||||
```text
|
||||
npm i @commitlint/cli @commitlint/config-conventional -D
|
||||
```
|
||||
|
||||
> **配置命令(在.husky 文件夹下添加 commit-msg 文件):**
|
||||
|
||||
```text
|
||||
npx husky add .husky/commit-msg 'npx --no-install commitlint --edit "$1"'
|
||||
```
|
||||
|
||||
### 4、commitizen(基于 Node.js 的 git commit 命令行工具,生成标准化的 message)
|
||||
|
||||
```text
|
||||
// 安装 commitizen,如此一来可以快速使用 cz 或 git cz 命令进行启动。
|
||||
npm install commitizen -D
|
||||
```
|
||||
|
||||
### 5、cz-git
|
||||
|
||||
> **指定提交文字规范,一款工程性更强,高度自定义,标准输出格式的 commitizen 适配器**
|
||||
|
||||
```text
|
||||
npm install cz-git -D
|
||||
```
|
||||
|
||||
> **配置 package.json:**
|
||||
|
||||
```text
|
||||
"config": {
|
||||
"commitizen": {
|
||||
"path": "node_modules/cz-git"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> **新建 commitlint.config.js 文件:**
|
||||
|
||||
```javascript
|
||||
// @see: https://cz-git.qbenben.com/zh/guide
|
||||
/** @type {import('cz-git').UserConfig} */
|
||||
|
||||
module.exports = {
|
||||
ignores: [(commit) => commit.includes('init')],
|
||||
extends: ['@commitlint/config-conventional'],
|
||||
rules: {
|
||||
// @see: https://commitlint.js.org/#/reference-rules
|
||||
'body-leading-blank': [2, 'always'],
|
||||
'footer-leading-blank': [1, 'always'],
|
||||
'header-max-length': [2, 'always', 108],
|
||||
'subject-empty': [2, 'never'],
|
||||
'type-empty': [2, 'never'],
|
||||
'subject-case': [0],
|
||||
'type-enum': [
|
||||
2,
|
||||
'always',
|
||||
[
|
||||
'feat',
|
||||
'fix',
|
||||
'docs',
|
||||
'style',
|
||||
'refactor',
|
||||
'perf',
|
||||
'test',
|
||||
'build',
|
||||
'ci',
|
||||
'chore',
|
||||
'revert',
|
||||
'wip',
|
||||
'workflow',
|
||||
'types',
|
||||
'release',
|
||||
],
|
||||
],
|
||||
},
|
||||
prompt: {
|
||||
messages: {
|
||||
type: "Select the type of change that you're committing:",
|
||||
scope: 'Denote the SCOPE of this change (optional):',
|
||||
customScope: 'Denote the SCOPE of this change:',
|
||||
subject: 'Write a SHORT, IMPERATIVE tense description of the change:\n',
|
||||
body: 'Provide a LONGER description of the change (optional). Use "|" to break new line:\n',
|
||||
breaking: 'List any BREAKING CHANGES (optional). Use "|" to break new line:\n',
|
||||
footerPrefixsSelect: 'Select the ISSUES type of changeList by this change (optional):',
|
||||
customFooterPrefixs: 'Input ISSUES prefix:',
|
||||
footer: 'List any ISSUES by this change. E.g.: #31, #34:\n',
|
||||
confirmCommit: 'Are you sure you want to proceed with the commit above?',
|
||||
// 中文版
|
||||
// type: "选择你要提交的类型 :",
|
||||
// scope: "选择一个提交范围(可选):",
|
||||
// customScope: "请输入自定义的提交范围 :",
|
||||
// subject: "填写简短精炼的变更描述 :\n",
|
||||
// body: '填写更加详细的变更描述(可选)。使用 "|" 换行 :\n',
|
||||
// breaking: '列举非兼容性重大的变更(可选)。使用 "|" 换行 :\n',
|
||||
// footerPrefixsSelect: "选择关联issue前缀(可选):",
|
||||
// customFooterPrefixs: "输入自定义issue前缀 :",
|
||||
// footer: "列举关联issue (可选) 例如: #31, #I3244 :\n",
|
||||
// confirmCommit: "是否提交或修改commit ?"
|
||||
},
|
||||
types: [
|
||||
{
|
||||
value: 'feat',
|
||||
name: 'feat: 🚀 A new feature',
|
||||
emoji: '🚀',
|
||||
},
|
||||
{
|
||||
value: 'fix',
|
||||
name: 'fix: 🧩 A bug fix',
|
||||
emoji: '🧩',
|
||||
},
|
||||
{
|
||||
value: 'docs',
|
||||
name: 'docs: 📚 Documentation only changes',
|
||||
emoji: '📚',
|
||||
},
|
||||
{
|
||||
value: 'style',
|
||||
name: 'style: 🎨 Changes that do not affect the meaning of the code',
|
||||
emoji: '🎨',
|
||||
},
|
||||
{
|
||||
value: 'refactor',
|
||||
name: 'refactor: ♻️ A code change that neither fixes a bug nor adds a feature',
|
||||
emoji: '♻️',
|
||||
},
|
||||
{
|
||||
value: 'perf',
|
||||
name: 'perf: ⚡️ A code change that improves performance',
|
||||
emoji: '⚡️',
|
||||
},
|
||||
{
|
||||
value: 'test',
|
||||
name: 'test: ✅ Adding missing tests or correcting existing tests',
|
||||
emoji: '✅',
|
||||
},
|
||||
{
|
||||
value: 'build',
|
||||
name: 'build: 📦️ Changes that affect the build system or external dependencies',
|
||||
emoji: '📦️',
|
||||
},
|
||||
{
|
||||
value: 'ci',
|
||||
name: 'ci: 🎡 Changes to our CI configuration files and scripts',
|
||||
emoji: '🎡',
|
||||
},
|
||||
{
|
||||
value: 'chore',
|
||||
name: "chore: 🔨 Other changes that don't modify src or test files",
|
||||
emoji: '🔨',
|
||||
},
|
||||
{
|
||||
value: 'revert',
|
||||
name: 'revert: ⏪️ Reverts a previous commit',
|
||||
emoji: '⏪️',
|
||||
},
|
||||
// 中文版
|
||||
// { value: "特性", name: "特性: 🚀 新增功能", emoji: "🚀" },
|
||||
// { value: "修复", name: "修复: 🧩 修复缺陷", emoji: "🧩" },
|
||||
// { value: "文档", name: "文档: 📚 文档变更", emoji: "📚" },
|
||||
// { value: "格式", name: "格式: 🎨 代码格式(不影响功能,例如空格、分号等格式修正)", emoji: "🎨" },
|
||||
// { value: "重构", name: "重构: ♻️ 代码重构(不包括 bug 修复、功能新增)", emoji: "♻️" },
|
||||
// { value: "性能", name: "性能: ⚡️ 性能优化", emoji: "⚡️" },
|
||||
// { value: "测试", name: "测试: ✅ 添加疏漏测试或已有测试改动", emoji: "✅" },
|
||||
// { value: "构建", name: "构建: 📦️ 构建流程、外部依赖变更(如升级 npm 包、修改 webpack 配置等)", emoji: "📦️" },
|
||||
// { value: "集成", name: "集成: 🎡 修改 CI 配置、脚本", emoji: "🎡" },
|
||||
// { value: "回退", name: "回退: ⏪️ 回滚 commit", emoji: "⏪️" },
|
||||
// { value: "其他", name: "其他: 🔨 对构建过程或辅助工具和库的更改(不影响源文件、测试用例)", emoji: "🔨" }
|
||||
],
|
||||
useEmoji: true,
|
||||
themeColorCode: '',
|
||||
scopes: [],
|
||||
allowCustomScopes: true,
|
||||
allowEmptyScopes: true,
|
||||
customScopesAlign: 'bottom',
|
||||
customScopesAlias: 'custom',
|
||||
emptyScopesAlias: 'empty',
|
||||
upperCaseSubject: false,
|
||||
allowBreakingChanges: ['feat', 'fix'],
|
||||
breaklineNumber: 100,
|
||||
breaklineChar: '|',
|
||||
skipQuestions: [],
|
||||
issuePrefixs: [{ value: 'closed', name: 'closed: ISSUES has been processed' }],
|
||||
customIssuePrefixsAlign: 'top',
|
||||
emptyIssuePrefixsAlias: 'skip',
|
||||
customIssuePrefixsAlias: 'custom',
|
||||
allowCustomIssuePrefixs: true,
|
||||
allowEmptyIssuePrefixs: true,
|
||||
confirmColorize: true,
|
||||
maxHeaderLength: Infinity,
|
||||
maxSubjectLength: Infinity,
|
||||
minSubjectLength: 0,
|
||||
scopeOverrides: undefined,
|
||||
defaultBody: '',
|
||||
defaultIssues: '',
|
||||
defaultScope: '',
|
||||
defaultSubject: '',
|
||||
},
|
||||
};
|
||||
```
|
||||
@@ -0,0 +1,97 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title><%- title %></title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app">
|
||||
<style>
|
||||
html,
|
||||
body,
|
||||
#app {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
.first-loading-wrap {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.first-loading-wrap > h1 {
|
||||
font-size: 128px;
|
||||
}
|
||||
.first-loading-wrap .loading-wrap {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 98px;
|
||||
}
|
||||
.dot {
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
display: inline-block;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 32px;
|
||||
transform: rotate(45deg);
|
||||
animation: ant-rotate 1.2s infinite linear;
|
||||
}
|
||||
.dot i {
|
||||
position: absolute;
|
||||
display: block;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
background-color: #1890ff;
|
||||
border-radius: 100%;
|
||||
opacity: 0.3;
|
||||
transform: scale(0.75);
|
||||
transform-origin: 50% 50%;
|
||||
animation: ant-spin-move 1s infinite linear alternate;
|
||||
}
|
||||
.dot i:nth-child(1) {
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
.dot i:nth-child(2) {
|
||||
top: 0;
|
||||
right: 0;
|
||||
animation-delay: 0.4s;
|
||||
}
|
||||
.dot i:nth-child(3) {
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
animation-delay: 0.8s;
|
||||
}
|
||||
.dot i:nth-child(4) {
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
animation-delay: 1.2s;
|
||||
}
|
||||
@keyframes ant-rotate {
|
||||
to {
|
||||
transform: rotate(405deg);
|
||||
}
|
||||
}
|
||||
@keyframes ant-spin-move {
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<div class="first-loading-wrap">
|
||||
<div class="loading-wrap">
|
||||
<span class="dot dot-spin"><i></i><i></i><i></i><i></i></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,8 @@
|
||||
module.exports = {
|
||||
'*.{js,jsx,ts,tsx}': ['eslint --fix', 'prettier --write'],
|
||||
'{!(package)*.json,*.code-snippets,.!(browserslist)*rc}': ['prettier --write--parser json'],
|
||||
'package.json': ['prettier --write'],
|
||||
'*.vue': ['eslint --fix', 'prettier --write', 'stylelint --fix'],
|
||||
'*.{scss,less,styl,html}': ['stylelint --fix', 'prettier --write'],
|
||||
'*.md': ['prettier --write'],
|
||||
};
|
||||
@@ -0,0 +1,89 @@
|
||||
{
|
||||
"name": "panel",
|
||||
"private": true,
|
||||
"version": "0.0.1",
|
||||
"description": "1Panel 前端",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"serve": "vite",
|
||||
"build:dev": "vue-tsc --noEmit && vite build --mode development",
|
||||
"build:test": "vue-tsc --noEmit && vite build --mode test",
|
||||
"build:pro": "vue-tsc --noEmit && vite build --mode production",
|
||||
"preview": "vite preview",
|
||||
"lint:eslint": "eslint --fix --ext .js,.ts,.vue ./src",
|
||||
"lint:prettier": "prettier --write --loglevel warn \"src/**/*.{js,ts,json,tsx,css,less,scss,vue,html,md}\"",
|
||||
"lint:stylelint": "stylelint --cache --fix \"**/*.{vue,less,postcss,css,scss}\" --cache --cache-location node_modules/.cache/stylelint/",
|
||||
"lint:lint-staged": "lint-staged",
|
||||
"release": "standard-version",
|
||||
"commit": "git pull && git add -A && git-cz && git push",
|
||||
"prettier:comment": "自动格式化当前目录下的所有文件",
|
||||
"prettier": "prettier --write ."
|
||||
},
|
||||
"dependencies": {
|
||||
"@element-plus/icons-vue": "^1.1.4",
|
||||
"@vueuse/core": "^8.0.1",
|
||||
"axios": "^0.27.2",
|
||||
"echarts": "^5.3.0",
|
||||
"echarts-liquidfill": "^3.1.0",
|
||||
"element-plus": "^2.2.6",
|
||||
"fit2cloud-ui-plus": "^0.0.1-beta.12",
|
||||
"js-md5": "^0.7.3",
|
||||
"nprogress": "^0.2.0",
|
||||
"pinia": "^2.0.12",
|
||||
"pinia-plugin-persistedstate": "^1.6.1",
|
||||
"qs": "^6.10.3",
|
||||
"sass-loader": "^13.0.2",
|
||||
"unplugin-vue-define-options": "^0.7.3",
|
||||
"vue": "^3.2.25",
|
||||
"vue-i18n": "^9.1.9",
|
||||
"vue-router": "^4.0.12",
|
||||
"vue3-seamless-scroll": "^1.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@commitlint/cli": "^17.0.1",
|
||||
"@commitlint/config-conventional": "^17.0.0",
|
||||
"@types/node": "^17.0.31",
|
||||
"@typescript-eslint/eslint-plugin": "^5.22.0",
|
||||
"@typescript-eslint/parser": "^5.22.0",
|
||||
"@vitejs/plugin-vue": "^2.2.0",
|
||||
"@vitejs/plugin-vue-jsx": "^1.3.10",
|
||||
"autoprefixer": "^10.4.7",
|
||||
"commitizen": "^4.2.4",
|
||||
"cz-git": "^1.3.2",
|
||||
"driver.js": "^0.9.8",
|
||||
"eslint": "^8.14.0",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"eslint-plugin-prettier": "^4.0.0",
|
||||
"eslint-plugin-vue": "^8.7.1",
|
||||
"lint-staged": "^12.4.2",
|
||||
"postcss": "^8.4.14",
|
||||
"postcss-html": "^1.4.1",
|
||||
"prettier": "^2.6.2",
|
||||
"rollup-plugin-visualizer": "^5.5.4",
|
||||
"sass": "^1.49.7",
|
||||
"standard-version": "^9.5.0",
|
||||
"stylelint": "^14.8.5",
|
||||
"stylelint-config-html": "^1.0.0",
|
||||
"stylelint-config-prettier": "^9.0.3",
|
||||
"stylelint-config-recess-order": "^3.0.0",
|
||||
"stylelint-config-recommended-scss": "^6.0.0",
|
||||
"stylelint-config-recommended-vue": "^1.4.0",
|
||||
"stylelint-config-standard": "^25.0.0",
|
||||
"stylelint-config-standard-scss": "^3.0.0",
|
||||
"typescript": "^4.5.4",
|
||||
"unplugin-auto-import": "^0.6.0",
|
||||
"unplugin-vue-components": "^0.17.18",
|
||||
"vite": "^2.8.0",
|
||||
"vite-plugin-cdn-import": "^0.3.5",
|
||||
"vite-plugin-compression": "^0.5.1",
|
||||
"vite-plugin-eslint": "^1.6.0",
|
||||
"vite-plugin-html": "^3.2.0",
|
||||
"vite-plugin-vue-setup-extend": "^0.4.0",
|
||||
"vue-tsc": "^0.29.8"
|
||||
},
|
||||
"config": {
|
||||
"commitizen": {
|
||||
"path": "node_modules/cz-git"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
autoprefixer: {},
|
||||
},
|
||||
};
|
||||
|
After Width: | Height: | Size: 4.2 KiB |
@@ -0,0 +1,34 @@
|
||||
<template>
|
||||
<el-config-provider :locale="i18nLocale" :button="config" :size="assemblySize">
|
||||
<router-view></router-view>
|
||||
</el-config-provider>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { reactive, computed } from 'vue';
|
||||
import { GlobalStore } from '@/store';
|
||||
// 配置element中英文
|
||||
import zhCn from 'element-plus/es/locale/lang/zh-cn';
|
||||
import en from 'element-plus/es/locale/lang/en';
|
||||
// 使用主题
|
||||
import { useTheme } from '@/hooks/use-theme';
|
||||
useTheme();
|
||||
|
||||
const globalStore = GlobalStore();
|
||||
// 配置element按钮文字中间是否有空格
|
||||
const config = reactive({
|
||||
autoInsertSpace: false,
|
||||
});
|
||||
|
||||
// element 语言配置
|
||||
const i18nLocale = computed((): any => {
|
||||
if (globalStore.language && globalStore.language == 'zh') return zhCn;
|
||||
if (globalStore.language == 'en') return en;
|
||||
return '';
|
||||
});
|
||||
|
||||
// 配置全局组件大小 (small/default(medium)/large)
|
||||
const assemblySize = computed((): string => globalStore.assemblySize);
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
@@ -0,0 +1,3 @@
|
||||
// * 后端微服务端口名
|
||||
export const PORT1 = '9999';
|
||||
export const PORT2 = '/hooks';
|
||||
@@ -0,0 +1,63 @@
|
||||
import axios, { AxiosRequestConfig, Canceler } from 'axios';
|
||||
import { isFunction } from '@/utils/is/index';
|
||||
import qs from 'qs';
|
||||
|
||||
// * 声明一个 Map 用于存储每个请求的标识 和 取消函数
|
||||
let pendingMap = new Map<string, Canceler>();
|
||||
|
||||
// * 序列化参数
|
||||
export const getPendingUrl = (config: AxiosRequestConfig) =>
|
||||
[config.method, config.url, qs.stringify(config.data), qs.stringify(config.params)].join('&');
|
||||
|
||||
export class AxiosCanceler {
|
||||
/**
|
||||
* @description: 添加请求
|
||||
* @param {Object} config
|
||||
* @return void
|
||||
*/
|
||||
addPending(config: AxiosRequestConfig) {
|
||||
// * 在请求开始前,对之前的请求做检查取消操作
|
||||
this.removePending(config);
|
||||
const url = getPendingUrl(config);
|
||||
config.cancelToken =
|
||||
config.cancelToken ||
|
||||
new axios.CancelToken((cancel) => {
|
||||
if (!pendingMap.has(url)) {
|
||||
// 如果 pending 中不存在当前请求,则添加进去
|
||||
pendingMap.set(url, cancel);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 移除请求
|
||||
* @param {Object} config
|
||||
*/
|
||||
removePending(config: AxiosRequestConfig) {
|
||||
const url = getPendingUrl(config);
|
||||
|
||||
if (pendingMap.has(url)) {
|
||||
// 如果在 pending 中存在当前请求标识,需要取消当前请求,并且移除
|
||||
const cancel = pendingMap.get(url);
|
||||
cancel && cancel();
|
||||
pendingMap.delete(url);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 清空所有pending
|
||||
*/
|
||||
removeAllPending() {
|
||||
pendingMap.forEach((cancel) => {
|
||||
cancel && isFunction(cancel) && cancel();
|
||||
});
|
||||
pendingMap.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 重置
|
||||
*/
|
||||
reset(): void {
|
||||
pendingMap = new Map<string, Canceler>();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
import { ElMessage } from 'element-plus';
|
||||
|
||||
/**
|
||||
* @description: 校验网络请求状态码
|
||||
* @param {Number} status
|
||||
* @return void
|
||||
*/
|
||||
export const checkStatus = (status: number): void => {
|
||||
switch (status) {
|
||||
case 400:
|
||||
ElMessage.error('请求失败!请您稍后重试');
|
||||
break;
|
||||
case 401:
|
||||
ElMessage.error('登录失效!请您重新登录');
|
||||
break;
|
||||
case 403:
|
||||
ElMessage.error('当前账号无权限访问!');
|
||||
break;
|
||||
case 404:
|
||||
ElMessage.error('你所访问的资源不存在!');
|
||||
break;
|
||||
case 405:
|
||||
ElMessage.error('请求方式错误!请您稍后重试');
|
||||
break;
|
||||
case 408:
|
||||
ElMessage.error('请求超时!请您稍后重试');
|
||||
break;
|
||||
case 500:
|
||||
ElMessage.error('服务异常!');
|
||||
break;
|
||||
case 502:
|
||||
ElMessage.error('网关错误!');
|
||||
break;
|
||||
case 503:
|
||||
ElMessage.error('服务不可用!');
|
||||
break;
|
||||
case 504:
|
||||
ElMessage.error('网关超时!');
|
||||
break;
|
||||
default:
|
||||
ElMessage.error('请求失败!');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,90 @@
|
||||
import axios, { AxiosInstance, AxiosError, AxiosRequestConfig, AxiosResponse } from 'axios';
|
||||
import { showFullScreenLoading, tryHideFullScreenLoading } from '@/config/service-loading';
|
||||
import { AxiosCanceler } from './helper/axios-cancel';
|
||||
import { ResultData } from '@/api/interface';
|
||||
import { ResultEnum } from '@/enums/http-enum';
|
||||
import { checkStatus } from './helper/check-status';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import router from '@/routers';
|
||||
import { GlobalStore } from '@/store';
|
||||
|
||||
const globalStore = GlobalStore();
|
||||
const axiosCanceler = new AxiosCanceler();
|
||||
|
||||
const config = {
|
||||
baseURL: import.meta.env.VITE_API_URL as string,
|
||||
timeout: ResultEnum.TIMEOUT as number,
|
||||
// 跨域时候允许携带凭证
|
||||
withCredentials: true,
|
||||
};
|
||||
|
||||
class RequestHttp {
|
||||
service: AxiosInstance;
|
||||
public constructor(config: AxiosRequestConfig) {
|
||||
this.service = axios.create(config);
|
||||
this.service.interceptors.request.use(
|
||||
(config: AxiosRequestConfig) => {
|
||||
if (config.method != 'get') {
|
||||
config.headers = {
|
||||
'X-CSRF-TOKEN': globalStore.csrfToken,
|
||||
...config.headers,
|
||||
};
|
||||
}
|
||||
axiosCanceler.addPending(config);
|
||||
config.headers!.noLoading || showFullScreenLoading();
|
||||
return {
|
||||
...config,
|
||||
};
|
||||
},
|
||||
(error: AxiosError) => {
|
||||
return Promise.reject(error);
|
||||
},
|
||||
);
|
||||
|
||||
this.service.interceptors.response.use(
|
||||
(response: AxiosResponse) => {
|
||||
const { data, config } = response;
|
||||
if (response.headers['x-csrf-token']) {
|
||||
globalStore.setCsrfToken(response.headers['x-csrf-token']);
|
||||
}
|
||||
axiosCanceler.removePending(config);
|
||||
tryHideFullScreenLoading();
|
||||
if (data.code == ResultEnum.OVERDUE) {
|
||||
ElMessage.error(data.msg);
|
||||
router.replace({
|
||||
path: '/login',
|
||||
});
|
||||
return Promise.reject(data);
|
||||
}
|
||||
if (data.code && data.code !== ResultEnum.SUCCESS) {
|
||||
ElMessage.error(data.msg);
|
||||
return Promise.reject(data);
|
||||
}
|
||||
return data;
|
||||
},
|
||||
async (error: AxiosError) => {
|
||||
const { response } = error;
|
||||
tryHideFullScreenLoading();
|
||||
if (error.message.indexOf('timeout') !== -1) ElMessage.error('请求超时!请您稍后重试');
|
||||
if (response) checkStatus(response.status);
|
||||
if (!window.navigator.onLine) router.replace({ path: '/500' });
|
||||
return Promise.reject(error);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
get<T>(url: string, params?: object, _object = {}): Promise<ResultData<T>> {
|
||||
return this.service.get(url, { params, ..._object });
|
||||
}
|
||||
post<T>(url: string, params?: object, _object = {}): Promise<ResultData<T>> {
|
||||
return this.service.post(url, params, _object);
|
||||
}
|
||||
put<T>(url: string, params?: object, _object = {}): Promise<ResultData<T>> {
|
||||
return this.service.put(url, params, _object);
|
||||
}
|
||||
delete<T>(url: string, params?: any, _object = {}): Promise<ResultData<T>> {
|
||||
return this.service.delete(url, { params, ..._object });
|
||||
}
|
||||
}
|
||||
|
||||
export default new RequestHttp(config);
|
||||
@@ -0,0 +1,58 @@
|
||||
// * 请求响应参数(不包含data)
|
||||
export interface Result {
|
||||
code: number;
|
||||
message: string;
|
||||
}
|
||||
|
||||
// * 请求响应参数(包含data)
|
||||
export interface ResultData<T> {
|
||||
code: number;
|
||||
message: string;
|
||||
data: T;
|
||||
}
|
||||
|
||||
// * 分页响应参数
|
||||
export interface ResPage<T> {
|
||||
items: T[];
|
||||
total: number;
|
||||
}
|
||||
|
||||
// * 分页请求参数
|
||||
export interface ReqPage {
|
||||
page: number;
|
||||
pageSize: number;
|
||||
}
|
||||
export interface CommonModel {
|
||||
id: number;
|
||||
CreatedAt?: string;
|
||||
UpdatedAt?: string;
|
||||
}
|
||||
|
||||
// * 登录模块
|
||||
export namespace Login {
|
||||
export interface ReqLoginForm {
|
||||
name: string;
|
||||
password: string;
|
||||
captcha: string;
|
||||
captchaID: string;
|
||||
authMethod: string;
|
||||
}
|
||||
export interface ResLogin {
|
||||
name: string;
|
||||
token: string;
|
||||
}
|
||||
export interface ResCaptcha {
|
||||
imagePath: string;
|
||||
captchaID: string;
|
||||
captchaLength: number;
|
||||
}
|
||||
export interface ResAuthButtons {
|
||||
[propName: string]: any;
|
||||
}
|
||||
}
|
||||
// * 文件上传模块
|
||||
export namespace Upload {
|
||||
export interface ResFileUrl {
|
||||
fileUrl: string;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import { DateTimeFormats } from '@intlify/core-base';
|
||||
|
||||
export interface ResOperationLog {
|
||||
id: number;
|
||||
group: string;
|
||||
source: string;
|
||||
action: string;
|
||||
ip: string;
|
||||
path: string;
|
||||
method: string;
|
||||
userAgent: string;
|
||||
body: string;
|
||||
resp: string;
|
||||
|
||||
status: number;
|
||||
latency: number;
|
||||
errorMessage: string;
|
||||
|
||||
detail: string;
|
||||
createdAt: DateTimeFormats;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import { CommonModel, ReqPage } from '.';
|
||||
|
||||
export namespace User {
|
||||
export interface User extends CommonModel {
|
||||
name: string;
|
||||
email: string;
|
||||
password: string;
|
||||
}
|
||||
export interface UserCreate {
|
||||
username: string;
|
||||
email: string;
|
||||
}
|
||||
|
||||
export interface ReqGetUserParams extends ReqPage {
|
||||
name?: string;
|
||||
email?: string;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import { Login } from '@/api/interface/index';
|
||||
import http from '@/api';
|
||||
|
||||
export const loginApi = (params: Login.ReqLoginForm) => {
|
||||
return http.post<Login.ResLogin>(`/auth/login`, params);
|
||||
};
|
||||
|
||||
export const getCaptcha = () => {
|
||||
return http.get<Login.ResCaptcha>(`/auth/captcha`);
|
||||
};
|
||||
|
||||
export const logOutApi = () => {
|
||||
return http.post<any>(`/auth/logout`);
|
||||
};
|
||||
@@ -0,0 +1,11 @@
|
||||
import http from '@/api';
|
||||
import { ResPage, ReqPage } from '../interface';
|
||||
import { ResOperationLog } from '../interface/operation-log';
|
||||
|
||||
export const getOperationList = (info: ReqPage) => {
|
||||
return http.post<ResPage<ResOperationLog>>(`/operations`, info);
|
||||
};
|
||||
|
||||
export const deleteOperation = (params: { ids: number[] }) => {
|
||||
return http.post(`/operations/del`, params);
|
||||
};
|
||||
@@ -0,0 +1,17 @@
|
||||
import { Upload } from '@/api/interface/index';
|
||||
import { PORT1 } from '@/api/config/service-port';
|
||||
|
||||
import http from '@/api';
|
||||
|
||||
/**
|
||||
* @name 文件上传模块
|
||||
*/
|
||||
// * 图片上传
|
||||
export const uploadImg = (params: FormData) => {
|
||||
return http.post<Upload.ResFileUrl>(PORT1 + `/file/upload/img`, params);
|
||||
};
|
||||
|
||||
// * 视频上传
|
||||
export const uploadVideo = (params: FormData) => {
|
||||
return http.post<Upload.ResFileUrl>(PORT1 + `/file/upload/video`, params);
|
||||
};
|
||||
@@ -0,0 +1,23 @@
|
||||
import http from '@/api';
|
||||
import { ResPage } from '../interface';
|
||||
import { User } from '../interface/user';
|
||||
|
||||
export const getUserList = (params: User.ReqGetUserParams) => {
|
||||
return http.post<ResPage<User.User>>(`/users/search`, params);
|
||||
};
|
||||
|
||||
export const addUser = (params: User.User) => {
|
||||
return http.post(`/users`, params);
|
||||
};
|
||||
|
||||
export const getUserById = (id: number) => {
|
||||
return http.get<User.User>(`/users/${id}`);
|
||||
};
|
||||
|
||||
export const editUser = (params: User.User) => {
|
||||
return http.put(`/users/` + params.id, params);
|
||||
};
|
||||
|
||||
export const deleteUser = (params: { ids: number[] }) => {
|
||||
return http.post(`/users/del`, params);
|
||||
};
|
||||
@@ -0,0 +1,12 @@
|
||||
@font-face {
|
||||
font-family: YouSheBiaoTiHei;
|
||||
src: url('./YouSheBiaoTiHei.ttf');
|
||||
}
|
||||
@font-face {
|
||||
font-family: MetroDF;
|
||||
src: url('./MetroDF.ttf');
|
||||
}
|
||||
@font-face {
|
||||
font-family: DIN;
|
||||
src: url('./DIN.Otf');
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
@font-face {
|
||||
font-family: iconfont; /* Project id */
|
||||
src: url('iconfont.ttf?t=1656986270860') format('truetype');
|
||||
}
|
||||
.iconfont {
|
||||
font-family: iconfont !important;
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
.icon-zhongyingwen::before {
|
||||
content: '\e605';
|
||||
}
|
||||
.icon-suoxiao::before {
|
||||
content: '\e641';
|
||||
}
|
||||
.icon-fangda::before {
|
||||
content: '\e826';
|
||||
}
|
||||
.icon-contentright::before {
|
||||
content: '\e8c9';
|
||||
}
|
||||
.icon-sousuo::before {
|
||||
content: '\e611';
|
||||
}
|
||||
.icon-zhuti::before {
|
||||
font-size: 22.4px;
|
||||
content: '\e638';
|
||||
}
|
||||
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 7.6 KiB |
|
After Width: | Height: | Size: 7.9 KiB |
|
After Width: | Height: | Size: 6.2 KiB |
|
After Width: | Height: | Size: 723 B |
@@ -0,0 +1,3 @@
|
||||
<?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="1658573860772" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2346" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css">@font-face { font-family: element-icons; src: url("chrome-extension://moombeodfomdpjnpocobemoiaemednkg/fonts/element-icons.woff") format("woff"), url("chrome-extension://moombeodfomdpjnpocobemoiaemednkg/fonts/element-icons.ttf ") format("truetype"); }
|
||||
@font-face { font-family: feedback-iconfont; src: url("//at.alicdn.com/t/font_1031158_u69w8yhxdu.woff2?t=1630033759944") format("woff2"), url("//at.alicdn.com/t/font_1031158_u69w8yhxdu.woff?t=1630033759944") format("woff"), url("//at.alicdn.com/t/font_1031158_u69w8yhxdu.ttf?t=1630033759944") format("truetype"); }
|
||||
</style></defs><path d="M469.333333 298.666667a42.666667 42.666667 0 0 1 42.666667 42.666666v85.333334h85.333333a42.666667 42.666667 0 0 1 42.368 37.674666L640 469.333333a42.666667 42.666667 0 0 1-42.666667 42.666667h-85.333333v85.333333a42.666667 42.666667 0 0 1-37.674667 42.368L469.333333 640a42.666667 42.666667 0 0 1-42.666666-42.666667v-85.333333H341.333333a42.666667 42.666667 0 0 1-42.368-37.674667L298.666667 469.333333a42.666667 42.666667 0 0 1 42.666666-42.666666h85.333334V341.333333a42.666667 42.666667 0 0 1 37.674666-42.368L469.333333 298.666667z m0-128a298.666667 298.666667 0 1 0 207.573334 513.408l3.285333-3.84a40.448 40.448 0 0 1 3.797333-3.370667A298.666667 298.666667 0 0 0 469.333333 170.666667z m0-85.333334a384 384 0 0 1 384 384 382.122667 382.122667 0 0 1-83.968 239.658667l156.8 156.842667a42.624 42.624 0 1 1-60.330666 60.330666l-156.842667-156.8A382.122667 382.122667 0 0 1 469.333333 853.333333c-212.096 0-384-171.904-384-384s171.904-384 384-384z" p-id="2347" fill="#1d1d1c"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.9 KiB |
@@ -0,0 +1,3 @@
|
||||
<?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="1658573860772" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2346" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css">@font-face { font-family: element-icons; src: url("chrome-extension://moombeodfomdpjnpocobemoiaemednkg/fonts/element-icons.woff") format("woff"), url("chrome-extension://moombeodfomdpjnpocobemoiaemednkg/fonts/element-icons.ttf ") format("truetype"); }
|
||||
@font-face { font-family: feedback-iconfont; src: url("//at.alicdn.com/t/font_1031158_u69w8yhxdu.woff2?t=1630033759944") format("woff2"), url("//at.alicdn.com/t/font_1031158_u69w8yhxdu.woff?t=1630033759944") format("woff"), url("//at.alicdn.com/t/font_1031158_u69w8yhxdu.ttf?t=1630033759944") format("truetype"); }
|
||||
</style></defs><path d="M469.333333 298.666667a42.666667 42.666667 0 0 1 42.666667 42.666666v85.333334h85.333333a42.666667 42.666667 0 0 1 42.368 37.674666L640 469.333333a42.666667 42.666667 0 0 1-42.666667 42.666667h-85.333333v85.333333a42.666667 42.666667 0 0 1-37.674667 42.368L469.333333 640a42.666667 42.666667 0 0 1-42.666666-42.666667v-85.333333H341.333333a42.666667 42.666667 0 0 1-42.368-37.674667L298.666667 469.333333a42.666667 42.666667 0 0 1 42.666666-42.666666h85.333334V341.333333a42.666667 42.666667 0 0 1 37.674666-42.368L469.333333 298.666667z m0-128a298.666667 298.666667 0 1 0 207.573334 513.408l3.285333-3.84a40.448 40.448 0 0 1 3.797333-3.370667A298.666667 298.666667 0 0 0 469.333333 170.666667z m0-85.333334a384 384 0 0 1 384 384 382.122667 382.122667 0 0 1-83.968 239.658667l156.8 156.842667a42.624 42.624 0 1 1-60.330666 60.330666l-156.842667-156.8A382.122667 382.122667 0 0 1 469.333333 853.333333c-212.096 0-384-171.904-384-384s171.904-384 384-384z" p-id="2347" fill="#ffffff"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 3.0 KiB |
@@ -0,0 +1,33 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" baseProfile="full" width="100%" height="100%" viewBox="0 0 1400 800">
|
||||
|
||||
<rect x="1300" y="400" rx="40" ry="40" width="150" height="150" stroke="rgb(129, 201, 149)" fill="rgb(129, 201, 149)">
|
||||
<animateTransform attributeType="XML" attributeName="transform" begin="0s" dur="35s" type="rotate" from="0 1450 550" to="360 1450 550" repeatCount="indefinite"/>
|
||||
</rect>
|
||||
|
||||
<path d="M 100 350 A 150 150 0 1 1 400 350 Q400 370 380 370 L 250 370 L 120 370 Q100 370 100 350" fill="#a2b3ff">
|
||||
<animateMotion path="M 800 -200 L 800 -300 L 800 -200" dur="20s" begin="0s" repeatCount="indefinite"/>
|
||||
<animateTransform attributeType="XML" attributeName="transform" begin="0s" dur="30s" type="rotate" values="0 210 530 ; -30 210 530 ; 0 210 530" keyTimes="0 ; 0.5 ; 1" repeatCount="indefinite"/>
|
||||
</path>
|
||||
|
||||
<circle cx="150" cy="150" r="180" stroke="#85FFBD" fill="#85FFBD">
|
||||
<animateMotion path="M 0 0 L 40 20 Z" dur="5s" repeatCount="indefinite"/>
|
||||
</circle>
|
||||
|
||||
<!-- 三角形 -->
|
||||
<path d="M 165 580 L 270 580 Q275 578 270 570 L 223 483 Q220 480 217 483 L 165 570 Q160 578 165 580" fill="#a2b3ff">
|
||||
<animateTransform attributeType="XML" attributeName="transform" begin="0s" dur="35s" type="rotate" from="0 210 530" to="360 210 530" repeatCount="indefinite"/>
|
||||
</path>
|
||||
|
||||
<!-- <circle cx="1200" cy="600" r="30" stroke="rgb(241, 243, 244)" fill="rgb(241, 243, 244)">-->
|
||||
<!-- <animateMotion path="M 0 0 L -20 40 Z" dur="9s" repeatCount="indefinite"/>-->
|
||||
<!-- </circle>-->
|
||||
|
||||
<path d="M 100 350 A 40 40 0 1 1 180 350 L 180 430 A 40 40 0 1 1 100 430 Z" fill="#3054EB">
|
||||
<animateMotion path="M 140 390 L 180 360 L 140 390" dur="20s" begin="0s" repeatCount="indefinite"/>
|
||||
<animateTransform attributeType="XML" attributeName="transform" begin="0s" dur="30s" type="rotate" values="0 140 390; -60 140 390; 0 140 390" keyTimes="0 ; 0.5 ; 1" repeatCount="indefinite"/>
|
||||
</path>
|
||||
|
||||
<rect x="400" y="600" rx="40" ry="40" width="100" height="100" stroke="rgb(129, 201, 149)" fill="#3054EB">
|
||||
<animateTransform attributeType="XML" attributeName="transform" begin="0s" dur="35s" type="rotate" from="-30 550 750" to="330 550 750" repeatCount="indefinite"/>
|
||||
</rect>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 98 KiB |
@@ -0,0 +1,123 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="600px" height="400px" viewBox="0 0 600 400" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>搭建网站</title>
|
||||
<g id="搭建网站" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="plant_14_" transform="translate(460.407080, 84.286076)" fill-rule="nonzero">
|
||||
<path d="M6.00259646,105.330035 C6.00259646,105.330035 1.84230973,-7.8292962 69.9789416,0.726420253 C77.8013257,4.31886582 64.1880531,16.8041013 56.9170106,19.4970835 C47.509069,22.9804759 53.7747239,35.7991797 43.601931,40.9868658 C31.1228726,47.3507038 34.275069,56.1659848 32.8093292,70.5177418 C31.7543929,80.8417418 10.7340442,87.5300354 6.00259646,105.330035 Z" id="Path" fill="#F3F9FF"></path>
|
||||
<path d="M0.51440531,138.928324 C0.51440531,138.928324 38.1236496,40.5109975 92.8416,86.7062785 C98.1748389,94.224638 82.0814301,97.0428962 74.9320071,95.1736709 C65.6808195,92.7546734 66.3384655,107.213681 55.8188319,105.804101 C42.9145558,104.074572 42.3848354,113.35761 35.9227876,124.700041 C31.2733204,132.860101 11.0007062,126.525104 0.51440531,138.928324 Z" id="Path" fill="#E7F4FE"></path>
|
||||
<path d="M8.45390442,132.267068 C8.45390442,132.267068 18.6906602,6.36023291 81.8670195,35.9181468 C87.3029593,39.0338228 77.5076372,49.6381165 68.456446,50.3645367 C59.4061558,51.090957 62.246646,68.606157 55.6071239,78.2605165 C48.237885,88.9774684 34.5723611,87.3542886 32.0372708,103.647372 C30.2147805,115.370137 14.933069,112.333772 8.45390442,132.267068 Z" id="Path" fill="#DCEEFD"></path>
|
||||
</g>
|
||||
<g id="cog_4_" transform="translate(291.941593, 205.055696)" fill="#F3F9FF" fill-rule="nonzero">
|
||||
<path d="M120.51048,32.7700253 C119.524011,30.3879797 116.994326,29.0342785 114.467343,29.534481 L106.4828,31.1144 C104.59995,31.4875241 102.652237,30.8314025 101.381088,29.3938835 C100.034265,27.8716456 98.6072637,26.4386329 97.1054885,25.1011544 C95.6739823,23.8267646 95.0253451,21.8881418 95.3965097,20.0072 L96.9739593,12.0328 C97.4739504,9.5038481 96.1199204,6.97309367 93.7397823,5.98710886 L81.9508018,1.1022481 C79.5706637,0.115362025 76.8247664,0.94723038 75.3905575,3.08953924 L70.8690159,9.84362532 C69.8041699,11.4343595 67.970869,12.348243 66.060092,12.2373873 C64.0529204,12.1202228 62.0313345,12.1247291 60.0034425,12.2482025 C58.0854584,12.3644658 56.2494549,11.4523848 55.1801044,9.85534177 L50.6513558,3.08953924 C49.2171469,0.94723038 46.4712496,0.116263291 44.0911115,1.1022481 L32.3030319,5.98710886 C29.9219929,6.97399494 28.5688637,9.5038481 29.0688549,12.0328 L30.6490071,20.020719 C31.0210726,21.9043646 30.3661292,23.852 28.9292177,25.1245873 C27.407623,26.4719797 25.9752159,27.8995848 24.6383027,29.4019949 C23.3644513,30.8341063 21.4257469,31.4830177 19.5465009,31.1116962 L11.5754708,29.534481 C9.04758761,29.0342785 6.51790265,30.388881 5.53233451,32.7700253 L0.649538053,44.5639899 C-0.336930973,46.9451342 0.494585841,49.6921924 2.63598938,51.1270076 L9.38722124,55.6495595 C10.9772832,56.7148557 11.8907805,58.5489316 11.7790708,60.4605165 C11.6628566,62.4685367 11.6664602,64.4918785 11.7898814,66.5206278 C11.9069965,68.4394228 10.9944,70.2771038 9.39803186,71.3460051 L2.6350885,75.8766684 C0.493684956,77.3105823 -0.336930973,80.0585418 0.648637168,82.4396861 L5.53143363,94.2327494 C6.51790265,96.6147949 9.04668673,97.9684962 11.5745699,97.4682937 L19.5591133,95.8874734 C21.4419628,95.5152506 23.3896761,96.1704709 24.6608248,97.6079899 C26.0076478,99.1302278 27.4346496,100.563241 28.9364248,101.900719 C30.367931,103.175109 31.017469,105.114633 30.6454035,106.994673 L29.0688549,114.968172 C28.5688637,117.497124 29.9219929,120.027878 32.3030319,121.014765 L44.0911115,125.899625 C46.4721504,126.886511 49.2171469,126.054643 50.6513558,123.912334 L55.1719965,117.158248 C56.2368425,115.567514 58.0701434,114.65363 59.9809204,114.764486 C61.988092,114.881651 64.0096779,114.877144 66.0384708,114.753671 C67.9564549,114.637408 69.7924584,115.548587 70.8618088,117.146532 L75.3905575,123.912334 C76.8247664,126.053742 79.5706637,126.88561 81.9508018,125.899625 L93.7388814,121.014765 C96.1199204,120.027878 97.4730496,117.498025 96.9730584,114.969073 L95.3929062,106.981154 C95.0208407,105.097509 95.6757841,103.149873 97.1126956,101.877286 C98.6342903,100.529894 100.066697,99.1022886 101.403611,97.5998785 C102.677462,96.1677671 104.616166,95.5188557 106.496313,95.8901772 L114.467343,97.4673924 C116.995227,97.9675949 119.524912,96.6138937 120.51048,94.2318481 L125.393276,82.4387848 C126.378844,80.0567392 125.548228,77.309681 123.406825,75.8757671 L116.655593,71.3532152 C115.065531,70.287919 114.152034,68.453843 114.262842,66.5422582 C114.379958,64.534238 114.375453,62.5117975 114.252032,60.4830481 C114.135818,58.5642532 115.047513,56.7265722 116.643881,55.6576709 L123.406825,51.1270076 C125.547327,49.6921924 126.378844,46.9451342 125.393276,44.5639899 L120.51048,32.7700253 Z M73.3635664,88.4835747 C59.5719186,94.1985013 43.7613876,87.6462987 38.0488761,73.8497215 C32.3363646,60.052243 38.8857982,44.2350278 52.6765451,38.5201013 C66.4681929,32.8051747 82.2787239,39.3573772 87.9912354,53.1539544 C93.7037469,66.9505316 87.1552142,82.7686481 73.3635664,88.4835747 Z" id="Shape"></path>
|
||||
</g>
|
||||
<g id="window" transform="translate(116.269027, 38.321519)" fill-rule="nonzero">
|
||||
<g id="Group">
|
||||
<rect id="Rectangle" fill="#F3F9FF" x="11.4115097" y="11.7164557" width="342.937173" height="202.78481"></rect>
|
||||
<path d="M355.838747,0.450632911 L9.92054513,0.450632911 C4.52424425,0.450632911 0.150447788,4.82627848 0.150447788,10.2248608 L0.150447788,215.992861 C0.150447788,221.391443 4.52424425,225.767089 9.92054513,225.767089 L355.839648,225.767089 C361.235949,225.767089 365.609745,221.391443 365.609745,215.992861 L365.609745,10.2248608 C365.609745,4.82627848 361.235048,0.450632911 355.838747,0.450632911 Z M354.348683,214.501266 L11.4115097,214.501266 L11.4115097,11.7164557 L354.348683,11.7164557 L354.348683,214.501266 L354.348683,214.501266 Z" id="Shape" fill="#224380"></path>
|
||||
</g>
|
||||
<g id="Group" transform="translate(25.224779, 23.432911)">
|
||||
<path d="M307.56843,179.926005 L119.367257,179.926005 C116.879913,179.926005 114.862832,177.908071 114.862832,175.419676 L114.862832,9.01265823 C114.862832,6.52426329 116.879913,4.50632911 119.367257,4.50632911 L307.56843,4.50632911 C310.055773,4.50632911 312.072855,6.52426329 312.072855,9.01265823 L312.072855,175.419676 C312.072855,177.908071 310.055773,179.926005 307.56843,179.926005 Z" id="Path" fill="#DAE7FF"></path>
|
||||
<path d="M307.56843,175.419676 L119.367257,175.419676 C116.879913,175.419676 114.862832,173.401742 114.862832,170.913347 L114.862832,4.50632911 C114.862832,2.01793418 116.879913,0 119.367257,0 L307.56843,0 C310.055773,0 312.072855,2.01793418 312.072855,4.50632911 L312.072855,170.913347 C312.072855,173.401742 310.055773,175.419676 307.56843,175.419676 Z" id="Path" fill="#FFFFFF"></path>
|
||||
<path d="M101.296405,44.7667747 L5.21161947,44.7667747 C2.72427611,44.7667747 0.70719469,42.7488405 0.70719469,40.2604456 L0.70719469,9.01265823 C0.70719469,6.52426329 2.72427611,4.50632911 5.21161947,4.50632911 L101.296405,4.50632911 C103.783749,4.50632911 105.80083,6.52426329 105.80083,9.01265823 L105.80083,40.2604456 C105.80083,42.7497418 103.78465,44.7667747 101.296405,44.7667747 Z" id="Path" fill="#DAE7FF"></path>
|
||||
<path d="M101.296405,40.2604456 L5.21161947,40.2604456 C2.72427611,40.2604456 0.70719469,38.2425114 0.70719469,35.7541165 L0.70719469,4.50632911 C0.70719469,2.01793418 2.72427611,0 5.21161947,0 L101.296405,0 C103.783749,0 105.80083,2.01793418 105.80083,4.50632911 L105.80083,35.7541165 C105.80083,38.2434127 103.78465,40.2604456 101.296405,40.2604456 Z" id="Path" fill="#FFFFFF"></path>
|
||||
<path d="M101.296405,106.279068 L5.21161947,106.279068 C2.72427611,106.279068 0.70719469,104.261134 0.70719469,101.772739 L0.70719469,70.5249519 C0.70719469,68.036557 2.72427611,66.0186228 5.21161947,66.0186228 L101.296405,66.0186228 C103.783749,66.0186228 105.80083,68.036557 105.80083,70.5249519 L105.80083,101.772739 C105.80083,104.261134 103.78465,106.279068 101.296405,106.279068 Z" id="Path" fill="#DAE7FF"></path>
|
||||
<path d="M101.296405,101.772739 L5.21161947,101.772739 C2.72427611,101.772739 0.70719469,99.7548051 0.70719469,97.2664101 L0.70719469,66.0186228 C0.70719469,63.5302278 2.72427611,61.5122937 5.21161947,61.5122937 L101.296405,61.5122937 C103.783749,61.5122937 105.80083,63.5302278 105.80083,66.0186228 L105.80083,97.2664101 C105.80083,99.7548051 103.78465,101.772739 101.296405,101.772739 Z" id="Path" fill="#FFFFFF"></path>
|
||||
<path d="M226.222122,22.7272203 L133.116563,22.7272203 C132.121085,22.7272203 131.314793,21.9205873 131.314793,20.9246886 L131.314793,13.3116962 C131.314793,12.3157975 132.121085,11.5091646 133.116563,11.5091646 L226.222122,11.5091646 C227.2176,11.5091646 228.023892,12.3157975 228.023892,13.3116962 L228.023892,20.9246886 C228.023892,21.9205873 227.2176,22.7272203 226.222122,22.7272203 Z" id="Path" fill="#1B90FB"></path>
|
||||
<path d="M170.846526,80.5434228 L133.117464,80.5434228 C132.121986,80.5434228 131.315694,79.7367899 131.315694,78.7408911 L131.315694,71.1278987 C131.315694,70.132 132.121986,69.3253671 133.117464,69.3253671 L170.846526,69.3253671 C171.842004,69.3253671 172.648296,70.132 172.648296,71.1278987 L172.648296,78.7408911 C172.648296,79.7358886 171.841103,80.5434228 170.846526,80.5434228 Z" id="Path" fill="#7266D5"></path>
|
||||
<g transform="translate(130.628319, 29.741772)" fill="#DCEEFD" id="Path">
|
||||
<path d="M163.352064,3.56540759 L2.32608496,3.56540759 C1.42069558,3.56540759 0.686474336,2.83087595 0.686474336,1.9251038 L0.686474336,1.91879494 C0.686474336,1.01302278 1.42069558,0.278491139 2.32608496,0.278491139 L163.352064,0.278491139 C164.257453,0.278491139 164.991674,1.01302278 164.991674,1.91879494 L164.991674,1.9251038 C164.991674,2.83177722 164.258354,3.56540759 163.352064,3.56540759 Z"></path>
|
||||
<path d="M163.352064,13.1404557 L2.32608496,13.1404557 C1.42069558,13.1404557 0.686474336,12.4059241 0.686474336,11.5001519 L0.686474336,11.493843 C0.686474336,10.5880709 1.42069558,9.85353924 2.32608496,9.85353924 L163.352064,9.85353924 C164.257453,9.85353924 164.991674,10.5880709 164.991674,11.493843 L164.991674,11.5001519 C164.991674,12.4059241 164.258354,13.1404557 163.352064,13.1404557 Z"></path>
|
||||
<path d="M163.352064,22.7155038 L2.32608496,22.7155038 C1.42069558,22.7155038 0.686474336,21.9809722 0.686474336,21.0752 L0.686474336,21.0688911 C0.686474336,20.163119 1.42069558,19.4285873 2.32608496,19.4285873 L163.352064,19.4285873 C164.257453,19.4285873 164.991674,20.163119 164.991674,21.0688911 L164.991674,21.0752 C164.991674,21.9809722 164.258354,22.7155038 163.352064,22.7155038 Z"></path>
|
||||
<path d="M63.241223,32.2896506 L2.32608496,32.2896506 C1.42069558,32.2896506 0.686474336,31.555119 0.686474336,30.6493468 L0.686474336,30.643038 C0.686474336,29.7372658 1.42069558,29.0027342 2.32608496,29.0027342 L63.241223,29.0027342 C64.1466124,29.0027342 64.8808336,29.7372658 64.8808336,30.643038 L64.8808336,30.6493468 C64.8808336,31.5560203 64.1475133,32.2896506 63.241223,32.2896506 Z"></path>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="chevron_arrow_2_" transform="translate(61.315044, 96.903797)" fill-rule="nonzero">
|
||||
<polygon id="Path" fill="#F3F9FF" points="19.8068566 71.8750481 38.8335469 90.9097823 38.8335469 104.425165 19.8068566 85.3904304 0.781067257 104.425165 0.781067257 90.9097823"></polygon>
|
||||
<polygon id="Path" fill="#E7F4FE" points="19.8068566 48.0825316 38.8335469 67.1163646 38.8335469 80.6317468 19.8068566 61.5970127 0.781067257 80.6317468 0.781067257 67.1163646"></polygon>
|
||||
<polygon id="Path" fill="#DCEEFD" points="19.8068566 24.2891139 38.8335469 43.3238481 38.8335469 56.8383291 19.8068566 37.8035949 0.781067257 56.8383291 0.781067257 43.3238481"></polygon>
|
||||
<polygon id="Path" fill="#D0E7FE" points="19.8068566 0.495696203 38.8335469 19.5304304 38.8335469 33.0449114 19.8068566 14.0110785 0.781067257 33.0449114 0.781067257 19.5304304"></polygon>
|
||||
</g>
|
||||
<g id="woman_7_" transform="translate(138.791150, 38.321519)" fill-rule="nonzero">
|
||||
<rect id="Rectangle" fill="#0D1F42" x="8.16652212" y="0.450632911" width="2.57022478" height="156.781499"></rect>
|
||||
<g id="Group" transform="translate(9.008850, 72.101266)">
|
||||
<path d="M76.4626106,27.1326076 L65.7285664,27.1326076 C65.2510973,27.1326076 64.8637168,26.7450633 64.8637168,26.2673924 L64.8637168,26.2628861 C64.8637168,25.7852152 65.2510973,25.3976709 65.7285664,25.3976709 L76.4626106,25.3976709 L78.714823,26.2646886 L76.4626106,27.1326076 Z" id="Path" fill="#224380"></path>
|
||||
<polygon id="Path" fill="#F1B282" points="23.8941717 111.707392 24.5590248 114.999716 20.2473894 115.60717 18.9420071 112.501408"></polygon>
|
||||
<path d="M24.6527168,114.604962 C24.6527168,114.604962 20.5636,115.675666 19.2212814,115.806349 C17.8789628,115.937033 15.6015257,118.817478 14.2249735,119.255494 C12.5547327,119.786339 12.7511257,121.708739 14.0159681,122.049418 C15.802423,122.529792 26.1932301,122.361256 26.2202566,118.839109 C26.2481841,115.10877 24.6527168,114.604962 24.6527168,114.604962 Z" id="Path" fill="#102B5E"></path>
|
||||
<polygon id="Path" fill="#F1B282" points="31.0661168 110.626775 29.2409239 113.484689 33.2084212 115.548587 35.5894602 113.08723"></polygon>
|
||||
<path d="M12.8195929,82.2080608 C11.4817788,83.8438582 15.0916248,112.72402 17.2681628,114.131797 C19.4447009,115.539575 24.9346938,112.509519 24.9346938,112.509519 C24.9346938,112.509519 28.4364336,85.8059139 26.8274531,82.2071595 C25.2193735,78.6102076 14.1574071,80.5722633 12.8195929,82.2080608 Z" id="Path" fill="#0B224E"></path>
|
||||
<path d="M59.1998531,83.8177215 C57.5908726,74.8221873 38.5470655,67.4354127 33.2084212,67.1515139 C27.869777,66.8676152 8.66921593,73.2828253 7.34221239,74.5139544 C6.01520885,75.7450835 5.43684071,85.1434835 17.1744708,84.8595848 C28.9121009,84.5756861 34.4011929,82.8713924 34.4011929,82.8713924 C34.4011929,82.8713924 41.3794478,85.7013671 45.7226142,87.4930835 C41.6380018,92.0300557 30.3481115,105.116435 30.4255876,111.090025 C30.8039593,112.794319 35.2525292,114.594147 36.4831381,114.025448 C37.7137469,113.456749 60.8097345,92.814157 59.1998531,83.8177215 Z" id="Path" fill="#102B5E"></path>
|
||||
<path d="M29.1949788,24.0187342 C29.1949788,24.0187342 27.3968124,30.6475443 26.2607965,32.1625722 C25.1247805,33.6776 34.0228212,35.9505924 34.9687504,35.003362 C35.9146796,34.0561316 36.1047664,27.6174886 36.1047664,27.6174886 C36.1047664,27.6174886 40.837115,28.7467747 42.0677239,27.7328506 C43.2983327,26.7189266 43.6767044,24.8713316 43.7712973,24.3035342 C43.8658903,23.7357367 45.5694637,23.1201722 45.5694637,22.2675747 C45.5694637,21.4149772 44.7172265,15.5360203 43.0614,14.1715038 C42.1406956,13.4135392 31.4661097,10.0995848 30.4255876,16.1596962 C29.3841646,22.2198076 29.1949788,24.0187342 29.1949788,24.0187342 Z" id="Path" fill="#F1B282"></path>
|
||||
<path d="M30.2364018,25.7239291 C33.1705841,25.7239291 37.9984265,24.8713316 37.9984265,21.9359089 C37.9984265,19.0004861 34.2120071,14.7393013 37.9984265,15.2124658 C41.784846,15.6856304 44.7172265,15.6901367 44.7190283,11.7092456 C44.7208301,6.21693165 30.3192832,0.219007595 25.1256814,12.7420962 C21.5284478,21.4149772 26.4725044,25.7239291 30.2364018,25.7239291 Z" id="Path" fill="#102B5E"></path>
|
||||
<path d="M62.1601611,33.5811646 C62.1601611,33.5811646 65.9087434,29.8958886 66.3483752,29.365043 C68.3321239,26.9685772 67.5024088,24.0358582 70.2699274,24.0358582 C71.7906212,24.0358582 74.5076903,25.177762 74.5860673,27.0956557 C74.6203009,27.9221165 74.8950708,30.9557772 72.4284478,31.3442228 C70.3645204,31.6686785 64.8330867,37.3926177 64.8330867,37.3926177 L62.1601611,33.5811646 Z" id="Path" fill="#F1B282"></path>
|
||||
<path d="M67.5258319,34.2661266 C67.0528673,31.2360709 63.5376142,30.7475848 61.4682814,32.6564658 C60.806131,33.2675241 52.9008655,37.2925772 49.451377,39.8017013 C46.444223,35.9731241 38.7758903,29.748081 29.067954,29.748081 C15.2808106,29.748081 0.136033628,43.2896 0.136033628,52.6636658 C0.136033628,59.1960405 3.8386708,59.6340557 7.24311504,58.6588861 C6.12151327,63.0868051 4.64135929,70.6421165 5.90800354,75.9586835 C6.88005841,80.0396152 18.2149929,75.200719 22.7581558,74.5382886 C27.3013186,73.8758582 36.6723239,74.159757 36.2939522,69.5200405 C35.9155805,64.8803241 34.9939752,50.2086177 42.9109522,50.565519 C46.5081858,50.565519 47.1730389,53.0430987 50.3027133,53.0430987 C56.2656708,53.0421975 68.1915858,38.5273114 67.5258319,34.2661266 Z" id="Path" fill="#1B90FB"></path>
|
||||
<path d="M26.5923221,6.78472911 C26.5923221,6.78472911 26.7724991,0.0612860759 17.1744708,0.0612860759 C-2.32428319,0.0612860759 5.43774159,18.2425215 5.15306195,27.7121215 C4.8683823,37.1817215 0.326120354,36.5183899 0.136033628,39.8332456 C-0.0531522124,43.1472 18.5933646,40.4956759 19.9185664,27.6174886 C21.2437681,14.7393013 22.1896973,8.80716962 24.9355947,9.90941772 C27.681492,11.0125671 26.5923221,6.78472911 26.5923221,6.78472911 Z" id="Path" fill="#102B5E"></path>
|
||||
<path d="M29.1526372,5.0948557 C31.0309823,5.98891139 26.6860142,13.8064911 24.0518265,12.785357 C21.4176389,11.7642228 26.7040319,3.92951899 29.1526372,5.0948557 Z" id="Path" fill="#1B90FB"></path>
|
||||
<path d="M7.22059292,58.7472101 C7.22059292,58.7472101 10.453869,46.5089215 15.1393717,43.3842329 C15.1393717,43.3842329 9.3178531,58.7472101 5.51161416,73.4017924 C5.51161416,73.4017924 5.26567257,64.6342785 7.22059292,58.7472101 Z" id="Path" fill="#1277D7"></path>
|
||||
<path d="M45.5703646,87.6625215 L48.2676142,88.5529722 C48.2676142,88.5529722 41.6551186,94.9123038 37.5551912,97.3430177 C37.5551912,97.3430177 43.5803097,88.4961924 45.5703646,87.6625215 Z" id="Path" fill="#0B224E"></path>
|
||||
<path d="M29.2265097,112.703291 C29.2265097,112.703291 33.6705752,115.496314 35.2291062,116.174066 C36.7867363,116.852719 38.2443681,120.878673 39.7065044,121.903413 C41.4803469,123.146258 40.3875735,125.147068 38.7137292,125.010076 C36.3489062,124.816304 23.9184956,120.478511 25.4554053,116.657144 C27.0815027,112.610461 29.2265097,112.703291 29.2265097,112.703291 Z" id="Path" fill="#102B5E"></path>
|
||||
</g>
|
||||
<path d="M4.07650442,162.738866 L70.4573115,162.738866 C72.2392619,162.738866 73.6833805,161.294137 73.6833805,159.511433 L73.6833805,159.206805 C73.6833805,157.424101 72.2392619,155.979372 70.4573115,155.979372 L4.07650442,155.979372 C2.29455398,155.979372 0.850435398,157.424101 0.850435398,159.206805 L0.850435398,159.511433 C0.850435398,161.294137 2.29455398,162.738866 4.07650442,162.738866 Z" id="Path" fill="#0D1F42"></path>
|
||||
<rect id="Rectangle" fill="#0D1F42" x="65.3069522" y="0.450632911" width="2.57022478" height="156.781499"></rect>
|
||||
</g>
|
||||
<g id="man_on_ladder_1_" transform="translate(377.525664, 121.237975)" fill-rule="nonzero">
|
||||
<rect id="Rectangle" fill="#183168" x="46.2235062" y="14.6356557" width="41.7163788" height="3.60506329"></rect>
|
||||
<rect id="Rectangle" fill="#183168" x="45.943331" y="41.523119" width="41.0064814" height="3.60506329"></rect>
|
||||
<rect id="Rectangle" fill="#183168" x="46.2235062" y="76.2317671" width="42.0704265" height="3.60506329"></rect>
|
||||
<rect id="Rectangle" fill="#183168" x="52.0098903" y="107.820233" width="35.6840531" height="3.60506329"></rect>
|
||||
<rect id="Rectangle" fill="#183168" x="46.3613416" y="206.888273" width="41.3605292" height="3.60506329"></rect>
|
||||
<rect id="Rectangle" fill="#183168" x="46.3613416" y="239.241013" width="41.3605292" height="3.60506329"></rect>
|
||||
<rect id="Rectangle" fill="#183168" x="46.5784549" y="138.928324" width="41.3605292" height="3.60506329"></rect>
|
||||
<rect id="Rectangle" fill="#183168" x="86.2282035" y="0.89315443" width="3.60353982" height="256.868871"></rect>
|
||||
<rect id="Rectangle" fill="#183168" x="45.4856814" y="0.89315443" width="3.60353982" height="256.868871"></rect>
|
||||
<g id="Group" transform="translate(0.000000, 14.420253)">
|
||||
<path d="M11.6682619,19.8963443 L12.5195982,18.2290025 C12.8105841,17.6585013 12.8628354,17.0095899 12.6646407,16.4003342 C12.4673469,15.7919797 12.0448319,15.2962835 11.4754726,15.0051747 L1.85131858,10.0887696 C1.08556637,9.69762025 0.781067257,8.75669873 1.17115044,7.99062278 L3.18823186,4.03857215 C3.37831858,3.66725063 3.7008354,3.39236456 4.09632389,3.26438481 C4.49271327,3.13460253 4.91703009,3.1670481 5.28549204,3.35901772 L8.88452743,5.1976 L9.26470088,4.45225316 L5.66566549,2.61367089 C5.09360354,2.32526582 4.44676814,2.27118987 3.8386708,2.46946835 C3.22967257,2.66684557 2.73508673,3.08953924 2.44410088,3.65913924 L0.427019469,7.61209114 C-0.172969912,8.7891443 0.295490265,10.2347747 1.47114513,10.835919 L11.0952991,15.7523241 C11.4664637,15.9406886 11.7412336,16.2633418 11.8691593,16.6598987 C11.997085,17.0564557 11.9637522,17.4791494 11.7745664,17.8495696 L10.9232301,19.5169114 L11.6682619,19.8963443 Z" id="Path" fill="#4D78CC"></path>
|
||||
<path d="M6.84762655,8.24027342 L23.1878779,16.5868962 C24.2734442,17.1411747 25.6022496,16.7103696 26.1571947,15.6243443 L27.8373451,12.3320203 C28.3913894,11.2459949 27.9607664,9.91662785 26.8752,9.3614481 L10.5349487,1.01482532 C9.4493823,0.460546835 8.12057699,0.891351899 7.56563186,1.97737722 L5.88548142,5.26970127 C5.33143717,6.35662785 5.76206018,7.68599494 6.84762655,8.24027342 Z" id="Path" fill="#7266D5"></path>
|
||||
<path d="M9.85297876,27.6463291 L13.5087699,20.4830684 C14.0628142,19.397043 13.6321912,18.0676759 12.5466248,17.5124962 L12.3385204,17.4061468 C11.252954,16.8518684 9.92414867,17.2826734 9.36920354,18.3686987 L5.71341239,25.5319595 C5.15936814,26.6179848 5.58999115,27.9473519 6.67555752,28.5025316 L6.88366195,28.608881 C7.96922832,29.1631595 9.29893451,28.7323544 9.85297876,27.6463291 Z" id="Path" fill="#224380"></path>
|
||||
</g>
|
||||
<rect id="Rectangle" fill="#183168" x="48.0324832" y="174.534633" width="41.3605292" height="3.60506329"></rect>
|
||||
<path d="M62.5376319,165.929347 L64.1970619,170.75923 C64.1970619,170.75923 67.5699752,173.811818 69.317692,169.460506 C69.317692,169.460506 68.7744584,166.525084 68.5762637,165.453478 C68.378069,164.380972 62.5376319,165.929347 62.5376319,165.929347 Z" id="Path" fill="#F1B282"></path>
|
||||
<path d="M69.8465115,166.232172 L61.3826973,166.232172 C61.3826973,166.232172 53.460315,135.112365 51.2567504,126.868486 C49.5207451,120.375767 47.5027628,98.9157266 48.3036496,88.9197873 C49.0676,79.3898025 65.2727186,84.5513519 66.1177487,91.2441519 C68.5960832,110.863808 63.9835522,125.415646 64.220485,128.846765 C68.2411345,139.022056 69.8465115,166.232172 69.8465115,166.232172 Z" id="Path" fill="#102B5E"></path>
|
||||
<path d="M46.6280035,27.6598481 C45.6045982,29.859838 46.1063912,31.0576203 45.8550442,31.9841215 C45.6036973,32.9115241 44.6424531,34.7825519 44.6019133,35.6702987 C44.5613735,36.5580456 45.5036991,36.6788152 45.7072991,37.8387443 C45.9108991,38.9986734 45.3766743,40.5100962 46.083869,41.7259038 C46.7910637,42.9417114 49.5351593,42.6605165 49.373,43.8339646 C49.2117416,45.0074127 47.4820425,49.3623291 47.4820425,49.3623291 L58.7656265,49.5434835 C58.7656265,49.5434835 58.3151841,43.204881 58.8890478,40.5849013 C59.4629115,37.9649215 60.7349611,28.3394025 58.3917593,27.1722633 C56.0485575,26.0051241 47.7144708,25.3246684 46.6280035,27.6598481 Z" id="Path" fill="#F1B282"></path>
|
||||
<path d="M52.8224885,22.6695392 C62.2097097,22.9624506 65.5033451,25.3291747 65.3961398,32.667281 C65.332177,37.0411241 62.9196071,43.0949266 59.7520956,43.0751473 C57.4683522,43.0606785 52.9657292,42.8065215 52.176554,41.5582684 C50.3036142,38.5949063 51.9071894,34.5040608 50.6522566,33.0917772 C48.8685044,31.0846582 44.815423,32.1184101 45.3721699,25.2038987 C45.9289168,18.2893873 49.6982195,22.5713013 52.8224885,22.6695392 Z" id="Path" fill="#0B224E"></path>
|
||||
<path d="M74.1401292,20.5768 C74.1401292,20.5768 75.7401009,16.118238 75.4545204,13.7343899 C74.9941681,9.89319494 65.856492,9.87607089 65.4628053,13.202643 C65.0456956,16.7347038 66.6916124,17.6585013 67.7663681,18.5804962 C68.8411239,19.5024911 70.069931,21.115757 70.3771327,21.7304203 C70.6843345,22.3450835 73.6788761,22.3441823 74.1401292,20.5768 Z" id="Path" fill="#F1B282"></path>
|
||||
<path d="M9.9646885,46.2709873 C9.9646885,46.2709873 5.46656991,44.7902076 3.76389735,43.0985316 C1.02070265,40.3722025 6.62781062,33.1548658 9.49082301,34.8907038 C12.5313097,36.7337924 12.2466301,38.6003139 12.3114938,40.0153013 C12.3763575,41.4302886 12.8925646,43.391443 13.1880549,44.0124152 C13.4835451,44.6324861 11.6412354,46.9938025 9.9646885,46.2709873 Z" id="Path" fill="#F1B282"></path>
|
||||
<path d="M40.234423,158.566005 L42.6650106,163.057013 C42.6650106,163.057013 46.4937717,165.513863 47.5027628,160.934532 C47.5027628,160.934532 46.4847628,158.128891 46.1126973,157.103251 C45.7406319,156.078511 40.234423,158.566005 40.234423,158.566005 Z" id="Path" fill="#F1B282"></path>
|
||||
<path d="M40.4794637,89.3334684 C40.4794637,89.3334684 24.1797522,112.419392 23.7833628,117.053701 C23.0671593,125.416547 39.0173274,158.62639 39.0173274,158.62639 L47.266731,156.855403 C47.266731,156.855403 41.7416035,131.821843 37.375915,123.286856 C45.7721628,111.929104 59.0728283,105.586896 59.0728283,90.7520608 C59.0737292,86.7297114 40.4794637,89.3334684 40.4794637,89.3334684 Z" id="Path" fill="#0B224E"></path>
|
||||
<path d="M57.6548354,47.6616405 C55.2386619,47.4985114 50.7414442,47.7454582 48.1225717,47.6958886 C41.4163841,47.5697114 32.3552832,53.5342886 25.0923487,55.6657823 C20.3464867,50.5267646 13.3880513,42.3730127 13.3880513,42.3730127 L7.53590265,45.9888911 C7.53590265,45.9888911 17.9825646,64.0142076 24.4040726,65.0479595 C28.2904903,65.673438 34.5381274,65.4337013 39.3866903,63.4229772 C39.3866903,69.4596557 31.6561965,93.1169823 37.3362761,94.3219747 C43.0163558,95.5269671 51.9675487,93.4612658 57.4755593,94.3219747 C62.9835699,95.1826835 69.0077876,99.3158886 67.6312354,88.9837772 C66.2537823,78.6516658 60.2286637,79.7629266 73.1383451,60.0513418 C76.5193664,54.8888911 80.4553327,47.3687291 79.8589469,41.0625722 C78.6923009,28.7368608 75.6986602,19.4718481 75.6986602,19.4718481 L69.2933681,20.5046987 C69.2933681,20.5046987 69.8465115,33.4811241 71.2230637,41.0580658 C69.8456106,44.7045873 67.7681699,47.2137114 65.3961398,49.8075544 C64.0970637,51.2288506 61.4376513,47.5255494 57.6548354,47.6616405 Z" id="Path" fill="#FFCB01"></path>
|
||||
<path d="M69.4618336,168.30959 C70.1293894,168.222167 70.8347823,168.939575 70.8068549,171.602815 C70.7789274,174.266056 70.9014478,175.676537 68.8366195,175.962238 C67.5393451,176.14159 62.6502425,174.847372 57.8872637,174.932992 C56.1665735,174.963635 53.0044673,174.156101 51.8954779,172.677124 C51.6693558,172.3752 51.6189062,171.939889 51.7675522,171.571271 C51.9161982,171.202653 52.2360124,170.969225 52.5801504,170.979139 C56.4170195,171.08639 56.483685,171.459514 59.5890354,169.253215 C60.7160425,168.45199 61.6574673,167.703038 62.2520513,167.662481 C63.4087876,167.58317 63.9439133,169.124334 65.8285646,168.923352 C67.7150177,168.721468 68.3078,168.461003 69.4618336,168.30959 Z" id="Path" fill="#0B224E"></path>
|
||||
<path d="M47.5622212,159.639413 C48.2604071,159.50242 49.1703009,160.431625 49.5207451,162.892081 C49.8711894,165.353438 50.5801858,167.32 48.4207646,167.758916 C47.0649327,168.034704 40.0074,167.316395 34.9471292,167.800375 C33.1192336,167.97522 30.9769292,167.088375 29.5850619,165.81759 C29.3003823,165.558025 29.185069,165.160567 29.2913735,164.807271 C29.3976779,164.453975 29.7048796,164.211534 30.0724407,164.190805 C34.1741699,163.963686 34.2993929,164.302562 37.2921327,162.002532 C38.3786,161.167058 39.2740796,160.395575 39.9019965,160.308152 C41.1226956,160.136911 41.8569168,161.186886 43.8361611,160.8408 C45.8154053,160.494714 46.3541345,159.877347 47.5622212,159.639413 Z" id="Path" fill="#0B224E"></path>
|
||||
</g>
|
||||
<g id="man_with_paper" transform="translate(204.555752, 213.167089)">
|
||||
<path d="M24.5076743,4.49641519 C27.5787912,4.09174684 30.3147788,6.04569114 30.3147788,8.43404557 C30.3147788,8.95407595 30.1111788,9.89229367 30.508469,11.0810633 C30.7742301,11.8759797 31.8886248,12.9025215 31.8787807,13.985843 C31.8688053,15.0691646 30.249915,14.8249215 30.3787416,15.7937823 C30.5075681,16.7617418 31.1535027,18.7634532 30.7012584,19.5376405 C30.249915,20.3127291 25.2166708,19.4087595 25.0878442,21.2157975 C24.9590177,23.0228354 26.249085,26.4990177 26.249085,26.4990177 L15.4087363,25.6698532 C15.4087363,25.6698532 14.5033469,8.93064304 15.4087363,7.65895696 C16.9258265,5.52926582 22.0554655,4.81906835 24.5076743,4.49641519 Z" id="Path" fill="#F1B282" fill-rule="nonzero"></path>
|
||||
<path d="M4.81072566,153.006096 C4.81072566,153.006096 2.79995044,161.619494 4.79541062,163.34722 L9.20163894,163.750086 C9.60343363,163.787038 9.9583823,163.490522 9.99531858,163.088557 L10.0583805,162.39999 C10.0583805,162.39999 11.9042938,164.207028 13.359223,164.339514 C14.8141522,164.472 22.5743752,164.531484 22.5743752,164.531484 C22.5743752,164.531484 24.3635327,165.012759 24.5220885,163.273316 C24.7491115,160.782218 18.4041788,160.457762 15.9816991,158.191078 C13.5592195,155.924395 12.4322124,153.701873 12.4322124,153.701873 L4.81072566,153.006096 Z" id="Path" fill="#0B224E"></path>
|
||||
<path d="M28.0544584,155.318744 C28.0544584,155.318744 26.8355611,164.079949 28.9796673,165.61841 L33.4048142,165.61841 C33.8084106,165.61841 34.1354319,165.291251 34.1354319,164.887484 L34.1354319,164.196213 C34.1354319,164.196213 36.1380991,165.827504 37.5984336,165.827504 C39.0587681,165.827504 48.4054496,165.81038 48.4054496,165.81038 C48.4054496,165.81038 50.2306425,166.126724 50.2306425,164.380071 C50.2306425,161.879058 42.2695221,161.502329 39.6506496,159.465468 C37.031777,157.428608 35.7074761,155.317843 35.7074761,155.317843 L28.0544584,155.317843 L28.0544584,155.318744 Z" id="Path" fill="#0B224E"></path>
|
||||
<path d="M66.1042354,54.1687797 C66.1042354,54.8068759 65.8420779,55.4170329 65.379023,55.8559494 C49.6603823,70.7583797 22.9167115,70.6430313 22.9167115,70.6430313 L22.9167115,36.7103595 C22.9167115,36.7103595 47.0676354,38.9427949 64.8033575,22.4712608 C65.0285788,22.2612658 65.3574018,22.2053873 65.6393788,22.3288608 C65.9213558,22.4523342 66.1042354,22.7308253 66.1042354,23.0390582 L66.1042354,54.1687797 Z" id="Path" fill="#1B90FB" fill-rule="nonzero"></path>
|
||||
<path d="M57.7800584,41.8836253 C57.7800584,41.8836253 60.1998354,39.075281 60.6836106,38.4948658 C61.1673858,37.9135494 61.5628743,34.5247899 62.4295257,33.6532658 C63.296177,32.7817418 66.0339664,31.2739241 66.757377,31.355038 C67.7798814,31.4704 68.8699522,35.8550582 68.4978867,39.0013772 C68.1555504,41.8944405 66.5501735,42.8741165 64.142108,43.2616608 C62.888977,43.4635443 60.5124425,46.2637772 60.3187522,47.0379646 L57.7800584,41.8836253 Z" id="Path" fill="#F1B282" fill-rule="nonzero"></path>
|
||||
<path d="M27.5085221,31.9309468 C27.5085221,31.9309468 27.8409487,26.9343291 26.1130513,26.3872608 C24.385154,25.8401924 16.1871009,23.3860456 15.0826159,25.0326582 C13.9790319,26.6792709 13.8150708,30.9999392 13.8150708,30.9999392 L27.5085221,31.9309468 Z" id="Path" fill="#1EAF69" fill-rule="nonzero"></path>
|
||||
<path d="M33.0913062,4.272 C32.9489664,-0.794916456 11.855646,-1.93141266 11.3988973,9.17398481 C11.1358389,15.5846886 12.8168903,20.892243 14.2952425,20.8733165 C18.1726513,20.8228456 15.8015221,14.053438 18.0447257,13.7542177 C20.5392761,13.4207494 18.0231044,24.7920203 24.3563257,24.123281 C34.9786602,23.0003038 32.2057363,15.0691646 25.6256726,16.3372456 C19.0456088,17.6053266 20.6951292,10.2726278 26.1788159,8.90090127 C31.6616018,7.52737215 33.194908,7.96538734 33.0913062,4.272 Z" id="Path" fill="#0B224E" fill-rule="nonzero"></path>
|
||||
<path d="M7.34941947,79.5511291 C7.34941947,79.5511291 6.88546372,96.574238 9.19082832,113.829873 L3.74768142,153.827149 C3.70353805,154.149803 3.79723009,154.475159 4.00443363,154.725711 C4.21163717,154.976263 4.5152354,155.127676 4.83955398,155.1448 L14.0781292,155.614359 C14.6186602,155.641397 15.1042372,155.292608 15.2528832,154.772577 C16.7123168,149.656992 23.9617381,123.890704 24.4833504,115.185377 C25.0644212,105.502177 27.1932124,79.7449013 22.5473487,77.0338937 C17.901485,74.3228861 7.94490442,71.5983595 7.34941947,79.5511291 Z" id="Path" fill="#0B224E" fill-rule="nonzero"></path>
|
||||
<path d="M13.0628319,78.5831696 C14.3592053,88.772881 23.3230106,117.509742 25.4518018,121.382481 L26.7553823,155.848689 C26.7851115,156.627382 27.3869027,157.262775 28.1625646,157.333073 L35.8615274,158.033357 C36.2786372,158.07121 36.6939451,157.938724 37.0119575,157.664739 C37.3299699,157.390754 37.5227593,157.000506 37.5479841,156.581418 L39.7767735,118.846319 C39.7767735,118.846319 37.2606018,80.7119595 32.6138372,76.8392203 C27.9670726,72.966481 12.3970779,73.3513215 13.0628319,78.5831696 Z" id="Path" fill="#102B5E" fill-rule="nonzero"></path>
|
||||
<path d="M12.9592301,52.5113519 C15.7285504,50.4420456 18.9293947,47.7193215 19.9978442,45.5139241 C22.0113221,41.3563848 20.8464779,23.5464709 13.4312938,28.9684861 C6.01610973,34.3905013 0.362155752,50.7241418 0.665753982,56.2452962 C0.969352212,61.7664506 25.7806248,72.8943797 25.7806248,72.8943797 L27.9031097,63.9087595 C27.9031097,63.9078582 17.8528372,56.1569722 12.9592301,52.5113519 Z" id="Path" fill="#1EAF69" fill-rule="nonzero"></path>
|
||||
<path d="M63.0241097,45.171443 L59.0079646,39.8674937 C58.7548159,39.5331241 58.2818513,39.4610228 57.9404159,39.7052658 C55.9945044,41.0977215 49.8567752,45.5788152 45.6649575,49.5768304 C42.1596142,43.3589975 33.1904035,28.3736506 27.9688743,28.0365772 C25.1067628,27.1650532 15.9663841,25.9059848 12.2889717,29.392081 C8.61065841,32.8781772 9.96558938,41.3996456 9.38541947,54.5680405 C8.80434867,67.7373367 3.19093451,79.9377722 7.64310796,81.8745924 C12.0952814,83.8114127 16.5474549,78.9698127 22.5491504,79.1635848 C28.5499451,79.357357 35.9209858,82.8434532 36.4948496,78.0018532 C37.0110566,73.6415291 32.3705982,62.8497722 32.8525717,54.2390785 C37.1993416,58.9328709 42.5028513,63.8159291 45.1982991,63.0904101 C49.7153363,61.8737013 60.7791044,48.8044456 63.0024885,46.1348962 C63.2313133,45.8582076 63.241223,45.4589468 63.0241097,45.171443 Z" id="Path" fill="#2CCD7F" fill-rule="nonzero"></path>
|
||||
<path d="M33.9003009,64.2827848 C30.4391009,61.5483443 23.8653434,54.6852051 25.6463929,42.9489215 C25.6463929,42.9489215 26.3202549,43.9871797 27.4661805,45.7915139 C28.7436354,47.7824101 30.610269,50.7187342 32.8318513,54.2201519 C32.8318513,54.2201519 32.8318513,54.2282633 32.8399593,54.2282633 L32.8399593,54.2363747 C32.6534761,57.4268557 33.1877009,60.9282734 33.9003009,64.2827848 Z" id="Path" fill="#1EAF69" fill-rule="nonzero"></path>
|
||||
</g>
|
||||
<g id="check_2_" transform="translate(446.893805, 23.000000)" fill-rule="nonzero">
|
||||
<ellipse id="Oval" fill="#DBFEED" cx="25.0725292" cy="24.9245063" rx="24.7743363" ry="24.7848101"></ellipse>
|
||||
<ellipse id="Oval" fill="#9AEEC5" cx="25.0725292" cy="24.9245063" rx="19.2690283" ry="19.2771747"></ellipse>
|
||||
<ellipse id="Oval" fill="#60DFA0" cx="25.0725292" cy="24.9245063" rx="13.7637204" ry="13.7695392"></ellipse>
|
||||
<path d="M23.6383204,30.4321418 C23.6194018,30.4321418 23.5995823,30.4321418 23.5806637,30.4321418 C23.1986885,30.415919 22.8410372,30.2410734 22.5941947,29.9508658 L18.4654389,25.1317975 C17.9690513,24.5558886 18.0366177,23.6852658 18.6149858,23.1904709 C19.1942549,22.7010835 20.0609062,22.7659747 20.5545912,23.3409823 L23.7230035,27.0379747 L31.0453965,19.8116253 C31.5841257,19.2789772 32.4597858,19.2870886 32.991308,19.8251443 C33.5246319,20.3686076 33.5192265,21.2401316 32.9777947,21.7745823 L24.604069,30.0364861 C24.3473168,30.2897418 24.0004761,30.4321418 23.6383204,30.4321418 Z" id="Path" fill="#FFFFFF"></path>
|
||||
</g>
|
||||
<g id="plant_13_" transform="translate(46.000000, 234.797468)" fill-rule="nonzero">
|
||||
<path d="M69.5330035,53.0530127 C69.1789558,53.0530127 68.8726549,52.7889418 68.8240071,52.4275342 C65.7591965,27.7085165 68.1564513,12.8727797 75.9482053,8.33941266 C83.4336584,3.98629873 92.8938513,10.8981063 93.2830336,11.1973266 C93.6028478,11.4298532 93.6650088,11.8786835 93.428977,12.1914228 C93.1929451,12.5077671 92.7334938,12.5735595 92.4353009,12.3374278 C92.3452124,12.2680304 83.3579841,5.67617215 76.6581027,9.57324557 C71.6744071,12.4762228 66.5177416,22.2387342 70.2429009,52.2535899 C70.2915487,52.6429367 70.0131752,52.9980354 69.6239929,53.0467038 C69.5888584,53.0494076 69.560931,53.0530127 69.5330035,53.0530127 Z" id="Path" fill="#2CCD7F"></path>
|
||||
<path d="M54.1738159,114.058795 L61.3106265,139.114886 C61.8250319,140.921023 66.3871133,142.334208 71.9383664,142.334208 C77.4896195,142.334208 82.0517009,140.921023 82.5661062,139.114886 L89.7029168,114.058795 L54.1738159,114.058795 Z" id="Path" fill="#C3D3F3"></path>
|
||||
<path d="M89.7317451,113.853306 C89.7317451,113.922704 89.7218354,113.9912 89.7029168,114.058795 C89.1704938,115.928922 81.4210814,117.413306 71.9392673,117.413306 C62.4574531,117.413306 54.7080407,115.929823 54.1756177,114.058795 C54.1566991,113.990299 54.1467894,113.921803 54.1467894,113.853306 C54.1467894,111.886744 62.113315,110.293306 71.9401681,110.293306 C81.7670212,110.293306 89.7317451,111.887646 89.7317451,113.853306 Z" id="Path" fill="#DAE7FF"></path>
|
||||
<path d="M70.4528071,113.826268 C70.7627115,96.5814481 70.6203717,79.2627241 69.4005735,62.0611646 C68.6807664,53.5036456 67.9627611,44.8001215 65.5105522,36.6842228 C64.8267805,34.7474025 64.0745416,32.6348354 62.7277186,31.4064101 C61.4881009,30.002238 59.6611062,30.8070684 58.3214903,32.4311494 C55.5638814,35.4548962 53.7693186,39.5015797 51.9477292,43.3454785 L51.9288106,43.3851342 C51.7603451,43.7402329 51.3360283,43.8916456 50.9810796,43.7231089 C50.6288336,43.5554734 50.477485,43.1363848 50.6396442,42.7821873 C52.4612336,38.8265316 54.1242673,34.7582177 57.1575469,31.3090734 C57.8719487,30.4177215 58.8890478,29.7048203 60.0674053,29.1676658 C61.4340478,28.8107646 62.9493363,29.1965063 63.8565274,30.1176 C65.7402779,31.8597468 66.4276531,34.0408101 67.2258372,36.1074127 C69.9086726,44.557681 70.7248743,53.2476861 71.5897239,61.8818127 C73.0752832,79.1924253 73.4743752,96.5219646 73.4221239,113.881246 C73.4194212,114.702299 72.7527664,115.364729 71.9320602,115.36293 C71.111354,115.360223 70.4492035,114.693286 70.4510053,113.872233 C70.4528071,113.866825 70.4528071,113.831676 70.4528071,113.826268 Z" id="Path" fill="#2CCD7F"></path>
|
||||
<path d="M71.8266566,90.3095392 C71.4302673,90.3095392 71.1176602,89.9895899 71.1176602,89.596638 C71.1176602,70.4537519 73.7167133,59.9053367 78.840046,58.2434025 C83.5732956,56.6932253 88.3137522,63.1904506 88.5155504,63.4653367 C88.7452761,63.7852861 88.6750071,64.2269063 88.3560938,64.4594329 C88.0362796,64.6892557 87.5912425,64.618957 87.3624177,64.2999089 C87.3137699,64.2404253 83.053485,58.3930127 79.278777,59.5962025 C76.2067593,60.5975089 72.536554,66.612557 72.536554,89.596638 C72.5356531,89.9895899 72.223046,90.3095392 71.8266566,90.3095392 Z" id="Path" fill="#2CCD7F"></path>
|
||||
<path d="M68.3411327,40.6407797 C68.3411327,40.6407797 70.7230726,25.8329823 58.4845504,11.307281 C58.4845504,11.307281 65.0159664,26.8577215 68.3411327,40.6407797 Z" id="Path" fill="#2CCD7F"></path>
|
||||
<path d="M87.939885,63.8817215 C83.3850106,55.6937215 92.2028726,46.1267848 98.9450956,46.1267848 C105.687319,46.1267848 114.907876,56.2371848 114.907876,56.2371848 C114.907876,56.2371848 98.9450956,54.5581266 98.9450956,56.8401316 C98.9450956,59.1221367 112.528639,55.1538633 117.981696,57.8324253 C123.434752,60.5109873 124.327529,68.3465924 124.327529,68.3465924 C124.327529,68.3465924 102.018915,63.1886481 101.919818,64.9740557 C101.82072,66.7594633 124.228432,66.9577418 125.715793,71.4217114 C127.203154,75.885681 126.707667,80.0513316 126.707667,80.0513316 C126.707667,80.0513316 105.787317,70.7277367 105.191832,73.9010937 C104.597248,77.0753519 126.786945,77.6701873 126.786945,83.7212861 C126.786945,89.7723848 123.039264,101.476223 123.93114,103.46081 C123.93114,103.46081 111.041278,107.825641 107.869262,103.46081 C104.696345,99.0959797 104.101761,86.5981266 101.523428,86.5981266 C98.9450956,86.5981266 100.036067,102.181914 105.985512,105.400334 C105.985512,105.400334 94.2857186,107.824739 93.0956496,100.48483 C91.9055805,93.1449215 94.7812053,80.4478886 92.6992602,79.5556354 C90.617315,78.6633823 85.7588425,92.9538532 92.0055788,103.163392 C92.0055788,103.163392 83.0823133,104.849661 82.0904389,96.9140152 C81.0985646,88.9783696 86.2543292,75.9559797 84.767869,75.9559797 C83.2814088,75.9559797 75.1500212,88.0861165 75.7455062,95.0294684 C75.7455062,95.0294684 66.0285611,87.9869772 68.0114088,83.2255899 C69.9942566,78.4642025 77.5301593,72.9097013 76.2409929,71.8182684 C74.9518265,70.7268354 66.2267558,77.1744911 65.731269,80.9435848 C65.731269,80.9435848 61.0709912,70.2311392 70.1933522,61.7006582 C77.0490867,55.2872506 87.939885,63.8817215 87.939885,63.8817215 Z" id="Path" fill="#45D48F"></path>
|
||||
<path d="M118.215025,36.6139241 C118.215025,36.6139241 123.626641,27.639119 119.994273,22.8921519 C111.987207,12.3599595 104.054014,15.1782177 104.128788,13.3243139 C104.20266,11.4704101 116.806041,12.5825722 119.40149,16.5138937 C119.40149,16.5138937 119.253745,6.27821772 114.656529,6.27821772 C110.059313,6.27821772 104.20266,8.50344304 103.758524,7.09386329 C103.313487,5.68428354 113.989874,4.64602532 113.989874,4.64602532 C113.989874,4.64602532 106.138662,-0.397458228 99.4252673,0.195574684 C91.165954,0.9256 92.8605186,11.7660253 92.8605186,11.7660253 C92.8605186,11.7660253 86.4849558,0.6408 80.6274018,6.20341266 C74.7707487,11.7660253 76.8463876,17.9225722 79.2184177,21.631281 C80.7012743,17.403443 85.1489434,11.6182177 86.1876637,13.1386532 C87.2254832,14.6590886 75.512177,25.0425722 83.7408602,30.9765063 C85.0750708,24.7460557 90.0425504,15.3269266 91.4515345,17.2547342 C93.0587133,17.688243 83.6669876,29.9094076 86.3363097,34.3138937 C89.0056319,37.2808608 92.8605186,38.8382481 92.8605186,38.8382481 C92.8605186,38.8382481 94.3433752,20.8147342 97.3090885,22.6695392 C100.274802,24.5243443 91.0073982,35.0556354 95.7523593,40.2478278 C100.49732,45.4400203 109.319687,44.9947949 110.134988,47.5904405 C110.134988,47.5904405 118.142053,41.5086987 116.807842,36.9843443 C115.002469,30.8638481 105.835064,27.639119 106.502619,26.1186835 C107.168373,24.5982481 117.473596,27.0460861 118.215025,36.6139241 Z M104.128788,18.3677975 C103.906269,17.1808304 105.018862,16.1425722 110.134087,17.6999595 C115.249312,19.2573468 118.511416,27.0451848 118.511416,27.0451848 C114.730402,20.1477975 104.351306,19.5547646 104.128788,18.3677975 Z" id="Shape" fill="#7BE6B1"></path>
|
||||
<path d="M25.3373894,64.1214582 C26.1238619,66.812638 10.0763982,70.216719 5.72332212,80.069357 C2.50536106,87.3524861 14.5871292,99.2762329 14.5871292,99.2762329 C16.5168248,95.1078785 31.1895381,97.7729215 40.2614496,90.1139646 C49.3333611,82.4550076 36.1092708,62.8074127 41.4767434,60.3478582 C46.8442159,57.8892051 45.4082053,88.3835342 45.4082053,88.3835342 C45.4082053,88.3835342 52.2008779,86.6152506 57.3134,82.2296911 C62.7394301,75.4368506 49.6747965,52.9349468 52.458531,52.5582177 C55.2359593,49.6363139 61.5034159,66.4863797 62.3826796,77.2069367 C77.4490796,69.0649013 60.0971345,49.1325063 62.1655664,46.8126481 C64.2339982,44.4927899 70.4230779,55.1457519 71.9852124,62.5460456 C76.7635062,56.8536506 81.5769345,47.0001114 72.9815912,36.4129418 C64.3862478,25.8257722 51.2936867,43.0633823 51.2936867,43.0633823 C51.2936867,43.0633823 56.4872885,25.2850127 42.8190619,22.2648709 C31.7093487,19.8107241 17.4699611,26.5449823 17.4699611,26.5449823 C17.4699611,26.5449823 35.1164956,30.6069873 34.0651628,32.8691646 C33.0138301,35.1313418 23.6932743,30.1320203 15.997915,29.1316152 C8.30255575,28.1312101 5.83052743,45.2345316 5.83052743,45.2345316 C11.0286336,39.2185823 32.3696973,40.0982177 32.090423,43.2184 C31.8111487,46.3385823 19.1447062,39.8945316 3.45038938,55.7829468 C-3.66299823,62.9389975 3.44588496,79.1410532 3.44588496,79.1410532 C6.7665469,63.2850835 24.5509168,61.4302785 25.3373894,64.1214582 Z M5.02964071,63.0597671 C5.02964071,63.0597671 12.1835681,50.7322532 21.0861133,49.2379544 C29.9886584,47.7436557 31.6246655,49.7237367 30.994046,51.6623595 C30.3634265,53.600081 12.8592319,52.3347038 5.02964071,63.0597671 Z" id="Shape" fill="#9AEEC5"></path>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 41 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 109 KiB |
|
After Width: | Height: | Size: 150 KiB |
|
After Width: | Height: | Size: 275 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 261.76 226.69"><path d="M161.096.001l-30.225 52.351L100.647.001H-.005l130.877 226.688L261.749.001z" fill="#41b883"/><path d="M161.096.001l-30.225 52.351L100.647.001H52.346l78.526 136.01L209.398.001z" fill="#34495e"/></svg>
|
||||
|
After Width: | Height: | Size: 276 B |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 83 KiB |
@@ -0,0 +1,11 @@
|
||||
.footer {
|
||||
height: 30px;
|
||||
background: #ffffff;
|
||||
border-top: 1px solid #e4e7ed;
|
||||
a {
|
||||
font-size: 14px;
|
||||
color: #858585;
|
||||
text-decoration: none;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<template>
|
||||
<div>
|
||||
<slot></slot>
|
||||
</div>
|
||||
<div class="footer flx-center">
|
||||
<a href="http://www.spicyboy.cn/" target="_blank"> 2022 © 1Panel By 飞致云. </a>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import './index.scss';
|
||||
</style>
|
||||
@@ -0,0 +1,67 @@
|
||||
<template>
|
||||
<el-dropdown trigger="click">
|
||||
<div class="avatar">
|
||||
<img src="@/assets/images/avatar.gif" alt="avatar" />
|
||||
</div>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item @click="openDialog('infoRef')">{{ $t('header.personalData') }}</el-dropdown-item>
|
||||
<el-dropdown-item @click="openDialog('passwordRef')">{{
|
||||
$t('header.changePassword')
|
||||
}}</el-dropdown-item>
|
||||
<el-dropdown-item @click="logout" divided>{{ $t('header.logout') }}</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
<InfoDialog ref="infoRef"></InfoDialog>
|
||||
<PasswordDialog ref="passwordRef"></PasswordDialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import InfoDialog from './info-dialog.vue';
|
||||
import PasswordDialog from './password-dialog.vue';
|
||||
import { ElMessageBox, ElMessage } from 'element-plus';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { GlobalStore } from '@/store';
|
||||
import { logOutApi } from '@/api/modules/login';
|
||||
import i18n from '@/lang';
|
||||
|
||||
const router = useRouter();
|
||||
const globalStore = GlobalStore();
|
||||
|
||||
const logout = () => {
|
||||
ElMessageBox.confirm(i18n.global.t('commons.msg.sureLogOut'), i18n.global.t('commons.msg.infoTitle'), {
|
||||
confirmButtonText: i18n.global.t('commons.button.confirm'),
|
||||
cancelButtonText: i18n.global.t('commons.button.cancel'),
|
||||
type: 'warning',
|
||||
}).then(() => {
|
||||
systemLogOut();
|
||||
router.push({ name: 'login' });
|
||||
globalStore.setLogStatus(false);
|
||||
ElMessage({
|
||||
type: 'success',
|
||||
message: i18n.global.t('commons.msg.operationSuccess'),
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const systemLogOut = async () => {
|
||||
await logOutApi();
|
||||
};
|
||||
|
||||
interface DialogExpose {
|
||||
openDialog: () => void;
|
||||
}
|
||||
const infoRef = ref<null | DialogExpose>(null);
|
||||
const passwordRef = ref<null | DialogExpose>(null);
|
||||
|
||||
const openDialog = (refName: string) => {
|
||||
if (refName == 'infoRef') return infoRef.value?.openDialog();
|
||||
passwordRef.value?.openDialog();
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import '../index.scss';
|
||||
</style>
|
||||
@@ -0,0 +1,22 @@
|
||||
<template>
|
||||
<el-breadcrumb :separator-icon="ArrowRight">
|
||||
<transition-group name="breadcrumb" mode="out-in">
|
||||
<el-breadcrumb-item :to="{ path: HOME_URL }" key="/home">{{ $t('menu.home') }}</el-breadcrumb-item>
|
||||
<el-breadcrumb-item v-for="item in matched" :key="item.path" :to="{ path: item.path }">
|
||||
{{ $t(item.meta?.title as string) }}
|
||||
</el-breadcrumb-item>
|
||||
</transition-group>
|
||||
</el-breadcrumb>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { ArrowRight } from '@element-plus/icons-vue';
|
||||
import { HOME_URL } from '@/config/config';
|
||||
const route = useRoute();
|
||||
|
||||
const matched = computed(() =>
|
||||
route.matched.filter((item) => item.meta && item.meta.title && item.meta.title !== 'menu.home'),
|
||||
);
|
||||
</script>
|
||||
@@ -0,0 +1,16 @@
|
||||
<template>
|
||||
<el-icon class="collapse-icon" @click="menuStore.setCollapse()">
|
||||
<component :is="isCollapse ? 'expand' : 'fold'"></component>
|
||||
</el-icon>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import { MenuStore } from '@/store/modules/menu';
|
||||
const menuStore = MenuStore();
|
||||
const isCollapse = computed((): boolean => menuStore.isCollapse);
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import '../index.scss';
|
||||
</style>
|
||||
@@ -0,0 +1,19 @@
|
||||
<template>
|
||||
<el-dialog v-model="dialogVisible" title="个人资料" width="500px" draggable>
|
||||
<span>This is userInfo</span>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
const dialogVisible = ref(false);
|
||||
|
||||
// openDialog
|
||||
const openDialog = () => {
|
||||
dialogVisible.value = true;
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
openDialog,
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,40 @@
|
||||
<template>
|
||||
<el-dropdown trigger="click" @command="handleSetLanguage">
|
||||
<span>
|
||||
<el-tooltip effect="dark" content="国际化" placement="bottom">
|
||||
<i :class="'iconfont icon-zhongyingwen'" class="icon-style"></i>
|
||||
</el-tooltip>
|
||||
</span>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item :disabled="language && language === 'zh'" command="zh">简体中文</el-dropdown-item>
|
||||
<el-dropdown-item :disabled="language === 'en'" command="en">English</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { GlobalStore } from '@/store';
|
||||
import { getBrowserLang } from '@/utils/util';
|
||||
|
||||
const i18n = useI18n();
|
||||
const globalStore = GlobalStore();
|
||||
const language = computed((): string => globalStore.language);
|
||||
|
||||
// 切换语言
|
||||
const handleSetLanguage = (lang: string) => {
|
||||
i18n.locale.value = lang;
|
||||
globalStore.updateLanguage(lang);
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
handleSetLanguage(language.value || getBrowserLang());
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import '../index.scss';
|
||||
</style>
|
||||
@@ -0,0 +1,60 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-tooltip effect="dark" content="布局设置" placement="bottom">
|
||||
<i :class="'iconfont icon-zhuti'" class="icon-style" @click="openDrawer"></i>
|
||||
</el-tooltip>
|
||||
<el-drawer v-model="drawerVisible" title="布局设置" size="300px">
|
||||
<el-divider class="divider" content-position="center">
|
||||
<el-icon><ColdDrink /></el-icon>
|
||||
全局主题
|
||||
</el-divider>
|
||||
<div class="theme-item">
|
||||
<span>主题颜色</span>
|
||||
<el-color-picker v-model="themeConfig.primary" :predefine="colorList" @change="changePrimary">
|
||||
</el-color-picker>
|
||||
</div>
|
||||
<div class="theme-item">
|
||||
<span>暗黑模式</span>
|
||||
<SwitchDark></SwitchDark>
|
||||
</div>
|
||||
<br />
|
||||
</el-drawer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue';
|
||||
import { useTheme } from '@/hooks/use-theme';
|
||||
import SwitchDark from '@/components/switch-dark/index.vue';
|
||||
import { GlobalStore } from '@/store';
|
||||
|
||||
// 预定义主题颜色
|
||||
const colorList = [
|
||||
'#409EFF',
|
||||
'#DAA96E',
|
||||
'#0C819F',
|
||||
'#009688',
|
||||
'#27ae60',
|
||||
'#ff5c93',
|
||||
'#e74c3c',
|
||||
'#fd726d',
|
||||
'#f39c12',
|
||||
'#9b59b6',
|
||||
];
|
||||
|
||||
// 主题初始化
|
||||
const globalStore = GlobalStore();
|
||||
const themeConfig = computed(() => globalStore.themeConfig);
|
||||
|
||||
const { changePrimary } = useTheme();
|
||||
|
||||
// 打开主题设置
|
||||
const drawerVisible = ref(false);
|
||||
const openDrawer = () => {
|
||||
drawerVisible.value = true;
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import '../index.scss';
|
||||
</style>
|
||||
@@ -0,0 +1,25 @@
|
||||
<template>
|
||||
<el-dialog v-model="dialogVisible" title="修改密码" width="500px" draggable>
|
||||
<span>This is Password</span>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="dialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="dialogVisible = false">确认</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
const dialogVisible = ref(false);
|
||||
|
||||
// openDialog
|
||||
const openDialog = () => {
|
||||
dialogVisible.value = true;
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
openDialog,
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,101 @@
|
||||
.header {
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: 55px;
|
||||
padding: 0 15px;
|
||||
background-color: #ffffff;
|
||||
border-bottom: 1px solid #f6f6f6;
|
||||
.header-lf {
|
||||
.collapse-icon {
|
||||
margin-right: 20px;
|
||||
font-size: 22px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
.header-ri {
|
||||
margin: 0 30px;
|
||||
.header-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
width: 66px;
|
||||
margin-right: 22px;
|
||||
.icon-style {
|
||||
font-size: 20px;
|
||||
color: rgb(0 0 0 / 75%);
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
.username {
|
||||
margin: 0 20px 0 0;
|
||||
font-size: 15px;
|
||||
color: rgb(0 0 0 / 75%);
|
||||
}
|
||||
.avatar {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
border-radius: 50%;
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
.theme-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin: 15px 0;
|
||||
span {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
.divider {
|
||||
margin-top: 20px;
|
||||
.el-icon {
|
||||
position: relative;
|
||||
top: 2px;
|
||||
right: 5px;
|
||||
font-size: 15px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.theme-switch {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-top: 35px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 菜单搜索样式 */
|
||||
.layout-search-dialog {
|
||||
:deep(.el-dialog) {
|
||||
background: rgb(0 0 0 / 50%);
|
||||
border-radius: 0 !important;
|
||||
box-shadow: unset !important;
|
||||
.el-dialog__header {
|
||||
border-bottom: none !important;
|
||||
}
|
||||
}
|
||||
:deep(.el-autocomplete) {
|
||||
position: absolute;
|
||||
top: 100px;
|
||||
left: 50%;
|
||||
width: 560px;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
}
|
||||
.el-autocomplete__popper {
|
||||
.el-icon {
|
||||
position: relative;
|
||||
top: 2px;
|
||||
font-size: 16px;
|
||||
}
|
||||
span {
|
||||
margin: 0 0 0 10px;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<template>
|
||||
<div class="header">
|
||||
<div class="header-lf flx-center">
|
||||
<CollapseIcon id="collapseIcon"></CollapseIcon>
|
||||
<Breadcrumb id="breadcrumb" v-if="themeConfig.breadcrumb"></Breadcrumb>
|
||||
</div>
|
||||
<div class="header-ri flx-center">
|
||||
<div class="header-icon">
|
||||
<!-- Language -->
|
||||
<Language id="language"></Language>
|
||||
<!-- Theme -->
|
||||
<Theme id="theme"></Theme>
|
||||
</div>
|
||||
<!-- User name -->
|
||||
<span class="username">1Panel</span>
|
||||
<!-- Avatar -->
|
||||
<Avatar></Avatar>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import CollapseIcon from './components/collapseicon.vue';
|
||||
import Breadcrumb from './components/breadcrumb.vue';
|
||||
import Language from './components/language.vue';
|
||||
import Theme from './components/theme.vue';
|
||||
import Avatar from './components/avatar.vue';
|
||||
import { GlobalStore } from '@/store';
|
||||
|
||||
const globalStore = GlobalStore();
|
||||
const themeConfig = computed(() => globalStore.themeConfig);
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import './index.scss';
|
||||
</style>
|
||||
@@ -0,0 +1,19 @@
|
||||
<template>
|
||||
<Layout>
|
||||
<template #menu>
|
||||
<Menu></Menu>
|
||||
</template>
|
||||
<template #header>
|
||||
<Header></Header>
|
||||
</template>
|
||||
<template #footer>
|
||||
<Footer></Footer>
|
||||
</template>
|
||||
</Layout>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import Layout from '@/layout/index.vue';
|
||||
import Header from './header/index.vue';
|
||||
import Footer from './footer/index.vue';
|
||||
import Menu from './menu/index.vue';
|
||||
</script>
|
||||
@@ -0,0 +1,14 @@
|
||||
<template>
|
||||
<div class="logo flx-center">
|
||||
<img src="@/assets/images/logo.svg" alt="logo" />
|
||||
<span v-show="!isCollapse">1Panel</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
defineProps<{ isCollapse: boolean }>();
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import '../index.scss';
|
||||
</style>
|
||||
@@ -0,0 +1,42 @@
|
||||
<template>
|
||||
<template v-for="subItem in menuList" :key="subItem.path">
|
||||
<el-sub-menu v-if="subItem.children && subItem.children.length > 1" :index="subItem.path">
|
||||
<template #title>
|
||||
<el-icon>
|
||||
<component :is="subItem.meta?.icon"></component>
|
||||
</el-icon>
|
||||
<span>{{ subItem.meta?.title }}</span>
|
||||
</template>
|
||||
<SubItem :menuList="subItem.children" />
|
||||
</el-sub-menu>
|
||||
<el-menu-item v-else-if="subItem.children && subItem.children.length === 1" :index="subItem.children[0].path">
|
||||
<el-icon>
|
||||
<component :is="subItem.meta?.icon"></component>
|
||||
</el-icon>
|
||||
<template v-if="!subItem.meta?.isLink" #title>
|
||||
<span>{{ $t(subItem.meta?.title as string) }}</span>
|
||||
</template>
|
||||
</el-menu-item>
|
||||
<el-menu-item v-else :index="subItem.path">
|
||||
<el-icon>
|
||||
<component :is="subItem.meta?.icon"></component>
|
||||
</el-icon>
|
||||
<template v-if="!subItem.meta?.isLink" #title>
|
||||
<span>{{ subItem.meta?.title }}</span>
|
||||
</template>
|
||||
<template v-else #title>
|
||||
<a class="menu-href" :href="subItem.isLink" target="_blank">{{ subItem.meta?.title }}</a>
|
||||
</template>
|
||||
</el-menu-item>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { RouteRecordRaw } from 'vue-router';
|
||||
|
||||
defineProps<{ menuList: RouteRecordRaw[] }>();
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import '../index.scss';
|
||||
</style>
|
||||
@@ -0,0 +1,58 @@
|
||||
.menu {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
background-color: #191a20;
|
||||
transition: all 0.3s ease;
|
||||
.logo {
|
||||
box-sizing: border-box;
|
||||
height: 55px;
|
||||
border-bottom: 1px solid #282a35;
|
||||
box-shadow: 2px 0 6px rgb(0 21 41 / 35%);
|
||||
span {
|
||||
font-size: 22px;
|
||||
font-weight: bold;
|
||||
color: #dadada;
|
||||
white-space: nowrap;
|
||||
}
|
||||
img {
|
||||
width: 30px;
|
||||
object-fit: contain;
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
.el-scrollbar {
|
||||
height: calc(100% - 55px);
|
||||
.el-menu {
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
overflow-x: hidden;
|
||||
border-right: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
.el-menu,
|
||||
.el-menu--popup {
|
||||
.el-menu-item {
|
||||
&.is-active {
|
||||
background-color: #060708;
|
||||
&::before {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 4px;
|
||||
content: '';
|
||||
background: $primary-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.menu-href {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
color: #bdbdc0;
|
||||
text-decoration: none;
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
<template>
|
||||
<div
|
||||
class="menu"
|
||||
:style="{ width: isCollapse ? '65px' : '220px' }"
|
||||
element-loading-text="Loading..."
|
||||
:element-loading-spinner="loadingSvg"
|
||||
element-loading-svg-view-box="-10, -10, 50, 50"
|
||||
element-loading-background="rgba(122, 122, 122, 0.01)"
|
||||
>
|
||||
<Logo :isCollapse="isCollapse"></Logo>
|
||||
<el-scrollbar>
|
||||
<el-menu
|
||||
:default-active="activeMenu"
|
||||
:router="true"
|
||||
:collapse="isCollapse"
|
||||
:collapse-transition="false"
|
||||
:unique-opened="true"
|
||||
background-color="#191a20"
|
||||
text-color="#bdbdc0"
|
||||
active-text-color="#fff"
|
||||
>
|
||||
<SubItem :menuList="routerMenus"></SubItem>
|
||||
</el-menu>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted } from 'vue';
|
||||
import { RouteRecordRaw, useRoute } from 'vue-router';
|
||||
import { MenuStore } from '@/store/modules/menu';
|
||||
import { loadingSvg } from '@/utils/svg';
|
||||
import Logo from './components/logo.vue';
|
||||
import SubItem from './components/sub-item.vue';
|
||||
import { menuList } from '@/routers/router';
|
||||
const route = useRoute();
|
||||
const menuStore = MenuStore();
|
||||
|
||||
onMounted(async () => {
|
||||
menuStore.setMenuList(menuList);
|
||||
});
|
||||
|
||||
// const activeMenu = computed((): string => route.path);
|
||||
const activeMenu = computed(() => {
|
||||
const { meta, path } = route;
|
||||
if (meta.activeMenu) {
|
||||
return meta.activeMenu;
|
||||
}
|
||||
return path;
|
||||
});
|
||||
const isCollapse = computed((): boolean => menuStore.isCollapse);
|
||||
const routerMenus = computed((): RouteRecordRaw[] => menuStore.menuList);
|
||||
|
||||
const screenWidth = ref<number>(0);
|
||||
const listeningWindow = () => {
|
||||
window.onresize = () => {
|
||||
return (() => {
|
||||
screenWidth.value = document.body.clientWidth;
|
||||
if (isCollapse.value === false && screenWidth.value < 1200) menuStore.setCollapse();
|
||||
if (isCollapse.value === true && screenWidth.value > 1200) menuStore.setCollapse();
|
||||
})();
|
||||
};
|
||||
};
|
||||
listeningWindow();
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import './index.scss';
|
||||
</style>
|
||||
@@ -0,0 +1,41 @@
|
||||
<template>
|
||||
<!-- <el-icon class="back-button" @click="jump">
|
||||
<Back />
|
||||
</el-icon> -->
|
||||
<el-page-header :content="header" @back="jump" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useRouter } from 'vue-router';
|
||||
const router = useRouter();
|
||||
const props = defineProps({
|
||||
path: String,
|
||||
name: String,
|
||||
to: Object,
|
||||
header: String,
|
||||
});
|
||||
function jump() {
|
||||
const { path, name, to } = props;
|
||||
if (path) {
|
||||
router.push(path);
|
||||
}
|
||||
if (name) {
|
||||
router.push({ name: name });
|
||||
}
|
||||
if (to) {
|
||||
router.push(to);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
// .back-button {
|
||||
// cursor: pointer;
|
||||
// margin-right: 10px;
|
||||
// font-weight: 600;
|
||||
|
||||
// &:active {
|
||||
// transform: scale(0.85);
|
||||
// }
|
||||
// }
|
||||
</style>
|
||||
@@ -0,0 +1,90 @@
|
||||
<template>
|
||||
<div class="complex-table">
|
||||
<div class="complex-table__header" v-if="$slots.header || header">
|
||||
<slot name="header">{{ header }}</slot>
|
||||
</div>
|
||||
<div v-if="$slots.toolbar && !searchConfig" style="margin-bottom: 10px">
|
||||
<slot name="toolbar"></slot>
|
||||
</div>
|
||||
|
||||
<template v-if="searchConfig">
|
||||
<fu-filter-bar v-bind="searchConfig" @exec="search">
|
||||
<template #tl>
|
||||
<slot name="toolbar"></slot>
|
||||
</template>
|
||||
<template #default>
|
||||
<slot name="complex"></slot>
|
||||
</template>
|
||||
<template #buttons>
|
||||
<slot name="buttons"></slot>
|
||||
</template>
|
||||
</fu-filter-bar>
|
||||
</template>
|
||||
|
||||
<div class="complex-table__body">
|
||||
<fu-table v-bind="$attrs" @selection-change="handleSelectionChange">
|
||||
<slot></slot>
|
||||
</fu-table>
|
||||
</div>
|
||||
|
||||
<div class="complex-table__pagination" v-if="$slots.pagination || paginationConfig">
|
||||
<slot name="pagination">
|
||||
<fu-table-pagination
|
||||
v-model:current-page="paginationConfig.currentPage"
|
||||
v-model:page-size="paginationConfig.pageSize"
|
||||
v-bind="paginationConfig"
|
||||
@change="search"
|
||||
/>
|
||||
</slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
defineOptions({ name: 'ComplexTable' }); // 组件名
|
||||
defineProps({
|
||||
header: String,
|
||||
searchConfig: Object,
|
||||
paginationConfig: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
});
|
||||
const emit = defineEmits(['search', 'update:selects']);
|
||||
const condition = ref({});
|
||||
function search(conditions: any, e: any) {
|
||||
if (conditions) {
|
||||
condition.value = conditions;
|
||||
}
|
||||
emit('search', condition.value, e);
|
||||
}
|
||||
|
||||
function handleSelectionChange(row: any) {
|
||||
emit('update:selects', row);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@use '@/styles/mixins.scss' as *;
|
||||
|
||||
.complex-table {
|
||||
.complex-table__header {
|
||||
@include flex-row(flex-start, center);
|
||||
line-height: 60px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.complex-table__toolbar {
|
||||
@include flex-row(space-between, center);
|
||||
|
||||
.fu-search-bar {
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.complex-table__pagination {
|
||||
margin-top: 20px;
|
||||
@include flex-row(flex-end);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,18 @@
|
||||
<template>
|
||||
<div class="not-container">
|
||||
<img src="@/assets/images/403.png" class="not-img" alt="403" />
|
||||
<div class="not-detail">
|
||||
<h2>403</h2>
|
||||
<h4>抱歉,您无权访问该页面~🙅♂️🙅♀️</h4>
|
||||
<!-- <el-button type="primary" @click="tabStore.goHome()"
|
||||
>返回首页</el-button
|
||||
> -->
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="403"></script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import './index.scss';
|
||||
</style>
|
||||
@@ -0,0 +1,18 @@
|
||||
<template>
|
||||
<div class="not-container">
|
||||
<img src="@/assets/images/404.png" class="not-img" alt="404" />
|
||||
<div class="not-detail">
|
||||
<h2>404</h2>
|
||||
<h4>抱歉,您访问的页面不存在~🤷♂️🤷♀️</h4>
|
||||
<!-- <el-button type="primary" @click="tabStore.goHome()"
|
||||
>返回首页</el-button -->
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="404"></script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import './index.scss';
|
||||
</style>
|
||||
@@ -0,0 +1,18 @@
|
||||
<template>
|
||||
<div class="not-container">
|
||||
<img src="@/assets/images/500.png" class="not-img" alt="500" />
|
||||
<div class="not-detail">
|
||||
<h2>500</h2>
|
||||
<h4>抱歉,您的网络不见了~🤦♂️🤦♀️</h4>
|
||||
<!-- <el-button type="primary" @click="tabStore.goHome()"
|
||||
>返回首页</el-button -->
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="500"></script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import './index.scss';
|
||||
</style>
|
||||
@@ -0,0 +1,32 @@
|
||||
.not-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
.not-img {
|
||||
margin-right: 120px;
|
||||
}
|
||||
.not-detail {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
h2,
|
||||
h4 {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
h2 {
|
||||
font-size: 60px;
|
||||
color: #434e59;
|
||||
}
|
||||
h4 {
|
||||
margin: 30px 0 20px;
|
||||
font-size: 19px;
|
||||
font-weight: normal;
|
||||
color: #848587;
|
||||
}
|
||||
.el-button {
|
||||
width: 100px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<template>
|
||||
<el-switch
|
||||
v-model="themeConfig.isDark"
|
||||
@change="onAddDarkChange"
|
||||
inline-prompt
|
||||
active-color="#0a0a0a"
|
||||
inactive-color="#dcdfe6"
|
||||
:active-icon="Sunny"
|
||||
:inactive-icon="Moon"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="switchDark">
|
||||
import { computed } from 'vue';
|
||||
import { GlobalStore } from '@/store';
|
||||
import { Sunny, Moon } from '@element-plus/icons-vue';
|
||||
import { useTheme } from '@/hooks/use-theme';
|
||||
const globalStore = GlobalStore();
|
||||
|
||||
const { switchDark } = useTheme();
|
||||
|
||||
const themeConfig = computed(() => globalStore.themeConfig);
|
||||
|
||||
const onAddDarkChange = () => {
|
||||
switchDark();
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,4 @@
|
||||
// ? 全局不动配置项 只做导出不做修改
|
||||
|
||||
// * 首页地址(默认)
|
||||
export const HOME_URL: string = '/home/index';
|
||||
@@ -0,0 +1,11 @@
|
||||
import NProgress from 'nprogress';
|
||||
import 'nprogress/nprogress.css';
|
||||
|
||||
NProgress.configure({
|
||||
easing: 'ease', // 动画方式
|
||||
speed: 500, // 递增进度条的速度
|
||||
showSpinner: true, // 是否显示加载ico
|
||||
trickleSpeed: 200, // 自动递增间隔
|
||||
minimum: 0.3, // 初始化时的最小百分比
|
||||
});
|
||||
export default NProgress;
|
||||
@@ -0,0 +1,17 @@
|
||||
import { PersistedStateOptions } from 'pinia-plugin-persistedstate';
|
||||
|
||||
/**
|
||||
* @description pinia持久化参数配置
|
||||
* @param {String} key 存储到持久化的 name
|
||||
* @return persist
|
||||
* */
|
||||
const piniaPersistConfig = (key: string) => {
|
||||
const persist: PersistedStateOptions = {
|
||||
key,
|
||||
storage: window.localStorage,
|
||||
// storage: window.sessionStorage,
|
||||
};
|
||||
return persist;
|
||||
};
|
||||
|
||||
export default piniaPersistConfig;
|
||||
@@ -0,0 +1,35 @@
|
||||
import { ElLoading } from 'element-plus';
|
||||
|
||||
/* 全局请求 loading(服务方式调用) */
|
||||
let loadingInstance: ReturnType<typeof ElLoading.service>;
|
||||
|
||||
const startLoading = () => {
|
||||
loadingInstance = ElLoading.service({
|
||||
fullscreen: true,
|
||||
lock: true,
|
||||
text: 'Loading',
|
||||
background: 'rgba(0, 0, 0, 0.7)',
|
||||
});
|
||||
};
|
||||
const endLoading = () => {
|
||||
loadingInstance.close();
|
||||
};
|
||||
|
||||
// 那么 showFullScreenLoading() tryHideFullScreenLoading() 要做的事就是将同一时刻的请求合并。
|
||||
// 声明一个变量 needLoadingRequestCount,每次调用showFullScreenLoading方法 needLoadingRequestCount + 1。
|
||||
// 调用tryHideFullScreenLoading()方法,needLoadingRequestCount - 1。needLoadingRequestCount为 0 时,结束 loading。
|
||||
let needLoadingRequestCount = 0;
|
||||
export const showFullScreenLoading = () => {
|
||||
if (needLoadingRequestCount === 0) {
|
||||
startLoading();
|
||||
}
|
||||
needLoadingRequestCount++;
|
||||
};
|
||||
|
||||
export const tryHideFullScreenLoading = () => {
|
||||
if (needLoadingRequestCount <= 0) return;
|
||||
needLoadingRequestCount--;
|
||||
if (needLoadingRequestCount === 0) {
|
||||
endLoading();
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,28 @@
|
||||
import { App } from 'vue';
|
||||
import copy from './modules/copy';
|
||||
import waterMarker from './modules/water-marker';
|
||||
import draggable from './modules/draggable';
|
||||
import debounce from './modules/debounce';
|
||||
import throttle from './modules/throttle';
|
||||
import longpress from './modules/longpress';
|
||||
|
||||
const directivesList: any = {
|
||||
// Custom directives
|
||||
copy,
|
||||
waterMarker,
|
||||
draggable,
|
||||
debounce,
|
||||
throttle,
|
||||
longpress,
|
||||
};
|
||||
|
||||
const directives = {
|
||||
install: function (app: App<Element>) {
|
||||
Object.keys(directivesList).forEach((key) => {
|
||||
// 注册所有自定义指令
|
||||
app.directive(key, directivesList[key]);
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export default directives;
|
||||
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* v-copy
|
||||
* 复制某个值至剪贴板
|
||||
* 接收参数:string类型/Ref<string>类型/Reactive<string>类型
|
||||
*/
|
||||
import type { Directive, DirectiveBinding } from 'vue';
|
||||
import { ElMessage } from 'element-plus';
|
||||
interface ElType extends HTMLElement {
|
||||
copyData: string | number;
|
||||
__handleClick__: any;
|
||||
}
|
||||
const copy: Directive = {
|
||||
mounted(el: ElType, binding: DirectiveBinding) {
|
||||
el.copyData = binding.value;
|
||||
el.addEventListener('click', handleClick);
|
||||
},
|
||||
updated(el: ElType, binding: DirectiveBinding) {
|
||||
el.copyData = binding.value;
|
||||
},
|
||||
beforeUnmount(el: ElType) {
|
||||
el.removeEventListener('click', el.__handleClick__);
|
||||
},
|
||||
};
|
||||
|
||||
function handleClick(this: any) {
|
||||
const input = document.createElement('input');
|
||||
input.value = this.copyData.toLocaleString();
|
||||
document.body.appendChild(input);
|
||||
input.select();
|
||||
document.execCommand('Copy');
|
||||
document.body.removeChild(input);
|
||||
ElMessage({
|
||||
type: 'success',
|
||||
message: '复制成功',
|
||||
});
|
||||
}
|
||||
|
||||
export default copy;
|
||||
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* v-debounce
|
||||
* 按钮防抖指令,可自行扩展至input
|
||||
* 接收参数:function类型
|
||||
*/
|
||||
import type { Directive, DirectiveBinding } from 'vue';
|
||||
interface ElType extends HTMLElement {
|
||||
__handleClick__: () => any;
|
||||
}
|
||||
const debounce: Directive = {
|
||||
mounted(el: ElType, binding: DirectiveBinding) {
|
||||
if (typeof binding.value !== 'function') {
|
||||
throw 'callback must be a function';
|
||||
}
|
||||
let timer: NodeJS.Timeout | null = null;
|
||||
el.__handleClick__ = function () {
|
||||
if (timer) {
|
||||
clearInterval(timer);
|
||||
}
|
||||
timer = setTimeout(() => {
|
||||
binding.value();
|
||||
}, 500);
|
||||
};
|
||||
el.addEventListener('click', el.__handleClick__);
|
||||
},
|
||||
beforeUnmount(el: ElType) {
|
||||
el.removeEventListener('click', el.__handleClick__);
|
||||
},
|
||||
};
|
||||
|
||||
export default debounce;
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
需求:实现一个拖拽指令,可在父元素区域任意拖拽元素。
|
||||
|
||||
思路:
|
||||
1、设置需要拖拽的元素为absolute,其父元素为relative。
|
||||
2、鼠标按下(onmousedown)时记录目标元素当前的 left 和 top 值。
|
||||
3、鼠标移动(onmousemove)时计算每次移动的横向距离和纵向距离的变化值,并改变元素的 left 和 top 值
|
||||
4、鼠标松开(onmouseup)时完成一次拖拽
|
||||
|
||||
使用:在 Dom 上加上 v-draggable 即可
|
||||
<div class="dialog-model" v-draggable></div>
|
||||
*/
|
||||
import type { Directive } from 'vue';
|
||||
interface ElType extends HTMLElement {
|
||||
parentNode: any;
|
||||
}
|
||||
const draggable: Directive = {
|
||||
mounted: function (el: ElType) {
|
||||
el.style.cursor = 'move';
|
||||
el.style.position = 'absolute';
|
||||
el.onmousedown = function (e) {
|
||||
let disX = e.pageX - el.offsetLeft;
|
||||
let disY = e.pageY - el.offsetTop;
|
||||
document.onmousemove = function (e) {
|
||||
let x = e.pageX - disX;
|
||||
let y = e.pageY - disY;
|
||||
let maxX = el.parentNode.offsetWidth - el.offsetWidth;
|
||||
let maxY = el.parentNode.offsetHeight - el.offsetHeight;
|
||||
if (x < 0) {
|
||||
x = 0;
|
||||
} else if (x > maxX) {
|
||||
x = maxX;
|
||||
}
|
||||
|
||||
if (y < 0) {
|
||||
y = 0;
|
||||
} else if (y > maxY) {
|
||||
y = maxY;
|
||||
}
|
||||
el.style.left = x + 'px';
|
||||
el.style.top = y + 'px';
|
||||
};
|
||||
document.onmouseup = function () {
|
||||
document.onmousemove = document.onmouseup = null;
|
||||
};
|
||||
};
|
||||
},
|
||||
};
|
||||
export default draggable;
|
||||
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* v-longpress
|
||||
* 长按指令,长按时触发事件
|
||||
*/
|
||||
import type { Directive, DirectiveBinding } from 'vue';
|
||||
|
||||
const directive: Directive = {
|
||||
mounted(el: HTMLElement, binding: DirectiveBinding) {
|
||||
if (typeof binding.value !== 'function') {
|
||||
throw 'callback must be a function';
|
||||
}
|
||||
// 定义变量
|
||||
let pressTimer: any = null;
|
||||
// 创建计时器( 2秒后执行函数 )
|
||||
const start = (e: any) => {
|
||||
if (e.button) {
|
||||
if (e.type === 'click' && e.button !== 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (pressTimer === null) {
|
||||
pressTimer = setTimeout(() => {
|
||||
handler(e);
|
||||
}, 1000);
|
||||
}
|
||||
};
|
||||
// 取消计时器
|
||||
const cancel = () => {
|
||||
if (pressTimer !== null) {
|
||||
clearTimeout(pressTimer);
|
||||
pressTimer = null;
|
||||
}
|
||||
};
|
||||
// 运行函数
|
||||
const handler = (e: MouseEvent | TouchEvent) => {
|
||||
binding.value(e);
|
||||
};
|
||||
// 添加事件监听器
|
||||
el.addEventListener('mousedown', start);
|
||||
el.addEventListener('touchstart', start);
|
||||
// 取消计时器
|
||||
el.addEventListener('click', cancel);
|
||||
el.addEventListener('mouseout', cancel);
|
||||
el.addEventListener('touchend', cancel);
|
||||
el.addEventListener('touchcancel', cancel);
|
||||
},
|
||||
};
|
||||
|
||||
export default directive;
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
需求:防止按钮在短时间内被多次点击,使用节流函数限制规定时间内只能点击一次。
|
||||
|
||||
思路:
|
||||
1、第一次点击,立即调用方法并禁用按钮,等延迟结束再次激活按钮
|
||||
2、将需要触发的方法绑定在指令上
|
||||
|
||||
使用:给 Dom 加上 v-throttle 及回调函数即可
|
||||
<button v-throttle="debounceClick">节流提交</button>
|
||||
*/
|
||||
import type { Directive, DirectiveBinding } from 'vue';
|
||||
interface ElType extends HTMLElement {
|
||||
__handleClick__: () => any;
|
||||
disabled: boolean;
|
||||
}
|
||||
const throttle: Directive = {
|
||||
mounted(el: ElType, binding: DirectiveBinding) {
|
||||
if (typeof binding.value !== 'function') {
|
||||
throw 'callback must be a function';
|
||||
}
|
||||
let timer: NodeJS.Timeout | null = null;
|
||||
el.__handleClick__ = function () {
|
||||
if (timer) {
|
||||
clearTimeout(timer);
|
||||
}
|
||||
if (!el.disabled) {
|
||||
el.disabled = true;
|
||||
binding.value();
|
||||
timer = setTimeout(() => {
|
||||
el.disabled = false;
|
||||
}, 1000);
|
||||
}
|
||||
};
|
||||
el.addEventListener('click', el.__handleClick__);
|
||||
},
|
||||
beforeUnmount(el: ElType) {
|
||||
el.removeEventListener('click', el.__handleClick__);
|
||||
},
|
||||
};
|
||||
|
||||
export default throttle;
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
需求:给整个页面添加背景水印。
|
||||
|
||||
思路:
|
||||
1、使用 canvas 特性生成 base64 格式的图片文件,设置其字体大小,颜色等。
|
||||
2、将其设置为背景图片,从而实现页面或组件水印效果
|
||||
|
||||
使用:设置水印文案,颜色,字体大小即可
|
||||
<div v-waterMarker="{text:'版权所有',textColor:'rgba(180, 180, 180, 0.4)'}"></div>
|
||||
*/
|
||||
|
||||
import type { Directive, DirectiveBinding } from 'vue';
|
||||
const addWaterMarker: Directive = (str: string, parentNode: any, font: any, textColor: string) => {
|
||||
// 水印文字,父元素,字体,文字颜色
|
||||
let can: HTMLCanvasElement = document.createElement('canvas');
|
||||
parentNode.appendChild(can);
|
||||
can.width = 200;
|
||||
can.height = 150;
|
||||
can.style.display = 'none';
|
||||
let cans = can.getContext('2d') as CanvasRenderingContext2D;
|
||||
cans.rotate((-20 * Math.PI) / 180);
|
||||
cans.font = font || '16px Microsoft JhengHei';
|
||||
cans.fillStyle = textColor || 'rgba(180, 180, 180, 0.3)';
|
||||
cans.textAlign = 'left';
|
||||
cans.textBaseline = 'Middle' as CanvasTextBaseline;
|
||||
cans.fillText(str, can.width / 10, can.height / 2);
|
||||
parentNode.style.backgroundImage = 'url(' + can.toDataURL('image/png') + ')';
|
||||
};
|
||||
|
||||
const waterMarker = {
|
||||
mounted(el: DirectiveBinding, binding: DirectiveBinding) {
|
||||
addWaterMarker(binding.value.text, el, binding.value.font, binding.value.textColor);
|
||||
},
|
||||
};
|
||||
|
||||
export default waterMarker;
|
||||
@@ -0,0 +1,32 @@
|
||||
export enum ResultEnum {
|
||||
SUCCESS = 200,
|
||||
ERROR = 500,
|
||||
OVERDUE = 401,
|
||||
TIMEOUT = 10000,
|
||||
TYPE = 'success',
|
||||
}
|
||||
|
||||
/**
|
||||
* @description:请求方法
|
||||
*/
|
||||
export enum RequestEnum {
|
||||
GET = 'GET',
|
||||
POST = 'POST',
|
||||
PATCH = 'PATCH',
|
||||
PUT = 'PUT',
|
||||
DELETE = 'DELETE',
|
||||
}
|
||||
|
||||
/**
|
||||
* @description:常用的contentTyp类型
|
||||
*/
|
||||
export enum ContentTypeEnum {
|
||||
// json
|
||||
JSON = 'application/json;charset=UTF-8',
|
||||
// text
|
||||
TEXT = 'text/plain;charset=UTF-8',
|
||||
// form-data 一般配合qs
|
||||
FORM_URLENCODED = 'application/x-www-form-urlencoded;charset=UTF-8',
|
||||
// form-data 上传
|
||||
FORM_DATA = 'multipart/form-data;charset=UTF-8',
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
/// <reference types="vite/client" />
|
||||
|
||||
declare module '*.vue' {
|
||||
import type { DefineComponent } from 'vue';
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
|
||||
const component: DefineComponent<{}, {}, any>;
|
||||
export default component;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import i18n from '@/lang';
|
||||
import { FormItemRule } from 'element-plus';
|
||||
|
||||
interface CommonRule {
|
||||
required: FormItemRule;
|
||||
name: FormItemRule;
|
||||
email: FormItemRule;
|
||||
}
|
||||
|
||||
export const Rules: CommonRule = {
|
||||
required: {
|
||||
required: true,
|
||||
message: i18n.global.t('commons.rule.required'),
|
||||
trigger: 'blur',
|
||||
},
|
||||
name: {
|
||||
type: 'regexp',
|
||||
min: 1,
|
||||
max: 30,
|
||||
message: i18n.global.t('commons.rule.commonName'),
|
||||
trigger: 'blur',
|
||||
pattern: '/^[a-zA-Z0-9\u4e00-\u9fa5]{1}[a-zA-Z0-9_.\u4e00-\u9fa5-]{0,30}$/',
|
||||
},
|
||||
email: {
|
||||
type: 'email',
|
||||
message: i18n.global.t('commons.rule.email'),
|
||||
trigger: 'blur',
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,3 @@
|
||||
export namespace HandleData {
|
||||
export type MessageType = '' | 'success' | 'warning' | 'info' | 'error';
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import { ElMessageBox, ElMessage } from 'element-plus';
|
||||
import { HandleData } from './interface';
|
||||
import i18n from '@/lang';
|
||||
|
||||
/**
|
||||
* @description 删除操作使用
|
||||
* @param {Function} api 操作数据接口的api方法(必传)
|
||||
* @param {Object} params 携带的操作数据参数 {id,params}(必传)
|
||||
* @param {String} message 提示信息(必传)
|
||||
* @param {String} confirmType icon类型(不必传,默认为 warning)
|
||||
* @return Promise
|
||||
*/
|
||||
export const useDeleteData = <P = any, R = any>(
|
||||
api: (params: P) => Promise<R>,
|
||||
params: Parameters<typeof api>[0],
|
||||
message: string,
|
||||
confirmType: HandleData.MessageType = 'error',
|
||||
) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
ElMessageBox.confirm(i18n.global.t(`${message}`) + '?', i18n.global.t('commons.msg.deleteTitle'), {
|
||||
confirmButtonText: i18n.global.t('commons.button.confirm'),
|
||||
cancelButtonText: i18n.global.t('commons.button.cancel'),
|
||||
type: confirmType,
|
||||
draggable: true,
|
||||
}).then(async () => {
|
||||
const res = await api(params);
|
||||
if (!res) return reject(false);
|
||||
ElMessage({
|
||||
type: 'success',
|
||||
message: i18n.global.t('commons.msg.deleteSuccess'),
|
||||
});
|
||||
resolve(true);
|
||||
});
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,49 @@
|
||||
import { ElNotification } from 'element-plus';
|
||||
|
||||
/**
|
||||
* @description 接收数据流生成blob,创建链接,下载文件
|
||||
* @param {Function} api 导出表格的api方法(必传)
|
||||
* @param {String} tempName 导出的文件名(必传)
|
||||
* @param {Object} params 导出的参数(默认为空对象)
|
||||
* @param {Boolean} isNotify 是否有导出消息提示(默认为 true)
|
||||
* @param {String} fileType 导出的文件格式(默认为.xlsx)
|
||||
* @return void
|
||||
* */
|
||||
export const useDownload = async (
|
||||
api: (param: any) => Promise<any>,
|
||||
tempName: string,
|
||||
params: any = {},
|
||||
isNotify: boolean = true,
|
||||
fileType: string = '.xlsx',
|
||||
) => {
|
||||
if (isNotify) {
|
||||
ElNotification({
|
||||
title: '温馨提示',
|
||||
message: '如果数据庞大会导致下载缓慢哦,请您耐心等待!',
|
||||
type: 'info',
|
||||
duration: 3000,
|
||||
});
|
||||
}
|
||||
try {
|
||||
const res = await api(params);
|
||||
// 这个地方的type,经测试不传也没事,因为zip文件不知道type是什么
|
||||
// const blob = new Blob([res], {
|
||||
// type: "application/vnd.ms-excel;charset=UTF-8"
|
||||
// });
|
||||
const blob = new Blob([res]);
|
||||
// 兼容edge不支持createObjectURL方法
|
||||
if ('msSaveOrOpenBlob' in navigator) return window.navigator.msSaveOrOpenBlob(blob, tempName + fileType);
|
||||
const blobUrl = window.URL.createObjectURL(blob);
|
||||
const exportFile = document.createElement('a');
|
||||
exportFile.style.display = 'none';
|
||||
exportFile.download = `${tempName}${fileType}`;
|
||||
exportFile.href = blobUrl;
|
||||
document.body.appendChild(exportFile);
|
||||
exportFile.click();
|
||||
// 去除下载对url的影响
|
||||
document.body.removeChild(exportFile);
|
||||
window.URL.revokeObjectURL(blobUrl);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,23 @@
|
||||
import { onUnmounted } from 'vue';
|
||||
import * as echarts from 'echarts';
|
||||
|
||||
/**
|
||||
* @description 使用Echarts(只是为了添加图表响应式)
|
||||
* @param {Element} myChart Echarts实例(必传)
|
||||
* @param {Object} options 绘制Echarts的参数(必传)
|
||||
* @return void
|
||||
* */
|
||||
export const useEcharts = (myChart: echarts.ECharts, options: echarts.EChartsCoreOption) => {
|
||||
if (options && typeof options === 'object') {
|
||||
myChart.setOption(options);
|
||||
}
|
||||
const echartsResize = () => {
|
||||
myChart && myChart.resize();
|
||||
};
|
||||
|
||||
window.addEventListener('resize', echartsResize, false);
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('resize', echartsResize);
|
||||
});
|
||||
};
|
||||