110 changed files with 40426 additions and 704 deletions
@ -0,0 +1,755 @@ |
|||
<template> |
|||
<view class="main_box"> |
|||
<view class="main_section"> |
|||
<!--最近课程--> |
|||
<view class="section_3"> |
|||
<view class="title_box"> |
|||
<view class="top_box"> |
|||
<text>最近课程</text> |
|||
<view></view> |
|||
</view> |
|||
<view class="line"></view> |
|||
</view> |
|||
<view class="ul" v-if="infoData.course_list && infoData.course_list.length > 0"> |
|||
<!-- 上课中--> |
|||
<view class="li" v-for="(v,k) in infoData.course_list" :key="k" @click="openViewCourseInfoList(v)"> |
|||
<view class="top_box"> |
|||
<view class="title">课程:{{ v.course_name }}</view> |
|||
<view class="title">时间:{{ v.course_date }} {{ v.time_slot }} |
|||
</view> |
|||
<view class="title">地点:{{ v.address }}</view> |
|||
</view> |
|||
|
|||
<view class="botton_box" v-if="v.status == 'ongoing'"> |
|||
<view class="box"> |
|||
<view>已签到学生({{ v.sign_count }}/{{ v.students_count }})</view> |
|||
<view> |
|||
查看 |
|||
<fui-icon size="35" color="#fff" name="arrowright"></fui-icon> |
|||
</view> |
|||
</view> |
|||
<!-- <view class="box">--> |
|||
<!-- <view>尚未发布作业</view>--> |
|||
<!-- <view></view>--> |
|||
<!-- </view>--> |
|||
</view> |
|||
<view class="botton_box" v-else> |
|||
<view class="box"> |
|||
<view>应到学生({{ v.students_count }})</view> |
|||
<view> |
|||
查看 |
|||
<fui-icon size="35" color="#fff" name="arrowright"></fui-icon> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<view |
|||
v-if="v.status == 'pending'" |
|||
class="tag" |
|||
style="background:#fad24e;">待开始 |
|||
</view> |
|||
|
|||
<view |
|||
v-if="v.status == 'upcoming'" |
|||
class="tag" |
|||
style="background:#1cd188;">待上课 |
|||
</view> |
|||
|
|||
<view |
|||
v-if="v.status == 'ongoing'" |
|||
class="tag" |
|||
style="background:#fad24e;">上课中 |
|||
</view> |
|||
</view> |
|||
</view> |
|||
<!-- 没有数据时的占位区域 --> |
|||
<view class="empty-placeholder" v-else> |
|||
<image :src="$util.img('/static/icon-img/empty.png')" mode="aspectFit"></image> |
|||
<text>暂无课程数据</text> |
|||
</view> |
|||
</view> |
|||
|
|||
<!--作业批改--> |
|||
<view class="section_4"> |
|||
<view class="title_box"> |
|||
<view class="top_box"> |
|||
<text>作业批改</text> |
|||
<view @click="openObjListView()">全部</view> |
|||
</view> |
|||
<view class="line"></view> |
|||
</view> |
|||
<view class="ul" v-if="infoData.task_list && infoData.task_list.length > 0"> |
|||
<view class="li" v-for="(v,k) in infoData.task_list" :key="k" @click="openViewWorkDetails(v)"> |
|||
<!-- <view class="left_box"> |
|||
<view class="date_box"> |
|||
<text>{{v.wc_count}}</text> |
|||
<text>/</text> |
|||
<text>{{v.student_count}}</text> |
|||
</view> |
|||
<view class="ratio"> |
|||
完成率:{{v.rate}}% |
|||
</view> |
|||
</view> --> |
|||
<view class="center_box"> |
|||
<view>班级:{{v.class_name}}</view> |
|||
<view>时间:{{v.create_time}}</view> |
|||
<view>课程:{{v.course_name}} |
|||
</view> |
|||
</view> |
|||
<view class="right_box"> |
|||
<!-- <view class="tag" style="background:#fad24e;">上课中</view>--> |
|||
<view class="tag" style="background:#1cd188;">待批改</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
<!-- 没有数据时的占位区域 --> |
|||
<view class="empty-placeholder" v-else> |
|||
<image :src="$util.img('/static/icon-img/empty.png')" mode="aspectFit"></image> |
|||
<text>暂无作业数据</text> |
|||
</view> |
|||
</view> |
|||
|
|||
<!--我的服务列表--> |
|||
<view class="section_6"> |
|||
<view class="title_box"> |
|||
<view class="top_box"> |
|||
<text>我的服务</text> |
|||
<view @click="openServiceListView()">全部</view> |
|||
</view> |
|||
<view class="line"></view> |
|||
</view> |
|||
<view class="service-list" v-if="serviceList && serviceList.length > 0"> |
|||
<view class="service-item" v-for="(item, index) in serviceList" :key="index" @click="viewServiceDetail(item)"> |
|||
<view class="service-info"> |
|||
<view class="service-name">{{item.service_name}}</view> |
|||
<view class="service-desc">{{item.description}}</view> |
|||
<view class="service-time">创建时间:{{item.created_at}}</view> |
|||
</view> |
|||
<view class="service-status" :class="{'active': item.status === 1}"> |
|||
{{item.status === 1 ? '进行中' : '已结束'}} |
|||
</view> |
|||
</view> |
|||
</view> |
|||
<!-- 没有数据时的占位区域 --> |
|||
<view class="empty-placeholder" v-else> |
|||
<image :src="$util.img('/static/icon-img/empty.png')" mode="aspectFit"></image> |
|||
<text>暂无服务数据</text> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
|
|||
|
|||
<!-- 底部导航--> |
|||
<AQTabber/> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
import memberApi from '@/api/member.js'; |
|||
import AQTabber from "@/components/AQ/AQTabber.vue" |
|||
|
|||
|
|||
export default { |
|||
components: { |
|||
AQTabber, |
|||
}, |
|||
data() { |
|||
return { |
|||
//首页数据 |
|||
infoData:{ |
|||
course_list:[],//课程列表 |
|||
task_list:[],//作业批改 |
|||
}, |
|||
serviceList: [], // 我的服务列表 |
|||
} |
|||
}, |
|||
onLoad() { |
|||
this.init() // 页面加载时初始化 |
|||
}, |
|||
onShow() { |
|||
this.init() // 每次显示页面时初始化 |
|||
}, |
|||
methods: { |
|||
|
|||
//初始化 |
|||
async init(){ |
|||
await this.getInfo() |
|||
// await this.getServiceList() // 获取服务列表 |
|||
}, |
|||
|
|||
//获取首页信息 |
|||
async getInfo(){ |
|||
// 注释掉API调用,使用模拟数据 |
|||
let res = await memberApi.jlIndex({}) |
|||
if(res.code != 1){ |
|||
uni.showToast({ |
|||
title: res.msg, |
|||
icon: 'none' |
|||
}) |
|||
return |
|||
} |
|||
let course_list = res.data.course_list |
|||
let task_list = res.data.task_list |
|||
this.serviceList = res.data.service_list |
|||
|
|||
// 使用模拟数据 |
|||
this.infoData = { |
|||
course_list: course_list, |
|||
task_list: task_list |
|||
} |
|||
}, |
|||
|
|||
// 获取服务列表 |
|||
async getServiceList() { |
|||
// 注释掉API调用,使用模拟数据 |
|||
// try { |
|||
// // 这里替换为实际的API调用 |
|||
// let res = await memberApi.serviceList({}) |
|||
// if(res.code == 1){ |
|||
// this.serviceList = res.data || [] |
|||
// } else { |
|||
// uni.showToast({ |
|||
// title: res.msg || '获取服务列表失败', |
|||
// icon: 'none' |
|||
// }) |
|||
// } |
|||
// } catch (error) { |
|||
// console.error('获取服务列表失败:', error) |
|||
// uni.showToast({ |
|||
// title: '获取服务列表失败', |
|||
// icon: 'none' |
|||
// }) |
|||
// } |
|||
|
|||
// 使用模拟数据 |
|||
// this.serviceList = [ |
|||
// { |
|||
// id: 1, |
|||
// name: '一对一辅导服务', |
|||
// description: '针对学生个人情况定制的一对一辅导计划,帮助学生快速提高成绩。', |
|||
// create_time: '2025-07-15', |
|||
// status: 1 |
|||
// }, |
|||
// { |
|||
// id: 2, |
|||
// name: '小组课程服务', |
|||
// description: '3-5人小组课程,提供互动学习环境,培养学生团队协作能力。', |
|||
// create_time: '2025-07-20', |
|||
// status: 0 |
|||
// }, |
|||
// { |
|||
// id: 3, |
|||
// name: '课后辅导服务', |
|||
// description: '针对课后作业和复习提供专业辅导,巩固课堂所学知识。', |
|||
// create_time: '2025-07-01', |
|||
// status: 1 |
|||
// } |
|||
// ] |
|||
}, |
|||
|
|||
//打开-发布作业页 |
|||
openObjAddView(){ |
|||
uni.navigateTo({ |
|||
url: '/pages-coach/coach/job/add' |
|||
}) |
|||
}, |
|||
|
|||
//打开作业列表页 |
|||
openObjListView(){ |
|||
uni.navigateTo({ |
|||
url: '/pages-coach/coach/job/list' |
|||
}) |
|||
}, |
|||
|
|||
//跳转页面-课程详情 |
|||
openViewCourseInfoList(item){ |
|||
let id = item.id |
|||
uni.navigateTo({ |
|||
url: `/pages-coach/coach/course/info_list?id=${id}` |
|||
}) |
|||
}, |
|||
|
|||
//跳转页面-作业详情 |
|||
openViewWorkDetails(item){ |
|||
let id = item.id |
|||
uni.navigateTo({ |
|||
url: `/pages-coach/coach/student/work_details?id=${id}` |
|||
}) |
|||
}, |
|||
|
|||
// 打开服务列表页面 |
|||
openServiceListView() { |
|||
uni.navigateTo({ |
|||
url: '/pages-coach/coach/my/service_list' |
|||
}) |
|||
}, |
|||
|
|||
// 查看服务详情 |
|||
viewServiceDetail(item) { |
|||
let id = item.id |
|||
uni.navigateTo({ |
|||
url: `/pages-coach/coach/my/service_detail?id=${id}` |
|||
}) |
|||
}, |
|||
|
|||
// 跳转到我的学员列表页面 |
|||
goToStudentList() { |
|||
uni.navigateTo({ |
|||
url: '/pages-coach/coach/student/student_list' |
|||
}) |
|||
}, |
|||
|
|||
// 跳转到班级列表页面 |
|||
goToClassList() { |
|||
uni.navigateTo({ |
|||
url: '/pages-coach/coach/class/list' |
|||
}) |
|||
}, |
|||
|
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style lang="less" scoped> |
|||
|
|||
.main_box{ |
|||
background: #292929 ; |
|||
} |
|||
|
|||
//自定义导航栏 |
|||
.navbar_section{ |
|||
padding: 0 40rpx; |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
background: #29d3b4; |
|||
view{ |
|||
width: 30%; |
|||
text-align: center; |
|||
} |
|||
.left{ |
|||
text-align: left; |
|||
} |
|||
.title{ |
|||
padding: 40rpx 0rpx; |
|||
|
|||
/* 小程序端样式 */ |
|||
// #ifdef MP-WEIXIN |
|||
padding: 80rpx 0rpx; |
|||
// #endif |
|||
|
|||
font-size: 30rpx; |
|||
color: #fff; |
|||
} |
|||
.right{ |
|||
padding: 20rpx 0; |
|||
/* H5端样式 */ |
|||
// #ifdef H5 |
|||
padding: 40rpx 0 20rpx; |
|||
// #endif |
|||
|
|||
/* 小程序端样式 */ |
|||
// #ifdef MP-WEIXIN |
|||
padding: 80rpx 0 20rpx; |
|||
// #endif |
|||
|
|||
font-size: 26rpx; |
|||
color: #fff; |
|||
text-align: right; |
|||
} |
|||
} |
|||
|
|||
.main_section{ |
|||
min-height: 100vh; |
|||
background: #292929 100%; |
|||
padding: 0 24rpx; |
|||
padding-top: 40rpx; |
|||
padding-bottom: 150rpx; |
|||
font-size: 28rpx; |
|||
|
|||
/* 空数据占位区域样式 */ |
|||
.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; |
|||
} |
|||
} |
|||
|
|||
.section_1{ |
|||
background: #FFFFFF 100%; |
|||
height: 264rpx; |
|||
border-radius: 14rpx; |
|||
margin: 28rpx auto; |
|||
padding: 28rpx; |
|||
display: flex; |
|||
flex-direction: column; |
|||
justify-content: space-between; |
|||
font-size: 28rpx; |
|||
.top{ |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
.user{ |
|||
display: flex; |
|||
align-items: center; |
|||
gap: 20rpx; |
|||
.pic{ |
|||
width: 144rpx; |
|||
height: 144rpx; |
|||
border-radius: 50%; |
|||
image{ |
|||
width: 100%; |
|||
height: 100%; |
|||
} |
|||
} |
|||
} |
|||
.right{ |
|||
display: flex; |
|||
flex-direction: column; |
|||
gap: 20rpx; |
|||
view{ |
|||
display: flex; |
|||
gap: 10rpx; |
|||
.title{ |
|||
width: 148rpx; |
|||
text-align: right; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
.bottom{ |
|||
padding: 0 44rpx; |
|||
} |
|||
} |
|||
|
|||
.section_2{ |
|||
border-radius: 60rpx; |
|||
padding: 28rpx 36rpx; |
|||
background: #29D3B4 100%; |
|||
color: #fff; |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
.left_box{ |
|||
display: flex; |
|||
view{ |
|||
display: flex; |
|||
flex-direction: column; |
|||
text{} |
|||
} |
|||
view:nth-child(1){ |
|||
padding-right: 10rpx; |
|||
border-right: 2px solid #fff; |
|||
} |
|||
view:nth-child(2){ |
|||
padding-left: 10rpx; |
|||
} |
|||
} |
|||
.rigth_box{ |
|||
view{ |
|||
width: 106rpx; |
|||
height: 50rpx; |
|||
line-height: 50rpx; |
|||
text-align: center; |
|||
border-radius: 25rpx; |
|||
background: #32baa1; |
|||
font-size: 24rpx; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.section_3{ |
|||
margin-top: 36rpx; |
|||
color: #fff; |
|||
font-size: 24rpx; |
|||
.title_box{ |
|||
display: flex; |
|||
flex-direction: column; |
|||
.top_box{ |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
text{ |
|||
font-size: 30rpx; |
|||
} |
|||
} |
|||
.line{ |
|||
width: 90rpx; |
|||
height: 2px; |
|||
background: #29D3B4; |
|||
} |
|||
} |
|||
.ul{ |
|||
margin-top: 30rpx; |
|||
display: flex; |
|||
flex-direction: column; |
|||
gap: 20rpx; |
|||
.li{ |
|||
border: 1px solid #00E5BB; |
|||
position: relative; |
|||
border-radius: 22rpx; |
|||
background: #434544 100%; |
|||
padding: 14rpx 20rpx; |
|||
padding-bottom: 44rpx; |
|||
display: flex; |
|||
flex-direction: column; |
|||
.top_box{ |
|||
padding-bottom: 18rpx; |
|||
border-bottom: 1px dashed #F2F2F2; |
|||
display: flex; |
|||
flex-direction: column; |
|||
gap: 18rpx; |
|||
.title{} |
|||
} |
|||
.botton_box{ |
|||
padding-top: 18rpx; |
|||
display: flex; |
|||
flex-direction: column; |
|||
gap: 18rpx; |
|||
color: #D7D7D7; |
|||
.box{ |
|||
display: flex; |
|||
justify-content: space-between; |
|||
view{ |
|||
display: flex; |
|||
align-items: center; |
|||
} |
|||
} |
|||
} |
|||
.tag{ |
|||
position:absolute; |
|||
top: 0rpx; |
|||
right: 0rpx; |
|||
padding: 10rpx; |
|||
width: 102rpx; |
|||
text-align: center; |
|||
font-size: 24rpx; |
|||
border-bottom-left-radius: 20rpx; |
|||
border-top-right-radius: 20rpx; |
|||
} |
|||
} |
|||
|
|||
} |
|||
} |
|||
|
|||
.section_4{ |
|||
margin-top: 36rpx; |
|||
color: #fff; |
|||
font-size: 24rpx; |
|||
.title_box{ |
|||
display: flex; |
|||
flex-direction: column; |
|||
.top_box{ |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
text{ |
|||
font-size: 30rpx; |
|||
} |
|||
} |
|||
.line{ |
|||
width: 90rpx; |
|||
height: 2px; |
|||
background: #29D3B4; |
|||
} |
|||
} |
|||
.ul{ |
|||
margin-top: 30rpx; |
|||
display: flex; |
|||
flex-direction: column; |
|||
gap: 20rpx; |
|||
.li{ |
|||
position: relative; |
|||
border-radius: 22rpx; |
|||
background: #434544 100%; |
|||
padding: 32rpx 0; |
|||
display: flex; |
|||
align-items: center; |
|||
.left_box{ |
|||
margin-left: 28rpx; |
|||
width: 146rpx; |
|||
display: flex; |
|||
flex-direction: column; |
|||
gap: 10rpx; |
|||
.date_box{ |
|||
display: flex; |
|||
font-size: 48rpx; |
|||
text:nth-child(1){ |
|||
color: #29D3B4; |
|||
} |
|||
} |
|||
.ratio{ |
|||
color: #AAAAAA; |
|||
} |
|||
} |
|||
.center_box{ |
|||
margin-left: 52rpx; |
|||
display: flex; |
|||
flex-direction: column; |
|||
gap: 10rpx; |
|||
} |
|||
.right_box{ |
|||
.tag{ |
|||
position:absolute; |
|||
top: 0rpx; |
|||
right: 0rpx; |
|||
padding: 10rpx; |
|||
width: 102rpx; |
|||
text-align: center; |
|||
font-size: 24rpx; |
|||
border-bottom-left-radius: 20rpx; |
|||
border-top-right-radius: 20rpx; |
|||
} |
|||
|
|||
} |
|||
} |
|||
|
|||
} |
|||
} |
|||
|
|||
.section_5{ |
|||
margin-top: 36rpx; |
|||
color: #fff; |
|||
font-size: 24rpx; |
|||
.title_box{ |
|||
display: flex; |
|||
flex-direction: column; |
|||
.top_box{ |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
text{ |
|||
font-size: 30rpx; |
|||
} |
|||
} |
|||
.line{ |
|||
width: 90rpx; |
|||
height: 2px; |
|||
background: #29D3B4; |
|||
} |
|||
} |
|||
|
|||
.quick-links { |
|||
margin-top: 30rpx; |
|||
display: flex; |
|||
justify-content: space-around; |
|||
|
|||
.quick-link-item { |
|||
display: flex; |
|||
flex-direction: column; |
|||
align-items: center; |
|||
background: #434544; |
|||
border-radius: 20rpx; |
|||
padding: 30rpx 20rpx; |
|||
width: 30%; |
|||
|
|||
.quick-link-icon { |
|||
margin-bottom: 20rpx; |
|||
} |
|||
|
|||
.quick-link-text { |
|||
font-size: 28rpx; |
|||
color: #fff; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
.section_6{ |
|||
margin-top: 36rpx; |
|||
color: #fff; |
|||
font-size: 24rpx; |
|||
.title_box{ |
|||
display: flex; |
|||
flex-direction: column; |
|||
.top_box{ |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
text{ |
|||
font-size: 30rpx; |
|||
} |
|||
} |
|||
.line{ |
|||
width: 90rpx; |
|||
height: 2px; |
|||
background: #29D3B4; |
|||
} |
|||
} |
|||
.service-list{ |
|||
margin-top: 30rpx; |
|||
display: flex; |
|||
flex-direction: column; |
|||
gap: 20rpx; |
|||
.service-item{ |
|||
position: relative; |
|||
border-radius: 22rpx; |
|||
background: #434544 100%; |
|||
padding: 24rpx 20rpx; |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
border-left: 4rpx solid #29D3B4; |
|||
|
|||
.service-info{ |
|||
flex: 1; |
|||
display: flex; |
|||
flex-direction: column; |
|||
gap: 12rpx; |
|||
|
|||
.service-name{ |
|||
font-size: 30rpx; |
|||
color: #fff; |
|||
font-weight: 500; |
|||
} |
|||
|
|||
.service-desc{ |
|||
font-size: 24rpx; |
|||
color: #D7D7D7; |
|||
overflow: hidden; |
|||
text-overflow: ellipsis; |
|||
display: -webkit-box; |
|||
-webkit-line-clamp: 2; |
|||
-webkit-box-orient: vertical; |
|||
} |
|||
|
|||
.service-time{ |
|||
font-size: 22rpx; |
|||
color: #AAAAAA; |
|||
margin-top: 6rpx; |
|||
} |
|||
} |
|||
|
|||
.service-status{ |
|||
padding: 8rpx 20rpx; |
|||
border-radius: 30rpx; |
|||
background: #666; |
|||
color: #fff; |
|||
font-size: 24rpx; |
|||
text-align: center; |
|||
|
|||
&.active { |
|||
background: #29D3B4; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
} |
|||
|
|||
|
|||
</style> |
|||
@ -0,0 +1,227 @@ |
|||
<!--即将到期--> |
|||
<template> |
|||
<view class="main_box"> |
|||
|
|||
<view class="main_section"> |
|||
<!-- 班级成员列表--> |
|||
<view class="section_4"> |
|||
<view class="ul"> |
|||
<!-- @click="openViewStudentInfo({id:1})" --> |
|||
<view class="li" v-for="(item, index) in studentList" :key="index"> |
|||
<view class="left"> |
|||
<view class="box_1"> |
|||
<image :src="item.avatar || $util.img('/static/icon-img/avatar.png')" mode="aspectFill" class="pic"></image> |
|||
<view class="tag_box"> |
|||
即将到期 |
|||
</view> |
|||
</view> |
|||
<view class="box_2"> |
|||
<view class="name">{{item.name}}</view> |
|||
<view class="date">课程截止时间:{{item.end_date}}</view> |
|||
</view> |
|||
</view> |
|||
<view class="right"> |
|||
<view class="item"> |
|||
<view>{{ |
|||
(item.use_total_hours + item.use_gift_hours) |
|||
}}节</view> |
|||
<view>已上课时</view> |
|||
</view> |
|||
<view class="item"> |
|||
<view>{{ |
|||
(item.total_hours + item.gift_hours) - (item.use_total_hours + item.use_gift_hours) |
|||
}}节</view> |
|||
<view>剩余课时</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 底部导航--> |
|||
<!-- <AQTabber/>--> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
import memberApi from '@/api/member.js'; |
|||
import AQTabber from "@/components/AQ/AQTabber.vue" |
|||
|
|||
|
|||
export default { |
|||
components: { |
|||
AQTabber, |
|||
}, |
|||
data() { |
|||
return { |
|||
formData: {}, |
|||
tabType: '1', //1=班级成员,2=作业任务 |
|||
studentList: [] |
|||
} |
|||
}, |
|||
onLoad() { |
|||
this.getStudentList(); |
|||
}, |
|||
methods: { |
|||
async getStudentList() { |
|||
// 模拟数据,实际开发中应该从API获取 |
|||
const res = await memberApi.jlGetStudentList({ |
|||
'type': 'daoqi' |
|||
}); |
|||
if (res.code == 1) { |
|||
this.studentList = res.data || []; |
|||
} else { |
|||
uni.showToast({ |
|||
title: res.msg || '获取学员列表失败', |
|||
icon: 'none' |
|||
}); |
|||
} |
|||
}, |
|||
//打开课程详情 |
|||
openViewCourseInfo(item) { |
|||
uni.navigateTo({ |
|||
url: '/pages-coach/coach/course/info' |
|||
}) |
|||
}, |
|||
//打开学员详情页 |
|||
openViewStudentInfo(item) { |
|||
uni.navigateTo({ |
|||
url: '/pages-coach/coach/student/info' |
|||
}) |
|||
}, |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style lang="less" scoped> |
|||
.main_box { |
|||
background: #292929; |
|||
} |
|||
|
|||
//自定义导航栏 |
|||
.navbar_section { |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: center; |
|||
background: #292929; |
|||
|
|||
.title { |
|||
padding: 40rpx 0rpx; |
|||
|
|||
/* 小程序端样式 */ |
|||
// #ifdef MP-WEIXIN |
|||
padding: 80rpx 0rpx; |
|||
// #endif |
|||
|
|||
font-size: 30rpx; |
|||
color: #fff; |
|||
} |
|||
} |
|||
|
|||
.main_section { |
|||
min-height: 100vh; |
|||
background: #292929 100%; |
|||
padding: 0 24rpx; |
|||
padding-top: 40rpx; |
|||
padding-bottom: 150rpx; |
|||
font-size: 24rpx; |
|||
color: #FFFFFF; |
|||
|
|||
//班级成员列表 |
|||
.section_4 { |
|||
.ul { |
|||
display: flex; |
|||
flex-direction: column; |
|||
gap: 10rpx; |
|||
|
|||
.li { |
|||
padding: 20rpx 0; |
|||
padding-bottom: 40rpx; |
|||
border-bottom: 2px solid #D7D7D7; |
|||
display: flex; |
|||
justify-content: space-between; |
|||
|
|||
.left { |
|||
display: flex; |
|||
align-items: center; |
|||
gap: 30rpx; |
|||
|
|||
.box_1 { |
|||
padding-left: 20rpx; |
|||
display: flex; |
|||
flex-direction: column; |
|||
align-items: center; |
|||
justify-content: center; |
|||
position: relative; |
|||
|
|||
.pic { |
|||
width: 84rpx; |
|||
height: 84rpx; |
|||
border-radius: 50%; |
|||
} |
|||
|
|||
.tag_box { |
|||
position: absolute; |
|||
bottom: -30rpx; |
|||
width: 120rpx; |
|||
height: 38rpx; |
|||
background-color: #F59A23; |
|||
border-radius: 4rpx; |
|||
line-height: 35rpx; |
|||
text-align: center; |
|||
font-size: 20rpx; |
|||
} |
|||
} |
|||
|
|||
.box_2 { |
|||
display: flex; |
|||
flex-direction: column; |
|||
gap: 20rpx; |
|||
|
|||
.name { |
|||
font-size: 28rpx; |
|||
} |
|||
|
|||
.date { |
|||
font-size: 24rpx; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.right { |
|||
display: flex; |
|||
align-items: center; |
|||
gap: 14rpx; |
|||
|
|||
.item { |
|||
border: 1px solid #00E5BB; |
|||
border-radius: 10rpx; |
|||
width: 102rpx; |
|||
display: flex; |
|||
flex-direction: column; |
|||
|
|||
view { |
|||
text-align: center; |
|||
height: 50rpx; |
|||
line-height: 50rpx; |
|||
} |
|||
|
|||
view:nth-child(1) { |
|||
font-size: 32rpx; |
|||
background-color: #fff; |
|||
color: #00e5bb; |
|||
} |
|||
|
|||
view:nth-child(2) { |
|||
font-size: 20rpx; |
|||
background-color: #00e5bb; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
</style> |
|||
@ -0,0 +1,47 @@ |
|||
<template> |
|||
<view> |
|||
<view style="margin-top: 380rpx;text-align: center;font-size: 50rpx;font-weight: bold;"> |
|||
答对{{success}},答错{{error}} |
|||
</view> |
|||
<view style="margin-top: 15rpx;text-align: center;font-size: 50rpx;font-weight: bold;"> |
|||
总共得分{{num}} |
|||
</view> |
|||
<view @click="back" style="background-color: #00bfa5;margin: auto;text-align: center;margin-top: 30rpx;border-radius: 50rpx;font-size: 42rpx;padding: 20rpx;"> |
|||
返回列表 |
|||
</view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
export default { |
|||
data() { |
|||
return { |
|||
error: '', |
|||
success: '', |
|||
num: '' |
|||
} |
|||
}, |
|||
onLoad(options) { |
|||
if(options.error){ |
|||
this.error = options.error; |
|||
} |
|||
if(options.success){ |
|||
this.success = options.success; |
|||
} |
|||
if(options.num){ |
|||
this.num = options.num; |
|||
} |
|||
}, |
|||
methods: { |
|||
back() { |
|||
uni.navigateTo({ |
|||
url: '/pages-coach/coach/my/teaching_management' |
|||
}) |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style> |
|||
|
|||
</style> |
|||
@ -0,0 +1,185 @@ |
|||
<template> |
|||
<view class="whole"> |
|||
<view style="height: 36rpx;"></view> |
|||
|
|||
<view class="topic" v-for="(item,index) in quantityArr" :key="index" v-show="quantity == index"> |
|||
<view class="dis_style"> |
|||
<view class="title_icon">{{index + 1}}</view> |
|||
<view class="title_con" v-if="item.question_content_type === 'text'">{{item.question_content}}?</view> |
|||
</view> |
|||
<view class="dis_style"> |
|||
<view class="title_con" style="margin: auto;" v-if="item.question_content_type === 'image'"> |
|||
<img style="width: 300rpx;height: 300rpx;" :src="item.question_content" mode="aspectFit"> |
|||
</view> |
|||
</view> |
|||
<view style="line-height: 60rpx;margin-top: 16rpx;"> |
|||
<fui-radio-group name="radio" v-model="optionList[index]"> |
|||
<fui-checkbox-group name="checkbox" v-model="optionList[index]"> |
|||
<view class="title_con title_font" style="display: flex;align-items: center;" v-for="(item_option,index_option) in item.option_json" :key="index_option"> |
|||
<view style="display: flex;align-items: center;"> |
|||
<fui-radio :value="item_option.option" v-if="item.question_type != 'multiple_choice'"></fui-radio> |
|||
<fui-checkbox :value="item_option.option" v-if="item.question_type == 'multiple_choice'"></fui-checkbox> |
|||
</view> |
|||
<view>{{item_option.option}}. {{item_option.option_content}}</view> |
|||
</view> |
|||
</fui-checkbox-group> |
|||
</fui-radio-group> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="progress_bar"> |
|||
<fui-progress :percent="number_progress" background="#ddd" activeColor="#00bfa5" height="10"></fui-progress> |
|||
</view> |
|||
|
|||
<view class="switch_style"> |
|||
<view class="dis_style" style="color: #3e94ed;font-size: 30rpx;"> |
|||
<view @click="previous_question">上一题</view> |
|||
<view style="margin-left: 20rpx;" @click="next_question">下一题</view> |
|||
</view> |
|||
<view class="sub_style" @click="submit"> |
|||
提交试卷 |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</template> |
|||
<script> |
|||
import apiRoute from '@/api/apiRoute.js'; |
|||
export default { |
|||
data() { |
|||
return { |
|||
quantity: 0, |
|||
quantityArr: [1,2,3,4], |
|||
number_progress: 0, |
|||
testPaperId: 0, |
|||
optionList: [], |
|||
zid: 0 |
|||
} |
|||
}, |
|||
onLoad(options) { |
|||
if(options.id){ |
|||
this.testPaperId = options.id; |
|||
} |
|||
if(options.zid){ |
|||
this.zid = options.zid; |
|||
} |
|||
console.log(this.testPaperId, 999) |
|||
this.init() |
|||
}, |
|||
methods: { |
|||
async init() { |
|||
const res = await apiRoute.getTeachingTestPaper({exam_papers_id: this.testPaperId}); |
|||
if(res.code === 1) { |
|||
this.quantityArr = res.data.resText |
|||
this.number_progress = (Math.round(100 / res.data.count) * ( this.quantity + 1)); |
|||
} |
|||
}, |
|||
previous_question() { |
|||
if(this.quantity <= 0){ |
|||
uni.showToast({ |
|||
title: '已经是第一题了', |
|||
icon: 'none' |
|||
}) |
|||
return |
|||
} else { |
|||
this.quantity-- |
|||
} |
|||
this.number_progress = (Math.round(100 / this.quantityArr.length) * ( this.quantity + 1)); |
|||
}, |
|||
next_question() { |
|||
if((this.quantity + 1) >= this.quantityArr.length){ |
|||
uni.showToast({ |
|||
title: '已经是最后一题了', |
|||
icon: 'none' |
|||
}) |
|||
return |
|||
} else { |
|||
this.quantity++ |
|||
} |
|||
this.number_progress = (Math.round(100 / this.quantityArr.length) * ( this.quantity + 1)); |
|||
}, |
|||
async submit() { |
|||
this.optionList.forEach((item,index) => { |
|||
if (Array.isArray(item)) { |
|||
this.optionList[index] = item.join(',') |
|||
} |
|||
}) |
|||
const res = await apiRoute.submitTestPaper({optionList: this.optionList,testPaperId: this.testPaperId, id: this.zid}); |
|||
if(res.code === 1) { |
|||
uni.navigateTo({ |
|||
url: '/pages-coach/coach/my/exam_results?error=' + res.data.error + '&success=' + res.data.success + '&num=' + res.data.num |
|||
}) |
|||
} else { |
|||
uni.showToast({ |
|||
title: res.msg, |
|||
icon: 'none' |
|||
}) |
|||
return |
|||
} |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style> |
|||
.whole{ |
|||
width: 100%; |
|||
height: 100vh; |
|||
background-color: #171717; |
|||
} |
|||
.topic{ |
|||
width: 95%; |
|||
height: auto; |
|||
background-color: #2d2d2d; |
|||
border: #3e91e8 4rpx solid; |
|||
border-radius: 16rpx; |
|||
padding: 26rpx; |
|||
margin: auto; |
|||
} |
|||
.dis_style{ |
|||
display: flex; |
|||
align-items: center; |
|||
} |
|||
.title_icon{ |
|||
width: 60rpx; |
|||
height: 60rpx; |
|||
border-radius: 50rpx; |
|||
background-color: #00bfa5; |
|||
font-size: 26rpx; |
|||
color: #fff; |
|||
font-weight: bold; |
|||
text-align: center; |
|||
line-height: 60rpx; |
|||
} |
|||
.title_con{ |
|||
margin-left: 16rpx; |
|||
font-size: 26rpx; |
|||
color: #fff; |
|||
} |
|||
.title_font{ |
|||
font-size: 30rpx; |
|||
} |
|||
.progress_bar{ |
|||
width: 95%; |
|||
margin: 36rpx auto; |
|||
} |
|||
.switch_style{ |
|||
width: 95%; |
|||
height: auto; |
|||
padding: 26rpx; |
|||
background-color: #2d2d2d; |
|||
border-radius: 16rpx; |
|||
margin: auto; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: space-between; |
|||
} |
|||
.sub_style{ |
|||
font-size: 32rpx; |
|||
padding: 15rpx 30rpx; |
|||
border-radius: 30rpx; |
|||
color: #000; |
|||
font-weight: bold; |
|||
text-align: center; |
|||
background-color: #35a3f0; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,505 @@ |
|||
<!--销售-个人资料-详情--> |
|||
<template> |
|||
<view class="main_box"> |
|||
|
|||
<view class="main_section"> |
|||
<view class="section"> |
|||
<view class="item"> |
|||
<image |
|||
@click="changeAvatar()" |
|||
class="pic" |
|||
:src="$util.img(formData.head_img)" |
|||
></image> |
|||
|
|||
<view class="btn" @click="changeAvatar()">修改头像</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="section"> |
|||
<view class="item"> |
|||
<view class="title"> |
|||
姓名 <text class="required">*</text> |
|||
</view> |
|||
<view class="input"> |
|||
<input v-model="formData.name" placeholder="请输入姓名" /> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="item"> |
|||
<view class="title"> |
|||
账号 <text class="required"></text> |
|||
</view> |
|||
<view class="input"> |
|||
<input v-model="formData.username" disabled placeholder="暂无" /> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="item"> |
|||
<view class="title"> |
|||
部门 <text class="required"></text> |
|||
</view> |
|||
<view class="input"> |
|||
<!-- <input disabled :placeholder="formData.department_name_str" />--> |
|||
<view class="dept disabled">{{formData.department_name_str || '暂无'}}</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="item"> |
|||
<view class="title"> |
|||
等级 <text class="required"></text> |
|||
</view> |
|||
<view class="input"> |
|||
<input v-model="formData.member_level_name" disabled placeholder="暂无" /> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="section"> |
|||
<view class="item"> |
|||
<view class="title"> |
|||
性别 <text class="required">*</text> |
|||
</view> |
|||
<view class="input"> |
|||
<input placeholder="请选择性别" v-model="formData.gender_str" @click="picker_show_sex=true"/> |
|||
<fui-picker |
|||
layer="1" |
|||
:linkage="true" |
|||
:options="options_sex_arr" |
|||
:show="picker_show_sex" |
|||
@change="changePickerSex" |
|||
@cancel="picker_show_sex=false" |
|||
></fui-picker> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="item"> |
|||
<view class="title"> |
|||
生日 <text class="required">*</text> |
|||
</view> |
|||
<view class="input"> |
|||
<input placeholder="请选择生日" @click="picker_show_birthday=true" v-model="formData.birthday"/> |
|||
<fui-date-picker |
|||
:minDate="minDate" |
|||
:maxDate="maxDate" |
|||
:show="picker_show_birthday" |
|||
type="3" |
|||
@change="changePickerBirthday" |
|||
@cancel="picker_show_birthday=false" |
|||
></fui-date-picker> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="item"> |
|||
<view class="title"> |
|||
邮箱 <text class="required">*</text> |
|||
</view> |
|||
<view class="input"> |
|||
<input v-model="formData.email" placeholder="请输入邮箱" /> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="item"> |
|||
<view class="title"> |
|||
手机 <text class="required">*</text> |
|||
</view> |
|||
<view class="input"> |
|||
<input v-model="formData.phone" placeholder="请输入手机" /> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="item"> |
|||
<view class="title"> |
|||
微信 <text class="required"></text> |
|||
</view> |
|||
<view class="input"> |
|||
<input v-model="formData.wx" placeholder="请输入微信" /> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="submet_btn" @click="submit">提交</view> |
|||
</view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
import apiRoute from '@/api/apiRoute.js'; |
|||
import marketApi from '@/api/market.js'; |
|||
import { |
|||
Api_url |
|||
} from "@/common/config.js"; |
|||
import AQTabber from "@/components/AQ/AQTabber" |
|||
|
|||
|
|||
export default { |
|||
components: { |
|||
AQTabber, |
|||
}, |
|||
data() { |
|||
return { |
|||
formData:{ |
|||
head_img:'',//头像地址 |
|||
name:'',//姓名 |
|||
username:'',//账号 |
|||
address:'',//住址 |
|||
gender:'',//性别|1男,2女 |
|||
gender_str:'', |
|||
birthday:'',//生日 |
|||
email:'',//邮箱 |
|||
phone:'',//手机 |
|||
wx:'',//微信号 |
|||
}, |
|||
|
|||
userInfo: {}, |
|||
|
|||
//上传图片APi路径 |
|||
uploadUrl: `${Api_url}/uploadImage`, |
|||
|
|||
//性别选择器 相关 |
|||
picker_show_sex: false, |
|||
sex_name:'请选择', |
|||
options_sex_arr: [ |
|||
{ |
|||
value: 1, |
|||
text: '男' |
|||
}, |
|||
{ |
|||
value: 2, |
|||
text: '女' |
|||
}, |
|||
], |
|||
|
|||
//生日选择器相关 |
|||
minDate: '', |
|||
maxDate: '', |
|||
picker_show_birthday: false, |
|||
upload_type: 1, |
|||
uploadHeadimg: '', |
|||
editHeadimg: '', |
|||
} |
|||
}, |
|||
onLoad() { |
|||
}, |
|||
onShow() { |
|||
this.init() |
|||
}, |
|||
methods: { |
|||
|
|||
async init(){ |
|||
// this.getBirthday() |
|||
this.setDateYear() |
|||
await this.getUserInfo() |
|||
}, |
|||
|
|||
//设置年份 |
|||
setDateYear() { |
|||
let currentYear = new Date().getFullYear(); |
|||
this.minDate = String(currentYear - 100); |
|||
this.maxDate = String(currentYear + 1); |
|||
}, |
|||
|
|||
//获取用户详情 |
|||
async getUserInfo(){ |
|||
let res = await apiRoute.getPersonnelInfo({}) |
|||
if (res.code != 1){ |
|||
uni.showToast({ |
|||
title: res.msg, |
|||
icon: 'none' |
|||
}) |
|||
return |
|||
} |
|||
|
|||
let gender_str = '' |
|||
if(res.data.gender == 1){ |
|||
gender_str = '男' |
|||
}else if(res.data.gender == 2){ |
|||
gender_str = '女' |
|||
} |
|||
|
|||
//用户表单 |
|||
this.formData = { |
|||
head_img: res.data.head_img,//头像地址 |
|||
name: res.data.name,//姓名 |
|||
username: res.data.phone,//账号 |
|||
address: res.data.address,//住址 |
|||
gender: res.data.gender,//性别|1男,2女 |
|||
gender_str:gender_str, |
|||
birthday: res.data.birthday,//生日 |
|||
email: res.data.email || '',//邮箱 |
|||
phone: res.data.phone,//手机 |
|||
wx: res.data.wx || '',//微信号 |
|||
member_level_name: res.data.member_level_name || '',//等级名称 |
|||
department_name_str:res.data.department_name_str || '暂无',//部门名称 |
|||
} |
|||
}, |
|||
|
|||
// 修改头像按钮 |
|||
changeAvatar() { |
|||
uni.chooseImage({ |
|||
count: 1, |
|||
sizeType: ['compressed'], |
|||
sourceType: ['album', 'camera'], |
|||
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, "") || "{}") |
|||
console.log('上传成功2', res) |
|||
if (res.code == 1) { |
|||
this.upload_type = 2 |
|||
this.formData.head_img = res.data.url |
|||
// this.editHeadimg = res.data.path |
|||
// this.uploadHeadimg = res.data.url |
|||
} else { |
|||
uni.showToast({ |
|||
title: res.msg, |
|||
icon: 'none' |
|||
}) |
|||
} |
|||
}, |
|||
}); |
|||
}, |
|||
|
|||
//性别选择相关 |
|||
changePickerSex(e) { |
|||
console.log('监听选择', e) |
|||
this.formData.gender = e.value |
|||
this.formData.gender_str = e.text |
|||
this.picker_show_sex = false |
|||
}, |
|||
|
|||
//生日选择相关 |
|||
//获取当前年月日+获取30年前的日期 |
|||
getBirthday() { |
|||
let date = new Date(); |
|||
let year = date.getFullYear(); |
|||
let month = date.getMonth() + 1; |
|||
let day = date.getDate(); |
|||
let year_30 = year - 30; |
|||
let month_30 = month; |
|||
let day_30 = day; |
|||
if (month_30 == 2 && day_30 > 28) { |
|||
month_30 = 3; |
|||
day_30 = 1; |
|||
} |
|||
if (month_30 == 4 && day_30 > 30) { |
|||
month_30 = 5; |
|||
day_30 = 1; |
|||
} |
|||
if (month_30 == 6 && day_30 > 30) { |
|||
month_30 = 7; |
|||
day_30 = 1; |
|||
} |
|||
if (month_30 == 9 && day_30 > 30) { |
|||
month_30 = 10; |
|||
day_30 = 1; |
|||
} |
|||
if (month_30 == 11 && day_30 > 30) { |
|||
month_30 = 12; |
|||
day_30 = 1; |
|||
} |
|||
if (month_30 > 12) { |
|||
month_30 = month_30 - 12; |
|||
year_30 = year_30 + 1; |
|||
} |
|||
let minDate = year_30 + "-" + month_30 + "-" + day_30 |
|||
let maxDate = year + "-" + month + "-" + day |
|||
this.minDate = minDate |
|||
this.maxDate = maxDate |
|||
}, |
|||
|
|||
//监听生日选择 |
|||
changePickerBirthday(e) { |
|||
console.log('监听生日选择', e) |
|||
this.formData.birthday = e.result |
|||
this.picker_show_birthday = false |
|||
}, |
|||
|
|||
//提交信息 |
|||
async submit() { |
|||
let data = {...this.formData} |
|||
|
|||
if(!data.head_img){ |
|||
uni.showToast({ |
|||
title: '请上传头像', |
|||
icon: 'none' |
|||
}) |
|||
return |
|||
} |
|||
|
|||
if(!data.name){ |
|||
uni.showToast({ |
|||
title: '请填写', |
|||
icon: 'none' |
|||
}) |
|||
return |
|||
} |
|||
|
|||
if(!data.gender){ |
|||
uni.showToast({ |
|||
title: '请选择性别', |
|||
icon: 'none' |
|||
}) |
|||
return |
|||
} |
|||
|
|||
if(!data.birthday){ |
|||
uni.showToast({ |
|||
title: '请选择生日', |
|||
icon: 'none' |
|||
}) |
|||
return |
|||
} |
|||
|
|||
if(!data.email){ |
|||
uni.showToast({ |
|||
title: '请填写邮箱', |
|||
icon: 'none' |
|||
}) |
|||
return |
|||
} |
|||
|
|||
if(!data.phone){ |
|||
uni.showToast({ |
|||
title: '请填写手机', |
|||
icon: 'none' |
|||
}) |
|||
return |
|||
} |
|||
|
|||
|
|||
let res = await apiRoute.editPersonnelInfo(data) |
|||
if(res.code != 1){ |
|||
uni.showToast({ |
|||
title: res.msg, |
|||
icon: 'none' |
|||
}) |
|||
return |
|||
} |
|||
uni.showToast({ |
|||
title: res.msg, |
|||
icon: 'success' |
|||
}) |
|||
//延迟1s执行 |
|||
setTimeout(() => { |
|||
this.getUserInfo() |
|||
}, 1000) |
|||
}, |
|||
|
|||
|
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style lang="less" scoped> |
|||
|
|||
.main_box{ |
|||
background: #292929 ; |
|||
} |
|||
|
|||
//自定义导航栏 |
|||
.navbar_section{ |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: center; |
|||
background: #29d3b4; |
|||
.title{ |
|||
padding: 40rpx 0rpx; |
|||
|
|||
/* 小程序端样式 */ |
|||
// #ifdef MP-WEIXIN |
|||
padding: 80rpx 0rpx; |
|||
// #endif |
|||
|
|||
font-size: 30rpx; |
|||
color: #315d55; |
|||
} |
|||
} |
|||
|
|||
.main_section{ |
|||
min-height: 100vh; |
|||
background: #292929 100%; |
|||
padding: 0 0rpx; |
|||
padding-top: 32rpx; |
|||
padding-bottom: 150rpx; |
|||
font-size: 28rpx; |
|||
color: #fff; |
|||
display: flex; |
|||
flex-direction: column; |
|||
gap: 20rpx; |
|||
|
|||
.section{ |
|||
background-color: #434544; |
|||
.item{ |
|||
padding: 20rpx 40rpx; |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
.pic{ |
|||
width: 82rpx; |
|||
height: 82rpx; |
|||
border-radius: 50%; |
|||
} |
|||
.btn{} |
|||
|
|||
.title{ |
|||
min-width: 100rpx; |
|||
display: flex; |
|||
align-items: center; |
|||
font-size: 26rpx; |
|||
color: #D7D7D7; |
|||
.required{ |
|||
margin-left: 10rpx; |
|||
color: red; |
|||
} |
|||
} |
|||
.input{ |
|||
display: flex; |
|||
justify-content: flex-end; |
|||
input{ |
|||
text-align: right; |
|||
} |
|||
.dept{ |
|||
width: 50%; |
|||
} |
|||
.disabled{ |
|||
color: #808080; |
|||
//禁止图标 |
|||
cursor: not-allowed; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
.submet_btn{ |
|||
margin: 0 auto; |
|||
margin-top: 40rpx; |
|||
border: 2px solid #25a18b; |
|||
color: #25a18b; |
|||
width: 80%; |
|||
height: 80rpx; |
|||
font-size: 30rpx; |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: center; |
|||
} |
|||
|
|||
|
|||
|
|||
} |
|||
|
|||
|
|||
</style> |
|||
@ -0,0 +1,546 @@ |
|||
<!--员工-我的工资--> |
|||
<template> |
|||
<view class="main_box"> |
|||
<view class="main_section"> |
|||
|
|||
<!-- 筛选条件 --> |
|||
<view class="filter_section"> |
|||
<view class="filter_item"> |
|||
<picker mode="date" fields="month" :value="selectedMonth" @change="onMonthChange"> |
|||
<view class="picker_display"> |
|||
<text>{{ selectedMonth || '选择月份' }}</text> |
|||
<text class="picker_arrow">></text> |
|||
</view> |
|||
</picker> |
|||
</view> |
|||
<view class="filter_btn" @click="loadSalaryList">查询</view> |
|||
</view> |
|||
|
|||
<!-- 工资条列表 --> |
|||
<view class="salary_list" v-if="salaryList.length > 0"> |
|||
<view |
|||
class="salary_item" |
|||
v-for="(item, index) in salaryList" |
|||
:key="item.id" |
|||
@click="viewSalaryDetail(item)" |
|||
> |
|||
<view class="salary_header"> |
|||
<view class="salary_month">{{ formatMonth(item.salary_month) }}</view> |
|||
<view :class="['salary_status',getStatusClass(item.status)]"> |
|||
{{ getStatusText(item.status) }} |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="salary_content"> |
|||
<view class="salary_row"> |
|||
<text class="label">基础工资:</text> |
|||
<text class="value">¥{{ formatMoney(item.base_salary) }}</text> |
|||
</view> |
|||
<view class="salary_row"> |
|||
<text class="label">出勤工资:</text> |
|||
<text class="value">¥{{ formatMoney(item.work_salary) }}</text> |
|||
</view> |
|||
<view class="salary_row"> |
|||
<text class="label">应发工资:</text> |
|||
<text class="value highlight">¥{{ formatMoney(item.gross_salary) }}</text> |
|||
</view> |
|||
<view class="salary_row"> |
|||
<text class="label">实发工资:</text> |
|||
<text class="value net_salary">¥{{ formatMoney(item.net_salary) }}</text> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="salary_footer"> |
|||
<text class="view_detail">点击查看详情 ></text> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 暂无数据 --> |
|||
<view class="no_data" v-else-if="!loading"> |
|||
<image class="no_data_icon" src="/static/images/no_data.png" mode="aspectFit"></image> |
|||
<text class="no_data_text">暂无工资数据</text> |
|||
</view> |
|||
|
|||
<!-- 加载中 --> |
|||
<view class="loading" v-if="loading"> |
|||
<text>加载中...</text> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 工资详情弹窗 --> |
|||
<view class="salary_modal" v-if="showDetail" @click="closeDetail"> |
|||
<view class="modal_content" @click.stop> |
|||
<view class="modal_header"> |
|||
<text class="modal_title">工资详情</text> |
|||
<text class="modal_close" @click="closeDetail">×</text> |
|||
</view> |
|||
|
|||
<view class="modal_body" v-if="salaryDetail"> |
|||
<view class="detail_section"> |
|||
<view class="section_title">基础信息</view> |
|||
<view class="detail_row"> |
|||
<text class="detail_label">工资月份:</text> |
|||
<text class="detail_value">{{ formatMonth(salaryDetail.salary_month) }}</text> |
|||
</view> |
|||
<view class="detail_row"> |
|||
<text class="detail_label">基础工资:</text> |
|||
<text class="detail_value">¥{{ formatMoney(salaryDetail.base_salary) }}</text> |
|||
</view> |
|||
<view class="detail_row"> |
|||
<text class="detail_label">满勤天数:</text> |
|||
<text class="detail_value">{{ salaryDetail.full_attendance_days }}天</text> |
|||
</view> |
|||
<view class="detail_row"> |
|||
<text class="detail_label">出勤天数:</text> |
|||
<text class="detail_value">{{ salaryDetail.attendance }}天</text> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="detail_section"> |
|||
<view class="section_title">收入明细</view> |
|||
<view class="detail_row"> |
|||
<text class="detail_label">出勤工资:</text> |
|||
<text class="detail_value">¥{{ formatMoney(salaryDetail.work_salary) }}</text> |
|||
</view> |
|||
<view class="detail_row" v-if="salaryDetail.mgr_performance > 0"> |
|||
<text class="detail_label">管理绩效:</text> |
|||
<text class="detail_value">¥{{ formatMoney(salaryDetail.mgr_performance) }}</text> |
|||
</view> |
|||
<view class="detail_row" v-if="salaryDetail.performance_bonus > 0"> |
|||
<text class="detail_label">销售提成:</text> |
|||
<text class="detail_value">¥{{ formatMoney(salaryDetail.performance_bonus) }}</text> |
|||
</view> |
|||
<view class="detail_row" v-if="salaryDetail.other_subsidies > 0"> |
|||
<text class="detail_label">其他补贴:</text> |
|||
<text class="detail_value">¥{{ formatMoney(salaryDetail.other_subsidies) }}</text> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="detail_section"> |
|||
<view class="section_title">扣除明细</view> |
|||
<view class="detail_row" v-if="salaryDetail.deductions > 0"> |
|||
<text class="detail_label">其他扣款:</text> |
|||
<text class="detail_value text_red">-¥{{ formatMoney(salaryDetail.deductions) }}</text> |
|||
</view> |
|||
<view class="detail_row" v-if="salaryDetail.social_security > 0"> |
|||
<text class="detail_label">社保:</text> |
|||
<text class="detail_value text_red">-¥{{ formatMoney(salaryDetail.social_security) }}</text> |
|||
</view> |
|||
<view class="detail_row" v-if="salaryDetail.individual_income_tax > 0"> |
|||
<text class="detail_label">个税:</text> |
|||
<text class="detail_value text_red">-¥{{ formatMoney(salaryDetail.individual_income_tax) }}</text> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="detail_section"> |
|||
<view class="section_title">工资汇总</view> |
|||
<view class="detail_row highlight_row"> |
|||
<text class="detail_label">应发工资:</text> |
|||
<text class="detail_value highlight">¥{{ formatMoney(salaryDetail.gross_salary) }}</text> |
|||
</view> |
|||
<view class="detail_row net_salary_row"> |
|||
<text class="detail_label">实发工资:</text> |
|||
<text class="detail_value net_salary">¥{{ formatMoney(salaryDetail.net_salary) }}</text> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="detail_section" v-if="salaryDetail.remarks"> |
|||
<view class="section_title">备注</view> |
|||
<view class="detail_remarks">{{ salaryDetail.remarks }}</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
import memberApi from '@/api/member.js'; |
|||
|
|||
export default { |
|||
data() { |
|||
return { |
|||
selectedMonth: '', |
|||
salaryList: [], |
|||
loading: false, |
|||
showDetail: false, |
|||
salaryDetail: null, |
|||
} |
|||
}, |
|||
|
|||
onLoad() { |
|||
// 默认查询当月数据 |
|||
const now = new Date(); |
|||
this.selectedMonth = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}`; |
|||
this.loadSalaryList(); |
|||
}, |
|||
|
|||
methods: { |
|||
// 月份选择 |
|||
onMonthChange(e) { |
|||
this.selectedMonth = e.detail.value; |
|||
}, |
|||
|
|||
// 加载工资列表 |
|||
async loadSalaryList() { |
|||
this.loading = true; |
|||
try { |
|||
const params = {}; |
|||
if (this.selectedMonth) { |
|||
params.salary_month = this.selectedMonth; |
|||
} |
|||
|
|||
const res = await memberApi.getSalaryList(params); |
|||
if (res.code === 1) { |
|||
this.salaryList = res.data.data || []; |
|||
} else { |
|||
uni.showToast({ |
|||
title: res.msg || '获取数据失败', |
|||
icon: 'none' |
|||
}); |
|||
} |
|||
} catch (error) { |
|||
console.error('获取工资列表失败:', error); |
|||
uni.showToast({ |
|||
title: '网络错误,请稍后重试', |
|||
icon: 'none' |
|||
}); |
|||
} finally { |
|||
this.loading = false; |
|||
} |
|||
}, |
|||
|
|||
// 查看工资详情 |
|||
async viewSalaryDetail(item) { |
|||
try { |
|||
const res = await memberApi.getSalaryInfo({ id: item.id }); |
|||
if (res.code === 1) { |
|||
this.salaryDetail = res.data; |
|||
this.showDetail = true; |
|||
} else { |
|||
uni.showToast({ |
|||
title: res.msg || '获取详情失败', |
|||
icon: 'none' |
|||
}); |
|||
} |
|||
} catch (error) { |
|||
console.error('获取工资详情失败:', error); |
|||
uni.showToast({ |
|||
title: '网络错误,请稍后重试', |
|||
icon: 'none' |
|||
}); |
|||
} |
|||
}, |
|||
|
|||
// 关闭详情弹窗 |
|||
closeDetail() { |
|||
this.showDetail = false; |
|||
this.salaryDetail = null; |
|||
}, |
|||
|
|||
// 格式化月份显示 |
|||
formatMonth(month) { |
|||
if (!month) return ''; |
|||
return month.substring(0, 7).replace('-', '年') + '月'; |
|||
}, |
|||
|
|||
// 格式化金额 |
|||
formatMoney(amount) { |
|||
if (!amount && amount !== 0) return '0.00'; |
|||
return Number(amount).toFixed(2); |
|||
}, |
|||
|
|||
// 获取状态文字 |
|||
getStatusText(status) { |
|||
switch (status) { |
|||
case 1: return '未发放'; |
|||
case 2: return '已发放'; |
|||
default: return '未知'; |
|||
} |
|||
}, |
|||
|
|||
// 获取状态样式类 |
|||
getStatusClass(status) { |
|||
switch (status) { |
|||
case 1: return 'status_pending'; |
|||
case 2: return 'status_paid'; |
|||
default: return ''; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style lang="less" scoped> |
|||
.main_box { |
|||
background: #292929; |
|||
min-height: 100vh; |
|||
} |
|||
|
|||
.main_section { |
|||
padding: 20rpx; |
|||
padding-bottom: 150rpx; |
|||
} |
|||
|
|||
/* 筛选条件 */ |
|||
.filter_section { |
|||
display: flex; |
|||
align-items: center; |
|||
background: #434544; |
|||
border-radius: 10rpx; |
|||
padding: 20rpx; |
|||
margin-bottom: 20rpx; |
|||
|
|||
.filter_item { |
|||
flex: 1; |
|||
|
|||
.picker_display { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
color: #D7D7D7; |
|||
font-size: 28rpx; |
|||
|
|||
.picker_arrow { |
|||
color: #25a18b; |
|||
font-size: 24rpx; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.filter_btn { |
|||
background: #25a18b; |
|||
color: #fff; |
|||
padding: 15rpx 30rpx; |
|||
border-radius: 8rpx; |
|||
font-size: 26rpx; |
|||
margin-left: 20rpx; |
|||
} |
|||
} |
|||
|
|||
/* 工资条列表 */ |
|||
.salary_list { |
|||
.salary_item { |
|||
background: #434544; |
|||
border-radius: 10rpx; |
|||
padding: 30rpx; |
|||
margin-bottom: 20rpx; |
|||
|
|||
.salary_header { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
margin-bottom: 20rpx; |
|||
padding-bottom: 15rpx; |
|||
border-bottom: 1rpx solid #555; |
|||
|
|||
.salary_month { |
|||
color: #fff; |
|||
font-size: 32rpx; |
|||
font-weight: bold; |
|||
} |
|||
|
|||
.salary_status { |
|||
padding: 8rpx 16rpx; |
|||
border-radius: 20rpx; |
|||
font-size: 24rpx; |
|||
|
|||
&.status_pending { |
|||
background: rgba(255, 165, 0, 0.2); |
|||
color: #FFA500; |
|||
} |
|||
|
|||
&.status_paid { |
|||
background: rgba(34, 197, 94, 0.2); |
|||
color: #22c55e; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.salary_content { |
|||
.salary_row { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
margin-bottom: 15rpx; |
|||
|
|||
.label { |
|||
color: #D7D7D7; |
|||
font-size: 26rpx; |
|||
} |
|||
|
|||
.value { |
|||
color: #fff; |
|||
font-size: 28rpx; |
|||
|
|||
&.highlight { |
|||
color: #25a18b; |
|||
font-weight: bold; |
|||
} |
|||
|
|||
&.net_salary { |
|||
color: #22c55e; |
|||
font-weight: bold; |
|||
font-size: 30rpx; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
.salary_footer { |
|||
text-align: center; |
|||
margin-top: 20rpx; |
|||
padding-top: 15rpx; |
|||
border-top: 1rpx solid #555; |
|||
|
|||
.view_detail { |
|||
color: #25a18b; |
|||
font-size: 24rpx; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
/* 暂无数据 */ |
|||
.no_data { |
|||
text-align: center; |
|||
padding: 100rpx 0; |
|||
color: #888; |
|||
|
|||
.no_data_icon { |
|||
width: 150rpx; |
|||
height: 150rpx; |
|||
margin-bottom: 30rpx; |
|||
} |
|||
|
|||
.no_data_text { |
|||
font-size: 28rpx; |
|||
} |
|||
} |
|||
|
|||
/* 加载中 */ |
|||
.loading { |
|||
text-align: center; |
|||
padding: 50rpx 0; |
|||
color: #888; |
|||
font-size: 28rpx; |
|||
} |
|||
|
|||
/* 工资详情弹窗 */ |
|||
.salary_modal { |
|||
position: fixed; |
|||
top: 0; |
|||
left: 0; |
|||
right: 0; |
|||
bottom: 0; |
|||
background: rgba(0, 0, 0, 0.7); |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
z-index: 1000; |
|||
|
|||
.modal_content { |
|||
background: #434544; |
|||
border-radius: 15rpx; |
|||
width: 90%; |
|||
max-height: 80%; |
|||
overflow: hidden; |
|||
|
|||
.modal_header { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
padding: 30rpx; |
|||
border-bottom: 1rpx solid #555; |
|||
|
|||
.modal_title { |
|||
color: #fff; |
|||
font-size: 32rpx; |
|||
font-weight: bold; |
|||
} |
|||
|
|||
.modal_close { |
|||
color: #888; |
|||
font-size: 40rpx; |
|||
width: 60rpx; |
|||
height: 60rpx; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
} |
|||
} |
|||
|
|||
.modal_body { |
|||
max-height: 60vh; |
|||
overflow-y: auto; |
|||
padding: 30rpx; |
|||
|
|||
.detail_section { |
|||
margin-bottom: 40rpx; |
|||
|
|||
.section_title { |
|||
color: #25a18b; |
|||
font-size: 28rpx; |
|||
font-weight: bold; |
|||
margin-bottom: 20rpx; |
|||
padding-bottom: 10rpx; |
|||
border-bottom: 1rpx solid #555; |
|||
} |
|||
|
|||
.detail_row { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
margin-bottom: 15rpx; |
|||
|
|||
.detail_label { |
|||
color: #D7D7D7; |
|||
font-size: 26rpx; |
|||
} |
|||
|
|||
.detail_value { |
|||
color: #fff; |
|||
font-size: 26rpx; |
|||
|
|||
&.highlight { |
|||
color: #25a18b; |
|||
font-weight: bold; |
|||
} |
|||
|
|||
&.net_salary { |
|||
color: #22c55e; |
|||
font-weight: bold; |
|||
} |
|||
|
|||
&.text_red { |
|||
color: #f56565; |
|||
} |
|||
} |
|||
} |
|||
|
|||
&.highlight_row { |
|||
background: rgba(37, 161, 139, 0.1); |
|||
padding: 15rpx; |
|||
border-radius: 8rpx; |
|||
} |
|||
|
|||
&.net_salary_row { |
|||
background: rgba(34, 197, 94, 0.1); |
|||
padding: 15rpx; |
|||
border-radius: 8rpx; |
|||
} |
|||
|
|||
.detail_remarks { |
|||
background: #555; |
|||
padding: 20rpx; |
|||
border-radius: 8rpx; |
|||
color: #D7D7D7; |
|||
font-size: 26rpx; |
|||
line-height: 1.6; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
</style> |
|||
@ -0,0 +1,241 @@ |
|||
<!--授课统计-详情--> |
|||
<template> |
|||
<view class="main_box"> |
|||
|
|||
<view class="main_section"> |
|||
<view class="section_1"> |
|||
<view class="ul"> |
|||
<view class="li" v-for="(v,k) in sktjlist" :key="k"> |
|||
<view class="title">{{v.month_date}}</view> |
|||
<view class="box"> |
|||
<view class="top"> |
|||
<view class="top_item"> |
|||
<view class="num">{{v.ysks}}</view> |
|||
<view class="explain">月授课数/节</view> |
|||
</view> |
|||
<view class="top_item"> |
|||
<view class="num">{{v.zsbj}}</view> |
|||
<view class="explain">总授班级/个</view> |
|||
</view> |
|||
<view class="top_item"> |
|||
<view class="num">{{v.yfzxy}}</view> |
|||
<view class="explain">月负责学员/名</view> |
|||
</view> |
|||
</view> |
|||
<view class="bottom"> |
|||
月到课率<text>{{v.ydkl}}%</text> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
import memberApi from '@/api/member.js'; |
|||
import AQTabber from "@/components/AQ/AQTabber.vue" |
|||
|
|||
|
|||
export default { |
|||
components: { |
|||
AQTabber, |
|||
}, |
|||
data() { |
|||
return { |
|||
sktjlist:[],//授课统计列表 |
|||
formData:{}, |
|||
|
|||
//课程下拉菜单相关 |
|||
show_course:false,//是否显示下拉菜单 |
|||
//课程下拉菜单 |
|||
course_name:'课程',//选中的下拉菜单名称 |
|||
options_course: [ |
|||
{ |
|||
text: '请选择课程', |
|||
value: '', |
|||
checked: true |
|||
}, { |
|||
text: '羽毛球课程1', |
|||
value: '1' |
|||
}, { |
|||
text: '篮球课程2', |
|||
value: '2' |
|||
} |
|||
], |
|||
|
|||
//课室下拉菜单相关 |
|||
show_classroom:false,//是否显示下拉菜单 |
|||
//课程下拉菜单 |
|||
classroom_name:'课室',//选中的下拉菜单名称 |
|||
options_classroom: [ |
|||
{ |
|||
text: '请选择课室', |
|||
value: '', |
|||
checked: true |
|||
}, { |
|||
text: '羽毛球201', |
|||
value: '1' |
|||
}, { |
|||
text: '篮球室101', |
|||
value: '2' |
|||
} |
|||
], |
|||
} |
|||
}, |
|||
onLoad() { |
|||
}, |
|||
onShow(){ |
|||
this.init() |
|||
}, |
|||
methods: { |
|||
//初始化 |
|||
async init(){ |
|||
await this.getList() |
|||
}, |
|||
|
|||
//获取授课统计 |
|||
async getList(){ |
|||
let res = await memberApi.jlSktj({}) |
|||
if (res.code != 1){ |
|||
uni.showToast({ |
|||
title: res.msg, |
|||
icon: 'none' |
|||
}) |
|||
return |
|||
} |
|||
|
|||
this.sktjlist = res.data//授课统计列表 |
|||
|
|||
}, |
|||
|
|||
//选中课程下拉菜单点击事件 |
|||
clickCourse(e){ |
|||
console.log(e) |
|||
this.course_name = e.text |
|||
this.show_course = true |
|||
}, |
|||
//显示下拉菜单 |
|||
filterTapCourse() { |
|||
//显示下拉框 |
|||
this.$refs.ref_course.show() |
|||
this.show_course = true; |
|||
}, |
|||
|
|||
|
|||
|
|||
//选中课室 |
|||
clickClassroom(e){ |
|||
console.log(e) |
|||
this.classroom_name = e.text |
|||
this.show_classroom = true |
|||
}, |
|||
//显示课室下拉菜单 |
|||
filterTapClassroom() { |
|||
//显示下拉框 |
|||
this.$refs.ref_classroom.show() |
|||
this.show_classroom = true; |
|||
}, |
|||
|
|||
//打开课时详情页 |
|||
openViewCourseInfo(item){ |
|||
uni.navigateTo({ |
|||
url: '/pages-coach/coach/course/info' |
|||
}) |
|||
}, |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style lang="less" scoped> |
|||
|
|||
.main_box{ |
|||
background: #292929 ; |
|||
} |
|||
|
|||
//自定义导航栏 |
|||
.navbar_section{ |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: center; |
|||
background: #29d3b4; |
|||
.title{ |
|||
padding: 40rpx 0rpx; |
|||
|
|||
/* 小程序端样式 */ |
|||
// #ifdef MP-WEIXIN |
|||
padding: 80rpx 0rpx; |
|||
// #endif |
|||
|
|||
|
|||
font-size: 30rpx; |
|||
color: #315d55; |
|||
} |
|||
} |
|||
|
|||
.main_section{ |
|||
min-height: 100vh; |
|||
background: #292929 100%; |
|||
padding: 0 24rpx; |
|||
padding-top: 32rpx; |
|||
padding-bottom: 150rpx; |
|||
font-size: 28rpx; |
|||
.section_1{ |
|||
color: #fff; |
|||
font-size: 24rpx; |
|||
.ul{ |
|||
display: flex; |
|||
flex-direction: column; |
|||
gap: 56rpx; |
|||
.li{ |
|||
.title{ |
|||
color: #fff; |
|||
font-size: 24rpx; |
|||
} |
|||
.box{ |
|||
margin-top: 24rpx; |
|||
padding: 36rpx 32rpx 28rpx 44rpx; |
|||
width: 700rpx; |
|||
border-radius: 14rpx; |
|||
background-color: #fff; |
|||
color: #333333FF; |
|||
font-size: 26rpx; |
|||
.top{ |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
.top_item{ |
|||
display: flex; |
|||
flex-direction: column; |
|||
align-items: center; |
|||
gap: 12rpx; |
|||
.num{ |
|||
font-size: 56rpx; |
|||
color: #29D3B4; |
|||
} |
|||
.explain{ |
|||
font-size: 24rpx; |
|||
color: #AAAAAA; |
|||
} |
|||
} |
|||
} |
|||
.bottom{ |
|||
margin-top: 34rpx; |
|||
font-size: 24rpx; |
|||
text{ |
|||
margin-left: 15rpx; |
|||
color: #29D3B4; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
} |
|||
} |
|||
|
|||
} |
|||
|
|||
|
|||
</style> |
|||
@ -0,0 +1,58 @@ |
|||
<!--设置页--> |
|||
<template> |
|||
<view class="assemble"> |
|||
<view style="height: 30rpx;"></view> |
|||
<view class="option" @click="update_pass()">修改密码</view> |
|||
<view class="option" @click="privacy_agreement(1)">用户协议</view> |
|||
<view class="option" @click="privacy_agreement(2)">隐私策略</view> |
|||
<view class="option">清空缓存</view> |
|||
|
|||
<view style="width:90%;margin: 60rpx auto;"> |
|||
<fui-button background="#29d3b4" @click="loginOut()">退出账号</fui-button> |
|||
</view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
export default { |
|||
data() { |
|||
return { |
|||
|
|||
} |
|||
}, |
|||
methods: { |
|||
//退出登陆 |
|||
loginOut(){ |
|||
this.$util.loginOut() |
|||
}, |
|||
|
|||
privacy_agreement(type){ |
|||
uni.navigateTo({ |
|||
url: '/pages-common/privacy_agreement?type='+type |
|||
}) |
|||
}, |
|||
update_pass(){ |
|||
uni.navigateTo({ |
|||
url: '/pages-coach/coach/my/update_pass' |
|||
}) |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style lang="less" scoped> |
|||
.assemble{ |
|||
width: 100%; |
|||
height: 100vh; |
|||
background: #333333; |
|||
} |
|||
.option{ |
|||
margin-bottom: 20rpx; |
|||
background: #404045; |
|||
width: 100%; |
|||
font-size: 28rpx; |
|||
color: #fff; |
|||
line-height: 56rpx; |
|||
padding: 20rpx 0 20rpx 100rpx; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,247 @@ |
|||
<template> |
|||
<view class="dark-theme"> |
|||
<fui-tabs :tabs="tabsList" @change="change" isDot scroll alignLeft class="custom-tabs" :color="'#00d18c'" :selectedColor="'#00d18c'" :background="'#121212'" :itemBackground="'#121212'"></fui-tabs> |
|||
<scroll-view scroll-y :scroll-with-animation="true" @scrolltolower="onReachBottom"> |
|||
<view class="dis_style div_style" v-for="(item,index) in arrayList" :key="index"> |
|||
<view @click="info(item.id)"> |
|||
<view class='color_style'>{{item.title}}</view> |
|||
<view class='color_type_style'>{{getTableType(item.table_type)}}</view> |
|||
</view> |
|||
|
|||
<view> |
|||
<view class='color_date_style'>{{item.create_time}}</view> |
|||
<view class='color_date_style exam-btn' v-if="item.exam_papers_id != 0" @click="goTake(item.exam_papers_id,item.id)">去考试</view> |
|||
</view> |
|||
</view> |
|||
<view v-if="loading" class="loading">加载中...</view> |
|||
<view v-else-if="noMoreData" class="no-more">没有更多数据了</view> |
|||
</scroll-view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
import apiRoute from '@/api/apiRoute.js'; |
|||
export default { |
|||
data() { |
|||
return { |
|||
arrayList: [], |
|||
tabsList: [], |
|||
tableTypeName: { |
|||
'1': "课程教学大纲", |
|||
'2': "跳绳教案库", |
|||
'3': "增高教案库", |
|||
'4': "篮球教案库", |
|||
'5': "强化教案库", |
|||
'6': "空中忍者教案库", |
|||
'7': "少儿安防教案库", |
|||
'8': "体能教案库", |
|||
'9': "热身动作库", |
|||
'10': "体能动作库", |
|||
'11': "趣味游戏库", |
|||
'12': "放松动作库", |
|||
'13': "训练内容", |
|||
'14': "训练视频", |
|||
'15': "课后作业", |
|||
'16': "优秀一堂课", |
|||
'17': "空中忍者", |
|||
'18': "篮球动作", |
|||
'19': "跳绳动作", |
|||
'20': "跑酷动作", |
|||
'21': "安防动作", |
|||
'22': "标准化动作", |
|||
'23': "3-6岁体测", |
|||
'24': "7+体测", |
|||
'25': "3-6岁体测讲解—解读", |
|||
'26': "7+岁体测讲解—解读", |
|||
'27': "互动游戏", |
|||
'28': "套圈游戏", |
|||
'29': "鼓励方式" |
|||
}, |
|||
tabsListArr: [], |
|||
searchArr: { |
|||
table_type: undefined, |
|||
page: 1, |
|||
limit: 10 |
|||
}, |
|||
loading: false, |
|||
noMoreData: false, |
|||
initId: undefined |
|||
} |
|||
}, |
|||
onLoad() { |
|||
this.getTabs() |
|||
}, |
|||
methods: { |
|||
getTabs() { |
|||
this.tabsListArr = [] |
|||
this.tabsList = [] |
|||
apiRoute.teachingResearchLookType().then(res => { |
|||
if (res.code == 1) { |
|||
res.data.forEach(item => { |
|||
let arr = { |
|||
value: item, |
|||
lebal: this.tableTypeName[item] |
|||
} |
|||
this.tabsListArr.push(arr) |
|||
this.tabsList.push(this.tableTypeName[item]) |
|||
}) |
|||
if (this.tabsListArr.length > 0) { |
|||
this.initId = this.tabsListArr[0].value |
|||
this.init() |
|||
} |
|||
} |
|||
}) |
|||
}, |
|||
change(e) { |
|||
const item = this.tabsListArr.find(item => item.lebal == e.name); |
|||
const id = item ? item.value : 0 |
|||
this.initId = id |
|||
this.arrayList = [] |
|||
this.searchArr.page = 1 |
|||
this.init() |
|||
}, |
|||
getTableType(text) { |
|||
return this.tableTypeName[text] |
|||
}, |
|||
info(id) { |
|||
uni.navigateTo({ |
|||
url: '/pages-coach/coach/my/teaching_management_info?id=' + id |
|||
}) |
|||
}, |
|||
onReachBottom() { |
|||
if (this.noMoreData || this.loading) return; |
|||
this.searchArr.page += 1 |
|||
this.init(); |
|||
}, |
|||
init(id) { |
|||
this.searchArr.table_type = this.initId |
|||
apiRoute.teachingResearchList(this.searchArr).then(res => { |
|||
this.loading = true |
|||
if (res.code == 1) { |
|||
this.arrayList.push(...res.data.data) |
|||
if(res.last_page <= this.searchArr.page){ |
|||
this.noMoreData = true; |
|||
} |
|||
this.loading = false |
|||
} |
|||
}) |
|||
}, |
|||
goTake(id,zid) { |
|||
uni.navigateTo({ |
|||
url: '/pages-coach/coach/my/gotake_exam?id=' + id + '&zid=' + zid |
|||
}) |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style lang="scss"> |
|||
.dark-theme { |
|||
background-color: #121212; |
|||
color: #ffffff; |
|||
min-height: 100vh; |
|||
} |
|||
|
|||
.dark-theme .div_style { |
|||
padding: 18rpx 50rpx; |
|||
background-color: #1e1e1e; |
|||
margin-top: 10rpx; |
|||
line-height: 30px; |
|||
border-radius: 8rpx; |
|||
} |
|||
|
|||
.dark-theme .color_style { |
|||
font-size: 30rpx; |
|||
font-weight: bold; |
|||
color: #ffffff; |
|||
} |
|||
|
|||
.dark-theme .color_type_style { |
|||
font-size: 22rpx; |
|||
color: #b0b0b0; |
|||
} |
|||
|
|||
.dark-theme .color_date_style { |
|||
font-size: 22rpx; |
|||
color: #b0b0b0; |
|||
} |
|||
|
|||
.dark-theme .exam-btn { |
|||
color: #7cb9ff; |
|||
} |
|||
|
|||
.dark-theme .loading, |
|||
.dark-theme .no-more { |
|||
color: #b0b0b0; |
|||
text-align: center; |
|||
padding: 20rpx 0; |
|||
} |
|||
|
|||
/* 非暗黑模式样式 */ |
|||
.div_style { |
|||
padding: 18rpx 50rpx; |
|||
background-color: #fff; |
|||
margin-top: 10rpx; |
|||
line-height: 30px; |
|||
} |
|||
|
|||
.dis_style { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
} |
|||
|
|||
.color_style { |
|||
font-size: 30rpx; |
|||
font-weight: bold; |
|||
} |
|||
|
|||
.color_type_style { |
|||
font-size: 22rpx; |
|||
} |
|||
|
|||
.color_date_style { |
|||
font-size: 22rpx; |
|||
} |
|||
|
|||
.exam-btn { |
|||
color: blue; |
|||
} |
|||
|
|||
/* 自定义 tab 栏样式 */ |
|||
.custom-tabs { |
|||
/* 确保背景是黑色 */ |
|||
background-color: #121212 !important; |
|||
|
|||
.fui-tabs__item { |
|||
background-color: #121212 !important; |
|||
color: #00d18c !important; |
|||
} |
|||
|
|||
.fui-tabs__item-active { |
|||
color: #00d18c !important; |
|||
} |
|||
|
|||
.fui-tabs__line { |
|||
background-color: #00d18c !important; |
|||
} |
|||
} |
|||
|
|||
/* 全局样式覆盖 - 使用dart-sass语法 */ |
|||
:deep(.fui-tabs__wrap) { |
|||
background-color: #121212 !important; |
|||
} |
|||
|
|||
:deep(.fui-tabs__item) { |
|||
background-color: #121212 !important; |
|||
color: #00d18c !important; |
|||
} |
|||
|
|||
:deep(.fui-tabs__item-active) { |
|||
color: #00d18c !important; |
|||
} |
|||
|
|||
:deep(.fui-tabs__line) { |
|||
background-color: #00d18c !important; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,229 @@ |
|||
<!--修改密码--> |
|||
<template> |
|||
<view> |
|||
<view class="title"> |
|||
<view :class="{'green-text': tset_style === 1}">1.验证旧密码</view> |
|||
<view :class="{'green-text': tset_style === 2}">2.设置新密码</view> |
|||
</view> |
|||
<view :style="{'background-color':'#fff','width':'100%','height':'100vh' }"> |
|||
<view style="width: 95%;height: 30rpx;"></view> |
|||
<view v-if="tset_style == 1"> |
|||
<view class="describe"> |
|||
为保障您的账号安全,修改密码前请填写原密码 |
|||
</view> |
|||
<view style="width: 95%;margin:30rpx auto;"> |
|||
<fui-input borderTop placeholder="请输入原登录密码" v-model="old_password" |
|||
backgroundColor="#f2f2f2"></fui-input> |
|||
</view> |
|||
</view> |
|||
<view v-if="tset_style == 2"> |
|||
<view style="width: 95%;margin:30rpx auto;"> |
|||
<fui-input borderTop placeholder="请设置6-20位新的登录密码" v-model="formData.new_password" @input="input" |
|||
backgroundColor="#f2f2f2"></fui-input> |
|||
</view> |
|||
<view style="width: 95%;margin: auto;"> |
|||
<fui-input borderTop :padding="['20rpx','32rpx']" v-model="formData.new_password_2" placeholder="请再次输入新的登录密码" @input="input" |
|||
backgroundColor="#f2f2f2"> |
|||
</fui-input> |
|||
</view> |
|||
</view> |
|||
<view style="width: 95%;margin:60rpx auto;"> |
|||
<view class="btn_box"> |
|||
<fui-button |
|||
background="#465cff" |
|||
radius="5rpx" |
|||
@click="nextStep(1)" |
|||
v-if="tset_style != 1">上一步 |
|||
</fui-button> |
|||
|
|||
<fui-button background="#00be8c" radius="5rpx" @click="nextStep(2)" v-if="tset_style == 1">下一步</fui-button> |
|||
|
|||
<fui-button background="#00be8c" radius="5rpx" @click="submit" v-if="tset_style == 2">提交</fui-button> |
|||
</view> |
|||
|
|||
|
|||
<view style="width: 95%;margin:60rpx auto;"> |
|||
<fui-button background="#fff" radius="5rpx" @click="forgot" color="#999999" v-if="tset_style == 1">忘记原密码</fui-button> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
import apiRoute from '@/api/apiRoute.js'; |
|||
|
|||
export default { |
|||
data() { |
|||
return { |
|||
code: '', |
|||
user: '', |
|||
tset_style: 1,//tab栏目|1=验证旧密码页,2=设置新密码页 |
|||
|
|||
old_password:'',//旧密码 |
|||
|
|||
|
|||
formData:{ |
|||
phone:'',//当前登录用户的手机号 |
|||
new_password:'',//新密码 |
|||
new_password_2:'',//二次新密码 |
|||
key_value:'',//修改密码的key_value |
|||
}, |
|||
} |
|||
}, |
|||
onLoad() {}, |
|||
onShow() { |
|||
this.init()//初始化 |
|||
}, |
|||
methods: { |
|||
//初始化 |
|||
async init(){ |
|||
await this.getUserInfo() |
|||
}, |
|||
//获取用户信息 |
|||
async getUserInfo() { |
|||
let res = await apiRoute.getPersonnelInfo({}) |
|||
if (res.code != 1) { |
|||
uni.showToast({ |
|||
title: res.msg, |
|||
icon: 'none' |
|||
}) |
|||
return |
|||
} |
|||
this.formData.phone = res.data.phone//用户手机号 |
|||
}, |
|||
|
|||
//上/下一步切换 |
|||
async nextStep(tset_style){ |
|||
//tset_style|1=第一页,2=第二页 |
|||
if(tset_style == 2){ |
|||
if(!this.old_password){ |
|||
uni.showToast({ |
|||
title: '请输入原登录密码', |
|||
icon: 'none' |
|||
}) |
|||
return |
|||
} |
|||
//验证旧密码 |
|||
let params = { |
|||
old_password: this.old_password |
|||
} |
|||
//验证旧密码是否正确 |
|||
let res = await apiRoute.common_personnelCheckOldPwd(params) |
|||
if(!res.code){ |
|||
uni.showToast({ |
|||
title: res.msg, |
|||
icon: 'none' |
|||
}) |
|||
return |
|||
} |
|||
this.formData.key_value = res.data.key_value |
|||
} |
|||
this.tset_style = Number(tset_style) |
|||
}, |
|||
//忘记密码 |
|||
forgot() { |
|||
uni.navigateTo({ |
|||
url: '/pages/student/login/forgot' |
|||
}) |
|||
}, |
|||
|
|||
//提交 |
|||
async submit() { |
|||
//验证数据 |
|||
if (!this.formData.new_password) { |
|||
uni.showToast({ |
|||
title: '请输入新密码', |
|||
icon: 'none' |
|||
}) |
|||
return |
|||
} |
|||
|
|||
if (this.formData.new_password.length < 6 || this.formData.new_password.length > 20) { |
|||
uni.showToast({ |
|||
title: '新密码长度为6-20位', |
|||
icon: 'none' |
|||
}) |
|||
return |
|||
} |
|||
|
|||
if (!this.formData.new_password) { |
|||
uni.showToast({ |
|||
title: '请输入新密码', |
|||
icon: 'none' |
|||
}) |
|||
return |
|||
} |
|||
//验证两次密码是否一致 |
|||
if (this.formData.new_password != this.formData.new_password_2) { |
|||
uni.showToast({ |
|||
title: '两次密码不一致', |
|||
icon: 'none' |
|||
}) |
|||
return |
|||
} |
|||
|
|||
let res = await apiRoute.common_personnelEdidPassword(this.formData) |
|||
if(!res.code){ |
|||
uni.showToast({ |
|||
title: res.msg, |
|||
icon: 'none' |
|||
}) |
|||
return |
|||
} |
|||
uni.showToast({ |
|||
title: res.msg, |
|||
icon: 'success' |
|||
}) |
|||
//延迟1s执行 |
|||
setTimeout(() => { |
|||
//跳转页面-个人中心 |
|||
//关闭当前页跳转新页面 |
|||
uni.redirectTo({ |
|||
url: `/pages-coach/coach/my/index` |
|||
}) |
|||
}, 1000) |
|||
}, |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style lang="less" scoped> |
|||
page { |
|||
font-weight: normal; |
|||
} |
|||
|
|||
.fui-section__title { |
|||
margin-left: 32rpx; |
|||
} |
|||
|
|||
.fui-left__icon { |
|||
padding-right: 24rpx; |
|||
} |
|||
|
|||
.title { |
|||
display: flex; |
|||
justify-content: space-around; |
|||
align-items: center; |
|||
height: 100rpx; |
|||
width: 100%; |
|||
background-color: #fff; |
|||
font-size: 26rpx; |
|||
border: 4rpx #f5f5f5 solid; |
|||
} |
|||
|
|||
.green-text{ |
|||
color: #36d6b9; |
|||
} |
|||
|
|||
.describe{ |
|||
color: #999999; |
|||
padding-left: 30rpx; |
|||
} |
|||
|
|||
.btn_box{ |
|||
display: flex; |
|||
flex-direction: column; |
|||
gap: 40rpx; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,682 @@ |
|||
<template> |
|||
<view class="add-schedule-container"> |
|||
<view class="form-container"> |
|||
<fui-form> |
|||
<!-- 课程选择 --> |
|||
<fui-form-item label="课程" required> |
|||
<view class="selector-input" @click="showCoursePicker = true"> |
|||
<text>{{ selectedCourse ? selectedCourse.course_name : '请选择课程' }}</text> |
|||
<fui-icon name="arrowdown" :size="32" color="#CCCCCC"></fui-icon> |
|||
</view> |
|||
<single-picker |
|||
:show="showCoursePicker" |
|||
:data="courseOptions" |
|||
valueKey="id" |
|||
textKey="course_name" |
|||
title="选择课程" |
|||
@change="onCourseSelect" |
|||
@cancel="showCoursePicker = false" |
|||
@update:show="showCoursePicker = $event" |
|||
></single-picker> |
|||
</fui-form-item> |
|||
|
|||
<!-- 班级选择 --> |
|||
<fui-form-item label="班级"> |
|||
<view class="selector-input" @click="showClassPicker = true"> |
|||
<text>{{ selectedClass ? selectedClass.class_name : '请选择班级(可选)' }}</text> |
|||
<fui-icon name="arrowdown" :size="32" color="#CCCCCC"></fui-icon> |
|||
</view> |
|||
<single-picker |
|||
:show="showClassPicker" |
|||
:data="classOptions" |
|||
valueKey="id" |
|||
textKey="class_name" |
|||
title="选择班级" |
|||
@change="onClassSelect" |
|||
@cancel="showClassPicker = false" |
|||
@update:show="showClassPicker = $event" |
|||
></single-picker> |
|||
</fui-form-item> |
|||
|
|||
<!-- 教练选择 --> |
|||
<fui-form-item label="授课教练" required> |
|||
<view class="selector-input" @click="showCoachPicker = true"> |
|||
<text>{{ selectedCoach ? selectedCoach.name : '请选择教练' }}</text> |
|||
<fui-icon name="arrowdown" :size="32" color="#CCCCCC"></fui-icon> |
|||
</view> |
|||
<single-picker |
|||
:show="showCoachPicker" |
|||
:data="coachOptions" |
|||
valueKey="id" |
|||
textKey="name" |
|||
title="选择教练" |
|||
@change="onCoachSelect" |
|||
@cancel="showCoachPicker = false" |
|||
@update:show="showCoachPicker = $event" |
|||
></single-picker> |
|||
</fui-form-item> |
|||
|
|||
<!-- 场地选择 --> |
|||
<fui-form-item label="上课场地" required> |
|||
<view class="selector-input" @click="showVenuePicker = true"> |
|||
<text>{{ selectedVenue ? selectedVenue.venue_name : '请选择场地' }}</text> |
|||
<fui-icon name="arrowdown" :size="32" color="#CCCCCC"></fui-icon> |
|||
</view> |
|||
<single-picker |
|||
:show="showVenuePicker" |
|||
:data="venueOptions" |
|||
valueKey="id" |
|||
textKey="venue_name" |
|||
title="选择场地" |
|||
@change="onVenueSelect" |
|||
@cancel="showVenuePicker = false" |
|||
@update:show="showVenuePicker = $event" |
|||
></single-picker> |
|||
</fui-form-item> |
|||
|
|||
<!-- 日期选择 --> |
|||
<fui-form-item label="上课日期" required> |
|||
<view class="selector-input" @click="showDatePicker = true"> |
|||
<text>{{ formData.course_date || '请选择日期' }}</text> |
|||
<fui-icon name="calendar" :size="32" color="#CCCCCC"></fui-icon> |
|||
</view> |
|||
<fui-date-picker |
|||
:show="showDatePicker" |
|||
@confirm="onDateSelect" |
|||
@cancel="showDatePicker = false" |
|||
:value="formData.course_date" |
|||
></fui-date-picker> |
|||
</fui-form-item> |
|||
|
|||
<!-- 时间选择 --> |
|||
<fui-form-item label="上课时间" required> |
|||
<view class="selector-input" @click="showTimePicker = true"> |
|||
<text>{{ formData.time_slot || '请选择时间段' }}</text> |
|||
<fui-icon name="time" :size="32" color="#CCCCCC"></fui-icon> |
|||
</view> |
|||
<single-picker |
|||
:show="showTimePicker" |
|||
:data="timeSlotOptions" |
|||
valueKey="value" |
|||
textKey="text" |
|||
title="选择时间" |
|||
@change="onTimeSelect" |
|||
@cancel="showTimePicker = false" |
|||
@update:show="showTimePicker = $event" |
|||
></single-picker> |
|||
</fui-form-item> |
|||
|
|||
<!-- 容量设置 --> |
|||
<fui-form-item label="课程容量" required> |
|||
<view class="capacity-container"> |
|||
<text class="capacity-text">{{ formData.available_capacity || '0' }}</text> |
|||
<text class="capacity-hint">(根据场地自动设置)</text> |
|||
</view> |
|||
</fui-form-item> |
|||
|
|||
<!-- 备注信息 --> |
|||
<fui-form-item label="备注"> |
|||
<fui-textarea |
|||
:value="formData.remark" |
|||
placeholder="请输入备注信息(可选)" |
|||
@input="formData.remark = $event" |
|||
maxlength="200" |
|||
></fui-textarea> |
|||
</fui-form-item> |
|||
</fui-form> |
|||
|
|||
<!-- 提交按钮 --> |
|||
<view class="btn-container"> |
|||
<fui-button type="primary" @click="submitForm" :loading="submitting">创建课程安排</fui-button> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
import api from '@/api/apiRoute.js'; |
|||
import SinglePicker from '@/components/custom-picker/single-picker.vue'; |
|||
|
|||
export default { |
|||
components: { |
|||
SinglePicker |
|||
}, |
|||
data() { |
|||
return { |
|||
// 表单数据 |
|||
formData: { |
|||
course_id: '', |
|||
class_id: '', |
|||
coach_id: '', |
|||
venue_id: '', |
|||
course_date: '', |
|||
time_slot: '', |
|||
available_capacity: '', |
|||
remark: '' |
|||
}, |
|||
|
|||
// 选择器数据 |
|||
showCoursePicker: false, |
|||
showClassPicker: false, |
|||
showCoachPicker: false, |
|||
showVenuePicker: false, |
|||
showDatePicker: false, |
|||
showTimePicker: false, |
|||
|
|||
// 选项数据 |
|||
courseOptions: [], |
|||
classOptions: [], |
|||
coachOptions: [], |
|||
venueOptions: [], |
|||
timeSlotOptions: [], |
|||
|
|||
// 选中的数据对象 |
|||
selectedCourse: null, |
|||
selectedClass: null, |
|||
selectedCoach: null, |
|||
selectedVenue: null, |
|||
|
|||
// 状态标记 |
|||
submitting: false, |
|||
|
|||
// 预填充数据 |
|||
prefillDate: '', |
|||
prefillTime: '', |
|||
prefillTimeSlot: '' |
|||
}; |
|||
}, |
|||
|
|||
onLoad(options) { |
|||
// 从路由参数获取预填充数据 |
|||
if (options.date) { |
|||
this.prefillDate = options.date; |
|||
this.formData.course_date = options.date; |
|||
} |
|||
|
|||
if (options.time) { |
|||
this.prefillTime = options.time; |
|||
} |
|||
|
|||
if (options.time_slot) { |
|||
this.prefillTimeSlot = options.time_slot; |
|||
this.formData.time_slot = options.time_slot; |
|||
} |
|||
|
|||
// 加载初始数据 |
|||
this.loadFilterOptions(); |
|||
}, |
|||
|
|||
methods: { |
|||
// 返回上一页 |
|||
goBack() { |
|||
uni.navigateBack(); |
|||
}, |
|||
|
|||
// 加载选项数据 |
|||
async loadFilterOptions() { |
|||
uni.showLoading({ |
|||
title: '加载数据中...' |
|||
}); |
|||
|
|||
try { |
|||
// 并行加载所有选项数据 |
|||
const [courseRes, classRes, coachRes, venueRes] = await Promise.all([ |
|||
api.getCourseListForSchedule(), |
|||
api.getClassListForSchedule(), |
|||
api.getCoachListForSchedule(), |
|||
api.getVenueListForSchedule() |
|||
]); |
|||
|
|||
// 设置课程选项 |
|||
if (courseRes.code === 1) { |
|||
this.courseOptions = courseRes.data || []; |
|||
} |
|||
|
|||
// 设置班级选项 |
|||
if (classRes.code === 1) { |
|||
this.classOptions = classRes.data || []; |
|||
} |
|||
|
|||
// 设置教练选项 |
|||
if (coachRes.code === 1) { |
|||
this.coachOptions = coachRes.data || []; |
|||
} |
|||
|
|||
// 设置场地选项 |
|||
if (venueRes.code === 1) { |
|||
this.venueOptions = venueRes.data || []; |
|||
} |
|||
|
|||
// 如果有预填充时间段,设置选中的时间段 |
|||
if (this.prefillTimeSlot) { |
|||
this.formData.time_slot = this.prefillTimeSlot; |
|||
} |
|||
|
|||
console.log('加载的数据:', { |
|||
courses: this.courseOptions.length, |
|||
classes: this.classOptions.length, |
|||
coaches: this.coachOptions.length, |
|||
venues: this.venueOptions.length |
|||
}); |
|||
|
|||
// 检查是否有必要的数据 |
|||
if (this.courseOptions.length === 0) { |
|||
uni.showToast({ |
|||
title: '暂无可用课程', |
|||
icon: 'none' |
|||
}); |
|||
} |
|||
|
|||
if (this.coachOptions.length === 0) { |
|||
uni.showToast({ |
|||
title: '暂无可用教练', |
|||
icon: 'none' |
|||
}); |
|||
} |
|||
|
|||
if (this.venueOptions.length === 0) { |
|||
uni.showToast({ |
|||
title: '暂无可用场地', |
|||
icon: 'none' |
|||
}); |
|||
} |
|||
|
|||
} catch (error) { |
|||
console.error('加载筛选选项失败:', error); |
|||
|
|||
// 提供更详细的错误信息 |
|||
let errorMsg = '加载数据失败'; |
|||
if (error.response) { |
|||
errorMsg = `服务器错误: ${error.response.status}`; |
|||
} else if (error.request) { |
|||
errorMsg = '网络连接失败,请检查网络'; |
|||
} else { |
|||
errorMsg = error.message || '未知错误'; |
|||
} |
|||
|
|||
uni.showToast({ |
|||
title: errorMsg, |
|||
icon: 'none', |
|||
duration: 3000 |
|||
}); |
|||
} finally { |
|||
uni.hideLoading(); |
|||
} |
|||
}, |
|||
|
|||
// 生成时间段选项 |
|||
generateTimeSlotOptions() { |
|||
const timeSlots = []; |
|||
|
|||
// 早上时间段 |
|||
for (let hour = 8; hour < 12; hour++) { |
|||
const startHour = hour.toString().padStart(2, '0'); |
|||
const endHour = (hour + 1).toString().padStart(2, '0'); |
|||
timeSlots.push({ |
|||
value: `${startHour}:00-${endHour}:00`, |
|||
text: `${startHour}:00-${endHour}:00` |
|||
}); |
|||
} |
|||
|
|||
// 下午时间段 |
|||
for (let hour = 12; hour < 18; hour++) { |
|||
const startHour = hour.toString().padStart(2, '0'); |
|||
const endHour = (hour + 1).toString().padStart(2, '0'); |
|||
timeSlots.push({ |
|||
value: `${startHour}:00-${endHour}:00`, |
|||
text: `${startHour}:00-${endHour}:00` |
|||
}); |
|||
} |
|||
|
|||
// 晚上时间段 |
|||
for (let hour = 18; hour < 22; hour++) { |
|||
const startHour = hour.toString().padStart(2, '0'); |
|||
const endHour = (hour + 1).toString().padStart(2, '0'); |
|||
timeSlots.push({ |
|||
value: `${startHour}:00-${endHour}:00`, |
|||
text: `${startHour}:00-${endHour}:00` |
|||
}); |
|||
} |
|||
|
|||
this.timeSlotOptions = timeSlots; |
|||
}, |
|||
|
|||
// 动态加载场地可用时间段 |
|||
async loadTimeSlots() { |
|||
if (!this.formData.venue_id || !this.formData.course_date) { |
|||
return; |
|||
} |
|||
|
|||
try { |
|||
const res = await api.getVenueTimeSlots({ |
|||
venue_id: this.formData.venue_id, |
|||
date: this.formData.course_date |
|||
}); |
|||
|
|||
if (res.code === 1) { |
|||
// 转换API返回的时间段格式为选择器需要的格式 |
|||
this.timeSlotOptions = res.data.map(slot => ({ |
|||
value: slot.time_slot, |
|||
text: slot.time_slot |
|||
})); |
|||
|
|||
console.log('可用时间段:', this.timeSlotOptions); |
|||
} else { |
|||
// 如果API失败,则使用默认时间段 |
|||
this.generateTimeSlotOptions(); |
|||
uni.showToast({ |
|||
title: res.msg || '获取可用时间段失败,使用默认时间段', |
|||
icon: 'none' |
|||
}); |
|||
} |
|||
} catch (error) { |
|||
console.error('加载时间段失败:', error); |
|||
// 如果API失败,则使用默认时间段 |
|||
this.generateTimeSlotOptions(); |
|||
|
|||
let errorMsg = '获取可用时间段失败,使用默认时间段'; |
|||
if (error.response && error.response.status === 404) { |
|||
errorMsg = '该场地暂无可用时间段'; |
|||
} else if (error.request) { |
|||
errorMsg = '网络连接失败,使用默认时间段'; |
|||
} |
|||
|
|||
uni.showToast({ |
|||
title: errorMsg, |
|||
icon: 'none', |
|||
duration: 2000 |
|||
}); |
|||
} |
|||
}, |
|||
|
|||
// 选择器处理方法 |
|||
onCourseSelect(e) { |
|||
console.log('onCourseSelect', e); |
|||
if (e && e.item) { |
|||
this.selectedCourse = e.item; |
|||
this.formData.course_id = e.value; |
|||
} |
|||
}, |
|||
|
|||
onClassSelect(e) { |
|||
console.log('onClassSelect', e); |
|||
if (e && e.item) { |
|||
this.selectedClass = e.item; |
|||
this.formData.class_id = e.value; |
|||
} else { |
|||
this.selectedClass = null; |
|||
this.formData.class_id = ''; |
|||
} |
|||
}, |
|||
|
|||
onCoachSelect(e) { |
|||
console.log('onCoachSelect', e); |
|||
if (e && e.item) { |
|||
this.selectedCoach = e.item; |
|||
this.formData.coach_id = e.value; |
|||
} |
|||
}, |
|||
|
|||
onVenueSelect(e) { |
|||
console.log('onVenueSelect', e); |
|||
if (e && e.item) { |
|||
this.selectedVenue = e.item; |
|||
this.formData.venue_id = e.value; |
|||
|
|||
// 设置容量为场地容量,如果没有场地容量则设为0 |
|||
this.formData.available_capacity = this.selectedVenue.capacity || 0; |
|||
|
|||
// 如果已选择日期,则重新加载时间段 |
|||
if (this.formData.course_date) { |
|||
this.loadTimeSlots(); |
|||
} |
|||
} else { |
|||
// 如果未选择场地,容量设为0 |
|||
this.formData.available_capacity = 0; |
|||
} |
|||
}, |
|||
|
|||
onDateSelect(e) { |
|||
this.formData.course_date = e.result; |
|||
this.showDatePicker = false; |
|||
|
|||
// 如果已选择场地,则重新加载时间段 |
|||
if (this.formData.venue_id) { |
|||
this.loadTimeSlots(); |
|||
} |
|||
}, |
|||
|
|||
onTimeSelect(e) { |
|||
console.log('onTimeSelect', e); |
|||
if (e && e.item) { |
|||
this.formData.time_slot = e.value; |
|||
} |
|||
}, |
|||
|
|||
// 检查教练时间冲突 |
|||
async checkCoachTimeConflict() { |
|||
if (!this.formData.coach_id || !this.formData.course_date || !this.formData.time_slot) { |
|||
return true; // 如果信息不完整,无需检查 |
|||
} |
|||
|
|||
try { |
|||
const res = await api.checkCoachConflict({ |
|||
coach_id: this.formData.coach_id, |
|||
date: this.formData.course_date, |
|||
time_slot: this.formData.time_slot, |
|||
exclude_schedule_id: this.formData.id || 0 // 如果是编辑则排除当前课程 |
|||
}); |
|||
|
|||
if (res.code === 1) { |
|||
if (res.data.has_conflict) { |
|||
uni.showModal({ |
|||
title: '时间冲突提示', |
|||
content: `教练“${this.selectedCoach.name}”在选择的时间段已有其他课程安排,是否仍要继续?`, |
|||
confirmText: '继续添加', |
|||
cancelText: '重新选择', |
|||
success: (modalRes) => { |
|||
if (modalRes.confirm) { |
|||
this.submitForm(true); // 强制继续提交 |
|||
} |
|||
} |
|||
}); |
|||
return false; |
|||
} |
|||
} |
|||
return true; |
|||
} catch (error) { |
|||
console.error('检查教练时间冲突失败:', error); |
|||
return true; // 出错时允许继续,不阻止提交 |
|||
} |
|||
}, |
|||
|
|||
// 表单验证 |
|||
validateForm() { |
|||
if (!this.formData.course_id) { |
|||
uni.showToast({ |
|||
title: '请选择课程', |
|||
icon: 'none' |
|||
}); |
|||
return false; |
|||
} |
|||
|
|||
if (!this.formData.coach_id) { |
|||
uni.showToast({ |
|||
title: '请选择授课教练', |
|||
icon: 'none' |
|||
}); |
|||
return false; |
|||
} |
|||
|
|||
if (!this.formData.venue_id) { |
|||
uni.showToast({ |
|||
title: '请选择上课场地', |
|||
icon: 'none' |
|||
}); |
|||
return false; |
|||
} |
|||
|
|||
if (!this.formData.course_date) { |
|||
uni.showToast({ |
|||
title: '请选择上课日期', |
|||
icon: 'none' |
|||
}); |
|||
return false; |
|||
} |
|||
|
|||
if (!this.formData.time_slot) { |
|||
uni.showToast({ |
|||
title: '请选择上课时间', |
|||
icon: 'none' |
|||
}); |
|||
return false; |
|||
} |
|||
|
|||
return true; |
|||
}, |
|||
|
|||
// 提交表单 |
|||
async submitForm(ignoreConflict = false) { |
|||
if (!this.validateForm()) { |
|||
return; |
|||
} |
|||
|
|||
// 如果未忽略冲突检查,先检查教练时间冲突 |
|||
if (!ignoreConflict) { |
|||
const noConflict = await this.checkCoachTimeConflict(); |
|||
if (!noConflict) { |
|||
return; // 如果有冲突且用户未选择继续,停止提交 |
|||
} |
|||
} |
|||
|
|||
this.submitting = true; |
|||
|
|||
try { |
|||
// 准备提交的数据,确保字段名与API接口匹配 |
|||
const submitData = { |
|||
campus_id: 1, // 默认校区ID,实际项目中应从用户信息获取 |
|||
venue_id: this.formData.venue_id, |
|||
course_date: this.formData.course_date, |
|||
time_slot: this.formData.time_slot, |
|||
course_id: this.formData.course_id, |
|||
coach_id: this.formData.coach_id, |
|||
available_capacity: parseInt(this.formData.available_capacity), |
|||
class_id: this.formData.class_id || 0, // 可选字段 |
|||
remarks: this.formData.remark || '', // 字段名转换 |
|||
created_by: 'manual' |
|||
}; |
|||
|
|||
console.log('提交数据:', submitData); |
|||
|
|||
const res = await api.createCourseSchedule(submitData); |
|||
|
|||
if (res.code === 1) { |
|||
uni.showToast({ |
|||
title: '创建成功', |
|||
icon: 'success' |
|||
}); |
|||
|
|||
// 延迟返回,让用户看到成功提示 |
|||
setTimeout(() => { |
|||
// 返回上一页并发送刷新信号 |
|||
const pages = getCurrentPages(); |
|||
const prevPage = pages[pages.length - 2]; |
|||
if (prevPage && prevPage.$vm && prevPage.$vm.loadScheduleList) { |
|||
// 返回并传递刷新信号 |
|||
uni.navigateBack({ |
|||
success: function() { |
|||
prevPage.$vm.loadScheduleList(); |
|||
} |
|||
}); |
|||
} else { |
|||
uni.navigateBack(); |
|||
} |
|||
}, 1500); |
|||
} else { |
|||
uni.showToast({ |
|||
title: res.msg || '创建失败', |
|||
icon: 'none' |
|||
}); |
|||
} |
|||
} catch (error) { |
|||
console.error('创建课程安排失败:', error); |
|||
|
|||
// 提供更详细的错误信息 |
|||
let errorMsg = '创建失败,请重试'; |
|||
if (error.response) { |
|||
if (error.response.status === 401) { |
|||
errorMsg = '登录已过期,请重新登录'; |
|||
} else if (error.response.status === 403) { |
|||
errorMsg = '没有权限执行此操作'; |
|||
} else if (error.response.status >= 500) { |
|||
errorMsg = '服务器内部错误,请联系管理员'; |
|||
} else { |
|||
errorMsg = `请求失败: ${error.response.status}`; |
|||
} |
|||
} else if (error.request) { |
|||
errorMsg = '网络连接失败,请检查网络后重试'; |
|||
} else { |
|||
errorMsg = error.message || '创建失败,请重试'; |
|||
} |
|||
|
|||
uni.showToast({ |
|||
title: errorMsg, |
|||
icon: 'none', |
|||
duration: 3000 |
|||
}); |
|||
} finally { |
|||
this.submitting = false; |
|||
} |
|||
} |
|||
} |
|||
}; |
|||
</script> |
|||
|
|||
<style lang="scss" scoped> |
|||
.add-schedule-container { |
|||
min-height: 100vh; |
|||
background-color: #18181c; |
|||
} |
|||
|
|||
.form-container { |
|||
padding: 30rpx; |
|||
} |
|||
|
|||
.selector-input { |
|||
height: 80rpx; |
|||
background-color: #23232a; |
|||
border-radius: 8rpx; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: space-between; |
|||
padding: 0 24rpx; |
|||
font-size: 28rpx; |
|||
color: #fff; |
|||
} |
|||
|
|||
.capacity-container { |
|||
height: 80rpx; |
|||
background-color: #23232a; |
|||
border-radius: 8rpx; |
|||
display: flex; |
|||
align-items: center; |
|||
padding: 0 24rpx; |
|||
font-size: 28rpx; |
|||
} |
|||
|
|||
.capacity-text { |
|||
color: #fff; |
|||
font-size: 28rpx; |
|||
} |
|||
|
|||
.capacity-hint { |
|||
color: #999999; |
|||
font-size: 24rpx; |
|||
margin-left: 16rpx; |
|||
} |
|||
|
|||
.btn-container { |
|||
margin-top: 60rpx; |
|||
padding: 0 30rpx; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,583 @@ |
|||
<template> |
|||
<view class="adjust-course-container"> |
|||
<view class="form-container"> |
|||
<view v-if="loading" class="loading-container"> |
|||
<fui-loading></fui-loading> |
|||
<text class="loading-text">加载中...</text> |
|||
</view> |
|||
|
|||
<fui-form v-else> |
|||
<!-- 课程信息 --> |
|||
<view class="section-title">当前课程信息</view> |
|||
<view class="course-info-card"> |
|||
<view class="info-row"> |
|||
<text class="info-label">课程名称:</text> |
|||
<text class="info-value">{{ scheduleInfo.course_name }}</text> |
|||
</view> |
|||
<view class="info-row"> |
|||
<text class="info-label">上课日期:</text> |
|||
<text class="info-value">{{ scheduleInfo.course_date }}</text> |
|||
</view> |
|||
<view class="info-row"> |
|||
<text class="info-label">上课时间:</text> |
|||
<text class="info-value">{{ scheduleInfo.time_slot }}</text> |
|||
</view> |
|||
<view class="info-row"> |
|||
<text class="info-label">授课教练:</text> |
|||
<text class="info-value">{{ scheduleInfo.coach_name }}</text> |
|||
</view> |
|||
<view class="info-row"> |
|||
<text class="info-label">上课场地:</text> |
|||
<text class="info-value">{{ scheduleInfo.venue_name }}</text> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="section-title">调整后信息</view> |
|||
|
|||
<!-- 教练选择 --> |
|||
<fui-form-item label="授课教练"> |
|||
<picker |
|||
:value="coachPickerIndex" |
|||
:range="coachOptions" |
|||
:range-key="'name'" |
|||
@change="onCoachSelect" |
|||
> |
|||
<view class="selector-input"> |
|||
<text>{{ selectedCoach ? selectedCoach.name : scheduleInfo.coach_name }}</text> |
|||
<fui-icon name="arrowdown" :size="32" color="#CCCCCC"></fui-icon> |
|||
</view> |
|||
</picker> |
|||
</fui-form-item> |
|||
|
|||
<!-- 场地选择 --> |
|||
<fui-form-item label="上课场地"> |
|||
<picker |
|||
:value="venuePickerIndex" |
|||
:range="venueOptions" |
|||
:range-key="'venue_name'" |
|||
@change="onVenueSelect" |
|||
> |
|||
<view class="selector-input"> |
|||
<text>{{ selectedVenue ? selectedVenue.venue_name : scheduleInfo.venue_name }}</text> |
|||
<fui-icon name="arrowdown" :size="32" color="#CCCCCC"></fui-icon> |
|||
</view> |
|||
</picker> |
|||
</fui-form-item> |
|||
|
|||
<!-- 日期选择 --> |
|||
<fui-form-item label="上课日期"> |
|||
<picker |
|||
mode="date" |
|||
:value="formData.course_date || scheduleInfo.course_date || getCurrentDate()" |
|||
:start="getMinDate()" |
|||
:end="getMaxDate()" |
|||
@change="onDateSelect" |
|||
> |
|||
<view class="selector-input"> |
|||
<text>{{ formData.course_date || scheduleInfo.course_date }}</text> |
|||
<fui-icon name="calendar" :size="32" color="#CCCCCC"></fui-icon> |
|||
</view> |
|||
</picker> |
|||
</fui-form-item> |
|||
|
|||
<!-- 时间选择 --> |
|||
<fui-form-item label="上课时间"> |
|||
<picker |
|||
:value="timePickerIndex" |
|||
:range="timeSlotOptions" |
|||
:range-key="'text'" |
|||
@change="onTimeSelect" |
|||
> |
|||
<view class="selector-input"> |
|||
<text>{{ formData.time_slot || scheduleInfo.time_slot }}</text> |
|||
<fui-icon name="time" :size="32" color="#CCCCCC"></fui-icon> |
|||
</view> |
|||
</picker> |
|||
</fui-form-item> |
|||
|
|||
<!-- 容量设置 --> |
|||
<fui-form-item label="课程容量"> |
|||
<fui-input |
|||
type="number" |
|||
:value="formData.available_capacity || scheduleInfo.available_capacity" |
|||
placeholder="请输入课程容量" |
|||
@input="formData.available_capacity = $event" |
|||
></fui-input> |
|||
</fui-form-item> |
|||
|
|||
|
|||
<!-- 提交按钮 --> |
|||
<view class="btn-container"> |
|||
<fui-button type="primary" @click="submitForm" :loading="submitting">确认调整</fui-button> |
|||
</view> |
|||
</fui-form> |
|||
</view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
import api from '@/api/apiRoute.js'; |
|||
|
|||
export default { |
|||
data() { |
|||
return { |
|||
// 状态标记 |
|||
loading: true, |
|||
submitting: false, |
|||
|
|||
// 课程ID |
|||
scheduleId: null, |
|||
|
|||
// 课程信息 |
|||
scheduleInfo: {}, |
|||
|
|||
// 表单数据 |
|||
formData: { |
|||
schedule_id: '', |
|||
coach_id: '', |
|||
venue_id: '', |
|||
course_date: '', |
|||
time_slot: '', |
|||
available_capacity: '' |
|||
}, |
|||
|
|||
// 移除不再需要的showDatePicker |
|||
|
|||
// 选项数据 |
|||
coachOptions: [], |
|||
venueOptions: [], |
|||
timeSlotOptions: [], |
|||
|
|||
// 选中的数据对象 |
|||
selectedCoach: null, |
|||
selectedVenue: null, |
|||
|
|||
// picker索引 |
|||
coachPickerIndex: 0, |
|||
venuePickerIndex: 0, |
|||
timePickerIndex: 0 |
|||
}; |
|||
}, |
|||
|
|||
onLoad(options) { |
|||
if (options.id) { |
|||
this.scheduleId = options.id; |
|||
this.formData.schedule_id = options.id; |
|||
this.loadScheduleInfo(); |
|||
this.loadFilterOptions(); |
|||
} else { |
|||
uni.showToast({ |
|||
title: '参数错误', |
|||
icon: 'none' |
|||
}); |
|||
setTimeout(() => { |
|||
uni.navigateBack(); |
|||
}, 1500); |
|||
} |
|||
}, |
|||
|
|||
methods: { |
|||
// 返回上一页 |
|||
goBack() { |
|||
uni.navigateBack(); |
|||
}, |
|||
|
|||
// 加载课程安排信息 |
|||
async loadScheduleInfo() { |
|||
try { |
|||
const res = await api.getCourseScheduleInfo({ schedule_id: this.scheduleId }); |
|||
|
|||
if (res.code === 1) { |
|||
this.scheduleInfo = res.data; |
|||
|
|||
// 初始化表单数据 |
|||
this.formData.coach_id = this.scheduleInfo.coach_id; |
|||
this.formData.venue_id = this.scheduleInfo.venue_id; |
|||
this.formData.course_date = this.scheduleInfo.course_date; |
|||
this.formData.time_slot = this.scheduleInfo.time_slot; |
|||
this.formData.available_capacity = this.scheduleInfo.available_capacity; |
|||
} else { |
|||
uni.showToast({ |
|||
title: res.msg || '获取课程安排信息失败', |
|||
icon: 'none' |
|||
}); |
|||
} |
|||
} catch (error) { |
|||
console.error('获取课程安排信息失败:', error); |
|||
uni.showToast({ |
|||
title: '获取课程安排信息失败', |
|||
icon: 'none' |
|||
}); |
|||
} |
|||
}, |
|||
|
|||
// 加载选项数据 |
|||
async loadFilterOptions() { |
|||
try { |
|||
const res = await api.getCourseScheduleFilterOptions(); |
|||
|
|||
if (res.code === 1) { |
|||
// 设置教练选项 |
|||
this.coachOptions = res.data.coaches || []; |
|||
|
|||
// 设置场地选项 |
|||
this.venueOptions = res.data.venues || []; |
|||
|
|||
// 生成时间段选项 |
|||
this.generateTimeSlotOptions(); |
|||
|
|||
// 找到当前选中的教练和场地 |
|||
this.findSelectedOptions(); |
|||
} else { |
|||
uni.showToast({ |
|||
title: res.msg || '加载筛选选项失败', |
|||
icon: 'none' |
|||
}); |
|||
} |
|||
} catch (error) { |
|||
console.error('加载筛选选项失败:', error); |
|||
uni.showToast({ |
|||
title: '加载筛选选项失败', |
|||
icon: 'none' |
|||
}); |
|||
} finally { |
|||
this.loading = false; |
|||
} |
|||
}, |
|||
|
|||
// 查找当前选中的选项 |
|||
findSelectedOptions() { |
|||
// 查找当前教练 |
|||
if (this.scheduleInfo.coach_id) { |
|||
this.selectedCoach = this.coachOptions.find(coach => coach.id === this.scheduleInfo.coach_id); |
|||
this.coachPickerIndex = this.coachOptions.findIndex(coach => coach.id === this.scheduleInfo.coach_id); |
|||
if (this.coachPickerIndex === -1) this.coachPickerIndex = 0; |
|||
} |
|||
|
|||
// 查找当前场地 |
|||
if (this.scheduleInfo.venue_id) { |
|||
this.selectedVenue = this.venueOptions.find(venue => venue.id === this.scheduleInfo.venue_id); |
|||
this.venuePickerIndex = this.venueOptions.findIndex(venue => venue.id === this.scheduleInfo.venue_id); |
|||
if (this.venuePickerIndex === -1) this.venuePickerIndex = 0; |
|||
} |
|||
|
|||
// 查找当前时间段 |
|||
if (this.scheduleInfo.time_slot && this.timeSlotOptions.length > 0) { |
|||
this.timePickerIndex = this.timeSlotOptions.findIndex(time => time.value === this.scheduleInfo.time_slot); |
|||
if (this.timePickerIndex === -1) this.timePickerIndex = 0; |
|||
} |
|||
}, |
|||
|
|||
// 生成时间段选项(保留原有方法作为备用) |
|||
generateTimeSlotOptions() { |
|||
// 使用新的默认时间选项生成方法 |
|||
this.generateDefaultTimeOptions(); |
|||
}, |
|||
|
|||
// 选择器事件处理 |
|||
onCoachSelect(e) { |
|||
const index = e.detail.value; |
|||
this.coachPickerIndex = index; |
|||
if (index >= 0 && index < this.coachOptions.length) { |
|||
this.selectedCoach = this.coachOptions[index]; |
|||
this.formData.coach_id = this.selectedCoach.id; |
|||
} |
|||
}, |
|||
|
|||
onVenueSelect(e) { |
|||
const index = e.detail.value; |
|||
this.venuePickerIndex = index; |
|||
if (index >= 0 && index < this.venueOptions.length) { |
|||
this.selectedVenue = this.venueOptions[index]; |
|||
this.formData.venue_id = this.selectedVenue.id; |
|||
|
|||
// 自动填充场地容量 |
|||
if (this.selectedVenue.capacity) { |
|||
this.formData.available_capacity = this.selectedVenue.capacity; |
|||
} |
|||
|
|||
// 获取该场地的时间选项 |
|||
this.loadVenueTimeOptions(this.selectedVenue.id); |
|||
} |
|||
}, |
|||
|
|||
onDateSelect(e) { |
|||
this.formData.course_date = e.detail.value; |
|||
}, |
|||
|
|||
onTimeSelect(e) { |
|||
const index = e.detail.value; |
|||
this.timePickerIndex = index; |
|||
if (index >= 0 && index < this.timeSlotOptions.length) { |
|||
this.formData.time_slot = this.timeSlotOptions[index].value; |
|||
} |
|||
}, |
|||
|
|||
// 表单验证 |
|||
validateForm() { |
|||
// 检查是否有任何修改 |
|||
const hasChanges = this.formData.coach_id !== this.scheduleInfo.coach_id || |
|||
this.formData.venue_id !== this.scheduleInfo.venue_id || |
|||
this.formData.course_date !== this.scheduleInfo.course_date || |
|||
this.formData.time_slot !== this.scheduleInfo.time_slot || |
|||
this.formData.available_capacity !== this.scheduleInfo.available_capacity; |
|||
|
|||
if (!hasChanges) { |
|||
uni.showToast({ |
|||
title: '未进行任何修改', |
|||
icon: 'none' |
|||
}); |
|||
return false; |
|||
} |
|||
|
|||
|
|||
return true; |
|||
}, |
|||
|
|||
// 获取场地时间选项 |
|||
async loadVenueTimeOptions(venueId) { |
|||
if (!venueId) { |
|||
// 如果没有选择场地,使用默认时间选项 |
|||
this.generateDefaultTimeOptions(); |
|||
return; |
|||
} |
|||
|
|||
try { |
|||
const res = await api.getVenueTimeOptions({ venue_id: venueId }); |
|||
|
|||
if (res.code === 1) { |
|||
this.timeSlotOptions = res.data.time_options || []; |
|||
// 更新时间picker索引 |
|||
this.updateTimePickerIndex(); |
|||
} else { |
|||
console.error('获取场地时间选项失败:', res.msg); |
|||
// 如果获取失败,使用默认时间选项 |
|||
this.generateDefaultTimeOptions(); |
|||
this.updateTimePickerIndex(); |
|||
} |
|||
} catch (error) { |
|||
console.error('获取场地时间选项失败:', error); |
|||
// 如果获取失败,使用默认时间选项 |
|||
this.generateDefaultTimeOptions(); |
|||
this.updateTimePickerIndex(); |
|||
} |
|||
}, |
|||
|
|||
// 生成默认时间选项(8:30开始,每小时一档) |
|||
generateDefaultTimeOptions() { |
|||
const timeSlots = []; |
|||
|
|||
for (let hour = 8; hour < 22; hour++) { |
|||
const minute = (hour === 8) ? '30' : '00'; // 8:30开始 |
|||
const startHour = hour.toString().padStart(2, '0'); |
|||
const endHour = (hour + 1).toString().padStart(2, '0'); |
|||
const startTime = `${startHour}:${minute}`; |
|||
const endTime = `${endHour}:${minute}`; |
|||
|
|||
timeSlots.push({ |
|||
value: `${startTime}-${endTime}`, |
|||
text: `${startTime}-${endTime}` |
|||
}); |
|||
} |
|||
|
|||
this.timeSlotOptions = timeSlots; |
|||
}, |
|||
|
|||
// 更新时间picker索引 |
|||
updateTimePickerIndex() { |
|||
if (this.formData.time_slot && this.timeSlotOptions.length > 0) { |
|||
this.timePickerIndex = this.timeSlotOptions.findIndex(time => time.value === this.formData.time_slot); |
|||
if (this.timePickerIndex === -1) this.timePickerIndex = 0; |
|||
} |
|||
}, |
|||
|
|||
// 获取最小日期(当前日期) |
|||
getMinDate() { |
|||
const today = new Date(); |
|||
const year = today.getFullYear(); |
|||
const month = (today.getMonth() + 1).toString().padStart(2, '0'); |
|||
const day = today.getDate().toString().padStart(2, '0'); |
|||
return `${year}-${month}-${day}`; |
|||
}, |
|||
|
|||
// 获取最大日期(一年后) |
|||
getMaxDate() { |
|||
const nextYear = new Date(); |
|||
nextYear.setFullYear(nextYear.getFullYear() + 1); |
|||
const year = nextYear.getFullYear(); |
|||
const month = (nextYear.getMonth() + 1).toString().padStart(2, '0'); |
|||
const day = nextYear.getDate().toString().padStart(2, '0'); |
|||
return `${year}-${month}-${day}`; |
|||
}, |
|||
|
|||
// 获取当前日期作为默认值 |
|||
getCurrentDate() { |
|||
const today = new Date(); |
|||
const year = today.getFullYear(); |
|||
const month = (today.getMonth() + 1).toString().padStart(2, '0'); |
|||
const day = today.getDate().toString().padStart(2, '0'); |
|||
return `${year}-${month}-${day}`; |
|||
}, |
|||
|
|||
// 提交表单 |
|||
async submitForm() { |
|||
if (!this.validateForm()) { |
|||
return; |
|||
} |
|||
|
|||
this.submitting = true; |
|||
|
|||
try { |
|||
const res = await api.updateCourseSchedule(this.formData); |
|||
|
|||
if (res.code === 1) { |
|||
uni.showToast({ |
|||
title: '调整成功', |
|||
icon: 'success' |
|||
}); |
|||
|
|||
// 延迟返回,让用户看到成功提示 |
|||
setTimeout(() => { |
|||
uni.navigateBack(); |
|||
}, 1500); |
|||
} else { |
|||
uni.showToast({ |
|||
title: res.msg || '调整失败', |
|||
icon: 'none' |
|||
}); |
|||
} |
|||
} catch (error) { |
|||
console.error('调整课程安排失败:', error); |
|||
uni.showToast({ |
|||
title: '调整失败,请重试', |
|||
icon: 'none' |
|||
}); |
|||
} finally { |
|||
this.submitting = false; |
|||
} |
|||
} |
|||
} |
|||
}; |
|||
</script> |
|||
|
|||
<style lang="scss" scoped> |
|||
.adjust-course-container { |
|||
min-height: 100vh; |
|||
background-color: #18181c; |
|||
} |
|||
|
|||
.form-container { |
|||
padding: 30rpx; |
|||
} |
|||
|
|||
.loading-container { |
|||
height: 200rpx; |
|||
display: flex; |
|||
flex-direction: column; |
|||
justify-content: center; |
|||
align-items: center; |
|||
} |
|||
|
|||
.loading-text { |
|||
margin-top: 20rpx; |
|||
font-size: 28rpx; |
|||
color: #999; |
|||
} |
|||
|
|||
.section-title { |
|||
font-size: 32rpx; |
|||
font-weight: bold; |
|||
color: #29d3b4; |
|||
margin: 30rpx 0 20rpx; |
|||
padding-bottom: 10rpx; |
|||
border-bottom: 1px solid #333; |
|||
} |
|||
|
|||
.course-info-card { |
|||
background-color: #23232a; |
|||
border-radius: 12rpx; |
|||
padding: 20rpx; |
|||
margin-bottom: 30rpx; |
|||
} |
|||
|
|||
.info-row { |
|||
display: flex; |
|||
margin-bottom: 16rpx; |
|||
font-size: 28rpx; |
|||
} |
|||
|
|||
.info-label { |
|||
color: #999; |
|||
width: 160rpx; |
|||
flex-shrink: 0; |
|||
} |
|||
|
|||
.info-value { |
|||
color: #fff; |
|||
flex: 1; |
|||
} |
|||
|
|||
.selector-input { |
|||
height: 80rpx; |
|||
background-color: #23232a; |
|||
border-radius: 8rpx; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: space-between; |
|||
padding: 0 24rpx; |
|||
font-size: 28rpx; |
|||
color: #fff; |
|||
} |
|||
|
|||
.btn-container { |
|||
margin-top: 60rpx; |
|||
padding: 0 30rpx; |
|||
} |
|||
|
|||
/* Picker样式 */ |
|||
.picker-mask { |
|||
position: fixed; |
|||
top: 0; |
|||
left: 0; |
|||
right: 0; |
|||
bottom: 0; |
|||
background-color: rgba(0, 0, 0, 0.5); |
|||
z-index: 9999; |
|||
display: flex; |
|||
align-items: flex-end; |
|||
} |
|||
|
|||
.picker-content { |
|||
width: 100%; |
|||
background-color: #23232a; |
|||
border-radius: 20rpx 20rpx 0 0; |
|||
max-height: 80vh; |
|||
} |
|||
|
|||
.picker-header { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
padding: 30rpx; |
|||
border-bottom: 1px solid #333; |
|||
} |
|||
|
|||
.picker-cancel, .picker-confirm { |
|||
font-size: 28rpx; |
|||
color: #29d3b4; |
|||
} |
|||
|
|||
.picker-title { |
|||
font-size: 32rpx; |
|||
color: #fff; |
|||
font-weight: bold; |
|||
} |
|||
|
|||
.picker-item { |
|||
height: 80rpx; |
|||
line-height: 80rpx; |
|||
text-align: center; |
|||
font-size: 28rpx; |
|||
color: #fff; |
|||
} |
|||
</style> |
|||
File diff suppressed because it is too large
@ -0,0 +1,521 @@ |
|||
<template> |
|||
<view class="sign-in-container"> |
|||
<uni-nav-bar title="课程点名" left-icon="left" fixed="true" background-color="#292929" color="#FFFFFF" |
|||
@clickLeft="goBack"></uni-nav-bar> |
|||
|
|||
<view class="content"> |
|||
<!-- 课程信息 --> |
|||
<view class="course-info-card" v-if="scheduleInfo"> |
|||
<view class="course-title">{{ scheduleInfo.course_name }}</view> |
|||
<view class="course-time">{{ scheduleInfo.course_date }} {{ scheduleInfo.time_slot }}</view> |
|||
<view class="course-detail"> |
|||
<view class="detail-item"> |
|||
<text class="detail-label">授课教练:</text> |
|||
<text class="detail-value">{{ scheduleInfo.coach_name }}</text> |
|||
</view> |
|||
<view class="detail-item"> |
|||
<text class="detail-label">上课场地:</text> |
|||
<text class="detail-value">{{ scheduleInfo.venue_name }}</text> |
|||
</view> |
|||
<view class="detail-item"> |
|||
<text class="detail-label">学员人数:</text> |
|||
<text class="detail-value">{{ studentList.length }}人</text> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 学员列表 --> |
|||
<view class="student-section"> |
|||
<view class="section-header"> |
|||
<view class="section-title">学员点名</view> |
|||
<view class="action-buttons"> |
|||
<fui-button type="primary" size="small" @click="checkAllStudents">全部签到</fui-button> |
|||
<fui-button type="danger" size="small" @click="uncheckAllStudents">全部取消</fui-button> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="empty-list" v-if="studentList.length === 0"> |
|||
<image :src="$util.img('/static/icon-img/empty.png')" mode="aspectFit" class="empty-img"></image> |
|||
<text class="empty-text">暂无学员数据</text> |
|||
</view> |
|||
|
|||
<view class="student-list" v-else> |
|||
<view class="student-item" v-for="(student, index) in studentList" :key="index" |
|||
@click="toggleStudentStatus(index)"> |
|||
<view class="student-avatar"> |
|||
<image :src="student.avatar || $util.img('/static/icon-img/avatar.png')" mode="aspectFill"></image> |
|||
<view :class="['status-badge',student.statusClass]"></view> |
|||
</view> |
|||
|
|||
<view class="student-info"> |
|||
<text class="student-name">{{ student.name }}</text> |
|||
<text class="student-phone">{{ student.phone_number || '无联系电话' }}</text> |
|||
</view> |
|||
|
|||
<view class="status-container"> |
|||
<view class="status-select"> |
|||
<view class="status-option" :class="{ active: student.status === 1 }" |
|||
@click.stop="setStudentStatus(index, 1)"> |
|||
已到 |
|||
</view> |
|||
<view class="status-option" :class="{ active: student.status === 2 }" |
|||
@click.stop="setStudentStatus(index, 2)"> |
|||
请假 |
|||
</view> |
|||
<view class="status-option" :class="{ active: student.status === 0 }" |
|||
@click.stop="setStudentStatus(index, 0)"> |
|||
未到 |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 点名备注 --> |
|||
<view class="remark-section"> |
|||
<view class="section-title">点名备注</view> |
|||
<fui-textarea v-model="signInRemark" placeholder="请输入点名备注(可选)" maxlength="200"></fui-textarea> |
|||
</view> |
|||
|
|||
<!-- 提交按钮 --> |
|||
<view class="submit-btn"> |
|||
<fui-button type="primary" @click="submitSignIn" :loading="submitting">提交点名</fui-button> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
import api from '@/api/apiRoute.js'; |
|||
|
|||
export default { |
|||
data() { |
|||
return { |
|||
// 课程ID |
|||
scheduleId: null, |
|||
|
|||
// 课程信息 |
|||
scheduleInfo: null, |
|||
|
|||
// 学员列表 |
|||
studentList: [], |
|||
|
|||
// 点名备注 |
|||
signInRemark: '', |
|||
|
|||
// 提交状态 |
|||
submitting: false |
|||
}; |
|||
}, |
|||
computed: { |
|||
statusClass() { |
|||
const statusMap = { |
|||
'pending': 'status-pending', |
|||
'upcoming': 'status-upcoming', |
|||
'ongoing': 'status-ongoing', |
|||
'completed': 'status-completed' |
|||
}; |
|||
return statusMap[this.scheduleInfo.status] || ''; |
|||
}, |
|||
studentList() { |
|||
const statusMap = { |
|||
0: 'status-absent', |
|||
1: 'status-present', |
|||
2: 'status-leave' |
|||
}; |
|||
|
|||
return this.studentListRaw.map(student => ({ |
|||
...student, |
|||
statusClass: statusMap[student.status] || 'status-absent', |
|||
status_text: this.getStatusText(student.status) |
|||
})); |
|||
} |
|||
}, |
|||
onLoad(options) { |
|||
if (options.id) { |
|||
this.scheduleId = options.id; |
|||
this.loadScheduleInfo(); |
|||
} else { |
|||
uni.showToast({ |
|||
title: '参数错误', |
|||
icon: 'none' |
|||
}); |
|||
setTimeout(() => { |
|||
uni.navigateBack(); |
|||
}, 1500); |
|||
} |
|||
}, |
|||
|
|||
methods: { |
|||
// 返回上一页 |
|||
goBack() { |
|||
uni.navigateBack(); |
|||
}, |
|||
|
|||
// 加载课程安排信息 |
|||
async loadScheduleInfo() { |
|||
uni.showLoading({ |
|||
title: '加载中...' |
|||
}); |
|||
|
|||
try { |
|||
const res = await api.getCourseScheduleInfo({ |
|||
schedule_id: this.scheduleId |
|||
}); |
|||
|
|||
if (res.code === 1) { |
|||
this.scheduleInfo = res.data; |
|||
|
|||
// 处理学员列表 |
|||
if (this.scheduleInfo.students && this.scheduleInfo.students.length > 0) { |
|||
this.studentList = [...this.scheduleInfo.students]; |
|||
} |
|||
} else { |
|||
uni.showToast({ |
|||
title: res.msg || '获取课程安排信息失败', |
|||
icon: 'none' |
|||
}); |
|||
} |
|||
} catch (error) { |
|||
console.error('获取课程安排信息失败:', error); |
|||
uni.showToast({ |
|||
title: '获取课程安排信息失败', |
|||
icon: 'none' |
|||
}); |
|||
} finally { |
|||
uni.hideLoading(); |
|||
} |
|||
}, |
|||
|
|||
// 获取学员状态样式 |
|||
getStatusClass(status) { |
|||
const statusMap = { |
|||
0: 'status-absent', |
|||
1: 'status-present', |
|||
2: 'status-leave' |
|||
}; |
|||
|
|||
return statusMap[status] || 'status-absent'; |
|||
}, |
|||
// 获取状态文本 |
|||
getStatusText(status) { |
|||
const statusTextMap = { |
|||
0: '待上课', |
|||
1: '已上课', |
|||
2: '请假' |
|||
}; |
|||
return statusTextMap[status] || '未知状态'; |
|||
}, |
|||
// 切换学员状态 |
|||
toggleStudentStatus(index) { |
|||
const student = this.studentList[index]; |
|||
|
|||
// 状态循环:未到 -> 已到 -> 请假 -> 未到 |
|||
let newStatus = 0; |
|||
|
|||
if (student.status === 0) { |
|||
newStatus = 1; |
|||
} else if (student.status === 1) { |
|||
newStatus = 2; |
|||
} else { |
|||
newStatus = 0; |
|||
} |
|||
|
|||
this.setStudentStatus(index, newStatus); |
|||
}, |
|||
|
|||
// 设置学员状态 |
|||
setStudentStatus(index, status) { |
|||
if (index >= 0 && index < this.studentList.length) { |
|||
this.studentList[index].status = status; |
|||
|
|||
// 更新状态文本 |
|||
const statusTextMap = { |
|||
0: '待上课', |
|||
1: '已上课', |
|||
2: '请假' |
|||
}; |
|||
|
|||
this.studentList[index].status_text = statusTextMap[status]; |
|||
} |
|||
}, |
|||
|
|||
// 全部签到 |
|||
checkAllStudents() { |
|||
this.studentList.forEach((student, index) => { |
|||
this.setStudentStatus(index, 1); |
|||
}); |
|||
}, |
|||
|
|||
// 全部取消 |
|||
uncheckAllStudents() { |
|||
this.studentList.forEach((student, index) => { |
|||
this.setStudentStatus(index, 0); |
|||
}); |
|||
}, |
|||
|
|||
// 提交点名 |
|||
async submitSignIn() { |
|||
// 准备提交数据 |
|||
const studentData = this.studentList.map(student => ({ |
|||
student_id: student.student_id, |
|||
resource_id: student.resource_id, |
|||
status: student.status |
|||
})); |
|||
|
|||
const submitData = { |
|||
schedule_id: this.scheduleId, |
|||
students: studentData, |
|||
remark: this.signInRemark |
|||
}; |
|||
|
|||
this.submitting = true; |
|||
|
|||
try { |
|||
// 使用API进行点名 |
|||
const res = await api.submitScheduleSignIn(submitData); |
|||
|
|||
if (res.code === 1) { |
|||
uni.showToast({ |
|||
title: '点名成功', |
|||
icon: 'success' |
|||
}); |
|||
|
|||
// 延迟返回 |
|||
setTimeout(() => { |
|||
uni.navigateBack(); |
|||
}, 1500); |
|||
} else { |
|||
uni.showToast({ |
|||
title: res.msg || '点名失败', |
|||
icon: 'none' |
|||
}); |
|||
} |
|||
} catch (error) { |
|||
console.error('点名失败:', error); |
|||
uni.showToast({ |
|||
title: '点名失败,请重试', |
|||
icon: 'none' |
|||
}); |
|||
} finally { |
|||
this.submitting = false; |
|||
} |
|||
} |
|||
} |
|||
}; |
|||
</script> |
|||
|
|||
<style lang="scss" scoped> |
|||
.sign-in-container { |
|||
min-height: 100vh; |
|||
background-color: #18181c; |
|||
padding-top: 88rpx; |
|||
} |
|||
|
|||
.content { |
|||
padding: 30rpx; |
|||
} |
|||
|
|||
.course-info-card { |
|||
background-color: #23232a; |
|||
border-radius: 12rpx; |
|||
padding: 24rpx; |
|||
margin-bottom: 30rpx; |
|||
} |
|||
|
|||
.course-title { |
|||
font-size: 32rpx; |
|||
font-weight: bold; |
|||
color: #fff; |
|||
margin-bottom: 10rpx; |
|||
} |
|||
|
|||
.course-time { |
|||
font-size: 26rpx; |
|||
color: #29d3b4; |
|||
margin-bottom: 20rpx; |
|||
} |
|||
|
|||
.course-detail { |
|||
background-color: #2a2a2a; |
|||
border-radius: 8rpx; |
|||
padding: 16rpx; |
|||
} |
|||
|
|||
.detail-item { |
|||
display: flex; |
|||
margin-bottom: 10rpx; |
|||
font-size: 26rpx; |
|||
|
|||
&:last-child { |
|||
margin-bottom: 0; |
|||
} |
|||
} |
|||
|
|||
.detail-label { |
|||
color: #999; |
|||
width: 140rpx; |
|||
} |
|||
|
|||
.detail-value { |
|||
color: #fff; |
|||
flex: 1; |
|||
} |
|||
|
|||
.student-section { |
|||
background-color: #23232a; |
|||
border-radius: 12rpx; |
|||
padding: 24rpx; |
|||
margin-bottom: 30rpx; |
|||
} |
|||
|
|||
.section-header { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
margin-bottom: 24rpx; |
|||
} |
|||
|
|||
.section-title { |
|||
font-size: 30rpx; |
|||
font-weight: bold; |
|||
color: #fff; |
|||
} |
|||
|
|||
.action-buttons { |
|||
display: flex; |
|||
gap: 16rpx; |
|||
} |
|||
|
|||
.student-list { |
|||
max-height: 600rpx; |
|||
overflow-y: auto; |
|||
} |
|||
|
|||
.student-item { |
|||
display: flex; |
|||
align-items: center; |
|||
background-color: #2a2a2a; |
|||
border-radius: 8rpx; |
|||
padding: 16rpx; |
|||
margin-bottom: 16rpx; |
|||
|
|||
&:last-child { |
|||
margin-bottom: 0; |
|||
} |
|||
} |
|||
|
|||
.student-avatar { |
|||
width: 80rpx; |
|||
height: 80rpx; |
|||
border-radius: 40rpx; |
|||
overflow: hidden; |
|||
position: relative; |
|||
margin-right: 20rpx; |
|||
|
|||
image { |
|||
width: 100%; |
|||
height: 100%; |
|||
} |
|||
} |
|||
|
|||
.status-badge { |
|||
position: absolute; |
|||
bottom: 0; |
|||
right: 0; |
|||
width: 24rpx; |
|||
height: 24rpx; |
|||
border-radius: 12rpx; |
|||
background-color: #999; |
|||
border: 2rpx solid #fff; |
|||
} |
|||
|
|||
.status-absent { |
|||
background-color: #ff3b30; |
|||
} |
|||
|
|||
.status-present { |
|||
background-color: #34c759; |
|||
} |
|||
|
|||
.status-leave { |
|||
background-color: #ff9500; |
|||
} |
|||
|
|||
.student-info { |
|||
flex: 1; |
|||
display: flex; |
|||
flex-direction: column; |
|||
} |
|||
|
|||
.student-name { |
|||
font-size: 28rpx; |
|||
color: #fff; |
|||
margin-bottom: 6rpx; |
|||
} |
|||
|
|||
.student-phone { |
|||
font-size: 24rpx; |
|||
color: #999; |
|||
} |
|||
|
|||
.status-container { |
|||
margin-left: 16rpx; |
|||
} |
|||
|
|||
.status-select { |
|||
display: flex; |
|||
gap: 10rpx; |
|||
} |
|||
|
|||
.status-option { |
|||
padding: 8rpx 16rpx; |
|||
font-size: 24rpx; |
|||
border-radius: 30rpx; |
|||
background-color: #3a3a3a; |
|||
color: #fff; |
|||
|
|||
&.active { |
|||
background-color: #29d3b4; |
|||
} |
|||
} |
|||
|
|||
.empty-list { |
|||
display: flex; |
|||
flex-direction: column; |
|||
align-items: center; |
|||
justify-content: center; |
|||
padding: 60rpx 0; |
|||
|
|||
.empty-img { |
|||
width: 200rpx; |
|||
height: 200rpx; |
|||
margin-bottom: 20rpx; |
|||
} |
|||
|
|||
.empty-text { |
|||
font-size: 28rpx; |
|||
color: #999; |
|||
} |
|||
} |
|||
|
|||
.remark-section { |
|||
background-color: #23232a; |
|||
border-radius: 12rpx; |
|||
padding: 24rpx; |
|||
margin-bottom: 40rpx; |
|||
|
|||
.section-title { |
|||
font-size: 30rpx; |
|||
font-weight: bold; |
|||
color: #fff; |
|||
margin-bottom: 20rpx; |
|||
} |
|||
} |
|||
|
|||
.submit-btn { |
|||
margin-top: 40rpx; |
|||
padding-bottom: 40rpx; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,282 @@ |
|||
<!--体测数据-详情页--> |
|||
<template> |
|||
<view class="overall"> |
|||
<view class="date">{{$util.formatToDateTime(surveyInfo.created_at,'Y-m-d')}}</view> |
|||
|
|||
<view class="content"> |
|||
<view class="circle-container"> |
|||
<view class="card-con-txt1-left"> |
|||
<image :src="$util.img('/uniapp_src/static/images/index/score.png')" class="overlay-image"></image> |
|||
</view> |
|||
<view class="card-con-txt1-left-txt">{{surveyInfo.calculateChildHealthScore}}</view> |
|||
<view class="card-con-txt1-left-txt top1">综合评分</view> |
|||
</view> |
|||
<view style="height: 170rpx;"></view> |
|||
<view style="display: flex;justify-content: space-around;"> |
|||
<view style="text-align: center;"> |
|||
<view style="color: #AAAAAA;font-size: 30rpx;padding: 15rpx 0;">身高 (CM)</view> |
|||
<view style="font-size: 55rpx;color: #29d3b4;">{{(surveyInfo.height)}}</view> |
|||
</view> |
|||
<view style="text-align: center;"> |
|||
<view style="color: #AAAAAA;font-size: 30rpx;padding: 15rpx 0;">体重 (KG)</view> |
|||
<view style="font-size: 55rpx;color: #29d3b4;">{{surveyInfo.weight}}</view> |
|||
</view> |
|||
</view> |
|||
<!-- <view class="coach-message">--> |
|||
<!-- <view>--> |
|||
<!-- <image :src="$util.img('/uniapp_src/static/images/index/lv.png')" class="drop-image"></image>--> |
|||
<!-- </view>--> |
|||
<!-- <view style="padding: 15rpx 0 0 5rpx;line-height: 1.6;font-size: 30rpx;color: #7F7F7F;">{{v.content}}</view>--> |
|||
<!-- </view>--> |
|||
<view class="list_box"> |
|||
<!-- <view class="ul"> |
|||
<view class="li"> |
|||
<view class="li_title">坐位体前屈</view> |
|||
<view class="li_content">测试结果:{{surveyInfo.seated_forward_bend}}</view> |
|||
</view> |
|||
|
|||
<view class="li"> |
|||
<view class="li_title">仰卧卷腹</view> |
|||
<view class="li_content">测试结果:{{surveyInfo.sit_ups}}</view> |
|||
</view> |
|||
|
|||
<view class="li"> |
|||
<view class="li_title">九十度仰卧撑</view> |
|||
<view class="li_content">测试结果:{{surveyInfo.push_ups}}</view> |
|||
</view> |
|||
|
|||
<view class="li"> |
|||
<view class="li_title">火烈鸟平衡测试</view> |
|||
<view class="li_content">测试结果:{{surveyInfo.flamingo_balance}}</view> |
|||
</view> |
|||
|
|||
<view class="li"> |
|||
<view class="li_title">三十秒双脚连续跳</view> |
|||
<view class="li_content">测试结果:{{surveyInfo.thirty_sec_jump}}</view> |
|||
</view> |
|||
|
|||
<view class="li"> |
|||
<view class="li_title">立定跳远</view> |
|||
<view class="li_content">测试结果:{{surveyInfo.standing_long_jump}}</view> |
|||
</view> |
|||
|
|||
<view class="li"> |
|||
<view class="li_title">4乘10m灵敏折返跑</view> |
|||
<view class="li_content">测试结果:{{surveyInfo.agility_run}}</view> |
|||
</view> |
|||
|
|||
<view class="li"> |
|||
<view class="li_title">走平衡木</view> |
|||
<view class="li_content">测试结果:{{surveyInfo.balance_beam}}</view> |
|||
</view> |
|||
|
|||
<view class="li"> |
|||
<view class="li_title">网球掷远</view> |
|||
<view class="li_content">测试结果:{{surveyInfo.tennis_throw}}</view> |
|||
</view> |
|||
|
|||
<view class="li"> |
|||
<view class="li_title">十米往返跑</view> |
|||
<view class="li_content">测试结果:{{surveyInfo.ten_meter_shuttle_run}}</view> |
|||
</view> |
|||
</view> --> |
|||
<view v-for="(item,index) in surveyInfo.physical_test_report"> |
|||
<view style="color: blue;" @click="previewFile(item)">{{surveyInfo.created_at}}体测报告{{index}}</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
import memberApi from '@/api/member.js'; |
|||
import apiRoute from '@/api/apiRoute.js'; |
|||
export default { |
|||
data() { |
|||
return { |
|||
survey_id: '', //评测id |
|||
|
|||
surveyInfo: {}, //评测详情 |
|||
} |
|||
}, |
|||
onLoad(options) { |
|||
this.survey_id = options.survey_id //评测id |
|||
}, |
|||
onShow() { |
|||
this.init() |
|||
}, |
|||
methods: { |
|||
//初始化 |
|||
async init() { |
|||
this.getInfo() |
|||
}, |
|||
|
|||
//评测详情 |
|||
async getInfo() { |
|||
let data = { |
|||
survey_id: this.survey_id |
|||
} |
|||
let res = await apiRoute.physicalTestInfo(data) |
|||
if (res.code != 1) { |
|||
uni.showToast({ |
|||
title: res.msg, |
|||
icon: 'none' |
|||
}) |
|||
return |
|||
} |
|||
this.surveyInfo = res.data |
|||
}, |
|||
|
|||
async previewFile(url) { |
|||
|
|||
console.log(url) |
|||
|
|||
try { |
|||
// 1. 下载文件到本地 |
|||
const { |
|||
tempFilePath |
|||
} = await this.downloadFile(url); |
|||
|
|||
// 2. 打开文件 |
|||
await uni.openDocument({ |
|||
filePath: tempFilePath, |
|||
showMenu: true, |
|||
success: () => { |
|||
console.log('打开文档成功'); |
|||
} |
|||
}); |
|||
} catch (err) { |
|||
uni.showToast({ |
|||
title: '预览失败', |
|||
icon: 'none' |
|||
}); |
|||
console.error('预览失败:', err); |
|||
} |
|||
}, |
|||
downloadFile(url) { |
|||
return new Promise((resolve, reject) => { |
|||
uni.downloadFile({ |
|||
url, |
|||
success: (res) => { |
|||
if (res.statusCode === 200) { |
|||
resolve(res); |
|||
} else { |
|||
reject(new Error('下载失败')); |
|||
} |
|||
}, |
|||
fail: (err) => { |
|||
reject(err); |
|||
} |
|||
}); |
|||
}); |
|||
} |
|||
|
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style lang="less" scoped> |
|||
.overall { |
|||
width: 100%; |
|||
height: 100vh; |
|||
background-color: #29d3b4; |
|||
} |
|||
|
|||
.date { |
|||
color: #fff; |
|||
width: 92%; |
|||
margin: auto; |
|||
text-align: left; |
|||
font-size: 30rpx; |
|||
padding: 20rpx 0; |
|||
} |
|||
|
|||
.content { |
|||
width: 92%; |
|||
height: 70vh; |
|||
background-color: #fff; |
|||
border-radius: 15rpx; |
|||
margin: 150rpx auto 0; |
|||
position: relative; |
|||
} |
|||
|
|||
.circle-container::before { |
|||
content: ''; |
|||
width: 200px; |
|||
height: 100px; |
|||
background-color: #fff; |
|||
border-radius: 100px 100px 0 0; |
|||
display: inline-block; |
|||
transform: translate(-50%, 0) rotate(0deg); |
|||
transform-origin: center top; |
|||
position: absolute; |
|||
top: -12%; |
|||
left: 50%; |
|||
transform: translate(-50%, -0%); |
|||
} |
|||
|
|||
.card-con-txt1-left { |
|||
width: 100%; |
|||
height: 100%; |
|||
position: relative; |
|||
} |
|||
|
|||
.overlay-image { |
|||
width: 300rpx; |
|||
height: 200rpx; |
|||
position: absolute; |
|||
left: 50%; |
|||
transform: translate(-50%, -50%); |
|||
} |
|||
|
|||
.card-con-txt1-left-txt { |
|||
font-size: 32rpx; |
|||
color: #29d3b4; |
|||
position: absolute; |
|||
left: 50%; |
|||
transform: translate(-50%, -0%); |
|||
} |
|||
|
|||
.top1 { |
|||
top: 5%; |
|||
} |
|||
|
|||
.coach-message { |
|||
width: 92%; |
|||
margin: 10rpx auto; |
|||
display: flex; |
|||
} |
|||
|
|||
.drop-image { |
|||
width: 60rpx; |
|||
height: 60rpx; |
|||
align-items: center; |
|||
} |
|||
|
|||
.list_box{ |
|||
font-size: 30rpx; |
|||
// text-align: center; |
|||
margin-left: 50rpx; |
|||
margin-top: 20rpx; |
|||
.ul{ |
|||
padding: 0 20rpx; |
|||
display: flex; |
|||
flex-wrap: wrap; /* 允许换行 */ |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
gap: 20rpx; |
|||
.li{ |
|||
width: 48%; /* 每个列表项占宽度的48%,留出一些间距 */ |
|||
display: flex; |
|||
flex-direction: column; |
|||
gap: 5rpx; |
|||
.li_title{ |
|||
text-align: left; |
|||
} |
|||
.li_content{ |
|||
text-align: left; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
</style> |
|||
@ -0,0 +1,728 @@ |
|||
<template> |
|||
<view class="container safe-area"> |
|||
<view class="search-bar" @click="showSearch = true"> |
|||
<uni-icons type="search" size="22" color="#00d18c" /> |
|||
<text class="search-placeholder">搜索学员...</text> |
|||
</view> |
|||
<view class="content"> |
|||
<view v-if="studentList.length === 0" class="empty-box"> |
|||
<image :src="$util.img('/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 || $util.img('/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">{{ getRemainingCourses(item) }}节</text> |
|||
</view> |
|||
<view class="info-row"> |
|||
<text class="info-label">到期时间:</text> |
|||
<text class="info-value">{{item.end_date}}</text> |
|||
</view> |
|||
</view> |
|||
<view class="arrow-right"> |
|||
<uni-icons type="right" size="16" color="#CCCCCC"></uni-icons> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
<!-- 搜索弹窗 --> |
|||
<view v-if="showSearch" class="search_popup_mask" @tap="showSearch=false"> |
|||
<view class="search_popup_content" @tap.stop> |
|||
<view class="popup_search_content"> |
|||
<view class="popup_header"> |
|||
<view class="popup_title">学员筛选</view> |
|||
<view class="popup_close" @tap="showSearch=false"> |
|||
<text class="close_text">✕</text> |
|||
</view> |
|||
</view> |
|||
|
|||
<scroll-view :scroll-y="true" class="popup_scroll_view"> |
|||
<!-- 第一筛选区域 --> |
|||
<view class="popup_filter_section"> |
|||
<view class="popup_filter_row"> |
|||
<view class="popup_filter_item"> |
|||
<text class="popup_filter_label">学生姓名</text> |
|||
<input class="popup_filter_input" placeholder="请输入学生姓名" v-model="searchForm.name" /> |
|||
</view> |
|||
<view class="popup_filter_item"> |
|||
<text class="popup_filter_label">联系电话</text> |
|||
<input class="popup_filter_input" placeholder="请输入联系电话" v-model="searchForm.phone" /> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="popup_filter_row"> |
|||
<view class="popup_filter_item"> |
|||
<text class="popup_filter_label">课时数量</text> |
|||
<input class="popup_filter_input" placeholder="请输入课时数量" v-model="searchForm.lessonCount" /> |
|||
</view> |
|||
<view class="popup_filter_item"> |
|||
<text class="popup_filter_label">请假次数</text> |
|||
<input class="popup_filter_input" placeholder="请输入请假次数" v-model="searchForm.leaveCount" /> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="popup_filter_row"> |
|||
<view class="popup_filter_item"> |
|||
<text class="popup_filter_label">课程名称</text> |
|||
<view class="popup_filter_picker" @click="showCoursePicker = true"> |
|||
{{ selectedCourseName || '请选择课程' }} |
|||
</view> |
|||
</view> |
|||
<view class="popup_filter_item"> |
|||
<text class="popup_filter_label">班级</text> |
|||
<view class="popup_filter_picker" @click="showClassPicker = true"> |
|||
{{ selectedClassName || '请选择班级' }} |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</scroll-view> |
|||
|
|||
<view class="popup_filter_buttons"> |
|||
<view class="popup_filter_btn reset_btn" @click="resetSearch">重置</view> |
|||
<view class="popup_filter_btn search_btn" @click="doSearchAndClose">搜索</view> |
|||
<view class="popup_filter_btn close_btn" @click="closeSearch">关闭</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 选择器组件 --> |
|||
<single-picker |
|||
:show.sync="showCoursePicker" |
|||
:data="courseList" |
|||
valueKey="id" |
|||
textKey="course_name" |
|||
title="选择课程" |
|||
@change="onCourseChange" |
|||
@cancel="showCoursePicker = false" |
|||
></single-picker> |
|||
<single-picker |
|||
:show.sync="showClassPicker" |
|||
:data="classList" |
|||
valueKey="id" |
|||
:textKey="['campus_name', 'class_name']" |
|||
textSeparator="-" |
|||
title="选择班级" |
|||
@change="onClassChange" |
|||
@cancel="showClassPicker = false" |
|||
></single-picker> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
import memberApi from '@/api/member.js' |
|||
import apiRoute from '@/api/apiRoute.js'; |
|||
import SinglePicker from "@/components/custom-picker/single-picker.vue" |
|||
export default { |
|||
components: { |
|||
SinglePicker |
|||
}, |
|||
data() { |
|||
return { |
|||
studentList: [], |
|||
showSearch: false, |
|||
showCoursePicker: false, |
|||
showClassPicker: false, |
|||
searchForm: { |
|||
name: '', |
|||
phone: '', |
|||
lessonCount: '', |
|||
leaveCount: '', |
|||
courseId: null, |
|||
classId: null, |
|||
}, |
|||
selectedCourseName: '', |
|||
selectedClassName: '', |
|||
courseList: [], |
|||
classList: [], |
|||
} |
|||
}, |
|||
onLoad() { |
|||
this.getStudentList(); |
|||
this.getClassesList(); |
|||
}, |
|||
methods: { |
|||
navigateBack() { |
|||
uni.navigateBack(); |
|||
}, |
|||
async getStudentList() { |
|||
try { |
|||
// 合并基础参数和搜索表单参数,后端现在支持完整的搜索功能 |
|||
const params = { type: 'all' }; |
|||
const res = await apiRoute.xs_getStudentList(Object.assign(params, this.searchForm)); |
|||
console.log('获取学员列表响应:', res); |
|||
if(res.code == 1) { |
|||
this.studentList = res.data || []; |
|||
// 如果没有数据,添加一些测试数据用于测试编辑功能 |
|||
if (this.studentList.length === 0) { |
|||
this.studentList = [ |
|||
{ |
|||
id: 1, |
|||
name: '于支付', |
|||
avatar: '', |
|||
campus: '测试校区', |
|||
total_hours: 20, |
|||
gift_hours: 5, |
|||
use_total_hours: 8, |
|||
use_gift_hours: 2, |
|||
end_date: '2025-08-31', |
|||
resource_sharing_id: 1 |
|||
}, |
|||
{ |
|||
id: 2, |
|||
name: '测试学员', |
|||
avatar: '', |
|||
campus: '测试校区', |
|||
total_hours: 15, |
|||
gift_hours: 3, |
|||
use_total_hours: 5, |
|||
use_gift_hours: 1, |
|||
end_date: '2025-08-15', |
|||
resource_sharing_id: 5 |
|||
} |
|||
]; |
|||
} |
|||
console.log('学员列表更新成功:', this.studentList); |
|||
} else { |
|||
console.error('API返回错误:', res); |
|||
uni.showToast({ |
|||
title: res.msg || '获取学员列表失败', |
|||
icon: 'none' |
|||
}); |
|||
} |
|||
}catch ( error) { |
|||
console.error('获取学员列表错误', error); |
|||
uni.showToast({ |
|||
title: '获取学员列表失败', |
|||
icon: 'none' |
|||
}); |
|||
return; |
|||
} |
|||
}, |
|||
goToDetail(student) { |
|||
this.$navigateToPage(`/pages-market/clue/clue_info`, { |
|||
resource_sharing_id: student.resource_sharing_id |
|||
}); |
|||
}, |
|||
getRemainingCourses(item) { |
|||
const totalHours = (item.total_hours || 0) |
|||
+ (item.gift_hours || 0); |
|||
const usedHours = (item.use_total_hours || |
|||
0) + (item.use_gift_hours || 0); |
|||
return totalHours - usedHours; |
|||
}, |
|||
|
|||
// 输入处理方法 |
|||
onNameInput(e) { |
|||
this.searchForm.name = e; |
|||
}, |
|||
onPhoneInput(e) { |
|||
this.searchForm.phone = e; |
|||
}, |
|||
onLessonCountInput(e) { |
|||
this.searchForm.lessonCount = e; |
|||
}, |
|||
onLeaveCountInput(e) { |
|||
this.searchForm.leaveCount = e; |
|||
}, |
|||
|
|||
// 选择器变更处理 |
|||
onCourseChange(e) { |
|||
this.searchForm.courseId = e.value; |
|||
this.selectedCourseName = e.text; |
|||
}, |
|||
onClassChange(e) { |
|||
this.searchForm.classId = e.value; |
|||
this.selectedClassName = e.text; |
|||
}, |
|||
|
|||
// 清除选择器选中项 |
|||
clearCourseSelection(e) { |
|||
e.stopPropagation(); // 阻止事件冒泡到点击打开选择器 |
|||
this.searchForm.courseId = null; |
|||
this.selectedCourseName = ''; |
|||
}, |
|||
clearClassSelection(e) { |
|||
e.stopPropagation(); // 阻止事件冒泡到点击打开选择器 |
|||
this.searchForm.classId = null; |
|||
this.selectedClassName = ''; |
|||
}, |
|||
|
|||
// 关闭搜索窗口 |
|||
closeSearch() { |
|||
this.showSearch = false; |
|||
}, |
|||
|
|||
// 监听遮罩层点击事件 |
|||
onMaskClick() { |
|||
this.showSearch = false; |
|||
}, |
|||
|
|||
// 获取班级列表 |
|||
async getClassesList() { |
|||
try { |
|||
const res = await apiRoute.jlGetClassesList(); |
|||
if (res.code == 1) { |
|||
// 确保API返回的数据是数组格式 |
|||
this.classList = res.data.classes || []; |
|||
this.courseList = res.data.course || []; |
|||
} else { |
|||
uni.showToast({ |
|||
title: res.msg || '获取班级列表失败', |
|||
icon: 'none' |
|||
}); |
|||
} |
|||
} catch (error) { |
|||
console.error('获取班级列表错误', error); |
|||
uni.showToast({ |
|||
title: '获取班级列表失败', |
|||
icon: 'none' |
|||
}); |
|||
} |
|||
}, |
|||
|
|||
doSearch() { |
|||
// 这里可以根据 searchForm 的内容进行筛选或请求 |
|||
this.showSearch = false; |
|||
this.getStudentList() |
|||
}, |
|||
|
|||
doSearchAndClose() { |
|||
this.doSearch(); |
|||
}, |
|||
|
|||
resetSearch() { |
|||
this.searchForm = { |
|||
name: '', |
|||
phone: '', |
|||
lessonCount: '', |
|||
leaveCount: '', |
|||
courseId: null, |
|||
classId: null, |
|||
}; |
|||
this.selectedCourseName = ''; |
|||
this.selectedClassName = ''; |
|||
this.getStudentList(); |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style lang="scss"> |
|||
.container { |
|||
background-color: #18181c; |
|||
display: flex; |
|||
flex-direction: column; |
|||
} |
|||
|
|||
.safe-area { |
|||
padding-top: var(--status-bar-height); |
|||
padding-bottom: 120rpx; |
|||
} |
|||
|
|||
.search-bar { |
|||
display: flex; |
|||
align-items: center; |
|||
background: #23232a; |
|||
border-radius: 12rpx; |
|||
padding: 18rpx 24rpx; |
|||
margin: 24rpx 24rpx 0 24rpx; |
|||
color: #bdbdbd; |
|||
font-size: 28rpx; |
|||
box-shadow: 0 2rpx 10rpx rgba(0,0,0,0.08); |
|||
&:active { |
|||
background: #2a2a31; |
|||
} |
|||
} |
|||
.search-placeholder { |
|||
margin-left: 12rpx; |
|||
color: #bdbdbd; |
|||
flex: 1; |
|||
} |
|||
|
|||
.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: #23232a; |
|||
border-radius: 12rpx; |
|||
padding: 30rpx; |
|||
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.12); |
|||
transition: box-shadow 0.2s; |
|||
&:active { |
|||
box-shadow: 0 4rpx 20rpx rgba(0,255,128,0.12); |
|||
} |
|||
} |
|||
|
|||
.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: #fff; |
|||
} |
|||
|
|||
.info-row { |
|||
display: flex; |
|||
font-size: 26rpx; |
|||
margin-top: 8rpx; |
|||
|
|||
.info-label { |
|||
color: #bdbdbd; |
|||
} |
|||
|
|||
.info-value { |
|||
color: #fff; |
|||
} |
|||
} |
|||
|
|||
.arrow-right { |
|||
padding-left: 20rpx; |
|||
} |
|||
} |
|||
|
|||
.popup-content { |
|||
background: #23232a; |
|||
color: #fff; |
|||
padding: 32rpx 24rpx; |
|||
border-radius: 16rpx; |
|||
min-width: 300rpx; |
|||
text-align: left; |
|||
} |
|||
.popup-title { |
|||
font-size: 32rpx; |
|||
font-weight: bold; |
|||
color: #00d18c; |
|||
margin-bottom: 24rpx; |
|||
text-align: center; |
|||
} |
|||
.picker-row { |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: space-between; |
|||
padding: 18rpx 0; |
|||
border-bottom: 1px solid #333; |
|||
.picker-label { |
|||
color: #bdbdbd; |
|||
font-size: 28rpx; |
|||
} |
|||
.picker-value { |
|||
color: #fff; |
|||
font-size: 28rpx; |
|||
} |
|||
} |
|||
.search-btn { |
|||
width: 100%; |
|||
background: #00d18c; |
|||
color: #fff; |
|||
border: none; |
|||
border-radius: 12rpx; |
|||
font-size: 30rpx; |
|||
padding: 20rpx 0; |
|||
margin-top: 32rpx; |
|||
margin-bottom: 8rpx; |
|||
} |
|||
|
|||
.fui-picker__input { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
height: 72rpx; |
|||
padding: 0 24rpx; |
|||
background-color: #2c2c34; |
|||
border-radius: 8rpx; |
|||
|
|||
text { |
|||
font-size: 28rpx; |
|||
color: #fff; |
|||
} |
|||
} |
|||
|
|||
.fui-btn__box { |
|||
margin-top: 50rpx; |
|||
padding: 0 30rpx; |
|||
} |
|||
|
|||
.fui-page__bd { |
|||
padding: 30rpx; |
|||
padding-top: 70rpx; |
|||
background-color: #23232a; |
|||
position: relative; |
|||
border-top-left-radius: 24rpx; |
|||
border-top-right-radius: 24rpx; |
|||
} |
|||
|
|||
.fui-section__title { |
|||
font-size: 36rpx; |
|||
color: #00d18c; |
|||
font-weight: bold; |
|||
margin-top: 30rpx; |
|||
text-align: center; |
|||
} |
|||
|
|||
.search-close-icon { |
|||
position: absolute; |
|||
top: 24rpx; |
|||
right: 24rpx; |
|||
z-index: 10; |
|||
padding: 10rpx; |
|||
} |
|||
|
|||
.custom-picker-input { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
padding: 24rpx; |
|||
border-radius: 8rpx; |
|||
border-bottom: 2rpx solid #00d18c; |
|||
} |
|||
|
|||
.picker-actions { |
|||
display: flex; |
|||
align-items: center; |
|||
gap: 10rpx; |
|||
} |
|||
|
|||
// 搜索弹窗样式 - 参考 market/clue 页面 |
|||
.search_popup_mask { |
|||
position: fixed; |
|||
top: 0; |
|||
left: 0; |
|||
right: 0; |
|||
bottom: 0; |
|||
background: rgba(0, 0, 0, 0.6); |
|||
z-index: 999; |
|||
display: flex; |
|||
flex-direction: column; |
|||
} |
|||
|
|||
.search_popup_content { |
|||
background: #fff; |
|||
border-bottom-left-radius: 24rpx; |
|||
border-bottom-right-radius: 24rpx; |
|||
animation: slideDown 0.3s ease-out; |
|||
width: 100%; |
|||
max-height: 80vh; |
|||
overflow: hidden; |
|||
display: flex; |
|||
flex-direction: column; |
|||
} |
|||
|
|||
@keyframes slideDown { |
|||
from { |
|||
transform: translateY(-100%); |
|||
} |
|||
to { |
|||
transform: translateY(0); |
|||
} |
|||
} |
|||
|
|||
// 弹窗搜索内容样式 |
|||
.popup_search_content { |
|||
padding: 0; |
|||
background: #fff; |
|||
min-height: 60vh; |
|||
max-height: 80vh; |
|||
display: flex; |
|||
flex-direction: column; |
|||
border-bottom-left-radius: 24rpx; |
|||
border-bottom-right-radius: 24rpx; |
|||
overflow: hidden; |
|||
} |
|||
|
|||
.popup_header { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
padding: 32rpx; |
|||
border-bottom: 1px solid #f0f0f0; |
|||
} |
|||
|
|||
.popup_title { |
|||
font-size: 32rpx; |
|||
font-weight: 600; |
|||
color: #333; |
|||
} |
|||
|
|||
.popup_close { |
|||
width: 60rpx; |
|||
height: 60rpx; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
|
|||
.close_text { |
|||
font-size: 32rpx; |
|||
color: #999; |
|||
} |
|||
} |
|||
|
|||
.popup_scroll_view { |
|||
flex: 1; |
|||
padding: 32rpx; |
|||
overflow-y: auto; |
|||
} |
|||
|
|||
.popup_filter_section { |
|||
margin-bottom: 32rpx; |
|||
|
|||
&:last-child { |
|||
margin-bottom: 0; |
|||
} |
|||
} |
|||
|
|||
.popup_filter_row { |
|||
display: flex; |
|||
gap: 20rpx; |
|||
margin-bottom: 24rpx; |
|||
|
|||
&:last-child { |
|||
margin-bottom: 0; |
|||
} |
|||
} |
|||
|
|||
.popup_filter_item { |
|||
flex: 1; |
|||
display: flex; |
|||
flex-direction: column; |
|||
gap: 12rpx; |
|||
|
|||
&.full_width { |
|||
flex: 1; |
|||
} |
|||
|
|||
.popup_filter_label { |
|||
font-size: 26rpx; |
|||
color: #666; |
|||
font-weight: 500; |
|||
} |
|||
|
|||
.popup_filter_input { |
|||
height: 72rpx; |
|||
line-height: 72rpx; |
|||
padding: 0 16rpx; |
|||
border: 1px solid #ddd; |
|||
border-radius: 8rpx; |
|||
font-size: 28rpx; |
|||
color: #333; |
|||
background: #fff; |
|||
|
|||
&::placeholder { |
|||
color: #999; |
|||
} |
|||
} |
|||
|
|||
.popup_filter_picker { |
|||
height: 72rpx; |
|||
line-height: 72rpx; |
|||
padding: 0 16rpx; |
|||
border: 1px solid #ddd; |
|||
border-radius: 8rpx; |
|||
font-size: 28rpx; |
|||
color: #333; |
|||
background: #fff; |
|||
position: relative; |
|||
|
|||
&::after { |
|||
content: '▼'; |
|||
position: absolute; |
|||
right: 16rpx; |
|||
font-size: 20rpx; |
|||
color: #999; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.popup_filter_buttons { |
|||
display: flex; |
|||
gap: 20rpx; |
|||
padding: 32rpx; |
|||
margin-top: auto; |
|||
border-top: 1px solid #f0f0f0; |
|||
background: #fff; |
|||
border-bottom-left-radius: 24rpx; |
|||
border-bottom-right-radius: 24rpx; |
|||
} |
|||
|
|||
.popup_filter_btn { |
|||
flex: 1; |
|||
height: 72rpx; |
|||
line-height: 72rpx; |
|||
text-align: center; |
|||
border-radius: 8rpx; |
|||
font-size: 28rpx; |
|||
font-weight: 600; |
|||
|
|||
&.search_btn { |
|||
background: #00d18c; |
|||
color: #fff; |
|||
} |
|||
|
|||
&.reset_btn { |
|||
background: #f5f5f5; |
|||
color: #666; |
|||
border: 1px solid #ddd; |
|||
} |
|||
|
|||
&.close_btn { |
|||
background: #666; |
|||
color: #fff; |
|||
} |
|||
} |
|||
</style> |
|||
@ -0,0 +1,129 @@ |
|||
<!--文章-详情--> |
|||
<template> |
|||
<view class="main_box"> |
|||
|
|||
<view class="main_section"> |
|||
<view class="section_1"> |
|||
<view class="titile">{{infoData.title}}</view> |
|||
<view class="content" v-html="infoData.content"></view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
import commonApi from '@/api/common.js'; |
|||
|
|||
|
|||
|
|||
export default { |
|||
components: { |
|||
}, |
|||
data() { |
|||
return { |
|||
//筛选条件 |
|||
filteredData:{ |
|||
id: '1', |
|||
}, |
|||
|
|||
infoData: { |
|||
title: '文章标题xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx111111',//文章标题 |
|||
content: ` |
|||
<p>aspectFit:保持纵横比缩放图片,使图片的长边能完全显示出来</p> |
|||
<p><img src="https://qiniu-web-assets.dcloud.net.cn/unidoc/zh/cat-2.png" alt="uniapp" loading="lazy"></p> |
|||
`,//文章内容 |
|||
},//文章详情 |
|||
} |
|||
}, |
|||
onLoad(options) { |
|||
this.filteredData.id = options.id//发信人id |
|||
}, |
|||
onShow(){ |
|||
this.init() |
|||
}, |
|||
//下拉刷新 |
|||
async onPullDownRefresh() { |
|||
await this.getInfo() |
|||
}, |
|||
methods: { |
|||
//初始化 |
|||
async init(){ |
|||
await this.getInfo(); |
|||
}, |
|||
//获取文章详情 |
|||
//获取课程详情 |
|||
async getInfo(){ |
|||
let res = await memberApi.courseInfo({ |
|||
id: this.filteredData.id, |
|||
}) |
|||
if(res.code != 1){ |
|||
uni.showToast({ |
|||
title: res.msg, |
|||
icon: 'none' |
|||
}) |
|||
return |
|||
} |
|||
this.infoData = res.data |
|||
}, |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style lang="less" scoped> |
|||
.main_box { |
|||
background: #fff; |
|||
word-wrap: break-word; /* 允许长单词或 URL 换行 */ |
|||
word-break: break-all; /* 强制所有字符换行 */ |
|||
} |
|||
|
|||
//自定义导航栏 |
|||
.navbar_section { |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: center; |
|||
background: #29d3b4; |
|||
|
|||
.title { |
|||
padding: 20rpx 0; |
|||
font-size: 30rpx; |
|||
color: #315d55; |
|||
} |
|||
} |
|||
|
|||
.main_section { |
|||
min-height: 100vh; |
|||
background: #fff; |
|||
padding: 0 0rpx; |
|||
padding-top: 32rpx; |
|||
padding-bottom: 150rpx; |
|||
font-size: 28rpx; |
|||
display: flex; |
|||
flex-direction: column; |
|||
gap: 20rpx; |
|||
|
|||
.section { |
|||
background-color: #434544; |
|||
padding: 40rpx 40rpx; |
|||
} |
|||
|
|||
.section_1{ |
|||
padding: 0 24rpx; |
|||
display: flex; |
|||
flex-direction: column; |
|||
gap: 30rpx; |
|||
.titile{ |
|||
font-size: 40rpx; |
|||
|
|||
} |
|||
} |
|||
|
|||
|
|||
|
|||
|
|||
} |
|||
|
|||
.describe { |
|||
color: #999999; |
|||
padding-left: 30rpx; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,707 @@ |
|||
<template> |
|||
<view class="contract-detail-container"> |
|||
|
|||
<!-- 加载状态 --> |
|||
<view v-if="loading" class="loading-container"> |
|||
<uni-load-more status="loading" content-text="加载中..."></uni-load-more> |
|||
</view> |
|||
|
|||
<!-- 合同内容 --> |
|||
<view v-else class="contract-content"> |
|||
<!-- 合同状态卡片 --> |
|||
<view class="status-card"> |
|||
<view class="status-header"> |
|||
<view class="status-info"> |
|||
<text class="contract-name">{{contractData.contract_name}}</text> |
|||
<view class="status-badge" :class="needSignButton() ? 'status-unsigned' : 'status-signed'"> |
|||
{{needSignButton() ? '待签订' : '已签订'}} |
|||
</view> |
|||
</view> |
|||
<view class="status-icon"> |
|||
<text v-if="needSignButton()" class="status-icon-text">⏰</text> |
|||
<text v-else class="status-icon-text">✅</text> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="status-desc"> |
|||
<text v-if="needSignButton()" class="desc-text">请仔细阅读合同内容,确认无误后进行签订</text> |
|||
<text v-else class="desc-text">合同已签订完成,您可以查看或下载合同文件</text> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 合同基本信息 --> |
|||
<view class="info-section"> |
|||
<view class="section-title">基本信息</view> |
|||
<view class="info-list"> |
|||
<view class="info-item"> |
|||
<text class="info-label">合同名称</text> |
|||
<text class="info-value">{{contractData.contract_name}}</text> |
|||
</view> |
|||
<view class="info-item"> |
|||
<text class="info-label">合同类型</text> |
|||
<text class="info-value">{{contractData.contract_type}}</text> |
|||
</view> |
|||
<view class="info-item"> |
|||
<text class="info-label">合同状态</text> |
|||
<text class="info-value">{{contractData.contract_status}}</text> |
|||
</view> |
|||
<view class="info-item"> |
|||
<text class="info-label">创建时间</text> |
|||
<text class="info-value">{{formatDate(contractData.created_at)}}</text> |
|||
</view> |
|||
<view class="info-item" v-if="contractData.sign_time"> |
|||
<text class="info-label">签订时间</text> |
|||
<text class="info-value">{{formatDate(contractData.sign_time)}}</text> |
|||
</view> |
|||
<view class="info-item" v-if="contractData.remarks"> |
|||
<text class="info-label">备注</text> |
|||
<text class="info-value">{{contractData.remarks}}</text> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 合同模板预览 --> |
|||
<view class="template-section" v-if="contractData.contract_template"> |
|||
<view class="section-title">合同模板</view> |
|||
<view class="template-preview" @click="previewTemplate"> |
|||
<view class="template-icon"> |
|||
<text class="iconfont icon-file"></text> |
|||
</view> |
|||
<view class="template-info"> |
|||
<text class="template-name">{{getTemplateName()}}</text> |
|||
<text class="template-desc">点击预览合同模板</text> |
|||
</view> |
|||
<view class="template-arrow"> |
|||
<text class="iconfont icon-arrow-right"></text> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 签名文件 --> |
|||
<view class="signature-section" v-if="contractData.sign_file"> |
|||
<view class="section-title">签名文件</view> |
|||
<view class="signature-preview" @click="previewSignature"> |
|||
<image :src="getSignatureUrl()" class="signature-image" mode="aspectFit"></image> |
|||
<view class="signature-overlay"> |
|||
<text class="signature-text">点击查看完整签名</text> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 操作历史 --> |
|||
<view class="history-section"> |
|||
<view class="section-title">操作历史</view> |
|||
<view class="history-list"> |
|||
<view class="history-item"> |
|||
<view class="history-dot"></view> |
|||
<view class="history-content"> |
|||
<text class="history-title">合同创建</text> |
|||
<text class="history-time">{{formatDate(contractData.created_at)}}</text> |
|||
</view> |
|||
</view> |
|||
<view class="history-item" v-if="contractData.sign_time"> |
|||
<view class="history-dot active"></view> |
|||
<view class="history-content"> |
|||
<text class="history-title">合同签订</text> |
|||
<text class="history-time">{{formatDate(contractData.sign_time)}}</text> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 底部操作按钮 --> |
|||
<view class="footer-actions" v-if="!loading"> |
|||
<button v-if="needSignButton()" |
|||
class="action-btn primary" |
|||
@click="goToSign"> |
|||
立即签订 |
|||
</button> |
|||
<button v-else |
|||
class="action-btn secondary" |
|||
@click="downloadContract"> |
|||
下载合同 |
|||
</button> |
|||
</view> |
|||
|
|||
<!-- 图片预览模态框 --> |
|||
<view v-if="showImagePreview" class="image-preview-modal" @click="closeImagePreview"> |
|||
<view class="preview-content" @click.stop> |
|||
<image :src="previewImageUrl" class="preview-image" mode="aspectFit"></image> |
|||
<view class="preview-close" @click="closeImagePreview"> |
|||
<text class="iconfont icon-close"></text> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
import apiRoute from '@/common/axios.js' |
|||
|
|||
export default { |
|||
data() { |
|||
return { |
|||
contractId: 0, |
|||
contractData: {}, |
|||
loading: true, |
|||
showImagePreview: false, |
|||
previewImageUrl: '' |
|||
} |
|||
}, |
|||
onLoad(options) { |
|||
if (options.id) { |
|||
this.contractId = parseInt(options.id) |
|||
this.loadContractDetail() |
|||
} else { |
|||
uni.showToast({ |
|||
title: '合同ID不能为空', |
|||
icon: 'none' |
|||
}) |
|||
setTimeout(() => { |
|||
uni.navigateBack() |
|||
}, 1500) |
|||
} |
|||
}, |
|||
methods: { |
|||
|
|||
// 加载合同详情 |
|||
async loadContractDetail() { |
|||
this.loading = true |
|||
|
|||
try { |
|||
const response = await apiRoute.get('/contract/detail', { |
|||
id: this.contractId |
|||
}) |
|||
|
|||
if (response.code === 1) { |
|||
this.contractData = response.data.data || {} |
|||
} else { |
|||
uni.showToast({ |
|||
title: response.data.msg || '加载失败', |
|||
icon: 'none' |
|||
}) |
|||
setTimeout(() => { |
|||
uni.navigateBack() |
|||
}, 1500) |
|||
} |
|||
} catch (error) { |
|||
console.error('加载合同详情失败:', error) |
|||
uni.showToast({ |
|||
title: '网络错误,请稍后重试', |
|||
icon: 'none' |
|||
}) |
|||
} finally { |
|||
this.loading = false |
|||
} |
|||
}, |
|||
|
|||
// 判断是否需要显示签订按钮 |
|||
needSignButton() { |
|||
return this.contractData.status === 1 && ( |
|||
this.contractData.sign_file === null || |
|||
this.contractData.sign_file === '' || |
|||
this.contractData.sign_file === undefined |
|||
) |
|||
}, |
|||
|
|||
|
|||
// 前往签名页面 |
|||
goToSign() { |
|||
uni.navigateTo({ |
|||
url: `/pages-common/contract/contract_sign?id=${this.contractId}&contractName=${encodeURIComponent(this.contractData.contract_name)}` |
|||
}) |
|||
}, |
|||
|
|||
// 预览合同模板 |
|||
previewTemplate() { |
|||
if (this.contractData.contract_template) { |
|||
const templateUrl = this.$baseUrl + '/' + this.contractData.contract_template |
|||
// #ifdef APP-PLUS |
|||
plus.runtime.openURL(templateUrl) |
|||
// #endif |
|||
|
|||
// #ifdef H5 |
|||
window.open(templateUrl, '_blank') |
|||
// #endif |
|||
|
|||
// #ifdef MP-WEIXIN |
|||
uni.downloadFile({ |
|||
url: templateUrl, |
|||
success: (res) => { |
|||
uni.openDocument({ |
|||
filePath: res.tempFilePath, |
|||
fileType: this.getFileType(this.contractData.contract_template) |
|||
}) |
|||
} |
|||
}) |
|||
// #endif |
|||
} |
|||
}, |
|||
|
|||
// 预览签名文件 |
|||
previewSignature() { |
|||
if (this.contractData.sign_file) { |
|||
this.previewImageUrl = this.getSignatureUrl() |
|||
this.showImagePreview = true |
|||
} |
|||
}, |
|||
|
|||
// 关闭图片预览 |
|||
closeImagePreview() { |
|||
this.showImagePreview = false |
|||
this.previewImageUrl = '' |
|||
}, |
|||
|
|||
// 获取签名文件URL |
|||
getSignatureUrl() { |
|||
if (this.contractData.sign_file) { |
|||
if (this.contractData.sign_file.startsWith('http')) { |
|||
return this.contractData.sign_file |
|||
} else { |
|||
return this.$baseUrl + '/' + this.contractData.sign_file |
|||
} |
|||
} |
|||
return '' |
|||
}, |
|||
|
|||
// 获取模板名称 |
|||
getTemplateName() { |
|||
if (this.contractData.contract_template) { |
|||
const parts = this.contractData.contract_template.split('/') |
|||
return parts[parts.length - 1] |
|||
} |
|||
return '合同模板' |
|||
}, |
|||
|
|||
// 获取文件类型 |
|||
getFileType(fileName) { |
|||
const ext = fileName.split('.').pop().toLowerCase() |
|||
const typeMap = { |
|||
'pdf': 'pdf', |
|||
'doc': 'doc', |
|||
'docx': 'doc', |
|||
'xls': 'xls', |
|||
'xlsx': 'xls' |
|||
} |
|||
return typeMap[ext] || 'doc' |
|||
}, |
|||
|
|||
// 下载合同 |
|||
downloadContract() { |
|||
if (this.contractData.sign_file) { |
|||
const signUrl = this.getSignatureUrl() |
|||
uni.downloadFile({ |
|||
url: signUrl, |
|||
success: (res) => { |
|||
uni.showToast({ |
|||
title: '下载成功', |
|||
icon: 'success' |
|||
}) |
|||
}, |
|||
fail: () => { |
|||
uni.showToast({ |
|||
title: '下载失败', |
|||
icon: 'none' |
|||
}) |
|||
} |
|||
}) |
|||
} |
|||
}, |
|||
|
|||
// 分享合同 |
|||
shareContract() { |
|||
uni.showActionSheet({ |
|||
itemList: ['分享给好友', '保存到相册'], |
|||
success: (res) => { |
|||
if (res.tapIndex === 0) { |
|||
// 分享功能 |
|||
this.shareToFriend() |
|||
} else if (res.tapIndex === 1) { |
|||
// 保存到相册 |
|||
this.saveToAlbum() |
|||
} |
|||
} |
|||
}) |
|||
}, |
|||
|
|||
// 分享给好友 |
|||
shareToFriend() { |
|||
uni.showToast({ |
|||
title: '分享功能开发中', |
|||
icon: 'none' |
|||
}) |
|||
}, |
|||
|
|||
// 保存到相册 |
|||
saveToAlbum() { |
|||
if (this.contractData.sign_file) { |
|||
uni.saveImageToPhotosAlbum({ |
|||
filePath: this.getSignatureUrl(), |
|||
success: () => { |
|||
uni.showToast({ |
|||
title: '保存成功', |
|||
icon: 'success' |
|||
}) |
|||
}, |
|||
fail: () => { |
|||
uni.showToast({ |
|||
title: '保存失败', |
|||
icon: 'none' |
|||
}) |
|||
} |
|||
}) |
|||
} |
|||
}, |
|||
|
|||
// 格式化日期 |
|||
formatDate(dateString) { |
|||
if (!dateString) return '-' |
|||
const date = new Date(dateString) |
|||
const year = date.getFullYear() |
|||
const month = String(date.getMonth() + 1).padStart(2, '0') |
|||
const day = String(date.getDate()).padStart(2, '0') |
|||
const hour = String(date.getHours()).padStart(2, '0') |
|||
const minute = String(date.getMinutes()).padStart(2, '0') |
|||
return `${year}-${month}-${day} ${hour}:${minute}` |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style lang="scss" scoped> |
|||
.contract-detail-container { |
|||
min-height: 100vh; |
|||
background-color: #1a1a1a; |
|||
padding-bottom: 120rpx; |
|||
} |
|||
|
|||
|
|||
/* 合同内容 */ |
|||
.contract-content { |
|||
padding: 20rpx; |
|||
} |
|||
|
|||
/* 状态卡片 */ |
|||
.status-card { |
|||
background: linear-gradient(135deg, #007ACC 0%, #0056b3 100%); |
|||
border-radius: 20rpx; |
|||
padding: 40rpx; |
|||
margin-bottom: 24rpx; |
|||
color: #fff; |
|||
|
|||
.status-header { |
|||
display: flex; |
|||
align-items: flex-start; |
|||
justify-content: space-between; |
|||
margin-bottom: 24rpx; |
|||
|
|||
.status-info { |
|||
flex: 1; |
|||
|
|||
.contract-name { |
|||
font-size: 36rpx; |
|||
font-weight: 600; |
|||
display: block; |
|||
margin-bottom: 16rpx; |
|||
} |
|||
|
|||
.status-badge { |
|||
display: inline-block; |
|||
padding: 8rpx 20rpx; |
|||
border-radius: 20rpx; |
|||
font-size: 24rpx; |
|||
font-weight: 500; |
|||
background-color: rgba(255, 255, 255, 0.2); |
|||
border: 1rpx solid rgba(255, 255, 255, 0.3); |
|||
} |
|||
} |
|||
|
|||
.status-icon { |
|||
width: 80rpx; |
|||
height: 80rpx; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
|
|||
.status-icon-text { |
|||
font-size: 48rpx; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.status-desc { |
|||
.desc-text { |
|||
font-size: 28rpx; |
|||
opacity: 0.9; |
|||
line-height: 1.5; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/* 信息区块 */ |
|||
.info-section, .template-section, .signature-section, .history-section { |
|||
background-color: #2a2a2a; |
|||
border-radius: 16rpx; |
|||
padding: 32rpx; |
|||
margin-bottom: 24rpx; |
|||
border: 1rpx solid #444; |
|||
|
|||
.section-title { |
|||
font-size: 32rpx; |
|||
font-weight: 600; |
|||
color: #fff; |
|||
margin-bottom: 24rpx; |
|||
border-left: 6rpx solid #29d3b4; |
|||
padding-left: 16rpx; |
|||
} |
|||
} |
|||
|
|||
.info-list { |
|||
.info-item { |
|||
display: flex; |
|||
align-items: center; |
|||
margin-bottom: 24rpx; |
|||
|
|||
&:last-child { |
|||
margin-bottom: 0; |
|||
} |
|||
|
|||
.info-label { |
|||
font-size: 28rpx; |
|||
color: #999; |
|||
width: 140rpx; |
|||
flex-shrink: 0; |
|||
} |
|||
|
|||
.info-value { |
|||
font-size: 28rpx; |
|||
color: #ccc; |
|||
flex: 1; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/* 模板预览 */ |
|||
.template-preview { |
|||
display: flex; |
|||
align-items: center; |
|||
padding: 24rpx; |
|||
background-color: #1a1a1a; |
|||
border-radius: 12rpx; |
|||
cursor: pointer; |
|||
border: 1rpx solid #444; |
|||
|
|||
.template-icon { |
|||
width: 60rpx; |
|||
height: 60rpx; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
background-color: #29d3b4; |
|||
border-radius: 12rpx; |
|||
margin-right: 20rpx; |
|||
|
|||
.iconfont { |
|||
font-size: 32rpx; |
|||
color: #fff; |
|||
} |
|||
} |
|||
|
|||
.template-info { |
|||
flex: 1; |
|||
|
|||
.template-name { |
|||
font-size: 28rpx; |
|||
color: #fff; |
|||
font-weight: 500; |
|||
display: block; |
|||
margin-bottom: 8rpx; |
|||
} |
|||
|
|||
.template-desc { |
|||
font-size: 24rpx; |
|||
color: #999; |
|||
} |
|||
} |
|||
|
|||
.template-arrow { |
|||
width: 40rpx; |
|||
height: 40rpx; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
|
|||
.iconfont { |
|||
font-size: 24rpx; |
|||
color: #666; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/* 签名预览 */ |
|||
.signature-preview { |
|||
position: relative; |
|||
width: 100%; |
|||
height: 300rpx; |
|||
border-radius: 12rpx; |
|||
overflow: hidden; |
|||
cursor: pointer; |
|||
|
|||
.signature-image { |
|||
width: 100%; |
|||
height: 100%; |
|||
border-radius: 12rpx; |
|||
} |
|||
|
|||
.signature-overlay { |
|||
position: absolute; |
|||
bottom: 0; |
|||
left: 0; |
|||
right: 0; |
|||
background: linear-gradient(transparent, rgba(0, 0, 0, 0.7)); |
|||
padding: 20rpx; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
|
|||
.signature-text { |
|||
color: #fff; |
|||
font-size: 24rpx; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/* 操作历史 */ |
|||
.history-list { |
|||
.history-item { |
|||
display: flex; |
|||
align-items: flex-start; |
|||
margin-bottom: 32rpx; |
|||
|
|||
&:last-child { |
|||
margin-bottom: 0; |
|||
} |
|||
|
|||
.history-dot { |
|||
width: 20rpx; |
|||
height: 20rpx; |
|||
border-radius: 50%; |
|||
background-color: #e5e5e5; |
|||
margin-right: 20rpx; |
|||
margin-top: 8rpx; |
|||
flex-shrink: 0; |
|||
|
|||
&.active { |
|||
background-color: #29d3b4; |
|||
} |
|||
} |
|||
|
|||
.history-content { |
|||
flex: 1; |
|||
|
|||
.history-title { |
|||
font-size: 28rpx; |
|||
color: #fff; |
|||
font-weight: 500; |
|||
display: block; |
|||
margin-bottom: 8rpx; |
|||
} |
|||
|
|||
.history-time { |
|||
font-size: 24rpx; |
|||
color: #999; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
/* 底部操作按钮 */ |
|||
.footer-actions { |
|||
position: fixed; |
|||
bottom: 0; |
|||
left: 0; |
|||
right: 0; |
|||
background-color: #2a2a2a; |
|||
padding: 24rpx 32rpx; |
|||
border-top: 1rpx solid #444; |
|||
z-index: 100; |
|||
|
|||
.action-btn { |
|||
width: 100%; |
|||
height: 88rpx; |
|||
border-radius: 12rpx; |
|||
font-size: 32rpx; |
|||
font-weight: 600; |
|||
border: none; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
|
|||
&.primary { |
|||
background-color: #29d3b4; |
|||
color: #fff; |
|||
|
|||
&:active { |
|||
background-color: #22b39a; |
|||
} |
|||
} |
|||
|
|||
&.secondary { |
|||
background-color: #f5f5f5; |
|||
color: #666; |
|||
|
|||
&:active { |
|||
background-color: #e5e5e5; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
/* 加载状态 */ |
|||
.loading-container { |
|||
padding: 120rpx 40rpx; |
|||
text-align: center; |
|||
} |
|||
|
|||
/* 图片预览模态框 */ |
|||
.image-preview-modal { |
|||
position: fixed; |
|||
top: 0; |
|||
left: 0; |
|||
right: 0; |
|||
bottom: 0; |
|||
background-color: rgba(0, 0, 0, 0.8); |
|||
z-index: 1000; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
|
|||
.preview-content { |
|||
position: relative; |
|||
width: 90%; |
|||
height: 70%; |
|||
|
|||
.preview-image { |
|||
width: 100%; |
|||
height: 100%; |
|||
} |
|||
|
|||
.preview-close { |
|||
position: absolute; |
|||
top: -60rpx; |
|||
right: 0; |
|||
width: 60rpx; |
|||
height: 60rpx; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
|
|||
.iconfont { |
|||
font-size: 36rpx; |
|||
color: #fff; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
</style> |
|||
@ -0,0 +1,733 @@ |
|||
<template> |
|||
<view class="contract-sign-container"> |
|||
<!-- 顶部操作栏 --> |
|||
<view class="top-actions"> |
|||
<view class="clear-btn" @click="clearSignature"> |
|||
<text class="clear-text">清除签名</text> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 合同信息 --> |
|||
<view class="contract-info"> |
|||
<view class="info-card"> |
|||
<text class="contract-name">{{contractName}}</text> |
|||
<text class="sign-tip">请在下方签名区域进行手写签名</text> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 签名区域 --> |
|||
<view class="signature-section"> |
|||
<view class="signature-title"> |
|||
<text class="title-text">请在此区域签名</text> |
|||
<text class="tip-text">支持手指或触控笔签名</text> |
|||
</view> |
|||
|
|||
<!-- Canvas 签名板 --> |
|||
<view class="signature-canvas-container"> |
|||
<canvas |
|||
class="signature-canvas" |
|||
canvas-id="signatureCanvas" |
|||
@touchstart="touchStart" |
|||
@touchmove="touchMove" |
|||
@touchend="touchEnd" |
|||
disable-scroll="true"> |
|||
</canvas> |
|||
|
|||
<!-- 签名提示 --> |
|||
<view v-if="!hasSigned" class="signature-placeholder"> |
|||
<text class="placeholder-icon">✍️</text> |
|||
<text class="placeholder-text">请在此处签名</text> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 签名工具栏 --> |
|||
<view class="signature-tools"> |
|||
<view class="tool-group"> |
|||
<text class="tool-label">笔迹颜色:</text> |
|||
<view class="color-picker"> |
|||
<view v-for="color in penColors" |
|||
:key="color" |
|||
class="color-item" |
|||
:class="currentColor === color ? 'active' : ''" |
|||
:style="{ backgroundColor: color }" |
|||
@click="setPenColor(color)"> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="tool-group"> |
|||
<text class="tool-label">笔迹粗细:</text> |
|||
<view class="width-picker"> |
|||
<view v-for="width in penWidths" |
|||
:key="width" |
|||
class="width-item" |
|||
:class="currentWidth === width ? 'active' : ''" |
|||
@click="setPenWidth(width)"> |
|||
<view class="width-preview" :style="{ width: width + 'rpx', height: width + 'rpx' }"></view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 签名预览 --> |
|||
<view v-if="signatureImageUrl" class="preview-section"> |
|||
<view class="preview-title">签名预览</view> |
|||
<view class="preview-image-container"> |
|||
<image :src="signatureImageUrl" class="preview-image" mode="aspectFit"></image> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 底部操作按钮 --> |
|||
<view class="footer-actions"> |
|||
<button class="action-btn secondary" @click="previewSignature">预览签名</button> |
|||
<button class="action-btn primary" |
|||
@click="submitSignature" |
|||
:disabled="!hasSigned || submitting" |
|||
:loading="submitting"> |
|||
{{submitting ? '提交中...' : '确认签订'}} |
|||
</button> |
|||
</view> |
|||
|
|||
<!-- 签名预览弹窗 --> |
|||
<view v-if="showPreview" class="preview-modal" @click="closePreview"> |
|||
<view class="modal-content" @click.stop> |
|||
<view class="modal-header"> |
|||
<text class="modal-title">签名预览</text> |
|||
<view class="modal-close" @click="closePreview"> |
|||
<text class="iconfont icon-close"></text> |
|||
</view> |
|||
</view> |
|||
<view class="modal-body"> |
|||
<image v-if="signatureImageUrl" :src="signatureImageUrl" class="modal-image" mode="aspectFit"></image> |
|||
<text v-else class="no-signature">暂无签名</text> |
|||
</view> |
|||
<view class="modal-footer"> |
|||
<button class="modal-btn secondary" @click="closePreview">取消</button> |
|||
<button class="modal-btn primary" @click="confirmSignature">确认签订</button> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
import apiRoute from '@/common/axios.js' |
|||
import { uploadFile } from '@/common/util.js'; |
|||
|
|||
export default { |
|||
data() { |
|||
return { |
|||
contractId: 0, |
|||
contractName: '', |
|||
canvas: null, |
|||
ctx: null, |
|||
isDrawing: false, |
|||
lastPoint: null, |
|||
hasSigned: false, |
|||
signatureImageUrl: '', |
|||
submitting: false, |
|||
showPreview: false, |
|||
|
|||
// 画笔设置 |
|||
currentColor: '#000000', |
|||
currentWidth: 6, |
|||
penColors: ['#000000', '#FF0000', '#0000FF', '#008000', '#800080'], |
|||
penWidths: [3, 6, 9, 12], |
|||
|
|||
// Canvas 尺寸 |
|||
canvasWidth: 0, |
|||
canvasHeight: 0, |
|||
pixelRatio: 1 |
|||
} |
|||
}, |
|||
onLoad(options) { |
|||
if (options.id) { |
|||
this.contractId = parseInt(options.id) |
|||
} |
|||
if (options.contractName) { |
|||
this.contractName = decodeURIComponent(options.contractName) |
|||
} |
|||
|
|||
this.$nextTick(() => { |
|||
this.initCanvas() |
|||
}) |
|||
}, |
|||
onReady() { |
|||
this.initCanvas() |
|||
}, |
|||
methods: { |
|||
// 初始化Canvas |
|||
initCanvas() { |
|||
const query = uni.createSelectorQuery().in(this) |
|||
query.select('.signature-canvas').boundingClientRect((rect) => { |
|||
if (rect) { |
|||
this.canvasWidth = rect.width |
|||
this.canvasHeight = rect.height |
|||
|
|||
// 获取设备像素比 |
|||
const systemInfo = uni.getSystemInfoSync() |
|||
this.pixelRatio = systemInfo.pixelRatio || 1 |
|||
|
|||
// 创建Canvas上下文 |
|||
this.canvas = uni.createCanvasContext('signatureCanvas', this) |
|||
this.ctx = this.canvas |
|||
|
|||
// 设置Canvas尺寸 |
|||
this.ctx.scale(this.pixelRatio, this.pixelRatio) |
|||
|
|||
// 设置画笔属性 |
|||
this.ctx.lineWidth = this.currentWidth |
|||
this.ctx.strokeStyle = this.currentColor |
|||
this.ctx.lineCap = 'round' |
|||
this.ctx.lineJoin = 'round' |
|||
|
|||
// 清空Canvas |
|||
this.ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight) |
|||
this.ctx.draw() |
|||
} |
|||
}).exec() |
|||
}, |
|||
|
|||
// 触摸开始 |
|||
touchStart(e) { |
|||
if (!this.ctx) return |
|||
|
|||
this.isDrawing = true |
|||
const touch = e.touches[0] |
|||
this.lastPoint = { |
|||
x: touch.x, |
|||
y: touch.y |
|||
} |
|||
|
|||
this.ctx.beginPath() |
|||
this.ctx.moveTo(touch.x, touch.y) |
|||
}, |
|||
|
|||
// 触摸移动 |
|||
touchMove(e) { |
|||
if (!this.ctx || !this.isDrawing) return |
|||
|
|||
const touch = e.touches[0] |
|||
const currentPoint = { |
|||
x: touch.x, |
|||
y: touch.y |
|||
} |
|||
|
|||
this.ctx.lineTo(currentPoint.x, currentPoint.y) |
|||
this.ctx.stroke() |
|||
this.ctx.draw(true) |
|||
|
|||
this.lastPoint = currentPoint |
|||
this.hasSigned = true |
|||
}, |
|||
|
|||
// 触摸结束 |
|||
touchEnd(e) { |
|||
this.isDrawing = false |
|||
this.lastPoint = null |
|||
}, |
|||
|
|||
// 设置画笔颜色 |
|||
setPenColor(color) { |
|||
this.currentColor = color |
|||
if (this.ctx) { |
|||
this.ctx.strokeStyle = color |
|||
} |
|||
}, |
|||
|
|||
// 设置画笔粗细 |
|||
setPenWidth(width) { |
|||
this.currentWidth = width |
|||
if (this.ctx) { |
|||
this.ctx.lineWidth = width |
|||
} |
|||
}, |
|||
|
|||
// 清除签名 |
|||
clearSignature() { |
|||
if (this.ctx) { |
|||
this.ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight) |
|||
this.ctx.draw() |
|||
this.hasSigned = false |
|||
this.signatureImageUrl = '' |
|||
} |
|||
}, |
|||
|
|||
// 预览签名 |
|||
previewSignature() { |
|||
if (!this.hasSigned) { |
|||
uni.showToast({ |
|||
title: '请先进行签名', |
|||
icon: 'none' |
|||
}) |
|||
return |
|||
} |
|||
|
|||
this.generateSignatureImage(() => { |
|||
this.showPreview = true |
|||
}) |
|||
}, |
|||
|
|||
// 生成签名图片 |
|||
generateSignatureImage(callback) { |
|||
if (!this.canvas) return |
|||
|
|||
uni.canvasToTempFilePath({ |
|||
canvasId: 'signatureCanvas', |
|||
success: (res) => { |
|||
this.signatureImageUrl = res.tempFilePath |
|||
if (callback) callback() |
|||
}, |
|||
fail: (err) => { |
|||
console.error('生成签名图片失败:', err) |
|||
uni.showToast({ |
|||
title: '生成签名失败', |
|||
icon: 'none' |
|||
}) |
|||
} |
|||
}, this) |
|||
}, |
|||
|
|||
// 关闭预览 |
|||
closePreview() { |
|||
this.showPreview = false |
|||
}, |
|||
|
|||
// 确认签名 |
|||
confirmSignature() { |
|||
this.closePreview() |
|||
this.submitSignature() |
|||
}, |
|||
|
|||
// 提交签名 |
|||
submitSignature() { |
|||
if (!this.hasSigned) { |
|||
uni.showToast({ |
|||
title: '请先进行签名', |
|||
icon: 'none' |
|||
}) |
|||
return |
|||
} |
|||
|
|||
this.generateSignatureImage(() => { |
|||
this.uploadSignature() |
|||
}) |
|||
}, |
|||
|
|||
// 上传签名 |
|||
async uploadSignature() { |
|||
if (!this.signatureImageUrl) { |
|||
uni.showToast({ |
|||
title: '签名生成失败,请重试', |
|||
icon: 'none' |
|||
}) |
|||
return |
|||
} |
|||
|
|||
this.submitting = true |
|||
|
|||
try { |
|||
// 先上传签名图片 |
|||
const uploadResult = await this.uploadSignatureFile() |
|||
|
|||
if (!uploadResult.success) { |
|||
throw new Error(uploadResult.message || '上传签名文件失败') |
|||
} |
|||
|
|||
// 提交签名信息 |
|||
const response = await apiRoute.post('/member/contract_sign', { |
|||
contract_sign_id: this.contractId, |
|||
pic_file: uploadResult.url |
|||
}) |
|||
|
|||
if (response.code === 1) { |
|||
uni.showToast({ |
|||
title: '签名提交成功', |
|||
icon: 'success', |
|||
duration: 2000 |
|||
}) |
|||
|
|||
setTimeout(() => { |
|||
// 返回到合同详情页面 |
|||
uni.navigateBack({ |
|||
delta: 1 |
|||
}) |
|||
}, 2000) |
|||
} else { |
|||
throw new Error(response.data.msg || '提交签名失败') |
|||
} |
|||
} catch (error) { |
|||
console.error('提交签名失败:', error) |
|||
uni.showToast({ |
|||
title: error.message || '网络错误,请稍后重试', |
|||
icon: 'none' |
|||
}) |
|||
} finally { |
|||
this.submitting = false |
|||
} |
|||
}, |
|||
|
|||
// 上传签名文件 |
|||
async uploadSignatureFile() { |
|||
return new Promise((resolve, reject) => { |
|||
uploadFile( |
|||
this.signatureImageUrl, |
|||
(fileData) => { |
|||
resolve({ success: true, url: fileData.url }); |
|||
}, |
|||
(err) => { |
|||
resolve({ success: false, message: '上传失败' }); |
|||
} |
|||
); |
|||
}); |
|||
}, |
|||
|
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style lang="scss" scoped> |
|||
.contract-sign-container { |
|||
min-height: 100vh; |
|||
background-color: #1a1a1a; |
|||
padding-bottom: 120rpx; |
|||
} |
|||
|
|||
/* 顶部操作栏 */ |
|||
.top-actions { |
|||
display: flex; |
|||
justify-content: flex-end; |
|||
padding: 20rpx 32rpx; |
|||
|
|||
.clear-btn { |
|||
padding: 12rpx 24rpx; |
|||
background-color: #29d3b4; |
|||
border-radius: 8rpx; |
|||
|
|||
.clear-text { |
|||
font-size: 28rpx; |
|||
color: #fff; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/* 合同信息 */ |
|||
.contract-info { |
|||
padding: 20rpx; |
|||
|
|||
.info-card { |
|||
background-color: #2a2a2a; |
|||
border-radius: 16rpx; |
|||
padding: 32rpx; |
|||
text-align: center; |
|||
border: 1rpx solid #444; |
|||
|
|||
.contract-name { |
|||
font-size: 32rpx; |
|||
font-weight: 600; |
|||
color: #fff; |
|||
display: block; |
|||
margin-bottom: 16rpx; |
|||
} |
|||
|
|||
.sign-tip { |
|||
font-size: 26rpx; |
|||
color: #999; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/* 签名区域 */ |
|||
.signature-section { |
|||
padding: 0 20rpx; |
|||
margin-bottom: 40rpx; |
|||
|
|||
.signature-title { |
|||
text-align: center; |
|||
margin-bottom: 24rpx; |
|||
|
|||
.title-text { |
|||
font-size: 30rpx; |
|||
font-weight: 600; |
|||
color: #fff; |
|||
display: block; |
|||
margin-bottom: 8rpx; |
|||
} |
|||
|
|||
.tip-text { |
|||
font-size: 24rpx; |
|||
color: #999; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.signature-canvas-container { |
|||
position: relative; |
|||
background-color: #fff; |
|||
border-radius: 16rpx; |
|||
margin-bottom: 32rpx; |
|||
overflow: hidden; |
|||
border: 2rpx dashed #29d3b4; |
|||
|
|||
.signature-canvas { |
|||
width: 100%; |
|||
height: 400rpx; |
|||
display: block; |
|||
} |
|||
|
|||
.signature-placeholder { |
|||
position: absolute; |
|||
top: 50%; |
|||
left: 50%; |
|||
transform: translate(-50%, -50%); |
|||
text-align: center; |
|||
pointer-events: none; |
|||
|
|||
.placeholder-icon { |
|||
font-size: 60rpx; |
|||
display: block; |
|||
margin-bottom: 16rpx; |
|||
} |
|||
|
|||
.placeholder-text { |
|||
font-size: 28rpx; |
|||
color: #999; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/* 签名工具栏 */ |
|||
.signature-tools { |
|||
background-color: #2a2a2a; |
|||
border-radius: 16rpx; |
|||
padding: 24rpx; |
|||
border: 1rpx solid #444; |
|||
|
|||
.tool-group { |
|||
display: flex; |
|||
align-items: center; |
|||
margin-bottom: 24rpx; |
|||
|
|||
&:last-child { |
|||
margin-bottom: 0; |
|||
} |
|||
|
|||
.tool-label { |
|||
font-size: 26rpx; |
|||
color: #ccc; |
|||
margin-right: 20rpx; |
|||
min-width: 120rpx; |
|||
} |
|||
} |
|||
|
|||
.color-picker { |
|||
display: flex; |
|||
gap: 16rpx; |
|||
|
|||
.color-item { |
|||
width: 40rpx; |
|||
height: 40rpx; |
|||
border-radius: 50%; |
|||
border: 3rpx solid transparent; |
|||
cursor: pointer; |
|||
|
|||
&.active { |
|||
border-color: #29d3b4; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.width-picker { |
|||
display: flex; |
|||
gap: 20rpx; |
|||
|
|||
.width-item { |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
width: 50rpx; |
|||
height: 50rpx; |
|||
border-radius: 8rpx; |
|||
border: 2rpx solid #e5e5e5; |
|||
cursor: pointer; |
|||
|
|||
&.active { |
|||
border-color: #29d3b4; |
|||
background-color: rgba(41, 211, 180, 0.1); |
|||
} |
|||
|
|||
.width-preview { |
|||
background-color: #333; |
|||
border-radius: 50%; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
/* 签名预览 */ |
|||
.preview-section { |
|||
padding: 0 20rpx; |
|||
margin-bottom: 40rpx; |
|||
|
|||
.preview-title { |
|||
font-size: 30rpx; |
|||
font-weight: 600; |
|||
color: #fff; |
|||
text-align: center; |
|||
margin-bottom: 24rpx; |
|||
} |
|||
|
|||
.preview-image-container { |
|||
background-color: #2a2a2a; |
|||
border-radius: 16rpx; |
|||
padding: 32rpx; |
|||
border: 1rpx solid #444; |
|||
|
|||
.preview-image { |
|||
width: 100%; |
|||
height: 200rpx; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/* 底部操作按钮 */ |
|||
.footer-actions { |
|||
position: fixed; |
|||
bottom: 0; |
|||
left: 0; |
|||
right: 0; |
|||
background-color: #2a2a2a; |
|||
padding: 24rpx 32rpx; |
|||
border-top: 1rpx solid #444; |
|||
display: flex; |
|||
gap: 24rpx; |
|||
z-index: 100; |
|||
|
|||
.action-btn { |
|||
flex: 1; |
|||
height: 88rpx; |
|||
border-radius: 12rpx; |
|||
font-size: 32rpx; |
|||
font-weight: 600; |
|||
border: none; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
|
|||
&.primary { |
|||
background-color: #29d3b4; |
|||
color: #fff; |
|||
|
|||
&:active:not(:disabled) { |
|||
background-color: #22b39a; |
|||
} |
|||
|
|||
&:disabled { |
|||
background-color: #666; |
|||
color: #999; |
|||
} |
|||
} |
|||
|
|||
&.secondary { |
|||
background-color: #444; |
|||
color: #ccc; |
|||
|
|||
&:active { |
|||
background-color: #555; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
/* 预览弹窗 */ |
|||
.preview-modal { |
|||
position: fixed; |
|||
top: 0; |
|||
left: 0; |
|||
right: 0; |
|||
bottom: 0; |
|||
background-color: rgba(0, 0, 0, 0.5); |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
z-index: 1000; |
|||
|
|||
.modal-content { |
|||
background-color: #fff; |
|||
border-radius: 20rpx; |
|||
width: 80%; |
|||
max-height: 70%; |
|||
overflow: hidden; |
|||
|
|||
.modal-header { |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: space-between; |
|||
padding: 32rpx; |
|||
border-bottom: 1rpx solid #e5e5e5; |
|||
|
|||
.modal-title { |
|||
font-size: 32rpx; |
|||
font-weight: 600; |
|||
color: #333; |
|||
} |
|||
|
|||
.modal-close { |
|||
width: 48rpx; |
|||
height: 48rpx; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
|
|||
.iconfont { |
|||
font-size: 32rpx; |
|||
color: #999; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.modal-body { |
|||
padding: 32rpx; |
|||
text-align: center; |
|||
|
|||
.modal-image { |
|||
width: 100%; |
|||
height: 300rpx; |
|||
} |
|||
|
|||
.no-signature { |
|||
font-size: 28rpx; |
|||
color: #999; |
|||
padding: 60rpx 0; |
|||
} |
|||
} |
|||
|
|||
.modal-footer { |
|||
display: flex; |
|||
gap: 24rpx; |
|||
padding: 32rpx; |
|||
border-top: 1rpx solid #e5e5e5; |
|||
|
|||
.modal-btn { |
|||
flex: 1; |
|||
height: 72rpx; |
|||
border-radius: 12rpx; |
|||
font-size: 28rpx; |
|||
font-weight: 600; |
|||
border: none; |
|||
|
|||
&.primary { |
|||
background-color: #29d3b4; |
|||
color: #fff; |
|||
} |
|||
|
|||
&.secondary { |
|||
background-color: #f5f5f5; |
|||
color: #666; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
</style> |
|||
@ -0,0 +1,411 @@ |
|||
<template> |
|||
<view class="contract-container"> |
|||
|
|||
<!-- 筛选栏 --> |
|||
<view class="filter-bar"> |
|||
<view class="filter-item" |
|||
:class="filterStatus === '' ? 'active' : ''" |
|||
@click="filterContract('')"> |
|||
全部 |
|||
</view> |
|||
<view class="filter-item" |
|||
:class="filterStatus === 'unsigned' ? 'active' : ''" |
|||
@click="filterContract('unsigned')"> |
|||
待签订 |
|||
</view> |
|||
<view class="filter-item" |
|||
:class="filterStatus === 'signed' ? 'active' : ''" |
|||
@click="filterContract('signed')"> |
|||
已签订 |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 合同列表 --> |
|||
<view class="contract-list"> |
|||
<view v-if="loading" class="loading-container"> |
|||
<uni-load-more status="loading" content-text="加载中..."></uni-load-more> |
|||
</view> |
|||
|
|||
<view v-else-if="filteredContracts.length === 0" class="empty-container"> |
|||
<view class="empty-icon">📄</view> |
|||
<view class="empty-text">暂无合同数据</view> |
|||
</view> |
|||
|
|||
<view v-else> |
|||
<view v-for="contract in filteredContracts" |
|||
:key="contract.id" |
|||
class="contract-item" |
|||
@click="goToDetail(contract)"> |
|||
|
|||
<!-- 合同卡片 --> |
|||
<view class="contract-card"> |
|||
<!-- 合同标题和状态 --> |
|||
<view class="contract-header"> |
|||
<view class="contract-title"> |
|||
<text class="title-text">{{contract.contract_name}}</text> |
|||
<view class="contract-status" :class="needSignButton(contract) ? 'status-unsigned' : 'status-signed'"> |
|||
{{needSignButton(contract) ? '待签订' : '已签订'}} |
|||
</view> |
|||
</view> |
|||
<view class="contract-arrow"> |
|||
<text class="iconfont icon-arrow-right"></text> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 合同信息 --> |
|||
<view class="contract-info"> |
|||
<view class="info-row"> |
|||
<text class="info-label">合同类型:</text> |
|||
<text class="info-value">{{contract.contract_type}}</text> |
|||
</view> |
|||
<view class="info-row"> |
|||
<text class="info-label">创建时间:</text> |
|||
<text class="info-value">{{formatDate(contract.created_at)}}</text> |
|||
</view> |
|||
<view class="info-row" v-if="contract.sign_time"> |
|||
<text class="info-label">签订时间:</text> |
|||
<text class="info-value">{{formatDate(contract.sign_time)}}</text> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 操作按钮 --> |
|||
<view class="contract-actions" v-if="needSignButton(contract)"> |
|||
<button class="sign-btn" @click.stop="goToSign(contract)"> |
|||
立即签订 |
|||
</button> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 底部加载更多 --> |
|||
<view v-if="hasMore && !loading && filteredContracts.length > 0" class="load-more"> |
|||
<uni-load-more :status="loadMoreStatus" @clickLoadMore="loadMore"></uni-load-more> |
|||
</view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
import apiRoute from '@/common/axios.js' |
|||
|
|||
export default { |
|||
data() { |
|||
return { |
|||
loading: true, |
|||
contracts: [], |
|||
filterStatus: '', // '', 'unsigned', 'signed' |
|||
currentPage: 1, |
|||
pageSize: 10, |
|||
hasMore: true, |
|||
loadMoreStatus: 'more' |
|||
} |
|||
}, |
|||
computed: { |
|||
filteredContracts() { |
|||
if (this.filterStatus === '') { |
|||
return this.contracts |
|||
} else if (this.filterStatus === 'unsigned') { |
|||
return this.contracts.filter(contract => this.needSignButton(contract)) |
|||
} else if (this.filterStatus === 'signed') { |
|||
return this.contracts.filter(contract => !this.needSignButton(contract)) |
|||
} |
|||
return this.contracts |
|||
} |
|||
}, |
|||
onLoad() { |
|||
this.loadContracts() |
|||
}, |
|||
onPullDownRefresh() { |
|||
this.refreshContracts() |
|||
}, |
|||
onReachBottom() { |
|||
if (this.hasMore && !this.loading) { |
|||
this.loadMore() |
|||
} |
|||
}, |
|||
methods: { |
|||
|
|||
// 加载合同列表 |
|||
async loadContracts(refresh = false) { |
|||
if (refresh) { |
|||
this.currentPage = 1 |
|||
this.hasMore = true |
|||
this.contracts = [] |
|||
} |
|||
|
|||
this.loading = true |
|||
this.loadMoreStatus = 'loading' |
|||
|
|||
try { |
|||
const response = await apiRoute.get('/contract/myContracts', { |
|||
page: this.currentPage, |
|||
limit: this.pageSize |
|||
}) |
|||
if (response.code === 1) { |
|||
const newContracts = response.data.data || [] |
|||
if (refresh) { |
|||
this.contracts = newContracts |
|||
} else { |
|||
this.contracts = [...this.contracts, ...newContracts] |
|||
} |
|||
|
|||
// 检查是否还有更多数据 |
|||
this.hasMore = newContracts.length === this.pageSize |
|||
this.loadMoreStatus = this.hasMore ? 'more' : 'noMore' |
|||
} else { |
|||
uni.showToast({ |
|||
title: response.data.msg || '加载失败', |
|||
icon: 'none' |
|||
}) |
|||
} |
|||
} catch (error) { |
|||
console.error('加载合同列表失败:', error) |
|||
uni.showToast({ |
|||
title: '网络错误,请稍后重试', |
|||
icon: 'none' |
|||
}) |
|||
} finally { |
|||
this.loading = false |
|||
if (refresh) { |
|||
uni.stopPullDownRefresh() |
|||
} |
|||
} |
|||
}, |
|||
|
|||
// 刷新合同列表 |
|||
refreshContracts() { |
|||
this.loadContracts(true) |
|||
}, |
|||
|
|||
// 加载更多 |
|||
loadMore() { |
|||
if (this.hasMore && !this.loading) { |
|||
this.currentPage++ |
|||
this.loadContracts() |
|||
} |
|||
}, |
|||
|
|||
// 筛选合同 |
|||
filterContract(status) { |
|||
this.filterStatus = status |
|||
}, |
|||
|
|||
// 前往合同详情 |
|||
goToDetail(contract) { |
|||
uni.navigateTo({ |
|||
url: `/pages-common/contract/contract_detail?id=${contract.id}` |
|||
}) |
|||
}, |
|||
|
|||
// 前往签名页面 |
|||
goToSign(contract) { |
|||
uni.navigateTo({ |
|||
url: `/pages-common/contract/contract_sign?id=${contract.id}&contractName=${encodeURIComponent(contract.contract_name)}` |
|||
}) |
|||
}, |
|||
|
|||
// 判断是否需要显示签订按钮 |
|||
needSignButton(contract) { |
|||
return contract.status == 1 |
|||
}, |
|||
|
|||
|
|||
// 格式化日期 |
|||
formatDate(dateString) { |
|||
if (!dateString) return '-' |
|||
const date = new Date(dateString) |
|||
const year = date.getFullYear() |
|||
const month = String(date.getMonth() + 1).padStart(2, '0') |
|||
const day = String(date.getDate()).padStart(2, '0') |
|||
return `${year}-${month}-${day}` |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style lang="scss" scoped> |
|||
.contract-container { |
|||
min-height: 100vh; |
|||
background-color: #1a1a1a; |
|||
} |
|||
|
|||
|
|||
/* 筛选栏 */ |
|||
.filter-bar { |
|||
display: flex; |
|||
background-color: #2a2a2a; |
|||
padding: 24rpx 32rpx; |
|||
margin-bottom: 20rpx; |
|||
border-bottom: 1rpx solid #444; |
|||
|
|||
.filter-item { |
|||
flex: 1; |
|||
text-align: center; |
|||
padding: 16rpx 24rpx; |
|||
font-size: 28rpx; |
|||
color: #ccc; |
|||
border-radius: 8rpx; |
|||
transition: all 0.3s; |
|||
|
|||
&.active { |
|||
background-color: #29d3b4; |
|||
color: #fff; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/* 合同列表 */ |
|||
.contract-list { |
|||
padding: 0 20rpx; |
|||
} |
|||
|
|||
.contract-item { |
|||
margin-bottom: 24rpx; |
|||
} |
|||
|
|||
.contract-card { |
|||
background-color: #2a2a2a; |
|||
border-radius: 16rpx; |
|||
padding: 32rpx; |
|||
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.3); |
|||
border: 1rpx solid #444; |
|||
} |
|||
|
|||
.contract-header { |
|||
display: flex; |
|||
align-items: flex-start; |
|||
justify-content: space-between; |
|||
margin-bottom: 24rpx; |
|||
|
|||
.contract-title { |
|||
flex: 1; |
|||
|
|||
.title-text { |
|||
font-size: 32rpx; |
|||
font-weight: 600; |
|||
color: #fff; |
|||
display: block; |
|||
margin-bottom: 12rpx; |
|||
} |
|||
|
|||
.contract-status { |
|||
display: inline-block; |
|||
padding: 8rpx 16rpx; |
|||
border-radius: 20rpx; |
|||
font-size: 24rpx; |
|||
font-weight: 500; |
|||
|
|||
&.status-unsigned { |
|||
background-color: #fff3cd; |
|||
color: #856404; |
|||
border: 1rpx solid #ffeaa7; |
|||
} |
|||
|
|||
&.status-signed { |
|||
background-color: #d4edda; |
|||
color: #155724; |
|||
border: 1rpx solid #c3e6cb; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.contract-arrow { |
|||
width: 40rpx; |
|||
height: 40rpx; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
|
|||
.iconfont { |
|||
font-size: 24rpx; |
|||
color: #666; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.contract-info { |
|||
.info-row { |
|||
display: flex; |
|||
margin-bottom: 16rpx; |
|||
|
|||
.info-label { |
|||
font-size: 26rpx; |
|||
color: #999; |
|||
width: 140rpx; |
|||
flex-shrink: 0; |
|||
} |
|||
|
|||
.info-value { |
|||
font-size: 26rpx; |
|||
color: #ccc; |
|||
flex: 1; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.contract-actions { |
|||
margin-top: 24rpx; |
|||
padding-top: 24rpx; |
|||
border-top: 1rpx solid #f0f0f0; |
|||
|
|||
.sign-btn { |
|||
width: 100%; |
|||
height: 72rpx; |
|||
background-color: #29d3b4; |
|||
color: #fff; |
|||
border: none; |
|||
border-radius: 12rpx; |
|||
font-size: 28rpx; |
|||
font-weight: 600; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
|
|||
&:active { |
|||
background-color: #22b39a; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/* 加载状态 */ |
|||
.loading-container { |
|||
padding: 60rpx 0; |
|||
text-align: center; |
|||
} |
|||
|
|||
.empty-container { |
|||
text-align: center; |
|||
padding: 120rpx 40rpx; |
|||
|
|||
.empty-icon { |
|||
font-size: 120rpx; |
|||
margin-bottom: 24rpx; |
|||
} |
|||
|
|||
.empty-text { |
|||
font-size: 28rpx; |
|||
color: #666; |
|||
} |
|||
} |
|||
|
|||
.load-more { |
|||
padding: 40rpx 0; |
|||
} |
|||
|
|||
/* 动画效果 */ |
|||
.contract-item { |
|||
animation: slideIn 0.3s ease-out; |
|||
} |
|||
|
|||
@keyframes slideIn { |
|||
from { |
|||
opacity: 0; |
|||
transform: translateY(20rpx); |
|||
} |
|||
to { |
|||
opacity: 1; |
|||
transform: translateY(0); |
|||
} |
|||
} |
|||
</style> |
|||
@ -0,0 +1,235 @@ |
|||
<!--授课统计-详情--> |
|||
<template> |
|||
<view class="main_box"> |
|||
|
|||
<view class="main_section"> |
|||
<view class="section"> |
|||
<view class="text_input"> |
|||
<fui-textarea placeholder="请输入反馈内容" v-model="formData.feedback_text"></fui-textarea> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="section"> |
|||
<view class="upload_box"> |
|||
<view>上传图片</view> |
|||
<AQUplodeImgMulti :inputName="`attachment_url_arr`" :inputValue="formData.attachment_url_arr || []" :uploadApiUrl="uploadApiUrl" :maxFileNum="1" |
|||
@AQUploadSuccess="AQUploadSuccess" /> |
|||
</view> |
|||
</view> |
|||
|
|||
|
|||
<!-- <view class="section">--> |
|||
<!-- <view class="input_box">--> |
|||
<!-- <fui-input label="邮箱方式" borderTop placeholder="请输入邮箱" v-model="formData.mailbox"></fui-input>--> |
|||
<!-- </view>--> |
|||
<!-- </view>--> |
|||
|
|||
<!-- <view class="describe">--> |
|||
<!-- 反馈的相关问题会第一时间通过邮箱解答。--> |
|||
<!-- </view>--> |
|||
|
|||
<view class="btn" @click="submitForm()">提交</view> |
|||
</view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
import apiRoute from '@/api/apiRoute.js'; |
|||
import memberApi from '@/api/member.js'; |
|||
import AQUplodeImgMulti from '@/components/AQ/AQUplodeImgMulti'; |
|||
import AQTabber from "@/components/AQ/AQTabber" |
|||
import { |
|||
Api_url |
|||
} from "@/common/config.js"; |
|||
|
|||
|
|||
export default { |
|||
components: { |
|||
AQTabber, |
|||
AQUplodeImgMulti, |
|||
}, |
|||
data() { |
|||
return { |
|||
uploadApiUrl:`${Api_url}/memberUploadImage`,//上传文件接口的url |
|||
formData: { |
|||
attachment_url_arr: [], |
|||
attachment_url:undefined,//图片地址,多个使用,号拼接 |
|||
feedback_text:'',//反馈内容 |
|||
user_id:'',//用户ID|school_customer_resources表id |
|||
}, |
|||
} |
|||
}, |
|||
onLoad() {}, |
|||
onShow(){ |
|||
this.init() |
|||
}, |
|||
methods: { |
|||
async init(){ |
|||
//获取学生详情 |
|||
await this.getMemberInfo() |
|||
}, |
|||
|
|||
//获取学员信息 |
|||
async getMemberInfo() { |
|||
let res = await apiRoute.xy_memberInfo({}) |
|||
if(res.code != 1){ |
|||
uni.showToast({ |
|||
title: res.msg, |
|||
icon: 'none' |
|||
}) |
|||
return |
|||
} |
|||
this.formData.user_id = res.data.id |
|||
console.log('xxxx',res.data) |
|||
}, |
|||
|
|||
|
|||
//######AQ上传文件组件相关###### |
|||
// 上传文件回调 |
|||
AQUploadSuccess(res) { |
|||
console.log('接收AQ上传回调xxx1', res) |
|||
// 使用 split 方法分割字符串 |
|||
let _inputValue = [] |
|||
if (res.filePathArr.length) { |
|||
_inputValue = res.filePathArr |
|||
} |
|||
this.formData[res.inputName] = _inputValue |
|||
// console.log('接收AQ上传回调xxx1',res) |
|||
// console.log('接收AQ上传回调xxx2',this.formData[res.inputName]) |
|||
}, |
|||
|
|||
async submitForm() { |
|||
let data = {...this.formData} |
|||
if(data.attachment_url_arr.length){ |
|||
data.attachment_url = data.attachment_url_arr.join(',') |
|||
} |
|||
|
|||
if(!data.feedback_text){ |
|||
//反馈内容为必填项 |
|||
uni.showToast({ |
|||
title:'反馈内容为必填项', |
|||
icon:'none' |
|||
}) |
|||
return |
|||
} |
|||
let res = await apiRoute.xy_userFeedbackAdd(data) |
|||
if(res.code != 1){ |
|||
uni.showToast({ |
|||
title:res.msg, |
|||
icon:'none' |
|||
}) |
|||
}else{ |
|||
uni.showToast({ |
|||
title:'提交成功', |
|||
icon:'none' |
|||
}) |
|||
} |
|||
}, |
|||
|
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style lang="less" scoped> |
|||
.main_box { |
|||
background: #292929; |
|||
} |
|||
|
|||
//自定义导航栏 |
|||
.navbar_section { |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: center; |
|||
background: #29d3b4; |
|||
|
|||
.title { |
|||
padding: 20rpx 0; |
|||
font-size: 30rpx; |
|||
color: #315d55; |
|||
} |
|||
} |
|||
|
|||
.main_section { |
|||
min-height: 100vh; |
|||
background: #292929 100%; |
|||
padding: 0 0rpx; |
|||
padding-top: 32rpx; |
|||
padding-bottom: 150rpx; |
|||
font-size: 28rpx; |
|||
color: #fff; |
|||
display: flex; |
|||
flex-direction: column; |
|||
gap: 20rpx; |
|||
|
|||
.section { |
|||
background-color: #434544; |
|||
padding: 40rpx 40rpx; |
|||
|
|||
.text_input { |
|||
border: 1px solid #434544; |
|||
background-color: #434544 !important; |
|||
|
|||
::v-deep .fui-textarea__wrap { |
|||
border: 1px solid #797979; |
|||
background-color: #434544 !important; |
|||
} |
|||
|
|||
::v-deep textarea { |
|||
color: #fff !important; |
|||
} |
|||
|
|||
::v-deep .fui-textarea__background { |
|||
border: 0; |
|||
background-color: #434544 !important; |
|||
} |
|||
} |
|||
|
|||
.upload_box { |
|||
display: flex; |
|||
flex-direction: column; |
|||
gap: 20rpx; |
|||
} |
|||
|
|||
.input_box { |
|||
padding: 0; |
|||
color: #fff; |
|||
|
|||
::v-deep .fui-input__wrap { |
|||
background: #434544 !important; |
|||
padding-left: 0 !important; |
|||
} |
|||
|
|||
::v-deep .fui-input__label { |
|||
span { |
|||
color: #fff !important; |
|||
} |
|||
} |
|||
|
|||
::v-deep .uni-input-input { |
|||
color: #fff; |
|||
} |
|||
|
|||
::v-deep .fui-input__background { |
|||
background: #434544 !important; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.btn { |
|||
margin: 0 auto; |
|||
margin-top: 40rpx; |
|||
border: 1px solid #25a18b; |
|||
color: #25a18b; |
|||
width: 80%; |
|||
height: 80rpx; |
|||
line-height: 80rpx; |
|||
text-align: center; |
|||
} |
|||
|
|||
} |
|||
|
|||
.describe { |
|||
color: #999999; |
|||
padding-left: 30rpx; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,600 @@ |
|||
<!--聊天记录-列表--> |
|||
<template> |
|||
<view class="main_box"> |
|||
|
|||
<view class="main_section"> |
|||
<scroll-view |
|||
class="section_1" |
|||
scroll-y="true" |
|||
:scroll-top="scrollTop" |
|||
:lower-threshold="lowerThreshold" |
|||
style="height: 78vh;" |
|||
> |
|||
<view class="ul"> |
|||
<view class="item_box" v-for="(v,k) in tableList" :key="k" :id="'item_' + v.id"> |
|||
<view class="time_section" v-if="v.created_at">{{v.created_at}}</view> |
|||
|
|||
<view class="li" v-if="v.direction == `left`"> |
|||
<view class="item left_item" :style="{ backgroundColor: v.message_type === 'text' ? '#f4f6f9' : '' }"> |
|||
<!--文本内容--> |
|||
<view class="text_box" v-if="v.message_type == 'text'">{{v.content}}</view> |
|||
<!-- 图片内容 --> |
|||
<view class="img_box" v-if="v.message_type == 'img'" @click="previewImage(v.content)"> |
|||
<image class="chat_img" :src="v.content" mode="aspectFill"></image> |
|||
</view> |
|||
</view> |
|||
<view class="item"></view> |
|||
</view> |
|||
|
|||
<view class="li" v-if="v.direction == `right`"> |
|||
<view class="item"></view> |
|||
<view class="item right_item" :style="{ backgroundColor: v.message_type === 'text' ? '#1684fc' : '' }"> |
|||
<view class="text_box" v-if="v.message_type == 'text'">{{v.content}}</view> |
|||
<!-- 图片内容 --> |
|||
<view class="img_box" v-if="v.message_type == 'img'" @click="previewImage(v.content)"> |
|||
<image class="chat_img" :src="v.content" mode="aspectFill"></image> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</scroll-view> |
|||
</view> |
|||
|
|||
<view class="input_section"> |
|||
<view class="left_box"> |
|||
<input class="input" v-model="formData.content" type="text" placeholder="请输入"> |
|||
</view> |
|||
<view class="right_box"> |
|||
<!--发送--> |
|||
<view class="img_box"> |
|||
<image @click="submitTextForm()" class="send_img" :src="$util.img('/uniapp_src/static/images/common/fa_song.png')"></image> |
|||
</view> |
|||
|
|||
<!--更多选择--> |
|||
<view class="img_box"> |
|||
<image @click="openMore()" class="send_img" :src="$util.img('/uniapp_src/static/images/common/jia_hao.png')"></image> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<!--更多选择--> |
|||
<fui-bottom-popup :show="moreShow" @close="closeMore"> |
|||
<view class="more_section"> |
|||
<view class="fui-scroll__wrap"> |
|||
<scroll-view scroll-y class="fui-scroll__view"> |
|||
<view class="ul"> |
|||
<view class="li" @click=""> |
|||
<AQUplodeImage |
|||
:uploadUrl=uploadUrl |
|||
:extraData="{ input_name: 'img_uplode', formData:{} }" |
|||
@uplodeImageRes="uplodeImageRes" |
|||
> |
|||
<view class="icon_box"> |
|||
<fui-icon name="picture-fill" :size="80"></fui-icon> |
|||
</view> |
|||
<view class="title">相册</view> |
|||
</AQUplodeImage> |
|||
</view> |
|||
</view> |
|||
</scroll-view> |
|||
</view> |
|||
</view> |
|||
</fui-bottom-popup> |
|||
|
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
import apiRoute from '@/api/apiRoute.js'; |
|||
|
|||
import { |
|||
Api_url |
|||
} from "@/common/config.js"; |
|||
import AQUplodeImage from '@/components/AQ/AQUplodeImage';//单图上传组件 |
|||
import AQTabber from "@/components/AQ/AQTabber" |
|||
|
|||
|
|||
export default { |
|||
components: { |
|||
AQTabber, |
|||
AQUplodeImage, |
|||
}, |
|||
data() { |
|||
return { |
|||
uploadUrl: `${Api_url}/uploadImage`,//图片文件上传路径|默认为员工端上传文件 |
|||
|
|||
|
|||
scrollTop:'',//滚动条位置,用于加载第一页后滚动到底部 |
|||
loading:false,//加载状态 |
|||
lowerThreshold: 100,//距离底部多远触发 |
|||
isReachedBottom: false,//防止重复加载|true=不可加载|false=可加载 |
|||
|
|||
//筛选条件 |
|||
filteredData:{ |
|||
page:1,//当前页码 |
|||
limit:10,//每页返回数据条数 |
|||
total:10,//数据总条数 |
|||
friend_id: '',//chat_friends表id |
|||
}, |
|||
tableList:[],//聊天数据列表 |
|||
|
|||
from_id:'',//发送者ID |
|||
to_id:'',//接收者ID |
|||
from_type:'',//发送者类型|personnel=员工,customer=学生(客户) |
|||
|
|||
//请求参数 |
|||
chatFriendsInfoParams:{ |
|||
personnel_id:'', |
|||
customer_resources_id:'', |
|||
}, |
|||
chatFriendsInfo:{},//好友关系详情 |
|||
|
|||
//聊天表单信息 |
|||
formData: { |
|||
from_type:'',//发送者类型|personnel=员工,customer=学生(客户) |
|||
from_id:'',//发送者ID |
|||
to_id:'',//接收者ID |
|||
friend_id:'',//关联chat_friends表id |
|||
message_type: 'text',//消息类型|text=文本,img=图片 |
|||
content: '',//文本内容(JSON 格式扩展字段),文本类型=纯文字,图片类型=绝对路径 |
|||
}, |
|||
|
|||
//更多选项相关 |
|||
moreShow:false, |
|||
} |
|||
}, |
|||
onLoad(options) { |
|||
this.from_id = options.from_id//发送者的id |
|||
this.to_id = options.to_id//接收者的id |
|||
}, |
|||
onShow(){ |
|||
this.init() |
|||
}, |
|||
//下拉刷新 |
|||
async onPullDownRefresh() { |
|||
//加载更多(下一页) |
|||
await this.loadMoreData() |
|||
}, |
|||
methods: { |
|||
|
|||
//初始化 |
|||
async init(){ |
|||
//获取用户类型 |
|||
let userType = uni.getStorageSync('userType') |
|||
//1=教练,2=销售,3=学员 |
|||
if (['1', '2'].includes(String(userType))) { |
|||
// |
|||
this.from_type = 'personnel'//员工 |
|||
this.chatFriendsInfoParams.personnel_id = this.from_id//员工资源的id |
|||
this.chatFriendsInfoParams.customer_resources_id = this.to_id//学生资源的id |
|||
|
|||
this.formData.from_type = 'personnel'//员工 |
|||
this.formData.from_id = this.from_id//发送者的id(员工) |
|||
this.formData.to_id = this.to_id//接收者的id(学生) |
|||
|
|||
} else { |
|||
// |
|||
this.from_type = 'customer'//学生 |
|||
this.chatFriendsInfoParams.personnel_id = this.to_id//员工资源的id |
|||
this.chatFriendsInfoParams.customer_resources_id = this.from_id//学生资源的id |
|||
|
|||
this.formData.from_type = 'customer'//学生 |
|||
this.formData.from_id = this.to_id//发送者的id(学生) |
|||
this.formData.to_id = this.from_id//接收者的id(员工) |
|||
|
|||
this.uploadUrl = `${Api_url}/memberUploadImage`//图片文件上传路径|学生端端上传文件 |
|||
} |
|||
//获取好友关系详情 |
|||
await this.getChatFriendsInfo() |
|||
//获取聊天记录列表 |
|||
await this.getList(); |
|||
|
|||
// 首次加载后滚动到底部 |
|||
if (this.filteredData.page == 2) { |
|||
this.scrollToBottom() |
|||
} |
|||
}, |
|||
|
|||
//获取好友关系详情 |
|||
async getChatFriendsInfo(){ |
|||
let params = { |
|||
personnel_id: this.chatFriendsInfoParams.personnel_id,//员工资源ID |
|||
customer_resources_id: this.chatFriendsInfoParams.customer_resources_id,//学生资源ID |
|||
} |
|||
|
|||
let res |
|||
if( this.from_type == 'personnel'){ |
|||
//员工 |
|||
res = await apiRoute.xs_chatGetChatFriendsInfo(params) |
|||
}else{ |
|||
//学生 |
|||
res = await apiRoute.xy_chatGetChatFriendsInfo(params) |
|||
} |
|||
|
|||
|
|||
if (res.code != 1){ |
|||
uni.showToast({ |
|||
title: res.msg, |
|||
icon: 'none' |
|||
}) |
|||
return |
|||
} |
|||
|
|||
this.chatFriendsInfo = res.data |
|||
|
|||
this.filteredData.friend_id = res.data.id |
|||
|
|||
this.formData.friend_id = res.data.id |
|||
}, |
|||
|
|||
//加载更多(下一页) |
|||
loadMoreData() { |
|||
//判断是否加载 |
|||
if (!this.isReachedBottom) { |
|||
this.isReachedBottom = true;//设置为不可请求状态 |
|||
this.getList(); |
|||
} |
|||
}, |
|||
//重置为第一页 |
|||
async resetFilteredData() { |
|||
this.isReachedBottom = false; // 重置状态,以便下次触发加载更多 |
|||
|
|||
this.filteredData.page = 1//当前页码 |
|||
this.filteredData.limit = 10//每页返回数据条数 |
|||
this.filteredData.total = 10//数据总条数 |
|||
}, |
|||
|
|||
//获取聊天记录列表 |
|||
async getList(){ |
|||
this.loading = true |
|||
|
|||
let data = {...this.filteredData} |
|||
|
|||
// 判断是否还有数据可加载 |
|||
if ((this.filteredData.page - 1) * this.filteredData.limit >= this.filteredData.total) { |
|||
this.loading = false; |
|||
uni.showToast({ |
|||
title: '暂无更多', |
|||
icon: 'none' |
|||
}); |
|||
return; |
|||
} |
|||
|
|||
if(data.page == 1){ |
|||
this.tableList = [] |
|||
} |
|||
|
|||
|
|||
let res |
|||
if( this.from_type == 'personnel'){ |
|||
//员工 |
|||
res = await apiRoute.xs_chatGetChatMessagesList(data)//获取消息列表 |
|||
}else{ |
|||
//学生 |
|||
res = await apiRoute.xy_chatGetChatMessagesList(data)//获取消息列表 |
|||
} |
|||
|
|||
|
|||
this.loading = false |
|||
this.isReachedBottom = false; |
|||
if (res.code != 1){ |
|||
uni.showToast({ |
|||
title: res.msg, |
|||
icon: 'none' |
|||
}) |
|||
return |
|||
} |
|||
|
|||
// this.tableList = this.tableList.concat(res.data.data); // 使用 concat 方法 将新数据追加到数组中 |
|||
|
|||
let list = res.data.data |
|||
|
|||
list.forEach((v, k) => { |
|||
|
|||
if (this.from_type == 'personnel') { |
|||
//当前是员工登陆聊天页面时 |
|||
if (v.from_type == 'personnel') { |
|||
v.direction = 'right'//是员工发的消息 |
|||
} else { |
|||
v.direction = 'left'//是学生发的消息 |
|||
} |
|||
} else if (this.from_type == 'customer') { |
|||
//当前是学生登陆聊天页面时 |
|||
if (v.from_type == 'customer') { |
|||
v.direction = 'right'//是员工发的消息 |
|||
} else { |
|||
v.direction = 'left'//是学生发的消息 |
|||
} |
|||
} |
|||
}) |
|||
|
|||
list.reverse() |
|||
this.tableList.unshift(...list); // 将新数据插入到数组头部 |
|||
|
|||
console.log('列表',res.data.total) |
|||
this.filteredData.total = res.data.total |
|||
this.filteredData.page++ |
|||
}, |
|||
|
|||
|
|||
//######AQ上传文件组件相关###### |
|||
// 上传文件回调 |
|||
AQUploadSuccess(res) { |
|||
console.log('接收AQ上传回调xxx1', res) |
|||
// 使用 split 方法分割字符串 |
|||
let _inputValue = [] |
|||
if (res.filePathArr.length) { |
|||
_inputValue = res.filePathArr |
|||
} |
|||
this.formData[res.inputName] = _inputValue |
|||
// console.log('接收AQ上传回调xxx1',res) |
|||
// console.log('接收AQ上传回调xxx2',this.formData.member_store_certification_arr) |
|||
}, |
|||
|
|||
|
|||
//发送文本信息 |
|||
submitTextForm(){ |
|||
this.formData.message_type = 'text' |
|||
this.submitForm() |
|||
}, |
|||
//发送站内信 |
|||
async submitForm() { |
|||
let data = {...this.formData} |
|||
if (!data.content) { |
|||
//反馈内容为必填项 |
|||
uni.showToast({ |
|||
title: '请输入内容', |
|||
icon: 'none' |
|||
}) |
|||
return |
|||
} |
|||
|
|||
let res |
|||
if( this.from_type == 'personnel'){ |
|||
//员工 |
|||
res = await apiRoute.xs_chatSendChatMessages(data) |
|||
}else{ |
|||
//学生 |
|||
res = await apiRoute.xy_chatSendChatMessages(data) |
|||
} |
|||
|
|||
if (res.code != 1) { |
|||
uni.showToast({ |
|||
title: res.msg, |
|||
icon: 'none' |
|||
}) |
|||
} else { |
|||
let content = this.formData.content |
|||
this.formData.content = ''//清空输入框 |
|||
|
|||
|
|||
|
|||
let msgData = { |
|||
id:'', |
|||
from_type:data.from_type,//发送者类型|personnel=员工,customer=学生(客户) |
|||
from_id:data.from_id,//发送者ID |
|||
to_id:data.to_id,//接收者ID |
|||
friend_id:data.friend_id,//关联chat_friends表id |
|||
message_type: data.message_type,//消息类型|text=文本,img=图片 |
|||
content: data.content,//文本内容(JSON 格式扩展字段),文本类型=纯文字,图片类型=绝对路径 |
|||
direction:'right' |
|||
} |
|||
//将发送的消息放在最下方 |
|||
this.tableList = this.tableList.concat(msgData); |
|||
} |
|||
}, |
|||
|
|||
//页面滚动到底部 |
|||
scrollToBottom() { |
|||
this.$nextTick(() => { |
|||
this.scrollTop = 999999 // 足够大的值确保滚动到底部 |
|||
}) |
|||
}, |
|||
|
|||
//打开更多选择弹窗 |
|||
openMore(){ |
|||
this.moreShow = true |
|||
}, |
|||
//关闭更多选择弹窗 |
|||
closeMore(){ |
|||
this.moreShow = false |
|||
}, |
|||
|
|||
//文件上传回调 |
|||
uplodeImageRes(resData,extraData){ |
|||
console.log('上传成功回调',resData,extraData) |
|||
//判断是不是上传相册图片 |
|||
if(extraData.input_name == 'img_uplode'){ |
|||
this.closeMore()//关闭更多选择弹窗 |
|||
this.formData.content = resData.url |
|||
this.formData.message_type = 'img' |
|||
this.submitForm() |
|||
} |
|||
}, |
|||
|
|||
//图片预览 |
|||
previewImage(url){ |
|||
uni.previewImage({ |
|||
current: url, // 当前图片地址 |
|||
urls: [url] // 所有图片列表(可以是多个) |
|||
}); |
|||
}, |
|||
|
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style lang="less" scoped> |
|||
.main_box { |
|||
background: #292929; |
|||
} |
|||
|
|||
//自定义导航栏 |
|||
.navbar_section { |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: center; |
|||
background: #29d3b4; |
|||
|
|||
.title { |
|||
padding: 20rpx 0; |
|||
font-size: 30rpx; |
|||
color: #315d55; |
|||
} |
|||
} |
|||
|
|||
.main_section { |
|||
min-height: 100vh; |
|||
background: #292929 100%; |
|||
padding: 0 0rpx; |
|||
padding-top: 120rpx; |
|||
padding-bottom: 120rpx; |
|||
font-size: 28rpx; |
|||
display: flex; |
|||
flex-direction: column; |
|||
gap: 20rpx; |
|||
|
|||
.section { |
|||
background-color: #434544; |
|||
padding: 40rpx 40rpx; |
|||
} |
|||
|
|||
.section_1{ |
|||
color: #FFFFFF; |
|||
font-size: 28rpx; |
|||
padding: 0 24rpx; |
|||
display: flex; |
|||
flex-direction: column; |
|||
gap: 40rpx; |
|||
.ul{ |
|||
.time_section{ |
|||
text-align: center; |
|||
font-size: 28rpx; |
|||
color: #989898; |
|||
} |
|||
.li{ |
|||
margin: 40rpx 0; |
|||
display: flex; |
|||
justify-content: space-between; |
|||
.item{ |
|||
max-width: 70%; |
|||
padding: 32rpx; |
|||
border-radius: 32rpx; |
|||
word-wrap: break-word; /* 允许长单词或 URL 换行 */ |
|||
word-break: break-all; /* 强制所有字符换行 */ |
|||
.text_box{} |
|||
.img_box { |
|||
.chat_img { |
|||
width: 200rpx; |
|||
height: 200rpx; |
|||
border-radius: 16rpx; |
|||
} |
|||
} |
|||
} |
|||
.left_item{ |
|||
//background-color: #f4f6f9; |
|||
color: #343434; |
|||
} |
|||
.right_item{ |
|||
//background-color: #1684fc; |
|||
color: #fff; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
} |
|||
|
|||
//输入框板块 |
|||
.input_section{ |
|||
width: 100%; |
|||
position: fixed; |
|||
bottom: 0; |
|||
|
|||
padding: 30rpx; |
|||
padding-bottom: 50rpx; |
|||
display: flex; |
|||
justify-content:space-between; |
|||
align-items: center; |
|||
gap: 20rpx; |
|||
.left_box{ |
|||
.input{ |
|||
background-color: #f4f6f9; |
|||
height: 88rpx; |
|||
padding: 28rpx; |
|||
font-size: 28rpx; |
|||
border-radius: 32rpx; |
|||
|
|||
width: 480rpx; |
|||
color: #292929; |
|||
font-size: 28rpx; |
|||
} |
|||
} |
|||
.right_box{ |
|||
width: 100%; |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
.img_box{ |
|||
width: 88rpx; |
|||
height: 88rpx; |
|||
border-radius: 50%; |
|||
background-color: #a2cefe; |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: center; |
|||
.send_img{ |
|||
width: 36rpx; |
|||
height: 36rpx; |
|||
} |
|||
} |
|||
} |
|||
|
|||
} |
|||
|
|||
.describe { |
|||
color: #999999; |
|||
padding-left: 30rpx; |
|||
} |
|||
|
|||
|
|||
/* 更多选项相关 自定义内容区样式需自行控制 */ |
|||
.more_section{ |
|||
.fui-scroll__wrap { |
|||
padding-top: 30rpx; |
|||
position: relative; |
|||
} |
|||
.fui-title { |
|||
font-size: 30rpx; |
|||
font-weight: bold; |
|||
text-align: center; |
|||
padding-bottom: 24rpx; |
|||
} |
|||
.fui-icon__close { |
|||
position: absolute; |
|||
top: 24rpx; |
|||
left: 24rpx; |
|||
} |
|||
.fui-scroll__view { |
|||
width: 100%; |
|||
height: 350rpx; |
|||
} |
|||
|
|||
.ul{ |
|||
padding: 10rpx 40rpx; |
|||
display: flex; |
|||
.li{ |
|||
display: flex; |
|||
flex-direction: column; |
|||
align-items: center; |
|||
.icon_box{ |
|||
} |
|||
.title{ |
|||
font-size: 28rpx; |
|||
text-align: center; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
</style> |
|||
File diff suppressed because it is too large
@ -0,0 +1,374 @@ |
|||
<!--我的消息-列表--> |
|||
<template> |
|||
<view class="main_box"> |
|||
|
|||
<view class="main_section"> |
|||
<view class="section_1"> |
|||
|
|||
<!-- <view class="item" @click="openViewSysMsgList()">--> |
|||
<!-- <view class="left">--> |
|||
<!-- <image class="pic" :src="$util.img('/uniapp_src/static/images/common/xi_tong_xiao_xi.png')"></image>--> |
|||
<!-- <view>系统消息</view>--> |
|||
<!-- </view>--> |
|||
<!-- <view class="right">--> |
|||
<!-- <view>99</view>--> |
|||
<!-- <fui-icon name="arrowright" :size="60"></fui-icon>--> |
|||
<!-- </view>--> |
|||
<!-- </view>--> |
|||
|
|||
<view |
|||
v-for="(v,k) in contactList" |
|||
:key="k" |
|||
class="item" |
|||
@click="openViewImChatInfo(v)" |
|||
> |
|||
<view class="left"> |
|||
<image |
|||
v-if="(['1','2','3'].includes(String(userType)))" |
|||
class="pic" |
|||
:src="$util.img('/uniapp_src/static/images/common/yong_hu.png')" |
|||
model="aspectFit" |
|||
></image> |
|||
|
|||
<image v-else class="pic" :src="$util.img('/uniapp_src/static/images/common/xi_tong_xiao_xi.png')"></image> |
|||
|
|||
<view>{{v.name}}</view> |
|||
</view> |
|||
<view class="right"> |
|||
<view>{{v.count >= 99 ? '99':v.count}}</view> |
|||
<fui-icon name="arrowright" :size="60"></fui-icon> |
|||
</view> |
|||
</view> |
|||
|
|||
</view> |
|||
</view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
import apiRoute from '@/api/apiRoute.js'; |
|||
import memberApi from '@/api/member.js'; |
|||
import commonApi from '@/api/common.js'; |
|||
import AQTabber from "@/components/AQ/AQTabber" |
|||
|
|||
|
|||
export default { |
|||
components: { |
|||
AQTabber, |
|||
}, |
|||
data() { |
|||
return { |
|||
//筛选条件 |
|||
filteredData:{ |
|||
page:1,//当前页码 |
|||
limit:10,//每页返回数据条数 |
|||
total:10,//数据总条数 |
|||
personnel_id:'',//员工人力资源表id(两个参数2选1) |
|||
customer_resources_id:'',//学生资源表id(两个参数2选1) |
|||
}, |
|||
|
|||
formData: { |
|||
images_arr: [], |
|||
images: '', |
|||
content: '', |
|||
mailbox: '', |
|||
}, |
|||
|
|||
//联系人列表 |
|||
contactList:[], |
|||
userType:'',//获取用户类型|1=教练,2=销售,3=学员 |
|||
from_type: '',//发送者类型|personnel=员工,customer=学生(客户) |
|||
} |
|||
}, |
|||
onLoad() { |
|||
}, |
|||
onShow() { |
|||
this.init(); |
|||
}, |
|||
//下拉刷新 |
|||
async onPullDownRefresh() { |
|||
//重置为第一页 |
|||
await this.resetFilteredData() |
|||
await this.getList() |
|||
}, |
|||
methods: { |
|||
//初始化 |
|||
async init(){ |
|||
//获取用户类型 |
|||
this.userType = uni.getStorageSync('userType') |
|||
//1=教练,2=销售,3=学员 |
|||
if (['1', '2'].includes(String(this.userType))) { |
|||
this.from_type = 'personnel'//员工 |
|||
await this.getPersonnelUserInfo() |
|||
}else{ |
|||
//获取学生用户详情 |
|||
this.from_type = 'customer'//学生 |
|||
await this.getMemberInit() |
|||
} |
|||
this.getContactList() |
|||
}, |
|||
|
|||
//获取员工用户详情 |
|||
async getPersonnelUserInfo(){ |
|||
let data = {} |
|||
let res = await apiRoute.getPersonnelInfo(data); |
|||
if (res.code != 1){ |
|||
uni.showToast({ |
|||
title: res.msg, |
|||
icon: 'none' |
|||
}) |
|||
return |
|||
} |
|||
|
|||
res.data.cameus_dept_arr.forEach((v,k)=>{ |
|||
let d_arr = [] |
|||
v.dept_arr.forEach((dv,dk)=>{ |
|||
d_arr.push(dv.dept_name) |
|||
}) |
|||
//数组转字符串 |
|||
v.dept_name_str = d_arr.join(',') |
|||
}) |
|||
this.filteredData.personnel_id = res.data.id |
|||
}, |
|||
|
|||
//获取学员用户信息 |
|||
async getMemberInit() { |
|||
let res = await apiRoute.xy_memberInfo({}) |
|||
if(res.code != 1){ |
|||
uni.showToast({ |
|||
title: res.msg, |
|||
icon: 'none' |
|||
}) |
|||
return |
|||
} |
|||
this.member_info = res.data |
|||
this.filteredData.customer_resources_id = res.data.id//学生资源的id |
|||
}, |
|||
|
|||
//获取联系人列表 |
|||
async getContactList(){ |
|||
let params = {...this.filteredData} |
|||
//判断是否还有数据 |
|||
if ((this.filteredData.page - 1) * this.filteredData.limit >= this.filteredData.total) { |
|||
this.loading = false |
|||
uni.showToast({ |
|||
title: '暂无更多', |
|||
icon: 'none' |
|||
}) |
|||
return |
|||
} |
|||
|
|||
if(params.page == 1){ |
|||
this.contactList = [] |
|||
} |
|||
|
|||
let res |
|||
//1=教练,2=销售,3=学员 |
|||
if (['1', '2'].includes(String(this.userType))) { |
|||
//员工 |
|||
res = await apiRoute.xs_chatGetChatFriendsList(params) |
|||
}else{ |
|||
//学生 |
|||
res = await apiRoute.xy_chatGetChatFriendsList(params) |
|||
} |
|||
|
|||
|
|||
|
|||
this.loading = false |
|||
this.isReachedBottom = false; |
|||
if (res.code != 1) { |
|||
uni.showToast({ |
|||
title: res.msg, |
|||
icon: 'none' |
|||
}) |
|||
return |
|||
} |
|||
|
|||
res.data.data.forEach((v,k)=>{ |
|||
v.name = '' |
|||
v.count = '0' |
|||
if(this.from_type == 'personnel'){ |
|||
v.name = v.personnel_id_name |
|||
v.count = v.unread_count_personnel |
|||
}else{ |
|||
v.name = v.customer_resources_id_name |
|||
v.count = v.unread_count_customer_resources |
|||
} |
|||
}) |
|||
console.log('列表',res.data.data) |
|||
|
|||
this.contactList = this.contactList.concat(res.data.data); // 使用 concat 方法 将新数据追加到数组中 |
|||
this.filteredData.total = res.data.total |
|||
this.filteredData.page++ |
|||
}, |
|||
|
|||
//######AQ上传文件组件相关###### |
|||
// 上传文件回调 |
|||
AQUploadSuccess(res) { |
|||
console.log('接收AQ上传回调xxx1', res) |
|||
// 使用 split 方法分割字符串 |
|||
let _inputValue = [] |
|||
if (res.filePathArr.length) { |
|||
_inputValue = res.filePathArr |
|||
} |
|||
this.formData[res.inputName] = _inputValue |
|||
// console.log('接收AQ上传回调xxx1',res) |
|||
// console.log('接收AQ上传回调xxx2',this.formData.member_store_certification_arr) |
|||
}, |
|||
|
|||
async submitForm() { |
|||
let data = {...this.formData} |
|||
data.images = data.images_arr.join(',') |
|||
if (!data.content) { |
|||
//反馈内容为必填项 |
|||
uni.showToast({ |
|||
title: '反馈内容为必填项', |
|||
icon: 'none' |
|||
}) |
|||
return |
|||
} |
|||
let res = await memberApi.setFeedback(data) |
|||
if (res.code != 1) { |
|||
uni.showToast({ |
|||
title: res.msg, |
|||
icon: 'none' |
|||
}) |
|||
} else { |
|||
uni.showToast({ |
|||
title: '提交成功', |
|||
icon: 'none' |
|||
}) |
|||
} |
|||
}, |
|||
|
|||
//跳转页面-系统消息列表 |
|||
openViewSysMsgList(e){ |
|||
let hair_staff_id = e.hair_staff_id//发信人id |
|||
uni.navigateTo({ |
|||
url: `/pages-common/sys_msg_list?hair_staff_id=${hair_staff_id}` |
|||
}) |
|||
}, |
|||
|
|||
//跳转页面-聊天页面 |
|||
openViewImChatInfo(e){ |
|||
|
|||
if(e.type == 1){ |
|||
//系统消息 |
|||
this.openViewSysMsgList(e) |
|||
}else{ |
|||
let from_id = ''//发送者的id |
|||
let to_id = ''//接收者ID |
|||
//站内信 |
|||
if (this.from_type == 'personnel') { |
|||
from_id = e.personnel_id |
|||
to_id = e.customer_resources_id |
|||
} else { |
|||
from_id = e.customer_resources_id |
|||
to_id = e.personnel_id |
|||
} |
|||
|
|||
uni.navigateTo({ |
|||
url: `/pages-common/im_chat_info?from_id=${from_id}&to_id=${to_id}` |
|||
}) |
|||
} |
|||
|
|||
|
|||
}, |
|||
|
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style lang="less" scoped> |
|||
.main_box { |
|||
background: #292929; |
|||
} |
|||
|
|||
//自定义导航栏 |
|||
.navbar_section { |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: center; |
|||
background: #29d3b4; |
|||
|
|||
.title { |
|||
padding: 20rpx 0; |
|||
font-size: 30rpx; |
|||
color: #315d55; |
|||
} |
|||
} |
|||
|
|||
.main_section { |
|||
min-height: 100vh; |
|||
background: #292929 100%; |
|||
padding: 0 0rpx; |
|||
padding-top: 32rpx; |
|||
padding-bottom: 150rpx; |
|||
font-size: 28rpx; |
|||
display: flex; |
|||
flex-direction: column; |
|||
gap: 20rpx; |
|||
|
|||
.section { |
|||
background-color: #434544; |
|||
padding: 40rpx 40rpx; |
|||
} |
|||
|
|||
.section_1{ |
|||
padding: 0 24rpx; |
|||
display: flex; |
|||
flex-direction: column; |
|||
gap: 38rpx; |
|||
.item{ |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
|
|||
padding: 40rpx 18rpx 36rpx; |
|||
|
|||
border-radius: 14rpx; |
|||
background-color: rgba(255,255,255,1); |
|||
border: 2rpx solid rgba(187,187,187,1); |
|||
|
|||
.left{ |
|||
display: flex; |
|||
align-items: center; |
|||
gap: 16rpx; |
|||
.pic{ |
|||
width: 68rpx; |
|||
height: 68rpx; |
|||
border-radius: 50%; |
|||
} |
|||
view{ |
|||
color: #D9001B; |
|||
font-size: 32rpx; |
|||
} |
|||
} |
|||
.right{ |
|||
display: flex; |
|||
align-items: center; |
|||
view{ |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: center; |
|||
width: 50rpx; |
|||
height: 50rpx; |
|||
padding: 5rpx; |
|||
border: 1px solid #BBBBBB; |
|||
border-radius: 50%; |
|||
color: #D9001B; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
|
|||
|
|||
} |
|||
|
|||
.describe { |
|||
color: #999999; |
|||
padding-left: 30rpx; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,899 @@ |
|||
<template> |
|||
<view class="personnel-form-container"> |
|||
<!-- 自定义导航栏 --> |
|||
<view class="custom-nav"> |
|||
<view class="nav-left" @click="goBack"> |
|||
<text class="iconfont icon-arrow-left"></text> |
|||
</view> |
|||
<view class="nav-title">新员工信息填写</view> |
|||
<view class="nav-right"></view> |
|||
</view> |
|||
|
|||
<!-- 进度条 --> |
|||
<view class="progress-container"> |
|||
<view class="progress-bar"> |
|||
<view class="progress-step" :class="currentStep >= 1 ? 'active' : ''"> |
|||
<text class="step-number">1</text> |
|||
<text class="step-text">基本信息</text> |
|||
</view> |
|||
<view class="progress-line" :class="currentStep >= 2 ? 'active' : ''"></view> |
|||
<view class="progress-step" :class="currentStep >= 2 ? 'active' : ''"> |
|||
<text class="step-number">2</text> |
|||
<text class="step-text">详细信息</text> |
|||
</view> |
|||
<view class="progress-line" :class="currentStep >= 3 ? 'active' : ''"></view> |
|||
<view class="progress-step" :class="currentStep >= 3 ? 'active' : ''"> |
|||
<text class="step-number">3</text> |
|||
<text class="step-text">确认提交</text> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 表单内容 --> |
|||
<view class="form-content"> |
|||
<!-- 第一步:基本信息 --> |
|||
<view v-if="currentStep === 1" class="step-content"> |
|||
<view class="section-title">基本信息</view> |
|||
|
|||
<!-- 头像上传 --> |
|||
<view class="form-item"> |
|||
<view class="label">头像</view> |
|||
<view class="avatar-upload" @click="chooseAvatar"> |
|||
<image v-if="formData.head_img" :src="formData.head_img" class="avatar-preview"></image> |
|||
<view v-else class="avatar-placeholder"> |
|||
<text class="iconfont icon-camera"></text> |
|||
<text class="placeholder-text">点击上传头像</text> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 姓名 --> |
|||
<view class="form-item required"> |
|||
<view class="label">姓名</view> |
|||
<input class="form-input" v-model="formData.name" placeholder="请输入姓名" /> |
|||
</view> |
|||
|
|||
<!-- 性别 --> |
|||
<view class="form-item required"> |
|||
<view class="label">性别</view> |
|||
<view class="radio-group"> |
|||
<label class="radio-item" @click="formData.gender = 1"> |
|||
<view class="radio" :class="formData.gender === 1 ? 'checked' : ''"></view> |
|||
<text>男</text> |
|||
</label> |
|||
<label class="radio-item" @click="formData.gender = 0"> |
|||
<view class="radio" :class="formData.gender === 0 ? 'checked' : ''"></view> |
|||
<text>女</text> |
|||
</label> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 生日 --> |
|||
<view class="form-item"> |
|||
<view class="label">出生日期</view> |
|||
<picker mode="date" :value="formData.birthday" @change="onBirthdayChange"> |
|||
<view class="picker-input"> |
|||
{{formData.birthday || '请选择出生日期'}} |
|||
</view> |
|||
</picker> |
|||
</view> |
|||
|
|||
<!-- 手机号 --> |
|||
<view class="form-item required"> |
|||
<view class="label">手机号码</view> |
|||
<input class="form-input" v-model="formData.phone" placeholder="请输入手机号码" type="number" /> |
|||
</view> |
|||
|
|||
<!-- 邮箱 --> |
|||
<view class="form-item"> |
|||
<view class="label">邮箱</view> |
|||
<input class="form-input" v-model="formData.email" placeholder="请输入邮箱地址" /> |
|||
</view> |
|||
|
|||
<!-- 微信号 --> |
|||
<view class="form-item"> |
|||
<view class="label">微信号</view> |
|||
<input class="form-input" v-model="formData.wx" placeholder="请输入微信号" /> |
|||
</view> |
|||
|
|||
<!-- 账号类型 --> |
|||
<view class="form-item required"> |
|||
<view class="label">职位类型</view> |
|||
<view class="radio-group"> |
|||
<label class="radio-item" @click="formData.account_type = 'teacher'"> |
|||
<view class="radio" :class="formData.account_type === 'teacher' ? 'checked' : ''"></view> |
|||
<text>教师</text> |
|||
</label> |
|||
<label class="radio-item" @click="formData.account_type = 'market'"> |
|||
<view class="radio" :class="formData.account_type === 'market' ? 'checked' : ''"></view> |
|||
<text>市场</text> |
|||
</label> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 入职时间 --> |
|||
<view class="form-item"> |
|||
<view class="label">入职时间</view> |
|||
<picker mode="date" :value="formData.join_time" @change="onJoinTimeChange"> |
|||
<view class="picker-input"> |
|||
{{formData.join_time || '请选择入职时间'}} |
|||
</view> |
|||
</picker> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 第二步:详细信息 --> |
|||
<view v-if="currentStep === 2" class="step-content"> |
|||
<view class="section-title">详细信息</view> |
|||
|
|||
<!-- 民族 --> |
|||
<view class="form-item"> |
|||
<view class="label">民族</view> |
|||
<input class="form-input" v-model="detailData.ethnicity" placeholder="请输入民族" /> |
|||
</view> |
|||
|
|||
<!-- 年龄 --> |
|||
<view class="form-item"> |
|||
<view class="label">年龄</view> |
|||
<input class="form-input" v-model.number="detailData.age" placeholder="请输入年龄" type="number" /> |
|||
</view> |
|||
|
|||
<!-- 政治面貌 --> |
|||
<view class="form-item"> |
|||
<view class="label">政治面貌</view> |
|||
<picker :range="politicsOptions" @change="onPoliticsChange"> |
|||
<view class="picker-input"> |
|||
{{detailData.politics || '请选择政治面貌'}} |
|||
</view> |
|||
</picker> |
|||
</view> |
|||
|
|||
<!-- 毕业院校 --> |
|||
<view class="form-item"> |
|||
<view class="label">毕业院校</view> |
|||
<input class="form-input" v-model="detailData.university" placeholder="请输入毕业院校" /> |
|||
</view> |
|||
|
|||
<!-- 学历 --> |
|||
<view class="form-item"> |
|||
<view class="label">学历</view> |
|||
<picker :range="educationOptions" @change="onEducationChange"> |
|||
<view class="picker-input"> |
|||
{{detailData.education || '请选择学历'}} |
|||
</view> |
|||
</picker> |
|||
</view> |
|||
|
|||
<!-- 专业 --> |
|||
<view class="form-item"> |
|||
<view class="label">专业</view> |
|||
<input class="form-input" v-model="detailData.major" placeholder="请输入专业" /> |
|||
</view> |
|||
|
|||
<!-- 毕业日期 --> |
|||
<view class="form-item"> |
|||
<view class="label">毕业日期</view> |
|||
<picker mode="date" :value="detailData.graduation_date" @change="onGraduationDateChange"> |
|||
<view class="picker-input"> |
|||
{{detailData.graduation_date || '请选择毕业日期'}} |
|||
</view> |
|||
</picker> |
|||
</view> |
|||
|
|||
<!-- 籍贯 --> |
|||
<view class="form-item"> |
|||
<view class="label">籍贯</view> |
|||
<input class="form-input" v-model="detailData.native_place" placeholder="请输入籍贯" /> |
|||
</view> |
|||
|
|||
<!-- 户口所在地 --> |
|||
<view class="form-item"> |
|||
<view class="label">户口所在地</view> |
|||
<input class="form-input" v-model="detailData.household_place" placeholder="请输入户口所在地" /> |
|||
</view> |
|||
|
|||
<!-- 户口性质 --> |
|||
<view class="form-item"> |
|||
<view class="label">户口性质</view> |
|||
<picker :range="householdTypeOptions" @change="onHouseholdTypeChange"> |
|||
<view class="picker-input"> |
|||
{{detailData.household_type || '请选择户口性质'}} |
|||
</view> |
|||
</picker> |
|||
</view> |
|||
|
|||
<!-- 户籍地址 --> |
|||
<view class="form-item"> |
|||
<view class="label">户籍地址</view> |
|||
<textarea class="form-textarea" v-model="detailData.household_address" placeholder="请输入户籍地址"></textarea> |
|||
</view> |
|||
|
|||
<!-- 现居地址 --> |
|||
<view class="form-item"> |
|||
<view class="label">现居地址</view> |
|||
<textarea class="form-textarea" v-model="detailData.current_address" placeholder="请输入现居地址"></textarea> |
|||
</view> |
|||
|
|||
<!-- 紧急联系人 --> |
|||
<view class="form-item"> |
|||
<view class="label">紧急联系人</view> |
|||
<input class="form-input" v-model="detailData.emergency_contact" placeholder="请输入紧急联系人姓名" /> |
|||
</view> |
|||
|
|||
<!-- 紧急联系电话 --> |
|||
<view class="form-item"> |
|||
<view class="label">紧急联系电话</view> |
|||
<input class="form-input" v-model="detailData.emergency_phone" placeholder="请输入紧急联系电话" type="number" /> |
|||
</view> |
|||
|
|||
<!-- 婚姻状况 --> |
|||
<view class="form-item"> |
|||
<view class="label">婚姻状况</view> |
|||
<picker :range="maritalStatusOptions" @change="onMaritalStatusChange"> |
|||
<view class="picker-input"> |
|||
{{detailData.marital_status || '请选择婚姻状况'}} |
|||
</view> |
|||
</picker> |
|||
</view> |
|||
|
|||
<!-- 银行卡号 --> |
|||
<view class="form-item"> |
|||
<view class="label">银行卡号</view> |
|||
<input class="form-input" v-model="detailData.bank_card" placeholder="请输入银行卡号" type="number" /> |
|||
</view> |
|||
|
|||
<!-- 开户银行 --> |
|||
<view class="form-item"> |
|||
<view class="label">开户银行</view> |
|||
<input class="form-input" v-model="detailData.bank_name" placeholder="请输入开户银行" /> |
|||
</view> |
|||
|
|||
<!-- 备注 --> |
|||
<view class="form-item"> |
|||
<view class="label">备注</view> |
|||
<textarea class="form-textarea" v-model="detailData.remark" placeholder="请输入备注信息"></textarea> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 第三步:确认信息 --> |
|||
<view v-if="currentStep === 3" class="step-content"> |
|||
<view class="section-title">确认信息</view> |
|||
<view class="confirm-info"> |
|||
<view class="info-section"> |
|||
<view class="info-title">基本信息</view> |
|||
<view class="info-item"> |
|||
<text class="info-label">姓名:</text> |
|||
<text class="info-value">{{formData.name}}</text> |
|||
</view> |
|||
<view class="info-item"> |
|||
<text class="info-label">性别:</text> |
|||
<text class="info-value">{{formData.gender === 1 ? '男' : '女'}}</text> |
|||
</view> |
|||
<view class="info-item"> |
|||
<text class="info-label">手机:</text> |
|||
<text class="info-value">{{formData.phone}}</text> |
|||
</view> |
|||
<view class="info-item"> |
|||
<text class="info-label">职位:</text> |
|||
<text class="info-value">{{formData.account_type === 'teacher' ? '教师' : '市场'}}</text> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="info-section"> |
|||
<view class="info-title">详细信息</view> |
|||
<view class="info-item" v-if="detailData.education"> |
|||
<text class="info-label">学历:</text> |
|||
<text class="info-value">{{detailData.education}}</text> |
|||
</view> |
|||
<view class="info-item" v-if="detailData.university"> |
|||
<text class="info-label">毕业院校:</text> |
|||
<text class="info-value">{{detailData.university}}</text> |
|||
</view> |
|||
<view class="info-item" v-if="detailData.emergency_contact"> |
|||
<text class="info-label">紧急联系人:</text> |
|||
<text class="info-value">{{detailData.emergency_contact}}</text> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 底部按钮 --> |
|||
<view class="footer-buttons"> |
|||
<button v-if="currentStep > 1" class="btn btn-secondary" @click="prevStep">上一步</button> |
|||
<button v-if="currentStep < 3" class="btn btn-primary" @click="nextStep">下一步</button> |
|||
<button v-if="currentStep === 3" class="btn btn-primary" @click="submitForm" :loading="submitting">提交</button> |
|||
</view> |
|||
|
|||
<!-- 加载提示 --> |
|||
<uni-load-more v-if="submitting" status="loading" content-text="正在提交..."></uni-load-more> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
import apiRoute from '@/common/axios.js' |
|||
|
|||
export default { |
|||
data() { |
|||
return { |
|||
currentStep: 1, |
|||
submitting: false, |
|||
// 基本信息 |
|||
formData: { |
|||
name: '', |
|||
head_img: '', |
|||
gender: null, |
|||
birthday: '', |
|||
phone: '', |
|||
email: '', |
|||
wx: '', |
|||
account_type: '', |
|||
join_time: '' |
|||
}, |
|||
// 详细信息 |
|||
detailData: { |
|||
ethnicity: '', |
|||
age: null, |
|||
politics: '', |
|||
university: '', |
|||
education: '', |
|||
major: '', |
|||
graduation_date: '', |
|||
native_place: '', |
|||
household_place: '', |
|||
household_type: '', |
|||
household_address: '', |
|||
current_address: '', |
|||
emergency_contact: '', |
|||
emergency_phone: '', |
|||
marital_status: '', |
|||
bank_card: '', |
|||
bank_name: '', |
|||
remark: '' |
|||
}, |
|||
// 选项数据 |
|||
politicsOptions: ['群众', '共青团员', '中共党员', '民主党派', '无党派人士'], |
|||
educationOptions: ['高中', '中专', '大专', '本科', '硕士', '博士'], |
|||
householdTypeOptions: ['城镇', '农村'], |
|||
maritalStatusOptions: ['未婚', '已婚', '离异', '丧偶'] |
|||
} |
|||
}, |
|||
onLoad() { |
|||
// 设置当前日期为默认入职时间 |
|||
const today = new Date() |
|||
const year = today.getFullYear() |
|||
const month = String(today.getMonth() + 1).padStart(2, '0') |
|||
const day = String(today.getDate()).padStart(2, '0') |
|||
this.formData.join_time = `${year}-${month}-${day}` |
|||
}, |
|||
methods: { |
|||
// 返回上一页 |
|||
goBack() { |
|||
uni.navigateBack() |
|||
}, |
|||
|
|||
// 选择头像 |
|||
chooseAvatar() { |
|||
uni.chooseImage({ |
|||
count: 1, |
|||
sizeType: ['compressed'], |
|||
sourceType: ['album', 'camera'], |
|||
success: (res) => { |
|||
const tempFilePath = res.tempFilePaths[0] |
|||
this.uploadAvatar(tempFilePath) |
|||
} |
|||
}) |
|||
}, |
|||
|
|||
// 上传头像 |
|||
uploadAvatar(filePath) { |
|||
uni.showLoading({ title: '上传中...' }) |
|||
|
|||
uni.uploadFile({ |
|||
url: this.$baseUrl + '/file/avatar', |
|||
filePath: filePath, |
|||
name: 'file', |
|||
success: (res) => { |
|||
const data = JSON.parse(res.data) |
|||
if (data.code === 1) { |
|||
this.formData.head_img = data.data.url |
|||
uni.showToast({ title: '头像上传成功', icon: 'success' }) |
|||
} else { |
|||
uni.showToast({ title: data.msg || '上传失败', icon: 'none' }) |
|||
} |
|||
}, |
|||
fail: (error) => { |
|||
console.error('头像上传失败:', error) |
|||
uni.showToast({ title: '上传失败', icon: 'none' }) |
|||
}, |
|||
complete: () => { |
|||
uni.hideLoading() |
|||
} |
|||
}) |
|||
}, |
|||
|
|||
// 日期选择事件 |
|||
onBirthdayChange(e) { |
|||
this.formData.birthday = e.detail.value |
|||
// 自动计算年龄 |
|||
this.calculateAge() |
|||
}, |
|||
|
|||
onJoinTimeChange(e) { |
|||
this.formData.join_time = e.detail.value |
|||
}, |
|||
|
|||
onGraduationDateChange(e) { |
|||
this.detailData.graduation_date = e.detail.value |
|||
}, |
|||
|
|||
// 下拉选择事件 |
|||
onPoliticsChange(e) { |
|||
this.detailData.politics = this.politicsOptions[e.detail.value] |
|||
}, |
|||
|
|||
onEducationChange(e) { |
|||
this.detailData.education = this.educationOptions[e.detail.value] |
|||
}, |
|||
|
|||
onHouseholdTypeChange(e) { |
|||
this.detailData.household_type = this.householdTypeOptions[e.detail.value] |
|||
}, |
|||
|
|||
onMaritalStatusChange(e) { |
|||
this.detailData.marital_status = this.maritalStatusOptions[e.detail.value] |
|||
}, |
|||
|
|||
// 计算年龄 |
|||
calculateAge() { |
|||
if (this.formData.birthday) { |
|||
const birthDate = new Date(this.formData.birthday) |
|||
const today = new Date() |
|||
let age = today.getFullYear() - birthDate.getFullYear() |
|||
const monthDiff = today.getMonth() - birthDate.getMonth() |
|||
|
|||
if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birthDate.getDate())) { |
|||
age-- |
|||
} |
|||
|
|||
this.detailData.age = age |
|||
} |
|||
}, |
|||
|
|||
// 步骤控制 |
|||
nextStep() { |
|||
if (this.validateCurrentStep()) { |
|||
this.currentStep++ |
|||
} |
|||
}, |
|||
|
|||
prevStep() { |
|||
this.currentStep-- |
|||
}, |
|||
|
|||
// 验证当前步骤 |
|||
validateCurrentStep() { |
|||
if (this.currentStep === 1) { |
|||
if (!this.formData.name) { |
|||
uni.showToast({ title: '请输入姓名', icon: 'none' }) |
|||
return false |
|||
} |
|||
if (this.formData.gender === null) { |
|||
uni.showToast({ title: '请选择性别', icon: 'none' }) |
|||
return false |
|||
} |
|||
if (!this.formData.phone) { |
|||
uni.showToast({ title: '请输入手机号码', icon: 'none' }) |
|||
return false |
|||
} |
|||
if (!/^1[3-9]\d{9}$/.test(this.formData.phone)) { |
|||
uni.showToast({ title: '请输入正确的手机号码', icon: 'none' }) |
|||
return false |
|||
} |
|||
if (!this.formData.account_type) { |
|||
uni.showToast({ title: '请选择职位类型', icon: 'none' }) |
|||
return false |
|||
} |
|||
} |
|||
return true |
|||
}, |
|||
|
|||
// 提交表单 |
|||
async submitForm() { |
|||
if (!this.validateCurrentStep()) { |
|||
return |
|||
} |
|||
|
|||
this.submitting = true |
|||
|
|||
try { |
|||
const submitData = { |
|||
...this.formData, |
|||
...this.detailData |
|||
} |
|||
|
|||
const response = await apiRoute.post('personnel/add', submitData) |
|||
|
|||
if (response.data.code === 1) { |
|||
uni.showToast({ |
|||
title: '员工信息提交成功', |
|||
icon: 'success', |
|||
duration: 2000 |
|||
}) |
|||
|
|||
setTimeout(() => { |
|||
uni.navigateBack() |
|||
}, 2000) |
|||
} else { |
|||
uni.showToast({ |
|||
title: response.data.msg || '提交失败', |
|||
icon: 'none' |
|||
}) |
|||
} |
|||
} catch (error) { |
|||
console.error('提交员工信息失败:', error) |
|||
uni.showToast({ |
|||
title: '网络错误,请稍后重试', |
|||
icon: 'none' |
|||
}) |
|||
} finally { |
|||
this.submitting = false |
|||
} |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style lang="scss" scoped> |
|||
.personnel-form-container { |
|||
min-height: 100vh; |
|||
background-color: #f5f5f5; |
|||
padding-bottom: 120rpx; |
|||
} |
|||
|
|||
/* 自定义导航栏 */ |
|||
.custom-nav { |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: space-between; |
|||
height: 88rpx; |
|||
padding: 0 32rpx; |
|||
background-color: #fff; |
|||
border-bottom: 1rpx solid #e5e5e5; |
|||
position: sticky; |
|||
top: 0; |
|||
z-index: 100; |
|||
|
|||
.nav-left { |
|||
width: 80rpx; |
|||
height: 80rpx; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
|
|||
.iconfont { |
|||
font-size: 36rpx; |
|||
color: #333; |
|||
} |
|||
} |
|||
|
|||
.nav-title { |
|||
font-size: 36rpx; |
|||
font-weight: 600; |
|||
color: #333; |
|||
} |
|||
|
|||
.nav-right { |
|||
width: 80rpx; |
|||
} |
|||
} |
|||
|
|||
/* 进度条 */ |
|||
.progress-container { |
|||
background-color: #fff; |
|||
padding: 40rpx 32rpx; |
|||
margin-bottom: 20rpx; |
|||
} |
|||
|
|||
.progress-bar { |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: space-between; |
|||
} |
|||
|
|||
.progress-step { |
|||
display: flex; |
|||
flex-direction: column; |
|||
align-items: center; |
|||
flex: 1; |
|||
|
|||
.step-number { |
|||
width: 60rpx; |
|||
height: 60rpx; |
|||
border-radius: 50%; |
|||
background-color: #e5e5e5; |
|||
color: #999; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
font-size: 28rpx; |
|||
font-weight: 600; |
|||
margin-bottom: 12rpx; |
|||
} |
|||
|
|||
.step-text { |
|||
font-size: 24rpx; |
|||
color: #999; |
|||
} |
|||
|
|||
&.active { |
|||
.step-number { |
|||
background-color: #007ACC; |
|||
color: #fff; |
|||
} |
|||
|
|||
.step-text { |
|||
color: #007ACC; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.progress-line { |
|||
flex: 1; |
|||
height: 4rpx; |
|||
background-color: #e5e5e5; |
|||
margin: 0 20rpx; |
|||
margin-top: -30rpx; |
|||
|
|||
&.active { |
|||
background-color: #007ACC; |
|||
} |
|||
} |
|||
|
|||
/* 表单内容 */ |
|||
.form-content { |
|||
background-color: #fff; |
|||
margin: 0 20rpx 20rpx; |
|||
border-radius: 16rpx; |
|||
padding: 32rpx; |
|||
} |
|||
|
|||
.section-title { |
|||
font-size: 36rpx; |
|||
font-weight: 600; |
|||
color: #333; |
|||
margin-bottom: 40rpx; |
|||
border-left: 8rpx solid #007ACC; |
|||
padding-left: 20rpx; |
|||
} |
|||
|
|||
.form-item { |
|||
margin-bottom: 40rpx; |
|||
|
|||
&.required .label::before { |
|||
content: '*'; |
|||
color: #ff4d4f; |
|||
margin-right: 8rpx; |
|||
} |
|||
|
|||
.label { |
|||
font-size: 28rpx; |
|||
color: #333; |
|||
margin-bottom: 16rpx; |
|||
font-weight: 500; |
|||
} |
|||
|
|||
.form-input, |
|||
.picker-input { |
|||
width: 100%; |
|||
height: 88rpx; |
|||
border: 2rpx solid #e5e5e5; |
|||
border-radius: 12rpx; |
|||
padding: 0 24rpx; |
|||
font-size: 28rpx; |
|||
color: #333; |
|||
background-color: #fff; |
|||
box-sizing: border-box; |
|||
display: flex; |
|||
align-items: center; |
|||
|
|||
&:focus { |
|||
border-color: #007ACC; |
|||
} |
|||
} |
|||
|
|||
.form-textarea { |
|||
width: 100%; |
|||
min-height: 120rpx; |
|||
border: 2rpx solid #e5e5e5; |
|||
border-radius: 12rpx; |
|||
padding: 20rpx 24rpx; |
|||
font-size: 28rpx; |
|||
color: #333; |
|||
background-color: #fff; |
|||
box-sizing: border-box; |
|||
resize: none; |
|||
|
|||
&:focus { |
|||
border-color: #007ACC; |
|||
} |
|||
} |
|||
|
|||
.picker-input { |
|||
color: #999; |
|||
cursor: pointer; |
|||
} |
|||
} |
|||
|
|||
/* 头像上传 */ |
|||
.avatar-upload { |
|||
width: 120rpx; |
|||
height: 120rpx; |
|||
border: 2rpx solid #e5e5e5; |
|||
border-radius: 50%; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
overflow: hidden; |
|||
cursor: pointer; |
|||
|
|||
.avatar-preview { |
|||
width: 100%; |
|||
height: 100%; |
|||
border-radius: 50%; |
|||
} |
|||
|
|||
.avatar-placeholder { |
|||
display: flex; |
|||
flex-direction: column; |
|||
align-items: center; |
|||
justify-content: center; |
|||
color: #999; |
|||
|
|||
.iconfont { |
|||
font-size: 32rpx; |
|||
margin-bottom: 8rpx; |
|||
} |
|||
|
|||
.placeholder-text { |
|||
font-size: 20rpx; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/* 单选按钮组 */ |
|||
.radio-group { |
|||
display: flex; |
|||
gap: 60rpx; |
|||
} |
|||
|
|||
.radio-item { |
|||
display: flex; |
|||
align-items: center; |
|||
cursor: pointer; |
|||
|
|||
.radio { |
|||
width: 32rpx; |
|||
height: 32rpx; |
|||
border: 2rpx solid #e5e5e5; |
|||
border-radius: 50%; |
|||
margin-right: 16rpx; |
|||
position: relative; |
|||
|
|||
&.checked { |
|||
border-color: #007ACC; |
|||
|
|||
&::after { |
|||
content: ''; |
|||
width: 16rpx; |
|||
height: 16rpx; |
|||
background-color: #007ACC; |
|||
border-radius: 50%; |
|||
position: absolute; |
|||
top: 50%; |
|||
left: 50%; |
|||
transform: translate(-50%, -50%); |
|||
} |
|||
} |
|||
} |
|||
|
|||
text { |
|||
font-size: 28rpx; |
|||
color: #333; |
|||
} |
|||
} |
|||
|
|||
/* 确认信息 */ |
|||
.confirm-info { |
|||
.info-section { |
|||
margin-bottom: 40rpx; |
|||
|
|||
.info-title { |
|||
font-size: 32rpx; |
|||
font-weight: 600; |
|||
color: #333; |
|||
margin-bottom: 24rpx; |
|||
border-left: 6rpx solid #007ACC; |
|||
padding-left: 16rpx; |
|||
} |
|||
|
|||
.info-item { |
|||
display: flex; |
|||
margin-bottom: 16rpx; |
|||
|
|||
.info-label { |
|||
font-size: 28rpx; |
|||
color: #666; |
|||
width: 120rpx; |
|||
flex-shrink: 0; |
|||
} |
|||
|
|||
.info-value { |
|||
font-size: 28rpx; |
|||
color: #333; |
|||
flex: 1; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
/* 底部按钮 */ |
|||
.footer-buttons { |
|||
position: fixed; |
|||
bottom: 0; |
|||
left: 0; |
|||
right: 0; |
|||
background-color: #fff; |
|||
padding: 24rpx 32rpx 24rpx; |
|||
border-top: 1rpx solid #e5e5e5; |
|||
display: flex; |
|||
gap: 24rpx; |
|||
z-index: 100; |
|||
|
|||
.btn { |
|||
flex: 1; |
|||
height: 88rpx; |
|||
border-radius: 12rpx; |
|||
font-size: 32rpx; |
|||
font-weight: 600; |
|||
border: none; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
|
|||
&.btn-primary { |
|||
background-color: #007ACC; |
|||
color: #fff; |
|||
|
|||
&:active { |
|||
background-color: #0056b3; |
|||
} |
|||
} |
|||
|
|||
&.btn-secondary { |
|||
background-color: #f5f5f5; |
|||
color: #666; |
|||
|
|||
&:active { |
|||
background-color: #e5e5e5; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
/* 步骤内容动画 */ |
|||
.step-content { |
|||
animation: fadeIn 0.3s ease-in-out; |
|||
} |
|||
|
|||
@keyframes fadeIn { |
|||
from { |
|||
opacity: 0; |
|||
transform: translateX(20rpx); |
|||
} |
|||
to { |
|||
opacity: 1; |
|||
transform: translateX(0); |
|||
} |
|||
} |
|||
</style> |
|||
@ -0,0 +1,111 @@ |
|||
<template> |
|||
<view class="assemble"> |
|||
<view class="html-style" v-html="text"></view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
import agreement from '@/api/agreement.js'; |
|||
export default { |
|||
data() { |
|||
return { |
|||
type: 1, //类型 1为用户协议,2为隐私策略 |
|||
text: '', |
|||
} |
|||
}, |
|||
onLoad(item) { |
|||
console.log(item.type, '类型') |
|||
this.init(item.type) |
|||
}, |
|||
methods: { |
|||
//初始化拿数据的接口 |
|||
init(type) { |
|||
if (type == 1) { |
|||
//获取用户协议 |
|||
agreement.userAgreement('service').then(res => { |
|||
if(res.code == 1){ |
|||
this.text = res.data.content |
|||
}else{ |
|||
this.text = '' |
|||
} |
|||
}) |
|||
uni.setNavigationBarTitle({ |
|||
title: '用户协议' |
|||
}); |
|||
} else if (type == 2) { |
|||
//获取隐私策略 |
|||
agreement.userAgreement('privacy').then(res => { |
|||
if(res.code == 1){ |
|||
this.text = res.data.content |
|||
}else{ |
|||
this.text = '' |
|||
} |
|||
}) |
|||
uni.setNavigationBarTitle({ |
|||
title: '隐私策略' |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style lang="scss" scoped> |
|||
.assemble { |
|||
width: 100%; |
|||
height: 100vh; |
|||
overflow: auto; |
|||
background-color: $bg-color-white; |
|||
} |
|||
|
|||
.html-style { |
|||
padding: 30rpx 40rpx; |
|||
line-height: 1.8; |
|||
font-size: $font-size-base; |
|||
color: $text-color-base; |
|||
|
|||
// 富文本内容样式优化 |
|||
::v-deep p { |
|||
margin-bottom: 20rpx; |
|||
line-height: 1.6; |
|||
} |
|||
|
|||
::v-deep h1, ::v-deep h2, ::v-deep h3 { |
|||
color: $color-primary; |
|||
margin: 30rpx 0 20rpx 0; |
|||
font-weight: bold; |
|||
} |
|||
|
|||
::v-deep h1 { |
|||
font-size: $font-size-extra-lg; |
|||
} |
|||
|
|||
::v-deep h2 { |
|||
font-size: $font-size-lg; |
|||
} |
|||
|
|||
::v-deep h3 { |
|||
font-size: $font-size-medium; |
|||
} |
|||
|
|||
::v-deep ul, ::v-deep ol { |
|||
margin: 20rpx 0; |
|||
padding-left: 40rpx; |
|||
} |
|||
|
|||
::v-deep li { |
|||
margin-bottom: 10rpx; |
|||
line-height: 1.6; |
|||
} |
|||
|
|||
::v-deep strong { |
|||
color: $text-color-base; |
|||
font-weight: bold; |
|||
} |
|||
|
|||
::v-deep a { |
|||
color: $color-primary; |
|||
text-decoration: underline; |
|||
} |
|||
} |
|||
</style> |
|||
@ -0,0 +1,259 @@ |
|||
<template> |
|||
<view class="profile-container"> |
|||
<!-- 自定义导航栏 --> |
|||
<uni-nav-bar |
|||
:statusBar="true" |
|||
backgroundColor="#181A20" |
|||
color="#fff" |
|||
title="我的" |
|||
/> |
|||
|
|||
<!-- 用户头像和基本信息 --> |
|||
<view class="profile-header"> |
|||
<view class="avatar-section"> |
|||
<image :src="userInfo.avatar || $util.img('/static/icon-img/tou.png')" mode="aspectFill"></image> |
|||
</view> |
|||
<view class="user-info"> |
|||
<text class="user-name">{{ userInfo.name || '员工姓名' }}</text> |
|||
<text class="user-role">{{ (userInfo.role_info && userInfo.role_info.role_name) || '员工角色' }}</text> |
|||
<text class="user-phone">{{ userInfo.phone || '手机号码' }}</text> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 我的功能九宫格 --> |
|||
<view class="grid-container"> |
|||
<view class="grid-title">个人中心</view> |
|||
<view class="grid-content"> |
|||
<view |
|||
class="grid-item" |
|||
v-for="(item, index) in profileItems" |
|||
:key="index" |
|||
@click="handleProfileClick(item)" |
|||
> |
|||
<view class="grid-icon"> |
|||
<uni-icons :type="item.icon" size="32" color="#29d3b4"></uni-icons> |
|||
</view> |
|||
<text class="grid-text">{{ item.title }}</text> |
|||
<text class="grid-desc" v-if="item.desc">{{ item.desc }}</text> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
export default { |
|||
data() { |
|||
return { |
|||
userInfo: {}, |
|||
profileItems: [ |
|||
{ |
|||
title: '我的资料', |
|||
icon: 'contact', |
|||
desc: '查看编辑个人信息', |
|||
action: 'viewProfile' |
|||
}, |
|||
{ |
|||
title: '我的合同', |
|||
icon: 'compose', |
|||
desc: '查看签署合同', |
|||
path: '/pages-common/contract/my_contract' |
|||
}, |
|||
{ |
|||
title: '我的工资', |
|||
icon: 'wallet', |
|||
desc: '查看工资明细', |
|||
action: 'viewSalary' |
|||
}, |
|||
{ |
|||
title: '我的考勤', |
|||
icon: 'calendar', |
|||
path: '/pages-common/my_attendance' |
|||
}, |
|||
{ |
|||
title: '系统设置', |
|||
icon: 'settings', |
|||
path: '/pages-market/my/set_up' |
|||
} |
|||
] |
|||
} |
|||
}, |
|||
onLoad() { |
|||
this.loadUserInfo(); |
|||
}, |
|||
onShow() { |
|||
this.loadUserInfo(); |
|||
}, |
|||
methods: { |
|||
loadUserInfo() { |
|||
// 从本地存储获取用户信息 |
|||
const userInfo = uni.getStorageSync('userInfo'); |
|||
if (userInfo) { |
|||
this.userInfo = userInfo; |
|||
} |
|||
}, |
|||
handleProfileClick(item) { |
|||
if (item.action) { |
|||
// 处理特殊操作 |
|||
switch (item.action) { |
|||
case 'viewProfile': |
|||
this.viewPersonalProfile(); |
|||
break; |
|||
case 'viewSalary': |
|||
this.viewSalaryInfo(); |
|||
break; |
|||
} |
|||
} else if (item.path) { |
|||
// 页面跳转 |
|||
console.log('跳转到页面:', item.path); |
|||
uni.navigateTo({ |
|||
url: item.path, |
|||
fail: (err) => { |
|||
console.error('页面跳转失败:', err); |
|||
uni.showToast({ |
|||
title: '页面跳转失败', |
|||
icon: 'none' |
|||
}); |
|||
} |
|||
}); |
|||
} |
|||
}, |
|||
viewPersonalProfile() { |
|||
// 跳转到个人资料页面 |
|||
console.log('跳转到个人资料页面'); |
|||
uni.navigateTo({ |
|||
url: '/pages-common/profile/personal_info', |
|||
fail: (err) => { |
|||
console.error('跳转到个人资料页面失败:', err); |
|||
uni.showToast({ |
|||
title: '页面跳转失败', |
|||
icon: 'none' |
|||
}); |
|||
} |
|||
}); |
|||
}, |
|||
viewSalaryInfo() { |
|||
// 跳转到工资页面 |
|||
console.log('跳转到工资页面'); |
|||
uni.navigateTo({ |
|||
url: '/pages-coach/coach/my/salary', |
|||
fail: (err) => { |
|||
console.error('跳转到工资页面失败:', err); |
|||
uni.showToast({ |
|||
title: '页面跳转失败', |
|||
icon: 'none' |
|||
}); |
|||
} |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style scoped> |
|||
.profile-container { |
|||
background-color: #181A20; |
|||
min-height: 100vh; |
|||
color: #fff; |
|||
} |
|||
|
|||
.profile-header { |
|||
display: flex; |
|||
align-items: center; |
|||
padding: 30px 20px; |
|||
background: linear-gradient(135deg, #29d3b4 0%, #1a9b7c 100%); |
|||
margin: 20px; |
|||
border-radius: 12px; |
|||
} |
|||
|
|||
.avatar-section { |
|||
width: 80px; |
|||
height: 80px; |
|||
margin-right: 20px; |
|||
} |
|||
|
|||
.avatar-section image { |
|||
width: 100%; |
|||
height: 100%; |
|||
border-radius: 50%; |
|||
border: 3px solid rgba(255, 255, 255, 0.3); |
|||
} |
|||
|
|||
.user-info { |
|||
flex: 1; |
|||
display: flex; |
|||
flex-direction: column; |
|||
} |
|||
|
|||
.user-name { |
|||
font-size: 20px; |
|||
font-weight: bold; |
|||
margin-bottom: 8px; |
|||
color: #fff; |
|||
} |
|||
|
|||
.user-role { |
|||
font-size: 14px; |
|||
color: rgba(255, 255, 255, 0.8); |
|||
margin-bottom: 5px; |
|||
} |
|||
|
|||
.user-phone { |
|||
font-size: 12px; |
|||
color: rgba(255, 255, 255, 0.6); |
|||
} |
|||
|
|||
.grid-container { |
|||
margin: 20px; |
|||
} |
|||
|
|||
.grid-title { |
|||
font-size: 16px; |
|||
font-weight: bold; |
|||
margin-bottom: 15px; |
|||
color: #fff; |
|||
} |
|||
|
|||
.grid-content { |
|||
display: grid; |
|||
grid-template-columns: repeat(3, 1fr); |
|||
gap: 15px; |
|||
} |
|||
|
|||
.grid-item { |
|||
background-color: #292929; |
|||
border-radius: 8px; |
|||
padding: 20px 10px; |
|||
display: flex; |
|||
flex-direction: column; |
|||
align-items: center; |
|||
justify-content: center; |
|||
min-height: 90px; |
|||
transition: all 0.3s ease; |
|||
} |
|||
|
|||
.grid-item:active { |
|||
background-color: #3a3a3a; |
|||
transform: scale(0.95); |
|||
} |
|||
|
|||
.grid-icon { |
|||
margin-bottom: 8px; |
|||
} |
|||
|
|||
.grid-text { |
|||
font-size: 12px; |
|||
color: #fff; |
|||
text-align: center; |
|||
line-height: 1.2; |
|||
margin-bottom: 3px; |
|||
font-weight: bold; |
|||
} |
|||
|
|||
.grid-desc { |
|||
font-size: 10px; |
|||
color: rgba(255, 255, 255, 0.6); |
|||
text-align: center; |
|||
line-height: 1.1; |
|||
} |
|||
</style> |
|||
File diff suppressed because it is too large
@ -0,0 +1,225 @@ |
|||
<!--系统消息-列表--> |
|||
<template> |
|||
<view class="main_box"> |
|||
|
|||
<view class="main_section"> |
|||
<scroll-view |
|||
class="section_1" |
|||
scroll-y="true" |
|||
:lower-threshold="lowerThreshold" |
|||
@scrolltolower="loadMoreData" |
|||
style="height: 90vh;" |
|||
> |
|||
<view |
|||
class="item" |
|||
v-for="(v,k) in tableList" |
|||
:key="k" |
|||
@click="openViewArticleInfo(v)" |
|||
> |
|||
<view class="title">{{v.title}}</view> |
|||
<!-- <image--> |
|||
<!-- class="img_box"--> |
|||
<!-- :src="$util.img('/upload/attachment/image/202504/02/1743562333d1bb6666f969da1b7170381d0845153e_local.png')"--> |
|||
<!-- model="aspectFit"--> |
|||
<!-- ></image>--> |
|||
|
|||
<view class="content" v-html="v.content"></view> |
|||
|
|||
<view class="time">{{v.show_time}}</view> |
|||
</view> |
|||
|
|||
</scroll-view> |
|||
</view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
import commonApi from '@/api/common.js'; |
|||
|
|||
|
|||
|
|||
export default { |
|||
components: { |
|||
}, |
|||
data() { |
|||
return { |
|||
loading:false,//加载状态 |
|||
lowerThreshold: 100,//距离底部多远触发 |
|||
isReachedBottom: false,//防止重复加载|true=不可加载|false=可加载 |
|||
|
|||
//筛选条件 |
|||
filteredData:{ |
|||
page:1,//当前页码 |
|||
limit:10,//每页返回数据条数 |
|||
total:10,//数据总条数 |
|||
hair_staff_id: '',//发信人id |
|||
}, |
|||
tableList:[],//聊天数据列表 |
|||
} |
|||
}, |
|||
onLoad(options) { |
|||
this.filteredData.hair_staff_id = options.hair_staff_id//发信人id |
|||
}, |
|||
onShow(){ |
|||
this.init() |
|||
}, |
|||
//下拉刷新 |
|||
async onPullDownRefresh() { |
|||
//重置为第一页 |
|||
await this.resetFilteredData() |
|||
await this.getList() |
|||
}, |
|||
methods: { |
|||
//初始化 |
|||
async init(){ |
|||
await this.getList(); |
|||
}, |
|||
//加载更多(下一页) |
|||
loadMoreData() { |
|||
//判断是否加载 |
|||
if (!this.isReachedBottom) { |
|||
this.isReachedBottom = true;//设置为不可请求状态 |
|||
this.getList(); |
|||
} |
|||
}, |
|||
//重置为第一页 |
|||
async resetFilteredData() { |
|||
this.isReachedBottom = false; // 重置状态,以便下次触发加载更多 |
|||
|
|||
this.filteredData.page = 1//当前页码 |
|||
this.filteredData.limit = 10//每页返回数据条数 |
|||
this.filteredData.total = 10//数据总条数 |
|||
}, |
|||
|
|||
//获取列表 |
|||
async getList(){ |
|||
this.loading = true |
|||
|
|||
let data = {...this.filteredData} |
|||
|
|||
//判断是否还有数据 |
|||
if ((this.filteredData.page - 1) * this.filteredData.limit >= this.filteredData.total) { |
|||
this.loading = false |
|||
uni.showToast({ |
|||
title: '暂无更多', |
|||
icon: 'none' |
|||
}) |
|||
return |
|||
} |
|||
|
|||
if(data.page == 1){ |
|||
this.tableList = [] |
|||
} |
|||
|
|||
let res = await commonApi.getContactMessage(data)//获取消息列表 |
|||
this.loading = false |
|||
this.isReachedBottom = false; |
|||
if (res.code != 1){ |
|||
uni.showToast({ |
|||
title: res.msg, |
|||
icon: 'none' |
|||
}) |
|||
return |
|||
} |
|||
|
|||
this.tableList = this.tableList.concat(res.data.data); // 使用 concat 方法 将新数据追加到数组中 |
|||
// this.tableList.unshift(...res.data.data); // 将新数据插入到数组头部 |
|||
|
|||
console.log('列表',this.tableList) |
|||
this.filteredData.total = res.data.total |
|||
this.filteredData.page++ |
|||
}, |
|||
|
|||
//跳转文章详情 |
|||
openViewArticleInfo(item) { |
|||
let id = item.id |
|||
let redirect = item.redirect//重定向地址 |
|||
uni.navigateTo({ |
|||
url: `/pages-common/article_info?id=${id}` |
|||
}) |
|||
}, |
|||
|
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style lang="less" scoped> |
|||
.main_box { |
|||
background: #292929; |
|||
} |
|||
|
|||
//自定义导航栏 |
|||
.navbar_section { |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: center; |
|||
background: #29d3b4; |
|||
|
|||
.title { |
|||
padding: 20rpx 0; |
|||
font-size: 30rpx; |
|||
color: #315d55; |
|||
} |
|||
} |
|||
|
|||
.main_section { |
|||
min-height: 100vh; |
|||
background: #292929 100%; |
|||
padding: 0 0rpx; |
|||
padding-top: 32rpx; |
|||
padding-bottom: 150rpx; |
|||
font-size: 28rpx; |
|||
display: flex; |
|||
flex-direction: column; |
|||
gap: 20rpx; |
|||
|
|||
.section { |
|||
background-color: #434544; |
|||
padding: 40rpx 40rpx; |
|||
} |
|||
|
|||
.section_1{ |
|||
padding: 0 24rpx; |
|||
display: flex; |
|||
flex-direction: column; |
|||
|
|||
.item{ |
|||
margin-bottom: 38rpx; |
|||
display: flex; |
|||
flex-direction: column; |
|||
|
|||
padding: 32rpx 24rpx; |
|||
border-radius: 14rpx; |
|||
background-color: rgba(255,255,255,1); |
|||
border: 2rpx solid rgba(187,187,187,1); |
|||
|
|||
color: #4F4F4F; |
|||
font-size: 32rpx; |
|||
|
|||
.title{ |
|||
} |
|||
.img_box{ |
|||
margin-top: 30rpx; |
|||
width: 100%; |
|||
} |
|||
.content{ |
|||
margin-top: 30rpx; |
|||
} |
|||
.time{ |
|||
display: flex; |
|||
justify-content: flex-end; |
|||
margin-top: 36rpx; |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
|
|||
|
|||
} |
|||
|
|||
.describe { |
|||
color: #999999; |
|||
padding-left: 30rpx; |
|||
} |
|||
</style> |
|||
File diff suppressed because it is too large
@ -0,0 +1,631 @@ |
|||
<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="openSearchPopup"> |
|||
<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 v-if="showSearchPopup" class="search_popup_mask" @tap="showSearchPopup=false"> |
|||
<view class="search_popup_content" @tap.stop> |
|||
<view class="popup_search_content"> |
|||
<view class="popup_header"> |
|||
<view class="popup_title">筛选</view> |
|||
<view class="popup_close" @tap="showSearchPopup=false"> |
|||
<text class="close_text">✕</text> |
|||
</view> |
|||
</view> |
|||
|
|||
<scroll-view :scroll-y="true" class="popup_scroll_view"> |
|||
<!-- 筛选区域 --> |
|||
<view class="popup_filter_section"> |
|||
<view class="popup_filter_row"> |
|||
<view class="popup_filter_item"> |
|||
<text class="popup_filter_label">时间查询</text> |
|||
<picker :value="timeIndex" :range="timeOptions" @change="onTimeChange"> |
|||
<view class="popup_filter_picker">{{ timeOptions[timeIndex] }}</view> |
|||
</picker> |
|||
</view> |
|||
<view class="popup_filter_item"> |
|||
<text class="popup_filter_label">班主任筛选</text> |
|||
<input class="popup_filter_input" placeholder="人员名称筛选" v-model="searchForm.teacher_name" /> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="popup_filter_row"> |
|||
<view class="popup_filter_item"> |
|||
<text class="popup_filter_label">开始日期</text> |
|||
<picker mode="date" :value="searchForm.start_date" @change="onStartDateChange"> |
|||
<view class="popup_filter_input"> |
|||
{{ searchForm.start_date || '选择开始日期' }} |
|||
</view> |
|||
</picker> |
|||
</view> |
|||
<view class="popup_filter_item"> |
|||
<text class="popup_filter_label">结束日期</text> |
|||
<picker mode="date" :value="searchForm.end_date" @change="onEndDateChange"> |
|||
<view class="popup_filter_input"> |
|||
{{ searchForm.end_date || '选择结束日期' }} |
|||
</view> |
|||
</picker> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="popup_filter_row"> |
|||
<view class="popup_filter_item"> |
|||
<text class="popup_filter_label">固定位筛选</text> |
|||
<input class="popup_filter_input" placeholder="输入固定位编号" v-model="searchForm.venue_number" type="number" /> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</scroll-view> |
|||
|
|||
<view class="popup_filter_buttons"> |
|||
<view class="popup_filter_btn reset_btn" @click="resetSearchOnly">重置</view> |
|||
<view class="popup_filter_btn search_btn" @click="searchDataAndClose">搜索</view> |
|||
<view class="popup_filter_btn close_btn" @click="closeSearchPopup">关闭</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
|
|||
<!-- 课程卡片列表 --> |
|||
<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 ? course.venue.venue_name : '未设置' }}</view> |
|||
<view class="row">课程:{{ course.course ? course.course.course_name : '未设置' }}</view> |
|||
<view class="row">人数:{{ course.available_capacity || 0 }}</view> |
|||
<view class="row">安排情况:{{ course.student ? course.student.length : 0 }}/{{course.max_students ? course.max_students : '不限'}}</view> |
|||
</view> |
|||
<view class="card-footer"> |
|||
<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: '', |
|||
student_id: '', |
|||
|
|||
// 查询弹窗相关 |
|||
showSearchPopup: false, |
|||
searchForm: { |
|||
time_hour: '', |
|||
start_date: '', |
|||
end_date: '', |
|||
teacher_name: '', |
|||
venue_number: '' |
|||
}, |
|||
|
|||
// 时间选择器相关 |
|||
timeIndex: 0, |
|||
timeOptions: ['全部时间', '上午(8:00-12:00)', '下午(12:00-18:00)', '晚上(18:00-22:00)'] |
|||
}; |
|||
}, |
|||
onLoad(options) { |
|||
this.resource_id = options.resource_id || ''; |
|||
this.student_id = options.student_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 |
|||
}) |
|||
// 确保courseList是数组,并处理可能的空数据 |
|||
this.courseList = Array.isArray(data.data) ? data.data : [] |
|||
|
|||
|
|||
} catch (error) { |
|||
console.error('获取信息失败:', error); |
|||
// 确保即使出错也有默认数据 |
|||
this.courseList = []; |
|||
uni.showToast({ |
|||
title: '获取课程数据失败', |
|||
icon: 'none' |
|||
}); |
|||
} |
|||
}, |
|||
openCalendar() { |
|||
this.$refs.calendarPopup.open(); |
|||
}, |
|||
closeCalendar() { |
|||
console.log(123123) |
|||
this.$refs.calendarPopup.close(); |
|||
}, |
|||
viewDetail(course) { |
|||
// 跳转到课程详情页 |
|||
const resourceId = this.resource_id || ''; |
|||
const studentId = this.student_id || ''; |
|||
console.log('跳转到课程详情页:', course.id, resourceId, studentId); |
|||
uni.navigateTo({ |
|||
url: '/pages-market/clue/class_arrangement_detail?schedule_id=' + course.id + '&resource_id=' + resourceId + '&student_id=' + studentId, |
|||
fail: (err) => { |
|||
console.error('跳转到课程详情页失败:', err); |
|||
uni.showToast({ |
|||
title: '页面跳转失败', |
|||
icon: 'none' |
|||
}); |
|||
} |
|||
}); |
|||
}, |
|||
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; |
|||
}, |
|||
|
|||
// 查询弹窗相关方法 |
|||
openSearchPopup() { |
|||
this.showSearchPopup = true; |
|||
}, |
|||
|
|||
closeSearchPopup() { |
|||
this.showSearchPopup = false; |
|||
}, |
|||
|
|||
// 时间选择器变化 |
|||
onTimeChange(e) { |
|||
this.timeIndex = e.detail.value; |
|||
this.searchForm.time_hour = this.timeOptions[this.timeIndex]; |
|||
}, |
|||
|
|||
// 开始日期选择变化 |
|||
onStartDateChange(e) { |
|||
this.searchForm.start_date = e.detail.value; |
|||
console.log('开始日期选择:', e.detail.value); |
|||
}, |
|||
|
|||
// 结束日期选择变化 |
|||
onEndDateChange(e) { |
|||
this.searchForm.end_date = e.detail.value; |
|||
console.log('结束日期选择:', e.detail.value); |
|||
}, |
|||
|
|||
// 搜索并关闭弹窗 |
|||
searchDataAndClose() { |
|||
console.log('执行搜索,表单数据:', this.searchForm); |
|||
this.searchCourseData(); |
|||
this.showSearchPopup = false; |
|||
}, |
|||
|
|||
// 搜索课程数据 |
|||
async searchCourseData() { |
|||
try { |
|||
let searchParams = { |
|||
schedule_date: this.date |
|||
}; |
|||
|
|||
// 添加搜索条件 |
|||
if (this.searchForm.time_hour && this.searchForm.time_hour !== '全部时间') { |
|||
searchParams.time_hour = this.searchForm.time_hour; |
|||
} |
|||
if (this.searchForm.start_date) { |
|||
searchParams.start_date = this.searchForm.start_date; |
|||
} |
|||
if (this.searchForm.end_date) { |
|||
searchParams.end_date = this.searchForm.end_date; |
|||
} |
|||
if (this.searchForm.teacher_name) { |
|||
searchParams.teacher_name = this.searchForm.teacher_name; |
|||
} |
|||
if (this.searchForm.venue_number) { |
|||
searchParams.venue_number = this.searchForm.venue_number; |
|||
} |
|||
|
|||
console.log('搜索参数:', searchParams); |
|||
|
|||
let data = await apiRoute.courseAllList(searchParams); |
|||
// 确保courseList是数组,并处理可能的空数据 |
|||
this.courseList = Array.isArray(data.data) ? data.data : []; |
|||
|
|||
uni.showToast({ |
|||
title: '查询完成', |
|||
icon: 'success' |
|||
}); |
|||
} catch (error) { |
|||
console.error('搜索失败:', error); |
|||
uni.showToast({ |
|||
title: '搜索失败', |
|||
icon: 'none' |
|||
}); |
|||
} |
|||
}, |
|||
|
|||
// 重置搜索条件 |
|||
resetSearchOnly() { |
|||
this.searchForm = { |
|||
time_hour: '', |
|||
start_date: '', |
|||
end_date: '', |
|||
teacher_name: '', |
|||
venue_number: '' |
|||
}; |
|||
this.timeIndex = 0; |
|||
|
|||
// 重新加载默认数据 |
|||
this.getDate(); |
|||
}, |
|||
|
|||
// 数据安全处理方法 - 确保嵌套对象访问的安全性 |
|||
safeGet(obj, path, defaultValue = '') { |
|||
if (!obj) return defaultValue; |
|||
const keys = path.split('.'); |
|||
let result = obj; |
|||
for (let key of keys) { |
|||
if (result === null || result === undefined || !result.hasOwnProperty(key)) { |
|||
return defaultValue; |
|||
} |
|||
result = result[key]; |
|||
} |
|||
return result || defaultValue; |
|||
} |
|||
}, |
|||
}; |
|||
</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; |
|||
width: 100%; |
|||
margin-top: 40rpx; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
// 搜索弹窗样式 |
|||
.search_popup_mask { |
|||
position: fixed; |
|||
top: 0; |
|||
left: 0; |
|||
right: 0; |
|||
bottom: 0; |
|||
background: rgba(0, 0, 0, 0.6); |
|||
z-index: 999; |
|||
display: flex; |
|||
flex-direction: column; |
|||
} |
|||
|
|||
.search_popup_content { |
|||
background: #fff; |
|||
border-bottom-left-radius: 24rpx; |
|||
border-bottom-right-radius: 24rpx; |
|||
animation: slideDown 0.3s ease-out; |
|||
width: 100%; |
|||
max-height: 80vh; |
|||
overflow: hidden; |
|||
display: flex; |
|||
flex-direction: column; |
|||
} |
|||
|
|||
@keyframes slideDown { |
|||
from { |
|||
transform: translateY(-100%); |
|||
} |
|||
to { |
|||
transform: translateY(0); |
|||
} |
|||
} |
|||
|
|||
// 弹窗搜索内容样式 |
|||
.popup_search_content { |
|||
padding: 0; |
|||
background: #fff; |
|||
min-height: 60vh; |
|||
max-height: 80vh; |
|||
display: flex; |
|||
flex-direction: column; |
|||
border-bottom-left-radius: 24rpx; |
|||
border-bottom-right-radius: 24rpx; |
|||
overflow: hidden; |
|||
} |
|||
|
|||
.popup_header { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
padding: 32rpx; |
|||
border-bottom: 1px solid #f0f0f0; |
|||
} |
|||
|
|||
.popup_title { |
|||
font-size: 32rpx; |
|||
font-weight: 600; |
|||
color: #333; |
|||
} |
|||
|
|||
.popup_close { |
|||
width: 60rpx; |
|||
height: 60rpx; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
|
|||
.close_text { |
|||
font-size: 32rpx; |
|||
color: #999; |
|||
} |
|||
} |
|||
|
|||
.popup_scroll_view { |
|||
flex: 1; |
|||
padding: 32rpx; |
|||
overflow-y: auto; |
|||
} |
|||
|
|||
.popup_filter_section { |
|||
margin-bottom: 32rpx; |
|||
|
|||
&:last-child { |
|||
margin-bottom: 0; |
|||
} |
|||
} |
|||
|
|||
.popup_filter_row { |
|||
display: flex; |
|||
gap: 20rpx; |
|||
margin-bottom: 24rpx; |
|||
|
|||
&:last-child { |
|||
margin-bottom: 0; |
|||
} |
|||
} |
|||
|
|||
.popup_filter_item { |
|||
flex: 1; |
|||
display: flex; |
|||
flex-direction: column; |
|||
gap: 12rpx; |
|||
|
|||
&.full_width { |
|||
flex: 1; |
|||
} |
|||
|
|||
.popup_filter_label { |
|||
font-size: 26rpx; |
|||
color: #666; |
|||
font-weight: 500; |
|||
} |
|||
|
|||
.popup_filter_input { |
|||
height: 72rpx; |
|||
line-height: 72rpx; |
|||
padding: 0 16rpx; |
|||
border: 1px solid #ddd; |
|||
border-radius: 8rpx; |
|||
font-size: 28rpx; |
|||
color: #333; |
|||
background: #fff; |
|||
|
|||
&::placeholder { |
|||
color: #999; |
|||
} |
|||
} |
|||
|
|||
.popup_filter_picker { |
|||
height: 72rpx; |
|||
line-height: 72rpx; |
|||
padding: 0 16rpx; |
|||
border: 1px solid #ddd; |
|||
border-radius: 8rpx; |
|||
font-size: 28rpx; |
|||
color: #333; |
|||
background: #fff; |
|||
position: relative; |
|||
|
|||
&::after { |
|||
content: '▼'; |
|||
position: absolute; |
|||
right: 16rpx; |
|||
font-size: 20rpx; |
|||
color: #999; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.popup_filter_buttons { |
|||
display: flex; |
|||
gap: 20rpx; |
|||
padding: 32rpx; |
|||
margin-top: auto; |
|||
border-top: 1px solid #f0f0f0; |
|||
background: #fff; |
|||
border-bottom-left-radius: 24rpx; |
|||
border-bottom-right-radius: 24rpx; |
|||
} |
|||
|
|||
.popup_filter_btn { |
|||
flex: 1; |
|||
height: 72rpx; |
|||
line-height: 72rpx; |
|||
text-align: center; |
|||
border-radius: 8rpx; |
|||
font-size: 28rpx; |
|||
font-weight: 600; |
|||
|
|||
&.search_btn { |
|||
background: #29d3b4; |
|||
color: #fff; |
|||
} |
|||
|
|||
&.reset_btn { |
|||
background: #f5f5f5; |
|||
color: #666; |
|||
border: 1px solid #ddd; |
|||
} |
|||
|
|||
&.close_btn { |
|||
background: #666; |
|||
color: #fff; |
|||
} |
|||
} |
|||
</style> |
|||
File diff suppressed because it is too large
@ -0,0 +1,431 @@ |
|||
// 客户详情页样式文件 |
|||
.assemble { |
|||
background-color: #292929; |
|||
min-height: 100vh; |
|||
} |
|||
|
|||
.content { |
|||
padding: 20rpx; |
|||
} |
|||
|
|||
.tab-switcher-container { |
|||
margin: 20rpx 0; |
|||
} |
|||
|
|||
// 学生信息区域 |
|||
.student-section { |
|||
margin-top: 20rpx; |
|||
|
|||
.section-header { |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: space-between; |
|||
padding: 20rpx; |
|||
|
|||
.section-title { |
|||
font-size: 32rpx; |
|||
font-weight: bold; |
|||
color: #fff; |
|||
} |
|||
|
|||
.add-student-btn { |
|||
display: flex; |
|||
align-items: center; |
|||
background: #29d3b4; |
|||
color: #fff; |
|||
border-radius: 20rpx; |
|||
padding: 12rpx 20rpx; |
|||
font-size: 24rpx; |
|||
|
|||
.add-icon { |
|||
margin-right: 8rpx; |
|||
font-weight: bold; |
|||
font-size: 20rpx; |
|||
} |
|||
|
|||
&:active { |
|||
background: #1ea08e; |
|||
transform: scale(0.95); |
|||
} |
|||
} |
|||
} |
|||
|
|||
.student-cards { |
|||
width: 100%; |
|||
min-height: 600rpx; |
|||
|
|||
.student-swiper { |
|||
width: 100%; |
|||
height: 560rpx; |
|||
|
|||
.student-swiper-content { |
|||
display: flex; |
|||
flex-direction: column; |
|||
height: 580rpx; |
|||
padding: 20rpx; |
|||
box-sizing: border-box; |
|||
background-color: #434544; |
|||
border-radius: 16rpx; |
|||
margin: 0 10rpx; |
|||
color: #fff; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
.popup-footer { |
|||
display: flex; |
|||
padding: 30rpx 40rpx; |
|||
padding-bottom: calc(30rpx + env(safe-area-inset-bottom)); |
|||
border-top: 1px solid #333; |
|||
gap: 30rpx; |
|||
flex-shrink: 0; /* 确保底部按钮区域不被压缩 */ |
|||
background: #1a1a1a; /* 确保背景色一致 */ |
|||
|
|||
.popup-footer-btns { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
|
|||
.footer-btn { |
|||
flex: 1; |
|||
height: 88rpx; |
|||
display: flex; |
|||
width: 45%; |
|||
align-items: center; |
|||
justify-content: center; |
|||
border-radius: 12rpx; |
|||
font-size: 32rpx; |
|||
font-weight: 500; |
|||
|
|||
&.cancel-btn { |
|||
background: #cccccc; |
|||
} |
|||
|
|||
&.confirm-btn { |
|||
background: linear-gradient(45deg, #29D3B4, #1DB584); |
|||
color: #ffffff; |
|||
} |
|||
} |
|||
} |
|||
|
|||
} |
|||
|
|||
// 课程、通话、体测记录等区域 |
|||
.course-section, .call-section, .fitness-section, .study-plan-section { |
|||
background-color: #434544; |
|||
border-radius: 16rpx; |
|||
margin: 20rpx 0; |
|||
|
|||
.section-header { |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: space-between; |
|||
padding: 20rpx; |
|||
|
|||
.context-title { |
|||
font-size: 28rpx; |
|||
font-weight: bold; |
|||
color: #fff; |
|||
} |
|||
|
|||
.add-record-btn { |
|||
display: flex; |
|||
align-items: center; |
|||
background: #29d3b4; |
|||
color: #fff; |
|||
border-radius: 20rpx; |
|||
padding: 12rpx 20rpx; |
|||
font-size: 24rpx; |
|||
|
|||
.add-icon { |
|||
margin-right: 8rpx; |
|||
font-weight: bold; |
|||
} |
|||
|
|||
&:active { |
|||
background: #1ea08e; |
|||
transform: scale(0.95); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
// 操作按钮区域 - 独立于Swiper外部 |
|||
.student-section .action-buttons-section { |
|||
display: flex; |
|||
gap: 8rpx; |
|||
margin-top: 15rpx; |
|||
padding: 15rpx; |
|||
flex-wrap: nowrap; |
|||
height: 100rpx; |
|||
box-sizing: border-box; |
|||
overflow-x: auto; |
|||
overflow-y: hidden; |
|||
-webkit-overflow-scrolling: touch; |
|||
background-color: #434544; |
|||
border-radius: 16rpx; |
|||
margin: 15rpx 0; |
|||
|
|||
&::-webkit-scrollbar { |
|||
display: none; |
|||
} |
|||
|
|||
.action-item { |
|||
display: flex; |
|||
flex-direction: column; |
|||
align-items: center; |
|||
justify-content: center; |
|||
background: rgba(255, 255, 255, 0.05); |
|||
border: 1px solid rgba(41, 211, 180, 0.3); |
|||
border-radius: 8rpx; |
|||
padding: 12rpx 8rpx; |
|||
min-width: 90rpx; |
|||
flex-shrink: 0; |
|||
transition: all 0.3s ease; |
|||
|
|||
.action-icon { |
|||
font-size: 24rpx; |
|||
margin-bottom: 5rpx; |
|||
line-height: 1; |
|||
} |
|||
|
|||
.action-text { |
|||
font-size: 18rpx; |
|||
color: #fff; |
|||
text-align: center; |
|||
line-height: 1.2; |
|||
word-break: break-all; |
|||
} |
|||
|
|||
&:active { |
|||
background: rgba(41, 211, 180, 0.2); |
|||
border-color: #29d3b4; |
|||
transform: scale(0.95); |
|||
|
|||
.action-text { |
|||
color: #29d3b4; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
// 通用空状态样式 |
|||
.empty-state { |
|||
display: flex; |
|||
flex-direction: column; |
|||
align-items: center; |
|||
justify-content: center; |
|||
padding: 60rpx 20rpx; |
|||
min-height: 200rpx; |
|||
|
|||
.empty-icon { |
|||
font-size: 60rpx; |
|||
opacity: 0.6; |
|||
margin-bottom: 20rpx; |
|||
} |
|||
|
|||
.empty-text { |
|||
color: #ccc; |
|||
font-size: 28rpx; |
|||
margin-bottom: 20rpx; |
|||
} |
|||
|
|||
.empty-add-btn { |
|||
background: #29d3b4; |
|||
color: #fff; |
|||
padding: 12rpx 24rpx; |
|||
border-radius: 20rpx; |
|||
font-size: 24rpx; |
|||
transition: all 0.3s ease; |
|||
|
|||
&:active { |
|||
background: #1ea08e; |
|||
transform: scale(0.95); |
|||
} |
|||
} |
|||
} |
|||
|
|||
// 备注弹窗样式 |
|||
.remark-dialog { |
|||
width: 600rpx; |
|||
padding: 30rpx; |
|||
background: #fff; |
|||
border-radius: 16rpx; |
|||
|
|||
textarea { |
|||
width: 100%; |
|||
height: 200rpx; |
|||
padding: 20rpx; |
|||
border: 2rpx solid #e9ecef; |
|||
border-radius: 12rpx; |
|||
font-size: 28rpx; |
|||
color: #333; |
|||
background-color: #fff; |
|||
box-sizing: border-box; |
|||
resize: none; |
|||
|
|||
&::placeholder { |
|||
color: #999; |
|||
} |
|||
|
|||
&:focus { |
|||
border-color: #29d3b4; |
|||
outline: none; |
|||
box-shadow: 0 0 0 2rpx rgba(41, 211, 180, 0.1); |
|||
} |
|||
} |
|||
|
|||
.dialog-btns { |
|||
display: flex; |
|||
gap: 20rpx; |
|||
margin-top: 30rpx; |
|||
|
|||
.btn { |
|||
flex: 1; |
|||
padding: 20rpx; |
|||
border-radius: 8rpx; |
|||
font-size: 28rpx; |
|||
text-align: center; |
|||
|
|||
&.cancel { |
|||
background: #f8f9fa; |
|||
color: #666; |
|||
border: 2rpx solid #e9ecef; |
|||
} |
|||
|
|||
&.confirm { |
|||
background: #29d3b4; |
|||
color: #fff; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
// 二维码支付弹窗样式 |
|||
.qrcode-payment-modal { |
|||
width: 600rpx; |
|||
background: #fff; |
|||
border-radius: 20rpx; |
|||
overflow: hidden; |
|||
|
|||
.modal-header { |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: space-between; |
|||
padding: 40rpx 40rpx 20rpx; |
|||
border-bottom: 1px solid #f0f0f0; |
|||
|
|||
.modal-title { |
|||
font-size: 36rpx; |
|||
font-weight: 600; |
|||
color: #333; |
|||
} |
|||
|
|||
.close-btn { |
|||
width: 60rpx; |
|||
height: 60rpx; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
border-radius: 50%; |
|||
background: #f5f5f5; |
|||
color: #666; |
|||
font-size: 32rpx; |
|||
|
|||
&:active { |
|||
background: #e5e5e5; |
|||
transform: scale(0.95); |
|||
} |
|||
} |
|||
} |
|||
|
|||
.order-info { |
|||
padding: 30rpx 40rpx; |
|||
|
|||
.info-row { |
|||
display: flex; |
|||
align-items: center; |
|||
margin-bottom: 20rpx; |
|||
|
|||
.label { |
|||
font-size: 28rpx; |
|||
color: #666; |
|||
margin-right: 20rpx; |
|||
} |
|||
|
|||
.value { |
|||
font-size: 28rpx; |
|||
color: #333; |
|||
flex: 1; |
|||
} |
|||
|
|||
.amount { |
|||
font-size: 32rpx; |
|||
color: #ff4757; |
|||
font-weight: 600; |
|||
flex: 1; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.qrcode-container { |
|||
display: flex; |
|||
flex-direction: column; |
|||
align-items: center; |
|||
padding: 40rpx; |
|||
background: #fafafa; |
|||
|
|||
.qrcode-image { |
|||
width: 300rpx; |
|||
height: 300rpx; |
|||
border: 1px solid #e5e5e5; |
|||
border-radius: 12rpx; |
|||
background: #fff; |
|||
margin-bottom: 20rpx; |
|||
} |
|||
|
|||
.qrcode-tip { |
|||
font-size: 24rpx; |
|||
color: #666; |
|||
text-align: center; |
|||
line-height: 1.4; |
|||
} |
|||
} |
|||
|
|||
.modal-buttons { |
|||
display: flex; |
|||
padding: 30rpx 40rpx 40rpx; |
|||
gap: 20rpx; |
|||
|
|||
.btn { |
|||
flex: 1; |
|||
height: 80rpx; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
border-radius: 40rpx; |
|||
font-size: 28rpx; |
|||
font-weight: 500; |
|||
|
|||
&.secondary { |
|||
background: #f5f5f5; |
|||
color: #666; |
|||
|
|||
&:active { |
|||
background: #e5e5e5; |
|||
transform: scale(0.98); |
|||
} |
|||
} |
|||
|
|||
&.primary { |
|||
background: #29d3b4; |
|||
color: #fff; |
|||
|
|||
&:active { |
|||
background: #1ea08e; |
|||
transform: scale(0.98); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
File diff suppressed because it is too large
@ -0,0 +1,164 @@ |
|||
<template> |
|||
<view class="dark-table-container"> |
|||
<view v-for="(row, rIdx) in data" :key="rIdx" class="card"> |
|||
<view class="card-title" @click="toggleExpand(rIdx)"> |
|||
{{ row.channel }} |
|||
<text class="expand-icon">{{ expanded[rIdx] ? '▲' : '▼' }}</text> |
|||
</view> |
|||
<view class="card-content"> |
|||
<view |
|||
class="card-row" |
|||
v-for="(col, cIdx) in getVisibleColumns(rIdx)" |
|||
:key="cIdx" |
|||
> |
|||
<view class="card-label"> |
|||
{{ col.label }} |
|||
<text v-if="col.tip" class="tip" @click.stop="showTip(col.tip)">?</text> |
|||
</view> |
|||
<view class="card-value">{{ row[col.key] || '-' }}</view> |
|||
</view> |
|||
</view> |
|||
<view v-if="columns.length > 6" class="expand-btn" @click="toggleExpand(rIdx)"> |
|||
{{ expanded[rIdx] ? '收起' : '展开全部' }} |
|||
</view> |
|||
</view> |
|||
<!-- 说明弹窗 --> |
|||
<uni-popup ref="popup" type="center"> |
|||
<view class="popup-content">{{ tipContent }}</view> |
|||
</uni-popup> |
|||
<AQTabber></AQTabber> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
import AQTabber from "@/components/AQ/AQTabber.vue" |
|||
|
|||
export default { |
|||
components: { |
|||
AQTabber, |
|||
}, |
|||
data() { |
|||
return { |
|||
columns: [ |
|||
{ label: '渠道', key: 'channel' }, |
|||
{ label: '资源数', key: 'resource', tip: '本月的新资源' }, |
|||
{ label: '到访数(一访)', key: 'visit1', tip: '本月资源的一访到访数' }, |
|||
{ label: '到访率(一访)', key: 'visitRate1', tip: '一访到访数/本月新资源数' }, |
|||
{ label: '到访数(二访)', key: 'visit2', tip: '二访到访数/一访到访数' }, |
|||
{ label: '到访率(二访)', key: 'visitRate2', tip: '二访到访数/一访到访数' }, |
|||
{ label: '关单数(一访)', key: 'close1', tip: '本月一访到访的关单数' }, |
|||
{ label: '关单率(一访)', key: 'closeRate1', tip: '一访关单数/一访到访数' }, |
|||
{ label: '关单数(二访)', key: 'close2', tip: '二访到访的关单数' }, |
|||
{ label: '关单率(二访)', key: 'closeRate2', tip: '二访关单数/二访到访数' }, |
|||
{ label: '月总转', key: 'monthTrans', tip: '关单数(合计)/资源数' }, |
|||
{ label: '往月到访数', key: 'lastMonthVisit', tip: '本月资源在往月到访的到访数' }, |
|||
{ label: '月共到访', key: 'monthTotalVisit', tip: '本月资源任在本月到访/关单' }, |
|||
{ label: '往月关单数', key: 'lastMonthClose', tip: '本月关单-往月关单' }, |
|||
{ label: '月共关单', key: 'monthTotalClose', tip: '本月关单+往月关单' }, |
|||
], |
|||
data: [ |
|||
{ channel: '体检包(地推)', resource: 10, visit1: 5, visitRate1: '50%', visit2: 2, visitRate2: '40%', close1: 1, closeRate1: '20%', close2: 1, closeRate2: '50%', monthTrans: '10%', lastMonthVisit: 0, monthTotalVisit: 5, lastMonthClose: 0, monthTotalClose: 1 }, |
|||
], |
|||
tipContent: '', |
|||
expanded: {}, |
|||
} |
|||
}, |
|||
methods: { |
|||
showTip(content) { |
|||
this.tipContent = content |
|||
this.$refs.popup.open() |
|||
}, |
|||
toggleExpand(idx) { |
|||
this.$set(this.expanded, idx, !this.expanded[idx]) |
|||
}, |
|||
getVisibleColumns(idx) { |
|||
// 只展示前5个字段,展开后展示全部(不含渠道) |
|||
const cols = this.columns.slice(1) |
|||
if (this.expanded[idx]) return cols |
|||
return cols.slice(0, 5) |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style scoped> |
|||
.dark-table-container { |
|||
background: #18181c; |
|||
color: #f1f1f1; |
|||
min-height: 100vh; |
|||
padding: 24rpx 12rpx; |
|||
} |
|||
.card { |
|||
background: #23232a; |
|||
border-radius: 18rpx; |
|||
box-shadow: 0 4rpx 24rpx rgba(0,0,0,0.18); |
|||
margin-bottom: 32rpx; |
|||
padding: 24rpx 20rpx 8rpx 20rpx; |
|||
transition: box-shadow 0.2s; |
|||
} |
|||
.card-title { |
|||
font-size: 32rpx; |
|||
font-weight: bold; |
|||
margin-bottom: 18rpx; |
|||
color: #ffb300; |
|||
letter-spacing: 2rpx; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: space-between; |
|||
cursor: pointer; |
|||
} |
|||
.expand-icon { |
|||
font-size: 24rpx; |
|||
color: #bdbdbd; |
|||
margin-left: 12rpx; |
|||
} |
|||
.card-content { |
|||
display: flex; |
|||
flex-direction: column; |
|||
gap: 12rpx; |
|||
overflow: hidden; |
|||
transition: max-height 0.3s; |
|||
} |
|||
.card-row { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
border-bottom: 1px dashed #333; |
|||
padding: 8rpx 0; |
|||
} |
|||
.card-label { |
|||
color: #bdbdbd; |
|||
font-size: 26rpx; |
|||
display: flex; |
|||
align-items: center; |
|||
} |
|||
.card-value { |
|||
color: #f1f1f1; |
|||
font-size: 26rpx; |
|||
text-align: right; |
|||
max-width: 60%; |
|||
word-break: break-all; |
|||
} |
|||
.tip { |
|||
color: #ffb300; |
|||
margin-left: 8rpx; |
|||
font-size: 24rpx; |
|||
cursor: pointer; |
|||
} |
|||
.expand-btn { |
|||
color: #ffb300; |
|||
text-align: center; |
|||
font-size: 26rpx; |
|||
margin: 10rpx 0 0 0; |
|||
padding-bottom: 8rpx; |
|||
cursor: pointer; |
|||
} |
|||
.popup-content { |
|||
background: #23232a; |
|||
color: #fff; |
|||
padding: 32rpx; |
|||
border-radius: 16rpx; |
|||
min-width: 300rpx; |
|||
text-align: center; |
|||
} |
|||
</style> |
|||
File diff suppressed because it is too large
@ -0,0 +1,365 @@ |
|||
<!--编辑客户--> |
|||
<template> |
|||
<view class="assemble"> |
|||
<!--切换--> |
|||
<view class="tab_section"> |
|||
<fui-segmented-control |
|||
:values="optionTable" |
|||
:current="(Number(optionTableId))" |
|||
type="button" |
|||
radius="8" |
|||
height="80" |
|||
color="#29d3b4" |
|||
bold="true" |
|||
@click="segmented"> |
|||
</fui-segmented-control> |
|||
</view> |
|||
|
|||
<!-- 客户资源修改记录--> |
|||
<scroll-view |
|||
v-show="filteredData.type == `resource`" |
|||
scroll-y="true" |
|||
:lower-threshold="lowerThreshold" |
|||
@scrolltolower="loadMoreData" |
|||
style="margin-top:40rpx;height: 80vh;" |
|||
> |
|||
<!--时间轴--> |
|||
<view class="table_list"> |
|||
<fui-timeaxis background="#292929" :padding="['36rpx','16rpx']"> |
|||
<fui-timeaxis-node |
|||
:lineColor="k<3 ? v.activeColor:'#ccc'" |
|||
:lined="k!==tableList.length-1" |
|||
v-for="(v,k) in tableList" |
|||
:key="k" |
|||
> |
|||
<!--inco--> |
|||
<view |
|||
class="fui-node" |
|||
:style="{borderColor:(k != tableList.length-1 ? '#29d3b4' :'' ) }" |
|||
> |
|||
</view> |
|||
<template v-slot:right> |
|||
<view |
|||
class="fui-process__node table_itme" |
|||
:style="{paddingBottom:(k !== tableList.length-1 ? '50rpx' :'0rpx')}" |
|||
:class="{'fui-node__pb':k!==tableList.length-1}" |
|||
> |
|||
|
|||
<view class="fui-title"> |
|||
<!--修改时间--> |
|||
{{v.modification_time}} |
|||
</view> |
|||
<view class="itme_box"> |
|||
<view class="title_name">修改人:{{v.staff_id_name}} 修改了如下内容</view> |
|||
<view class="table_section"> |
|||
<table class="table_box"> |
|||
<!-- 自定义表头 --> |
|||
<thead> |
|||
<tr> |
|||
<th style="width: 30%; border: 1px solid #ddd; padding: 12rpx;">字段名称</th> |
|||
<th style="width: 34%; border: 1px solid #ddd; padding: 12rpx;">原数据</th> |
|||
<th style="width: 34%; border: 1px solid #ddd; padding: 12rpx;">新数据</th> |
|||
</tr> |
|||
</thead> |
|||
|
|||
<!-- 自定义每一行数据 --> |
|||
<tbody> |
|||
<tr v-for="(row, index) in v.update_arr" :key="index"> |
|||
<td style="width: 30%; border: 1px solid #ddd; padding: 12rpx;">{{ row.field_name_zh }}</td> |
|||
<td style="width: 34%; border: 1px solid #ddd; padding: 12rpx;">{{ row.old_value }}</td> |
|||
<td style="width: 34%; border: 1px solid #ddd; padding: 12rpx;">{{ row.new_value }}</td> |
|||
</tr> |
|||
</tbody> |
|||
</table> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</template> |
|||
</fui-timeaxis-node> |
|||
</fui-timeaxis> |
|||
</view> |
|||
</scroll-view> |
|||
|
|||
<!-- 六要素修改记录--> |
|||
<scroll-view |
|||
v-show="filteredData.type == `six_speed`" |
|||
scroll-y="true" |
|||
:lower-threshold="lowerThreshold" |
|||
@scrolltolower="loadMoreData" |
|||
style="margin-top:40rpx;height: 80vh;" |
|||
> |
|||
<!--时间轴--> |
|||
<view class="table_list"> |
|||
<fui-timeaxis background="#292929" :padding="['36rpx','16rpx']"> |
|||
<fui-timeaxis-node |
|||
:lineColor="k<3?v.activeColor:'#ccc'" |
|||
:lined="k!==tableList.length-1" |
|||
v-for="(v,k) in tableList" |
|||
:key="k" |
|||
> |
|||
<!--inco--> |
|||
<view |
|||
class="fui-node" |
|||
:style="{borderColor:(k != tableList.length-1 ? '#29d3b4' :'' ) }" |
|||
> |
|||
</view> |
|||
<template v-slot:right> |
|||
<view |
|||
class="fui-process__node table_itme" |
|||
:style="{paddingBottom:(k !== tableList.length-1 ? '50rpx' :'0rpx')}" |
|||
:class="{'fui-node__pb':k!==tableList.length-1}" |
|||
> |
|||
|
|||
<view class="fui-title"> |
|||
<!--修改时间--> |
|||
{{v.modification_time}} |
|||
</view> |
|||
<view class="itme_box"> |
|||
<view class="title_name">修改人:{{v.staff_id_name}} 修改了如下内容</view> |
|||
<view class="table_section"> |
|||
<table class="table_box"> |
|||
<!-- 自定义表头 --> |
|||
<thead> |
|||
<tr> |
|||
<th style="width: 30%; border: 1px solid #ddd; padding: 12rpx;">字段名称</th> |
|||
<th style="width: 34%; border: 1px solid #ddd; padding: 12rpx;">原数据</th> |
|||
<th style="width: 34%; border: 1px solid #ddd; padding: 12rpx;">新数据</th> |
|||
</tr> |
|||
</thead> |
|||
|
|||
<!-- 自定义每一行数据 --> |
|||
<tbody> |
|||
<tr v-for="(row, index) in v.update_arr" :key="index"> |
|||
<td style="width: 30%; border: 1px solid #ddd; padding: 12rpx;">{{ row.field_name_zh }}</td> |
|||
<td style="width: 34%; border: 1px solid #ddd; padding: 12rpx;">{{ row.old_value }}</td> |
|||
<td style="width: 34%; border: 1px solid #ddd; padding: 12rpx;">{{ row.new_value }}</td> |
|||
</tr> |
|||
</tbody> |
|||
</table> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</template> |
|||
</fui-timeaxis-node> |
|||
</fui-timeaxis> |
|||
</view> |
|||
</scroll-view> |
|||
|
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
import apiRoute from '@/api/apiRoute.js'; |
|||
|
|||
export default { |
|||
data() { |
|||
return { |
|||
|
|||
loading:false,//加载状态 |
|||
lowerThreshold: 100,//距离底部多远触发 |
|||
isReachedBottom: false,//防止重复加载|true=不可加载|false=可加载 |
|||
|
|||
//筛选条件 |
|||
filteredData:{ |
|||
page:1,//当前页码 |
|||
limit:10,//每页返回数据条数 |
|||
total:10,//数据总条数 |
|||
type:'resource',//查询类型|resource=客户资源,six_speed=六要素 |
|||
customer_resource_id: '',//客户资源表id |
|||
}, |
|||
//数据列表 |
|||
tableList:[],//表格数据 |
|||
|
|||
//tab切换 |
|||
optionTableId:0,//分段器初始选中项索引 |
|||
optionTable:[ |
|||
{ |
|||
id: 0, |
|||
name: '客户资源修改记录', |
|||
type:'resource', |
|||
}, |
|||
{ |
|||
id: 1, |
|||
name: '六要素修改记录', |
|||
type:'six_speed', |
|||
} |
|||
] |
|||
} |
|||
}, |
|||
onLoad(options) { |
|||
this.filteredData.customer_resource_id = options.resource_id//客户资源表id |
|||
}, |
|||
onShow() { |
|||
this.init() |
|||
}, |
|||
//下拉刷新 |
|||
async onPullDownRefresh() { |
|||
//重置为第一页 |
|||
await this.resetFilteredData() |
|||
await this.getList() |
|||
}, |
|||
methods: { |
|||
//初始化 |
|||
async init() { |
|||
await this.getList()//获取客户修改记录列表 |
|||
}, |
|||
|
|||
//加载更多(下一页) |
|||
loadMoreData() { |
|||
//判断是否加载 |
|||
if (!this.isReachedBottom) { |
|||
this.isReachedBottom = true;//设置为不可请求状态 |
|||
this.getList(); |
|||
} |
|||
}, |
|||
//重置为第一页 |
|||
async resetFilteredData() { |
|||
this.isReachedBottom = false; // 重置状态,以便下次触发加载更多 |
|||
|
|||
this.filteredData.page = 1//当前页码 |
|||
this.filteredData.limit = 10//每页返回数据条数 |
|||
this.filteredData.total = 10//数据总条数 |
|||
}, |
|||
//获取列表-客户信息修改记录 |
|||
async getList(){ |
|||
this.loading = true |
|||
|
|||
let data = {...this.filteredData} |
|||
|
|||
//判断是否还有数据 |
|||
if ((this.filteredData.page - 1) * this.filteredData.limit >= this.filteredData.total) { |
|||
this.loading = false |
|||
uni.showToast({ |
|||
title: '暂无更多', |
|||
icon: 'none' |
|||
}) |
|||
return |
|||
} |
|||
|
|||
if(data.page == 1){ |
|||
this.tableList = [] |
|||
} |
|||
|
|||
let res = await apiRoute.xs_customerResourcesGetEditLogList(data) |
|||
this.loading = false |
|||
this.isReachedBottom = false; |
|||
if (res.code != 1){ |
|||
uni.showToast({ |
|||
title: res.msg, |
|||
icon: 'none' |
|||
}) |
|||
return |
|||
} |
|||
|
|||
console.log(123123123,res) |
|||
|
|||
this.tableList = this.tableList.concat(res.data.data); // 使用 concat 方法 将新数据追加到数组中 |
|||
|
|||
console.log('列表1',this.tableList) |
|||
this.filteredData.total = res.data.total |
|||
this.filteredData.page++ |
|||
|
|||
}, |
|||
|
|||
//切换tag列表 |
|||
async segmented(e) { |
|||
console.log('切换',e) |
|||
//e.id|0=基础信息 1=六要素 |
|||
let status = e.id |
|||
this.optionTableId = String(status) |
|||
this.filteredData.type = e.type//查询类型|resource=客户资源,six_speed=六要素 |
|||
|
|||
await this.resetFilteredData() |
|||
await this.getList() |
|||
}, |
|||
|
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style lang="less" scoped> |
|||
.assemble{ |
|||
width: 100%; |
|||
height: 100%; |
|||
background-color: #292929; |
|||
overflow: auto; |
|||
.tab_section{ |
|||
padding: 0 20rpx; |
|||
padding-top: 30rpx; |
|||
} |
|||
.table_list{ |
|||
color: #fff; |
|||
::v-deep .fui-timeaxis__line{ |
|||
background-color: #fff; |
|||
} |
|||
|
|||
.itme_box { |
|||
margin-top: 30rpx; |
|||
display: flex; |
|||
flex-direction: column; |
|||
gap: 25rpx; |
|||
.title_name{ |
|||
font-size: 28rpx; |
|||
} |
|||
.table_section{ |
|||
.table_box{ |
|||
width: 100%; |
|||
border-collapse: collapse; |
|||
table-layout: fixed; |
|||
tr{ |
|||
display: flex; |
|||
th{} |
|||
td{} |
|||
} |
|||
} |
|||
} |
|||
|
|||
} |
|||
|
|||
::v-deep .fui-timeaxis__wrap{ |
|||
|
|||
} |
|||
.fui-node { |
|||
width: 32rpx; |
|||
height: 32rpx; |
|||
border-radius: 50%; |
|||
background: #fff; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
border: 4px solid #ccc; |
|||
box-sizing: border-box; |
|||
} |
|||
|
|||
.fui-process__node { |
|||
padding-left: 20rpx; |
|||
} |
|||
|
|||
.fui-title { |
|||
font-size: 34rpx; |
|||
line-height: 34rpx; |
|||
font-weight: bold; |
|||
} |
|||
|
|||
.fui-descr { |
|||
font-size: 28rpx; |
|||
padding-top: 12rpx; |
|||
color: #7F7F7F; |
|||
} |
|||
|
|||
.fui-time__left { |
|||
font-size: 36rpx; |
|||
line-height: 36rpx; |
|||
text-align: right; |
|||
padding-right: 20rpx; |
|||
} |
|||
|
|||
.fui-node__pbtm { |
|||
padding-bottom: 88rpx; |
|||
} |
|||
|
|||
|
|||
} |
|||
|
|||
} |
|||
</style> |
|||
File diff suppressed because it is too large
@ -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,427 @@ |
|||
<template> |
|||
<view class="assemble"> |
|||
<view style="height: 20rpx;"></view> |
|||
<!-- 时间筛选 --> |
|||
<view class="filter-section"> |
|||
<picker mode="date" fields="month" :value="currentDate" @change="onDateChange"> |
|||
<view class="date-picker"> |
|||
<text>{{currentDate}}</text> |
|||
<image class="drop-image" :src="$util.img('/static/images/drop.png')" mode="aspectFit"></image> |
|||
</view> |
|||
</picker> |
|||
</view> |
|||
|
|||
<!-- 本月提成卡片 --> |
|||
<view class="commission-card"> |
|||
<view class="card-title">本月提成</view> |
|||
<view class="commission-amount">¥{{totalCommission}}</view> |
|||
</view> |
|||
|
|||
<!-- 续费提成记录 --> |
|||
<view class="record-card"> |
|||
<view class="card-title">续费提成</view> |
|||
<view class="table"> |
|||
<view class="table-header"> |
|||
<view class="th">时间</view> |
|||
<view class="th">到期数</view> |
|||
<view class="th">续费数</view> |
|||
<view class="th">续费率</view> |
|||
<view class="th">提成</view> |
|||
</view> |
|||
<view class="table-body"> |
|||
<view class="tr" v-for="(item, index) in renewalRecords" :key="index"> |
|||
<view class="td">{{item.time}}</view> |
|||
<view class="td">{{item.expireCount}}</view> |
|||
<view class="td">{{item.renewCount}}</view> |
|||
<view class="td">{{item.renewRate}}%</view> |
|||
<view class="td">¥{{item.commission}}</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 新招课包 --> |
|||
<view class="record-card"> |
|||
<view class="card-title">新招课包</view> |
|||
<view class="table"> |
|||
<view class="table-header"> |
|||
<view class="th">成交数</view> |
|||
<view class="th">提成</view> |
|||
<view class="th">合计</view> |
|||
</view> |
|||
<view class="table-body"> |
|||
<view class="tr"> |
|||
<view class="td">{{newPackageCount}}</view> |
|||
<view class="td">¥{{newPackageCommission}}</view> |
|||
<view class="td">¥{{newPackageTotal}}</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 私教课包 --> |
|||
<view class="record-card"> |
|||
<view class="card-title">私教课包</view> |
|||
<view class="table"> |
|||
<view class="table-header"> |
|||
<view class="th">数量</view> |
|||
<view class="th">提成</view> |
|||
</view> |
|||
<view class="table-body"> |
|||
<view class="tr"> |
|||
<view class="td">{{privatePackageCount}}</view> |
|||
<view class="td">¥{{privatePackageCommission}}</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 时间卡 --> |
|||
<view class="record-card"> |
|||
<view class="card-title">时间卡</view> |
|||
<view class="table"> |
|||
<view class="table-header"> |
|||
<view class="th">时间卡</view> |
|||
<view class="th">数量</view> |
|||
<view class="th">提成</view> |
|||
</view> |
|||
<view class="table-body"> |
|||
<view class="tr"> |
|||
<view class="td">{{timeCardCount}}</view> |
|||
<view class="td">{{timeCardAmount}}</view> |
|||
<view class="td">¥{{timeCardCommission}}</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 其他提成 --> |
|||
<view class="record-card"> |
|||
<view class="card-title">其他提成</view> |
|||
<view class="table"> |
|||
<view class="table-header"> |
|||
<view class="th">项目</view> |
|||
<view class="th">金额</view> |
|||
</view> |
|||
<view class="table-body"> |
|||
<view class="tr" v-for="(item, index) in otherCommissions" :key="index"> |
|||
<view class="td">{{item.project}}</view> |
|||
<view class="td">¥{{item.amount}}</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
import apiRoute from '@/api/apiRoute.js' |
|||
|
|||
export default { |
|||
data() { |
|||
return { |
|||
currentDate: this.formatDate(new Date()), |
|||
totalCommission: 0, |
|||
renewalRecords: [], |
|||
newPackageCount: 0, |
|||
newPackageCommission: 0, |
|||
newPackageTotal: 0, |
|||
privatePackageCount: 0, |
|||
privatePackageCommission: 0, |
|||
timeCardCount: 0, |
|||
timeCardAmount: 0, |
|||
timeCardCommission: 0, |
|||
otherCommissions: [] |
|||
} |
|||
}, |
|||
onShow() { |
|||
this.getStatisticsData() |
|||
}, |
|||
methods: { |
|||
formatDate(date) { |
|||
const year = date.getFullYear() |
|||
const month = String(date.getMonth() + 1).padStart(2, '0') |
|||
return `${year}-${month}` |
|||
}, |
|||
onDateChange(e) { |
|||
this.currentDate = e.detail.value |
|||
this.getStatisticsData() |
|||
}, |
|||
async getStatisticsData() { |
|||
try { |
|||
const res = await apiRoute.xs_statisticsMarketData({ |
|||
date: this.currentDate |
|||
}) |
|||
if (res && res.code === 1) { |
|||
const data = res.data |
|||
this.totalCommission = data.totalCommission || 0 |
|||
this.renewalRecords = data.renewalRecords || [] |
|||
this.newPackageCount = data.newPackageCount || 0 |
|||
this.newPackageCommission = data.newPackageCommission || 0 |
|||
this.newPackageTotal = data.newPackageTotal || 0 |
|||
this.privatePackageCount = data.privatePackageCount || 0 |
|||
this.privatePackageCommission = data.privatePackageCommission || 0 |
|||
this.timeCardCount = data.timeCardCount || 0 |
|||
this.timeCardAmount = data.timeCardAmount || 0 |
|||
this.timeCardCommission = data.timeCardCommission || 0 |
|||
this.otherCommissions = data.otherCommissions || [] |
|||
} |
|||
} catch (error) { |
|||
console.error('获取统计数据失败:', error) |
|||
uni.showToast({ |
|||
title: '获取数据失败', |
|||
icon: 'none' |
|||
}) |
|||
} |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style lang="less" scoped> |
|||
//自定义导航栏 |
|||
.navbar_section { |
|||
border: 1px solid #23262F; |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: center; |
|||
background: #23262F; |
|||
box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.18); |
|||
|
|||
.title { |
|||
padding: 40rpx 0rpx; |
|||
// #ifdef MP-WEIXIN |
|||
padding: 80rpx 0rpx; |
|||
// #endif |
|||
font-size: 30rpx; |
|||
color: #F5F6FA; |
|||
font-weight: bold; |
|||
letter-spacing: 2rpx; |
|||
} |
|||
} |
|||
|
|||
.assemble { |
|||
width: 100%; |
|||
min-height: 100vh; |
|||
background: #181A20; |
|||
padding-bottom: 100rpx; |
|||
} |
|||
|
|||
.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; |
|||
filter: brightness(0.8); |
|||
} |
|||
|
|||
.title { |
|||
font-size: 30rpx; |
|||
color: #F5F6FA; |
|||
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; |
|||
} |
|||
|
|||
.filter-section { |
|||
padding: 20rpx 30rpx; |
|||
background: #23262F; |
|||
margin-bottom: 20rpx; |
|||
border-radius: 12rpx; |
|||
box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.2); |
|||
|
|||
.date-picker { |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
font-size: 32rpx; |
|||
color: #F5F6FA; |
|||
|
|||
.drop-image { |
|||
width: 30rpx; |
|||
height: 30rpx; |
|||
margin-left: 10rpx; |
|||
filter: brightness(0.8); |
|||
} |
|||
} |
|||
} |
|||
|
|||
.commission-card { |
|||
background: linear-gradient(135deg, #FF6B6B, #FF8E8E); |
|||
margin: 20rpx; |
|||
padding: 30rpx; |
|||
border-radius: 16rpx; |
|||
color: #fff; |
|||
box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.25); |
|||
|
|||
.card-title { |
|||
font-size: 28rpx; |
|||
margin-bottom: 20rpx; |
|||
color: #fff; |
|||
} |
|||
|
|||
.commission-amount { |
|||
font-size: 48rpx; |
|||
font-weight: bold; |
|||
color: #fff; |
|||
} |
|||
} |
|||
|
|||
.record-card { |
|||
background: #23262F; |
|||
margin: 20rpx; |
|||
padding: 20rpx; |
|||
border-radius: 16rpx; |
|||
box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.18); |
|||
|
|||
.card-title { |
|||
font-size: 30rpx; |
|||
color: #F5F6FA; |
|||
margin-bottom: 20rpx; |
|||
font-weight: bold; |
|||
} |
|||
|
|||
.table { |
|||
width: 100%; |
|||
|
|||
.table-header { |
|||
display: flex; |
|||
background: #181A20; |
|||
padding: 20rpx 0; |
|||
border-radius: 8rpx 8rpx 0 0; |
|||
|
|||
.th { |
|||
flex: 1; |
|||
text-align: center; |
|||
font-size: 26rpx; |
|||
color: #A0A3B1; |
|||
font-weight: 500; |
|||
} |
|||
} |
|||
|
|||
.table-body { |
|||
.tr { |
|||
display: flex; |
|||
padding: 20rpx 0; |
|||
border-bottom: 1px solid #33343A; |
|||
transition: background 0.2s; |
|||
|
|||
&:last-child { |
|||
border-bottom: none; |
|||
} |
|||
|
|||
.td { |
|||
flex: 1; |
|||
text-align: center; |
|||
font-size: 26rpx; |
|||
color: #F5F6FA; |
|||
font-weight: 400; |
|||
} |
|||
&:hover { |
|||
background: #23262F; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
// 其他细节样式 |
|||
::-webkit-scrollbar { |
|||
display: none; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,495 @@ |
|||
<!--校区数据--> |
|||
<template> |
|||
<view class="main_box"> |
|||
<!--自定义导航栏--> |
|||
<view class="navbar_section"> |
|||
<view class="navbar_content"> |
|||
<view class="back_btn" @click="goBack"> |
|||
<fui-icon name="arrowleft" size="32" color="#fff"></fui-icon> |
|||
</view> |
|||
<view class="title">校区数据</view> |
|||
<view class="placeholder"></view> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="content_section"> |
|||
<!-- 校区选择器 --> |
|||
<view class="campus_selector"> |
|||
<view class="selector_label">选择校区:</view> |
|||
<view class="selector_box"> |
|||
<view class="selected_campus">请选择校区</view> |
|||
<fui-icon name="dropdown" size="24" color="#999"></fui-icon> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 数据概览卡片 --> |
|||
<view class="overview_cards"> |
|||
<view class="card_item"> |
|||
<view class="card_icon"> |
|||
<fui-icon name="home" size="40" color="#29D3B4"></fui-icon> |
|||
</view> |
|||
<view class="card_content"> |
|||
<view class="card_title">部门数量</view> |
|||
<view class="card_value">--</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="card_item"> |
|||
<view class="card_icon"> |
|||
<fui-icon name="addressbook" size="40" color="#29D3B4"></fui-icon> |
|||
</view> |
|||
<view class="card_content"> |
|||
<view class="card_title">员工总数</view> |
|||
<view class="card_value">--</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="card_item"> |
|||
<view class="card_icon"> |
|||
<fui-icon name="star" size="40" color="#29D3B4"></fui-icon> |
|||
</view> |
|||
<view class="card_content"> |
|||
<view class="card_title">客户总数</view> |
|||
<view class="card_value">--</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="card_item"> |
|||
<view class="card_icon"> |
|||
<fui-icon name="wallet" size="40" color="#29D3B4"></fui-icon> |
|||
</view> |
|||
<view class="card_content"> |
|||
<view class="card_title">校区业绩</view> |
|||
<view class="card_value">--</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 部门对比 --> |
|||
<view class="dept_comparison"> |
|||
<view class="section_title">部门业绩对比</view> |
|||
<view class="comparison_list"> |
|||
<view class="comparison_item"> |
|||
<view class="dept_info"> |
|||
<view class="dept_name">销售部</view> |
|||
<view class="dept_score">¥120000</view> |
|||
</view> |
|||
<view class="progress_bar"> |
|||
<view class="progress_fill" style="width: 80%"></view> |
|||
</view> |
|||
<view class="dept_percent">80%</view> |
|||
</view> |
|||
|
|||
<view class="comparison_item"> |
|||
<view class="dept_info"> |
|||
<view class="dept_name">市场部</view> |
|||
<view class="dept_score">¥100000</view> |
|||
</view> |
|||
<view class="progress_bar"> |
|||
<view class="progress_fill" style="width: 67%"></view> |
|||
</view> |
|||
<view class="dept_percent">67%</view> |
|||
</view> |
|||
|
|||
<view class="comparison_item"> |
|||
<view class="dept_info"> |
|||
<view class="dept_name">运营部</view> |
|||
<view class="dept_score">¥80000</view> |
|||
</view> |
|||
<view class="progress_bar"> |
|||
<view class="progress_fill" style="width: 53%"></view> |
|||
</view> |
|||
<view class="dept_percent">53%</view> |
|||
</view> |
|||
|
|||
<view class="comparison_item"> |
|||
<view class="dept_info"> |
|||
<view class="dept_name">客服部</view> |
|||
<view class="dept_score">¥60000</view> |
|||
</view> |
|||
<view class="progress_bar"> |
|||
<view class="progress_fill" style="width: 40%"></view> |
|||
</view> |
|||
<view class="dept_percent">40%</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 月度趋势 --> |
|||
<view class="trend_section"> |
|||
<view class="section_title">月度趋势</view> |
|||
<view class="trend_chart"> |
|||
<view class="chart_placeholder"> |
|||
<fui-icon name="linechart" size="80" color="#ddd"></fui-icon> |
|||
<view class="placeholder_text">图表功能待开发</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 功能按钮区域 --> |
|||
<view class="function_section"> |
|||
<view class="section_title">数据分析</view> |
|||
<view class="function_grid"> |
|||
<view class="function_item"> |
|||
<view class="function_icon"> |
|||
<fui-icon name="barchart" size="32" color="#29D3B4"></fui-icon> |
|||
</view> |
|||
<view class="function_text">校区对比</view> |
|||
</view> |
|||
|
|||
<view class="function_item"> |
|||
<view class="function_icon"> |
|||
<fui-icon name="piechart" size="32" color="#29D3B4"></fui-icon> |
|||
</view> |
|||
<view class="function_text">部门分析</view> |
|||
</view> |
|||
|
|||
<view class="function_item"> |
|||
<view class="function_icon"> |
|||
<fui-icon name="linechart" size="32" color="#29D3B4"></fui-icon> |
|||
</view> |
|||
<view class="function_text">趋势分析</view> |
|||
</view> |
|||
|
|||
<view class="function_item"> |
|||
<view class="function_icon"> |
|||
<fui-icon name="list" size="32" color="#29D3B4"></fui-icon> |
|||
</view> |
|||
<view class="function_text">详细报表</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 提示信息 --> |
|||
<view class="tips_section"> |
|||
<view class="tips_title">功能说明</view> |
|||
<view class="tips_content"> |
|||
这里将显示校区的各项数据统计,包括部门数量、员工总数、客户数量、校区业绩等。 |
|||
具体功能待后续开发实现。 |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
import fuiIcon from "@/components/firstui/fui-icon/fui-icon.vue" |
|||
|
|||
export default { |
|||
components: { |
|||
fuiIcon, |
|||
}, |
|||
data() { |
|||
return { |
|||
|
|||
} |
|||
}, |
|||
onLoad() { |
|||
|
|||
}, |
|||
methods: { |
|||
// 返回上一页 |
|||
goBack() { |
|||
uni.navigateBack() |
|||
}, |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style lang="less" scoped> |
|||
.main_box { |
|||
background: #f5f5f5; |
|||
min-height: 100vh; |
|||
} |
|||
|
|||
// 自定义导航栏 |
|||
.navbar_section { |
|||
background: #29D3B4; |
|||
padding-top: 20rpx; |
|||
|
|||
// 小程序端样式 |
|||
// #ifdef MP-WEIXIN |
|||
padding-top: 90rpx; |
|||
// #endif |
|||
|
|||
.navbar_content { |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: space-between; |
|||
padding: 20rpx 24rpx 40rpx 24rpx; |
|||
|
|||
.back_btn { |
|||
width: 60rpx; |
|||
height: 60rpx; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
} |
|||
|
|||
.title { |
|||
font-size: 32rpx; |
|||
color: #fff; |
|||
font-weight: 500; |
|||
} |
|||
|
|||
.placeholder { |
|||
width: 60rpx; |
|||
} |
|||
} |
|||
} |
|||
|
|||
// 内容区域 |
|||
.content_section { |
|||
padding: 40rpx 24rpx; |
|||
|
|||
// 校区选择器 |
|||
.campus_selector { |
|||
background: #fff; |
|||
border-radius: 16rpx; |
|||
padding: 32rpx 24rpx; |
|||
margin-bottom: 32rpx; |
|||
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05); |
|||
|
|||
.selector_label { |
|||
font-size: 28rpx; |
|||
color: #333; |
|||
margin-bottom: 16rpx; |
|||
font-weight: 500; |
|||
} |
|||
|
|||
.selector_box { |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: space-between; |
|||
padding: 20rpx 24rpx; |
|||
background: #f8f8f8; |
|||
border-radius: 12rpx; |
|||
|
|||
.selected_campus { |
|||
font-size: 26rpx; |
|||
color: #666; |
|||
} |
|||
} |
|||
} |
|||
|
|||
// 概览卡片 |
|||
.overview_cards { |
|||
display: grid; |
|||
grid-template-columns: repeat(2, 1fr); |
|||
gap: 24rpx; |
|||
margin-bottom: 40rpx; |
|||
|
|||
.card_item { |
|||
background: #fff; |
|||
border-radius: 16rpx; |
|||
padding: 32rpx 24rpx; |
|||
display: flex; |
|||
align-items: center; |
|||
gap: 20rpx; |
|||
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05); |
|||
|
|||
.card_icon { |
|||
width: 64rpx; |
|||
height: 64rpx; |
|||
background: rgba(41, 211, 180, 0.1); |
|||
border-radius: 50%; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
} |
|||
|
|||
.card_content { |
|||
flex: 1; |
|||
|
|||
.card_title { |
|||
font-size: 24rpx; |
|||
color: #999; |
|||
margin-bottom: 8rpx; |
|||
} |
|||
|
|||
.card_value { |
|||
font-size: 32rpx; |
|||
color: #333; |
|||
font-weight: 600; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
// 部门对比 |
|||
.dept_comparison { |
|||
margin-bottom: 40rpx; |
|||
|
|||
.section_title { |
|||
font-size: 28rpx; |
|||
color: #333; |
|||
font-weight: 600; |
|||
margin-bottom: 24rpx; |
|||
} |
|||
|
|||
.comparison_list { |
|||
background: #fff; |
|||
border-radius: 16rpx; |
|||
padding: 32rpx 24rpx; |
|||
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05); |
|||
|
|||
.comparison_item { |
|||
display: flex; |
|||
align-items: center; |
|||
margin-bottom: 32rpx; |
|||
|
|||
&:last-child { |
|||
margin-bottom: 0; |
|||
} |
|||
|
|||
.dept_info { |
|||
width: 150rpx; |
|||
|
|||
.dept_name { |
|||
font-size: 26rpx; |
|||
color: #333; |
|||
margin-bottom: 8rpx; |
|||
font-weight: 500; |
|||
} |
|||
|
|||
.dept_score { |
|||
font-size: 22rpx; |
|||
color: #29D3B4; |
|||
font-weight: 600; |
|||
} |
|||
} |
|||
|
|||
.progress_bar { |
|||
flex: 1; |
|||
height: 16rpx; |
|||
background: #f0f0f0; |
|||
border-radius: 8rpx; |
|||
margin: 0 20rpx; |
|||
overflow: hidden; |
|||
|
|||
.progress_fill { |
|||
height: 100%; |
|||
background: linear-gradient(90deg, #29D3B4, #5CE1E6); |
|||
border-radius: 8rpx; |
|||
transition: width 0.3s ease; |
|||
} |
|||
} |
|||
|
|||
.dept_percent { |
|||
width: 60rpx; |
|||
text-align: right; |
|||
font-size: 24rpx; |
|||
color: #29D3B4; |
|||
font-weight: 600; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
// 月度趋势 |
|||
.trend_section { |
|||
margin-bottom: 40rpx; |
|||
|
|||
.section_title { |
|||
font-size: 28rpx; |
|||
color: #333; |
|||
font-weight: 600; |
|||
margin-bottom: 24rpx; |
|||
} |
|||
|
|||
.trend_chart { |
|||
background: #fff; |
|||
border-radius: 16rpx; |
|||
padding: 40rpx 24rpx; |
|||
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05); |
|||
|
|||
.chart_placeholder { |
|||
display: flex; |
|||
flex-direction: column; |
|||
align-items: center; |
|||
justify-content: center; |
|||
height: 200rpx; |
|||
|
|||
.placeholder_text { |
|||
font-size: 24rpx; |
|||
color: #999; |
|||
margin-top: 16rpx; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
// 功能区域 |
|||
.function_section { |
|||
margin-bottom: 40rpx; |
|||
|
|||
.section_title { |
|||
font-size: 28rpx; |
|||
color: #333; |
|||
font-weight: 600; |
|||
margin-bottom: 24rpx; |
|||
} |
|||
|
|||
.function_grid { |
|||
background: #fff; |
|||
border-radius: 16rpx; |
|||
padding: 32rpx 24rpx; |
|||
display: grid; |
|||
grid-template-columns: repeat(2, 1fr); |
|||
gap: 32rpx; |
|||
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05); |
|||
|
|||
.function_item { |
|||
display: flex; |
|||
flex-direction: column; |
|||
align-items: center; |
|||
padding: 24rpx 16rpx; |
|||
border-radius: 12rpx; |
|||
transition: all 0.3s ease; |
|||
|
|||
&:active { |
|||
background-color: #f5f5f5; |
|||
transform: scale(0.95); |
|||
} |
|||
|
|||
.function_icon { |
|||
width: 64rpx; |
|||
height: 64rpx; |
|||
background: rgba(41, 211, 180, 0.1); |
|||
border-radius: 50%; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
margin-bottom: 16rpx; |
|||
} |
|||
|
|||
.function_text { |
|||
font-size: 24rpx; |
|||
color: #333; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
// 提示信息 |
|||
.tips_section { |
|||
background: #fff; |
|||
border-radius: 16rpx; |
|||
padding: 32rpx 24rpx; |
|||
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05); |
|||
|
|||
.tips_title { |
|||
font-size: 28rpx; |
|||
color: #333; |
|||
font-weight: 600; |
|||
margin-bottom: 16rpx; |
|||
} |
|||
|
|||
.tips_content { |
|||
font-size: 24rpx; |
|||
color: #666; |
|||
line-height: 1.6; |
|||
} |
|||
} |
|||
} |
|||
</style> |
|||
@ -0,0 +1,445 @@ |
|||
<!--部门数据--> |
|||
<template> |
|||
<view class="main_box"> |
|||
<!--自定义导航栏--> |
|||
<view class="navbar_section"> |
|||
<view class="navbar_content"> |
|||
<view class="back_btn" @click="goBack"> |
|||
<fui-icon name="arrowleft" size="32" color="#fff"></fui-icon> |
|||
</view> |
|||
<view class="title">部门数据</view> |
|||
<view class="placeholder"></view> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="content_section"> |
|||
<!-- 部门选择器 --> |
|||
<view class="dept_selector"> |
|||
<view class="selector_label">选择部门:</view> |
|||
<view class="selector_box"> |
|||
<view class="selected_dept">请选择部门</view> |
|||
<fui-icon name="dropdown" size="24" color="#999"></fui-icon> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 数据概览卡片 --> |
|||
<view class="overview_cards"> |
|||
<view class="card_item"> |
|||
<view class="card_icon"> |
|||
<fui-icon name="addressbook" size="40" color="#29D3B4"></fui-icon> |
|||
</view> |
|||
<view class="card_content"> |
|||
<view class="card_title">部门人数</view> |
|||
<view class="card_value">--</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="card_item"> |
|||
<view class="card_icon"> |
|||
<fui-icon name="star" size="40" color="#29D3B4"></fui-icon> |
|||
</view> |
|||
<view class="card_content"> |
|||
<view class="card_title">总客户数</view> |
|||
<view class="card_value">--</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="card_item"> |
|||
<view class="card_icon"> |
|||
<fui-icon name="wallet" size="40" color="#29D3B4"></fui-icon> |
|||
</view> |
|||
<view class="card_content"> |
|||
<view class="card_title">部门业绩</view> |
|||
<view class="card_value">--</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="card_item"> |
|||
<view class="card_icon"> |
|||
<fui-icon name="check" size="40" color="#29D3B4"></fui-icon> |
|||
</view> |
|||
<view class="card_content"> |
|||
<view class="card_title">完成率</view> |
|||
<view class="card_value">--</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 排行榜 --> |
|||
<view class="ranking_section"> |
|||
<view class="section_title">部门排行榜</view> |
|||
<view class="ranking_list"> |
|||
<view class="ranking_item"> |
|||
<view class="rank_number first">1</view> |
|||
<view class="member_info"> |
|||
<view class="member_name">张三</view> |
|||
<view class="member_score">业绩:¥50000</view> |
|||
</view> |
|||
<view class="medal"> |
|||
<fui-icon name="star-fill" size="24" color="#FFD700"></fui-icon> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="ranking_item"> |
|||
<view class="rank_number second">2</view> |
|||
<view class="member_info"> |
|||
<view class="member_name">李四</view> |
|||
<view class="member_score">业绩:¥45000</view> |
|||
</view> |
|||
<view class="medal"> |
|||
<fui-icon name="star-fill" size="24" color="#C0C0C0"></fui-icon> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="ranking_item"> |
|||
<view class="rank_number third">3</view> |
|||
<view class="member_info"> |
|||
<view class="member_name">王五</view> |
|||
<view class="member_score">业绩:¥40000</view> |
|||
</view> |
|||
<view class="medal"> |
|||
<fui-icon name="star-fill" size="24" color="#CD7F32"></fui-icon> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 功能按钮区域 --> |
|||
<view class="function_section"> |
|||
<view class="section_title">数据分析</view> |
|||
<view class="function_grid"> |
|||
<view class="function_item"> |
|||
<view class="function_icon"> |
|||
<fui-icon name="barchart" size="32" color="#29D3B4"></fui-icon> |
|||
</view> |
|||
<view class="function_text">业绩对比</view> |
|||
</view> |
|||
|
|||
<view class="function_item"> |
|||
<view class="function_icon"> |
|||
<fui-icon name="piechart" size="32" color="#29D3B4"></fui-icon> |
|||
</view> |
|||
<view class="function_text">人员分布</view> |
|||
</view> |
|||
|
|||
<view class="function_item"> |
|||
<view class="function_icon"> |
|||
<fui-icon name="linechart" size="32" color="#29D3B4"></fui-icon> |
|||
</view> |
|||
<view class="function_text">趋势分析</view> |
|||
</view> |
|||
|
|||
<view class="function_item"> |
|||
<view class="function_icon"> |
|||
<fui-icon name="list" size="32" color="#29D3B4"></fui-icon> |
|||
</view> |
|||
<view class="function_text">详细报表</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 提示信息 --> |
|||
<view class="tips_section"> |
|||
<view class="tips_title">功能说明</view> |
|||
<view class="tips_content"> |
|||
这里将显示部门的各项数据统计,包括部门人员、客户数量、销售业绩、排行榜等。 |
|||
具体功能待后续开发实现。 |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
import fuiIcon from "@/components/firstui/fui-icon/fui-icon.vue" |
|||
|
|||
export default { |
|||
components: { |
|||
fuiIcon, |
|||
}, |
|||
data() { |
|||
return { |
|||
|
|||
} |
|||
}, |
|||
onLoad() { |
|||
|
|||
}, |
|||
methods: { |
|||
// 返回上一页 |
|||
goBack() { |
|||
uni.navigateBack() |
|||
}, |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style lang="less" scoped> |
|||
.main_box { |
|||
background: #f5f5f5; |
|||
min-height: 100vh; |
|||
} |
|||
|
|||
// 自定义导航栏 |
|||
.navbar_section { |
|||
background: #29D3B4; |
|||
padding-top: 20rpx; |
|||
|
|||
// 小程序端样式 |
|||
// #ifdef MP-WEIXIN |
|||
padding-top: 90rpx; |
|||
// #endif |
|||
|
|||
.navbar_content { |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: space-between; |
|||
padding: 20rpx 24rpx 40rpx 24rpx; |
|||
|
|||
.back_btn { |
|||
width: 60rpx; |
|||
height: 60rpx; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
} |
|||
|
|||
.title { |
|||
font-size: 32rpx; |
|||
color: #fff; |
|||
font-weight: 500; |
|||
} |
|||
|
|||
.placeholder { |
|||
width: 60rpx; |
|||
} |
|||
} |
|||
} |
|||
|
|||
// 内容区域 |
|||
.content_section { |
|||
padding: 40rpx 24rpx; |
|||
|
|||
// 部门选择器 |
|||
.dept_selector { |
|||
background: #fff; |
|||
border-radius: 16rpx; |
|||
padding: 32rpx 24rpx; |
|||
margin-bottom: 32rpx; |
|||
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05); |
|||
|
|||
.selector_label { |
|||
font-size: 28rpx; |
|||
color: #333; |
|||
margin-bottom: 16rpx; |
|||
font-weight: 500; |
|||
} |
|||
|
|||
.selector_box { |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: space-between; |
|||
padding: 20rpx 24rpx; |
|||
background: #f8f8f8; |
|||
border-radius: 12rpx; |
|||
|
|||
.selected_dept { |
|||
font-size: 26rpx; |
|||
color: #666; |
|||
} |
|||
} |
|||
} |
|||
|
|||
// 概览卡片 |
|||
.overview_cards { |
|||
display: grid; |
|||
grid-template-columns: repeat(2, 1fr); |
|||
gap: 24rpx; |
|||
margin-bottom: 40rpx; |
|||
|
|||
.card_item { |
|||
background: #fff; |
|||
border-radius: 16rpx; |
|||
padding: 32rpx 24rpx; |
|||
display: flex; |
|||
align-items: center; |
|||
gap: 20rpx; |
|||
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05); |
|||
|
|||
.card_icon { |
|||
width: 64rpx; |
|||
height: 64rpx; |
|||
background: rgba(41, 211, 180, 0.1); |
|||
border-radius: 50%; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
} |
|||
|
|||
.card_content { |
|||
flex: 1; |
|||
|
|||
.card_title { |
|||
font-size: 24rpx; |
|||
color: #999; |
|||
margin-bottom: 8rpx; |
|||
} |
|||
|
|||
.card_value { |
|||
font-size: 32rpx; |
|||
color: #333; |
|||
font-weight: 600; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
// 排行榜 |
|||
.ranking_section { |
|||
margin-bottom: 40rpx; |
|||
|
|||
.section_title { |
|||
font-size: 28rpx; |
|||
color: #333; |
|||
font-weight: 600; |
|||
margin-bottom: 24rpx; |
|||
} |
|||
|
|||
.ranking_list { |
|||
background: #fff; |
|||
border-radius: 16rpx; |
|||
padding: 24rpx; |
|||
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05); |
|||
|
|||
.ranking_item { |
|||
display: flex; |
|||
align-items: center; |
|||
padding: 20rpx 16rpx; |
|||
border-bottom: 1px solid #f0f0f0; |
|||
|
|||
&:last-child { |
|||
border-bottom: none; |
|||
} |
|||
|
|||
.rank_number { |
|||
width: 48rpx; |
|||
height: 48rpx; |
|||
border-radius: 50%; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
font-size: 24rpx; |
|||
font-weight: 600; |
|||
color: #fff; |
|||
margin-right: 24rpx; |
|||
|
|||
&.first { |
|||
background: #FFD700; |
|||
} |
|||
|
|||
&.second { |
|||
background: #C0C0C0; |
|||
} |
|||
|
|||
&.third { |
|||
background: #CD7F32; |
|||
} |
|||
} |
|||
|
|||
.member_info { |
|||
flex: 1; |
|||
|
|||
.member_name { |
|||
font-size: 28rpx; |
|||
color: #333; |
|||
margin-bottom: 8rpx; |
|||
font-weight: 500; |
|||
} |
|||
|
|||
.member_score { |
|||
font-size: 24rpx; |
|||
color: #666; |
|||
} |
|||
} |
|||
|
|||
.medal { |
|||
margin-left: 16rpx; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
// 功能区域 |
|||
.function_section { |
|||
margin-bottom: 40rpx; |
|||
|
|||
.section_title { |
|||
font-size: 28rpx; |
|||
color: #333; |
|||
font-weight: 600; |
|||
margin-bottom: 24rpx; |
|||
} |
|||
|
|||
.function_grid { |
|||
background: #fff; |
|||
border-radius: 16rpx; |
|||
padding: 32rpx 24rpx; |
|||
display: grid; |
|||
grid-template-columns: repeat(2, 1fr); |
|||
gap: 32rpx; |
|||
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05); |
|||
|
|||
.function_item { |
|||
display: flex; |
|||
flex-direction: column; |
|||
align-items: center; |
|||
padding: 24rpx 16rpx; |
|||
border-radius: 12rpx; |
|||
transition: all 0.3s ease; |
|||
|
|||
&:active { |
|||
background-color: #f5f5f5; |
|||
transform: scale(0.95); |
|||
} |
|||
|
|||
.function_icon { |
|||
width: 64rpx; |
|||
height: 64rpx; |
|||
background: rgba(41, 211, 180, 0.1); |
|||
border-radius: 50%; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
margin-bottom: 16rpx; |
|||
} |
|||
|
|||
.function_text { |
|||
font-size: 24rpx; |
|||
color: #333; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
// 提示信息 |
|||
.tips_section { |
|||
background: #fff; |
|||
border-radius: 16rpx; |
|||
padding: 32rpx 24rpx; |
|||
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05); |
|||
|
|||
.tips_title { |
|||
font-size: 28rpx; |
|||
color: #333; |
|||
font-weight: 600; |
|||
margin-bottom: 16rpx; |
|||
} |
|||
|
|||
.tips_content { |
|||
font-size: 24rpx; |
|||
color: #666; |
|||
line-height: 1.6; |
|||
} |
|||
} |
|||
} |
|||
</style> |
|||
@ -0,0 +1,523 @@ |
|||
<!--销售我的-首页--> |
|||
<template> |
|||
<view class="main_box"> |
|||
<!--自定义导航栏--> |
|||
<view class="navbar_section"> |
|||
<view class="title">我的</view> |
|||
</view> |
|||
|
|||
<view style="background:#29D3B4;"> |
|||
<!--用户信息--> |
|||
<view class="user_section"> |
|||
<view class="box"> |
|||
<view class="left" @click="openViewMyInfo()"> |
|||
<image class="pic" :src="userInfo.head_img"></image> |
|||
<view class="name">{{userInfo.name}}</view> |
|||
</view> |
|||
<view class="right"> |
|||
<view class="btn"></view> |
|||
<!-- <view class="btn">切换身份</view>--> |
|||
<view class="btn"></view> |
|||
</view> |
|||
</view> |
|||
<view class="bottom" v-for="(v,k) in userInfo.cameus_dept_arr"> |
|||
<view class="left"> |
|||
<view class="title">校区:</view> |
|||
<view class="title">{{v.campus_id_name || ''}}</view> |
|||
</view> |
|||
<view class="division"></view> |
|||
<view class="right"> |
|||
<view class="title">部门:</view> |
|||
<view class="title dept">{{v.dept_name_str || ''}}</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
|
|||
|
|||
<view class="main_section"> |
|||
<view class="grid_container"> |
|||
<view class="grid_item" @click="openViewReimbursementList()"> |
|||
<view class="icon_wrapper"> |
|||
<fui-icon name="bankcard" size="48" color="#29D3B4"></fui-icon> |
|||
</view> |
|||
<view class="item_text">报销记录</view> |
|||
</view> |
|||
|
|||
<view class="grid_item" @click="openViewMyAttendance()"> |
|||
<view class="icon_wrapper"> |
|||
<fui-icon name="calendar" size="48" color="#29D3B4"></fui-icon> |
|||
</view> |
|||
<view class="item_text">我的考勤</view> |
|||
</view> |
|||
|
|||
<view class="grid_item" @click="goCourseSchedule()"> |
|||
<view class="icon_wrapper"> |
|||
<fui-icon name="list" size="48" color="#29D3B4"></fui-icon> |
|||
</view> |
|||
<view class="item_text">课程安排</view> |
|||
</view> |
|||
|
|||
<view class="grid_item" @click="openViewMyMessage()"> |
|||
<view class="icon_wrapper"> |
|||
<fui-icon name="message" size="48" color="#29D3B4"></fui-icon> |
|||
</view> |
|||
<view class="item_text">我的消息</view> |
|||
</view> |
|||
|
|||
<view class="grid_item" @click="my_contract()"> |
|||
<view class="icon_wrapper"> |
|||
<fui-icon name="order" size="48" color="#29D3B4"></fui-icon> |
|||
</view> |
|||
<view class="item_text">我的合同</view> |
|||
</view> |
|||
|
|||
<view class="grid_item" @click="openMyData()"> |
|||
<view class="icon_wrapper"> |
|||
<fui-icon name="barchart" size="48" color="#29D3B4"></fui-icon> |
|||
</view> |
|||
<view class="item_text">我的数据</view> |
|||
</view> |
|||
|
|||
<view class="grid_item" @click="openDeptData()"> |
|||
<view class="icon_wrapper"> |
|||
<fui-icon name="piechart" size="48" color="#29D3B4"></fui-icon> |
|||
</view> |
|||
<view class="item_text">部门数据</view> |
|||
</view> |
|||
|
|||
<view class="grid_item" @click="openCampusData()"> |
|||
<view class="icon_wrapper"> |
|||
<fui-icon name="linechart" size="48" color="#29D3B4"></fui-icon> |
|||
</view> |
|||
<view class="item_text">校区数据</view> |
|||
</view> |
|||
|
|||
<view class="grid_item" @click="openViewSetUp()"> |
|||
<view class="icon_wrapper"> |
|||
<fui-icon name="setup" size="48" color="#29D3B4"></fui-icon> |
|||
</view> |
|||
<view class="item_text">设置</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 底部导航--> |
|||
<AQTabber/> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
import marketApi from '@/api/market.js'; |
|||
import apiRoute from '@/api/apiRoute.js'; |
|||
import { |
|||
Api_url |
|||
} from "@/common/config.js"; |
|||
import AQTabber from "@/components/AQ/AQTabber.vue" |
|||
import fuiIcon from "@/components/firstui/fui-icon/fui-icon.vue" |
|||
|
|||
|
|||
export default { |
|||
components: { |
|||
AQTabber, |
|||
fuiIcon, |
|||
}, |
|||
data() { |
|||
return { |
|||
formData:{}, |
|||
userInfo:{},//用户信息 |
|||
//上传图片APi路径 |
|||
uploadUrl: `${Api_url}/file/image`, |
|||
|
|||
signedClientListCount:0,//已签客户数量 |
|||
} |
|||
}, |
|||
onLoad() { |
|||
}, |
|||
onShow() { |
|||
this.init(); |
|||
}, |
|||
methods: { |
|||
|
|||
//初始化 |
|||
async init(){ |
|||
await this.getUserInfo()//获取用户详情 |
|||
// await this.getSignedClientListCount()//获取已签客户数量 |
|||
}, |
|||
|
|||
//获取已签客户数量 |
|||
async getSignedClientListCount(){ |
|||
let data = { |
|||
page:1, |
|||
limit:1, |
|||
} |
|||
let res = await marketApi.signClient(data); |
|||
if (res.code != 1){ |
|||
uni.showToast({ |
|||
title: res.msg, |
|||
icon: 'none' |
|||
}) |
|||
return |
|||
} |
|||
this.signedClientListCount = res.data.total |
|||
}, |
|||
|
|||
//获取用户详情 |
|||
async getUserInfo(){ |
|||
let data = {} |
|||
let res = await apiRoute.getPersonnelInfo(data); |
|||
if (res.code != 1){ |
|||
uni.showToast({ |
|||
title: res.msg, |
|||
icon: 'none' |
|||
}) |
|||
return |
|||
} |
|||
|
|||
res.data.cameus_dept_arr.forEach((v,k)=>{ |
|||
let d_arr = [] |
|||
v.dept_arr.forEach((dv,dk)=>{ |
|||
d_arr.push(dv.dept_name) |
|||
}) |
|||
//数组转字符串 |
|||
v.dept_name_str = d_arr.join(',') |
|||
}) |
|||
|
|||
this.userInfo = res.data |
|||
}, |
|||
|
|||
|
|||
//打开到课率统计 |
|||
openViewArrivalStatistics(){ |
|||
uni.navigateTo({ |
|||
url: '/pages-market/my/arrival_statistics' |
|||
}) |
|||
}, |
|||
|
|||
//打开即将到期 |
|||
openViewDueSoon(){ |
|||
uni.navigateTo({ |
|||
url: '/pages-market/my/due_soon' |
|||
}) |
|||
}, |
|||
|
|||
//打开授课统计 |
|||
openViewSchoolingStatistics(){ |
|||
uni.navigateTo({ |
|||
url: '/pages-market/my/schooling_statistics' |
|||
}) |
|||
}, |
|||
|
|||
//打开意见反馈 |
|||
openViewFeedback(){ |
|||
uni.navigateTo({ |
|||
url: '/pages-common/feedback' |
|||
}) |
|||
}, |
|||
|
|||
//打开个人资料 |
|||
openViewMyInfo(){ |
|||
uni.navigateTo({ |
|||
url: '/pages-market/my/info' |
|||
}) |
|||
}, |
|||
|
|||
|
|||
//跳转页面-已签客户 |
|||
openViewSignedClientList(){ |
|||
uni.navigateTo({ |
|||
url: '/pages-market/my/signed_client_list' |
|||
}) |
|||
}, |
|||
|
|||
//跳转页面-我的考勤 |
|||
openViewMyAttendance(){ |
|||
uni.navigateTo({ |
|||
url: '/pages-common/my_attendance' |
|||
}) |
|||
}, |
|||
|
|||
|
|||
//打开企业信息 |
|||
openViewFirmInfo(){ |
|||
uni.navigateTo({ |
|||
url: '/pages-market/my/firm_info' |
|||
}) |
|||
}, |
|||
|
|||
|
|||
//打开设置 |
|||
openViewSetUp(){ |
|||
uni.navigateTo({ |
|||
url: '/pages-market/my/set_up' |
|||
}) |
|||
}, |
|||
|
|||
//跳转页面-我的消息 |
|||
openViewMyMessage(){ |
|||
uni.navigateTo({ |
|||
url: '/pages-common/my_message' |
|||
}) |
|||
}, |
|||
|
|||
//跳转页面-报销记录 |
|||
openViewReimbursementList(){ |
|||
uni.navigateTo({ |
|||
url: '/pages-market/reimbursement/list' |
|||
}) |
|||
}, |
|||
|
|||
|
|||
goCourseSchedule(){ |
|||
uni.navigateTo({ |
|||
url: '/pages-coach/coach/schedule/schedule_table' |
|||
}) |
|||
}, |
|||
my_contract(){ |
|||
uni.navigateTo({ |
|||
url: '/pages-common/contract/my_contract' |
|||
}) |
|||
}, |
|||
|
|||
//打开我的数据 |
|||
openMyData(){ |
|||
uni.navigateTo({ |
|||
url: '/pages-market/my/my_data' |
|||
}) |
|||
}, |
|||
|
|||
//打开部门数据 |
|||
openDeptData(){ |
|||
uni.navigateTo({ |
|||
url: '/pages-market/my/dept_data' |
|||
}) |
|||
}, |
|||
|
|||
//打开校区数据 |
|||
openCampusData(){ |
|||
uni.navigateTo({ |
|||
url: '/pages-market/my/campus_data' |
|||
}) |
|||
}, |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style lang="less" scoped> |
|||
|
|||
.main_box{ |
|||
background: #292929; |
|||
//min-height: 28vh; |
|||
min-height: 100%; |
|||
} |
|||
|
|||
//自定义导航栏 |
|||
.navbar_section{ |
|||
border: 1px solid #29D3B4; |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: center; |
|||
background: #29D3B4; |
|||
.title{ |
|||
padding: 40rpx 0rpx; |
|||
|
|||
/* 小程序端样式 */ |
|||
// #ifdef MP-WEIXIN |
|||
padding-top: 110rpx; |
|||
padding-bottom: 40rpx; |
|||
// #endif |
|||
|
|||
font-size: 30rpx; |
|||
color: #fff; |
|||
} |
|||
} |
|||
|
|||
//用户信息 |
|||
.user_section { |
|||
background-color: #29D3B4; |
|||
padding-top: 10rpx; |
|||
padding-bottom: 42rpx; |
|||
color: #fff; |
|||
font-size: 28rpx; |
|||
.box{ |
|||
padding-left: 19rpx; |
|||
padding-right: 29rpx; |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
gap: 15rpx; |
|||
.left{ |
|||
display: flex; |
|||
align-items: center; |
|||
gap: 20rpx; |
|||
.pic{ |
|||
width: 144rpx; |
|||
height: 144rpx; |
|||
border-radius: 50%; |
|||
} |
|||
.name{ |
|||
font-size: 28rpx; |
|||
} |
|||
} |
|||
.right{ |
|||
display: flex; |
|||
flex-direction: column; |
|||
gap: 20rpx; |
|||
.btn{ |
|||
min-height: 28rpx; |
|||
font-size: 28rpx; |
|||
} |
|||
} |
|||
} |
|||
.bottom{ |
|||
margin-top: 30rpx; |
|||
padding: 0rpx 40rpx; |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: flex-start; |
|||
.title{ |
|||
font-size: 28rpx; |
|||
} |
|||
//分割线段 |
|||
.division{ |
|||
width: 2px; |
|||
height: 35rpx; |
|||
background-color: #fff; |
|||
} |
|||
.left{ |
|||
width: 48%; |
|||
display: flex; |
|||
} |
|||
.right{ |
|||
width: 48%; |
|||
display: flex; |
|||
.dept{ |
|||
width: 70%; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
//统计信息 |
|||
.count_section{ |
|||
position: relative; |
|||
.main{ |
|||
position: relative; |
|||
z-index: 2; |
|||
padding: 0rpx 24rpx; |
|||
display: flex; |
|||
justify-content: center; |
|||
.course_box{ |
|||
padding: 42rpx 28rpx; |
|||
width: 692rpx; |
|||
border-radius: 20rpx; |
|||
background-color: #fff; |
|||
display: flex; |
|||
flex-direction: column; |
|||
gap: 32rpx; |
|||
.top{ |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
.item{ |
|||
display: flex; |
|||
flex-direction: column; |
|||
align-items: center; |
|||
gap: 12rpx; |
|||
.num{ |
|||
color: #29D3B4; |
|||
font-size: 56rpx; |
|||
} |
|||
.intro{ |
|||
color: #AAAAAA; |
|||
font-size: 24rpx; |
|||
} |
|||
} |
|||
} |
|||
.bottom{ |
|||
font-size: 24rpx; |
|||
color: #333333; |
|||
text{ |
|||
margin-left: 10rpx; |
|||
color: #29D3B4; |
|||
} |
|||
.reduce{ |
|||
color: #ef95a0; |
|||
} |
|||
} |
|||
|
|||
} |
|||
} |
|||
.bg_box{ |
|||
z-index: 1; |
|||
width: 100%; |
|||
height: 150rpx; |
|||
} |
|||
|
|||
.bg_top{ |
|||
position: absolute; |
|||
top: 0; |
|||
background-color: #29D3B4; |
|||
} |
|||
.bg_bottom{ |
|||
top: 50%; |
|||
position: absolute; |
|||
background-color: #292929; |
|||
} |
|||
} |
|||
|
|||
.main_section{ |
|||
background: #292929 100%; |
|||
padding: 0 24rpx; |
|||
padding-top: 40rpx; |
|||
padding-bottom: 150rpx; |
|||
font-size: 24rpx; |
|||
color: #333333; |
|||
|
|||
.grid_container { |
|||
background: #fff; |
|||
border-radius: 16rpx; |
|||
padding: 40rpx 24rpx; |
|||
display: grid; |
|||
grid-template-columns: repeat(3, 1fr); |
|||
gap: 40rpx 20rpx; |
|||
|
|||
.grid_item { |
|||
display: flex; |
|||
flex-direction: column; |
|||
align-items: center; |
|||
justify-content: center; |
|||
padding: 20rpx 10rpx; |
|||
border-radius: 12rpx; |
|||
transition: all 0.3s ease; |
|||
|
|||
&:active { |
|||
background-color: #f5f5f5; |
|||
transform: scale(0.95); |
|||
} |
|||
|
|||
.icon_wrapper { |
|||
width: 80rpx; |
|||
height: 80rpx; |
|||
background: rgba(41, 211, 180, 0.1); |
|||
border-radius: 50%; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
margin-bottom: 16rpx; |
|||
} |
|||
|
|||
.item_text { |
|||
font-size: 24rpx; |
|||
color: #333333; |
|||
text-align: center; |
|||
font-weight: 400; |
|||
line-height: 1.2; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
</style> |
|||
@ -0,0 +1,505 @@ |
|||
<!--销售-个人资料-详情--> |
|||
<template> |
|||
<view class="main_box"> |
|||
|
|||
<view class="main_section"> |
|||
<view class="section"> |
|||
<view class="item"> |
|||
<image |
|||
@click="changeAvatar()" |
|||
class="pic" |
|||
:src="$util.img(formData.head_img)" |
|||
></image> |
|||
|
|||
<view class="btn" @click="changeAvatar()">修改头像</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="section"> |
|||
<view class="item"> |
|||
<view class="title"> |
|||
姓名 <text class="required">*</text> |
|||
</view> |
|||
<view class="input"> |
|||
<input v-model="formData.name" placeholder="请输入姓名" /> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="item"> |
|||
<view class="title"> |
|||
账号 <text class="required"></text> |
|||
</view> |
|||
<view class="input"> |
|||
<input v-model="formData.username" disabled placeholder="暂无" /> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="item"> |
|||
<view class="title"> |
|||
部门 <text class="required"></text> |
|||
</view> |
|||
<view class="input"> |
|||
<!-- <input disabled :placeholder="formData.department_name_str" />--> |
|||
<view class="dept disabled">{{formData.department_name_str || '暂无'}}</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="item"> |
|||
<view class="title"> |
|||
等级 <text class="required"></text> |
|||
</view> |
|||
<view class="input"> |
|||
<input v-model="formData.member_level_name" disabled placeholder="暂无" /> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="section"> |
|||
<view class="item"> |
|||
<view class="title"> |
|||
性别 <text class="required">*</text> |
|||
</view> |
|||
<view class="input"> |
|||
<input placeholder="请选择性别" v-model="formData.gender_str" @click="picker_show_sex=true"/> |
|||
<fui-picker |
|||
layer="1" |
|||
:linkage="true" |
|||
:options="options_sex_arr" |
|||
:show="picker_show_sex" |
|||
@change="changePickerSex" |
|||
@cancel="picker_show_sex=false" |
|||
></fui-picker> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="item"> |
|||
<view class="title"> |
|||
生日 <text class="required">*</text> |
|||
</view> |
|||
<view class="input"> |
|||
<input placeholder="请选择生日" @click="picker_show_birthday=true" v-model="formData.birthday"/> |
|||
<fui-date-picker |
|||
:minDate="minDate" |
|||
:maxDate="maxDate" |
|||
:show="picker_show_birthday" |
|||
type="3" |
|||
@change="changePickerBirthday" |
|||
@cancel="picker_show_birthday=false" |
|||
></fui-date-picker> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="item"> |
|||
<view class="title"> |
|||
邮箱 <text class="required">*</text> |
|||
</view> |
|||
<view class="input"> |
|||
<input v-model="formData.email" placeholder="请输入邮箱" /> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="item"> |
|||
<view class="title"> |
|||
手机 <text class="required">*</text> |
|||
</view> |
|||
<view class="input"> |
|||
<input v-model="formData.phone" placeholder="请输入手机" /> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="item"> |
|||
<view class="title"> |
|||
微信 <text class="required"></text> |
|||
</view> |
|||
<view class="input"> |
|||
<input v-model="formData.wx" placeholder="请输入微信" /> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="submet_btn" @click="submit">提交</view> |
|||
</view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
import apiRoute from '@/api/apiRoute.js'; |
|||
import marketApi from '@/api/market.js'; |
|||
import { |
|||
Api_url |
|||
} from "@/common/config.js"; |
|||
import AQTabber from "@/components/AQ/AQTabber" |
|||
|
|||
|
|||
export default { |
|||
components: { |
|||
AQTabber, |
|||
}, |
|||
data() { |
|||
return { |
|||
formData:{ |
|||
head_img:'',//头像地址 |
|||
name:'',//姓名 |
|||
username:'',//账号 |
|||
address:'',//住址 |
|||
gender:'',//性别|1男,2女 |
|||
gender_str:'', |
|||
birthday:'',//生日 |
|||
email:'',//邮箱 |
|||
phone:'',//手机 |
|||
wx:'',//微信号 |
|||
}, |
|||
|
|||
userInfo: {}, |
|||
|
|||
//上传图片APi路径 |
|||
uploadUrl: `${Api_url}/uploadImage`, |
|||
|
|||
//性别选择器 相关 |
|||
picker_show_sex: false, |
|||
sex_name:'请选择', |
|||
options_sex_arr: [ |
|||
{ |
|||
value: 1, |
|||
text: '男' |
|||
}, |
|||
{ |
|||
value: 2, |
|||
text: '女' |
|||
}, |
|||
], |
|||
|
|||
//生日选择器相关 |
|||
minDate: '', |
|||
maxDate: '', |
|||
picker_show_birthday: false, |
|||
upload_type: 1, |
|||
uploadHeadimg: '', |
|||
editHeadimg: '', |
|||
} |
|||
}, |
|||
onLoad() { |
|||
}, |
|||
onShow() { |
|||
this.init() |
|||
}, |
|||
methods: { |
|||
|
|||
async init(){ |
|||
// this.getBirthday() |
|||
this.setDateYear() |
|||
await this.getUserInfo() |
|||
}, |
|||
|
|||
//设置年份 |
|||
setDateYear() { |
|||
let currentYear = new Date().getFullYear(); |
|||
this.minDate = String(currentYear - 100); |
|||
this.maxDate = String(currentYear + 1); |
|||
}, |
|||
|
|||
//获取用户详情 |
|||
async getUserInfo(){ |
|||
let res = await apiRoute.getPersonnelInfo({}) |
|||
if (res.code != 1){ |
|||
uni.showToast({ |
|||
title: res.msg, |
|||
icon: 'none' |
|||
}) |
|||
return |
|||
} |
|||
|
|||
let gender_str = '' |
|||
if(res.data.gender == 1){ |
|||
gender_str = '男' |
|||
}else if(res.data.gender == 2){ |
|||
gender_str = '女' |
|||
} |
|||
|
|||
//用户表单 |
|||
this.formData = { |
|||
head_img: res.data.head_img,//头像地址 |
|||
name: res.data.name,//姓名 |
|||
username: res.data.phone,//账号 |
|||
address: res.data.address,//住址 |
|||
gender: res.data.gender,//性别|1男,2女 |
|||
gender_str:gender_str, |
|||
birthday: res.data.birthday,//生日 |
|||
email: res.data.email || '',//邮箱 |
|||
phone: res.data.phone,//手机 |
|||
wx: res.data.wx || '',//微信号 |
|||
member_level_name: res.data.member_level_name || '',//等级名称 |
|||
department_name_str:res.data.department_name_str || '暂无',//部门名称 |
|||
} |
|||
}, |
|||
|
|||
// 修改头像按钮 |
|||
changeAvatar() { |
|||
uni.chooseImage({ |
|||
count: 1, |
|||
sizeType: ['compressed'], |
|||
sourceType: ['album', 'camera'], |
|||
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, "") || "{}") |
|||
console.log('上传成功2', res) |
|||
if (res.code == 1) { |
|||
this.upload_type = 2 |
|||
this.formData.head_img = res.data.url |
|||
// this.editHeadimg = res.data.path |
|||
// this.uploadHeadimg = res.data.url |
|||
} else { |
|||
uni.showToast({ |
|||
title: res.msg, |
|||
icon: 'none' |
|||
}) |
|||
} |
|||
}, |
|||
}); |
|||
}, |
|||
|
|||
//性别选择相关 |
|||
changePickerSex(e) { |
|||
console.log('监听选择', e) |
|||
this.formData.gender = e.value |
|||
this.formData.gender_str = e.text |
|||
this.picker_show_sex = false |
|||
}, |
|||
|
|||
//生日选择相关 |
|||
//获取当前年月日+获取30年前的日期 |
|||
getBirthday() { |
|||
let date = new Date(); |
|||
let year = date.getFullYear(); |
|||
let month = date.getMonth() + 1; |
|||
let day = date.getDate(); |
|||
let year_30 = year - 30; |
|||
let month_30 = month; |
|||
let day_30 = day; |
|||
if (month_30 == 2 && day_30 > 28) { |
|||
month_30 = 3; |
|||
day_30 = 1; |
|||
} |
|||
if (month_30 == 4 && day_30 > 30) { |
|||
month_30 = 5; |
|||
day_30 = 1; |
|||
} |
|||
if (month_30 == 6 && day_30 > 30) { |
|||
month_30 = 7; |
|||
day_30 = 1; |
|||
} |
|||
if (month_30 == 9 && day_30 > 30) { |
|||
month_30 = 10; |
|||
day_30 = 1; |
|||
} |
|||
if (month_30 == 11 && day_30 > 30) { |
|||
month_30 = 12; |
|||
day_30 = 1; |
|||
} |
|||
if (month_30 > 12) { |
|||
month_30 = month_30 - 12; |
|||
year_30 = year_30 + 1; |
|||
} |
|||
let minDate = year_30 + "-" + month_30 + "-" + day_30 |
|||
let maxDate = year + "-" + month + "-" + day |
|||
this.minDate = minDate |
|||
this.maxDate = maxDate |
|||
}, |
|||
|
|||
//监听生日选择 |
|||
changePickerBirthday(e) { |
|||
console.log('监听生日选择', e) |
|||
this.formData.birthday = e.result |
|||
this.picker_show_birthday = false |
|||
}, |
|||
|
|||
//提交信息 |
|||
async submit() { |
|||
let data = {...this.formData} |
|||
|
|||
if(!data.head_img){ |
|||
uni.showToast({ |
|||
title: '请上传头像', |
|||
icon: 'none' |
|||
}) |
|||
return |
|||
} |
|||
|
|||
if(!data.name){ |
|||
uni.showToast({ |
|||
title: '请填写', |
|||
icon: 'none' |
|||
}) |
|||
return |
|||
} |
|||
|
|||
if(!data.gender){ |
|||
uni.showToast({ |
|||
title: '请选择性别', |
|||
icon: 'none' |
|||
}) |
|||
return |
|||
} |
|||
|
|||
if(!data.birthday){ |
|||
uni.showToast({ |
|||
title: '请选择生日', |
|||
icon: 'none' |
|||
}) |
|||
return |
|||
} |
|||
|
|||
if(!data.email){ |
|||
uni.showToast({ |
|||
title: '请填写邮箱', |
|||
icon: 'none' |
|||
}) |
|||
return |
|||
} |
|||
|
|||
if(!data.phone){ |
|||
uni.showToast({ |
|||
title: '请填写手机', |
|||
icon: 'none' |
|||
}) |
|||
return |
|||
} |
|||
|
|||
|
|||
let res = await apiRoute.editPersonnelInfo(data) |
|||
if(res.code != 1){ |
|||
uni.showToast({ |
|||
title: res.msg, |
|||
icon: 'none' |
|||
}) |
|||
return |
|||
} |
|||
uni.showToast({ |
|||
title: res.msg, |
|||
icon: 'success' |
|||
}) |
|||
//延迟1s执行 |
|||
setTimeout(() => { |
|||
this.getUserInfo() |
|||
}, 1000) |
|||
}, |
|||
|
|||
|
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style lang="less" scoped> |
|||
|
|||
.main_box{ |
|||
background: #292929 ; |
|||
} |
|||
|
|||
//自定义导航栏 |
|||
.navbar_section{ |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: center; |
|||
background: #29d3b4; |
|||
.title{ |
|||
padding: 40rpx 0rpx; |
|||
|
|||
/* 小程序端样式 */ |
|||
// #ifdef MP-WEIXIN |
|||
padding: 80rpx 0rpx; |
|||
// #endif |
|||
|
|||
font-size: 30rpx; |
|||
color: #315d55; |
|||
} |
|||
} |
|||
|
|||
.main_section{ |
|||
min-height: 100vh; |
|||
background: #292929 100%; |
|||
padding: 0 0rpx; |
|||
padding-top: 32rpx; |
|||
padding-bottom: 150rpx; |
|||
font-size: 28rpx; |
|||
color: #fff; |
|||
display: flex; |
|||
flex-direction: column; |
|||
gap: 20rpx; |
|||
|
|||
.section{ |
|||
background-color: #434544; |
|||
.item{ |
|||
padding: 20rpx 40rpx; |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
.pic{ |
|||
width: 82rpx; |
|||
height: 82rpx; |
|||
border-radius: 50%; |
|||
} |
|||
.btn{} |
|||
|
|||
.title{ |
|||
min-width: 100rpx; |
|||
display: flex; |
|||
align-items: center; |
|||
font-size: 26rpx; |
|||
color: #D7D7D7; |
|||
.required{ |
|||
margin-left: 10rpx; |
|||
color: red; |
|||
} |
|||
} |
|||
.input{ |
|||
display: flex; |
|||
justify-content: flex-end; |
|||
input{ |
|||
text-align: right; |
|||
} |
|||
.dept{ |
|||
width: 50%; |
|||
} |
|||
.disabled{ |
|||
color: #808080; |
|||
//禁止图标 |
|||
cursor: not-allowed; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
.submet_btn{ |
|||
margin: 0 auto; |
|||
margin-top: 40rpx; |
|||
border: 2px solid #25a18b; |
|||
color: #25a18b; |
|||
width: 80%; |
|||
height: 80rpx; |
|||
font-size: 30rpx; |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: center; |
|||
} |
|||
|
|||
|
|||
|
|||
} |
|||
|
|||
|
|||
</style> |
|||
@ -0,0 +1,292 @@ |
|||
<!--我的数据--> |
|||
<template> |
|||
<view class="main_box"> |
|||
<!--自定义导航栏--> |
|||
<view class="navbar_section"> |
|||
<view class="navbar_content"> |
|||
<view class="back_btn" @click="goBack"> |
|||
<fui-icon name="arrowleft" size="32" color="#fff"></fui-icon> |
|||
</view> |
|||
<view class="title">我的数据</view> |
|||
<view class="placeholder"></view> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="content_section"> |
|||
<!-- 数据概览卡片 --> |
|||
<view class="overview_cards"> |
|||
<view class="card_item"> |
|||
<view class="card_icon"> |
|||
<fui-icon name="star" size="40" color="#29D3B4"></fui-icon> |
|||
</view> |
|||
<view class="card_content"> |
|||
<view class="card_title">总客户数</view> |
|||
<view class="card_value">--</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="card_item"> |
|||
<view class="card_icon"> |
|||
<fui-icon name="check" size="40" color="#29D3B4"></fui-icon> |
|||
</view> |
|||
<view class="card_content"> |
|||
<view class="card_title">已签客户</view> |
|||
<view class="card_value">--</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="card_item"> |
|||
<view class="card_icon"> |
|||
<fui-icon name="wallet" size="40" color="#29D3B4"></fui-icon> |
|||
</view> |
|||
<view class="card_content"> |
|||
<view class="card_title">销售业绩</view> |
|||
<view class="card_value">--</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="card_item"> |
|||
<view class="card_icon"> |
|||
<fui-icon name="calendar" size="40" color="#29D3B4"></fui-icon> |
|||
</view> |
|||
<view class="card_content"> |
|||
<view class="card_title">本月任务</view> |
|||
<view class="card_value">--</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 功能按钮区域 --> |
|||
<view class="function_section"> |
|||
<view class="section_title">数据统计</view> |
|||
<view class="function_grid"> |
|||
<view class="function_item"> |
|||
<view class="function_icon"> |
|||
<fui-icon name="barchart" size="32" color="#29D3B4"></fui-icon> |
|||
</view> |
|||
<view class="function_text">客户统计</view> |
|||
</view> |
|||
|
|||
<view class="function_item"> |
|||
<view class="function_icon"> |
|||
<fui-icon name="piechart" size="32" color="#29D3B4"></fui-icon> |
|||
</view> |
|||
<view class="function_text">业绩统计</view> |
|||
</view> |
|||
|
|||
<view class="function_item"> |
|||
<view class="function_icon"> |
|||
<fui-icon name="linechart" size="32" color="#29D3B4"></fui-icon> |
|||
</view> |
|||
<view class="function_text">趋势分析</view> |
|||
</view> |
|||
|
|||
<view class="function_item"> |
|||
<view class="function_icon"> |
|||
<fui-icon name="list" size="32" color="#29D3B4"></fui-icon> |
|||
</view> |
|||
<view class="function_text">详细报表</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 提示信息 --> |
|||
<view class="tips_section"> |
|||
<view class="tips_title">功能说明</view> |
|||
<view class="tips_content"> |
|||
这里将显示您个人的各项数据统计,包括客户数量、销售业绩、任务完成情况等。 |
|||
具体功能待后续开发实现。 |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
import fuiIcon from "@/components/firstui/fui-icon/fui-icon.vue" |
|||
|
|||
export default { |
|||
components: { |
|||
fuiIcon, |
|||
}, |
|||
data() { |
|||
return { |
|||
|
|||
} |
|||
}, |
|||
onLoad() { |
|||
|
|||
}, |
|||
methods: { |
|||
// 返回上一页 |
|||
goBack() { |
|||
uni.navigateBack() |
|||
}, |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style lang="less" scoped> |
|||
.main_box { |
|||
background: #f5f5f5; |
|||
min-height: 100vh; |
|||
} |
|||
|
|||
// 自定义导航栏 |
|||
.navbar_section { |
|||
background: #29D3B4; |
|||
padding-top: 20rpx; |
|||
|
|||
// 小程序端样式 |
|||
// #ifdef MP-WEIXIN |
|||
padding-top: 90rpx; |
|||
// #endif |
|||
|
|||
.navbar_content { |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: space-between; |
|||
padding: 20rpx 24rpx 40rpx 24rpx; |
|||
|
|||
.back_btn { |
|||
width: 60rpx; |
|||
height: 60rpx; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
} |
|||
|
|||
.title { |
|||
font-size: 32rpx; |
|||
color: #fff; |
|||
font-weight: 500; |
|||
} |
|||
|
|||
.placeholder { |
|||
width: 60rpx; |
|||
} |
|||
} |
|||
} |
|||
|
|||
// 内容区域 |
|||
.content_section { |
|||
padding: 40rpx 24rpx; |
|||
|
|||
// 概览卡片 |
|||
.overview_cards { |
|||
display: grid; |
|||
grid-template-columns: repeat(2, 1fr); |
|||
gap: 24rpx; |
|||
margin-bottom: 40rpx; |
|||
|
|||
.card_item { |
|||
background: #fff; |
|||
border-radius: 16rpx; |
|||
padding: 32rpx 24rpx; |
|||
display: flex; |
|||
align-items: center; |
|||
gap: 20rpx; |
|||
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05); |
|||
|
|||
.card_icon { |
|||
width: 64rpx; |
|||
height: 64rpx; |
|||
background: rgba(41, 211, 180, 0.1); |
|||
border-radius: 50%; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
} |
|||
|
|||
.card_content { |
|||
flex: 1; |
|||
|
|||
.card_title { |
|||
font-size: 24rpx; |
|||
color: #999; |
|||
margin-bottom: 8rpx; |
|||
} |
|||
|
|||
.card_value { |
|||
font-size: 32rpx; |
|||
color: #333; |
|||
font-weight: 600; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
// 功能区域 |
|||
.function_section { |
|||
margin-bottom: 40rpx; |
|||
|
|||
.section_title { |
|||
font-size: 28rpx; |
|||
color: #333; |
|||
font-weight: 600; |
|||
margin-bottom: 24rpx; |
|||
} |
|||
|
|||
.function_grid { |
|||
background: #fff; |
|||
border-radius: 16rpx; |
|||
padding: 32rpx 24rpx; |
|||
display: grid; |
|||
grid-template-columns: repeat(2, 1fr); |
|||
gap: 32rpx; |
|||
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05); |
|||
|
|||
.function_item { |
|||
display: flex; |
|||
flex-direction: column; |
|||
align-items: center; |
|||
padding: 24rpx 16rpx; |
|||
border-radius: 12rpx; |
|||
transition: all 0.3s ease; |
|||
|
|||
&:active { |
|||
background-color: #f5f5f5; |
|||
transform: scale(0.95); |
|||
} |
|||
|
|||
.function_icon { |
|||
width: 64rpx; |
|||
height: 64rpx; |
|||
background: rgba(41, 211, 180, 0.1); |
|||
border-radius: 50%; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
margin-bottom: 16rpx; |
|||
} |
|||
|
|||
.function_text { |
|||
font-size: 24rpx; |
|||
color: #333; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
// 提示信息 |
|||
.tips_section { |
|||
background: #fff; |
|||
border-radius: 16rpx; |
|||
padding: 32rpx 24rpx; |
|||
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05); |
|||
|
|||
.tips_title { |
|||
font-size: 28rpx; |
|||
color: #333; |
|||
font-weight: 600; |
|||
margin-bottom: 16rpx; |
|||
} |
|||
|
|||
.tips_content { |
|||
font-size: 24rpx; |
|||
color: #666; |
|||
line-height: 1.6; |
|||
} |
|||
} |
|||
} |
|||
</style> |
|||
@ -0,0 +1,105 @@ |
|||
<!--设置页--> |
|||
<template> |
|||
<view class="assemble"> |
|||
<view style="height: 30rpx;"></view> |
|||
<view class="option" @click="update_pass()">修改密码</view> |
|||
<view class="option" @click="privacy_agreement(1)">用户协议</view> |
|||
<view class="option" @click="privacy_agreement(2)">隐私策略</view> |
|||
<view class="option" @click="clearCache()">清空缓存</view> |
|||
|
|||
<view style="width:90%;margin: 60rpx auto;"> |
|||
<fui-button background="#29d3b4" @click="loginOut()">退出账号</fui-button> |
|||
</view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
export default { |
|||
data() { |
|||
return { |
|||
|
|||
} |
|||
}, |
|||
methods: { |
|||
//退出登陆 |
|||
loginOut(){ |
|||
this.$util.loginOut() |
|||
}, |
|||
|
|||
privacy_agreement(type){ |
|||
uni.navigateTo({ |
|||
url: '/pages-common/privacy_agreement?type='+type |
|||
}) |
|||
}, |
|||
update_pass(){ |
|||
uni.navigateTo({ |
|||
url: '/pages-market/my/update_pass' |
|||
}) |
|||
}, |
|||
|
|||
// 清空缓存 |
|||
clearCache() { |
|||
uni.showModal({ |
|||
title: '清空缓存', |
|||
content: '确定要清空所有字典缓存吗?', |
|||
success: (res) => { |
|||
if (res.confirm) { |
|||
this.performClearCache(); |
|||
} |
|||
} |
|||
}); |
|||
}, |
|||
|
|||
// 执行清空缓存操作 |
|||
performClearCache() { |
|||
try { |
|||
// 获取本地存储的所有键 |
|||
const storageInfo = uni.getStorageInfoSync(); |
|||
const keys = storageInfo.keys; |
|||
|
|||
// 筛选出dict_开头的键并删除 |
|||
let clearCount = 0; |
|||
keys.forEach(key => { |
|||
if (key.startsWith('dict_')) { |
|||
uni.removeStorageSync(key); |
|||
clearCount++; |
|||
} |
|||
}); |
|||
|
|||
// 显示清理结果 |
|||
uni.showToast({ |
|||
title: `已清空${clearCount}个字典缓存`, |
|||
icon: 'success', |
|||
duration: 2000 |
|||
}); |
|||
|
|||
console.log(`清空缓存完成,共清理${clearCount}个dict_开头的缓存项`); |
|||
} catch (error) { |
|||
console.error('清空缓存失败:', error); |
|||
uni.showToast({ |
|||
title: '清空缓存失败', |
|||
icon: 'none', |
|||
duration: 2000 |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style lang="less" scoped> |
|||
.assemble{ |
|||
width: 100%; |
|||
height: 100vh; |
|||
background: #333333; |
|||
} |
|||
.option{ |
|||
margin-bottom: 20rpx; |
|||
background: #404045; |
|||
width: 100%; |
|||
font-size: 28rpx; |
|||
color: #fff; |
|||
line-height: 56rpx; |
|||
padding: 20rpx 0 20rpx 100rpx; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,275 @@ |
|||
<!--已签客户-列表--> |
|||
<template> |
|||
<view class="main_box"> |
|||
<!--自定义导航栏--> |
|||
<!-- <view class="navbar_section">--> |
|||
<!-- <view class="title">班级详情</view>--> |
|||
<!-- </view>--> |
|||
|
|||
<view class="main_section"> |
|||
<!-- 班级成员列表--> |
|||
<scroll-view |
|||
class="section_4" |
|||
scroll-y="true" |
|||
:lower-threshold="lowerThreshold" |
|||
@scrolltolower="loadMoreData" |
|||
style="height: 83vh;" |
|||
> |
|||
<view class="ul"> |
|||
<view class="li" |
|||
v-for="(v,k) in tableList" |
|||
:key="k" |
|||
@click="openViewStudentInfo(v)"> |
|||
<view class="left"> |
|||
<view class="box_1"> |
|||
<image class="pic" |
|||
v-if="v.header" :src="$util.img(v.header)"></image> |
|||
<image v-else class="pic" :src="$util.img('/uniapp_src/static/images/index/myk.png')"></image> |
|||
<!-- <view class="tag_box">--> |
|||
<!-- 即将到期--> |
|||
<!-- </view>--> |
|||
</view> |
|||
<view class="box_2"> |
|||
<view class="name">{{v.name}}</view> |
|||
<view class="date">课程截止时间:{{v.end_time}}</view> |
|||
</view> |
|||
</view> |
|||
<view class="right"> |
|||
<view class="item"> |
|||
<view>{{v.have_study_time}}</view> |
|||
<view>已上课时</view> |
|||
</view> |
|||
<view class="item"> |
|||
<view>{{v.end_study_time ? v.end_study_time:0}}</view> |
|||
<view>剩余课时</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</scroll-view> |
|||
</view> |
|||
|
|||
<!-- 底部导航--> |
|||
<!-- <AQTabber/>--> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
import marketApi from '@/api/market.js'; |
|||
import AQTabber from "@/components/AQ/AQTabber.vue" |
|||
|
|||
|
|||
export default { |
|||
components: { |
|||
AQTabber, |
|||
}, |
|||
data() { |
|||
return { |
|||
loading:false,//加载状态 |
|||
lowerThreshold: 100,//距离底部多远触发 |
|||
isReachedBottom: false,//防止重复加载|true=不可加载|false=可加载 |
|||
|
|||
//筛选条件 |
|||
filteredData:{ |
|||
page:1,//当前页码 |
|||
limit:10,//每页返回数据条数 |
|||
total:10,//数据总条数 |
|||
name: '',//班级名称 |
|||
}, |
|||
tableList:[],//聊天数据列表 |
|||
} |
|||
}, |
|||
onLoad() { |
|||
}, |
|||
onShow() { |
|||
this.init(); |
|||
}, |
|||
methods: { |
|||
//初始化 |
|||
async init(){ |
|||
await this.getList()//获取已签客户 |
|||
}, |
|||
|
|||
//加载更多(下一页) |
|||
loadMoreData() { |
|||
//判断是否加载 |
|||
if (!this.isReachedBottom) { |
|||
this.isReachedBottom = true;//设置为不可请求状态 |
|||
this.getList(); |
|||
} |
|||
}, |
|||
//重置为第一页 |
|||
async resetFilteredData() { |
|||
this.isReachedBottom = false; // 重置状态,以便下次触发加载更多 |
|||
|
|||
this.filteredData.page = 1//当前页码 |
|||
this.filteredData.limit = 10//每页返回数据条数 |
|||
this.filteredData.total = 10//数据总条数 |
|||
}, |
|||
//获取列表 |
|||
async getList(){ |
|||
this.loading = true |
|||
|
|||
let data = {...this.filteredData} |
|||
|
|||
//判断是否还有数据 |
|||
if ((this.filteredData.page - 1) * this.filteredData.limit >= this.filteredData.total) { |
|||
this.loading = false |
|||
uni.showToast({ |
|||
title: '暂无更多', |
|||
icon: 'none' |
|||
}) |
|||
return |
|||
} |
|||
|
|||
if(data.page == 1){ |
|||
this.tableList = [] |
|||
} |
|||
|
|||
let res = await marketApi.signClient(data) |
|||
this.loading = false |
|||
this.isReachedBottom = false; |
|||
if (res.code != 1){ |
|||
uni.showToast({ |
|||
title: res.msg, |
|||
icon: 'none' |
|||
}) |
|||
return |
|||
} |
|||
|
|||
this.tableList = this.tableList.concat(res.data.data); // 使用 concat 方法 将新数据追加到数组中 |
|||
|
|||
console.log('列表',this.tableList) |
|||
this.filteredData.total = res.data.total |
|||
this.filteredData.page++ |
|||
}, |
|||
|
|||
//打开学员详情页 |
|||
openViewStudentInfo(item){ |
|||
let students_id= item.id |
|||
uni.navigateTo({ |
|||
url: `/pages-coach/coach/student/info?students_id=${students_id}` |
|||
}) |
|||
}, |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style lang="less" scoped> |
|||
|
|||
.main_box{ |
|||
background: #292929 ; |
|||
} |
|||
|
|||
//自定义导航栏 |
|||
.navbar_section{ |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: center; |
|||
background: #292929; |
|||
.title{ |
|||
padding: 40rpx 0rpx; |
|||
|
|||
/* 小程序端样式 */ |
|||
// #ifdef MP-WEIXIN |
|||
padding: 80rpx 0rpx; |
|||
// #endif |
|||
|
|||
font-size: 30rpx; |
|||
color: #fff; |
|||
} |
|||
} |
|||
|
|||
.main_section{ |
|||
min-height: 95vh; |
|||
background: #292929 100%; |
|||
padding: 0 24rpx; |
|||
padding-top: 40rpx; |
|||
padding-bottom: 150rpx; |
|||
font-size: 24rpx; |
|||
color: #FFFFFF; |
|||
//班级成员列表 |
|||
.section_4{ |
|||
.ul{ |
|||
display: flex; |
|||
flex-direction: column; |
|||
gap: 10rpx; |
|||
.li{ |
|||
padding: 20rpx 0; |
|||
padding-bottom: 40rpx; |
|||
border-bottom: 2px solid #D7D7D7; |
|||
display: flex; |
|||
justify-content: space-between; |
|||
.left{ |
|||
display: flex; |
|||
align-items: center; |
|||
gap: 30rpx; |
|||
.box_1{ |
|||
padding-left: 20rpx; |
|||
display: flex; |
|||
flex-direction: column; |
|||
align-items: center; |
|||
justify-content: center; |
|||
position: relative; |
|||
.pic{ |
|||
width: 84rpx; |
|||
height: 84rpx; |
|||
border-radius: 50%; |
|||
} |
|||
.tag_box{ |
|||
position: absolute; |
|||
bottom: -30rpx; |
|||
width: 120rpx; |
|||
height: 38rpx; |
|||
background-color: #F59A23; |
|||
border-radius: 4rpx; |
|||
line-height: 35rpx; |
|||
text-align: center; |
|||
font-size: 20rpx; |
|||
} |
|||
} |
|||
.box_2{ |
|||
display: flex; |
|||
flex-direction: column; |
|||
gap: 20rpx; |
|||
.name{ |
|||
font-size: 28rpx; |
|||
} |
|||
.date{ |
|||
font-size: 24rpx; |
|||
} |
|||
} |
|||
} |
|||
.right{ |
|||
display: flex; |
|||
align-items: center; |
|||
gap: 14rpx; |
|||
.item{ |
|||
border: 1px solid #00E5BB; |
|||
border-radius: 10rpx; |
|||
width: 102rpx; |
|||
display: flex; |
|||
flex-direction: column; |
|||
view{ |
|||
text-align: center; |
|||
height: 50rpx; |
|||
line-height: 50rpx; |
|||
} |
|||
view:nth-child(1){ |
|||
font-size: 32rpx; |
|||
background-color: #fff; |
|||
color: #00e5bb; |
|||
} |
|||
view:nth-child(2){ |
|||
font-size: 20rpx; |
|||
background-color: #00e5bb; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
</style> |
|||
@ -0,0 +1,229 @@ |
|||
<!--修改密码--> |
|||
<template> |
|||
<view> |
|||
<view class="title"> |
|||
<view :class="{'green-text': tset_style === 1}">1.验证旧密码</view> |
|||
<view :class="{'green-text': tset_style === 2}">2.设置新密码</view> |
|||
</view> |
|||
<view :style="{'background-color':'#fff','width':'100%','height':'100vh' }"> |
|||
<view style="width: 95%;height: 30rpx;"></view> |
|||
<view v-if="tset_style == 1"> |
|||
<view class="describe"> |
|||
为保障您的账号安全,修改密码前请填写原密码 |
|||
</view> |
|||
<view style="width: 95%;margin:30rpx auto;"> |
|||
<fui-input borderTop placeholder="请输入原登录密码" v-model="old_password" |
|||
backgroundColor="#f2f2f2"></fui-input> |
|||
</view> |
|||
</view> |
|||
<view v-if="tset_style == 2"> |
|||
<view style="width: 95%;margin:30rpx auto;"> |
|||
<fui-input borderTop placeholder="请设置6-20位新的登录密码" v-model="formData.new_password" @input="input" |
|||
backgroundColor="#f2f2f2"></fui-input> |
|||
</view> |
|||
<view style="width: 95%;margin: auto;"> |
|||
<fui-input borderTop :padding="['20rpx','32rpx']" v-model="formData.new_password_2" placeholder="请再次输入新的登录密码" @input="input" |
|||
backgroundColor="#f2f2f2"> |
|||
</fui-input> |
|||
</view> |
|||
</view> |
|||
<view style="width: 95%;margin:60rpx auto;"> |
|||
<view class="btn_box"> |
|||
<fui-button |
|||
background="#465cff" |
|||
radius="5rpx" |
|||
@click="nextStep(1)" |
|||
v-if="tset_style != 1">上一步 |
|||
</fui-button> |
|||
|
|||
<fui-button background="#00be8c" radius="5rpx" @click="nextStep(2)" v-if="tset_style == 1">下一步</fui-button> |
|||
|
|||
<fui-button background="#00be8c" radius="5rpx" @click="submit" v-if="tset_style == 2">提交</fui-button> |
|||
</view> |
|||
|
|||
|
|||
<view style="width: 95%;margin:60rpx auto;"> |
|||
<fui-button background="#fff" radius="5rpx" @click="forgot" color="#999999" v-if="tset_style == 1">忘记原密码</fui-button> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
import apiRoute from '@/api/apiRoute.js'; |
|||
|
|||
export default { |
|||
data() { |
|||
return { |
|||
code: '', |
|||
user: '', |
|||
tset_style: 1,//tab栏目|1=验证旧密码页,2=设置新密码页 |
|||
|
|||
old_password:'',//旧密码 |
|||
|
|||
|
|||
formData:{ |
|||
phone:'',//当前登录用户的手机号 |
|||
new_password:'',//新密码 |
|||
new_password_2:'',//二次新密码 |
|||
key_value:'',//修改密码的key_value |
|||
}, |
|||
} |
|||
}, |
|||
onLoad() {}, |
|||
onShow() { |
|||
this.init()//初始化 |
|||
}, |
|||
methods: { |
|||
//初始化 |
|||
async init(){ |
|||
await this.getUserInfo() |
|||
}, |
|||
//获取用户信息 |
|||
async getUserInfo() { |
|||
let res = await apiRoute.getPersonnelInfo({}) |
|||
if (res.code != 1) { |
|||
uni.showToast({ |
|||
title: res.msg, |
|||
icon: 'none' |
|||
}) |
|||
return |
|||
} |
|||
this.formData.phone = res.data.phone//用户手机号 |
|||
}, |
|||
|
|||
//上/下一步切换 |
|||
async nextStep(tset_style){ |
|||
//tset_style|1=第一页,2=第二页 |
|||
if(tset_style == 2){ |
|||
if(!this.old_password){ |
|||
uni.showToast({ |
|||
title: '请输入原登录密码', |
|||
icon: 'none' |
|||
}) |
|||
return |
|||
} |
|||
//验证旧密码 |
|||
let params = { |
|||
old_password: this.old_password |
|||
} |
|||
//验证旧密码是否正确 |
|||
let res = await apiRoute.common_personnelCheckOldPwd(params) |
|||
if(!res.code){ |
|||
uni.showToast({ |
|||
title: res.msg, |
|||
icon: 'none' |
|||
}) |
|||
return |
|||
} |
|||
this.formData.key_value = res.data.key_value |
|||
} |
|||
this.tset_style = Number(tset_style) |
|||
}, |
|||
//忘记密码 |
|||
forgot() { |
|||
uni.navigateTo({ |
|||
url: '/pages/student/login/forgot' |
|||
}) |
|||
}, |
|||
|
|||
//提交 |
|||
async submit() { |
|||
//验证数据 |
|||
if (!this.formData.new_password) { |
|||
uni.showToast({ |
|||
title: '请输入新密码', |
|||
icon: 'none' |
|||
}) |
|||
return |
|||
} |
|||
|
|||
if (this.formData.new_password.length < 6 || this.formData.new_password.length > 20) { |
|||
uni.showToast({ |
|||
title: '新密码长度为6-20位', |
|||
icon: 'none' |
|||
}) |
|||
return |
|||
} |
|||
|
|||
if (!this.formData.new_password) { |
|||
uni.showToast({ |
|||
title: '请输入新密码', |
|||
icon: 'none' |
|||
}) |
|||
return |
|||
} |
|||
//验证两次密码是否一致 |
|||
if (this.formData.new_password != this.formData.new_password_2) { |
|||
uni.showToast({ |
|||
title: '两次密码不一致', |
|||
icon: 'none' |
|||
}) |
|||
return |
|||
} |
|||
|
|||
let res = await apiRoute.common_personnelEdidPassword(this.formData) |
|||
if(!res.code){ |
|||
uni.showToast({ |
|||
title: res.msg, |
|||
icon: 'none' |
|||
}) |
|||
return |
|||
} |
|||
uni.showToast({ |
|||
title: res.msg, |
|||
icon: 'success' |
|||
}) |
|||
//延迟1s执行 |
|||
setTimeout(() => { |
|||
//跳转页面-个人中心 |
|||
//关闭当前页跳转新页面 |
|||
uni.redirectTo({ |
|||
url: `/pages-market/my/index` |
|||
}) |
|||
}, 1000) |
|||
}, |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style lang="less" scoped> |
|||
page { |
|||
font-weight: normal; |
|||
} |
|||
|
|||
.fui-section__title { |
|||
margin-left: 32rpx; |
|||
} |
|||
|
|||
.fui-left__icon { |
|||
padding-right: 24rpx; |
|||
} |
|||
|
|||
.title { |
|||
display: flex; |
|||
justify-content: space-around; |
|||
align-items: center; |
|||
height: 100rpx; |
|||
width: 100%; |
|||
background-color: #fff; |
|||
font-size: 26rpx; |
|||
border: 4rpx #f5f5f5 solid; |
|||
} |
|||
|
|||
.green-text{ |
|||
color: #36d6b9; |
|||
} |
|||
|
|||
.describe{ |
|||
color: #999999; |
|||
padding-left: 30rpx; |
|||
} |
|||
|
|||
.btn_box{ |
|||
display: flex; |
|||
flex-direction: column; |
|||
gap: 40rpx; |
|||
} |
|||
</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') { |
|||
this.$navigateToPage(`/pages-market/reimbursement/add`, { |
|||
id: item.id |
|||
}); |
|||
} else { |
|||
this.$navigateToPage(`/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> |
|||
@ -0,0 +1,590 @@ |
|||
{ |
|||
"pages": [ |
|||
{ |
|||
"path": "pages/student/login/login", |
|||
"style": { |
|||
"navigationBarTitleText": "", |
|||
"navigationStyle": "default", |
|||
"navigationBarBackgroundColor": "#fff", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "pages/student/home/index", |
|||
"style": { |
|||
"navigationBarTitleText": "首页", |
|||
"navigationStyle": "custom", |
|||
"navigationBarBackgroundColor": "#29d3b4", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "pages/common/home/index", |
|||
"style": { |
|||
"navigationBarTitleText": "首页", |
|||
"navigationStyle": "custom", |
|||
"navigationBarBackgroundColor": "#181A20", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
} |
|||
], |
|||
"subPackages": [ |
|||
{ |
|||
"root": "pages-student", |
|||
"name": "student", |
|||
"pages": [ |
|||
{ |
|||
"path": "profile/index", |
|||
"style": { |
|||
"navigationBarTitleText": "个人信息管理", |
|||
"navigationStyle": "custom", |
|||
"navigationBarBackgroundColor": "#29d3b4", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "physical-test/index", |
|||
"style": { |
|||
"navigationBarTitleText": "体测数据", |
|||
"navigationStyle": "custom", |
|||
"navigationBarBackgroundColor": "#29d3b4", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "schedule/index", |
|||
"style": { |
|||
"navigationBarTitleText": "课程安排", |
|||
"navigationStyle": "custom", |
|||
"navigationBarBackgroundColor": "#29d3b4", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "course-booking/index", |
|||
"style": { |
|||
"navigationBarTitleText": "课程预约", |
|||
"navigationStyle": "custom", |
|||
"navigationBarBackgroundColor": "#29d3b4", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "orders/index", |
|||
"style": { |
|||
"navigationBarTitleText": "订单管理", |
|||
"navigationStyle": "custom", |
|||
"navigationBarBackgroundColor": "#29d3b4", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "contracts/index", |
|||
"style": { |
|||
"navigationBarTitleText": "合同管理", |
|||
"navigationStyle": "custom", |
|||
"navigationBarBackgroundColor": "#29d3b4", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "knowledge/index", |
|||
"style": { |
|||
"navigationBarTitleText": "知识库", |
|||
"navigationStyle": "custom", |
|||
"navigationBarBackgroundColor": "#29d3b4", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "messages/index", |
|||
"style": { |
|||
"navigationBarTitleText": "消息管理", |
|||
"navigationStyle": "custom", |
|||
"navigationBarBackgroundColor": "#29d3b4", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "settings/index", |
|||
"style": { |
|||
"navigationBarTitleText": "系统设置", |
|||
"navigationStyle": "custom", |
|||
"navigationBarBackgroundColor": "#29d3b4", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "child/add", |
|||
"style": { |
|||
"navigationBarTitleText": "添加孩子", |
|||
"navigationStyle": "custom", |
|||
"navigationBarBackgroundColor": "#29d3b4", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "my/set_up", |
|||
"style": { |
|||
"navigationBarTitleText": "设置", |
|||
"navigationStyle": "default", |
|||
"navigationBarBackgroundColor": "#29D3B4", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "my/update_pass", |
|||
"style": { |
|||
"navigationBarTitleText": "修改密码", |
|||
"navigationStyle": "default", |
|||
"navigationBarBackgroundColor": "#fff", |
|||
"navigationBarTextStyle": "black" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "my/personal_data", |
|||
"style": { |
|||
"navigationBarTitleText": "个人资料", |
|||
"navigationStyle": "default", |
|||
"navigationBarBackgroundColor": "#333333", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
} |
|||
] |
|||
}, |
|||
{ |
|||
"root": "pages-market", |
|||
"name": "market", |
|||
"pages": [ |
|||
{ |
|||
"path": "clue/index", |
|||
"style": { |
|||
"navigationBarTitleText": "线索", |
|||
"navigationStyle": "default", |
|||
"navigationBarBackgroundColor": "#292929", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "clue/add_clues", |
|||
"style": { |
|||
"navigationBarTitleText": "添加客户", |
|||
"navigationStyle": "default", |
|||
"navigationBarBackgroundColor": "#fff", |
|||
"navigationBarTextStyle": "black" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "clue/edit_clues", |
|||
"style": { |
|||
"navigationBarTitleText": "编辑客户", |
|||
"navigationStyle": "default", |
|||
"navigationBarBackgroundColor": "#fff", |
|||
"navigationBarTextStyle": "black" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "clue/edit_clues_log", |
|||
"style": { |
|||
"navigationBarTitleText": "修改记录", |
|||
"navigationStyle": "default", |
|||
"navigationBarBackgroundColor": "#fff", |
|||
"navigationBarTextStyle": "black" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "clue/clue_info", |
|||
"style": { |
|||
"navigationBarTitleText": "客户详情", |
|||
"navigationStyle": "default", |
|||
"navigationBarBackgroundColor": "#29d3b4", |
|||
"navigationBarTextStyle": "black" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "clue/class_arrangement", |
|||
"style": { |
|||
"navigationBarTitleText": "课程安排", |
|||
"navigationStyle": "default", |
|||
"navigationBarBackgroundColor": "#232323", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "clue/class_arrangement_detail", |
|||
"style": { |
|||
"navigationBarTitleText": "课程安排详情", |
|||
"navigationStyle": "default", |
|||
"navigationBarBackgroundColor": "#232323", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "clue/clue_table", |
|||
"style": { |
|||
"navigationBarTitleText": "数据统计", |
|||
"navigationBarBackgroundColor": "#292929", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "index/index", |
|||
"style": { |
|||
"navigationBarTitleText": "销售数据" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "my/index", |
|||
"style": { |
|||
"navigationBarTitleText": "我的", |
|||
"navigationStyle": "custom", |
|||
"navigationBarBackgroundColor": "#fff", |
|||
"navigationBarTextStyle": "black" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "my/signed_client_list", |
|||
"style": { |
|||
"navigationBarTitleText": "已签客户", |
|||
"navigationStyle": "default", |
|||
"navigationBarBackgroundColor": "#292929", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "my/info", |
|||
"style": { |
|||
"navigationBarTitleText": "个人资料", |
|||
"navigationStyle": "default", |
|||
"navigationBarBackgroundColor": "#292929", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "my/set_up", |
|||
"style": { |
|||
"navigationBarTitleText": "设置", |
|||
"navigationStyle": "default", |
|||
"navigationBarBackgroundColor": "#292929", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "my/update_pass", |
|||
"style": { |
|||
"navigationBarTitleText": "修改密码", |
|||
"navigationStyle": "default", |
|||
"navigationBarBackgroundColor": "#fff", |
|||
"navigationBarTextStyle": "black" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "my/my_data", |
|||
"style": { |
|||
"navigationBarTitleText": "我的数据", |
|||
"navigationStyle": "custom", |
|||
"navigationBarBackgroundColor": "#29d3b4", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "my/dept_data", |
|||
"style": { |
|||
"navigationBarTitleText": "部门数据", |
|||
"navigationStyle": "custom", |
|||
"navigationBarBackgroundColor": "#29d3b4", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "my/campus_data", |
|||
"style": { |
|||
"navigationBarTitleText": "校区数据", |
|||
"navigationStyle": "custom", |
|||
"navigationBarBackgroundColor": "#29d3b4", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "reimbursement/list", |
|||
"style": { |
|||
"navigationBarTitleText": "报销列表", |
|||
"navigationBarBackgroundColor": "#292929", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "reimbursement/add", |
|||
"style": { |
|||
"navigationBarTitleText": "新增报销", |
|||
"navigationBarBackgroundColor": "#292929", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "reimbursement/detail", |
|||
"style": { |
|||
"navigationBarTitleText": "报销详情", |
|||
"navigationBarBackgroundColor": "#292929", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "data/statistics", |
|||
"style": { |
|||
"navigationBarTitleText": "市场数据统计", |
|||
"navigationBarBackgroundColor": "#292929", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
} |
|||
] |
|||
}, |
|||
{ |
|||
"root": "pages-coach", |
|||
"name": "coach", |
|||
"pages": [ |
|||
{ |
|||
"path": "student/student_list", |
|||
"style": { |
|||
"navigationBarTitleText": "我的学员", |
|||
"navigationBarBackgroundColor": "#29d3b4", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "my/teaching_management", |
|||
"style": { |
|||
"navigationBarTitleText": "教研管理列表", |
|||
"navigationStyle": "default", |
|||
"navigationBarBackgroundColor": "#171717", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "my/gotake_exam", |
|||
"style": { |
|||
"navigationBarTitleText": "考试", |
|||
"navigationStyle": "default", |
|||
"navigationBarBackgroundColor": "#171717", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "my/exam_results", |
|||
"style": { |
|||
"navigationBarTitleText": "考试结果", |
|||
"navigationStyle": "default", |
|||
"navigationBarBackgroundColor": "#fff", |
|||
"navigationBarTextStyle": "black" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "my/salary", |
|||
"style": { |
|||
"navigationBarTitleText": "我的工资", |
|||
"navigationStyle": "default", |
|||
"navigationBarBackgroundColor": "#29d3b4", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "schedule/schedule_table", |
|||
"style": { |
|||
"navigationBarTitleText": "课程安排", |
|||
"navigationBarBackgroundColor": "#292929", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "schedule/add_schedule", |
|||
"style": { |
|||
"navigationBarTitleText": "添加课程安排", |
|||
"navigationBarBackgroundColor": "#292929", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "schedule/adjust_course", |
|||
"style": { |
|||
"navigationBarTitleText": "调整课程安排", |
|||
"navigationBarBackgroundColor": "#292929", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "schedule/sign_in", |
|||
"style": { |
|||
"navigationBarTitleText": "课程点名", |
|||
"navigationStyle": "custom", |
|||
"navigationBarBackgroundColor": "#292929", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
} |
|||
] |
|||
}, |
|||
{ |
|||
"root": "pages-common", |
|||
"name": "common", |
|||
"pages": [ |
|||
{ |
|||
"path": "privacy_agreement", |
|||
"style": { |
|||
"navigationBarTitleText": "隐私协议", |
|||
"navigationStyle": "default", |
|||
"navigationBarBackgroundColor": "#fff", |
|||
"navigationBarTextStyle": "black" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "my_message", |
|||
"style": { |
|||
"navigationBarTitleText": "我的消息", |
|||
"navigationStyle": "default", |
|||
"navigationBarBackgroundColor": "#292929", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "im_chat_info", |
|||
"style": { |
|||
"navigationBarTitleText": "消息", |
|||
"navigationStyle": "default", |
|||
"navigationBarBackgroundColor": "#292929", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "sys_msg_list", |
|||
"style": { |
|||
"navigationBarTitleText": "系统消息", |
|||
"navigationStyle": "default", |
|||
"navigationBarBackgroundColor": "#292929", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "article_info", |
|||
"style": { |
|||
"navigationBarTitleText": "文章详情", |
|||
"navigationStyle": "default", |
|||
"navigationBarBackgroundColor": "#292929", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "feedback", |
|||
"style": { |
|||
"navigationBarTitleText": "意见反馈", |
|||
"navigationStyle": "default", |
|||
"navigationBarBackgroundColor": "#292929", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "my_attendance", |
|||
"style": { |
|||
"navigationBarTitleText": "我的考勤", |
|||
"navigationStyle": "default", |
|||
"navigationBarBackgroundColor": "#292929", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "personnel/add_personnel", |
|||
"style": { |
|||
"navigationBarTitleText": "新员工信息填写", |
|||
"navigationStyle": "custom", |
|||
"navigationBarBackgroundColor": "#fff", |
|||
"navigationBarTextStyle": "black" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "contract/my_contract", |
|||
"style": { |
|||
"navigationBarTitleText": "我的合同", |
|||
"navigationStyle": "default", |
|||
"navigationBarBackgroundColor": "#29d3b4", |
|||
"navigationBarTextStyle": "white", |
|||
"enablePullDownRefresh": true |
|||
} |
|||
}, |
|||
{ |
|||
"path": "contract/contract_detail", |
|||
"style": { |
|||
"navigationBarTitleText": "合同详情", |
|||
"navigationStyle": "default", |
|||
"navigationBarBackgroundColor": "#29d3b4", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "contract/contract_sign", |
|||
"style": { |
|||
"navigationBarTitleText": "电子签名", |
|||
"navigationBarBackgroundColor": "#181A20", |
|||
"navigationBarTextStyle": "white", |
|||
"backgroundColor": "#181A20" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "profile/index", |
|||
"style": { |
|||
"navigationBarTitleText": "我的", |
|||
"navigationStyle": "custom", |
|||
"navigationBarBackgroundColor": "#181A20", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "profile/personal_info", |
|||
"style": { |
|||
"navigationBarTitleText": "个人资料", |
|||
"navigationStyle": "custom", |
|||
"navigationBarBackgroundColor": "#181A20", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
} |
|||
] |
|||
} |
|||
], |
|||
"preloadRule": { |
|||
"pages/student/home/index": { |
|||
"network": "all", |
|||
"packages": ["student"] |
|||
}, |
|||
"pages/common/home/index": { |
|||
"network": "all", |
|||
"packages": ["market", "coach", "common"] |
|||
} |
|||
}, |
|||
"globalStyle": { |
|||
"navigationBarTextStyle": "black", |
|||
"navigationBarTitleText": "智慧教务", |
|||
"navigationBarBackgroundColor": "#F8F8F8", |
|||
"backgroundColor": "#F8F8F8" |
|||
}, |
|||
"tabBar": { |
|||
"color": "#7A7E83", |
|||
"selectedColor": "#3cc51f", |
|||
"borderStyle": "black", |
|||
"backgroundColor": "#ffffff", |
|||
"list": [ |
|||
{ |
|||
"pagePath": "pages/student/home/index", |
|||
"iconPath": "static/icon-img/home.png", |
|||
"selectedIconPath": "static/icon-img/home-active.png", |
|||
"text": "首页" |
|||
}, |
|||
{ |
|||
"pagePath": "pages-student/profile/index", |
|||
"iconPath": "static/icon-img/profile.png", |
|||
"selectedIconPath": "static/icon-img/profile-active.png", |
|||
"text": "我的" |
|||
} |
|||
] |
|||
}, |
|||
"condition": { |
|||
"current": 0, |
|||
"list": [] |
|||
} |
|||
} |
|||
@ -0,0 +1,501 @@ |
|||
<!--添加孩子页面--> |
|||
<template> |
|||
<view class="add_child_container"> |
|||
<!-- 自定义导航栏 --> |
|||
<view class="navbar_section"> |
|||
<view class="navbar_content"> |
|||
<view class="back_button" @click="goBack"> |
|||
<image src="/static/icon-img/back.png" class="back_icon"></image> |
|||
</view> |
|||
<view class="navbar_title">添加孩子</view> |
|||
<view class="navbar_placeholder"></view> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 表单区域 --> |
|||
<view class="form_section"> |
|||
<view class="form_card"> |
|||
<view class="form_title">孩子基本信息</view> |
|||
|
|||
<!-- 头像选择 --> |
|||
<view class="form_item"> |
|||
<view class="item_label">头像</view> |
|||
<view class="avatar_selector" @click="selectAvatar"> |
|||
<image |
|||
:src="formData.headimg || '/static/default-avatar.png'" |
|||
class="avatar_preview" |
|||
mode="aspectFill" |
|||
></image> |
|||
<view class="avatar_text">点击选择头像</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 姓名输入 --> |
|||
<view class="form_item"> |
|||
<view class="item_label required">姓名</view> |
|||
<input |
|||
v-model="formData.name" |
|||
placeholder="请输入孩子姓名" |
|||
class="form_input" |
|||
maxlength="20" |
|||
/> |
|||
</view> |
|||
|
|||
<!-- 性别选择 --> |
|||
<view class="form_item"> |
|||
<view class="item_label required">性别</view> |
|||
<view class="gender_selector"> |
|||
<view |
|||
:class="['gender_option', formData.gender === '1' ? 'active' : '']" |
|||
@click="selectGender('1')" |
|||
> |
|||
<image src="/static/icon-img/male.png" class="gender_icon"></image> |
|||
<text>男孩</text> |
|||
</view> |
|||
<view |
|||
:class="['gender_option', formData.gender === '2' ? 'active' : '']" |
|||
@click="selectGender('2')" |
|||
> |
|||
<image src="/static/icon-img/female.png" class="gender_icon"></image> |
|||
<text>女孩</text> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 出生日期 --> |
|||
<view class="form_item"> |
|||
<view class="item_label required">出生日期</view> |
|||
<picker |
|||
mode="date" |
|||
:value="formData.birthday" |
|||
@change="onBirthdayChange" |
|||
class="date_picker" |
|||
> |
|||
<view class="picker_display"> |
|||
{{ formData.birthday || '请选择出生日期' }} |
|||
</view> |
|||
</picker> |
|||
</view> |
|||
|
|||
<!-- 紧急联系人 --> |
|||
<view class="form_item"> |
|||
<view class="item_label">紧急联系人</view> |
|||
<input |
|||
v-model="formData.emergency_contact" |
|||
placeholder="请输入紧急联系人姓名" |
|||
class="form_input" |
|||
maxlength="20" |
|||
/> |
|||
</view> |
|||
|
|||
<!-- 联系电话 --> |
|||
<view class="form_item"> |
|||
<view class="item_label">联系电话</view> |
|||
<input |
|||
v-model="formData.contact_phone" |
|||
placeholder="请输入联系电话" |
|||
class="form_input" |
|||
type="number" |
|||
maxlength="11" |
|||
/> |
|||
</view> |
|||
|
|||
<!-- 备注信息 --> |
|||
<view class="form_item"> |
|||
<view class="item_label">备注</view> |
|||
<textarea |
|||
v-model="formData.note" |
|||
placeholder="请输入备注信息(选填)" |
|||
class="form_textarea" |
|||
maxlength="500" |
|||
></textarea> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 提交按钮 --> |
|||
<view class="submit_section"> |
|||
<button |
|||
class="submit_button" |
|||
@click="submitForm" |
|||
:disabled="submitting" |
|||
> |
|||
{{ submitting ? '提交中...' : '添加孩子' }} |
|||
</button> |
|||
</view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
import apiRoute from '@/api/member.js' |
|||
|
|||
export default { |
|||
data() { |
|||
return { |
|||
formData: { |
|||
name: '', |
|||
gender: '1', // '1':男 '2':女 |
|||
birthday: '', |
|||
headimg: '', |
|||
emergency_contact: '', |
|||
contact_phone: '', |
|||
note: '' |
|||
}, |
|||
submitting: false, |
|||
uploadingAvatar: false |
|||
} |
|||
}, |
|||
methods: { |
|||
goBack() { |
|||
uni.navigateBack() |
|||
}, |
|||
|
|||
selectAvatar() { |
|||
uni.chooseImage({ |
|||
count: 1, |
|||
sizeType: ['original', 'compressed'], |
|||
sourceType: ['album', 'camera'], |
|||
success: (res) => { |
|||
const tempFilePath = res.tempFilePaths[0] |
|||
this.formData.headimg = tempFilePath |
|||
|
|||
// 这里可以上传图片到服务器 |
|||
this.uploadAvatar(tempFilePath) |
|||
}, |
|||
fail: (err) => { |
|||
console.error('选择图片失败:', err) |
|||
} |
|||
}) |
|||
}, |
|||
|
|||
async uploadAvatar(filePath) { |
|||
this.uploadingAvatar = true |
|||
uni.showLoading({ |
|||
title: '上传中...' |
|||
}) |
|||
|
|||
try { |
|||
// 调用头像上传API(暂时不需要student_id,因为是新增孩子) |
|||
const response = await apiRoute.uploadAvatarForAdd(filePath) |
|||
console.log('头像上传响应:', response) |
|||
|
|||
if (response.code === 1) { |
|||
// 更新表单中的头像字段 |
|||
this.formData.headimg = response.data.url |
|||
uni.showToast({ |
|||
title: '头像上传成功', |
|||
icon: 'success' |
|||
}) |
|||
} else { |
|||
throw new Error(response.msg || '上传失败') |
|||
} |
|||
} catch (error) { |
|||
console.error('头像上传失败:', error) |
|||
uni.showToast({ |
|||
title: error.message || '头像上传失败', |
|||
icon: 'none' |
|||
}) |
|||
// 恢复本地预览图片 |
|||
this.formData.headimg = filePath |
|||
} finally { |
|||
uni.hideLoading() |
|||
this.uploadingAvatar = false |
|||
} |
|||
}, |
|||
|
|||
selectGender(gender) { |
|||
this.formData.gender = gender |
|||
}, |
|||
|
|||
onBirthdayChange(e) { |
|||
this.formData.birthday = e.detail.value |
|||
}, |
|||
|
|||
validateForm() { |
|||
if (!this.formData.name.trim()) { |
|||
uni.showToast({ |
|||
title: '请输入孩子姓名', |
|||
icon: 'none' |
|||
}) |
|||
return false |
|||
} |
|||
|
|||
if (!this.formData.gender) { |
|||
uni.showToast({ |
|||
title: '请选择性别', |
|||
icon: 'none' |
|||
}) |
|||
return false |
|||
} |
|||
|
|||
if (!this.formData.birthday) { |
|||
uni.showToast({ |
|||
title: '请选择出生日期', |
|||
icon: 'none' |
|||
}) |
|||
return false |
|||
} |
|||
|
|||
// 手机号验证 |
|||
if (this.formData.contact_phone && !/^1[3-9]\d{9}$/.test(this.formData.contact_phone)) { |
|||
uni.showToast({ |
|||
title: '请输入正确的手机号', |
|||
icon: 'none' |
|||
}) |
|||
return false |
|||
} |
|||
|
|||
return true |
|||
}, |
|||
|
|||
async submitForm() { |
|||
if (!this.validateForm()) { |
|||
return |
|||
} |
|||
|
|||
this.submitting = true |
|||
|
|||
try { |
|||
console.log('提交表单数据:', this.formData) |
|||
|
|||
// 调用真实API |
|||
const response = await apiRoute.addChild(this.formData) |
|||
console.log('添加孩子API响应:', response) |
|||
|
|||
if (response.code === 1) { |
|||
uni.showToast({ |
|||
title: '添加成功', |
|||
icon: 'success' |
|||
}) |
|||
|
|||
// 延迟跳转,让用户看到成功提示 |
|||
setTimeout(() => { |
|||
uni.navigateBack() |
|||
}, 1500) |
|||
} else { |
|||
uni.showToast({ |
|||
title: response.msg || '添加失败', |
|||
icon: 'none' |
|||
}) |
|||
} |
|||
|
|||
} catch (error) { |
|||
console.error('添加孩子失败:', error) |
|||
uni.showToast({ |
|||
title: error.message || '添加失败', |
|||
icon: 'none' |
|||
}) |
|||
} finally { |
|||
this.submitting = false |
|||
} |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style lang="less" scoped> |
|||
.add_child_container { |
|||
background: #f8f9fa; |
|||
min-height: 100vh; |
|||
} |
|||
|
|||
// 自定义导航栏 |
|||
.navbar_section { |
|||
background: linear-gradient(135deg, #29D3B4 0%, #1BA297 100%); |
|||
padding: 40rpx 32rpx 32rpx; |
|||
|
|||
// 小程序端适配状态栏 |
|||
// #ifdef MP-WEIXIN |
|||
padding-top: 80rpx; |
|||
// #endif |
|||
|
|||
.navbar_content { |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: space-between; |
|||
|
|||
.back_button { |
|||
width: 40rpx; |
|||
height: 40rpx; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
|
|||
.back_icon { |
|||
width: 24rpx; |
|||
height: 24rpx; |
|||
} |
|||
} |
|||
|
|||
.navbar_title { |
|||
color: #fff; |
|||
font-size: 36rpx; |
|||
font-weight: 600; |
|||
} |
|||
|
|||
.navbar_placeholder { |
|||
width: 40rpx; |
|||
} |
|||
} |
|||
} |
|||
|
|||
// 表单区域 |
|||
.form_section { |
|||
padding: 32rpx 20rpx; |
|||
|
|||
.form_card { |
|||
background: #fff; |
|||
border-radius: 16rpx; |
|||
padding: 32rpx; |
|||
|
|||
.form_title { |
|||
font-size: 32rpx; |
|||
font-weight: 600; |
|||
color: #333; |
|||
margin-bottom: 32rpx; |
|||
text-align: center; |
|||
} |
|||
|
|||
.form_item { |
|||
margin-bottom: 32rpx; |
|||
|
|||
.item_label { |
|||
font-size: 28rpx; |
|||
color: #333; |
|||
margin-bottom: 16rpx; |
|||
font-weight: 500; |
|||
|
|||
&.required::after { |
|||
content: '*'; |
|||
color: #ff4757; |
|||
margin-left: 4rpx; |
|||
} |
|||
} |
|||
|
|||
.form_input { |
|||
width: 100%; |
|||
padding: 24rpx; |
|||
background: #f8f9fa; |
|||
border-radius: 12rpx; |
|||
border: 1px solid #e9ecef; |
|||
font-size: 28rpx; |
|||
color: #333; |
|||
|
|||
&:focus { |
|||
border-color: #29d3b4; |
|||
} |
|||
} |
|||
|
|||
.form_textarea { |
|||
width: 100%; |
|||
min-height: 120rpx; |
|||
padding: 24rpx; |
|||
background: #f8f9fa; |
|||
border-radius: 12rpx; |
|||
border: 1px solid #e9ecef; |
|||
font-size: 28rpx; |
|||
color: #333; |
|||
resize: none; |
|||
} |
|||
|
|||
// 头像选择器 |
|||
.avatar_selector { |
|||
display: flex; |
|||
align-items: center; |
|||
gap: 24rpx; |
|||
padding: 24rpx; |
|||
background: #f8f9fa; |
|||
border-radius: 12rpx; |
|||
border: 1px solid #e9ecef; |
|||
|
|||
.avatar_preview { |
|||
width: 100rpx; |
|||
height: 100rpx; |
|||
border-radius: 50%; |
|||
border: 2px solid #29d3b4; |
|||
} |
|||
|
|||
.avatar_text { |
|||
font-size: 28rpx; |
|||
color: #666; |
|||
} |
|||
} |
|||
|
|||
// 性别选择器 |
|||
.gender_selector { |
|||
display: flex; |
|||
gap: 32rpx; |
|||
|
|||
.gender_option { |
|||
flex: 1; |
|||
display: flex; |
|||
flex-direction: column; |
|||
align-items: center; |
|||
gap: 12rpx; |
|||
padding: 32rpx 20rpx; |
|||
background: #f8f9fa; |
|||
border-radius: 12rpx; |
|||
border: 2px solid #e9ecef; |
|||
|
|||
&.active { |
|||
border-color: #29d3b4; |
|||
background: rgba(41, 211, 180, 0.1); |
|||
|
|||
text { |
|||
color: #29d3b4; |
|||
font-weight: 600; |
|||
} |
|||
} |
|||
|
|||
.gender_icon { |
|||
width: 48rpx; |
|||
height: 48rpx; |
|||
} |
|||
|
|||
text { |
|||
font-size: 26rpx; |
|||
color: #666; |
|||
} |
|||
} |
|||
} |
|||
|
|||
// 日期选择器 |
|||
.date_picker { |
|||
.picker_display { |
|||
width: 100%; |
|||
padding: 24rpx; |
|||
background: #f8f9fa; |
|||
border-radius: 12rpx; |
|||
border: 1px solid #e9ecef; |
|||
font-size: 28rpx; |
|||
color: #333; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
// 提交按钮 |
|||
.submit_section { |
|||
padding: 32rpx 20rpx 60rpx; |
|||
|
|||
.submit_button { |
|||
width: 100%; |
|||
background: linear-gradient(135deg, #29D3B4 0%, #1BA297 100%); |
|||
color: #fff; |
|||
border: none; |
|||
border-radius: 16rpx; |
|||
padding: 28rpx 0; |
|||
font-size: 32rpx; |
|||
font-weight: 600; |
|||
|
|||
&:disabled { |
|||
opacity: 0.6; |
|||
} |
|||
|
|||
&:active:not(:disabled) { |
|||
opacity: 0.8; |
|||
} |
|||
} |
|||
} |
|||
</style> |
|||
@ -0,0 +1,988 @@ |
|||
<!--学员合同管理页面--> |
|||
<template> |
|||
<view class="main_box"> |
|||
<!-- 自定义导航栏 --> |
|||
<view class="navbar_section"> |
|||
<view class="navbar_back" @click="goBack"> |
|||
<text class="back_icon">‹</text> |
|||
</view> |
|||
<view class="navbar_title">合同管理</view> |
|||
<view class="navbar_action"></view> |
|||
</view> |
|||
|
|||
<!-- 学员信息 --> |
|||
<view class="student_info_section" v-if="studentInfo"> |
|||
<view class="student_name">{{ studentInfo.name }}</view> |
|||
<view class="contract_stats"> |
|||
<text class="stat_item">有效合同:{{ contractStats.active_contracts }}个</text> |
|||
<text class="stat_item">剩余课时:{{ contractStats.remaining_hours }}节</text> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 合同状态筛选 --> |
|||
<view class="filter_section"> |
|||
<view class="filter_tabs"> |
|||
<view |
|||
v-for="tab in statusTabs" |
|||
:key="tab.value" |
|||
:class="['filter_tab', activeStatus === tab.value ? 'active' : '']" |
|||
@click="changeStatus(tab.value)" |
|||
> |
|||
{{ tab.text }} |
|||
<view class="tab_badge" v-if="tab.count > 0">{{ tab.count }}</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 合同列表 --> |
|||
<view class="contracts_section"> |
|||
<view v-if="loading" class="loading_section"> |
|||
<view class="loading_text">加载中...</view> |
|||
</view> |
|||
|
|||
<view v-else-if="filteredContracts.length === 0" class="empty_section"> |
|||
<view class="empty_icon">📋</view> |
|||
<view class="empty_text">暂无合同</view> |
|||
<view class="empty_hint">签署合同后会在这里显示</view> |
|||
</view> |
|||
|
|||
<view v-else class="contracts_list"> |
|||
<view |
|||
v-for="contract in filteredContracts" |
|||
:key="contract.id" |
|||
class="contract_item" |
|||
@click="viewContractDetail(contract)" |
|||
> |
|||
<view class="contract_header"> |
|||
<view class="contract_info"> |
|||
<view class="contract_name">{{ contract.contract_name }}</view> |
|||
<view class="contract_number">合同编号:{{ contract.contract_no }}</view> |
|||
</view> |
|||
<view class="contract_status" :class="contract.status"> |
|||
{{ getStatusText(contract.status) }} |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="contract_content"> |
|||
<view class="contract_details"> |
|||
<view class="detail_row"> |
|||
<text class="detail_label">课程类型:</text> |
|||
<text class="detail_value">{{ contract.course_type }}</text> |
|||
</view> |
|||
<view class="detail_row"> |
|||
<text class="detail_label">总课时:</text> |
|||
<text class="detail_value">{{ contract.total_hours }}节</text> |
|||
</view> |
|||
<view class="detail_row"> |
|||
<text class="detail_label">剩余课时:</text> |
|||
<text class="detail_value remaining">{{ contract.remaining_hours }}节</text> |
|||
</view> |
|||
<view class="detail_row"> |
|||
<text class="detail_label">合同金额:</text> |
|||
<text class="detail_value amount">¥{{ contract.total_amount }}</text> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="contract_dates"> |
|||
<view class="date_row"> |
|||
<text class="date_label">签署日期:</text> |
|||
<text class="date_value">{{ formatDate(contract.sign_date) }}</text> |
|||
</view> |
|||
<view class="date_row"> |
|||
<text class="date_label">生效日期:</text> |
|||
<text class="date_value">{{ formatDate(contract.start_date) }}</text> |
|||
</view> |
|||
<view class="date_row"> |
|||
<text class="date_label">到期日期:</text> |
|||
<text class="date_value">{{ formatDate(contract.end_date) }}</text> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="contract_progress" v-if="contract.status === 'active'"> |
|||
<view class="progress_info"> |
|||
<text class="progress_text">课时使用进度</text> |
|||
<text class="progress_percent">{{ getProgressPercent(contract) }}%</text> |
|||
</view> |
|||
<view class="progress_bar"> |
|||
<view class="progress_fill" :style="{ width: getProgressPercent(contract) + '%' }"></view> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="contract_actions"> |
|||
<fui-button |
|||
v-if="contract.status === 'active'" |
|||
background="transparent" |
|||
color="#29d3b4" |
|||
size="small" |
|||
@click.stop="viewContractDetail(contract)" |
|||
> |
|||
查看详情 |
|||
</fui-button> |
|||
|
|||
<fui-button |
|||
v-if="contract.status === 'active' && contract.can_renew" |
|||
background="#29d3b4" |
|||
size="small" |
|||
@click.stop="renewContract(contract)" |
|||
> |
|||
续约 |
|||
</fui-button> |
|||
|
|||
<fui-button |
|||
v-if="contract.status === 'pending'" |
|||
background="#f39c12" |
|||
size="small" |
|||
@click.stop="signContract(contract)" |
|||
> |
|||
签署合同 |
|||
</fui-button> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 加载更多 --> |
|||
<view class="load_more_section" v-if="!loading && hasMore"> |
|||
<fui-button |
|||
background="transparent" |
|||
color="#666" |
|||
@click="loadMoreContracts" |
|||
:loading="loadingMore" |
|||
> |
|||
{{ loadingMore ? '加载中...' : '加载更多' }} |
|||
</fui-button> |
|||
</view> |
|||
|
|||
<!-- 合同详情弹窗 --> |
|||
<view class="contract_popup" v-if="showContractPopup" @click="closeContractPopup"> |
|||
<view class="popup_content" @click.stop> |
|||
<view class="popup_header"> |
|||
<view class="popup_title">合同详情</view> |
|||
<view class="popup_close" @click="closeContractPopup">×</view> |
|||
</view> |
|||
|
|||
<view class="popup_contract_detail" v-if="selectedContract"> |
|||
<view class="detail_section"> |
|||
<view class="section_title">基本信息</view> |
|||
<view class="info_grid"> |
|||
<view class="info_row"> |
|||
<text class="info_label">合同名称:</text> |
|||
<text class="info_value">{{ selectedContract.contract_name }}</text> |
|||
</view> |
|||
<view class="info_row"> |
|||
<text class="info_label">合同编号:</text> |
|||
<text class="info_value">{{ selectedContract.contract_no }}</text> |
|||
</view> |
|||
<view class="info_row"> |
|||
<text class="info_label">课程类型:</text> |
|||
<text class="info_value">{{ selectedContract.course_type }}</text> |
|||
</view> |
|||
<view class="info_row"> |
|||
<text class="info_label">总课时:</text> |
|||
<text class="info_value">{{ selectedContract.total_hours }}节</text> |
|||
</view> |
|||
<view class="info_row"> |
|||
<text class="info_label">剩余课时:</text> |
|||
<text class="info_value">{{ selectedContract.remaining_hours }}节</text> |
|||
</view> |
|||
<view class="info_row"> |
|||
<text class="info_label">合同金额:</text> |
|||
<text class="info_value">¥{{ selectedContract.total_amount }}</text> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="detail_section"> |
|||
<view class="section_title">时间信息</view> |
|||
<view class="info_grid"> |
|||
<view class="info_row"> |
|||
<text class="info_label">签署日期:</text> |
|||
<text class="info_value">{{ formatFullDate(selectedContract.sign_date) }}</text> |
|||
</view> |
|||
<view class="info_row"> |
|||
<text class="info_label">生效日期:</text> |
|||
<text class="info_value">{{ formatFullDate(selectedContract.start_date) }}</text> |
|||
</view> |
|||
<view class="info_row"> |
|||
<text class="info_label">到期日期:</text> |
|||
<text class="info_value">{{ formatFullDate(selectedContract.end_date) }}</text> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="detail_section" v-if="selectedContract.terms"> |
|||
<view class="section_title">合同条款</view> |
|||
<view class="contract_terms">{{ selectedContract.terms }}</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="popup_actions"> |
|||
<fui-button |
|||
v-if="selectedContract && selectedContract.contract_file_url" |
|||
background="#3498db" |
|||
@click="downloadContract" |
|||
> |
|||
下载合同 |
|||
</fui-button> |
|||
|
|||
<fui-button |
|||
background="#f8f9fa" |
|||
color="#666" |
|||
@click="closeContractPopup" |
|||
> |
|||
关闭 |
|||
</fui-button> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
import apiRoute from '@/api/apiRoute.js' |
|||
|
|||
export default { |
|||
data() { |
|||
return { |
|||
studentId: 0, |
|||
studentInfo: {}, |
|||
contractsList: [], |
|||
filteredContracts: [], |
|||
contractStats: {}, |
|||
loading: false, |
|||
loadingMore: false, |
|||
hasMore: true, |
|||
currentPage: 1, |
|||
activeStatus: 'all', |
|||
showContractPopup: false, |
|||
selectedContract: null, |
|||
statusTabs: [ |
|||
{ value: 'all', text: '全部', count: 0 }, |
|||
{ value: 'active', text: '生效中', count: 0 }, |
|||
{ value: 'pending', text: '待签署', count: 0 }, |
|||
{ value: 'expired', text: '已到期', count: 0 }, |
|||
{ value: 'terminated', text: '已终止', count: 0 } |
|||
] |
|||
} |
|||
}, |
|||
|
|||
onLoad(options) { |
|||
this.studentId = parseInt(options.student_id) || 0 |
|||
if (this.studentId) { |
|||
this.initPage() |
|||
} else { |
|||
uni.showToast({ |
|||
title: '参数错误', |
|||
icon: 'none' |
|||
}) |
|||
} |
|||
}, |
|||
|
|||
methods: { |
|||
goBack() { |
|||
uni.navigateBack() |
|||
}, |
|||
|
|||
async initPage() { |
|||
await this.loadStudentInfo() |
|||
await this.loadContracts() |
|||
this.updateStatusCounts() |
|||
}, |
|||
|
|||
async loadStudentInfo() { |
|||
try { |
|||
// 模拟获取学员信息 |
|||
const mockStudentInfo = { |
|||
id: this.studentId, |
|||
name: '小明' |
|||
} |
|||
this.studentInfo = mockStudentInfo |
|||
} catch (error) { |
|||
console.error('获取学员信息失败:', error) |
|||
} |
|||
}, |
|||
|
|||
async loadContracts() { |
|||
this.loading = true |
|||
try { |
|||
console.log('加载合同列表:', this.studentId) |
|||
|
|||
// 模拟API调用 |
|||
// const response = await apiRoute.getStudentContracts({ |
|||
// student_id: this.studentId, |
|||
// page: this.currentPage, |
|||
// limit: 10 |
|||
// }) |
|||
|
|||
// 使用模拟数据 |
|||
const mockResponse = { |
|||
code: 1, |
|||
data: { |
|||
list: [ |
|||
{ |
|||
id: 1, |
|||
contract_no: 'HT202401150001', |
|||
contract_name: '少儿体适能训练合同', |
|||
course_type: '少儿体适能', |
|||
total_hours: 48, |
|||
remaining_hours: 32, |
|||
used_hours: 16, |
|||
total_amount: '4800.00', |
|||
status: 'active', |
|||
sign_date: '2024-01-15', |
|||
start_date: '2024-01-20', |
|||
end_date: '2024-07-20', |
|||
can_renew: true, |
|||
contract_file_url: '/uploads/contracts/contract_001.pdf', |
|||
terms: '1. 本合同自签署之日起生效\n2. 学员应按时参加课程\n3. 如需请假,请提前24小时通知\n4. 课程有效期为6个月\n5. 未使用完的课时可申请延期' |
|||
}, |
|||
{ |
|||
id: 2, |
|||
contract_no: 'HT202312100002', |
|||
contract_name: '基础体能训练合同', |
|||
course_type: '基础体能', |
|||
total_hours: 24, |
|||
remaining_hours: 0, |
|||
used_hours: 24, |
|||
total_amount: '2400.00', |
|||
status: 'expired', |
|||
sign_date: '2023-12-10', |
|||
start_date: '2023-12-15', |
|||
end_date: '2024-01-15', |
|||
can_renew: false, |
|||
contract_file_url: '/uploads/contracts/contract_002.pdf', |
|||
terms: '已到期的合同条款...' |
|||
}, |
|||
{ |
|||
id: 3, |
|||
contract_no: 'HT202401200003', |
|||
contract_name: '专项技能训练合同', |
|||
course_type: '专项技能', |
|||
total_hours: 36, |
|||
remaining_hours: 36, |
|||
used_hours: 0, |
|||
total_amount: '3600.00', |
|||
status: 'pending', |
|||
sign_date: null, |
|||
start_date: '2024-02-01', |
|||
end_date: '2024-08-01', |
|||
can_renew: false, |
|||
contract_file_url: '/uploads/contracts/contract_003.pdf', |
|||
terms: '待签署的合同条款...' |
|||
} |
|||
], |
|||
total: 3, |
|||
has_more: false, |
|||
stats: { |
|||
active_contracts: 1, |
|||
remaining_hours: 32, |
|||
total_amount: '4800.00' |
|||
} |
|||
} |
|||
} |
|||
|
|||
if (mockResponse.code === 1) { |
|||
const newList = mockResponse.data.list || [] |
|||
if (this.currentPage === 1) { |
|||
this.contractsList = newList |
|||
} else { |
|||
this.contractsList = [...this.contractsList, ...newList] |
|||
} |
|||
|
|||
this.hasMore = mockResponse.data.has_more || false |
|||
this.contractStats = mockResponse.data.stats || {} |
|||
this.applyStatusFilter() |
|||
console.log('合同数据加载成功:', this.contractsList) |
|||
} else { |
|||
uni.showToast({ |
|||
title: mockResponse.msg || '获取合同列表失败', |
|||
icon: 'none' |
|||
}) |
|||
} |
|||
} catch (error) { |
|||
console.error('获取合同列表失败:', error) |
|||
uni.showToast({ |
|||
title: '获取合同列表失败', |
|||
icon: 'none' |
|||
}) |
|||
} finally { |
|||
this.loading = false |
|||
this.loadingMore = false |
|||
} |
|||
}, |
|||
|
|||
async loadMoreContracts() { |
|||
if (this.loadingMore || !this.hasMore) return |
|||
|
|||
this.loadingMore = true |
|||
this.currentPage++ |
|||
await this.loadContracts() |
|||
}, |
|||
|
|||
changeStatus(status) { |
|||
this.activeStatus = status |
|||
this.applyStatusFilter() |
|||
}, |
|||
|
|||
applyStatusFilter() { |
|||
if (this.activeStatus === 'all') { |
|||
this.filteredContracts = [...this.contractsList] |
|||
} else { |
|||
this.filteredContracts = this.contractsList.filter(contract => contract.status === this.activeStatus) |
|||
} |
|||
}, |
|||
|
|||
updateStatusCounts() { |
|||
const counts = {} |
|||
this.contractsList.forEach(contract => { |
|||
counts[contract.status] = (counts[contract.status] || 0) + 1 |
|||
}) |
|||
|
|||
this.statusTabs.forEach(tab => { |
|||
if (tab.value === 'all') { |
|||
tab.count = this.contractsList.length |
|||
} else { |
|||
tab.count = counts[tab.value] || 0 |
|||
} |
|||
}) |
|||
}, |
|||
|
|||
getStatusText(status) { |
|||
const statusMap = { |
|||
'active': '生效中', |
|||
'pending': '待签署', |
|||
'expired': '已到期', |
|||
'terminated': '已终止' |
|||
} |
|||
return statusMap[status] || status |
|||
}, |
|||
|
|||
getProgressPercent(contract) { |
|||
if (contract.total_hours === 0) return 0 |
|||
return Math.round((contract.used_hours / contract.total_hours) * 100) |
|||
}, |
|||
|
|||
formatDate(dateString) { |
|||
if (!dateString) return '未设置' |
|||
const date = new Date(dateString) |
|||
const month = String(date.getMonth() + 1).padStart(2, '0') |
|||
const day = String(date.getDate()).padStart(2, '0') |
|||
return `${month}-${day}` |
|||
}, |
|||
|
|||
formatFullDate(dateString) { |
|||
if (!dateString) return '未设置' |
|||
const date = new Date(dateString) |
|||
const year = date.getFullYear() |
|||
const month = String(date.getMonth() + 1).padStart(2, '0') |
|||
const day = String(date.getDate()).padStart(2, '0') |
|||
return `${year}-${month}-${day}` |
|||
}, |
|||
|
|||
viewContractDetail(contract) { |
|||
this.selectedContract = contract |
|||
this.showContractPopup = true |
|||
}, |
|||
|
|||
closeContractPopup() { |
|||
this.showContractPopup = false |
|||
this.selectedContract = null |
|||
}, |
|||
|
|||
async renewContract(contract) { |
|||
uni.showModal({ |
|||
title: '确认续约', |
|||
content: '确定要续约此合同吗?', |
|||
success: async (res) => { |
|||
if (res.confirm) { |
|||
try { |
|||
console.log('续约合同:', contract.id) |
|||
|
|||
// 模拟API调用 |
|||
await new Promise(resolve => setTimeout(resolve, 1000)) |
|||
const mockResponse = { code: 1, message: '续约申请已提交' } |
|||
|
|||
if (mockResponse.code === 1) { |
|||
uni.showToast({ |
|||
title: '续约申请已提交', |
|||
icon: 'success' |
|||
}) |
|||
} else { |
|||
uni.showToast({ |
|||
title: mockResponse.message || '续约申请失败', |
|||
icon: 'none' |
|||
}) |
|||
} |
|||
} catch (error) { |
|||
console.error('续约申请失败:', error) |
|||
uni.showToast({ |
|||
title: '续约申请失败', |
|||
icon: 'none' |
|||
}) |
|||
} |
|||
} |
|||
} |
|||
}) |
|||
}, |
|||
|
|||
async signContract(contract) { |
|||
uni.showModal({ |
|||
title: '确认签署', |
|||
content: '确定要签署此合同吗?', |
|||
success: async (res) => { |
|||
if (res.confirm) { |
|||
try { |
|||
console.log('签署合同:', contract.id) |
|||
|
|||
// 模拟API调用 |
|||
await new Promise(resolve => setTimeout(resolve, 1500)) |
|||
const mockResponse = { code: 1, message: '合同签署成功' } |
|||
|
|||
if (mockResponse.code === 1) { |
|||
uni.showToast({ |
|||
title: '合同签署成功', |
|||
icon: 'success' |
|||
}) |
|||
|
|||
// 更新合同状态 |
|||
const contractIndex = this.contractsList.findIndex(c => c.id === contract.id) |
|||
if (contractIndex !== -1) { |
|||
this.contractsList[contractIndex].status = 'active' |
|||
this.contractsList[contractIndex].sign_date = new Date().toISOString().split('T')[0] |
|||
} |
|||
|
|||
this.applyStatusFilter() |
|||
this.updateStatusCounts() |
|||
} else { |
|||
uni.showToast({ |
|||
title: mockResponse.message || '合同签署失败', |
|||
icon: 'none' |
|||
}) |
|||
} |
|||
} catch (error) { |
|||
console.error('合同签署失败:', error) |
|||
uni.showToast({ |
|||
title: '合同签署失败', |
|||
icon: 'none' |
|||
}) |
|||
} |
|||
} |
|||
} |
|||
}) |
|||
}, |
|||
|
|||
downloadContract() { |
|||
if (!this.selectedContract || !this.selectedContract.contract_file_url) { |
|||
uni.showToast({ |
|||
title: '合同文件不存在', |
|||
icon: 'none' |
|||
}) |
|||
return |
|||
} |
|||
|
|||
uni.showModal({ |
|||
title: '提示', |
|||
content: '合同下载功能开发中', |
|||
showCancel: false |
|||
}) |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style lang="less" scoped> |
|||
.main_box { |
|||
background: #f8f9fa; |
|||
min-height: 100vh; |
|||
} |
|||
|
|||
// 自定义导航栏 |
|||
.navbar_section { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
background: #29D3B4; |
|||
padding: 40rpx 32rpx 20rpx; |
|||
|
|||
// 小程序端适配状态栏 |
|||
// #ifdef MP-WEIXIN |
|||
padding-top: 80rpx; |
|||
// #endif |
|||
|
|||
.navbar_back { |
|||
width: 60rpx; |
|||
|
|||
.back_icon { |
|||
color: #fff; |
|||
font-size: 40rpx; |
|||
font-weight: 600; |
|||
} |
|||
} |
|||
|
|||
.navbar_title { |
|||
color: #fff; |
|||
font-size: 32rpx; |
|||
font-weight: 600; |
|||
} |
|||
|
|||
.navbar_action { |
|||
width: 60rpx; |
|||
} |
|||
} |
|||
|
|||
// 学员信息 |
|||
.student_info_section { |
|||
background: #fff; |
|||
padding: 24rpx 32rpx; |
|||
border-bottom: 1px solid #f0f0f0; |
|||
|
|||
.student_name { |
|||
font-size: 32rpx; |
|||
font-weight: 600; |
|||
color: #333; |
|||
margin-bottom: 12rpx; |
|||
} |
|||
|
|||
.contract_stats { |
|||
display: flex; |
|||
gap: 24rpx; |
|||
|
|||
.stat_item { |
|||
font-size: 24rpx; |
|||
color: #666; |
|||
} |
|||
} |
|||
} |
|||
|
|||
// 状态筛选 |
|||
.filter_section { |
|||
background: #fff; |
|||
margin: 20rpx; |
|||
border-radius: 16rpx; |
|||
padding: 24rpx 32rpx; |
|||
|
|||
.filter_tabs { |
|||
display: flex; |
|||
flex-wrap: wrap; |
|||
gap: 12rpx; |
|||
|
|||
.filter_tab { |
|||
position: relative; |
|||
padding: 10rpx 20rpx; |
|||
font-size: 24rpx; |
|||
color: #666; |
|||
background: #f8f9fa; |
|||
border-radius: 16rpx; |
|||
|
|||
&.active { |
|||
color: #fff; |
|||
background: #29D3B4; |
|||
} |
|||
|
|||
.tab_badge { |
|||
position: absolute; |
|||
top: -6rpx; |
|||
right: -6rpx; |
|||
background: #ff4757; |
|||
color: #fff; |
|||
font-size: 18rpx; |
|||
padding: 2rpx 6rpx; |
|||
border-radius: 10rpx; |
|||
min-width: 16rpx; |
|||
text-align: center; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
// 合同列表 |
|||
.contracts_section { |
|||
margin: 0 20rpx; |
|||
|
|||
.loading_section, .empty_section { |
|||
background: #fff; |
|||
border-radius: 16rpx; |
|||
padding: 80rpx 32rpx; |
|||
text-align: center; |
|||
|
|||
.loading_text, .empty_text { |
|||
font-size: 28rpx; |
|||
color: #666; |
|||
margin-bottom: 12rpx; |
|||
} |
|||
|
|||
.empty_icon { |
|||
font-size: 80rpx; |
|||
margin-bottom: 24rpx; |
|||
} |
|||
|
|||
.empty_hint { |
|||
font-size: 24rpx; |
|||
color: #999; |
|||
} |
|||
} |
|||
|
|||
.contracts_list { |
|||
.contract_item { |
|||
background: #fff; |
|||
border-radius: 16rpx; |
|||
padding: 24rpx; |
|||
margin-bottom: 20rpx; |
|||
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.08); |
|||
|
|||
.contract_header { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: flex-start; |
|||
margin-bottom: 20rpx; |
|||
|
|||
.contract_info { |
|||
flex: 1; |
|||
|
|||
.contract_name { |
|||
font-size: 28rpx; |
|||
font-weight: 600; |
|||
color: #333; |
|||
margin-bottom: 8rpx; |
|||
} |
|||
|
|||
.contract_number { |
|||
font-size: 24rpx; |
|||
color: #666; |
|||
} |
|||
} |
|||
|
|||
.contract_status { |
|||
font-size: 22rpx; |
|||
padding: 6rpx 12rpx; |
|||
border-radius: 12rpx; |
|||
|
|||
&.active { |
|||
color: #27ae60; |
|||
background: rgba(39, 174, 96, 0.1); |
|||
} |
|||
|
|||
&.pending { |
|||
color: #f39c12; |
|||
background: rgba(243, 156, 18, 0.1); |
|||
} |
|||
|
|||
&.expired { |
|||
color: #e74c3c; |
|||
background: rgba(231, 76, 60, 0.1); |
|||
} |
|||
|
|||
&.terminated { |
|||
color: #95a5a6; |
|||
background: rgba(149, 165, 166, 0.1); |
|||
} |
|||
} |
|||
} |
|||
|
|||
.contract_content { |
|||
display: flex; |
|||
gap: 32rpx; |
|||
margin-bottom: 20rpx; |
|||
|
|||
.contract_details { |
|||
flex: 1; |
|||
|
|||
.detail_row { |
|||
display: flex; |
|||
margin-bottom: 8rpx; |
|||
|
|||
.detail_label { |
|||
font-size: 24rpx; |
|||
color: #666; |
|||
min-width: 120rpx; |
|||
} |
|||
|
|||
.detail_value { |
|||
font-size: 24rpx; |
|||
color: #333; |
|||
|
|||
&.remaining { |
|||
color: #29D3B4; |
|||
font-weight: 600; |
|||
} |
|||
|
|||
&.amount { |
|||
color: #e74c3c; |
|||
font-weight: 600; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
.contract_dates { |
|||
.date_row { |
|||
display: flex; |
|||
margin-bottom: 8rpx; |
|||
|
|||
.date_label { |
|||
font-size: 22rpx; |
|||
color: #999; |
|||
min-width: 100rpx; |
|||
} |
|||
|
|||
.date_value { |
|||
font-size: 22rpx; |
|||
color: #666; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
.contract_progress { |
|||
margin-bottom: 20rpx; |
|||
|
|||
.progress_info { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
margin-bottom: 8rpx; |
|||
|
|||
.progress_text { |
|||
font-size: 22rpx; |
|||
color: #666; |
|||
} |
|||
|
|||
.progress_percent { |
|||
font-size: 22rpx; |
|||
color: #29D3B4; |
|||
font-weight: 600; |
|||
} |
|||
} |
|||
|
|||
.progress_bar { |
|||
height: 8rpx; |
|||
background: #f0f0f0; |
|||
border-radius: 4rpx; |
|||
overflow: hidden; |
|||
|
|||
.progress_fill { |
|||
height: 100%; |
|||
background: linear-gradient(90deg, #29D3B4, #26c6a0); |
|||
border-radius: 4rpx; |
|||
transition: width 0.3s ease; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.contract_actions { |
|||
display: flex; |
|||
gap: 16rpx; |
|||
justify-content: flex-end; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
// 加载更多 |
|||
.load_more_section { |
|||
padding: 40rpx 20rpx 80rpx; |
|||
} |
|||
|
|||
// 合同详情弹窗 |
|||
.contract_popup { |
|||
position: fixed; |
|||
top: 0; |
|||
left: 0; |
|||
right: 0; |
|||
bottom: 0; |
|||
background: rgba(0, 0, 0, 0.5); |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: center; |
|||
z-index: 1000; |
|||
|
|||
.popup_content { |
|||
background: #fff; |
|||
border-radius: 16rpx; |
|||
width: 90%; |
|||
max-height: 80vh; |
|||
overflow: hidden; |
|||
|
|||
.popup_header { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
padding: 32rpx; |
|||
border-bottom: 1px solid #f0f0f0; |
|||
|
|||
.popup_title { |
|||
font-size: 32rpx; |
|||
font-weight: 600; |
|||
color: #333; |
|||
} |
|||
|
|||
.popup_close { |
|||
font-size: 48rpx; |
|||
color: #999; |
|||
font-weight: 300; |
|||
} |
|||
} |
|||
|
|||
.popup_contract_detail { |
|||
padding: 32rpx; |
|||
max-height: 60vh; |
|||
overflow-y: auto; |
|||
|
|||
.detail_section { |
|||
margin-bottom: 32rpx; |
|||
|
|||
&:last-child { |
|||
margin-bottom: 0; |
|||
} |
|||
|
|||
.section_title { |
|||
font-size: 28rpx; |
|||
font-weight: 600; |
|||
color: #333; |
|||
margin-bottom: 16rpx; |
|||
padding-bottom: 8rpx; |
|||
border-bottom: 2rpx solid #29D3B4; |
|||
} |
|||
|
|||
.info_grid { |
|||
.info_row { |
|||
display: flex; |
|||
margin-bottom: 12rpx; |
|||
|
|||
.info_label { |
|||
font-size: 26rpx; |
|||
color: #666; |
|||
min-width: 120rpx; |
|||
} |
|||
|
|||
.info_value { |
|||
font-size: 26rpx; |
|||
color: #333; |
|||
flex: 1; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.contract_terms { |
|||
font-size: 24rpx; |
|||
color: #666; |
|||
line-height: 1.6; |
|||
white-space: pre-line; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.popup_actions { |
|||
padding: 24rpx 32rpx; |
|||
display: flex; |
|||
gap: 16rpx; |
|||
border-top: 1px solid #f0f0f0; |
|||
|
|||
fui-button { |
|||
flex: 1; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
</style> |
|||
File diff suppressed because it is too large
File diff suppressed because it is too large
@ -0,0 +1,887 @@ |
|||
<!--学员消息管理页面--> |
|||
<template> |
|||
<view class="main_box"> |
|||
<!-- 自定义导航栏 --> |
|||
<view class="navbar_section"> |
|||
<view class="navbar_back" @click="goBack"> |
|||
<text class="back_icon">‹</text> |
|||
</view> |
|||
<view class="navbar_title">消息管理</view> |
|||
<view class="navbar_action"> |
|||
<view class="mark_all_read" @click="markAllAsRead" v-if="unreadCount > 0"> |
|||
<text class="mark_text">全部已读</text> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 学员信息 --> |
|||
<view class="student_info_section" v-if="studentInfo"> |
|||
<view class="student_name">{{ studentInfo.name }}</view> |
|||
<view class="message_stats"> |
|||
<text class="stat_item">未读消息:{{ unreadCount }}条</text> |
|||
<text class="stat_item">总消息:{{ messagesList.length }}条</text> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 消息类型筛选 --> |
|||
<view class="filter_section"> |
|||
<view class="filter_tabs"> |
|||
<view |
|||
v-for="tab in typeTabs" |
|||
:key="tab.value" |
|||
:class="['filter_tab', activeType === tab.value ? 'active' : '']" |
|||
@click="changeType(tab.value)" |
|||
> |
|||
{{ tab.text }} |
|||
<view class="tab_badge" v-if="tab.count > 0">{{ tab.count }}</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 消息列表 --> |
|||
<view class="messages_section"> |
|||
<view v-if="loading" class="loading_section"> |
|||
<view class="loading_text">加载中...</view> |
|||
</view> |
|||
|
|||
<view v-else-if="filteredMessages.length === 0" class="empty_section"> |
|||
<view class="empty_icon">💬</view> |
|||
<view class="empty_text">暂无消息</view> |
|||
<view class="empty_hint">新消息会在这里显示</view> |
|||
</view> |
|||
|
|||
<view v-else class="messages_list"> |
|||
<view |
|||
v-for="message in filteredMessages" |
|||
:key="message.id" |
|||
:class="['message_item', !message.is_read ? 'unread' : '']" |
|||
@click="viewMessage(message)" |
|||
> |
|||
<view class="message_header"> |
|||
<view class="message_type" :class="message.type"> |
|||
{{ getTypeText(message.type) }} |
|||
</view> |
|||
<view class="message_time">{{ formatTime(message.send_time) }}</view> |
|||
<view class="unread_dot" v-if="!message.is_read"></view> |
|||
</view> |
|||
|
|||
<view class="message_content"> |
|||
<view class="message_title">{{ message.title }}</view> |
|||
<view class="message_preview">{{ message.content | truncate(50) }}</view> |
|||
</view> |
|||
|
|||
<view class="message_meta" v-if="message.sender_name"> |
|||
<text class="sender_name">来自:{{ message.sender_name }}</text> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 加载更多 --> |
|||
<view class="load_more_section" v-if="!loading && hasMore"> |
|||
<fui-button |
|||
background="transparent" |
|||
color="#666" |
|||
@click="loadMoreMessages" |
|||
:loading="loadingMore" |
|||
> |
|||
{{ loadingMore ? '加载中...' : '加载更多' }} |
|||
</fui-button> |
|||
</view> |
|||
|
|||
<!-- 消息详情弹窗 --> |
|||
<view class="message_popup" v-if="showMessagePopup" @click="closeMessagePopup"> |
|||
<view class="popup_content" @click.stop> |
|||
<view class="popup_header"> |
|||
<view class="popup_title">消息详情</view> |
|||
<view class="popup_close" @click="closeMessagePopup">×</view> |
|||
</view> |
|||
|
|||
<view class="popup_message_detail" v-if="selectedMessage"> |
|||
<view class="detail_header"> |
|||
<view class="detail_type" :class="selectedMessage.type"> |
|||
{{ getTypeText(selectedMessage.type) }} |
|||
</view> |
|||
<view class="detail_time">{{ formatFullTime(selectedMessage.send_time) }}</view> |
|||
</view> |
|||
|
|||
<view class="detail_title">{{ selectedMessage.title }}</view> |
|||
|
|||
<view class="detail_content">{{ selectedMessage.content }}</view> |
|||
|
|||
<view class="detail_sender" v-if="selectedMessage.sender_name"> |
|||
<text class="sender_label">发送人:</text> |
|||
<text class="sender_value">{{ selectedMessage.sender_name }}</text> |
|||
</view> |
|||
|
|||
<view class="detail_attachment" v-if="selectedMessage.attachment_url"> |
|||
<fui-button |
|||
background="#29d3b4" |
|||
size="small" |
|||
@click="viewAttachment(selectedMessage.attachment_url)" |
|||
> |
|||
查看附件 |
|||
</fui-button> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="popup_actions"> |
|||
<fui-button |
|||
v-if="selectedMessage && selectedMessage.type === 'homework'" |
|||
background="#f39c12" |
|||
@click="submitHomework" |
|||
> |
|||
提交作业 |
|||
</fui-button> |
|||
|
|||
<fui-button |
|||
v-if="selectedMessage && selectedMessage.type === 'notification'" |
|||
background="#29d3b4" |
|||
@click="confirmNotification" |
|||
> |
|||
确认已读 |
|||
</fui-button> |
|||
|
|||
<fui-button |
|||
background="#f8f9fa" |
|||
color="#666" |
|||
@click="closeMessagePopup" |
|||
> |
|||
关闭 |
|||
</fui-button> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
import apiRoute from '@/api/apiRoute.js' |
|||
|
|||
export default { |
|||
filters: { |
|||
truncate(text, length) { |
|||
if (!text) return '' |
|||
if (text.length <= length) return text |
|||
return text.substring(0, length) + '...' |
|||
} |
|||
}, |
|||
|
|||
data() { |
|||
return { |
|||
studentId: 0, |
|||
studentInfo: {}, |
|||
messagesList: [], |
|||
filteredMessages: [], |
|||
loading: false, |
|||
loadingMore: false, |
|||
hasMore: true, |
|||
currentPage: 1, |
|||
activeType: 'all', |
|||
showMessagePopup: false, |
|||
selectedMessage: null, |
|||
typeTabs: [ |
|||
{ value: 'all', text: '全部', count: 0 }, |
|||
{ value: 'system', text: '系统消息', count: 0 }, |
|||
{ value: 'notification', text: '通知公告', count: 0 }, |
|||
{ value: 'homework', text: '作业任务', count: 0 }, |
|||
{ value: 'feedback', text: '反馈评价', count: 0 }, |
|||
{ value: 'reminder', text: '课程提醒', count: 0 } |
|||
] |
|||
} |
|||
}, |
|||
|
|||
computed: { |
|||
unreadCount() { |
|||
return this.messagesList.filter(msg => !msg.is_read).length |
|||
} |
|||
}, |
|||
|
|||
onLoad(options) { |
|||
this.studentId = parseInt(options.student_id) || 0 |
|||
if (this.studentId) { |
|||
this.initPage() |
|||
} else { |
|||
uni.showToast({ |
|||
title: '参数错误', |
|||
icon: 'none' |
|||
}) |
|||
} |
|||
}, |
|||
|
|||
methods: { |
|||
goBack() { |
|||
uni.navigateBack() |
|||
}, |
|||
|
|||
async initPage() { |
|||
await this.loadStudentInfo() |
|||
await this.loadMessages() |
|||
this.updateTypeCounts() |
|||
}, |
|||
|
|||
async loadStudentInfo() { |
|||
try { |
|||
// 模拟获取学员信息 |
|||
const mockStudentInfo = { |
|||
id: this.studentId, |
|||
name: '小明' |
|||
} |
|||
this.studentInfo = mockStudentInfo |
|||
} catch (error) { |
|||
console.error('获取学员信息失败:', error) |
|||
} |
|||
}, |
|||
|
|||
async loadMessages() { |
|||
this.loading = true |
|||
try { |
|||
console.log('加载消息列表:', this.studentId) |
|||
|
|||
// 模拟API调用 |
|||
// const response = await apiRoute.getStudentMessages({ |
|||
// student_id: this.studentId, |
|||
// page: this.currentPage, |
|||
// limit: 10 |
|||
// }) |
|||
|
|||
// 使用模拟数据 |
|||
const mockResponse = { |
|||
code: 1, |
|||
data: { |
|||
list: [ |
|||
{ |
|||
id: 1, |
|||
type: 'system', |
|||
title: '欢迎加入运动识堂', |
|||
content: '欢迎您的孩子加入我们的运动训练课程!我们将为您的孩子提供专业的体能训练指导,帮助孩子健康成长。课程安排和相关信息会及时通过消息推送给您,请注意查收。', |
|||
sender_name: '系统管理员', |
|||
send_time: '2024-01-15 09:00:00', |
|||
is_read: false, |
|||
attachment_url: '' |
|||
}, |
|||
{ |
|||
id: 2, |
|||
type: 'notification', |
|||
title: '本周课程安排通知', |
|||
content: '本周课程安排已更新,请及时查看课程表。周三下午16:00-17:00的基础体能训练课程请准时参加,课程地点:训练馆A。如有疑问请联系教练。', |
|||
sender_name: '张教练', |
|||
send_time: '2024-01-14 15:30:00', |
|||
is_read: true, |
|||
attachment_url: '' |
|||
}, |
|||
{ |
|||
id: 3, |
|||
type: 'homework', |
|||
title: '体能训练作业', |
|||
content: '请完成以下体能训练作业:1. 每天跳绳100个 2. 俯卧撑10个 3. 仰卧起坐15个。请在下次课程前完成并提交训练视频。', |
|||
sender_name: '李教练', |
|||
send_time: '2024-01-13 18:20:00', |
|||
is_read: false, |
|||
attachment_url: '/uploads/homework/homework_guide.pdf' |
|||
}, |
|||
{ |
|||
id: 4, |
|||
type: 'feedback', |
|||
title: '上次课程反馈', |
|||
content: '您的孩子在上次的基础体能训练中表现优秀,动作标准,学习积极。建议继续加强核心力量训练,可以适当增加训练强度。', |
|||
sender_name: '王教练', |
|||
send_time: '2024-01-12 20:15:00', |
|||
is_read: true, |
|||
attachment_url: '' |
|||
}, |
|||
{ |
|||
id: 5, |
|||
type: 'reminder', |
|||
title: '明日课程提醒', |
|||
content: '提醒您的孩子明天下午14:00有专项技能训练课程,请准时到达训练馆B。建议提前10分钟到场进行热身准备。', |
|||
sender_name: '系统提醒', |
|||
send_time: '2024-01-11 19:00:00', |
|||
is_read: true, |
|||
attachment_url: '' |
|||
} |
|||
], |
|||
total: 5, |
|||
has_more: false |
|||
} |
|||
} |
|||
|
|||
if (mockResponse.code === 1) { |
|||
const newList = mockResponse.data.list || [] |
|||
if (this.currentPage === 1) { |
|||
this.messagesList = newList |
|||
} else { |
|||
this.messagesList = [...this.messagesList, ...newList] |
|||
} |
|||
|
|||
this.hasMore = mockResponse.data.has_more || false |
|||
this.applyTypeFilter() |
|||
console.log('消息数据加载成功:', this.messagesList) |
|||
} else { |
|||
uni.showToast({ |
|||
title: mockResponse.msg || '获取消息列表失败', |
|||
icon: 'none' |
|||
}) |
|||
} |
|||
} catch (error) { |
|||
console.error('获取消息列表失败:', error) |
|||
uni.showToast({ |
|||
title: '获取消息列表失败', |
|||
icon: 'none' |
|||
}) |
|||
} finally { |
|||
this.loading = false |
|||
this.loadingMore = false |
|||
} |
|||
}, |
|||
|
|||
async loadMoreMessages() { |
|||
if (this.loadingMore || !this.hasMore) return |
|||
|
|||
this.loadingMore = true |
|||
this.currentPage++ |
|||
await this.loadMessages() |
|||
}, |
|||
|
|||
changeType(type) { |
|||
this.activeType = type |
|||
this.applyTypeFilter() |
|||
}, |
|||
|
|||
applyTypeFilter() { |
|||
if (this.activeType === 'all') { |
|||
this.filteredMessages = [...this.messagesList] |
|||
} else { |
|||
this.filteredMessages = this.messagesList.filter(message => message.type === this.activeType) |
|||
} |
|||
}, |
|||
|
|||
updateTypeCounts() { |
|||
const counts = {} |
|||
this.messagesList.forEach(message => { |
|||
counts[message.type] = (counts[message.type] || 0) + 1 |
|||
}) |
|||
|
|||
this.typeTabs.forEach(tab => { |
|||
if (tab.value === 'all') { |
|||
tab.count = this.messagesList.length |
|||
} else { |
|||
tab.count = counts[tab.value] || 0 |
|||
} |
|||
}) |
|||
}, |
|||
|
|||
getTypeText(type) { |
|||
const typeMap = { |
|||
'system': '系统消息', |
|||
'notification': '通知公告', |
|||
'homework': '作业任务', |
|||
'feedback': '反馈评价', |
|||
'reminder': '课程提醒' |
|||
} |
|||
return typeMap[type] || type |
|||
}, |
|||
|
|||
formatTime(timeString) { |
|||
const now = new Date() |
|||
const msgTime = new Date(timeString) |
|||
const diffHours = (now - msgTime) / (1000 * 60 * 60) |
|||
|
|||
if (diffHours < 1) { |
|||
return '刚刚' |
|||
} else if (diffHours < 24) { |
|||
return Math.floor(diffHours) + '小时前' |
|||
} else if (diffHours < 48) { |
|||
return '昨天' |
|||
} else { |
|||
const month = String(msgTime.getMonth() + 1).padStart(2, '0') |
|||
const day = String(msgTime.getDate()).padStart(2, '0') |
|||
return `${month}-${day}` |
|||
} |
|||
}, |
|||
|
|||
formatFullTime(timeString) { |
|||
const date = new Date(timeString) |
|||
const year = date.getFullYear() |
|||
const month = String(date.getMonth() + 1).padStart(2, '0') |
|||
const day = String(date.getDate()).padStart(2, '0') |
|||
const hours = String(date.getHours()).padStart(2, '0') |
|||
const minutes = String(date.getMinutes()).padStart(2, '0') |
|||
return `${year}-${month}-${day} ${hours}:${minutes}` |
|||
}, |
|||
|
|||
viewMessage(message) { |
|||
this.selectedMessage = message |
|||
this.showMessagePopup = true |
|||
|
|||
// 标记为已读 |
|||
if (!message.is_read) { |
|||
this.markAsRead(message) |
|||
} |
|||
}, |
|||
|
|||
closeMessagePopup() { |
|||
this.showMessagePopup = false |
|||
this.selectedMessage = null |
|||
}, |
|||
|
|||
async markAsRead(message) { |
|||
try { |
|||
console.log('标记已读:', message.id) |
|||
|
|||
// 模拟API调用 |
|||
await new Promise(resolve => setTimeout(resolve, 200)) |
|||
|
|||
// 更新本地状态 |
|||
const index = this.messagesList.findIndex(msg => msg.id === message.id) |
|||
if (index !== -1) { |
|||
this.messagesList[index].is_read = true |
|||
} |
|||
} catch (error) { |
|||
console.error('标记已读失败:', error) |
|||
} |
|||
}, |
|||
|
|||
async markAllAsRead() { |
|||
if (this.unreadCount === 0) { |
|||
uni.showToast({ |
|||
title: '暂无未读消息', |
|||
icon: 'none' |
|||
}) |
|||
return |
|||
} |
|||
|
|||
try { |
|||
console.log('标记全部已读') |
|||
|
|||
// 模拟API调用 |
|||
await new Promise(resolve => setTimeout(resolve, 500)) |
|||
|
|||
// 更新本地状态 |
|||
this.messagesList.forEach(message => { |
|||
message.is_read = true |
|||
}) |
|||
|
|||
uni.showToast({ |
|||
title: '已全部标记为已读', |
|||
icon: 'success' |
|||
}) |
|||
} catch (error) { |
|||
console.error('标记全部已读失败:', error) |
|||
uni.showToast({ |
|||
title: '操作失败', |
|||
icon: 'none' |
|||
}) |
|||
} |
|||
}, |
|||
|
|||
viewAttachment(attachmentUrl) { |
|||
console.log('查看附件:', attachmentUrl) |
|||
uni.showModal({ |
|||
title: '提示', |
|||
content: '附件下载功能开发中', |
|||
showCancel: false |
|||
}) |
|||
}, |
|||
|
|||
submitHomework() { |
|||
console.log('提交作业') |
|||
uni.showModal({ |
|||
title: '提示', |
|||
content: '作业提交功能开发中', |
|||
showCancel: false |
|||
}) |
|||
}, |
|||
|
|||
confirmNotification() { |
|||
console.log('确认通知') |
|||
this.closeMessagePopup() |
|||
uni.showToast({ |
|||
title: '已确认', |
|||
icon: 'success' |
|||
}) |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style lang="less" scoped> |
|||
.main_box { |
|||
background: #f8f9fa; |
|||
min-height: 100vh; |
|||
} |
|||
|
|||
// 自定义导航栏 |
|||
.navbar_section { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
background: #29D3B4; |
|||
padding: 40rpx 32rpx 20rpx; |
|||
|
|||
// 小程序端适配状态栏 |
|||
// #ifdef MP-WEIXIN |
|||
padding-top: 80rpx; |
|||
// #endif |
|||
|
|||
.navbar_back { |
|||
width: 60rpx; |
|||
|
|||
.back_icon { |
|||
color: #fff; |
|||
font-size: 40rpx; |
|||
font-weight: 600; |
|||
} |
|||
} |
|||
|
|||
.navbar_title { |
|||
color: #fff; |
|||
font-size: 32rpx; |
|||
font-weight: 600; |
|||
} |
|||
|
|||
.navbar_action { |
|||
width: 120rpx; |
|||
display: flex; |
|||
justify-content: flex-end; |
|||
|
|||
.mark_all_read { |
|||
background: rgba(255, 255, 255, 0.2); |
|||
padding: 8rpx 16rpx; |
|||
border-radius: 16rpx; |
|||
|
|||
.mark_text { |
|||
color: #fff; |
|||
font-size: 24rpx; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
// 学员信息 |
|||
.student_info_section { |
|||
background: #fff; |
|||
padding: 24rpx 32rpx; |
|||
border-bottom: 1px solid #f0f0f0; |
|||
|
|||
.student_name { |
|||
font-size: 32rpx; |
|||
font-weight: 600; |
|||
color: #333; |
|||
margin-bottom: 12rpx; |
|||
} |
|||
|
|||
.message_stats { |
|||
display: flex; |
|||
gap: 24rpx; |
|||
|
|||
.stat_item { |
|||
font-size: 24rpx; |
|||
color: #666; |
|||
} |
|||
} |
|||
} |
|||
|
|||
// 类型筛选 |
|||
.filter_section { |
|||
background: #fff; |
|||
margin: 20rpx; |
|||
border-radius: 16rpx; |
|||
padding: 24rpx 32rpx; |
|||
|
|||
.filter_tabs { |
|||
display: flex; |
|||
flex-wrap: wrap; |
|||
gap: 16rpx; |
|||
|
|||
.filter_tab { |
|||
position: relative; |
|||
padding: 12rpx 24rpx; |
|||
font-size: 26rpx; |
|||
color: #666; |
|||
background: #f8f9fa; |
|||
border-radius: 20rpx; |
|||
|
|||
&.active { |
|||
color: #fff; |
|||
background: #29D3B4; |
|||
} |
|||
|
|||
.tab_badge { |
|||
position: absolute; |
|||
top: -8rpx; |
|||
right: -8rpx; |
|||
background: #ff4757; |
|||
color: #fff; |
|||
font-size: 18rpx; |
|||
padding: 2rpx 8rpx; |
|||
border-radius: 12rpx; |
|||
min-width: 16rpx; |
|||
text-align: center; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
// 消息列表 |
|||
.messages_section { |
|||
margin: 0 20rpx; |
|||
|
|||
.loading_section, .empty_section { |
|||
background: #fff; |
|||
border-radius: 16rpx; |
|||
padding: 80rpx 32rpx; |
|||
text-align: center; |
|||
|
|||
.loading_text, .empty_text { |
|||
font-size: 28rpx; |
|||
color: #666; |
|||
margin-bottom: 12rpx; |
|||
} |
|||
|
|||
.empty_icon { |
|||
font-size: 80rpx; |
|||
margin-bottom: 24rpx; |
|||
} |
|||
|
|||
.empty_hint { |
|||
font-size: 24rpx; |
|||
color: #999; |
|||
} |
|||
} |
|||
|
|||
.messages_list { |
|||
.message_item { |
|||
background: #fff; |
|||
border-radius: 16rpx; |
|||
padding: 24rpx; |
|||
margin-bottom: 16rpx; |
|||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.06); |
|||
position: relative; |
|||
|
|||
&.unread { |
|||
background: #f8fdff; |
|||
border-left: 4rpx solid #29D3B4; |
|||
} |
|||
|
|||
.message_header { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
margin-bottom: 16rpx; |
|||
|
|||
.message_type { |
|||
font-size: 22rpx; |
|||
padding: 4rpx 12rpx; |
|||
border-radius: 12rpx; |
|||
|
|||
&.system { |
|||
color: #3498db; |
|||
background: rgba(52, 152, 219, 0.1); |
|||
} |
|||
|
|||
&.notification { |
|||
color: #f39c12; |
|||
background: rgba(243, 156, 18, 0.1); |
|||
} |
|||
|
|||
&.homework { |
|||
color: #e74c3c; |
|||
background: rgba(231, 76, 60, 0.1); |
|||
} |
|||
|
|||
&.feedback { |
|||
color: #27ae60; |
|||
background: rgba(39, 174, 96, 0.1); |
|||
} |
|||
|
|||
&.reminder { |
|||
color: #9b59b6; |
|||
background: rgba(155, 89, 182, 0.1); |
|||
} |
|||
} |
|||
|
|||
.message_time { |
|||
font-size: 22rpx; |
|||
color: #999; |
|||
} |
|||
|
|||
.unread_dot { |
|||
position: absolute; |
|||
top: 20rpx; |
|||
right: 20rpx; |
|||
width: 12rpx; |
|||
height: 12rpx; |
|||
background: #ff4757; |
|||
border-radius: 50%; |
|||
} |
|||
} |
|||
|
|||
.message_content { |
|||
margin-bottom: 12rpx; |
|||
|
|||
.message_title { |
|||
font-size: 28rpx; |
|||
font-weight: 600; |
|||
color: #333; |
|||
margin-bottom: 8rpx; |
|||
} |
|||
|
|||
.message_preview { |
|||
font-size: 24rpx; |
|||
color: #666; |
|||
line-height: 1.4; |
|||
} |
|||
} |
|||
|
|||
.message_meta { |
|||
.sender_name { |
|||
font-size: 22rpx; |
|||
color: #999; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
// 加载更多 |
|||
.load_more_section { |
|||
padding: 40rpx 20rpx 80rpx; |
|||
} |
|||
|
|||
// 消息详情弹窗 |
|||
.message_popup { |
|||
position: fixed; |
|||
top: 0; |
|||
left: 0; |
|||
right: 0; |
|||
bottom: 0; |
|||
background: rgba(0, 0, 0, 0.5); |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: center; |
|||
z-index: 1000; |
|||
|
|||
.popup_content { |
|||
background: #fff; |
|||
border-radius: 16rpx; |
|||
width: 90%; |
|||
max-height: 80vh; |
|||
overflow: hidden; |
|||
|
|||
.popup_header { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
padding: 32rpx; |
|||
border-bottom: 1px solid #f0f0f0; |
|||
|
|||
.popup_title { |
|||
font-size: 32rpx; |
|||
font-weight: 600; |
|||
color: #333; |
|||
} |
|||
|
|||
.popup_close { |
|||
font-size: 48rpx; |
|||
color: #999; |
|||
font-weight: 300; |
|||
} |
|||
} |
|||
|
|||
.popup_message_detail { |
|||
padding: 32rpx; |
|||
max-height: 60vh; |
|||
overflow-y: auto; |
|||
|
|||
.detail_header { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
margin-bottom: 20rpx; |
|||
|
|||
.detail_type { |
|||
font-size: 24rpx; |
|||
padding: 6rpx 16rpx; |
|||
border-radius: 16rpx; |
|||
|
|||
&.system { |
|||
color: #3498db; |
|||
background: rgba(52, 152, 219, 0.1); |
|||
} |
|||
|
|||
&.notification { |
|||
color: #f39c12; |
|||
background: rgba(243, 156, 18, 0.1); |
|||
} |
|||
|
|||
&.homework { |
|||
color: #e74c3c; |
|||
background: rgba(231, 76, 60, 0.1); |
|||
} |
|||
|
|||
&.feedback { |
|||
color: #27ae60; |
|||
background: rgba(39, 174, 96, 0.1); |
|||
} |
|||
|
|||
&.reminder { |
|||
color: #9b59b6; |
|||
background: rgba(155, 89, 182, 0.1); |
|||
} |
|||
} |
|||
|
|||
.detail_time { |
|||
font-size: 24rpx; |
|||
color: #999; |
|||
} |
|||
} |
|||
|
|||
.detail_title { |
|||
font-size: 32rpx; |
|||
font-weight: 600; |
|||
color: #333; |
|||
margin-bottom: 20rpx; |
|||
} |
|||
|
|||
.detail_content { |
|||
font-size: 28rpx; |
|||
color: #666; |
|||
line-height: 1.6; |
|||
margin-bottom: 20rpx; |
|||
} |
|||
|
|||
.detail_sender { |
|||
margin-bottom: 20rpx; |
|||
padding-top: 20rpx; |
|||
border-top: 1px solid #f8f9fa; |
|||
|
|||
.sender_label { |
|||
font-size: 24rpx; |
|||
color: #999; |
|||
} |
|||
|
|||
.sender_value { |
|||
font-size: 24rpx; |
|||
color: #666; |
|||
} |
|||
} |
|||
|
|||
.detail_attachment { |
|||
margin-bottom: 20rpx; |
|||
} |
|||
} |
|||
|
|||
.popup_actions { |
|||
padding: 24rpx 32rpx; |
|||
display: flex; |
|||
gap: 16rpx; |
|||
border-top: 1px solid #f0f0f0; |
|||
|
|||
fui-button { |
|||
flex: 1; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
</style> |
|||
@ -0,0 +1,195 @@ |
|||
<!--课时消耗列表--> |
|||
<template> |
|||
<view class="assemble"> |
|||
<view style="height: 50rpx;"></view> |
|||
|
|||
<view class="message_box" v-if="!tableList.length"> |
|||
暂无更多数据 |
|||
</view> |
|||
|
|||
<view class="ul" v-if="tableList.length"> |
|||
<view class="li" v-for="(v,k) in tableList" :key="k"> |
|||
<view class="left"> |
|||
<view class="title">{{v.name}}</view> |
|||
<view class="date">课程使用日期:{{v.usage_date}}</view> |
|||
</view> |
|||
<view class="right"> |
|||
<view |
|||
class="btn" |
|||
style="background-color: #29d3b4;" |
|||
> |
|||
{{v.usage_date}}课时 |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
import apiRoute from '@/api/apiRoute.js'; |
|||
|
|||
export default { |
|||
data() { |
|||
return { |
|||
loading:false,//加载状态 |
|||
lowerThreshold: 100,//距离底部多远触发 |
|||
isReachedBottom: false,//防止重复加载|true=不可加载|false=可加载 |
|||
|
|||
//筛选条件 |
|||
filteredData:{ |
|||
page:1,//当前页码 |
|||
limit:10,//每页返回数据条数 |
|||
total:10,//数据总条数 |
|||
resources_id:'',//学生资源表id |
|||
}, |
|||
|
|||
tableList:[],//表格数据 |
|||
|
|||
memberInfo:{},//当前登录的学生资源信息 |
|||
} |
|||
}, |
|||
onLoad(options) { |
|||
|
|||
}, |
|||
onShow(){ |
|||
this.init()//初始化 |
|||
}, |
|||
//下拉刷新 |
|||
async onPullDownRefresh() { |
|||
//重置为第一页 |
|||
await this.resetFilteredData() |
|||
await this.getList() |
|||
}, |
|||
methods: { |
|||
//初始化 |
|||
async init(){ |
|||
await this.getMemberInfo()//获取当前登录的学生信息 |
|||
await this.getList()//获取列表 |
|||
}, |
|||
|
|||
//获取当前登录的学生信息 |
|||
async getMemberInfo() { |
|||
let res = await apiRoute.xy_memberInfo({}) |
|||
if(res.code != 1){ |
|||
uni.showToast({ |
|||
title: res.msg, |
|||
icon: 'none' |
|||
}) |
|||
return |
|||
} |
|||
this.memberInfo = res.data |
|||
this.filteredData.resources_id = res.data.id |
|||
}, |
|||
|
|||
//加载更多(下一页) |
|||
loadMoreData() { |
|||
//判断是否加载 |
|||
if (!this.isReachedBottom) { |
|||
this.isReachedBottom = true;//设置为不可请求状态 |
|||
this.getList(); |
|||
} |
|||
}, |
|||
//重置为第一页 |
|||
async resetFilteredData() { |
|||
this.isReachedBottom = false; // 重置状态,以便下次触发加载更多 |
|||
|
|||
this.filteredData.page = 1//当前页码 |
|||
this.filteredData.limit = 10//每页返回数据条数 |
|||
this.filteredData.total = 10//数据总条数 |
|||
}, |
|||
|
|||
//获取作业列表 |
|||
async getList(){ |
|||
this.loading = true |
|||
|
|||
let data = {...this.filteredData} |
|||
|
|||
//判断是否还有数据 |
|||
if ((this.filteredData.page - 1) * this.filteredData.limit >= this.filteredData.total) { |
|||
this.loading = false |
|||
uni.showToast({ |
|||
title: '暂无更多', |
|||
icon: 'none' |
|||
}) |
|||
return |
|||
} |
|||
|
|||
if(data.page == 1){ |
|||
this.tableList = [] |
|||
} |
|||
|
|||
let res = await apiRoute.xy_personCourseScheduleGetStudentCourseUsageList(data) |
|||
this.loading = false |
|||
this.isReachedBottom = false; |
|||
if (res.code != 1){ |
|||
uni.showToast({ |
|||
title: res.msg, |
|||
icon: 'none' |
|||
}) |
|||
return |
|||
} |
|||
|
|||
this.tableList = this.tableList.concat(res.data.data); // 使用 concat 方法 将新数据追加到数组中 |
|||
|
|||
console.log('列表',this.tableList) |
|||
this.filteredData.total = res.data.total |
|||
this.filteredData.page++ |
|||
}, |
|||
|
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style lang="less" scoped> |
|||
.message_box{ |
|||
font-size: 30rpx; |
|||
text-align: center; |
|||
color: #fff; |
|||
} |
|||
.assemble { |
|||
width: 100%; |
|||
height: 100vh; |
|||
background: #333333; |
|||
overflow: auto; |
|||
} |
|||
|
|||
.ul { |
|||
display: flex; |
|||
flex-direction: column; |
|||
gap: 12rpx; |
|||
background-color: #fff; |
|||
width: 90%; |
|||
margin: 0 auto 30rpx; |
|||
padding: 26rpx; |
|||
border-radius: 16rpx; |
|||
} |
|||
|
|||
.li { |
|||
padding: 30rpx 20rpx; |
|||
border: 1px solid #29D3B4; |
|||
border-radius: 18rpx; |
|||
background-color: rgba(41, 211, 180, 0.16); |
|||
font-size: 26rpx; |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
} |
|||
|
|||
.left { |
|||
display: flex; |
|||
flex-direction: column; |
|||
gap: 15rpx; |
|||
} |
|||
|
|||
.btn { |
|||
width: 110rpx; |
|||
height: 44rpx; |
|||
line-height: 44rpx; |
|||
border-radius: 8rpx; |
|||
background-color: rgba(41, 211, 180, 1); |
|||
color: rgba(255, 255, 255, 1); |
|||
font-size: 20rpx; |
|||
text-align: center; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,211 @@ |
|||
<!--我的教练-列表--> |
|||
<template> |
|||
<view class="main_box"> |
|||
<scroll-view scroll-y="true" :lower-threshold="lowerThreshold" |
|||
@scrolltolower="loadMoreData" style="height: 100%;padding-top:50rpx;padding-bottom: 50rpx"> |
|||
|
|||
<!-- <view class="data_hint" v-if="!this.tableList.length">暂无更多数据</view>--> |
|||
|
|||
<view class="main_section" v-for="(v,k) in tableList" :key="k"> |
|||
<view class="left"> |
|||
<!-- 头像--> |
|||
<image :src="v.head_img ? v.head_img : $util.img('/uniapp_src/static/images/common/yong_hu.png')" class="pic"></image> |
|||
</view> |
|||
<view class="right"> |
|||
<view class="title">姓名:{{v.name}}</view> |
|||
<view class="title">电话:{{v.phone}}</view> |
|||
</view> |
|||
|
|||
</view> |
|||
</scroll-view> |
|||
<!-- 加载状态--> |
|||
<!-- <fui-loading :isFixed="true" srcCol="/static/icon-img/loading_white.png" text="正在加载..." v-if="loading"></fui-loading>--> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
import apiRoute from '@/api/apiRoute.js'; |
|||
import memberApi from '@/api/member.js'; |
|||
import AQTabber from "@/components/AQ/AQTabber.vue" |
|||
|
|||
|
|||
export default { |
|||
components: { |
|||
AQTabber, |
|||
}, |
|||
data() { |
|||
return { |
|||
loading:false,//加载状态 |
|||
lowerThreshold: 100,//距离底部多远触发 |
|||
isReachedBottom: false,//防止重复加载|true=不可加载|false=可加载 |
|||
|
|||
memberInfo:{id:''},//客户资源信息 |
|||
|
|||
//筛选条件 |
|||
filteredData:{ |
|||
page:1,//当前页码 |
|||
limit:10,//每页返回数据条数 |
|||
total:10,//数据总条数 |
|||
resources_id:'',//学生资源表id |
|||
}, |
|||
|
|||
tableList:[],//表格数据 |
|||
} |
|||
}, |
|||
onLoad(options) {}, |
|||
onShow() { |
|||
this.init()//初始化 |
|||
}, |
|||
//下拉刷新 |
|||
async onPullDownRefresh() { |
|||
//重置为第一页 |
|||
await this.resetFilteredData() |
|||
await this.getList() |
|||
}, |
|||
methods: { |
|||
//初始化 |
|||
async init() { |
|||
await this.getMemberInfo(); |
|||
await this.getList(); |
|||
}, |
|||
|
|||
//重置为第一页 |
|||
async resetFilteredData() { |
|||
this.isReachedBottom = false; // 重置状态,以便下次触发加载更多 |
|||
|
|||
this.filteredData.page = 1//当前页码 |
|||
this.filteredData.limit = 10//每页返回数据条数 |
|||
this.filteredData.total = 10//数据总条数 |
|||
}, |
|||
|
|||
//获取当前登录的学生信息 |
|||
async getMemberInfo() { |
|||
let res = await apiRoute.xy_memberInfo({}) |
|||
if(res.code != 1){ |
|||
uni.showToast({ |
|||
title: res.msg, |
|||
icon: 'none' |
|||
}) |
|||
return |
|||
} |
|||
this.memberInfo = res.data |
|||
this.filteredData.resources_id = res.data.id |
|||
}, |
|||
|
|||
|
|||
//加载更多(下一页) |
|||
loadMoreData() { |
|||
return //本页面无需下一页 |
|||
//判断是否加载 |
|||
if (!this.isReachedBottom) { |
|||
this.isReachedBottom = true;//设置为不可请求状态 |
|||
this.getList(); |
|||
} |
|||
}, |
|||
|
|||
//重置为第一页 |
|||
loadData() { |
|||
setTimeout(() => { |
|||
this.isReachedBottom = false; // 重置状态,以便下次触发加载更多 |
|||
}, 1000); |
|||
}, |
|||
|
|||
//获取教练列表 |
|||
async getList(){ |
|||
this.loading = true |
|||
|
|||
let data = {...this.filteredData} |
|||
|
|||
//判断是否还有数据 |
|||
if ((this.filteredData.page - 1) * this.filteredData.limit >= this.filteredData.total) { |
|||
this.loading = false |
|||
uni.showToast({ |
|||
title: '暂无更多', |
|||
icon: 'none' |
|||
}) |
|||
return |
|||
} |
|||
|
|||
if(data.page == 1){ |
|||
this.tableList = [] |
|||
} |
|||
|
|||
let res = await apiRoute.xy_personCourseScheduleGetMyCoach(data) |
|||
this.loading = false |
|||
this.isReachedBottom = false; |
|||
if (res.code != 1){ |
|||
uni.showToast({ |
|||
title: res.msg, |
|||
icon: 'none' |
|||
}) |
|||
return |
|||
} |
|||
|
|||
this.tableList = this.tableList.concat(res.data.data); // 使用 concat 方法 将新数据追加到数组中 |
|||
|
|||
console.log('列表',this.tableList) |
|||
this.filteredData.total = res.data.total |
|||
this.filteredData.page++ |
|||
}, |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style lang="less" scoped> |
|||
.main_box { |
|||
width: 100%; |
|||
height: 100%; |
|||
overflow: auto; |
|||
background: #292929; |
|||
} |
|||
.data_hint{ |
|||
margin-top: 100rpx; |
|||
font-size: 30rpx; |
|||
text-align: center; |
|||
color: #fff; |
|||
} |
|||
.main_section{ |
|||
width: 92%; |
|||
border-radius: 15rpx; |
|||
background-color: #404045; |
|||
margin: 20rpx auto; |
|||
padding: 30rpx; |
|||
color: #fff; |
|||
display: flex; |
|||
align-items: center; |
|||
.left{ |
|||
display: flex; |
|||
flex-direction: column; |
|||
align-items: center; |
|||
.pic{ |
|||
width: 100rpx; |
|||
height: 100rpx; |
|||
border-radius: 50%; |
|||
} |
|||
} |
|||
.right{ |
|||
margin-left: 20rpx; |
|||
display: flex; |
|||
flex-direction: column; |
|||
gap: 20rpx; |
|||
} |
|||
} |
|||
.title{ |
|||
font-size: 32rpx; |
|||
} |
|||
.con{ |
|||
color: #D7D7D7; |
|||
font-size: 26rpx; |
|||
margin-top: 20rpx; |
|||
} |
|||
.current-venue{ |
|||
border-radius: 8rpx; |
|||
border: 2rpx #F59A23 solid; |
|||
width: 120rpx; |
|||
text-align: center; |
|||
color: #F59A23; |
|||
position: absolute; |
|||
top: 10%; |
|||
right: 3%; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,158 @@ |
|||
<template> |
|||
<view class="assemble"> |
|||
<view style="height: 50rpx;"></view> |
|||
<view class="ul"> |
|||
<view class="li"> |
|||
<view class="left"> |
|||
<view class="title">智卓燕</view> |
|||
<view class="date">哥哥</view> |
|||
</view> |
|||
<view class="right"> |
|||
<view class="btn" style="background-color: #29d3b4;">5课时</view> |
|||
</view> |
|||
</view> |
|||
<view class="li"> |
|||
<view class="left"> |
|||
<view class="title">智卓燕</view> |
|||
<view class="date">哥哥</view> |
|||
</view> |
|||
<view class="right"> |
|||
<view class="btn" style="background-color: #29d3b4;">5课时</view> |
|||
</view> |
|||
</view> |
|||
<view class="li"> |
|||
<view class="left"> |
|||
<view class="title">智卓燕</view> |
|||
<view class="date">哥哥</view> |
|||
</view> |
|||
<view class="right"> |
|||
<view class="btn" style="background-color: #29d3b4;">5课时</view> |
|||
</view> |
|||
</view> |
|||
<view class="li"> |
|||
<view class="left"> |
|||
<view class="title">智卓燕</view> |
|||
<view class="date">哥哥</view> |
|||
</view> |
|||
<view class="right"> |
|||
<view class="btn" style="background-color: #29d3b4;">5课时</view> |
|||
</view> |
|||
</view> |
|||
<view class="li"> |
|||
<view class="left"> |
|||
<view class="title">智卓燕</view> |
|||
<view class="date">哥哥</view> |
|||
</view> |
|||
<view class="right"> |
|||
<view class="btn" style="background-color: #29d3b4;">5课时</view> |
|||
</view> |
|||
</view> |
|||
<view class="li"> |
|||
<view class="left"> |
|||
<view class="title">智卓燕</view> |
|||
<view class="date">哥哥</view> |
|||
</view> |
|||
<view class="right"> |
|||
<view class="btn" style="background-color: #29d3b4;">5课时</view> |
|||
</view> |
|||
</view> |
|||
<view class="li"> |
|||
<view class="left"> |
|||
<view class="title">智卓燕</view> |
|||
<view class="date">哥哥</view> |
|||
</view> |
|||
<view class="right"> |
|||
<view class="btn" style="background-color: #29d3b4;">5课时</view> |
|||
</view> |
|||
</view> |
|||
<view class="li"> |
|||
<view class="left"> |
|||
<view class="title">智卓燕</view> |
|||
<view class="date">哥哥</view> |
|||
</view> |
|||
<view class="right"> |
|||
<view class="btn" style="background-color: #29d3b4;">5课时</view> |
|||
</view> |
|||
</view> |
|||
<view class="li"> |
|||
<view class="left"> |
|||
<view class="title">智卓燕</view> |
|||
<view class="date">哥哥</view> |
|||
</view> |
|||
<view class="right"> |
|||
<view class="btn" style="background-color: #29d3b4;">5课时</view> |
|||
</view> |
|||
</view> |
|||
<view class="li"> |
|||
<view class="left"> |
|||
<view class="title">智卓燕</view> |
|||
<view class="date">哥哥</view> |
|||
</view> |
|||
<view class="right"> |
|||
<view class="btn" style="background-color: #29d3b4;">5课时</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
export default { |
|||
data() { |
|||
return { |
|||
|
|||
} |
|||
}, |
|||
methods: { |
|||
|
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style lang="less" scoped> |
|||
.assemble { |
|||
width: 100%; |
|||
height: 100vh; |
|||
background: #333333; |
|||
overflow: auto; |
|||
} |
|||
|
|||
.ul { |
|||
display: flex; |
|||
flex-direction: column; |
|||
gap: 12rpx; |
|||
background-color: #fff; |
|||
width: 90%; |
|||
margin: 0 auto 30rpx; |
|||
padding: 26rpx; |
|||
border-radius: 16rpx; |
|||
} |
|||
|
|||
.li { |
|||
padding: 30rpx 20rpx; |
|||
border: 1px solid #29D3B4; |
|||
border-radius: 18rpx; |
|||
background-color: rgba(41, 211, 180, 0.16); |
|||
font-size: 26rpx; |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
} |
|||
|
|||
.left { |
|||
display: flex; |
|||
flex-direction: column; |
|||
gap: 15rpx; |
|||
} |
|||
|
|||
.btn { |
|||
width: 110rpx; |
|||
height: 44rpx; |
|||
line-height: 44rpx; |
|||
border-radius: 8rpx; |
|||
background-color: rgba(41, 211, 180, 1); |
|||
color: rgba(255, 255, 255, 1); |
|||
font-size: 20rpx; |
|||
text-align: center; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,421 @@ |
|||
<!--授课统计-详情--> |
|||
<template> |
|||
<view class="main_box"> |
|||
|
|||
<view class="main_section"> |
|||
<view class="section"> |
|||
<view class="item"> |
|||
<image @click="previewImage(editHeadimg)" class="pic" :src="editHeadimg"></image> |
|||
<view class="btn"> |
|||
<AQUplodeImage |
|||
:uploadUrl=uploadUrl |
|||
:extraData="{ input_name: 'headimg', formData:{} }" |
|||
@uplodeImageRes="uplodeImageRes" |
|||
> |
|||
修改头像 |
|||
</AQUplodeImage> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="section"> |
|||
<view class="item"> |
|||
<view class="title"> |
|||
学员姓名 <text class="required">*</text> |
|||
</view> |
|||
<view class="input"> |
|||
<input v-model="formData.name" placeholder="请输入姓名" /> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="item"> |
|||
<view class="title"> |
|||
账号 <text class="required"></text> |
|||
</view> |
|||
<view class="input"> |
|||
<input disabled placeholder="暂无" v-model="formData.memberHasOne.mobile" /> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- <view class="item">--> |
|||
<!-- <view class="title">--> |
|||
<!-- 住址 <text class="required"></text>--> |
|||
<!-- </view>--> |
|||
<!-- <view class="input">--> |
|||
<!-- <input placeholder="暂无" v-model="formData.address" />--> |
|||
<!-- </view>--> |
|||
<!-- </view>--> |
|||
|
|||
<view class="item"> |
|||
<view class="title"> |
|||
课程 <text class="required"></text> |
|||
</view> |
|||
<view class="input"> |
|||
<input disabled placeholder="暂无" :value="formData.classes_list" /> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="section"> |
|||
<view class="item"> |
|||
<view class="title"> |
|||
性别 <text class="required">*</text> |
|||
</view> |
|||
<view class="input"> |
|||
<input placeholder="请选择性别" v-model="formData.gender_name" @click="picker_show_sex=true" /> |
|||
<fui-picker layer="1" :linkage="true" :options="options_sex_arr" :show="picker_show_sex" |
|||
@change="changePickerSex" @cancel="cancelPickerSex"></fui-picker> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="item"> |
|||
<view class="title"> |
|||
年龄 <text class="required">*</text> |
|||
</view> |
|||
<view class="input"> |
|||
<input type="number" v-model.number="formData.age" :min="3" :max="18" placeholder="请输入年龄" @input="handleAgeInput" /> </view> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="section"> |
|||
<!-- <view class="item">--> |
|||
<!-- <view class="title">--> |
|||
<!-- 邮箱 <text class="required">*</text>--> |
|||
<!-- </view>--> |
|||
<!-- <view class="input">--> |
|||
<!-- <input v-model="formData.email" placeholder="请输入邮箱" />--> |
|||
<!-- </view>--> |
|||
<!-- </view>--> |
|||
|
|||
<view class="item"> |
|||
<view class="title"> |
|||
手机 <text class="required">*</text> |
|||
</view> |
|||
<view class="input"> |
|||
<input v-model="formData.phone_number" placeholder="请输入手机" /> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
|
|||
<view class="submet_btn" @click="submit">提交</view> |
|||
</view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
import apiRoute from '@/api/apiRoute.js'; |
|||
import member from '@/api/member.js'; |
|||
import { |
|||
Api_url |
|||
} from "@/common/config.js"; |
|||
import AQTabber from "@/components/AQ/AQTabber" |
|||
import AQUplodeImage from '@/components/AQ/AQUplodeImage';//单图上传组件 |
|||
|
|||
|
|||
export default { |
|||
components: { |
|||
AQTabber, |
|||
AQUplodeImage, |
|||
}, |
|||
data() { |
|||
return { |
|||
member_info: {}, |
|||
|
|||
|
|||
//上传图片APi路径 |
|||
uploadUrl: `${Api_url}/memberUploadImage`, |
|||
|
|||
//性别选择器 相关 |
|||
picker_show_sex: false, |
|||
sex_name: '请选择', |
|||
options_sex_arr: [ |
|||
// { |
|||
// value: 1, |
|||
// text: '男' |
|||
// }, |
|||
], |
|||
|
|||
//生日选择器相关 |
|||
minDate: '', |
|||
maxDate: '', |
|||
picker_show_birthday: false, |
|||
upload_type: 1, |
|||
uploadHeadimg: '', |
|||
editHeadimg: '', |
|||
|
|||
//表单 |
|||
formData: { |
|||
headimg:'',//头像 |
|||
name:'',//姓名 |
|||
course_arr:[],//课程 |
|||
gender:'',//性别: male-男性, female-女性, other-其他 |
|||
gender_name:'',//性别: male-男性, female-女性, other-其他 |
|||
age:'',//年龄 |
|||
phone_number:'',//手机号 |
|||
}, |
|||
} |
|||
}, |
|||
onLoad() {}, |
|||
onShow() { |
|||
this.init() |
|||
}, |
|||
methods: { |
|||
|
|||
async init() { |
|||
await this.memberInfo() |
|||
await this.getSexDict() |
|||
this.getBirthday() |
|||
}, |
|||
|
|||
//获取学员信息 |
|||
async memberInfo() { |
|||
let res = await apiRoute.xy_memberInfo({}) |
|||
if(res.code != 1){ |
|||
uni.showToast({ |
|||
title: res.msg, |
|||
icon: 'none' |
|||
}) |
|||
return |
|||
} |
|||
this.formData = res.data |
|||
|
|||
this.formData.headimg = res.data.memberHasOne ? res.data.memberHasOne.headimg : $util.img('/uniapp_src/static/images/common/yong_hu.png') |
|||
this.editHeadimg = this.formData.headimg |
|||
|
|||
console.log('xq',this.formData) |
|||
}, |
|||
|
|||
//获取性别字典 |
|||
async getSexDict() { |
|||
let res = await apiRoute.common_Dictionary({key:'zy_sex'}) |
|||
if(res.code != 1){ |
|||
uni.showToast({ |
|||
title: res.msg, |
|||
icon: 'none' |
|||
}) |
|||
return |
|||
} |
|||
|
|||
let dictionary = res.data.dictionary |
|||
let arr = [] |
|||
dictionary.forEach((v,k)=>{ |
|||
arr.push({ |
|||
text: v.name, |
|||
value: v.value, |
|||
}) |
|||
}) |
|||
this.options_sex_arr = arr |
|||
}, |
|||
|
|||
//性别选择相关 |
|||
changePickerSex(e) { |
|||
console.log('监听选择', e) |
|||
this.formData.gender_name = e.text |
|||
this.formData.gender = e.value |
|||
this.cancelPickerSex() |
|||
}, |
|||
cancelPickerSex(){ |
|||
this.picker_show_sex = false |
|||
}, |
|||
|
|||
//生日选择相关 |
|||
//获取当前年月日+获取30年前的日期 |
|||
getBirthday() { |
|||
let date = new Date(); |
|||
let year = date.getFullYear(); |
|||
let month = date.getMonth() + 1; |
|||
let day = date.getDate(); |
|||
let year_30 = year - 30; |
|||
let month_30 = month; |
|||
let day_30 = day; |
|||
if (month_30 == 2 && day_30 > 28) { |
|||
month_30 = 3; |
|||
day_30 = 1; |
|||
} |
|||
if (month_30 == 4 && day_30 > 30) { |
|||
month_30 = 5; |
|||
day_30 = 1; |
|||
} |
|||
if (month_30 == 6 && day_30 > 30) { |
|||
month_30 = 7; |
|||
day_30 = 1; |
|||
} |
|||
if (month_30 == 9 && day_30 > 30) { |
|||
month_30 = 10; |
|||
day_30 = 1; |
|||
} |
|||
if (month_30 == 11 && day_30 > 30) { |
|||
month_30 = 12; |
|||
day_30 = 1; |
|||
} |
|||
if (month_30 > 12) { |
|||
month_30 = month_30 - 12; |
|||
year_30 = year_30 + 1; |
|||
} |
|||
let minDate = year_30 + "-" + month_30 + "-" + day_30 |
|||
let maxDate = year + "-" + month + "-" + day |
|||
this.minDate = minDate |
|||
this.maxDate = maxDate |
|||
}, |
|||
|
|||
//监听生日选择 |
|||
changePickerBirthday(e) { |
|||
console.log('监听生日选择', e) |
|||
this.formData.birthday = e.result |
|||
this.picker_show_birthday = false |
|||
}, |
|||
|
|||
//监听-年龄修改 |
|||
handleAgeInput(e) { |
|||
let age = e.detail.value; |
|||
if (age < 3) { |
|||
uni.showToast({ |
|||
title: '年龄不能小于3岁', |
|||
icon: 'none' |
|||
}); |
|||
this.formData.age = 3; |
|||
} else if (age > 18) { |
|||
uni.showToast({ |
|||
title: '年龄不能大于18岁', |
|||
icon: 'none' |
|||
}); |
|||
this.formData.age = 18; |
|||
} else { |
|||
this.formData.age = age; |
|||
} |
|||
}, |
|||
|
|||
|
|||
//文件上传回调 |
|||
uplodeImageRes(resData,extraData){ |
|||
console.log('上传成功回调',resData,extraData) |
|||
//判断是不是上传相册图片 |
|||
if (extraData.input_name == 'headimg') { |
|||
console.log('收到的图片地址:', resData.url); |
|||
this.editHeadimg = resData.url; |
|||
} |
|||
}, |
|||
|
|||
//图片预览 |
|||
previewImage(url){ |
|||
uni.previewImage({ |
|||
current: url, // 当前图片地址 |
|||
urls: [url] // 所有图片列表(可以是多个) |
|||
}); |
|||
}, |
|||
|
|||
//提交信息 |
|||
async submit() { |
|||
if (this.editHeadimg) { |
|||
this.formData.headimg = this.editHeadimg |
|||
} |
|||
let params = { |
|||
...this.formData |
|||
} |
|||
|
|||
let res = await apiRoute.xy_memberEdit(params) |
|||
if(res.code != 1){ |
|||
uni.showToast({ |
|||
title: res.msg, |
|||
icon: 'none' |
|||
}) |
|||
return |
|||
} |
|||
uni.showToast({ |
|||
title: res.msg, |
|||
icon: 'success' |
|||
}) |
|||
}, |
|||
|
|||
|
|||
|
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style lang="less" scoped> |
|||
.main_box { |
|||
background: #292929; |
|||
} |
|||
|
|||
//自定义导航栏 |
|||
.navbar_section { |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: center; |
|||
background: #29d3b4; |
|||
|
|||
.title { |
|||
padding: 20rpx 0; |
|||
font-size: 30rpx; |
|||
color: #315d55; |
|||
} |
|||
} |
|||
|
|||
.main_section { |
|||
min-height: 100vh; |
|||
background: #292929 100%; |
|||
padding: 0 0rpx; |
|||
padding-top: 32rpx; |
|||
padding-bottom: 150rpx; |
|||
font-size: 28rpx; |
|||
color: #fff; |
|||
display: flex; |
|||
flex-direction: column; |
|||
gap: 20rpx; |
|||
|
|||
.section { |
|||
background-color: #434544; |
|||
|
|||
.item { |
|||
padding: 20rpx 40rpx; |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
|
|||
.pic { |
|||
width: 82rpx; |
|||
height: 82rpx; |
|||
border-radius: 50%; |
|||
} |
|||
|
|||
.btn {} |
|||
|
|||
.title { |
|||
display: flex; |
|||
align-items: center; |
|||
font-size: 26rpx; |
|||
color: #D7D7D7; |
|||
|
|||
.required { |
|||
margin-left: 10rpx; |
|||
color: red; |
|||
} |
|||
} |
|||
|
|||
.input { |
|||
input { |
|||
text-align: right; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
.submet_btn { |
|||
margin: 0 auto; |
|||
margin-top: 40rpx; |
|||
border: 2px solid #25a18b; |
|||
color: #25a18b; |
|||
width: 80%; |
|||
height: 80rpx; |
|||
font-size: 30rpx; |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: center; |
|||
} |
|||
|
|||
|
|||
|
|||
} |
|||
</style> |
|||
@ -0,0 +1,57 @@ |
|||
<template> |
|||
<view class="assemble"> |
|||
<view style="height: 30rpx;"></view> |
|||
<view class="option" @click="update_pass()">修改密码</view> |
|||
<view class="option" @click="privacy_agreement(1)">用户协议</view> |
|||
<view class="option" @click="privacy_agreement(2)">隐私策略</view> |
|||
<view class="option">清空缓存</view> |
|||
|
|||
<view style="width:90%;margin: 60rpx auto;"> |
|||
<fui-button background="#29d3b4" @click="loginOut()">退出账号</fui-button> |
|||
</view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
export default { |
|||
data() { |
|||
return { |
|||
|
|||
} |
|||
}, |
|||
methods: { |
|||
//退出登陆 |
|||
loginOut(){ |
|||
this.$util.loginOut() |
|||
}, |
|||
|
|||
privacy_agreement(type){ |
|||
uni.navigateTo({ |
|||
url: '/pages-common/privacy_agreement?type='+type |
|||
}) |
|||
}, |
|||
update_pass(){ |
|||
uni.navigateTo({ |
|||
url: '/pages-student/my/update_pass' |
|||
}) |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style lang="less" scoped> |
|||
.assemble{ |
|||
width: 100%; |
|||
height: 100vh; |
|||
background: #333333; |
|||
} |
|||
.option{ |
|||
margin-bottom: 20rpx; |
|||
background: #404045; |
|||
width: 100%; |
|||
font-size: 28rpx; |
|||
color: #fff; |
|||
line-height: 56rpx; |
|||
padding: 20rpx 0 20rpx 100rpx; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,125 @@ |
|||
<template> |
|||
<view> |
|||
<view class="title"> |
|||
<view :class="{'green-text': tset_style === 1}">1.验证原密码</view> |
|||
<view :class="{'green-text': tset_style === 2}">2.设置新密码</view> |
|||
</view> |
|||
<view :style="{'background-color':'#fff','width':'100%','height':'100vh' }"> |
|||
<view style="width: 95%;height: 30rpx;"></view> |
|||
<view v-if="tset_style == 1"> |
|||
<view class="describe"> |
|||
为保障您的账号安全,修改密码前请填写原密码 |
|||
</view> |
|||
<view style="width: 95%;margin:30rpx auto;"> |
|||
<fui-input borderTop placeholder="请输入原登录密码" v-model="password" backgroundColor="#f2f2f2"></fui-input> |
|||
</view> |
|||
</view> |
|||
<view v-if="tset_style == 2"> |
|||
<view style="width: 95%;margin:30rpx auto;"> |
|||
<fui-input borderTop placeholder="请设置6-20位新的登录密码" v-model="passwords" backgroundColor="#f2f2f2"></fui-input> |
|||
</view> |
|||
<view style="width: 95%;margin: auto;"> |
|||
<fui-input borderTop :padding="['20rpx','32rpx']" v-model="old_password" placeholder="请再次输入新的登录密码" backgroundColor="#f2f2f2"> |
|||
</fui-input> |
|||
</view> |
|||
</view> |
|||
<view style="width: 95%;margin:60rpx auto;"> |
|||
<fui-button background="#00be8c" radius="5rpx" @click="nextStep" v-if="tset_style == 1">下一步</fui-button> |
|||
<fui-button background="#00be8c" radius="5rpx" @click="submit" v-if="tset_style == 2">提交</fui-button> |
|||
<view style="width: 95%;margin:60rpx auto;"> |
|||
<fui-button background="#fff" radius="5rpx" @click="forgot" color="#999999" |
|||
v-if="tset_style == 1">忘记原密码</fui-button> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
import member from '@/api/member.js'; |
|||
export default { |
|||
data() { |
|||
return { |
|||
password: '', |
|||
passwords: '', |
|||
old_password: '', |
|||
tset_style: 1, |
|||
} |
|||
}, |
|||
onLoad() { |
|||
|
|||
}, |
|||
methods: { |
|||
//验证原密码 |
|||
nextStep() { |
|||
member.is_pass({ |
|||
password: this.password |
|||
}).then(res => { |
|||
if (res.code == 1) { |
|||
this.tset_style = 2 |
|||
} else { |
|||
uni.showToast({ |
|||
title: res.msg, |
|||
icon: 'none' |
|||
}) |
|||
} |
|||
}); |
|||
}, |
|||
//提交 |
|||
submit() { |
|||
member.set_pass({ |
|||
password: this.passwords, |
|||
old_password: this.old_password, |
|||
}).then(res => { |
|||
if (res.code == 1) { |
|||
this.$util.loginOut(); |
|||
} else { |
|||
uni.showToast({ |
|||
title: res.msg, |
|||
icon: 'none' |
|||
}) |
|||
} |
|||
}); |
|||
}, |
|||
forgot() { |
|||
uni.navigateTo({ |
|||
url: '/pages-student/login/forgot' |
|||
}) |
|||
}, |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style lang="less" scoped> |
|||
page { |
|||
font-weight: normal; |
|||
} |
|||
|
|||
.fui-section__title { |
|||
margin-left: 32rpx; |
|||
} |
|||
|
|||
.fui-left__icon { |
|||
padding-right: 24rpx; |
|||
} |
|||
|
|||
.title { |
|||
display: flex; |
|||
justify-content: space-around; |
|||
align-items: center; |
|||
height: 100rpx; |
|||
width: 100%; |
|||
background-color: #fff; |
|||
font-size: 26rpx; |
|||
border: 4rpx #f5f5f5 solid; |
|||
} |
|||
|
|||
.green-text { |
|||
color: #36d6b9; |
|||
} |
|||
|
|||
.describe { |
|||
color: #999999; |
|||
padding-left: 30rpx; |
|||
} |
|||
</style> |
|||
File diff suppressed because it is too large
@ -0,0 +1,823 @@ |
|||
<!--学员体测数据页面--> |
|||
<template> |
|||
<view class="main_box"> |
|||
<!-- 自定义导航栏 --> |
|||
<view class="navbar_section"> |
|||
<view class="navbar_content"> |
|||
<view class="navbar_back" @click="goBack"> |
|||
<text class="back_icon">‹</text> |
|||
</view> |
|||
<view class="navbar_title">体测数据</view> |
|||
<view class="navbar_action"> |
|||
<view class="share_button" @click="sharePhysicalTest" v-if="physicalTestList.length > 0"> |
|||
<image src="/static/icon-img/share.png" class="share_icon"></image> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 学员基本信息 --> |
|||
<view class="student_basic_section" v-if="studentInfo"> |
|||
<view class="student_name">{{ studentInfo.name }}</view> |
|||
<view class="student_meta"> |
|||
<text class="meta_item">{{ studentInfo.gender_text }}</text> |
|||
<text class="meta_item">{{ studentInfo.age }}岁</text> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 数据统计卡片 --> |
|||
<view class="stats_section"> |
|||
<view class="stat_card"> |
|||
<view class="stat_number">{{ physicalTestList.length }}</view> |
|||
<view class="stat_label">测试次数</view> |
|||
</view> |
|||
<view class="stat_card" v-if="latestTest"> |
|||
<view class="stat_number">{{ latestTest.height }}cm</view> |
|||
<view class="stat_label">最新身高</view> |
|||
</view> |
|||
<view class="stat_card" v-if="latestTest"> |
|||
<view class="stat_number">{{ latestTest.weight }}kg</view> |
|||
<view class="stat_label">最新体重</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 体测记录列表 --> |
|||
<view class="test_list_section"> |
|||
<view v-if="loading" class="loading_section"> |
|||
<view class="loading_text">加载中...</view> |
|||
</view> |
|||
|
|||
<view v-else-if="physicalTestList.length === 0" class="empty_section"> |
|||
<view class="empty_icon">📊</view> |
|||
<view class="empty_text">暂无体测数据</view> |
|||
<view class="empty_hint">完成体测后数据会在这里显示</view> |
|||
</view> |
|||
|
|||
<view v-else class="test_list"> |
|||
<view |
|||
v-for="test in physicalTestList" |
|||
:key="test.id" |
|||
class="test_item" |
|||
> |
|||
<view class="test_header"> |
|||
<view class="test_date">{{ formatDate(test.created_at) }}</view> |
|||
<view class="test_status">体测记录</view> |
|||
</view> |
|||
|
|||
<view class="test_content"> |
|||
<view class="measurement_row"> |
|||
<view class="measurement_item"> |
|||
<view class="item_label">身高</view> |
|||
<view class="item_value">{{ test.height || '-' }}cm</view> |
|||
</view> |
|||
<view class="measurement_item"> |
|||
<view class="item_label">体重</view> |
|||
<view class="item_value">{{ test.weight || '-' }}kg</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- PDF报告列表 --> |
|||
<view class="pdf_reports" v-if="test.physical_test_report"> |
|||
<view class="reports_title">体测报告</view> |
|||
<view class="reports_list"> |
|||
<view |
|||
v-for="(pdfUrl, index) in getPdfList(test.physical_test_report)" |
|||
:key="index" |
|||
class="pdf_item" |
|||
@click="previewPdf(pdfUrl, test.id, index)" |
|||
> |
|||
<view class="pdf_icon"> |
|||
<image src="/static/icon-img/pdf.png" class="icon_image"></image> |
|||
</view> |
|||
<view class="pdf_info"> |
|||
<view class="pdf_name">体测报告{{ index + 1 }}</view> |
|||
<view class="pdf_action">点击预览</view> |
|||
</view> |
|||
<view class="pdf_arrow"> |
|||
<image src="/static/icon-img/arrow-right.png" class="arrow_icon"></image> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 加载更多 --> |
|||
<view class="load_more_section" v-if="!loading && hasMore"> |
|||
<button class="load_more_button" @click="loadMoreTests" :disabled="loadingMore"> |
|||
{{ loadingMore ? '加载中...' : '加载更多' }} |
|||
</button> |
|||
</view> |
|||
|
|||
<!-- PDF预览弹窗 --> |
|||
<view class="pdf_preview_popup" v-if="showPdfPreview" @click="closePdfPreview"> |
|||
<view class="preview_content" @click.stop> |
|||
<view class="preview_header"> |
|||
<view class="preview_title">体测报告预览</view> |
|||
<view class="preview_close" @click="closePdfPreview">×</view> |
|||
</view> |
|||
<view class="preview_body"> |
|||
<view v-if="pdfPreviewLoading" class="preview_loading"> |
|||
<view class="loading_text">加载中...</view> |
|||
</view> |
|||
<view v-else-if="pdfImageUrl" class="preview_image_container"> |
|||
<image |
|||
:src="pdfImageUrl" |
|||
class="preview_image" |
|||
mode="widthFix" |
|||
@error="onImageError" |
|||
></image> |
|||
</view> |
|||
<view v-else class="preview_error"> |
|||
<view class="error_text">预览失败</view> |
|||
<button class="retry_button" @click="retryPreview">重试</button> |
|||
</view> |
|||
</view> |
|||
<view class="preview_footer"> |
|||
<button class="share_pdf_button" @click="sharePdfImage" :disabled="!pdfImageUrl"> |
|||
分享报告 |
|||
</button> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
import apiRoute from '@/api/member.js' |
|||
|
|||
export default { |
|||
data() { |
|||
return { |
|||
studentId: 0, |
|||
studentInfo: {}, |
|||
physicalTestList: [], |
|||
loading: false, |
|||
loadingMore: false, |
|||
hasMore: true, |
|||
currentPage: 1, |
|||
showPdfPreview: false, |
|||
pdfPreviewLoading: false, |
|||
pdfImageUrl: '', |
|||
currentPdfUrl: '', |
|||
currentTestId: 0, |
|||
currentPdfIndex: 0 |
|||
} |
|||
}, |
|||
|
|||
computed: { |
|||
latestTest() { |
|||
if (this.physicalTestList.length === 0) return null |
|||
return this.physicalTestList[0] |
|||
} |
|||
}, |
|||
|
|||
onLoad(options) { |
|||
this.studentId = parseInt(options.student_id) || 0 |
|||
if (this.studentId) { |
|||
this.initPage() |
|||
} else { |
|||
uni.showToast({ |
|||
title: '参数错误', |
|||
icon: 'none' |
|||
}) |
|||
} |
|||
}, |
|||
|
|||
methods: { |
|||
goBack() { |
|||
uni.navigateBack() |
|||
}, |
|||
|
|||
async initPage() { |
|||
await this.loadStudentInfo() |
|||
await this.loadPhysicalTests() |
|||
}, |
|||
|
|||
async loadStudentInfo() { |
|||
try { |
|||
// 调用真实API获取学员信息 |
|||
const response = await apiRoute.getStudentSummary(this.studentId) |
|||
if (response.code === 1) { |
|||
this.studentInfo = response.data |
|||
} else { |
|||
console.error('获取学员信息失败:', response) |
|||
} |
|||
} catch (error) { |
|||
console.error('获取学员信息失败:', error) |
|||
} |
|||
}, |
|||
|
|||
async loadPhysicalTests() { |
|||
this.loading = true |
|||
try { |
|||
console.log('加载体测数据:', this.studentId) |
|||
|
|||
// 调用真实API获取体测数据 |
|||
const response = await apiRoute.getPhysicalTestList({ |
|||
student_id: this.studentId, |
|||
page: this.currentPage, |
|||
limit: 10 |
|||
}) |
|||
|
|||
if (response.code === 1) { |
|||
const newList = response.data.list || [] |
|||
if (this.currentPage === 1) { |
|||
this.physicalTestList = newList |
|||
} else { |
|||
this.physicalTestList = [...this.physicalTestList, ...newList] |
|||
} |
|||
|
|||
this.hasMore = response.data.has_more || false |
|||
console.log('体测数据加载成功:', this.physicalTestList) |
|||
} else { |
|||
uni.showToast({ |
|||
title: response.msg || '获取体测数据失败', |
|||
icon: 'none' |
|||
}) |
|||
} |
|||
} catch (error) { |
|||
console.error('获取体测数据失败:', error) |
|||
uni.showToast({ |
|||
title: '获取体测数据失败', |
|||
icon: 'none' |
|||
}) |
|||
} finally { |
|||
this.loading = false |
|||
this.loadingMore = false |
|||
} |
|||
}, |
|||
|
|||
async loadMoreTests() { |
|||
if (this.loadingMore || !this.hasMore) return |
|||
|
|||
this.loadingMore = true |
|||
this.currentPage++ |
|||
await this.loadPhysicalTests() |
|||
}, |
|||
|
|||
getPdfList(pdfReportString) { |
|||
if (!pdfReportString || pdfReportString.trim() === '') { |
|||
return [] |
|||
} |
|||
|
|||
// 按逗号分割PDF URL列表,并过滤空值 |
|||
return pdfReportString.split(',').filter(url => url.trim() !== '') |
|||
}, |
|||
|
|||
async previewPdf(pdfUrl, testId, pdfIndex) { |
|||
console.log('预览PDF:', pdfUrl) |
|||
|
|||
this.currentPdfUrl = pdfUrl |
|||
this.currentTestId = testId |
|||
this.currentPdfIndex = pdfIndex |
|||
this.showPdfPreview = true |
|||
this.pdfPreviewLoading = true |
|||
this.pdfImageUrl = '' |
|||
|
|||
try { |
|||
// 调用后端API将PDF转换为图片 |
|||
const response = await apiRoute.convertPdfToImage({ |
|||
pdf_url: pdfUrl, |
|||
test_id: testId, |
|||
pdf_index: pdfIndex |
|||
}) |
|||
|
|||
if (response.code === 1) { |
|||
this.pdfImageUrl = response.data.image_url |
|||
} else { |
|||
throw new Error(response.msg || 'PDF转换失败') |
|||
} |
|||
} catch (error) { |
|||
console.error('PDF预览失败:', error) |
|||
uni.showToast({ |
|||
title: error.message || 'PDF预览失败', |
|||
icon: 'none' |
|||
}) |
|||
} finally { |
|||
this.pdfPreviewLoading = false |
|||
} |
|||
}, |
|||
|
|||
closePdfPreview() { |
|||
this.showPdfPreview = false |
|||
this.pdfImageUrl = '' |
|||
this.currentPdfUrl = '' |
|||
}, |
|||
|
|||
retryPreview() { |
|||
this.previewPdf(this.currentPdfUrl, this.currentTestId, this.currentPdfIndex) |
|||
}, |
|||
|
|||
onImageError() { |
|||
console.error('图片加载失败') |
|||
uni.showToast({ |
|||
title: '图片加载失败', |
|||
icon: 'none' |
|||
}) |
|||
}, |
|||
|
|||
async sharePdfImage() { |
|||
if (!this.pdfImageUrl) { |
|||
uni.showToast({ |
|||
title: '没有可分享的内容', |
|||
icon: 'none' |
|||
}) |
|||
return |
|||
} |
|||
|
|||
try { |
|||
uni.showActionSheet({ |
|||
itemList: ['保存到相册', '分享给朋友'], |
|||
success: async (res) => { |
|||
if (res.tapIndex === 0) { |
|||
// 保存到相册 |
|||
try { |
|||
await uni.saveImageToPhotosAlbum({ |
|||
filePath: this.pdfImageUrl |
|||
}) |
|||
uni.showToast({ |
|||
title: '已保存到相册', |
|||
icon: 'success' |
|||
}) |
|||
} catch (error) { |
|||
console.error('保存图片失败:', error) |
|||
uni.showToast({ |
|||
title: '保存失败', |
|||
icon: 'none' |
|||
}) |
|||
} |
|||
} else if (res.tapIndex === 1) { |
|||
// 分享功能 |
|||
uni.showToast({ |
|||
title: '分享功能开发中', |
|||
icon: 'none' |
|||
}) |
|||
} |
|||
} |
|||
}) |
|||
} catch (error) { |
|||
console.error('分享操作失败:', error) |
|||
} |
|||
}, |
|||
|
|||
formatDate(dateString) { |
|||
if (!dateString) return '-' |
|||
|
|||
const date = new Date(dateString) |
|||
const year = date.getFullYear() |
|||
const month = String(date.getMonth() + 1).padStart(2, '0') |
|||
const day = String(date.getDate()).padStart(2, '0') |
|||
const hours = String(date.getHours()).padStart(2, '0') |
|||
const minutes = String(date.getMinutes()).padStart(2, '0') |
|||
|
|||
return `${year}-${month}-${day} ${hours}:${minutes}` |
|||
}, |
|||
|
|||
async sharePhysicalTest() { |
|||
try { |
|||
uni.showLoading({ |
|||
title: '生成分享图片...' |
|||
}) |
|||
|
|||
// 调用API生成分享图片 |
|||
const response = await apiRoute.generateShareImage({ |
|||
student_id: this.studentId, |
|||
type: 'physical_test' |
|||
}) |
|||
|
|||
if (response.code === 1) { |
|||
uni.hideLoading() |
|||
uni.showActionSheet({ |
|||
itemList: ['保存到相册', '分享给朋友'], |
|||
success: async (res) => { |
|||
if (res.tapIndex === 0) { |
|||
try { |
|||
await uni.saveImageToPhotosAlbum({ |
|||
filePath: response.data.image_url |
|||
}) |
|||
uni.showToast({ |
|||
title: '已保存到相册', |
|||
icon: 'success' |
|||
}) |
|||
} catch (error) { |
|||
uni.showToast({ |
|||
title: '保存失败', |
|||
icon: 'none' |
|||
}) |
|||
} |
|||
} else if (res.tapIndex === 1) { |
|||
uni.showToast({ |
|||
title: '分享功能开发中', |
|||
icon: 'none' |
|||
}) |
|||
} |
|||
} |
|||
}) |
|||
} else { |
|||
throw new Error(response.msg || '生成分享图片失败') |
|||
} |
|||
} catch (error) { |
|||
console.error('分享失败:', error) |
|||
uni.hideLoading() |
|||
uni.showToast({ |
|||
title: error.message || '分享失败', |
|||
icon: 'none' |
|||
}) |
|||
} |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style lang="less" scoped> |
|||
.main_box { |
|||
background: #f8f9fa; |
|||
min-height: 100vh; |
|||
} |
|||
|
|||
// 自定义导航栏 |
|||
.navbar_section { |
|||
background: linear-gradient(135deg, #29D3B4 0%, #1BA297 100%); |
|||
padding: 40rpx 32rpx 32rpx; |
|||
|
|||
// 小程序端适配状态栏 |
|||
// #ifdef MP-WEIXIN |
|||
padding-top: 80rpx; |
|||
// #endif |
|||
.navbar_back { |
|||
width: 60rpx; |
|||
|
|||
.back_icon { |
|||
color: #fff; |
|||
font-size: 40rpx; |
|||
font-weight: 600; |
|||
} |
|||
} |
|||
.navbar_content { |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: space-between; |
|||
|
|||
.back_button { |
|||
width: 40rpx; |
|||
height: 40rpx; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
|
|||
.back_icon { |
|||
width: 24rpx; |
|||
height: 24rpx; |
|||
} |
|||
} |
|||
|
|||
.navbar_title { |
|||
color: #fff; |
|||
font-size: 36rpx; |
|||
font-weight: 600; |
|||
} |
|||
|
|||
.navbar_action { |
|||
width: 40rpx; |
|||
height: 40rpx; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
|
|||
.share_button { |
|||
background: rgba(255, 255, 255, 0.2); |
|||
padding: 12rpx; |
|||
border-radius: 50%; |
|||
|
|||
.share_icon { |
|||
width: 20rpx; |
|||
height: 20rpx; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
// 学员基本信息 |
|||
.student_basic_section { |
|||
background: #fff; |
|||
padding: 24rpx 32rpx; |
|||
border-bottom: 1px solid #f0f0f0; |
|||
|
|||
.student_name { |
|||
font-size: 32rpx; |
|||
font-weight: 600; |
|||
color: #333; |
|||
margin-bottom: 12rpx; |
|||
} |
|||
|
|||
.student_meta { |
|||
display: flex; |
|||
gap: 16rpx; |
|||
|
|||
.meta_item { |
|||
font-size: 24rpx; |
|||
color: #666; |
|||
background: #f0f0f0; |
|||
padding: 6rpx 12rpx; |
|||
border-radius: 12rpx; |
|||
} |
|||
} |
|||
} |
|||
|
|||
// 统计卡片 |
|||
.stats_section { |
|||
display: flex; |
|||
background: #fff; |
|||
margin: 20rpx; |
|||
border-radius: 16rpx; |
|||
padding: 32rpx 24rpx; |
|||
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.08); |
|||
|
|||
.stat_card { |
|||
flex: 1; |
|||
text-align: center; |
|||
|
|||
.stat_number { |
|||
font-size: 36rpx; |
|||
font-weight: 600; |
|||
color: #29D3B4; |
|||
margin-bottom: 8rpx; |
|||
} |
|||
|
|||
.stat_label { |
|||
font-size: 24rpx; |
|||
color: #666; |
|||
} |
|||
} |
|||
} |
|||
|
|||
// 体测记录列表 |
|||
.test_list_section { |
|||
margin: 0 20rpx; |
|||
|
|||
.loading_section, .empty_section { |
|||
background: #fff; |
|||
border-radius: 16rpx; |
|||
padding: 80rpx 32rpx; |
|||
text-align: center; |
|||
|
|||
.loading_text, .empty_text { |
|||
font-size: 28rpx; |
|||
color: #666; |
|||
margin-bottom: 12rpx; |
|||
} |
|||
|
|||
.empty_icon { |
|||
font-size: 80rpx; |
|||
margin-bottom: 24rpx; |
|||
} |
|||
|
|||
.empty_hint { |
|||
font-size: 24rpx; |
|||
color: #999; |
|||
} |
|||
} |
|||
|
|||
.test_list { |
|||
.test_item { |
|||
background: #fff; |
|||
border-radius: 16rpx; |
|||
padding: 32rpx; |
|||
margin-bottom: 20rpx; |
|||
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.08); |
|||
|
|||
.test_header { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
margin-bottom: 24rpx; |
|||
|
|||
.test_date { |
|||
font-size: 28rpx; |
|||
color: #333; |
|||
font-weight: 600; |
|||
} |
|||
|
|||
.test_status { |
|||
font-size: 24rpx; |
|||
color: #29D3B4; |
|||
background: rgba(41, 211, 180, 0.1); |
|||
padding: 6rpx 16rpx; |
|||
border-radius: 12rpx; |
|||
} |
|||
} |
|||
|
|||
.test_content { |
|||
.measurement_row { |
|||
display: flex; |
|||
gap: 32rpx; |
|||
margin-bottom: 24rpx; |
|||
|
|||
.measurement_item { |
|||
flex: 1; |
|||
text-align: center; |
|||
background: #f8f9fa; |
|||
padding: 24rpx; |
|||
border-radius: 12rpx; |
|||
|
|||
.item_label { |
|||
font-size: 24rpx; |
|||
color: #666; |
|||
margin-bottom: 8rpx; |
|||
} |
|||
|
|||
.item_value { |
|||
font-size: 32rpx; |
|||
color: #333; |
|||
font-weight: 600; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.pdf_reports { |
|||
.reports_title { |
|||
font-size: 26rpx; |
|||
color: #333; |
|||
font-weight: 600; |
|||
margin-bottom: 16rpx; |
|||
} |
|||
|
|||
.reports_list { |
|||
.pdf_item { |
|||
display: flex; |
|||
align-items: center; |
|||
gap: 20rpx; |
|||
padding: 20rpx; |
|||
background: #f8f9fa; |
|||
border-radius: 12rpx; |
|||
margin-bottom: 12rpx; |
|||
|
|||
&:last-child { |
|||
margin-bottom: 0; |
|||
} |
|||
|
|||
.pdf_icon { |
|||
width: 60rpx; |
|||
height: 60rpx; |
|||
background: rgba(231, 76, 60, 0.1); |
|||
border-radius: 12rpx; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
|
|||
.icon_image { |
|||
width: 32rpx; |
|||
height: 32rpx; |
|||
} |
|||
} |
|||
|
|||
.pdf_info { |
|||
flex: 1; |
|||
|
|||
.pdf_name { |
|||
font-size: 26rpx; |
|||
color: #333; |
|||
font-weight: 500; |
|||
margin-bottom: 4rpx; |
|||
} |
|||
|
|||
.pdf_action { |
|||
font-size: 22rpx; |
|||
color: #666; |
|||
} |
|||
} |
|||
|
|||
.pdf_arrow { |
|||
.arrow_icon { |
|||
width: 16rpx; |
|||
height: 16rpx; |
|||
opacity: 0.6; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
// 加载更多 |
|||
.load_more_section { |
|||
padding: 40rpx 20rpx 80rpx; |
|||
|
|||
.load_more_button { |
|||
width: 100%; |
|||
background: #f8f9fa; |
|||
color: #666; |
|||
border: 1px solid #e9ecef; |
|||
border-radius: 16rpx; |
|||
padding: 24rpx 0; |
|||
font-size: 28rpx; |
|||
|
|||
&:disabled { |
|||
opacity: 0.6; |
|||
} |
|||
} |
|||
} |
|||
|
|||
// PDF预览弹窗 |
|||
.pdf_preview_popup { |
|||
position: fixed; |
|||
top: 0; |
|||
left: 0; |
|||
right: 0; |
|||
bottom: 0; |
|||
background: rgba(0, 0, 0, 0.5); |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: center; |
|||
z-index: 1000; |
|||
|
|||
.preview_content { |
|||
background: #fff; |
|||
border-radius: 16rpx; |
|||
width: 90%; |
|||
max-height: 80vh; |
|||
overflow: hidden; |
|||
|
|||
.preview_header { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
padding: 32rpx; |
|||
border-bottom: 1px solid #f0f0f0; |
|||
|
|||
.preview_title { |
|||
font-size: 32rpx; |
|||
font-weight: 600; |
|||
color: #333; |
|||
} |
|||
|
|||
.preview_close { |
|||
font-size: 48rpx; |
|||
color: #999; |
|||
font-weight: 300; |
|||
} |
|||
} |
|||
|
|||
.preview_body { |
|||
max-height: 60vh; |
|||
overflow-y: auto; |
|||
|
|||
.preview_loading, .preview_error { |
|||
padding: 80rpx 32rpx; |
|||
text-align: center; |
|||
|
|||
.loading_text, .error_text { |
|||
font-size: 28rpx; |
|||
color: #666; |
|||
margin-bottom: 24rpx; |
|||
} |
|||
|
|||
.retry_button { |
|||
background: #29D3B4; |
|||
color: #fff; |
|||
border: none; |
|||
border-radius: 12rpx; |
|||
padding: 16rpx 32rpx; |
|||
font-size: 26rpx; |
|||
} |
|||
} |
|||
|
|||
.preview_image_container { |
|||
padding: 20rpx; |
|||
|
|||
.preview_image { |
|||
width: 100%; |
|||
border-radius: 12rpx; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.preview_footer { |
|||
padding: 32rpx; |
|||
border-top: 1px solid #f0f0f0; |
|||
|
|||
.share_pdf_button { |
|||
width: 100%; |
|||
background: linear-gradient(135deg, #29D3B4 0%, #1BA297 100%); |
|||
color: #fff; |
|||
border: none; |
|||
border-radius: 16rpx; |
|||
padding: 24rpx 0; |
|||
font-size: 28rpx; |
|||
font-weight: 600; |
|||
|
|||
&:disabled { |
|||
opacity: 0.6; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
</style> |
|||
@ -0,0 +1,605 @@ |
|||
<!--学员个人信息管理页面--> |
|||
<template> |
|||
<view class="main_box"> |
|||
<!-- 自定义导航栏 --> |
|||
<view class="navbar_section"> |
|||
<view class="navbar_back" @click="goBack"> |
|||
<text class="back_icon">‹</text> |
|||
</view> |
|||
<view class="navbar_title">个人信息管理</view> |
|||
<view class="navbar_action"></view> |
|||
</view> |
|||
|
|||
<!-- 学员头像区域 --> |
|||
<view class="avatar_section"> |
|||
<view class="avatar_container"> |
|||
<image |
|||
:src="studentInfo.headimg || '/static/default-avatar.png'" |
|||
class="avatar_image" |
|||
mode="aspectFill" |
|||
@click="uploadAvatar" |
|||
></image> |
|||
<view class="avatar_edit_icon" @click="uploadAvatar"> |
|||
<text class="edit_text">✎</text> |
|||
</view> |
|||
</view> |
|||
<view class="student_name">{{ studentInfo.name || '学员姓名' }}</view> |
|||
<view class="student_basic_info"> |
|||
<text class="info_tag">{{ studentInfo.gender_text }}</text> |
|||
<text class="info_tag">{{ studentInfo.ageText }}岁</text> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 基本信息表单 --> |
|||
<view class="form_section"> |
|||
<view class="section_title">基本信息</view> |
|||
|
|||
<view class="form_item"> |
|||
<view class="form_label">姓名</view> |
|||
<fui-input |
|||
v-model="formData.name" |
|||
placeholder="请输入学员姓名" |
|||
borderColor="transparent" |
|||
backgroundColor="#f8f9fa" |
|||
:maxlength="20" |
|||
></fui-input> |
|||
</view> |
|||
|
|||
<view class="form_item"> |
|||
<view class="form_label">性别</view> |
|||
<fui-input |
|||
v-model="genderText" |
|||
placeholder="请选择性别" |
|||
borderColor="transparent" |
|||
backgroundColor="#f8f9fa" |
|||
readonly |
|||
@click="showGenderPicker = true" |
|||
> |
|||
<fui-icon name="arrowdown" color="#B2B2B2" :size="40"></fui-icon> |
|||
</fui-input> |
|||
<fui-picker |
|||
:options="genderOptions" |
|||
:show="showGenderPicker" |
|||
@change="changeGender" |
|||
@cancel="showGenderPicker = false" |
|||
></fui-picker> |
|||
</view> |
|||
|
|||
<view class="form_item"> |
|||
<view class="form_label">生日</view> |
|||
<fui-date-picker |
|||
:show="showDatePicker" |
|||
v-model="formData.birthday" |
|||
@change="changeBirthday" |
|||
@cancel="showDatePicker = false" |
|||
> |
|||
<fui-input |
|||
:value="birthdayText" |
|||
placeholder="请选择生日" |
|||
borderColor="transparent" |
|||
backgroundColor="#f8f9fa" |
|||
readonly |
|||
@click="showDatePicker = true" |
|||
> |
|||
<fui-icon name="arrowdown" color="#B2B2B2" :size="40"></fui-icon> |
|||
</fui-input> |
|||
</fui-date-picker> |
|||
</view> |
|||
|
|||
<view class="form_item"> |
|||
<view class="form_label">年龄</view> |
|||
<fui-input |
|||
:value="ageText" |
|||
placeholder="根据生日自动计算" |
|||
borderColor="transparent" |
|||
backgroundColor="#f0f0f0" |
|||
readonly |
|||
></fui-input> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 体测信息(只读展示) --> |
|||
<view class="form_section"> |
|||
<view class="section_title">体测信息</view> |
|||
|
|||
<view class="form_item"> |
|||
<view class="form_label">身高 (cm)</view> |
|||
<fui-input |
|||
:value="physicalTestInfo.height || '暂无数据'" |
|||
placeholder="暂无体测数据" |
|||
borderColor="transparent" |
|||
backgroundColor="#f0f0f0" |
|||
readonly |
|||
></fui-input> |
|||
</view> |
|||
|
|||
<view class="form_item"> |
|||
<view class="form_label">体重 (kg)</view> |
|||
<fui-input |
|||
:value="physicalTestInfo.weight || '暂无数据'" |
|||
placeholder="暂无体测数据" |
|||
borderColor="transparent" |
|||
backgroundColor="#f0f0f0" |
|||
readonly |
|||
></fui-input> |
|||
</view> |
|||
|
|||
<view class="form_item" v-if="physicalTestInfo.test_date"> |
|||
<view class="form_label">体测日期</view> |
|||
<fui-input |
|||
:value="physicalTestInfo.test_date" |
|||
placeholder="暂无体测数据" |
|||
borderColor="transparent" |
|||
backgroundColor="#f0f0f0" |
|||
readonly |
|||
></fui-input> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 联系信息 --> |
|||
<view class="form_section"> |
|||
<view class="section_title">紧急联系人</view> |
|||
|
|||
<view class="form_item"> |
|||
<view class="form_label">联系人姓名</view> |
|||
<fui-input |
|||
v-model="formData.emergency_contact" |
|||
placeholder="请输入紧急联系人姓名" |
|||
borderColor="transparent" |
|||
backgroundColor="#f8f9fa" |
|||
:maxlength="20" |
|||
></fui-input> |
|||
</view> |
|||
|
|||
<view class="form_item"> |
|||
<view class="form_label">联系电话</view> |
|||
<fui-input |
|||
v-model="formData.contact_phone" |
|||
placeholder="请输入联系电话" |
|||
borderColor="transparent" |
|||
backgroundColor="#f8f9fa" |
|||
type="number" |
|||
:maxlength="11" |
|||
></fui-input> |
|||
</view> |
|||
|
|||
<view class="form_item"> |
|||
<view class="form_label">备注信息</view> |
|||
<fui-textarea |
|||
v-model="formData.note" |
|||
placeholder="其他需要说明的信息" |
|||
backgroundColor="#f8f9fa" |
|||
:maxlength="500" |
|||
:rows="4" |
|||
></fui-textarea> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 保存按钮 --> |
|||
<view class="save_section"> |
|||
<fui-button |
|||
background="#29d3b4" |
|||
radius="12rpx" |
|||
:loading="saving" |
|||
@click="saveStudentInfo" |
|||
> |
|||
{{ saving ? '保存中...' : '保存信息' }} |
|||
</fui-button> |
|||
</view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
import apiRoute from '@/api/member.js' |
|||
|
|||
export default { |
|||
data() { |
|||
return { |
|||
studentId: 0, |
|||
studentInfo: {}, |
|||
physicalTestInfo: {}, // 体测信息 |
|||
formData: { |
|||
name: '', |
|||
gender: '', |
|||
birthday: '', |
|||
emergency_contact: '', |
|||
contact_phone: '', |
|||
note: '' |
|||
}, |
|||
saving: false, |
|||
showGenderPicker: false, |
|||
showDatePicker: false, |
|||
genderOptions: [ |
|||
{ value: '1', text: '男' }, |
|||
{ value: '2', text: '女' } |
|||
] |
|||
} |
|||
}, |
|||
computed: { |
|||
genderText() { |
|||
const gender = this.genderOptions.find(item => item.value === this.formData.gender) |
|||
return gender ? gender.text : '' |
|||
}, |
|||
birthdayText() { |
|||
return this.formData.birthday || '' |
|||
}, |
|||
ageText() { |
|||
if (!this.formData.birthday) return '' |
|||
|
|||
const today = new Date() |
|||
const birthDate = new Date(this.formData.birthday) |
|||
let age = today.getFullYear() - birthDate.getFullYear() |
|||
let months = today.getMonth() - birthDate.getMonth() |
|||
|
|||
if (months < 0 || (months === 0 && today.getDate() < birthDate.getDate())) { |
|||
age-- |
|||
months += 12 |
|||
} |
|||
|
|||
if (months < 0) { |
|||
months = 0 |
|||
} |
|||
|
|||
return age > 0 ? `${age}.${months.toString().padStart(2, '0')}岁` : `${months}个月` |
|||
} |
|||
}, |
|||
onLoad(options) { |
|||
this.studentId = parseInt(options.student_id) || 0 |
|||
if (this.studentId) { |
|||
this.loadStudentInfo() |
|||
} else { |
|||
uni.showToast({ |
|||
title: '参数错误', |
|||
icon: 'none' |
|||
}) |
|||
} |
|||
}, |
|||
|
|||
methods: { |
|||
goBack() { |
|||
uni.navigateBack() |
|||
}, |
|||
|
|||
async loadStudentInfo() { |
|||
try { |
|||
console.log('加载学员信息:', this.studentId) |
|||
|
|||
// 调用真实API |
|||
const response = await apiRoute.getStudentInfo(this.studentId) |
|||
console.log('学员信息API响应:', response) |
|||
|
|||
if (response.code === 1) { |
|||
this.studentInfo = response.data.student_info |
|||
this.physicalTestInfo = response.data.physical_test_info || {} |
|||
|
|||
// 填充表单数据 |
|||
this.formData = { |
|||
name: this.studentInfo.name || '', |
|||
gender: String(this.studentInfo.gender || ''), |
|||
birthday: this.studentInfo.birthday || '', |
|||
emergency_contact: this.studentInfo.emergency_contact || '', |
|||
contact_phone: this.studentInfo.contact_phone || '', |
|||
note: this.studentInfo.note || '' |
|||
} |
|||
console.log('学员信息加载成功:', this.studentInfo) |
|||
console.log('体测信息加载成功:', this.physicalTestInfo) |
|||
} else { |
|||
uni.showToast({ |
|||
title: response.msg || '获取学员信息失败', |
|||
icon: 'none' |
|||
}) |
|||
} |
|||
} catch (error) { |
|||
console.error('获取学员信息失败:', error) |
|||
uni.showToast({ |
|||
title: '获取学员信息失败', |
|||
icon: 'none' |
|||
}) |
|||
} |
|||
}, |
|||
|
|||
changeGender(e) { |
|||
this.formData.gender = e.value |
|||
this.showGenderPicker = false |
|||
}, |
|||
|
|||
changeBirthday(e) { |
|||
this.formData.birthday = e.result |
|||
this.showDatePicker = false |
|||
}, |
|||
|
|||
async uploadAvatar() { |
|||
try { |
|||
uni.chooseImage({ |
|||
count: 1, |
|||
sizeType: ['compressed'], |
|||
sourceType: ['album', 'camera'], |
|||
success: async (res) => { |
|||
const tempFilePath = res.tempFilePaths[0] |
|||
console.log('选择的图片:', tempFilePath) |
|||
|
|||
// 显示上传中提示 |
|||
uni.showLoading({ |
|||
title: '上传中...' |
|||
}) |
|||
|
|||
try { |
|||
// 调用头像上传API(无需token验证) |
|||
const response = await apiRoute.uploadAvatarForAdd(tempFilePath) |
|||
console.log('头像上传API响应:', response) |
|||
|
|||
if (response.code === 1 && response.data && response.data.url) { |
|||
// 更新本地头像显示 |
|||
this.studentInfo.headimg = response.data.url |
|||
|
|||
// 立即保存头像到数据库 |
|||
await this.saveAvatarToDatabase(response.data.url) |
|||
|
|||
uni.showToast({ |
|||
title: '头像上传成功', |
|||
icon: 'success' |
|||
}) |
|||
} else { |
|||
throw new Error(response.msg || '上传接口返回数据异常') |
|||
} |
|||
} catch (uploadError) { |
|||
console.error('头像上传失败:', uploadError) |
|||
uni.showToast({ |
|||
title: uploadError.message || '头像上传失败', |
|||
icon: 'none' |
|||
}) |
|||
} finally { |
|||
uni.hideLoading() |
|||
} |
|||
}, |
|||
fail: (error) => { |
|||
console.error('选择图片失败:', error) |
|||
uni.showToast({ |
|||
title: '选择图片失败', |
|||
icon: 'none' |
|||
}) |
|||
} |
|||
}) |
|||
} catch (error) { |
|||
console.error('选择头像失败:', error) |
|||
uni.showToast({ |
|||
title: '操作失败', |
|||
icon: 'none' |
|||
}) |
|||
} |
|||
}, |
|||
|
|||
async saveAvatarToDatabase(avatarUrl) { |
|||
try { |
|||
// 包含完整的必要字段,特别是name字段 |
|||
const updateData = { |
|||
student_id: this.studentId, |
|||
name: this.formData.name || this.studentInfo.name, |
|||
gender: this.formData.gender || String(this.studentInfo.gender || ''), |
|||
headimg: avatarUrl |
|||
} |
|||
|
|||
console.log('保存头像到数据库:', updateData) |
|||
|
|||
// 调用更新学员信息API |
|||
const response = await apiRoute.updateStudentInfo(updateData) |
|||
console.log('保存头像API响应:', response) |
|||
|
|||
if (response.code !== 1) { |
|||
throw new Error(response.msg || '保存头像失败') |
|||
} |
|||
} catch (error) { |
|||
console.error('保存头像到数据库失败:', error) |
|||
throw error |
|||
} |
|||
}, |
|||
|
|||
async saveStudentInfo() { |
|||
// 表单验证 |
|||
if (!this.formData.name.trim()) { |
|||
uni.showToast({ |
|||
title: '请输入学员姓名', |
|||
icon: 'none' |
|||
}) |
|||
return |
|||
} |
|||
|
|||
if (!this.formData.gender) { |
|||
uni.showToast({ |
|||
title: '请选择性别', |
|||
icon: 'none' |
|||
}) |
|||
return |
|||
} |
|||
|
|||
// 手机号验证 |
|||
if (this.formData.contact_phone && !/^1[3-9]\d{9}$/.test(this.formData.contact_phone)) { |
|||
uni.showToast({ |
|||
title: '请输入正确的手机号', |
|||
icon: 'none' |
|||
}) |
|||
return |
|||
} |
|||
|
|||
this.saving = true |
|||
try { |
|||
const updateData = { |
|||
student_id: this.studentId, |
|||
...this.formData |
|||
} |
|||
|
|||
console.log('保存学员信息:', updateData) |
|||
|
|||
// 调用真实API |
|||
const response = await apiRoute.updateStudentInfo(updateData) |
|||
console.log('更新学员信息API响应:', response) |
|||
|
|||
if (response.code === 1) { |
|||
// 更新本地学员信息 |
|||
Object.assign(this.studentInfo, this.formData) |
|||
|
|||
uni.showToast({ |
|||
title: '保存成功', |
|||
icon: 'success' |
|||
}) |
|||
|
|||
// 延迟返回上一页 |
|||
setTimeout(() => { |
|||
uni.navigateBack() |
|||
}, 1500) |
|||
} else { |
|||
uni.showToast({ |
|||
title: response.message || '保存失败', |
|||
icon: 'none' |
|||
}) |
|||
} |
|||
} catch (error) { |
|||
console.error('保存学员信息失败:', error) |
|||
uni.showToast({ |
|||
title: '保存失败,请重试', |
|||
icon: 'none' |
|||
}) |
|||
} finally { |
|||
this.saving = false |
|||
} |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style lang="less" scoped> |
|||
.main_box { |
|||
background: #f8f9fa; |
|||
min-height: 100vh; |
|||
} |
|||
|
|||
// 自定义导航栏 |
|||
.navbar_section { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
background: #29D3B4; |
|||
padding: 40rpx 32rpx 20rpx; |
|||
|
|||
// 小程序端适配状态栏 |
|||
// #ifdef MP-WEIXIN |
|||
padding-top: 80rpx; |
|||
// #endif |
|||
|
|||
.navbar_back { |
|||
width: 60rpx; |
|||
|
|||
.back_icon { |
|||
color: #fff; |
|||
font-size: 40rpx; |
|||
font-weight: 600; |
|||
} |
|||
} |
|||
|
|||
.navbar_title { |
|||
color: #fff; |
|||
font-size: 32rpx; |
|||
font-weight: 600; |
|||
} |
|||
|
|||
.navbar_action { |
|||
width: 60rpx; |
|||
} |
|||
} |
|||
|
|||
// 头像区域 |
|||
.avatar_section { |
|||
background: #fff; |
|||
padding: 40rpx 32rpx; |
|||
text-align: center; |
|||
|
|||
.avatar_container { |
|||
position: relative; |
|||
display: inline-block; |
|||
margin-bottom: 24rpx; |
|||
|
|||
.avatar_image { |
|||
width: 120rpx; |
|||
height: 120rpx; |
|||
border-radius: 50%; |
|||
border: 4rpx solid #f0f0f0; |
|||
} |
|||
|
|||
.avatar_edit_icon { |
|||
position: absolute; |
|||
bottom: 0; |
|||
right: 0; |
|||
width: 40rpx; |
|||
height: 40rpx; |
|||
background: #29d3b4; |
|||
border-radius: 50%; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
border: 2rpx solid #fff; |
|||
|
|||
.edit_text { |
|||
color: #fff; |
|||
font-size: 20rpx; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.student_name { |
|||
font-size: 32rpx; |
|||
font-weight: 600; |
|||
color: #333; |
|||
margin-bottom: 12rpx; |
|||
} |
|||
|
|||
.student_basic_info { |
|||
.info_tag { |
|||
display: inline-block; |
|||
font-size: 22rpx; |
|||
color: #666; |
|||
background: #f0f0f0; |
|||
padding: 6rpx 16rpx; |
|||
border-radius: 16rpx; |
|||
margin: 0 8rpx; |
|||
} |
|||
} |
|||
} |
|||
|
|||
// 表单区域 |
|||
.form_section { |
|||
background: #fff; |
|||
margin: 20rpx; |
|||
border-radius: 16rpx; |
|||
padding: 32rpx; |
|||
|
|||
.section_title { |
|||
font-size: 28rpx; |
|||
font-weight: 600; |
|||
color: #333; |
|||
margin-bottom: 32rpx; |
|||
padding-bottom: 16rpx; |
|||
border-bottom: 1px solid #f0f0f0; |
|||
} |
|||
|
|||
.form_item { |
|||
margin-bottom: 32rpx; |
|||
|
|||
&:last-child { |
|||
margin-bottom: 0; |
|||
} |
|||
|
|||
.form_label { |
|||
font-size: 26rpx; |
|||
color: #333; |
|||
margin-bottom: 12rpx; |
|||
font-weight: 500; |
|||
} |
|||
} |
|||
} |
|||
|
|||
// 保存按钮区域 |
|||
.save_section { |
|||
padding: 40rpx 32rpx; |
|||
padding-bottom: 80rpx; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,933 @@ |
|||
<!--学员课程安排页面--> |
|||
<template> |
|||
<view class="main_box"> |
|||
<!-- 自定义导航栏 --> |
|||
<view class="navbar_section"> |
|||
<view class="navbar_back" @click="goBack"> |
|||
<text class="back_icon">‹</text> |
|||
</view> |
|||
<view class="navbar_title">课程安排</view> |
|||
<view class="navbar_action"> |
|||
<view class="today_button" @click="goToToday"> |
|||
<text class="today_text">今天</text> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 学员信息 --> |
|||
<view class="student_info_section" v-if="studentInfo"> |
|||
<view class="student_name">{{ studentInfo.name }}</view> |
|||
<view class="schedule_stats"> |
|||
<text class="stat_item">本周课程:{{ weeklyStats.total_courses }}节</text> |
|||
<text class="stat_item">已完成:{{ weeklyStats.completed_courses }}节</text> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 日历切换 --> |
|||
<view class="calendar_section"> |
|||
<view class="calendar_header"> |
|||
<view class="month_controls"> |
|||
<view class="control_button" @click="prevWeek">‹</view> |
|||
<view class="current_period">{{ currentWeekText }}</view> |
|||
<view class="control_button" @click="nextWeek">›</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="week_tabs"> |
|||
<view |
|||
v-for="day in weekDays" |
|||
:key="day.date" |
|||
:class="['week_tab', selectedDate === day.date ? 'active' : '', day.isToday ? 'today' : '']" |
|||
@click="selectDate(day.date)" |
|||
> |
|||
<view class="tab_weekday">{{ day.weekday }}</view> |
|||
<view class="tab_date">{{ day.day }}</view> |
|||
<view class="tab_indicator" v-if="day.hasCourse"></view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 课程列表 --> |
|||
<view class="courses_section"> |
|||
<view v-if="loading" class="loading_section"> |
|||
<view class="loading_text">加载中...</view> |
|||
</view> |
|||
|
|||
<view v-else-if="dailyCourses.length === 0" class="empty_section"> |
|||
<view class="empty_icon">📅</view> |
|||
<view class="empty_text">当日暂无课程安排</view> |
|||
<view class="empty_hint">选择其他日期查看课程</view> |
|||
</view> |
|||
|
|||
<view v-else class="courses_list"> |
|||
<view |
|||
v-for="course in dailyCourses" |
|||
:key="course.id" |
|||
:class="['course_item', course.status]" |
|||
@click="viewCourseDetail(course)" |
|||
> |
|||
<view class="course_time"> |
|||
<view class="time_range">{{ course.start_time }}</view> |
|||
<view class="time_duration">{{ course.duration }}分钟</view> |
|||
</view> |
|||
|
|||
<view class="course_info"> |
|||
<view class="course_name">{{ course.course_name }}</view> |
|||
<view class="course_details"> |
|||
<view class="detail_row"> |
|||
<text class="detail_label">教练:</text> |
|||
<text class="detail_value">{{ course.coach_name }}</text> |
|||
</view> |
|||
<view class="detail_row"> |
|||
<text class="detail_label">场地:</text> |
|||
<text class="detail_value">{{ course.venue_name }}</text> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="course_status"> |
|||
<view :class="['status_badge', course.status]"> |
|||
{{ getStatusText(course.status) }} |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 课程详情弹窗 --> |
|||
<view class="course_popup" v-if="showCoursePopup" @click="closeCoursePopup"> |
|||
<view class="popup_content" @click.stop> |
|||
<view class="popup_header"> |
|||
<view class="popup_title">课程详情</view> |
|||
<view class="popup_close" @click="closeCoursePopup">×</view> |
|||
</view> |
|||
|
|||
<view class="popup_course_detail" v-if="selectedCourse"> |
|||
<view class="detail_section"> |
|||
<view class="section_title">基本信息</view> |
|||
<view class="info_grid"> |
|||
<view class="info_row"> |
|||
<text class="info_label">课程名称:</text> |
|||
<text class="info_value">{{ selectedCourse.course_name }}</text> |
|||
</view> |
|||
<view class="info_row"> |
|||
<text class="info_label">上课时间:</text> |
|||
<text class="info_value">{{ formatDateTime(selectedCourse.course_date, selectedCourse.start_time) }}</text> |
|||
</view> |
|||
<view class="info_row"> |
|||
<text class="info_label">课程时长:</text> |
|||
<text class="info_value">{{ selectedCourse.duration }}分钟</text> |
|||
</view> |
|||
<view class="info_row"> |
|||
<text class="info_label">授课教练:</text> |
|||
<text class="info_value">{{ selectedCourse.coach_name }}</text> |
|||
</view> |
|||
<view class="info_row"> |
|||
<text class="info_label">上课地点:</text> |
|||
<text class="info_value">{{ selectedCourse.venue_name }}</text> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="detail_section" v-if="selectedCourse.course_description"> |
|||
<view class="section_title">课程介绍</view> |
|||
<view class="course_description">{{ selectedCourse.course_description }}</view> |
|||
</view> |
|||
|
|||
<view class="detail_section" v-if="selectedCourse.preparation_items"> |
|||
<view class="section_title">课前准备</view> |
|||
<view class="preparation_list"> |
|||
<view |
|||
v-for="item in selectedCourse.preparation_items" |
|||
:key="item" |
|||
class="preparation_item" |
|||
> |
|||
• {{ item }} |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="popup_actions"> |
|||
<fui-button |
|||
v-if="selectedCourse && selectedCourse.status === 'scheduled'" |
|||
background="#f39c12" |
|||
@click="requestLeave" |
|||
> |
|||
请假 |
|||
</fui-button> |
|||
|
|||
<fui-button |
|||
background="#f8f9fa" |
|||
color="#666" |
|||
@click="closeCoursePopup" |
|||
> |
|||
关闭 |
|||
</fui-button> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
import apiRoute from '@/api/apiRoute.js' |
|||
|
|||
export default { |
|||
data() { |
|||
return { |
|||
studentId: 0, |
|||
studentInfo: {}, |
|||
selectedDate: '', |
|||
currentWeekStart: new Date(), |
|||
weekDays: [], |
|||
dailyCourses: [], |
|||
weeklyStats: {}, |
|||
loading: false, |
|||
showCoursePopup: false, |
|||
selectedCourse: null |
|||
} |
|||
}, |
|||
|
|||
computed: { |
|||
currentWeekText() { |
|||
const start = new Date(this.currentWeekStart) |
|||
const end = new Date(start) |
|||
end.setDate(start.getDate() + 6) |
|||
|
|||
const startMonth = start.getMonth() + 1 |
|||
const startDay = start.getDate() |
|||
const endMonth = end.getMonth() + 1 |
|||
const endDay = end.getDate() |
|||
|
|||
if (startMonth === endMonth) { |
|||
return `${startMonth}月${startDay}日-${endDay}日` |
|||
} else { |
|||
return `${startMonth}月${startDay}日-${endMonth}月${endDay}日` |
|||
} |
|||
} |
|||
}, |
|||
|
|||
onLoad(options) { |
|||
this.studentId = parseInt(options.student_id) || 0 |
|||
if (this.studentId) { |
|||
this.initPage() |
|||
} else { |
|||
uni.showToast({ |
|||
title: '参数错误', |
|||
icon: 'none' |
|||
}) |
|||
} |
|||
}, |
|||
|
|||
methods: { |
|||
goBack() { |
|||
uni.navigateBack() |
|||
}, |
|||
|
|||
async initPage() { |
|||
await this.loadStudentInfo() |
|||
this.generateWeekDays() |
|||
|
|||
// 默认选择今天 |
|||
const today = this.formatDateString(new Date()) |
|||
this.selectedDate = today |
|||
await this.loadDailyCourses() |
|||
await this.loadWeeklyStats() |
|||
}, |
|||
|
|||
async loadStudentInfo() { |
|||
try { |
|||
// 模拟获取学员信息 |
|||
const mockStudentInfo = { |
|||
id: this.studentId, |
|||
name: '小明' |
|||
} |
|||
this.studentInfo = mockStudentInfo |
|||
} catch (error) { |
|||
console.error('获取学员信息失败:', error) |
|||
} |
|||
}, |
|||
|
|||
generateWeekDays() { |
|||
const days = [] |
|||
const startDate = new Date(this.currentWeekStart) |
|||
const today = new Date() |
|||
const todayStr = this.formatDateString(today) |
|||
|
|||
for (let i = 0; i < 7; i++) { |
|||
const date = new Date(startDate) |
|||
date.setDate(startDate.getDate() + i) |
|||
|
|||
const dateStr = this.formatDateString(date) |
|||
days.push({ |
|||
date: dateStr, |
|||
day: date.getDate(), |
|||
weekday: this.getWeekday(date.getDay()), |
|||
isToday: dateStr === todayStr, |
|||
hasCourse: false // 待后续加载课程数据后更新 |
|||
}) |
|||
} |
|||
|
|||
this.weekDays = days |
|||
}, |
|||
|
|||
prevWeek() { |
|||
this.currentWeekStart.setDate(this.currentWeekStart.getDate() - 7) |
|||
this.generateWeekDays() |
|||
this.loadDailyCourses() |
|||
this.loadWeeklyStats() |
|||
}, |
|||
|
|||
nextWeek() { |
|||
this.currentWeekStart.setDate(this.currentWeekStart.getDate() + 7) |
|||
this.generateWeekDays() |
|||
this.loadDailyCourses() |
|||
this.loadWeeklyStats() |
|||
}, |
|||
|
|||
goToToday() { |
|||
const today = new Date() |
|||
this.currentWeekStart = this.getWeekStartDate(today) |
|||
this.generateWeekDays() |
|||
this.selectedDate = this.formatDateString(today) |
|||
this.loadDailyCourses() |
|||
this.loadWeeklyStats() |
|||
}, |
|||
|
|||
selectDate(date) { |
|||
this.selectedDate = date |
|||
this.loadDailyCourses() |
|||
}, |
|||
|
|||
async loadDailyCourses() { |
|||
if (!this.selectedDate) return |
|||
|
|||
this.loading = true |
|||
try { |
|||
console.log('加载课程安排:', this.selectedDate, '学员ID:', this.studentId) |
|||
|
|||
// 调用真实API |
|||
const response = await apiRoute.getCourseScheduleList({ |
|||
student_id: this.studentId, |
|||
date: this.selectedDate |
|||
}) |
|||
|
|||
console.log('课程安排API响应:', response) |
|||
|
|||
if (response.code === 1) { |
|||
// 处理API返回的数据格式 |
|||
const courses = response.data.list || [] |
|||
this.dailyCourses = courses.map(course => ({ |
|||
id: course.id, |
|||
course_date: course.course_date, |
|||
start_time: course.start_time, |
|||
end_time: course.end_time, |
|||
duration: course.duration, |
|||
course_name: course.course_name, |
|||
course_description: course.course_description, |
|||
coach_name: course.coach_name, |
|||
venue_name: course.venue_name, |
|||
status: this.mapApiStatusToFrontend(course.status), |
|||
preparation_items: course.preparation_items || [] |
|||
})) |
|||
|
|||
// 更新周视图中的课程指示器 |
|||
this.updateWeekCourseIndicators() |
|||
|
|||
console.log('课程数据加载成功:', this.dailyCourses) |
|||
} else { |
|||
console.warn('API返回错误,使用模拟数据:', response.msg) |
|||
// 如果API失败,使用模拟数据 |
|||
this.loadMockCourseData() |
|||
} |
|||
} catch (error) { |
|||
console.error('获取课程安排失败:', error) |
|||
console.warn('API调用失败,使用模拟数据') |
|||
// 如果API调用失败,使用模拟数据 |
|||
this.loadMockCourseData() |
|||
} finally { |
|||
this.loading = false |
|||
} |
|||
}, |
|||
|
|||
// 加载模拟课程数据(作为后备方案) |
|||
loadMockCourseData() { |
|||
const mockCourses = [ |
|||
{ |
|||
id: 1, |
|||
course_date: this.selectedDate, |
|||
start_time: '09:00', |
|||
end_time: '10:00', |
|||
duration: 60, |
|||
course_name: '基础体能训练', |
|||
course_description: '通过基础的体能训练动作,提升学员的身体素质,包括力量、耐力、协调性等方面的训练。', |
|||
coach_name: '张教练', |
|||
venue_name: '训练馆A', |
|||
status: 'scheduled', |
|||
preparation_items: ['运动服装', '运动鞋', '毛巾', '水杯'] |
|||
}, |
|||
{ |
|||
id: 2, |
|||
course_date: this.selectedDate, |
|||
start_time: '14:00', |
|||
end_time: '15:30', |
|||
duration: 90, |
|||
course_name: '专项技能训练', |
|||
course_description: '针对特定运动项目进行专项技能训练,提高学员在该项目上的技术水平。', |
|||
coach_name: '李教练', |
|||
venue_name: '训练馆B', |
|||
status: 'completed', |
|||
preparation_items: ['专项器材', '护具', '运动服装'] |
|||
} |
|||
] |
|||
this.dailyCourses = mockCourses |
|||
this.updateWeekCourseIndicators() |
|||
}, |
|||
|
|||
// 映射API状态到前端状态 |
|||
mapApiStatusToFrontend(apiStatus) { |
|||
const statusMap = { |
|||
0: 'scheduled', // 待上课 |
|||
1: 'completed', // 已完成 |
|||
2: 'leave_requested', // 请假 |
|||
3: 'cancelled' // 取消 |
|||
} |
|||
return statusMap[apiStatus] || 'scheduled' |
|||
}, |
|||
|
|||
async loadWeeklyStats() { |
|||
try { |
|||
// 模拟获取本周统计数据 |
|||
const mockStats = { |
|||
total_courses: 8, |
|||
completed_courses: 3, |
|||
scheduled_courses: 4, |
|||
cancelled_courses: 1 |
|||
} |
|||
this.weeklyStats = mockStats |
|||
} catch (error) { |
|||
console.error('获取周统计失败:', error) |
|||
} |
|||
}, |
|||
|
|||
updateWeekCourseIndicators() { |
|||
// 更新周视图中每天是否有课程的指示器 |
|||
this.weekDays.forEach(day => { |
|||
// 这里简化处理,实际应该查询每天的课程数据 |
|||
day.hasCourse = day.date === this.selectedDate && this.dailyCourses.length > 0 |
|||
}) |
|||
}, |
|||
|
|||
viewCourseDetail(course) { |
|||
this.selectedCourse = course |
|||
this.showCoursePopup = true |
|||
}, |
|||
|
|||
closeCoursePopup() { |
|||
this.showCoursePopup = false |
|||
this.selectedCourse = null |
|||
}, |
|||
|
|||
async requestLeave() { |
|||
if (!this.selectedCourse) return |
|||
|
|||
uni.showModal({ |
|||
title: '确认请假', |
|||
content: '确定要为此课程申请请假吗?', |
|||
success: async (res) => { |
|||
if (res.confirm) { |
|||
try { |
|||
console.log('申请请假:', this.selectedCourse.id) |
|||
|
|||
// 调用真实API |
|||
const response = await apiRoute.requestCourseLeave({ |
|||
schedule_id: this.selectedCourse.id, |
|||
reason: '学员申请请假' |
|||
}) |
|||
|
|||
console.log('请假申请API响应:', response) |
|||
|
|||
if (response.code === 1) { |
|||
uni.showToast({ |
|||
title: '请假申请已提交', |
|||
icon: 'success' |
|||
}) |
|||
|
|||
// 更新课程状态 |
|||
const courseIndex = this.dailyCourses.findIndex(c => c.id === this.selectedCourse.id) |
|||
if (courseIndex !== -1) { |
|||
this.dailyCourses[courseIndex].status = 'leave_requested' |
|||
} |
|||
|
|||
this.closeCoursePopup() |
|||
} else { |
|||
uni.showToast({ |
|||
title: response.msg || '请假申请失败', |
|||
icon: 'none' |
|||
}) |
|||
} |
|||
} catch (error) { |
|||
console.error('请假申请失败:', error) |
|||
uni.showToast({ |
|||
title: '请假申请失败', |
|||
icon: 'none' |
|||
}) |
|||
} |
|||
} |
|||
} |
|||
}) |
|||
}, |
|||
|
|||
// 工具方法 |
|||
formatDateString(date) { |
|||
const year = date.getFullYear() |
|||
const month = String(date.getMonth() + 1).padStart(2, '0') |
|||
const day = String(date.getDate()).padStart(2, '0') |
|||
return `${year}-${month}-${day}` |
|||
}, |
|||
|
|||
formatDateTime(dateString, timeString) { |
|||
const date = new Date(dateString) |
|||
const month = date.getMonth() + 1 |
|||
const day = date.getDate() |
|||
const weekday = this.getWeekday(date.getDay()) |
|||
return `${month}月${day}日 ${weekday} ${timeString}` |
|||
}, |
|||
|
|||
getWeekday(dayIndex) { |
|||
const weekdays = ['日', '一', '二', '三', '四', '五', '六'] |
|||
return weekdays[dayIndex] |
|||
}, |
|||
|
|||
getWeekStartDate(date) { |
|||
const start = new Date(date) |
|||
const day = start.getDay() |
|||
const diff = start.getDate() - day |
|||
start.setDate(diff) |
|||
return start |
|||
}, |
|||
|
|||
getStatusText(status) { |
|||
const statusMap = { |
|||
'scheduled': '待上课', |
|||
'completed': '已完成', |
|||
'cancelled': '已取消', |
|||
'leave_requested': '请假中' |
|||
} |
|||
return statusMap[status] || status |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style lang="less" scoped> |
|||
.main_box { |
|||
background: #f8f9fa; |
|||
min-height: 100vh; |
|||
} |
|||
|
|||
// 自定义导航栏 |
|||
.navbar_section { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
background: #29D3B4; |
|||
padding: 40rpx 32rpx 20rpx; |
|||
|
|||
// 小程序端适配状态栏 |
|||
// #ifdef MP-WEIXIN |
|||
padding-top: 80rpx; |
|||
// #endif |
|||
|
|||
.navbar_back { |
|||
width: 60rpx; |
|||
|
|||
.back_icon { |
|||
color: #fff; |
|||
font-size: 40rpx; |
|||
font-weight: 600; |
|||
} |
|||
} |
|||
|
|||
.navbar_title { |
|||
color: #fff; |
|||
font-size: 32rpx; |
|||
font-weight: 600; |
|||
} |
|||
|
|||
.navbar_action { |
|||
width: 80rpx; |
|||
display: flex; |
|||
justify-content: flex-end; |
|||
|
|||
.today_button { |
|||
background: rgba(255, 255, 255, 0.2); |
|||
padding: 8rpx 16rpx; |
|||
border-radius: 16rpx; |
|||
|
|||
.today_text { |
|||
color: #fff; |
|||
font-size: 24rpx; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
// 学员信息 |
|||
.student_info_section { |
|||
background: #fff; |
|||
padding: 24rpx 32rpx; |
|||
border-bottom: 1px solid #f0f0f0; |
|||
|
|||
.student_name { |
|||
font-size: 32rpx; |
|||
font-weight: 600; |
|||
color: #333; |
|||
margin-bottom: 12rpx; |
|||
} |
|||
|
|||
.schedule_stats { |
|||
display: flex; |
|||
gap: 24rpx; |
|||
|
|||
.stat_item { |
|||
font-size: 24rpx; |
|||
color: #666; |
|||
} |
|||
} |
|||
} |
|||
|
|||
// 日历部分 |
|||
.calendar_section { |
|||
background: #fff; |
|||
margin: 20rpx; |
|||
border-radius: 16rpx; |
|||
padding: 24rpx 32rpx; |
|||
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.08); |
|||
|
|||
.calendar_header { |
|||
margin-bottom: 24rpx; |
|||
|
|||
.month_controls { |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: center; |
|||
gap: 32rpx; |
|||
|
|||
.control_button { |
|||
width: 40rpx; |
|||
height: 40rpx; |
|||
background: #f8f9fa; |
|||
border-radius: 50%; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
font-size: 24rpx; |
|||
color: #666; |
|||
} |
|||
|
|||
.current_period { |
|||
font-size: 28rpx; |
|||
font-weight: 600; |
|||
color: #333; |
|||
min-width: 200rpx; |
|||
text-align: center; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.week_tabs { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
|
|||
.week_tab { |
|||
flex: 1; |
|||
text-align: center; |
|||
padding: 16rpx 8rpx; |
|||
border-radius: 12rpx; |
|||
position: relative; |
|||
|
|||
&.active { |
|||
background: #29D3B4; |
|||
color: #fff; |
|||
} |
|||
|
|||
&.today { |
|||
background: rgba(41, 211, 180, 0.1); |
|||
|
|||
&.active { |
|||
background: #29D3B4; |
|||
} |
|||
} |
|||
|
|||
.tab_weekday { |
|||
font-size: 22rpx; |
|||
margin-bottom: 4rpx; |
|||
opacity: 0.8; |
|||
} |
|||
|
|||
.tab_date { |
|||
font-size: 28rpx; |
|||
font-weight: 600; |
|||
} |
|||
|
|||
.tab_indicator { |
|||
position: absolute; |
|||
bottom: 4rpx; |
|||
left: 50%; |
|||
transform: translateX(-50%); |
|||
width: 6rpx; |
|||
height: 6rpx; |
|||
background: #ff4757; |
|||
border-radius: 50%; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
// 课程列表 |
|||
.courses_section { |
|||
margin: 0 20rpx; |
|||
|
|||
.loading_section, .empty_section { |
|||
background: #fff; |
|||
border-radius: 16rpx; |
|||
padding: 80rpx 32rpx; |
|||
text-align: center; |
|||
|
|||
.loading_text, .empty_text { |
|||
font-size: 28rpx; |
|||
color: #666; |
|||
margin-bottom: 12rpx; |
|||
} |
|||
|
|||
.empty_icon { |
|||
font-size: 80rpx; |
|||
margin-bottom: 24rpx; |
|||
} |
|||
|
|||
.empty_hint { |
|||
font-size: 24rpx; |
|||
color: #999; |
|||
} |
|||
} |
|||
|
|||
.courses_list { |
|||
.course_item { |
|||
background: #fff; |
|||
border-radius: 16rpx; |
|||
padding: 24rpx; |
|||
margin-bottom: 16rpx; |
|||
display: flex; |
|||
align-items: center; |
|||
gap: 20rpx; |
|||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.06); |
|||
|
|||
&.scheduled { |
|||
border-left: 4rpx solid #29D3B4; |
|||
} |
|||
|
|||
&.completed { |
|||
background: #f8f9fa; |
|||
border-left: 4rpx solid #27ae60; |
|||
} |
|||
|
|||
&.cancelled { |
|||
background: #f8f9fa; |
|||
border-left: 4rpx solid #e74c3c; |
|||
opacity: 0.7; |
|||
} |
|||
|
|||
&.leave_requested { |
|||
background: #f8f9fa; |
|||
border-left: 4rpx solid #f39c12; |
|||
} |
|||
|
|||
.course_time { |
|||
min-width: 100rpx; |
|||
text-align: center; |
|||
|
|||
.time_range { |
|||
font-size: 28rpx; |
|||
font-weight: 600; |
|||
color: #333; |
|||
margin-bottom: 4rpx; |
|||
} |
|||
|
|||
.time_duration { |
|||
font-size: 22rpx; |
|||
color: #999; |
|||
} |
|||
} |
|||
|
|||
.course_info { |
|||
flex: 1; |
|||
|
|||
.course_name { |
|||
font-size: 28rpx; |
|||
font-weight: 600; |
|||
color: #333; |
|||
margin-bottom: 12rpx; |
|||
} |
|||
|
|||
.course_details { |
|||
.detail_row { |
|||
display: flex; |
|||
margin-bottom: 6rpx; |
|||
|
|||
.detail_label { |
|||
font-size: 24rpx; |
|||
color: #666; |
|||
min-width: 80rpx; |
|||
} |
|||
|
|||
.detail_value { |
|||
font-size: 24rpx; |
|||
color: #333; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
.course_status { |
|||
.status_badge { |
|||
font-size: 22rpx; |
|||
padding: 6rpx 12rpx; |
|||
border-radius: 12rpx; |
|||
|
|||
&.scheduled { |
|||
color: #29D3B4; |
|||
background: rgba(41, 211, 180, 0.1); |
|||
} |
|||
|
|||
&.completed { |
|||
color: #27ae60; |
|||
background: rgba(39, 174, 96, 0.1); |
|||
} |
|||
|
|||
&.cancelled { |
|||
color: #e74c3c; |
|||
background: rgba(231, 76, 60, 0.1); |
|||
} |
|||
|
|||
&.leave_requested { |
|||
color: #f39c12; |
|||
background: rgba(243, 156, 18, 0.1); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
// 课程详情弹窗 |
|||
.course_popup { |
|||
position: fixed; |
|||
top: 0; |
|||
left: 0; |
|||
right: 0; |
|||
bottom: 0; |
|||
background: rgba(0, 0, 0, 0.5); |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: center; |
|||
z-index: 1000; |
|||
|
|||
.popup_content { |
|||
background: #fff; |
|||
border-radius: 16rpx; |
|||
width: 90%; |
|||
max-height: 80vh; |
|||
overflow: hidden; |
|||
|
|||
.popup_header { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
padding: 32rpx; |
|||
border-bottom: 1px solid #f0f0f0; |
|||
|
|||
.popup_title { |
|||
font-size: 32rpx; |
|||
font-weight: 600; |
|||
color: #333; |
|||
} |
|||
|
|||
.popup_close { |
|||
font-size: 48rpx; |
|||
color: #999; |
|||
font-weight: 300; |
|||
} |
|||
} |
|||
|
|||
.popup_course_detail { |
|||
padding: 32rpx; |
|||
max-height: 60vh; |
|||
overflow-y: auto; |
|||
|
|||
.detail_section { |
|||
margin-bottom: 32rpx; |
|||
|
|||
&:last-child { |
|||
margin-bottom: 0; |
|||
} |
|||
|
|||
.section_title { |
|||
font-size: 28rpx; |
|||
font-weight: 600; |
|||
color: #333; |
|||
margin-bottom: 16rpx; |
|||
padding-bottom: 8rpx; |
|||
border-bottom: 2rpx solid #29D3B4; |
|||
} |
|||
|
|||
.info_grid { |
|||
.info_row { |
|||
display: flex; |
|||
margin-bottom: 12rpx; |
|||
|
|||
.info_label { |
|||
font-size: 26rpx; |
|||
color: #666; |
|||
min-width: 120rpx; |
|||
} |
|||
|
|||
.info_value { |
|||
font-size: 26rpx; |
|||
color: #333; |
|||
flex: 1; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.course_description { |
|||
font-size: 26rpx; |
|||
color: #333; |
|||
line-height: 1.6; |
|||
} |
|||
|
|||
.preparation_list { |
|||
.preparation_item { |
|||
font-size: 24rpx; |
|||
color: #666; |
|||
line-height: 1.5; |
|||
margin-bottom: 8rpx; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
.popup_actions { |
|||
padding: 24rpx 32rpx; |
|||
display: flex; |
|||
gap: 16rpx; |
|||
border-top: 1px solid #f0f0f0; |
|||
|
|||
fui-button { |
|||
flex: 1; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
</style> |
|||
@ -0,0 +1,254 @@ |
|||
<!--学员端系统设置页面--> |
|||
<template> |
|||
<view class="settings_container"> |
|||
<!-- 自定义导航栏 --> |
|||
<view class="navbar_section"> |
|||
<view class="navbar_content"> |
|||
<view class="back_button" @click="goBack"> |
|||
<image src="/static/icon-img/back.png" class="back_icon"></image> |
|||
</view> |
|||
<view class="navbar_title">系统设置</view> |
|||
<view class="navbar_placeholder"></view> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 设置选项列表 --> |
|||
<view class="settings_section"> |
|||
<view class="setting_item" @click="navigateToProfile"> |
|||
<view class="setting_left"> |
|||
<view class="setting_icon profile_setting"> |
|||
<image src="/static/icon-img/profile.png" class="icon_image"></image> |
|||
</view> |
|||
<view class="setting_text">个人资料</view> |
|||
</view> |
|||
<view class="setting_arrow"> |
|||
<image src="/static/icon-img/arrow-right.png" class="arrow_icon"></image> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="setting_item" @click="navigateToPassword"> |
|||
<view class="setting_left"> |
|||
<view class="setting_icon password_setting"> |
|||
<image src="/static/icon-img/lock.png" class="icon_image"></image> |
|||
</view> |
|||
<view class="setting_text">修改密码</view> |
|||
</view> |
|||
<view class="setting_arrow"> |
|||
<image src="/static/icon-img/arrow-right.png" class="arrow_icon"></image> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="setting_item" @click="navigateToAbout"> |
|||
<view class="setting_left"> |
|||
<view class="setting_icon about_setting"> |
|||
<image src="/static/icon-img/info.png" class="icon_image"></image> |
|||
</view> |
|||
<view class="setting_text">关于我们</view> |
|||
</view> |
|||
<view class="setting_arrow"> |
|||
<image src="/static/icon-img/arrow-right.png" class="arrow_icon"></image> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="setting_item" @click="navigateToPrivacy"> |
|||
<view class="setting_left"> |
|||
<view class="setting_icon privacy_setting"> |
|||
<image src="/static/icon-img/shield.png" class="icon_image"></image> |
|||
</view> |
|||
<view class="setting_text">隐私协议</view> |
|||
</view> |
|||
<view class="setting_arrow"> |
|||
<image src="/static/icon-img/arrow-right.png" class="arrow_icon"></image> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 退出登录按钮 --> |
|||
<view class="logout_section"> |
|||
<button class="logout_button" @click="logout">退出登录</button> |
|||
</view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
export default { |
|||
data() { |
|||
return { |
|||
|
|||
} |
|||
}, |
|||
methods: { |
|||
goBack() { |
|||
uni.navigateBack() |
|||
}, |
|||
|
|||
navigateToProfile() { |
|||
uni.navigateTo({ |
|||
url: '/pages-common/profile/personal_info' |
|||
}) |
|||
}, |
|||
|
|||
navigateToPassword() { |
|||
uni.navigateTo({ |
|||
url: '/pages-student/my/update_pass' |
|||
}) |
|||
}, |
|||
|
|||
navigateToAbout() { |
|||
uni.navigateTo({ |
|||
url: '/pages-student/settings/about' |
|||
}) |
|||
}, |
|||
|
|||
navigateToPrivacy() { |
|||
uni.navigateTo({ |
|||
url: '/pages-common/privacy_agreement' |
|||
}) |
|||
}, |
|||
|
|||
logout() { |
|||
uni.showModal({ |
|||
title: '确认退出', |
|||
content: '确定要退出登录吗?', |
|||
success: (res) => { |
|||
if (res.confirm) { |
|||
// 清除本地存储 |
|||
uni.clearStorageSync() |
|||
|
|||
// 跳转到登录页 |
|||
uni.reLaunch({ |
|||
url: '/pages-student/login/login' |
|||
}) |
|||
} |
|||
} |
|||
}) |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style lang="less" scoped> |
|||
.settings_container { |
|||
background: #f8f9fa; |
|||
min-height: 100vh; |
|||
} |
|||
|
|||
// 自定义导航栏 |
|||
.navbar_section { |
|||
background: linear-gradient(135deg, #29D3B4 0%, #1BA297 100%); |
|||
padding: 40rpx 32rpx 32rpx; |
|||
|
|||
// 小程序端适配状态栏 |
|||
// #ifdef MP-WEIXIN |
|||
padding-top: 80rpx; |
|||
// #endif |
|||
|
|||
.navbar_content { |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: space-between; |
|||
|
|||
.back_button { |
|||
width: 40rpx; |
|||
height: 40rpx; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
|
|||
.back_icon { |
|||
width: 24rpx; |
|||
height: 24rpx; |
|||
} |
|||
} |
|||
|
|||
.navbar_title { |
|||
color: #fff; |
|||
font-size: 36rpx; |
|||
font-weight: 600; |
|||
} |
|||
|
|||
.navbar_placeholder { |
|||
width: 40rpx; |
|||
} |
|||
} |
|||
} |
|||
|
|||
// 设置选项 |
|||
.settings_section { |
|||
margin: 32rpx 20rpx; |
|||
background: #fff; |
|||
border-radius: 16rpx; |
|||
overflow: hidden; |
|||
|
|||
.setting_item { |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: space-between; |
|||
padding: 32rpx; |
|||
border-bottom: 1px solid #f0f0f0; |
|||
|
|||
&:last-child { |
|||
border-bottom: none; |
|||
} |
|||
|
|||
.setting_left { |
|||
display: flex; |
|||
align-items: center; |
|||
gap: 24rpx; |
|||
|
|||
.setting_icon { |
|||
width: 60rpx; |
|||
height: 60rpx; |
|||
border-radius: 50%; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
|
|||
&.profile_setting { background: rgba(52, 152, 219, 0.15); } |
|||
&.password_setting { background: rgba(231, 76, 60, 0.15); } |
|||
&.about_setting { background: rgba(155, 89, 182, 0.15); } |
|||
&.privacy_setting { background: rgba(46, 204, 113, 0.15); } |
|||
|
|||
.icon_image { |
|||
width: 32rpx; |
|||
height: 32rpx; |
|||
} |
|||
} |
|||
|
|||
.setting_text { |
|||
font-size: 28rpx; |
|||
color: #333; |
|||
font-weight: 500; |
|||
} |
|||
} |
|||
|
|||
.setting_arrow { |
|||
.arrow_icon { |
|||
width: 20rpx; |
|||
height: 20rpx; |
|||
opacity: 0.6; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
// 退出登录按钮 |
|||
.logout_section { |
|||
margin: 60rpx 20rpx; |
|||
|
|||
.logout_button { |
|||
width: 100%; |
|||
background: #ff4757; |
|||
color: #fff; |
|||
border: none; |
|||
border-radius: 16rpx; |
|||
padding: 24rpx 0; |
|||
font-size: 28rpx; |
|||
font-weight: 600; |
|||
|
|||
&:active { |
|||
background: #ff3742; |
|||
} |
|||
} |
|||
} |
|||
</style> |
|||
@ -0,0 +1,601 @@ |
|||
{ |
|||
"pages": [ |
|||
{ |
|||
"path": "pages/student/login/login", |
|||
"style": { |
|||
"navigationBarTitleText": "", |
|||
"navigationStyle": "default", |
|||
"navigationBarBackgroundColor": "#fff", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "pages/student/home/index", |
|||
"style": { |
|||
"navigationBarTitleText": "首页", |
|||
"navigationStyle": "custom", |
|||
"navigationBarBackgroundColor": "#29d3b4", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "pages/common/home/index", |
|||
"style": { |
|||
"navigationBarTitleText": "首页", |
|||
"navigationStyle": "custom", |
|||
"navigationBarBackgroundColor": "#181A20", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
} |
|||
{ |
|||
"path": "pages/student/my/set_up", |
|||
"style": { |
|||
"navigationBarTitleText": "设置", |
|||
"navigationStyle": "default", |
|||
"navigationBarBackgroundColor": "#29D3B4", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "pages/student/my/update_pass", |
|||
"style": { |
|||
"navigationBarTitleText": "修改密码", |
|||
"navigationStyle": "default", |
|||
"navigationBarBackgroundColor": "#fff", |
|||
"navigationBarTextStyle": "black" |
|||
} |
|||
}, |
|||
|
|||
{ |
|||
"path": "pages/student/my/personal_data", |
|||
"style": { |
|||
"navigationBarTitleText": "个人资料", |
|||
"navigationStyle": "default", |
|||
"navigationBarBackgroundColor": "#333333", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "pages/student/profile/index", |
|||
"style": { |
|||
"navigationBarTitleText": "个人信息管理", |
|||
"navigationStyle": "custom", |
|||
"navigationBarBackgroundColor": "#29d3b4", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "pages/student/physical-test/index", |
|||
"style": { |
|||
"navigationBarTitleText": "体测数据", |
|||
"navigationStyle": "custom", |
|||
"navigationBarBackgroundColor": "#29d3b4", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "pages/student/schedule/index", |
|||
"style": { |
|||
"navigationBarTitleText": "课程安排", |
|||
"navigationStyle": "custom", |
|||
"navigationBarBackgroundColor": "#29d3b4", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "pages/student/course-booking/index", |
|||
"style": { |
|||
"navigationBarTitleText": "课程预约", |
|||
"navigationStyle": "custom", |
|||
"navigationBarBackgroundColor": "#29d3b4", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "pages/student/orders/index", |
|||
"style": { |
|||
"navigationBarTitleText": "订单管理", |
|||
"navigationStyle": "custom", |
|||
"navigationBarBackgroundColor": "#29d3b4", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "pages/student/contracts/index", |
|||
"style": { |
|||
"navigationBarTitleText": "合同管理", |
|||
"navigationStyle": "custom", |
|||
"navigationBarBackgroundColor": "#29d3b4", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "pages/student/knowledge/index", |
|||
"style": { |
|||
"navigationBarTitleText": "知识库", |
|||
"navigationStyle": "custom", |
|||
"navigationBarBackgroundColor": "#29d3b4", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "pages/student/messages/index", |
|||
"style": { |
|||
"navigationBarTitleText": "消息管理", |
|||
"navigationStyle": "custom", |
|||
"navigationBarBackgroundColor": "#29d3b4", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "pages/student/settings/index", |
|||
"style": { |
|||
"navigationBarTitleText": "系统设置", |
|||
"navigationStyle": "custom", |
|||
"navigationBarBackgroundColor": "#29d3b4", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "pages/student/child/add", |
|||
"style": { |
|||
"navigationBarTitleText": "添加孩子", |
|||
"navigationStyle": "custom", |
|||
"navigationBarBackgroundColor": "#29d3b4", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "pages/common/privacy_agreement", |
|||
"style": { |
|||
"navigationBarTitleText": "隐私协议", |
|||
"navigationStyle": "default", |
|||
"navigationBarBackgroundColor": "#fff", |
|||
"navigationBarTextStyle": "black" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "pages/common/my_message", |
|||
"style": { |
|||
"navigationBarTitleText": "我的消息", |
|||
"navigationStyle": "default", |
|||
"navigationBarBackgroundColor": "#292929", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "pages/common/im_chat_info", |
|||
"style": { |
|||
"navigationBarTitleText": "消息", |
|||
"navigationStyle": "default", |
|||
"navigationBarBackgroundColor": "#292929", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "pages/common/sys_msg_list", |
|||
"style": { |
|||
"navigationBarTitleText": "系统消息", |
|||
"navigationStyle": "default", |
|||
"navigationBarBackgroundColor": "#292929", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "pages/common/article_info", |
|||
"style": { |
|||
"navigationBarTitleText": "文章详情", |
|||
"navigationStyle": "default", |
|||
"navigationBarBackgroundColor": "#292929", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "pages/common/feedback", |
|||
"style": { |
|||
"navigationBarTitleText": "意见反馈", |
|||
"navigationStyle": "default", |
|||
"navigationBarBackgroundColor": "#292929", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "pages/common/my_attendance", |
|||
"style": { |
|||
"navigationBarTitleText": "我的考勤", |
|||
"navigationStyle": "default", |
|||
"navigationBarBackgroundColor": "#292929", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "pages/common/personnel/add_personnel", |
|||
"style": { |
|||
"navigationBarTitleText": "新员工信息填写", |
|||
"navigationStyle": "custom", |
|||
"navigationBarBackgroundColor": "#fff", |
|||
"navigationBarTextStyle": "black" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "pages/common/contract/my_contract", |
|||
"style": { |
|||
"navigationBarTitleText": "我的合同", |
|||
"navigationStyle": "default", |
|||
"navigationBarBackgroundColor": "#29d3b4", |
|||
"navigationBarTextStyle": "white", |
|||
"enablePullDownRefresh": true |
|||
} |
|||
}, |
|||
{ |
|||
"path": "pages/common/contract/contract_detail", |
|||
"style": { |
|||
"navigationBarTitleText": "合同详情", |
|||
"navigationStyle": "default", |
|||
"navigationBarBackgroundColor": "#29d3b4", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "pages/coach/student/student_list", |
|||
"style": { |
|||
"navigationBarTitleText": "我的学员", |
|||
"navigationBarBackgroundColor": "#29d3b4", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
|
|||
{ |
|||
"path": "pages/coach/my/teaching_management", |
|||
"style": { |
|||
"navigationBarTitleText": "教研管理列表", |
|||
"navigationStyle": "default", |
|||
"navigationBarBackgroundColor": "#171717", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "pages/coach/my/gotake_exam", |
|||
"style": { |
|||
"navigationBarTitleText": "考试", |
|||
"navigationStyle": "default", |
|||
"navigationBarBackgroundColor": "#171717", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "pages/coach/my/exam_results", |
|||
"style": { |
|||
"navigationBarTitleText": "考试结果", |
|||
"navigationStyle": "default", |
|||
"navigationBarBackgroundColor": "#fff", |
|||
"navigationBarTextStyle": "black" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "pages/coach/my/salary", |
|||
"style": { |
|||
"navigationBarTitleText": "我的工资", |
|||
"navigationStyle": "default", |
|||
"navigationBarBackgroundColor": "#29d3b4", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "pages/market/clue/add_clues", |
|||
"style": { |
|||
"navigationBarTitleText": "添加客户", |
|||
"navigationStyle": "default", |
|||
"navigationBarBackgroundColor": "#fff", |
|||
"navigationBarTextStyle": "black" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "pages/market/clue/edit_clues", |
|||
"style": { |
|||
"navigationBarTitleText": "编辑客户", |
|||
"navigationStyle": "default", |
|||
"navigationBarBackgroundColor": "#fff", |
|||
"navigationBarTextStyle": "black" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "pages/market/clue/edit_clues_log", |
|||
"style": { |
|||
"navigationBarTitleText": "修改记录", |
|||
"navigationStyle": "default", |
|||
"navigationBarBackgroundColor": "#fff", |
|||
"navigationBarTextStyle": "black" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "pages/market/clue/clue_info", |
|||
"style": { |
|||
"navigationBarTitleText": "客户详情", |
|||
"navigationStyle": "default", |
|||
"navigationBarBackgroundColor": "#29d3b4", |
|||
"navigationBarTextStyle": "black" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "pages/market/clue/index", |
|||
"style": { |
|||
"navigationBarTitleText": "线索", |
|||
"navigationStyle": "default", |
|||
"navigationBarBackgroundColor": "#292929", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "pages/market/index/index", |
|||
"style": { |
|||
"navigationBarTitleText": "销售数据" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "pages/market/my/index", |
|||
"style": { |
|||
"navigationBarTitleText": "我的", |
|||
"navigationStyle": "custom", |
|||
"navigationBarBackgroundColor": "#fff", |
|||
"navigationBarTextStyle": "black" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "pages/market/my/signed_client_list", |
|||
"style": { |
|||
"navigationBarTitleText": "已签客户", |
|||
"navigationStyle": "default", |
|||
"navigationBarBackgroundColor": "#292929", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "pages/market/my/info", |
|||
"style": { |
|||
"navigationBarTitleText": "个人资料", |
|||
"navigationStyle": "default", |
|||
"navigationBarBackgroundColor": "#292929", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "pages/market/my/set_up", |
|||
"style": { |
|||
"navigationBarTitleText": "设置", |
|||
"navigationStyle": "default", |
|||
"navigationBarBackgroundColor": "#292929", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "pages/market/my/update_pass", |
|||
"style": { |
|||
"navigationBarTitleText": "修改密码", |
|||
"navigationStyle": "default", |
|||
"navigationBarBackgroundColor": "#fff", |
|||
"navigationBarTextStyle": "black" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "pages/market/my/my_data", |
|||
"style": { |
|||
"navigationBarTitleText": "我的数据", |
|||
"navigationStyle": "custom", |
|||
"navigationBarBackgroundColor": "#29d3b4", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "pages/market/my/dept_data", |
|||
"style": { |
|||
"navigationBarTitleText": "部门数据", |
|||
"navigationStyle": "custom", |
|||
"navigationBarBackgroundColor": "#29d3b4", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "pages/market/my/campus_data", |
|||
"style": { |
|||
"navigationBarTitleText": "校区数据", |
|||
"navigationStyle": "custom", |
|||
"navigationBarBackgroundColor": "#29d3b4", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "pages/market/clue/class_arrangement", |
|||
"style": { |
|||
"navigationBarTitleText": "课程安排", |
|||
"navigationStyle": "default", |
|||
"navigationBarBackgroundColor": "#232323", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "pages/market/clue/class_arrangement_detail", |
|||
"style": { |
|||
"navigationBarTitleText": "课程安排详情", |
|||
"navigationStyle": "default", |
|||
"navigationBarBackgroundColor": "#232323", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "pages/market/reimbursement/list", |
|||
"style": { |
|||
"navigationBarTitleText": "报销列表", |
|||
"navigationBarBackgroundColor": "#292929", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "pages/market/reimbursement/add", |
|||
"style": { |
|||
"navigationBarTitleText": "新增报销", |
|||
"navigationBarBackgroundColor": "#292929", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "pages/market/reimbursement/detail", |
|||
"style": { |
|||
"navigationBarTitleText": "报销详情", |
|||
"navigationBarBackgroundColor": "#292929", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "pages/market/data/statistics", |
|||
"style": { |
|||
"navigationBarTitleText": "市场数据统计", |
|||
"navigationBarBackgroundColor": "#292929", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "pages/coach/schedule/schedule_table", |
|||
"style": { |
|||
"navigationBarTitleText": "课程安排", |
|||
"navigationBarBackgroundColor": "#292929", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "pages/coach/schedule/add_schedule", |
|||
"style": { |
|||
"navigationBarTitleText": "添加课程安排", |
|||
"navigationBarBackgroundColor": "#292929", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "pages/coach/schedule/adjust_course", |
|||
"style": { |
|||
"navigationBarTitleText": "调整课程安排", |
|||
"navigationBarBackgroundColor": "#292929", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "pages/coach/schedule/sign_in", |
|||
"style": { |
|||
"navigationBarTitleText": "课程点名", |
|||
"navigationStyle": "custom", |
|||
"navigationBarBackgroundColor": "#292929", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "pages/market/clue/clue_table", |
|||
"style": { |
|||
"navigationBarTitleText": "数据统计", |
|||
"navigationBarBackgroundColor": "#292929", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "pages/common/home/index", |
|||
"style": { |
|||
"navigationBarTitleText": "首页", |
|||
"navigationStyle": "custom", |
|||
"navigationBarBackgroundColor": "#181A20", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "pages/common/profile/index", |
|||
"style": { |
|||
"navigationBarTitleText": "我的", |
|||
"navigationStyle": "custom", |
|||
"navigationBarBackgroundColor": "#181A20", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
} |
|||
, |
|||
{ |
|||
"path": "pages/common/profile/personal_info", |
|||
"style": { |
|||
"navigationBarTitleText": "个人资料", |
|||
"navigationStyle": "custom", |
|||
"navigationBarBackgroundColor": "#181A20", |
|||
"navigationBarTextStyle": "white" |
|||
} |
|||
} |
|||
, |
|||
{ |
|||
"path": "pages/contract/list", |
|||
"style": { |
|||
"navigationBarTitleText": "我的合同", |
|||
"navigationBarBackgroundColor": "#181A20", |
|||
"navigationBarTextStyle": "white", |
|||
"backgroundColor": "#181A20" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "pages/contract/fill", |
|||
"style": { |
|||
"navigationBarTitleText": "填写信息", |
|||
"navigationBarBackgroundColor": "#181A20", |
|||
"navigationBarTextStyle": "white", |
|||
"backgroundColor": "#181A20" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "pages/contract/detail", |
|||
"style": { |
|||
"navigationBarTitleText": "合同详情", |
|||
"navigationBarBackgroundColor": "#181A20", |
|||
"navigationBarTextStyle": "white", |
|||
"backgroundColor": "#181A20" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "pages/common/contract/contract_sign", |
|||
"style": { |
|||
"navigationBarTitleText": "电子签名", |
|||
"navigationBarBackgroundColor": "#181A20", |
|||
"navigationBarTextStyle": "white", |
|||
"backgroundColor": "#181A20" |
|||
} |
|||
} |
|||
|
|||
], |
|||
"globalStyle": { |
|||
"navigationBarTextStyle": "white", |
|||
"navigationBarTitleText": "", |
|||
"navigationBarBackgroundColor": "#181A20", |
|||
"backgroundColor": "#FDFDFD", |
|||
"enablePullDownRefresh": true |
|||
}, |
|||
"tabBar": { |
|||
"color": "#7A7E83", |
|||
"selectedColor": "#29d3b4", |
|||
"borderStyle": "black", |
|||
"backgroundColor": "#1a1a1a", |
|||
"list": [ |
|||
{ |
|||
"pagePath": "pages/common/home/index", |
|||
"iconPath": "static/icon-img/home.png", |
|||
"selectedIconPath": "static/icon-img/home-active.png", |
|||
"text": "首页" |
|||
}, |
|||
{ |
|||
"pagePath": "pages/common/profile/index", |
|||
"iconPath": "static/icon-img/profile.png", |
|||
"selectedIconPath": "static/icon-img/profile-active.png", |
|||
"text": "我的" |
|||
} |
|||
] |
|||
}, |
|||
"easycom": { |
|||
"autoscan": true, |
|||
"custom": { |
|||
"fui-(.*)": "@/components/firstui/fui-$1/fui-$1.vue", |
|||
"uni-icons": "@/uni_modules/uni-icons/components/uni-icons/uni-icons.vue", |
|||
"uni-calendar": "@/uni_modules/uni-calendar/components/uni-calendar/uni-calendar.vue", |
|||
"uni-file-picker": "@/uni_modules/uni-file-picker/components/uni-file-picker/uni-file-picker.vue" |
|||
} |
|||
} |
|||
} |
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue