未验证 提交 319ce5f5 编写于 作者: J Juntao Zhang 提交者: GitHub

Feat:Import and export the configuration file for the dashboard layout (#288)

上级 5ce77769
...@@ -127,6 +127,9 @@ ...@@ -127,6 +127,9 @@
.ml-5 { .ml-5 {
margin-left: 5px; margin-left: 5px;
} }
.ml-10 {
margin-left: 10px;
}
.mr-0 { .mr-0 {
margin-right: 0px; margin-right: 0px;
} }
......
...@@ -65,6 +65,10 @@ const mutations: MutationTree<State> = { ...@@ -65,6 +65,10 @@ const mutations: MutationTree<State> = {
[types.SET_COMPS_TREE](state: State, data: CompsTree[]) { [types.SET_COMPS_TREE](state: State, data: CompsTree[]) {
state.tree = data; state.tree = data;
}, },
[types.IMPORT_TREE](state: State, data: CompsTree[]) {
state.tree.push(...data);
window.localStorage.setItem('dashboard', JSON.stringify(state.tree));
},
[types.SET_GROUP_QUERY](state: State, params: any) { [types.SET_GROUP_QUERY](state: State, params: any) {
state.tree[state.group].query = params; state.tree[state.group].query = params;
}, },
...@@ -137,12 +141,22 @@ const mutations: MutationTree<State> = { ...@@ -137,12 +141,22 @@ const mutations: MutationTree<State> = {
state.tree[state.group].children.push({ name: params.name, children: [] }); state.tree[state.group].children.push({ name: params.name, children: [] });
window.localStorage.setItem('dashboard', JSON.stringify(state.tree)); window.localStorage.setItem('dashboard', JSON.stringify(state.tree));
}, },
[types.IMPORT_COMPS_TREE](state: State, params: any) {
state.tree[state.group].children.push(params);
window.localStorage.setItem('dashboard', JSON.stringify(state.tree));
},
[types.DELETE_COMPS_GROUP](state: State, index: number) { [types.DELETE_COMPS_GROUP](state: State, index: number) {
state.tree.splice(index, 1); state.tree.splice(index, 1);
if (!state.tree[state.group]) {
state.group--;
}
window.localStorage.setItem('dashboard', JSON.stringify(state.tree)); window.localStorage.setItem('dashboard', JSON.stringify(state.tree));
}, },
[types.DELETE_COMPS_TREE](state: State, index: number) { [types.DELETE_COMPS_TREE](state: State, index: number) {
state.tree[state.group].children.splice(index, 1); state.tree[state.group].children.splice(index, 1);
if (!state.tree[state.group].children[state.current]) {
state.current--;
}
window.localStorage.setItem('dashboard', JSON.stringify(state.tree)); window.localStorage.setItem('dashboard', JSON.stringify(state.tree));
}, },
[types.ADD_COMP](state: State) { [types.ADD_COMP](state: State) {
......
...@@ -28,14 +28,17 @@ const actions: ActionTree<State, any> = { ...@@ -28,14 +28,17 @@ const actions: ActionTree<State, any> = {
params: { params: {
index: number; index: number;
duration: any; duration: any;
itemConfig: any;
}, },
) { ) {
const { currentDatabase, currentEndpoint, currentInstance, currentService } = context.rootState.rocketOption; const { currentDatabase, currentEndpoint, currentInstance, currentService } = context.rootState.rocketOption;
const dashboard: string = `${window.localStorage.getItem('dashboard')}`; const dashboard: string = `${window.localStorage.getItem('dashboard')}`;
const tree = JSON.parse(dashboard) || context.state.tree; const tree = JSON.parse(dashboard) || context.state.tree;
const normal = tree[context.state.group].type === 'database' ? false : true; const normal = tree[context.state.group].type === 'database' ? false : true;
const config = tree[context.state.group].children[context.state.current].children[params.index]; const config =
params.itemConfig || tree[context.state.group].children[context.state.current].children[params.index];
const names = ['readSampledRecords', 'sortMetrics']; const names = ['readSampledRecords', 'sortMetrics'];
if (!config) { if (!config) {
return; return;
} }
......
...@@ -41,6 +41,8 @@ export const SET_CURRENT_GROUP = 'SET_CURRENT_GROUP'; ...@@ -41,6 +41,8 @@ export const SET_CURRENT_GROUP = 'SET_CURRENT_GROUP';
export const SET_CURRENT_GROUP_WITH_CURRENT = 'SET_CURRENT_GROUP_WITH_CURRENT'; export const SET_CURRENT_GROUP_WITH_CURRENT = 'SET_CURRENT_GROUP_WITH_CURRENT';
export const SET_CURRENT_COMPS = 'SET_CURRENT_COMPS'; export const SET_CURRENT_COMPS = 'SET_CURRENT_COMPS';
export const ADD_COMPS_GROUP = 'ADD_COMPS_GROUP'; export const ADD_COMPS_GROUP = 'ADD_COMPS_GROUP';
export const IMPORT_TREE = 'IMPORT_TREE';
export const IMPORT_COMPS_TREE = 'IMPORT_COMPS_TREE';
export const ADD_COMPS_TREE = 'ADD_COMPS_TREE'; export const ADD_COMPS_TREE = 'ADD_COMPS_TREE';
export const DELETE_COMPS_GROUP = 'DELETE_COMPS_GROUP'; export const DELETE_COMPS_GROUP = 'DELETE_COMPS_GROUP';
export const DELETE_COMPS_TREE = 'DELETE_COMPS_TREE'; export const DELETE_COMPS_TREE = 'DELETE_COMPS_TREE';
......
/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export const readFile = (event: any) => {
return new Promise((resolve) => {
const { files } = event.target;
if (files.length < 1) {
return;
}
const file = files[0];
const reader: FileReader = new FileReader();
reader.readAsText(file);
reader.onload = function() {
if (typeof this.result === 'string') {
resolve(JSON.parse(this.result));
}
};
});
};
/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export const saveFile = (data: any, name: string) => {
const newData = JSON.stringify(data);
const tagA = document.createElement('a');
tagA.download = name;
tagA.style.display = 'none';
const blob = new Blob([newData]);
tagA.href = URL.createObjectURL(blob);
document.body.appendChild(tagA);
tagA.click();
document.body.removeChild(tagA);
};
...@@ -64,6 +64,7 @@ limitations under the License. --> ...@@ -64,6 +64,7 @@ limitations under the License. -->
import { Vue, Component, Prop, Watch } from 'vue-property-decorator'; import { Vue, Component, Prop, Watch } from 'vue-property-decorator';
import charts from './charts'; import charts from './charts';
import { QueryTypes } from './constant'; import { QueryTypes } from './constant';
import { TopologyType, ObjectsType } from '../../constant';
import { MetricsType, CalculationType } from './charts/constant'; import { MetricsType, CalculationType } from './charts/constant';
import { uuid } from '@/utils/uuid.ts'; import { uuid } from '@/utils/uuid.ts';
...@@ -84,6 +85,7 @@ limitations under the License. --> ...@@ -84,6 +85,7 @@ limitations under the License. -->
@Prop() private item!: any; @Prop() private item!: any;
@Prop() private index!: number; @Prop() private index!: number;
@Prop() private type!: string; @Prop() private type!: string;
@Prop() private updateObjects!: string;
private pageTypes = ['TOPOLOGY_ENDPOINT', 'TOPOLOGY_INSTANCE']; private pageTypes = ['TOPOLOGY_ENDPOINT', 'TOPOLOGY_INSTANCE'];
private dialogConfigVisible = false; private dialogConfigVisible = false;
...@@ -102,8 +104,9 @@ limitations under the License. --> ...@@ -102,8 +104,9 @@ limitations under the License. -->
this.height = this.item.height; this.height = this.item.height;
this.unit = this.item.unit; this.unit = this.item.unit;
this.itemConfig = this.item; this.itemConfig = this.item;
const types = [ObjectsType.UPDATE_INSTANCES, ObjectsType.UPDATE_ENDPOINTS] as any[];
if (this.pageTypes.includes(this.type)) { if (this.updateObjects && !types.includes(this.updateObjects)) {
return; return;
} }
this.chartRender(); this.chartRender();
...@@ -113,9 +116,12 @@ limitations under the License. --> ...@@ -113,9 +116,12 @@ limitations under the License. -->
if (this.rocketGlobal.edit) { if (this.rocketGlobal.edit) {
return; return;
} }
const pageTypes = [TopologyType.TOPOLOGY_ENDPOINT, TopologyType.TOPOLOGY_INSTANCE] as any[];
this.GET_QUERY({ this.GET_QUERY({
duration: this.durationTime, duration: this.durationTime,
index: this.index, index: this.index,
itemConfig: pageTypes.includes(this.type) ? this.itemConfig : undefined,
}).then((params: Array<{ metricName: string; [key: string]: any; config: any }>) => { }).then((params: Array<{ metricName: string; [key: string]: any; config: any }>) => {
if (!params) { if (!params) {
return; return;
......
<template>
<div class="flex-h btn-box">
<div class="rk-dashboard-bar-btn">
<span v-tooltip:bottom="{ content: rocketGlobal.edit ? 'view' : 'edit' }">
<svg
class="icon lg vm cp rk-btn ghost"
:style="`color:${!rocketGlobal.edit ? '' : '#ffc107'}`"
@click="handleSetEdit"
>
<use :xlink:href="!rocketGlobal.edit ? '#lock' : '#lock-open'"></use>
</svg>
</span>
</div>
<div class="rk-dashboard-bar-btn">
<span v-tooltip:bottom="{ content: 'import' }">
<input id="tool-bar-file" type="file" name="file" title="" accept=".json" @change="importData" />
<label class="rk-btn ghost input-label" for="tool-bar-file">
<svg class="icon lg vm cp " :style="`marginTop: 0px`">
<use :xlink:href="'#folder_open'"></use>
</svg>
</label>
</span>
</div>
<div class="rk-dashboard-bar-btn">
<span v-tooltip:bottom="{ content: 'export' }">
<svg class="icon lg vm cp rk-btn ghost" @click="exportData">
<use :xlink:href="'#save_alt'"></use>
</svg>
</span>
</div>
<div class="rk-dashboard-bar-btn">
<svg class="icon lg vm cp rk-btn ghost" @click="handleOption">
<use xlink:href="#retry"></use>
</svg>
</div>
</div>
</template>
<script lang="ts">
import { Vue, Component, Prop } from 'vue-property-decorator';
import { Action, Mutation } from 'vuex-class';
import { readFile } from '@/utils/readFile';
import { saveFile } from '@/utils/saveFile';
@Component({})
export default class ToolBarBtns extends Vue {
@Prop() private compType!: any;
@Prop() private rocketGlobal!: any;
@Prop() private rocketComps!: any;
@Prop() private durationTime!: any;
@Prop() private rocketOption: any;
@Mutation('SET_COMPS_TREE') private SET_COMPS_TREE: any;
@Mutation('IMPORT_TREE') private IMPORT_TREE: any;
@Action('SET_EDIT') private SET_EDIT: any;
@Action('MIXHANDLE_GET_OPTION') private MIXHANDLE_GET_OPTION: any;
private handleOption() {
return this.MIXHANDLE_GET_OPTION({
compType: this.compType,
duration: this.durationTime,
keywordServiceName: this.rocketOption.keywordService,
});
}
private handleSetEdit() {
this.SET_EDIT(!this.rocketGlobal.edit);
}
private async importData(event: any) {
try {
const data: any = await readFile(event);
if (!Array.isArray(data)) {
throw new Error();
}
const { children, name, type } = data[0];
if (children && name && type) {
this.IMPORT_TREE(data);
} else {
throw new Error('error');
}
const el: any = document.getElementById('tool-bar-file');
el!.value = '';
} catch (e) {
this.$modal.show('dialog', { text: 'ERROR' });
}
}
private exportData() {
const data = this.rocketComps.tree;
const name = 'dashboard.json';
saveFile(data, name);
}
}
</script>
<style lang="scss" scoped>
.rk-dashboard-bar-btn {
padding: 0 5px;
border-right: 2px solid #252a2f;
height: 19px;
}
#tool-bar-file {
display: none;
}
.input-label {
display: inline;
line-height: inherit;
}
.btn-box {
height: 58px;
}
</style>
...@@ -13,24 +13,15 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ...@@ -13,24 +13,15 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. --> limitations under the License. -->
<template> <template>
<div> <div class="rk-dashboard-bar flex-h">
<ToolBarBtns
:rocketGlobal="rocketGlobal"
:rocketComps="rocketComps"
:compType="compType"
:durationTime="durationTime"
:rocketOption="rocketOption"
></ToolBarBtns>
<div class="rk-dashboard-bar flex-h" v-if="compType !== dashboardType.DATABASE"> <div class="rk-dashboard-bar flex-h" v-if="compType !== dashboardType.DATABASE">
<div class="rk-dashboard-bar-reload">
<span v-tooltip:bottom="{ content: rocketGlobal.edit ? 'view' : 'edit' }">
<svg
class="icon lg vm cp rk-btn ghost"
:style="`color:${!rocketGlobal.edit ? '' : '#ffc107'}`"
@click="handleSetEdit"
>
<use :xlink:href="!rocketGlobal.edit ? '#lock' : '#lock-open'"></use>
</svg>
</span>
</div>
<div class="rk-dashboard-bar-reload">
<svg class="icon lg vm cp rk-btn ghost" @click="handleOption">
<use xlink:href="#retry"></use>
</svg>
</div>
<div class="sm grey service-search" v-if="compType === dashboardType.SERVICE"> <div class="sm grey service-search" v-if="compType === dashboardType.SERVICE">
<div>{{ this.$t('serviceFilter') }}</div> <div>{{ this.$t('serviceFilter') }}</div>
<input type="text" :value="rocketOption.keywordService" @change="searchServices($event.target.value)" /> <input type="text" :value="rocketOption.keywordService" @change="searchServices($event.target.value)" />
...@@ -61,20 +52,6 @@ limitations under the License. --> ...@@ -61,20 +52,6 @@ limitations under the License. -->
/> />
</div> </div>
<div class="rk-dashboard-bar flex-h" v-else> <div class="rk-dashboard-bar flex-h" v-else>
<div class="rk-dashboard-bar-reload">
<svg
class="icon lg vm cp rk-btn ghost"
:style="`color:${!rocketGlobal.edit ? '' : '#ffc107'}`"
@click="handleSetEdit"
>
<use :xlink:href="!rocketGlobal.edit ? '#lock' : '#lock-open'"></use>
</svg>
</div>
<div class="rk-dashboard-bar-reload">
<svg class="icon lg vm cp rk-btn ghost" @click="handleOption">
<use xlink:href="#retry"></use>
</svg>
</div>
<ToolBarSelect <ToolBarSelect
@onChoose="SELECT_DATABASE" @onChoose="SELECT_DATABASE"
:title="this.$t('currentDatabase')" :title="this.$t('currentDatabase')"
...@@ -90,10 +67,11 @@ limitations under the License. --> ...@@ -90,10 +67,11 @@ limitations under the License. -->
import { Vue, Component, Prop } from 'vue-property-decorator'; import { Vue, Component, Prop } from 'vue-property-decorator';
import ToolBarSelect from './tool-bar-select.vue'; import ToolBarSelect from './tool-bar-select.vue';
import ToolBarEndpointSelect from './tool-bar-endpoint-select.vue'; import ToolBarEndpointSelect from './tool-bar-endpoint-select.vue';
import ToolBarBtns from './tool-bar-btns.vue';
import { State, Action, Mutation } from 'vuex-class'; import { State, Action, Mutation } from 'vuex-class';
import { DASHBOARDTYPE } from './constant'; import { DASHBOARDTYPE } from './constant';
@Component({ components: { ToolBarSelect, ToolBarEndpointSelect } }) @Component({ components: { ToolBarSelect, ToolBarEndpointSelect, ToolBarBtns } })
export default class ToolBar extends Vue { export default class ToolBar extends Vue {
@Prop() private compType!: any; @Prop() private compType!: any;
@Prop() private stateDashboard!: any; @Prop() private stateDashboard!: any;
...@@ -103,7 +81,6 @@ limitations under the License. --> ...@@ -103,7 +81,6 @@ limitations under the License. -->
@State('rocketOption') private rocketOption: any; @State('rocketOption') private rocketOption: any;
@Mutation('ADD_COMP') private ADD_COMP: any; @Mutation('ADD_COMP') private ADD_COMP: any;
@Mutation('SET_KEYWORDSERVICE') private SET_KEYWORDSERVICE: any; @Mutation('SET_KEYWORDSERVICE') private SET_KEYWORDSERVICE: any;
@Action('SET_EDIT') private SET_EDIT: any;
@Action('SELECT_SERVICE') private SELECT_SERVICE: any; @Action('SELECT_SERVICE') private SELECT_SERVICE: any;
@Action('SELECT_DATABASE') private SELECT_DATABASE: any; @Action('SELECT_DATABASE') private SELECT_DATABASE: any;
@Action('SELECT_ENDPOINT') private SELECT_ENDPOINT: any; @Action('SELECT_ENDPOINT') private SELECT_ENDPOINT: any;
...@@ -118,16 +95,6 @@ limitations under the License. --> ...@@ -118,16 +95,6 @@ limitations under the License. -->
} }
return current[current.length - 1].k; return current[current.length - 1].k;
} }
private handleOption() {
return this.MIXHANDLE_GET_OPTION({
compType: this.compType,
duration: this.durationTime,
keywordServiceName: this.rocketOption.keywordService,
});
}
private handleSetEdit() {
this.SET_EDIT(this.rocketGlobal.edit ? false : true);
}
private selectService(i: any) { private selectService(i: any) {
this.SELECT_SERVICE({ service: i, duration: this.durationTime }); this.SELECT_SERVICE({ service: i, duration: this.durationTime });
} }
...@@ -164,8 +131,4 @@ limitations under the License. --> ...@@ -164,8 +131,4 @@ limitations under the License. -->
} }
} }
} }
.rk-dashboard-bar-reload {
padding: 15px 5px;
border-right: 2px solid #252a2f;
}
</style> </style>
...@@ -60,11 +60,14 @@ limitations under the License. --> ...@@ -60,11 +60,14 @@ limitations under the License. -->
import { Component, Prop } from 'vue-property-decorator'; import { Component, Prop } from 'vue-property-decorator';
import { Mutation, Action, Getter } from 'vuex-class'; import { Mutation, Action, Getter } from 'vuex-class';
import { DASHBOARDTYPE } from './constant'; import { DASHBOARDTYPE } from './constant';
import { readFile } from '@/utils/readFile';
import { saveFile } from '@/utils/saveFile';
@Component({}) @Component({})
export default class ToolGroup extends Vue { export default class ToolGroup extends Vue {
@Prop() private rocketGlobal: any; @Prop() private rocketGlobal: any;
@Prop() private rocketComps: any; @Prop() private rocketComps: any;
@Mutation('SET_COMPS_TREE') private SET_COMPS_TREE: any;
@Mutation('DELETE_COMPS_GROUP') private DELETE_COMPS_GROUP: any; @Mutation('DELETE_COMPS_GROUP') private DELETE_COMPS_GROUP: any;
@Mutation('ADD_COMPS_GROUP') private ADD_COMPS_GROUP: any; @Mutation('ADD_COMPS_GROUP') private ADD_COMPS_GROUP: any;
@Action('MIXHANDLE_CHANGE_GROUP') private MIXHANDLE_CHANGE_GROUP: any; @Action('MIXHANDLE_CHANGE_GROUP') private MIXHANDLE_CHANGE_GROUP: any;
......
...@@ -32,7 +32,7 @@ limitations under the License. --> ...@@ -32,7 +32,7 @@ limitations under the License. -->
<use xlink:href="#file-deletion"></use> <use xlink:href="#file-deletion"></use>
</svg> </svg>
</span> </span>
<a class="rk-dashboard-nav-add" v-clickout="handleHide" v-if="rocketGlobal.edit"> <a class="rk-dashboard-nav-add mr-10" v-clickout="handleHide" v-if="rocketGlobal.edit">
<svg class="icon vm" @click="show = !show"> <svg class="icon vm" @click="show = !show">
<use xlink:href="#todo-add"></use> <use xlink:href="#todo-add"></use>
</svg> </svg>
...@@ -62,6 +62,19 @@ limitations under the License. --> ...@@ -62,6 +62,19 @@ limitations under the License. -->
<a class="rk-btn r vm long tc confirm" @click="handleCreate">{{ $t('confirm') }}</a> <a class="rk-btn r vm long tc confirm" @click="handleCreate">{{ $t('confirm') }}</a>
</div> </div>
</a> </a>
<a class="rk-dashboard-import mr-10">
<input id="tool-nav-file" class="ipt" type="file" name="file" title="" accept=".json" @change="importData" />
<label for="tool-nav-file" class="input-label">
<svg class="icon open vm">
<use xlink:href="#folder_open"></use>
</svg>
</label>
</a>
<a>
<svg class="icon vm" @click="exportData">
<use xlink:href="#save_alt"></use>
</svg>
</a>
</nav> </nav>
</template> </template>
...@@ -69,11 +82,14 @@ limitations under the License. --> ...@@ -69,11 +82,14 @@ limitations under the License. -->
import Vue from 'vue'; import Vue from 'vue';
import { Component, Prop, Model } from 'vue-property-decorator'; import { Component, Prop, Model } from 'vue-property-decorator';
import { State, Mutation, Action } from 'vuex-class'; import { State, Mutation, Action } from 'vuex-class';
import { readFile } from '@/utils/readFile';
import { saveFile } from '@/utils/saveFile';
@Component @Component
export default class ToolNav extends Vue { export default class ToolNav extends Vue {
@Prop() private rocketGlobal: any; @Prop() private rocketGlobal: any;
@Prop() private rocketComps: any; @Prop() private rocketComps: any;
@Mutation('IMPORT_COMPS_TREE') private IMPORT_COMPS_TREE: any;
@Mutation('SET_CURRENT_COMPS') private SET_CURRENT_COMPS: any; @Mutation('SET_CURRENT_COMPS') private SET_CURRENT_COMPS: any;
@Mutation('DELETE_COMPS_TREE') private DELETE_COMPS_TREE: any; @Mutation('DELETE_COMPS_TREE') private DELETE_COMPS_TREE: any;
@Mutation('ADD_COMPS_TREE') private ADD_COMPS_TREE: any; @Mutation('ADD_COMPS_TREE') private ADD_COMPS_TREE: any;
...@@ -96,6 +112,27 @@ limitations under the License. --> ...@@ -96,6 +112,27 @@ limitations under the License. -->
this.handleHide(); this.handleHide();
// this.template = 'nouse'; // this.template = 'nouse';
} }
private async importData(event: any) {
try {
const data: any = await readFile(event);
const { children, name } = data;
if (children && name && children[0] && children[0].width) {
this.IMPORT_COMPS_TREE(data);
} else {
throw new Error();
}
const el: any = document.getElementById('tool-nav-file');
el!.value = '';
} catch (e) {
this.$modal.show('dialog', { text: 'ERROR' });
}
}
private exportData() {
const { tree, group, current } = this.rocketComps;
const currentData = tree[group].children[current];
const name = `${currentData.name}.comps.json`;
saveFile(currentData, name);
}
} }
</script> </script>
...@@ -164,4 +201,17 @@ limitations under the License. --> ...@@ -164,4 +201,17 @@ limitations under the License. -->
.confirm { .confirm {
margin: 10px 0; margin: 10px 0;
} }
.rk-dashboard-import {
.icon.open {
margin-top: 2px;
}
.ipt {
display: none;
}
.input-label {
display: inline;
line-height: inherit;
cursor: pointer;
}
}
</style> </style>
/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export enum TopologyType {
TOPOLOGY_ENDPOINT = 'TOPOLOGY_ENDPOINT',
TOPOLOGY_INSTANCE = 'TOPOLOGY_INSTANCE',
}
export enum ObjectsType {
UPDATE_INSTANCES = 'UPDATE_INSTANCES',
UPDATE_ENDPOINTS = 'UPDATE_ENDPOINTS',
}
...@@ -17,6 +17,7 @@ limitations under the License. --> ...@@ -17,6 +17,7 @@ limitations under the License. -->
<ToolGroup :rocketGlobal="rocketGlobal" :rocketComps="rocketComps" /> <ToolGroup :rocketGlobal="rocketGlobal" :rocketComps="rocketComps" />
<ToolBar <ToolBar
:rocketGlobal="rocketGlobal" :rocketGlobal="rocketGlobal"
:rocketComps="rocketComps"
:compType="compType" :compType="compType"
:durationTime="durationTime" :durationTime="durationTime"
:stateDashboard="stateDashboardOption" :stateDashboard="stateDashboardOption"
...@@ -36,6 +37,7 @@ limitations under the License. --> ...@@ -36,6 +37,7 @@ limitations under the License. -->
+ Add An Item + Add An Item
</div> </div>
</div> </div>
<v-dialog width="300px" />
</div> </div>
</template> </template>
......
...@@ -22,6 +22,7 @@ limitations under the License. --> ...@@ -22,6 +22,7 @@ limitations under the License. -->
:item="i" :item="i"
:index="index" :index="index"
:type="'TOPOLOGY_ENDPOINT'" :type="'TOPOLOGY_ENDPOINT'"
:updateObjects="updateObjects"
/> />
</div> </div>
</template> </template>
...@@ -39,6 +40,7 @@ limitations under the License. --> ...@@ -39,6 +40,7 @@ limitations under the License. -->
}) })
export default class InstancesSurvey extends Vue { export default class InstancesSurvey extends Vue {
@Prop() private endpointComps: any; @Prop() private endpointComps: any;
@Prop() private updateObjects!: string;
} }
</script> </script>
......
...@@ -15,6 +15,25 @@ limitations under the License. --> ...@@ -15,6 +15,25 @@ limitations under the License. -->
<template> <template>
<div> <div>
<div class="rk-dashboard-bar flex-h"> <div class="rk-dashboard-bar flex-h">
<span class="flex-h">
<div class="rk-dashboard-bar-btn">
<span v-tooltip:bottom="{ content: 'import' }">
<input id="endpoint-file" type="file" name="file" title="" accept=".json" @change="importData" />
<label class="rk-btn ghost input-label" for="endpoint-file">
<svg class="icon lg vm cp " :style="`marginTop: 0px`">
<use :xlink:href="'#folder_open'"></use>
</svg>
</label>
</span>
</div>
<div class="rk-dashboard-bar-btn">
<span v-tooltip:bottom="{ content: 'export' }">
<svg class="icon lg vm cp rk-btn ghost" @click="exportData">
<use :xlink:href="'#save_alt'"></use>
</svg>
</span>
</div>
</span>
<ToolBarSelect :selectable="false" :title="this.$t('currentService')" :current="current" icon="package" /> <ToolBarSelect :selectable="false" :title="this.$t('currentService')" :current="current" icon="package" />
<ToolBarEndpointSelect <ToolBarEndpointSelect
@onChoose="selectEndpoint" @onChoose="selectEndpoint"
...@@ -24,7 +43,7 @@ limitations under the License. --> ...@@ -24,7 +43,7 @@ limitations under the License. -->
icon="code" icon="code"
/> />
</div> </div>
<endpoints-survey :endpointComps="endpointComps" /> <endpoints-survey :endpointComps="endpointComps" :updateObjects="updateObjects" />
</div> </div>
</template> </template>
...@@ -35,6 +54,8 @@ limitations under the License. --> ...@@ -35,6 +54,8 @@ limitations under the License. -->
import EndpointsSurvey from './endpoints-survey.vue'; import EndpointsSurvey from './endpoints-survey.vue';
import ToolBarSelect from '@/views/components/dashboard/tool-bar-select.vue'; import ToolBarSelect from '@/views/components/dashboard/tool-bar-select.vue';
import ToolBarEndpointSelect from '@/views/components/dashboard/tool-bar-endpoint-select.vue'; import ToolBarEndpointSelect from '@/views/components/dashboard/tool-bar-endpoint-select.vue';
import { readFile } from '@/utils/readFile';
import { saveFile } from '@/utils/saveFile';
interface Endpoint { interface Endpoint {
label: string; label: string;
...@@ -59,6 +80,7 @@ limitations under the License. --> ...@@ -59,6 +80,7 @@ limitations under the License. -->
@Action('MIXHANDLE_CHANGE_GROUP_WITH_CURRENT') private MIXHANDLE_CHANGE_GROUP_WITH_CURRENT: any; @Action('MIXHANDLE_CHANGE_GROUP_WITH_CURRENT') private MIXHANDLE_CHANGE_GROUP_WITH_CURRENT: any;
@Prop() private current!: { key: number | string; label: number | string }; @Prop() private current!: { key: number | string; label: number | string };
@Prop() private endpointComps: any; @Prop() private endpointComps: any;
@Prop() private updateObjects!: string;
private selectEndpoint(i: any) { private selectEndpoint(i: any) {
this.SELECT_ENDPOINT({ endpoint: i, duration: this.durationTime }); this.SELECT_ENDPOINT({ endpoint: i, duration: this.durationTime });
...@@ -71,6 +93,24 @@ limitations under the License. --> ...@@ -71,6 +93,24 @@ limitations under the License. -->
this.selectEndpoint(this.stateDashboardOption.endpoints[0]); this.selectEndpoint(this.stateDashboardOption.endpoints[0]);
}); });
} }
private async importData(event: any) {
try {
const data: any = await readFile(event);
if (!Array.isArray(data)) {
throw new Error();
}
this.$emit('changeEndpointComps', data);
const el: any = document.getElementById('endpoint-file');
el!.value = '';
} catch (e) {
this.$modal.show('dialog', { text: 'ERROR' });
}
}
private exportData() {
const data = this.endpointComps;
const name = 'endpointComps.json';
saveFile(data, name);
}
} }
</script> </script>
...@@ -80,4 +120,19 @@ limitations under the License. --> ...@@ -80,4 +120,19 @@ limitations under the License. -->
color: #efefef; color: #efefef;
background-color: #333840; background-color: #333840;
} }
.rk-dashboard-bar-btn {
padding: 0 5px;
border-right: 2px solid #252a2f;
height: 19px;
}
#endpoint-file {
display: none;
}
.input-label {
display: inline !important;
line-height: inherit;
}
.input-label.rk-btn {
line-height: 22px !important;
}
</style> </style>
...@@ -16,6 +16,25 @@ limitations under the License. --> ...@@ -16,6 +16,25 @@ limitations under the License. -->
<template> <template>
<div style="height: 100%"> <div style="height: 100%">
<div class="rk-dashboard-bar flex-h"> <div class="rk-dashboard-bar flex-h">
<span class="flex-h">
<div class="rk-dashboard-bar-btn">
<span v-tooltip:bottom="{ content: 'import' }">
<input id="instance-file" type="file" name="file" title="" accept=".json" @change="importData" />
<label class="rk-btn ghost input-label" for="instance-file">
<svg class="icon lg vm cp " :style="`marginTop: 0px`">
<use :xlink:href="'#folder_open'"></use>
</svg>
</label>
</span>
</div>
<div class="rk-dashboard-bar-btn">
<span v-tooltip:bottom="{ content: 'export' }">
<svg class="icon lg vm cp rk-btn ghost" @click="exportData">
<use :xlink:href="'#save_alt'"></use>
</svg>
</span>
</div>
</span>
<ToolBarSelect :selectable="false" :title="this.$t('currentService')" :current="current" icon="package" /> <ToolBarSelect :selectable="false" :title="this.$t('currentService')" :current="current" icon="package" />
<ToolBarSelect <ToolBarSelect
@onChoose="selectInstance" @onChoose="selectInstance"
...@@ -25,7 +44,7 @@ limitations under the License. --> ...@@ -25,7 +44,7 @@ limitations under the License. -->
icon="disk" icon="disk"
/> />
</div> </div>
<instances-survey :instanceComps="instanceComps" /> <instances-survey :instanceComps="instanceComps" :updateObjects="updateObjects" />
</div> </div>
</template> </template>
...@@ -37,6 +56,8 @@ limitations under the License. --> ...@@ -37,6 +56,8 @@ limitations under the License. -->
import Vue from 'vue'; import Vue from 'vue';
import { Component, PropSync, Watch, Prop } from 'vue-property-decorator'; import { Component, PropSync, Watch, Prop } from 'vue-property-decorator';
import { Action, Getter, State } from 'vuex-class'; import { Action, Getter, State } from 'vuex-class';
import { readFile } from '@/utils/readFile';
import { saveFile } from '@/utils/saveFile';
interface Instance { interface Instance {
label: string; label: string;
...@@ -60,6 +81,7 @@ limitations under the License. --> ...@@ -60,6 +81,7 @@ limitations under the License. -->
@Action('MIXHANDLE_CHANGE_GROUP_WITH_CURRENT') private MIXHANDLE_CHANGE_GROUP_WITH_CURRENT: any; @Action('MIXHANDLE_CHANGE_GROUP_WITH_CURRENT') private MIXHANDLE_CHANGE_GROUP_WITH_CURRENT: any;
@Prop() private current!: { key: number | string; label: number | string }; @Prop() private current!: { key: number | string; label: number | string };
@Prop() private instanceComps: any; @Prop() private instanceComps: any;
@Prop() private updateObjects!: string;
private selectInstance(i: any) { private selectInstance(i: any) {
this.SELECT_INSTANCE({ instance: i, duration: this.durationTime }); this.SELECT_INSTANCE({ instance: i, duration: this.durationTime });
...@@ -71,7 +93,38 @@ limitations under the License. --> ...@@ -71,7 +93,38 @@ limitations under the License. -->
this.selectInstance(this.stateDashboardOption.instances[0]); this.selectInstance(this.stateDashboardOption.instances[0]);
}); });
} }
private async importData(event: any) {
try {
const data: any = await readFile(event);
if (!Array.isArray(data)) {
throw new Error();
}
this.$emit('changeInstanceComps', data);
const el: any = document.getElementById('instance-file');
el!.value = '';
} catch (e) {
this.$modal.show('dialog', { text: 'ERROR' });
}
}
private exportData() {
const data = this.instanceComps;
const name = 'instanceComps.json';
saveFile(data, name);
}
} }
</script> </script>
<style lang="less" scoped></style> <style lang="scss" scoped>
.rk-dashboard-bar-btn {
padding: 0 5px;
border-right: 2px solid #252a2f;
height: 19px;
}
#instance-file {
display: none;
}
.input-label {
display: inline;
line-height: inherit;
}
</style>
...@@ -22,6 +22,7 @@ limitations under the License. --> ...@@ -22,6 +22,7 @@ limitations under the License. -->
:item="i" :item="i"
:index="index" :index="index"
:type="'TOPOLOGY_INSTANCE'" :type="'TOPOLOGY_INSTANCE'"
:updateObjects="updateObjects"
/> />
</div> </div>
</template> </template>
...@@ -39,6 +40,7 @@ limitations under the License. --> ...@@ -39,6 +40,7 @@ limitations under the License. -->
}) })
export default class InstancesSurvey extends Vue { export default class InstancesSurvey extends Vue {
@Prop() private instanceComps: any; @Prop() private instanceComps: any;
@Prop() private updateObjects!: string;
} }
</script> </script>
......
...@@ -25,8 +25,20 @@ limitations under the License. --> ...@@ -25,8 +25,20 @@ limitations under the License. -->
<TopoAside /> <TopoAside />
<TopoGroup /> <TopoGroup />
<rk-sidebox :show="dialog.length" @update:show="dialog = ''" :fixed="true" width="80%"> <rk-sidebox :show="dialog.length" @update:show="dialog = ''" :fixed="true" width="80%">
<window-endpoint v-if="dialog === 'endpoint'" :current="this.current" :endpointComps="endpointComps" /> <window-endpoint
<window-instance v-if="dialog === 'instance'" :current="this.current" :instanceComps="instanceComps" /> v-if="dialog === 'endpoint'"
:current="this.current"
:endpointComps="endpointComps"
@changeEndpointComps="changeEndpointComps"
:updateObjects="updateObjects"
/>
<window-instance
v-if="dialog === 'instance'"
:current="this.current"
:instanceComps="instanceComps"
@changeInstanceComps="changeInstanceComps"
:updateObjects="updateObjects"
/>
<window-trace v-if="dialog === 'trace'" :current="this.current" /> <window-trace v-if="dialog === 'trace'" :current="this.current" />
<window-alarm v-if="dialog === 'alarm'" :current="this.current" /> <window-alarm v-if="dialog === 'alarm'" :current="this.current" />
</rk-sidebox> </rk-sidebox>
...@@ -37,6 +49,7 @@ limitations under the License. --> ...@@ -37,6 +49,7 @@ limitations under the License. -->
import { State, Action, Getter, Mutation } from 'vuex-class'; import { State, Action, Getter, Mutation } from 'vuex-class';
import { AxiosResponse } from 'axios'; import { AxiosResponse } from 'axios';
import { State as topoState } from '@/store/modules/topology'; import { State as topoState } from '@/store/modules/topology';
import { TopologyType, ObjectsType } from '../../constant';
import WindowEndpoint from '@/views/containers/topology/endpoint/index.vue'; import WindowEndpoint from '@/views/containers/topology/endpoint/index.vue';
import WindowInstance from '@/views/containers/topology/instance/index.vue'; import WindowInstance from '@/views/containers/topology/instance/index.vue';
import WindowTrace from '@/views/containers/topology/trace/index.vue'; import WindowTrace from '@/views/containers/topology/trace/index.vue';
...@@ -67,7 +80,19 @@ limitations under the License. --> ...@@ -67,7 +80,19 @@ limitations under the License. -->
private dialog: string = ''; private dialog: string = '';
private instanceComps: any = []; private instanceComps: any = [];
private endpointComps: any = []; private endpointComps: any = [];
private updateObjects: string = '';
private created() { private created() {
if (window.localStorage.getItem('topologyInstances') || window.localStorage.getItem('topologyEndpoints')) {
const instanceComps: string = `${window.localStorage.getItem('topologyInstances')}`;
this.instanceComps = JSON.parse(instanceComps);
const endpointComps: string = `${window.localStorage.getItem('topologyEndpoints')}`;
this.endpointComps = JSON.parse(endpointComps);
} else {
this.queryTemplates();
}
}
private queryTemplates() {
this.GET_ALL_TEMPLATES().then( this.GET_ALL_TEMPLATES().then(
( (
allTemplates: Array<{ allTemplates: Array<{
...@@ -79,11 +104,13 @@ limitations under the License. --> ...@@ -79,11 +104,13 @@ limitations under the License. -->
}>, }>,
) => { ) => {
const template = const template =
allTemplates.filter((item: any) => item.type === 'TOPOLOGY_INSTANCE' && item.activated)[0] || {}; allTemplates.filter((item: any) => item.type === TopologyType.TOPOLOGY_INSTANCE && item.activated)[0] || {};
this.instanceComps = JSON.parse(template.configuration) || []; this.instanceComps = JSON.parse(template.configuration) || [];
window.localStorage.setItem('topologyInstances', JSON.stringify(this.instanceComps));
const endpointTemplate = const endpointTemplate =
allTemplates.filter((item: any) => item.type === 'TOPOLOGY_ENDPOINT' && item.activated)[0] || {}; allTemplates.filter((item: any) => item.type === TopologyType.TOPOLOGY_ENDPOINT && item.activated)[0] || {};
this.endpointComps = JSON.parse(endpointTemplate.configuration) || []; this.endpointComps = JSON.parse(endpointTemplate.configuration) || [];
window.localStorage.setItem('topologyEndpoints', JSON.stringify(this.endpointComps));
}, },
); );
} }
...@@ -94,6 +121,16 @@ limitations under the License. --> ...@@ -94,6 +121,16 @@ limitations under the License. -->
this.CLEAR_TOPO_INFO(); this.CLEAR_TOPO_INFO();
this.CLEAR_TOPO(); this.CLEAR_TOPO();
} }
private changeInstanceComps(data: any) {
this.updateObjects = ObjectsType.UPDATE_INSTANCES;
this.instanceComps.push(...data);
window.localStorage.setItem('topologyInstances', JSON.stringify(this.instanceComps));
}
private changeEndpointComps(data: any) {
this.updateObjects = ObjectsType.UPDATE_ENDPOINTS;
this.endpointComps.push(...data);
window.localStorage.setItem('topologyEndpoints', JSON.stringify(this.endpointComps));
}
} }
</script> </script>
<style lang="scss"> <style lang="scss">
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册