209 lines
4.5 KiB
Plaintext
209 lines
4.5 KiB
Plaintext
<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>
|