| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182 |
- <template>
- <div class="chat-page">
- <div class="chat-header">
- <el-button text @click="router.back()">
- <el-icon><ArrowLeft /></el-icon>
- </el-button>
- <span class="chat-partner">{{ partnerName }}</span>
- </div>
- <div class="message-list" ref="messageListRef">
- <div
- v-for="msg in messages"
- :key="msg.id"
- :class="['message-row', msg.fromUserId === currentUserId ? 'own' : 'other']"
- >
- <el-avatar :src="msg.fromUserAvatar" :size="32" />
- <div class="message-bubble">
- <p>{{ msg.content }}</p>
- <el-image v-if="msg.imageUrl" :src="msg.imageUrl" fit="cover" class="msg-image" />
- </div>
- </div>
- <div v-if="loading" class="loading-more">
- <el-icon class="is-loading"><Loading /></el-icon>
- </div>
- </div>
- <div class="input-area">
- <el-input
- v-model="text"
- type="textarea"
- :rows="2"
- placeholder="输入消息..."
- @keyup.enter.ctrl="handleSend"
- />
- <el-button type="primary" @click="handleSend">
- 发送
- </el-button>
- </div>
- </div>
- </template>
- <script setup lang="ts">
- import { ref, onMounted, nextTick } from 'vue'
- import { useRoute, useRouter } from 'vue-router'
- import { ElMessage } from 'element-plus'
- import { ArrowLeft, Loading } from '@element-plus/icons-vue'
- import { getMessages } from '../api/endpoints/messages'
- import { useUserStore } from '../store'
- import type { MessageVO } from '../api/models'
- const route = useRoute()
- const router = useRouter()
- const userStore = useUserStore()
- const messageListRef = ref<HTMLElement | null>(null)
- const messages = ref<MessageVO[]>([])
- const text = ref('')
- const loading = ref(false)
- const partnerName = ref('')
- const currentUserId = userStore.state.userId
- const targetUserId = Number(route.params.userId)
- const scrollToBottom = async () => {
- await nextTick()
- if (messageListRef.value) {
- messageListRef.value.scrollTop = messageListRef.value.scrollHeight
- }
- }
- const loadMessages = async () => {
- loading.value = true
- try {
- const res = await getMessages().history(targetUserId)
- if (res.code === 200) {
- messages.value = (res.data?.list ?? []).reverse()
- if (messages.value.length > 0) {
- partnerName.value = messages.value[0].fromUserId === currentUserId
- ? (messages.value[0].toUserNickname ?? '')
- : (messages.value[0].fromUserNickname ?? '')
- }
- await scrollToBottom()
- }
- } finally {
- loading.value = false
- }
- }
- const handleSend = async () => {
- if (!text.value.trim()) return
- try {
- const res = await getMessages().send({ toUserId: targetUserId, content: text.value.trim() })
- if (res.code === 200) {
- messages.value.push({
- id: res.data,
- fromUserId: currentUserId,
- content: text.value.trim(),
- createdAt: new Date().toISOString(),
- } as MessageVO)
- text.value = ''
- await scrollToBottom()
- }
- } catch (err: unknown) {
- if (err && typeof err === 'object' && 'message' in err) {
- ElMessage.error((err as { message: string }).message)
- }
- }
- }
- onMounted(loadMessages)
- </script>
- <style scoped>
- .chat-page {
- display: flex;
- flex-direction: column;
- height: calc(100vh - 120px);
- max-width: 700px;
- margin: 0 auto;
- }
- .chat-header {
- display: flex;
- align-items: center;
- gap: 12px;
- padding: 12px 16px;
- border-bottom: 1px solid #e4e7ed;
- background: #fff;
- }
- .chat-partner {
- font-size: 16px;
- font-weight: 500;
- }
- .message-list {
- flex: 1;
- overflow-y: auto;
- padding: 16px;
- }
- .message-row {
- display: flex;
- gap: 8px;
- margin-bottom: 16px;
- }
- .message-row.own {
- flex-direction: row-reverse;
- }
- .message-bubble {
- max-width: 70%;
- padding: 10px 14px;
- border-radius: 12px;
- font-size: 14px;
- line-height: 1.5;
- }
- .message-row.other .message-bubble {
- background: #f0f0f0;
- border-bottom-left-radius: 4px;
- }
- .message-row.own .message-bubble {
- background: #409eff;
- color: #fff;
- border-bottom-right-radius: 4px;
- }
- .msg-image {
- width: 120px;
- height: 120px;
- border-radius: 4px;
- margin-top: 8px;
- cursor: pointer;
- }
- .loading-more {
- text-align: center;
- padding: 8px;
- }
- .input-area {
- display: flex;
- gap: 8px;
- padding: 12px 16px;
- border-top: 1px solid #e4e7ed;
- background: #fff;
- }
- .input-area .el-button {
- align-self: flex-end;
- }
- </style>
|