wx/src/views/chat/ChatModeSelector.vue

367 lines
7.4 KiB
Vue
Raw Normal View History

<template>
<div class="mode-selector-container">
<div
class="mode-selector-backdrop"
:class="{ visible: !settingsStore.sidebarCollapsed }"
@click="settingsStore.toggleSidebar"
v-if="isMobile"
></div>
<div class="mode-selector" :class="{ 'collapsed': settingsStore.sidebarCollapsed }">
<div class="mode-header">
<div class="mode-header-title" v-if="!settingsStore.sidebarCollapsed">
<img src="/lgogo.svg" alt="Logo" class="mode-header-icon" />
</div>
</div>
<el-menu
:default-active="chatStore.currentMode"
class="mode-menu"
:collapse="settingsStore.sidebarCollapsed"
@select="selectMode"
>
<el-menu-item v-for="mode in chatModes" :key="mode.id" :index="mode.id" class="mode-item">
<el-icon><component :is="mode.icon" /></el-icon>
<span class="mode-name">{{ mode.name }}</span>
</el-menu-item>
</el-menu>
<!-- Settings Button Popover -->
<el-popover
placement="top-end"
trigger="click"
popper-class="settings-popover-popper"
>
<template #reference>
<div class="sidebar-footer">
<div class="settings-button" title="设置">
<el-icon><Setting /></el-icon>
<span class="button-text">设置</span>
</div>
</div>
</template>
<!-- Popover Content -->
<SettingsPanel />
</el-popover>
</div>
</div>
</template>
<script setup>
import { useChatStore } from '@/store/chat'
import { useSettingsStore } from '@/store/settings'
import { computed, ref, onMounted, onUnmounted } from 'vue'
import { ChatDotRound, ChatLineRound, ChatRound, Service, Setting } from '@element-plus/icons-vue'
import { ElMenu, ElMenuItem, ElIcon, ElPopover } from 'element-plus'
import SettingsPanel from '@/components/settings/SettingsPanel.vue'
const chatStore = useChatStore()
const settingsStore = useSettingsStore()
const chatModes = computed(() => {
return Object.keys(chatStore.chatModes).map(key => ({
id: key,
name: chatStore.chatModes[key].name,
icon: key === 'general' ? ChatRound :
key === 'assistant' ? Service :
key === 'chat' ? ChatDotRound : ChatLineRound
}))
})
// 获取当前模式下的历史会话
const currentModeHistory = computed(() => {
return chatStore.conversations.filter(chat => chat.mode === chatStore.currentMode)
})
const selectMode = (modeId) => {
chatStore.setCurrentMode(modeId)
}
const isMobile = ref(false)
const checkMobile = () => {
isMobile.value = window.innerWidth <= 768
}
onMounted(() => {
checkMobile()
window.addEventListener('resize', checkMobile)
})
onUnmounted(() => {
window.removeEventListener('resize', checkMobile)
})
</script>
<style lang="scss" scoped>
.mode-selector-container {
height: 100vh;
position: relative;
}
.mode-selector {
width: 240px;
height: 100%;
background-color: var(--el-bg-color);
border-right: 1px solid var(--el-border-color-light);
position: relative;
z-index: 10;
display: flex;
flex-direction: column;
body:not(.no-animations) & {
transition: width 0.3s ease-in-out;
}
&.collapsed {
width: 64px;
}
}
.mode-header {
height: 60px;
padding: 0 16px;
display: flex;
align-items: center;
&-title {
display: flex;
align-items: center;
.mode-header-icon {
height: 32px;
will-change: filter;
body:not(.no-animations) & {
transition: filter 300ms;
}
&:hover {
filter: drop-shadow(0 0 0.75em rgba(var(--el-color-primary-rgb), 0.6));
}
}
}
}
.mode-menu {
background-color: transparent;
border: none;
padding: 8px;
flex-grow: 1;
overflow-y: auto;
:deep(.el-menu-item) {
height: 44px;
line-height: 44px;
color: var(--el-text-color-regular);
border-radius: 8px;
margin-bottom: 4px;
padding: 0 12px !important;
overflow: hidden;
.el-icon {
margin-right: 12px;
font-size: 18px;
width: 18px;
}
&.is-active {
background-image: linear-gradient(135deg, var(--el-color-primary) 0%, var(--el-color-primary-light-3) 100%);
color: white;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
.el-icon {
color: white;
}
}
&:not(.is-active):hover {
background-color: var(--el-color-primary-light-9);
color: var(--el-color-primary);
}
.mode-name {
font-size: 14px;
margin-left: 12px;
white-space: nowrap;
opacity: 1;
transform: translateX(0);
body:not(.no-animations) & {
transition: opacity 0.3s ease-in-out;
}
}
}
}
.mode-setting-btn {
position: absolute;
right: 8px;
top: 50%;
transform: translateY(-50%);
color: var(--el-text-color-secondary);
&:hover {
color: var(--el-color-primary);
}
}
/* 移动端样式 */
@media screen and (max-width: 768px) {
.mode-selector-backdrop {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: var(--el-overlay-color-lighter);
z-index: 9;
display: none;
&.visible {
display: block;
}
}
.mode-selector {
position: fixed;
left: 0;
top: 0;
&.collapsed {
transform: translateX(-100%);
}
}
}
.mode-selector.collapsed {
.mode-menu {
padding: 8px 12px;
}
:deep(.el-menu-item) {
width: 40px;
height: 40px;
padding: 0 !important;
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto 4px auto;
.el-icon {
margin-right: 0;
}
.mode-name {
display: none;
}
}
.settings-button {
width: 40px;
height: 40px;
.mode-name,
.button-text {
opacity: 0;
}
}
}
.sidebar-footer {
padding: 8px;
margin-top: auto;
}
.settings-button {
display: flex;
align-items: center;
height: 44px;
line-height: 44px;
color: var(--el-text-color-regular);
border-radius: 8px;
padding: 0 12px !important;
cursor: pointer;
overflow: hidden;
body:not(.no-animations) & {
transition: background-color 0.3s, color 0.3s;
}
.el-icon {
margin-right: 12px;
font-size: 18px;
width: 18px;
color: var(--el-text-color-regular);
body:not(.no-animations) & {
transition: color 0.3s;
}
}
.button-text {
font-size: 14px;
margin-left: 12px;
white-space: nowrap;
opacity: 1;
transform: translateX(0);
body:not(.no-animations) & {
transition: opacity 0.3s ease-in-out;
}
}
&:hover {
background-color: var(--el-color-primary-light-9);
color: var(--el-color-primary);
.el-icon {
color: var(--el-color-primary);
}
}
}
.mode-selector.collapsed {
.sidebar-footer {
padding: 8px 12px;
.settings-button {
width: 40px;
height: 40px;
}
}
.mode-menu {
padding: 8px 12px;
}
:deep(.el-menu-item) {
width: 40px;
height: 40px;
padding: 0 !important;
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto 4px auto;
.el-icon {
margin-right: 0;
}
.mode-name {
display: none;
}
}
.settings-button {
.mode-name,
.button-text {
opacity: 0;
}
}
}
</style>
<style lang="scss">
.settings-popover-popper {
padding: 0 !important;
min-width: 240px !important;
width: auto !important;
}
</style>