feat: 新前端

This commit is contained in:
fit2cloud-chenyw 2021-03-03 15:06:52 +08:00
parent f5cc065301
commit f04809c638
282 changed files with 147052 additions and 0 deletions

14
frontend/.editorconfig Normal file
View File

@ -0,0 +1,14 @@
# http://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
insert_final_newline = false
trim_trailing_whitespace = false

14
frontend/.env.development Normal file
View File

@ -0,0 +1,14 @@
# just a flag
ENV = 'development'
# base api
# VUE_APP_BASE_API = '/dev-api'
VUE_APP_BASE_API = 'http://localhost:8081/'
# vue-cli uses the VUE_CLI_BABEL_TRANSPILE_MODULES environment variable,
# to control whether the babel-plugin-dynamic-import-node plugin is enabled.
# It only does one thing by converting all import() to require().
# This configuration can significantly increase the speed of hot updates,
# when you have a large number of pages.
# Detail: https://github.com/vuejs/vue-cli/blob/dev/packages/@vue/babel-preset-app/index.js
VUE_CLI_BABEL_TRANSPILE_MODULES = true

6
frontend/.env.production Normal file
View File

@ -0,0 +1,6 @@
# just a flag
ENV = 'production'
# base api
VUE_APP_BASE_API = '/prod-api'

8
frontend/.env.staging Normal file
View File

@ -0,0 +1,8 @@
NODE_ENV = production
# just a flag
ENV = 'staging'
# base api
VUE_APP_BASE_API = '/stage-api'

4
frontend/.eslintignore Normal file
View File

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

198
frontend/.eslintrc.js Normal file
View File

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

16
frontend/.gitignore vendored Normal file
View File

@ -0,0 +1,16 @@
.DS_Store
node_modules/
dist/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
package-lock.json
tests/**/coverage/
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln

5
frontend/.travis.yml Normal file
View File

@ -0,0 +1,5 @@
language: node_js
node_js: 10
script: npm run test
notifications:
email: false

21
frontend/LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2017-present PanJiaChen
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

128
frontend/README.md Normal file
View File

@ -0,0 +1,128 @@
# 项目说明
> 这是一个极简的 vue admin 管理后台基于vue-admin-template进行了细节改造主要是把侧边导航改造为了顶部和侧边两个导航。
>
> 本项目默认开启了csssourceMap和devtool('source-map'),便于在开发中调试,除非编译速度过慢,否则开发环境不建议修改。
## IDE
编辑器建议使用VS Code格式化时可以统一代码风格配置项建议统一设置为默认不自动保存手动保存后自动修复部分错误。具体参数如下
```js
{
"emmet.triggerExpansionOnTab": true,
"files.autoSave": "off",
"vetur.format.defaultFormatterOptions": {
"js-beautify-html": {
"wrap_attributes": "force-aligned"
},
"prettyhtml": {
"printWidth": 100,
"singleQuote": false,
"wrapAttributes": false,
"sortAttributes": true
},
"prettier": {
"semi": false,
"singleQuote": true
}
},
"eslint.run": "onSave",
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
}
}
```
## 目录结构
```bash
├── build # 构建相关
├── mock # 项目mock 模拟数据
├── public # 静态资源
│ │── favicon.ico # favicon图标
│ └── index.html # html模板
├── src # 源代码
│ ├── api # 所有请求
│ ├── assets # 主题 字体等静态资源
│ ├── components # 全局公用组件
│ ├── directive # 全局指令
│ ├── filters # 全局 filter
│ ├── icons # 项目所有 svg icons
│ ├── lang # 国际化 language
│ ├── layout # 全局 layout
│ ├── router # 路由
│ ├── store # 全局 store管理
│ ├── styles # 全局样式
│ ├── utils # 全局公用方法
│ ├── vendor # 公用vendor
│ ├── views # views 所有页面
│ ├── App.vue # 入口页面
│ ├── main.js # 入口文件 加载组件 初始化等
│ └── permission.js # 权限管理
├── tests # 测试
├── .env.xxx # 环境变量配置
├── .eslintrc.js # eslint 配置项
├── .babelrc # babel-loader 配置
├── .travis.yml # 自动化CI配置
├── vue.config.js # vue-cli 配置
├── postcss.config.js # postcss 配置
└── package.json # package.json
```
## 构建步骤
```bash
# 克隆项目
git clone 项目地址
# 进入项目目录
cd admin-web
# 安装依赖
npm install
# 建议不要直接使用 cnpm 安装依赖,会有各种诡异的 bug。可以通过如下操作解决 npm 下载速度慢的问题
npm install --registry=https://registry.npm.taobao.org
# 启动服务
npm run dev
```
浏览器访问 [http://localhost:9528](http://localhost:9528)
## 发布
```bash
# 构建测试环境
npm run build:stage
# 构建生产环境
npm run build:prod
```
## 其它
```bash
# 预览发布环境效果
npm run preview
# 预览发布环境效果 + 静态资源分析
npm run preview -- --report
# 代码格式检查
npm run lint
# 代码格式检查并自动修复
npm run lint -- --fix
```
更多信息请参考花裤衩大佬的vue-element-admin [使用文档](https://panjiachen.github.io/vue-element-admin-site/zh/)
[线上地址](http://panjiachen.github.io/vue-admin-template)
[国内访问](https://panjiachen.gitee.io/vue-admin-template)

5
frontend/babel.config.js Normal file
View File

@ -0,0 +1,5 @@
module.exports = {
presets: [
'@vue/app'
]
}

35
frontend/build/index.js Normal file
View File

@ -0,0 +1,35 @@
const { run } = require('runjs')
const chalk = require('chalk')
const config = require('../vue.config.js')
const rawArgv = process.argv.slice(2)
const args = rawArgv.join(' ')
if (process.env.npm_config_preview || rawArgv.includes('--preview')) {
const report = rawArgv.includes('--report')
run(`vue-cli-service build ${args}`)
const port = 9526
const publicPath = config.publicPath
var connect = require('connect')
var serveStatic = require('serve-static')
const app = connect()
app.use(
publicPath,
serveStatic('./dist', {
index: ['index.html', '/']
})
)
app.listen(port, function () {
console.log(chalk.green(`> Preview at http://localhost:${port}${publicPath}`))
if (report) {
console.log(chalk.green(`> Report at http://localhost:${port}${publicPath}report.html`))
}
})
} else {
run(`vue-cli-service build ${args}`)
}

24
frontend/jest.config.js Normal file
View File

@ -0,0 +1,24 @@
module.exports = {
moduleFileExtensions: ['js', 'jsx', 'json', 'vue'],
transform: {
'^.+\\.vue$': 'vue-jest',
'.+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$':
'jest-transform-stub',
'^.+\\.jsx?$': 'babel-jest'
},
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1'
},
snapshotSerializers: ['jest-serializer-vue'],
testMatch: [
'**/tests/unit/**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)'
],
collectCoverageFrom: ['src/utils/**/*.{js,vue}', '!src/utils/auth.js', '!src/utils/request.js', 'src/components/**/*.{js,vue}'],
coverageDirectory: '<rootDir>/tests/unit/coverage',
// 'collectCoverage': true,
'coverageReporters': [
'lcov',
'text-summary'
],
testURL: 'http://localhost/'
}

9
frontend/jsconfig.json Normal file
View File

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

67
frontend/mock/index.js Normal file
View File

@ -0,0 +1,67 @@
import Mock from 'mockjs'
import { param2Obj } from '../src/utils'
import user from './user'
import table from './table'
const mocks = [
...user,
...table
]
// for front mock
// please use it cautiously, it will redefine XMLHttpRequest,
// which will cause many of your third-party libraries to be invalidated(like progress event).
export function mockXHR() {
// mock patch
// https://github.com/nuysoft/Mock/issues/300
Mock.XHR.prototype.proxy_send = Mock.XHR.prototype.send
Mock.XHR.prototype.send = function() {
if (this.custom.xhr) {
this.custom.xhr.withCredentials = this.withCredentials || false
if (this.responseType) {
this.custom.xhr.responseType = this.responseType
}
}
this.proxy_send(...arguments)
}
function XHR2ExpressReqWrap(respond) {
return function(options) {
let result = null
if (respond instanceof Function) {
const { body, type, url } = options
// https://expressjs.com/en/4x/api.html#req
result = respond({
method: type,
body: JSON.parse(body),
query: param2Obj(url)
})
} else {
result = respond
}
return Mock.mock(result)
}
}
for (const i of mocks) {
Mock.mock(new RegExp(i.url), i.type || 'get', XHR2ExpressReqWrap(i.response))
}
}
// for mock server
const responseFake = (url, type, respond) => {
return {
url: new RegExp(`${process.env.VUE_APP_BASE_API}${url}`),
type: type || 'get',
response(req, res) {
console.log('request invoke:' + req.path)
res.json(Mock.mock(respond instanceof Function ? respond(req, res) : respond))
}
}
}
export default mocks.map(route => {
return responseFake(route.url, route.type, route.response)
})

View File

@ -0,0 +1,68 @@
const chokidar = require('chokidar')
const bodyParser = require('body-parser')
const chalk = require('chalk')
const path = require('path')
const mockDir = path.join(process.cwd(), 'mock')
function registerRoutes(app) {
let mockLastIndex
const { default: mocks } = require('./index.js')
for (const mock of mocks) {
app[mock.type](mock.url, mock.response)
mockLastIndex = app._router.stack.length
}
const mockRoutesLength = Object.keys(mocks).length
return {
mockRoutesLength: mockRoutesLength,
mockStartIndex: mockLastIndex - mockRoutesLength
}
}
function unregisterRoutes() {
Object.keys(require.cache).forEach(i => {
if (i.includes(mockDir)) {
delete require.cache[require.resolve(i)]
}
})
}
module.exports = app => {
// es6 polyfill
require('@babel/register')
// parse app.body
// https://expressjs.com/en/4x/api.html#req.body
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({
extended: true
}))
const mockRoutes = registerRoutes(app)
var mockRoutesLength = mockRoutes.mockRoutesLength
var mockStartIndex = mockRoutes.mockStartIndex
// watch files, hot reload mock server
chokidar.watch(mockDir, {
ignored: /mock-server/,
ignoreInitial: true
}).on('all', (event, path) => {
if (event === 'change' || event === 'add') {
try {
// remove mock routes stack
app._router.stack.splice(mockStartIndex, mockRoutesLength)
// clear routes cache
unregisterRoutes()
const mockRoutes = registerRoutes(app)
mockRoutesLength = mockRoutes.mockRoutesLength
mockStartIndex = mockRoutes.mockStartIndex
console.log(chalk.magentaBright(`\n > Mock Server hot reload success! changed ${path}`))
} catch (error) {
console.log(chalk.redBright(error))
}
}
})
}

29
frontend/mock/table.js Normal file
View File

@ -0,0 +1,29 @@
import Mock from 'mockjs'
const data = Mock.mock({
'items|30': [{
id: '@id',
title: '@sentence(10, 20)',
'status|1': ['published', 'draft', 'deleted'],
author: 'name',
display_time: '@datetime',
pageviews: '@integer(300, 5000)'
}]
})
export default [
{
url: '/vue-admin-template/table/list',
type: 'get',
response: config => {
const items = data.items
return {
code: 20000,
data: {
total: items.length,
items: items
}
}
}
}
]

84
frontend/mock/user.js Normal file
View File

@ -0,0 +1,84 @@
const tokens = {
admin: {
token: 'admin-token'
},
editor: {
token: 'editor-token'
}
}
const users = {
'admin-token': {
roles: ['admin'],
introduction: 'I am a super administrator',
avatar: 'https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif',
name: 'Super Admin'
},
'editor-token': {
roles: ['editor'],
introduction: 'I am an editor',
avatar: 'https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif',
name: 'Normal Editor'
}
}
export default [
// user login
{
url: '/vue-admin-template/user/login',
type: 'post',
response: config => {
const { username } = config.body
const token = tokens[username]
// mock error
if (!token) {
return {
code: 60204,
message: 'Account and password are incorrect.'
}
}
return {
code: 20000,
data: token
}
}
},
// get user info
{
url: '/vue-admin-template/user/info\.*',
type: 'get',
response: config => {
const { token } = config.query
const info = users[token]
// mock error
if (!info) {
return {
code: 50008,
message: 'Login failed, unable to get user details.'
}
}
return {
code: 20000,
data: info
}
}
},
// user logout
{
url: '/vue-admin-template/user/logout',
type: 'post',
response: _ => {
return {
code: 20000,
data: 'success'
}
}
}
]

71
frontend/package.json Normal file
View File

@ -0,0 +1,71 @@
{
"name": "vue-admin-template",
"version": "4.2.1",
"description": "A vue admin template with Element UI & axios & iconfont & permission control & lint",
"author": "Pan <panfree23@gmail.com>",
"license": "MIT",
"scripts": {
"serve": "vue-cli-service serve",
"build:prod": "vue-cli-service build",
"build:stage": "vue-cli-service build --mode staging",
"preview": "node build/index.js --preview",
"lint": "eslint --ext .js,.vue src",
"test:unit": "jest --clearCache && vue-cli-service test:unit",
"test:ci": "npm run lint && npm run test:unit",
"svgo": "svgo -f src/icons/svg --config=src/icons/svgo.yml"
},
"dependencies": {
"axios": "0.18.1",
"element-ui": "2.13.0",
"fit2cloud-ui": "^0.1.12",
"js-cookie": "2.2.0",
"normalize.css": "7.0.0",
"nprogress": "0.2.0",
"path-to-regexp": "2.4.0",
"screenfull": "4.2.0",
"stylus": "^0.54.8",
"stylus-loader": "^5.0.0",
"svg-sprite-loader": "4.1.3",
"svgo": "1.2.2",
"vue": "2.6.10",
"vue-i18n": "7.3.2",
"vue-router": "3.0.6",
"vuex": "3.1.0",
"@riophae/vue-treeselect": "0.4.0",
"vuedraggable": "^2.24.3"
},
"devDependencies": {
"@babel/core": "7.0.0",
"@babel/register": "7.0.0",
"@vue/cli-plugin-babel": "3.6.0",
"@vue/cli-plugin-eslint": "^3.9.1",
"@vue/cli-plugin-unit-jest": "3.6.3",
"@vue/cli-service": "3.6.0",
"@vue/test-utils": "1.0.0-beta.29",
"autoprefixer": "^9.5.1",
"babel-core": "7.0.0-bridge.0",
"babel-eslint": "10.0.1",
"babel-jest": "23.6.0",
"chalk": "2.4.2",
"connect": "3.6.6",
"eslint": "5.15.3",
"eslint-plugin-vue": "5.2.2",
"html-webpack-plugin": "3.2.0",
"mockjs": "1.0.1-beta3",
"node-sass": "^4.9.0",
"runjs": "^4.3.2",
"sass-loader": "^7.1.0",
"script-ext-html-webpack-plugin": "2.1.3",
"script-loader": "0.7.2",
"serve-static": "^1.13.2",
"vue-template-compiler": "2.6.10"
},
"engines": {
"node": ">=8.9",
"npm": ">= 3.0.0"
},
"browserslist": [
"> 1%",
"last 2 versions"
]
}

View File

@ -0,0 +1,8 @@
// https://github.com/michael-ciniawsky/postcss-load-config
module.exports = {
'plugins': {
// to edit target browsers: use "browserslist" field in package.json
'autoprefixer': {}
}
}

BIN
frontend/public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

View File

@ -0,0 +1,17 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= webpackConfig.name %></title>
</head>
<body>
<noscript>
<strong>We're sorry but <%= webpackConfig.name %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>

11
frontend/src/App.vue Normal file
View File

@ -0,0 +1,11 @@
<template>
<div id="app">
<router-view />
</div>
</template>
<script>
export default {
name: 'App'
}
</script>

View File

@ -0,0 +1,55 @@
import request from '@/utils/request'
export function loadTable(data) {
return request({
url: '/dataset/table/list',
method: 'post',
data
})
}
export function getScene(sceneId) {
return request({
url: '/dataset/group/getScene/' + sceneId,
method: 'post'
})
}
export function addGroup(data) {
return request({
url: '/dataset/group/save',
method: 'post',
data
})
}
export function delGroup(groupId) {
return request({
url: '/dataset/group/delete/' + groupId,
method: 'post'
})
}
export function addTable(data) {
return request({
url: '/dataset/table/update',
method: 'post',
data
})
}
export function delTable(tableId) {
return request({
url: '/dataset/table/delete/' + tableId,
method: 'post'
})
}
export function groupTree(data) {
return request({
url: '/dataset/group/tree',
method: 'post',
data
})
}
export default { loadTable, getScene, addGroup, delGroup, addTable, delTable, groupTree }

View File

@ -0,0 +1,28 @@
import request from '@/utils/request'
const example = {
// 获取员工列表
staffList(data) {
return request.post(`/staff/findPage?pageNo=${data.pageNo - 1}&pageSize=${data.pageSize}`, {
name: data.name,
idCard: data.idCard,
mobile: data.mobile,
company: data.company,
department: data.department,
status: data.status,
reportTime: data.reportTime
})
},
// 删除员工信息
deleteByStaffInfo(id) {
return request.get('/staff/deleteById', {
params: {
id: id
}
})
}
}
export default example

10
frontend/src/api/index.js Normal file
View File

@ -0,0 +1,10 @@
/**
api接口的统一出口
*/
import example from './example'
export default {
example
// xxxx
}

View File

@ -0,0 +1,42 @@
import request from '@/utils/request'
export function dsGrid(pageIndex, pageSize, data) {
return request({
url: 'datasource/list/' + pageIndex + '/' + pageSize,
method: 'post',
data
})
}
export function addDs(data) {
return request({
url: 'datasource/add/',
method: 'post',
data
})
}
export function editDs(data) {
return request({
url: 'datasource/update/',
method: 'post',
data
})
}
export function delDs(id) {
return request({
url: 'datasource/delete/' + id,
method: 'post'
})
}
export function validateDs(data) {
return request({
url: 'datasource/validate/',
method: 'post',
data
})
}
export default { dsGrid, addDs, editDs, delDs, validateDs }

View File

@ -0,0 +1,34 @@
import request from '@/utils/request'
export function getDeptTree(pid) {
return request({
url: 'api/dept/childNodes/' + pid,
method: 'post'
})
}
export function addDept(data) {
return request({
url: '/api/dept/create',
method: 'post',
data
})
}
export function delDept(ids) {
return request({
url: '/api/dept/delete',
method: 'post',
data: ids
})
}
export function editDept(data) {
return request({
url: '/api/dept/update',
method: 'post',
data
})
}
export default { addDept, delDept, editDept, getDeptTree }

View File

@ -0,0 +1,48 @@
import request from '@/utils/request'
export function getMenusTree(pid) {
return request({
url: 'api/menu/childNodes/' + pid,
method: 'post'
})
}
export function getChild(id) {
return request({
url: '/api/menu/childMenus/' + id,
method: 'post'
})
}
export function buildMenus() {
return request({
url: 'api/dynamicMenu/menus',
method: 'post'
})
}
export function addMenu(data) {
return request({
url: '/api/menu/create',
method: 'post',
data
})
}
export function delMenu(ids) {
return request({
url: '/api/menu/delete',
method: 'post',
data: ids
})
}
export function editMenu(data) {
return request({
url: '/api/menu/update',
method: 'post',
data
})
}
export default { addMenu, editMenu, delMenu, getMenusTree, getChild }

View File

@ -0,0 +1,48 @@
import request from '@/utils/request'
export function allRoles() {
return request({
url: '/api/role/all',
method: 'post'
})
}
export function roleGrid(pageIndex, pageSize, data) {
return request({
url: '/api/role/roleGrid/' + pageIndex + '/' + pageSize,
method: 'post',
data
})
}
export function delRole(pid) {
return request({
url: '/api/role/delete/' + pid,
method: 'post'
})
}
export function addRole(data) {
return request({
url: '/api/role/create',
method: 'post',
data
})
}
export function editRole(data) {
return request({
url: '/api/role/update',
method: 'post',
data
})
}
export function addRoleMenus(data) {
return request({
url: '/api/role/saveRolesMenus',
method: 'post',
data
})
}
export default { addRole, editRole, delRole, roleGrid, allRoles, addRoleMenus }

View File

@ -0,0 +1,9 @@
import request from '@/utils/request'
export function userLists(page, size, data) {
return request({
url: 'api/user/userGrid/' + page + '/' + size,
method: 'post',
data
})
}

View File

@ -0,0 +1,9 @@
import request from '@/utils/request'
export function getList(params) {
return request({
url: '/vue-admin-template/table/list',
method: 'get',
params
})
}

23
frontend/src/api/user.js Normal file
View File

@ -0,0 +1,23 @@
import request from '@/utils/request'
export function login(data) {
return request({
url: '/api/auth/login',
method: 'post',
data
})
}
export function getInfo(token) {
return request({
url: '/api/auth/userInfo',
method: 'post'
})
}
export function logout() {
return request({
url: '/api/auth/logout',
method: 'post'
})
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 355 KiB

View File

@ -0,0 +1,79 @@
<template>
<el-breadcrumb class="app-breadcrumb" separator="/">
<transition-group name="breadcrumb">
<el-breadcrumb-item v-for="(item,index) in levelList" :key="item.path">
<span v-if="index === 0">当前位置</span>
<span v-if="item.redirect==='noRedirect'||index==levelList.length-1" class="no-redirect">{{item.meta.title }}</span>
<a v-else @click.prevent="handleLink(item)">{{ item.meta.title }}</a>
</el-breadcrumb-item>
</transition-group>
</el-breadcrumb>
</template>
<script>
import pathToRegexp from 'path-to-regexp'
export default {
data() {
return {
levelList: null
}
},
watch: {
$route() {
this.getBreadcrumb()
}
},
created() {
this.getBreadcrumb()
},
methods: {
getBreadcrumb() {
// only show routes with meta.title
let matched = this.$route.matched.filter(item => item.meta && item.meta.title)
const first = matched[0]
if (!this.isDashboard(first)) {
matched = [{ path: '/dashboard', meta: { title: 'Dashboard' }}].concat(matched)
}
this.levelList = matched.filter(item => item.meta && item.meta.title && item.meta.breadcrumb !== false)
},
isDashboard(route) {
const name = route && route.name
if (!name) {
return false
}
return name.trim().toLocaleLowerCase() === 'Dashboard'.toLocaleLowerCase()
},
pathCompile(path) {
// To solve this problem https://github.com/PanJiaChen/vue-element-admin/issues/561
const { params } = this.$route
var toPath = pathToRegexp.compile(path)
return toPath(params)
},
handleLink(item) {
const { redirect, path } = item
if (redirect) {
this.$router.push(redirect)
return
}
this.$router.push(this.pathCompile(path))
}
}
}
</script>
<style lang="scss" scoped>
.app-breadcrumb.el-breadcrumb {
display: inline-block;
font-size: 14px;
line-height: 50px;
margin-left: 8px;
.no-redirect {
color: #97a8be;
cursor: text;
}
}
</style>

View File

@ -0,0 +1,16 @@
<template>
<div>
<svg-icon icon-class="doc" @click="click" />
</div>
</template>
<script>
export default {
name: 'Doc',
methods: {
click() {
window.open(process.env.VUE_APP_BASE_API + 'doc.html', '_blank')
}
}
}
</script>

View File

@ -0,0 +1,44 @@
<template>
<div style="padding: 0 15px;" @click="toggleClick">
<svg
:class="{'is-active':isActive}"
class="hamburger"
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
width="64"
height="64"
>
<path d="M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM142.4 642.1L298.7 519a8.84 8.84 0 0 0 0-13.9L142.4 381.9c-5.8-4.6-14.4-.5-14.4 6.9v246.3a8.9 8.9 0 0 0 14.4 7z" />
</svg>
</div>
</template>
<script>
export default {
name: 'Hamburger',
props: {
isActive: {
type: Boolean,
default: false
}
},
methods: {
toggleClick() {
this.$emit('toggleClick')
}
}
}
</script>
<style scoped>
.hamburger {
display: inline-block;
vertical-align: middle;
width: 20px;
height: 20px;
}
.hamburger.is-active {
transform: rotate(180deg);
}
</style>

View File

@ -0,0 +1,68 @@
<!-- @author zhengjie -->
<template>
<div class="icon-body">
<el-input v-model="name" style="position: relative;" clearable placeholder="请输入图标名称" @clear="filterIcons" @input.native="filterIcons">
<i slot="suffix" class="el-icon-search el-input__icon" />
</el-input>
<div class="icon-list">
<div v-for="(item, index) in iconList" :key="index" @click="selectedIcon(item)">
<svg-icon :icon-class="item" style="height: 30px;width: 16px;" />
<span>{{ item }}</span>
</div>
</div>
</div>
</template>
<script>
import icons from './requireIcons'
export default {
name: 'IconSelect',
data() {
return {
name: '',
iconList: icons
}
},
methods: {
filterIcons() {
this.iconList = icons
if (this.name) {
this.iconList = this.iconList.filter(item => item.includes(this.name))
}
},
selectedIcon(name) {
this.$emit('selected', name)
document.body.click()
},
reset() {
this.name = ''
this.iconList = icons
}
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
.icon-body {
width: 100%;
padding: 10px;
.icon-list {
height: 200px;
overflow-y: scroll;
div {
height: 30px;
line-height: 30px;
margin-bottom: -5px;
cursor: pointer;
width: 33%;
float: left;
}
span {
display: inline-block;
vertical-align: -0.15em;
fill: currentColor;
overflow: hidden;
}
}
}
</style>

View File

@ -0,0 +1,11 @@
const req = require.context('@/icons/svg', false, /\.svg$/)
const requireAll = requireContext => requireContext.keys()
const re = /\.\/(.*)\.svg/
const icons = requireAll(req).map(i => {
return i.match(re)[1]
})
export default icons

View File

@ -0,0 +1,44 @@
<template>
<el-dropdown trigger="click" class="international" @command="handleSetLanguage">
<div>
<svg-icon class-name="international-icon" icon-class="language" />
</div>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item :disabled="language==='zh'" command="zh">
简体
</el-dropdown-item>
<el-dropdown-item :disabled="language==='tw'" command="tw">
繁体
</el-dropdown-item>
<el-dropdown-item :disabled="language==='en'" command="en">
English
</el-dropdown-item>
<el-dropdown-item :disabled="language==='es'" command="es">
Español
</el-dropdown-item>
<el-dropdown-item :disabled="language==='ja'" command="ja">
日本語
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</template>
<script>
export default {
computed: {
language() {
return this.$store.getters.language
}
},
methods: {
handleSetLanguage(lang) {
this.$i18n.locale = lang
this.$store.dispatch('app/setLanguage', lang)
this.$message({
message: 'Switch Language Success',
type: 'success'
})
}
}
}
</script>

View File

@ -0,0 +1,101 @@
<template>
<div :class="{'hidden':hidden}" class="pagination-container">
<el-pagination
:background="background"
:current-page.sync="currentPage"
:page-size.sync="pageSize"
:layout="layout"
:page-sizes="pageSizes"
:total="total"
v-bind="$attrs"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</template>
<script>
import { scrollTo } from '@/utils/scroll-to'
export default {
name: 'Pagination',
props: {
total: {
required: true,
type: Number
},
page: {
type: Number,
default: 0
},
limit: {
type: Number,
default: 10
},
pageSizes: {
type: Array,
default() {
return [10, 20, 30, 50]
}
},
layout: {
type: String,
default: 'total, sizes, prev, pager, next, jumper'
},
background: {
type: Boolean,
default: true
},
autoScroll: {
type: Boolean,
default: true
},
hidden: {
type: Boolean,
default: false
}
},
computed: {
currentPage: {
get() {
return this.page
},
set(val) {
this.$emit('update:page', val)
}
},
pageSize: {
get() {
return this.limit
},
set(val) {
this.$emit('update:limit', val)
}
}
},
methods: {
handleSizeChange(val) {
this.$emit('pagination', { page: this.currentPage, limit: val })
if (this.autoScroll) {
scrollTo(0, 800)
}
},
handleCurrentChange(val) {
this.$emit('pagination', { page: val, limit: this.pageSize })
if (this.autoScroll) {
scrollTo(0, 800)
}
}
}
}
</script>
<style scoped>
.pagination-container {
background: #fff;
padding: 32px 16px;
}
.pagination-container.hidden {
display: none;
}
</style>

View File

@ -0,0 +1,145 @@
<template>
<div ref="rightPanel" :class="{show:show}" class="rightPanel-container">
<div class="rightPanel-background" />
<div class="rightPanel">
<div class="handle-button" :style="{'top':buttonTop+'px','background-color':theme}" @click="show=!show">
<i :class="show?'el-icon-close':'el-icon-setting'" />
</div>
<div class="rightPanel-items">
<slot />
</div>
</div>
</div>
</template>
<script>
import { addClass, removeClass } from '@/utils'
export default {
name: 'RightPanel',
props: {
clickNotClose: {
default: false,
type: Boolean
},
buttonTop: {
default: 250,
type: Number
}
},
data() {
return {
show: false
}
},
computed: {
theme() {
return this.$store.state.settings.theme
}
},
watch: {
show(value) {
if (value && !this.clickNotClose) {
this.addEventClick()
}
if (value) {
addClass(document.body, 'showRightPanel')
} else {
removeClass(document.body, 'showRightPanel')
}
}
},
mounted() {
this.insertToBody()
},
beforeDestroy() {
const elx = this.$refs.rightPanel
elx.remove()
},
methods: {
addEventClick() {
window.addEventListener('click', this.closeSidebar)
},
closeSidebar(evt) {
const parent = evt.target.closest('.rightPanel')
if (!parent) {
this.show = false
window.removeEventListener('click', this.closeSidebar)
}
},
insertToBody() {
const elx = this.$refs.rightPanel
const body = document.querySelector('body')
body.insertBefore(elx, body.firstChild)
}
}
}
</script>
<style>
.showRightPanel {
overflow: hidden;
position: relative;
width: calc(100% - 15px);
}
</style>
<style lang="scss" scoped>
.rightPanel-background {
position: fixed;
top: 0;
left: 0;
opacity: 0;
transition: opacity .3s cubic-bezier(.7, .3, .1, 1);
background: rgba(0, 0, 0, .2);
z-index: -1;
}
.rightPanel {
width: 100%;
max-width: 260px;
height: 100vh;
position: fixed;
top: 0;
right: 0;
box-shadow: 0px 0px 15px 0px rgba(0, 0, 0, .05);
transition: all .25s cubic-bezier(.7, .3, .1, 1);
transform: translate(100%);
background: #fff;
z-index: 40000;
}
.show {
transition: all .3s cubic-bezier(.7, .3, .1, 1);
.rightPanel-background {
z-index: 20000;
opacity: 1;
width: 100%;
height: 100%;
}
.rightPanel {
transform: translate(0);
}
}
.handle-button {
width: 48px;
height: 48px;
position: absolute;
left: -48px;
text-align: center;
font-size: 24px;
border-radius: 6px 0 0 6px !important;
z-index: 0;
pointer-events: auto;
cursor: pointer;
color: #fff;
line-height: 48px;
i {
font-size: 24px;
line-height: 48px;
}
}
</style>

View File

@ -0,0 +1,60 @@
<template>
<div>
<svg-icon :icon-class="isFullscreen?'exit-fullscreen':'fullscreen'" @click="click" />
</div>
</template>
<script>
import screenfull from 'screenfull'
export default {
name: 'Screenfull',
data() {
return {
isFullscreen: false
}
},
mounted() {
this.init()
},
beforeDestroy() {
this.destroy()
},
methods: {
click() {
if (!screenfull.enabled) {
this.$message({
message: 'you browser can not work',
type: 'warning'
})
return false
}
screenfull.toggle()
},
change() {
this.isFullscreen = screenfull.isFullscreen
},
init() {
if (screenfull.enabled) {
screenfull.on('change', this.change)
}
},
destroy() {
if (screenfull.enabled) {
screenfull.off('change', this.change)
}
}
}
}
</script>
<style scoped>
.screenfull-svg {
display: inline-block;
cursor: pointer;
fill: #5a5e66;;
width: 20px;
height: 20px;
vertical-align: 10px;
}
</style>

View File

@ -0,0 +1,107 @@
<template>
<div class="drawer-container">
<div>
<h3 class="drawer-title">{{ $t('settings.title') }}</h3>
<div class="drawer-item">
<span>{{ $t('settings.theme') }}</span>
<theme-picker style="float: right;height: 26px;margin: -3px 8px 0 0;" @change="themeChange" />
</div>
<div class="drawer-item">
<span>{{ $t('settings.fixedHeader') }}</span>
<el-switch v-model="fixedHeader" class="drawer-switch" />
</div>
<div class="drawer-item">
<span>{{ $t('settings.sidebarLogo') }}</span>
<el-switch v-model="sidebarLogo" class="drawer-switch" />
</div>
</div>
</div>
</template>
<script>
import ThemePicker from '@/components/ThemePicker'
export default {
name: 'Settings',
components: { ThemePicker },
data() {
return {}
},
computed: {
fixedHeader: {
get() {
return this.$store.state.settings.fixedHeader
},
set(val) {
this.$store.dispatch('settings/changeSetting', {
key: 'fixedHeader',
value: val
})
}
},
sidebarLogo: {
get() {
return this.$store.state.settings.sidebarLogo
},
set(val) {
this.$store.dispatch('settings/changeSetting', {
key: 'sidebarLogo',
value: val
})
}
},
lang() {
return this.$store.getters.language
}
},
methods: {
themeChange(val) {
this.$store.dispatch('settings/changeSetting', {
key: 'theme',
value: val
})
}
}
}
</script>
<style lang="scss" scoped>
.drawer-container {
padding: 24px;
font-size: 14px;
line-height: 1.5;
word-wrap: break-word;
.drawer-title {
margin-bottom: 12px;
color: rgba(0, 0, 0, .85);
font-size: 14px;
line-height: 22px;
}
.drawer-item {
color: rgba(0, 0, 0, .65);
font-size: 14px;
padding: 12px 0;
}
.drawer-switch {
float: right
}
.job-link{
display: block;
position: absolute;
width: 100%;
left: 0;
bottom: 0;
}
}
</style>

View File

@ -0,0 +1,56 @@
<template>
<el-dropdown trigger="click" @command="handleSetSize">
<div>
<svg-icon class-name="size-icon" icon-class="size" />
</div>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item v-for="item of sizeOptions" :key="item.value" :disabled="size===item.value" :command="item.value">
{{
item.label }}
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</template>
<script>
export default {
data() {
return {
sizeOptions: [
{ label: 'Default', value: 'default' },
{ label: 'Medium', value: 'medium' },
{ label: 'Small', value: 'small' },
{ label: 'Mini', value: 'mini' }
]
}
},
computed: {
size() {
return this.$store.getters.size
}
},
methods: {
handleSetSize(size) {
this.$ELEMENT.size = size
this.$store.dispatch('app/setSize', size)
this.refreshView()
this.$message({
message: '布局设置成功',
type: 'success'
})
},
refreshView() {
// In order to make the cached page re-rendered
// this.$store.dispatch('tagsView/delAllCachedViews', this.$route)
const { fullPath } = this.$route
this.$nextTick(() => {
this.$router.replace({
path: '/redirect' + fullPath
})
})
}
}
}
</script>

View File

@ -0,0 +1,62 @@
<template>
<div v-if="isExternal" :style="styleExternalIcon" class="svg-external-icon svg-icon" v-on="$listeners" />
<svg v-else :class="svgClass" aria-hidden="true" v-on="$listeners">
<use :href="iconName" />
</svg>
</template>
<script>
// doc: https://panjiachen.github.io/vue-element-admin-site/feature/component/svg-icon.html#usage
import { isExternal } from '@/utils/validate'
export default {
name: 'SvgIcon',
props: {
iconClass: {
type: String,
required: true
},
className: {
type: String,
default: ''
}
},
computed: {
isExternal() {
return isExternal(this.iconClass)
},
iconName() {
return `#icon-${this.iconClass}`
},
svgClass() {
if (this.className) {
return 'svg-icon ' + this.className
} else {
return 'svg-icon'
}
},
styleExternalIcon() {
return {
mask: `url(${this.iconClass}) no-repeat 50% 50%`,
'-webkit-mask': `url(${this.iconClass}) no-repeat 50% 50%`
}
}
}
}
</script>
<style scoped>
.svg-icon {
width: 1em;
height: 1em;
vertical-align: -0.15em;
fill: currentColor;
overflow: hidden;
}
.svg-external-icon {
background-color: currentColor;
mask-size: cover!important;
display: inline-block;
}
</style>

View File

@ -0,0 +1,165 @@
<template>
<el-color-picker
v-model="theme"
:predefine="['#409EFF', '#1890ff', '#304156','#212121','#11a983', '#13c2c2', '#6959CD', '#f5222d']"
class="theme-picker"
popper-class="theme-picker-dropdown"
/>
</template>
<script>
const version = require('element-ui/package.json').version // element-ui version from node_modules
const ORIGINAL_THEME = '#409EFF' // default color
import Cookies from 'js-cookie'
export default {
data() {
return {
chalk: '', // content of theme-chalk css
theme: ''
}
},
computed: {
defaultTheme() {
return this.$store.state.settings.theme
}
},
watch: {
defaultTheme: {
handler: function(val, oldVal) {
this.theme = val
},
immediate: true
},
async theme(val) {
Cookies.set('theme', val, { expires: 365 })
const oldVal = this.chalk ? this.theme : Cookies.get('theme') ? Cookies.get('theme') : ORIGINAL_THEME
if (typeof val !== 'string') return
const themeCluster = this.getThemeCluster(val.replace('#', ''))
const originalCluster = this.getThemeCluster(oldVal.replace('#', ''))
const getHandler = (variable, id) => {
return () => {
const originalCluster = this.getThemeCluster(ORIGINAL_THEME.replace('#', ''))
const newStyle = this.updateStyle(this[variable], originalCluster, themeCluster)
let styleTag = document.getElementById(id)
if (!styleTag) {
styleTag = document.createElement('style')
styleTag.setAttribute('id', id)
document.head.appendChild(styleTag)
}
styleTag.innerText = newStyle
}
}
if (!this.chalk) {
const url = `https://unpkg.com/element-ui@${version}/lib/theme-chalk/index.css`
await this.getCSSString(url, 'chalk')
}
const chalkHandler = getHandler('chalk', 'chalk-style')
chalkHandler()
const styles = [].slice.call(document.querySelectorAll('style'))
.filter(style => {
const text = style.innerText
return new RegExp(oldVal, 'i').test(text) && !/Chalk Variables/.test(text)
})
styles.forEach(style => {
const { innerText } = style
if (typeof innerText !== 'string') return
style.innerText = this.updateStyle(innerText, originalCluster, themeCluster)
})
this.$emit('change', val)
}
},
methods: {
updateStyle(style, oldCluster, newCluster) {
let newStyle = style
oldCluster.forEach((color, index) => {
newStyle = newStyle.replace(new RegExp(color, 'ig'), newCluster[index])
})
return newStyle
},
getCSSString(url, variable) {
return new Promise(resolve => {
const xhr = new XMLHttpRequest()
xhr.onreadystatechange = () => {
if (xhr.readyState === 4 && xhr.status === 200) {
this[variable] = xhr.responseText.replace(/@font-face{[^}]+}/, '')
resolve()
}
}
xhr.open('GET', url)
xhr.send()
})
},
getThemeCluster(theme) {
const tintColor = (color, tint) => {
let red = parseInt(color.slice(0, 2), 16)
let green = parseInt(color.slice(2, 4), 16)
let blue = parseInt(color.slice(4, 6), 16)
if (tint === 0) { // when primary color is in its rgb space
return [red, green, blue].join(',')
} else {
red += Math.round(tint * (255 - red))
green += Math.round(tint * (255 - green))
blue += Math.round(tint * (255 - blue))
red = red.toString(16)
green = green.toString(16)
blue = blue.toString(16)
return `#${red}${green}${blue}`
}
}
const shadeColor = (color, shade) => {
let red = parseInt(color.slice(0, 2), 16)
let green = parseInt(color.slice(2, 4), 16)
let blue = parseInt(color.slice(4, 6), 16)
red = Math.round((1 - shade) * red)
green = Math.round((1 - shade) * green)
blue = Math.round((1 - shade) * blue)
red = red.toString(16)
green = green.toString(16)
blue = blue.toString(16)
return `#${red}${green}${blue}`
}
const clusters = [theme]
for (let i = 0; i <= 9; i++) {
clusters.push(tintColor(theme, Number((i / 10).toFixed(2))))
}
clusters.push(shadeColor(theme, 0.1))
return clusters
}
}
}
</script>
<style>
.theme-message,
.theme-picker-dropdown {
z-index: 99999 !important;
}
.theme-picker .el-color-picker__trigger {
height: 26px !important;
width: 26px !important;
padding: 2px;
}
.theme-picker-dropdown .el-color-dropdown__link-btn {
display: none;
}
</style>

View File

@ -0,0 +1,27 @@
<template>
<div class="content-container">
<slot />
</div>
</template>
<script>
export default {
name: 'LayoutContent'
}
</script>
<style lang="scss">
@import "~@/styles/element-variables";
.content-container {
transition: 0.3s;
color: $--color-text-primary;
background-color: #FFFFFF;
overflow: auto;
height: 100%;
padding: 20px;
border-radius: 4px;
box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 14%);
box-sizing: border-box;
}
</style>

View File

@ -0,0 +1,85 @@
<template>
<div class="complex-table">
<div v-if="$slots.header || header" class="complex-table__header">
<slot name="header">{{ header }}</slot>
</div>
<div v-if="$slots.toolbar || searchConfig" class="complex-table__toolbar">
<slot name="toolbar">
<fu-search-bar v-bind="searchConfig" @exec="search">
<slot name="buttons" />
<fu-table-column-select :columns="columns" />
</fu-search-bar>
</slot>
</div>
<div class="complex-table__body">
<fu-table v-bind="$attrs" :columns="columns" :local-key="localKey" v-on="$listeners">
<slot />
</fu-table>
</div>
<div v-if="$slots.pagination || paginationConfig" class="complex-table__pagination">
<slot name="pagination">
<fu-table-pagination
:current-page.sync="paginationConfig.currentPage"
:page-size.sync="paginationConfig.pageSize"
v-bind="paginationConfig"
@change="search"
/>
</slot>
</div>
</div>
</template>
<script>
export default {
name: 'ComplexTable',
props: {
columns: {
type: Array,
default: () => []
},
localKey: String, // Key
header: String,
searchConfig: Object,
paginationConfig: Object
},
data() {
return {
condition: {}
}
},
methods: {
search(condition, e) {
if (condition) {
this.condition = condition
}
this.$emit('search', this.condition, e)
}
}
}
</script>
<style lang="scss">
@import "~@/styles/mixin.scss";
@import "~@/styles/element-variables.scss";
.complex-table {
.complex-table__header {
@include flex-row(flex-start, center);
height: 60px;
font-size: 20px;
}
.complex-table__toolbar {
@include flex-row(flex-end, center);
}
.complex-table__pagination {
margin-top: 20px;
@include flex-row(flex-end);
}
}
</style>

View File

@ -0,0 +1,60 @@
const options = function(value, array) {
if (!value) return ''
if (array) {
for (let i = 0; i < array.length; i++) {
if (value === array[i].key) {
return array[i].value
}
}
}
return value
}
const timestampFormatDate = function(timestamp, showMs) {
if (!timestamp) {
return timestamp
}
const date = new Date(timestamp)
const y = date.getFullYear()
let MM = date.getMonth() + 1
MM = MM < 10 ? ('0' + MM) : MM
let d = date.getDate()
d = d < 10 ? ('0' + d) : d
let h = date.getHours()
h = h < 10 ? ('0' + h) : h
let m = date.getMinutes()
m = m < 10 ? ('0' + m) : m
let s = date.getSeconds()
s = s < 10 ? ('0' + s) : s
let format = y + '-' + MM + '-' + d + ' ' + h + ':' + m + ':' + s
if (showMs === true) {
const ms = date.getMilliseconds()
format += ':' + ms
}
return format
}
const filters = {
'options': options,
'timestampFormatDate': timestampFormatDate
}
export default {
install(Vue) {
// 注册公用过滤器
Object.keys(filters).forEach(key => {
Vue.filter(key, filters[key])
})
}
}

View File

@ -0,0 +1,9 @@
import Vue from 'vue'
import SvgIcon from '@/components/SvgIcon'// svg component
// register globally
Vue.component('svg-icon', SvgIcon)
const req = require.context('./svg', false, /\.svg$/)
const requireAll = requireContext => requireContext.keys().map(requireContext)
requireAll(req)

View File

@ -0,0 +1 @@
<svg width="128" height="100" xmlns="http://www.w3.org/2000/svg"><path d="M27.429 63.638c0-2.508-.893-4.65-2.679-6.424-1.786-1.775-3.94-2.662-6.464-2.662-2.524 0-4.679.887-6.465 2.662-1.785 1.774-2.678 3.916-2.678 6.424 0 2.508.893 4.65 2.678 6.424 1.786 1.775 3.94 2.662 6.465 2.662 2.524 0 4.678-.887 6.464-2.662 1.786-1.775 2.679-3.916 2.679-6.424zm13.714-31.801c0-2.508-.893-4.65-2.679-6.424-1.785-1.775-3.94-2.662-6.464-2.662-2.524 0-4.679.887-6.464 2.662-1.786 1.774-2.679 3.916-2.679 6.424 0 2.508.893 4.65 2.679 6.424 1.785 1.774 3.94 2.662 6.464 2.662 2.524 0 4.679-.888 6.464-2.662 1.786-1.775 2.679-3.916 2.679-6.424zM71.714 65.98l7.215-27.116c.285-1.23.107-2.378-.536-3.443-.643-1.064-1.56-1.762-2.75-2.094-1.19-.33-2.333-.177-3.429.462-1.095.639-1.81 1.573-2.143 2.804l-7.214 27.116c-2.857.237-5.405 1.266-7.643 3.088-2.238 1.822-3.738 4.152-4.5 6.992-.952 3.644-.476 7.098 1.429 10.364 1.905 3.265 4.69 5.37 8.357 6.317 3.667.947 7.143.474 10.429-1.42 3.285-1.892 5.404-4.66 6.357-8.305.762-2.84.619-5.607-.429-8.305-1.047-2.697-2.762-4.85-5.143-6.46zm47.143-2.342c0-2.508-.893-4.65-2.678-6.424-1.786-1.775-3.94-2.662-6.465-2.662-2.524 0-4.678.887-6.464 2.662-1.786 1.774-2.679 3.916-2.679 6.424 0 2.508.893 4.65 2.679 6.424 1.786 1.775 3.94 2.662 6.464 2.662 2.524 0 4.679-.887 6.465-2.662 1.785-1.775 2.678-3.916 2.678-6.424zm-45.714-45.43c0-2.509-.893-4.65-2.679-6.425C68.68 10.01 66.524 9.122 64 9.122c-2.524 0-4.679.887-6.464 2.661-1.786 1.775-2.679 3.916-2.679 6.425 0 2.508.893 4.65 2.679 6.424 1.785 1.774 3.94 2.662 6.464 2.662 2.524 0 4.679-.888 6.464-2.662 1.786-1.775 2.679-3.916 2.679-6.424zm32 13.629c0-2.508-.893-4.65-2.679-6.424-1.785-1.775-3.94-2.662-6.464-2.662-2.524 0-4.679.887-6.464 2.662-1.786 1.774-2.679 3.916-2.679 6.424 0 2.508.893 4.65 2.679 6.424 1.785 1.774 3.94 2.662 6.464 2.662 2.524 0 4.679-.888 6.464-2.662 1.786-1.775 2.679-3.916 2.679-6.424zM128 63.638c0 12.351-3.357 23.78-10.071 34.286-.905 1.372-2.19 2.058-3.858 2.058H13.93c-1.667 0-2.953-.686-3.858-2.058C3.357 87.465 0 76.037 0 63.638c0-8.613 1.69-16.847 5.071-24.703C8.452 31.08 13 24.312 18.714 18.634c5.715-5.68 12.524-10.199 20.429-13.559C47.048 1.715 55.333.035 64 .035c8.667 0 16.952 1.68 24.857 5.04 7.905 3.36 14.714 7.88 20.429 13.559 5.714 5.678 10.262 12.446 13.643 20.301 3.38 7.856 5.071 16.09 5.071 24.703z"/></svg>

After

Width:  |  Height:  |  Size: 2.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="1574649229600" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3752" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M942 191.9C939.8 128.9 748.1 78 512 78S84.2 128.9 82 191.9V831c0 63.5 192.5 115 430 115s430-51.5 430-115V191.9z m-56.7 393.8c-4.6 3.3-11.6 7.4-21.9 12.2-21.3 9.8-50.5 19.1-84.4 26.8-74 16.8-168.8 26-267 26s-193-9.2-267-26c-33.9-7.7-63.1-16.9-84.4-26.8-10.3-4.8-17.3-8.9-21.9-12.2 0.1-0.1 0.2-0.1 0.3-0.2h-7v-123c72.2 36.4 215.3 61.1 380 61.1s307.8-24.8 380-61.1v122.9h-7l0.3 0.3z m0-177c-4.6 3.3-11.6 7.4-21.9 12.2-21.3 9.8-50.5 19.1-84.4 26.8-74 16.8-168.8 26-267 26s-193-9.2-267-26c-33.9-7.7-63.1-16.9-84.4-26.8-10.3-4.8-17.3-8.9-21.9-12.2 0.1-0.1 0.2-0.1 0.3-0.2h-7V246.9c72.2 36.4 215.3 61.1 380 61.1s307.8-24.8 380-61.1v161.6h-7c0.1 0 0.2 0.1 0.3 0.2zM160.7 180.8C182 171 211.2 161.7 245 154c74-16.8 168.8-26 267-26s193 9.2 267 26c33.9 7.7 63.1 16.9 84.4 26.8 10.3 4.8 17.3 8.9 21.9 12.2-4.6 3.3-11.6 7.4-21.9 12.2C842 215 812.8 224.3 779 232c-74 16.8-168.8 26-267 26s-193-9.2-267-26c-33.9-7.7-63.1-16.9-84.4-26.8-10.3-4.8-17.3-8.9-21.9-12.2 4.7-3.3 11.7-7.4 22-12.2zM885.3 831c-4.6 3.3-11.6 7.4-21.9 12.2C842 853 812.8 862.3 779 870c-74 16.8-168.8 26-267 26s-193-9.2-267-26c-33.9-7.7-63.1-16.9-84.4-26.8-10.3-4.8-17.3-8.9-21.9-12.2 0.1-0.1 0.2-0.1 0.3-0.2h-7V639.5c72.2 36.4 215.3 61.1 380 61.1s307.8-24.8 380-61.1v191.3h-7c0.1 0.1 0.2 0.1 0.3 0.2z" fill="#cdcdcd" p-id="3753"></path></svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200" class="icon" p-id="1799" t="1553478255619" version="1.1" viewBox="0 0 1024 1024"><defs><style type="text/css"/></defs><path fill="#bfbfbf" d="M329.285097 317.714062l-8.422833 4.428869c-8.78099 4.584412-13.528108 14.84715-11.923564 24.415063 1.644453 4.909823 3.491521 9.864672 5.492084 14.747889 2.030239 4.854565 4.230348 9.652847 6.53688 14.293541 5.621021 7.891737 16.246009 11.824303 25.699312 8.858762l9.041934-2.868327c14.741749-3.860934 31.115672-0.056282 42.62582 11.512195 11.549034 11.526521 15.374152 27.863604 11.549034 42.570561l-2.882654 9.126868c-2.958378 9.438976 0.938372 20.042475 8.830109 25.706475 4.634554 2.328022 9.403161 4.52813 14.323217 6.529717 4.876054 2.043542 9.80839 3.846608 14.739702 5.478781 9.538237 1.603521 19.87363-3.123131 24.414039-11.910261l4.402263-8.388041c7.67889-13.144368 21.915126-22.002107 38.267559-22.002107 16.338107 0 30.547737 8.829086 38.255279 21.931498l4.41352 8.459672c4.584412 8.78713 14.84715 13.513782 24.414039 11.910261 4.91187-1.632173 9.851369-3.462868 14.734586-5.478781 4.882194-2.030239 9.66615-4.201695 14.322194-6.529717 7.891737-5.622044 11.809977-16.253172 8.843412-25.706475l-2.852978-9.041934c-3.859911-14.733563-0.069585-31.085996 11.484565-42.655496 11.55415-11.525498 27.878954-15.372106 42.599214-11.512195l9.097192 2.88163c9.426697 2.952238 20.044522-0.937348 25.693172-8.829086 2.313695-4.656043 4.527107-9.411347 6.54302-14.322194 2.029216-4.883217 3.847631-9.80839 5.495154-14.748912 1.616824-9.581216-3.108804-19.843954-11.911284-24.429389l-8.402367-4.400217c-13.132088-7.665587-21.98778-21.901823-21.98778-38.255279 0-16.32378 8.830109-30.589692 21.974477-38.268582l8.416693-4.443196c8.80248-4.571109 13.528108-14.832823 11.924587-24.400736-1.6465-4.910846-3.479241-9.850345-5.493108-14.733563-2.031263-4.868891-4.202719-9.680477-6.529717-14.308891-5.622044-7.890714-16.253172-11.82328-25.708522-8.842389l-9.05626 2.852978c-14.747889 3.861958-31.071669 0.057305-42.654472-11.512195-11.55415-11.55415-15.344476-27.877931-11.484565-42.612517l2.852978-9.05626c2.966565-9.44-0.951675-20.043499-8.856715-25.692149-4.641717-2.328022-9.397021-4.542456-14.307867-6.544043-4.883217-2.029216-9.82374-3.846608-14.734586-5.465478-9.567913-1.632173-19.872606 3.123131-24.414039 11.895935l-4.400217 8.389064c-7.67889 13.174044-21.931498 22.002107-38.268582 22.002107-16.309454 0-30.576389-8.828063-38.267559-22.002107l-4.387937-8.389064c-4.554736-8.771781-14.8318-13.528108-24.405853-11.895935-4.954849 1.604544-9.873882 3.435239-14.763239 5.4225-4.883217 2.044566-9.688663 4.217045-14.323217 6.545066-7.891737 5.649674-11.808954 16.266475-8.830109 25.735128l2.826372 9.05626c3.882424 14.762215 0.057305 31.085996-11.491729 42.612517-11.510148 11.5695-27.849278 15.373129-42.611493 11.526521l-9.070586-2.867304c-9.44-2.980891-20.063965 0.951675-25.686009 8.842389-2.342348 4.628414-4.52813 9.44-6.53688 14.308891-2.036379 4.882194-3.847631 9.822716-5.492084 14.733563-1.603521 9.581216 3.142573 19.85828 11.923564 24.443715l8.402367 4.400217c13.156648 7.67889 21.986757 21.944801 21.986757 38.268582C351.251388 295.79689 342.421278 310.019823 329.285097 317.714062zM511.977999 171.706687c59.532885 0 107.795075 48.275493 107.795075 107.779725 0 59.490929-48.26219 107.752096-107.795075 107.752096-59.533908 0-107.752096-48.26219-107.752096-107.752096C404.226926 219.98218 452.445114 171.706687 511.977999 171.706687z" p-id="1800"/><path fill="#bfbfbf" d="M924.647713 689.174212 798.570249 689.174212 798.570249 581.650313c0-26.387997-21.476127-47.850821-47.864124-47.850821L276.2543 533.799492c-26.386974 0-47.851844 21.462824-47.851844 47.850821l0 107.523899L99.345124 689.174212c-20.419052 0-36.95568 16.550954-36.95568 36.948517l0 184.771237c0 20.399609 16.536628 36.962843 36.95568 36.962843l273.965675 0c20.397562 0 36.947494-16.564257 36.947494-36.962843L410.258293 726.122729c0-20.398586-16.550954-36.948517-36.947494-36.948517l-123.103736 0L250.207064 581.650313c0-14.366196 11.68104-26.047236 26.047236-26.047236l474.451826 0c14.364149 0 26.062586 11.68104 26.062586 26.047236l0 107.523899L650.689201 689.174212c-20.412912 0-36.962843 16.550954-36.962843 36.948517l0 184.771237c0 20.399609 16.549931 36.962843 36.962843 36.962843l273.958512 0c20.397562 0 36.96182-16.564257 36.96182-36.962843L961.609533 726.122729C961.609533 705.725166 945.044252 689.174212 924.647713 689.174212z" p-id="1801"/></svg>

After

Width:  |  Height:  |  Size: 4.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="1583752001956" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="9290" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M891.8 320H608V39.2L891.8 320zM704 384v260c0 133.6-73 200.2-226.6 200.2H288V169.6h189.4c24 0 46.2 1.6 66.2 5v-168C521.8 2.2 498.8 0 474.4 0H96v1024h378.4C755.4 1024 896 894.8 896 636.2V384h-192z" fill="#707070" p-id="9291"></path></svg>

After

Width:  |  Height:  |  Size: 613 B

View File

@ -0,0 +1 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M96.258 57.462h31.421C124.794 27.323 100.426 2.956 70.287.07v31.422a32.856 32.856 0 0 1 25.971 25.97zm-38.796-25.97V.07C27.323 2.956 2.956 27.323.07 57.462h31.422a32.856 32.856 0 0 1 25.97-25.97zm12.825 64.766v31.421c30.46-2.885 54.507-27.253 57.713-57.712H96.579c-2.886 13.466-13.146 23.726-26.292 26.291zM31.492 70.287H.07c2.886 30.46 27.253 54.507 57.713 57.713V96.579c-13.466-2.886-23.726-13.146-26.291-26.292z"/></svg>

After

Width:  |  Height:  |  Size: 497 B

View File

@ -0,0 +1 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M49.217 41.329l-.136-35.24c-.06-2.715-2.302-4.345-5.022-4.405h-3.65c-2.712-.06-4.866 2.303-4.806 5.016l.152 19.164-24.151-23.79a6.698 6.698 0 0 0-9.499 0 6.76 6.76 0 0 0 0 9.526l23.93 23.713-18.345.074c-2.712-.069-5.228 1.813-5.64 5.02v3.462c.069 2.721 2.31 4.97 5.022 5.03l35.028-.207c.052.005.087.025.133.025l2.457.054a4.626 4.626 0 0 0 3.436-1.38c.88-.874 1.205-2.096 1.169-3.462l-.262-2.465c0-.048.182-.081.182-.136h.002zm52.523 51.212l18.32-.073c2.713.06 5.224-1.609 5.64-4.815v-3.462c-.068-2.722-2.317-4.97-5.021-5.04l-34.58.21c-.053 0-.086-.021-.138-.021l-2.451-.06a4.64 4.64 0 0 0-3.445 1.381c-.885.868-1.201 2.094-1.174 3.46l.27 2.46c.005.06-.177.095-.177.141l.141 34.697c.069 2.713 2.31 4.338 5.022 4.397l3.45.006c2.705.062 4.867-2.31 4.8-5.026l-.153-18.752 24.151 23.946a6.69 6.69 0 0 0 9.494 0 6.747 6.747 0 0 0 0-9.523L101.74 92.54v.001zM48.125 80.662a4.636 4.636 0 0 0-3.437-1.382l-2.457.06c-.05 0-.082.022-.137.022l-35.025-.21c-2.712.07-4.957 2.318-5.022 5.04v3.462c.409 3.206 2.925 4.874 5.633 4.814l18.554.06-24.132 23.928c-2.62 2.626-2.62 6.89 0 9.524a6.694 6.694 0 0 0 9.496 0l24.155-23.79-.155 18.866c-.06 2.722 2.094 5.093 4.801 5.025h3.65c2.72-.069 4.962-1.685 5.022-4.406l.141-34.956c0-.05-.182-.082-.182-.136l.262-2.46c.03-1.366-.286-2.592-1.166-3.46h-.001zM80.08 47.397a4.62 4.62 0 0 0 3.443 1.374l2.45-.054c.055 0 .088-.02.143-.028l35.08.21c2.712-.062 4.953-2.312 5.021-5.033l.009-3.463c-.417-3.211-2.937-5.084-5.64-5.025l-18.615-.073 23.917-23.715c2.63-2.623 2.63-6.879.008-9.513a6.691 6.691 0 0 0-9.494 0L92.251 26.016l.155-19.312c.065-2.713-2.097-5.085-4.802-5.025h-3.45c-2.713.069-4.954 1.693-5.022 4.406l-.139 35.247c0 .054.18.088.18.136l-.267 2.465c-.028 1.366.288 2.588 1.174 3.463v.001z"/></svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -0,0 +1 @@
<svg class="icon" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="128" height="128"><defs><style/></defs><path d="M512 128q69.675 0 135.51 21.163t115.498 54.997 93.483 74.837 73.685 82.006 51.67 74.837 32.17 54.827L1024 512q-2.347 4.992-6.315 13.483T998.87 560.17t-31.658 51.669-44.331 59.99-56.832 64.34-69.504 60.16-82.347 51.5-94.848 34.687T512 896q-69.675 0-135.51-21.163t-115.498-54.826-93.483-74.326-73.685-81.493-51.67-74.496-32.17-54.997L0 513.707q2.347-4.992 6.315-13.483t18.816-34.816 31.658-51.84 44.331-60.33 56.832-64.683 69.504-60.331 82.347-51.84 94.848-34.816T512 128.085zm0 85.333q-46.677 0-91.648 12.331t-81.152 31.83-70.656 47.146-59.648 54.485-48.853 57.686-37.675 52.821-26.325 43.99q12.33 21.674 26.325 43.52t37.675 52.351 48.853 57.003 59.648 53.845T339.2 767.02t81.152 31.488T512 810.667t91.648-12.331 81.152-31.659 70.656-46.848 59.648-54.186 48.853-57.344 37.675-52.651T927.957 512q-12.33-21.675-26.325-43.648t-37.675-52.65-48.853-57.345-59.648-54.186-70.656-46.848-81.152-31.659T512 213.334zm0 128q70.656 0 120.661 50.006T682.667 512 632.66 632.661 512 682.667 391.339 632.66 341.333 512t50.006-120.661T512 341.333zm0 85.334q-35.328 0-60.33 25.002T426.666 512t25.002 60.33T512 597.334t60.33-25.002T597.334 512t-25.002-60.33T512 426.666z"/></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -0,0 +1 @@
<svg width="128" height="64" xmlns="http://www.w3.org/2000/svg"><path d="M127.072 7.994c1.37-2.208.914-5.152-.914-6.87-2.056-1.717-4.797-1.226-6.396.982-.229.245-25.586 32.382-55.74 32.382-29.24 0-55.74-32.382-55.968-32.627-1.6-1.963-4.57-2.208-6.397-.49C-.17 3.086-.399 6.275 1.2 8.238c.457.736 5.94 7.36 14.62 14.72L4.17 35.96c-1.828 1.963-1.6 5.152.228 6.87.457.98 1.6 1.471 2.742 1.471s2.284-.49 3.198-1.472l12.564-13.983c5.94 4.416 13.021 8.587 20.788 11.53l-4.797 17.418c-.685 2.699.686 5.397 3.198 6.133h1.37c2.057 0 3.884-1.472 4.341-3.68L52.6 42.83c3.655.736 7.538 1.227 11.422 1.227 3.883 0 7.767-.49 11.422-1.227l4.797 17.173c.457 2.208 2.513 3.68 4.34 3.68.457 0 .914 0 1.143-.246 2.513-.736 3.883-3.434 3.198-6.133l-4.797-17.172c7.767-2.944 14.848-7.114 20.788-11.53l12.336 13.738c.913.981 2.056 1.472 3.198 1.472s2.284-.49 3.198-1.472c1.828-1.963 1.828-4.906.228-6.87l-11.65-13.001c9.366-7.36 14.849-14.474 14.849-14.474z"/></svg>

After

Width:  |  Height:  |  Size: 944 B

View File

@ -0,0 +1 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M84.068 23.784c-1.02 0-1.877-.32-2.572-.96a8.588 8.588 0 0 1-1.738-2.237 11.524 11.524 0 0 1-1.042-2.621c-.232-.895-.348-1.641-.348-2.238V0h.278c.834 0 1.622.085 2.363.256.742.17 1.645.575 2.711 1.214 1.066.64 2.363 1.535 3.892 2.686 1.53 1.15 3.453 2.664 5.77 4.54 2.502 2.045 4.494 3.771 5.977 5.178 1.483 1.406 2.618 2.6 3.406 3.58.787.98 1.274 1.812 1.46 2.494.185.682.277 1.278.277 1.79v2.046H84.068zM127.3 84.01c.278.682.464 1.535.556 2.558.093 1.023-.37 2.003-1.39 2.94-.463.427-.88.832-1.25 1.215-.372.384-.696.704-.974.96a6.69 6.69 0 0 1-.973.767l-11.816-10.741a44.331 44.331 0 0 0 1.877-1.535 31.028 31.028 0 0 1 1.737-1.406c1.112-.938 2.317-1.343 3.615-1.215 1.297.128 2.363.405 3.197.83.927.427 1.923 1.173 2.989 2.239 1.065 1.065 1.876 2.195 2.432 3.388zM78.23 95.902c2.038 0 3.752-.511 5.143-1.534l-26.969 25.83H18.037c-1.761 0-3.684-.47-5.77-1.407a24.549 24.549 0 0 1-5.838-3.709 21.373 21.373 0 0 1-4.518-5.306c-1.204-2.003-1.807-4.07-1.807-6.202V16.495c0-1.79.44-3.665 1.32-5.626A18.41 18.41 0 0 1 5.04 5.562a21.798 21.798 0 0 1 5.213-3.964C12.198.533 14.237 0 16.37 0h53.24v15.984c0 1.62.278 3.367.834 5.242a16.704 16.704 0 0 0 2.572 5.179c1.159 1.577 2.665 2.898 4.518 3.964 1.853 1.066 4.078 1.598 6.673 1.598h20.295v42.325L85.458 92.45c1.02-1.364 1.529-2.856 1.529-4.476 0-2.216-.857-4.113-2.572-5.69-1.714-1.577-3.776-2.366-6.186-2.366H26.1c-2.409 0-4.448.789-6.116 2.366-1.668 1.577-2.502 3.474-2.502 5.69 0 2.217.834 4.092 2.502 5.626 1.668 1.535 3.707 2.302 6.117 2.302h52.13zM26.1 47.951c-2.41 0-4.449.789-6.117 2.366-1.668 1.577-2.502 3.473-2.502 5.69 0 2.216.834 4.092 2.502 5.626 1.668 1.534 3.707 2.302 6.117 2.302h52.13c2.409 0 4.47-.768 6.185-2.302 1.715-1.534 2.572-3.41 2.572-5.626 0-2.217-.857-4.113-2.572-5.69-1.714-1.577-3.776-2.366-6.186-2.366H26.1zm52.407 64.063l1.807-1.663 3.476-3.196a479.75 479.75 0 0 0 4.587-4.284 500.757 500.757 0 0 1 5.004-4.667c3.985-3.666 8.48-7.758 13.485-12.276l11.677 10.741-13.485 12.404-5.004 4.603-4.587 4.22a179.46 179.46 0 0 0-3.267 3.068c-.88.853-1.367 1.322-1.46 1.407-.463.341-.973.703-1.529 1.087-.556.383-1.112.703-1.668.959-.556.256-1.413.575-2.572.959a83.5 83.5 0 0 1-3.545 1.087 72.2 72.2 0 0 1-3.475.895c-1.112.256-1.946.426-2.502.511-1.112.17-1.854.043-2.224-.383-.371-.426-.464-1.151-.278-2.174.092-.511.278-1.279.556-2.302.278-1.023.602-2.067.973-3.132l1.042-3.005c.325-.938.58-1.577.765-1.918a10.157 10.157 0 0 1 2.224-2.941z"/></svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@ -0,0 +1 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M38.47 52L52 38.462l-23.648-23.67L43.209 0H.035L0 43.137l14.757-14.865L38.47 52zm74.773 47.726L89.526 76 76 89.536l23.648 23.672L84.795 128h43.174L128 84.863l-14.757 14.863zM89.538 52l23.668-23.648L128 43.207V.038L84.866 0 99.73 14.76 76 38.472 89.538 52zM38.46 76L14.792 99.651 0 84.794v43.173l43.137.033-14.865-14.757L52 89.53 38.46 76z"/></svg>

After

Width:  |  Height:  |  Size: 421 B

View File

@ -0,0 +1 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M83.287 103.01c-1.57-3.84-6.778-10.414-15.447-19.548-2.327-2.444-2.182-4.306-1.338-9.862v-.64c.553-3.81 1.513-6.05 14.313-8.087 6.516-1.018 8.203 1.57 10.589 5.178l.785 1.193a12.625 12.625 0 0 0 6.43 5.207c1.134.524 2.53 1.164 4.421 2.24 4.596 2.53 4.596 5.41 4.596 11.753v.727a26.91 26.91 0 0 1-5.178 17.454 59.055 59.055 0 0 1-19.025 11.026c3.49-6.546.814-14.313 0-16.553l-.146-.087zM64 5.12a58.502 58.502 0 0 1 25.484 5.818 54.313 54.313 0 0 0-12.859 10.327c-.93 1.28-1.716 2.473-2.472 3.579-2.444 3.694-3.637 5.352-5.818 5.614a25.105 25.105 0 0 1-4.219 0c-4.276-.29-10.094-.64-11.956 4.422-1.193 3.23-1.396 11.956 2.444 16.495.66 1.077.778 2.4.32 3.578a7.01 7.01 0 0 1-2.066 3.229 18.938 18.938 0 0 1-2.909-2.91 18.91 18.91 0 0 0-8.32-6.603c-1.25-.349-2.647-.64-3.985-.93-3.782-.786-8.03-1.688-9.019-3.812a14.895 14.895 0 0 1-.727-5.818 21.935 21.935 0 0 0-1.396-9.25 8.873 8.873 0 0 0-5.557-4.946A58.705 58.705 0 0 1 64 5.12zM0 64c0 35.346 28.654 64 64 64 35.346 0 64-28.654 64-64 0-35.346-28.654-64-64-64C28.654 0 0 28.654 0 64z"/></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M84.742 36.8c2.398 7.2 5.595 12.8 11.19 18.4 4.795-4.8 7.992-11.2 10.39-18.4h-21.58zm-52.748 40h20.78l-10.39-28-10.39 28z"/><path d="M111.916 0H16.009C7.218 0 .025 7.2.025 16v96c0 8.8 7.193 16 15.984 16h95.907c8.791 0 15.984-7.2 15.984-16V16c0-8.8-6.394-16-15.984-16zM72.754 103.2c-1.598 1.6-3.197 1.6-4.795 1.6-.8 0-2.398 0-3.197-.8-.8-.8-1.599 0-1.599-.8s-.799-1.6-1.598-3.2c-.8-1.6-.8-2.4-1.599-4l-3.196-8.8H28.797L25.6 96c-1.598 3.2-2.398 5.6-3.197 7.2-.8 1.6-2.398 1.6-4.795 1.6-1.599 0-3.197-.8-4.796-1.6-1.598-1.6-2.397-2.4-2.397-4 0-.8 0-1.6.799-3.2.8-1.6.8-2.4 1.598-4l17.583-44.8c.8-1.6.8-3.2 1.599-4.8.799-1.6 1.598-3.2 2.397-4 .8-.8 1.599-2.4 3.197-3.2 1.599-.8 3.197-.8 4.796-.8 1.598 0 3.196 0 4.795.8 1.598.8 2.398 1.6 3.197 3.2.799.8 1.598 2.4 2.397 4 .8 1.6 1.599 3.2 2.398 5.6l17.583 44c1.598 3.2 2.398 5.6 2.398 7.2-.8.8-1.599 2.4-2.398 4zM116.711 72c-8.791-3.2-15.185-7.2-20.78-12-5.594 5.6-12.787 9.6-21.579 12l-2.397-4c8.791-2.4 15.984-5.6 21.579-11.2C87.939 51.2 83.144 44 81.545 36h-7.992v-3.2h21.58c-1.6-2.4-3.198-5.6-4.796-8l2.397-.8c1.599 2.4 3.997 5.6 5.595 8.8h19.98v4h-7.992c-2.397 8-6.393 15.2-11.189 20 5.595 4.8 11.988 8.8 20.78 11.2l-3.197 4z"/></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M115.625 127.937H.063V12.375h57.781v12.374H12.438v90.813h90.813V70.156h12.374z"/><path d="M116.426 2.821l8.753 8.753-56.734 56.734-8.753-8.745z"/><path d="M127.893 37.982h-12.375V12.375H88.706V0h39.187z"/></svg>

After

Width:  |  Height:  |  Size: 285 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200" class="icon" p-id="2559" t="1545037285158" version="1.1" viewBox="0 0 1024 1024"><defs><style type="text/css"/></defs><path d="M96 128h832v192H96zM96 416h832v192H96zM96 704h832v192H96z" p-id="2560"/></svg>

After

Width:  |  Height:  |  Size: 313 B

View File

@ -0,0 +1 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M.002 9.2c0 5.044 3.58 9.133 7.998 9.133 4.417 0 7.997-4.089 7.997-9.133 0-5.043-3.58-9.132-7.997-9.132S.002 4.157.002 9.2zM31.997.066h95.981V18.33H31.997V.066zm0 45.669c0 5.044 3.58 9.132 7.998 9.132 4.417 0 7.997-4.088 7.997-9.132 0-3.263-1.524-6.278-3.998-7.91-2.475-1.63-5.524-1.63-7.998 0-2.475 1.632-4 4.647-4 7.91zM63.992 36.6h63.986v18.265H63.992V36.6zm-31.995 82.2c0 5.043 3.58 9.132 7.998 9.132 4.417 0 7.997-4.089 7.997-9.132 0-5.044-3.58-9.133-7.997-9.133s-7.998 4.089-7.998 9.133zm31.995-9.131h63.986v18.265H63.992V109.67zm0-27.404c0 5.044 3.58 9.133 7.998 9.133 4.417 0 7.997-4.089 7.997-9.133 0-3.263-1.524-6.277-3.998-7.909-2.475-1.631-5.524-1.631-7.998 0-2.475 1.632-4 4.646-4 7.91zm31.995-9.13h31.991V91.4H95.987V73.135z"/></svg>

After

Width:  |  Height:  |  Size: 821 B

View File

@ -0,0 +1 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M108.8 44.322H89.6v-5.36c0-9.04-3.308-24.163-25.6-24.163-23.145 0-25.6 16.881-25.6 24.162v5.361H19.2v-5.36C19.2 15.281 36.798 0 64 0c27.202 0 44.8 15.281 44.8 38.961v5.361zm-32 39.356c0-5.44-5.763-9.832-12.8-9.832-7.037 0-12.8 4.392-12.8 9.832 0 3.682 2.567 6.808 6.407 8.477v11.205c0 2.718 2.875 4.962 6.4 4.962 3.524 0 6.4-2.244 6.4-4.962V92.155c3.833-1.669 6.393-4.795 6.393-8.477zM128 64v49.201c0 8.158-8.645 14.799-19.2 14.799H19.2C8.651 128 0 121.359 0 113.201V64c0-8.153 8.645-14.799 19.2-14.799h89.6c10.555 0 19.2 6.646 19.2 14.799z"/></svg>

After

Width:  |  Height:  |  Size: 623 B

View File

@ -0,0 +1 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M104.185 95.254c8.161 7.574 13.145 17.441 13.145 28.28 0 1.508-.098 2.998-.285 4.466h-10.784c.238-1.465.403-2.948.403-4.465 0-8.983-4.36-17.115-11.419-23.216C86 104.66 75.355 107.162 64 107.162c-11.344 0-21.98-2.495-31.22-6.83-7.064 6.099-11.444 14.218-11.444 23.203 0 1.517.165 3 .403 4.465H10.955a35.444 35.444 0 0 1-.285-4.465c0-10.838 4.974-20.713 13.127-28.291C9.294 85.42.003 70.417.003 53.58.003 23.99 28.656.001 64 .001s63.997 23.988 63.997 53.58c0 16.842-9.299 31.85-23.812 41.673zM64 36.867c-29.454 0-53.33-10.077-53.33 15.342 0 25.418 23.876 46.023 53.33 46.023 29.454 0 53.33-20.605 53.33-46.023 0-25.419-23.876-15.342-53.33-15.342zm24.888 25.644c-3.927 0-7.111-2.665-7.111-5.953 0-3.288 3.184-5.954 7.11-5.954 3.928 0 7.111 2.666 7.111 5.954s-3.183 5.953-7.11 5.953zm-3.556 16.372c0 4.11-9.55 7.442-21.332 7.442-11.781 0-21.332-3.332-21.332-7.442 0-1.06.656-2.064 1.8-2.976 3.295 2.626 10.79 4.465 19.532 4.465 8.743 0 16.237-1.84 19.531-4.465 1.145.912 1.801 1.916 1.801 2.976zm-46.22-16.372c-3.927 0-7.11-2.665-7.11-5.953 0-3.288 3.183-5.954 7.11-5.954 3.927 0 7.111 2.666 7.111 5.954s-3.184 5.953-7.11 5.953z"/></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><g><path d="M95.648 118.762c0 5.035-3.563 9.121-7.979 9.121H7.98c-4.416 0-7.979-4.086-7.979-9.121C0 100.519 15.408 83.47 31.152 76.75c-9.099-6.43-15.216-17.863-15.216-30.987v-9.128c0-20.16 14.293-36.518 31.893-36.518s31.894 16.358 31.894 36.518v9.122c0 13.137-6.123 24.556-15.216 30.993 15.738 6.726 31.141 23.769 31.141 42.012z"/><path d="M106.032 118.252h15.867c3.376 0 6.101-3.125 6.101-6.972 0-13.957-11.787-26.984-23.819-32.123 6.955-4.919 11.638-13.66 11.638-23.704v-6.985c0-15.416-10.928-27.926-24.39-27.926-1.674 0-3.306.193-4.89.561 1.936 4.713 3.018 9.974 3.018 15.526v9.121c0 13.137-3.056 23.111-11.066 30.993 14.842 4.41 27.312 23.42 27.541 41.509z"/></g></svg>

After

Width:  |  Height:  |  Size: 738 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200" class="icon" p-id="2866" t="1543477186503" version="1.1" viewBox="0 0 1024 1024"><defs><style type="text/css">@font-face{font-family:rbicon;src:url(chrome-extension://dipiagiiohfljcicegpgffpbnjmgjcnf/fonts/rbicon.woff2) format(&quot;woff2&quot;);font-weight:400;font-style:normal}</style></defs><path d="M609.093716 908.277839c0-106.125689 85.803749-191.929438 191.929438-191.929438 38.950386 0 75.07828 11.854465 105.561191 31.611907l0-3.38699c0-60.965821-71.69129-136.044101-128.705623-145.076075l76.771775-127.012128-178.945976 0c32.176406-46.288864 53.627343-107.254686 53.627343-174.994487 0-5.080485 0-10.725469 0-15.805954 69.433297-12.418964 114.593164-32.176406 114.593164-51.933848 0-21.450937-50.240353-41.208379-127.012128-53.627343C712.961411 158.059537 661.592062 11.289967 600.62624 11.289967c-27.095921 0-48.546858 14.112459-62.659316 35.563396 0 10.725469-10.725469 21.450937-24.837927 21.450937-12.418964 0-23.144432-9.031974-24.837927-19.757442C475.307607 27.660419 452.163175 11.289967 425.631753 11.289967 366.359427 11.289967 311.038589 145.640573 298.619625 179.510474 232.573319 191.929438 189.671444 209.993385 189.671444 231.444322c0 21.450937 44.595369 39.514884 114.593164 51.933848 0 5.080485 0 10.725469 0 15.805954 0 67.739802 19.757442 128.705623 53.627343 174.994487L178.945976 474.178611l76.771775 125.318633c-57.014333 0-132.092613 78.46527-132.092613 146.76957l1.693495 205.477398c0 33.869901 26.531422 60.965821 60.965821 60.965821l453.85667 0C620.383682 982.227122 609.093716 946.663727 609.093716 908.277839zM468.533627 962.46968l-28.789416 0-53.627343-395.148842 112.335171 98.222712L468.533627 962.46968zM498.45204 351.117971c-7.338479 62.659316-39.514884 76.771775-57.014333 76.771775-42.901874 0-55.320838-57.014333-57.014333-76.771775l-14.112459 0L370.310915 338.699008l0-12.418964 107.254686 0L477.565601 338.699008l71.69129 0 0-12.418964 107.254686 0L656.511577 338.699008l3.38699 0 0 12.418964-14.112459 0c0 0-9.031974 76.771775-57.014333 76.771775C539.660419 427.889746 530.628445 351.117971 530.628445 351.117971L498.45204 351.117971zM562.804851 962.46968l-30.482911-296.92613 112.335171-99.916207-53.627343 396.842337L562.804851 962.46968z" p-id="2867"/><path d="M918.438809 907.148842c0 1.693495-1.128997 2.822492-2.822492 2.822492l-97.658214 0 0 97.658214c0 1.693495-1.128997 2.822492-2.822492 2.822492l-43.466373 0c-1.693495 0-2.822492-1.128997-2.822492-2.822492l0-97.658214-97.658214 0c-1.693495 0-2.822492-1.128997-2.822492-2.822492L668.366042 863.68247c0-1.693495 1.128997-2.822492 2.822492-2.822492l97.658214 0 0-97.658214c0-1.693495 1.128997-2.822492 2.822492-2.822492l43.466373 0c1.693495 0 2.822492 1.128997 2.822492 2.822492l0 97.658214 97.658214 0c1.693495 0 2.822492 1.128997 2.822492 2.822492L918.438809 907.148842z" p-id="2868"/></svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@ -0,0 +1 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M0 54.857h54.796v18.286H36.531V128H18.265V73.143H0V54.857zm127.857-36.571H91.935V128H72.456V18.286H36.534V0h91.326l-.003 18.286z"/></svg>

After

Width:  |  Height:  |  Size: 211 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="1574576079728" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6611" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M981.917808 277.742466H813.589041V122.038356c0-19.638356-15.430137-35.068493-35.068493-35.068493H252.493151c-19.638356 0-35.068493 15.430137-35.068493 35.068493v155.70411H42.082192c-19.638356 0-35.068493 15.430137-35.068493 35.068493V911.780822c0 19.638356 15.430137 35.068493 35.068493 35.068493h939.835616c19.638356 0 35.068493-15.430137 35.068493-35.068493V312.810959c0-19.638356-15.430137-35.068493-35.068493-35.068493z m-694.356164-120.635617H743.452055v120.635617H287.561644v-120.635617zM252.493151 347.879452h694.356164v133.260274h-74.345205v-50.49863c0-19.638356-15.430137-35.068493-35.068494-35.068493h-117.830137c-19.638356 0-35.068493 15.430137-35.068493 35.068493v50.49863h-336.657534v-50.49863c0-19.638356-15.430137-35.068493-35.068493-35.068493h-117.830137c-19.638356 0-35.068493 15.430137-35.068493 35.068493v50.49863H77.150685v-133.260274H252.493151z m549.873972 119.232877v140.273972h-47.69315v-140.273972h47.69315z m-526.027397 0v140.273972h-47.693151v-140.273972h47.693151zM77.150685 876.712329V551.276712h81.358904v89.775343c0 19.638356 15.430137 35.068493 35.068493 35.068493h117.830137c19.638356 0 35.068493-15.430137 35.068493-35.068493v-89.775343h336.657535v89.775343c0 19.638356 15.430137 35.068493 35.068493 35.068493h117.830137c19.638356 0 35.068493-15.430137 35.068493-35.068493v-89.775343h74.345205v324.032877H77.150685z" p-id="6612" fill="#bfbfbf"></path></svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="211.719" height="200" class="icon" p-id="10233" t="1543827724451" version="1.1" viewBox="0 0 1084 1024"><defs><style type="text/css">@font-face{font-family:rbicon;src:url(chrome-extension://dipiagiiohfljcicegpgffpbnjmgjcnf/fonts/rbicon.woff2) format(&quot;woff2&quot;);font-weight:400;font-style:normal}</style></defs><path fill="#bfbfbf" d="M1080.09609 434.500756c-4.216302-23.731757-26.9241-47.945376-50.595623-53.185637l-17.648235-4.095836a175.940257 175.940257 0 0 1-101.612877-80.832531 177.807476 177.807476 0 0 1-18.732427-129.801867l5.541425-16.684509c7.10748-23.129428-2.108151-54.992624-20.599646-70.833873 0 0-16.624276-14.094495-63.244529-41.199293-46.800951-26.984332-66.858502-34.513443-66.858502-34.513443-22.76803-8.372371-54.631227-0.361397-71.255503 17.407304l-12.287509 13.251234a173.470708 173.470708 0 0 1-120.465769 48.065842A174.13327 174.13327 0 0 1 421.329029 33.590675L409.583617 20.761071C393.140039 2.99237 361.096144-4.898138 338.267881 3.353767c0 0-20.358715 7.529111-67.099434 34.513443-46.800951 27.34573-63.244529 41.440225-63.244529 41.440225-18.431263 15.66055-27.646894 47.222582-20.539413 70.592941l5.059562 16.865207a178.048407 178.048407 0 0 1-18.672194 129.621169 174.916297 174.916297 0 0 1-102.275439 81.073463l-17.045906 3.854904c-23.310126 5.42096-46.258856 29.333415-50.595623 53.185637 0 0-3.854905 21.382674-3.854905 75.712737 0 54.330062 3.854905 75.712736 3.854905 75.712736 4.216302 23.972688 26.9241 47.945376 50.595623 53.185637l16.624276 3.854905a174.253736 174.253736 0 0 1 102.395904 81.314394c23.310126 40.837896 28.911785 87.337683 18.732427 129.801867l-4.81863 16.443578c-7.10748 23.129428 2.108151 54.992624 20.599646 70.833872 0 0 16.624276 14.094495 63.244529 41.199293 46.800951 27.104798 66.918735 34.513443 66.918735 34.513443 22.707798 8.372371 54.631227 0.361397 71.255503-17.407303l11.624947-12.588673a175.096996 175.096996 0 0 1 242.256662 0.120465l11.624947 12.648906c16.383345 17.708468 48.427239 25.598976 71.255503 17.347071 0 0 20.358715-7.529111 67.159666-34.513443 46.740719-27.104798 63.124063-41.199293 63.124064-41.199293 18.491496-15.600317 27.707127-47.463513 20.599646-70.833873l-5.059562-17.106139a176.723284 176.723284 0 0 1 18.672194-129.139305 176.060722 176.060722 0 0 1 102.395904-81.314394l16.68451-3.854905c23.310126-5.42096 46.258856-29.333415 50.595623-53.185637 0 0 3.854905-21.382674 3.854904-75.712737-0.240932-54.330062-4.095836-75.833202-4.095836-75.833202z m-537.819428 293.334149c-119.261112 0-216.175824-97.336342-216.175824-217.621412a216.657687 216.657687 0 0 1 216.236057-217.320249c119.200879 0 216.115591 97.276109 216.11559 217.56118-0.240932 120.044139-96.974945 217.320248-216.175823 217.320249z" p-id="10234"/></svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@ -0,0 +1 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M.006.064h127.988v31.104H.006V.064zm0 38.016h38.396v41.472H.006V38.08zm0 48.384h38.396v41.472H.006V86.464zM44.802 38.08h38.396v41.472H44.802V38.08zm0 48.384h38.396v41.472H44.802V86.464zM89.598 38.08h38.396v41.472H89.598zm0 48.384h38.396v41.472H89.598z"/><path d="M.006.064h127.988v31.104H.006V.064zm0 38.016h38.396v41.472H.006V38.08zm0 48.384h38.396v41.472H.006V86.464zM44.802 38.08h38.396v41.472H44.802V38.08zm0 48.384h38.396v41.472H44.802V86.464zM89.598 38.08h38.396v41.472H89.598zm0 48.384h38.396v41.472H89.598z"/></svg>

After

Width:  |  Height:  |  Size: 597 B

View File

@ -0,0 +1 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M126.713 90.023c.858.985 1.287 2.134 1.287 3.447v29.553c0 1.423-.429 2.6-1.287 3.53-.858.93-1.907 1.395-3.146 1.395H97.824c-1.145 0-2.146-.465-3.004-1.395-.858-.93-1.287-2.107-1.287-3.53V93.47c0-.875.19-1.696.572-2.462.382-.766.906-1.368 1.573-1.806a3.84 3.84 0 0 1 2.146-.657h9.725V69.007a3.84 3.84 0 0 0-.43-1.806 3.569 3.569 0 0 0-1.143-1.313 2.714 2.714 0 0 0-1.573-.492h-36.47v23.149h9.725c1.144 0 2.145.492 3.004 1.478.858.985 1.287 2.134 1.287 3.447v29.553c0 .876-.191 1.696-.573 2.463-.38.766-.905 1.368-1.573 1.806a3.84 3.84 0 0 1-2.145.656H51.915a3.84 3.84 0 0 1-2.145-.656c-.668-.438-1.216-1.04-1.645-1.806a4.96 4.96 0 0 1-.644-2.463V93.47c0-1.313.43-2.462 1.288-3.447.858-.986 1.907-1.478 3.146-1.478h9.582v-23.15h-37.9c-.953 0-1.74.356-2.359 1.068-.62.711-.93 1.56-.93 2.544v19.538h9.726c1.239 0 2.264.492 3.074 1.478.81.985 1.216 2.134 1.216 3.447v29.553c0 1.423-.405 2.6-1.216 3.53-.81.93-1.835 1.395-3.074 1.395H4.29c-.476 0-.93-.082-1.358-.246a4.1 4.1 0 0 1-1.144-.657 4.658 4.658 0 0 1-.93-1.067 5.186 5.186 0 0 1-.643-1.395 5.566 5.566 0 0 1-.215-1.56V93.47c0-.437.048-.875.143-1.313a3.95 3.95 0 0 1 .429-1.15c.19-.328.429-.656.715-.984.286-.329.572-.602.858-.821.286-.22.62-.383 1.001-.493.382-.11.763-.164 1.144-.164h9.726V61.619c0-.985.31-1.833.93-2.544.619-.712 1.358-1.068 2.216-1.068h44.335V39.62h-9.582c-1.24 0-2.288-.492-3.146-1.477a5.09 5.09 0 0 1-1.287-3.448V5.14c0-1.423.429-2.627 1.287-3.612.858-.985 1.907-1.477 3.146-1.477h25.743c.763 0 1.478.246 2.145.739a5.17 5.17 0 0 1 1.573 1.888c.382.766.573 1.587.573 2.462v29.553c0 1.313-.43 2.463-1.287 3.448-.859.985-1.86 1.477-3.004 1.477h-9.725v18.389h42.762c.954 0 1.74.355 2.36 1.067.62.711.93 1.56.93 2.545v26.925h9.582c1.239 0 2.288.492 3.146 1.478z"/></svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -0,0 +1 @@
<svg width="130" height="130" xmlns="http://www.w3.org/2000/svg"><path d="M63.444 64.996c20.633 0 37.359-14.308 37.359-31.953 0-17.649-16.726-31.952-37.359-31.952-20.631 0-37.36 14.303-37.358 31.952 0 17.645 16.727 31.953 37.359 31.953zM80.57 75.65H49.434c-26.652 0-48.26 18.477-48.26 41.27v2.664c0 9.316 21.608 9.325 48.26 9.325H80.57c26.649 0 48.256-.344 48.256-9.325v-2.663c0-22.794-21.605-41.271-48.256-41.271z" stroke="#979797"/></svg>

After

Width:  |  Height:  |  Size: 440 B

View File

@ -0,0 +1,22 @@
# replace default config
# multipass: true
# full: true
plugins:
# - name
#
# or:
# - name: false
# - name: true
#
# or:
# - name:
# param1: 1
# param2: 2
- removeAttrs:
attrs:
- 'fill'
- 'fill-rule'

181
frontend/src/lang/en.js Normal file
View File

@ -0,0 +1,181 @@
export default {
route: {
dashboard: 'Dashboard',
documentation: 'Documentation',
guide: 'Guide',
permission: 'Permission',
pagePermission: 'Page Permission',
rolePermission: 'Role Permission',
directivePermission: 'Directive Permission',
icons: 'Icons',
components: 'Components',
tinymce: 'Tinymce',
markdown: 'Markdown',
jsonEditor: 'JSON Editor',
dndList: 'Dnd List',
splitPane: 'SplitPane',
avatarUpload: 'Avatar Upload',
dropzone: 'Dropzone',
sticky: 'Sticky',
countTo: 'Count To',
componentMixin: 'Mixin',
backToTop: 'Back To Top',
dragDialog: 'Drag Dialog',
dragSelect: 'Drag Select',
dragKanban: 'Drag Kanban',
charts: 'Charts',
keyboardChart: 'Keyboard Chart',
lineChart: 'Line Chart',
mixChart: 'Mix Chart',
example: 'Example',
nested: 'Nested Routes',
menu1: 'Menu 1',
'menu1-1': 'Menu 1-1',
'menu1-2': 'Menu 1-2',
'menu1-2-1': 'Menu 1-2-1',
'menu1-2-2': 'Menu 1-2-2',
'menu1-3': 'Menu 1-3',
menu2: 'Menu 2',
Table: 'Table',
dynamicTable: 'Dynamic Table',
dragTable: 'Drag Table',
inlineEditTable: 'Inline Edit',
complexTable: 'Complex Table',
tab: 'Tab',
form: 'Form',
createArticle: 'Create Article',
editArticle: 'Edit Article',
articleList: 'Article List',
errorPages: 'Error Pages',
page401: '401',
page404: '404',
errorLog: 'Error Log',
excel: 'Excel',
exportExcel: 'Export Excel',
selectExcel: 'Export Selected',
mergeHeader: 'Merge Header',
uploadExcel: 'Upload Excel',
zip: 'Zip',
pdf: 'PDF',
exportZip: 'Export Zip',
theme: 'Theme',
clipboardDemo: 'Clipboard',
i18n: 'I18n',
externalLink: 'External Link',
profile: 'Profile'
},
navbar: {
dashboard: 'Dashboard',
github: 'Github',
logOut: 'Log Out',
profile: 'Profile',
theme: 'Theme',
size: 'Global Size'
},
login: {
title: 'Login Form',
welcome: 'welcome To DataEase',
logIn: 'Login',
username: 'Username',
password: 'Password',
any: 'any',
thirdparty: 'Or connect with',
thirdpartyTips: 'Can not be simulated on local, so please combine you own business simulation! ! !'
},
commons: {
button: {
login: 'Login'
}
},
documentation: {
documentation: 'Documentation',
github: 'Github Repository'
},
permission: {
addRole: 'New Role',
editPermission: 'Edit',
roles: 'Your roles',
switchRoles: 'Switch roles',
tips: 'In some cases, using v-permission will have no effect. For example: Element-UI el-tab or el-table-column and other scenes that dynamically render dom. You can only do this with v-if.',
delete: 'Delete',
confirm: 'Confirm',
cancel: 'Cancel'
},
guide: {
description: 'The guide page is useful for some people who entered the project for the first time. You can briefly introduce the features of the project. Demo is based on ',
button: 'Show Guide'
},
components: {
documentation: 'Documentation',
tinymceTips: 'Rich text is a core feature of the management backend, but at the same time it is a place with lots of pits. In the process of selecting rich texts, I also took a lot of detours. The common rich texts on the market have been basically used, and I finally chose Tinymce. See the more detailed rich text comparison and introduction.',
dropzoneTips: 'Because my business has special needs, and has to upload images to qiniu, so instead of a third party, I chose encapsulate it by myself. It is very simple, you can see the detail code in @/components/Dropzone.',
stickyTips: 'when the page is scrolled to the preset position will be sticky on the top.',
backToTopTips1: 'When the page is scrolled to the specified position, the Back to Top button appears in the lower right corner',
backToTopTips2: 'You can customize the style of the button, show / hide, height of appearance, height of the return. If you need a text prompt, you can use element-ui el-tooltip elements externally',
imageUploadTips: 'Since I was using only the vue@1 version, and it is not compatible with mockjs at the moment, I modified it myself, and if you are going to use it, it is better to use official version.'
},
table: {
dynamicTips1: 'Fixed header, sorted by header order',
dynamicTips2: 'Not fixed header, sorted by click order',
dragTips1: 'The default order',
dragTips2: 'The after dragging order',
title: 'Title',
importance: 'Imp',
type: 'Type',
remark: 'Remark',
search: 'Search',
add: 'Add',
export: 'Export',
reviewer: 'reviewer',
id: 'ID',
date: 'Date',
author: 'Author',
readings: 'Readings',
status: 'Status',
actions: 'Actions',
edit: 'Edit',
publish: 'Publish',
draft: 'Draft',
delete: 'Delete',
cancel: 'Cancel',
confirm: 'Confirm'
},
example: {
warning: 'Creating and editing pages cannot be cached by keep-alive because keep-alive include does not currently support caching based on routes, so it is currently cached based on component name. If you want to achieve a similar caching effect, you can use a browser caching scheme such as localStorage. Or do not use keep-alive include to cache all pages directly. See details'
},
errorLog: {
tips: 'Please click the bug icon in the upper right corner',
description: 'Now the management system are basically the form of the spa, it enhances the user experience, but it also increases the possibility of page problems, a small negligence may lead to the entire page deadlock. Fortunately Vue provides a way to catch handling exceptions, where you can handle errors or report exceptions.',
documentation: 'Document introduction'
},
excel: {
export: 'Export',
selectedExport: 'Export Selected Items',
placeholder: 'Please enter the file name (default excel-list)'
},
zip: {
export: 'Export',
placeholder: 'Please enter the file name (default file)'
},
pdf: {
tips: 'Here we use window.print() to implement the feature of downloading PDF.'
},
theme: {
change: 'Change Theme',
documentation: 'Theme documentation',
tips: 'Tips: It is different from the theme-pick on the navbar is two different skinning methods, each with different application scenarios. Refer to the documentation for details.'
},
tagsView: {
refresh: 'Refresh',
close: 'Close',
closeOthers: 'Close Others',
closeAll: 'Close All'
},
settings: {
title: 'Page style setting',
theme: 'Theme Color',
tagsView: 'Open Tags-View',
fixedHeader: 'Fixed Header',
sidebarLogo: 'Sidebar Logo'
}
}

175
frontend/src/lang/es.js Executable file
View File

@ -0,0 +1,175 @@
export default {
route: {
dashboard: 'Panel de control',
documentation: 'Documentación',
guide: 'Guía',
permission: 'Permisos',
rolePermission: 'Permisos de rol',
pagePermission: 'Permisos de la página',
directivePermission: 'Permisos de la directiva',
icons: 'Iconos',
components: 'Componentes',
tinymce: 'Tinymce',
markdown: 'Markdown',
jsonEditor: 'Editor JSON',
dndList: 'Lista Dnd',
splitPane: 'Panel dividido',
avatarUpload: 'Subir avatar',
dropzone: 'Subir ficheros',
sticky: 'Sticky',
countTo: 'CountTo',
componentMixin: 'Mixin',
backToTop: 'Ir arriba',
dragDialog: 'Drag Dialog',
dragSelect: 'Drag Select',
dragKanban: 'Drag Kanban',
charts: 'Gráficos',
keyboardChart: 'Keyboard Chart',
lineChart: 'Gráfico de líneas',
mixChart: 'Mix Chart',
example: 'Ejemplo',
nested: 'Rutas anidadass',
menu1: 'Menu 1',
'menu1-1': 'Menu 1-1',
'menu1-2': 'Menu 1-2',
'menu1-2-1': 'Menu 1-2-1',
'menu1-2-2': 'Menu 1-2-2',
'menu1-3': 'Menu 1-3',
menu2: 'Menu 2',
Table: 'Tabla',
dynamicTable: 'Tabla dinámica',
dragTable: 'Arrastrar tabla',
inlineEditTable: 'Editor',
complexTable: 'Complex Table',
tab: 'Pestaña',
form: 'Formulario',
createArticle: 'Crear artículo',
editArticle: 'Editar artículo',
articleList: 'Listado de artículos',
errorPages: 'Páginas de error',
page401: '401',
page404: '404',
errorLog: 'Registro de errores',
excel: 'Excel',
exportExcel: 'Exportar a Excel',
selectExcel: 'Export seleccionado',
mergeHeader: 'Merge Header',
uploadExcel: 'Subir Excel',
zip: 'Zip',
pdf: 'PDF',
exportZip: 'Exportar a Zip',
theme: 'Tema',
clipboardDemo: 'Clipboard',
i18n: 'I18n',
externalLink: 'Enlace externo',
profile: 'Profile'
},
navbar: {
logOut: 'Salir',
dashboard: 'Panel de control',
github: 'Github',
theme: 'Tema',
size: 'Tamaño global',
profile: 'Profile'
},
login: {
title: 'Formulario de acceso',
logIn: 'Acceso',
username: 'Usuario',
password: 'Contraseña',
any: 'nada',
thirdparty: 'Conectar con',
thirdpartyTips: 'No se puede simular en local, así que combine su propia simulación de negocios. ! !'
},
documentation: {
documentation: 'Documentación',
github: 'Repositorio Github'
},
permission: {
addRole: 'Nuevo rol',
editPermission: 'Permiso de edición',
roles: 'Tus permisos',
switchRoles: 'Cambiar permisos',
tips: 'In some cases it is not suitable to use v-permission, such as element Tab component or el-table-column and other asynchronous rendering dom cases which can only be achieved by manually setting the v-if.',
delete: 'Borrar',
confirm: 'Confirmar',
cancel: 'Cancelar'
},
guide: {
description: 'The guide page is useful for some people who entered the project for the first time. You can briefly introduce the features of the project. Demo is based on ',
button: 'Ver guía'
},
components: {
documentation: 'Documentación',
tinymceTips: 'Rich text editor is a core part of management system, but at the same time is a place with lots of problems. In the process of selecting rich texts, I also walked a lot of detours. The common rich text editors in the market are basically used, and the finally chose Tinymce. See documentation for more detailed rich text editor comparisons and introductions.',
dropzoneTips: 'Because my business has special needs, and has to upload images to qiniu, so instead of a third party, I chose encapsulate it by myself. It is very simple, you can see the detail code in @/components/Dropzone.',
stickyTips: 'when the page is scrolled to the preset position will be sticky on the top.',
backToTopTips1: 'When the page is scrolled to the specified position, the Back to Top button appears in the lower right corner',
backToTopTips2: 'You can customize the style of the button, show / hide, height of appearance, height of the return. If you need a text prompt, you can use element-ui el-tooltip elements externally',
imageUploadTips: 'Since I was using only the vue@1 version, and it is not compatible with mockjs at the moment, I modified it myself, and if you are going to use it, it is better to use official version.'
},
table: {
dynamicTips1: 'Fixed header, sorted by header order',
dynamicTips2: 'Not fixed header, sorted by click order',
dragTips1: 'Orden por defecto',
dragTips2: 'The after dragging order',
title: 'Título',
importance: 'Importancia',
type: 'Tipo',
remark: 'Remark',
search: 'Buscar',
add: 'Añadir',
export: 'Exportar',
reviewer: 'reviewer',
id: 'ID',
date: 'Fecha',
author: 'Autor',
readings: 'Lector',
status: 'Estado',
actions: 'Acciones',
edit: 'Editar',
publish: 'Publicar',
draft: 'Draft',
delete: 'Eliminar',
cancel: 'Cancelar',
confirm: 'Confirmar'
},
example: {
warning: 'Creating and editing pages cannot be cached by keep-alive because keep-alive include does not currently support caching based on routes, so it is currently cached based on component name. If you want to achieve a similar caching effect, you can use a browser caching scheme such as localStorage. Or do not use keep-alive include to cache all pages directly. See details'
},
errorLog: {
tips: 'Please click the bug icon in the upper right corner',
description: 'Now the management system are basically the form of the spa, it enhances the user experience, but it also increases the possibility of page problems, a small negligence may lead to the entire page deadlock. Fortunately Vue provides a way to catch handling exceptions, where you can handle errors or report exceptions.',
documentation: 'Documento de introducción'
},
excel: {
export: 'Exportar',
selectedExport: 'Exportar seleccionados',
placeholder: 'Por favor escribe un nombre de fichero'
},
zip: {
export: 'Exportar',
placeholder: 'Por favor escribe un nombre de fichero'
},
pdf: {
tips: 'Here we use window.print() to implement the feature of downloading pdf.'
},
theme: {
change: 'Cambiar tema',
documentation: 'Documentación del tema',
tips: 'Tips: It is different from the theme-pick on the navbar is two different skinning methods, each with different application scenarios. Refer to the documentation for details.'
},
tagsView: {
refresh: 'Actualizar',
close: 'Cerrar',
closeOthers: 'Cerrar otros',
closeAll: 'Cerrar todos'
},
settings: {
title: 'Page style setting',
theme: 'Theme Color',
tagsView: 'Open Tags-View',
fixedHeader: 'Fixed Header',
sidebarLogo: 'Sidebar Logo'
}
}

View File

@ -0,0 +1,61 @@
import Vue from 'vue'
import VueI18n from 'vue-i18n'
import Cookies from 'js-cookie'
import elementEnLocale from 'element-ui/lib/locale/lang/en' // element-ui lang
import elementZhLocale from 'element-ui/lib/locale/lang/zh-CN'// element-ui lang
import elementTWLocale from 'element-ui/lib/locale/lang/zh-TW'// element-ui lang
import elementEsLocale from 'element-ui/lib/locale/lang/es'// element-ui lang
import elementJaLocale from 'element-ui/lib/locale/lang/ja'// element-ui lang
import enLocale from './en'
import zhLocale from './zh'
import twLocale from './tw'
import esLocale from './es'
import jaLocale from './ja'
Vue.use(VueI18n)
const messages = {
en: {
...enLocale,
...elementEnLocale
},
zh: {
...zhLocale,
...elementZhLocale
},
tw: {
...twLocale,
...elementTWLocale
},
es: {
...esLocale,
...elementEsLocale
},
ja: {
...jaLocale,
...elementJaLocale
}
}
export function getLanguage() {
const chooseLanguage = Cookies.get('language')
if (chooseLanguage) return chooseLanguage
// if has not choose language
const language = (navigator.language || navigator.browserLanguage).toLowerCase()
const locales = Object.keys(messages)
for (const locale of locales) {
if (language.indexOf(locale) > -1) {
return locale
}
}
return 'zh'
}
const i18n = new VueI18n({
// set locale
// options: en | zh | es
locale: getLanguage(),
// set locale messages
messages
})
export default i18n

175
frontend/src/lang/ja.js Normal file
View File

@ -0,0 +1,175 @@
export default {
route: {
dashboard: 'トップ',
documentation: 'ドキュメント',
guide: 'ガイド',
permission: '権限',
rolePermission: '権限ロール',
pagePermission: 'ページ権限',
directivePermission: 'ディレクティブ権限',
icons: 'アイコン',
components: 'コンポーネント',
tinymce: 'TinyMCE',
markdown: 'Markdown',
jsonEditor: 'JSON Editor',
dndList: 'Drag-And-Drop',
splitPane: 'パネル',
avatarUpload: 'アバターアップロード',
dropzone: 'Dropzone',
sticky: 'Sticky',
countTo: 'Count To',
componentMixin: 'コンポーネントMixin',
backToTop: 'BackToTop',
dragDialog: 'Drag Dialog',
dragSelect: 'Drag Select',
dragKanban: 'Drag 看板',
charts: 'チャート',
keyboardChart: 'Keyboardチャート',
lineChart: 'Lineチャート',
mixChart: 'Mixチャート',
example: 'Example',
nested: 'Nested Routes',
menu1: 'メニュー1',
'menu1-1': 'メニュー 1-1',
'menu1-2': 'メニュー 1-2',
'menu1-2-1': 'メニュー 1-2-1',
'menu1-2-2': 'メニュー 1-2-2',
'menu1-3': 'メニュー 1-3',
menu2: 'メニュー 2',
Table: 'Table',
dynamicTable: '可変 Table',
dragTable: 'Drag Table',
inlineEditTable: 'Inline Edit Table',
complexTable: 'Complex Table',
tab: 'Tab',
form: 'フォーム',
createArticle: '投稿作成',
editArticle: '投稿編集',
articleList: '投稿リスト',
errorPages: 'エラーページ',
page401: '401',
page404: '404',
errorLog: 'エラーログ',
excel: 'Excel',
exportExcel: '一括エクスポート',
selectExcel: '複数選択エクスポート',
mergeHeader: 'ヘッダーマージ',
uploadExcel: 'アップロード',
zip: 'Zip',
pdf: 'PDF',
exportZip: 'Export Zip',
theme: 'テーマ変更',
clipboardDemo: 'Clipboard',
i18n: '多言語',
externalLink: '外部リンク',
profile: 'プロフィール'
},
navbar: {
dashboard: 'トップ',
github: 'GitHub',
logOut: 'ログアウト',
profile: 'プロフィール',
theme: 'テーマ変更',
size: '画面サイズ'
},
login: {
title: 'ユーザログイン',
logIn: 'ログイン',
username: 'ユーザ名',
password: 'パスワード',
any: 'password',
thirdparty: '外部IDでログイン',
thirdpartyTips: 'ローカル環境ではログインできません。実装が必要です。'
},
documentation: {
documentation: 'ドキュメント',
github: 'Github Link'
},
permission: {
addRole: 'ロール追加',
editPermission: 'ロール変更',
roles: 'ロール',
switchRoles: 'ロール切替',
tips: 'v-permissionは使えない時があります。例えば: Element-UI の el-tab、 el-table-column 及び他の dom。v-ifを使う必要があります。',
delete: '削除',
confirm: '確認',
cancel: 'キャンセル'
},
guide: {
description: 'ガイドは各機能の説明です。',
button: 'ガイドを見る'
},
components: {
documentation: 'ドキュメント',
tinymceTips: 'tinymceは管理画面に重要な機能ですが、その同時に落とし穴がありあす。tinymceを使う道のりが大変でした。Tinymceを使う時に各自のプロジェクト状況で判断が必要です。ドキュメントはこちら',
dropzoneTips: 'Third partyのパッケージを使わず、独自の実装しています。詳細は @/components/Dropzone',
stickyTips: 'ページの指定位置へスクロールした場合、表示されます。',
backToTopTips1: 'トップへスクロールが表示されます。',
backToTopTips2: 'ボタンのスタイルはカスタマイズできます。例えば、show/hide、height、position。 またはElementのel-tooltipを使って、ツールチップを実装できます。',
imageUploadTips: 'mockjsは使えないため、カスタマイズしています。公式の最新バージョンを使ってください。'
},
table: {
dynamicTips1: '先頭は固定、最後に追加',
dynamicTips2: '戦後に追加せず、指定列に追加',
dragTips1: 'デフォルト順番',
dragTips2: 'Drag後の順番',
title: 'タイトル',
importance: '重要',
type: 'タイプ',
remark: '評価',
search: '検索',
add: '追加',
export: 'エクスポート',
reviewer: 'レビュアー',
id: '番号',
date: '日時',
author: '作成者',
readings: '閲覧数',
status: 'ステータス',
actions: '操作',
edit: '編集',
publish: '公開',
draft: '下書き',
delete: 'キャンセル',
cancel: 'キャンセル',
confirm: '確認'
},
example: {
warning: '新規作成と編集画面は keep-alive を使えないです。keep-alive の include はrouteのキャッシュは使えないです。そのため、component name を使ってキャッシュさせるようにします。このようなキャッシュ機能を作りたい場合localStorageを使う手があります。もしくは keep-alive の includeを使って、全ページキャッシュする方法はあります。'
},
errorLog: {
tips: '右上のbugアイコンをクリックしてください。',
description: '管理画面はspaを使う場合が多い、ユーザ体現向上はできますが、想定外エラーが発生する場合があります。Vueはそのエラーハンドリング機能を提供し、エラーレポートができます。',
documentation: 'ドキュメント'
},
excel: {
export: 'エクスポート',
selectedExport: 'エクスポート対象を選択してください。',
placeholder: 'ファイル名を入力してください。'
},
zip: {
export: 'エクスポート',
placeholder: 'ファイル名を入力してください。'
},
pdf: {
tips: 'window.print() を使ってPDFダウンロードしています。'
},
theme: {
change: 'テーマ切替',
documentation: 'ドキュメント',
tips: 'Tips: テーマの切り替え方法はnavbarのtheme-pickと異なります、使い方はドキュメントを確認してください。'
},
tagsView: {
refresh: '更新',
close: '閉じる',
closeOthers: 'その他閉じる',
closeAll: 'すべて閉じる'
},
settings: {
title: 'システムテーマ',
theme: 'テーマ色',
tagsView: 'Tags-View 開く',
fixedHeader: 'Fixed Header',
sidebarLogo: 'Sidebar Logo'
}
}

182
frontend/src/lang/tw.js Normal file
View File

@ -0,0 +1,182 @@
export default {
route: {
dashboard: '首页',
documentation: '文档',
guide: '引导页',
permission: '权限测试页',
rolePermission: '角色权限',
pagePermission: '页面权限',
directivePermission: '指令权限',
icons: '图标',
components: '组件',
tinymce: '富文本编辑器',
markdown: 'Markdown',
jsonEditor: 'JSON 编辑器',
dndList: '列表拖拽',
splitPane: 'Splitpane',
avatarUpload: '头像上传',
dropzone: 'Dropzone',
sticky: 'Sticky',
countTo: 'Count To',
componentMixin: '小组件',
backToTop: '返回顶部',
dragDialog: '拖拽 Dialog',
dragSelect: '拖拽 Select',
dragKanban: '可拖拽看板',
charts: '图表',
keyboardChart: '键盘图表',
lineChart: '折线图',
mixChart: '混合图表',
example: '综合实例',
nested: '路由嵌套',
menu1: '菜单1',
'menu1-1': '菜单 1-1',
'menu1-2': '菜单 1-2',
'menu1-2-1': '菜单 1-2-1',
'menu1-2-2': '菜单 1-2-2',
'menu1-3': '菜单 1-3',
menu2: '菜单 2',
Table: 'Table',
dynamicTable: '动态 Table',
dragTable: '拖拽 Table',
inlineEditTable: 'Table 内编辑',
complexTable: '综合 Table',
tab: 'Tab',
form: '表单',
createArticle: '创建文章',
editArticle: '编辑文章',
articleList: '文章列表',
errorPages: '错误页面',
page401: '401',
page404: '404',
errorLog: '错误日志',
excel: 'Excel',
exportExcel: '导出 Excel',
selectExcel: '导出 已选择项',
mergeHeader: '导出 多级表头',
uploadExcel: '上传 Excel',
zip: 'Zip',
pdf: 'PDF',
exportZip: 'Export Zip',
theme: '换肤',
clipboardDemo: 'Clipboard',
i18n: '国际化',
externalLink: '外链',
profile: '个人中心'
},
navbar: {
dashboard: '首页',
github: '项目地址',
logOut: '退出登录',
profile: '个人中心',
theme: '换肤',
size: '布局大小'
},
login: {
title: '系统登录',
welcome: '歡迎使用DATAEASE',
logIn: '登錄',
username: '账号',
password: '密码',
any: '随便填',
thirdparty: '第三方登录',
thirdpartyTips: '本地不能模拟,请结合自己业务进行模拟!!!'
},
commons: {
button: {
login: '登錄'
}
},
documentation: {
documentation: '文档',
github: 'Github 地址'
},
permission: {
addRole: '新增角色',
editPermission: '编辑权限',
roles: '你的权限',
switchRoles: '切换权限',
tips: '在某些情况下,不适合使用 v-permission。例如Element-UI 的 el-tab 或 el-table-column 以及其它动态渲染 dom 的场景。你只能通过手动设置 v-if 来实现。',
delete: '删除',
confirm: '确定',
cancel: '取消'
},
guide: {
description: '引导页对于一些第一次进入项目的人很有用,你可以简单介绍下项目的功能。本 Demo 是基于',
button: '打开引导'
},
components: {
documentation: '文档',
tinymceTips: '富文本是管理后台一个核心的功能但同时又是一个有很多坑的地方。在选择富文本的过程中我也走了不少的弯路市面上常见的富文本都基本用过了最终权衡了一下选择了Tinymce。更详细的富文本比较和介绍见',
dropzoneTips: '由于我司业务有特殊需求,而且要传七牛 所以没用第三方,选择了自己封装。代码非常的简单,具体代码你可以在这里看到 @/components/Dropzone',
stickyTips: '当页面滚动到预设的位置会吸附在顶部',
backToTopTips1: '页面滚动到指定位置会在右下角出现返回顶部按钮',
backToTopTips2: '可自定义按钮的样式、show/hide、出现的高度、返回的位置 如需文字提示可在外部使用Element的el-tooltip元素',
imageUploadTips: '由于我在使用时它只有vue@1版本而且和mockjs不兼容所以自己改造了一下如果大家要使用的话优先还是使用官方版本。'
},
table: {
dynamicTips1: '固定表头, 按照表头顺序排序',
dynamicTips2: '不固定表头, 按照点击顺序排序',
dragTips1: '默认顺序',
dragTips2: '拖拽后顺序',
title: '标题',
importance: '重要性',
type: '类型',
remark: '点评',
search: '搜索',
add: '添加',
export: '导出',
reviewer: '审核人',
id: '序号',
date: '时间',
author: '作者',
readings: '阅读数',
status: '状态',
actions: '操作',
edit: '编辑',
publish: '发布',
draft: '草稿',
delete: '删除',
cancel: '取 消',
confirm: '确 定'
},
example: {
warning: '创建和编辑页面是不能被 keep-alive 缓存的因为keep-alive 的 include 目前不支持根据路由来缓存,所以目前都是基于 component name 来进行缓存的。如果你想类似的实现缓存效果,可以使用 localStorage 等浏览器缓存方案。或者不要使用 keep-alive 的 include直接缓存所有页面。详情见'
},
errorLog: {
tips: '请点击右上角bug小图标',
description: '现在的管理后台基本都是spa的形式了它增强了用户体验但同时也会增加页面出问题的可能性可能一个小小的疏忽就导致整个页面的死锁。好在 Vue 官网提供了一个方法来捕获处理异常,你可以在其中进行错误处理或者异常上报。',
documentation: '文档介绍'
},
excel: {
export: '导出',
selectedExport: '导出已选择项',
placeholder: '请输入文件名(默认excel-list)'
},
zip: {
export: '导出',
placeholder: '请输入文件名(默认file)'
},
pdf: {
tips: '这里使用 window.print() 来实现下载pdf的功能'
},
theme: {
change: '换肤',
documentation: '换肤文档',
tips: 'Tips: 它区别于 navbar 上的 theme-pick, 是两种不同的换肤方法,各自有不同的应用场景,具体请参考文档。'
},
tagsView: {
refresh: '刷新',
close: '关闭',
closeOthers: '关闭其它',
closeAll: '关闭所有'
},
settings: {
title: '系统布局配置',
theme: '主题色',
tagsView: '开启 Tags-View',
fixedHeader: '固定 Header',
sidebarLogo: '侧边栏 Logo'
}
}

664
frontend/src/lang/zh.js Normal file
View File

@ -0,0 +1,664 @@
export default {
route: {
dashboard: '首页',
documentation: '文档',
guide: '引导页',
permission: '权限测试页',
rolePermission: '角色权限',
pagePermission: '页面权限',
directivePermission: '指令权限',
icons: '图标',
components: '组件',
tinymce: '富文本编辑器',
markdown: 'Markdown',
jsonEditor: 'JSON 编辑器',
dndList: '列表拖拽',
splitPane: 'Splitpane',
avatarUpload: '头像上传',
dropzone: 'Dropzone',
sticky: 'Sticky',
countTo: 'Count To',
componentMixin: '小组件',
backToTop: '返回顶部',
dragDialog: '拖拽 Dialog',
dragSelect: '拖拽 Select',
dragKanban: '可拖拽看板',
charts: '图表',
keyboardChart: '键盘图表',
lineChart: '折线图',
mixChart: '混合图表',
example: '综合实例',
nested: '路由嵌套',
menu1: '菜单1',
'menu1-1': '菜单 1-1',
'menu1-2': '菜单 1-2',
'menu1-2-1': '菜单 1-2-1',
'menu1-2-2': '菜单 1-2-2',
'menu1-3': '菜单 1-3',
menu2: '菜单 2',
Table: 'Table',
dynamicTable: '动态 Table',
dragTable: '拖拽 Table',
inlineEditTable: 'Table 内编辑',
complexTable: '综合 Table',
tab: 'Tab',
form: '表单',
createArticle: '创建文章',
editArticle: '编辑文章',
articleList: '文章列表',
errorPages: '错误页面',
page401: '401',
page404: '404',
errorLog: '错误日志',
excel: 'Excel',
exportExcel: '导出 Excel',
selectExcel: '导出 已选择项',
mergeHeader: '导出 多级表头',
uploadExcel: '上传 Excel',
zip: 'Zip',
pdf: 'PDF',
exportZip: 'Export Zip',
theme: '换肤',
clipboardDemo: 'Clipboard',
i18n: '国际化',
externalLink: '外链',
profile: '个人中心'
},
navbar: {
dashboard: '首页',
github: '项目地址',
logOut: '退出登录',
profile: '个人中心',
theme: '换肤',
size: '布局大小'
},
login: {
title: '系统登录',
welcome: '欢迎使用DATAEASE',
logIn: '登录',
username: '账号',
password: '密码',
any: '随便填',
thirdparty: '第三方登录',
thirdpartyTips: '本地不能模拟,请结合自己业务进行模拟!!!'
},
commons: {
upload: '上传',
cover: '覆盖',
not_cover: '不覆盖',
import_mode: '导入模式',
import_module: '导入模块',
please_fill_in_the_template: '请填写模版内容',
cut_back_old_version: '切回旧版',
cut_back_new_version: '切回新版',
comment: '评论',
examples: '示例',
help_documentation: '帮助文档',
api_help_documentation: 'API文档',
delete_cancelled: '已取消删除',
workspace: '工作空间',
organization: '组织',
menu: '菜单',
setting: '设置',
project: '项目',
about_us: '关于',
current_project: '当前项目',
name: '名称',
description: '描述',
annotation: '注释',
clear: '清空',
save: '保存',
update: '更新',
save_success: '保存成功',
delete_success: '删除成功',
copy_success: '复制成功',
modify_success: '修改成功',
delete_cancel: '已取消删除',
confirm: '确定',
cancel: '取消',
prompt: '提示',
operating: '操作',
input_limit: '长度在 {0} 到 {1} 个字符',
login: '登录',
welcome: '一站式开源数据分析平台',
username: '姓名',
password: '密码',
input_username: '请输入用户姓名',
input_password: '请输入密码',
test: '测试',
create_time: '创建时间',
update_time: '更新时间',
add: '添加',
member: '成员',
email: '邮箱',
phone: '电话',
role: '角色',
personal_info: '个人信息',
api_keys: 'API Keys',
quota: '配额管理',
status: '状态',
show_all: '显示全部',
show: '显示',
report: '报告',
user: '用户',
system: '系统',
personal_setting: '个人设置',
test_resource_pool: '测试资源池',
system_setting: '系统设置',
api: '视图',
dataset: '数据',
functional: '功能测试',
input_content: '请输入内容',
create: '新建',
edit: '编辑',
copy: '复制',
refresh: '刷新',
remark: '备注',
delete: '删除',
reduction: '恢复',
not_filled: '未填写',
please_select: '请选择',
search_by_name: '根据名称搜索',
search_by_name_or_id: '根据 ID/名称 搜索',
search_by_id_name_tag: '根据 ID/名称/标签 搜索',
personal_information: '个人信息',
exit_system: '退出系统',
verification: '验证',
title: '标题',
custom: '自定义',
select_date: '选择日期',
calendar_heatmap: '测试日历',
months_1: '一月',
months_2: '二月',
months_3: '三月',
months_4: '四月',
months_5: '五月',
months_6: '六月',
months_7: '七月',
months_8: '八月',
months_9: '九月',
months_10: '十月',
months_11: '十一月',
months_12: '十二月',
weeks_0: '周日',
weeks_1: '周一',
weeks_2: '周二',
weeks_3: '周三',
weeks_4: '周四',
weeks_5: '周五',
weeks_6: '周六',
test_unit: '测试',
system_parameter_setting: '系统参数设置',
connection_successful: '连接成功',
connection_failed: '连接失败',
save_failed: '保存失败',
host_cannot_be_empty: '主机不能为空',
port_cannot_be_empty: '端口号不能为空',
account_cannot_be_empty: '帐户不能为空',
remove: '移除',
remove_cancel: '移除取消',
remove_success: '移除成功',
tips: '认证信息已过期,请重新登录',
not_performed_yet: '尚未执行',
incorrect_input: '输入内容不正确',
delete_confirm: '请输入以下内容,确认删除:',
login_username: 'ID 或 邮箱',
input_login_username: '请输入用户 ID 或 邮箱',
input_name: '请输入名称',
please_upload: '请上传文件',
please_fill_path: '请填写ur路径',
formatErr: '格式错误',
please_save: '请先保存',
reference_documentation: '参考文档',
id: 'ID',
millisecond: '毫秒',
cannot_be_null: '不能为空',
required: '{0}是必填的',
already_exists: '名称不能重复',
modifier: '修改人',
validate: '校验',
batch_add: '批量添加',
check_project_tip: '请先创建或选择项目',
auth_redirect_tip: '即将跳转到认证源页面进行认证',
tag_tip: '输入回车添加标签',
table: {
select_tip: '已选中 {0} 条数据'
},
date: {
select_date: '选择日期',
start_date: '开始日期',
end_date: '结束日期',
select_date_time: '选择日期时间',
start_date_time: '开始日期时间',
end_date_time: '结束日期时间',
range_separator: '至',
data_time_error: '开始日期不能大于结束日期'
},
trigger_mode: {
name: '触发方式',
manual: '手动触发',
schedule: '定时任务',
api: 'API调用',
case: '用例触发'
},
adv_search: {
title: '高级搜索',
combine: '组合查询',
test: '所属测试',
project: '所属项目',
search: '查询',
reset: '重置',
and: '所有',
or: '任意一个',
operators: {
is_empty: '空',
is_not_empty: '非空',
like: '包含',
not_like: '不包含',
in: '属于',
not_in: '不属于',
gt: '大于',
ge: '大于等于',
lt: '小于',
le: '小于等于',
equals: '等于',
not_equals: '不等于',
between: '之间',
current_user: '是当前用户'
}
},
monitor: '监控',
all_label: {
case: '全部用例',
review: '全部评审'
},
image: '镜像',
tag: '标签',
module: {
select_module: '选择模块',
default_module: '默认模块'
},
datasource: '数据连接',
button: {
login: '登录'
}
},
documentation: {
documentation: '文档',
github: 'Github 地址'
},
permission: {
addRole: '新增角色',
editPermission: '编辑权限',
roles: '你的权限',
switchRoles: '切换权限',
tips: '在某些情况下,不适合使用 v-permission。例如Element-UI 的 el-tab 或 el-table-column 以及其它动态渲染 dom 的场景。你只能通过手动设置 v-if 来实现。',
delete: '删除',
confirm: '确定',
cancel: '取消'
},
guide: {
description: '引导页对于一些第一次进入项目的人很有用,你可以简单介绍下项目的功能。本 Demo 是基于',
button: '打开引导'
},
components: {
documentation: '文档',
tinymceTips: '富文本是管理后台一个核心的功能但同时又是一个有很多坑的地方。在选择富文本的过程中我也走了不少的弯路市面上常见的富文本都基本用过了最终权衡了一下选择了Tinymce。更详细的富文本比较和介绍见',
dropzoneTips: '由于我司业务有特殊需求,而且要传七牛 所以没用第三方,选择了自己封装。代码非常的简单,具体代码你可以在这里看到 @/components/Dropzone',
stickyTips: '当页面滚动到预设的位置会吸附在顶部',
backToTopTips1: '页面滚动到指定位置会在右下角出现返回顶部按钮',
backToTopTips2: '可自定义按钮的样式、show/hide、出现的高度、返回的位置 如需文字提示可在外部使用Element的el-tooltip元素',
imageUploadTips: '由于我在使用时它只有vue@1版本而且和mockjs不兼容所以自己改造了一下如果大家要使用的话优先还是使用官方版本。'
},
table: {
dynamicTips1: '固定表头, 按照表头顺序排序',
dynamicTips2: '不固定表头, 按照点击顺序排序',
dragTips1: '默认顺序',
dragTips2: '拖拽后顺序',
title: '标题',
importance: '重要性',
type: '类型',
remark: '点评',
search: '搜索',
add: '添加',
export: '导出',
reviewer: '审核人',
id: '序号',
date: '时间',
author: '作者',
readings: '阅读数',
status: '状态',
actions: '操作',
edit: '编辑',
publish: '发布',
draft: '草稿',
delete: '删除',
cancel: '取 消',
confirm: '确 定'
},
example: {
warning: '创建和编辑页面是不能被 keep-alive 缓存的因为keep-alive 的 include 目前不支持根据路由来缓存,所以目前都是基于 component name 来进行缓存的。如果你想类似的实现缓存效果,可以使用 localStorage 等浏览器缓存方案。或者不要使用 keep-alive 的 include直接缓存所有页面。详情见'
},
errorLog: {
tips: '请点击右上角bug小图标',
description: '现在的管理后台基本都是spa的形式了它增强了用户体验但同时也会增加页面出问题的可能性可能一个小小的疏忽就导致整个页面的死锁。好在 Vue 官网提供了一个方法来捕获处理异常,你可以在其中进行错误处理或者异常上报。',
documentation: '文档介绍'
},
excel: {
export: '导出',
selectedExport: '导出已选择项',
placeholder: '请输入文件名(默认excel-list)'
},
zip: {
export: '导出',
placeholder: '请输入文件名(默认file)'
},
pdf: {
tips: '这里使用 window.print() 来实现下载pdf的功能'
},
theme: {
change: '换肤',
documentation: '换肤文档',
tips: 'Tips: 它区别于 navbar 上的 theme-pick, 是两种不同的换肤方法,各自有不同的应用场景,具体请参考文档。'
},
tagsView: {
refresh: '刷新',
close: '关闭',
closeOthers: '关闭其它',
closeAll: '关闭所有'
},
settings: {
title: '系统布局配置',
theme: '主题色',
tagsView: '开启 Tags-View',
fixedHeader: '固定 Header',
sidebarLogo: '侧边栏 Logo'
},
license: {
title: '授权管理',
corporation: '客户名称',
time: '授权时间',
product: '产品名称',
edition: '产品版本',
licenseVersion: '授权版本',
count: '授权数量',
valid_license: '授权验证',
show_license: '查看授权',
valid_license_error: '授权验证失败',
status: '授权状态',
valid: '有效',
invalid: '无效',
expired: '已过期'
},
user: {
create: '创建用户',
modify: '修改用户',
input_name: '请输入用户姓名',
input_id: '请输入ID',
input_email: '请输入邮箱',
input_password: '请输入密码',
input_phone: '请输入电话号码',
special_characters_are_not_supported: '不支持特殊字符',
mobile_number_format_is_incorrect: '手机号码格式不正确',
email_format_is_incorrect: '邮箱格式不正确',
delete_confirm: '这个用户确定要删除吗?',
apikey_delete_confirm: '这个 API Key 确定要删除吗?',
input_id_placeholder: '请输入ID (不支持中文)',
source: '用户来源'
},
role: {
please_choose_role: '请选择角色',
admin: '系统管理员',
org_admin: '组织管理员',
org_member: '组织成员',
test_manager: '测试经理',
test_user: '测试人员',
test_viewer: '只读用户',
add: '添加角色',
delete: '删除角色',
modify: '修改角色'
},
menu: {
create: '创建菜单',
modify: '修改菜单',
delete: '删除菜单'
},
organization: {
create: '创建组织',
modify: '修改组织',
delete: '删除组织',
delete_confirm: '删除该组织会关联删除该组织下的所有资源(如:相关工作空间,项目,测试用例等),确定要删除吗?',
input_name: '请输入组织名称',
select_organization: '请选择组织',
search_by_name: '根据名称搜索',
special_characters_are_not_supported: '格式错误(不支持特殊字符,且不能以\'-\'开头结尾)',
none: '无组织',
select: '选择组织',
delete_warning: '删除该组织将同步删除该组织下所有相关工作空间和相关工作空间下的所有项目,以及项目中的所有用例、接口测试、性能测试等,确定要删除吗?',
service_integration: '服务集成',
defect_manage: '缺陷管理平台',
message_settings: '消息设置',
message: {
jenkins_task_notification: 'Jenkins接口调用任务通知',
test_plan_task_notification: '测试计划任务通知',
test_review_task_notice: '测试评审任务通知',
create_new_notification: '创建新通知',
mail_template_example: '邮件模版',
robot_template: '机器人模版',
select_events: '选择事件',
defect_task_notification: '缺陷任务通知',
select_receiving_method: '选择接收方式',
mail: '邮件',
nail_robot: '钉钉机器人',
enterprise_wechat_robot: '企业微信机器人',
notes: '1.钉钉和企业群里新建一个自定义机器人,然后复制 webhook 地址在我们平台上;\n' +
' 2.机器人选择为群机器人,安全验证选择“自定义关键词” "任务通知";\n' +
' 3.选择接收人时必须是你所建的群里包含的人,接收人手机号为必填项且为钉钉企业所使用的手机号,',
message: '事件,接收人,接收方式为必填项',
message_webhook: '接收方式为钉钉和企业机器人时webhook为必填项',
template: '模版'
},
integration: {
select_defect_platform: '请选择要集成的缺陷管理平台:',
basic_auth_info: 'Basic Auth 账号信息:',
api_account: 'API 账号',
api_password: 'API 口令',
app_name: '应用代号',
app_key: '密钥',
account: '账号',
password: '密码',
jira_url: 'JIRA 地址',
jira_issuetype: '问题类型',
input_api_account: '请输入账号',
input_api_password: '请输入密码',
input_jira_url: '请输入Jira地址https://metersphere.atlassian.net/',
input_jira_issuetype: '请输入问题类型',
zentao_url: 'Zentao 地址',
input_zentao_url: '请输入Zentao地址http://xx.xx.xx.xx/zentao/',
use_tip: '使用指引:',
use_tip_tapd: 'Tapd Basic Auth 账号信息在"公司管理-安全与集成-开放平台"中查询',
use_tip_jira: 'Jira software server 认证信息为 账号密码Jira software cloud 认证信息为 账号+令牌(账户设置-安全-创建API令牌)',
use_tip_zentao: '账号密码为具有相应权限的Zentao账号账号需要具有 超级model调用接口权限',
use_tip_two: '保存 Basic Auth 账号信息后,需要在 Metersphere 项目中手动关联 ID/key',
link_the_project_now: '马上关联项目',
cancel_edit: '取消编辑',
cancel_integration: '取消集成',
cancel_confirm: '确认取消集成 ',
successful_operation: '操作成功',
not_integrated: '未集成该平台',
choose_platform: '请选择集成的平台',
verified: '验证通过'
}
},
system_parameter_setting: {
mailbox_service_settings: '邮件设置',
ldap_setting: 'LDAP设置',
test_connection: '测试连接',
SMTP_host: 'SMTP主机',
SMTP_port: 'SMTP端口',
SMTP_account: 'SMTP账户',
SMTP_password: 'SMTP密码',
SSL: '开启SSL(如果SMTP端口是465通常需要启用SSL)',
TLS: '开启TLS(如果SMTP端口是587通常需要启用TLS)',
SMTP: '是否免密 SMTP',
host: '主机号不能为空',
port: '端口号不能为空',
account: '账户不能为空',
test_recipients: '测试收件人',
tip: '提示:仅用来作为测试邮件收件人'
},
variables: {
cvs_info: '只能上传CSV文件',
end: '结束',
start: '开始',
increment: '增量',
counter_info: '000产生至少3位数字。user_000输出形式为user_nnn',
config: '配置',
add_file: '添加文件',
delimiter: '分隔符',
format: '输出格式'
},
auth_source: {
delete_prompt: '此操作会删除认证源,是否继续?',
title: '认证设置',
auth_name_valid: '名称不支持特殊字符'
},
chart: {
datalist: '视图列表',
add_group: '添加分组',
add_scene: '添加场景',
group: '分组',
scene: '场景',
delete: '删除',
move_to: '移动到',
rename: '重命名',
tips: '提示',
confirm_delete: '确认删除',
delete_success: '删除成功',
confirm: '确认',
cancel: '取消',
search: '搜索',
back: '返回',
add_table: '添加表',
process: '进度',
add_chart: '添加视图',
db_data: '数据库表',
sql_data: 'SQL数据集',
excel_data: 'Excel数据集',
custom_data: '自助数据集',
pls_slc_tbl_left: '请从左侧选视图',
add_db_table: '添加数据库表',
pls_slc_data_source: '请选择数据库连接',
table: '表',
edit: '编辑',
create_view: '创建试图',
data_preview: '数据预览',
dimension: '维度',
quota: '指标',
title: '标题',
show: '显示',
chart_type: '图表类型',
shape_attr: '图形属性',
module_style: '组件样式',
result_filter: '结果过滤器',
x_axis: '横轴',
y_axis: '纵轴',
chart: '视图'
},
dataset: {
datalist: '数据列表',
add_group: '添加分组',
add_scene: '添加场景',
group: '分组',
scene: '场景',
delete: '删除',
move_to: '移动到',
rename: '重命名',
tips: '提示',
confirm_delete: '确认删除',
delete_success: '删除成功',
confirm: '确认',
cancel: '取消',
search: '搜索',
back: '返回',
add_table: '添加表',
process: '进度',
update: '更新',
db_data: '数据库表',
sql_data: 'SQL数据集',
excel_data: 'Excel数据集',
custom_data: '自助数据集',
pls_slc_tbl_left: '请从左侧选择表',
add_db_table: '添加数据库表',
pls_slc_data_source: '请选择数据库连接',
table: '表',
edit: '编辑',
create_view: '创建视图',
data_preview: '数据预览',
field_type: '字段类型',
field_name: '字段名',
field_origin_name: '原始名',
field_check: '选中',
update_info: '更新信息',
join_view: '关联视图',
text: '文本',
time: '时间',
value: '数值',
mode: '模式',
direct_connect: '直连',
sync_data: '定时同步',
update_setting: '更新设置',
sync_now: '立即更新',
add_task: '添加任务'
},
datasource: {
create: '新建数据连接',
type: '类型',
please_choose_type: '请选择数据源类型',
data_base: '数据库名称',
user_name: '用户名',
password: '密码',
host: '主机',
port: '端口',
please_input_data_base: '请输入数据库名称',
please_input_user_name: '请输入用户名',
please_input_password: '请输入密码',
please_input_host: '请输入主机',
please_input_port: '请输入端口',
modify: '修改组织',
validate_success: '校验成功',
delete: '删除组织',
delete_confirm: '删除该组织会关联删除该组织下的所有资源(如:相关工作空间,项目,测试用例等),确定要删除吗?',
input_name: '请输入名称',
input_desc: '请输入描述',
select_organization: '请选择组织',
search_by_name: '根据名称搜索',
special_characters_are_not_supported: '格式错误(不支持特殊字符,且不能以\'-\'开头结尾)',
none: '无组织',
select: '选择组织',
delete_warning: '删除该数据连接将同步删除该数据连接下所有相关的数据集, 确定要删除吗?',
service_integration: '服务集成',
defect_manage: '缺陷管理平台',
message_settings: '消息设置',
message: {
jenkins_task_notification: 'Jenkins接口调用任务通知',
test_plan_task_notification: '测试计划任务通知',
test_review_task_notice: '测试评审任务通知',
create_new_notification: '创建新通知',
mail_template_example: '邮件模版',
robot_template: '机器人模版',
select_events: '选择事件',
defect_task_notification: '缺陷任务通知',
select_receiving_method: '选择接收方式',
mail: '邮件',
nail_robot: '钉钉机器人',
enterprise_wechat_robot: '企业微信机器人',
notes: '1.钉钉和企业群里新建一个自定义机器人,然后复制 webhook 地址在我们平台上;\n' +
' 2.机器人选择为群机器人,安全验证选择“自定义关键词” "任务通知";\n' +
' 3.选择接收人时必须是你所建的群里包含的人,接收人手机号为必填项且为钉钉企业所使用的手机号,',
message: '事件,接收人,接收方式为必填项',
message_webhook: '接收方式为钉钉和企业机器人时webhook为必填项',
template: '模版'
}
}
}

View File

@ -0,0 +1,46 @@
<template>
<section class="app-main">
<transition name="fade-transform" mode="out-in">
<el-main class="ms-main-container">
<keep-alive>
<router-view :key="key" />
</keep-alive>
</el-main>
</transition>
</section>
</template>
<script>
export default {
name: 'AppMain',
computed: {
key() {
return this.$route.path
}
}
}
</script>
<style lang="scss" scoped>
@import "~@/styles/variables.scss";
.app-main {
/*107 = navbar 50 + topbar 57 */
min-height: calc(100vh - 107px);
width: 100%;
position: relative;
overflow: hidden;
}
.fixed-header+.app-main {
padding-top: 50px;
}
</style>
<style lang="scss">
// fix css style bug in open el-dialog
.el-popup-parent--hidden {
.fixed-header {
padding-right: 15px;
}
}
</style>

View File

@ -0,0 +1,140 @@
<template>
<div class="drawer-container">
<div>
<h3 class="drawer-title">系统布局设置</h3>
<div class="drawer-item">
<span>主题颜色</span>
<theme-picker style="float: right;height: 26px;margin: -3px 8px 0 0;" @change="themeChange" />
</div>
<!-- <div class="drawer-item">
<span>显示标签</span>
<el-switch v-model="tagsView" class="drawer-switch" />
</div>
<div class="drawer-item">
<span>固定头部</span>
<el-switch v-model="fixedHeader" class="drawer-switch" />
</div> -->
<div class="drawer-item">
<span>显示LOGO</span>
<el-switch v-model="sidebarLogo" class="drawer-switch" />
</div>
<!-- <div class="drawer-item">
<span>菜单UniqueOpened</span>
<el-switch v-model="uniqueOpened" class="drawer-switch" />
</div> -->
<div class="drawer-item">
<span>展开菜单</span>
<el-switch v-model="toggleSideBar" class="drawer-switch" />
</div>
</div>
</div>
</template>
<script>
import ThemePicker from '@/components/ThemePicker'
export default {
components: { ThemePicker },
data() {
return {
showMenu: true
}
},
computed: {
// fixedHeader: {
// get() {
// return this.$store.state.settings.fixedHeader
// },
// set(val) {
// this.$store.dispatch('settings/changeSetting', {
// key: 'fixedHeader',
// value: val
// })
// }
// },
// tagsView: {
// get() {
// return this.$store.state.settings.tagsView
// },
// set(val) {
// this.$store.dispatch('settings/changeSetting', {
// key: 'tagsView',
// value: val
// })
// }
// },
sidebarLogo: {
get() {
return this.$store.state.settings.sidebarLogo
},
set(val) {
this.$store.dispatch('settings/changeSetting', {
key: 'sidebarLogo',
value: val
})
}
},
toggleSideBar: {
get() {
return this.showMenu
},
set(val) {
this.showMenu = !this.showMenu
this.$store.dispatch('app/toggleSideBar')
}
}
// uniqueOpened: {
// get() {
// return this.$store.state.settings.uniqueOpened
// },
// set(val) {
// this.$store.dispatch('settings/changeSetting', {
// key: 'uniqueOpened',
// value: val
// })
// }
// }
},
methods: {
themeChange(val) {
this.$store.dispatch('settings/changeSetting', {
key: 'theme',
value: val
})
}
}
}
</script>
<style lang="scss" scoped>
.drawer-container {
padding: 24px;
font-size: 14px;
line-height: 1.5;
word-wrap: break-word;
.drawer-title {
margin-bottom: 12px;
color: rgba(0, 0, 0, .85);
font-size: 14px;
line-height: 22px;
}
.drawer-item {
color: rgba(0, 0, 0, .65);
font-size: 14px;
padding: 12px 0;
}
.drawer-switch {
float: right
}
}
</style>

View File

@ -0,0 +1,26 @@
export default {
computed: {
device() {
return this.$store.state.app.device
}
},
mounted() {
// In order to fix the click on menu on the ios device will trigger the mouseleave bug
// https://github.com/PanJiaChen/vue-element-admin/issues/1135
this.fixBugIniOS()
},
methods: {
fixBugIniOS() {
const $subMenu = this.$refs.subMenu
if ($subMenu) {
const handleMouseleave = $subMenu.handleMouseleave
$subMenu.handleMouseleave = (e) => {
if (this.device === 'mobile') {
return
}
handleMouseleave(e)
}
}
}
}
}

View File

@ -0,0 +1,29 @@
<script>
export default {
name: 'MenuItem',
functional: true,
props: {
icon: {
type: String,
default: ''
},
title: {
type: String,
default: ''
}
},
render(h, context) {
const { icon, title } = context.props
const vnodes = []
if (icon) {
vnodes.push(<svg-icon icon-class={icon}/>)
}
if (title) {
vnodes.push(<span slot='title'>{(title)}</span>)
}
return vnodes
}
}
</script>

View File

@ -0,0 +1,36 @@
<template>
<!-- eslint-disable vue/require-component-is -->
<component v-bind="linkProps(to)">
<slot />
</component>
</template>
<script>
import { isExternal } from '@/utils/validate'
export default {
props: {
to: {
type: String,
required: true
}
},
methods: {
linkProps(url) {
if (isExternal(url)) {
return {
is: 'a',
href: url,
target: '_blank',
rel: 'noopener'
}
}
return {
is: 'router-link',
to: url
}
}
}
}
</script>

View File

@ -0,0 +1,82 @@
<template>
<div class="sidebar-logo-container" :class="{'collapse':collapse}">
<transition name="sidebarLogoFade">
<router-link v-if="collapse" key="collapse" class="sidebar-logo-link" to="/">
<img v-if="logo" :src="logo" class="sidebar-logo">
<h1 v-else class="sidebar-title">{{ title }} </h1>
</router-link>
<router-link v-else key="expand" class="sidebar-logo-link" to="/">
<img v-if="logo" :src="logo" class="sidebar-logo">
<h1 class="sidebar-title">{{ title }} </h1>
</router-link>
</transition>
</div>
</template>
<script>
export default {
name: 'SidebarLogo',
props: {
collapse: {
type: Boolean,
required: true
}
},
data() {
return {
title: 'Vue Admin Template',
logo: 'https://wpimg.wallstcn.com/69a1c46c-eb1c-4b46-8bd4-e9e686ef5251.png'
}
}
}
</script>
<style lang="scss" scoped>
.sidebarLogoFade-enter-active {
transition: opacity 1.5s;
}
.sidebarLogoFade-enter,
.sidebarLogoFade-leave-to {
opacity: 0;
}
.sidebar-logo-container {
position: relative;
width: 100%;
height: 50px;
line-height: 50px;
background: #2b2f3a;
text-align: center;
overflow: hidden;
& .sidebar-logo-link {
height: 100%;
width: 100%;
& .sidebar-logo {
width: 32px;
height: 32px;
vertical-align: middle;
margin-right: 12px;
}
& .sidebar-title {
display: inline-block;
margin: 0;
color: #fff;
font-weight: 600;
line-height: 50px;
font-size: 14px;
font-family: Avenir, Helvetica Neue, Arial, Helvetica, sans-serif;
vertical-align: middle;
}
}
&.collapse {
.sidebar-logo {
margin-right: 0px;
}
}
}
</style>

View File

@ -0,0 +1,98 @@
<template>
<div v-if="!item.hidden">
<template v-if="hasOneShowingChild(item.children,item) && (!onlyOneChild.children||onlyOneChild.noShowingChildren)&&!item.alwaysShow">
<app-link v-if="onlyOneChild.meta" :to="resolvePath(onlyOneChild.path)">
<el-menu-item :index="resolvePath(onlyOneChild.path)" :class="{'submenu-title-noDropdown':!isNest}">
<item :icon="onlyOneChild.meta.icon||(item.meta&&item.meta.icon)" :title="onlyOneChild.meta.title" />
</el-menu-item>
</app-link>
</template>
<el-submenu v-else ref="subMenu" :index="resolvePath(item.path)" popper-append-to-body>
<template slot="title">
<item v-if="item.meta" :icon="item.meta && item.meta.icon" :title="item.meta.title" />
</template>
<sidebar-item
v-for="child in item.children"
:key="child.path"
:is-nest="true"
:item="child"
:base-path="resolvePath(child.path)"
class="nest-menu"
/>
</el-submenu>
</div>
</template>
<script>
import path from 'path'
import { isExternal } from '@/utils/validate'
import Item from './Item'
import AppLink from './Link'
import FixiOSBug from './FixiOSBug'
export default {
name: 'SidebarItem',
components: { Item, AppLink },
mixins: [FixiOSBug],
props: {
// route object
item: {
type: Object,
required: true
},
isNest: {
type: Boolean,
default: false
},
basePath: {
type: String,
default: ''
}
},
data() {
// To fix https://github.com/PanJiaChen/vue-admin-template/issues/237
// TODO: refactor with render function
this.onlyOneChild = null
return {}
},
methods: {
hasOneShowingChild(children = [], parent) {
const showingChildren = children.filter(item => {
if (item.hidden) {
return false
} else {
// Temp set(will be used if only has one showing child)
this.onlyOneChild = item
return true
}
})
// When there is only one child router, the child router is displayed by default
if (showingChildren.length === 1) {
return true
}
// Show parent if there are no child router to display
if (showingChildren.length === 0) {
this.onlyOneChild = { ... parent, path: '', noShowingChildren: true }
return true
}
return false
},
resolvePath(routePath) {
if (isExternal(routePath)) {
return routePath
}
if (isExternal(this.basePath)) {
return this.basePath
}
const currentRoutes = this.$store.state.permission.currentRoutes
if (currentRoutes && currentRoutes.path) {
return path.resolve(currentRoutes.path, this.basePath, routePath)
}
}
}
}
</script>

View File

@ -0,0 +1,57 @@
<template>
<div :class="{'has-logo':showLogo}">
<logo v-if="showLogo" :collapse="isCollapse" />
<el-scrollbar wrap-class="scrollbar-wrapper">
<el-menu
:default-active="activeMenu"
:collapse="isCollapse"
:background-color="variables.menuBg"
:text-color="variables.menuText"
:unique-opened="false"
:active-text-color="variables.menuActiveText"
:collapse-transition="false"
mode="vertical"
>
<sidebar-item v-for="route in routes" :key="route.path" :item="route" :base-path="route.path" />
</el-menu>
</el-scrollbar>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
import Logo from './Logo'
import SidebarItem from './SidebarItem'
import variables from '@/styles/variables.scss'
export default {
components: { SidebarItem, Logo },
computed: {
...mapGetters([
'sidebar'
]),
routes() {
// return this.$router.options.routes
return this.$store.state.permission.currentRoutes.children
},
activeMenu() {
const route = this.$route
const { meta, path } = route
// if set path, the sidebar will highlight the path you set
if (meta.activeMenu) {
return meta.activeMenu
}
return path
},
showLogo() {
return this.$store.state.settings.sidebarLogo
},
variables() {
return variables
},
isCollapse() {
return !this.sidebar.opened
}
}
}
</script>

View File

@ -0,0 +1,198 @@
<template>
<div class="top-nav">
<div class="log">DATA_EASE</div>
<el-menu
:active-text-color="variables.menuActiveText"
:default-active="activeMenu"
mode="horizontal"
@select="handleSelect"
>
<div v-for="item in permission_routes" :key="item.path" class="nav-item">
<app-link :to="resolvePath(item)">
<el-menu-item
v-if="!item.hidden"
:index="item.path"
>{{ item.meta ? item.meta.title : item.children[0].meta.title }}</el-menu-item>
</app-link>
</div>
</el-menu>
<div class="right-menu">
<template>
<el-tooltip content="项目文档" effect="dark" placement="bottom">
<Doc class="right-menu-item hover-effect" />
</el-tooltip>
<el-tooltip content="全屏缩放" effect="dark" placement="bottom">
<screenfull id="screenfull" class="right-menu-item hover-effect" />
</el-tooltip>
<el-tooltip :content="$t('navbar.size')" effect="dark" placement="bottom">
<size-select id="size-select" class="right-menu-item hover-effect" />
</el-tooltip>
<lang-select class="right-menu-item hover-effect" />
</template>
<el-dropdown class="avatar-container" trigger="click">
<div class="avatar-wrapper">
<img src="@/assets/avatar.jpeg" class="user-avatar">
</div>
<el-dropdown-menu slot="dropdown" class="user-dropdown">
<router-link to="/">
<el-dropdown-item>Home</el-dropdown-item>
</router-link>
<a href="https://github.com/PanJiaChen/vue-admin-template/" target="_blank">
<el-dropdown-item>Github</el-dropdown-item>
</a>
<a href="https://panjiachen.github.io/vue-element-admin-site/#/" target="_blank">
<el-dropdown-item>Docs</el-dropdown-item>
</a>
<el-dropdown-item divided @click.native="logout">
<span style="display:block;">Log Out</span>
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
import AppLink from './Sidebar/Link'
import variables from '@/styles/variables.scss'
import { isExternal } from '@/utils/validate'
import Doc from '@/components/Doc'
import Screenfull from '@/components/Screenfull'
import SizeSelect from '@/components/SizeSelect'
import LangSelect from '@/components/LangSelect'
export default {
name: 'Topbar',
components: {
AppLink,
Screenfull,
SizeSelect,
LangSelect,
Doc
},
data() {
return {
}
},
computed: {
activeMenu() {
const route = this.$route
const { meta, path } = route
// if set path, the sidebar will highlight the path you set
if (meta.activeMenu) {
return meta.activeMenu
}
//
if (path === '/dashboard') {
return '/'
}
//
const activeMenu = '/' + path.split('/')[1]
return activeMenu
},
variables() {
return variables
},
sidebar() {
return this.$store.state.app.sidebar
},
...mapGetters([
'avatar',
'permission_routes'
])
},
mounted() {
this.initCurrentRoutes()
},
methods: {
// store
initCurrentRoutes() {
const { path } = this.$route
let route = this.permission_routes.find(
item => item.path === '/' + path.split('/')[1]
)
//
if (!route) {
route = this.permission_routes.find(item => item.path === '/')
}
this.$store.commit('permission/SET_CURRENT_ROUTES', route)
this.setSidebarHide(route)
},
//
isOnlyOneChild(item) {
if (item.children && item.children.length === 1) {
return true
}
return false
},
resolvePath(item) {
// url
if (isExternal(item.path)) {
return item.path
}
//
if (item.path === '/') {
const path = item.redirect
return path
}
//
let path = ''
/**
* item 路由子项
* parent 路由父项
*/
const getDefaultPath = (item, parent) => {
// path
if (isExternal(item.path)) {
path = item.path
return
}
// parent
if (parent) {
path += (parent.path + '/' + item.path)
} else {
path += ('/' + item.path)
}
//
if (item.children) {
getDefaultPath(item.children[0])
}
}
if (item.children) {
getDefaultPath(item.children[0], item)
return path
}
return item.path
},
handleSelect(key, keyPath) {
// store
const route = this.permission_routes.find(item => item.path === key)
this.$store.commit('permission/SET_CURRENT_ROUTES', route)
this.setSidebarHide(route)
},
//
setSidebarHide(route) {
if (!route.children || route.children.length === 1) {
this.$store.dispatch('app/toggleSideBarHide', true)
} else {
this.$store.dispatch('app/toggleSideBarHide', false)
}
},
async logout() {
await this.$store.dispatch('user/logout')
this.$router.push(`/login?redirect=${this.$route.fullPath}`)
}
}
}
</script>
<style lang="scss" scoped>
</style>

View File

@ -0,0 +1,5 @@
// export { default as Navbar } from './Navbar'
export { default as Sidebar } from './Sidebar'
export { default as Settings } from './Settings'
export { default as AppMain } from './AppMain'
export { default as Topbar } from './Topbar'

View File

@ -0,0 +1,104 @@
<template>
<div :class="classObj" class="app-wrapper">
<topbar />
<div v-if="device==='mobile'&&sidebar.opened" class="drawer-bg" @click="handleClickOutside" />
<sidebar v-if="!sidebar.hide" class="sidebar-container" />
<div :class="{sidebarHide: sidebar.hide}" class="main-container">
<div :class="{'fixed-header':fixedHeader}">
<!-- <navbar /> -->
</div>
<app-main />
<right-panel v-if="showSettings">
<settings />
</right-panel>
</div>
</div>
</template>
<script>
import RightPanel from '@/components/RightPanel'
import { Sidebar, Settings, AppMain, Topbar } from './components'
import ResizeMixin from './mixin/ResizeHandler'
export default {
name: 'Layout',
components: {
RightPanel,
Sidebar,
Settings,
AppMain,
Topbar
},
mixins: [ResizeMixin],
computed: {
sidebar() {
return this.$store.state.app.sidebar
},
device() {
return this.$store.state.app.device
},
fixedHeader() {
return this.$store.state.settings.fixedHeader
},
showSettings() {
return this.$store.state.settings.showSettings
},
classObj() {
return {
hideSidebar: !this.sidebar.opened,
openSidebar: this.sidebar.opened,
withoutAnimation: this.sidebar.withoutAnimation,
mobile: this.device === 'mobile'
}
}
},
methods: {
handleClickOutside() {
this.$store.dispatch('app/closeSideBar', { withoutAnimation: false })
}
}
}
</script>
<style lang="scss" scoped>
@import "~@/styles/mixin.scss";
@import "~@/styles/variables.scss";
.app-wrapper {
@include clearfix;
position: relative;
// height: 100%;
height: $contentHeight;
width: 100%;
&.mobile.openSidebar{
position: fixed;
top: 0;
}
}
.drawer-bg {
background: #000;
opacity: 0.3;
width: 100%;
top: 0;
height: 100%;
position: absolute;
z-index: 999;
}
.fixed-header {
position: fixed;
top: 0;
right: 0;
z-index: 9;
width: calc(100% - #{$sideBarWidth});
transition: width 0.28s;
}
.hideSidebar .fixed-header {
width: calc(100% - 54px)
}
.mobile .fixed-header {
width: 100%;
}
</style>

View File

@ -0,0 +1,45 @@
import store from '@/store'
const { body } = document
const WIDTH = 992 // refer to Bootstrap's responsive design
export default {
watch: {
$route(route) {
if (this.device === 'mobile' && this.sidebar.opened) {
store.dispatch('app/closeSideBar', { withoutAnimation: false })
}
}
},
beforeMount() {
window.addEventListener('resize', this.$_resizeHandler)
},
beforeDestroy() {
window.removeEventListener('resize', this.$_resizeHandler)
},
mounted() {
const isMobile = this.$_isMobile()
if (isMobile) {
store.dispatch('app/toggleDevice', 'mobile')
store.dispatch('app/closeSideBar', { withoutAnimation: true })
}
},
methods: {
// use $_ for mixins properties
// https://vuejs.org/v2/style-guide/index.html#Private-property-names-essential
$_isMobile() {
const rect = body.getBoundingClientRect()
return rect.width - 1 < WIDTH
},
$_resizeHandler() {
if (!document.hidden) {
const isMobile = this.$_isMobile()
store.dispatch('app/toggleDevice', isMobile ? 'mobile' : 'desktop')
if (isMobile) {
store.dispatch('app/closeSideBar', { withoutAnimation: true })
}
}
}
}
}

60
frontend/src/main.js Normal file
View File

@ -0,0 +1,60 @@
import Vue from 'vue'
import Cookies from 'js-cookie'
import 'normalize.css/normalize.css' // A modern alternative to CSS resets
import '@/styles/index.scss' // global css
import ElementUI from 'element-ui'
import Fit2CloudUI from 'fit2cloud-ui'
import 'element-ui/lib/theme-chalk/index.css'
import './styles/element-variables.scss'
import i18n from './lang' // internationalization
import App from './App'
import store from './store'
import router from './router'
import '@/icons' // icon
import '@/permission' // permission control
import api from '@/api/index.js'
import filter from '@/filter/filter'
import message from '@/metersphere/common/js/message'
import { left2RightDrag, bottom2TopDrag, right2LeftDrag } from '@/metersphere/common/js/directive'
Vue.prototype.$api = api
/**
* If you don't want to use mock-server
* you want to use MockJs for mock api
* you can execute: mockXHR()
*
* Currently MockJs will be used in the production environment,
* please remove it before going online ! ! !
*/
if (process.env.NODE_ENV === 'production') {
const { mockXHR } = require('../mock')
mockXHR()
}
// set ElementUI lang to EN
// Vue.use(ElementUI, { locale })
// 如果想要中文版 element-ui按如下方式声明
Vue.use(ElementUI, {
size: Cookies.get('size') || 'medium', // set element-ui default size
i18n: (key, value) => i18n.t(key, value)
})
Vue.use(Fit2CloudUI, {
i18n: (key, value) => i18n.t(key, value)
})
Vue.use(filter)
Vue.use(message)
Vue.config.productionTip = false
// 支持左右拖拽
Vue.directive('left-to-right-drag', left2RightDrag)
Vue.directive('right-to-left-drag', right2LeftDrag)
Vue.directive('bottom-to-top-drag', bottom2TopDrag)
new Vue({
el: '#app',
router,
store,
i18n,
render: h => h(App)
})

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