# 定制主题
自 1.8.5 版本之后,我们对样式进行了重构,更加灵活和精致,主题定制的方式也发生了变化。可以通过低代码开发IDE,直接配置主题,无需手动修改样式变量。以下是主题定制的详细步骤。
# 调整依赖包
在 ui/package.json 中增加依赖包:
"@fly-vue/style": "1.8.5-2" // 根据实际情况调整版本号,参考版本更新日志
同时,修改 ui/src/styles/default/index.less 文件,调整引入主题路径:
@import '~@fly-vue/style/style/index.less';
# 调整配置文件
修改 ui/src/setting.js 文件,增加配置:
const Setting = {
...
menuSideCollapseWidth: 60, // 侧边栏折叠宽度
...
}
# 调整布局文件
修改 ui/src/layouts/basic-layout/index.vue 文件:
index.vue
<template>
<Layout class="i-layout">
<Sider
v-show="!isMobile && !hideSider && !disabledSiderMenu"
class="i-layout-sider"
:class="siderClasses"
:width="menuSideWidth"
>
<i-menu-side :hide-logo="isHeaderStick && headerFix && showHeader" >
<template slot="pre">
<h4 v-if="!menuCollapse && currentHeader" class="i-layout-sider-title text-elip">
{{ currentHeader.title }}
</h4>
</template>
<template slot="suffix">
<i-header-collapse
v-if="!isMobile && showSiderCollapse && !hideSider && !disabledSiderMenu"
class="i-layout-sider-trigger"
:class="menuCollapse ? 'i-layout-sider-trigger-collapse' : ''"
@on-toggle-drawer="handleToggleDrawer"
/>
</template>
</i-menu-side>
</Sider>
<Layout class="i-layout-inside" :class="insideClasses">
<transition name="fade-quick">
<Header
v-show="showHeader"
v-resize="handleHeaderWidthChange"
class="i-layout-header"
:class="headerClasses"
:style="headerStyle"
>
<i-header-logo v-if="isMobile && showMobileLogo" />
<i-header-logo v-if="!isMobile && isHeaderStick && headerFix" :logo="logoInfo" />
<i-menu-head v-if="headerMenu && !isMobile" ref="menuHead" />
<i-header-search v-if="showSearch && !headerMenu && !isMobile && !showBreadcrumb" />
<div class="i-layout-header-right">
<i-header-mode v-if="!isMobile && showModeSwitch" />
<i-header-ide v-if="!isMobile && showIDEOnline" />
<i-header-search v-if="(showSearch && isMobile) || (showSearch && (headerMenu || showBreadcrumb))" />
<i-header-reload v-if="!isMobile && showReload" class="mr-4" @on-reload="handleReload" />
<i-menu-head v-if="headerMenu && isMobile" />
<i-header-log v-if="isDesktop && showLog" />
<i-header-fullscreen v-if="isDesktop && showFullscreen" />
<i-header-notice v-if="showNotice" />
<!-- 主题配置 -->
<iHeaderTheme v-if="!isMobile && showThemeChange" ></iHeaderTheme>
<i-header-user />
<i-header-i18n v-if="showI18n" />
<i-header-setting v-if="enableSetting && !isMobile" />
</div>
</Header>
</transition>
<Content class="i-layout-content" :class="contentClasses">
<transition name="fade-quick">
<i-tabs v-if="tabs" v-show="showHeader" @on-reload="handleReload" />
</transition>
<div class="i-layout-content-main">
<i-header-breadcrumb class="mb-8" v-if="showBreadcrumb && !isMobile" ref="breadcrumb" />
<!-- <bg-keep-alive :include="keepAlive"> -->
<router-view v-if="loadRouter" />
<!-- </bg-keep-alive> -->
</div>
</Content>
<i-copyright v-if="showCopyRight" />
</Layout>
<div v-if="isMobile && !hideSider">
<Drawer v-model="showDrawer" placement="left" :closable="false" :class-name="drawerClasses">
<i-menu-side />
</Drawer>
</div>
</Layout>
</template>
<script>
import { mapState, mapGetters, mapMutations } from 'vuex'
import { config } from '@fly-vue/core'
import iHeaderNotice from './header-notice'
import iHeaderSearch from './header-search'
import iHeaderUser from './header-user'
import iHeaderMode from './header-dev/mode.vue'
import iHeaderIde from './header-dev/ide.vue'
import { requestAnimation } from '@fly-vue/iview-admin'
const Setting = config()
function joinPath(path, base) {
return path.startsWith('http') ? path : base + path
}
export default {
name: 'BasicLayout',
components: {
iHeaderNotice,
iHeaderSearch,
iHeaderUser,
iHeaderMode,
iHeaderIde
},
data() {
let logoInfo = {}
const layout = config().layout || {}
const resourceBaseUrl = Setting.resourceBaseUrl || ''
const normal = layout.logo || ''
const small = layout.logoSmall || ''
const dark = layout.logoDark || ''
if (normal) {
logoInfo.normal = joinPath(normal, resourceBaseUrl)
}
if (small) {
logoInfo.small = joinPath(small, resourceBaseUrl)
}
if (dark) {
logoInfo.dark = joinPath(dark, resourceBaseUrl)
}
if (!Object.keys(logoInfo).length) {
logoInfo = undefined
}
return {
showDrawer: false,
ticking: false,
headerVisible: true,
oldScrollTop: 0,
isDelayHideSider: false, // hack,当从隐藏侧边栏的 header 切换到正常 header 时,防止 Logo 抖动
loadRouter: true,
logoInfo
}
},
computed: {
...mapState('admin/layout', [
'siderTheme',
'headerTheme',
'headerStick',
"showThemeChange",
'tabs',
'tabsFix',
'siderFix',
'headerFix',
'headerHide',
'headerMenu',
'isMobile',
'isTablet',
'isDesktop',
'menuCollapse',
'showMobileLogo',
'showSearch',
'showNotice',
'showFullscreen',
'showSiderCollapse',
'showBreadcrumb',
'showLog',
'showI18n',
'showReload',
'enableSetting',
'showCopyRight',
'showModeSwitch',
'showIDEOnline',
'disabledSiderMenu'
]),
...mapState('admin/page', ['keepAlive']),
...mapGetters('admin/menu', ['hideSider']),
// 如果开启 headerMenu,且当前 header 的 hideSider 为 true,则将顶部按 headerStick 处理
// 这时,即使没有开启 headerStick,仍然按开启处理
isHeaderStick() {
let state = this.headerStick
if (this.hideSider) state = true
return state
},
showHeader() {
//_pst=h 强制隐藏头部
if (this.headerHide) {
return false
}
//滚动隐藏头部
if (this.headerFix && !this.headerVisible) {
return false
}
return true
},
headerClasses() {
return [
`i-layout-header-color-${this.headerTheme}`,
{
'i-layout-header-fix': this.headerFix,
'i-layout-header-fix-collapse': this.headerFix && this.menuCollapse,
'i-layout-header-mobile': this.isMobile,
'i-layout-header-stick': this.isHeaderStick && !this.isMobile,
'i-layout-header-with-menu': this.headerMenu,
'i-layout-header-with-hide-sider': this.hideSider || this.isDelayHideSider
}
]
},
headerStyle() {
const menuWidth = this.isHeaderStick
? 0
: this.menuCollapse
? Setting.menuSideCollapseWidth
: Setting.menuSideWidth
return this.isMobile || !this.headerFix
? {}
: {
width: `calc(100% - ${menuWidth}px)`
}
},
siderClasses() {
return {
'i-layout-sider-fix': this.siderFix,
'i-layout-sider-dark': this.siderTheme === 'dark'
}
},
contentClasses() {
return {
'i-layout-content-fix-with-header': this.headerFix,
'i-layout-content-with-tabs': this.tabs,
'i-layout-content-with-tabs-fix': this.tabs && this.tabsFix
}
},
insideClasses() {
return {
'i-layout-inside-fix-with-sider': this.siderFix && !this.disabledSiderMenu,
'i-layout-inside-fix-with-sider-collapse': this.siderFix && this.menuCollapse,
'i-layout-inside-with-hide-sider': this.hideSider,
'i-layout-inside-mobile': this.isMobile
}
},
drawerClasses() {
let className = 'i-layout-drawer'
if (this.siderTheme === 'dark') className += ' i-layout-drawer-dark'
return className
},
menuSideWidth() {
return this.menuCollapse ? Setting.menuSideCollapseWidth : Setting.menuSideWidth
}
},
watch: {
hideSider() {
this.isDelayHideSider = true
setTimeout(() => {
this.isDelayHideSider = false
}, 0)
},
$route(to, from) {
if (to.name === from.name) {
// 相同路由,不同参数,跳转时,重载页面
if (Setting.sameRouteForceUpdate) {
this.handleReload()
}
}
}
},
methods: {
...mapMutations('admin/layout', ['updateMenuCollapse']),
...mapMutations('admin/page', ['keepAlivePush', 'keepAliveRemove']),
handleToggleDrawer(state) {
if (typeof state === 'boolean') {
this.showDrawer = state
} else {
this.showDrawer = !this.showDrawer
}
},
handleScroll() {
if (!this.headerHide) return
const scrollTop = document.body.scrollTop + document.documentElement.scrollTop
if (!this.ticking) {
this.ticking = true
requestAnimation(() => {
if (this.oldScrollTop > scrollTop) {
this.headerVisible = true
} else if (scrollTop > 300 && this.headerVisible) {
this.headerVisible = false
} else if (scrollTop < 300 && !this.headerVisible) {
this.headerVisible = true
}
this.oldScrollTop = scrollTop
this.ticking = false
})
}
},
handleHeaderWidthChange() {
const $breadcrumb = this.$refs.breadcrumb
if ($breadcrumb) {
$breadcrumb.handleGetWidth()
$breadcrumb.handleCheckWidth()
}
const $menuHead = this.$refs.menuHead
if ($menuHead) {
// todo $menuHead.handleGetMenuHeight();
}
},
handleReload() {
// 针对缓存的页面也生效
const isCurrentPageCache = this.keepAlive.indexOf(this.$route.name) > -1
const pageName = this.$route.name
if (isCurrentPageCache) {
this.keepAliveRemove(pageName)
}
this.loadRouter = false
this.$nextTick(() => {
this.loadRouter = true
if (isCurrentPageCache) {
this.keepAlivePush(pageName)
}
})
}
},
mounted() {
document.addEventListener('scroll', this.handleScroll, { passive: true })
},
beforeDestroy() {
document.removeEventListener('scroll', this.handleScroll)
},
created() {
if (this.isTablet && this.showSiderCollapse) this.updateMenuCollapse(true)
}
}
</script>
# IDE配置主题
打开低代码开发IDE,进入主题配置页面,可以直接调整主题变量,实时预览效果。例如切换成 党政红。

设置为当前主题后,刷新页面即可看到效果。
