This is an automated email from the ASF dual-hosted git repository.
wusheng pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/skywalking-rocketbot-ui.git
The following commit(s) were added to refs/heads/master by this push:
new 319ce5f Feat:Import and export the configuration file for the
dashboard layout (#288)
319ce5f is described below
commit 319ce5f525ce5b986e4ebe6f5607e0c1dc625636
Author: Juntao Zhang <[email protected]>
AuthorDate: Wed Jun 3 18:10:19 2020 +0800
Feat:Import and export the configuration file for the dashboard layout
(#288)
---
src/assets/styles/lib.scss | 3 +
.../modules/dashboard/dashboard-data-layout.ts | 14 +++
.../modules/dashboard/dashboard-data-query.ts | 5 +-
src/store/modules/dashboard/mutation-types.ts | 2 +
src/utils/readFile.ts | 33 +++++++
src/utils/saveFile.ts | 28 ++++++
src/views/components/dashboard/dashboard-item.vue | 8 +-
src/views/components/dashboard/tool-bar-btns.vue | 109 +++++++++++++++++++++
src/views/components/dashboard/tool-bar.vue | 57 ++---------
src/views/components/dashboard/tool-group.vue | 3 +
src/views/components/dashboard/tool-nav.vue | 52 +++++++++-
src/views/constant.ts | 26 +++++
src/views/containers/dashboard.vue | 2 +
.../topology/endpoint/endpoints-survey.vue | 2 +
src/views/containers/topology/endpoint/index.vue | 57 ++++++++++-
src/views/containers/topology/instance/index.vue | 57 ++++++++++-
.../topology/instance/instances-survey.vue | 2 +
src/views/containers/topology/topology.vue | 45 ++++++++-
18 files changed, 448 insertions(+), 57 deletions(-)
diff --git a/src/assets/styles/lib.scss b/src/assets/styles/lib.scss
index 8841ccd..b2e7297 100644
--- a/src/assets/styles/lib.scss
+++ b/src/assets/styles/lib.scss
@@ -127,6 +127,9 @@
.ml-5 {
margin-left: 5px;
}
+.ml-10 {
+ margin-left: 10px;
+}
.mr-0 {
margin-right: 0px;
}
diff --git a/src/store/modules/dashboard/dashboard-data-layout.ts
b/src/store/modules/dashboard/dashboard-data-layout.ts
index 1d83948..0b44edb 100644
--- a/src/store/modules/dashboard/dashboard-data-layout.ts
+++ b/src/store/modules/dashboard/dashboard-data-layout.ts
@@ -65,6 +65,10 @@ const mutations: MutationTree<State> = {
[types.SET_COMPS_TREE](state: State, data: CompsTree[]) {
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) {
state.tree[state.group].query = params;
},
@@ -137,12 +141,22 @@ const mutations: MutationTree<State> = {
state.tree[state.group].children.push({ name: params.name, children: [] });
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) {
state.tree.splice(index, 1);
+ if (!state.tree[state.group]) {
+ state.group--;
+ }
window.localStorage.setItem('dashboard', JSON.stringify(state.tree));
},
[types.DELETE_COMPS_TREE](state: State, index: number) {
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));
},
[types.ADD_COMP](state: State) {
diff --git a/src/store/modules/dashboard/dashboard-data-query.ts
b/src/store/modules/dashboard/dashboard-data-query.ts
index 45d30d0..1e7b013 100644
--- a/src/store/modules/dashboard/dashboard-data-query.ts
+++ b/src/store/modules/dashboard/dashboard-data-query.ts
@@ -28,14 +28,17 @@ const actions: ActionTree<State, any> = {
params: {
index: number;
duration: any;
+ itemConfig: any;
},
) {
const { currentDatabase, currentEndpoint, currentInstance, currentService
} = context.rootState.rocketOption;
const dashboard: string = `${window.localStorage.getItem('dashboard')}`;
const tree = JSON.parse(dashboard) || context.state.tree;
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'];
+
if (!config) {
return;
}
diff --git a/src/store/modules/dashboard/mutation-types.ts
b/src/store/modules/dashboard/mutation-types.ts
index 2668093..f5aaa35 100644
--- a/src/store/modules/dashboard/mutation-types.ts
+++ b/src/store/modules/dashboard/mutation-types.ts
@@ -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_COMPS = 'SET_CURRENT_COMPS';
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 DELETE_COMPS_GROUP = 'DELETE_COMPS_GROUP';
export const DELETE_COMPS_TREE = 'DELETE_COMPS_TREE';
diff --git a/src/utils/readFile.ts b/src/utils/readFile.ts
new file mode 100644
index 0000000..07667d6
--- /dev/null
+++ b/src/utils/readFile.ts
@@ -0,0 +1,33 @@
+/**
+ * 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));
+ }
+ };
+ });
+};
diff --git a/src/utils/saveFile.ts b/src/utils/saveFile.ts
new file mode 100644
index 0000000..79883a6
--- /dev/null
+++ b/src/utils/saveFile.ts
@@ -0,0 +1,28 @@
+/**
+ * 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);
+};
diff --git a/src/views/components/dashboard/dashboard-item.vue
b/src/views/components/dashboard/dashboard-item.vue
index 78061b0..0f10c62 100644
--- a/src/views/components/dashboard/dashboard-item.vue
+++ b/src/views/components/dashboard/dashboard-item.vue
@@ -64,6 +64,7 @@ limitations under the License. -->
import { Vue, Component, Prop, Watch } from 'vue-property-decorator';
import charts from './charts';
import { QueryTypes } from './constant';
+ import { TopologyType, ObjectsType } from '../../constant';
import { MetricsType, CalculationType } from './charts/constant';
import { uuid } from '@/utils/uuid.ts';
@@ -84,6 +85,7 @@ limitations under the License. -->
@Prop() private item!: any;
@Prop() private index!: number;
@Prop() private type!: string;
+ @Prop() private updateObjects!: string;
private pageTypes = ['TOPOLOGY_ENDPOINT', 'TOPOLOGY_INSTANCE'];
private dialogConfigVisible = false;
@@ -102,8 +104,9 @@ limitations under the License. -->
this.height = this.item.height;
this.unit = this.item.unit;
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;
}
this.chartRender();
@@ -113,9 +116,12 @@ limitations under the License. -->
if (this.rocketGlobal.edit) {
return;
}
+ const pageTypes = [TopologyType.TOPOLOGY_ENDPOINT,
TopologyType.TOPOLOGY_INSTANCE] as any[];
+
this.GET_QUERY({
duration: this.durationTime,
index: this.index,
+ itemConfig: pageTypes.includes(this.type) ? this.itemConfig :
undefined,
}).then((params: Array<{ metricName: string; [key: string]: any; config:
any }>) => {
if (!params) {
return;
diff --git a/src/views/components/dashboard/tool-bar-btns.vue
b/src/views/components/dashboard/tool-bar-btns.vue
new file mode 100644
index 0000000..4543a40
--- /dev/null
+++ b/src/views/components/dashboard/tool-bar-btns.vue
@@ -0,0 +1,109 @@
+<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>
diff --git a/src/views/components/dashboard/tool-bar.vue
b/src/views/components/dashboard/tool-bar.vue
index 026908d..39234bf 100644
--- a/src/views/components/dashboard/tool-bar.vue
+++ b/src/views/components/dashboard/tool-bar.vue
@@ -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
limitations under the License. -->
<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-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>{{ this.$t('serviceFilter') }}</div>
<input type="text" :value="rocketOption.keywordService"
@change="searchServices($event.target.value)" />
@@ -61,20 +52,6 @@ limitations under the License. -->
/>
</div>
<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
@onChoose="SELECT_DATABASE"
:title="this.$t('currentDatabase')"
@@ -90,10 +67,11 @@ limitations under the License. -->
import { Vue, Component, Prop } from 'vue-property-decorator';
import ToolBarSelect from './tool-bar-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 { DASHBOARDTYPE } from './constant';
- @Component({ components: { ToolBarSelect, ToolBarEndpointSelect } })
+ @Component({ components: { ToolBarSelect, ToolBarEndpointSelect, ToolBarBtns
} })
export default class ToolBar extends Vue {
@Prop() private compType!: any;
@Prop() private stateDashboard!: any;
@@ -103,7 +81,6 @@ limitations under the License. -->
@State('rocketOption') private rocketOption: any;
@Mutation('ADD_COMP') private ADD_COMP: any;
@Mutation('SET_KEYWORDSERVICE') private SET_KEYWORDSERVICE: any;
- @Action('SET_EDIT') private SET_EDIT: any;
@Action('SELECT_SERVICE') private SELECT_SERVICE: any;
@Action('SELECT_DATABASE') private SELECT_DATABASE: any;
@Action('SELECT_ENDPOINT') private SELECT_ENDPOINT: any;
@@ -118,16 +95,6 @@ limitations under the License. -->
}
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) {
this.SELECT_SERVICE({ service: i, duration: this.durationTime });
}
@@ -164,8 +131,4 @@ limitations under the License. -->
}
}
}
- .rk-dashboard-bar-reload {
- padding: 15px 5px;
- border-right: 2px solid #252a2f;
- }
</style>
diff --git a/src/views/components/dashboard/tool-group.vue
b/src/views/components/dashboard/tool-group.vue
index 59b1b8d..f5b68ac 100644
--- a/src/views/components/dashboard/tool-group.vue
+++ b/src/views/components/dashboard/tool-group.vue
@@ -60,11 +60,14 @@ limitations under the License. -->
import { Component, Prop } from 'vue-property-decorator';
import { Mutation, Action, Getter } from 'vuex-class';
import { DASHBOARDTYPE } from './constant';
+ import { readFile } from '@/utils/readFile';
+ import { saveFile } from '@/utils/saveFile';
@Component({})
export default class ToolGroup extends Vue {
@Prop() private rocketGlobal: any;
@Prop() private rocketComps: any;
+ @Mutation('SET_COMPS_TREE') private SET_COMPS_TREE: any;
@Mutation('DELETE_COMPS_GROUP') private DELETE_COMPS_GROUP: any;
@Mutation('ADD_COMPS_GROUP') private ADD_COMPS_GROUP: any;
@Action('MIXHANDLE_CHANGE_GROUP') private MIXHANDLE_CHANGE_GROUP: any;
diff --git a/src/views/components/dashboard/tool-nav.vue
b/src/views/components/dashboard/tool-nav.vue
index 7585f39..1ccb2c0 100644
--- a/src/views/components/dashboard/tool-nav.vue
+++ b/src/views/components/dashboard/tool-nav.vue
@@ -32,7 +32,7 @@ limitations under the License. -->
<use xlink:href="#file-deletion"></use>
</svg>
</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">
<use xlink:href="#todo-add"></use>
</svg>
@@ -62,6 +62,19 @@ limitations under the License. -->
<a class="rk-btn r vm long tc confirm" @click="handleCreate">{{
$t('confirm') }}</a>
</div>
</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>
</template>
@@ -69,11 +82,14 @@ limitations under the License. -->
import Vue from 'vue';
import { Component, Prop, Model } from 'vue-property-decorator';
import { State, Mutation, Action } from 'vuex-class';
+ import { readFile } from '@/utils/readFile';
+ import { saveFile } from '@/utils/saveFile';
@Component
export default class ToolNav extends Vue {
@Prop() private rocketGlobal: any;
@Prop() private rocketComps: any;
+ @Mutation('IMPORT_COMPS_TREE') private IMPORT_COMPS_TREE: any;
@Mutation('SET_CURRENT_COMPS') private SET_CURRENT_COMPS: any;
@Mutation('DELETE_COMPS_TREE') private DELETE_COMPS_TREE: any;
@Mutation('ADD_COMPS_TREE') private ADD_COMPS_TREE: any;
@@ -96,6 +112,27 @@ limitations under the License. -->
this.handleHide();
// 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>
@@ -164,4 +201,17 @@ limitations under the License. -->
.confirm {
margin: 10px 0;
}
+ .rk-dashboard-import {
+ .icon.open {
+ margin-top: 2px;
+ }
+ .ipt {
+ display: none;
+ }
+ .input-label {
+ display: inline;
+ line-height: inherit;
+ cursor: pointer;
+ }
+ }
</style>
diff --git a/src/views/constant.ts b/src/views/constant.ts
new file mode 100644
index 0000000..5e0a3fe
--- /dev/null
+++ b/src/views/constant.ts
@@ -0,0 +1,26 @@
+/**
+ * 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',
+}
diff --git a/src/views/containers/dashboard.vue
b/src/views/containers/dashboard.vue
index b616fac..beffe15 100644
--- a/src/views/containers/dashboard.vue
+++ b/src/views/containers/dashboard.vue
@@ -17,6 +17,7 @@ limitations under the License. -->
<ToolGroup :rocketGlobal="rocketGlobal" :rocketComps="rocketComps" />
<ToolBar
:rocketGlobal="rocketGlobal"
+ :rocketComps="rocketComps"
:compType="compType"
:durationTime="durationTime"
:stateDashboard="stateDashboardOption"
@@ -36,6 +37,7 @@ limitations under the License. -->
+ Add An Item
</div>
</div>
+ <v-dialog width="300px" />
</div>
</template>
diff --git a/src/views/containers/topology/endpoint/endpoints-survey.vue
b/src/views/containers/topology/endpoint/endpoints-survey.vue
index 218969d..9670d26 100644
--- a/src/views/containers/topology/endpoint/endpoints-survey.vue
+++ b/src/views/containers/topology/endpoint/endpoints-survey.vue
@@ -22,6 +22,7 @@ limitations under the License. -->
:item="i"
:index="index"
:type="'TOPOLOGY_ENDPOINT'"
+ :updateObjects="updateObjects"
/>
</div>
</template>
@@ -39,6 +40,7 @@ limitations under the License. -->
})
export default class InstancesSurvey extends Vue {
@Prop() private endpointComps: any;
+ @Prop() private updateObjects!: string;
}
</script>
diff --git a/src/views/containers/topology/endpoint/index.vue
b/src/views/containers/topology/endpoint/index.vue
index 609fa41..13e2a62 100644
--- a/src/views/containers/topology/endpoint/index.vue
+++ b/src/views/containers/topology/endpoint/index.vue
@@ -15,6 +15,25 @@ limitations under the License. -->
<template>
<div>
<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" />
<ToolBarEndpointSelect
@onChoose="selectEndpoint"
@@ -24,7 +43,7 @@ limitations under the License. -->
icon="code"
/>
</div>
- <endpoints-survey :endpointComps="endpointComps" />
+ <endpoints-survey :endpointComps="endpointComps"
:updateObjects="updateObjects" />
</div>
</template>
@@ -35,6 +54,8 @@ limitations under the License. -->
import EndpointsSurvey from './endpoints-survey.vue';
import ToolBarSelect from '@/views/components/dashboard/tool-bar-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 {
label: string;
@@ -59,6 +80,7 @@ limitations under the License. -->
@Action('MIXHANDLE_CHANGE_GROUP_WITH_CURRENT') private
MIXHANDLE_CHANGE_GROUP_WITH_CURRENT: any;
@Prop() private current!: { key: number | string; label: number | string };
@Prop() private endpointComps: any;
+ @Prop() private updateObjects!: string;
private selectEndpoint(i: any) {
this.SELECT_ENDPOINT({ endpoint: i, duration: this.durationTime });
@@ -71,6 +93,24 @@ limitations under the License. -->
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>
@@ -80,4 +120,19 @@ limitations under the License. -->
color: #efefef;
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>
diff --git a/src/views/containers/topology/instance/index.vue
b/src/views/containers/topology/instance/index.vue
index 017fa68..5a3e1f6 100644
--- a/src/views/containers/topology/instance/index.vue
+++ b/src/views/containers/topology/instance/index.vue
@@ -16,6 +16,25 @@ limitations under the License. -->
<template>
<div style="height: 100%">
<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
@onChoose="selectInstance"
@@ -25,7 +44,7 @@ limitations under the License. -->
icon="disk"
/>
</div>
- <instances-survey :instanceComps="instanceComps" />
+ <instances-survey :instanceComps="instanceComps"
:updateObjects="updateObjects" />
</div>
</template>
@@ -37,6 +56,8 @@ limitations under the License. -->
import Vue from 'vue';
import { Component, PropSync, Watch, Prop } from 'vue-property-decorator';
import { Action, Getter, State } from 'vuex-class';
+ import { readFile } from '@/utils/readFile';
+ import { saveFile } from '@/utils/saveFile';
interface Instance {
label: string;
@@ -60,6 +81,7 @@ limitations under the License. -->
@Action('MIXHANDLE_CHANGE_GROUP_WITH_CURRENT') private
MIXHANDLE_CHANGE_GROUP_WITH_CURRENT: any;
@Prop() private current!: { key: number | string; label: number | string };
@Prop() private instanceComps: any;
+ @Prop() private updateObjects!: string;
private selectInstance(i: any) {
this.SELECT_INSTANCE({ instance: i, duration: this.durationTime });
@@ -71,7 +93,38 @@ limitations under the License. -->
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>
-<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>
diff --git a/src/views/containers/topology/instance/instances-survey.vue
b/src/views/containers/topology/instance/instances-survey.vue
index d65c52e..90f01e5 100644
--- a/src/views/containers/topology/instance/instances-survey.vue
+++ b/src/views/containers/topology/instance/instances-survey.vue
@@ -22,6 +22,7 @@ limitations under the License. -->
:item="i"
:index="index"
:type="'TOPOLOGY_INSTANCE'"
+ :updateObjects="updateObjects"
/>
</div>
</template>
@@ -39,6 +40,7 @@ limitations under the License. -->
})
export default class InstancesSurvey extends Vue {
@Prop() private instanceComps: any;
+ @Prop() private updateObjects!: string;
}
</script>
diff --git a/src/views/containers/topology/topology.vue
b/src/views/containers/topology/topology.vue
index 5a458ad..759f871 100644
--- a/src/views/containers/topology/topology.vue
+++ b/src/views/containers/topology/topology.vue
@@ -25,8 +25,20 @@ limitations under the License. -->
<TopoAside />
<TopoGroup />
<rk-sidebox :show="dialog.length" @update:show="dialog = ''" :fixed="true"
width="80%">
- <window-endpoint v-if="dialog === 'endpoint'" :current="this.current"
:endpointComps="endpointComps" />
- <window-instance v-if="dialog === 'instance'" :current="this.current"
:instanceComps="instanceComps" />
+ <window-endpoint
+ 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-alarm v-if="dialog === 'alarm'" :current="this.current" />
</rk-sidebox>
@@ -37,6 +49,7 @@ limitations under the License. -->
import { State, Action, Getter, Mutation } from 'vuex-class';
import { AxiosResponse } from 'axios';
import { State as topoState } from '@/store/modules/topology';
+ import { TopologyType, ObjectsType } from '../../constant';
import WindowEndpoint from '@/views/containers/topology/endpoint/index.vue';
import WindowInstance from '@/views/containers/topology/instance/index.vue';
import WindowTrace from '@/views/containers/topology/trace/index.vue';
@@ -67,7 +80,19 @@ limitations under the License. -->
private dialog: string = '';
private instanceComps: any = [];
private endpointComps: any = [];
+ private updateObjects: string = '';
+
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(
(
allTemplates: Array<{
@@ -79,11 +104,13 @@ limitations under the License. -->
}>,
) => {
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) || [];
+ window.localStorage.setItem('topologyInstances',
JSON.stringify(this.instanceComps));
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) ||
[];
+ window.localStorage.setItem('topologyEndpoints',
JSON.stringify(this.endpointComps));
},
);
}
@@ -94,6 +121,16 @@ limitations under the License. -->
this.CLEAR_TOPO_INFO();
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>
<style lang="scss">