Feat: Optimize global settings (#554)
diff --git a/src/assets/lang/en.ts b/src/assets/lang/en.ts
index f29b106..38e570a 100644
--- a/src/assets/lang/en.ts
+++ b/src/assets/lang/en.ts
@@ -181,7 +181,7 @@
logCategory: 'Log Category',
errorCatalog: 'Error Catalog',
logDetail: 'Log Detail ',
- timeReload: 'The time interval must be greater than 0',
+ timeReload: 'Notice: The time interval must be greater than 0',
errorInfo: 'Error Info',
stack: 'Stack',
serviceVersion: 'Service Version',
@@ -258,6 +258,7 @@
yes: 'Yes',
no: 'No',
cacheReminderContent: 'SkyWalking detected dashboard template updates, do you want to update?',
+ language: 'Language',
};
export default m;
diff --git a/src/assets/lang/zh.ts b/src/assets/lang/zh.ts
index 3000465..684a256 100644
--- a/src/assets/lang/zh.ts
+++ b/src/assets/lang/zh.ts
@@ -180,7 +180,7 @@
logCategory: '日志类别',
errorCatalog: '错误类目',
logDetail: '日志详情',
- timeReload: '时间间隔必须大于0',
+ timeReload: '注意:时间间隔必须大于0',
errorInfo: '错误信息',
stack: '堆栈',
serviceVersion: '服务版本',
@@ -256,6 +256,7 @@
yes: '是的',
no: '不',
cacheReminderContent: 'SkyWalking检测到仪表板模板更新,是否需要更新?',
+ language: '语言',
};
export default m;
diff --git a/src/components/index.ts b/src/components/index.ts
index 7918f5a..04a0933 100644
--- a/src/components/index.ts
+++ b/src/components/index.ts
@@ -32,6 +32,7 @@
import RkIcon from './rk-icon.vue';
import RkRadio from './rk-radio.vue';
import RkAlert from './rk-alert.vue';
+import RkSwitch from './rk-switch.vue';
const components: any = {
RkProgress,
@@ -50,6 +51,7 @@
RkIcon,
RkRadio,
RkAlert,
+ RkSwitch,
};
const componentsName: string[] = Object.keys(components);
diff --git a/src/components/rk-date.vue b/src/components/rk-date.vue
index b4f5a79..20b0978 100755
--- a/src/components/rk-date.vue
+++ b/src/components/rk-date.vue
@@ -436,7 +436,7 @@
}
.datepicker-range {
- min-width: 280px;
+ min-width: 238px;
}
.datepicker-range .datepicker-popup {
diff --git a/src/components/rk-footer-time.vue b/src/components/rk-footer-time.vue
index b16a679..8f713f0 100644
--- a/src/components/rk-footer-time.vue
+++ b/src/components/rk-footer-time.vue
@@ -15,7 +15,7 @@
<template>
<div>
<span class="rk-time-tips" v-show="timeRange">{{ $t('timeTips') }}</span>
- <RkDate class="mr-10" v-model="time" position="top" format="YYYY-MM-DD HH:mm" />
+ <RkDate class="mr-10" v-model="time" position="bottom" format="YYYY-MM-DD HH:mm" />
</div>
</template>
diff --git a/src/components/rk-sidebox.vue b/src/components/rk-sidebox.vue
index 7cc1660..2e0e41a 100644
--- a/src/components/rk-sidebox.vue
+++ b/src/components/rk-sidebox.vue
@@ -82,12 +82,12 @@
position: fixed;
right: 0;
top: 50px;
- bottom: 30px;
+ bottom: 0;
z-index: 200;
background-color: #fff;
}
.rk-sidebox-inner {
- padding: 20px;
+ padding: 0 20px;
}
.rk-sidebox-inner-fixed {
height: 100%;
diff --git a/src/components/rk-switch.vue b/src/components/rk-switch.vue
new file mode 100644
index 0000000..ee70857
--- /dev/null
+++ b/src/components/rk-switch.vue
@@ -0,0 +1,86 @@
+<!-- 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. -->
+
+<template>
+ <button type="button" class="rk-switch" :class="checked ? 'switch-checked' : ''" @click="changeStatus">
+ <div class="switch-handle"></div>
+ <span class="switch-inner"></span>
+ </button>
+</template>
+
+<script lang="ts">
+ import { Vue, Component, Prop } from 'vue-property-decorator';
+ @Component
+ export default class RkSwitch extends Vue {
+ @Prop() private checked!: boolean;
+
+ private changeStatus() {
+ this.$emit('onChange', !this.checked);
+ }
+ }
+</script>
+<style lang="scss" scoped>
+ .rk-switch {
+ padding: 0;
+ color: #000000d9;
+ font-size: 14px;
+ line-height: 1.5715;
+ position: relative;
+ display: inline-block;
+ min-width: 44px;
+ height: 22px;
+ line-height: 22px;
+ background-color: #00000040;
+ border: 0;
+ border-radius: 100px;
+ cursor: pointer;
+ transition: all 0.2s;
+ }
+ .switch-checked {
+ background-color: #448dfe;
+ }
+ .switch-inner {
+ margin: 0 25px 0 7px;
+ display: block;
+ margin: 0 7px 0 25px;
+ color: #fff;
+ font-size: 12px;
+ transition: all 0.2s;
+ }
+ .switch-handle {
+ position: absolute;
+ top: 2px;
+ left: 2px;
+ width: 18px;
+ height: 18px;
+ transition: all 0.2s ease-in-out;
+ &:before {
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ background-color: #fff;
+ border-radius: 9px;
+ box-shadow: 0 2px 4px #00230b33;
+ transition: all 0.2s ease-in-out;
+ content: '';
+ }
+ }
+ .switch-checked .switch-handle {
+ left: calc(100% - 20px);
+ transition: all 0.2s;
+ }
+</style>
diff --git a/src/views/components/common/page-tool-bar.vue b/src/views/components/common/page-tool-bar.vue
new file mode 100644
index 0000000..ce61f51
--- /dev/null
+++ b/src/views/components/common/page-tool-bar.vue
@@ -0,0 +1,240 @@
+<!-- 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. -->
+<template>
+ <div class="sm flex-h page-tools">
+ <RkFooterTime />
+ <!-- <span class="mr-10 cp" @click="openSettings">{{ lang === 'zh' ? '中' : 'En' }}</span> -->
+ <span class="mr-10 cp" @click="openSettings">
+ UTC{{ utcHour >= 0 ? '+' : '' }}{{ `${this.utcHour}:${this.utcMin}` }}
+ </span>
+ <span class="mr-10 sm" :class="auto ? 'blue' : 'ghost'" @click="openSettings">
+ <a>{{ $t('auto') }}</a>
+ </span>
+ <a class="sm ghost" @click="handleReload">
+ <rk-icon icon="retry" :loading="auto" />
+ <span>{{ $t('reload') }}</span>
+ </a>
+ <div class="tool-bar-setting " v-show="showSetting">
+ <div class="flex-h item">
+ <span class="label">{{ $t('language') }}</span>
+ <span>Zh</span>
+ <rk-switch class="mr-5 ml-5" :checked="lang === 'en'" @onChange="setLang" />
+ <span>En</span>
+ </div>
+ <div class="flex-h item">
+ <span class="label">{{ $t('serverZone') }}</span>
+ <div>
+ <span>UTC{{ utcHour >= 0 ? '+' : '' }}</span>
+ <input v-model="utcHour" min="-12" max="14" class="rk-utc" type="number" />
+ <span>:</span>
+ <span class="utc-min">{{ utcMin > 9 || utcMin === 0 ? null : 0 }}</span>
+ <input v-model="utcMin" min="0" max="59" class="rk-utc" type="number" />
+ </div>
+ </div>
+ <div class="flex-h item">
+ <span class="label">{{ $t('auto') }}</span>
+ <rk-switch class="mr-10" :checked="auto" @onChange="handleAuto" />
+ <div class="auto-time">
+ <span class="rk-auto-select">
+ <input v-model="autoTime" type="number" @change="changeAutoTime" min="1" />
+ </span>
+ {{ $t('second') }}
+ <i class="ml-10">{{ $t('timeReload') }}</i>
+ </div>
+ </div>
+ </div>
+ </div>
+</template>
+
+<script lang="ts">
+ import { Vue, Component, Watch } from 'vue-property-decorator';
+ import { State, Action, Getter } from 'vuex-class';
+ import timeFormat from '@/utils/timeFormat';
+
+ @Component
+ export default class PageToolBar extends Vue {
+ @Getter('duration') private duration: any;
+ @State('rocketbot') private rocketbotGlobal: any;
+ @Action('SET_DURATION') private SET_DURATION: any;
+ @Action('SET_UTC') private SET_UTC: any;
+ private lang: string | null = '';
+ private utcHour: number = 0;
+ private utcMin: number = 0;
+ private show: boolean = false;
+ private auto: boolean = false;
+ private autoTime: number = 6;
+ private timer: any = null;
+ private showSetting: boolean = false;
+
+ private beforeMount() {
+ let utc = localStorage.getItem('utc') || '';
+ if (!utc.includes(':')) {
+ utc = (localStorage.getItem('utc') || -(new Date().getTimezoneOffset() / 60)) + ':0';
+ }
+ const utcArr = (utc || '').split(':');
+ this.utcHour = isNaN(Number(utcArr[0])) ? 0 : Number(utcArr[0]);
+ this.utcMin = isNaN(Number(utcArr[1])) ? 0 : Number(utcArr[1]);
+ this.SET_UTC(`${this.utcHour}:${this.utcMin}`);
+ this.lang = window.localStorage.getItem('lang');
+ }
+
+ private mounted() {
+ document.addEventListener('click', this.closeSettings, true);
+ }
+
+ private openSettings() {
+ this.showSetting = true;
+ }
+
+ private setLang() {
+ if (this.lang === 'zh') {
+ this.$i18n.locale = 'en';
+ window.localStorage.setItem('lang', 'en');
+ this.lang = 'en';
+ } else {
+ this.$i18n.locale = 'zh';
+ window.localStorage.setItem('lang', 'zh');
+ this.lang = 'zh';
+ }
+ }
+
+ private handleReload() {
+ const gap = this.duration.end.getTime() - this.duration.start.getTime();
+ const time: Date[] = [new Date(new Date().getTime() - gap), new Date()];
+ this.SET_DURATION(timeFormat(time));
+ }
+ private handleAuto(status: boolean) {
+ if (this.autoTime < 1) {
+ return;
+ }
+ this.auto = status;
+ if (this.auto) {
+ this.handleReload();
+ this.timer = setInterval(this.handleReload, this.autoTime * 1000);
+ } else {
+ clearInterval(this.timer);
+ }
+ }
+ private handleHide() {
+ this.show = false;
+ }
+ private handleShow() {
+ this.show = !this.show;
+ }
+ private changeAutoTime() {
+ if (this.autoTime < 1) {
+ return;
+ }
+ clearInterval(this.timer);
+ if (this.auto) {
+ this.handleReload();
+ this.timer = setInterval(this.handleReload, this.autoTime * 1000);
+ }
+ }
+
+ private closeSettings(e: any) {
+ this.showSetting = this.$el.contains(e.target) && !this.showSetting;
+ }
+
+ @Watch('utcHour')
+ private onUtcUpdate() {
+ if (this.utcHour < -12) {
+ this.utcHour = -12;
+ }
+ if (this.utcHour > 14) {
+ this.utcHour = 14;
+ }
+ if (isNaN(this.utcHour)) {
+ this.utcHour = 0;
+ }
+ this.SET_UTC(`${this.utcHour}:${this.utcMin}`);
+ localStorage.setItem('utc', `${this.utcHour}:${this.utcMin}`);
+ }
+
+ @Watch('utcMin')
+ private onUtcMinUpdate() {
+ if (this.utcMin < 0) {
+ this.utcMin = 0;
+ }
+ if (this.utcMin > 59) {
+ this.utcMin = 59;
+ }
+ if (isNaN(this.utcMin)) {
+ this.utcMin = 0;
+ }
+ this.SET_UTC(`${this.utcHour}:${this.utcMin}`);
+ localStorage.setItem('utc', `${this.utcHour}:${this.utcMin}`);
+ }
+ }
+</script>
+
+<style lang="scss" scoped>
+ .page-tools {
+ position: relative;
+ }
+ .rk-utc {
+ color: inherit;
+ background: 0;
+ border: 0;
+ outline: none;
+ width: 40px;
+ padding-bottom: 0;
+ }
+ .utc-min {
+ display: inline-block;
+ padding-top: 2px;
+ }
+ .rk-auto-select {
+ border-radius: 3px;
+ background-color: #fff;
+ padding: 1px;
+ border-radius: 3px;
+
+ input {
+ width: 38px;
+ border-style: unset;
+ outline: 0;
+ }
+ }
+ .tool-bar-setting {
+ position: absolute;
+ top: 30px;
+ right: 0;
+ color: #666;
+ font-family: inherit;
+ font-size: 12px;
+ background: #fff;
+ z-index: 10000;
+ padding: 20px;
+ border-radius: 3px;
+ box-shadow: 0 2px 4px #00230b33;
+ width: 550px;
+ .item {
+ margin-top: 10px;
+ }
+ input {
+ outline: 0;
+ width: 40px;
+ border-radius: 3px;
+ border: 1px solid #ccc;
+ text-align: center;
+ height: 22px;
+ }
+ .label {
+ width: 100px;
+ display: inline-block;
+ }
+ }
+</style>
diff --git a/src/views/components/common/rk-footer.vue b/src/views/components/common/rk-footer.vue
deleted file mode 100644
index 6448b51..0000000
--- a/src/views/components/common/rk-footer.vue
+++ /dev/null
@@ -1,137 +0,0 @@
-<!-- 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. -->
-<template>
- <footer class="rk-footer trans" :class="{ 'rk-footer-dark': $route.path === '/topology' }">
- <div class="rk-footer-inner">
- <div class="flex-h"></div>
- <div class="sm flex-h">
- <RkFooterTime />
- <span class="mr-15 cp" @click="setLang">{{ lang === 'zh' ? '中' : 'En' }}</span>
- <span>{{ $t('serverZone') }} UTC {{ utcHour >= 0 ? '+' : '' }}</span>
- <input v-model="utcHour" min="-12" max="14" class="rk-footer-utc" type="number" />
- <span>: </span>
- <span class="utc-min">{{ utcMin > 9 ? null : 0 }}</span>
- <input v-model="utcMin" min="0" max="59" class="rk-footer-utc" type="number" />
- </div>
- </div>
- </footer>
-</template>
-
-<script lang="ts">
- import { Vue, Component, Watch } from 'vue-property-decorator';
- import { State, Action } from 'vuex-class';
-
- @Component
- export default class Footerssd extends Vue {
- @State('rocketbot') private rocketbotGlobal: any;
- @Action('SET_DURATION') private SET_DURATION: any;
- @Action('SET_UTC') private SET_UTC: any;
- private lang: string | null = '';
- private utcHour: number = 0;
- private utcMin: number = 0;
-
- private beforeMount() {
- let utc = localStorage.getItem('utc') || '';
- if (!utc.includes(':')) {
- utc = (localStorage.getItem('utc') || -(new Date().getTimezoneOffset() / 60)) + ':0';
- }
- const utcArr = (utc || '').split(':');
- this.utcHour = isNaN(Number(utcArr[0])) ? 0 : Number(utcArr[0]);
- this.utcMin = isNaN(Number(utcArr[1])) ? 0 : Number(utcArr[1]);
- this.SET_UTC(`${this.utcHour}:${this.utcMin}`);
- this.lang = window.localStorage.getItem('lang');
- }
-
- private setLang() {
- if (this.lang === 'zh') {
- this.$i18n.locale = 'en';
- window.localStorage.setItem('lang', 'en');
- this.lang = 'en';
- } else {
- this.$i18n.locale = 'zh';
- window.localStorage.setItem('lang', 'zh');
- this.lang = 'zh';
- }
- }
-
- @Watch('utcHour')
- private onUtcUpdate() {
- if (this.utcHour < -12) {
- this.utcHour = -12;
- }
- if (this.utcHour > 14) {
- this.utcHour = 14;
- }
- if (isNaN(this.utcHour)) {
- this.utcHour = 0;
- }
- this.SET_UTC(`${this.utcHour}:${this.utcMin}`);
- localStorage.setItem('utc', `${this.utcHour}:${this.utcMin}`);
- }
-
- @Watch('utcMin')
- private onUtcMinUpdate() {
- if (this.utcMin < 0) {
- this.utcMin = 0;
- }
- if (this.utcMin > 59) {
- this.utcMin = 59;
- }
- if (isNaN(this.utcMin)) {
- this.utcMin = 0;
- }
- this.SET_UTC(`${this.utcHour}:${this.utcMin}`);
- localStorage.setItem('utc', `${this.utcHour}:${this.utcMin}`);
- }
- }
-</script>
-
-<style scoped>
- .rk-footer {
- color: #515a6e;
- flex-shrink: 0;
- padding-right: 15px;
- padding-left: 15px;
- padding-bottom: 1px;
- box-shadow: 0 -1px 0px rgba(0, 0, 0, 0.08);
- z-index: 2;
- }
- .rk-footer-dark {
- color: #ddd;
- background: #252a2f;
- border-top: 1px solid #252a2f;
- }
- .rk-footer-edit {
- color: #eee;
- background: #448dfe;
- border-top: 1px solid #448dfe;
- }
- .rk-footer-utc {
- color: inherit;
- background: 0;
- border: 0;
- outline: none;
- width: 40px;
- padding-bottom: 0;
- }
- .rk-footer-inner {
- justify-content: space-between;
- display: flex;
- }
- .utc-min {
- display: inline-block;
- padding-top: 2px;
- }
-</style>
diff --git a/src/views/components/common/rk-header.vue b/src/views/components/common/rk-header.vue
index 1b784fe..476bb19 100644
--- a/src/views/components/common/rk-header.vue
+++ b/src/views/components/common/rk-header.vue
@@ -30,85 +30,29 @@
<span class="vm hide-xs ml-5">{{ $t(menu.meta.title) }}</span>
</router-link>
</div>
- <div class="flex-h">
- <a
- class="rk-btn mr-5 sm"
- :class="auto ? 'blue' : 'ghost'"
- @click="handleAuto"
- v-tooltip:bottom="{ content: $t('timeReload') }"
- >
- <span class="vm">{{ $t('auto') }}</span>
- </a>
- <div class="auto-time">
- <span class="rk-auto-select">
- <input v-model="autoTime" type="number" @change="changeAutoTime" min="1" />
- </span>
- {{ $t('second') }}
- </div>
- <a class="rk-btn sm ghost" @click="handleReload">
- <rk-icon icon="retry" :loading="auto" />
- <span class="vm">{{ $t('reload') }}</span>
- </a>
- </div>
+ <PageToolBar />
</header>
</template>
<script lang="ts">
import { Vue, Component } from 'vue-property-decorator';
- import { Action, Getter } from 'vuex-class';
import { routes } from '@/router';
- import timeFormat from '@/utils/timeFormat';
+ import PageToolBar from './page-tool-bar.vue';
- @Component
+ @Component({
+ components: {
+ PageToolBar,
+ },
+ })
export default class Header extends Vue {
- @Getter('duration') private duration: any;
- @Action('SET_DURATION') private SET_DURATION: any;
- private show: boolean = false;
- private auto: boolean = false;
- private autoTime: number = 6;
- private timer: any = null;
-
private get menus() {
return routes[0].children;
}
- private handleReload() {
- const gap = this.duration.end.getTime() - this.duration.start.getTime();
- const time: Date[] = [new Date(new Date().getTime() - gap), new Date()];
- this.SET_DURATION(timeFormat(time));
- }
- private handleAuto() {
- if (this.autoTime < 1) {
- return;
- }
- this.auto = !this.auto;
- if (this.auto) {
- this.handleReload();
- this.timer = setInterval(this.handleReload, this.autoTime * 1000);
- } else {
- clearInterval(this.timer);
- }
- }
- private handleHide() {
- this.show = false;
- }
- private handleShow() {
- this.show = !this.show;
- }
private handleSignout() {
localStorage.removeItem('skywalking-authority');
this.$router.push('/login');
}
- private changeAutoTime() {
- if (this.autoTime < 1) {
- return;
- }
- clearInterval(this.timer);
- if (this.auto) {
- this.handleReload();
- this.timer = setInterval(this.handleReload, this.autoTime * 1000);
- }
- }
}
</script>
@@ -177,16 +121,4 @@
background-color: #dededf;
}
}
- .rk-auto-select {
- border-radius: 3px;
- background-color: #fff;
- padding: 1px;
- border-radius: 3px;
-
- input {
- width: 38px;
- border-style: unset;
- outline: 0;
- }
- }
</style>
diff --git a/src/views/containers/index.vue b/src/views/containers/index.vue
index 8d6fe9c..593e630 100644
--- a/src/views/containers/index.vue
+++ b/src/views/containers/index.vue
@@ -14,34 +14,22 @@
limitations under the License. -->
<template>
<div id="app">
- <RkHeader @reloadFooter="reloadFooter" />
+ <RkHeader />
<router-view></router-view>
- <RkFooter ref="footer" />
<AlertsContent />
</div>
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
- import { State } from 'vuex-class';
- import { State as optionState } from '@/store/modules/global/selectors';
import RkHeader from '@/views/components/common/rk-header.vue';
- import RkFooter from '@/views/components/common/rk-footer.vue';
import AlertsContent from '@/views/components/common/alerts-content.vue';
@Component({
components: {
RkHeader,
- RkFooter,
AlertsContent,
},
})
- export default class RouterIndex extends Vue {
- @State('rocketOption') private stateDashboardOption!: optionState;
- private isRouterAlive: boolean = true;
- public reloadFooter(timeArray: Date[]): void {
- const footer: any = this.$refs.footer;
- footer.time = timeArray;
- }
- }
+ export default class RouterIndex extends Vue {}
</script>