记录--Vue3+TS(uniapp)手撸一个聊天页面

这篇具有很好参考价值的文章主要介绍了记录--Vue3+TS(uniapp)手撸一个聊天页面。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助

记录--Vue3+TS(uniapp)手撸一个聊天页面

Vue3+TS(uniapp)手撸一个聊天页面

前言

最近在自己的小程序中做了一个智能客服,API使用的是云厂商的API,然后聊天页面...嗯,找了一下关于UniApp(vite/ts)版本的好像不多,有一个官方的但其中的其他代码太多了,去看懂再删除那些对我无用的代码不如自己手撸一个,先看效果:

记录--Vue3+TS(uniapp)手撸一个聊天页面

好,下面开始介绍如何一步一步实现

重难点调研

1. 如何编写气泡

记录--Vue3+TS(uniapp)手撸一个聊天页面

 可以发现一般的气泡是有个“小箭头”,一般是指向用户的头像,所以这里我们的初步思路就是通过beforeafter伪类来放置这个小三角形,这个小三角形通过隐藏border的其余三边来实现。

记录--Vue3+TS(uniapp)手撸一个聊天页面

然后其中一个细节就是聊天气泡的最大宽度不超过对方的头像,超过就换行。这个简单,设置一个max-width: cacl(100vw - XX)就可以了

2. 如何编写输入框

考虑到用户可能输入多行文字,这里使用的是<textarea>标签,点开微信发个消息试试,发现它是自适应的,这里去调研了解了一下,发现小程序自带组件有这个实现,好,那直接用:

记录--Vue3+TS(uniapp)手撸一个聊天页面

然后我们继续注意到发送按钮与输入框的底线保持水平,这个flex里有对应属性可以实现,跳过...

3.如何实现滚动条始终居于底部

当聊天消息较多时,我们发现我们继续输入消息,页面并没有更新(滚动)。打开微信聊天框一看,当消息过多时,你发一条消息,页面就自动滚动到了最新的消息,这又是怎实现的呢?

继续调研,发现小程序自带的<scroll-view>标签中有个属性scroll-into-view可以自动跳转:

<scroll-view scroll-y="true" :scroll-into-view="`msg${messages.length-1}`" :scroll-with-animation="true">
	<view class="msg-list" :id="`msg${index}`" v-for="(msg, index) in messages" :key="msg.time">
		<view class="msg-item">
		略
		</view>
	</view>
</scroll-view>

概述

简单分析下来好像一点都不难,如下是我的文件列表,话不多说,开始撸代码!

chat
├─ chat.vue
├─ leftBubble.vue
└─ rightBubble.vue 

左气泡模块

左气泡模块就是刚刚分析的那一部分,然后增加一点点细节,如下:

<template>
	<view class="left-bubble-container">
		<view class="left">
			<image :src="props.avatarUrl"></image>
		</view>
		<view class="right">
			<view class="bubble">
				<text>{{ props.message }}</text>
			</view>
		</view>
	</view>
</template>
<script setup lang="ts">
import { userDefaultData } from "@/const";

interface propsI {
	message: string;
	avatarUrl: string;
}

const props = withDefaults(defineProps<propsI>(), {
	avatarUrl: userDefaultData.avatarUrl,
});
</script>
<style lang="scss" scoped>
.left-bubble-container {
  margin: 10px 0;
	display: flex;
	.left {
		image {
			height: 50px;
			width: 50px;
			border-radius: 5px;
		}
	}
}
.bubble {
	max-width: calc(100vw - 160px);
	min-height: 25px;
	border-radius: 10px;
	background-color: #ffffff;
	position: relative;
	margin-left: 20px;
	padding: 15px;
	text {
		height: 25px;
		line-height: 25px;
	}
}
.bubble::before {
	position: absolute;
	top: 15px;
	left: -20px;
	content: "";
	width: 0;
	height: 0;
	border-right: 10px solid #ffffff;
	border-bottom: 10px solid transparent;
	border-left: 10px solid transparent;
	border-top: 10px solid transparent;
}
</style>

右气泡模块

右气泡模块我们需要将三角形放在右边,这个好实现。然后这整个气泡我们需要让它处于水平居右,所以这里我使用了:

display: flex;
direction: rtl;

这个属性,但使用的过程中发现气泡中的内容(符号与文字)会出现翻转,“遇事不决,再加一层”,所以我们在内容节点外再套一层:

<span style="direction: ltr; unicode-bidi: bidi-override">
	<text>{{ props.message }}</text>
</span>

然后继续增加一点点细节:

<template>
	<view class="left-bubble-container">
		<view class="right">
			<image :src="props.avatarUrl"></image>
		</view>
		<view class="left">
			<view class="bubble">
				<span style="direction: ltr; unicode-bidi: bidi-override">
					<text>{{ props.message }}</text>
				</span>
			</view>
		</view>
	</view>
</template>
<script setup lang="ts">
import { userDefaultData } from "@/const";

interface propsI {
	message: string;
	avatarUrl: string;
}

const props = withDefaults(defineProps<propsI>(), {
	avatarUrl: userDefaultData.avatarUrl,
});
</script>
<style lang="scss" scoped>
.left-bubble-container {
	display: flex;
	direction: rtl;
	margin: 10px 0;
	.right {
		image {
			height: 50px;
			width: 50px;
			border-radius: 5px;
		}
	}
}
.bubble {
	max-width: calc(100vw - 160px);
	min-height: 25px;
	border-radius: 10px;
	background-color: #ffffff;
	position: relative;
	margin-right: 20px;
	padding: 15px;
	text-align: left;
	text {
		height: 25px;
		line-height: 25px;
	}
}
.bubble::after {
	position: absolute;
	top: 15px;
	right: -20px;
	content: "";
	width: 0;
	height: 0;
	border-right: 10px solid transparent;
	border-bottom: 10px solid transparent;
	border-left: 10px solid #ffffff;
	border-top: 10px solid transparent;
}
</style>

输入模块

没啥说的,需要注意的是:Button记得防抖

<view class="bottom-input">
	<view class="textarea-container">
		<textarea
			auto-height
			fixed="true"
			confirm-type="send"
			v-model="input"
			@confirm="submit"
		/>
	</view>
	<button
		style="
			width: 70px;
			height: 40px;
			line-height: 34px;
			margin: 0 10px;
			background-color: #ffffff;
			border: 3px solid #0256ff;
			color: #0256ff;
		"
		@click="submit"
>
		发送
	</button>

记录--Vue3+TS(uniapp)手撸一个聊天页面

整体

1)考虑如何存储消息

这里仅考虑内存中如何存储,不考虑本地存储,后续思考中会聊到。

export interface messagesI {
  left: boolean;
  text: string;
  time: number;
}

如上是消息列表中的一项,为了区分是渲染到左气泡还是右气泡,这里用left来区分了一下;

const messages: Ref<messagesI[]> = ref([]);

2)如何推荐消息

这边我封装的服务端接口是这样的:

mutation chat{
  customerChat(talk: "你好啊"){
  	knowledge
    text
    recommend
  }
}

recommend是用户可能输入了错误的消息,这里是预测用户的输入字符串,所以我们需要在得到这个字符串后直接显示,然后用户可以一键通过这条消息回复:

function submit(){
	// 略...
	const finalMsg = receive?.knowledge || receive?.text || "你是否想问: " + receive?.recommend;
	// 略...
	if (receive?.recommend) {
		input.value = receive?.recommend;
	} else {
		input.value = "";
	}
}

如上,得益于Vue框架,这里实现起来也非常简单,当用户提交之后,如果有推荐的消息,就直接修改input.value从而修改输入框的文字;如果没有就直接清空方便下一次输入。

接下来继续增加一点点细节(chat.vue文件)

<template>
	<view class="chat-container">
		<view class="msg-container">
			<!-- https://github.com/wepyjs/wepy-wechat-demo/issues/7 -->
			<scroll-view scroll-y="true" :scroll-into-view="`msg${messages.length-1}`" :scroll-with-animation="true">
				<view class="msg-list" :id="`msg${index}`" v-for="(msg, index) in messages" :key="msg.time">
					<view class="msg-item">
						<left-bubble v-if="msg.left" :message="msg.text" :avatar-url="meStore.user?.avatarUrl"></left-bubble>
						<right-bubble v-else :message="msg.text" :avatar-url="logoUrl"></right-bubble>
					</view>
				</view>
			</scroll-view>
		</view>
		<view class="bottom-input">
			<view class="textarea-container">
				<textarea
					auto-height
					fixed="true"
					confirm-type="send"
					v-model="input"
					@confirm="submit"
				/>
			</view>
			<button
				style="
					width: 70px;
					height: 40px;
					line-height: 34px;
					margin: 0 10px;
					background-color: #ffffff;
					border: 3px solid #0256ff;
					color: #0256ff;
				"
				@click="submit"
			>
				发送
			</button>
		</view>
	</view>
</template>
<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<messagesI[]> = 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: `加载错误`,
			icon: "error",
			duration: 3000,
		});
		throw new Error(`加载错误: ${error}`);
	}
	const receive = data?.customerChat;
	const finalMsg = receive?.knowledge || receive?.text || "你是否想问: " + receive?.recommend;
	messages.value.push({
		left: false,
		text: finalMsg,
		time: new Date().getTime(),
	});
	if (receive?.recommend) {
		input.value = receive?.recommend;
	} else {
		input.value = "";
	}
}

</script>
<style lang="scss" scoped>
.chat-container {
	.msg-container {
		padding: 20px 5px 100px 5px;
		height: calc(100vh - 120px);
		scroll-view {
			height: 100%;
		}
	}
	.bottom-input {
		display: flex;
		align-items: flex-end;
		position: fixed;
		bottom: 0px;
		background-color: #fbfbfb;
		padding: 20px;
		box-shadow: 0px -10px 30px #eeeeee;
		.textarea-container {
			background-color: #ffffff;
			padding: 10px;
			textarea {
				width: calc(100vw - 146px);
				background-color: #ffffff;
			}
		}
	}
}
</style>

思考

如何保存到本地,然后每次加载最新消息,然后向上滚动进行懒加载?

我这里没有实现该功能,毕竟只是一个客服,前端没必要保存消息记录到本地如Localstorage。

这里抛砖引玉,想到了一个最基础的数据结构--链表,用Localstorage-key/value的形式来实现消息队列在本地的多段存储:

记录--Vue3+TS(uniapp)手撸一个聊天页面

当然,有效性有待验证,这里仅仅属于一些想法

最后

然后,我撸了小半天的页面,准备给朋友看看来着,他告诉我微信小程序自带一个客服系统,只需要让buttonopen-type属性等于contract

记录--Vue3+TS(uniapp)手撸一个聊天页面

本文转载于:

https://juejin.cn/post/7224059698911641658

如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。

 记录--Vue3+TS(uniapp)手撸一个聊天页面文章来源地址https://www.toymoban.com/news/detail-439068.html

到了这里,关于记录--Vue3+TS(uniapp)手撸一个聊天页面的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • vue3+ts+vite 搭建uniapp项目(微信小程序)

    模板下载: uniapp 官网通过vue-cli 命令行创建uniapp,参考uni-app官网,使用  npx degit dcloudio/uni-preset-vue#vite-ts my-vue3-project 下载模板; 安装css预处理 sass: 项目终端输入: yarn add node-sass@^4.0.0 sass-loader@^10.0.1 sass (模板没有默认安装sass, 如果不安装直接使用会报错)  安装uni-ui组件

    2024年02月09日
    浏览(41)
  • uniapp vue3版本+ts使用 vant小程序 组件库

    1.首先从uniapp官网下载vue3版本+ts的模板 2.安装vant微信小程序版本 3.在项目src目录创建wxcomponents文件夹,在wxcomponents文件夹下创建vant文件夹 4.从node_modules文件夹下的@vant文件夹里面的weapp,weapp下的dist文件夹,将dist文件夹里面的文件复制到/wxcomponents/vant 5.全局引用   在 pages

    2024年02月13日
    浏览(42)
  • 微信小程序uniapp+vue3+ts+pinia的环境搭建

    一.创建uniapp项目 通过vue-cli创建 二.安装依赖: 1.pnpm i 2.运行项目: 将package.json的 3.导入微信小程序开发工具 打开微信开发者工具, 导入 distdevmp-weixin 运行 三. TS 类型校验 在tsconfig.json文件中\\\"compilerOptions\\\"配置项内添加\\\"ignoreDeprecations\\\": “5.0” 额外配置Ts类型校验: 安装类型

    2024年04月10日
    浏览(46)
  • 2023 最新最细 vite+vue3+ts 多页面项目架构,建议收藏备用!

    本文教程 github地址 、码云。 如果对你有帮助,希望能点个star ⭐️⭐️⭐️ 万分感谢😊😊😊 不久前我司需要重新部署一个前端项目,由我来负责这个项目的搭建。因为这个项目是需要和app混合开发的h5页面,包括以后可能会做一些运营h5,所以自然不能采用常规的 SPA单页

    2024年02月06日
    浏览(34)
  • uniapp微信小程序使用axios(vue3+axios+ts版)

    \\\"vue\\\": \\\"^3.2.45\\\",  \\\"axios\\\": \\\"^1.4.0\\\",  \\\"axios-miniprogram-adapter\\\": \\\"^0.3.5\\\", yarn add axios axios-miniprogram-adapter 在 utils 创建 utils/request.ts 文件 在 src 目录下创建 src/api/config 文件夹 config文件夹中创建home.ts文件,首页的接口都放在里面统一管理  和 config 文件夹同级创建home.ts文件,统一管理请求

    2024年02月16日
    浏览(46)
  • 用vue3写一个AI聊天室

    效果图如下: 1、页面布局: 2、封转函数(用户输入问题和AI回答问题): 3、从后端获取最近的10个对话: 4、为了使用户发送问题后内容滚动在最底处,写一个函数让其自动滚动,在发送信息和获取信息时调用该函数即可 5、点击发送按钮,发送到后端进行处理: 完整代码

    2024年04月29日
    浏览(24)
  • Vue3项目(Vite+TS)使用Web Serial Api全记录

    之前写了一个vue+django的一个通过串口控制的上位机系统。但是实际生产中,不如部署到服务器上,这样可以更好的节约成本。但是这样就需要弄一个客户端来控制处理串口信息。那我就在想能不能通过网页直接拿到客户端的串口信息。所以问了万能的chatgpt,得到了以下答案

    2024年02月02日
    浏览(35)
  • uniapp-chatgpt跨端仿ChatGPT实例|uniapp+vue3+pinia多端聊天模板

    基于 uniapp+vite4+pinia 跨多端实现chatgpt会话模板 Uniapp-ChatGPT 。 uni-chatgpt 使用 uni-app+vite4+vue3+pinia+uview-plus 等技术构建多端仿制ChatGPT手机端APP会话应用模板。支持 编译到h5+小程序+APP端 ,支持 渲染markdown语法及代码高亮 、解决 软键盘撑起布局 问题。 编译 [ H5+小程序+App端 ] 效果

    2024年02月11日
    浏览(48)
  • uniapp----微信小程序 日历组件(周日历&& 月日历)【Vue3+ts+uView】

    用Vue3+ts+uView来编写日历组件; 存在周日历和月日历两种显示方式; 高亮显示当天日期,红点渲染有数据的日期,点击显示数据 1. calendar-week-mouth组件代码 2. 在页面引用组件

    2024年02月04日
    浏览(35)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包