diff options
| author | chenjinsong <[email protected]> | 2021-04-08 20:28:54 +0800 |
|---|---|---|
| committer | chenjinsong <[email protected]> | 2021-04-08 20:28:54 +0800 |
| commit | 527c189ced8f569a96045689e12c79ac6f53b143 (patch) | |
| tree | e3b523726a833287b2bf7f9b227548e2ea0269dc /nezha-fronted/src/components/common | |
| parent | f259ed27507d30ebb0d345cac732f3c64ec2bc8a (diff) | |
perf: settings列表改版(细节未调整)codeCheck
Diffstat (limited to 'nezha-fronted/src/components/common')
13 files changed, 863 insertions, 54 deletions
diff --git a/nezha-fronted/src/components/common/bottomBox/bottomBox.vue b/nezha-fronted/src/components/common/bottomBox/bottomBox.vue index f73d74ad8..b9dd894b0 100644 --- a/nezha-fronted/src/components/common/bottomBox/bottomBox.vue +++ b/nezha-fronted/src/components/common/bottomBox/bottomBox.vue @@ -18,24 +18,27 @@ <!------TAB区------> <!--机柜--> - <cabinet-tab v-if="from === $CONSTANTS.fromRoute.dc && targetTab === 'cabinet'" v-show="subResizeShow" :obj="obj" @changeTab="changeTab"></cabinet-tab> + <cabinet-tab v-if="from === fromRoute.dc && targetTab === 'cabinet'" v-show="subResizeShow" :obj="obj" @changeTab="changeTab"></cabinet-tab> <!--告警信息--> - <alert-message-tab v-if="((from === $CONSTANTS.fromRoute.rule || from === $CONSTANTS.fromRoute.asset || from === $CONSTANTS.fromRoute.endpoint) && targetTab === 'alertMessage')" v-show="subResizeShow" :from="from" :obj="obj" @changeTab="changeTab"></alert-message-tab> + <alert-message-tab v-if="((from === fromRoute.rule || from === fromRoute.asset || from === fromRoute.endpoint) && targetTab === 'alertMessage')" v-show="subResizeShow" :from="from" :obj="obj" @changeTab="changeTab"></alert-message-tab> <!--asset页的endpoint列表--> - <endpoint-tab v-if="from === $CONSTANTS.fromRoute.asset && targetTab === $CONSTANTS.fromRoute.endpoint" v-show="subResizeShow" :from="from" :obj="obj" @changeTab="changeTab"></endpoint-tab> + <endpoint-tab v-if="from === fromRoute.asset && targetTab === fromRoute.endpoint" v-show="subResizeShow" :from="from" :obj="obj" @changeTab="changeTab"></endpoint-tab> <!--endpoint-query--> - <endpoint-query-tab v-if="(from === $CONSTANTS.fromRoute.endpoint && targetTab === 'endpointQuery')" v-show="subResizeShow" ref="endpointQuery" :from="from" :obj="obj" @changeTab="changeTab"></endpoint-query-tab> + <endpoint-query-tab v-if="(from === fromRoute.endpoint && targetTab === 'endpointQuery')" v-show="subResizeShow" ref="endpointQuery" :from="from" :obj="obj" @changeTab="changeTab"></endpoint-query-tab> <!-- model-panel/asset-detail/project-overview的panel--> - <panel-tab v-if="(from === $CONSTANTS.fromRoute.model || from === $CONSTANTS.fromRoute.asset || from === $CONSTANTS.fromRoute.project || from === $CONSTANTS.fromRoute.rule || from === $CONSTANTS.fromRoute.endpoint) && targetTab === 'panel'" v-show="subResizeShow" ref="panelTab" :from="from" :obj="obj" + <panel-tab v-if="(from === fromRoute.model || from === fromRoute.asset || from === fromRoute.project || from === fromRoute.rule || from === fromRoute.endpoint) && targetTab === 'panel'" v-show="subResizeShow" ref="panelTab" :from="from" :obj="obj" @changeTab="changeTab" :targetTab.sync="targetTab" :detail="detail"></panel-tab> <!--terminal-log的记录和回放--> - <terminal-log-cmd-tab v-if="from === $CONSTANTS.fromRoute.terminalLog && targetTab === 'cmd'" ref="reminalLogCMDTab" :from="from" :obj="obj" @changeTab="changeTab"></terminal-log-cmd-tab> - <terminal-log-record-tab v-if="from === $CONSTANTS.fromRoute.terminalLog && targetTab === 'record'" ref="reminalLogRecordTab" :from="from" :obj="obj" @changeTab="changeTab"></terminal-log-record-tab> + <terminal-log-cmd-tab v-if="from === fromRoute.terminalLog && targetTab === 'cmd'" ref="reminalLogCMDTab" :from="from" :obj="obj" @changeTab="changeTab"></terminal-log-cmd-tab> + <terminal-log-record-tab v-if="from === fromRoute.terminalLog && targetTab === 'record'" ref="reminalLogRecordTab" :from="from" :obj="obj" @changeTab="changeTab"></terminal-log-record-tab> - <terminal-log-monitor-tab v-if="from === $CONSTANTS.fromRoute.terminalLog && targetTab === 'monitor'" ref="reminalLogRecordTab" :from="from" :obj="obj" @changeTab="changeTab" @exit="closeSubList"></terminal-log-monitor-tab> + <terminal-log-monitor-tab v-if="from === fromRoute.terminalLog && targetTab === 'monitor'" ref="reminalLogRecordTab" :from="from" :obj="obj" @changeTab="changeTab" @exit="closeSubList"></terminal-log-monitor-tab> + <!--user列表的两个日志--> + <operation-log-tab v-if="from === fromRoute.account && targetTab === 'operationLogTab'" :from="from" :obj="obj" @changeTab="changeTab"></operation-log-tab> + <terminal-log-tab v-if="from === fromRoute.account && targetTab === 'terminalLogTab'" :from="from" :obj="obj" @changeTab="changeTab"></terminal-log-tab> </div> </div> </template> @@ -49,18 +52,23 @@ import panelTab from './tabs/panelTab' import terminalLogRecordTab from './tabs/terminalLogRecordTab' import terminalLogMonitorTab from './tabs/terminalLogMonitorTab' import terminalLogCMDTab from './tabs/terminalLogCMDTab' +import operationLogTab from './tabs/operationLogTab' +import terminalLogTab from './tabs/terminalLogTab' +import { fromRoute } from '@/components/common/js/constants' export default { name: 'bottomBox', components: { - 'cabinet-tab': cabinetTab, - 'alert-message-tab': alertMessageTab, - 'endpoint-query-tab': endpointQueryTab, - 'endpoint-tab': endpointTab, - 'panel-tab': panelTab, + cabinetTab, + alertMessageTab, + endpointQueryTab, + endpointTab, + panelTab, terminalLogRecordTab, - 'terminal-log-cmd-tab': terminalLogCMDTab, - terminalLogMonitorTab + terminalLogMonitorTab, + operationLogTab, + terminalLogTab, + 'terminal-log-cmd-tab': terminalLogCMDTab }, props: { isFullScreen: Boolean, // 是否全屏 @@ -75,7 +83,9 @@ export default { assetDetail: Object // endpoint页的asset详情 }, data () { - return {} + return { + fromRoute: fromRoute + } }, methods: { exitFullScreen () { @@ -97,9 +107,9 @@ export default { this.$emit('update:targetTab', tab) }, afterResize () { - if (this.from === this.$CONSTANTS.fromRoute.endpoint && this.targetTab === 'endpointQuery') { + if (this.from === this.fromRoute.endpoint && this.targetTab === 'endpointQuery') { this.$refs.endpointQuery.tableReload() - } else if (this.from === this.$CONSTANTS.fromRoute.terminalLog && this.targetTab === 'record') { + } else if (this.from === this.fromRoute.terminalLog && this.targetTab === 'record') { setTimeout(() => { this.$refs.reminalLogRecordTab.consoleResize() }, 600) diff --git a/nezha-fronted/src/components/common/bottomBox/tabs/operationLogTab.vue b/nezha-fronted/src/components/common/bottomBox/tabs/operationLogTab.vue new file mode 100644 index 000000000..ea10ddf5b --- /dev/null +++ b/nezha-fronted/src/components/common/bottomBox/tabs/operationLogTab.vue @@ -0,0 +1,206 @@ +<template> + <div style="height: 100%"> + <div class="sub-top-tools"> + <div class="sub-list-tabs"> + <div class="sub-list-tab-title">{{obj.id}}</div><div + id="endpoint-tab-change-panel" class="sub-list-tab sub-list-tab-active">{{$t("config.operationlog.operationlog")}}</div><div + id="endpoint-tab-change-alertmsg" class="sub-list-tab" @click="changeTab('terminalLogTab')">{{$t("config.terminallog.terminallog")}}</div> + </div> + <div class="top-tool-right"> + <div class="top-tool-search"> + <search-input :searchMsg="searchMsg" position="endpoint-bottom" @search="search"></search-input> + </div> + </div> + </div> + <el-table + id="role-list-table" + ref="dataTable" + v-loading="tools.loading" + :data="tableData" + :height="mainTableHeight" + border + @header-dragend="dragend" + @sort-change="tableDataSort" + @selection-change="(selection)=>{batchDeleteObjs=selection}" + > + <el-table-column + :resizable="false" + align="center" + type="selection" + width="55"> + </el-table-column> + <el-table-column + v-for="(item, index) in tools.customTableTitle" + v-if="item.show" + :key="`col-${index}`" + :fixed="item.fixed" + :label="item.label" + :prop="item.prop" + :resizable="true" + :sort-orders="['ascending', 'descending']" + :width="`${item.width}`" + class="data-column" + > + <template slot="header"> + <span> + <span>{{item.label}}</span> + <div class="col-resize-area"></div> + </span> + </template> + <template slot-scope="scope" :column="item"> + <span v-if="item.prop === 'time'"> + {{scope.row[item.prop]}} ms + </span> + <span v-else-if="item.prop === 'username'">{{formatUsername(scope.row)}}</span> + <span v-else-if="item.prop === 'createDate'">{{utcTimeToTimezoneStr(scope.row[item.prop])}}</span> + <span v-else>{{scope.row[item.prop]}}</span> + </template> + </el-table-column> + </el-table> + </div> +</template> + +<script> +import tableMixin from '@/components/common/mixin/table' +export default { + name: 'operationLogTab', + mixins: [tableMixin], + data () { + return { + tableId: 'operationLogTable', // 需要分页的table的id,用于记录每页数量 + tableTitle: [ + { + label: this.$t('config.operationlog.id'), + prop: 'id', + show: true, + width: 80 + }, { + label: this.$t('config.operationlog.username'), + prop: 'username', + show: true + }, + { + label: this.$t('config.operationlog.ip'), + prop: 'ip', + show: true + }, + { + label: this.$t('config.operationlog.operation'), + prop: 'operation', + show: true + }, + { + label: this.$t('config.operationlog.type'), + prop: 'type', + show: true + }, + { + label: this.$t('config.operationlog.state'), + prop: 'state', + show: true + }, + // { + // label: this.$t('config.operationlog.userId'), + // prop: 'userId', + // show: false, + // }, + { + label: this.$t('config.operationlog.operaId'), + prop: 'operaId', + show: false + }, + { + label: this.$t('config.operationlog.createDate'), + prop: 'createDate', + show: true + }, + { + label: this.$t('config.operationlog.time'), + prop: 'time', + show: false + }, + { + label: this.$t('config.operationlog.params'), + prop: 'params', + show: false + }, + { + label: this.$t('config.operationlog.response'), + prop: 'response', + show: false + } + ], + searchMsg: { // 给搜索框子组件传递的信息 + zheze_none: true, + searchLabelList: [ + { + id: 11, + name: this.$t('config.operationlog.type'), + type: 'input', + label: 'type', + disabled: false + }, { + id: 12, + name: this.$t('config.operationlog.username'), + type: 'input', + label: 'username', + disabled: false + }, { + id: 13, + name: this.$t('config.operationlog.operation'), + type: 'selectString', + label: 'operation', + disabled: false + } + ] + } + } + }, + props: { + obj: Object // 关联的实体对象 + }, + methods: { + // 切换tab + changeTab (tab) { + this.$emit('changeTab', tab) + }, + messageStyle (e) { + if (e.column.label === this.$t('config.operationlog.state')) { + if (e.row.state === 'success') { + return 'success' + } else { + return 'danger' + } + } + return '' + }, + getTableData () { + this.$set(this.searchLabel, 'pageNo', this.pageObj.pageNo) + this.$set(this.searchLabel, 'pageSize', this.pageObj.pageSize) + this.tools.loading = true + this.$get('sys/log/list', this.searchLabel).then(response => { + this.tools.loading = false + if (response.code === 200) { + this.tableData = response.data.list + this.pageObj.total = response.data.total + if (!this.scrollbarWrap) { + this.$nextTick(() => { + this.scrollbarWrap = this.$refs.dataTable.bodyWrapper + this.toTopBtnHandler(this.scrollbarWrap) + }) + } + } + }) + }, + formatUsername (row) { + if (row.username) { + return row.username + } else if (row.operation === 'login' && !row.username) { // 如果是登录 且登录失败 + return JSON.parse(row.params).username + } else { + return '-' + } + } + } +} +</script> diff --git a/nezha-fronted/src/components/common/bottomBox/tabs/terminalLogTab.vue b/nezha-fronted/src/components/common/bottomBox/tabs/terminalLogTab.vue new file mode 100644 index 000000000..065848041 --- /dev/null +++ b/nezha-fronted/src/components/common/bottomBox/tabs/terminalLogTab.vue @@ -0,0 +1,291 @@ +<template> + <div style="height: 100%"> + <div class="sub-top-tools"> + <div class="sub-list-tabs"> + <div class="sub-list-tab-title">{{obj.id}}</div><div + id="endpoint-tab-change-panel" class="sub-list-tab" @click="changeTab('operationLogTab')">{{$t("config.operationlog.operationlog")}}</div><div + id="endpoint-tab-change-alertmsg" class="sub-list-tab sub-list-tab-active">{{$t("config.terminallog.terminallog")}}</div> + </div> + <div class="top-tool-right"> + <div class="top-tool-search"> + <search-input :searchMsg="searchMsg" position="endpoint-bottom" @search="search"></search-input> + </div> + </div> + </div> + <el-table + id="terminal-log-list-table" + ref="dataTable" + v-loading="tools.loading" + :data="tableData" + :height="mainTableHeight" + border + @header-dragend="dragend" + @sort-change="tableDataSort" + @selection-change="(selection)=>{batchDeleteObjs=selection}" + > + <el-table-column + :resizable="false" + align="center" + type="selection" + width="55"> + </el-table-column> + <el-table-column + v-for="(item, index) in tools.customTableTitle" + v-if="item.show" + :key="`col-${index}`" + :fixed="item.fixed" + :label="item.label" + :prop="item.prop" + :resizable="true" + :sort-orders="['ascending', 'descending']" + :width="`${item.width}`" + class="data-column" + > + <template slot="header"> + <span> + <span>{{item.label}}</span> + <div class="col-resize-area"></div> + </span> + </template> + <template slot-scope="scope" :column="item"> + <span v-if="item.prop === 'time'">{{utcTimeToTimezoneStr(scope.row[item.prop])}}</span> + <template v-else-if="item.prop === 'status'"> + <span>{{getStatusText(scope.row.status)}}</span> + </template> + <template v-else-if="item.prop === 'uuid'"> + <span>{{scope.row.uuid.substring(0, 8).toUpperCase()}}</span> + </template> + <template v-else-if="item.prop === 'remote'"> + <span>{{getRemoteText(scope.row)}}</span> + </template> + <template v-else-if="item.prop === 'duration'"> + <el-tooltip :disabled="!scope.row.status" effect="light" placement="right"> + <div slot="content"> + {{$t('config.terminallog.endTime')}}<br/> + {{scope.row.endTime}} + </div> + <span>{{getDuration(scope.row)}}</span> + </el-tooltip> + </template> + <template v-else-if="item.prop === 'authType'"> + <span v-if="scope.row.authType == 1">{{$t('config.terminallog.password')}}</span> + <span v-else-if="scope.row.authType == 2">{{$t('config.terminallog.key')}}</span> + </template> + <span v-else>{{scope.row[item.prop]}}</span> + </template> + </el-table-column> + <el-table-column + :resizable="false" + :width="operationWidth" + fixed="right"> + <div slot="header" class="table-operation-title">{{$t('overall.option')}}</div> + <div slot-scope="scope" class="table-operation-items"> + <template v-if="scope.row.status == 0"> + <button :title="$t('config.terminallog.monitor.monitor')" class="table-operation-item" @click="$refs.dataList.showBottomBox('monitor', scope.row)"><i class="nz-icon nz-icon-JC"></i></button> + <el-dropdown size="medium" trigger="hover" @command="tableOperation"> + <div class="table-operation-item table-operation-item--more"> + <span>…</span><i class="nz-icon nz-icon-arrow-down"></i> + </div> + <el-dropdown-menu slot="dropdown"> + <el-dropdown-item :command="['shutdown', scope.row]"><i class="nz-icon nz-icon-ZD"></i><span class="operation-dropdown-text">Kill</span></el-dropdown-item> + </el-dropdown-menu> + </el-dropdown> + </template> + <template v-else> + <button class="table-operation-item" @click="$refs.dataList.showBottomBox('cmd', scope.row)"><i class="nz-icon nz-icon-view1"></i></button> + <el-dropdown size="medium" trigger="hover" @command="tableOperation"> + <div class="table-operation-item table-operation-item--more"> + <span>…</span><i class="nz-icon nz-icon-arrow-down"></i> + </div> + <el-dropdown-menu slot="dropdown"> + <el-dropdown-item :command="['record', scope.row]"><i class="nz-icon nz-icon-edit"></i><span class="operation-dropdown-text">{{$t('config.terminallog.record.record')}}</span></el-dropdown-item> + </el-dropdown-menu> + </el-dropdown> + </template> + </div> + </el-table-column> + </el-table> + </div> +</template> + +<script> +import tableMixin from '@/components/common/mixin/table' +import { terminalLog } from '@/components/common/js/constants' +import { calcDurationByStringTimeB } from '@/components/common/js/tools' + +export default { + name: 'terminalLogTab', + mixins: [tableMixin], + data () { + return { + tableId: 'terminalLogTable', // 需要分页的table的id,用于记录每页数量 + + tableTitle: [ + { + label: this.$t('config.terminallog.id'), + prop: 'id', + show: true, + width: 80 + }, { + label: 'Session ID', + prop: 'uuid', + show: true + }, { + label: 'Username', + prop: 'username', + show: true + }, + { + label: this.$t('config.terminallog.source'), + prop: 'remoteAddr', + show: true + }, + { + label: this.$t('config.terminallog.remote'), + prop: 'remote', + show: true + }, + { + label: this.$t('config.terminallog.protocol'), + prop: 'protocol', + show: true + }, + { + label: this.$t('config.terminallog.startTime'), + prop: 'startTime', + show: true + }, + { + label: this.$t('config.terminallog.duration'), + prop: 'duration', + show: true + }, + { + label: 'AuthType', + prop: 'authType', + show: false + }, + { + label: this.$t('config.terminallog.status'), // killusername鼠标悬停形式 + prop: 'status', + show: true, + width: 100 + } + ], + searchMsg: { // 给搜索框子组件传递的信息 + zheze_none: true, + searchLabelList: [ + { + id: 11, + name: this.$t('config.terminallog.host'), + type: 'input', + label: 'host', + disabled: false + }, { + id: 12, + name: this.$t('config.terminallog.user'), + type: 'input', + label: 'username', + disabled: false + } + ] + }, + nowTime: '' + } + }, + computed: { + getStatusText () { + return function (status) { + return terminalLog.status[status] + } + }, + getRemoteText () { + return function (record) { + return `${record.loginUser}@${record.host}:${record.port}` + } + }, + getDuration () { + return function (record) { + if (record.endTime) { + return calcDurationByStringTimeB(record.startTime, record.endTime) + } + return calcDurationByStringTimeB(record.startTime, this.nowTime) + } + } + }, + props: { + obj: Object // 关联的实体对象 + }, + methods: { + // 切换tab + changeTab (tab) { + this.$emit('changeTab', tab) + }, + tableOperation ([command, row]) { + switch (command) { + case 'shutdown': { + this.shutdown(row) + break + } + default: + this.$refs.dataList.showBottomBox(command, row) + break + } + }, + getTableData () { + const params = { + ...this.searchLabel, + pageNo: this.pageObj.pageNo, + pageSize: this.pageObj.pageSize + } + this.$get('terminal/session', params).then(response => { + this.tools.loading = false + if (response.code === 200) { + this.tableData = response.data.list + this.nowTime = this.utcTimeToTimezoneStr(response.time) + this.pageObj.total = response.data.total + if (!this.scrollbarWrap) { + this.$nextTick(() => { + this.scrollbarWrap = this.$refs.dataTable.bodyWrapper + this.toTopBtnHandler(this.scrollbarWrap) + }) + } + } + }) + }, + shutdown (record) { + this.$confirm(this.$t('tip.killTerm'), { + confirmButtonText: this.$t('tip.yes'), + cancelButtonText: this.$t('tip.no'), + type: 'warning' + }).then(() => { + this.$put('/terminal/kill', { uuid: record.uuid }).then(res => { + if (res.code === 200) { + this.$message.success(this.$t('config.terminallog.success')) + this.bottomBox.showSubList = false + this.getTableData() + } else { + this.$message.error(this.$t('config.terminallog.killErrorTip')) + } + }) + }) + }, + messageStyle (e) { + if (e.column.label == this.$t('config.terminallog.status')) { + if (e.row.status == '0') { + return 'success' + } else if (e.row.status == '1') { + return 'warning' + } else if (e.row.status == '2') { + return 'suspended' + } else if (e.row.status == '3') { + return 'danger' + } else if (e.row.status == '4') { + return 'danger' + } + } + return '' + } + } +} +</script> diff --git a/nezha-fronted/src/components/common/exportXLSX.vue b/nezha-fronted/src/components/common/exportXLSX.vue index 2a54387a7..48ff11154 100644 --- a/nezha-fronted/src/components/common/exportXLSX.vue +++ b/nezha-fronted/src/components/common/exportXLSX.vue @@ -1,10 +1,11 @@ <template> <div class="export-xlsx"> - <div class="nz-btn-group nz-btn-group-size-normal nz-btn-group-light"> - <slot name="optionZone"></slot><button @mouseenter="exportMenuHandler(true)" @mouseleave="exportMenuHandler(false)" class="nz-btn nz-btn-size-normal nz-btn-style-light export-dropdown-btn" id="browser-go" style="padding: 0" v-has="[permissions.import, permissions.export]"> - <i class="nz-icon nz-icon-arrow-down"></i> + <div class="top-tool-btn-group"> + <slot name="optionZone"></slot> + <button id="browser-go" v-has="[permissions.import, permissions.export]" class="top-tool-btn" style="position: relative" @mouseenter="exportMenuHandler(true)" @mouseleave="exportMenuHandler(false)"> + <i class="nz-icon nz-icon-arrow-down" style="font-size: 12px;"></i> <transition name="el-zoom-in-top"> - <ul class="el-dropdown-menu el-popper el-dropdown-menu--mini export-dropdown" style="z-index: 101" v-show="exportShow"> + <ul v-show="exportShow" class="el-dropdown-menu el-popper el-dropdown-menu--mini export-dropdown"> <li @click="showImportBox(1)" class="el-dropdown-menu__item dropdown-content" v-has="permissions.import" :id="id+'-xlsx-import'"><i class="nz-icon nz-icon-upload"></i>{{$t('overall.importExcel')}}</li> <li @click="showImportBox(2)" class="el-dropdown-menu__item dropdown-content" v-has="permissions.export" :id="id+'-xlsx-export'"><i class="nz-icon nz-icon-download1"></i>{{$t('overall.exportExcel')}}</li> </ul> @@ -26,13 +27,13 @@ </div> <div slot="footer" class="footer"> <div class="el-message-box__btns" style="text-align: right;"> - <button @click="downloadTemplate" class="el-button el-button--default el-button--small" :id="id+'-xlsx-import-template'"> + <button :id="id+'-xlsx-import-template'" class="el-button el-button--default el-button--small" @click="downloadTemplate"> <span>{{$t('overall.template')}}</span> </button> - <button @click="importExcel" class="nz-btn el-button el-button--default el-button--small" :disabled="prevent_opt.import" :class="{'nz-btn-disabled':prevent_opt.import}" :id="id+'-xlsx-import-add'"> + <button :id="id+'-xlsx-import-add'" :class="{'nz-btn-disabled':prevent_opt.import}" :disabled="prevent_opt.import" class="nz-btn el-button el-button--default el-button--small" @click="importExcel"> <span>{{$t('overall.importExcel')}}</span> </button> - <button @click="closeDialog" class="el-button el-button--default el-button--small" :id="id+'-xlsx-import-esc'"> + <button :id="id+'-xlsx-import-esc'" class="el-button el-button--default el-button--small" @click="closeDialog"> <span>{{$t('overall.cancel')}}</span> </button> </div> @@ -366,4 +367,82 @@ export default { .import-result-item .line-num{ width: 55px; } + + .export-dropdown-btn { + position: relative; + } + .endpoint-query-dropdown { + position: absolute; + right: 0; + top: 31px; + } + .export-dropdown { + width: 90px; + right: 0; + left: unset !important; + top: 35px; + } + .endpoint-query-dropdown::after, .export-dropdown::after { + content: ''; + display: block; + width:0; + height:0; + overflow: hidden; + font-size: 0; + line-height: 0; + border: 5px; + border-style: dashed dashed solid dashed; + border-color: transparent transparent #fff transparent; + position: absolute; + right: 3px; + bottom: 0; + + } + .export-dropdown::after { + transform: translate(-50%, -54px); + } + .endpoint-query-dropdown::after { + transform: translate(-50%, -45px); + } + .export-xlsx .el-dialog__body{ + padding: 10px 20px 20px 20px; + } + .export-xlsx .el-button:focus, .export-xlsx .el-button:hover { + color: unset; + border-color: unset; + background-color:unset; + } + .dropdownBtn .el-button--primary{ + top:2px; + padding: 0 8px; + background-image: linear-gradient(180deg, #fff 0%, #E0E0E0 100%); + border: 0px; + color: #666; + -webkit-box-shadow: 0 0 1px 1px rgba(162,162,162,0.5); + box-shadow: 0 0 1px 1px rgba(162,162,162,0.5); + letter-spacing: 0; + background-color: unset; + } + .dropdownBtn .el-button--primary:hover{ + background-image: linear-gradient(180deg, #F0F0F0 0%, #D8D8D8 99%) !important; + } + .dropdownBtn .el-button--mini{ + font-size: 12px; + height: 24px; + } + .dropdownBtn .el-button--mini:first-of-type { + right: 3px; + } + .el-dropdown .el-button-group{ + display: block; + position: relative; + top:-2px; + } + .export-xlsx .el-dropdown .el-dropdown__caret-button { + padding-left: 5px; + padding-right: 5px; + border-left: none; + top: 0; + left: -1px; + } </style> diff --git a/nezha-fronted/src/components/common/header.vue b/nezha-fronted/src/components/common/header.vue index 5df9db7b0..459ae481b 100644 --- a/nezha-fronted/src/components/common/header.vue +++ b/nezha-fronted/src/components/common/header.vue @@ -418,7 +418,7 @@ export default { }, getUserData () { return new Promise(resolve => { - this.$get('sys/user/list', { pageSize: -1, pageNo: 1 }).then(response => { + this.$get('sys/user', { pageSize: -1, pageNo: 1 }).then(response => { if (response.code === 200) { this.userData = response.data.list } diff --git a/nezha-fronted/src/components/common/js/constants.js b/nezha-fronted/src/components/common/js/constants.js index eda5853b0..e9cc3ecf0 100644 --- a/nezha-fronted/src/components/common/js/constants.js +++ b/nezha-fronted/src/components/common/js/constants.js @@ -182,10 +182,18 @@ export const fromRoute = { message: 'message', rule: 'rule', model: 'model', + mib: 'mib', asset: 'asset', + assetType: 'assetType', + assetState: 'assetState', + expressionTemplate: 'expressionTemplate', + account: 'account', + promServer: 'promServer', dc: 'dc', + role: 'role', endpoint: 'endpoint', project: 'project', endpointQuery: 'endpointQuery', - terminalLog: 'terminal' + terminalLog: 'terminalLog', + operationLog: 'operationLog' } diff --git a/nezha-fronted/src/components/common/js/tools.js b/nezha-fronted/src/components/common/js/tools.js index 5d2050944..aebb5da49 100644 --- a/nezha-fronted/src/components/common/js/tools.js +++ b/nezha-fronted/src/components/common/js/tools.js @@ -242,7 +242,7 @@ export const bottomBoxWindow = { vm.tools.toTopBtnTop = vm.$tableHeight.toTopBtnTop vm.bottomBox.isFullScreen = false // 移动分页组件的位置 - const paginationTop = document.querySelector('.pagination-top') + /* const paginationTop = document.querySelector('.pagination-top') const paginationBottom = document.querySelector('.pagination-bottom') paginationTop.classList.remove('display-none') if (paginationTop.classList.contains('pagination-top-show')) { @@ -254,7 +254,7 @@ export const bottomBoxWindow = { setTimeout(() => { paginationTop.classList.add('display-none') paginationBottom.appendChild(paginationTop.removeChild(document.querySelector('.pagination'))) - }, 210) + }, 210) */ // 主列表恢复全屏 vm.bottomBox.mainResizeShow = vm.bottomBox.subResizeShow = true @@ -265,7 +265,7 @@ export const bottomBoxWindow = { vm.mainTableHeight = vm.$tableHeight.openSubList.mainList // 重置table高度 vm.tools.toTopBtnTop = vm.$tableHeight.openSubList.toTopBtnTop // 移动分页组件的位置 - const paginationTop = document.querySelector('.pagination-top') + /* const paginationTop = document.querySelector('.pagination-top') paginationTop.appendChild(document.querySelector('.pagination-bottom').removeChild(document.querySelector('.pagination'))) paginationTop.classList.remove('display-none') setTimeout(() => { @@ -275,7 +275,7 @@ export const bottomBoxWindow = { if (!paginationTop.classList.contains('pagination-top-show')) { paginationTop.classList.add('pagination-top-show') } - }, 210) + }, 210) */ } } } diff --git a/nezha-fronted/src/components/common/language/cn.js b/nezha-fronted/src/components/common/language/cn.js index 8d0293300..84917d151 100644 --- a/nezha-fronted/src/components/common/language/cn.js +++ b/nezha-fronted/src/components/common/language/cn.js @@ -48,6 +48,7 @@ const cn = { createAlertRule: '新增告警规则', createAccount: '新增用户名', createRole: '新增角色', + createTemplate: '新增模板', createPrometheusServer: '新增prometheus服务', createDatacenter: '新增数据中心', active: '活跃', @@ -641,12 +642,17 @@ const cn = { account: { accountList: '用户列表', account: '用户', + name: '姓名', // "用户" + username: '登录名', // 登录名 roles: '角色', language: '语言', receiver: '接收人', createTime: '创建时间', enable: '可用', option: '操作', + lastLoginTime: '最后登录时间', // 最后登录时间 + lastLoginIp: '最后登录IP', // 最后登录IP + source: '来源', accountId: '用户ID', createAccount: '新增用户', editAccount: '编辑用户', @@ -1000,6 +1006,7 @@ const cn = { exprTempImport: 'expression模板导入', exprTempExport: 'expression模板导出', exprTempImportCancel: 'expression模板导入撤销', + copy: '复制', name: '名称', gname: '组名', expression: '表达式', diff --git a/nezha-fronted/src/components/common/language/en.js b/nezha-fronted/src/components/common/language/en.js index 7a10f11ee..0700845bb 100644 --- a/nezha-fronted/src/components/common/language/en.js +++ b/nezha-fronted/src/components/common/language/en.js @@ -63,6 +63,7 @@ const en = { createMib: 'Create MIB', createAssetType: 'Create asset type', createAssetState: 'Create asset state', + createTemplate: 'Create template', exportExcel: 'Export', importExcel: 'Import', importExcelLower: 'import', @@ -640,19 +641,24 @@ const en = { config: { config: 'Settings', // "设置" account: { - accountList: 'Account list', // "用户列表" + accountList: 'User list', // "用户列表" + account: 'User', // 列表表头 - account: 'Account', // "用户" + name: 'Name', // "用户" + username: 'Username', // 登录名 roles: 'Role', language: 'Language', // "语言" receiver: 'Receiver', // "用户组" createTime: 'Create time', // "创建时间" enable: 'Enable', // "可用" option: 'Operation', // "操作" + lastLoginTime: 'Last login time', // 最后登录时间 + lastLoginIp: 'Last login IP', // 最后登录IP + source: 'Source', // 侧滑框// - accountId: 'Account ID', // "用户ID" - createAccount: 'New account', // "新增用户" - editAccount: 'Edit account', // "编辑用户" + accountId: 'User ID', // "用户ID" + createAccount: 'New user', // "新增用户" + editAccount: 'Edit user', // "编辑用户" notCurrentlySupport: 'Not available', // '暂不支持' password: 'Password', // '密码' oldPwd: 'Old password', @@ -1010,6 +1016,7 @@ const en = { exprTempImport: 'expression template import', exprTempExport: 'expression template export', exprTempImportCancel: 'expression template import rollback', + copy: 'Copy', name: 'Name', gname: 'Group', expression: 'Expression', diff --git a/nezha-fronted/src/components/common/mixin/table.js b/nezha-fronted/src/components/common/mixin/table.js index 00bf3ecb7..eaacb6287 100644 --- a/nezha-fronted/src/components/common/mixin/table.js +++ b/nezha-fronted/src/components/common/mixin/table.js @@ -1,12 +1,19 @@ import bus from '@/libs/bus' +import { tableSet } from '@/components/common/js/tools' +import { fromRoute } from '@/components/common/js/constants' export default { data () { return { + fromRoute: fromRoute, // 侧滑 rightBox: { show: false }, - + pageObj: { // 分页对象 + pageNo: 1, + pageSize: this.$CONSTANTS.defaultPageSize, + total: 0 + }, /* 工具参数 */ tools: { loading: false, // 是否显示table加载动画 @@ -19,24 +26,51 @@ export default { tableData: [], searchLabel: {}, // 搜索参数 scrollbarWrap: null, - delFlag: false + delFlag: false, + + operationWidth: "165" // 操作列宽 } }, methods: { - tableOperation ([command, row]) { + sortableShow: tableSet.sortableShow, + propTitle: tableSet.propTitle, + asce: tableSet.asce, + desc: tableSet.desc, + strTodate: tableSet.strTodate, + tableOperation ([command, row, url]) { switch (command) { case 'edit': { this.edit(row) break } case 'delete': { - this.del(row) + this.del(row, url) break } default: break } }, + isBuildIn (row) { + return (row.buildIn && row.buildIn == 1) || (row.builtIn && row.builtIn == 1) + }, + del (row, url) { + this.$confirm(this.$t('tip.confirmDelete'), { + confirmButtonText: this.$t('tip.yes'), + cancelButtonText: this.$t('tip.no'), + type: 'warning' + }).then(() => { + this.$delete(url).then(response => { + if (response.code === 200) { + this.delFlag = true + this.$message({ duration: 2000, type: 'success', message: this.$t('tip.deleteSuccess') }) + this.getTableData() + } else { + this.$message.error(response.msg) + } + }) + }) + }, newObject () { return JSON.parse(JSON.stringify(this.blankObject)) }, @@ -156,5 +190,10 @@ export default { : this.tableTitle this.tableTitleReset(this.tableTitle, this.tools.customTableTitle) this.getTableData() + }, + beforeDestroy () { + if (this.scrollbarWrap) { + this.scrollbarWrap.removeEventListener('scroll', bus.debounce) + } } } diff --git a/nezha-fronted/src/components/common/popBox/selectAssetMetaGroup.vue b/nezha-fronted/src/components/common/popBox/selectAssetMetaGroup.vue new file mode 100644 index 000000000..43460c3ac --- /dev/null +++ b/nezha-fronted/src/components/common/popBox/selectAssetMetaGroup.vue @@ -0,0 +1,152 @@ +<template> + <el-popover ref="selectAssetMetaGroupPopBox" v-model="popBox.show" :placement="placement" popper-class="nz-pop nz-pop-select-panel" transition="slide" width="300"> + <div> + <div class="pop-item-wider"> + + <slot name="header"></slot> + + <div class="select-panel-tree"> + <el-tree + ref="tree" + :data="objectData" + :expand-on-click-node="false" + :filter-node-method="filterNode" + :props="{label: 'name', children: 'children'}" + check-on-click-node + check-strictly + default-expand-all + highlight-current + node-key="id" + @node-click="selectObject"> + <div slot-scope="{ node, data }" class="tree--node"> + <span>{{ node.label }}</span> + <span class="tree--operation"> + <span class="panel-dropdown-btn panel-dropdown-btn-delete" @click.stop="del(data)"><i class="nz-icon nz-icon-delete"></i></span> + <span class="panel-dropdown-btn" @click.stop="edit(data)"><i class="nz-icon nz-icon-edit"></i></span> + </span> + </div> + </el-tree> + </div> + </div> + </div> + <div slot="reference"> + <slot name="trigger"></slot> + </div> + </el-popover> +</template> + +<script> +export default { + name: 'selectAssetMetaGroup', + props: { + placement: { type: String }, + isEdit: { type: Boolean, default: true }, + objectData: { type: Array }, + showObject: { type: Object }, + filterObject: { type: String } + }, + mounted () { + this.$refs.tree.setCurrentKey(this.object) + }, + watch: { + filterObject: { + immediate: true, + handler (n) { + this.$refs.tree && this.$refs.tree.filter(n) + } + }, + showObject: { + immediate: true, + handler (n) { + if (n) { + this.object = JSON.parse(JSON.stringify(n)) + } + } + } + }, + data () { + return { + popBox: { show: false }, + object: { id: 0, name: '' } + } + }, + methods: { + filterNode (value, data) { + if (!value) return true + return data.name.indexOf(value) !== -1 + }, + del (data) { + this.$emit('del', data) + }, + edit (data) { + this.$emit('edit', data) + }, + esc () { + this.popBox.show = false + }, + // 确认选择某个节点,与父组件交互 + selectObject (data, checked, child) { + this.$emit('selectObject', data) + this.$refs.tree.setCurrentKey(data) + this.esc() + } + } +} +</script> + +<style lang="scss"> +.movable { + .el-tree-node__content { + cursor: move; + .tree--node>span:first-of-type { + cursor: pointer; + } + .tree--node>span:last-of-type>span { + cursor: pointer; + } + } +} +.tree--node>span:last-of-type>span>i { + font-weight: normal !important; +} +.select-panel-tree { + height: 350px; + overflow: auto; +} +.select-panel-tree .el-tree-node__content { + height: 34px; + line-height: 34px; +} +.select-panel-tree .el-tree-node__content:hover { + color: $global-text-color-active; +} +.select-panel-tree .el-tree--highlight-current .el-tree-node.is-current>.el-tree-node__content { + background-color: #F5F7FA; + font-weight: bold; + color: $global-text-color-active; +} +.tree--node { + display: flex; + justify-content: space-between; + width: calc(100% - 28px); +} +.tree--operation { + opacity: 0; +} +.tree--node:hover .tree--operation { + opacity: 1; +} +.panel-dropdown-btn { + color: #60BEFF; +} +.panel-dropdown-btn:hover { + color: #409EFF; +} +.panel-dropdown-btn-delete { + color: #F98D9A; +} + +.panel-dropdown-btn-delete:hover { + color: #D96D7A; +} +</style> diff --git a/nezha-fronted/src/components/common/rightBox/accountBox.vue b/nezha-fronted/src/components/common/rightBox/accountBox.vue index 620676836..debfb5b72 100644 --- a/nezha-fronted/src/components/common/rightBox/accountBox.vue +++ b/nezha-fronted/src/components/common/rightBox/accountBox.vue @@ -5,7 +5,7 @@ $--input-focus-border: red; <div class="right-box right-box-account" v-clickoutside="{obj:editUser,func:clickOutside}"> <!-- begin--顶部按钮--> <div class="right-box-top-btns right-box-form-delete"> - <button @click="del" type="button" v-has="'account_delete'" v-if="editUser.userId&&editUser.userId!==1" + <button v-if="editUser.id&&editUser.id!==1" v-has="'account_delete'" type="button" @click="del" class="nz-btn nz-btn-size-normal nz-btn-size-alien" id="account-edit-del"> <span class="right-box-top-btn-icon"><i class="nz-icon nz-icon-delete"></i></span> @@ -15,16 +15,16 @@ $--input-focus-border: red; <!-- end--顶部按钮--> <!-- begin--标题--> - <div class="right-box-title">{{editUser.userId ? ($t("config.account.editAccount") + " ID:" + editUser.userId) : $t("config.account.createAccount")}}</div> + <div class="right-box-title">{{editUser.id ? ($t("config.account.editAccount") + " ID:" + editUser.id) : $t("config.account.createAccount")}}</div> <!-- end--标题--> <!-- begin--表单--> <div class="right-box-form-box"> - <el-form :model="editUser" :rules="editUser.userId ? rules2 : rules" class="right-box-form right-box-form-left" label-position = "top" label-width="120px" ref="accountForm"> + <el-form ref="accountForm" :model="editUser" :rules="editUser.id ? rules2 : rules" class="right-box-form right-box-form-left" label-position = "top" label-width="120px"> <!--username--> <el-form-item :label="$t('config.account.account')" prop="username"> <el-input autocomplete="new-password" type="text" placeholder="" id="account-input-username" - v-model="editUser.username" maxlength="64" show-word-limit size="small" :disabled="editUser.username==='admin' && editUser.userId==1"></el-input> + v-model="editUser.username" :disabled="editUser.username==='admin' && editUser.id==1" maxlength="64" show-word-limit size="small"></el-input> </el-form-item> <!--password--> <el-form-item :label="$t('config.account.password')" prop="pin"> @@ -42,19 +42,19 @@ $--input-focus-border: red; </el-form-item> <!--enable--> <el-form-item :label="$t('config.account.enable')"> - <el-switch v-model="editUser.status" active-color="#ee9d3f" :disabled="isCurrentUser(editUser.username) || (editUser.username==='admin' && editUser.userId==1) " active-value="1" id="account-input-status" + <el-switch id="account-input-status" v-model="editUser.status" :disabled="isCurrentUser(editUser.username) || (editUser.username==='admin' && editUser.id==1) " active-color="#ee9d3f" active-value="1" inactive-value="0"> </el-switch> </el-form-item> <!--roles--> <el-form-item :label="$t('config.account.roles')" prop="roleIds"> - <el-select @change="()=>{this.$forceUpdate()}" clearable collapse-tags placeholder="" popper-class="config-dropdown" size="small" v-model="editUser.roleIds" :disabled="(editUser.username==='admin') && editUser.userId==1" id="account-input-roleIds"> + <el-select id="account-input-roleIds" v-model="editUser.roleIds" :disabled="(editUser.username==='admin') && editUser.id==1" clearable collapse-tags placeholder="" popper-class="config-dropdown" size="small" @change="()=>{this.$forceUpdate()}"> <template v-for="role in roles"> <el-option :key="role.id" :label="role.i18n?$t(role.i18n):role.name" :value="role.id"></el-option> </template> </el-select> </el-form-item> - <el-form-item :label="$t('config.account.createTime')" v-if="editUser.userId"> + <el-form-item v-if="editUser.id" :label="$t('config.account.createTime')"> <div class="right-box-form-content-txt">{{editUser.createTime}}</div> </el-form-item> @@ -180,8 +180,8 @@ export default { if (valid) { const editUser = JSON.parse(JSON.stringify(this.editUser)) editUser.roleIds = [editUser.roleIds] - if (this.editUser.userId) { - this.$put('sys/user/update', editUser).then(response => { + if (this.editUser.id) { + this.$put('sys/user', editUser).then(response => { this.prevent_opt.save = false if (response.code === 200) { this.$message({ duration: 1000, type: 'success', message: this.$t('tip.saveSuccess') }) @@ -192,7 +192,7 @@ export default { }) } else { editUser.roleIds = this.roles.find(t => t.name == 'common').id - this.$post('sys/user/save', editUser).then(response => { + this.$post('sys/user', editUser).then(response => { this.prevent_opt.save = false if (response.code === 200) { this.$message({ duration: 1000, type: 'success', message: this.$t('tip.saveSuccess') }) @@ -217,7 +217,7 @@ export default { cancelButtonText: this.$t('tip.no'), type: 'warning' }).then(() => { - this.$delete('sys/user/delete?userIds=' + this.editUser.userId).then(response => { + this.$delete('sys/user?ids=' + this.editUser.id).then(response => { if (response.code === 200) { this.$message({ duration: 1000, type: 'success', message: this.$t('tip.deleteSuccess') }) this.esc(true) diff --git a/nezha-fronted/src/components/common/table/nzDataList.vue b/nezha-fronted/src/components/common/table/nzDataList.vue index 36bd68362..0b45fef0b 100644 --- a/nezha-fronted/src/components/common/table/nzDataList.vue +++ b/nezha-fronted/src/components/common/table/nzDataList.vue @@ -5,11 +5,14 @@ <!-- 顶部工具栏 --> <div class="main-modal"></div> <div v-show="bottomBox.mainResizeShow" class="top-tools"> + <div class="top-tool-main-left" style="width: 300px"> + <slot name="top-tool-left"></slot> + </div> <div :class="{'top-tool-main-right-to-left': bottomBox.showSubList}" class="top-tool-main-right"> <div v-if="components.indexOf('searchInput') > -1" class="top-tool-search"> <search-input ref="searchInput" :inTransform="bottomBox.inTransform" :searchMsg="searchMsg" @search="search"></search-input> </div> - <slot name="top-tool"></slot> + <slot name="top-tool-right"></slot> <button v-if="components.indexOf('elementSet') > -1" id="account-column-setting" class="top-tool-btn margin-l-10" type="button" @click="!tools.showCustomTableTitle && (tools.showCustomTableTitle = true)"> <i class="nz-icon-gear nz-icon"></i> @@ -33,7 +36,7 @@ <div v-show="bottomBox.mainResizeShow" class="nz-table2"> <slot v-bind:mainResizeShow="bottomBox.mainResizeShow"></slot> </div> - <div v-show="!bottomBox.showSubList" class="pagination-bottom"> + <div class="pagination-bottom"> <slot name="pagination"></slot> </div> </div> @@ -45,16 +48,23 @@ :obj="bottomBox.object" :sub-resize-show="bottomBox.subResizeShow" :target-tab.sync="bottomBox.targetTab" - @closeSubList="bottomBox.showSubList = false" @exitFullScreen="exitFullScreen" @fullScreen="fullScreen" @listResize="listResize" ></bottom-box> + @closeSubList="bottomBox.showSubList = false" + @exitFullScreen="exitFullScreen" + @fullScreen="fullScreen" + @listResize="listResize" ></bottom-box> </transition> </div> </template> <script> +import bottomBox from '@/components/common/bottomBox/bottomBox' import { bottomBoxWindow } from '@/components/common/js/tools' export default { name: 'nzDataList', + components: { + bottomBox + }, props: { from: { type: String, |
