最近在自己的小程序中做了一個(gè)智能客服,API使用的是云廠商的API,然后聊天頁(yè)面...嗯,找了一下關(guān)于UniApp(vite/ts)版本的好像不多,有一個(gè)官方的但其中的其他代碼太多了,去看懂再刪除那些對(duì)我無(wú)用的代碼不如自己手?jǐn)]一個(gè),先看效果:
好,下面開始介紹如何一步一步實(shí)現(xiàn)
(資料圖片僅供參考)
可以發(fā)現(xiàn)一般的氣泡是有個(gè)“小箭頭”,一般是指向用戶的頭像,所以這里我們的初步思路就是通過(guò)before
與after
偽類來(lái)放置這個(gè)小三角形,這個(gè)小三角形通過(guò)隱藏border的其余三邊來(lái)實(shí)現(xiàn)。
然后其中一個(gè)細(xì)節(jié)就是聊天氣泡的最大寬度不超過(guò)對(duì)方的頭像,超過(guò)就換行。這個(gè)簡(jiǎn)單,設(shè)置一個(gè)max-width: cacl(100vw - XX)
就可以了
考慮到用戶可能輸入多行文字,這里使用的是標(biāo)簽,點(diǎn)開微信發(fā)個(gè)消息試試,發(fā)現(xiàn)它是自適應(yīng)的,這里去調(diào)研了解了一下,發(fā)現(xiàn)小程序自帶組件有這個(gè)實(shí)現(xiàn),好,那直接用:
然后我們繼續(xù)注意到發(fā)送按鈕與輸入框的底線保持水平,這個(gè)flex
里有對(duì)應(yīng)屬性可以實(shí)現(xiàn),跳過(guò)...
當(dāng)聊天消息較多時(shí),我們發(fā)現(xiàn)我們繼續(xù)輸入消息,頁(yè)面并沒(méi)有更新(滾動(dòng))。打開微信聊天框一看,當(dāng)消息過(guò)多時(shí),你發(fā)一條消息,頁(yè)面就自動(dòng)滾動(dòng)到了最新的消息,這又是怎實(shí)現(xiàn)的呢?
繼續(xù)調(diào)研,發(fā)現(xiàn)小程序自帶的
標(biāo)簽中有個(gè)屬性scroll-into-view
可以自動(dòng)跳轉(zhuǎn):
略
簡(jiǎn)單分析下來(lái)好像一點(diǎn)都不難,如下是我的文件列表,話不多說(shuō),開始擼代碼!
chat├─ chat.vue├─ leftBubble.vue└─ rightBubble.vue
左氣泡模塊就是剛剛分析的那一部分,然后增加一點(diǎn)點(diǎn)細(xì)節(jié),如下:
<script setup lang="ts">import { userDefaultData } from "@/const";interface propsI {message: string;avatarUrl: string;}const props = withDefaults(defineProps {{ props.message }} (), {avatarUrl: userDefaultData.avatarUrl,});</script>
右氣泡模塊我們需要將三角形放在右邊,這個(gè)好實(shí)現(xiàn)。然后這整個(gè)氣泡我們需要讓它處于水平居右,所以這里我使用了:
display: flex;direction: rtl;
這個(gè)屬性,但使用的過(guò)程中發(fā)現(xiàn)氣泡中的內(nèi)容(符號(hào)與文字)會(huì)出現(xiàn)翻轉(zhuǎn),“遇事不決,再加一層”,所以我們?cè)趦?nèi)容節(jié)點(diǎn)外再套一層:
{{ props.message }}
然后繼續(xù)增加一點(diǎn)點(diǎn)細(xì)節(jié):
<script setup lang="ts">import { userDefaultData } from "@/const";interface propsI {message: string;avatarUrl: string;}const props = withDefaults(defineProps {{ props.message }} (), {avatarUrl: userDefaultData.avatarUrl,});</script>
沒(méi)啥說(shuō)的,需要注意的是:Button
記得防抖
發(fā)送
1)考慮如何存儲(chǔ)消息
這里僅考慮內(nèi)存中如何存儲(chǔ),不考慮本地存儲(chǔ),后續(xù)思考中會(huì)聊到。
export interface messagesI { left: boolean; text: string; time: number;}
如上是消息列表中的一項(xiàng),為了區(qū)分是渲染到左氣泡還是右氣泡,這里用left
來(lái)區(qū)分了一下;
const messages: Ref= ref([]);
2)如何推薦消息
這邊我封裝的服務(wù)端接口是這樣的:
mutation chat{ customerChat(talk: "你好啊"){ knowledge text recommend }}
recommend
是用戶可能輸入了錯(cuò)誤的消息,這里是預(yù)測(cè)用戶的輸入字符串,所以我們需要在得到這個(gè)字符串后直接顯示,然后用戶可以一鍵通過(guò)這條消息回復(fù):
function submit(){// 略...const finalMsg = receive?.knowledge || receive?.text || "你是否想問(wèn): " + receive?.recommend;// 略...if (receive?.recommend) {input.value = receive?.recommend;} else {input.value = "";}}
如上,得益于Vue框架,這里實(shí)現(xiàn)起來(lái)也非常簡(jiǎn)單,當(dāng)用戶提交之后,如果有推薦的消息,就直接修改input.value
從而修改輸入框的文字;如果沒(méi)有就直接清空方便下一次輸入。
接下來(lái)繼續(xù)增加一點(diǎn)點(diǎn)細(xì)節(jié)(chat.vue
文件)
<script setup lang="ts">import { ref, type Ref } from "vue";import leftBubble from "./leftBubble.vue";import rightBubble from "./rightBubble.vue";import type { messagesI } from "./chat.interface";import { chatGQL } from "@/graphql/me.graphql";import { useMutation } from "villus";import { logoUrl } from "@/const";import { useMeStore } from "@/stores/me.store";const meStore = useMeStore();const messages: Ref 發(fā)送 = ref([]);const input = ref("");async function submit() {if (input.value === "") return;messages.value.push({left: true,text: input.value,time: new Date().getTime(),});const { execute } = useMutation(chatGQL);const { error, data } = await execute({ talk: input.value })if (error) {uni.showToast({title: `加載錯(cuò)誤`,icon: "error",duration: 3000,});throw new Error(`加載錯(cuò)誤: ${error}`);}const receive = data?.customerChat;const finalMsg = receive?.knowledge || receive?.text || "你是否想問(wèn): " + receive?.recommend;messages.value.push({left: false,text: finalMsg,time: new Date().getTime(),});if (receive?.recommend) {input.value = receive?.recommend;} else {input.value = "";}}</script>
如何保存到本地,然后每次加載最新消息,然后向上滾動(dòng)進(jìn)行懶加載?
我這里沒(méi)有實(shí)現(xiàn)該功能,畢竟只是一個(gè)客服,前端沒(méi)必要保存消息記錄到本地如Localstorage。
這里拋磚引玉,想到了一個(gè)最基礎(chǔ)的數(shù)據(jù)結(jié)構(gòu)--鏈表,用Localstorage-key/value的形式來(lái)實(shí)現(xiàn)消息隊(duì)列在本地的多段存儲(chǔ):
當(dāng)然,有效性有待驗(yàn)證,這里僅僅屬于一些想法
然后,我擼了小半天的頁(yè)面,準(zhǔn)備給朋友看看來(lái)著,他告訴我微信小程序自帶一個(gè)客服系統(tǒng),只需要讓button
的open-type
屬性等于contract
;