Files
assistant/components/AppDrawer.uvue

209 lines
4.5 KiB
Plaintext
Raw Normal View History

<template>
<!-- Overlay -->
<view
v-if="visible"
class="drawer-overlay"
:class="{ 'drawer-overlay--visible': visible }"
@tap="close"
@touchmove.stop.prevent="() => {}"
></view>
<!-- Drawer -->
<view
class="drawer"
:class="{ 'drawer--open': visible }"
@touchstart="onTouchStart"
@touchmove="onTouchMove"
@touchend="onTouchEnd"
>
<!-- Header -->
<DrawerHeader />
<!-- Scrollable content -->
<scroll-view class="drawer__content" scroll-y="true" show-scrollbar="false">
<!-- New conversation button -->
<view class="drawer__new-chat" @tap="onNewConversation">
<text class="drawer__new-chat-icon">+</text>
<text class="drawer__new-chat-text">新建对话</text>
</view>
<!-- History section -->
<HistoryList
:conversations="conversations"
@select="onSelectConversation"
@delete="onDeleteConversation"
/>
<!-- Divider -->
<view class="drawer__divider"></view>
<!-- Workspace section -->
<WorkspaceView
:items="workspaceItems"
@select="onSelectWorkspace"
/>
</scroll-view>
</view>
</template>
<script setup lang="uts">
import type { Conversation, WorkspaceItem } from '@/types/index'
import DrawerHeader from './DrawerHeader.uvue'
import HistoryList from './HistoryList.uvue'
import WorkspaceView from './WorkspaceView.uvue'
const props = defineProps<{
visible: boolean
conversations: Conversation[]
workspaceItems: WorkspaceItem[]
}>()
const emit = defineEmits<{
close: []
'new-conversation': []
'select-conversation': [id: string]
'delete-conversation': [id: string]
'select-workspace': [id: string]
}>()
// Swipe-to-close tracking
const touchStartX = ref(0)
const touchStartY = ref(0)
const drawerTranslateX = ref(0)
const isDragging = ref(false)
function close(): void {
emit('close')
}
function onTouchStart(e: any): void {
const touch = e.touches[0]
touchStartX.value = touch.clientX
touchStartY.value = touch.clientY
isDragging.value = true
}
function onTouchMove(e: any): void {
if (!isDragging.value) return
const touch = e.touches[0]
const deltaX = touch.clientX - touchStartX.value
const deltaY = touch.clientY - touchStartY.value
// Only track horizontal swipes (left direction)
if (Math.abs(deltaX) > Math.abs(deltaY) && deltaX < 0) {
drawerTranslateX.value = deltaX
}
}
function onTouchEnd(_e: any): void {
isDragging.value = false
// If swiped more than 80rpx left, close drawer
if (drawerTranslateX.value < -80) {
close()
}
drawerTranslateX.value = 0
}
function onNewConversation(): void {
emit('new-conversation')
close()
}
function onSelectConversation(id: string): void {
emit('select-conversation', id)
close()
}
function onDeleteConversation(id: string): void {
emit('delete-conversation', id)
}
function onSelectWorkspace(id: string): void {
emit('select-workspace', id)
close()
}
</script>
<style scoped>
/* Overlay */
.drawer-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.55);
z-index: 999;
opacity: 0;
transition: opacity 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.drawer-overlay--visible {
opacity: 1;
}
/* Drawer */
.drawer {
position: fixed;
top: 0;
left: 0;
bottom: 0;
width: 600rpx;
background: #1a1a2e;
z-index: 1000;
transform: translateX(-100%);
transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
display: flex;
flex-direction: column;
box-shadow: 8rpx 0 40rpx rgba(0, 0, 0, 0.5);
}
.drawer--open {
transform: translateX(0);
}
/* Content area */
.drawer__content {
flex: 1;
overflow-y: auto;
}
/* New chat button */
.drawer__new-chat {
display: flex;
flex-direction: row;
align-items: center;
padding: 20rpx 24rpx;
margin: 8rpx 20rpx;
border-radius: 16rpx;
background: linear-gradient(135deg, rgba(108, 92, 231, 0.2), rgba(162, 155, 254, 0.1));
border: 1px solid rgba(108, 92, 231, 0.25);
transition: all 0.2s ease;
}
.drawer__new-chat:active {
background: linear-gradient(135deg, rgba(108, 92, 231, 0.35), rgba(162, 155, 254, 0.2));
border-color: rgba(108, 92, 231, 0.45);
}
.drawer__new-chat-icon {
font-size: 36rpx;
color: #a29bfe;
font-weight: 300;
margin-right: 12rpx;
}
.drawer__new-chat-text {
font-size: 28rpx;
color: rgba(255, 255, 255, 0.85);
font-weight: 500;
}
/* Divider between history and workspace */
.drawer__divider {
height: 1px;
background: rgba(255, 255, 255, 0.06);
margin: 16rpx 24rpx;
}
</style>