Compare commits

...

3 Commits

5 changed files with 195 additions and 42 deletions

View File

@ -1,6 +1,6 @@
<template>
<div class="main-layout">
<Sidebar />
<Sidebar class="sidebar" />
<main class="content-area">
<!-- 路由视图将渲染在这里 -->
<router-view />
@ -16,7 +16,7 @@ import Sidebar from '../components/Sidebar.vue'
.main-layout {
display: flex;
height: 100%;
min-width: 640px;
/* min-width: 640px; */
overflow: hidden; /* 防止内部滚动影响布局 */
}
@ -28,6 +28,21 @@ import Sidebar from '../components/Sidebar.vue'
background-color: #ffffff; /* 示例背景色 */
}
.sidebar {
transition: all 0.3s ease;
}
/* 移动端样式 */
@media screen and (max-width: 768px) {
.sidebar {
display: none;
}
.content-area {
width: 100%;
}
}
/* 简单的侧边栏列表样式 */
.sidebar ul {
list-style: none;

View File

@ -26,6 +26,11 @@ export const useSettingsStore = defineStore('settings', {
}
}),
persist: {
key: 'settings',
paths: ['sidebarCollapsed']
},
actions: {
// 切换侧边栏状态
toggleSidebar() {

View File

@ -2,6 +2,13 @@
<div class="chat-header">
<div class="header-content">
<div class="title">
<el-button
class="mobile-sidebar-toggle"
:icon="Expand"
@click="settingsStore.toggleSidebar"
text
v-if="isMobile"
/>
<span class="title-text">{{ title }}</span>
<el-tag v-if="chatStore.currentConversation?.conversationStatus === 'typing'"
size="small"
@ -49,10 +56,10 @@
</template>
<script setup>
import { ref, computed } from 'vue'
import { ref, computed, onMounted, onUnmounted } from 'vue'
import { useChatStore } from '@/store/chat'
import { useSettingsStore } from '@/store/settings'
import { Delete, Setting, User } from '@element-plus/icons-vue'
import { Delete, Setting, User, Expand } from '@element-plus/icons-vue'
import { ElMessageBox } from 'element-plus'
import HistoryButton from '@/components/chat/HistoryButton.vue'
import CustomerBackground from '@/components/chat/CustomerBackground.vue'
@ -61,6 +68,7 @@ const chatStore = useChatStore()
const settingsStore = useSettingsStore()
const showUserInfo = ref(false)
const customerBackgroundRef = ref(null)
const isMobile = ref(false)
const title = computed(() => {
const conversation = chatStore.currentConversation
@ -101,6 +109,19 @@ const handleSettings = () => {
// TODO:
console.log('打开设置')
}
const checkMobile = () => {
isMobile.value = window.innerWidth <= 768
}
onMounted(() => {
checkMobile()
window.addEventListener('resize', checkMobile)
})
onUnmounted(() => {
window.removeEventListener('resize', checkMobile)
})
</script>
<style lang="scss" scoped>
@ -121,7 +142,38 @@ const handleSettings = () => {
.title {
display: flex;
align-items: center;
gap: 12px;
gap: 4px;
height: 100%;
.mobile-sidebar-toggle {
height: 24px;
width: 24px;
padding: 0;
color: #666;
transition: all 0.3s ease;
border: none;
background: transparent !important;
font-size: 14px;
display: flex;
align-items: center;
justify-content: center;
margin-left: -4px;
&:hover {
color: #07c160;
background: transparent !important;
}
&:focus {
outline: none;
box-shadow: none;
}
:deep(.el-icon) {
font-size: 18px;
margin-top: 1px;
}
}
.title-text {
font-size: 16px;
@ -131,6 +183,8 @@ const handleSettings = () => {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
line-height: 24px;
padding-top: 1px;
}
.status-tag {

View File

@ -1,41 +1,49 @@
<template>
<div class="mode-selector" :class="{ 'collapsed': settingsStore.sidebarCollapsed }">
<div class="mode-header">
<h3 class="mode-title">对话场景</h3>
<div class="header-actions">
<HistoryButton v-if="!settingsStore.sidebarCollapsed" :icon-only="true" />
<el-button
class="collapse-btn"
:icon="settingsStore.sidebarCollapsed ? Expand : Fold"
text
bg
@click="settingsStore.toggleSidebar"
/>
<div>
<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">
<h3 class="mode-title">对话场景</h3>
<div class="header-actions">
<HistoryButton v-if="!settingsStore.sidebarCollapsed" :icon-only="true" />
<el-button
class="collapse-btn"
:icon="settingsStore.sidebarCollapsed ? Expand : Fold"
text
bg
@click="settingsStore.toggleSidebar"
/>
</div>
</div>
</div>
<div class="mode-list">
<div
v-for="mode in chatModes"
:key="mode.id"
:class="['mode-item', { active: chatStore.currentMode === mode.id }]"
@click="selectMode(mode.id)"
>
<span class="emoji-icon">{{ mode.icon }}</span>
<span v-if="!settingsStore.sidebarCollapsed" class="mode-name">{{ mode.name }}</span>
<ModeSetting v-if="!settingsStore.sidebarCollapsed" @command="(cmd) => handleModeSettings(cmd, mode.id)">
<el-dropdown-item command="edit">
<el-icon><EditPen /></el-icon>
编辑设置
</el-dropdown-item>
<el-dropdown-item command="reset">
<el-icon><RefreshRight /></el-icon>
重置设置
</el-dropdown-item>
<el-dropdown-item command="delete" divided>
<el-icon><Delete /></el-icon>
删除场景
</el-dropdown-item>
</ModeSetting>
<div class="mode-list">
<div
v-for="mode in chatModes"
:key="mode.id"
:class="['mode-item', { active: chatStore.currentMode === mode.id }]"
@click="selectMode(mode.id)"
>
<span class="emoji-icon">{{ mode.icon }}</span>
<span v-if="!settingsStore.sidebarCollapsed" class="mode-name">{{ mode.name }}</span>
<ModeSetting v-if="!settingsStore.sidebarCollapsed" @command="(cmd) => handleModeSettings(cmd, mode.id)">
<el-dropdown-item command="edit">
<el-icon><EditPen /></el-icon>
编辑设置
</el-dropdown-item>
<el-dropdown-item command="reset">
<el-icon><RefreshRight /></el-icon>
重置设置
</el-dropdown-item>
<el-dropdown-item command="delete" divided>
<el-icon><Delete /></el-icon>
删除场景
</el-dropdown-item>
</ModeSetting>
</div>
</div>
</div>
</div>
@ -44,7 +52,7 @@
<script setup>
import { useChatStore } from '@/store/chat'
import { useSettingsStore } from '@/store/settings'
import { computed } from 'vue'
import { computed, ref, onMounted, onUnmounted } from 'vue'
import { Delete, EditPen, RefreshRight, Expand, Fold } from '@element-plus/icons-vue'
import { ElDropdown, ElDropdownMenu, ElDropdownItem, ElButton, ElMessageBox } from 'element-plus'
import ModeSetting from '@/components/chat/ModeSetting.vue'
@ -117,6 +125,21 @@ const handleModeSettings = (command, modeId) => {
break
}
}
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>
@ -139,6 +162,8 @@ $hover-color-dark: #2d2d2d;
padding: 0;
box-shadow: 2px 0 5px rgba(0, 0, 0, 0.1);
transition: all 0.3s ease;
position: relative;
z-index: 1000;
&.collapsed {
width: 64px;
@ -349,6 +374,44 @@ $hover-color-dark: #2d2d2d;
}
}
/* 移动端样式 */
@media screen and (max-width: 768px) {
.mode-selector {
position: fixed;
left: 0;
top: 0;
bottom: 0;
transform: translateX(-100%);
z-index: 1000;
&.collapsed {
transform: translateX(-240px);
}
&:not(.collapsed) {
transform: translateX(0);
}
}
.mode-selector-backdrop {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
z-index: 999;
opacity: 0;
visibility: hidden;
transition: all 0.3s ease;
&.visible {
opacity: 1;
visibility: visible;
}
}
}
// @media (prefers-color-scheme: dark) {
// .mode-selector {
// background: $background-color-dark;

View File

@ -55,4 +55,20 @@ const handleModeChange = (mode) => {
background-color: #f5f5f5;
position: relative;
}
/* 移动端样式 */
@media screen and (max-width: 768px) {
.chat-container {
position: relative;
}
.chat-wrapper {
width: 100%;
position: absolute;
left: 0;
top: 0;
bottom: 0;
right: 0;
}
}
</style>