feat: 新增外观设置功能

feat: 新增外观设置功能
This commit is contained in:
wangdan-fit2cloud 2024-07-16 03:29:53 -07:00 committed by GitHub
commit d493667f09
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
66 changed files with 954 additions and 617 deletions

View File

@ -55,7 +55,7 @@
<div
style="
width: 230px;
background: linear-gradient(180deg, #3370ff 0%, #7f3bf5 100%);
background: linear-gradient(180deg, #3370FF 0%, #7f3bf5 100%);
-webkit-background-clip: text;
font-size: 24px;
-webkit-text-fill-color: transparent;

View File

@ -37,6 +37,7 @@
"pinia": "^2.1.6",
"pinyin-pro": "^3.18.2",
"screenfull": "^6.0.2",
"use-element-plus-theme": "^0.0.5",
"vue": "^3.3.4",
"vue-clipboard3": "^2.0.0",
"vue-i18n": "^9.13.1",

35
ui/src/api/theme.ts Normal file
View File

@ -0,0 +1,35 @@
import { Result } from '@/request/Result'
import { get, post, del, put } from '@/request/index'
import type { Ref } from 'vue'
const prefix = '/display'
/**
*
*/
const getThemeInfo: () => Promise<Result<any>> = () => {
return get(`${prefix}/info`)
}
/**
*
* @param
* * formData {
* theme
* icon
* loginLogo
* loginImage
* title
* slogan
* }
*/
const postThemeInfo: (data: any, loading?: Ref<boolean>) => Promise<Result<boolean>> = (
data,
loading
) => {
return post(`${prefix}/update`, data, undefined, loading)
}
export default {
getThemeInfo,
postThemeInfo
}

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.0 KiB

View File

@ -0,0 +1,20 @@
<svg width="122" height="36" viewBox="0 0 122 36" xmlns="http://www.w3.org/2000/svg" fill="#FF8800">
<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>
<defs>
<clipPath id="clip0_5682_1471">
<rect width="121" height="36" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 5.4 KiB

View File

@ -0,0 +1,64 @@
<svg width="122" height="36" viewBox="0 0 122 36" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_5734_851)">
<path d="M75.3094 19.0802V27.0498H71.8274L71.8109 26.2433C70.5933 26.876 69.4033 27.1922 68.2412 27.1921H67.8972C66.7033 27.1921 65.7546 26.7335 65.051 25.8164C64.5855 25.1005 64.342 24.2629 64.3513 23.409V23.314C64.3513 21.7486 64.9008 20.7089 65.9997 20.195C66.4505 19.8947 67.5929 19.7445 69.4271 19.7444H71.3008V19.3056C71.3008 18.4043 71.1703 17.8865 70.9094 17.752C70.6249 17.5385 70.1228 17.4318 69.4033 17.4318H65.6678L65.7312 14.2393L70.032 14.123C72.5857 14.123 74.1669 14.7555 74.7758 16.0206C75.1315 16.7558 75.3094 17.7757 75.3094 19.0802ZM68.6032 22.3899C68.4844 22.5313 68.3597 22.99 68.3597 23.314C68.3597 24.0256 68.7234 24.3814 69.4508 24.3814C69.8855 24.3814 70.5022 24.2035 71.3008 23.8478V22.111C71.3008 22.111 69.0361 21.8747 68.6032 22.3899Z" fill="url(#paint0_linear_5734_851)"/>
<path d="M56.6308 27.3319L55.4162 15.1817L52.0028 27.3319H48.028L44.6928 15.1817L43.4348 27.3319L37.9399 27.2851L40.6207 9.35059H47.6212L50.0211 17.8847L52.4444 9.35059H59.281L62.0087 27.3319H56.6308Z" fill="url(#paint1_linear_5734_851)"/>
<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="url(#paint2_linear_5734_851)"/>
<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="url(#paint3_linear_5734_851)"/>
<path d="M121.036 22.1453C121.036 24.7452 119.74 27.1284 115.097 27.1284H107.032V9.02713L113.203 8.90893C113.203 8.90893 116.858 8.74776 118.706 10.1533C120.068 11.1888 120.515 13.0023 120.384 14.5199C120.254 16.0375 119.553 17.0132 118.405 17.7272C119.979 18.3542 121.036 19.5453 121.036 22.1453ZM114.228 16.3327C115.766 16.3327 116.62 15.5561 116.62 14.4502C116.62 13.2738 115.833 12.5839 114.228 12.5839L111.576 12.5909V16.3327H114.228ZM114.365 23.5376C116.497 23.5376 117.022 22.3933 117.022 21.6318C117.022 20.4311 116.17 19.5633 114.752 19.5633H111.576V23.5376H114.365Z" fill="url(#paint4_linear_5734_851)"/>
<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="url(#paint5_linear_5734_851)"/>
<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="url(#paint6_linear_5734_851)"/>
<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="url(#paint7_linear_5734_851)"/>
<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="url(#paint8_linear_5734_851)"/>
<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="url(#paint9_linear_5734_851)"/>
<path d="M23.3607 6.91309H7.69709C6.3139 6.91465 4.98782 7.46481 4.00976 8.44287C3.0317 9.42092 2.48154 10.747 2.47998 12.1302V20.9609C2.48154 22.3441 3.03169 23.6702 4.00975 24.6483C4.98781 25.6264 6.3139 26.1766 7.69709 26.1781H23.3607C24.7439 26.1766 26.07 25.6264 27.0481 24.6483C28.0262 23.6703 28.5764 22.3442 28.5779 20.9609V12.1302C28.5763 10.747 28.0262 9.4209 27.0481 8.44284C26.07 7.46478 24.7439 6.91463 23.3607 6.91309ZM23.7988 20.9082C23.7988 21.1575 23.6998 21.3965 23.5235 21.5727C23.3473 21.749 23.1083 21.848 22.859 21.848H15.2189C14.0629 21.848 12.9263 22.1451 11.9181 22.7108L9.355 24.149V21.848H8.19882C7.94958 21.848 7.71055 21.749 7.53432 21.5728C7.35808 21.3966 7.25907 21.1575 7.25906 20.9083V11.5468C7.25907 11.2976 7.35808 11.0585 7.53432 10.8823C7.71056 10.7061 7.94958 10.6071 8.19882 10.6071H22.859C23.1082 10.6071 23.3472 10.7061 23.5235 10.8823C23.6997 11.0585 23.7987 11.2976 23.7987 11.5468L23.7988 20.9082Z" fill="url(#paint10_linear_5734_851)"/>
</g>
<defs>
<linearGradient id="paint0_linear_5734_851" x1="69.8304" y1="10.0003" x2="69.8304" y2="27.3566" gradientUnits="userSpaceOnUse">
<stop stop-color="#3370FF"/>
<stop offset="1" stop-color="#7F3BF5"/>
</linearGradient>
<linearGradient id="paint1_linear_5734_851" x1="49.9743" y1="10.5855" x2="49.9743" y2="27.4838" gradientUnits="userSpaceOnUse">
<stop stop-color="#3370FF"/>
<stop offset="1" stop-color="#7F3BF5"/>
</linearGradient>
<linearGradient id="paint2_linear_5734_851" x1="83.4827" y1="9.95634" x2="83.4827" y2="26.918" gradientUnits="userSpaceOnUse">
<stop stop-color="#3370FF"/>
<stop offset="1" stop-color="#7F3BF5"/>
</linearGradient>
<linearGradient id="paint3_linear_5734_851" x1="98.4139" y1="9.57325" x2="98.4139" y2="26.5967" gradientUnits="userSpaceOnUse">
<stop stop-color="#3370FF"/>
<stop offset="1" stop-color="#7F3BF5"/>
</linearGradient>
<linearGradient id="paint4_linear_5734_851" x1="114.034" y1="9.05828" x2="114.034" y2="26.3934" gradientUnits="userSpaceOnUse">
<stop stop-color="#3370FF"/>
<stop offset="1" stop-color="#7F3BF5"/>
</linearGradient>
<linearGradient id="paint5_linear_5734_851" x1="15.1255" y1="26.7354" x2="15.1255" y2="29.6542" gradientUnits="userSpaceOnUse">
<stop stop-color="#3370FF"/>
<stop offset="1" stop-color="#7F3BF5"/>
</linearGradient>
<linearGradient id="paint6_linear_5734_851" x1="29.9319" y1="13.3823" x2="29.9319" y2="19.7499" gradientUnits="userSpaceOnUse">
<stop stop-color="#3370FF"/>
<stop offset="1" stop-color="#7F3BF5"/>
</linearGradient>
<linearGradient id="paint7_linear_5734_851" x1="1.12576" y1="13.3823" x2="1.12576" y2="19.7499" gradientUnits="userSpaceOnUse">
<stop stop-color="#3370FF"/>
<stop offset="1" stop-color="#7F3BF5"/>
</linearGradient>
<linearGradient id="paint8_linear_5734_851" x1="19.0238" y1="14.2251" x2="19.0238" y2="17.7273" gradientUnits="userSpaceOnUse">
<stop stop-color="#3370FF"/>
<stop offset="1" stop-color="#7F3BF5"/>
</linearGradient>
<linearGradient id="paint9_linear_5734_851" x1="12.3012" y1="14.2251" x2="12.3012" y2="17.7273" gradientUnits="userSpaceOnUse">
<stop stop-color="#3370FF"/>
<stop offset="1" stop-color="#7F3BF5"/>
</linearGradient>
<linearGradient id="paint10_linear_5734_851" x1="15.5289" y1="6.91309" x2="15.5289" y2="26.1782" gradientUnits="userSpaceOnUse">
<stop stop-color="#3370FF"/>
<stop offset="1" stop-color="#7F3BF5"/>
</linearGradient>
<clipPath id="clip0_5734_851">
<rect width="121" height="36" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 7.9 KiB

View File

@ -0,0 +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>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -0,0 +1 @@
<svg id="图层_1" data-name="图层 1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 232.4409 232.4409"><defs><style>.cls-1{fill:url(#未命名的渐变_7);}.cls-2{fill:url(#未命名的渐变_7-2);}.cls-3{fill:url(#未命名的渐变_7-3);}.cls-4{fill:url(#未命名的渐变_7-4);}.cls-5{fill:url(#未命名的渐变_7-5);}.cls-6{fill:url(#未命名的渐变_7-6);}</style><linearGradient id="未命名的渐变_7" x1="113.6159" y1="176.9998" x2="113.6159" y2="195.8629" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#3370FF"/><stop offset="1" stop-color="#7f3bf5"/></linearGradient><linearGradient id="未命名的渐变_7-2" x1="209.3027" y1="90.7042" x2="209.3027" y2="131.8553" xlink:href="#未命名的渐变_7"/><linearGradient id="未命名的渐变_7-3" x1="23.1384" y1="90.7042" x2="23.1384" y2="131.8553" xlink:href="#未命名的渐变_7"/><linearGradient id="未命名的渐变_7-4" x1="138.8087" y1="96.1512" x2="138.8087" y2="118.7847" xlink:href="#未命名的渐变_7"/><linearGradient id="未命名的渐变_7-5" x1="95.3622" y1="96.1512" x2="95.3622" y2="118.7847" xlink:href="#未命名的渐变_7"/><linearGradient id="未命名的渐变_7-6" x1="116.2206" y1="48.8968" x2="116.2206" y2="173.4002" xlink:href="#未命名的渐变_7"/></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-2" 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-3" 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-4" 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-5" 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-6" 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>

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

Before

Width:  |  Height:  |  Size: 770 KiB

After

Width:  |  Height:  |  Size: 770 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 647 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 683 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 602 KiB

BIN
ui/src/assets/theme/red.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 626 KiB

View File

@ -23,8 +23,8 @@
</el-icon>
{{ data?.document_name }}
</el-text>
<div class="flex align-center" style="line-height: 32px;">
<AppAvatar class="mr-8" shape="square" :size="18">
<div class="flex align-center" style="line-height: 32px">
<AppAvatar class="mr-8 avatar-blue" shape="square" :size="18">
<img src="@/assets/icon_document.svg" style="width: 58%" alt="" />
</AppAvatar>
@ -52,7 +52,7 @@ const props = defineProps({
}
@media only screen and (max-width: 768px) {
.paragraph-source-card-height {
height: 285px;
}
height: 285px;
}
}
</style>

View File

@ -4,7 +4,7 @@
<slot name="header">
<div class="title flex align-center">
<slot name="icon">
<AppAvatar v-if="showIcon" class="mr-12" shape="square" :size="32">
<AppAvatar v-if="showIcon" class="mr-12 avatar-blue" shape="square" :size="32">
<img src="@/assets/icon_document.svg" style="width: 58%" alt="" />
</AppAvatar>
</slot>

View File

@ -12,7 +12,7 @@
<img src="@/assets/icon_web.svg" style="width: 58%" alt="" />
</AppAvatar>
<AppAvatar v-else class="mr-12" shape="square" :size="32">
<AppAvatar v-else class="mr-12 avatar-blue" shape="square" :size="32">
<img src="@/assets/icon_document.svg" style="width: 58%" alt="" />
</AppAvatar>
</slot>

View File

@ -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 = {
)
])
}
},
}
}

View File

@ -19,6 +19,8 @@ import InfiniteScroll from './infinite-scroll/index.vue'
import AutoTooltip from './auto-tooltip/index.vue'
import MdEditor from './markdown/MdEditor.vue'
import MdPreview from './markdown/MdPreview.vue'
import LogoFull from './logo/LogoFull.vue'
import LogoIcon from './logo/LogoIcon.vue'
export default {
install(app: App) {
@ -42,5 +44,7 @@ export default {
app.component(AutoTooltip.name, AutoTooltip)
app.component(MdPreview.name, MdPreview)
app.component(MdEditor.name, MdEditor)
app.component(LogoFull.name, LogoFull)
app.component(LogoIcon.name, LogoIcon)
}
}

View File

@ -1,7 +1,9 @@
<template>
<div class="login-form-container">
<div class="login-title">
<div class="logo text-center"><img src="@/assets/MaxKB-logo.svg" height="45" /></div>
<div class="logo text-center">
<LogoFull height="45px" />
</div>
<div class="sub-title text-center" v-if="subTitle">
<el-text type="info">{{ subTitle }}</el-text>
</div>
@ -12,7 +14,6 @@
</div>
</template>
<script setup lang="ts">
const defaultTitle = import.meta.env.VITE_APP_TITLE
defineOptions({ name: 'LoginContainer' })
defineProps({
title: String,

View File

@ -2,9 +2,8 @@
<div class="login-warp flex-center">
<div class="login-container w-full h-full">
<el-row class="container w-full h-full">
<!-- v-if="screenWidth && screenWidth >= 990" -->
<el-col :xs="0" :sm="0" :md="10" :lg="10" :xl="10" class="left-container">
<div 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>
@ -14,25 +13,43 @@
</div>
</template>
<script setup lang="ts">
// import { ref, onMounted } from 'vue'
// import type { Ref } from 'vue'
import { computed } from 'vue'
import { getThemeImg } from '@/utils/theme'
import useStore from '@/stores'
defineOptions({ name: 'LoginLayout' })
// const screenWidth: Ref<number | null> = ref(null)
// onMounted(() => {
// screenWidth.value = document.body.clientWidth
// window.onresize = () => {
// return (() => {
// screenWidth.value = document.body.clientWidth
// })()
// }
// })
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>
<style lang="scss" scope>
.login-warp {
height: 100vh;
.login-image {
background: url(@/assets/login.jpg) no-repeat center;
background-repeat: no-repeat;
background-position: center;
background-size: cover;
width: 100%;
height: 100%;

View File

@ -0,0 +1,95 @@
<template>
<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'
import useStore from '@/stores'
defineOptions({ name: 'LogoFull' })
defineProps({
height: {
type: String,
default: '36px'
}
})
const { user } = useStore()
const isDefaultTheme = computed(() => {
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>
.custom-logo-color {
path {
fill: var(--el-color-primary);
}
}
</style>

View File

@ -0,0 +1,59 @@
<template>
<svg
v-if="!isDefaultTheme"
:class="!isDefaultTheme ? 'custom-logo-color' : ''"
:height="height"
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>
<img v-else src="@/assets/logo/logo.svg" :height="height" />
</template>
<script setup lang="ts">
import { ref, computed, onMounted } from 'vue'
import useStore from '@/stores'
defineOptions({ name: 'LogoIcon' })
defineProps({
height: {
type: String,
default: '36px'
}
})
const { user } = useStore()
const isDefaultTheme = computed(() => {
return user.isDefaultTheme()
})
</script>
<style lang="scss" scoped>
.custom-logo-color {
path {
fill: var(--el-color-primary);
}
}
</style>

View File

@ -28,12 +28,6 @@
<div class="value" v-else>{{ item.username }}</div>
</template>
</el-autocomplete>
<!-- <el-input
:validate-event="false"
v-model="currentval"
:placeholder="tagsList.length == 0 ? placeholder : ''"
@keydown.enter="addTags"
/> -->
</div>
</template>
<script setup lang="ts">

View File

@ -0,0 +1,29 @@
<template>
<div class="app-header" :class="!isDefaultTheme ? 'custom-header' : ''">
<TopBar />
</div>
</template>
<script setup lang="ts">
import { ref, computed, onMounted } from 'vue'
import TopBar from '../top-bar/index.vue'
import useStore from '@/stores'
const { user } = useStore()
const isDefaultTheme = computed(() => {
return user.isDefaultTheme()
})
</script>
<style lang="scss" scoped>
.app-header {
background: var(--app-header-bg-color);
position: fixed;
width: 100%;
left: 0;
top: 0;
z-index: 100;
}
.custom-header {
background: var(--el-color-primary-light-9) !important;
}
</style>

View File

@ -36,7 +36,7 @@
>
<img src="@/assets/icon_web.svg" style="width: 58%" alt="" />
</AppAvatar>
<AppAvatar v-else class="mr-8" shape="square" :size="24">
<AppAvatar v-else class="mr-8 avatar-blue" shape="square" :size="24">
<img src="@/assets/icon_document.svg" style="width: 58%" alt="" />
</AppAvatar>
<div class="ellipsis">{{ current?.name }}</div>
@ -228,7 +228,7 @@ onMounted(() => {
border-radius: 4px;
&:hover {
background: var(--el-color-primary-light-9);
color: var(--el-menu-active-color);
color: var(--el-color-primary);
}
}
&__footer {

View File

@ -1,3 +1,4 @@
export { default as Sidebar } from './sidebar/index.vue'
export { default as AppMain } from './app-main/index.vue'
export { default as TopBar } from './top-bar/index.vue'
export { default as AppHeader } from './app-header/index.vue'

View File

@ -50,6 +50,8 @@ const {
params: { id, type }
} = route as any
function showMenu() {
if (isWorkFlow(type)) {
return props.menu.name !== 'AppHitTest'
@ -84,14 +86,14 @@ const menuIcon = computed(() => {
border-radius: 4px;
&:hover {
background: none;
color: var(--el-menu-active-color);
color: var(--el-color-primary);
}
}
:deep(.el-sub-menu__title) {
padding: 13px 12px 13px 16px !important;
&:hover {
background: none;
color: var(--el-menu-active-color);
color: var(--el-color-primary);
}
}
.el-sub-menu {
@ -100,7 +102,7 @@ const menuIcon = computed(() => {
}
}
.el-menu-item.is-active {
color: var(--el-menu-active-color);
color: var(--el-color-primary);
background: var(--el-color-primary-light-9);
}
}

View File

@ -1,16 +1,16 @@
<template>
<el-dialog v-model="aboutDialogVisible" class="about-dialog border-r-4">
<el-dialog
v-model="aboutDialogVisible"
class="about-dialog border-r-4"
:class="!isDefaultTheme ? 'custom-header' : ''"
>
<template #header="{ titleId, titleClass }">
<div class="logo flex-center" :id="titleId" :class="titleClass">
<img src="@/assets/MaxKB-logo.svg" height="59" />
<LogoFull height="59px" />
</div>
</template>
<div class="about-ui">
<el-card
shadow="hover"
class="mb-16"
@click="toUrl('https://maxkb.cn/docs/')"
>
<el-card shadow="hover" class="mb-16" @click="toUrl('https://maxkb.cn/docs/')">
<div class="flex align-center cursor">
<AppIcon iconName="app-reading" class="mr-16 ml-8" style="font-size: 24px"></AppIcon>
<span>{{ $t('layout.topbar.wiki') }}</span>
@ -33,11 +33,12 @@
</el-dialog>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { ref, computed } from 'vue'
import useStore from '@/stores'
const defaultTitle = import.meta.env.VITE_APP_TITLE
const { user } = useStore()
const isDefaultTheme = computed(() => {
return user.isDefaultTheme()
})
const aboutDialogVisible = ref(false)
@ -80,4 +81,10 @@ defineExpose({ open })
}
}
}
.custom-header {
.el-dialog__header {
background: var(--el-color-primary-light-9) !important;
}
}
</style>

View File

@ -12,7 +12,7 @@
<div class="flex-center h-full">
<div class="app-title-container cursor" @click="router.push('/')">
<div class="logo flex-center">
<img src="@/assets/MaxKB-logo.svg" height="35" />
<LogoFull />
</div>
</div>
<TopMenu></TopMenu>

View File

@ -1,36 +1,15 @@
<script setup lang="ts">
import { TopBar, AppMain } from '../components'
import { AppHeader, AppMain } from '../components'
</script>
<template>
<div class="app-layout">
<div class="app-header">
<TopBar />
</div>
<AppHeader />
<div class="app-main">
<AppMain />
</div>
</div>
</template>
<style lang="scss">
.app-layout {
background-color: var(--app-layout-bg-color);
height: 100%;
}
.app-main {
position: relative;
height: 100%;
padding: var(--app-header-height) 0 0 !important;
box-sizing: border-box;
overflow: auto;
}
.app-header {
background: var(--app-header-bg-color);
position: fixed;
width: 100%;
left: 0;
top: 0;
z-index: 100;
}
@import './index.scss';
</style>

View File

@ -12,15 +12,7 @@
<script setup lang="ts">
import { Sidebar, AppMain } from '../components'
</script>
<style lang="scss">
.sidebar-container {
box-sizing: border-box;
transition: width 0.28s;
width: var(--sidebar-width);
min-width: var(--sidebar-width);
background-color: var(--sidebar-bg-color);
}
.view-container {
width: calc(100% - var(--sidebar-width));
}
<style lang="scss" scoped>
@import './index.scss';
</style>

View File

@ -1,8 +1,6 @@
<template>
<div class="app-layout">
<div class="app-header">
<TopBar />
</div>
<AppHeader />
<div class="app-main">
<div class="main-layout h-full flex">
<div class="sidebar-container">
@ -17,37 +15,8 @@
</template>
<script setup lang="ts">
import { TopBar, Sidebar, AppMain } from '../components'
import { AppHeader, Sidebar, AppMain } from '../components'
</script>
<style lang="scss">
.app-layout {
background-color: var(--app-layout-bg-color);
height: 100%;
}
.app-main {
position: relative;
height: 100%;
padding: var(--app-header-height) 0 0 !important;
box-sizing: border-box;
overflow: auto;
}
.app-header {
background: var(--app-header-bg-color);
position: fixed;
width: 100%;
left: 0;
top: 0;
z-index: 100;
}
.sidebar-container {
box-sizing: border-box;
transition: width 0.28s;
width: var(--sidebar-width);
min-width: var(--sidebar-width);
background-color: var(--sidebar-bg-color);
}
.view-container {
width: calc(100% - var(--sidebar-width));
}
<style lang="scss" scoped>
@import './index.scss';
</style>

View File

@ -0,0 +1,23 @@
.app-layout {
background-color: var(--app-layout-bg-color);
height: 100%;
}
.app-main {
position: relative;
height: 100%;
padding: var(--app-header-height) 0 0 !important;
box-sizing: border-box;
overflow: auto;
}
.sidebar-container {
box-sizing: border-box;
transition: width 0.28s;
width: var(--sidebar-width);
min-width: var(--sidebar-width);
background-color: var(--sidebar-bg-color);
}
.view-container {
width: calc(100% - var(--sidebar-width));
}

View File

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

View File

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

View File

@ -1,6 +1,9 @@
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 对话用户
@ -10,6 +13,7 @@ export interface userStateTypes {
accessToken?: string
XPACK_LICENSE_IS_VALID: false
isXPack: false
themeInfo: any
}
const useUserStore = defineStore({
@ -20,9 +24,17 @@ const useUserStore = defineStore({
token: '',
version: '',
XPACK_LICENSE_IS_VALID: false,
isXPack: false
isXPack: false,
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
},
@ -78,6 +90,18 @@ const useUserStore = defineStore({
})
},
async theme() {
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'
}
})
},
async profile() {
return UserApi.profile().then((ok) => {
this.userInfo = ok.data

View File

@ -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 {
@ -390,6 +390,12 @@ h5 {
border-color: #e0d7f0;
}
.blue-tag {
background: #ebf1ff;
color: #3370ff;
border-color: #d6e2ff;
}
/*
card 无边框无阴影 灰色背景
*/
@ -455,6 +461,9 @@ h5 {
.avatar-purple {
background: #7f3bf5;
}
.avatar-blue {
background: #3370ff;
}
.success {
color: var(--el-color-success);
@ -651,3 +660,29 @@ h5 {
border: 1px solid var(--el-color-primary);
}
}
.app-card {
background: #fff;
border-radius: 8px;
box-shadow: 0px 2px 4px 0px rgba(31, 35, 41, 0.12);
}
.app-radio-button-group {
border: 1px solid var(--app-border-color-dark);
border-radius: var(--el-border-radius-base);
.el-radio-button {
padding: 3px;
}
.el-radio-button__inner {
border: none !important;
border-radius: var(--el-border-radius-base) !important;
padding: 5px 8px;
font-weight: 400;
}
.el-radio-button__original-radio:checked + .el-radio-button__inner {
color: var(--el-color-primary);
background: var(--el-color-primary-light-9);
border: none;
box-shadow: none;
}
}

View File

@ -1,6 +1,5 @@
:root {
--el-color-primary: #3370ff;
--el-color-primary-light-9: rgba(51, 112, 255, 0.1);
--el-color-primary: #3370FF;
--el-menu-item-height: 45px;
--el-box-shadow-light: 0px 2px 4px 0px rgba(31, 35, 41, 0.12);
--el-border-color: #dee0e3;

View File

@ -14,8 +14,8 @@
--app-header-height: 56px;
--app-header-padding: 0 20px;
--app-header-bg-color: linear-gradient(90deg, #ebf1ff 24.34%, #e5fbf8 56.18%, #f2ebfe 90.18%);
--app-logo-color: linear-gradient(180deg, #3370ff 0%, #7f3bf5 100%);
--app-avatar-gradient-color: linear-gradient(270deg, #9258f7 0%, #3370ff 100%);
--app-logo-color: linear-gradient(180deg, #3370FF 0%, #7f3bf5 100%);
--app-avatar-gradient-color: linear-gradient(270deg, #9258f7 0%, #3370FF 100%);
/* 计算高度 */
--app-main-height: calc(100vh - var(--app-header-height) - var(--app-view-padding) * 2 - 40px);

View File

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

View File

@ -1,5 +0,0 @@
import type { KeyValueData } from './type'
const keyValueData: KeyValueData = {
'--el-header-padding': '0px'
}
export default keyValueData

View File

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

View File

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

View File

@ -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,
};

44
ui/src/utils/theme.ts Normal file
View File

@ -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 defaultSetting = {
icon: '',
loginLogo: '',
loginImage: '',
title: 'MaxKB',
slogan: '欢迎使用 MaxKB 智能知识库'
}

View File

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

View File

@ -220,7 +220,7 @@
<img src="@/assets/icon_web.svg" style="width: 58%" alt="" />
</AppAvatar>
<AppAvatar v-else class="mr-8" shape="square" :size="32">
<AppAvatar v-else class="mr-8 avatar-blue" shape="square" :size="32">
<img src="@/assets/icon_document.svg" style="width: 58%" alt="" />
</AppAvatar>
<div class="ellipsis">

View File

@ -218,7 +218,7 @@
<img src="@/assets/icon_web.svg" style="width: 58%" alt="" />
</AppAvatar>
<AppAvatar v-else class="mr-8" shape="square" :size="32">
<AppAvatar v-else class="mr-8 avatar-blue" shape="square" :size="32">
<img src="@/assets/icon_document.svg" style="width: 58%" alt="" />
</AppAvatar>
<div class="ellipsis">

View File

@ -64,7 +64,7 @@
</template>
<div class="status-tag">
<el-tag type="warning" v-if="isWorkFlow(item.type)">高级编排</el-tag>
<el-tag v-else>简单配置</el-tag>
<el-tag class="blue-tag" v-else>简单配置</el-tag>
</div>
<template #footer>

View File

@ -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(() => {})

View File

@ -15,7 +15,7 @@
<el-form-item label="知识库类型" required>
<el-card shadow="never" class="mb-8" v-if="detail.type === '0'">
<div class="flex align-center">
<AppAvatar class="mr-8" shape="square" :size="32">
<AppAvatar class="mr-8 avatar-blue" shape="square" :size="32">
<img src="@/assets/icon_document.svg" style="width: 58%" alt="" />
</AppAvatar>
<div>

View File

@ -41,12 +41,12 @@
>
<img src="@/assets/icon_web.svg" style="width: 58%" alt="" />
</AppAvatar>
<AppAvatar v-else class="mr-8" shape="square" :size="32">
<AppAvatar v-else class="mr-8 avatar-blue" shape="square" :size="32">
<img src="@/assets/icon_document.svg" style="width: 58%" alt="" />
</AppAvatar>
</template>
<div class="delete-button">
<el-tag v-if="item.type === '0'">通用型</el-tag>
<el-tag class="blue-tag" v-if="item.type === '0'">通用型</el-tag>
<el-tag class="purple-tag" v-else-if="item.type === '1'" type="warning"
>Web 站点</el-tag
>

View File

@ -18,7 +18,7 @@
<el-card shadow="never" class="mb-16" :class="form.type === '0' ? 'active' : ''">
<el-radio value="0" size="large">
<div class="flex align-center">
<AppAvatar class="mr-8" shape="square" :size="32">
<AppAvatar class="mr-8 avatar-blue" shape="square" :size="32">
<img src="@/assets/icon_document.svg" style="width: 58%" alt="" />
</AppAvatar>
<div>

View File

@ -21,7 +21,12 @@
>
<el-radio :value="item.id" size="large">
<div class="flex align-center">
<AppAvatar v-if="item?.type === '0'" class="mr-8" shape="square" :size="32">
<AppAvatar
v-if="item?.type === '0'"
class="mr-8 avatar-blue"
shape="square"
:size="32"
>
<img src="@/assets/icon_document.svg" style="width: 58%" alt="" />
</AppAvatar>
<AppAvatar

View File

@ -57,7 +57,7 @@
</AppAvatar>
<AppAvatar
v-else-if="!item.dataset_id && item.type === '0'"
class="mr-12"
class="mr-12 avatar-blue"
shape="square"
:size="24"
>

View File

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

View File

@ -28,7 +28,7 @@
</AppAvatar>
<AppAvatar
v-else-if="!item.dataset_id && item.type === '0'"
class="mr-12"
class="mr-12 avatar-blue"
shape="square"
:size="24"
>

View File

@ -22,27 +22,6 @@
>
<el-form-item label="用户名/邮箱" prop="users">
<tags-input v-model:tags="memberForm.users" placeholder="请输入成员的用户名或邮箱" />
<!-- <el-select
ref="SelectRemoteRef"
class="custom-select-multiple"
v-model="memberForm.users"
multiple
filterable
remote
reserve-keyword
placeholder="请输入成员的用户名或邮箱"
no-data-text="用户不存在"
:remote-method="remoteMethod"
:loading="loading"
@change="changeSelectHandle"
>
<el-option
v-for="item in userOptions"
:key="item?.id"
:label="item?.username"
:value="item?.id"
/>
</el-select> -->
</el-form-item>
</el-form>
<template #footer>

View File

@ -19,7 +19,7 @@
shape="square"
:size="24"
/>
<AppAvatar v-else-if="isDataset" class="mr-12" shape="square" :size="24">
<AppAvatar v-else-if="isDataset" class="mr-12 avatar-blue" shape="square" :size="24">
<img src="@/assets/icon_document.svg" style="width: 58%" alt="" />
</AppAvatar>
<auto-tooltip :content="row?.name">

View File

@ -32,7 +32,7 @@
<el-progress
type="circle"
:width="56"
color="#3370ff"
color="#3370FF"
:percentage="progress"
class="percentage"
>

View File

@ -0,0 +1,104 @@
<template>
<div class="login-preview mr-16">
<div class="header">
<div class="tag flex-between">
<div class="flex align-center">
<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">
<LoginContainer :subTitle="data.slogan" class="login-container">
<div class="mask"></div>
<h2 class="mb-24">{{ '普通登录' }}</h2>
<el-form class="login-form">
<div class="mb-24">
<el-form-item>
<el-input size="large" class="input-item" placeholder="请输入用户名"> </el-input>
</el-form-item>
</div>
<div class="mb-24">
<el-form-item>
<el-input
type="password"
size="large"
class="input-item"
placeholder="请输入密码"
show-password
>
</el-input>
</el-form-item>
</div>
</el-form>
<el-button size="large" type="primary" class="w-full">登录</el-button>
<div class="operate-container flex-between mt-12">
<el-button class="forgot-password" link type="primary"> 忘记密码? </el-button>
</div>
</LoginContainer>
</login-layout>
</div>
</template>
<script lang="ts" setup>
import { computed } from 'vue'
const props = defineProps({
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>
<style lang="scss" scoped>
.login-preview {
background: #ffffff;
border-radius: 4px;
transform-origin: center;
.login-container {
transform: translate(0, 0) scale(0.8);
position: relative;
.mask {
position: absolute;
left: 0;
top: 0;
height: 100%;
width: 100%;
z-index: 3;
}
}
.header {
background: #eff0f1;
height: 38px;
border-radius: 4px 4px 0 0;
position: relative;
.tag {
width: 180px;
height: 30px;
background: #ffffff;
box-shadow: var(-app-text-color-light-1);
position: absolute;
bottom: 0;
left: 8px;
border-radius: 4px 4px 0 0;
padding: 0 8px;
}
}
}
</style>

View File

@ -1,5 +1,255 @@
<template>
<LayoutContainer header="外观设置"> </LayoutContainer>
<div class="theme-setting" v-loading="loading">
<h4 class="p-16-24">外观设置</h4>
<el-scrollbar>
<div class="p-24 pt-0">
<div class="app-card p-24">
<h5 class="mb-16">平台显示主题</h5>
<el-radio-group
v-model="themeForm.theme"
class="app-radio-button-group"
@change="changeThemeHandle"
>
<template v-for="(item, index) in themeList" :key="index">
<el-radio-button :label="item.label" :value="item.value" />
</template>
</el-radio-group>
</div>
<div class="app-card p-24 mt-16">
<h5 class="mb-16">平台登陆设置</h5>
<el-card shadow="never" class="layout-bg">
<div class="flex-between">
<h5 class="mb-16">页面预览</h5>
<el-button type="primary" link @click="resetForm"> 恢复默认 </el-button>
</div>
<div class="theme-preview">
<el-row :gutter="8">
<el-col :span="16">
<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-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支持 JPGPNGSVG大小不超过
200KB</el-text
>
</el-card>
<el-card shadow="never" class="mb-8">
<div class="flex-between mb-8">
<span class="lighter">登录 Logo</span>
<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支持 JPGPNGSVG大小不超过
200KB</el-text
>
</el-card>
<el-card shadow="never" class="mb-8">
<div class="flex-between mb-8">
<span class="lighter">登录背景图</span>
<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支持
JPGPNGSVG大小不超过 5M
</el-text>
</el-card>
<el-form
ref="themeFormRef"
:model="themeForm"
label-position="top"
require-asterisk-position="right"
:rules="rules"
@submit.prevent
>
<el-form-item label="网站名称" prop="title">
<el-input v-model="themeForm.title" placeholder="请输入网站名称">
</el-input>
<el-text type="info"> 显示在网页 Tab 的平台名称 </el-text>
</el-form-item>
<el-form-item label="欢迎语" prop="slogan">
<el-input v-model="themeForm.slogan" placeholder="请输入欢迎语"> </el-input>
<el-text type="info"> 产品 Logo 下的 欢迎语 </el-text>
</el-form-item>
</el-form>
</div></el-col
>
</el-row>
</div>
<div class="mt-16">
<el-text type="info">默认为 MaxKB 登录界面支持自定义设置</el-text>
</div>
</el-card>
</div>
</div>
</el-scrollbar>
<div class="theme-setting__operate w-full p-16-24">
<el-button @click="resetTheme">放弃更新</el-button>
<el-button type="primary" @click="updataTheme(themeFormRef)"> 保存并应用 </el-button>
</div>
</div>
</template>
<script setup lang="ts"></script>
<style lang="scss" scoped></style>
<script lang="ts" setup>
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 { themeList, defaultSetting } from '@/utils/theme'
import ThemeApi from '@/api/theme'
import { MsgSuccess, MsgError } from '@/utils/message'
import useStore from '@/stores'
const { user } = useStore()
onBeforeRouteLeave((to, from) => {
user.setTheme(cloneTheme.value)
})
const themeInfo = computed(() => user.themeInfo)
const themeFormRef = ref<FormInstance>()
const loading = ref(false)
const cloneTheme = ref(null)
const themeForm = ref<any>({
theme: '#3370FF',
icon: '',
loginLogo: '',
loginImage: '',
title: 'MaxKB',
slogan: '欢迎使用 MaxKB 智能知识库'
})
const rules = reactive<FormRules>({
title: [{ required: true, message: '请输入网站标题', trigger: 'blur' }],
slogan: [{ required: true, message: '请输入欢迎语', trigger: 'blur' }]
})
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,
...defaultSetting
}
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>
.theme-setting {
height: 100%;
display: flex;
flex-direction: column;
box-sizing: border-box;
position: relative;
padding-bottom: 64px;
&__operate {
position: absolute;
bottom: 0;
right: 0;
left: 0;
background: #ffffff;
text-align: right;
box-sizing: border-box;
box-shadow: 0px -2px 4px 0px rgba(31, 35, 41, 0.08);
}
.theme-preview {
min-width: 1000px;
}
}
</style>

View File

@ -1,7 +1,7 @@
<template>
<div @mousedown="mousedown" class="workflow-node-container p-16" style="overflow: visible">
<div
class="step-container p-16"
class="step-container app-card p-16"
:class="props.nodeModel.isSelected ? 'isSelected' : ''"
style="overflow: visible"
>
@ -161,14 +161,8 @@ function showOperate(type: string) {
<style lang="scss" scoped>
.workflow-node-container {
.step-container {
box-sizing: border-box;
width: 100%;
height: 100%;
overflow: hidden;
background: #fff;
border-radius: 9px;
border: 2px solid #ffffff !important;
box-shadow: 0px 2px 4px 0px rgba(31, 35, 41, 0.12);
box-sizing: border-box;
&:hover {
box-shadow: 0px 6px 24px 0px rgba(31, 35, 41, 0.08);
}

View File

@ -1,5 +1,5 @@
<template>
<AppAvatar shape="square">
<AppAvatar shape="square" class="avatar-blue">
<img src="@/assets/icon_document.svg" style="width: 58%" alt="" />
</AppAvatar>
</template>

View File

@ -39,7 +39,7 @@
<img src="@/assets/icon_web.svg" style="width: 58%" alt="" />
</AppAvatar>
<AppAvatar v-else class="mr-8" shape="square" :size="20">
<AppAvatar v-else class="mr-8 avatar-blue" shape="square" :size="20">
<img src="@/assets/icon_document.svg" style="width: 58%" alt="" />
</AppAvatar>
<div class="ellipsis">