Merge branch 'dev-3.6' of git.mesalab.cn:nezha/nezha-fronted into dev-3.6

This commit is contained in:
18317449825
2022-11-22 19:17:12 +08:00
40 changed files with 4698 additions and 348 deletions

View File

@@ -1583,9 +1583,9 @@
}
},
"tar": {
"version": "6.1.11",
"resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz",
"integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==",
"version": "6.1.12",
"resolved": "https://registry.npmjs.org/tar/-/tar-6.1.12.tgz",
"integrity": "sha512-jU4TdemS31uABHd+Lt5WEYJuzn+TJTCBLljvIAHZOz6M9Os5pJ4dD+vRFLxPa/n3T0iEFzpi+0x1UfuDZYbRMw==",
"requires": {
"chownr": "^2.0.0",
"fs-minipass": "^2.0.0",
@@ -4103,12 +4103,12 @@
"integrity": "sha512-+TeEIee1gS5bYOiuf+PS/kp2mrXic37Hl66VY6EAfxasIk5fELTktK2oOezYed12H8w7jt3s512PpulQidPjwA=="
},
"canvas": {
"version": "2.10.1",
"resolved": "https://registry.npmjs.org/canvas/-/canvas-2.10.1.tgz",
"integrity": "sha512-29pIjn9uwTUsIgJUNd7GXxKk8sg4iyJwLm1wIilNIqX1mVzXSc2nUij9exW1LqNpis1d2ebMYfMqTWcokZ4pdA==",
"version": "2.10.2",
"resolved": "https://registry.npmjs.org/canvas/-/canvas-2.10.2.tgz",
"integrity": "sha512-FSmlsip0nZ0U4Zcfht0qBJqDhlfGuevTZKE8h+dBOYrJjGvY3iqMGSzzbvkaFhvMXiVxfcMaPHS/kge++T5SKg==",
"requires": {
"@mapbox/node-pre-gyp": "^1.0.0",
"nan": "^2.15.0",
"nan": "^2.17.0",
"simple-get": "^3.0.3"
},
"dependencies": {

4072
nezha-fronted/src/assets/css/animate.css vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,77 @@
.fileDirectory {
height: 80%;
position: absolute;
bottom: 0;
background: #1E1E1E;
box-shadow: 5px 0 3px 0 #5E5E5E;
width: 100% !important;
z-index: 10;
.file-directory-header{
display: flex;
width: 100%;
justify-content: space-between;
padding: 0 10px;
background: #1E1E1E;
box-sizing: border-box;
color: #ffffff;
i {
margin-right: 22px;
}
}
.file-directory-content{
height: calc(100% - 26px);
width: calc(100% - 15px);
overflow: auto;
}
.file-state-panel-content::-webkit-scrollbar {
width: 6px;
}
.file-directory-content::-webkit-scrollbar-thumb {
background: rgba(244,244,244,0.16);
border-radius: 4px;
border:none
}
.file-directory-content::-webkit-scrollbar-thumb:hover {
background: rgba(244,244,244,0.16);
border-radius: 4px;
border:none
}
.file-item{
font-family: Roboto-Regular;
font-size: 14px;
color: #B7B7B7;
line-height: 21px;
font-weight: 400;
margin-top: 8px;
}
.file-item{
display: flex;
padding: 0 10px;
.file-name{
width: calc(100% - 300px);
flex: 1;
}
.file-feature{
width: 100px;
flex-shrink: 1;
>.nz-icon-download1 {
margin-right: 24px;
}
}
.file-date {
width: 260px;
flex-shrink: 1;
display: flex;
justify-content: space-between;
flex-direction: row-reverse;
}
}
.file-item:hover{
background: rgba(255,134,0,0.50);
font-family: Roboto-Regular;
font-size: 14px;
color: #FF9230;
line-height: 21px;
font-weight: 400;
}
}

View File

@@ -0,0 +1,113 @@
.file-state-panel{
position: absolute;
z-index: 10;
right:0;
top:-100px;
max-height: 194px;
min-width: 96px;
width: 308px;
background: #222329;
box-shadow: 1px 1px 4px -1px #1E1E1E;
border-radius: 2px;
.file-state-panel-content::-webkit-scrollbar {
width: 6px;
}
.file-state-panel-content::-webkit-scrollbar-thumb {
background: rgba(244,244,244,0.16);
border-radius: 4px;
border:none
}
.file-state-panel-content::-webkit-scrollbar-thumb:hover {
background: rgba(244,244,244,0.16);
border-radius: 4px;
border:none
}
.file-state-panel-header{
height: 48px;
background: #222329;
box-shadow: 0 1px 0 0 #19191C;
padding: 0 20px;
line-height: 48px;
}
.file-state-panel-title{
font-size: 14px;
color: #E7EAED;
letter-spacing: 0;
font-weight: 500;
}
.file-state-panel-content {
min-height: 50px;
max-height: 146px;
line-height: 48px;
width: 100%;
overflow-y: auto;
.file-state-panel-item{
height: 48px;
width: 100%;
box-sizing: border-box;
padding: 12px 14px 0px 20px;
display: flex;
justify-content: space-around;
.item-icon{
width: 28px;
height: 28px;
background: #19191C;
border-radius: 2px;
color: #fff;
display: flex;
justify-items: center;
justify-content: center;
align-items: center;
flex-shrink:0;
}
.item-progress{
width: calc(100% - 86px);
flex: 1;
margin: 0 10px;
display: flex;
flex-direction: column;
.item-progress-top{
width: 100%;
color: #fff;
height: 15px;
line-height: 15px;
font-size: 12px;
font-weight: 400;
}
.item-progress-middle{
}
.item-progress-bottom{
display: flex;
justify-content: space-between;
font-size: 10px;
color: #B7B7B7;
line-height: 12px;
font-weight: 400;
}
}
.item-state{
flex-shrink:0;
width: 28px;
height: 28px;
color: #fff;
line-height: 28px;
}
}
}
.file-nodata{
text-align: center;
color: #b7b7b7;
padding-top: 20px;
padding-bottom: 15px;
.table-no-data__title{
text-align: center;
color: #b7b7b7;
}
}
.translationOriginUp{
transform-origin: 70% 100%
}
.translationOriginDown{
transform-origin: 70% 0%
}
}

View File

@@ -291,3 +291,11 @@ div.sp-header{
}
}
.ternimal-header{
display: flex;
width: 100%;
justify-content: space-between;
background: #101010;
box-shadow: 0 1px 0 0 #303030;
}

View File

@@ -132,7 +132,7 @@
.nz-table-list .el-table--border {
.gutter {
position: fixed;
right: 31px;
right: 21px;
height: 41px;
border-bottom: 1px solid $--border-color-light;
background-color: $--background-color-empty;
@@ -269,13 +269,16 @@
// process network vsys comment 二级页面
#processBottomTab,
#networkBottomTab,
#vsysBottomTab {
#vsysBottomTab,
#sftpBottomTab,
#terminalLogCMDTab {
.sub-container .nz-table-list {
height: 100%;
}
#assetProcessTable,
#assetNetworkTable,
#assetVsysTable {
#assetVsysTable,
#terminalLogSftpTable {
height: calc(100% - 15px) !important;
}
}
@@ -322,17 +325,14 @@
}
}
#assetNetworkTable{
.process-name{
#assetNetworkTable {
.process-name {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
#assetVsysTable{
}
// comments
#commentBottomTab {
.sub-container .nz-table-list {
@@ -431,3 +431,10 @@
box-sizing: content-box;
}
}
//record command 二级页面搜索
#terminalLogRecordTab {
.pagination-bottom{
bottom: 5px;
}
}

View File

@@ -2,6 +2,8 @@
@import './charts/chart.scss';
@import './charts/chart-list.scss';
@import './cli/webSSH.scss';
@import './cli/fileDirectory.scss';
@import './cli/fileListState.scss';
@import './common/alert/alertLabel.scss';
@import './common/alert/alertStateInfo.scss';
@import './common/alert/alertRuleInfo.scss';

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -5,6 +5,20 @@
"css_prefix_text": "nz-icon-",
"description": "",
"glyphs": [
{
"icon_id": "32964483",
"name": "File",
"font_class": "File",
"unicode": "e7ae",
"unicode_decimal": 59310
},
{
"icon_id": "32961611",
"name": "Clear",
"font_class": "Clear",
"unicode": "e7b0",
"unicode_decimal": 59312
},
{
"icon_id": "32660007",
"name": "Folder ",
@@ -12,13 +26,6 @@
"unicode": "e7ad",
"unicode_decimal": 59309
},
{
"icon_id": "32660008",
"name": "File",
"font_class": "File",
"unicode": "e7ae",
"unicode_decimal": 59310
},
{
"icon_id": "32659266",
"name": "file transfer",

File diff suppressed because one or more lines are too long

View File

@@ -11,7 +11,7 @@
}
/*---滚动条大小--*/
::-webkit-scrollbar {
width: 14px;
width: 6px;
height: 14px;
}
/*---滚动框背景样式--*/
@@ -1415,7 +1415,7 @@ li {
.dropdown-item-active {
color: $--color-primary !important;
font-weight: bold;
background-color: #FAFAFA;
background-color: $--background-color-base;
}
.el-dropdown-multi .el-dropdown-menu__item:focus {
color: $--color-text-regular;
@@ -1617,4 +1617,4 @@ li {
}
.hover .user-username{
color: $--color-primary;
}
}

View File

@@ -27,9 +27,9 @@
</span>
</el-popover>
</span>
<div class="chart-header__title" :title="nameFormate()">
<div class="chart-header__title" :title="nameFormate">
<slot name="title-icon"></slot>
{{nameFormate()}}
{{nameFormate}}
</div>
<div class="chart-header__tools">
<span v-if="chartInfo.remark" class="chart-header__tool top-tool-btn-group">

View File

@@ -40,10 +40,10 @@
</span>
</el-popover>
</span>
<div class="chart-header__title" v-if="!isGroup" :title="nameFormate()">{{nameFormate()}}</div>
<div class="chart-header__title" v-if="!isGroup" :title="nameFormate">{{nameFormate}}</div>
<div class="chart-header__title groupTitle" v-else >
<span @click="groupShow" style="cursor:pointer"> <i class="nz-icon" :class="chartInfo.param.collapse ? 'nz-icon-arrow-right': 'nz-icon-arrow-down'"></i></span>
{{nameFormate()}}
{{nameFormate}}
<span v-show="chartInfo.param.collapse" class="collapse-content">({{chartData ? chartData.length : 0}} charts)</span>
</div>
<div class="temp"></div>

View File

@@ -84,6 +84,11 @@ export default {
},
unitChange (val) {
this.$emit('unitChange', val)
}
},
computed: {
variablesArr () {
return this.$store.getters.getVariablesArr
},
// 所有类型 chart name 支持变量替换
nameFormate () {
@@ -114,11 +119,6 @@ export default {
return str
}
},
computed: {
variablesArr () {
return this.$store.getters.getVariablesArr
}
},
watch: {
isError: {
immediate: true,
@@ -127,14 +127,6 @@ export default {
this.errorText = this.chartData.filter(item => item.error).map(item => item.error).join('\n')
}
}
},
'chartInfo.repeatVariable': {
immediate: true,
handler (n) {
if (n) {
this.nameFormate()
}
}
}
}
}

View File

@@ -591,9 +591,9 @@ export default {
if (item.type === 'group' && variables.name === repeatVariable) {
if (!variables.checked.length) {
item.children.forEach(children => {
delete children.repeatIndex
delete children.repeatVariable
delete children.repeatValue
this.$delete(children, 'repeatIndex')
this.$delete(children, 'repeatVariable')
this.$delete(children, 'repeatValue')
})
return
}
@@ -616,13 +616,13 @@ export default {
// 复制数据
this.copyDataList.splice(index, 0, repeatItem)
} else {
item.repeatIndex = 0
item.repeatVariable = repeatVariable
item.repeatValue = subItem
this.$set(item, 'repeatIndex', 0)
this.$set(item, 'repeatVariable', repeatVariable)
this.$set(item, 'repeatValue', subItem)
item.children.forEach(children => {
children.repeatIndex = 0
children.repeatVariable = repeatVariable
children.repeatValue = subItem
this.$set(children, 'repeatIndex', 0)
this.$set(children, 'repeatVariable', repeatVariable)
this.$set(children, 'repeatValue', subItem)
})
this.$refs['chart' + item.id] && this.$refs['chart' + item.id][0] && this.$refs['chart' + item.id][0].getChartData()
}

View File

@@ -8,18 +8,18 @@
</style>
<template>
<div :id="'ternimalContainer'+idIndex" class="console">
<div style="display: flex;width: 100%; justify-content: space-between;background: #101010;box-shadow: 0 1px 0 0 #303030;">
<div class="ternimal-header">
<span style="color: #fff">
<div class="active-icon green-bg"></div>
{{userName}}
</span>
<span style="color: #fff">
<i class="nz-icon nz-icon-reconnect" style="margin-right: 22px"></i>
<i class="nz-icon nz-icon-SFTP" style="margin-right: 22px" @click="fileDirectoryShow = !fileDirectoryShow"></i>
<span style="color: #fff" v-show="isLogin">
<i class="nz-icon nz-icon-reconnect" style="margin-right: 22px" @click="reconnect"></i>
<i class="nz-icon nz-icon-SFTP" v-if="userName && terminalType == '1'" style="margin-right: 22px" @click="showFileDir(true)"></i>
</span>
</div>
<div :id="'terminal'+idIndex" ></div>
<fileDirectory :uuid="terminal.uuid" v-if="fileDirectoryShow" @close="fileDirectoryShow=false" style=""/>
<fileDirectory :uuid="terminal.uuid" v-show="fileDirectoryShow" @close="showFileDir(false)" :fileDirectoryShow="fileDirectoryShow" ref="fileDirectory"/>
</div>
</template>
<script>
@@ -28,10 +28,11 @@ import fileDirectory from './fileDirectory'
export default {
name: 'console',
components: {
fileDirectory,
fileDirectory
},
props: {
terminal: { },
terminalType: { default: '1' },
idIndex: {
type: Number,
default: 0
@@ -55,6 +56,7 @@ export default {
obj: {
id: 2
},
isLogin: false,
successBackContent: 'Connecting to',
failBackContent: 'Sorry',
connectFailContent: 'Connection failed',
@@ -63,7 +65,7 @@ export default {
conFinish: false,
conSuccessNum: 0,
inputSecret: false,
userName: 'root@192.168.44.36',
userName: '',
fileDirectoryShow: false
}
},
@@ -192,10 +194,18 @@ export default {
authPinTip: this.$loadsh.get(this.terminal, 'custom.authPinTip', '')
}
this.$post('/terminal/login', params).then(res => {
this.terminal.uuid = res.data.uuid
this.userName = res.data.authUsername + '@' + res.data.host
this.host = res.data.host
this.create()
this.isLogin = true
if (res.code == 200) {
this.terminal.uuid = res.data.uuid
this.userName = res.data.authUsername + '@' + res.data.host
this.host = res.data.host
this.create()
} else {
this.terminal.uuid = res.data.uuid
this.userName = res.data.authUsername + '@' + res.data.host
this.host = res.data.host
this.$message.error(res.msg)
}
})
},
create () {
@@ -335,6 +345,26 @@ export default {
}
})
})
},
reconnect () {
this.isLogin = false
this.closeSocket()
this.term.off('selection')
this.term.off('data')
this.beforeCreate()
},
showFileDir (show) {
if (JSON.stringify(show) == JSON.stringify(this.fileDirectoryShow)) {
return
}
if (show) {
this.fileDirectoryShow = show
}
let animationClass = ''
animationClass = show ? 'backInUp' : 'backOutDown'
this.animateCSS(this.$refs.fileDirectory.$el, animationClass).then((message) => {
this.fileDirectoryShow = show
})
}
},
mounted () {
@@ -347,3 +377,6 @@ export default {
}
}
</script>
<style scoped lang="scss">
</style>

View File

@@ -1,62 +1,98 @@
<template>
<div class="fileDirectory">
<div class="fileDirectory" style="width: 100% !important;transform: scale(1) !important;">
<div class="file-directory-header">
<span style="color: #fff">SFTP <span style="color: #B7B7B7">{{fileDirectory}}</span></span>
<span style="color: #fff">{{$t('terminal.sftp')}} <span style="color: #B7B7B7">{{fileDirectory}}</span></span>
<span style="color: #fff">
<i class="nz-icon nz-icon-a-newfolder" @click="newFolderBoxShow = true"></i>
<i class="nz-icon nz-icon-upload" @click="uploadFile"></i>
<i class="nz-icon nz-icon-close" @click="$emit('close')"></i>
</span>
</div>
<div class="file-directory-content">
<div v-if="fileDirectory !== '/'" @click="backFileDirectory" class="file-item"><i class="nz-icon nz-icon-a-upperlevel"/>上一级</div>
<div class="file-directory-content" v-my-loading="fileDirectoryLoading">
<div v-if="fileDirectory !== '/'" @click="backFileDirectory" class="file-item"><i class="nz-icon nz-icon-a-upperlevel" style="margin-right: 10px"/>{{$t('terminal.back')}}</div>
<div v-for="(item,index) in fileList" :key="index" class="file-item" @click="selectFile(item)">
<span>
<div class="text-ellipsis file-name">
<i class="nz-icon" :class="selIcon(item)"/>
{{item.name}}
</span>
<span>
</div>
<div class="file-feature" v-if="!item.isDir">
<i class="nz-icon nz-icon-download1" v-if="!item.isDir" @click="downloadFile(item)"></i>
<i class="nz-icon nz-icon-delete" v-if="!item.isDir" @click="delFile(item)"></i>
</span>
</div>
<div class="file-date">
<span>{{momentTz(item.cts * 1000)}}</span>
<span v-if="!item.isDir">{{bytes(item.size, 0, 0)}}</span>
</div>
</div>
</div>
<el-dialog
title='新建文件夹'
:title='$t("overall.newFolder")'
:visible.sync="newFolderBoxShow"
:modal-append-to-body="false"
:append-to-body="false"
width="30%"
:modal-append-to-body="true"
:append-to-body="true"
:width="'400px'"
>
<div>
<el-input v-model="folder" size="small"/>
<div style="display: flex; align-items: center">
<div style="width: 100px;flex-shrink: 1">{{$t('overall.folderName')}}</div>
<el-input v-model="folder" size="small" style="flex: 1"/>
</div>
<span slot="footer" class="dialog-footer">
<el-button @click="newFolder(false)"> </el-button>
<el-button type="primary" @click="newFolder(true)"> </el-button>
<el-button @click="newFolder(false)" size="small">{{$t('overall.cancel')}}</el-button>
<el-button type="primary" @click="newFolder(true)" size="small">{{$t('overall.create')}}</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import chartDataFormat from '@/components/chart/chartDataFormat'
export default {
name: 'fileDirectory',
props: {
uuid: {}
uuid: {},
fileDirectoryShow: {}
},
data () {
return {
fileDirectory: '/',
fileList: [],
newFolderBoxShow: false,
folder: ''
folder: '',
fileDirectoryLoading: false,
timer: ''
}
},
computed: {
updateUuid () {
return this.$store.getters.getUpdateUuid
},
updateIndex () {
return this.$store.getters.getUpdateIndex
}
},
mounted () {
this.init()
// this.init()
},
watch: {
fileDirectoryShow (n) {
if (n) {
this.init()
}
},
updateIndex (n) {
if (this.updateUuid == this.uuid) {
if (this.timer) {
clearTimeout(this.timer)
}
this.timer = setTimeout(() => {
this.getSftpPath(this.fileDirectory)
this.timer = null
}, 200)
}
}
},
methods: {
bytes: chartDataFormat.getUnit(7).compute,
init () {
this.getSftpPath(this.fileDirectory)
},
@@ -78,7 +114,6 @@ export default {
path: path + '/' + this.folder
}
this.$post('/terminal/sftp/mkdir', params).then(res => {
console.log(res)
this.newFolderBoxShow = false
this.getSftpPath(this.fileDirectory)
})
@@ -86,9 +121,7 @@ export default {
backFileDirectory () {
const arr = this.fileDirectory.split('/')
arr.pop()
console.log(arr, this.fileDirectory.split('/').pop())
const path = arr.join('/')
console.log(path)
this.getSftpPath(path || '/')
},
getSftpPath (path) {
@@ -96,10 +129,11 @@ export default {
uuid: this.uuid,
path: path
}
this.fileDirectoryLoading = true
this.$post('/terminal/sftp/ls', params).then(res => {
this.fileDirectoryLoading = false
this.fileDirectory = res.data.path
this.fileList = res.data.list
console.log(res)
})
},
uploadFile () {
@@ -116,28 +150,35 @@ export default {
fileLength: '',
startTime: '',
cancel: '',
axios: ''
axios: '',
timer: '',
done: 0
}
this.$store.dispatch('uploadFile', params)
},
downloadFile (item) {
const path = this.fileDirectory == '/' ? '' : this.fileDirectory
const params = {
...item,
uuid: this.uuid,
path: path + '/' + item.name,
myId: 'download' + this.uuid + new Date().getTime(),
type: 'download',
isStart: false,
isFinish: false,
total: '',
speed: '',
fileLength: '',
startTime: '',
cancel: '',
axios: ''
if (item.timer) {
clearTimeout(item.timer)
}
this.$store.dispatch('dispatchAddFileList', params)
item.timer = setTimeout(() => {
const path = this.fileDirectory == '/' ? '' : this.fileDirectory
const params = {
...item,
uuid: this.uuid,
path: path + '/' + item.name,
myId: 'download' + this.uuid + new Date().getTime(),
type: 'download',
isStart: false,
isFinish: false,
total: '',
speed: '',
fileLength: '',
startTime: '',
cancel: '',
axios: ''
}
this.$store.dispatch('dispatchAddFileList', params)
}, 300)
},
delFile (item) {
const params = {
@@ -148,6 +189,7 @@ export default {
if (res.code == 200) {
this.getSftpPath(this.fileDirectory)
} else {
this.getSftpPath(this.fileDirectory)
this.$message.error(res.msg)
}
})
@@ -168,53 +210,5 @@ export default {
</script>
<style scoped lang="scss">
.fileDirectory {
height: 80%;
position: absolute;
bottom: 0;
background: #1E1E1E;
box-shadow: 5px 0 3px 0 #5E5E5E;
width: 100%;
z-index: 10;
/deep/ .el-menu::-webkit-scrollbar-thumb {
background-color: #fff;
border-radius: 3px;
}
/deep/ .el-menu::-webkit-scrollbar-thumb:hover {
background-color: #fff;
border-radius: 3px;
}
.file-directory-header{
display: flex;
width: 100%;
justify-content: space-between;
padding: 0 10px;
background: #1E1E1E;
box-sizing: border-box;
color: #ffffff;
i {
margin-right: 22px;
}
}
.file-directory-content{
height: calc(100% - 26px);
overflow: auto;
}
.file-item{
font-family: Roboto-Regular;
font-size: 14px;
color: #B7B7B7;
line-height: 21px;
font-weight: 400;
margin-top: 8px;
}
.file-item:hover{
background: rgba(255,134,0,0.50);
font-family: Roboto-Regular;
font-size: 14px;
color: #FF9230;
line-height: 21px;
font-weight: 400;
}
}
</style>

View File

@@ -1,10 +1,10 @@
<template>
<div>
<!-- 显示进度-->
<div class="file-state-panel" v-show="fileStateBox" :style="{'top': position.top + 'px', right: '10px'}">
<div class="file-state-panel" v-show="fileStateBox && fileList.length" :style="{'top': position.top + 'px', right: '10px'}" ref="fileStatePanel" :class="position.top>0? 'translationOriginDown': 'translationOriginUp'">
<div class="file-state-panel-header">
<span class="file-state-panel-title">File transfers</span>
<i class="nz-icon nz-icon-delete"></i>
<span class="file-state-panel-title">{{$t('terminal.filetransfer')}}}</span>
<i class="nz-icon nz-icon-Clear" @click="clearFileList"></i>
</div>
<div class="file-state-panel-content" v-if="fileList.length">
<div v-for="item in fileList" :key="item.myId" class="file-state-panel-item">
@@ -12,7 +12,7 @@
<div class="item-progress">
<div class="item-progress-top text-ellipsis">{{item.name}}</div>
<div class="item-progress-middle">
<el-progress :show-text="false" :percentage="(item.fileLength/item.total) * 100" :color="customColorMethod"></el-progress>
<el-progress :show-text="false" :percentage="item.done" :color="customColorMethod"></el-progress>
</div>
<div class="item-progress-bottom">
<span>{{bytes(item.total, 0, 0)}}</span>
@@ -66,7 +66,7 @@ export default {
myFileList: [],
importFileList: [],
position: {
top: 10,
top: 50,
right: 0
},
fileStateBox: false
@@ -77,7 +77,6 @@ export default {
immediate: true,
handler (n) {
n.forEach(item => {
console.log(item.isStart)
if (!item.isStart) {
item.total = 1
item.fileLength = 0
@@ -98,7 +97,6 @@ export default {
uploadItem: {
handler (n) {
if (n && n.myId) {
console.log(this.$refs.upload)
this.$refs.upload.$children[0].$refs.input.click()
}
}
@@ -132,7 +130,7 @@ export default {
item.total = progressEvent.total
item.fileLength = progressEvent.loaded
item.speed = item.fileLength / ((new Date().getTime() - item.startTime) / 1000)
item.speed = self.bytes(item.speed, 0 , 0) + '/s'
item.speed = self.bytes(item.speed, 0, 0) + '/s'
// this.$set(this.fileList, 0,item)
}
}).then(res => {
@@ -177,6 +175,17 @@ export default {
removeRecord (item) {
if (!item.isFinish) {
item.cancel.cancel('operation canceled by the user.')
if (item.done || (item.fileLength === item.total)) { // 取消 上传 50-100
clearInterval(item.timer)
if (item.tid) {
this.$delete('/terminal/sftp/cancel/' + item.tid).then(res => {
if (res.code === 200) {
} else {
this.$message.error(res.msg)
}
})
}
}
}
this.$store.dispatch('dispatchDelFileList', item)
},
@@ -202,140 +211,68 @@ export default {
item.fileLength = progressEvent.loaded
item.speed = item.fileLength / ((new Date().getTime() - item.startTime) / 1000)
item.speed = self.bytes(item.speed, 0, 0) + '/s'
item.done = (item.fileLength / (item.total * 2)) * 100
}
}).then(response => {
console.log(response)
item.isFinish = true
if (response.code == 200) {
this.$message.success(response.msg)
// todo 上传成功后 刷新对应terminal
const res = response.data
// item.isFinish = true
if (res.code == 200) {
self.nextUpload(item, res.data.tid)
} else {
this.$message.error(response.msg)
this.$message.error(res.msg)
}
})
},
nextUpload (item, tid){
// this.$message.success(response.msg)
item.timer = setInterval(() => { // 上传 50-100
item.tid = tid
this.$get('/terminal/sftp/process/' + tid).then((res) => {
item.done = 50 + parseInt(res.data.done) / 2
item.speed = (item.fileLength + (item.done * 0.01 * item.total)) / ((new Date().getTime() - item.startTime) / 1000)
item.speed = this.bytes(item.speed, 0, 0) + '/s'
if (item.done === 100) {
item.isFinish = true
clearInterval(item.timer)
// todo 上传成功后 刷新对应terminal
this.$store.dispatch('upDateConsole', item.uuid)
}
})
}, 200)
},
fileStateShow (flag, type) {
this.fileStateBox = flag
if (flag) {
// if (type === 'top') {
// this.position = {
// top: '',
// right: ''
// }
// } else {
// this.position = {
// top: '',
// right: ''
// }
// }
if (JSON.stringify(flag) == JSON.stringify(this.fileStateBox)) {
return
}
if (flag) {
this.fileStateBox = flag
if (type === 'down') {
this.position = {
top: 45,
right: 0
}
} else {
this.position = {
top: -195,
right: 0
}
}
}
let animationClass = ''
animationClass = flag ? 'zoomIn' : 'zoomOut'
this.animateCSS(this.$refs.fileStatePanel, animationClass).then((message) => {
this.fileStateBox = flag
})
},
clearFileList () {
const arr = this.fileList.filter(item => !item.isFinish)
this.$store.dispatch('dispatchFileList', arr)
}
}
}
</script>
<style scoped lang="scss">
.file-state-panel{
position: absolute;
z-index: 10;
right:0;
top:-100px;
max-height: 194px;
min-width: 96px;
width: 308px;
background: #222329;
box-shadow: 1px 1px 4px -1px #1E1E1E;
border-radius: 2px;
/deep/ .file-state-panel-content::-webkit-scrollbar-thumb {
background-color: #fff;
border-radius: 3px;
}
/deep/ .file-state-panel-content::-webkit-scrollbar-thumb:hover {
background-color: #fff;
border-radius: 3px;
}
.file-state-panel-header{
height: 48px;
background: #222329;
box-shadow: 0 1px 0 0 #19191C;
padding: 0 20px;
line-height: 48px;
}
.file-state-panel-title{
font-size: 14px;
color: #E7EAED;
letter-spacing: 0;
font-weight: 500;
}
.file-state-panel-content {
min-height: 50px;
max-height: 146px;
line-height: 48px;
width: 100%;
overflow-y: auto;
.file-state-panel-item{
height: 48px;
width: 100%;
box-sizing: border-box;
padding: 12px 14px 0px 20px;
display: flex;
justify-content: space-around;
.item-icon{
width: 28px;
height: 28px;
background: #19191C;
border-radius: 2px;
color: #fff;
display: flex;
justify-items: center;
justify-content: center;
align-items: center;
flex-shrink:0;
}
.item-progress{
width: calc(100% - 86px);
flex: 1;
margin: 0 10px;
display: flex;
flex-direction: column;
.item-progress-top{
width: 100%;
color: #fff;
height: 15px;
line-height: 15px;
font-size: 12px;
font-weight: 400;
}
.item-progress-middle{
}
.item-progress-bottom{
display: flex;
justify-content: space-between;
font-size: 10px;
color: #B7B7B7;
line-height: 12px;
font-weight: 400;
}
}
.item-state{
flex-shrink:0;
width: 28px;
height: 28px;
color: #fff;
line-height: 28px;
}
}
}
.file-nodata{
text-align: center;
color: #b7b7b7;
padding-top: 20px;
padding-bottom: 15px;
.table-no-data__title{
text-align: center;
color: #b7b7b7;
}
}
}
</style>

View File

@@ -39,7 +39,16 @@
style="margin-top: 0px;"></div>{{item.title}}
</span>
<my-console :fontSize="fontSize" :idIndex="index" :isFullScreen="isFullScreen" :ref="'console'+index" :terminal="item.terminal" @closeConsole="removeTab" @refreshConsoleTitle="refreshTabTitle"></my-console>
<my-console
:fontSize="fontSize"
:terminalType="item.terminal.terminalType"
:idIndex="index"
:isFullScreen="isFullScreen"
:ref="'console'+index"
:terminal="item.terminal"
@closeConsole="removeTab"
@refreshConsoleTitle="refreshTabTitle"
></my-console>
</el-tab-pane>
<el-tab-pane key="add" name="addConsole" style="width: 20px">
@@ -59,8 +68,8 @@
</el-tab-pane>
</el-tabs>
<div class="console-icon">
<div class="console-title-icon" style='right: 106px;display: inline;' @click="showFileState">
<i class="nz-icon nz-icon-a-filetransfer" :title="$t('guide.webTerminal')"></i>
<div class="console-title-icon" style='right: 106px;display: inline;' @click="showFileState" v-show="fileList.length">
<i class="nz-icon nz-icon-a-filetransfer" :title="$t('terminal.filetransfer')"></i>
<span v-show="fileList.length>0" class="right-tip">{{fileList.length<=99?fileList.length:'99+'}}</span>
</div>
<i @click="minScreen" class="nz-icon nz-icon-minus console-title-icon" style='right: 76px;' :title="$t('overall.shrink')"></i>
@@ -273,12 +282,13 @@ export default {
language () { return this.$store.getters.getLanguage },
fileList () {
return this.$store.getters.getFileList
},
}
},
data () {
const termFontSize = parseInt(localStorage.getItem('termFontSize'))
return {
selectValue: 'SSH',
fileListStateType: 'down',
searchMetrics: [
{
value: 'SSH',
@@ -519,6 +529,7 @@ export default {
host: this.customConnect.host,
port: this.customConnect.port,
authType: this.customConnect.authType,
terminalType: this.customConnect.authProtocol,
authUsername: encodeURIComponent(this.customConnect.authUsername),
authPin: this.encode(this.customConnect.authPin),
authPriKey: encodeURIComponent(this.customConnect.authPriKey),
@@ -528,9 +539,16 @@ export default {
authProtocol: encodeURIComponent(this.customConnect.authProtocol)
}
}
if (id) {
this.$get('/asset/asset/' + id).then(res => {
console.terminal.terminalType = res.data.type.authProtocol
this.editableTabsValue = newTabName
this.editableTabs.push(console)
})
return
}
this.editableTabsValue = newTabName
this.editableTabs.push(console)
setTimeout(function () {
const tabScroll = document.getElementsByClassName('el-tabs__nav is-top')
const tabViewWidth = document.getElementsByClassName('el-tabs__nav-scroll')
@@ -980,12 +998,23 @@ export default {
}
},
showFileState () {
let type = 'top'
console.log( this.$refs.fileListState)
this.$refs.fileListState.fileStateShow(true, type)
let type = 'down'
const targetDiv = document.getElementById('shell-service')
let height = targetDiv.style.height
if (height) {
height = height.slice(0, -2)
if (height < 265) {
this.fileListStateType = type = 'up'
} else {
this.fileListStateType = type = 'down'
}
} else {
this.fileListStateType = type = 'down'
}
this.$refs.fileListState.fileStateShow(!this.$refs.fileListState.fileStateBox, type)
},
hideFileState () {
this.$refs.fileListState.fileStateShow(false)
this.$refs.fileListState.fileStateShow(false, this.fileListStateType)
}
},
watch: {

View File

@@ -252,13 +252,13 @@ export default {
{ prop: 'ipam', name: this.$t('ipam.subnet.ipDetails'), active: true }
],
recordRule: {
evalLog: [
{ prop: 'recordRule', name: this.$t('overall.alertRuleEvalLog'), active: true },
{ prop: 'Metrics', name: this.$t('overall.metric'), active: false }
],
Metrics: [
{ prop: 'recordRule', name: this.$t('overall.alertRuleEvalLog'), active: false },
{ prop: 'Metrics', name: this.$t('overall.metric'), active: true }
{ prop: 'Metrics', name: this.$t('overall.metric'), active: true },
{ prop: 'recordRule', name: this.$t('overall.alertRuleEvalLog'), active: false }
],
evalLog: [
{ prop: 'Metrics', name: this.$t('overall.metric'), active: false },
{ prop: 'recordRule', name: this.$t('overall.alertRuleEvalLog'), active: true }
]
},
alertSilence: [

View File

@@ -151,7 +151,6 @@ export default {
}
},
clearInput () {
console.log(666)
this.$refs.elementQuery.focus()
},
changeTime (size, unit) {

View File

@@ -126,7 +126,6 @@ export default {
}
},
clearInput () {
console.log(666)
this.$refs.elementQuery.focus()
},
changeTime (size, unit) {

View File

@@ -51,7 +51,6 @@ export default {
calcTime () {
return function (time) {
if (this.obj.startTime) {
console.log(this.utcTimeToTimezoneStr(this.obj.startTime))
const startTime = this.momentStrToTimestamp(this.utcTimeToTimezoneStr(this.obj.startTime))
if (startTime) {
const thisTime = startTime + time

View File

@@ -7,6 +7,7 @@
:targetTab="targetTab"
@changeTab="changeTab"
:title="'Session ID'"
id="terminalLogRecordTab"
>
<template v-slot:title><span :title="obj.uuid.substring(0, 8).toUpperCase()">{{obj.uuid.substring(0, 8).toUpperCase()}}</span></template>
<template v-slot>

View File

@@ -95,6 +95,22 @@ export default {
this.$copyText(txt).then(() => {
this.$message.success({ message: this.$t('overall.copySuccess') })
})
},
animateCSS (node, animation, prefix = 'animate__') {
// We create a Promise and return it
return new Promise((resolve, reject) => {
const animationName = `${prefix}${animation}`
node.classList.add(`${prefix}animated`, animationName)
// When the animation ends, we clean the classes and resolve the Promise
function handleAnimationEnd (event) {
event.stopPropagation()
node.classList.remove(`${prefix}animated`, animationName)
resolve('Animation ended')
}
node.addEventListener('animationend', handleAnimationEnd, { once: true })
})
}
}
}

View File

@@ -198,9 +198,7 @@ export default {
},
methods: {
dateChange (val) {
console.log(val)
const startTime = this.momentTz(this.momentStrToTimestamp(val))
console.log(this.stepSearchTime[1], this.stepSearchTime[0])
const end = this.momentStrToTimestamp(val) + this.momentStrToTimestamp(this.stepSearchTime[1]) - this.momentStrToTimestamp(this.stepSearchTime[0])
const endTime = this.momentTz(end, this.multipleTime)
this.$set(this.searchTime, 0, startTime)

View File

@@ -26,7 +26,7 @@
</el-select>
</el-form-item>
<!-- ChartTemplate -->
<el-form-item :label="$t('overall.chartTemp')" prop="ChartTemplate">
<!-- <el-form-item :label="$t('overall.chartTemp')" prop="ChartTemplate">
<v-selectpage
:data="chartlList"
:tb-columns="ChartSearchShowFields"
@@ -45,6 +45,25 @@
@values="(data) => {editModel.chartIds = data.map(d => d.id).join(',')}"
:result-format="resultFormat"
></v-selectpage>
</el-form-item> -->
<!-- DashboardTemplate -->
<el-form-item :label="$t('model.dashboardtemplate')" prop="DashboardTemplate">
<v-selectpage
:data="dashboardList"
:tb-columns="DashboardSearchShowFields"
:params="{
varType: 1,}"
:multiple="false"
:language="language"
title="DashboardSearch"
key-field="id"
:width="640"
v-model="editModel.panelId"
show-field="name"
class="form-control"
@values="(data) => {editModel.panelId = data.map(d => d.id).join(',')}"
:result-format="resultFormat"
></v-selectpage>
</el-form-item>
<!-- sys object id -->
<el-form-item :label="$t('config.model.sysObjectId')" prop="sysObjectId">
@@ -100,11 +119,17 @@ export default {
brandList: [], // brand 列表数据
editModule: {},
typeDataList: [],
chartlList: [], // chart 列表数据
ChartSearchShowFields: [ // ChartSearch 下拉搜索表头
// chartlList: [], // chart 列表数据
dashboardList: [], // dashboard 列表数据
// ChartSearchShowFields: [ // ChartSearch 下拉搜索表头
// { title: 'ID', data: 'id' },
// { title: this.$t('overall.name'), data: 'name', key: 'name' },
// { title: this.$t('overall.type'), data: 'type', key: 'type' },
// { title: this.$t('overall.remark'), data: 'remark', key: 'remark' }
// ],
DashboardSearchShowFields: [ // DashboardSearch 下拉搜索表头
{ title: 'ID', data: 'id' },
{ title: this.$t('overall.name'), data: 'name', key: 'name' },
{ title: this.$t('overall.type'), data: 'type', key: 'type' },
{ title: this.$t('overall.remark'), data: 'remark', key: 'remark' }
],
url: 'asset/model',
@@ -134,13 +159,15 @@ export default {
handler (n) {
this.isEdit = true
this.editModel = JSON.parse(JSON.stringify(n))
this.editModel.panelId = n.panelId ? n.panelId + '' : ''
}
}
},
created () {
this.getBrandList()
this.modelTypeList()
this.ChartTemplateList()
// this.ChartTemplateList()
this.DashboardTemplateList ()
},
methods: {
clickOutside () {
@@ -154,8 +181,12 @@ export default {
saveModel () {
this.$refs.modelForm.validate((valid) => {
if (valid) {
const params = {
...this.editModel,
panelId: Number(this.editModel.panelId),
}
if (this.editModel.id) {
this.$put(this.url, this.editModel).then(res => {
this.$put(this.url, params).then(res => {
this.prevent_opt.save = false
if (res.code === 200) {
this.$message({ duration: 2000, type: 'success', message: this.$t('tip.saveSuccess') })
@@ -165,7 +196,7 @@ export default {
}
})
} else {
this.$post(this.url, this.editModel).then(res => {
this.$post(this.url, params).then(res => {
this.prevent_opt.save = false
if (res.code === 200) {
this.$message({ duration: 2000, type: 'success', message: this.$t('tip.saveSuccess') })
@@ -225,10 +256,16 @@ export default {
return assetData
}
},
/* 获取chart列表数据 */
ChartTemplateList () {
this.$get('visual/panel/chart', { pageSize: -1, varType: 1, panelId: 0, groupId: 0, returnChildren: 0 }).then(res => {
this.chartlList = res.data.list
// /* 获取chart列表数据 */
// ChartTemplateList () {
// this.$get('visual/panel/chart', { pageSize: -1, varType: 1, panelId: 0, groupId: 0, returnChildren: 0 }).then(res => {
// this.chartlList = res.data.list
// })
// },
/* 获取Dashboard列表数据 */
DashboardTemplateList () {
this.$get('visual/panel', { pageSize: -1, varType: 1}).then(res => {
this.dashboardList = res.data.list
})
},
modelTypeList () {

View File

@@ -101,7 +101,7 @@ export default {
const vars = this.$store.getters.getVariablesArr.map(item => {
return {
name: item.name,
value: item.checked.join('|')
values: item.checked
}
})
const params = {

View File

@@ -203,7 +203,7 @@ export default {
}
},
downloadBackup (backup) {
this.$emit('loading', true)
this.$store.dispatch('dispatchHomeLoading', true)
axios.get('/sys/backup/download?filename=' + backup.fileName, { responseType: 'blob' }).then(data => {
let fileName = new Date().getTime() + '.txt'
const disposition = data.headers['content-disposition']
@@ -225,7 +225,7 @@ export default {
link.click()
window.URL.revokeObjectURL(link.href)
}
this.$emit('loading', false)
this.$store.dispatch('dispatchHomeLoading', false)
})
}
},

View File

@@ -108,7 +108,7 @@
{{ $t("overall.option") }}
</div>
<div slot-scope="scope" class="table-operation-items">
<button class="table-operation-item" @click="showBottomBox('recordRule', scope.row)" :title="$t('overall.view')"><i class="nz-icon nz-icon-view1"></i></button>
<button class="table-operation-item" @click="showBottomBox('Metrics', scope.row)" :title="$t('overall.view')"><i class="nz-icon nz-icon-view1"></i></button>
<el-dropdown size="medium" v-has="['record_rule_edit','record_rule_delete']" trigger="click" @command="tableOperation">
<div class="table-operation-item table-operation-item--more" :title="$t('overall.moreOperations')">
<i class="nz-icon nz-icon-more3"></i>

View File

@@ -18,6 +18,7 @@
:fixed="item.fixed"
:label="item.label"
:min-width="`${item.minWidth}`"
:width="`${item.Width}`"
:prop="item.prop"
:resizable="true"
:sort-orders="['ascending', 'descending']"
@@ -33,6 +34,13 @@
<template v-if="item.prop === 'id'">
{{scope.row[item.prop] ? scope.row[item.prop] : '-'}}
</template>
<template v-else-if="item.prop === 'type'">
<div v-if="scope.row[item.prop] == 1">{{$t("overall.uploadFiles")}}</div>
<div v-else-if="scope.row[item.prop] == 2">{{$t("overall.downloadFile")}}</div>
<div v-else-if="scope.row[item.prop] == 3">{{$t("overall.newFolder")}}</div>
<div v-else-if="scope.row[item.prop] == 4">{{$t("overall.deleteFiles")}}</div>
<div v-else>{{'-'}}</div>
</template>
<template v-else-if="item.prop === 'ts'">
{{scope.row[item.prop] ? momentTz(scope.row[item.prop]) : '-'}}
</template>
@@ -68,19 +76,19 @@ export default {
label: 'ID',
prop: 'id',
show: true,
width: 120,
width: 80,
sortable: 'custom'
},
{
label: this.$t('overall.name'),
prop: 'name',
show: true,
width: 150,
minWidth: 150,
sortable: 'custom'
}, {
label: this.$t('overall.type'),
prop: 'type',
width: 150,
width: 80,
show: true,
sortable: 'custom'
},
@@ -88,7 +96,7 @@ export default {
label: this.$t('overall.time'),
prop: 'ts',
show: true,
width: 150,
width: 80,
sortable: 'custom'
}
]

View File

@@ -1,5 +1,5 @@
<template>
<div class="system backup" v-my-loading.dark="loading">
<div class="system backup">
<div class="system-config-form system-config-backup">
<div class="system-title">{{ $t("backup.configurations") }}</div>
<nz-data-list
@@ -39,7 +39,6 @@
key="backups2"
:table-data2="tableData2"
@getTableData="getTableData"
@loading="load"
>
</backups-table>
</template>
@@ -96,8 +95,7 @@ export default {
// }
],
rightBoxshow: false,
isRefresh: '',
loading: false
isRefresh: ''
}
},
provide () {
@@ -186,9 +184,6 @@ export default {
}
this.rightBoxshow = true
},
load (flag) {
this.loading = flag
},
closeRightBox (refresh) {
if (refresh) {
this.getTableData('backup')

View File

@@ -103,7 +103,8 @@ export default {
blankObject: { // 空白对象
id: '',
name: '',
chartIds: '',
// chartIds: '',
panelId:'',
sysObjectId: '',
type: { id: '', name: '' },
remark: ''

View File

@@ -2,6 +2,7 @@
// import 'element-ui/lib/theme-chalk/index.css'
import 'xterm/dist/xterm.css'
import '@/assets/css/main.scss'
import '@/assets/css/animate.css'
import '@/assets/css/font/iconfont.js'
import 'intro.js/introjs.css'
import ElementUI from 'element-ui'

View File

@@ -75,10 +75,9 @@ axios.interceptors.request.use(
}
} else if (config.method === 'post' || config.method === 'put') {
try {
console.log(config.data.name)
let params = config.data
if ((typeof params == 'string') && params.constructor == String) {
params = JSON.parse( config.data)
params = JSON.parse(config.data)
}
Object.keys(params).forEach(key => {
if ((typeof params[key] == 'string') && params[key].constructor == String) {

View File

@@ -1,14 +1,15 @@
const terminalFile = {
state: {
fileList: [],
uploadItem: {}
uploadItem: {},
updateUuid: '', // 根据uuid的变化 判断页面是否需要刷新
updateIndex: 1
},
mutations: {
setFileList (state, arr) {
state.fileList = arr
},
addFileList (state, item) {
console.log(item)
state.fileList.push(item)
},
delFileList (state, item) {
@@ -16,6 +17,13 @@ const terminalFile = {
},
setUploadItem (state, item) {
state.uploadItem = item
},
setUpdateUuid (state, uuid) {
state.updateUuid = uuid
state.updateIndex++
if (state.updateIndex == 10) {
state.updateIndex = 1
}
}
},
getters: {
@@ -24,6 +32,12 @@ const terminalFile = {
},
getUploadItem (state) {
return state.uploadItem
},
getUpdateUuid (state) {
return state.updateUuid
},
getUpdateIndex (state) {
return state.updateIndex
}
},
actions: {
@@ -38,6 +52,9 @@ const terminalFile = {
},
uploadFile (store, item) {
store.commit('setUploadItem', item)
},
upDateConsole (store, uuid) {
store.commit('setUpdateUuid', uuid)
}
}
}