7 Commits

  1. 80
      src/pages.json
  2. 6
      src/pages/index/index.vue
  3. BIN
      src/static/images/icon/index/tab_10.png
  4. BIN
      src/static/images/icon/index/tab_8.png
  5. BIN
      src/static/images/icon/index/tab_9.png
  6. 1
      src/store/index.js
  7. 147
      src/subpackage/course/components/cancel_class_modal/cancel_class_modal.vue
  8. 175
      src/subpackage/course/components/class_reset_modal/class_reset_modal.vue
  9. 143
      src/subpackage/course/components/manage/bclass_reserve_item/bclass_reserve_item.vue
  10. 93
      src/subpackage/course/components/manage/class_manage_item/class_manage_item.vue
  11. 125
      src/subpackage/course/components/manage/private_reserve_item/private_reserve_item.vue
  12. 86
      src/subpackage/course/components/manage/student_course_item/student_course_item.vue
  13. 114
      src/subpackage/course/components/sclass_record_section/sclass_record_section.vue
  14. 95
      src/subpackage/course/components/store_name/store_name.vue
  15. 117
      src/subpackage/course/components/tab_bar/tab_bar.vue
  16. 192
      src/subpackage/course/components/task_section/task_section.vue
  17. 33
      src/subpackage/course/js/course_api.js
  18. 10
      src/subpackage/course/js/course_server.js
  19. 409
      src/subpackage/course/pages/arrange_class/arrange_class.vue
  20. 226
      src/subpackage/course/pages/class_add_student/class_add_student.vue
  21. 242
      src/subpackage/course/pages/class_student_detail/class_student_detail.vue
  22. 250
      src/subpackage/course/pages/class_time_change/class_time_change.vue
  23. 242
      src/subpackage/course/pages/index/index.vue
  24. 396
      src/subpackage/course/pages/manage_detail/bclass_reserve_detail/bclass_reserve_detail.vue
  25. 540
      src/subpackage/course/pages/manage_detail/class_manage_detail/class_manage_detail.vue
  26. 236
      src/subpackage/course/pages/manage_detail/private_reserve_detail/private_reserve_detail.vue
  27. 323
      src/subpackage/course/pages/manage_detail/student_course_detail/student_course_detail.vue
  28. 352
      src/subpackage/course/pages/manage_list/manage_list.vue
  29. 138
      src/subpackage/course/pages/task_detail/task_detail.vue
  30. 146
      src/subpackage/course/pages/task_finish_list/task_finish_list.vue
  31. BIN
      src/subpackage/course/static/images/icon/arrow_b2.png
  32. BIN
      src/subpackage/course/static/images/icon/arrow_black.png
  33. BIN
      src/subpackage/course/static/images/icon/arrow_green.png
  34. BIN
      src/subpackage/course/static/images/icon/arrow_white.png
  35. BIN
      src/subpackage/course/static/images/icon/calendar.png
  36. BIN
      src/subpackage/course/static/images/icon/close_gray.png
  37. BIN
      src/subpackage/course/static/images/icon/phone.png
  38. BIN
      src/subpackage/course/static/images/icon/triangle.png
  39. BIN
      src/subpackage/course/static/images/no_order.png
  40. BIN
      src/subpackage/course/static/images/tab/tab_0_0.png
  41. BIN
      src/subpackage/course/static/images/tab/tab_0_1.png
  42. BIN
      src/subpackage/course/static/images/tab/tab_0_2.png
  43. BIN
      src/subpackage/course/static/images/tab/tab_0_3.png

80
src/pages.json

@ -220,7 +220,7 @@
"navigationBarTitleText": "门店信息列表" "navigationBarTitleText": "门店信息列表"
} }
} }
], ],
"subpackages": [ "subpackages": [
{ {
@ -385,6 +385,84 @@
} }
} }
] ]
},
{
"root": "subpackage/course",
"pages": [
{
"path": "pages/index/index",
"style" : {
"navigationBarTitleText": "课程管理"
}
},
{
"path": "pages/manage_list/manage_list",
"style" : {
"navigationBarTitleText": "课程管理列表"
}
},
{
"path": "pages/manage_detail/student_course_detail/student_course_detail",
"style" : {
"navigationBarTitleText": "学员课程详情"
}
},
{
"path": "pages/manage_detail/private_reserve_detail/private_reserve_detail",
"style" : {
"navigationBarTitleText": "私教约课记录详情"
}
},
{
"path": "pages/manage_detail/bclass_reserve_detail/bclass_reserve_detail",
"style" : {
"navigationBarTitleText": "大班约课记录详情"
}
},
{
"path": "pages/manage_detail/class_manage_detail/class_manage_detail",
"style" : {
"navigationBarTitleText": "班级管理详情"
}
},
{
"path": "pages/task_detail/task_detail",
"style" : {
"navigationBarTitleText": "作业详情"
}
},
{
"path": "pages/arrange_class/arrange_class",
"style" : {
"navigationBarTitleText": "安排上课"
}
},
{
"path": "pages/class_add_student/class_add_student",
"style" : {
"navigationBarTitleText": "添加学员"
}
},
{
"path": "pages/class_student_detail/class_student_detail",
"style" : {
"navigationBarTitleText": "学员详情"
}
},
{
"path": "pages/class_time_change/class_time_change",
"style" : {
"navigationBarTitleText": "更改上课时间"
}
},
{
"path": "pages/task_finish_list/task_finish_list",
"style" : {
"navigationBarTitleText": "作业完成列表"
}
}
]
} }
], ],
"globalStyle": { "globalStyle": {

6
src/pages/index/index.vue

@ -125,6 +125,12 @@
name: '商品零售', name: '商品零售',
path: '/subpackage/retail/pages/index/index', path: '/subpackage/retail/pages/index/index',
serverKey: 1011 // serverKey: 1011 //
},
{
id: 8,
name: '课程管理',
path: '/subpackage/course/pages/index/index',
serverKey: 1013 // 1013
} }
]; ];

BIN
src/static/images/icon/index/tab_10.png

Before

Width: 52  |  Height: 52  |  Size: 402 B

BIN
src/static/images/icon/index/tab_8.png

Before

Width: 52  |  Height: 52  |  Size: 477 B

After

Width: 52  |  Height: 52  |  Size: 2.4 KiB

BIN
src/static/images/icon/index/tab_9.png

Before

Width: 52  |  Height: 52  |  Size: 696 B

1
src/store/index.js

@ -29,6 +29,7 @@ export default new Vuex.Store({
'1010': '设备管理', '1010': '设备管理',
'1011': '商品零售', '1011': '商品零售',
'1012': '订单管理', '1012': '订单管理',
'1013': '课程管理',
}, },
// 场地占用提交页面信息 // 场地占用提交页面信息

147
src/subpackage/course/components/cancel_class_modal/cancel_class_modal.vue

@ -0,0 +1,147 @@
<template>
<!--弹框 取消上课 -->
<view class="cancel-class-modal" @click.stop="">
<view class="ox-dark-mask fixed-mask " @touchmove.stop.prevent="moveHandle">
<view class="mask-modal" @click.stop="">
<view class="mm-close">
<image src="/subpackage/course/static/images/icon/close_gray.png" @click="closeChange"></image>
</view>
<view class="mm-tit">取消上课</view>
<view class="mm-cause">
<textarea class="mc-textarea" @blur="bindTextAreaBlur" placeholder="请输入取消上课的原因(选填)" maxlength="25" v-model="causeTxt"></textarea>
<view class="mc-num">{{causeTxt.length || 0}}/25</view>
</view>
<view class="mm-btn">
<view class="mb-btn1" hover-class="hover-active" @click="confirmChange">确认</view>
<view class="mb-btn2" hover-class="hover-active" @click="closeChange">取消</view>
</view>
</view>
</view>
</view>
</template>
<script>
import util from '@/utils/util';
export default {
props:{
orderInfo: {
type: Object,
default: ()=>({})
}
},
computed: {
},
data(){
return {
causeTxt: "",
}
},
methods: {
moveHandle(){},
//
bindTextAreaBlur: function(e) {
// console.log( ": " + e.detail.value)
this.causeTxt = e.detail.value
this.$emit('bindTextAreaBlur', this.causeTxt)
},
closeChange(){
this.$emit('reasonClose')
},
confirmChange(){
this.$emit('reasonConfirm', this.causeTxt)
},
},
}
</script>
<style lang="scss">
@import '~style/public.scss';
.cancel-class-modal{
}
//
.fixed-mask{
// position: fixed;
// left: 0;
// top: 0;
// right: 0;
// bottom: 0;
// background-color: rgba(0, 0, 0, .5);
z-index: 999;
}
.mask-modal{
position: absolute;
left: 50%;
top: 46%;
transform: translate(-50%, -50%);
width: 620rpx;
padding: 30rpx 0 82rpx;
border-radius: 10rpx;
background-color: #FFFFFF;
.mm-close{
@include centerFlex(flex-end);
>image{
margin-right: 30rpx;
width: 34rpx;
height: 34rpx;
}
}
.mm-tit{
margin: 16rpx auto 38rpx;
color: #333333;
font-size: 32rpx;
line-height: 44rpx;
text-align: center;
font-weight: 700;
}
.mm-cause{
margin: auto;
position: relative;
width: 538rpx;
height: 150rpx;
border-radius: 10rpx;
border: 2rpx solid #D8D8D8;
.mc-textarea{
margin: 24rpx 20rpx;
width: 498rpx;
height: 102rpx;
color: #333;
font-size: 28rpx;
line-height: 40rpx;
}
.mc-num{
position: absolute;
right: 20rpx;
bottom: 20rpx;
color: #9A9A9D;
font-size: 28rpx;
}
}
.mm-btn{
margin-top: 80rpx;
@include centerFlex(center);
>view{
margin: 0 10rpx;
padding: 22rpx 0;
width: 240rpx;
border-radius: 10rpx;
border: 2rpx solid #009874;
font-size: 32rpx;
line-height: 44rpx;
text-align: center;
}
.mb-btn1{
color: #FFFFFF;
background-color: #009874;
}
.mb-btn2{
color: #009874;
}
}
}
</style>

175
src/subpackage/course/components/class_reset_modal/class_reset_modal.vue

@ -0,0 +1,175 @@
<template>
<!-- 转班弹框 -->
<view class="class-reset-modal" @click.stop="">
<view class="ox-dark-mask fixed-mask" @touchmove.stop.prevent="moveHandle" v-if="isShowClass" @click="closeReset">
<view class="mask-modal" @click.stop="">
<view class="mm-close">
<image src="/subpackage/course/static/images/icon/close_gray.png" @click="closeReset"></image>
</view>
<view class="mm-tit">选择班级</view>
<view class="mm-line">课程名称{{info.course_name || '-'}}</view>
<scroll-view class="mm-scroll" scroll-y>
<view class="ms-list" v-for="(e,i) in classList" :key="i">
<view class="ml-item" @click="selectClass(e)">
<image :class="[resetInfo.new_id==e.id?'mi-img':'mi-icon']" :src="resetInfo.new_id==e.id?'/static/images/icon/selected_987.png':''"></image>
<view class="mi-info">
<view>班级名称{{e.name || '-'}}</view>
<view>班级人数{{e.nums || 0}}</view>
<view>上课教练{{e.coach || '-'}}</view>
</view>
</view>
</view>
</scroll-view>
<view class="mm-btn" hover-class="hover-active" @click="confirmReset">确定</view>
</view>
</view>
</view>
</template>
<script>
import util from '@/utils/util';
export default {
props:{
info: {
type: Object,
default: ()=>({})
},
canlist: {
type: Array,
default() {
return [];
}
},
isShowClass: {
type: Boolean,
default: false
}
},
computed: {
// userInfo(){
// return uni.getStorageSync('loginInfo') || {};
// },
classList(){
let { info, canlist } = this
let _list = []
if(canlist.length > 0)_list = canlist.filter(e=>e.id != info.sclass_id);
return _list || [];
},
},
data() {
return {
resetInfo:{
new_id: "", //ID
// user_id: "",
// user_order_no: "",
},
}
},
methods: {
moveHandle(){},
selectClass(item){
this.resetInfo.new_id = item.id
},
closeReset(){
this.$emit('closeReset')
},
confirmReset(){
let { resetInfo, classList } = this
if(classList.length == 0)return util.showNone('无可选班级!');
if(resetInfo.new_id == '')return util.showNone('请选择!');
this.$emit('confirmReset',resetInfo)
},
},
}
</script>
<style lang="scss">
@import '~style/public.scss';
.class-reset-modal{
.fixed-mask{
z-index: 999;
}
.mask-modal{
position: absolute;
left: 50%;
top: 45%;
transform: translate(-50%, -50%);
width: 558rpx;
padding: 20rpx 0;
border-radius: 10rpx;
background-color: #FFFFFF;
.mm-close{
@include centerFlex(flex-end);
>image{
margin-right: 22rpx;
width: 36rpx;
height: 36rpx;
}
}
.mm-tit{
margin: 6rpx auto 40rpx;
color: #1A1A1A;
font-size: 36rpx;
font-weight: 700;
text-align: center;
}
.mm-line{
flex-grow: 1;
margin: 0 38rpx;
color: #1A1A1A;
font-size: 32rpx;
padding-bottom: 20rpx;
border-bottom: 2rpx solid #E5E5E5;
}
.mm-scroll{
margin-left: 42rpx;
height: 368rpx;
.ms-list{
.ml-item{
padding-top: 28rpx;
display: flex;
align-items: center;
justify-content: flex-start;
>image{
flex-shrink: 0;
margin-right: 24rpx;
margin-top: 14rpx;
}
.mi-icon{
width: 36rpx;
height: 36rpx;
border-radius: 50%;
border: 2rpx solid #9C9C9F;
}
.mi-img{
width: 40rpx;
height: 40rpx;
}
.mi-info{
>view{
color: #1A1A1A;
font-size: 28rpx;
line-height: 52rpx;
}
}
}
}
}
.mm-btn{
margin: 60rpx auto;
padding: 18rpx 0;
width: 240rpx;
border-radius: 6rpx;
background-color: #009874;
color: #FFFFFF;
font-size: 32rpx;
text-align: center;
}
}
}
</style>

143
src/subpackage/course/components/manage/bclass_reserve_item/bclass_reserve_item.vue

@ -0,0 +1,143 @@
<template>
<!-- 大班约课记录 -->
<view class="bclass-reserve-item" @click="toDetail">
<view class="bri-line">
<view class="bl-store">{{orderInfo.brand_name ||''}}({{orderInfo.stadium_name||'-'}})</view>
<view class="bl-status" :class="[orderInfo.status_text=='已上课'?'status-gray':orderInfo.status_text=='已取消'?'status-red':'']">{{orderInfo.status_text||'-'}}</view>
</view>
<view class="bri-view">已预约人数<text>{{orderInfo.bclass_nums||0}}上课区间{{orderInfo.bclass_nums_min||0}}-{{orderInfo.bclass_nums_max||0}}</text></view>
<view class="bri-view">上课时间<text>{{ formatDate({date: orderInfo.date, partition: '.'}) || ''}}{{orderInfo.week||'-'}} {{orderInfo.start_duration||''}}-{{orderInfo.end_duration||''}}</text></view>
<view class="bri-view">上课地点<text>{{orderInfo.addr||''}}</text></view>
<view class="bri-view">上课教练<text>{{orderInfo.coach_name||''}}</text></view>
<view class="bri-view">课程名称<text>{{orderInfo.course_name||''}}</text></view>
<block v-if="orderInfo.status_text=='已取消'">
<view class="bri-view">取消时间<text>{{orderInfo.cancel_time||''}}</text></view>
<view class="bri-view">取消原因<text>{{orderInfo.cancel_reason||''}}</text></view>
</block>
<view class="bri-btn" v-if="orderInfo.status_text=='待确认'||orderInfo.status_text=='待上课'">
<view class="bb-btn1" hover-class="hover-active" @click.stop="isCancelClass=true">取消上课</view>
<view class="bb-btn2" hover-class="hover-active" @click.stop="confirmClass" v-if="orderInfo.status_text=='待确认'">确认上课</view>
</view>
<!-- 弹框 取消上课 -->
<cancel-class-modal
v-if="isCancelClass"
@reasonConfirm="reasonConfirm"
@reasonClose="isCancelClass=false"
></cancel-class-modal>
</view>
</template>
<script>
import util from '@/utils/util';
import cancel_class_modal from '../../cancel_class_modal/cancel_class_modal.vue';
export default {
components: {
'cancel-class-modal': cancel_class_modal,
},
props:{
orderInfo: {
type: Object,
default: ()=>({})
},
},
computed: {
},
data(){
return {
isCancelClass: false,
}
},
methods: {
formatDate: util.formatDate,
toDetail(){
let { orderInfo } = this
util.routeTo(`/subpackage/course/pages/manage_detail/bclass_reserve_detail/bclass_reserve_detail?id=${orderInfo.id}`,'nT');
},
//
reasonConfirm(e){
this.isCancelClass = false
let { orderInfo } = this
let _info = {
id: orderInfo.id,
reason: e,
course_kind: orderInfo.course_kind,
}
this.$emit('bclassCancel', _info)
},
//
confirmClass(){
let { orderInfo } = this
this.$emit('bclassConfirm', orderInfo.subscribe_no)
},
},
}
</script>
<style lang="scss">
@import '~style/public.scss';
.bclass-reserve-item{
padding: 26rpx 44rpx 34rpx;
background-color: #FFFFFF;
.bri-line{
margin-bottom: 20rpx;
padding: 0 0 32rpx 2rpx;
border-bottom: 2rpx solid #E5E5E5;
@include centerFlex(space-between);
.bl-store{
color: #1A1A1A;
font-size: 28rpx;
line-height: 40rpx;
font-weight: 700;
@include textHide(1);
}
.bl-status{
flex-shrink: 0;
color: #009874;
font-size: 28rpx;
line-height: 40rpx;
}
.status-gray{
color: #9C9C9F;
}
.status-red{
color: #EA5061;
}
}
.bri-view{
color: #9C9C9F;
font-size: 28rpx;
line-height: 52rpx;
>text{
color: #1A1A1A;
}
}
.bri-btn{
margin-top: 18rpx;
@include centerFlex(flex-end);
>view{
margin-left: 20rpx;
padding: 20rpx 0;
width: 192rpx;
border-radius: 6rpx;
font-size: 28rpx;
line-height: 40rpx;
font-weight: 700;
text-align: center;
}
.bb-btn1{
color: #EA5061;
border: 2rpx solid #EA5061;
background-color: #FFFFFF;
}
.bb-btn2{
color: #FFFFFF;
border: 2rpx solid #009874;
background-color: #009874;
}
}
}
</style>

93
src/subpackage/course/components/manage/class_manage_item/class_manage_item.vue

@ -0,0 +1,93 @@
<template>
<!-- 班级管理 -->
<view class="class-manage-item" @click="toDetail">
<view class="cmi-line">
<view class="cl-store">{{orderInfo.barnd_name||''}}({{orderInfo.stadium_name||'-'}})</view>
<view class="cl-status" :class="[orderInfo.sclass_status_text=='已结束'?'status-gray':orderInfo.sclass_status_text=='成班失败'?'status-red':'']">{{orderInfo.sclass_status_text||'-'}}</view>
</view>
<block v-if="orderInfo.sclass_status_text=='成班中'||orderInfo.sclass_status_text=='成班失败'">
<view class="cmi-view">课程名称<text>{{orderInfo.course_name||''}}</text></view>
<view class="cmi-view">班级id<text>{{orderInfo.sclass_no||''}}</text></view>
<view class="cmi-view">成班人数区间<text>{{orderInfo.sclass_min_nums||0}}-{{orderInfo.sclass_max_nums||0}}</text></view>
<view class="cmi-view">已报名人数<text>{{orderInfo.sclass_nums||0}}</text></view>
<view class="cmi-view">报名截止时间<text>{{orderInfo.sclass_join_deadline||''}}</text></view>
</block>
<block v-if="orderInfo.sclass_status_text=='进行中'||orderInfo.sclass_status_text=='已结束'">
<view class="cmi-view">班级名称<text>{{orderInfo.sclass_name||''}}</text></view>
<view class="cmi-view">班级人数<text>{{orderInfo.sclass_nums||0}}</text></view>
<view class="cmi-view">上课教练<text>{{orderInfo.sclass_coach||''}}</text></view>
<view class="cmi-view">上课地点<text>{{orderInfo.sclass_addr||''}}</text></view>
<view class="cmi-view">课程日期<text>{{orderInfo.sclass_stime||''}}-{{orderInfo.sclass_etime||''}}</text></view>
<view class="cmi-view">课程名称<text>{{orderInfo.course_name||''}}</text></view>
</block>
</view>
</template>
<script>
import util from '@/utils/util';
export default {
props:{
orderInfo: {
type: Object,
default: ()=>({})
},
},
computed: {
},
data(){
return {
}
},
methods: {
toDetail(){
let { orderInfo } = this
util.routeTo(`/subpackage/course/pages/manage_detail/class_manage_detail/class_manage_detail?id=${orderInfo.id}`,'nT');
},
},
}
</script>
<style lang="scss">
@import '~style/public.scss';
.class-manage-item{
padding: 26rpx 44rpx 30rpx;
background-color: #FFFFFF;
.cmi-line{
margin-bottom: 20rpx;
padding: 0 0 32rpx 2rpx;
border-bottom: 2rpx solid #E5E5E5;
@include centerFlex(space-between);
.cl-store{
color: #1A1A1A;
font-size: 28rpx;
line-height: 40rpx;
font-weight: 700;
@include textHide(1);
}
.cl-status{
flex-shrink: 0;
color: #009874;
font-size: 28rpx;
line-height: 40rpx;
}
.status-gray{
color: #9C9C9F;
}
.status-red{
color: #EA5061;
}
}
.cmi-view{
color: #9C9C9F;
font-size: 28rpx;
line-height: 52rpx;
>text{
color: #1A1A1A;
}
}
}
</style>

125
src/subpackage/course/components/manage/private_reserve_item/private_reserve_item.vue

@ -0,0 +1,125 @@
<template>
<!-- 私教约课记录 -->
<view class="private-reserve-item" @click="toDetail">
<view class="pri-line">
<view class="pl-store">{{orderInfo.brand_name||''}}({{orderInfo.stadium_name||'-'}})</view>
<view class="pl-status" :class="[orderInfo.status_text=='已上课'?'status-gray':orderInfo.status_text=='已取消'?'status-red':'']">{{orderInfo.status_text||'-'}}</view>
</view>
<view class="pri-view">学员信息<text>{{orderInfo.student_name||'-'}}{{orderInfo.student_gender||'-'}} {{orderInfo.student_phone||''}}</text></view>
<view class="pri-view">上课时间<text>{{ formatDate({date: orderInfo.date, partition: '.'}) || ''}}{{orderInfo.week||'-'}} {{orderInfo.start_duration||''}}-{{orderInfo.end_duration||''}}</text></view>
<view class="pri-view">上课地点<text>{{orderInfo.addr||''}}</text></view>
<view class="pri-view">上课教练<text>{{orderInfo.coach_name||''}}</text></view>
<view class="pri-view">课程名称<text>{{orderInfo.course_name||''}}</text></view>
<block v-if="orderInfo.status_text=='已取消'">
<view class="pri-view">取消时间<text>{{orderInfo.cancel_time||''}}</text></view>
<view class="pri-view">取消原因<text>{{orderInfo.cancel_reason||''}}</text></view>
</block>
<view class="pri-btn" v-if="orderInfo.status_text=='待上课'">
<view hover-class="hover-active" @click.stop="isCancelClass=true">取消上课</view>
</view>
<cancel-class-modal
v-if="isCancelClass"
@reasonConfirm="reasonConfirm"
@reasonClose="isCancelClass=false"
></cancel-class-modal>
</view>
</template>
<script>
import util from '@/utils/util';
import cancel_class_modal from '../../cancel_class_modal/cancel_class_modal.vue';
export default {
components: {
'cancel-class-modal': cancel_class_modal,
},
props:{
orderInfo: {
type: Object,
default: ()=>({})
},
},
computed: {
},
data(){
return {
isCancelClass: false,
}
},
methods: {
formatDate: util.formatDate,
toDetail(){
let { orderInfo } = this
util.routeTo(`/subpackage/course/pages/manage_detail/private_reserve_detail/private_reserve_detail?id=${orderInfo.id}`,'nT');
},
reasonConfirm(e){
this.isCancelClass = false
let { orderInfo } = this
let _info = {
subscribe_no: orderInfo.subscribe_no,
reason: e,
course_kind: orderInfo.course_kind,
}
this.$emit('priclassCancel', _info)
},
},
}
</script>
<style lang="scss">
@import '~style/public.scss';
.private-reserve-item{
padding: 26rpx 44rpx 30rpx;
background-color: #FFFFFF;
.pri-line{
margin-bottom: 20rpx;
padding: 0 0 32rpx 2rpx;
border-bottom: 2rpx solid #E5E5E5;
@include centerFlex(space-between);
.pl-store{
color: #1A1A1A;
font-size: 28rpx;
line-height: 40rpx;
font-weight: 700;
@include textHide(1);
}
.pl-status{
flex-shrink: 0;
color: #009874;
font-size: 28rpx;
line-height: 40rpx;
}
.status-gray{
color: #9C9C9F;
}
.status-red{
color: #EA5061;
}
}
.pri-view{
color: #9C9C9F;
font-size: 28rpx;
line-height: 52rpx;
>text{
color: #1A1A1A;
}
}
.pri-btn{
margin-top: 6rpx;
@include centerFlex(flex-end);
>view{
padding: 20rpx 0;
width: 192rpx;
border-radius: 6rpx;
border: 2rpx solid #EA5061;
color: #EA5061;
font-size: 28rpx;
line-height: 40rpx;
font-weight: 700;
text-align: center;
}
}
}
</style>

86
src/subpackage/course/components/manage/student_course_item/student_course_item.vue

@ -0,0 +1,86 @@
<template>
<!-- 学员课程 -->
<view class="student-course-item" @click="toDetail">
<view class="sci-status" :class="[orderInfo.order_status==2?'status-gray':orderInfo.order_status==3?'status-red':'']">{{zh_order_status()}}</view>
<view class="sci-view">学员信息<text>{{orderInfo.student_name || '-'}}{{orderInfo.student_gender||'-'}} {{orderInfo.student_phone||''}}</text></view>
<view class="sci-view">剩余课时<text>{{orderInfo.period_nums_surplus||0}}课时 {{orderInfo.period_nums_use||0}}/{{orderInfo.period_nums||0}})</text></view>
<view class="sci-view">课程名称<text>{{orderInfo.course_name||''}}</text></view>
<view class="sci-view">有效期至<text>{{ formatDate({date: orderInfo.course_end, partition: '.'}) || ''}}</text></view>
<view class="sci-view">课程类型<text>{{orderInfo.course_kind||''}}</text></view>
<view class="sci-view" v-if="orderInfo.course_kind!='成班课'">上课教练<text>{{orderInfo.course_coach||''}}</text></view>
</view>
</template>
<script>
import util from '@/utils/util';
export default {
props:{
orderInfo: {
type: Object,
default: ()=>({})
},
},
computed: {
},
data(){
return {
}
},
methods: {
formatDate: util.formatDate,
zh_order_status(){
let { orderInfo } = this
if(orderInfo.order_status==2)return "已结束";
if(orderInfo.order_status==3)return "已退款";
if(orderInfo.sclass_status){
if(orderInfo.sclass_status==0)return "成班中";
if(orderInfo.sclass_status==1)return "进行中";
if(orderInfo.sclass_status==2)return "已结束";
if(orderInfo.sclass_status==3)return "成班失败";
if(orderInfo.sclass_status==4)return "已退款";
}
return "进行中";
},
toDetail(){
// this.$emit('closeChange')
let { orderInfo } = this
util.routeTo(`/subpackage/course/pages/manage_detail/student_course_detail/student_course_detail?order_no=${orderInfo.order_no}`,'nT');
},
},
}
</script>
<style lang="scss">
@import '~style/public.scss';
.student-course-item{
position: relative;
padding: 30rpx 44rpx;
background-color: #FFFFFF;
.sci-status{
position: absolute;
top: 30rpx;
right: 44rpx;
font-size: 28rpx;
line-height: 40rpx;
color: #009874;
}
.status-gray{
color: #9A9A9D;
}
.status-red{
color: #EA5061;
}
.sci-view{
// flex-shrink: 0;
margin-right: 84rpx;
color: #9C9C9F;
font-size: 28rpx;
line-height: 52rpx;
>text{
color: #1A1A1A;
}
}
}
</style>

114
src/subpackage/course/components/sclass_record_section/sclass_record_section.vue

@ -0,0 +1,114 @@
<template>
<view class="sclass-record-section">
<view class="cds-tab">
<view>课时编号</view>
<view>上课时间</view>
<view>考勤状态</view>
</view>
<view class="cds-list" v-for="(e,i) in recordInfo" :key="i">
<view class="cl-item">
<view>{{ e.user_lesson|| '-'}}</view>
<view>{{e.period_date!=''?formatDate({date: e.period_date, partition: '.'}) :'-'}}{{`${' '+e.period_start_time || ''}-${e.period_end_time || ''}` ||''}}</view>
<view
:class="[e.user_status==0?'ci-txt-green':
e.user_status==4?'ci-txt-red':'']"
>{{ zh_status(e.user_status)}}</view>
</view>
</view>
</view>
</template>
<script>
import util from '@/utils/util';
export default {
props:{
recordInfo: {
type: Array,
default() {
return [];
}
},
},
computed: {
},
data() {
return {
}
},
methods: {
formatDate: util.formatDate,
zh_status(status){
let arr = ["待上课","已上课","申请请假中","已请假","缺勤"]
return arr[status] || '';
},
}
}
</script>
<style lang="scss">
@import '~style/public.scss';
.sclass-record-section{
.cds-tab{
margin: 0 24rpx;
border: 2rpx solid #E8E8E8;
background-color: #F6F8F8;
@include centerFlex(flex-start);
>view{
height: 110rpx;
color: #1A1A1A;
font-size: 28rpx;
text-align: center;
@include centerFlex(center);
&:first-child {
width: 166rpx;
}
&:nth-child(2){
width: 338rpx;
border-left: 2rpx solid #E8E8E8;
border-right: 2rpx solid #E8E8E8;
}
&:nth-child(3){
width: 194rpx;
// border-right: 2rpx solid #E8E8E8;
}
}
}
.cds-list{
.cl-item{
margin: 0 24rpx;
border-left: 2rpx solid #E8E8E8;
border-right: 2rpx solid #E8E8E8;
border-bottom: 2rpx solid #E8E8E8;
background-color: #FFFFFF;
@include centerFlex(flex-start);
>view{
height: 110rpx;
color: #1A1A1A;
font-size: 28rpx;
text-align: center;
@include centerFlex(center);
&:first-child {
width: 166rpx;
}
&:nth-child(2){
width: 338rpx;
border-left: 2rpx solid #E8E8E8;
border-right: 2rpx solid #E8E8E8;
}
&:nth-child(3){
width: 194rpx;
}
}
.ci-txt-green{
color: #009874;
}
.ci-txt-red{
color: #EA5061;
}
}
}
}
</style>

95
src/subpackage/course/components/store_name/store_name.vue

@ -0,0 +1,95 @@
<template>
<picker class="store-name-picker" :range="storeList" range-key="name" @change="storeChange">
<view class="store-name" :style="{background:getThemeBg()}">
<image mode="aspectFit" :src="curStoreInfo.logo || ''" :class="[theme=='light'?'sn-logo':'sn-logo2']"></image>
<view class="sn-txt" :style="{color:getThemeColor()}">{{curStoreInfo.name || '-'}}</view>
<image :class="[theme=='light'?'sn-arrow':'sn-arrow2']" mode="aspectFit" :src="getThemeIcon()"></image>
</view>
</picker>
</template>
<script>
import { mapState } from 'vuex'
export default {
computed: {
...mapState({
storeList: state => state.device.storeList,
curStoreInfo: state => state.device.curStoreInfo,
}),
getThemeIcon(){
return ()=> {return (this.theme=='light')?'/subpackage/course/static/images/icon/arrow_white.png':'/subpackage/course/static/images/icon/arrow_black.png';}
},
getThemeColor(){
return ()=> {return (this.theme=='light')?'#FFFFFF':'#333333';}
},
getThemeBg(){
return ()=> {return (this.theme=='light')?'none':'none';} //
},
},
props: ["theme"],//, "light":
created() {
// console.log("store_name theme: ", this.theme);
},
methods: {
storeChange(e){
let { storeList, curStoreInfo } = this;
let _curSelect = storeList[e.detail.value];
if(_curSelect.id!==curStoreInfo.id)this.$store.commit('setStoreInfo', _curSelect);
console.log(e)
}
}
}
</script>
<style>
.store-name-picker{
width: 100%;
z-index: 5;
}
.store-name{
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-start;
padding: 0 40upx;
height: 100upx;
/* background-color: #fff; */
}
.sn-logo{
flex-grow: 0;
flex-shrink: 0;
margin-right: 20upx;
width: 52upx;
height: 52upx;
}
.sn-logo2{
flex-grow: 0;
flex-shrink: 0;
margin-right: 10upx;
width: 30upx;
height: 30upx;
}
.sn-txt{
/* flex-grow: 1; */
line-height: 40upx;
font-size: 28upx;
color: #1a1a1a;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.sn-arrow{
flex-shrink: 0;
flex-grow: 0;
margin-left: 20upx;
width: 28upx;
height: 28upx;
}
.sn-arrow2{
flex-shrink: 0;
flex-grow: 0;
margin-left: 20upx;
width: 20upx;
height: 20upx;
}
</style>

117
src/subpackage/course/components/tab_bar/tab_bar.vue

@ -0,0 +1,117 @@
<template>
<view class="tab-list">
<block v-for="(e,i) in tabList" :key="i">
<view :class="['tab_no', curTabID == e.id ? 'tab_yes':'']" @click="tabChange(e)">
<view class="tab-txt">{{e.name}}
<!-- <view class="tab-hint" v-if="e.hint"><text>{{hintValue}}</text></view> -->
</view>
</view>
</block>
</view>
</template>
<script>
const tabArr = [{name:'全部订单',id:0},{name:'待上课',id:1},{name:'已上课',id:2},{name:'已取消',id:3}];
export default {
name: 'tabBar',
props: {
tabList:{
type: Array,
default: ()=>(tabArr),
},
curTabID:{
type: Number,
default:0
},
hintValue:{
type: Number,
default:0
},
isBottomBorder: {
type: Boolean,
default: false
}
},
data(){
return {
// curTabID: 0
}
},
methods:{
tabChange(item){
// this.curTabID = ID;
this.$emit('change', item)
},
}
}
</script>
<style lang="scss">
@import '~style/public.scss';
.tab-list{
width: 750rpx;
background-color: #fff;
display: flex;
align-items: center;
justify-content: space-around;
.tab_no{
position: relative;
flex-shrink: 0;
flex-grow: 1;
height: 100rpx;
line-height: 100rpx;
text-align: center;
.tab-txt{
position: relative;
display: inline-block;
font-size: 28rpx;
font-weight: 700;
color: #9A9A9D;
&::after{
content: '';
transition: all .2s;
position: absolute;
left: 50%;
bottom: 2rpx;
transform: translateX(-50%) translateY(50%);
display: block;
// width: 70%;
width: 40rpx;
height: 10rpx;
border-radius: 5rpx;
background-color: #009874;
opacity: 0;
}
// .tab-hint{
// position: absolute;
// top: 20rpx;
// right: -36rpx;
// width: 36rpx;
// height: 36rpx;
// border-radius: 50%;
// background-color: #EA5061;
// display: flex;
// align-items: center;
// justify-content: space-around;
// >text{
// color: #FFFFFF;
// font-size: 24rpx;
// font-weight: 400;
// }
// }
}
&.tab_yes{
.tab-txt{
color: #009874;
&::after{
opacity: 1;
transform: translateX(-50%) translateY(0);
}
}
}
}
}
</style>

192
src/subpackage/course/components/task_section/task_section.vue

@ -0,0 +1,192 @@
<template>
<view class="task-section">
<view class="ts-list" v-for="(e,i) in taskList" :key="i">
<view class="tl-item">
<view class="ti-line">
<view>{{e.created_at!=''?formatDate({date: e.created_at}) : '-'}}</view>
<view v-if="taskType==''" :class="[e.work_status==0?'status-red':'status-gray']">{{e.work_status==0?'未完成':e.work_status==1?'已完成':'-'}}</view>
<view v-if="taskType=='finish'" class="status-gray">完成情况{{e.complete_nums||0}}/{{e.total_nums||0}}</view>
</view>
<view class="ti-desc" :class="e.expande?'desc-omit':''" :id="'noname'+i">
{{e.work_content || ''}}
<image class="td-img" @click="expandeChange(i)" v-if="e.isShowExpande" src="/subpackage/course/static/images/icon/arrow_green.png"></image>
</view>
<view class="ti-end">
<view hover-class="hover-active" @click="toTaskDetail(e)">查看</view>
</view>
</view>
</view>
</view>
</template>
<script>
import util from '@/utils/util';
export default {
props:{
// taskInfo: {
// type: Object,
// default: ()=>({})
// },
taskInfo:{
type: Array,
default() {
return [];
}
},
taskType:{
type: String,
default: '' //finish
},
},
computed: {
},
data() {
return {
taskList: [],
}
},
created() {
let _list = this.taskInfo || []
if(_list.length > 0){
_list.forEach((value,index)=>{
value['expande'] = false
value['isShowExpande'] = false
})
this.$nextTick(_=>{
this.taskList = _list
})
}
},
methods: {
formatDate: util.formatDate,
expandeChange(index){
this.taskList[index].isShowExpande = false
this.taskList[index].expande = false
},
async initDesc(){
let that = this
let { taskList } = this
for(var i in taskList){
let res = await this.getQueryResult(i);
if(res==1){
this.taskList[i].isShowExpande = true
this.taskList[i].expande = true
}
}
},
getQueryResult(i){
let that = this
return new Promise((rs,rj)=>{
/**
* 自定义组件需使用uni.createSelectorQuery().in(this)方法
* 页面上直接使用uni.createSelectorQuery()方法即可
* **/
let query = uni.createSelectorQuery().in(this); //wx.createSelectorQuery();
//id
let _id = "noname"+i
query.select('#noname'+i).boundingClientRect()
query.exec(function (res) {
console.log("获取节点信息:",res)
//res mjltest\
if (res[0].height / 20 > 3) {
rs(1)
} else {
rs(2)
}
rj(false)
})
})
},
toTaskDetail(item){
// let _query = {
// work_number: item.work_number,
// work_coach_name: item.work_coach_name,
// created_at: item.created_at,
// }
if(this.taskType == 'finish')return util.routeTo(`/subpackage/course/pages/task_finish_list/task_finish_list?work_number=${item.work_number}`,'nT');;
util.routeTo(`/subpackage/course/pages/task_detail/task_detail?id=${item.id}`,'nT');
},
// toTaskDetail(item){
// // util.routeTo(`/subpackage/manage/pages/student/task_detail/task_detail?query=${util.jsonStr(item)}`,'nT')
// // util.routeTo(`/subpackage/manage/pages/student/task_detail/task_detail?id=${item.id}`,'nT')
// },
}
}
</script>
<style lang="scss">
@import '~style/public.scss';
.task-section{
.ts-list{
margin-bottom: 24rpx;
.tl-item{
// flex-grow: 1;
padding: 30rpx 42rpx;
background-color: #FFFFFF;
.ti-line{
margin-bottom: 20rpx;
@include centerFlex(space-between);
>view{
&:first-child{
color: #1A1A1A;
font-size: 32rpx;
font-weight: 700;
line-height: 44rpx;
}
&:nth-child(2){
margin: 0 6rpx;
font-size: 28rpx;
line-height: 40rpx;
}
}
.status-gray{
color: #9C9C9F;
}
.status-red{
color: #EA5061;
}
}
.ti-desc{
position: relative;
color: #1A1A1A;
font-size: 28rpx;
line-height: 20px;
.td-img{
position: absolute;
bottom: 0;
right: 0;
width: 24rpx;
height: 24rpx;
}
}
.desc-omit{
@include textHide(3);
}
.ti-end{
margin-top: 30rpx;
@include centerFlex(flex-end);
>view{
margin-left: 20rpx;
padding: 12rpx 0;
width: 156rpx;
border-radius: 6rpx;
font-size: 28rpx;
line-height: 40rpx;
text-align: center;
color: #009874;
border: 2rpx solid #009874;
}
}
}
}
}
</style>

33
src/subpackage/course/js/course_api.js

@ -0,0 +1,33 @@
import { ORIGIN } from '../../../js/api';
export const COURSE_API = {
stadiumList:`${ORIGIN}/admin/stadium/list`, // 店铺列表
courseHome:`${ORIGIN}/admin/assistant/venueCourse/home`, //课程首页
stuCourse:`${ORIGIN}/admin/assistant/venueCourse/student`, //学员课程
stuInfo:`${ORIGIN}/admin/assistant/venueCourse/studentInfo`, //学员课程 - 详情
stuWorkInfo:`${ORIGIN}/admin/assistant/venueCourse/studentWorkInfo`, //学员作业详情
arrangeStuAcCoach:`${ORIGIN}/admin/assistant/venueCourse/arrangeStudentAcCoach`, //学员课程 - 详情 - 安排上课[私教课] - 获取教练
arrangeStuAc:`${ORIGIN}/admin/assistant/venueCourse/arrangeStudentAc`, //学员课程 - 详情 - 安排上课[私教课]
subscribePri:`${ORIGIN}/admin/assistant/venueCourse/subscribePri`, //约课记录 - 私教课
subscribeBclass:`${ORIGIN}/admin/assistant/venueCourse/subscribeBclass`, //约课记录 - 大班课
subscribeInfo:`${ORIGIN}/admin/assistant/venueCourse/subscribeInfo`, //约课记录 - 详情
setBclassAc:`${ORIGIN}/admin/assistant/venueCourse/setBclassAc`, //约课记录 - 大班课提前确认上课
cancelBclassAc:`${ORIGIN}/admin/assistant/venueCourse/cancelBclassAc`, //约课记录 - 大班课取消上课
cancelStuAc:`${ORIGIN}/admin/assistant/venueCourse/cancelStudentAc`, //约课记录 - 详情 - 某个学员取消上课
bclassStus:`${ORIGIN}/admin/assistant/venueCourse/bclassStudents`, //约课记录 - 详情 - 获取大班课可添加学员
bclassStuAdd:`${ORIGIN}/admin/assistant/venueCourse/bclassStudentAdd`, //约课记录 - 详情 - 大班课添加学员
sclassManage:`${ORIGIN}/admin/assistant/venueCourse/sclass`, //班级管理
sclassInfo:`${ORIGIN}/admin/assistant/venueCourse/sclassInfo`, //班级管理 - 详情
sclassConfirm:`${ORIGIN}/admin/assistant/venueCourse/sclassConfirm`, //班级管理 - 成班课确认成班
stuCanChangeSclass:`${ORIGIN}/admin/assistant/venueCourse/studentCanChangeSclass`, //班级管理 - 学员详情 - 可换班级列表
resetStuSclass:`${ORIGIN}/admin/assistant/venueCourse/resetStudentSclass`, //班级管理 - 学员详情 - 换班
sclassStuWork:`${ORIGIN}/admin/assistant/venueCourse/sclassStudentWork`, //班级管理 - 作业完成情况
sclassTimeChange:`${ORIGIN}/admin/assistant/venueCourse/sclassTimeChange`, //班级管理 - 修改上课时间
}
export default COURSE_API;

10
src/subpackage/course/js/course_server.js

@ -0,0 +1,10 @@
import { Server } from '../../../js/server';
class CourseServer extends Server {
constructor(props){
super(props)
}
}
export default new CourseServer();

409
src/subpackage/course/pages/arrange_class/arrange_class.vue

@ -0,0 +1,409 @@
<template>
<view class="arrange-class">
<view class="ac-info">
<view>{{pageInfo.brand_name || ''}}{{pageInfo.stadium_name ||'-'}}</view>
<view>课程名称{{pageInfo.course_name || '-'}}</view>
<view>上课教练{{pageInfo.course_coach || '-'}}</view>
<view>
<text>学员信息{{pageInfo.student_name || ''}}{{pageInfo.student_gender!=''?`${pageInfo.student_gender||''}`:''}} {{' '+(pageInfo.student_phone||'')}}</text>
<image src="/subpackage/course/static/images/icon/phone.png" @click="phoneCall(pageInfo.student_phone)"></image>
</view>
<view>剩余课时{{pageInfo.period_nums_surplus || 0}} {{pageInfo.period_nums || 0}})</view>
</view>
<view class="ac-section">
<view class="as-tit">{{pageInfo.next_class_number || '-'}}</view>
<!-- 上课教练 -->
<view class="as-line">
<view class="al-txt"><text>*</text>上课教练</view>
<picker class="al-picker" :value="coachName" :range="coachs" @change="coachBindChange">
<view class="al-view al-box">
<view>{{coachName || '请选择上课教练'}}</view>
<image src="/subpackage/course/static/images/icon/triangle.png"></image>
</view>
</picker>
</view>
<!-- 上课日期 -->
<view class="as-line">
<view class="al-txt"><text>*</text>上课日期</view>
<picker class="al-picker" mode="date" :value="submitInfo.date" @change="bindDateChange" >
<view class="al-view al-date">
<view class="ad-txt">{{submitInfo.date}}</view>
<view class="ad-img">
<image src="/subpackage/course/static/images/icon/calendar.png"></image>
</view>
</view>
</picker>
</view>
<!-- 上课时间 -->
<view class="as-line">
<view class="al-txt"><text>*</text>上课时间</view>
<picker class="al-picker" :value="submitInfo.start" :range="timeList" @change="startBindChange">
<view class="al-view al-time">
<view>{{submitInfo.start}}</view>
<image src="/subpackage/course/static/images/icon/triangle.png"></image>
</view>
</picker>
<view class="al-to"></view>
<picker class="al-picker" :value="submitInfo.end" :range="timeList" @change="endBindChange">
<view class="al-view al-time">
<view>{{submitInfo.end}}</view>
<image src="/subpackage/course/static/images/icon/triangle.png"></image>
</view>
</picker>
</view>
<!-- 上课场地 -->
<view class="as-line" v-if="pageInfo.is_booking_venue == true">
<view class="al-txt"><text>*</text>上课场地<view class="at-type">({{pageInfo.course_venue_type || '-'}})</view></view>
<picker class="al-picker" :value="venueName" :range="venues" @change="venueBindChange">
<view class="al-view al-box">
<view>{{venueName || '请选择场地'}}</view>
<image src="/subpackage/course/static/images/icon/triangle.png"></image>
</view>
</picker>
</view>
</view>
<view class="ac-fixed">
<view hover-class="hover-active" @click="confirmChange">确定</view>
</view>
</view>
</template>
<script>
import util from '@/utils/util';
import boxServer from '../../js/course_server';
import BOX_API from '../../js/course_api';
import { mapState } from 'vuex';
export default {
computed: {
...mapState([ 'brandInfo',]),
timeList(){
let _list = []
for(let i = 0; i<24; i++){
_list.push(`${i<10?'0'+i:i}:00`)
}
return _list;
},
coachs(){
let { coachList } = this
let _arr = []
if(coachList.length > 0){
_arr = this.coachList.map(e=>e.name)
}
return _arr || [];
},
venues(){
let { venueList } = this
let _arr = []
if(venueList.length > 0){
_arr = this.venueList.map(e=>e.name)
}
return _arr || [];
},
},
data() {
return {
coachList: [], //
coachName: "", //
venueList: [], //
venueName: "", //
pageInfo: {},
submitInfo: {
coach_id: "",
date: "",
start: "",
end: "",
venue_id: "",
},
}
},
async onLoad(options){
// let _query = util.jsonPar(options.query);
// this.pageInfo = _query
this.getStuInfo(options.order_no)
},
methods: {
//
bindDateChange: function(e) {
let _selectDate = e.target.value
this.submitInfo.date = _selectDate
},
startBindChange(e){
console.log(e);
let _list = this.timeList || [];
this.submitInfo.start = _list[e.detail.value] || ''
},
endBindChange(e){
console.log(e);
let _list = this.timeList || [];
this.submitInfo.end = _list[e.detail.value] || ''
},
coachBindChange(e){
console.log(e);
let _list = this.coachs || [];
this.coachName = _list[e.detail.value] || ''
this.submitInfo.coach_id = this.coachList[e.detail.value].id
},
venueBindChange(e){
console.log(e);
let _list = this.venues || [];
this.venueName = _list[e.detail.value] || ''
this.submitInfo.venue_id = this.venueList[e.detail.value].id
},
phoneCall(mobile) {
let _phoneStr = mobile || ''
let _phoneArr = _phoneStr.split(',') || [];
uni.showActionSheet({
itemList: _phoneArr,
success: res =>{
uni.makePhoneCall({
phoneNumber: _phoneArr[res.tapIndex]
})
}
})
},
confirmChange(){
let { pageInfo, submitInfo } = this
let { coach_id, date, start, end, venue_id } = this.submitInfo
if(coach_id==''||date==""||start=="" ||end=="" || (pageInfo.is_booking_venue==true && venue_id==''))return util.showNone("请完善必选项!");
let nowTime = util.formatDate(new Date(),'-') // 2020-07-21
if(new Date(nowTime).getTime() > new Date(date).getTime())return util.showNone("上课日期不能小于当前系统日期!");
// 2021-09-01便
if(Date.parse('2021-09-01 '+ start) >= Date.parse('2021-09-01 '+ end))return util.showNone("上课时间的结束时间不能小于起始时间!");
let _data = {
order_no: pageInfo.order_no,
coach_id, //ID
date, //
start, //
end, //
}
if(pageInfo.is_booking_venue == true)_data["venue_id"] = venue_id; //ID
util.showLoad();
boxServer.get({
url: BOX_API.arrangeStuAc, //[]
data: _data,
failMsg: '加载失败!'
})
.then(res=>{
util.hideLoad();
util.showNone("操作成功!");
setTimeout(_=>{ uni.navigateBack() }, 1200)
})
},
// -
getStuInfo(order_no){
let { brandInfo } = this
util.showLoad();
boxServer.get({
url: BOX_API.stuInfo,
data: {
brand_id: brandInfo.brand.id,
order_no,
},
failMsg: '加载失败!'
})
.then(res=>{
util.hideLoad();
this.pageInfo = res
this.getArrangeAcCoach(res.order_no);
})
},
// [] -
getArrangeAcCoach(order_no){
let { brandInfo } = this
util.showLoad();
boxServer.get({
url: BOX_API.arrangeStuAcCoach,
data: {
brand_id: brandInfo.brand.id,
order_no,
},
failMsg: '加载失败!'
})
.then(res=>{
util.hideLoad();
this.coachList = res.coachs || []
this.venueList = res.venues || []
})
},
}
}
</script>
<style lang="scss">
@import '~style/public.scss';
page{
background: #F2F2F7;
}
.arrange-class{
position: relative;
padding-bottom: 150rpx;
.ac-info{
padding: 28rpx 22rpx 30rpx;
background-color: #FFFFFF;
>view{
color: #1A1A1A;
font-size: 28rpx;
line-height: 52rpx;
&:first-child{
font-weight: 700;
margin-bottom: 4rpx;
}
&+view{
@include centerFlex(flex-start);
>image{
margin-left: 20rpx;
flex-shrink: 0;
width: 32rpx;
height: 32rpx;
}
}
}
}
.ac-section{
margin: 12rpx 0;
padding: 30rpx 28rpx 50rpx;
background-color: #FFFFFF;
.as-tit{
padding-bottom: 4rpx;
color: #333333;
font-size: 36rpx;
font-weight: 700;
line-height: 50rpx;
}
.as-line{
margin: 20rpx 46rpx 0 6rpx;
@include centerFlex(flex-start);
.al-txt{
position: relative;
margin-right: 18rpx;
flex-shrink: 0;
color: #1A1A1A;
font-size: 28rpx;
line-height: 44rpx;
>text{
color: #EA5061;
font-size: 32rpx;
}
.at-type{
position: absolute;
bottom: -36rpx;
left: 0;
min-width: 130rpx;
text-align: center;
color: #9C9C9F;
font-size: 24rpx;
line-height: 34rpx;
}
}
.al-picker{
flex-grow: 1;
}
.al-view{
flex-grow: 1;
padding-left: 20rpx;
height: 76rpx;
border-radius: 10rpx;
background-color: #F2F2F7;
@include centerFlex(flex-start);
}
.al-box{
>view{
flex-grow: 1;
color: #9C9C9F;
font-size: 28rpx;
line-height: 40rpx;
}
>image{
margin: 0 18rpx;
flex-shrink: 0;
width: 22rpx;
height: 22rpx;
}
}
.al-date{
.ad-txt{
flex-grow: 1;
color: #1A1A1A;
font-size: 28rpx;
line-height: 40rpx;
border-right: 2rpx solid #D8D8D8;
}
.ad-img{
flex-shrink: 0;
height: 76rpx;
padding: 0 16rpx 0 18rpx;
@include centerFlex(center);
>image{
flex-shrink: 0;
width: 34rpx;
height: 34rpx;
}
}
}
.al-time{
>view{
flex-grow: 1;
color: #1A1A1A;
font-size: 28rpx;
line-height: 40rpx;
min-width: 80rpx;
}
>image{
margin: 0 18rpx;
flex-shrink: 0;
width: 22rpx;
height: 22rpx;
}
}
.al-to{
margin: 0 28rpx;
flex-shrink: 0;
color: #B2B2B2;
font-size: 32rpx;
line-height: 44rpx;
}
}
}
.ac-fixed{
position: fixed;
bottom: 0;
left: 0;
padding: 20rpx 0;
width: 750rpx;
background-color: #FFFFFF;
>view{
margin: 0 auto;
padding: 24rpx 0;
width: 670rpx;
border-radius: 44rpx;
background-color: #009874;
color: #FFFFFF;
font-size: 28rpx;
line-height: 40rpx;
text-align: center;
font-weight: 700;
}
}
}
</style>

226
src/subpackage/course/pages/class_add_student/class_add_student.vue

@ -0,0 +1,226 @@
<template>
<view class="add-student">
<view class="as-search">
<view class="as-box">
<image src="/static/images/icon/search.png"></image>
<input class="ab-ipt" placeholder="请输入学员姓名/电话查找" confirm-type="search" v-model="searchText" @change="searchChange"/>
</view>
</view>
<view class="as-name">课程名称{{pageInfo.course_name||'-'}}</view>
<view class="as-list" v-for="(e,i) in studentList" :key="i">
<view class="al-item" @click="itemChange(e,i)">
<image :class="[submitInfo.order_no==e.order_no?'ai-img':'ai-icon']" :src="submitInfo.order_no==e.order_no?'/static/images/icon/selected_987.png':''"></image>
<view class="ai-info">
<view>学员信息{{e.student_name||'-'}}{{e.student_gender || '-'}} {{userInfo.type==2?e.student_phone:''}}</view>
<view>剩余课时<text>{{e.course_period_nums_surplus || 0}}课时</text><text>{{e.course_period_nums_use||0}}/{{e.course_period_nums_total||0}})</text></view>
</view>
</view>
</view>
<view class="as-fixed">
<view hover-class="hover-active" @click="btnChange()">确认添加学员</view>
</view>
</view>
</template>
<script>
import util from '@/utils/util';
import boxServer from '../../js/course_server';
import BOX_API from '../../js/course_api';
import { mapState } from 'vuex';
export default {
computed:{
...mapState([ 'brandInfo',]),
},
data() {
return {
pageInfo: {},
studentList: [],
searchText: "",
submitInfo: {
order_no: "",
subscribe_no: "",
},
}
},
onLoad(options) {
this.submitInfo.subscribe_no = options.subscribe_no
this.getSubStudent(options.subscribe_no)
},
methods: {
//
searchChange(e){
let { searchText, pageInfo } = this
if(searchText == '')return this.studentList = pageInfo.students;
this.studentList = pageInfo.students.filter(e=>e.student_name.indexOf(searchText)!=-1||e.student_phone.indexOf(searchText)!=-1 );
},
itemChange(info){
this.submitInfo.order_no = info.order_no
},
btnChange(){
if(this.submitInfo.order_no=='')return util.showNone('请选择需要添加的学员!');
util.showModal({
title: '是否确认添加学员上课?',
content: '',
showCancel: true,
success: modalRes=>{
if(modalRes.confirm){
this.getSubAdd()
}
}
})
},
//
getSubAdd(){
let { brandInfo } = this
let { order_no, subscribe_no} = this.submitInfo
util.showLoad();
boxServer.get({
url: BOX_API.bclassStuAdd,
data: {
brand_id: brandInfo.brand.id,
order_no,
subscribe_no,
},
failMsg: '加载失败!'
})
.then(res=>{
util.hideLoad();
util.showNone('操作成功!');
setTimeout(_=>{
this.getSubStudent(subscribe_no);
util.previousPageFunction({
fnName: 'refreshList',
query: { isLoad: false},
});
},1200);
})
},
//
getSubStudent(subscribe_no){
let { brandInfo } = this
util.showLoad();
boxServer.get({
url: BOX_API.bclassStus,
data: {
brand_id: brandInfo.brand.id,
subscribe_no,
},
failMsg: '加载失败!'
})
.then(res=>{
util.hideLoad();
this.searchText = ""
this.submitInfo.order_no = ""
this.pageInfo = res || {};
this.studentList = this.pageInfo.students || []
})
},
}
}
</script>
<style lang="scss">
@import '~style/public.scss';
page{
background: #F2F2F7;
}
.add-student{
position: relative;
padding-bottom: 170rpx;
.as-search{
padding: 26rpx 24rpx;
background-color: #FFFFFF;
.as-box{
padding: 0rpx 20rpx;
height: 92rpx;
border-radius: 10rpx;
background-color: #F2F2F7;
@include centerFlex(flex-start);
>image{
flex-shrink: 0;
margin-right: 20rpx;
width: 40rpx;
height: 40rpx;
}
.ab-ipt{
flex-grow: 1;
color: #1A1A1A;
font-size: 32rpx;
}
}
}
.as-name{
margin-top: 2rpx;
padding: 26rpx 30rpx;
background-color: #FFFFFF;
color: #1A1A1A;
font-size: 28rpx;
font-weight: 700;
}
.as-list{
margin-top: 2rpx;
.al-item{
padding: 28rpx 40rpx;
display: flex;
justify-content: flex-start;
background-color: #FFFFFF;
>image{
flex-shrink: 0;
margin-right: 28rpx;
}
.ai-icon{
width: 36rpx;
height: 36rpx;
border-radius: 50%;
border: 2rpx solid #9A9A9D;
}
.ai-img{
width: 40rpx;
height: 40rpx;
}
.ai-info{
flex-grow: 1;
margin-top: -10rpx;
>view{
color: #1A1A1A;
font-size: 28rpx;
line-height: 52rpx;
>text{
&:first-child{
color: #009874;
}
&:nth-child(2){
color: #9C9C9F;
}
}
}
}
}
}
.as-fixed{
position: fixed;
bottom: 0;
left: 0;
padding: 22rpx 0;
width: 750rpx;
background-color: #FFFFFF;
border-top: 2rpx solid #F2F2F7;
>view{
margin: 0 auto;
padding: 34rpx 0;
width: 670rpx;
border-radius: 10rpx;
background-color: #009874;
color: #FFFFFF;
font-size: 32rpx;
line-height: 44rpx;
text-align: center;
}
}
}
</style>

242
src/subpackage/course/pages/class_student_detail/class_student_detail.vue

@ -0,0 +1,242 @@
<template>
<view class="class-student-detail">
<view class="csd-section">
<view class="cs-view">学员姓名{{pageInfo.student_name||'-'}}{{pageInfo.student_gender||'-'}} </view>
<!-- 只有教务才有 -->
<view class="cs-view">
<text>学员电话{{pageInfo.student_phone ||'-'}}</text>
<image src="/subpackage/course/static/images/icon/phone.png" @click="phoneCall(pageInfo.student_phone)"></image>
</view>
<view class="cs-view">剩余课时{{pageInfo.course_period_residue ||0}}课时 {{pageInfo.course_period_nums_use||0}}/{{pageInfo.course_period_nums||0}})</view>
<view class="cs-end" v-if="pageInfo.order_status==0||pageInfo.order_status==1">
<!-- 只有教务才有 -->
<view class="ce-btn1" hover-class="hover-active" @click="isShowClass=true" >转班</view>
</view>
</view>
<view class="csd-record">
<view class="cr-tit">上课记录</view>
<sclass-record-section :recordInfo="pageInfo.student_period_time"></sclass-record-section>
</view>
<!-- 转班弹框 -->
<class-reset-modal
:isShowClass="isShowClass"
:info="pageInfo"
:canlist="classList"
@confirmReset="confirmReset"
@closeReset="isShowClass=false"
></class-reset-modal>
</view>
</template>
<script>
import util from '@/utils/util';
import boxServer from '../../js/course_server';
import BOX_API from '../../js/course_api';
import { mapState } from 'vuex';
import class_reset_modal from '../../components/class_reset_modal/class_reset_modal.vue';
import sclass_record_section from '../../components/sclass_record_section/sclass_record_section.vue';
export default {
components: {
'class-reset-modal': class_reset_modal,
'sclass-record-section': sclass_record_section,
},
computed: {
...mapState([ 'brandInfo',]),
},
data() {
return {
pageInfo: {},
isShowClass: false,
classList: [],
resetInfo: {},
}
},
async onLoad(options){
let _query = util.jsonPar(options.query);
this.pageInfo = _query
this.$nextTick(_=>{
this.getCanlist(this.pageInfo.course_id);
})
},
methods: {
phoneCall(mobile) {
let _phoneStr = mobile || ''
let _phoneArr = _phoneStr.split(',') || [];
uni.showActionSheet({
itemList: _phoneArr,
success: res =>{
uni.makePhoneCall({
phoneNumber: _phoneArr[res.tapIndex]
})
}
})
},
//
confirmReset(e){
let { brandInfo, pageInfo } = this
util.showLoad();
boxServer.get({
url: BOX_API.resetStuSclass,
data: {
brand_id: brandInfo.brand.id,
old_id: pageInfo.sclass_id,
new_id: e.new_id,
user_id: pageInfo.user_id,
user_order_no: pageInfo.order_no,
},
failMsg: '加载失败!'
})
.then(res=>{
util.hideLoad();
util.showNone('操作成功!');
setTimeout(_=>{
uni.navigateBack({delta:2})
util.previousPageFunction({
fnName: 'refreshList',
query: { isLoad: false},
});
}, 1200)
})
},
//
getCanlist(course_id){
let { brandInfo, pageInfo } = this
util.showLoad();
boxServer.get({
url: BOX_API.stuCanChangeSclass,
data: {
brand_id: brandInfo.brand.id,
course_id,
},
failMsg: '加载失败!'
})
.then(res=>{
util.hideLoad();
this.classList = res.list || []
})
},
}
}
</script>
<style lang="scss">
@import '~style/public.scss';
page{
background: #FFFFFF;
}
.class-student-detail{
position: relative;
padding-bottom: 160rpx;
.csd-section{
padding: 24rpx 22rpx 0;
.cs-view{
color: #1A1A1A;
font-size: 28rpx;
line-height: 52rpx;
@include centerFlex(flex-start);
>image{
margin-left: 30rpx;
flex-shrink: 0;
width: 32rpx;
height: 32rpx;
}
}
.cs-end{
margin-top: 16rpx;
padding: 30rpx 0;
border-top: 2rpx solid #E5E5E5;
@include centerFlex(flex-end);
>view{
margin-left: 20rpx;
padding: 12rpx 0;
width: 156rpx;
text-align: center;
font-size: 28rpx;
line-height: 40rpx;
border-radius: 6rpx;
border: 2rpx solid #009874;
}
.ce-btn1{
color: #009874;
background-color: #FFFFFF;
}
.ce-btn2{
color: #FFFFFF;
background-color: #009874;
}
}
}
.csd-record{
padding-bottom: 26rpx;
border-top: 24rpx solid #F2F2F7;
.cr-tit{
padding: 26rpx 22rpx;
color: #1A1A1A;
font-size: 28rpx;
line-height: 52rpx;
}
.cds-tab{
margin: 0 24rpx;
border: 2rpx solid #E8E8E8;
background-color: #F6F8F8;
@include centerFlex(flex-start);
>view{
height: 110rpx;
color: #1A1A1A;
font-size: 28rpx;
text-align: center;
@include centerFlex(center);
&:first-child {
width: 166rpx;
}
&:nth-child(2){
width: 338rpx;
border-left: 2rpx solid #E8E8E8;
border-right: 2rpx solid #E8E8E8;
}
&:nth-child(3){
width: 194rpx;
// border-right: 2rpx solid #E8E8E8;
}
}
}
.cds-list{
.cl-item{
margin: 0 24rpx;
border-left: 2rpx solid #E8E8E8;
border-right: 2rpx solid #E8E8E8;
border-bottom: 2rpx solid #E8E8E8;
background-color: #FFFFFF;
@include centerFlex(flex-start);
>view{
height: 110rpx;
color: #1A1A1A;
font-size: 28rpx;
text-align: center;
@include centerFlex(center);
&:first-child {
width: 166rpx;
}
&:nth-child(2){
width: 338rpx;
border-left: 2rpx solid #E8E8E8;
border-right: 2rpx solid #E8E8E8;
}
&:nth-child(3){
width: 194rpx;
}
}
.ci-txt-green{
color: #009874;
}
.ci-txt-red{
color: #EA5061;
}
}
}
}
}
</style>

250
src/subpackage/course/pages/class_time_change/class_time_change.vue

@ -0,0 +1,250 @@
<template>
<view class="class-time-change">
<view class="ctc-info">
<view class="ci-weight">{{pageInfo.brand_name||'-'}}</view>
<view>课程名称{{pageInfo.course_name||'-'}}</view>
<view>所在班级{{pageInfo.sclass_name||'-'}}</view>
</view>
<view class="ctc-section">
<view class="cs-tit">{{pageInfo.lesson_no|| 0}}</view>
<view class="cs-line">
<view class="cl-txt"><text>*</text>上课日期</view>
<picker class="cl-picker" mode="date" :value="pageInfo.period_date" @change="bindDateChange" >
<view class="cl-box">
<view class="cb-txt">{{pageInfo.period_date}}</view>
<view class="cb-img">
<image src="/subpackage/course/static/images/icon/calendar.png"></image>
</view>
</view>
</picker>
</view>
<view class="cs-line">
<view class="cl-txt"><text>*</text>上课时间</view>
<picker class="cl-picker" :value="pageInfo.period_start_time" :range="timeList" @change="startBindChange">
<view class="cl-view">
<view>{{pageInfo.period_start_time}}</view>
<image src="/subpackage/course/static/images/icon/triangle.png"></image>
</view>
</picker>
<view class="cl-to"></view>
<picker class="cl-picker" :value="pageInfo.period_end_time" :range="timeList" @change="endBindChange">
<view class="cl-view">
<view>{{pageInfo.period_end_time}}</view>
<image src="/subpackage/course/static/images/icon/triangle.png"></image>
</view>
</picker>
</view>
</view>
<view class="fixed-btn">
<view hover-class="hover-active" @click="sclassTimeChange">确认更改</view>
</view>
</view>
</template>
<script>
import util from '@/utils/util';
import boxServer from '../../js/course_server';
import BOX_API from '../../js/course_api';
import { mapState } from 'vuex';
export default {
computed: {
...mapState([ 'brandInfo',]),
timeList(){
let _list = []
for(let i = 0; i<24; i++){
_list.push(`${i<10?'0'+i:i}:00`)
}
return _list;
},
},
data() {
return {
pageInfo: {
period_date: "",
period_start_time: "",
period_end_time: "",
},
}
},
onLoad(options){
let _query = util.jsonPar(options.query);
this.pageInfo = _query
},
methods: {
//
bindDateChange: function(e) {
let _selectDate = e.target.value
this.pageInfo.period_date = _selectDate
},
startBindChange(e){
console.log(e);
let _list = this.timeList || [];
this.pageInfo.period_start_time = _list[e.detail.value] || ''
},
endBindChange(e){
console.log(e);
let _list = this.timeList || [];
this.pageInfo.period_end_time = _list[e.detail.value] || ''
},
//
sclassTimeChange(){
let { pageInfo, brandInfo } = this
if(pageInfo.period_date==''|| pageInfo.period_start_time==''|| pageInfo.period_end_time=='')return util.showNone("请完善必选项!");
let nowTime = util.formatDate(new Date(),'-') // 2020-07-21
if(new Date(nowTime).getTime() > new Date(pageInfo.period_date).getTime())return util.showNone("上课日期不能小于当前系统日期!");
// 2021-09-01便
if(Date.parse('2021-09-01 '+ pageInfo.period_start_time) >= Date.parse('2021-09-01 '+ pageInfo.period_end_time))return util.showNone("上课时间的结束时间不能小于起始时间!");
this.$emit('closeChange')
util.showLoad();
boxServer.get({
url: BOX_API.sclassTimeChange,
data: {
brand_id: brandInfo.brand.id,
id: pageInfo.sclass_id,
lesson_no: pageInfo.lesson_no,
period_date: pageInfo.period_date,
period_start_time: pageInfo.period_start_time,
period_end_time: pageInfo.period_end_time,
},
failMsg: '加载失败!'
})
.then(res=>{
util.hideLoad();
util.showNone('操作成功!');
setTimeout(_=>{
uni.navigateBack()
util.previousPageFunction({
fnName: 'refreshList',
query: { isLoad: false},
});
}, 1200)
})
},
}
}
</script>
<style lang="scss">
@import '~style/public.scss';
page{
background: #F2F2F7;
}
.class-time-change{
position: relative;
padding-bottom: 160rpx;
.ctc-info{
padding: 24rpx 22rpx;
background-color: #FFFFFF;
>view{
color: #1A1A1A;
font-size: 28rpx;
line-height: 52rpx;
}
.ci-weight{
font-weight: 700;
}
}
.ctc-section{
margin: 12rpx 0;
padding: 30rpx 56rpx 58rpx 28rpx;
background-color: #FFFFFF;
.cs-tit{
color: #333333;
font-size: 36rpx;
font-weight: 700;
line-height: 50rpx;
margin-bottom: 16rpx;
}
.cs-line{
margin-top: 32rpx;
@include centerFlex(flex-start);
.cl-txt{
margin-right: 20rpx;
flex-shrink: 0;
color: #1A1A1A;
font-size: 32rpx;
line-height: 44rpx;
>text{
color: #EA5061;
}
}
.cl-box{
flex-grow: 1;
padding-left: 20rpx;
height: 88rpx;
border-radius: 10rpx;
background-color: #F2F2F7;
@include centerFlex(flex-start);
.cb-txt{
flex-grow: 1;
border-right: 2rpx solid #D8D8D8;
color: #1A1A1A;
font-size: 28rpx;
}
.cb-img{
height: 88rpx;
padding: 0 22rpx 0 18rpx;
@include centerFlex(center);
>image{
width: 34rpx;
height: 34rpx;
}
}
}
.cl-picker{
flex-grow: 1;
}
.cl-view{
padding-left: 20rpx;
flex-grow: 1;
height: 88rpx;
border-radius: 10rpx;
background-color: #F2F2F7;
@include centerFlex(flex-end);
>view{
flex-grow: 1;
color: #1A1A1A;
font-size: 28rpx;
min-width: 80rpx;
}
>image{
margin: 0 18rpx;
flex-shrink: 0;
width: 22rpx;
height: 22rpx;
}
}
.cl-to{
margin: 0 26rpx;
flex-shrink: 0;
color: #B2B2B2;
font-size: 32rpx;
line-height: 44rpx;
}
}
}
.fixed-btn{
position: fixed;
bottom: 0;
left: 0;
width: 750rpx;
padding: 20rpx 0 24rpx;
background-color: #FFFFFF;
z-index: 2;
>view{
margin: auto;
padding: 24rpx 0;
width: 670rpx;
border-radius: 10rpx;
background-color: #009874;
color: #FFFFFF;
font-size: 28rpx;
text-align: center;
font-weight: 700;
}
}
}
</style>

242
src/subpackage/course/pages/index/index.vue

@ -0,0 +1,242 @@
<template>
<view class="index-content">
<view class="ic-head">
<store-name :theme="'light'"></store-name>
</view>
<view class="ic-section">
<view class="is-box">
<view class="ib-line">
<view class="il-view">
<view>进行中</view>
<view>{{pageInfo.ongoing_nums || 0}}</view>
</view>
<view class="il-cut"></view>
<view class="il-view">
<view>已结束</view>
<view>{{pageInfo.ended_nums || 0}}</view>
</view>
</view>
<view class="ib-line">
<view class="il-view">
<view>今日待上课</view>
<view>{{pageInfo.dsk_nums || 0}}</view>
</view>
<view class="il-cut"></view>
<view class="il-view">
<view>今日已上课</view>
<view>{{pageInfo.ysk_nums || 0}}</view>
</view>
</view>
</view>
</view>
<view class="ic-tablist" v-for="(e,i) in tabList" :key="i">
<view class="it-title">{{e.type}}</view>
<view class="it-tabs">
<view class="itt-item" v-for="(item,index) in e.itemList" :key="index" @click="tabChange(e.id,item.id)">
<view>
<image :src="'/subpackage/course/static/images/tab/tab_'+ e.id +'_'+ item.id + '.png'"></image>
<view>{{item.name}}</view>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
import util from '@/utils/util';
import boxServer from '../../js/course_server';
import BOX_API from '../../js/course_api';
import { mapState } from 'vuex'
import store_name from '../../components/store_name/store_name';
const tabList = [
{
type: "更多操作",
id: 0,
itemList:[
{
id: 0,
name: "学员课程",
},
{
id: 1,
name: "私教课上课记录",
},
{
id: 2,
name: "大班上课记录",
},
{
id: 3,
name: "班级管理",
},
],
},
]
export default {
components: {
'store-name': store_name
},
computed:{
...mapState([ 'brandInfo',]),
...mapState({
curStoreInfo: state => state.device.curStoreInfo,
}),
},
watch: {
curStoreInfo(newVal, oldVal) {
this.pageInfo = {};
this.getCourseHome({
stadium_id: newVal.id,
})
}
},
data() {
return {
tabList,
pageInfo: {},
isShowAgain: false,
}
},
async onLoad(){
// console.log("",this.brandInfo)
try{
util.showLoad();
let _brandInfo = await this.$store.dispatch('getBrandInfo');
await this.$store.dispatch('getStoreList');
this.getCourseHome({
stadium_id: this.curStoreInfo.id,
})
util.hideLoad();
}catch(err){
util.hideLoad();
}
},
onShow() {
if(this.isShowAgain)this.getCourseHome({stadium_id: this.curStoreInfo.id,});
},
methods: {
tabChange(id1,id2){
// if(path=='')return util.showNone('');
let _type_id = `${id1}-${id2}`
util.routeTo(`/subpackage/course/pages/manage_list/manage_list?type_id=${_type_id}`,'nT')
},
getCourseHome({stadium_id}){
let { brandInfo } = this
// util.showLoad();
boxServer.get({
url: BOX_API.courseHome,
data: {
brand_id: brandInfo.brand.id,
stadium_id,
},
failMsg: '加载失败!'
})
.then(res=>{
// util.hideLoad();
this.pageInfo = res
this.isShowAgain = true
})
.catch(util.hideLoad)
},
}
}
</script>
<style lang="scss">
@import '~style/public.scss';
page{
background: #FFFFFF;
}
.index-content{
.ic-head{
width: 750rpx;
height: 218rpx;
background-color: #009874;
}
.ic-section{
position: relative;
width: 750rpx;
height: 320rpx;
background-color: #F2F2F7;
.is-box{
position: absolute;
top: -100rpx;
left: 40rpx;
width: 670rpx;
padding-bottom: 50rpx;
// height: 378rpx;
border-radius: 10rpx;
background-color: #FFFFFF;
.ib-line{
padding: 60rpx 0 10rpx;
@include centerFlex(flex-start);
.il-view{
margin-left: 60rpx;
flex-grow: 1;
>view{
&:first-child{
color: #9C9C9F;
font-size: 28rpx;
line-height: 40rpx;
}
&:nth-child(2){
color: #1A1A1A;
font-size: 40rpx;
line-height: 56rpx;
font-weight: 700;
}
}
}
.il-cut{
flex-shrink: 0;
width: 2rpx;
height: 92rpx;
background-color: #D8D8D8;
}
}
}
}
.ic-tablist{
padding-bottom: 24rpx;
background: #FFFFFF;
.it-title{
padding: 52rpx 40rpx 0rpx;
color: #1A1A1A;
font-size: 28rpx;
line-height: 40rpx;
}
.it-tabs{
display: flex;
flex-wrap: wrap;
.itt-item{
margin: 60rpx 0 20rpx;
flex-wrap: wrap;
flex-shrink: 0;
width: 33.33%;
>view{
display: flex;
flex-direction: column;
flex-wrap: wrap;
justify-content: center;
align-items: center;
>image{
width: 52rpx;
height: 52rpx;
}
>view{
margin-top: 20rpx;
color: #1A1A1A;
font-size: 28rpx;
line-height: 40rpx;
text-align: center;
}
}
}
}
}
}
</style>

396
src/subpackage/course/pages/manage_detail/bclass_reserve_detail/bclass_reserve_detail.vue

@ -0,0 +1,396 @@
<template>
<view class="bclass-reserve-detail">
<view class="brd-line">
<view>{{pageInfo.brand_name||''}}({{pageInfo.stadium_name||'-'}})</view>
<view :class="[pageInfo.status_text=='已上课'?'bl-gray':pageInfo.status_text=='已取消'?'bl-red':'bl-green']">{{pageInfo.status_text||'-'}}</view>
</view>
<view class="brd-section">
<view>预约单号<text>{{pageInfo.subscribe_no||''}}</text></view>
<view>预约时间<text>{{pageInfo.subscribe_at||''}} </text></view>
<!-- 已取消才显示 -->
<block v-if="pageInfo.status_text=='已取消'">
<view>取消时间<text>{{pageInfo.cancel_time||''}}</text></view>
<view>取消原因<text>{{pageInfo.cancel_reason||''}}</text></view>
</block>
</view>
<view class="brd-section">
<view>上课信息</view>
<view>上课时间<text>{{pageInfo.date||''}}{{pageInfo.week||''}} {{pageInfo.start_duration||''}}-{{pageInfo.end_duration||''}}</text></view>
<view>上课地点<text>{{pageInfo.addr||''}}</text></view>
<view>上课教练<text>{{pageInfo.coach_name||''}}</text></view>
<view>课程名称<text>{{pageInfo.course_name||''}}</text></view>
<view>课程类型<text>{{pageInfo.course_kind||''}}</text></view>
</view>
<view class="brd-stu">
<view class="bs-tit">学员信息</view>
<view class="bs-line">
<view>已预约人数{{pageInfo.bclass_nums||0}}<text>上课区间{{pageInfo.bclass_nums_min||0}}-{{pageInfo.bclass_nums_max||0}}</text></view>
<!-- 待确认待上课才显示 -->
<view v-if="pageInfo.status_text=='待确认'||pageInfo.status_text=='待上课'" @click="toAddStu">添加学员</view>
</view>
<view class="bs-list" v-for="(e,i) in pageInfo.students" :key="i">
<view class="bl-item">
<view class="bi-info">
<view>{{e.student_name||'-'}}({{e.student_gender||'-'}})
<block v-if="e.student_phone!=''">
<text>{{e.student_phone||'-'}}</text>
<image src="/subpackage/course/static/images/icon/phone.png" @click="phoneCall(e.student_phone)"></image>
</block>
</view>
<view>预约时间{{e.subscribe_at||''}}</view>
</view>
<!-- 待确认待上课才显示 -->
<view
class="bi-btn"
hover-class="hover-active"
v-if="pageInfo.status_text=='待确认'||pageInfo.status_text=='待上课'"
@click="cancelClassStuChange(1,e)"
>取消上课</view>
</view>
</view>
</view>
<!-- 待确认待上课才显示 -->
<view class="brd-fixed" v-if="pageInfo.status_text=='待确认'||pageInfo.status_text=='待上课'">
<view class="bf-line">
<view class="bl-btn1" hover-class="hover-active" @click="cancelClassChange(0)">取消上课</view>
<view class="bl-btn2" hover-class="hover-active" @click="bclassConfirm" v-if="pageInfo.status_text=='待确认'">确认上课</view>
</view>
</view>
<cancel-class-modal
v-if="isCancelClass"
@reasonConfirm="reasonConfirm"
@reasonClose="isCancelClass=false"
></cancel-class-modal>
</view>
</template>
<script>
import util from '@/utils/util';
import boxServer from '../../../js/course_server';
import BOX_API from '../../../js/course_api';
import { mapState } from 'vuex';
import cancel_class_modal from '../../../components/cancel_class_modal/cancel_class_modal.vue';
export default {
components: {
'cancel-class-modal': cancel_class_modal,
},
computed:{
...mapState([ 'brandInfo',]),
},
data() {
return {
isCancelClass: false,
pageInfo: {},
cancelType: "",
submitInfo: {
type: "", // 0 1
id: -1,
subscribe_no: "",
},
}
},
onLoad(options) {
this.submitInfo.id = options.id
this.getDetail(options.id)
},
methods: {
getDetail(id){
let { brandInfo } = this
util.showLoad();
boxServer.get({
url: BOX_API.subscribeInfo,
data: {
brand_id: brandInfo.brand.id,
id,
},
failMsg: '加载失败!'
})
.then(res=>{
util.hideLoad();
this.pageInfo = res
})
},
refreshList(){
let { submitInfo } = this
this.getDetail(submitInfo.id)
},
toAddStu(){
let { pageInfo } = this
util.routeTo(`/subpackage/course/pages/class_add_student/class_add_student?subscribe_no=${pageInfo.subscribe_no}`,'nT');
},
// -
cancelClassChange(type){
this.submitInfo.type = type
this.isCancelClass = true
},
// -
cancelClassStuChange(type,info){
this.submitInfo.type = type
this.submitInfo.subscribe_no = info.student_no
this.isCancelClass = true
},
reasonConfirm(e){
this.isCancelClass = false
let { submitInfo } = this
if(submitInfo.type == 0){
this.getCancelPabAc({
id: submitInfo.id,
reason: e,
})
return;
}
if(submitInfo.type == 1){
this.getCancelPabAc({
subscribe_no: submitInfo.subscribe_no,
reason: e,
})
return;
}
},
// [/]
getCancelPabAc({subscribe_no, id, reason}){
let { brandInfo, submitInfo } = this
let _data = {
brand_id: brandInfo.brand.id,
reason, //
}
if(submitInfo.type == 0)_data["id"] = id;
if(submitInfo.type == 1)_data["subscribe_no"] = subscribe_no;
util.showLoad();
boxServer.get({
url: submitInfo.type == 0? BOX_API.cancelBclassAc:submitInfo.type == 1? BOX_API.cancelStuAc:'',
data: _data,
failMsg: '加载失败!'
})
.then(res=>{
util.hideLoad();
util.showNone('操作成功!');
setTimeout(_=>{
this.refreshList()
util.previousPageFunction({
fnName: 'refreshList',
query: { isLoad: false},
});
}, 1200)
})
},
//
bclassConfirm(){
let { brandInfo, pageInfo } = this
util.showModal({
title: '提示',
content: '是否确认上课?',
showCancel: true,
success: modalRes=>{
if(modalRes.confirm){
util.showLoad();
boxServer.get({
url: BOX_API.setBclassAc,
data: {
brand_id: brandInfo.brand.id,
subscribe_no: pageInfo.subscribe_no, //
},
failMsg: '加载失败!'
})
.then(res=>{
util.hideLoad();
util.showNone('操作成功!');
setTimeout(_=>{
this.refreshList()
util.previousPageFunction({
fnName: 'refreshList',
query: { isLoad: false},
});
}, 1200)
})
}
}
})
},
phoneCall(mobile) {
let _phoneStr = mobile || ''
let _phoneArr = _phoneStr.split(',') || [];
uni.showActionSheet({
itemList: _phoneArr,
success: res =>{
uni.makePhoneCall({
phoneNumber: _phoneArr[res.tapIndex]
})
}
})
},
}
}
</script>
<style lang="scss">
@import '~style/public.scss';
.bclass-reserve-detail{
position: relative;
padding-bottom: 150rpx;
.brd-line{
padding: 24rpx 44rpx 30rpx 30rpx;
background-color: #FFFFFF;
border-bottom: 2rpx solid #E5E5E5;
@include centerFlex(space-between);
>view{
font-size: 28rpx;
line-height: 40rpx;
&:first-child{
color: #1A1A1A;
font-weight: 700;
}
&:nth-child(2){
flex-shrink: 0;
margin-left: 10rpx;
}
}
.bl-green{
color: #009874;
}
.bl-gray{
color: #9C9C9F;
}
.bl-red{
color: #EA5061;
}
}
.brd-section{
margin-bottom: 2rpx;
padding: 25rpx 30rpx 30rpx;
background-color: #FFFFFF;
>view{
color: #9C9C9F;
font-size: 28rpx;
line-height: 52rpx;
>text{
color: #1A1A1A;
}
}
}
.brd-stu{
margin-top: 24rpx;
// padding: 26rpx 30rpx 38rpx;
background-color: #FFFFFF;
.bs-tit{
padding: 30rpx 30rpx 0;
color: #9A9A9D;
font-size: 28rpx;
line-height: 40rpx;
}
.bs-line{
padding: 12rpx 44rpx 26rpx 26rpx;
border-bottom: 2rpx solid #F2F2F7;
@include centerFlex(space-between);
>view{
font-size: 28rpx;
&:first-child{
color: #1A1A1A;
line-height: 52rpx;
>text{
color: #9C9C9F;
}
}
&:nth-child(2){
margin-left: 10rpx;
flex-shrink: 0;
color: #009874;
line-height: 40rpx;
text-decoration: underline;
}
}
}
.bs-list{
.bl-item{
margin: 0 30rpx;
padding: 30rpx 0rpx;
border-bottom: 2rpx solid #D8D8D8;
@include centerFlex(space-between);
.bi-info{
>view{
&:first-child{
color: #333333;
font-size: 32rpx;
line-height: 44rpx;
>text{
margin: 0 28rpx 0 40rpx;
}
>image{
flex-shrink: 0;
width: 32rpx;
height: 32rpx;
}
}
&:nth-child(2){
margin-top: 6rpx;
color: #B2B2B2;
font-size: 28rpx;
line-height: 40rpx;
}
}
}
.bi-btn{
padding: 12rpx 0;
width: 140rpx;
border: 2rpx solid #EA5061;
border-radius: 6rpx;
color: #EA5061;
font-size: 28rpx;
line-height: 40rpx;
text-align: center;
}
}
}
}
.brd-fixed{
position: fixed;
bottom: 0;
left: 0;
padding: 30rpx 0;
width: 750rpx;
background-color: #FFFFFF;
.bf-line{
margin-right: 32rpx;
@include centerFlex(flex-end);
>view{
margin-left: 20rpx;
padding: 20rpx 0;
width: 192rpx;
font-size: 28rpx;
text-align: center;
font-weight: 700;
border-radius: 6rpx;
}
.bl-btn1{
color: #EA5061;
border: 2rpx solid #EA5061;
background-color: #FFFFFF;
}
.bl-btn2{
color: #FFFFFF;
border: 2rpx solid #009874;
background-color: #009874;
}
.bl-btn3{
color: #009874;
border: 2rpx solid #009874;
background-color: #FFFFFF;
}
}
}
}
</style>

540
src/subpackage/course/pages/manage_detail/class_manage_detail/class_manage_detail.vue

@ -0,0 +1,540 @@
<template>
<view class="class-manage-detail" :class="[curTabID==2?'cmd-content':'']">
<view class="cmd-tab">
<tab-bar :tabList="tabList" :curTabID="curTabID" @change="tabChange"></tab-bar>
</view>
<!-- 班级详情 -->
<block v-if="curTabID==0">
<view class="cmd-status">课程状态<text :class="[pageInfo.sclass_status==2?'cs-gray':pageInfo.sclass_status==3?'cs-red':'cs-green']">{{zh_sclass_status(pageInfo.sclass_status)||'-'}}</text></view>
<view class="cmd-section">
<view>课程信息</view>
<view>{{pageInfo.brand_name||''}}{{pageInfo.stadium_name||'-'}}</view>
<view><text>课程名称</text>{{pageInfo.course_name||''}}</view>
<view><text>课程课时数量</text>{{pageInfo.course_number ||0}}节课</view>
<view><text>课时时长</text>{{pageInfo.course_time_length}}分钟</view>
<view><text>课程有效期</text>{{pageInfo.course_valid_period||''}}</view>
<!-- 成班中失败 -->
<block v-if="pageInfo.sclass_status==0||pageInfo.sclass_status==3">
<view><text>成班区间人数</text>{{pageInfo.sclass_min_nums||''}}-{{pageInfo.sclass_max_nums||''}}</view>
<view><text>报名截止时间</text>{{pageInfo.sclass_join_deadline||''}}</view>
</block>
</view>
<view class="cmd-section">
<view>班级信息</view>
<view><text>班级id</text>{{pageInfo.sclass_number||''}}</view>
<view><text>班级名称</text>{{pageInfo.sclass_name||''}}</view>
<view><text>班级人数</text>{{pageInfo.sclass_nums||''}}</view>
<view><text>上课教练</text>{{pageInfo.sclass_coach||''}}</view>
<view><text>上课地点</text>{{pageInfo.sclass_addr||''}}</view>
<view><text>课程日期</text>{{pageInfo.sclass_date||''}}</view>
</view>
<view class="cmd-fixed" v-if="pageInfo.sclass_status==0">
<view hover-class="hover-active" @click="confirmClass()">确认成班</view>
</view>
</block>
<!-- 班级学员 -->
<view class="cmd-stu" v-for="(e,i) in pageInfo.students" :key="i" v-if="curTabID==1">
<view class="cs-item" @click="toStuDetail(e)">
<view class="ci-line">
<view>{{e.student_name||'-'}}{{e.student_gender||'-'}}
<block v-if="e.student_phone!=''">
<text>{{e.student_phone||'-'}}</text>
<image src="/subpackage/course/static/images/icon/phone.png" @click.stop="phoneCall(e.student_phone)"></image>
</block>
</view>
<image src="/subpackage/course/static/images/icon/arrow_black.png"></image>
</view>
<view class="ci-view">剩余课时{{e.course_period_residue||0}} {{e.course_period_nums||0}})</view>
<view class="ci-view"><text>有效期{{e.end_at || ''}}</text></view>
<view class="ci-btn" v-if="e.order_status==0||e.order_status==1">
<view hover-class="hover-active" @click.stop="resetBtn(e)" >转班</view>
</view>
</view>
</view>
<!-- 上课表 -->
<view class="cmd-schedule" v-if="curTabID==2">
<block v-if="pageInfo.sclass_period_time.length > 0">
<view class="cs-tab">
<view>课时</view>
<view>上课时间</view>
<view>上课状态</view>
<view>操作</view>
</view>
<view class="cs-list" v-for="(e,i) in pageInfo.sclass_period_time" :key="i">
<view class="cl-item">
<view>{{e.lesson_no|| '-'}}</view>
<view>{{e.period_date!=''?formatDate({date: e.period_date, partition: '.'}):'-'}}{{`${' '+e.period_start_time || ''}-${e.period_end_time || ''}`}}</view>
<view
:class="[!e.status?'ci-txt-green':'']"
>{{e.status?'已上课':'待上课'}}</view>
<view>
<view hover-class="hover-active" v-if="!e.status" @click="toChangeTime(e)">更改</view>
</view>
</view>
</view>
</block>
<view class="cmd-no" v-if="pageInfo.sclass_period_time.length == 0">
<image src="/subpackage/course/static/images/no_order.png"></image>
<view>暂未排课</view>
</view>
</view>
<!-- 作业列表 -->
<view class="cmd-task" v-if="curTabID==3">
<task-section ref="refTask" :taskInfo="pageInfo.work_list" taskType="finish" v-if="pageInfo.work_list.length>0"></task-section>
<view class="cmd-no" v-if="pageInfo.work_list.length==0">
<image src="/subpackage/course/static/images/no_order.png"></image>
<view>暂未布置作业</view>
</view>
</view>
<!-- 转班弹框 -->
<class-reset-modal
:isShowClass="isShowClass"
:info="pageInfo"
:canlist="classList"
@confirmReset="confirmReset"
@closeReset="isShowClass=false"
></class-reset-modal>
</view>
</template>
<script>
import util from '@/utils/util';
import boxServer from '../../../js/course_server';
import BOX_API from '../../../js/course_api';
import { mapState } from 'vuex';
import tabBar from '../../../components/tab_bar/tab_bar.vue';
import class_reset_modal from '../../../components/class_reset_modal/class_reset_modal.vue';
import task_section from '../../../components/task_section/task_section.vue';
const tabList=[
{name: '班级详情', id: 0}, {name: '班级学员', id: 1}, {name: '上课表', id: 2}, {name: '作业列表', id: 3},
];
export default {
components: {
'tab-bar': tabBar,
'class-reset-modal': class_reset_modal,
'task-section': task_section,
},
computed:{
...mapState([ 'brandInfo',]),
},
data() {
return {
tabList,
curTabID: 0,
isShowClass: false,
classList: [],
pageInfo: {
students: [], //
work_list: [], //
sclass_period_time: [], //
},
optionsId: -1,
resetInfo: {}, //
}
},
onLoad(options) {
this.optionsId = options.id
this.getDetail(options.id)
},
methods: {
formatDate: util.formatDate,
getDetail(id){
let { brandInfo } = this
util.showLoad();
boxServer.get({
url: BOX_API.sclassInfo,
data: {
brand_id: brandInfo.brand.id,
id,
},
failMsg: '加载失败!'
})
.then(res=>{
util.hideLoad();
let _res = res
_res.students = res.students || []
_res.work_list = res.work_list || []
_res.sclass_period_time = res.sclass_period_time || []
this.pageInfo = _res
this.getCanlist(res.course_id);
})
},
zh_sclass_status(status){
let _arr = ["成班中","进行中","已结束","成班失败"]
return _arr[status] || '-';
},
tabChange: util.debounce(function(e){
this.curTabID = e.id;
this.$nextTick(() => {
if(this.curTabID==3 && this.pageInfo.work_list.length>0)this.$refs.refTask.initDesc();
});
}, 300, true),
refreshList(){
this.curTabID = 0
this.getDetail(this.optionsId)
},
//
confirmClass(){
let { brandInfo, pageInfo } = this
util.showModal({
title: '是否确认成班',
content: '确认后代表用户报名成功,提前成立班级安排上课',
showCancel: true,
success: modalRes=>{
if(modalRes.confirm){
util.showLoad();
boxServer.get({
url: BOX_API.sclassConfirm,
data: {
brand_id: brandInfo.brand.id,
id: pageInfo.sclass_id,
},
failMsg: '加载失败!'
})
.then(res=>{
util.hideLoad();
util.showNone('操作成功!');
setTimeout(_=>{this.refreshList()}, 1200)
})
}
}
})
},
toStuDetail(item){
let { pageInfo } = this
let _query = item
_query["course_id"] = pageInfo.course_id
_query["course_name"] = pageInfo.course_name
// _query["sclass_name"] = pageInfo.sclass_name
// _query["sclass_nums"] = pageInfo.sclass_nums
_query["sclass_id"] = pageInfo.sclass_id
console.log("的点点滴滴:", _query)
util.routeTo(`/subpackage/course/pages/class_student_detail/class_student_detail?query=${util.jsonStr(_query)}`,'nT')
},
//
getCanlist(course_id){
let { brandInfo, pageInfo } = this
util.showLoad();
boxServer.get({
url: BOX_API.stuCanChangeSclass,
data: {
brand_id: brandInfo.brand.id,
course_id,
},
failMsg: '加载失败!'
})
.then(res=>{
util.hideLoad();
this.classList = res.list || []
})
},
//
resetBtn(item){
this.isShowClass = true
this.resetInfo.user_id = item.user_id
this.resetInfo.order_no = item.order_no
},
//
confirmReset(e){
// this.resetInfo["new_id"] = e.new_id
let { brandInfo, pageInfo, resetInfo } = this
util.showLoad();
boxServer.get({
url: BOX_API.resetStuSclass,
data: {
brand_id: brandInfo.brand.id,
old_id: pageInfo.sclass_id,
new_id: e.new_id,
user_id: resetInfo.user_id,
user_order_no: resetInfo.order_no,
},
failMsg: '加载失败!'
})
.then(res=>{
util.hideLoad();
util.showNone('操作成功!');
this.isShowClass = false
setTimeout(_=>{this.refreshList()}, 1200)
})
},
//
toChangeTime(item){
let { pageInfo } = this
let _query = {
brand_name: `${pageInfo.brand_name||''}${pageInfo.stadium_name||''}`,
course_name: pageInfo.course_name,
sclass_name: pageInfo.sclass_name,
sclass_id: pageInfo.sclass_id,
lesson_no: item.lesson_no,
period_date: item.period_date,
period_start_time: item.period_start_time,
period_end_time: item.period_end_time,
}
util.routeTo(`/subpackage/course/pages/class_time_change/class_time_change?query=${util.jsonStr(_query)}`,'nT');
},
phoneCall(mobile) {
let _phoneStr = mobile || ''
let _phoneArr = _phoneStr.split(',') || [];
uni.showActionSheet({
itemList: _phoneArr,
success: res =>{
uni.makePhoneCall({
phoneNumber: _phoneArr[res.tapIndex]
})
}
})
},
}
}
</script>
<style lang="scss">
@import '~style/public.scss';
.class-manage-detail{
position: relative;
padding: 102rpx 0 170rpx;
.cmd-tab{
position: fixed;
top: 0;
left: 0;
width: 750rpx;
border-bottom: 2rpx solid #F2F2F7;
z-index: 10;
}
.cmd-status{
margin-top: 24rpx;
padding: 24rpx 48rpx;
background-color: #FFFFFF;
color: #1A1A1A;
font-size: 28rpx;
line-height: 52rpx;
font-weight: 700;
.cs-green{
color: #009874;
}
.cs-gray{
color: #9C9C9F;
}
.cs-red{
color: #EA5061;
}
}
.cmd-section{
margin-top: 2rpx;
padding: 38rpx 48rpx 30rpx;
background-color: #FFFFFF;
>view{
&:first-child{
margin-bottom: 20rpx;
color: #9C9C9F;
font-size: 32rpx;
line-height: 44rpx;
}
&+view{
color: #1A1A1A;
font-size: 28rpx;
line-height: 52rpx;
>text{
color: #9C9C9F;
}
}
}
}
.cmd-fixed{
position: fixed;
bottom: 0;
left: 0;
padding: 22rpx 0;
width: 750rpx;
background-color: #FFFFFF;
border-top: 2rpx solid #F2F2F7;
>view{
margin: 0 auto;
padding: 34rpx 0;
width: 670rpx;
border-radius: 10rpx;
background-color: #009874;
color: #FFFFFF;
font-size: 32rpx;
line-height: 44rpx;
text-align: center;
}
}
.cmd-stu{
margin-top: 24rpx;
.cs-item{
position: relative;
flex-grow: 1;
margin-top: 2rpx;
padding: 26rpx 22rpx 20rpx;
background-color: #FFFFFF;
.ci-line{
margin: 0 26rpx;
@include centerFlex(space-between);
>view{
color: #1A1A1A;
font-size: 28rpx;
line-height: 52rpx;
>text{
margin: 0 30rpx;
}
>image{
flex-shrink: 0;
width: 32rpx;
height: 32rpx;
}
}
>image{
flex-shrink: 0;
width: 20rpx;
height: 20rpx;
}
}
.ci-view{
margin-left: 26rpx;
color: #1A1A1A;
font-size: 28rpx;
line-height: 52rpx;
>text{
color: #9C9C9F;
}
}
.ci-btn{
margin-top: 18rpx;
padding-top: 28rpx;
border-top: 2rpx solid #E5E5E5;
@include centerFlex(flex-end);
>view{
padding: 12rpx 0;
width: 156rpx;
border-radius: 6rpx;
border: 2rpx solid #009874;
color: #009874;
font-size: 28rpx;
line-height: 40rpx;
text-align: center;
}
}
}
}
.cmd-schedule{
padding-top: 24rpx;
border-top: 24rpx solid #F2F2F7;
.cs-tab{
margin: 0 24rpx;
border: 2rpx solid #E8E8E8;
background-color: #F6F8F8;
@include centerFlex(flex-start);
>view{
height: 110rpx;
color: #1A1A1A;
font-size: 28rpx;
text-align: center;
@include centerFlex(center);
&:first-child {
width: 130rpx;
}
&:nth-child(2){
width: 204rpx;
border-left: 2rpx solid #E8E8E8;
border-right: 2rpx solid #E8E8E8;
}
&:nth-child(3){
width: 204rpx;
border-right: 2rpx solid #E8E8E8;
}
&:nth-child(4){
width: 158rpx;
}
}
}
.cs-list{
.cl-item{
margin: 0 24rpx;
border-left: 2rpx solid #E8E8E8;
border-right: 2rpx solid #E8E8E8;
border-bottom: 2rpx solid #E8E8E8;
background-color: #FFFFFF;
@include centerFlex(flex-start);
>view{
height: 110rpx;
color: #1A1A1A;
font-size: 28rpx;
text-align: center;
@include centerFlex(center);
&:first-child {
width: 130rpx;
}
&:nth-child(2){
width: 204rpx;
border-left: 2rpx solid #E8E8E8;
border-right: 2rpx solid #E8E8E8;
}
&:nth-child(3){
width: 204rpx;
border-right: 2rpx solid #E8E8E8;
}
&:nth-child(4){
width: 158rpx;
>view{
padding: 4rpx 0;
width: 110rpx;
border-radius: 6rpx;
background-color: #009874;
color: #FFFFFF;
font-size: 28rpx;
font-weight: 700;
text-align: center;
line-height: 40rpx;
}
}
}
.ci-txt-green{
color: #009874;
}
}
}
}
.cmd-task{
margin-top: 24rpx;
}
.cmd-no{
display: flex;
flex-direction: row;
flex-direction: column;
flex-wrap: wrap;
justify-content: center;
align-items: center;
margin-top: 150rpx;
>image{
width: 346rpx;
height: 346rpx;
}
>view{
margin-top: 46rpx;
color: #9C9C9F;
font-size: 32rpx;
line-height: 44rpx;
text-align: center;
}
}
}
.cmd-content{
min-height: 100vh;
background-color: #FFFFFF;
}
</style>

236
src/subpackage/course/pages/manage_detail/private_reserve_detail/private_reserve_detail.vue

@ -0,0 +1,236 @@
<template>
<view class="private-reserve-detail">
<view class="prd-line">
<view>{{pageInfo.brand_name||''}}({{pageInfo.stadium_name||'-'}})</view>
<view :class="[pageInfo.status_text=='已上课'?'pl-gray':pageInfo.status_text=='已取消'?'pl-red':'pl-green']">{{pageInfo.status_text||'-'}}</view>
</view>
<view class="prd-section">
<view>预约单号<text>{{pageInfo.subscribe_no||''}}</text></view>
<view>预约时间<text>{{pageInfo.subscribe_at||''}}</text></view>
<!-- 已取消才显示 -->
<block v-if="pageInfo.status_text=='已取消'">
<view>取消时间<text>{{pageInfo.cancel_time||''}}</text></view>
<view>取消原因<text>{{pageInfo.cancel_reason||''}}</text></view>
</block>
</view>
<view class="prd-section">
<view>上课信息</view>
<view>上课时间<text>{{pageInfo.date||''}}{{pageInfo.week||'-'}} {{pageInfo.start_duration||''}}-{{pageInfo.end_duration||''}}</text></view>
<view>上课地点<text>{{pageInfo.addr||''}}</text></view>
<view>上课教练<text>{{pageInfo.coach_name||''}}</text></view>
<view>课程名称<text>{{pageInfo.course_name||''}}</text></view>
<view>课程类型<text>{{pageInfo.course_kind||''}}</text></view>
<view>课程节数<text>{{pageInfo.course_period_number_cur||0}}{{pageInfo.course_period_number||0}}</text></view>
</view>
<view class="prd-stu">
<view class="ps-tit">学员信息</view>
<view class="ps-line">
<view class="pl-info">
<view>{{pageInfo.student_name||'-'}}({{pageInfo.student_gender||'-'}})
<block v-if="pageInfo.student_phone!=''">
<text>{{pageInfo.student_phone||'-'}}</text>
<image src="/subpackage/course/static/images/icon/phone.png" @click="phoneCall(pageInfo.student_phone)"></image>
</block>
</view>
<view>预约时间{{pageInfo.subscribe_at||''}}</view>
</view>
<!-- 待上课才显示 -->
<view class="pl-btn" hover-class="hover-active" v-if="pageInfo.status_text=='待上课'" @click="isCancelClass=true">取消上课</view>
</view>
</view>
<cancel-class-modal
v-if="isCancelClass"
@reasonConfirm="reasonConfirm"
@reasonClose="isCancelClass=false"
></cancel-class-modal>
</view>
</template>
<script>
import util from '@/utils/util';
import boxServer from '../../../js/course_server';
import BOX_API from '../../../js/course_api';
import { mapState } from 'vuex';
import cancel_class_modal from '../../../components/cancel_class_modal/cancel_class_modal.vue';
export default {
components: {
'cancel-class-modal': cancel_class_modal,
},
computed:{
...mapState([ 'brandInfo',]),
},
data() {
return {
isCancelClass: false,
pageInfo: {},
optionsId: -1,
}
},
onLoad(options) {
this.optionsId = options.id
this.getDetail(options.id)
},
methods: {
getDetail(id){
let { brandInfo } = this
util.showLoad();
boxServer.get({
url: BOX_API.subscribeInfo,
data: {
brand_id: brandInfo.brand.id,
id,
},
failMsg: '加载失败!'
})
.then(res=>{
util.hideLoad();
this.pageInfo = res
})
},
// [/]
getCancelPabAc({ subscribe_no, reason}){
let { brandInfo } = this
let _data = {
brand_id: brandInfo.brand.id,
reason, //
subscribe_no,
}
util.showLoad();
boxServer.get({
url: BOX_API.cancelStuAc,
data: _data,
failMsg: '加载失败!'
})
.then(res=>{
util.hideLoad();
util.showNone('操作成功!');
setTimeout(_=>{
this.getDetail(this.optionsId)
util.previousPageFunction({
fnName: 'refreshList',
query: { isLoad: false},
});
}, 1200)
})
},
reasonConfirm(e){
this.isCancelClass = false
let { pageInfo } = this
this.getCancelPabAc({
subscribe_no: pageInfo.subscribe_no,
reason: e,
})
},
phoneCall(mobile) {
let _phoneStr = mobile || ''
let _phoneArr = _phoneStr.split(',') || [];
uni.showActionSheet({
itemList: _phoneArr,
success: res =>{
uni.makePhoneCall({
phoneNumber: _phoneArr[res.tapIndex]
})
}
})
},
}
}
</script>
<style lang="scss">
@import '~style/public.scss';
.private-reserve-detail{
.prd-line{
padding: 24rpx 44rpx 30rpx 30rpx;
background-color: #FFFFFF;
border-bottom: 2rpx solid #E5E5E5;
@include centerFlex(space-between);
>view{
font-size: 28rpx;
line-height: 40rpx;
&:first-child{
color: #1A1A1A;
font-weight: 700;
}
&:nth-child(2){
flex-shrink: 0;
margin-left: 10rpx;
}
}
.pl-green{
color: #009874;
}
.pl-gray{
color: #9C9C9F;
}
.pl-red{
color: #EA5061;
}
}
.prd-section{
margin-bottom: 2rpx;
padding: 25rpx 30rpx 30rpx;
background-color: #FFFFFF;
>view{
color: #9C9C9F;
font-size: 28rpx;
line-height: 52rpx;
>text{
color: #1A1A1A;
}
}
}
.prd-stu{
margin-top: 24rpx;
padding: 26rpx 30rpx 38rpx;
background-color: #FFFFFF;
.ps-tit{
color: #9A9A9D;
font-size: 28rpx;
line-height: 40rpx;
}
.ps-line{
margin-top: 26rpx;
@include centerFlex(space-between);
.pl-info{
>view{
&:first-child{
color: #333333;
font-size: 32rpx;
line-height: 44rpx;
>text{
margin: 0 28rpx 0 40rpx;
}
>image{
flex-shrink: 0;
width: 32rpx;
height: 32rpx;
}
}
&:nth-child(2){
margin-top: 6rpx;
color: #B2B2B2;
font-size: 28rpx;
line-height: 40rpx;
}
}
}
.pl-btn{
padding: 12rpx 0;
width: 140rpx;
border: 2rpx solid #EA5061;
border-radius: 6rpx;
color: #EA5061;
font-size: 28rpx;
line-height: 40rpx;
text-align: center;
}
}
}
}
</style>

323
src/subpackage/course/pages/manage_detail/student_course_detail/student_course_detail.vue

@ -0,0 +1,323 @@
<template>
<view class="student-course-detail" :class="[false?'scd-content':'']">
<view class="scd-section">
<view class="ss-status" :class="[pageInfo.order_status==2?'ss-gary':'']">{{pageInfo.order_status==1?'进行中':pageInfo.order_status==2?'已结束':''}}</view>
<view class="ss-view">学员姓名{{pageInfo.student_name || ''}}{{pageInfo.student_gender ||'-'}}</view>
<view class="ss-view">学员电话{{pageInfo.student_phone||''}}
<image src="/subpackage/course/static/images/icon/phone.png" @click="phoneCall(pageInfo.student_phone)"></image>
</view>
<view class="ss-view">剩余课时{{pageInfo.period_nums_surplus||0}}课时 {{pageInfo.period_nums||0}})</view>
<!-- 私教课大班课 才有安排上课 -->
<view class="ss-line" v-if="pageInfo.order_status==1&&(pageInfo.course_kind=='私教课'||pageInfo.course_kind=='大班课')">
<view hover-class="hover-active" @click="toArrangeClass">安排上课</view>
</view>
</view>
<view class="scd-tab">
<tab-bar :tabList="tabList" :curTabID="curTabID" @change="tabChange"></tab-bar>
</view>
<!-- 课程详情 -->
<view class="scd-info" v-if="curTabID==0">
<view class="si-tit">{{pageInfo.brand_name || ''}}({{pageInfo.stadium_name||'-'}})</view>
<view>{{pageInfo.course_name||''}}</view>
<view><text>课程类型</text>{{pageInfo.course_kind||''}}</view>
<view><text>总课时</text>{{pageInfo.period_nums||0}}</view>
<!-- 私教课大班课 -->
<block v-if="pageInfo.course_kind=='私教课'||pageInfo.course_kind=='大班课'">
<view><text>购买课时</text>{{pageInfo.period_nums_gm||0}}</view>
<view><text>赠送课时</text>{{pageInfo.period_nums_zs||0}}</view>
<view><text>有效期至</text>{{pageInfo.course_end||''}}</view>
<view><text>课时时长</text>{{pageInfo.period_time||'-'}}/</view>
<view><text>上课教练</text>{{pageInfo.course_coach||''}}</view>
<!-- 私教课才有上课内容 -->
<view v-if="pageInfo.course_kind=='私教课'"><text>上课内容</text></view>
<view><text>上课是否需要场地</text>不含场地费用</view>
</block>
<!-- 成班课 -->
<block v-if="pageInfo.course_kind=='成班课'">
<view><text>课时时长</text>{{pageInfo.period_time||'-'}}/</view>
<view><text>有效期至</text>{{pageInfo.course_end||''}}</view>
<view class="si-tit si-mar">班级信息</view>
<view><text>班级id</text>{{pageInfo.sclass_no||''}}</view>
<view><text>班级名称</text>{{pageInfo.sclass_name||''}}</view>
<view><text>班级人数</text>{{pageInfo.sclass_nums||0}}</view>
<view><text>上课教练</text>{{pageInfo.course_coach||''}}</view>
<view><text>上课地点</text>{{pageInfo.sclass_addr||''}}</view>
<view><text>课程日期</text>{{pageInfo.sclass_date||''}}</view>
</block>
</view>
<!-- 上课记录 -->
<view class="scd-record" v-if="curTabID==1">
<!-- 私教课大班课 -->
<view class="sr-list" v-for="(e,i) in pageInfo.subscribe_period_time" :key="i" v-if="pageInfo.course_kind=='私教课'||pageInfo.course_kind=='大班课'">
<view class="sl-item">
<view>
<view class="si-box">
<view>预约单号{{e.subscribe_no}}</view>
<view :class="[e.status_text=='已取消'?'sb-red':e.status_text=='已上课'?'sb-gray':'sb-green']">{{e.status_text||'-'}}</view>
</view>
<view class="si-time">{{e.created_at||'-'}} 预约</view>
</view>
<view class="si-wei">课程节数{{e.lesson_no||0}}{{e.lesson_no_total||0}}</view>
<view><text>上课时间</text>{{e.date||''}}{{e.week||'-'}} {{e.start_duration||''}}-{{e.end_duration||''}}</view>
<view><text>上课地点</text>{{e.addr||''}}</view>
<view><text>上课教练</text>{{e.coach_name||''}}</view>
<!-- 已取消显示 -->
<block v-if="e.status_text=='已取消'">
<view><text>取消时间</text>{{e.cancel_time||''}}</view>
<view><text>取消原因</text>{{e.cancel_reason||''}}</view>
</block>
</view>
</view>
<view class="sr-sclass" v-if="pageInfo.course_kind=='成班课'">
<sclass-record-section :recordInfo="pageInfo.sclass_period_time"></sclass-record-section>
</view>
</view>
<!-- 作业列表 -->
<view class="scd-task" v-if="curTabID==2">
<task-section ref="refTask" :taskInfo="pageInfo.student_work_list"></task-section>
</view>
</view>
</template>
<script>
import util from '@/utils/util';
import boxServer from '../../../js/course_server';
import BOX_API from '../../../js/course_api';
import { mapState } from 'vuex';
import tabBar from '../../../components/tab_bar/tab_bar.vue';
import sclass_record_section from '../../../components/sclass_record_section/sclass_record_section.vue';
import task_section from '../../../components/task_section/task_section.vue';
const tabList=[
{name: '课程详情', id: 0}, {name: '上课记录', id: 1}, {name: '作业列表', id: 2},
];
export default {
components: {
'tab-bar': tabBar,
'sclass-record-section': sclass_record_section,
'task-section': task_section,
},
computed:{
...mapState([ 'brandInfo',]),
},
data() {
return {
tabList,
curTabID: 0,
pageInfo: {
subscribe_period_time: [], //[/]
sclass_period_time: [], //[]
student_work_list: [], //
},
}
},
onLoad(options) {
this.getDetail(options.order_no)
},
methods: {
getDetail(order_no){
let { brandInfo } = this
util.showLoad();
boxServer.get({
url: BOX_API.stuInfo,
data: {
brand_id: brandInfo.brand.id,
order_no,
},
failMsg: '加载失败!'
})
.then(res=>{
util.hideLoad();
this.pageInfo = res
})
},
tabChange: util.debounce(function(e){
this.curTabID = e.id;
this.refreshList();
this.$nextTick(() => {
if(this.curTabID==2)this.$refs.refTask.initDesc();
});
}, 300, true),
refreshList(){
},
toArrangeClass(){
let { pageInfo } = this
util.routeTo(`/subpackage/course/pages/arrange_class/arrange_class?order_no=${pageInfo.order_no}`,'nT');
},
phoneCall(mobile) {
let _phoneStr = mobile || ''
let _phoneArr = _phoneStr.split(',') || [];
uni.showActionSheet({
itemList: _phoneArr,
success: res =>{
uni.makePhoneCall({
phoneNumber: _phoneArr[res.tapIndex]
})
}
})
},
}
}
</script>
<style lang="scss">
@import '~style/public.scss';
page{
background: #F2F2F7;
}
.student-course-detail{
.scd-section{
border-bottom: 26rpx solid #F2F2F7;
position: relative;
padding: 24rpx 24rpx 18rpx;
background-color: #FFFFFF;
.ss-status{
position: absolute;
top: 24rpx;
right: 24rpx;
color: #009874;
font-size: 28rpx;
line-height: 52rpx;
}
.ss-gary{
color: #9A9A9D;
}
.ss-view{
padding-right: 84rpx;
color: #1A1A1A;
font-size: 28rpx;
line-height: 52rpx;
@include centerFlex(flex-start);
>image{
margin-left: 30rpx;
flex-shrink: 0;
width: 32rpx;
height: 32rpx;
}
}
.ss-line{
margin-top: 18rpx;
padding: 28rpx 0 12rpx;
border-top: 2rpx solid #E5E5E5;
@include centerFlex(flex-end);
>view{
padding: 12rpx 0;
width: 156rpx;
border-radius: 6rpx;
background-color: #009874;
color: #FFFFFF;
font-size: 28rpx;
line-height: 40rpx;
text-align: center;
}
}
}
.scd-tab{
border-bottom: 2rpx solid #F2F2F7;
}
.scd-info{
padding: 26rpx 44rpx 30rpx;
background-color: #FFFFFF;
>view{
color: #1A1A1A;
font-size: 28rpx;
line-height: 52rpx;
>text{
color: #9C9C9F;
}
}
.si-tit{
margin: 0 2rpx 14rpx;
color: #1A1A1A;
font-size: 28rpx;
line-height: 40rpx;
font-weight: 700;
}
.si-view{
color: #9C9C9F;
font-size: 28rpx;
line-height: 52rpx;
>text{
color: #1A1A1A;
}
}
.si-mar{
margin-top: 40rpx;
}
}
.scd-record{
.sr-list{
margin-bottom: 24rpx;
.sl-item{
padding: 24rpx 44rpx 30rpx;
background-color: #FFFFFF;
>view{
&:first-child{
margin-bottom: 18rpx;
padding-bottom: 26rpx;
border-bottom: 2rpx solid #E8E8E8;
}
&+view{
color: #1A1A1A;
font-size: 28rpx;
line-height: 52rpx;
>text{
color: #9A9A9D;
}
}
}
.si-box{
@include centerFlex(space-between);
>view{
font-size: 28rpx;
line-height: 40rpx;
&:first-child{
color: #333333;
font-weight: 700;
}
&:nth-child(2){
flex-shrink: 0;
}
}
.sb-green{
color: #009874;
}
.sb-gray{
color: #9C9C9F;
}
.sb-red{
color: #EA5061;
}
}
.si-time{
margin-top: 12rpx;
color: #9A9A9D;
font-size: 24rpx;
line-height: 34rpx;
}
.si-wei{
font-weight: 700;
}
}
}
.sr-sclass{
padding: 20rpx 0 100rpx;
background-color: #FFFFFF;
}
}
.scd-task{
margin-top: 24rpx;
}
}
.scd-content{
min-height: 100vh;
background-color: #FFFFFF;
}
</style>

352
src/subpackage/course/pages/manage_list/manage_list.vue

@ -0,0 +1,352 @@
<template>
<view class="manage-list">
<view class="ml-head">
<store-name></store-name>
<block v-if="isShowOrder">
<view class="mh-view">
<image class="mv-icon" src="/static/images/icon/search.png"></image>
<input class="mv-ipt" :placeholder="plaTxt" confirm-type="search" v-model="searchText" @confirm="onSearch"/>
</view>
<tab-bar :tabList="tabList" :curTabID="curTabID" @change="tabChange"></tab-bar>
</block>
</view>
<block v-if="isShowOrder">
<view class="ml-list" v-for="(e,i) in orderList" :key="i" v-if="orderList.length > 0">
<student-course-item :orderInfo="e" v-if="typeId == '0-0'"></student-course-item>
<private-reserve-item :orderInfo="e" v-if="typeId == '0-1'" @priclassCancel="cancelCalss"></private-reserve-item>
<bclass-reserve-item :orderInfo="e" v-if="typeId == '0-2'" @bclassCancel="cancelCalss" @bclassConfirm="bclassConfirm"></bclass-reserve-item>
<class-manage-item :orderInfo="e" v-if="typeId == '0-3'"></class-manage-item>
</view>
<view class="no-item no-list" v-if="orderList.length == 0">
<image src="/subpackage/course/static/images/no_order.png"></image>
<view>{{ tipsText }}</view>
</view>
</block>
<view class="no-list" v-if="!isShowOrder">
<image src="/subpackage/course/static/images/no_order.png"></image>
<view>{{ tipsText }}</view>
</view>
</view>
</template>
<script>
import util from '@/utils/util';
import boxServer from '../../js/course_server';
import BOX_API from '../../js/course_api';
import { mapState } from 'vuex';
import tabBar from '../../components/tab_bar/tab_bar.vue';
import store_name from '../../components/store_name/store_name';
import student_course_item from '../../components/manage/student_course_item/student_course_item.vue';
import private_reserve_item from '../../components/manage/private_reserve_item/private_reserve_item.vue';
import bclass_reserve_item from '../../components/manage/bclass_reserve_item/bclass_reserve_item.vue';
import class_manage_item from '../../components/manage/class_manage_item/class_manage_item.vue';
export default {
components: {
'tab-bar': tabBar,
'store-name': store_name,
'student-course-item': student_course_item,
'private-reserve-item': private_reserve_item,
'bclass-reserve-item': bclass_reserve_item,
'class-manage-item': class_manage_item,
},
computed:{
...mapState([ 'brandInfo',]),
...mapState({
curStoreInfo: state => state.device.curStoreInfo,
}),
titleName(){
let { typeId } = this;
if(typeId == '0-0')return '学员管理';
if(typeId == '0-1')return '私教约课记录';
if(typeId == '0-2')return '大班约课记录';
if(typeId == '0-3')return '班级管理';
return '订单管理列表';
},
tipsText(){
let { typeId } = this;
if(typeId == '0-0')return '暂无学员';
if(typeId == '0-1')return '暂无约课记录';
if(typeId == '0-2')return '暂无约课记录';
if(typeId == '0-3')return '暂无班级';
return '';
},
plaTxt(){
let { typeId } = this;
if(typeId == '0-0')return '请输入课程名称/学员姓名/电话查找';
if(typeId == '0-1')return '请输入课程名称/学员姓名/电话查找';
if(typeId == '0-2')return '请输入教练姓名查找';
if(typeId == '0-3')return '请输入课程名称/班级名称查找';
return '';
},
tabList(){
let _list0 = [{name: '全部', id: 0, status: ''}, {name: '进行中', id: 1, status: 1}, {name: '已结束', id: 2, status: 2}, {name: '已退款', id: 3, status: 3}];
let _list1 = [{name: '全部', id: 0, status: 'all'}, {name: '待上课', id: 1, status: 'dsk'}, {name: '已上课', id: 2, status: 'ysk'}, {name: '已取消', id: 3, status: 'yqx'}];
let _list2 = [{name: '全部', id: 0, status: 'all'}, {name: '待确认', id: 1, status: 'dqr'}, {name: '待上课', id: 2, status: 'dsk'}, {name: '已上课', id: 3, status: 'ysk'}, {name: '已取消', id: 4, status: 'yqx'}];
let _list3 = [{name: '全部', id: 0, status: ''}, {name: '成班中', id: 1, status: 0}, {name: '进行中', id: 2, status: 1}, {name: '已结束', id: 3, status: 2}, {name: '失败', id: 4, status: 3}];
let { typeId } = this;
if(typeId == '0-0')return _list0;
if(typeId == '0-1')return _list1;
if(typeId == '0-2')return _list2;
if(typeId == '0-3')return _list3;
return [];
},
},
watch: {
curStoreInfo(newVal, oldVal) {
this.orderList = [];
// console.log("", newVal)
this.storeInfo = newVal
// this.$nextTick(_=>{
// })
this.getOrderList({})
}
},
data() {
return {
typeId: "",
curTabID: 0,
curStatus: "",
searchText: "",
storeInfo: {},
isShowOrder: false,
orderList: [],
page: 1,
}
},
async onLoad(options){
// let _query = util.jsonPar(options.query);
// this.optionsQuery = _query
try{
util.showLoad();
let _brandInfo = await this.$store.dispatch('getBrandInfo');
await this.$store.dispatch('getStoreList');
util.hideLoad();
this.typeId = options.type_id || ''
this.storeInfo.id = this.curStoreInfo.id || ''
this.$nextTick(_=>{
uni.setNavigationBarTitle({
title: this.titleName
})
this.getOrderList({})
})
}catch(err){
util.hideLoad();
}
},
onReachBottom(){
let { page } = this;
this.getOrderList({
page: ++page
})
},
methods: {
tabChange: util.debounce(function(e){
this.curTabID = e.id;
this.curStatus = e.status
this.refreshList();
}, 300, true),
refreshList(){
this.page = 1;
this.orderList = [];
this.getOrderList({});
},
//
onSearch(){
this.page = 1
this.orderList = []
this.getOrderList({})
},
getApi(){
let { typeId } = this
if(typeId == '0-0')return BOX_API.stuCourse; //
if(typeId == '0-1')return BOX_API.subscribePri; //
if(typeId == '0-2')return BOX_API.subscribeBclass; //
if(typeId == '0-3')return BOX_API.sclassManage; //
},
getOrderList({page=1, page_size=20}){
let { brandInfo, storeInfo, curTabID, typeId, curStatus, searchText } = this
let _data = {
page,
page_size,
stadium_id: storeInfo.id,
brand_id: brandInfo.brand.id,
}
if(searchText!='')_data["keyword"] = searchText;
if(typeId=='0-0')_data["order_status"] = curStatus;
if(typeId!='0-0')_data["status"] = curStatus;
util.showLoad();
boxServer.get({
url: this.getApi(),
data: _data,
failMsg: '加载失败!'
})
.then(res=>{
util.hideLoad();
let _list = res.list || [];
if(page == 1){
this.orderList = _list;
if(curTabID==0 && this.orderList.length==0)this.isShowOrder = false;
else this.isShowOrder = true;
return;
}
if(_list.length<=0)return util.showNone('没有更多!');
this.page = page;
this.orderList = [...this.orderList, ...res.list];
})
},
//
cancelCalss(e){
if(e.course_kind=='私教课'){
this.getCancelPabAc({
course_kind: e.course_kind,
subscribe_no: e.subscribe_no,
reason: e.reason,
})
return;
}
if(e.course_kind=='大班课'){
this.getCancelPabAc({
course_kind: e.course_kind,
id: e.id,
reason: e.reason,
})
return;
}
},
// [/]
getCancelPabAc({ course_kind, subscribe_no, id, reason}){
let { brandInfo } = this
let _data = {
brand_id: brandInfo.brand.id,
reason, //
}
if(course_kind=='私教课')_data["subscribe_no"] = subscribe_no;
if(course_kind=='大班课')_data["id"] = id;
util.showLoad();
boxServer.get({
url: course_kind=='大班课'? BOX_API.cancelBclassAc:course_kind=='私教课'? BOX_API.cancelStuAc:'',
data: _data,
failMsg: '加载失败!'
})
.then(res=>{
util.hideLoad();
util.showNone('操作成功!');
setTimeout(_=>{ this.refreshList() }, 1200)
})
},
//
bclassConfirm(e){
util.showModal({
title: '提示',
content: '是否确认上课?',
showCancel: true,
success: modalRes=>{
if(modalRes.confirm){
this.getBclassAc({subscribe_no: e})
}
}
})
},
//
getBclassAc({ subscribe_no}){
let { brandInfo } = this
util.showLoad();
boxServer.get({
url: BOX_API.setBclassAc,
data: {
brand_id: brandInfo.brand.id,
subscribe_no, //
},
failMsg: '加载失败!'
})
.then(res=>{
util.hideLoad();
util.showNone('操作成功!');
setTimeout(_=>{ this.refreshList() }, 1200)
})
},
}
}
</script>
<style lang="scss">
@import '~style/public.scss';
page{
background: #F2F2F7;
}
.manage-list{
position: relative;
padding-top: 292rpx;
padding-bottom: 24rpx;
.ml-head{
position: fixed;
top: 0;
left: 0;
width: 750rpx;
background-color: #FFFFFF;
border-bottom: 2rpx solid #F2F2F7;
z-index: 6;
.mh-view{
margin: 0 24rpx;
padding: 0 20rpx;
flex-grow: 1;
height: 92rpx;
border-radius: 10rpx;
background-color: #F2F2F7;
@include centerFlex(flex-start);
.mv-icon{
margin-right: 20rpx;
flex-shrink: 0;
width: 40rpx;
height: 40rpx;
}
.mv-ipt{
flex-grow: 1;
color: #333;
font-size: 32rpx;
line-height: 44rpx;
}
}
}
.ml-list{
margin-top: 24rpx;
}
.no-item{
margin-top: 128rpx;
}
.no-list{
display: flex;
flex-direction: row;
flex-direction: column;
flex-wrap: wrap;
justify-content: center;
align-items: center;
>image{
width: 346rpx;
height: 346rpx;
}
>view{
margin: 46rpx 0;
color: #9C9C9F;
font-size: 32rpx;
line-height: 44rpx;
text-align: center;
}
}
}
</style>

138
src/subpackage/course/pages/task_detail/task_detail.vue

@ -0,0 +1,138 @@
<template>
<view class="task-detail">
<view class="td-info">
<view class="td-txt">布置作业时间{{formatDate({date: pageInfo.created_at, partition: '/'}) || '-'}}</view>
<view class="td-txt">发布人{{pageInfo.work_coach_name || '-'}}</view>
<view class="td-txt td-tit">作业内容</view>
<view class="td-txt">{{pageInfo.work_content || '-'}}</view>
<view class="td-line">
<view>视频链接{{pageInfo.work_video || '无'}}</view>
<view @click="copyOrder(pageInfo.work_video)">复制</view>
</view>
<image class="td-img" mode="aspectFill" v-for="(e,i) in pageInfo.work_imgs" :key="i" :src="e || ''" @tap="previewImageWork(e)"></image>
</view>
<view class="td-info">
<view class="td-txt">作业提交时间{{pageInfo.student_work_at!=''?formatDate({date: pageInfo.student_work_at, partition: '/'}) : ''}}</view>
<view class="td-txt">提交人{{pageInfo.student_name || '-'}}</view>
<view class="td-txt td-tit">完成情况</view>
<view class="td-txt">{{pageInfo.student_work_content ||'无'}}</view>
<view class="td-line">
<view>视频链接{{pageInfo.student_work_video || '无'}}</view>
<view @click="copyOrder(pageInfo.student_work_video)">复制</view>
</view>
<image class="td-img" mode="aspectFill" v-for="(e,i) in pageInfo.student_work_imgs" :key="i" :src="e || ''" @tap="previewImageStu(e)"></image>
</view>
</view>
</template>
<script>
import util from '@/utils/util';
import boxServer from '../../js/course_server';
import BOX_API from '../../js/course_api';
import { mapState } from 'vuex';
export default {
computed:{
...mapState([ 'brandInfo',]),
},
data() {
return {
pageInfo: {},
}
},
async onLoad(options){
// let _query = util.jsonPar(options.query);
// this.pageInfo = _query
this.getWorkInfo(options.id)
},
methods: {
formatDate: util.formatDate,
copyOrder(data){
uni.setClipboardData({ data })
},
previewImageWork(img){
uni.previewImage({
current: img,
urls: this.pageInfo.work_imgs
})
},
//
previewImageStu(img) {
// var current = e.target.dataset.src
uni.previewImage({
current: img,
urls: this.pageInfo.student_work_imgs
})
},
// -
getWorkInfo(id){
let { brandInfo } = this
util.showLoad();
boxServer.get({
url: BOX_API.stuWorkInfo,
data: {
brand_id: brandInfo.brand.id,
id,
},
failMsg: '加载失败!'
})
.then(res=>{
util.hideLoad();
this.pageInfo = res
})
},
}
}
</script>
<style lang="scss">
@import '~style/public.scss';
// page{
// background: #F2F2F7;
// }
.task-detail{
.td-info{
margin-bottom: 12rpx;
padding: 24rpx 0 30rpx;
background-color: #FFFFFF;
.td-txt{
margin: 0 60rpx 0 24rpx;
color: #1A1A1A;
font-size: 28rpx;
line-height: 40rpx;
}
.td-tit{
font-weight: 700;
margin: 44rpx 24rpx 20rpx;
}
.td-line{
margin: 30rpx 60rpx 0 26rpx;
flex-grow: 1;
padding: 30rpx 4rpx 6rpx;
border-top: 2rpx solid #D8D8D8;
@include centerFlex(flex-start);
>view{
font-size: 28rpx;
&:first-child{
color: #9A9A9D;
line-height: 40rpx;
@include textHide(1);
}
&:nth-child(2){
margin-left: 20rpx;
flex-shrink: 0;
color: #009874;
}
}
}
.td-img{
margin: 24rpx 16rpx 0 30rpx;
display: inline-block;
width: 200rpx;
height: 200rpx;
border-radius: 10rpx;
}
}
}
</style>

146
src/subpackage/course/pages/task_finish_list/task_finish_list.vue

@ -0,0 +1,146 @@
<template>
<view class="task-finish-list">
<block v-if="workList.length>0">
<view class="tfl-box">
<view>布置作业时间{{ pageInfo.created_at!=''?formatDate({date: pageInfo.created_at, partition: '/'}) : '-'}}</view>
<view>发布人{{pageInfo.work_coach_name||'-'}}</view>
</view>
<view class="tfl-list" v-for="(e,i) in workList" :key="i">
<view class="tl-item" @click="toDetail(e)">
<view class="ti-info">
<view>{{e.student_name||'-'}}{{e.student_gender||'-'}}</view>
<view>{{e.student_phone ||'-'}}</view>
</view>
<view class="ti-section">
<view v-if="e.work_status==1">{{e.student_work_at!=''?formatDate({date: e.student_work_at,}) :'-'}}</view>
<view class="ts-line">
<view :class="[e.work_status==1?'':'tl-txt']">{{e.work_status==1?'已完成':'未完成'}}</view>
<image src="/subpackage/course/static/images/icon/arrow_black.png"></image>
</view>
</view>
</view>
</view>
<!-- <view class="fixed-btn">
<view hover-class="hover-active" @click="batchRemind">批量提醒完成</view>
</view> -->
</block>
</view>
</template>
<script>
import util from '@/utils/util';
import boxServer from '../../js/course_server';
import BOX_API from '../../js/course_api';
import { mapState } from 'vuex';
export default {
computed:{
...mapState([ 'brandInfo',]),
},
data() {
return {
pageInfo: {},
workList: [],
}
},
onLoad(options) {
let { work_number } = options
this.pageInfo = options
this.$nextTick(_=>{
this.getClassWork()
})
},
methods: {
formatDate: util.formatDate,
// -
getClassWork(){
let { brandInfo } = this
let { work_number } = this.pageInfo
util.showLoad();
boxServer.get({
url: BOX_API.sclassStuWork,
data: {
brand_id: brandInfo.brand.id,
work_number,
},
failMsg: '加载失败!'
})
.then(res=>{
util.hideLoad();
let _list = res || []
this.workList = _list
if(_list.length>0){
this.pageInfo["created_at"] = _list[0].created_at
this.pageInfo["work_coach_name"] = _list[0].work_coach_name
}
})
},
toDetail(item){
util.routeTo(`/subpackage/course/pages/task_detail/task_detail?id=${item.id}`,'nT')
},
//
batchRemind(){
util.showNone('开发中!');
},
}
}
</script>
<style lang="scss">
@import '~style/public.scss';
page{
background: #F2F2F7;
}
.task-finish-list{
position: relative;
padding-bottom: 160rpx;
.tfl-box{
flex-grow: 1;
padding: 24rpx 22rpx;
background-color: #FFFFFF;
border-bottom: 12rpx solid #F2F2F7;
>view{
color: #1A1A1A;
font-size: 28rpx;
line-height: 52rpx;
}
}
.tfl-list{
.tl-item{
padding: 20rpx 36rpx 20rpx 46rpx;
background-color: #FFFFFF;
border-bottom: 2rpx solid #F2F2F7;
@include centerFlex(space-between);
.ti-info{
>view{
color: #1A1A1A;
font-size: 28rpx;
line-height: 52rpx;
}
}
.ti-section{
color: #9C9C9F;
font-size: 28rpx;
line-height: 52rpx;
.ts-time{
}
.ts-line{
@include centerFlex(flex-end);
>image{
margin: 0 2rpx 0 20rpx;
flex-shrink: 0;
width: 20rpx;
height: 20rpx;
}
.tl-txt{
color: #EA5061;
}
}
}
}
}
}
</style>

BIN
src/subpackage/course/static/images/icon/arrow_b2.png

After

Width: 40  |  Height: 40  |  Size: 251 B

BIN
src/subpackage/course/static/images/icon/arrow_black.png

After

Width: 20  |  Height: 20  |  Size: 624 B

BIN
src/subpackage/course/static/images/icon/arrow_green.png

After

Width: 24  |  Height: 24  |  Size: 653 B

BIN
src/subpackage/course/static/images/icon/arrow_white.png

After

Width: 28  |  Height: 28  |  Size: 563 B

BIN
src/subpackage/course/static/images/icon/calendar.png

After

Width: 34  |  Height: 34  |  Size: 786 B

BIN
src/subpackage/course/static/images/icon/close_gray.png

After

Width: 34  |  Height: 34  |  Size: 667 B

BIN
src/subpackage/course/static/images/icon/phone.png

After

Width: 32  |  Height: 32  |  Size: 1.1 KiB

BIN
src/subpackage/course/static/images/icon/triangle.png

After

Width: 22  |  Height: 22  |  Size: 428 B

BIN
src/subpackage/course/static/images/no_order.png

After

Width: 346  |  Height: 346  |  Size: 14 KiB

BIN
src/subpackage/course/static/images/tab/tab_0_0.png

After

Width: 52  |  Height: 52  |  Size: 2.1 KiB

BIN
src/subpackage/course/static/images/tab/tab_0_1.png

After

Width: 52  |  Height: 52  |  Size: 2.5 KiB

BIN
src/subpackage/course/static/images/tab/tab_0_2.png

After

Width: 52  |  Height: 52  |  Size: 2.4 KiB

BIN
src/subpackage/course/static/images/tab/tab_0_3.png

After

Width: 52  |  Height: 52  |  Size: 2.2 KiB

Loading…
Cancel
Save