12 changed files with 1499 additions and 427 deletions
@ -1,3 +1,4 @@ |
|||
node_modules |
|||
pages/test/ |
|||
pages/test-tools/ |
|||
pages/test-tools/ |
|||
server-example/db-query.js |
|||
@ -1,165 +0,0 @@ |
|||
App({ |
|||
// 全局事件总线功能
|
|||
eventBus: { |
|||
listeners: {}, |
|||
|
|||
// 监听事件
|
|||
on: function (event, callback) { |
|||
if (!this.listeners[event]) { |
|||
this.listeners[event] = []; |
|||
} |
|||
this.listeners[event].push(callback); |
|||
}, |
|||
|
|||
// 触发事件
|
|||
emit: function (event, data) { |
|||
if (this.listeners[event]) { |
|||
this.listeners[event].forEach(callback => { |
|||
try { |
|||
callback(data); |
|||
} catch (error) { |
|||
console.error('事件处理错误:', error); |
|||
} |
|||
}); |
|||
} |
|||
}, |
|||
|
|||
// 移除事件监听
|
|||
off: function (event, callback) { |
|||
if (this.listeners[event]) { |
|||
if (callback) { |
|||
this.listeners[event] = this.listeners[event].filter(cb => cb !== callback); |
|||
} else { |
|||
delete this.listeners[event]; |
|||
} |
|||
} |
|||
} |
|||
}, |
|||
|
|||
onLaunch: function () { |
|||
// 初始化应用
|
|||
console.log('App Launch') |
|||
// 初始化本地存储的标签和用户数据
|
|||
if (!wx.getStorageSync('users')) { |
|||
wx.setStorageSync('users', {}) |
|||
} |
|||
if (!wx.getStorageSync('tags')) { |
|||
wx.setStorageSync('tags', {}) |
|||
} |
|||
if (!wx.getStorageSync('goods')) { |
|||
// 初始化空的商品列表,不预置默认数据,由服务器获取
|
|||
wx.setStorageSync('goods', []) |
|||
} |
|||
if (!wx.getStorageSync('supplies')) { |
|||
// 初始化空的供应列表,不预置默认数据,由服务器获取
|
|||
wx.setStorageSync('supplies', []) |
|||
} |
|||
|
|||
// 检查是否是首次启动
|
|||
const isFirstLaunch = !wx.getStorageSync('hasLaunched') |
|||
if (isFirstLaunch) { |
|||
// 标记应用已经启动过
|
|||
wx.setStorageSync('hasLaunched', true) |
|||
|
|||
// 只有在首次启动时才检查用户身份并可能跳转
|
|||
const userId = wx.getStorageSync('userId') |
|||
if (userId) { |
|||
const users = wx.getStorageSync('users') |
|||
const user = users[userId] |
|||
if (user && user.type) { |
|||
// 延迟跳转,确保页面加载完成
|
|||
setTimeout(() => { |
|||
try { |
|||
if (user.type === 'buyer') { |
|||
wx.switchTab({ url: '/pages/buyer/index' }) |
|||
} else if (user.type === 'seller') { |
|||
wx.switchTab({ url: '/pages/seller/index' }) |
|||
} |
|||
} catch (e) { |
|||
console.error('启动时页面跳转异常:', e) |
|||
// 即使跳转失败,也不影响应用正常启动
|
|||
} |
|||
}, 100) |
|||
} |
|||
} |
|||
} |
|||
|
|||
// 获取本地存储的用户信息和用户类型
|
|||
const storedUserInfo = wx.getStorageSync('userInfo'); |
|||
const storedUserType = wx.getStorageSync('userType'); |
|||
|
|||
if (storedUserInfo) { |
|||
this.globalData.userInfo = storedUserInfo; |
|||
} |
|||
|
|||
if (storedUserType) { |
|||
this.globalData.userType = storedUserType; |
|||
} |
|||
|
|||
console.log('App初始化 - 用户类型:', this.globalData.userType); |
|||
console.log('App初始化 - 用户信息:', this.globalData.userInfo); |
|||
|
|||
// 获取用户信息
|
|||
wx.getSetting({ |
|||
success: res => { |
|||
if (res.authSetting['scope.userInfo']) { |
|||
// 已经授权,可以直接调用 getUserInfo 获取头像昵称
|
|||
wx.getUserInfo({ |
|||
success: res => { |
|||
// 将用户信息存储到本地和全局,不自动登录
|
|||
// 登录流程将在用户点击登录按钮时触发
|
|||
console.log('获取用户信息成功,暂不自动登录'); |
|||
|
|||
// 更新全局用户信息
|
|||
this.globalData.userInfo = res.userInfo; |
|||
|
|||
// 更新本地存储的用户信息
|
|||
wx.setStorageSync('userInfo', res.userInfo); |
|||
}, |
|||
fail: error => { |
|||
console.error('获取用户信息失败:', error); |
|||
} |
|||
}); |
|||
} |
|||
}, |
|||
fail: error => { |
|||
console.error('获取用户设置失败:', error); |
|||
} |
|||
}); |
|||
}, |
|||
onShow: function () { |
|||
console.log('App Show') |
|||
}, |
|||
onHide: function () { |
|||
console.log('App Hide') |
|||
}, |
|||
|
|||
// 更新当前选中的tab
|
|||
updateCurrentTab(tabKey) { |
|||
if (this.globalData) { |
|||
this.globalData.currentTab = tabKey |
|||
} |
|||
}, |
|||
|
|||
// 跳转到估价页面
|
|||
goToEvaluatePage() { |
|||
wx.navigateTo({ |
|||
url: '/pages/evaluate/index' |
|||
}) |
|||
}, |
|||
|
|||
// 上传手机号数据
|
|||
async uploadPhoneNumberData(phoneData) { |
|||
const API = require('./utils/api.js') |
|||
return await API.uploadPhoneNumberData(phoneData) |
|||
}, |
|||
|
|||
globalData: { |
|||
userInfo: null, |
|||
userType: 'customer', // 默认客户类型
|
|||
currentTab: 'index', // 当前选中的tab
|
|||
showTabBar: true, // 控制底部tab-bar显示状态
|
|||
// 测试环境配置
|
|||
isTestMode: false |
|||
} |
|||
}) |
|||
@ -0,0 +1,221 @@ |
|||
// pages/customer-service/detail/index.js
|
|||
const api = require('../../../utils/api'); |
|||
|
|||
Page({ |
|||
data: { |
|||
customerData: null, |
|||
customerServices: [ |
|||
{ |
|||
id: 1, |
|||
managerId: 'PM001', |
|||
managercompany: '鸡蛋贸易有限公司', |
|||
managerdepartment: '采购部', |
|||
organization: '鸡蛋采购组', |
|||
projectName: '高级采购经理', |
|||
name: '张三', |
|||
alias: '张经理', |
|||
phoneNumber: '13800138001', |
|||
avatarUrl: '', |
|||
score: 999, |
|||
isOnline: true, |
|||
responsibleArea: '华北区鸡蛋采购', |
|||
experience: '1-3年', |
|||
serviceCount: 200, |
|||
purchaseCount: 15000, |
|||
profitIncreaseRate: 15, |
|||
profitFarmCount: 120, |
|||
skills: ['渠道拓展', '供应商维护', '质量把控', '精准把控市场价格'] |
|||
}, |
|||
{ |
|||
id: 2, |
|||
managerId: 'PM002', |
|||
managercompany: '鸡蛋贸易有限公司', |
|||
managerdepartment: '采购部', |
|||
organization: '全国采购组', |
|||
projectName: '采购经理', |
|||
name: '李四', |
|||
alias: '李经理', |
|||
phoneNumber: '13900139002', |
|||
avatarUrl: '', |
|||
score: 998, |
|||
isOnline: true, |
|||
responsibleArea: '全国鸡蛋采购', |
|||
experience: '2-3年', |
|||
serviceCount: 200, |
|||
purchaseCount: 20000, |
|||
profitIncreaseRate: 18, |
|||
profitFarmCount: 150, |
|||
skills: ['精准把控市场价格', '渠道拓展', '供应商维护'] |
|||
}, |
|||
{ |
|||
id: 3, |
|||
managerId: 'PM003', |
|||
managercompany: '鸡蛋贸易有限公司', |
|||
managerdepartment: '采购部', |
|||
organization: '华东采购组', |
|||
projectName: '采购专员', |
|||
name: '王五', |
|||
alias: '王专员', |
|||
phoneNumber: '13700137003', |
|||
avatarUrl: '', |
|||
score: 997, |
|||
isOnline: false, |
|||
responsibleArea: '华东区鸡蛋采购', |
|||
experience: '1-2年', |
|||
serviceCount: 150, |
|||
purchaseCount: 12000, |
|||
profitIncreaseRate: 12, |
|||
profitFarmCount: 80, |
|||
skills: ['质量把控', '供应商维护'] |
|||
}, |
|||
{ |
|||
id: 4, |
|||
managerId: 'PM004', |
|||
managercompany: '鸡蛋贸易有限公司', |
|||
managerdepartment: '采购部', |
|||
organization: '华南采购组', |
|||
projectName: '高级采购经理', |
|||
name: '赵六', |
|||
alias: '赵经理', |
|||
phoneNumber: '13600136004', |
|||
avatarUrl: '', |
|||
score: 996, |
|||
isOnline: true, |
|||
responsibleArea: '华南区鸡蛋采购', |
|||
experience: '3-5年', |
|||
serviceCount: 250, |
|||
purchaseCount: 25000, |
|||
profitIncreaseRate: 20, |
|||
profitFarmCount: 180, |
|||
skills: ['精准把控市场价格', '渠道拓展', '质量把控', '供应商维护'] |
|||
} |
|||
] |
|||
}, |
|||
|
|||
onLoad: function (options) { |
|||
// 获取传递过来的客服ID
|
|||
const { id } = options; |
|||
// 根据ID查找客服数据
|
|||
const customerData = this.data.customerServices.find(item => item.id === parseInt(id)); |
|||
|
|||
if (customerData) { |
|||
this.setData({ |
|||
customerData: customerData |
|||
}); |
|||
// 设置导航栏标题
|
|||
wx.setNavigationBarTitle({ |
|||
title: `${customerData.alias} - 客服详情`, |
|||
}); |
|||
} else { |
|||
// 如果找不到对应ID的客服,显示错误提示
|
|||
wx.showToast({ |
|||
title: '未找到客服信息', |
|||
icon: 'none' |
|||
}); |
|||
} |
|||
}, |
|||
|
|||
onShow() { |
|||
// 更新自定义tabBar状态
|
|||
if (typeof this.getTabBar === 'function' && this.getTabBar()) { |
|||
this.getTabBar().setData({ |
|||
selected: -1 // 不选中任何tab
|
|||
}); |
|||
} |
|||
}, |
|||
|
|||
// 返回上一页
|
|||
onBack: function () { |
|||
wx.navigateBack(); |
|||
}, |
|||
|
|||
// 在线沟通
|
|||
onChat: function () { |
|||
const { customerData } = this.data; |
|||
if (!customerData) return; |
|||
|
|||
// 获取当前用户的手机号
|
|||
let userPhone = ''; |
|||
try { |
|||
// 尝试从不同的存储位置获取手机号
|
|||
const userInfo = wx.getStorageSync('userInfo'); |
|||
if (userInfo && userInfo.phoneNumber) { |
|||
userPhone = userInfo.phoneNumber; |
|||
} else { |
|||
// 尝试从其他可能的存储位置获取
|
|||
const users = wx.getStorageSync('users') || {}; |
|||
const userId = wx.getStorageSync('userId'); |
|||
if (userId && users[userId] && users[userId].phoneNumber) { |
|||
userPhone = users[userId].phoneNumber; |
|||
} else { |
|||
userPhone = wx.getStorageSync('phoneNumber'); |
|||
} |
|||
} |
|||
} catch (e) { |
|||
console.error('获取用户手机号失败:', e); |
|||
} |
|||
|
|||
console.log('当前用户手机号:', userPhone); |
|||
console.log('客服手机号:', customerData.phoneNumber); |
|||
|
|||
// 验证手机号
|
|||
if (!userPhone) { |
|||
wx.showToast({ |
|||
title: '请先登录获取手机号', |
|||
icon: 'none' |
|||
}); |
|||
return; |
|||
} |
|||
|
|||
// 显示加载提示
|
|||
wx.showLoading({ |
|||
title: '正在建立聊天...', |
|||
}); |
|||
|
|||
// 调用API添加聊天记录
|
|||
api.addChatRecord(userPhone, customerData.phoneNumber).then(res => { |
|||
console.log('添加聊天记录成功:', res); |
|||
// 隐藏加载提示
|
|||
wx.hideLoading(); |
|||
// 导航到聊天页面
|
|||
wx.navigateTo({ |
|||
url: `/pages/chat/index?id=${customerData.id}&name=${customerData.alias}&phone=${customerData.phoneNumber}` |
|||
}); |
|||
}).catch(err => { |
|||
console.error('添加聊天记录失败:', err); |
|||
// 隐藏加载提示
|
|||
wx.hideLoading(); |
|||
// 显示错误提示
|
|||
wx.showToast({ |
|||
title: '建立聊天失败: ' + (err.message || '请稍后重试'), |
|||
icon: 'none' |
|||
}); |
|||
}); |
|||
}, |
|||
|
|||
// 拨打电话
|
|||
onCall: function () { |
|||
const { customerData } = this.data; |
|||
if (customerData && customerData.phoneNumber) { |
|||
wx.makePhoneCall({ |
|||
phoneNumber: customerData.phoneNumber, |
|||
success: function () { |
|||
console.log('拨打电话成功'); |
|||
}, |
|||
fail: function () { |
|||
console.log('拨打电话失败'); |
|||
} |
|||
}); |
|||
} |
|||
}, |
|||
|
|||
// 分享功能
|
|||
onShareAppMessage: function () { |
|||
const { customerData } = this.data; |
|||
return { |
|||
title: `${customerData?.alias || '优秀客服'} - 鸡蛋贸易平台`, |
|||
path: `/pages/customer-service/detail/index?id=${customerData?.id}`, |
|||
imageUrl: '' |
|||
}; |
|||
} |
|||
}); |
|||
@ -1,9 +1,7 @@ |
|||
{ |
|||
"navigationBarTitleText": "客服详情", |
|||
"navigationStyle": "custom", |
|||
"usingComponents": { |
|||
"custom-header": "../../components/custom-header/custom-header", |
|||
"service-card": "../../components/service-card/service-card", |
|||
"faq-item": "../../components/faq-item/faq-item" |
|||
} |
|||
} |
|||
"navigationBarBackgroundColor": "#ffffff", |
|||
"navigationBarTextStyle": "black", |
|||
"backgroundColor": "#f8f8f8", |
|||
"usingComponents": {} |
|||
} |
|||
|
|||
@ -1,65 +1,93 @@ |
|||
<!-- pages/customer-service/detail/index.wxml --> |
|||
<view class="container"> |
|||
<view class="service-detail-header"> |
|||
<view class="header-back" bindtap="onBack"> |
|||
<text>←</text> |
|||
<!-- 顶部导航栏 --> |
|||
<view class="nav-bar"> |
|||
<view class="nav-left" bindtap="onBack"> |
|||
<text class="back-icon">返回</text> |
|||
</view> |
|||
<view class="nav-title">客服详情</view> |
|||
<view class="nav-right"> |
|||
<text class="share-icon">📤</text> |
|||
</view> |
|||
<text class="header-title">客服详情</text> |
|||
<view class="header-placeholder"></view> |
|||
</view> |
|||
|
|||
<view class="service-detail-content"> |
|||
<view class="service-info-section"> |
|||
<view class="service-avatar"> |
|||
<text>💁</text> |
|||
|
|||
<!-- 客服基本信息 --> |
|||
<view class="info-section"> |
|||
<view class="header-info"> |
|||
<view class="avatar-container"> |
|||
<image class="avatar" src="{{customerData.avatarUrl || '/images/default-avatar.png'}}" mode="aspectFill" /> |
|||
<view wx:if="{{customerData.isOnline}}" class="online-indicator-large">在线</view> |
|||
<view wx:else class="offline-indicator-large">离线</view> |
|||
</view> |
|||
<view class="service-basic-info"> |
|||
<text class="service-name">客服小王</text> |
|||
<text class="service-status">在线</text> |
|||
<view class="header-details"> |
|||
<view class="name-score-row"> |
|||
<text class="name-large">{{customerData.alias}}</text> |
|||
<text class="score-large">{{customerData.score}} 鸡蛋分</text> |
|||
</view> |
|||
<text class="position">{{customerData.projectName}}</text> |
|||
<text class="company-large">{{customerData.managercompany}}</text> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="service-details"> |
|||
<view class="detail-item"> |
|||
<text class="detail-label">服务类型</text> |
|||
<text class="detail-value">在线客服</text> |
|||
</view> |
|||
|
|||
<!-- 核心信息卡片 --> |
|||
<view class="core-info-section"> |
|||
<view class="info-card"> |
|||
<view class="info-item"> |
|||
<text class="info-label">负责区域</text> |
|||
<text class="info-value">{{customerData.responsibleArea}}</text> |
|||
</view> |
|||
<view class="info-item"> |
|||
<text class="info-label">联系电话</text> |
|||
<text class="info-value phone-number" bindtap="onCall">{{customerData.phoneNumber}}</text> |
|||
</view> |
|||
<view class="detail-item"> |
|||
<text class="detail-label">服务时间</text> |
|||
<text class="detail-value">24小时在线</text> |
|||
<view class="info-item"> |
|||
<text class="info-label">工作经验</text> |
|||
<text class="info-value">服务平台{{customerData.experience}}</text> |
|||
</view> |
|||
<view class="detail-item"> |
|||
<text class="detail-label">专业领域</text> |
|||
<text class="detail-value">产品咨询、订单处理、售后支持</text> |
|||
<view class="info-item"> |
|||
<text class="info-label">服务规模</text> |
|||
<text class="info-value">服务{{customerData.serviceCount}}家鸡场</text> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="contact-section"> |
|||
<text class="section-title">联系方式</text> |
|||
<button class="contact-button" bindtap="startChat"> |
|||
<text>💬 开始聊天</text> |
|||
</button> |
|||
<button class="contact-button phone-button" open-type="makePhoneCall" phone-number="400-123-4567"> |
|||
<text>📞 电话联系</text> |
|||
</button> |
|||
<button class="contact-button email-button" bindtap="sendEmail"> |
|||
<text>📧 发送邮件</text> |
|||
</button> |
|||
</view> |
|||
|
|||
<!-- 专业技能标签 --> |
|||
<view class="skills-section"> |
|||
<view class="section-title">专业技能</view> |
|||
<view class="skills-container"> |
|||
<view wx:for="{{customerData.skills}}" wx:key="index" class="skill-tag"> |
|||
{{item}} |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="faq-section"> |
|||
<text class="section-title">常见问题</text> |
|||
<view class="faq-item" bindtap="onFaqItemTap"> |
|||
<text class="faq-question">如何查询订单状态?</text> |
|||
<text class="faq-arrow">></text> |
|||
</view> |
|||
|
|||
<!-- 业绩数据 --> |
|||
<view class="performance-section"> |
|||
<view class="section-title">业绩数据</view> |
|||
<view class="performance-cards"> |
|||
<view class="performance-card"> |
|||
<view class="performance-number">{{customerData.purchaseCount}}</view> |
|||
<view class="performance-label">累计采购鸡蛋(件)</view> |
|||
</view> |
|||
<view class="faq-item" bindtap="onFaqItemTap"> |
|||
<text class="faq-question">如何申请退款?</text> |
|||
<text class="faq-arrow">></text> |
|||
<view class="performance-card"> |
|||
<view class="performance-number">{{customerData.profitFarmCount}}</view> |
|||
<view class="performance-label">累计服务鸡场(家)</view> |
|||
</view> |
|||
<view class="faq-item" bindtap="onFaqItemTap"> |
|||
<text class="faq-question">物流配送时间是多久?</text> |
|||
<text class="faq-arrow">></text> |
|||
<view class="performance-card"> |
|||
<view class="performance-number profit-rate">{{customerData.profitIncreaseRate}}%</view> |
|||
<view class="performance-label">平均盈利增长</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 底部操作按钮 --> |
|||
<view class="bottom-actions"> |
|||
<view class="action-button chat-button" bindtap="onChat"> |
|||
<text class="button-text">💬 在线沟通</text> |
|||
</view> |
|||
<view class="action-button call-button" bindtap="onCall"> |
|||
<text class="button-text">📞 电话联系</text> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
@ -1,180 +1,317 @@ |
|||
/* pages/customer-service/detail/index.wxss */ |
|||
.container { |
|||
background-color: #f5f5f5; |
|||
padding-bottom: 100rpx; |
|||
background-color: #f8f8f8; |
|||
min-height: 100vh; |
|||
} |
|||
|
|||
.service-detail-header { |
|||
background-color: #fff; |
|||
/* 顶部导航栏 */ |
|||
.nav-bar { |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: space-between; |
|||
padding: 16rpx 24rpx; |
|||
border-bottom: 1rpx solid #e8e8e8; |
|||
position: sticky; |
|||
align-items: center; |
|||
padding: 44rpx 30rpx 20rpx; |
|||
background-color: #fff; |
|||
border-bottom: 1rpx solid #f0f0f0; |
|||
position: fixed; |
|||
top: 0; |
|||
z-index: 100; |
|||
left: 0; |
|||
right: 0; |
|||
z-index: 1000; |
|||
width: 100%; |
|||
box-sizing: border-box; |
|||
} |
|||
|
|||
.header-back, .header-placeholder { |
|||
width: 60rpx; |
|||
height: 60rpx; |
|||
.nav-left { |
|||
width: 80rpx; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
font-size: 32rpx; |
|||
font-weight: bold; |
|||
justify-content: flex-start; |
|||
} |
|||
|
|||
.header-title { |
|||
.back-icon { |
|||
font-size: 32rpx; |
|||
color: #333; |
|||
font-weight: normal; |
|||
} |
|||
|
|||
.nav-title { |
|||
font-size: 36rpx; |
|||
font-weight: bold; |
|||
color: #333; |
|||
flex: 1; |
|||
text-align: center; |
|||
} |
|||
|
|||
.service-detail-content { |
|||
padding: 20rpx; |
|||
.nav-right { |
|||
display: flex; |
|||
align-items: center; |
|||
gap: 30rpx; |
|||
} |
|||
|
|||
.service-info-section { |
|||
.share-icon { |
|||
font-size: 32rpx; |
|||
color: #333; |
|||
} |
|||
|
|||
/* 客服基本信息 */ |
|||
.info-section { |
|||
background-color: #fff; |
|||
border-radius: 16rpx; |
|||
padding: 32rpx; |
|||
padding: 120rpx 30rpx 30rpx; |
|||
margin-bottom: 20rpx; |
|||
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05); |
|||
} |
|||
|
|||
.header-info { |
|||
display: flex; |
|||
align-items: center; |
|||
} |
|||
|
|||
.service-avatar { |
|||
width: 120rpx; |
|||
height: 120rpx; |
|||
background-color: #f0f0f0; |
|||
.avatar-container { |
|||
width: 160rpx; |
|||
height: 160rpx; |
|||
margin-right: 30rpx; |
|||
position: relative; |
|||
} |
|||
|
|||
.avatar { |
|||
width: 100%; |
|||
height: 100%; |
|||
border-radius: 50%; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
font-size: 64rpx; |
|||
margin-right: 32rpx; |
|||
background-color: #f0f0f0; |
|||
border: 4rpx solid #f0f0f0; |
|||
} |
|||
|
|||
.online-indicator-large { |
|||
position: absolute; |
|||
bottom: 0; |
|||
right: 0; |
|||
background-color: #52c41a; |
|||
color: white; |
|||
font-size: 24rpx; |
|||
padding: 8rpx 20rpx; |
|||
border-radius: 20rpx; |
|||
border: 4rpx solid white; |
|||
} |
|||
|
|||
.service-basic-info { |
|||
.offline-indicator-large { |
|||
position: absolute; |
|||
bottom: 0; |
|||
right: 0; |
|||
background-color: #999; |
|||
color: white; |
|||
font-size: 24rpx; |
|||
padding: 8rpx 20rpx; |
|||
border-radius: 20rpx; |
|||
border: 4rpx solid white; |
|||
} |
|||
|
|||
.header-details { |
|||
flex: 1; |
|||
} |
|||
|
|||
.service-name { |
|||
display: block; |
|||
font-size: 36rpx; |
|||
.name-score-row { |
|||
display: flex; |
|||
align-items: center; |
|||
margin-bottom: 12rpx; |
|||
} |
|||
|
|||
.name-large { |
|||
font-size: 40rpx; |
|||
font-weight: bold; |
|||
color: #333; |
|||
margin-right: 16rpx; |
|||
} |
|||
|
|||
.score-large { |
|||
font-size: 28rpx; |
|||
color: #fff; |
|||
background: linear-gradient(135deg, #ffb800, #ff7700); |
|||
padding: 6rpx 16rpx; |
|||
border-radius: 20rpx; |
|||
} |
|||
|
|||
.position { |
|||
font-size: 32rpx; |
|||
color: #666; |
|||
margin-bottom: 8rpx; |
|||
} |
|||
|
|||
.service-status { |
|||
display: block; |
|||
.company-large { |
|||
font-size: 28rpx; |
|||
color: #07C160; |
|||
color: #999; |
|||
} |
|||
|
|||
.service-details { |
|||
background-color: #fff; |
|||
border-radius: 16rpx; |
|||
padding: 24rpx; |
|||
/* 核心信息卡片 */ |
|||
.core-info-section { |
|||
padding: 0 30rpx; |
|||
margin-bottom: 20rpx; |
|||
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05); |
|||
} |
|||
|
|||
.detail-item { |
|||
.info-card { |
|||
background-color: #fff; |
|||
border-radius: 24rpx; |
|||
padding: 30rpx; |
|||
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05); |
|||
} |
|||
|
|||
.info-item { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
padding: 16rpx 0; |
|||
padding: 20rpx 0; |
|||
border-bottom: 1rpx solid #f0f0f0; |
|||
} |
|||
|
|||
.detail-item:last-child { |
|||
.info-item:last-child { |
|||
border-bottom: none; |
|||
} |
|||
|
|||
.detail-label { |
|||
font-size: 28rpx; |
|||
.info-label { |
|||
font-size: 30rpx; |
|||
color: #666; |
|||
} |
|||
|
|||
.detail-value { |
|||
font-size: 28rpx; |
|||
.info-value { |
|||
font-size: 30rpx; |
|||
color: #333; |
|||
text-align: right; |
|||
flex: 1; |
|||
margin-left: 40rpx; |
|||
margin-left: 30rpx; |
|||
} |
|||
|
|||
.phone-number { |
|||
color: #1890ff; |
|||
} |
|||
|
|||
.contact-section, .faq-section { |
|||
/* 专业技能标签 */ |
|||
.skills-section { |
|||
background-color: #fff; |
|||
border-radius: 16rpx; |
|||
padding: 24rpx; |
|||
padding: 30rpx; |
|||
margin-bottom: 20rpx; |
|||
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05); |
|||
} |
|||
|
|||
.section-title { |
|||
display: block; |
|||
font-size: 32rpx; |
|||
font-weight: bold; |
|||
color: #333; |
|||
margin-bottom: 24rpx; |
|||
} |
|||
|
|||
.contact-button { |
|||
display: block; |
|||
width: 100%; |
|||
background-color: #FF6B81; |
|||
color: #fff; |
|||
font-size: 32rpx; |
|||
font-weight: bold; |
|||
padding: 24rpx; |
|||
border-radius: 12rpx; |
|||
border: none; |
|||
margin-bottom: 16rpx; |
|||
line-height: normal; |
|||
height: auto; |
|||
margin-bottom: 20rpx; |
|||
} |
|||
|
|||
.contact-button:last-child { |
|||
margin-bottom: 0; |
|||
.skills-container { |
|||
display: flex; |
|||
flex-wrap: wrap; |
|||
gap: 16rpx; |
|||
padding-bottom: 10rpx; |
|||
} |
|||
|
|||
.contact-button.phone-button { |
|||
background-color: #07C160; |
|||
.skill-tag { |
|||
background-color: #f0f9ff; |
|||
color: #1890ff; |
|||
font-size: 26rpx; |
|||
padding: 12rpx 24rpx; |
|||
border-radius: 20rpx; |
|||
border: 1rpx solid #bae7ff; |
|||
transition: all 0.2s ease; |
|||
} |
|||
|
|||
.contact-button.email-button { |
|||
background-color: #1989fa; |
|||
.skill-tag:active { |
|||
background-color: #bae7ff; |
|||
transform: scale(0.98); |
|||
} |
|||
|
|||
.contact-button:hover { |
|||
opacity: 0.9; |
|||
/* 业绩数据 */ |
|||
.performance-section { |
|||
background-color: #fff; |
|||
padding: 30rpx; |
|||
margin-bottom: 120rpx; |
|||
} |
|||
|
|||
.faq-item { |
|||
.performance-cards { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
padding: 24rpx 0; |
|||
border-bottom: 1rpx solid #f0f0f0; |
|||
gap: 20rpx; |
|||
} |
|||
|
|||
.faq-item:last-child { |
|||
border-bottom: none; |
|||
.performance-card { |
|||
flex: 1; |
|||
background-color: #f9f9f9; |
|||
padding: 24rpx; |
|||
border-radius: 16rpx; |
|||
text-align: center; |
|||
transition: all 0.2s ease; |
|||
position: relative; |
|||
overflow: hidden; |
|||
} |
|||
|
|||
.faq-question { |
|||
font-size: 28rpx; |
|||
.performance-card:active { |
|||
transform: translateY(2rpx); |
|||
background-color: #f0f0f0; |
|||
} |
|||
|
|||
.performance-number { |
|||
font-size: 36rpx; |
|||
font-weight: bold; |
|||
color: #333; |
|||
margin-bottom: 12rpx; |
|||
} |
|||
|
|||
.performance-number.profit-rate { |
|||
color: #52c41a; |
|||
} |
|||
|
|||
.performance-label { |
|||
font-size: 24rpx; |
|||
color: #666; |
|||
line-height: 1.4; |
|||
} |
|||
|
|||
/* 底部操作按钮 */ |
|||
.bottom-actions { |
|||
position: fixed; |
|||
bottom: 0; |
|||
left: 0; |
|||
right: 0; |
|||
display: flex; |
|||
background-color: #fff; |
|||
padding: 20rpx 30rpx; |
|||
border-top: 1rpx solid #f0f0f0; |
|||
gap: 20rpx; |
|||
} |
|||
|
|||
.action-button { |
|||
flex: 1; |
|||
padding: 24rpx; |
|||
border-radius: 40rpx; |
|||
text-align: center; |
|||
font-size: 32rpx; |
|||
font-weight: 500; |
|||
transition: all 0.2s ease; |
|||
position: relative; |
|||
overflow: hidden; |
|||
} |
|||
|
|||
.faq-arrow { |
|||
font-size: 28rpx; |
|||
color: #999; |
|||
margin-left: 20rpx; |
|||
} |
|||
.action-button:active { |
|||
transform: scale(0.98); |
|||
opacity: 0.9; |
|||
} |
|||
|
|||
.chat-button:active { |
|||
background-color: #096dd9; |
|||
} |
|||
|
|||
.call-button:active { |
|||
background-color: #389e0d; |
|||
} |
|||
|
|||
.chat-button { |
|||
background-color: #1890ff; |
|||
color: white; |
|||
} |
|||
|
|||
.call-button { |
|||
background-color: #52c41a; |
|||
color: white; |
|||
} |
|||
|
|||
.button-text { |
|||
font-size: 30rpx; |
|||
} |
|||
|
|||
@ -1,55 +1,377 @@ |
|||
// pages/customer-service/index.js
|
|||
const api = require('../../utils/api'); |
|||
Page({ |
|||
// 客服数据,将从后端动态获取
|
|||
data: { |
|||
serviceList: [] |
|||
customerServices: [], |
|||
filteredServices: [], |
|||
searchKeyword: '', |
|||
selectedArea: '全部', |
|||
totalCount: 0, |
|||
onlineCount: 0 |
|||
}, |
|||
|
|||
// 获取客服列表的方法
|
|||
async fetchCustomerServices() { |
|||
try { |
|||
console.log('开始请求客服列表...'); |
|||
// 导入API工具并使用正确的请求方法
|
|||
const api = require('../../utils/api'); |
|||
|
|||
const res = await new Promise((resolve, reject) => { |
|||
wx.request({ |
|||
url: 'http://localhost:3003/api/managers', |
|||
method: 'GET', |
|||
timeout: 15000, |
|||
header: { |
|||
'content-type': 'application/json' |
|||
}, |
|||
success: resolve, |
|||
fail: (error) => { |
|||
console.error('网络请求失败:', error); |
|||
reject(error); |
|||
} |
|||
}); |
|||
}); |
|||
|
|||
console.log('API响应状态码:', res?.statusCode); |
|||
console.log('API响应数据:', res?.data ? JSON.stringify(res.data) : 'undefined'); |
|||
|
|||
// 更宽松的响应检查,确保能处理各种有效的响应格式
|
|||
if (res && res.statusCode === 200 && res.data) { |
|||
// 无论success字段是否存在,只要有data字段就尝试处理
|
|||
const dataSource = res.data.data || res.data; |
|||
if (Array.isArray(dataSource)) { |
|||
const processedData = dataSource.map(item => ({ |
|||
id: item.id || `id_${Date.now()}_${Math.random()}`, // 确保有id
|
|||
managerId: item.managerId || '', |
|||
managercompany: item.managercompany || '', |
|||
managerdepartment: item.managerdepartment || '', |
|||
organization: item.organization || '', |
|||
projectName: item.projectName || '', |
|||
name: item.name || '未知', |
|||
alias: item.alias || item.name || '未知', |
|||
phoneNumber: item.phoneNumber || '', |
|||
avatarUrl: item.avatar || item.avatarUrl || '', // 兼容avatar和avatarUrl
|
|||
score: Math.floor(Math.random() * 20) + 980, // 随机生成分数
|
|||
isOnline: !!item.online, // 转换为布尔值
|
|||
responsibleArea: `${this.getRandomArea()}鸡蛋采购`, |
|||
experience: this.getRandomExperience(), |
|||
serviceCount: this.getRandomNumber(100, 300), |
|||
purchaseCount: this.getRandomNumber(10000, 30000), |
|||
profitIncreaseRate: this.getRandomNumber(10, 25), |
|||
profitFarmCount: this.getRandomNumber(50, 200), |
|||
skills: this.getRandomSkills() |
|||
})); |
|||
console.log('处理后的数据数量:', processedData.length); |
|||
return processedData; |
|||
} else { |
|||
console.error('响应数据格式错误,不是预期的数组格式:', dataSource); |
|||
return []; |
|||
} |
|||
} else { |
|||
console.error('获取客服列表失败,状态码:', res?.statusCode, '响应数据:', res?.data); |
|||
return []; |
|||
} |
|||
} catch (error) { |
|||
console.error('请求客服列表出错:', error); |
|||
// 网络错误时显示提示
|
|||
wx.showToast({ |
|||
title: '网络请求失败,请检查网络连接', |
|||
icon: 'none' |
|||
}); |
|||
return []; |
|||
} |
|||
}, |
|||
|
|||
onLoad: function (options) { |
|||
this.loadServiceInfo(); |
|||
// 辅助函数:生成随机区域
|
|||
getRandomArea() { |
|||
const areas = ['华北区', '华东区', '华南区', '全国', '西南区', '西北区', '东北区']; |
|||
return areas[Math.floor(Math.random() * areas.length)]; |
|||
}, |
|||
|
|||
loadServiceInfo: function () { |
|||
// 模拟加载客服信息
|
|||
const serviceList = [ |
|||
{ |
|||
id: 1, |
|||
type: 'phone', |
|||
name: '电话客服', |
|||
desc: '周一至周日 9:00-18:00', |
|||
number: '400-123-4567' |
|||
}, |
|||
{ |
|||
id: 2, |
|||
type: 'online', |
|||
name: '在线客服', |
|||
desc: '24小时在线为您服务' |
|||
}, |
|||
{ |
|||
id: 3, |
|||
type: 'email', |
|||
name: '邮箱客服', |
|||
desc: 'service@example.com' |
|||
// 辅助函数:生成随机工作经验
|
|||
getRandomExperience() { |
|||
const experiences = ['1-2年', '1-3年', '2-3年', '3-5年', '5年以上']; |
|||
return experiences[Math.floor(Math.random() * experiences.length)]; |
|||
}, |
|||
|
|||
// 辅助函数:生成随机数字
|
|||
getRandomNumber(min, max) { |
|||
return Math.floor(Math.random() * (max - min + 1)) + min; |
|||
}, |
|||
|
|||
// 辅助函数:生成随机技能
|
|||
getRandomSkills() { |
|||
const allSkills = ['渠道拓展', '供应商维护', '质量把控', '精准把控市场价格', '谈判技巧', '库存管理']; |
|||
const skillCount = Math.floor(Math.random() * 3) + 2; // 2-4个技能
|
|||
const selectedSkills = []; |
|||
|
|||
while (selectedSkills.length < skillCount) { |
|||
const skill = allSkills[Math.floor(Math.random() * allSkills.length)]; |
|||
if (!selectedSkills.includes(skill)) { |
|||
selectedSkills.push(skill); |
|||
} |
|||
} |
|||
|
|||
return selectedSkills; |
|||
}, |
|||
|
|||
|
|||
|
|||
// 定期刷新客服列表(每30秒)
|
|||
startPeriodicRefresh() { |
|||
this.refreshTimer = setInterval(() => { |
|||
console.log('定期刷新客服列表...'); |
|||
this.loadCustomerServices(); |
|||
}, 30000); |
|||
}, |
|||
|
|||
// 停止定期刷新
|
|||
stopPeriodicRefresh() { |
|||
if (this.refreshTimer) { |
|||
clearInterval(this.refreshTimer); |
|||
this.refreshTimer = null; |
|||
} |
|||
}, |
|||
|
|||
// 加载客服列表
|
|||
async loadCustomerServices() { |
|||
console.log('开始加载客服列表...'); |
|||
// 确保在开始时调用hideLoading,防止重复调用showLoading
|
|||
try { |
|||
wx.hideLoading(); |
|||
} catch (e) { |
|||
console.log('没有正在显示的loading'); |
|||
} |
|||
|
|||
wx.showLoading({ title: '加载中...' }); |
|||
try { |
|||
const services = await this.fetchCustomerServices(); |
|||
console.log('获取到的客服数量:', services.length); |
|||
|
|||
// 计算在线数量
|
|||
const onlineCount = services.filter(item => item.isOnline).length; |
|||
console.log('在线客服数量:', onlineCount); |
|||
|
|||
// 更新数据
|
|||
this.setData({ |
|||
customerServices: services, |
|||
totalCount: services.length, |
|||
onlineCount: onlineCount |
|||
}); |
|||
console.log('数据更新成功'); |
|||
|
|||
// 应用当前的筛选条件
|
|||
this.filterServices(); |
|||
console.log('筛选条件应用完成'); |
|||
|
|||
// 如果没有数据,显示提示(确保在hideLoading后显示)
|
|||
if (services.length === 0) { |
|||
setTimeout(() => { |
|||
wx.showToast({ |
|||
title: '暂无客服数据', |
|||
icon: 'none', |
|||
duration: 2000 |
|||
}); |
|||
}, 100); |
|||
} |
|||
} catch (error) { |
|||
console.error('加载客服列表失败:', error); |
|||
// 确保在hideLoading后显示错误提示
|
|||
setTimeout(() => { |
|||
wx.showToast({ |
|||
title: '加载失败,请重试', |
|||
icon: 'none', |
|||
duration: 2000 |
|||
}); |
|||
}, 100); |
|||
} finally { |
|||
wx.hideLoading(); |
|||
} |
|||
}, |
|||
|
|||
onLoad: function () { |
|||
// 加载客服列表
|
|||
this.loadCustomerServices(); |
|||
|
|||
// 启动定期刷新
|
|||
this.startPeriodicRefresh(); |
|||
|
|||
// 检查当前用户身份
|
|||
this.checkUserType(); |
|||
}, |
|||
|
|||
/** |
|||
* 检查当前用户身份 |
|||
*/ |
|||
checkUserType: function() { |
|||
const app = getApp(); |
|||
const userInfo = app.globalData.userInfo || {}; |
|||
const isManager = userInfo.userType === 'manager' || userInfo.type === 'manager'; |
|||
console.log('当前用户身份检查:', { isManager, userType: userInfo.userType }); |
|||
this.setData({ |
|||
isCurrentUserManager: isManager |
|||
}); |
|||
}, |
|||
|
|||
onShow() { |
|||
// 更新自定义tabBar状态
|
|||
if (typeof this.getTabBar === 'function' && this.getTabBar()) { |
|||
this.getTabBar().setData({ |
|||
selected: -1 // 不选中任何tab
|
|||
}); |
|||
} |
|||
|
|||
// 当页面显示时重新加载数据,确保数据最新
|
|||
this.loadCustomerServices(); |
|||
}, |
|||
|
|||
onUnload: function() { |
|||
// 停止定期刷新
|
|||
this.stopPeriodicRefresh(); |
|||
}, |
|||
|
|||
onSearch: function (e) { |
|||
const keyword = e.detail.value; |
|||
this.setData({ |
|||
searchKeyword: keyword |
|||
}); |
|||
this.filterServices(); |
|||
}, |
|||
|
|||
onAreaFilter: function () { |
|||
// 区域筛选弹窗 - 鸡蛋采购区域
|
|||
wx.showActionSheet({ |
|||
itemList: ['全部', '华北区', '华东区', '华南区', '全国', '西南区', '西北区', '东北区'], |
|||
success: res => { |
|||
const areas = ['全部', '华北区', '华东区', '华南区', '全国', '西南区', '西北区', '东北区']; |
|||
const selectedArea = areas[res.tapIndex]; |
|||
this.setData({ |
|||
selectedArea: selectedArea |
|||
}); |
|||
this.filterServices(); |
|||
} |
|||
]; |
|||
}); |
|||
}, |
|||
|
|||
filterServices: function () { |
|||
const { customerServices, searchKeyword, selectedArea } = this.data; |
|||
|
|||
let filtered = customerServices; |
|||
|
|||
// 关键词搜索
|
|||
if (searchKeyword) { |
|||
const keyword = searchKeyword.toLowerCase(); |
|||
filtered = filtered.filter(item => { |
|||
return item.alias?.toLowerCase().includes(keyword) || |
|||
item.name.toLowerCase().includes(keyword) || |
|||
item.phoneNumber?.includes(keyword) || |
|||
item.managercompany?.toLowerCase().includes(keyword); |
|||
}); |
|||
} |
|||
|
|||
// 区域筛选
|
|||
if (selectedArea && selectedArea !== '全部') { |
|||
filtered = filtered.filter(item => { |
|||
return item.responsibleArea?.includes(selectedArea); |
|||
}); |
|||
} |
|||
|
|||
this.setData({ |
|||
serviceList: serviceList |
|||
filteredServices: filtered |
|||
}); |
|||
}, |
|||
|
|||
startOnlineChat: function () { |
|||
// 跳转到在线聊天页面
|
|||
wx.showToast({ |
|||
title: '在线客服功能开发中', |
|||
icon: 'none' |
|||
onChat: function (e) { |
|||
const id = e.currentTarget.dataset.id; |
|||
const service = this.data.customerServices.find(item => item.id === id); |
|||
if (!service) return; |
|||
|
|||
// 获取当前用户的手机号
|
|||
let userPhone = ''; |
|||
try { |
|||
// 尝试从不同的存储位置获取手机号
|
|||
const userInfo = wx.getStorageSync('userInfo'); |
|||
if (userInfo && userInfo.phoneNumber) { |
|||
userPhone = userInfo.phoneNumber; |
|||
} else { |
|||
// 尝试从其他可能的存储位置获取
|
|||
const users = wx.getStorageSync('users') || {}; |
|||
const userId = wx.getStorageSync('userId'); |
|||
if (userId && users[userId] && users[userId].phoneNumber) { |
|||
userPhone = users[userId].phoneNumber; |
|||
} else { |
|||
userPhone = wx.getStorageSync('phoneNumber'); |
|||
} |
|||
} |
|||
} catch (e) { |
|||
console.error('获取用户手机号失败:', e); |
|||
} |
|||
|
|||
console.log('当前用户手机号:', userPhone); |
|||
console.log('客服手机号:', service.phoneNumber); |
|||
|
|||
// 验证手机号
|
|||
if (!userPhone) { |
|||
wx.showToast({ |
|||
title: '请先登录获取手机号', |
|||
icon: 'none' |
|||
}); |
|||
return; |
|||
} |
|||
|
|||
// 显示加载提示
|
|||
wx.showLoading({ |
|||
title: '正在建立聊天...', |
|||
}); |
|||
|
|||
// 调用API添加聊天记录
|
|||
api.addChatRecord(userPhone, service.phoneNumber).then(res => { |
|||
console.log('添加聊天记录成功:', res); |
|||
// 隐藏加载提示
|
|||
wx.hideLoading(); |
|||
|
|||
// 确保使用managerId作为聊天对象的唯一标识符
|
|||
const chatUserId = service?.managerId || id; |
|||
// 跳转到聊天页面
|
|||
wx.navigateTo({ |
|||
url: `/pages/chat-detail/index?userId=${chatUserId}&userName=${encodeURIComponent(service?.alias || '')}&phone=${service?.phoneNumber || ''}&isManager=true` |
|||
}); |
|||
console.log('跳转到聊天页面:', { chatUserId, userName: service?.alias }); |
|||
}).catch(err => { |
|||
console.error('添加聊天记录失败:', err); |
|||
// 隐藏加载提示
|
|||
wx.hideLoading(); |
|||
wx.showToast({ |
|||
title: '建立聊天失败,请重试', |
|||
icon: 'none' |
|||
}); |
|||
}); |
|||
}, |
|||
|
|||
sendEmail: function () { |
|||
// 打开邮件客户端
|
|||
wx.showToast({ |
|||
title: '邮箱客服功能开发中', |
|||
icon: 'none' |
|||
onCall: function (e) { |
|||
const phone = e.currentTarget.dataset.phone; |
|||
wx.makePhoneCall({ |
|||
phoneNumber: phone, |
|||
success: function () { |
|||
console.log('拨打电话成功'); |
|||
}, |
|||
fail: function () { |
|||
console.log('拨打电话失败'); |
|||
} |
|||
}); |
|||
}, |
|||
|
|||
// 查看客服详情
|
|||
onViewDetail: function (e) { |
|||
const id = e.currentTarget.dataset.id; |
|||
wx.navigateTo({ |
|||
url: `/pages/customer-service/detail?id=${id}` |
|||
}); |
|||
}, |
|||
|
|||
onBack: function () { |
|||
wx.navigateBack(); |
|||
} |
|||
}); |
|||
}); |
|||
|
|||
@ -1,3 +1,5 @@ |
|||
{ |
|||
"navigationBarTitleText": "客服中心" |
|||
} |
|||
"navigationBarBackgroundColor": "#f8f8f8", |
|||
"navigationBarTextStyle": "black", |
|||
"usingComponents": {} |
|||
} |
|||
|
|||
@ -1,40 +1,89 @@ |
|||
<!-- pages/customer-service/index.wxml --> |
|||
<view class="container"> |
|||
<view class="service-header"> |
|||
<text class="service-title">客服中心</text> |
|||
<!-- 顶部导航栏 --> |
|||
<view class="nav-bar"> |
|||
<view class="nav-left" bindtap="onBack"> |
|||
<text class="back-icon">返回</text> |
|||
</view> |
|||
<view class="nav-title">客服列表</view> |
|||
<view class="nav-right"> |
|||
<text class="settings-icon">⚙️</text> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="service-content"> |
|||
<view class="service-item"> |
|||
<view class="service-icon"> |
|||
<text>📞</text> |
|||
</view> |
|||
<view class="service-info"> |
|||
<text class="service-name">电话客服</text> |
|||
<text class="service-desc">周一至周日 9:00-18:00</text> |
|||
</view> |
|||
<button class="call-button" open-type="makePhoneCall" phone-number="400-123-4567">呼叫</button> |
|||
|
|||
<!-- 搜索区域 --> |
|||
<view class="search-container"> |
|||
<view class="search-bar"> |
|||
<text class="search-icon-small">🔍</text> |
|||
<input class="search-input" placeholder="客服人称或手机号" bindinput="onSearch" value="{{searchKeyword}}" /> |
|||
</view> |
|||
|
|||
<view class="service-item"> |
|||
<view class="service-icon"> |
|||
<text>💬</text> |
|||
<view class="filter-area"> |
|||
<view class="area-picker" bindtap="onAreaFilter"> |
|||
<text>区域</text> |
|||
<text class="picker-arrow">▼</text> |
|||
</view> |
|||
<view class="service-info"> |
|||
<text class="service-name">在线客服</text> |
|||
<text class="service-desc">24小时在线为您服务</text> |
|||
</view> |
|||
<button class="chat-button" bindtap="startOnlineChat">在线咨询</button> |
|||
</view> |
|||
|
|||
<view class="service-item"> |
|||
<view class="service-icon"> |
|||
<text>📧</text> |
|||
</view> |
|||
<view class="service-info"> |
|||
<text class="service-name">邮箱客服</text> |
|||
<text class="service-desc">service@example.com</text> |
|||
</view> |
|||
|
|||
<!-- 客服列表 --> |
|||
<view class="broker-list"> |
|||
<block wx:if="{{filteredServices.length > 0}}"> |
|||
<view class="broker-item" wx:for="{{filteredServices}}" wx:key="id" bindtap="onViewDetail" data-id="{{item.id}}"> |
|||
<view class="broker-info"> |
|||
<view class="avatar-container"> |
|||
<image class="avatar" src="{{item.avatarUrl || '/images/default-avatar.png'}}" mode="aspectFill" /> |
|||
<view wx:if="{{item.isOnline}}" class="online-indicator">在线</view> |
|||
<view wx:else class="offline-indicator">离线</view> |
|||
</view> |
|||
<view class="broker-details"> |
|||
<view class="name-row"> |
|||
<text class="name">{{item.alias}}</text> |
|||
<text class="score-text">{{item.score}} 鸡蛋分</text> |
|||
<text wx:if="{{item.isOnline}}" class="online-status">(在线)</text> |
|||
</view> |
|||
<text class="company">{{item.managercompany || '暂无公司信息'}}</text> |
|||
<text class="department">{{item.managerdepartment}} · {{item.projectName}}</text> |
|||
<text class="area">负责区域:{{item.responsibleArea}}</text> |
|||
<text class="experience">服务平台{{item.experience}} 服务{{item.serviceCount}}家鸡场</text> |
|||
<!-- 业绩数据统计 --> |
|||
<view class="performance-stats"> |
|||
<view class="stat-item"> |
|||
<text class="stat-value">{{item.purchaseCount}}</text> |
|||
<text class="stat-label">累计采购(件)</text> |
|||
</view> |
|||
<view class="stat-divider">|</view> |
|||
<view class="stat-item"> |
|||
<text class="stat-value profit-rate">{{item.profitFarmCount}}</text> |
|||
<text class="stat-label">服务盈利鸡场(家)</text> |
|||
</view> |
|||
<view class="stat-divider">|</view> |
|||
<view class="stat-item"> |
|||
<text class="stat-value profit-rate">{{item.profitIncreaseRate}}%</text> |
|||
<text class="stat-label">平均盈利增长</text> |
|||
</view> |
|||
</view> |
|||
<!-- 专业技能标签 --> |
|||
<view class="skills-preview"> |
|||
<view wx:for="{{item.skills}}" wx:key="index" wx:if="{{index < 3}}" class="skill-tag-small"> |
|||
{{item}}</view> |
|||
<view wx:if="{{item.skills.length > 3}}" class="skill-more"> |
|||
+{{item.skills.length - 3}}</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
<view class="action-buttons"> |
|||
<view wx:if="{{!isCurrentUserManager}}" class="button-chat" bindtap="onChat" data-id="{{item.id}}"> |
|||
<text class="button-icon">💬</text> |
|||
</view> |
|||
<view class="button-call" bindtap="onCall" data-phone="{{item.phoneNumber}}"> |
|||
<text class="button-icon">📞</text> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
<button class="email-button" bindtap="sendEmail">发送邮件</button> |
|||
</block> |
|||
<view wx:else class="empty-state"> |
|||
<text>👤</text> |
|||
<text class="empty-text">暂无匹配的经纪人</text> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
@ -1,80 +1,383 @@ |
|||
/* pages/customer-service/index.wxss */ |
|||
.container { |
|||
background-color: #f5f5f5; |
|||
padding-bottom: 100rpx; |
|||
background-color: #f8f8f8; |
|||
min-height: 100vh; |
|||
} |
|||
|
|||
.service-header { |
|||
background-color: #fff; |
|||
padding: 20rpx; |
|||
border-bottom: 1rpx solid #e8e8e8; |
|||
/* 顶部导航栏 */ |
|||
.nav-bar { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
justify-content: center; |
|||
padding: 44rpx 30rpx 20rpx; |
|||
background-color: #fff; |
|||
border-bottom: 1rpx solid #f0f0f0; |
|||
position: fixed; |
|||
top: 0; |
|||
left: 0; |
|||
right: 0; |
|||
z-index: 1000; |
|||
width: 100%; |
|||
box-sizing: border-box; |
|||
} |
|||
|
|||
.nav-left { |
|||
width: 80rpx; |
|||
display: flex; |
|||
justify-content: flex-start; |
|||
} |
|||
|
|||
.back-icon { |
|||
font-size: 32rpx; |
|||
color: #333; |
|||
font-weight: normal; |
|||
} |
|||
|
|||
.service-title { |
|||
.nav-title { |
|||
font-size: 36rpx; |
|||
font-weight: bold; |
|||
color: #333; |
|||
flex: 1; |
|||
text-align: center; |
|||
} |
|||
|
|||
.nav-right { |
|||
display: flex; |
|||
align-items: center; |
|||
gap: 30rpx; |
|||
} |
|||
|
|||
.service-content { |
|||
padding: 20rpx; |
|||
.search-icon, .settings-icon { |
|||
font-size: 32rpx; |
|||
color: #333; |
|||
} |
|||
|
|||
.service-item { |
|||
/* 搜索区域 */ |
|||
.search-container { |
|||
background-color: #fff; |
|||
border-radius: 12rpx; |
|||
padding: 24rpx; |
|||
padding: 20rpx 30rpx; |
|||
border-bottom: 10rpx solid #f8f8f8; |
|||
position: fixed; |
|||
top: 110rpx; |
|||
left: 0; |
|||
right: 0; |
|||
z-index: 999; |
|||
width: 100%; |
|||
box-sizing: border-box; |
|||
} |
|||
|
|||
.search-bar { |
|||
display: flex; |
|||
align-items: center; |
|||
background-color: #f5f5f5; |
|||
border-radius: 40rpx; |
|||
padding: 16rpx 24rpx; |
|||
margin-bottom: 20rpx; |
|||
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05); |
|||
transition: all 0.3s ease; |
|||
} |
|||
|
|||
.search-bar:focus-within { |
|||
background-color: #e6f7ff; |
|||
box-shadow: 0 0 0 4rpx rgba(24, 144, 255, 0.1); |
|||
} |
|||
|
|||
.search-icon-small { |
|||
font-size: 28rpx; |
|||
color: #999; |
|||
margin-right: 16rpx; |
|||
} |
|||
|
|||
.search-input { |
|||
flex: 1; |
|||
font-size: 28rpx; |
|||
color: #333; |
|||
background: none; |
|||
padding: 0; |
|||
} |
|||
|
|||
.search-input::placeholder { |
|||
color: #999; |
|||
} |
|||
|
|||
.filter-area { |
|||
display: flex; |
|||
align-items: center; |
|||
} |
|||
|
|||
.service-icon { |
|||
width: 80rpx; |
|||
height: 80rpx; |
|||
border-radius: 50%; |
|||
background-color: #f0f0f0; |
|||
.area-picker { |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
background-color: #f5f5f5; |
|||
padding: 12rpx 24rpx; |
|||
border-radius: 24rpx; |
|||
font-size: 28rpx; |
|||
color: #333; |
|||
} |
|||
|
|||
.picker-arrow { |
|||
margin-left: 8rpx; |
|||
font-size: 20rpx; |
|||
color: #999; |
|||
} |
|||
|
|||
/* 经纪人列表 */ |
|||
.broker-list { |
|||
background-color: #f8f8f8; |
|||
padding: 0 30rpx; |
|||
margin-top: 280rpx; /* 为固定导航和搜索区域留出空间 */ |
|||
} |
|||
|
|||
.broker-item { |
|||
background-color: #fff; |
|||
border-radius: 24rpx; |
|||
margin: 20rpx 0; |
|||
padding: 28rpx; |
|||
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05); |
|||
transition: all 0.2s ease; |
|||
} |
|||
|
|||
.broker-item:active { |
|||
transform: translateY(2rpx); |
|||
box-shadow: 0 1rpx 6rpx rgba(0, 0, 0, 0.03); |
|||
background-color: #fafafa; |
|||
} |
|||
|
|||
.broker-info { |
|||
display: flex; |
|||
margin-bottom: 24rpx; |
|||
} |
|||
|
|||
.avatar-container { |
|||
width: 100rpx; |
|||
height: 100rpx; |
|||
margin-right: 24rpx; |
|||
font-size: 40rpx; |
|||
position: relative; |
|||
} |
|||
|
|||
.avatar { |
|||
width: 100%; |
|||
height: 100%; |
|||
border-radius: 50%; |
|||
background-color: #f0f0f0; |
|||
border: 2rpx solid #f0f0f0; |
|||
} |
|||
|
|||
.online-indicator { |
|||
position: absolute; |
|||
bottom: 0; |
|||
right: 0; |
|||
background-color: #52c41a; |
|||
color: white; |
|||
font-size: 18rpx; |
|||
padding: 4rpx 12rpx; |
|||
border-radius: 12rpx; |
|||
border: 2rpx solid white; |
|||
} |
|||
|
|||
.service-info { |
|||
.offline-indicator { |
|||
position: absolute; |
|||
bottom: 0; |
|||
right: 0; |
|||
background-color: #999; |
|||
color: white; |
|||
font-size: 18rpx; |
|||
padding: 4rpx 12rpx; |
|||
border-radius: 12rpx; |
|||
border: 2rpx solid white; |
|||
} |
|||
|
|||
.broker-details { |
|||
flex: 1; |
|||
display: flex; |
|||
flex-direction: column; |
|||
justify-content: center; |
|||
} |
|||
|
|||
.service-name { |
|||
display: block; |
|||
.name-row { |
|||
display: flex; |
|||
align-items: center; |
|||
margin-bottom: 8rpx; |
|||
flex-wrap: wrap; |
|||
} |
|||
|
|||
.name { |
|||
font-size: 32rpx; |
|||
font-weight: bold; |
|||
color: #333; |
|||
margin-bottom: 8rpx; |
|||
margin-right: 12rpx; |
|||
} |
|||
|
|||
.service-desc { |
|||
display: block; |
|||
font-size: 26rpx; |
|||
.score-text { |
|||
font-size: 24rpx; |
|||
color: #fff; |
|||
background: linear-gradient(135deg, #ffb800, #ff7700); |
|||
padding: 4rpx 12rpx; |
|||
border-radius: 12rpx; |
|||
margin-right: 8rpx; |
|||
} |
|||
|
|||
.online-status { |
|||
font-size: 22rpx; |
|||
color: #52c41a; |
|||
} |
|||
|
|||
.company { |
|||
font-size: 28rpx; |
|||
color: #666; |
|||
margin-bottom: 6rpx; |
|||
line-height: 1.4; |
|||
} |
|||
|
|||
.call-button, .chat-button, .email-button { |
|||
padding: 12rpx 32rpx; |
|||
border-radius: 24rpx; |
|||
.department { |
|||
font-size: 26rpx; |
|||
color: #999; |
|||
margin-bottom: 6rpx; |
|||
line-height: 1.4; |
|||
} |
|||
|
|||
.area { |
|||
font-size: 26rpx; |
|||
color: #333; |
|||
margin-bottom: 6rpx; |
|||
line-height: 1.4; |
|||
font-weight: 500; |
|||
} |
|||
|
|||
.experience { |
|||
font-size: 24rpx; |
|||
color: #999; |
|||
margin-top: 8rpx; |
|||
} |
|||
|
|||
/* 专业技能标签预览 */ |
|||
.skills-preview { |
|||
display: flex; |
|||
flex-wrap: wrap; |
|||
gap: 10rpx; |
|||
margin-top: 12rpx; |
|||
} |
|||
|
|||
.skill-tag-small { |
|||
background-color: #f0f9ff; |
|||
color: #1890ff; |
|||
font-size: 22rpx; |
|||
padding: 8rpx 16rpx; |
|||
border-radius: 16rpx; |
|||
border: 1rpx solid #bae7ff; |
|||
} |
|||
|
|||
.skill-more { |
|||
background-color: #f5f5f5; |
|||
color: #999; |
|||
font-size: 22rpx; |
|||
padding: 8rpx 16rpx; |
|||
border-radius: 16rpx; |
|||
border: 1rpx solid #e0e0e0; |
|||
} |
|||
|
|||
/* 业绩数据统计样式 */ |
|||
.performance-stats { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
margin-top: 16rpx; |
|||
padding-top: 16rpx; |
|||
border-top: 1rpx solid #f0f0f0; |
|||
} |
|||
|
|||
.stat-item { |
|||
flex: 1; |
|||
text-align: center; |
|||
} |
|||
|
|||
.stat-value { |
|||
font-size: 28rpx; |
|||
font-weight: bold; |
|||
background-color: #FF6B81; |
|||
color: #fff; |
|||
border: none; |
|||
margin: 0; |
|||
line-height: normal; |
|||
height: auto; |
|||
color: #333; |
|||
display: block; |
|||
} |
|||
|
|||
.call-button:hover, .chat-button:hover, .email-button:hover { |
|||
background-color: #FF526D; |
|||
} |
|||
.stat-value.profit-rate { |
|||
color: #52c41a; |
|||
} |
|||
|
|||
.stat-label { |
|||
font-size: 22rpx; |
|||
color: #999; |
|||
margin-top: 4rpx; |
|||
display: block; |
|||
} |
|||
|
|||
.stat-divider { |
|||
color: #e0e0e0; |
|||
font-size: 24rpx; |
|||
margin: 0 10rpx; |
|||
display: flex; |
|||
align-items: center; |
|||
} |
|||
|
|||
.action-buttons { |
|||
display: flex; |
|||
justify-content: flex-end; |
|||
gap: 24rpx; |
|||
padding-top: 20rpx; |
|||
border-top: 1rpx solid #f0f0f0; |
|||
} |
|||
|
|||
.button-chat, .button-call { |
|||
width: 72rpx; |
|||
height: 72rpx; |
|||
border-radius: 50%; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
transition: all 0.2s ease; |
|||
position: relative; |
|||
overflow: hidden; |
|||
} |
|||
|
|||
.button-chat { |
|||
background-color: #e6f7f0; |
|||
} |
|||
|
|||
.button-call { |
|||
background-color: #e6f0f7; |
|||
} |
|||
|
|||
.button-chat:active { |
|||
transform: scale(0.95); |
|||
opacity: 0.9; |
|||
background-color: #c3e6cb; |
|||
} |
|||
|
|||
.button-call:active { |
|||
transform: scale(0.95); |
|||
opacity: 0.9; |
|||
background-color: #b3d8ff; |
|||
} |
|||
|
|||
.button-icon { |
|||
font-size: 36rpx; |
|||
} |
|||
|
|||
/* 空状态 */ |
|||
.empty-state { |
|||
display: flex; |
|||
flex-direction: column; |
|||
align-items: center; |
|||
justify-content: center; |
|||
padding: 100rpx 0; |
|||
background-color: #fff; |
|||
margin: 20rpx 0; |
|||
border-radius: 24rpx; |
|||
} |
|||
|
|||
.empty-state text:first-child { |
|||
font-size: 100rpx; |
|||
margin-bottom: 20rpx; |
|||
} |
|||
|
|||
.empty-text { |
|||
font-size: 28rpx; |
|||
color: #999; |
|||
} |
|||
|
|||
Loading…
Reference in new issue