feat: 自定义主题基本功能
@ -42,6 +42,10 @@ public class ShiroServiceImpl implements ShiroService {
|
||||
filterChainDefinitionMap.put("/index.html", ANON);
|
||||
filterChainDefinitionMap.put("/link.html", ANON);
|
||||
|
||||
//获取主题信息
|
||||
filterChainDefinitionMap.put("/plugin/theme/themes", ANON);
|
||||
filterChainDefinitionMap.put("/plugin/theme/items/**", ANON);
|
||||
|
||||
//验证链接
|
||||
filterChainDefinitionMap.put("/api/link/validate**", ANON);
|
||||
filterChainDefinitionMap.put("/api/map/areaEntitys/**", ANON);
|
||||
|
@ -0,0 +1,50 @@
|
||||
package io.dataease.plugins.server;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestPart;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import io.dataease.plugins.config.SpringContextUtil;
|
||||
import io.dataease.plugins.xpack.theme.dto.ThemeDto;
|
||||
import io.dataease.plugins.xpack.theme.dto.ThemeItem;
|
||||
import io.dataease.plugins.xpack.theme.dto.ThemeRequest;
|
||||
import io.dataease.plugins.xpack.theme.service.ThemeXpackService;
|
||||
|
||||
@RequestMapping("/plugin/theme")
|
||||
@RestController
|
||||
public class ThemeServer {
|
||||
|
||||
|
||||
|
||||
@PostMapping("/themes")
|
||||
public List<ThemeDto> themes(){
|
||||
|
||||
ThemeXpackService themeXpackService = SpringContextUtil.getBean(ThemeXpackService.class);
|
||||
return themeXpackService.themes();
|
||||
}
|
||||
|
||||
@PostMapping("/items/{themeId}")
|
||||
public List<ThemeItem> themeItems(@PathVariable("themeId") int themeId) {
|
||||
ThemeXpackService themeXpackService = SpringContextUtil.getBean(ThemeXpackService.class);
|
||||
return themeXpackService.queryItems(themeId);
|
||||
}
|
||||
|
||||
@PostMapping("/save")
|
||||
public void save(@RequestPart("request") ThemeRequest request, @RequestPart(value = "file", required = false) MultipartFile bodyFile) {
|
||||
ThemeXpackService themeXpackService = SpringContextUtil.getBean(ThemeXpackService.class);
|
||||
themeXpackService.save(request, bodyFile);
|
||||
}
|
||||
|
||||
@PostMapping("/delete/{themeId}")
|
||||
public void save(@PathVariable("themeId") int themeId) {
|
||||
ThemeXpackService themeXpackService = SpringContextUtil.getBean(ThemeXpackService.class);
|
||||
themeXpackService.deleteTheme(themeId);
|
||||
}
|
||||
|
||||
}
|
@ -1,14 +1,16 @@
|
||||
<template>
|
||||
<div id="app">
|
||||
<router-view />
|
||||
<plugin-com v-show="false" ref="de-theme" component-name="ThemeSetting" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import PluginCom from '@/views/system/plugin/PluginCom'
|
||||
|
||||
export default {
|
||||
name: 'App',
|
||||
|
||||
components: { PluginCom },
|
||||
beforeCreate() {
|
||||
|
||||
}
|
||||
|
BIN
frontend/src/assets/fill_radio.png
Normal file
After Width: | Height: | Size: 3.0 KiB |
BIN
frontend/src/assets/theme-custom.png
Normal file
After Width: | Height: | Size: 20 KiB |
BIN
frontend/src/assets/theme-dark.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
frontend/src/assets/theme-default.png
Normal file
After Width: | Height: | Size: 16 KiB |
@ -435,7 +435,7 @@ export default {
|
||||
},
|
||||
settings: {
|
||||
title: '系统布局配置',
|
||||
theme: '主题色',
|
||||
theme: '主题',
|
||||
tagsView: '开启 Tags-View',
|
||||
fixedHeader: '固定 Header',
|
||||
sidebarLogo: '侧边栏 Logo'
|
||||
|
@ -62,7 +62,6 @@
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
<theme-picker v-show="false" ref="de-theme" />
|
||||
</div>
|
||||
|
||||
</template>
|
||||
@ -76,20 +75,22 @@ import Notification from '@/components/Notification'
|
||||
import bus from '@/utils/bus'
|
||||
import LangSelect from '@/components/LangSelect'
|
||||
import { getSysUI } from '@/utils/auth'
|
||||
import ThemePicker from '@/components/ThemePicker'
|
||||
import { pluginLoaded } from '@/api/user'
|
||||
import { initTheme } from '@/utils/ThemeUtil'
|
||||
export default {
|
||||
name: 'Topbar',
|
||||
components: {
|
||||
AppLink,
|
||||
Notification,
|
||||
LangSelect,
|
||||
ThemePicker
|
||||
LangSelect
|
||||
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
uiInfo: null,
|
||||
logoUrl: null,
|
||||
axiosFinished: false
|
||||
axiosFinished: false,
|
||||
isPluginLoaded: false
|
||||
}
|
||||
},
|
||||
|
||||
@ -147,7 +148,6 @@ export default {
|
||||
|
||||
mounted() {
|
||||
this.initCurrentRoutes()
|
||||
bus.$on('set-theme-info', this.setThemeInfo)
|
||||
bus.$on('set-top-menu-info', this.setTopMenuInfo)
|
||||
bus.$on('set-top-menu-active-info', this.setTopMenuActiveInfo)
|
||||
bus.$on('set-top-text-info', this.setTopTextInfo)
|
||||
@ -156,6 +156,12 @@ export default {
|
||||
created() {
|
||||
this.loadUiInfo()
|
||||
},
|
||||
beforeCreate() {
|
||||
pluginLoaded().then(res => {
|
||||
this.isPluginLoaded = res.success && res.data
|
||||
if (this.isPluginLoaded) { initTheme() }
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
// 通过当前路径找到二级菜单对应项,存到store,用来渲染左侧菜单
|
||||
initCurrentRoutes() {
|
||||
@ -259,20 +265,17 @@ export default {
|
||||
})
|
||||
}
|
||||
|
||||
if (this.uiInfo['ui.themeStr'] && this.uiInfo['ui.themeStr'].paramValue) {
|
||||
/* if (this.uiInfo['ui.themeStr'] && this.uiInfo['ui.themeStr'].paramValue) {
|
||||
if (this.uiInfo['ui.themeStr'].paramValue === 'dark') {
|
||||
document.body.className = 'blackTheme'
|
||||
} else if (this.uiInfo['ui.themeStr'].paramValue === 'light') {
|
||||
document.body.className = ''
|
||||
}
|
||||
}
|
||||
} */
|
||||
this.axiosFinished = true
|
||||
})
|
||||
},
|
||||
setThemeInfo(val) {
|
||||
// console.log('切换的主题颜色是:' + val)
|
||||
this.$refs['de-theme'] && this.$refs['de-theme'].switch && this.$refs['de-theme'].switch(val)
|
||||
},
|
||||
|
||||
setTopMenuInfo(val) {
|
||||
this.loadUiInfo()
|
||||
},
|
||||
|
@ -80,7 +80,7 @@ export default {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
// height: $contentHeight;
|
||||
background-color: var(--MainContentBG);
|
||||
background-color: var(--ContentBG);
|
||||
width: 100%;
|
||||
&.mobile.openSidebar{
|
||||
position: fixed;
|
||||
|
@ -22,7 +22,6 @@ router.beforeEach(async(to, from, next) => {
|
||||
|
||||
// determine whether the user has logged in
|
||||
const hasToken = getToken()
|
||||
// bus.$emit('set-theme-info', store.state.settings.theme)
|
||||
if (hasToken) {
|
||||
if (to.path === '/login') {
|
||||
// if is logged in, redirect to the home page
|
||||
|
@ -1,14 +1,11 @@
|
||||
|
||||
$--font-path: '~element-ui/lib/theme-chalk/fonts';
|
||||
/* $--font-path: '~element-ui/lib/theme-chalk/fonts'; */
|
||||
|
||||
/* .defaultTheme {
|
||||
@import './theme/default.scss';
|
||||
@import "~element-ui/packages/theme-chalk/src/index";
|
||||
} */
|
||||
.blackTheme {
|
||||
|
||||
/* .blackTheme {
|
||||
@import './theme/dark.scss';
|
||||
@import "~element-ui/packages/theme-chalk/src/index";
|
||||
}
|
||||
} */
|
||||
|
||||
|
||||
|
||||
@ -460,7 +457,7 @@ div:focus {
|
||||
color: var(--TableColor) !important;
|
||||
}
|
||||
.blackTheme .el-tag.el-tag--info {
|
||||
background-color: var(--MainContentBG);
|
||||
background-color: var(--ContentBG);
|
||||
border-color: var(--TableBorderColor);
|
||||
color: var(--TableColor) !important; ;
|
||||
}
|
||||
@ -575,7 +572,7 @@ div:focus {
|
||||
color: var(--TextPrimary) ;
|
||||
}
|
||||
.blackTheme .el-drawer__body {
|
||||
background-color: var(--MainContentBG);
|
||||
background-color: var(--ContentBG);
|
||||
}
|
||||
|
||||
.blackTheme .el-select-dropdown__item.hover {
|
||||
|
@ -26,15 +26,11 @@ $--background-color-base: #171b22;
|
||||
// 与CSS原生变量映射
|
||||
#app {
|
||||
--Main:#2681ff;
|
||||
--test:#FF9227;
|
||||
--color-primary: #000;
|
||||
--MainBG: #171b22;
|
||||
--ContentBG: #1b2a32;
|
||||
--MainBorderColor: #2681ff;
|
||||
--TextActive: #FFFFFF;
|
||||
--TextPrimary: #F2F6FC;
|
||||
--border-color-input: #303640;
|
||||
--border-color-input-hovor: #303640;
|
||||
|
||||
--TopBG: #00364d;
|
||||
|
||||
@ -47,11 +43,10 @@ $--background-color-base: #171b22;
|
||||
--SiderBG: #17242b;
|
||||
--SiderTextColor: #acbac3;
|
||||
|
||||
--MainContentBG: #1b2a32;
|
||||
|
||||
--TableBG: #21333b;
|
||||
--TableColor: #acbac3;
|
||||
--TableBorder: .05rem solid #495865;
|
||||
// --TableBorder: .05rem solid #495865;
|
||||
--TableBorderColor: #495865;
|
||||
|
||||
--background-color-base: #171b22;
|
||||
|
27
frontend/src/utils/ThemeUtil.js
Normal file
@ -0,0 +1,27 @@
|
||||
const styleId = 'dataease-wggznb'
|
||||
|
||||
export function initTheme() {
|
||||
const json = localStorage.getItem('theme-css-text')
|
||||
if (!json) return
|
||||
const { cssText, themeId } = JSON.parse(json)
|
||||
if (!themeId) return
|
||||
|
||||
let style = document.getElementById(styleId)
|
||||
if (themeId === 1) {
|
||||
document.body.className = ''
|
||||
style && document.getElementById(styleId).parentNode.removeChild(document.getElementById(styleId))
|
||||
return
|
||||
}
|
||||
|
||||
document.body.className = 'blackTheme'
|
||||
|
||||
if (style) {
|
||||
style.innerText = cssText
|
||||
} else {
|
||||
style = document.createElement('style')
|
||||
style.id = styleId
|
||||
style.innerText = cssText
|
||||
document.head.appendChild(style)
|
||||
}
|
||||
}
|
||||
|
@ -436,7 +436,7 @@ export default {
|
||||
}
|
||||
|
||||
.el-header {
|
||||
background-color: var(--MainContentBG, rgb(241, 243, 248));
|
||||
background-color: var(--ContentBG, rgb(241, 243, 248));
|
||||
color: var(--TextActive, #333);
|
||||
line-height: 30px;
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="login-background">
|
||||
<div class="login-background" :v-show="themeLoaded">
|
||||
<div class="login-container">
|
||||
<el-row v-loading="loading" type="flex">
|
||||
<el-col :span="12">
|
||||
@ -57,6 +57,7 @@
|
||||
</el-row>
|
||||
</div>
|
||||
<plugin-com v-if="loginTypes.includes(2) && loginForm.loginType === 2" ref="SSOComponent" component-name="SSOComponent" />
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -65,6 +66,7 @@
|
||||
import { encrypt } from '@/utils/rsaEncrypt'
|
||||
import { ldapStatus, oidcStatus } from '@/api/user'
|
||||
import { getSysUI } from '@/utils/auth'
|
||||
import { initTheme } from '@/utils/ThemeUtil'
|
||||
import PluginCom from '@/views/system/plugin/PluginCom'
|
||||
import Cookies from 'js-cookie'
|
||||
export default {
|
||||
@ -88,7 +90,8 @@ export default {
|
||||
loginImageUrl: null,
|
||||
loginLogoUrl: null,
|
||||
axiosFinished: false,
|
||||
loginTypes: [0]
|
||||
loginTypes: [0],
|
||||
themeLoaded: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@ -105,6 +108,8 @@ export default {
|
||||
}
|
||||
},
|
||||
beforeCreate() {
|
||||
initTheme()
|
||||
this.themeLoaded = true
|
||||
ldapStatus().then(res => {
|
||||
if (res.success && res.data) {
|
||||
this.loginTypes.push(1)
|
||||
@ -117,6 +122,11 @@ export default {
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
mounted() {
|
||||
// this.loading = false
|
||||
},
|
||||
|
||||
created() {
|
||||
this.$store.dispatch('user/getUI').then(() => {
|
||||
// const uiLists = this.$store.state.user.uiInfo
|
||||
@ -133,6 +143,7 @@ export default {
|
||||
}
|
||||
this.clearOidcMsg()
|
||||
},
|
||||
|
||||
methods: {
|
||||
clearOidcMsg() {
|
||||
Cookies.remove('OidcError')
|
||||
@ -146,14 +157,13 @@ export default {
|
||||
if (this.uiInfo['ui.loginLogo'] && this.uiInfo['ui.loginLogo'].paramValue) {
|
||||
this.loginLogoUrl = '/system/ui/image/' + this.uiInfo['ui.loginLogo'].paramValue
|
||||
}
|
||||
if (this.uiInfo['ui.themeStr'] && this.uiInfo['ui.themeStr'].paramValue) {
|
||||
// this.loginLogoUrl = '/system/ui/image/' + this.uiInfo['ui.loginLogo'].paramValue
|
||||
/* if (this.uiInfo['ui.themeStr'] && this.uiInfo['ui.themeStr'].paramValue) {
|
||||
if (this.uiInfo['ui.themeStr'].paramValue === 'dark') {
|
||||
document.body.className = 'blackTheme'
|
||||
} else if (this.uiInfo['ui.themeStr'].paramValue === 'light') {
|
||||
document.body.className = ''
|
||||
}
|
||||
}
|
||||
} */
|
||||
},
|
||||
|
||||
handleLogin() {
|
||||
@ -208,7 +218,7 @@ export default {
|
||||
min-width: 900px;
|
||||
width: 1280px;
|
||||
height: 520px;
|
||||
background-color: var(--MainContentBG, #FFFFFF);
|
||||
background-color: var(--ContentBG, #FFFFFF);
|
||||
@media only screen and (max-width: 1280px) {
|
||||
width: 900px;
|
||||
height: 380px;
|
||||
|
@ -921,7 +921,7 @@ export default {
|
||||
overflow-y: auto;
|
||||
}
|
||||
.button-show{
|
||||
background-color: var(--MainContentBG, #ebf2fe)!important;
|
||||
background-color: var(--ContentBG, #ebf2fe)!important;
|
||||
}
|
||||
|
||||
.button-closed{
|
||||
|
@ -68,7 +68,7 @@
|
||||
</el-row>
|
||||
</el-col>
|
||||
<el-col v-if="panelInfo.name.length===0" style="height: 100%;">
|
||||
<el-row style="height: 100%; background-color: var(--MainContentBG);" class="custom-position">
|
||||
<el-row style="height: 100%; background-color: var(--ContentBG);" class="custom-position">
|
||||
{{ $t('panel.select_panel_from_left') }}
|
||||
</el-row>
|
||||
</el-col>
|
||||
|
@ -6,19 +6,23 @@
|
||||
<basic-setting />
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane :lazy="true" :label="$t('system_parameter_setting.mailbox_service_settings')" name="first">
|
||||
<!-- <el-tab-pane :lazy="true" :label="$t('system_parameter_setting.mailbox_service_settings')" name="first">
|
||||
<email-setting />
|
||||
</el-tab-pane>
|
||||
</el-tab-pane> -->
|
||||
|
||||
<el-tab-pane v-if="isPluginLoaded" :lazy="true" :label="$t('sysParams.display')" name="second">
|
||||
<plugin-com v-if="isPluginLoaded" ref="DisplaySetting" component-name="DisplaySetting" />
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane v-if="isPluginLoaded" :lazy="true" :label="$t('sysParams.ldap')" name="third">
|
||||
<el-tab-pane v-if="isPluginLoaded" :lazy="true" :label="$t('sysParams.theme')" name="third">
|
||||
<plugin-com v-if="isPluginLoaded" ref="ThemeSetting" component-name="ThemeSetting" />
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane v-if="isPluginLoaded" :lazy="true" :label="$t('sysParams.ldap')" name="fourth">
|
||||
<plugin-com v-if="isPluginLoaded" ref="DisplaySetting" component-name="LdapSetting" />
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane v-if="isPluginLoaded" :lazy="true" :label="$t('sysParams.oidc')" name="fourth">
|
||||
<el-tab-pane v-if="isPluginLoaded" :lazy="true" :label="$t('sysParams.oidc')" name="five">
|
||||
<plugin-com v-if="isPluginLoaded" ref="DisplaySetting" component-name="SsoSetting" />
|
||||
</el-tab-pane>
|
||||
|
||||
|
BIN
frontend/static/img/theme-custom.946c21a.png
Normal file
After Width: | Height: | Size: 20 KiB |
BIN
frontend/static/img/theme-dark.974c426.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
frontend/static/img/theme-default.ebe6160.png
Normal file
After Width: | Height: | Size: 16 KiB |