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> |
||||
File diff suppressed because it is too large
@ -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