init
This commit is contained in:
1
.env.development
Normal file
1
.env.development
Normal file
@@ -0,0 +1 @@
|
|||||||
|
NODE_ENV="DEV"
|
||||||
1
.env.production
Normal file
1
.env.production
Normal file
@@ -0,0 +1 @@
|
|||||||
|
NODE_ENV="PROD"
|
||||||
25
.eslintrc.js
Normal file
25
.eslintrc.js
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
module.exports = {
|
||||||
|
env: {
|
||||||
|
browser: true,
|
||||||
|
es2021: true
|
||||||
|
},
|
||||||
|
extends: [
|
||||||
|
'plugin:vue/essential',
|
||||||
|
'standard'
|
||||||
|
],
|
||||||
|
parserOptions: {
|
||||||
|
ecmaVersion: 12,
|
||||||
|
sourceType: 'module'
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
'vue'
|
||||||
|
],
|
||||||
|
rules: {
|
||||||
|
eqeqeq: 0, // 关闭必须使用全等
|
||||||
|
'no-extend-native': 0,
|
||||||
|
'vue/no-parsing-error': 0, // 关闭此项避免在{{}}中使用>、<号导致报错的问题
|
||||||
|
'vue/no-use-v-if-with-v-for': 0, // vue2暂时关闭v-if和v-for写在一起的错误提示,到vue3后要遵守
|
||||||
|
'no-useless-escape': 0,
|
||||||
|
'no-eval': 0
|
||||||
|
}
|
||||||
|
}
|
||||||
89
README.md
89
README.md
@@ -1,24 +1,99 @@
|
|||||||
# sn
|
# Cyber Narrator(CN)
|
||||||
|
|
||||||
## Project setup
|
## 安装
|
||||||
```
|
```
|
||||||
npm install
|
npm install
|
||||||
```
|
```
|
||||||
|
|
||||||
### Compiles and hot-reloads for development
|
### 开发环境
|
||||||
```
|
```
|
||||||
npm run serve
|
npm run serve
|
||||||
```
|
```
|
||||||
|
|
||||||
### Compiles and minifies for production
|
### 生产环境
|
||||||
```
|
```
|
||||||
npm run build
|
npm run build
|
||||||
```
|
```
|
||||||
|
|
||||||
### Lints and fixes files
|
### Lints
|
||||||
```
|
```
|
||||||
npm run lint
|
npm run lint
|
||||||
```
|
```
|
||||||
|
|
||||||
### Customize configuration
|
## 技术选型
|
||||||
See [Configuration Reference](https://cli.vuejs.org/config/).
|
|
||||||
|
### 1.VUE3
|
||||||
|
|
||||||
|
### 2.基础UI库
|
||||||
|
项目UI设计与NEZHA相似度较高,考虑到工作量、学习成本,结合NEZHA的体验,选用element-plus
|
||||||
|
|
||||||
|
### 3.图表组件库
|
||||||
|
图表:echarts5.0
|
||||||
|
地图(支持下钻)(待定):echarts+自定义事件 | amCharts
|
||||||
|
拖拽 & Resize:Vue-grid-layout https://jbaysolutions.github.io/vue-grid-layout/guide/
|
||||||
|
|
||||||
|
### 4.响应式方案
|
||||||
|
lib-flexible + postcss-px2rem-exclude + 媒体查询
|
||||||
|
lib-flexible 将px自动转为rem,postcss-px2rem-exclude 避免将ui库的css转换,媒体查询根据不同屏幕大小使用不同的root font size
|
||||||
|
|
||||||
|
### 5.工具
|
||||||
|
使用lodash库,该库包含大部分常用工具方法,包括数组操作、函数功能扩展、对象操作等,且高性能、一致。开发中应该优先使用该库的方法
|
||||||
|
https://www.lodashjs.com/
|
||||||
|
|
||||||
|
## 开发规范 & 要求
|
||||||
|
|
||||||
|
### 1.语义化
|
||||||
|
- 所有命名要求使用语义化英文,意思表达清晰、准确
|
||||||
|
- 除如IP、SNMP这类特殊名词外,避免将单词缩略简写
|
||||||
|
- **禁用汉语拼音**
|
||||||
|
|
||||||
|
### 2.命名
|
||||||
|
多个单词时,应该以高阶的 (通常是一般化描述的) 单词开头,以描述性的修饰词结尾
|
||||||
|
eg:`SearchButtonClear.vue` 反例:`ClearSearchButton.vue`
|
||||||
|
- **文件夹**
|
||||||
|
使用小写,单词间使用连字符`-`连接
|
||||||
|
eg:`right-box`
|
||||||
|
|
||||||
|
|
||||||
|
- **VUE组件文件**
|
||||||
|
使用大写开头的驼峰格式
|
||||||
|
eg:`AssetBox.vue`
|
||||||
|
|
||||||
|
|
||||||
|
- **JS/CSS/SCSS/png等**
|
||||||
|
使用小写,单词间使用连字符`-`连接
|
||||||
|
eg:`table-common.scss`
|
||||||
|
|
||||||
|
|
||||||
|
- **JS标识符**
|
||||||
|
使用小写开头的驼峰格式
|
||||||
|
eg:`const tableTitle = 'Role'`
|
||||||
|
|
||||||
|
|
||||||
|
- **CLASS**
|
||||||
|
使用BEM规范命名: B__E--M,其中B名称必须是唯一的
|
||||||
|
BEM具体内容见 http://getbem.com/introduction/
|
||||||
|
|
||||||
|
**推荐阅读**
|
||||||
|
VUE官方风格指南 https://v3.cn.vuejs.org/style-guide/
|
||||||
|
|
||||||
|
### 3.开发
|
||||||
|
- 功能开发之初应考虑未来复用的可能性,将可能复用的功能代码提取为组件或使用混入,合理暴露参数、插槽,降低耦合;时间紧迫时优先级放后
|
||||||
|
- **性能**
|
||||||
|
1.组件销毁前注意解绑挂载在window对象和其他组件实例上的事件
|
||||||
|
2.减少能够触发浏览器回流、重绘的代码,必须使用时将使用次数尽量减少
|
||||||
|
**触发回流、重绘的js函数**
|
||||||
|
`offsetTop、offsetLeft、offsetWidth、offsetHeight
|
||||||
|
scrollTop、scrollLeft、scrollWidth、scrollHeight
|
||||||
|
clientTop、clientLeft、clientWidth、clientHeight
|
||||||
|
getComputedStyle()
|
||||||
|
getBoundingClientRect()`
|
||||||
|
**需要使用js修改style时,一次性赋值而不是分为多次**
|
||||||
|
eg:
|
||||||
|
`const el = document.getElementById('el')`
|
||||||
|
`el.style.cssText += 'border-left: 1px; border-right: 2px; padding: 5px;';`
|
||||||
|
反例:
|
||||||
|
`const el = document.getElementById('el')`
|
||||||
|
`el.style.padding = 'xxx'`
|
||||||
|
`el.style.margin = 'xxx'`
|
||||||
|
`el.style.border = 'xxx'`
|
||||||
|
|||||||
2551
package-lock.json
generated
2551
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
37
package.json
37
package.json
@@ -8,8 +8,21 @@
|
|||||||
"lint": "vue-cli-service lint"
|
"lint": "vue-cli-service lint"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"axios": "^0.21.1",
|
||||||
"core-js": "^3.6.5",
|
"core-js": "^3.6.5",
|
||||||
"vue": "^3.0.0"
|
"echarts": "^5.1.1",
|
||||||
|
"element-plus": "^1.0.2-beta.44",
|
||||||
|
"lib-flexible": "^0.3.2",
|
||||||
|
"lodash": "^4.17.21",
|
||||||
|
"node-sass": "^4.14.1",
|
||||||
|
"postcss-px2rem-exclude": "0.0.6",
|
||||||
|
"sass-loader": "^8.0.2",
|
||||||
|
"sass-resources-loader": "^2.2.1",
|
||||||
|
"vue": "^3.0.0",
|
||||||
|
"vue-grid-layout": "^2.3.12",
|
||||||
|
"vue-i18n": "^8.24.4",
|
||||||
|
"vue-router": "^4.0.8",
|
||||||
|
"vuex": "^4.0.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vue/cli-plugin-babel": "~4.5.0",
|
"@vue/cli-plugin-babel": "~4.5.0",
|
||||||
@@ -17,22 +30,12 @@
|
|||||||
"@vue/cli-service": "~4.5.0",
|
"@vue/cli-service": "~4.5.0",
|
||||||
"@vue/compiler-sfc": "^3.0.0",
|
"@vue/compiler-sfc": "^3.0.0",
|
||||||
"babel-eslint": "^10.1.0",
|
"babel-eslint": "^10.1.0",
|
||||||
"eslint": "^6.7.2",
|
"eslint": "^7.22.0",
|
||||||
"eslint-plugin-vue": "^7.0.0"
|
"eslint-config-standard": "^16.0.2",
|
||||||
},
|
"eslint-plugin-import": "^2.22.1",
|
||||||
"eslintConfig": {
|
"eslint-plugin-node": "^11.1.0",
|
||||||
"root": true,
|
"eslint-plugin-promise": "^4.3.1",
|
||||||
"env": {
|
"eslint-plugin-vue": "^7.7.0"
|
||||||
"node": true
|
|
||||||
},
|
|
||||||
"extends": [
|
|
||||||
"plugin:vue/vue3-essential",
|
|
||||||
"eslint:recommended"
|
|
||||||
],
|
|
||||||
"parserOptions": {
|
|
||||||
"parser": "babel-eslint"
|
|
||||||
},
|
|
||||||
"rules": {}
|
|
||||||
},
|
},
|
||||||
"browserslist": [
|
"browserslist": [
|
||||||
"> 1%",
|
"> 1%",
|
||||||
|
|||||||
9
postcss.config.js
Normal file
9
postcss.config.js
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
module.exports = {
|
||||||
|
plugins: {
|
||||||
|
autoprefixer: {},
|
||||||
|
/* 'postcss-px2rem-exclude': {
|
||||||
|
remUnit: 16,
|
||||||
|
exclude: /node_modules/i
|
||||||
|
} */
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,6 +12,17 @@
|
|||||||
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
|
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
|
||||||
</noscript>
|
</noscript>
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
<!-- built files will be auto injected -->
|
|
||||||
</body>
|
</body>
|
||||||
|
<style>
|
||||||
|
@media only screen and (min-width : 1224px) {
|
||||||
|
html {
|
||||||
|
font-size: 12px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media only screen and (min-width : 1824px) {
|
||||||
|
html {
|
||||||
|
font-size: 16px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
24
src/App.vue
24
src/App.vue
@@ -1,26 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<img alt="Vue logo" src="./assets/logo.png">
|
<div id="app">
|
||||||
<HelloWorld msg="Welcome to Your Vue.js App"/>
|
<router-view/>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import HelloWorld from './components/HelloWorld.vue'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'App',
|
name: 'App'
|
||||||
components: {
|
|
||||||
HelloWorld
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
|
||||||
#app {
|
|
||||||
font-family: Avenir, Helvetica, Arial, sans-serif;
|
|
||||||
-webkit-font-smoothing: antialiased;
|
|
||||||
-moz-osx-font-smoothing: grayscale;
|
|
||||||
text-align: center;
|
|
||||||
color: #2c3e50;
|
|
||||||
margin-top: 60px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
13
src/Login.vue
Normal file
13
src/Login.vue
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<template>
|
||||||
|
<div></div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'Login'
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
|
||||||
|
</style>
|
||||||
15
src/assets/css/common.scss
Normal file
15
src/assets/css/common.scss
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
[v-cloak] {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
html, body, #app {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
min-width: 1024px;
|
||||||
|
margin: 0 !important;
|
||||||
|
padding: 0 !important;
|
||||||
|
cursor: default !important;
|
||||||
|
}
|
||||||
0
src/assets/css/font/iconfont.scss
Normal file
0
src/assets/css/font/iconfont.scss
Normal file
3
src/assets/css/main.scss
Normal file
3
src/assets/css/main.scss
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
@import './font/iconfont';
|
||||||
|
@import './theme';
|
||||||
|
@import './common';
|
||||||
16
src/assets/css/theme.scss
Normal file
16
src/assets/css/theme.scss
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
/*** 定义自定义变量和重写element-ui变量 ***/
|
||||||
|
|
||||||
|
/** 自定义变量 **/
|
||||||
|
|
||||||
|
/** 重写element-ui变量 **/
|
||||||
|
$--color-primary: #0091ff; // 主题色
|
||||||
|
|
||||||
|
/* menu相关 */
|
||||||
|
$--menu-background-color: #00162B; // menu背景色
|
||||||
|
$--menu-hover-background-color: #000C18; // menu背景色
|
||||||
|
$--menu-item-font-color: #BEBEBE; // menu字色
|
||||||
|
$--menu-item-hover-fill: $--color-primary; // menu鼠标悬浮、激活时背景色
|
||||||
|
|
||||||
|
/** 改变 icon 字体路径变量,并引入element-ui变量文件 **/
|
||||||
|
$--font-path: '~element-plus/lib/theme-chalk/fonts';
|
||||||
|
@import "~element-plus/packages/theme-chalk/src/index";
|
||||||
BIN
src/assets/img/logo1-2.png
Normal file
BIN
src/assets/img/logo1-2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
@@ -1,58 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="hello">
|
|
||||||
<h1>{{ msg }}</h1>
|
|
||||||
<p>
|
|
||||||
For a guide and recipes on how to configure / customize this project,<br>
|
|
||||||
check out the
|
|
||||||
<a href="https://cli.vuejs.org" target="_blank" rel="noopener">vue-cli documentation</a>.
|
|
||||||
</p>
|
|
||||||
<h3>Installed CLI Plugins</h3>
|
|
||||||
<ul>
|
|
||||||
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel" target="_blank" rel="noopener">babel</a></li>
|
|
||||||
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint" target="_blank" rel="noopener">eslint</a></li>
|
|
||||||
</ul>
|
|
||||||
<h3>Essential Links</h3>
|
|
||||||
<ul>
|
|
||||||
<li><a href="https://vuejs.org" target="_blank" rel="noopener">Core Docs</a></li>
|
|
||||||
<li><a href="https://forum.vuejs.org" target="_blank" rel="noopener">Forum</a></li>
|
|
||||||
<li><a href="https://chat.vuejs.org" target="_blank" rel="noopener">Community Chat</a></li>
|
|
||||||
<li><a href="https://twitter.com/vuejs" target="_blank" rel="noopener">Twitter</a></li>
|
|
||||||
<li><a href="https://news.vuejs.org" target="_blank" rel="noopener">News</a></li>
|
|
||||||
</ul>
|
|
||||||
<h3>Ecosystem</h3>
|
|
||||||
<ul>
|
|
||||||
<li><a href="https://router.vuejs.org" target="_blank" rel="noopener">vue-router</a></li>
|
|
||||||
<li><a href="https://vuex.vuejs.org" target="_blank" rel="noopener">vuex</a></li>
|
|
||||||
<li><a href="https://github.com/vuejs/vue-devtools#vue-devtools" target="_blank" rel="noopener">vue-devtools</a></li>
|
|
||||||
<li><a href="https://vue-loader.vuejs.org" target="_blank" rel="noopener">vue-loader</a></li>
|
|
||||||
<li><a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">awesome-vue</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
name: 'HelloWorld',
|
|
||||||
props: {
|
|
||||||
msg: String
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
|
||||||
<style scoped>
|
|
||||||
h3 {
|
|
||||||
margin: 40px 0 0;
|
|
||||||
}
|
|
||||||
ul {
|
|
||||||
list-style-type: none;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
li {
|
|
||||||
display: inline-block;
|
|
||||||
margin: 0 10px;
|
|
||||||
}
|
|
||||||
a {
|
|
||||||
color: #42b983;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
26
src/components/layout/Container.vue
Normal file
26
src/components/layout/Container.vue
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<template>
|
||||||
|
<div class="cn-container">
|
||||||
|
<router-view/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'Container',
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.cn-container {
|
||||||
|
height: calc(100% - 50px);
|
||||||
|
background-color: #f6f6f6;
|
||||||
|
width: 100%;
|
||||||
|
&>div {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
168
src/components/layout/Header.vue
Normal file
168
src/components/layout/Header.vue
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
<template>
|
||||||
|
<div class="cn-header">
|
||||||
|
<!-- <div class="left-menu--pin" :class="false ? 'left-menu--pin-normal' : 'left-menu--pin-reverse'" @click="shrink"><i :class="{'icon-reverse': false}" class="el-icon-s-fold"></i></div>-->
|
||||||
|
<!--导航面包屑-->
|
||||||
|
<div style="flex-grow: 1"></div>
|
||||||
|
<!--个人操作-->
|
||||||
|
<div class="personal">
|
||||||
|
<el-dropdown>
|
||||||
|
<div class="header-menu--item">Language</div>
|
||||||
|
<template #dropdown>
|
||||||
|
<el-dropdown-menu>
|
||||||
|
<el-dropdown-item>
|
||||||
|
<div id="header-to-english" :style="language === 'en'?'color:#0091ff':''" @click="changeLocal('en')">English</div>
|
||||||
|
</el-dropdown-item>
|
||||||
|
<el-dropdown-item>
|
||||||
|
<div id="header-to-chinese" :style="language === 'cn'?'color:#0091ff':''" @click="changeLocal('cn')">中文</div>
|
||||||
|
</el-dropdown-item>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</template>
|
||||||
|
</el-dropdown>
|
||||||
|
<el-dropdown>
|
||||||
|
<div class='login-user header-menu--item'>{{username}} <i class="cn-icon cn-icon-arrow-down"></i></div>
|
||||||
|
<template #dropdown>
|
||||||
|
<el-dropdown-menu slot="dropdown">
|
||||||
|
<el-dropdown-item>
|
||||||
|
<div id="header-to-changepin" @click="showPinDialog">Change pin</div>
|
||||||
|
</el-dropdown-item>
|
||||||
|
<el-dropdown-item>
|
||||||
|
<div id="header-to-logout" @click="logout">Sign out</div>
|
||||||
|
</el-dropdown-item>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</template>
|
||||||
|
</el-dropdown>
|
||||||
|
</div>
|
||||||
|
<!-- <change-password :cur-user="username" :show-dialog="showChangePin" @click="showPinDialog" @dialogClosed="dialogClosed"></change-password>-->
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'Header',
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
username: 'admin', // sessionStorage.getItem('cn-username'),
|
||||||
|
language: localStorage.getItem('cn-language') ? localStorage.getItem('cn-language') : 'en',
|
||||||
|
|
||||||
|
showChangePin: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
changeLocal (lang) {
|
||||||
|
if (lang !== localStorage.getItem('cn-language')) {
|
||||||
|
localStorage.setItem('cn-language', lang)
|
||||||
|
// this.$i18n.locale = lang
|
||||||
|
window.location.reload()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
showPinDialog () {
|
||||||
|
this.showChangePin = true
|
||||||
|
},
|
||||||
|
logout () {
|
||||||
|
},
|
||||||
|
refreshLang () {
|
||||||
|
this.language = localStorage.getItem('cn-language')
|
||||||
|
this.$i18n.locale = this.language
|
||||||
|
this.$nextTick(() => {
|
||||||
|
window.location.reload()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
shrink () {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.cn-header {
|
||||||
|
display: flex;
|
||||||
|
height: 50px;
|
||||||
|
|
||||||
|
.header-menu {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
flex-grow: 1;
|
||||||
|
|
||||||
|
.el-dropdown {
|
||||||
|
width: 60px;
|
||||||
|
text-align: center;
|
||||||
|
height: 36px;
|
||||||
|
line-height: 50px;
|
||||||
|
.el-dialog{
|
||||||
|
width: 1000px;
|
||||||
|
height: 70px;
|
||||||
|
.el-dialog__header{
|
||||||
|
.el-dialog__title{
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
.el-dialog__close{
|
||||||
|
color: #fff;
|
||||||
|
line-height: 50px;
|
||||||
|
font-size: 30px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.header-menu--item {
|
||||||
|
color: #778391;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: color linear .2s;
|
||||||
|
i {
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.header-menu--item:hover {
|
||||||
|
color: #313336;
|
||||||
|
}
|
||||||
|
.personal {
|
||||||
|
display: flex;
|
||||||
|
.el-dropdown {
|
||||||
|
margin: 0 10px 0 30px;
|
||||||
|
height: 36px;
|
||||||
|
line-height: 50px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.login-user {
|
||||||
|
color: #333;
|
||||||
|
i {
|
||||||
|
color: #999;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.left-menu--pin {
|
||||||
|
width: 20px;
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 100;
|
||||||
|
color: #999999;
|
||||||
|
transition: all .4s;
|
||||||
|
height: 100%;
|
||||||
|
line-height: 50px;
|
||||||
|
margin-left: 10px;
|
||||||
|
i {
|
||||||
|
transform: rotateY(0);
|
||||||
|
transition: transform .4s;
|
||||||
|
}
|
||||||
|
i.icon-reverse {
|
||||||
|
transform: rotateY(180deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.left-menu--pin-normal{
|
||||||
|
}
|
||||||
|
.left-menu--pin-reverse{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.link-title a {
|
||||||
|
color: inherit;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
.cn-breakcrumb {
|
||||||
|
padding-left: 15px;
|
||||||
|
line-height: 50px;
|
||||||
|
|
||||||
|
.el-breadcrumb__item .el-breadcrumb__inner {
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
49
src/components/layout/Home.vue
Normal file
49
src/components/layout/Home.vue
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
<template>
|
||||||
|
<div class="cn-home">
|
||||||
|
<left-menu @refresh="refresh"></left-menu>
|
||||||
|
<div ref="body" class="cn-body">
|
||||||
|
<cn-header></cn-header>
|
||||||
|
<cn-container v-if="containerShow" ref="container"></cn-container>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Header from './Header'
|
||||||
|
import LeftMenu from './LeftMenu'
|
||||||
|
import Container from './Container'
|
||||||
|
export default {
|
||||||
|
name: 'Home',
|
||||||
|
components: {
|
||||||
|
LeftMenu,
|
||||||
|
'cn-header': Header,
|
||||||
|
'cn-container': Container
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
containerShow: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
refresh () {
|
||||||
|
this.containerShow = false
|
||||||
|
this.$nextTick(() => { this.containerShow = true })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.cn-home {
|
||||||
|
display: flex;
|
||||||
|
height: 100%;
|
||||||
|
transition: all .2s;
|
||||||
|
.cn-body {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: calc(100% - 240px);
|
||||||
|
transition: all .2s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
201
src/components/layout/LeftMenu.vue
Normal file
201
src/components/layout/LeftMenu.vue
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
<template>
|
||||||
|
<div class="left-menu">
|
||||||
|
<el-menu :collapse="isShrink" active-text-color="#ffffff" class="header-logo" text-color="#ffffff">
|
||||||
|
<el-menu-item index="logo">
|
||||||
|
<div id="home-to-overview" class="logo link">
|
||||||
|
<img alt="loading..." height="26" :src="logo?logo:require('../../assets/img/logo1-2.png')"/>
|
||||||
|
<span class="system-name">{{systemName && systemName !== 'undefined' ? systemName : 'dashboard.overview.contentTitle'}}</span>
|
||||||
|
</div>
|
||||||
|
</el-menu-item>
|
||||||
|
</el-menu>
|
||||||
|
<el-menu :collapse="isShrink" :default-active="route" class="menu-list" mode="vertical" unique-opened @select="jump">
|
||||||
|
<template v-for="(menu, index) in menuList">
|
||||||
|
<el-submenu v-if="menu.children && menu.children.length > 0" :key="index" :index="`${index}`">
|
||||||
|
<template #title>
|
||||||
|
<i :class="menu.icon"></i>
|
||||||
|
<span>{{menu.name}}</span>
|
||||||
|
</template>
|
||||||
|
<template v-for="(secondMenu, secondIndex) in menu.children">
|
||||||
|
<template v-if="secondMenu.children && secondMenu.children.length > 0">
|
||||||
|
<el-submenu :key="secondIndex" :index="`${index}-${secondIndex}`">
|
||||||
|
<span slot="title" class="data-column__span">{{secondMenu.name}}</span>
|
||||||
|
<el-menu-item v-for="(thirdMenu, thirdIndex) in secondMenu.children" :key="`${index}-${secondIndex}-${thirdIndex}`" :index="thirdMenu.route">{{thirdMenu.name}}</el-menu-item>
|
||||||
|
</el-submenu>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<el-menu-item :key="secondIndex" :index="secondMenu.route">{{secondMenu.name}}</el-menu-item>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</el-submenu>
|
||||||
|
<el-menu-item v-else :key="index + 'a'" :index="menu.route">
|
||||||
|
<i :class="menu.icon"></i>
|
||||||
|
<span slot="title" class="data-column__span">{{menu.name}}</span>
|
||||||
|
</el-menu-item>
|
||||||
|
</template>
|
||||||
|
</el-menu>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'LeftMenu',
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
systemName: localStorage.getItem('cn-sys-name'),
|
||||||
|
logo: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
setup () {
|
||||||
|
const self = this
|
||||||
|
window.addEventListener('setItemEvent', function (e) {
|
||||||
|
if (e.key === 'cn-sys-logo' && e.value) {
|
||||||
|
self.logo = e.value
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
menuList () {
|
||||||
|
const allMenu = this.$store.getters.menuList
|
||||||
|
excludeButtonAndMenu(this.$store.getters.menuList)
|
||||||
|
return allMenu
|
||||||
|
function excludeButtonAndMenu (menu) {
|
||||||
|
for (let i = 0; i < menu.length; i++) {
|
||||||
|
if (menu[i].type === 2 || menu[i].type === 3) {
|
||||||
|
menu.splice(i, 1)
|
||||||
|
i--
|
||||||
|
} else {
|
||||||
|
if (menu[i].children && menu[i].children.length > 0) {
|
||||||
|
excludeButtonAndMenu(menu[i].children)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
route () {
|
||||||
|
return this.$route.path
|
||||||
|
},
|
||||||
|
isShrink () {
|
||||||
|
return this.$store.getters.getIsShrink
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
jump (route) {
|
||||||
|
if (route === this.route) {
|
||||||
|
this.refresh()
|
||||||
|
}
|
||||||
|
this.$router.push({
|
||||||
|
path: route,
|
||||||
|
query: {
|
||||||
|
t: +new Date()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
refresh () {
|
||||||
|
this.$emit('refresh')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.left-menu {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
background-color: $--menu-background-color;
|
||||||
|
|
||||||
|
.el-menu-item .cn-icon, .el-submenu .cn-icon {
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
margin-right: 5px;
|
||||||
|
width: 24px;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
>.el-menu:not(.el-menu--collapse) {
|
||||||
|
width: 240px;
|
||||||
|
}
|
||||||
|
>.el-menu.menu-list {
|
||||||
|
height: calc(100% - 110px);
|
||||||
|
border-right: none;
|
||||||
|
overflow: auto;
|
||||||
|
|
||||||
|
// el-submenu active字色
|
||||||
|
.el-submenu.is-active .el-submenu__title,
|
||||||
|
.el-submenu.is-active .el-submenu__title>i,
|
||||||
|
.el-menu-item.is-active {
|
||||||
|
color: white !important;
|
||||||
|
}
|
||||||
|
// el-submenu active且open背景色
|
||||||
|
|
||||||
|
.el-submenu__title:not(.is-active):hover, .el-menu-item:not(.is-active):hover, .el-menu-item:not(.is-active):focus {
|
||||||
|
background-color: mix($--color-white, $--menu-background-color, 7%) !important;
|
||||||
|
}
|
||||||
|
.el-menu-item {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
|
||||||
|
&.is-active {
|
||||||
|
background-color: $--color-primary;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-menu-item, .el-submenu__title {
|
||||||
|
height: 46px;
|
||||||
|
line-height: 46px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*---滚动条默认显示样式--*/
|
||||||
|
>.el-menu::-webkit-scrollbar-thumb {
|
||||||
|
background-color: rgba(255,255,255,.4);
|
||||||
|
border-radius: 2px;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
/*---鼠标点击滚动条显示样式--*/
|
||||||
|
>.el-menu::-webkit-scrollbar-thumb:hover {
|
||||||
|
background-color: rgba(255,255,255,.3);
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
/*---滚动条大小--*/
|
||||||
|
>.el-menu::-webkit-scrollbar {
|
||||||
|
width: 6px;
|
||||||
|
height: 14px;
|
||||||
|
}
|
||||||
|
/*---顶部logo---*/
|
||||||
|
>.el-menu.header-logo {
|
||||||
|
border-right: none;
|
||||||
|
>.el-menu-item {
|
||||||
|
padding: 13px 0 0 18px !important;
|
||||||
|
height: 50px;
|
||||||
|
border-right: 1px solid #202F3F;
|
||||||
|
box-sizing: border-box;
|
||||||
|
background-color: #182534 !important;
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
display: flex;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
img {
|
||||||
|
box-shadow: 0 0 2px 0 rgba(0,0,0,0.50);
|
||||||
|
}
|
||||||
|
|
||||||
|
.system-name {
|
||||||
|
padding-left: 5px;
|
||||||
|
color: white;
|
||||||
|
font-size: 12px;
|
||||||
|
letter-spacing: 0;
|
||||||
|
line-height: 34px;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.el-menu--popup {
|
||||||
|
.el-menu-item {
|
||||||
|
height: 49px;
|
||||||
|
line-height: 49px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
13
src/main.js
13
src/main.js
@@ -1,4 +1,15 @@
|
|||||||
import { createApp } from 'vue'
|
import { createApp } from 'vue'
|
||||||
|
import router from './router'
|
||||||
|
import store from './store'
|
||||||
import App from './App.vue'
|
import App from './App.vue'
|
||||||
|
|
||||||
createApp(App).mount('#app')
|
import '@/assets/css/main.scss' // 样式入口
|
||||||
|
|
||||||
|
import ElementPlus from 'element-plus'
|
||||||
|
|
||||||
|
const app = createApp(App)
|
||||||
|
app.use(router)
|
||||||
|
app.use(store)
|
||||||
|
app.use(ElementPlus)
|
||||||
|
|
||||||
|
app.mount('#app')
|
||||||
|
|||||||
34
src/router/index.js
Normal file
34
src/router/index.js
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import { createRouter, createWebHashHistory } from 'vue-router'
|
||||||
|
|
||||||
|
const routes = [
|
||||||
|
{ path: '/', redirect: '/login' },
|
||||||
|
{
|
||||||
|
path: '/login',
|
||||||
|
component: () => import('@/Login')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/',
|
||||||
|
component: () => import('@/components/layout/Home'),
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: '/traffic',
|
||||||
|
component: () => import('@/views/dashboards/TrafficSummary')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/na',
|
||||||
|
component: () => import('@/views/dashboards/TrafficSummary')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/dns',
|
||||||
|
component: () => import('@/views/dashboards/TrafficSummary')
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
const router = createRouter({
|
||||||
|
history: createWebHashHistory(),
|
||||||
|
routes: routes
|
||||||
|
})
|
||||||
|
|
||||||
|
export default router
|
||||||
26
src/store/index.js
Normal file
26
src/store/index.js
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import { createStore } from 'vuex'
|
||||||
|
import user from './modules/user'
|
||||||
|
|
||||||
|
const store = createStore({
|
||||||
|
modules: {
|
||||||
|
user
|
||||||
|
},
|
||||||
|
state () {
|
||||||
|
return {
|
||||||
|
isShrink: localStorage.getItem('cn-left-menu-shrink') === 'true'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getters: {
|
||||||
|
getIsShrink (state) {
|
||||||
|
return state.isShrink
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mutations: {
|
||||||
|
isShrink (state) {
|
||||||
|
state.isShrink = !state.isShrink
|
||||||
|
localStorage.setItem('nz-left-menu-shrink', state.isShrink)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
export default store
|
||||||
104
src/store/modules/user.js
Normal file
104
src/store/modules/user.js
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
const user = {
|
||||||
|
state() {
|
||||||
|
return {
|
||||||
|
menuList: [],
|
||||||
|
buttonList: [],
|
||||||
|
roleList: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mutations: {
|
||||||
|
setMenuList (state, menuList) {
|
||||||
|
state.menuList = [...menuList]
|
||||||
|
},
|
||||||
|
setButtonList (state, buttonList) {
|
||||||
|
state.buttonList = [...buttonList]
|
||||||
|
},
|
||||||
|
setRoleList (state, roleList) {
|
||||||
|
state.roleList = [...roleList]
|
||||||
|
},
|
||||||
|
clean (state) {
|
||||||
|
state.menuList = []
|
||||||
|
state.buttonList = []
|
||||||
|
state.roleList = []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getters: {
|
||||||
|
menuList (state) {
|
||||||
|
const menuList = JSON.parse(`[
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"name": "dashboards",
|
||||||
|
"code": "dashboard",
|
||||||
|
"i18n": "dashboard.title",
|
||||||
|
"parentId": 0,
|
||||||
|
"perms": "",
|
||||||
|
"type": 1,
|
||||||
|
"route": "",
|
||||||
|
"orderNum": 1,
|
||||||
|
"icon": "cn-icon cn-icon-menu-dashboard",
|
||||||
|
"required": "",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"name": "Traffic summary",
|
||||||
|
"code": "overview",
|
||||||
|
"i18n": "dashboard.overview.title",
|
||||||
|
"parentId": 1,
|
||||||
|
"perms": "",
|
||||||
|
"type": 1,
|
||||||
|
"route": "/traffic",
|
||||||
|
"orderNum": 1,
|
||||||
|
"icon": "",
|
||||||
|
"required": "",
|
||||||
|
"children": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3,
|
||||||
|
"name": "Network & Application performance",
|
||||||
|
"code": "panel",
|
||||||
|
"i18n": "dashboard.panel.title",
|
||||||
|
"parentId": 1,
|
||||||
|
"perms": "",
|
||||||
|
"type": 1,
|
||||||
|
"route": "/na",
|
||||||
|
"orderNum": 2,
|
||||||
|
"icon": "",
|
||||||
|
"required": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 4,
|
||||||
|
"name": "DNS service insights",
|
||||||
|
"code": "explore",
|
||||||
|
"i18n": "dashboard.metricPreview.title",
|
||||||
|
"parentId": 1,
|
||||||
|
"perms": "",
|
||||||
|
"type": 1,
|
||||||
|
"route": "/dns",
|
||||||
|
"orderNum": 3,
|
||||||
|
"icon": "",
|
||||||
|
"required": "",
|
||||||
|
"children": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]`)
|
||||||
|
return menuList
|
||||||
|
},
|
||||||
|
buttonList (state) {
|
||||||
|
return state.buttonList
|
||||||
|
},
|
||||||
|
roleList (state) {
|
||||||
|
return state.roleList
|
||||||
|
}
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
loginSuccess (store, res) {
|
||||||
|
},
|
||||||
|
logoutSuccess (store, res) {
|
||||||
|
sessionStorage.removeItem('nz-username')
|
||||||
|
localStorage.removeItem('nz-username')
|
||||||
|
sessionStorage.removeItem('nz-token')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export default user
|
||||||
13
src/views/dashboards/TrafficSummary.vue
Normal file
13
src/views/dashboards/TrafficSummary.vue
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<template>
|
||||||
|
<div>hehe</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'TrafficSummary'
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
23
vue.config.js
Normal file
23
vue.config.js
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
const path = require('path')
|
||||||
|
function resolve (dir) {
|
||||||
|
return path.join(__dirname, dir)
|
||||||
|
}
|
||||||
|
module.exports = {
|
||||||
|
css: {
|
||||||
|
loaderOptions: {
|
||||||
|
// scss变量全局应用
|
||||||
|
scss: {
|
||||||
|
prependData: '@import "~@/assets/css/theme.scss";'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
sourceMap: true // 开启浏览器调试css定位
|
||||||
|
},
|
||||||
|
chainWebpack: (config) => {
|
||||||
|
config.resolve.alias // 路径别名
|
||||||
|
.set('@', resolve('./src'))
|
||||||
|
},
|
||||||
|
lintOnSave: true,
|
||||||
|
devServer: {
|
||||||
|
port: 80
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user