11 changed files with 3515 additions and 0 deletions
@ -0,0 +1,274 @@ |
|||||
|
<!--服务详情页面--> |
||||
|
<template> |
||||
|
<view class="container"> |
||||
|
<view class="main-content"> |
||||
|
<view class="service-header"> |
||||
|
<view class="service-title">我的服务详情</view> |
||||
|
<view class="service-subtitle">查看当前服务状态和详细信息</view> |
||||
|
</view> |
||||
|
|
||||
|
<view class="service-cards"> |
||||
|
<view class="service-card" v-for="(service, index) in serviceList" :key="index"> |
||||
|
<view class="card-header"> |
||||
|
<view class="service-name">{{ service.name }}</view> |
||||
|
<view class="service-status" :class="service.status === '正常' ? 'status-active' : 'status-inactive'"> |
||||
|
{{ service.status }} |
||||
|
</view> |
||||
|
</view> |
||||
|
<view class="card-content"> |
||||
|
<view class="service-info"> |
||||
|
<view class="info-item"> |
||||
|
<text class="label">服务类型:</text> |
||||
|
<text class="value">{{ service.type }}</text> |
||||
|
</view> |
||||
|
<view class="info-item"> |
||||
|
<text class="label">开始时间:</text> |
||||
|
<text class="value">{{ service.startTime }}</text> |
||||
|
</view> |
||||
|
<view class="info-item"> |
||||
|
<text class="label">结束时间:</text> |
||||
|
<text class="value">{{ service.endTime }}</text> |
||||
|
</view> |
||||
|
<view class="info-item"> |
||||
|
<text class="label">服务描述:</text> |
||||
|
<text class="value">{{ service.description }}</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
<view class="card-actions"> |
||||
|
<view class="btn btn-detail" @click="viewDetail(service)">查看详情</view> |
||||
|
<view class="btn btn-contact" @click="contactService(service)">联系客服</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<view class="empty-state" v-if="serviceList.length === 0"> |
||||
|
<image class="empty-icon" src="/static/icon-img/empty.png"></image> |
||||
|
<view class="empty-text">暂无服务记录</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import apiRoute from '@/api/apiRoute.js'; |
||||
|
|
||||
|
export default { |
||||
|
data() { |
||||
|
return { |
||||
|
serviceList: [] |
||||
|
} |
||||
|
}, |
||||
|
onLoad() { |
||||
|
this.init(); |
||||
|
}, |
||||
|
methods: { |
||||
|
async init() { |
||||
|
this.getServiceList(); |
||||
|
}, |
||||
|
|
||||
|
// 获取服务列表 |
||||
|
async getServiceList() { |
||||
|
try { |
||||
|
// 模拟数据,实际应该调用API |
||||
|
this.serviceList = [ |
||||
|
{ |
||||
|
id: 1, |
||||
|
name: '教练服务套餐A', |
||||
|
type: '专业训练', |
||||
|
status: '正常', |
||||
|
startTime: '2024-01-01', |
||||
|
endTime: '2024-12-31', |
||||
|
description: '专业体能训练指导服务' |
||||
|
}, |
||||
|
{ |
||||
|
id: 2, |
||||
|
name: '教练服务套餐B', |
||||
|
type: '基础指导', |
||||
|
status: '即将到期', |
||||
|
startTime: '2024-06-01', |
||||
|
endTime: '2024-06-30', |
||||
|
description: '基础动作指导和纠正' |
||||
|
} |
||||
|
]; |
||||
|
|
||||
|
// 实际API调用示例: |
||||
|
// let res = await apiRoute.getServiceList({}); |
||||
|
// if (res.code === 1) { |
||||
|
// this.serviceList = res.data; |
||||
|
// } |
||||
|
} catch (error) { |
||||
|
console.error('获取服务列表失败:', error); |
||||
|
uni.showToast({ |
||||
|
title: '获取数据失败', |
||||
|
icon: 'none' |
||||
|
}); |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
// 查看详情 |
||||
|
viewDetail(service) { |
||||
|
uni.showModal({ |
||||
|
title: '服务详情', |
||||
|
content: `服务名称:${service.name}\n服务类型:${service.type}\n状态:${service.status}\n描述:${service.description}`, |
||||
|
showCancel: false |
||||
|
}); |
||||
|
}, |
||||
|
|
||||
|
// 联系客服 |
||||
|
contactService(service) { |
||||
|
uni.showActionSheet({ |
||||
|
itemList: ['电话客服', '在线客服', '邮件客服'], |
||||
|
success: (res) => { |
||||
|
const actions = ['拨打客服电话', '打开在线客服', '发送邮件']; |
||||
|
uni.showToast({ |
||||
|
title: actions[res.tapIndex], |
||||
|
icon: 'none' |
||||
|
}); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="less" scoped> |
||||
|
.container { |
||||
|
background: #f5f5f5; |
||||
|
min-height: 100vh; |
||||
|
} |
||||
|
|
||||
|
.main-content { |
||||
|
padding: 20rpx; |
||||
|
} |
||||
|
|
||||
|
.service-header { |
||||
|
background: #29d3b4; |
||||
|
border-radius: 16rpx; |
||||
|
padding: 40rpx; |
||||
|
margin-bottom: 30rpx; |
||||
|
color: white; |
||||
|
|
||||
|
.service-title { |
||||
|
font-size: 36rpx; |
||||
|
font-weight: bold; |
||||
|
margin-bottom: 10rpx; |
||||
|
} |
||||
|
|
||||
|
.service-subtitle { |
||||
|
font-size: 26rpx; |
||||
|
opacity: 0.8; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.service-cards { |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
gap: 20rpx; |
||||
|
} |
||||
|
|
||||
|
.service-card { |
||||
|
background: white; |
||||
|
border-radius: 16rpx; |
||||
|
padding: 30rpx; |
||||
|
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1); |
||||
|
} |
||||
|
|
||||
|
.card-header { |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
align-items: center; |
||||
|
margin-bottom: 20rpx; |
||||
|
|
||||
|
.service-name { |
||||
|
font-size: 32rpx; |
||||
|
font-weight: bold; |
||||
|
color: #333; |
||||
|
} |
||||
|
|
||||
|
.service-status { |
||||
|
padding: 8rpx 16rpx; |
||||
|
border-radius: 20rpx; |
||||
|
font-size: 24rpx; |
||||
|
|
||||
|
&.status-active { |
||||
|
background: #e8f5e8; |
||||
|
color: #52c41a; |
||||
|
} |
||||
|
|
||||
|
&.status-inactive { |
||||
|
background: #fff2e8; |
||||
|
color: #fa8c16; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.card-content { |
||||
|
margin-bottom: 25rpx; |
||||
|
} |
||||
|
|
||||
|
.service-info { |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
gap: 15rpx; |
||||
|
} |
||||
|
|
||||
|
.info-item { |
||||
|
display: flex; |
||||
|
|
||||
|
.label { |
||||
|
color: #666; |
||||
|
font-size: 28rpx; |
||||
|
min-width: 160rpx; |
||||
|
} |
||||
|
|
||||
|
.value { |
||||
|
color: #333; |
||||
|
font-size: 28rpx; |
||||
|
flex: 1; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.card-actions { |
||||
|
display: flex; |
||||
|
gap: 20rpx; |
||||
|
} |
||||
|
|
||||
|
.btn { |
||||
|
flex: 1; |
||||
|
padding: 20rpx; |
||||
|
border-radius: 12rpx; |
||||
|
text-align: center; |
||||
|
font-size: 28rpx; |
||||
|
|
||||
|
&.btn-detail { |
||||
|
background: #29d3b4; |
||||
|
color: white; |
||||
|
} |
||||
|
|
||||
|
&.btn-contact { |
||||
|
background: #f0f0f0; |
||||
|
color: #333; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.empty-state { |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
padding: 100rpx 0; |
||||
|
|
||||
|
.empty-icon { |
||||
|
width: 120rpx; |
||||
|
height: 120rpx; |
||||
|
margin-bottom: 30rpx; |
||||
|
opacity: 0.3; |
||||
|
} |
||||
|
|
||||
|
.empty-text { |
||||
|
color: #999; |
||||
|
font-size: 28rpx; |
||||
|
} |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,512 @@ |
|||||
|
<!--服务列表页面--> |
||||
|
<template> |
||||
|
<view class="container"> |
||||
|
<view class="header"> |
||||
|
<view class="search-bar"> |
||||
|
<view class="search-input"> |
||||
|
<input |
||||
|
placeholder="搜索服务..." |
||||
|
v-model="searchText" |
||||
|
@input="handleSearch" |
||||
|
/> |
||||
|
</view> |
||||
|
<view class="filter-btn" @click="showFilter"> |
||||
|
<text>筛选</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<view class="service-list"> |
||||
|
<view class="service-item" |
||||
|
v-for="(service, index) in filteredServiceList" |
||||
|
:key="index" |
||||
|
@click="goToDetail(service)"> |
||||
|
<view class="service-header"> |
||||
|
<view class="service-title">{{ service.name }}</view> |
||||
|
<view class="service-badge" :class="[ |
||||
|
service.status === '正常' ? 'badge-success' : '', |
||||
|
service.status === '即将到期' ? 'badge-warning' : '', |
||||
|
service.status === '已过期' ? 'badge-danger' : '', |
||||
|
!['正常', '即将到期', '已过期'].includes(service.status) ? 'badge-default' : '' |
||||
|
]"> |
||||
|
{{ service.status }} |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<view class="service-content"> |
||||
|
<view class="service-meta"> |
||||
|
<view class="meta-item"> |
||||
|
<text class="meta-label">类型:</text> |
||||
|
<text class="meta-value">{{ service.type }}</text> |
||||
|
</view> |
||||
|
<view class="meta-item"> |
||||
|
<text class="meta-label">时长:</text> |
||||
|
<text class="meta-value">{{ service.duration }}</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<view class="service-desc">{{ service.description }}</view> |
||||
|
|
||||
|
<view class="service-footer"> |
||||
|
<view class="service-time"> |
||||
|
{{ service.startTime }} - {{ service.endTime }} |
||||
|
</view> |
||||
|
<view class="service-action"> |
||||
|
<text class="action-text">查看详情</text> |
||||
|
<text class="action-arrow">></text> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<view class="empty-state" v-if="filteredServiceList.length === 0"> |
||||
|
<image class="empty-icon" src="/static/icon-img/empty.png"></image> |
||||
|
<view class="empty-text">{{ searchText ? '未找到相关服务' : '暂无服务记录' }}</view> |
||||
|
</view> |
||||
|
|
||||
|
<!-- 筛选弹窗 --> |
||||
|
<fui-bottom-popup v-model="showFilterPopup" :zIndex="9999"> |
||||
|
<view class="filter-popup"> |
||||
|
<view class="popup-header"> |
||||
|
<view class="popup-title">筛选条件</view> |
||||
|
<view class="popup-close" @click="showFilterPopup = false">✕</view> |
||||
|
</view> |
||||
|
|
||||
|
<view class="filter-content"> |
||||
|
<view class="filter-group"> |
||||
|
<view class="filter-title">服务状态</view> |
||||
|
<view class="filter-options"> |
||||
|
<view |
||||
|
class="filter-option" |
||||
|
:class="{ active: selectedStatus === '' }" |
||||
|
@click="selectedStatus = ''" |
||||
|
> |
||||
|
全部 |
||||
|
</view> |
||||
|
<view |
||||
|
class="filter-option" |
||||
|
:class="{ active: selectedStatus === '正常' }" |
||||
|
@click="selectedStatus = '正常'" |
||||
|
> |
||||
|
正常 |
||||
|
</view> |
||||
|
<view |
||||
|
class="filter-option" |
||||
|
:class="{ active: selectedStatus === '即将到期' }" |
||||
|
@click="selectedStatus = '即将到期'" |
||||
|
> |
||||
|
即将到期 |
||||
|
</view> |
||||
|
<view |
||||
|
class="filter-option" |
||||
|
:class="{ active: selectedStatus === '已过期' }" |
||||
|
@click="selectedStatus = '已过期'" |
||||
|
> |
||||
|
已过期 |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<view class="popup-actions"> |
||||
|
<view class="btn btn-reset" @click="resetFilter">重置</view> |
||||
|
<view class="btn btn-confirm" @click="applyFilter">确定</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</fui-bottom-popup> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import apiRoute from '@/api/apiRoute.js'; |
||||
|
|
||||
|
export default { |
||||
|
data() { |
||||
|
return { |
||||
|
serviceList: [], |
||||
|
filteredServiceList: [], |
||||
|
searchText: '', |
||||
|
showFilterPopup: false, |
||||
|
selectedStatus: '' |
||||
|
} |
||||
|
}, |
||||
|
onLoad() { |
||||
|
this.init(); |
||||
|
}, |
||||
|
methods: { |
||||
|
async init() { |
||||
|
this.getServiceList(); |
||||
|
}, |
||||
|
|
||||
|
// 获取服务列表 |
||||
|
async getServiceList() { |
||||
|
try { |
||||
|
// 模拟数据,实际应该调用API |
||||
|
this.serviceList = [ |
||||
|
{ |
||||
|
id: 1, |
||||
|
name: '专业体能训练服务', |
||||
|
type: '体能训练', |
||||
|
status: '正常', |
||||
|
duration: '3个月', |
||||
|
startTime: '2024-01-01', |
||||
|
endTime: '2024-03-31', |
||||
|
description: '专业的体能训练指导,包含有氧运动、力量训练等综合项目' |
||||
|
}, |
||||
|
{ |
||||
|
id: 2, |
||||
|
name: '基础动作指导服务', |
||||
|
type: '技术指导', |
||||
|
status: '即将到期', |
||||
|
duration: '1个月', |
||||
|
startTime: '2024-06-01', |
||||
|
endTime: '2024-06-30', |
||||
|
description: '针对基础动作的专业指导和纠正' |
||||
|
}, |
||||
|
{ |
||||
|
id: 3, |
||||
|
name: '营养咨询服务', |
||||
|
type: '营养指导', |
||||
|
status: '正常', |
||||
|
duration: '6个月', |
||||
|
startTime: '2024-01-15', |
||||
|
endTime: '2024-07-15', |
||||
|
description: '专业营养师提供个性化营养方案和饮食建议' |
||||
|
}, |
||||
|
{ |
||||
|
id: 4, |
||||
|
name: '康复训练服务', |
||||
|
type: '康复指导', |
||||
|
status: '已过期', |
||||
|
duration: '2个月', |
||||
|
startTime: '2023-10-01', |
||||
|
endTime: '2023-11-30', |
||||
|
description: '运动损伤康复和预防性训练指导' |
||||
|
} |
||||
|
]; |
||||
|
|
||||
|
this.filteredServiceList = [...this.serviceList]; |
||||
|
|
||||
|
// 实际API调用示例: |
||||
|
// let res = await apiRoute.getServiceList({}); |
||||
|
// if (res.code === 1) { |
||||
|
// this.serviceList = res.data; |
||||
|
// this.filteredServiceList = [...this.serviceList]; |
||||
|
// } |
||||
|
} catch (error) { |
||||
|
console.error('获取服务列表失败:', error); |
||||
|
uni.showToast({ |
||||
|
title: '获取数据失败', |
||||
|
icon: 'none' |
||||
|
}); |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
// 搜索处理 |
||||
|
handleSearch() { |
||||
|
this.filterServices(); |
||||
|
}, |
||||
|
|
||||
|
// 筛选服务 |
||||
|
filterServices() { |
||||
|
let filtered = [...this.serviceList]; |
||||
|
|
||||
|
// 按搜索文本筛选 |
||||
|
if (this.searchText) { |
||||
|
filtered = filtered.filter(service => |
||||
|
service.name.includes(this.searchText) || |
||||
|
service.type.includes(this.searchText) || |
||||
|
service.description.includes(this.searchText) |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
// 按状态筛选 |
||||
|
if (this.selectedStatus) { |
||||
|
filtered = filtered.filter(service => service.status === this.selectedStatus); |
||||
|
} |
||||
|
|
||||
|
this.filteredServiceList = filtered; |
||||
|
}, |
||||
|
|
||||
|
// 显示筛选 |
||||
|
showFilter() { |
||||
|
this.showFilterPopup = true; |
||||
|
}, |
||||
|
|
||||
|
// 重置筛选 |
||||
|
resetFilter() { |
||||
|
this.selectedStatus = ''; |
||||
|
this.filterServices(); |
||||
|
}, |
||||
|
|
||||
|
// 应用筛选 |
||||
|
applyFilter() { |
||||
|
this.filterServices(); |
||||
|
this.showFilterPopup = false; |
||||
|
}, |
||||
|
|
||||
|
|
||||
|
// 跳转到详情页 |
||||
|
goToDetail(service) { |
||||
|
this.$navigateTo({ |
||||
|
url: `/pages/coach/my/service_detail?id=${service.id}` |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="less" scoped> |
||||
|
.container { |
||||
|
background: #f5f5f5; |
||||
|
min-height: 100vh; |
||||
|
} |
||||
|
|
||||
|
.header { |
||||
|
background: white; |
||||
|
padding: 20rpx; |
||||
|
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1); |
||||
|
} |
||||
|
|
||||
|
.search-bar { |
||||
|
display: flex; |
||||
|
gap: 20rpx; |
||||
|
align-items: center; |
||||
|
} |
||||
|
|
||||
|
.search-input { |
||||
|
flex: 1; |
||||
|
background: #f5f5f5; |
||||
|
border-radius: 25rpx; |
||||
|
padding: 15rpx 30rpx; |
||||
|
|
||||
|
input { |
||||
|
width: 100%; |
||||
|
font-size: 28rpx; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.filter-btn { |
||||
|
background: #29d3b4; |
||||
|
color: white; |
||||
|
padding: 15rpx 30rpx; |
||||
|
border-radius: 25rpx; |
||||
|
font-size: 28rpx; |
||||
|
} |
||||
|
|
||||
|
.service-list { |
||||
|
padding: 20rpx; |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
gap: 20rpx; |
||||
|
} |
||||
|
|
||||
|
.service-item { |
||||
|
background: white; |
||||
|
border-radius: 16rpx; |
||||
|
padding: 30rpx; |
||||
|
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1); |
||||
|
} |
||||
|
|
||||
|
.service-header { |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
align-items: center; |
||||
|
margin-bottom: 20rpx; |
||||
|
} |
||||
|
|
||||
|
.service-title { |
||||
|
font-size: 32rpx; |
||||
|
font-weight: bold; |
||||
|
color: #333; |
||||
|
flex: 1; |
||||
|
margin-right: 20rpx; |
||||
|
} |
||||
|
|
||||
|
.service-badge { |
||||
|
padding: 8rpx 16rpx; |
||||
|
border-radius: 20rpx; |
||||
|
font-size: 24rpx; |
||||
|
|
||||
|
&.badge-success { |
||||
|
background: #e8f5e8; |
||||
|
color: #52c41a; |
||||
|
} |
||||
|
|
||||
|
&.badge-warning { |
||||
|
background: #fff2e8; |
||||
|
color: #fa8c16; |
||||
|
} |
||||
|
|
||||
|
&.badge-danger { |
||||
|
background: #fff1f0; |
||||
|
color: #ff4d4f; |
||||
|
} |
||||
|
|
||||
|
&.badge-default { |
||||
|
background: #f0f0f0; |
||||
|
color: #666; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.service-content { |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
gap: 15rpx; |
||||
|
} |
||||
|
|
||||
|
.service-meta { |
||||
|
display: flex; |
||||
|
gap: 30rpx; |
||||
|
} |
||||
|
|
||||
|
.meta-item { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
|
||||
|
.meta-label { |
||||
|
color: #666; |
||||
|
font-size: 26rpx; |
||||
|
} |
||||
|
|
||||
|
.meta-value { |
||||
|
color: #333; |
||||
|
font-size: 26rpx; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.service-desc { |
||||
|
color: #666; |
||||
|
font-size: 26rpx; |
||||
|
line-height: 1.5; |
||||
|
} |
||||
|
|
||||
|
.service-footer { |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
align-items: center; |
||||
|
padding-top: 15rpx; |
||||
|
border-top: 1px solid #f0f0f0; |
||||
|
} |
||||
|
|
||||
|
.service-time { |
||||
|
color: #999; |
||||
|
font-size: 24rpx; |
||||
|
} |
||||
|
|
||||
|
.service-action { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
color: #29d3b4; |
||||
|
font-size: 26rpx; |
||||
|
|
||||
|
.action-arrow { |
||||
|
margin-left: 10rpx; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.empty-state { |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
padding: 100rpx 0; |
||||
|
|
||||
|
.empty-icon { |
||||
|
width: 120rpx; |
||||
|
height: 120rpx; |
||||
|
margin-bottom: 30rpx; |
||||
|
opacity: 0.3; |
||||
|
} |
||||
|
|
||||
|
.empty-text { |
||||
|
color: #999; |
||||
|
font-size: 28rpx; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 筛选弹窗样式 |
||||
|
.filter-popup { |
||||
|
background: white; |
||||
|
border-radius: 20rpx 20rpx 0 0; |
||||
|
max-height: 80vh; |
||||
|
} |
||||
|
|
||||
|
.popup-header { |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
align-items: center; |
||||
|
padding: 30rpx; |
||||
|
border-bottom: 1px solid #f0f0f0; |
||||
|
} |
||||
|
|
||||
|
.popup-title { |
||||
|
font-size: 32rpx; |
||||
|
font-weight: bold; |
||||
|
} |
||||
|
|
||||
|
.popup-close { |
||||
|
font-size: 36rpx; |
||||
|
color: #999; |
||||
|
} |
||||
|
|
||||
|
.filter-content { |
||||
|
padding: 30rpx; |
||||
|
} |
||||
|
|
||||
|
.filter-group { |
||||
|
margin-bottom: 40rpx; |
||||
|
} |
||||
|
|
||||
|
.filter-title { |
||||
|
font-size: 28rpx; |
||||
|
font-weight: bold; |
||||
|
margin-bottom: 20rpx; |
||||
|
color: #333; |
||||
|
} |
||||
|
|
||||
|
.filter-options { |
||||
|
display: flex; |
||||
|
flex-wrap: wrap; |
||||
|
gap: 20rpx; |
||||
|
} |
||||
|
|
||||
|
.filter-option { |
||||
|
padding: 15rpx 30rpx; |
||||
|
background: #f5f5f5; |
||||
|
border-radius: 25rpx; |
||||
|
font-size: 26rpx; |
||||
|
color: #666; |
||||
|
|
||||
|
&.active { |
||||
|
background: #29d3b4; |
||||
|
color: white; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.popup-actions { |
||||
|
display: flex; |
||||
|
gap: 20rpx; |
||||
|
padding: 30rpx; |
||||
|
border-top: 1px solid #f0f0f0; |
||||
|
} |
||||
|
|
||||
|
.btn { |
||||
|
flex: 1; |
||||
|
padding: 20rpx; |
||||
|
border-radius: 12rpx; |
||||
|
text-align: center; |
||||
|
font-size: 28rpx; |
||||
|
|
||||
|
&.btn-reset { |
||||
|
background: #f0f0f0; |
||||
|
color: #333; |
||||
|
} |
||||
|
|
||||
|
&.btn-confirm { |
||||
|
background: #29d3b4; |
||||
|
color: white; |
||||
|
} |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,499 @@ |
|||||
|
<template> |
||||
|
<view class="container"> |
||||
|
<uni-nav-bar fixed status-bar left-icon="left" title="学员详情" @clickLeft="navigateBack"></uni-nav-bar> |
||||
|
<view class="content" v-if="studentInfo"> |
||||
|
<view class="student-header"> |
||||
|
<view class="avatar-box"> |
||||
|
<image :src="studentInfo.avatar || '/static/icon-img/avatar.png'" mode="aspectFill" class="avatar-img"></image> |
||||
|
</view> |
||||
|
<view class="name-box"> |
||||
|
<text class="name">{{studentInfo.name}}</text> |
||||
|
<text class="id">ID: {{studentInfo.id}}</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<view class="card-section"> |
||||
|
<view class="section-title">基本信息</view> |
||||
|
<view class="info-card"> |
||||
|
<view class="info-item"> |
||||
|
<text class="item-label">所属校区</text> |
||||
|
<text class="item-value">{{studentInfo.campus}}</text> |
||||
|
</view> |
||||
|
<view class="info-item"> |
||||
|
<text class="item-label">联系电话</text> |
||||
|
<text class="item-value">{{studentInfo.phone}}</text> |
||||
|
</view> |
||||
|
<view class="info-item"> |
||||
|
<text class="item-label">年龄</text> |
||||
|
<text class="item-value">{{studentInfo.age}}岁</text> |
||||
|
</view> |
||||
|
<view class="info-item"> |
||||
|
<text class="item-label">入学时间</text> |
||||
|
<text class="item-value">{{studentInfo.enrollmentDate}}</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<view class="card-section"> |
||||
|
<view class="section-title">课程信息</view> |
||||
|
<view class="info-card"> |
||||
|
<view class="info-item"> |
||||
|
<text class="item-label">剩余课程</text> |
||||
|
<text class="item-value highlight">{{studentInfo.remainingCourses}}节</text> |
||||
|
</view> |
||||
|
<view class="info-item"> |
||||
|
<text class="item-label">课程到期时间</text> |
||||
|
<text class="item-value highlight">{{studentInfo.expiryDate}}</text> |
||||
|
</view> |
||||
|
<view class="info-item"> |
||||
|
<text class="item-label">已消课程</text> |
||||
|
<text class="item-value">{{studentInfo.completedCourses}}节</text> |
||||
|
</view> |
||||
|
<view class="info-item"> |
||||
|
<text class="item-label">报名课程</text> |
||||
|
<text class="item-value">{{studentInfo.courseName}}</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<view class="card-section"> |
||||
|
<view class="section-title">最近上课记录</view> |
||||
|
<view class="info-card" v-if="studentInfo.recentClasses && studentInfo.recentClasses.length > 0"> |
||||
|
<view class="class-item" v-for="(item, index) in studentInfo.recentClasses" :key="index"> |
||||
|
<view class="class-date">{{item.date}}</view> |
||||
|
<view class="class-info"> |
||||
|
<text class="class-name">{{item.courseName}}</text> |
||||
|
<text class="class-time">{{item.timeSlot}}</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
<view class="empty-class" v-else> |
||||
|
<text>暂无上课记录</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<view class="btn-group"> |
||||
|
<button class="btn primary" @click="contactStudent">联系学员</button> |
||||
|
<button class="btn secondary" @click="viewSchedule">查看课表</button> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<view v-else class="loading-box"> |
||||
|
<text>加载中...</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
export default { |
||||
|
data() { |
||||
|
return { |
||||
|
id: null, |
||||
|
studentInfo: null |
||||
|
} |
||||
|
}, |
||||
|
onLoad(options) { |
||||
|
if (options.id) { |
||||
|
this.id = options.id; |
||||
|
this.getStudentDetail(); |
||||
|
} else { |
||||
|
uni.showToast({ |
||||
|
title: '参数错误', |
||||
|
icon: 'none' |
||||
|
}); |
||||
|
setTimeout(() => { |
||||
|
this.navigateBack(); |
||||
|
}, 1500); |
||||
|
} |
||||
|
}, |
||||
|
methods: { |
||||
|
navigateBack() { |
||||
|
uni.navigateBack(); |
||||
|
}, |
||||
|
getStudentDetail() { |
||||
|
// 模拟数据,实际开发中应该从API获取 |
||||
|
// try { |
||||
|
// const res = await memberApi.getStudentDetail({id: this.id}); |
||||
|
// if(res.code == 1) { |
||||
|
// this.studentInfo = res.data || null; |
||||
|
// } else { |
||||
|
// uni.showToast({ |
||||
|
// title: res.msg || '获取学员详情失败', |
||||
|
// icon: 'none' |
||||
|
// }); |
||||
|
// } |
||||
|
// } catch(error) { |
||||
|
// console.error('获取学员详情失败:', error); |
||||
|
// uni.showToast({ |
||||
|
// title: '获取学员详情失败', |
||||
|
// icon: 'none' |
||||
|
// }); |
||||
|
// } |
||||
|
|
||||
|
// 使用模拟数据 |
||||
|
setTimeout(() => { |
||||
|
// 定义一些模拟数据对象 |
||||
|
const studentData = { |
||||
|
'1': { |
||||
|
id: '1', |
||||
|
name: '张三', |
||||
|
avatar: '/static/icon-img/avatar.png', |
||||
|
campus: '总部校区', |
||||
|
phone: '13812341000', |
||||
|
age: 11, |
||||
|
enrollmentDate: '2023-01-11', |
||||
|
remainingCourses: 10, |
||||
|
expiryDate: '2023-12-31', |
||||
|
completedCourses: 20, |
||||
|
courseName: '少儿英语基础班', |
||||
|
recentClasses: [ |
||||
|
{ |
||||
|
date: '2023-09-15', |
||||
|
courseName: '少儿英语基础班', |
||||
|
timeSlot: '15:30-17:00' |
||||
|
}, |
||||
|
{ |
||||
|
date: '2023-09-08', |
||||
|
courseName: '少儿英语基础班', |
||||
|
timeSlot: '15:30-17:00' |
||||
|
}, |
||||
|
{ |
||||
|
date: '2023-09-01', |
||||
|
courseName: '少儿英语基础班', |
||||
|
timeSlot: '15:30-17:00' |
||||
|
} |
||||
|
] |
||||
|
}, |
||||
|
'2': { |
||||
|
id: '2', |
||||
|
name: '李四', |
||||
|
avatar: '/static/icon-img/avatar.png', |
||||
|
campus: '西区校区', |
||||
|
phone: '13812341001', |
||||
|
age: 12, |
||||
|
enrollmentDate: '2023-01-12', |
||||
|
remainingCourses: 5, |
||||
|
expiryDate: '2023-11-15', |
||||
|
completedCourses: 25, |
||||
|
courseName: '少儿英语进阶班', |
||||
|
recentClasses: [ |
||||
|
{ |
||||
|
date: '2023-09-14', |
||||
|
courseName: '少儿英语进阶班', |
||||
|
timeSlot: '14:00-15:30' |
||||
|
}, |
||||
|
{ |
||||
|
date: '2023-09-07', |
||||
|
courseName: '少儿英语进阶班', |
||||
|
timeSlot: '14:00-15:30' |
||||
|
} |
||||
|
] |
||||
|
}, |
||||
|
'3': { |
||||
|
id: '3', |
||||
|
name: '王五', |
||||
|
avatar: '/static/icon-img/avatar.png', |
||||
|
campus: '东区校区', |
||||
|
phone: '13812341002', |
||||
|
age: 13, |
||||
|
enrollmentDate: '2023-01-13', |
||||
|
remainingCourses: 15, |
||||
|
expiryDate: '2024-01-20', |
||||
|
completedCourses: 15, |
||||
|
courseName: '少儿英语口语班', |
||||
|
recentClasses: [ |
||||
|
{ |
||||
|
date: '2023-09-16', |
||||
|
courseName: '少儿英语口语班', |
||||
|
timeSlot: '10:00-11:30' |
||||
|
}, |
||||
|
{ |
||||
|
date: '2023-09-09', |
||||
|
courseName: '少儿英语口语班', |
||||
|
timeSlot: '10:00-11:30' |
||||
|
}, |
||||
|
{ |
||||
|
date: '2023-09-02', |
||||
|
courseName: '少儿英语口语班', |
||||
|
timeSlot: '10:00-11:30' |
||||
|
} |
||||
|
] |
||||
|
}, |
||||
|
'4': { |
||||
|
id: '4', |
||||
|
name: '赵六', |
||||
|
avatar: '/static/icon-img/avatar.png', |
||||
|
campus: '南区校区', |
||||
|
phone: '13812341003', |
||||
|
age: 10, |
||||
|
enrollmentDate: '2023-02-15', |
||||
|
remainingCourses: 8, |
||||
|
expiryDate: '2023-11-30', |
||||
|
completedCourses: 12, |
||||
|
courseName: '少儿英语基础班', |
||||
|
recentClasses: [ |
||||
|
{ |
||||
|
date: '2023-09-13', |
||||
|
courseName: '少儿英语基础班', |
||||
|
timeSlot: '16:00-17:30' |
||||
|
}, |
||||
|
{ |
||||
|
date: '2023-09-06', |
||||
|
courseName: '少儿英语基础班', |
||||
|
timeSlot: '16:00-17:30' |
||||
|
} |
||||
|
] |
||||
|
}, |
||||
|
'5': { |
||||
|
id: '5', |
||||
|
name: '刘七', |
||||
|
avatar: '/static/icon-img/avatar.png', |
||||
|
campus: '北区校区', |
||||
|
phone: '13812341004', |
||||
|
age: 14, |
||||
|
enrollmentDate: '2023-03-20', |
||||
|
remainingCourses: 20, |
||||
|
expiryDate: '2024-02-15', |
||||
|
completedCourses: 10, |
||||
|
courseName: '少儿英语进阶班', |
||||
|
recentClasses: [ |
||||
|
{ |
||||
|
date: '2023-09-12', |
||||
|
courseName: '少儿英语进阶班', |
||||
|
timeSlot: '17:00-18:30' |
||||
|
}, |
||||
|
{ |
||||
|
date: '2023-09-05', |
||||
|
courseName: '少儿英语进阶班', |
||||
|
timeSlot: '17:00-18:30' |
||||
|
} |
||||
|
] |
||||
|
}, |
||||
|
'6': { |
||||
|
id: '6', |
||||
|
name: '陈八', |
||||
|
avatar: '/static/icon-img/avatar.png', |
||||
|
campus: '总部校区', |
||||
|
phone: '13812341005', |
||||
|
age: 9, |
||||
|
enrollmentDate: '2023-04-05', |
||||
|
remainingCourses: 3, |
||||
|
expiryDate: '2023-10-30', |
||||
|
completedCourses: 27, |
||||
|
courseName: '少儿英语口语班', |
||||
|
recentClasses: [ |
||||
|
{ |
||||
|
date: '2023-09-11', |
||||
|
courseName: '少儿英语口语班', |
||||
|
timeSlot: '14:30-16:00' |
||||
|
}, |
||||
|
{ |
||||
|
date: '2023-09-04', |
||||
|
courseName: '少儿英语口语班', |
||||
|
timeSlot: '14:30-16:00' |
||||
|
} |
||||
|
] |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
// 根据ID获取对应的学员数据 |
||||
|
this.studentInfo = studentData[this.id] || { |
||||
|
id: this.id, |
||||
|
name: '未知学员', |
||||
|
avatar: '/static/icon-img/avatar.png', |
||||
|
campus: '未知校区', |
||||
|
phone: '暂无', |
||||
|
age: '暂无', |
||||
|
enrollmentDate: '暂无', |
||||
|
remainingCourses: 0, |
||||
|
expiryDate: '暂无', |
||||
|
completedCourses: 0, |
||||
|
courseName: '暂无', |
||||
|
recentClasses: [] |
||||
|
}; |
||||
|
}, 500); |
||||
|
}, |
||||
|
contactStudent() { |
||||
|
if (this.studentInfo && this.studentInfo.phone) { |
||||
|
uni.makePhoneCall({ |
||||
|
phoneNumber: this.studentInfo.phone.replace(/\*/g, '0') |
||||
|
}); |
||||
|
} |
||||
|
}, |
||||
|
viewSchedule() { |
||||
|
uni.navigateTo({ |
||||
|
url: `/pages/coach/student/timetable?id=${this.id}` |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss"> |
||||
|
.container { |
||||
|
min-height: 100vh; |
||||
|
background-color: #F5F5F5; |
||||
|
} |
||||
|
|
||||
|
.content { |
||||
|
padding: 20rpx; |
||||
|
} |
||||
|
|
||||
|
.loading-box { |
||||
|
display: flex; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
height: 80vh; |
||||
|
color: #999; |
||||
|
font-size: 28rpx; |
||||
|
} |
||||
|
|
||||
|
.student-header { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
background-color: #FFFFFF; |
||||
|
border-radius: 12rpx; |
||||
|
padding: 40rpx; |
||||
|
margin-bottom: 20rpx; |
||||
|
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05); |
||||
|
|
||||
|
.avatar-box { |
||||
|
width: 150rpx; |
||||
|
height: 150rpx; |
||||
|
border-radius: 75rpx; |
||||
|
overflow: hidden; |
||||
|
margin-right: 30rpx; |
||||
|
|
||||
|
.avatar-img { |
||||
|
width: 100%; |
||||
|
height: 100%; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.name-box { |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
|
||||
|
.name { |
||||
|
font-size: 36rpx; |
||||
|
font-weight: bold; |
||||
|
color: #333; |
||||
|
margin-bottom: 10rpx; |
||||
|
} |
||||
|
|
||||
|
.id { |
||||
|
font-size: 24rpx; |
||||
|
color: #999; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.card-section { |
||||
|
margin-bottom: 20rpx; |
||||
|
|
||||
|
.section-title { |
||||
|
font-size: 30rpx; |
||||
|
font-weight: bold; |
||||
|
color: #333; |
||||
|
margin: 30rpx 10rpx 20rpx; |
||||
|
} |
||||
|
|
||||
|
.info-card { |
||||
|
background-color: #FFFFFF; |
||||
|
border-radius: 12rpx; |
||||
|
padding: 20rpx 30rpx; |
||||
|
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05); |
||||
|
} |
||||
|
|
||||
|
.info-item { |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
padding: 20rpx 0; |
||||
|
border-bottom: 1px solid #F5F5F5; |
||||
|
|
||||
|
&:last-child { |
||||
|
border-bottom: none; |
||||
|
} |
||||
|
|
||||
|
.item-label { |
||||
|
color: #666; |
||||
|
font-size: 28rpx; |
||||
|
} |
||||
|
|
||||
|
.item-value { |
||||
|
color: #333; |
||||
|
font-size: 28rpx; |
||||
|
|
||||
|
&.highlight { |
||||
|
color: #FF6600; |
||||
|
font-weight: bold; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.class-item { |
||||
|
display: flex; |
||||
|
padding: 20rpx 0; |
||||
|
border-bottom: 1px solid #F5F5F5; |
||||
|
|
||||
|
&:last-child { |
||||
|
border-bottom: none; |
||||
|
} |
||||
|
|
||||
|
.class-date { |
||||
|
width: 180rpx; |
||||
|
font-size: 28rpx; |
||||
|
color: #666; |
||||
|
} |
||||
|
|
||||
|
.class-info { |
||||
|
flex: 1; |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
|
||||
|
.class-name { |
||||
|
font-size: 28rpx; |
||||
|
color: #333; |
||||
|
margin-bottom: 10rpx; |
||||
|
} |
||||
|
|
||||
|
.class-time { |
||||
|
font-size: 24rpx; |
||||
|
color: #999; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.empty-class { |
||||
|
padding: 40rpx 0; |
||||
|
text-align: center; |
||||
|
color: #999; |
||||
|
font-size: 28rpx; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.btn-group { |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
margin: 40rpx 0; |
||||
|
|
||||
|
.btn { |
||||
|
width: 48%; |
||||
|
height: 80rpx; |
||||
|
line-height: 80rpx; |
||||
|
border-radius: 40rpx; |
||||
|
font-size: 30rpx; |
||||
|
|
||||
|
&.primary { |
||||
|
background-color: #3B7CF9; |
||||
|
color: #FFFFFF; |
||||
|
} |
||||
|
|
||||
|
&.secondary { |
||||
|
background-color: #FFFFFF; |
||||
|
color: #3B7CF9; |
||||
|
border: 1px solid #3B7CF9; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,216 @@ |
|||||
|
<template> |
||||
|
<view class="container"> |
||||
|
<view class="content"> |
||||
|
<view v-if="studentList.length === 0" class="empty-box"> |
||||
|
<image src="/static/icon-img/empty.png" mode="aspectFit" class="empty-img"></image> |
||||
|
<text class="empty-text">暂无学员数据</text> |
||||
|
</view> |
||||
|
<view v-else class="student-list"> |
||||
|
<view v-for="(item, index) in studentList" :key="index" class="student-item" @click="goToDetail(item)"> |
||||
|
<view class="student-card"> |
||||
|
<view class="student-avatar"> |
||||
|
<image :src="item.avatar || '/static/icon-img/avatar.png'" mode="aspectFill" class="avatar-img"></image> |
||||
|
</view> |
||||
|
<view class="student-info"> |
||||
|
<view class="student-name">{{item.name}}</view> |
||||
|
<view class="info-row"> |
||||
|
<text class="info-label">所属校区:</text> |
||||
|
<text class="info-value">{{item.campus}}</text> |
||||
|
</view> |
||||
|
<view class="info-row"> |
||||
|
<text class="info-label">剩余课程:</text> |
||||
|
<text class="info-value">{{item.remainingCourses}}节</text> |
||||
|
</view> |
||||
|
<view class="info-row"> |
||||
|
<text class="info-label">到期时间:</text> |
||||
|
<text class="info-value">{{item.expiryDate}}</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
<view class="arrow-right"> |
||||
|
<uni-icons type="right" size="16" color="#CCCCCC"></uni-icons> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
<AQTabber /> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import AQTabber from "@/components/AQ/AQTabber.vue" |
||||
|
export default { |
||||
|
components: { |
||||
|
AQTabber, |
||||
|
}, |
||||
|
data() { |
||||
|
return { |
||||
|
studentList: [] |
||||
|
} |
||||
|
}, |
||||
|
onLoad() { |
||||
|
this.getStudentList(); |
||||
|
}, |
||||
|
methods: { |
||||
|
navigateBack() { |
||||
|
uni.navigateBack(); |
||||
|
}, |
||||
|
getStudentList() { |
||||
|
// 模拟数据,实际开发中应该从API获取 |
||||
|
// const res = await memberApi.getStudentList({}); |
||||
|
// if(res.code == 1) { |
||||
|
// this.studentList = res.data || []; |
||||
|
// } else { |
||||
|
// uni.showToast({ |
||||
|
// title: res.msg || '获取学员列表失败', |
||||
|
// icon: 'none' |
||||
|
// }); |
||||
|
// } |
||||
|
|
||||
|
// 使用模拟数据 |
||||
|
this.studentList = [ |
||||
|
{ |
||||
|
id: 1, |
||||
|
name: '张三', |
||||
|
avatar: '/static/icon-img/avatar.png', |
||||
|
campus: '总部校区', |
||||
|
remainingCourses: 10, |
||||
|
expiryDate: '2023-12-31' |
||||
|
}, |
||||
|
{ |
||||
|
id: 2, |
||||
|
name: '李四', |
||||
|
avatar: '/static/icon-img/avatar.png', |
||||
|
campus: '西区校区', |
||||
|
remainingCourses: 5, |
||||
|
expiryDate: '2023-11-15' |
||||
|
}, |
||||
|
{ |
||||
|
id: 3, |
||||
|
name: '王五', |
||||
|
avatar: '/static/icon-img/avatar.png', |
||||
|
campus: '东区校区', |
||||
|
remainingCourses: 15, |
||||
|
expiryDate: '2024-01-20' |
||||
|
}, |
||||
|
{ |
||||
|
id: 4, |
||||
|
name: '赵六', |
||||
|
avatar: '/static/icon-img/avatar.png', |
||||
|
campus: '南区校区', |
||||
|
remainingCourses: 8, |
||||
|
expiryDate: '2023-11-30' |
||||
|
}, |
||||
|
{ |
||||
|
id: 5, |
||||
|
name: '刘七', |
||||
|
avatar: '/static/icon-img/avatar.png', |
||||
|
campus: '北区校区', |
||||
|
remainingCourses: 20, |
||||
|
expiryDate: '2024-02-15' |
||||
|
}, |
||||
|
{ |
||||
|
id: 6, |
||||
|
name: '陈八', |
||||
|
avatar: '/static/icon-img/avatar.png', |
||||
|
campus: '总部校区', |
||||
|
remainingCourses: 3, |
||||
|
expiryDate: '2023-10-30' |
||||
|
} |
||||
|
]; |
||||
|
}, |
||||
|
goToDetail(student) { |
||||
|
uni.navigateTo({ |
||||
|
url: `/pages/market/clue/clue_info?resource_sharing_id=25` |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss"> |
||||
|
.container { |
||||
|
min-height: 100vh; |
||||
|
background-color: #F5F5F5; |
||||
|
} |
||||
|
|
||||
|
.content { |
||||
|
padding: 20rpx; |
||||
|
} |
||||
|
|
||||
|
.empty-box { |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
padding-top: 200rpx; |
||||
|
|
||||
|
.empty-img { |
||||
|
width: 200rpx; |
||||
|
height: 200rpx; |
||||
|
} |
||||
|
|
||||
|
.empty-text { |
||||
|
margin-top: 20rpx; |
||||
|
font-size: 28rpx; |
||||
|
color: #999; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.student-list { |
||||
|
.student-item { |
||||
|
margin-bottom: 20rpx; |
||||
|
} |
||||
|
|
||||
|
.student-card { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
background-color: #FFFFFF; |
||||
|
border-radius: 12rpx; |
||||
|
padding: 30rpx; |
||||
|
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05); |
||||
|
} |
||||
|
|
||||
|
.student-avatar { |
||||
|
width: 120rpx; |
||||
|
height: 120rpx; |
||||
|
border-radius: 60rpx; |
||||
|
overflow: hidden; |
||||
|
margin-right: 30rpx; |
||||
|
|
||||
|
.avatar-img { |
||||
|
width: 100%; |
||||
|
height: 100%; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.student-info { |
||||
|
flex: 1; |
||||
|
} |
||||
|
|
||||
|
.student-name { |
||||
|
font-size: 32rpx; |
||||
|
font-weight: bold; |
||||
|
margin-bottom: 10rpx; |
||||
|
color: #333; |
||||
|
} |
||||
|
|
||||
|
.info-row { |
||||
|
display: flex; |
||||
|
font-size: 26rpx; |
||||
|
margin-top: 8rpx; |
||||
|
|
||||
|
.info-label { |
||||
|
color: #666; |
||||
|
} |
||||
|
|
||||
|
.info-value { |
||||
|
color: #333; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.arrow-right { |
||||
|
padding-left: 20rpx; |
||||
|
} |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,239 @@ |
|||||
|
<template> |
||||
|
<view class="class-arrange-root"> |
||||
|
<!-- 顶部日期选择 --> |
||||
|
<view class="date-bar"> |
||||
|
<view class="date-item" v-for="(item, idx) in weekList" :key="idx" :class="{active: item.status}" @click="getDate(item.date,item.day)"> |
||||
|
<view class="week">{{ item.week }}</view> |
||||
|
<view class="day">{{ item.day }}</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
<!-- "查看更多"按钮移到日期条下方 --> |
||||
|
<view class="more-bar-wrapper"> |
||||
|
<view class="more-bar" @click="openCalendar"> |
||||
|
<text>查看更多</text> |
||||
|
<uni-icons type="arrowdown" size="18" color="#bdbdbd" /> |
||||
|
</view> |
||||
|
</view> |
||||
|
<!-- 日历底部弹窗 --> |
||||
|
<uni-popup ref="calendarPopup" type="bottom"> |
||||
|
<uni-calendar @change="onCalendarChange" /> |
||||
|
</uni-popup> |
||||
|
|
||||
|
<!-- 课程卡片列表 --> |
||||
|
<view class="course-list"> |
||||
|
<view class="course-card" v-for="(course, idx) in courseList" :key="idx"> |
||||
|
<view class="card-header"> |
||||
|
<view class="status-end">{{ getStatusText(course.status) }}</view> |
||||
|
</view> |
||||
|
<view class="card-body"> |
||||
|
<view class="row">时间:{{ course.course_date }}</view> |
||||
|
<view class="row">校区:{{ course.campus_name }}</view> |
||||
|
<view class="row">教室:{{ course.venue.venue_name }}</view> |
||||
|
<view class="row">课程:{{ course.course.course_name }}</view> |
||||
|
<view class="row">人数:{{ course.available_capacity }}</view> |
||||
|
</view> |
||||
|
<view class="card-footer"> |
||||
|
<view class="sign-info"></view> |
||||
|
<button class="detail-btn" @click="viewDetail(course)">详情</button> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import apiRoute from '@/api/apiRoute.js'; |
||||
|
export default { |
||||
|
data() { |
||||
|
return { |
||||
|
weekList: [], |
||||
|
selectedDayIndex: 4, |
||||
|
date: '', |
||||
|
courseList: [], |
||||
|
resource_id:'' |
||||
|
}; |
||||
|
}, |
||||
|
onLoad(options) { |
||||
|
this.resource_id = options.resource_id |
||||
|
this.getDate(); |
||||
|
|
||||
|
}, |
||||
|
methods: { |
||||
|
async getDate(date = '', day = '') { |
||||
|
try { |
||||
|
let res = await apiRoute.getDate({ |
||||
|
'date': date, |
||||
|
'day': day |
||||
|
}) |
||||
|
this.weekList = res.data.dates |
||||
|
this.date = res.data.date |
||||
|
|
||||
|
let data = await apiRoute.courseAllList({ |
||||
|
'schedule_date': this.date |
||||
|
}) |
||||
|
this.courseList = data.data |
||||
|
|
||||
|
|
||||
|
} catch (error) { |
||||
|
console.error('获取信息失败:', error); |
||||
|
} |
||||
|
}, |
||||
|
openCalendar() { |
||||
|
this.$refs.calendarPopup.open(); |
||||
|
}, |
||||
|
closeCalendar() { |
||||
|
console.log(123123) |
||||
|
this.$refs.calendarPopup.close(); |
||||
|
}, |
||||
|
viewDetail(course) { |
||||
|
// 跳转到课程详情页 |
||||
|
this.$navigateTo({ |
||||
|
url: '/pages/market/clue/class_arrangement_detail?id=' + course.id+'&resource_id='+this.resource_id |
||||
|
}); |
||||
|
}, |
||||
|
onCalendarConfirm(e) { |
||||
|
|
||||
|
// 这里可以处理选中的日期 e.fulldate |
||||
|
uni.showToast({ |
||||
|
title: '选择日期:' + e.fulldate, |
||||
|
icon: 'none' |
||||
|
}); |
||||
|
|
||||
|
this.closeCalendar(); |
||||
|
}, |
||||
|
onCalendarChange(e) { |
||||
|
this.getDate(e.fulldate, e.date); |
||||
|
this.closeCalendar(); |
||||
|
}, |
||||
|
getStatusText(status) { |
||||
|
const statusMap = { |
||||
|
pending: '待开始', |
||||
|
upcoming: '即将开始', |
||||
|
ongoing: '进行中', |
||||
|
completed: '已结束' |
||||
|
}; |
||||
|
return statusMap[status] || status; |
||||
|
} |
||||
|
}, |
||||
|
}; |
||||
|
</script> |
||||
|
|
||||
|
<style lang="less" scoped> |
||||
|
.class-arrange-root { |
||||
|
background: #232323; |
||||
|
min-height: 100vh; |
||||
|
padding-bottom: 30rpx; |
||||
|
} |
||||
|
|
||||
|
.date-bar { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
background: #232323; |
||||
|
padding: 0 0 10rpx 0; |
||||
|
overflow-x: auto; |
||||
|
border-bottom: 1px solid #333; |
||||
|
|
||||
|
.date-item { |
||||
|
flex: 1; |
||||
|
text-align: center; |
||||
|
color: #bdbdbd; |
||||
|
padding: 16rpx 0 0 0; |
||||
|
|
||||
|
.week { |
||||
|
font-size: 22rpx; |
||||
|
} |
||||
|
|
||||
|
.day { |
||||
|
font-size: 28rpx; |
||||
|
margin-top: 4rpx; |
||||
|
} |
||||
|
|
||||
|
&.active { |
||||
|
color: #29d3b4; |
||||
|
|
||||
|
.day { |
||||
|
border-radius: 50%; |
||||
|
background: #333; |
||||
|
color: #29d3b4; |
||||
|
padding: 2rpx 10rpx; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.more-bar-wrapper { |
||||
|
width: 100%; |
||||
|
display: flex; |
||||
|
justify-content: center; |
||||
|
margin: 10rpx 0 0 0; |
||||
|
} |
||||
|
|
||||
|
.more-bar { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
color: #bdbdbd; |
||||
|
font-size: 22rpx; |
||||
|
cursor: pointer; |
||||
|
background: #333; |
||||
|
border-radius: 20rpx; |
||||
|
padding: 8rpx 24rpx; |
||||
|
} |
||||
|
|
||||
|
.course-list { |
||||
|
margin-top: 20rpx; |
||||
|
|
||||
|
.course-card { |
||||
|
background: #434544; |
||||
|
border-radius: 10rpx; |
||||
|
margin: 0 0 20rpx 0; |
||||
|
padding: 0 0 20rpx 0; |
||||
|
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.08); |
||||
|
|
||||
|
.card-header { |
||||
|
display: flex; |
||||
|
justify-content: flex-end; |
||||
|
padding: 10rpx 20rpx 0 0; |
||||
|
|
||||
|
.status-end { |
||||
|
background: #e95c6b; |
||||
|
color: #fff; |
||||
|
border-radius: 10rpx; |
||||
|
padding: 4rpx 18rpx; |
||||
|
font-size: 22rpx; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.card-body { |
||||
|
padding: 0 20rpx; |
||||
|
|
||||
|
.row { |
||||
|
color: #fff; |
||||
|
font-size: 24rpx; |
||||
|
margin: 8rpx 0; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.card-footer { |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
align-items: center; |
||||
|
padding: 0 20rpx; |
||||
|
|
||||
|
.sign-info { |
||||
|
color: #bdbdbd; |
||||
|
font-size: 22rpx; |
||||
|
} |
||||
|
|
||||
|
.detail-btn { |
||||
|
background: transparent; |
||||
|
border: 2rpx solid #ffd86b; |
||||
|
color: #ffd86b; |
||||
|
border-radius: 8rpx; |
||||
|
padding: 6rpx 24rpx; |
||||
|
font-size: 24rpx; |
||||
|
margin-left: 10rpx; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,490 @@ |
|||||
|
<template> |
||||
|
<view class="detail-root"> |
||||
|
<view class="header"> |
||||
|
<view class="title">课程安排详情</view> |
||||
|
<view class="date">日期:{{ course_info.course_date }} {{course_info.time_slot}}</view> |
||||
|
</view> |
||||
|
<view class="section"> |
||||
|
<view class="section-title">学员列表</view> |
||||
|
<view class="student-list" v-if="course_info && course_info.available_capacity"> |
||||
|
<!-- 显示已安排的学员 --> |
||||
|
<view |
||||
|
v-for="(stu, idx) in students" |
||||
|
:key="idx" |
||||
|
class="student-item" |
||||
|
@tap="viewStudent(stu)" |
||||
|
> |
||||
|
<view class="avatar">{{ stu.name && stu.name.charAt(0) }}</view> |
||||
|
<view class="info"> |
||||
|
<view class="name">{{ stu.name }}</view> |
||||
|
<view class="desc">{{ getStatusText(stu.status) }}</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<!-- 显示空位 --> |
||||
|
<view |
||||
|
v-for="index in emptySeats" |
||||
|
:key="index" |
||||
|
class="student-item empty" |
||||
|
@tap="addStudent($event, index)" |
||||
|
> |
||||
|
<view class="avatar empty-avatar">+</view> |
||||
|
<view class="info"> |
||||
|
<view class="name">空位</view> |
||||
|
<view class="desc">点击添加学员</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
<!-- 没有数据时的占位区域 --> |
||||
|
<view class="empty-placeholder" v-else> |
||||
|
<image src="/static/icon-img/empty.png" mode="aspectFit"></image> |
||||
|
<text>暂无课程数据</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<!-- 请假原因弹窗 --> |
||||
|
<fui-modal ref="leaveReasonModal" :buttons="[]" width="600" title="请假申请"> |
||||
|
<view class="leave-form"> |
||||
|
<view class="leave-label">请假原因</view> |
||||
|
<view class="leave-input"> |
||||
|
<fui-textarea v-model="leaveReason" placeholder="请输入请假原因" :isCounter="true" :maxlength="200" :minHeight="200" :isAutoHeight="true"></fui-textarea> |
||||
|
</view> |
||||
|
<view class="leave-buttons"> |
||||
|
<fui-button background="#434544" color="#fff" borderColor="#666" btnSize="medium" @tap="$refs.leaveReasonModal.close()">取消</fui-button> |
||||
|
<fui-button background="#29d3b4" color="#fff" btnSize="medium" @tap="submitLeaveRequest">提交</fui-button> |
||||
|
</view> |
||||
|
</view> |
||||
|
</fui-modal> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import apiRoute from '@/api/apiRoute.js'; |
||||
|
export default { |
||||
|
data() { |
||||
|
return { |
||||
|
course_id:'', |
||||
|
course_info:[], |
||||
|
date: '', |
||||
|
students: [ |
||||
|
// { |
||||
|
// name: '张三', |
||||
|
// desc: '已签到' |
||||
|
// }, |
||||
|
// { |
||||
|
// name: '李四', |
||||
|
// desc: '未签到' |
||||
|
// }, |
||||
|
], |
||||
|
resource_id:'', |
||||
|
leaveReason: '', // 请假原因 |
||||
|
currentStudent: null, // 当前操作的学生 |
||||
|
emptySeats: [] // 空位数组 |
||||
|
}; |
||||
|
}, |
||||
|
onLoad(query) { |
||||
|
console.log('onLoad 参数:', query); |
||||
|
this.course_id = query.id || ''; |
||||
|
this.resource_id = query.resource_id |
||||
|
console.log('初始化参数 - course_id:', this.course_id, 'resource_id:', this.resource_id); |
||||
|
this.courseInfo(); |
||||
|
}, |
||||
|
methods: { |
||||
|
viewStudent(stu){ |
||||
|
console.log(stu, this.course_info); |
||||
|
|
||||
|
// 判断学员类型 |
||||
|
if (stu.person_type === 'customer_resource') { |
||||
|
// 如果是客户资源,弹出确认取消课程的弹窗 |
||||
|
uni.showModal({ |
||||
|
title: '取消课程', |
||||
|
content: `是否取消学员 ${stu.name} 的课程?`, |
||||
|
success: async (res) => { |
||||
|
if (res.confirm) { |
||||
|
// 用户点击确定,调用schedule_del接口 |
||||
|
try { |
||||
|
uni.showLoading({ |
||||
|
title: '处理中...' |
||||
|
}); |
||||
|
|
||||
|
const params = { |
||||
|
resources_id: stu.resources_id, |
||||
|
id: this.course_info.id |
||||
|
}; |
||||
|
|
||||
|
const result = await apiRoute.schedule_del(params); |
||||
|
|
||||
|
uni.hideLoading(); |
||||
|
|
||||
|
if (result.code === 1) { |
||||
|
uni.showToast({ |
||||
|
title: '取消课程成功', |
||||
|
icon: 'success' |
||||
|
}); |
||||
|
|
||||
|
// 刷新数据 |
||||
|
await this.courseInfo(); |
||||
|
} else { |
||||
|
uni.showToast({ |
||||
|
title: result.msg || '取消课程失败', |
||||
|
icon: 'none' |
||||
|
}); |
||||
|
} |
||||
|
} catch (error) { |
||||
|
uni.hideLoading(); |
||||
|
uni.showToast({ |
||||
|
title: '操作失败,请重试', |
||||
|
icon: 'none' |
||||
|
}); |
||||
|
console.error('取消课程失败:', error); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
} else if (stu.person_type === 'student') { |
||||
|
// 如果是学生,弹出请假原因输入框 |
||||
|
this.$refs.leaveReasonModal.open(); |
||||
|
this.currentStudent = stu; // 保存当前操作的学生信息 |
||||
|
} |
||||
|
}, |
||||
|
getStatusText(status) { |
||||
|
const statusMap = { |
||||
|
0: '待上课', |
||||
|
1: '已上课', |
||||
|
2: '请假' |
||||
|
}; |
||||
|
return statusMap[status] || status; |
||||
|
}, |
||||
|
async scheduleList() { |
||||
|
try { |
||||
|
console.log('开始获取学员列表, schedule_id:', this.course_id); |
||||
|
let res = await apiRoute.scheduleList({ |
||||
|
'schedule_id': this.course_id |
||||
|
}) |
||||
|
|
||||
|
console.log('学员列表响应:', res); |
||||
|
|
||||
|
if (res.code === 1 && Array.isArray(res.data)) { |
||||
|
// 确保学员列表按照位置排序 |
||||
|
this.students = res.data.sort((a, b) => { |
||||
|
// 如果有position字段,按position排序 |
||||
|
if (a.position !== undefined && b.position !== undefined) { |
||||
|
return parseInt(a.position) - parseInt(b.position); |
||||
|
} |
||||
|
// 否则按照ID排序 |
||||
|
return a.id - b.id; |
||||
|
}); |
||||
|
} else { |
||||
|
this.students = []; |
||||
|
} |
||||
|
|
||||
|
// 更新可用容量 |
||||
|
this.updateAvailableCapacity(); |
||||
|
|
||||
|
return this.students; |
||||
|
} catch (error) { |
||||
|
console.error('获取学员列表失败:', error); |
||||
|
this.students = []; |
||||
|
return []; |
||||
|
} |
||||
|
}, |
||||
|
async courseInfo() { |
||||
|
try { |
||||
|
console.log('开始获取课程信息, id:', this.course_id); |
||||
|
let res = await apiRoute.courseInfo({ |
||||
|
'id': this.course_id |
||||
|
}) |
||||
|
console.log('课程信息响应:', res); |
||||
|
|
||||
|
if (res.code === 1 && res.data) { |
||||
|
this.course_info = res.data; |
||||
|
|
||||
|
|
||||
|
// 获取学员列表 |
||||
|
await this.scheduleList(); |
||||
|
|
||||
|
// 计算可用容量 |
||||
|
this.updateAvailableCapacity(); |
||||
|
} else { |
||||
|
uni.showToast({ |
||||
|
title: res.msg || '获取课程信息失败', |
||||
|
icon: 'none' |
||||
|
}); |
||||
|
} |
||||
|
} catch (error) { |
||||
|
console.error('获取课程信息失败:', error); |
||||
|
uni.showToast({ |
||||
|
title: '获取课程信息失败', |
||||
|
icon: 'none' |
||||
|
}); |
||||
|
} |
||||
|
}, |
||||
|
// 更新可用容量 |
||||
|
updateAvailableCapacity() { |
||||
|
// 确保course_info和students都已加载 |
||||
|
if (!this.course_info || !this.students) return; |
||||
|
|
||||
|
console.log('更新可用容量 - 课程总容量:', this.course_info.available_capacity, '学员数量:', this.students.length); |
||||
|
|
||||
|
// 计算已占用的位置数 |
||||
|
const occupiedSeats = this.students.length; |
||||
|
|
||||
|
// 获取课程总容量 |
||||
|
const totalCapacity = parseInt(this.course_info.available_capacity) || 0; |
||||
|
|
||||
|
// 计算可用容量 |
||||
|
this.course_info.available_capacity = Math.max(0, totalCapacity - occupiedSeats); |
||||
|
|
||||
|
console.log('计算后的可用容量:', this.course_info.available_capacity); |
||||
|
|
||||
|
// 初始化空位数组 |
||||
|
this.emptySeats = []; |
||||
|
for (let i = 1; i <= this.course_info.available_capacity; i++) { |
||||
|
this.emptySeats.push(i); |
||||
|
} |
||||
|
}, |
||||
|
async addStudent(e, index) { |
||||
|
console.log('添加学员到位置:', index); |
||||
|
|
||||
|
const data = { |
||||
|
'resources_id': this.resource_id, |
||||
|
'person_type': 'customer_resource', |
||||
|
'schedule_id': this.course_id, |
||||
|
'course_date': this.course_info.course_date, |
||||
|
'time_slot': this.course_info.time_slot, |
||||
|
'position': index // 添加位置信息 |
||||
|
}; |
||||
|
|
||||
|
try { |
||||
|
uni.showLoading({ |
||||
|
title: '添加中...' |
||||
|
}); |
||||
|
|
||||
|
let res = await apiRoute.addSchedule(data) |
||||
|
|
||||
|
uni.hideLoading(); |
||||
|
|
||||
|
if(res.code == 1){ |
||||
|
uni.showToast({ |
||||
|
title: '添加成功', |
||||
|
icon: 'success' |
||||
|
}); |
||||
|
|
||||
|
// 刷新数据并更新可用容量 |
||||
|
await this.courseInfo(); |
||||
|
}else{ |
||||
|
uni.showToast({ |
||||
|
title: res.msg || '添加失败', |
||||
|
icon: 'none' |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
} catch (error) { |
||||
|
uni.hideLoading(); |
||||
|
uni.showToast({ |
||||
|
title: '添加失败', |
||||
|
icon: 'none' |
||||
|
}); |
||||
|
console.error('添加学员失败:', error); |
||||
|
} |
||||
|
}, |
||||
|
// 提交请假申请 |
||||
|
submitLeaveRequest() { |
||||
|
if (!this.leaveReason.trim()) { |
||||
|
uni.showToast({ |
||||
|
title: '请填写请假原因', |
||||
|
icon: 'none' |
||||
|
}); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
// 调用请假接口 |
||||
|
uni.showLoading({ |
||||
|
title: '提交中...' |
||||
|
}); |
||||
|
|
||||
|
// 构建请假参数 |
||||
|
const params = { |
||||
|
resources_id: this.currentStudent.resources_id, |
||||
|
id: this.course_info.id, |
||||
|
remark: this.leaveReason |
||||
|
}; |
||||
|
|
||||
|
// 调用请假接口 |
||||
|
apiRoute.schedule_del(params) |
||||
|
.then(res => { |
||||
|
uni.hideLoading(); |
||||
|
if (res.code === 1) { |
||||
|
uni.showToast({ |
||||
|
title: '请假申请提交成功', |
||||
|
icon: 'success' |
||||
|
}); |
||||
|
this.$refs.leaveReasonModal.close(); |
||||
|
this.leaveReason = ''; // 清空请假原因 |
||||
|
|
||||
|
// 刷新数据并更新可用容量 |
||||
|
this.courseInfo().then(() => { |
||||
|
// 确保数据刷新后更新可用容量 |
||||
|
this.updateAvailableCapacity(); |
||||
|
}); |
||||
|
} else { |
||||
|
uni.showToast({ |
||||
|
title: res.msg || '请假申请提交失败', |
||||
|
icon: 'none' |
||||
|
}); |
||||
|
} |
||||
|
}) |
||||
|
.catch(err => { |
||||
|
uni.hideLoading(); |
||||
|
uni.showToast({ |
||||
|
title: '请假申请提交失败,请重试', |
||||
|
icon: 'none' |
||||
|
}); |
||||
|
console.error('请假申请提交失败:', err); |
||||
|
}); |
||||
|
}, |
||||
|
}, |
||||
|
}; |
||||
|
</script> |
||||
|
|
||||
|
<style lang="less" scoped> |
||||
|
.detail-root { |
||||
|
background: #232323; |
||||
|
min-height: 100vh; |
||||
|
padding-bottom: 30rpx; |
||||
|
} |
||||
|
|
||||
|
.header { |
||||
|
padding: 40rpx 30rpx 20rpx 30rpx; |
||||
|
|
||||
|
.title { |
||||
|
color: #fff; |
||||
|
font-size: 36rpx; |
||||
|
font-weight: bold; |
||||
|
} |
||||
|
|
||||
|
.date { |
||||
|
color: #29d3b4; |
||||
|
font-size: 26rpx; |
||||
|
margin-top: 10rpx; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.section { |
||||
|
margin: 30rpx; |
||||
|
background: #434544; |
||||
|
border-radius: 16rpx; |
||||
|
padding: 30rpx 20rpx; |
||||
|
} |
||||
|
|
||||
|
.section-title { |
||||
|
color: #ffd86b; |
||||
|
font-size: 28rpx; |
||||
|
margin-bottom: 20rpx; |
||||
|
} |
||||
|
|
||||
|
.student-list { |
||||
|
display: flex; |
||||
|
flex-wrap: wrap; |
||||
|
gap: 20rpx; |
||||
|
} |
||||
|
|
||||
|
.student-item { |
||||
|
background: #333; |
||||
|
border-radius: 12rpx; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
padding: 18rpx 24rpx; |
||||
|
min-width: 260rpx; |
||||
|
|
||||
|
.avatar { |
||||
|
width: 60rpx; |
||||
|
height: 60rpx; |
||||
|
border-radius: 50%; |
||||
|
background: #29d3b4; |
||||
|
color: #fff; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
font-size: 32rpx; |
||||
|
margin-right: 18rpx; |
||||
|
} |
||||
|
|
||||
|
.info { |
||||
|
.name { |
||||
|
color: #fff; |
||||
|
font-size: 28rpx; |
||||
|
} |
||||
|
|
||||
|
.desc { |
||||
|
color: #bdbdbd; |
||||
|
font-size: 22rpx; |
||||
|
margin-top: 4rpx; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
&.empty { |
||||
|
border: 2rpx dashed #ffd86b; |
||||
|
background: #232323; |
||||
|
|
||||
|
.avatar.empty-avatar { |
||||
|
background: #ffd86b; |
||||
|
color: #232323; |
||||
|
font-size: 36rpx; |
||||
|
} |
||||
|
|
||||
|
.info .name { |
||||
|
color: #ffd86b; |
||||
|
} |
||||
|
|
||||
|
.info .desc { |
||||
|
color: #ffd86b; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 请假弹窗样式 |
||||
|
.leave-form { |
||||
|
padding: 20rpx; |
||||
|
|
||||
|
.leave-label { |
||||
|
font-size: 28rpx; |
||||
|
color: #333; |
||||
|
margin-bottom: 20rpx; |
||||
|
} |
||||
|
|
||||
|
.leave-input { |
||||
|
margin-bottom: 30rpx; |
||||
|
} |
||||
|
|
||||
|
.leave-buttons { |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
gap: 20rpx; |
||||
|
|
||||
|
.fui-button { |
||||
|
flex: 1; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/* 空数据占位区域样式 */ |
||||
|
.empty-placeholder { |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
padding: 60rpx 0; |
||||
|
|
||||
|
image { |
||||
|
width: 200rpx; |
||||
|
height: 200rpx; |
||||
|
margin-bottom: 20rpx; |
||||
|
} |
||||
|
|
||||
|
text { |
||||
|
color: #999; |
||||
|
font-size: 28rpx; |
||||
|
} |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,343 @@ |
|||||
|
<!--市场数据统计页面--> |
||||
|
<template> |
||||
|
<view class="main_box" :class="{'has-safe-area': hasSafeArea}"> |
||||
|
<!--自定义导航栏--> |
||||
|
<view class="navbar_section"> |
||||
|
<view class="title">市场数据统计</view> |
||||
|
</view> |
||||
|
|
||||
|
<!-- 顶部筛选 --> |
||||
|
<view class="filter-section"> |
||||
|
<view class="filter-item"> |
||||
|
<view class="label">统计时间:</view> |
||||
|
<picker mode="date" fields="month" :value="currentDate" @change="dateChange"> |
||||
|
<view class="picker-value">{{currentDate}}</view> |
||||
|
</picker> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<!-- 市场人员资源量统计 --> |
||||
|
<view class="table-section"> |
||||
|
<view class="table-title">市场人员资源量统计</view> |
||||
|
<view class="table-container"> |
||||
|
<view class="table-header"> |
||||
|
<view class="th th-person">人员</view> |
||||
|
<view class="th th-week">周数量</view> |
||||
|
<view class="th th-month">月数量</view> |
||||
|
<view class="th th-year">年数量</view> |
||||
|
</view> |
||||
|
<view class="table-body"> |
||||
|
<view class="table-row" v-for="(item, index) in staffData" :key="index"> |
||||
|
<view class="td td-person">{{item.name}}</view> |
||||
|
<view class="td td-week">{{item.weekCount}}</view> |
||||
|
<view class="td td-month">{{item.monthCount}}</view> |
||||
|
<view class="td td-year">{{item.yearCount}}</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<!-- 校区人员资源渠道量 --> |
||||
|
<view class="table-section"> |
||||
|
<view class="table-title">{{currentMonth}}月 - 年度校区人员资源渠道量</view> |
||||
|
<view class="table-container"> |
||||
|
<view class="table-header"> |
||||
|
<view class="th th-channel" style="width: 100px;">渠道</view> |
||||
|
<view class="th" v-for="(school, index) in schoolList" :key="index">{{school.campus_name}}</view> |
||||
|
<view class="th th-total">渠道合计</view> |
||||
|
</view> |
||||
|
<view class="table-body"> |
||||
|
<view class="table-row" v-for="(channel, index) in channelList" :key="index"> |
||||
|
<view class="td td-channel" style="width: 100px;">{{channel.name}}</view> |
||||
|
<view class="td" v-for="(school, sIndex) in schoolList" :key="sIndex"> |
||||
|
{{channel[school.id] || 0}} |
||||
|
</view> |
||||
|
<view class="td td-total">{{channel.total}}</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<!-- 市场人员资源渠道统计 --> |
||||
|
<view class="table-section"> |
||||
|
<view class="table-title">{{currentMonth}}月 - 年度市场人员资源渠道统计</view> |
||||
|
<view class="table-container"> |
||||
|
<view class="table-header"> |
||||
|
<view class="th th-channel" style="width: 100px;">渠道</view> |
||||
|
<view class="th" v-for="(staff, index) in staffData" :key="index">市场{{staff.name}}</view> |
||||
|
<view class="th th-total">资源合计</view> |
||||
|
</view> |
||||
|
<view class="table-body"> |
||||
|
<view class="table-row" v-for="(channel, index) in channelList" :key="index"> |
||||
|
<view class="td td-channel" style="width: 100px;">{{channel.name}}</view> |
||||
|
<view class="td" v-for="(staff, sIndex) in staffData" :key="sIndex"> |
||||
|
{{staff['channel'][channel.value] || 0}} |
||||
|
</view> |
||||
|
<view class="td td-total">{{channel.total}}</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
<AQTabber /> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import apiRoute from '@/api/apiRoute.js'; |
||||
|
import AQTabber from "@/components/AQ/AQTabber.vue" |
||||
|
|
||||
|
export default { |
||||
|
components: { |
||||
|
AQTabber, |
||||
|
}, |
||||
|
data() { |
||||
|
return { |
||||
|
currentDate: this.formatDate(new Date()), |
||||
|
currentMonth: new Date().getMonth() + 1, |
||||
|
// 市场人员资源量统计 |
||||
|
staffData: [], |
||||
|
// 校区列表 |
||||
|
schoolList: [], |
||||
|
// 是否有安全区域 |
||||
|
hasSafeArea: false, |
||||
|
// 渠道列表 |
||||
|
channelList: [], |
||||
|
// 市场人员列表 |
||||
|
marketStaffList: [ |
||||
|
{ id: 'A', name: 'A' }, |
||||
|
{ id: 'B', name: 'B' }, |
||||
|
{ id: 'C', name: 'C' }, |
||||
|
{ id: 'D', name: 'D' } |
||||
|
], |
||||
|
// 校区渠道数据 |
||||
|
channelSchoolData: [], |
||||
|
// 人员渠道数据 |
||||
|
channelStaffData: [] |
||||
|
} |
||||
|
}, |
||||
|
onLoad() { |
||||
|
this.initData(); |
||||
|
this.checkSafeArea(); |
||||
|
}, |
||||
|
methods: { |
||||
|
// 格式化日期为 YYYY-MM |
||||
|
formatDate(date) { |
||||
|
const year = date.getFullYear(); |
||||
|
const month = (date.getMonth() + 1).toString().padStart(2, '0'); |
||||
|
return `${year}-${month}`; |
||||
|
}, |
||||
|
|
||||
|
// 日期选择器变更 |
||||
|
dateChange(e) { |
||||
|
this.currentDate = e.detail.value; |
||||
|
const [year, month] = this.currentDate.split('-'); |
||||
|
this.currentMonth = parseInt(month); |
||||
|
this.initData(); |
||||
|
}, |
||||
|
|
||||
|
// 初始化数据 |
||||
|
async initData() { |
||||
|
// 模拟获取数据,实际项目中应该调用API |
||||
|
await this.getStaffStatistics(); |
||||
|
// await this.getChannelSchoolStatistics(); |
||||
|
// this.getChannelStaffStatistics(); |
||||
|
}, |
||||
|
|
||||
|
// 获取人员资源量统计 |
||||
|
async getStaffStatistics() { |
||||
|
// 模拟数据,实际项目中应该调用API |
||||
|
let res = await apiRoute.getStaffStatistics({date: this.currentDate}) |
||||
|
|
||||
|
console.log(res.data.staffData,"================"); |
||||
|
this.staffData = res.data.staffData |
||||
|
this.schoolList = res.data.schoolList |
||||
|
this.channelList = res.data.channelList |
||||
|
|
||||
|
}, |
||||
|
|
||||
|
// 获取校区渠道统计 |
||||
|
getChannelSchoolStatistics() { |
||||
|
// 模拟数据,实际项目中应该调用API |
||||
|
this.channelSchoolData = this.channelList.map(channel => { |
||||
|
const schools = {}; |
||||
|
let total = 0; |
||||
|
|
||||
|
this.schoolList.forEach(school => { |
||||
|
const count = Math.floor(Math.random() * 50); |
||||
|
schools[school.id] = count; |
||||
|
total += count; |
||||
|
}); |
||||
|
|
||||
|
return { |
||||
|
id: channel.id, |
||||
|
name: channel.name, |
||||
|
schools, |
||||
|
total |
||||
|
} |
||||
|
}); |
||||
|
}, |
||||
|
|
||||
|
// 获取人员渠道统计 |
||||
|
getChannelStaffStatistics() { |
||||
|
// 模拟数据,实际项目中应该调用API |
||||
|
this.channelStaffData = this.channelList.map(channel => { |
||||
|
const staffs = {}; |
||||
|
let total = 0; |
||||
|
|
||||
|
this.marketStaffList.forEach(staff => { |
||||
|
const count = Math.floor(Math.random() * 40); |
||||
|
staffs[staff.id] = count; |
||||
|
total += count; |
||||
|
}); |
||||
|
|
||||
|
return { |
||||
|
id: channel.id, |
||||
|
name: channel.name, |
||||
|
staffs, |
||||
|
total |
||||
|
} |
||||
|
}); |
||||
|
}, |
||||
|
|
||||
|
// 检测设备是否有安全区域 |
||||
|
checkSafeArea() { |
||||
|
try { |
||||
|
const systemInfo = uni.getSystemInfoSync(); |
||||
|
// 检测是否为iPhone X及以上机型或其他有安全区域的设备 |
||||
|
if (systemInfo.safeAreaInsets && systemInfo.safeAreaInsets.bottom > 0) { |
||||
|
this.hasSafeArea = true; |
||||
|
} |
||||
|
} catch (e) { |
||||
|
console.error('获取系统信息失败', e); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss" scoped> |
||||
|
.main_box { |
||||
|
min-height: 100vh; |
||||
|
background-color: #1a1a1a; |
||||
|
color: #ffffff; |
||||
|
padding-bottom: 150rpx; /* 增加底部内边距,防止内容被底部导航栏遮挡 */ |
||||
|
} |
||||
|
|
||||
|
/* 适配有安全区域的设备(如iPhone X及以上机型) */ |
||||
|
.has-safe-area { |
||||
|
padding-bottom: calc(150rpx + constant(safe-area-inset-bottom)); /* iOS 11.0-11.2 */ |
||||
|
padding-bottom: calc(150rpx + env(safe-area-inset-bottom)); /* iOS 11.2+ */ |
||||
|
} |
||||
|
|
||||
|
.navbar_section { |
||||
|
background-color: #1a1a1a; |
||||
|
height: 88rpx; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
position: relative; |
||||
|
|
||||
|
.title { |
||||
|
font-size: 36rpx; |
||||
|
font-weight: bold; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.filter-section { |
||||
|
padding: 20rpx; |
||||
|
background-color: #222222; |
||||
|
margin: 20rpx; |
||||
|
border-radius: 10rpx; |
||||
|
|
||||
|
.filter-item { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
|
||||
|
.label { |
||||
|
font-size: 28rpx; |
||||
|
margin-right: 10rpx; |
||||
|
} |
||||
|
|
||||
|
.picker-value { |
||||
|
padding: 10rpx 20rpx; |
||||
|
background-color: #333333; |
||||
|
border-radius: 6rpx; |
||||
|
font-size: 28rpx; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.table-section { |
||||
|
margin: 30rpx 20rpx; |
||||
|
background-color: #222222; |
||||
|
border-radius: 10rpx; |
||||
|
overflow: hidden; |
||||
|
|
||||
|
.table-title { |
||||
|
font-size: 32rpx; |
||||
|
font-weight: bold; |
||||
|
padding: 20rpx; |
||||
|
border-bottom: 1px solid #333333; |
||||
|
} |
||||
|
|
||||
|
.table-container { |
||||
|
width: 100%; |
||||
|
overflow-x: auto; |
||||
|
} |
||||
|
|
||||
|
.table-header { |
||||
|
display: flex; |
||||
|
background-color: #333333; |
||||
|
|
||||
|
.th { |
||||
|
padding: 15rpx 10rpx; |
||||
|
font-size: 26rpx; |
||||
|
text-align: center; |
||||
|
flex: 1; |
||||
|
min-width: 100rpx; |
||||
|
font-weight: bold; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.table-body { |
||||
|
.table-row { |
||||
|
display: flex; |
||||
|
border-bottom: 1px solid #333333; |
||||
|
|
||||
|
&:last-child { |
||||
|
border-bottom: none; |
||||
|
} |
||||
|
|
||||
|
.td { |
||||
|
padding: 15rpx 10rpx; |
||||
|
font-size: 26rpx; |
||||
|
text-align: center; |
||||
|
flex: 1; |
||||
|
min-width: 100rpx; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.th-person, .td-person { |
||||
|
min-width: 80rpx; |
||||
|
flex: 0.8; |
||||
|
} |
||||
|
|
||||
|
.th-week, .td-week, |
||||
|
.th-month, .td-month, |
||||
|
.th-year, .td-year { |
||||
|
flex: 1; |
||||
|
} |
||||
|
|
||||
|
.th-channel, .td-channel { |
||||
|
flex: 1.2; |
||||
|
text-align: left; |
||||
|
padding-left: 20rpx; |
||||
|
} |
||||
|
|
||||
|
.th-total, .td-total { |
||||
|
flex: 1.2; |
||||
|
font-weight: bold; |
||||
|
} |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,390 @@ |
|||||
|
<template> |
||||
|
<view class="assemble"> |
||||
|
<view style="height: 20rpx;"></view> |
||||
|
<!-- 市场人员展示--> |
||||
|
<view class="div-style"> |
||||
|
<view style="height: 38vh;"> |
||||
|
<view style="display: flex;align-items: center;padding: 20rpx 0 0 20rpx;"> |
||||
|
<view> |
||||
|
<image :src="$util.img('/uniapp_src/static/images/index/danlan.png')" class="drop-image"> |
||||
|
</image> |
||||
|
</view> |
||||
|
<view class="title">本月业绩</view> |
||||
|
</view> |
||||
|
<view class="coach-message"> |
||||
|
<view class="left1"> |
||||
|
<view style="padding: 20rpx 0;"> |
||||
|
<view style="display: flex;align-items: center;"> |
||||
|
<view style="padding: 12rpx;"> |
||||
|
<image :src="$util.img('/uniapp_src/static/images/index/huang.png')" |
||||
|
class="drop-image-x"></image> |
||||
|
</view> |
||||
|
<view class="title-x">资源总数</view> |
||||
|
</view> |
||||
|
<view class="title-x1">{{infoData.month.new_total}}人</view> |
||||
|
</view> |
||||
|
|
||||
|
<view> |
||||
|
<view style="display: flex;align-items: center;"> |
||||
|
<view style="padding: 12rpx;"> |
||||
|
<image :src="$util.img('/uniapp_src/static/images/index/lvs.png')" |
||||
|
class="drop-image-x"></image> |
||||
|
</view> |
||||
|
<view class="title-x">已分配</view> |
||||
|
</view> |
||||
|
<view class="title-x1">{{infoData.month.new_total}}人</view> |
||||
|
</view> |
||||
|
|
||||
|
<view> |
||||
|
<view style="display: flex;align-items: center;"> |
||||
|
<view style="padding: 12rpx;"> |
||||
|
<image :src="$util.img('/uniapp_src/static/images/index/shenlan.png')" |
||||
|
class="drop-image-x"></image> |
||||
|
</view> |
||||
|
<view class="title-x">昨日新增</view> |
||||
|
</view> |
||||
|
<view class="title-x1">{{infoData.month.yesterday_new}}人</view> |
||||
|
</view> |
||||
|
|
||||
|
<view> |
||||
|
<view style="display: flex;align-items: center;"> |
||||
|
<view style="padding: 12rpx;"> |
||||
|
<image :src="$util.img('/uniapp_src/static/images/index/lan.png')" |
||||
|
class="drop-image-x"></image> |
||||
|
</view> |
||||
|
<view class="title-x">今日新增</view> |
||||
|
</view> |
||||
|
<view class="title-x1">{{infoData.month.today_new}}人</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
<!-- 统计图--> |
||||
|
<view class="right1"> |
||||
|
<view style="text-align: center;">{{infoData.date_range}}</view> |
||||
|
<view class="statistics_box"> |
||||
|
|
||||
|
<view class="item"> |
||||
|
<view class="box"> |
||||
|
<view class="progress-bar" |
||||
|
:style="{ height: `${infoData.month.yesterday_new_rate}%`, background: '#f59a23' }"> |
||||
|
</view> |
||||
|
<view class="ratio" |
||||
|
:style="{ color: infoData.month.yesterday_new_rate <= 0 ? '#333333' : '#000' }"> |
||||
|
{{ infoData.month.yesterday_new_rate }}% |
||||
|
</view> |
||||
|
</view> |
||||
|
<view class="title">昨日</view> |
||||
|
</view> |
||||
|
|
||||
|
<view class="item"> |
||||
|
<view class="box"> |
||||
|
<view class="progress-bar" |
||||
|
:style="{ height: `${infoData.month.assigned_sales_rate}%`, background: '#039f64' }"> |
||||
|
</view> |
||||
|
<view class="ratio" |
||||
|
:style="{ color: infoData.month.assigned_sales_rate <= 0 ? '#333333' : '#000' }"> |
||||
|
{{ infoData.month.assigned_sales_rate }}% |
||||
|
</view> |
||||
|
</view> |
||||
|
<view class="title">分配</view> |
||||
|
</view> |
||||
|
|
||||
|
<view class="item"> |
||||
|
<view class="box"> |
||||
|
<view class="progress-bar" |
||||
|
:style="{ height: `${infoData.month.today_new_rate}%`, background: '#4066f2' }"> |
||||
|
</view> |
||||
|
<view class="ratio" |
||||
|
:style="{ color: infoData.month.today_new_rate <= 0 ? '#333333' : '#000' }"> |
||||
|
{{ infoData.month.today_new_rate }}% |
||||
|
</view> |
||||
|
</view> |
||||
|
<view class="title">今日</view> |
||||
|
</view> |
||||
|
|
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<view style="width: 90%;background: #EFF3F8;height: 4rpx;margin: auto;"></view> |
||||
|
|
||||
|
<view style="height: 38vh;"> |
||||
|
<view style="display: flex;align-items: center;padding: 20rpx 0 0 20rpx;"> |
||||
|
<view> |
||||
|
<image :src="$util.img('/uniapp_src/static/images/index/danlv.png')" class="drop-image"></image> |
||||
|
</view> |
||||
|
<view class="title">个人业绩</view> |
||||
|
</view> |
||||
|
|
||||
|
<view class="coach-message"> |
||||
|
<view class="this_month"> |
||||
|
<view style="padding: 20rpx 0;display: flex;justify-content: space-between;"> |
||||
|
<view style="width: 48%;"> |
||||
|
<view style="display: flex;align-items: center;"> |
||||
|
<view style="padding: 12rpx;"> |
||||
|
<image :src="$util.img('/uniapp_src/static/images/index/danlv.png')" |
||||
|
class="drop-image-x"></image> |
||||
|
</view> |
||||
|
<view class="title-x">今日新增资源</view> |
||||
|
</view> |
||||
|
<view class="title-x1">{{infoData.last_month.xzzy}}人</view> |
||||
|
</view> |
||||
|
|
||||
|
<view style="width: 48%;"> |
||||
|
<view style="display: flex;align-items: center;"> |
||||
|
<view style="padding: 12rpx;"> |
||||
|
<image :src="$util.img('/uniapp_src/static/images/index/danlv.png')" |
||||
|
class="drop-image-x"></image> |
||||
|
</view> |
||||
|
<view class="title-x">今日业绩收入</view> |
||||
|
</view> |
||||
|
<view class="title-x1">{{infoData.last_month.yjsr}}元</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<view style="padding: 20rpx 0;display: flex;justify-content: space-between;"> |
||||
|
<view style="width: 48%;"> |
||||
|
<view style="display: flex;align-items: center;"> |
||||
|
<view style="padding: 12rpx;"> |
||||
|
<image :src="$util.img('/uniapp_src/static/images/index/danlv.png')" |
||||
|
class="drop-image-x"></image> |
||||
|
</view> |
||||
|
<view class="title-x">历史关单数量</view> |
||||
|
</view> |
||||
|
<view class="title-x1">{{infoData.last_month.gdsl}}人</view> |
||||
|
</view> |
||||
|
|
||||
|
<view style="width: 48%;"> |
||||
|
<view style="display: flex;align-items: center;"> |
||||
|
<view style="padding: 12rpx;"> |
||||
|
<image :src="$util.img('/uniapp_src/static/images/index/danlv.png')" |
||||
|
class="drop-image-x"></image> |
||||
|
</view> |
||||
|
<view class="title-x">其他奖励</view> |
||||
|
</view> |
||||
|
<view class="title-x1">{{infoData.last_month.qtjl}}元</view> |
||||
|
</view> |
||||
|
|
||||
|
|
||||
|
</view> |
||||
|
<view style="padding: 20rpx 0;display: flex;justify-content: space-between;"> |
||||
|
<view style="width: 48%;"> |
||||
|
<view style="display: flex;align-items: center;"> |
||||
|
<view style="padding: 12rpx;"> |
||||
|
<image :src="$util.img('/uniapp_src/static/images/index/danlv.png')" |
||||
|
class="drop-image-x"></image> |
||||
|
</view> |
||||
|
<view class="title-x">本月提成</view> |
||||
|
</view> |
||||
|
<view class="title-x1">{{infoData.last_month.bytc}}元</view> |
||||
|
</view> |
||||
|
|
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
<AQTabber /> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import apiRoute from '@/api/apiRoute.js'; |
||||
|
|
||||
|
import AQTabber from "@/components/AQ/AQTabber.vue" |
||||
|
|
||||
|
export default { |
||||
|
components: { |
||||
|
AQTabber, |
||||
|
}, |
||||
|
data() { |
||||
|
return { |
||||
|
infoData: {}, //详情 |
||||
|
|
||||
|
userInfo: {}, //当前登录的用户信息 |
||||
|
} |
||||
|
}, |
||||
|
onShow() { |
||||
|
this.init() |
||||
|
}, |
||||
|
methods: { |
||||
|
async init() { |
||||
|
await this.getUserInfo() |
||||
|
await this.getXsIndex() |
||||
|
}, |
||||
|
|
||||
|
//获取用户信息 |
||||
|
async getUserInfo() { |
||||
|
let res = await apiRoute.getPersonnelInfo({}) |
||||
|
if (res.code != 1) { |
||||
|
uni.showToast({ |
||||
|
title: res.msg, |
||||
|
icon: 'none' |
||||
|
}) |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
this.userInfo = res.data |
||||
|
}, |
||||
|
|
||||
|
//获取销售首页详情 |
||||
|
async getXsIndex() { |
||||
|
let role_key_arr = this.userInfo.role_key_arr.join(',') |
||||
|
let params = { |
||||
|
personnel_id: this.userInfo.id, //员工表id |
||||
|
role_key_arr: role_key_arr, // 角色key 转换数组为字符串,若为空则赋予空字符串 |
||||
|
} |
||||
|
let res = await apiRoute.xs_statisticsMarketHome(params) |
||||
|
if (res.code != 1) { |
||||
|
uni.showToast({ |
||||
|
title: res.msg, |
||||
|
icon: 'none' |
||||
|
}) |
||||
|
return |
||||
|
} |
||||
|
this.infoData = res.data |
||||
|
console.log('统计', this.infoData) |
||||
|
}, |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="less" scoped> |
||||
|
//自定义导航栏 |
||||
|
.navbar_section { |
||||
|
border: 1px solid #fff; |
||||
|
display: flex; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
background: #fff; |
||||
|
|
||||
|
.title { |
||||
|
padding: 40rpx 0rpx; |
||||
|
|
||||
|
/* 小程序端样式 */ |
||||
|
// #ifdef MP-WEIXIN |
||||
|
padding: 80rpx 0rpx; |
||||
|
// #endif |
||||
|
|
||||
|
font-size: 30rpx; |
||||
|
color: #858585; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.assemble { |
||||
|
width: 100%; |
||||
|
height: 100vh; |
||||
|
background: #292929; |
||||
|
} |
||||
|
|
||||
|
.div-style { |
||||
|
width: 92%; |
||||
|
height: 85vh; |
||||
|
background: #fff; |
||||
|
border-radius: 16rpx; |
||||
|
margin: auto; |
||||
|
} |
||||
|
|
||||
|
.coach-message { |
||||
|
width: 92%; |
||||
|
margin: 10rpx auto; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
padding-top: 20rpx; |
||||
|
} |
||||
|
|
||||
|
.drop-image { |
||||
|
width: 50rpx; |
||||
|
height: 50rpx; |
||||
|
} |
||||
|
|
||||
|
.title { |
||||
|
font-size: 30rpx; |
||||
|
color: #7F7F7F; |
||||
|
padding-left: 20rpx; |
||||
|
} |
||||
|
|
||||
|
.left1 { |
||||
|
width: 48%; |
||||
|
height: 95%; |
||||
|
margin: auto; |
||||
|
} |
||||
|
|
||||
|
.right1 { |
||||
|
width: 48%; |
||||
|
height: 95%; |
||||
|
margin: auto; |
||||
|
|
||||
|
.statistics_box { |
||||
|
margin: auto; |
||||
|
margin-top: 10rpx; |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
|
||||
|
.item { |
||||
|
width: 90rpx; |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
align-items: center; |
||||
|
|
||||
|
.box { |
||||
|
width: 100%; |
||||
|
height: 328rpx; |
||||
|
border: 1px solid #ddd; |
||||
|
border-radius: 6rpx; |
||||
|
background: #f5f5f5; |
||||
|
position: relative; |
||||
|
|
||||
|
.progress-bar { |
||||
|
width: 100%; |
||||
|
height: 0; |
||||
|
transition: height 0.3s ease; |
||||
|
position: absolute; |
||||
|
bottom: 0; |
||||
|
} |
||||
|
|
||||
|
.ratio { |
||||
|
width: 100%; |
||||
|
position: absolute; |
||||
|
bottom: -0rpx; |
||||
|
font-size: 26rpx; |
||||
|
text-align: center; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.title { |
||||
|
margin-top: 5rpx; |
||||
|
padding: 0; |
||||
|
font-size: 26rpx; |
||||
|
color: #999999; |
||||
|
; |
||||
|
text-align: center; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.this_month { |
||||
|
width: 100%; |
||||
|
height: 95%; |
||||
|
margin: auto; |
||||
|
} |
||||
|
|
||||
|
.drop-image-x { |
||||
|
width: 20rpx; |
||||
|
height: 20rpx; |
||||
|
} |
||||
|
|
||||
|
.title-x { |
||||
|
font-size: 28rpx; |
||||
|
color: #7F7F7F; |
||||
|
padding-left: 20rpx; |
||||
|
} |
||||
|
|
||||
|
.title-x1 { |
||||
|
font-size: 28rpx; |
||||
|
color: #333333; |
||||
|
padding-left: 60rpx; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,272 @@ |
|||||
|
<template> |
||||
|
<view class="reim-add-page"> |
||||
|
<view class="header-bar"> |
||||
|
<view class="title">{{ pageTitle }}</view> |
||||
|
</view> |
||||
|
<view class="form-box"> |
||||
|
<!-- 金额 --> |
||||
|
<view class="form-row"> |
||||
|
<view class="label">报销金额</view> |
||||
|
<input class="input-amount" type="number" v-model="form.amount" placeholder="0.00" |
||||
|
:disabled="disabled" /> |
||||
|
</view> |
||||
|
<!-- 描述 --> |
||||
|
<view class="form-row-top"> |
||||
|
<view class="label">报销描述</view> |
||||
|
<textarea class="textarea" v-model="form.description" placeholder="请输入报销事由" :disabled="disabled" /> |
||||
|
</view> |
||||
|
<!-- 附件 --> |
||||
|
<view class="form-row"> |
||||
|
<view class="label">发票/收据</view> |
||||
|
<view class="file-upload-wrapper"> |
||||
|
<view v-if="form.receipt_url" class="preview-box" @click="previewImage"> |
||||
|
<image v-if="isImage(form.receipt_url)" :src="form.receipt_url" class="preview-img" |
||||
|
mode="aspectFit" /> |
||||
|
<view v-else class="file-link">{{ getFileName(form.receipt_url) }}</view> |
||||
|
</view> |
||||
|
<button class="upload-btn" @click="chooseFile" :disabled="disabled"> |
||||
|
{{ form.receipt_url ? '重新选择' : '选择附件' }} |
||||
|
</button> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
<view class="save-btn-box" v-if="!disabled"> |
||||
|
<fui-button background="#434544" color="#24BA9F" borderColor="#24BA9F" @click="submit">提交</fui-button> |
||||
|
</view> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import apiRoute from '@/api/apiRoute.js'; |
||||
|
import { |
||||
|
Api_url |
||||
|
} from "@/common/config.js"; |
||||
|
export default { |
||||
|
data() { |
||||
|
return { |
||||
|
uploadUrl: `${Api_url}/uploadImage`, |
||||
|
pageTitle: '新增报销', |
||||
|
disabled: false, |
||||
|
form: { |
||||
|
id: null, |
||||
|
amount: '', |
||||
|
description: '', |
||||
|
receipt_url: '', |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
onLoad(options) { |
||||
|
if (options.id) { |
||||
|
this.form.id = options.id; |
||||
|
this.fetchDetail(options.id); |
||||
|
} |
||||
|
}, |
||||
|
methods: { |
||||
|
async fetchDetail(id) { |
||||
|
uni.showLoading({ |
||||
|
title: '加载中...' |
||||
|
}); |
||||
|
|
||||
|
let res = await apiRoute.reimbursement_info({id:id}) |
||||
|
let mockData = res.data |
||||
|
|
||||
|
if (mockData.status !== 'pending') { |
||||
|
this.disabled = true; |
||||
|
this.pageTitle = '查看报销'; |
||||
|
} else { |
||||
|
this.pageTitle = '编辑报销'; |
||||
|
} |
||||
|
|
||||
|
this.form.amount = mockData.amount; |
||||
|
this.form.description = mockData.description; |
||||
|
this.form.receipt_url = mockData.receipt_url; |
||||
|
uni.hideLoading(); |
||||
|
}, |
||||
|
chooseFile() { |
||||
|
|
||||
|
uni.chooseImage({ |
||||
|
count: 1, |
||||
|
success: (res) => { |
||||
|
const tempFilePath = res.tempFilePaths[0] |
||||
|
// 这里可以调用上传接口 |
||||
|
this.uploadFilePromise(tempFilePath) |
||||
|
} |
||||
|
}) |
||||
|
}, |
||||
|
uploadFilePromise(url) { |
||||
|
let token = uni.getStorageSync('token') || '' |
||||
|
let a = uni.uploadFile({ |
||||
|
url: this.uploadUrl, //仅为示例,非真实的接口地址 |
||||
|
filePath: url, |
||||
|
name: 'file', |
||||
|
header: { |
||||
|
'token': `${token}`, //请求头设置token |
||||
|
}, |
||||
|
success: (e) => { |
||||
|
let res = JSON.parse(e.data.replace(/\ufeff/g, "") || "{}") |
||||
|
|
||||
|
if (res.code == 1) { |
||||
|
this.form.receipt_url = res.data.url |
||||
|
} else { |
||||
|
uni.showToast({ |
||||
|
title: res.msg, |
||||
|
icon: 'none' |
||||
|
}) |
||||
|
} |
||||
|
}, |
||||
|
}); |
||||
|
}, |
||||
|
isImage(url) { |
||||
|
if (!url) return false; |
||||
|
return /\.(jpg|jpeg|png|gif|bmp)$/i.test(url); |
||||
|
}, |
||||
|
getFileName(path) { |
||||
|
if (!path) return ''; |
||||
|
return path.split('/').pop(); |
||||
|
}, |
||||
|
previewImage() { |
||||
|
if (this.form.receipt_url && this.isImage(this.form.receipt_url)) { |
||||
|
uni.previewImage({ |
||||
|
urls: [this.form.receipt_url] |
||||
|
}); |
||||
|
} |
||||
|
}, |
||||
|
submit() { |
||||
|
if (!this.form.amount || !this.form.description) { |
||||
|
uni.showToast({ |
||||
|
title: '请填写完整', |
||||
|
icon: 'none' |
||||
|
}); |
||||
|
return; |
||||
|
} |
||||
|
let res = apiRoute.reimbursement_add(this.form) |
||||
|
|
||||
|
if (res['code'] == 1) { |
||||
|
uni.showToast({ |
||||
|
title: '提交成功', |
||||
|
icon: 'success' |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
setTimeout(() => { |
||||
|
uni.navigateBack(); |
||||
|
}, 1000); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="less" scoped> |
||||
|
.reim-add-page { |
||||
|
min-height: 100vh; |
||||
|
background: #292929; |
||||
|
padding-bottom: 120rpx; |
||||
|
} |
||||
|
|
||||
|
.header-bar { |
||||
|
padding: 32rpx; |
||||
|
|
||||
|
.title { |
||||
|
font-size: 44rpx; |
||||
|
color: #fff; |
||||
|
font-weight: bold; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.form-box { |
||||
|
margin: 0 32rpx; |
||||
|
background: #434544; |
||||
|
border-radius: 18rpx; |
||||
|
padding: 0 24rpx; |
||||
|
} |
||||
|
|
||||
|
.form-row, |
||||
|
.form-row-top { |
||||
|
display: flex; |
||||
|
padding: 32rpx 0; |
||||
|
border-bottom: 1rpx solid #3a3a3a; |
||||
|
|
||||
|
&:last-of-type { |
||||
|
border-bottom: none; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.form-row { |
||||
|
align-items: center; |
||||
|
} |
||||
|
|
||||
|
.form-row-top { |
||||
|
align-items: flex-start; |
||||
|
} |
||||
|
|
||||
|
.label { |
||||
|
color: #aaa; |
||||
|
font-size: 28rpx; |
||||
|
width: 180rpx; |
||||
|
flex-shrink: 0; |
||||
|
} |
||||
|
|
||||
|
.input-amount { |
||||
|
flex: 1; |
||||
|
color: #fff; |
||||
|
font-size: 28rpx; |
||||
|
text-align: right; |
||||
|
} |
||||
|
|
||||
|
.textarea { |
||||
|
flex: 1; |
||||
|
height: 180rpx; |
||||
|
color: #fff; |
||||
|
font-size: 28rpx; |
||||
|
background-color: transparent; |
||||
|
padding: 0; |
||||
|
width: 100%; |
||||
|
} |
||||
|
|
||||
|
.file-upload-wrapper { |
||||
|
flex: 1; |
||||
|
display: flex; |
||||
|
justify-content: flex-end; |
||||
|
align-items: center; |
||||
|
gap: 20rpx; |
||||
|
} |
||||
|
|
||||
|
.upload-btn { |
||||
|
background: #24BA9F; |
||||
|
color: #fff; |
||||
|
border: none; |
||||
|
border-radius: 8rpx; |
||||
|
padding: 0 32rpx; |
||||
|
line-height: 64rpx; |
||||
|
height: 64rpx; |
||||
|
font-size: 26rpx; |
||||
|
margin: 0; |
||||
|
} |
||||
|
|
||||
|
.preview-box { |
||||
|
.preview-img { |
||||
|
width: 120rpx; |
||||
|
height: 120rpx; |
||||
|
border-radius: 8rpx; |
||||
|
background: #222; |
||||
|
border: 1rpx solid #333; |
||||
|
} |
||||
|
|
||||
|
.file-link { |
||||
|
color: #24BA9F; |
||||
|
font-size: 26rpx; |
||||
|
word-break: break-all; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.save-btn-box { |
||||
|
position: fixed; |
||||
|
left: 0; |
||||
|
right: 0; |
||||
|
bottom: 0; |
||||
|
z-index: 100; |
||||
|
background: #292929; |
||||
|
padding: 20rpx 40rpx 40rpx 40rpx; |
||||
|
box-shadow: 0 -2rpx 8rpx rgba(0, 0, 0, 0.12); |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,136 @@ |
|||||
|
<template> |
||||
|
<view class="reim-detail-page"> |
||||
|
<view class="header-bar"> |
||||
|
<view class="title">报销详情</view> |
||||
|
</view> |
||||
|
<view class="detail-box"> |
||||
|
<view class="row"> |
||||
|
<view class="label">报销金额</view> |
||||
|
<view class="value">¥{{ detail.amount }}</view> |
||||
|
</view> |
||||
|
<view class="row"> |
||||
|
<view class="label">报销描述</view> |
||||
|
<view class="value">{{ detail.description }}</view> |
||||
|
</view> |
||||
|
<view class="row"> |
||||
|
<view class="label">发票/收据</view> |
||||
|
<view class="value"> |
||||
|
<image v-if="detail.receipt_url && isImage(detail.receipt_url)" :src="detail.receipt_url" class="receipt-img" mode="aspectFit" /> |
||||
|
<view v-else-if="detail.receipt_url" class="file-link">{{ detail.receipt_url }}</view> |
||||
|
<text v-else>无附件</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
<view class="row"> |
||||
|
<view class="label">状态</view> |
||||
|
<view :class="['value', 'status-' + detail.status]">{{ statusMap[detail.status] || detail.status }}</view> |
||||
|
</view> |
||||
|
<view class="row"> |
||||
|
<view class="label">创建时间</view> |
||||
|
<view class="value">{{ detail.created_at }}</view> |
||||
|
</view> |
||||
|
<view class="row"> |
||||
|
<view class="label">修改时间</view> |
||||
|
<view class="value">{{ detail.updated_at }}</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
export default { |
||||
|
data() { |
||||
|
return { |
||||
|
detail: {}, |
||||
|
statusMap: { |
||||
|
pending: '待审批', |
||||
|
approved: '已批准', |
||||
|
rejected: '已拒绝', |
||||
|
}, |
||||
|
} |
||||
|
}, |
||||
|
onLoad(options) { |
||||
|
if (options.id) { |
||||
|
this.fetchDetail(options.id); |
||||
|
} else { |
||||
|
uni.showToast({ title: '缺少报销ID', icon: 'none' }); |
||||
|
uni.navigateBack(); |
||||
|
} |
||||
|
}, |
||||
|
methods: { |
||||
|
fetchDetail(id) { |
||||
|
// 模拟接口获取详情 |
||||
|
uni.showLoading({ title: '加载中...' }); |
||||
|
setTimeout(() => { |
||||
|
const mockData = { |
||||
|
id: id, |
||||
|
amount: 300.00, |
||||
|
description: '办公用品采购(已批准)', |
||||
|
receipt_url: 'https://cdn.uviewui.com/uview/swiper/1.jpg', |
||||
|
status: 'approved', |
||||
|
created_at: '2024-06-02 09:30', |
||||
|
updated_at: '2024-06-03 12:00', |
||||
|
}; |
||||
|
this.detail = mockData; |
||||
|
uni.hideLoading(); |
||||
|
}, 500); |
||||
|
}, |
||||
|
isImage(url) { |
||||
|
if (!url) return false; |
||||
|
return /\.(jpg|jpeg|png|gif|bmp)$/i.test(url) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="less" scoped> |
||||
|
.reim-detail-page { |
||||
|
min-height: 100vh; |
||||
|
background: #292929; |
||||
|
} |
||||
|
.header-bar { |
||||
|
padding: 32rpx 32rpx 0 32rpx; |
||||
|
.title { |
||||
|
font-size: 36rpx; |
||||
|
color: #24BA9F; |
||||
|
font-weight: bold; |
||||
|
} |
||||
|
} |
||||
|
.detail-box { |
||||
|
margin: 32rpx; |
||||
|
background: #434544; |
||||
|
border-radius: 18rpx; |
||||
|
padding: 32rpx 24rpx 24rpx 24rpx; |
||||
|
} |
||||
|
.row { |
||||
|
display: flex; |
||||
|
align-items: flex-start; |
||||
|
margin-bottom: 32rpx; |
||||
|
.label { |
||||
|
color: #aaa; |
||||
|
font-size: 28rpx; |
||||
|
width: 180rpx; |
||||
|
flex-shrink: 0; |
||||
|
margin-top: 10rpx; |
||||
|
} |
||||
|
.value { |
||||
|
color: #fff; |
||||
|
font-size: 28rpx; |
||||
|
word-break: break-all; |
||||
|
} |
||||
|
.status-pending { color: #f0ad4e; } |
||||
|
.status-approved { color: #24BA9F; } |
||||
|
.status-rejected { color: #e74c3c; } |
||||
|
.receipt-img { |
||||
|
width: 160rpx; |
||||
|
height: 160rpx; |
||||
|
border-radius: 8rpx; |
||||
|
background: #222; |
||||
|
border: 1rpx solid #333; |
||||
|
} |
||||
|
.file-link { |
||||
|
color: #24BA9F; |
||||
|
font-size: 26rpx; |
||||
|
word-break: break-all; |
||||
|
} |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,144 @@ |
|||||
|
<template> |
||||
|
<view class="reim-list-page"> |
||||
|
<view class="header-bar"> |
||||
|
<view class="title">报销列表</view> |
||||
|
<fui-button |
||||
|
background="transparent" |
||||
|
color="#24BA9F" |
||||
|
borderColor="#24BA9F" |
||||
|
width="180rpx" |
||||
|
height="64rpx" |
||||
|
radius="32rpx" |
||||
|
@click="goAdd"> |
||||
|
新增报销 |
||||
|
</fui-button> |
||||
|
</view> |
||||
|
<view v-if="list.length === 0" class="empty-tip">暂无报销记录</view> |
||||
|
<view v-for="item in list" :key="item.id" class="reim-card" @click="goDetail(item)"> |
||||
|
<view class="row"> |
||||
|
<view class="label">金额:</view> |
||||
|
<view class="value">¥{{ item.amount }}</view> |
||||
|
</view> |
||||
|
<view class="row"> |
||||
|
<view class="label">描述:</view> |
||||
|
<view class="value">{{ item.description }}</view> |
||||
|
</view> |
||||
|
<view class="row"> |
||||
|
<view class="label">发票/收据:</view> |
||||
|
<view class="value"> |
||||
|
<image v-if="item.receipt_url" :src="item.receipt_url" class="receipt-img" mode="aspectFit" /> |
||||
|
<text v-else>无附件</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
<view class="row"> |
||||
|
<view class="label">状态:</view> |
||||
|
<view :class="['value', 'status-' + item.status]">{{ statusMap[item.status] || item.status }}</view> |
||||
|
</view> |
||||
|
<view class="row"> |
||||
|
<view class="label">创建时间:</view> |
||||
|
<view class="value">{{ item.created_at }}</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import apiRoute from '@/api/apiRoute.js'; |
||||
|
export default { |
||||
|
data() { |
||||
|
return { |
||||
|
list: [], |
||||
|
statusMap: { |
||||
|
pending: '待审批', |
||||
|
approved: '已批准', |
||||
|
rejected: '已拒绝', |
||||
|
}, |
||||
|
} |
||||
|
}, |
||||
|
async onShow() { |
||||
|
await this.reimbursementList(); |
||||
|
}, |
||||
|
methods: { |
||||
|
async reimbursementList(){ |
||||
|
let res = await apiRoute.reimbursement_list({}) |
||||
|
this.list = res.data; |
||||
|
|
||||
|
}, |
||||
|
goAdd() { |
||||
|
uni.navigateTo({ |
||||
|
url: '/pages/market/reimbursement/add' |
||||
|
}); |
||||
|
}, |
||||
|
goDetail(item) { |
||||
|
// 待审批状态可编辑,否则只能查看 |
||||
|
if (item.status === 'pending') { |
||||
|
uni.navigateTo({ |
||||
|
url: `/pages/market/reimbursement/add?id=${item.id}` |
||||
|
}); |
||||
|
} else { |
||||
|
uni.navigateTo({ |
||||
|
url: `/pages/market/reimbursement/detail?id=${item.id}` |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="less" scoped> |
||||
|
.reim-list-page { |
||||
|
min-height: 100vh; |
||||
|
background: #292929; |
||||
|
padding-bottom: 120rpx; |
||||
|
} |
||||
|
.header-bar { |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
align-items: center; |
||||
|
padding: 32rpx 32rpx 0 32rpx; |
||||
|
.title { |
||||
|
font-size: 36rpx; |
||||
|
color: #fff; |
||||
|
font-weight: bold; |
||||
|
} |
||||
|
} |
||||
|
.empty-tip { |
||||
|
color: #888; |
||||
|
text-align: center; |
||||
|
margin: 80rpx 0; |
||||
|
} |
||||
|
.reim-card { |
||||
|
background: #434544; |
||||
|
border-radius: 18rpx; |
||||
|
margin: 32rpx; |
||||
|
margin-bottom: 0; |
||||
|
padding: 32rpx 24rpx 24rpx 24rpx; |
||||
|
box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.08); |
||||
|
.row { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
margin-bottom: 18rpx; |
||||
|
.label { |
||||
|
color: #aaa; |
||||
|
font-size: 26rpx; |
||||
|
width: 140rpx; |
||||
|
flex-shrink: 0; |
||||
|
} |
||||
|
.value { |
||||
|
color: #fff; |
||||
|
font-size: 28rpx; |
||||
|
word-break: break-all; |
||||
|
} |
||||
|
.status-pending { color: #f0ad4e; } |
||||
|
.status-approved { color: #24BA9F; } |
||||
|
.status-rejected { color: #e74c3c; } |
||||
|
.receipt-img { |
||||
|
width: 120rpx; |
||||
|
height: 120rpx; |
||||
|
border-radius: 8rpx; |
||||
|
background: #222; |
||||
|
border: 1rpx solid #333; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</style> |
||||
Loading…
Reference in new issue