فهرست منبع

评价,回访功能

lex-xin 3 سال پیش
والد
کامیت
12ec043993

+ 1 - 1
public/index.html

@@ -1,5 +1,5 @@
 <!DOCTYPE html>
-<html lang="en">
+<html lang="zh-cn">
   <head>
     <meta charset="utf-8">
     <meta http-equiv="X-UA-Compatible" content="IE=edge">

+ 2 - 3
src/App.vue

@@ -19,9 +19,8 @@ export default {
     const whiteList = ["#/order"];
     // const routePath = this.$route.path;
     const locationHash = window.location.hash;
-    console.log(this.$route.path, locationHash);
-    if (locationHash.includes('#/order') == -1) {
-      console.log('true')
+    // console.log(this.$route.path, locationHash);
+    if (locationHash.indexOf('#/order') == -1) {
       try {
         let Authorization = this.getQueryVariable("Authorization") || null;
         if (window.location.hash.indexOf("+") >= 0) {

BIN
src/assets/images/icon_nodata.png


+ 8 - 0
src/router/index.js

@@ -70,6 +70,14 @@ let defaultRouter = [
           descrition: '课后评价',
           weight: 0
       }
+    }, {
+      path: '/afterClassEvaluateDetail',
+      name: 'afterClassEvaluateDetail',
+      component: () => import(/* webpackChunkName:'afterClassEvaluateDetail' */'@/views/afterClassEvaluate/detail.vue'),
+      meta: {
+          descrition: '课后详情',
+          weight: 0
+      }
     }
 ]
 

+ 9 - 0
src/router/teacherRouter.js

@@ -243,6 +243,15 @@ let teacherRouter = [
       weight: 8 // 页面权重
     }
   }, {
+    path: "/visitDetail",
+    name: "visitDetail",
+    component: () =>
+      import(/* webpackChunkName: "visitDetail" */ "@/views/visitManager/visitDetail"),
+    meta: {
+      descrition: "回访记录",
+      weight: 8 // 页面权重
+    }
+  }, {
     path: '/rules',
     name: 'rules',
     component: () => import(/* webpackChunkName:'UserProtocol'*/'@/views/rules/index.vue'),

+ 61 - 0
src/views/afterClassEvaluate/api.js

@@ -0,0 +1,61 @@
+import request from '@/helpers/request'
+
+export const courseEvaluateList = (data) => {
+  return request({
+    url: '/teacherCourseSchedule/courseEvaluateList',
+    method: 'post',
+    data
+  })
+}
+
+// 获取课程头部信息
+export const getCourseInfoHead = (data) => {
+  return request({
+    url: '/courseReview/getCourseInfoHead',
+    method: 'get',
+    params: data
+  })
+}
+
+// 获取评论详情
+export const getReviewInfo = (data) => {
+  return request({
+    url: '/courseReview/getReviewInfo',
+    method: 'get',
+    params: data
+  })
+}
+
+export const studyStandardList = (data)=> {
+  return request({
+    url: '/teacherCourseSchedule/studyStandardList',
+    method: 'post',
+    requestType: 'form',
+    data
+  })
+}
+
+export const checkeIsAssignHomework = (data)=> {
+  return request({
+    url: '/teacherCourseHomeworkReply/checkeIsAssignHomework',
+    method: 'get',
+    params: data
+  })
+}
+
+// 提交评论
+export const courseReviewAdd = (data)=> {
+  return request({
+    url: '/courseReview/add',
+    method: 'post',
+    data
+  })
+}
+
+export const getCurrentCourseDetail = (data)=> {
+  return request({
+    url: '/teacherCourseSchedule/getCurrentCourseDetail',
+    method: 'get',
+    params: data
+  })
+}

+ 288 - 0
src/views/afterClassEvaluate/detail.vue

@@ -0,0 +1,288 @@
+<template>
+  <div class="detail">
+    <item :isComment="false" :dataInfo="dataInfo" />
+
+    <van-cell-group class="section">
+      <p class="work title">
+        <van-icon :name="icon_content" size="22" style="padding-right: .06rem;" />
+        教学内容
+      </p>
+      <van-field
+        rows="1"
+        type="textarea"
+        :disabled="isReview"
+        v-model="memo"
+        placeholder="请输入本课程教学内容"
+      />
+    </van-cell-group>
+
+    <van-cell-group class="section">
+      <van-cell
+        :is-link="!isAssignHomework && !isReview"
+        :value="isAssignHomework ? '已布置' : '未布置'"
+        @click="onWork()"
+        title-class="work"
+      >
+        <template #title>
+          <van-icon :name="icon_class" size="22" style="padding-right: .06rem;" />
+          课后训练
+        </template>
+      </van-cell>
+    </van-cell-group>
+
+    <van-cell-group class="section" :style="{ marginBottom: isReview ? '.2rem' : '1rem' }">
+      <p class="work title">
+        <van-icon :name="icon_nice" size="22" style="padding-right: .06rem;" />
+        学员是否达到学习标准
+      </p>
+      <div style="padding: .08rem 0;">
+        <template v-if="studentList && studentList.length >0">
+          <van-cell center title-class="titleStyle" v-for="(item, index) in studentList" :key="index" :border="false">
+            <template #icon>
+              <img class="student-icon" v-if="item.avatar" :src="item.avatar" alt="" />
+              <img class="student-icon" v-else src="../../assets/images/icon_student.png" alt="" />
+            </template>
+            <template #title>
+              <span>{{ item.username }}</span>
+              {{ item.subjectName }}
+            </template>
+            <template #default>
+              <div v-if="isReview">
+                <van-button type="primary" size="small" style="min-width: 64px" disabled round :plain="item.qualifiedFlag !== 1">{{ item.qualifiedFlag == 1 ? '达标' : '不达标' }}</van-button>
+              </div>
+              <div v-else>
+                <van-button type="primary" size="small" style="min-width: 64px" @click="item.qualifiedFlag = 1" round :plain="item.qualifiedFlag !== 1">达标</van-button>
+                <van-button type="primary" size="small" style="min-width: 64px" @click="item.qualifiedFlag = 0" round :plain="item.qualifiedFlag !== 0">不达标</van-button>
+              </div>
+            </template>
+          </van-cell>
+        </template>
+        <MEmpty v-else msg="暂无学员" />
+      </div>
+    </van-cell-group>
+
+    <div class="button-group" v-if="!isReview">
+      <van-button type="primary" round size="large" @click="submitReview">
+        提交评价
+      </van-button>
+    </div>
+  </div>
+</template>
+
+<script>
+import { getCourseInfoHead, getReviewInfo, studyStandardList, checkeIsAssignHomework, courseReviewAdd, getCurrentCourseDetail } from './api'
+import Item from "./modal/item";
+import MEmpty from '@/components/MEmpty';
+export default {
+  name: "afterClassEvaluateDetail",
+  components: { Item, MEmpty },
+  data() {
+    const query = this.$route.query;
+    return {
+      id: query.id,
+      isInside: query.isInside,
+      reviewId: query.reviewId,
+      isAssignHomework: 0, // 是否有布置作业,0 => 否 1 => 是
+      enableAssignHomework: 0, // 是否要布置作业  0 否  1 是
+      memberStatus: 0, // 是否有学生或者乐团是会员
+      tenantId: null, // 当前机构编号
+      icon_content: require('@/views/afterClassEvaluate/images/icon_content.png'),
+      icon_class: require('@/views/afterClassEvaluate/images/icon_class.png'),
+      icon_nice: require('@/views/afterClassEvaluate/images/icon_nice.png'),
+      dataInfo: {},
+      studentList: [],
+      memo: null,
+    };
+  },
+  computed: {
+    isReview() {
+      return !!(this.reviewId && this.reviewId > 0);
+    },
+  },
+  created() {
+		this.tenantId = sessionStorage.getItem('tenantId') || null
+	},
+  async mounted() {
+    try {
+      // 获取是否有布置作业
+      const homeWork = await checkeIsAssignHomework({ courseScheduleId: this.id });
+      let tempData = homeWork.data || {}
+      this.isAssignHomework = tempData.isAssignHomework;
+      this.enableAssignHomework = tempData.enableAssignHomework;
+      const memberNum = tempData.memberNum;
+      const courseStudentNum = tempData.courseStudentNum;
+      if(courseStudentNum == memberNum) {
+        this.memberStatus = 1
+      }
+
+      const detail = await getCurrentCourseDetail({ courseID: this.id })
+      const resultDetail = detail.data
+      let tempMemo = resultDetail.coursePlan
+      this.dataInfo = {
+        classStartTime: resultDetail.startClassTime,
+        classEndTime: resultDetail.endClassTime,
+        courseType: resultDetail.courseType,
+        courseName: resultDetail.courseScheduleName,
+        teachMode: resultDetail.teachMode,
+        schoolName: resultDetail.address,
+        courseScheduleId: this.id
+      }
+
+      if(this.isReview) {
+        const res = await getReviewInfo({ id: this.reviewId });
+        const result = res.data
+        tempMemo = result.courseScheduleReview.memo || '该课程未设置教学内容'
+      }
+      this.memo = tempMemo
+      //  else {
+      //   const res = await getCourseInfoHead({
+      //     courseId: this.id
+      //   });
+      //   const result = res.data
+      //   params = {
+      //     classStartTime: result.classDate + ' ' + result.startClassTime + ':00',
+      //     classEndTime: result.endClassTime + ':00',
+      //     courseType: result.courseScheduleType,
+      //     courseName: result.classGroupName,
+      //     teachMode: result.teachMode,
+      //     schoolName: null,
+      //     courseScheduleId: this.id
+      //   }
+      // }
+      // this.dataInfo = params
+
+      // 获取学生列表
+      const studyList = await studyStandardList({ courseScheduleId: this.id, page: 1, rows: 200 })
+      const resultStudy = studyList.data
+      this.studentList = resultStudy.rows || []
+      if(!this.reviewId || this.reviewId <= 0) {
+        this.studentList.forEach(item => {
+          item.qualifiedFlag = 1
+        })
+      }
+    } catch {
+      //
+    }
+  },
+  methods: {
+    async submitReview() {
+      // 不是大雅机构则作业非必填
+      if(!this.isAssignHomework && this.enableAssignHomework && this.tenantId == 1) {
+        this.$toast('请布置课后训练')
+        return
+      }
+
+      try {
+        let params = []
+        this.studentList.forEach(item => {
+          params.push({
+            userId: item.userId,
+            qualifiedFlag: item.qualifiedFlag
+          })
+        })
+        let obj = {
+          memo: this.memo, // 备注
+          courseScheduleId: this.id,
+          studentStandardDtos: params
+        };
+        console.log(obj)
+        await courseReviewAdd(obj)
+        this.$toast("提交成功");
+        setTimeout(() => {
+          //  && this.tenantId == 1
+          if (this.isInside) {
+            this.$router.push({ path: "/afterClassEvaluate" });
+          } else {
+            this.onAppBack();
+          }
+        }, 1000);
+      } catch {
+        //
+      }
+    },
+    onWork() {
+      if(this.isAssignHomework == 1 || this.isReview) {
+        return
+      }
+      this.$router.push({
+        path: "/arrangeWork",
+        query: {
+          courseId: this.courseId,
+          memberNum: this.memberStatus,
+          classGroupName: this.classGroupName,
+          ...this.$route.query
+        },
+      });
+    },
+  }
+};
+</script>
+
+<style lang="less" scoped>
+@import url("../../assets/commonLess/variable.less");
+.detail {
+  min-height: 100vh;
+  overflow: hidden;
+  position: relative;
+}
+/deep/.van-button--plain {
+  background: #fff;
+  color: @mColor;
+}
+/deep/.van-button+.van-button {
+  margin-left: 0.1rem;
+}
+.section {
+  margin: 0.1rem 0.12rem 0;
+  border-radius: .1rem;
+  overflow: hidden;
+  .work {
+    display: flex;
+    align-items: center;
+    font-size:16px;
+    color: #333;
+  }
+  .title {
+    padding: 12px 16px 0;
+  }
+
+  .student-icon {
+    width: 32px;
+    height: 32px;
+    border-radius: 50%;
+  }
+  .titleStyle {
+    font-size: 16px;
+    color: #666666;
+    span {
+      color: #1A1A1A;
+      padding: 0 .1rem;
+    }
+  }
+}
+
+.button-group {
+  position: fixed;
+  bottom: 0;
+  left: 0;
+  right: 0;
+  padding: 0.2rem .16rem 0.15rem;
+  background: #f3f4f8;
+  border: 0.01rem solid #f3f4f8;
+  .van-button--primary {
+    font-size: 0.18rem;
+    height: 0.48rem;
+    line-height: 0.48rem;
+  }
+}
+/deep/.mempty .icon {
+  margin-top: 0;
+}
+/deep/.van-button--disabled {
+  opacity: 0.8;
+}
+/deep/.van-field__control:disabled {
+  color: #a1a1a1 !important;
+  -webkit-text-fill-color: #a1a1a1 !important;
+}
+</style>

BIN
src/views/afterClassEvaluate/images/dot.png


BIN
src/views/afterClassEvaluate/images/icon_address.png


BIN
src/views/afterClassEvaluate/images/icon_class.png


BIN
src/views/afterClassEvaluate/images/icon_content.png


BIN
src/views/afterClassEvaluate/images/icon_music.png


BIN
src/views/afterClassEvaluate/images/icon_nice.png


BIN
src/views/afterClassEvaluate/images/icon_p.png


BIN
src/views/afterClassEvaluate/images/icon_phone.png


BIN
src/views/afterClassEvaluate/images/icon_timer.png


BIN
src/views/afterClassEvaluate/images/icon_vip.png


+ 113 - 12
src/views/afterClassEvaluate/index.vue

@@ -1,26 +1,127 @@
 <template>
-  <div>
-    课后价格
+  <div class="afterClassEvaluate">
+    <van-sticky>
+      <m-header v-if="headerStatus" :isFixed="false" />
+      <van-tabs
+        v-model="params.evaluateFlag"
+        title-active-color="#000000"
+        title-inactive-color="#808080"
+        @change="onTabChange"
+        color="#01C1B5"
+        class="van-hairline--bottom"
+      >
+        <van-tab title="待评价课程" :name="0"> </van-tab>
+        <van-tab title="评价记录" :name="1"> </van-tab>
+      </van-tabs>
+    </van-sticky>
+
+    <van-list
+      v-model="loading"
+      v-if="dataShow"
+      :finished="finished"
+      finished-text="- 没有更多了 -"
+      :immediate-check="false"
+      @load="getList"
+    >
+      <item v-for="(item, i) in list" :dataInfo="item" :key="i" @onItem="onItem" />
+    </van-list>
+    <m-empty v-else msg="暂无课后评价" />
   </div>
 </template>
 
 <script>
+import dayjs from 'dayjs';
+import MHeader from "@/components/MHeader";
+import MEmpty from '@/components/MEmpty';
+import { browser } from "@/common/common";
+import { courseEvaluateList } from './api'
+import Item from './modal/item'
+import { postMessage } from '@/helpers/native-message'
 export default {
-  name: 'afterClassEvaluate',
-  data () {
+  name: "afterClassEvaluate",
+  components: { MHeader, MEmpty, Item },
+  data() {
+    let evaluateFlag = sessionStorage.getItem('afterClassEvaluate')
+    sessionStorage.removeItem('afterClassEvaluate')
     return {
-
-    }
+      headerStatus: true,
+      list: [],
+      loading: false,
+      finished: false,
+      params: {
+        evaluateFlag: Number(evaluateFlag) || 0,
+        page: 1,
+        rows: 20,
+      },
+      dataShow: true,
+    };
   },
   methods: {
-
+    onItem(item) {
+      // if(this.params.evaluateFlag === 0 && item.groupType == 'MUSIC') {
+      //   postMessage({ api: 'openMusicEvaluate', content: { courseScheduleId: item.courseScheduleId, masterTeacherFlag: item.masterTeacherFlag} })
+      // } else {
+        this.$router.push({
+          path: '/afterClassEvaluateDetail',
+          query: {
+            id: item.courseScheduleId,
+            reviewId: item.courseScheduleReviewId,
+            isInside: true // 判断是否是内部跳转
+          }
+        })
+      // }
+    },
+    onTabChange(val) {
+      sessionStorage.setItem('afterClassEvaluate', val)
+      this.list = [];
+      this.params.page = 1;
+      this.params.evaluateFlag = val;
+      this.dataShow = true;
+      this.loading = true;
+      this.finished = false;
+      this.getList()
+    },
+    async getList() {
+      let params = this.params;
+      try {
+        let res = await courseEvaluateList({ ...params });
+        let result = res.data;
+        this.loading = false;
+        if (params.page > result.pageInfo.pageNo) {
+          return;
+        }
+        params.page = result.pageInfo.pageNo;
+        const rows = result.pageInfo.rows
+        rows.forEach(item => {
+          item.classEndTime = dayjs(item.classEndTime).format('HH:mm:ss')
+        })
+        this.list = this.list.concat(rows);
+        if (params.page >= result.pageInfo.totalPage) {
+          this.finished = true;
+        }
+        this.params.page++;
+        // 判断是否有数据
+        if (this.list.length <= 0) {
+          this.dataShow = false;
+        }
+      } catch {
+        this.finished = true;
+        this.dataShow = false;
+      }
+    },
   },
-  mounted () {
+  async mounted() {
+    if (browser().android || browser().iPhone) {
+      this.headerStatus = false;
+    }
 
-  }
-}
+    await this.getList()
+  },
+};
 </script>
 
 <style lang="less" scoped>
-
-</style>
+.afterClassEvaluate {
+  min-height: 100vh;
+}
+</style>

+ 144 - 0
src/views/afterClassEvaluate/modal/item.vue

@@ -0,0 +1,144 @@
+<template>
+  <van-cell-group class="item" @click="onItem" v-show="dataInfo.courseScheduleId">
+    <van-cell title-class="timer" class="comment">
+      <template #title>
+        <van-icon :name="icon_timer" size="16" style="padding-right: .06rem;margin-top: 0.02rem;" />
+        {{dataInfo.classStartTime}}-{{ dataInfo.classEndTime }}
+      </template>
+      <template #default>
+        <span v-show="isComment" style="color: #01C1B5; font-size: .15rem">
+          {{ dataInfo.courseScheduleReviewId ?  '已评价' : '待评价' }}
+        </span>
+      </template>
+    </van-cell>
+    <van-cell label-class="address">
+      <template #title>
+        <div class="course-info">
+          <div class="course-img">
+            <!-- courseType -->
+            <img v-if="dataInfo.courseType === 'VIP'" src="../images/icon_vip.png" alt="">
+            <img v-if="dataInfo.courseType === 'PRACTICE'" src="../images/icon_p.png" alt="">
+            <img v-if="dataInfo.courseType !== 'VIP' && dataInfo.courseType !== 'PRACTICE'" src="../images/icon_music.png" alt="">
+          </div>
+          <div class="course-name">
+            {{ dataInfo.courseName }}<span class="teachMode" v-show="dataInfo.teachMode">{{ dataInfo.teachMode === 'ONLINE' ? '线上' : '线下' }}</span>
+          </div>
+        </div>
+      </template>
+      <template #label>
+        <van-icon v-if="dataInfo.schoolName || dataInfo.teachMode == 'ONLINE'" :name="icon_address" class="icon-address" style="padding-right: .02rem;vertical-align: middle;" />
+        {{ dataInfo.teachMode == 'ONLINE' ? '上课地点:网络教室' : dataInfo.schoolName }}
+      </template>
+    </van-cell>
+  </van-cell-group>
+</template>
+
+<script>
+// classStartTime
+// classEndTime
+// courseType
+// courseName
+// teachMode
+// schoolName
+// courseScheduleId
+import dayjs from 'dayjs';
+export default {
+  name: "item",
+  props: {
+    dataInfo: { // 单个课程对象
+      type: Object,
+      default() {
+        return {}
+      }
+    },
+    isComment: {
+      type: Boolean,
+      default: true
+    }
+  },
+  data() {
+    return {
+      icon_timer: require('@/views/afterClassEvaluate/images/icon_timer.png'),
+      icon_address: require('@/views/afterClassEvaluate/images/icon_address.png'),
+    }
+  },
+  methods: {
+    onItem() {
+      this.$emit('onItem', this.dataInfo)
+    }
+  },
+  filters: {
+    dayFormat(val, formatStr = 'HH:mm:ss') {
+      return dayjs(val).format(formatStr)
+    }
+  }
+}
+</script>
+
+<style lang="less" scoped>
+.item {
+  margin: .15rem .12rem 0;
+  border-radius: .1rem;
+  overflow: hidden;
+  .timer {
+    display: flex;
+    align-items: center;
+    font-size: .14rem;
+    color: #666;
+    flex: 1 auto;
+  }
+  .address {
+    font-size: .14rem;
+    color: #666;
+    line-height: 1.5;
+  }
+  .icon-address {
+    width: .12rem;
+    height: .15rem;
+    .van-icon__image {
+      width: inherit;
+      height: inherit;
+    }
+  }
+
+  .course-info {
+    display: flex;
+    align-items: center;
+    padding: 2px 0 16px;
+    .course-img {
+      width: .48rem;
+      height: .4rem;
+      margin-right: .08rem;
+      img {
+        border-radius: 50%;
+        width: .4rem;
+        height: .4rem;
+      }
+    }
+    .course-name {
+      color: #1A1A1A;
+      font-size: .16rem;
+      font-weight: 500;
+    }
+  }
+
+  .teachMode {
+    background: #F2FFFC;
+    border-radius: .03rem;
+    border: 1px solid #01C1B5;
+    font-size: 12px;
+    color: #01C1B5;
+    display: inline-block;
+    // height: 16px;
+    line-height: 16px;
+    padding: 0 5px;
+    margin-left: .05rem;
+  }
+
+  .comment {
+    /deep/.van-cell__value {
+      flex-basis: auto;
+    }
+  }
+}
+</style>

+ 2 - 2
src/views/audition/ArrangeWork.vue

@@ -101,7 +101,7 @@
       <!-- <van-sticky>
         <m-header name="学员列表" :backUrl="backUrlStudent" />
       </van-sticky> -->
-      <student-list-model :dataList="dataList" style="margin-bottom: 0.8rem;" :dataSubjectList="dataSubjectList"></student-list-model>
+      <student-list-model :dataList="dataList" style="margin-bottom: 0.8rem;" @close="studentStatus = false" :dataSubjectList="dataSubjectList"></student-list-model>
     </van-popup>
   </div>
 </template>
@@ -335,7 +335,7 @@ export default {
           setTimeout(() => {
             let query = this.$route.query
             this.$router.replace({
-              path: '/courseEvaluation',
+              path: '/afterClassEvaluateDetail',
               query: {
                 id: query.id,
                 reviewId: query.reviewId,

+ 8 - 4
src/views/audition/modal/studentList.vue

@@ -45,9 +45,9 @@
             </van-cell-group>
             <m-empty class="empty" v-else key="data" />
         </div>
-        <!-- <div class="button-group-popup">
-            <span class="btn">取消</span>
-        </div> -->
+        <div class="button-group-popup">
+            <span class="btn" @click="close">取消</span>
+        </div>
     </div>
 </template>
 
@@ -142,6 +142,10 @@ export default {
             if(tempArray.length <= 0) {
                 this.dataShow = false
             }
+        },
+        close() {
+            this.$emit('close')
+            document.querySelector('.van-popup').scrollTop = 0
         }
     }
 }
@@ -227,7 +231,7 @@ export default {
     line-height: 0.5rem;
     display: inline-block;
     border: 1px solid @mColor;
-    width: 1.65rem;
+    width: 90%;
     border-radius: 0.4rem;
     color: @mColor;
     background: #fff;

+ 22 - 8
src/views/visitManager/addVisit.vue

@@ -216,6 +216,7 @@ export default {
       name: query.name,
       userId: query.userId, // 如果有userId的时候说明是从评测详情进来的
       studentId: query.studentId, // 这个参数是从原生传过来的,单独做了处理
+      beforeId: query.beforeId, // 如果有beforeId的时候说明是从待回访列表进来的
       inside: query.inside || 0,
       visitFlag: Number(query.visitFlag) || 0,
       dataForm: {
@@ -241,12 +242,12 @@ export default {
       teacherName: null,
       form: {
         teacherId: null,
-        studentId: query.userId || query.studentId || null,
-        type: query.userId ? '团练宝' : null,
-        purpose:  query.userId ? '体验回访' : null,
+        studentId: query.userId || query.studentId || query.beforeId || null,
+        type: query.userId ? '团练宝' : query.beforeId ? '常规回访' : null,
+        purpose:  query.userId ? '体验回访' : query.beforeId ? '教学内容未达标' : null,
         overview: "",
         feedback: "",
-        visitTime: query.userId || query.id ? dayjs().format("YYYY年MM月DD日") : null,
+        visitTime: query.userId || query.id || query.beforeId ? dayjs().format("YYYY年MM月DD日") : null,
         visiterType: "TEACHER",
       },
       loading: false,
@@ -308,7 +309,7 @@ export default {
           this.$toast(res.msg);
         }
       }
-      let userId = this.userId || this.studentId
+      let userId = this.userId || this.studentId || this.beforeId;
       if(userId) {
         await queryUserById({ userId }).then(res => {
           let result = res.data
@@ -350,6 +351,10 @@ export default {
         ...form,
         visitTime
       }
+      // 待回访时要传的参数
+      if(this.beforeId) {
+        params.objectId = this.$route.query.objectId
+      }
       let res = await visitAdd({ ...params });
       let result = res.data;
       setLoading(false);
@@ -368,6 +373,15 @@ export default {
                 visitFlag
               }
             });
+          } else if(this.beforeId) {
+            let { ...query } = this.$route.query
+            this.$router.replace({
+              path: '/visitList',
+              query: {
+                userId: this.beforeId,
+                ...query
+              }
+            });
           } else {
             this.$router.replace("visitList");
           }
@@ -388,13 +402,13 @@ export default {
       }
     },
     onCheckStudent() {
-      if (this.id || this.userId || this.studentId) {
+      if (this.id || this.userId || this.studentId || this.beforeId) {
         return;
       }
       this.statusList.studentStatus = true;
     },
     onChange(type) {
-      if (this.id || this.userId) {
+      if (this.id || this.userId || this.beforeId) {
         return;
       }
       let visit = this.visit;
@@ -407,7 +421,7 @@ export default {
         } else if (form.type == "课程推荐") {
           visit.data = [{ name: "新课推荐" }, { name: "续费提醒" }];
         } else if (form.type == "常规回访") {
-          visit.data = [{ name: "课后及作业回访" }, { name: "练习及乐团表现" }];
+          visit.data = [{ name: "课后及作业回访" }, { name: "练习及乐团表现" }, { name: "教学内容未达标" },];
         } else if(form.type == '团练宝') {
           visit.data = [{ name: "体验回访" }]
         } else {

+ 372 - 0
src/views/visitManager/afterVisitList.vue

@@ -0,0 +1,372 @@
+<template>
+  <div class="visitList">
+    <!-- <m-header v-if="headerStatus" /> -->
+    <van-dropdown-menu  :close-on-click-outside="false" active-color="#01C1B5">
+      <van-dropdown-item v-model="value1" @change="onTypeChange" title="回访类型" :options="option1" />
+      <van-dropdown-item
+        v-model="value2"
+        @change="onPurposeChange"
+        :disabled="purposeStatus"
+        title="回访目的"
+        :options="option2"
+      />
+      <van-dropdown-item title="回访时间" ref="item" class="visitTime">
+        <van-cell title="开始时间" is-link @click="onChangeDate('showStart')" :value="formatStartTime"></van-cell>
+        <van-cell title="结束时间" is-link @click="onChangeDate('showEnd')" :value="formatEndTime"></van-cell>
+        <div class="btnWrap">
+          <div class="cancelBtn" @click="cancelBtn">重置</div>
+          <div class="okBtn" @click="okBtn">确定</div>
+        </div>
+      </van-dropdown-item>
+    </van-dropdown-menu>
+    <van-popup v-model="dataForm.status" position="bottom" :style="{ height: '40%' }">
+      <van-datetime-picker
+        v-model="dataForm.currentDate"
+        :min-date="dataForm.minDate"
+        :max-date="dataForm.maxDate"
+        :formatter="formatter"
+        @cancel="dataForm.status = false"
+        type="date"
+        @confirm="chioseDate"
+      />
+    </van-popup>
+    <van-list
+      v-model="loading"
+      v-if="dataShow"
+      :finished="finished"
+      finished-text="- 没有更多了 -"
+      @load="getList"
+    >
+      <van-cell
+        v-for="(item, index) in list"
+        :key="index"
+        class="cellGroup"
+        title-class="sectionTitle"
+        value-class="sectionValue"
+        is-link
+        @click="onHref(item)"
+      >
+        <template slot="title">
+          <p >{{ item.type }}</p>
+          <p class="type">{{ item.studentName }}</p>
+        </template>
+        <template solt="default">
+          <p class="content van-ellipsis">{{ item.purpose }}</p>
+          <p class="time">{{ item.visitTime }}</p>
+        </template>
+      </van-cell>
+    </van-list>
+    <m-empty v-else msg="暂无回访记录" />
+    <van-icon name="plus" class="addVisit" @click="onAdd" />
+  </div>
+</template>
+<script>
+import MHeader from "@/components/MHeader";
+import MEmpty from '@/components/MEmpty';
+import dayjs from "dayjs";
+import { browser } from "@/common/common";
+import { geteduVisitList } from "@/api/teacher";
+export default {
+  components: { MHeader, MEmpty },
+  data() {
+    return {
+      // addImg: require("@/assets/images/add_icon.png"),
+      headerStatus: true,
+      dataForm: {
+        // 时间下拉框
+        type: null,
+        status: false,
+        minDate: new Date(2000, 0, 1),
+        maxDate: new Date(2025, 10, 1),
+        currentDate: new Date(),
+      },
+      value1: "全部",
+      value2: "全部",
+      purposeStatus: true,
+      option1: [
+        { text: "全部", value: "全部" },
+        { text: "课程推荐", value: "课程推荐" },
+        { text: "常规回访", value: "常规回访" },
+        { text: "团练宝", value: "团练宝" },
+        { text: "其它", value: "其它" },
+      ],
+      option2: [],
+      startDate: null,
+      endDate: null,
+      showStart: false,
+      showEnd: false,
+      formatEndTime: null,
+      formatStartTime: null,
+      list: [],
+      loading: false,
+      finished: false,
+      params: {
+        page: 1,
+        rows: 20,
+      },
+      dataShow: true,
+    };
+  },
+  mounted() {
+    let params = this.$route.query;
+    if (params.Authorization) {
+      localStorage.setItem("Authorization", decodeURI(params.Authorization));
+      localStorage.setItem("userInfo", decodeURI(params.Authorization));
+    }
+    if (browser().android || browser().iPhone) {
+      this.headerStatus = false;
+    }
+    document.title = '回访记录'
+    // this.getList()
+  },
+  methods: {
+    onResetList() {
+      this.list = [];
+      this.params.page = 1;
+      this.dataShow = true;
+      this.loading = true;
+      this.finished = false;
+      this.getList()
+    },
+    onHref(item) {
+      this.$router.push({
+        path: '/addVisit',
+        query: {
+          id: item.id,
+          name: '回访记录详情'
+        }
+      })
+    },
+    cancelBtn() {
+      this.formatStartTime = null;
+      this.formatEndTime = null;
+      this.onResetList()
+      this.$refs.item.toggle();
+    },
+    okBtn() {
+      if(this.formatStartTime && this.formatEndTime) {
+        this.onResetList()
+      }
+      this.$refs.item.toggle();
+    },
+    onChangeDate(type) {
+      let dataForm = this.dataForm
+      if(type == 'showEnd') {
+        if(this.formatStartTime) {
+          dataForm.minDate = new Date(dayjs(this.formatStartTime))
+        }
+        setTimeout(() => {
+          dataForm.currentDate = this.formatEndTime ? new Date(dayjs(this.formatEndTime)) : new Date()
+        }, 500)
+      } else if(type == 'showStart') {
+        dataForm.minDate = new Date(2000, 0, 1)
+        setTimeout(() => {
+          dataForm.currentDate = this.formatStartTime ? new Date(dayjs(this.formatStartTime)) : new Date()
+        }, 500)
+      }
+      dataForm.status = true
+      dataForm.type = type
+    },
+    chioseDate(value) {
+      let dataForm = this.dataForm
+      if(dataForm.type == 'showStart') {
+        this.formatStartTime = dayjs(value).format('YYYY/MM/DD')
+        if(this.formatEndTime && dayjs(value).unix() > dayjs(this.formatEndTime).unix()) {
+          this.formatEndTime = null
+        }
+      } else if(dataForm.type == 'showEnd') {
+        this.formatEndTime = dayjs(value).format('YYYY/MM/DD')
+      }
+      dataForm.status = false
+    },
+    onTypeChange() {
+      if (this.value1 == "全部") {
+        this.purposeStatus = true;
+        this.value2 = "全部";
+      } else if (this.value1 == "课程推荐") {
+        this.purposeStatus = false;
+        this.option2 = [
+          { text: "全部", value: "全部" },
+          { text: "新课推荐", value: "新课推荐" },
+          { text: "续费提醒", value: "续费提醒" },
+        ];
+      } else if (this.value1 == "常规回访") {
+        this.purposeStatus = false;
+        this.option2 = [
+          { text: "全部", value: "全部" },
+          { text: "课后及作业回访", value: "课后及作业回访" },
+          { text: "练习及乐团表现", value: "练习及乐团表现" },
+          { text: "教学内容未达标", value: "教学内容未达标" },
+        ];
+      } else if (this.value1 == "团练宝") {
+        this.purposeStatus = false;
+        this.option2 = [{ text: "体验回访", value: "体验回访" }];
+      } else if (this.value1 == "其它") {
+        this.purposeStatus = false;
+        this.option2 = [{ text: "其它", value: "其它" }];
+      }
+      this.onResetList()
+    },
+    onPurposeChange() {
+      this.onResetList()
+    },
+    getList() {
+      let params = this.params;
+      params.type = this.value1 == "全部" ? null : this.value1;
+      params.purpose = this.value2 == "全部" ? null : this.value2;
+      if(this.formatStartTime && this.formatEndTime) {
+        params.startTime = dayjs(new Date(this.formatStartTime)).format("YYYY-MM-DD")
+        params.endTime = dayjs(new Date(this.formatEndTime)).format("YYYY-MM-DD")
+      } else {
+        params.startTime = null
+        params.endTime = null
+      }
+      geteduVisitList(params).then((res) => {
+        let result = res.data;
+        this.loading = false;
+        if (result.code == 200) {
+          params.page = result.data.pageNo;
+          result.data.rows.forEach((item) => {
+            item.visitTime = dayjs(item.visitTime).format("YYYY/MM/DD");
+          });
+          this.list = this.list.concat(result.data.rows);
+          if (params.page >= result.data.totalPage) {
+            this.finished = true;
+          }
+          this.params.page++;
+        } else {
+          this.finished = true;
+        }
+        // 判断是否有数据
+        if (this.list.length <= 0) {
+          this.dataShow = false;
+        }
+      });
+    },
+    onAdd() {
+      this.$router.push({
+        path: "/addVisit",
+        query: {
+          name: '新增回访记录'
+        }
+      });
+    },
+    formatter(type, val) {
+      if (type === "year") {
+        return `${val}年`;
+      } else if (type === "month") {
+        return `${val}月`;
+      } else if (type == "day") {
+        return `${val}日`;
+      }
+      return val;
+    },
+  },
+};
+</script>
+<style lang="less" scoped>
+@import url("../../assets/commonLess/variable.less");
+
+.visitList {
+  min-height: 100vh;
+  .visitTime {
+    .van-cell__right-icon{
+      line-height: .36rem;
+    }
+  }
+}
+.addClass {
+  width: 0.2rem;
+  height: 0.2rem;
+  line-height: 0.2rem;
+  position: relative;
+  top: 0.05rem;
+} 
+.cellGroup {
+  display: flex;
+  align-items: center;
+  line-height: .61rem;
+}
+.sectionTitle {
+  font-size: 0.15rem!important;
+  color: #1a1a1a;
+  align-items: center;
+  line-height: .21rem;
+  .type {
+    color: #666;
+    font-size: 0.14rem;
+  }
+}
+.sectionValue {
+  display: flex;
+  width: 60%;
+  flex: 1 auto;
+  justify-content: space-between;
+  align-items: center;
+  height: auto !important;
+  .time{
+    font-size: .14rem;
+    color: #808080;
+  }
+  .content {
+    font-size: 0.16rem;
+    color: #1a1a1a;
+    flex: 1;
+    text-align: center;
+  }
+  .van-button {
+    font-size: 0.14rem;
+    background-color: @mColor;
+    border-color: @mColor;
+    padding: 0 0.12rem;
+    min-width: 0.88rem;
+    height: 0.3rem;
+    &.van-update {
+      background-color: #fff;
+      color: @mColor;
+    }
+  }
+}
+/deep/.van-cell__title {
+  font-size: 0.14rem;
+  color: @mFontColor;
+  flex: 1 auto;
+}
+.btnWrap {
+  display: flex;
+  flex-direction: row;
+  .cancelBtn {
+    height: 48px;
+    line-height: 48px;
+    background: #eeeff3;
+    color: @mColor;
+    text-align: center;
+    width: 100%;
+  }
+  .okBtn {
+    width: 100%;
+    height: 48px;
+    line-height: 48px;
+    background: @mColor;
+    color: #fff;
+    text-align: center;
+  }
+}
+.addVisit {
+  position: fixed;
+  bottom: 1rem;
+  right: 0.2rem;
+  font-size: 30px;
+  padding: 10px;
+  border-radius: 50%;
+  background: #fff;
+  color: @mColor;
+  box-shadow: 0 2px 12px rgba(100, 101, 102, 0.12);
+}
+.van-cell{
+    color: #1A1A1A;
+    line-height: .36rem!important;
+    font-size: .16rem;
+}
+
+</style>

+ 9 - 0
src/views/visitManager/api.js

@@ -0,0 +1,9 @@
+import request from '@/helpers/request'
+
+export const queryStudyStandardWaitVisit = (data) => {
+  return request({
+    url: '/teacherCourseSchedule/queryStudyStandardWaitVisit',
+    method: 'post',
+    data
+  })
+}

+ 279 - 0
src/views/visitManager/beforeVisitList.vue

@@ -0,0 +1,279 @@
+<template>
+  <div class="beforeVisitList">
+    <van-sticky>
+      <van-cell
+        @click="showCalendar = true"
+        title-style="font-size: .14rem;"
+        value-class="calendarColor"
+      >
+        {{ calendarValue }}
+      </van-cell>
+    </van-sticky>
+
+    <div class="section">
+      <van-list
+        v-model="loading"
+        v-if="dataShow"
+        :finished="finished"
+        finished-text="- 没有更多了 -"
+        @load="getList"
+      >
+        <van-cell is-link class="visit-item" @click="onDetail(item)" v-for="(item, index) in list" :key="index">
+          <template #icon>
+            <img class="user-logo" v-if="item.avatar" :src="item.avatar" alt="" />
+            <img
+              v-else
+              class="user-logo"
+              src="../../assets/images/icon_student.png"
+              alt=""
+            />
+          </template>
+          <template #title>
+            <div class="title">
+              <p class="name">{{ item.username }}</p>
+              <p class="time">
+                {{ item.classDate }}
+                <span class="tips"><i class="icon_dot"></i>教学内容未达标</span>
+              </p>
+            </div>
+          </template>
+        </van-cell>
+      </van-list>
+      <m-empty v-else msg="暂无待回访记录" />
+    </div>
+
+    <van-calendar
+      v-model="showCalendar"
+      :minDate="minDate"
+      :default-date="defaultDate"
+      :first-day-of-week="1"
+      :formatter="formatterDay"
+      color="#01C1B5"
+      type="range"
+      get-container="body"
+      @select="selectDate"
+      @confirm="onConfirm"
+      @close="onClose"
+    />
+  </div>
+</template>
+
+<script>
+import MEmpty from "@/components/MEmpty";
+import { getNowDateAndMonday, getNowDateAndSunday } from "@/common/common";
+import dayjs from "dayjs";
+import { queryStudyStandardWaitVisit } from "./api";
+export default {
+  name: "beforeVisitList",
+  components: { MEmpty },
+  data() {
+    return {
+      showCalendar: false,
+      minDate: new Date(2000, 0, 1),
+      defaultDate: [],
+      // 类型为全部时
+      startDay: null,
+      endDay: null,
+      list: [],
+      loading: false,
+      finished: false,
+      params: {
+        page: 1,
+        rows: 20,
+      },
+      dataShow: true,
+    };
+  },
+  mounted() {
+    let day = 0;
+    let startTime = new Date(),
+      endTime = new Date();
+    startTime = getNowDateAndMonday(
+      dayjs()
+        .subtract(Math.abs(day), "day")
+        .format("YYYY-MM-DD")
+    );
+    endTime = getNowDateAndSunday(
+      dayjs()
+        .subtract(Math.abs(day), "day")
+        .format("YYYY-MM-DD")
+    );
+    this.defaultDate = [new Date(startTime), new Date(endTime)];
+    this.startDay = startTime;
+    this.endDay = endTime;
+  },
+  computed: {
+    calendarValue() {
+      return `${dayjs(this.startDay).format("YYYY年MM月DD日")} - ${dayjs(
+        this.endDay
+      ).format("YYYY年MM月DD日")}`;
+    },
+  },
+  methods: {
+    onDetail(item) {
+      this.$router.push({
+        path: '/visitDetail',
+        query: {
+          avatar: item.avatar,
+          username: item.username,
+          classDate: item.classDate,
+          reviewId: item.courseScheduleReviewId,
+          objectId: item.studentAttendanceId,
+          userId: item.userId,
+          phone: item.phone
+        }
+      })
+    },
+    formatterDay(day) {
+      const month = day.date.getMonth() + 1;
+      const date = day.date.getDate();
+      let nowDate = new Date();
+      if (month == nowDate.getMonth() + 1 && date == nowDate.getDate()) {
+        day.text = "今天";
+      }
+      return day;
+    },
+    selectDate(date) {
+      let [start, end] = date;
+      if (start) {
+        const num = dayjs(start).get("day");
+        if (num === 0) {
+          start = dayjs(start).subtract(6, "day");
+        } else {
+          start = dayjs(start).subtract(num - 1, "day");
+        }
+      }
+      if (start) {
+        const num = 7 - dayjs(start).get("day");
+        if (num < 7) {
+          end = dayjs(start).add(num, "day");
+        }
+      }
+      this.defaultDate = [new Date(start.valueOf()), new Date(end.valueOf())];
+    },
+    onConfirm(date) {
+      let [start, end] = date;
+      this.showCalendar = false;
+      if (start) {
+        const num = dayjs(start).get("day");
+        if (num === 0) {
+          start = dayjs(start).subtract(6, "day");
+        } else {
+          start = dayjs(start).subtract(num - 1, "day");
+        }
+      }
+      if (end) {
+        const num = 7 - dayjs(end).get("day");
+        if (num < 7) {
+          end = dayjs(end).add(num, "day");
+        }
+      }
+      this.startDay = dayjs(start).format("YYYY-MM-DD");
+      this.endDay = dayjs(end).format("YYYY-MM-DD");
+      //
+      this.changeDropDownItemStatus();
+    },
+    changeDropDownItemStatus() {
+      this.$refs.item.toggle(false);
+    },
+    onClose() {
+      // 关闭弹窗时初始化默认日期
+      this.defaultDate = [new Date(this.startDay), new Date(this.endDay)];
+    },
+    async getList() {
+      let params = this.params;
+      if (this.formatStartTime && this.formatEndTime) {
+        params.startTime = dayjs(new Date(this.formatStartTime)).format(
+          "YYYY-MM-DD"
+        );
+        params.endTime = dayjs(new Date(this.formatEndTime)).format(
+          "YYYY-MM-DD"
+        );
+      } else {
+        params.startTime = null;
+        params.endTime = null;
+      }
+      try {
+        const res = await queryStudyStandardWaitVisit(params);
+        const result = res.data;
+        this.loading = false;
+        if (params.page > result.pageNo) {
+          return;
+        }
+        params.page = result.pageNo;
+        this.list = this.list.concat(result.rows);
+        if (params.page >= result.totalPage) {
+          this.finished = true;
+        }
+        this.params.page++;
+        // 判断是否有数据
+        if (this.list.length <= 0) {
+          this.dataShow = false;
+        }
+      } catch {
+        this.finished = true;
+        this.dataShow = false;
+      }
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.calendarColor {
+  font-size: 0.15rem;
+  color: #1a1a1a;
+  text-align: center;
+}
+/deep/.van-calendar__confirm {
+  height: 46px;
+}
+
+.visit-item {
+  margin: 0.1rem 0.12rem;
+  border-radius: 0.1rem;
+  padding: 0.18rem 0.12rem;
+  width: auto;
+  .user-logo {
+    width: 0.42rem;
+    height: 0.42rem;
+    border-radius: 50%;
+  }
+  .title {
+    display: flex;
+    flex-direction: column;
+    justify-content: space-between;
+    padding-left: 0.1rem;
+    color: #666666;
+    font-size: 0.13rem;
+    .name {
+      font-size: 0.16rem;
+      color: #1a1a1a;
+    }
+    .time {
+      display: flex;
+      align-items: center;
+    }
+    .tips {
+      font-size: 0.12rem;
+      color: #ff5a5f;
+      background: #fff3f3;
+      border-radius: 3px;
+      margin-left: 0.05rem;
+      padding: 0 0.05rem 0 0.03rem;
+      line-height: 0.18rem;
+      display: inline-block;
+      display: flex;
+      align-items: center;
+      .icon_dot {
+        display: inline-block;
+        margin-right: 0.03rem;
+        width: 0.1rem;
+        height: 0.1rem;
+        background: url("../afterClassEvaluate/images/dot.png") no-repeat center;
+        background-size: contain;
+      }
+    }
+  }
+}
+</style>

+ 204 - 0
src/views/visitManager/visitDetail.vue

@@ -0,0 +1,204 @@
+<template>
+  <div class="visitDetail">
+    <van-cell
+      class="visit-item"
+      center
+    >
+      <template #icon>
+        <img class="user-logo" v-if="userInfo.avatar" :src="userInfo.avatar" alt="" />
+        <img
+          v-else
+          class="user-logo"
+          src="../../assets/images/icon_student.png"
+          alt=""
+        />
+      </template>
+      <template #title>
+        <div class="title">
+          <p class="name">{{ userInfo.username }}</p>
+          <p class="time">
+            {{ userInfo.classDate }}
+            <span class="tips"><i class="icon_dot"></i>教学内容未达标</span>
+          </p>
+        </div>
+      </template>
+      <template #right-icon>
+        <a :href="'tel:' + userInfo.phone"><van-icon :name="icon_phone" size="18" /></a>
+      </template>
+    </van-cell>
+
+    <Item v-if="dataInfo.courseScheduleId" :dataInfo="dataInfo" />
+
+    <van-cell-group class="section">
+      <p class="work title">
+        <van-icon :name="icon_content" size="22" style="padding-right: .06rem;" />
+        教学内容
+      </p>
+      <van-field
+        rows="1"
+        type="textarea"
+        disabled
+        v-model="memo"
+        placeholder="请输入本课程教学内容"
+      />
+    </van-cell-group>
+
+    <div class="button-group">
+      <van-button type="primary" round size="large" @click="submitReview">
+        添加回访
+      </van-button>
+    </div>
+  </div>
+</template>
+
+<script>
+import Item from "../afterClassEvaluate/modal/item.vue";
+import { getReviewInfo } from '../afterClassEvaluate/api';
+export default {
+  name: "visitDetail",
+  components: { Item },
+  data() {
+    const query = this.$route.query || {};
+    return {
+      userInfo: query || {},
+      icon_content: require('@/views/afterClassEvaluate/images/icon_content.png'),
+      icon_phone: require('@/views/afterClassEvaluate/images/icon_phone.png'),
+      memo: null,
+      dataInfo: {}
+    }
+  },
+  computed: {
+    isReview() {
+      return this.$route.query.reviewId;
+    }
+  },
+  async mounted() {
+    let params = {}
+    if(this.isReview) {
+      const res = await getReviewInfo({ id: this.userInfo.reviewId });
+      const result = res.data
+      console.log(result, 'result')
+      let info = result.teacherClassHeadInfo;
+      params = {
+        classStartTime: info.classDate + ' ' + info.startClassTime + ':00',
+        classEndTime: info.endClassTime + ':00',
+        courseType: info.courseScheduleType,
+        courseName: info.classGroupName,
+        teachMode: info.teachMode,
+        schoolName: null,
+        courseScheduleId: result.courseScheduleReview.courseScheduleId
+      }
+      this.memo = result.courseScheduleReview.memo || '该课程未设置教学内容'
+    }
+    this.dataInfo = params
+    console.log(this.dataInfo)
+  },
+  methods: {
+    submitReview() {
+      let { userId, ...query } = this.$route.query
+      this.$router.push({
+        path: "/addVisit",
+        query: {
+          beforeId: this.userInfo.userId,
+          ...query
+        }
+      });
+    }
+  }
+};
+</script>
+
+<style lang="less" scoped>
+.visitDetail {
+  min-height: 100vh;
+  background: #f3f4f8;
+  overflow: hidden;
+}
+.visit-item {
+  margin: 0.1rem 0.12rem;
+  border-radius: 0.1rem;
+  padding: 0.18rem 0.12rem;
+  width: auto;
+  .user-logo {
+    width: 0.42rem;
+    height: 0.42rem;
+    border-radius: 50%;
+  }
+  .title {
+    display: flex;
+    flex-direction: column;
+    justify-content: space-between;
+    padding-left: 0.1rem;
+    color: #666666;
+    font-size: 0.13rem;
+    .name {
+      font-size: 0.16rem;
+      color: #1a1a1a;
+    }
+    .time {
+      display: flex;
+      align-items: center;
+    }
+    .tips {
+      font-size: 0.12rem;
+      color: #ff5a5f;
+      background: #fff3f3;
+      border-radius: 3px;
+      margin-left: 0.05rem;
+      padding: 0 0.05rem 0 0.03rem;
+      line-height: 0.18rem;
+      display: inline-block;
+      display: flex;
+      align-items: center;
+      .icon_dot {
+        display: inline-block;
+        margin-right: 0.03rem;
+        width: 0.1rem;
+        height: 0.1rem;
+        background: url("../afterClassEvaluate/images/dot.png") no-repeat center;
+        background-size: contain;
+      }
+    }
+  }
+}
+
+.section {
+  margin: 0.1rem 0.12rem 0;
+  border-radius: .1rem;
+  overflow: hidden;
+  .work {
+    display: flex;
+    align-items: center;
+    font-size:16px;
+    color: #333;
+  }
+  .title {
+    padding: 12px 16px 0;
+  }
+
+  .student-icon {
+    width: 32px;
+    height: 32px;
+    border-radius: 50%;
+  }
+  .titleStyle {
+    font-size: 16px;
+    color: #666666;
+    span {
+      color: #1A1A1A;
+      padding: 0 .1rem;
+    }
+  }
+}
+
+.button-group {
+  padding: 0.2rem .16rem 0.15rem;
+  background: #f3f4f8;
+  border: 0.01rem solid #f3f4f8;
+  .van-button--primary {
+    font-size: 0.18rem;
+    height: 0.48rem;
+    line-height: 0.48rem;
+  }
+}
+</style>

+ 106 - 325
src/views/visitManager/visitList.vue

@@ -1,335 +1,132 @@
 <template>
-  <div class="visitList">
-    <m-header v-if="headerStatus" />
-    <van-dropdown-menu  :close-on-click-outside="false" active-color="#01C1B5">
-      <van-dropdown-item v-model="value1" @change="onTypeChange" title="回访类型" :options="option1" />
-      <van-dropdown-item
-        v-model="value2"
-        @change="onPurposeChange"
-        :disabled="purposeStatus"
-        title="回访目的"
-        :options="option2"
-      />
-      <van-dropdown-item title="回访时间" ref="item" class="visitTime">
-        <van-cell title="开始时间" is-link @click="onChangeDate('showStart')" :value="formatStartTime"></van-cell>
-        <van-cell title="结束时间" is-link @click="onChangeDate('showEnd')" :value="formatEndTime"></van-cell>
-        <div class="btnWrap">
-          <div class="cancelBtn" @click="cancelBtn">重置</div>
-          <div class="okBtn" @click="okBtn">确定</div>
-        </div>
-      </van-dropdown-item>
-    </van-dropdown-menu>
-    <van-popup v-model="dataForm.status" position="bottom" :style="{ height: '40%' }">
-      <van-datetime-picker
-        v-model="dataForm.currentDate"
-        :min-date="dataForm.minDate"
-        :max-date="dataForm.maxDate"
-        :formatter="formatter"
-        @cancel="dataForm.status = false"
-        type="date"
-        @confirm="chioseDate"
-      />
-    </van-popup>
-    <van-list
-      v-model="loading"
-      v-if="dataShow"
-      :finished="finished"
-      finished-text="- 没有更多了 -"
-      @load="getList"
-    >
-      <van-cell
-        v-for="(item, index) in list"
-        :key="index"
-        class="cellGroup"
-        title-class="sectionTitle"
-        value-class="sectionValue"
-        is-link
-        @click="onHref(item)"
-      >
-        <template slot="title">
-          <p >{{ item.type }}</p>
-          <p class="type">{{ item.studentName }}</p>
-        </template>
-        <template solt="default">
-          <p class="content van-ellipsis">{{ item.purpose }}</p>
-          <p class="time">{{ item.visitTime }}</p>
-        </template>
-      </van-cell>
-    </van-list>
-    <m-empty v-else msg="暂无回访记录" />
-    <van-icon name="plus" class="addVisit" @click="onAdd" />
-  </div>
+    <div class="visitList">
+        <van-sticky>
+            <m-header v-if="headerStatus" :isFixed="false" />
+            <van-tabs v-model="active" @change="tabChange" title-active-color="#000000" title-inactive-color="#808080" color="#01C1B5" class="van-hairline--bottom">
+                <van-tab title="待回访列表" name="all">
+                  <beforeVisitList />
+                </van-tab>
+                <van-tab title="回访记录" name="visited">
+                  <afterVisitList />
+                </van-tab>
+            </van-tabs>
+        </van-sticky>
+    </div>
 </template>
 <script>
 import MHeader from "@/components/MHeader";
-import MEmpty from '@/components/MEmpty';
-import dayjs from "dayjs";
 import { browser } from "@/common/common";
-import { geteduVisitList } from "@/api/teacher";
+import afterVisitList from './afterVisitList.vue';
+import beforeVisitList from './beforeVisitList.vue';
 export default {
-  components: { MHeader, MEmpty },
-  data() {
-    return {
-      // addImg: require("@/assets/images/add_icon.png"),
-      headerStatus: true,
-      dataForm: {
-        // 时间下拉框
-        type: null,
-        status: false,
-        minDate: new Date(2000, 0, 1),
-        maxDate: new Date(2025, 10, 1),
-        currentDate: new Date(),
-      },
-      value1: "全部",
-      value2: "全部",
-      purposeStatus: true,
-      option1: [
-        { text: "全部", value: "全部" },
-        { text: "课程推荐", value: "课程推荐" },
-        { text: "常规回访", value: "常规回访" },
-        { text: "团练宝", value: "团练宝" },
-        { text: "其它", value: "其它" },
-      ],
-      option2: [],
-      startDate: null,
-      endDate: null,
-      showStart: false,
-      showEnd: false,
-      formatEndTime: null,
-      formatStartTime: null,
-      list: [],
-      loading: false,
-      finished: false,
-      params: {
-        page: 1,
-        rows: 20,
-      },
-      dataShow: true,
-    };
-  },
-  mounted() {
-    let params = this.$route.query;
-    if (params.Authorization) {
-      localStorage.setItem("Authorization", decodeURI(params.Authorization));
-      localStorage.setItem("userInfo", decodeURI(params.Authorization));
-    }
-    if (browser().android || browser().iPhone) {
-      this.headerStatus = false;
-    }
-    document.title = '回访记录'
-    // this.getList()
-  },
-  methods: {
-    onResetList() {
-      this.list = [];
-      this.params.page = 1;
-      this.dataShow = true;
-      this.loading = true;
-      this.finished = false;
-      this.getList()
+    components: { MHeader, afterVisitList, beforeVisitList },
+    data() {
+        const activeType = sessionStorage.getItem('visited')
+        sessionStorage.removeItem('visited')
+        return {
+            headerStatus: true,
+            active: activeType || 'all',
+            list: [],
+            loading: false,
+            finished: false,
+            params: {
+                page: 1,
+                rows: 20,
+            },
+            dataShow: true,
+            // 类型为回访学员时
+            formatEndTime: null,
+            formatStartTime: null,
+            trainNum: 0,
+            playTime: 0,
+        };
     },
-    onHref(item) {
-      this.$router.push({
-        path: '/addVisit',
-        query: {
-          id: item.id,
-          name: '回访记录详情'
+    created() {
+		this.tenantId = sessionStorage.getItem('tenantId') || null
+	},
+    async mounted() {
+        let params = this.$route.query;
+        if (params.Authorization) {
+            localStorage.setItem("Authorization", decodeURI(params.Authorization));
+            localStorage.setItem("userInfo", decodeURI(params.Authorization));
         }
-      })
-    },
-    cancelBtn() {
-      this.formatStartTime = null;
-      this.formatEndTime = null;
-      this.onResetList()
-      this.$refs.item.toggle();
-    },
-    okBtn() {
-      if(this.formatStartTime && this.formatEndTime) {
-        this.onResetList()
-      }
-      this.$refs.item.toggle();
-    },
-    onChangeDate(type) {
-      let dataForm = this.dataForm
-      if(type == 'showEnd') {
-        if(this.formatStartTime) {
-          dataForm.minDate = new Date(dayjs(this.formatStartTime))
+        if (browser().android || browser().iPhone) {
+            this.headerStatus = false;
         }
-        setTimeout(() => {
-          dataForm.currentDate = this.formatEndTime ? new Date(dayjs(this.formatEndTime)) : new Date()
-        }, 500)
-      } else if(type == 'showStart') {
-        dataForm.minDate = new Date(2000, 0, 1)
-        setTimeout(() => {
-          dataForm.currentDate = this.formatStartTime ? new Date(dayjs(this.formatStartTime)) : new Date()
-        }, 500)
-      }
-      dataForm.status = true
-      dataForm.type = type
+        document.title = '训练统计'
     },
-    chioseDate(value) {
-      let dataForm = this.dataForm
-      if(dataForm.type == 'showStart') {
-        this.formatStartTime = dayjs(value).format('YYYY/MM/DD')
-        if(this.formatEndTime && dayjs(value).unix() > dayjs(this.formatEndTime).unix()) {
-          this.formatEndTime = null
-        }
-      } else if(dataForm.type == 'showEnd') {
-        this.formatEndTime = dayjs(value).format('YYYY/MM/DD')
+    methods: {
+      tabChange() {
+        
       }
-      dataForm.status = false
-    },
-    onTypeChange() {
-      if (this.value1 == "全部") {
-        this.purposeStatus = true;
-        this.value2 = "全部";
-      } else if (this.value1 == "课程推荐") {
-        this.purposeStatus = false;
-        this.option2 = [
-          { text: "全部", value: "全部" },
-          { text: "新课推荐", value: "新课推荐" },
-          { text: "续费提醒", value: "续费提醒" },
-        ];
-      } else if (this.value1 == "常规回访") {
-        this.purposeStatus = false;
-        this.option2 = [
-          { text: "全部", value: "全部" },
-          { text: "课后及作业回访", value: "课后及作业回访" },
-          { text: "练习及乐团表现", value: "练习及乐团表现" },
-        ];
-      } else if (this.value1 == "团练宝") {
-        this.purposeStatus = false;
-        this.option2 = [{ text: "体验回访", value: "体验回访" }];
-      } else if (this.value1 == "其它") {
-        this.purposeStatus = false;
-        this.option2 = [{ text: "其它", value: "其它" }];
-      }
-      this.onResetList()
-    },
-    onPurposeChange() {
-      this.onResetList()
     },
-    getList() {
-      let params = this.params;
-      params.type = this.value1 == "全部" ? null : this.value1;
-      params.purpose = this.value2 == "全部" ? null : this.value2;
-      if(this.formatStartTime && this.formatEndTime) {
-        params.startTime = dayjs(new Date(this.formatStartTime)).format("YYYY-MM-DD")
-        params.endTime = dayjs(new Date(this.formatEndTime)).format("YYYY-MM-DD")
-      } else {
-        params.startTime = null
-        params.endTime = null
-      }
-      geteduVisitList(params).then((res) => {
-        let result = res.data;
-        this.loading = false;
-        if (result.code == 200) {
-          params.page = result.data.pageNo;
-          result.data.rows.forEach((item) => {
-            item.visitTime = dayjs(item.visitTime).format("YYYY/MM/DD");
-          });
-          this.list = this.list.concat(result.data.rows);
-          if (params.page >= result.data.totalPage) {
-            this.finished = true;
-          }
-          this.params.page++;
-        } else {
-          this.finished = true;
-        }
-        // 判断是否有数据
-        if (this.list.length <= 0) {
-          this.dataShow = false;
-        }
-      });
-    },
-    onAdd() {
-      this.$router.push({
-        path: "/addVisit",
-        query: {
-          name: '新增回访记录' 
-        }
-      });
-    },
-    formatter(type, val) {
-      if (type === "year") {
-        return `${val}年`;
-      } else if (type === "month") {
-        return `${val}月`;
-      } else if (type == "day") {
-        return `${val}日`;
-      }
-      return val;
-    },
-  },
 };
 </script>
 <style lang="less" scoped>
 @import url("../../assets/commonLess/variable.less");
 
 .visitList {
-  min-height: 100vh;
-  .visitTime {
-    .van-cell__right-icon{
-      line-height: .36rem;
+    min-height: 100vh;
+    // .visitTime {
+    //     .van-cell__right-icon{
+    //     // line-height: .36rem;
+    //     }
+    // }
+    /deep/.van-dropdown-menu__bar {
+        box-shadow: none;
     }
-  }
 }
-.addClass {
-  width: 0.2rem;
-  height: 0.2rem;
-  line-height: 0.2rem;
-  position: relative;
-  top: 0.05rem;
-} 
-.cellGroup {
-  display: flex;
-  align-items: center;
-  line-height: .61rem;
+
+/deep/.van-tab {
+    font-size: .16rem;
 }
-.sectionTitle {
-  font-size: 0.15rem!important;
-  color: #1a1a1a;
-  align-items: center;
-  line-height: .21rem;
-  .type {
-    color: #666;
-    font-size: 0.14rem;
-  }
+/deep/.search .van-search {
+    margin: 10px 0 ;
 }
-.sectionValue {
-  display: flex;
-  width: 60%;
-  flex: 1 auto;
-  justify-content: space-between;
-  align-items: center;
-  height: auto !important;
-  .time{
-    font-size: .14rem;
-    color: #808080;
-  }
-  .content {
-    font-size: 0.16rem;
-    color: #1a1a1a;
-    flex: 1;
-    text-align: center;
-  }
-  .van-button {
-    font-size: 0.14rem;
-    background-color: @mColor;
-    border-color: @mColor;
-    padding: 0 0.12rem;
-    min-width: 0.88rem;
-    height: 0.3rem;
-    &.van-update {
-      background-color: #fff;
-      color: @mColor;
+
+.data-content {
+    margin: .1rem .15rem 0;
+    border-radius: .1rem;
+    overflow: hidden;
+    .logo {
+        width: 0.40rem;
+        height: 0.4rem;
+        // margin-right: 0.12rem;
+        border-radius: 100%;
+    }
+    &:first-child {
+        margin-top: 0;
+    }
+    .van-row-item {
+        display: flex;
+        align-items: center;
+    }
+    /deep/.van-grid-item__content {
+        padding: .03rem
+    }
+    /deep/.van-grid-item__content {
+        background-color: transparent;
+    }
+    /deep/.van-grid-item__icon-wrapper {
+        font-size: .15rem;
+        font-weight: 500;
+        color: #000;
+    }
+    /deep/.van-grid-item__text {
+        padding-top: .03rem;
+        font-size: .12rem;
+        color: #808080;
+    }
+    .teacher_info {
+        display: flex;
+        align-items: center;
+        img {
+            margin-right: .1rem;
+            width: .4rem;
+            height: .4rem;
+            border-radius: 50%;
+        }
     }
-  }
-}
-/deep/.van-cell__title {
-  font-size: 0.14rem;
-  color: @mFontColor;
-  flex: 1 auto;
 }
 .btnWrap {
   display: flex;
@@ -351,21 +148,5 @@ export default {
     text-align: center;
   }
 }
-.addVisit {
-  position: fixed;
-  bottom: 1rem;
-  right: 0.2rem;
-  font-size: 30px;
-  padding: 10px;
-  border-radius: 50%;
-  background: #fff;
-  color: @mColor;
-  box-shadow: 0 2px 12px rgba(100, 101, 102, 0.12);
-}
-.van-cell{
-    color: #1A1A1A;
-    line-height: .36rem!important;
-    font-size: .16rem;
-}
 
 </style>

+ 1 - 1
vue.config.js

@@ -1,5 +1,5 @@
 let targetUrl = 'https://mteatest.dayaedu.com'
-// let targetUrl = 'http://192.168.3.139:8000' // 箭河
+// let targetUrl = 'http://192.168.3.20:8000'
 // let targetUrl = 'https://online.dayaedu.com'
 // let targetUrl = 'http://dev.dayaedu.com/'
 // let targetUrl = 'http://192.168.3.124:8000'