NEZ-3486 feat: Collectors页面(原Agents)开发

This commit is contained in:
zyh
2024-06-04 09:27:19 +08:00
parent b71dfb9a0a
commit 2555a35df4
7 changed files with 835 additions and 50 deletions

View File

@@ -2,7 +2,6 @@
.nz-table-list {
.el-table__row {
td:last-of-type {
// border-bottom: 1px solid $--border-color-light !important;
border: none !important;
}
}
@@ -11,10 +10,6 @@
.table-operation-items {
display: flex;
justify-content: center;
.table-operation-item.delete-button {
background: $--background-color-2 !important;
border: 1px solid $--border-color-base !important;
}
.table-operation-edit {
background: $--background-color-2;
border: 1px solid $--border-color-base;
@@ -25,16 +20,6 @@
font-size: 12px !important;
}
}
.table-operation-del {
background: $--background-color-2;
border: 1px solid $--border-color-base;
border-radius: 2px;
width: 22px;
height: 22px;
i {
font-size: 12px !important;
}
}
.table-operation-button {
border: none;
background-color: $--color-primary;
@@ -59,9 +44,6 @@
.nz-icon-shujubeifenhuifu::before {
font-size: 12px;
}
.el-table--border td:first-child .cell {
padding-left: 20px !important;
}
.el-table__empty-block {
.el-table__empty-text {

View File

@@ -43,3 +43,104 @@
color: $--color-text-primary;
text-align: center;
}
.collectors{
display: flex;
flex-direction: column;
background-color: $--background-color-empty;
.system-title {
font-family: Roboto-Medium;
font-size: 14px;
color: $--license-left-title-color;
letter-spacing: 0;
font-weight: 700;
margin-left: 0;
margin-bottom: 0px;
padding-left: 20px;
padding-top: 20px;
}
.list-page{
flex: 1;
}
.collectors-settingTable{
&.list-page{
height: unset;
flex: unset;
background: unset;
padding: 0;
padding-top: 20px;
.nz-table-list{
height: unset;
}
}
.el-table{
flex: unset;
td {
padding: 8px 0;
border-bottom: 1px solid $--border-color-light;
border-right: none !important;
}
tr {
background-color: $--table-row-background-color;
}
th {
border-color: $--border-color-light;
padding: 8px 0;
}
thead {
color: $--color-text-primary;
}
.cell{
padding: 0 20px !important;
}
.el-table__fixed-body-wrapper {
td:not(.is-hidden) {
border-left: 1px solid $--border-color-light;
}
}
.el-table__fixed-header-wrapper {
th:not(.is-hidden) {
border-left: 1px solid $--border-color-light;
}
th:last-of-type {
border-right: none !important;
}
}
.el-table--border:not(.chart-table)::after, .el-table--group:not(.chart-table)::after {
width: 0;
}
.table-operation-items {
display: flex;
justify-content: center;
.table-operation-edit {
background: $--background-color-2;
border: 1px solid $--border-color-base;
border-radius: 2px;
width: 30px;
height: 22px;
i {
font-size: 12px !important;
}
}
.table-operation-button {
border: none;
background-color: $--color-primary;
color: $--button-primary-color;
display: flex;
text-align: center;
line-height: 22px;
justify-content: center;
align-items: center;
height: 22px;
width: 30px;
padding: 0 5px 0 5px;
margin-right: 10px;
cursor: pointer;
i.nz-icon-beifen.nz-icon::before {
font-size: 12px;
}
}
}
}
}
}

View File

@@ -1,21 +1,20 @@
.system.backup {
box-sizing: border-box;
height: 100% !important;
padding-top: 40px;
padding: 20px 0;
.system-config-backup {
width: 100% !important;
}
.system-config-form {
padding-bottom: 10px;
.system-title {
font-family: Roboto-Medium;
font-size: 14px;
color: $--license-left-title-color;
letter-spacing: 0;
font-weight: 700;
margin-top: 20px;
margin-left: 10px;
margin-bottom: -2px;
margin-left: 0;
margin-bottom: 0px;
padding-left: 20px;
}
#modelTable {
@@ -23,7 +22,6 @@
.main-container {
border: none;
padding: inherit;
.el-table__header-wrapper {
.table-operation-items {
.items-button {
@@ -180,33 +178,13 @@
}
}
}
}
}
.el-table_1_column_1 .cell{
padding-left: 20px !important;
}
.el-table_2_column_5 .cell{
padding-left: 20px !important;
}
.el-table--border th:first-child .cell {
padding-left: 20px !important;
}
.el-checkbox-button__inner{
background-color:$--background-color-empty;
}
.el-checkbox-button.is-checked{
border: 1px solid #fbb569 !important;
}
.el-checkbox-button.is-checked .el-checkbox-button__inner {
color: #fff;
background-color: #FA901C;
border-color: #FA901C;
// margin-right: -1px;
-webkit-box-shadow: -1px 0 0 0 #fcbc77;
box-shadow: -1px 0 0 0 #fcbc77;
border-radius: 0;
.el-table .cell{
padding: 0 20px !important;
}
.nz-icon-delete:before{
color: $--color-text-regular;
}

View File

@@ -0,0 +1,122 @@
<template>
<div v-clickoutside="{obj: editBox, func: esc}" class="right-box">
<div class="right-box__header">
<div class="header__title">{{$t('config.edit_APM_settings')}}</div>
<div class="header__operation">
<span v-cancel="{obj: editBox, func: esc}"><i class="nz-icon nz-icon-close" :title="$t('overall.close')"></i></span>
</div>
</div>
<div class="right-box__container">
<div class="container__form">
<el-form ref="form" :model="editBox" :rules="rules" label-position="top" label-width="120px">
<!-- asset_ping_interval -->
<el-form-item :label="$t('overall.name')" prop="asset_ping_interval">
<el-input v-model.number="editBox.asset_ping_interval" size="small">
<template slot="append"><span>{{$t('alert.config.second')}}</span></template>
</el-input>
</el-form-item>
<!-- default_scrape_interval -->
<el-form-item :label="$t('config.default_scrape_interval')" prop="default_scrape_interval">
<el-input v-model.number="editBox.default_scrape_interval" size="small">
<template slot="append"><span>{{$t('alert.config.second')}}</span></template>
</el-input>
</el-form-item>
<!-- metrics_storage_retention -->
<el-form-item :label="$t('config.metric_retention_days')" prop="metrics_storage_retention">
<el-input v-model.number="editBox.metrics_storage_retention" size="small">
<template slot="append"><span>{{$t('config.system.basic.day')}}</span></template>
</el-input>
</el-form-item>
<!-- logs_storage_retention -->
<el-form-item :label="$t('config.log_retention_days')" prop="logs_storage_retention">
<el-input v-model.number="editBox.logs_storage_retention" size="small">
<template slot="append"><span>{{$t('config.system.basic.day')}}</span></template>
</el-input>
</el-form-item>
</el-form>
</div>
</div>
<div class="right-box__footer">
<button id="asset-edit-cancel" v-cancel="{obj: editBox, func: esc}" class="footer__btn footer__btn--light">
<span>{{$t('overall.cancel')}}</span>
</button>
<button id="asset-edit-save" :class="{'footer__btn--disabled': prevent_opt.save}" :disabled="prevent_opt.save" class="footer__btn" @click="save">
<span>{{$t('overall.save')}}</span>
</button>
</div>
</div>
</template>
<script>
import editRigthBox from '../mixin/editRigthBox'
export default {
name: 'configurationBox',
props: {
obj: {
type: Object
}
},
mixins: [editRigthBox],
data () {
return {
url: '/sys/config/monitor',
editBox: {
asset_ping_interval: '',
default_scrape_interval: '',
metrics_storage_retention: '',
logs_storage_retention: ''
},
rules: {
asset_ping_interval: [{ required: true, message: this.$t('validate.required'), trigger: 'blur' }],
default_scrape_interval: [{ required: true, message: this.$t('validate.required'), trigger: 'blur' }],
metrics_storage_retention: [{ required: true, message: this.$t('validate.required'), trigger: 'blur' }],
logs_storage_retention: [{ required: true, message: this.$t('validate.required'), trigger: 'blur' }]
}
}
},
methods: {
clickOutside () {
this.esc(false)
},
/* 关闭弹框 */
esc (refresh) {
this.prevent_opt.save = false
this.$emit('close', refresh)
},
save () {
if (this.prevent_opt.save) {
return
}
this.prevent_opt.save = true
this.$refs.form.validate((valid) => {
if (valid) {
const params = {
...this.editBox
}
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') })
this.esc(true)
} else {
this.$message.error(res.msg)
}
})
} else {
this.prevent_opt.save = false
}
})
}
},
watch: {
obj: {
immediate: true,
deep: true,
handler (n) {
this.isEdit = true
this.editBox = this.$lodash.cloneDeep(n)
}
}
}
}
</script>

View File

@@ -24,7 +24,7 @@
</div>
<div class="system-config-form system-config-backup">
<div class="system-title">{{ $t("backup.recent") }}</div>
<div class="system-title" style="margin-top:20px;">{{ $t("backup.recent") }}</div>
<nz-data-list
ref="dataList"
id="modelTable2"

View File

@@ -0,0 +1,602 @@
<template>
<div class="collectors">
<div class="system-title">{{ $t("buttons.system.monitor") }}</div>
<div class="collectors-settingTable list-page">
<div class="nz-table-list">
<el-table :data="configurationData" border>
<el-table-column
class="table-column__head"
v-for="(item, index) in tableTitle"
:key="`col-${index}-${item.prop}`"
:width="`${item.width}`"
:min-width="`${item.minWidth}`"
:label="item.label"
:prop="item.prop"
:resizable="true"
>
<template slot="header">
<span class="data-column__span">{{ item.label }}</span>
<div class="col-resize-area"></div>
</template>
<template slot-scope="scope" :column="item">
<span v-if="item.prop === 'asset_ping_interval' || item.prop === 'default_scrape_interval'">
{{ scope.row[item.prop] }}
<span>{{$t('alert.config.second')}}</span>
</span>
<span v-if="item.prop === 'metrics_storage_retention' || item.prop === 'logs_storage_retention'">
{{ scope.row[item.prop] }}
<span>{{$t('config.system.basic.day')}}</span>
</span>
</template>
</el-table-column>
<el-table-column :resizable="false" fixed="right" width="165px">
<div slot-scope="scope">
<div class="table-operation-items">
<button
class="table-operation-edit"
style="cursor: pointer"
@click="configurationEdit()"
:title="$t('config.edit_APM_settings')"
>
<i class="nz-icon-gear nz-icon"></i>
</button>
</div>
</div>
</el-table-column>
<template slot="empty">
<div class="table-no-data">
<svg class="icon" aria-hidden="true">
<use xlink:href="#nz-icon-no-data-list"></use>
</svg>
<div class="table-no-data__title">No results found</div>
</div>
</template>
</el-table>
</div>
</div>
<div class="system-title">{{ $t("overall.collectors") }}</div>
<nz-data-list
ref="dataList"
:api="url"
:custom-table-title.sync="tools.customTableTitle"
:from="fromRoute.agent"
:layout="['searchInput', 'elementSet', 'pagination']"
:search-msg="searchMsg"
@search="search"
>
<template v-slot:top-tool-right>
<button
id="prom-add"
v-has="'agent_add'"
:title="$t('overall.createPrometheusServer')"
class="top-tool-btn margin-r-10"
type="button"
@click="add"
>
<i class="nz-icon-create-square nz-icon"></i>
</button>
<button
id="load-agent"
class="top-tool-btn margin-r-10"
type="button"
@click="toDownloadAgent"
v-has="'agent_edit'"
:title="$t('overall.download')"
>
<i class="nz-icon-download nz-icon"></i>
</button>
<delete-button
ref="deleteButton"
:single="false"
:from="'agent'"
:forceDeleteShow="true"
id="promserver-list-batch-delete"
v-has="'agent_delete'"
:delete-objs="batchDeleteObjs"
api="agent"
@after="getTableData"
@before="delFlag = true"
></delete-button>
</template>
<template v-slot:default="slotProps">
<agent-table
ref="dataTable"
:orderByFa="orderBy"
v-my-loading="tools.loading"
:loading="tools.loading"
:api="url"
:custom-table-title="tools.customTableTitle"
:height="mainTableHeight"
:table-data="tableData"
@del="del"
@edit="edit"
@copy="copy"
@orderBy="tableDataSort"
@reload="getTableData"
@selectionChange="selectionChange"
@showBottomBox="
(targetTab, object) => {
$refs.dataList.showBottomBox(targetTab, object);
}
"
></agent-table>
</template>
<!-- 分页组件 -->
<template v-slot:pagination>
<Pagination
ref="Pagination"
:pageObj="pageObj"
:tableId="tableId"
@pageNo="pageNo"
@pageSize="pageSize"
></Pagination>
</template>
</nz-data-list>
<transition name="right-box">
<agent-box
v-if="rightBox.show"
:agent="object"
@close="closeRightBox"
></agent-box>
</transition>
<transition name="right-box">
<configuration-box
v-if="configurationBox"
:obj="configurationObject"
@close="closeConfiguration"
></configuration-box>
</transition>
<el-dialog
:title="$t('guide.downloadAgent')"
:visible.sync="showAgentDownload"
:append-to-body="false"
class="nz-dialog agent-dialog no-transform-dialog"
width="800px"
@close="closeDialog"
>
<div class="agent-box">
<el-form
v-model="agentParam"
class="right-box-form right-box-form-left"
label-position="top"
label-width="120px"
size="small"
style="width: 100%"
>
<div class="right-box-sub-title">{{ $t("overall.download") }}</div>
<div style="margin-bottom: 20px; width: 100%"></div>
<el-form-item
:label="$t('config.agent.agent.osType')"
class="half-form-item"
prop="osType"
>
<el-select
v-model="agentParam.osType"
class="right-box-row-with-btn"
popper-class="right-box-select-top right-public-box-dropdown-top"
placeholder=""
:popper-append-to-body="false"
>
<el-option
v-for="item in osTypes"
:key="item.name"
:label="item.label"
:value="item.name"
>
<span class="panel-dropdown-label-txt">{{ item.label }}</span>
</el-option>
</el-select>
</el-form-item>
<el-form-item class="half-form-item" style="width: 300px">
<button
:class="{ 'nz-btn-disabled': downloadAgentFlag }"
:disabled="downloadAgentFlag"
class="nz-btn nz-btn-size-normal nz-btn-style-normal"
type="button"
@click="downloadAgent"
>
{{ $t("overall.download") }}
</button>
<span class="downloading" v-if="downloadAgentFlag">{{
$t("overall.downloading")
}}</span>
</el-form-item>
<div class="right-box-sub-title">
{{ $t("config.agent.agent.autoScript") }}
</div>
<div style="margin-bottom: 20px; width: 100%"></div>
<el-form-item
:label="$t('overall.dc')"
class="half-form-item"
prop="dc"
>
<el-select
v-model="agentParam.dc"
class="right-box-row-with-btn"
popper-class="right-box-select-top right-public-box-dropdown-top"
placeholder=""
:popper-append-to-body="false"
>
<el-option
v-for="item in allDc"
:key="item.id"
:label="item.name"
:value="item.id"
>
<span class="panel-dropdown-label-txt">{{ item.name }}</span>
</el-option>
</el-select>
</el-form-item>
<el-form-item
:label="$t('config.agent.agent.type')"
class="half-form-item"
prop="type"
>
<el-select
v-model="agentParam.type"
class="right-box-row-with-btn"
popper-class="right-box-select-top right-public-box-dropdown-top"
placeholder=""
:popper-append-to-body="false"
>
<el-option
v-for="item in agent2.theData"
:key="item.value"
:label="$t(item.label)"
:value="item.value"
:disabled="federationEnabled && item.value == 2"
>
<span class="panel-dropdown-label-txt">{{ item.label }}</span>
</el-option>
</el-select>
</el-form-item>
<el-form-item label="CURL" v-if="loadFinish" class="download-url">
<el-input id="download-url-curl" :disabled="true" v-model="curlUrl">
<el-popover
:content="$t('overall.copySuccess')"
placement="top"
trigger="manual"
v-model="curlVisible"
popper-class="small-pop"
slot="suffix"
@after-enter="popShow('curl')"
style="cursor: pointer"
>
<i
v-if="curlUrl"
slot="reference"
class="nz-icon nz-icon-override"
@click="copyUrl(curlUrl)"
:title="$t('overall.copyText')"
></i>
</el-popover>
</el-input>
</el-form-item>
<el-form-item label="Wget" v-if="loadFinish" class="download-url">
<el-input id="download-url-wget" :disabled="true" v-model="wgetUrl">
<el-popover
:content="$t('overall.copySuccess')"
placement="top"
trigger="manual"
v-model="wgetVisible"
popper-class="small-pop"
slot="suffix"
@after-enter="popShow('wget')"
style="cursor: pointer"
>
<i
v-if="wgetUrl"
slot="reference"
class="nz-icon nz-icon-override"
@click="copyUrl(wgetUrl)"
:title="$t('overall.copyText')"
></i>
</el-popover>
</el-input>
</el-form-item>
</el-form>
</div>
</el-dialog>
</div>
</template>
<script>
import deleteButton from '@/components/common/deleteButton'
import agentBox from '@/components/common/rightBox/agentBox'
import configurationBox from '@/components/common/rightBox/configurationBox'
import { agent, agent2 } from '@/components/common/js/constants'
import nzDataList from '@/components/common/table/nzDataList'
import dataListMixin from '@/components/common/mixin/dataList'
import agentTable from '@/components/common/table/settings/agentTable'
import axios from 'axios'
export default {
name: 'agent',
components: {
nzDataList,
agentBox,
agentTable,
deleteButton,
configurationBox
},
mixins: [dataListMixin],
computed: {
wgetUrl () {
return (
'wget -qO- --no-check-certificate --header="Authorization:' +
this.token +
'" ' +
this.ipAddr +
'/agent/' +
this.agentParam.dc +
'/' +
this.agentParam.type +
'/install.sh | sudo -E bash'
)
},
curlUrl () {
return (
'curl -o- -k -H "Authorization:' +
this.token +
'" ' +
this.ipAddr +
'/agent/' +
this.agentParam.dc +
'/' +
this.agentParam.type +
'/install.sh | sudo -E bash'
)
}
},
data () {
return {
url: 'agent',
tableId: 'promTable', // 需要分页的table的id用于记录每页数量
detailType: 'list',
blankObject: {
id: '',
name: '',
host: '',
port: 10090,
dc: { id: '', name: '', location: '' },
protocol: 'http'
},
agent: agent,
agent2: agent2,
dcData: [],
searchMsg: {
// 给搜索框子组件传递的信息
zheze_none: true,
searchLabelList: [
{
name: 'ID',
type: 'input',
label: 'ids',
disabled: false
},
{
name: this.$t('overall.name'),
type: 'input',
label: 'name',
disabled: false
},
{
name: this.$t('overall.dc'),
type: 'dc',
label: 'dcIds',
disabled: false
},
{
name: this.$t('overall.type'),
type: 'select',
label: 'promType',
readonly: true,
disabled: false
},
{
name: this.$t('overall.state'),
type: 'select',
label: 'promState',
readonly: true,
disabled: false
},
{
name: 'IP',
type: 'input',
label: 'host',
disabled: false
}
]
},
promServerType: null,
showAgentDownload: false,
token: '',
ipAddr: '',
allDc: [],
loadFinish: false,
osTypes: [
{
label: 'Centos',
name: 'centos'
}
// {
// label: 'Ubuntu',
// name: 'ubuntu'
// }
],
agentParam: {
osType: 'centos',
dc: '',
type: 1
},
wgetVisible: false,
curlVisible: false,
federationEnabled: !Number(
localStorage.getItem('nz-prometheus-federation-enabled')
),
downloadAgentFlag: false,
tableTitle: [
{
label: this.$t('config.system.basic.assetPingInterval'),
prop: 'asset_ping_interval'
},
{
label: this.$t('config.default_scrape_interval'),
prop: 'default_scrape_interval'
},
{
label: this.$t('config.metric_retention_days'),
prop: 'metrics_storage_retention'
},
{
label: this.$t('config.log_retention_days'),
prop: 'logs_storage_retention'
}
],
configurationData: [
{
asset_ping_interval: '',
default_scrape_interval: '',
metrics_storage_retention: '',
logs_storage_retention: ''
}
],
configurationObject: {},
configurationBox: false
}
},
methods: {
closeConfiguration (refresh) {
if (refresh) {
this.getConfiguration()
}
this.configurationBox = false
},
configurationEdit () {
this.$get('/sys/config/monitor').then(res => {
if (res.code === 200) {
this.configurationObject = res.data
this.configurationBox = true
}
})
},
getConfiguration () {
this.$get('/sys/config/monitor').then((res) => {
if (res.code == 200) {
if (res.data) {
this.$set(this.configurationData, 0, res.data)
}
}
})
},
toDownloadAgent: function () {
this.agentParam = {
osType: 'centos',
dc: '',
type: 1
}
this.getAllDc()
this.showAgentDownload = true
this.token = localStorage.getItem('nz-token')
axios.get('/healthy').then((response) => {
const url = response.request.responseURL
this.ipAddr = url.split('/healthy')[0]
})
},
closeDialog: function () {
this.showAgentDownload = false
},
getAllDc () {
this.$get('dc?pageSize=-1').then((response) => {
this.tools.loading = false
if (response.code === 200) {
this.allDc = response.data.list
if (this.allDc && this.allDc.length > 0) {
this.loadFinish = true
this.agentParam.dc = this.allDc[0].id
}
}
})
},
copyUrl (txt) {
this.$copyText(txt).then(() => {
this.$message.success({ message: this.$t('overall.copySuccess') })
})
},
popShow: function (where) {
const self = this
if (where == 'curl') {
const timeout = setTimeout(() => {
self.curlVisible = false
clearTimeout(timeout)
}, 1000)
} else {
const timeout = setTimeout(() => {
self.wgetVisible = false
clearTimeout(timeout)
}, 1000)
}
},
downloadAgent: function () {
this.downloadAgentFlag = true
axios
.get('agent/download?os=' + this.agentParam.osType, {
responseType: 'blob'
})
.then((data) => {
this.downloadAgentFlag = false
let fileName = 'confagent'
const disposition = data.headers['content-disposition']
if (disposition) {
fileName = disposition.split(';')[1].split('filename=')[1]
}
// 由于ie不支持download属性故需要做兼容判断
if (navigator.appVersion.toString().indexOf('.NET') > 0) {
// ie独有的msSaveBlob属性data.data为Blob文件流
window.navigator.msSaveBlob(data.data, fileName)
} else {
// 以下流程即为文章开始的下载流程
const url = window.URL.createObjectURL(data.data)
const link = document.createElement('a')
link.style.display = 'none'
link.href = url
link.download = fileName
document.body.appendChild(link)
link.click()
window.URL.revokeObjectURL(link.href)
}
})
.catch(() => {
this.downloadAgentFlag = false
})
}
},
mounted () {
this.getConfiguration()
},
watch: {
$route: {
immediate: true,
handler () {
// 是否弹出侧滑
const add = this.$route.query.add
const download = this.$route.query.download
if (add) {
if (add === 'agent') {
this.add()
const newQuery = JSON.parse(JSON.stringify(this.$route.query)) // 深拷贝
delete newQuery.add
this.$router.replace({ query: newQuery })
}
}
if (download) {
this.toDownloadAgent()
const newQuery = JSON.parse(JSON.stringify(this.$route.query)) // 深拷贝
delete newQuery.download
this.$router.replace({ query: newQuery })
}
}
}
}
}
</script>

View File

@@ -66,7 +66,7 @@ export default new Router({
},
{
path: '/agent',
component: resolve => require(['@/components/page/config/agent'], resolve)
component: resolve => require(['@/components/page/config/collectors'], resolve)
},
{
path: '/recordRule',