|
@@ -0,0 +1,269 @@
|
|
|
+<template>
|
|
|
+ <div class="chat-container">
|
|
|
+ <aside class="sidebar">
|
|
|
+ <div class="sidebar-header">
|
|
|
+ <h2>应用标题</h2>
|
|
|
+ </div>
|
|
|
+ <div class="user-info">
|
|
|
+ <p>用户名</p>
|
|
|
+ </div>
|
|
|
+ <div class="contact-list">
|
|
|
+ <a-list item-layout="horizontal" :data-source="contacts">
|
|
|
+ <template #renderItem="{ item }">
|
|
|
+ <a-list-item>
|
|
|
+ <a-list-item-meta>
|
|
|
+ <template #avatar>
|
|
|
+ <a-avatar :src="item.avatar" />
|
|
|
+ </template>
|
|
|
+ <template #title>
|
|
|
+ <a href="javascript:;" @click="selectContact(item)">{{ item.name }}</a>
|
|
|
+ </template>
|
|
|
+ <template #description>
|
|
|
+ {{ item.lastMessage }}
|
|
|
+ </template>
|
|
|
+ </a-list-item-meta>
|
|
|
+ </a-list-item>
|
|
|
+ </template>
|
|
|
+ </a-list>
|
|
|
+ </div>
|
|
|
+ </aside>
|
|
|
+ <main class="chat-area">
|
|
|
+ <div class="message-container" ref="messageContainer">
|
|
|
+ <a-list item-layout="horizontal" :data-source="messages">
|
|
|
+ <template #renderItem="{ item }">
|
|
|
+ <a-list-item>
|
|
|
+ <a-list-item-meta>
|
|
|
+ <template #description>
|
|
|
+ <div :class="['message-bubble', item.sender === 'self' ? 'self' : 'other']">
|
|
|
+ <p>{{ item.text }}</p>
|
|
|
+ <span class="timestamp">{{ item.timestamp }}</span>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </a-list-item-meta>
|
|
|
+ </a-list-item>
|
|
|
+ </template>
|
|
|
+ </a-list>
|
|
|
+ </div>
|
|
|
+ <div class="input-area">
|
|
|
+ <a-input v-model:value="inputText" placeholder="输入消息..." @pressEnter="sendMessage" />
|
|
|
+ <a-button type="primary" @click="sendMessage">发送</a-button>
|
|
|
+ <a-upload :before-upload="handleUpload" :show-upload-list="false">
|
|
|
+ <a-button icon="upload">上传文件</a-button>
|
|
|
+ </a-upload>
|
|
|
+ </div>
|
|
|
+ </main>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+import { ref, reactive, onMounted, nextTick } from 'vue';
|
|
|
+import { message } from 'ant-design-vue';
|
|
|
+
|
|
|
+export default {
|
|
|
+ setup() {
|
|
|
+ const contacts = reactive([
|
|
|
+ { id: 1, avatar: '@/assets/img/user-avatar.png', name: '联系人1', lastMessage: '你好!' },
|
|
|
+ { id: 2, avatar: '@/assets/img/user-avatar.png', name: '联系人2', lastMessage: '最近怎么样?' },
|
|
|
+ ]);
|
|
|
+
|
|
|
+ const messages = reactive([]);
|
|
|
+ const inputText = ref('');
|
|
|
+ const selectedContact = ref(null);
|
|
|
+ const messageContainer = ref(null);
|
|
|
+
|
|
|
+ const selectContact = (contact) => {
|
|
|
+ selectedContact.value = contact;
|
|
|
+ // Load messages for the selected contact
|
|
|
+ messages.length = 0; // Clear previous messages
|
|
|
+ messages.push(
|
|
|
+ { sender: 'other', text: '你好!', timestamp: '10:00 AM' },
|
|
|
+ { sender: 'self', text: '你好!', timestamp: '10:01 AM' }
|
|
|
+ );
|
|
|
+ nextTick(() => {
|
|
|
+ scrollToBottom();
|
|
|
+ });
|
|
|
+ };
|
|
|
+
|
|
|
+ const sendMessage = () => {
|
|
|
+ if (inputText.value.trim()) {
|
|
|
+ messages.push({
|
|
|
+ sender: 'self',
|
|
|
+ text: inputText.value,
|
|
|
+ timestamp: new Date().toLocaleTimeString(),
|
|
|
+ });
|
|
|
+ inputText.value = '';
|
|
|
+ nextTick(() => {
|
|
|
+ scrollToBottom();
|
|
|
+ });
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ const handleUpload = (file) => {
|
|
|
+ message.info(`上传文件: ${file.name}`);
|
|
|
+ messages.push({
|
|
|
+ sender: 'self',
|
|
|
+ text: `上传了文件: ${file.name}`,
|
|
|
+ timestamp: new Date().toLocaleTimeString(),
|
|
|
+ });
|
|
|
+ nextTick(() => {
|
|
|
+ scrollToBottom();
|
|
|
+ });
|
|
|
+ return false; // Prevent default upload behavior
|
|
|
+ };
|
|
|
+
|
|
|
+ const scrollToBottom = () => {
|
|
|
+ if (messageContainer.value) {
|
|
|
+ messageContainer.value.scrollTop = messageContainer.value.scrollHeight;
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ onMounted(() => {
|
|
|
+ selectContact(contacts[0]); // Select the first contact by default
|
|
|
+ });
|
|
|
+
|
|
|
+ return {
|
|
|
+ contacts,
|
|
|
+ messages,
|
|
|
+ inputText,
|
|
|
+ selectedContact,
|
|
|
+ messageContainer,
|
|
|
+ selectContact,
|
|
|
+ sendMessage,
|
|
|
+ handleUpload,
|
|
|
+ };
|
|
|
+ },
|
|
|
+};
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+.chat-container {
|
|
|
+ display: flex;
|
|
|
+ height: 100vh;
|
|
|
+ background-color: #f0f0f0;
|
|
|
+}
|
|
|
+
|
|
|
+.sidebar {
|
|
|
+ width: 250px;
|
|
|
+ background-color: #08002e;
|
|
|
+ color: white;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+}
|
|
|
+
|
|
|
+.sidebar-header {
|
|
|
+ background-color: #6a0dad;
|
|
|
+ padding: 10px 0;
|
|
|
+ text-align: center;
|
|
|
+}
|
|
|
+
|
|
|
+.sidebar-header h2 {
|
|
|
+ margin: 0;
|
|
|
+ font-size: 18px;
|
|
|
+ font-weight: bold;
|
|
|
+}
|
|
|
+
|
|
|
+.user-info {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ padding: 10px;
|
|
|
+ border-bottom: 1px solid #440088;
|
|
|
+}
|
|
|
+
|
|
|
+.user-info .avatar {
|
|
|
+ width: 40px;
|
|
|
+ height: 40px;
|
|
|
+ border-radius: 50%;
|
|
|
+ margin-right: 10px;
|
|
|
+}
|
|
|
+
|
|
|
+.contact-list {
|
|
|
+ flex: 1;
|
|
|
+ overflow-y: auto;
|
|
|
+}
|
|
|
+
|
|
|
+.chat-area {
|
|
|
+ flex: 1;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+}
|
|
|
+
|
|
|
+.message-container {
|
|
|
+ flex: 1;
|
|
|
+ padding: 10px;
|
|
|
+ overflow-y: auto;
|
|
|
+ background-color: #f0f0f0;
|
|
|
+}
|
|
|
+
|
|
|
+.message-bubble {
|
|
|
+ max-width: 70%;
|
|
|
+ margin: 5px 0;
|
|
|
+ padding: 10px;
|
|
|
+ border-radius: 15px;
|
|
|
+ position: relative;
|
|
|
+}
|
|
|
+
|
|
|
+.message-bubble.self {
|
|
|
+ background-color: #e0c3fc;
|
|
|
+ align-self: flex-end;
|
|
|
+}
|
|
|
+
|
|
|
+.message-bubble.other {
|
|
|
+ background-color: #ffffff;
|
|
|
+ align-self: flex-start;
|
|
|
+}
|
|
|
+
|
|
|
+.message-bubble p {
|
|
|
+ margin: 0;
|
|
|
+}
|
|
|
+
|
|
|
+.timestamp {
|
|
|
+ font-size: 12px;
|
|
|
+ color: #888;
|
|
|
+ position: absolute;
|
|
|
+ bottom: -15px;
|
|
|
+ left: 50%;
|
|
|
+ transform: translateX(-50%);
|
|
|
+}
|
|
|
+
|
|
|
+.input-area {
|
|
|
+ display: flex;
|
|
|
+ padding: 10px;
|
|
|
+ background-color: #ffffff;
|
|
|
+ border-top: 1px solid #ddd;
|
|
|
+}
|
|
|
+
|
|
|
+.input-area .ant-input {
|
|
|
+ flex: 1;
|
|
|
+ margin-right: 10px;
|
|
|
+}
|
|
|
+
|
|
|
+.input-area .ant-btn {
|
|
|
+ background-color: #6a0dad;
|
|
|
+ color: white;
|
|
|
+ border: none;
|
|
|
+}
|
|
|
+
|
|
|
+.input-area .ant-btn:hover {
|
|
|
+ background-color: #5b008a;
|
|
|
+}
|
|
|
+
|
|
|
+.input-area .ant-upload.ant-upload-select {
|
|
|
+ margin-left: 10px;
|
|
|
+}
|
|
|
+
|
|
|
+.input-area .ant-upload-select-button {
|
|
|
+ background-color: #6a0dad;
|
|
|
+ color: white;
|
|
|
+ border: none;
|
|
|
+ border-radius: 50%;
|
|
|
+ width: 32px;
|
|
|
+ height: 32px;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+}
|
|
|
+
|
|
|
+.input-area .ant-upload-select-button:hover {
|
|
|
+ background-color: #5b008a;
|
|
|
+}
|
|
|
+</style>
|