Browse Source

专家列表,弹框详情,对话框

master
ZhaoYang 1 month ago
parent
commit
466f978ef7
  1. 4
      app.json
  2. 7
      pages/home/home.js
  3. 2
      pages/home/home.wxml
  4. BIN
      pagesA/images/1.png
  5. BIN
      pagesA/images/2.png
  6. BIN
      pagesA/images/3.png
  7. BIN
      pagesA/images/dzyx.png
  8. BIN
      pagesA/images/gzdw.png
  9. BIN
      pagesA/images/kzt.png
  10. BIN
      pagesA/images/phone.png
  11. BIN
      pagesA/images/ss.png
  12. 97
      pagesA/pages/askingSy/askingSy.js
  13. 45
      pagesA/pages/askingSy/askingSy.wxml
  14. 873
      pagesA/pages/askingSy/askingSy.wxss
  15. 216
      pagesA/pages/askingSyAdd/askingSyAdd.js
  16. 87
      pagesA/pages/askingSyAdd/askingSyAdd.wxml
  17. 11
      pagesA/pages/askingSyAdd/askingSyAdd.wxss
  18. 172
      pagesA/pages/askingSyDetails/askingSyDetails.js
  19. 49
      pagesA/pages/askingSyDetails/askingSyDetails.wxml
  20. 40
      pagesA/pages/askingSyDetails/askingSyDetails.wxss
  21. 431
      pagesA/pages/expert/expert.js
  22. 4
      pagesA/pages/expert/expert.json
  23. 248
      pagesA/pages/expert/expert.wxml
  24. 769
      pagesA/pages/expert/expert.wxss
  25. 753
      pagesA/pages/expertChat/expertChat.js
  26. 4
      pagesA/pages/expertChat/expertChat.json
  27. 296
      pagesA/pages/expertChat/expertChat.wxml
  28. 928
      pagesA/pages/expertChat/expertChat.wxss
  29. 23
      utils/api.js

4
app.json

@ -14,7 +14,9 @@
"pages/wzai/wzai", "pages/wzai/wzai",
"pages/askingSy/askingSy", "pages/askingSy/askingSy",
"pages/askingSyAdd/askingSyAdd", "pages/askingSyAdd/askingSyAdd",
"pages/askingSyDetails/askingSyDetails"
"pages/askingSyDetails/askingSyDetails",
"pages/expert/expert",
"pages/expertChat/expertChat"
] ]
}, },
{ {

7
pages/home/home.js

@ -55,6 +55,13 @@ Page({
}) })
}, },
// 找专家
bindZj(){
wx.navigateTo({
url: '/pagesA/pages/expert/expert',
})
},
//获取当前位置信息 //获取当前位置信息
getLocation() { getLocation() {

2
pages/home/home.wxml

@ -47,7 +47,7 @@
<view class="card2_1">平均5分钟恢复</view> <view class="card2_1">平均5分钟恢复</view>
</view> </view>
<view class="card3"> <view class="card3">
<view class="card3_1 card3_kp">
<view class="card3_1 card3_kp" bind:tap="bindZj">
<view>找专家</view> <view>找专家</view>
<view>智能匹配医生</view> <view>智能匹配医生</view>
</view> </view>

BIN
pagesA/images/1.png

After

Width: 556  |  Height: 674  |  Size: 368 KiB

BIN
pagesA/images/2.png

After

Width: 466  |  Height: 678  |  Size: 374 KiB

BIN
pagesA/images/3.png

After

Width: 459  |  Height: 682  |  Size: 285 KiB

BIN
pagesA/images/dzyx.png

After

Width: 200  |  Height: 200  |  Size: 2.4 KiB

BIN
pagesA/images/gzdw.png

After

Width: 200  |  Height: 200  |  Size: 2.4 KiB

BIN
pagesA/images/kzt.png

After

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

BIN
pagesA/images/phone.png

After

Width: 200  |  Height: 200  |  Size: 4.0 KiB

BIN
pagesA/images/ss.png

After

Width: 200  |  Height: 200  |  Size: 4.1 KiB

97
pagesA/pages/askingSy/askingSy.js

@ -1,64 +1,29 @@
import http from '../../../utils/api'
Page({ Page({
data: { data: {
diagnosisList: [
{
id: 1,
status: 'replied',
createdAt:'2025-02-12',
userInfo: {
avatar: '/pages/images/tx.png',
nickName: '张小明'
},
petInfo: {
type: '狗',
age: 3,
gender: '公'
},
description: '最近三天食欲不振,精神萎靡,偶尔呕吐黄色泡沫,大便偏软带血丝,体温正常但鼻头干燥。',
images: ['/images/pet1.jpg', '/images/pet2.jpg'],
replies: [{}, {}, {}]
},
{
id: 2,
status: 'pending',
createdAt: '2025-02-12',
userInfo: {
avatar: '/pages/images/tx.png',
nickName: '李小花'
},
petInfo: {
type: '猫',
age: 2,
gender: '母'
},
description: '今天发现眼睛有黄色分泌物,频繁抓挠眼睛周围,食欲正常但饮水量减少。',
images: ['/images/cat1.jpg'],
replies: []
},
{
id: 3,
status: 'replied',
createdAt: '2025-02-12',
userInfo: {
avatar: '/pages/images/tx.png',
nickName: '王先生'
},
petInfo: {
type: '狗',
age: 5,
gender: '公'
},
description: '狗狗最近走路跛脚,右前腿不敢着地,触摸时会叫。之前从沙发上跳下来可能受伤了。',
images: [],
replies: [{}, {}]
}
]
diagnosisList: []
}, },
onLoad: function() { onLoad: function() {
// 页面加载
this.getwzd()
}, },
// 问诊单
getwzd(){
http.wzd({
data:{},
success:res=>{
console.log(1111,res);
this.setData({
diagnosisList:res.rows
})
}
})
},
// 格式化日期显示 // 格式化日期显示
formatDate: function(dateString) { formatDate: function(dateString) {
const date = new Date(dateString); const date = new Date(dateString);
@ -85,7 +50,7 @@ Page({
} }
}, },
// 显示创建问诊单
// 新增问诊单
showCreateModal: function() { showCreateModal: function() {
wx.navigateTo({ wx.navigateTo({
url: '/pagesA/pages/askingSyAdd/askingSyAdd', url: '/pagesA/pages/askingSyAdd/askingSyAdd',
@ -94,11 +59,23 @@ Page({
// 查看详情 // 查看详情
viewDetail: function(e) { viewDetail: function(e) {
const index = e.currentTarget.dataset.index;
const item = this.data.diagnosisList[index];
wx.navigateTo({
url: `/pagesA/pages/askingSyDetails/askingSyDetails`,
});
console.log(1111,e);
const status = e.currentTarget.dataset.value.status
const data = e.currentTarget.dataset.value
console.log(data);
if(status=='已回复'){
wx.navigateTo({
url: `/pagesA/pages/askingSyDetails/askingSyDetails?data=${encodeURIComponent(JSON.stringify(data))}`,
});
}else{
wx.showToast({
title: `无回复内容`,
icon: 'none',
duration: 1000
});
}
}, },
}); });

45
pagesA/pages/askingSy/askingSy.wxml

@ -17,7 +17,7 @@
<scroll-view class="record-list" scroll-y enable-back-to-top> <scroll-view class="record-list" scroll-y enable-back-to-top>
<!-- 空状态 --> <!-- 空状态 -->
<view wx:if="{{diagnosisList.length === 0}}" class="empty-state"> <view wx:if="{{diagnosisList.length === 0}}" class="empty-state">
<image class="empty-icon" src="/images/empty-icon.png"></image>
<image class="empty-icon" src="/pagesA/images/kzt.png" mode="widthFix"></image>
<text class="empty-text">暂无问诊记录</text> <text class="empty-text">暂无问诊记录</text>
<text class="empty-tip">开始您的第一次问诊吧</text> <text class="empty-tip">开始您的第一次问诊吧</text>
</view> </view>
@ -29,26 +29,43 @@
<text class="section-count">{{diagnosisList.length}}条</text> <text class="section-count">{{diagnosisList.length}}条</text>
</view> </view>
<view wx:for="{{diagnosisList}}" wx:key="id" class="record-card" bindtap="viewDetail" data-index="{{index}}">
<view wx:for="{{diagnosisList}}" wx:key="id" class="record-card" bindtap="viewDetail" data-value="{{item}}">
<!-- 卡片头部:用户信息 + 状态 --> <!-- 卡片头部:用户信息 + 状态 -->
<view class="card-header"> <view class="card-header">
<view class="user-info">
<image class="user-avatar" src="{{item.userInfo.avatar || '/icons/default-avatar.svg'}}"></image>
<view class="user-details">
<text class="user-name">{{item.userInfo.nickName || '用户'}}</text>
<text class="pet-info">{{item.petInfo.type}},{{item.petInfo.age}}岁,{{item.petInfo.gender}}</text>
<view class="user-section">
<image class="user-avatar" src="{{item.userInfo.avatar || '/pages/images/tx.png'}}"></image>
<text class="user-name">{{item.farmerName || '用户'}}</text>
</view>
<view class="status-tag {{item.status === '已回复' ? 'status-replied' : 'status-pending'}}">
{{item.status}}
</view>
</view>
<!-- 牲畜信息 -->
<view class="livestock-section">
<view class="livestock-title-wrapper">
<view class="livestock-title-line"></view>
<view class="livestock-title-content">
<text class="livestock-title-text">牲畜信息</text>
</view> </view>
<view class="livestock-title-line"></view>
</view> </view>
<view class="status-tag {{item.status === 'replied' ? 'status-replied' : 'status-pending'}}">
{{item.status === 'replied' ? '已回复' : '待回复'}}
<view class="livestock-tags">
<view class="livestock-tag type-tag">
<text class="tag-text">{{item.animalType}}</text>
</view>
<view class="livestock-tag age-tag">
<text class="tag-text">{{item.animalAge}}</text>
</view>
<view class="livestock-tag gender-tag">
<text class="tag-text">{{item.animalGender}}</text>
</view>
</view> </view>
</view> </view>
<!-- 症状描述 --> <!-- 症状描述 -->
<view class="symptom-section"> <view class="symptom-section">
<view class="section-title-row">
<text class="section-title-text">症状描述</text>
</view>
<view class="symptom-content"> <view class="symptom-content">
<text class="symptom-text">{{item.description}}</text> <text class="symptom-text">{{item.description}}</text>
</view> </view>
@ -58,13 +75,13 @@
<view class="card-footer"> <view class="card-footer">
<view class="footer-left"> <view class="footer-left">
<view class="time-info"> <view class="time-info">
<text class="time-text">{{item.createdAt}}</text>
<text class="time-text">{{item.createdTime}}</text>
</view> </view>
</view> </view>
<view class="footer-right"> <view class="footer-right">
<view class="reply-info"> <view class="reply-info">
<text class="reply-count">{{item.replies.length}}条回复</text>
<text class="reply-count">{{item.replyCount}}条回复</text>
</view> </view>
</view> </view>
</view> </view>

873
pagesA/pages/askingSy/askingSy.wxss

@ -1,415 +1,484 @@
/* 全局样式 */ /* 全局样式 */
.wzbox{
min-height: 100vh;
position: relative;
background: linear-gradient(180deg, #F8FBFF 0%, #F0F7FF 100%);
}
/* 头部样式优化 */
.header {
padding: 20rpx 40rpx 20rpx;
background: linear-gradient(135deg, #6D9EFF 0%, #4A7CFF 100%);
border-radius: 0 0 36rpx 36rpx;
box-shadow: 0 4rpx 20rpx rgba(74, 144, 226, 0.15);
position: relative;
overflow: hidden;
}
.header-content {
position: relative;
z-index: 2;
}
.title-section {
display: flex;
flex-direction: column;
gap: 8rpx;
}
.title {
font-size: 44rpx;
font-weight: 700;
color: #FFFFFF;
letter-spacing: 0.5rpx;
text-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
}
.subtitle {
font-size: 28rpx;
color: rgba(255, 255, 255, 0.9);
font-weight: 400;
}
/* 头部装饰元素 */
.header-decoration {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
overflow: hidden;
z-index: 1;
}
.decoration-circle {
position: absolute;
border-radius: 50%;
background: rgba(255, 255, 255, 0.08);
}
.circle-1 {
width: 200rpx;
height: 200rpx;
top: -80rpx;
right: -40rpx;
}
.circle-2 {
width: 120rpx;
height: 120rpx;
bottom: -40rpx;
left: -20rpx;
}
/* 问诊记录列表 */
.record-list {
height: calc(100vh - 200rpx);
padding: 0 32rpx 20rpx;
box-sizing: border-box;
}
.records-container {
padding-top: 24rpx;
}
/* 列表头部 */
.section-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 24rpx;
padding: 0 4rpx;
}
.section-title {
font-size: 32rpx;
font-weight: 700;
color: #1A1A1A;
position: relative;
padding-left: 16rpx;
}
.section-title::before {
content: '';
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
width: 3rpx;
height: 20rpx;
background: linear-gradient(180deg, #6D9EFF 0%, #4A7CFF 100%);
border-radius: 2rpx;
}
.section-count {
font-size: 26rpx;
color: #666;
background: #F0F7FF;
padding: 6rpx 16rpx;
border-radius: 16rpx;
font-weight: 500;
}
/* 问诊记录卡片 */
.record-card {
background: #FFFFFF;
border-radius: 24rpx;
margin-bottom: 24rpx;
box-shadow: 0 6rpx 24rpx rgba(74, 144, 226, 0.08);
position: relative;
overflow: hidden;
transition: all 0.3s cubic-bezier(0.2, 0, 0.2, 1);
border: 1rpx solid #F0F7FF;
}
.record-card:active {
transform: translateY(-2rpx);
box-shadow: 0 10rpx 30rpx rgba(74, 144, 226, 0.15);
}
/* 卡片头部 */
.card-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
padding: 28rpx 28rpx 24rpx;
border-bottom: 1rpx solid #F5F9FF;
}
.user-info {
display: flex;
align-items: flex-start;
gap: 16rpx;
flex: 1;
}
.user-avatar {
width: 60rpx;
height: 60rpx;
border-radius: 50%;
border: 2rpx solid rgba(255, 255, 255, 0.8);
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.08);
}
.user-details {
display: flex;
flex-direction: column;
gap: 6rpx;
}
.user-name {
font-size: 28rpx;
color: #1A1A1A;
font-weight: 600;
line-height: 1.2;
}
.pet-info {
font-size: 24rpx;
color: #666;
font-weight: 400;
line-height: 1.2;
}
/* 状态标签 - 移至右上角 */
.status-tag {
padding: 8rpx 20rpx;
border-radius: 20rpx;
font-size: 22rpx;
font-weight: 600;
min-width: 80rpx;
text-align: center;
flex-shrink: 0;
margin-left: 16rpx;
}
.status-replied {
background: linear-gradient(135deg, rgba(76, 217, 100, 0.12) 0%, rgba(46, 204, 113, 0.12) 100%);
color: #2ECC71;
border: 1rpx solid rgba(46, 204, 113, 0.2);
}
.status-pending {
background: linear-gradient(135deg, rgba(255, 149, 0, 0.12) 0%, rgba(255, 127, 0, 0.12) 100%);
color: #FF9500;
border: 1rpx solid rgba(255, 149, 0, 0.2);
}
/* 症状描述 */
.symptom-section {
padding: 24rpx 28rpx;
border-bottom: 1rpx solid #F5F9FF;
}
.section-title-row {
display: flex;
align-items: center;
gap: 12rpx;
margin-bottom: 16rpx;
}
.wzbox{
min-height: 100vh;
position: relative;
background: linear-gradient(180deg, #F8FBFF 0%, #F0F7FF 100%);
}
.section-title-text {
font-size: 28rpx;
font-weight: 600;
color: #1A1A1A;
}
.symptom-content {
display: flex;
align-items: flex-start;
gap: 20rpx;
}
.symptom-text {
flex: 1;
font-size: 28rpx;
color: #333;
line-height: 1.6;
font-weight: 400;
}
/* 头部样式优化 */
.header {
padding: 20rpx 40rpx 20rpx;
background: linear-gradient(135deg, #6D9EFF 0%, #4A7CFF 100%);
border-radius: 0 0 36rpx 36rpx;
box-shadow: 0 4rpx 20rpx rgba(74, 144, 226, 0.15);
position: relative;
overflow: hidden;
}
.header-content {
position: relative;
z-index: 2;
}
/* 卡片底部 */
.card-footer {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20rpx 28rpx;
}
.time-info {
display: flex;
align-items: center;
gap: 8rpx;
}
.time-text {
font-size: 24rpx;
color: #999;
font-weight: 400;
}
.reply-info {
display: flex;
align-items: center;
gap: 8rpx;
padding: 8rpx 16rpx;
background: #F0F7FF;
border-radius: 16rpx;
border: 1rpx solid #E5EFFF;
}
.title-section {
display: flex;
flex-direction: column;
gap: 8rpx;
}
.reply-count {
font-size: 24rpx;
font-weight: 500;
color: #4A7CFF;
}
/* 空状态优化 */
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
padding-top: 120rpx;
text-align: center;
}
.empty-icon {
width: 240rpx;
height: 180rpx;
margin-bottom: 32rpx;
opacity: 0.5;
}
.empty-text {
font-size: 32rpx;
color: #666;
margin-bottom: 12rpx;
font-weight: 600;
}
.empty-tip {
font-size: 26rpx;
color: #999;
margin-bottom: 40rpx;
line-height: 1.4;
}
/* 列表底部 */
.list-footer {
text-align: center;
padding: 40rpx 0;
color: #999;
font-size: 24rpx;
}
.footer-text {
opacity: 0.6;
letter-spacing: 1rpx;
}
/* 新建问诊按钮优化 */
.create-btn-container {
position: fixed;
bottom: 40rpx;
right: 32rpx;
z-index: 100;
.title {
font-size: 44rpx;
font-weight: 700;
color: #FFFFFF;
letter-spacing: 0.5rpx;
text-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
}
.subtitle {
font-size: 28rpx;
color: rgba(255, 255, 255, 0.9);
font-weight: 400;
}
/* 头部装饰元素 */
.header-decoration {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
overflow: hidden;
z-index: 1;
}
.decoration-circle {
position: absolute;
border-radius: 50%;
background: rgba(255, 255, 255, 0.08);
}
.circle-1 {
width: 200rpx;
height: 200rpx;
top: -80rpx;
right: -40rpx;
}
.circle-2 {
width: 120rpx;
height: 120rpx;
bottom: -40rpx;
left: -20rpx;
}
/* 问诊记录列表 */
.record-list {
height: calc(100vh - 200rpx);
padding: 0 32rpx 20rpx;
box-sizing: border-box;
}
.records-container {
padding-top: 24rpx;
}
/* 列表头部 */
.section-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 24rpx;
padding: 0 4rpx;
}
.section-title {
font-size: 32rpx;
font-weight: 700;
color: #1A1A1A;
position: relative;
padding-left: 16rpx;
}
.section-title::before {
content: '';
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
width: 4rpx;
height: 20rpx;
background: linear-gradient(180deg, #6D9EFF 0%, #4A7CFF 100%);
border-radius: 2rpx;
}
.section-count {
font-size: 26rpx;
color: #666;
background: #F0F7FF;
padding: 6rpx 16rpx;
border-radius: 16rpx;
font-weight: 500;
}
/* 问诊记录卡片 */
.record-card {
background: #FFFFFF;
border-radius: 24rpx;
margin-bottom: 24rpx;
box-shadow: 0 6rpx 24rpx rgba(74, 144, 226, 0.08);
position: relative;
overflow: hidden;
transition: all 0.3s cubic-bezier(0.2, 0, 0.2, 1);
border: 1rpx solid #F0F7FF;
}
.record-card:active {
transform: translateY(-2rpx);
box-shadow: 0 10rpx 30rpx rgba(74, 144, 226, 0.15);
}
/* 卡片头部 */
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 24rpx 28rpx;
border-bottom: 1rpx solid #F5F9FF;
}
.user-section {
display: flex;
align-items: center;
gap: 16rpx;
flex: 1;
}
.user-avatar {
width: 60rpx;
height: 60rpx;
border-radius: 50%;
border: 2rpx solid rgba(255, 255, 255, 0.8);
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.08);
}
.user-name {
font-size: 28rpx;
color: #1A1A1A;
font-weight: 600;
}
/* 状态标签 */
.status-tag {
padding: 8rpx 20rpx;
border-radius: 20rpx;
font-size: 22rpx;
font-weight: 600;
min-width: 80rpx;
text-align: center;
flex-shrink: 0;
}
.status-replied {
background: linear-gradient(135deg, rgba(76, 217, 100, 0.12) 0%, rgba(46, 204, 113, 0.12) 100%);
color: #2ECC71;
border: 1rpx solid rgba(46, 204, 113, 0.2);
}
.status-pending {
background: linear-gradient(135deg, rgba(255, 149, 0, 0.12) 0%, rgba(255, 127, 0, 0.12) 100%);
color: #FF9500;
border: 1rpx solid rgba(255, 149, 0, 0.2);
}
/* 牲畜信息 */
.livestock-section {
padding: 20rpx 28rpx;
}
/* 牲畜信息标题优化 */
.livestock-title-wrapper {
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 20rpx;
position: relative;
}
.livestock-title-line {
flex: 1;
height: 2rpx;
background: linear-gradient(90deg, transparent, #4A7CFF 50%, transparent);
opacity: 0.3;
}
.livestock-title-content {
display: flex;
align-items: center;
gap: 12rpx;
padding: 0 20rpx;
position: relative;
}
.livestock-title-text {
font-size: 30rpx;
font-weight: 700;
color: #333;
background: linear-gradient(135deg, #4A7CFF 0%, #8CB4FF 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
text-shadow: 0 2rpx 4rpx rgba(74, 124, 255, 0.1);
letter-spacing: 1rpx;
}
@keyframes cowPulse {
0%, 100% {
transform: scale(1) rotate(0deg);
} }
.create-btn {
width: 140rpx;
height: 140rpx;
background: linear-gradient(135deg, #6D9EFF 0%, #4A7CFF 100%);
border-radius: 50%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
box-shadow: 0 8rpx 32rpx rgba(74, 144, 226, 0.3);
transition: all 0.3s cubic-bezier(0.2, 0, 0.2, 1);
position: relative;
overflow: hidden;
50% {
transform: scale(1.1) rotate(5deg);
} }
.create-btn::after {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(255, 255, 255, 0.1);
}
.livestock-tags {
display: flex;
gap: 16rpx;
flex-wrap: wrap;
}
.livestock-tag {
padding: 5rpx 20rpx;
border-radius: 20rpx;
font-size: 26rpx;
font-weight: 600;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
}
.type-tag {
background: linear-gradient(135deg, #6D9EFF 0%, #4A7CFF 100%);
color: #FFFFFF;
border: 2rpx solid rgba(255, 255, 255, 0.3);
}
.age-tag {
background: linear-gradient(135deg, #FFB347 0%, #FFA033 100%);
color: #FFFFFF;
border: 2rpx solid rgba(255, 255, 255, 0.3);
}
.gender-tag {
background: linear-gradient(135deg, #42E695 0%, #3BB2B8 100%);
color: #FFFFFF;
border: 2rpx solid rgba(255, 255, 255, 0.3);
}
.tag-text {
font-size: 26rpx;
font-weight: 600;
color: #FFFFFF;
}
/* 症状描述 */
.symptom-section {
padding: 24rpx 28rpx 28rpx;
}
.symptom-content {
background: #F9FAFF;
padding: 20rpx 24rpx;
border-radius: 16rpx;
border: 1rpx solid #E6ECFF;
position: relative;
}
.symptom-content::before {
content: '症状描述';
position: absolute;
top: -12rpx;
left: 24rpx;
background: #FFFFFF;
padding: 0 12rpx;
font-size: 24rpx;
font-weight: 600;
color: #4A7CFF;
z-index: 1;
}
.symptom-text {
font-size: 28rpx;
color: #333;
line-height: 1.6;
font-weight: 400;
}
/* 卡片底部 */
.card-footer {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20rpx 28rpx;
border-top: 1rpx solid #F5F9FF;
}
.time-info {
display: flex;
align-items: center;
gap: 8rpx;
}
.time-text {
font-size: 24rpx;
color: #999;
font-weight: 400;
}
.reply-info {
display: flex;
align-items: center;
gap: 8rpx;
padding: 8rpx 16rpx;
background: #F0F7FF;
border-radius: 16rpx;
border: 1rpx solid #E5EFFF;
}
.reply-count {
font-size: 24rpx;
font-weight: 500;
color: #4A7CFF;
}
/* 空状态优化 */
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
padding-top: 120rpx;
text-align: center;
}
.empty-icon {
width: 400rpx;
margin-bottom: 32rpx;
opacity: 0.5;
}
.empty-text {
font-size: 32rpx;
color: #666;
margin-bottom: 12rpx;
font-weight: 600;
}
.empty-tip {
font-size: 26rpx;
color: #999;
margin-bottom: 40rpx;
line-height: 1.4;
}
/* 列表底部 */
.list-footer {
text-align: center;
padding: 40rpx 0;
color: #999;
font-size: 24rpx;
}
.footer-text {
opacity: 0.6;
letter-spacing: 1rpx;
}
/* 新建问诊按钮优化 */
.create-btn-container {
position: fixed;
bottom: 40rpx;
right: 32rpx;
z-index: 100;
}
.create-btn {
width: 140rpx;
height: 140rpx;
background: linear-gradient(135deg, #6D9EFF 0%, #4A7CFF 100%);
border-radius: 50%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
box-shadow: 0 8rpx 32rpx rgba(74, 144, 226, 0.3);
transition: all 0.3s cubic-bezier(0.2, 0, 0.2, 1);
position: relative;
overflow: hidden;
}
.create-btn::after {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(255, 255, 255, 0.1);
opacity: 0;
transition: opacity 0.2s ease;
}
.create-btn:active {
transform: scale(0.95);
box-shadow: 0 4rpx 20rpx rgba(74, 144, 226, 0.4);
}
.create-btn:active::after {
opacity: 1;
}
.btn-icon {
width: 40rpx;
height: 40rpx;
margin-bottom: 10rpx;
}
.btn-text {
font-size: 26rpx;
color: white;
font-weight: 600;
letter-spacing: 0.5rpx;
}
/* 卡片入场动画 */
@keyframes cardSlideIn {
from {
opacity: 0; opacity: 0;
transition: opacity 0.2s ease;
}
.create-btn:active {
transform: scale(0.95);
box-shadow: 0 4rpx 20rpx rgba(74, 144, 226, 0.4);
transform: translateY(30rpx);
} }
.create-btn:active::after {
to {
opacity: 1; opacity: 1;
transform: translateY(0);
} }
.btn-icon {
width: 40rpx;
height: 40rpx;
margin-bottom: 10rpx;
}
.btn-text {
font-size: 26rpx;
color: white;
font-weight: 600;
letter-spacing: 0.5rpx;
}
/* 卡片入场动画 */
@keyframes cardSlideIn {
from {
opacity: 0;
transform: translateY(30rpx);
}
to {
opacity: 1;
transform: translateY(0);
}
}
}
.record-card {
animation: cardSlideIn 0.4s ease-out forwards;
opacity: 0;
}
.record-card:nth-child(1) { animation-delay: 0.1s; }
.record-card:nth-child(2) { animation-delay: 0.15s; }
.record-card:nth-child(3) { animation-delay: 0.2s; }
.record-card:nth-child(4) { animation-delay: 0.25s; }
.record-card:nth-child(5) { animation-delay: 0.3s; }
/* 响应式适配 */
@media screen and (min-width: 768px) {
.record-card { .record-card {
animation: cardSlideIn 0.4s ease-out forwards;
opacity: 0;
max-width: 600rpx;
margin-left: auto;
margin-right: auto;
} }
.record-card:nth-child(1) { animation-delay: 0.1s; }
.record-card:nth-child(2) { animation-delay: 0.15s; }
.record-card:nth-child(3) { animation-delay: 0.2s; }
.record-card:nth-child(4) { animation-delay: 0.25s; }
.record-card:nth-child(5) { animation-delay: 0.3s; }
/* 响应式适配 */
@media screen and (min-width: 768px) {
.record-card {
max-width: 600rpx;
margin-left: auto;
margin-right: auto;
}
}
}

216
pagesA/pages/askingSyAdd/askingSyAdd.js

@ -1,18 +1,22 @@
import http from '../../../utils/api'
const baseUrl = require('../../../utils/baseUrl')
Page({ Page({
/** /**
* 页面的初始数据 * 页面的初始数据
*/ */
data: { data: {
formData: { formData: {
petType: '',
petAge: '',
petGender: '',
symptoms: '',
images: []
animalType: '',
animalAge: '',
animalGender: '',
description: '',
images: [] // 这里应该存储服务器返回的文件名数组
}, },
symptomsLength: 0, symptomsLength: 0,
isSubmitting: false, isSubmitting: false,
isFormValid: false
isFormValid: false,
tempImages: [], // 新增:临时存储本地图片路径
isUploading: false // 新增:防止重复上传
}, },
/** /**
@ -23,12 +27,11 @@ Page({
this.checkFormValidity(); this.checkFormValidity();
}, },
// 宠物类型输入 // 宠物类型输入
onPetTypeInput(e) { onPetTypeInput(e) {
const value = e.detail.value; const value = e.detail.value;
this.setData({ this.setData({
'formData.petType': value
'formData.animalType': value
}, () => { }, () => {
this.checkFormValidity(); this.checkFormValidity();
}); });
@ -38,7 +41,7 @@ Page({
onPetAgeInput(e) { onPetAgeInput(e) {
const value = e.detail.value; const value = e.detail.value;
this.setData({ this.setData({
'formData.petAge': value
'formData.animalAge': value
}, () => { }, () => {
this.checkFormValidity(); this.checkFormValidity();
}); });
@ -48,7 +51,7 @@ Page({
selectGender(e) { selectGender(e) {
const value = e.currentTarget.dataset.value; const value = e.currentTarget.dataset.value;
this.setData({ this.setData({
'formData.petGender': value
'formData.animalGender': value
}, () => { }, () => {
this.checkFormValidity(); this.checkFormValidity();
}); });
@ -58,62 +61,159 @@ Page({
onSymptomsInput(e) { onSymptomsInput(e) {
const value = e.detail.value; const value = e.detail.value;
this.setData({ this.setData({
'formData.symptoms': value,
'formData.description': value,
symptomsLength: value.length symptomsLength: value.length
}, () => { }, () => {
this.checkFormValidity(); this.checkFormValidity();
}); });
}, },
// 选择图片
// 选择图片并上传
chooseImage() { chooseImage() {
if (this.data.formData.images.length >= 3) {
if (this.data.isUploading) {
wx.showToast({ wx.showToast({
title: '最多上传3张图片',
title: '正在上传中,请稍候',
icon: 'none' icon: 'none'
}); });
return; return;
} }
wx.chooseImage({
count: 3 - this.data.formData.images.length,
sizeType: ['compressed'],
wx.chooseMedia({
mediaType: ['image'],
sourceType: ['album', 'camera'], sourceType: ['album', 'camera'],
success: (res) => { success: (res) => {
const tempFilePaths = res.tempFilePaths;
const images = [...this.data.formData.images, ...tempFilePaths];
this.setData({
'formData.images': images
});
if (res.tempFiles && res.tempFiles.length > 0) {
this.setData({
isUploading: true
});
// 显示加载提示
wx.showLoading({
title: '上传图片中...',
mask: true
});
// 逐个上传图片
this.uploadImages(res.tempFiles);
}
} }
}); });
}, },
// 上传图片到服务器
uploadImages(files) {
console.log(1111,files);
const uploadPromises = files.map(file => {
return new Promise((resolve, reject) => {
wx.uploadFile({
url: baseUrl + '/common/upload',
header: {
'Authorization': 'Bearer ' + wx.getStorageSync('token')
},
filePath: file.tempFilePath,
name: 'file',
success: (uploadRes) => {
console.log(2222,uploadRes);
try {
const result = JSON.parse(uploadRes.data);
if (result.code === 200 || result.fileName) {
resolve({
tempPath: file.tempFilePath,
serverPath: result.fileName
});
} else {
reject(new Error('上传失败: ' + (result.msg || '服务器错误')));
}
} catch (error) {
reject(new Error('解析服务器响应失败'));
}
},
fail: (error) => {
reject(new Error('网络请求失败'));
}
});
});
});
// 等待所有图片上传完成
Promise.all(uploadPromises)
.then(results => {
// 更新临时图片列表(用于预览)
const newTempPaths = results.map(item => item.tempPath);
const newTempImages = [...this.data.tempImages, ...newTempPaths];
// 更新服务器返回的图片路径
const newServerPaths = results.map(item => item.serverPath);
const newFormImages = [...this.data.formData.images, ...newServerPaths];
console.log(555,this.data.formData.images);
console.log(666,newServerPaths);
this.setData({
tempImages: newTempImages,
'formData.images': newFormImages,
isUploading: false
});
wx.hideLoading();
wx.showToast({
title: '上传成功',
icon: 'success'
});
})
.catch(error => {
wx.hideLoading();
this.setData({
isUploading: false
});
wx.showToast({
title: error.message || '上传失败',
icon: 'none'
});
});
},
// 移除图片 // 移除图片
removeImage(e) { removeImage(e) {
const index = e.currentTarget.dataset.index; const index = e.currentTarget.dataset.index;
// 移除临时图片
const tempImages = [...this.data.tempImages];
tempImages.splice(index, 1);
// 移除服务器图片路径
const images = [...this.data.formData.images]; const images = [...this.data.formData.images];
images.splice(index, 1); images.splice(index, 1);
this.setData({ this.setData({
tempImages,
'formData.images': images 'formData.images': images
}); });
}, },
// 预览图片 // 预览图片
previewImage(e) { previewImage(e) {
const current = e.currentTarget.dataset.url;
const urls = e.currentTarget.dataset.urls;
const index = e.currentTarget.dataset.index;
const urls = this.data.tempImages;
wx.previewImage({
current,
urls
});
if (urls && urls.length > 0 && urls[index]) {
wx.previewImage({
current: urls[index],
urls: urls
});
}
}, },
// 检查表单有效性 // 检查表单有效性
checkFormValidity() { checkFormValidity() {
const { petType, petAge, petGender, symptoms } = this.data.formData;
const isValid = petType.trim() && petAge && petGender && symptoms.trim();
const {
animalType,
animalAge,
animalGender,
description
} = this.data.formData;
const isValid = animalType.trim() && animalAge && animalGender && description.trim();
this.setData({ this.setData({
isFormValid: !!isValid isFormValid: !!isValid
}); });
@ -125,24 +225,52 @@ Page({
return; return;
} }
// 检查是否还有图片正在上传
if (this.data.isUploading) {
wx.showToast({
title: '图片正在上传中,请稍后提交',
icon: 'none'
});
return;
}
this.setData({ this.setData({
isSubmitting: true isSubmitting: true
}); });
// 模拟提交
setTimeout(() => {
wx.showToast({
title: '提交成功',
icon: 'success',
duration: 1500,
success: () => {
setTimeout(() => {
wx.navigateTo({
url: '/pagesA/pages/askingSy/askingSy',
})
}, 1500);
// 准备提交数据
const submitData = {
...this.data.formData,
images: this.data.formData.images.join(',') // 将数组转为字符串,如果需要的话
};
http.wzdAdd({
data: submitData,
success: (res) => {
console.log('提交成功', res);
this.setData({
isSubmitting: false
});
if(res.code==200){
// 提交成功后的处理
wx.showToast({
title: '提交成功',
icon: 'success',
duration: 1500,
success: () => {
setTimeout(() => {
// 返回上一页
// wx.navigateBack();
}, 1500);
}
});
}else{
wx.showToast({
title: '提交失败,请重试',
icon: 'none'
});
} }
});
}, 1500);
},
});
} }
}); });

87
pagesA/pages/askingSyAdd/askingSyAdd.wxml

@ -3,8 +3,8 @@
<!-- 表单内容 --> <!-- 表单内容 -->
<scroll-view class="page-content" scroll-y> <scroll-view class="page-content" scroll-y>
<form bindsubmit="submitForm"> <form bindsubmit="submitForm">
<!-- 宠物类型 -->
<!-- 牲畜类型 -->
<view class="form-section"> <view class="form-section">
<view class="section-header"> <view class="section-header">
<view class="section-title"> <view class="section-title">
@ -12,25 +12,18 @@
</view> </view>
<text class="section-required">必填</text> <text class="section-required">必填</text>
</view> </view>
<input
class="form-input"
name="petType"
placeholder="请输入牲畜品种(如:山羊、奶牛)"
value="{{formData.petType}}"
bindinput="onPetTypeInput"
maxlength="20"
/>
<input class="form-input" name="petType" placeholder="请输入牲畜品种(如:山羊、奶牛)" value="{{formData.animalType}}" bindinput="onPetTypeInput" maxlength="20" />
</view> </view>
<!-- 宠物信息 -->
<!-- 牲畜信息 -->
<view class="form-section"> <view class="form-section">
<view class="section-header"> <view class="section-header">
<view class="section-title"> <view class="section-title">
<text>宠物信息</text>
<text>牲畜信息</text>
</view> </view>
<text class="section-required">必填</text> <text class="section-required">必填</text>
</view> </view>
<view class="info-grid"> <view class="info-grid">
<!-- 年龄 --> <!-- 年龄 -->
<view class="info-item"> <view class="info-item">
@ -38,15 +31,7 @@
<text>年龄</text> <text>年龄</text>
</view> </view>
<view class="item-input-wrapper"> <view class="item-input-wrapper">
<input
class="item-input"
name="petAge"
type="digit"
placeholder="0"
value="{{formData.petAge}}"
bindinput="onPetAgeInput"
/>
<text class="input-unit">岁</text>
<input class="item-input" name="petAge" type="digit" placeholder="例如:3岁或者3个月" value="{{formData.animalAge}}" bindinput="onPetAgeInput" />
</view> </view>
</view> </view>
@ -56,18 +41,10 @@
<text>性别</text> <text>性别</text>
</view> </view>
<view class="gender-buttons"> <view class="gender-buttons">
<view
class="gender-btn {{formData.petGender === '公' ? 'active' : ''}}"
data-value="公"
bindtap="selectGender"
>
<view class="gender-btn {{formData.animalGender === '公' ? 'active' : ''}}" data-value="公" bindtap="selectGender">
<text>公</text> <text>公</text>
</view> </view>
<view
class="gender-btn {{formData.petGender === '母' ? 'active' : ''}}"
data-value="母"
bindtap="selectGender"
>
<view class="gender-btn {{formData.animalGender === '母' ? 'active' : ''}}" data-value="母" bindtap="selectGender">
<text>母</text> <text>母</text>
</view> </view>
</view> </view>
@ -84,15 +61,7 @@
<text class="section-required">必填</text> <text class="section-required">必填</text>
</view> </view>
<view class="symptom-box"> <view class="symptom-box">
<textarea
class="symptom-textarea"
name="symptoms"
placeholder="请详细描述宠物的症状,包括持续时间、频率、具体表现等..."
value="{{formData.symptoms}}"
bindinput="onSymptomsInput"
maxlength="500"
auto-height
/>
<textarea class="symptom-textarea" name="symptoms" placeholder="请详细描述宠物的症状,包括持续时间、频率、具体表现等..." value="{{formData.description}}" bindinput="onSymptomsInput" maxlength="500" auto-height />
<view class="textarea-counter"> <view class="textarea-counter">
<text>{{symptomsLength}}/500</text> <text>{{symptomsLength}}/500</text>
</view> </view>
@ -103,40 +72,28 @@
<view class="form-section"> <view class="form-section">
<view class="section-header"> <view class="section-header">
<view class="section-title"> <view class="section-title">
<text>上传图片</text> <text>上传图片</text>
</view> </view>
<text class="section-optional">可选</text> <text class="section-optional">可选</text>
</view> </view>
<view class="upload-tips"> <view class="upload-tips">
<text>最多可上传3张图片,用于帮助医生更直观了解病情</text> <text>最多可上传3张图片,用于帮助医生更直观了解病情</text>
</view> </view>
<view class="image-upload-grid"> <view class="image-upload-grid">
<!-- 已上传图片 -->
<block wx:for="{{formData.images}}" wx:key="*this">
<!-- 已上传图片 - 使用tempImages预览 -->
<block wx:for="{{tempImages}}" wx:key="*this">
<view class="image-preview"> <view class="image-preview">
<image
class="preview-image"
src="{{item}}"
mode="aspectFill"
bindtap="previewImage"
data-url="{{item}}"
data-urls="{{formData.images}}"
></image>
<image class="preview-image" src="{{item}}" mode="aspectFill" bindtap="previewImage" data-index="{{index}}"></image>
<view class="remove-overlay" data-index="{{index}}" bindtap="removeImage"> <view class="remove-overlay" data-index="{{index}}" bindtap="removeImage">
<image class="remove-icon" src="/pagesA/images/ch.png"></image> <image class="remove-icon" src="/pagesA/images/ch.png"></image>
</view> </view>
</view> </view>
</block> </block>
<!-- 上传按钮 --> <!-- 上传按钮 -->
<view
class="upload-btn"
wx:if="{{formData.images.length < 3}}"
bindtap="chooseImage"
>
<view class="upload-btn" wx:if="{{tempImages.length < 3}}" bindtap="chooseImage">
<view class="upload-icon-wrapper"> <view class="upload-icon-wrapper">
<image class="upload-icon" src="/pagesA/images/jh.png"></image> <image class="upload-icon" src="/pagesA/images/jh.png"></image>
</view> </view>
@ -147,17 +104,11 @@
<!-- 提交按钮 --> <!-- 提交按钮 -->
<view class="submit-section"> <view class="submit-section">
<button
class="submit-btn"
type="primary"
formType="submit"
loading="{{isSubmitting}}"
disabled="{{isSubmitting || !isFormValid}}"
>
<button class="submit-btn" type="primary" formType="submit" loading="{{isSubmitting}}" disabled="{{isSubmitting || !isFormValid}}">
{{isSubmitting ? '提交中...' : '提交问诊单'}} {{isSubmitting ? '提交中...' : '提交问诊单'}}
</button> </button>
</view> </view>
</form> </form>
</scroll-view> </scroll-view>
</view> </view>

11
pagesA/pages/askingSyAdd/askingSyAdd.wxss

@ -123,15 +123,6 @@
text-align: center; text-align: center;
} }
.input-unit {
position: absolute;
right: 24rpx;
top: 50%;
transform: translateY(-50%);
font-size: 26rpx;
color: #999;
font-weight: 400;
}
/* 性别选择 */ /* 性别选择 */
.gender-buttons { .gender-buttons {
@ -305,9 +296,9 @@
} }
.submit-btn { .submit-btn {
height: 96rpx;
background: linear-gradient(135deg, #4A90E2 0%, #357AE8 100%); background: linear-gradient(135deg, #4A90E2 0%, #357AE8 100%);
border-radius: 24rpx; border-radius: 24rpx;
padding: 10rpx 0;
font-size: 32rpx; font-size: 32rpx;
font-weight: 700; font-weight: 700;
color: #FFFFFF; color: #FFFFFF;

172
pagesA/pages/askingSyDetails/askingSyDetails.js

@ -1,58 +1,13 @@
import http from '../../../utils/api'
const baseUrl = require('../../../utils/baseUrl')
Page({ Page({
/** /**
* 页面的初始数据 * 页面的初始数据
*/ */
data: { data: {
diagnosisData: {
id: 1,
status: 'replied',
createdAt: '2025-02-21',
userInfo: {
avatar: '/images/avatars/user1.jpg',
nickName: '张小明'
},
petInfo: {
type: '狗',
age: 3,
gender: '公'
},
description: '最近三天食欲不振,精神萎靡,偶尔呕吐黄色泡沫,大便偏软带血丝,体温正常但鼻头干燥。已经尝试喂食清淡食物但无改善。昨晚开始呼吸似乎有些急促。',
images: [
'/images/pet1.jpg',
'/images/pet2.jpg',
'/images/pet3.jpg'
],
replies: [
{
id: 1,
vet: {
avatar: '/images/vets/vet1.jpg',
name: '李医生',
title: '主治兽医',
hospital: '爱宠动物医院',
years: 8
},
content: '根据您的描述,狗狗可能出现了消化系统问题。呕吐黄色泡沫通常是胃液,结合大便带血丝的情况,建议立即禁食12小时观察。可以少量喂水,但不要喂食。如果症状持续或加重,建议立即带往医院进行血常规和粪便检查。',
createdAt: '2025-02-21',
likes: 12,
liked: false
},
{
id: 2,
vet: {
avatar: '/images/vets/vet2.jpg',
name: '王医生',
title: '资深兽医',
hospital: '萌宠医疗中心',
years: 12
},
content: '同意李医生的建议。此外,呼吸急促需要特别关注,可能是疼痛或炎症反应。建议测量肛温,正常范围是38-39℃。如果超过39.5℃需要紧急处理。可以检查牙龈颜色,正常应为粉色,发白或发紫需要立即就医。',
createdAt: '2025-02-21',
likes: 8,
liked: true
}
]
},
diagnosisData:{},
replies:[],
baseUrl:baseUrl,
refreshing: false refreshing: false
}, },
@ -60,61 +15,40 @@ Page({
* 生命周期函数--监听页面加载 * 生命周期函数--监听页面加载
*/ */
onLoad(options) { onLoad(options) {
if (options.id) {
// 根据id加载问诊数据
this.loadDiagnosisData(options.id);
if (options.data) {
// 根据id加载兽医回复数据
const data = JSON.parse(decodeURIComponent(options.data));
if(data.images){
const images = data.images.split(',')
this.setData({
images:images
})
}
this.setData({
diagnosisData:data,
})
this.loadDiagnosisData(data.formId);
} }
}, },
// 加载问诊数据 // 加载问诊数据
loadDiagnosisData(id) { loadDiagnosisData(id) {
// 这里应该是API请求,暂时使用模拟数据
// TODO: 替换为实际API调用
console.log('加载问诊数据:', id); console.log('加载问诊数据:', id);
// 确保时间数据正确显示
this.setData({
'diagnosisData.createdAt': new Date().toISOString(),
'diagnosisData.replies[0].createdAt': new Date(Date.now() - 2 * 60 * 60 * 1000).toISOString(),
'diagnosisData.replies[1].createdAt': new Date(Date.now() - 4 * 60 * 60 * 1000).toISOString()
});
http.wzdxq({
data:{
consultationId:id
},
success:res=>{
console.log(111111111,res);
this.setData({
replies:res.rows
})
}
})
}, },
// 格式化时间 - 确保正确显示
formatTime(dateString) {
if (!dateString) return '刚刚';
try {
const date = new Date(dateString);
const now = new Date();
const diff = now - date;
const diffSeconds = Math.floor(diff / 1000);
const diffMinutes = Math.floor(diff / (1000 * 60));
const diffHours = Math.floor(diff / (1000 * 60 * 60));
const diffDays = Math.floor(diff / (1000 * 60 * 60 * 24));
if (diffSeconds < 60) {
return '刚刚';
} else if (diffMinutes < 60) {
return `${diffMinutes}分钟前`;
} else if (diffHours < 24) {
return `${diffHours}小时前`;
} else if (diffDays === 1) {
return '昨天';
} else if (diffDays < 7) {
return `${diffDays}天前`;
} else {
const month = date.getMonth() + 1;
const day = date.getDate();
const hours = date.getHours().toString().padStart(2, '0');
const minutes = date.getMinutes().toString().padStart(2, '0');
return `${month}${day}${hours}:${minutes}`;
}
} catch (error) {
console.error('时间格式化错误:', error);
return '刚刚';
}
},
// 返回上一页 // 返回上一页
goBack() { goBack() {
@ -141,6 +75,7 @@ Page({
this.setData({ this.setData({
refreshing: true refreshing: true
}); });
this.loadDiagnosisData(this.data.diagnosisData.formId)
setTimeout(() => { setTimeout(() => {
this.setData({ this.setData({
@ -156,43 +91,14 @@ Page({
// 预览图片 // 预览图片
previewImage(e) { previewImage(e) {
const current = e.currentTarget.dataset.url; const current = e.currentTarget.dataset.url;
const urls = e.currentTarget.dataset.urls;
const urls = e.currentTarget.dataset.urls;
const urlsArray = urls.map(item=>{
return baseUrl+item
})
console.log(666,urlsArray);
wx.previewImage({ wx.previewImage({
current,
urls
current:current,
urls:urlsArray
}); });
}, },
// 点赞/取消点赞
toggleLike(e) {
const index = e.currentTarget.dataset.index;
const replies = [...this.data.diagnosisData.replies];
const reply = replies[index];
if (reply.liked) {
// 取消点赞
reply.liked = false;
reply.likes = Math.max(0, (reply.likes || 1) - 1);
wx.showToast({
title: '已取消',
icon: 'none'
});
} else {
// 点赞
reply.liked = true;
reply.likes = (reply.likes || 0) + 1;
wx.showToast({
title: '已点赞',
icon: 'success'
});
}
this.setData({
'diagnosisData.replies': replies
});
// 震动反馈
wx.vibrateShort();
}
}); });

49
pagesA/pages/askingSyDetails/askingSyDetails.wxml

@ -12,19 +12,19 @@
</view> </view>
<view class="user-details"> <view class="user-details">
<view class="user-name-row"> <view class="user-name-row">
<text class="user-name">{{diagnosisData.userInfo.nickName || '用户'}}</text>
<view class="status-badge {{diagnosisData.status === 'replied' ? 'status-replied' : 'status-pending'}} pulse">
{{diagnosisData.status === 'replied' ? '已回复' : '待回复'}}
<text class="user-name">{{diagnosisData.farmerName || '用户'}}</text>
<view class="status-badge {{diagnosisData.status === '已回复' ? 'status-replied' : 'status-pending'}} pulse">
{{diagnosisData.status}}
</view> </view>
</view> </view>
<view class="pet-info"> <view class="pet-info">
<view class="pet-info-tags"> <view class="pet-info-tags">
<view class="pet-tag gradient-blue">{{diagnosisData.petInfo.type}}</view>
<view class="pet-tag gradient-orange">{{diagnosisData.petInfo.age}}岁</view>
<view class="pet-tag gradient-pink">{{diagnosisData.petInfo.gender}}</view>
<view class="pet-tag gradient-blue">{{diagnosisData.animalType}}</view>
<view class="pet-tag gradient-orange">{{diagnosisData.animalAge}}</view>
<view class="pet-tag gradient-pink">{{diagnosisData.animalGender}}</view>
</view> </view>
<view class="time-info"> <view class="time-info">
<text class="time-text">{{diagnosisData.createdAt}}</text>
<text class="time-text">{{diagnosisData.createdTime}}</text>
</view> </view>
</view> </view>
</view> </view>
@ -45,26 +45,23 @@
</view> </view>
<!-- 相关图片 --> <!-- 相关图片 -->
<view class="image-section" wx:if="{{diagnosisData.images && diagnosisData.images.length > 0}}">
<view class="image-section" wx:if="{{images && images.length > 0}}">
<view class="section-header"> <view class="section-header">
<text class="section-title">相关图片</text> <text class="section-title">相关图片</text>
<text class="image-count gradient-blue">{{diagnosisData.images.length}}张</text>
<text class="image-count gradient-blue">{{images.length}}张</text>
</view> </view>
<scroll-view class="images-scroll" scroll-x> <scroll-view class="images-scroll" scroll-x>
<view class="images-container"> <view class="images-container">
<block wx:for="{{diagnosisData.images}}" wx:key="*this" wx:for-index="index">
<block wx:for="{{images}}" wx:key="*this" wx:for-index="index">
<view class="image-wrapper"> <view class="image-wrapper">
<image <image
class="symptom-image" class="symptom-image"
src="{{item}}"
src="{{baseUrl+item}}"
mode="aspectFill" mode="aspectFill"
bindtap="previewImage" bindtap="previewImage"
data-url="{{item}}"
data-urls="{{diagnosisData.images}}"
data-url="{{baseUrl+item}}"
data-urls="{{images}}"
></image> ></image>
<view class="image-overlay">
<text class="image-index">{{index + 1}}</text>
</view>
</view> </view>
</block> </block>
</view> </view>
@ -84,36 +81,34 @@
<view class="section-header"> <view class="section-header">
<text class="section-title">兽医回复</text> <text class="section-title">兽医回复</text>
<view class="reply-count gradient-purple"> <view class="reply-count gradient-purple">
<text class="count-number">{{diagnosisData.replies.length}}</text>
<text class="count-number">{{replies.length}}</text>
<text class="count-text">条回复</text> <text class="count-text">条回复</text>
</view> </view>
</view> </view>
<!-- 回复列表 --> <!-- 回复列表 -->
<view class="replies-list"> <view class="replies-list">
<block wx:for="{{diagnosisData.replies}}" wx:key="id" wx:for-index="index">
<block wx:for="{{replies}}" wx:key="id" wx:for-index="index">
<view class="vet-reply-card fade-in-up" style="animation-delay: {{index * 0.1}}s;"> <view class="vet-reply-card fade-in-up" style="animation-delay: {{index * 0.1}}s;">
<!-- 兽医信息 --> <!-- 兽医信息 -->
<view class="vet-info"> <view class="vet-info">
<view class="avatar-wrapper"> <view class="avatar-wrapper">
<image class="vet-avatar" src="/pages/images/tx.png"></image>
<image class="vet-avatar" src="{{item.avatar?baseUrl+item.avatar:'/pages/images/tx.png'}}"></image>
<view class="vet-verified"></view> <view class="vet-verified"></view>
</view> </view>
<view class="vet-details"> <view class="vet-details">
<view class="vet-name-row"> <view class="vet-name-row">
<text class="vet-name">{{item.vet.name}}</text>
<text class="vet-name">{{item.replyName}}</text>
<view class="vet-badge gradient-gold"> <view class="vet-badge gradient-gold">
<text class="vet-title">{{item.vet.title}}</text>
<text class="vet-title">{{item.title}}</text>
</view> </view>
</view> </view>
<view class="vet-meta"> <view class="vet-meta">
<view class="meta-item"> <view class="meta-item">
<image class="meta-icon" src="/icons/hospital.svg"></image>
<text class="vet-hospital">{{item.vet.hospital}}</text>
<text class="vet-hospital">{{item.hospital}}</text>
</view> </view>
<view class="meta-item"> <view class="meta-item">
<image class="meta-icon" src="/icons/experience.svg"></image>
<text class="vet-experience">{{item.vet.years}}年经验</text>
<text class="vet-experience">{{item.experience}}</text>
</view> </view>
</view> </view>
</view> </view>
@ -133,10 +128,6 @@
</view> </view>
</view> </view>
<!-- 回复装饰 -->
<view class="reply-decoration">
<image class="decoration-icon" src="/icons/star.svg"></image>
</view>
</view> </view>
</block> </block>

40
pagesA/pages/askingSyDetails/askingSyDetails.wxss

@ -364,25 +364,6 @@
border-radius: 20rpx; border-radius: 20rpx;
} }
.image-overlay {
position: absolute;
top: 12rpx;
left: 12rpx;
background: rgba(0, 0, 0, 0.6);
width: 40rpx;
height: 40rpx;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
backdrop-filter: blur(4rpx);
}
.image-index {
color: white;
font-size: 20rpx;
font-weight: 600;
}
/* 兽医回复区域 */ /* 兽医回复区域 */
.replies-section { .replies-section {
@ -428,19 +409,6 @@
backdrop-filter: blur(20rpx); backdrop-filter: blur(20rpx);
} }
/* 回复装饰 */
.reply-decoration {
position: absolute;
top: 20rpx;
right: 20rpx;
opacity: 0.1;
}
.decoration-icon {
width: 40rpx;
height: 40rpx;
animation: float 3s ease-in-out infinite;
}
/* 兽医信息 */ /* 兽医信息 */
.vet-info { .vet-info {
@ -489,7 +457,7 @@
} }
.vet-badge { .vet-badge {
padding: 6rpx 20rpx;
padding: 2rpx 10rpx;
border-radius: 20rpx; border-radius: 20rpx;
box-shadow: 0 4rpx 12rpx rgba(255, 215, 0, 0.3); box-shadow: 0 4rpx 12rpx rgba(255, 215, 0, 0.3);
} }
@ -515,11 +483,7 @@
border-radius: 16rpx; border-radius: 16rpx;
} }
.meta-icon {
width: 20rpx;
height: 20rpx;
opacity: 0.7;
}
.vet-hospital, .vet-hospital,
.vet-experience { .vet-experience {

431
pagesA/pages/expert/expert.js

@ -0,0 +1,431 @@
import http from '../../../utils/api'
Page({
data: {
// 搜索关键词
searchKeyword: '',
// 当前筛选条件
currentFilter: 'all',
// 所有专家数据
allExperts: [
{
id: 1,
name: '张建国',
title: '畜牧学博士',
expertise: '牛类养殖与疾病防治',
experience: 15,
online: true,
senior: true,
phone: '138-0013-8001',
email: 'zhangjg@agri-expert.com',
institution: '国家畜牧业研究所',
address: '北京市朝阳区农业科技大厦',
bio: '15年牛类养殖研究经验,发表SCI论文20余篇,擅长规模化养殖场管理与疾病防控。',
tags: ['肉牛养殖', '疾病预防', '饲料配方', '规模化养殖'],
successRate: 98,
responseTime: '15分钟内响应',
avatar: '/pagesA/images/1.png'
},
{
id: 2,
name: '李秀英',
title: '兽医学硕士',
expertise: '猪病诊断与治疗',
experience: 12,
online: true,
senior: true,
phone: '139-0013-9002',
email: 'lixy@vet-hospital.cn',
institution: '农业大学动物医学院',
address: '上海市徐汇区农业路58号',
bio: '资深猪病防治专家,参与多项国家级科研项目,成功解决多个大规模猪场疫情。',
tags: ['猪病诊疗', '疫苗管理', '养殖场规划', '传染病防治'],
successRate: 96,
responseTime: '20分钟内响应',
avatar: '/pagesA/images/2.png'
},
{
id: 3,
name: '王伟民',
title: '高级畜牧师',
expertise: '羊类繁殖与饲养管理',
experience: 20,
online: false,
senior: true,
phone: '137-0013-7003',
email: 'wangwm@livestock.cn',
institution: '草原畜牧业研究中心',
address: '内蒙古呼和浩特市牧业科技园',
bio: '羊类养殖领域权威专家,拥有20年草原牧区养殖经验,精通各类羊只繁殖技术。',
tags: ['绵羊养殖', '山羊繁殖', '牧草种植', '草原管理'],
successRate: 95,
responseTime: '1小时内响应',
avatar: '/pagesA/images/3.png'
},
{
id: 4,
name: '陈秀兰',
title: '家禽养殖专家',
expertise: '鸡鸭养殖技术',
experience: 18,
online: true,
senior: false,
phone: '136-0013-6004',
email: 'chenxl@poultry-expert.com',
institution: '家禽养殖技术中心',
address: '广东省广州市禽业科技园',
bio: '家禽养殖技术推广专家,帮助300+养殖户实现科学化、标准化养殖管理。',
tags: ['蛋鸡养殖', '肉鸭饲养', '疾病防控', '禽舍设计'],
successRate: 94,
responseTime: '30分钟内响应',
avatar: '/pagesA/images/1.png'
},
{
id: 5,
name: '刘志强',
title: '特种养殖专家',
expertise: '鹿、兔等特种动物养殖',
experience: 10,
online: false,
senior: false,
phone: '135-0013-5005',
email: 'liuzq@special-livestock.cn',
institution: '特种动物养殖研究所',
address: '四川省成都市农业科技园',
bio: '特种动物养殖新兴力量,专注于梅花鹿、肉兔等特种动物的现代化养殖技术。',
tags: ['梅花鹿养殖', '肉兔饲养', '市场分析', '特色养殖'],
successRate: 92,
responseTime: '2小时内响应',
avatar: '/pagesA/images/2.png'
},
{
id: 6,
name: '赵雪梅',
title: '畜牧营养学博士',
expertise: '牲畜饲料配方与营养',
experience: 14,
online: true,
senior: true,
phone: '134-0013-4006',
email: 'zhaoxm@feed-research.cn',
institution: '农业科学院饲料研究所',
address: '江苏省南京市科研路88号',
bio: '饲料营养学专家,研发多个高效饲料配方,帮助养殖户降低30%饲料成本。',
tags: ['饲料配方', '营养管理', '成本控制', '饲料安全'],
successRate: 97,
responseTime: '25分钟内响应',
avatar: '/pagesA/images/3.png'
},
{
id: 7,
name: '孙建国',
title: '养殖场管理专家',
expertise: '现代化养殖场设计与运营',
experience: 22,
online: true,
senior: true,
phone: '133-0013-3007',
email: 'sunjg@farm-management.com',
institution: '现代农业发展中心',
address: '浙江省杭州市创新产业园',
bio: '养殖场规划设计专家,参与设计200+现代化养殖场,精通自动化养殖设备应用。',
tags: ['场舍设计', '设备管理', '成本核算', '环保处理'],
successRate: 99,
responseTime: '10分钟内响应',
avatar: '/pagesA/images/2.png'
},
{
id: 8,
name: '周小芳',
title: '兽医病理学硕士',
expertise: '牲畜常见病与传染病防治',
experience: 11,
online: false,
senior: false,
phone: '132-0013-2008',
email: 'zhouxf@vet-pathology.cn',
institution: '动物疫病防控中心',
address: '湖北省武汉市动物防疫站',
bio: '专攻牲畜病理诊断与传染病防控,在快速诊断和精准治疗方面有丰富经验。',
tags: ['传染病防治', '病理诊断', '用药指导', '疫情处理'],
successRate: 93,
responseTime: '1.5小时内响应',
avatar: '/pagesA/images/3.png'
}
],
// 筛选后的专家列表
filteredExperts: [],
// 在线专家数量
onlineCount: 0,
// 当前选中的专家
currentExpert: null,
// 是否显示联系方式弹窗
showContactDialog: false
},
onLoad() {
this.sortAndFilterExperts()
this.getexpertsList()
},
onShow() {
// 页面显示时随机更新一些专家的在线状态(模拟实时状态)
this.updateOnlineStatus();
},
// 专家列表
getexpertsList(){
http.expertsList({
data:{},
success:res=>{
console.log(111,res);
}
})
},
// 更新在线状态
updateOnlineStatus() {
const experts = this.data.allExperts.map(expert => ({
...expert,
online: Math.random() > 0.4 // 60%的概率在线
}));
this.setData({
allExperts: experts
}, () => {
this.sortAndFilterExperts();
});
},
// 处理搜索输入
onSearchInput(e) {
this.setData({
searchKeyword: e.detail.value
});
this.sortAndFilterExperts();
},
// 清空搜索
clearSearch() {
this.setData({
searchKeyword: '',
currentFilter: 'all'
});
this.sortAndFilterExperts();
},
// 更改筛选条件
changeFilter(e) {
const filter = e.currentTarget.dataset.filter;
this.setData({
currentFilter: filter
});
this.sortAndFilterExperts();
},
// 排序和筛选专家
sortAndFilterExperts() {
const { allExperts, searchKeyword, currentFilter } = this.data;
let filtered = [...allExperts];
// 关键词搜索
if (searchKeyword.trim()) {
const keyword = searchKeyword.toLowerCase();
filtered = filtered.filter(expert =>
expert.name.toLowerCase().includes(keyword) ||
expert.expertise.toLowerCase().includes(keyword) ||
expert.tags.some(tag => tag.toLowerCase().includes(keyword)) ||
expert.institution.toLowerCase().includes(keyword)
);
}
// 筛选条件
switch (currentFilter) {
case 'online':
filtered = filtered.filter(expert => expert.online);
break;
case 'offline':
filtered = filtered.filter(expert => !expert.online);
break;
case 'senior':
filtered = filtered.filter(expert => expert.senior);
break;
case 'veterinary':
filtered = filtered.filter(expert =>
expert.expertise.includes('病') ||
expert.tags.some(tag => tag.includes('病') || tag.includes('医'))
);
break;
}
// 排序:在线优先 > 资深专家 > 经验丰富 > 解决率高
filtered.sort((a, b) => {
// 在线状态优先
if (a.online !== b.online) {
return a.online ? -1 : 1;
}
// 资深专家优先
if (a.senior !== b.senior) {
return a.senior ? -1 : 1;
}
// 经验丰富优先
if (a.experience !== b.experience) {
return b.experience - a.experience;
}
// 解决率高优先
return (b.successRate || 0) - (a.successRate || 0);
});
// 计算在线人数
const onlineCount = filtered.filter(expert => expert.online).length;
this.setData({
filteredExperts: filtered,
onlineCount: onlineCount
});
},
// 显示专家联系方式
showContactInfo(e) {
const index = e.currentTarget.dataset.index;
const expert = this.data.filteredExperts[index];
this.setData({
currentExpert: expert,
showContactDialog: true
});
},
// 隐藏联系方式弹窗
hideContactDialog() {
this.setData({
showContactDialog: false
});
},
// 阻止事件冒泡
stopPropagation() {
// 阻止事件冒泡
},
// 拨打电话
makePhoneCall(e) {
const phone = e.currentTarget.dataset.phone;
const cleanPhone = phone.replace(/-/g, '');
wx.showModal({
title: '拨打电话',
content: `确定要拨打 ${phone} 吗?`,
success: (res) => {
if (res.confirm) {
wx.makePhoneCall({
phoneNumber: cleanPhone,
success: () => {
wx.showToast({
title: '拨号成功',
icon: 'success'
});
},
fail: (err) => {
console.error('拨打电话失败:', err);
wx.showToast({
title: '拨号失败',
icon: 'none'
});
}
});
}
}
});
},
// 复制邮箱
copyEmail(e) {
const email = e.currentTarget.dataset.email;
wx.setClipboardData({
data: email,
success: () => {
wx.showToast({
title: '邮箱已复制',
icon: 'success'
});
},
fail: () => {
wx.showToast({
title: '复制失败',
icon: 'none'
});
}
});
},
// 查看位置
viewLocation(e) {
const address = e.currentTarget.dataset.address
wx.showModal({
title: '单位地址',
content: address,
showCancel: false,
confirmText: '知道了'
});
},
// 开始咨询
startConsultation() {
const expert = this.data.currentExpert;
wx.showModal({
title: '咨询确认',
content: expert.online
? `确定要立即咨询 ${expert.name} 专家吗?`
: `确定要预约咨询 ${expert.name} 专家吗?`,
success: (res) => {
if (res.confirm) {
if (expert.online) {
wx.showToast({
title: '正在为您连接专家...',
icon: 'loading',
duration: 2000
});
setTimeout(() => {
// 跳转一对一咨询专家
wx.navigateTo({
url: '/pagesA/pages/expertChat/expertChat',
})
this.hideContactDialog();
}, 2000);
} else {
wx.showToast({
title: '预约成功,专家将尽快回复',
icon: 'success'
});
this.hideContactDialog();
}
}
}
});
},
// 分享专家
onShareAppMessage() {
return {
title: '牲畜专家咨询平台',
path: '/pages/expert/expert',
imageUrl: '/images/share-cover.jpg'
};
}
});

4
pagesA/pages/expert/expert.json

@ -0,0 +1,4 @@
{
"navigationBarTitleText":"专家列表",
"usingComponents": {}
}

248
pagesA/pages/expert/expert.wxml

@ -0,0 +1,248 @@
<view class="expert-page">
<!-- 顶部区域 -->
<view class="header-section">
<!-- 背景装饰 -->
<view class="header-bg">
<view class="bg-circle bg-circle-1"></view>
<view class="bg-circle bg-circle-2"></view>
<view class="bg-circle bg-circle-3"></view>
</view>
<!-- 标题内容 -->
<view class="header-content">
<view class="title-row">
<text class="main-title">牲畜专家</text>
<view class="expert-badge">
<text class="badge-text">专业咨询</text>
</view>
</view>
<text class="sub-title">连接行业专家 · 解决养殖难题</text>
<!-- 统计信息 -->
<view class="stats-row">
<view class="stat-item">
<text class="stat-number">{{allExperts.length}}</text>
<text class="stat-label">总专家数</text>
</view>
<view class="stat-divider"></view>
<view class="stat-item">
<text class="stat-number">{{onlineCount}}</text>
<text class="stat-label">在线专家</text>
</view>
<view class="stat-divider"></view>
<view class="stat-item">
<text class="stat-number">24h</text>
<text class="stat-label">快速响应</text>
</view>
</view>
</view>
</view>
<!-- 搜索和筛选区域 -->
<view class="search-filter-section">
<!-- 搜索框 -->
<view class="search-wrapper">
<view class="search-box">
<image src="/pagesA/images/ss.png" class="search-icon"></image>
<input type="text" placeholder="搜索专家姓名、擅长领域..." placeholder-class="placeholder" bindinput="onSearchInput" value="{{searchKeyword}}" />
<view wx:if="{{searchKeyword}}" class="clear-btn" bindtap="clearSearch">
<image src="/pagesA/images/ch.png" class="clear-icon"></image>
</view>
</view>
</view>
<!-- 筛选标签 -->
<scroll-view class="filter-scroll" scroll-x>
<view class="filter-tags">
<view class="filter-tag {{currentFilter === 'all' ? 'active' : ''}}" bindtap="changeFilter" data-filter="all">
<text>全部专家</text>
<view class="tag-count">{{allExperts.length}}</view>
</view>
<view class="filter-tag {{currentFilter === 'online' ? 'active' : ''}}" bindtap="changeFilter" data-filter="online">
<text>在线专家</text>
<view class="tag-count online-count">{{onlineCount}}</view>
</view>
<view class="filter-tag {{currentFilter === 'offline' ? 'active' : ''}}" bindtap="changeFilter" data-filter="offline">
<text>离线专家</text>
<view class="tag-count">{{allExperts.length - onlineCount}}</view>
</view>
<view class="filter-tag {{currentFilter === 'senior' ? 'active' : ''}}" bindtap="changeFilter" data-filter="senior">
<text>资深专家</text>
</view>
<view class="filter-tag {{currentFilter === 'veterinary' ? 'active' : ''}}" bindtap="changeFilter" data-filter="veterinary">
<text>兽医专家</text>
</view>
</view>
</scroll-view>
</view>
<!-- 专家列表 -->
<view class="expert-list-section">
<!-- 列表标题 -->
<view class="list-header">
<text class="list-title">专家列表</text>
<text class="list-count">已为您找到 {{filteredExperts.length}} 位专家</text>
</view>
<!-- 专家卡片列表 -->
<scroll-view class="expert-cards" scroll-y>
<!-- 空状态 -->
<view wx:if="{{filteredExperts.length === 0}}" class="empty-state">
<image src="/pagesA/images/kzt.png" class="empty-image"></image>
<text class="empty-title">暂无相关专家</text>
<text class="empty-desc">换个关键词试试,或联系客服为您推荐</text>
</view>
<!-- 专家卡片 -->
<view wx:else>
<block wx:for="{{filteredExperts}}" wx:key="id">
<view class="expert-card">
<!-- 专家头像和在线状态 -->
<view class="card-left">
<view class="avatar-container">
<image src="{{item.avatar}}" class="expert-avatar" mode="aspectFill"></image>
<view class="online-badge {{item.online ? 'online' : 'offline'}}">
{{item.online ? '在线' : '离线'}}
</view>
</view>
</view>
<!-- 专家信息 -->
<view class="card-middle">
<view class="name-title-row">
<text class="expert-name">{{item.name}}</text>
<text class="expert-title">{{item.title}}</text>
</view>
<!-- 专门的信息展示区域 -->
<view class="info-display">
<view class="info-item">
<text class="info-label">擅长领域:</text>
<text class="info-value">{{item.expertise}}</text>
</view>
<view class="info-item">
<text class="info-label">从业经验:</text>
<text class="info-value">{{item.experience}}年</text>
</view>
</view>
</view>
<!-- 点击按钮 -->
<view class="contact-btn {{item.online ? 'online-btn' : 'offline-btn'}}" bindtap="showContactInfo" data-index="{{index}}">
{{item.online ? '立即咨询' : '查看联系'}}
</view>
</view>
</block>
</view>
</scroll-view>
</view>
<!-- 联系方式弹窗 -->
<view wx:if="{{showContactDialog}}" class="dialog-overlay" bindtap="hideContactDialog">
<view class="contact-modal" catchtap="stopPropagation">
<!-- 可滚动的内容区域 -->
<scroll-view class="modal-content" scroll-y>
<!-- 模态框头部 -->
<view class="modal-header">
<view class="header-left">
<image src="/pages/images/tx.png" class="modal-avatar"></image>
<view class="expert-intro">
<view class="modallei">
<view class="modal-name">{{currentExpert.name}}</view>
<view class="modal-title">{{currentExpert.title}}</view>
</view>
<view class="title-status">
<view class="modal-status {{currentExpert.online ? 'online' : 'offline'}}">
<view class="status-dot"></view>
{{currentExpert.online ? '在线可咨询' : '暂时离线'}}
</view>
</view>
</view>
</view>
</view>
<!-- 专家简介 -->
<view class="expert-bio">
<view class="bio-header">
<text class="bio-title">专家简介</text>
</view>
<text class="bio-content">{{currentExpert.bio || '资深牲畜养殖专家,拥有丰富的实践经验和理论知识,擅长解决各类养殖难题'}}</text>
</view>
<!-- 联系方式 -->
<view class="contact-section">
<!-- 电话 -->
<view class="contact-item phone-item">
<view class="contact-icon-container">
<image src="/pagesA/images/phone.png" class="contact-item-icon"></image>
</view>
<view class="contact-info">
<text class="contact-label">联系电话</text>
<text class="contact-value">{{currentExpert.phone}}</text>
<text class="contact-desc">可直接拨打电话咨询</text>
</view>
<button class="action-btn call-btn" bindtap="makePhoneCall" data-phone="{{currentExpert.phone}}">
拨打
</button>
</view>
<!-- 邮箱 -->
<view class="contact-item email-item">
<view class="contact-icon-container">
<image src="/pagesA/images/dzyx.png" class="contact-item-icon"></image>
</view>
<view class="contact-info">
<text class="contact-label">电子邮箱</text>
<text class="contact-value">{{currentExpert.email}}</text>
<text class="contact-desc">发送邮件可获得详细回复</text>
</view>
<button class="action-btn copy-btn" bindtap="copyEmail" data-email="{{currentExpert.email}}">
复制
</button>
</view>
<!-- 工作单位 -->
<view class="contact-item institution-item">
<view class="contact-icon-container">
<image src="/pagesA/images/gzdw.png" class="contact-item-icon"></image>
</view>
<view class="contact-info">
<text class="contact-label">工作单位</text>
<text class="contact-value">{{currentExpert.institution}}</text>
<text class="contact-desc">专业机构认证专家</text>
</view>
<button class="action-btn address-btn" bindtap="viewLocation" data-address="{{currentExpert.address}}">
位置
</button>
</view>
</view>
<!-- 咨询时间 -->
<view class="consultation-time">
<view class="time-header">
<text class="time-title">咨询时间建议</text>
</view>
<text class="time-content">{{currentExpert.online ? '专家当前在线,可直接联系咨询' : '专家当前不在线,建议在工作时间联系或发送邮件咨询'}}</text>
<view class="time-slots">
<text class="time-slot">工作日 9:00-18:00</text>
<text class="time-slot">周末 10:00-16:00</text>
</view>
</view>
</scroll-view>
<!-- 操作按钮(固定在底部) -->
<view class="modal-actions">
<button class="secondary-btn" bindtap="hideContactDialog">稍后联系</button>
<button class="primary-btn" bindtap="startConsultation">
{{currentExpert.online ? '立即咨询' : '预约咨询'}}
</button>
</view>
</view>
</view>
</view>

769
pagesA/pages/expert/expert.wxss

@ -0,0 +1,769 @@
.expert-page {
min-height: 100vh;
background: linear-gradient(180deg, #f5f7fa 0%, #ffffff 100%);
}
/* 顶部区域样式 */
.header-section {
background: linear-gradient(135deg, #2c8c34 0%, #4caf50 100%);
padding: 40rpx 40rpx 50rpx;
position: relative;
overflow: hidden;
}
.header-bg {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
.bg-circle {
position: absolute;
border-radius: 50%;
background: rgba(255, 255, 255, 0.1);
}
.bg-circle-1 {
width: 300rpx;
height: 300rpx;
top: -150rpx;
right: -100rpx;
}
.bg-circle-2 {
width: 200rpx;
height: 200rpx;
bottom: -80rpx;
left: -80rpx;
}
.bg-circle-3 {
width: 150rpx;
height: 150rpx;
top: 50%;
right: 20%;
}
.header-content {
position: relative;
z-index: 2;
}
.title-row {
display: flex;
align-items: center;
margin-bottom: 20rpx;
}
.main-title {
font-size: 52rpx;
font-weight: bold;
color: white;
margin-right: 20rpx;
}
.expert-badge {
background: rgba(255, 255, 255, 0.2);
border: 2rpx solid rgba(255, 255, 255, 0.3);
border-radius: 30rpx;
padding: 6rpx 20rpx;
backdrop-filter: blur(10rpx);
}
.badge-text {
font-size: 24rpx;
color: white;
font-weight: 500;
}
.sub-title {
font-size: 28rpx;
color: rgba(255, 255, 255, 0.9);
margin-bottom: 40rpx;
display: block;
}
.stats-row {
display: flex;
align-items: center;
background: rgba(255, 255, 255, 0.15);
border-radius: 24rpx;
padding: 20rpx;
backdrop-filter: blur(10rpx);
}
.stat-item {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
}
.stat-number {
font-size: 36rpx;
font-weight: bold;
color: white;
margin-bottom: 8rpx;
}
.stat-label {
font-size: 24rpx;
color: rgba(255, 255, 255, 0.85);
}
.stat-divider {
width: 2rpx;
height: 40rpx;
background: rgba(255, 255, 255, 0.3);
}
/* 搜索和筛选区域 */
.search-filter-section {
background: white;
border-radius: 40rpx 40rpx 0 0;
margin-top: -30rpx;
position: relative;
z-index: 10;
padding: 30rpx 40rpx 0;
box-shadow: 0 -10rpx 30rpx rgba(44, 140, 52, 0.1);
}
.search-wrapper {
margin-bottom: 30rpx;
}
.search-box {
display: flex;
align-items: center;
background: #f8faf9;
border-radius: 50rpx;
padding: 20rpx 30rpx;
border: 2rpx solid #e8f5e9;
transition: all 0.3s;
}
.search-box:active {
background: #f0f9f1;
border-color: #2c8c34;
}
.search-icon {
width: 36rpx;
height: 36rpx;
margin-right: 20rpx;
opacity: 0.5;
}
.search-box input {
flex: 1;
font-size: 30rpx;
color: #333;
height: 40rpx;
line-height: 40rpx;
}
.placeholder {
color: #999;
font-size: 28rpx;
}
.clear-btn {
width: 40rpx;
height: 40rpx;
display: flex;
align-items: center;
justify-content: center;
background: #EAEBED;
border-radius: 50%;
margin-left: 10rpx;
}
.clear-icon {
width: 28rpx;
height: 28rpx;
}
/* 筛选标签 */
.filter-scroll {
white-space: nowrap;
}
.filter-tags {
display: inline-flex;
padding-bottom: 20rpx;
}
.filter-tag {
display: inline-flex;
align-items: center;
padding: 16rpx 28rpx;
margin-right: 20rpx;
background: #f8faf9;
border-radius: 30rpx;
border: 2rpx solid #e8f5e9;
color: #666;
font-size: 28rpx;
font-weight: 500;
transition: all 0.3s;
}
.filter-tag.active {
background: #2c8c34;
border-color: #2c8c34;
color: white;
}
.tag-count {
background: rgba(255, 255, 255, 0.2);
color: #2c8c34;
font-size: 22rpx;
padding: 2rpx 10rpx;
border-radius: 20rpx;
margin-left: 8rpx;
}
.filter-tag.active .tag-count {
background: rgba(255, 255, 255, 0.3);
color: white;
}
.online-count {
color: #4caf50;
}
/* 专家列表区域 */
.expert-list-section {
padding: 0 30rpx 30rpx;
}
.list-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 30rpx 0 20rpx;
border-bottom: 2rpx solid #f0f5f1;
}
.list-title {
font-size: 36rpx;
font-weight: bold;
color: #333;
}
.list-count {
font-size: 26rpx;
color: #666;
}
.expert-cards {
height: calc(100vh - 700rpx);
padding-bottom: 30rpx;
}
/* 空状态 */
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 120rpx 0;
}
.empty-image {
width: 240rpx;
height: 240rpx;
margin-bottom: 40rpx;
opacity: 0.6;
}
.empty-title {
font-size: 34rpx;
color: #999;
font-weight: bold;
margin-bottom: 20rpx;
}
/* 专家卡片 */
.expert-card {
background: white;
border-radius: 30rpx;
padding: 30rpx;
margin-bottom: 10rpx;
display: grid;
grid-template-columns: 1fr 2fr;
align-items: center;
box-shadow: 0 6rpx 30rpx rgba(0, 0, 0, 0.05);
border: 2rpx solid #f0f5f1;
transition: all 0.3s;
position: relative;
}
.expert-card:active {
transform: translateY(-4rpx);
box-shadow: 0 12rpx 40rpx rgba(0, 0, 0, 0.1);
border-color: #e8f5e9;
}
.card-left {
margin-right: 25rpx;
}
.avatar-container {
position: relative;
width: 150rpx;
height: 180rpx;
border-radius: 16rpx;
overflow: hidden;
}
.expert-avatar {
width: 100%;
height: 100%;
background: #f5f7fa;
}
.online-badge {
position: absolute;
bottom: 0rpx;
right: 0rpx;
padding: 5rpx 15rpx;
border-radius: 20rpx;
font-size: 18rpx;
font-weight: bold;
color: white;
text-align: center;
}
.online-badge.online {
background: linear-gradient(135deg, #4caf50 0%, #2c8c34 100%);
}
.online-badge.offline {
background: linear-gradient(135deg, #9e9e9e 0%, #757575 100%);
}
.card-middle {
flex: 1;
}
.name-title-row {
display: flex;
align-items: center;
margin-bottom: 20rpx;
}
.expert-name {
font-size: 36rpx;
font-weight: bold;
color: #333;
margin-right: 15rpx;
}
.expert-title {
font-size: 26rpx;
color: #2c8c34;
background: #f0f9f1;
padding: 4rpx 12rpx;
border-radius: 8rpx;
font-weight: 500;
}
/* 信息展示区域 */
.info-display {
display: flex;
flex-direction: column;
gap: 12rpx;
margin-bottom: 20rpx;
}
.info-item {
display: flex;
align-items: flex-start;
font-size: 28rpx;
line-height: 1.4;
}
.info-label {
color: #666;
min-width: 150rpx;
font-weight: 500;
}
.info-value {
color: #333;
flex: 1;
font-weight: 600;
}
.contact-btn {
font-size: 22rpx;
padding: 6rpx 20rpx;
border-radius: 30rpx;
border: none;
color: white;
font-weight: bold;
box-shadow: 0 4rpx 15rpx rgba(0, 0, 0, 0.1);
transition: all 0.3s;
display: inline-block;
position: absolute;
right: 20rpx;
bottom: 20rpx;
}
.contact-btn.online-btn {
background: linear-gradient(135deg, #4caf50 0%, #2c8c34 100%);
}
.contact-btn.offline-btn {
background: linear-gradient(135deg, #9e9e9e 0%, #757575 100%);
}
.contact-btn:active {
transform: scale(0.95);
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.2);
}
/* 联系方式弹窗 */
.dialog-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
animation: fadeIn 0.3s ease;
}
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
.contact-modal {
width: 86vw;
max-height: 80vh;
background: white;
border-radius: 20rpx;
overflow: hidden;
animation: slideUp 0.4s cubic-bezier(0.22, 0.61, 0.36, 1);
box-shadow: 0 20rpx 60rpx rgba(0, 0, 0, 0.2);
display: flex;
flex-direction: column;
}
@keyframes slideUp {
from {
opacity: 0;
transform: translateY(100rpx) scale(0.95);
}
to {
opacity: 1;
transform: translateY(0) scale(1);
}
}
/* 可滚动的内容区域 */
.modal-content {
flex: 1;
overflow-y: auto;
max-height: calc(80vh - 150rpx);
}
/* 模态框头部 */
.modal-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
padding: 40rpx 40rpx 30rpx;
background: linear-gradient(135deg, #f0f9f1 0%, #ffffff 100%);
border-bottom: 2rpx solid #f0f5f1;
}
.header-left {
display: flex;
align-items: flex-start;
flex: 1;
}
.modal-avatar {
width: 120rpx;
height: 120rpx;
border-radius: 50%;
margin-right: 25rpx;
border: 4rpx solid white;
box-shadow: 0 6rpx 20rpx rgba(0, 0, 0, 0.1);
}
.expert-intro {
flex: 1;
}
.modallei {
display: flex;
align-items: center;
}
.modal-name {
display: block;
font-size: 38rpx;
font-weight: bold;
color: #333;
}
.title-status {
display: flex;
align-items: center;
margin: 10rpx 0;
}
.modal-title {
font-size: 28rpx;
color: #2c8c34;
background: #f0f9f1;
padding: 0rpx 10rpx;
border-radius: 10rpx;
font-weight: 500;
margin: 0 20rpx;
}
.modal-status {
display: flex;
align-items: center;
font-size: 26rpx;
padding: 6rpx 15rpx;
border-radius: 10rpx;
font-weight: 500;
}
.modal-status.online {
background: #e8f5e9;
color: #2c8c34;
}
.modal-status.offline {
background: #f5f5f5;
color: #757575;
}
.status-dot {
width: 12rpx;
height: 12rpx;
border-radius: 50%;
margin-right: 8rpx;
background: currentColor;
}
/* 专家简介 */
.expert-bio {
padding: 30rpx 40rpx;
border-bottom: 2rpx solid #f0f5f1;
}
.bio-header {
display: flex;
align-items: center;
margin-bottom: 10rpx;
}
.bio-title {
font-size: 30rpx;
font-weight: bold;
color: #333;
}
.bio-content {
font-size: 28rpx;
color: #555;
line-height: 1.6;
}
/* 联系方式区域 */
.contact-section {
padding: 30rpx 40rpx;
border-bottom: 2rpx solid #f0f5f1;
}
.contact-item {
display: flex;
align-items: center;
padding: 25rpx 0;
}
.contact-item:not(:last-child) {
border-bottom: 1rpx solid #f5f5f5;
}
.contact-icon-container {
width: 60rpx;
height: 60rpx;
border-radius: 18rpx;
display: flex;
align-items: center;
justify-content: center;
margin-right: 20rpx;
}
.phone-item .contact-icon-container {
background: linear-gradient(135deg, #e8f5e9 0%, #c8e6c9 100%);
}
.email-item .contact-icon-container {
background: linear-gradient(135deg, #e3f2fd 0%, #bbdefb 100%);
}
.institution-item .contact-icon-container {
background: linear-gradient(135deg, #f3e5f5 0%, #e1bee7 100%);
}
.contact-item-icon {
width: 32rpx;
height: 32rpx;
}
.contact-info {
flex: 1;
}
.contact-label {
display: block;
font-size: 26rpx;
color: #888;
margin-bottom: 6rpx;
}
.contact-value {
display: block;
font-size: 32rpx;
color: #333;
font-weight: 600;
margin-bottom: 4rpx;
}
.contact-desc {
display: block;
font-size: 24rpx;
color: #aaa;
}
.action-btn {
display: flex;
align-items: center;
justify-content: center;
border-radius: 50rpx;
font-size: 22rpx;
font-weight: 600;
border: none;
margin-left: 20rpx;
transition: all 0.3s;
}
.call-btn {
background: linear-gradient(135deg, #4caf50 0%, #2c8c34 100%);
color: white;
}
.copy-btn {
background: linear-gradient(135deg, #2196f3 0%, #1976d2 100%);
color: white;
}
.address-btn {
background: linear-gradient(135deg, #9c27b0 0%, #7b1fa2 100%);
color: white;
}
.action-btn:active {
transform: scale(0.95);
}
/* 咨询时间 */
.consultation-time {
padding: 30rpx 40rpx;
border-bottom: 2rpx solid #f0f5f1;
}
.time-header {
display: flex;
align-items: center;
margin-bottom: 20rpx;
}
.time-title {
font-size: 30rpx;
font-weight: bold;
color: #333;
}
.time-content {
display: block;
font-size: 28rpx;
color: #555;
line-height: 1.5;
margin-bottom: 20rpx;
}
.time-slots {
display: flex;
gap: 20rpx;
}
.time-slot {
background: #f8faf9;
padding: 10rpx 20rpx;
border-radius: 15rpx;
font-size: 24rpx;
color: #666;
border: 1rpx solid #e8f5e9;
}
/* 操作按钮(固定在底部) */
.modal-actions {
display: flex;
padding: 30rpx 40rpx;
gap: 20rpx;
border-top: 2rpx solid #f0f5f1;
background: white;
flex-shrink: 0;
}
.secondary-btn,
.primary-btn {
flex: 1;
border-radius: 50rpx;
font-size: 32rpx;
font-weight: bold;
transition: all 0.3s;
}
.secondary-btn {
background: #f8faf9;
color: #666;
border: 2rpx solid #e8f5e9;
}
.primary-btn {
background: linear-gradient(135deg, #4caf50 0%, #2c8c34 100%);
color: white;
box-shadow: 0 6rpx 20rpx rgba(44, 140, 52, 0.3);
}
.secondary-btn:active,
.primary-btn:active {
transform: scale(0.98);
}

753
pagesA/pages/expertChat/expertChat.js

@ -0,0 +1,753 @@
// 获取应用实例
const app = getApp()
Page({
data: {
// 专家信息
expertInfo: {
id: 1,
name: '张明专家',
title: '资深畜牧兽医',
expertise: '牛羊疾病防治',
avatar: '/images/avatars/expert1.png',
online: true,
phone: '13800138000'
},
// 用户信息
userInfo: {
id: 1001,
name: '养殖户',
avatar: '/images/avatars/user.png'
},
// 消息列表
messageList: [],
scrollToView: '',
// 输入相关
inputValue: '',
inputFocus: false,
inputMode: 'keyboard', // keyboard or voice
inputPlaceholder: '输入消息...',
// 多媒体
showMediaSheet: false,
// 录音相关
isRecording: false,
recordingTime: 0,
recordingTip: '上滑取消录音',
recordingTimer: null,
recordManager: null,
// 页面状态
isFirstLoad: true,
showDateDivider: true,
todayDate: '',
// 消息ID计数器
messageId: 1000,
// 存储键名
storageKey: 'consult_messages_'
},
onLoad: function(options) {
// 初始化录音管理器
this.initRecordManager()
// 获取今天日期
this.setTodayDate()
// 加载用户信息
this.loadUserInfo()
// 加载专家信息
if (options.expertId) {
this.loadExpertInfo(options.expertId)
}
// 加载聊天记录
this.loadChatHistory()
// 设置键盘监听
wx.onKeyboardHeightChange(this.onKeyboardHeightChange)
// 模拟首次进入时的欢迎消息
setTimeout(() => {
this.setData({ isFirstLoad: false })
}, 2000)
},
onUnload: function() {
// 清理定时器
if (this.data.recordingTimer) {
clearInterval(this.data.recordingTimer)
}
// 移除监听器
wx.offKeyboardHeightChange()
// 保存聊天记录
this.saveChatHistory()
},
onShow: function() {
// 页面显示时自动滚动到底部
setTimeout(() => {
this.scrollToBottom()
}, 300)
},
// 初始化录音管理器
initRecordManager: function() {
this.recordManager = wx.getRecorderManager()
this.recordManager.onStart(() => {
console.log('录音开始')
})
this.recordManager.onStop((res) => {
const { tempFilePath, duration } = res
if (tempFilePath) {
this.sendAudioMessage(tempFilePath, Math.floor(duration / 1000))
}
})
this.recordManager.onError((err) => {
console.error('录音失败:', err)
wx.showToast({
title: '录音失败',
icon: 'none'
})
this.setData({ isRecording: false })
})
},
// 设置今天日期
setTodayDate: function() {
const now = new Date()
const month = now.getMonth() + 1
const date = now.getDate()
const week = ['日', '一', '二', '三', '四', '五', '六'][now.getDay()]
this.setData({
todayDate: `${month}${date}日 星期${week}`
})
},
// 加载用户信息
loadUserInfo: function() {
const userInfo = wx.getStorageSync('userInfo') || this.data.userInfo
this.setData({ userInfo })
},
// 加载专家信息
loadExpertInfo: function(expertId) {
// 这里应该是API请求,暂时使用模拟数据
wx.showLoading({ title: '加载中...' })
setTimeout(() => {
const expertInfo = {
id: expertId,
name: ['张明', '李华', '王强', '赵刚'][expertId - 1] + '专家',
title: '资深畜牧兽医',
expertise: '牛羊疾病防治',
avatar: `/images/avatars/expert${expertId}.png`,
online: Math.random() > 0.3,
phone: '138' + Math.floor(Math.random() * 100000000).toString().padStart(8, '0')
}
this.setData({
expertInfo,
storageKey: `consult_messages_${expertId}_${this.data.userInfo.id}`
})
wx.hideLoading()
}, 500)
},
// 加载聊天记录
loadChatHistory: function() {
const storageKey = this.data.storageKey
const savedMessages = wx.getStorageSync(storageKey) || []
if (savedMessages.length > 0) {
// 处理消息时间显示
const processedMessages = this.processMessageTimes(savedMessages)
this.setData({
messageList: processedMessages,
isFirstLoad: false
})
// 滚动到底部
setTimeout(() => {
this.scrollToBottom()
}, 200)
} else {
// 初始化第一条欢迎消息
this.initWelcomeMessage()
}
},
// 处理消息时间显示
processMessageTimes: function(messages) {
if (!messages || messages.length === 0) return messages
let lastTimestamp = 0
return messages.map((msg, index) => {
const currentTimestamp = msg.timestamp
// 如果两条消息间隔超过5分钟,显示时间
const timeDiff = currentTimestamp - lastTimestamp
msg.showTime = timeDiff > 5 * 60 * 1000 || index === 0
if (msg.showTime) {
lastTimestamp = currentTimestamp
}
return msg
})
},
// 初始化欢迎消息
initWelcomeMessage: function() {
const welcomeMessage = {
id: 'welcome-' + Date.now(),
sender: 'expert',
type: 'text',
content: '您好,我是' + this.data.expertInfo.name + ',有什么可以帮您?',
timestamp: Date.now() - 60000,
showTime: true
}
this.setData({
messageList: [welcomeMessage]
})
},
// 保存聊天记录
saveChatHistory: function() {
const { storageKey, messageList } = this.data
if (messageList.length > 0) {
try {
wx.setStorageSync(storageKey, messageList)
} catch (e) {
console.error('保存聊天记录失败:', e)
}
}
},
// 返回上一页
goBack: function() {
wx.navigateBack()
},
// 打电话
makePhoneCall: function() {
const phone = this.data.expertInfo.phone
wx.makePhoneCall({
phoneNumber: phone,
fail: () => {
wx.showToast({
title: '拨打失败',
icon: 'none'
})
}
})
},
// 输入处理
onInput: function(e) {
this.setData({
inputValue: e.detail.value
})
},
// 发送文本消息
sendTextMessage: function() {
const content = this.data.inputValue.trim()
if (!content) return
const newMessage = {
id: 'msg-' + (++this.data.messageId),
sender: 'user',
type: 'text',
content: content,
timestamp: Date.now(),
status: 'sending'
}
// 添加到消息列表
this.addMessageToList(newMessage)
// 清空输入框
this.setData({
inputValue: '',
inputFocus: false
})
// 模拟发送成功
setTimeout(() => {
this.updateMessageStatus(newMessage.id, 'success')
// 模拟专家回复
setTimeout(() => {
this.receiveExpertReply()
}, 1000)
}, 500)
},
// 添加消息到列表
addMessageToList: function(message) {
const { messageList } = this.data
// 确定是否需要显示时间
const lastMessage = messageList[messageList.length - 1]
const timeDiff = lastMessage ? message.timestamp - lastMessage.timestamp : 0
message.showTime = timeDiff > 5 * 60 * 1000 || !lastMessage
messageList.push(message)
this.setData({
messageList,
scrollToView: 'msg-' + message.id
})
},
// 更新消息状态
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 })
}
},
// 接收专家回复
receiveExpertReply: function() {
const replies = [
'收到您的消息,让我分析一下您说的情况。',
'建议您提供更多细节,比如发病时间、具体症状等。',
'根据描述,可能是饲料问题引起的,建议调整饲料配方。',
'可以考虑添加一些维生素补充剂,改善食欲问题。',
'最好能提供照片,这样我可以更准确地判断情况。'
]
const randomReply = replies[Math.floor(Math.random() * replies.length)]
const newMessage = {
id: 'exp-' + Date.now(),
sender: 'expert',
type: 'text',
content: randomReply,
timestamp: Date.now(),
showTime: false
}
this.addMessageToList(newMessage)
},
// 切换输入模式
switchInputMode: function() {
const newMode = this.data.inputMode === 'keyboard' ? 'voice' : 'keyboard'
const placeholder = newMode === 'voice' ? '按住说话' : '输入消息...'
this.setData({
inputMode: newMode,
inputPlaceholder: placeholder,
inputFocus: newMode === 'keyboard'
})
},
// 显示多媒体选择面板
showMediaActionSheet: function() {
this.setData({
showMediaSheet: true,
inputFocus: false
})
},
// 隐藏多媒体选择面板
hideMediaActionSheet: function() {
this.setData({
showMediaSheet: false
})
},
// 选择图片
chooseImage: function() {
this.hideMediaActionSheet()
wx.chooseImage({
count: 9,
sizeType: ['compressed'],
sourceType: ['album'],
success: (res) => {
this.uploadImages(res.tempFilePaths)
}
})
},
// 拍照
takePhoto: function() {
this.hideMediaActionSheet()
wx.chooseImage({
count: 1,
sizeType: ['compressed'],
sourceType: ['camera'],
success: (res) => {
this.uploadImages(res.tempFilePaths)
}
})
},
// 选择视频
chooseVideo: function() {
this.hideMediaActionSheet()
wx.chooseVideo({
sourceType: ['album'],
compressed: true,
maxDuration: 60,
success: (res) => {
this.uploadVideo(res.tempFilePath, res.thumbTempFilePath)
}
})
},
// 录制语音
recordAudio: function() {
this.hideMediaActionSheet()
this.startVoiceRecord()
},
// 选择文件
chooseFile: function() {
this.hideMediaActionSheet()
wx.chooseMessageFile({
count: 1,
type: 'all',
success: (res) => {
const file = res.tempFiles[0]
this.uploadFile(file.path, file.name, file.size)
}
})
},
// 上传图片
uploadImages: function(tempFilePaths) {
tempFilePaths.forEach((tempFilePath, index) => {
const fileName = 'image_' + Date.now() + '_' + index + '.jpg'
this.uploadFile(tempFilePath, fileName, 0, 'image')
})
},
// 上传视频
uploadVideo: function(tempFilePath, thumbPath) {
const fileName = 'video_' + Date.now() + '.mp4'
this.uploadFile(tempFilePath, fileName, 0, 'video', thumbPath)
},
// 通用文件上传
uploadFile: function(tempFilePath, fileName, fileSize = 0, type = 'file', thumbPath = '') {
// 获取文件扩展名
const extension = fileName.split('.').pop().toLowerCase()
// 创建消息
const messageId = 'file-' + Date.now()
const message = {
id: messageId,
sender: 'user',
type: type,
content: tempFilePath,
thumb: thumbPath,
fileName: fileName,
fileSize: fileSize,
extension: extension,
timestamp: Date.now(),
status: 'uploading',
progress: 0
}
this.addMessageToList(message)
// 模拟上传过程
let progress = 0
const uploadInterval = setInterval(() => {
progress += Math.random() * 20 + 10
if (progress >= 100) {
progress = 100
clearInterval(uploadInterval)
setTimeout(() => {
this.updateMessageStatus(messageId, 'success')
// 清除进度信息
const { messageList } = this.data
const index = messageList.findIndex(msg => msg.id === messageId)
if (index !== -1) {
delete messageList[index].progress
this.setData({ messageList })
// 模拟专家回复
if (type === 'image' || type === 'video') {
setTimeout(() => {
this.receiveMediaReply(type)
}, 800)
}
}
}, 200)
}
// 更新进度
const { messageList } = this.data
const index = messageList.findIndex(msg => msg.id === messageId)
if (index !== -1) {
messageList[index].progress = Math.min(progress, 100)
this.setData({ messageList })
}
}, 100)
},
// 接收媒体回复
receiveMediaReply: function(type) {
const imageReplies = [
'照片收到了,牛的状况看起来确实不太理想。',
'从照片看,饲养环境需要改善一下。',
'图片清晰,我可以更准确地判断问题了。'
]
const videoReplies = [
'视频看到了,动物的精神状态需要关注。',
'从视频可以观察到更多细节,这很有帮助。',
'视频内容很有价值,让我了解了具体情况。'
]
const replies = type === 'image' ? imageReplies : videoReplies
const randomReply = replies[Math.floor(Math.random() * replies.length)]
const newMessage = {
id: 'exp-media-' + Date.now(),
sender: 'expert',
type: 'text',
content: randomReply,
timestamp: Date.now(),
showTime: false
}
this.addMessageToList(newMessage)
},
// 开始语音录制
startVoiceRecord: function(e) {
this.setData({
isRecording: true,
recordingTime: 0,
recordingTip: '上滑取消录音'
})
// 开始录音
this.recordManager.start({
duration: 60000, // 最长60秒
sampleRate: 44100,
numberOfChannels: 1,
encodeBitRate: 192000,
format: 'mp3'
})
// 开始计时
const timer = setInterval(() => {
const time = this.data.recordingTime + 1
this.setData({ recordingTime: time })
}, 1000)
this.setData({ recordingTimer: timer })
},
// 结束语音录制
endVoiceRecord: function() {
if (this.data.isRecording) {
this.recordManager.stop()
if (this.data.recordingTimer) {
clearInterval(this.data.recordingTimer)
}
this.setData({
isRecording: false,
recordingTime: 0
})
}
},
// 发送语音消息
sendAudioMessage: function(tempFilePath, duration) {
const message = {
id: 'audio-' + Date.now(),
sender: 'user',
type: 'audio',
content: tempFilePath,
duration: duration,
timestamp: Date.now(),
status: 'sending'
}
this.addMessageToList(message)
// 模拟发送成功
setTimeout(() => {
this.updateMessageStatus(message.id, 'success')
// 模拟专家回复
setTimeout(() => {
const reply = {
id: 'exp-audio-' + Date.now(),
sender: 'expert',
type: 'text',
content: '语音收到了,我会仔细听取分析。',
timestamp: Date.now(),
showTime: false
}
this.addMessageToList(reply)
}, 1500)
}, 800)
},
// 预览图片
previewImage: function(e) {
const url = e.currentTarget.dataset.url
wx.previewImage({
current: url,
urls: [url]
})
},
// 下载文件
downloadFile: function(e) {
const url = e.currentTarget.dataset.url
wx.showLoading({ title: '下载中...' })
wx.downloadFile({
url: url,
success: (res) => {
wx.hideLoading()
wx.showToast({
title: '下载成功',
icon: 'success'
})
// 保存到相册(如果是图片)
if (url.match(/\.(jpg|jpeg|png|gif)$/i)) {
wx.saveImageToPhotosAlbum({
filePath: res.tempFilePath,
success: () => {
wx.showToast({
title: '已保存到相册',
icon: 'success'
})
}
})
}
},
fail: () => {
wx.hideLoading()
wx.showToast({
title: '下载失败',
icon: 'none'
})
}
})
},
// 键盘高度变化
onKeyboardHeightChange: function(res) {
if (res.height > 0) {
// 键盘弹出时隐藏多媒体面板
this.setData({ showMediaSheet: false })
// 滚动到底部
setTimeout(() => {
this.scrollToBottom()
}, 100)
}
},
// 滚动到底部
scrollToBottom: function() {
if (this.data.messageList.length > 0) {
const lastMessage = this.data.messageList[this.data.messageList.length - 1]
this.setData({
scrollToView: lastMessage.id
})
}
},
// 格式化时间(微信样式)
formatTime: function(timestamp) {
const now = new Date()
const date = new Date(timestamp)
const diff = now - date
// 今天
if (date.toDateString() === now.toDateString()) {
return this.formatMessageTime(timestamp)
}
// 昨天
const yesterday = new Date(now)
yesterday.setDate(yesterday.getDate() - 1)
if (date.toDateString() === yesterday.toDateString()) {
return '昨天 ' + this.formatMessageTime(timestamp)
}
// 一周内
if (diff < 7 * 24 * 60 * 60 * 1000) {
const weekDays = ['周日', '周一', '周二', '周三', '周四', '周五', '周六']
return weekDays[date.getDay()] + ' ' + this.formatMessageTime(timestamp)
}
// 今年内
if (date.getFullYear() === now.getFullYear()) {
return `${date.getMonth() + 1}${date.getDate()}${this.formatMessageTime(timestamp)}`
}
// 更早
return `${date.getFullYear()}/${date.getMonth() + 1}/${date.getDate()} ${this.formatMessageTime(timestamp)}`
},
// 格式化消息时间(HH:mm)
formatMessageTime: function(timestamp) {
const date = new Date(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 === 0) return '未知大小'
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]
},
// 阻止事件冒泡
stopPropagation: function() {
// 空函数,仅用于阻止事件冒泡
}
})

4
pagesA/pages/expertChat/expertChat.json

@ -0,0 +1,4 @@
{
"navigationBarTitleText":"问专家",
"usingComponents": {}
}

296
pagesA/pages/expertChat/expertChat.wxml

@ -0,0 +1,296 @@
<view class="consult-page">
<!-- 头部专家信息 -->
<view class="consult-header">
<view class="header-content">
<view class="header-left">
<button class="back-btn" bindtap="goBack">
<image src="/images/icons/back.png" class="back-icon"></image>
</button>
</view>
<view class="header-center">
<text class="expert-name">{{expertInfo.name}}</text>
<view class="expert-status">
<view class="status-dot {{expertInfo.online ? 'online' : 'offline'}}"></view>
<text class="status-text">{{expertInfo.online ? '在线' : '离线'}}</text>
</view>
</view>
<view class="header-right">
<button class="header-action-btn" bindtap="makePhoneCall">
<image src="/images/icons/phone.png" class="header-action-icon"></image>
</button>
</view>
</view>
</view>
<!-- 聊天内容区域 -->
<scroll-view class="chat-container" scroll-y scroll-into-view="{{scrollToView}}" scroll-with-animation="true">
<!-- 首次咨询欢迎语 -->
<view class="system-welcome" wx:if="{{isFirstLoad}}">
<view class="welcome-avatar">
<image src="{{expertInfo.avatar}}"></image>
</view>
<text class="welcome-name">{{expertInfo.name}}</text>
<text class="welcome-title">{{expertInfo.title}} · {{expertInfo.expertise}}</text>
<text class="welcome-tip">您好,我是{{expertInfo.name}},很高兴为您服务!</text>
</view>
<!-- 日期分隔线 -->
<view class="date-divider" wx:if="{{showDateDivider}}">
<text class="date-text">{{todayDate}}</text>
</view>
<!-- 消息列表 -->
<block wx:for="{{messageList}}" wx:key="id">
<!-- 时间分隔 -->
<view class="time-divider" wx:if="{{item.showTime}}">
<text>{{formatTime(item.timestamp)}}</text>
</view>
<!-- 对方消息 -->
<view class="message-item message-left" wx:if="{{item.sender === 'expert'}}">
<view class="message-avatar">
<image src="{{expertInfo.avatar}}"></image>
</view>
<view class="message-content-wrapper">
<!-- 文本消息 -->
<view class="message-bubble message-bubble-left" wx:if="{{item.type === 'text'}}">
<text class="message-text">{{item.content}}</text>
</view>
<!-- 图片消息 -->
<view class="message-bubble message-bubble-left" wx:elif="{{item.type === 'image'}}">
<image src="{{item.content}}" class="message-image" mode="widthFix" bindtap="previewImage" data-url="{{item.content}}"></image>
</view>
<!-- 视频消息 -->
<view class="message-bubble message-bubble-left" wx:elif="{{item.type === 'video'}}">
<video src="{{item.content}}" class="message-video" controls poster="{{item.thumb}}"></video>
<view class="video-play-btn">
<image src="/images/icons/play.png"></image>
</view>
</view>
<!-- 语音消息 -->
<view class="message-bubble message-bubble-left message-audio" wx:elif="{{item.type === 'audio'}}">
<image src="/images/icons/audio-left.png" class="audio-icon-left"></image>
<view class="audio-content">
<view class="audio-wave">
<view class="wave-bar"></view>
<view class="wave-bar"></view>
<view class="wave-bar"></view>
<view class="wave-bar"></view>
<view class="wave-bar"></view>
</view>
<text class="audio-duration">{{item.duration}}''</text>
</view>
</view>
<!-- 文件消息 -->
<view class="message-bubble message-bubble-left message-file" wx:elif="{{item.type === 'file'}}">
<view class="file-icon-box">
<image src="/images/icons/file.png" class="file-icon"></image>
<text class="file-extension">{{item.extension}}</text>
</view>
<view class="file-info">
<text class="file-name">{{item.fileName}}</text>
<text class="file-size">{{formatFileSize(item.fileSize)}}</text>
</view>
<button class="file-download-btn" bindtap="downloadFile" data-url="{{item.content}}">下载</button>
</view>
<text class="message-time">{{formatMessageTime(item.timestamp)}}</text>
</view>
</view>
<!-- 我的消息 -->
<view class="message-item message-right" wx:else>
<view class="message-content-wrapper">
<!-- 消息状态 -->
<view class="message-status">
<image wx:if="{{item.status === 'sending'}}" src="/images/icons/sending.png" class="status-icon"></image>
<image wx:if="{{item.status === 'success'}}" src="/images/icons/success.png" class="status-icon"></image>
<image wx:if="{{item.status === 'error'}}" src="/images/icons/error.png" class="status-icon"></image>
</view>
<!-- 文本消息 -->
<view class="message-bubble message-bubble-right" wx:if="{{item.type === 'text'}}">
<text class="message-text">{{item.content}}</text>
</view>
<!-- 图片消息 -->
<view class="message-bubble message-bubble-right" wx:elif="{{item.type === 'image'}}">
<image src="{{item.content}}" class="message-image" mode="widthFix" bindtap="previewImage" data-url="{{item.content}}"></image>
<view class="upload-progress" wx:if="{{item.status === 'uploading'}}">
<view class="progress-circle">
<view class="progress-fill" style="transform: rotate({{item.progress * 3.6}}deg);"></view>
<text class="progress-text">{{item.progress}}%</text>
</view>
</view>
</view>
<!-- 视频消息 -->
<view class="message-bubble message-bubble-right" wx:elif="{{item.type === 'video'}}">
<video src="{{item.content}}" class="message-video" controls poster="{{item.thumb}}"></video>
<view class="video-play-btn">
<image src="/images/icons/play.png"></image>
</view>
<view class="upload-progress" wx:if="{{item.status === 'uploading'}}">
<view class="progress-circle">
<view class="progress-fill" style="transform: rotate({{item.progress * 3.6}}deg);"></view>
<text class="progress-text">{{item.progress}}%</text>
</view>
</view>
</view>
<!-- 语音消息 -->
<view class="message-bubble message-bubble-right message-audio" wx:elif="{{item.type === 'audio'}}">
<view class="audio-content">
<view class="audio-wave">
<view class="wave-bar"></view>
<view class="wave-bar"></view>
<view class="wave-bar"></view>
<view class="wave-bar"></view>
<view class="wave-bar"></view>
</view>
<text class="audio-duration">{{item.duration}}''</text>
</view>
<image src="/images/icons/audio-right.png" class="audio-icon-right"></image>
</view>
<!-- 文件消息 -->
<view class="message-bubble message-bubble-right message-file" wx:elif="{{item.type === 'file'}}">
<view class="file-icon-box">
<image src="/images/icons/file.png" class="file-icon"></image>
<text class="file-extension">{{item.extension}}</text>
</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">
<view class="progress-fill" style="transform: rotate({{item.progress * 3.6}}deg);"></view>
<text class="progress-text">{{item.progress}}%</text>
</view>
</view>
</view>
<text class="message-time">{{formatMessageTime(item.timestamp)}}</text>
</view>
<view class="message-avatar">
<image src="{{userInfo.avatar}}"></image>
</view>
</view>
</block>
</scroll-view>
<!-- 输入区域 -->
<view class="input-section">
<!-- 录音面板 -->
<view class="voice-panel" wx:if="{{inputMode === 'voice'}}">
<button class="voice-record-btn" bindtouchstart="startVoiceRecord" bindtouchend="endVoiceRecord">
<image src="/images/icons/mic.png" class="mic-icon"></image>
<text class="voice-tip">按住说话</text>
</button>
</view>
<!-- 正常输入面板 -->
<view class="input-panel" wx:else>
<!-- 语音/键盘切换按钮 -->
<button class="input-mode-btn" bindtap="switchInputMode">
<image src="{{inputMode === 'keyboard' ? '/images/icons/voice.png' : '/images/icons/keyboard.png'}}" class="mode-icon"></image>
</button>
<!-- 输入框 -->
<view class="input-box-wrapper">
<input
type="text"
class="chat-input"
placeholder="{{inputPlaceholder}}"
placeholder-class="placeholder"
value="{{inputValue}}"
bindinput="onInput"
bindconfirm="sendTextMessage"
confirm-type="send"
focus="{{inputFocus}}"
adjust-position="{{false}}"
/>
</view>
<!-- 多媒体按钮 -->
<button class="media-btn" bindtap="showMediaActionSheet">
<image src="/images/icons/add.png" class="media-icon"></image>
</button>
<!-- 发送按钮 -->
<button class="send-btn" bindtap="sendTextMessage" wx:if="{{inputValue.trim()}}">
<image src="/images/icons/send.png" class="send-icon"></image>
</button>
</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>
<button class="close-sheet-btn" bindtap="hideMediaActionSheet">
<image src="/images/icons/close.png"></image>
</button>
</view>
<view class="media-options">
<button class="media-option" bindtap="chooseImage">
<view class="option-icon photo-icon">
<image src="/images/icons/photo.png"></image>
</view>
<text class="option-text">照片</text>
</button>
<button class="media-option" bindtap="takePhoto">
<view class="option-icon camera-icon">
<image src="/images/icons/camera.png"></image>
</view>
<text class="option-text">拍摄</text>
</button>
<button class="media-option" bindtap="chooseVideo">
<view class="option-icon video-icon">
<image src="/images/icons/video.png"></image>
</view>
<text class="option-text">视频</text>
</button>
<button class="media-option" bindtap="recordAudio">
<view class="option-icon audio-icon">
<image src="/images/icons/voice.png"></image>
</view>
<text class="option-text">语音</text>
</button>
<button class="media-option" bindtap="chooseFile">
<view class="option-icon file-icon">
<image src="/images/icons/file2.png"></image>
</view>
<text class="option-text">文件</text>
</button>
</view>
</view>
</view>
<!-- 录音提示 -->
<view class="recording-modal" wx:if="{{isRecording}}">
<view class="recording-box">
<view class="recording-wave-box">
<view class="recording-wave"></view>
<image src="/images/icons/mic2.png" class="recording-mic-icon"></image>
</view>
<text class="recording-tip">{{recordingTip}}</text>
<text class="recording-time">{{recordingTime}}s</text>
</view>
</view>
</view>

928
pagesA/pages/expertChat/expertChat.wxss

@ -0,0 +1,928 @@
/* 页面整体样式 */
.consult-page {
width: 100vw;
height: 100vh;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
display: flex;
flex-direction: column;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', sans-serif;
}
/* 头部样式 */
.consult-header {
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(20px);
padding: 15rpx 30rpx;
border-bottom: 1rpx solid rgba(0, 0, 0, 0.1);
position: relative;
z-index: 100;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
}
.header-content {
display: flex;
align-items: center;
justify-content: space-between;
height: 90rpx;
}
.header-left {
flex: 1;
display: flex;
justify-content: flex-start;
}
.header-center {
flex: 2;
display: flex;
flex-direction: column;
align-items: center;
}
.header-right {
flex: 1;
display: flex;
justify-content: flex-end;
}
.back-btn {
width: 70rpx;
height: 70rpx;
border-radius: 50%;
border: none;
background: rgba(0, 0, 0, 0.05);
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s;
}
.back-btn:active {
background: rgba(0, 0, 0, 0.1);
transform: scale(0.95);
}
.back-icon {
width: 30rpx;
height: 30rpx;
}
.expert-name {
font-size: 34rpx;
font-weight: 600;
color: #333;
margin-bottom: 5rpx;
}
.expert-status {
display: flex;
align-items: center;
justify-content: center;
}
.status-dot {
width: 14rpx;
height: 14rpx;
border-radius: 50%;
margin-right: 8rpx;
}
.status-dot.online {
background: #07c160;
box-shadow: 0 0 10rpx rgba(7, 193, 96, 0.5);
}
.status-dot.offline {
background: #999;
}
.status-text {
font-size: 24rpx;
color: #666;
}
.header-action-btn {
width: 70rpx;
height: 70rpx;
border-radius: 50%;
border: none;
background: rgba(0, 0, 0, 0.05);
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s;
}
.header-action-btn:active {
background: rgba(0, 0, 0, 0.1);
transform: scale(0.95);
}
.header-action-icon {
width: 32rpx;
height: 32rpx;
}
/* 聊天容器 */
.chat-container {
flex: 1;
padding: 30rpx 20rpx 0;
background: linear-gradient(180deg, #f5f7fa 0%, #f0f2f5 100%);
overflow-y: auto;
position: relative;
}
/* 欢迎语 */
.system-welcome {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 60rpx 40rpx;
text-align: center;
background: white;
border-radius: 30rpx;
margin: 0 auto 40rpx;
max-width: 80%;
box-shadow: 0 8rpx 30rpx rgba(0, 0, 0, 0.08);
animation: fadeInUp 0.6s ease;
}
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(50rpx);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.welcome-avatar {
width: 120rpx;
height: 120rpx;
border-radius: 50%;
overflow: hidden;
margin-bottom: 20rpx;
border: 4rpx solid white;
box-shadow: 0 10rpx 30rpx rgba(0, 0, 0, 0.1);
}
.welcome-avatar image {
width: 100%;
height: 100%;
}
.welcome-name {
font-size: 34rpx;
font-weight: 600;
color: #333;
margin-bottom: 8rpx;
}
.welcome-title {
font-size: 26rpx;
color: #666;
margin-bottom: 25rpx;
}
.welcome-tip {
font-size: 28rpx;
color: #4caf50;
line-height: 1.4;
}
/* 日期分隔线 */
.date-divider {
display: flex;
justify-content: center;
margin: 40rpx 0;
}
.date-text {
background: rgba(0, 0, 0, 0.05);
padding: 8rpx 24rpx;
border-radius: 20rpx;
font-size: 24rpx;
color: #999;
font-weight: 500;
}
/* 时间分隔 */
.time-divider {
display: flex;
justify-content: center;
margin: 30rpx 0;
animation: fadeIn 0.3s ease;
}
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
.time-divider text {
background: rgba(0, 0, 0, 0.05);
padding: 6rpx 20rpx;
border-radius: 15rpx;
font-size: 22rpx;
color: #999;
}
/* 消息项 */
.message-item {
display: flex;
margin-bottom: 40rpx;
animation: slideIn 0.3s ease;
}
@keyframes slideIn {
from {
opacity: 0;
transform: translateY(20rpx);
}
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: 10rpx;
overflow: hidden;
flex-shrink: 0;
}
.message-left .message-avatar {
margin-right: 20rpx;
}
.message-right .message-avatar {
margin-left: 20rpx;
}
.message-avatar image {
width: 100%;
height: 100%;
}
.message-content-wrapper {
max-width: 65%;
display: flex;
flex-direction: column;
}
.message-left .message-content-wrapper {
align-items: flex-start;
}
.message-right .message-content-wrapper {
align-items: flex-end;
}
/* 消息气泡 */
.message-bubble {
padding: 20rpx 25rpx;
border-radius: 20rpx;
position: relative;
word-break: break-word;
line-height: 1.5;
transition: all 0.3s;
}
.message-bubble-left {
background: white;
border-radius: 0 20rpx 20rpx 20rpx;
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.08);
}
.message-bubble-right {
background: linear-gradient(135deg, #95ec69 0%, #5ac725 100%);
color: white;
border-radius: 20rpx 0 20rpx 20rpx;
box-shadow: 0 4rpx 20rpx rgba(90, 199, 37, 0.2);
}
.message-text {
font-size: 30rpx;
line-height: 1.5;
}
.message-bubble-left .message-text {
color: #333;
}
.message-bubble-right .message-text {
color: white;
}
/* 消息时间 */
.message-time {
font-size: 22rpx;
color: #999;
margin-top: 8rpx;
text-align: center;
}
/* 消息状态 */
.message-status {
margin-bottom: 10rpx;
}
.status-icon {
width: 28rpx;
height: 28rpx;
}
/* 图片消息 */
.message-image {
max-width: 300rpx;
max-height: 400rpx;
border-radius: 12rpx;
display: block;
}
/* 视频消息 */
.message-video {
width: 300rpx;
height: 200rpx;
border-radius: 12rpx;
background: #000;
}
.video-play-btn {
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;
}
.video-play-btn image {
width: 40rpx;
height: 40rpx;
}
/* 语音消息 */
.message-audio {
display: flex;
align-items: center;
padding: 25rpx;
}
.audio-icon-left {
width: 40rpx;
height: 40rpx;
margin-right: 20rpx;
}
.audio-icon-right {
width: 40rpx;
height: 40rpx;
margin-left: 20rpx;
}
.audio-content {
display: flex;
align-items: center;
}
.audio-wave {
display: flex;
align-items: flex-end;
height: 40rpx;
margin-right: 15rpx;
}
.wave-bar {
width: 4rpx;
margin: 0 2rpx;
background: currentColor;
animation: wave 1.2s ease-in-out infinite;
}
.wave-bar:nth-child(1) {
height: 20rpx;
animation-delay: 0s;
}
.wave-bar:nth-child(2) {
height: 30rpx;
animation-delay: 0.2s;
}
.wave-bar:nth-child(3) {
height: 40rpx;
animation-delay: 0.4s;
}
.wave-bar:nth-child(4) {
height: 30rpx;
animation-delay: 0.6s;
}
.wave-bar:nth-child(5) {
height: 20rpx;
animation-delay: 0.8s;
}
@keyframes wave {
0%, 100% {
transform: scaleY(0.5);
}
50% {
transform: scaleY(1);
}
}
.audio-duration {
font-size: 28rpx;
font-weight: 500;
}
.message-bubble-left .audio-duration {
color: #666;
}
.message-bubble-right .audio-duration {
color: white;
}
/* 文件消息 */
.message-file {
display: flex;
align-items: center;
padding: 25rpx;
min-width: 300rpx;
}
.file-icon-box {
position: relative;
margin-right: 20rpx;
}
.file-icon {
width: 60rpx;
height: 60rpx;
}
.file-extension {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 18rpx;
font-weight: 600;
color: white;
}
.file-info {
flex: 1;
display: flex;
flex-direction: column;
}
.file-name {
font-size: 28rpx;
font-weight: 500;
margin-bottom: 8rpx;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
max-width: 200rpx;
}
.message-bubble-left .file-name {
color: #333;
}
.message-bubble-right .file-name {
color: white;
}
.file-size {
font-size: 24rpx;
}
.message-bubble-left .file-size {
color: #999;
}
.message-bubble-right .file-size {
color: rgba(255, 255, 255, 0.9);
}
.file-download-btn {
background: rgba(0, 0, 0, 0.1);
border: none;
padding: 10rpx 20rpx;
border-radius: 15rpx;
font-size: 24rpx;
color: white;
margin-left: 15rpx;
}
.file-download-btn:active {
background: rgba(0, 0, 0, 0.2);
}
/* 上传进度 */
.upload-progress {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
border-radius: 12rpx;
display: flex;
align-items: center;
justify-content: center;
}
.progress-circle {
position: relative;
width: 80rpx;
height: 80rpx;
}
.progress-fill {
position: absolute;
width: 100%;
height: 100%;
border: 6rpx solid #4caf50;
border-radius: 50%;
border-top-color: transparent;
border-right-color: transparent;
}
.progress-text {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 22rpx;
color: white;
font-weight: 600;
}
/* 输入区域 */
.input-section {
background: white;
border-top: 1rpx solid #e0e0e0;
padding: 20rpx 30rpx;
position: relative;
box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.05);
}
/* 语音面板 */
.voice-panel {
display: flex;
align-items: center;
justify-content: center;
padding: 20rpx 0;
}
.voice-record-btn {
width: 100%;
height: 100rpx;
background: linear-gradient(135deg, #f5f5f5 0%, #e0e0e0 100%);
border: none;
border-radius: 50rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
transition: all 0.3s;
}
.voice-record-btn:active {
background: linear-gradient(135deg, #e0e0e0 0%, #d0d0d0 100%);
transform: scale(0.98);
}
.mic-icon {
width: 50rpx;
height: 50rpx;
margin-bottom: 10rpx;
}
.voice-tip {
font-size: 26rpx;
color: #666;
}
/* 输入面板 */
.input-panel {
display: flex;
align-items: center;
gap: 20rpx;
}
.input-mode-btn {
width: 80rpx;
height: 80rpx;
border-radius: 50%;
border: none;
background: #f5f5f5;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s;
}
.input-mode-btn:active {
background: #e0e0e0;
transform: scale(0.95);
}
.mode-icon {
width: 36rpx;
height: 36rpx;
}
.input-box-wrapper {
flex: 1;
background: #f5f5f5;
border-radius: 40rpx;
padding: 0 30rpx;
height: 80rpx;
display: flex;
align-items: center;
transition: all 0.3s;
}
.input-box-wrapper:active {
background: #e8e8e8;
}
.chat-input {
flex: 1;
height: 100%;
font-size: 30rpx;
color: #333;
}
.placeholder {
color: #999;
font-size: 28rpx;
}
.media-btn,
.send-btn {
width: 80rpx;
height: 80rpx;
border-radius: 50%;
border: none;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s;
}
.media-btn {
background: #f5f5f5;
}
.media-btn:active {
background: #e0e0e0;
transform: scale(0.95);
}
.send-btn {
background: linear-gradient(135deg, #95ec69 0%, #5ac725 100%);
}
.send-btn:active {
background: linear-gradient(135deg, #5ac725 0%, #4caf50 100%);
transform: scale(0.95);
}
.media-icon,
.send-icon {
width: 36rpx;
height: 36rpx;
}
.send-icon {
filter: brightness(0) invert(1);
}
/* 多媒体选择面板 */
.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: 1000;
animation: fadeIn 0.3s ease;
}
.media-sheet-content {
width: 100%;
background: white;
border-radius: 40rpx 40rpx 0 0;
padding: 40rpx 30rpx 60rpx;
animation: slideUp 0.3s ease;
}
@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;
}
.sheet-title {
font-size: 32rpx;
font-weight: 600;
color: #333;
}
.close-sheet-btn {
width: 60rpx;
height: 60rpx;
border-radius: 50%;
border: none;
background: #f5f5f5;
display: flex;
align-items: center;
justify-content: center;
}
.close-sheet-btn image {
width: 24rpx;
height: 24rpx;
}
.media-options {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 30rpx;
}
.media-option {
display: flex;
flex-direction: column;
align-items: center;
border: none;
background: none;
padding: 20rpx 10rpx;
border-radius: 20rpx;
transition: all 0.3s;
}
.media-option:active {
background: #f5f5f5;
}
.option-icon {
width: 100rpx;
height: 100rpx;
border-radius: 25rpx;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 20rpx;
}
.photo-icon {
background: linear-gradient(135deg, #e3f2fd 0%, #bbdefb 100%);
}
.camera-icon {
background: linear-gradient(135deg, #f3e5f5 0%, #e1bee7 100%);
}
.video-icon {
background: linear-gradient(135deg, #e8f5e9 0%, #c8e6c9 100%);
}
.audio-icon {
background: linear-gradient(135deg, #fff3e0 0%, #ffe0b2 100%);
}
.file-icon {
background: linear-gradient(135deg, #f5f5f5 0%, #e0e0e0 100%);
}
.option-icon image {
width: 50rpx;
height: 50rpx;
}
.option-text {
font-size: 26rpx;
color: #666;
}
/* 录音模态框 */
.recording-modal {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.7);
display: flex;
align-items: center;
justify-content: center;
z-index: 1001;
animation: fadeIn 0.2s ease;
}
.recording-box {
background: white;
border-radius: 40rpx;
padding: 60rpx;
display: flex;
flex-direction: column;
align-items: center;
box-shadow: 0 20rpx 60rpx rgba(0, 0, 0, 0.3);
}
.recording-wave-box {
position: relative;
width: 200rpx;
height: 200rpx;
margin-bottom: 30rpx;
}
.recording-wave {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
border: 8rpx solid #5ac725;
border-radius: 50%;
animation: pulse 1.5s ease-in-out infinite;
}
@keyframes pulse {
0% {
transform: scale(1);
opacity: 1;
}
100% {
transform: scale(1.2);
opacity: 0;
}
}
.recording-mic-icon {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 80rpx;
height: 80rpx;
}
.recording-tip {
font-size: 32rpx;
color: #333;
font-weight: 500;
margin-bottom: 15rpx;
}
.recording-time {
font-size: 36rpx;
color: #5ac725;
font-weight: bold;
}

23
utils/api.js

@ -56,9 +56,30 @@ function trend(params) {
http('/muhu/trend/list', 'get', params) http('/muhu/trend/list', 'get', params)
} }
// 问兽医-问诊单列表
function wzd(params) {
http('/muhu/consultation/list', 'get', params)
}
// 问兽医-问诊单新增
function wzdAdd(params) {
http('/muhu/consultation', 'post', params)
}
// 问兽医-兽医回复详情
function wzdxq(params) {
http('/vet/comments/list', 'get', params)
}
// 专家列表
function expertsList(params) {
http('/vet/experts/list', 'get', params)
}
export default { // 暴露接口 export default { // 暴露接口
login,carousel,disaster,pharmacy,guidance,getPhoneNumber,inquiry, login,carousel,disaster,pharmacy,guidance,getPhoneNumber,inquiry,
search,trend,feed,sales
search,trend,feed,sales,wzd,wzdxq,wzdAdd,expertsList
} }
Loading…
Cancel
Save