Merge branch 'AstrBotDevs:master' into master
This commit is contained in:
@@ -5,7 +5,7 @@
|
||||
<img width="110" src="@/assets/images/astrbot_logo_mini.webp" alt="AstrBot Logo">
|
||||
</div>
|
||||
<div class="logo-text">
|
||||
<h2 class="text-secondary">{{ title }}</h2>
|
||||
<h2 :style="{color: useCustomizerStore().uiTheme === 'PurpleTheme' ? '#5e35b1' : '#d7c5fa'}">{{ title }}</h2>
|
||||
<!-- 父子组件传递css变量可能会出错,暂时使用十六进制颜色值 -->
|
||||
<h4 :style="{color: useCustomizerStore().uiTheme === 'PurpleTheme' ? '#000000aa' : '#ffffffcc'}"
|
||||
class="hint-text">{{ subtitle }}</h4>
|
||||
|
||||
@@ -160,25 +160,23 @@ function endDrag() {
|
||||
width="220"
|
||||
:rail="customizer.mini_sidebar"
|
||||
>
|
||||
<v-list class="pa-4 listitem" style="height: auto;">
|
||||
<template v-for="(item, i) in sidebarMenu" :key="i">
|
||||
<NavItem :item="item" class="leftPadding" />
|
||||
</template>
|
||||
</v-list>
|
||||
<div style="position: absolute; bottom: 16px; width: 100%; font-size: 13px;" class="text-center">
|
||||
<v-btn style="margin-bottom: 8px;" size="small" variant="primary" v-if="!customizer.mini_sidebar" to="/settings">
|
||||
🔧 设置
|
||||
</v-btn>
|
||||
<br/>
|
||||
<v-btn style="margin-bottom: 8px;" size="small" variant="plain" v-if="!customizer.mini_sidebar" @click="toggleIframe">
|
||||
官方文档
|
||||
</v-btn>
|
||||
<br/>
|
||||
<v-btn style="margin-bottom: 8px;" size="small" variant="plain" v-if="!customizer.mini_sidebar" @click="openIframeLink('https://github.com/AstrBotDevs/AstrBot')">
|
||||
GitHub
|
||||
</v-btn>
|
||||
<br/>
|
||||
|
||||
<div class="sidebar-container">
|
||||
<v-list class="pa-4 listitem flex-grow-1">
|
||||
<template v-for="(item, i) in sidebarMenu" :key="i">
|
||||
<NavItem :item="item" class="leftPadding" />
|
||||
</template>
|
||||
</v-list>
|
||||
<div class="sidebar-footer" v-if="!customizer.mini_sidebar">
|
||||
<v-btn style="margin-bottom: 8px;" size="small" variant="primary" to="/settings">
|
||||
🔧 设置
|
||||
</v-btn>
|
||||
<v-btn style="margin-bottom: 8px;" size="small" variant="plain" @click="toggleIframe">
|
||||
官方文档
|
||||
</v-btn>
|
||||
<v-btn style="margin-bottom: 8px;" size="small" variant="plain" @click="openIframeLink('https://github.com/AstrBotDevs/AstrBot')">
|
||||
GitHub
|
||||
</v-btn>
|
||||
</div>
|
||||
</div>
|
||||
</v-navigation-drawer>
|
||||
|
||||
|
||||
@@ -10,6 +10,69 @@
|
||||
.v-field__outline {
|
||||
color: rgb(var(--v-theme-inputBorder));
|
||||
}
|
||||
|
||||
// 亮色主题样式
|
||||
.v-theme--PurpleTheme .v-text-field .v-field__outline {
|
||||
--v-field-border-width: 2px !important;
|
||||
--v-field-border-opacity: 1 !important;
|
||||
color: rgba(149, 117, 205, 0.6) !important;
|
||||
border-color: rgba(149, 117, 205, 0.6) !important;
|
||||
}
|
||||
|
||||
.v-theme--PurpleTheme .v-text-field:hover .v-field__outline {
|
||||
--v-field-border-width: 2px !important;
|
||||
--v-field-border-opacity: 1 !important;
|
||||
color: rgba(149, 117, 205, 0.6) !important;
|
||||
border-color: rgba(149, 117, 205, 0.6) !important;
|
||||
}
|
||||
|
||||
.v-theme--PurpleTheme .v-text-field .v-field--focused .v-field__outline {
|
||||
--v-field-border-width: 2.5px !important;
|
||||
--v-field-border-opacity: 1 !important;
|
||||
color: rgba(149, 117, 205, 0.8) !important;
|
||||
border-color: rgba(149, 117, 205, 0.8) !important;
|
||||
}
|
||||
|
||||
// 深色主题样式
|
||||
.v-theme--PurpleThemeDark .v-text-field .v-field {
|
||||
background-color: rgba(255, 255, 255, 0.08) !important;
|
||||
}
|
||||
|
||||
.v-theme--PurpleThemeDark .v-text-field .v-field__outline {
|
||||
--v-field-border-width: 2px !important;
|
||||
--v-field-border-opacity: 1 !important;
|
||||
color: rgba(255, 255, 255, 0.5) !important;
|
||||
border-color: rgba(255, 255, 255, 0.5) !important;
|
||||
}
|
||||
|
||||
.v-theme--PurpleThemeDark .v-text-field:hover .v-field__outline {
|
||||
--v-field-border-width: 2px !important;
|
||||
--v-field-border-opacity: 1 !important;
|
||||
color: rgba(255, 255, 255, 0.7) !important;
|
||||
border-color: rgba(255, 255, 255, 0.7) !important;
|
||||
}
|
||||
|
||||
.v-theme--PurpleThemeDark .v-text-field .v-field--focused .v-field__outline {
|
||||
--v-field-border-width: 2.5px !important;
|
||||
--v-field-border-opacity: 1 !important;
|
||||
color: rgb(129, 102, 176) !important;
|
||||
border-color: rgb(126, 99, 171) !important;
|
||||
}
|
||||
|
||||
.v-theme--PurpleThemeDark .v-text-field input {
|
||||
color: #ffffff !important;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.v-theme--PurpleThemeDark .v-text-field .v-field__label {
|
||||
color: rgba(255, 255, 255, 0.8) !important;
|
||||
}
|
||||
|
||||
.v-theme--PurpleThemeDark .v-text-field .v-field__prepend-inner .v-icon,
|
||||
.v-theme--PurpleThemeDark .v-text-field .v-field__append-inner .v-icon {
|
||||
color: rgba(255, 255, 255, 0.8) !important;
|
||||
}
|
||||
|
||||
.inputWithbg {
|
||||
.v-field--variant-outlined {
|
||||
background-color: rgba(0, 0, 0, 0.025);
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
box-shadow: none !important;
|
||||
}
|
||||
.listitem {
|
||||
height: calc(100vh - 100px);
|
||||
overflow-y: auto;
|
||||
.v-list {
|
||||
color: rgb(var(--v-theme-secondaryText));
|
||||
}
|
||||
@@ -16,6 +16,39 @@
|
||||
color: rgb(var(--v-theme-secondary));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 深色主题下的侧边栏悬停和选中样式
|
||||
.v-theme--PurpleThemeDark .leftSidebar {
|
||||
.listitem {
|
||||
.v-list-group__items .v-list-item,
|
||||
.v-list-item {
|
||||
&:hover {
|
||||
color: #b794f6 !important;
|
||||
|
||||
.v-list-item-title {
|
||||
color: #b794f6 !important;
|
||||
}
|
||||
|
||||
.v-icon {
|
||||
color: #b794f6 !important;
|
||||
}
|
||||
}
|
||||
|
||||
// 选中状态的样式
|
||||
&.v-list-item--active {
|
||||
color: #b794f6 !important;
|
||||
|
||||
.v-list-item-title {
|
||||
color: #b794f6 !important;
|
||||
}
|
||||
|
||||
.v-icon {
|
||||
color: #b794f6 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.v-list-item--density-default.v-list-item--one-line {
|
||||
min-height: 42px;
|
||||
}
|
||||
@@ -52,3 +85,34 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 新的flex布局样式
|
||||
.leftSidebar {
|
||||
.sidebar-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
|
||||
.flex-grow-1 {
|
||||
flex: 1 1 auto;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.sidebar-footer {
|
||||
padding: 16px;
|
||||
background: inherit;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
font-size: 13px;
|
||||
text-align: center;
|
||||
flex-shrink: 0;
|
||||
|
||||
.v-btn {
|
||||
width: 100%;
|
||||
max-width: 180px;
|
||||
margin-bottom: 8px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
<script setup>
|
||||
import ChatPage from './ChatPage.vue';
|
||||
import { useCustomizerStore } from '@/stores/customizer';
|
||||
const customizer = useCustomizerStore();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div style="height: 100%; width: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center;">
|
||||
<div id="container">
|
||||
<ChatPage chatbox-mode="true"></ChatPage>
|
||||
<v-app :theme="customizer.uiTheme" style="height: 100%; width: 100%;">
|
||||
<div
|
||||
style="height: 100%; width: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center;">
|
||||
<div id="container">
|
||||
<ChatPage chatbox-mode="true"></ChatPage>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</v-app>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -4,6 +4,7 @@ import axios from 'axios';
|
||||
import { marked } from 'marked';
|
||||
import { ref } from 'vue';
|
||||
import { defineProps } from 'vue';
|
||||
import { useCustomizerStore } from '@/stores/customizer';
|
||||
|
||||
marked.setOptions({
|
||||
breaks: true
|
||||
@@ -39,13 +40,16 @@ const props = defineProps({
|
||||
</div>
|
||||
|
||||
<div style="padding: 16px; padding-top: 8px;">
|
||||
<v-btn rounded="lg" class="new-chat-btn" @click="newC" :disabled="!currCid"
|
||||
v-if="!sidebarCollapsed" prepend-icon="mdi-plus">创建对话</v-btn>
|
||||
<v-btn block variant="text" class="new-chat-btn" @click="newC" :disabled="!currCid"
|
||||
v-if="!sidebarCollapsed" prepend-icon="mdi-plus" style="box-shadow: 0 1px 2px rgba(0,0,0,0.1); background-color: transparent !important; border-radius: 4px;">创建对话</v-btn>
|
||||
<v-btn icon="mdi-plus" rounded="lg" @click="newC" :disabled="!currCid" v-if="sidebarCollapsed"
|
||||
elevation="0"></v-btn>
|
||||
</div>
|
||||
<div v-if="!sidebarCollapsed">
|
||||
<v-divider class="mx-2"></v-divider>
|
||||
</div>
|
||||
|
||||
<div style="overflow-y: auto;" :class="{ 'fade-in': sidebarHoverExpanded }"
|
||||
<div style="overflow-y: auto; flex-grow: 1;" class="sidebar-panel" :class="{ 'fade-in': sidebarHoverExpanded }"
|
||||
v-if="!sidebarCollapsed">
|
||||
<v-card class="conversation-list-card" v-if="conversations.length > 0" flat>
|
||||
<v-list density="compact" nav class="conversation-list"
|
||||
@@ -75,14 +79,17 @@ const props = defineProps({
|
||||
</v-fade-transition>
|
||||
</div>
|
||||
|
||||
<div style="padding: 16px; padding-bottom: 0px;" :class="{ 'fade-in': sidebarHoverExpanded }"
|
||||
<div v-if="!sidebarCollapsed">
|
||||
<v-divider class="mx-2"></v-divider>
|
||||
</div>
|
||||
<div style="padding: 16px;" :class="{ 'fade-in': sidebarHoverExpanded }"
|
||||
v-if="!sidebarCollapsed">
|
||||
<div class="sidebar-section-title">
|
||||
系统状态
|
||||
</div>
|
||||
<div class="status-chips">
|
||||
<v-chip class="status-chip" :color="status?.llm_enabled ? 'primary' : 'grey-lighten-2'"
|
||||
variant="elevated" size="small">
|
||||
variant="outlined" size="small" rounded="sm">
|
||||
<template v-slot:prepend>
|
||||
<v-icon :icon="status?.llm_enabled ? 'mdi-check-circle' : 'mdi-alert-circle'"
|
||||
size="x-small"></v-icon>
|
||||
@@ -91,7 +98,7 @@ const props = defineProps({
|
||||
</v-chip>
|
||||
|
||||
<v-chip class="status-chip" :color="status?.stt_enabled ? 'success' : 'grey-lighten-2'"
|
||||
variant="elevated" size="small">
|
||||
variant="outlined" size="small" rounded="sm">
|
||||
<template v-slot:prepend>
|
||||
<v-icon :icon="status?.stt_enabled ? 'mdi-check-circle' : 'mdi-alert-circle'"
|
||||
size="x-small"></v-icon>
|
||||
@@ -100,11 +107,22 @@ const props = defineProps({
|
||||
</v-chip>
|
||||
</div>
|
||||
|
||||
<v-btn variant="tonal" rounded="lg" class="delete-chat-btn" v-if="currCid"
|
||||
@click="deleteConversation(currCid)" color="error" density="comfortable" size="small">
|
||||
<v-icon start size="small">mdi-delete</v-icon>
|
||||
删除此对话
|
||||
</v-btn>
|
||||
<transition
|
||||
name="expand"
|
||||
@before-enter="beforeEnter"
|
||||
@enter="enter"
|
||||
@after-enter="afterEnter"
|
||||
@before-leave="beforeLeave"
|
||||
@leave="leave"
|
||||
>
|
||||
<div v-if="currCid" class="delete-btn-container">
|
||||
<v-btn variant="outlined" rounded="sm" class="delete-chat-btn"
|
||||
@click="deleteConversation(currCid)" color="error" density="comfortable" size="small">
|
||||
<v-icon start size="small">mdi-delete</v-icon>
|
||||
删除此对话
|
||||
</v-btn>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -118,8 +136,27 @@ const props = defineProps({
|
||||
</div>
|
||||
<div class="conversation-header-actions">
|
||||
<!-- router 推送到 /chatbox -->
|
||||
<v-icon @click="router.push('/chatbox')" v-if="!props.chatboxMode"
|
||||
class="fullscreen-icon">mdi-fullscreen</v-icon>
|
||||
<v-tooltip text="全屏模式" v-if="!props.chatboxMode">
|
||||
<template v-slot:activator="{ props }">
|
||||
<v-icon v-bind="props" @click="router.push(currCid ? `/chatbox/${currCid}` : '/chatbox')"
|
||||
class="fullscreen-icon">mdi-fullscreen</v-icon>
|
||||
</template>
|
||||
</v-tooltip>
|
||||
<!-- 主题切换按钮 -->
|
||||
<v-tooltip :text="isDark ? '切换到日间模式' : '切换到夜间模式'" v-if="props.chatboxMode">
|
||||
<template v-slot:activator="{ props }">
|
||||
<v-btn v-bind="props" icon @click="toggleTheme" class="theme-toggle-icon" variant="text">
|
||||
<v-icon>{{ isDark ? 'mdi-weather-night' : 'mdi-white-balance-sunny' }}</v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
</v-tooltip>
|
||||
<!-- router 推送到 /chat -->
|
||||
<v-tooltip text="退出全屏" v-if="props.chatboxMode">
|
||||
<template v-slot:activator="{ props }">
|
||||
<v-icon v-bind="props" @click="router.push(currCid ? `/chat/${currCid}` : '/chat')"
|
||||
class="fullscreen-icon">mdi-fullscreen-exit</v-icon>
|
||||
</template>
|
||||
</v-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
<v-divider v-if="currCid && getCurrentConversation" class="conversation-divider"></v-divider>
|
||||
@@ -312,6 +349,9 @@ export default {
|
||||
},
|
||||
|
||||
computed: {
|
||||
isDark() {
|
||||
return useCustomizerStore().uiTheme === 'PurpleThemeDark';
|
||||
},
|
||||
// Get the current conversation from the conversations array
|
||||
getCurrentConversation() {
|
||||
if (!this.currCid) return null;
|
||||
@@ -360,6 +400,7 @@ export default {
|
||||
},
|
||||
|
||||
mounted() {
|
||||
// Theme is now handled globally by the customizer store.
|
||||
this.startListeningEvent();
|
||||
this.checkStatus();
|
||||
this.getConversations();
|
||||
@@ -401,6 +442,11 @@ export default {
|
||||
},
|
||||
|
||||
methods: {
|
||||
toggleTheme() {
|
||||
const customizer = useCustomizerStore();
|
||||
const newTheme = customizer.uiTheme === 'PurpleTheme' ? 'PurpleThemeDark' : 'PurpleTheme';
|
||||
customizer.SET_UI_THEME(newTheme);
|
||||
},
|
||||
// 切换侧边栏折叠状态
|
||||
toggleSidebar() {
|
||||
if (this.sidebarHoverExpanded) {
|
||||
@@ -912,6 +958,23 @@ export default {
|
||||
});
|
||||
this.mediaCache = {};
|
||||
},
|
||||
|
||||
// For smooth height transition on delete button
|
||||
beforeEnter(el) {
|
||||
el.style.height = '0';
|
||||
},
|
||||
enter(el) {
|
||||
el.style.height = el.scrollHeight + 'px';
|
||||
},
|
||||
afterEnter(el) {
|
||||
el.style.height = 'auto';
|
||||
},
|
||||
beforeLeave(el) {
|
||||
el.style.height = el.scrollHeight + 'px';
|
||||
},
|
||||
leave(el) {
|
||||
el.style.height = '0';
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@@ -967,12 +1030,18 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
.fade-in {
|
||||
animation: fadeInContent 0.2s ease-in forwards;
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.fade-enter-from,
|
||||
.fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
|
||||
/* 聊天页面布局 */
|
||||
/* todo: 聊天页面背景颜色有问题 */
|
||||
.chat-page-card {
|
||||
margin-bottom: 16px;
|
||||
width: 100%;
|
||||
@@ -1008,6 +1077,23 @@ export default {
|
||||
/* 防止内容溢出 */
|
||||
}
|
||||
|
||||
.sidebar-panel ::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
.sidebar-panel ::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.sidebar-panel ::-webkit-scrollbar-thumb {
|
||||
background-color: rgba(0, 0, 0, 0.2);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.sidebar-panel ::-webkit-scrollbar-thumb:hover {
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
/* 侧边栏折叠状态 */
|
||||
.sidebar-collapsed {
|
||||
max-width: 75px;
|
||||
@@ -1074,24 +1160,32 @@ export default {
|
||||
margin-bottom: 12px;
|
||||
padding-left: 4px;
|
||||
transition: opacity 0.25s ease;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.status-chips {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
flex-wrap: nowrap;
|
||||
gap: 8px;
|
||||
margin-bottom: 16px;
|
||||
margin-bottom: 8px;
|
||||
transition: opacity 0.25s ease;
|
||||
}
|
||||
|
||||
.status-chips .v-chip {
|
||||
flex: 1 1 0;
|
||||
justify-content: center;
|
||||
opacity: 0.7; /* Make border and text slightly transparent */
|
||||
}
|
||||
|
||||
.status-chip {
|
||||
font-size: 12px;
|
||||
height: 24px !important;
|
||||
}
|
||||
|
||||
.delete-chat-btn {
|
||||
height: 32px !important;
|
||||
width: 100%;
|
||||
color: #d32f2f !important;
|
||||
color: rgb(var(--v-theme-error)) !important;
|
||||
font-weight: 500;
|
||||
box-shadow: none !important;
|
||||
margin-top: 8px;
|
||||
@@ -1100,10 +1194,21 @@ export default {
|
||||
font-size: 12px;
|
||||
line-height: 1.2em;
|
||||
transition: opacity 0.25s ease;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.delete-chat-btn:hover {
|
||||
background-color: rgba(211, 47, 47, 0.1) !important;
|
||||
background-color: rgba(var(--v-theme-error-rgb), 0.1) !important;
|
||||
}
|
||||
|
||||
.delete-btn-container {
|
||||
/* margin-top: -8px; */ /* Removed for better layout practices */
|
||||
}
|
||||
|
||||
.expand-enter-active,
|
||||
.expand-leave-active {
|
||||
transition: height 0.15s ease-in-out;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.no-conversations {
|
||||
|
||||
@@ -9,6 +9,14 @@ import {useCustomizerStore} from "@/stores/customizer";
|
||||
const cardVisible = ref(false);
|
||||
const router = useRouter();
|
||||
const authStore = useAuthStore();
|
||||
const customizer = useCustomizerStore();
|
||||
|
||||
// 主题切换函数
|
||||
function toggleTheme() {
|
||||
customizer.SET_UI_THEME(
|
||||
customizer.uiTheme === 'PurpleThemeDark' ? 'PurpleTheme' : 'PurpleThemeDark'
|
||||
);
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
// 检查用户是否已登录,如果已登录则重定向
|
||||
@@ -27,6 +35,25 @@ onMounted(() => {
|
||||
<template>
|
||||
<div v-if="useCustomizerStore().uiTheme==='PurpleTheme'" class="login-page-container">
|
||||
<div class="login-background"></div>
|
||||
|
||||
<!-- 主题切换按钮 -->
|
||||
<div class="theme-toggle-container">
|
||||
<v-btn
|
||||
@click="toggleTheme"
|
||||
class="theme-toggle-btn"
|
||||
icon
|
||||
variant="flat"
|
||||
size="small"
|
||||
color="primary"
|
||||
elevation="2"
|
||||
>
|
||||
<v-icon size="20">mdi-weather-night</v-icon>
|
||||
<v-tooltip activator="parent" location="left">
|
||||
切换到深色主题
|
||||
</v-tooltip>
|
||||
</v-btn>
|
||||
</div>
|
||||
|
||||
<v-card
|
||||
variant="outlined"
|
||||
class="login-card"
|
||||
@@ -45,6 +72,25 @@ onMounted(() => {
|
||||
</div>
|
||||
<div v-else class="login-page-container-dark">
|
||||
<div class="login-background-dark"></div>
|
||||
|
||||
<!-- 主题切换按钮 -->
|
||||
<div class="theme-toggle-container">
|
||||
<v-btn
|
||||
@click="toggleTheme"
|
||||
class="theme-toggle-btn"
|
||||
icon
|
||||
variant="flat"
|
||||
size="small"
|
||||
color="secondary"
|
||||
elevation="2"
|
||||
>
|
||||
<v-icon size="20">mdi-white-balance-sunny</v-icon>
|
||||
<v-tooltip activator="parent" location="left">
|
||||
切换到浅色主题
|
||||
</v-tooltip>
|
||||
</v-btn>
|
||||
</div>
|
||||
|
||||
<v-card
|
||||
variant="outlined"
|
||||
class="login-card"
|
||||
@@ -71,7 +117,16 @@ onMounted(() => {
|
||||
height: 100vh;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
background: linear-gradient(135deg, #ebf5fd 0%, #e0e9f8 100%);
|
||||
background:
|
||||
linear-gradient(-45deg,
|
||||
#faf9f7 0%,
|
||||
#f9f2f1 25%,
|
||||
#f1f9f9 50%,
|
||||
#f9f3f7 75%,
|
||||
#faf9f7 100%
|
||||
);
|
||||
background-size: 400% 400%;
|
||||
animation: gradientShift 15s ease infinite;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
@@ -82,17 +137,38 @@ onMounted(() => {
|
||||
height: 100vh;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
background: linear-gradient(135deg, #1a1b1c 0%, #1d1e21 100%);
|
||||
background:
|
||||
linear-gradient(-45deg,
|
||||
#1e1f21 0%,
|
||||
#221e25 25%,
|
||||
#1e2225 50%,
|
||||
#221f23 75%,
|
||||
#1e1f21 100%
|
||||
);
|
||||
background-size: 400% 400%;
|
||||
animation: gradientShift 15s ease infinite;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
@keyframes gradientShift {
|
||||
0% {
|
||||
background-position: 0% 50%;
|
||||
}
|
||||
50% {
|
||||
background-position: 100% 50%;
|
||||
}
|
||||
100% {
|
||||
background-position: 0% 50%;
|
||||
}
|
||||
}
|
||||
|
||||
.login-background {
|
||||
position: absolute;
|
||||
width: 200%;
|
||||
height: 200%;
|
||||
top: -50%;
|
||||
left: -50%;
|
||||
background: radial-gradient(circle, rgba(94, 53, 177, 0.03) 0%, rgba(30, 136, 229, 0.06) 70%);
|
||||
background: radial-gradient(circle, rgba(94, 53, 177, 0.02) 0%, rgba(94, 53, 177, 0.03) 70%);
|
||||
z-index: 0;
|
||||
animation: rotate 60s linear infinite;
|
||||
}
|
||||
@@ -103,7 +179,7 @@ onMounted(() => {
|
||||
height: 200%;
|
||||
top: -50%;
|
||||
left: -50%;
|
||||
background-color: var(--v-theme-surface);
|
||||
background: radial-gradient(circle, rgba(114, 46, 209, 0.03) 0%, rgba(114, 46, 209, 0.04) 70%);
|
||||
z-index: 0;
|
||||
animation: rotate 60s linear infinite;
|
||||
}
|
||||
@@ -117,23 +193,85 @@ onMounted(() => {
|
||||
}
|
||||
}
|
||||
|
||||
.theme-toggle-container {
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.theme-toggle-btn {
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&:hover {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
}
|
||||
|
||||
.login-card {
|
||||
max-width: 520px;
|
||||
width: 90%;
|
||||
position: relative;
|
||||
color: var(--v-theme-primaryText) !important;
|
||||
border-radius: 12px !important;
|
||||
border-color: var(--v-theme-border) !important;
|
||||
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.07) !important;
|
||||
background-color: var(--v-theme-surface) !important;
|
||||
border-radius: 16px !important;
|
||||
border: 1px solid rgba(94, 53, 177, 0.15) !important;
|
||||
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.08) !important;
|
||||
background: #f8f6fc !important;
|
||||
backdrop-filter: blur(10px);
|
||||
transform: translateY(20px);
|
||||
opacity: 0;
|
||||
transition: all 0.5s ease;
|
||||
transition: transform 0.5s ease, opacity 0.5s ease, border-color 0.3s ease, box-shadow 0.3s ease;
|
||||
z-index: 1;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: calc(100% + 4px);
|
||||
height: calc(100% + 4px);
|
||||
transform: translate(-50%, -50%);
|
||||
border-radius: 18px;
|
||||
border: 2px solid rgba(94, 53, 177, 0);
|
||||
transition: border-color 0.3s ease;
|
||||
pointer-events: none;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
&.card-visible {
|
||||
transform: translateY(0);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
border-color: rgba(158, 126, 222, 0.99) !important;
|
||||
box-shadow: 0 12px 40px rgba(175, 145, 230, 0.741) !important;
|
||||
transform: translateY(-2px);
|
||||
|
||||
&::before {
|
||||
border-color: rgba(156, 114, 239, 0.907);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.login-page-container-dark .login-card {
|
||||
border: 1px solid rgba(110, 60, 180, 0.692) !important;
|
||||
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.3) !important;
|
||||
background: #2a2733 !important;
|
||||
|
||||
&::before {
|
||||
border: 2px solid rgba(114, 46, 209, 0);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
border-color: rgba(160, 118, 219, 0.782) !important;
|
||||
box-shadow: 0 12px 40px rgba(99, 44, 175, 0.462) !important;
|
||||
transform: translateY(-2px);
|
||||
|
||||
&::before {
|
||||
border-color: rgba(114, 46, 209, 0.15);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.logo-wrapper {
|
||||
@@ -145,8 +283,12 @@ onMounted(() => {
|
||||
}
|
||||
|
||||
.custom-divider {
|
||||
border-color: rgba(0, 0, 0, 0.05) !important;
|
||||
opacity: 0.8;
|
||||
border-color: rgba(94, 53, 177, 0.1) !important;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.login-page-container-dark .custom-divider {
|
||||
border-color: rgba(114, 46, 209, 0.08) !important;
|
||||
}
|
||||
|
||||
.loginBox {
|
||||
|
||||
@@ -77,6 +77,7 @@ async function validate(values: any, { setErrors }: any) {
|
||||
:disabled="valid"
|
||||
type="submit"
|
||||
elevation="2"
|
||||
|
||||
>
|
||||
<span class="login-btn-text">登录</span>
|
||||
</v-btn>
|
||||
@@ -146,6 +147,7 @@ async function validate(values: any, { setErrors }: any) {
|
||||
height: 48px;
|
||||
transition: all 0.3s ease;
|
||||
letter-spacing: 0.5px;
|
||||
border-radius: 8px !important;
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-2px);
|
||||
|
||||
Reference in New Issue
Block a user