From a9f0ac1217d4eacecfff483477dd50982e69c074 Mon Sep 17 00:00:00 2001 From: liqi Date: Wed, 3 Dec 2025 18:30:55 +0800 Subject: [PATCH] =?UTF-8?q?UI=E4=BC=98=E5=8C=96=20=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E6=97=A0=E6=B3=95=E5=8F=91=E9=80=81=20=E5=B7=B2=E7=9F=A5?= =?UTF-8?q?=E9=97=AE=E9=A2=98=EF=BC=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/chat/MessageBubble.vue | 79 +++-- src/components/chat/MessageInput.vue | 97 ++++--- src/components/chat/bubble/Audio.vue | 81 +++++- src/components/chat/bubble/Image.vue | 14 +- src/views/chat/ChatView.vue | 400 ++++++++++++-------------- src/views/contact/ContactView.vue | 159 +++++----- src/views/login/LoginView.vue | 173 +++++------ 7 files changed, 528 insertions(+), 475 deletions(-) diff --git a/src/components/chat/MessageBubble.vue b/src/components/chat/MessageBubble.vue index 685fe32..f7641c3 100644 --- a/src/components/chat/MessageBubble.vue +++ b/src/components/chat/MessageBubble.vue @@ -1,35 +1,74 @@ +// 更现代的圆角逻辑 +const bubbleShapeClass = computed(() => { + if (props.message.isSelf) { + return 'rounded-2xl rounded-tr-sm' // 自己发的消息,右上角微尖 + } else { + return 'rounded-2xl rounded-tl-sm' // 对方发的消息,左上角微尖 + } +}) + +// 使用 format.ts 中的逻辑,但可以根据需要微调为 HH:mm +const formattedTime = computed(() => { + const date = new Date(props.message.created_at) + // 如果是当天,只显示时间,否则显示日期+时间 + const now = new Date() + const isToday = date.toDateString() === now.toDateString() + + if (isToday) { + return date.toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' }) + } + return formatTime(props.message.created_at) +}) + diff --git a/src/components/chat/MessageInput.vue b/src/components/chat/MessageInput.vue index eed2901..3fabd16 100644 --- a/src/components/chat/MessageInput.vue +++ b/src/components/chat/MessageInput.vue @@ -1,68 +1,78 @@ @@ -99,8 +109,12 @@ function insertEmoji(emoji: string) { function triggerFile(type: number) { if (!fileInput.value) return + // 类型映射:1图片,3视频,8文件 fileInput.value.accept = type === 1 ? 'image/*' : type === 3 ? 'video/*' : '*/*' fileInput.value.value = '' + // 保存当前触发的类型到 input 元素上,或者利用闭包,这里简单利用 data-type 属性不太方便, + // 我们直接利用闭包变量 capturing + // 更好的方式是创建一个 reactive state 存储当前上传类型,但这里为了简单,我们通过 accept 判断 fileInput.value.click() } @@ -108,7 +122,10 @@ function handleFileChange(e: Event) { const target = e.target as HTMLInputElement const file = target.files?.[0] if (file) { - const type = file.type.startsWith('image/') ? 1 : file.type.startsWith('video/') ? 3 : 8 + let type = 8 + if (file.type.startsWith('image/')) type = 1 + else if (file.type.startsWith('video/')) type = 3 + emit('file', type, file) } } @@ -128,6 +145,7 @@ function handlePaste(e: ClipboardEvent) { } } +// ... (录音逻辑保持不变) async function handleRecordStart() { if (props.isRecording) return @@ -181,4 +199,3 @@ function handleRecordCancel() { } } - diff --git a/src/components/chat/bubble/Audio.vue b/src/components/chat/bubble/Audio.vue index 162bfc6..03e6340 100644 --- a/src/components/chat/bubble/Audio.vue +++ b/src/components/chat/bubble/Audio.vue @@ -1,38 +1,101 @@ + diff --git a/src/components/chat/bubble/Image.vue b/src/components/chat/bubble/Image.vue index 342e50c..909a9ca 100644 --- a/src/components/chat/bubble/Image.vue +++ b/src/components/chat/bubble/Image.vue @@ -1,8 +1,9 @@ @@ -22,8 +23,11 @@ const imageUrl = computed(() => { function previewImage() { const w = window.open('') if (w) { - w.document.write(``) + w.document.write(` + + + + `) } } - diff --git a/src/views/chat/ChatView.vue b/src/views/chat/ChatView.vue index 01f5a1e..cafdef8 100644 --- a/src/views/chat/ChatView.vue +++ b/src/views/chat/ChatView.vue @@ -1,35 +1,34 @@