master
Hoshi 2024-03-14 11:36:22 +08:00
commit a9cf567b08
533 changed files with 75930 additions and 0 deletions

3
.browserslistrc Normal file
View File

@ -0,0 +1,3 @@
> 1%
last 2 versions
not ie <= 8

5
.editorconfig Normal file
View File

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

4
.env.development Normal file
View File

@ -0,0 +1,4 @@
/**
* Created by Zhang Haijun on 2018/8/13.
*/
VUE_APP_BASEURL = '/cop-web/'

4
.env.production Normal file
View File

@ -0,0 +1,4 @@
/**
* Created by Zhang Haijun on 2018/8/13.
*/
VUE_APP_BASEURL = '/cop-web/'

1
.eslintignore Normal file
View File

@ -0,0 +1 @@
/src/common/fonts

29
.eslintrc.js Normal file
View File

@ -0,0 +1,29 @@
module.exports = {
root: true,
env: {
node: true
},
extends: ['plugin:vue/essential', '@vue/standard', '@vue/typescript'],
rules: {
'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
'no-unused-vars': [
0,
{
vars: 'all',
args: 'none'
}
],
semi: 0,
eqeqeq: 0,
'one-var': 0,
camelcase: 0,
'no-case-declarations': 0,
'space-before-function-paren': 0,
'vue/no-parsing-error': [2, { "x-invalid-end-tag": false }],
'@typescript-eslint/indent': ['error', 2]
},
parserOptions: {
parser: '@typescript-eslint/parser'
}
};

31
.gitignore vendored Normal file
View File

@ -0,0 +1,31 @@
.DS_Store
node_modules
/cop-web
cop-web.tar.gz
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
#yarn3
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/sdks
!.yarn/versions

6
.prettierrc Normal file
View File

@ -0,0 +1,6 @@
{
"printWidth": 300,
"tabWidth": 2,
"singleQuote": true,
"semi": false
}

10
Jenkinsfile vendored Normal file
View File

@ -0,0 +1,10 @@
@Library('jgpl') _
webPipeline([
onBuildInstallShell:{->
return """
npm install url-loader --userconfig ${env.NPM_CONFIG_USERCONFIG} --no-package-lock
pnpm install -no-frozen-lockfile
pnpm run build || echo 'Ignore build error !!!'
"""
},
])

24
README.md Normal file
View File

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

6
auto-imports.d.ts vendored Normal file
View File

@ -0,0 +1,6 @@
// Generated by 'unplugin-auto-import'
// We suggest you to commit this file into source control
declare global {
const ElButton: typeof import('element-ui/lib/button')['ElButton']
}
export {}

6
babel.config.js Normal file
View File

@ -0,0 +1,6 @@
module.exports = {
plugins: ['babel-plugin-transform-vite-meta-glob'],
presets: [
'@vue/cli-plugin-babel/preset'
]
}

37
backup/mongodb.json Normal file
View File

@ -0,0 +1,37 @@
{
"deploy": {
"standalone": {
"icon": "mongodb",
"depend": "^task[.]resource[.]cvm[.][a-z]{1,20}$",
"limits": "1"
},
"cluster": {
"icon": "mongodb-cluster",
"topo": [{
"role": "mongo_master",
"name": "主节点(1)",
"code": "task.software.mongodb",
"depend": "^task[.]resource[.]cvm[.][a-z]{1,20}$",
"limits": "^1$",
"limits_message": "主节点主机数量必须为1"
},
{
"role": "mongo_replicas",
"name": "从节点(2)",
"code": "task.software.mongodb",
"depend": "^task[.]resource[.]cvm[.][a-z]{1,20}$",
"limits": "^2$",
"limits_message": "主机数量必须为2"
},
{
"role": "mongo_arbiter",
"name": "仲裁节点(1)",
"code": "task.software.mongodb",
"depend": "^task[.]resource[.]cvm[.][a-z]{1,20}$",
"limits": "^1$",
"limits_message": "仲裁节点主机数量必须为1"
}
]
}
}
}

91
backup/mysql.json Normal file
View File

@ -0,0 +1,91 @@
{
"limits": {
"cluster": {
"limits": "^[357]$",
"limits_message": "主机数量必须为3、5或者7"
},
"standalone": {
"limits": "^[1-9][0-9]*$$",
"limits_message": "主机数量必须大于等于1"
}
},
"deploy": {
"standalone": {
"icon": "mysql",
"depend": "^task[.]resource[.]cvm[.][a-z]{1,20}$",
"limits": "^[1-9][0-9]*$",
"limits_message": "主机数量必须大于等于1"
},
"cluster": {
"icon": "mysql-cluster",
"msv": [{
"role": "mysql_master",
"name": "主节点(1)",
"code": "task.software.mysql",
"depend": "^task[.]resource[.]cvm[.][a-z]{1,20}$",
"limits": "^1$",
"limits_message": "主节点主机数量必须为1"
},
{
"role": "mysql_slave",
"name": "从节点(1)",
"code": "task.software.mysql",
"depend": "^task[.]resource[.]cvm[.][a-z]{1,20}$",
"limits": "^1$",
"limits_message": "从节点主机数量必须为1"
}
],
"msb": [{
"role": "mysql_master",
"name": "主节点(1)",
"code": "task.software.mysql",
"depend": "^task[.]resource[.]cvm[.][a-z]{1,20}$",
"limits": "^1$",
"limits_message": "主节点主机数量必须为1"
},
{
"role": "mysql_slave",
"name": "从节点(1)",
"code": "task.software.mysql",
"depend": "^task[.]resource[.]cvm[.][a-z]{1,20}$",
"limits": "^1$",
"limits_message": "从节点主机数量必须为1"
}
],
"mha": [{
"role": "mysql_master",
"name": "主节点(1)",
"code": "task.software.mysql",
"depend": "^task[.]resource[.]cvm[.][a-z]{1,20}$",
"limits": "^1$",
"limits_message": "主机连接数量必须为1"
},
{
"role": "mysql_slave",
"name": "从节点(2)",
"code": "task.software.mysql",
"depend": "^task[.]resource[.]cvm[.][a-z]{1,20}$",
"limits": "^2$",
"limits_message": "从节点主机连接数量必须为2"
}
],
"mgr.single": [{
"role": "mysql_master",
"name": "主节点(1)",
"code": "task.software.mysql",
"depend": "^task[.]resource[.]cvm[.][a-z]{1,20}$",
"limits": "^1$",
"limits_message": "主机连接数量必须为1"
},
{
"role": "mysql_slave",
"name": "从节点(2)",
"code": "task.software.mysql",
"depend": "^task[.]resource[.]cvm[.][a-z]{1,20}$",
"limits": "^2$",
"limits_message": "从节点主机连接数量必须为2"
}
]
}
}
}

78
backup/redis.json Normal file
View File

@ -0,0 +1,78 @@
{
"limits": {
"standalone": {
"limits": "^[1-9][0-9]*$$",
"limits_message": "主机数量必须大于等于1"
}
},
"deploy": {
"standalone": {
"icon": "redis",
"depend": "^task[.]resource[.]cvm[.][a-z]{1,20}$",
"limits": "^[1-9][0-9]*$",
"limits_message": "主机数量必须大于等于1"
},
"cluster": {
"icon": "redis-cluster",
"omts": [{
"role": "redis_master",
"name": "主节点(1)",
"code": "task.software.redis",
"depend": "^task[.]resource[.]cvm[.][a-z]{1,20}$",
"limits": "^1$",
"limits_message": "主节点主机数量必须为1"
},
{
"role": "redis_slave",
"name": "从节点(2)",
"code": "task.software.redis",
"depend": "^task[.]resource[.]cvm[.][a-z]{1,20}$",
"limits": "^2$",
"limits_message": "从节点主机数量必须为2"
}
],
"omtsts": [{
"role": "redis_master",
"name": "主节点(1)",
"code": "task.software.redis",
"depend": "^task[.]resource[.]cvm[.][a-z]{1,20}$",
"limits": "^1$",
"limits_message": "主节点主机数量必须为1"
},
{
"role": "redis_slave",
"name": "从节点(2)",
"code": "task.software.redis",
"depend": "^task[.]resource[.]cvm[.][a-z]{1,20}$",
"limits": "^2$",
"limits_message": "从节点主机数量必须为2"
},
{
"role": "redis_sentinel",
"name": "哨兵节点(3)",
"code": "task.software.redis",
"depend": "^task[.]resource[.]cvm[.][a-z]{1,20}$",
"limits": "^3$",
"limits_message": "哨兵节点主机数量必须为3"
}
],
"tmts": [{
"role": "redis_master",
"name": "主节点(3)",
"code": "task.software.redis",
"depend": "^task[.]resource[.]cvm[.][a-z]{1,20}$",
"limits": "^3$",
"limits_message": "主机连接数量必须为3"
},
{
"role": "redis_slave",
"name": "从节点(3)",
"code": "task.software.redis",
"depend": "^task[.]resource[.]cvm[.][a-z]{1,20}$",
"limits": "^3$",
"limits_message": "从节点主机连接数量必须为3"
}
]
}
}
}

25
backup/test.json Normal file
View File

@ -0,0 +1,25 @@
{
"deploy": {
"default": "cluster",
"cluster": {
"icon": "iscsi-cluster",
"topo": [{
"role": "iscsi_servers",
"name": "Server(1)",
"code": "task.software.iscsi",
"depend": "^task[.]resource[.]cvm[.][a-z]{1,20}$",
"limits": "^1$",
"limits_message": "Server主机数量必须为1"
},
{
"role": "iscsi_client",
"name": "Client(2)",
"code": "task.software.iscsi",
"depend": "^task[.]resource[.]cvm[.][a-z]{1,20}$",
"limits": "^2$",
"limits_message": "Client主机数量必须为2"
}
]
}
}
}

4
commitlint.config.js Normal file
View File

@ -0,0 +1,4 @@
module.exports = {
extends: ['@commitlint/config-conventional']
};

45
components.d.ts vendored Normal file
View File

@ -0,0 +1,45 @@
// generated by unplugin-vue-components
// We suggest you to commit this file into source control
// Read more: https://github.com/vuejs/vue-next/pull/3399
declare module 'vue' {
export interface GlobalComponents {
ElAlert: typeof import('element-ui/lib/alert')['default']
ElBadge: typeof import('element-ui/lib/badge')['default']
ElButton: typeof import('element-ui/lib/button')['default']
ElCard: typeof import('element-ui/lib/card')['default']
ElCascader: typeof import('element-ui/lib/cascader')['default']
ElCol: typeof import('element-ui/lib/col')['default']
ElDatePicker: typeof import('element-ui/lib/date-picker')['default']
ElDialog: typeof import('element-ui/lib/dialog')['default']
ElDivider: typeof import('element-ui/lib/divider')['default']
ElDropdown: typeof import('element-ui/lib/dropdown')['default']
ElDropdownItem: typeof import('element-ui/lib/dropdown-item')['default']
ElDropdownMenu: typeof import('element-ui/lib/dropdown-menu')['default']
ElForm: typeof import('element-ui/lib/form')['default']
ElFormItem: typeof import('element-ui/lib/form-item')['default']
ElInput: typeof import('element-ui/lib/input')['default']
ElOption: typeof import('element-ui/lib/option')['default']
ElPagination: typeof import('element-ui/lib/pagination')['default']
ElProgress: typeof import('element-ui/lib/progress')['default']
ElRadio: typeof import('element-ui/lib/radio')['default']
ElRadioButton: typeof import('element-ui/lib/radio-button')['default']
ElRadioGroup: typeof import('element-ui/lib/radio-group')['default']
ElRow: typeof import('element-ui/lib/row')['default']
ElScrollbar: typeof import('element-ui/lib/scrollbar')['default']
ElSelect: typeof import('element-ui/lib/select')['default']
ElStep: typeof import('element-ui/lib/step')['default']
ElSteps: typeof import('element-ui/lib/steps')['default']
ElSwitch: typeof import('element-ui/lib/switch')['default']
ElTable: typeof import('element-ui/lib/table')['default']
ElTableColumn: typeof import('element-ui/lib/table-column')['default']
ElTabPane: typeof import('element-ui/lib/tab-pane')['default']
ElTabs: typeof import('element-ui/lib/tabs')['default']
ElTag: typeof import('element-ui/lib/tag')['default']
ElTooltip: typeof import('element-ui/lib/tooltip')['default']
ElTransfer: typeof import('element-ui/lib/transfer')['default']
ElUpload: typeof import('element-ui/lib/upload')['default']
}
}
export { }

103
package.json Normal file
View File

@ -0,0 +1,103 @@
{
"name": "cop-web",
"version": "5.6.0",
"private": true,
"author": "Haijun Zhang <zhanghaijun@beyondcent.com>",
"scripts": {
"dev": "vite",
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint",
"fix": "vue-cli-service lint --fix",
"fix-memory-limit": "cross-env LIMIT=4096 increase-memory-limit",
"precommit": "lint-staged",
"upload": "node upload.js",
"deploy": "yarn build && yarn upload"
},
"dependencies": {
"@vue/composition-api": "^1.0.0-rc.2",
"axios": "^0.21.1",
"clipboard": "^2.0.6",
"cmp-echarts": "2.0.0-5.6-release",
"cmp-element": "1.0.0-5.6-release",
"cmp-graph-editor": "1.0.0-5.6-release",
"cmp-socket": "1.0.0",
"core-js": "^3.3.2",
"crypto-js": "^3.1.9-1",
"dayjs": "^1.10.4",
"element-ui": "2.13.0",
"jquery": "^3.6.0",
"js-cookie": "^2.2.0",
"lodash-es": "^4.17.21",
"mergely": "4.0.15",
"nprogress": "^0.2.0",
"qs": "^6.7.0",
"sortablejs": "^1.8.4",
"vue": "2.6.14",
"vue-class-component": "^7.0.2",
"vue-code-diff": "^0.0.4",
"vue-i18n": "^8.15.0",
"vue-property-decorator": "^8.3.0",
"vue-router": "^3.3.4",
"vue2-animate": "^1.0.4",
"vuex": "^3.0.1"
},
"devDependencies": {
"@commitlint/cli": "^11.0.0",
"@commitlint/config-conventional": "^11.0.0",
"@types/js-cookie": "^2.2.4",
"@types/lodash-es": "^4.17.5",
"@types/nprogress": "^0.2.0",
"@types/qs": "^6.5.3",
"@typescript-eslint/eslint-plugin": "^2.18.0",
"@typescript-eslint/parser": "^2.18.0",
"@vue/cli-plugin-babel": "~4.4.1",
"@vue/cli-plugin-eslint": "~4.4.1",
"@vue/cli-plugin-router": "~4.4.1",
"@vue/cli-plugin-typescript": "~4.4.1",
"@vue/cli-plugin-vuex": "~4.4.1",
"@vue/cli-service": "~4.4.1",
"@vue/eslint-config-standard": "^5.1.0",
"@vue/eslint-config-typescript": "^5.0.1",
"autoprefixer": "9.8.6",
"babel-plugin-transform-vite-meta-glob": "^1.0.3",
"chalk": "^4.1.1",
"compress-webpack-plugin": "^1.0.6",
"cross-env": "^5.2.0",
"eslint": "^6.7.2",
"eslint-plugin-import": "^2.20.1",
"eslint-plugin-node": "^11.0.0",
"eslint-plugin-promise": "^4.2.1",
"eslint-plugin-standard": "^4.0.0",
"eslint-plugin-vue": "^6.1.2",
"husky": "^1.3.1",
"increase-memory-limit": "^1.0.6",
"lint-staged": "^8.1.5",
"ora": "^5.4.0",
"sass": "^1.19.0",
"sass-loader": "^8.0.0",
"svg-sprite-loader": "^6.0.2",
"typescript": "^4.4.4",
"unplugin-auto-import": "^0.6.1",
"unplugin-vue-components": "^0.17.21",
"vue-template-compiler": "2.6.14",
"webpack-cli": "^4.9.2"
},
"eslintConfig": {
"parserOptions": {
"parser": "@typescript-eslint/parser"
}
},
"lint-staged": {
"src/**/*.{js,vue}": [
"eslint --fix",
"git add"
]
},
"husky": {
"hooks": {
"pre-commit": "lint-staged",
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
}
}
}

11220
pnpm-lock.yaml Normal file

File diff suppressed because it is too large Load Diff

5
postcss.config.js Normal file
View File

@ -0,0 +1,5 @@
module.exports = {
plugins: {
autoprefixer: {}
}
}

31
public/index.html Normal file
View File

@ -0,0 +1,31 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="renderer" content="webkit">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<title>多云集中纳管系统</title>
<link rel="stylesheet" href="/web-common-resource/css/loading.css">
</head>
<body id="cmp">
<noscript>
<strong>We're sorry but cmc-web3.0 doesn't work properly without JavaScript enabled. Please enable it to
continue.</strong>
</noscript>
<div class="loading">
<div class="loader-inner line-scale-pulse-out-rapid">
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</div>
</div>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>

12
script/index.js Normal file
View File

@ -0,0 +1,12 @@
const exec = require('child_process').exec;
const params = process.argv[2];
const str = `git subtree ${params || 'pull'} --prefix=src/common http://haijun@58.210.154.140:8888/web/cmp-common.git master --squash`
exec(str, function (err, stdout, stderr) {
if (err) {
console.log('======执行失败=======');
console.log(err)
} else {
console.log('======执行成功=======');
console.log(stdout);
}
});

48
src/App.vue Normal file
View File

@ -0,0 +1,48 @@
<template>
<div id="app" @click="setTime">
<router-view></router-view>
</div>
</template>
<script>
import { onMounted, ref } from '@vue/composition-api';
export default {
setup(props, context) {
const dialogVisible = ref(false);
context.root.$store.dispatch('GetPageConfigs');
onMounted(() => {
// 退 hack
window.addEventListener('hashchange', () => {
const currentPath = window.location.hash.slice(1);
if (context.root.$route.path !== currentPath) {
context.root.$router.push(currentPath)
}
}, false);
// loading
setTimeout(() => {
const el = document.getElementsByClassName('loading')[0];
if (el && el.parentElement) {
el.parentElement.removeChild(el)
}
}, 1000 * 2)
});
function judgeAgent () {
if (window.navigator.userAgent.indexOf('MSIE') > 0) {
dialogVisible.value = true
}
}
function setTime() {
context.root.$store.commit('SET_OPERATETIME')
}
judgeAgent()
return {
dialogVisible,
setTime
}
}
}
</script>
<style lang="scss">
@import './common/css/index.scss';
</style>

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
src/assets/create_task.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

BIN
src/assets/execute_task.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

BIN
src/assets/result_task.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
src/assets/role.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

BIN
src/assets/rule.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

BIN
src/assets/tpl_task.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

53
src/common/commonData.js Normal file
View File

@ -0,0 +1,53 @@
/**
* Created by Zhang Haijun on 2017/8/31.
*/
export const taskExeOptions = {
RUNNING: ' 正在执行',
SUCCESS: '执行成功',
FAILED: '执行失败',
EXCEPTION: ' 执行异常',
CANCELED: '手动结束',
SUSPENDED: '已暂停'
}
export const authenModeOptions = {
BATCH: '批量认证',
TRUST: '互信认证',
SINGLE: '单一认证'
}
export const environmentData = {
DEVELOP: '开发环境',
TEST: '测试环境',
PRODUCTION: '生产环境',
READY: '预发环境',
UAT: 'UAT环境'
}
export const networkType = {
0: '容器镜像',
1: '虚拟机镜像',
2: 'rpm安装',
3: '应用程序包',
4: '其他'
}
export const httpMethod = ['GET', 'POST', 'PUT', 'DELETE']
export const httpFormat = ['RAW', 'FORM_DATA', 'FORM_URLENCODED', 'BINARY']
export const scriptTypeData = [
{ name: 'shell', value: 'SHELL' },
{ name: 'bat', value: 'BAT' },
{ name: 'perl', value: 'PERL' },
{ name: 'python', value: 'PYTHON' },
{ name: 'playbook', value: 'PLAYBOOK' },
{ name: 'powershell', value: 'POWERSHELL' }
]
export const hostType = {
MAINFRAME: '大型机',
MINICOMPUTER: '小型机',
PC: 'PC',
X86: 'X86服务器'
}
export const funData = [{ name: 'mean' }, { name: 'median' }, { name: 'count' }, { name: 'min' }, { name: 'max' }, { name: 'sum' }, { name: 'first' }, { name: 'last' }, { name: 'spread' }, { name: 'stddev' }]
export const processLevel = [
{ name: '一般', value: 'COMMON' },
{ name: '紧急', value: 'EMERGENCY' }
]

View File

@ -0,0 +1,118 @@
<template>
<div>
<el-radio-group v-model="params.time" @change="selectTime">
<el-radio-button v-for="item in timeList" :key="item.value" :label="item.value">{{ item.label }}</el-radio-button>
</el-radio-group>
<el-button class="m-l-sm" :type="params.startTime ? 'primary' : 'ghost'" @click="selectTime()"></el-button>
<span class="tip m-l" v-if="params.startTime">{{ params.startTime }} - {{ params.endTime }}</span>
<el-dialog title="时间选择" :visible.sync="dialogVisible" width="500px" v-if="dialogVisible">
<el-date-picker v-model="time" value-format="yyyy-MM-dd HH:mm:ss" type="datetimerange" :picker-options="pickerOptions" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" align="right"> </el-date-picker>
<div slot="footer" class="dialog-footer">
<el-button @click.native="dialogVisible = false">取消</el-button>
<el-button type="primary" @click.native="submit">确定</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
export default {
props: {
timeList: {
type: Array,
default: function () {
return [
{ label: '天', value: 'Days' },
{ label: '周', value: 'Weeks' },
{ label: '月', value: 'Months' },
{ label: '季度', value: 'QuarterYears' },
{ label: '半年', value: 'HalfYears' },
{ label: '一年', value: 'Years' }
]
}
},
getData: {
type: Function
},
defaultTime: {
type: String,
default: 'Months'
}
},
data() {
return {
pickerOptions: {
shortcuts: [
{
text: '最近一周',
onClick(picker) {
const end = new Date()
const start = new Date()
start.setTime(start.getTime() - 3600 * 1000 * 24 * 7)
picker.$emit('pick', [start, end])
}
},
{
text: '最近一个月',
onClick(picker) {
const end = new Date()
const start = new Date()
start.setTime(start.getTime() - 3600 * 1000 * 24 * 30)
picker.$emit('pick', [start, end])
}
},
{
text: '最近三个月',
onClick(picker) {
const end = new Date()
const start = new Date()
start.setTime(start.getTime() - 3600 * 1000 * 24 * 90)
picker.$emit('pick', [start, end])
}
}
],
disabledDate(time) {
return time.getTime() > new Date().getTime()
}
},
dialogVisible: false,
time: '',
params: {
time: '',
startTime: '',
endTime: ''
}
}
},
created() {
this.params.time = this.defaultTime
},
methods: {
selectTime(value) {
//
if (!value) {
this.dialogVisible = true
this.dialogVisible = true
return
}
this.time = ''
this.params.startTime = ''
this.params.endTime = ''
this.getData(this.params)
},
submit() {
if (!this.time) return this.$message.error('请选择时间范围')
const [startTime, endTime] = this.time
if (new Date(endTime).getTime() - new Date(startTime) < 1000 * 60 * 60) {
return this.$message.error('时间间隔必须大于一小时')
}
this.params.time = ''
this.params.startTime = startTime
this.params.endTime = endTime
this.dialogVisible = false
this.getData(this.params)
}
}
}
</script>
<style scoped lang="scss">
</style>

View File

@ -0,0 +1,72 @@
<template>
<el-input v-bind="$attrs"></el-input>
</template>
<script>
import { computed } from '@vue/composition-api'
const colorMap = {
normal: {
bg: '#2E8CF0',
bc: '#CBE3FB'
},
primary: {
bg: '#5D59B4',
bc: '#B3B0B4'
},
success: {
bg: '#54C54E',
bc: '#D5F1D3'
},
warning: {
bg: '#FF9900',
bc: '#D8EEDD'
},
danger: {
bg: '#DC1A1A',
bc: '#F6C6C6'
},
disabled: {
bg: '#808490',
bc: '#E0E1E4'
}
}
export default {
props: {
type: {
type: String,
default: 'normal'
},
bgColor: {
type: String
},
borderColor: {
type: String
}
},
setup (props) {
const colorObj = computed(() => (colorMap[props.type] || {}))
return { colorObj }
}
}
</script>
<style lang="scss" scoped>
.icon-wrapper {
display: flex;
align-items: center;
.icon {
display: inline-block;
width: 8px;
height: 8px;
border-radius: 8px;
background: #2e8cf0;
border: 4px solid #cbe3fb;
}
.icon-text {
margin: 3px 0 0 5px;
display: inline-block;
width: calc(100% - 21px);
overflow: hidden;
text-overflow: ellipsis;
}
}
</style>

View File

@ -0,0 +1,5 @@
import BasicInput from './BasicInput.vue'
BasicInput.install = function (Vue) {
Vue.component('BasicInput', BasicInput)
}
export default BasicInput

View File

@ -0,0 +1,191 @@
<template>
<el-row :gutter="10">
<el-col :span="12">
<el-card>
<div slot="header">
<span>{{titles[0]}}</span>
<el-button type="primary" class="pull-right" style="margin-top: 5px;" size="mini" @click="addItems">
<i class="el-icon-right"></i>
</el-button>
</div>
<div style="height:360px;overflow-y: auto;">
<el-row v-for="(item, key) in source" :key="key">
<el-col :span="24" class="cell-title">{{prefix}}{{key}}</el-col>
<el-col class="cell" :span="24" v-for="(cell, key) in item" :key="key">
<el-checkbox v-model="cell.checked">{{cell[label]}}</el-checkbox>
</el-col>
</el-row>
</div>
</el-card>
</el-col>
<el-col :span="12">
<el-card>
<div slot="header">
<span class="pull-right">{{titles[1]}}</span>
<el-button type="danger" size="mini" @click="removeItems">
<i class="el-icon-back"></i>
移除
</el-button>
</div>
<div style="height:360px;overflow-y: auto;">
<el-row v-for="(item, key) in target" :key="key">
<el-col :span="24" class="cell-title">{{prefix}}{{key}}</el-col>
<el-col class="cell" :span="24" v-for="(cell, key) in item" :key="`${key}${item.name}`">
<el-checkbox v-model="cell.checked">{{cell[label]}}</el-checkbox>
</el-col>
</el-row>
</div>
</el-card>
</el-col>
</el-row>
</template>
<script>
export default {
model: {
prop: 'checkedIds',
event: 'change'
},
props: {
titles: {
type: Array,
default: function () {
return ['列表1', '列表2']
}
},
label: {
type: String,
default: 'name'
},
value: {
type: String,
default: 'id'
},
data: {
type: Array,
required: true
},
groupKey: {
type: String,
default: 'group'
},
prefix: {
type: String,
default: ''
},
checkedIds: {
type: Array,
default: function () {
return []
}
}
},
data () {
return {
selectList: [],
sourceList: []
}
},
created () {
this.init()
},
watch: {
data: {
handler (newVal, oldVal) {
this.selectList = [];
this.sourceList = [];
this.init()
}
}
},
computed: {
source () {
return this.formatData(this.sourceList)
},
target () {
return this.formatData(this.selectList)
}
},
methods: {
init () {
[...this.data].forEach((item, key) => {
const result = {
...item,
checked: false,
orderKey: key
}
if (this.checkedIds.includes(item[this.value])) {
this.selectList.push(result)
} else {
this.sourceList.push(result)
}
})
},
formatData (data) {
const source = {}
data.forEach(item => {
const key = item[this.groupKey]
if (source[key]) {
source[key].push(item)
} else {
source[key] = [item]
}
})
return source
},
addItems () {
const list = [];
this.sourceList.forEach(item => {
if (item.checked) {
this.selectList.push({
...item,
checked: false
})
} else {
list.push(item)
}
})
this.sourceList = [...list].sort((a, b) => {
return a.orderKey - b.orderKey
});
this.getChekckIds()
},
removeItems () {
const list = [];
this.selectList.forEach(item => {
if (item.checked) {
this.sourceList.push({
...item,
checked: false
})
} else {
list.push(item)
}
})
this.selectList = [...list];
this.getChekckIds()
},
getChekckIds () {
const checkedIds = this.selectList.map(item => item[this.value])
this.$emit('change', checkedIds)
}
}
}
</script>
<style scoped>
.cell-title{
font-size: 12px;
font-weight: bold;
}
.cell{
padding: 5px;
color: #606266;
font-weight: 500;
white-space: nowrap;
font-size: 14px;
overflow: hidden;
text-overflow: ellipsis;
}
</style>

View File

@ -0,0 +1,3 @@
const elementIcons = ['platform-eleme', 'eleme', 'delete-solid', 'delete', 's-tools', 'setting', 'user-solid', 'user', 'phone', 'phone-outline', 'more', 'more-outline', 'star-on', 'star-off', 's-goods', 'goods', 'warning', 'warning-outline', 'question', 'info', 'remove', 'circle-plus', 'success', 'error', 'zoom-in', 'zoom-out', 'remove-outline', 'circle-plus-outline', 'circle-check', 'circle-close', 's-help', 'help', 'minus', 'plus', 'check', 'close', 'picture', 'picture-outline', 'picture-outline-round', 'upload', 'upload2', 'download', 'camera-solid', 'camera', 'video-camera-solid', 'video-camera', 'message-solid', 'bell', 's-cooperation', 's-order', 's-platform', 's-fold', 's-unfold', 's-operation', 's-promotion', 's-home', 's-release', 's-ticket', 's-management', 's-open', 's-shop', 's-marketing', 's-flag', 's-comment', 's-finance', 's-claim', 's-custom', 's-opportunity', 's-data', 's-check', 's-grid', 'menu', 'share', 'd-caret', 'caret-left', 'caret-right', 'caret-bottom', 'caret-top', 'bottom-left', 'bottom-right', 'back', 'right', 'bottom', 'top', 'top-left', 'top-right', 'arrow-left', 'arrow-right', 'arrow-down', 'arrow-up', 'd-arrow-left', 'd-arrow-right', 'video-pause', 'video-play', 'refresh', 'refresh-right', 'refresh-left', 'finished', 'sort', 'sort-up', 'sort-down', 'rank', 'view', 'c-scale-to-original', 'date', 'edit', 'edit-outline', 'folder', 'folder-opened', 'folder-add', 'folder-remove', 'folder-delete', 'folder-checked', 'tickets', 'document-remove', 'document-delete', 'document-copy', 'document-checked', 'document', 'document-add', 'printer', 'paperclip', 'takeaway-box', 'search', 'monitor', 'attract', 'mobile', 'scissors', 'umbrella', 'headset', 'brush', 'mouse', 'coordinate', 'magic-stick', 'reading', 'data-line', 'data-board', 'pie-chart', 'data-analysis', 'collection-tag', 'film', 'suitcase', 'suitcase-1', 'receiving', 'collection', 'files', 'notebook-1', 'notebook-2', 'toilet-paper', 'office-building', 'school', 'table-lamp', 'house', 'no-smoking', 'smoking', 'shopping-cart-full', 'shopping-cart-1', 'shopping-cart-2', 'shopping-bag-1', 'shopping-bag-2', 'sold-out', 'sell', 'present', 'box', 'bank-card', 'money', 'coin', 'wallet', 'discount', 'price-tag', 'news', 'guide', 'male', 'female', 'thumb', 'cpu', 'link', 'connection', 'open', 'turn-off', 'set-up', 'chat-round', 'chat-line-round', 'chat-square', 'chat-dot-round', 'chat-dot-square', 'chat-line-square', 'message', 'postcard', 'position', 'turn-off-microphone', 'microphone', 'close-notification', 'bangzhu', 'time', 'odometer', 'crop', 'aim', 'switch-button', 'full-screen', 'copy-document', 'mic', 'stopwatch', 'medal-1', 'medal', 'trophy', 'trophy-1', 'first-aid-kit', 'discover', 'place', 'location', 'location-outline', 'location-information', 'add-location', 'delete-location', 'map-location', 'alarm-clock', 'timer', 'watch-1', 'watch', 'lock', 'unlock', 'key', 'service', 'mobile-phone', 'bicycle', 'truck', 'ship', 'basketball', 'football', 'soccer', 'baseball', 'wind-power', 'light-rain', 'lightning', 'heavy-rain', 'sunrise', 'sunrise-1', 'sunset', 'sunny', 'cloudy', 'partly-cloudy', 'cloudy-and-sunny', 'moon', 'moon-night', 'dish', 'dish-1', 'food', 'chicken', 'fork-spoon', 'knife-fork', 'burger', 'tableware', 'sugar', 'dessert', 'ice-cream', 'hot-water', 'water-cup', 'coffee-cup', 'cold-drink', 'goblet', 'goblet-full', 'goblet-square', 'goblet-square-full', 'refrigerator', 'grape', 'watermelon', 'cherry', 'apple', 'pear', 'orange', 'coffee', 'ice-tea', 'ice-drink', 'milk-tea', 'potato-strips', 'lollipop', 'ice-cream-square', 'ice-cream-round']
export default elementIcons

View File

@ -0,0 +1,35 @@
<template>
<el-select clearable :value="iconName" placeholder="请选择图标" filterable="" @change="change">
<el-option v-for="item in iconData" :key="item" :label="item" :value="item">
<svg-icon :icon-name="item"></svg-icon>
{{ item }}
</el-option>
</el-select>
</template>
<script lang="ts">
import elementIcons from './elementIcons'
import svgIcons from './svgIcons'
import { defineComponent } from '@vue/composition-api'
export default defineComponent({
model: {
prop: 'iconName',
event: 'change'
},
props: {
iconName: {
required: true
}
},
setup(props, context) {
const iconData = [...svgIcons, ...elementIcons.map(item => `el-icon-${item}`)]
const change = (val: string) => {
context.emit('change', val)
}
return {
iconData,
change
}
}
})
</script>

View File

@ -0,0 +1,18 @@
// webpack
// const req = require.context('@/icons/svg', false, /\.svg$/)
// const requireAll = requireContext => requireContext.keys()
// const re = /\.\/(.*)\.svg/;
// const svgIcons = requireAll(req).map(i => {
// return i.match(re)[1]
// })
// vite
// const req = import.meta.globEager('/src/icons/svg/*.svg')
// const re = /\/svg\/(.*)\.svg/
// const svgIcons = Object.keys(req).map(i => {
// return i.match(re)[1]
// })
export default ['svg-about', 'svg-basic-resource', 'svg-bill', 'svg-business', 'svg-compute-resource', 'svg-db', 'svg-disabled', 'svg-dot', 'svg-middle', 'svg-network-resource', 'svg-operate', 'svg-ops-analysis', 'svg-order', 'svg-permission', 'svg-port', 'svg-product', 'svg-repository', 'svg-resource-interface', 'svg-resource-manage', 'svg-resource-monitor', 'svg-resource-ops', 'svg-screen', 'svg-security', 'svg-service-ops', 'svg-setting', 'svg-storage-resource', 'svg-task-platform']

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,87 @@
<template>
<span>
<el-button class="m-l-sm m-r-sm" @click.stop="openDialog" icon="el-icon-upload2">导入 </el-button>
<el-dialog title="导入Excel新增数据" :close-on-click-modal="false" :visible.sync="dialogVisible" width="480px" append-to-body>
<el-row>
<el-col :span="24">
<el-alert title="" type="warning" :closable="false">
<template slot="">
<div class="text-center">
<p>您是否有标准的Excel模版需要依照模版导入否则会失败</p>
<a class="text-info cur-point" @click="exportData()">Excel?</a>
</div>
</template>
</el-alert>
</el-col>
<el-col :span="24" class="text-center m-t">
<el-upload ref="uploadRef" class="upload-demo" drag accept=".xlsx" :on-success="handleSuccess" :action="url" :headers="headers" :data="params">
<i class="el-icon-upload"></i>
<div class="el-upload__text">将文件拖到此处<em>点击上传</em></div>
<div class="el-upload__tip" slot="tip">只能上传excel文件</div>
</el-upload>
</el-col>
</el-row>
<div slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false">关闭</el-button>
</div>
</el-dialog>
</span>
</template>
<script lang="ts">
import { downloadFile } from 'utils/index'
import { getToken } from 'utils/auth'
import { reactive, toRefs, ref, defineComponent } from '@vue/composition-api'
import { Message } from 'element-ui'
export default defineComponent({
props: {
url: {
type: String,
required: true
},
templateUrl: {
type: String,
required: true
},
params: {
type: Object,
default: function() {
return {}
}
}
},
setup(props: any, context: any) {
const state = reactive({
dialogVisible: false,
headers: { token: getToken() }
})
const uploadRef = ref(null)
function openDialog() {
uploadRef.value && (uploadRef.value as any).clearFiles()
state.dialogVisible = true
state.headers.token = getToken()
}
//
function exportData() {
downloadFile(props.templateUrl, props.params)
}
//
function handleSuccess(res: Base.IResponseData) {
if (res.success) {
Message.success(res.message)
state.dialogVisible = false
context.emit('getData')
} else {
Message.error(res.message)
}
}
return {
...toRefs(state),
uploadRef,
openDialog,
exportData,
handleSuccess
}
}
})
</script>

View File

@ -0,0 +1,14 @@
import Vue from 'vue'
import TimeSelect from './TimeSelect.vue'
import RichCharts from './rich-chart/index.vue'
import SkuTable from './sku-table/index.vue'
import WsUploadFile from './upload-file/index.vue'
const components = {
TimeSelect,
RichCharts,
SkuTable,
WsUploadFile
}
Object.keys(components).forEach(key => {
Vue.component(key, components[key]);
})

View File

@ -0,0 +1,138 @@
<template>
<el-card class="chart-card">
<div slot="header" class="chart-header">
<div>{{title}}</div>
<div class="operate">
<el-radio-group v-model="chartType" size="mini" class="m-r-sm">
<template v-if="defaultChart === 'loop-charts'">
<el-radio-button label="loop-charts">环状</el-radio-button>
<el-radio-button label="bar-charts">柱状</el-radio-button>
</template>
<template v-else>
<el-radio-button label="line-charts">折线</el-radio-button>
<el-radio-button label="bar-charts">柱状</el-radio-button>
</template>
</el-radio-group>
<el-button type="ghost" class="operate-btn" @click="enlarge"></el-button>
<!-- <el-button type="ghost" class="operate-btn" @click="download"></el-button> -->
</div>
</div>
<slot></slot>
<component ref="charts" :is="chartMap[chartType]" :setting="chartSetting" :data="getResData()" v-if="data" :theme="title" :id="chartId" :height="height" width="100%"></component>
<el-dialog :title="title" :visible.sync="dialogVisible" v-if="dialogVisible" fullscreen class="chart-dialog">
<component :is="chartMap[chartType]" :setting="chartSetting" :data="getResData()" :theme="title" :id="`${chartId}dialog`" height="100%" width="100%"></component>
</el-dialog>
</el-card>
</template>
<script>
import { downloadFile } from './tools'
export default {
props: {
title: {
type: String
},
defaultChart: {
type: String,
default: 'loop-charts'
},
data: {
type: [Object, Array]
},
setting: {
type: Object,
default: function () {
return {}
}
},
height: {
type: String,
default: '260px'
},
//
downloadOpt: {
type: Object,
default: function () {
return {}
}
}
},
data () {
return {
chartId: Math.random().toString(),
chartType: '',
dialogVisible: false,
chartMap: {
'pie-charts': 'pie-charts',
'bar-charts': 'bar-charts',
'line-charts': 'line-charts',
'loop-charts': 'pie-charts'
}
}
},
computed: {
chartSetting () {
const setting = {}
if (this.chartType === 'pie-charts') setting.radius = '75%'
return {
...this.setting,
...setting
};
}
},
created () {
this.chartType = this.defaultChart;
},
methods: {
//
getResData() {
if (this.defaultChart === 'loop-charts' && this.chartType === 'bar-charts') {
const keys = [];
const values = [{
name: '统计数据',
data: []
}]
this.data.forEach(item => {
const { name, value } = item;
keys.push(name);
values[0].data.push(value);
});
return {
keys,
values
}
}
return this.data
},
enlarge () {
this.dialogVisible = true;
},
download () {
const image = this.$refs.charts.chart.getDataURL(this.downloadOpt);
downloadFile(this.downloadOpt.name || this.title, image)
}
}
}
</script>
<style scoped lang="scss">
.chart-card {
.chart-header {
display: flex;
align-items: center;
}
.operate {
flex: 1;
text-align: right;
.operate-btn {
padding: 7px 15px;
}
}
::v-deep .el-card__body {
padding: 10px !important;
}
.chart-dialog {
::v-deep .el-dialog__body {
height: calc(100vh - 130px);
}
}
}
</style>

View File

@ -0,0 +1,25 @@
function base64ToBlob(code) {
const parts = code.split(';base64,')
const contentType = parts[0].split(':')[1]
const raw = window.atob(parts[1])
const rawLength = raw.length
const uInt8Array = new Uint8Array(rawLength)
for (let i = 0; i < rawLength; ++i) {
uInt8Array[i] = raw.charCodeAt(i)
}
return new Blob([uInt8Array], { type: contentType })
}
export const downloadFile = (fileName, content) => {
const aLink = document.createElement('a')
const blob = base64ToBlob(content) // new Blob([content]);
const evt = document.createEvent('HTMLEvents')
evt.initEvent('click', true, true) // initEvent 不加后两个参数在FF下会报错 事件类型,是否冒泡,是否阻止浏览器的默认行为
aLink.download = fileName
aLink.href = URL.createObjectURL(blob)
// aLink.dispatchEvent(evt);
aLink.click()
aLink.remove()
}

View File

@ -0,0 +1,46 @@
<template>
<div ref='editor' class="rich-editor"></div>
</template>
<script>
import { onMounted, onBeforeUnmount, ref, watch } from '@vue/composition-api';
import WangEditor from 'wangeditor';
export default {
props: {
value: {
type: String,
default: ''
}
},
setup(props, context) {
watch(() => props.value, () => {
if (instance) {
instance.txt.html(props.value);
}
});
const editor = ref();
let instance;
onMounted(() => {
instance = new WangEditor(editor.value);
Object.assign(instance.config, {
onchange(val) {
context.emit('change', val)
}
});
instance.create();
});
onBeforeUnmount(() => {
instance.destroy();
instance = null;
});
return {
editor
};
}
};
</script>
<style scoped>
.rich-editor{
position: relative;
z-index: 1;
}
</style>

View File

@ -0,0 +1,91 @@
<template>
<basic-table ref="table" :data="specList" v-bind="tableProps">
<slot></slot>
<el-table-column sortable v-for="item in columnProps" :prop="item.value" show-overflow-tooltip :label="item.label" :key="item.value" width="150px">
</el-table-column>
<el-table-column show-overflow-tooltip label="参考价格" v-if="showPrice">
<template v-slot="scope">
{{getPrice(scope.row, mode)}} / {{modeUnitFilter(mode)}}
</template>
</el-table-column>
<slot name="append"></slot>
<div slot="pagination"></div>
</basic-table>
</template>
<script lang="ts">
import { computed, defineComponent } from '@vue/composition-api'
import { getPrice, modeUnitFilter } from './utils'
interface IColumnProps {
label: string,
value: string
}
type IProps = {
skus: any[],
mode: string,
columnProps: IColumnProps[]
}
export default defineComponent({
props: {
skus: {
type: Array,
default: function () {
return []
}
},
mode: {
type: String,
default: 'Hour'
},
showPrice: {
default: false
},
columnProps: {
type: Array,
default: function () {
return [
{
label: 'CPU',
value: 'cpu'
},
{
label: '内存GB',
value: 'memory'
}
]
}
}
},
setup(props:IProps, context:any) {
const specList = computed(() => {
return props.skus.map((item:any) => {
const result:any = {}
item.spec.forEach((cell:any) => {
result[cell.specName] = Number(cell.specValue)
})
return { ...item, ...result }
})
})
//
const tableProps = computed(() => {
const [{ value } = { value: '' }] = props.columnProps;
const prop: any = {
defaultSort: {
prop: value
}
}
const length = props.skus.length
if (length >= 6) {
prop.height = '260'
}
return prop
})
return {
specList,
tableProps,
getPrice,
modeUnitFilter
}
}
})
</script>

View File

@ -0,0 +1,32 @@
export function modeUnitFilter(value:string) {
const map: any = {
Hour: '小时',
Month: '月',
Year: '年'
}
return map[value]
}
export function getPrice(sku:any, mode:string) {
const { spec, billPolicy, billable } = sku;
// 不开启计费
if (!billable) return 0;
const key = `${mode.toLowerCase()}Price`
// 规格计费
if (billPolicy !== 'agility') return sku[key];
const basicPrice = JSON.parse(sku.basicPrice);
// 没有specName说明没有规格设置
const flag = spec.every((item:any) => !item.specName || item.specValue);
// 无值直接返回
if (!flag) return '/';
// 获取数值map
const countMap:any = {};
spec.forEach((item:any) => {
countMap[item.specName] = item.specValue;
})
let totalPrice = 0;
basicPrice.forEach((item:any) => {
const { specName } = item;
totalPrice += item[key] * (countMap[specName] || 1)
});
return totalPrice;
}

View File

@ -0,0 +1,85 @@
<template>
<div class="top-container">
<div class="top-cell" v-for="(item, index) in data" :key="`item.name${index}`">
<div class="cell-icon" :class="`icon-${index}`">{{ index + 1 }}</div>
<el-tooltip :content="item.name">
<div class="cell-title">{{ item.name }}</div>
</el-tooltip>
<div class="cell-progress">
<div :style="{ width: `${item.percent}%` }"></div>
</div>
<div class="cell-number">{{ item.value }}</div>
</div>
</div>
</template>
<script setup>
import { reactive } from '@vue/composition-api'
export default {
props: {
data: {
type: Array,
default: function () {
return []
}
}
}
}
</script>
<style lang="scss" scoped>
.top-container {
display: flex;
flex-direction: column;
justify-content: space-around;
height: 100%;
.top-cell {
display: flex;
align-items: center;
.cell-icon {
width: 16px;
height: 16px;
border-radius: 8px;
background: #cacaca;
text-align: center;
color: #fff;
font-size: 12px;
line-height: 16px;
margin-right: 15px;
&.icon-0 {
background: #e03b3b;
}
&.icon-1 {
background: #f09c2b;
}
&.icon-2 {
background: #4076e2;
}
}
.cell-title {
color: #393b3e;
width: 95px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.cell-progress {
height: 15px;
background: #f2f4f8;
border-radius: 8px;
flex: 1;
margin: 0 12px;
position: relative;
div {
height: 100%;
border-radius: 8px;
position: absolute;
top: 0;
left: 0;
background: #3e74f8;
}
}
.cell-number {
color: #707274;
}
}
}
</style>

View File

@ -0,0 +1,105 @@
<template>
<div>
<el-form-item :label="label" required>
<input :id="id" type="file" name="file" v-if="isShowUploadFile"
style="border: 1px solid #d8dce5;border-radius: 4px;padding: 5px 10px; width: 80%">
<el-button type="primary" v-if="isShowUploadFile" @click="submitUpload(file)"></el-button>
<el-progress :percentage="file.progress" v-if="!isShowUploadFile"></el-progress>
</el-form-item>
</div>
</template>
<script>
import uploadFile from 'utils/uploadFile';
export default {
props: {
fileType: {
type: String
},
id: {
type: String,
default: 'btnFileUpload'
},
label: {
type: String,
default: '文件上传:'
}
},
data () {
return {
file: null,
isShowUploadFile: true,
uploadSuccess: false
};
},
created () {
},
mounted () {
const self = this;
document.getElementById(this.id).addEventListener('change', function (event) {
const files = event.target.files;
const file = files[0];
if (/[\u4e00-\u9fa5\s]/.test(file.name)) {
self.$message({
message: '文件名不允许存在中文和空格',
type: 'error'
});
}
if (file.name.length > 64) {
self.$message({
message: '文件名称过长',
type: 'error'
});
}
self.file = {
file: file,
name: file.name,
isUploading: false,
isCancel: false,
isReady: false,
isSuccess: false,
progress: 0
};
});
},
methods: {
submitUpload (file) {
if (file == undefined || file == null) {
return this.$message.error('请选择上传文件');
}
if (/[\u4e00-\u9fa5\s]/.test(file.name)) {
return this.$message.error('文件名不允许存在中文和空格');
}
if (this.fileType == 'EXCEL') {
const fileName = file.name.split('.');
let flag
if (fileName[fileName.length - 1] === 'xlsx' ||
fileName[fileName.length - 1] === 'xls' ||
fileName[fileName.length - 1] === 'xltx' ||
fileName[fileName.length - 1] === 'xlt' ||
fileName[fileName.length - 1] === 'xlsm' ||
fileName[fileName.length - 1] === 'xlsb' ||
fileName[fileName.length - 1] === 'xltm' ||
fileName[fileName.length - 1] === 'csv'
) {
flag = true
} else {
return this.$message.error('请上传EXCEL表格');
}
}
this.isShowUploadFile = false;
this.$emit('show', this.isShowUploadFile);
uploadFile(file, this.message);
},
message (item) {
this.$message({
message: '上传成功',
type: 'success'
});
this.uploadSuccess = true;
}
}
};
</script>

View File

@ -0,0 +1,220 @@
/* eslint-disable */
import { saveAs } from 'file-saver'
import XLSX from 'xlsx'
function generateArray(table) {
var out = [];
var rows = table.querySelectorAll('tr');
var ranges = [];
for (var R = 0; R < rows.length; ++R) {
var outRow = [];
var row = rows[R];
var columns = row.querySelectorAll('td');
for (var C = 0; C < columns.length; ++C) {
var cell = columns[C];
var colspan = cell.getAttribute('colspan');
var rowspan = cell.getAttribute('rowspan');
var cellValue = cell.innerText;
if (cellValue !== "" && cellValue == +cellValue) cellValue = +cellValue;
//Skip ranges
ranges.forEach(function (range) {
if (R >= range.s.r && R <= range.e.r && outRow.length >= range.s.c && outRow.length <= range.e.c) {
for (var i = 0; i <= range.e.c - range.s.c; ++i) outRow.push(null);
}
});
//Handle Row Span
if (rowspan || colspan) {
rowspan = rowspan || 1;
colspan = colspan || 1;
ranges.push({
s: {
r: R,
c: outRow.length
},
e: {
r: R + rowspan - 1,
c: outRow.length + colspan - 1
}
});
};
//Handle Value
outRow.push(cellValue !== "" ? cellValue : null);
//Handle Colspan
if (colspan)
for (var k = 0; k < colspan - 1; ++k) outRow.push(null);
}
out.push(outRow);
}
return [out, ranges];
};
function datenum(v, date1904) {
if (date1904) v += 1462;
var epoch = Date.parse(v);
return (epoch - new Date(Date.UTC(1899, 11, 30))) / (24 * 60 * 60 * 1000);
}
function sheet_from_array_of_arrays(data, opts) {
var ws = {};
var range = {
s: {
c: 10000000,
r: 10000000
},
e: {
c: 0,
r: 0
}
};
for (var R = 0; R != data.length; ++R) {
for (var C = 0; C != data[R].length; ++C) {
if (range.s.r > R) range.s.r = R;
if (range.s.c > C) range.s.c = C;
if (range.e.r < R) range.e.r = R;
if (range.e.c < C) range.e.c = C;
var cell = {
v: data[R][C]
};
if (cell.v == null) continue;
var cell_ref = XLSX.utils.encode_cell({
c: C,
r: R
});
if (typeof cell.v === 'number') cell.t = 'n';
else if (typeof cell.v === 'boolean') cell.t = 'b';
else if (cell.v instanceof Date) {
cell.t = 'n';
cell.z = XLSX.SSF._table[14];
cell.v = datenum(cell.v);
} else cell.t = 's';
ws[cell_ref] = cell;
}
}
if (range.s.c < 10000000) ws['!ref'] = XLSX.utils.encode_range(range);
return ws;
}
function Workbook() {
if (!(this instanceof Workbook)) return new Workbook();
this.SheetNames = [];
this.Sheets = {};
}
function s2ab(s) {
var buf = new ArrayBuffer(s.length);
var view = new Uint8Array(buf);
for (var i = 0; i != s.length; ++i) view[i] = s.charCodeAt(i) & 0xFF;
return buf;
}
export function export_table_to_excel(id) {
var theTable = document.getElementById(id);
var oo = generateArray(theTable);
var ranges = oo[1];
/* original data */
var data = oo[0];
var ws_name = "SheetJS";
var wb = new Workbook(),
ws = sheet_from_array_of_arrays(data);
/* add ranges to worksheet */
// ws['!cols'] = ['apple', 'banan'];
ws['!merges'] = ranges;
/* add worksheet to workbook */
wb.SheetNames.push(ws_name);
wb.Sheets[ws_name] = ws;
var wbout = XLSX.write(wb, {
bookType: 'xlsx',
bookSST: false,
type: 'binary'
});
saveAs(new Blob([s2ab(wbout)], {
type: "application/octet-stream"
}), "test.xlsx")
}
export function exportJsonToExcel({
multiHeader = [],
header,
data,
filename,
merges = [],
autoWidth = true,
bookType = 'xlsx'
} = {}) {
/* original data */
filename = filename || 'excel-list'
data = [...data]
data.unshift(header);
for (let i = multiHeader.length - 1; i > -1; i--) {
data.unshift(multiHeader[i])
}
var ws_name = "SheetJS";
var wb = new Workbook(),
ws = sheet_from_array_of_arrays(data);
if (merges.length > 0) {
if (!ws['!merges']) ws['!merges'] = [];
merges.forEach(item => {
ws['!merges'].push(XLSX.utils.decode_range(item))
})
}
if (autoWidth) {
/*设置worksheet每列的最大宽度*/
const colWidth = data.map(row => row.map(val => {
/*先判断是否为null/undefined*/
if (val == null) {
return {
'wch': 10
};
}
/*再判断是否为中文*/
else if (val.toString().charCodeAt(0) > 255) {
return {
'wch': val.toString().length * 2
};
} else {
return {
'wch': val.toString().length
};
}
}))
/*以第一行为初始值*/
let result = colWidth[0];
for (let i = 1; i < colWidth.length; i++) {
for (let j = 0; j < colWidth[i].length; j++) {
if (result[j]['wch'] < colWidth[i][j]['wch']) {
result[j]['wch'] = colWidth[i][j]['wch'];
}
}
}
ws['!cols'] = result;
}
/* add worksheet to workbook */
wb.SheetNames.push(ws_name);
wb.Sheets[ws_name] = ws;
var wbout = XLSX.write(wb, {
bookType: bookType,
bookSST: false,
type: 'binary'
});
saveAs(new Blob([s2ab(wbout)], {
type: "application/octet-stream"
}), `${filename}.${bookType}`);
}

View File

@ -0,0 +1,103 @@
/**
* Created by Zhang Haijun on 2017/8/24.
* axios#request(config)
* axios#get(url[, config])
* axios#delete(url[, config])
* axios#head(url[, config])
* axios#options(url[, config])
* axios#post(url[, data[, config]])
* axios#put(url[, data[, config]])
* axios#patch(url[, data[, config]])
*/
import axios from 'axios'
import qs from 'qs'
const codeMessage = {
200: '服务器成功返回请求的数据。',
201: '新建或修改数据成功。',
202: '一个请求已经进入后台排队(异步任务)。',
204: '删除数据成功。',
400: '发出的请求有错误,服务器没有进行新建或修改数据的操作。',
401: '用户没有权限(令牌、用户名、密码错误)。',
403: '用户得到授权,但是访问是被禁止的。',
404: '发出的请求针对的是不存在的记录,服务器没有进行操作。',
406: '请求的格式不可得。',
410: '请求的资源被永久删除,且不会再得到的。',
422: '当创建一个对象时,发生一个验证错误。',
500: '服务器发生错误,请检查服务器。',
502: '网关错误。',
503: '服务不可用,服务器暂时过载或维护。',
504: '网关超时。'
}
const axiosInstance = axios.create({
baseURL: '/api',
headers: { 'Content-Type': 'application/x-www-form-urlencoded', BsmAjaxHeader: true },
timeout: 20000,
paramsSerializer: params => {
return qs.stringify(params, { arrayFormat: 'indices' })
}
})
// 请求完成回调
const finishCallback = function () {
}
// 报错处理
const handleError = function (response) {
if (!response) return // 容错处理
const errorText = codeMessage[response.status] || response.statusText
// Notification({
// type: 'error',
// title: `请求错误 ${response.status}: ${response.config.url}`,
// message: errorText
// })
const error = new Error(errorText)
error.name = response.status
error.response = response
throw error
}
axiosInstance.interceptors.request.use(
config => {
const {
headers: { options = {} }
} = config
if (config.method === 'get') {
// 清除get缓存
config.url = `${config.url}?t=${new Date().getTime()}`
}
config.headers.token = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhcGlLZXkiOiIxMjE0NTQ2NzgyIiwiY2F0YWxvZyI6Ik1hbmFnZXIiLCJuYW1lIjoi6LaF57qn566h55CG5ZGYIiwiZXhwIjoxNjIzODI5OTA1LCJ1dWlkIjoxLCJhY2NvdW50IjoiYWRtaW4ifQ.H1TLrKni0z4xp3M55SUbUyLImgELqrZhCYrPXoKmucY'
delete config.headers.options
config.options = options
return config
},
error => {
return Promise.reject(error)
}
)
axiosInstance.interceptors.response.use(
data => {
// const requestKey = getRequestIdentify(data.config);
// removePending(requestKey);
finishCallback()
const responseData = data.data
const { options } = data.config
if (!responseData.success) {
switch (responseData.status) {
case '402':
location.href = '/#/license'
break
case '401':
case '509':
break
default:
}
if (!options.ignoreError) {
}
}
return responseData
},
error => {
finishCallback()
handleError(error.response)
return Promise.reject(error)
}
)
export default axiosInstance

View File

@ -0,0 +1,13 @@
/**
* Created by HaijunZhang on 2018/12/10.
*/
import path from 'path';
export function isExternalLink (path) {
return /^(http:|https:|mailto:|tel:)\/\//.test(path);
}
export function resolvePath (basePath, routePath) {
if (isExternalLink(routePath)) {
return routePath;
}
return path.resolve(basePath, routePath);
}

View File

@ -0,0 +1,62 @@
/**
* Created by Zhang Haijun on 2021/2/2.
*/
// 查询参数封装优化
export function handleSearchParam (params) {
// 设置参数
const objParams = {}
function setParams (sign, key, value) {
if (objParams[sign]) {
objParams[sign][key] = value
} else {
objParams[sign] = {
[key]: value
}
}
}
// 将参数处理为对象
for (const a in params) {
const value = params[a]
// 对参数进行处理,去除空参数
if (value === '' || value === undefined || value === null) continue
// 对key值进行处理
const [key, sign = 'EQ'] = a.split(':')
// 将sign全部转换为大写
const signs = sign.toLocaleUpperCase()
if (signs === 'RANGE') {
const [first, second] = value
setParams('GET', key, first)
setParams('LET', key, second)
continue
}
setParams(signs, key, value)
}
const result = []
for (const a in objParams) {
result.push({ param: objParams[a], sign: a })
}
return JSON.stringify(result)
}
/**
* 判断字符类型
* @param {*} word 字符
* @returns 0中文, 1: 英文, 2数字, 3: 其他类型
*/
export const getWordType = (word) => {
// 验证是否是中文
const pattern0 = new RegExp('[\u4E00-\u9FA5]+');
// 验证是否是英文
const pattern1 = new RegExp('[A-Za-z]+');
// 验证是否是数字
const pattern2 = new RegExp('[0-9]+');
if (pattern0.test(word)) {
return 0
} else if (pattern1.test(word)) {
return 1
} else if (pattern2.test(word)) {
return 2
} else {
return 3
}
}

View File

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

View File

@ -0,0 +1,40 @@
.buy-btn {
position: fixed;
top: 58px;
right: 20px;
}
.tip {
font-size: 12px;
color: #999;
}
.el-message-box__message {
word-break: break-all;
}
.viewer-container.viewer-backdrop {
z-index: 99999999999999 !important;
}
.w {
width: 200px !important;
}
.el-radio-button__orig-radio:checked+.el-radio-button__inner {
color: #409EFF;
background-color: #D5E8FC;
border-color: #409EFF;
}
.el-radio-button__inner {
min-width: 80px;
}
.el-drawer.rtl {
overflow: auto
}
.tab-label-padding {
padding: 0 10px;
}

View File

@ -0,0 +1,8 @@
/* 改变主题色变量 */
@import './common-var';
/* 改变 icon 字体路径变量,必需 */
$--font-path: '~element-ui/lib/theme-chalk/fonts';
@import"~element-ui/packages/theme-chalk/src/index"

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

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

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

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

View File

@ -0,0 +1,4 @@
@import './lib/index';
@import './hack.scss';
@import './font.scss';
@import './common.scss';

View File

@ -0,0 +1,29 @@
.el-button--text {
padding: 0 !important;
}
.el-button.el-button--text [class*=el-icon-]+span {
margin-left: 2px;
}
.el-button--ghost {
color: #666;
background-color: #fff;
border-color: #d9d9d9;
}
.el-button--ghost:focus,
.el-button--ghost:hover {
color: #2b85e4;
background-color: transparent;
border-color: #2b85e4;
}
.el-button--ghost.is-disabled,
.el-button--ghost.is-disabled:active,
.el-button--ghost.is-disabled:focus,
.el-button--ghost.is-disabled:hover {
color: #bbb;
background-color: #f7f7f7;
border-color: #d9d9d9;
}

View File

@ -0,0 +1,818 @@
html,
body,
#app {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
font-size: 14px;
overflow: hidden !important;
font-family: Microsoft YaHei, Hiragino Sans GB;
}
.app-wrapper {
position: relative;
width: 100%;
height: 100%;
}
.wrapper {
box-shadow: none !important;
}
.wrapper>.el-card__body {
padding: 15px;
}
.wrapper-container {
padding: 8px;
}
.wrapper-sm {
padding: 15px;
}
.search-container {
position: relative;
padding: 15px !important;
border: 1px solid #ddd;
margin-bottom: 15px;
}
.search-container>.legend {
color: rgba(18, 19, 20, 0.69);
background: #fff;
position: absolute;
text-align: center;
width: 78px;
top: -10px;
left: 15px;
font-size: 14px;
}
.search-content>.search-item {
width: 20%;
}
.pagination-container {
margin-top: 10px;
text-align: right;
}
.iconfont {
font-size: 13px;
}
.panel {
background-color: #fff;
margin-bottom: 15px;
border: 1px solid #dee5e7;
}
.panel .panel-heading {
background-color: #edf1f2;
padding: 10px 15px;
border-bottom: 1px solid transparent;
}
.panel .panel-body {
padding: 15px;
}
/*边距共有样式*/
.text-center {
text-align: center;
}
.pull-right {
float: right;
}
.pull-left {
float: left;
}
.p-none {
padding: 0 !important;
}
.m-xxs {
margin: 2px 4px;
}
.m-xs {
margin: 5px;
}
.m-sm {
margin: 10px;
}
.m {
margin: 15px;
}
.m-md {
margin: 20px;
}
.m-lg {
margin: 30px;
}
.m-xl {
margin: 50px;
}
.m-n {
margin: 0 !important;
}
.m-l-none {
margin-left: 0 !important;
}
.m-l-xs {
margin-left: 5px;
}
.m-l-sm {
margin-left: 10px !important;
}
.m-l {
margin-left: 15px;
}
.m-l-md {
margin-left: 20px;
}
.m-l-lg {
margin-left: 30px;
}
.m-l-xl {
margin-left: 40px;
}
.m-l-xxl {
margin-left: 50px;
}
.m-l-n-xxs {
margin-left: -1px;
}
.m-l-n-xs {
margin-left: -5px;
}
.m-l-n-sm {
margin-left: -10px;
}
.m-l-n {
margin-left: -15px;
}
.m-l-n-md {
margin-left: -20px;
}
.m-l-n-lg {
margin-left: -30px;
}
.m-l-n-xl {
margin-left: -40px;
}
.m-l-n-xxl {
margin-left: -50px;
}
.m-t-none {
margin-top: 0 !important;
}
.m-t-xxs {
margin-top: 1px;
}
.m-t-xs {
margin-top: 5px !important;
}
.m-t-sm {
margin-top: 10px !important;
}
.m-t {
margin-top: 15px;
}
.m-t-md {
margin-top: 20px;
}
.m-t-lg {
margin-top: 30px;
}
.m-t-xl {
margin-top: 40px;
}
.m-t-xxl {
margin-top: 50px;
}
.m-t-n-xxs {
margin-top: -1px;
}
.m-t-n-xs {
margin-top: -5px;
}
.m-t-n-7 {
margin-top: -7px;
}
.m-t-n-sm {
margin-top: -10px;
}
.m-t-n {
margin-top: -15px;
}
.m-t-n-md {
margin-top: -20px;
}
.m-t-n-lg {
margin-top: -30px;
}
.m-t-n-xl {
margin-top: -40px;
}
.m-t-n-xxl {
margin-top: -50px;
}
.m-t-n-xxxl {
margin-top: -60px;
}
.m-r-none {
margin-right: 0 !important;
}
.m-r-xxs {
margin-right: 1px;
}
.m-r-xs {
margin-right: 5px;
}
.m-r-sm {
margin-right: 10px !important;
}
.m-r {
margin-right: 15px !important;
}
.m-r-md {
margin-right: 20px;
}
.m-r-lg {
margin-right: 30px;
}
.m-r-xl {
margin-right: 40px;
}
.m-r-xxl {
margin-right: 50px;
}
.m-r-n-xxs {
margin-right: -1px;
}
.m-r-n-xs {
margin-right: -5px;
}
.m-r-n-sm {
margin-right: -10px;
}
.m-r-n {
margin-right: -15px;
}
.m-r-n-md {
margin-right: -20px;
}
.m-r-n-lg {
margin-right: -30px;
}
.m-r-n-xl {
margin-right: -40px;
}
.m-r-n-xxl {
margin-right: -50px;
}
.m-b-none {
margin-bottom: 0 !important;
}
.m-b-xxs {
margin-bottom: 1px;
}
.m-b-xs {
margin-bottom: 5px;
}
.m-b-sm {
margin-bottom: 10px !important;
}
.m-b {
margin-bottom: 15px !important;
}
.m-b-md {
margin-bottom: 20px;
}
.m-b-lg {
margin-bottom: 30px;
}
.m-b-xl {
margin-bottom: 40px;
}
.m-b-xxl {
margin-bottom: 50px;
}
.m-b-n-xxs {
margin-bottom: -1px;
}
.m-b-n-xs {
margin-bottom: -5px;
}
.m-b-n-sm {
margin-bottom: -10px;
}
.m-b-n {
margin-bottom: -15px;
}
.m-b-n-md {
margin-bottom: -20px;
}
.m-b-n-lg {
margin-bottom: -30px;
}
.m-b-n-xl {
margin-bottom: -40px;
}
.m-b-n-xxl {
margin-bottom: -50px;
}
/*颜色共有样式*/
.btn-info {
color: #ffffff !important;
background-color: #497edf;
border-color: #497edf;
}
.btn-info:hover,
.btn-info:focus,
.btn-info:active,
.btn-info.active,
.open .dropdown-toggle.btn-info {
color: #ffffff !important;
background-color: #2059c1;
border-color: #2059c1;
}
.btn-success {
color: #ffffff !important;
background-color: #27c24c;
border-color: #27c24c;
}
.btn-success:hover,
.btn-success:focus,
.btn-success:active,
.btn-success.active,
.open .dropdown-toggle.btn-success {
color: #ffffff !important;
background-color: #23ad44;
border-color: #20a03f;
}
.btn-danger {
color: #ffffff !important;
background-color: #f05050;
border-color: #f05050;
}
.btn-danger:hover,
.btn-danger:focus,
.btn-danger:active,
.btn-danger.active,
.open .dropdown-toggle.btn-danger {
color: #ffffff !important;
background-color: #ee3939;
border-color: #ed2a2a;
}
.text-info {
color: #00aeef !important;
}
.text-success {
color: #27c24c !important;
}
.text-warning {
color: #fad733 !important;
}
.text-danger {
color: #f05050;
}
.text-white {
color: #FFF !important;
}
.text-left {
text-align: left !important;
}
.text-center {
text-align: center !important;
}
.text-right {
text-align: right !important;
}
/*宽度样式*/
.w-xxs {
width: 60px;
}
.w-xs {
width: 90px;
}
.w-110 {
width: 110px;
}
.w-ss {
width: 120px;
}
.w-sm {
width: 150px;
}
.w {
width: 200px;
}
.w-md {
width: 240px;
}
.w-lg {
width: 280px !important;
}
.w-xl {
width: 320px;
}
.w-xxl {
width: 360px;
}
.w-full {
width: 100% !important;
}
.w-auto {
width: auto;
}
.upload-file-btn {
position: relative;
cursor: pointer;
}
.upload-file-btn input[type=file] {
width: 100%;
height: 100%;
font-size: 300px;
opacity: 0;
filter: alpha(opacity=0);
cursor: pointer;
left: 0;
right: 0;
bottom: 0;
top: 0;
position: absolute;
padding: 0px;
margin: 0px;
overflow: hidden;
}
/*详情表格*/
.detail-box {
margin-top: 10px;
margin-bottom: 10px;
}
.detail-title {
margin-bottom: 15px;
text-indent: 8px;
border-left: 3px solid #88B7E0;
font-size: 14px;
font-weight: 600;
}
.detail-href {
color: $--color-primary !important;
cursor: pointer;
text-decoration: none !important;
}
.detail-table {
width: 100%;
border-collapse: collapse;
}
.detail-table td.title {
color: #000;
width: 85px;
font-size: 12px;
font-weight: bold;
}
.detail-table td,
.detail-table th {
padding: 6px 8px;
border: 1px solid #ddd;
min-width: 85px;
width: 250px;
word-break: break-all;
white-space: pre-wrap;
/*border: none;*/
}
.border-top-none {
border-top: none !important;
}
a {
color: inherit;
cursor: pointer;
text-decoration: none;
}
.bounce-enter-active,
.bounce-leave-active,
.bounceDown-enter-active,
.bounceDown-leave-active,
.bounceIn,
.bounceInDown,
.bounceInLeft,
.bounceInRight,
.bounceInUp,
.bounceLeft-enter-active,
.bounceLeft-leave-active,
.bounceOut,
.bounceOutDown,
.bounceOutLeft,
.bounceOutRight,
.bounceOutUp,
.bounceRight-enter-active,
.bounceRight-leave-active,
.bounceUp-enter-active,
.bounceUp-leave-active,
.fade-enter-active,
.fade-leave-active,
.fadeDown-enter-active,
.fadeDown-leave-active,
.fadeDownBig-enter-active,
.fadeDownBig-leave-active,
.fadeIn,
.fadeInDown,
.fadeInDownBig,
.fadeInLeft,
.fadeInLeftBig,
.fadeInRight,
.fadeInRightBig,
.fadeInUp,
.fadeInUpBig,
.fadeLeft-enter-active,
.fadeLeft-leave-active,
.fadeLeftBig-enter-active,
.fadeLeftBig-leave-active,
.fadeOut,
.fadeOutDown,
.fadeOutDownBig,
.fadeOutLeft,
.fadeOutLeftBig,
.fadeOutRight,
.fadeOutRightBig,
.fadeOutUp,
.fadeOutUpBig,
.fadeRight-enter-active,
.fadeRight-leave-active,
.fadeRightBig-enter-active,
.fadeRightBig-leave-active,
.fadeUp-enter-active,
.fadeUp-leave-active,
.fadeUpBig-enter-active,
.fadeUpBig-leave-active,
.rotateDownLeft-enter-active,
.rotateDownLeft-leave-active,
.rotateDownRight-enter-active,
.rotateDownRight-leave-active,
.rotateInDownLeft,
.rotateInDownRight,
.rotateInUpLeft,
.rotateInUpRight,
.rotateOutDownLeft,
.rotateOutDownRight,
.rotateOutUpLeft,
.rotateOutUpRight,
.rotateUpLeft-enter-active,
.rotateUpLeft-leave-active,
.rotateUpRight-enter-active,
.rotateUpRight-leave-active,
.slide-enter-active,
.slide-leave-active,
.slideDown-enter-active,
.slideDown-leave-active,
.slideIn,
.slideInDown,
.slideInLeft,
.slideInRight,
.slideInUp,
.slideLeft-enter-active,
.slideLeft-leave-active,
.slideOut,
.slideOutDown,
.slideOutLeft,
.slideOutRight,
.slideOutUp,
.slideRight-enter-active,
.slideRight-leave-active,
.slideUp-enter-active,
.slideUp-leave-active,
.zoom-enter-active,
.zoom-leave-active,
.zoomDown-enter-active,
.zoomDown-leave-active,
.zoomIn,
.zoomInDown,
.zoomInLeft,
.zoomInRight,
.zoomInUp,
.zoomLeft-enter-active,
.zoomLeft-leave-active,
.zoomOut,
.zoomOutDown,
.zoomOutLeft,
.zoomOutRight,
.zoomOutUp,
.zoomRight-enter-active,
.zoomRight-leave-active,
.zoomUp-enter-active,
.zoomUp-leave-active {
animation-duration: 0.6s !important;
animation-fill-mode: both;
}
.cur-point {
cursor: pointer !important;
}
/*无数据*/
.no-data {
text-align: center;
min-height: 200px;
line-height: 200px;
}
.no-data .icon-zanwushuju {
font-size: 26px;
color: #d2cccc;
}
/*.custom-dialog .el-dialog{*/
/*width: 80%;*/
/*height: 100%;*/
/*position: absolute;*/
/*right: 0;*/
/*}*/
.text-ellipsis {
display: block;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.text-muted {
color: #98a6ad;
}
.custom-scrollbar .el-scrollbar__wrap {
overflow-x: hidden !important;
}
.action-divider {
margin: 0 8px;
display: inline-block;
height: 1em;
width: 1px;
vertical-align: middle;
position: relative;
top: -.06em;
font-size: 14px;
line-height: 1.5;
-webkit-box-sizing: border-box;
box-sizing: border-box;
padding: 0;
background: #e8e8e8;
}
.dialog-sm {
width: 300px;
}
.dialog-md {
width: 800px;
}
.dialog-lg {
width: 950px;
}
:-ms-input-placeholder {
/* Internet Explorer 10+ */
color: #e9d6cf !important;
}

View File

@ -0,0 +1,2 @@
@import "./theme.scss";
@import "./table.scss";

View File

@ -0,0 +1,130 @@
.sidebar-container {
z-index: 3;
width: 180px !important;
transition: width 0.18s;
.scrollbar-wrapper {
height: calc(100vh - 50px);
.el-scrollbar__wrap{
overflow-x: hidden;
}
}
.sidebar-menu {
width: 180px;
height: calc(100vh - 50px);
border: 0;
}
.sidebar-menu::-webkit-scrollbar {
display: none;
}
.el-submenu__title > i,
.el-menu-item > i{
margin-right: 6px;
}
.el-submenu.is-opened .el-submenu__title{
color: #fff !important;
& > i {
color: #fff !important;
}
}
.el-menu-item.is-active i {
color: #fff !important;
}
.el-submenu .el-menu-item{
min-width: 180px !important;
background: #15171d !important;
&.is-active{
background: #1890ff !important;
}
}
.menu-wrapper .el-menu-item{
&.is-active{
background: #1890ff !important;
}
}
.el-menu--collapse {
width: 64px;
overflow: inherit;
}
.logo {
height: 50px;
position: relative;
line-height: 50px;
transition: all 0.3s;
background: #21242e;
overflow: hidden;
text-align: center;
img {
display: inline-block;
vertical-align: middle;
height: 32px;
}
.short-logo{
display: none;
}
h1 {
color: #fff;
display: inline-block;
vertical-align: middle;
font-size: 18px;
margin: 0 0 0 8px;
font-family: "Myriad Pro", "Helvetica Neue", Arial, Helvetica,
sans-serif;
font-weight: 600;
}
}
}
.hideSidebar {
.sidebar-container {
width: 64px !important;
.el-menu--collapse {
.el-submenu {
overflow: hidden;
&>.el-submenu__title {
&>span {
height: 0;
width: 0;
overflow: hidden;
visibility: hidden;
display: inline-block;
}
.el-submenu__icon-arrow{
display: none;
}
}
}
}
.logo{
img{
display: none;
}
.short-logo{
display: inline-block;
}
}
}
}
.top-menu{
float: left;
}
//
.el-menu--horizontal {
border: none !important;
&>.menu-wrapper{
float: left;
}
.el-menu-item>i, .el-submenu__title>i {
margin-right: 6px;
}
.el-menu-item {
float: left;
height: 60px;
line-height: 60px;
margin: 0;
//border-bottom: 2px solid transparent;
color: #909399;
&.is-active {
//border-bottom: 2px solid #409eff;
color: #303133;
}
}
}

View File

@ -0,0 +1,22 @@
.table-container {
.custom-header {
background: #F5F7FA !important;
color: #333;
border-bottom: 1px solid #DCDFE6 !important;
}
.el-table--enable-row-hover .el-table__body tr:hover>td {
background-color: #EAF3FD;
}
.gutter {
background: #f5f7fa !important;
color: #333;
border-bottom: 1px solid #dcdfe6 !important;
}
.pagination-container {
margin-top: 10px;
text-align: right;
.el-pagination__jump {
margin-left: 0;
}
}
}

View File

@ -0,0 +1,49 @@
.theme-container{
width: 280px;
padding: 0 !important;
top: 30px !important;
.popper__arrow {
display: none !important;
}
.el-card__header{
padding: 10px 20px;
}
}
.theme-setting{
.icon-huanfu{
font-size: 18px;
cursor: pointer;
}
}
.theme-cell{
height: 30px;
position: relative;
cursor: pointer;
margin-bottom: 5px;
span {
height: 100%;
width: 100%;
position: absolute;
background-color: rgba(32, 43, 54, 0.5);
text-align: center;
display: inherit;
.icon-ok{
position: relative;
top: 6px;
display: inline-block;
font-weight: 400;
font-size: 17px;
line-height: 1;
color: #fff;
}
}
b {
display: inline-block;
float: left;
width: 50%;
height: 20px;
}
b.t-header{
height: 10px;
}
}

View File

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

View File

@ -0,0 +1,3 @@
@import "./common.scss";
@import "./element-ui.scss";
@import "./components/index.scss";

89
src/common/css/main.css Normal file
View File

@ -0,0 +1,89 @@
.bsm_btn {
height: 28px !important;
line-height: 28px !important;
}
.dialog-header {
width: 100% !important;
height: 44px;
line-height: 44px;
z-index: 99;
background: #fff;
border-bottom: 1px solid #e6e6e6;
font-weight: 550;
padding-left: 5px;
margin-top: 0;
}
.dialog-header .title{
display: inline-block;
color: #333;
}
.dialog-header .line,
.dialog_header_dark .line{
position: relative;
top: 4px;
margin: 0 10px;
height: 20px;
width: 1px;
display: inline-block;
background-color: #b5b5b5;
}
.dialog-header .dialog-back,
.dialog_header_dark .dialog-back
{
color: #46ABf1;
cursor: pointer;
}
.dialog_header_dark {
width: 100%;
height: 44px;
line-height: 44px;
z-index: 99;
background: #21242e;
border-bottom: 1px solid #e6e6e6;
font-weight: 550;
padding-left: 5px;
margin-top: 0;
}
.dialog_header_dark .title{
display: inline-block;
color: #fff;
}
.bsm-content {
padding: 20px;
}
.bsm-item {
height: 34px;
}
.bsm-item .item-label {
display: inline-block;
font-size: 12px;
height: 16px;
color: #999;
width: 105px;
overflow: hidden;
text-overflow: ellipsis;
}
.bsm-item .item-value {
font-size: 12px;
color: #333;
display: inline-block;
white-space: nowrap;
width: calc(100% - 145px);
overflow: hidden;
text-overflow: ellipsis;
}
.bsm-item .heightClass {
color: red;
}
.alert_bottom_10 {
margin-bottom: 10px;
}

View File

@ -0,0 +1,12 @@
/**
* Created by Zhang Haijun on 2018/1/25.
*/
/* global $ */
import Vue from 'vue'
import store from '@/store'
export const permission = Vue.directive('permission', function (el, binding) {
const buttons = store.state.permission.buttons
if (!buttons.includes(binding.value)) {
el.parentNode && el.parentNode.removeChild(el)
}
})

View File

@ -0,0 +1,25 @@
import { computed, onUnmounted, unref } from '@vue/composition-api'
import actions from '@/shared/action'
export default function(onmessage: {(data: any):void}, store: any) {
const webSocket = computed(() => store.state.app.$webSocket);
console.log(webSocket)
if (unref(webSocket)) {
webSocket.value.onmessage = onmessage
} else {
actions.setGlobalState({
name: 'cop-web',
onmessage
})
}
onUnmounted(() => {
if (unref(webSocket)) {
webSocket.value.onmessage = null
} else {
actions.setGlobalState({
name: 'cop-web',
onmessage: null
})
}
})
}

View File

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

View File

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

View File

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

View File

@ -0,0 +1,22 @@
export interface IListParams {
page: number,
rows: number,
simple?: boolean,
params?:string
}
export interface IAjaxData {
success: boolean,
failed: boolean,
solution: string,
message: string,
errorMsg: string,
data: any,
status: string
}
export interface IDialogConfig {
visible: boolean,
id?: number,
vendorId?: number
}

View File

@ -0,0 +1,29 @@
import actions from '@/shared/action'
export default {
computed: {
webSocket() {
return this.$store.state.app.$webSocket
}
},
mounted() {
if (this.webSocket) {
// 独立应用
this.webSocket.onmessage = this.onmessage
} else {
// 子应用处理方式
actions.setGlobalState({
onmessage: this.onmessage
})
}
this.$once('hook:beforeDestroy', () => {
if (this.webSocket) {
this.webSocket.onmessage = null
} else {
actions.setGlobalState({
onmessage: null
})
}
})
}
}

View File

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

View File

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

21
src/common/types/shims-ajax.d.ts vendored Normal file
View File

@ -0,0 +1,21 @@
import {} from 'axios'
declare module 'axios' {
export interface AxiosInstance {
(config: AxiosRequestConfig): AxiosPromise;
(url: string, config?: AxiosRequestConfig): AxiosPromise;
defaults: AxiosRequestConfig;
interceptors: {
request: AxiosInterceptorManager<AxiosRequestConfig>;
response: AxiosInterceptorManager<AxiosResponse>;
};
getUri(config?: AxiosRequestConfig): string;
request<T = any, R = Base.IResponseData<T>> (config: AxiosRequestConfig): Promise<R>;
get<T = any, R = Base.IResponseData<T>>(url: string, config?: AxiosRequestConfig): Promise<R>;
delete<T = any, R = Base.IResponseData<T>>(url: string, config?: AxiosRequestConfig): Promise<R>;
head<T = any, R = Base.IResponseData<T>>(url: string, config?: AxiosRequestConfig): Promise<R>;
options<T = any, R = Base.IResponseData<T>>(url: string, config?: AxiosRequestConfig): Promise<R>;
post<T = any, R = Base.IResponseData<T>>(url: string, data?: any, config?: AxiosRequestConfig): Promise<R>;
put<T = any, R = Base.IResponseData<T>>(url: string, data?: any, config?: AxiosRequestConfig): Promise<R>;
patch<T = any, R = Base.IResponseData<T>>(url: string, data?: any, config?: AxiosRequestConfig): Promise<R>;
}
}

53
src/common/types/shims-global.d.ts vendored Normal file
View File

@ -0,0 +1,53 @@
declare module Base {
// ajax请求返回数据格式
interface IResponseData<T = any> {
success: boolean
failed: boolean
solution: string
message: string
errorMsg: string
data: T
status: string
}
// list接口返回数据格式
interface IListData<T = any>{
page: number,
pages: number,
total:number,
rows: T[]
}
interface IResponseList<T = any> extends IResponseData{
data: IListData<T>
}
// 列表查询传参
interface IListParams {
page?: number
rows?: number
simple?: boolean
params?: string,
sorter?: string
}
// 模态框打开
interface IDialog<T = any> {
visible: boolean
record: T
}
// 搜索配置
interface ISearchConfig<T = any> {
label?: string,
value: string,
type: string,
initValue?: string | number,
sign?: string,
data?: T[],
onChange?: {
(val: string, listQuery: any): void
}
}
// 列表
type IListState<T = any> = {
list: T[],
total: number,
params: IListParams,
}
}

13
src/common/types/shims-tsx.d.ts vendored Normal file
View File

@ -0,0 +1,13 @@
import Vue, { VNode } from 'vue'
declare global {
namespace JSX {
// tslint:disable no-empty-interface
interface Element extends VNode {}
// tslint:disable no-empty-interface
interface ElementClass extends Vue {}
interface IntrinsicElements {
[elem: string]: any
}
}
}

10
src/common/types/shims-vue.d.ts vendored Normal file
View File

@ -0,0 +1,10 @@
import Vue from 'vue';
declare module '*.vue' {
export default Vue;
}
declare module 'vue/types/vue' {
interface Vue {
$tools: any
}
}

9
src/common/types/shims.tools.d.ts vendored Normal file
View File

@ -0,0 +1,9 @@
declare module 'cmp-basic'
declare module 'cmp-echarts'
declare module 'cmp-element'
declare module 'cmp-socket'
declare module 'cmp-element/utils'
declare module 'vite-plugin-svg-icons';
declare module 'services'
declare module 'v-viewer/src/component.vue'
declare module 'cmp-graph-editor'

17
src/common/utils/auth.js Normal file
View File

@ -0,0 +1,17 @@
/**
* Created by HaijunZhang on 2018/11/16.
*/
import Cookies from 'js-cookie'
import { tokenKey } from '@/config'
export function getToken() {
return Cookies.get(tokenKey)
}
export function setToken(token) {
return Cookies.set(tokenKey, token)
}
export function removeToken() {
return Cookies.remove(tokenKey)
}

View File

@ -0,0 +1,26 @@
// 同步加载
export function getModules(files: any) {
// 当files为方法时为webapock,为对象时为vite
const isWebpack = typeof files === 'function'
// 路径包含compoennts的是内置组件不向外暴露
const modules = (isWebpack ? files.keys() : Object.keys(files)).filter((item: string) => !item.includes('components')).reduce((modules: any, modulePath: string) => {
// set './app.vue' => 'app'
const moduleName = modulePath.replace(/^\.\S*\/(.*)\.\w+$/, '$1')
const value = isWebpack ? files(modulePath) : files[modulePath]
modules[moduleName] = value.default
return modules
}, {});
return modules;
}
// 异步加载
export function getAsyncModules(files: any, asyncImport: any) {
// 当files为方法时为webapock,为对象时为vite
const isWebpack = typeof files === 'function'
const modules = (isWebpack ? files.keys() : Object.keys(files)).filter((item: string) => !item.includes('components')).reduce((modules: any, modulePath: string) => {
// set './app.vue' => 'app'
const moduleName = modulePath.replace(/^\.\S*\/(.*)\.\w+$/, '$1')
modules[moduleName] = isWebpack ? asyncImport(modulePath) : files[moduleName]
return modules
}, {});
return modules;
}

View File

@ -0,0 +1,48 @@
/**
* Created by HaijunZhang on 2018/11/19.
*/
import CryptoJS from 'crypto-js'
const defaultKey = CryptoJS.enc.Utf8.parse(decryptByBase64('Qm9jbG91ZENNUFY1ODchIQ=='))
const iv = CryptoJS.enc.Utf8.parse(decryptByBase64('QmV5b25kQ01QVjU4NyEhIQ=='))
const options = {
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
}
// 加密方法
export function encrypt (word, key = defaultKey) {
if (typeof word === 'object') {
word = JSON.stringify(word)
}
const srcs = CryptoJS.enc.Utf8.parse(word)
const encrypted = CryptoJS.AES.encrypt(srcs, key, options)
return encrypted.toString()
}
// 解密方法
export function decrypt (word, key = defaultKey) {
const decrypt = CryptoJS.AES.decrypt(word, key, options)
return decrypt.toString(CryptoJS.enc.Utf8)
}
// base64解码
function decryptByBase64 (word) {
const base64string = CryptoJS.enc.Base64.parse(word)
return CryptoJS.enc.Utf8.stringify(base64string)
}
// base64编码
function encryptByBase64 (word) {
const src = CryptoJS.enc.Utf8.parse(word)
const base64string = CryptoJS.enc.Base64.stringify(src)
return base64string
}
export default {
encrypt,
decrypt,
encryptByBase64,
decryptByBase64
}

5
src/common/utils/day.js Normal file
View File

@ -0,0 +1,5 @@
import dayjs from 'dayjs'
import 'dayjs/locale/zh-cn'
dayjs.locale('zh-cn')
export default dayjs;

89
src/common/utils/index.js Normal file
View File

@ -0,0 +1,89 @@
/**
* Created by HaijunZhang on 2019/7/23.
*/
import Clipboard from 'clipboard'
import { getToken } from 'utils/auth'
import dayjs from './day'
export function wrapperParams(data) {
return data;
// return { params: JSON.stringify(data) }
}
export const formatEqParams = params => {
return { page: 1, rows: 9999, params: JSON.stringify([{ param: params, sign: 'EQ' }]) }
}
export const copyText = (text, event, successCallback, errorCallback) => {
const clipboard = new Clipboard(event.target, {
text: () => text
})
clipboard.on('success', () => {
successCallback && successCallback()
clipboard.destroy()
})
clipboard.on('error', () => {
errorCallback && errorCallback()
clipboard.destroy()
})
clipboard.onClick(event)
}
// export const downloadFile = (url, params = {}) => {
// request
// .get(url, {
// // headers: { 'Content-Type': params.fileFormat },
// responseType: 'blob',
// // params: wrapperParams(params),
// options: {
// isBlob: true
// }
// })
// .then(data => {
// var ele = document.createElement('a') // 创建下载链接
// ele.download = 'filename' // 设置下载的名称
// ele.style.display = 'none' // 隐藏的可下载链接
// // 字符内容转变成blob地址
// const blob = new Blob([data])
// ele.href = URL.createObjectURL(blob)
// // 绑定点击时间
// document.body.appendChild(ele)
// ele.click()
// // 然后移除
// document.body.removeChild(ele)
// })
// }
export const downloadFile = (url, params = {}) => {
let str = ''
Object.keys(params).forEach(item => {
str += `&${item}=${params[item]}`
})
window.location.href = encodeURI(`/api${url}?token=${getToken()}${str}`)
}
export const getQuery = hash => {
const queryArr = hash.split('?');
if (queryArr.length === 1) {
return {}
}
const query = {}
queryArr[1].split('&').forEach(item => {
const [key, value] = item.split('=');
query[key] = value;
})
return query
}
// ip比较大小
export const compareIp = (ip1, ip2) => {
const ip1Arr = ip1.split('.');
const ip2Arr = ip2.split('.');
let flag;
for (let i = 0; i < 4; i++) {
if (Number(ip1Arr[i]) > Number(ip2Arr[i])) {
flag = 0;
break;
} else if (Number(ip1Arr[i]) < Number(ip2Arr[i])) {
flag = 1
}
}
return flag;
}
export function makeTimeStamp() {
return dayjs().format('YYYYMMDDHHmmss')
}

119
src/common/utils/request.js Normal file
View File

@ -0,0 +1,119 @@
/**
* Created by Zhang Haijun on 2017/8/24.
* axios#request(config)
* axios#get(url[, config])
* axios#delete(url[, config])
* axios#head(url[, config])
* axios#options(url[, config])
* axios#post(url[, data[, config]])
* axios#put(url[, data[, config]])
* axios#patch(url[, data[, config]])
*/
import axios from 'axios'
import NProgress from 'nprogress'
import qs from 'qs'
import 'nprogress/nprogress.css'
import { Notification, MessageBox } from 'element-ui'
import { getToken } from 'utils/auth'
import store from '@/store'
const codeMessage = {
200: '服务器成功返回请求的数据。',
201: '新建或修改数据成功。',
202: '一个请求已经进入后台排队(异步任务)。',
204: '删除数据成功。',
400: '发出的请求有错误,服务器没有进行新建或修改数据的操作。',
401: '用户没有权限(令牌、用户名、密码错误)。',
403: '用户得到授权,但是访问是被禁止的。',
404: '发出的请求针对的是不存在的记录,服务器没有进行操作。',
406: '请求的格式不可得。',
410: '请求的资源被永久删除,且不会再得到的。',
422: '当创建一个对象时,发生一个验证错误。',
500: '服务器发生错误,请检查服务器。',
502: '网关错误。',
503: '服务不可用,服务器暂时过载或维护。',
504: '网关超时。'
}
const axiosInstance = axios.create({
baseURL: '/api',
headers: { 'Content-Type': 'application/json', BsmAjaxHeader: true },
timeout: 20000,
paramsSerializer: params => {
return qs.stringify(params, { arrayFormat: 'indices' })
}
})
// 请求完成回调
const finishCallback = function() {
NProgress.done()
}
// 报错处理
const handleError = function(response) {
if (!response) return // 容错处理
const errorText = codeMessage[response.status] || response.statusText
Notification({
type: 'error',
title: `请求错误 ${response.status}: ${response.config.url}`,
message: errorText
})
const error = new Error(errorText)
error.name = response.status
error.response = response
throw error
}
axiosInstance.interceptors.request.use(
config => {
const {
headers,
headers: { options = {} }
} = config
NProgress.start()
if (config.method === 'get') {
// 清除get缓存
config.url = `${config.url}?t=${new Date().getTime()}`
} else if (headers['Content-Type'] === 'application/x-www-form-urlencoded') {
config.data = qs.stringify(config.data || {})
}
config.headers.token = getToken()
delete config.headers.options
config.options = options;
return config
},
error => {
return Promise.reject(error)
}
)
axiosInstance.interceptors.response.use(
data => {
// const requestKey = getRequestIdentify(data.config);
// removePending(requestKey);
finishCallback()
const responseData = data.data
const { options } = data.config
if (!responseData.success) {
switch (responseData.status) {
case '402':
store.dispatch('permission/ResetRoutes')
location.href = '/#/license'
break
case '401':
case '509':
store.dispatch('permission/ResetRoutes')
break
default:
}
if (!options.ignoreError) {
Notification({
message: responseData.message || responseData.data,
type: 'error'
})
}
}
return responseData
},
error => {
finishCallback()
handleError(error.response)
return Promise.reject(error)
}
)
export default axiosInstance

View File

@ -0,0 +1,21 @@
/**
* Created by HaijunZhang on 2018/12/10.
*/
// import path from 'path';
import { startsWith } from 'lodash-es'
export function isExternalLink (path) {
return /^(http:|https:|mailto:|tel:)\/\//.test(path);
}
export function resolvePath (basePath, routePath) {
if (isExternalLink(routePath)) {
return routePath;
}
// return path.resolve(basePath, routePath);
const basePathArr = basePath.split('/');
const routePathArr = routePath.split('/');
if (startsWith(routePath, '/')) {
return routePath;
}
const res = [...basePathArr, ...routePathArr].filter(item => item);
return `/${res.join('/')}`
}

View File

@ -0,0 +1,84 @@
// websocket文件上传
import { getToken } from './auth'
export default function uploadFile (item, callback, errorCallBack) {
const protocol = location.protocol === 'http:' ? 'ws' : 'wss'
item.isReady = true;
const socket = new WebSocket(`${protocol}://${location.host}/api/sms/uploadService`, getToken());
let i = 0;
let startSize = 0, endSize = 0;
const paragraph = 4 * 1024 * 1024; // 以4MB为一个分片
const count = parseInt(item.file.size / paragraph) + 1;
socket.onopen = function () {
item.isUploading = true;
socket.send(JSON.stringify({
filename: item.file.name,
upload: 'file'
}));
// 取消上传
item.cancel = function () {
item.progress = 0;
socket.send(JSON.stringify({
UPLOAD_CANCEL: 'UPLOAD_CANCEL'
}));
item.isUploading = false;
};
};
socket.onmessage = function (event) {
const sendFile = function () {
if (startSize < item.file.size) {
let blob;
endSize += paragraph;
if (item.file.webkitSlice) {
blob = item.file.webkitSlice(startSize, endSize);
} else if (item.file.mozSlice) {
blob = item.file.mozSlice(startSize, endSize);
} else {
blob = item.file.slice(startSize, endSize);
}
const reader = new FileReader();
reader.readAsArrayBuffer(blob);
reader.onload = function loaded (evt) {
const result = evt.target.result;
// i++;
// const isok = (i / count) * 100;
// item.progress = parseInt(isok);
startSize = endSize;
socket.send(result);
};
} else {
item.progress = 100;
socket.send(JSON.stringify({
sendover: 'sendover'
}));
}
};
item.isUploading = true;
item.isCancel = false;
const obj = JSON.parse(event.data);
if (obj.category == 'UPLOAD_ACK') {
item.filePath = obj.content;
sendFile();
} else if (obj.category == 'UPLOAD') {
if (obj.content == 'SAVE_FAILURE') {
item.isUploading = false;
errorCallBack(item);
} else if (obj.content == 'SAVE_SUCCESS') {
sendFile();
i++;
const isok = (i / count) * 100;
item.progress = parseInt(isok);
} else if (obj.content == 'TRUE') {
callback(item);
item.isReady = true;
item.isSuccess = true;
item.isUploading = false;
socket.close();
}
} else if (obj.category == 'UPLOAD_CANCEL') {
item.progress = 0;
item.isCancel = true;
socket.close();
}
};
}

16
src/config.js Normal file
View File

@ -0,0 +1,16 @@
/**
* 启用缓存加载速度会变快但是数据安全性和实时性降低
*/
// 是否启用权限本地缓存
export const enablePermissionStorage = false
// 本地缓存的菜单key值
export const menuKey = 'cmcMenuData'
// 是否启用用户本地缓存
export const enableUserStorage = false
// 本地缓存的用户key值
export const userKey = 'cmcUserData'
// 本地存储的cookie kye值
export const tokenKey = 'CMC_TOKEN'
// 最大缓存组件实例数
export const cacheViewMax = 15
export const baseUrl = '/cop-web'

29
src/errorLog.js Normal file
View File

@ -0,0 +1,29 @@
/**
* Created by HaijunZhang on 2019/10/21.
*/
import Vue from 'vue'
import store from './store'
import request from 'utils/request'
const { host, hash } = location
const sendLog = (msg, vm, info, level) => {
request.post('http://10.20.51.92:7001/log', {
service: 'CMC',
level,
username: store.state.app.userData.username,
host,
view: hash,
msg: `${msg}`,
info
})
}
if (process.env.NODE_ENV === 'development') {
Vue.config.errorHandler = function(err, vm, info) {
console.error(err, vm, info)
// sendLog(err, vm, info, 'error')
}
Vue.config.warnHandler = function(msg, vm, info) {
console.warn(msg, vm, info)
// sendLog(msg, vm, info, 'warning')
}
}

209
src/filters/index.js Normal file
View File

@ -0,0 +1,209 @@
// 正反编译
export function booleanFilter(value) {
const obj = {
true: '是',
false: '否',
1: '是',
0: '否',
YES: '是',
NO: '否'
}
return obj[value] || value
}
export function vmStatusFilter(status) {
const statusMap = {
RUNNING: '运行中',
STOPPED: '关机',
STOPPING: '关机中',
STARTING: '开机中',
SUSPENDED: '挂起',
SUSPENDING: '挂起中',
ACTIVING: '激活中',
PAUSED: '停止',
PAUSING: '停止中',
RECOVERING: '恢复中',
BUILDING: '创建中',
RESTARTING: '重启中',
EXCEPTION: '异常',
SYNSEXCEPTION: '同步异常',
UNKNOWN: '已断开',
UNKNOWNON: '已断开',
UNKNOWNOFF: '已断开',
UNKNOWNSUSPEND: '已断开'
}
return statusMap[status] || '未知'
}
// 虚拟机状态颜色
export function vmStatusColorFilter(status) {
const statusMap = {
DEALLOCATE: 'danger',
DANGER: 'danger',
INACCESSIBLE: 'danger',
WARN: 'warning',
RUNNING: 'success',
STOPPED: 'danger',
STOPPING: 'warning',
SOFTRESTARTING: 'warning',
STARTING: 'warning',
SUSPENDED: 'danger',
SUSPENDING: 'warning',
ACTIVING: 'warning',
ACTIVE: 'success',
PAUSED: 'danger',
PAUSING: 'warning',
RECOVERING: 'warning',
BUILDING: 'warning',
RESTARTING: 'warning',
EXCEPTION: 'danger',
SYNSEXCEPTION: 'danger',
UNKNOWN: 'disabled',
UNKNOWNON: 'disabled',
UNKNOWNOFF: 'disabled',
UNKNOWNSUSPEND: 'disabled',
RESIZING: 'warning',
RESIZINGTCE: 'warning',
RESIZINGAWS: 'warning',
VERIFYRESIZE: 'warning',
SHELVED: 'danger',
RESTORING: 'warning',
AVAILABLE: 'normal',
INAVAILABLE: 'danger',
IN_USE: 'success',
'IN-USE': 'success',
DELETING: 'warning',
DOWNLOADING: 'warning',
green: 'success',
red: 'danger',
yellow: 'danger',
gray: 'danger',
ENABLED: 'success',
INSTALLING: 'warning',
UNINSTALL: 'danger',
FAILED: 'danger',
inactive: 'danger',
PENDING_CREATE: 'warning',
UPLOADING: 'warning',
PENDING_UPDATE: 'warning',
PENDING_DELETE: 'warning',
SYSTEMRESIZING: 'warning',
SHUTDOWN: 'warning',
TORECYCLE: 'warning',
RESERVED: 'warning',
ERROR: 'danger',
PENDING: 'warning',
DISABLED: 'danger',
DEALLOCATEING: 'warning',
PROTECTING: 'warning',
DOWN: 'info'
}
return statusMap[status] || 'danger'
}
export function taskStatusFilter(value, type) {
const statusMap = {
DEVELOPING: '开发中',
APPROVING: '发布中',
APPROVED: ' 已发布'
}
const colorMap = {
DEVELOPING: 'normal',
APPROVING: 'warning',
APPROVED: 'success'
}
return type === 'color' ? colorMap[value] : statusMap[value]
}
// 任务类型
export function taskTypeFilter(value) {
const obj = {
SCRIPT: '执行脚本',
FILE: '文件分发',
BACKUP: '文件备份',
RECOVERY: '文件恢复',
HTTP: 'HTTP请求',
DATABASE: '数据库',
POINT: '聚合节点'
}
return obj[value]
}
// 审批状态
export function applyStatusFilter(value, type) {
const obj = {
// APPROVING: {
// name: '审批中',
// color: 'normal'
// },
// REFUSED: {
// name: '审批拒绝',
// color: 'danger'
// },
APPROVED: {
name: '已发布',
color: 'success'
},
DEVELOPING: {
name: '开发中',
color: 'normal'
},
UNAPPLY: {
name: '未发布',
color: 'primary'
},
CREATED: {
name: '新创建',
color: 'normal'
}
}
return obj[value] && obj[value][type]
}
// 任务执行状态
export function taskExeStatusFilter(value, type = 'name') {
const obj = {
CREATED: {
name: '未执行',
color: 'normal'
},
NOREADY: {
name: '已跳过',
color: 'warning'
},
READY: {
name: '准备中',
color: 'normal'
},
WAITTING: {
name: '等待执行',
color: 'primary'
},
CANCELING: {
name: '取消中',
color: 'primary'
},
RUNNING: {
name: '正在执行',
color: 'normal'
},
SUCCESS: {
name: '执行成功',
color: 'success'
},
SUSPENDED: {
name: '已暂停',
color: 'warning'
},
FAILED: {
name: '执行失败',
color: 'danger'
},
CANCELED: {
name: '手动结束',
color: 'warning'
},
EXCEPTION: {
name: '执行异常',
color: 'danger'
}
}
return obj[value] && obj[value][type] // 容错处理(初始化值不存在)
}

3
src/icons/index.js Normal file
View File

@ -0,0 +1,3 @@
const req = require.context('./svg', false, /\.svg$/)
const requireAll = requireContext => requireContext.keys().map(requireContext)
requireAll(req)

View File

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

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

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

After

Width:  |  Height:  |  Size: 1.1 KiB

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