Browse Source

添加活动排课

lex-xin 3 năm trước cách đây
mục cha
commit
3932c649f8

+ 5 - 0
src/assets/commonLess/common.less

@@ -166,4 +166,9 @@ textarea:-ms-input-placeholder {
 }
 .HIGH_ONLINE {
   background: @color15;
+}
+
+.img-icon {
+  width: 20px;
+  height: 20px;
 }

+ 2 - 22
src/common/vueFilters.js

@@ -1,4 +1,5 @@
 import Vue from 'vue'
+import * as constant from '../constant'
 
 // 乐团状态
 Vue.filter('bandStatus', value => {
@@ -13,28 +14,7 @@ Vue.filter('bandStatus', value => {
 })
 
 // 课程类型
-Vue.filter('coursesType', (value) => {
-    let template = {
-      NORMAL: '声部课',
-      SINGLE: '声部课',
-      MIX: "合奏课",
-      HIGH: "基础技能课",
-      VIP: "VIP课",
-      DEMO: "试听课",
-      COMPREHENSIVE: '综合课',
-      ENLIGHTENMENT: '启蒙课',
-      TRAINING: '集训课',
-      TRAINING_SINGLE: '集训声部课',
-      TRAINING_MIX: '集训合奏课',
-      CLASSROOM: '课堂课',
-      PRACTICE: '网管课',
-      COMM: '对外课',
-      MUSIC: '乐团课',
-      HIGH_ONLINE: '线上基础技能课',
-      MUSIC_NETWORK: '乐团网管课'
-    }
-    return template[value]
-  })
+Vue.filter('coursesType', val => constant.courseType[val])
 
 // 合并数组
 Vue.filter('joinArray', (value, type) => {

+ 19 - 0
src/constant/index.js

@@ -0,0 +1,19 @@
+export const courseType = {
+  NORMAL: '声部课',
+  SINGLE: '声部课',
+  MIX: "合奏课",
+  HIGH: "基础技能课",
+  VIP: "VIP课",
+  DEMO: "试听课",
+  COMPREHENSIVE: '综合课',
+  ENLIGHTENMENT: '启蒙课',
+  TRAINING: '集训课',
+  TRAINING_SINGLE: '集训声部课',
+  TRAINING_MIX: '集训合奏课',
+  CLASSROOM: '课堂课',
+  PRACTICE: '网管课',
+  COMM: '对外课',
+  MUSIC: '乐团课',
+  HIGH_ONLINE: '线上基础技能课',
+  MUSIC_NETWORK: '乐团网管课'
+}

+ 9 - 0
src/main.js

@@ -24,6 +24,15 @@ Vue.config.productionTip = false
 
 // import Vconsole from 'vconsole'
 // const vconsole = new Vconsole()
+// 将selects全局混入当前vue实例中
+Vue.mixin({
+    data() {
+      return {
+        activeButtonIcon: require('@/assets/images/common/icon_check.png'),
+        inactiveButtonIcon: require('@/assets/images/common/icon_default.png'),
+      }
+    }
+})
 
 import VueAMap from 'vue-amap'
 Vue.use(VueAMap)

+ 94 - 41
src/views/activeProgram/activeDetail.vue

@@ -1,81 +1,134 @@
 <template>
     <div class="activeDetail">
         <van-cell-group class="van-cell-group--inset">
-            <van-cell title="2020年双十一1v1活动" class="titleContent" title-class="titleStyle" label-class="labelStyle">
+            <van-cell :title="vipGroup.name" class="titleContent" title-class="titleStyle" label-class="labelStyle">
                 <template #label>
-                    <p>2020年双十一VIP1v1活动</p>
-                    <p>11.11抵扣1111</p>
+                    <p>{{ vipGroup.description }}</p>
                 </template>
             </van-cell>
         </van-cell-group>
 
         <h2 class="van-block__title">详细规则</h2>
         <van-row class="active-row">
-            <van-col span="8">排课时间范围</van-col>
-            <van-col span="16">2020-12-01 至 2021-12-31</van-col>
-            <van-col span="8">排课方式</van-col>
-            <van-col span="16">课程余额</van-col>
+            <van-col span="10">排课时间范围</van-col>
+            <van-col span="14">{{ vipGroup.coursesStartTime }} 至 {{ vipGroup.coursesEndTime }}</van-col>
+            <van-col span="10">是否扣减课程余额</van-col>
+            <van-col span="14">{{ vipGroup.payToBalance ? '是' : '否' }}</van-col>
         </van-row>
 
         <h2 class="van-block__title">付费课程</h2>
         <div class="active-row">
             <van-row>
                 <van-col span="8">课程类型</van-col>
-                <van-col span="16">Vip课</van-col>
+                <van-col span="16">{{ vipGroup.courseType | coursesType }}&nbsp;</van-col>
                 <van-col span="8">课程形式</van-col>
-                <van-col span="16">1v1</van-col>
+                <van-col span="16">{{ vipGroup.vipGroupCategoryNames }}&nbsp;</van-col>
                 <van-col span="8">上课模式</van-col>
-                <van-col span="16">线上、线下</van-col>
+                <van-col span="16">{{ vipGroup.teachMode | formatTeachModel }}&nbsp;</van-col>
                 <van-col span="8">单课时时长</van-col>
-                <van-col span="16">45分钟</van-col>
+                <van-col span="16">{{ vipGroup.singleCourseTime }}分钟</van-col>
                 <van-col span="8">课时数</van-col>
-                <van-col span="16">25课时</van-col>
+                <van-col span="16">{{ vipGroup.avgCourseNum }}&nbsp;</van-col>
             </van-row>
             <van-cell is-link class="teaching">
                 <template #title>
-                    剩余 <span style="color: #01C1B5">4</span> 名学员未排课
+                    剩余 <span style="color: #01C1B5">
+                        {{vipGroup.courseType == 'VIP' ? vipDetail.vipNum : null}}
+                        {{vipGroup.courseType == 'PRACTICE' ? vipDetail.practiceNum : null}}
+                    </span> 名学员未排课
                 </template>
                 <template #default>
-                    <span style="color: #01C1B5" @click="onProgram">立即排课</span>
+                    <span style="color: #01C1B5" @click="onProgram('pay')">立即排课</span>
                 </template>
             </van-cell>
         </div>
 
-        <h2 class="van-block__title">赠送课程</h2>
-        <div class="active-row">
-            <van-row>
-                <van-col span="8">课程类型</van-col>
-                <van-col span="16">Vip课</van-col>
-                <van-col span="8">课程形式</van-col>
-                <van-col span="16">1v1</van-col>
-                <van-col span="8">上课模式</van-col>
-                <van-col span="16">线上、线下</van-col>
-                <van-col span="8">单课时时长</van-col>
-                <van-col span="16">45分钟</van-col>
-                <van-col span="8">课时数</van-col>
-                <van-col span="16">25课时</van-col>
-            </van-row>
-            <van-cell is-link class="teaching">
-                <template #title>
-                    剩余 <span style="color: #01C1B5">4</span> 名学员未排课
-                </template>
-                <template #default>
-                    <span style="color: #01C1B5" @click="onProgram">立即排课</span>
-                </template>
-            </van-cell>
-        </div>
+        <!-- 赠送课程类型,只有VIP和网管课的时候才有赠送课程 -->
+        <template v-if="vipGroup.giveCourseType == 'VIP' || vipGroup.giveCourseType == 'PRACTICE'">
+            <h2 class="van-block__title">赠送课程</h2>
+            <div class="active-row">
+                <van-row>
+                    <van-col span="8">课程类型</van-col>
+                    <van-col span="16">{{ vipGroup.giveCourseType | coursesType }}&nbsp;</van-col>
+                    <van-col span="8">课程形式</van-col>
+                    <van-col span="16">{{ vipGroup.giveCategoryName }}&nbsp;</van-col>
+                    <van-col span="8">上课模式</van-col>
+                    <van-col span="16">{{ vipGroup.giveTeachMode | formatTeachModel }}&nbsp;</van-col>
+                    <van-col span="8">单课时时长</van-col>
+                    <van-col span="16">{{ vipGroup.giveSingleCourseTime }}分钟</van-col>
+                    <van-col span="8">课时数</van-col>
+                    <van-col span="16">{{ vipGroup.giveCourseNum }}课时</van-col>
+                </van-row>
+                <van-cell is-link class="teaching">
+                    <template #title>
+                        剩余 <span style="color: #01C1B5">
+                            {{vipGroup.giveCourseType == 'VIP' ? vipDetail.giveVipNum : null}}
+                            {{vipGroup.giveCourseType == 'PRACTICE' ? vipDetail.givePracticeNum : null}}
+                        </span> 名学员未排课
+                    </template>
+                    <template #default>
+                        <span style="color: #01C1B5" @click="onProgram('give')">立即排课</span>
+                    </template>
+                </van-cell>
+            </div>
+        </template>
     </div>
 </template>
 
 <script>
+import { getActivityWaitCourseStudentNum } from './api'
+import dayjs from 'dayjs'
 export default {
+    data() {
+        const query = this.$route.query
+        return {
+            activityId: query.activityId,
+            vipDetail: {},
+            vipGroup: {}
+        }
+    },
     mounted() {
         document.title = '活动详情'
+        this.__init()
     },
     methods: {
-        onProgram() {
-            console.log('开始排课吧,少年')
-            this.$router.push('/program')
+        async __init() {
+            try {
+                let res = await getActivityWaitCourseStudentNum({ activityId: this.activityId })
+                this.vipDetail = res.data
+                let vipGroup = res.data.vipGroupActivity
+                vipGroup.coursesStartTime = dayjs(vipGroup.coursesStartTime).format('YYYY-MM-DD')
+                vipGroup.coursesEndTime = dayjs(vipGroup.coursesEndTime).format('YYYY-MM-DD')
+                if(vipGroup.minCourseNum != vipGroup.maxCourseNum) {
+                    vipGroup.avgCourseNum = vipGroup.minCourseNum + '课时 ~ ' + vipGroup.maxCourseNum + '课时'
+                } else {
+                    vipGroup.avgCourseNum = vipGroup.maxCourseNum + '课时'
+                }
+                this.vipGroup = vipGroup
+            } catch {
+                //
+            }
+        },
+        onProgram(type) {
+            // type pay 付费课 give 赠送课
+            this.$router.push({
+                path: '/program',
+                query: {
+                    activityId: this.activityId,
+                    type: type
+                }
+            })
+        }
+    },
+    filters: {
+        formatTeachModel(val) {
+            if(val === -1) {
+                return '线上,线下'
+            } else if(val === 0) {
+                return '线上'
+            } else if(val === 1) {
+                return '线下'
+            }
         }
     }
 }
@@ -123,7 +176,7 @@ export default {
         padding: .09rem .08rem;
         margin-bottom: -1px;
     }
-    .van-col--8 {
+    .van-col--8, .van-col--10 {
         margin-right: -1px;
         padding-left: .14rem;
         background: #F8F8F8;

+ 33 - 2
src/views/activeProgram/api.js

@@ -1,9 +1,40 @@
 import request from '@/helpers/request'
 
-export const getChildrenDayActivity = (data) => {
+export const queryWaitCourseActivity = (data) => {
     return request({
-        url: '/vipGroupActivity/getChildrenDayActivitys',
+        url: '/vipGroupActivity/queryWaitCourseActivity',
         method: 'get',
         params: data
     })
+}
+
+export const getActivityWaitCourseStudentNum = (data) => {
+    return request({
+        url: '/vipGroupActivity/getActivityWaitCourseStudentNum',
+        method: 'get',
+        params: data
+    })
+}
+
+export const getActivityStudentCanCourseNum = (data) => {
+    return request({
+        url: '/vipGroupActivity/getActivityStudentCanCourseNum',
+        method: 'get',
+        params: data
+    })
+}
+
+export const createVipGroup = (data) => {
+    return request({
+        url: '/teacherVipGroup/createVipGroup',
+        method: 'post',
+        data
+    })
+}
+export const createPracticeGroup = (data) => {
+    return request({
+        url: '/teacherPracticeGroup/createPracticeGroup',
+        method: 'post',
+        data
+    })
 }

+ 22 - 29
src/views/activeProgram/index.vue

@@ -1,30 +1,21 @@
 <template>
     <div class="activeProgram">
-        <van-list
-            v-model="loading"
-            class="studentContainer"
-            v-if="dataShow"
-            key="data"
-            :finished="finished"
-            finished-text=""
-            @load="getActiveList"
-        >
-            <van-cell-group :border="false">
-                <van-cell v-for="i in 5" :key="i"
-                    title="2020年双十一1v1活动20202020年双"
-                    title-class="titleStyle"
-                    value="4"
-                    value-class="valueStyle"
-                    class="activeItem" is-link
-                    @click="onDetail(i)" />
-            </van-cell-group>
-        </van-list>
+        <van-cell-group :border="false" v-if="dataShow">
+            <van-cell v-for="item in dataList" :key="item.activityId"
+                :title="item.activityName"
+                title-class="titleStyle"
+                :value="item.studentNum"
+                value-class="valueStyle"
+                class="activeItem" is-link
+                @click="onDetail(item)" />
+        </van-cell-group>
         <m-empty class="empty" msg="暂无活动方案" v-else key="data" />
     </div>
 </template>
 
 <script>
 import MEmpty from "@/components/MEmpty";
+import { queryWaitCourseActivity } from './api'
 export default {
     name: 'activeProgram',
     components: {
@@ -32,29 +23,31 @@ export default {
     },
     data() {
         return {
-            loading: false,
-            finished: false,
-            params: {
-                search: null,
-                page: 1,
-                rows: 20,
-            },
             dataShow: true, // 是否有数据
             dataList: [],
         }
     },
     mounted() {
         document.title = '活动方案'
+        this.getActiveList()
     },
     methods: {
-        getActiveList() {
-            this.finished = true
+        async getActiveList() {
+            try {
+                const res = await queryWaitCourseActivity()
+                this.dataList = [...res.data]
+                if(this.dataList.length <= 0) {
+                    this.dataShow = false
+                }
+            } catch {
+                //
+            }
         },
         onDetail(item) {
             this.$router.push({
                 path: '/activeDetail',
                 query: {
-                    item
+                    activityId: item.activityId
                 }
             })
         }

+ 66 - 56
src/views/activeProgram/modal/studentList.vue

@@ -1,6 +1,6 @@
 <template>
     <div>
-        <van-sticky>
+        <!-- <van-sticky>
             <van-search
             show-action
             shape="round"
@@ -12,9 +12,9 @@
                 <div @click="onSearch">搜索</div>
             </template>
             </van-search>
-        </van-sticky>
+        </van-sticky> -->
         <div class="paddingB80">
-            <van-list
+            <!-- <van-list
                 v-model="loading"
                 class="studentContainer"
                 v-if="dataShow"
@@ -22,7 +22,8 @@
                 :finished="finished"
                 finished-text=""
                 @load="getStudent"
-            >
+            > -->
+            <div class="studentContainer" v-if="dataShow" key="data">
                 <van-checkbox-group v-model="checkboxSelect">
                     <van-cell-group>
                         <van-cell
@@ -30,6 +31,7 @@
                             :key="index"
                             @click="onCheckboxSelect(item)"
                             class="input-cell"
+                            value-class="value-times"
                             :center="true"
                         >
                             <template slot="icon">
@@ -47,18 +49,23 @@
                                 />
                             </template>
                             <template slot="title">
-                            {{ item.userName }}
-                            </template>
-                            <template slot="label">
-                                <span>{{ desensitPhone(item.phone) }}</span>
+                                {{ item.username }}
                             </template>
                             <template slot="default">
-                                <van-checkbox :name="item.userId.toString()"></van-checkbox>
+                                <span style="font-size: .16rem;color: #1A1A1A;">可排课次数:<span style="color: #01C1B5;">{{ item.showStudentNum }}</span></span>
+                            </template>
+                            <template slot="extra">
+                                <van-checkbox :name="item.userId">
+                                    <template #icon="props">
+                                        <img v-if="!item.radioDisabled" class="img-icon" :src="props.checked ? activeButtonIcon : inactiveButtonIcon" />
+                                    </template>
+                                </van-checkbox>
                             </template>
                         </van-cell>
                     </van-cell-group>
                 </van-checkbox-group>
-            </van-list>
+            </div>
+            <!-- </van-list> -->
             <m-empty class="empty" msg="暂无学生" v-else key="data" />
         </div>
         <div class="button-group-popup">
@@ -70,25 +77,22 @@
 
 <script>
 import MEmpty from "@/components/MEmpty";
-import { queryStudentsWithTeacher } from "@/api/teacher";
+import { getActivityStudentCanCourseNum } from '../api'
 export default {
     components: {
         MEmpty
     },
-    props: ['studentList'],
+    // courseTypeIsVip 当前排课的类型,可能是赠送课程
+    props: ['studentList', 'activityId', 'courseTypeIsVip', 'typeStatus', 'studentNum'],
     data() {
         return {
             params: {
                 search: null,
-                page: 1,
-                rows: 20,
+                activityId: this.activityId
             },
-            loading: false,
-            finished: false,
             dataShow: true, // 是否有数据
             dataList: [],
             checkboxSelect: [],
-            checkboxSelectList: [], //选中学生列表
         }
     },
     watch: {
@@ -97,66 +101,67 @@ export default {
                 let tempVal = newVal || []
                 this.checkboxSelect = []
                 tempVal.forEach(item => {
-                    this.checkboxSelect.push(item.userId.toString())
+                    this.checkboxSelect.push(item.userId)
                 })
             }
         }
     },
+    mounted() {
+        this.getStudent()
+    },
     methods: {
         // 搜索
         onSearch() {
-            this.params.page = 1;
             this.dataList = [];
-            this.dataShow = true;
-            this.loading = true;
-            this.finished = false;
             this.getStudent();
         },
         async getStudent() {
             let params = this.params;
-            await queryStudentsWithTeacher(params).then((res) => {
-                let result = res.data;
-                this.loading = false;
-                if (result.code == 200) {
-                    params.page = result.data.pageNo;
-                    this.dataList = this.dataList.concat(result.data.rows);
-                    if (params.page >= result.data.totalPage) {
-                        this.finished = true;
+            try {
+                const res = await getActivityStudentCanCourseNum(params)
+                let result = res.data || []
+                result.forEach(item => {
+                    let showStudentNum = 0
+                    if (this.typeStatus) {
+                        showStudentNum = this.courseTypeIsVip ? item.vipNum : item.practiceNum
+                    } else {
+                        showStudentNum = this.courseTypeIsVip ? item.giveVipNum : item.givePracticeNum
                     }
-                    this.params.page++;
-                } else {
-                    this.finished = true;
-                }
-                // 判断是否有数据
-                if (this.dataList.length <= 0) {
-                    this.dataShow = false;
-                }
-            });
+                    item.showStudentNum = showStudentNum
+                })
+                this.dataList = result
+            } catch {
+                //
+            }
         },
         onCheckboxSelect(value) {
-            if (this.checkboxSelect.includes(value.userId.toString())) {
-                this.checkboxSelectList.forEach((item, index) => {
-                    if (item.userId == value.userId) {
-                        this.checkboxSelectList.splice(index, 1);
+            if (this.checkboxSelect.includes(value.userId)) {
+                this.checkboxSelect.forEach((item, index) => {
+                    if(item === value.userId) {
+                        this.checkboxSelect.splice(index, 1)
                     }
-                });
+                })
             } else {
-                this.checkboxSelect.push(value.userId.toString())
-                this.checkboxSelectList.push(value)
+                this.checkboxSelect.push(value.userId)
             }
         },
         onPopupCancel() {
             this.$listeners.close()
         },
         onPopupSubmit() {
-            this.$emit('submit', this.checkboxSelectList)
-        },
-        desensitPhone(phone) {
-            // 手机号脱敏
-            let first = phone.substr(0, 3);
-            let last = phone.substr(-4);
-            return first + "****" + last;
-        },
+            // if (this.checkboxSelect.length != this.studentNum) {
+            //     this.$toast(`请选择学生${this.studentNum}名,当前选择${this.checkboxSelect.length}名`);
+            //     return;
+            // }
+            let dataList = this.dataList || []
+            let checkList = []
+            dataList.forEach(item => {
+                if(this.checkboxSelect.includes(item.userId)) {
+                    checkList.push(item)
+                }
+            })
+            this.$emit('submit', checkList)
+        }
     }
 }
 </script>
@@ -165,9 +170,9 @@ export default {
 @import url("../../../assets/commonLess/variable.less");
 .studentContainer {
     /deep/.van-cell__title {
-        font-size: 0.14rem;
+        font-size: 0.16rem;
         color: @mFontColor;
-        flex: 1 auto;
+        // flex: 1 auto;
     }
 
     .logo {
@@ -200,6 +205,11 @@ export default {
         margin-left: 0.08rem;
     }
 }
+.value-times {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+}
 .paddingB80 {
     padding-bottom: 0.8rem;
 }

+ 627 - 30
src/views/activeProgram/program.vue

@@ -1,36 +1,35 @@
 <template>
     <div class="program">
         <van-cell-group class="van-cell-group--inset">
-            <van-cell title="2020年双十一1v1活动" class="titleContent" title-class="titleStyle" label-class="labelStyle">
+            <van-cell :title="vipGroup.name" class="titleContent" title-class="titleStyle" label-class="labelStyle">
                 <template #label>
-                    <p>2020年双十一VIP1v1活动</p>
-                    <p>11.11抵扣1111</p>
+                    <p>{{ vipGroup.description }}</p>
+                    <p>排课时间范围:{{ vipGroup.coursesStartTime }} 至 {{ vipGroup.coursesEndTime }}</p>
                 </template>
             </van-cell>
         </van-cell-group>
 
-        <h2 class="van-block__title">付费课程排课</h2>
+        <h2 class="van-block__title">{{ typeStatus ? '付费' : '赠送' }}课程排课</h2>
         <van-cell-group>
             <van-field
-                v-model="formName.courseType"
+                :value="typeStatus ? courseType[vipGroup.courseType] : courseType[vipGroup.giveCourseType]"
                 label="课程类型"
                 :readonly="true"
                 input-align="right"
-                is-link
                 size="large"
                 placeholder="请选择"
             />
             <van-field
-                v-model="formName.vipGroupCategoryName"
+                :value="typeStatus ? vipGroup.vipGroupCategoryNames : vipGroup.giveCategoryName"
                 label="课程形式"
                 :readonly="true"
                 input-align="right"
-                is-link
                 size="large"
                 placeholder="请选择"
             />
             <van-field
                 v-model="formName.subjectListName"
+                @click="onGetSheetList('subjectList')"
                 label="排课声部"
                 :readonly="true"
                 input-align="right"
@@ -40,6 +39,7 @@
             />
             <van-field
                 v-model="formName.educationalTeacherName"
+                @click="onGetSheetList('teacherList')"
                 label="乐团主管"
                 :readonly="true"
                 input-align="right"
@@ -52,9 +52,9 @@
         <template v-if="studentList.length > 0">
             <h2 class="van-block__title">上课学员</h2>
             <van-cell-group>
-                <van-cell title-style="flex: 1 auto;" size="large" v-for="(item, index) in studentList" :key="index">
+                <van-cell title-style="flex: 1 auto; color: #1A1A1A;" size="large" v-for="(item, index) in studentList" :key="index">
                     <template #title>
-                        {{ item.userName }} - {{ item.phone }}
+                        {{ item.username }} - {{ item.phone }}
                     </template>
                     <template #default>
                         <span @click="onDelete('student', item)"><van-icon name="delete-o" size=".14rem" /> <span style="font-size: .12rem;">删除</span></span>
@@ -69,8 +69,30 @@
         <h2 class="van-block__title">课时组成</h2>
         <van-cell-group>
             <van-field
-                v-model="formName.subjectListName"
-                label="班级人数"
+                v-if="statusList.hasOnline && teachMode == -1"
+                v-model="form.onlineClassesNums"
+                @keyup="onClassKeyUp"
+                label="线上课次数"
+                input-align="right"
+                size="large"
+                placeholder="请输入次数"
+                type="number"
+            />
+            <van-field
+                v-if="statusList.hasOffline && teachMode == -1"
+                v-model="form.offlineClassesNums"
+                @keyup="onClassKeyUp('offLine')"
+                label="线下课次数"
+                input-align="right"
+                size="large"
+                placeholder="请输入次数"
+                type="number"
+            />
+            <van-field
+                v-if="tempOfflineNum > 0"
+                v-model="formName.teacherSchoolName"
+                @click="onGetSheetList('teacherSchool')"
+                label="线下课地址"
                 :readonly="true"
                 input-align="right"
                 is-link
@@ -78,35 +100,40 @@
                 placeholder="请选择"
             />
             <van-field
-                v-model="formName.vipGroupCategoryName"
+                :value="form.studentNum + '人'"
+                label="班级人数"
+                :readonly="true"
+                input-align="right"
+                size="large"
+            />
+            <van-field
+                :value="(form.singleClassMinutes || 0) + '分钟'"
                 label="单课时时长"
                 :readonly="true"
                 input-align="right"
-                is-link
                 size="large"
-                placeholder="请选择"
             />
         </van-cell-group>
 
         <h2 class="van-block__title">课时安排</h2>
         <van-cell-group>
             <van-field
-                v-model="formName.subjectListName"
+                :value="((typeStatus ? vipGroup.minCourseNum : vipGroup.giveCourseNum) || 0) + '课时'"
                 label="课时总数"
                 :readonly="true"
                 input-align="right"
-                is-link
                 size="large"
                 placeholder="请输入排课课时数"
             />
             <van-field
-                v-model="formName.vipGroupCategoryName"
+                v-model="form.courseStart"
                 label="排课开始时间"
                 :readonly="true"
                 input-align="right"
                 is-link
                 size="large"
                 placeholder="请选择"
+                @click="dataForm.status = true"
             />
         </van-cell-group>
 
@@ -130,54 +157,399 @@
             <van-icon name="plus" /> 添加课时安排
         </div>
 
+        <van-cell-group>
+            <van-field
+                label="排课列表"
+                v-if="scheduleList.length > 0"
+                disabled
+                input-align="right"
+                @click="onShowTimeTable"
+                is-link
+                size="large"
+            />
+        </van-cell-group>
+
         <div class="button-group">
             <van-button type="primary" @click="onSubmit" round size="large">确认</van-button>
         </div>
 
-        <!-- 选择上课学生 -->
+        <!-- 选择上课学 -->
         <van-popup
             v-model="studentStatus"
             :lock-scroll="true"
             position="bottom"
-            :style="{ height: '180%' }"
-            class="studentChiose"
+            :style="{ height: '80%' }"
+            class="studentChose"
         >
-            <student-list @close="studentStatus = false" :studentList="studentList" @submit="onSelectStudent" />
+            <student-list
+                @close="studentStatus = false"
+                :studentList="studentList"
+                :activityId="activityId"
+                :studentNum="form.studentNum"
+                :courseTypeIsVip="courseTypeIsVip"
+                :typeStatus="typeStatus"
+                @submit="onSelectStudent" />
+        </van-popup>
+
+        <!-- 排课开始时间 -->
+        <van-popup v-model="dataForm.status" position="bottom">
+            <van-datetime-picker
+                v-model="dataForm.currentDate"
+                type="date"
+                :min-date="dataForm.minDate"
+                :max-date="dataForm.maxDate"
+                :formatter="formatter"
+                @cancel="dataForm.status = false"
+                @confirm="onCurrentConfirm"
+            />
         </van-popup>
         <!-- 课时安排 -->
         <van-popup v-model="teachingStatus" position="bottom">
             <course-modal :scheduleList="scheduleList" :singleClassMinutes="form.singleClassMinutes" @close="teachingStatus = false" />
         </van-popup>
+
+        <van-popup v-model="sheetForm.sheetStatus" position="bottom">
+            <van-picker
+                :loading="sheetForm.loading"
+                :default-index="sheetForm.index"
+                :columns="sheetForm.columns"
+                show-toolbar
+                @cancel="sheetForm.sheetStatus = false"
+                @confirm="onSheetConfirm"
+            />
+        </van-popup>
+
+        <!-- 课表展示 -->
+        <van-popup v-model="statusList.classTime" position="bottom" >
+            <van-row>
+                <van-col span="12">上课类型</van-col>
+                <van-col span="12">上课时间</van-col>
+            </van-row>
+            <div class="tableContainer">
+                <van-row v-for="(item, index) in timeTable" :key="index">
+                <van-col span="12">
+                    {{ item.teachMode == "ONLINE" ? "线上" : "线下" }}
+                </van-col>
+                <van-col span="12">
+                    {{ item.classDate }} {{ item.startClassTimeStr }}
+                </van-col>
+                </van-row>
+            </div>
+        </van-popup>
     </div>
 </template>
 
 <script>
+import dayjs from 'dayjs'
 import studentList from './modal/studentList'
 import courseModal from './modal/course'
+import { courseType } from '../../constant'
+import { getActivityWaitCourseStudentNum, createVipGroup, createPracticeGroup } from './api'
+import { findSubSubjects, findEducationUsers, findVipSchoolByTeacher2 } from "@/api/teacher";
 export default {
     components: { studentList, courseModal },
     data() {
+        const query = this.$route.query
         return {
+            courseType,
+            type: query.type,
+            activityId: query.activityId,
+            vipDetail: {},
+            vipGroup: {},
             studentStatus: false,
             studentList: [],
+            checkboxSelectIds: [],
+            teachMode: null, // -1:所有;0:线上;1:线下"
+            sheetForm: {
+                // 上拉弹窗
+                currentType: null, // 当前选择的类型
+                sheetStatus: false,
+                loading: true, // 加载数据
+                index: 0, // 选中的索引值
+                columns: [],
+            },
+            timeTable: [], // 生成的课表
+            loadData: {
+                // 下拉加载数据
+                subjectList: [], // 声部列表
+                teacherList: [],
+                teacherSchool: [], // 线下课地址
+            },
+            tempOfflineNum: 0, // 临时存放线下课次数
             form: {
-                singleClassMinutes: 45
+                vipGroupCategoryId: null,
+                subjectIdList: null,
+                educationalTeacherId: null,
+                singleClassMinutes: null,
+                onlineClassesNums: null,
+                offlineClassesNums: null,
+                totalClassTime: null, // 总课时数
+                studentNum: null, // 每班人数
+                courseStart: null, // 排课开始时间
+                teacherSchoolId: null,
+                vipGroupActivityId: query.activityId,
             },
             formName: {
-                courseType: 'VIP课',
+                vipGroupCategoryId: null,
+                subjectListName: null,
+                subjectListIndex: 0, // 声部名称
+                educationalTeacherName: null, // 乐团主管
+                educationalTeacherIndex: 0,
+                teacherSchoolName: null,
+                teacherSchoolIndex: 0, // 线下课地址
+            },
+            statusList: {
+                hasOnline: false, // 是否显示线上
+                hasOffline: false, // 是否显示线下
+                classTime: false, // 查看排课列表
             },
             scheduleList: [],
             // 排课弹窗
             teachingStatus: false, // 课时安排状态
+            dataForm: {
+                // 时间下拉框
+                status: false,
+                minDate: new Date(),
+                maxDate: new Date(2035, 10, 1),
+                currentDate: new Date(),
+            },
         }
     },
+    computed: {
+        typeStatus() { // 是否是付费课程
+            return this.type == 'pay' ? true : false
+        },
+        courseTypeIsVip() { // 目前只有两种课程,VIP 网管课,则可以这样判断
+            const type = this.typeStatus ? this.vipGroup.courseType : this.vipGroup.giveCourseType
+            return type == 'VIP' ? true : false
+        }
+    },
+    mounted() {
+        this.__init()
+    },
     methods: {
-        onSubmit() {
-            console.log('排课了')
+        async __init() {
+            try {
+                let res = await getActivityWaitCourseStudentNum({ activityId: this.activityId })
+                this.vipDetail = res.data
+                let vipGroup = res.data.vipGroupActivity
+                vipGroup.coursesStartTime = dayjs(vipGroup.coursesStartTime).format('YYYY-MM-DD')
+                vipGroup.coursesEndTime = dayjs(vipGroup.coursesEndTime).format('YYYY-MM-DD')
+                this.vipGroup = vipGroup
+                let form = this.form
+                // 课程形式
+                form.vipGroupCategoryId = this.typeStatus ? vipGroup.vipGroupCategoryIdList : vipGroup.giveCategoryId
+                // 单课时长
+                form.singleClassMinutes = this.typeStatus ? vipGroup.singleCourseTime : vipGroup.giveSingleCourseTime
+                // 排课次数,活动排课没有范围一说,最大次数和最小次数必须一致
+                form.totalClassTime = this.typeStatus ? vipGroup.minCourseNum : vipGroup.giveCourseNum
+                // 每班人数
+                form.studentNum = this.typeStatus ? vipGroup.vipGroupCategoryNum : vipGroup.giveCategoryNum
+                this.statusList.hasOnline = this.typeStatus ? this.formatStatus('online', vipGroup.teachMode) : this.formatStatus('online', vipGroup.giveTeachMode)
+                this.statusList.hasOffline = this.typeStatus ? this.formatStatus('offline', vipGroup.teachMode) : this.formatStatus('online', vipGroup.giveTeachMode)
+
+                // 如果
+                if(this.teachMode == 0) {
+                    form.onlineClassesNums = form.totalClassTime || 0
+                    form.offlineClassesNums = 0
+                } else if(this.teachMode == 1) {
+                    form.onlineClassesNums = 0
+                    form.offlineClassesNums = form.totalClassTime || 0
+                    this.tempOfflineNum = form.totalClassTime || 0
+                }
+            } catch {
+                //
+            }
+        },
+        onGetSheetList(name) {
+            // 获取科目列表
+            let sheetForm = this.sheetForm;
+            sheetForm.columns = [];
+            sheetForm.sheetStatus = true;
+            sheetForm.loading = true;
+            sheetForm.currentType = name;
+            sheetForm.index = 0;
+            let arr = this.loadData[name];
+            if (arr.length > 0) {
+                sheetForm.columns = arr;
+                sheetForm.index = this.formName[name + "Index"];
+                sheetForm.loading = false;
+            } else {
+                this.onLoadingData(name);
+            }
+        },
+        onLoadingData() {
+            // 加载数据
+            let sheetForm = this.sheetForm;
+            if (sheetForm.currentType == "subjectList") {
+                // 声部列表
+                findSubSubjects().then((res) => {
+                let result = res.data;
+                if (result.code == 200 && result.data.length > 0) {
+                    let tempArr = [];
+                    result.data.forEach((item) => {
+                        item.value = item.id;
+                        item.text = item.name;
+                        tempArr.push(item);
+                    });
+                    this.loadData.subjectList = tempArr;
+                    sheetForm.columns = tempArr;
+                    sheetForm.loading = false;
+                } else {
+                    this.$toast("暂无科目列表");
+                    sheetForm.loading = false;
+                }
+                });
+            } else if (sheetForm.currentType == "teacherSchool") {
+                // 教师教学点
+                findVipSchoolByTeacher2().then((res) => {
+                    let result = res.data;
+                    if (result.code == 200 && result.data.length > 0) {
+                        let tempArr = [];
+                        result.data.forEach((item) => {
+                            item.value = item.id;
+                            item.text = item.name;
+                            tempArr.push(item);
+                        });
+                        this.loadData.teacherSchool = tempArr;
+                        sheetForm.columns = tempArr;
+                        sheetForm.loading = false;
+                    } else {
+                        this.$toast("暂无教学点");
+                        sheetForm.loading = false;
+                    }
+                });
+            } else if (sheetForm.currentType == "teacherList") {
+                // 乐团主管
+                findEducationUsers().then((res) => {
+                    let result = res.data;
+                    if (result.code == 200 && result.data.length > 0) {
+                        let tempArr = [];
+                        result.data.forEach((item) => {
+                            item.value = item.userId;
+                            item.text = item.userName;
+                            tempArr.push(item);
+                        });
+                        this.loadData.teacherList = tempArr;
+                        sheetForm.columns = tempArr;
+                        sheetForm.loading = false;
+                    } else {
+                        this.$toast("暂无乐团主管");
+                        sheetForm.loading = false;
+                    }
+                });
+            }
+        },
+        onSheetConfirm(value, index) {
+            // 上拉弹窗
+            let sheetForm = this.sheetForm,
+                form = this.form,
+                formName = this.formName
+            if (sheetForm.currentType == "subjectList") {
+                // 科目名称赋值
+                form.subjectIdList = value.value;
+                formName.subjectListName = value.text;
+                formName.subjectListIndex = index;
+            } else if (sheetForm.currentType == "teacherSchool") {
+                // 线下课地址
+                form.teacherSchoolId = value.value;
+                formName.teacherSchoolName = value.text;
+                formName.teacherSchoolIndex = index;
+            } else if (sheetForm.currentType == "teacherList") {
+                // 乐团主管
+                form.educationalTeacherId = value.value;
+                formName.educationalTeacherName = value.text;
+                formName.educationalTeacherIndex = index;
+            }
+
+            sheetForm.sheetStatus = false;
+        },
+        async onSubmit() {
+            // 次数限制是否可以继续创建
+            let form = this.form;
+            let statusList = this.statusList;
+
+            if (!form.subjectIdList) {
+                this.$toast("请选择排课声部");
+                return false;
+            }
+            if (!form.educationalTeacherId) {
+                this.$toast("请选择乐团主管");
+                return;
+            }
+            if (this.checkboxSelectIds.length <= 0) {
+                this.$toast("请选择上课学员");
+                return;
+            }
+
+            let onlineClassesStatus = !form.onlineClassesNums && form.onlineClassesNums <= 0 ? true : false;
+            let offlineClassesStatus = !form.offlineClassesNums && form.offlineClassesNums <= 0 ? true : false;
+
+            if (statusList.hasOnline && onlineClassesStatus) {
+                this.$toast("请输入线上课次数");
+                return false;
+            }
+
+            if (statusList.hasOffline) {
+                if (offlineClassesStatus) {
+                    this.$toast("请输入线下课次数");
+                    return false;
+                }
+
+                // 判断是否有线下
+                if (form.offlineClassesNums > 0 && !form.teacherSchoolId) {
+                    this.$toast("请选择线下课地址");
+                    return false;
+                }
+            }
+
+            if (this.scheduleList.length <= 0) {
+                this.$toast("课时安排不能为空");
+                return false;
+            }
+            if (!this.checkCourseList()) {
+                return;
+            }
+            // 排课
+            this.setTimeTable();
+
+            form.studentIdList = this.checkboxSelectIds.join(",");
+
+            form.firstStudentId = this.studentList.length > 0 ? this.studentList[0].userId : null;
+            let params = {
+                courseSchedules: this.timeTable,
+                vipGroupApplyBaseInfo: form,
+            }
+            console.log(params)
+            if(this.courseTypeIsVip) {
+                params.giveFlag = false
+                await this.onPayVip(params)
+            } else {
+                params.giveFlag = true
+                await this.onPayPractice(params)
+            }
+        },
+        async onPayVip(params) {
+            try {
+                await createVipGroup(params)
+            } catch {
+                //
+            }
+        },
+        async onPayPractice(params) {
+            try {
+                await createPracticeGroup(params)
+            } catch {
+                //
+            }
         },
         onSelectStudent(items) {
             // 选中的数据
-            this.studentList = items
+            const tempItems = items || []
+            this.studentList = tempItems
+            tempItems.forEach(item => {
+                this.checkboxSelectIds.push(item.userId)
+            })
             this.studentStatus = false
         },
         onDelete(type, item) {
@@ -210,7 +582,200 @@ export default {
                     }
                 })
             }
-        }
+        },
+        onCurrentConfirm(value) {
+            // 排课开始时间
+            this.form.courseStart = dayjs(value).format('YYYY-MM-DD')
+            this.dataForm.status = false;
+        },
+        onClassKeyUp(type) {
+            // 线上课&线下课修改时
+            if(this.teachMode != -1) return
+            let form = this.form
+            let onlineNum = form.onlineClassesNums
+            let offLineNum = form.offlineClassesNums
+            // 重置次数,不能
+            if(parseInt(onlineNum || 0) + parseInt(offLineNum || 0) >= form.totalClassTime) {
+                if(type == 'offLine') {
+                    let diffNum = form.totalClassTime - parseInt(onlineNum || 0)
+                    offLineNum = diffNum < 0 ? 0 : diffNum
+                } else {
+                    let diffNum = form.totalClassTime - parseInt(offLineNum || 0)
+                    onlineNum = diffNum < 0 ? 0 : diffNum
+                }
+            }
+            this.form.onlineClassesNums = onlineNum
+            this.form.offlineClassesNums = offLineNum
+            this.tempOfflineNum = offLineNum || 0
+        },
+        onShowTimeTable() {
+            // 显示排课列表
+            if (!this.checkCourseList()) {
+                return;
+            }
+            this.statusList.classTime = true;
+            this.setTimeTable();
+        },
+        setTimeTable() {
+            if (!this.checkCourseList(false)) {
+                return;
+            }
+            // 重置排课列表
+            this.timeTable = [];
+
+            let form = this.form,
+                scheduleList = this.scheduleList;
+
+            // 拿到线上课数与线下课数 以及
+            let online = parseInt(
+                form.onlineClassesNums ? form.onlineClassesNums : 0
+            );
+            let offline = parseInt(
+                form.offlineClassesNums ? form.offlineClassesNums : 0
+            );
+            // 判断是否有课程安排
+            if (scheduleList.length <= 0) {
+                return;
+            }
+            let totalCount = Number(online) + Number(offline);
+            let tempCourseStart = form.courseStart.replace(/-/gi, "/");
+            let dateOperation = new Date(tempCourseStart);
+            let forMark = 0;
+            while (totalCount && totalCount > 0) {
+                for (let i = 0; i < scheduleList.length; i++) {
+                if (online == 0 && offline == 0) break;
+                let num = scheduleList[i].weekIndex - dateOperation.getDay();
+                // 如果是同一天一个周期会出现排课都排到一天
+                if (forMark > 0 && num == 0 && i == 0) {
+                    num = num + 7;
+                }
+                if (num < 0) {
+                    // 如果为负数则为下周
+                    num = num + 7;
+                }
+                let dataStr = this.getThinkDate(dateOperation, num);
+
+                // 判断是否大于当前时间
+                let nowGetTime = new Date().getTime();
+                let courseTime = new Date(dataStr.replace(/-/gi, "/") +" " + scheduleList[i].startTime + ":00").getTime();
+                if (nowGetTime < courseTime) {
+                    let tempArr = {
+                        classDate: dataStr,
+                        startClassTimeStr: scheduleList[i].startTime,
+                        endClassTimeStr: scheduleList[i].endTime,
+                    };
+                    // console.log(scheduleList[i].type, online, offline)
+                    if (scheduleList[i].type == "线上" && online > 0) {
+                        tempArr.teachMode = "ONLINE";
+                        this.timeTable.push(tempArr);
+                        online--;
+
+                        totalCount--;
+                    } else if (scheduleList[i].type == "线下" && offline > 0) {
+                        tempArr.teachMode = "OFFLINE";
+                        this.timeTable.push(tempArr);
+                        offline--;
+
+                        totalCount--;
+                    }
+                }
+                }
+                // 加一周
+                if (scheduleList.length == 1) {
+                dateOperation.setDate(dateOperation.getDate() + 7);
+                } else if (
+                scheduleList.every((item) => item.weekStr === scheduleList[0].weekStr)
+                ) {
+                // 标记循环次数(标记判断课程安排是不是同一天)
+                forMark++;
+                }
+            }
+
+            this.timeTable.sort((a, b) => {
+                let aStr = dayjs(dayjs(a.classDate).format("YYYY-MM-DD") + " " + a.startClassTimeStr + ":00").valueOf();
+                let bStr = dayjs(dayjs(b.classDate).format("YYYY-MM-DD") + " " + b.startClassTimeStr + ":00").valueOf();
+                return aStr - bStr;
+            });
+        },
+        getThinkDate(date, num) {
+            let Stamp = date;
+            Stamp.setDate(date.getDate() + num); // 获取当前月数的第几天
+            return dayjs(Stamp).format('YYYY-MM-DD')
+        },
+        checkCourseList(isShowToast = true) {
+            let form = this.form;
+            let scheduleList = this.scheduleList || [];
+            let hasOnLine = false; // 是否有线上课时安排
+            let hasOffLine = false;
+            scheduleList.forEach((item) => {
+                if (item.type == "线上") {
+                    hasOnLine = true;
+                }
+                if (item.type == "线下") {
+                    hasOffLine = true;
+                }
+            });
+            let statusList = this.statusList;
+            let onlineClassesStatus = !form.onlineClassesNums && form.onlineClassesNums <= 0 ? true : false;
+            let offlineClassesStatus = !form.offlineClassesNums && form.offlineClassesNums <= 0 ? true : false;
+            if (statusList.hasOnline) {
+                if (onlineClassesStatus) {
+                    if (isShowToast) {
+                        this.$toast("请输入线上课次数");
+                    }
+                    return false;
+                }
+                if (!onlineClassesStatus && !hasOnLine && form.onlineClassesNums > 0) {
+                    if (isShowToast) {
+                        this.$toast("课时安排缺少线上课类型");
+                    }
+                    return false;
+                }
+            }
+
+            if (statusList.hasOffline) {
+                if (offlineClassesStatus) {
+                    if (isShowToast) {
+                        this.$toast("请输入线下课次数");
+                    }
+                    return false;
+                }
+                if (
+                !offlineClassesStatus &&
+                !hasOffLine &&
+                form.offlineClassesNums > 0
+                ) {
+                    if (isShowToast) {
+                        this.$toast("课时安排缺少线下课类型");
+                    }
+                    return false;
+                }
+            }
+            return true;
+        },
+        formatStatus(type, teachMode) {
+            // -1:所有;0:线上;1:线下
+            this.teachMode = teachMode
+            if(type == 'online' && teachMode == 0) {
+                return true
+            } else if(type == 'offline' && teachMode == 1) {
+                return true
+            } else if(teachMode == -1) {
+                return true
+            } else {
+                return false
+            }
+        },
+        formatter(type, value) {
+            if (type === "year") {
+                return `${value}年`;
+            } else if (type === "month") {
+                return `${value}月`;
+            } else if (type === "day") {
+                return `${value}日`;
+            }
+            return value;
+        },
     }
 }
 </script>
@@ -241,6 +806,31 @@ export default {
         line-height: .2rem;
     }
 }
+.van-row {
+    line-height: 0.4rem;
+    border-top: 1px solid #edeef0;
+    text-align: center;
+    font-size: 0.14rem;
+
+    &:first-child {
+        border-top: 0;
+        background: #edeef0;
+        color: #444;
+        font-size: 0.15rem;
+    }
+}
+.tableContainer {
+    max-height: 2.44rem;
+    overflow: auto;
+    .van-row {
+        color: #444;
+        &:first-child {
+            border-top: 0;
+            background: #fff;
+            font-size: 0.14rem;
+        }
+    }
+}
 .van-block__title {
     padding: .12rem .14rem .06rem;
     color: #808080;
@@ -267,9 +857,16 @@ export default {
         padding-right: 0.15rem;
     }
 }
-/deep/.studentChiose {
-    border-radius: 0 0 0px 0px;
+/deep/.studentChose {
+    border-radius: .1rem .1rem 0px 0px;
     overflow: auto;
+    background: #F5F5F5;
+}
+/deep/.van-field__label {
+    color: #1A1A1A;
+}
+/deep/.van-field__control {
+    font-size: 16px;
 }
 .addButton {
     margin: .1rem .28rem;

+ 1 - 1
vue.config.js

@@ -2,7 +2,7 @@ let targetUrl = 'https://mteatest.dayaedu.com'
 // let targetUrl = 'http://192.168.3.139:8000' // 箭河
 // let targetUrl = 'https://online.dayaedu.com'
 // let targetUrl = 'http://dev.dayaedu.com/'
-// let targetUrl = 'http://192.168.3.196'
+// let targetUrl = 'http://192.168.3.124:8000'
 module.exports = {
   chainWebpack: config => {
     config.devtool('inline-source-map')