Compare commits
3 Commits
f6555e5726
...
0d3d30952d
Author | SHA1 | Date |
---|---|---|
|
0d3d30952d | |
|
f0ee5102af | |
|
1fcc3432c1 |
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="main-layout">
|
<div class="main-layout">
|
||||||
<Sidebar />
|
<Sidebar class="sidebar" />
|
||||||
<main class="content-area">
|
<main class="content-area">
|
||||||
<!-- 路由视图将渲染在这里 -->
|
<!-- 路由视图将渲染在这里 -->
|
||||||
<router-view />
|
<router-view />
|
||||||
|
@ -16,7 +16,7 @@ import Sidebar from '../components/Sidebar.vue'
|
||||||
.main-layout {
|
.main-layout {
|
||||||
display: flex;
|
display: flex;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
min-width: 640px;
|
/* min-width: 640px; */
|
||||||
overflow: hidden; /* 防止内部滚动影响布局 */
|
overflow: hidden; /* 防止内部滚动影响布局 */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,6 +28,21 @@ import Sidebar from '../components/Sidebar.vue'
|
||||||
background-color: #ffffff; /* 示例背景色 */
|
background-color: #ffffff; /* 示例背景色 */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sidebar {
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 移动端样式 */
|
||||||
|
@media screen and (max-width: 768px) {
|
||||||
|
.sidebar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-area {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* 简单的侧边栏列表样式 */
|
/* 简单的侧边栏列表样式 */
|
||||||
.sidebar ul {
|
.sidebar ul {
|
||||||
list-style: none;
|
list-style: none;
|
||||||
|
|
|
@ -26,6 +26,11 @@ export const useSettingsStore = defineStore('settings', {
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
persist: {
|
||||||
|
key: 'settings',
|
||||||
|
paths: ['sidebarCollapsed']
|
||||||
|
},
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
// 切换侧边栏状态
|
// 切换侧边栏状态
|
||||||
toggleSidebar() {
|
toggleSidebar() {
|
||||||
|
|
|
@ -2,6 +2,13 @@
|
||||||
<div class="chat-header">
|
<div class="chat-header">
|
||||||
<div class="header-content">
|
<div class="header-content">
|
||||||
<div class="title">
|
<div class="title">
|
||||||
|
<el-button
|
||||||
|
class="mobile-sidebar-toggle"
|
||||||
|
:icon="Expand"
|
||||||
|
@click="settingsStore.toggleSidebar"
|
||||||
|
text
|
||||||
|
v-if="isMobile"
|
||||||
|
/>
|
||||||
<span class="title-text">{{ title }}</span>
|
<span class="title-text">{{ title }}</span>
|
||||||
<el-tag v-if="chatStore.currentConversation?.conversationStatus === 'typing'"
|
<el-tag v-if="chatStore.currentConversation?.conversationStatus === 'typing'"
|
||||||
size="small"
|
size="small"
|
||||||
|
@ -49,10 +56,10 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed } from 'vue'
|
import { ref, computed, onMounted, onUnmounted } from 'vue'
|
||||||
import { useChatStore } from '@/store/chat'
|
import { useChatStore } from '@/store/chat'
|
||||||
import { useSettingsStore } from '@/store/settings'
|
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 { ElMessageBox } from 'element-plus'
|
||||||
import HistoryButton from '@/components/chat/HistoryButton.vue'
|
import HistoryButton from '@/components/chat/HistoryButton.vue'
|
||||||
import CustomerBackground from '@/components/chat/CustomerBackground.vue'
|
import CustomerBackground from '@/components/chat/CustomerBackground.vue'
|
||||||
|
@ -61,6 +68,7 @@ const chatStore = useChatStore()
|
||||||
const settingsStore = useSettingsStore()
|
const settingsStore = useSettingsStore()
|
||||||
const showUserInfo = ref(false)
|
const showUserInfo = ref(false)
|
||||||
const customerBackgroundRef = ref(null)
|
const customerBackgroundRef = ref(null)
|
||||||
|
const isMobile = ref(false)
|
||||||
|
|
||||||
const title = computed(() => {
|
const title = computed(() => {
|
||||||
const conversation = chatStore.currentConversation
|
const conversation = chatStore.currentConversation
|
||||||
|
@ -101,6 +109,19 @@ const handleSettings = () => {
|
||||||
// TODO: 实现设置功能
|
// TODO: 实现设置功能
|
||||||
console.log('打开设置')
|
console.log('打开设置')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const checkMobile = () => {
|
||||||
|
isMobile.value = window.innerWidth <= 768
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
checkMobile()
|
||||||
|
window.addEventListener('resize', checkMobile)
|
||||||
|
})
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
window.removeEventListener('resize', checkMobile)
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
@ -121,7 +142,38 @@ const handleSettings = () => {
|
||||||
.title {
|
.title {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
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 {
|
.title-text {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
|
@ -131,6 +183,8 @@ const handleSettings = () => {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
|
line-height: 24px;
|
||||||
|
padding-top: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-tag {
|
.status-tag {
|
||||||
|
|
|
@ -1,41 +1,49 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="mode-selector" :class="{ 'collapsed': settingsStore.sidebarCollapsed }">
|
<div>
|
||||||
<div class="mode-header">
|
<div
|
||||||
<h3 class="mode-title">对话场景</h3>
|
class="mode-selector-backdrop"
|
||||||
<div class="header-actions">
|
:class="{ visible: !settingsStore.sidebarCollapsed }"
|
||||||
<HistoryButton v-if="!settingsStore.sidebarCollapsed" :icon-only="true" />
|
@click="settingsStore.toggleSidebar"
|
||||||
<el-button
|
v-if="isMobile"
|
||||||
class="collapse-btn"
|
></div>
|
||||||
:icon="settingsStore.sidebarCollapsed ? Expand : Fold"
|
<div class="mode-selector" :class="{ 'collapsed': settingsStore.sidebarCollapsed }">
|
||||||
text
|
<div class="mode-header">
|
||||||
bg
|
<h3 class="mode-title">对话场景</h3>
|
||||||
@click="settingsStore.toggleSidebar"
|
<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>
|
<div class="mode-list">
|
||||||
<div class="mode-list">
|
<div
|
||||||
<div
|
v-for="mode in chatModes"
|
||||||
v-for="mode in chatModes"
|
:key="mode.id"
|
||||||
:key="mode.id"
|
:class="['mode-item', { active: chatStore.currentMode === mode.id }]"
|
||||||
:class="['mode-item', { active: chatStore.currentMode === mode.id }]"
|
@click="selectMode(mode.id)"
|
||||||
@click="selectMode(mode.id)"
|
>
|
||||||
>
|
<span class="emoji-icon">{{ mode.icon }}</span>
|
||||||
<span class="emoji-icon">{{ mode.icon }}</span>
|
<span v-if="!settingsStore.sidebarCollapsed" class="mode-name">{{ mode.name }}</span>
|
||||||
<span v-if="!settingsStore.sidebarCollapsed" class="mode-name">{{ mode.name }}</span>
|
<ModeSetting v-if="!settingsStore.sidebarCollapsed" @command="(cmd) => handleModeSettings(cmd, mode.id)">
|
||||||
<ModeSetting v-if="!settingsStore.sidebarCollapsed" @command="(cmd) => handleModeSettings(cmd, mode.id)">
|
<el-dropdown-item command="edit">
|
||||||
<el-dropdown-item command="edit">
|
<el-icon><EditPen /></el-icon>
|
||||||
<el-icon><EditPen /></el-icon>
|
编辑设置
|
||||||
编辑设置
|
</el-dropdown-item>
|
||||||
</el-dropdown-item>
|
<el-dropdown-item command="reset">
|
||||||
<el-dropdown-item command="reset">
|
<el-icon><RefreshRight /></el-icon>
|
||||||
<el-icon><RefreshRight /></el-icon>
|
重置设置
|
||||||
重置设置
|
</el-dropdown-item>
|
||||||
</el-dropdown-item>
|
<el-dropdown-item command="delete" divided>
|
||||||
<el-dropdown-item command="delete" divided>
|
<el-icon><Delete /></el-icon>
|
||||||
<el-icon><Delete /></el-icon>
|
删除场景
|
||||||
删除场景
|
</el-dropdown-item>
|
||||||
</el-dropdown-item>
|
</ModeSetting>
|
||||||
</ModeSetting>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -44,7 +52,7 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { useChatStore } from '@/store/chat'
|
import { useChatStore } from '@/store/chat'
|
||||||
import { useSettingsStore } from '@/store/settings'
|
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 { Delete, EditPen, RefreshRight, Expand, Fold } from '@element-plus/icons-vue'
|
||||||
import { ElDropdown, ElDropdownMenu, ElDropdownItem, ElButton, ElMessageBox } from 'element-plus'
|
import { ElDropdown, ElDropdownMenu, ElDropdownItem, ElButton, ElMessageBox } from 'element-plus'
|
||||||
import ModeSetting from '@/components/chat/ModeSetting.vue'
|
import ModeSetting from '@/components/chat/ModeSetting.vue'
|
||||||
|
@ -117,6 +125,21 @@ const handleModeSettings = (command, modeId) => {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isMobile = ref(false)
|
||||||
|
|
||||||
|
const checkMobile = () => {
|
||||||
|
isMobile.value = window.innerWidth <= 768
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
checkMobile()
|
||||||
|
window.addEventListener('resize', checkMobile)
|
||||||
|
})
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
window.removeEventListener('resize', checkMobile)
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
@ -139,6 +162,8 @@ $hover-color-dark: #2d2d2d;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
box-shadow: 2px 0 5px rgba(0, 0, 0, 0.1);
|
box-shadow: 2px 0 5px rgba(0, 0, 0, 0.1);
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
|
position: relative;
|
||||||
|
z-index: 1000;
|
||||||
|
|
||||||
&.collapsed {
|
&.collapsed {
|
||||||
width: 64px;
|
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) {
|
// @media (prefers-color-scheme: dark) {
|
||||||
// .mode-selector {
|
// .mode-selector {
|
||||||
// background: $background-color-dark;
|
// background: $background-color-dark;
|
||||||
|
|
|
@ -55,4 +55,20 @@ const handleModeChange = (mode) => {
|
||||||
background-color: #f5f5f5;
|
background-color: #f5f5f5;
|
||||||
position: relative;
|
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>
|
</style>
|
||||||
|
|
Loading…
Reference in New Issue