mirror of
https://github.com/1Panel-dev/MaxKB.git
synced 2025-12-26 01:33:05 +00:00
feat: 外观设置
This commit is contained in:
parent
80a54401b0
commit
7a9d8bf96d
|
|
@ -1,7 +1,6 @@
|
|||
import { Result } from '@/request/Result'
|
||||
import { get, post, del, put } from '@/request/index'
|
||||
import type { TeamMember } from '@/api/type/team'
|
||||
|
||||
import type { Ref } from 'vue'
|
||||
const prefix = '/display'
|
||||
|
||||
/**
|
||||
|
|
@ -13,7 +12,7 @@ const getThemeInfo: () => Promise<Result<any>> = () => {
|
|||
|
||||
/**
|
||||
* 更新外观设置
|
||||
* @param 参数
|
||||
* @param 参数
|
||||
* * formData {
|
||||
* theme
|
||||
* icon
|
||||
|
|
@ -23,12 +22,14 @@ const getThemeInfo: () => Promise<Result<any>> = () => {
|
|||
* slogan
|
||||
* }
|
||||
*/
|
||||
const postThemeInfo: (data: any) => Promise<Result<boolean>> = (data) => {
|
||||
return post(`${prefix}/update`, data)
|
||||
const postThemeInfo: (data: any, loading?: Ref<boolean>) => Promise<Result<boolean>> = (
|
||||
data,
|
||||
loading
|
||||
) => {
|
||||
return post(`${prefix}/update`, data, undefined, loading)
|
||||
}
|
||||
|
||||
export default {
|
||||
getThemeInfo,
|
||||
postThemeInfo
|
||||
}
|
||||
|
||||
getThemeInfo,
|
||||
postThemeInfo
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
<svg id="图层_1" data-name="图层 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 232.4409 232.4409"><title>MaxKB</title><path class="cls-1" d="M128.4532,177H98.7785L87.78,187.9985a4.6069,4.6069,0,0,0,3.2576,7.8644h45.1569a4.6069,4.6069,0,0,0,3.2575-7.8644Z"/><path class="cls-1" d="M210.0008,90.7042h-5.85v41.1511h5.85a4.4537,4.4537,0,0,0,4.4537-4.4537V95.1579A4.4537,4.4537,0,0,0,210.0008,90.7042Z"/><path class="cls-1" d="M28.29,90.7042H22.44a4.4538,4.4538,0,0,0-4.4538,4.4537v32.2437a4.4538,4.4538,0,0,0,4.4538,4.4537h5.85Z"/><path class="cls-1" d="M138.8087,96.1512a8.33,8.33,0,0,0-8.33,8.33v5.9727a8.33,8.33,0,1,0,16.6607,0v-5.9727A8.33,8.33,0,0,0,138.8087,96.1512Z"/><path class="cls-1" d="M95.3622,96.1512a8.33,8.33,0,0,0-8.33,8.33v5.9727a8.33,8.33,0,1,0,16.6607,0v-5.9727A8.33,8.33,0,0,0,95.3622,96.1512Z"/><path class="cls-1" d="M166.8344,48.8968H65.6064A33.7544,33.7544,0,0,0,31.89,82.6131v57.07A33.7548,33.7548,0,0,0,65.6064,173.4h101.228a33.7549,33.7549,0,0,0,33.7168-33.7168v-57.07A33.7545,33.7545,0,0,0,166.8344,48.8968Zm2.831,90.4457a6.0733,6.0733,0,0,1-6.0732,6.0733H114.2168a43.5922,43.5922,0,0,0-21.3313,5.5757l-16.5647,9.2946v-14.87h-7.472a6.0733,6.0733,0,0,1-6.0733-6.0733v-60.5a6.0733,6.0733,0,0,1,6.0733-6.0733h94.7434a6.0733,6.0733,0,0,1,6.0732,6.0733Z"/></svg>
|
||||
<svg id="图层_1" data-name="图层 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 232.4409 232.4409"><defs><style>.cls-1{fill:#fff;}</style></defs><title>MaxKB</title><path class="cls-1" d="M128.4532,177H98.7785L87.78,187.9985a4.6069,4.6069,0,0,0,3.2576,7.8644h45.1569a4.6069,4.6069,0,0,0,3.2575-7.8644Z"/><path class="cls-1" d="M210.0008,90.7042h-5.85v41.1511h5.85a4.4537,4.4537,0,0,0,4.4537-4.4537V95.1579A4.4537,4.4537,0,0,0,210.0008,90.7042Z"/><path class="cls-1" d="M28.29,90.7042H22.44a4.4538,4.4538,0,0,0-4.4538,4.4537v32.2437a4.4538,4.4538,0,0,0,4.4538,4.4537h5.85Z"/><path class="cls-1" d="M138.8087,96.1512a8.33,8.33,0,0,0-8.33,8.33v5.9727a8.33,8.33,0,1,0,16.6607,0v-5.9727A8.33,8.33,0,0,0,138.8087,96.1512Z"/><path class="cls-1" d="M95.3622,96.1512a8.33,8.33,0,0,0-8.33,8.33v5.9727a8.33,8.33,0,1,0,16.6607,0v-5.9727A8.33,8.33,0,0,0,95.3622,96.1512Z"/><path class="cls-1" d="M166.8344,48.8968H65.6064A33.7544,33.7544,0,0,0,31.89,82.6131v57.07A33.7548,33.7548,0,0,0,65.6064,173.4h101.228a33.7549,33.7549,0,0,0,33.7168-33.7168v-57.07A33.7545,33.7545,0,0,0,166.8344,48.8968Zm2.831,90.4457a6.0733,6.0733,0,0,1-6.0732,6.0733H114.2168a43.5922,43.5922,0,0,0-21.3313,5.5757l-16.5647,9.2946v-14.87h-7.472a6.0733,6.0733,0,0,1-6.0733-6.0733v-60.5a6.0733,6.0733,0,0,1,6.0733-6.0733h94.7434a6.0733,6.0733,0,0,1,6.0732,6.0733Z"/></svg>
|
||||
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
|
|
@ -955,7 +955,7 @@ export const iconMap: any = {
|
|||
])
|
||||
}
|
||||
},
|
||||
'app-minify': {
|
||||
'app-magnify': {
|
||||
iconReader: () => {
|
||||
return h('i', [
|
||||
h(
|
||||
|
|
@ -976,7 +976,7 @@ export const iconMap: any = {
|
|||
])
|
||||
}
|
||||
},
|
||||
'app-magnify': {
|
||||
'app-minify': {
|
||||
iconReader: () => {
|
||||
return h('i', [
|
||||
h(
|
||||
|
|
@ -1071,5 +1071,5 @@ export const iconMap: any = {
|
|||
)
|
||||
])
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,9 +3,7 @@
|
|||
<div class="login-container w-full h-full">
|
||||
<el-row class="container w-full h-full">
|
||||
<el-col :xs="0" :sm="0" :md="10" :lg="10" :xl="10" class="left-container">
|
||||
<div class="login-image">
|
||||
<img :src="`../src/assets/theme/${themeImg}.jpg`" class="login-image" />
|
||||
</div>
|
||||
<div class="login-image" :style="loginImageStyle"></div>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="24" :md="14" :lg="14" :xl="14" class="right-container flex-center">
|
||||
<slot></slot>
|
||||
|
|
@ -15,12 +13,33 @@
|
|||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, watch } from 'vue'
|
||||
import { computed } from 'vue'
|
||||
import { getThemeImg } from '@/utils/theme'
|
||||
import useStore from '@/stores'
|
||||
defineOptions({ name: 'LoginLayout' })
|
||||
const props = defineProps({
|
||||
themeImg: {
|
||||
type: String,
|
||||
default: 'default'
|
||||
const { user } = useStore()
|
||||
|
||||
const fileURL = computed(() => {
|
||||
if (user.themeInfo.loginImage) {
|
||||
if (typeof user.themeInfo.loginImage === 'string') {
|
||||
return user.themeInfo.loginImage
|
||||
} else {
|
||||
return URL.createObjectURL(user.themeInfo.loginImage)
|
||||
}
|
||||
} else {
|
||||
return ''
|
||||
}
|
||||
})
|
||||
|
||||
const loginImageStyle = computed(() => {
|
||||
if (user.themeInfo.loginImage) {
|
||||
return {
|
||||
backgroundImage: `url(${fileURL.value})`
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
backgroundImage: `url(../src/assets/theme/${getThemeImg(user.themeInfo?.theme)}.jpg)`
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
|
@ -29,7 +48,9 @@ const props = defineProps({
|
|||
height: 100vh;
|
||||
|
||||
.login-image {
|
||||
object-fit: cover;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
background-size: cover;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,59 +1,62 @@
|
|||
<template>
|
||||
<svg
|
||||
v-if="!isDefaultTheme"
|
||||
viewBox="0 0 122 36"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
:height="height"
|
||||
:class="!isDefaultTheme ? 'custom-logo-color' : ''"
|
||||
>
|
||||
<g clip-path="url(#clip0_5682_1471)" fill-rule="evenodd">
|
||||
<path
|
||||
d="M75.3094 19.0805V27.05H71.8274L71.8109 26.2436C70.5933 26.8762 69.4033 27.1925 68.2412 27.1923H67.8972C66.7033 27.1923 65.7546 26.7337 65.051 25.8166C64.5855 25.1007 64.342 24.2631 64.3513 23.4092V23.3143C64.3513 21.7489 64.9008 20.7092 65.9997 20.1953C66.4505 19.8949 67.5929 19.7447 69.4271 19.7447H71.3008V19.3058C71.3008 18.4045 71.1703 17.8867 70.9094 17.7523C70.6249 17.5388 70.1228 17.4321 69.4033 17.4321H65.6678L65.7312 14.2396L70.032 14.1233C72.5857 14.1233 74.1669 14.7558 74.7758 16.0208C75.1315 16.756 75.3094 17.7759 75.3094 19.0805ZM68.6032 22.3901C68.4844 22.5315 68.3597 22.9902 68.3597 23.3143C68.3597 24.0259 68.7234 24.3817 69.4508 24.3817C69.8855 24.3817 70.5022 24.2038 71.3008 23.848V22.1112C71.3008 22.1112 69.0361 21.875 68.6032 22.3901Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M56.6308 27.3317L55.4162 15.1815L52.0028 27.3317H48.028L44.6928 15.1815L43.4348 27.3317L37.9399 27.2849L40.6207 9.35034H47.6212L50.0211 17.8845L52.4444 9.35034H59.281L62.0087 27.3317H56.6308Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M85.3943 26.9654L83.5118 23.1105L81.6291 26.9654H77.2017L80.831 20.5778L77.3644 14.6084H81.6525L83.5118 18.2543L85.313 14.6084H89.6009L86.1576 20.5778L89.7637 26.9654H85.3943Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M101.114 26.9656C101.029 26.8419 96.8966 20.1336 96.8966 20.1336L95.5396 22.2226V26.9656H90.9727V9.11621H95.5396V16.3526L99.7128 9.17639H104.477L99.514 17.1079L105.855 26.9656H101.114Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M121.036 22.145C121.036 24.745 119.74 27.1282 115.097 27.1282H107.032V9.02689L113.203 8.90869C113.203 8.90869 116.858 8.74751 118.706 10.153C120.068 11.1885 120.515 13.0021 120.384 14.5197C120.254 16.0373 119.553 17.0129 118.405 17.727C119.979 18.354 121.036 19.5451 121.036 22.145ZM114.228 16.3325C115.766 16.3325 116.62 15.5559 116.62 14.45C116.62 13.2735 115.833 12.5837 114.228 12.5837L111.576 12.5906V16.3325H114.228ZM114.365 23.5374C116.497 23.5374 117.022 22.393 117.022 21.6316C117.022 20.4308 116.17 19.563 114.752 19.563H111.576V23.5374H114.365Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M17.4213 26.7354H12.8296L11.1277 28.4372C11.028 28.5369 10.9601 28.6639 10.9326 28.8022C10.9051 28.9405 10.9193 29.0838 10.9732 29.2141C11.0272 29.3443 11.1185 29.4557 11.2358 29.534C11.353 29.6123 11.4908 29.6541 11.6318 29.6541H18.6192C18.7602 29.6541 18.898 29.6123 19.0153 29.534C19.1325 29.4557 19.2239 29.3443 19.2778 29.2141C19.3318 29.0838 19.3459 28.9405 19.3184 28.8022C19.2909 28.6639 19.223 28.5369 19.1233 28.4372L17.4213 26.7354Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M30.04 13.3823H29.1348V19.7499H30.04C30.1305 19.7499 30.2201 19.732 30.3037 19.6974C30.3873 19.6628 30.4633 19.612 30.5273 19.548C30.5913 19.484 30.642 19.4081 30.6767 19.3244C30.7113 19.2408 30.7291 19.1512 30.7291 19.0607V14.0715C30.7291 13.8887 30.6565 13.7134 30.5273 13.5842C30.398 13.4549 30.2227 13.3823 30.04 13.3823Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M1.92296 13.3823H1.01776C0.834985 13.3823 0.659698 13.4549 0.530458 13.5842C0.401219 13.7134 0.328613 13.8887 0.328613 14.0715V19.0607C0.328611 19.1512 0.346435 19.2408 0.381067 19.3244C0.415699 19.4081 0.466461 19.484 0.530455 19.548C0.594448 19.612 0.670419 19.6628 0.754031 19.6974C0.837643 19.732 0.927258 19.7499 1.01776 19.7499H1.92296V13.3823Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M19.0238 14.2251C18.682 14.2251 18.3541 14.3609 18.1124 14.6026C17.8707 14.8443 17.7349 15.1722 17.7349 15.514V16.4382C17.7349 16.7801 17.8707 17.108 18.1124 17.3497C18.3541 17.5914 18.682 17.7272 19.0239 17.7272C19.3657 17.7272 19.6936 17.5914 19.9353 17.3497C20.1771 17.108 20.3129 16.7801 20.3129 16.4382V15.5141C20.3129 15.3448 20.2796 15.1772 20.2148 15.0208C20.15 14.8644 20.055 14.7223 19.9353 14.6026C19.8156 14.4829 19.6735 14.388 19.5171 14.3232C19.3607 14.2584 19.1931 14.2251 19.0238 14.2251Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M12.3012 14.2251C11.9593 14.2251 11.6315 14.3609 11.3897 14.6026C11.148 14.8443 11.0122 15.1722 11.0122 15.514V16.4382C11.0122 16.7801 11.148 17.108 11.3897 17.3497C11.6315 17.5914 11.9593 17.7272 12.3012 17.7272C12.6431 17.7272 12.9709 17.5914 13.2127 17.3497C13.4544 17.108 13.5902 16.7801 13.5902 16.4382V15.5141C13.5902 15.3448 13.5569 15.1772 13.4921 15.0208C13.4273 14.8644 13.3324 14.7223 13.2127 14.6026C13.093 14.4829 12.9509 14.388 12.7945 14.3232C12.6381 14.2584 12.4704 14.2251 12.3012 14.2251Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M23.3607 6.91333H7.69709C6.3139 6.91489 4.98782 7.46505 4.00976 8.44311C3.0317 9.42117 2.48154 10.7473 2.47998 12.1304V20.9612C2.48154 22.3444 3.03169 23.6705 4.00975 24.6486C4.98781 25.6266 6.3139 26.1768 7.69709 26.1784H23.3607C24.7439 26.1768 26.07 25.6267 27.0481 24.6486C28.0262 23.6705 28.5764 22.3444 28.5779 20.9612V12.1304C28.5763 10.7472 28.0262 9.42115 27.0481 8.44309C26.07 7.46503 24.7439 6.91487 23.3607 6.91333ZM23.7988 20.9085C23.7988 21.1577 23.6998 21.3968 23.5235 21.573C23.3473 21.7492 23.1083 21.8482 22.859 21.8482H15.2189C14.0629 21.8482 12.9263 22.1453 11.9181 22.711L9.355 24.1492V21.8483H8.19882C7.94958 21.8483 7.71055 21.7493 7.53432 21.573C7.35808 21.3968 7.25907 21.1578 7.25906 20.9085V11.547C7.25907 11.2978 7.35808 11.0588 7.53432 10.8825C7.71056 10.7063 7.94958 10.6073 8.19882 10.6073H22.859C23.1082 10.6073 23.3472 10.7063 23.5235 10.8825C23.6997 11.0588 23.7987 11.2978 23.7987 11.5471L23.7988 20.9085Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
<img v-else src="@/assets/logo/MaxKB-logo.svg" :height="height" />
|
||||
<img v-if="user.themeInfo.loginLogo" :src="fileURL" alt="" height="45px" class="mr-8" />
|
||||
<template v-else>
|
||||
<svg
|
||||
v-if="!isDefaultTheme"
|
||||
viewBox="0 0 122 36"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
:height="height"
|
||||
:class="!isDefaultTheme ? 'custom-logo-color' : ''"
|
||||
>
|
||||
<g clip-path="url(#clip0_5682_1471)" fill-rule="evenodd">
|
||||
<path
|
||||
d="M75.3094 19.0805V27.05H71.8274L71.8109 26.2436C70.5933 26.8762 69.4033 27.1925 68.2412 27.1923H67.8972C66.7033 27.1923 65.7546 26.7337 65.051 25.8166C64.5855 25.1007 64.342 24.2631 64.3513 23.4092V23.3143C64.3513 21.7489 64.9008 20.7092 65.9997 20.1953C66.4505 19.8949 67.5929 19.7447 69.4271 19.7447H71.3008V19.3058C71.3008 18.4045 71.1703 17.8867 70.9094 17.7523C70.6249 17.5388 70.1228 17.4321 69.4033 17.4321H65.6678L65.7312 14.2396L70.032 14.1233C72.5857 14.1233 74.1669 14.7558 74.7758 16.0208C75.1315 16.756 75.3094 17.7759 75.3094 19.0805ZM68.6032 22.3901C68.4844 22.5315 68.3597 22.9902 68.3597 23.3143C68.3597 24.0259 68.7234 24.3817 69.4508 24.3817C69.8855 24.3817 70.5022 24.2038 71.3008 23.848V22.1112C71.3008 22.1112 69.0361 21.875 68.6032 22.3901Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M56.6308 27.3317L55.4162 15.1815L52.0028 27.3317H48.028L44.6928 15.1815L43.4348 27.3317L37.9399 27.2849L40.6207 9.35034H47.6212L50.0211 17.8845L52.4444 9.35034H59.281L62.0087 27.3317H56.6308Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M85.3943 26.9654L83.5118 23.1105L81.6291 26.9654H77.2017L80.831 20.5778L77.3644 14.6084H81.6525L83.5118 18.2543L85.313 14.6084H89.6009L86.1576 20.5778L89.7637 26.9654H85.3943Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M101.114 26.9656C101.029 26.8419 96.8966 20.1336 96.8966 20.1336L95.5396 22.2226V26.9656H90.9727V9.11621H95.5396V16.3526L99.7128 9.17639H104.477L99.514 17.1079L105.855 26.9656H101.114Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M121.036 22.145C121.036 24.745 119.74 27.1282 115.097 27.1282H107.032V9.02689L113.203 8.90869C113.203 8.90869 116.858 8.74751 118.706 10.153C120.068 11.1885 120.515 13.0021 120.384 14.5197C120.254 16.0373 119.553 17.0129 118.405 17.727C119.979 18.354 121.036 19.5451 121.036 22.145ZM114.228 16.3325C115.766 16.3325 116.62 15.5559 116.62 14.45C116.62 13.2735 115.833 12.5837 114.228 12.5837L111.576 12.5906V16.3325H114.228ZM114.365 23.5374C116.497 23.5374 117.022 22.393 117.022 21.6316C117.022 20.4308 116.17 19.563 114.752 19.563H111.576V23.5374H114.365Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M17.4213 26.7354H12.8296L11.1277 28.4372C11.028 28.5369 10.9601 28.6639 10.9326 28.8022C10.9051 28.9405 10.9193 29.0838 10.9732 29.2141C11.0272 29.3443 11.1185 29.4557 11.2358 29.534C11.353 29.6123 11.4908 29.6541 11.6318 29.6541H18.6192C18.7602 29.6541 18.898 29.6123 19.0153 29.534C19.1325 29.4557 19.2239 29.3443 19.2778 29.2141C19.3318 29.0838 19.3459 28.9405 19.3184 28.8022C19.2909 28.6639 19.223 28.5369 19.1233 28.4372L17.4213 26.7354Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M30.04 13.3823H29.1348V19.7499H30.04C30.1305 19.7499 30.2201 19.732 30.3037 19.6974C30.3873 19.6628 30.4633 19.612 30.5273 19.548C30.5913 19.484 30.642 19.4081 30.6767 19.3244C30.7113 19.2408 30.7291 19.1512 30.7291 19.0607V14.0715C30.7291 13.8887 30.6565 13.7134 30.5273 13.5842C30.398 13.4549 30.2227 13.3823 30.04 13.3823Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M1.92296 13.3823H1.01776C0.834985 13.3823 0.659698 13.4549 0.530458 13.5842C0.401219 13.7134 0.328613 13.8887 0.328613 14.0715V19.0607C0.328611 19.1512 0.346435 19.2408 0.381067 19.3244C0.415699 19.4081 0.466461 19.484 0.530455 19.548C0.594448 19.612 0.670419 19.6628 0.754031 19.6974C0.837643 19.732 0.927258 19.7499 1.01776 19.7499H1.92296V13.3823Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M19.0238 14.2251C18.682 14.2251 18.3541 14.3609 18.1124 14.6026C17.8707 14.8443 17.7349 15.1722 17.7349 15.514V16.4382C17.7349 16.7801 17.8707 17.108 18.1124 17.3497C18.3541 17.5914 18.682 17.7272 19.0239 17.7272C19.3657 17.7272 19.6936 17.5914 19.9353 17.3497C20.1771 17.108 20.3129 16.7801 20.3129 16.4382V15.5141C20.3129 15.3448 20.2796 15.1772 20.2148 15.0208C20.15 14.8644 20.055 14.7223 19.9353 14.6026C19.8156 14.4829 19.6735 14.388 19.5171 14.3232C19.3607 14.2584 19.1931 14.2251 19.0238 14.2251Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M12.3012 14.2251C11.9593 14.2251 11.6315 14.3609 11.3897 14.6026C11.148 14.8443 11.0122 15.1722 11.0122 15.514V16.4382C11.0122 16.7801 11.148 17.108 11.3897 17.3497C11.6315 17.5914 11.9593 17.7272 12.3012 17.7272C12.6431 17.7272 12.9709 17.5914 13.2127 17.3497C13.4544 17.108 13.5902 16.7801 13.5902 16.4382V15.5141C13.5902 15.3448 13.5569 15.1772 13.4921 15.0208C13.4273 14.8644 13.3324 14.7223 13.2127 14.6026C13.093 14.4829 12.9509 14.388 12.7945 14.3232C12.6381 14.2584 12.4704 14.2251 12.3012 14.2251Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M23.3607 6.91333H7.69709C6.3139 6.91489 4.98782 7.46505 4.00976 8.44311C3.0317 9.42117 2.48154 10.7473 2.47998 12.1304V20.9612C2.48154 22.3444 3.03169 23.6705 4.00975 24.6486C4.98781 25.6266 6.3139 26.1768 7.69709 26.1784H23.3607C24.7439 26.1768 26.07 25.6267 27.0481 24.6486C28.0262 23.6705 28.5764 22.3444 28.5779 20.9612V12.1304C28.5763 10.7472 28.0262 9.42115 27.0481 8.44309C26.07 7.46503 24.7439 6.91487 23.3607 6.91333ZM23.7988 20.9085C23.7988 21.1577 23.6998 21.3968 23.5235 21.573C23.3473 21.7492 23.1083 21.8482 22.859 21.8482H15.2189C14.0629 21.8482 12.9263 22.1453 11.9181 22.711L9.355 24.1492V21.8483H8.19882C7.94958 21.8483 7.71055 21.7493 7.53432 21.573C7.35808 21.3968 7.25907 21.1578 7.25906 20.9085V11.547C7.25907 11.2978 7.35808 11.0588 7.53432 10.8825C7.71056 10.7063 7.94958 10.6073 8.19882 10.6073H22.859C23.1082 10.6073 23.3472 10.7063 23.5235 10.8825C23.6997 11.0588 23.7987 11.2978 23.7987 11.5471L23.7988 20.9085Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
<img v-else src="@/assets/logo/MaxKB-logo.svg" :height="height" />
|
||||
</template>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
|
|
@ -66,9 +69,21 @@ defineProps({
|
|||
default: '36px'
|
||||
}
|
||||
})
|
||||
const { common } = useStore()
|
||||
const { user } = useStore()
|
||||
const isDefaultTheme = computed(() => {
|
||||
return common.isDefaultTheme()
|
||||
return user.isDefaultTheme()
|
||||
})
|
||||
|
||||
const fileURL = computed(() => {
|
||||
if (user.themeInfo.loginLogo) {
|
||||
if (typeof user.themeInfo.loginLogo === 'string') {
|
||||
return user.themeInfo.loginLogo
|
||||
} else {
|
||||
return URL.createObjectURL(user.themeInfo.loginLogo)
|
||||
}
|
||||
} else {
|
||||
return ''
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
|
|
|
|||
|
|
@ -45,9 +45,9 @@ defineProps({
|
|||
default: '36px'
|
||||
}
|
||||
})
|
||||
const { common } = useStore()
|
||||
const { user } = useStore()
|
||||
const isDefaultTheme = computed(() => {
|
||||
return common.isDefaultTheme()
|
||||
return user.isDefaultTheme()
|
||||
})
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
|
|
|
|||
|
|
@ -8,9 +8,9 @@
|
|||
import { ref, computed, onMounted } from 'vue'
|
||||
import TopBar from '../top-bar/index.vue'
|
||||
import useStore from '@/stores'
|
||||
const { common } = useStore()
|
||||
const { user } = useStore()
|
||||
const isDefaultTheme = computed(() => {
|
||||
return common.isDefaultTheme()
|
||||
return user.isDefaultTheme()
|
||||
})
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -35,9 +35,9 @@
|
|||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue'
|
||||
import useStore from '@/stores'
|
||||
const { common, user } = useStore()
|
||||
const { user } = useStore()
|
||||
const isDefaultTheme = computed(() => {
|
||||
return common.isDefaultTheme()
|
||||
return user.isDefaultTheme()
|
||||
})
|
||||
|
||||
const aboutDialogVisible = ref(false)
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import * as ElementPlusIcons from '@element-plus/icons-vue'
|
|||
import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
|
||||
import { createApp } from 'vue'
|
||||
import { store } from '@/stores'
|
||||
import theme from '@/theme'
|
||||
import directives from '@/directives'
|
||||
import App from './App.vue'
|
||||
import router from '@/router'
|
||||
|
|
@ -56,8 +55,6 @@ app.use(ElementPlus, {
|
|||
locale: zhCn
|
||||
})
|
||||
|
||||
app.use(theme)
|
||||
|
||||
app.use(router)
|
||||
app.use(i18n)
|
||||
app.use(Components)
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { Role, ComplexPermission } from '@/utils/permission/type'
|
|||
const settingRouter = {
|
||||
path: '/setting',
|
||||
name: 'setting',
|
||||
meta: { icon: 'Setting', title: '系统设置', permission: 'SETTING:READ' },
|
||||
meta: { icon: 'Setting', title: '系统管理', permission: 'SETTING:READ' },
|
||||
redirect: () => {
|
||||
if (hasPermission(new Role('ADMIN'), 'AND')) {
|
||||
return '/user'
|
||||
|
|
@ -59,7 +59,7 @@ const settingRouter = {
|
|||
meta: {
|
||||
icon: 'app-setting',
|
||||
iconActive: 'app-setting-active',
|
||||
title: '系统设置',
|
||||
title: '系统管理',
|
||||
activeMenu: '/setting',
|
||||
parentPath: '/setting',
|
||||
parentName: 'setting',
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ export interface commonTypes {
|
|||
paginationConfig: any | null
|
||||
search: any
|
||||
device: string
|
||||
theme: string
|
||||
}
|
||||
|
||||
const useCommonStore = defineStore({
|
||||
|
|
@ -18,16 +17,9 @@ const useCommonStore = defineStore({
|
|||
// 搜索和分页缓存
|
||||
paginationConfig: {},
|
||||
search: {},
|
||||
device: DeviceType.Desktop,
|
||||
theme: ''
|
||||
device: DeviceType.Desktop
|
||||
}),
|
||||
actions: {
|
||||
isDefaultTheme() {
|
||||
return !this.theme || this.theme === '#3370FF'
|
||||
},
|
||||
setTheme(val: string) {
|
||||
this.theme = val
|
||||
},
|
||||
saveBreadcrumb(data: any) {
|
||||
this.breadcrumb = data
|
||||
},
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ import { defineStore } from 'pinia'
|
|||
import type { User } from '@/api/type/user'
|
||||
import UserApi from '@/api/user'
|
||||
import ThemeApi from '@/api/theme'
|
||||
import { useElementPlusTheme } from 'use-element-plus-theme'
|
||||
const { changeTheme } = useElementPlusTheme()
|
||||
|
||||
export interface userStateTypes {
|
||||
userType: number // 1 系统操作者 2 对话用户
|
||||
|
|
@ -26,6 +28,13 @@ const useUserStore = defineStore({
|
|||
themeInfo: null
|
||||
}),
|
||||
actions: {
|
||||
isDefaultTheme() {
|
||||
return !this.themeInfo?.theme || this.themeInfo?.theme === '#3370FF'
|
||||
},
|
||||
setTheme(data: any) {
|
||||
changeTheme(data?.['theme'])
|
||||
this.themeInfo = data
|
||||
},
|
||||
isExpire() {
|
||||
return this.isXPack && !this.XPACK_LICENSE_IS_VALID
|
||||
},
|
||||
|
|
@ -82,8 +91,14 @@ const useUserStore = defineStore({
|
|||
},
|
||||
|
||||
async theme() {
|
||||
return ThemeApi.getThemeInfo().then((ok) => {
|
||||
return await ThemeApi.getThemeInfo().then((ok) => {
|
||||
this.themeInfo = ok.data
|
||||
changeTheme(this.themeInfo['theme'])
|
||||
window.document.title = this.themeInfo['title'] || 'MaxKB'
|
||||
const link = document.querySelector('link[rel="icon"]') as any
|
||||
if (link) {
|
||||
link['href'] = this.themeInfo['icon'] || '/favicon.ico'
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -369,8 +369,8 @@ h5 {
|
|||
|
||||
/* tag */
|
||||
.default-tag {
|
||||
background: var(--tag-default-bg);
|
||||
color: var(--tag-default-color);
|
||||
background: var(--el-color-primary-light-7);
|
||||
color: var(--el-color-primary);
|
||||
border: none;
|
||||
}
|
||||
.success-tag {
|
||||
|
|
|
|||
|
|
@ -1,13 +0,0 @@
|
|||
import type { InferData } from "./type";
|
||||
const inferData: Array<InferData> = [
|
||||
{
|
||||
key: "primary",
|
||||
value: "#3370FF",
|
||||
},
|
||||
{ key: "success", value: "#67c23a" },
|
||||
{ key: "warning", value: "#e6a23c" },
|
||||
{ key: "danger", value: "#f56c6c" },
|
||||
{ key: "error", value: "#F54A45" },
|
||||
{ key: "info", value: "#909399" },
|
||||
];
|
||||
export default inferData;
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
import type { KeyValueData } from './type'
|
||||
const keyValueData: KeyValueData = {
|
||||
'--el-header-padding': '0px'
|
||||
}
|
||||
export default keyValueData
|
||||
|
|
@ -1,281 +0,0 @@
|
|||
import type {
|
||||
ThemeSetting,
|
||||
InferData,
|
||||
KeyValueData,
|
||||
UpdateInferData,
|
||||
UpdateKeyValueData
|
||||
} from './type'
|
||||
import { TinyColor } from '@ctrl/tinycolor'
|
||||
// 引入默认推断数据
|
||||
import inferData from './defaultInferData'
|
||||
// 引入默认keyValue数据
|
||||
import keyValueData from './defaultKeyValueData'
|
||||
// 引入设置对象
|
||||
import setting from './setting'
|
||||
import type { App } from 'vue'
|
||||
declare global {
|
||||
interface ChildNode {
|
||||
innerText: string
|
||||
}
|
||||
}
|
||||
class Theme {
|
||||
/**
|
||||
* 主题设置
|
||||
*/
|
||||
themeSetting: ThemeSetting
|
||||
/**
|
||||
* 键值数据
|
||||
*/
|
||||
keyValue: KeyValueData
|
||||
/**
|
||||
* 外推数据
|
||||
*/
|
||||
inferData: Array<InferData>
|
||||
/**
|
||||
*是否是第一次初始化
|
||||
*/
|
||||
isFirstWriteStyle: boolean
|
||||
/**
|
||||
* 混色白
|
||||
*/
|
||||
colorWhite: string
|
||||
/**
|
||||
* 混色黑
|
||||
*/
|
||||
colorBlack: string
|
||||
|
||||
constructor(themeSetting: ThemeSetting, keyValue: KeyValueData, inferData: Array<InferData>) {
|
||||
this.themeSetting = themeSetting
|
||||
this.keyValue = keyValue
|
||||
this.inferData = inferData
|
||||
this.isFirstWriteStyle = true
|
||||
this.colorWhite = '#ffffff'
|
||||
this.colorBlack = '#000000'
|
||||
this.initDefaultTheme()
|
||||
}
|
||||
|
||||
/**
|
||||
* 拼接
|
||||
* @param setting 主题设置
|
||||
* @param names 需要拼接的所有值
|
||||
* @returns 拼接后的数据
|
||||
*/
|
||||
getVarName = (setting: ThemeSetting, ...names: Array<string>) => {
|
||||
return (
|
||||
setting.startDivision + setting.namespace + setting.division + names.join(setting.division)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换外推数据
|
||||
* @param setting 主题设置对象
|
||||
* @param inferData 外推数据
|
||||
* @returns
|
||||
*/
|
||||
mapInferMainStyle = (setting: ThemeSetting, inferData: InferData) => {
|
||||
const key: string = this.getVarName(
|
||||
setting,
|
||||
inferData.setting ? inferData.setting.type : setting.colorInferSetting.type,
|
||||
inferData.key
|
||||
)
|
||||
return {
|
||||
[key]: inferData.value,
|
||||
...this.mapInferDataStyle(setting, inferData)
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 转换外推数据
|
||||
* @param setting 设置
|
||||
* @param inferData 外推数据
|
||||
*/
|
||||
mapInferData = (setting: ThemeSetting, inferData: Array<InferData>) => {
|
||||
return inferData
|
||||
.map((itemData) => {
|
||||
return this.mapInferMainStyle(setting, itemData)
|
||||
})
|
||||
.reduce((pre, next) => {
|
||||
return { ...pre, ...next }
|
||||
}, {})
|
||||
}
|
||||
/**
|
||||
* 转换外推数据
|
||||
* @param setting 主题设置对象
|
||||
* @param inferData 外推数据
|
||||
* @returns
|
||||
*/
|
||||
mapInferDataStyle = (setting: ThemeSetting, inferData: InferData) => {
|
||||
const inferSetting = inferData.setting ? inferData.setting : setting.colorInferSetting
|
||||
if (inferSetting.type === 'color') {
|
||||
return Object.keys(inferSetting)
|
||||
.map((key: string) => {
|
||||
if (key === 'light' || key === 'dark') {
|
||||
return inferSetting[key]
|
||||
.map((l: any) => {
|
||||
const varName = this.getVarName(
|
||||
setting,
|
||||
inferSetting.type,
|
||||
inferData.key,
|
||||
key,
|
||||
l.toString()
|
||||
)
|
||||
return {
|
||||
[varName]: new TinyColor(inferData.value)
|
||||
.mix(key === 'light' ? this.colorWhite : this.colorBlack, l * 10)
|
||||
.toHexString()
|
||||
}
|
||||
})
|
||||
.reduce((pre: any, next: any) => {
|
||||
return { ...pre, ...next }
|
||||
}, {})
|
||||
}
|
||||
return {}
|
||||
})
|
||||
.reduce((pre, next) => {
|
||||
return { ...pre, ...next }
|
||||
}, {})
|
||||
}
|
||||
return {}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param themeSetting 主题设置
|
||||
* @param keyValueData 键值数据
|
||||
* @returns 映射后的键值数据
|
||||
*/
|
||||
mapKeyValue = (themeSetting: ThemeSetting, keyValueData: KeyValueData) => {
|
||||
return Object.keys(keyValueData)
|
||||
.map((key: string) => {
|
||||
return {
|
||||
[this.updateKeyBySetting(key, themeSetting)]: keyValueData[key]
|
||||
}
|
||||
})
|
||||
.reduce((pre, next) => {
|
||||
return { ...pre, ...next }
|
||||
}, {})
|
||||
}
|
||||
/**
|
||||
* 根据配置文件修改Key
|
||||
* @param key key
|
||||
* @param themeSetting 主题设置
|
||||
* @returns
|
||||
*/
|
||||
updateKeyBySetting = (key: string, themeSetting: ThemeSetting) => {
|
||||
return key.startsWith(themeSetting.startDivision)
|
||||
? key
|
||||
: key.startsWith(themeSetting.namespace)
|
||||
? themeSetting.startDivision + key
|
||||
: key.startsWith(themeSetting.division)
|
||||
? themeSetting.startDivision + themeSetting.namespace
|
||||
: themeSetting.startDivision + themeSetting.namespace + themeSetting.division + key
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param setting 主题设置
|
||||
* @param keyValue 主题键值对数据
|
||||
* @param inferData 外推数据
|
||||
* @returns 合并后的键值对数据
|
||||
*/
|
||||
tokeyValueStyle = () => {
|
||||
return {
|
||||
...this.mapInferData(this.themeSetting, this.inferData),
|
||||
...this.mapKeyValue(this.themeSetting, this.keyValue)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将keyValue对象转换为S
|
||||
* @param keyValue
|
||||
* @returns
|
||||
*/
|
||||
toString = (keyValue: KeyValueData) => {
|
||||
const inner = Object.keys(keyValue)
|
||||
.map((key: string) => {
|
||||
return key + ':' + keyValue[key] + ';'
|
||||
})
|
||||
.join('')
|
||||
return `@charset "UTF-8";:root{${inner}}`
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param elNewStyle 新的变量样式
|
||||
*/
|
||||
writeNewStyle = (elNewStyle: string) => {
|
||||
if (this.isFirstWriteStyle) {
|
||||
const style = document.createElement('style')
|
||||
style.innerText = elNewStyle
|
||||
document.head.appendChild(style)
|
||||
this.isFirstWriteStyle = false
|
||||
} else {
|
||||
if (document.head.lastChild) {
|
||||
document.head.lastChild.innerText = elNewStyle
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改数据并且写入dom
|
||||
* @param updateInferData 平滑数据修改
|
||||
* @param updateKeyvalueData keyValue数据修改
|
||||
*/
|
||||
updateWrite = (updateInferData?: UpdateInferData, updateKeyvalueData?: UpdateKeyValueData) => {
|
||||
this.update(updateInferData, updateKeyvalueData)
|
||||
const newStyle = this.tokeyValueStyle()
|
||||
const newStyleString = this.toString(newStyle)
|
||||
this.writeNewStyle(newStyleString)
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改数据
|
||||
* @param inferData
|
||||
* @param keyvalueData
|
||||
*/
|
||||
update = (updateInferData?: UpdateInferData, updateKeyvalueData?: UpdateKeyValueData) => {
|
||||
if (updateInferData) {
|
||||
this.updateInferData(updateInferData)
|
||||
}
|
||||
if (updateKeyvalueData) {
|
||||
this.updateOrCreateKeyValueData(updateKeyvalueData)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改外推数据 外推数据只能修改,不能新增
|
||||
* @param inferData
|
||||
*/
|
||||
updateInferData = (updateInferData: UpdateInferData) => {
|
||||
Object.keys(updateInferData).forEach((key) => {
|
||||
const findInfer = this.inferData.find((itemInfer) => {
|
||||
return itemInfer.key === key
|
||||
})
|
||||
if (findInfer) {
|
||||
findInfer.value = updateInferData[key]
|
||||
} else {
|
||||
this.inferData.push({ key, value: updateInferData[key] })
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化默认主题
|
||||
*/
|
||||
initDefaultTheme = () => {
|
||||
this.updateWrite()
|
||||
}
|
||||
/**
|
||||
* 修改KeyValue数据
|
||||
* @param keyvalueData keyValue数据
|
||||
*/
|
||||
updateOrCreateKeyValueData = (updateKeyvalueData: UpdateKeyValueData) => {
|
||||
Object.keys(updateKeyvalueData).forEach((key) => {
|
||||
const newKey = this.updateKeyBySetting(key, this.themeSetting)
|
||||
this.keyValue[newKey] = updateKeyvalueData[newKey]
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const install = (app: App) => {
|
||||
app.config.globalProperties.theme = new Theme(setting, keyValueData, inferData)
|
||||
}
|
||||
export default { install }
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
import type { ThemeSetting } from "./type";
|
||||
const setting: ThemeSetting = {
|
||||
namespace: "el",
|
||||
division: "-",
|
||||
startDivision: "--",
|
||||
colorInferSetting: {
|
||||
light: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
|
||||
dark: [2],
|
||||
type: "color",
|
||||
},
|
||||
};
|
||||
export default setting;
|
||||
|
|
@ -1,71 +0,0 @@
|
|||
interface ThemeSetting {
|
||||
/**
|
||||
*element-ui Namespace
|
||||
*/
|
||||
namespace: string;
|
||||
/**
|
||||
* 数据分隔符
|
||||
*/
|
||||
division: string;
|
||||
/**
|
||||
* 前缀
|
||||
*/
|
||||
startDivision: string;
|
||||
/**
|
||||
* 颜色外推设置
|
||||
*/
|
||||
colorInferSetting: ColorInferSetting;
|
||||
}
|
||||
|
||||
/**
|
||||
* 颜色混和设置
|
||||
*/
|
||||
interface ColorInferSetting {
|
||||
/**
|
||||
* 与白色混
|
||||
*/
|
||||
light: Array<number>;
|
||||
/**
|
||||
* 与黑色混
|
||||
*/
|
||||
dark: Array<number>;
|
||||
/**
|
||||
* 类型
|
||||
*/
|
||||
type: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 平滑数据
|
||||
*/
|
||||
interface KeyValueData {
|
||||
[propName: string]: string;
|
||||
}
|
||||
type UpdateInferData = KeyValueData;
|
||||
|
||||
type UpdateKeyValueData = KeyValueData;
|
||||
/**
|
||||
*平滑数据
|
||||
*/
|
||||
interface InferData {
|
||||
/**
|
||||
* 设置
|
||||
*/
|
||||
setting?: ColorInferSetting | any;
|
||||
/**
|
||||
* 健
|
||||
*/
|
||||
key: string;
|
||||
/**
|
||||
* 值
|
||||
*/
|
||||
value: string;
|
||||
}
|
||||
|
||||
export type {
|
||||
KeyValueData,
|
||||
InferData,
|
||||
ThemeSetting,
|
||||
UpdateInferData,
|
||||
UpdateKeyValueData,
|
||||
};
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
export const themeList = [
|
||||
{
|
||||
label: '默认',
|
||||
value: '#3370FF',
|
||||
loginBackground: 'default'
|
||||
},
|
||||
{
|
||||
label: '活力橙',
|
||||
value: '#FF8800',
|
||||
loginBackground: 'orange'
|
||||
},
|
||||
{
|
||||
label: '松石绿',
|
||||
value: '#00B69D',
|
||||
loginBackground: 'green'
|
||||
},
|
||||
{
|
||||
label: '商务蓝',
|
||||
value: '#4954E6',
|
||||
loginBackground: 'default'
|
||||
},
|
||||
{
|
||||
label: '神秘紫',
|
||||
value: '#7F3BF5',
|
||||
loginBackground: 'purple'
|
||||
},
|
||||
{
|
||||
label: '胭脂红',
|
||||
value: '#F01D94',
|
||||
loginBackground: 'red'
|
||||
}
|
||||
]
|
||||
|
||||
export function getThemeImg(val: string) {
|
||||
return themeList.filter((v) => v.value === val)?.[0]?.loginBackground || 'default'
|
||||
}
|
||||
|
||||
export const defautSetting = {
|
||||
icon: '',
|
||||
loginLogo: '',
|
||||
loginImage: '',
|
||||
title: 'MaxKB',
|
||||
slogan: '欢迎使用 MaxKB 智能知识库'
|
||||
}
|
||||
|
|
@ -64,7 +64,7 @@
|
|||
<div class="mr-16">
|
||||
<el-button link @click="enlarge = !enlarge">
|
||||
<AppIcon
|
||||
:iconName="enlarge ? 'app-magnify' : 'app-minify'"
|
||||
:iconName="enlarge ? 'app-minify' : 'app-magnify'"
|
||||
class="color-secondary"
|
||||
style="font-size: 20px"
|
||||
></AppIcon>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div class="authentication-setting p-24">
|
||||
<h4>{{$t('login.authentication')}}</h4>
|
||||
<h4>{{ $t('login.authentication') }}</h4>
|
||||
<el-tabs v-model="activeName" class="demo-tabs" @tab-click="handleClick">
|
||||
<template v-for="(item, index) in tabList" :key="index">
|
||||
<el-tab-pane :label="item.label" :name="item.name">
|
||||
|
|
@ -29,13 +29,6 @@ const tabList = [
|
|||
}
|
||||
]
|
||||
|
||||
// 动态引入组件
|
||||
const loadComponent = async (componentName: string) => {
|
||||
await import(`./component/${componentName}.vue`).then((res) => res.default)
|
||||
}
|
||||
|
||||
const currentComponent = computed(() => loadComponent(activeName.value))
|
||||
|
||||
function handleClick() {}
|
||||
|
||||
onMounted(() => {})
|
||||
|
|
|
|||
|
|
@ -1,21 +1,21 @@
|
|||
<template>
|
||||
<login-layout v-loading="loading">
|
||||
<LoginContainer subTitle="欢迎使用 MaxKB 智能知识库">
|
||||
<LoginContainer :subTitle="user.themeInfo?.slogan || '欢迎使用 MaxKB 智能知识库'">
|
||||
<h2 class="mb-24">{{ loginMode || '普通登录' }}</h2>
|
||||
<el-form
|
||||
class="login-form"
|
||||
:rules="rules"
|
||||
:model="loginForm"
|
||||
ref="loginFormRef"
|
||||
@keyup.enter="login"
|
||||
class="login-form"
|
||||
:rules="rules"
|
||||
:model="loginForm"
|
||||
ref="loginFormRef"
|
||||
@keyup.enter="login"
|
||||
>
|
||||
<div class="mb-24">
|
||||
<el-form-item prop="username">
|
||||
<el-input
|
||||
size="large"
|
||||
class="input-item"
|
||||
v-model="loginForm.username"
|
||||
placeholder="请输入用户名"
|
||||
size="large"
|
||||
class="input-item"
|
||||
v-model="loginForm.username"
|
||||
placeholder="请输入用户名"
|
||||
>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
|
|
@ -23,12 +23,12 @@
|
|||
<div class="mb-24">
|
||||
<el-form-item prop="password">
|
||||
<el-input
|
||||
type="password"
|
||||
size="large"
|
||||
class="input-item"
|
||||
v-model="loginForm.password"
|
||||
placeholder="请输入密码"
|
||||
show-password
|
||||
type="password"
|
||||
size="large"
|
||||
class="input-item"
|
||||
v-model="loginForm.password"
|
||||
placeholder="请输入密码"
|
||||
show-password
|
||||
>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
|
|
@ -40,10 +40,10 @@
|
|||
注册
|
||||
</el-button> -->
|
||||
<el-button
|
||||
class="forgot-password"
|
||||
@click="router.push('/forgot_password')"
|
||||
link
|
||||
type="primary"
|
||||
class="forgot-password"
|
||||
@click="router.push('/forgot_password')"
|
||||
link
|
||||
type="primary"
|
||||
>
|
||||
忘记密码?
|
||||
</el-button>
|
||||
|
|
@ -55,21 +55,21 @@
|
|||
<div class="text-center mt-16">
|
||||
<template v-for="item in modeList">
|
||||
<el-button
|
||||
v-if="item !== ''&&loginMode !== item"
|
||||
circle
|
||||
:key="item"
|
||||
class="login-button-circle color-secondary"
|
||||
@click="changeMode(item)"
|
||||
>{{ item }}
|
||||
v-if="item !== '' && loginMode !== item"
|
||||
circle
|
||||
:key="item"
|
||||
class="login-button-circle color-secondary"
|
||||
@click="changeMode(item)"
|
||||
>{{ item }}
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="item === ''&&loginMode !== ''"
|
||||
circle
|
||||
:key="item"
|
||||
class="login-button-circle color-secondary"
|
||||
style="font-size: 24px"
|
||||
icon="UserFilled"
|
||||
@click="changeMode('')"
|
||||
v-if="item === '' && loginMode !== ''"
|
||||
circle
|
||||
:key="item"
|
||||
class="login-button-circle color-secondary"
|
||||
style="font-size: 24px"
|
||||
icon="UserFilled"
|
||||
@click="changeMode('')"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
|
|
@ -77,14 +77,14 @@
|
|||
</login-layout>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import {onMounted, ref} from 'vue'
|
||||
import type {LoginRequest} from '@/api/type/user'
|
||||
import {useRouter} from 'vue-router'
|
||||
import type {FormInstance, FormRules} from 'element-plus'
|
||||
import { onMounted, ref } from 'vue'
|
||||
import type { LoginRequest } from '@/api/type/user'
|
||||
import { useRouter } from 'vue-router'
|
||||
import type { FormInstance, FormRules } from 'element-plus'
|
||||
import useStore from '@/stores'
|
||||
|
||||
const loading = ref<boolean>(false)
|
||||
const {user} = useStore()
|
||||
const { user } = useStore()
|
||||
const router = useRouter()
|
||||
const loginForm = ref<LoginRequest>({
|
||||
username: '',
|
||||
|
|
@ -109,8 +109,7 @@ const rules = ref<FormRules<LoginRequest>>({
|
|||
})
|
||||
const loginFormRef = ref<FormInstance>()
|
||||
|
||||
|
||||
const modeList = ref<string[]>(['']);
|
||||
const modeList = ref<string[]>([''])
|
||||
const loginMode = ref('')
|
||||
|
||||
function changeMode(val: string) {
|
||||
|
|
@ -126,25 +125,28 @@ const login = () => {
|
|||
loginFormRef.value?.validate().then(() => {
|
||||
loading.value = true
|
||||
user
|
||||
.login(loginMode.value, loginForm.value.username, loginForm.value.password)
|
||||
.then(() => {
|
||||
router.push({name: 'home'})
|
||||
})
|
||||
.finally(() => (loading.value = false))
|
||||
.login(loginMode.value, loginForm.value.username, loginForm.value.password)
|
||||
.then(() => {
|
||||
router.push({ name: 'home' })
|
||||
})
|
||||
.finally(() => (loading.value = false))
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
user.theme()
|
||||
user.asyncGetProfile().then((res) => {
|
||||
if (user.isXPack) {
|
||||
loading.value = true
|
||||
user.getAuthType().then((res) => {
|
||||
modeList.value = [...modeList.value, ...res];
|
||||
}).finally(() => (loading.value = false))
|
||||
user
|
||||
.getAuthType()
|
||||
.then((res) => {
|
||||
modeList.value = [...modeList.value, ...res]
|
||||
})
|
||||
.finally(() => (loading.value = false))
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
</script>
|
||||
<style lang="scss" scope>
|
||||
.login-gradient-divider {
|
||||
|
|
|
|||
|
|
@ -3,14 +3,15 @@
|
|||
<div class="header">
|
||||
<div class="tag flex-between">
|
||||
<div class="flex align-center">
|
||||
<LogoIcon height="24px" class="mr-8" />
|
||||
<span class="ellipsis">{{ title }}</span>
|
||||
<img v-if="props.data.icon" :src="fileURL" alt="" height="20px" class="mr-8" />
|
||||
<img v-else src="@/assets/logo/logo.svg" height="24px" class="mr-8" />
|
||||
<span class="ellipsis">{{ data.title }}</span>
|
||||
</div>
|
||||
<el-icon><Close /></el-icon>
|
||||
</div>
|
||||
</div>
|
||||
<login-layout style="height: 530px" :themeImg="themeImg">
|
||||
<LoginContainer :subTitle="slogan" class="login-container">
|
||||
<login-layout style="height: 530px">
|
||||
<LoginContainer :subTitle="data.slogan" class="login-container">
|
||||
<div class="mask"></div>
|
||||
<h2 class="mb-24">{{ '普通登录' }}</h2>
|
||||
<el-form class="login-form">
|
||||
|
|
@ -42,18 +43,24 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
themeImg: {
|
||||
type: String,
|
||||
default: 'default'
|
||||
},
|
||||
slogan: {
|
||||
type: String,
|
||||
default: '欢迎使用 MaxKB 智能知识库'
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
default: 'MaxKB'
|
||||
data: {
|
||||
type: Object,
|
||||
default: null
|
||||
}
|
||||
})
|
||||
|
||||
const fileURL = computed(() => {
|
||||
if (props.data.icon) {
|
||||
if (typeof props.data.icon === 'string') {
|
||||
return props.data.icon
|
||||
} else {
|
||||
return URL.createObjectURL(props.data.icon)
|
||||
}
|
||||
} else {
|
||||
return ''
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div class="theme-setting">
|
||||
<div class="theme-setting" v-loading="loading">
|
||||
<h4 class="p-16-24">外观设置</h4>
|
||||
<el-scrollbar>
|
||||
<div class="p-24 pt-0">
|
||||
|
|
@ -8,7 +8,7 @@
|
|||
<el-radio-group
|
||||
v-model="themeForm.theme"
|
||||
class="app-radio-button-group"
|
||||
@change="changeTheme"
|
||||
@change="changeThemeHandle"
|
||||
>
|
||||
<template v-for="(item, index) in themeList" :key="index">
|
||||
<el-radio-button :label="item.label" :value="item.value" />
|
||||
|
|
@ -20,19 +20,30 @@
|
|||
<el-card shadow="never" class="layout-bg">
|
||||
<div class="flex-between">
|
||||
<h5 class="mb-16">页面预览</h5>
|
||||
<el-button type="primary" link> 恢复默认 </el-button>
|
||||
<el-button type="primary" link @click="resetForm"> 恢复默认 </el-button>
|
||||
</div>
|
||||
<div class="theme-preview">
|
||||
<el-row :gutter="8">
|
||||
<el-col :span="16">
|
||||
<LoginPreview :themeImg="themeImg" :slogan="themeForm.slogan" :title="themeForm.title" />
|
||||
<LoginPreview :data="themeForm" />
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="theme-form">
|
||||
<el-card shadow="never" class="mb-8">
|
||||
<div class="flex-between mb-8">
|
||||
<span class="lighter">网站 Logo</span>
|
||||
<el-button size="small"> 替换图片 </el-button>
|
||||
<el-upload
|
||||
ref="uploadRef"
|
||||
action="#"
|
||||
:auto-upload="false"
|
||||
:show-file-list="false"
|
||||
accept="image/*"
|
||||
:on-change="
|
||||
(file: any, fileList: any) => onChange(file, fileList, 'icon')
|
||||
"
|
||||
>
|
||||
<el-button size="small"> 替换图片 </el-button>
|
||||
</el-upload>
|
||||
</div>
|
||||
<el-text type="info" size="small"
|
||||
>顶部网站显示的 Logo,建议尺寸 48 x 48,支持 JPG、PNG、SVG,大小不超过
|
||||
|
|
@ -42,7 +53,18 @@
|
|||
<el-card shadow="never" class="mb-8">
|
||||
<div class="flex-between mb-8">
|
||||
<span class="lighter">登录 Logo</span>
|
||||
<el-button size="small"> 替换图片 </el-button>
|
||||
<el-upload
|
||||
ref="uploadRef"
|
||||
action="#"
|
||||
:auto-upload="false"
|
||||
:show-file-list="false"
|
||||
accept="image/*"
|
||||
:on-change="
|
||||
(file: any, fileList: any) => onChange(file, fileList, 'loginLogo')
|
||||
"
|
||||
>
|
||||
<el-button size="small"> 替换图片 </el-button>
|
||||
</el-upload>
|
||||
</div>
|
||||
<el-text type="info" size="small"
|
||||
>登录页面右侧 Logo,建议尺寸 204*52,支持 JPG、PNG、SVG,大小不超过
|
||||
|
|
@ -52,7 +74,18 @@
|
|||
<el-card shadow="never" class="mb-8">
|
||||
<div class="flex-between mb-8">
|
||||
<span class="lighter">登录背景图</span>
|
||||
<el-button size="small"> 替换图片 </el-button>
|
||||
<el-upload
|
||||
ref="uploadRef"
|
||||
action="#"
|
||||
:auto-upload="false"
|
||||
:show-file-list="false"
|
||||
accept="image/*"
|
||||
:on-change="
|
||||
(file: any, fileList: any) => onChange(file, fileList, 'loginImage')
|
||||
"
|
||||
>
|
||||
<el-button size="small"> 替换图片 </el-button>
|
||||
</el-upload>
|
||||
</div>
|
||||
<el-text type="info" size="small">
|
||||
左侧背景图,矢量图建议尺寸 576*900,位图建议尺寸1152*1800;支持
|
||||
|
|
@ -92,53 +125,34 @@
|
|||
</el-scrollbar>
|
||||
<div class="theme-setting__operate w-full p-16-24">
|
||||
<el-button @click="resetTheme">放弃更新</el-button>
|
||||
<el-button type="primary"> 保存并应用 </el-button>
|
||||
<el-button type="primary" @click="updataTheme(themeFormRef)"> 保存并应用 </el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive, watch } from 'vue'
|
||||
import type { FormInstance, FormRules } from 'element-plus'
|
||||
import { ref, reactive, onMounted, computed, watch } from 'vue'
|
||||
import { onBeforeRouteLeave } from 'vue-router'
|
||||
import type { FormInstance, FormRules, UploadFiles } from 'element-plus'
|
||||
import { cloneDeep } from 'lodash'
|
||||
import LoginPreview from './LoginPreview.vue'
|
||||
import { useElementPlusTheme } from 'use-element-plus-theme'
|
||||
import { themeList, defautSetting } from '@/utils/theme'
|
||||
import ThemeApi from '@/api/theme'
|
||||
import { MsgSuccess, MsgError } from '@/utils/message'
|
||||
import useStore from '@/stores'
|
||||
const { common } = useStore()
|
||||
|
||||
const themeList = [
|
||||
{
|
||||
label: '默认',
|
||||
value: '#3370FF',
|
||||
loginBackground: 'default'
|
||||
},
|
||||
{
|
||||
label: '活力橙',
|
||||
value: '#FF8800',
|
||||
loginBackground: 'orange'
|
||||
},
|
||||
{
|
||||
label: '松石绿',
|
||||
value: '#00B69D',
|
||||
loginBackground: 'green'
|
||||
},
|
||||
{
|
||||
label: '商务蓝',
|
||||
value: '#4954E6',
|
||||
loginBackground: 'default'
|
||||
},
|
||||
{
|
||||
label: '神秘紫',
|
||||
value: '#7F3BF5',
|
||||
loginBackground: 'purple'
|
||||
},
|
||||
{
|
||||
label: '胭脂红',
|
||||
value: '#F01D94',
|
||||
loginBackground: 'red'
|
||||
}
|
||||
]
|
||||
const { user } = useStore()
|
||||
|
||||
onBeforeRouteLeave((to, from) => {
|
||||
user.setTheme(cloneTheme.value)
|
||||
})
|
||||
|
||||
const themeInfo = computed(() => user.themeInfo)
|
||||
|
||||
const themeFormRef = ref<FormInstance>()
|
||||
const themeForm = ref({
|
||||
const loading = ref(false)
|
||||
const cloneTheme = ref(null)
|
||||
const themeForm = ref<any>({
|
||||
theme: '#3370FF',
|
||||
icon: '',
|
||||
loginLogo: '',
|
||||
|
|
@ -152,24 +166,67 @@ const rules = reactive<FormRules>({
|
|||
slogan: [{ required: true, message: '请输入欢迎语', trigger: 'blur' }]
|
||||
})
|
||||
|
||||
const themeImg = ref('default')
|
||||
|
||||
const { changeTheme } = useElementPlusTheme(themeForm.value.theme)
|
||||
|
||||
function resetTheme() {
|
||||
themeForm.value.theme = '#3370FF'
|
||||
changeTheme(themeForm.value.theme)
|
||||
}
|
||||
|
||||
watch(
|
||||
() => themeForm.value.theme,
|
||||
(val) => {
|
||||
if (val) {
|
||||
common.setTheme(val)
|
||||
themeImg.value = themeList.filter((v) => v.value === val)[0].loginBackground
|
||||
const onChange = (file: any, fileList: UploadFiles, attr: string) => {
|
||||
if (attr === 'loginImage') {
|
||||
const isLimit = file?.size / 1024 / 1024 < 5
|
||||
if (!isLimit) {
|
||||
// @ts-ignore
|
||||
MsgError(`文件大小超过 5M`)
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
const isLimit = file?.size / 1024 < 200
|
||||
if (!isLimit) {
|
||||
// @ts-ignore
|
||||
MsgError(`文件大小超过 200KB`)
|
||||
return false
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
themeForm.value[attr] = file.raw
|
||||
}
|
||||
|
||||
function changeThemeHandle(val: string) {
|
||||
themeForm.value.theme = val
|
||||
user.setTheme(themeForm.value)
|
||||
}
|
||||
|
||||
function resetTheme() {
|
||||
user.setTheme(cloneTheme.value)
|
||||
themeForm.value = cloneDeep(themeInfo.value)
|
||||
}
|
||||
|
||||
function resetForm() {
|
||||
themeForm.value = {
|
||||
theme: themeForm.value.theme,
|
||||
...defautSetting
|
||||
}
|
||||
user.setTheme(themeForm.value)
|
||||
}
|
||||
|
||||
const updataTheme = async (formEl: FormInstance | undefined, test?: string) => {
|
||||
if (!formEl) return
|
||||
await formEl.validate((valid, fields) => {
|
||||
if (valid) {
|
||||
let fd = new FormData()
|
||||
Object.keys(themeForm.value).map((item) => {
|
||||
fd.append(item, themeForm.value[item])
|
||||
})
|
||||
ThemeApi.postThemeInfo(fd, loading).then((res) => {
|
||||
user.theme()
|
||||
cloneTheme.value = cloneDeep(themeForm.value)
|
||||
MsgSuccess('外观设置成功')
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (themeInfo.value) {
|
||||
themeForm.value = themeInfo.value
|
||||
cloneTheme.value = cloneDeep(themeInfo.value)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<AppAvatar shape="square avatar-blue">
|
||||
<AppAvatar shape="square" class="avatar-blue">
|
||||
<img src="@/assets/icon_document.svg" style="width: 58%" alt="" />
|
||||
</AppAvatar>
|
||||
</template>
|
||||
|
|
|
|||
Loading…
Reference in New Issue