mirror of
https://github.com/veops/cmdb.git
synced 2025-08-12 01:04:39 +08:00
前后端全面升级
This commit is contained in:
@@ -4,20 +4,14 @@
|
||||
<router-link
|
||||
v-if="item.name != name && index != 1"
|
||||
:to="{ path: item.path === '' ? '/' : item.path }"
|
||||
>{{ i18nRender(item.meta.title) }}</router-link>
|
||||
<span v-else>{{ i18nRender(item.meta.title) }}</span>
|
||||
>{{ item.meta.title }}</router-link>
|
||||
<span v-else>{{ item.meta.title }}</span>
|
||||
</a-breadcrumb-item>
|
||||
</a-breadcrumb>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
i18nRender: {
|
||||
type: Function,
|
||||
default: (r) => `${r}`
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
name: '',
|
||||
|
38
cmdb-ui/src/components/tools/DocumentLink.vue
Normal file
38
cmdb-ui/src/components/tools/DocumentLink.vue
Normal file
@@ -0,0 +1,38 @@
|
||||
<template>
|
||||
<a-tooltip>
|
||||
<template slot="title">文档中心</template>
|
||||
<span class="document-link">
|
||||
<a-icon type="question-circle" @click="handleClick" />
|
||||
</span>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'DocumentLink',
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
methods: {
|
||||
handleClick() {
|
||||
window.open(`${location.origin}/docs`, '_blank')
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
@import '../../style/static.less';
|
||||
|
||||
.document-link {
|
||||
cursor: pointer;
|
||||
color: @layout-header-font-color;
|
||||
width: 40px;
|
||||
height: @layout-header-icon-height;
|
||||
line-height: @layout-header-icon-height;
|
||||
text-align: center;
|
||||
border-radius: 4px;
|
||||
&:hover {
|
||||
background: linear-gradient(0deg, rgba(0, 80, 201, 0.2) 0%, rgba(174, 207, 255, 0.06) 86.76%);
|
||||
color: @layout-header-font-selected-color;
|
||||
}
|
||||
}
|
||||
</style>
|
@@ -1,38 +0,0 @@
|
||||
<template>
|
||||
<a-dropdown>
|
||||
<span class="action global-lang">
|
||||
<a-icon type="global" style="font-size: 16px"/>
|
||||
</span>
|
||||
<a-menu slot="overlay" style="width: 150px;" @click="SwitchLang">
|
||||
<a-menu-item key="zh-CN">
|
||||
<a rel="noopener noreferrer">
|
||||
<span role="img" aria-label="简体中文">🇨🇳</span> 简体中文
|
||||
</a>
|
||||
</a-menu-item>
|
||||
<a-menu-item key="en-US">
|
||||
<a rel="noopener noreferrer">
|
||||
<span role="img" aria-label="English">🇬🇧</span> English
|
||||
</a>
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</a-dropdown>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mixin as langMixin } from '@/store/i18n-mixin'
|
||||
|
||||
export default {
|
||||
name: 'LangSelect',
|
||||
mixins: [langMixin],
|
||||
data () {
|
||||
return {}
|
||||
},
|
||||
inject: ['reload'],
|
||||
methods: {
|
||||
SwitchLang (row) {
|
||||
this.setLang(row.key)
|
||||
this.reload()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
@@ -1,35 +1,60 @@
|
||||
<template>
|
||||
<div class="logo">
|
||||
<router-link :to="{name:'cmdb'}">
|
||||
<div style="margin-right: 24px">
|
||||
<div style="display: inline-block;">
|
||||
<img src="@/assets/cmdb.png" width="50px" height="50px" style="margin:0; padding:0" />
|
||||
</div>
|
||||
<h1 v-if="showTitle">{{ title }}</h1>
|
||||
</div>
|
||||
</router-link>
|
||||
<img
|
||||
@click="jumpTo"
|
||||
v-if="showTitle && !collapsed"
|
||||
style="width: 100%; height: 100%; cursor: pointer"
|
||||
:src="file_name ? `/api/common-setting/v1/file/${file_name}` : require('@/assets/logo_VECMDB.png')"
|
||||
/>
|
||||
<img
|
||||
@click="jumpTo"
|
||||
v-else
|
||||
style="width: 32px; height: 32px; margin-left: 24px; cursor: pointer"
|
||||
:src="small_file_name ? `/api/common-setting/v1/file/${small_file_name}` : require('@/assets/logo.png')"
|
||||
/>
|
||||
<!-- <logo-svg/> -->
|
||||
<!-- <img v-if="showTitle" style="width:92px;height: 32px" src="@/assets/OneOps.png" /> -->
|
||||
<!-- <h1 v-if="showTitle">{{ title }}</h1> -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import LogoSvg from '@/assets/logo.svg?inline'
|
||||
|
||||
// import LogoSvg from '@/assets/logo.svg?inline'
|
||||
import { mapState } from 'vuex'
|
||||
export default {
|
||||
name: 'Logo',
|
||||
components: {
|
||||
LogoSvg
|
||||
// LogoSvg,
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
file_name: (state) => state.logo.file_name,
|
||||
small_file_name: (state) => state.logo.small_file_name,
|
||||
}),
|
||||
},
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
default: 'CMDB',
|
||||
required: false
|
||||
default: 'OneOps',
|
||||
required: false,
|
||||
},
|
||||
showTitle: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
required: false
|
||||
}
|
||||
}
|
||||
required: false,
|
||||
},
|
||||
collapsed: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
jumpTo() {
|
||||
if (this.$route.path !== '/cmdb/dashboard') {
|
||||
this.$router.push('/cmdb/dashboard')
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
@@ -1,34 +1,178 @@
|
||||
<template>
|
||||
<div class="top-menu" v-if="routes.length > 2">
|
||||
<a-menu v-model="current" mode="horizontal">
|
||||
<!-- <a-menu v-model="current" mode="horizontal">
|
||||
<a-menu-item :key="route.name" v-for="route in routes.slice(0, routes.length - 1)">
|
||||
<router-link :to="{name: route.name}">{{ route.meta.title }}</router-link>
|
||||
<router-link :to="{ name: route.name }">{{ route.meta.title }}</router-link>
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</a-menu>-->
|
||||
<span
|
||||
:class="current === route.name ? 'top-menu-selected' : ''"
|
||||
v-for="route in defaultShowRoutes"
|
||||
:key="route.name"
|
||||
@click="() => handleClick(route)"
|
||||
>
|
||||
{{ route.meta.title }}
|
||||
</span>
|
||||
<!-- <a-popover v-model="visible" placement="bottom" trigger="click" overlayClassName="top-menu-dropdown">
|
||||
<template slot="content">
|
||||
<div class="title">
|
||||
更多应用
|
||||
</div>
|
||||
<a-divider style="margin:10px 0;" />
|
||||
<div
|
||||
@click="
|
||||
() => {
|
||||
handleClick(route)
|
||||
}
|
||||
"
|
||||
:class="`more ${current == route.name ? 'more-selected' : ''}`"
|
||||
v-for="route in defaultUnShowRoutes"
|
||||
:key="route.name"
|
||||
>
|
||||
<div class="more-icon-block">
|
||||
<components :is="`top_${route.name}`" style="width:40px;height:40px;" />
|
||||
</div>
|
||||
{{ route.meta.title }}
|
||||
</div>
|
||||
</template>
|
||||
<span class="top-menu-icon"><gridSvg /></span>
|
||||
</a-popover> -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import store from '@/store'
|
||||
|
||||
import { gridSvg, top_agent, top_acl } from '@/core/icons'
|
||||
export default {
|
||||
data () {
|
||||
name: 'TopMenu',
|
||||
components: { gridSvg, top_agent, top_acl },
|
||||
data() {
|
||||
return {
|
||||
routes: store.getters.addRouters,
|
||||
current: [store.getters.addRouters[0].name]
|
||||
defaultShowRouteName: ['cmdb'],
|
||||
defaultUnShowRouteName: ['acl'],
|
||||
routes: store.getters.appRoutes.filter((i) => !(i.meta || {}).hiddenInTopMenu),
|
||||
current: store.getters.appRoutes[0].name,
|
||||
visible: false,
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
defaultShowRoutes() {
|
||||
return this.routes.filter((item) => this.defaultShowRouteName.includes(item.name))
|
||||
},
|
||||
defaultUnShowRoutes() {
|
||||
return this.routes.filter((item) => this.defaultUnShowRouteName.includes(item.name))
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
$route: {
|
||||
immediate: true,
|
||||
deep: true,
|
||||
handler(newVal) {
|
||||
if (newVal) {
|
||||
this.current = newVal.matched[0].name
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.current = this.$route.matched[0].name
|
||||
},
|
||||
methods: {
|
||||
handleClick(route) {
|
||||
this.visible = false
|
||||
if (route.name !== this.current) {
|
||||
this.$router.push(route.redirect)
|
||||
// this.current = route.name
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
@import '../../style/static.less';
|
||||
// .top-menu {
|
||||
// display: inline-block;
|
||||
// }
|
||||
// .ant-menu-horizontal {
|
||||
// border-bottom: 0 !important;
|
||||
// }
|
||||
// .ant-menu-horizontal > .ant-menu-item {
|
||||
// border-bottom: 0;
|
||||
// }
|
||||
|
||||
.top-menu {
|
||||
display: inline-block;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
> .top-menu-icon {
|
||||
width: 40px;
|
||||
height: @layout-header-icon-height;
|
||||
line-height: @layout-header-icon-height;
|
||||
border-radius: 4px !important;
|
||||
display: inline-flex;
|
||||
align-items: flex-end;
|
||||
}
|
||||
> span {
|
||||
cursor: pointer;
|
||||
padding: 4px 10px;
|
||||
margin: 0 5px;
|
||||
border-radius: 4px;
|
||||
color: @layout-header-font-color;
|
||||
height: @layout-header-height;
|
||||
line-height: @layout-header-height;
|
||||
&:hover {
|
||||
background: linear-gradient(0deg, rgba(0, 80, 201, 0.2) 0%, rgba(174, 207, 255, 0.06) 86.76%);
|
||||
color: @layout-header-font-selected-color;
|
||||
border-radius: 3px 3px 0px 0px;
|
||||
}
|
||||
}
|
||||
.top-menu-selected {
|
||||
background: linear-gradient(0deg, rgba(0, 80, 201, 0.2) 0%, rgba(174, 207, 255, 0.06) 86.76%);
|
||||
color: @layout-header-font-selected-color;
|
||||
border-radius: 3px 3px 0px 0px;
|
||||
border-bottom: 3px solid @layout-header-font-selected-color;
|
||||
&:hover {
|
||||
background: linear-gradient(0deg, rgba(0, 80, 201, 0.2) 0%, rgba(174, 207, 255, 0.06) 86.76%);
|
||||
color: @layout-header-font-selected-color;
|
||||
border-radius: 3px 3px 0px 0px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.ant-menu-horizontal {
|
||||
border-bottom: 0 !important;
|
||||
|
||||
.top-menu-dropdown.ant-popover-placement-bottom .ant-popover-content {
|
||||
margin-top: -8px;
|
||||
}
|
||||
.ant-menu-horizontal > .ant-menu-item {
|
||||
border-bottom: 0;
|
||||
|
||||
.top-menu-dropdown {
|
||||
min-width: 500px;
|
||||
.ant-popover-arrow {
|
||||
display: none;
|
||||
}
|
||||
.title {
|
||||
font-weight: 700;
|
||||
}
|
||||
.more {
|
||||
display: inline-block;
|
||||
padding: 8px 16px;
|
||||
margin: 0px 30px 0 10px;
|
||||
border-radius: 4px;
|
||||
background: linear-gradient(0deg, #eeeeee 55%, white);
|
||||
color: @layout-header-font-color;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
.more-icon-block {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
}
|
||||
&:hover {
|
||||
background: linear-gradient(0deg, rgba(0, 80, 201, 0.2) 0%, rgba(174, 207, 255, 0.06) 86.76%);
|
||||
color: @layout-header-font-selected-color;
|
||||
}
|
||||
}
|
||||
.more-selected {
|
||||
background-color: #001428;
|
||||
color: @layout-header-font-color;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@@ -1,81 +1,103 @@
|
||||
<template>
|
||||
<div class="user-wrapper">
|
||||
<div class="content-box">
|
||||
<a href="https://github.com/pycook/cmdb" target="_blank">
|
||||
<span class="action">
|
||||
{{ $t('tip.sourceCode') }} -> <a-icon type="github" style="font-size: 20px; color: #002140"></a-icon>
|
||||
</span>
|
||||
</a>
|
||||
<!-- <a href="https://pro.loacg.com/docs/getting-started" target="_blank">
|
||||
<span class="action">
|
||||
<a-icon type="question-circle-o"></a-icon>
|
||||
</span>
|
||||
</a>
|
||||
<notice-icon class="action"/> -->
|
||||
<a-dropdown>
|
||||
<!-- <document-link /> -->
|
||||
<span
|
||||
v-if="hasBackendPermission"
|
||||
@click="handleClick"
|
||||
class="action"
|
||||
style="width: 40px; display: flex; justify-content: center"
|
||||
>
|
||||
<a-icon type="setting" />
|
||||
</span>
|
||||
<a-popover
|
||||
trigger="click"
|
||||
:overlayStyle="{ width: '120px' }"
|
||||
placement="bottomRight"
|
||||
overlayClassName="custom-user"
|
||||
>
|
||||
<template slot="content">
|
||||
<router-link :to="{ name: 'setting_person' }" :style="{ color: '#000000a6' }">
|
||||
<div class="custom-user-item">
|
||||
<a-icon type="user" :style="{ marginRight: '10px' }" />
|
||||
<span>个人中心</span>
|
||||
</div>
|
||||
</router-link>
|
||||
<div @click="handleLogout" class="custom-user-item">
|
||||
<a-icon type="logout" :style="{ marginRight: '10px' }" />
|
||||
<span>退出登录</span>
|
||||
</div>
|
||||
</template>
|
||||
<span class="action ant-dropdown-link user-dropdown-menu">
|
||||
<a-avatar class="avatar" size="small" :src="avatar()"/>
|
||||
<a-avatar
|
||||
v-if="avatar()"
|
||||
class="avatar"
|
||||
size="small"
|
||||
:src="avatar().startsWith('https') ? avatar() : `/api/common-setting/v1/file/${avatar()}`"
|
||||
/>
|
||||
<a-avatar v-else class="avatar" size="small" icon="user" />
|
||||
<span>{{ nickname() }}</span>
|
||||
</span>
|
||||
<a-menu slot="overlay" class="user-dropdown-menu-wrapper">
|
||||
<!-- <a-menu-item key="0">
|
||||
<router-link :to="{ name: 'center' }">
|
||||
<a-icon type="user"/>
|
||||
<span>个人中心</span>
|
||||
</router-link>
|
||||
</a-menu-item>
|
||||
<a-menu-item key="1">
|
||||
<router-link :to="{ name: 'settings' }">
|
||||
<a-icon type="setting"/>
|
||||
<span>账户设置</span>
|
||||
</router-link>
|
||||
</a-menu-item>
|
||||
<a-menu-divider/> -->
|
||||
<a-menu-item key="3">
|
||||
<a href="javascript:;" @click="handleLogout">
|
||||
<a-icon type="logout"/>
|
||||
<span>{{ $t('login.logout') }}</span>
|
||||
</a>
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</a-dropdown>
|
||||
<lang-select v-if="showLocale" />
|
||||
</a-popover>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import NoticeIcon from '@/components/NoticeIcon'
|
||||
import { mapActions, mapGetters } from 'vuex'
|
||||
import LangSelect from '@/components/tools/LangSelect'
|
||||
import config from '@/config/defaultSettings'
|
||||
import DocumentLink from './DocumentLink.vue'
|
||||
import { mapState, mapActions, mapGetters } from 'vuex'
|
||||
|
||||
export default {
|
||||
name: 'UserMenu',
|
||||
components: {
|
||||
NoticeIcon, LangSelect
|
||||
DocumentLink,
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
showLocale: config.showLocale
|
||||
}
|
||||
computed: {
|
||||
...mapState(['user']),
|
||||
hasBackendPermission() {
|
||||
return this.user?.roles?.permissions.includes('acl_admin', 'backend_admin') || false
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['Logout']),
|
||||
...mapGetters(['nickname', 'avatar']),
|
||||
handleLogout () {
|
||||
handleLogout() {
|
||||
const that = this
|
||||
|
||||
this.$confirm({
|
||||
title: this.$t('tip.warning'),
|
||||
content: this.$t('login.confirmLogout'),
|
||||
onOk () {
|
||||
title: '提示',
|
||||
content: '真的要注销登录吗 ?',
|
||||
onOk() {
|
||||
// localStorage.removeItem('ops_cityps_currentId')
|
||||
localStorage.clear()
|
||||
return that.Logout()
|
||||
},
|
||||
onCancel () {
|
||||
}
|
||||
onCancel() {},
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
handleClick() {
|
||||
this.$router.push('/setting')
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style lang="less">
|
||||
@import '~@/style/static.less';
|
||||
.color {
|
||||
color: #custom_colors[color_1];
|
||||
background-color: #custom_colors[color_2];
|
||||
}
|
||||
.custom-user {
|
||||
.custom-user-item {
|
||||
cursor: pointer;
|
||||
padding: 5px 10px;
|
||||
&:hover {
|
||||
.color() !important;
|
||||
}
|
||||
}
|
||||
.ant-popover-inner-content {
|
||||
padding: 0;
|
||||
color: #000000a6;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
Reference in New Issue
Block a user