微信小程序实现聊天窗口升级版_小程序开发聊天对话页面
微信小程序聊天界面实现:从布局到交互
在移动应用开发中,聊天功能是许多社交、客服类小程序的核心模块。本文将基于实际代码,详细讲解如何在微信小程序中实现一个功能完善的聊天界面,包括消息展示、发送、撤回、复制等核心功能,以及键盘适配、自动滚动等细节处理。
先看示例图片效果:
一、功能概述
本次实现的聊天界面包含以下核心功能:
区分当前用户与其他用户的消息(左对齐 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\"> <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\"}
更多示例,请关注 蒜鸟编程(微信小程序 / 抖音 / 小红书/微信公众号/微博****等同名称),在这里,编程与生活的边界从未如此清晰,期待与大家共同成长!后续将围绕编程技术持续更新更多实践示例,如果在实现过程中遇到问题,或者有更好的优化建议,欢迎在评论区交流讨论!