微信小程序实现聊天窗口升级版
微信小程序聊天界面实现:从布局到交互
在移动应用开发中,聊天功能是许多社交、客服类小程序的核心模块。本文将基于实际代码,详细讲解如何在微信小程序中实现一个功能完善的聊天界面,包括消息展示、发送、撤回、复制等核心功能,以及键盘适配、自动滚动等细节处理。
先看示例图片效果:

一、功能概述
本次实现的聊天界面包含以下核心功能:
区分当前用户与其他用户的消息(左对齐 vs 右对齐)
支持消息时间展示(特定消息显示发送时间)
消息发送功能(文本消息)
长按消息支持复制、撤回操作(撤回仅支持自己 5 分钟内的消息)
自动适配键盘高度变化,保持聊天区域正常显示
消息发送后自动滚动到底部
支持消息撤回状态展示(“你撤回了一条消息”)
二、核心代码结构
整个聊天界面的实现分为三个部分:数据结构设计(JS data)、页面布局(WXML)、样式设计(WXSS)和逻辑处理(JS 方法)。
- 数据结构设计(Page.data)
首先需要定义界面所需的核心数据,主要包括当前用户信息、聊天消息列表、布局计算参数等:
data: {
// 当前登录用户信息
loginInfo: {
'userId': '20258888',
'nick': '西红柿炒番茄',
'avatar': '/static/images/local/logo.jpg',
},
// 聊天消息列表
dataList: [{
'chatId': '1752843999000', // 消息唯一标识(时间戳)
'userId': '20250718', // 发送者ID
'nick': '蒜鸟编程', // 发送者昵称
'avatar': 'https://xxx.jpg',// 发送者头像
'status': '0', // 消息状态(0-正常,1-撤回)
'showTime': '21:06', // 显示时间(仅部分消息展示)
'content': '欢迎关注蒜鸟编程!', // 消息内容
'createDate': 1752843999000 // 创建时间戳
},
// ...更多消息
],
msgInfo: '', // 输入框内容
toolInfo: '', // 长按操作框信息
scrollToId: '', // 滚动到指定元素ID
// 布局计算参数
footHeight: 36, // 底部区域高度
chatHeight: 540, // 聊天区域高度
viewHeight: 640, // 窗口总高度
boardHeight: 0, // 键盘高度
lineCount: 1 // 输入框行数
}
核心字段说明:
- status:用于区分消息状态,0为正常消息,1为撤回消息
- createDate:用于判断消息是否在撤回时效内(5 分钟)
- 布局参数:用于动态计算聊天区域高度,适配不同设备和键盘状态
2. 页面布局实现(WXML)
聊天界面的布局核心是scroll-view滚动容器,配合消息列表渲染和底部输入区域:
<!-- 聊天消息区域 -->
<scroll-view
scroll-y="{{true}}"
scroll-into-view="{{scrollToId}}"
bind:tap="onPageTap"
style="height: {{chatHeight}}px;"
>
<view class="seat"></view>
<!-- 循环渲染消息列表 -->
<block wx:for="{{dataList}}" wx:for-item="item" wx:key="item.chatId">
<!-- 时间展示(仅当消息有showTime时显示) -->
<view class="row-date" hidden="{{!item.showTime}}">
<text>{{item.showTime}}</text>
</view>
<!-- 撤回消息展示 -->
<view class="row-back" wx:if="{{item.status=='1'}}">
<text wx:if="{{item.userId==loginInfo.userId}}">你撤回了一条消息</text>
<text wx:else>“{{item.nick}}”撤回了一条消息</text>
</view>
<!-- 正常消息展示 -->
<view class="row {{item.userId==loginInfo.userId?'row-r':''}}" wx:else>
<!-- 头像区域 -->
<view class="row-left">
<image class="row-avatar" src="{{item.avatar}}" mode="widthFix"></image>
</view>
<!-- 消息内容区域 -->
<view class="row-right">
<!-- 对方昵称(仅显示他人消息的昵称) -->
<view class="row-nick" wx:if="{{item.userId!=loginInfo.userId}}">{{item.nick}}</view>
<!-- 消息内容(绑定长按事件) -->
<view class="row-info {{item.userId==loginInfo.userId?'info-r':'info-l'}}"
bind:longpress="toolClick"
data-item="{{item}}">
{{item.content}}
</view>
<!-- 长按操作框(复制/撤回) -->
<view class="level tool-box {{toolInfo.boxRight+' '+toolInfo.boxBottom}}"
wx:if="{{toolInfo.chatId == item.chatId}}">
<view class="tool-trans {{toolInfo.trans}}"></view>
<view class="tool" bind:tap="buttonClick" data-type="copy">
<text>复制</text>
</view>
<view class="tool" bind:tap="buttonClick" data-type="back" wx:if="{{toolInfo.showBack}}">
<text>撤回</text>
</view>
</view>
</view>
</view>
</block>
<!-- 用于滚动到底部的锚点 -->
<view id="endView" class="seat"></view>
</scroll-view>
<!-- 底部输入区域 -->
<view class="foot-box" id="bottomArea" bind:tap="onPageTap">
<view class="level {{lineCount>2?'input-box':''}}">
<view class="input-area">
<textarea class="input-text"
bindinput="inputClick"
bindkeyboardheightchange="getKeyBoardHeight"
bindlinechange="lineClick"
value="{{msgInfo}}"
show-confirm-bar="{{false}}"
hold-keyboard="{{true}}"
adjust-keyboard-to="bottom"
cursor-spacing="{{20}}"
confirm-type="return"
auto-height="{{true}}"></textarea>
</view>
<view class="input-btn">
<text bind:tap="sendTextMessage" class="input-send">发 送</text>
</view>
</view>
</view>
布局核心要点:
- 使用scroll-view实现消息列表滚动,通过scroll-into-view实现自动滚动到底部
- 用wx:for循环渲染消息,通过wx:if区分撤回消息和正常消息
- 通过item.userId==loginInfo.userId判断消息发送者,动态添加样式类(左 / 右对齐)
- 长按消息时显示操作框(复制 / 撤回),通过toolInfo.chatId控制显示哪个消息的操作框
- 底部textarea支持自动高度调整、键盘高度监听,适配输入体验
3. 样式设计(WXSS)
样式设计需要解决消息区分显示、操作框定位、响应式布局等问题:
/* 基础样式 */
page {
background-color: white;
overflow: hidden;
}
.seat {
height: 10rpx; /* 占位元素,避免滚动到边缘时内容被遮挡 */
}
/* 消息行样式 */
.row {
display: flex;
margin-bottom: 20rpx;
}
.row-r {
flex-direction: row-reverse; /* 自己的消息右对齐 */
text-align: right;
}
/* 头像样式 */
.row-left {
width: 15%;
text-align: center;
}
.row-avatar {
width: 80rpx;
height: 80rpx;
border-radius: 10rpx;
}
/* 时间样式 */
.row-date {
text-align: center;
font-size: 26rpx;
color: #616060;
margin: 20rpx 0;
}
.row-date text {
padding: 3rpx 15rpx;
border-radius: 5rpx;
background-color: #e9e9e9;
}
/* 撤回消息样式 */
.row-back {
text-align: center;
font-size: 26rpx;
color: #616060;
margin: 20rpx 0;
}
/* 消息内容区域 */
.row-right {
width: 70%;
position: relative; /* 用于操作框绝对定位 */
}
.row-nick {
font-size: 24rpx;
margin-bottom: 10rpx;
color: #6a6b6e;
}
.row-info {
font-size: 30rpx;
border-radius: 10rpx;
padding: 20rpx;
color: white;
display: inline-block;
text-align: left;
}
.info-l {
background-color: #377AF6; /* 他人消息蓝色背景 */
}
.info-r {
background-color: #18b566; /* 自己消息绿色背景 */
}
/* 长按操作框样式 */
.tool-box {
position: absolute;
z-index: 9;
background-color: #818181;
color: white;
padding: 15rpx 10rpx;
border-radius: 10rpx;
}
.tool {
margin: 0 20rpx;
display: flex;
flex-direction: column;
text-align: center;
font-size: 26rpx;
}
/* 操作框定位 */
.tool-r { right: 10rpx; }
.tool-t { top: -120rpx; }
.tool-b { bottom: -120rpx; }
/* 操作框三角指示器 */
.tool-trans {
width: 0;
height: 0;
position: absolute;
border-left: 14rpx solid transparent;
border-right: 14rpx solid transparent;
}
.trans-tlf { /* 上方操作框的下三角 */
bottom: -17rpx;
border-top: 20rpx solid #858383;
}
.trans-blf { /* 下方操作框的上三角 */
top: -17rpx;
border-bottom: 20rpx solid #858383;
}
.trans-r { right: 20rpx; }
.trans-l { left: 20rpx; }
/* 底部输入区域样式 */
.foot-box {
background-color: #f1f1f1;
padding: 10rpx;
}
.level {
display: flex;
flex-direction: row;
align-items: center;
}
.input-box {
align-items: flex-end; /* 输入框多行时底部对齐 */
}
.input-area {
width: 84%;
padding: 20rpx;
background-color: white;
border-radius: 10rpx;
}
.input-text {
width: 100%;
font-size: 32rpx;
max-height: 300rpx; /* 限制输入框最大高度 */
}
.input-btn {
width: 16%;
display: flex;
justify-content: center;
align-items: center;
}
.input-send {
color: white;
font-size: 30rpx;
padding: 15rpx;
background-color: #fa4242;
border-radius: 10rpx;
}
/* 隐藏滚动条 */
::-webkit-scrollbar {
width: 0;
height: 0;
}
样式设计核心:
- 通过flex布局实现消息左 / 右对齐
- 用position: absolute实现长按操作框的动态定位
- 用 CSS 边框特性实现操作框的三角指示器(小箭头)
- 隐藏滚动条提升视觉体验
- 限制输入框最大高度,避免输入内容过多时界面变形
4. 逻辑处理(JS 方法)
逻辑处理主要解决消息发送、操作交互、布局适配等问题,核心方法如下:
(1)初始化布局计算
在页面加载完成后,计算聊天区域高度(适配不同设备屏幕):
onReady() {
let query = wx.createSelectorQuery();
let sys = wx.getSystemInfoSync().windowHeight; // 获取屏幕高度
// 查询底部区域高度
query.select('#bottomArea').boundingClientRect((res) => {
let top = res.height;
this.setData({
footHeight: top,
chatHeight: sys - top, // 聊天区域高度 = 屏幕高度 - 底部高度
viewHeight: sys,
scrollToId: 'endView' // 初始滚动到底部
});
}).exec();
}
(2)消息发送功能
处理文本消息发送,更新消息列表并滚动到底部:
sendTextMessage() {
const msg = this.data.msgInfo.trim();
if (!msg) return; // 空消息不发送
const { viewHeight, footHeight, boardHeight, loginInfo, dataList } = this.data;
let time = new Date().getTime(); // 用时间戳作为消息唯一ID
// 构造新消息对象
let newMsg = [{
...loginInfo,
'chatId': time,
'status': '0', // 正常消息状态
'content': msg,
'createDate': time
}];
// 更新数据
this.setData({
msgInfo: '', // 清空输入框
chatHeight: viewHeight - footHeight - boardHeight, // 调整聊天区域高度
dataList: [...dataList, ...newMsg] // 添加新消息
}, () => {
this.scrollToBottom(); // 发送后滚动到底部
});
}
(3)长按消息操作(复制 / 撤回)
处理消息长按事件,显示操作框并计算定位:
toolClick(e) {
const row = e.currentTarget.dataset.item;
if (!row.status) return; // 仅处理正常消息
const top = e.changedTouches[0].clientY; // 点击位置Y坐标
const flag = row.userId == this.data.loginInfo.userId; // 是否是自己的消息
// 计算操作框定位(左右)
row.boxRight = flag ? 'tool-r' : '';
// 计算操作框定位(上下,点击位置过小时显示在下方)
row.boxBottom = top < 100 ? 'tool-b' : 'tool-t';
// 计算三角指示器位置
if (top < 100) {
row.trans = flag ? 'trans-blf trans-r' : 'trans-blf trans-l';
} else {
row.trans = flag ? 'trans-tlf trans-r' : 'trans-tlf trans-l';
}
// 判断是否允许撤回(自己的消息且发送时间<5分钟)
row.showBack = ((Date.now() - row.createDate) < 5 * 60 * 1000) && flag;
this.setData({
toolInfo: row // 显示操作框
});
}
(4)复制与撤回实现
处理操作框的点击事件,执行复制或撤回操作:
buttonClick(e) {
const tag = e.currentTarget.dataset.type;
const obj = this.data.toolInfo;
if (tag == 'copy') {
// 复制消息内容到剪贴板
wx.setClipboardData({
data: obj.content,
})
return;
}
// 撤回消息(更新消息状态为1)
const index = this.data.dataList.findIndex(item => item.chatId === obj.chatId);
if (index !== -1) {
this.setData({
[`dataList[${index}].status`]: '1' // 用数据路径更新指定消息状态
});
}
}
(5)键盘适配与滚动控制
处理键盘高度变化,动态调整聊天区域高度,并确保消息滚动到底部:
// 键盘高度变化时触发
getKeyBoardHeight(e) {
let board = e.detail.height || 0;
const { viewHeight, footHeight } = this.data;
this.setData({
chatHeight: viewHeight - footHeight - board, // 聊天区域高度 = 屏幕高度 - 底部高度 - 键盘高度
boardHeight: board
})
this.scrollToBottom(); // 键盘弹出后滚动到底部
},
// 滚动到最底部
scrollToBottom() {
this.setData({
scrollToId: 'endView' // 通过scroll-into-view滚动到endView
})
}
三、关键技术点解析
1、动态布局计算
通过wx.getSystemInfoSync()获取屏幕高度,结合wx.createSelectorQuery()查询元素尺寸,动态计算聊天区域高度,确保在不同设备和键盘状态下都能正常显示。
2、消息定位与样式区分
通过userId判断消息发送者,动态添加样式类实现左 / 右对齐;用status字段区分正常消息和撤回消息,分别渲染不同 UI。
3、长按操作框定位逻辑
根据点击位置的 Y 坐标(clientY)和消息发送者(自己 / 他人),动态计算操作框的上下左右位置,确保操作框不会超出屏幕范围。
4、自动滚动到底部
通过scroll-view的scroll-into-view属性,每次发送消息或键盘弹出时,将scrollToId设为底部锚点endView,实现自动滚动。
5、textarea 适配
通过auto-height属性实现输入框高度自适应,bindlinechange监听行数变化调整布局,bindkeyboardheightchange适配键盘高度。
四、扩展与优化建议
1、功能扩展
- 支持发送图片、表情、语音等多媒体消息
- 添加消息状态显示(发送中、已发送、已读)
- 实现消息分页加载(历史消息下拉加载)
2、性能优化
- 对长列表消息使用virtual-list虚拟列表,减少 DOM 节点
- 图片懒加载,避免一次性加载过多头像
- 消息数据本地缓存(wx.setStorageSync),提升页面加载速度
3、体验优化
- 添加消息发送动画
- 支持消息撤回动画过渡
- 键盘弹出时自动聚焦到输入框
五、总结
本文实现的聊天界面涵盖了消息展示、发送、撤回、复制等核心功能,并通过动态布局计算、键盘适配、自动滚动等细节处理,提供了良好的用户体验。代码结构清晰,逻辑明确,可作为小程序聊天功能开发的基础模板,根据实际需求进行扩展即可。
最后贴出详细的完整代码
下面是完整的代码,可以直接复制到微信小程序项目中使用:
1.js代码:
Page({
data: {
loginInfo: {
'userId': '20258888',
'nick': '西红柿炒番茄',
'avatar': '/static/images/local/logo.jpg',
},
dataList: [{
'chatId': '1752843999000',
'userId': '20250718',
'nick': '蒜鸟编程',
'avatar': 'https://img1.baidu.com/it/u=143492308,3605851733&fm=253&app=120&f=JPEG?w=800&h=800',
'status': '0',
'showTime': '21:06',
'content': '欢迎关注蒜鸟编程!',
'createDate': 1752843999000
},
{
'chatId': '1752844119000',
'userId': '20258888',
'nick': '西红柿炒番茄',
'avatar': '/static/images/local/logo.jpg',
'status': '0',
'content': '哈喽',
'createDate': 1752844119000
},
{
'chatId': '1752844179000',
'userId': '20258888',
'nick': '西红柿炒番茄',
'avatar': '/static/images/local/logo.jpg',
'status': '0',
'content': '还在吗?',
'createDate': 1752844179000
},
{
'chatId': '1752844299000',
'userId': '20250718',
'nick': '蒜鸟编程',
'avatar': 'https://img1.baidu.com/it/u=143492308,3605851733&fm=253&app=120&f=JPEG?w=800&h=800',
'status': '0',
'showTime': '21:11',
'content': '在的呀',
'createDate': 1752844299000
},
{
'chatId': '1752844479000',
'userId': '20250707',
'nick': '公众号',
'avatar': 'https://q6.itc.cn/q_70/images03/20250306/355fba6a5cb049f5b98c2ed9f03cc5e1.jpeg',
'status': '0',
'content': '我是公众号,嘿嘿,欢迎关注我呦~',
'createDate': 1752844479000
},
{
'chatId': '1752844659000',
'userId': '20258888',
'nick': '西红柿炒番茄',
'avatar': '/static/images/local/logo.jpg',
'status': '1',
'showTime': '21:17',
'content': '撤回的消息',
'createDate': 1752844659000
},
{
'chatId': '1752844839000',
'userId': '20258888',
'nick': '西红柿炒番茄',
'avatar': '/static/images/local/logo.jpg',
'status': '0',
'content': '谢谢大家',
'createDate': 1752844839000
},
],
msgInfo: '',
toolInfo: '',
scrollToId: '',
// 键盘计算相关
footHeight: 36,
chatHeight: 540,
viewHeight: 640,
boardHeight: 0,
lineCount: 1
},
onReady() {
let query = wx.createSelectorQuery();
let sys = wx.getSystemInfoSync().windowHeight;
query.select('#bottomArea').boundingClientRect((res) => {
let top = res.height;
this.setData({
footHeight: top,
chatHeight: sys - top,
viewHeight: sys,
scrollToId: 'endView'
});
}).exec();
},
// 页面点击监听
onPageTap() {
this.toolMethod();
},
toolMethod() {
this.setData({
toolInfo: ''
})
},
// 发送文本
sendTextMessage() {
const msg = this.data.msgInfo.trim();
if (!msg) return;
const {
viewHeight,
footHeight,
boardHeight,
loginInfo,
dataList
} = this.data;
let time = new Date().getTime();
let attr = [{
...loginInfo,
'chatId': time,
'status': '0',
'content': msg,
'createDate': time
}];
this.setData({
msgInfo: '',
chatHeight: viewHeight - footHeight - boardHeight,
dataList: [...dataList, ...attr]
}, () => {
this.scrollToBottom();
});
},
// 长按监听
toolClick(e) {
const row = e.currentTarget.dataset.item;
if (!row.status) {
return;
}
const top = e.changedTouches[0].clientY;
const flag = row.userId == this.data.loginInfo.userId;
row.boxRight = flag ? 'tool-r' : '';
row.boxBottom = top < 100 ? 'tool-b' : 'tool-t';
if (top < 100) {
row.trans = flag ? 'trans-blf trans-r' : 'trans-blf trans-l';
} else {
row.trans = flag ? 'trans-tlf trans-r' : 'trans-tlf trans-l';
}
row.showBack = ((Date.now() - row.createDate) < 5 * 60 * 1000) && flag;
this.setData({
toolInfo: row
});
},
// 更新聊天记录【撤回、复制】
buttonClick(e) {
const tag = e.currentTarget.dataset.type;
const obj = this.data.toolInfo;
if (tag == 'copy') {
wx.setClipboardData({
data: obj.content,
})
return;
}
const index = this.data.dataList.findIndex(item => item.chatId === obj.chatId);
if (index !== -1) {
this.setData({
[`dataList[${index}].status`]: '1'
});
}
},
// 回车换行监听
lineClick(e) {
let obj = e.detail;
let top = obj.height + 10;
if (obj.lineCount > 1 && obj.lineCount < 8) {
const {
viewHeight,
footHeight,
boardHeight
} = this.data;
this.setData({
chatHeight: viewHeight - footHeight - boardHeight - top,
lineCount: obj.lineCount
}, () => {
this.scrollToBottom();
})
}
},
// 键盘高度
getKeyBoardHeight(e) {
let board = e.detail.height || 0;
const {
viewHeight,
footHeight
} = this.data;
this.setData({
chatHeight: viewHeight - footHeight - board,
boardHeight: board
})
this.scrollToBottom();
},
//输入时数据绑定
inputClick(e) {
this.setData({
msgInfo: e.detail.value
});
},
// 滑动到最底部
scrollToBottom() {
this.setData({
scrollToId: 'endView'
})
},
})
2.wxml代码:
<scroll-view scroll-y="{{true}}" bindscrolltoupper="scrollToupperClick" scroll-into-view="{{scrollToId}}" bind:tap="onPageTap" style="height: {{chatHeight}}px;">
<view class="seat"></view>
<block wx:for="{{dataList}}" wx:for-item="item" wx:for-index="index" wx:key="item">
<view class="row-date" hidden="{{!item.showTime}}">
<text>{{item.showTime}}</text>
</view>
<view class="row-back" wx:if="{{item.status=='1'}}">
<text wx:if="{{item.userId==loginInfo.userId}}">你撤回了一条消息</text>
<text wx:else>“{{item.nick}}”你撤回了一条消息</text>
</view>
<view class="row {{item.userId==loginInfo.userId?'row-r':''}}" wx:else>
<view class="row-left">
<image class="row-avatar" src="{{item.avatar}}" mode="widthFix"></image>
</view>
<view class="row-right">
<view class="row-nick" wx:if="{{item.userId!=loginInfo.userId}}">{{item.nick}}</view>
<view class="row-info {{item.userId==loginInfo.userId?'info-r':'info-l'}}" bind:longpress="toolClick" data-item="{{item}}">{{item.content}}
</view>
<view class="level tool-box {{toolInfo.boxRight+' '+toolInfo.boxBottom}}" wx:if="{{toolInfo.chatId == item.chatId}}">
<view class="tool-trans {{toolInfo.trans}}"></view>
<view class="tool" bind:tap="buttonClick" data-type="copy">
<text>复制</text>
</view>
<view class="tool" bind:tap="buttonClick" data-type="back" wx:if="{{toolInfo.showBack}}">
<text>撤回</text>
</view>
</view>
</view>
</view>
</block>
<view id="endView" class="seat"></view>
</scroll-view>
<!-- 键盘 -->
<view class="foot-box" id="bottomArea" bind:tap="onPageTap">
<view class="level {{lineCount>2?'input-box':''}}">
<view class="input-area">
<textarea class="input-text" bindinput="inputClick" bindkeyboardheightchange="getKeyBoardHeight" bindlinechange="lineClick" value="{{msgInfo}}" show-confirm-bar="{{false}}" hold-keyboard="{{true}}" adjust-keyboard-to="bottom" cursor-spacing="{{20}}" confirm-type="return" confirm-hold="{{true}}" disable-default-padding="{{true}}" adjust-position="{{false}}" auto-height="{{true}}"></textarea>
</view>
<view class="input-btn">
<text bind:tap="sendTextMessage" class="input-send">发 送</text>
</view>
</view>
</view>
3.wxss代码:
page {
background-color: white;
overflow: hidden;
}
.seat {
height: 10rpx;
}
.row {
display: flex;
margin-bottom: 20rpx;
}
.row-r {
flex-direction: row-reverse;
text-align: right;
}
.row-left {
width: 15%;
text-align: center;
}
.row-avatar {
width: 80rpx;
height: 80rpx;
border-radius: 10rpx;
}
.row-date,
.row-back {
text-align: center;
font-size: 26rpx;
color: #616060;
margin-top: 20rpx;
margin-bottom: 20rpx;
}
.row-date text {
padding: 3rpx 15rpx;
border-radius: 5rpx;
background-color: #e9e9e9;
}
.row-right {
width: 70%;
position: relative;
}
.row-nick {
font-size: 24rpx;
margin-bottom: 10rpx;
color: #6a6b6e;
}
.row-info {
font-size: 30rpx;
border-radius: 10rpx;
padding: 20rpx;
color: white;
display: inline-block;
text-align: left;
}
.info-l {
background-color: #377AF6;
}
.info-r {
background-color: #18b566;
}
/* 弹出 */
.tool-box {
position: absolute;
z-index: 9;
background-color: #818181;
color: white;
padding: 15rpx 10rpx;
border-radius: 10rpx;
}
.tool {
margin: 0 20rpx;
display: flex;
flex-direction: column;
text-align: center;
font-size: 26rpx;
}
.tool-r {
right: 10rpx;
}
.tool-t {
top: -120rpx;
}
.tool-b {
bottom: -120rpx;
}
.tool-trans {
width: 0;
height: 0;
position: absolute;
text-align: center;
border-left: 14rpx solid transparent;
border-right: 14rpx solid transparent;
}
.trans-tlf {
bottom: -17rpx;
border-top: 20rpx solid #858383;
}
.trans-blf {
top: -17rpx;
border-bottom: 20rpx solid #858383;
}
.trans-r {
right: 20rpx;
}
.trans-l {
left: 20rpx;
}
/* 底部键盘 */
.foot-box {
background-color: #f1f1f1;
padding: 10rpx;
}
.level {
display: flex;
flex-direction: row;
align-items: center;
}
.input-box {
align-items: flex-end;
}
.input-area {
width: 84%;
padding: 20rpx;
background-color: white;
border-radius: 10rpx;
color: #6a6b6e;
}
.input-text {
width: 100%;
font-size: 32rpx;
max-height: 300rpx;
}
.input-btn {
width: 16%;
display: flex;
justify-content: center;
align-items: center;
}
.input-send {
color: white;
font-size: 30rpx;
padding: 15rpx;
background-color: #fa4242;
border-radius: 10rpx;
}
::-webkit-scrollbar {
width: 0;
height: 0;
}
4、json代码:
{
"navigationBarTitleText": "聊天窗口升级版",
"usingComponents": {},
"navigationBarTextStyle":"white",
"navigationBarBackgroundColor": "#2b85e4"
}
更多示例,请关注 蒜鸟编程(微信小程序 / 抖音 / 小红书/微信公众号/微博****等同名称),在这里,编程与生活的边界从未如此清晰,期待与大家共同成长!后续将围绕编程技术持续更新更多实践示例,如果在实现过程中遇到问题,或者有更好的优化建议,欢迎在评论区交流讨论!
更多推荐



所有评论(0)