增加暗黑模式

This commit is contained in:
hooray
2022-05-21 01:20:44 +08:00
parent 1046693c3f
commit a1979c84f0
35 changed files with 1374 additions and 729 deletions
+21 -21
View File
@@ -17,50 +17,50 @@
},
"dependencies": {
"@tinymce/tinymce-vue": "^4.0.7",
"@vueuse/core": "^8.3.1",
"@vueuse/integrations": "^8.3.1",
"@vueuse/core": "^8.5.0",
"@vueuse/integrations": "^8.5.0",
"axios": "^0.27.2",
"dayjs": "^1.11.1",
"element-plus": "^2.1.11",
"hotkeys-js": "^3.9.2",
"dayjs": "^1.11.2",
"element-plus": "^2.2.0",
"hotkeys-js": "^3.9.3",
"js-cookie": "^3.0.1",
"mitt": "^3.0.0",
"mockjs": "^1.1.0",
"nprogress": "^0.2.0",
"path-browserify": "^1.0.1",
"path-to-regexp": "^6.2.0",
"pinia": "^2.0.13",
"path-to-regexp": "^6.2.1",
"pinia": "^2.0.14",
"qs": "^6.10.3",
"tinymce": "^5.10.4",
"vue": "^3.2.33",
"vue-router": "^4.0.14"
"vue": "^3.2.34",
"vue-router": "^4.0.15"
},
"devDependencies": {
"@iconify-json/ep": "^1.1.3",
"@iconify/vue": "^3.2.1",
"@vitejs/plugin-vue": "^2.3.1",
"@vitejs/plugin-vue": "^2.3.3",
"@vitejs/plugin-vue-jsx": "^1.3.10",
"@vue/compiler-sfc": "^3.2.33",
"eslint": "^8.14.0",
"eslint-plugin-vue": "^8.7.1",
"esno": "^0.14.1",
"@vue/compiler-sfc": "^3.2.34",
"eslint": "^8.15.0",
"eslint-plugin-vue": "^9.0.1",
"esno": "^0.16.3",
"fs-extra": "^10.1.0",
"http-server": "^14.1.0",
"husky": "^7.0.4",
"husky": "^8.0.1",
"lint-staged": "^12.4.1",
"plop": "^3.1.0",
"postcss-html": "^1.4.1",
"postcss-scss": "^4.0.4",
"sass": "^1.51.0",
"stylelint": "^14.8.1",
"sass": "^1.52.0",
"stylelint": "^14.8.2",
"stylelint-config-recommended-scss": "^6.0.0",
"stylelint-config-recommended-vue": "^1.4.0",
"stylelint-config-standard": "^25.0.0",
"stylelint-scss": "^4.2.0",
"svgo": "^2.8.0",
"unplugin-auto-import": "^0.7.1",
"unplugin-vue-components": "^0.19.3",
"vite": "^2.9.6",
"unplugin-auto-import": "^0.7.2",
"unplugin-vue-components": "^0.19.5",
"vite": "^2.9.9",
"vite-plugin-banner": "^0.2.0",
"vite-plugin-compression": "^0.5.1",
"vite-plugin-html": "^3.2.0",
@@ -71,6 +71,6 @@
"vite-plugin-svg-icons": "^2.0.1",
"vite-plugin-vue-layouts": "^0.6.0",
"vite-plugin-vue-setup-extend": "^0.4.0",
"vue-eslint-parser": "^8.3.0"
"vue-eslint-parser": "^9.0.2"
}
}
+612 -356
View File
File diff suppressed because it is too large Load Diff
+11 -1
View File
@@ -24,6 +24,16 @@ const subSidebarActualWidth = computed(() => {
return `${actualWidth}px`
})
watch(() => settingsStore.app.colorScheme, () => {
if (settingsStore.app.colorScheme === 'dark') {
document.documentElement.classList.add('dark')
} else {
document.documentElement.classList.remove('dark')
}
}, {
immediate: true
})
watch(() => settingsStore.mode, () => {
if (settingsStore.mode === 'pc') {
settingsStore.$patch(state => {
@@ -68,7 +78,7 @@ onMounted(() => {
</script>
<template>
<el-config-provider :locale="zhCn" :size="settingsStore.app.elementSize">
<el-config-provider :locale="zhCn" :size="settingsStore.app.elementSize" :button="{autoInsertSpace: true}">
<RouterView
:style="{
'--g-main-sidebar-actual-width': mainSidebarActualWidth,
Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

@@ -0,0 +1,9 @@
// 暗黑模式
@forward "element-plus/theme-chalk/src/dark/var.scss" with (
$bg-color: (
"page": #0a0a0a,
"": #141414,
"overlay": #1d1e1f,
)
);
@use "element-plus/theme-chalk/src/dark/css-vars.scss" as *;
@@ -0,0 +1,8 @@
// @forward "element-plus/theme-chalk/src/common/var.scss" with (
// $colors: (
// "primary": (
// "base": green
// )
// )
// );
@use "element-plus/theme-chalk/src/index.scss" as *;
+6 -7
View File
@@ -1,13 +1,16 @@
@use "./element.customized.scss" as *;
@use "./element.customized.dark.scss" as *;
// 全局样式
::-webkit-scrollbar {
width: 13px;
height: 13px;
width: 12px;
height: 12px;
}
::-webkit-scrollbar-thumb {
background-color: rgb(0 0 0 / 40%);
background-clip: padding-box;
border: 3px solid transparent;
border-radius: 7px;
border-radius: 6px;
}
::-webkit-scrollbar-thumb:hover {
background-color: rgb(0 0 0 / 50%);
@@ -15,16 +18,12 @@
::-webkit-scrollbar-track {
background-color: transparent;
}
::-webkit-scrollbar-track:hover {
background-color: #f8fafc;
}
html,
body {
height: 100%;
}
body {
margin: 0;
color: #333;
box-sizing: border-box;
font-family: Lato, "PingFang SC", "Microsoft YaHei", sans-serif;
-webkit-tap-highlight-color: transparent;
+4 -4
View File
@@ -7,7 +7,7 @@
left: 0;
width: 100%;
height: 2px;
background: $g-nprogress-color;
background: var(--g-nprogress-color);
}
.peg {
display: block;
@@ -17,7 +17,7 @@
height: 100%;
opacity: 1;
transform: rotate(3deg) translate(0, -4px);
box-shadow: 0 0 10px $g-nprogress-color, 0 0 5px $g-nprogress-color;
box-shadow: 0 0 10px var(--g-nprogress-color), 0 0 5px var(--g-nprogress-color);
}
.spinner {
display: block;
@@ -32,8 +32,8 @@
border: solid 2px transparent;
border-radius: 50%;
animation: nprogress-spinner 400ms linear infinite;
border-top-color: $g-nprogress-color;
border-left-color: $g-nprogress-color;
border-top-color: var(--g-nprogress-color);
border-left-color: var(--g-nprogress-color);
}
}
}
+120 -48
View File
@@ -1,52 +1,124 @@
/* ----- 应用 ----- */
// 主区域背景色
$g-main-bg: #f0f2f5;
:root {
--g-box-shadow-color: rgb(0 0 0 / 12%);
/* ----- 顶部 ----- */
// 背景色
$g-header-bg: #222b45;
// 文字颜色
$g-header-color: #fff;
// 导航文字颜色
$g-header-menu-color: #ccc;
// 导航鼠标悬浮文字颜色
$g-header-menu-hover-color: #fff;
// 导航鼠标悬浮背景色
$g-header-menu-hover-bg: #334067;
// 导航选中文字颜色
$g-header-menu-active-color: #fff;
// 导航选中背景色
$g-header-menu-active-bg: #334067;
/* ----- 应用 ----- */
// 应用背景色
--g-app-bg: var(--el-bg-color);
// 主区域背景色
--g-main-bg: var(--el-bg-color-page);
/* ----- 主导航 ----- */
// 背景色
$g-main-sidebar-bg: #222b45;
// 菜单文字颜色
$g-main-sidebar-menu-color: #ccc;
// 菜单鼠标悬浮文字颜色
$g-main-sidebar-menu-hover-color: #fff;
// 菜单鼠标悬浮背景色
$g-main-sidebar-menu-hover-bg: #334067;
// 菜单选中文字颜色
$g-main-sidebar-menu-active-color: #fff;
// 菜单选中背景色
$g-main-sidebar-menu-active-bg: #334067;
/* ----- 顶部 ----- */
// 背景色
--g-header-bg: #222b45;
// 文字颜色
--g-header-color: #fff;
// 导航文字颜色
--g-header-menu-color: #fff;
// 导航鼠标悬浮文字颜色
--g-header-menu-hover-color: #fff;
// 导航鼠标悬浮背景色
--g-header-menu-hover-bg: #334067;
// 导航选中文字颜色
--g-header-menu-active-color: #fff;
// 导航选中背景色
--g-header-menu-active-bg: #334067;
/* ----- 次导航 ----- */
// 背景色
$g-sub-sidebar-bg: #fafafa;
// 菜单背景色
$g-sub-sidebar-menu-bg: darken(#fafafa, 7);
// 菜单文字颜色
$g-sub-sidebar-menu-color: #37414b;
// 菜单鼠标悬浮文字颜色
$g-sub-sidebar-menu-hover-color: #37414b;
// 菜单鼠标悬浮背景色
$g-sub-sidebar-menu-hover-bg: darken(#fafafa, 10);
// 菜单选中文字颜色
$g-sub-sidebar-menu-active-color: #e7f4ff;
// 菜单选中背景色
$g-sub-sidebar-menu-active-bg: #5482ee;
/* ----- 主导航 ----- */
// 背景色
--g-main-sidebar-bg: #222b45;
// 菜单文字颜色
--g-main-sidebar-menu-color: #eee;
// 菜单鼠标悬浮文字颜色
--g-main-sidebar-menu-hover-color: #fff;
// 菜单鼠标悬浮背景色
--g-main-sidebar-menu-hover-bg: #334067;
// 菜单选中文字颜色
--g-main-sidebar-menu-active-color: #fff;
// 菜单选中背景色
--g-main-sidebar-menu-active-bg: #334067;
/* ----- 进度条 ----- */
$g-nprogress-color: #29d;
/* ----- 次导航 ----- */
// 背景色
--g-sub-sidebar-bg: #fff;
// 菜单背景色
--g-sub-sidebar-menu-bg: #e8e8e8;
// 菜单文字颜色
--g-sub-sidebar-menu-color: var(--el-text-color-primary);
// 菜单鼠标悬浮文字颜色
--g-sub-sidebar-menu-hover-color: var(--el-text-color-primary);
// 菜单鼠标悬浮背景色
--g-sub-sidebar-menu-hover-bg: #e1e1e1;
// 菜单选中文字颜色
--g-sub-sidebar-menu-active-color: #fff;
// 菜单选中背景色
--g-sub-sidebar-menu-active-bg: var(--el-color-primary);
/* ----- 顶栏 ----- */
--g-toolbar-bg: var(--el-bg-color);
/* ----- 进度条 ----- */
--g-nprogress-color: var(--el-color-primary);
/* ----- 暗黑模式 ----- */
&.dark {
--g-box-shadow-color: rgb(0 0 0 / 72%);
/* ----- 应用 ----- */
// 应用背景色
--g-app-bg: var(--el-bg-color);
// 主区域背景色
--g-main-bg: var(--el-bg-color-page);
/* ----- 顶部 ----- */
// 背景色
--g-header-bg: var(--el-bg-color);
// 文字颜色
--g-header-color: #fff;
// 导航文字颜色
--g-header-menu-color: #ccc;
// 导航鼠标悬浮文字颜色
--g-header-menu-hover-color: #fff;
// 导航鼠标悬浮背景色
--g-header-menu-hover-bg: var(--el-color-primary-light-5);
// 导航选中文字颜色
--g-header-menu-active-color: #fff;
// 导航选中背景色
--g-header-menu-active-bg: var(--el-color-primary-light-3);
/* ----- 主导航 ----- */
// 背景色
--g-main-sidebar-bg: var(--el-bg-color);
// 菜单文字颜色
--g-main-sidebar-menu-color: #ccc;
// 菜单鼠标悬浮文字颜色
--g-main-sidebar-menu-hover-color: #fff;
// 菜单鼠标悬浮背景色
--g-main-sidebar-menu-hover-bg: var(--el-color-primary-light-5);
// 菜单选中文字颜色
--g-main-sidebar-menu-active-color: #fff;
// 菜单选中背景色
--g-main-sidebar-menu-active-bg: var(--el-color-primary-light-3);
/* ----- 次导航 ----- */
// 背景色
--g-sub-sidebar-bg: var(--el-bg-color-page);
// 菜单背景色
--g-sub-sidebar-menu-bg: var(--el-bg-color);
// 菜单文字颜色
--g-sub-sidebar-menu-color: var(--el-text-color-secondary);
// 菜单鼠标悬浮文字颜色
--g-sub-sidebar-menu-hover-color: var(--el-text-color-primary);
// 菜单鼠标悬浮背景色
--g-sub-sidebar-menu-hover-bg: var(--el-color-primary-light-5);
// 菜单选中文字颜色
--g-sub-sidebar-menu-active-color: var(--el-text-color-primary);
// 菜单选中背景色
--g-sub-sidebar-menu-active-bg: var(--el-color-primary-light-3);
/* ----- 顶栏 ----- */
--g-toolbar-bg: var(--el-bg-color);
/* ----- 进度条 ----- */
--g-nprogress-color: var(--el-color-primary);
}
}
+4 -3
View File
@@ -17,13 +17,14 @@ footer {
margin: 40px 0 20px;
line-height: 20px;
text-align: center;
color: #808695;
color: var(--el-text-color-secondary);
font-size: 14px;
a {
text-decoration: none;
color: #808695;
color: var(--el-text-color-secondary);
transition: var(--el-transition-color);
&:hover {
color: #3d4047;
color: var(--el-text-color-primary);
}
}
}
+9 -8
View File
@@ -90,16 +90,17 @@ function onSuccess(res, file) {
</template>
<style lang="scss" scoped>
:deep(.el-upload-dragger) {
width: auto;
height: auto;
overflow: unset;
:deep(.el-upload) {
display: inline-block;
.el-upload-dragger {
padding: 0;
}
&.is-dragover {
border-width: 1px;
}
}
.slot {
width: 300px;
height: 160px;
.slot {
width: 300px;
padding: 40px 0;
}
}
</style>
+4 -4
View File
@@ -40,11 +40,11 @@ function onScroll() {
bottom: 0;
padding: 20px;
text-align: center;
background-color: #fff;
transition: all 0.3s, box-shadow 0.5s;
box-shadow: 0 0 1px 0 #ccc;
background-color: var(--g-app-bg);
box-shadow: 0 0 1px 0 var(--g-box-shadow-color);
transition: all 0.3s, var(--el-transition-box-shadow);
&.shadow {
box-shadow: 0 -10px 10px -10px #ccc;
box-shadow: 0 -10px 10px -10px var(--g-box-shadow-color);
}
}
:deep(.el-form-item) {
+3 -2
View File
@@ -38,8 +38,9 @@ const realHeight = computed(() => {
<style lang="scss" scoped>
.el-image {
border-radius: 5px;
background-color: #ebeef5;
box-shadow: 0 0 5px 1px #ccc;
background-color: var(--el-fill-color);
box-shadow: var(--el-box-shadow-light);
transition: background-color 0.3s, var(--el-transition-box-shadow);
:deep(.el-image__inner) {
transition: all 0.3s;
cursor: pointer;
+30 -27
View File
@@ -106,10 +106,11 @@ function onSuccess(res) {
:on-progress="onProgress"
:on-success="onSuccess"
drag
class="image-upload"
>
<el-image v-if="url === ''" :src="url === '' ? placeholder : url" :style="`width:${width}px;height:${height}px;`" fit="fill">
<template #error>
<div class="image-slot">
<div class="image-slot" :style="`width:${width}px;height:${height}px;`">
<el-icon>
<svg-icon name="ep:plus" />
</el-icon>
@@ -151,16 +152,21 @@ function onSuccess(res) {
.upload-container {
line-height: initial;
}
.el-image {
display: block;
}
.image {
position: relative;
border-radius: 6px;
overflow: hidden;
.mask {
opacity: 0;
position: absolute;
top: 0;
width: 100%;
height: 100%;
background-color: rgb(0 0 0 / 50%);
transition: all 0.3s;
background-color: var(--el-overlay-color-lighter);
transition: opacity 0.3s;
.actions {
width: 100px;
height: 100px;
@@ -172,14 +178,10 @@ function onSuccess(res) {
span {
width: 50%;
text-align: center;
color: #fff;
cursor: pointer;
transition: all 0.1s;
&.disabled {
color: #999;
cursor: not-allowed;
}
&:hover:not(.disabled) {
color: var(--el-color-white);
transition: color 0.1s, transform 0.1s;
&:hover {
transform: scale(1.5);
}
.el-icon {
@@ -192,26 +194,27 @@ function onSuccess(res) {
opacity: 1;
}
}
.image-upload {
display: inline-block;
vertical-align: top;
}
:deep(.el-upload) {
.el-upload-dragger {
width: auto;
height: auto;
display: inline-block;
padding: 0;
&.is-dragover {
border-width: 1px;
}
.el-image {
display: block;
.image-slot {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
color: #909399;
background-color: transparent;
i {
font-size: 30px;
}
.image-slot {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
color: var(--el-text-color-placeholder);
background-color: transparent;
i {
font-size: 30px;
}
}
.progress {
@@ -224,13 +227,13 @@ function onSuccess(res) {
height: 100%;
left: 0;
top: 0;
background-color: rgb(0 0 0 / 20%);
background-color: var(--el-overlay-color-lighter);
}
.el-progress {
z-index: 1;
@include position-center(xy);
.el-progress__text {
color: #fff;
color: var(--el-text-color-placeholder);
}
}
}
+14 -14
View File
@@ -178,24 +178,24 @@ function onSuccess(res) {
.upload-container {
line-height: initial;
}
.el-image {
display: block;
}
.images {
position: relative;
display: inline-block;
margin-right: 10px;
border: 1px dashed #d9d9d9;
border: 1px dashed var(--el-border-color);
border-radius: 6px;
overflow: hidden;
.el-image {
display: block;
}
.mask {
opacity: 0;
position: absolute;
top: 0;
width: 100%;
height: 100%;
background-color: rgb(0 0 0 / 50%);
transition: all 0.3s;
background-color: var(--el-overlay-color-lighter);
transition: opacity 0.3s;
.actions {
width: 100px;
height: 100px;
@@ -207,11 +207,11 @@ function onSuccess(res) {
span {
width: 50%;
text-align: center;
color: #fff;
cursor: pointer;
transition: all 0.1s;
color: var(--el-color-white);
transition: color 0.1s, transform 0.1s;
&.disabled {
color: #999;
color: var(--el-text-color-disabled);
cursor: not-allowed;
}
&:hover:not(.disabled) {
@@ -233,8 +233,8 @@ function onSuccess(res) {
}
:deep(.el-upload) {
.el-upload-dragger {
width: auto;
height: auto;
display: inline-block;
padding: 0;
&.is-dragover {
border-width: 1px;
}
@@ -244,7 +244,7 @@ function onSuccess(res) {
align-items: center;
width: 100%;
height: 100%;
color: #909399;
color: var(--el-text-color-placeholder);
background-color: transparent;
i {
font-size: 30px;
@@ -260,13 +260,13 @@ function onSuccess(res) {
height: 100%;
left: 0;
top: 0;
background-color: rgb(0 0 0 / 20%);
background-color: var(--el-overlay-color-lighter);
}
.el-progress {
z-index: 1;
@include position-center(xy);
.el-progress__text {
color: #fff;
color: var(--el-text-color-placeholder);
}
}
}
+7 -4
View File
@@ -32,18 +32,21 @@ defineProps({
justify-content: space-between;
margin-bottom: 20px;
padding: 16px 20px;
background: #fff;
border-bottom: 1px solid #e8eaec;
background-color: var(--g-app-bg);
transition: background-color 0.3s;
.main {
flex: 1;
margin-right: 20px;
.title {
font-size: 22px;
color: var(--el-text-color-primary);
transition: var(--el-transition-color);
}
.content {
color: #909399;
font-size: 14px;
margin-top: 10px;
font-size: 14px;
color: var(--el-text-color-secondary);
transition: var(--el-transition-color);
&:empty {
display: none;
}
+9 -7
View File
@@ -45,7 +45,8 @@ function unCollaspe() {
position: relative;
margin: 20px;
padding: 20px;
background-color: #fff;
background-color: var(--g-app-bg);
transition: background-color 0.3s;
&.is-collaspe {
overflow: hidden;
.collaspe {
@@ -56,13 +57,13 @@ function unCollaspe() {
padding: 40px 0 10px;
text-align: center;
font-size: 24px;
color: #ccc;
text-shadow: 0 0 1px #fff;
background: linear-gradient(to bottom, transparent, #fff);
color: var(--el-text-color-primary);
text-shadow: 0 0 1px var(--el-text-color-primary);
background: linear-gradient(to bottom, transparent, var(--g-app-bg));
transition: background 0.3s, var(--el-transition-color);
cursor: pointer;
transition: 0.3s;
&:hover {
color: #333;
color: var(--el-text-color-secondary);
}
}
}
@@ -72,7 +73,8 @@ function unCollaspe() {
margin-left: -20px;
margin-top: -20px;
margin-bottom: 20px;
border-bottom: 1px solid #eee;
border-bottom: 1px solid var(--el-border-color-lighter);
transition: var(--el-transition-border);
}
}
</style>
+6 -2
View File
@@ -28,7 +28,7 @@ function toggle() {
<div class="search-container">
<slot />
<div v-if="showMore" class="more">
<el-button type="text" size="small" @click="toggle">
<el-button text size="small" @click="toggle">
<template #icon>
<el-icon>
<svg-icon :name="isUnfold ? 'ep:caret-top' : 'ep:caret-bottom'" />
@@ -45,10 +45,14 @@ function toggle() {
position: relative;
margin: 20px 0;
padding: 20px;
background-color: #f7f8fa;
background-color: var(--el-fill-color);
transition: background-color 0.3s;
&:first-child {
margin-top: 0;
}
&:last-child {
margin-bottom: 0;
}
:deep(.el-form) {
margin-bottom: -10px;
.el-select {
+11 -5
View File
@@ -38,22 +38,28 @@ const isUp = computed(() => {
<span class="text">{{ value }}</span>
<span v-if="suffix" class="suffix">{{ suffix }}</span>
<el-icon>
<svg-icon :name="`${isUp ? 'ep:caret-top' : 'ep:caret-bottom'}`" />
<svg-icon name="ep:caret-top" />
</el-icon>
</div>
</template>
<style lang="scss" scoped>
.trend {
display: inline-block;
display: flex;
align-items: center;
transition: var(--el-transition-color);
&.up {
color: #67c23a;
color: var(--el-color-success);
}
&.down {
color: #f56c6c;
color: var(--el-color-danger);
.el-icon {
transform: rotate(180deg);
}
}
i {
.el-icon {
margin-left: 5px;
transition: transform 0.3s;
}
}
</style>
+127 -44
View File
@@ -45,10 +45,20 @@ function handleCopy() {
<el-drawer v-model="isShow" title="应用配置" direction="rtl" :size="350" custom-class="flex-container">
<div class="container">
<el-alert title="应用配置可实时预览效果,但仅是临时生效,要想真正作用于项目,可以点击下方的“复制配置”按钮,将配置粘贴到 src/settings.custom.json 中即可,或者也可在 src/settings.js 中直接修改默认配置。同时建议在生产环境隐藏应用配置功能。" type="error" :closable="false" />
<el-divider>颜色主题</el-divider>
<div class="color-scheme">
<div class="switch" :class="settings.app.colorScheme" @click="settings.app.colorScheme = settings.app.colorScheme === 'dark' ? 'light' : 'dark'">
<el-icon class="icon">
<svg-icon v-show="settings.app.colorScheme === 'light'" name="ep:sunny" />
<svg-icon v-show="settings.app.colorScheme === 'dark'" name="ep:moon" />
</el-icon>
</div>
</div>
<el-divider v-if="settingsStore.mode === 'pc'">导航栏模式</el-divider>
<div v-if="settingsStore.mode === 'pc'" class="menu-mode">
<el-tooltip content="侧边栏模式(含主导航)" placement="top" :show-after="500">
<div class="mode mode-side" :class="{'active': settings.menu.menuMode === 'side'}" @click="settings.menu.menuMode = 'side'">
<div class="mode-container" />
<el-icon>
<svg-icon name="ep:check" />
</el-icon>
@@ -56,6 +66,7 @@ function handleCopy() {
</el-tooltip>
<el-tooltip content="顶部模式" placement="top" :show-after="500">
<div class="mode mode-head" :class="{'active': settings.menu.menuMode === 'head'}" @click="settings.menu.menuMode = 'head'">
<div class="mode-container" />
<el-icon>
<svg-icon name="ep:check" />
</el-icon>
@@ -63,6 +74,7 @@ function handleCopy() {
</el-tooltip>
<el-tooltip content="侧边栏模式(不含主导航)" placement="top" :show-after="500">
<div class="mode mode-single" :class="{'active': settings.menu.menuMode === 'single'}" @click="settings.menu.menuMode = 'single'">
<div class="mode-container" />
<el-icon>
<svg-icon name="ep:check" />
</el-icon>
@@ -142,6 +154,17 @@ function handleCopy() {
</div>
<el-switch v-model="settings.topbar.enablePageReload" />
</div>
<div class="setting-item">
<div class="label">
颜色主题
<el-tooltip content="开启后可在明亮/暗黑模式中切换" placement="top">
<el-icon>
<svg-icon name="ep:question-filled" />
</el-icon>
</el-tooltip>
</div>
<el-switch v-model="settings.topbar.enableColorScheme" />
</div>
<el-divider>底部版权</el-divider>
<div class="setting-item">
<div class="label">是否启用</div>
@@ -242,7 +265,7 @@ function handleCopy() {
:deep(.el-drawer__header) {
margin-bottom: initial;
padding-bottom: 20px;
border-bottom: 1px solid #ddd;
border-bottom: 1px solid var(--el-border-color);
}
.flex-container {
.container {
@@ -253,8 +276,7 @@ function handleCopy() {
.action-buttons {
padding: 15px;
text-align: center;
background-color: #fff;
border-top: 1px solid #ddd;
border-top: 1px solid var(--el-border-color);
.el-button {
width: 100%;
}
@@ -263,91 +285,152 @@ function handleCopy() {
:deep(.el-divider) {
margin: 36px 0 24px;
}
.menu-mode {
.color-scheme {
display: flex;
align-items: center;
justify-content: space-around;
justify-content: center;
padding-bottom: 10px;
$width: 50px;
.switch {
width: $width;
height: 30px;
border-radius: 15px;
cursor: pointer;
background-color: var(--el-fill-color-darker);
transition: background-color 0.3s;
&.dark {
.icon {
margin-left: calc($width - 24px - 3px);
}
}
.icon {
margin: 3px;
padding: 5px;
font-size: 24px;
border-radius: 50%;
background-color: var(--el-fill-color-lighter);
transition: margin-left 0.3s, background-color 0.3s;
}
}
}
.menu-mode {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: center;
padding-bottom: 10px;
.mode {
position: relative;
width: 50px;
height: 40px;
border-radius: 4px;
width: 80px;
height: 55px;
margin: 10px;
border-radius: 5px;
overflow: hidden;
cursor: pointer;
background-color: #e1e3e6;
box-shadow: 0 0 15px 1px #aaa;
transition: 0.3s;
background-color: var(--g-app-bg);
box-shadow: 0 0 5px 1px var(--el-border-color-lighter);
transition: 0.2s;
&:hover {
box-shadow: 0 0 15px 1px #666;
box-shadow: 0 0 5px 1px var(--el-border-color-darker);
}
&.active {
box-shadow: 0 0 0 2px #409eff;
box-shadow: 0 0 0 2px var(--el-color-primary);
}
&::before,
&::after {
&::after,
.mode-container {
pointer-events: none;
position: absolute;
border-radius: 3px;
}
.mode-container::before {
content: "";
position: absolute;
width: 100%;
height: 100%;
background-color: var(--g-sub-sidebar-menu-active-bg);
opacity: 0.2;
}
&-side {
&::before {
content: "";
position: absolute;
top: 0;
left: 0;
top: 5px;
left: 5px;
bottom: 5px;
width: 10px;
height: 100%;
background-color: #222b45;
background-color: var(--g-sub-sidebar-menu-active-bg);
}
&::after {
content: "";
position: absolute;
top: 0;
left: 10px;
top: 5px;
left: 20px;
bottom: 5px;
width: 15px;
height: 100%;
background-color: #fff;
background-color: var(--g-sub-sidebar-menu-active-bg);
opacity: 0.5;
}
.mode-container {
top: 5px;
left: 40px;
right: 5px;
bottom: 5px;
border: 1px dashed var(--g-sub-sidebar-menu-active-bg);
}
}
&-head {
&::before {
content: "";
position: absolute;
top: 0;
left: 0;
width: 100%;
top: 5px;
left: 5px;
right: 5px;
height: 10px;
background-color: #222b45;
background-color: var(--g-sub-sidebar-menu-active-bg);
}
&::after {
content: "";
position: absolute;
top: 10px;
left: 0;
top: 20px;
left: 5px;
bottom: 5px;
width: 15px;
height: calc(100% - 10px);
background-color: #fff;
background-color: var(--g-sub-sidebar-menu-active-bg);
opacity: 0.5;
}
.mode-container {
top: 20px;
left: 25px;
right: 5px;
bottom: 5px;
border: 1px dashed var(--g-sub-sidebar-menu-active-bg);
}
}
&-single {
&::before {
content: "";
position: absolute;
top: 0;
left: 0;
top: 5px;
left: 5px;
bottom: 5px;
width: 15px;
height: 100%;
background-color: #fff;
background-color: var(--g-sub-sidebar-menu-active-bg);
opacity: 0.5;
}
.mode-container {
top: 5px;
left: 25px;
right: 5px;
bottom: 5px;
border: 1px dashed var(--g-sub-sidebar-menu-active-bg);
}
}
i {
position: absolute;
right: 2px;
bottom: 2px;
right: 10px;
bottom: 10px;
display: none;
}
&.active i {
display: block;
color: #409eff;
color: var(--el-color-primary);
}
}
}
@@ -360,17 +443,17 @@ function handleCopy() {
border-radius: 5px;
transition: all 0.3s;
&:hover {
background: #f1f1f1;
background: var(--el-fill-color);
}
.label {
font-size: 14px;
color: #666;
color: var(--el-text-color-regular);
display: flex;
align-items: center;
i {
margin-left: 4px;
font-size: 17px;
color: #e6a23c;
color: var(--el-color-warning);
cursor: help;
}
}
+15 -11
View File
@@ -52,8 +52,9 @@ header {
align-items: center;
padding: 0 20px;
height: $g-header-height;
color: $g-header-color;
background-color: $g-header-bg;
color: var(--g-header-color);
background-color: var(--g-header-bg);
transition: background-color 0.3s, var(--el-transition-color);
.header-container {
width: $g-header-width;
height: 100%;
@@ -85,7 +86,7 @@ header {
span {
font-size: 24px;
letter-spacing: 1px;
color: $g-header-color;
color: var(--g-header-color);
}
}
.nav {
@@ -100,16 +101,16 @@ header {
padding: 0 5px;
width: 80px;
cursor: pointer;
transition: all 0.3s;
background-color: $g-header-bg;
color: $g-header-menu-color;
color: var(--g-header-menu-color);
background-color: var(--g-header-bg);
transition: background-color 0.3s, var(--el-transition-color);
&:hover {
color: $g-header-menu-hover-color;
background-color: $g-header-menu-hover-bg;
color: var(--g-header-menu-hover-color);
background-color: var(--g-header-menu-hover-bg);
}
&.active {
color: $g-header-menu-active-color;
background-color: $g-header-menu-active-bg;
color: var(--g-header-menu-active-color);
background-color: var(--g-header-menu-active-bg);
}
.el-icon {
font-size: 24px;
@@ -125,9 +126,12 @@ header {
}
:deep(.tools) {
padding: 0;
.buttons .item .el-icon {
color: var(--g-header-color);
}
.user-container {
font-size: 16px;
color: $g-header-color;
color: var(--g-header-color);
}
}
}
+11 -8
View File
@@ -55,11 +55,12 @@ const switchMenu = inject('switchMenu')
position: relative;
z-index: 1;
width: var(--g-main-sidebar-width);
color: $g-main-sidebar-menu-color;
background-color: $g-main-sidebar-bg;
color: var(--g-main-sidebar-menu-color);
background-color: var(--g-main-sidebar-bg);
transition: background-color 0.3s, var(--el-transition-color);
.sidebar-logo {
transition: 0.3s;
background-color: $g-main-sidebar-bg;
background-color: var(--g-main-sidebar-bg);
}
.nav {
width: inherit;
@@ -73,14 +74,16 @@ const switchMenu = inject('switchMenu')
height: 60px;
padding: 0 5px;
cursor: pointer;
transition: all 0.3s;
color: var(--g-main-sidebar-menu-color);
background-color: var(--g-main-sidebar-bg);
transition: background-color 0.3s, var(--el-transition-color);
&:hover {
color: $g-main-sidebar-menu-hover-color;
background-color: $g-main-sidebar-menu-hover-bg;
color: var(--g-main-sidebar-menu-hover-color);
background-color: var(--g-main-sidebar-menu-hover-bg);
}
&.active {
color: $g-main-sidebar-menu-active-color;
background-color: $g-main-sidebar-menu-active-bg;
color: var(--g-main-sidebar-menu-active-color);
background-color: var(--g-main-sidebar-menu-active-bg);
}
.el-icon {
margin: 0 auto;
+16 -15
View File
@@ -73,7 +73,7 @@ onMounted(() => {
proxy.$hotkeys('esc', e => {
if (settingsStore.topbar.enableNavSearch) {
e.preventDefault()
proxy.$eventBus.emit('global-search-toggle')
isShow.value = false
}
})
if (settingsStore.app.routeBaseOn !== 'filesystem') {
@@ -257,7 +257,7 @@ function handleScroll() {
left: 0;
width: 100%;
height: 100%;
background-color: rgb(0 0 0 / 50%);
background-color: var(--el-overlay-color-lighter);
backdrop-filter: blur(10px);
transition: all 0.2s;
transform: translateZ(0);
@@ -306,9 +306,9 @@ function handleScroll() {
border-radius: 5px;
font-size: 12px;
font-weight: bold;
color: #333;
background-color: rgb(255 255 255 / 80%);
box-shadow: inset 0 -2px #505e5f, inset 0 0 1px 1px #dec6c6, 0 1px 2px 1px #acb0c166;
color: var(--el-text-color-primary);
background-color: var(--el-fill-color);
box-shadow: inset 0 -2px var(--el-fill-color-lighter), inset 0 0 1px 1px var(--el-fill-color-darker);
}
}
}
@@ -318,7 +318,8 @@ function handleScroll() {
max-height: calc(100% - 250px);
border-radius: 5px;
overflow: auto;
background-color: #fff;
background-color: var(--el-bg-color);
box-shadow: 0 0 0 1px var(--el-border-color-darker);
&.mobile {
max-height: calc(100% - 200px);
}
@@ -329,26 +330,26 @@ function handleScroll() {
cursor: pointer;
transition: all 0.3s;
&.actived {
background-color: #dbe7f8;
background-color: var(--el-bg-color-page);
.icon {
color: #409eff;
color: var(--el-color-primary);
transform: scale(1.2);
}
.info {
border-left-color: #b7cafc;
border-left-color: var(--el-border-color);
.title {
color: #333;
color: var(--el-text-color-primary);
}
.breadcrumb,
.path {
color: #606266;
color: var(--el-text-color-regular);
}
}
}
.icon {
flex: 0 0 66px;
text-align: center;
color: #999;
color: var(--el-color-info);
font-size: 20px;
transition: all 0.3s;
}
@@ -358,20 +359,20 @@ function handleScroll() {
display: flex;
flex-direction: column;
justify-content: space-around;
border-left: 1px solid #ebeef5;
border-left: 1px solid var(--el-border-color-lighter);
padding: 5px 10px 7px;
transition: all 0.3s;
@include text-overflow(1, true);
.title {
font-size: 18px;
font-weight: bold;
color: #666;
color: var(--el-text-color-regular);
@include text-overflow(1, true);
}
.breadcrumb,
.path {
font-size: 12px;
color: #c0c4cc;
color: var(--el-text-color-secondary);
transition: all 0.3s;
@include text-overflow(1, true);
}
+10 -10
View File
@@ -114,31 +114,31 @@ a {
<style lang="scss">
.el-menu--inline {
background-color: $g-sub-sidebar-menu-bg !important;
background-color: var(--g-sub-sidebar-menu-bg) !important;
.el-menu-item,
.el-sub-menu > .el-sub-menu__title {
color: $g-sub-sidebar-menu-color;
background-color: $g-sub-sidebar-menu-bg !important;
color: var(--g-sub-sidebar-menu-color);
background-color: var(--g-sub-sidebar-menu-bg) !important;
&:hover {
color: $g-sub-sidebar-menu-hover-color !important;
background-color: $g-sub-sidebar-menu-hover-bg !important;
color: var(--g-sub-sidebar-menu-hover-color) !important;
background-color: var(--g-sub-sidebar-menu-hover-bg) !important;
}
}
}
.el-menu-item,
.el-sub-menu__title {
color: $g-sub-sidebar-menu-color !important;
color: var(--g-sub-sidebar-menu-color) !important;
background: transparent !important;
&:hover {
color: $g-sub-sidebar-menu-hover-color !important;
background-color: $g-sub-sidebar-menu-hover-bg !important;
color: var(--g-sub-sidebar-menu-hover-color) !important;
background-color: var(--g-sub-sidebar-menu-hover-bg) !important;
}
}
.el-menu-item.is-active,
.el-menu--collapse .el-sub-menu.is-active > .el-sub-menu__title,
.el-sub-menu .el-menu--inline .el-menu-item.is-active {
color: $g-sub-sidebar-menu-active-color !important;
background-color: $g-sub-sidebar-menu-active-bg !important;
color: var(--g-sub-sidebar-menu-active-color) !important;
background-color: var(--g-sub-sidebar-menu-active-bg) !important;
.icon,
.el-sub-menu__icon-arrow {
color: unset;
+8 -8
View File
@@ -68,8 +68,8 @@ function onSidebarScroll(e) {
top: 0;
bottom: 0;
transition: 0.3s;
background-color: $g-sub-sidebar-bg;
box-shadow: 10px 0 10px -10px darken($g-sub-sidebar-bg, 20);
background-color: var(--g-sub-sidebar-bg);
box-shadow: 10px 0 10px -10px var(--g-box-shadow-color);
&.is-collapse {
width: 64px;
.sidebar-logo {
@@ -83,24 +83,24 @@ function onSidebarScroll(e) {
}
.sidebar-logo {
transition: box-shadow 0.2s, background-color 0.3s, color 0.3s;
background-color: $g-sub-sidebar-bg;
background-color: var(--g-sub-sidebar-bg);
&:not(.sidebar-logo-bg) {
:deep(span) {
color: $g-sub-sidebar-menu-color;
color: var(--g-sub-sidebar-menu-color);
}
}
&.sidebar-logo-bg {
background-color: $g-main-sidebar-bg;
background-color: var(--g-main-sidebar-bg);
}
&.shadow {
box-shadow: 0 10px 10px -10px darken($g-sub-sidebar-bg, 20);
box-shadow: 0 10px 10px -10px var(--g-box-shadow-color);
}
}
.el-menu {
border-right: 0;
padding-top: $g-sidebar-logo-height;
transition: border-color 0.3s, background-color 0.3s, color 0.3s;
background-color: $g-sub-sidebar-bg;
background-color: var(--g-sub-sidebar-bg);
&:not(.el-menu--collapse) {
width: inherit;
}
@@ -108,7 +108,7 @@ function onSidebarScroll(e) {
padding-top: 0;
}
&.el-menu--collapse {
:deep(.icon) {
:deep(.title-icon) {
margin-right: 0;
}
:deep(.el-menu-item),
+10 -1
View File
@@ -51,6 +51,12 @@ function userCommand(command) {
<svg-icon name="ep:refresh-right" />
</el-icon>
</span>
<span v-if="settingsStore.topbar.enableColorScheme" class="item" @click="settingsStore.setColorScheme(settingsStore.app.colorScheme === 'dark' ? 'light' : 'dark')">
<el-icon>
<svg-icon v-show="settingsStore.app.colorScheme === 'light'" name="ep:sunny" />
<svg-icon v-show="settingsStore.app.colorScheme === 'dark'" name="ep:moon" />
</el-icon>
</span>
<span v-if="settingsStore.topbar.enableAppSetting" class="item" @click="$eventBus.emit('global-theme-toggle')">
<el-icon>
<svg-icon name="ep:setting" />
@@ -96,7 +102,10 @@ function userCommand(command) {
width: 34px;
cursor: pointer;
vertical-align: middle;
transition: all 0.3s;
.el-icon {
color: var(--el-text-color-primary);
transition: var(--el-transition-color);
}
}
}
}
+6 -7
View File
@@ -80,13 +80,12 @@ function pathCompile(path) {
align-items: center;
justify-content: space-between;
height: $g-topbar-height;
background-color: #fff;
transition: 0.3s, box-shadow 0.2s;
box-shadow: 0 0 1px 0 #ccc;
background-color: var(--g-toolbar-bg);
transition: width 0.3s, transform 0.3s, background-color 0.3s, var(--el-transition-box-shadow);
&.fixed {
position: fixed;
&.shadow {
box-shadow: 0 10px 10px -10px #ccc;
box-shadow: 0 10px 10px -10px var(--g-box-shadow-color);
}
}
.left-box {
@@ -102,12 +101,12 @@ function pathCompile(path) {
padding: 0 20px;
height: 50px;
cursor: pointer;
transition: 0.3s;
.el-icon {
transition: 0.3s;
color: var(--el-text-color-primary);
transition: var(--el-transition-color), var(--el-transition-md-fade);
}
&:hover .el-icon {
color: #5482ee;
color: var(--el-color-primary);
}
&.is-collapse .el-icon {
transform: rotateZ(-180deg);
+3 -4
View File
@@ -132,7 +132,7 @@ function switchMenu(index) {
position: relative;
width: 100%;
height: 100%;
box-shadow: -1px 0 0 0 darken($g-main-bg, 10);
box-shadow: -1px 0 0 0 var(--g-box-shadow-color);
.sidebar-container {
position: fixed;
z-index: 1010;
@@ -167,10 +167,9 @@ function switchMenu(index) {
display: flex;
flex-direction: column;
min-height: 100%;
transition: margin-left 0.3s;
background-color: $g-main-bg;
box-shadow: 1px 0 0 0 darken($g-main-bg, 10);
margin-left: calc(var(--g-main-sidebar-actual-width) + var(--g-sub-sidebar-actual-width));
background-color: var(--g-main-bg);
transition: margin-left 0.3s, background-color 0.3s;
.topbar-container {
top: 0;
z-index: 998;
+4
View File
@@ -1,5 +1,7 @@
let globalSettings = {
app: {
// 颜色方案,可选值为 light、dark
colorScheme: 'light',
// Element 组件默认尺寸,支持:large、default、small
elementSize: 'default',
// 是否将 iconify 图标集中的 Element Plus 图标下载到本地,以便在离线环境下使用
@@ -72,6 +74,8 @@ let globalSettings = {
enableFullscreen: false,
// 是否开启页面刷新
enablePageReload: false,
// 是否开启颜色主题
enableColorScheme: false,
// 是否开启应用配置(建议在生产环境关闭)
enableAppSetting: false
},
+4
View File
@@ -43,6 +43,10 @@ const useSettingsStore = defineStore(
this.subMenuCollapseLastStatus = !this.subMenuCollapseLastStatus
}
},
// 设置主题颜色模式
setColorScheme(color) {
this.app.colorScheme = color
},
// 更新主题配置
updateThemeSetting(data) {
Object.assign(this, data)
+257 -95
View File
@@ -17,11 +17,13 @@ const settingsStore = useSettingsStore()
import useUserStore from '@/store/modules/user'
const userStore = useUserStore()
const banner = new URL('../assets/images/login-banner.png', import.meta.url).href
const title = import.meta.env.VITE_APP_TITLE
// 表单类型,login 登录,reset 重置密码
const formType = ref('login')
// 登录
const loginForm = ref({
account: localStorage.login_account || '',
password: '',
@@ -36,40 +38,6 @@ const loginRules = ref({
{ min: 6, max: 18, trigger: 'blur', message: '密码长度为6到18位' }
]
})
const resetForm = ref({
account: localStorage.login_account || '',
captcha: '',
newPassword: ''
})
const resetRules = ref({
account: [
{ required: true, trigger: 'blur', message: '请输入用户名' }
],
captcha: [
{ required: true, trigger: 'blur', message: '请输入验证码' }
],
newPassword: [
{ required: true, trigger: 'blur', message: '请输入新密码' },
{ min: 6, max: 18, trigger: 'blur', message: '密码长度为6到18位' }
]
})
const loading = ref(false)
const passwordType = ref('password')
const redirect = ref(null)
onMounted(() => {
redirect.value = route.query.redirect ?? '/'
})
function showPassword() {
passwordType.value = passwordType.value === 'password' ? '' : 'password'
nextTick(() => {
proxy.$refs.password.focus()
})
}
function handleLogin() {
proxy.$refs.loginFormRef.validate(valid => {
if (valid) {
@@ -89,10 +57,69 @@ function handleLogin() {
})
}
function handleFind() {
// 注册
const registerForm = ref({
account: '',
captcha: '',
password: '',
checkPassword: ''
})
const registerRules = ref({
account: [
{ required: true, trigger: 'blur', message: '请输入用户名' }
],
captcha: [
{ required: true, trigger: 'blur', message: '请输入验证码' }
],
password: [
{ required: true, trigger: 'blur', message: '请输入密码' },
{ min: 6, max: 18, trigger: 'blur', message: '密码长度为6到18位' }
],
checkPassword: [
{ required: true, trigger: 'blur', message: '请再次输入密码' },
{ validator: (rule, value, callback) => {
if (value !== registerForm.password) {
callback(new Error('两次输入的密码不一致'))
} else {
callback()
}
} }
]
})
function handleRegister() {
ElMessage({
message: '重置密码仅提供界面演示,无实际功能,需开发者自行扩展',
type: 'info'
message: '注册模块仅提供界面演示,无实际功能,需开发者自行扩展',
type: 'warning'
})
proxy.$refs.registerFormRef.validate(valid => {
if (valid) {
// 这里编写业务代码
}
})
}
// 重置密码
const resetForm = ref({
account: localStorage.login_account || '',
captcha: '',
newPassword: ''
})
const resetRules = ref({
account: [
{ required: true, trigger: 'blur', message: '请输入用户名' }
],
captcha: [
{ required: true, trigger: 'blur', message: '请输入验证码' }
],
newPassword: [
{ required: true, trigger: 'blur', message: '请输入新密码' },
{ min: 6, max: 18, trigger: 'blur', message: '密码长度为6到18位' }
]
})
function handleReset() {
ElMessage({
message: '重置密码模块仅提供界面演示,无实际功能,需开发者自行扩展',
type: 'warning'
})
proxy.$refs.resetFormRef.validate(valid => {
if (valid) {
@@ -101,6 +128,21 @@ function handleFind() {
})
}
const loading = ref(false)
const passwordType = ref('password')
const redirect = ref(null)
onMounted(() => {
redirect.value = route.query.redirect ?? '/'
})
function showPassword() {
passwordType.value = passwordType.value === 'password' ? '' : 'password'
nextTick(() => {
proxy.$refs.password.focus()
})
}
function testAccount(account) {
loginForm.value.account = account
loginForm.value.password = '123456'
@@ -112,14 +154,17 @@ function testAccount(account) {
<div>
<div class="bg-banner" />
<div id="login-box">
<div class="login-banner" />
<el-form v-show="formType == 'login'" ref="loginFormRef" :model="loginForm" :rules="loginRules" class="login-form" autocomplete="on" label-position="left">
<div class="login-banner">
<div class="logo" />
<img :src="banner" class="banner">
</div>
<el-form v-show="formType == 'login'" ref="loginFormRef" :model="loginForm" :rules="loginRules" class="login-form" autocomplete="on">
<div class="title-container">
<h3 class="title">{{ title }}管理后台</h3>
<h3 class="title">欢迎来到 {{ title }} ! 👋🏻</h3>
</div>
<div>
<el-form-item prop="account">
<el-input ref="name" v-model="loginForm.account" placeholder="用户名" type="text" tabindex="1" autocomplete="on">
<el-input ref="name" v-model="loginForm.account" placeholder="用户名" text tabindex="1" autocomplete="on">
<template #prefix>
<el-icon>
<svg-icon name="user" />
@@ -144,22 +189,26 @@ function testAccount(account) {
</div>
<div class="flex-bar">
<el-checkbox v-model="loginForm.remember">记住我</el-checkbox>
<el-button type="text" @click="formType = 'reset'">忘记密码</el-button>
<el-link type="primary" :underline="false" @click="formType = 'reset'">忘记密码了?</el-link>
</div>
<el-button :loading="loading" type="primary" size="large" style="width: 100%;" @click.prevent="handleLogin">登 录</el-button>
<div style="margin-top: 20px; margin-bottom: -10px; color: #666; font-size: 14px; text-align: center; font-weight: bold;">
<span style="margin-right: 5px;">演示帐号一键登录:</span>
<el-button type="danger" size="small" @click="testAccount('admin')">admin</el-button>
<el-button type="danger" size="small" plain @click="testAccount('test')">test</el-button>
<el-button :loading="loading" type="primary" size="large" style="width: 100%;" @click.prevent="handleLogin">登录</el-button>
<div class="sub-link">
<span class="text">还没有帐号?</span>
<el-link type="primary" :underline="false" @click="formType = 'register'">创建新帐号</el-link>
</div>
<div style="margin-top: 20px; margin-bottom: -20px; text-align: center;">
<el-divider>演示账号一键登录</el-divider>
<el-button type="primary" size="small" plain @click="testAccount('admin')">admin</el-button>
<el-button size="small" plain @click="testAccount('test')">test</el-button>
</div>
</el-form>
<el-form v-show="formType == 'reset'" ref="resetFormRef" :model="resetForm" :rules="resetRules" class="login-form" auto-complete="on" label-position="left">
<el-form v-show="formType == 'register'" ref="registerFormRef" :model="registerForm" :rules="registerRules" class="login-form" auto-complete="on">
<div class="title-container">
<h3 class="title">重置密码</h3>
<h3 class="title">探索从这里开始! 🚀</h3>
</div>
<div>
<el-form-item prop="account">
<el-input ref="name" v-model="resetForm.account" placeholder="用户名" type="text" tabindex="1" autocomplete="on">
<el-input ref="name" v-model="registerForm.account" placeholder="用户名" tabindex="1" autocomplete="on">
<template #prefix>
<el-icon>
<svg-icon name="user" />
@@ -168,7 +217,68 @@ function testAccount(account) {
</el-input>
</el-form-item>
<el-form-item prop="captcha">
<el-input ref="captcha" v-model="resetForm.captcha" placeholder="验证码" type="text" tabindex="2" autocomplete="on">
<el-input ref="captcha" v-model="registerForm.captcha" placeholder="验证码" tabindex="2" autocomplete="on">
<template #prefix>
<el-icon>
<svg-icon name="captcha" />
</el-icon>
</template>
<template #append>
<el-button>发送验证码</el-button>
</template>
</el-input>
</el-form-item>
<el-form-item prop="password">
<el-input ref="password" v-model="registerForm.password" :type="passwordType" placeholder="密码" tabindex="3" autocomplete="on">
<template #prefix>
<el-icon>
<svg-icon name="password" />
</el-icon>
</template>
<template #suffix>
<el-icon>
<svg-icon :name="passwordType === 'password' ? 'eye' : 'eye-open'" @click="showPassword" />
</el-icon>
</template>
</el-input>
</el-form-item>
<el-form-item prop="checkPassword">
<el-input ref="checkPassword" v-model="registerForm.checkPassword" :type="passwordType" placeholder="确认密码" tabindex="4" autocomplete="on">
<template #prefix>
<el-icon>
<svg-icon name="password" />
</el-icon>
</template>
<template #suffix>
<el-icon>
<svg-icon :name="passwordType === 'password' ? 'eye' : 'eye-open'" @click="showPassword" />
</el-icon>
</template>
</el-input>
</el-form-item>
</div>
<el-button :loading="loading" type="primary" size="large" style="width: 100%; margin-top: 20px;" @click.prevent="handleRegister">注册</el-button>
<div class="sub-link">
<span class="text">已经有帐号?</span>
<el-link type="primary" :underline="false" @click="formType = 'login'">去登录</el-link>
</div>
</el-form>
<el-form v-show="formType == 'reset'" ref="resetFormRef" :model="resetForm" :rules="resetRules" class="login-form" auto-complete="on">
<div class="title-container">
<h3 class="title">忘记密码了? 🔒</h3>
</div>
<div>
<el-form-item prop="account">
<el-input ref="name" v-model="resetForm.account" placeholder="用户名" tabindex="1" autocomplete="on">
<template #prefix>
<el-icon>
<svg-icon name="user" />
</el-icon>
</template>
</el-input>
</el-form-item>
<el-form-item prop="captcha">
<el-input ref="captcha" v-model="resetForm.captcha" placeholder="验证码" tabindex="2" autocomplete="on">
<template #prefix>
<el-icon>
<svg-icon name="captcha" />
@@ -194,14 +304,10 @@ function testAccount(account) {
</el-input>
</el-form-item>
</div>
<el-row :gutter="15" style="padding-top: 20px;">
<el-col :md="6">
<el-button size="large" style="width: 100%;" @click="formType = 'login'">去登录</el-button>
</el-col>
<el-col :md="18">
<el-button :loading="loading" type="primary" size="large" style="width: 100%;" @click.prevent="handleFind">确 认</el-button>
</el-col>
</el-row>
<el-button :loading="loading" type="primary" size="large" style="width: 100%; margin-top: 20px;" @click.prevent="handleReset">确认</el-button>
<div class="sub-link">
<el-link type="primary" :underline="false" @click="formType = 'login'">返回登录</el-link>
</div>
</el-form>
</div>
<Copyright v-if="settingsStore.copyright.enable" />
@@ -211,10 +317,40 @@ function testAccount(account) {
<style lang="scss" scoped>
[data-mode="mobile"] {
#login-box {
max-width: 80%;
position: relative;
width: 100%;
height: 100%;
top: inherit;
left: inherit;
transform: translateX(0) translateY(0);
flex-direction: column;
justify-content: start;
border-radius: 0;
box-shadow: none;
.login-banner {
display: none;
width: 100%;
padding: 20px 0;
.banner {
position: relative;
right: inherit;
width: 100%;
max-width: 375px;
margin: 0 auto;
display: inherit;
top: inherit;
transform: translateY(0);
}
}
.login-form {
width: 100%;
min-height: auto;
padding: 30px;
}
}
.copyright {
position: relative;
bottom: 0;
padding-bottom: 10px;
}
}
:deep(input[type="password"]::-ms-reveal) {
@@ -225,9 +361,7 @@ function testAccount(account) {
z-index: 0;
width: 100%;
height: 100%;
background-image: url("../assets/images/login-bg.jpg");
background-size: cover;
background-position: center center;
background: radial-gradient(circle at center, var(--el-fill-color-lighter), var(--el-bg-color-page));
}
#login-box {
display: flex;
@@ -236,58 +370,87 @@ function testAccount(account) {
top: 50%;
left: 50%;
transform: translateX(-50%) translateY(-50%);
background: rgb(255 255 255 / 80%);
background-color: var(--el-bg-color);
border-radius: 10px;
overflow: hidden;
box-shadow: 0 0 5px #999;
box-shadow: var(--el-box-shadow);
.login-banner {
width: 300px;
background-image: url("../assets/images/login-banner.jpg");
background-size: cover;
background-position: center center;
position: relative;
width: 450px;
background-color: var(--el-fill-color-light);
.banner {
width: calc(100% + 100px);
right: -50px;
@include position-center(y);
}
.logo {
position: absolute;
top: 20px;
left: 20px;
width: 30px;
height: 30px;
border-radius: 4px;
background: url("../assets/images/logo.png") no-repeat;
background-size: contain;
box-shadow: var(--el-box-shadow-light);
}
}
.login-form {
width: 450px;
padding: 50px 35px 30px;
display: flex;
flex-direction: column;
justify-content: center;
min-height: 500px;
width: 500px;
padding: 50px;
overflow: hidden;
.title-container {
position: relative;
.title {
font-size: 22px;
color: #666;
font-size: 1.3em;
color: var(--el-text-color-primary);
margin: 0 auto 30px;
text-align: center;
font-weight: bold;
@include text-overflow;
}
}
}
:deep(.el-input) {
height: 48px;
line-height: inherit;
width: 100%;
input {
.el-form-item {
margin-bottom: 24px;
:deep(.el-input) {
height: 48px;
}
.el-input__prefix,
.el-input__suffix {
display: flex;
align-items: center;
}
.el-input__prefix {
left: 10px;
}
.el-input__suffix {
right: 10px;
line-height: inherit;
width: 100%;
input {
height: 48px;
}
.el-input__prefix,
.el-input__suffix {
display: flex;
align-items: center;
}
.el-input__prefix {
left: 10px;
}
.el-input__suffix {
right: 10px;
}
}
}
.flex-bar {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 20px;
}
.el-checkbox {
margin: 20px 0;
.sub-link {
display: flex;
align-items: center;
justify-content: center;
margin-top: 20px;
font-size: 14px;
color: var(--el-text-color-secondary);
.text {
margin-right: 10px;
}
}
}
.copyright {
@@ -295,6 +458,5 @@ function testAccount(account) {
bottom: 30px;
width: 100%;
margin: 0;
mix-blend-mode: difference;
}
</style>
+13 -11
View File
@@ -72,7 +72,7 @@ function editPassword() {
<div class="desc">当前密码强度:强</div>
</div>
<div class="action">
<el-button type="text" @click="editPassword">修改</el-button>
<el-button text @click="editPassword">修改</el-button>
</div>
</div>
<div class="item">
@@ -81,7 +81,7 @@ function editPassword() {
<div class="desc">已绑定手机:187****3441</div>
</div>
<div class="action">
<el-button type="text">修改</el-button>
<el-button text>修改</el-button>
</div>
</div>
<div class="item">
@@ -90,7 +90,7 @@ function editPassword() {
<div class="desc">当前未绑定备用邮箱</div>
</div>
<div class="action">
<el-button type="text">绑定</el-button>
<el-button text>绑定</el-button>
</div>
</div>
</div>
@@ -106,8 +106,9 @@ function editPassword() {
.el-tabs__active-bar {
z-index: 0;
width: 100%;
background-color: #e1f0ff;
border-right: 2px solid #409eff;
background-color: var(--el-color-primary-light-9);
border-right: 2px solid var(--el-color-primary);
transition: transform 0.3s, background-color 0.3s, var(--el-transition-border);
}
.el-tabs__item {
text-align: left;
@@ -125,9 +126,7 @@ h2 {
}
.basic {
:deep(.headimg-upload) {
> div {
text-align: center;
}
text-align: center;
.el-upload-dragger {
border-radius: 50%;
}
@@ -140,15 +139,18 @@ h2 {
align-items: center;
justify-content: space-between;
padding: 10px 0;
border-bottom: 1px solid #e8e8e8;
border-bottom: 1px solid var(--el-border-color-lighter);
transition: var(--el-transition-border);
.content {
.title {
margin-bottom: 5px;
color: #666;
color: var(--el-text-color-primary);
transition: var(--el-transition-color);
}
.desc {
font-size: 14px;
color: #999;
color: var(--el-text-color-secondary);
transition: var(--el-transition-color);
}
}
&:last-child {
+2 -2
View File
@@ -11,14 +11,14 @@ export default ({ mode, command }) => {
const scssResources = []
fs.readdirSync('src/assets/styles/resources').map(dirname => {
if (fs.statSync(`src/assets/styles/resources/${dirname}`).isFile()) {
scssResources.push(`@import "src/assets/styles/resources/${dirname}";`)
scssResources.push(`@use "src/assets/styles/resources/${dirname}" as *;`)
}
})
// css 精灵图相关
fs.readdirSync('src/assets/sprites').map(dirname => {
if (fs.statSync(`src/assets/sprites/${dirname}`).isDirectory()) {
// css 精灵图生成的 scss 文件也需要放入全局 scss 资源
scssResources.push(`@import "src/assets/sprites/_${dirname}.scss";`)
scssResources.push(`@use "src/assets/sprites/_${dirname}.scss" as *;`)
}
})
// BUG https://github.com/element-plus/element-plus/issues/4916