spring boot admin ui 二次开发(二)
上次文章已经说明了如何启动进行spring boot ui的入门.本篇文章叙述如何整合element ui 以及添加一个菜单.
第一步:添加element ui
package.json中的devDependencies中添加
"element-ui": "^2.13.0"
控制台执行
cnpm install
在index.js中添加引入element ui的代码
/**添加代码 */
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
Vue.use(ElementUI, {
size: 'medium' // set element-ui default size
})
修改scss代码,避免样式冲突.
base.scss中修改
body {
flex-grow: 1;
display: flex;
flex-direction: column;
& > div[id=app] {
flex-grow: 1;
display: flex;
flex-direction: column;
}
}
至此 element ui引入完成.
二.菜单页面
在views文件夹下添加如下文件
源码如下: sidebar.vue
<template>
<aside class="sidebar">
<div v-sticks-below="['#navigation']">
<router-link
:to="{name: 'home/console'}"
class="instance-summary--UP"
>
<div
class="instance-summary__name"
>
xxx平台
</div>
<div
class="instance-summary__id"
/>
</router-link>
<ul>
<li
v-for="group in enabledGroupedViews"
:key="group.name"
class="sidebar-group"
:class="{'is-active' : isActiveGroup(group)}"
@mouseenter="hasMultipleViews(group) && !isActiveGroup(group) && showFlyout($event)"
@mouseleave="hasMultipleViews(group) && !isActiveGroup(group) && hideFlyout($event)"
>
<router-link
:to="{ name: group.views[0].name, params: { } }"
v-text="hasMultipleViews(group) ? group.name : group.views[0].label"
active-class=""
exact-active-class=""
:class="{'is-active' : isActiveGroup(group) }"
/>
<ul
v-if="hasMultipleViews(group)"
class="sidebar-group-items"
>
<li
v-for="view in group.views"
:key="view.name"
>
<router-link :to="{ name: view.name, params: { } }">
<component :is="view.handle" />
</router-link>
</li>
</ul>
</li>
</ul>
</div>
</aside>
</template>
<script>
import sticksBelow from '@/directives/sticks-below';
import {compareBy} from '@/utils/collections';
export default {
props: {
views: {
type: Array,
default: () => []
},
},
directives: {sticksBelow},
data: () => ({
isStuck: false
}),
computed: {
enabledViews() {
return [...this.views].filter(
view => typeof view.isEnabled === 'undefined' || view.isEnabled()
).sort(compareBy(v => v.order));
},
enabledGroupedViews() {
const groups = new Map();
this.enabledViews.forEach(view => {
const groupName = view.group || view.label;
const group = groups.get(groupName) || {
name: groupName,
order: Number.MAX_SAFE_INTEGER,
views: []
};
groups.set(groupName, {
...group,
order: Math.min(group.order, view.order),
views: [...group.views, view]
})
}
);
return Array.from(groups.values());
}
},
methods: {
isActiveGroup(group) {
return group.views.includes(this.$route.meta.view);
},
hasMultipleViews(group) {
return group.views.length > 1;
},
onScroll() {
this.isStuck = this.$el.getBoundingClientRect().top <= 52;
},
showFlyout(event) {
const groupEl = event.target;
groupEl.classList.add('is-showing-flyout');
const boundingRect = groupEl.getBoundingClientRect();
const itemsEl = event.target.querySelector('.sidebar-group-items');
itemsEl.style.top = `${boundingRect.top}px`;
itemsEl.style.left = `${boundingRect.right + 1}px`;
},
hideFlyout(event) {
const groupEl = event.target;
groupEl.classList.remove('is-showing-flyout');
const itemsEl = event.target.querySelector('.sidebar-group-items');
itemsEl.style = undefined;
}
},
mounted() {
window.addEventListener('scroll', this.onScroll);
},
beforeDestroy() {
window.removeEventListener('scroll', this.onScroll);
},
}
</script>
<style lang="scss" scoped>
@import "~@/assets/css/utilities";
.sidebar {
height: 100%;
width: 175px;
background-color: $white-bis;
border-right: 1px solid $grey-lighter;
.instance-summary {
padding: 1rem 0.5rem;
color: $text;
background-color: $grey;
text-align: center;
&:hover {
background-color: rgba($grey, 0.90);
}
&__name {
font-weight: $weight-semibold;
font-size: $size-5;
text-align: center;
}
&__id {
font-size: $size-6;
}
&--UP {
color: $primary-invert;
background-color: $primary;
&:hover {
background-color: rgba($primary, 0.90);
}
}
&--RESTRICTED {
color: $warning-invert;
background-color: $warning;
&:hover {
background-color: rgba($warning, 0.90);
}
}
&--OUT_OF_SERVICE,
&--DOWN {
color: $danger-invert;
background-color: $danger;
&:hover {
background-color: rgba($danger, 0.90);
}
}
}
a {
border-radius: $radius-small;
color: $text;
display: block;
padding: 0.5em 0.75em;
&:hover {
background-color: rgba(0, 0, 0, 0.04);
}
}
.sidebar-group {
.sidebar-group-items {
display: none;
}
&.is-active {
box-shadow: inset 4px 0 0 $primary;
background-color: rgba(0, 0, 0, 0.04);
> a {
color: $text;
font-weight: $weight-semibold;
}
.sidebar-group-items {
display: block;
padding-bottom: 0.25em;
a {
padding-left: 2em;
&.is-active {
background-color: $primary;
color: $link-invert;
}
}
}
}
&.is-showing-flyout {
.sidebar-group-items {
position: fixed;
display: block;
z-index: 999;
background-color: $white;
min-width: 150px;
box-shadow: 0 2px 3px rgba($black, 0.1), 0 0 0 1px rgba($black, 0.1);
}
}
}
}
</style>
index.vue
<template>
<div class="instances">
<div class="instances__body">
<div class="instances__sidebar">
<home-sidebar
:views="views"
/>
</div>
<div class="instances__view">
<router-view />
</div>
</div>
</div>
</template>
<script>
import HomeSidebar from './sidebar';
export default {
components: {HomeSidebar},
props: {
views: {
type: Array,
default: () => []
},
error: {
type: Error,
default: null
}
},
computed: {
},
install({viewRegistry}) {
viewRegistry.addView({
name: 'home',
path: '/home',
component: this,
label: '监控',
order: -1000,
redirectView: 'home/console'
});
}
}
</script>
<style lang="scss">
.instances {
display: flex;
flex-grow: 1;
flex-direction: column;
&__body {
display: flex;
flex-grow: 1;
}
&__view,
&__sidebar {
position: relative;
}
&__sidebar {
z-index: 20;
}
&__view {
flex-grow: 1;
flex-shrink: 1;
z-index: 10;
max-width: calc(100vw - 175px); /*sidebar-width*/
}
}
</style>
console/index.vue
<template>
<section class="section">
<!-- <div-->
<!-- class="container"-->
<!-- >-->
<div v-if="error" class="message is-danger">
<div class="message-body">
<strong>
<font-awesome-icon
class="has-text-danger"
icon="exclamation-triangle"
/>
Fetching caches failed.
</strong>
<p v-text="error.message" />
</div>
</div>
<el-card>
<el-container>
<el-header height="30px">
今日统计数据
</el-header>
<el-main>
<el-row :gutter="12">
<el-col :span="4">
<el-card shadow="always" class="card">
<div class="center">
总调用次数
</div>
<div>
<span>{{
systemData.count == null ? 0 : systemData.count
}}</span>
次
</div>
</el-card>
</el-col>
<el-col :span="4">
<el-card shadow="always" class="card">
<div class="center">
调用平均耗时
</div>
<div>
<span> {{ systemData.costAvg }}</span> ms
</div>
</el-card>
</el-col>
</el-row>
</el-main>
</el-container>
</el-card>
</section>
</template>
<script>
import subscribing from '@/mixins/subscribing';
import {timer} from '@/utils/rxjs';
export default {
components: { },
props: {},
mixins: [subscribing],
data: () => ({
isLoading: false,
error: null,
caches: [],
filter: '',
systemData: {
count: 111,
costAvg: 111
},
oneDayData: {},
}),
computed: {},
methods: {
/** 定時刷新*/
createSubscription() {
const vm = this;
return timer(0, 60000).subscribe({
next: (data) => {
},
error: (error) => {
console.warn('Fetching console data failed:', error);
},
});
},
},
created() {
},
install({ viewRegistry }) {
//用于路由注册 这是spring boot ui的路由注册方式
viewRegistry.addView({
name: 'home/console',
parent: 'home',
path: 'console',
group: '接口监控',
component: this,
label: '监控',
order: 970,
});
},
};
</script>
<style lang="scss" scoped>
.tool {
margin-bottom: 20px;
}
.form-class {
height: 800px;
margin-bottom: 20px;
}
.input-class {
.el-input__inner {
width: 115%;
}
}
.center {
text-align: center;
}
.card {
background-color: #f2f2f2;
color: #888;
div {
text-align: center;
}
span {
text-align: center;
font-size: 35px;
}
}
.el-one-day-col {
margin-top: 20px;
background-color: #fff;
div {
-webkit-line-clamp: 1;
overflow: hidden;
display: -webkit-box;
-webkit-box-orient: vertical;
white-space: normal;
cursor: pointer;
}
.card {
margin-top: 10px;
}
}
</style>
AdminServerUiAutoConfiguration.java 文件中添加 "/home/**"
修改文件记录
执行启动 SpringBootAdminServletApplication.java
npm run watch
效果展示如下: