前后端全面升级

This commit is contained in:
pycook
2023-07-10 17:42:15 +08:00
parent f57ff80099
commit 98cc853dbc
641 changed files with 97789 additions and 23995 deletions

View File

@@ -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: '',

View 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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>