Browse Source

远程治理,个人中心,经验分享

master
ZhaoYang 1 month ago
parent
commit
b885746494
  1. 11
      app.json
  2. 62
      pages/home/home.js
  3. 2
      pages/home/home.wxml
  4. BIN
      pages/images/bj.png
  5. 366
      pages/personal/personal.js
  6. 1
      pages/personal/personal.json
  7. 136
      pages/personal/personal.wxml
  8. 612
      pages/personal/personal.wxss
  9. BIN
      pagesA/images/ch.png
  10. 159
      pagesA/pages/advisory/advisory.js
  11. 4
      pagesA/pages/advisory/advisory.json
  12. 62
      pagesA/pages/advisory/advisory.wxml
  13. 360
      pagesA/pages/advisory/advisory.wxss
  14. 66
      pagesA/pages/precept/precept.js
  15. 3
      pagesA/pages/precept/precept.json
  16. 95
      pagesA/pages/precept/precept.wxml
  17. 428
      pagesA/pages/precept/precept.wxss
  18. 191
      pagesA/pages/releaseSuffer/releaseSuffer.js
  19. 4
      pagesA/pages/releaseSuffer/releaseSuffer.json
  20. 109
      pagesA/pages/releaseSuffer/releaseSuffer.wxml
  21. 278
      pagesA/pages/releaseSuffer/releaseSuffer.wxss
  22. 37
      pagesB/pages/experienceDetails/experienceDetails.js
  23. 4
      pagesB/pages/experienceDetails/experienceDetails.json
  24. 52
      pagesB/pages/experienceDetails/experienceDetails.wxml
  25. 158
      pagesB/pages/experienceDetails/experienceDetails.wxss
  26. 217
      pagesB/pages/experienceList/experienceList.js
  27. 4
      pagesB/pages/experienceList/experienceList.json
  28. 89
      pagesB/pages/experienceList/experienceList.wxml
  29. 390
      pagesB/pages/experienceList/experienceList.wxss
  30. 477
      pagesB/pages/forumlist/forumlist.js
  31. 4
      pagesB/pages/forumlist/forumlist.json
  32. 208
      pagesB/pages/forumlist/forumlist.wxml
  33. 758
      pagesB/pages/forumlist/forumlist.wxss
  34. 328
      pagesB/pages/onlineAsk/onlineAsk.js
  35. 4
      pagesB/pages/onlineAsk/onlineAsk.json
  36. 152
      pagesB/pages/onlineAsk/onlineAsk.wxml
  37. 434
      pagesB/pages/onlineAsk/onlineAsk.wxss
  38. 21
      utils/api.js

11
app.json

@ -11,7 +11,10 @@
"pages": [
"pages/carouselDetail/carouselDetail",
"pages/askingSy/askingSy",
"pages/askingSyDetails/askingSyDetails"
"pages/askingSyDetails/askingSyDetails",
"pages/advisory/advisory",
"pages/releaseSuffer/releaseSuffer",
"pages/precept/precept"
]
},
{
@ -21,7 +24,11 @@
"pages/training/training",
"pages/wzDetails/wzDetails",
"pages/spDetails/spDetails",
"pages/publishAdd/publishAdd"
"pages/publishAdd/publishAdd",
"pages/forumlist/forumlist",
"pages/onlineAsk/onlineAsk",
"pages/experienceList/experienceList",
"pages/experienceDetails/experienceDetails"
],
"independent": true
}

62
pages/home/home.js

@ -6,9 +6,11 @@ Page({
currentSwiper: 0,
baseUrl: baseUrl,
swiperList: [],
articleList:[
{title:'发布信息',describe:'快速发布养殖知识|视频培训发布',icon:'/pages/images/fkjy.png'},
],
articleList: [{
title: '发布信息',
describe: '快速发布养殖知识|视频培训发布',
icon: '/pages/images/fkjy.png'
}, ],
// 通知公告数据
currentNotice: 0,
noticeList: [],
@ -24,7 +26,7 @@ Page({
http.UserInfo({
data: {},
success: res => {
console.log(111,res);
console.log(111, res);
this.setData({
user: res.data.user.area
})
@ -40,15 +42,31 @@ Page({
})
},
// 远程诊疗
bindYczl() {
wx.navigateTo({
url: '/pagesA/pages/askingSy/askingSy',
})
},
// 远程诊疗
bindYczl() {
wx.navigateTo({
url: '/pagesA/pages/askingSy/askingSy',
})
},
// 在线问答列表页
bindwdlist() {
wx.navigateTo({
url: '/pagesB/pages/forumlist/forumlist',
})
},
// 在线问答列表
// 问答详情
bindTw(e) {
console.log(e);
const id = e.currentTarget.dataset.id
wx.navigateTo({
url: `/pagesB/pages/onlineAsk/onlineAsk?id=${id}`,
})
},
// 在线问答
getforumList() {
http.forumList({
data: {},
@ -104,10 +122,10 @@ Page({
})
},
// 问兽医
// 接收咨询
bindWsy() {
wx.navigateTo({
url: '/pagesA/pages/askingSy/askingSy',
url: '/pagesA/pages/advisory/advisory',
})
},
@ -118,10 +136,10 @@ Page({
})
},
// 去买药
// 方案制定
bindYao() {
wx.navigateTo({
url: '/pagesA/pages/medicine/medicine',
url: '/pagesA/pages/precept/precept',
})
},
@ -133,14 +151,6 @@ Page({
})
},
// 问答详情
bindTw(e) {
console.log(e);
const id = e.currentTarget.dataset.id
wx.navigateTo({
url: `/pagesB/pages/onlineAsk/onlineAsk?id=${id}`,
})
},
// 经验分享列表
viewexperience() {
@ -231,9 +241,9 @@ Page({
onSwiperTap(e) {
console.log(1111, e);
var id = e.currentTarget.dataset.value.carouselId
wx.navigateTo({
url: `/pagesA/pages/carouselDetail/carouselDetail?id=${id}`,
})
wx.navigateTo({
url: `/pagesA/pages/carouselDetail/carouselDetail?id=${id}`,
})
},

2
pages/home/home.wxml

@ -49,7 +49,7 @@
<view class="card">
<view class="card2" bind:tap="bindWsy">
<view>接受咨询</view>
<view class="card2_1">回复问诊单信息</view>
<view class="card2_1">一对一在线咨询</view>
<view class="card2_1">快捷回复</view>
</view>
<view class="card3">

BIN
pages/images/bj.png

After

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

366
pages/personal/personal.js

@ -1,66 +1,356 @@
// pages/personal/personal.js
Page({
import http from '../../utils/api'
const baseUrl = require('../../utils/baseUrl')
/**
* 页面的初始数据
*/
Page({
data: {
// 用户信息
avatarUrl: '', // 原始头像路径(相对路径)
avatarFullUrl: '', // 完整的头像URL(用于显示)
userInfo: {
user: {}
},
baseUrl: baseUrl,
displayNickName: '', // 显示用的昵称
},
// 弹窗状态
showFeedbackModal: false,
showNicknameModal: false,
showLogoutModal: false,
showToast: false,
// 反馈相关
feedbackContent: '',
canSubmit: false,
isSubmitting: false,
// 编辑相关
newNickname: '',
// 提示信息
toastText: '',
// 表单数据
formData: {
avatar: null,
nickName: null
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
// 上传状态
isUploadingAvatar: false,
isUpdatingNickname: false,
// 消息数字
totalToday: 0
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady() {
onLoad() {
this.gettoday()
},
/**
* 生命周期函数--监听页面显示
*/
onShow() {
console.log('个人中心页面显示,重新获取用户信息')
// 每次显示页面都从服务器获取最新数据
this.getUserInfo()
// 刷新消息数
this.gettoday()
},
// 获取用户信息
getUserInfo() {
http.UserInfo({
data: {},
success: (res) => {
console.log('获取用户信息成功', res)
// 更新数据
this.setData({
userInfo: res.data,
displayNickName: res.data.user.nickName,
avatarUrl: baseUrl + res.data.user.avatar
})
// 更新缓存
wx.setStorageSync('userInfo', res.data)
},
fail: (err) => {
console.error('获取用户信息失败:', err)
}
})
},
// 问诊消息 - 获取今日消息数
gettoday() {
http.today({
data: {},
success: res => {
if (res.rows && res.rows[0]) {
const num = res.rows[0].totalTodayReplyCount || 0
this.setData({
totalToday: num
})
}
},
fail: err => {
console.error('获取消息数失败', err)
}
})
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide() {
// 选择头像
onChooseAvatar(e) {
console.log(7788, e);
// 防止重复上传
if (this.data.isUploadingAvatar) {
this.showToast('正在上传中...')
return
}
const {
avatarUrl
} = e.detail
if (!avatarUrl) {
this.showToast('选择头像失败')
return
}
this.setData({
isUploadingAvatar: true
})
wx.showLoading({
title: '上传中...',
mask: true
})
// 上传头像到服务器
wx.uploadFile({
url: baseUrl + '/common/upload',
header: {
'Authorization': 'Bearer ' + wx.getStorageSync('token')
},
filePath: avatarUrl,
name: 'file',
success: (uploadRes) => {
const result = JSON.parse(uploadRes.data)
console.log('上传结果', result)
if (result && result.fileName) {
// 获取上传后的文件路径
const uploadedFilePath = result.fileName
this.setData({
avatarUrl: uploadedFilePath,
})
// 更新缓存中的用户信息
const cachedUserInfo = wx.getStorageSync('userInfo') || {}
if (!cachedUserInfo.user) {
cachedUserInfo.user = {}
}
cachedUserInfo.user.avatar = uploadedFilePath
wx.setStorageSync('userInfo', cachedUserInfo)
// 更新头像的API
http.revise({
data: {
avatar: uploadedFilePath
},
success: (res) => {
console.log('头像更新成功')
wx.hideLoading()
this.showToast('头像更新成功')
// 4. 重新获取用户信息以确保数据同步
setTimeout(() => {
this.getUserInfo()
}, 500)
},
fail: (err) => {
console.error('头像更新失败:', err)
wx.hideLoading()
this.showToast('头像保存失败,请重试')
}
})
} else {
throw new Error('上传失败:返回数据格式错误')
}
},
fail: (err) => {
wx.hideLoading()
console.error('上传失败:', err)
this.showToast('上传失败,请检查网络')
},
complete: () => {
this.setData({
isUploadingAvatar: false
})
}
})
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload() {
// 编辑昵称
editNickname() {
this.setData({
showNicknameModal: true,
newNickname: this.data.userInfo.user?.nickName || this.data.displayNickName || ''
})
},
hideNicknameModal() {
this.setData({
showNicknameModal: false
})
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh() {
onNicknameInput(e) {
this.setData({
newNickname: e.detail.value
})
},
saveNickname() {
const newNickname = this.data.newNickname.trim()
if (!newNickname) {
this.showToast('昵称不能为空')
return
}
if (newNickname.length > 10) {
this.showToast('昵称不能超过10个字符')
return
}
// 如果昵称没有变化,直接关闭弹窗
const currentNickName = this.data.userInfo.user?.nickName || this.data.displayNickName
if (newNickname === currentNickName) {
this.hideNicknameModal()
return
}
this.setData({
isUpdatingNickname: true
})
// 立即更新本地显示
this.setData({
'userInfo.user.nickName': newNickname,
displayNickName: newNickname,
'formData.nickName': newNickname
})
// 更新缓存
const cachedUserInfo = wx.getStorageSync('userInfo') || {}
if (!cachedUserInfo.user) {
cachedUserInfo.user = {}
}
cachedUserInfo.user.nickName = newNickname
wx.setStorageSync('userInfo', cachedUserInfo)
// 更新到服务器
http.revise({
data: {
nickName: newNickname
},
success: (res) => {
console.log('昵称更新成功')
this.setData({
showNicknameModal: false,
isUpdatingNickname: false,
'formData.nickName': null
})
this.showToast('昵称修改成功')
// 重新获取用户信息以确保数据同步
setTimeout(() => {
this.getUserInfo()
}, 500)
},
fail: (err) => {
console.error('昵称更新失败:', err)
this.setData({
isUpdatingNickname: false
})
this.showToast('修改失败,请重试')
}
})
},
// 查看问诊消息
goToConsultation() {
wx.navigateTo({
url: '/pagesA/pages/todayInquiry/todayInquiry'
})
},
// 查看问答消息
goToQA() {
wx.navigateTo({
url: '' // 请填写实际的问答消息页面路径
})
},
// 实名认证
goToAuth() {
if (this.data.userInfo.authStatus == '已认证') {
this.showToast('您已完成实名认证')
} else {
wx.navigateTo({
url: '/pagesA/pages/attestation/attestation'
})
}
},
// 退出登录相关
showLogoutConfirm() {
this.setData({
showLogoutModal: true
})
},
hideLogoutModal() {
this.setData({
showLogoutModal: false
})
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom() {
doLogout() {
// 清除本地存储
wx.clearStorageSync()
// 跳转到登录页
wx.reLaunch({
url: '/pages/login/login'
})
this.showToast('已退出登录')
},
/**
* 用户点击右上角分享
*/
onShareAppMessage() {
// 显示提示
showToast(text) {
this.setData({
toastText: text,
showToast: true
})
setTimeout(() => {
this.setData({
showToast: false
})
}, 2000)
},
// 下拉刷新
onPullDownRefresh() {
this.getUserInfo()
this.gettoday() // 刷新消息数
setTimeout(() => {
wx.stopPullDownRefresh()
this.showToast('刷新成功')
}, 1000)
},
}
})

1
pages/personal/personal.json

@ -1,3 +1,4 @@
{
"navigationBarTitleText":"个人中心",
"usingComponents": {}
}

136
pages/personal/personal.wxml

@ -1,2 +1,134 @@
<!--pages/personal/personal.wxml-->
<text>pages/personal/personal.wxml</text>
<view class="personal-center">
<!-- 用户信息区域 -->
<view class="user-section fade-in">
<view class="user-card">
<!-- 头像 -->
<button class="avatar-btn" open-type="chooseAvatar" bind:chooseavatar="onChooseAvatar">
<image class="avatar" src="{{avatarUrl || '/pages/images/tx.png'}}" mode="aspectFill"></image>
<view class="avatar-edit-tip">点击修改</view>
</button>
<!-- 用户信息 -->
<view class="user-info">
<view class="nickname-section" bindtap="editNickname">
<text class="nickname">{{displayNickName || '微信用户'}}</text>
<image src="/pages/images/bj.png"></image>
</view>
<view class="user-meta">
<view class="auth-tag {{userInfo.isVerified ? 'verified' : ''}}" bindtap="goToAuth">
<text>{{userInfo.authStatus}}</text>
</view>
</view>
</view>
</view>
</view>
<!-- 消息通知 -->
<!-- <view class="section-card fade-in-delay-1">
<view class="section-header">
<text class="section-title">消息通知</text>
</view>
<view class="message-row">
<view class="message-item" bindtap="goToConsultation">
<view class="message-icon-wrapper">
<image class="message-icon" src="/pages/images/wz.png" mode=""></image>
<view class="badge" wx:if="{{totalToday > 0}}">
<view class="badge-dot"></view>
<view class="badge-num animation-badge">{{totalToday}}</view>
</view>
</view>
<text class="message-label">问诊消息</text>
</view>
<view class="divider"></view>
<view class="message-item" bindtap="goToQA">
<view class="message-icon-wrapper">
<image class="message-icon" src="/pages/images/wdxx.png" mode=""></image>
</view>
<text class="message-label">问答消息</text>
</view>
</view>
</view> -->
<!-- 功能列表 -->
<view class="section-card fade-in-delay-2">
<view class="function-list">
<!-- 实名认证 -->
<view class="function-item" bindtap="goToAuth">
<view class="item-left">
<image class="item-icon" src="/pages/images/smrz.png"></image>
<text class="item-title">实名认证</text>
</view>
<view class="item-status {{userInfo.isVerified ? 'verified' : ''}}">
{{userInfo.authStatus}}
</view>
</view>
<!-- 反馈建议 -->
<view class="function-item" bindtap="showFeedback">
<view class="item-left">
<image class="item-icon" src="/pages/images/fkjy.png"></image>
<text class="item-title">服务评价</text>
</view>
</view>
<!-- 退出登录 -->
<view class="function-item" bindtap="showLogoutConfirm">
<view class="item-left">
<image class="item-icon" src="/pages/images/logout.png"></image>
<text class="item-title logout-title">退出登录</text>
</view>
</view>
</view>
</view>
<!-- 修改昵称弹窗 -->
<view class="nickname-modal {{showNicknameModal ? 'show' : ''}}">
<view class="modal-mask" bindtap="hideNicknameModal"></view>
<view class="modal-content">
<view class="modal-header">
<text class="modal-title">修改昵称</text>
</view>
<view class="modal-body">
<input
class="nickname-input"
type="text"
placeholder="请输入昵称"
value="{{newNickname}}"
bindinput="onNicknameInput"
maxlength="10"
focus="{{showNicknameModal}}"
confirm-type="done"
/>
<text class="input-tip">最多10个字符</text>
</view>
<view class="modal-footer">
<button class="cancel-btn" bindtap="hideNicknameModal">取消</button>
<button class="confirm-btn" bindtap="saveNickname">确定</button>
</view>
</view>
</view>
<!-- 退出登录确认弹窗 -->
<view class="logout-modal {{showLogoutModal ? 'show' : ''}}">
<view class="modal-mask" bindtap="hideLogoutModal"></view>
<view class="modal-content">
<view class="logout-modal-body">
<text class="logout-title">确认退出登录?</text>
<text class="logout-desc">退出后需要重新登录才能使用完整功能</text>
</view>
<view class="logout-modal-footer">
<button class="logout-cancel-btn" bindtap="hideLogoutModal">取消</button>
<button class="logout-confirm-btn" bindtap="doLogout">退出登录</button>
</view>
</view>
</view>
<!-- 提示信息 -->
<view class="toast {{showToast ? 'show' : ''}}">
<text>{{toastText}}</text>
</view>
</view>

612
pages/personal/personal.wxss

@ -1 +1,611 @@
/* pages/personal/personal.wxss */
.personal-center {
min-height: 100vh;
background: linear-gradient(180deg, #86D8D0 0%, #a9dfda 30%, #cfe9e7 60%, #ECF8F7 90%);
padding-bottom: 40rpx;
}
/* 淡入动画 */
.fade-in {
animation: fadeIn 0.6s ease forwards;
opacity: 0;
transform: translateY(20rpx);
}
.fade-in-delay-1 {
animation: fadeIn 0.6s 0.2s ease forwards;
opacity: 0;
transform: translateY(20rpx);
}
.fade-in-delay-2 {
animation: fadeIn 0.6s 0.4s ease forwards;
opacity: 0;
transform: translateY(20rpx);
}
@keyframes fadeIn {
to {
opacity: 1;
transform: translateY(0);
}
}
/* 用户信息区域 */
.user-section {
padding: 40rpx 30rpx 30rpx;
}
.user-card {
background: white;
border-radius: 24rpx;
padding: 40rpx 30rpx;
box-shadow: 0 10rpx 30rpx rgba(0, 0, 0, 0.08);
display: flex;
align-items: center;
transition: all 0.3s ease;
}
.user-card:active {
transform: scale(0.99);
box-shadow: 0 5rpx 20rpx rgba(0, 0, 0, 0.05);
}
.avatar-btn {
position: relative;
width: 140rpx;
height: 140rpx;
margin-right: 30rpx;
flex-shrink: 0;
padding: 0;
background: transparent;
border: none;
border-radius: 50%;
overflow: visible;
}
.avatar-btn::after {
border: none;
}
.avatar {
width: 100%;
height: 100%;
border-radius: 50%;
border: 4rpx solid white;
box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.1);
transition: all 0.3s ease;
}
.avatar-btn:active .avatar {
transform: scale(0.95);
}
.avatar-edit-tip {
position: absolute;
bottom: -10rpx;
left: 50%;
transform: translateX(-50%);
background: rgba(0, 0, 0, 0.7);
color: white;
font-size: 20rpx;
padding: 4rpx 12rpx;
border-radius: 20rpx;
white-space: nowrap;
opacity: 0;
transition: all 0.3s ease;
}
.avatar-btn:hover .avatar-edit-tip {
opacity: 1;
bottom: -20rpx;
}
.user-info {
flex: 1;
}
.nickname-section {
display: flex;
align-items: center;
margin-bottom: 16rpx;
padding: 8rpx 0;
border-radius: 8rpx;
transition: all 0.3s ease;
}
.nickname-section:active {
background: rgba(0, 0, 0, 0.05);
}
.nickname {
font-size: 36rpx;
font-weight: 700;
color: #1e293b;
margin-right: 20rpx;
max-width: 300rpx;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.nickname-section image {
width: 26rpx;
height: 26rpx;
opacity: 0.6;
transition: all 0.3s ease;
}
.nickname-section:active image {
opacity: 0.8;
transform: scale(1.1);
}
.user-meta {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 16rpx;
}
.auth-tag {
display: flex;
align-items: center;
padding: 6rpx 16rpx;
background: #fef3c7;
border-radius: 20rpx;
transition: all 0.3s ease;
}
.auth-tag.verified {
background: #d1fae5;
}
.auth-tag:active {
transform: scale(0.95);
opacity: 0.8;
}
.auth-tag text {
font-size: 24rpx;
color: #92400e;
font-weight: 500;
}
.auth-tag.verified text {
color: #065f46;
}
/* 卡片样式 */
.section-card {
background: white;
margin: 0 30rpx 30rpx;
border-radius: 20rpx;
overflow: hidden;
box-shadow: 0 6rpx 20rpx rgba(0, 0, 0, 0.05);
}
.section-header {
padding: 30rpx 30rpx 20rpx;
border-bottom: 1rpx solid #f1f5f9;
display: flex;
align-items: center;
justify-content: space-between;
}
.section-title {
font-size: 32rpx;
font-weight: 600;
color: #1e293b;
position: relative;
padding-left: 20rpx;
}
.section-title::before {
content: '';
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
width: 6rpx;
height: 24rpx;
background: linear-gradient(135deg, #667eea, #764ba2);
border-radius: 3rpx;
}
/* 消息行 */
.message-row {
display: flex;
align-items: center;
padding: 30rpx;
}
.message-item {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
transition: all 0.3s ease;
}
.message-item:active {
transform: scale(0.95);
opacity: 0.8;
}
.message-icon-wrapper {
position: relative;
width: 80rpx;
height: 80rpx;
margin-bottom: 16rpx;
display: flex;
align-items: center;
justify-content: center;
background: #f8fafc;
border-radius: 50%;
border: 1rpx solid #e2e8f0;
transition: all 0.3s ease;
}
.message-item:active .message-icon-wrapper {
background: #e2e8f0;
}
.message-icon {
width: 60rpx;
height: 60rpx;
}
/* 数字气泡 - 定位在顶部 */
.badge {
position: absolute;
top: -10rpx;
right: -10rpx;
display: flex;
align-items: center;
justify-content: center;
z-index: 2;
}
.badge-num {
background: linear-gradient(135deg, #ff6b6b, #ee5253);
border-radius: 36rpx;
color: white;
font-size: 22rpx;
font-weight: 600;
display: flex;
align-items: center;
justify-content: center;
padding: 0 10rpx;
box-shadow: 0 4rpx 8rpx rgba(238, 82, 83, 0.3);
}
/* 动画效果:跳动+呼吸 */
.animation-badge {
animation: badgeBounce 2.5s ease-in-out infinite;
}
@keyframes badgeBounce {
0%, 100% {
transform: scale(1);
}
30% {
transform: scale(1.2);
}
50% {
transform: scale(1.1);
}
70% {
transform: scale(1.15);
}
}
/* 可选的小红点效果(如果有需要可以保留,当前用的是数字) */
.badge-dot {
display: none; /* 隐藏,用数字替代 */
}
.message-label {
font-size: 26rpx;
color: #475569;
font-weight: 500;
}
.divider {
width: 1rpx;
height: 60rpx;
background: #e2e8f0;
margin: 0 40rpx;
}
/* 功能列表 */
.function-list {
padding: 0 30rpx 20rpx;
}
.function-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 30rpx 0;
border-bottom: 1rpx solid #f1f5f9;
transition: all 0.3s ease;
}
.function-item:last-child {
border-bottom: none;
}
.function-item:active {
background: #f8fafc;
border-radius: 12rpx;
}
.item-left {
display: flex;
align-items: center;
}
.item-icon {
width: 40rpx;
height: 40rpx;
margin-right: 20rpx;
}
.item-title {
font-size: 30rpx;
color: #1e293b;
font-weight: 500;
}
.logout-title {
color: #ef4444;
}
.item-status {
font-size: 26rpx;
color: #f59e0b;
font-weight: 500;
}
.item-status.verified {
color: #10b981;
}
/* 昵称修改弹窗 */
.nickname-modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1000;
display: flex;
align-items: center;
justify-content: center;
opacity: 0;
visibility: hidden;
transition: all 0.3s ease;
pointer-events: none;
}
.nickname-modal.show {
opacity: 1;
visibility: visible;
pointer-events: auto;
}
.nickname-modal .modal-content {
width: 600rpx;
background: white;
border-radius: 20rpx;
overflow: hidden;
box-shadow: 0 20rpx 60rpx rgba(0, 0, 0, 0.2);
transform: scale(0.8);
transition: all 0.3s ease;
}
.nickname-modal.show .modal-content {
transform: scale(1);
}
.nickname-modal .modal-header {
padding: 40rpx 40rpx 20rpx;
border-bottom: 1rpx solid #f1f5f9;
}
.nickname-modal .modal-body {
padding: 40rpx;
}
.nickname-input {
width: 100%;
height: 80rpx;
background: #f8fafc;
border: 2rpx solid #e2e8f0;
border-radius: 12rpx;
font-size: 28rpx;
color: #1e293b;
text-align: center;
transition: all 0.3s ease;
}
.nickname-input:focus {
border-color: #667eea;
background: white;
box-shadow: 0 0 0 4rpx rgba(102, 126, 234, 0.1);
}
.input-tip {
display: block;
font-size: 24rpx;
color: #94a3b8;
text-align: center;
margin-top: 16rpx;
}
.nickname-modal .modal-footer {
padding: 0 40rpx 40rpx;
display: flex;
gap: 20rpx;
}
.cancel-btn,
.confirm-btn {
flex: 1;
padding: 5rpx 0;
border-radius: 40rpx;
font-size: 28rpx;
font-weight: 500;
transition: all 0.3s ease;
border: none;
}
.cancel-btn {
background: #f1f5f9;
color: #64748b;
}
.cancel-btn::after{
border: none;
}
.cancel-btn:active {
background: #e2e8f0;
}
.confirm-btn {
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
box-shadow: 0 4rpx 16rpx rgba(102, 126, 234, 0.3);
}
.confirm-btn:active {
transform: scale(0.98);
opacity: 0.9;
}
/* 退出登录确认弹窗 */
.logout-modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1000;
display: flex;
align-items: center;
justify-content: center;
opacity: 0;
visibility: hidden;
transition: all 0.3s ease;
pointer-events: none;
}
.logout-modal.show {
opacity: 1;
visibility: visible;
pointer-events: auto;
}
.logout-modal .modal-content {
width: 600rpx;
background: white;
border-radius: 24rpx;
overflow: hidden;
box-shadow: 0 20rpx 60rpx rgba(0, 0, 0, 0.2);
transform: scale(0.8);
transition: all 0.3s ease;
}
.logout-modal.show .modal-content {
transform: scale(1);
}
.logout-modal-body {
padding: 60rpx 40rpx 40rpx;
text-align: center;
}
.logout-title {
font-weight: 700;
color: #1e293b;
display: block;
}
.logout-desc {
font-size: 28rpx;
color: #64748b;
line-height: 1.5;
}
.logout-modal-footer {
padding: 0 40rpx 40rpx;
display: flex;
gap: 20rpx;
}
.logout-cancel-btn,
.logout-confirm-btn {
flex: 1;
border-radius: 44rpx;
font-size: 30rpx;
font-weight: 600;
transition: all 0.3s ease;
border: none;
}
.logout-cancel-btn::after{
border: none;
}
.logout-cancel-btn {
background: #f1f5f9;
color: #64748b;
}
.logout-cancel-btn:active {
background: #e2e8f0;
transform: scale(0.98);
}
.logout-confirm-btn {
background: linear-gradient(135deg, #ef4444, #dc2626);
color: white;
box-shadow: 0 8rpx 24rpx rgba(239, 68, 68, 0.3);
}
.logout-confirm-btn:active {
transform: scale(0.98);
opacity: 0.9;
}
/* 提示信息 */
.toast {
position: fixed;
top: 150rpx;
left: 50%;
transform: translateX(-50%) translateY(-100rpx);
background: rgba(30, 41, 59, 0.95);
backdrop-filter: blur(20rpx);
color: white;
padding: 24rpx 48rpx;
border-radius: 16rpx;
font-size: 28rpx;
font-weight: 500;
opacity: 0;
visibility: hidden;
transition: all 0.3s ease;
z-index: 1001;
box-shadow: 0 10rpx 40rpx rgba(0, 0, 0, 0.2);
max-width: 80%;
text-align: center;
white-space: nowrap;
}
.toast.show {
opacity: 1;
visibility: visible;
transform: translateX(-50%) translateY(0);
}

BIN
pagesA/images/ch.png

After

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

159
pagesA/pages/advisory/advisory.js

@ -0,0 +1,159 @@
Page({
data: {
// 专家信息(新增在线状态)
expertInfo: {
avatar: 'https://api.dicebear.com/7.x/avataaars/png?seed=expert_pro&size=140',
name: '张振农',
specialty: '作物栽培 · 土壤改良',
experience: 15,
online: true // 在线状态
},
// 聊天申请列表数据
applyList: [],
// 分页相关
page: 1,
pageSize: 10,
hasMore: true,
total: 0,
},
onLoad() {
this.loadApplyList(1);
},
// 加载申请列表(增强模拟数据)
loadApplyList(page) {
const { pageSize } = this.data;
wx.showLoading({ title: '加载中...', mask: true });
setTimeout(() => {
const mockData = this.generateMockData(page, pageSize);
if (page === 1) {
this.setData({
applyList: mockData.list,
total: mockData.total,
hasMore: mockData.hasMore,
page: page
});
} else {
this.setData({
applyList: this.data.applyList.concat(mockData.list),
total: mockData.total,
hasMore: mockData.hasMore,
page: page
});
}
wx.hideLoading();
}, 600);
},
// 生成更丰富的模拟数据(含标签)
generateMockData(page, pageSize) {
const total = 23;
const start = (page - 1) * pageSize;
const end = start + pageSize;
const hasMore = end < total;
const list = [];
const seeds = ['farmer1', 'sheep', 'cattle', 'wheat', 'tractor', 'green', 'sun', 'rain', 'soil', 'seed'];
const firstNames = ['李', '王', '张', '刘', '赵', '陈', '杨', '周', '吴', '黄'];
const farmTypes = ['水稻种植', '牛羊养殖', '果树栽培', '蔬菜大棚', '有机农场', '蜂蜜养殖', '食用菌', '药材种植'];
const tagPool = [
['玉米', '虫害'],
['羊羔', '腹泻'],
['施肥', '建议'],
['土壤', '检测'],
['大棚', '温度'],
['猪病', '防治'],
['果树', '修剪'],
['蜂蜜', '取蜜']
];
for (let i = start; i < Math.min(end, total); i++) {
const id = `apply_${i + 1}`;
const userId = `user_${(i % 10) + 1}`;
const seedIndex = i % seeds.length;
const nameIndex = i % firstNames.length;
const typeIndex = (i * 3) % farmTypes.length;
const unread = i % 4 === 0 ? 2 : (i % 7 === 0 ? 1 : 0);
const statuses = ['pending', 'accepted', 'completed'];
const status = statuses[i % 3];
// 随机标签
const tags = tagPool[(i * 2) % tagPool.length];
const time = new Date(Date.now() - (i * 7200000) - Math.floor(Math.random() * 5000000));
const timeStr = `${time.getMonth()+1}.${time.getDate()} ${time.getHours()}:${time.getMinutes().toString().padStart(2,'0')}`;
// 更真实的预览消息
let lastMessage = '';
if (i % 5 === 0) lastMessage = '专家您好,我家玉米出现黄叶,能帮看看吗?';
else if (i % 3 === 0) lastMessage = '咨询一下羊羔腹泻的问题,急!';
else if (i % 4 === 0) lastMessage = '请问什么时候方便通话?';
else lastMessage = '请求添加您为咨询顾问';
list.push({
id: id,
user: {
id: userId,
name: firstNames[nameIndex] + (i % 2 === 0 ? '建国' : '秀英') + (i + 1),
avatar: `https://api.dicebear.com/7.x/avataaars/png?seed=${seeds[seedIndex]}_${i}&size=96`,
farmType: farmTypes[typeIndex],
},
applyTime: timeStr,
lastMessage: lastMessage,
unreadCount: unread,
status: status,
tags: tags,
});
}
return {
list,
total,
hasMore,
};
},
// 处理点击申请项
handleApply(e) {
const { id, user } = e.currentTarget.dataset;
wx.showActionSheet({
itemList: ['接受并回复', '标记为已读', '稍后处理'],
success: (res) => {
if (res.tapIndex === 0) {
wx.showToast({ title: '已接受申请', icon: 'success' });
// 模拟更新状态
const newList = this.data.applyList.map(item =>
item.id === id ? {...item, status: 'accepted'} : item
);
this.setData({ applyList: newList });
} else if (res.tapIndex === 1) {
const newList = this.data.applyList.map(item =>
item.id === id ? {...item, unreadCount: 0} : item
);
this.setData({ applyList: newList });
wx.showToast({ title: '已标记已读', icon: 'none' });
} else {
wx.showToast({ title: '已稍后处理', icon: 'none' });
}
}
});
},
loadMore() {
const { hasMore, page } = this.data;
if (hasMore) {
this.loadApplyList(page + 1);
}
},
onPullDownRefresh() {
this.setData({ page: 1, hasMore: true });
this.loadApplyList(1);
wx.stopPullDownRefresh();
}
});

4
pagesA/pages/advisory/advisory.json

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

62
pagesA/pages/advisory/advisory.wxml

@ -0,0 +1,62 @@
<view class="container-box">
<!-- 头部:专家信息卡片 -->
<view class="expert-card">
<view class="avatar-wrapper">
<image class="avatar" src="{{expertInfo.avatar}}" mode="aspectFill"></image>
<view class="online-status {{expertInfo.online ? 'online' : 'offline'}}"></view>
</view>
<view class="info">
<view class="name-row">
<text class="name">{{expertInfo.name}}</text>
<text class="online-text">{{expertInfo.online ? '在线' : '离线'}}</text>
</view>
<view class="specialty">{{expertInfo.specialty}}</view>
<view class="experience-tag">
<text class="tag-text">从业 {{expertInfo.experience}} 年</text>
</view>
</view>
</view>
<!-- 聊天申请列表区域 -->
<view class="list-header">
<view class="list-title-wrap">
<text class="list-title">咨询申请</text>
<text class="list-count">{{applyList.length}}</text>
</view>
</view>
<scroll-view scroll-y class="apply-scroll" bindscrolltolower="loadMore" enhanced show-scrollbar="{{false}}">
<view class="apply-list">
<block wx:for="{{applyList}}" wx:key="id">
<view class="apply-item" bindtap="handleApply" data-id="{{item.id}}" data-user="{{item.user}}">
<image class="user-avatar" src="{{item.user.avatar}}" mode="aspectFill"></image>
<view class="apply-content">
<view class="apply-header">
<view class="user-info">
<text class="user-name">{{item.user.name}}</text>
</view>
<text class="apply-time">{{item.applyTime}}</text>
</view>
<view class="message-area">
<text class="message-preview">{{item.lastMessage || '请求咨询...'}}</text>
<view wx:if="{{item.unreadCount > 0}}" class="unread-badge">{{item.unreadCount > 99 ? '99+' : item.unreadCount}}</view>
</view>
</view>
</view>
</block>
<!-- 空状态提示 -->
<view wx:if="{{applyList.length === 0}}" class="empty-state">
<text class="empty-text">暂无新的咨询申请</text>
<text class="empty-subtext">稍后刷新试试</text>
</view>
<!-- 加载更多指示 -->
<view wx:if="{{hasMore && applyList.length > 0}}" class="loading-more">
<view class="loading-spinner"></view>
<text>正在加载更多...</text>
</view>
<view wx:if="{{!hasMore && applyList.length > 0}}" class="no-more">没有更多了</view>
</view>
</scroll-view>
</view>

360
pagesA/pages/advisory/advisory.wxss

@ -0,0 +1,360 @@
.container-box {
display: flex;
flex-direction: column;
height: 100vh;
overflow: hidden;
}
/* 专家卡片*/
.expert-card {
background-image: linear-gradient(to top, #48c6ef 0%, #6f86d6 100%);
padding: 36rpx 32rpx;
display: flex;
align-items: center;
color: white;
box-shadow: 0 8rpx 24rpx rgba(0, 30, 10, 0.25);
flex-shrink: 0;
position: relative;
}
.avatar-wrapper {
position: relative;
margin-right: 28rpx;
flex-shrink: 0;
}
.avatar {
width: 140rpx;
height: 140rpx;
border-radius: 70rpx;
border: 4rpx solid rgba(255, 255, 255, 0.25);
background-color: #e6f0ea;
box-shadow: 0 6rpx 16rpx rgba(0, 0, 0, 0.15);
transition: transform 0.2s;
}
.avatar:active {
transform: scale(0.98);
}
.online-status {
position: absolute;
bottom: 6rpx;
right: 6rpx;
width: 28rpx;
height: 28rpx;
border-radius: 14rpx;
border: 4rpx solid #1a4b2e;
background-color: #a0a0a0;
transition: background-color 0.2s;
}
.online-status.online {
background-color: #4caf50;
box-shadow: 0 0 0 2rpx rgba(76, 175, 80, 0.3);
}
.online-status.offline {
background-color: #9e9e9e;
}
.info {
flex: 1;
display: flex;
flex-direction: column;
min-width: 0;
}
.name-row {
display: flex;
align-items: center;
margin-bottom: 12rpx;
flex-wrap: wrap;
gap: 16rpx;
}
.name {
font-size: 44rpx;
font-weight: 600;
line-height: 1.2;
letter-spacing: 1rpx;
text-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.2);
}
.online-text {
font-size: 26rpx;
background-color: rgba(255, 255, 255, 0.2);
padding: 3rpx 20rpx;
border-radius: 30rpx;
backdrop-filter: blur(4px);
border: 1rpx solid rgba(255, 255, 255, 0.15);
font-weight: 400;
color: #ffecb3;
}
.specialty {
font-size: 26rpx;
opacity: 0.95;
margin-bottom: 16rpx;
display: flex;
align-items: center;
background-color: rgba(255, 255, 255, 0.1);
padding: 8rpx 20rpx;
border-radius: 40rpx;
width: fit-content;
backdrop-filter: blur(4px);
border: 1rpx solid rgba(255, 255, 255, 0.1);
}
.experience-tag {
display: flex;
align-items: center;
background-color: rgba(255, 255, 255, 0.1);
padding: 8rpx 22rpx 8rpx 18rpx;
border-radius: 60rpx;
width: fit-content;
}
.tag-text {
font-size: 26rpx;
font-weight: 400;
}
/* 列表头部 */
.list-header {
padding: 28rpx 32rpx 20rpx;
display: flex;
justify-content: space-between;
align-items: center;
background-color: transparent;
flex-shrink: 0;
}
.list-title-wrap {
display: flex;
align-items: baseline;
gap: 12rpx;
}
.list-title {
font-size: 34rpx;
font-weight: 600;
color: #1f2d3d;
letter-spacing: 0.5rpx;
}
.list-count {
font-size: 28rpx;
color: #5f6b7a;
background-color: #e9ecf0;
padding: 4rpx 16rpx;
border-radius: 30rpx;
font-weight: 500;
}
/* 滚动区域 */
.apply-scroll {
flex: 1;
overflow-y: auto;
padding: 0 32rpx;
background-color: transparent;
box-sizing: border-box;
}
.apply-list {
padding: 8rpx 0 30rpx;
}
/* 申请项卡片 */
.apply-item {
background-color: #ffffff;
border-radius: 32rpx;
padding: 32rpx 28rpx;
margin-bottom: 20rpx;
display: flex;
align-items: flex-start;
box-shadow: 0 8rpx 24rpx rgba(0, 20, 10, 0.04);
transition: all 0.25s ease;
border: 1rpx solid #edf2f7;
position: relative;
backdrop-filter: blur(2px);
}
.apply-item:active {
background-color: #fafdff;
transform: translateY(-4rpx);
box-shadow: 0 16rpx 32rpx rgba(30, 60, 40, 0.08);
border-color: #cbd5e0;
}
.user-avatar {
width: 96rpx;
height: 96rpx;
border-radius: 10rpx;
background-color: #dde5ed;
margin-right: 28rpx;
flex-shrink: 0;
border: 2rpx solid #e2e8f0;
transition: border-color 0.2s;
}
.apply-item:active .user-avatar {
border-color: #2b6c4e;
}
.apply-content {
flex: 1;
min-width: 0;
}
.apply-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16rpx;
}
.user-info {
display: flex;
align-items: center;
gap: 16rpx;
flex-wrap: wrap;
flex: 1;
min-width: 0;
}
.user-name {
font-size: 34rpx;
font-weight: 600;
color: #1e293b;
max-width: 240rpx;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.apply-time {
font-size: 24rpx;
color: #94a3b8;
flex-shrink: 0;
margin-left: 16rpx;
background-color: #f8fafc;
padding: 4rpx 16rpx;
border-radius: 30rpx;
}
.message-area {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 22rpx;
}
.message-preview {
font-size: 28rpx;
color: #475569;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
flex: 1;
min-width: 0;
margin-right: 16rpx;
line-height: 1.4;
}
.unread-badge {
background: linear-gradient(145deg, #ff5f6d, #ff7b89);
color: white;
font-size: 22rpx;
font-weight: 600;
min-width: 40rpx;
height: 40rpx;
line-height: 40rpx;
text-align: center;
border-radius: 40rpx;
padding: 0 12rpx;
flex-shrink: 0;
box-shadow: 0 4rpx 8rpx rgba(255, 95, 109, 0.25);
border: 2rpx solid rgba(255, 255, 255, 0.5);
}
/* 空状态 */
.empty-state {
text-align: center;
padding: 80rpx 0;
color: #94a3b8;
display: flex;
flex-direction: column;
align-items: center;
}
.empty-text {
font-size: 32rpx;
font-weight: 500;
color: #64748b;
margin-bottom: 8rpx;
}
.empty-subtext {
font-size: 26rpx;
color: #a0afbe;
}
/* 加载更多 */
.loading-more {
text-align: center;
padding: 30rpx 0 40rpx;
color: #5f7d9c;
font-size: 26rpx;
display: flex;
align-items: center;
justify-content: center;
gap: 16rpx;
}
.loading-spinner {
width: 32rpx;
height: 32rpx;
border: 4rpx solid #d1d9e6;
border-top-color: #2b6c4e;
border-radius: 50%;
animation: spin 0.8s linear infinite;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
.no-more {
text-align: center;
padding: 30rpx 0;
color: #9aa9b9;
font-size: 26rpx;
position: relative;
}
.no-more::before,
.no-more::after {
content: '';
position: absolute;
top: 50%;
width: 60rpx;
height: 1rpx;
background-color: #dce3ec;
}
.no-more::before {
left: 60rpx;
}
.no-more::after {
right: 60rpx;
}

66
pagesA/pages/precept/precept.js

@ -0,0 +1,66 @@
import http from '../../../utils/api'
Page({
data: {
diagnosisList: []
},
onLoad: function () {
},
onShow:function(){
this.getwzd()
},
// 问诊单
getwzd() {
http.wzd({
data: {},
success: res => {
console.log(1111, res);
this.setData({
diagnosisList: res.rows
})
}
})
},
// 格式化日期显示
formatDate: function (dateString) {
const date = new Date(dateString);
const now = new Date();
const diff = now - date;
const diffDays = Math.floor(diff / (1000 * 60 * 60 * 24));
const diffHours = Math.floor(diff / (1000 * 60 * 60));
const diffMinutes = Math.floor(diff / (1000 * 60));
if (diffMinutes < 60) {
return `${diffMinutes}分钟前`;
} else if (diffHours < 24) {
return `${diffHours}小时前`;
} else if (diffDays === 1) {
return '昨天';
} else if (diffDays === 2) {
return '前天';
} else if (diffDays < 7) {
return `${diffDays}天前`;
} else {
const month = date.getMonth() + 1;
const day = date.getDate();
return `${month}${day}`;
}
},
// 查看详情
viewDetail: function (e) {
const data = e.currentTarget.dataset.value
wx.navigateTo({
url: `/pagesA/pages/askingSyDetails/askingSyDetails?data=${encodeURIComponent(JSON.stringify(data))}`,
});
},
});

3
pagesA/pages/precept/precept.json

@ -0,0 +1,3 @@
{
"usingComponents": {}
}

95
pagesA/pages/precept/precept.wxml

@ -0,0 +1,95 @@
<view class="wzbox">
<!-- 顶部标题栏 -->
<view class="header">
<view class="header-content">
<view class="title-section">
<text class="title">远程诊疗</text>
<text class="subtitle">专业兽医在线解答</text>
</view>
</view>
<view class="header-decoration">
<view class="decoration-circle circle-1"></view>
<view class="decoration-circle circle-2"></view>
</view>
</view>
<!-- 问诊记录列表 -->
<scroll-view class="record-list" scroll-y enable-back-to-top>
<!-- 空状态 -->
<view wx:if="{{diagnosisList.length === 0}}" class="empty-state">
<image class="empty-icon" src="/pagesA/images/kzt.png" mode="widthFix"></image>
<text class="empty-text">暂无问诊记录</text>
<text class="empty-tip">开始您的第一次问诊吧</text>
</view>
<!-- 问诊记录卡片 -->
<view wx:else class="records-container">
<view wx:for="{{diagnosisList}}" wx:key="id" class="record-card" bindtap="viewDetail" data-value="{{item}}">
<!-- 卡片头部:用户信息 + 状态 -->
<view class="card-header">
<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 class="livestock-title-line"></view>
</view>
<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 class="symptom-section">
<view class="symptom-content">
<text class="symptom-text">{{item.description}}</text>
</view>
</view>
<!-- 卡片底部 -->
<view class="card-footer">
<view class="footer-left">
<view class="time-info">
<text class="time-text">{{item.createdTime}}</text>
</view>
</view>
<view class="footer-right">
<view class="reply-info">
<text class="reply-count">制定方案</text>
</view>
</view>
</view>
</view>
</view>
<!-- 列表底部提示 -->
<view wx:if="{{diagnosisList.length > 0}}" class="list-footer">
<text class="footer-text">已显示全部记录</text>
</view>
</scroll-view>
</view>

428
pagesA/pages/precept/precept.wxss

@ -0,0 +1,428 @@
/* 全局样式 */
.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: 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);
}
50% {
transform: scale(1.1) rotate(5deg);
}
}
.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;
}
/* 卡片入场动画 */
@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 {
max-width: 600rpx;
margin-left: auto;
margin-right: auto;
}
}

191
pagesA/pages/releaseSuffer/releaseSuffer.js

@ -0,0 +1,191 @@
import http from '../../../utils/api';
const baseUrl = require('../../../utils/baseUrl');
Page({
data: {
baseUrl: baseUrl,
// 表单数据
formData: {
title: '',
summary: '',
categoryName: '',
tags: '', // 改为字符串格式,如 "传染病,疫苗"
content: ''
},
// 分类列表 - 直接从接口返回的 rows 赋值
categoryList: [],
categoryIndex: -1,
// 标签列表 - 直接从接口返回的 rows 赋值
tagList: [],
// 已选中的标签对象数组
selectedTags: [],
// 提交状态
submitting: false
},
onLoad(options) {
this.getCategoryList();
this.getTagList();
},
// 输入处理
onInput(e) {
const { field } = e.currentTarget.dataset;
const { value } = e.detail;
this.setData({
[`formData.${field}`]: value
});
},
// 获取文章分类 - 直接使用 res.rows
getCategoryList() {
http.articleZd({
data: {
dictType: 'vet_experience_category'
},
success: (res) => {
// 直接使用 res.rows 赋值
if (res.rows && Array.isArray(res.rows)) {
this.setData({ categoryList: res.rows });
} else {
console.error('分类数据格式错误', res);
wx.showToast({ title: '分类加载失败', icon: 'none' });
}
},
fail: (err) => {
console.error('分类接口异常', err);
wx.showToast({ title: '网络错误', icon: 'none' });
}
});
},
// 获取文章标签 - 直接使用 res.rows
getTagList() {
http.videoZd({
data: {
dictType: 'vet_experience_tag'
},
success: (res) => {
// 直接使用 res.rows 赋值
if (res.rows && Array.isArray(res.rows)) {
this.setData({ tagList: res.rows });
} else {
console.error('标签数据格式错误', res);
wx.showToast({ title: '标签加载失败', icon: 'none' });
}
},
fail: (err) => {
console.error('标签接口异常', err);
wx.showToast({ title: '网络错误', icon: 'none' });
}
});
},
// 分类选择
onCategoryChange(e) {
const index = parseInt(e.detail.value, 10);
const selectedCategory = this.data.categoryList[index];
this.setData({
categoryIndex: index,
[`formData.categoryName`]: selectedCategory.dictLabel
});
},
// 标签点击切换
toggleTag(e) {
const tagItem = e.currentTarget.dataset.item;
let selectedTags = [...this.data.selectedTags];
const index = selectedTags.findIndex(t => t.dictValue === tagItem.dictValue);
if (index > -1) {
selectedTags.splice(index, 1);
} else {
selectedTags.push(tagItem);
}
// 生成逗号分隔的标签字符串,如 "传染病,疫苗"
const tagString = selectedTags.map(item => item.dictLabel).join(',');
this.setData({
selectedTags: selectedTags,
[`formData.tags`]: tagString
});
},
// 表单提交
formSubmit(e) {
const { title, categoryName, content } = this.data.formData;
// 表单验证
if (!title || title.trim() === '') {
this.showError('请填写文章标题');
return;
}
if (!categoryName) {
this.showError('请选择文章分类');
return;
}
if (this.data.selectedTags.length === 0) {
this.showError('请至少选择一个标签');
return;
}
if (!content || content.trim() === '') {
this.showError('请填写文章内容');
return;
}
this.setData({ submitting: true });
// 构建提交数据 - tags 已经是字符串格式
const postData = {
title: this.data.formData.title,
summary: this.data.formData.summary || '',
categoryName: this.data.formData.categoryName,
tags: this.data.formData.tags, // 已经是 "传染病,疫苗" 格式
content: this.data.formData.content
};
console.log('提交数据:', postData); // 调试用
http.shareAdd({
data: postData,
success: (res) => {
if(res.code == 200){
wx.showToast({
title: '发布成功',
icon: 'success',
duration: 2000,
success: () => {
setTimeout(() => {
wx.navigateBack();
}, 1500);
}
})
}else{
console.error('发布失败', err);
this.showError('发布失败,请重试');
this.setData({ submitting: false });
}
},
fail: (err) => {
console.error('发布失败', err);
this.showError('发布失败,请重试');
this.setData({ submitting: false });
},
complete: () => {
setTimeout(() => {
this.setData({ submitting: false });
}, 3000);
}
});
},
// 错误提示
showError(msg) {
wx.showToast({
title: msg,
icon: 'none',
duration: 2000
});
}
});

4
pagesA/pages/releaseSuffer/releaseSuffer.json

@ -0,0 +1,4 @@
{
"navigationBarTitleText":"发布文章",
"usingComponents": {}
}

109
pagesA/pages/releaseSuffer/releaseSuffer.wxml

@ -0,0 +1,109 @@
<view class="page-wrapper">
<form catchsubmit="formSubmit">
<!-- 标题卡片-->
<view class="form-card">
<view class="card-header">
<text class="header-icon">📝</text>
<text class="header-title">文章标题</text>
<text class="required-badge">必填</text>
</view>
<view class="card-content no-padding">
<view class="input-wrapper">
<input type="text" placeholder="请输入吸引人的标题(最多50字)"
name="title" maxlength="50" value="{{formData.title}}"
bindinput="onInput" data-field="title"
placeholder-class="placeholder-style" />
</view>
</view>
</view>
<!-- 摘要卡片 -->
<view class="form-card">
<view class="card-header">
<text class="header-icon">📋</text>
<text class="header-title">文章摘要</text>
<text class="optional-badge">选填</text>
</view>
<view class="card-content no-padding">
<view class="textarea-wrapper">
<textarea placeholder="简单描述文章要点,让读者快速了解内容(最多200字)"
name="summary" maxlength="200" auto-height
value="{{formData.summary}}" bindinput="onInput"
data-field="summary" placeholder-class="placeholder-style" />
</view>
</view>
</view>
<!-- 分类卡片 -->
<view class="form-card">
<view class="card-header">
<text class="header-icon">📂</text>
<text class="header-title">文章分类</text>
<text class="required-badge">必填</text>
</view>
<view class="card-content">
<picker mode="selector" range="{{categoryList}}" range-key="dictLabel"
bindchange="onCategoryChange" value="{{categoryIndex}}">
<view class="picker-trigger {{categoryIndex > -1 ? 'selected' : ''}}">
<text>{{categoryIndex > -1 ? categoryList[categoryIndex].dictLabel : '请选择文章分类'}}</text>
<text class="picker-arrow">▼</text>
</view>
</picker>
</view>
</view>
<!-- 标签卡片 -->
<view class="form-card">
<view class="card-header">
<text class="header-icon">🏷️</text>
<text class="header-title">文章标签</text>
<text class="required-badge">必填</text>
</view>
<view class="card-content">
<view class="tag-group">
<block wx:for="{{tagList}}" wx:key="dictValue">
<view class="tag-item {{selectedTags.includes(item) ? 'active' : ''}}"
bindtap="toggleTag" data-item="{{item}}">
{{item.dictLabel}}
</view>
</block>
</view>
<view class="selected-tags" wx:if="{{selectedTags.length}}">
<text class="selected-label">已选标签:</text>
<view class="selected-tag-list">
<view class="selected-tag-item" wx:for="{{selectedTags}}" wx:key="index">
{{item.dictLabel}}
</view>
</view>
</view>
</view>
</view>
<!-- 内容卡片 -->
<view class="form-card content-card">
<view class="card-header">
<text class="header-icon">📄</text>
<text class="header-title">文章内容</text>
<text class="required-badge">必填</text>
</view>
<view class="card-content no-padding">
<view class="textarea-wrapper">
<textarea placeholder="开始撰写您的文章吧...(支持最多5000字)"
name="content" maxlength="5000" auto-height
value="{{formData.content}}" bindinput="onInput"
data-field="content" placeholder-class="placeholder-style"
class="content-textarea" />
</view>
</view>
</view>
<!-- 提交按钮区域 -->
<view class="submit-area">
<button form-type="submit" class="submit-btn" loading="{{submitting}}" disabled="{{submitting}}">
<text wx:if="{{!submitting}}">✨ 发布文章</text>
<text wx:else>发布中...</text>
</button>
<view class="tip-text">好的内容值得被更多人看见</view>
</view>
</form>
</view>

278
pagesA/pages/releaseSuffer/releaseSuffer.wxss

@ -0,0 +1,278 @@
.page-wrapper {
min-height: 100vh;
background: linear-gradient(135deg, #f5f7fa 0%, #e9edf5 100%);
padding: 30rpx;
box-sizing: border-box;
}
/* 卡片样式 */
.form-card {
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(10px);
border-radius: 32rpx;
margin-bottom: 30rpx;
box-shadow: 0 20rpx 40rpx rgba(0, 0, 0, 0.05), 0 4rpx 12rpx rgba(0, 0, 0, 0.03);
overflow: hidden;
border: 2rpx solid rgba(255, 255, 255, 0.8);
transition: all 0.3s ease;
}
/* 卡片头部 */
.card-header {
padding: 28rpx 32rpx;
background: linear-gradient(90deg, #ffffff, #fafcff);
border-bottom: 2rpx solid rgba(7, 193, 96, 0.1);
display: flex;
align-items: center;
gap: 12rpx;
margin-bottom: 20rpx;
}
.header-icon {
font-size: 36rpx;
line-height: 1;
filter: drop-shadow(0 4rpx 6rpx rgba(7, 193, 96, 0.2));
}
.header-title {
font-size: 32rpx;
font-weight: 600;
color: #1a2b3c;
letter-spacing: 1rpx;
}
/* 徽章样式 */
.required-badge {
background: linear-gradient(135deg, #ff6b6b, #ff4757);
color: white;
font-size: 22rpx;
padding: 4rpx 16rpx;
border-radius: 30rpx;
margin-left: 16rpx;
box-shadow: 0 4rpx 10rpx rgba(255, 71, 87, 0.3);
}
.optional-badge {
background: linear-gradient(135deg, #a0a0a0, #808080);
color: white;
font-size: 22rpx;
padding: 4rpx 16rpx;
border-radius: 30rpx;
margin-left: 16rpx;
opacity: 0.8;
}
/* 卡片内容 */
.card-content {
padding: 0 32rpx 32rpx 32rpx;
}
.card-content.no-padding {
padding: 0;
}
/* 输入框包装器 */
.input-wrapper,
.textarea-wrapper {
background: #f8fafd;
border-radius: 24rpx;
margin: 0 32rpx 32rpx 32rpx;
border: 2rpx solid transparent;
transition: all 0.3s ease;
box-shadow: inset 0 2rpx 6rpx rgba(0, 0, 0, 0.02);
overflow: hidden;
}
.input-wrapper:focus-within,
.textarea-wrapper:focus-within {
border-color: #07c160;
background: #ffffff;
box-shadow: 0 0 0 6rpx rgba(7, 193, 96, 0.1), inset 0 2rpx 6rpx rgba(0, 0, 0, 0.02);
}
/* 输入框样式 */
input, textarea {
width: 100%;
font-size: 30rpx;
color: #1e293b;
padding: 24rpx 28rpx;
background: transparent;
border: none;
outline: none;
}
textarea {
min-height: 120rpx;
line-height: 1.6;
}
.placeholder-style {
color: #aab8c5;
font-size: 28rpx;
}
/* 分类选择器 */
.picker-trigger {
background: #f8fafd;
padding: 24rpx 28rpx;
border-radius: 24rpx;
font-size: 30rpx;
color: #aab8c5;
display: flex;
justify-content: space-between;
align-items: center;
border: 2rpx solid transparent;
transition: all 0.3s ease;
box-shadow: inset 0 2rpx 6rpx rgba(0, 0, 0, 0.02);
}
.picker-trigger.selected {
color: #1e293b;
border-color: #07c160;
background: #ffffff;
}
.picker-arrow {
font-size: 28rpx;
color: #94a3b8;
transition: transform 0.3s ease;
}
/* 标签组 */
.tag-group {
display: flex;
flex-wrap: wrap;
gap: 20rpx;
margin-bottom: 24rpx;
}
.tag-item {
padding: 16rpx 36rpx;
background: #f1f5f9;
border-radius: 60rpx;
font-size: 28rpx;
color: #475569;
border: 2rpx solid #e2e8f0;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
box-shadow: 0 4rpx 10rpx rgba(0, 0, 0, 0.02);
}
.tag-item.active {
background: linear-gradient(135deg, #07c160, #059669);
color: white;
border-color: #07c160;
transform: translateY(-2rpx);
box-shadow: 0 10rpx 20rpx rgba(7, 193, 96, 0.3);
}
/* 已选标签区域 */
.selected-tags {
margin-top: 24rpx;
padding: 24rpx;
background: linear-gradient(135deg, #f0fdf4, #dcfce7);
border-radius: 24rpx;
border: 2rpx solid rgba(7, 193, 96, 0.2);
}
.selected-label {
font-size: 26rpx;
color: #059669;
font-weight: 500;
display: block;
margin-bottom: 16rpx;
}
.selected-tag-list {
display: flex;
flex-wrap: wrap;
gap: 16rpx;
margin-bottom: 16rpx;
}
.selected-tag-item {
padding: 10rpx 28rpx;
background: white;
border-radius: 40rpx;
font-size: 26rpx;
color: #07c160;
border: 2rpx solid #07c160;
box-shadow: 0 4rpx 10rpx rgba(7, 193, 96, 0.1);
}
/* 标签字符串预览 */
.tag-string-preview {
font-size: 26rpx;
color: #475569;
padding: 16rpx;
background: rgba(255, 255, 255, 0.7);
border-radius: 16rpx;
border: 2rpx dashed #07c160;
}
.tag-string-value {
color: #07c160;
font-weight: 500;
word-break: break-all;
}
/* 内容区域特殊样式 */
.content-card .content-textarea {
min-height: 300rpx;
}
/* 提交区域 */
.submit-area {
margin-top: 60rpx;
padding-bottom: 40rpx;
text-align: center;
}
.submit-btn {
width: 100%;
height: 96rpx;
background: linear-gradient(135deg, #07c160, #059669);
border-radius: 60rpx;
color: white;
font-size: 36rpx;
font-weight: 600;
display: flex;
align-items: center;
justify-content: center;
border: none;
box-shadow: 0 20rpx 40rpx rgba(7, 193, 96, 0.3);
transition: all 0.3s ease;
}
.submit-btn:active {
transform: translateY(-4rpx);
box-shadow: 0 30rpx 50rpx rgba(7, 193, 96, 0.4);
}
.submit-btn[disabled] {
opacity: 0.7;
transform: none;
box-shadow: 0 10rpx 20rpx rgba(7, 193, 96, 0.2);
}
.tip-text {
margin-top: 24rpx;
font-size: 26rpx;
color: #94a3b8;
letter-spacing: 2rpx;
}
/* 动画效果 */
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(20rpx);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.form-card {
animation: fadeIn 0.4s ease-out forwards;
}

37
pagesB/pages/experienceDetails/experienceDetails.js

@ -0,0 +1,37 @@
import http from '../../../utils/api'
const baseUrl = require('../../../utils/baseUrl')
Page({
data: {
id: '', // 文章ID
baseUrl:baseUrl,
detail: {}, // 文章详情数据
tags:[]
},
onLoad(options) {
this.getexperienceDetails(options)
},
// 经验分享详情
getexperienceDetails(options){
http.experienceDetails({
data:{
id:options.id
},
success:res=>{
console.log(1111,res);
const tag = res.data.tags.split(',')
var ch ='<img src="/dev-api'
const mmg = res.data.content.replace(new RegExp(ch, 'g'), '<img src="' + baseUrl)
const images = mmg.replace(/\<img/g, '<img style="width:100%;display:block; border-radius:3px;"');
this.setData({
detail:res.data,
content:images,
tags:tag
})
}
})
},
})

4
pagesB/pages/experienceDetails/experienceDetails.json

@ -0,0 +1,4 @@
{
"navigationBarTitleText": "分享详情",
"usingComponents": {}
}

52
pagesB/pages/experienceDetails/experienceDetails.wxml

@ -0,0 +1,52 @@
<view class="page-container">
<!-- 内容区域 -->
<scroll-view class="content-scroll" scroll-y="true">
<!-- 文章头部 -->
<view class="article-header">
<!-- 分类标签 -->
<view class="category-tag">
<text class="category-text">{{detail.categoryName}}</text>
</view>
<!-- 标题 -->
<view class="article-title">{{detail.title}}</view>
<!-- 作者信息和发布时间 -->
<view class="article-meta">
<view class="author-info">
<image src="{{baseUrl+detail.vetAvatar}}" class="author-avatar"></image>
<view class="author-details">
<text class="author-name">{{detail.vetName}}</text>
<text class="publish-time">{{detail.publishTime}}</text>
</view>
</view>
<!-- 浏览量 -->
<view class="view-info">
<image src="/pagesB/images/lll.png" class="view-icon"></image>
<text class="view-count">{{detail.viewCount}}次浏览</text>
</view>
</view>
<!-- 分类 -->
<view class="fl">
<view wx:for="{{tags}}">{{item}}</view>
</view>
</view>
<!-- 文章内容 -->
<view class="article-content">
<!-- 摘要 -->
<view wx:if="{{detail.summary}}" class="article-summary">
{{detail.summary}}
</view>
<!-- 正文内容 -->
<view class="article-body">
<rich-text class="content-text" space="emsp" nodes="{{content}}"></rich-text>
</view>
</view>
</scroll-view>
</view>

158
pagesB/pages/experienceDetails/experienceDetails.wxss

@ -0,0 +1,158 @@
.page-container {
background-color: #f5f7fa;
min-height: 100vh;
box-sizing: border-box;
}
/* 内容区域 */
.content-scroll {
height: 100vh;
padding-top: calc(88rpx + var(--status-bar-height));
box-sizing: border-box;
}
/* 文章头部 */
.article-header {
background-color: white;
padding: 40rpx 32rpx 32rpx;
margin-bottom: 20rpx;
}
.category-tag {
display: inline-block;
background-color: #E8F3FF;
padding: 8rpx 20rpx;
border-radius: 24rpx;
margin-bottom: 24rpx;
}
.category-text {
font-size: 24rpx;
color: #4A90E2;
font-weight: 500;
}
.article-title {
font-size: 40rpx;
color: #1a1a1a;
font-weight: 700;
line-height: 1.4;
margin-bottom: 32rpx;
}
.article-meta {
display: flex;
align-items: center;
justify-content: space-between;
}
.author-info {
display: flex;
align-items: center;
flex: 1;
}
.author-avatar {
width: 64rpx;
height: 64rpx;
border-radius: 50%;
margin-right: 16rpx;
border: 2rpx solid #f0f0f0;
}
.author-details {
display: flex;
flex-direction: column;
}
.author-name {
font-size: 28rpx;
color: #333;
font-weight: 500;
margin-bottom: 4rpx;
}
.publish-time {
font-size: 24rpx;
color: #999;
}
.view-info {
display: flex;
align-items: center;
}
.view-icon {
width: 32rpx;
height: 32rpx;
margin-right: 8rpx;
opacity: 0.7;
}
.view-count {
font-size: 26rpx;
color: #999;
}
.fl{
margin-top: 30rpx;
display: flex;
gap: 20rpx;
}
.fl view{
background-color: #F5F7FA;
font-size: 24rpx;
padding: 10rpx 20rpx;
border-radius: 10rpx;
}
/* 文章内容 */
.article-content {
background-color: white;
padding: 32rpx;
}
.article-summary {
font-size: 30rpx;
color: #666;
line-height: 1.6;
margin-bottom: 32rpx;
padding: 24rpx;
background-color: #f9f9f9;
border-radius: 12rpx;
border-left: 4rpx solid #4A90E2;
}
.article-body {
font-size: 30rpx;
line-height: 1.8;
color: #333;
}
.content-text {
font-size: 30rpx;
line-height: 1.8;
color: #333;
white-space: pre-line;
}
.content-text p {
margin-bottom: 32rpx;
}
/* 响应式调整 */
@media (max-width: 375px) {
.article-header,
.article-content {
padding: 24rpx;
}
.article-title {
font-size: 36rpx;
}
.content-text {
font-size: 28rpx;
}
}

217
pagesB/pages/experienceList/experienceList.js

@ -0,0 +1,217 @@
import http from '../../../utils/api'
const baseUrl = require('../../../utils/baseUrl')
Page({
data: {
// 搜索文本
searchText: '',
baseUrl: baseUrl,
// 当前选中的分类
activeCategory: '',
// 分类数据
categories: [],
// 经验分享列表
experienceList: [],
// 分页参数
pageNum: 1,
pageSize: 10,
total: 0,
// 加载状态
loading: false,
hasMore: true,
// 搜索防抖定时器
searchTimer: null
},
onLoad() {
// 获取分类数据
this.getCategories()
},
onShow(){
// 获取经验分享列表
this.getExperienceList(true)
},
// 获取经验分享列表
getExperienceList(isRefresh = false) {
if (this.data.loading) return
const params = {
pageNum: isRefresh ? 1 : this.data.pageNum,
pageSize: this.data.pageSize
}
// 添加搜索关键词
if (this.data.searchText) {
params.searchKey = this.data.searchText.trim()
}
// 添加分类筛选
if (this.data.activeCategory) {
params.categoryName = this.data.activeCategory
}
this.setData({ loading: true })
http.experience({
data: params,
success: res => {
console.log('经验列表数据:', res)
if (res.code === 200) {
const newList = isRefresh ? res.rows : [...this.data.experienceList, ...res.rows]
const total = res.total || 0
const hasMore = newList.length < total
this.setData({
experienceList: newList,
total: total,
hasMore: hasMore,
pageNum: isRefresh ? 2 : this.data.pageNum + 1,
loading: false
})
} else {
wx.showToast({
title: res.msg || '加载失败',
icon: 'none'
})
this.setData({ loading: false })
}
},
fail: err => {
console.error('请求失败:', err)
wx.showToast({
title: '网络错误,请重试',
icon: 'none'
})
this.setData({ loading: false })
}
})
},
// 获取分类数据
getCategories() {
http.experiencezd({
data: {
categoryName: 'category_id'
},
success: res => {
console.log('分类数据:', res)
if (res.code === 200) {
this.setData({
categories: res.data || []
})
}
},
fail: err => {
console.error('获取分类失败:', err)
}
})
},
// 处理搜索输入(带防抖)
onSearchInput(e) {
const searchText = e.detail.value
this.setData({
searchText: searchText
})
// 清除之前的定时器
if (this.data.searchTimer) {
clearTimeout(this.data.searchTimer)
}
// 设置新的定时器,500ms后执行搜索
const timer = setTimeout(() => {
this.handleSearch()
}, 500)
this.setData({
searchTimer: timer
})
},
// 搜索确认
onSearchConfirm() {
if (this.data.searchTimer) {
clearTimeout(this.data.searchTimer)
}
this.handleSearch()
},
// 执行搜索
handleSearch() {
this.setData({
pageNum: 1,
hasMore: true
})
this.getExperienceList(true)
},
// 清空搜索
clearSearch() {
this.setData({
searchText: '',
pageNum: 1,
hasMore: true
})
this.getExperienceList(true)
},
// 分类点击事件
onCategoryTap(e) {
const categoryId = e.currentTarget.dataset.id
if (this.data.activeCategory === categoryId) {
return
}
this.setData({
activeCategory: categoryId,
pageNum: 1,
hasMore: true
})
this.getExperienceList(true)
},
//发布文章
bindPublish(){
wx.navigateTo({
url: '/pagesA/pages/releaseSuffer/releaseSuffer',
})
},
// 滚动到底部加载更多
onScrollToLower() {
if (!this.data.loading && this.data.hasMore) {
this.getExperienceList()
}
},
// 点击加载更多
loadMoreData() {
if (!this.data.loading && this.data.hasMore) {
this.getExperienceList()
}
},
// 经验分享点击事件
onExperienceTap(e) {
const id = e.currentTarget.dataset.id
wx.navigateTo({
url: `/pagesB/pages/experienceDetails/experienceDetails?id=${id}`,
})
},
onUnload() {
// 清理定时器
if (this.data.searchTimer) {
clearTimeout(this.data.searchTimer)
}
}
})

4
pagesB/pages/experienceList/experienceList.json

@ -0,0 +1,4 @@
{
"navigationBarTitleText": "经验分享",
"usingComponents": {}
}

89
pagesB/pages/experienceList/experienceList.wxml

@ -0,0 +1,89 @@
<view class="page-container">
<!-- 搜索栏 -->
<view class="search-container">
<view class="search-box">
<image src="/pagesB/images/sou.png" class="search-icon"></image>
<input placeholder="搜索经验分享..." placeholder-class="placeholder" bindinput="onSearchInput" bindconfirm="onSearchConfirm" value="{{searchText}}" class="search-input" />
<view wx:if="{{searchText}}" bindtap="clearSearch" class="clear-btn">
<image src="/pagesA/images/ch.png" class="clear-icon"></image>
</view>
</view>
</view>
<!-- 分类标签 -->
<scroll-view class="category-container" scroll-x="true">
<view class="category-list">
<view class="category-item {{activeCategory === '' ? 'active' : ''}}" bindtap="onCategoryTap" data-id="">
<text class="category-text">全部</text>
</view>
<view wx:for="{{categories}}" wx:key="id" class="category-item {{activeCategory === item.label ? 'active' : ''}}" bindtap="onCategoryTap" data-id="{{item.label}}">
<text class="category-text">{{item.label}}</text>
</view>
</view>
</scroll-view>
<!-- 经验分享列表 -->
<scroll-view class="experience-scroll" scroll-y="true" bindscrolltolower="onScrollToLower" style="height: 100vh;">
<view class="experience-list">
<view wx:for="{{experienceList}}" wx:key="id" class="experience-item" bindtap="onExperienceTap" data-id="{{item.id}}">
<!-- 分类标签 -->
<view class="tag">
<view class="item-category-tag">
<text class="tag-text">{{item.categoryName}}</text>
</view>
<!-- 发布时间 -->
<text class="publish-time">{{item.publishTime}}</text>
</view>
<!-- 标题 -->
<view class="item-title">{{item.title}}</view>
<!-- 摘要 -->
<view class="item-summary">{{item.summary}}</view>
<!-- 底部信息 -->
<view class="item-footer">
<!-- 作者信息 -->
<view class="author-info">
<image src="{{baseUrl+item.vetAvatar}}" class="author-avatar"></image>
<text class="author-name">{{item.vetName}}</text>
</view>
<!-- 浏览量 -->
<view class="view-info">
<image src="/pagesB/images/lll.png" class="view-icon"></image>
<text class="view-count">{{item.viewCount}}</text>
</view>
</view>
</view>
<!-- 新增气泡按钮 -->
<view class="create-btn-container" bind:tap="bindPublish">
<view class="create-btn" bindtap="showCreateModal">
<image class="btn-icon" src="/pagesA/images/jh.png"></image>
<text class="btn-text">发布</text>
</view>
</view>
<!-- 加载更多 -->
<view wx:if="{{!loading && hasMore}}" class="load-more" bindtap="loadMoreData">
<text class="load-more-text">点击加载更多</text>
</view>
<view wx:if="{{loading}}" class="loading">
<text class="loading-text">加载中...</text>
</view>
<view wx:if="{{!hasMore && experienceList.length > 0}}" class="no-more">
<text class="no-more-text">没有更多了</text>
</view>
<!-- 空状态 -->
<view wx:if="{{experienceList.length === 0 && !loading}}" class="empty-state">
<text class="empty-text">暂无相关经验分享</text>
<text class="empty-hint">{{searchText || activeCategory ? '换个关键词试试吧' : '快去分享你的经验吧'}}</text>
</view>
</view>
</scroll-view>
</view>

390
pagesB/pages/experienceList/experienceList.wxss

@ -0,0 +1,390 @@
.page-container {
background-color: #f5f7fa;
min-height: 100vh;
}
/* 搜索区域 */
.search-container {
background: linear-gradient(135deg, #4A90E2 0%, #6AC5F8 100%);
padding: 20rpx 32rpx 32rpx;
}
.search-box {
background-color: white;
border-radius: 50rpx;
padding: 20rpx 32rpx;
display: flex;
align-items: center;
box-shadow: 0 4rpx 16rpx rgba(74, 144, 226, 0.2);
}
.search-icon {
width: 32rpx;
height: 32rpx;
margin-right: 16rpx;
}
.search-input {
width: 95%;
font-size: 28rpx;
color: #333;
}
.placeholder {
color: #999;
font-size: 28rpx;
}
.clear-btn {
width: 32rpx;
height: 32rpx;
display: flex;
align-items: center;
justify-content: center;
}
.clear-icon {
width: 30rpx;
height: 30rpx;
filter: brightness(0) contrast(100%);
}
/* 分类区域 */
.category-container {
background-color: white;
padding: 20rpx 32rpx;
white-space: nowrap;
box-sizing: border-box;
}
.category-list {
display: inline-flex;
}
.category-item {
display: inline-flex;
align-items: center;
padding: 12rpx 28rpx;
margin-right: 20rpx;
border-radius: 40rpx;
background-color: #f5f7fa;
border: 2rpx solid #f5f7fa;
transition: all 0.3s ease;
}
.category-item.active {
background-color: #E8F3FF;
border-color: #4A90E2;
}
.category-text {
font-size: 26rpx;
color: #666;
font-weight: 400;
}
.category-item.active .category-text {
color: #4A90E2;
font-weight: 500;
}
.category-item.active .category-count {
background-color: rgba(74, 144, 226, 0.1);
color: #4A90E2;
}
/* 经验列表 */
.experience-list {
padding: 32rpx;
}
.experience-item {
background-color: white;
border-radius: 20rpx;
padding: 32rpx;
margin-bottom: 24rpx;
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.04);
transition: all 0.3s ease;
}
.experience-item:active {
transform: translateY(-2rpx);
box-shadow: 0 6rpx 24rpx rgba(0, 0, 0, 0.08);
}
.tag{
display: flex;
align-items: center;
justify-content: space-between;
padding-bottom: 20rpx;
}
.item-category-tag {
display: inline-block;
background-color: #E8F3FF;
padding: 6rpx 16rpx;
border-radius: 20rpx;
}
.tag-text {
font-size: 22rpx;
color: #4A90E2;
font-weight: 500;
}
.item-title {
font-size: 34rpx;
color: #1a1a1a;
font-weight: 600;
line-height: 1.4;
margin-bottom: 16rpx;
}
.item-summary {
font-size: 28rpx;
color: #666;
line-height: 1.5;
margin-bottom: 24rpx;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
}
.item-footer {
display: flex;
align-items: center;
justify-content: space-between;
padding-top: 20rpx;
border-top: 1rpx solid #f0f0f0;
}
.author-info {
display: flex;
align-items: center;
flex: 1;
}
.author-avatar {
width: 48rpx;
height: 48rpx;
border-radius: 50%;
margin-right: 12rpx;
}
.author-name {
font-size: 24rpx;
color: #999;
}
.view-info {
display: flex;
align-items: center;
margin-right: 32rpx;
}
.view-icon {
width: 28rpx;
height: 28rpx;
margin-right: 8rpx;
opacity: 0.6;
}
.view-count {
font-size: 24rpx;
color: #999;
}
.publish-time {
font-size: 24rpx;
color: #ccc;
flex-shrink: 0;
}
/* 新增视频气泡按钮 */
.create-btn-container {
position: fixed;
bottom: 40rpx;
right: 15rpx;
z-index: 100;
}
.create-btn {
width: 120rpx;
height: 120rpx;
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;
}
/* 空状态 */
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 100rpx 0;
}
.empty-text {
font-size: 32rpx;
color: #999;
margin-bottom: 16rpx;
}
.empty-hint {
font-size: 26rpx;
color: #ccc;
}
.experience-scroll {
box-sizing: border-box;
}
/* 加载更多样式 */
.load-more {
text-align: center;
padding: 40rpx 0;
color: #4A90E2;
font-size: 28rpx;
}
.load-more-text {
padding: 16rpx 32rpx;
background-color: #E8F3FF;
border-radius: 24rpx;
display: inline-block;
}
.loading {
text-align: center;
padding: 40rpx 0;
color: #999;
font-size: 28rpx;
}
.loading-text {
display: inline-block;
padding: 16rpx 32rpx;
}
.no-more {
text-align: center;
padding: 40rpx 0;
color: #ccc;
font-size: 26rpx;
}
.no-more-text {
display: inline-block;
padding: 16rpx 32rpx;
}
/* 优化空状态样式 */
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 120rpx 0;
text-align: center;
}
.empty-text {
font-size: 32rpx;
color: #999;
margin-bottom: 20rpx;
font-weight: 500;
}
.empty-hint {
font-size: 28rpx;
color: #ccc;
}
/* 优化列表项样式 */
.experience-item {
position: relative;
overflow: hidden;
}
.experience-item:last-child {
margin-bottom: 0;
}
/* 响应式调整 */
@media (max-width: 375px) {
.load-more,
.loading,
.no-more {
padding: 30rpx 0;
}
.empty-state {
padding: 80rpx 0;
}
}
/* 响应式调整 */
@media (max-width: 375px) {
.search-container {
padding: 16rpx 24rpx 24rpx;
}
.experience-list {
padding: 24rpx;
}
.experience-item {
padding: 24rpx;
}
}

477
pagesB/pages/forumlist/forumlist.js

@ -0,0 +1,477 @@
import http from '../../../utils/api'
const baseUrl = require('../../../utils/baseUrl')
Page({
data: {
// 帖子列表相关
posts: [],
baseUrl: baseUrl,
loading: false,
searchLoading: false,
initialLoading: true,
loadingMore: false,
refreshing: false,
hasMore: true,
page: 1,
pageSize: 10,
searchKeyword: '',
lastSearchKeyword: '', // 新增:记录上次搜索关键词
scrollTop: 0,
showBackToTop: false,
scrollThreshold: 300,
// 发布帖子相关
showPostModal: false,
postTitle: '',
postContent: '',
postImages: [],
isSubmitting: false
},
onLoad: function () {
this.loadPosts(true);
},
onShow: function () {
// 每次显示页面时刷新数据
this.refreshData();
},
// 加载帖子列表
loadPosts: function (reset = false) {
// 防止重复请求
if (reset) {
this.setData({
page: 1,
hasMore: true,
loading: true,
searchLoading: !!this.data.searchKeyword
});
} else if (this.data.loadingMore) {
return;
}
this.setData({
loading: reset || this.data.page === 1,
loadingMore: !reset && this.data.page > 1
});
// 准备请求参数
const params = {
page: this.data.page,
pageSize: this.data.pageSize
};
// 如果有搜索关键词,添加搜索参数
const searchKeyword = this.data.searchKeyword.trim();
if (searchKeyword) {
params.searchKey = searchKeyword;
}
// 记录当前搜索关键词
this.setData({
lastSearchKeyword: searchKeyword
});
// 调用接口获取数据
http.forumList({
data: params,
success: (res) => {
if (res.code === 200) {
let postsData = res.rows || [];
// 处理图片字段(分割字符串为数组)
postsData = postsData.map(item => {
if (item.images && typeof item.images === 'string') {
item.images = item.images.split(',').filter(img => img.trim() !== '');
} else {
item.images = [];
}
return item;
});
if (reset) {
this.setData({
posts: postsData,
loading: false,
searchLoading: false,
initialLoading: false,
hasMore: postsData.length === this.data.pageSize
});
} else {
this.setData({
posts: [...this.data.posts, ...postsData],
loading: false,
initialLoading: false,
loadingMore: false,
hasMore: postsData.length === this.data.pageSize
});
}
} else {
wx.showToast({
title: res.msg || '加载失败',
icon: 'none'
});
this.setData({
loading: false,
searchLoading: false,
initialLoading: false,
loadingMore: false
});
}
if (this.data.refreshing) {
wx.stopPullDownRefresh();
this.setData({
refreshing: false
});
}
},
fail: (err) => {
wx.showToast({
title: '网络错误',
icon: 'none'
});
this.setData({
loading: false,
searchLoading: false,
initialLoading: false,
loadingMore: false,
refreshing: false
});
}
});
},
// 下拉刷新
onRefresh: function () {
this.setData({
refreshing: true
});
this.loadPosts(true);
},
// 加载更多
loadMore: function () {
if (!this.data.hasMore || this.data.loadingMore) return;
this.setData({
page: this.data.page + 1
}, () => {
this.loadPosts();
});
},
// 搜索输入(防抖)
onSearchInput: function (e) {
const keyword = e.detail.value;
this.setData({
searchKeyword: keyword
});
// 防抖搜索
clearTimeout(this.searchTimer);
// 如果输入框为空,立即重置搜索
if (!keyword.trim()) {
this.setData({
searchKeyword: ''
});
// 清空搜索时立即重新加载数据
this.loadPosts(true);
return;
}
this.searchTimer = setTimeout(() => {
// 只有当有搜索词且与上次不同时才搜索
if (keyword.trim() && keyword.trim() !== this.data.lastSearchKeyword) {
this.loadPosts(true);
}
}, 500);
},
// 搜索确认
onSearchConfirm: function (e) {
const keyword = e.detail.value.trim();
this.setData({
searchKeyword: keyword
});
// 如果搜索词为空,加载所有帖子
if (!keyword) {
this.loadPosts(true);
} else {
// 只有当搜索词与上次不同时才搜索
if (keyword !== this.data.lastSearchKeyword) {
this.loadPosts(true);
}
}
},
// 清空搜索 - 优化版
clearSearch: function () {
// 如果当前已经有搜索词,才执行清空操作
if (this.data.searchKeyword) {
this.setData({
searchKeyword: ''
}, () => {
// 清空后立即加载所有帖子
this.loadPosts(true);
});
}
},
// 跳转到详情页
goToDetail: function (e) {
const postId = e.currentTarget.dataset.id;
wx.navigateTo({
url: `/pagesB/pages/onlineAsk/onlineAsk?id=${postId}`
});
},
// 图片预览功能
previewImage: function (e) {
const postIndex = e.currentTarget.dataset.postindex;
const imageIndex = e.currentTarget.dataset.imageindex;
const post = this.data.posts[postIndex];
if (!post || !post.images || post.images.length === 0) return;
// 构建完整的图片URL数组
const urls = post.images.map(img => this.data.baseUrl + img);
wx.previewImage({
current: urls[imageIndex], // 当前显示图片的链接
urls: urls // 需要预览的图片链接列表
});
},
// 显示发布模态框
createPost: function () {
// 检查登录状态(如果需要)
// 这里可以添加登录检查逻辑
this.setData({
showPostModal: true
});
},
// 隐藏发布模态框
hidePostModal: function () {
if (this.data.isSubmitting) return;
this.setData({
showPostModal: false,
postTitle: '',
postContent: '',
postImages: []
});
},
// 阻止事件冒泡
stopPropagation: function () {},
// 标题输入
onPostTitleInput: function (e) {
this.setData({
postTitle: e.detail.value
});
},
// 内容输入
onPostContentInput: function (e) {
this.setData({
postContent: e.detail.value
});
},
// 选择图片
chooseImage: function () {
const remaining = 3 - this.data.postImages.length;
if (remaining <= 0) {
wx.showToast({
title: '最多上传3张图片',
icon: 'none'
});
return;
}
wx.chooseMedia({
count: remaining,
mediaType: ['image'],
sizeType: ['compressed'],
sourceType: ['album', 'camera'],
success: (res) => {
const tempFiles = res.tempFiles;
const newImages = tempFiles.map(file => file.tempFilePath);
const postImages = [...this.data.postImages, ...newImages];
this.setData({
postImages: postImages.slice(0, 3) // 确保不超过3张
});
},
fail: (err) => {
console.error('选择图片失败:', err);
}
});
},
// 移除图片
removeImage: function (e) {
const index = e.currentTarget.dataset.index;
const postImages = [...this.data.postImages];
postImages.splice(index, 1);
this.setData({
postImages
});
},
// 提交帖子
submitPost: function () {
const { postTitle, postContent, postImages } = this.data;
// 验证输入
if (!postTitle.trim()) {
wx.showToast({
title: '请输入标题',
icon: 'none'
});
return;
}
if (!postContent.trim()) {
wx.showToast({
title: '请输入内容',
icon: 'none'
});
return;
}
this.setData({
isSubmitting: true
});
// 如果有图片,先上传图片
const uploadPromises = postImages.map(imagePath => {
return new Promise((resolve, reject) => {
wx.uploadFile({
url: baseUrl+'/common/upload',
header: {
'Authorization': 'Bearer ' + wx.getStorageSync('token')
},
filePath: imagePath,
name: 'file',
success: (res) => {
const data = JSON.parse(res.data);
console.log(data);
if (data.code === 200) {
resolve(data.fileName); // 假设返回的图片路径在data.url中
} else {
reject(new Error('图片上传失败'));
}
},
fail: (err) => {
reject(err);
}
});
});
});
// 处理图片上传
Promise.all(uploadPromises)
.then((imageUrls) => {
// 所有图片上传成功,提交帖子数据
const postData = {
title: postTitle,
content: postContent,
images: imageUrls.join(',') // 将图片URL用逗号拼接
};
return http.forumAdd({
data: postData,
success:res=>{
console.log(1111,res);
if (res.code === 200) {
// 发布成功
this.setData({
showPostModal: false,
postTitle: '',
postContent: '',
postImages: [],
isSubmitting: false
});
wx.showToast({
title: '发布成功',
icon: 'success',
duration: 2000
});
// 刷新帖子列表
setTimeout(() => {
this.loadPosts(true);
}, 500);
} else {
throw new Error(res.msg || '发布失败');
}
}
});
})
},
// 滚动事件监听
onScroll: function (e) {
const scrollTop = e.detail.scrollTop;
const showBackToTop = scrollTop > this.data.scrollThreshold;
if (showBackToTop !== this.data.showBackToTop) {
this.setData({
scrollTop: scrollTop,
showBackToTop: showBackToTop
});
}
},
// 返回顶部
backToTop: function () {
this.setData({
showBackToTop: false
});
wx.pageScrollTo({
scrollTop: 0,
duration: 400,
success: () => {
this.setData({
scrollTop: 0
});
},
fail: (err) => {
console.log('滚动失败:', err);
this.setData({
scrollTop: 0
});
}
});
},
// 刷新数据
refreshData: function () {
const pages = getCurrentPages();
const currentPage = pages[pages.length - 1];
if (currentPage.data && currentPage.data.refresh) {
this.loadPosts(true);
currentPage.setData({
refresh: false
});
}
},
onPullDownRefresh: function () {
this.onRefresh();
},
onReachBottom: function () {
this.loadMore();
}
});

4
pagesB/pages/forumlist/forumlist.json

@ -0,0 +1,4 @@
{
"navigationBarTitleText": "问答论坛",
"usingComponents": {}
}

208
pagesB/pages/forumlist/forumlist.wxml

@ -0,0 +1,208 @@
<view class="forum-page">
<!-- 顶部栏 -->
<view class="header">
<view class="header-content">
<view class="header-main">
<text class="title-text">问答社区</text>
<text class="title-sub">互帮互助,共同成长</text>
</view>
</view>
</view>
<!-- 主内容区 -->
<view class="main-content">
<!-- 搜索框 -->
<view class="search-section">
<view class="search-box">
<image class="search-icon" src="/pagesB/images/sou.png" mode="aspectFit"></image>
<input class="search-input" placeholder="搜索问题或关键词..." value="{{searchKeyword}}" bindinput="onSearchInput" bindconfirm="onSearchConfirm" confirm-type="search" />
<view class="search-clear" wx:if="{{searchKeyword}}" bindtap="clearSearch">
<text class="clear-text">×</text>
</view>
</view>
</view>
<!-- 帖子列表 -->
<scroll-view class="post-list-section" scroll-y enable-back-to-top scroll-top="{{scrollTop}}" bindscrolltolower="loadMore" bindscroll="onScroll" refresher-enabled="{{true}}" refresher-triggered="{{refreshing}}" bindrefresherrefresh="onRefresh">
<!-- 搜索加载提示 -->
<view class="search-loading" wx:if="{{searchLoading && posts.length === 0}}">
<view class="search-spinner"></view>
<text class="search-loading-text">搜索中...</text>
</view>
<!-- 空状态 -->
<view class="empty-state" wx:if="{{!loading && !searchLoading && posts.length === 0}}">
<view class="empty-text" wx:if="{{searchKeyword}}">
没有找到"{{searchKeyword}}"相关的问题
</view>
<view class="empty-text" wx:else>
这里还没有问题,快来发布第一个吧!
</view>
<button class="empty-btn" bindtap="createPost" wx:if="{{!searchKeyword}}">
发布问题
</button>
</view>
<!-- 帖子列表 -->
<view class="post-list" wx:if="{{posts.length > 0}}">
<block wx:for="{{posts}}" wx:key="id">
<view class="post-card" data-id="{{item.id}}" bindtap="goToDetail">
<view class="post-content">
<view class="post-type">
<!-- 头像 -->
<view class="user-info">
<image class="user-avatar" src="{{baseUrl+item.avatar}}" mode="aspectFill"></image>
<text class="username">{{item.nickName}}</text>
</view>
<!-- 时间 -->
<text class="post-time">{{item.createdAt}}</text>
</view>
<!-- 标题 -->
<view class="post-title-wrapper">
<text class="post-title">{{item.title}}</text>
</view>
<!-- 内容摘要 -->
<view class="post-summary">
{{item.content}}
</view>
<!-- 图片预览 -->
<view class="post-images" wx:if="{{item.images && item.images.length>0}}">
<view class="images-grid">
<block wx:for="{{item.images}}" wx:key="index" wx:for-index="imgIndex">
<image class="post-image" src="{{baseUrl+item}}" mode="aspectFill" data-postindex="{{index}}" data-imageindex="{{imgIndex}}" bindtap="previewImage"></image>
</block>
</view>
</view>
<!-- 底部信息 -->
<view class="post-footer">
<view class="post-meta">
<view class="meta-item">
<image class="meta-icon" src="/pagesB/images/hf.png" mode="aspectFit"></image>
<text class="meta-count">{{item.answerCount || 0}}</text>
</view>
<view class="meta-item">
<image class="meta-icon" src="/pagesB/images/lll.png" mode="aspectFit"></image>
<text class="meta-count">{{item.viewCount || 0}}</text>
</view>
</view>
</view>
</view>
</view>
</block>
<!-- 加载更多 -->
<view class="load-more" wx:if="{{hasMore && posts.length > 0}}">
<view class="loading-spinner" wx:if="{{!loadingMore}}">
<text class="loading-text">上拉加载更多</text>
</view>
<view class="loading-spinner" wx:else>
<view class="spinner"></view>
<text class="loading-text">加载中...</text>
</view>
</view>
<!-- 没有更多了 -->
<view class="no-more" wx:if="{{!hasMore && posts.length > 0}}">
<view class="no-more-line"></view>
<text class="no-more-text">已经到底了</text>
<view class="no-more-line"></view>
</view>
</view>
</scroll-view>
</view>
<!-- 固定发布按钮(左下角) -->
<!-- <view class="floating-create-btn" bindtap="createPost">
<image class="create-icon" src="/pagesA/images/jh.png" mode="aspectFit"></image>
</view> -->
<!-- 返回顶部按钮(右下角) -->
<view class="back-to-top-btn {{showBackToTop ? 'show' : ''}}" bindtap="backToTop">
<view class="back-top-icon">↑</view>
</view>
<!-- 发布模态框 -->
<view class="post-modal-overlay" wx:if="{{showPostModal}}" bindtap="hidePostModal">
<view class="post-modal-content" catchtap="stopPropagation">
<view class="modal-header">
<text class="modal-title">发布问题</text>
<view class="modal-close" bindtap="hidePostModal">
<text class="close-icon">×</text>
</view>
</view>
<view class="modal-body">
<view class="form-field">
<view class="field-header">
<text class="field-label">问题标题</text>
<text class="field-counter">{{postTitle.length}}/50</text>
</view>
<input class="title-input" placeholder="请输入问题标题" value="{{postTitle}}" bindinput="onPostTitleInput" maxlength="50" />
</view>
<view class="form-field">
<view class="field-header">
<text class="field-label">详细描述</text>
<text class="field-counter">{{postContent.length}}/500</text>
</view>
<textarea class="content-input" placeholder="请详细描述您的问题..." value="{{postContent}}" bindinput="onPostContentInput" maxlength="500" auto-height />
</view>
<view class="form-field">
<view class="field-header">
<text class="field-label">添加图片</text>
<text class="field-hint">最多3张</text>
</view>
<view class="image-upload-area">
<view class="uploaded-images" wx:if="{{postImages.length > 0}}">
<block wx:for="{{postImages}}" wx:key="index">
<view class="image-item">
<image class="preview-image" src="{{item}}" mode="aspectFill"></image>
<view class="image-remove" bindtap="removeImage" data-index="{{index}}">
<text class="remove-icon">×</text>
</view>
</view>
</block>
</view>
<view class="upload-btn" wx:if="{{postImages.length < 3}}" bindtap="chooseImage">
<image class="camera-icon" src="/pagesA/images/jh.png" mode="aspectFit"></image>
<text class="upload-text">添加图片</text>
</view>
</view>
</view>
</view>
<view class="modal-footer">
<button class="cancel-btn" bindtap="hidePostModal">取消</button>
<button class="submit-btn" bindtap="submitPost" disabled="{{!postTitle || !postContent || isSubmitting}}">
{{isSubmitting ? '发布中...' : '发布'}}
</button>
</view>
</view>
</view>
<!-- 首次加载遮罩 -->
<view class="loading-overlay" wx:if="{{initialLoading && posts.length === 0}}">
<view class="loading-content">
<view class="spinner-large"></view>
<text class="loading-tip">加载中...</text>
</view>
</view>
</view>

758
pagesB/pages/forumlist/forumlist.wxss

@ -0,0 +1,758 @@
.forum-page {
min-height: 100vh;
background: linear-gradient(180deg, #f8fafc 0%, #f1f5f9 100%);
position: relative;
font-family: '思源宋体 Light' !important;
}
/* 顶部栏 */
.header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
padding: 40rpx 30rpx;
border-radius: 0 0 40rpx 40rpx;
box-shadow: 0 10rpx 40rpx rgba(102, 126, 234, 0.15);
margin-bottom: 30rpx;
}
.header-content {
display: flex;
flex-direction: column;
gap: 30rpx;
}
.header-main {
display: flex;
flex-direction: column;
gap: 10rpx;
}
.title-text {
font-size: 44rpx;
font-weight: 800;
color: white;
letter-spacing: -0.5rpx;
}
.title-sub {
font-size: 26rpx;
color: rgba(255, 255, 255, 0.9);
font-weight: 400;
}
/* 搜索框 */
.search-section {
padding: 0 30rpx 25rpx;
}
.search-box {
display: flex;
align-items: center;
background: white;
border-radius: 50rpx;
padding: 0 30rpx;
height: 88rpx;
box-shadow: 0 15rpx 50rpx rgba(0, 0, 0, 0.05);
border: 2rpx solid #e2e8f0;
transition: all 0.3s ease;
}
.search-box:focus-within {
border-color: #667eea;
box-shadow: 0 15rpx 50rpx rgba(102, 126, 234, 0.15);
}
.search-icon {
width: 36rpx;
height: 36rpx;
margin-right: 20rpx;
}
.search-input {
flex: 1;
font-size: 30rpx;
height: 88rpx;
line-height: 88rpx;
color: #1e293b;
box-sizing: border-box;
}
.search-clear {
width: 44rpx;
height: 44rpx;
display: flex;
align-items: center;
justify-content: center;
background: #f1f5f9;
border-radius: 50%;
}
.clear-text {
font-size: 32rpx;
color: #94a3b8;
line-height: 1;
}
/* 搜索加载提示 */
.search-loading {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 100rpx 30rpx;
text-align: center;
}
.search-spinner {
width: 60rpx;
height: 60rpx;
border: 4rpx solid rgba(102, 126, 234, 0.1);
border-top-color: #667eea;
border-radius: 50%;
animation: spin 1s linear infinite;
margin-bottom: 30rpx;
}
.search-loading-text {
font-size: 30rpx;
color: #64748b;
font-weight: 500;
}
/* 帖子列表区域 */
.post-list-section {
height: calc(100vh - 320rpx);
padding: 0 20rpx;
box-sizing: border-box;
}
/* 空状态 */
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 150rpx 30rpx;
text-align: center;
}
.empty-text {
font-size: 32rpx;
color: #94a3b8;
margin-bottom: 50rpx;
line-height: 1.5;
max-width: 400rpx;
}
.empty-btn {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
font-size: 30rpx;
font-weight: 600;
padding: 24rpx 60rpx;
border-radius: 50rpx;
box-shadow: 0 10rpx 30rpx rgba(102, 126, 234, 0.3);
border: none;
margin: 0;
}
.empty-btn::after {
border: none;
}
/* 帖子卡片 */
.post-list {
padding: 10rpx;
}
.post-card {
background: white;
border-radius: 24rpx;
margin-bottom: 25rpx;
box-shadow: 0 10rpx 40rpx rgba(0, 0, 0, 0.04);
border: 1rpx solid #f1f5f9;
position: relative;
overflow: hidden;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.post-card:active {
transform: translateY(-4rpx);
box-shadow: 0 20rpx 60rpx rgba(0, 0, 0, 0.1);
}
/* 帖子内容 */
.post-content {
padding: 20rpx;
}
.post-type {
display: flex;
align-items: center;
justify-content: space-between;
border-bottom: 1rpx solid #f1f5f9;
padding-bottom: 20rpx;
}
/* 帖子标题 */
.post-title-wrapper {
margin: 20rpx 0;
}
.post-title {
font-size: 34rpx;
font-weight: 700;
color: #1e293b;
line-height: 1.4;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
/* 帖子摘要 */
.post-summary {
font-size: 28rpx;
color: #64748b;
line-height: 1.6;
margin-bottom: 25rpx;
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
}
/* 图片区域 */
.post-images {
margin-bottom: 25rpx;
}
.images-grid {
width: 100%;
display: grid;
grid-template-columns: repeat(3,1fr);
position: relative;
}
.post-image {
width: 180rpx;
height: 180rpx;
border-radius: 16rpx;
flex-shrink: 0;
object-fit: cover;
}
/* 帖子底部信息 */
.post-footer {
display: flex;
align-items: center;
justify-content: flex-end;
padding-top: 25rpx;
border-top: 1rpx solid #f1f5f9;
}
/* 用户信息 */
.user-info {
display: flex;
align-items: center;
gap: 15rpx;
}
.user-avatar {
width: 64rpx;
height: 64rpx;
border-radius: 50%;
border: 2rpx solid white;
box-shadow: 0 6rpx 20rpx rgba(0, 0, 0, 0.08);
}
.username {
font-size: 28rpx;
font-weight: 600;
color: #334155;
}
/* 元信息 */
.post-meta {
display: flex;
align-items: center;
gap: 25rpx;
}
.meta-item {
display: flex;
align-items: center;
gap: 8rpx;
}
.meta-icon {
width: 28rpx;
height: 28rpx;
opacity: 0.5;
}
.meta-count {
font-size: 26rpx;
font-weight: 600;
color: #64748b;
}
.post-time {
font-size: 24rpx;
color: #94a3b8;
font-weight: 500;
}
/* 加载更多 */
.load-more {
padding: 50rpx 0;
text-align: center;
}
.loading-spinner {
display: flex;
flex-direction: column;
align-items: center;
gap: 20rpx;
}
.spinner {
width: 40rpx;
height: 40rpx;
border: 4rpx solid rgba(102, 126, 234, 0.1);
border-top-color: #667eea;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.loading-text {
font-size: 28rpx;
color: #94a3b8;
font-weight: 500;
}
/* 没有更多了 */
.no-more {
display: flex;
align-items: center;
justify-content: center;
gap: 30rpx;
padding: 50rpx 0;
}
.no-more-line {
flex: 1;
height: 1rpx;
background: linear-gradient(90deg, transparent, #e2e8f0, transparent);
}
.no-more-text {
font-size: 26rpx;
color: #cbd5e1;
font-weight: 500;
padding: 0 20rpx;
}
/* 固定发布按钮(左下角) */
.floating-create-btn {
position: fixed;
left: 40rpx;
bottom: 40rpx;
width: 100rpx;
height: 100rpx;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 15rpx 50rpx rgba(102, 126, 234, 0.4);
z-index: 100;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.floating-create-btn:active {
transform: scale(0.95);
box-shadow: 0 10rpx 30rpx rgba(102, 126, 234, 0.3);
}
.create-icon {
width: 44rpx;
height: 44rpx;
}
/* 返回顶部按钮 - 修复图标显示 */
.back-to-top-btn {
position: fixed;
right: 40rpx;
bottom: 40rpx;
width: 100rpx;
height: 100rpx;
background: white;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 10rpx 40rpx rgba(0, 0, 0, 0.1);
z-index: 100;
opacity: 0;
transform: translateY(50rpx) scale(0.8);
transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
pointer-events: none;
}
.back-to-top-btn.show {
opacity: 1;
transform: translateY(0) scale(1);
pointer-events: auto;
}
.back-to-top-btn:active {
transform: translateY(2rpx) scale(0.98);
background: #f8fafc;
transition: transform 0.1s ease;
}
.back-top-icon {
font-size: 40rpx;
color: #667eea;
font-weight: bold;
transition: all 0.3s ease;
}
.back-to-top-btn:active .back-top-icon {
transform: translateY(-2rpx);
}
/* 发布模态框 */
.post-modal-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
backdrop-filter: blur(10rpx);
display: flex;
align-items: flex-end;
justify-content: center;
z-index: 1000;
animation: fadeIn 0.3s ease;
}
.post-modal-content {
background: white;
width: 100%;
border-radius: 40rpx 40rpx 0 0;
max-height: 85vh;
overflow-y: auto;
animation: slideUp 0.3s ease;
}
.modal-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 40rpx 30rpx 30rpx;
border-bottom: 1rpx solid #f1f5f9;
}
.modal-title {
font-size: 38rpx;
font-weight: 700;
color: #1e293b;
}
.modal-close {
width: 50rpx;
height: 50rpx;
display: flex;
align-items: center;
justify-content: center;
background: #f1f5f9;
border-radius: 50%;
}
.close-icon {
font-size: 36rpx;
color: #64748b;
line-height: 1;
}
.modal-body {
padding: 30rpx;
box-sizing: border-box;
}
.form-field {
margin-bottom: 40rpx;
}
.field-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16rpx;
}
.field-label {
font-size: 30rpx;
font-weight: 600;
color: #1e293b;
}
.field-counter, .field-hint {
font-size: 26rpx;
color: #94a3b8;
}
.title-input {
font-family: '思源宋体 Light' !important;
background: #f8fafc;
border-radius: 16rpx;
font-size: 30rpx;
width: 100%;
height: 80rpx;
line-height: 80rpx;
padding: 0 20rpx;
box-sizing: border-box;
transition: all 0.3s ease;
border: 2rpx solid transparent;
}
.title-input:focus {
border-color: #667eea;
background: white;
box-shadow: 0 0 0 4rpx rgba(102, 126, 234, 0.1);
outline: none;
}
.content-input {
background: #f8fafc;
border-radius: 16rpx;
font-size: 30rpx;
min-height: 200rpx;
line-height: 1.6;
padding: 20rpx;
box-sizing: border-box;
width: 100%;
transition: all 0.3s ease;
border: 2rpx solid transparent;
}
.content-input:focus {
border-color: #667eea;
background: white;
box-shadow: 0 0 0 4rpx rgba(102, 126, 234, 0.1);
outline: none;
}
.image-upload-area {
margin-top: 10rpx;
}
.uploaded-images {
display: flex;
flex-wrap: wrap;
gap: 20rpx;
margin-bottom: 20rpx;
}
.image-item {
position: relative;
width: 180rpx;
height: 180rpx;
}
.preview-image {
width: 100%;
height: 100%;
border-radius: 16rpx;
object-fit: cover;
}
.image-remove {
position: absolute;
top: -12rpx;
right: -12rpx;
width: 44rpx;
height: 44rpx;
background: #ef4444;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 6rpx 20rpx rgba(239, 68, 68, 0.3);
}
.remove-icon {
font-size: 28rpx;
color: white;
line-height: 1;
font-weight: bold;
}
.upload-btn {
width: 180rpx;
height: 180rpx;
border: 2rpx dashed #cbd5e1;
border-radius: 16rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background: #f8fafc;
transition: all 0.3s ease;
gap: 15rpx;
}
.upload-btn:active {
border-color: #667eea;
background: rgba(102, 126, 234, 0.05);
}
.camera-icon {
width: 48rpx;
height: 48rpx;
filter: brightness(0);
}
.upload-text {
font-size: 26rpx;
color: #64748b;
font-weight: 500;
}
.modal-footer {
display: flex;
gap: 20rpx;
padding: 30rpx;
background: #f8fafc;
border-top: 1rpx solid #f1f5f9;
}
.cancel-btn, .submit-btn {
flex: 1;
border-radius: 16rpx;
font-size: 32rpx;
font-weight: 600;
border: none;
margin: 0;
transition: all 0.3s ease;
}
.cancel-btn::after, .submit-btn::after {
border: none;
}
.cancel-btn {
background: white;
color: #64748b;
border: 1rpx solid #e2e8f0;
}
.cancel-btn:active {
background: #f8fafc;
}
.submit-btn {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
box-shadow: 0 10rpx 30rpx rgba(102, 126, 234, 0.3);
}
.submit-btn:active:not([disabled]) {
transform: translateY(-2rpx);
box-shadow: 0 15rpx 40rpx rgba(102, 126, 234, 0.4);
}
.submit-btn[disabled] {
background: #cbd5e1;
box-shadow: none;
color: #94a3b8;
}
/* 首次加载遮罩 */
.loading-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(255, 255, 255, 0.9);
backdrop-filter: blur(10rpx);
display: flex;
align-items: center;
justify-content: center;
z-index: 999;
}
.loading-content {
display: flex;
flex-direction: column;
align-items: center;
gap: 30rpx;
}
.spinner-large {
width: 80rpx;
height: 80rpx;
border: 6rpx solid rgba(102, 126, 234, 0.1);
border-top-color: #667eea;
border-radius: 50%;
animation: spin 1s linear infinite;
}
.loading-tip {
font-size: 32rpx;
color: #64748b;
font-weight: 500;
}
/* 动画 */
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes slideUp {
from { transform: translateY(100%); }
to { transform: translateY(0); }
}
/* 添加返回顶部按钮的呼吸动画 */
@keyframes breathe {
0%, 100% {
box-shadow: 0 10rpx 40rpx rgba(0, 0, 0, 0.1);
}
50% {
box-shadow: 0 10rpx 40rpx rgba(102, 126, 234, 0.3);
}
}
.back-to-top-btn.show {
animation: breathe 3s ease-in-out infinite;
}
/* 响应式调整 */
@media (min-width: 768px) {
.post-card {
max-width: 600rpx;
margin-left: auto;
margin-right: auto;
}
.floating-create-btn {
left: calc(50% - 250rpx);
}
.back-to-top-btn {
right: calc(50% - 250rpx);
}
}

328
pagesB/pages/onlineAsk/onlineAsk.js

@ -0,0 +1,328 @@
import http from '../../../utils/api'
const baseUrl = require('../../../utils/baseUrl')
Page({
data: {
postId: null, // 存储帖子ID
post: null,
postHf: [], // 帖子回复列表
replyContent: '',
baseUrl: baseUrl,
replyTarget: {
type: '', // 'post'或'reply'或'comment'
id: '', // 回复或评论的ID
nickName: '',
replyIndex: null,
commentIndex: null,
parentId: null // 父级ID(用于二级评论)
},
replyPlaceholder: '输入您的回复...',
isInputFocused: false,
inputTransformY: '0',
isSubmitting: false,
showPreview: false,
previewImages: [],
previewIndex: 0,
loading: false,
scrollToId: '',
keyboardHeight: 0
},
onLoad: function (options) {
const postId = options.id
this.setData({
postId: postId
})
this.loadPostDetail(postId);
this.getforumReply(postId)
// 监听键盘高度变化
wx.onKeyboardHeightChange(res => {
if (res.height > 0) {
this.setData({
keyboardHeight: res.height
});
}
});
},
// 加载帖子详情
loadPostDetail: function (postId) {
this.setData({
loading: true
});
http.forumDetails({
data: {
id: postId
},
success: res => {
console.log('帖子详情:', res);
const postData = res.data
const images = postData.images ? postData.images.split(',') : []
this.setData({
post: postData,
images: images,
loading: false
});
},
fail: err => {
console.error('加载帖子详情失败:', err);
wx.showToast({
title: '加载失败',
icon: 'none'
});
this.setData({
loading: false
});
}
})
},
// 帖子回复列表
getforumReply(postId) {
http.forumReply({
data: {
questionId: postId
},
success: res => {
console.log('回复列表:', res);
this.setData({
postHf: res.rows || []
})
},
fail: err => {
console.error('加载回复列表失败:', err);
this.setData({
postHf: []
})
}
})
},
// 滚动监听
onScroll: function (e) {
// 可以在这里实现滚动相关逻辑
},
// 输入框获取焦点
onInputFocus: function (e) {
this.setData({
isInputFocused: true,
inputTransformY: `-${e.detail.height}px`
});
},
// 输入框失去焦点
onInputBlur: function () {
this.setData({
isInputFocused: false,
inputTransformY: '0'
});
},
// 回复输入
onReplyInput: function (e) {
this.setData({
replyContent: e.detail.value
});
},
// 回复一级评论
replyToUser: function (e) {
console.log(567,e);
const {
type,
index,
username
} = e.currentTarget.dataset;
const replyItem = this.data.postHf[index];
this.setData({
replyTarget: {
type: 'reply', // 回复一级评论
id: replyItem.id,
nickName: username,
replyIndex: parseInt(index),
parentId: null // 一级评论没有父级ID
},
replyPlaceholder: `回复 @${username}...`,
replyContent: '',
isInputFocused: true
});
// 滚动到对应回复位置
setTimeout(() => {
this.setData({
scrollToId: `reply-${replyItem.id}`
});
}, 100);
},
// 清除回复目标
clearReplyTarget: function () {
this.setData({
replyTarget: {
type: '',
id: '',
nickName: '',
replyIndex: null,
commentIndex: null,
parentId: null
},
replyPlaceholder: '输入您的回复...',
replyContent: ''
});
},
// 提交回复
submitReply: function () {
const {
replyContent,
replyTarget,
postId
} = this.data;
const content = replyContent.trim();
if (!content) {
wx.showToast({
title: '请输入内容',
icon: 'none'
});
return;
}
if (content.length > 500) {
wx.showToast({
title: '回复内容不能超过500字',
icon: 'none'
});
return;
}
this.setData({
isSubmitting: true
});
// 准备提交数据
const submitData = {
questionId: postId, // 帖子ID
content: content // 回复内容
};
// 如果有回复目标,添加父级ID
if (replyTarget.type === 'reply' && replyTarget.id) {
submitData.parentId = replyTarget.id; // 回复一级评论
} else if (replyTarget.type === 'comment' && replyTarget.id) {
submitData.parentId = replyTarget.id; // 回复二级评论
}
// 如果type为'post'或为空,则表示直接回复帖子,parentId为空
console.log('提交数据:', submitData);
// 调用提交接口
http.commentReply({
data: submitData,
success: res => {
console.log('回复成功:', res);
// 提交成功后的处理
this.handleReplySuccess(content, replyTarget);
// 刷新回复列表
setTimeout(() => {
this.getforumReply(postId);
}, 500);
},
fail: err => {
console.error('回复失败:', err);
wx.showToast({
title: '回复失败,请重试',
icon: 'none',
duration: 2000
});
this.setData({
isSubmitting: false
});
}
});
},
// 处理回复成功后的UI更新
handleReplySuccess: function (content, replyTarget) {
// 清空输入框和回复目标
this.setData({
replyContent: '',
replyTarget: {
type: '',
id: '',
nickName: '',
replyIndex: null,
commentIndex: null,
parentId: null
},
replyPlaceholder: '输入您的回复...',
isInputFocused: false,
isSubmitting: false
});
// 如果是直接回复帖子,滚动到底部
if (!replyTarget.type || replyTarget.type === 'post') {
setTimeout(() => {
wx.pageScrollTo({
scrollTop: 9999,
duration: 300
});
}, 300);
}
},
// 图片预览
previewImage: function (e) {
const imgIndex = e.currentTarget.dataset.imgIndex;
const images = this.data.images;
this.setData({
showPreview: true,
previewImages: images,
previewIndex: imgIndex
});
},
// 图片预览滑动
onSwiperChange: function (e) {
this.setData({
previewIndex: e.detail.current
});
},
// 隐藏预览
hidePreview: function () {
this.setData({
showPreview: false
});
},
// 下拉刷新
onPullDownRefresh: function () {
if (this.data.postId) {
this.loadPostDetail(this.data.postId);
this.getforumReply(this.data.postId);
}
wx.stopPullDownRefresh();
},
// 页面上拉触底
onReachBottom: function () {
// 这里可以实现加载更多回复的逻辑
console.log('加载更多回复');
},
// 页面卸载
onUnload: function () {
// 移除键盘高度监听
wx.offKeyboardHeightChange();
}
});

4
pagesB/pages/onlineAsk/onlineAsk.json

@ -0,0 +1,4 @@
{
"navigationBarTitleText": "问答论坛",
"usingComponents": {}
}

152
pagesB/pages/onlineAsk/onlineAsk.wxml

@ -0,0 +1,152 @@
<view class="forum-page">
<!-- 帖子详情 -->
<scroll-view
class="post-detail-container"
scroll-y
scroll-with-animation
scroll-into-view="{{scrollToId}}"
bindscroll="onScroll"
>
<!-- 帖子主体 -->
<view class="post-main" id="post-main">
<!-- 帖子头部 -->
<view class="post-header">
<view class="user-info">
<image class="avatar" src="{{baseUrl+post.avatar}}" mode="aspectFill"></image>
<view class="user-detail">
<view class="username">{{post.nickName}}</view>
<view class="post-time">{{post.createdAt}}</view>
</view>
</view>
</view>
<!-- 帖子内容 -->
<view class="post-content">
<view class="post-title">{{post.title}}</view>
<view class="post-desc">{{post.content}}</view>
<!-- 帖子图片 -->
<view class="post-images" wx:if="{{post.images && post.images.length > 0}}">
<view class="images-container">
<block wx:for="{{images}}" wx:for-item="image" wx:for-index="imgIndex" wx:key="index">
<image
class="post-image"
src="{{baseUrl+image}}"
mode="aspectFill"
data-img-index="{{imgIndex}}"
bindtap="previewImage"
></image>
</block>
</view>
</view>
</view>
</view>
<!-- 回复列表 -->
<view class="replies-section" id="replies-section">
<view class="section-title">
<text class="title-text">全部回复</text>
<text class="reply-count">{{postHf.length}}条</text>
</view>
<view class="reply-list" wx:if="{{postHf.length > 0}}">
<block wx:for="{{postHf}}" wx:key="id" wx:for-index="replyIndex">
<view class="reply-item" id="reply-{{item.id}}">
<view class="reply-user">
<image class="reply-avatar" src="{{baseUrl+item.avatar}}" mode="aspectFill"></image>
<view class="reply-user-info">
<view class="reply-username">{{item.nickName}}</view>
<view class="reply-time">{{item.createdAt}}</view>
</view>
</view>
<view class="reply-content">{{item.content}}</view>
<!-- 回复操作 -->
<view class="reply-actions">
<view class="reply-action" bindtap="replyToUser" data-type="reply" data-index="{{replyIndex}}" data-username="{{item.nickName}}">
<image class="reply-action-icon" src="/pagesB/images/hf.png" mode="aspectFit"></image>
<text class="reply-action-text">回复</text>
</view>
</view>
<!-- 二级评论列表 -->
<view class="comment-list" wx:if="{{item.children.length > 0}}">
<block wx:for="{{item.children}}" wx:key="id" wx:for-index="commentIndex">
<view class="comment-item">
<view class="comment-user">
<image class="comment-avatar" src="{{baseUrl+item.avatar}}" mode="aspectFill"></image>
<view class="comment-user-info">
<view class="comment-username">{{item.nickName}}</view>
<view class="comment-time">{{item.createdAt}}</view>
</view>
</view>
<view class="comment-content">
{{item.content}}
</view>
</view>
</block>
</view>
</view>
</block>
</view>
<!-- 空回复状态 -->
<view class="empty-replies" wx:else>
<view class="empty-reply-text">还没有回复,快来第一个回复吧!</view>
</view>
</view>
<!-- 底部占位 -->
<view class="bottom-placeholder" style="height: 160rpx;"></view>
</scroll-view>
<!-- 底部回复输入栏 -->
<view class="bottom-input-container" style="transform: translateY({{inputTransformY}});">
<view class="input-wrapper">
<input
class="reply-input"
placeholder="{{replyPlaceholder}}"
value="{{replyContent}}"
bindinput="onReplyInput"
bindfocus="onInputFocus"
bindblur="onInputBlur"
bindconfirm="submitReply"
adjust-position="{{false}}"
focus="{{isInputFocused}}"
maxlength="500"
/>
<button
class="send-btn"
bindtap="submitReply"
disabled="{{!replyContent}}"
>
{{isSubmitting ? '发送中...' : '发送'}}
</button>
</view>
<!-- 回复对象显示 -->
<view class="reply-target" wx:if="{{replyTarget.nickName}}">
<text class="target-text">回复 {{replyTarget.nickName}}</text>
<text class="clear-target" bindtap="clearReplyTarget">×</text>
</view>
</view>
<!-- 图片预览模态框 -->
<view class="preview-overlay" wx:if="{{showPreview}}" catchtap="hidePreview">
<swiper class="preview-swiper" current="{{previewIndex}}" bindchange="onSwiperChange">
<block wx:for="{{previewImages}}" wx:key="index">
<swiper-item>
<image class="preview-image" src="{{baseUrl+item}}" mode="aspectFit"></image>
</swiper-item>
</block>
</swiper>
<view class="preview-indicator">{{previewIndex + 1}}/{{previewImages.length}}</view>
</view>
<!-- 加载提示 -->
<view class="loading" wx:if="{{loading}}">
加载中...
</view>
</view>

434
pagesB/pages/onlineAsk/onlineAsk.wxss

@ -0,0 +1,434 @@
.forum-page {
min-height: 100vh;
background-color: #f8f8f8;
position: relative;
}
/* 帖子详情容器 */
.post-detail-container {
height: 100vh;
box-sizing: border-box;
}
/* 帖子主体 */
.post-main {
background-color: white;
padding: 40rpx 30rpx;
margin-bottom: 20rpx;
}
.post-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 30rpx;
}
.user-info {
display: flex;
align-items: center;
}
.avatar {
width: 80rpx;
height: 80rpx;
border-radius: 50%;
margin-right: 20rpx;
}
.user-detail {
display: flex;
flex-direction: column;
}
.username {
font-size: 32rpx;
font-weight: bold;
color: #333;
margin-bottom: 8rpx;
}
.post-time {
font-size: 26rpx;
color: #999;
}
/* 帖子内容 */
.post-content {
margin-bottom: 30rpx;
}
.post-title {
font-size: 36rpx;
font-weight: bold;
color: #333;
margin-bottom: 24rpx;
line-height: 1.4;
}
.post-desc {
font-size: 30rpx;
color: #555;
line-height: 1.6;
margin-bottom: 30rpx;
}
/* 帖子图片 */
.post-images {
margin: 30rpx 0;
}
.images-container {
width: 100%;
display: grid;
grid-template-columns: repeat(3,1fr);
}
.post-image {
width: 200rpx;
height: 200rpx;
border-radius: 12rpx;
flex-shrink: 0;
}
/* 回复区域 */
.replies-section {
background-color: white;
border-radius: 20rpx 20rpx 0 0;
padding: 40rpx 30rpx;
margin: 0;
}
.section-title {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 30rpx;
padding-bottom: 20rpx;
border-bottom: 2rpx solid #f0f0f0;
}
.title-text {
font-size: 34rpx;
font-weight: bold;
color: #333;
}
.reply-count {
font-size: 28rpx;
color: #07c160;
font-weight: bold;
}
/* 回复列表 */
.reply-list {
margin-top: 10rpx;
}
.reply-item {
padding: 30rpx 0;
border-bottom: 1rpx solid #f8f8f8;
}
.reply-item:last-child {
border-bottom: none;
}
.reply-user {
display: flex;
align-items: center;
margin-bottom: 20rpx;
}
.reply-avatar {
width: 64rpx;
height: 64rpx;
border-radius: 50%;
margin-right: 20rpx;
}
.reply-user-info {
display: flex;
flex-direction: column;
}
.reply-username {
font-size: 28rpx;
color: #333;
margin-bottom: 6rpx;
font-weight: 500;
}
.reply-time {
font-size: 24rpx;
color: #999;
}
.reply-content {
font-size: 30rpx;
color: #444;
line-height: 1.5;
margin-left: 84rpx;
margin-bottom: 20rpx;
}
/* 回复操作 */
.reply-actions {
display: flex;
align-items: center;
margin-left: 84rpx;
margin-top: 20rpx;
gap: 40rpx;
}
.reply-action {
display: flex;
align-items: center;
color: #999;
font-size: 24rpx;
padding: 4rpx 8rpx;
}
.reply-action-icon {
width: 28rpx;
height: 28rpx;
margin-right: 8rpx;
}
.reply-action-text {
font-size: 24rpx;
color: #666;
}
/* 评论列表 */
.comment-list {
margin-left: 84rpx;
margin-top: 20rpx;
background-color: #fafafa;
border-radius: 12rpx;
padding: 20rpx;
}
.comment-item {
padding: 20rpx 0;
}
.comment-item:not(:last-child) {
border-bottom: 1rpx solid #eee;
}
.comment-user {
display: flex;
align-items: center;
margin-bottom: 12rpx;
}
.comment-avatar {
width: 48rpx;
height: 48rpx;
border-radius: 50%;
margin-right: 15rpx;
}
.comment-user-info {
display: flex;
flex-direction: column;
}
.comment-username {
font-size: 26rpx;
color: #666;
margin-bottom: 4rpx;
}
.comment-time {
font-size: 22rpx;
color: #999;
}
.comment-content {
font-size: 28rpx;
color: #555;
line-height: 1.4;
margin-left: 63rpx;
}
.comment-to {
color: #07c160;
font-weight: 500;
}
/* 评论操作 */
.comment-actions {
margin-left: 63rpx;
margin-top: 12rpx;
}
.comment-action {
font-size: 24rpx;
color: #999;
padding: 4rpx 8rpx;
}
/* 空回复状态 */
.empty-replies {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 100rpx 0;
text-align: center;
}
.empty-reply-text {
font-size: 28rpx;
color: #999;
}
.bottom-placeholder {
height: 160rpx;
}
/* 底部输入栏 */
.bottom-input-container {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background-color: white;
border-top: 1rpx solid #eee;
padding: 20rpx 30rpx;
box-shadow: 0 -4rpx 20rpx rgba(0, 0, 0, 0.05);
z-index: 100;
transition: transform 0.3s ease;
}
.input-wrapper {
display: flex;
align-items: center;
gap: 20rpx;
}
.reply-input {
flex: 1;
background-color: #f8f8f8;
border-radius: 40rpx;
padding: 10rpx 30rpx;
font-size: 30rpx;
}
.send-btn {
background-color: #07c160;
color: white;
font-size: 28rpx;
padding: 20rpx 40rpx;
line-height: normal;
border-radius: 40rpx;
margin: 0;
min-width: 120rpx;
transition: background-color 0.3s;
}
.send-btn[disabled] {
background-color: #cccccc;
color: white;
}
/* 回复对象显示 */
.reply-target {
display: flex;
justify-content: space-between;
align-items: center;
background-color: #f0f0f0;
border-radius: 20rpx;
padding: 16rpx 24rpx;
margin-top: 15rpx;
animation: slideIn 0.3s ease;
}
@keyframes slideIn {
from {
opacity: 0;
transform: translateY(-10rpx);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.target-text {
font-size: 26rpx;
color: #07c160;
font-weight: 500;
}
.clear-target {
font-size: 36rpx;
color: #999;
padding: 0 10rpx 4rpx 20rpx;
}
/* 图片预览 */
.preview-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.95);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
z-index: 1000;
}
.preview-swiper {
width: 100%;
height: 70vh;
}
.preview-image {
width: 100%;
height: 100%;
}
.preview-indicator {
position: absolute;
bottom: 60rpx;
color: white;
font-size: 28rpx;
background-color: rgba(0, 0, 0, 0.5);
padding: 10rpx 24rpx;
border-radius: 20rpx;
}
/* 加载提示 */
.loading {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 60rpx 0;
color: #999;
font-size: 28rpx;
}
/* 移除按钮边框 */
button::after {
border: none;
}
/* 滚动条样式 */
::-webkit-scrollbar {
width: 0;
height: 0;
color: transparent;
}

21
utils/api.js

@ -93,17 +93,12 @@ function wzdAdd(params) {
}
//在线问答列表
function forumList(params) {
http('/system/questions/list', 'get', params)
}
//在线问答详情
function forumDetails(params) {
http('/system/questions/'+params.data.id, 'get', params)
@ -139,6 +134,18 @@ function experiencezd(params) {
http('/vet/article/options', 'get', params)
}
// 新增经验分享
function shareAdd(params) {
http('/vet/article', 'post', params)
}
// 实名认证
function realName(params) {
http('/muhu/user/auth/submit', 'post', params)
@ -162,7 +169,7 @@ function today(params) {
export default { // 暴露接口
login,carousel,getPhoneNumber,article,articleDetails,articleZd,wzd,wzdAdd,
login,carousel,getPhoneNumber,article,articleDetails,articleZd,wzd,wzdAdd,shareAdd,
areaChildren,userCode,UserInfo,videoList,videoZd,videoDetails,forumList,forumAdd,forumDetails,
forumReply,commentReply,experience,experiencezd,experienceDetails,realName,revise,feedback,
today,carouselDetail,videoAdd,articleAdd,wzdxq

Loading…
Cancel
Save