This commit is contained in:
loser 2023-10-11 15:11:50 +08:00
parent 718493b831
commit e92b749eb8
155 changed files with 18534 additions and 13 deletions

1
.dockerignore Normal file
View File

@ -0,0 +1 @@
node_modules/

11
.env.development Normal file
View File

@ -0,0 +1,11 @@
ENV = 'development'
VITE_CLI_PORT = 8080
VITE_SERVER_PORT = 8888
VITE_BASE_API = /api
VITE_FILE_API = /api
VITE_BASE_PATH = http://127.0.0.1
VITE_EDITOR = vscode
// VITE_EDITOR = webstorm 如果使用webstorm开发且要使用dom定位到代码行功能 请先自定添加 webstorm到环境变量 再将VITE_EDITOR值修改为webstorm
// 如果使用docker-compose开发模式设置为下面的地址或本机主机IP
//VITE_BASE_PATH = http://177.7.0.12

8
.env.production Normal file
View File

@ -0,0 +1,8 @@
ENV = 'production'
VITE_CLI_PORT = 8080
VITE_SERVER_PORT = 8888
VITE_BASE_API = /api
VITE_FILE_API = /api
#下方修改为你的线上ip
VITE_BASE_PATH = https://demo.gin-vue-admin.com

4
.eslintignore Normal file
View File

@ -0,0 +1,4 @@
build/*.js
src/assets
public
dist

256
.eslintrc.js Normal file
View File

@ -0,0 +1,256 @@
module.exports = {
root: true,
parserOptions: {
parser: '@babel/eslint-parser',
sourceType: 'module'
},
env: {
browser: true,
node: true,
es6: true
},
extends: ['plugin:vue/recommended', 'eslint:recommended'],
rules: {
'vue/no-v-model-argument': 0,
'vue/max-attributes-per-line': 2,
'vue/singleline-html-element-content-newline': 'off',
'vue/multiline-html-element-content-newline': 'off',
'vue/multi-word-component-names': 'off',
'vue/no-v-html': 'off',
'accessor-pairs': 2,
'arrow-spacing': [
2,
{
before: true,
after: true
}
],
'block-spacing': [2, 'always'],
'brace-style': [
2,
'1tbs',
{
allowSingleLine: true
}
],
camelcase: [
0,
{
properties: 'always'
}
],
'comma-dangle': [2, 'only-multiline'],
'comma-spacing': [
2,
{
before: false,
after: true
}
],
'comma-style': [2, 'last'],
'constructor-super': 2,
curly: [2, 'multi-line'],
'dot-location': [2, 'property'],
'eol-last': 2,
eqeqeq: ['error', 'always', { null: 'ignore' }],
'generator-star-spacing': [
2,
{
before: true,
after: true
}
],
'handle-callback-err': [2, '^(err|error)$'],
indent: [
2,
2,
{
SwitchCase: 1
}
],
'jsx-quotes': [2, 'prefer-single'],
'key-spacing': [
2,
{
beforeColon: false,
afterColon: true
}
],
'keyword-spacing': [
2,
{
before: true,
after: true
}
],
'new-cap': [
2,
{
newIsCap: true,
capIsNew: false
}
],
'new-parens': 2,
'no-array-constructor': 2,
'no-caller': 2,
'no-console': 'off',
'no-class-assign': 2,
'no-cond-assign': 2,
'no-const-assign': 2,
'no-control-regex': 0,
'no-delete-var': 2,
'no-dupe-args': 2,
'no-dupe-class-members': 2,
'no-dupe-keys': 2,
'no-duplicate-case': 2,
'no-empty-character-class': 2,
'no-empty-pattern': 2,
'no-eval': 2,
'no-ex-assign': 2,
'no-extend-native': 2,
'no-extra-bind': 2,
'no-extra-boolean-cast': 2,
'no-extra-parens': [2, 'functions'],
'no-fallthrough': 2,
'no-floating-decimal': 2,
'no-func-assign': 2,
'no-implied-eval': 2,
'no-inner-declarations': [2, 'functions'],
'no-invalid-regexp': 2,
'no-irregular-whitespace': 2,
'no-iterator': 2,
'no-label-var': 2,
'no-labels': [
2,
{
allowLoop: false,
allowSwitch: false
}
],
'no-lone-blocks': 2,
'no-mixed-spaces-and-tabs': 2,
'no-multi-spaces': 2,
'no-multi-str': 2,
'no-multiple-empty-lines': [
2,
{
max: 1
}
],
'no-native-reassign': 2,
'no-negated-in-lhs': 2,
'no-new-object': 2,
'no-new-require': 2,
'no-new-symbol': 2,
'no-new-wrappers': 2,
'no-obj-calls': 2,
'no-octal': 2,
'no-octal-escape': 2,
'no-path-concat': 2,
'no-proto': 2,
'no-redeclare': 2,
'no-regex-spaces': 2,
'no-return-assign': [2, 'except-parens'],
'no-self-assign': 2,
'no-self-compare': 2,
'no-sequences': 2,
'no-shadow-restricted-names': 2,
'no-spaced-func': 2,
'no-sparse-arrays': 2,
'no-this-before-super': 2,
'no-throw-literal': 2,
'no-trailing-spaces': 2,
'no-undef': 'off',
'no-undef-init': 2,
'no-unexpected-multiline': 2,
'no-unmodified-loop-condition': 2,
'no-unneeded-ternary': [
2,
{
defaultAssignment: false
}
],
'no-unreachable': 2,
'no-unsafe-finally': 2,
'no-unused-vars': [
2,
{
vars: 'all',
args: 'none'
}
],
'no-useless-call': 2,
'no-useless-computed-key': 2,
'no-useless-constructor': 2,
'no-useless-escape': 0,
'no-whitespace-before-property': 2,
'no-with': 2,
'one-var': [
2,
{
initialized: 'never'
}
],
'operator-linebreak': [
2,
'after',
{
overrides: {
'?': 'before',
':': 'before'
}
}
],
'padded-blocks': [2, 'never'],
quotes: [
2,
'single',
{
avoidEscape: true,
allowTemplateLiterals: true
}
],
semi: [2, 'never'],
'semi-spacing': [
2,
{
before: false,
after: true
}
],
'space-before-blocks': [2, 'always'],
'space-before-function-paren': [2, 'never'],
'space-in-parens': [2, 'never'],
'space-infix-ops': 2,
'space-unary-ops': [
2,
{
words: true,
nonwords: false
}
],
'spaced-comment': [
2,
'always',
{
markers: ['global', 'globals', 'eslint', 'eslint-disable', '*package', '!', ',']
}
],
'template-curly-spacing': [2, 'never'],
'use-isnan': 2,
'valid-typeof': 2,
'wrap-iife': [2, 'any'],
'yield-star-spacing': [2, 'both'],
yoda: [2, 'never'],
'prefer-const': 2,
'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0,
'object-curly-spacing': [
2,
'always',
{
objectsInObjects: false
}
],
'array-bracket-spacing': [2, 'never']
}
}

14
.gitignore vendored
View File

@ -1,11 +1,3 @@
# ---> Vue
# gitignore template for Vue.js projects
#
# Recommended template: Node.gitignore
# TODO: where does this rule come from?
docs/_book
# TODO: where does this rule come from?
test/
node_modules/*
package-lock.json
yarn.lock

15
Dockerfile Normal file
View File

@ -0,0 +1,15 @@
FROM node:16
WORKDIR /gva_web/
COPY . .
RUN yarn && yarn build
FROM nginx:alpine
LABEL MAINTAINER="SliverHorn@sliver_horn@qq.com"
COPY .docker-compose/nginx/conf.d/my.conf /etc/nginx/conf.d/my.conf
COPY --from=0 /gva_web/dist /usr/share/nginx/html
RUN cat /etc/nginx/nginx.conf
RUN cat /etc/nginx/conf.d/my.conf
RUN ls -al /usr/share/nginx/html

102
README.md
View File

@ -1,3 +1,101 @@
# JM-admin-ui
# gin-vue-admin web
明小程序管理后台
## Project setup
```
npm install
```
### Compiles and hot-reloads for development
```
npm run serve
```
### Compiles and minifies for production
```
npm run build
```
### Run your tests
```
npm run test
```
### Lints and fixes files
```
npm run lint
```
整理代码结构
``` lua
web
├── babel.config.js
├── Dockerfile
├── favicon.ico
├── index.html -- 主页面
├── limit.js -- 助手代码
├── package.json -- 包管理器代码
├── src -- 源代码
│ ├── api -- api 组
│ ├── App.vue -- 主页面
│ ├── assets -- 静态资源
│ ├── components -- 全局组件
│ ├── core -- gva 组件包
│ │ ├── config.js -- gva网站配置文件
│ │ ├── gin-vue-admin.js -- 注册欢迎文件
│ │ └── global.js -- 统一导入文件
│ ├── directive -- v-auth 注册文件
│ ├── main.js -- 主文件
│ ├── permission.js -- 路由中间件
│ ├── pinia -- pinia 状态管理器取代vuex
│ │ ├── index.js -- 入口文件
│ │ └── modules -- modules
│ │ ├── dictionary.js
│ │ ├── router.js
│ │ └── user.js
│ ├── router -- 路由声明文件
│ │ └── index.js
│ ├── style -- 全局样式
│ │ ├── base.scss
│ │ ├── basics.scss
│ │ ├── element_visiable.scss -- 此处可以全局覆盖 element-plus 样式
│ │ ├── iconfont.css -- 顶部几个icon的样式文件
│ │ ├── main.scss
│ │ ├── mobile.scss
│ │ └── newLogin.scss
│ ├── utils -- 方法包库
│ │ ├── asyncRouter.js -- 动态路由相关
│ │ ├── bus.js -- 全局mitt声明文件
│ │ ├── date.js -- 日期相关
│ │ ├── dictionary.js -- 获取字典方法
│ │ ├── downloadImg.js -- 下载图片方法
│ │ ├── format.js -- 格式整理相关
│ │ ├── image.js -- 图片相关方法
│ │ ├── page.js -- 设置页面标题
│ │ ├── request.js -- 请求
│ │ └── stringFun.js -- 字符串文件
| ├── view -- 主要view代码
| | ├── about -- 关于我们
| | ├── dashboard -- 面板
| | ├── error -- 错误
| | ├── example --上传案例
| | ├── iconList -- icon列表
| | ├── init -- 初始化数据
| | | ├── index -- 新版本
| | | ├── init -- 旧版本
| | ├── layout -- layout约束页面
| | | ├── aside
| | | ├── bottomInfo -- bottomInfo
| | | ├── screenfull -- 全屏设置
| | | ├── setting -- 系统设置
| | | └── index.vue -- base 约束
| | ├── login --登录
| | ├── person --个人中心
| | ├── superAdmin -- 超级管理员操作
| | ├── system -- 系统检测页面
| | ├── systemTools -- 系统配置相关页面
| | └── routerHolder.vue -- page 入口页面
├── vite.config.js -- vite 配置文件
└── yarn.lock
```

8
babel.config.js Normal file
View File

@ -0,0 +1,8 @@
module.exports = {
presets: [
],
'plugins': [
]
}

BIN
favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

18
index.html Normal file
View File

@ -0,0 +1,18 @@
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<meta content="Gin,Vue,Admin.Gin-Vue-Admin,GVA,gin-vue-admin,后台管理框架,vue后台管理框架,gin-vue-admin文档,gin-vue-admin首页,gin-vue-admin" name="keywords" />
<link rel="icon" href="favicon.ico">
<title></title>
</head>
<body>
<div id="app"></div>
<script type="module" src="./src/main.js"></script>
</body>
</html>

10
jsconfig.json Normal file
View File

@ -0,0 +1,10 @@
{
"compilerOptions": {
"baseUrl": "./",
"paths": {
"@/*": ["src/*"],
}
},
"exclude": ["node_modules", "dist"],
"include": ["src/**/*"]
}

37
limit.js Normal file
View File

@ -0,0 +1,37 @@
// 运行项目前通过node执行此脚本 (此脚本与 node_modules 目录同级)
const fs = require('fs')
const path = require('path')
const wfPath = path.resolve(__dirname, './node_modules/.bin')
fs.readdir(wfPath, (err, files) => {
if (err) {
console.log(err)
} else {
if (files.length !== 0) {
files.forEach((item) => {
if (item.split('.')[1] === 'cmd') {
replaceStr(`${wfPath}/${item}`, /"%_prog%"/, '%_prog%')
}
})
}
}
})
// 参数:[文件路径、 需要修改的字符串、修改后的字符串] (替换对应文件内字符串的公共函数)
function replaceStr(filePath, sourceRegx, targetSrt) {
fs.readFile(filePath, (err, data) => {
if (err) {
console.log(err)
} else {
let str = data.toString()
str = str.replace(sourceRegx, targetSrt)
fs.writeFile(filePath, str, (err) => {
if (err) {
console.log(err)
} else {
console.log('\x1B[42m%s\x1B[0m', '文件修改成功')
}
})
}
})
}

23
openDocument.js Normal file
View File

@ -0,0 +1,23 @@
/*
商用代码公司自用产品无需授权
若作为代码出售的产品任何涉及代码交付第三方作为后续开发必须保留此脚本
或标注原作者信息
否则将依法维权
*/
var child_process = require('child_process')
var url = 'https://www.gin-vue-admin.com'
var cmd = ''
console.log(process.platform)
switch (process.platform) {
case 'win32':
cmd = 'start'
child_process.exec(cmd + ' ' + url)
break
case 'darwin':
cmd = 'open'
child_process.exec(cmd + ' ' + url)
break
}

59
package.json Normal file
View File

@ -0,0 +1,59 @@
{
"name": "gin-vue-admin",
"version": "2.5.7",
"private": true,
"scripts": {
"serve": "vite --host --mode development",
"build": "vite build --mode production",
"limit-build": "npm install increase-memory-limit-fixbug cross-env -g && npm run fix-memory-limit && node ./limit && npm run build",
"preview": "vite preview",
"fix-memory-limit": "cross-env LIMIT=4096 increase-memory-limit"
},
"dependencies": {
"@element-plus/icons-vue": "^2.1.0",
"@vue-office/docx": "^1.3.0",
"@vue-office/excel": "^1.4.5",
"@vue-office/pdf": "^1.5.3",
"@wangeditor/editor": "^5.1.23",
"@wangeditor/editor-for-vue": "^5.1.12",
"axios": "^1.4.0",
"core-js": "^3.31.1",
"echarts": "5.4.3",
"element-plus": "^2.3.8",
"highlight.js": "^11.8.0",
"marked": "4.3.0",
"mitt": "^3.0.1",
"nprogress": "^0.2.0",
"path": "^0.12.7",
"pinia": "^2.1.4",
"qs": "^6.11.2",
"screenfull": "^6.0.2",
"spark-md5": "^3.0.2",
"tailwindcss": "^3.3.3",
"vue": "^3.3.4",
"vue-router": "^4.2.4"
},
"devDependencies": {
"@babel/eslint-parser": "^7.22.9",
"@vitejs/plugin-legacy": "^4.1.0",
"@vitejs/plugin-vue": "^4.2.3",
"@vue/cli-plugin-babel": "~5.0.8",
"@vue/cli-plugin-eslint": "~5.0.8",
"@vue/cli-plugin-router": "~5.0.8",
"@vue/cli-plugin-vuex": "~5.0.8",
"@vue/cli-service": "~5.0.8",
"@vue/compiler-sfc": "^3.3.4",
"babel-plugin-import": "^1.13.6",
"chalk": "^4.1.2",
"dotenv": "^16.3.1",
"eslint": "^8.49.0",
"eslint-plugin-vue": "^9.15.1",
"sass": "^1.54.0",
"terser": "^5.19.1",
"unplugin-auto-import": "^0.16.6",
"unplugin-vue-components": "^0.25.1",
"vite": "^4.4.6",
"vite-plugin-banner": "^0.7.0",
"vite-plugin-importer": "^0.2.5"
}
}

6
postcss.config.js Normal file
View File

@ -0,0 +1,6 @@
module.exports = {
plugins: {
tailwindcss: {},
// autoprefixer: {},
},
}

33
src/App.vue Normal file
View File

@ -0,0 +1,33 @@
<template>
<div id="app">
<el-config-provider :locale="zhCn">
<router-view />
</el-config-provider>
</div>
</template>
<script setup>
import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
// element 2.3.8使
// import zhCn from 'element-plus/lib/locale/lang/zh-cn'
defineOptions({
name: 'App'
})
</script>
<style lang="scss">
@tailwind base;
@tailwind components;
@tailwind utilities;
//
#app {
background: #eee;
height: 100vh;
overflow: hidden;
font-weight: 400 !important;
}
.el-button{
font-weight: 400 !important;
}
</style>

146
src/api/api.js Normal file
View File

@ -0,0 +1,146 @@
import service from '@/utils/request'
// @Tags api
// @Summary 分页获取角色列表
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body modelInterface.PageInfo true "分页获取用户列表"
// @Success 200 {string} json "{"success":true,"data":{},"msg":"获取成功"}"
// @Router /api/getApiList [post]
// {
// page int
// pageSize int
// }
export const getApiList = (data) => {
return service({
url: '/api/getApiList',
method: 'post',
data
})
}
// @Tags Api
// @Summary 创建基础api
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body api.CreateApiParams true "创建api"
// @Success 200 {string} json "{"success":true,"data":{},"msg":"获取成功"}"
// @Router /api/createApi [post]
export const createApi = (data) => {
return service({
url: '/api/createApi',
method: 'post',
data
})
}
// @Tags menu
// @Summary 根据id获取菜单
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body api.GetById true "根据id获取菜单"
// @Success 200 {string} json "{"success":true,"data":{},"msg":"获取成功"}"
// @Router /menu/getApiById [post]
export const getApiById = (data) => {
return service({
url: '/api/getApiById',
method: 'post',
data
})
}
// @Tags Api
// @Summary 更新api
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body api.CreateApiParams true "更新api"
// @Success 200 {string} json "{"success":true,"data":{},"msg":"更新成功"}"
// @Router /api/updateApi [post]
export const updateApi = (data) => {
return service({
url: '/api/updateApi',
method: 'post',
data
})
}
// @Tags Api
// @Summary 更新api
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body api.CreateApiParams true "更新api"
// @Success 200 {string} json "{"success":true,"data":{},"msg":"更新成功"}"
// @Router /api/setAuthApi [post]
export const setAuthApi = (data) => {
return service({
url: '/api/setAuthApi',
method: 'post',
data
})
}
// @Tags Api
// @Summary 获取所有的Api 不分页
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Success 200 {string} json "{"success":true,"data":{},"msg":"获取成功"}"
// @Router /api/getAllApis [post]
export const getAllApis = (data) => {
return service({
url: '/api/getAllApis',
method: 'post',
data
})
}
// @Tags Api
// @Summary 删除指定api
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body dbModel.Api true "删除api"
// @Success 200 {string} json "{"success":true,"data":{},"msg":"删除成功"}"
// @Router /api/deleteApi [post]
export const deleteApi = (data) => {
return service({
url: '/api/deleteApi',
method: 'post',
data
})
}
// @Tags SysApi
// @Summary 删除选中Api
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body request.IdsReq true "ID"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"删除成功"}"
// @Router /api/deleteApisByIds [delete]
export const deleteApisByIds = (data) => {
return service({
url: '/api/deleteApisByIds',
method: 'delete',
data
})
}
// FreshCasbin
// @Tags SysApi
// @Summary 刷新casbin缓存
// @accept application/json
// @Produce application/json
// @Success 200 {object} response.Response{msg=string} "刷新成功"
// @Router /api/freshCasbin [get]
export const freshCasbin = () => {
return service({
url: '/api/freshCasbin',
method: 'get'
})
}

85
src/api/authority.js Normal file
View File

@ -0,0 +1,85 @@
import service from '@/utils/request'
// @Router /authority/getAuthorityList [post]
export const getAuthorityList = (data) => {
return service({
url: '/authority/getAuthorityList',
method: 'post',
data
})
}
// @Summary 删除角色
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body {authorityId uint} true "删除角色"
// @Success 200 {string} json "{"success":true,"data":{},"msg":"获取成功"}"
// @Router /authority/deleteAuthority [post]
export const deleteAuthority = (data) => {
return service({
url: '/authority/deleteAuthority',
method: 'post',
data
})
}
// @Summary 创建角色
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body api.CreateAuthorityPatams true "创建角色"
// @Success 200 {string} json "{"success":true,"data":{},"msg":"获取成功"}"
// @Router /authority/createAuthority [post]
export const createAuthority = (data) => {
return service({
url: '/authority/createAuthority',
method: 'post',
data
})
}
// @Tags authority
// @Summary 拷贝角色
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body api.CreateAuthorityPatams true "拷贝角色"
// @Success 200 {string} json "{"success":true,"data":{},"msg":"拷贝成功"}"
// @Router /authority/copyAuthority [post]
export const copyAuthority = (data) => {
return service({
url: '/authority/copyAuthority',
method: 'post',
data
})
}
// @Summary 设置角色资源权限
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body sysModel.SysAuthority true "设置角色资源权限"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"设置成功"}"
// @Router /authority/setDataAuthority [post]
export const setDataAuthority = (data) => {
return service({
url: '/authority/setDataAuthority',
method: 'post',
data
})
}
// @Summary 修改角色
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body model.SysAuthority true "修改角色"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"设置成功"}"
// @Router /authority/setDataAuthority [post]
export const updateAuthority = (data) => {
return service({
url: '/authority/updateAuthority',
method: 'put',
data
})
}

27
src/api/authorityBtn.js Normal file
View File

@ -0,0 +1,27 @@
import service from '@/utils/request'
export const getAuthorityBtnApi = (data) => {
return service({
url: '/authorityBtn/getAuthorityBtn',
method: 'post',
data
})
}
export const setAuthorityBtnApi = (data) => {
return service({
url: '/authorityBtn/setAuthorityBtn',
method: 'post',
data
})
}
export const canRemoveAuthorityBtnApi = (params) => {
return service({
url: '/authorityBtn/canRemoveAuthorityBtn',
method: 'post',
params
})
}

142
src/api/autoCode.js Normal file
View File

@ -0,0 +1,142 @@
import service from '@/utils/request'
export const preview = (data) => {
return service({
url: '/autoCode/preview',
method: 'post',
data
})
}
export const createTemp = (data) => {
return service({
url: '/autoCode/createTemp',
method: 'post',
data,
responseType: 'blob'
})
}
// @Tags SysApi
// @Summary 获取当前所有数据库
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Success 200 {string} string "{"success":true,"data":{},"msg":"创建成功"}"
// @Router /autoCode/getDatabase [get]
export const getDB = (params) => {
return service({
url: '/autoCode/getDB',
method: 'get',
params
})
}
// @Tags SysApi
// @Summary 获取当前数据库所有表
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Success 200 {string} string "{"success":true,"data":{},"msg":"创建成功"}"
// @Router /autoCode/getTables [get]
export const getTable = (params) => {
return service({
url: '/autoCode/getTables',
method: 'get',
params
})
}
// @Tags SysApi
// @Summary 获取当前数据库所有表
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Success 200 {string} string "{"success":true,"data":{},"msg":"创建成功"}"
// @Router /autoCode/getColumn [get]
export const getColumn = (params) => {
return service({
url: '/autoCode/getColumn',
method: 'get',
params
})
}
export const getSysHistory = (data) => {
return service({
url: '/autoCode/getSysHistory',
method: 'post',
data
})
}
export const rollback = (data) => {
return service({
url: '/autoCode/rollback',
method: 'post',
data
})
}
export const getMeta = (data) => {
return service({
url: '/autoCode/getMeta',
method: 'post',
data
})
}
export const delSysHistory = (data) => {
return service({
url: '/autoCode/delSysHistory',
method: 'post',
data
})
}
export const createPackageApi = (data) => {
return service({
url: '/autoCode/createPackage',
method: 'post',
data
})
}
export const getPackageApi = () => {
return service({
url: '/autoCode/getPackage',
method: 'post'
})
}
export const deletePackageApi = (data) => {
return service({
url: '/autoCode/delPackage',
method: 'post',
data
})
}
export const createPlugApi = (data) => {
return service({
url: '/autoCode/createPlug',
method: 'post',
data
})
}
export const installPlug = (data) => {
return service({
url: '/autoCode/installPlug',
method: 'post',
data
})
}
export const pubPlug = (params) => {
return service({
url: '/autoCode/pubPlug',
method: 'post',
params
})
}

43
src/api/breakpoint.js Normal file
View File

@ -0,0 +1,43 @@
import service from '@/utils/request'
// @Summary 设置角色资源权限
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body sysModel.SysAuthority true "设置角色资源权限"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"设置成功"}"
// @Router /authority/setDataAuthority [post]
export const findFile = (params) => {
return service({
url: '/fileUploadAndDownload/findFile',
method: 'get',
params
})
}
export const breakpointContinue = (data) => {
return service({
url: '/fileUploadAndDownload/breakpointContinue',
method: 'post',
donNotShowLoading: true,
headers: { 'Content-Type': 'multipart/form-data' },
data
})
}
export const breakpointContinueFinish = (params) => {
return service({
url: '/fileUploadAndDownload/breakpointContinueFinish',
method: 'post',
params
})
}
export const removeChunk = (data, params) => {
return service({
url: '/fileUploadAndDownload/removeChunk',
method: 'post',
data,
params
})
}

32
src/api/casbin.js Normal file
View File

@ -0,0 +1,32 @@
import service from '@/utils/request'
// @Tags authority
// @Summary 更改角色api权限
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body api.CreateAuthorityPatams true "更改角色api权限"
// @Success 200 {string} json "{"success":true,"data":{},"msg":"获取成功"}"
// @Router /casbin/UpdateCasbin [post]
export const UpdateCasbin = (data) => {
return service({
url: '/casbin/updateCasbin',
method: 'post',
data
})
}
// @Tags casbin
// @Summary 获取权限列表
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body api.CreateAuthorityPatams true "获取权限列表"
// @Success 200 {string} json "{"success":true,"data":{},"msg":"获取成功"}"
// @Router /casbin/getPolicyPathByAuthorityId [post]
export const getPolicyPathByAuthorityId = (data) => {
return service({
url: '/casbin/getPolicyPathByAuthorityId',
method: 'post',
data
})
}

31
src/api/chatgpt.js Normal file
View File

@ -0,0 +1,31 @@
import service from '@/utils/request'
export const getTableApi = (data) => {
return service({
url: '/chatGpt/getTable',
method: 'post',
data
})
}
export const createSKApi = (data) => {
return service({
url: '/chatGpt/createSK',
method: 'post',
data
})
}
export const getSKApi = () => {
return service({
url: '/chatGpt/getSK',
method: 'get',
})
}
export const deleteSKApi = () => {
return service({
url: '/chatGpt/deleteSK',
method: 'delete'
})
}

80
src/api/customer.js Normal file
View File

@ -0,0 +1,80 @@
import service from '@/utils/request'
// @Tags SysApi
// @Summary 删除客户
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body dbModel.ExaCustomer true "删除客户"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
// @Router /customer/customer [post]
export const createExaCustomer = (data) => {
return service({
url: '/customer/customer',
method: 'post',
data
})
}
// @Tags SysApi
// @Summary 更新客户信息
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body dbModel.ExaCustomer true "更新客户信息"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
// @Router /customer/customer [put]
export const updateExaCustomer = (data) => {
return service({
url: '/customer/customer',
method: 'put',
data
})
}
// @Tags SysApi
// @Summary 创建客户
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body dbModel.ExaCustomer true "创建客户"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
// @Router /customer/customer [delete]
export const deleteExaCustomer = (data) => {
return service({
url: '/customer/customer',
method: 'delete',
data
})
}
// @Tags SysApi
// @Summary 获取单一客户信息
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body dbModel.ExaCustomer true "获取单一客户信息"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
// @Router /customer/customer [get]
export const getExaCustomer = (params) => {
return service({
url: '/customer/customer',
method: 'get',
params
})
}
// @Tags SysApi
// @Summary 获取权限客户列表
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body modelInterface.PageInfo true "获取权限客户列表"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
// @Router /customer/customerList [get]
export const getExaCustomerList = (params) => {
return service({
url: '/customer/customerList',
method: 'get',
params
})
}

14
src/api/email.js Normal file
View File

@ -0,0 +1,14 @@
import service from '@/utils/request'
// @Tags email
// @Summary 发送测试邮件
// @Security ApiKeyAuth
// @Produce application/json
// @Success 200 {string} string "{"success":true,"data":{},"msg":"返回成功"}"
// @Router /email/emailTest [post]
export const emailTest = (data) => {
return service({
url: '/email/emailTest',
method: 'post',
data
})
}

View File

@ -0,0 +1,44 @@
import service from '@/utils/request'
// @Tags FileUploadAndDownload
// @Summary 分页文件列表
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body modelInterface.PageInfo true "分页获取文件户列表"
// @Success 200 {string} json "{"success":true,"data":{},"msg":"获取成功"}"
// @Router /fileUploadAndDownload/getFileList [post]
export const getFileList = (data) => {
return service({
url: '/fileUploadAndDownload/getFileList',
method: 'post',
data
})
}
// @Tags FileUploadAndDownload
// @Summary 删除文件
// @Security ApiKeyAuth
// @Produce application/json
// @Param data body dbModel.FileUploadAndDownload true "传入文件里面id即可"
// @Success 200 {string} json "{"success":true,"data":{},"msg":"返回成功"}"
// @Router /fileUploadAndDownload/deleteFile [post]
export const deleteFile = (data) => {
return service({
url: '/fileUploadAndDownload/deleteFile',
method: 'post',
data
})
}
/**
* 编辑文件名或者备注
* @param data
* @returns {*}
*/
export const editFileName = (data) => {
return service({
url: '/fileUploadAndDownload/editFileName',
method: 'post',
data
})
}

17
src/api/github.js Normal file
View File

@ -0,0 +1,17 @@
import axios from 'axios'
const service = axios.create()
export function Commits(page) {
return service({
url: 'https://api.github.com/repos/flipped-aurora/gin-vue-admin/commits?page=' + page,
method: 'get'
})
}
export function Members() {
return service({
url: 'https://api.github.com/orgs/FLIPPED-AURORA/members',
method: 'get'
})
}

26
src/api/initdb.js Normal file
View File

@ -0,0 +1,26 @@
import service from '@/utils/request'
// @Tags InitDB
// @Summary 初始化用户数据库
// @Produce application/json
// @Param data body request.InitDB true "初始化数据库参数"
// @Success 200 {string} string "{"code":0,"data":{},"msg":"自动创建数据库成功"}"
// @Router /init/initdb [post]
export const initDB = (data) => {
return service({
url: '/init/initdb',
method: 'post',
data
})
}
// @Tags CheckDB
// @Summary 初始化用户数据库
// @Produce application/json
// @Success 200 {string} string "{"code":0,"data":{},"msg":"探测完成"}"
// @Router /init/checkdb [post]
export const checkDB = () => {
return service({
url: '/init/checkdb',
method: 'post'
})
}

14
src/api/jwt.js Normal file
View File

@ -0,0 +1,14 @@
import service from '@/utils/request'
// @Tags jwt
// @Summary jwt加入黑名单
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Success 200 {string} string "{"success":true,"data":{},"msg":"拉黑成功"}"
// @Router /jwt/jsonInBlacklist [post]
export const jsonInBlacklist = () => {
return service({
url: '/jwt/jsonInBlacklist',
method: 'post'
})
}

113
src/api/menu.js Normal file
View File

@ -0,0 +1,113 @@
import service from '@/utils/request'
// @Summary 用户登录 获取动态路由
// @Produce application/json
// @Param 可以什么都不填 调一下即可
// @Router /menu/getMenu [post]
export const asyncMenu = () => {
return service({
url: '/menu/getMenu',
method: 'post'
})
}
// @Summary 获取menu列表
// @Produce application/json
// @Param {
// page int
// pageSize int
// }
// @Router /menu/getMenuList [post]
export const getMenuList = (data) => {
return service({
url: '/menu/getMenuList',
method: 'post',
data
})
}
// @Summary 新增基础menu
// @Produce application/json
// @Param menu Object
// @Router /menu/getMenuList [post]
export const addBaseMenu = (data) => {
return service({
url: '/menu/addBaseMenu',
method: 'post',
data
})
}
// @Summary 获取基础路由列表
// @Produce application/json
// @Param 可以什么都不填 调一下即可
// @Router /menu/getBaseMenuTree [post]
export const getBaseMenuTree = () => {
return service({
url: '/menu/getBaseMenuTree',
method: 'post'
})
}
// @Summary 添加用户menu关联关系
// @Produce application/json
// @Param menus Object authorityId string
// @Router /menu/getMenuList [post]
export const addMenuAuthority = (data) => {
return service({
url: '/menu/addMenuAuthority',
method: 'post',
data
})
}
// @Summary 获取用户menu关联关系
// @Produce application/json
// @Param authorityId string
// @Router /menu/getMenuAuthority [post]
export const getMenuAuthority = (data) => {
return service({
url: '/menu/getMenuAuthority',
method: 'post',
data
})
}
// @Summary 删除menu
// @Produce application/json
// @Param ID float64
// @Router /menu/deleteBaseMenu [post]
export const deleteBaseMenu = (data) => {
return service({
url: '/menu/deleteBaseMenu',
method: 'post',
data
})
}
// @Summary 修改menu列表
// @Produce application/json
// @Param menu Object
// @Router /menu/updateBaseMenu [post]
export const updateBaseMenu = (data) => {
return service({
url: '/menu/updateBaseMenu',
method: 'post',
data
})
}
// @Tags menu
// @Summary 根据id获取菜单
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body api.GetById true "根据id获取菜单"
// @Success 200 {string} json "{"success":true,"data":{},"msg":"获取成功"}"
// @Router /menu/getBaseMenuById [post]
export const getBaseMenuById = (data) => {
return service({
url: '/menu/getBaseMenuById',
method: 'post',
data
})
}

80
src/api/sysDictionary.js Normal file
View File

@ -0,0 +1,80 @@
import service from '@/utils/request'
// @Tags SysDictionary
// @Summary 创建SysDictionary
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body model.SysDictionary true "创建SysDictionary"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
// @Router /sysDictionary/createSysDictionary [post]
export const createSysDictionary = (data) => {
return service({
url: '/sysDictionary/createSysDictionary',
method: 'post',
data
})
}
// @Tags SysDictionary
// @Summary 删除SysDictionary
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body model.SysDictionary true "删除SysDictionary"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"删除成功"}"
// @Router /sysDictionary/deleteSysDictionary [delete]
export const deleteSysDictionary = (data) => {
return service({
url: '/sysDictionary/deleteSysDictionary',
method: 'delete',
data
})
}
// @Tags SysDictionary
// @Summary 更新SysDictionary
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body model.SysDictionary true "更新SysDictionary"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"更新成功"}"
// @Router /sysDictionary/updateSysDictionary [put]
export const updateSysDictionary = (data) => {
return service({
url: '/sysDictionary/updateSysDictionary',
method: 'put',
data
})
}
// @Tags SysDictionary
// @Summary 用id查询SysDictionary
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body model.SysDictionary true "用id查询SysDictionary"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"查询成功"}"
// @Router /sysDictionary/findSysDictionary [get]
export const findSysDictionary = (params) => {
return service({
url: '/sysDictionary/findSysDictionary',
method: 'get',
params
})
}
// @Tags SysDictionary
// @Summary 分页获取SysDictionary列表
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body request.PageInfo true "分页获取SysDictionary列表"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
// @Router /sysDictionary/getSysDictionaryList [get]
export const getSysDictionaryList = (params) => {
return service({
url: '/sysDictionary/getSysDictionaryList',
method: 'get',
params
})
}

View File

@ -0,0 +1,80 @@
import service from '@/utils/request'
// @Tags SysDictionaryDetail
// @Summary 创建SysDictionaryDetail
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body model.SysDictionaryDetail true "创建SysDictionaryDetail"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
// @Router /sysDictionaryDetail/createSysDictionaryDetail [post]
export const createSysDictionaryDetail = (data) => {
return service({
url: '/sysDictionaryDetail/createSysDictionaryDetail',
method: 'post',
data
})
}
// @Tags SysDictionaryDetail
// @Summary 删除SysDictionaryDetail
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body model.SysDictionaryDetail true "删除SysDictionaryDetail"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"删除成功"}"
// @Router /sysDictionaryDetail/deleteSysDictionaryDetail [delete]
export const deleteSysDictionaryDetail = (data) => {
return service({
url: '/sysDictionaryDetail/deleteSysDictionaryDetail',
method: 'delete',
data
})
}
// @Tags SysDictionaryDetail
// @Summary 更新SysDictionaryDetail
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body model.SysDictionaryDetail true "更新SysDictionaryDetail"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"更新成功"}"
// @Router /sysDictionaryDetail/updateSysDictionaryDetail [put]
export const updateSysDictionaryDetail = (data) => {
return service({
url: '/sysDictionaryDetail/updateSysDictionaryDetail',
method: 'put',
data
})
}
// @Tags SysDictionaryDetail
// @Summary 用id查询SysDictionaryDetail
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body model.SysDictionaryDetail true "用id查询SysDictionaryDetail"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"查询成功"}"
// @Router /sysDictionaryDetail/findSysDictionaryDetail [get]
export const findSysDictionaryDetail = (params) => {
return service({
url: '/sysDictionaryDetail/findSysDictionaryDetail',
method: 'get',
params
})
}
// @Tags SysDictionaryDetail
// @Summary 分页获取SysDictionaryDetail列表
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body request.PageInfo true "分页获取SysDictionaryDetail列表"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
// @Router /sysDictionaryDetail/getSysDictionaryDetailList [get]
export const getSysDictionaryDetailList = (params) => {
return service({
url: '/sysDictionaryDetail/getSysDictionaryDetailList',
method: 'get',
params
})
}

View File

@ -0,0 +1,48 @@
import service from '@/utils/request'
// @Tags SysOperationRecord
// @Summary 删除SysOperationRecord
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body model.SysOperationRecord true "删除SysOperationRecord"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"删除成功"}"
// @Router /sysOperationRecord/deleteSysOperationRecord [delete]
export const deleteSysOperationRecord = (data) => {
return service({
url: '/sysOperationRecord/deleteSysOperationRecord',
method: 'delete',
data
})
}
// @Tags SysOperationRecord
// @Summary 删除SysOperationRecord
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body request.IdsReq true "删除SysOperationRecord"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"删除成功"}"
// @Router /sysOperationRecord/deleteSysOperationRecord [delete]
export const deleteSysOperationRecordByIds = (data) => {
return service({
url: '/sysOperationRecord/deleteSysOperationRecordByIds',
method: 'delete',
data
})
}
// @Tags SysOperationRecord
// @Summary 分页获取SysOperationRecord列表
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body request.PageInfo true "分页获取SysOperationRecord列表"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
// @Router /sysOperationRecord/getSysOperationRecordList [get]
export const getSysOperationRecordList = (params) => {
return service({
url: '/sysOperationRecord/getSysOperationRecordList',
method: 'get',
params
})
}

42
src/api/system.js Normal file
View File

@ -0,0 +1,42 @@
import service from '@/utils/request'
// @Tags systrm
// @Summary 获取配置文件内容
// @Security ApiKeyAuth
// @Produce application/json
// @Success 200 {string} string "{"success":true,"data":{},"msg":"返回成功"}"
// @Router /system/getSystemConfig [post]
export const getSystemConfig = () => {
return service({
url: '/system/getSystemConfig',
method: 'post'
})
}
// @Tags system
// @Summary 设置配置文件内容
// @Security ApiKeyAuth
// @Produce application/json
// @Param data body sysModel.System true
// @Success 200 {string} string "{"success":true,"data":{},"msg":"返回成功"}"
// @Router /system/setSystemConfig [post]
export const setSystemConfig = (data) => {
return service({
url: '/system/setSystemConfig',
method: 'post',
data
})
}
// @Tags system
// @Summary 获取服务器运行状态
// @Security ApiKeyAuth
// @Produce application/json
// @Success 200 {string} string "{"success":true,"data":{},"msg":"返回成功"}"
// @Router /system/getServerInfo [post]
export const getSystemState = () => {
return service({
url: '/system/getServerInfo',
method: 'post',
donNotShowLoading: true
})
}

166
src/api/user.js Normal file
View File

@ -0,0 +1,166 @@
import service from '@/utils/request'
// @Summary 用户登录
// @Produce application/json
// @Param data body {username:"string",password:"string"}
// @Router /base/login [post]
export const login = (data) => {
return service({
url: '/base/login',
method: 'post',
data: data
})
}
// @Summary 获取验证码
// @Produce application/json
// @Param data body {username:"string",password:"string"}
// @Router /base/captcha [post]
export const captcha = (data) => {
return service({
url: '/base/captcha',
method: 'post',
data: data
})
}
// @Summary 用户注册
// @Produce application/json
// @Param data body {username:"string",password:"string"}
// @Router /base/resige [post]
export const register = (data) => {
return service({
url: '/user/admin_register',
method: 'post',
data: data
})
}
// @Summary 修改密码
// @Produce application/json
// @Param data body {username:"string",password:"string",newPassword:"string"}
// @Router /user/changePassword [post]
export const changePassword = (data) => {
return service({
url: '/user/changePassword',
method: 'post',
data: data
})
}
// @Tags User
// @Summary 分页获取用户列表
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body modelInterface.PageInfo true "分页获取用户列表"
// @Success 200 {string} json "{"success":true,"data":{},"msg":"获取成功"}"
// @Router /user/getUserList [post]
export const getUserList = (data) => {
return service({
url: '/user/getUserList',
method: 'post',
data: data
})
}
// @Tags User
// @Summary 设置用户权限
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body api.SetUserAuth true "设置用户权限"
// @Success 200 {string} json "{"success":true,"data":{},"msg":"修改成功"}"
// @Router /user/setUserAuthority [post]
export const setUserAuthority = (data) => {
return service({
url: '/user/setUserAuthority',
method: 'post',
data: data
})
}
// @Tags SysUser
// @Summary 删除用户
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body request.SetUserAuth true "删除用户"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"修改成功"}"
// @Router /user/deleteUser [delete]
export const deleteUser = (data) => {
return service({
url: '/user/deleteUser',
method: 'delete',
data: data
})
}
// @Tags SysUser
// @Summary 设置用户信息
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body model.SysUser true "设置用户信息"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"修改成功"}"
// @Router /user/setUserInfo [put]
export const setUserInfo = (data) => {
return service({
url: '/user/setUserInfo',
method: 'put',
data: data
})
}
// @Tags SysUser
// @Summary 设置用户信息
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body model.SysUser true "设置用户信息"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"修改成功"}"
// @Router /user/setSelfInfo [put]
export const setSelfInfo = (data) => {
return service({
url: '/user/setSelfInfo',
method: 'put',
data: data
})
}
// @Tags User
// @Summary 设置用户权限
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body api.setUserAuthorities true "设置用户权限"
// @Success 200 {string} json "{"success":true,"data":{},"msg":"修改成功"}"
// @Router /user/setUserAuthorities [post]
export const setUserAuthorities = (data) => {
return service({
url: '/user/setUserAuthorities',
method: 'post',
data: data
})
}
// @Tags User
// @Summary 获取用户信息
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Success 200 {string} json "{"success":true,"data":{},"msg":"获取成功"}"
// @Router /user/getUserInfo [get]
export const getUserInfo = () => {
return service({
url: '/user/getUserInfo',
method: 'get'
})
}
export const resetPassword = (data) => {
return service({
url: '/user/resetPassword',
method: 'post',
data: data
})
}

View File

@ -0,0 +1 @@
<svg xmlns='http://www.w3.org/2000/svg' width='100%'><rect fill='#29adff' width='540' height='450'/><defs><linearGradient id='a' gradientUnits='userSpaceOnUse' x1='0' x2='0' y1='0' y2='100%' gradientTransform='rotate(279,720,373)'><stop offset='0' stop-color='#29adff'/><stop offset='1' stop-color='#42b983'/></linearGradient><pattern patternUnits='userSpaceOnUse' id='b' width='300' height='250' x='0' y='0' viewBox='0 0 1080 900'><g fill-opacity='0'><polygon fill='#444' points='90 150 0 300 180 300'/><polygon points='90 150 180 0 0 0'/><polygon fill='#AAA' points='270 150 360 0 180 0'/><polygon fill='#DDD' points='450 150 360 300 540 300'/><polygon fill='#999' points='450 150 540 0 360 0'/><polygon points='630 150 540 300 720 300'/><polygon fill='#DDD' points='630 150 720 0 540 0'/><polygon fill='#444' points='810 150 720 300 900 300'/><polygon fill='#FFF' points='810 150 900 0 720 0'/><polygon fill='#DDD' points='990 150 900 300 1080 300'/><polygon fill='#444' points='990 150 1080 0 900 0'/><polygon fill='#DDD' points='90 450 0 600 180 600'/><polygon points='90 450 180 300 0 300'/><polygon fill='#666' points='270 450 180 600 360 600'/><polygon fill='#AAA' points='270 450 360 300 180 300'/><polygon fill='#DDD' points='450 450 360 600 540 600'/><polygon fill='#999' points='450 450 540 300 360 300'/><polygon fill='#999' points='630 450 540 600 720 600'/><polygon fill='#FFF' points='630 450 720 300 540 300'/><polygon points='810 450 720 600 900 600'/><polygon fill='#DDD' points='810 450 900 300 720 300'/><polygon fill='#AAA' points='990 450 900 600 1080 600'/><polygon fill='#444' points='990 450 1080 300 900 300'/><polygon fill='#222' points='90 750 0 900 180 900'/><polygon points='270 750 180 900 360 900'/><polygon fill='#DDD' points='270 750 360 600 180 600'/><polygon points='450 750 540 600 360 600'/><polygon points='630 750 540 900 720 900'/><polygon fill='#444' points='630 750 720 600 540 600'/><polygon fill='#AAA' points='810 750 720 900 900 900'/><polygon fill='#666' points='810 750 900 600 720 600'/><polygon fill='#999' points='990 750 900 900 1080 900'/><polygon fill='#999' points='180 0 90 150 270 150'/><polygon fill='#444' points='360 0 270 150 450 150'/><polygon fill='#FFF' points='540 0 450 150 630 150'/><polygon points='900 0 810 150 990 150'/><polygon fill='#222' points='0 300 -90 450 90 450'/><polygon fill='#FFF' points='0 300 90 150 -90 150'/><polygon fill='#FFF' points='180 300 90 450 270 450'/><polygon fill='#666' points='180 300 270 150 90 150'/><polygon fill='#222' points='360 300 270 450 450 450'/><polygon fill='#FFF' points='360 300 450 150 270 150'/><polygon fill='#444' points='540 300 450 450 630 450'/><polygon fill='#222' points='540 300 630 150 450 150'/><polygon fill='#AAA' points='720 300 630 450 810 450'/><polygon fill='#666' points='720 300 810 150 630 150'/><polygon fill='#FFF' points='900 300 810 450 990 450'/><polygon fill='#999' points='900 300 990 150 810 150'/><polygon points='0 600 -90 750 90 750'/><polygon fill='#666' points='0 600 90 450 -90 450'/><polygon fill='#AAA' points='180 600 90 750 270 750'/><polygon fill='#444' points='180 600 270 450 90 450'/><polygon fill='#444' points='360 600 270 750 450 750'/><polygon fill='#999' points='360 600 450 450 270 450'/><polygon fill='#666' points='540 600 630 450 450 450'/><polygon fill='#222' points='720 600 630 750 810 750'/><polygon fill='#FFF' points='900 600 810 750 990 750'/><polygon fill='#222' points='900 600 990 450 810 450'/><polygon fill='#DDD' points='0 900 90 750 -90 750'/><polygon fill='#444' points='180 900 270 750 90 750'/><polygon fill='#FFF' points='360 900 450 750 270 750'/><polygon fill='#AAA' points='540 900 630 750 450 750'/><polygon fill='#FFF' points='720 900 810 750 630 750'/><polygon fill='#222' points='900 900 990 750 810 750'/><polygon fill='#222' points='1080 300 990 450 1170 450'/><polygon fill='#FFF' points='1080 300 1170 150 990 150'/><polygon points='1080 600 990 750 1170 750'/><polygon fill='#666' points='1080 600 1170 450 990 450'/><polygon fill='#DDD' points='1080 900 1170 750 990 750'/></g></pattern></defs><rect x='0' y='0' fill='url(#a)' width='100%' height='100%'/><rect x='0' y='0' fill='url(#b)' width='100%' height='100%'/></svg>

BIN
src/assets/dashboard.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

BIN
src/assets/docs.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

BIN
src/assets/github.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

BIN
src/assets/kefu.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

View File

@ -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

123
src/assets/login_left.svg Normal file
View File

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 702 KiB

BIN
src/assets/logo.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

BIN
src/assets/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

BIN
src/assets/logo_login.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

BIN
src/assets/nav_logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

BIN
src/assets/noBody.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

BIN
src/assets/notFound.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

BIN
src/assets/qm.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

BIN
src/assets/video.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

View File

@ -0,0 +1,222 @@
<template>
<el-drawer
v-model="drawer"
title="媒体库"
size="650px"
>
<warning-bar
title="点击“文件名/备注”可以编辑文件名或者备注内容。"
/>
<div class="gva-btn-list">
<upload-common
v-model:imageCommon="imageCommon"
class="upload-btn-media-library"
@on-success="open"
/>
<upload-image
v-model:imageUrl="imageUrl"
:file-size="512"
:max-w-h="1080"
class="upload-btn-media-library"
@on-success="open"
/>
<el-form
ref="searchForm"
:inline="true"
:model="search"
>
<el-form-item label="">
<el-input
v-model="search.keyword"
class="keyword"
placeholder="请输入文件名或备注"
/>
</el-form-item>
<el-form-item>
<el-button
type="primary"
icon="search"
@click="open"
>查询</el-button>
</el-form-item>
</el-form>
</div>
<div class="media">
<div
v-for="(item,key) in picList"
:key="key"
class="media-box"
>
<div class="header-img-box-list">
<el-image
:key="key"
:src="getUrl(item.url)"
@click="chooseImg(item.url,target,targetKey)"
>
<template #error>
<div class="header-img-box-list">
<el-icon>
<picture />
</el-icon>
</div>
</template>
</el-image>
</div>
<div
class="img-title"
@click="editFileNameFunc(item)"
>{{ item.name }}</div>
</div>
</div>
<el-pagination
:current-page="page"
:page-size="pageSize"
:total="total"
:style="{'justify-content':'center'}"
layout="total, prev, pager, next, jumper"
@current-change="handleCurrentChange"
@size-change="handleSizeChange"
/>
</el-drawer>
</template>
<script setup>
import { getUrl } from '@/utils/image'
import { ref } from 'vue'
import { getFileList, editFileName } from '@/api/fileUploadAndDownload'
import UploadImage from '@/components/upload/image.vue'
import UploadCommon from '@/components/upload/common.vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import WarningBar from '@/components/warningBar/warningBar.vue'
const imageUrl = ref('')
const imageCommon = ref('')
const search = ref({})
const page = ref(1)
const total = ref(0)
const pageSize = ref(20)
//
const handleSizeChange = (val) => {
pageSize.value = val
open()
}
const handleCurrentChange = (val) => {
page.value = val
open()
}
const emit = defineEmits(['enterImg'])
defineProps({
target: {
type: Object,
default: null,
},
targetKey: {
type: String,
default: '',
},
})
const drawer = ref(false)
const picList = ref([])
const chooseImg = (url, target, targetKey) => {
if (target && targetKey) {
target[targetKey] = url
}
emit('enterImg', url)
drawer.value = false
}
const open = async() => {
const res = await getFileList({ page: page.value, pageSize: pageSize.value, ...search.value })
if (res.code === 0) {
picList.value = res.data.list
total.value = res.data.total
page.value = res.data.page
pageSize.value = res.data.pageSize
drawer.value = true
}
}
/**
* 编辑文件名或者备注
* @param row
* @returns {Promise<void>}
*/
const editFileNameFunc = async(row) => {
ElMessageBox.prompt('请输入文件名或者备注', '编辑', {
confirmButtonText: '确定',
cancelButtonText: '取消',
inputPattern: /\S/,
inputErrorMessage: '不能为空',
inputValue: row.name
}).then(async({ value }) => {
row.name = value
// console.log(row)
const res = await editFileName(row)
if (res.code === 0) {
ElMessage({
type: 'success',
message: '编辑成功!',
})
open()
}
}).catch(() => {
ElMessage({
type: 'info',
message: '取消修改'
})
})
}
defineExpose({ open })
</script>
<style lang="scss">
.upload-btn-media-library {
margin-left: 20px;
}
.media {
display: flex;
flex-wrap: wrap;
.media-box {
width: 120px;
margin-left: 20px;
.img-title {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
line-height: 36px;
text-align: center;
cursor: pointer;
}
.header-img-box-list {
width: 120px;
height: 120px;
border: 1px dashed #ccc;
border-radius: 8px;
text-align: center;
line-height: 120px;
cursor: pointer;
overflow: hidden;
.el-image__inner {
max-width: 120px;
max-height: 120px;
vertical-align: middle;
width: unset;
height: unset;
}
}
}
}
</style>

View File

@ -0,0 +1,196 @@
<template>
<el-dialog
v-model="dialogVisible"
width="30%"
class="overlay"
:show-close="false"
>
<template #header>
<input
v-model="searchInput"
class="quick-input"
placeholder="请输入你需要快捷到达的功能"
>
</template>
<div
v-for="(option,index) in options"
:key="index"
>
<div
v-if="option.children.length"
class="quick-title"
>{{ option.label }}</div>
<div
v-for="(item,key) in option.children"
:key="index+'-'+key"
class="quick-item"
@click="item.func"
>
{{ item.label }}
</div>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="close">关闭</el-button>
</span>
</template>
</el-dialog>
</template>
<script setup>
import { reactive, ref, watch } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { useRouterStore } from '@/pinia/modules/router'
import { useUserStore } from '@/pinia/modules/user'
defineOptions({
name: 'CommandMenu',
})
const router = useRouter()
const route = useRouter()
const userStore = useUserStore()
const routerStore = useRouterStore()
const dialogVisible = ref(false)
const searchInput = ref('')
const options = reactive([])
const deepMenus = (menus) => {
const arr = []
menus.forEach(menu => {
if (menu.children && menu.children.length > 0) {
arr.push(...deepMenus(menu.children))
} else {
if (menu.meta.title && menu.meta.title.indexOf(searchInput.value) > -1) {
arr.push({
label: menu.meta.title,
func: () => changeRouter(menu)
})
}
}
})
return arr
}
const addQuickMenu = () => {
const option = {
label: '跳转',
children: []
}
const menus = deepMenus(routerStore.asyncRouters[0].children)
option.children.push(...menus)
options.push(option)
}
const addQuickOption = () => {
const option = {
label: '操作',
children: []
}
const quickArr = [
{
label: '亮色主题',
func: () => changeMode('light')
}, {
label: '暗色主题',
func: () => changeMode('dark')
}, {
label: '退出登录',
func: () => userStore.LoginOut()
}
]
option.children.push(...quickArr.filter(item => item.label.indexOf(searchInput.value) > -1))
options.push(option)
}
addQuickMenu()
addQuickOption()
const open = () => {
dialogVisible.value = true
}
const changeRouter = (e) => {
const index = e.name
const query = {}
const params = {}
routerStore.routeMap[index]?.parameters &&
routerStore.routeMap[index]?.parameters.forEach((item) => {
if (item.type === 'query') {
query[item.key] = item.value
} else {
params[item.key] = item.value
}
})
if (index === route.name) return
if (e.name.indexOf('http://') > -1 || e.name.indexOf('https://') > -1) {
window.open(e.name)
} else {
router.push({ name: index, query, params })
}
dialogVisible.value = false
}
const changeMode = (e) => {
if (e === null) {
userStore.changeSideMode('dark')
return
}
userStore.changeSideMode(e)
}
const close = () => {
dialogVisible.value = false
}
defineExpose({ open })
watch(searchInput, () => {
options.length = 0
addQuickMenu()
addQuickOption()
})
</script>
<style lang="scss">
.overlay {
border-radius: 4px;
.el-dialog__header{
padding:0 !important;
margin-right:0 !important;
}
.el-dialog__body{
padding: 12px !important;
height: 50vh;
overflow: auto !important;
}
.quick-title{
margin-top: 8px;
font-size: 12px;
font-weight: 600;
color: #666;
}
.quick-input{
color: #666;
border-radius: 4px 4px 0 0;
border:none;
padding: 12px 16px;
box-sizing: border-box;
width: 100%;
font-size: 16px;
border-bottom: 1px solid #ddd;
}
.quick-item{
font-size: 14px;
padding: 8px;
margin: 4px 0;
&:hover{
cursor: pointer;
background: #eee;
border-radius: 4px;
}
}
}
</style>

View File

@ -0,0 +1,104 @@
<template>
<span class="headerAvatar">
<template v-if="picType === 'avatar'">
<el-avatar
v-if="userStore.userInfo.headerImg"
:size="30"
:src="avatar"
/>
<el-avatar
v-else
:size="30"
:src="noAvatar"
/>
</template>
<template v-if="picType === 'img'">
<img
v-if="userStore.userInfo.headerImg"
:src="avatar"
class="avatar"
>
<img
v-else
:src="noAvatar"
class="avatar"
>
</template>
<template v-if="picType === 'file'">
<el-image
:src="file"
class="file"
:preview-src-list="previewSrcList"
:preview-teleported="true"
/>
</template>
</span>
</template>
<script setup>
import noAvatarPng from '@/assets/noBody.png'
import { useUserStore } from '@/pinia/modules/user'
import { computed, ref } from 'vue'
defineOptions({
name: 'CustomPic'
})
const props = defineProps({
picType: {
type: String,
required: false,
default: 'avatar'
},
picSrc: {
type: String,
required: false,
default: ''
},
preview: {
type: Boolean,
default: false
}
})
const path = ref(import.meta.env.VITE_BASE_API + '/')
const noAvatar = ref(noAvatarPng)
const userStore = useUserStore()
const avatar = computed(() => {
if (props.picSrc === '') {
if (userStore.userInfo.headerImg !== '' && userStore.userInfo.headerImg.slice(0, 4) === 'http') {
return userStore.userInfo.headerImg
}
return path.value + userStore.userInfo.headerImg
} else {
if (props.picSrc !== '' && props.picSrc.slice(0, 4) === 'http') {
return props.picSrc
}
return path.value + props.picSrc
}
})
const file = computed(() => {
if (props.picSrc && props.picSrc.slice(0, 4) !== 'http') {
return path.value + props.picSrc
}
return props.picSrc
})
const previewSrcList = computed(() => props.preview ? [file.value] : [])
</script>
<style scoped>
.headerAvatar{
display: flex;
justify-content: center;
align-items: center;
margin-right: 8px;
}
.file{
width: 80px;
height: 80px;
position: relative;
}
</style>

View File

@ -0,0 +1,36 @@
<template>
<vue-office-docx :src="docx" @rendered="rendered">
</vue-office-docx>
</template>
<script>
export default {
name: "Docx"
}
</script>
<script setup>
import {ref, watch} from 'vue'
// VueOfficeDocx
import VueOfficeDocx from '@vue-office/docx'
//
import '@vue-office/docx/lib/index.css'
const props = defineProps({
modelValue: {
type: String,
default: () => ""
}
})
const docx = ref(null)
watch(
() => props.modelValue,
value => docx.value = value,
{immediate: true}
)
const rendered = () => {
}
</script>
<style lang="scss" scoped>
</style>

View File

@ -0,0 +1,33 @@
<template>
<VueOfficeExcel :src="excel" @rendered="renderedHandler" @error="errorHandler" style="height: 100vh;width: 100vh"/>
</template>
<script>
export default {
name: 'Excel'
}
</script>
<script setup>
//VueOfficeExcel
import VueOfficeExcel from '@vue-office/excel'
//
import '@vue-office/excel/lib/index.css'
import {ref, watch} from 'vue'
const props = defineProps({
modelValue: {
type: String,
default: () => ""
}
})
const excel = ref('')
watch(() => props.modelValue, val => excel.value = val, {immediate: true})
const renderedHandler = () => {
}
const errorHandler = () => {
}
</script>
<style>
</style>

View File

@ -0,0 +1,57 @@
<template>
<div class="border border-solid border-gray-100 h-full w-full">
<el-row>
<div v-if="ext==='docx'">
<Docx v-model="fullFileURL" />
</div>
<div v-else-if="ext==='pdf'">
<Pdf v-model="fullFileURL" />
</div>
<div v-else-if="ext==='xlsx'">
<Excel v-model="fullFileURL" />
</div>
<div v-else-if="ext==='image'">
<el-image
:src="fullFileURL"
lazy
/>
</div>
</el-row>
</div>
</template>
<script>
export default {
name: 'Office'
}
</script>
<script setup>
import { ref, watch, computed } from 'vue'
import Docx from '@/components/office/docx.vue'
import Pdf from '@/components/office/pdf.vue'
import Excel from '@/components/office/excel.vue'
const path = ref(import.meta.env.VITE_BASE_API)
const props = defineProps({
modelValue: {
type: String,
default: () => ''
}
})
const fileUrl = ref('')
const ext = ref('')
watch(
() => props.modelValue,
val => {
fileUrl.value = val
const fileExt = val.split('.')[1] || ''
const image = ['png', 'jpg', 'jpge', 'gif']
ext.value = image.includes(fileExt) ? 'image' : fileExt
},
{ immediate: true }
)
const fullFileURL = computed(() => {
return path.value + '/' + fileUrl.value
})
</script>

View File

@ -0,0 +1,36 @@
<template>
<vue-office-pdf
:src="pdf"
@rendered="renderedHandler"
@error="errorHandler"
/>
</template>
<script>
export default {
name: "Pdf"
}
</script>
<script setup>
import {ref, watch} from "vue"
//VueOfficeDocx
import VueOfficePdf from "@vue-office/pdf";
//
import '@vue-office/docx/lib/index.css'
console.log("pdf===>")
const props = defineProps({
modelValue: {
type: String,
default: () => ""
}
})
const pdf = ref(null)
watch(() => props.modelValue, val => pdf.value = val, {immediate: true})
const renderedHandler = () => {
console.log("pdf 加载成功")
}
const errorHandler = () => {
console.log("pdf 错误")
}
</script>

View File

@ -0,0 +1,92 @@
<template>
<div class="border border-solid border-gray-100 h-full">
<Toolbar
:editor="editorRef"
:default-config="toolbarConfig"
mode="default"
/>
<Editor
v-model="valueHtml"
class="overflow-y-hidden mt-0.5"
style="height: 18rem;"
:default-config="editorConfig"
mode="default"
@onCreated="handleCreated"
@onChange="change"
/>
</div>
</template>
<script setup>
import '@wangeditor/editor/dist/css/style.css' // css
const basePath = import.meta.env.VITE_BASE_API
import { onBeforeUnmount, ref, shallowRef, watch } from 'vue'
import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
import { useUserStore } from '@/pinia/modules/user'
import { ElMessage } from 'element-plus'
import { getUrl } from '@/utils/image'
const userStore = useUserStore()
const emits = defineEmits(['change', 'update:modelValue'])
const change = (editor) => {
emits('change', editor)
emits('update:modelValue', valueHtml.value)
}
const props = defineProps({
modelValue: {
type: String,
default: ''
}
})
const editorRef = shallowRef()
const valueHtml = ref('')
const toolbarConfig = {}
const editorConfig = {
placeholder: '请输入内容...',
MENU_CONF: {}
}
editorConfig.MENU_CONF['uploadImage'] = {
fieldName: 'file',
headers: {
'x-token': userStore.token,
},
server: basePath + '/fileUploadAndDownload/upload?noSave=1',
customInsert(res, insertFn) {
if (res.code === 0) {
const urlPath = getUrl(res.data.file.url)
insertFn(urlPath, res.data.file.name)
return
}
ElMessage.error(res.msg)
}
}
//
onBeforeUnmount(() => {
const editor = editorRef.value
if (editor == null) return
editor.destroy()
})
const handleCreated = (editor) => {
editorRef.value = editor
}
watch(() => props.modelValue, () => {
valueHtml.value = props.modelValue
})
</script>
<style scoped lang="scss">
</style>

View File

@ -0,0 +1,61 @@
<template>
<div class="border border-solid border-gray-100 h-full">
<Editor
v-model="valueHtml"
class="overflow-y-hidden mt-0.5"
:default-config="editorConfig"
mode="default"
@onCreated="handleCreated"
@onChange="change"
/>
</div>
</template>
<script setup>
import '@wangeditor/editor/dist/css/style.css' // css
import { onBeforeUnmount, ref, shallowRef, watch } from 'vue'
import { Editor } from '@wangeditor/editor-for-vue'
import { useUserStore } from '@/pinia/modules/user'
const userStore = useUserStore()
const emits = defineEmits(['change', 'update:modelValue'])
const editorConfig = ref({
readOnly: true
})
const change = (editor) => {
emits('change', editor)
emits('update:modelValue', valueHtml.value)
}
const props = defineProps({
modelValue: {
type: String,
default: ''
}
})
const editorRef = shallowRef()
const valueHtml = ref('')
//
onBeforeUnmount(() => {
const editor = editorRef.value
if (editor == null) return
editor.destroy()
})
const handleCreated = (editor) => {
editorRef.value = editor
}
watch(() => props.modelValue, () => {
valueHtml.value = props.modelValue
})
</script>
<style scoped lang="scss">
</style>

View File

@ -0,0 +1,76 @@
<template>
<div>
<el-upload
multiple
:action="`${path}/fileUploadAndDownload/upload?noSave=1`"
:headers="{ 'x-token': userStore.token }"
:on-error="uploadError"
:on-success="uploadSuccess"
:show-file-list="true"
:file-list="fileList"
class="upload-btn"
>
<el-button type="primary">上传文件</el-button>
</el-upload>
</div>
</template>
<script setup>
import {ref, watch} from 'vue'
import {ElMessage} from 'element-plus'
import {useUserStore} from '@/pinia/modules/user'
defineOptions({
name: 'UploadCommon',
})
const props = defineProps({
modelValue: {
type: Array,
default: () => []
}
})
const path = ref(import.meta.env.VITE_BASE_API)
const userStore = useUserStore()
const fullscreenLoading = ref(false)
const fileList = ref(props.modelValue)
const emits = defineEmits(['update:modelValue'])
watch(fileList.value, (val) => {
console.log(val)
emits('update:modelValue', val)
})
watch(
() => props.modelValue,
value => {
fileList.value = value
},
{immediate: true}
)
const uploadSuccess = (res) => {
const {data} = res
if (data.file) {
fileList.value.push({
name: data.file.name,
url: data.file.url
})
fullscreenLoading.value = false
}
}
const uploadError = () => {
ElMessage({
type: 'error',
message: '上传失败'
})
fullscreenLoading.value = false
}
</script>

View File

@ -0,0 +1,459 @@
<template>
<div
v-if="!multiple"
class="update-image"
:style="{
'background-image': `url(${getUrl(modelValue)})`,
'position': 'relative',
}"
>
<el-icon
v-if="isVideoExt(modelValue || '')"
:size="32"
class="video video-icon"
style=""
><VideoPlay /></el-icon>
<video
v-if="isVideoExt(modelValue || '')"
class="avatar video-avatar video"
muted
preload="metadata"
style=""
@click="openChooseImg"
>
<source :src="getUrl(modelValue) + '#t=1'">
</video>
<span
v-if="modelValue"
class="update"
style="position: absolute;"
@click="openChooseImg"
>
<el-icon>
<delete />
</el-icon>
删除</span>
<span
v-else
class="update text-gray-600"
@click="openChooseImg"
>
<el-icon>
<plus />
</el-icon>
上传</span>
</div>
<div
v-else
class="multiple-img"
>
<div
v-for="(item, index) in multipleValue"
:key="index"
class="update-image"
:style="{
'background-image': `url(${getUrl(item)})`,
'position': 'relative',
}"
>
<el-icon
v-if="isVideoExt(item || '')"
:size="32"
class="video video-icon"
><VideoPlay /></el-icon>
<video
v-if="isVideoExt(item || '')"
class="avatar video-avatar video"
muted
preload="metadata"
@click="deleteImg(index)"
>
<source :src="getUrl(item) + '#t=1'">
</video>
<span
class="update"
style="position: absolute;"
@click="deleteImg(index)"
>
<el-icon>
<delete />
</el-icon>
删除</span>
</div>
<div class="add-image">
<span
class="update text-gray-600"
@click="openChooseImg"
>
<el-icon>
<Plus />
</el-icon>
上传</span>
</div>
</div>
<el-drawer
v-model="drawer"
title="媒体库"
size="650px"
>
<warning-bar
title="点击“文件名/备注”可以编辑文件名或者备注内容。"
/>
<div class="gva-btn-list">
<upload-common
v-model:imageCommon="imageCommon"
class="upload-btn-media-library"
@on-success="getImageList"
/>
<upload-image
v-model:imageUrl="imageUrl"
:file-size="512"
:max-w-h="1080"
class="upload-btn-media-library"
@on-success="getImageList"
/>
<el-form
ref="searchForm"
:inline="true"
:model="search"
>
<el-form-item label="">
<el-input
v-model="search.keyword"
class="keyword"
placeholder="请输入文件名或备注"
/>
</el-form-item>
<el-form-item>
<el-button
type="primary"
icon="search"
@click="getImageList"
>查询</el-button>
</el-form-item>
</el-form>
</div>
<div class="media">
<div
v-for="(item,key) in picList"
:key="key"
class="media-box"
>
<div class="header-img-box-list">
<el-image
:key="key"
:src="getUrl(item.url)"
fit="cover"
style="width: 100%;height: 100%;"
@click="chooseImg(item.url)"
>
<template #error>
<el-icon
v-if="isVideoExt(item.url || '')"
:size="32"
class="video video-icon"
><VideoPlay /></el-icon>
<video
v-if="isVideoExt(item.url || '')"
class="avatar video-avatar video"
muted
preload="metadata"
@click="chooseImg(item.url)"
>
<source :src="getUrl(item.url) + '#t=1'">
您的浏览器不支持视频播放
</video>
<div
v-else
class="header-img-box-list"
>
<el-icon class="lost-image">
<icon-picture />
</el-icon>
</div>
</template>
</el-image>
</div>
<div
class="img-title"
@click="editFileNameFunc(item)"
>{{ item.name }}</div>
</div>
</div>
<el-pagination
:current-page="page"
:page-size="pageSize"
:total="total"
:style="{'justify-content':'center'}"
layout="total, prev, pager, next, jumper"
@current-change="handleCurrentChange"
@size-change="handleSizeChange"
/>
</el-drawer>
</template>
<script setup>
import { getUrl, isVideoExt } from '@/utils/image'
import { onMounted, ref } from 'vue'
import { getFileList, editFileName } from '@/api/fileUploadAndDownload'
import UploadImage from '@/components/upload/image.vue'
import UploadCommon from '@/components/upload/common.vue'
import WarningBar from '@/components/warningBar/warningBar.vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { Delete, FolderAdd, Plus, Picture as IconPicture } from '@element-plus/icons-vue'
const imageUrl = ref('')
const imageCommon = ref('')
const search = ref({})
const page = ref(1)
const total = ref(0)
const pageSize = ref(20)
const props = defineProps({
modelValue: {
type: [String, Array],
default: ''
},
multiple: {
type: Boolean,
default: false
},
fileType: {
type: String,
default: ''
}
})
const multipleValue = ref([])
onMounted(() => {
if (props.multiple) {
multipleValue.value = props.modelValue
}
})
const emits = defineEmits(['update:modelValue'])
const deleteImg = (index) => {
multipleValue.value.splice(index, 1)
emits('update:modelValue', multipleValue.value)
}
//
const handleSizeChange = (val) => {
pageSize.value = val
getImageList()
}
const handleCurrentChange = (val) => {
page.value = val
getImageList()
}
const editFileNameFunc = async(row) => {
ElMessageBox.prompt('请输入文件名或者备注', '编辑', {
confirmButtonText: '确定',
cancelButtonText: '取消',
inputPattern: /\S/,
inputErrorMessage: '不能为空',
inputValue: row.name
}).then(async({ value }) => {
row.name = value
// console.log(row)
const res = await editFileName(row)
if (res.code === 0) {
ElMessage({
type: 'success',
message: '编辑成功!',
})
getImageList()
}
}).catch(() => {
ElMessage({
type: 'info',
message: '取消修改'
})
})
}
const drawer = ref(false)
const picList = ref([])
const imageTypeList = ['png', 'jpg', 'jpge', 'gif', 'bmp', 'webp']
const videoTyteList = ['mp4', 'avi', 'rmvb', 'rm', 'asf', 'divx', 'mpg', 'mpeg', 'mpe', 'wmv', 'mkv', 'vob']
const listObj = {
image: imageTypeList,
video: videoTyteList
}
const chooseImg = (url) => {
console.log(url)
if (props.fileType) {
const typeSuccess = listObj[props.fileType].some(item => {
if (url.includes(item)) {
return true
}
})
if (!typeSuccess) {
ElMessage({
type: 'error',
message: '当前类型不支持使用'
})
return
}
}
if (props.multiple) {
multipleValue.value.push(url)
emits('update:modelValue', multipleValue.value)
} else {
emits('update:modelValue', url)
}
drawer.value = false
}
const openChooseImg = async() => {
if (props.modelValue && !props.multiple) {
emits('update:modelValue', '')
return
}
await getImageList()
drawer.value = true
}
const getImageList = async() => {
const res = await getFileList({ page: page.value, pageSize: pageSize.value, ...search.value })
if (res.code === 0) {
picList.value = res.data.list
total.value = res.data.total
page.value = res.data.page
pageSize.value = res.data.pageSize
}
}
</script>
<style scoped lang="scss">
.multiple-img{
display: flex;
gap:8px;
width: 100%;
flex-wrap: wrap;
}
.add-image{
width: 120px;
height: 120px;
line-height: 120px;
display: flex;
justify-content: center;
border-radius: 20px;
border: 1px dashed #ccc;
background-size: cover;
cursor: pointer;
}
.update-image {
cursor: pointer;
width: 120px;
height: 120px;
line-height: 120px;
display: flex;
justify-content: center;
border-radius: 20px;
border: 1px dashed #ccc;
background-repeat: no-repeat;
background-size: cover;
position: relative;
&:hover {
color: #fff;
background: linear-gradient(
to bottom,
rgba(255, 255, 255, 0.15) 0%,
rgba(0, 0, 0, 0.15) 100%
),
radial-gradient(
at top center,
rgba(255, 255, 255, 0.4) 0%,
rgba(0, 0, 0, 0.4) 120%
)
#989898;
background-blend-mode: multiply, multiply;
background-size: cover;
.update {
color: #fff;
}
.video {
opacity: 0.2;
}
}
.video-icon {
position: absolute; left: calc(50% - 16px); top: calc(50% - 16px);
}
video {
object-fit: cover; max-width:100%; border-radius: 20px;
}
.update {
height: 120px;
width: 120px;
text-align: center;
color: transparent;
position: absolute;
}
}
.upload-btn-media-library {
margin-left: 20px;
}
.media {
display: flex;
flex-wrap: wrap;
.media-box {
width: 120px;
margin-left: 20px;
.img-title {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
line-height: 36px;
text-align: center;
cursor: pointer;
}
.header-img-box-list {
width: 120px;
height: 120px;
border: 1px dashed #ccc;
border-radius: 8px;
text-align: center;
line-height: 120px;
cursor: pointer;
overflow: hidden;
.el-image__inner {
max-width: 120px;
max-height: 120px;
vertical-align: middle;
width: unset;
height: unset;
}
.el-image {
position: relative;
}
.video-icon {
position: absolute; left: calc(50% - 16px); top: calc(50% - 16px);
}
video {
object-fit: cover; max-width:100%; min-height: 100%; border-radius: 8px;
}
}
}
}
</style>

View File

@ -0,0 +1,78 @@
<template>
<div>
<el-upload
:action="`${path}/fileUploadAndDownload/upload`"
:before-upload="checkFile"
:headers="{ 'x-token': userStore.token }"
:on-error="uploadError"
:on-success="uploadSuccess"
:show-file-list="false"
class="upload-btn"
>
<el-button type="primary">普通上传</el-button>
</el-upload>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { ElMessage } from 'element-plus'
import { useUserStore } from '@/pinia/modules/user'
import { isVideoMime, isImageMime } from '@/utils/image'
defineOptions({
name: 'UploadCommon',
})
const emit = defineEmits(['on-success'])
const path = ref(import.meta.env.VITE_BASE_API)
const userStore = useUserStore()
const fullscreenLoading = ref(false)
const checkFile = (file) => {
fullscreenLoading.value = true
const isLt500K = file.size / 1024 / 1024 < 0.5 // 500K, @todo
const isLt5M = file.size / 1024 / 1024 < 5 // 5MB, @todo
const isVideo = isVideoMime(file.type)
const isImage = isImageMime(file.type)
let pass = true
if (!isVideo && !isImage) {
ElMessage.error('上传图片只能是 jpg,png,svg,webp 格式, 上传视频只能是 mp4,webm 格式!')
fullscreenLoading.value = false
pass = false
}
if (!isLt5M && isVideo) {
ElMessage.error('上传视频大小不能超过 5MB')
fullscreenLoading.value = false
pass = false
}
if (!isLt500K && isImage) {
ElMessage.error('未压缩的上传图片大小不能超过 500KB请使用压缩上传')
fullscreenLoading.value = false
pass = false
}
console.log('upload file check result: ', pass)
return pass
}
const uploadSuccess = (res) => {
const { data } = res
if (data.file) {
emit('on-success', data.file.url)
}
}
const uploadError = () => {
ElMessage({
type: 'error',
message: '上传失败'
})
fullscreenLoading.value = false
}
</script>

View File

@ -0,0 +1,98 @@
<template>
<div>
<el-upload
:action="`${path}/fileUploadAndDownload/upload`"
:headers="{ 'x-token': userStore.token }"
:show-file-list="false"
:on-success="handleImageSuccess"
:before-upload="beforeImageUpload"
:multiple="false"
>
<el-button type="primary">压缩上传</el-button>
</el-upload>
</div>
</template>
<script setup>
import ImageCompress from '@/utils/image'
import { ref } from 'vue'
import { ElMessage } from 'element-plus'
import { useUserStore } from '@/pinia/modules/user'
defineOptions({
name: 'UploadImage',
})
const emit = defineEmits(['on-success'])
const props = defineProps({
imageUrl: {
type: String,
default: ''
},
fileSize: {
type: Number,
default: 2048 // 2M
},
maxWH: {
type: Number,
default: 1920 //
}
})
const path = ref(import.meta.env.VITE_BASE_API)
const userStore = useUserStore()
const beforeImageUpload = (file) => {
const isJPG = file.type === 'image/jpeg'
const isPng = file.type === 'image/png'
if (!isJPG && !isPng) {
ElMessage.error('上传头像图片只能是 jpg或png 格式!')
return false
}
const isRightSize = file.size / 1024 < props.fileSize
if (!isRightSize) {
//
const compress = new ImageCompress(file, props.fileSize, props.maxWH)
return compress.compress()
}
return isRightSize
}
const handleImageSuccess = (res) => {
const { data } = res
if (data.file) {
emit('on-success', data.file.url)
}
}
</script>
<style lang="scss" scoped>
.image-uploader {
border: 1px dashed #d9d9d9;
width: 180px;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
}
.image-uploader {
border-color: #409eff;
}
.image-uploader-icon {
font-size: 28px;
color: #8c939d;
width: 178px;
height: 178px;
line-height: 178px;
text-align: center;
}
.image {
width: 178px;
height: 178px;
display: block;
}
</style>

View File

@ -0,0 +1,33 @@
<template>
<div
class="px-1.5 py-2 flex items-center bg-amber-50 rounded gap-2 mb-3 text-amber-500"
:class="href&&'cursor-pointer'"
@click="open"
>
<el-icon class="text-xl">
<warning-filled />
</el-icon>
<span>
{{ title }}
</span>
</div>
</template>
<script setup>
import { WarningFilled } from '@element-plus/icons-vue'
const prop = defineProps({
title: {
type: String,
default: ''
},
href: {
type: String,
default: ''
}
})
const open = () => {
if (prop.href) {
window.open(prop.href)
}
}
</script>

58
src/core/config.js Normal file
View File

@ -0,0 +1,58 @@
/**
* 网站配置文件
*/
const config = {
appName: 'Gin-Vue-Admin',
appLogo: 'https://www.gin-vue-admin.com/img/logo.png',
showViteLogo: true
}
export const viteLogo = (env) => {
if (config.showViteLogo) {
const chalk = require('chalk')
console.log(
chalk.green(
`> 欢迎使用Gin-Vue-Admin开源地址https://github.com/flipped-aurora/gin-vue-admin`
)
)
console.log(
chalk.green(
`> 当前版本:v2.5.7`
)
)
console.log(
chalk.green(
`> 加群方式:微信shouzi_1994 QQ群622360840`
)
)
console.log(
chalk.green(
`> GVA讨论社区https://support.qq.com/products/371961`
)
)
console.log(
chalk.green(
`> 插件市场:https://plugin.gin-vue-admin.com`
)
)
console.log(
chalk.green(
`> 默认自动化文档地址:http://127.0.0.1:${env.VITE_SERVER_PORT}/swagger/index.html`
)
)
console.log(
chalk.green(
`> 默认前端文件运行地址:http://127.0.0.1:${env.VITE_CLI_PORT}`
)
)
console.log(
chalk.green(
`> 如果项目让您获得了收益,希望您能请团队喝杯可乐:https://www.gin-vue-admin.com/coffee/index.html`
)
)
console.log('\n')
}
}
export default config

22
src/core/gin-vue-admin.js Normal file
View File

@ -0,0 +1,22 @@
/*
* gin-vue-admin web框架组
*
* */
// 加载网站配置文件夹
import { register } from './global'
export default {
install: (app) => {
register(app)
console.log(`
欢迎使用 Gin-Vue-Admin
当前版本:v2.5.7
加群方式:微信shouzi_1994 QQ群622360840
GVA讨论社区:https://support.qq.com/products/371961
插件市场:https://plugin.gin-vue-admin.com
默认自动化文档地址:http://127.0.0.1:${import.meta.env.VITE_SERVER_PORT}/swagger/index.html
默认前端文件运行地址:http://127.0.0.1:${import.meta.env.VITE_CLI_PORT}
如果项目让您获得了收益希望您能请团队喝杯可乐:https://www.gin-vue-admin.com/coffee/index.html
`)
}
}

13
src/core/global.js Normal file
View File

@ -0,0 +1,13 @@
import config from './config'
// 统一导入el-icon图标
import * as ElIconModules from '@element-plus/icons-vue'
// 导入转换图标名称的函数
export const register = (app) => {
// 统一注册el-icon图标
for (const iconName in ElIconModules) {
app.component(iconName, ElIconModules[iconName])
}
app.config.globalProperties.$GIN_VUE_ADMIN = config
}

41
src/directive/auth.js Normal file
View File

@ -0,0 +1,41 @@
// 权限按钮展示指令
import { useUserStore } from '@/pinia/modules/user'
export default {
install: (app) => {
const userStore = useUserStore()
app.directive('auth', {
// 当被绑定的元素插入到 DOM 中时……
mounted: function(el, binding) {
const userInfo = userStore.userInfo
let type = ''
switch (Object.prototype.toString.call(binding.value)) {
case '[object Array]':
type = 'Array'
break
case '[object String]':
type = 'String'
break
case '[object Number]':
type = 'Number'
break
default:
type = ''
break
}
if (type === '') {
el.parentNode.removeChild(el)
return
}
const waitUse = binding.value.toString().split(',')
let flag = waitUse.some(item => Number(item) === userInfo.authorityId)
if (binding.modifiers.not) {
flag = !flag
}
if (!flag) {
el.parentNode.removeChild(el)
}
}
})
}
}

42
src/main.js Normal file
View File

@ -0,0 +1,42 @@
import 'element-plus/es/components/message/style/css'
import 'element-plus/es/components/loading/style/css'
import 'element-plus/es/components/notification/style/css'
import 'element-plus/es/components/message-box/style/css'
import './style/element_visiable.scss'
import { createApp } from 'vue'
// 引入gin-vue-admin前端初始化相关内容
import './core/gin-vue-admin'
// 引入封装的router
import router from '@/router/index'
import '@/permission'
import run from '@/core/gin-vue-admin.js'
import auth from '@/directive/auth'
import { store } from '@/pinia'
import App from './App.vue'
import { initDom } from './utils/positionToCode'
initDom()
/**
* @description 导入加载进度条防止首屏加载时间过长用户等待
*
* */
import Nprogress from 'nprogress'
import 'nprogress/nprogress.css'
Nprogress.configure({ showSpinner: false, ease: 'ease', speed: 500 })
Nprogress.start()
/**
* 无需在这块结束会在路由中间件中结束此块内容
* */
const app = createApp(App)
app.config.productionTip = false
app
.use(run)
.use(store)
.use(auth)
.use(router)
.mount('#app')
export default app

119
src/permission.js Normal file
View File

@ -0,0 +1,119 @@
import { useUserStore } from '@/pinia/modules/user'
import { useRouterStore } from '@/pinia/modules/router'
import getPageTitle from '@/utils/page'
import router from '@/router'
import Nprogress from 'nprogress'
const whiteList = ['Login', 'Init']
const getRouter = async(userStore) => {
const routerStore = useRouterStore()
await routerStore.SetAsyncRouter()
await userStore.GetUserInfo()
const asyncRouters = routerStore.asyncRouters
asyncRouters.forEach(asyncRouter => {
router.addRoute(asyncRouter)
})
}
async function handleKeepAlive(to) {
if (to.matched.some(item => item.meta.keepAlive)) {
if (to.matched && to.matched.length > 2) {
for (let i = 1; i < to.matched.length; i++) {
const element = to.matched[i - 1]
if (element.name === 'layout') {
to.matched.splice(i, 1)
await handleKeepAlive(to)
}
// 如果没有按需加载完成则等待加载
if (typeof element.components.default === 'function') {
await element.components.default()
await handleKeepAlive(to)
}
}
}
}
}
router.beforeEach(async(to, from) => {
const routerStore = useRouterStore()
Nprogress.start()
const userStore = useUserStore()
to.meta.matched = [...to.matched]
handleKeepAlive(to)
const token = userStore.token
// 在白名单中的判断情况
document.title = getPageTitle(to.meta.title, to)
if (whiteList.indexOf(to.name) > -1) {
if (token) {
if (!routerStore.asyncRouterFlag && whiteList.indexOf(from.name) < 0) {
await getRouter(userStore)
}
// token 可以解析但是却是不存在的用户 id 或角色 id 会导致无限调用
if (userStore.userInfo?.authority?.defaultRouter != null) {
if (router.hasRoute(userStore.userInfo.authority.defaultRouter)) {
return { name: userStore.userInfo.authority.defaultRouter }
} else {
return { path: '/layout/404' }
}
} else {
// 强制退出账号
userStore.ClearStorage()
return {
name: 'Login',
query: {
redirect: document.location.hash
}
}
}
} else {
return true
}
} else {
// 不在白名单中并且已经登录的时候
if (token) {
// 添加flag防止多次获取动态路由和栈溢出
if (!routerStore.asyncRouterFlag && whiteList.indexOf(from.name) < 0) {
await getRouter(userStore)
if (userStore.token) {
if (router.hasRoute(userStore.userInfo.authority.defaultRouter)) {
return { ...to, replace: true }
} else {
return { path: '/layout/404' }
}
} else {
return {
name: 'Login',
query: { redirect: to.href }
}
}
} else {
if (to.matched.length) {
return true
} else {
return { path: '/layout/404' }
}
}
}
// 不在白名单中并且未登录的时候
if (!token) {
return {
name: 'Login',
query: {
redirect: document.location.hash
}
}
}
}
})
router.afterEach(() => {
// 路由加载完成后关闭进度条
document.getElementsByClassName('main-cont main-right')[0]?.scrollTo(0, 0)
Nprogress.done()
})
router.onError(() => {
// 路由发生错误后销毁进度条
Nprogress.remove()
})

7
src/pinia/index.js Normal file
View File

@ -0,0 +1,7 @@
import { createPinia } from 'pinia'
const store = createPinia()
export {
store
}

View File

@ -0,0 +1,39 @@
import { findSysDictionary } from '@/api/sysDictionary'
import { defineStore } from 'pinia'
import { ref } from 'vue'
export const useDictionaryStore = defineStore('dictionary', () => {
const dictionaryMap = ref({})
const setDictionaryMap = (dictionaryRes) => {
dictionaryMap.value = { ...dictionaryMap.value, ...dictionaryRes }
}
const getDictionary = async(type) => {
if (dictionaryMap.value[type] && dictionaryMap.value[type].length) {
return dictionaryMap.value[type]
} else {
const res = await findSysDictionary({ type })
if (res.code === 0) {
const dictionaryRes = {}
const dict = []
res.data.resysDictionary.sysDictionaryDetails && res.data.resysDictionary.sysDictionaryDetails.forEach(item => {
dict.push({
label: item.label,
value: item.value
})
})
dictionaryRes[res.data.resysDictionary.type] = dict
setDictionaryMap(dictionaryRes)
return dictionaryMap.value[type]
}
}
}
return {
dictionaryMap,
setDictionaryMap,
getDictionary
}
})

103
src/pinia/modules/router.js Normal file
View File

@ -0,0 +1,103 @@
import { asyncRouterHandle } from '@/utils/asyncRouter'
import { emitter } from '@/utils/bus.js'
import { asyncMenu } from '@/api/menu'
import { defineStore } from 'pinia'
import { ref } from 'vue'
const notLayoutRouterArr = []
const keepAliveRoutersArr = []
const nameMap = {}
const formatRouter = (routes, routeMap, parent) => {
routes && routes.forEach(item => {
item.parent = parent
item.meta.btns = item.btns
item.meta.hidden = item.hidden
if (item.meta.defaultMenu === true) {
if (!parent) {
item = { ...item, path: `/${item.path}` }
notLayoutRouterArr.push(item)
}
}
routeMap[item.name] = item
if (item.children && item.children.length > 0) {
formatRouter(item.children, routeMap, item)
}
})
}
const KeepAliveFilter = (routes) => {
routes && routes.forEach(item => {
// 子菜单中有 keep-alive 的,父菜单也必须 keep-alive否则无效。这里将子菜单中有 keep-alive 的父菜单也加入。
if ((item.children && item.children.some(ch => ch.meta.keepAlive) || item.meta.keepAlive)) {
item.component && item.component().then(val => {
keepAliveRoutersArr.push(val.default.name)
nameMap[item.name] = val.default.name
})
}
if (item.children && item.children.length > 0) {
KeepAliveFilter(item.children)
}
})
}
export const useRouterStore = defineStore('router', () => {
const keepAliveRouters = ref([])
const asyncRouterFlag = ref(0)
const setKeepAliveRouters = (history) => {
const keepArrTemp = []
history.forEach(item => {
if (nameMap[item.name]) {
keepArrTemp.push(nameMap[item.name])
}
})
keepAliveRouters.value = Array.from(new Set(keepArrTemp))
}
emitter.on('setKeepAlive', setKeepAliveRouters)
const asyncRouters = ref([])
const routeMap = ({})
// 从后台获取动态路由
const SetAsyncRouter = async() => {
asyncRouterFlag.value++
const baseRouter = [{
path: '/layout',
name: 'layout',
component: 'view/layout/index.vue',
meta: {
title: '底层layout'
},
children: []
}]
const asyncRouterRes = await asyncMenu()
const asyncRouter = asyncRouterRes.data.menus
asyncRouter && asyncRouter.push({
path: 'reload',
name: 'Reload',
hidden: true,
meta: {
title: '',
closeTab: true,
},
component: 'view/error/reload.vue'
})
formatRouter(asyncRouter, routeMap)
baseRouter[0].children = asyncRouter
if (notLayoutRouterArr.length !== 0) {
baseRouter.push(...notLayoutRouterArr)
}
asyncRouterHandle(baseRouter)
KeepAliveFilter(asyncRouter)
asyncRouters.value = baseRouter
return true
}
return {
asyncRouters,
keepAliveRouters,
asyncRouterFlag,
SetAsyncRouter,
routeMap
}
})

163
src/pinia/modules/user.js Normal file
View File

@ -0,0 +1,163 @@
import { login, getUserInfo, setSelfInfo } from '@/api/user'
import { jsonInBlacklist } from '@/api/jwt'
import router from '@/router/index'
import { ElLoading, ElMessage } from 'element-plus'
import { defineStore } from 'pinia'
import { ref, computed, watch } from 'vue'
import { useRouterStore } from './router'
export const useUserStore = defineStore('user', () => {
const loadingInstance = ref(null)
const userInfo = ref({
uuid: '',
nickName: '',
headerImg: '',
authority: {},
sideMode: 'dark',
activeColor: 'var(--el-color-primary)',
baseColor: '#fff'
})
const token = ref(window.localStorage.getItem('token') || '')
const setUserInfo = (val) => {
userInfo.value = val
}
const setToken = (val) => {
token.value = val
}
const NeedInit = () => {
token.value = ''
window.localStorage.removeItem('token')
localStorage.clear()
router.push({ name: 'Init', replace: true })
}
const ResetUserInfo = (value = {}) => {
userInfo.value = {
...userInfo.value,
...value
}
}
/* 获取用户信息*/
const GetUserInfo = async() => {
const res = await getUserInfo()
if (res.code === 0) {
setUserInfo(res.data.userInfo)
}
return res
}
/* 登录*/
const LoginIn = async(loginInfo) => {
loadingInstance.value = ElLoading.service({
fullscreen: true,
text: '登录中,请稍候...',
})
try {
const res = await login(loginInfo)
if (res.code === 0) {
setUserInfo(res.data.user)
setToken(res.data.token)
const routerStore = useRouterStore()
await routerStore.SetAsyncRouter()
const asyncRouters = routerStore.asyncRouters
asyncRouters.forEach(asyncRouter => {
router.addRoute(asyncRouter)
})
if (!router.hasRoute(userInfo.value.authority.defaultRouter)) {
ElMessage.error('请联系管理员进行授权')
} else {
await router.replace({ name: userInfo.value.authority.defaultRouter })
}
loadingInstance.value.close()
const isWin = ref(/windows/i.test(navigator.userAgent))
if (isWin.value) {
window.localStorage.setItem('osType', 'WIN')
} else {
window.localStorage.setItem('osType', 'MAC')
}
return true
}
} catch (e) {
loadingInstance.value.close()
}
loadingInstance.value.close()
}
/* 登出*/
const LoginOut = async() => {
const res = await jsonInBlacklist()
if (res.code === 0) {
token.value = ''
sessionStorage.clear()
localStorage.clear()
router.push({ name: 'Login', replace: true })
window.location.reload()
}
}
/* 清理数据 */
const ClearStorage = async() => {
token.value = ''
sessionStorage.clear()
localStorage.clear()
}
/* 设置侧边栏模式*/
const changeSideMode = async(data) => {
const res = await setSelfInfo({ sideMode: data })
if (res.code === 0) {
userInfo.value.sideMode = data
ElMessage({
type: 'success',
message: '设置成功'
})
}
}
const mode = computed(() => userInfo.value.sideMode)
const sideMode = computed(() => {
if (userInfo.value.sideMode === 'dark') {
return '#191a23'
} else if (userInfo.value.sideMode === 'light') {
return '#fff'
} else {
return userInfo.value.sideMode
}
})
const baseColor = computed(() => {
if (userInfo.value.sideMode === 'dark') {
return '#fff'
} else if (userInfo.value.sideMode === 'light') {
return '#191a23'
} else {
return userInfo.value.baseColor
}
})
const activeColor = computed(() => {
return 'var(--el-color-primary)'
})
watch(() => token.value, () => {
window.localStorage.setItem('token', token.value)
})
return {
userInfo,
token,
NeedInit,
ResetUserInfo,
GetUserInfo,
LoginIn,
LoginOut,
changeSideMode,
mode,
sideMode,
setToken,
baseColor,
activeColor,
loadingInstance,
ClearStorage
}
})

View File

@ -0,0 +1,30 @@
import service from '@/utils/request'
// @Tags System
// @Summary 发送测试邮件
// @Security ApiKeyAuth
// @Produce application/json
// @Success 200 {string} string "{"success":true,"data":{},"msg":"发送成功"}"
// @Router /email/emailTest [post]
export const emailTest = (data) => {
return service({
url: '/email/emailTest',
method: 'post',
data
})
}
// @Tags System
// @Summary 发送邮件
// @Security ApiKeyAuth
// @Produce application/json
// @Param data body email_response.Email true "发送邮件必须的参数"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"发送成功"}"
// @Router /email/sendEmail [post]
export const sendEmail = (data) => {
return service({
url: '/email/sendEmail',
method: 'post',
data
})
}

View File

@ -0,0 +1,63 @@
<template>
<div>
<warning-bar title="需要提前配置email配置文件为防止不必要的垃圾邮件在线体验功能不开放此功能体验。" />
<div class="gva-form-box">
<el-form
ref="emailForm"
label-position="right"
label-width="80px"
:model="form"
>
<el-form-item label="目标邮箱">
<el-input v-model="form.to" />
</el-form-item>
<el-form-item label="邮件">
<el-input v-model="form.subject" />
</el-form-item>
<el-form-item label="邮件内容">
<el-input
v-model="form.body"
type="textarea"
/>
</el-form-item>
<el-form-item>
<el-button @click="sendTestEmail">发送测试邮件</el-button>
<el-button @click="sendEmail">发送邮件</el-button>
</el-form-item>
</el-form>
</div>
</div>
</template>
<script setup>
import WarningBar from '@/components/warningBar/warningBar.vue'
import { emailTest } from '@/plugin/email/api/email.js'
import { ElMessage } from 'element-plus'
import { reactive, ref } from 'vue'
defineOptions({
name: 'Email',
})
const emailForm = ref(null)
const form = reactive({
to: '',
subject: '',
body: '',
})
const sendTestEmail = async() => {
const res = await emailTest()
if (res.code === 0) {
ElMessage.success('发送成功')
}
}
const sendEmail = async() => {
const res = await emailTest()
if (res.code === 0) {
ElMessage.success('发送成功,请查收')
}
}
</script>

31
src/router/index.js Normal file
View File

@ -0,0 +1,31 @@
import { createRouter, createWebHashHistory } from 'vue-router'
const routes = [{
path: '/',
redirect: '/login'
},
{
path: '/init',
name: 'Init',
component: () => import('@/view/init/index.vue')
},
{
path: '/login',
name: 'Login',
component: () => import('@/view/login/index.vue')
},
{
path: '/:catchAll(.*)',
meta: {
closeTab: true,
},
component: () => import('@/view/error/index.vue')
}
]
const router = createRouter({
history: createWebHashHistory(),
routes,
})
export default router

View File

@ -0,0 +1,24 @@
@forward 'element-plus/theme-chalk/src/common/var.scss' with (
$colors: (
'white': #ffffff,
'black': #000000,
'primary': (
'base': #4d70ff,
),
'success': (
'base': #67c23a,
),
'warning': (
'base': #e6a23c,
),
'danger': (
'base': #f56c6c,
),
'error': (
'base': #f56c6c,
),
'info': (
'base': #909399,
),
)
);

View File

@ -0,0 +1,37 @@
@import '@/style/main.scss';
#app {
.el-button {
font-weight: 400;
border-radius: 2px;
}
}
::-webkit-scrollbar {
@apply hidden;
}
.gva-search-box {
@apply p-6 pb-0.5 bg-white rounded mb-3;
}
.gva-form-box {
@apply p-6 bg-white rounded;
}
.gva-pagination {
@apply flex justify-end;
.el-pagination__editor {
.el-input__inner {
@apply h-8;
}
}
.is-active {
@apply rounded text-white;
background: var(--el-color-primary);
color: #ffffff !important;
}
}

47
src/style/iconfont.css Normal file
View File

@ -0,0 +1,47 @@
@font-face {
font-family: 'gvaIcon';
src: url('data:font/ttf;charset=utf-8;base64,AAEAAAANAIAAAwBQRkZUTZJUyU8AAA14AAAAHEdERUYAKQARAAANWAAAAB5PUy8yPJpJTAAAAVgAAABgY21hcM0T0L4AAAHYAAABWmdhc3D//wADAAANUAAAAAhnbHlmRk3UvwAAA0wAAAbYaGVhZB/a5jgAAADcAAAANmhoZWEHngOFAAABFAAAACRobXR4DaoBrAAAAbgAAAAebG9jYQbMCGgAAAM0AAAAGG1heHABGgB+AAABOAAAACBuYW1lXoIBAgAACiQAAAKCcG9zdN15OnUAAAyoAAAAqAABAAAAAQAA+a916l8PPPUACwQAAAAAAN5YUSMAAAAA3lhRIwBL/8ADwAM1AAAACAACAAAAAAAAAAEAAAOA/4AAXAQAAAAAAAPAAAEAAAAAAAAAAAAAAAAAAAAEAAEAAAALAHIABQAAAAAAAgAAAAoACgAAAP8AAAAAAAAABAQAAZAABQAAAokCzAAAAI8CiQLMAAAB6wAyAQgAAAIABQMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUGZFZADA5mXmfQOA/4AAAAPcAIAAAAABAAAAAAAAAAAAAAAgAAEEAAAAAAAAAAQAAAAEAACLAIoAYAB1AHYASwBLAGAAAAAAAAMAAAADAAAAHAABAAAAAABUAAMAAQAAABwABAA4AAAACgAIAAIAAuZm5mrmduZ9//8AAOZl5mrmdeZ7//8ZnhmbGZEZjQABAAAAAAAAAAAAAAAAAQYAAAEAAAAAAAAAAQIAAAACAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEYAigEcAbgCUAK6AxoDbAACAIsAIANsAswAEQAjAAAlIicBJjQ3ATYeAQYHCQEeAQYhIicBJjQ3ATYeAQYHCQEeAQYDSw0J/qsLCwFVChsSAgr+xAE8CgIV/qkNCP6qCgoBVgkbEgIK/sUBOwoCFCAJATULGQsBNQoCExwI/uL+4ggbFAkBNQsZCwE1CgITHAj+4v7iCRoUAAAAAAIAigAgA2sCzAARACIAAAE0JwEmDgEWFwkBDgEWMjcBNiUBJg4BFhcJAQ4BFjI3ATY0AiAL/qsJHBECCQE8/sQJAhQZCQFVCwFA/qsKGxICCgE8/sQKAhUZCQFVCwF1DQsBNQoCExwI/uL+4gkaFAkBNQskATUKAhMcCP7i/uIJGhQJATULGQADAGD/wAOgAzUATABcAGwAAAE1NCcmJyYiBwYHBh0BDgEdARQWOwEyNj0BNCYrATU0NzY3NjIXFhcWHQEjIgYdARQWOwEGBwYHLgEjIgYUFjMyNjc2NzY3PgE9ATQmBRUUBisBIiY9ATQ2OwEyFgUUBisBIiY9ATQ2OwEyFhUDYDAvT1O+U08vMBslLB9VHi0tHiAoJkFDnENBJiggHi0tHhUPJC5SChwRHCQkHBEeCHJAMxAfKiX9kAYFVQUGBgVVBQYCVQYFVQUGBgVVBQYByQxgUlAuMDAuUFJgDAQqG6seLCweqx4tCk5DQScnJydBQ04KLR6rHiwrGiAGDxElNiUSEAc1KkUBKx6rGyhFqwQGBgSrBQYGsAQGBgSrBQYGBQAABAB1//UDjQMLABsANwBSAHEAABMyNj0BFxYyNjQvATMyNjQmKwEiBwYHBh0BFBYFIgYdAScmIgYUHwEjIgYUFjsBMjc2NzY9ATYmJQc1NCYiBh0BFBcWFxY7ATI2NCYrATc2NCYGATQ1FSYnJisBIgYUFjsBBwYUFjI/ARUUFjI2PQEnJpUNE7wJHRMKvIcMFBQM1ggCDAgCFALiDRPJCRoTCcmJDBQUDNYIAg8CAwES/gbJExkUAggKBAbWDBQUDInJCRMXAgEHCwQG2AwUFAyJvAkSHgi8ExoTAgEB9RQMibwIEhkKvBMZFAIGDAQI1gwU6hQMickJExoJyRMZFAIICgQG2AwUIsmHDBQUDNYIAg8CAxQZE8kKGRMBAcABAQIOAwMUGRO8ChkTCbyHDBQUDNYFBAAABAB2//cDjgMMABoANQBRAG0AAAEjIgYUFjsBMjc2NzY9ATQmIgYdAScmIgYUFwEzMjY0JisBIgcGBwYdARQWMjY9ARcWMjY0JyUmJyYrASIGFBY7AQcGFBYyPwEVFBYyNj0BLgE3FhcWOwEyNjQmKwE3NjQmIg8BNTQmIgYdAR4BATqJDRMTDdUJAg8CAhMaE7cKGRQKAjeJDRMTDdUJAg8CAhMaE8gJHhIK/i8HCgQH1w0TEw2JyQoTHQnIFBkTAQKoBwoEBtYNExMNibwKFBkKvBMZFAICAhoUGRMCBwoEBtYNExMNib4KExoK/iAUGRMCBwoEB9UNExMNickIEhkK8w8CAhMZFMgKGRMJyYkNExMN1QIJzQ8CAhMZFLsKGhMKvIkNExMN1QMIAAAAAAUAS//LA7UDNQAUACkAKgA3AEQAAAEiBwYHBhQXFhcWMjc2NzY0JyYnJgMiJyYnJjQ3Njc2MhcWFxYUBwYHBgMjFB4BMj4BNC4BIg4BFyIGHQEUFjI2PQE0JgIAd2ZiOzs7O2Jm7mZiOzs7O2Jmd2VXVDIzMzJUV8pXVDIzMzJUV2UrDBQWFAwMFBYUDCsNExMaExMDNTs7YmbuZmI7Ozs7YmbuZmI7O/zWMzJUV8pXVDIzMzJUV8pXVDIzAjULFAwMFBYUDAwUgBQM6w0TEw3rDBQAAQBL/+ADwAMgAD0AAAEmBg8BLgEjIgcGBwYUFxYXFjMyPgE3Ni4BBgcOAiMiJyYnJjQ3Njc2MzIeARcnJg4BFh8BMj8BNj8BNCYDpgwXAxc5yXZyY184Ojo4X2NyWaB4HgULGhcFGWaJS2FUUTAwMTBRU2FIhGQbgA0WBw4NwgUIBAwDMQ0CsQMODFhmeDk3XmHiYV43OUV9UQ0XCQsMRWo6MC9PUr9TTy8wNmNBJQMOGhYDMwMBCAu6DRYAAAAAAgBg/8YDugMiAB4AMwAABSc+ATU0JyYnJiIHBgcGFBcWFxYzMjc2NxcWMjc2JiUiJyYnJjQ3Njc2MhcWFxYUBwYHBgOxviouNDFVV8lXVTIzMzJVV2RDPzwzvgkeCAcB/hxUSEYpKiopRkioSEYpKyspRkgCvjB9RGRYVDIzNDJVWMlXVTE0GBYqvgkJChuBKylGSKhIRikqKilGSKhIRikrAAAAABIA3gABAAAAAAAAABMAKAABAAAAAAABAAgATgABAAAAAAACAAcAZwABAAAAAAADAAgAgQABAAAAAAAEAAgAnAABAAAAAAAFAAsAvQABAAAAAAAGAAgA2wABAAAAAAAKACsBPAABAAAAAAALABMBkAADAAEECQAAACYAAAADAAEECQABABAAPAADAAEECQACAA4AVwADAAEECQADABAAbwADAAEECQAEABAAigADAAEECQAFABYApQADAAEECQAGABAAyQADAAEECQAKAFYA5AADAAEECQALACYBaABDAHIAZQBhAHQAZQBkACAAYgB5ACAAaQBjAG8AbgBmAG8AbgB0AABDcmVhdGVkIGJ5IGljb25mb250AABpAGMAbwBuAGYAbwBuAHQAAGljb25mb250AABSAGUAZwB1AGwAYQByAABSZWd1bGFyAABpAGMAbwBuAGYAbwBuAHQAAGljb25mb250AABpAGMAbwBuAGYAbwBuAHQAAGljb25mb250AABWAGUAcgBzAGkAbwBuACAAMQAuADAAAFZlcnNpb24gMS4wAABpAGMAbwBuAGYAbwBuAHQAAGljb25mb250AABHAGUAbgBlAHIAYQB0AGUAZAAgAGIAeQAgAHMAdgBnADIAdAB0AGYAIABmAHIAbwBtACAARgBvAG4AdABlAGwAbABvACAAcAByAG8AagBlAGMAdAAuAABHZW5lcmF0ZWQgYnkgc3ZnMnR0ZiBmcm9tIEZvbnRlbGxvIHByb2plY3QuAABoAHQAdABwADoALwAvAGYAbwBuAHQAZQBsAGwAbwAuAGMAbwBtAABodHRwOi8vZm9udGVsbG8uY29tAAAAAAIAAAAAAAAACgAAAAAAAQAAAAAAAAAAAAAAAAAAAAAACwAAAAEAAgECAQMBBAEFAQYBBwEIAQkRYXJyb3ctZG91YmxlLWxlZnQSYXJyb3ctZG91YmxlLXJpZ2h0EGN1c3RvbWVyLXNlcnZpY2URZnVsbHNjcmVlbi1leHBhbmQRZnVsbHNjcmVlbi1zaHJpbmsGcHJvbXB0B3JlZnJlc2gGc2VhcmNoAAAAAf//AAIAAQAAAAwAAAAWAAAAAgABAAMACgABAAQAAAACAAAAAAAAAAEAAAAA1aQnCAAAAADeWFEjAAAAAN5YUSM=') format('truetype');
font-weight: 600;
font-style: normal;
font-display: swap;
}
.gvaIcon {
font-family: "gvaIcon" !important;
font-size: 16px;
font-style: normal;
font-weight: 800;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.gvaIcon-arrow-double-left:before {
content: "\e665";
}
.gvaIcon-arrow-double-right:before {
content: "\e666";
}
.gvaIcon-fullscreen-shrink:before {
content: "\e676";
}
.gvaIcon-customer-service:before {
content: "\e66a";
}
.gvaIcon-fullscreen-expand:before {
content: "\e675";
}
.gvaIcon-prompt:before {
content: "\e67b";
}
.gvaIcon-refresh:before {
content: "\e67c";
}
.gvaIcon-search:before {
content: "\e67d";
}

689
src/style/main.scss Normal file
View File

@ -0,0 +1,689 @@
/* Document
========================================================================== */
/**
* 1. Correct the line height in all browsers.
* 2. Prevent adjustments of font size after orientation changes in iOS.
*/
@import '@/style/iconfont.css';
html {
line-height: 1.15;
/* 1 */
-webkit-text-size-adjust: 100%;
/* 2 */
}
/* Sections
========================================================================== */
/**
* Remove the margin in all browsers.
*/
body {
margin: 0;
}
/**
* Render the `main` element consistently in IE.
*/
main {
display: block;
}
/**
* Correct the font size and margin on `h1` elements within `section` and
* `article` contexts in Chrome, Firefox, and Safari.
*/
h1 {
font-size: 2em;
margin: 0.67em 0;
}
/* Grouping content
========================================================================== */
/**
* 1. Add the correct box sizing in Firefox.
* 2. Show the overflow in Edge and IE.
*/
hr {
box-sizing: content-box;
/* 1 */
height: 0;
/* 1 */
overflow: visible;
/* 2 */
}
/**
* 1. Correct the inheritance and scaling of font size in all browsers.
* 2. Correct the odd `em` font sizing in all browsers.
*/
pre {
font-family: monospace, monospace;
/* 1 */
font-size: 1em;
/* 2 */
}
/* Text-level semantics
========================================================================== */
/**
* Remove the gray background on active links in IE 10.
*/
a {
background-color: transparent;
}
/**
* 1. Remove the bottom border in Chrome 57-
* 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
*/
abbr[title] {
border-bottom: none;
/* 1 */
text-decoration: underline;
/* 2 */
text-decoration: underline dotted;
/* 2 */
}
/**
* Add the correct font weight in Chrome, Edge, and Safari.
*/
b,
strong {
font-weight: bolder;
}
/**
* 1. Correct the inheritance and scaling of font size in all browsers.
* 2. Correct the odd `em` font sizing in all browsers.
*/
code,
kbd,
samp {
font-family: monospace, monospace;
/* 1 */
font-size: 1em;
/* 2 */
}
/**
* Add the correct font size in all browsers.
*/
small {
font-size: 80%;
}
/**
* Prevent `sub` and `sup` elements from affecting the line height in
* all browsers.
*/
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sub {
bottom: -0.25em;
}
sup {
top: -0.5em;
}
/* Embedded content
========================================================================== */
/**
* Remove the border on images inside links in IE 10.
*/
img {
border-style: none;
}
/* Forms
========================================================================== */
/**
* 1. Change the font styles in all browsers.
* 2. Remove the margin in Firefox and Safari.
*/
button,
input,
optgroup,
select,
textarea {
font-family: inherit;
/* 1 */
font-size: 100%;
/* 1 */
line-height: 1.15;
/* 1 */
margin: 0;
/* 2 */
}
/**
* Show the overflow in IE.
* 1. Show the overflow in Edge.
*/
button,
input {
/* 1 */
overflow: visible;
}
/**
* Remove the inheritance of text transform in Edge, Firefox, and IE.
* 1. Remove the inheritance of text transform in Firefox.
*/
button,
select {
/* 1 */
text-transform: none;
}
/**
* Correct the inability to style clickable types in iOS and Safari.
*/
button,
[type="button"],
[type="reset"],
[type="submit"] {
-webkit-appearance: button;
}
/**
* Remove the inner border and padding in Firefox.
*/
button::-moz-focus-inner,
[type="button"]::-moz-focus-inner,
[type="reset"]::-moz-focus-inner,
[type="submit"]::-moz-focus-inner {
border-style: none;
padding: 0;
}
/**
* Restore the focus styles unset by the previous rule.
*/
button:-moz-focusring,
[type="button"]:-moz-focusring,
[type="reset"]:-moz-focusring,
[type="submit"]:-moz-focusring {
outline: 1px dotted ButtonText;
}
/**
* Correct the padding in Firefox.
*/
fieldset {
padding: 0.35em 0.75em 0.625em;
}
/**
* 1. Correct the text wrapping in Edge and IE.
* 2. Correct the color inheritance from `fieldset` elements in IE.
* 3. Remove the padding so developers are not caught out when they zero out
* `fieldset` elements in all browsers.
*/
legend {
box-sizing: border-box;
/* 1 */
color: inherit;
/* 2 */
display: table;
/* 1 */
max-width: 100%;
/* 1 */
padding: 0;
/* 3 */
white-space: normal;
/* 1 */
}
/**
* Add the correct vertical alignment in Chrome, Firefox, and Opera.
*/
progress {
vertical-align: baseline;
}
/**
* Remove the default vertical scrollbar in IE 10+.
*/
textarea {
overflow: auto;
}
/**
* 1. Add the correct box sizing in IE 10.
* 2. Remove the padding in IE 10.
*/
[type="checkbox"],
[type="radio"] {
box-sizing: border-box;
/* 1 */
padding: 0;
/* 2 */
}
/**
* Correct the cursor style of increment and decrement buttons in Chrome.
*/
[type="number"]::-webkit-inner-spin-button,
[type="number"]::-webkit-outer-spin-button {
height: auto;
}
/**
* 1. Correct the odd appearance in Chrome and Safari.
* 2. Correct the outline style in Safari.
*/
[type="search"] {
-webkit-appearance: textfield;
/* 1 */
outline-offset: -2px;
/* 2 */
}
/**
* Remove the inner padding in Chrome and Safari on macOS.
*/
[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
/**
* 1. Correct the inability to style clickable types in iOS and Safari.
* 2. Change font properties to `inherit` in Safari.
*/
::-webkit-file-upload-button {
-webkit-appearance: button;
/* 1 */
font: inherit;
/* 2 */
}
/* Interactive
========================================================================== */
/*
* Add the correct display in Edge, IE 10+, and Firefox.
*/
details {
display: block;
}
/*
* Add the correct display in all browsers.
*/
summary {
display: list-item;
}
/* Misc
========================================================================== */
/**
* Add the correct display in IE 10+.
*/
template {
display: none;
}
/**
* Add the correct display in IE 10.
*/
[hidden] {
display: none;
}
HTML,
body,
div,
ul,
ol,
dl,
li,
dt,
dd,
p,
blockquote,
pre,
form,
fieldset,
table,
th,
td {
border: none;
font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "微软雅黑", Arial, sans-serif;
font-size: 14px;
margin: 0px;
padding: 0px;
}
html,
body {
height: 100%;
width: 100%;
}
address,
caption,
cite,
code,
th,
var {
font-style: normal;
font-weight: normal;
}
a {
text-decoration: none;
}
input::-ms-clear {
display: none;
}
input::-ms-reveal {
display: none;
}
input {
-webkit-appearance: none;
margin: 0;
outline: none;
padding: 0;
}
input::-webkit-input-placeholder {
color: #ccc;
}
input::-ms-input-placeholder {
color: #ccc;
}
input::-moz-placeholder {
color: #ccc;
}
input[type=submit],
input[type=button] {
cursor: pointer;
}
button[disabled],
input[disabled] {
cursor: default;
}
img {
border: none;
}
ul,
ol,
li {
list-style-type: none;
}
// 导航
#app {
.el-container {
@apply relative h-full w-full;
}
.el-container.mobile.openside {
@apply fixed top-0;
}
.gva-aside {
@apply fixed top-0 left-0 z-[1001] overflow-hidden;
.el-menu {
@apply border-r-0;
}
}
.aside {
.el-menu--collapse {
>.el-menu-item {
display: flex;
justify-content: center;
}
}
.el-sub-menu {
.el-menu {
.is-active {
// 关闭三级菜单二级菜单样式
ul {
border: none;
}
}
// 关闭三级菜单二级菜单样式
.is-active.is-opened {
ul {
border: none;
}
}
}
}
}
.hideside {
.aside {
@apply w-[54px]
}
}
.mobile {
.gva-aside {
@apply w-[54px];
}
}
.hideside {
.main-cont.el-main {
@apply ml-[54px];
}
}
.mobile {
.main-cont.el-main {
@apply ml-0;
}
}
}
// layout
.admin-box {
@apply min-h-[calc(100vh-200px)] px-3 py-4 mt-28 mb-4 mx-1;
.el-table {
th {
@apply px-0 py-2;
.cell {
@apply leading-[40px] text-gray-700;
}
}
td {
@apply px-0 py-2;
.cell {
@apply leading-[40px] text-gray-600;
}
}
.is-leaf {
background: #F7FBFF !important;
@apply border-b border-t-0 border-l-0 border-r-0 border-solid border-gray-50;
}
}
}
// table
.el-pagination {
@apply mt-8;
.btn-prev,
.btn-next {
@apply border border-solid border-gray-300 rounded;
}
.el-pager {
li {
@apply border border-solid border-gray-300 rounded text-gray-600 text-sm mx-1;
}
}
}
.el-container.layout-cont {
.header-cont {
@apply px-4 h-16 bg-white;
}
.main-cont {
@apply h-screen overflow-visible;
&.el-main {
@apply min-h-full ml-[220px] bg-main p-0 overflow-auto;
}
.breadcrumb {
@apply h-16 flex items-center p-0 ml-12 text-lg;
.el-breadcrumb__item {
.el-breadcrumb__inner {
@apply text-gray-600;
}
}
.el-breadcrumb__item:nth-last-child(1) {
.el-breadcrumb__inner {
@apply text-gray-600;
}
}
}
.router-history {
@apply bg-white p-0 border-t border-l-0 border-r-0 border-b-0 border-solid border-gray-100;
.el-tabs__header {
@apply m-0;
.el-tabs__item{
@apply border-solid border-r border-t-0 border-gray-100 border-b-0 border-l-0;
}
.el-tabs__item.is-active {
@apply bg-blue-500 bg-opacity-5;
}
.el-tabs__nav {
@apply border-0;
}
}
}
.aside {
@apply overflow-auto;
}
.el-menu-vertical {
@apply h-[calc(100vh-60px)];
&:not(.el-menu--collapse) {
@apply w-[220px];
}
}
.el-menu--collapse {
@apply w-[54px];
li {
.el-tooltip,
.el-sub-menu__title {
@apply px-4;
}
}
}
}
}
.el-dropdown {
@apply overflow-hidden
}
.gva-table-box {
@apply p-6 bg-white rounded;
}
.gva-btn-list {
@apply mb-3 flex gap-3 items-center;
}
#nprogress .bar {
background: #29d !important;
}

31
src/utils/asyncRouter.js Normal file
View File

@ -0,0 +1,31 @@
const viewModules = import.meta.glob('../view/**/*.vue')
const pluginModules = import.meta.glob('../plugin/**/*.vue')
export const asyncRouterHandle = (asyncRouter) => {
asyncRouter.forEach(item => {
if (item.component && typeof item.component === 'string') {
if (item.component.split('/')[0] === 'view') {
item.component = dynamicImport(viewModules, item.component)
} else if (item.component.split('/')[0] === 'plugin') {
item.component = dynamicImport(pluginModules, item.component)
}
}
if (item.children) {
asyncRouterHandle(item.children)
}
})
}
function dynamicImport(
dynamicViewsModules,
component
) {
const keys = Object.keys(dynamicViewsModules)
const matchKeys = keys.filter((key) => {
const k = key.replace('../', '')
return k === component
})
const matchKey = matchKeys[0]
return dynamicViewsModules[matchKey]
}

6
src/utils/btnAuth.js Normal file
View File

@ -0,0 +1,6 @@
import { useRoute } from 'vue-router'
import { reactive } from 'vue'
export const useBtnAuth = () => {
const route = useRoute()
return route.meta.btns || reactive({})
}

6
src/utils/bus.js Normal file
View File

@ -0,0 +1,6 @@
// using ES6 modules
import mitt from 'mitt'
export const emitter = mitt()

View File

@ -0,0 +1,5 @@
import { emitter } from '@/utils/bus.js'
export const closeThisPage = () => {
emitter.emit('closeThisPage')
}

30
src/utils/date.js Normal file
View File

@ -0,0 +1,30 @@
// 对Date的扩展将 Date 转化为指定格式的String
// 月(M)、日(d)、小时(h)、分(m)、秒(s)、季度(q) 可以用 1-2 个占位符,
// 年(y)可以用 1-4 个占位符,毫秒(S)只能用 1 个占位符(是 1-3 位的数字)
// (new Date()).Format("yyyy-MM-dd hh:mm:ss.S") ==> 2006-07-02 08:09:04.423
// (new Date()).Format("yyyy-M-d h:m:s.S") ==> 2006-7-2 8:9:4.18
// eslint-disable-next-line no-extend-native
Date.prototype.Format = function(fmt) {
var o = {
'M+': this.getMonth() + 1, // 月份
'd+': this.getDate(), // 日
'h+': this.getHours(), // 小时
'm+': this.getMinutes(), // 分
's+': this.getSeconds(), // 秒
'q+': Math.floor((this.getMonth() + 3) / 3), // 季度
'S': this.getMilliseconds() // 毫秒
}
if (/(y+)/.test(fmt)) { fmt = fmt.replace(RegExp.$1, (this.getFullYear() + '').substr(4 - RegExp.$1.length)) }
for (var k in o) {
if (new RegExp('(' + k + ')').test(fmt)) { fmt = fmt.replace(RegExp.$1, (RegExp.$1.length === 1) ? (o[k]) : (('00' + o[k]).substr(('' + o[k]).length))) }
}
return fmt
}
export function formatTimeToStr(times, pattern) {
var d = new Date(times).Format('yyyy-MM-dd hh:mm:ss')
if (pattern) {
d = new Date(times).Format(pattern)
}
return d.toLocaleString()
}

19
src/utils/dictionary.js Normal file
View File

@ -0,0 +1,19 @@
import { useDictionaryStore } from '@/pinia/modules/dictionary'
// 获取字典方法 使用示例 getDict('sex').then(res) 或者 async函数下 const res = await getDict('sex')
export const getDict = async(type) => {
const dictionaryStore = useDictionaryStore()
await dictionaryStore.getDictionary(type)
return dictionaryStore.dictionaryMap[type]
}
// 字典文字展示方法
export const showDictLabel = (dict, code) => {
if (!dict) {
return ''
}
const dictMap = {}
dict.forEach(item => {
dictMap[item.value] = item.label
})
return dictMap[code]
}

3
src/utils/doc.js Normal file
View File

@ -0,0 +1,3 @@
export const toDoc = (url) => {
window.open(url, '_blank')
}

19
src/utils/downloadImg.js Normal file
View File

@ -0,0 +1,19 @@
export const downloadImage = (imgsrc, name) => { // 下载图片地址和图片名
var image = new Image()
image.setAttribute('crossOrigin', 'anonymous')
image.onload = function() {
var canvas = document.createElement('canvas')
canvas.width = image.width
canvas.height = image.height
var context = canvas.getContext('2d')
context.drawImage(image, 0, 0, image.width, image.height)
var url = canvas.toDataURL('image/png') // 得到图片的base64编码数据
var a = document.createElement('a') // 生成一个a元素
var event = new MouseEvent('click') // 创建一个单击事件
a.download = name || 'photo' // 设置图片名称
a.href = url // 将生成的URL设置为a.href属性
a.dispatchEvent(event) // 触发a的单击事件
}
image.src = imgsrc
}

View File

@ -0,0 +1,13 @@
export const fmtTitle = (title, now) => {
const reg = /\$\{(.+?)\}/
const reg_g = /\$\{(.+?)\}/g
const result = title.match(reg_g)
if (result) {
result.forEach((item) => {
const key = item.match(reg)[1]
const value = now.params[key] || now.query[key]
title = title.replace(item, value)
})
}
return title
}

53
src/utils/format.js Normal file
View File

@ -0,0 +1,53 @@
import { formatTimeToStr } from '@/utils/date'
import { getDict } from '@/utils/dictionary'
export const formatBoolean = (bool) => {
if (bool !== null) {
return bool ? '是' : '否'
} else {
return ''
}
}
export const formatDate = (time) => {
if (time !== null && time !== '') {
var date = new Date(time)
return formatTimeToStr(date, 'yyyy-MM-dd hh:mm:ss')
} else {
return ''
}
}
export const filterDict = (value, options) => {
const rowLabel = options && options.filter(item => item.value === value)
return rowLabel && rowLabel[0] && rowLabel[0].label
}
export const getDictFunc = async(type) => {
const dicts = await getDict(type)
return dicts
}
const path = import.meta.env.VITE_BASE_PATH + ':' + import.meta.env.VITE_SERVER_PORT + '/'
export const ReturnArrImg = (arr) => {
const imgArr = []
if (arr instanceof Array) { // 如果是数组类型
for (const arrKey in arr) {
if (arr[arrKey].slice(0, 4) !== 'http') {
imgArr.push(path + arr[arrKey])
} else {
imgArr.push(arr[arrKey])
}
}
} else { // 如果不是数组类型
if (arr.slice(0, 4) !== 'http') {
imgArr.push(path + arr)
} else {
imgArr.push(arr)
}
}
return imgArr
}
export const onDownloadFile = (url) => {
window.open(path + url)
}

101
src/utils/image.js Normal file
View File

@ -0,0 +1,101 @@
export default class ImageCompress {
constructor(file, fileSize, maxWH = 1920) {
this.file = file
this.fileSize = fileSize
this.maxWH = maxWH // 最大长宽
}
compress() {
// 压缩
const fileType = this.file.type
const fileSize = this.file.size / 1024
return new Promise(resolve => {
const reader = new FileReader()
reader.readAsDataURL(this.file)
reader.onload = () => {
const canvas = document.createElement('canvas')
const img = document.createElement('img')
img.src = reader.result
img.onload = () => {
const ctx = canvas.getContext('2d')
const _dWH = this.dWH(img.width, img.height, this.maxWH)
canvas.width = _dWH.width
canvas.height = _dWH.height
// 清空后, 重写画布
ctx.clearRect(0, 0, canvas.width, canvas.height)
ctx.drawImage(img, 0, 0, canvas.width, canvas.height)
const newImgData = canvas.toDataURL(fileType, 0.90)
// 压缩宽高后的图像大小
const newImgSize = this.fileSizeKB(newImgData)
if (newImgSize > this.fileSize) {
console.log('图片尺寸太大!' + fileSize + ' >> ' + newImgSize)
}
const blob = this.dataURLtoBlob(newImgData, fileType)
const nfile = new File([blob], this.file.name)
resolve(nfile)
}
}
})
}
/**
* 长宽等比缩小
* 图像的一边(长或宽)为最大目标值
*/
dWH(srcW, srcH, dMax) {
const defaults = {
width: srcW,
height: srcH
}
if (Math.max(srcW, srcH) > dMax) {
if (srcW > srcH) {
defaults.width = dMax
defaults.height = Math.round(srcH * (dMax / srcW))
return defaults
} else {
defaults.height = dMax
defaults.width = Math.round(srcW * (dMax / srcH))
return defaults
}
} else {
return defaults
}
}
fileSizeKB(dataURL) {
let sizeKB = 0
sizeKB = Math.round((dataURL.split(',')[1].length * 3 / 4) / 1024)
return sizeKB
}
/**
* 转为Blob
*/
dataURLtoBlob(dataURL, fileType) {
const byteString = atob(dataURL.split(',')[1])
let mimeString = dataURL.split(',')[0].split(':')[1].split(';')[0]
const ab = new ArrayBuffer(byteString.length)
const ia = new Uint8Array(ab)
for (let i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i)
}
if (fileType) {
mimeString = fileType
}
return new Blob([ab], { type: mimeString, lastModifiedDate: new Date() })
}
}
const path = import.meta.env.VITE_FILE_API + '/'
export const getUrl = (url) => url && url.slice(0, 4) !== 'http' ? path + url : url
export const isVideoExt = (url) => url.endsWith('.mp4') || url.endsWith('.mov') || url.endsWith('.webm') || url.endsWith('.ogg');
export const isVideoMime = (type) => type == 'video/mp4' || type == 'video/webm' || type == 'video/ogg';
export const isImageMime = (type) => type == 'image/jpeg' || type == 'image/png' || type == 'image/webp' || type == 'image/svg+xml';

9
src/utils/page.js Normal file
View File

@ -0,0 +1,9 @@
import { fmtTitle } from '@/utils/fmtRouterTitle'
import config from '@/core/config'
export default function getPageTitle(pageTitle, route) {
if (pageTitle) {
const title = fmtTitle(pageTitle, route)
return `${title} - ${config.appName}`
}
return `${config.appName}`
}

View File

@ -0,0 +1,36 @@
export const initDom = () => {
if (import.meta.env.MODE === 'development') {
document.onmousedown = function(e) {
if (e.shiftKey && e.button === 0) {
e.preventDefault()
sendRequestToOpenFileInEditor(getFilePath(e))
}
}
}
}
const getFilePath = (e) => {
let element = e
if (e.target) {
element = e.target
}
if (!element || !element.getAttribute) return null
if (element.getAttribute('code-location')) {
return element.getAttribute('code-location')
}
return getFilePath(element.parentNode)
}
const sendRequestToOpenFileInEditor = (filePath) => {
const protocol = window.location.protocol
? window.location.protocol
: 'http:'
const hostname = window.location.hostname
? window.location.hostname
: 'localhost'
const port = window.location.port ? window.location.port : '80'
fetch(`${protocol}//${hostname}:${port}/gvaPositionCode?filePath=${filePath}`)
.catch((error) => {
console.log(error)
})
}

141
src/utils/request.js Normal file
View File

@ -0,0 +1,141 @@
import axios from 'axios' // 引入axios
import { ElMessage, ElMessageBox } from 'element-plus'
import { useUserStore } from '@/pinia/modules/user'
import { emitter } from '@/utils/bus.js'
import router from '@/router/index'
const service = axios.create({
baseURL: import.meta.env.VITE_BASE_API,
timeout: 99999
})
let acitveAxios = 0
let timer
const showLoading = () => {
acitveAxios++
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(() => {
if (acitveAxios > 0) {
emitter.emit('showLoading')
}
}, 400)
}
const closeLoading = () => {
acitveAxios--
if (acitveAxios <= 0) {
clearTimeout(timer)
emitter.emit('closeLoading')
}
}
// http request 拦截器
service.interceptors.request.use(
config => {
if (!config.donNotShowLoading) {
showLoading()
}
const userStore = useUserStore()
config.headers = {
'Content-Type': 'application/json',
'x-token': userStore.token,
'x-user-id': userStore.userInfo.ID,
...config.headers
}
return config
},
error => {
if (!error.config.donNotShowLoading) {
closeLoading()
}
ElMessage({
showClose: true,
message: error,
type: 'error'
})
return error
}
)
// http response 拦截器
service.interceptors.response.use(
response => {
const userStore = useUserStore()
if (!response.config.donNotShowLoading) {
closeLoading()
}
if (response.headers['new-token']) {
userStore.setToken(response.headers['new-token'])
}
if (response.data.code === 0 || response.headers.success === 'true') {
if (response.headers.msg) {
response.data.msg = decodeURI(response.headers.msg)
}
return response.data
} else {
ElMessage({
showClose: true,
message: response.data.msg || decodeURI(response.headers.msg),
type: 'error'
})
if (response.data.data && response.data.data.reload) {
userStore.token = ''
localStorage.clear()
router.push({ name: 'Login', replace: true })
}
return response.data.msg ? response.data : response
}
},
error => {
if (!error.config.donNotShowLoading) {
closeLoading()
}
if (!error.response) {
ElMessageBox.confirm(`
<p>检测到请求错误</p>
<p>${error}</p>
`, '请求报错', {
dangerouslyUseHTMLString: true,
distinguishCancelAndClose: true,
confirmButtonText: '稍后重试',
cancelButtonText: '取消'
})
return
}
switch (error.response.status) {
case 500:
ElMessageBox.confirm(`
<p>检测到接口错误${error}</p>
<p>错误码<span style="color:red"> 500 </span>panic使</p>
`, '接口报错', {
dangerouslyUseHTMLString: true,
distinguishCancelAndClose: true,
confirmButtonText: '清理缓存',
cancelButtonText: '取消'
})
.then(() => {
const userStore = useUserStore()
userStore.token = ''
localStorage.clear()
router.push({ name: 'Login', replace: true })
})
break
case 404:
ElMessageBox.confirm(`
<p>检测到接口错误${error}</p>
<p>错误码<span style="color:red"> 404 </span>api--</p>
`, '接口报错', {
dangerouslyUseHTMLString: true,
distinguishCancelAndClose: true,
confirmButtonText: '我知道了',
cancelButtonText: '取消'
})
break
}
return error
}
)
export default service

29
src/utils/stringFun.js Normal file
View File

@ -0,0 +1,29 @@
/* eslint-disable */
export const toUpperCase = (str) => {
if (str[0]) {
return str.replace(str[0], str[0].toUpperCase())
} else {
return ''
}
}
export const toLowerCase = (str) => {
if (str[0]) {
return str.replace(str[0], str[0].toLowerCase())
} else {
return ''
}
}
// 驼峰转换下划线
export const toSQLLine = (str) => {
if (str === 'ID') return 'ID'
return str.replace(/([A-Z])/g, "_$1").toLowerCase();
}
// 下划线转换驼峰
export const toHump = (name) => {
return name.replace(/\_(\w)/g, function(all, letter) {
return letter.toUpperCase();
});
}

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