master
Hoshi 2024-03-14 11:30:18 +08:00
commit ad9a90e252
140 changed files with 14282 additions and 0 deletions

7
.editorconfig Normal file
View File

@ -0,0 +1,7 @@
[*.{js,jsx,ts,tsx,vue}]
indent_style = space
indent_size = 2
end_of_line = lf
trim_trailing_whitespace = true
insert_final_newline = true
max_line_length = 100

19
.eslintrc.js Normal file
View File

@ -0,0 +1,19 @@
/* eslint-env node */
require('@rushstack/eslint-patch/modern-module-resolution')
module.exports = {
root: true,
extends: ['plugin:vue/vue3-essential', 'eslint:recommended', '@vue/eslint-config-typescript/recommended', '@vue/eslint-config-prettier'],
env: {
'vue/setup-compiler-macros': true
},
rules: {
'@typescript-eslint/no-explicit-any': 0,
'vue/multi-word-component-names': [
'error',
{
ignores: ['index', 'home', '404']
}
]
}
}

25
.gitignore vendored Normal file
View File

@ -0,0 +1,25 @@
.DS_Store
node_modules
/main-web
main-web.tar.gz
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
public/index.html
public/static/config

8
.prettierrc Normal file
View File

@ -0,0 +1,8 @@
{
"printWidth": 300,
"tabWidth": 2,
"singleQuote": true,
"semi": false,
"endOfLine": "auto",
"trailingComma": "none"
}

2
Jenkinsfile vendored Normal file
View File

@ -0,0 +1,2 @@
@Library('jgpl') _
webPipeline()

24
README.md Normal file
View File

@ -0,0 +1,24 @@
# vue3.0-tpl
## Project setup
```
yarn install
```
### Compiles and hot-reloads for development
```
yarn serve
```
### Compiles and minifies for production
```
yarn build
```
### Lints and fixes files
```
yarn lint
```
### Customize configuration
See [Configuration Reference](https://cli.vuejs.org/config/).

21
bak/app.json Normal file
View File

@ -0,0 +1,21 @@
{
"deployAddress": "https://10.20.12.56:60003/",
"configs": [
{
"name": "cmp-web"
},
{
"name": "cos-web",
"activeRule": ["/cos", "/soa", "/screen", "/personal"]
},
{
"name": "cms-web"
},
{
"name": "sms-web"
},
{
"name": "cop-web"
}
]
}

212
bak/nginx.conf Normal file
View File

@ -0,0 +1,212 @@
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;
# Load dynamic modules. See /usr/share/nginx/README.dynamic.
include /usr/share/nginx/modules/*.conf;
events {
worker_connections 1024;
}
http {
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
# access_log /var/log/nginx/access.log main;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
add_header X-Frame-Options SAMEORIGIN always;
client_max_body_size 20m;
include /etc/nginx/mime.types;
default_type application/octet-stream;
# Load modular configuration files from the /etc/nginx/conf.d directory.
# See http://nginx.org/en/docs/ngx_core_module.html#include
# for more information.
# include /etc/nginx/conf.d/*.conf;
upstream gateways {
least_conn;
server cmp-10-20-12-56:8000;
}
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
gzip on;
gzip_min_length 1k;
gzip_buffers 4 16k;
#gzip_http_version 1.0;
gzip_comp_level 7;
gzip_types text/plain application/javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png;
gzip_vary off;
gzip_disable "MSIE [1-6]\.";
server {
listen 60003 ssl;
server_name localhost;
ssl_certificate /etc/nginx/bocloud.crt;
ssl_certificate_key /etc/nginx/bocloud.key;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!MD5;
keepalive_timeout 60;
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods *;
add_header Access-Control-Allow-Headers *;
location / {
root /opt/cmp/consoles/cos-web/;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
location ~^/(cmp-web|cms-web|scr-web|sms-web|cop-web|web-common-resource) {
root /opt/cmp/consoles/;
index index.html index.htm;
try_files $uri $uri/ /$1/index.html;
}
location /api {
proxy_pass http://gateways;
proxy_connect_timeout 20;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_pass_header X-XSRF-TOKEN;
proxy_set_header Host $host;
proxy_set_header X-Real_IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr:$remote_port;
proxy_set_header Connection "upgrade";
}
location /attachment {
root /home/cmp/;
}
location /config-files {
root /home/cmp/;
}
}
server {
listen 60006 ssl;
server_name localhost;
ssl_certificate /etc/nginx/bocloud.crt;
ssl_certificate_key /etc/nginx/bocloud.key;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!MD5;
keepalive_timeout 60;
location / {
root /opt/cmp/consoles/main-web/;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
location ~^/(cmp-web|cms-web|scr-web|sms-web|cop-web)\/(fonts|static\/img) {
proxy_pass https://127.0.0.1:60003;
}
location /web-common-resource {
proxy_pass https://127.0.0.1:60003;
}
location /api/terminal {
proxy_pass https://$arg_real_host;
proxy_connect_timeout 60;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_pass_header X-XSRF-TOKEN;
proxy_set_header Host $host;
proxy_set_header X-Real_IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr:$remote_port;
proxy_set_header Connection "upgrade";
rewrite "^/api/terminal/(.*)$" /$1 break;
}
location /api {
proxy_pass http://gateways;
proxy_connect_timeout 20;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_pass_header X-XSRF-TOKEN;
proxy_set_header Host $host;
proxy_set_header X-Real_IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr:$remote_port;
proxy_set_header Connection "upgrade";
}
location /attachment {
root /home/cmp/;
}
location /config-files {
root /home/cmp/;
}
}
server {
listen 60008 ssl;
server_name localhost;
root /opt/cmp/consoles/csc-web/;
ssl_certificate /etc/nginx/bocloud.crt;
ssl_certificate_key /etc/nginx/bocloud.key;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!MD5;
keepalive_timeout 60;
location / {
root /opt/cmp/consoles/csc-web/;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
location /api {
proxy_pass http://gateways;
proxy_connect_timeout 60;
proxy_http_version 1.1;
proxy_pass_header X-XSRF-TOKEN;
proxy_redirect http:// https://;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header Host $host;
proxy_set_header X-Real_IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr:$remote_port;
proxy_set_header Connection "upgrade";
}
location /web-common-resource {
proxy_pass https://127.0.0.1:60003;
}
location /ticket {
proxy_pass https://$arg_host;
proxy_connect_timeout 60;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_pass_header X-XSRF-TOKEN;
proxy_set_header Host $host;
proxy_set_header X-Real_IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr:$remote_port;
proxy_set_header Connection "upgrade";
}
location /attachment {
root /home/cmp/;
}
location /config-files {
root /home/cmp/;
}
}
}
stream {
upstream mon {
least_conn;
server cmp-10-20-12-56:162;
}
server {
listen 1162 udp reuseport;
proxy_pass mon;
proxy_timeout 1m;
proxy_connect_timeout 60s;
}
}

36
components.d.ts vendored Normal file
View File

@ -0,0 +1,36 @@
// generated by unplugin-vue-components
// We suggest you to commit this file into source control
// Read more: https://github.com/vuejs/vue-next/pull/3399
declare module 'vue' {
export interface GlobalComponents {
ABadge: typeof import('ant-design-vue/es')['Badge']
AButton: typeof import('ant-design-vue/es')['Button']
ACard: typeof import('ant-design-vue/es')['Card']
ACol: typeof import('ant-design-vue/es')['Col']
ADropdown: typeof import('ant-design-vue/es')['Dropdown']
AEmpty: typeof import('ant-design-vue/es')['Empty']
AForm: typeof import('ant-design-vue/es')['Form']
AFormItem: typeof import('ant-design-vue/es')['FormItem']
AInput: typeof import('ant-design-vue/es')['Input']
AInputPassword: typeof import('ant-design-vue/es')['InputPassword']
ALayout: typeof import('ant-design-vue/es')['Layout']
ALayoutContent: typeof import('ant-design-vue/es')['LayoutContent']
ALayoutHeader: typeof import('ant-design-vue/es')['LayoutHeader']
ALayoutSider: typeof import('ant-design-vue/es')['LayoutSider']
AMenu: typeof import('ant-design-vue/es')['Menu']
AMenuItem: typeof import('ant-design-vue/es')['MenuItem']
AModal: typeof import('ant-design-vue/es')['Modal']
ARow: typeof import('ant-design-vue/es')['Row']
ASubMenu: typeof import('ant-design-vue/es')['SubMenu']
ASwitch: typeof import('ant-design-vue/es')['Switch']
ATextarea: typeof import('ant-design-vue/es')['Textarea']
ATooltip: typeof import('ant-design-vue/es')['Tooltip']
ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
Empty: typeof import('./src/components/empty/Empty.vue')['default']
ImageCropper: typeof import('./src/components/image-cropper/index.vue')['default']
SvgIcon: typeof import('./src/components/svg-icon/SvgIcon.vue')['default']
}
}
export { }

1
env.d.ts vendored Normal file
View File

@ -0,0 +1 @@
/// <reference types="vite/client" />

29
index.html Normal file
View File

@ -0,0 +1,29 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<!-- <meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests"> -->
<title>混合云管理平台</title>
<link rel="stylesheet" href="/static/loading.css">
</head>
<body id="cmp">
<noscript>
<strong>We're sorry but doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div class="loading">
<div class="loader-inner line-scale-pulse-out-rapid">
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</div>
</div>
<div id="master-container"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

61
package.json Normal file
View File

@ -0,0 +1,61 @@
{
"name": "main-web",
"version": "5.6.0",
"private": true,
"author": "Haijun Zhang <zhanghaijun@beyondcent.com>",
"scripts": {
"serve": "vite",
"build": "vue-tsc --noEmit && vite build",
"preview": "vite preview",
"typecheck": "vue-tsc --noEmit",
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore"
},
"dependencies": {
"@ant-design/icons-vue": "^6.0.1",
"ant-design-vue": "^2.2.8",
"axios": "^0.21.1",
"clipboard": "^2.0.10",
"cmp-socket": "1.0.0",
"core-js": "^3.6.5",
"crypto-js": "^3.1.9-1",
"dayjs": "^1.10.4",
"element-plus": "^1.1.0-beta.9",
"js-cookie": "^2.2.0",
"lodash-es": "^4.17.21",
"nprogress": "^0.2.0",
"qiankun": "^2.4.6",
"qs": "^6.7.0",
"vue": "^3.2.29",
"vue-router": "^4.0.12",
"vuex": "^4.0.0-0"
},
"devDependencies": {
"@commitlint/cli": "^11.0.0",
"@commitlint/config-conventional": "^11.0.0",
"@rushstack/eslint-patch": "^1.1.0",
"@types/js-cookie": "^2.2.4",
"@types/lodash-es": "^4.17.4",
"@types/node": "^16.11.22",
"@types/nprogress": "^0.2.0",
"@types/qs": "^6.5.3",
"@vitejs/plugin-vue": "^2.2.0",
"@vitejs/plugin-vue-jsx": "^1.3.7",
"@vue/eslint-config-prettier": "^7.0.0",
"@vue/eslint-config-typescript": "^10.0.0",
"@vue/tsconfig": "^0.1.3",
"eslint": "^8.5.0",
"eslint-plugin-vue": "^8.2.0",
"husky": "^1.3.1",
"less": "^4.1.1",
"lint-staged": "^8.1.5",
"prettier": "^2.5.1",
"sass": "^1.26.5",
"svg-sprite-loader": "^6.0.2",
"typescript": "~4.5.5",
"unplugin-vue-components": "^0.17.18",
"vite": "^2.8.3",
"vite-plugin-style-import": "1.4.1",
"vite-plugin-svg-icons": "^2.0.1",
"vue-tsc": "^0.31.1"
}
}

5833
pnpm-lock.yaml Normal file

File diff suppressed because it is too large Load Diff

19
public/config/app.json Normal file
View File

@ -0,0 +1,19 @@
{
"configs": [{
"name": "cmp-web"
},
{
"name": "cos-web",
"activeRule": ["/cos", "/soa", "/screen", "/personal"]
},
{
"name": "cms-web"
},
{
"name": "sms-web"
},
{
"name": "cop-web"
}
]
}

View File

@ -0,0 +1 @@
.loading{position:absolute;top:0;left:0;text-align:center;line-height:100vh;z-index:10;height:100vh;width:100%;background:#fff}@-webkit-keyframes line-scale-pulse-out{0%{-webkit-transform:scaley(1);transform:scaley(1)}50%{-webkit-transform:scaley(0.4);transform:scaley(0.4)}100%{-webkit-transform:scaley(1);transform:scaley(1)}}@keyframes line-scale-pulse-out{0%{-webkit-transform:scaley(1);transform:scaley(1)}50%{-webkit-transform:scaley(0.4);transform:scaley(0.4)}100%{-webkit-transform:scaley(1);transform:scaley(1)}}.line-scale-pulse-out>div{background-color:#279fcf;width:4px;height:35px;border-radius:2px;margin:2px;-webkit-animation-fill-mode:both;animation-fill-mode:both;display:inline-block;-webkit-animation:line-scale-pulse-out .9s 0s infinite cubic-bezier(.85,.25,.37,.85);animation:line-scale-pulse-out .9s 0s infinite cubic-bezier(.85,.25,.37,.85)}.line-scale-pulse-out>div:nth-child(2),.line-scale-pulse-out>div:nth-child(4){-webkit-animation-delay:.2s!important;animation-delay:.2s!important}.line-scale-pulse-out>div:nth-child(1),.line-scale-pulse-out>div:nth-child(5){-webkit-animation-delay:.4s!important;animation-delay:.4s!important}@-webkit-keyframes line-scale-pulse-out-rapid{0%{-webkit-transform:scaley(1);transform:scaley(1)}80%{-webkit-transform:scaley(0.3);transform:scaley(0.3)}90%{-webkit-transform:scaley(1);transform:scaley(1)}}@keyframes line-scale-pulse-out-rapid{0%{-webkit-transform:scaley(1);transform:scaley(1)}80%{-webkit-transform:scaley(0.3);transform:scaley(0.3)}90%{-webkit-transform:scaley(1);transform:scaley(1)}}.line-scale-pulse-out-rapid>div{background-color:#279fcf;width:4px;height:35px;border-radius:2px;margin:2px;-webkit-animation-fill-mode:both;animation-fill-mode:both;display:inline-block;-webkit-animation:line-scale-pulse-out-rapid .9s 0s infinite cubic-bezier(.11,.49,.38,.78);animation:line-scale-pulse-out-rapid .9s 0s infinite cubic-bezier(.11,.49,.38,.78)}.line-scale-pulse-out-rapid>div:nth-child(2),.line-scale-pulse-out-rapid>div:nth-child(4){-webkit-animation-delay:.25s!important;animation-delay:.25s!important}.line-scale-pulse-out-rapid>div:nth-child(1),.line-scale-pulse-out-rapid>div:nth-child(5){-webkit-animation-delay:.5s!important;animation-delay:.5s!important}

45
src/App.vue Normal file
View File

@ -0,0 +1,45 @@
<template>
<config-provider :locale="zhCN">
<router-view v-if="showMainApp"></router-view>
<Home v-else-if="getToken()"></Home>
</config-provider>
</template>
<script setup lang="ts">
import { ConfigProvider } from 'ant-design-vue'
import { onMounted, computed } from 'vue'
import { useStore } from 'vuex'
import { useRoute, useRouter } from 'vue-router'
import zhCN from 'ant-design-vue/es/locale/zh_CN'
import { getToken } from 'utils/auth'
import Home from './layouts/home.vue'
const store = useStore()
store.dispatch('GetPageConfigs')
const route = useRoute()
const router = useRouter()
onMounted(() => {
// 退 hack
window.addEventListener(
'hashchange',
() => {
const currentPath = window.location.hash.slice(1)
if (route.path !== currentPath) {
router.push(currentPath)
}
},
false
)
// loading
setTimeout(() => {
const el = document.getElementsByClassName('loading')[0]
;(el?.parentElement as HTMLElement)?.removeChild(el)
}, 1000 * 2)
})
const showMainApp = computed(() => {
return ['/login', '/sso', '/lockme', '/redirect', '/404', '/license'].includes(route.path)
})
</script>
<style lang="scss">
@import './css/index.scss';
</style>

BIN
src/assets/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

View File

@ -0,0 +1,49 @@
<template>
<div class="no-result">
<img v-if="image" class="image" :src="image" alt="" />
<i v-else class="icon el-icon-tickets"></i>
<span>
<slot>{{ description }}</slot>
</span>
</div>
</template>
<script>
export default {
props: {
image: {
type: String,
},
icon: {
type: String,
default: 'el-icon-tickets',
},
description: {
type: String,
default: '暂无数据',
},
},
}
</script>
<style scoped lang="scss">
.no-result {
color: #909399;
margin: 32px 0;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
line-height: normal;
.image {
height: 80px;
}
.icon {
font-size: 40px;
margin-bottom: 10px;
}
span {
font-size: 14px;
}
}
</style>

View File

@ -0,0 +1,3 @@
import Empty from './Empty.vue'
export default Empty

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,19 @@
/**
* database64文件格式转换为2进制
*
* @param {[String]} data dataURL 的格式为 data:image/png;base64,****,逗号之前都是一些说明性的文字我们只需要逗号之后的就行了
* @param {[String]} mime [description]
* @return {[blob]} [description]
*/
export default function (data, mime) {
data = data.split(',')[1]
data = window.atob(data)
var ia = new Uint8Array(data.length)
for (var i = 0; i < data.length; i++) {
ia[i] = data.charCodeAt(i)
}
// canvas.toDataURL 返回的默认格式就是 image/png
return new Blob([ia], {
type: mime,
})
}

View File

@ -0,0 +1,42 @@
/**
* 点击波纹效果
*
* @param {[event]} e [description]
* @param {[Object]} argOpts [description]
* @return {[bollean]} [description]
*/
export default function (e, argOpts) {
const opts = Object.assign(
{
ele: e.target, // 波纹作用元素
type: 'hit', // hit点击位置扩散center中心点扩展
bgc: 'rgba(0, 0, 0, 0.15)', // 波纹颜色
},
argOpts
)
var target = opts.ele
if (target) {
const rect = target.getBoundingClientRect()
var ripple = target.querySelector('.e-ripple')
if (!ripple) {
ripple = document.createElement('span')
ripple.className = 'e-ripple'
ripple.style.height = ripple.style.width = Math.max(rect.width, rect.height) + 'px'
target.appendChild(ripple)
} else {
ripple.className = 'e-ripple'
}
switch (opts.type) {
case 'center':
ripple.style.top = rect.height / 2 - ripple.offsetHeight / 2 + 'px'
ripple.style.left = rect.width / 2 - ripple.offsetWidth / 2 + 'px'
break
default:
ripple.style.top = e.pageY - rect.top - ripple.offsetHeight / 2 - document.body.scrollTop + 'px'
ripple.style.left = e.pageX - rect.left - ripple.offsetWidth / 2 - document.body.scrollLeft + 'px'
}
ripple.style.backgroundColor = opts.bgc
ripple.className = 'e-ripple z-active'
return false
}
}

View File

@ -0,0 +1,365 @@
export default {
zh: {
hint: '点击,或拖动图片至此处',
loading: '正在上传……',
noSupported: '浏览器不支持该功能请使用IE10以上或其他现在浏览器',
success: '上传成功',
fail: '图片上传失败',
preview: '预览',
btn: {
off: '取消',
close: '关闭',
back: '上一步',
save: '保存',
},
error: {
onlyImg: '仅限图片格式',
outOfSize: '单文件大小不能超过 ',
lowestPx: '图片最低像素为(宽*高):',
},
},
'zh-tw': {
hint: '點擊,或拖動圖片至此處',
loading: '正在上傳……',
noSupported: '瀏覽器不支持該功能請使用IE10以上或其他現代瀏覽器',
success: '上傳成功',
fail: '圖片上傳失敗',
preview: '頭像預覽',
btn: {
off: '取消',
close: '關閉',
back: '上一步',
save: '保存',
},
error: {
onlyImg: '僅限圖片格式',
outOfSize: '單文件大小不能超過 ',
lowestPx: '圖片最低像素為(寬*高):',
},
},
en: {
hint: 'Click or drag the file here to upload',
loading: 'Uploading…',
noSupported: 'Browser is not supported, please use IE10+ or other browsers',
success: 'Upload success',
fail: 'Upload failed',
preview: 'Preview',
btn: {
off: 'Cancel',
close: 'Close',
back: 'Back',
save: 'Save',
},
error: {
onlyImg: 'Image only',
outOfSize: 'Image exceeds size limit: ',
lowestPx: "Image's size is too low. Expected at least: ",
},
},
ro: {
hint: 'Atinge sau trage fișierul aici',
loading: 'Se încarcă',
noSupported: 'Browser-ul tău nu suportă acest feature. Te rugăm încearcă cu alt browser.',
success: 'S-a încărcat cu succes',
fail: 'A apărut o problemă la încărcare',
preview: 'Previzualizează',
btn: {
off: 'Anulează',
close: 'Închide',
back: 'Înapoi',
save: 'Salvează',
},
error: {
onlyImg: 'Doar imagini',
outOfSize: 'Imaginea depășește limita de: ',
loewstPx: 'Imaginea este prea mică; Minim: ',
},
},
ru: {
hint: 'Нажмите, или перетащите файл в это окно',
loading: 'Загружаю……',
noSupported: 'Ваш браузер не поддерживается, пожалуйста, используйте IE10 + или другие браузеры',
success: 'Загрузка выполнена успешно',
fail: 'Ошибка загрузки',
preview: 'Предпросмотр',
btn: {
off: 'Отменить',
close: 'Закрыть',
back: 'Назад',
save: 'Сохранить',
},
error: {
onlyImg: 'Только изображения',
outOfSize: 'Изображение превышает предельный размер: ',
lowestPx: 'Минимальный размер изображения: ',
},
},
'pt-br': {
hint: 'Clique ou arraste o arquivo aqui para carregar',
loading: 'Carregando…',
noSupported: 'Browser não suportado, use o IE10+ ou outro browser',
success: 'Sucesso ao carregar imagem',
fail: 'Falha ao carregar imagem',
preview: 'Pré-visualizar',
btn: {
off: 'Cancelar',
close: 'Fechar',
back: 'Voltar',
save: 'Salvar',
},
error: {
onlyImg: 'Apenas imagens',
outOfSize: 'A imagem excede o limite de tamanho: ',
lowestPx: 'O tamanho da imagem é muito pequeno. Tamanho mínimo: ',
},
},
fr: {
hint: 'Cliquez ou glissez le fichier ici.',
loading: 'Téléchargement…',
noSupported: "Votre navigateur n'est pas supporté. Utilisez IE10 + ou un autre navigateur s'il vous plaît.",
success: 'Téléchargement réussit',
fail: 'Téléchargement echoué',
preview: 'Aperçu',
btn: {
off: 'Annuler',
close: 'Fermer',
back: 'Retour',
save: 'Enregistrer',
},
error: {
onlyImg: 'Image uniquement',
outOfSize: "L'image sélectionnée dépasse la taille maximum: ",
lowestPx: "L'image sélectionnée est trop petite. Dimensions attendues: ",
},
},
nl: {
hint: 'Klik hier of sleep een afbeelding in dit vlak',
loading: 'Uploaden…',
noSupported: 'Je browser wordt helaas niet ondersteund. Gebruik IE10+ of een andere browser.',
success: 'Upload succesvol',
fail: 'Upload mislukt',
preview: 'Voorbeeld',
btn: {
off: 'Annuleren',
close: 'Sluiten',
back: 'Terug',
save: 'Opslaan',
},
error: {
onlyImg: 'Alleen afbeeldingen',
outOfSize: 'De afbeelding is groter dan: ',
lowestPx: 'De afbeelding is te klein! Minimale afmetingen: ',
},
},
tr: {
hint: 'Tıkla veya yüklemek istediğini buraya sürükle',
loading: 'Yükleniyor…',
noSupported: 'Tarayıcı desteklenmiyor, lütfen IE10+ veya farklı tarayıcı kullanın',
success: 'Yükleme başarılı',
fail: 'Yüklemede hata oluştu',
preview: 'Önizle',
btn: {
off: 'İptal',
close: 'Kapat',
back: 'Geri',
save: 'Kaydet',
},
error: {
onlyImg: 'Sadece resim',
outOfSize: 'Resim yükleme limitini aşıyor: ',
lowestPx: 'Resmin boyutu çok küçük. En az olması gereken: ',
},
},
'es-MX': {
hint: 'Selecciona o arrastra una imagen',
loading: 'Subiendo...',
noSupported: 'Tu navegador no es soportado, por favor usa IE10+ u otros navegadores más recientes',
success: 'Subido exitosamente',
fail: 'Sucedió un error',
preview: 'Vista previa',
btn: {
off: 'Cancelar',
close: 'Cerrar',
back: 'Atrás',
save: 'Guardar',
},
error: {
onlyImg: 'Únicamente imágenes',
outOfSize: 'La imagen excede el tamaño maximo:',
lowestPx: 'La imagen es demasiado pequeña. Se espera por lo menos:',
},
},
de: {
hint: 'Klick hier oder zieh eine Datei hier rein zum Hochladen',
loading: 'Hochladen…',
noSupported: 'Browser wird nicht unterstützt, bitte verwende IE10+ oder andere Browser',
success: 'Upload erfolgreich',
fail: 'Upload fehlgeschlagen',
preview: 'Vorschau',
btn: {
off: 'Abbrechen',
close: 'Schließen',
back: 'Zurück',
save: 'Speichern',
},
error: {
onlyImg: 'Nur Bilder',
outOfSize: 'Das Bild ist zu groß: ',
lowestPx: 'Das Bild ist zu klein. Mindestens: ',
},
},
ja: {
hint: 'クリック・ドラッグしてファイルをアップロード',
loading: 'アップロード中...',
noSupported: 'このブラウザは対応されていません。IE10+かその他の主要ブラウザをお使いください。',
success: 'アップロード成功',
fail: 'アップロード失敗',
preview: 'プレビュー',
btn: {
off: 'キャンセル',
close: '閉じる',
back: '戻る',
save: '保存',
},
error: {
onlyImg: '画像のみ',
outOfSize: '画像サイズが上限を超えています。上限: ',
lowestPx: '画像が小さすぎます。最小サイズ: ',
},
},
ua: {
hint: 'Натисніть, або перетягніть файл в це вікно',
loading: 'Завантажую……',
noSupported: 'Ваш браузер не підтримується, будь ласка скористайтесь IE10 + або іншими браузерами',
success: 'Завантаження виконано успішно',
fail: 'Помилка завантаження',
preview: 'Попередній перегляд',
btn: {
off: 'Відмінити',
close: 'Закрити',
back: 'Назад',
save: 'Зберегти',
},
error: {
onlyImg: 'Тільки зображення',
outOfSize: 'Зображення перевищує граничний розмір: ',
lowestPx: 'Мінімальний розмір зображення: ',
},
},
it: {
hint: 'Clicca o trascina qui il file per caricarlo',
loading: 'Caricamento del file…',
noSupported: 'Browser non supportato, per favore usa IE10+ o un altro browser',
success: 'Caricamento completato',
fail: 'Caricamento fallito',
preview: 'Anteprima',
btn: {
off: 'Annulla',
close: 'Chiudi',
back: 'Indietro',
save: 'Salva',
},
error: {
onlyImg: 'Sono accettate solo immagini',
outOfSize: "L'immagine eccede i limiti di dimensione: ",
lowestPx: "L'immagine è troppo piccola. Il requisito minimo è: ",
},
},
ar: {
hint: 'اضغط أو اسحب الملف هنا للتحميل',
loading: 'جاري التحميل...',
noSupported: 'المتصفح غير مدعوم ، يرجى استخدام IE10 + أو متصفح أخر',
success: 'تم التحميل بنجاح',
fail: 'فشل التحميل',
preview: 'معاينه',
btn: {
off: 'إلغاء',
close: 'إغلاق',
back: 'رجوع',
save: 'حفظ',
},
error: {
onlyImg: 'صور فقط',
outOfSize: 'تتجاوز الصوره الحجم المحدد: ',
lowestPx: 'حجم الصورة صغير جدا. من المتوقع على الأقل: ',
},
},
ug: {
hint: 'مەزكۇر دائىرىنى چىكىپ رەسىم تاللاڭ ياكى رەسىمنى سۆرەپ ئەكىرىڭ',
loading: 'يوللىنىۋاتىدۇ...',
noSupported: 'تور كۆرگۈچ بۇ ئىقتىدارنى قوللىمايدۇ ، يۇقىرى نەشىردىكى تور كۆرگۈچنى ئىشلىتىڭ',
success: 'غەلبىلىك بولدى',
fail: 'مەغلۇب بولدى',
preview: 'ئۈنۈم رەسىم',
btn: {
off: 'بولدى قىلىش',
close: 'تاقاش',
back: 'ئالدىنقى قەدەم',
save: 'ساقلاش',
},
error: {
onlyImg: 'پەقەت رەسىم فورماتىنىلا قوللايدۇ',
outOfSize: 'رەسىم چوڭ - كىچىكلىكى چەكتىن ئىشىپ كەتتى',
lowestPx: 'رەسىمنىڭ ئەڭ كىچىك ئۆلچىمى :',
},
},
th: {
hint: 'คลิ๊กหรือลากรูปมาที่นี่',
loading: 'กำลังอัพโหลด…',
noSupported: 'เบราเซอร์ไม่รองรับ, กรุณาใช้ IE เวอร์ชั่น 10 ขึ้นไป หรือใช้เบราเซอร์ตัวอื่น',
success: 'อัพโหลดสำเร็จ',
fail: 'อัพโหลดล้มเหลว',
preview: 'ตัวอย่าง',
btn: {
off: 'ยกเลิก',
close: 'ปิด',
back: 'กลับ',
save: 'บันทึก',
},
error: {
onlyImg: 'ไฟล์ภาพเท่านั้น',
outOfSize: 'ไฟล์ใหญ่เกินกำหนด: ',
lowestPx: 'ไฟล์เล็กเกินไป. อย่างน้อยต้องมีขนาด: ',
},
},
mm: {
hint: 'ဖိုင်ကို ဤနေရာတွင် နှိပ်၍ (သို့) ဆွဲထည့်၍ တင်ပါ',
loading: 'တင်နေသည်…',
noSupported: 'ဤဘရောက်ဇာကို အထောက်အပံ့ မပေးပါ၊ ကျေးဇူးပြု၍ IE10+ သို့မဟုတ် အခြား ဘရောက်ဇာ ကို အသုံးပြုပါ',
success: 'ဖိုင်တင်နေမှု မပြီးမြောက်ပါ',
fail: 'ဖိုင်တင်နေမှု မအောင်မြင်ပါ',
preview: 'အစမ်းကြည့်',
btn: {
off: 'မလုပ်တော့ပါ',
close: 'ပိတ်မည်',
back: 'နောက်သို့',
save: 'သိမ်းမည်',
},
error: {
onlyImg: 'ဓာတ်ပုံ သီးသန့်သာ',
outOfSize: 'ဓာတ်ပုံဆိုဒ် ကြီးလွန်းသည် ။ အများဆုံး ဆိုဒ် : ',
lowestPx: 'ဓာတ်ပုံဆိုဒ် သေးလွန်းသည်။ အနည်းဆုံး ဆိုဒ် : ',
},
},
se: {
hint: 'Klicka eller dra en fil hit för att ladda upp den',
loading: 'Laddar upp…',
noSupported: 'Din webbläsare stöds inte, vänligen använd IE10+ eller andra webbläsare',
success: 'Uppladdning lyckades',
fail: 'Uppladdning misslyckades',
preview: 'Förhandsgranska',
btn: {
off: 'Avbryt',
close: 'Stäng',
back: 'Tillbaka',
save: 'Spara',
},
error: {
onlyImg: 'Endast bilder',
outOfSize: 'Bilden är större än max-gränsen: ',
lowestPx: 'Bilden är för liten. Minimum är: ',
},
},
}

View File

@ -0,0 +1,7 @@
export default {
jpg: 'image/jpeg',
png: 'image/png',
gif: 'image/gif',
svg: 'image/svg+xml',
psd: 'image/photoshop',
}

12
src/components/index.ts Normal file
View File

@ -0,0 +1,12 @@
import Empty from './empty/Empty.vue'
import SvgIcon from './svg-icon/SvgIcon.vue'
export const components: any = {
Empty,
SvgIcon
}
export default function registerComponent(app: any) {
Object.keys(components).forEach((key) => {
app.component(key, components[key])
})
}

View File

@ -0,0 +1,35 @@
<template>
<svg class="icon svg-icon" aria-hidden="true" v-if="iconName.includes('svg')">
<use :xlink:href="icon" />
</svg>
<i v-else class="icon" :class="iconName"></i>
</template>
<script lang="ts">
import { computed, defineComponent } from 'vue'
export default defineComponent({
props: {
iconName: {
type: String,
required: true,
},
},
setup(props) {
const icon = computed(() => `#icon-${props.iconName}`)
return {
icon,
}
},
})
</script>
<style scoped>
.svg-icon {
width: 1em;
height: 1em;
vertical-align: -0.15em;
fill: currentColor;
overflow: hidden;
}
</style>

View File

@ -0,0 +1,3 @@
import SvgIcon from './SvgIcon.vue'
export default SvgIcon

25
src/config.ts Normal file
View File

@ -0,0 +1,25 @@
/**
*
*/
// 是否启用权限本地缓存
export const enablePermissionStorage = false
// 本地缓存的菜单key值
export const menuKey = 'cmcMenuData'
// 是否启用用户本地缓存
export const enableUserStorage = false
// 本地缓存的用户key值
export const userKey = 'cmcUserData'
// 本地存储的cookie kye值
export const tokenKey = 'CMC_TOKEN'
// 最大缓存组件实例数
export const cacheViewMax = 15
export const formSetting = {
labelCol: {
xs: { span: 24 },
sm: { span: 6 }
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 18 }
}
}

19
src/core/actions.ts Normal file
View File

@ -0,0 +1,19 @@
import { initGlobalState } from 'qiankun'
import type { MicroAppStateActions } from 'qiankun'
import store from '@/store'
const initialState = {
name: 'main-web',
onmessage: (data: any) => console.log(data),
userData: null,
permissions: null
}
const actions: MicroAppStateActions = initGlobalState(initialState)
actions.onGlobalStateChange((state, prev) => {
// state: 变更后的状态; prev 变更前的状态
// console.log('from:', state.name, 'current:', state, 'pre:', prev)
store.commit('SET_ONMESSAGE', state.onmessage)
})
export default actions

35
src/core/config.ts Normal file
View File

@ -0,0 +1,35 @@
import mainRouter from '@/router/index'
import axios from 'axios'
export type IAppConfig = {
name: string
entry?: string
activeRule?: string | string[]
}
export async function getMicroApp() {
const { protocol, hostname } = location
const prefix = `${protocol}//${hostname}:60003/`
const {
data: { deployAddress = prefix, configs }
}: {
data: {
configs: IAppConfig[]
deployAddress: string
}
} = await axios.get('/config/app.json')
// 应用入口默认为60003端口加应用名称匹配规则为数组时是根路径(cos)
return configs.map((item: IAppConfig) => {
const { name, activeRule = `/${name}`, entry = `${deployAddress}${Array.isArray(activeRule) ? '' : `${name}/`}` } = item
return {
name,
entry,
container: '#subapp-viewport',
activeRule,
props: {
appPath: entry,
mainRouter
}
}
})
}

50
src/core/register.ts Normal file
View File

@ -0,0 +1,50 @@
import { registerMicroApps, start, addGlobalUncaughtErrorHandler } from 'qiankun'
import { message } from 'ant-design-vue'
import NProgress from 'nprogress'
import store from '@/store'
import 'nprogress/nprogress.css'
import './actions'
import { getMicroApp } from './config'
const startApp = async () => {
const apps = await getMicroApp()
registerMicroApps(apps, {
beforeLoad: [
() => {
NProgress.start()
store.commit('SET_APP_LOADING', true)
// console.log('[LifeCycle] before load %c%s', 'color: green;', app.name)
return Promise.resolve()
}
],
afterMount: [
() => {
NProgress.done()
store.commit('SET_APP_LOADING', false)
// console.log('[LifeCycle] after mount %c%s', 'color: green;', app.name)
return Promise.resolve()
}
],
afterUnmount: [
() => {
// console.log('[LifeCycle] after unmount %c%s', 'color: green;', app.name)
return Promise.resolve()
}
]
})
start({
prefetch: 'all'
})
}
/**
*
*/
addGlobalUncaughtErrorHandler((event: Event | string) => {
console.error(event)
const { message: msg } = event as any
// 加载失败时提示
if (msg && msg.includes('died in status LOADING_SOURCE_CODE')) {
message.error('微应用加载失败,请检查应用是否可运行')
}
})
export default startApp

22
src/css/animate.scss vendored Normal file
View File

@ -0,0 +1,22 @@
@keyframes swing {
20% {
transform: rotate3d(0, 0, 1, 15deg);
}
40% {
transform: rotate3d(0, 0, 1, -10deg);
}
60% {
transform: rotate3d(0, 0, 1, 5deg);
}
80% {
transform: rotate3d(0, 0, 1, -5deg);
}
to {
transform: rotate3d(0, 0, 1, 0deg);
}
}
.animated {
animation-duration: 0.7s;
animation-fill-mode: both;
}

28
src/css/antd.scss Normal file
View File

@ -0,0 +1,28 @@
*,
*::before,
*::after {
box-sizing: content-box;
}
.ant-modal-header {
.ant-modal-title,
.ant-modal-close {
color: #010033;
font-size: 14px;
font-weight: bold;
}
}
.ant-notification-notice-message {
word-break: break-all;
}
// .ant-modal-close {
// color: #fff;
// &:hover {
// color: #fff;
// }
// }
// .ant-modal-footer {
// background: #f0f3fa;
// }

4
src/css/common-var.scss Normal file
View File

@ -0,0 +1,4 @@
/* Color
-------------------------- */
$--color-primary: #1E54D5;

509
src/css/common.scss Normal file
View File

@ -0,0 +1,509 @@
html,
body,
.main-app,
#master-container {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
font-size: 14px;
overflow-y: hidden !important;
font-family: Microsoft YaHei, Hiragino Sans GB;
}
a {
text-decoration: none;
}
.app-wrapper {
position: relative;
width: 100%;
height: 100%;
}
.iconfont {
font-size: 13px;
}
/*边距共有样式*/
.text-center {
text-align: center;
}
.pull-right {
float: right;
}
.pull-left {
float: left;
}
.p-none {
padding: 0 !important;
}
.m-xxs {
margin: 2px 4px;
}
.m-xs {
margin: 5px;
}
.m-sm {
margin: 10px;
}
.m {
margin: 15px;
}
.m-md {
margin: 20px;
}
.m-lg {
margin: 30px;
}
.m-xl {
margin: 50px;
}
.m-n {
margin: 0 !important;
}
.m-l-none {
margin-left: 0 !important;
}
.m-l-xs {
margin-left: 5px;
}
.m-l-sm {
margin-left: 10px !important;
}
.m-l {
margin-left: 15px;
}
.m-l-md {
margin-left: 20px;
}
.m-l-lg {
margin-left: 30px;
}
.m-l-xl {
margin-left: 40px;
}
.m-l-xxl {
margin-left: 50px;
}
.m-l-n-xxs {
margin-left: -1px;
}
.m-l-n-xs {
margin-left: -5px;
}
.m-l-n-sm {
margin-left: -10px;
}
.m-l-n {
margin-left: -15px;
}
.m-l-n-md {
margin-left: -20px;
}
.m-l-n-lg {
margin-left: -30px;
}
.m-l-n-xl {
margin-left: -40px;
}
.m-l-n-xxl {
margin-left: -50px;
}
.m-t-none {
margin-top: 0 !important;
}
.m-t-xxs {
margin-top: 1px;
}
.m-t-xs {
margin-top: 5px !important;
}
.m-t-sm {
margin-top: 10px !important;
}
.m-t {
margin-top: 15px;
}
.m-t-md {
margin-top: 20px;
}
.m-t-lg {
margin-top: 30px;
}
.m-t-xl {
margin-top: 40px;
}
.m-t-xxl {
margin-top: 50px;
}
.m-t-n-xxs {
margin-top: -1px;
}
.m-t-n-xs {
margin-top: -5px;
}
.m-t-n-7 {
margin-top: -7px;
}
.m-t-n-sm {
margin-top: -10px;
}
.m-t-n {
margin-top: -15px;
}
.m-t-n-md {
margin-top: -20px;
}
.m-t-n-lg {
margin-top: -30px;
}
.m-t-n-xl {
margin-top: -40px;
}
.m-t-n-xxl {
margin-top: -50px;
}
.m-t-n-xxxl {
margin-top: -60px;
}
.m-r-none {
margin-right: 0 !important;
}
.m-r-xxs {
margin-right: 1px;
}
.m-r-xs {
margin-right: 5px;
}
.m-r-sm {
margin-right: 10px !important;
}
.m-r {
margin-right: 15px !important;
}
.m-r-md {
margin-right: 20px;
}
.m-r-lg {
margin-right: 30px;
}
.m-r-xl {
margin-right: 40px;
}
.m-r-xxl {
margin-right: 50px;
}
.m-r-n-xxs {
margin-right: -1px;
}
.m-r-n-xs {
margin-right: -5px;
}
.m-r-n-sm {
margin-right: -10px;
}
.m-r-n {
margin-right: -15px;
}
.m-r-n-md {
margin-right: -20px;
}
.m-r-n-lg {
margin-right: -30px;
}
.m-r-n-xl {
margin-right: -40px;
}
.m-r-n-xxl {
margin-right: -50px;
}
.m-b-none {
margin-bottom: 0 !important;
}
.m-b-xxs {
margin-bottom: 1px;
}
.m-b-xs {
margin-bottom: 5px;
}
.m-b-sm {
margin-bottom: 10px !important;
}
.m-b {
margin-bottom: 15px !important;
}
.m-b-md {
margin-bottom: 20px;
}
.m-b-lg {
margin-bottom: 30px;
}
.m-b-xl {
margin-bottom: 40px;
}
.m-b-xxl {
margin-bottom: 50px;
}
.m-b-n-xxs {
margin-bottom: -1px;
}
.m-b-n-xs {
margin-bottom: -5px;
}
.m-b-n-sm {
margin-bottom: -10px;
}
.m-b-n {
margin-bottom: -15px;
}
.m-b-n-md {
margin-bottom: -20px;
}
.m-b-n-lg {
margin-bottom: -30px;
}
.m-b-n-xl {
margin-bottom: -40px;
}
.m-b-n-xxl {
margin-bottom: -50px;
}
/*颜色共有样式*/
.btn-info {
color: #ffffff !important;
background-color: #497edf;
border-color: #497edf;
}
.btn-info:hover,
.btn-info:focus,
.btn-info:active,
.btn-info.active,
.open .dropdown-toggle.btn-info {
color: #ffffff !important;
background-color: #2059c1;
border-color: #2059c1;
}
.btn-success {
color: #ffffff !important;
background-color: #27c24c;
border-color: #27c24c;
}
.btn-success:hover,
.btn-success:focus,
.btn-success:active,
.btn-success.active,
.open .dropdown-toggle.btn-success {
color: #ffffff !important;
background-color: #23ad44;
border-color: #20a03f;
}
.btn-danger {
color: #ffffff !important;
background-color: #f05050;
border-color: #f05050;
}
.btn-danger:hover,
.btn-danger:focus,
.btn-danger:active,
.btn-danger.active,
.open .dropdown-toggle.btn-danger {
color: #ffffff !important;
background-color: #ee3939;
border-color: #ed2a2a;
}
.text-info {
color: #00aeef !important;
}
.text-success {
color: #27c24c !important;
}
.text-warning {
color: #fad733 !important;
}
.text-danger {
color: #f05050;
}
.text-white {
color: #FFF !important;
}
.text-left {
text-align: left !important;
}
.text-center {
text-align: center !important;
}
.text-right {
text-align: right !important;
}
/*宽度样式*/
.w-xxs {
width: 60px;
}
.w-xs {
width: 90px;
}
.w-110 {
width: 110px;
}
.w-ss {
width: 120px;
}
.w-sm {
width: 150px;
}
.w {
width: 200px;
}
.w-md {
width: 240px;
}
.w-lg {
width: 280px !important;
}
.w-xl {
width: 320px;
}
.w-xxl {
width: 360px;
}
.w-full {
width: 100% !important;
}
.w-auto {
width: auto;
}
.upload-file-btn {
position: relative;
cursor: pointer;
}
.upload-file-btn input[type=file] {
width: 100%;
height: 100%;
font-size: 300px;
opacity: 0;
filter: alpha(opacity=0);
cursor: pointer;
left: 0;
right: 0;
bottom: 0;
top: 0;
position: absolute;
padding: 0px;
margin: 0px;
overflow: hidden;
}

135
src/css/element-ui.scss Normal file
View File

@ -0,0 +1,135 @@
.el-select {
width: 100%;
}
.el-notification__content {
word-break: break-word;
}
.el-cascader {
width: 100%;
}
.el-form-item {
margin-bottom: 15px;
}
.el-button--mini,
.el-button--mini.is-round {
padding: 4px 15px !important;
}
.el-form-item--mini.el-form-item,
.el-form-item--small.el-form-item {
margin-bottom: 18px;
}
.el-table .cell {
white-space: nowrap;
}
.el-card {
box-shadow: none;
border-radius: 0 !important;
}
.el-tabs--border-card {
-webkit-box-shadow: none !important;
box-shadow: none !important;
}
.el-card__header {
padding: 10px 20px !important;
}
.el-tree--highlight-current .el-tree-node.is-current>.el-tree-node__content {
background-color: rgb(236 245 255) !important;
color: rgb(64 158 255);
}
.el-dialog__headerbtn .el-dialog__close {
color: #fff !important;
}
.el-breadcrumb__inner,
.el-breadcrumb__inner a {
-webkit-transition: color .2s cubic-bezier(.645, .045, .355, 1);
transition: color .2s cubic-bezier(.645, .045, .355, 1);
color: #666 !important;
font-weight: normal !important;
}
.el-progress-bar__inner {
max-width: 100%;
}
.el-progress__text {
font-size: 12px !important;
}
.el-dropdown-link {
cursor: pointer;
color: #409eff;
font-size: 12px;
}
.el-pagination button,
.el-pagination span:not([class*=suffix]) {
font-size: 12px;
}
.el-pagination__sizes .el-input .el-input__inner {
font-size: 12px;
}
.el-pager li {
font-size: 12px;
}
.el-input--small {
font-size: 13px;
}
.el-tabs__content {
position: static !important;
}
//menu
.el-submenu .el-menu-item {
height: 48px;
line-height: 48px;
}
.el-menu-item,
.el-submenu__title {
height: 48px;
line-height: 48px;
}
.el-dialog {
.el-dialog--middle {
width: 60%;
}
.el-dialog__header {
padding: 15px;
color: #FFF;
background: #21242e;
border-bottom: 1px solid #e5e5e5;
}
.el-dialog__title {
color: #FFF !important;
}
.el-dialog__footer {
background: #F0F3FA;
padding: 10px;
text-align: right;
border-top: 1px solid #e5e5e5;
}
.el-dialog__headerbtn .el-dialog__close {
font-weight: bold;
color: rgb(235, 213, 221);
}
.el-dialog__headerbtn .el-dialog__close:hover {
color: #fff;
}
}

23
src/css/font.scss Normal file
View File

@ -0,0 +1,23 @@
$font-size: 16px;
$small-font-size: 14px;
.font-big {
font-size: $font-size;
.el-menu-item,
.el-submenu__title,
ul.header-menu li,
.el-tabs__item {
font-size: $font-size;
.iconfont {
font-size: $font-size;
}
}
.el-table--mini,
.el-table--small,
.el-table__expand-icon,
.app-levelbar,
.el-button--small,
.el-dropdown-link,
.common-detail .detail-body .basic-info .attr {
font-size: $font-size;
}
}

10
src/css/hack.scss Normal file
View File

@ -0,0 +1,10 @@
@media screen and(-ms-high-contrast:active),
(-ms-high-contrast:none) {
.el-table__header,
.el-table__body {
width: 100% !important;
}
/* .basic-form-item{
height: 32.76px;
} */
}

6
src/css/index.scss Normal file
View File

@ -0,0 +1,6 @@
@import './common.scss';
// @import './element-ui.scss';
@import './antd.scss';
@import './hack.scss';
@import './font.scss';
@import './animate.scss';

11
src/hooks/useSelection.js Normal file
View File

@ -0,0 +1,11 @@
import { ref } from '@vue/composition-api'
export default function () {
const selectionIds = ref([])
function handleSelectionChange(selections) {
selectionIds.value = selections.map((item) => item.id)
}
return {
selectionIds,
handleSelectionChange,
}
}

87
src/hooks/useTable.ts Normal file
View File

@ -0,0 +1,87 @@
import { ref, toRefs, reactive } from 'vue'
// import { MessageBox, Message } from 'element-'
import { handleSearchParam } from 'cmp-element/utils'
interface IRemoveService {
(id: number): Promise<Base.IResponseData>
}
interface IConfigs {
getService: {
(params: Base.IListParams): Promise<Base.IResponseList>
}
removeService?: IRemoveService
listFormat?: {
(data: any): any[]
}
afterGetList?: {
(): void
}
rows?: number
params?: any
initParams?: any
deleteTipKey?: string
}
export function useDelete(removeService: IRemoveService, getData: { (): void }, deleteTipKey = 'name') {
function handleDelete(record: any) {
console.log(record)
// MessageBox.confirm(`您确定要删除【${record[deleteTipKey]}】吗?`, '提示', {
// confirmButtonClass: 'el-button--danger',
// type: 'warning',
// })
// .then(async () => {
// const res = await removeService(record.id)
// if (res.success) {
// Message.success(res.message)
// getData()
// }
// })
// .catch((e: any) => {
// console.error(e)
// })
}
return {
handleDelete,
}
}
export default function <T = any>(configs: IConfigs) {
const { getService, removeService, listFormat, rows = 10, params = {}, afterGetList, initParams, deleteTipKey = 'name' } = configs
const loading = ref(false)
const state: Base.IListState<T> = reactive({
list: [],
total: 0,
params: {
page: 1,
rows,
...params,
},
})
if (initParams) {
state.params.params = handleSearchParam(initParams)
}
async function getList() {
loading.value = true
try {
const data = await getService(state.params)
loading.value = false
if (data.success) {
if (listFormat) {
state.list = listFormat(data.data.rows)
} else {
state.list = data.data.rows
}
state.total = data.data.total
afterGetList && afterGetList()
}
} catch (e) {
loading.value = false
}
}
const { handleDelete } = useDelete(removeService as IRemoveService, getList, deleteTipKey)
return {
loading,
...toRefs(state),
getList,
handleDelete,
}
}

23
src/hooks/useWebsocket.ts Normal file
View File

@ -0,0 +1,23 @@
import { onUnmounted } from 'vue'
import WebSocket from 'cmp-socket'
import { getToken } from 'utils/auth'
export default function (onmessage: { (data: any): void }, messageCommonFun?: any) {
const protocol = location.protocol === 'http:' ? 'ws' : 'wss'
let webSocket = new WebSocket({
url: `${protocol}://${location.host}/api/sms/messageService`,
commonFun: messageCommonFun,
pingMsg: 'HeartBeat',
reConnectNum: 5,
params: getToken()
})
if (onmessage && typeof onmessage === 'function') {
webSocket.onmessage = onmessage
}
onUnmounted(() => {
webSocket.Destroy()
webSocket = null
})
return {
webSocket
}
}

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1617877873787" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="17931" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M512 153.6c197.9392 0 358.4 160.4608 358.4 358.4s-160.4608 358.4-358.4 358.4S153.6 709.9392 153.6 512 314.0608 153.6 512 153.6z m0 51.2C342.3232 204.8 204.8 342.3488 204.8 512s137.5232 307.2 307.2 307.2c169.6512 0 307.2-137.5488 307.2-307.2S681.6512 204.8 512 204.8z m6.6816 220.5952c34.5856 0 49.3312 25.856 48.4864 61.952l-0.256 6.6816c-0.896 15.616-3.8144 31.744-10.8032 63.3344l-4.352 19.3536c-9.6512 42.9312-13.312 69.2224-11.9808 81.92l0.4608 2.9696 2.1248-1.1008c2.304-1.28 4.9408-3.0208 7.7824-5.12l2.8928-2.2272a226.1248 226.1248 0 0 0 25.856-25.2416l0.4096-0.5376 45.44 23.6032c-4.5568 8.7296-23.5008 29.2864-39.8848 42.2912-30.7712 24.4224-61.2096 32.6144-83.2 4.3008-17.7408-22.8352-17.3312-49.6384-4.6848-109.8752l11.4432-51.9168c4.992-23.296 6.9888-35.7888 7.4752-47.0528l0.1024-2.56a59.5712 59.5712 0 0 0-0.4352-9.472l-1.0752 0.0256c-7.936 0.512-13.44 2.56-19.8912 7.0144-5.9904 4.1472-35.5072 31.5136-50.8416 45.056l-5.2992 4.5568-32.9472-39.1936 8.2944-7.296c15.3856-13.824 43.2128-39.3728 51.712-45.2608 16.0512-11.0592 32.384-16.2048 53.1712-16.2048zM512 280.576a38.4 38.4 0 1 1 0 76.8 38.4 38.4 0 0 1 0-76.8z" p-id="17932"></path></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1617879939853" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1574" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M950.4256 252.16v448.4096c0 13.6192-3.1744 26.2656-9.5232 37.9904a71.168 71.168 0 0 1-26.0096 27.4432L541.184 990.208a59.7504 59.7504 0 0 1-32.3584 9.3184c-11.6736 0-22.4768-3.1232-32.3584-9.3184l-373.7088-224.256a71.1168 71.1168 0 0 1-26.0096-27.392 77.9776 77.9776 0 0 1-9.5232-37.9904V252.16c0-15.5648 4.096-29.7472 12.1856-42.5984 8.192-12.8512 18.944-22.016 32.4096-27.4432L485.4784 32.6144a62.464 62.464 0 0 1 23.3472-4.608c7.7824 0 15.5648 1.536 23.3472 4.608l373.6576 149.504c13.4656 5.4272 24.2688 14.592 32.4096 27.4432 8.1408 12.8 12.1856 27.0336 12.1856 42.5984zM508.8256 399.36l370.4832-148.3264-370.4832-148.2752-370.4832 148.2752L508.8256 399.36z m33.9968 505.0368l339.6608-203.776V329.2672L542.8224 465.3056v439.04z" p-id="1575"></path></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1618221398745" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5067" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M817.92 135.893333c7.808 0 15.232 3.242667 20.778667 9.002667a31.146667 31.146667 0 0 1 8.661333 21.674667v564.224c0 8.106667-3.114667 15.786667-8.661333 21.674666a28.544 28.544 0 0 1-20.736 9.002667H330.581333a28.8 28.8 0 0 1-20.736-9.002667 30.848 30.848 0 0 1-8.661333-21.674666V166.570667c0-8.106667 3.114667-15.786667 8.661333-21.674667a28.330667 28.330667 0 0 1 20.736-9.002667h487.381334z m0-50.56H330.624c-42.88 0-78.08 36.48-78.08 81.109334v564.266666c0 44.629333 35.2 81.109333 78.08 81.109334h487.381333c42.88 0 78.037333-36.48 78.037334-81.109334V166.4C896.085333 121.898667 860.970667 85.333333 817.92 85.333333z" p-id="5068"></path><path d="M767.36 708.053333H381.056c-13.653333 0-24.746667-11.946667-24.746667-26.496 0-14.592 11.093333-26.453333 24.746667-26.453333h386.432c13.653333 0 24.746667 11.861333 24.746667 26.453333 0 14.549333-11.093333 26.453333-24.874667 26.453334z m-107.093333-265.002666h-68.138667V398.08h68.181333c13.653333 0 24.746667-11.904 24.746667-26.453333 0-14.677333-11.093333-26.496-24.746667-26.496H602.453333l64.554667-69.205334a27.946667 27.946667 0 0 0 0-37.546666 23.552 23.552 0 0 0-34.986667 0L567.381333 307.626667l-64.64-69.205334a23.594667 23.594667 0 0 0-35.029333 0 27.946667 27.946667 0 0 0 0 37.546667l64.554667 69.205333h-57.770667c-13.653333 0-24.746667 11.946667-24.746667 26.453334 0 14.72 11.093333 26.496 24.746667 26.496h68.181333v44.928H474.453333c-13.653333 0-24.746667 11.946667-24.746666 26.453333 0 14.72 11.093333 26.538667 24.746666 26.538667h68.181334v27.733333a24.746667 24.746667 0 1 0 49.493333 0v-27.733333h68.138667c13.653333 0 24.746667-11.946667 24.746666-26.496 0-14.677333-11.093333-26.453333-24.746666-26.453334v-0.042666z" p-id="5069"></path><path d="M829.525333 882.133333H177.92V174.378667c0-14.762667-11.221333-26.794667-25.002667-26.794667-13.738667 0-24.96 12.032-24.96 26.794667v735.189333c0 14.805333 11.221333 26.794667 24.96 26.794667a25.301333 25.301333 0 0 0 5.290667-0.554667h671.274667c13.781333 0 24.96-11.989333 24.96-26.794667 0-14.762667-11.221333-26.88-24.96-26.88z" p-id="5070"></path></svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1617878067266" class="icon" viewBox="0 0 1088 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="18832" xmlns:xlink="http://www.w3.org/1999/xlink" width="212.5" height="200"><defs><style type="text/css"></style></defs><path d="M768.149096 256.014491H448.009723a32.008484 32.008484 0 0 1 0-64.016969h320.139373a32.008484 32.008484 0 0 1 0 64.016969z m0 192.269022H448.009723a32.008484 32.008484 0 0 1 0-64.016969h320.139373a32.008484 32.008484 0 0 1 0 64.016969z" p-id="18833"></path><path d="M928.027931 607.998761a32.008484 32.008484 0 0 1-32.008485-32.008484v-512.026692H320.030315v512.026692a32.008484 32.008484 0 0 1-64.016969 0V32.009629A32.008484 32.008484 0 0 1 288.294475 0.001145h639.733456a32.008484 32.008484 0 0 1 32.008484 32.008484v543.980648a32.008484 32.008484 0 0 1-32.008484 32.008484z" p-id="18834"></path><path d="M288.294475 607.998761H128.033937a32.008484 32.008484 0 0 1-32.008484-32.008484V159.989038a32.008484 32.008484 0 0 1 32.008484-32.008484h160.260538a32.008484 32.008484 0 0 1 32.008484 32.008484v416.001239a32.008484 32.008484 0 0 1-32.008484 32.008484zM159.987893 543.981792h96.025453V191.997522H159.987893z" p-id="18835"></path><path d="M1056.007339 1024H32.008484A32.008484 32.008484 0 0 1 0 991.991516v-416.001239a32.008484 32.008484 0 0 1 32.008484-32.008485h405.368097a31.953955 31.953955 0 0 1 30.318087 21.811574l35.389279 106.058777h113.856414l35.389279-106.113306a31.899426 31.899426 0 0 1 30.318087-21.811573h373.359612a32.008484 32.008484 0 0 1 32.008485 32.008484v416.001239a32.008484 32.008484 0 0 1-32.008485 32.008484zM64.016969 959.983031H1024.053384V607.998761h-318.285389l-35.389278 106.113306a31.899426 31.899426 0 0 1-30.318088 21.811574H480.072736a31.899426 31.899426 0 0 1-30.372616-21.811574l-35.334749-106.113306H64.016969z" p-id="18836"></path></svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1617880094595" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1704" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M826.55232 438.82496H767.4368l5.4272-310.04672c0-13.18912-22.58944-17.92-35.78368-17.92H140.66688c-13.184 0-29.824 16.64512-29.824 29.83936v328.2432H289.792c13.18912 0 29.824 28.57984 29.824 41.77408V588.288h119.51616v59.6736H289.792c1.13664 4.77696-29.81888-16.64512-29.81888-29.83936v-89.50272H110.83776v238.70464c0-1.13152 16.64 0 29.824 0h298.4704v59.68384h-328.2944c-26.368 0-59.64288-33.28-59.64288-59.68384V110.87872C51.2 84.48 84.48 51.2 110.83776 51.2h656.07168c26.37312 0 59.64288 33.28 59.64288 59.67872v327.94624z m-172.96384-238.43328c13.18912 0 23.8592 16.64512 23.8592 29.84448s-16.63488 29.83424-29.824 29.83424H230.13376c-13.18912 0-29.824-16.64512-29.824-29.83424s10.6752-29.84448 23.8592-29.84448h429.42464z m-5.9648 149.19168c13.184 0 29.824 16.64 29.824 29.83936s-16.64 29.84448-29.824 29.84448H230.13376c-14.11072 3.4816-34.69312 1.78176-29.824-29.84448 2.01216-13.04064 16.63488-29.83936 29.824-29.83936h417.48992zM538.78784 487.3728h382.87872a51.2512 51.2512 0 0 1 51.13344 51.11296v383.15008a51.2 51.2 0 0 1-51.13344 51.16416h-382.87872a51.2 51.2 0 0 1-51.13856-51.16416v-383.15008a51.2 51.2 0 0 1 51.13856-51.11296z m-14.75072 423.62368a25.43616 25.43616 0 0 0 25.37984 25.40032h361.61536a25.44128 25.44128 0 0 0 25.37984-25.40032v-361.86112a25.43616 25.43616 0 0 0-25.37984-25.35936h-361.61536a25.43616 25.43616 0 0 0-25.37984 25.35936v361.86112z m348.32896-282.84928a12.18048 12.18048 0 1 1 0 24.2688h-92.70272a12.1856 12.1856 0 1 1 0-24.2688h92.70272z m0 157.76256a12.1856 12.1856 0 1 1 0 24.2688h-92.70272a12.1856 12.1856 0 1 1 0-24.2688h92.70272z m0 48.54784a12.18048 12.18048 0 1 1 0 24.2688h-92.70272a12.1856 12.1856 0 1 1 0-24.2688h92.70272z m-246.44608-204.4672v-40.7296a12.18048 12.18048 0 1 1 24.25856 0v40.7296h40.69888a12.13952 12.13952 0 0 1 0 24.27392h-40.69888v40.72448a12.18048 12.18048 0 1 1-24.25856 0v-40.72448h-40.704a12.13952 12.13952 0 1 1 0-24.27392h40.704z m40.96 145.72544h0.0512a12.18048 12.18048 0 1 1 17.21344 17.23904l-28.81024 28.77952 28.81024 28.83584a12.21632 12.21632 0 0 1-17.36192 17.18272l-28.7744-28.83072-28.81536 28.83072a12.18048 12.18048 0 1 1-17.21856-17.2288l29.1072-28.84096-29.1072-28.77952a12.20096 12.20096 0 1 1 17.3056-17.18784l28.7744 28.78464z m0 0" p-id="1705"></path></svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

1
src/icons/svg/svg-db.svg Normal file
View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1617880555359" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2098" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M936.33 732.203c-9.872-10.814-23.349-17.123-37.953-17.778-14.849-0.451-28.613 4.383-39.427 14.255-0.215 0.195-0.282 0.476-0.492 0.673l-144.256-83.448c0.625-2.939 1.994-5.635 2.133-8.697 0.676-14.624-4.383-28.634-14.255-39.448-20.379-22.284-55.096-23.841-77.401-3.523-22.305 20.4-23.882 55.096-3.502 77.421 7.932 8.707 18.534 13.533 29.878 15.799l17.069 160.864c-5.049 2.466-10.138 4.9-14.422 8.82-23.964 21.875-25.643 59.192-3.769 83.156 11.593 12.699 27.507 19.13 43.483 19.13 14.173 0 28.408-5.08 39.673-15.361 11.613-10.61 18.393-25.111 19.109-40.8 0.264-5.807-1.162-11.301-2.527-16.782l114.101-74.264c8.695 6.485 18.549 11.122 29.627 11.618 0.86 0.041 1.7 0.082 2.56 0.082 13.682 0 26.688-5.039 36.867-14.337 22.307-20.359 23.863-55.096 3.504-77.38z m-92.616 24.03c-2.588 10.576-2.855 21.552 0.968 31.893l-109.7 71.397c-9.88-10.018-22.696-16.007-36.719-17.202l-16.624-156.665a54.739 54.739 0 0 0 16.898-10.515c0.637-0.579 0.891-1.421 1.498-2.025l143.679 83.117z m-206.233-122.63c0.287-6.431 3.072-12.412 7.824-16.754a24.194 24.194 0 0 1 16.304-6.308c6.575 0 13.108 2.663 17.86 7.865 4.342 4.752 6.575 10.937 6.288 17.41-0.287 6.431-3.072 12.371-7.824 16.713h-0.021c-4.793 4.342-11.081 6.964-17.409 6.308-6.431-0.287-12.371-3.072-16.713-7.824-4.364-4.752-6.596-10.937-6.309-17.41z m74.963 287.851c-11.47 10.528-29.432 9.708-39.898-1.802-10.487-11.511-9.688-29.412 1.802-39.898 5.243-4.793 11.961-7.414 19.028-7.414 0.43 0 0.86 0.041 1.29 0.041 7.537 0.328 14.501 3.605 19.581 9.176 5.1 5.571 7.701 12.781 7.353 20.318-0.328 7.536-3.585 14.5-9.156 19.579z m199.738-134.483c-4.772 4.342-10.61 6.185-17.389 6.308-6.452-0.328-12.391-3.113-16.754-7.865-8.971-9.831-8.275-25.152 1.557-34.123a24.057 24.057 0 0 1 16.263-6.308c6.554 0 13.108 2.663 17.86 7.865 4.363 4.752 6.595 10.937 6.308 17.369-0.287 6.472-3.072 12.412-7.845 16.754z" p-id="2099"></path><path d="M151.81 219.064v18.751c65.713 49.346 192.006 82.731 337.007 82.731s271.291-33.385 337.005-82.731v-18.751c-57.667 50.36-186.873 85.432-337.005 85.432-150.134 0-279.341-35.072-337.007-85.432zM488.817 63.779c-221.567 0-401.197 71.858-401.197 160.48V721.72h2.025c20.146 81.105 191.141 144.432 399.172 144.432 24.529 0 32.095-2.058 55.411-3.746l0.023-31.962c-23.252 1.598-30.838 3.61-55.434 3.61-203.85 0-369.102-57.477-369.102-128.382V608.14c61.194 57.349 203.387 97.532 369.102 97.532 20.51 0 40.664-0.614 60.344-1.802l1.249-32.051a1057.38 1057.38 0 0 1-61.594 1.779c-203.85 0-369.102-57.477-369.102-128.382v-97.555c61.194 57.349 203.387 97.555 369.102 97.555s307.908-40.206 369.079-97.555v175.217l32.095-0.545V224.259c0.001-88.622-179.609-160.48-401.173-160.48z m369.079 320.957c0 70.908-165.229 128.382-369.079 128.382s-369.102-57.474-369.102-128.382v-97.555c61.194 57.352 203.387 97.555 369.102 97.555s307.908-40.203 369.079-97.555v97.555z m-369.079-32.095c-203.85 0-369.102-57.477-369.102-128.382 0-70.908 165.252-128.385 369.102-128.385s369.079 57.477 369.079 128.385c0 70.905-165.229 128.382-369.079 128.382z" p-id="2100"></path></svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1619519603943" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2064" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M512 64C262.4 64 64 262.4 64 512s198.4 448 448 448 448-198.4 448-448-198.4-448-448-448z m0 64c96 0 185.6 32 249.6 96L217.6 768C160 697.6 128 608 128 512c0-211.2 172.8-384 384-384z m0 768c-96 0-179.2-32-243.2-89.6l537.6-537.6c57.6 64 89.6 147.2 89.6 243.2 0 211.2-172.8 384-384 384z" p-id="2065"></path></svg>

After

Width:  |  Height:  |  Size: 685 B

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1617877444762" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="15703" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M509.93242628 444.74945473c82.85751593 0 150.02079234 67.13107507 150.02079236 149.98537081 0 82.85751593-67.16381295 150.02079234-150.02079236 150.02079235-82.8215584 0-149.98537081-67.16381295-149.98537081-150.02079235C359.94759199 511.88052927 427.11086789 444.74945473 509.93242628 444.74945473z" p-id="15704"></path></svg>

After

Width:  |  Height:  |  Size: 705 B

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1617880577563" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2229" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M827.9552 490.0864L757.76 530.7392l84.992 149.6064-35.1232 64.6656h-164.4032v79.4112h210.5856L936.96 680.3456l-109.0048-190.2592zM478.8224 161.28h64.6656l81.2544 140.3904 70.1952-40.6528-105.3184-181.0432H430.7968L325.5296 262.8608l70.1952 40.6528L478.8224 161.28z m-301.056 519.0656l84.992-147.7632-70.2464-40.6528-109.0048 188.416 83.1488 144.0768h212.4288v-79.4112H212.8384l-35.072-64.6656z m0 0" p-id="2230"></path><path d="M776.2432 401.408c29.5424 24.0128 72.0384 24.0128 99.7376-3.6864 29.5424-29.5424 29.5424-75.7248 0-105.3184-29.5424-29.5424-75.7248-29.5424-105.3184 0-16.64 16.64-24.0128 40.6528-20.3264 64.6656l-158.8736 88.6784-3.6864-3.6864c-42.496-42.496-112.6912-42.496-157.0304 0l-3.6864 3.6864-158.8224-88.6784c3.6864-22.1696-3.6864-46.1824-20.3264-64.6656-29.5424-29.5424-75.7248-29.5424-105.3184 0-29.5424 29.5424-29.5424 75.7248 0 105.3184 27.6992 27.6992 72.0384 27.6992 99.7376 3.6864l160.7168 88.6784c-11.1104 36.9664-1.8432 79.4112 27.6992 109.0048 14.7968 14.7968 33.2288 24.0128 53.5552 29.5424v179.2c-9.216 3.6864-18.4832 9.216-27.6992 16.64-29.5424 29.5424-29.5424 75.7248 0 105.2672s75.7248 29.5424 105.3184 0c29.5424-29.5424 29.5424-75.7248 0-105.2672-7.3728-7.3728-16.64-12.9536-27.6992-16.64v-179.2c18.4832-3.6864 36.9664-14.7968 53.5552-29.5424 29.5424-29.5424 38.8096-70.1952 27.6992-109.0048l160.768-88.6784z m0 0" p-id="2231"></path></svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 30 KiB

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1619161189850" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2649" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M819.2 870.4H204.8c-56.32 0-102.4-46.08-102.4-102.4V256c0-56.32 46.08-102.4 102.4-102.4h614.4c56.32 0 102.4 46.08 102.4 102.4v512c0 56.32-46.08 102.4-102.4 102.4zM204.8 256v512h614.4V256H204.8z" fill="" p-id="2650"></path><path d="M542.72 665.6h-10.24c-15.36-5.12-30.72-15.36-40.96-30.72L435.2 501.76l-87.04 97.28c-20.48 20.48-51.2 20.48-71.68 5.12-20.48-20.48-20.48-51.2-5.12-71.68l138.24-153.6c10.24-15.36 30.72-20.48 46.08-20.48 15.36 5.12 30.72 15.36 40.96 30.72l61.44 133.12 122.88-133.12c20.48-20.48 51.2-20.48 71.68-5.12 20.48 20.48 20.48 51.2 5.12 71.68l-174.08 194.56c-15.36 10.24-30.72 15.36-40.96 15.36z" fill="" p-id="2651"></path></svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1617876209511" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4338" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M109.7 590.8c-22.1 0-40-17.9-40-40v-6.4c0.2-11.3 0.8-22.7 1.8-33.9 6.1-65.6 27.7-129.8 62.3-185.8 33.8-54.5 80.3-101.7 134.4-136.3 18.6-11.9 43.3-6.5 55.3 12.1s6.5 43.3-12.1 55.3C219.6 314.5 161.2 410 151.2 517.9c-0.9 9.1-1.4 18.4-1.5 27.6v5.3c0 22.1-17.9 40-40 40zM499.7 980.8c-79.6 0-157.4-21.9-224.9-63.4-18.8-11.6-24.7-36.2-13.1-55 11.6-18.8 36.2-24.7 55-13.1 54.9 33.8 118.2 51.6 183 51.6 88.6 0 173.1-33.2 238-93.4 16.2-15 41.5-14.1 56.5 2.1s14.1 41.5-2.1 56.5c-79.8 74-183.6 114.7-292.4 114.7zM880.1 504.9c-18.3 0-34.8-12.6-39-31.2-21.4-95.3-82.6-177.9-167.7-226.7-19.2-11-25.8-35.4-14.8-54.6s35.4-25.8 54.6-14.8c104.6 59.9 179.7 161.5 206 278.6 4.8 21.6-8.7 43-30.2 47.8-3 0.6-6 0.9-8.9 0.9zM499.7 282c-66 0-120-54-120-120s54-120 120-120 120 54 120 120-54 120-120 120zM158.1 862c-66 0-120-54-120-120s54-120 120-120 120 54 120 120-54 120-120 120zM871.7 787.8c-66 0-120-54-120-120s54-120 120-120 120 54 120 120-54 120-120 120z" p-id="4339"></path></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1619161235402" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2782" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M727.2 374H292.6c-13.5 0-24.5-11-24.5-24.5v-1.1c0-13.5 11-24.5 24.5-24.5h434.5c13.5 0 24.5 11 24.5 24.5v1.1c0 13.5-11 24.5-24.4 24.5zM654.6 98.2H368.1c-11.1 0-20.1-9.1-20.1-20.1v-9.8c0-11.1 9.1-20.1 20.1-20.1h286.5c11.1 0 20.1 9.1 20.1 20.1V78c0 11.1-9 20.2-20.1 20.2zM727.2 547H292.6c-13.5 0-24.5-11-24.5-24.5v-1.1c0-13.5 11-24.5 24.5-24.5h434.5c13.5 0 24.5 11 24.5 24.5v1.1c0 13.5-11 24.5-24.4 24.5zM727.2 714.4H292.6c-13.5 0-24.5-11-24.5-24.5v-1.1c0-13.5 11-24.5 24.5-24.5h434.5c13.5 0 24.5 11 24.5 24.5v1.1c0 13.5-11 24.5-24.4 24.5z" fill="#686868" p-id="2783"></path><path d="M822.7 975.6H199.6c-62.3 0-113-50.7-113-113V162c0-62.3 50.7-113 113-113h95.3l42.3 87v0.8h350.2v-0.8l42.3-87h92.9c62.3 0 113 50.7 113 113v700.6c0.1 62.3-50.6 113-112.9 113zM199.6 99c-34.7 0-63 28.3-63 63v700.6c0 34.7 28.3 63 63 63h623.1c34.7 0 63-28.3 63-63V162c0-34.7-28.3-63-63-63H761l-24.1 49.6c-3.3 21.6-22 38.2-44.5 38.2H332.2c-22.5 0-41.2-16.6-44.5-38.2L263.6 99h-64z" fill="#686868" p-id="2784"></path></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1617877587265" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="15833" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M874.538573 127.922397H862.390562C734.516757 127.922397 610.479167 85.724041 559.329645 13.475342a54.985736 54.985736 0 0 0-38.362141-13.42675h-21.099178C478.769148 0.048592 463.424292 5.802913 458.30934 17.311556 409.077925 87.002779 290.155287 127.922397 159.084637 127.922397H146.297256a58.82195 58.82195 0 0 0-51.149521 59.461319v351.013593A526.840075 526.840075 0 0 0 502.425802 1023.039028a26.21413 26.21413 0 0 0 14.066119 0A526.200706 526.200706 0 0 0 926.327464 549.266583a30.689713 30.689713 0 0 0 0-12.787381V187.383716a56.903843 56.903843 0 0 0-51.149522-59.461319zM862.390562 524.97056v9.590535A462.263803 462.263803 0 0 1 509.458861 959.102126 462.263803 462.263803 0 0 1 159.084637 536.479202V199.531727A441.164626 441.164626 0 0 0 505.622647 63.985494h8.951167c63.936902 81.839235 196.925659 134.906864 347.177379 135.546233z" p-id="15834"></path><path d="M382.863795 351.701555A127.873805 127.873805 0 0 0 478.769148 474.460407V831.228322a31.968451 31.968451 0 0 0 31.968451 31.968451 31.968451 31.968451 0 0 0 31.968452-31.968451v-63.936903h63.936902a31.968451 31.968451 0 0 0 31.968451-31.968451 31.968451 31.968451 0 0 0-31.968451-31.968451H542.706051v-63.936902h63.936902a31.968451 31.968451 0 0 0 31.968451-31.968451A31.968451 31.968451 0 0 0 606.642953 575.480712H542.706051V474.460407A127.873805 127.873805 0 1 0 382.863795 351.701555z m191.810707 0a63.936902 63.936902 0 1 1-63.936903-63.936903 63.936902 63.936902 0 0 1 63.936903 63.936903z" p-id="15835"></path></svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1618310470844" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7840" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M852 261.2v-78.5H732v-110H292v110H172v78.5H64v690h896v-690H852z m58 640H114v-590h108v-78.5h120v-110h340v110h120v78.5h108v590zM245 474.8h50v319.5h-50V474.8z m161.3 0h50v319.5h-50V474.8z m161.4 0h50v319.5h-50V474.8z m161.3 0h50v319.5h-50V474.8z" fill="" p-id="7841"></path></svg>

After

Width:  |  Height:  |  Size: 654 B

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1619161122519" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2517" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M423.300438 484.294258L136.046395 334.422584a76.46514 76.46514 0 0 0-34.664197-8.666049 47.663271 47.663271 0 0 0-49.702341 48.937689v405.265243a105.521893 105.521893 0 0 0 60.407461 84.876305l287.254042 150.126558a80.543281 80.543281 0 0 0 34.409313 8.411166 47.663271 47.663271 0 0 0 49.702341-48.93769V569.425448A105.267009 105.267009 0 0 0 423.300438 484.294258z m2.803721 410.108035a35.173964 35.173964 0 0 1-36.448383 35.938616 57.858623 57.858623 0 0 1-25.48838-6.372095l-211.29867-110.109802a76.46514 76.46514 0 0 1-44.094898-61.936764V454.472854a34.919081 34.919081 0 0 1 36.448384-35.938616 54.545133 54.545133 0 0 1 25.48838 6.372095L381.754378 535.271018a76.46514 76.46514 0 0 1 44.349781 62.191648zM970.79084 779.959467V374.694224a47.918154 47.918154 0 0 0-50.97676-49.192573 80.798165 80.798165 0 0 0-34.409313 8.666049L598.660492 484.294258a105.521893 105.521893 0 0 0-61.172112 85.386073v405.265243a47.663271 47.663271 0 0 0 50.97676 48.937689 76.46514 76.46514 0 0 0 34.664197-8.666049l287.254043-150.381442A105.267009 105.267009 0 0 0 970.79084 779.959467z m-57.603739-28.037219A76.46514 76.46514 0 0 1 868.83732 814.113896l-211.043786 110.109801a56.839087 56.839087 0 0 1-25.48838 6.372095 35.173964 35.173964 0 0 1-36.448384-35.938615V597.462666A76.46514 76.46514 0 0 1 639.4419 535.271018l210.788903-110.364685a53.780482 53.780482 0 0 1 25.48838-6.372095 34.919081 34.919081 0 0 1 36.448383 35.938616V751.922248z m-25.48838-581.644831L566.290249 12.249461a136.872601 136.872601 0 0 0-58.623274-12.234423 142.734928 142.734928 0 0 0-62.446531 13.508842L120.753367 178.433698a57.093971 57.093971 0 0 0-35.173965 48.427922 54.290249 54.290249 0 0 0 34.919081 47.408387L441.906955 433.317498a136.107949 136.107949 0 0 0 58.623274 12.234423 141.715393 141.715393 0 0 0 62.446531-14.528377l324.721961-164.400051a56.32932 56.32932 0 0 0 34.664197-48.93769 55.054901 55.054901 0 0 0-34.91908-47.408386z m-116.481896 82.837235l-226.336815 114.69771a97.620495 97.620495 0 0 1-43.330246 9.4307 90.738633 90.738633 0 0 1-40.526524-8.666049l-224.552628-110.109802a38.23257 38.23257 0 0 1-25.48838-33.134894 39.506989 39.506989 0 0 1 25.48838-33.899545L462.807427 76.480178a103.227939 103.227939 0 0 1 43.585129-9.4307 95.836309 95.836309 0 0 1 41.036292 9.4307l224.042861 110.109802a38.23257 38.23257 0 0 1 24.213961 33.389778 38.997221 38.997221 0 0 1-25.48838 33.899545z" fill="" p-id="2518"></path></svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1617881405978" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4872" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M397.4 736.2h-79.9c-4.4 0-8 3.6-8 8v79.9c0 4.4 3.6 8 8 8h79.9c4.4 0 8-3.6 8-8v-79.9c0-4.5-3.6-8-8-8z m0-147.9h-79.9c-4.4 0-8 3.6-8 8v79.9c0 4.4 3.6 8 8 8h79.9c4.4 0 8-3.6 8-8v-79.9c0-4.4-3.6-8-8-8z m0-147.9h-79.9c-4.4 0-8 3.6-8 8v79.9c0 4.4 3.6 8 8 8h79.9c4.4 0 8-3.6 8-8v-79.9c0-4.4-3.6-8-8-8z m328 511.3c-13.6 0-27.4-1.2-40.8-3.6l-1.4-0.3c-10.4-1.9-18.8-10.2-20.7-20.7l-6.7-36.5c-14.8-6.1-28.8-14.2-41.6-24.2l-35 12.4c-2.8 1-5.7 1.5-8.6 1.5-7.6 0-14.7-3.3-19.6-9.1l-0.9-1c-17.8-21.1-31.6-44.9-40.9-70.9l-0.5-1.4c-3.5-9.9-0.5-21.3 7.5-28.2l28.3-24.2c-1-7.8-1.6-15.9-1.6-24s0.5-16.2 1.6-24l-28.3-24.2c-8.1-6.9-11.1-18.3-7.5-28.2l0.5-1.3c9.3-25.9 23.1-49.8 40.9-70.9l0.9-1.1c4.8-5.7 12-9.1 19.6-9.1 2.9 0 5.8 0.5 8.6 1.5l35 12.4c12.8-9.9 26.8-18 41.6-24.2l6.7-36.5c1.9-10.4 10.2-18.7 20.7-20.7l1.3-0.2c13.4-2.4 27.1-3.6 40.8-3.6 13.8 0 27.5 1.2 40.8 3.6l1.4 0.3c10.4 1.9 18.8 10.2 20.7 20.7l6.7 36.3c15 6.2 29.1 14.3 42 24.3l34.7-12.3c2.8-1 5.7-1.5 8.6-1.5 7.6 0 14.7 3.3 19.6 9.1l0.9 1c17.9 21.1 31.6 44.9 40.9 70.9l0.5 1.3c3.6 10 0.5 21.4-7.5 28.2l-27.9 23.9c1.1 8.1 1.6 16.3 1.6 24.4 0 8.1-0.5 16.3-1.6 24.4l27.9 23.9c8.1 7 11.1 18.3 7.5 28.2l-0.5 1.3c-9.3 25.9-23.1 49.8-40.9 70.9l-0.9 1.1c-4.8 5.7-12 9.1-19.6 9.1-2.9 0-5.8-0.5-8.6-1.5l-34.7-12.3c-12.9 9.9-26.9 18.1-42 24.3l-6.7 36.3c-1.9 10.4-10.2 18.7-20.7 20.7l-1.3 0.2c-13.4 2.2-27.2 3.5-40.8 3.5zM712.9 896c4.1 0.3 8.3 0.4 12.4 0.4s8.3-0.1 12.4-0.4l8.4-45.8 24-9c13-4.9 25.2-11.9 36.1-20.9L826 804l43.7 15.5c4.7-6.9 8.8-14.1 12.4-21.5L847 767.9l4.2-25.3c1.1-7 1.7-14 1.7-21s-0.6-14.1-1.7-21l-4.1-25.2 35.2-30.1c-3.7-7.5-7.8-14.7-12.4-21.5L826 639.3 806.3 623c-10.9-8.9-23-15.9-36.1-20.9l-24-9-8.4-45.8c-4.1-0.3-8.3-0.4-12.4-0.4s-8.3 0.1-12.4 0.4l-8.4 45.9-23.9 9c-12.9 4.9-25 11.9-35.8 20.8l-19.8 16.3-44.1-15.6c-4.7 6.9-8.8 14.1-12.4 21.5l35.6 30.4-4.1 25.2c-1.1 6.7-1.7 13.7-1.7 20.8 0 6.8 0.6 13.8 1.7 20.8l4.1 25.2-35.6 30.4c3.7 7.5 7.8 14.7 12.4 21.5l44-15.7 19.8 16.3c10.8 8.9 22.8 15.9 35.8 20.8l23.8 9.2 8.5 45.9z" p-id="4873"></path><path d="M725.4 682.4c17.6 0 32 14.4 32 32.1s-14.3 32.1-32 32.1-32-14.4-32-32.1 14.3-32.1 32-32.1m0-56c-48.6 0-88 39.4-88 88.1s39.4 88.1 88 88.1 88-39.4 88-88.1-39.5-88.1-88-88.1z" p-id="4874"></path><path d="M474.3 889H235.5V366.9l278.9-213.5 278.6 216v92.4c0 4.4 3.6 8 8 8h53c4.4 0 8-3.6 8-8v-38.9l38.8 30.1c3.5 2.7 8.5 2.1 11.2-1.4l34.3-44.2c2.7-3.5 2.1-8.5-1.4-11.2L534.2 77.7c-11.5-8.9-27.5-8.9-39-0.1l-416 318.5c-3.5 2.7-4.2 7.7-1.5 11.2l34 44.4c2.7 3.5 7.7 4.2 11.2 1.5l43.7-33.5V921c0 17.7 14.3 32 32 32h275.7c4.4 0 8-3.6 8-8v-48c0-4.4-3.6-8-8-8z" p-id="4875"></path></svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@ -0,0 +1 @@
<svg id="图层_1" data-name="图层 1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 48 48"><defs><style>.cls-1{fill:none;}.cls-2{clip-path:url(#clip-path);}</style><clipPath id="clip-path"><rect class="cls-1" x="1" y="2.84" width="46" height="42.33"/></clipPath></defs><title>画板 1</title><g class="cls-2"><path d="M6,37.68A5,5,0,0,1,1,32.74V28.67a1.65,1.65,0,1,1,3.3,0v4.07A1.65,1.65,0,0,0,6,34.39H42.07a1.65,1.65,0,0,0,1.64-1.65v-25a1.65,1.65,0,0,0-1.64-1.65H6A1.65,1.65,0,0,0,4.34,7.79V12A1.65,1.65,0,1,1,1,12V7.79A5,5,0,0,1,6,2.85H42.07A5,5,0,0,1,47,7.79v25a4.94,4.94,0,0,1-4.94,4.94Zm4.94,7.42a1.65,1.65,0,1,1,0-3.29h26.2a1.65,1.65,0,0,1,0,3.29Zm0,0"/></g><path d="M23.52,21.43l-5.71,5.71a1.63,1.63,0,0,1-1.17.49,1.65,1.65,0,0,1-1.16-.49,1.64,1.64,0,0,1,0-2.33l2.9-2.9H3.69a1.65,1.65,0,0,1,0-3.29H18.38l-2.9-2.9a1.65,1.65,0,1,1,2.33-2.33l5.71,5.71A1.65,1.65,0,0,1,23.52,21.43Z"/></svg>

After

Width:  |  Height:  |  Size: 940 B

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1617874168481" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2954" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M966.4 731.733333V78.933333H57.6v652.8H469.333333v125.866667H57.6v85.333333h908.8v-85.333333H554.666667v-125.866667h411.733333z m-85.333333-567.466666v204.8H142.933333V164.266667h738.133334zM142.933333 456.533333h738.133334v192H142.933333v-192z" p-id="2955"></path><path d="M627.2 230.4h194.133333v85.333333h-194.133333zM200.533333 230.4h85.333334v85.333333h-85.333334zM627.2 514.133333h194.133333v85.333334h-194.133333zM200.533333 514.133333h85.333334v85.333334h-85.333334z" p-id="2956"></path></svg>

After

Width:  |  Height:  |  Size: 878 B

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1617875310508" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3324" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M978.814586 886.315239h-902.491982V107.642238c0-17.14978-13.987409-31.13719-31.13719-31.13719-17.14978 0-31.13719 13.987409-31.13719 31.13719v809.931821c0 17.14978 13.987409 31.13719 31.13719 31.137189h933.629172c17.14978 0 31.13719-13.987409 31.13719-31.137189 0-17.27141-13.987409-31.258819-31.13719-31.25882z" p-id="3325"></path><path d="M141.759354 692.9241h82.100012c20.312151 0 35.637487-10.825039 46.340895-27.974819 6.081482-9.730372 9.973631-19.582373 15.933484-37.583561 0.12163-0.364889 2.675852-8.149186 3.40563-10.33852 5.108445-15.203706 8.757335-23.839411 12.771113-29.434374 2.432593-3.52726 3.284-4.013778 4.986816-4.013779 1.581185 0 2.067704 0.243259 4.257037 3.162371 3.770519 4.865186 7.176149 11.919705 13.622521 27.245041 0 0.12163 1.581185 3.892149 2.067704 4.986816 9.122224 21.771707 15.082076 33.569783 24.812448 45.367858 14.960447 18.122817 34.786079 28.339708 59.112008 28.339708 12.041335 0 22.744744-4.743556 31.988598-12.649483 6.568001-5.594964 12.527854-12.771113 18.122817-21.650078 9.122224-14.230669 18.122817-33.326523 27.60993-58.138971 8.027557-20.79867 16.420002-45.367858 26.150374-76.018529 6.93289-21.771707 7.297779-23.231263 26.150374-85.505642 22.258225-73.464307 34.664449-112.020905 48.28697-146.685355 8.149186-20.67704 15.690224-36.975413 22.623115-48.530229 2.432593-4.013778 4.621927-7.297779 6.68963-9.973631 3.52726 4.500297 7.419408 10.825039 11.676446 18.852595 11.068298 20.920299 23.109633 51.449341 37.461932 93.411569 8.270816 24.447559 15.082076 45.732747 29.920893 93.04668 27.73156 88.424754 39.772895 124.548759 55.341489 162.01069 10.33852 24.934078 20.190521 44.273192 30.407412 58.62549 6.324742 8.878964 12.892743 16.055113 20.068892 21.528448 9.608742 7.297779 20.433781 11.554816 32.231857 11.554816h142.793205c17.14978 0 31.13719-13.987409 31.13719-31.137189s-13.987409-31.13719-31.13719-31.13719h-138.657797c-1.459556-1.459556-3.40563-3.648889-5.594964-6.81126-7.05452-9.852001-14.960447-25.420596-23.717781-46.462526-14.473928-34.907709-26.272004-70.301936-53.395416-156.658985-14.960447-47.922081-21.893337-69.450529-30.407411-94.506236-15.203706-44.88134-28.339708-77.964604-41.35408-102.533793-8.027557-15.082076-16.055113-27.123411-24.569189-36.245635-12.041335-12.649483-25.907115-20.312151-42.205488-20.312151-7.541038 0-14.838817 1.824445-21.771707 4.986816-14.838817 7.05452-26.880152 20.433781-38.313338 39.651265-9.000594 14.960447-17.879558 34.177931-27.123412 57.774082-14.473928 36.732153-27.245041 76.261789-49.989785 151.428911-29.191115 96.69557-37.461931 122.724314-50.719563 157.145504-8.149186 21.163559-15.568595 36.975413-21.893336 46.949044-1.094667 1.824445-2.189334 3.284-3.284001 4.621927-1.946074-0.851408-3.648889-2.310963-5.594963-4.743556-4.621927-5.594964-8.635705-13.500891-15.325336-29.677634l-2.067704-4.865186c-8.514075-20.67704-13.62252-30.89393-21.893337-41.59734-13.379261-17.27141-31.258819-27.366671-53.517044-27.36667-23.474522 0-42.327117 11.311557-55.706379 30.164152-8.514075 12.041335-13.987409 24.934078-21.041929 45.732748-0.851408 2.432593-3.40563 10.33852-3.527259 10.460149-3.892149 11.919705-6.568001 18.609336-8.878965 22.866374H141.759354c-17.14978 0-31.13719 13.987409-31.13719 31.13719s13.86578 31.502079 31.13719 31.502078z" p-id="3326"></path></svg>

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1617874899211" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3085" width="200" height="200" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><style type="text/css"></style></defs><path d="M430.4 252.8l91.2-92.8c30.4-32 73.6-48 116.8-46.4 38.4 1.6 76.8 11.2 112 30.4l28.8 16-22.4 24-100.8 100.8c-11.2 11.2-17.6 25.6-19.2 41.6 0 16 6.4 32 17.6 43.2 11.2 11.2 27.2 17.6 43.2 17.6 16 0 32-6.4 43.2-19.2l123.2-121.6 16 30.4c44.8 84.8 41.6 177.6-14.4 233.6l-91.2 94.4c-24 22.4-56 35.2-88 35.2-40-3.2-78.4-12.8-115.2-30.4l-240 265.6c-22.4 25.6-56 40-89.6 38.4-70.4 0-126.4-56-126.4-126.4 0-33.6 14.4-67.2 38.4-89.6l260.8-238.4c-48-65.6-40-152 16-206.4z m348.8 148.8c-20.8 20.8-49.6 32-78.4 32-28.8 0-57.6-11.2-78.4-32-20.8-20.8-32-48-32-78.4 0-28.8 11.2-56 32-76.8l73.6-76.8c-19.2-6.4-40-9.6-59.2-8-30.4-1.6-59.2 9.6-81.6 32L464 286.4c-44.8 49.6-43.2 99.2 0 158.4l11.2 16-14.4 16-275.2 254.4c-14.4 14.4-22.4 33.6-22.4 52.8 0 41.6 35.2 76.8 76.8 76.8 20.8 0 40-8 52.8-22.4L561.6 544l17.6 9.6c33.6 19.2 70.4 30.4 108.8 33.6 19.2 0 38.4-8 54.4-20.8l91.2-94.4c33.6-40 41.6-96 19.2-142.4l-73.6 72z" fill="" p-id="3086"></path><path d="M204.8 788.8c0 16 12.8 30.4 30.4 30.4 16 0 30.4-12.8 30.4-30.4 0-16-12.8-30.4-30.4-30.4-17.6 1.6-30.4 14.4-30.4 30.4z" fill="" p-id="3087"></path></svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1617876370385" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7119" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M184.32 420.352h425.984V665.6H184.32V420.352zM430.08 230.4h180.224v119.808H430.08V230.4z m-245.76 0h180.224v119.808H184.32V230.4z m490.496 0H839.68v119.808h-164.864V230.4zM839.68 420.352V665.6h-164.864V420.352H839.68z" p-id="7120"></path><path d="M864.256 64H160.256c-53.248 0-96.256 43.008-96.256 96.256v576c0 53.248 43.008 96.256 96.256 96.256h704c53.248 0 96.256-43.008 96.256-96.256V160.256c-0.512-53.248-43.52-96.256-96.256-96.256z m31.744 672.256c0 17.92-14.336 32.256-32.256 32.256H160.256c-17.92 0-32.256-14.336-32.256-32.256V160.256c0-17.92 14.336-32.256 32.256-32.256h704c17.92 0 32.256 14.336 32.256 32.256v576zM911.872 896H112.128c-17.92 0-32.256 14.336-32.256 32.256s14.336 32.256 32.256 32.256h800.256c17.92 0 32.256-14.336 32.256-32.256s-14.848-32.256-32.768-32.256z" p-id="7121"></path></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1617881379579" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4741" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M512.276804 960.25621c-130.992403 0-374.619096-200.418612-374.619096-389.93492L137.657708 192.791911l37.249369-0.617054c0.794086 0 83.944877-1.766226 169.743986-36.589336 88.00331-35.748179 144.760988-76.794014 145.293107-77.190033l22.242584-16.198937 22.419616 16.198937c0.533143 0.396019 57.287751 41.442878 145.291061 77.190033 85.802178 34.82311 168.9499 36.589336 169.83506 36.589336l37.072337 0.617054 0.174985 377.530402C886.979812 759.836575 643.356189 960.25621 512.276804 960.25621zM213.305619 265.173428l0 305.147862c0 146.527215 212.20096 314.285986 298.972209 314.285986 86.768179 0 298.969139-167.758772 298.969139-314.285986L811.246967 265.173428c-37.074384-4.019547-97.273441-14.123672-159.771867-39.502689-64.080457-26.040073-112.718201-54.372352-139.197272-71.232344-26.39516 16.859993-75.120908 45.192272-139.115408 71.232344C310.577013 251.049756 250.377956 261.153882 213.305619 265.173428z" p-id="4742"></path><path d="M492.236376 712.171995 333.17366 576.677045 382.07644 519.125281 484.116441 605.983511 677.783279 382.880247 734.978932 432.487062Z" p-id="4743"></path></svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1617875580498" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3586" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M874.666667 138.666667H550.4v-64c0-23.466667-19.2-42.666667-42.666667-42.666667s-42.666667 19.2-42.666666 42.666667v66.133333H142.933333c-46.933333 0-85.333333 38.4-85.333333 85.333333v445.866667c0 46.933333 38.4 85.333333 85.333333 85.333333h324.266667v25.6c-6.4 2.133333-14.933333 6.4-21.333333 10.666667l-162.133334 121.6c-19.2 14.933333-23.466667 40.533333-8.533333 59.733333 8.533333 10.666667 21.333333 17.066667 34.133333 17.066667 8.533333 0 17.066667-2.133333 25.6-8.533333l162.133334-121.6c4.266667-2.133333 14.933333-2.133333 17.066666 0l168.533334 121.6c19.2 12.8 44.8 8.533333 59.733333-8.533334 12.8-19.2 8.533333-44.8-8.533333-59.733333l-168.533334-121.6c-4.266667-2.133333-8.533333-6.4-12.8-8.533333v-27.733334H874.666667c46.933333 0 85.333333-38.4 85.333333-85.333333v-448c0-46.933333-38.4-85.333333-85.333333-85.333333z m0 533.333333H142.933333v-448H874.666667v448z" p-id="3587"></path><path d="M209.066667 582.4c10.666667 0 21.333333-4.266667 29.866666-12.8l140.8-134.4c2.133333-2.133333 6.4-2.133333 6.4 0l151.466667 125.866667c34.133333 27.733333 87.466667 27.733333 119.466667-2.133334l174.933333-149.333333c17.066667-14.933333 19.2-42.666667 4.266667-59.733333-14.933333-17.066667-42.666667-19.2-59.733334-4.266667l-174.933333 149.333333c-2.133333 2.133333-8.533333 2.133333-10.666667 0l-151.466666-125.866666C405.333333 341.333333 352 341.333333 320 373.333333l-140.8 134.4c-17.066667 17.066667-17.066667 42.666667-2.133333 59.733334 10.666667 10.666667 21.333333 14.933333 32 14.933333z" p-id="3588"></path></svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1617878226642" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1442" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M841.38496 862.56128c-65.87392-11.76576-117.63712-68.22912-117.63712-138.80832 0-77.6448 63.51872-141.16352 141.16352-141.16352 77.63968 0 141.16352 63.51872 141.16352 141.16352 0 70.5792-51.75808 127.04256-117.64224 138.80832v131.75296c0 14.12096-9.40544 23.53152-23.5264 23.53152s-23.53152-9.41056-23.53152-23.53152v-131.75296z m-705.82784 0C69.67808 850.79552 17.92 794.33216 17.92 723.74784c0-77.6448 63.52384-141.16352 141.16352-141.16352s141.16864 63.51872 141.16864 141.16352c0 70.5792-51.75808 127.04256-117.64224 138.80832v131.75296c0 14.12096-9.41056 23.53152-23.5264 23.53152-14.11584 0-23.5264-9.41056-23.5264-23.53152v-131.74784zM488.4736 161.43872c-65.87904 11.76576-117.63712 68.22912-117.63712 138.81344 0 77.6448 63.52384 141.16352 141.16352 141.16352 77.63968 0 141.16352-63.52384 141.16352-141.16352 0-70.58432-51.75808-127.04768-117.63712-138.81344V29.68576c0-14.11584-9.41056-23.53152-23.53152-23.53152-14.11584 0-23.5264 9.41056-23.5264 23.53152v131.75296zM135.55712 29.68576c0-14.11584 9.41056-23.53152 23.5264-23.53152 14.11584 0 23.5264 9.41056 23.5264 23.53152v470.54848c0 14.11584-9.41056 23.53152-23.5264 23.53152-14.11584 0-23.5264-9.41056-23.5264-23.53152V29.68576z m705.82784 0c0-14.11584 9.41056-23.53152 23.53152-23.53152s23.5264 9.41056 23.5264 23.53152v470.54848c0 14.11584-9.40544 23.53152-23.5264 23.53152s-23.53152-9.41056-23.53152-23.53152V29.68576zM512 394.36288c-51.75808 0-94.11072-42.35264-94.11072-94.11072S460.2368 206.14144 512 206.14144s94.11072 42.35264 94.11072 94.11072S563.75808 394.36288 512 394.36288z m-23.5264 129.40288c0-14.11584 9.41056-23.53152 23.5264-23.53152 14.12096 0 23.53152 9.41056 23.53152 23.53152v470.54848c0 14.12096-9.41056 23.53152-23.53152 23.53152-14.11584 0-23.5264-9.41056-23.5264-23.53152v-470.54848z m-329.39008 294.0928c51.75808 0 94.11072-42.34752 94.11072-94.11072s-42.34752-94.11072-94.11072-94.11072-94.11072 42.34752-94.11072 94.11072 42.35264 94.11072 94.11072 94.11072z m705.83296 0c51.75808 0 94.11072-42.34752 94.11072-94.11072s-42.34752-94.11072-94.11072-94.11072-94.11072 42.34752-94.11072 94.11072 42.34752 94.11072 94.11072 94.11072z" fill="" p-id="1443"></path></svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1617880389215" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1834" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M383.832073 0c205.925907 0 373.980384 54.05635 383.38427 121.930655L767.664147 127.944024v128.455801a976.852627 976.852627 0 0 0-63.780096 0v-22.390204c-26.420441 11.898794-57.574811 22.006372-92.247642 30.19479-25.077029 3.326545-48.682701 7.676641-70.561129 13.05029C492.584494 284.22765 439.487724 287.874055 383.832073 287.874055c-128.455801 0-243.349535-19.447492-319.668145-53.73649l-0.127944 184.111451 9.21197 5.181733c13.306179 7.036921 29.746986 14.073843 49.258449 20.471044C190.636596 466.675829 283.90779 479.790092 383.832073 479.790092v63.972012c-128.583745 0-243.477479-19.447492-319.796089-53.73649L63.972012 674.009121l9.339914 5.245705c13.306179 7.036921 29.746986 14.073843 49.258449 20.471044 68.066221 22.710064 161.401387 35.888299 261.32567 35.888299v63.972012c-128.583745 0-243.733367-19.511464-319.988005-53.864434l-0.063972 145.792216 1.27944 0.31986 7.676642 5.501593c14.073843 10.107578 36.400075 20.471044 65.123508 29.683013C202.151559 947.553445 289.857187 959.580184 383.832073 959.580184v63.972012c-205.925907 0-373.980384-54.05635-383.384269-121.930655L0 895.608171V127.944024l0.447804-6.013369C9.85169 54.05635 177.842194 0 383.832073 0z m351.846068 319.860061c131.782345 0 242.90173 27.060161 277.062785 63.972012H1023.552196v23.413757l0.063972 0.575748v527.769101c0 48.618729-128.903605 87.961517-287.874055 87.961517-153.660773 0-279.237833-36.783907-287.426251-83.163616L447.804086 935.590679v-127.432248h-0.127944L447.804086 735.678141V383.832073h10.875242c34.161055-36.911851 145.216468-63.972012 277.062785-63.972012z m-224.157931 532.18317v68.194165l3.518461 2.175049c7.612669 4.158181 17.912163 8.444306 30.386705 12.474542l9.787718 3.070657c46.955457 13.626039 111.311301 21.62254 180.465047 21.62254 71.392766 0 137.731742-8.57225 184.879115-23.029925 13.370151-4.030237 24.437309-8.316362 32.625726-12.538514l6.397202-3.582433v-68.258137c-57.958643 27.571937-137.283938 43.436996-223.902043 43.436996-86.682077 0-166.135316-15.865059-224.157931-43.56494z m448.25189-191.916037c-58.022615 27.699881-137.411882 43.56494-224.093959 43.564941s-166.071344-15.865059-224.093959-43.500969v118.987943c43.24508 29.299182 126.600612 52.45705 224.093959 52.45705 97.429375 0 180.656963-23.157868 223.966015-52.393078l0.127944-119.051915z m0-159.93003c-57.958643 27.699881-137.411882 43.56494-224.093959 43.56494-86.618105 0-166.007372-15.865059-224.029987-43.500968v87.065908c43.373024 29.23521 126.664584 52.393078 224.029987 52.393078 97.493347 0 180.784907-23.157868 224.029987-52.45705l0.127944-87.001936zM735.678141 383.832073c-69.089773 0-133.50959 7.996502-180.401075 21.622541-17.016555 4.989817-30.706566 10.363466-40.238395 15.545199L511.776098 422.918973v4.478041c43.309052 29.299182 126.53664 52.393078 223.902043 52.393078 97.493347 0 180.848879-23.157868 224.093959-52.45705v-4.222153l-3.454489-2.111076a184.623227 184.623227 0 0 0-30.386706-12.474543l-9.787718-3.070656C869.18773 391.828575 804.767914 383.832073 735.678141 383.832073zM383.832073 63.972012c-94.038858 0-181.680515 11.962766-245.972387 32.497782-28.723433 9.147998-51.049666 19.575436-65.123508 29.683014l-6.908977 4.797901-1.087525 0.255888-0.511776-0.575748v31.79409l9.020054 5.117761c13.306179 7.036921 29.746986 14.073843 49.258449 20.471044C190.636596 210.78778 283.90779 223.902043 383.832073 223.902043c99.924283 0 193.259449-13.178235 261.32567-35.824327 24.501281-8.188418 44.140688-17.080527 58.79028-25.908665l-0.127944-30.258762-1.279441-0.255888-7.612669-5.501593c-14.073843-10.107578-36.400075-20.471044-65.123508-29.683014C565.512588 75.998751 477.806959 63.972012 383.832073 63.972012z" p-id="1835"></path></svg>

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1617881304895" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4610" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M898.133333 57.6H119.466667c-36.266667 0-64 27.733333-64 64v780.8c0 36.266667 27.733333 64 64 64h780.8c36.266667 0 64-27.733333 64-64V121.6c-2.133333-34.133333-29.866667-64-66.133334-64z m-21.333333 823.466667H140.8V142.933333h738.133333v738.133334z" p-id="4611"></path><path d="M535.466667 407.466667h264.533333c23.466667 0 42.666667-19.2 42.666667-42.666667s-19.2-42.666667-42.666667-42.666667H535.466667c-23.466667 0-42.666667 19.2-42.666667 42.666667s19.2 42.666667 42.666667 42.666667zM535.466667 693.333333h264.533333c23.466667 0 42.666667-19.2 42.666667-42.666666s-19.2-42.666667-42.666667-42.666667H535.466667c-23.466667 0-42.666667 19.2-42.666667 42.666667s19.2 42.666667 42.666667 42.666666zM251.733333 428.8c12.8 12.8 29.866667 19.2 44.8 19.2 17.066667 0 32-6.4 44.8-19.2l91.733334-89.6c17.066667-17.066667 17.066667-42.666667 0-59.733333s-42.666667-17.066667-59.733334 0l-76.8 74.666666-27.733333-27.733333c-17.066667-17.066667-42.666667-17.066667-59.733333 0-17.066667 17.066667-17.066667 42.666667 0 59.733333l42.666666 42.666667zM296.533333 765.866667c61.866667 0 113.066667-51.2 113.066667-113.066667s-51.2-113.066667-113.066667-113.066667-113.066667 51.2-113.066666 113.066667 49.066667 113.066667 113.066666 113.066667z m0-140.8c14.933333 0 27.733333 12.8 27.733334 27.733333 0 14.933333-12.8 27.733333-27.733334 27.733333s-27.733333-12.8-27.733333-27.733333c0-14.933333 10.666667-27.733333 27.733333-27.733333z" p-id="4612"></path></svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

13
src/layouts/app.vue Normal file
View File

@ -0,0 +1,13 @@
<template>
<keep-alive :include="cachedViews" :max="cacheViewMax">
<router-view></router-view>
</keep-alive>
</template>
<script lang="ts" setup>
import { useStore } from 'vuex'
import { computed } from 'vue'
import { cacheViewMax } from '@/config'
const store = useStore()
const cachedViews = computed(() => store.state.tagsView.cachedViews)
</script>

7
src/layouts/blank.vue Normal file
View File

@ -0,0 +1,7 @@
<script>
export default {
render: function (h) {
return h()
},
}
</script>

View File

@ -0,0 +1,91 @@
/** * Created by HaijunZhang on 2019/12/18. */
<template>
<a-layout-header class="common-header" :style="style">
<div class="header-logo">
<img :src="pageConfigs.headerLogo" alt="" class="pull-left" />
</div>
<Divider class="split-line" type="vertical"></Divider>
<router-link to="/sms-web/resource_dashboard">
<HomeOutlined class="home-icon" />
</router-link>
<HeaderMenu :page-configs="pageConfigs"></HeaderMenu>
<RightContent />
</a-layout-header>
</template>
<script lang="ts">
import RightContent from './rightContent.vue'
import HeaderMenu from './headerMenu.vue'
import { computed, defineComponent } from 'vue'
import { HomeOutlined } from '@ant-design/icons-vue'
import { Divider } from 'ant-design-vue'
import { useStore } from 'vuex'
export default defineComponent({
components: {
RightContent,
Divider,
HeaderMenu,
HomeOutlined,
},
setup() {
const store = useStore()
const pageConfigs = computed(() => store.getters.pageConfig)
const style = computed(() => {
return {
backgroundColor: pageConfigs.value.headerBgColour,
color: pageConfigs.value.headerFontColour,
}
})
return {
pageConfigs,
style,
}
},
})
</script>
<style lang="scss" scoped>
.common-header {
display: flex;
align-items: center;
-webkit-box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
box-sizing: border-box;
width: 100%;
height: 50px !important;
font-size: 22px;
line-height: 50px;
padding: 0 0 0 10px;
overflow: hidden;
background: #2c2e3b;
color: #b9b9ba;
& ::v-deep .el-dropdown {
color: inherit !important;
}
.header-logo {
min-width: 120px;
max-width: 260px;
img {
height: 30px;
margin: 4.5px 10px;
}
}
.home-icon {
font-size: 18px;
margin-right: 15px;
}
.split-line {
height: 20px;
margin-left: 30px;
margin-right: 15px;
border-color: #999999;
}
.toggle-class {
height: 50px;
line-height: 43px;
cursor: pointer;
margin-left: 15px;
}
}
</style>

View File

@ -0,0 +1,354 @@
<template>
<a-dropdown>
<span class="m-r">
<QuestionCircleOutlined class="cur-point" />
</span>
<template #overlay>
<a-menu>
<!-- <router-link to="/personal/documents">
<a-menu-item>帮助中心</a-menu-item>
</router-link> -->
<a-menu-item @click="visible = true">新手入门</a-menu-item>
</a-menu>
</template>
</a-dropdown>
<a-modal v-model:visible="visible" width="900px" :footer="null" wrapClassName="guid-modal">
<div class="guid-wrapper">
<div class="guid-title">新手指引</div>
<div class="guid-desc">当前还没有资源接入可按照下方新手指引完成接入和系统配置此页面关闭后可在新手指引中查找</div>
<div class="guid-content">
<div class="guid-cell">
<div class="system-title">管理端</div>
<div class="guid-flow-wrapper">
<div class="guid-flow" v-for="(item, key) in list" :key="key">
<div class="guid-flow-item">
<div class="guid-flow-item-number">{{ key + 1 }}</div>
<span>{{ item }}</span>
</div>
<div class="guid-flow-line" v-if="key !== list.length - 1"></div>
</div>
<div class="guid-flow" v-for="(item, key) in list2" :key="key">
<div class="guid-flow-item">
<div class="guid-flow-item-number">{{ 6 - key }}</div>
<span>{{ item }}</span>
</div>
<div class="guid-flow-line left" v-if="key !== list2.length - 1"></div>
</div>
</div>
<div class="special-line">
<div class="line-cover"></div>
</div>
</div>
<div class="guid-cell">
<div class="system-title">控制台</div>
<div class="console-flow-item">
<div class="console-bg"></div>
<span class="console-text">一站式申请资源</span>
<div class="console-flow-item-number">7</div>
</div>
<div class="special-line console">
<div class="line-cover"></div>
</div>
</div>
</div>
<div class="guid-module">
<div class="guid-module-cell" v-for="(item, index) in moduleList" :key="index" @click="goPage(item.path)">
<svg-icon :icon-name="item.icon" class="icon"></svg-icon>
<span class="name">{{ item.name }}</span>
<RightOutlined />
</div>
</div>
</div>
</a-modal>
</template>
<script lang="ts">
import { ref, defineComponent } from 'vue'
import { RightOutlined, QuestionCircleOutlined } from '@ant-design/icons-vue'
import { useRouter } from 'vue-router'
import { useStore } from 'vuex'
import { handleSearchParam } from 'utils/index'
import { getTrack } from 'services/manager'
export default defineComponent({
components: { RightOutlined, QuestionCircleOutlined },
setup() {
const visible = ref(false)
const store = useStore()
;(async function () {
const res = await getTrack({
simple: true,
params: handleSearchParam({
account: store.getters.userData.account
})
})
if (res.data?.total === 1) {
visible.value = true
}
})()
const router = useRouter()
function goPage(path: string) {
visible.value = false
router.push(path)
}
return {
list: ['资源接入', '资源池', '作业编排'],
list2: ['创建租户', '资源配置', '服务发布'],
visible,
moduleList: [
{
icon: 'svg-resource-manage',
name: '资源纳管',
path: '/cmp-web/vendors'
},
{
icon: 'svg-service-ops',
name: '服务运营',
path: '/cos/products/catalogs'
},
{
icon: 'svg-resource-monitor',
name: '资源监控',
path: '/cms-web/dashboard'
},
{
icon: 'svg-ops-analysis',
name: '运营分析',
path: '/soa/resource/statistics'
}
],
goPage
}
}
})
</script>
<style lang="scss" scoped>
.guid-wrapper {
display: flex;
flex-direction: column;
align-items: center;
width: 900px;
height: 600px;
box-sizing: border-box;
padding: 43px 32px 28px 32px;
.guid-title {
width: 112px;
font-size: 28px;
font-weight: bold;
color: #1b181c;
line-height: 21px;
margin-bottom: 19px;
}
.guid-desc {
width: 560px;
text-align: center;
color: #3a3a3c;
line-height: 30px;
font-size: 20px;
margin-bottom: 48px;
}
.guid-content {
padding: 0 27px;
width: 100%;
.guid-cell {
position: relative;
display: flex;
align-items: center;
}
.guid-flow-wrapper {
display: flex;
flex-wrap: wrap;
margin-left: 62px;
flex: 1;
.guid-flow {
display: flex;
align-items: center;
justify-content: flex-start;
margin-bottom: 22px;
}
}
.guid-flow-item {
position: relative;
width: 143px;
height: 57px;
display: flex;
align-items: center;
justify-content: center;
background: #f2f4f8;
border-radius: 16px;
font-weight: 400;
color: #3a3a3c;
.guid-flow-item-number {
position: absolute;
top: 16px;
text-align: center;
left: -12px;
width: 24px;
color: #fff;
height: 24px;
line-height: 24px;
border-radius: 12px;
background: #1e54d5;
}
}
.guid-flow-line {
position: relative;
width: 44px;
box-sizing: border-box;
border: 1px dashed #1e54d5;
margin: 0 23px 0 11px;
&::after {
position: absolute;
right: -10px;
top: -5px;
content: '';
opacity: 1;
border: 6px solid transparent;
border-left-color: #1e54d5;
}
&.left::after {
left: -10px;
right: auto;
border-left-color: transparent;
border-right-color: #1e54d5;
}
}
.console-flow-item {
position: relative;
margin-left: 92px;
width: 530px;
height: 76px;
display: flex;
align-items: center;
justify-content: center;
background: #f5faff;
border-radius: 5px;
.console-flow-item-number {
position: absolute;
top: 21px;
text-align: center;
left: -12px;
width: 34px;
color: #1e54d5;
font-size: 21px;
border: 1px solid #1e54d5;
height: 34px;
line-height: 34px;
border-radius: 17px;
}
.console-text {
position: absolute;
font-weight: bold;
color: #3a3a3c;
font-size: 20px;
}
.console-bg {
position: absolute;
overflow: hidden;
bottom: 10px;
width: 530px;
height: 65px;
&::after {
position: absolute;
left: -135px;
bottom: 0;
content: '一站式申请资源';
width: 800px;
height: 800px;
border-radius: 330px;
background: #f0f7ff;
}
}
}
}
.guid-module {
display: flex;
justify-content: space-between;
margin-top: 75px;
.guid-module-cell {
display: flex;
align-items: center;
box-sizing: border-box;
cursor: pointer;
color: #fff;
width: 200px;
height: 72px;
opacity: 1;
background: #4e7be9;
border-radius: 4px;
padding: 0 16px;
&:not(:last-child) {
margin-right: 12px;
}
&:nth-child(even) {
background: #5ec4ec;
}
.icon {
font-size: 41px;
}
.name {
font-size: 16px;
margin-left: 15px;
margin-right: 6px;
}
}
}
.system-title {
width: 119px;
height: 57px;
line-height: 57px;
text-align: center;
border-radius: 4px;
border: 1px solid #1e54d5;
font-weight: bold;
color: #1e54d5;
font-size: 16px;
}
}
.special-line {
position: absolute;
right: -15px;
top: 28px;
width: 20px;
height: 80px;
border: 1px dashed #1e54d5;
border-radius: 10px;
border-left: transparent;
&.console {
transform: rotateY(180deg);
left: 142px;
top: -50px;
height: 90px;
right: auto;
.line-cover {
height: 91px;
}
}
.line-cover {
background: #fff;
width: 15px;
height: 81px;
position: absolute;
border-top: 1px dashed #1e54d5;
border-bottom: 1px dashed #1e54d5;
top: -1px;
right: 8px;
&::after {
position: absolute;
left: -7px;
bottom: -6px;
content: '';
opacity: 1;
border: 6px solid transparent;
border-right-color: #1e54d5;
}
}
}
</style>
<style lang="scss">
.guid-modal {
z-index: 99999;
.ant-modal-body {
padding: 0;
}
}
</style>

View File

@ -0,0 +1,147 @@
<template>
<a-dropdown class="system-letter">
<span class="m-r-md">
<a-badge :count="totalMessage" class="item cur-point">
<MailOutlined class="message-icon" />
</a-badge>
</span>
<template #overlay>
<a-menu class="message-container list-group">
<a-card title="我的消息">
<template #extra>
<a-button type="link" @click="goProfileMessage('')"></a-button>
</template>
<div v-for="item in messageList" :key="item.id" class="list-group-item" @click="goProfileMessage(item.id)">
<span class="text-ellipsis">
<i class="dot dot-warning m-r-xs"></i>
{{ item.name }}
</span>
<a-tooltip :title="item.content">
<small class="text-content">{{ item.content }}</small>
</a-tooltip>
</div>
<a-empty v-if="messageList.length === 0"></a-empty>
</a-card>
</a-menu>
</template>
</a-dropdown>
</template>
<script>
import { ref } from 'vue'
import { notification } from 'ant-design-vue'
import { setToken } from 'utils/auth'
import useWebsocket from '@/hooks/useWebsocket'
import { getMessage } from '@/services/message'
import { MailOutlined } from '@ant-design/icons-vue'
import { handleSearchParam } from 'utils'
import { useStore } from 'vuex'
import { useRouter, useRoute } from 'vue-router'
export default {
components: { MailOutlined },
setup() {
//
const messageList = ref([])
const totalMessage = ref(0)
async function loadMessage() {
const data = await getMessage({
page: 1,
rows: 5,
params: handleSearchParam({
status: 'UNREAD'
})
})
if (data.success) {
messageList.value = data.data.rows
totalMessage.value = data.data.total
}
}
loadMessage()
function handleServiceMessage(data) {
if (data.operate === 'refresh.token') {
setToken(data.data)
}
}
function messageCommonFun(data) {
if (data.operate !== 'HeartBeat') console.log(data)
switch (data.category) {
case 'MachineDiscovered': //
case 'ResourceEvent': //
notification[data.success ? 'success' : 'error']({
message: data.message
})
break
case 'ServiceEvent': //
handleServiceMessage(data)
break
case 'SiteMessage': //
loadMessage()
if (data.operate !== 'message.change') {
notification.info({
message: '您有一条新的消息!',
description: data.message
})
}
break
}
}
const router = useRouter()
const route = useRoute()
function goProfileMessage(id) {
if (route.path !== '/personal/message') router.push({ path: '/personal/message', query: { id } })
}
const { webSocket } = useWebsocket('', messageCommonFun)
const store = useStore()
store.commit('SET_WEBSOCKET', webSocket)
return {
messageList,
totalMessage,
goProfileMessage
}
}
}
</script>
<style lang="scss" scoped>
.system-letter {
.message-icon {
font-size: 20px;
color: #98a6ad;
}
::v-deep(.ant-badge-count) {
box-shadow: none;
}
}
.message-container {
padding: 0 !important;
width: 300px;
.list-group-item {
position: relative;
display: block;
padding: 10px 0;
background-color: #fff;
border-bottom: 1px solid #e7ecee;
cursor: pointer;
.dot {
width: 10px;
height: 10px;
border-radius: 50%;
border-width: 3px;
display: inline-block;
}
.dot-warning {
background-color: #fad733;
}
.text-content {
margin-top: 5px;
margin-left: 15px;
display: block;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
color: #98a6ad;
}
}
}
</style>

View File

@ -0,0 +1,63 @@
<template>
<div class="tip-container" v-show="tipText">{{ tipText }}使</div>
</template>
<script>
import { onUnmounted, ref } from 'vue'
import { getLicense } from '@/services/license'
import dayjs from 'utils/day'
import { useStore } from 'vuex'
import relativeTime from 'dayjs/plugin/relativeTime'
dayjs.extend(relativeTime)
const unit = 1000 * 60 * 60 * 24
export default {
setup(props, context) {
const store = useStore()
const tipText = ref()
const interval = unit * 7
const getInfo = async () => {
const res = await getLicense()
if (res.success) {
const { expireDate } = res.data
const nowTimer = dayjs().valueOf()
const expireTimer = dayjs(expireDate).valueOf()
const { commit } = store
if (expireTimer - nowTimer <= interval) {
const time = dayjs(expireDate).fromNow(true)
if (expireTimer - nowTimer >= 0) {
tipText.value = `您的证书即将过期,还剩 ${time} 时间可以进行证书激活`
} else {
tipText.value = `您的证书已经过期 ${time} `
}
commit('SET_EXPIRE', true)
} else {
commit('SET_EXPIRE', false)
}
// 30
if (expireTimer - nowTimer > unit * 30) {
clearInterval(timer)
}
}
}
getInfo()
const timer = setInterval(() => {
getInfo()
}, 1000 * 60 * 60)
onUnmounted(() => {
clearInterval(timer)
})
return {
tipText,
}
},
}
</script>
<style scoped lang="scss">
.tip-container {
text-align: center;
background: #c92100;
color: #fff;
font-weight: bold;
padding: 5px 0;
}
</style>

View File

@ -0,0 +1,139 @@
<template>
<div class="tags-view-container" id="tags_view_container">
<span v-if="scrollable" class="tags_nav_operate" :class="scrollablePrev ? '' : 'disabled'" @click="scrollPrev"><i class="el-icon-arrow-left"></i></span>
<div class="tags-nav-scroll" ref="navScrollRef">
<div class="tags-view-wrapper" ref="navRef" :style="navStyle">
<slot></slot>
</div>
</div>
<span v-if="scrollable" class="tags_nav_operate" :class="scrollableNext ? '' : 'disabled'" @click="scrollNext"><i class="el-icon-arrow-right"></i></span>
</div>
</template>
<script>
import { computed, onMounted, onUpdated, reactive, toRefs, ref, unref } from 'vue'
export default {
setup() {
const state = reactive({
navOffset: 50,
scrollable: false,
scrollablePrev: false,
scrollableNext: false
})
const navScrollRef = ref(null)
const navRef = ref(null)
const navStyle = computed(() => {
return {
transform: `translateX(-${state.navOffset}px)`
}
})
onMounted(() => {
update()
})
onUpdated(() => {
update()
})
//
const update = () => {
//
if (!unref(navRef) || !unref(navScrollRef)) return
const navWidth = navRef.value.offsetWidth
const containerWidth = navScrollRef.value.offsetWidth
const currentOffset = state.navOffset
if (containerWidth < navWidth) {
const currentOffset = state.navOffset
state.scrollable = state.scrollable || {}
state.scrollablePrev = currentOffset
state.scrollableNext = currentOffset + containerWidth < navWidth
if (navWidth - currentOffset < containerWidth) {
state.navOffset = navWidth - containerWidth
}
} else {
state.scrollable = false
if (currentOffset > 0) {
state.navOffset = 0
}
}
}
//
const scrollToactive = () => {
const $el = document.getElementById('tags_view_container')
const activeTab = $el.querySelector('.tags-item.active')
//
if (!activeTab) return
const activeTabBounding = activeTab.getBoundingClientRect()
const navScrollBounding = navScrollRef.value.getBoundingClientRect()
const currentOffset = state.navOffset
let newOffset = currentOffset
if (activeTabBounding.left < navScrollBounding.left) {
newOffset = currentOffset - (navScrollBounding.left - activeTabBounding.left)
}
if (activeTabBounding.right > navScrollBounding.right) {
newOffset = currentOffset + activeTabBounding.right - navScrollBounding.right
}
state.navOffset = newOffset
}
//
const scrollPrev = () => {
const currentOffset = state.navOffset
// 0
if (!currentOffset) return
const containerWidth = navScrollRef.value.offsetWidth
// 0
state.navOffset = currentOffset > containerWidth ? currentOffset - containerWidth : 0
}
//
const scrollNext = () => {
const containerWidth = navScrollRef.value.offsetWidth
const navWidth = navRef.value.offsetWidth
const currentOffset = state.navOffset
//
if (currentOffset + containerWidth >= navWidth) return
const newOffset = navWidth - currentOffset > containerWidth * 2 ? currentOffset + containerWidth : navWidth - containerWidth
state.navOffset = newOffset
}
return {
...toRefs(state),
navStyle,
navScrollRef,
navRef,
scrollPrev,
scrollNext,
scrollToactive
}
}
}
</script>
<style lang="scss" scoped>
.tags-view-container {
// height: 34px;
display: flex;
justify-content: center;
align-items: center;
width: 100%;
position: relative;
.tags-nav-scroll {
position: relative;
width: 100%;
overflow: hidden;
padding: 0 10px;
}
.tags-view-wrapper {
transition: transform 0.3s;
float: left;
white-space: nowrap;
}
.tags_nav_operate {
cursor: pointer;
font-size: 12px;
color: #909399;
background: #ccc;
padding: 6px;
&.disabled {
cursor: default;
}
}
}
</style>

View File

@ -0,0 +1,29 @@
<template>
<div class="select-round">
<div class="content"></div>
</div>
</template>
<script>
export default {}
</script>
<style lang="scss" scoped>
.select-round {
width: 10px;
height: 10px;
overflow: hidden;
position: absolute;
bottom: 0;
right: -10px;
transform: rotate(-90deg);
&.left {
bottom: 0;
left: -10px;
transform: rotate(180deg);
}
.content {
width: 20px;
height: 20px;
background: radial-gradient(rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0) 71%, #e7f4ff 0%);
}
}
</style>

View File

@ -0,0 +1,289 @@
<template>
<div id="tags-view-container" class="tags-view" v-show="isShow()">
<ScrollPanel ref="scrollRef">
<router-link v-for="(tag, index) in visitedViews" :key="tag.path" :to="tag" class="tags-item" :class="isActive(tag) && 'active'" @contextmenu.prevent.stop="openMenu(tag, index, $event)">
<span class="line"></span>
<span class="">{{ tag.title }}</span>
<i class="el-icon-close" v-if="!tag.meta.fix" @click.prevent.stop="closeSelectedTag(tag, index)"></i>
<span v-if="isActive(tag)">
<SelectRound class="left"></SelectRound>
<SelectRound></SelectRound>
</span>
</router-link>
</ScrollPanel>
<ul v-show="visible" :style="{ left: left + 'px', top: top + 'px' }" class="contextmenu">
<!-- <li @click="refreshSelectedTag(selectedTag)"></li> -->
<li @click="closeSelectedTag(selectedTag)" v-if="selectedTag.meta && !selectedTag.meta.fix"></li>
<li @click="closeOthersTags" v-if="visitedViews.length !== 1"></li>
<li @click="closeAllTags(selectedTag)"></li>
<li @click="closeLeftTags(selectedTag)" v-if="selectIndex !== 0"></li>
<li @click="closeRightTags(selectedTag)" v-if="selectIndex !== visitedViews.length - 1"></li>
</ul>
</div>
</template>
<script>
import { onMounted, reactive, toRefs, ref, computed, onUnmounted, watch } from 'vue'
import ScrollPanel from './ScrollPanel.vue'
import SelectRound from './SelectRound.vue'
import { resolvePath } from 'utils/resolvePath'
import { nth } from 'lodash-es'
import { useStore } from 'vuex'
import { useRoute, useRouter } from 'vue-router'
export default {
components: { ScrollPanel, SelectRound },
setup() {
const store = useStore()
const route = useRoute()
const router = useRouter()
const state = reactive({
visible: false,
top: 0,
left: 0,
selectedTag: {},
fixTags: [],
selectIndex: 0
})
const scrollRef = ref(null)
const visitedViews = computed(() => store.state.tagsView.visitedViews)
onMounted(() => {
addFixTags()
addTags()
document.body.addEventListener('click', closeMenu)
})
onUnmounted(() => {
document.body.removeEventListener('click', closeMenu)
})
watch(
() => route.path,
() => {
addTags()
}
)
//
const isActive = (routeItem) => {
const {
meta: { parentTag }
} = route
let { path } = route
if (parentTag) {
path = resolvePath(path, '..')
}
return routeItem.path === path
}
//
const isShow = () => {
return !['/sms-web/resource_dashboard', '/screen/list', '/redirect'].includes(route.path)
}
const getFixTags = (routes) => {
const tags = []
routes.forEach((item) => {
const { meta, name, path, params, children } = item
if (meta && meta.fix) {
tags.push({
meta,
name,
path,
params,
children
})
}
if (children && children.length) {
tags.push(...getFixTags(children))
}
})
return tags
}
const routes = computed(() => store.getters.addRoutes)
const addFixTags = () => {
const fixTags = getFixTags(routes.value)
fixTags.forEach((tag) => {
store.dispatch('tagsView/addView', tag)
})
}
const addTags = () => {
const {
meta: { parentTag },
matched
} = route
// parentTag tag
const currentRoute = parentTag ? nth(matched, -2) : { ...route }
const { path } = currentRoute
if (path) {
state.activeName = path
store.dispatch('tagsView/addView', currentRoute)
}
setTimeout(() => {
scrollRef.value && scrollRef.value.scrollToactive()
})
return false
}
//
const closeSelectedTag = (view, index = state.selectIndex) => {
state.selectIndex = index
store.dispatch('tagsView/delView', view).then(({ visitedViews }) => {
if (isActive(view)) {
toNextView(visitedViews)
}
})
}
//
const closeOthersTags = () => {
if (!isActive(state.selectedTag)) router.push(state.selectedTag)
store.dispatch('tagsView/delOthersViews', state.selectedTag)
}
//
const closeAllTags = (view) => {
store.dispatch('tagsView/delAllViews').then(({ visitedViews }) => {})
}
//
const closeLeftTags = (view) => {
const left = visitedViews.value.slice(0, state.selectIndex)
scrollToCurrent(left, view)
store.dispatch('tagsView/delLeftViews', { view, index: state.selectIndex })
}
//
const scrollToCurrent = (views, view) => {
const { path } = route
if (views.some((v) => v.path === path)) {
router.push(view.path)
}
}
//
const closeRightTags = (view) => {
const right = visitedViews.value.slice(state.selectIndex + 1)
scrollToCurrent(right, view)
store.dispatch('tagsView/delRightViews', { view, index: state.selectIndex })
}
//
const toNextView = (visitedViews) => {
//
const index = state.selectIndex - 1 > 0 ? state.selectIndex - 1 : 0
const latestView = visitedViews[index]
if (latestView) {
router.push(latestView.path)
} else {
router.push('/')
}
}
//
const openMenu = (tag, index, e) => {
const menuMinWidth = 105
const maxLeft = window.innerWidth - menuMinWidth // left boundary
const left = e.clientX + 15 // 15: margin right
if (left > maxLeft) {
state.left = maxLeft
} else {
state.left = left
}
state.top = e.clientY + 10
state.visible = true
state.selectedTag = tag
state.selectIndex = index
}
//
function closeMenu() {
state.visible = false
}
return {
...toRefs(state),
scrollRef,
isActive,
isShow,
visitedViews,
closeSelectedTag,
closeOthersTags,
closeAllTags,
closeLeftTags,
closeRightTags,
closeMenu,
openMenu
}
}
}
</script>
<style lang="scss" scoped>
.tags-view {
margin: 0px 0 12px 0;
width: 100%;
background: #fff;
border: 1px solid #ededec;
border-bottom: none;
.tags-item {
display: inline-block;
position: relative;
cursor: pointer;
height: 32px;
line-height: 32px;
color: #666666;
padding: 0 15px;
font-size: 12px;
&.active {
background-color: #e8efff;
color: $--color-primary;
border-radius: 6px 6px 0 0;
.line {
display: none;
}
}
&:nth-child(1),
&.active + .tags-item {
.line {
display: none;
}
}
.line {
width: 1px;
height: 20px;
background: #d9d9d9;
display: inline-block;
position: absolute;
top: 6px;
left: 0;
}
.el-icon-close {
overflow: hidden;
width: 14px;
height: 14px;
line-height: 14px;
text-align: center;
transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
position: relative;
top: 3px;
left: 2px;
border-radius: 50%;
&:hover {
background-color: #c0c4cc;
color: #fff;
}
}
&.active .el-icon-close,
&:hover .el-icon-close {
width: 14px;
}
}
.contextmenu {
margin: 0;
background: #fff;
z-index: 3000;
position: fixed;
list-style-type: none;
padding: 5px 0;
border-radius: 4px;
font-size: 12px;
font-weight: 400;
color: #333;
box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, 0.3);
li {
margin: 0;
padding: 7px 16px;
cursor: pointer;
&:hover {
background: #eee;
}
}
}
}
</style>

View File

@ -0,0 +1,122 @@
<template>
<a-layout-sider theme="light" width="160px" class="third-menu-container" :class="{ collapsed: isCollapsed }">
<div class="menu-title">{{ menuData.meta.title }}</div>
<el-scrollbar class="scrollbar-wrapper">
<BaseMenu v-bind="menuProps"></BaseMenu>
</el-scrollbar>
<!-- <a href="javascript:;" class="subMenuToggle" @click="toggle"></a> -->
</a-layout-sider>
</template>
<script>
import { computed } from 'vue'
import BaseMenu from './sidebar/BaseMenu.vue'
export default {
components: { BaseMenu },
props: {
menuData: {
type: Object
}
},
data() {
return {
isCollapsed: false
}
},
setup(props) {
const menuProps = computed(() => {
return {
mode: 'inline',
limitLevel: 6,
defaultIcon: '',
menuData: props.menuData.children,
inlineIndent: 20
}
})
return {
menuProps
}
},
methods: {
toggle() {
this.isCollapsed = !this.isCollapsed
},
selectItem(path) {
if (this.$route.path.indexOf(path) > -1) {
setTimeout(() => {
this.$router.push({ name: 'Redirect', query: { path: this.$route.path } })
})
}
}
}
}
</script>
<style lang="scss" scoped>
.expire-top .third-menu {
height: calc(100vh - 180px) !important;
}
.third-menu-container.collapsed {
background: transparent !important;
border: 0;
margin-left: -160px;
.third-menu,
.menu-title {
visibility: hidden;
}
.subMenuToggle {
background: url(/web-common-resource/img/slideIcon-new.png) no-repeat right -110px;
right: 0;
}
}
.third-menu-container {
background: var(--czhj-color-background);
transition: margin 0.28s;
border-left: 1px solid var(--czhj-color-background-selected);
.scrollbar-wrapper {
height: calc(100% - 40px);
::v-deep .el-scrollbar__wrap {
overflow-x: hidden !important;
}
}
::v-deep(.ant-menu-item) {
height: 38px;
line-height: 38px;
font-size: 12px;
}
.third-menu {
height: calc(100vh - 150px);
border: 0;
}
}
.subMenuToggle {
display: block;
position: absolute;
z-index: 2;
left: 146px;
top: 50%;
width: 13px;
height: 0;
margin: -25px 0 0;
padding: 50px 0 0;
background: url(/web-common-resource/img/slideIcon-new.png) no-repeat 0 -60px;
overflow: hidden;
transition: width 0.1s, right 0.1s;
&:hover {
left: 142px;
width: 20px;
z-index: 3;
}
}
.menu-title {
visibility: visible;
height: 40px;
line-height: 40px;
font-size: 14px;
background: var(--czhj-color-background);
border-bottom: 1px solid var(--czhj-color-background-selected);
color: var(--czhj-color-text);
padding-left: 20px;
}
</style>

View File

@ -0,0 +1,149 @@
<template>
<ul class="header-menu">
<template v-for="item in menuData">
<li @click="selectMenu(item, 1)" :key="item.id" :class="{ selected: item.selected }" :style="getStyle(item)" v-if="!item.hidden">
<svg-icon class="icon animated" :icon-name="item.meta.icon"></svg-icon>
<span>{{ item.meta.title }}</span>
</li>
</template>
</ul>
</template>
<script>
export default {
props: {
pageConfigs: Object,
},
watch: {
firstPath() {
//
if (this.$route.name !== 'Redirect') this.initMenu('firstPath')
},
//
hiddenSide(cur) {
if (cur) {
this.resetMenu(1)
} else {
this.initMenu()
}
},
menuData: {
handler: function () {
this.initMenu('menuData')
},
immediate: true,
},
},
computed: {
firstPath: function () {
return `/${this.$route.path.split('/')[1]}`
},
menuData() {
return this.$store.state.permission.addRoutes
},
hiddenSide() {
return this.$route.meta.hiddenSide
},
},
created() {},
methods: {
getStyle(item) {
if (item.selected) {
return {
backgroundColor: this.pageConfigs.headerSelectColour,
color: this.pageConfigs.headerFontSelectColour,
}
}
},
getJumpRoute(data) {
const route = data[0]
if (route.children) {
return this.getJumpRoute(route.children)
}
return route.path
},
// flag:
resetMenu(flag) {
this.menuData.forEach((row) => {
row.selected = false
// this.$set(row, 'selected', false)
})
if (flag) {
this.$store.commit('SETTING_SIDE_MENU', [])
this.$store.commit('SETTING_BASE_PATH', '/')
}
},
selectMenu(item, flag) {
if (item.selected) return
this.resetMenu()
const menu = this.menuData.find((cell) => cell.id === item.id)
menu.selected = true
//
const menus = menu.children.filter((item) => !item.hidden)
if (menus.length) {
this.$store.commit('SETTING_SIDE_MENU', menu.children)
this.$store.commit('SETTING_BASE_PATH', menu.path)
} else {
this.resetMenu(true)
}
//
if (flag) {
this.$router.push({ path: this.getJumpRoute(menu.children) })
}
},
initMenu() {
// return
if (!this.menuData || this.menuData.length === 1 || this.hiddenSide) return
const selectMenu = this.menuData.find((item) => item.path === this.firstPath)
if (selectMenu) {
this.selectMenu(selectMenu, this.$route.name === 'Home')
} else {
this.resetMenu(1)
}
},
},
}
</script>
<style lang="scss" scoped>
ul.header-menu {
margin: 0;
list-style: none;
flex: 1;
display: flex;
padding: 0;
box-sizing: border-box;
overflow: hidden;
li {
display: flex;
flex: 1;
justify-content: center;
align-items: center;
font-size: 14px;
position: relative;
height: 48px;
cursor: pointer;
white-space: nowrap;
max-width: 120px;
min-width: 80px;
float: left;
&:hover .icon {
animation-name: swing;
}
.icon {
font-size: 16px;
margin-right: 4px;
}
&.selected {
color: #fff;
background: #15171d;
}
div {
display: none;
position: absolute;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.15);
}
}
}
</style>

View File

@ -0,0 +1,94 @@
<template>
<div class="m-r">
<LockOutlined class="cur-point" @click="lockScreen" />
</div>
</template>
<script lang="ts">
import { replaceToken } from 'services'
import { setToken, getToken } from 'utils/auth'
import { computed, defineComponent, onMounted, onUnmounted } from 'vue'
import { useStore } from 'vuex'
import { useRoute, useRouter } from 'vue-router'
import { LockOutlined } from '@ant-design/icons-vue'
export default defineComponent({
components: { LockOutlined },
setup() {
const store = useStore()
const route = useRoute()
const router = useRouter()
let timer: any = 0
function init() {
store.commit('SET_OPERATETIME')
let lockData = localStorage.getItem('lockData')
if (lockData) {
lockData = JSON.parse(lockData)
if ((lockData as any).isLock) router.push({ name: 'LockMe' })
}
}
init()
// token
async function getLastToken() {
const token = getToken()
const res = await replaceToken({ token })
if (res.success) {
setToken(res.data)
} else {
clearTimer()
store.dispatch('permission/ResetRoutes')
}
}
function clearTimer() {
clearInterval(timer)
timer = 0
}
function lockScreen() {
localStorage.setItem(
'lockData',
JSON.stringify({
path: route.fullPath,
isLock: true,
})
)
router.push({ name: 'LockMe' })
}
// const userData = computed(() => store.getters.userData)
// async function checkUserStatus() {
// const data = await checkUser(userData.value.id);
// if (!data.success) {
// clearTimer()
// context.root.$alert(' ', '', {
// confirmButtonText: '',
// callback: () => {
// store.dispatch('permission/ResetRoutes')
// }
// })
// }
// }
const operateTime = computed(() => store.state.app.operateTime)
const lockScreenTime = computed(() => store.getters.systemConfig.lockScreenTime)
onMounted(() => {
timer = setInterval(() => {
getLastToken()
//
const interval = 1000 * 60 * Number(lockScreenTime.value)
// checkUserStatus()
if (interval && new Date().getTime() - operateTime.value >= interval) {
lockScreen()
}
}, 1000 * 20)
})
onUnmounted(clearTimer)
return {
lockScreen,
}
},
})
</script>
<style scoped>
.lock {
font-size: 20px;
cursor: pointer;
}
</style>

View File

@ -0,0 +1,161 @@
<template>
<a-modal title="个人信息" ref="dialog" :close-on-click-modal="false" v-model:visible="visible" width="800px" @ok="editSubmit" okText="更新信息" cancelText="" :confirmLoading="loading">
<a-form v-bind="formSetting">
<a-row :gutter="5">
<a-col :span="12">
<a-col :span="24">
<div class="info-header-title">基本信息</div>
</a-col>
<a-col :span="24">
<a-form-item label="登录账号:">
<a-input v-model:value="userData.account" disabled></a-input>
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="用户姓名:" v-bind="validateInfos.name">
<a-input v-model:value="userData.name"></a-input>
</a-form-item>
</a-col>
<a-col :span="24">
<div class="info-header-title">联系信息</div>
</a-col>
<a-col :span="24">
<a-form-item label="用户邮箱:" v-bind="validateInfos.email">
<a-input v-model:value="userData.email"></a-input>
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="联系方式:" v-bind="validateInfos.mobile">
<a-input v-model:value="userData.mobile"></a-input>
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="公司名称:">
<a-input v-model:value="userData.company"></a-input>
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="组织机构:">
<a-input v-model:value="userData.departName" disabled></a-input>
</a-form-item>
</a-col>
</a-col>
<a-col :span="12">
<div class="info-img">
<img :src="userData.portrait" alt="" />
</div>
<div class="info-btn">
<a-button @click="imageCropperShow = true">
<template #icon><CloudUploadOutlined /></template>
更换头像</a-button
>
</div>
</a-col>
</a-row>
</a-form>
<image-cropper field="files" @crop-success="imageCropSuccess" v-if="imageCropperShow" v-model="imageCropperShow" :width="size" :height="size" img-format="png"></image-cropper>
</a-modal>
</template>
<script>
import { message, Form } from 'ant-design-vue'
import { cloneDeep } from 'lodash-es'
import ImageCropper from 'components/image-cropper/index.vue'
import { modifyUser } from '@/services/manager'
import { CloudUploadOutlined } from '@ant-design/icons-vue'
import { reactive, toRefs, ref } from 'vue'
import { useStore } from 'vuex'
import { required, email, mobile } from '@/validate'
import { formSetting } from '@/config'
const useForm = Form.useForm
export default {
props: {
data: {
type: Object
}
},
components: {
ImageCropper,
CloudUploadOutlined
},
setup(props) {
const state = reactive({
imageCropperShow: false,
size: 65,
visible: false,
userData: {}
})
function open() {
const userData = cloneDeep(props.data)
Object.keys(userData).forEach((key) => {
state.userData[key] = userData[key]
})
state.visible = true
}
function imageCropSuccess(imageDataUrl) {
state.userData.portrait = imageDataUrl
}
const formRef = ref(null)
const store = useStore()
const loading = ref(false)
const rulesRef = reactive({
name: [required],
email: [required, email],
mobile: [required, mobile]
})
const { validate, validateInfos } = useForm(state.userData, rulesRef)
async function editSubmit() {
try {
loading.value = true
await validate()
const data = await modifyUser(state.userData)
loading.value = false
if (data.success) {
message.success(data.message)
state.visible = false
store.dispatch('GetUserInfo')
}
} catch (error) {
loading.value = false
}
}
return {
loading,
formSetting,
...toRefs(state),
formRef,
validateInfos,
open,
editSubmit,
imageCropSuccess
}
},
methods: {}
}
</script>
<style lang="scss" scoped>
.info-header-title {
padding-left: 10px;
border-left: 3px solid #2d8cf0;
margin-bottom: 20px;
}
.info-img {
width: 100px;
height: 100px;
border-radius: 50%;
overflow: hidden;
margin: 90px 0px 20px 100px;
border: 1px solid #d9d9d9;
margin-top: 40px;
}
.info-img img {
width: 100%;
height: 100%;
}
.info-btn {
margin: 0px 0px 0px 100px;
}
</style>

View File

@ -0,0 +1,169 @@
<template>
<a-dropdown>
<div class="user-content">
<img :src="userData.portrait" class="head-portrait" />
<span class="user-name">{{ userData.name }}</span>
<DownOutlined />
</div>
<template #overlay>
<a-menu class="user-dropdown">
<a-menu-item @click="openInfoDialog" class="menu-item"> <UserOutlined /> 个人信息 </a-menu-item>
<a-menu-item @click="openPwdDialog" class="menu-item"> <LockOutlined /> 修改密码 </a-menu-item>
<a-menu-item @click="logoutSystem()" class="menu-item"> <PoweroffOutlined /> 退出系统 </a-menu-item>
</a-menu>
</template>
</a-dropdown>
<a-modal title="修改密码" :maskClosable="false" width="600px" v-if="pwdDialogVisible" v-model:visible="pwdDialogVisible" @ok="modifyPwdSubmit" :confirmLoading="loading">
<a-form :model="pwdData" ref="pwdRef" :colon="true" :labelCol="{ span: 4 }">
<a-form-item label="原密码" name="oldPassword" :rules="[required]">
<a-input-password v-model:value="pwdData.oldPassword" auto-complete="off"></a-input-password>
</a-form-item>
<a-form-item label="新密码" name="newPassword" :rules="pwdRule">
<a-input-password v-model:value="pwdData.newPassword" auto-complete="off"></a-input-password>
</a-form-item>
<a-form-item label="确认密码" name="confirmPassword" :rules="pwdRule">
<a-input-password v-model:value="pwdData.confirmPassword" auto-complete="off"></a-input-password>
</a-form-item>
</a-form>
</a-modal>
<InfoDialog ref="infoRef" :data="userData"></InfoDialog>
</template>
<script lang="ts">
import crypto from 'utils/crypto.js'
import { changePassword } from '@/services/manager'
import { logout } from 'services'
import InfoDialog from './InfoDialog.vue'
import { computed, defineComponent, ref, createVNode } from 'vue'
import { message, Modal } from 'ant-design-vue'
import { useStore } from 'vuex'
import { required, complexPassword } from '@/validate'
import { UserOutlined, PoweroffOutlined, LockOutlined, ExclamationCircleOutlined, DownOutlined } from '@ant-design/icons-vue'
export default defineComponent({
components: { InfoDialog, UserOutlined, PoweroffOutlined, LockOutlined, DownOutlined },
setup() {
const store = useStore()
//
const pwdData = ref({
oldPassword: '',
newPassword: '',
confirmPassword: ''
})
const pwdDialogVisible = ref(false)
function openPwdDialog() {
pwdDialogVisible.value = true
pwdData.value = {
oldPassword: '',
newPassword: '',
confirmPassword: ''
}
}
function checkPassword() {
const { newPassword, oldPassword, confirmPassword } = pwdData.value
if (newPassword === oldPassword) {
message.error('新密码不能与原密码相同')
return false
}
if (confirmPassword !== newPassword) {
message.error('确认密码与新密码不一致')
return false
}
return true
}
const pwdRef = ref()
const loading = ref(false)
async function modifyPwdSubmit() {
try {
const values = await pwdRef.value.validate()
if (!checkPassword()) return
loading.value = true
const res = await changePassword(userData.value.id, {
password: crypto.encrypt(values.newPassword),
oldPassword: crypto.encrypt(values.oldPassword)
})
if (res.success) {
pwdDialogVisible.value = false
message.success(res.message)
store.dispatch('permission/ResetRoutes')
}
} catch (error) {
console.log(error)
}
loading.value = false
}
const userData = computed(() => store.getters.userData || {})
const pwdRule = computed(() => {
const rule = store.state.app.systemConfig.pwdStrength
if (rule === 'required') return [required]
return [required, complexPassword]
})
//
function logoutSystem() {
Modal.confirm({
title: '提示',
icon: createVNode(ExclamationCircleOutlined),
content: '您确定要退出该系统吗?',
async onOk() {
const res = await logout()
if (res.success) {
store.dispatch('permission/ResetRoutes')
}
}
})
}
//
const infoRef = ref()
function openInfoDialog() {
infoRef.value.open()
}
return {
loading,
pwdData,
pwdDialogVisible,
pwdRef,
pwdRule,
openPwdDialog,
modifyPwdSubmit,
logoutSystem,
infoRef,
openInfoDialog,
userData,
required
}
}
})
</script>
<style lang="scss" scoped>
.user-content {
margin-right: 10px;
display: flex;
align-items: center;
cursor: pointer;
.head-portrait {
display: inline-block;
width: 24px;
height: 24px;
border-radius: 50%;
}
}
.user-name {
max-width: 140px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
font-size: 14px;
margin: 0 2px 0 5px;
}
.user-dropdown {
background: #2c2e3b;
border-color: #2c2e3b;
::v-deep(.ant-dropdown-menu-item) {
color: #ccc;
&:hover {
background: #2d8cf0 !important;
color: #fff;
}
}
}
</style>

View File

@ -0,0 +1,20 @@
<template>
<a-dropdown>
<span class="m-r">
<DesktopOutlined class="cur-point" />
</span>
<template #overlay>
<a-menu>
<router-link to="/personal/process_order">
<a-menu-item>流程工单</a-menu-item>
</router-link>
<router-link to="/personal/service_work_order">
<a-menu-item>问题工单</a-menu-item>
</router-link>
</a-menu>
</template>
</a-dropdown>
</template>
<script setup lang="ts">
import { DesktopOutlined } from '@ant-design/icons-vue'
</script>

View File

@ -0,0 +1,30 @@
/** * Created by HaijunZhang on 2019/12/18. */
<template>
<div class="right-container">
<LockScreen></LockScreen>
<process-item></process-item>
<HelpItem v-if="userData" />
<site-message></site-message>
<personal-info></personal-info>
</div>
</template>
<script lang="ts" setup>
import { computed } from 'vue'
import PersonalInfo from './personal/index.vue'
import ProcessItem from './process.vue'
import SiteMessage from './SiteMessage.vue'
import LockScreen from './lockScreen.vue'
import HelpItem from './Help.vue'
import { useStore } from 'vuex'
const store = useStore()
const userData = computed(() => store.getters.userData)
</script>
<style lang="scss">
.right-container {
display: flex;
font-size: 18px;
}
</style>

View File

@ -0,0 +1,100 @@
<template>
<a-menu class="czhj-menu" :mode="mode" :inlineIndent="inlineIndent" @click="clickMenu" @openChange="handleOpenChange" :openKeys="openKeys" :selectedKeys="urlToList($route.path)" v-bind="$attrs">
<sidebar-item v-for="route in sideMenuData" :menuItem="route" :key="route.path" :isLimitLevel="isLimitLevel" :base-path="basePath" :default-icon="defaultIcon"></sidebar-item>
</a-menu>
</template>
<script lang="ts">
import { computed, defineComponent } from 'vue'
import SidebarItem from './SidebarItem.vue'
import { urlToList } from './utils'
import { useLink } from './useLink'
import useOpenKeys from './useOpenKeys'
export default defineComponent({
components: { SidebarItem },
props: {
mode: {
type: String,
default: 'vertical'
},
menuData: {
type: Array
},
isCollapsed: {
type: Boolean,
default: false
},
mainMenu: {
type: Boolean,
default: false
},
inlineIndent: {
type: Number,
default: 12
},
isLimitLevel: {
type: Boolean,
default: false
},
limitLevel: {
type: Number,
default: 4
},
basePath: {
type: String,
default: '/'
},
defaultIcon: {
type: String,
default: 'svg-dot'
}
},
setup(props, context) {
//
function filterMenuData(data: any) {
const menu: any[] = []
data.forEach((item: any) => {
if (!item.hidden) {
const cache = { ...item }
menu.push(cache)
if (item.children && item.path.split('/').length < props.limitLevel + 1) {
cache.children = filterMenuData(item.children)
} else {
cache.children = null
}
}
})
return menu.length ? menu : null
}
const sideMenuData = computed(() => filterMenuData(props.menuData))
//
const { openKeys, handleOpenChange } = useOpenKeys({
mainMenu: props.mainMenu
})
//
const goPage = useLink()
function clickMenu({ key }: { key: string }) {
goPage(key)
}
return {
sideMenuData,
clickMenu,
urlToList,
openKeys,
handleOpenChange
}
}
})
</script>
<style lang="scss" scoped>
.czhj-menu {
background: var(--czhj-color-background);
color: var(--czhj-color-text);
border-right: none;
height: 100%;
::v-deep(.ant-menu-submenu-arrow) {
color: var(--czhj-color-text);
}
}
</style>

View File

@ -0,0 +1,70 @@
<template>
<a-menu-item class="czhj-menu-item" :key="path" ref="menuItemRef">
<template #icon v-if="meta.icon || defaultIcon">
<span>
<svg-icon :class="meta.icon && 'animated czhj-icon'" :icon-name="meta.icon || defaultIcon"></svg-icon>
</span>
</template>
<span>{{ meta.title }}</span>
</a-menu-item>
</template>
<script>
export default {
props: {
path: {
type: String,
},
meta: {
type: Object,
},
defaultIcon: {
type: String,
default: '',
},
},
}
</script>
<style lang="scss" scoped>
.czhj-menu-item {
color: var(--czhj-color-text) !important;
.czhj-icon {
font-size: 16px;
}
&:hover {
.czhj-icon {
animation-name: swing;
}
color: var(--czhj-color-text-selected) !important;
}
&::v-deep(.ant-menu-item-selected) {
background-color: var(--czhj-color-background-selected) !important;
color: var(--czhj-color-text-selected) !important;
}
}
</style>
<style lang="scss">
.czhj-menu-item {
color: var(--czhj-color-text) !important;
.czhj-icon {
font-size: 16px;
}
&:hover {
.czhj-icon {
animation-name: swing;
}
color: var(--czhj-color-text-selected) !important;
}
&.ant-menu-item-selected {
background-color: var(--czhj-color-background-selected) !important;
color: var(--czhj-color-text-selected) !important;
}
}
.czhj-menu,
.czhj-sub-menu {
.ant-menu-item:active,
.ant-menu-submenu-title:active {
background: var(--czhj-color-background);
}
}
</style>

View File

@ -0,0 +1,59 @@
<template>
<div class="box">
<div class="box1">
<slot>
<VerticalLeftOutlined class="operate-icon" />
</slot>
</div>
<div class="box2"></div>
<div class="box3"></div>
</div>
</template>
<script setup>
import { VerticalLeftOutlined } from '@ant-design/icons-vue'
</script>
<style lang="scss" scoped>
.operate-icon {
transform: rotate(-90deg);
}
$color: var(--czhj-color-background-selected);
.box {
width: 60px;
height: 20px;
overflow: hidden;
}
.box1 {
width: 20px;
height: 20px;
text-align: center;
line-height: 20px;
background: $color;
position: absolute;
left: 20px;
color: #fff;
font-weight: bold;
z-index: 2;
}
.box2,
.box3 {
border: 20px solid transparent;
border-bottom: 20px solid $color;
position: absolute;
left: 20px;
top: -20px;
}
.box3 {
left: 0px;
}
.collapsed {
.box1 {
background: $color;
}
.box2,
.box3 {
border: 20px solid transparent;
border-bottom: 20px solid $color;
}
}
</style>

View File

@ -0,0 +1,126 @@
<template>
<a-layout-sider :theme="theme" breakpoint="xl" @breakpoint="onBreakpoint" class="sidebar-container" width="190" :collapsed="isCollapsed" :class="isCollapsed && 'collapsed'">
<el-scrollbar class="scrollbar-wrapper">
<BaseMenu v-bind="menuProps" class="sidebar-menu" @select="selectItem" :page-configs="pageConfigs"></BaseMenu>
</el-scrollbar>
<OperateBtn class="operate" @click="$emit('toggleCollapsed')"> </OperateBtn>
</a-layout-sider>
</template>
<script>
import { computed, provide } from 'vue'
import BaseMenu from './BaseMenu.vue'
import OperateBtn from './OperateBtn.vue'
import { useStore } from 'vuex'
import useGlobalStyle from './useGlobalStyle'
export default {
components: { BaseMenu, OperateBtn },
props: {
theme: {
type: String,
default: 'light'
},
mode: {
type: String,
default: 'inline'
},
isCollapsed: {
type: Boolean,
default: false
},
menuData: {
type: Array
},
isLimitLevel: {
type: Boolean,
default: false
},
basePath: {
type: String,
default: '/'
},
matchPath: {
type: String,
default: ''
},
selectItem: {
type: Function
}
},
setup(props, context) {
const store = useStore()
const pageConfigs = computed(() => store.getters.pageConfig)
const menuProps = computed(() => {
const { mode, menuData, isLimitLevel, basePath, matchPath, isCollapsed } = props
return {
mode,
menuData,
isLimitLevel,
basePath,
matchPath,
isCollapsed,
mainMenu: true
}
})
const { style } = useGlobalStyle()
provide('styleConfigs', style)
function onBreakpoint(broken) {
context.emit('toggleCollapsed', broken)
}
return {
onBreakpoint,
menuProps,
pageConfigs
}
}
}
</script>
<style lang="scss" scoped>
.sidebar-container {
background: var(--czhj-color-background);
color: var(--czhj-color-text);
z-index: 3;
transition: width 0.18s;
overflow: hidden;
height: 100%;
&.collapsed {
width: 50px !important;
flex: auto !important;
min-width: 50px !important;
max-width: 50px !important;
overflow: initial;
.operate {
position: absolute;
left: 27px;
transform: rotate(90deg);
transition: left 0.1s;
z-index: -1;
&:hover {
left: 30px;
}
}
}
.scrollbar-wrapper {
height: 100%;
::v-deep(.el-scrollbar__view) {
height: 100%;
}
::v-deep(.el-scrollbar__wrap) {
overflow-x: hidden;
}
}
.operate {
position: absolute;
left: 155px;
top: 50%;
font-size: 18px;
cursor: pointer;
transform: rotate(-90deg);
transition: left 0.1s;
&:hover {
left: 150px;
}
}
}
</style>

View File

@ -0,0 +1,90 @@
<template>
<template v-if="menuItem.children">
<MenuItem v-if="hasOneChild(menuItem.children, menuItem)" :path="singleChild.path" :meta="singleChild.meta" :default-icon="defaultIcon"></MenuItem>
<a-sub-menu v-else :key="currPath" class="czhj-sub-menu" popupClassName="czhj-sub-menu">
<template #icon v-if="menuItem.meta.icon || defaultIcon">
<svg-icon class="animated czhj-icon" :icon-name="menuItem.meta.icon || defaultIcon"></svg-icon>
</template>
<template #title>
<span>{{ menuItem.meta.title }}</span>
</template>
<template v-for="child in menuItem.children">
<sidebar-item class="nest-menu" v-if="!child.hidden && !isLimitLevel && child.children" :menuItem="child" :key="child.path" :base-path="currPath" :default-icon="defaultIcon"></sidebar-item>
<MenuItem v-else-if="!child.hidden" :path="resolvePath(currPath, child.path)" :meta="child.meta" :key="`${child.path1}`" :default-icon="defaultIcon"></MenuItem>
</template>
</a-sub-menu>
</template>
<MenuItem v-else :path="currPath" :meta="menuItem.meta"></MenuItem>
</template>
<script>
import { resolvePath } from 'utils/resolvePath'
import MenuItem from './MenuItem.vue'
export default {
components: {
MenuItem
},
name: 'SidebarItem',
data() {
return {
singleChild: {}
}
},
props: {
menuItem: {
type: Object
},
basePath: {
type: String
},
isLimitLevel: {
type: Boolean
},
defaultIcon: {
type: String
}
},
computed: {
currPath() {
return this.resolvePath(this.basePath, this.menuItem.path)
}
},
setup() {},
methods: {
resolvePath(basePath, routePath) {
return resolvePath(basePath, routePath)
},
hasOneChild(children, parent) {
if (children.length === 1 && !parent.meta.alwaysShow) {
this.singleChild = children[0]
return true
}
return false
}
}
}
</script>
<style lang="scss">
.czhj-sub-menu {
.czhj-icon {
font-size: 16px !important;
}
.ant-menu-sub {
background-color: var(--czhj-color-background-sub) !important;
color: var(--czhj-color-text);
}
&:hover .czhj-icon {
animation-name: swing;
}
&:hover > .ant-menu-submenu-title > .ant-menu-submenu-arrow {
color: var(--czhj-color-text-selected) !important;
}
&.ant-menu-submenu-selected {
color: var(--czhj-color-text-selected);
}
.ant-menu-submenu-title:hover {
color: var(--czhj-color-text-selected) !important;
}
}
</style>

View File

@ -0,0 +1,15 @@
.sidebar-menu {
border: 0;
background: inherit;
::v-deep {
.el-menu {
background: inherit;
}
.el-menu-item,
.el-menu-item i,
.el-submenu__title,
.el-submenu__title i {
color: inherit;
}
}
}

View File

@ -0,0 +1,34 @@
import { onUnmounted, computed, unref } from 'vue'
import { useStore } from 'vuex'
export default function () {
const store = useStore()
const pageConfigs = computed(() => store.getters.pageConfig)
const cssStyle: any = computed(() => {
const { menuBgColour, subMenuBgColour, menuFontColour, menuFontSelectColour, menuSelectColour } = pageConfigs.value
return {
'--czhj-color-background': menuBgColour,
'--czhj-color-background-sub': subMenuBgColour,
'--czhj-color-text': menuFontColour,
'--czhj-color-background-selected': menuSelectColour,
'--czhj-color-text-selected': menuFontSelectColour
}
})
const style = document.createElement('style')
style.setAttribute('type', 'text/css')
function generateCss() {
let str = ''
Object.keys(unref(cssStyle)).forEach((item: string) => {
str += `${item}:${cssStyle.value[item]};`
})
return str
}
style.innerHTML = `:root{${generateCss()}}`
document.head.appendChild(style)
onUnmounted(() => {
style && style.remove()
})
return {
style: cssStyle
}
}

View File

@ -0,0 +1,20 @@
import { isExternalLink } from 'utils/resolvePath'
import { useRouter } from 'vue-router'
import type { Router } from 'vue-router'
export function openWindow(url: string, opt?: { target?: '_self' | '_blank' }) {
const { target = '__blank' } = opt || {}
window.open(url, target)
}
export function useLink(_router?: Router) {
const { push } = _router || useRouter()
function goPage(url: string) {
if (isExternalLink(url)) {
return openWindow(url)
}
push(url).catch((e) => {
console.log(e)
})
}
return goPage
}

View File

@ -0,0 +1,45 @@
import { ref, watch, computed, unref } from 'vue'
import { useRoute } from 'vue-router'
import { urlToList } from './utils'
import { useStore } from 'vuex'
export default function (props: { mainMenu: boolean }) {
const route = useRoute()
const store = useStore()
function getDefaultOpenKeys(path: string) {
return urlToList(path)
}
const { mainMenu } = props
const collapsed = computed(() => store.state.app.isCollapsed)
const openKeys = ref(getDefaultOpenKeys(route.path))
if (mainMenu) {
watch(
() => route.path,
() => {
if (unref(collapsed)) return
openKeys.value = getDefaultOpenKeys(route.path)
}
)
watch(collapsed, () => {
if (!collapsed.value) openKeys.value = getDefaultOpenKeys(route.path)
})
}
// 处理菜单数据,只保持一个子菜单展开
const handleOpenChange = (openKey: string[]) => {
const result: string[] = []
if (openKey.length) {
const lastOpenKey = [...openKey].pop() as string
openKey.forEach((item) => {
// 子菜单打开父菜单也需要展开 || 父菜单展开之前缓存的子菜单数据也要展开
if (lastOpenKey.includes(item) || item.includes(lastOpenKey)) {
result.push(item)
}
})
}
openKeys.value = result
}
return {
openKeys,
handleOpenChange,
}
}

View File

@ -0,0 +1,31 @@
/**
* Created by HaijunZhang on 2019/7/5.
*/
// 获取菜单数组
export function getFlatMenuPath(menus) {
const paths = []
menus.forEach((item) => {
paths.push(item.path)
if (item.children) {
paths.push(...getFlatMenuPath(item.children))
}
})
return paths
}
// /userinfo/2144/id => ['/userinfo','/useinfo/2144,'/userindo/2144/id']
export function urlToList(url) {
const urllist = url.split('/').filter((i) => i)
return urllist.map((urlItem, index) => {
return `/${urllist.slice(0, index + 1).join('/')}`
})
}
export const getIcon = (icon, iconMap) => {
if (iconMap) return iconMap[icon]
if (typeof icon !== 'string') return icon
// if (icon.indexOf('/') > -1) {
// return <img src={icon} alt="icon" className={'sider-menu-item-img'} />
// }
// if (icon.includes('iconfont')) {
// return <i className={icon} style={{ marginRight: '10px' }} />
// }
}

175
src/layouts/home.vue Normal file
View File

@ -0,0 +1,175 @@
<template>
<a-layout class="app-wrapper" :class="[{ hideSidebar: isCollapsed }, { 'font-big': isFontBig }, { 'expire-top': expire }]" @click="setTime">
<SystemTip />
<Header :match-path="matchPath"></Header>
<TagsView v-if="addRoutes"></TagsView>
<a-layout style="overflow: hidden">
<Sidebar v-if="showSidebar" @toggleCollapsed="toggleCollapsed" :isCollapsed="isCollapsed" :menuData="menuData" :isLimitLevel="true" :matchPath="matchPath" :basePath="basePath"></Sidebar>
<a-layout class="main-container">
<ThirdMenu :menuData="thirdMenuData" v-if="thirdMenuData.children"></ThirdMenu>
<a-layout-content class="main-body">
<el-scrollbar class="custom-scrollbar" style="flex: 1">
<transition enter-active-class="fadeInUp" mode="out-in">
<!-- <router-view v-if="$route.path === '/404'"></router-view> -->
<div id="subapp-viewport" class="app-view-box"></div>
</transition>
<!-- <el-backtop target=".custom-scrollbar .el-scrollbar__wrap" :right="5"></el-backtop> -->
</el-scrollbar>
</a-layout-content>
</a-layout>
</a-layout>
</a-layout>
</template>
<script>
import { watch, ref, computed, unref } from 'vue'
import { useRoute } from 'vue-router'
import { useStore } from 'vuex'
import { trimStart, cloneDeep } from 'lodash-es'
import Sidebar from './components/sidebar/Sidebar.vue'
import ThirdMenu from './components/ThirdMenu.vue'
import Header from './components/Header.vue'
import TagsView from './components/TagsView/index.vue'
import SystemTip from './components/SystemTip.vue'
// import startApp from '@/core/register'
export default {
components: {
Header,
SystemTip,
Sidebar,
ThirdMenu,
TagsView
},
data() {
return {
matchPath: ''
}
},
setup() {
const route = useRoute()
const store = useStore()
//
function init() {
store.commit('permission/SET_BUTTONS')
store.dispatch('GetSystemConfigs')
}
init()
// onMounted(() => {
// console.log('micro apps register')
// startApp()
// })
//
const thirdMenuData = ref({})
function getThirdMenu(data, matchPath) {
for (const items of data) {
if (matchPath.includes(items.path) && items.children) {
if (matchPath === items.path) {
const thirdMenu = cloneDeep(items)
//
const children = []
items.children = items.children || []
items.children.forEach((_) => {
if (!_.hidden) children.push(_)
})
thirdMenuData.value = Object.assign(thirdMenu, { children: children.length ? children : null })
} else {
getThirdMenu(items.children, matchPath)
}
break
}
}
}
//
function getLevelPath(level) {
return `/${trimStart(route.path, '/').split('/', level).join('/')}`
}
//
function handlePath() {
const matchPath = getLevelPath(3)
// menuData
setTimeout(() => {
const pathLen = route.path.split('/').length
thirdMenuData.value = {}
if (pathLen >= 6) {
//
getThirdMenu(addRoutes.value, getLevelPath(4))
} else if (pathLen >= 5) {
//
getThirdMenu(addRoutes.value, matchPath)
}
})
}
watch(
() => route.path,
() => {
handlePath()
},
{
immediate: true
}
)
watch(thirdMenuData, (current, pre) => {
// ,
if (!current.children === !pre.children) return
store.commit('SET_COLLAPSED', !!thirdMenuData.value.children)
})
const toggleCollapsed = (value) => {
if (typeof value === 'boolean') {
store.commit('SET_COLLAPSED', value)
} else {
store.commit('TOGGLE_SIDEBAR')
}
}
const addRoutes = computed(() => store.state.permission.addRoutes)
const menuData = computed(() => store.state.app.sideMenuData)
const showSidebar = computed(() => {
return !route.meta.hiddenSide && unref(menuData).length
})
function setTime() {
store.commit('SET_OPERATETIME')
}
return {
isCollapsed: computed(() => store.state.app.isCollapsed),
basePath: computed(() => store.state.app.basePath),
expire: computed(() => store.state.app.expire),
isFontBig: computed(() => store.state.app.pageConfig.contentFontSize === 'big'),
addRoutes,
menuData,
showSidebar,
thirdMenuData,
toggleCollapsed,
setTime
}
},
methods: {
//
selectItem(path) {
// const matchPath = [path, `${path}/list`]
// if (matchPath.includes(this.$route.path)) {
// setTimeout(() => {
// this.$router.push({ name: 'Redirect', query: { path: path } })
// })
// }
}
}
}
</script>
<style lang="scss" scoped>
.app-wrapper {
background: #e7f4ff;
flex-direction: column;
min-width: 1200px;
overflow-x: auto;
}
.main-container {
position: relative;
height: 100%;
background: #f5f7f9;
.main-body {
padding: 0 16px 12px 16px;
display: flex;
flex-direction: column;
}
}
</style>

12
src/layouts/redirect.vue Normal file
View File

@ -0,0 +1,12 @@
<template>
<div></div>
</template>
<script setup lang="ts">
import { useRouter, useRoute } from 'vue-router'
const router = useRouter()
const route = useRoute()
setTimeout(() => {
router.push(route.query.path as string)
})
</script>

11
src/main.ts Normal file
View File

@ -0,0 +1,11 @@
import { createApp } from 'vue'
import router from '@/router/index'
import store from '@/store'
import '@/permission'
import App from '@/App.vue'
import 'virtual:svg-icons-register'
import startApp from '@/core/register'
const application = createApp(App)
application.use(store).use(router).mount('#master-container')
startApp()

View File

@ -0,0 +1,10 @@
export default {
computed: {
$webSocket() {
return this.$store.state.app.$webSocket
}
},
created() {
this.$webSocket.onmessage = this.onmessage
}
}

12
src/mixins/selection.js Normal file
View File

@ -0,0 +1,12 @@
export default {
data() {
return {
selectionIds: [],
}
},
methods: {
handleSelectionChange(selections) {
this.selectionIds = selections.map((item) => item.id)
},
},
}

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