Browse Source

创建一对一聊天,实时

master
ZhaoYang 1 month ago
parent
commit
29ef3f50af
  1. 7
      app.json
  2. BIN
      pages/images/xx.png
  3. BIN
      pages/images/xx1.png
  4. 199
      pagesA/pages/advisory/advisory.js
  5. 26
      pagesA/pages/advisory/advisory.wxml
  6. 5
      pagesA/pages/advisory/advisory.wxss
  7. 1613
      pagesA/pages/expertChat/expertChat.js
  8. 155
      pagesA/pages/expertChat/expertChat.wxml
  9. 177
      pagesA/pages/expertChat/expertChat.wxss
  10. 15
      utils/api.js

7
app.json

@ -2,7 +2,6 @@
"pages": [ "pages": [
"pages/home/home", "pages/home/home",
"pages/login/login", "pages/login/login",
"pages/message/message",
"pages/personal/personal" "pages/personal/personal"
], ],
"subPackages": [ "subPackages": [
@ -48,12 +47,6 @@
"iconPath": "pages/images/home.png", "iconPath": "pages/images/home.png",
"selectedIconPath": "pages/images/home1.png" "selectedIconPath": "pages/images/home1.png"
}, },
{
"pagePath": "pages/message/message",
"text": "消息",
"iconPath": "pages/images/xx.png",
"selectedIconPath": "pages/images/xx1.png"
},
{ {
"pagePath": "pages/personal/personal", "pagePath": "pages/personal/personal",
"text": "我的", "text": "我的",

BIN
pages/images/xx.png

Before

Width: 200  |  Height: 200  |  Size: 4.3 KiB

BIN
pages/images/xx1.png

Before

Width: 200  |  Height: 200  |  Size: 4.3 KiB

199
pagesA/pages/advisory/advisory.js

@ -1,142 +1,129 @@
import http from '../../../utils/api'
const baseUrl = require('../../../utils/baseUrl')
Page({ Page({
data: { data: {
// 专家信息(新增在线状态)
expertInfo: {
avatar: 'https://api.dicebear.com/7.x/avataaars/png?seed=expert_pro&size=140',
name: '张振农',
specialty: '作物栽培 · 土壤改良',
experience: 15,
online: true // 在线状态
},
baseUrl: baseUrl,
user: {},
// 聊天申请列表数据 // 聊天申请列表数据
applyList: [], applyList: [],
// 分页相关 // 分页相关
page: 1, page: 1,
pageSize: 10, pageSize: 10,
hasMore: true,
total: 0, total: 0,
hasMore: true, // 是否还有更多数据
isLoading: false, // 是否正在加载
isFirstLoad: true, // 是否首次加载
}, },
onLoad() { onLoad() {
this.loadApplyList(1);
this.getzj()
this.getsessions(true) // true 表示首次加载(重置列表)
}, },
// 加载申请列表(增强模拟数据)
loadApplyList(page) {
const { pageSize } = this.data;
wx.showLoading({ title: '加载中...', mask: true });
setTimeout(() => {
const mockData = this.generateMockData(page, pageSize);
if (page === 1) {
// 个人专家信息
getzj() {
const user = wx.getStorageSync('userInfo')
console.log(222, user)
this.setData({ this.setData({
applyList: mockData.list,
total: mockData.total,
hasMore: mockData.hasMore,
page: page
});
} else {
this.setData({
applyList: this.data.applyList.concat(mockData.list),
total: mockData.total,
hasMore: mockData.hasMore,
page: page
});
}
wx.hideLoading();
}, 600);
user: user
})
}, },
// 生成更丰富的模拟数据(含标签)
generateMockData(page, pageSize) {
const total = 23;
const start = (page - 1) * pageSize;
const end = start + pageSize;
const hasMore = end < total;
const list = [];
// 咨询列表 - 支持分页加载
getsessions(isRefresh = false) {
// 防止重复请求
if (this.data.isLoading) return
// 如果是加载更多但已无更多数据,直接返回
if (!isRefresh && !this.data.hasMore) return
const seeds = ['farmer1', 'sheep', 'cattle', 'wheat', 'tractor', 'green', 'sun', 'rain', 'soil', 'seed'];
const firstNames = ['李', '王', '张', '刘', '赵', '陈', '杨', '周', '吴', '黄'];
const farmTypes = ['水稻种植', '牛羊养殖', '果树栽培', '蔬菜大棚', '有机农场', '蜂蜜养殖', '食用菌', '药材种植'];
const tagPool = [
['玉米', '虫害'],
['羊羔', '腹泻'],
['施肥', '建议'],
['土壤', '检测'],
['大棚', '温度'],
['猪病', '防治'],
['果树', '修剪'],
['蜂蜜', '取蜜']
];
this.setData({ isLoading: true })
for (let i = start; i < Math.min(end, total); i++) {
const id = `apply_${i + 1}`;
const userId = `user_${(i % 10) + 1}`;
const seedIndex = i % seeds.length;
const nameIndex = i % firstNames.length;
const typeIndex = (i * 3) % farmTypes.length;
const unread = i % 4 === 0 ? 2 : (i % 7 === 0 ? 1 : 0);
const statuses = ['pending', 'accepted', 'completed'];
const status = statuses[i % 3];
// 随机标签
const tags = tagPool[(i * 2) % tagPool.length];
// 准备请求参数
const params = {
page: isRefresh ? 1 : this.data.page,
pageSize: this.data.pageSize
}
const time = new Date(Date.now() - (i * 7200000) - Math.floor(Math.random() * 5000000));
const timeStr = `${time.getMonth()+1}.${time.getDate()} ${time.getHours()}:${time.getMinutes().toString().padStart(2,'0')}`;
http.sessions({
data: params,
success: (res) => {
console.log('接口返回数据:', res)
let newList = []
let total = 0
if (Array.isArray(res)) {
// 如果直接返回数组
newList = res
total = res.length
} else if (res.data && Array.isArray(res.data)) {
// 如果返回 { data: [], total: 100 }
newList = res.data
total = res.total || res.data.length
} else if (res.list && Array.isArray(res.list)) {
// 如果返回 { list: [], total: 100 }
newList = res.list
total = res.total || res.list.length
} else {
console.error('接口返回格式不正确', res)
newList = []
total = 0
}
// 更真实的预览消息
let lastMessage = '';
if (i % 5 === 0) lastMessage = '专家您好,我家玉米出现黄叶,能帮看看吗?';
else if (i % 3 === 0) lastMessage = '咨询一下羊羔腹泻的问题,急!';
else if (i % 4 === 0) lastMessage = '请问什么时候方便通话?';
else lastMessage = '请求添加您为咨询顾问';
// 计算是否有更多数据
const currentPage = isRefresh ? 1 : this.data.page
const pageSize = this.data.pageSize
const hasMore = currentPage * pageSize < total
list.push({
id: id,
user: {
id: userId,
name: firstNames[nameIndex] + (i % 2 === 0 ? '建国' : '秀英') + (i + 1),
avatar: `https://api.dicebear.com/7.x/avataaars/png?seed=${seeds[seedIndex]}_${i}&size=96`,
farmType: farmTypes[typeIndex],
this.setData({
applyList: isRefresh ? newList : [...this.data.applyList, ...newList],
total: total,
page: isRefresh ? 2 : this.data.page + 1, // 下次请求的页码
hasMore: hasMore,
isLoading: false,
isFirstLoad: false
})
}, },
applyTime: timeStr,
lastMessage: lastMessage,
unreadCount: unread,
status: status,
tags: tags,
});
fail: (err) => {
console.error('加载失败:', err)
wx.showToast({
title: '加载失败',
icon: 'none'
})
this.setData({
isLoading: false,
isFirstLoad: false
})
} }
})
},
return {
list,
total,
hasMore,
};
// 加载更多(上拉触底触发)
loadMore() {
// 如果有更多数据且不在加载中,则加载下一页
if (this.data.hasMore && !this.data.isLoading) {
this.getsessions(false)
}
}, },
// 处理点击申请项 // 处理点击申请项
handleApply(e) { handleApply(e) {
console.log(111,e);
console.log(111, e)
const id = e.currentTarget.dataset.id const id = e.currentTarget.dataset.id
wx.navigateTo({ wx.navigateTo({
url: `/pagesA/pages/expertChat/expertChat?id=${id}`, url: `/pagesA/pages/expertChat/expertChat?id=${id}`,
}) })
}, },
loadMore() {
const { hasMore, page } = this.data;
if (hasMore) {
this.loadApplyList(page + 1);
}
},
// 可选:下拉刷新功能
onPullDownRefresh() { onPullDownRefresh() {
this.setData({ page: 1, hasMore: true });
this.loadApplyList(1);
wx.stopPullDownRefresh();
this.setData({
page: 1,
hasMore: true
})
this.getsessions(true)
// 停止下拉刷新
wx.stopPullDownRefresh()
} }
});
})

26
pagesA/pages/advisory/advisory.wxml

@ -2,17 +2,18 @@
<!-- 头部:专家信息卡片 --> <!-- 头部:专家信息卡片 -->
<view class="expert-card"> <view class="expert-card">
<view class="avatar-wrapper"> <view class="avatar-wrapper">
<image class="avatar" src="{{expertInfo.avatar}}" mode="aspectFill"></image>
<view class="online-status {{expertInfo.online ? 'online' : 'offline'}}"></view>
<image class="avatar" src="{{user.user.avatar?baseUrl+user.user.avatar:'/pages/images/tx.png'}}" mode="aspectFill"></image>
<view class="online-status online"></view>
</view> </view>
<view class="info"> <view class="info">
<view class="name-row"> <view class="name-row">
<text class="name">{{expertInfo.name}}</text>
<text class="online-text">{{expertInfo.online ? '在线' : '离线'}}</text>
<text class="name">{{user.user.nickName}}</text>
<text class="senior">{{user.vetInfo.expertType}}</text>
<text class="online-text">在线</text>
</view> </view>
<view class="specialty">{{expertInfo.specialty}}</view>
<view class="specialty">{{user.vetInfo.specialty}}</view>
<view class="experience-tag"> <view class="experience-tag">
<text class="tag-text">从业 {{expertInfo.experience}} 年</text>
<text class="tag-text">从业 {{user.vetInfo.workExperience}}</text>
</view> </view>
</view> </view>
</view> </view>
@ -25,28 +26,27 @@
</view> </view>
</view> </view>
<scroll-view scroll-y class="apply-scroll" bindscrolltolower="loadMore" enhanced show-scrollbar="{{false}}">
<scroll-view scroll-y class="apply-scroll" bindscrolltolower="loadMore" enhanced show-scrollbar="{{false}}" lower-threshold="100">
<view class="apply-list"> <view class="apply-list">
<block wx:for="{{applyList}}" wx:key="id"> <block wx:for="{{applyList}}" wx:key="id">
<view class="apply-item" bindtap="handleApply" data-id="{{item.id}}" data-user="{{item.user}}">
<image class="user-avatar" src="{{item.user.avatar}}" mode="aspectFill"></image>
<view class="apply-item" bindtap="handleApply" data-id="{{item.otherUserId}}">
<image class="user-avatar" src="{{item.otherUserAvatar?baseUrl+item.otherUserAvatar:'/pages/images/tx.png'}}" mode="aspectFill"></image>
<view class="apply-content"> <view class="apply-content">
<view class="apply-header"> <view class="apply-header">
<view class="user-info"> <view class="user-info">
<text class="user-name">{{item.user.name}}</text>
<text class="user-name">{{item.otherUserName}}</text>
</view> </view>
<text class="apply-time">{{item.applyTime}}</text>
<text class="apply-time">{{item.lastMessageTime}}</text>
</view> </view>
<view class="message-area"> <view class="message-area">
<text class="message-preview">{{item.lastMessage || '请求咨询...'}}</text> <text class="message-preview">{{item.lastMessage || '请求咨询...'}}</text>
<view wx:if="{{item.unreadCount > 0}}" class="unread-badge">{{item.unreadCount > 99 ? '99+' : item.unreadCount}}</view>
</view> </view>
</view> </view>
</view> </view>
</block> </block>
<!-- 空状态提示 --> <!-- 空状态提示 -->
<view wx:if="{{applyList.length === 0}}" class="empty-state">
<view wx:if="{{applyList.length === 0 && !isLoading}}" class="empty-state">
<text class="empty-text">暂无新的咨询申请</text> <text class="empty-text">暂无新的咨询申请</text>
<text class="empty-subtext">稍后刷新试试</text> <text class="empty-subtext">稍后刷新试试</text>
</view> </view>

5
pagesA/pages/advisory/advisory.wxss

@ -81,10 +81,11 @@
text-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.2); text-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.2);
} }
.online-text { .online-text {
font-size: 26rpx;
font-size: 24rpx;
background-color: rgba(255, 255, 255, 0.2); background-color: rgba(255, 255, 255, 0.2);
padding: 3rpx 20rpx;
padding: 3rpx 14rpx;
border-radius: 30rpx; border-radius: 30rpx;
backdrop-filter: blur(4px); backdrop-filter: blur(4px);
border: 1rpx solid rgba(255, 255, 255, 0.15); border: 1rpx solid rgba(255, 255, 255, 0.15);

1613
pagesA/pages/expertChat/expertChat.js
File diff suppressed because it is too large
View File

155
pagesA/pages/expertChat/expertChat.wxml

@ -1,17 +1,14 @@
<!-- 专家端聊天页面 -->
<!-- 咨询页面 -->
<view class="consult-page"> <view class="consult-page">
<!-- 头部专家信息 --> <!-- 头部专家信息 -->
<view class="consult-header"> <view class="consult-header">
<view class="header-content"> <view class="header-content">
<view class="back-btn" bindtap="goBack">
<image src="/pagesA/images/back.png" class="back-icon"></image>
</view>
<view class="header-center"> <view class="header-center">
<view class="expert-info"> <view class="expert-info">
<text class="expert-name">{{conversation.userName || '用户'}}</text>
<text class="expert-name">{{conversation.otherUserName}}</text>
<view class="expert-status"> <view class="expert-status">
<view class="status-dot {{onlineStatus ? 'online' : 'offline'}}"></view>
<text class="status-text">{{onlineStatus ? '在线' : '离线'}}</text>
<view class="status-dot online"></view>
<text class="status-text">在线</text>
</view> </view>
</view> </view>
</view> </view>
@ -29,31 +26,31 @@
show-scrollbar="{{false}}" show-scrollbar="{{false}}"
bindscroll="onScroll" bindscroll="onScroll"
> >
<!-- 加载更多提示 -->
<view class="load-more-tip" wx:if="{{loadingMore}}">
<text>加载中...</text>
<!-- 日期分隔线 -->
<view class="date-divider" wx:if="{{showDateDivider}}">
<text class="date-text">{{todayDate}}</text>
</view> </view>
<!-- 消息列表 --> <!-- 消息列表 -->
<block wx:for="{{messageList}}" wx:key="id">
<!-- 对方消息(用户) -->
<block wx:for="{{messageList}}" wx:key="index">
<!-- 对方消息 -->
<view class="message-item message-left" wx:if="{{!item.isMe}}"> <view class="message-item message-left" wx:if="{{!item.isMe}}">
<view class="message-avatar"> <view class="message-avatar">
<image src="{{item.avatar || '/pages/images/tx.png'}}" class="avatar-img"></image>
<image src="{{conversation.otherUserAvatar?baseUrl+conversation.otherUserAvatar:' /pages/images/tx.png'}}" class="avatar-img"></image>
</view> </view>
<view class="messages-content-wrapper">
<view class="message-content-wrapper">
<view class="message-arrow arrow-left"></view> <view class="message-arrow arrow-left"></view>
<!-- 文本消息 --> <!-- 文本消息 -->
<view class="message-bubble bubble-left" wx:if="{{item.type === 'text'}}">
<view class="message-bubble bubble-left" wx:if="{{item.contentType === 'text'}}">
<text class="message-text">{{item.content}}</text> <text class="message-text">{{item.content}}</text>
</view> </view>
<!-- 图片消息 --> <!-- 图片消息 -->
<view class="media-bubble" wx:elif="{{item.type === 'image'}}">
<view class="media-bubble" wx:elif="{{item.contentType === 'image'}}">
<image <image
src="{{item.content}}"
src="{{baseUrl+item.content}}"
class="message-image" class="message-image"
mode="aspectFill" mode="aspectFill"
bindtap="previewImage" bindtap="previewImage"
@ -61,50 +58,28 @@
></image> ></image>
</view> </view>
<!-- 视频消息 -->
<view class="media-bubble" wx:elif="{{item.type === 'video'}}">
<video
src="{{item.content}}"
class="message-video"
controls
show-center-play-btn
poster="{{item.thumb}}"
></video>
<view class="video-play-overlay">
<image src="/images/icons/play.png" class="play-icon"></image>
</view>
</view>
<!-- 文件消息 -->
<view class="message-bubble bubble-left message-file" wx:elif="{{item.type === 'file'}}">
<view class="file-icon-box">
<image src="/images/icons/file_icon.png" class="file-icon"></image>
<!-- 消息状态 - 仅对方消息显示已读状态 -->
<view class="message-status" wx:if="{{item.status === 'read'}}">
<text class="status-text">已读</text>
</view> </view>
<view class="file-info">
<text class="file-name">{{item.fileName}}</text>
<text class="file-size">{{formatFileSize(item.fileSize)}}</text>
</view> </view>
</view> </view>
<!-- 消息时间(如果显示) -->
<text class="message-time" wx:if="{{item.showTime}}">{{formatTime(item.timestamp)}}</text>
</view>
</view>
<!-- 我的消息(专家) -->
<!-- 我的消息 -->
<view class="message-item message-right" wx:else> <view class="message-item message-right" wx:else>
<view class="message-content-wrapper"> <view class="message-content-wrapper">
<view class="message-arrow arrow-right"></view> <view class="message-arrow arrow-right"></view>
<!-- 文本消息 --> <!-- 文本消息 -->
<view class="message-bubble bubble-right" wx:if="{{item.type === 'text'}}">
<view class="message-bubble bubble-right" wx:if="{{item.contentType === 'text'}}">
<text class="message-text">{{item.content}}</text> <text class="message-text">{{item.content}}</text>
</view> </view>
<!-- 图片消息 --> <!-- 图片消息 -->
<view class="media-bubble" wx:elif="{{item.type === 'image'}}">
<view class="media-bubble" wx:elif="{{item.contentType === 'image'}}">
<image <image
src="{{item.content}}"
src="{{baseUrl+item.content}}"
class="message-image" class="message-image"
mode="aspectFill" mode="aspectFill"
bindtap="previewImage" bindtap="previewImage"
@ -117,74 +92,28 @@
</view> </view>
</view> </view>
</view> </view>
<!-- 视频消息 -->
<view class="media-bubble" wx:elif="{{item.type === 'video'}}">
<video
src="{{item.content}}"
class="message-video"
controls
show-center-play-btn
poster="{{item.thumb}}"
></video>
<view class="video-play-overlay">
<image src="/images/icons/play.png" class="play-icon"></image>
</view>
<view class="upload-progress" wx:if="{{item.status === 'uploading'}}">
<view class="progress-circle">
<text class="progress-text">{{item.progress}}%</text>
</view>
</view>
</view>
<!-- 文件消息 -->
<view class="message-bubble bubble-right message-file" wx:elif="{{item.type === 'file'}}">
<view class="file-icon-box">
<image src="/images/icons/file_icon.png" class="file-icon"></image>
</view>
<view class="file-info">
<text class="file-name">{{item.fileName}}</text>
<text class="file-size">{{formatFileSize(item.fileSize)}}</text>
</view>
<view class="upload-progress" wx:if="{{item.status === 'uploading'}}">
<view class="progress-circle">
<text class="progress-text">{{item.progress}}%</text>
</view>
</view>
</view>
<!-- 消息状态 -->
<view class="message-status" wx:if="{{item.status === 'sending'}}">
<text class="status-text">发送中</text>
</view>
<view class="message-status" wx:elif="{{item.status === 'fail'}}">
<text class="status-text fail">发送失败</text>
<text class="retry-text" bindtap="retrySend" data-id="{{item.id}}">重试</text>
</view>
<!-- 消息时间 -->
<text class="message-time" wx:if="{{item.showTime}}">{{formatTime(item.timestamp)}}</text>
</view> </view>
<view class="message-avatar"> <view class="message-avatar">
<image src="{{expertAvatar || '/pages/images/tx.png'}}" class="avatar-img"></image>
<image src="{{conversation.userAvatar?baseUrl+conversation.userAvatar:'/pages/images/tx.png'}}" class="avatar-img"></image>
</view> </view>
</view> </view>
</block> </block>
<!-- 底部空间 -->
<view class="chat-bottom-space"></view> <view class="chat-bottom-space"></view>
<!-- 空状态 -->
<view class="empty-tip" wx:if="{{messageList.length === 0}}">
<view class="load-more-tip" wx:if="{{loadingMore}}">
<text>加载中...</text>
</view>
<view class="empty-tip" wx:if="{{messageList.length === 0 && !loading}}">
<text class="empty-text">暂无聊天记录,开始咨询吧</text> <text class="empty-text">暂无聊天记录,开始咨询吧</text>
</view> </view>
</scroll-view> </scroll-view>
<!-- 输入区域 -->
<view class="input-section" id="inputSection"> <view class="input-section" id="inputSection">
<view class="text-input-panel"> <view class="text-input-panel">
<!-- 添加按钮 -->
<!-- 添加按钮 - 完美垂直居中 -->
<view class="add-btn" bindtap="showMediaActionSheet"> <view class="add-btn" bindtap="showMediaActionSheet">
<image src="/pagesA/images/add.png" class="add-icon"></image> <image src="/pagesA/images/add.png" class="add-icon"></image>
</view> </view>
@ -209,7 +138,7 @@
disable-default-padding="{{true}}" disable-default-padding="{{true}}"
></textarea> ></textarea>
<!-- 清空按钮 -->
<!-- 清空按钮 - 垂直居中 -->
<view class="input-actions" wx:if="{{inputValue}}"> <view class="input-actions" wx:if="{{inputValue}}">
<button class="clear-btn" bindtap="clearInput"> <button class="clear-btn" bindtap="clearInput">
<image src="/pagesA/images/ch.png" class="clear-icon"></image> <image src="/pagesA/images/ch.png" class="clear-icon"></image>
@ -217,7 +146,7 @@
</view> </view>
</view> </view>
<!-- 发送按钮 -->
<!-- 发送按钮 - 美观渐变绿色,完美垂直居中 -->
<button <button
class="send-btn" class="send-btn"
bindtap="sendTextMessage" bindtap="sendTextMessage"
@ -226,7 +155,7 @@
<text class="send-text">发送</text> <text class="send-text">发送</text>
</button> </button>
<!-- 占位 -->
<!-- 无内容时显示占位,保持布局稳定 -->
<view class="send-placeholder" wx:else></view> <view class="send-placeholder" wx:else></view>
</view> </view>
</view> </view>
@ -248,13 +177,6 @@
</view> </view>
<text class="option-text">照片</text> <text class="option-text">照片</text>
</view> </view>
<view class="media-option" bindtap="chooseVideo">
<view class="option-icon-box">
<image src="/pagesA/images/ps.png"></image>
</view>
<text class="option-text">视频</text>
</view>
</view> </view>
<view class="sheet-bottom"> <view class="sheet-bottom">
@ -262,19 +184,4 @@
</view> </view>
</view> </view>
</view> </view>
<!-- 更多菜单 -->
<view class="more-menu" wx:if="{{showMoreMenu}}" catchtap="hideMoreMenu">
<view class="menu-content">
<view class="menu-item" bindtap="clearHistory">
<text>清空聊天记录</text>
</view>
<view class="menu-item" bindtap="reportUser">
<text>举报用户</text>
</view>
<view class="menu-item" bindtap="blockUser">
<text>拉黑用户</text>
</view>
</view>
</view>
</view> </view>

177
pagesA/pages/expertChat/expertChat.wxss

@ -1,4 +1,4 @@
/* 专家端聊天样式 */
/* ========== 页面整体样式 ========== */
.consult-page { .consult-page {
width: 100vw; width: 100vw;
height: 100vh; height: 100vh;
@ -8,7 +8,7 @@
overflow: hidden; overflow: hidden;
} }
/* 头部样式 */
/* ========== 头部样式 ========== */
.consult-header { .consult-header {
background: #ffffff; background: #ffffff;
border-bottom: 1rpx solid #e5e5e5; border-bottom: 1rpx solid #e5e5e5;
@ -25,19 +25,6 @@
height: 96rpx; height: 96rpx;
} }
.back-btn, .header-right {
width: 48rpx;
height: 48rpx;
display: flex;
align-items: center;
justify-content: center;
}
.back-icon, .more-icon {
width: 40rpx;
height: 40rpx;
}
.header-center { .header-center {
flex: 1; flex: 1;
display: flex; display: flex;
@ -92,7 +79,7 @@
color: #666666; color: #666666;
} }
/* 聊天容器 */
/* ========== 聊天容器 ========== */
.chat-container { .chat-container {
flex: 1; flex: 1;
padding: 20rpx 0; padding: 20rpx 0;
@ -102,7 +89,23 @@
height: 0; height: 0;
} }
/* 消息项 */
/* 日期分隔线 */
.date-divider {
display: flex;
justify-content: center;
margin: 40rpx 0 30rpx;
}
.date-text {
background: rgba(0, 0, 0, 0.1);
padding: 8rpx 32rpx;
border-radius: 100rpx;
font-size: 24rpx;
color: #ffffff;
background-color: #d8d8d8;
}
/* ========== 消息项 ========== */
.message-item { .message-item {
display: flex; display: flex;
margin-bottom: 24rpx; margin-bottom: 24rpx;
@ -143,7 +146,7 @@
object-fit: cover; object-fit: cover;
} }
/* 消息内容包装器 */
/* 消息内容包装器 - 统一类名 */
.message-content-wrapper { .message-content-wrapper {
max-width: 480rpx; max-width: 480rpx;
position: relative; position: relative;
@ -238,26 +241,6 @@
background: #000000; background: #000000;
} }
.video-play-overlay {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 80rpx;
height: 80rpx;
border-radius: 50%;
background: rgba(0, 0, 0, 0.6);
display: flex;
align-items: center;
justify-content: center;
}
.play-icon {
width: 40rpx;
height: 40rpx;
margin-left: 4rpx;
}
/* 文件消息 */ /* 文件消息 */
.message-file { .message-file {
min-width: 280rpx; min-width: 280rpx;
@ -303,37 +286,7 @@
color: #666666; color: #666666;
} }
/* 消息时间 */
.message-time {
font-size: 22rpx;
color: #999999;
margin-top: 8rpx;
align-self: flex-start;
}
.message-right .message-time {
align-self: flex-end;
}
/* 消息状态 */
.message-status {
font-size: 22rpx;
color: #999999;
margin-top: 4rpx;
display: flex;
align-items: center;
gap: 8rpx;
}
.message-status .fail {
color: #ff4d4f;
}
.retry-text {
color: #1890ff;
text-decoration: underline;
margin-left: 8rpx;
}
/* 上传进度 */ /* 上传进度 */
.upload-progress { .upload-progress {
@ -400,7 +353,7 @@
color: #999999; color: #999999;
} }
/* 输入区域 */
/* ========== 输入区域 - 优化垂直对齐和按钮美化 ========== */
.input-section { .input-section {
background: #ffffff; background: #ffffff;
border-top: 1rpx solid #e5e5e5; border-top: 1rpx solid #e5e5e5;
@ -413,6 +366,7 @@
box-sizing: border-box; box-sizing: border-box;
} }
/* 文字输入面板 - 垂直居中优化 */
.text-input-panel { .text-input-panel {
display: flex; display: flex;
align-items: center; align-items: center;
@ -420,6 +374,7 @@
min-height: 72rpx; min-height: 72rpx;
} }
/* 添加按钮 - 完美垂直居中 */
.add-btn { .add-btn {
width: 72rpx; width: 72rpx;
height: 72rpx; height: 72rpx;
@ -434,6 +389,7 @@
height: 60rpx; height: 60rpx;
} }
/* 输入框包装器 - 优化高度和内边距 */
.input-wrapper { .input-wrapper {
flex: 1; flex: 1;
position: relative; position: relative;
@ -451,6 +407,7 @@
background: #e8e8e8; background: #e8e8e8;
} }
/* 多行文本输入框 - 优化垂直居中 */
.chat-textarea { .chat-textarea {
flex: 1; flex: 1;
width: 100%; width: 100%;
@ -467,12 +424,14 @@
overflow-y: auto; overflow-y: auto;
} }
/* 占位符样式 */
.input-placeholder { .input-placeholder {
color: #999999; color: #999999;
font-size: 28rpx; font-size: 28rpx;
line-height: 1.4; line-height: 1.4;
} }
/* 清空按钮 - 完美垂直居中 */
.input-actions { .input-actions {
position: absolute; position: absolute;
right: 16rpx; right: 16rpx;
@ -512,6 +471,7 @@
height: 28rpx; height: 28rpx;
} }
/* 发送按钮 */
.send-btn { .send-btn {
background: linear-gradient(135deg, #07c160 0%, #06ae56 100%); background: linear-gradient(135deg, #07c160 0%, #06ae56 100%);
width: 112rpx; width: 112rpx;
@ -531,12 +491,33 @@
overflow: hidden; overflow: hidden;
} }
/* 发送按钮点击效果 */
.send-btn:active { .send-btn:active {
background: linear-gradient(135deg, #06ae56 0%, #059c4c 100%); background: linear-gradient(135deg, #06ae56 0%, #059c4c 100%);
transform: scale(0.96); transform: scale(0.96);
box-shadow: 0 2rpx 4rpx rgba(7, 193, 96, 0.3); box-shadow: 0 2rpx 4rpx rgba(7, 193, 96, 0.3);
} }
/* 发送按钮水波纹效果 */
.send-btn::after {
content: '';
position: absolute;
top: 50%;
left: 50%;
width: 0;
height: 0;
border-radius: 50%;
background: rgba(255, 255, 255, 0.3);
transform: translate(-50%, -50%);
transition: width 0.3s, height 0.3s;
}
.send-btn:active::after {
width: 200rpx;
height: 200rpx;
opacity: 0;
}
.send-text { .send-text {
font-size: 28rpx; font-size: 28rpx;
color: #ffffff; color: #ffffff;
@ -544,13 +525,20 @@
letter-spacing: 2rpx; letter-spacing: 2rpx;
} }
/* 发送按钮占位 - 保持布局稳定 */
.send-placeholder { .send-placeholder {
width: 112rpx; width: 112rpx;
height: 72rpx; height: 72rpx;
flex-shrink: 0; flex-shrink: 0;
} }
/* 多媒体选择面板 */
/* 适配小屏幕 */
@media screen and (max-width: 320px) {
.send-btn { width: 100rpx; }
.send-placeholder { width: 100rpx; }
}
/* ========== 多媒体选择面板 ========== */
.media-action-sheet { .media-action-sheet {
position: fixed; position: fixed;
top: 0; top: 0;
@ -594,6 +582,12 @@
color: #000000; color: #000000;
} }
.close-sheet-btn {
display: flex;
align-items: center;
justify-content: center;
}
.close-sheet-btn image { .close-sheet-btn image {
width: 60rpx; width: 60rpx;
height: 60rpx; height: 60rpx;
@ -610,6 +604,11 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
border: none;
background: transparent;
padding: 0;
margin: 0;
line-height: 1;
} }
.option-icon-box { .option-icon-box {
@ -647,39 +646,7 @@
color: #999999; color: #999999;
} }
/* 更多菜单 */
.more-menu {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.3);
z-index: 2000;
}
.menu-content {
position: absolute;
top: 96rpx;
right: 24rpx;
background: #ffffff;
border-radius: 12rpx;
padding: 16rpx 0;
min-width: 200rpx;
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.15);
}
.menu-item {
padding: 24rpx 32rpx;
font-size: 28rpx;
color: #333333;
border-bottom: 1rpx solid #f0f0f0;
}
.menu-item:last-child {
border-bottom: none;
}
.menu-item:active {
background: #f5f5f5;
/* 适配全面屏 */
.safe-area-bottom {
padding-bottom: env(safe-area-inset-bottom);
} }

15
utils/api.js

@ -149,8 +149,21 @@ function feedback(params) {
http('/muhu/feedback/list', 'get', params) http('/muhu/feedback/list', 'get', params)
} }
//咨询列表
function sessions(params) {
http('/system/chat/sessions', 'get', params)
}
// 建立兽医一对一聊天
function create(params) {
http('/system/chat/session/create', 'post', params)
}
// 查找一对一聊天的记录
function direct(params) {
http('/system/chat/messages/direct', 'get', params)
}
@ -174,5 +187,5 @@ export default { // 暴露接口
login,carousel,getPhoneNumber,article,articleDetails,articleZd,wzd,wzdAdd,shareAdd, login,carousel,getPhoneNumber,article,articleDetails,articleZd,wzd,wzdAdd,shareAdd,
areaChildren,userCode,UserInfo,videoList,videoZd,videoDetails,forumList,forumAdd,forumDetails, areaChildren,userCode,UserInfo,videoList,videoZd,videoDetails,forumList,forumAdd,forumDetails,
forumReply,commentReply,experience,experiencezd,experienceDetails,realName,revise,feedback, forumReply,commentReply,experience,experiencezd,experienceDetails,realName,revise,feedback,
today,carouselDetail,videoAdd,articleAdd,wzdxq,fazdAdd
today,carouselDetail,videoAdd,articleAdd,wzdxq,fazdAdd,sessions,create,direct
} }
Loading…
Cancel
Save