fix: show customer chat before connection completes
Deploy olons-server production / deploy (push) Successful in 4m30s

This commit is contained in:
richjohnrambo
2026-10-19 21:32:56 +08:00
parent db1367b8fe
commit ba87d70478
+13 -26
View File
@@ -38,22 +38,11 @@
<section v-else class="m-chat">
<PageHeader :title="current?.name || '在线客服'" />
<div class="m-chat-time">会话内容已安全加密,请勿发送密码或验证码</div>
<div v-if="!chatLoading && connectionState !== 'online'" :class="['m-chat-connection', `is-${connectionState}`]">
<span>{{ connectionStatusText }}</span>
<div v-if="connectionState !== 'online' || chatError" :class="['m-chat-connection', `is-${connectionState}`]" role="status" aria-live="polite">
<span>{{ chatError || connectionStatusText }}</span>
<button v-if="connectionState === 'offline'" type="button" @click="connectChat">重新连接</button>
</div>
<div v-if="chatLoading" class="m-chat-empty">
<n-icon :component="ChatbubbleEllipsesOutline" />
<h2>正在连接客服</h2>
<p>首次连接会自动建立好友关系</p>
</div>
<div v-else-if="chatError" class="m-chat-empty">
<n-icon :component="ChatbubbleEllipsesOutline" />
<h2>连接失败</h2>
<p class="m-form-error">{{ chatError }}</p>
<button class="m-primary-btn" @click="connectChat">重新连接</button>
</div>
<div v-else ref="messageList" class="m-chat-messages">
<div ref="messageList" class="m-chat-messages">
<div v-for="message in historicalMessages" :key="`history-${message.seq}-${message.from}`" :class="['m-chat-row', { 'is-mine': message.mine }]">
<div class="m-chat-avatar" aria-hidden="true">
<img v-if="!message.mine && safeImage(current?.avatar_url)" :src="safeImage(current?.avatar_url)" alt="" />
@@ -77,7 +66,7 @@
</div>
</div>
<div v-if="historicalMessages.length" class="m-chat-history-divider"><span>以上是历史会话</span></div>
<div class="m-chat-session-tip">[ {{ current?.name || '在线客服' }} ] 与您建立了会话</div>
<div class="m-chat-session-tip">[ {{ current?.name || '在线客服' }} ] 客服会话已打开</div>
<div class="m-chat-system-row">
<div class="m-chat-avatar m-chat-avatar--agent" aria-hidden="true">
<img v-if="safeImage(current?.avatar_url)" :src="safeImage(current?.avatar_url)" alt="" />
@@ -85,7 +74,7 @@
</div>
<div class="m-chat-system-message">
<small>{{ current?.name || '在线客服' }} · {{ messageDateTime(sessionStartedAt) }}</small>
<p><b>【系统消息】</b>尊敬的用户您好,我是{{ current?.name || '在线客服' }},当前已为您接通在线客服,请问有什么可以帮您?</p>
<p><b>【系统消息】</b>您好,我是{{ current?.name || '在线客服' }}。您可以先输入要咨询的问题,正在为您连接客服。</p>
</div>
</div>
<div v-for="message in liveMessages" :key="`live-${message.seq}-${message.from}`" :class="['m-chat-row', { 'is-mine': message.mine }]">
@@ -113,12 +102,12 @@
</div>
<div class="m-chat-composer" :class="{ 'is-expanded': toolsOpen }">
<form class="m-chat-input" @submit.prevent="sendMessage">
<input v-model="draft" :disabled="chatLoading || Boolean(chatError) || connectionState !== 'online' || sending || mediaSending" maxlength="1000" placeholder="请输入消息" />
<input v-model="draft" :disabled="sending || mediaSending" maxlength="1000" placeholder="请输入消息" />
<button
v-if="draft.trim()"
class="m-chat-send"
type="submit"
:disabled="chatLoading || Boolean(chatError) || connectionState !== 'online' || sending || mediaSending"
:disabled="!chat || connectionState !== 'online' || sending || mediaSending"
>{{ sending ? '发送中' : '发送' }}</button>
<button v-else class="m-chat-tool" type="button" aria-label="更多" @click="toggleTools">
<img :src="mobileAssets.chatMore" alt="" />
@@ -127,8 +116,8 @@
<div v-if="mediaSending" class="m-chat-uploading"><progress max="100" :value="uploadProgress"></progress><b>正在上传 {{ uploadProgress }}%</b></div>
<div v-if="toolsOpen && !emojiOpen" class="m-chat-tools">
<button type="button" @click="emojiOpen = true"><n-icon :component="HappyOutline" /><span>表情</span></button>
<button type="button" :disabled="mediaSending" @click="photoInput?.click()"><n-icon :component="ImageOutline" /><span>照片</span></button>
<button type="button" :disabled="mediaSending" @click="videoInput?.click()"><n-icon :component="VideocamOutline" /><span>视频</span></button>
<button type="button" :disabled="!chat || connectionState !== 'online' || mediaSending" @click="photoInput?.click()"><n-icon :component="ImageOutline" /><span>照片</span></button>
<button type="button" :disabled="!chat || connectionState !== 'online' || mediaSending" @click="videoInput?.click()"><n-icon :component="VideocamOutline" /><span>视频</span></button>
<input ref="photoInput" class="m-chat-file-input" type="file" accept="image/jpeg,image/png,image/webp,image/gif" @change="sendMedia('image', $event)" />
<input ref="videoInput" class="m-chat-file-input" type="file" accept="video/mp4,video/webm,video/quicktime" @change="sendMedia('video', $event)" />
</div>
@@ -157,7 +146,6 @@
import { computed, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue';
import { useRoute } from 'vue-router';
import {
ChatbubbleEllipsesOutline,
HappyOutline,
HeadsetOutline,
ImageOutline,
@@ -184,7 +172,6 @@
services.value.find((item) => String(item.id) === String(route.params.id)) ||
services.value[0]
);
const chatLoading = ref(false);
const chatError = ref('');
const connectionState = ref<CustomerServiceConnectionState>('offline');
const connectionStatusText = computed(() => {
@@ -268,7 +255,6 @@
chat = null;
messages.value = [];
historyBoundarySeq.value = 0;
chatLoading.value = false;
chatError.value = '';
connectionState.value = 'offline';
draft.value = '';
@@ -283,7 +269,9 @@
disconnectChat();
if (!serviceID) return;
const version = connectionVersion;
chatLoading.value = true;
connectionState.value = 'connecting';
chatError.value = '';
sessionStartedAt.value = new Date().toISOString();
try {
const session = await request<CustomerServiceSession>({
url: `/v1/customer-services/${serviceID}/session`,
@@ -321,8 +309,7 @@
chat?.disconnect();
chat = null;
chatError.value = errorText(err);
} finally {
if (version === connectionVersion) chatLoading.value = false;
connectionState.value = 'offline';
}
}
async function sendMessage() {