Browse Source

兽医端咨询列表,详情

master
ZhaoYang 1 month ago
parent
commit
c1d214f87e
  1. 4
      app.json
  2. BIN
      pages/images/kzt.png
  3. 16
      pages/personal/personal.js
  4. BIN
      pagesA/images/add.png
  5. BIN
      pagesA/images/cuo.png
  6. BIN
      pagesA/images/duig.png
  7. BIN
      pagesA/images/ps.png
  8. BIN
      pagesA/images/zp.png
  9. 27
      pagesA/pages/advisory/advisory.js
  10. 1
      pagesA/pages/askingSyDetails/askingSyDetails.js
  11. 534
      pagesA/pages/expertChat/expertChat.js
  12. 4
      pagesA/pages/expertChat/expertChat.json
  13. 280
      pagesA/pages/expertChat/expertChat.wxml
  14. 685
      pagesA/pages/expertChat/expertChat.wxss
  15. 127
      pagesA/pages/serviceEvaluation/serviceEvaluation.js
  16. 4
      pagesA/pages/serviceEvaluation/serviceEvaluation.json
  17. 53
      pagesA/pages/serviceEvaluation/serviceEvaluation.wxml
  18. 146
      pagesA/pages/serviceEvaluation/serviceEvaluation.wxss
  19. 20
      pagesB/pages/publishAdd/publishAdd.js
  20. 2
      project.private.config.json
  21. 12
      utils/api.js

4
app.json

@ -15,7 +15,9 @@
"pages/advisory/advisory", "pages/advisory/advisory",
"pages/releaseSuffer/releaseSuffer", "pages/releaseSuffer/releaseSuffer",
"pages/precept/precept", "pages/precept/precept",
"pages/attestation/attestation"
"pages/attestation/attestation",
"pages/serviceEvaluation/serviceEvaluation",
"pages/expertChat/expertChat"
] ]
}, },
{ {

BIN
pages/images/kzt.png

After

Width: 320  |  Height: 320  |  Size: 8.5 KiB

16
pages/personal/personal.js

@ -48,6 +48,13 @@ Page({
}, },
// 服务评价
showFeedback(){
wx.navigateTo({
url: '/pagesA/pages/serviceEvaluation/serviceEvaluation',
})
},
onShow() { onShow() {
console.log('个人中心页面显示,重新获取用户信息') console.log('个人中心页面显示,重新获取用户信息')
// 每次显示页面都从服务器获取最新数据 // 每次显示页面都从服务器获取最新数据
@ -144,13 +151,6 @@ Page({
avatarUrl: uploadedFilePath, avatarUrl: uploadedFilePath,
}) })
// 更新缓存中的用户信息
const cachedUserInfo = wx.getStorageSync('userInfo') || {}
if (!cachedUserInfo.user) {
cachedUserInfo.user = {}
}
cachedUserInfo.user.avatar = uploadedFilePath
wx.setStorageSync('userInfo', cachedUserInfo)
// 更新头像的API // 更新头像的API
http.revise({ http.revise({
data: { data: {
@ -302,8 +302,6 @@ Page({
}, },
// 退出登录相关 // 退出登录相关
showLogoutConfirm() { showLogoutConfirm() {
this.setData({ this.setData({

BIN
pagesA/images/add.png

After

Width: 200  |  Height: 200  |  Size: 4.4 KiB

BIN
pagesA/images/cuo.png

After

Width: 200  |  Height: 200  |  Size: 4.6 KiB

BIN
pagesA/images/duig.png

After

Width: 200  |  Height: 200  |  Size: 2.5 KiB

BIN
pagesA/images/ps.png

After

Width: 200  |  Height: 200  |  Size: 3.3 KiB

BIN
pagesA/images/zp.png

After

Width: 200  |  Height: 200  |  Size: 2.7 KiB

27
pagesA/pages/advisory/advisory.js

@ -118,28 +118,11 @@ Page({
// 处理点击申请项 // 处理点击申请项
handleApply(e) { handleApply(e) {
const { id, user } = e.currentTarget.dataset;
wx.showActionSheet({
itemList: ['接受并回复', '标记为已读', '稍后处理'],
success: (res) => {
if (res.tapIndex === 0) {
wx.showToast({ title: '已接受申请', icon: 'success' });
// 模拟更新状态
const newList = this.data.applyList.map(item =>
item.id === id ? {...item, status: 'accepted'} : item
);
this.setData({ applyList: newList });
} else if (res.tapIndex === 1) {
const newList = this.data.applyList.map(item =>
item.id === id ? {...item, unreadCount: 0} : item
);
this.setData({ applyList: newList });
wx.showToast({ title: '已标记已读', icon: 'none' });
} else {
wx.showToast({ title: '已稍后处理', icon: 'none' });
}
}
});
console.log(111,e);
const id = e.currentTarget.dataset.id
wx.navigateTo({
url: `/pagesA/pages/expertChat/expertChat?id=${id}`,
})
}, },

1
pagesA/pages/askingSyDetails/askingSyDetails.js

@ -1,4 +1,3 @@
// 第一个页面的完整JS代码 - pages/diagnosisDetail/diagnosisDetail.js
import http from '../../../utils/api' import http from '../../../utils/api'
const baseUrl = require('../../../utils/baseUrl') const baseUrl = require('../../../utils/baseUrl')

534
pagesA/pages/expertChat/expertChat.js

@ -0,0 +1,534 @@
// 专家端聊天页面 - 纯模拟数据,无真实接口
Page({
data: {
// 专家信息
expertInfo: {
id: 'expert_001',
name: '张医生',
avatar: '/pages/images/tx.png'
},
expertAvatar: '/pages/images/tx.png',
// 对话信息
conversation: {
userId: 'user_001',
userName: '李华',
userAvatar: '/pages/images/tx.png'
},
// 在线状态
onlineStatus: true,
// 消息列表
messageList: [],
scrollTop: 0,
scrollAnimate: true,
// 输入相关
inputValue: '',
inputFocus: false,
// 多媒体面板
showMediaSheet: false,
// 页面状态
loadingMore: false,
hasMore: true,
page: 1,
// 模拟数据
mockMessages: [
{
id: 'msg_1',
isMe: false,
type: 'text',
content: '医生您好,我想咨询一下皮肤问题',
timestamp: Date.now() - 3600000,
status: 'success',
avatar: '/pages/images/tx.png'
},
{
id: 'msg_2',
isMe: true,
type: 'text',
content: '您好,请描述一下您的具体症状',
timestamp: Date.now() - 3500000,
status: 'success',
avatar: '/pages/images/tx.png'
},
{
id: 'msg_3',
isMe: false,
type: 'text',
content: '脸上起了很多小红点,有点痒',
timestamp: Date.now() - 3400000,
status: 'success',
avatar: '/pages/images/tx.png'
},
{
id: 'msg_4',
isMe: false,
type: 'image',
content: 'https://picsum.photos/200/200?random=1',
timestamp: Date.now() - 3300000,
status: 'success',
avatar: '/pages/images/tx.png'
},
{
id: 'msg_5',
isMe: true,
type: 'text',
content: '看起来像是过敏反应,最近有接触什么新的东西吗?',
timestamp: Date.now() - 3200000,
status: 'success',
avatar: '/pages/images/tx.png'
}
],
// 定时器
typingTimer: null,
mockUserTimer: null
},
onLoad: function(options) {
console.log('专家端聊天页面加载', options);
// 获取传递的参数
if (options.userId) {
this.setData({
'conversation.userId': options.userId,
'conversation.userName': options.userName || '用户'
});
}
// 加载模拟消息
this.loadMockMessages();
// 模拟用户在线状态变化
this.simulateOnlineStatus();
// 模拟用户发送消息
this.startMockUserTyping();
},
onShow: function() {
// 滚动到底部
setTimeout(() => {
this.scrollToBottom(false);
}, 200);
},
onUnload: function() {
// 清理定时器
if (this.data.mockUserTimer) {
clearInterval(this.data.mockUserTimer);
}
if (this.data.typingTimer) {
clearTimeout(this.data.typingTimer);
}
},
// 返回上一页
goBack: function() {
wx.navigateBack({
delta: 1
});
},
// 加载模拟消息
loadMockMessages: function() {
// 处理消息时间显示
const messages = this.processMessageTimes(this.data.mockMessages);
this.setData({
messageList: messages,
hasMore: false
});
},
// 加载更多消息(模拟)
loadMoreMessages: function() {
if (this.data.loadingMore || !this.data.hasMore) return;
this.setData({ loadingMore: true });
// 模拟网络延迟
setTimeout(() => {
// 模拟更早的消息
const olderMessages = [
{
id: 'old_' + Date.now(),
isMe: false,
type: 'text',
content: '之前也出现过类似情况',
timestamp: Date.now() - 86400000,
status: 'success',
avatar: '/pages/images/tx.png'
},
{
id: 'old_' + (Date.now() + 1),
isMe: true,
type: 'text',
content: '那当时是怎么处理的呢?',
timestamp: Date.now() - 86000000,
status: 'success',
avatar: '/pages/images/tx.png'
}
];
const processedOld = this.processMessageTimes(olderMessages);
const newList = [...processedOld, ...this.data.messageList];
this.setData({
messageList: newList,
loadingMore: false,
hasMore: false // 模拟没有更多了
});
// 调整滚动位置
setTimeout(() => {
this.setData({
scrollAnimate: false,
scrollTop: 300
}, () => {
setTimeout(() => {
this.setData({ scrollAnimate: true });
}, 100);
});
}, 50);
}, 800);
},
// 模拟用户在线状态变化
simulateOnlineStatus: function() {
setInterval(() => {
// 随机改变在线状态(模拟)
const random = Math.random();
this.setData({
onlineStatus: random > 0.3 // 70%时间在线
});
}, 30000);
},
// 模拟用户正在输入并发送消息
startMockUserTyping: function() {
// 每45-90秒模拟用户发送一条消息
const timer = setInterval(() => {
// 只有在线时才发送
if (this.data.onlineStatus) {
this.simulateUserMessage();
}
}, Math.random() * 45000 + 45000); // 45-90秒
this.setData({ mockUserTimer: timer });
},
// 模拟用户发送消息
simulateUserMessage: function() {
const messages = [
'好的,我明白了',
'谢谢医生!',
'需要用什么药吗?',
'大概多久能好?',
'有没有什么需要注意的?',
'好的,我试试看',
'明白了,非常感谢!'
];
const randomMsg = messages[Math.floor(Math.random() * messages.length)];
const newMsg = {
id: 'user_' + Date.now() + Math.random(),
isMe: false,
type: 'text',
content: randomMsg,
timestamp: Date.now(),
status: 'success',
avatar: this.data.conversation.userAvatar || '/pages/images/tx.png'
};
this.addMessageToList(newMsg);
// 震动提示(可选)
wx.vibrateShort({ type: 'light' });
},
// 发送文本消息
sendTextMessage: function() {
const content = this.data.inputValue.trim();
if (!content) return;
const messageId = 'msg_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
// 创建本地消息
const newMessage = {
id: messageId,
isMe: true,
type: 'text',
content: content,
timestamp: Date.now(),
status: 'sending',
avatar: this.data.expertAvatar
};
// 添加到列表
this.addMessageToList(newMessage);
// 清空输入框
this.setData({ inputValue: '' });
// 模拟发送延迟
setTimeout(() => {
this.updateMessageStatus(messageId, 'success');
}, 500);
},
// 添加消息到列表
addMessageToList: function(message) {
const { messageList } = this.data;
// 新消息添加到末尾
messageList.push(message);
// 重新处理时间显示
const processedMessages = this.processMessageTimes(messageList);
this.setData({
messageList: processedMessages
}, () => {
this.scrollToBottom(true);
});
},
// 更新消息状态
updateMessageStatus: function(messageId, status) {
const { messageList } = this.data;
const index = messageList.findIndex(msg => msg.id === messageId);
if (index !== -1) {
messageList[index].status = status;
this.setData({ messageList });
}
},
// 重试发送
retrySend: function(e) {
const messageId = e.currentTarget.dataset.id;
const { messageList } = this.data;
const msg = messageList.find(m => m.id === messageId);
if (msg) {
msg.status = 'sending';
this.setData({ messageList });
setTimeout(() => {
msg.status = 'success';
this.setData({ messageList });
}, 500);
}
},
// 处理消息时间显示
processMessageTimes: function(messages) {
if (!messages || messages.length === 0) return [];
const timeInterval = 5; // 5分钟间隔显示时间
return messages.map((msg, index) => {
const showTime = index === 0 ||
(msg.timestamp - messages[index - 1].timestamp) > timeInterval * 60 * 1000;
return {
...msg,
showTime
};
});
},
// 格式化时间
formatTime: function(timestamp) {
if (!timestamp) return '';
const date = new Date(Number(timestamp));
const hours = date.getHours().toString().padStart(2, '0');
const minutes = date.getMinutes().toString().padStart(2, '0');
return `${hours}:${minutes}`;
},
// 格式化文件大小
formatFileSize: function(bytes) {
if (!bytes || bytes === 0) return '0B';
const units = ['B', 'KB', 'MB', 'GB'];
let size = bytes;
let unitIndex = 0;
while (size >= 1024 && unitIndex < units.length - 1) {
size /= 1024;
unitIndex++;
}
return size.toFixed(1) + units[unitIndex];
},
// 输入处理
onInput: function(e) {
this.setData({ inputValue: e.detail.value });
},
// 输入框获得焦点
onInputFocus: function() {
this.setData({ inputFocus: true }, () => {
setTimeout(() => {
this.scrollToBottom(true);
}, 200);
});
},
// 输入框失去焦点
onInputBlur: function() {
this.setData({ inputFocus: false });
},
// 清除输入
clearInput: function() {
this.setData({
inputValue: '',
inputFocus: true
});
},
// 滚动事件
onScroll: function(e) {
const scrollTop = e.detail.scrollTop;
this.setData({ lastScrollTop: scrollTop });
// 滚动到顶部加载更多
if (scrollTop <= 30 && !this.data.loadingMore && this.data.hasMore) {
this.loadMoreMessages();
}
},
// 滚动到底部
scrollToBottom: function(animate = true) {
this.setData({
scrollAnimate: animate
}, () => {
setTimeout(() => {
this.setData({ scrollTop: 999999 });
}, 50);
});
},
// 显示多媒体选择面板
showMediaActionSheet: function() {
this.setData({
showMediaSheet: true,
inputFocus: false
});
},
// 隐藏多媒体选择面板
hideMediaActionSheet: function() {
this.setData({ showMediaSheet: false });
},
// 选择图片(模拟)
chooseImage: function() {
this.hideMediaActionSheet();
// 模拟选择图片
const mockImages = [
'https://picsum.photos/200/200?random=2',
'https://picsum.photos/200/200?random=3',
'https://picsum.photos/200/200?random=4'
];
const randomImage = mockImages[Math.floor(Math.random() * mockImages.length)];
const messageId = 'img_' + Date.now();
const newMessage = {
id: messageId,
isMe: true,
type: 'image',
content: randomImage,
timestamp: Date.now(),
status: 'uploading',
progress: 0,
avatar: this.data.expertAvatar
};
this.addMessageToList(newMessage);
// 模拟上传进度
let progress = 0;
const interval = setInterval(() => {
progress += 20;
if (progress >= 100) {
clearInterval(interval);
this.updateMessageStatus(messageId, 'success');
} else {
const msgIndex = this.data.messageList.findIndex(m => m.id === messageId);
if (msgIndex !== -1) {
this.data.messageList[msgIndex].progress = progress;
this.setData({ messageList: this.data.messageList });
}
}
}, 200);
},
// 选择视频(模拟)
chooseVideo: function() {
this.hideMediaActionSheet();
const messageId = 'video_' + Date.now();
const newMessage = {
id: messageId,
isMe: true,
type: 'video',
content: 'https://example.com/video.mp4',
thumb: 'https://picsum.photos/200/200?random=5',
timestamp: Date.now(),
status: 'uploading',
progress: 0,
avatar: this.data.expertAvatar
};
this.addMessageToList(newMessage);
// 模拟上传进度
let progress = 0;
const interval = setInterval(() => {
progress += 25;
if (progress >= 100) {
clearInterval(interval);
this.updateMessageStatus(messageId, 'success');
} else {
const msgIndex = this.data.messageList.findIndex(m => m.id === messageId);
if (msgIndex !== -1) {
this.data.messageList[msgIndex].progress = progress;
this.setData({ messageList: this.data.messageList });
}
}
}, 300);
},
// 预览图片
previewImage: function(e) {
const url = e.currentTarget.dataset.url;
wx.previewImage({
current: url,
urls: [url]
});
},
// 阻止事件冒泡
stopPropagation: function() {}
});

4
pagesA/pages/expertChat/expertChat.json

@ -0,0 +1,4 @@
{
"navigationBarTitleText":"咨询",
"usingComponents": {}
}

280
pagesA/pages/expertChat/expertChat.wxml

@ -0,0 +1,280 @@
<!-- 专家端聊天页面 -->
<view class="consult-page">
<!-- 头部专家信息 -->
<view class="consult-header">
<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="expert-info">
<text class="expert-name">{{conversation.userName || '用户'}}</text>
<view class="expert-status">
<view class="status-dot {{onlineStatus ? 'online' : 'offline'}}"></view>
<text class="status-text">{{onlineStatus ? '在线' : '离线'}}</text>
</view>
</view>
</view>
</view>
</view>
<!-- 聊天内容区域 -->
<scroll-view
id="chatScroll"
class="chat-container"
scroll-y
scroll-top="{{scrollTop}}"
scroll-with-animation="{{scrollAnimate}}"
enable-back-to-top="true"
show-scrollbar="{{false}}"
bindscroll="onScroll"
>
<!-- 加载更多提示 -->
<view class="load-more-tip" wx:if="{{loadingMore}}">
<text>加载中...</text>
</view>
<!-- 消息列表 -->
<block wx:for="{{messageList}}" wx:key="id">
<!-- 对方消息(用户) -->
<view class="message-item message-left" wx:if="{{!item.isMe}}">
<view class="message-avatar">
<image src="{{item.avatar || '/pages/images/tx.png'}}" class="avatar-img"></image>
</view>
<view class="messages-content-wrapper">
<view class="message-arrow arrow-left"></view>
<!-- 文本消息 -->
<view class="message-bubble bubble-left" wx:if="{{item.type === 'text'}}">
<text class="message-text">{{item.content}}</text>
</view>
<!-- 图片消息 -->
<view class="media-bubble" wx:elif="{{item.type === 'image'}}">
<image
src="{{item.content}}"
class="message-image"
mode="aspectFill"
bindtap="previewImage"
data-url="{{item.content}}"
></image>
</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>
<view class="file-info">
<text class="file-name">{{item.fileName}}</text>
<text class="file-size">{{formatFileSize(item.fileSize)}}</text>
</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-content-wrapper">
<view class="message-arrow arrow-right"></view>
<!-- 文本消息 -->
<view class="message-bubble bubble-right" wx:if="{{item.type === 'text'}}">
<text class="message-text">{{item.content}}</text>
</view>
<!-- 图片消息 -->
<view class="media-bubble" wx:elif="{{item.type === 'image'}}">
<image
src="{{item.content}}"
class="message-image"
mode="aspectFill"
bindtap="previewImage"
data-url="{{item.content}}"
></image>
<!-- 上传进度 -->
<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="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 class="message-avatar">
<image src="{{expertAvatar || '/pages/images/tx.png'}}" class="avatar-img"></image>
</view>
</view>
</block>
<!-- 底部空间 -->
<view class="chat-bottom-space"></view>
<!-- 空状态 -->
<view class="empty-tip" wx:if="{{messageList.length === 0}}">
<text class="empty-text">暂无聊天记录,开始咨询吧</text>
</view>
</scroll-view>
<!-- 输入区域 -->
<view class="input-section" id="inputSection">
<view class="text-input-panel">
<!-- 添加按钮 -->
<view class="add-btn" bindtap="showMediaActionSheet">
<image src="/pagesA/images/add.png" class="add-icon"></image>
</view>
<!-- 输入框包装器 -->
<view class="input-wrapper">
<textarea
auto-height
maxlength="500"
class="chat-textarea"
placeholder="请输入消息..."
placeholder-class="input-placeholder"
value="{{inputValue}}"
bindinput="onInput"
confirm-type="send"
focus="{{inputFocus}}"
adjust-position="{{false}}"
cursor-spacing="20"
bindfocus="onInputFocus"
bindblur="onInputBlur"
show-confirm-bar="{{false}}"
disable-default-padding="{{true}}"
></textarea>
<!-- 清空按钮 -->
<view class="input-actions" wx:if="{{inputValue}}">
<button class="clear-btn" bindtap="clearInput">
<image src="/pagesA/images/ch.png" class="clear-icon"></image>
</button>
</view>
</view>
<!-- 发送按钮 -->
<button
class="send-btn"
bindtap="sendTextMessage"
wx:if="{{inputValue}}"
>
<text class="send-text">发送</text>
</button>
<!-- 占位 -->
<view class="send-placeholder" wx:else></view>
</view>
</view>
<!-- 多媒体选择面板 -->
<view class="media-action-sheet" wx:if="{{showMediaSheet}}" catchtap="hideMediaActionSheet">
<view class="media-sheet-content" catchtap="stopPropagation">
<view class="media-sheet-header">
<text class="sheet-title">发送内容</text>
<view class="close-sheet-btn" bindtap="hideMediaActionSheet">
<image src="/pagesA/images/cuo.png"></image>
</view>
</view>
<view class="media-options-grid">
<view class="media-option" bindtap="chooseImage">
<view class="option-icon-box">
<image src="/pagesA/images/zp.png"></image>
</view>
<text class="option-text">照片</text>
</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 class="sheet-bottom">
<text class="bottom-tip">最多可选择9张照片</text>
</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>

685
pagesA/pages/expertChat/expertChat.wxss

@ -0,0 +1,685 @@
/* 专家端聊天样式 */
.consult-page {
width: 100vw;
height: 100vh;
background: #f5f5f5;
display: flex;
flex-direction: column;
overflow: hidden;
}
/* 头部样式 */
.consult-header {
background: #ffffff;
border-bottom: 1rpx solid #e5e5e5;
position: relative;
z-index: 1000;
box-shadow: 0 1rpx 6rpx rgba(0, 0, 0, 0.05);
flex-shrink: 0;
}
.header-content {
display: flex;
align-items: center;
padding: 12rpx 24rpx;
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 {
flex: 1;
display: flex;
justify-content: center;
}
.expert-info {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.expert-name {
font-size: 32rpx;
font-weight: 600;
color: #000000;
line-height: 44rpx;
margin-bottom: 4rpx;
}
.expert-status {
display: flex;
align-items: center;
justify-content: center;
}
.status-dot {
width: 16rpx;
height: 16rpx;
border-radius: 50%;
margin-right: 8rpx;
}
.status-dot.online {
background: #07c160;
animation: pulse 2s infinite;
}
.status-dot.offline {
background: #cccccc;
}
@keyframes pulse {
0% { box-shadow: 0 0 0 0 rgba(7, 193, 96, 0.4); }
70% { box-shadow: 0 0 0 8rpx rgba(7, 193, 96, 0); }
100% { box-shadow: 0 0 0 0 rgba(7, 193, 96, 0); }
}
.status-text {
font-size: 24rpx;
color: #666666;
}
/* 聊天容器 */
.chat-container {
flex: 1;
padding: 20rpx 0;
background: #f5f5f5;
overflow-y: auto;
position: relative;
height: 0;
}
/* 消息项 */
.message-item {
display: flex;
margin-bottom: 24rpx;
padding: 0 30rpx;
opacity: 0;
animation: fadeIn 0.3s ease forwards;
align-items: flex-start;
position: relative;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10rpx); }
to { opacity: 1; transform: translateY(0); }
}
.message-left { justify-content: flex-start; }
.message-right { justify-content: flex-end; }
/* 头像 */
.message-avatar {
width: 80rpx;
height: 80rpx;
border-radius: 8rpx;
overflow: hidden;
flex-shrink: 0;
background: #ffffff;
border: 1rpx solid #f0f0f0;
position: relative;
z-index: 1;
}
.message-left .message-avatar { margin-right: 16rpx; }
.message-right .message-avatar { margin-left: 16rpx; }
.avatar-img {
width: 100%;
height: 100%;
object-fit: cover;
}
/* 消息内容包装器 */
.message-content-wrapper {
max-width: 480rpx;
position: relative;
display: flex;
flex-direction: column;
z-index: 2;
}
.message-left .message-content-wrapper { align-items: flex-start; }
.message-right .message-content-wrapper { align-items: flex-end; }
/* 气泡箭头 */
.message-arrow {
position: absolute;
width: 0;
height: 0;
border-style: solid;
border-width: 12rpx;
top: 30rpx;
z-index: 1;
}
.arrow-left {
left: -24rpx;
border-color: transparent #ffffff transparent transparent;
}
.arrow-right {
right: -24rpx;
border-color: transparent transparent transparent #95ec69;
}
/* 消息气泡 */
.message-bubble {
position: relative;
padding: 16rpx 20rpx;
word-break: break-word;
box-sizing: border-box;
min-height: 60rpx;
display: flex;
align-items: center;
}
.bubble-left {
background: #ffffff;
border-radius: 10rpx;
border-top-left-radius: 4rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05);
}
.bubble-right {
background: #95ec69;
border-radius: 10rpx;
border-top-right-radius: 4rpx;
box-shadow: 0 2rpx 8rpx rgba(149, 236, 105, 0.2);
}
/* 文本消息 */
.message-text {
font-size: 32rpx;
color: #000000;
line-height: 1.4;
}
.bubble-right .message-text { color: #000000; }
/* 媒体消息 */
.media-bubble {
position: relative;
border-radius: 10rpx;
overflow: hidden;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05);
background: #ffffff;
min-height: 60rpx;
display: flex;
align-items: center;
justify-content: center;
}
.message-left .media-bubble { border-top-left-radius: 4rpx; }
.message-right .media-bubble { border-top-right-radius: 4rpx; }
.message-image {
width: 280rpx;
height: 280rpx;
display: block;
}
.message-video {
width: 280rpx;
height: 280rpx;
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 {
min-width: 280rpx;
padding: 20rpx;
display: flex;
align-items: center;
min-height: 60rpx;
}
.file-icon-box {
width: 56rpx;
height: 72rpx;
margin-right: 16rpx;
position: relative;
flex-shrink: 0;
}
.file-icon {
width: 100%;
height: 100%;
}
.file-info {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
overflow: hidden;
}
.file-name {
font-size: 28rpx;
font-weight: 500;
color: #000000;
margin-bottom: 6rpx;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.file-size {
font-size: 24rpx;
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 {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
border-radius: inherit;
}
.progress-circle {
width: 80rpx;
height: 80rpx;
border-radius: 50%;
border: 6rpx solid rgba(255, 255, 255, 0.3);
border-top-color: #ffffff;
animation: spin 1s linear infinite;
display: flex;
align-items: center;
justify-content: center;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.progress-text {
font-size: 20rpx;
color: #ffffff;
font-weight: 600;
}
/* 底部留白 */
.chat-bottom-space { height: 40rpx; }
/* 加载更多 */
.load-more-tip {
display: flex;
justify-content: center;
align-items: center;
padding: 30rpx 0;
color: #999999;
font-size: 24rpx;
}
/* 空状态 */
.empty-tip {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 100rpx 0;
color: #999999;
}
.empty-text {
font-size: 28rpx;
color: #999999;
}
/* 输入区域 */
.input-section {
background: #ffffff;
border-top: 1rpx solid #e5e5e5;
padding: 16rpx 30rpx;
position: relative;
z-index: 100;
box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.05);
flex-shrink: 0;
width: 100%;
box-sizing: border-box;
}
.text-input-panel {
display: flex;
align-items: center;
gap: 16rpx;
min-height: 72rpx;
}
.add-btn {
width: 72rpx;
height: 72rpx;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.add-icon {
width: 60rpx;
height: 60rpx;
}
.input-wrapper {
flex: 1;
position: relative;
background: #f5f5f5;
border-radius: 10rpx;
min-height: 72rpx;
display: flex;
align-items: center;
padding: 0 24rpx;
transition: all 0.2s;
box-sizing: border-box;
}
.input-wrapper:active {
background: #e8e8e8;
}
.chat-textarea {
flex: 1;
width: 100%;
font-size: 30rpx;
color: #333333;
line-height: 1.4;
min-height: 48rpx;
max-height: 160rpx;
padding: 12rpx 0;
margin: 0;
background: transparent;
border: none;
box-sizing: border-box;
overflow-y: auto;
}
.input-placeholder {
color: #999999;
font-size: 28rpx;
line-height: 1.4;
}
.input-actions {
position: absolute;
right: 16rpx;
top: 50%;
transform: translateY(-50%);
z-index: 2;
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: center;
}
.clear-btn {
width: 36rpx;
height: 36rpx;
border: none;
background: transparent;
padding: 0;
margin: 0;
line-height: 1;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
}
.clear-btn::after {
border: none;
}
.clear-btn:active {
background: rgba(0, 0, 0, 0.1);
}
.clear-icon {
width: 28rpx;
height: 28rpx;
}
.send-btn {
background: linear-gradient(135deg, #07c160 0%, #06ae56 100%);
width: 112rpx;
height: 66rpx;
border-radius: 10rpx;
border: none;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s ease;
padding: 0;
margin: 0;
line-height: 1;
flex-shrink: 0;
box-shadow: 0 4rpx 8rpx rgba(7, 193, 96, 0.2);
position: relative;
overflow: hidden;
}
.send-btn:active {
background: linear-gradient(135deg, #06ae56 0%, #059c4c 100%);
transform: scale(0.96);
box-shadow: 0 2rpx 4rpx rgba(7, 193, 96, 0.3);
}
.send-text {
font-size: 28rpx;
color: #ffffff;
font-weight: 600;
letter-spacing: 2rpx;
}
.send-placeholder {
width: 112rpx;
height: 72rpx;
flex-shrink: 0;
}
/* 多媒体选择面板 */
.media-action-sheet {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: flex-end;
justify-content: center;
z-index: 2000;
animation: fadeIn 0.3s ease;
}
.media-sheet-content {
width: 100%;
background: #F7F7F7;
border-radius: 40rpx 40rpx 0 0;
padding: 40rpx 30rpx calc(10rpx + env(safe-area-inset-bottom));
animation: slideUp 0.3s ease;
box-sizing: border-box;
}
@keyframes slideUp {
from { transform: translateY(100%); }
to { transform: translateY(0); }
}
.media-sheet-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 40rpx;
padding: 0 10rpx 20rpx;
border-bottom: 1px solid #E4E4E4;
}
.sheet-title {
font-size: 32rpx;
font-weight: 600;
color: #000000;
}
.close-sheet-btn image {
width: 60rpx;
height: 60rpx;
}
.media-options-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 40rpx 30rpx;
margin-bottom: 40rpx;
}
.media-option {
display: flex;
flex-direction: column;
align-items: center;
}
.option-icon-box {
width: 120rpx;
height: 120rpx;
border-radius: 30rpx;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 16rpx;
transition: transform 0.2s;
background-color: #fff;
}
.media-option:active .option-icon-box {
transform: scale(0.95);
}
.option-icon-box image {
width: 60rpx;
height: 60rpx;
}
.option-text {
font-size: 26rpx;
color: #666666;
}
.sheet-bottom {
text-align: center;
}
.bottom-tip {
font-size: 24rpx;
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;
}

127
pagesA/pages/serviceEvaluation/serviceEvaluation.js

@ -0,0 +1,127 @@
import http from '../../../utils/api'
const baseUrl = require('../../../utils/baseUrl')
Page({
data: {
baseUrl: baseUrl,
loadMoreText: '上拉加载更多',
evaluations: [],
pageNo: 1,
pageSize: 10,
total: 0,
isLoading: false,
hasMore: true
},
onLoad() {
this.getfeedback(true)
},
// 服务评价列表
getfeedback(isRefresh = false) {
// 防止重复请求
if (this.data.isLoading) return
// 如果没有更多数据且不是刷新,直接返回
if (!this.data.hasMore && !isRefresh) {
this.setData({
loadMoreText: '—— 没有更多了 ——'
})
return
}
this.setData({ isLoading: true })
// 如果是刷新,重置为第一页
if (isRefresh) {
this.setData({
pageNo: 1,
hasMore: true
})
}
// 显示加载提示
if (this.data.pageNo > 1) {
this.setData({ loadMoreText: '加载中...' })
}
http.feedback({
data: {
type: '服务评价',
pageNo: this.data.pageNo,
pageSize: this.data.pageSize
},
success: res => {
console.log('评价列表响应:', res)
if (res && res.rows) {
const list = res.rows || []
const total = res.total || 0
// 判断是否还有更多数据
const hasMore = this.data.pageNo * this.data.pageSize < total
this.setData({
evaluations: isRefresh ? list : this.data.evaluations.concat(list),
total: total,
hasMore: hasMore,
loadMoreText: hasMore ? '上拉加载更多' : '—— 没有更多了 ——',
pageNo: this.data.pageNo + 1
})
} else {
// 处理异常情况
if (isRefresh) {
this.setData({
evaluations: [],
hasMore: false,
loadMoreText: '—— 没有更多了 ——'
})
}
}
},
fail: err => {
console.error('获取评价列表失败:', err)
wx.showToast({
title: '加载失败',
icon: 'none'
})
// 加载失败时,恢复加载提示
this.setData({
loadMoreText: this.data.hasMore ? '点击重试' : '—— 没有更多了 ——'
})
},
complete: () => {
this.setData({ isLoading: false })
wx.hideNavigationBarLoading()
wx.stopPullDownRefresh()
}
})
},
// 下拉刷新
onPullDownRefresh() {
wx.showNavigationBarLoading()
this.getfeedback(true)
},
// 上拉加载更多
onReachBottom() {
if (this.data.hasMore && !this.data.isLoading) {
this.getfeedback()
} else if (!this.data.hasMore) {
this.setData({
loadMoreText: '—— 没有更多了 ——'
})
}
},
// 重新加载(可用于点击重试)
retryLoad() {
if (this.data.evaluations.length === 0) {
this.getfeedback(true)
} else if (this.data.hasMore) {
this.getfeedback()
}
}
})

4
pagesA/pages/serviceEvaluation/serviceEvaluation.json

@ -0,0 +1,4 @@
{
"navigationBarTitleText":"服务评价",
"usingComponents": {}
}

53
pagesA/pages/serviceEvaluation/serviceEvaluation.wxml

@ -0,0 +1,53 @@
<!-- 纯净评价列表页 - 无tab,只有列表 -->
<view class="page">
<!-- 自定义列表 -->
<view class="list-container">
<!-- 评价项列表 -->
<block wx:for="{{evaluations}}" wx:key="id" wx:for-index="idx">
<!-- 评价卡片 -->
<view class="evaluation-card" >
<!-- 用户信息行:头像 + 昵称 + 时间 -->
<view class="user-row">
<!-- 头像区域(支持图片和文字占位) -->
<view class="avatar-wrapper">
<image class="avatar" src="{{item.avatar?baseUrl+item.avatar:'/pages/images/tx.png'}}" mode="aspectFill"></image>
</view>
<!-- 用户信息和时间 -->
<view class="user-info">
<view class="name-row">
<text class="user-name">{{item.nickName?item.nickName:'用户'+idx}}</text>
<text class="time">{{item.createdAt}}</text>
</view>
</view>
</view>
<!-- 评价内容 -->
<view class="content-section">
<rich-text class="content-text" space="emsp" nodes="{{item.content}}"></rich-text>
</view>
<!-- 底部装饰线(极简分隔) -->
<view class="card-footer" wx:if="{{idx < evaluations.length - 1}}">
<view class="divider"></view>
</view>
</view>
</block>
<!-- 空状态 -->
<view class="empty-state" wx:if="{{evaluations.length === 0}}">
<image class="empty-icon" src="/pages/images/kzt.png" mode="aspectFit"></image>
<text class="empty-text">暂无评价</text>
</view>
<!-- 加载更多提示 -->
<view class="load-more" wx:if="{{evaluations.length > 0}}">
<text class="load-more-text">{{loadMoreText}}</text>
</view>
</view>
</view>

146
pagesA/pages/serviceEvaluation/serviceEvaluation.wxss

@ -0,0 +1,146 @@
.page {
min-height: 100vh;
background-color: #ffffff;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
}
/* 列表容器 - 直接贴边 */
.list-container {
padding: 0 16px;
}
/* 评价卡片 - 极简风格 */
.evaluation-card {
background-color: #ffffff;
padding: 20px 0;
position: relative;
transition: background-color 0.2s ease;
}
.evaluation-card:active {
background-color: #f8f9fc;
}
/* 用户信息行 */
.user-row {
display: flex;
align-items: center;
margin-bottom: 12px;
}
/* 头像区域 */
.avatar-wrapper {
margin-right: 12px;
}
.avatar {
width: 48px;
height: 48px;
border-radius: 50%;
background-color: #f0f2f6;
overflow: hidden;
flex-shrink: 0;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.03);
}
.avatar-placeholder {
display: flex;
align-items: center;
justify-content: center;
}
.avatar-text {
font-size: 20px;
font-weight: 500;
color: #ffffff;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
}
/* 用户信息区 */
.user-info {
flex: 1;
}
.name-row {
display: flex;
align-items: baseline;
justify-content: space-between;
margin-bottom: 4px;
}
.user-name {
font-size: 16px;
font-weight: 600;
color: #1e2a3a;
letter-spacing: -0.2px;
}
.time {
font-size: 12px;
color: #98a2b3;
font-weight: 400;
}
/* 评价内容 */
.content-section {
margin-bottom: 12px;
padding-right: 8px;
}
.content-text {
font-size: 15px;
line-height: 1.5;
color: #1e2a3a;
word-break: break-word;
font-weight: 400;
}
/* 卡片底部分隔线 */
.card-footer {
margin-top: 8px;
}
.divider {
height: 1px;
background-color: #eef1f4;
width: 100%;
}
/* 空状态 */
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 80px 20px;
background-color: #ffffff;
}
.empty-icon {
width: 140px;
height: 140px;
margin-bottom: 20px;
opacity: 0.6;
}
.empty-text {
font-size: 15px;
color: #9aa6b8;
font-weight: 400;
}
/* 加载更多 */
.load-more {
text-align: center;
padding: 24px 0 30px;
}
.load-more-text {
font-size: 12px;
color: #b8c0cc;
font-weight: 400;
}

20
pagesB/pages/publishAdd/publishAdd.js

@ -216,6 +216,8 @@ Page({
articleCoverTemp: tempPath, articleCoverTemp: tempPath,
'articleForm.coverImage': serverPath, 'articleForm.coverImage': serverPath,
isUploading: false isUploading: false
}, () => {
this.validateArticleForm();
}); });
} else { } else {
// 视频封面 // 视频封面
@ -277,9 +279,11 @@ Page({
maxDuration: 300, maxDuration: 300,
success: (res) => { success: (res) => {
if (res.tempFiles && res.tempFiles.length > 0) { if (res.tempFiles && res.tempFiles.length > 0) {
const tempFilePath = res.tempFiles[0].tempFilePath;
this.setData({ this.setData({
isUploading: true, isUploading: true,
videoUrlTemp: res.tempFiles[0].tempFilePath // 先显示本地路径
videoUrlTemp: tempFilePath // 先显示本地路径
}); });
// 显示加载提示 // 显示加载提示
@ -288,9 +292,18 @@ Page({
mask: true mask: true
}); });
console.log(111,tempFilePath);
// 上传视频 // 上传视频
this.uploadVideo(res.tempFiles[0].tempFilePath);
this.uploadVideo(tempFilePath);
} }
},
fail: (error) => {
console.error('选择视频失败:', error);
wx.showToast({
title: '选择视频失败',
icon: 'none'
});
} }
}); });
}, },
@ -307,8 +320,9 @@ Page({
success: (uploadRes) => { success: (uploadRes) => {
try { try {
const result = JSON.parse(uploadRes.data); const result = JSON.parse(uploadRes.data);
console.log(333,result)
if (result.code === 200 || result.fileName) { if (result.code === 200 || result.fileName) {
const serverPath = result.fileName || result.url;
const serverPath = result.fileName
this.setData({ this.setData({
'videoForm.videoUrl': serverPath, 'videoForm.videoUrl': serverPath,

2
project.private.config.json

@ -4,7 +4,7 @@
"setting": { "setting": {
"compileHotReLoad": true, "compileHotReLoad": true,
"skylineRenderEnable": true, "skylineRenderEnable": true,
"urlCheck": true,
"urlCheck": false,
"coverView": true, "coverView": true,
"lazyloadPlaceholderEnable": false, "lazyloadPlaceholderEnable": false,
"preloadBackgroundData": false, "preloadBackgroundData": false,

12
utils/api.js

@ -139,13 +139,15 @@ function shareAdd(params) {
http('/vet/article', 'post', params) http('/vet/article', 'post', params)
} }
// 制定方案新增 // 制定方案新增
function fazdAdd(params) { function fazdAdd(params) {
http('/vet/plan', 'post', params) http('/vet/plan', 'post', params)
} }
// 个人中心服务评价
function feedback(params) {
http('/muhu/feedback/list', 'get', params)
}
@ -162,12 +164,6 @@ function revise(params) {
http('/muhu/user', 'put', params) http('/muhu/user', 'put', params)
} }
// 个人中心反馈建议
function feedback(params) {
http('/muhu/feedback', 'post', params)
}
// 个人中心今日问诊 // 个人中心今日问诊
function today(params) { function today(params) {
http('/muhu/consultation/today', 'get', params) http('/muhu/consultation/today', 'get', params)

Loading…
Cancel
Save