|
@@ -0,0 +1,192 @@
|
|
|
|
+<template>
|
|
|
|
+ <div class="flex h-screen">
|
|
|
|
+ <!-- 左侧侧边栏 -->
|
|
|
|
+ <aside class="w-[250px] bg-[#08002E] text-white flex flex-col">
|
|
|
|
+ <!-- 标题 -->
|
|
|
|
+ <div class="p-4 bg-[#4B0082] text-center">
|
|
|
|
+ <h1 class="text-lg font-bold">用户故事生成器</h1>
|
|
|
|
+ </div>
|
|
|
|
+
|
|
|
|
+ <!-- 搜索框 -->
|
|
|
|
+ <div class="p-2 border-b border-gray-700">
|
|
|
|
+ <input
|
|
|
|
+ v-model="searchQuery"
|
|
|
|
+ placeholder="搜索联系人..."
|
|
|
|
+ class="w-full p-2 text-sm bg-[#1A1A2E] text-white rounded focus:outline-none"
|
|
|
|
+ />
|
|
|
|
+ </div>
|
|
|
|
+
|
|
|
|
+ <!-- 联系人列表 -->
|
|
|
|
+ <div class="flex-1 overflow-y-auto">
|
|
|
|
+ <div
|
|
|
|
+ v-for="contact in filteredContacts"
|
|
|
|
+ :key="contact.id"
|
|
|
|
+ @click="selectContact(contact)"
|
|
|
|
+ class="flex items-center p-3 cursor-pointer hover:bg-[#1A1A2E]"
|
|
|
|
+ :class="{ 'bg-[#1A1A2E]': selectedContact?.id === contact.id }"
|
|
|
|
+ >
|
|
|
|
+ <div class="w-8 h-8 bg-gray-600 rounded-full flex items-center justify-center">
|
|
|
|
+ {{ contact.name.charAt(0) }}
|
|
|
|
+ </div>
|
|
|
|
+ <span class="ml-3 text-sm">{{ contact.name }}</span>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+ </aside>
|
|
|
|
+
|
|
|
|
+ <!-- 右侧聊天区域 -->
|
|
|
|
+ <main class="flex-1 flex flex-col bg-gradient-to-b from-white to-purple-100">
|
|
|
|
+ <!-- 聊天内容 -->
|
|
|
|
+ <div ref="chatContainer" class="flex-1 overflow-y-auto p-4 space-y-4">
|
|
|
|
+ <div
|
|
|
|
+ v-for="(message, index) in messages"
|
|
|
|
+ :key="index"
|
|
|
|
+ :class="message.sender === 'user' ? 'justify-end' : 'justify-start'"
|
|
|
|
+ class="flex"
|
|
|
|
+ >
|
|
|
|
+ <div
|
|
|
|
+ class="max-w-[70%] p-3 rounded-lg"
|
|
|
|
+ :class="{
|
|
|
|
+ 'bg-gray-200': message.sender === 'user',
|
|
|
|
+ 'bg-[#ccffcc]': message.sender !== 'user'
|
|
|
|
+ }"
|
|
|
|
+ >
|
|
|
|
+ <div class="text-sm">{{ message.content }}</div>
|
|
|
|
+ <div class="mt-1 text-xs text-gray-500">{{ message.time }}</div>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+
|
|
|
|
+ <!-- 输入区域 -->
|
|
|
|
+ <div class="p-4 border-t bg-white">
|
|
|
|
+ <div class="flex items-center gap-2">
|
|
|
|
+ <!-- 文件上传 -->
|
|
|
|
+ <div class="relative">
|
|
|
|
+ <input
|
|
|
|
+ type="file"
|
|
|
|
+ @change="handleFileUpload"
|
|
|
|
+ class="hidden"
|
|
|
|
+ id="fileInput"
|
|
|
|
+ />
|
|
|
|
+ <label
|
|
|
|
+ for="fileInput"
|
|
|
|
+ class="w-10 h-10 bg-purple-500 rounded-full flex items-center justify-center cursor-pointer hover:bg-purple-600"
|
|
|
|
+ >
|
|
|
|
+ <Upload class="w-5 h-5 text-white" />
|
|
|
|
+ </label>
|
|
|
|
+ <span v-if="uploadedFile" class="ml-2 text-sm text-gray-600">
|
|
|
|
+ {{ uploadedFile.name }}
|
|
|
|
+ </span>
|
|
|
|
+ </div>
|
|
|
|
+
|
|
|
|
+ <!-- 消息输入框 -->
|
|
|
|
+ <input
|
|
|
|
+ v-model="newMessage"
|
|
|
|
+ @keyup.enter="sendMessage"
|
|
|
|
+ placeholder="输入消息..."
|
|
|
|
+ class="flex-1 p-2 border rounded-lg focus:outline-none"
|
|
|
|
+ />
|
|
|
|
+
|
|
|
|
+ <!-- 发送按钮 -->
|
|
|
|
+ <button
|
|
|
|
+ @click="sendMessage"
|
|
|
|
+ class="p-2 bg-purple-500 text-white rounded-lg hover:bg-purple-600"
|
|
|
|
+ >
|
|
|
|
+ <Send class="w-5 h-5" />
|
|
|
|
+ </button>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+ </main>
|
|
|
|
+ </div>
|
|
|
|
+</template>
|
|
|
|
+
|
|
|
|
+<script setup>
|
|
|
|
+import { ref, computed, onMounted, nextTick } from 'vue'
|
|
|
|
+import { Upload, Send } from 'lucide-vue-next'
|
|
|
|
+
|
|
|
|
+// 联系人数据
|
|
|
|
+const contacts = ref([
|
|
|
|
+ { id: 1, name: '产品经理' },
|
|
|
|
+ { id: 2, name: '开发团队' },
|
|
|
|
+ { id: 3, name: '测试团队' }
|
|
|
|
+])
|
|
|
|
+
|
|
|
|
+// 选中联系人
|
|
|
|
+const selectedContact = ref(null)
|
|
|
|
+const searchQuery = ref('')
|
|
|
|
+
|
|
|
|
+// 消息相关
|
|
|
|
+const messages = ref([])
|
|
|
|
+const newMessage = ref('')
|
|
|
|
+const chatContainer = ref(null)
|
|
|
|
+const uploadedFile = ref(null)
|
|
|
|
+
|
|
|
|
+// 过滤后的联系人列表
|
|
|
|
+const filteredContacts = computed(() => {
|
|
|
|
+ return contacts.value.filter(contact =>
|
|
|
|
+ contact.name.toLowerCase().includes(searchQuery.value.toLowerCase())
|
|
|
|
+ )
|
|
|
|
+})
|
|
|
|
+
|
|
|
|
+// 发送消息
|
|
|
|
+const sendMessage = () => {
|
|
|
|
+ if (!newMessage.value.trim()) return
|
|
|
|
+
|
|
|
|
+ const message = {
|
|
|
|
+ content: newMessage.value,
|
|
|
|
+ sender: 'user',
|
|
|
|
+ time: new Date().toLocaleTimeString()
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ messages.value.push(message)
|
|
|
|
+ newMessage.value = ''
|
|
|
|
+
|
|
|
|
+ // 自动滚动到底部
|
|
|
|
+ nextTick(() => {
|
|
|
|
+ chatContainer.value.scrollTop = chatContainer.value.scrollHeight
|
|
|
|
+ })
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// 处理文件上传
|
|
|
|
+const handleFileUpload = (event) => {
|
|
|
|
+ const file = event.target.files[0]
|
|
|
|
+ if (file) {
|
|
|
|
+ uploadedFile.value = file
|
|
|
|
+ messages.value.push({
|
|
|
|
+ content: `已上传文件: ${file.name}`,
|
|
|
|
+ sender: 'system',
|
|
|
|
+ time: new Date().toLocaleTimeString()
|
|
|
|
+ })
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// 初始化时加载示例消息
|
|
|
|
+onMounted(() => {
|
|
|
|
+ messages.value = [
|
|
|
|
+ {
|
|
|
|
+ content: '请描述您的用户故事需求',
|
|
|
|
+ sender: 'assistant',
|
|
|
|
+ time: new Date().toLocaleTimeString()
|
|
|
|
+ }
|
|
|
|
+ ]
|
|
|
|
+})
|
|
|
|
+</script>
|
|
|
|
+
|
|
|
|
+<style scoped>
|
|
|
|
+/* 自定义滚动条样式 */
|
|
|
|
+::-webkit-scrollbar {
|
|
|
|
+ width: 6px;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+::-webkit-scrollbar-track {
|
|
|
|
+ background: #f1f1f1;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+::-webkit-scrollbar-thumb {
|
|
|
|
+ background: #888;
|
|
|
|
+ border-radius: 3px;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+::-webkit-scrollbar-thumb:hover {
|
|
|
|
+ background: #555;
|
|
|
|
+}
|
|
|
|
+</style>
|