lex 2 år sedan
förälder
incheckning
a040849e7e
28 ändrade filer med 1036 tillägg och 227 borttagningar
  1. 3 1
      src/constant/index.ts
  2. 8 0
      src/router/routes-common.ts
  3. 16 0
      src/router/routes-teacher.ts
  4. 3 3
      src/school/attendance-rules/component/range.tsx
  5. 9 6
      src/school/attendance-rules/component/sigin-in-rule.tsx
  6. 3 3
      src/school/attendance-rules/component/sigin-out-rule.tsx
  7. 5 0
      src/school/attendance-rules/index.module.less
  8. 32 26
      src/school/attendance/components/attend-student.tsx
  9. 7 4
      src/school/attendance/student-att-day.tsx
  10. 1 3
      src/school/practice-rewards/index.tsx
  11. 16 37
      src/school/ranking-list/components/day-bang.tsx
  12. 21 44
      src/school/ranking-list/components/timer-bang.tsx
  13. 32 0
      src/school/ranking-list/index.module.less
  14. 50 5
      src/school/ranking-list/index.tsx
  15. BIN
      src/student/download/images/manage-center.png
  16. BIN
      src/student/download/images/student-center.png
  17. BIN
      src/student/download/images/teacher-center.png
  18. 6 7
      src/student/ranking-list/components/day-bang.tsx
  19. 7 21
      src/student/ranking-list/components/timer-bang.tsx
  20. 30 29
      src/student/ranking-list/index.tsx
  21. 2 2
      src/styles/index.less
  22. 13 0
      src/teacher/attendance-manage/index.tsx
  23. 187 0
      src/views/courseware-record/index.module.less
  24. 463 0
      src/views/courseware-record/index.tsx
  25. BIN
      src/views/exercise-record/images/icon-member.png
  26. 29 33
      src/views/exercise-record/index.tsx
  27. 51 0
      src/views/exercise-record/modals/student-item.module.less
  28. 42 3
      src/views/exercise-record/modals/student-item.tsx

+ 3 - 1
src/constant/index.ts

@@ -73,8 +73,10 @@ export const attType = {
   NORMAL: '正常',
   LATE: '迟到',
   LEAVE: '请假',
-  TRUANCY: '旷课'
+  TRUANCY: '旷课',
+  UNCALLED: '未点名'
 }
+
 // 老师考勤
 export const teacherAttType = {
   NORMAL: '正常',

+ 8 - 0
src/router/routes-common.ts

@@ -180,6 +180,14 @@ export const router: RouteRecordRaw[] = [
       title: '消息详情'
     }
   },
+  {
+    path: '/courseware-record',
+    name: 'courseware-record',
+    component: () => import('@/views/courseware-record'),
+    meta: {
+      title: '课件记录'
+    }
+  },
 ]
 
 // 不需要登录的路由

+ 16 - 0
src/router/routes-teacher.ts

@@ -47,6 +47,22 @@ export default [
         }
       },
       {
+        path: '/attendance-manage',
+        name: 'attendance-manage',
+        component: () => import('@/teacher/attendance-manage/index'),
+        meta: {
+          title: '考勤管理'
+        }
+      },
+      {
+        path: '/student-att-day',
+        name: 'student-att-day',
+        component: () => import('@/school/attendance/student-att-day'),
+        meta: {
+          title: '考勤详情'
+        }
+      },
+      {
         path: '/attendance',
         name: 'attendance',
         component: () => import('@/teacher/attendance/index'),

+ 3 - 3
src/school/attendance-rules/component/range.tsx

@@ -157,7 +157,7 @@ export default defineComponent({
           <Cell>
             {{
               title: () => (
-                <div class={[styles.ruleContent, styles.ruleMore]}>
+                <div class={[styles.ruleContent, styles.ruleMore, styles.flexDev]}>
                   单次扣减金额:
                   <Field
                     class={[styles.field, styles['field-m'], styles['field-w136']]}
@@ -206,7 +206,7 @@ export default defineComponent({
           <Cell>
             {{
               title: () => (
-                <div class={[styles.ruleContent, styles.ruleMore]}>
+                <div class={[styles.ruleContent, styles.ruleMore, styles.flexDev]}>
                   单次扣减金额:
                   <Field
                     class={[styles.field, styles['field-m'], styles['field-w136']]}
@@ -222,7 +222,7 @@ export default defineComponent({
                           class={styles.popover}
                           actions={actions}
                           onSelect={(val: any) => {
-                            forms['sign_in_attendance_type'] = val.value
+                            forms['sign_out_attendance_type'] = val.value
                           }}
                         >
                           {{

+ 9 - 6
src/school/attendance-rules/component/sigin-in-rule.tsx

@@ -79,7 +79,10 @@ export default defineComponent({
         showToast('请输入正常签到时间')
         return
       }
-
+      if (forms['normal_sign_in_end'] > forms['normal_sign_in_start']) {
+        showToast('正常签到时间不能大于签到时间范围')
+        return
+      }
       if (!forms['abnormal_sign_in_fee']) {
         showToast('请输入异常签到扣减金额')
         return
@@ -226,18 +229,18 @@ export default defineComponent({
               title: () => (
                 <div class={[styles.ruleContent, styles.ruleMore]}>
                   <div class={styles.ruleDesc}>
-                    签到时间晚于课程开始时间<span>前{forms['abnormal_sign_in'] || 0}分钟</span>{' '}
+                    签到时间晚于课程开始时间<span>前{forms['normal_sign_in_end'] || 0}分钟</span>{' '}
                     <br />
                     早于课程开始时间时,扣减当日训练补助
                   </div>
                   <div class={styles.ruleTips}>
                     以<span>12:00:00</span>开始的课程为例,在
                     <span>
-                      {reduceFormMinuteAddS('12:00:00', forms['abnormal_sign_in'])}~12:00:00
+                      {reduceFormMinuteAddS('12:00:00', forms['normal_sign_in_end'])}~12:00:00
                     </span>
                     时间范围内签到属于<span>异常签到</span>
                   </div>
-                  <div style={{ paddingTop: '20px' }}>
+                  <div style={{ paddingTop: '20px' }} class={styles.flexDev}>
                     单次扣减金额:
                     <Field
                       class={[styles.field, styles['field-m'], styles['field-w136']]}
@@ -304,7 +307,7 @@ export default defineComponent({
                     </span>
                     时间范围内签到属于<span>迟到</span>
                   </div>
-                  <div style={{ paddingTop: '20px' }}>
+                  <div style={{ paddingTop: '20px' }} class={styles.flexDev}>
                     单次扣减金额:
                     <Field
                       class={[styles.field, styles['field-m'], styles['field-w136']]}
@@ -361,7 +364,7 @@ export default defineComponent({
                     后签到(含{addFormMinute('12:00:00', forms['absence_sign_in'])}) 属于
                     <span>旷课</span>
                   </div>
-                  <div style={{ paddingTop: '20px' }}>
+                  <div style={{ paddingTop: '20px' }} class={styles.flexDev}>
                     单次扣减金额:
                     <Field
                       class={[styles.field, styles['field-m'], styles['field-w136']]}

+ 3 - 3
src/school/attendance-rules/component/sigin-out-rule.tsx

@@ -191,12 +191,12 @@ export default defineComponent({
                   <div class={styles.ruleTips}>
                     以<span>12:00:00</span>结束的课程为例, 在
                     <span>
-                      {reduceFormMinute('12:00:00', forms['abnormal_sign_out'])}~
+                      {reduceFormMinute('12:00:00', forms['premise_sign_out'])}~
                       {reduceFormMinuteMS('12:00:00', 0)}
                     </span>
                     时间范围内签退属于<span>异常签退</span>
                   </div>
-                  <div style={{ paddingTop: '20px' }}>
+                  <div style={{ paddingTop: '20px' }} class={styles.flexDev}>
                     单次扣减金额:
                     <Field
                       class={[styles.field, styles['field-m'], styles['field-w136']]}
@@ -253,7 +253,7 @@ export default defineComponent({
                     前签退 (含{reduceFormMinute('12:00:00', forms['premise_sign_out'])}) 属于
                     <span>早退</span>
                   </div>
-                  <div style={{ paddingTop: '20px' }}>
+                  <div style={{ paddingTop: '20px' }} class={styles.flexDev}>
                     单次扣减金额:
                     <Field
                       class={[styles.field, styles['field-m'], styles['field-w136']]}

+ 5 - 0
src/school/attendance-rules/index.module.less

@@ -161,3 +161,8 @@
 .popover {
   --van-popover-action-width: 100px;
 }
+
+.flexDev {
+  display: flex;
+  align-items: center;
+}

+ 32 - 26
src/school/attendance/components/attend-student.tsx

@@ -22,6 +22,7 @@ import request from '@/helpers/request'
 import { state as globalState } from '@/state'
 import StudentAttItem from '../modals/studentAtt-item'
 import { formatterDatePicker } from '@/helpers/utils'
+import { courseEmnu } from '@/constant'
 export default defineComponent({
   name: 'attend-student',
 
@@ -29,6 +30,10 @@ export default defineComponent({
     toHeight: {
       type: Number,
       default: 0
+    },
+    type: {
+      type: String,
+      default: ''
     }
   },
   setup(props, { emit }) {
@@ -47,8 +52,8 @@ export default defineComponent({
       timeName: state.currentDate[0] + '年' + state.currentDate[1] + '月',
       orchestraId: '',
       orchestraName: '全部乐团',
-      subjectId: '',
-      subjectName: '全部声部',
+      courseType: '',
+      courseTypeName: '全部课程类型',
       page: 1,
       rows: 20
     })
@@ -75,7 +80,7 @@ export default defineComponent({
         }
 
         const res = await request.post(
-          '/api-school/courseScheduleStudentAttendance/studentAttendance',
+          `${globalState.platformApi}/courseScheduleStudentAttendance/studentAttendance`,
           {
             data: { ...forms }
           }
@@ -120,8 +125,8 @@ export default defineComponent({
 
     const checkSubject = (val: any) => {
       const selectedOptions = val.selectedOptions[0] || {}
-      forms.subjectId = selectedOptions.value
-      forms.subjectName = selectedOptions.name
+      forms.courseType = selectedOptions.value
+      forms.courseTypeName = selectedOptions.name
       state.showPopoverSubject = false
       refreshing.value = true
       getList()
@@ -147,25 +152,18 @@ export default defineComponent({
       }
     }
 
-    const getSubjects = async () => {
-      try {
-        const res = await request.post('/api-school/subjectBasicConfig/page', {
-          data: { page: 1, rows: 9999 }
-        })
-        state.subjects = res.data.rows.map((item) => {
-          return {
-            name: item.subjectName,
-            value: item.subjectId as string
-          }
-        })
-        state.subjects.unshift({ name: '全部声部', value: '' })
-      } catch (e: any) {
-        const message = e.message
-        showToast(message)
-      }
-    }
     onMounted(() => {
-      getSubjects()
+      state.subjects.push({
+        name: '全部课程类型',
+        value: ''
+      })
+      Object.keys(courseEmnu).forEach((key: any) => {
+        state.subjects.push({
+          name: courseEmnu[key],
+          value: key
+        })
+      })
+      // getSubjects()
       getOrchestraList()
       getList()
     })
@@ -210,7 +208,7 @@ export default defineComponent({
               state.showPopoverSubject = true
             }}
           >
-            {forms.subjectName}
+            {forms.courseTypeName}
             <i class={'arrow'}></i>
           </div>
         </div>
@@ -219,14 +217,22 @@ export default defineComponent({
         <div
           style={{
             overflowY: 'auto',
-            height: 'calc(100vh - var(--van-tabs-line-height) - var(--header-height) - 1.17333rem)'
+            height:
+              props.type === 'teacher'
+                ? 'calc(100vh  - 1.17333rem)'
+                : 'calc(100vh - var(--van-tabs-line-height) - var(--header-height) - 1.17333rem)'
           }}
         >
           {showContact.value ? (
             <OFullRefresh
               v-model:modelValue={refreshing.value}
               onRefresh={onRefresh}
-              style="min-height: calc(100vh - var(--van-tabs-line-height) - var(--header-height) - 1.17333rem);"
+              style={{
+                minHeight:
+                  props.type === 'teacher'
+                    ? 'calc(100vh - 1.17333rem)'
+                    : 'calc(100vh - var(--van-tabs-line-height) - var(--header-height) - 1.17333rem)'
+              }}
             >
               <List
                 loading-text=" "

+ 7 - 4
src/school/attendance/student-att-day.tsx

@@ -13,6 +13,7 @@ import { state as globalState } from '@/state'
 import StudentCell from './modals/student-cell'
 import OSticky from '@/components/o-sticky'
 import { formatterDatePicker } from '@/helpers/utils'
+import { state as baseState } from '@/state'
 export default defineComponent({
   name: 'student-att-day',
   setup() {
@@ -52,10 +53,12 @@ export default defineComponent({
           list.value = []
           refreshing.value = false
         }
-
-        const res = await request.post('/api-school/courseScheduleStudentAttendance/page', {
-          data: { ...forms }
-        })
+        const res = await request.post(
+          `${baseState.platformApi}/courseScheduleStudentAttendance/page`,
+          {
+            data: { ...forms }
+          }
+        )
 
         if (list.value.length > 0 && res.data.pages === 1) {
           return

+ 1 - 3
src/school/practice-rewards/index.tsx

@@ -12,9 +12,7 @@ import { postMessage } from '@/helpers/native-message'
 export default defineComponent({
   name: 'practice-rewards',
   setup() {
-    const seeType = localStorage.getItem('practice-rewards-see-type')
     const state = reactive({
-      seeType: seeType || 'hide',
       list: [] as any,
       listState: {
         dataShow: true, // 判断是否有数据
@@ -92,7 +90,7 @@ export default defineComponent({
               <div class={styles.priceTitle}>待结算金额 (元)</div>
               <div class={styles.priceCount}>
                 <span>¥</span>
-                {state.seeType === 'see' ? moneyFormat(state.statistics.waitSalary) : '****'}
+                {moneyFormat(state.statistics.waitSalary)}
               </div>
 
               <span

+ 16 - 37
src/school/ranking-list/components/day-bang.tsx

@@ -23,8 +23,7 @@ import { state as globalState } from '@/state'
 import RankItem from '../modals/rank-item'
 import { formatterDatePicker } from '@/helpers/utils'
 export default defineComponent({
-  props: ['toHeight'],
-  emits: ['setTime'],
+  props: ['toHeight', 'startTime', 'endTime'],
   name: 'day-bang',
   setup(props, { slots, attrs, emit }) {
     const router = useRouter()
@@ -37,8 +36,7 @@ export default defineComponent({
       currentDate: [dayjs().format('YYYY'), dayjs().format('MM')]
     })
     const forms = reactive({
-      practiceMonth: state.currentDate[0] + '' + state.currentDate[1],
-      timeName: state.currentDate[0] + '年' + state.currentDate[1] + '月',
+      practiceMonth: props.startTime,
       orchestraId: '',
       orchestraName: '全部乐团',
       subjectId: '',
@@ -63,6 +61,14 @@ export default defineComponent({
         toTop.value = val
       }
     )
+    watch(
+      () => props.startTime,
+      () => {
+        forms.practiceMonth = props.startTime
+        refreshing.value = true
+        getList()
+      }
+    )
     const getList = async () => {
       loading.value = true
       try {
@@ -96,15 +102,6 @@ export default defineComponent({
         finished.value = true
       }
     }
-
-    const checkTimer = (val: any) => {
-      forms.practiceMonth = val.selectedValues[0] + val.selectedValues[1]
-      forms.timeName = val.selectedValues[0] + '年' + val.selectedValues[1] + '月'
-      state.showPopoverTime = false
-      emit('setTime', forms.timeName)
-      refreshing.value = true
-      getList()
-    }
     const checkOrchestra = (val: any) => {
       const selectedOptions = val.selectedOptions[0] || {}
       forms.orchestraId = selectedOptions.value
@@ -167,7 +164,6 @@ export default defineComponent({
       getSubjects()
       getOrchestraList()
       getList()
-      emit('setTime', forms.timeName)
     })
     const onRefresh = () => {
       finished.value = false
@@ -184,7 +180,7 @@ export default defineComponent({
         {/* <OSticky position="top" background="#FFF"> */}
         <Sticky offsetTop={toTop.value} style={{ width: '100%' }}>
           <div class={'searchGroup'}>
-            <div
+            {/* <div
               class={['searchItem searchItem-normal', state.showPopoverTime && 'searchItem-active']}
               onClick={() => {
                 state.showPopoverTime = true
@@ -192,7 +188,7 @@ export default defineComponent({
             >
               <span>{forms.timeName}</span>
               <i class="arrow"></i>
-            </div>
+            </div> */}
             <div
               class={[
                 'searchItem searchItem-normal',
@@ -225,12 +221,14 @@ export default defineComponent({
           <OFullRefresh
             v-model:modelValue={refreshing.value}
             onRefresh={onRefresh}
-            style="min-height: 100vh;"
+            style={{
+              minHeight: `calc(100vh - ${toTop.value}px - 1.17333rem)`
+            }}
           >
             <List
               // v-model:loading={loading.value}
               finished={finished.value}
-              finished-text="没有更多了"
+              finished-text=" "
               onLoad={getList}
               loading-text=" "
             >
@@ -244,25 +242,6 @@ export default defineComponent({
         )}
 
         <Popup
-          v-model:show={state.showPopoverTime}
-          position="bottom"
-          round
-          class={'popupBottomSearch'}
-        >
-          <DatePicker
-            onCancel={() => {
-              state.showPopoverTime = false
-            }}
-            onConfirm={checkTimer}
-            v-model={state.currentDate}
-            formatter={formatterDatePicker}
-            minDate={minDate.value}
-            maxDate={maxDate.value}
-            columnsType={columnsType.value}
-          />
-        </Popup>
-
-        <Popup
           v-model:show={state.showPopoverOrchestra}
           position="bottom"
           round

+ 21 - 44
src/school/ranking-list/components/timer-bang.tsx

@@ -19,15 +19,11 @@ import { defineComponent, reactive, ref, onMounted, watch } from 'vue'
 import { useRouter } from 'vue-router'
 import styles from './timer-bang.module.less'
 import request from '@/helpers/request'
-import { state as globalState } from '@/state'
 import RankItem from '../modals/rank-item'
-import { formatterDatePicker } from '@/helpers/utils'
 export default defineComponent({
   name: 'timer-bang',
-  props: ['toHeight'],
-  emits: ['setTime'],
-  setup(props, { slots, attrs, emit }) {
-    const router = useRouter()
+  props: ['toHeight', 'startTime', 'endTime'],
+  setup(props) {
     const state = reactive({
       showPopoverTime: false,
       showPopoverOrchestra: false,
@@ -37,8 +33,7 @@ export default defineComponent({
       currentDate: [dayjs().format('YYYY'), dayjs().format('MM')]
     })
     const forms = reactive({
-      practiceMonth: state.currentDate[0] + '' + state.currentDate[1],
-      timeName: state.currentDate[0] + '年' + state.currentDate[1] + '月',
+      practiceMonth: props.startTime,
       orchestraId: '',
       orchestraName: '全部乐团',
       subjectId: '',
@@ -47,9 +42,6 @@ export default defineComponent({
       rows: 50,
       sortType: 'PRACTICE_TIMES'
     })
-    const minDate = ref(new Date(dayjs().subtract(10, 'year').format('YYYY-MM-DD')))
-    const maxDate = ref(new Date(dayjs().add(10, 'year').format('YYYY-MM-DD')))
-    const columnsType = ref<DatePickerColumnType[]>(['year', 'month'])
     const refreshing = ref(false)
     const loading = ref(false)
     const finished = ref(false)
@@ -63,6 +55,14 @@ export default defineComponent({
         console.log(toTop.value)
       }
     )
+    watch(
+      () => props.startTime,
+      () => {
+        forms.practiceMonth = props.startTime
+        refreshing.value = true
+        getList()
+      }
+    )
     const getList = async () => {
       loading.value = true
       try {
@@ -97,14 +97,6 @@ export default defineComponent({
       }
     }
 
-    const checkTimer = (val: any) => {
-      forms.practiceMonth = val.selectedValues[0] + val.selectedValues[1]
-      forms.timeName = val.selectedValues[0] + '年' + val.selectedValues[1] + '月'
-      emit('setTime', forms.timeName)
-      state.showPopoverTime = false
-      refreshing.value = true
-      getList()
-    }
     const checkOrchestra = (val: any) => {
       const selectedOptions = val.selectedOptions[0] || {}
       forms.orchestraId = selectedOptions.value
@@ -166,7 +158,6 @@ export default defineComponent({
       getSubjects()
       getOrchestraList()
       getList()
-      emit('setTime', forms.timeName)
     })
     const onRefresh = () => {
       finished.value = false
@@ -183,7 +174,7 @@ export default defineComponent({
         {/* <OSticky position="top" background="#FFF"> */}
         <Sticky offsetTop={toTop.value} style={{ width: '100%' }}>
           <div class={'searchGroup'}>
-            <div
+            {/* <div
               class={['searchItem searchItem-normal', state.showPopoverTime && 'searchItem-active']}
               onClick={() => {
                 state.showPopoverTime = true
@@ -191,7 +182,7 @@ export default defineComponent({
             >
               <span>{forms.timeName}</span>
               <i class="arrow"></i>
-            </div>
+            </div> */}
             <div
               class={[
                 'searchItem searchItem-normal',
@@ -221,11 +212,17 @@ export default defineComponent({
         {/* </OSticky> */}
 
         {showContact.value ? (
-          <OFullRefresh v-model:modelValue={refreshing.value} onRefresh={onRefresh}>
+          <OFullRefresh
+            v-model:modelValue={refreshing.value}
+            onRefresh={onRefresh}
+            style={{
+              minHeight: `calc(100vh - ${toTop.value}px - 1.17333rem)`
+            }}
+          >
             <List
               // v-model:loading={loading.value}
               finished={finished.value}
-              finished-text="没有更多了"
+              finished-text=" "
               onLoad={getList}
               loading-text=" "
             >
@@ -237,26 +234,6 @@ export default defineComponent({
         ) : (
           <OEmpty tips="暂无排行" />
         )}
-
-        <Popup
-          v-model:show={state.showPopoverTime}
-          position="bottom"
-          round
-          class={'popupBottomSearch'}
-        >
-          <DatePicker
-            onCancel={() => {
-              state.showPopoverTime = false
-            }}
-            onConfirm={checkTimer}
-            v-model={state.currentDate}
-            formatter={formatterDatePicker}
-            minDate={minDate.value}
-            maxDate={maxDate.value}
-            columnsType={columnsType.value}
-          />
-        </Popup>
-
         <Popup
           v-model:show={state.showPopoverOrchestra}
           position="bottom"

+ 32 - 0
src/school/ranking-list/index.module.less

@@ -12,7 +12,39 @@
     color: #ffffff;
     line-height: 20px;
     letter-spacing: 1px;
+    display: flex;
+    align-items: center;
+    // i {
+    //   display: inline-block;
+    //   background: url('@/common/svg/arrow.png') no-repeat center center;
+    //   background-size: contain;
+    //   width: 9px;
+    //   height: 5px;
+
+    //   margin-left: 3px;
+    // }
+    .imgIcon {
+      font-size: 10px;
+      margin-left: 5px;
+    }
   }
+
+  // &::after {
+  //       content: ' ';
+  //       display: inline-block;
+  //       background: url('@/common/svg/arrow.png') no-repeat center center;
+  //       background-size: contain;
+  //       width: 9px;
+  //       height: 5px;
+  //       margin-left: 3px;
+  //     }
+
+  //     &.active {
+  //       &::after {
+  //         transform: rotateX(180deg);
+  //         margin-top: -2px;
+  //       }
+  //     }
 }
 
 .rankTabs {

+ 50 - 5
src/school/ranking-list/index.tsx

@@ -1,19 +1,27 @@
 import OHeader from '@/components/o-header'
 import OSticky from '@/components/o-sticky'
-import { Tabs, Tab } from 'vant'
+import { Tabs, Tab, Calendar, Icon } from 'vant'
 import { defineComponent, reactive, ref } from 'vue'
 import { useRouter } from 'vue-router'
 // import linkBg from './images/ranking-bg.png'
 import TimerBang from './components/timer-bang'
 import DayBang from './components/day-bang'
 import styles from './index.module.less'
+import dayjs from 'dayjs'
+import isBetween from 'dayjs/plugin/isBetween'
+dayjs.extend(isBetween)
 const activeName = ref('day')
 const timers = ref('')
 export default defineComponent({
   name: 'ranking-list',
   setup() {
     const router = useRouter()
-    const state = reactive({ heightV: 0 })
+    const state = reactive({
+      heightV: 0,
+      showPopoverTime: false,
+      startTime: dayjs().day(1).format('YYYY-MM-DD'),
+      endTime: dayjs().day(7).format('YYYY-MM-DD')
+    })
 
     const setTime = (val: string) => {
       timers.value = val
@@ -32,7 +40,14 @@ export default defineComponent({
               background={'transparent'}
               border={false}
             ></OHeader>
-            <span class={styles.topTime}>{timers.value}</span>
+            <span class={styles.topTime} onClick={() => (state.showPopoverTime = true)}>
+              {state.startTime}~{state.endTime}
+              {/* <i></i> */}
+              <Icon
+                name={state.showPopoverTime ? 'arrow-up' : 'arrow-down'}
+                class={styles.imgIcon}
+              />
+            </span>
           </div>
           <Tabs
             v-model:active={activeName.value}
@@ -47,10 +62,40 @@ export default defineComponent({
           </Tabs>
         </OSticky>
         {activeName.value == 'timer' ? (
-          <TimerBang onSetTime={(val: string) => setTime(val)} toHeight={state.heightV}></TimerBang>
+          <TimerBang
+            startTime={state.startTime}
+            endTime={state.endTime}
+            toHeight={state.heightV}
+          ></TimerBang>
         ) : (
-          <DayBang onSetTime={(val: string) => setTime(val)} toHeight={state.heightV}></DayBang>
+          <DayBang
+            startTime={state.startTime}
+            endTime={state.endTime}
+            toHeight={state.heightV}
+          ></DayBang>
         )}
+
+        <Calendar
+          v-model:show={state.showPopoverTime}
+          firstDayOfWeek={1}
+          showConfirm={false}
+          type="range"
+          maxRange={7}
+          minDate={new Date('2023-02-27')}
+          defaultDate={[dayjs(state.startTime).toDate(), dayjs(state.endTime).toDate()]}
+          style={{
+            height: '70%'
+          }}
+          onSelect={(item: any) => {
+            state.startTime = ''
+            state.endTime = ''
+            if (!dayjs(item[0]).isBetween(dayjs(state.startTime), dayjs(state.endTime))) {
+              state.startTime = dayjs(item[0]).day(1).format('YYYY-MM-DD')
+              state.endTime = dayjs(item[0]).day(7).format('YYYY-MM-DD')
+            }
+            state.showPopoverTime = false
+          }}
+        />
       </>
     )
   }

BIN
src/student/download/images/manage-center.png


BIN
src/student/download/images/student-center.png


BIN
src/student/download/images/teacher-center.png


+ 6 - 7
src/student/ranking-list/components/day-bang.tsx

@@ -35,20 +35,17 @@ export default defineComponent({
       subjects: [] as any,
       currentDate: [dayjs().format('YYYY'), dayjs().format('MM')]
     })
-    const parentData = inject('parentData', { practiceMonth: '', timeName: '' } as any)
+    const parentData = inject('parentData', { practiceMonth: '' } as any)
     const forms = reactive({
       practiceMonth: parentData.practiceMonth,
       page: 1,
       rows: 50,
       sortType: 'PRACTICE_DAY'
     })
-    const minDate = ref(new Date(dayjs().subtract(10, 'year').format('YYYY-MM-DD')))
-    const maxDate = ref(new Date(dayjs().add(10, 'year').format('YYYY-MM-DD')))
-    const columnsType = ref<DatePickerColumnType[]>(['year', 'month'])
     const refreshing = ref(false)
     const loading = ref(false)
     const finished = ref(false)
-    const showContact = ref(false)
+    const showContact = ref(true)
     const list = ref([])
     const toTop = ref(props.toHeight)
     const myInfo = ref({} as any)
@@ -122,13 +119,15 @@ export default defineComponent({
             <OFullRefresh
               v-model:modelValue={refreshing.value}
               onRefresh={onRefresh}
-              style="min-height: 100vh;"
+              style={{
+                minHeight: `calc(100vh - ${toTop.value}px - 1.17333rem)`
+              }}
             >
               <List
                 loading-text=" "
                 // v-model:loading={loading.value}
                 finished={finished.value}
-                finished-text="没有更多了"
+                finished-text=" "
                 onLoad={getList}
               >
                 {list.value.map((item: any, index: number) => (

+ 7 - 21
src/student/ranking-list/components/timer-bang.tsx

@@ -1,24 +1,10 @@
-import OSearch from '@/components/o-search'
 import OEmpty from '@/components/o-empty'
 import dayjs from 'dayjs'
-import {
-  Icon,
-  Popover,
-  DatePicker,
-  DatePickerColumnType,
-  Popup,
-  List,
-  PullRefresh,
-  ActionSheet,
-  showToast,
-  Sticky
-} from 'vant'
+import { List, showToast } from 'vant'
 import OFullRefresh from '@/components/o-full-refresh'
 import { defineComponent, reactive, ref, onMounted, watch, inject } from 'vue'
 import { useRouter } from 'vue-router'
-import styles from './timer-bang.module.less'
 import request from '@/helpers/request'
-import { state as globalState } from '@/state'
 import RankItem from '../modals/rank-item'
 import MyRankingItem from '../modals/my-ranking-item'
 export default defineComponent({
@@ -35,7 +21,7 @@ export default defineComponent({
       subjects: [] as any,
       currentDate: [dayjs().format('YYYY'), dayjs().format('MM')]
     })
-    const parentData = inject('parentData', { practiceMonth: '', timeName: '' } as any)
+    const parentData = inject('parentData', { practiceMonth: '' } as any)
     const forms = reactive({
       practiceMonth: parentData.practiceMonth,
       page: 1,
@@ -45,7 +31,7 @@ export default defineComponent({
     const refreshing = ref(false)
     const loading = ref(false)
     const finished = ref(false)
-    const showContact = ref(false)
+    const showContact = ref(true)
     const list = ref([])
     const toTop = ref(props.toHeight)
     const myInfo = ref({} as any)
@@ -114,19 +100,19 @@ export default defineComponent({
         class={!showContact.value && 'emptyRootContainer'}
         style={{ minHeight: `calc(100vh - ${toTop.value}px)` }}
       >
-        {/* <OSticky position="top" background="#FFF"> */}
-
         {showContact.value ? (
           <div>
             <OFullRefresh
               v-model:modelValue={refreshing.value}
               onRefresh={onRefresh}
-              style="min-height: 100vh;"
+              style={{
+                minHeight: `calc(100vh - ${toTop.value}px - 1.17333rem)`
+              }}
             >
               <List
                 // v-model:loading={loading.value}
                 finished={finished.value}
-                finished-text="没有更多了"
+                finished-text=" "
                 onLoad={getList}
                 loading-text=" "
               >

+ 30 - 29
src/student/ranking-list/index.tsx

@@ -1,6 +1,6 @@
 import OHeader from '@/components/o-header'
 import OSticky from '@/components/o-sticky'
-import { Tabs, Tab, Icon, Popup, DatePicker, DatePickerColumnType } from 'vant'
+import { Tabs, Tab, Icon, Popup, DatePicker, DatePickerColumnType, Calendar } from 'vant'
 import { defineComponent, reactive, ref, provide } from 'vue'
 import { useRouter } from 'vue-router'
 // import linkBg from './images/ranking-bg.png'
@@ -8,34 +8,28 @@ import TimerBang from './components/timer-bang'
 import DayBang from './components/day-bang'
 import styles from './index.module.less'
 import dayjs from 'dayjs'
-import { formatterDatePicker } from '@/helpers/utils'
+import isBetween from 'dayjs/plugin/isBetween'
+dayjs.extend(isBetween)
 const activeName = ref('student')
-const timers = ref('')
 export default defineComponent({
   name: 'ranking-list',
   setup() {
     const router = useRouter()
     const state = reactive({
       heightV: 0,
-      showPopoverTime: false,
-      currentDate: [dayjs().format('YYYY'), dayjs().format('MM')]
+      showPopoverTime: false
     })
 
     const forms = reactive({
-      practiceMonth: state.currentDate[0] + '' + state.currentDate[1],
-      timeName: state.currentDate[0] + '年' + state.currentDate[1] + '月'
+      practiceMonth: dayjs().day(1).format('YYYY-MM-DD'),
+      endTime: dayjs().day(7).format('YYYY-MM-DD')
     })
     provide('parentData', forms)
     const getHeight = (dataHeight: number) => {
       state.heightV = dataHeight
       console.log(state.heightV, '获取高度')
     }
-    const columnsType = ref<DatePickerColumnType[]>(['year', 'month'])
-    const checkTimer = (val: any) => {
-      forms.practiceMonth = val.selectedValues[0] + val.selectedValues[1]
-      forms.timeName = val.selectedValues[0] + '年' + val.selectedValues[1] + '月'
-      state.showPopoverTime = false
-    }
+
     return () => (
       <>
         <OSticky position="top" background="#F8F8F8" onGetHeight={getHeight}>
@@ -47,7 +41,8 @@ export default defineComponent({
               border={false}
             ></OHeader>
             <span class={styles.topTime} onClick={() => (state.showPopoverTime = true)}>
-              {forms.timeName} <Icon name={state.showPopoverTime ? 'arrow-up' : 'arrow-down'} />
+              {forms.practiceMonth}~{forms.endTime}{' '}
+              <Icon name={state.showPopoverTime ? 'arrow-up' : 'arrow-down'} />
             </span>
           </div>
           <Tabs
@@ -67,22 +62,28 @@ export default defineComponent({
         ) : (
           <DayBang toHeight={state.heightV}></DayBang>
         )}
-        <Popup
+
+        <Calendar
           v-model:show={state.showPopoverTime}
-          position="bottom"
-          round
-          class={'popupBottomSearch'}
-        >
-          <DatePicker
-            onCancel={() => {
-              state.showPopoverTime = false
-            }}
-            formatter={formatterDatePicker}
-            onConfirm={checkTimer}
-            v-model={state.currentDate}
-            columnsType={columnsType.value}
-          />
-        </Popup>
+          firstDayOfWeek={1}
+          showConfirm={false}
+          type="range"
+          maxRange={7}
+          minDate={new Date('2023-02-27')}
+          defaultDate={[dayjs(forms.practiceMonth).toDate(), dayjs(forms.endTime).toDate()]}
+          style={{
+            height: '70%'
+          }}
+          onSelect={(item: any) => {
+            forms.practiceMonth = ''
+            forms.endTime = ''
+            if (!dayjs(item[0]).isBetween(dayjs(forms.practiceMonth), dayjs(forms.endTime))) {
+              forms.practiceMonth = dayjs(item[0]).day(1).format('YYYY-MM-DD')
+              forms.endTime = dayjs(item[0]).day(7).format('YYYY-MM-DD')
+            }
+            state.showPopoverTime = false
+          }}
+        />
       </>
     )
   }

+ 2 - 2
src/styles/index.less

@@ -95,6 +95,7 @@
   --tag-border-color: #2dc7aa;
   --tag-bg-color: #e9fff8;
   --tag-color: #2dc7aa;
+  // --van-overlay-background: rgba(0, 0, 0, 0.5) !important;
 }
 
 * {
@@ -109,7 +110,7 @@
   -moz-osx-font-smoothing: grayscale;
   color: #333;
   min-height: 100vh;
-// font-family: PingFangSC-Medium, PingFang SC;
+  // font-family: PingFangSC-Medium, PingFang SC;
 }
 
 body {
@@ -161,7 +162,6 @@ body {
   }
 }
 
-
 .sticky {
   position: relative;
   .van-sticky {

+ 13 - 0
src/teacher/attendance-manage/index.tsx

@@ -0,0 +1,13 @@
+import AttendStudent from '@/school/attendance/components/attend-student'
+import { defineComponent } from 'vue'
+
+export default defineComponent({
+  name: 'attendance-manage',
+  setup() {
+    return () => (
+      <>
+        <AttendStudent type="teacher" />
+      </>
+    )
+  }
+})

+ 187 - 0
src/views/courseware-record/index.module.less

@@ -0,0 +1,187 @@
+.coursewareGroup {
+  margin: 0 13px 13px;
+  border-radius: 10px;
+  overflow: hidden;
+
+  .top1 {
+    padding: 12px;
+    font-size: 12px;
+    color: #777777 !important;
+  }
+  .top2 {
+    font-size: 12px;
+    color: #777777 !important;
+    padding: 14px 12px 20px;
+  }
+  // .courseTime {
+  //   color: #777777;
+  // }
+  // .orchestraName {
+  //   color: #777777;
+  // }
+
+  .userLogo {
+    width: 47px;
+    height: 47px;
+    border-radius: 50%;
+    overflow: hidden;
+    margin-right: 6px;
+  }
+  .courseName,
+  .userName {
+    max-width: 110px;
+  }
+  .userName {
+    font-size: 16px;
+    font-weight: 600;
+    color: #333333;
+    line-height: 22px;
+  }
+  .courseRange {
+    font-size: 16px;
+    font-family: DINA;
+    font-weight: bold;
+    color: #333333;
+    line-height: 19px;
+    span {
+      color: #f44541;
+    }
+  }
+}
+
+.exerciseRecord {
+  :global {
+    .van-haptics-feedback:active {
+      opacity: 1;
+    }
+  }
+}
+
+.searchMore {
+  line-height: 1.2 !important;
+  :global {
+    .van-dropdown-item {
+      z-index: 99;
+    }
+    .van-dropdown-menu__bar {
+      box-shadow: none;
+      height: var(--van-nav-bar-height);
+    }
+    .van-dropdown-item__content {
+      border-radius: 0 0 12px 12px;
+      // padding-bottom: calc(var(--van-button-large-height) + 52px);
+      max-height: 100%;
+    }
+    .van-button + .van-button {
+      margin-left: 15px;
+    }
+  }
+  .searchContainer {
+    max-height: 400px;
+    overflow-y: auto;
+    box-sizing: border-box;
+    padding-bottom: 16px;
+    background: var(--van-popup-background);
+    transition: var(--van-popup-transition);
+  }
+
+  .searchMoreGroup {
+    display: flex;
+    align-items: center;
+    padding: 18px 13px;
+    background-color: #fff;
+  }
+
+  .searchTitle {
+    padding: 15px 13px 2px;
+    font-size: 15px;
+    font-weight: 600;
+    color: #333333;
+    line-height: 21px;
+    text-align: left;
+  }
+
+  .searchTypeGroup {
+    display: flex;
+    align-items: center;
+    padding: 0 13px;
+    flex-wrap: wrap;
+    justify-content: space-between;
+  }
+  .searchTypeFlex {
+    .searchTypeItem {
+      width: 49%;
+    }
+  }
+  .searchTypeFlex1 {
+    justify-content: flex-start;
+    .searchTypeItem {
+      width: 31%;
+
+      &:nth-child(3n + 1) {
+        margin-right: 2.333%;
+      }
+      &:nth-child(3n + 3) {
+        margin-left: 2.333%;
+      }
+    }
+  }
+  .searchTypeFlex2 {
+    .searchTypeItem {
+      width: 45%;
+    }
+  }
+  .searchTypeItemLine {
+    margin-top: 10px;
+    width: 16px;
+    height: 1px;
+    background: #d8d8d8;
+  }
+  .searchTypeItem {
+    box-sizing: border-box;
+    margin-top: 10px;
+    height: 32px;
+    line-height: 32px;
+    background: #f6f6f6;
+    border: 1px solid #f6f6f6;
+    border-radius: 16px;
+    font-size: 13px;
+    color: #333333;
+    padding: 0 7px;
+    text-align: center;
+
+    &.is-active {
+      background: #fff5f2;
+      border: 1px solid #ffb097;
+      color: #f67146;
+    }
+  }
+}
+
+.searchPreview {
+  margin: 0 13px;
+  padding-bottom: 12px;
+  padding-top: 12px;
+  white-space: nowrap;
+  overflow-y: auto;
+  .searchPreviewItem {
+    display: inline-flex;
+    align-items: center;
+    line-height: 28px;
+    background: #f6f6f6;
+    border-radius: 16px;
+    margin-right: 8px;
+    padding: 1px 10px 0;
+    font-size: 13px;
+    color: #333333;
+    &:last-child {
+      margin-right: 0;
+    }
+
+    .cross {
+      font-size: 8px;
+      margin-left: 6px;
+      color: #aaaaaa;
+    }
+  }
+}

+ 463 - 0
src/views/courseware-record/index.tsx

@@ -0,0 +1,463 @@
+import OHeader from '@/components/o-header'
+import OSticky from '@/components/o-sticky'
+import OEmpty from '@/components/o-empty'
+import dayjs from 'dayjs'
+import isBetween from 'dayjs/plugin/isBetween'
+dayjs.extend(isBetween)
+import {
+  Cell,
+  Icon,
+  List,
+  showToast,
+  Image,
+  DropdownMenu,
+  DropdownItem,
+  Button,
+  Calendar,
+  CellGroup
+} from 'vant'
+import OFullRefresh from '@/components/o-full-refresh'
+import { defineComponent, reactive, ref, onMounted } from 'vue'
+import { state as globalState } from '@/state'
+import { useRouter } from 'vue-router'
+import styles from './index.module.less'
+import request from '@/helpers/request'
+import iconTeacher from '@common/images/icon_teacher.png'
+import { courseEmnu } from '@/constant'
+export default defineComponent({
+  name: 'exercise-record',
+  setup() {
+    const platformApi = ref(globalState.platformApi)
+    const router = useRouter()
+    const state = reactive({
+      showSearchStatus: true,
+      showPopoverTime: false,
+      actions: [] as any,
+      subjects: [] as any
+    })
+    const forms = reactive({
+      startTime: dayjs().day(1).format('YYYY-MM-DD'),
+      endTime: dayjs().day(7).format('YYYY-MM-DD'),
+      orchestraId: '',
+      orchestraName: '',
+      courseType: '',
+      courseTypeName: '',
+      coursewareErr: null,
+      coursewareErrName: '',
+      keyword: '',
+      page: 1,
+      rows: 20
+    })
+    const refreshing = ref(false)
+    const loading = ref(false)
+    const finished = ref(false)
+    const showContact = ref(true)
+    const list = ref([])
+    const getList = async () => {
+      loading.value = true
+      try {
+        if (refreshing.value) {
+          forms.page = 1
+          list.value = []
+          refreshing.value = false
+        }
+        const { endTime, startTime, ...re } = forms
+        const res = await request.post(`${platformApi.value}/courseSchedule/coursewareErrPage`, {
+          data: { ...re, endTime: endTime + ' 23:59:59', startTime: startTime + ' 00:00:00' }
+        })
+
+        if (list.value.length > 0 && res.data.pages === 1) {
+          return
+        }
+
+        forms.page = res.data.current + 1
+        list.value = list.value.concat(res.data.rows || [])
+        showContact.value = list.value.length > 0
+        // console.log(showContact.value, ' showContact.value ')
+        loading.value = false
+
+        finished.value = res.data.current >= res.data.pages
+      } catch (e: any) {
+        // console.log(e, 'e')
+        const message = e.message
+        showToast(message)
+        showContact.value = false
+        finished.value = true
+      }
+    }
+    onMounted(() => {
+      getList()
+      getOrchestraList()
+      getSubjects()
+    })
+
+    const onBack = () => {
+      router.go(-1)
+    }
+
+    const checkSort = (val: any) => {
+      console.log(val, 'val')
+      forms.coursewareErr = val.value
+      forms.coursewareErrName = val.name
+    }
+
+    const checkOrchestra = (val: any) => {
+      forms.orchestraId = val.value
+      forms.orchestraName = val.name
+    }
+
+    const checkSubject = (val: any) => {
+      forms.courseType = val.value
+      forms.courseTypeName = val.name
+    }
+    const getOrchestraList = async () => {
+      try {
+        const res = await request.post(`${platformApi.value}/orchestra/page`, {
+          data: { page: 1, rows: 9999, status: 'DONE' }
+        })
+        state.actions = res.data.rows.map((item: any) => {
+          return {
+            name: item.name,
+            value: item.id as string
+          }
+        })
+      } catch (e: any) {
+        const message = e.message
+        showToast(message)
+      }
+    }
+
+    const getSubjects = async () => {
+      try {
+        const res = await request.post(`${platformApi.value}/subjectBasicConfig/page`, {
+          data: { page: 1, rows: 9999 }
+        })
+        state.subjects = res.data.rows.map((item) => {
+          return {
+            name: item.courseTypeName,
+            value: item.courseType as string
+          }
+        })
+      } catch (e: any) {
+        const message = e.message
+        showToast(message)
+      }
+    }
+    const onRefresh = () => {
+      finished.value = false
+      // 重新加载数据
+      // 将 loading 设置为 true,表示处于加载状态
+      loading.value = true
+      getList()
+    }
+
+    // 名称
+    const formatLength = (name: string) => {
+      if (name.length > 11) {
+        const fristName = name.substring(0, 6)
+        const lastName = name.substring(name.length - 5, name.length)
+        return fristName + '...' + lastName
+      } else {
+        return name
+      }
+    }
+
+    const formatTime2Mins = (second: number) => {
+      if (second) {
+        const first = Math.floor(1098 / 60)
+        const last = second % 60
+        return (first >= 10 ? first : '0' + first) + ':' + (last >= 10 ? last : '0' + last)
+      } else {
+        return '00:00'
+      }
+    }
+
+    const dropdownMenuRef = ref()
+    const dropdownItemRef = ref()
+    // 重置
+    const onSearchReset = () => {
+      forms.startTime = dayjs().day(1).format('YYYY-MM-DD')
+      forms.endTime = dayjs().day(7).format('YYYY-MM-DD')
+      forms.orchestraId = ''
+      forms.orchestraName = ''
+      forms.courseType = ''
+      forms.courseTypeName = ''
+      forms.coursewareErr = null
+      forms.coursewareErrName = ''
+      dropdownItemRef.value?.toggle()
+      refreshing.value = true
+      getList()
+    }
+
+    // 搜索
+    const onSearchConfirm = () => {
+      dropdownItemRef.value?.toggle()
+      refreshing.value = true
+      getList()
+    }
+
+    return () => (
+      <div class={[!showContact.value ? 'emptyRootContainer' : '', styles.exerciseRecord]}>
+        <OSticky position="top" background="#F8F8F8">
+          <OHeader border={false}>
+            {{
+              right: () => (
+                <DropdownMenu
+                  class={styles.searchMore}
+                  closeOnClickOverlay={false}
+                  closeOnClickOutside={false}
+                  ref={dropdownMenuRef}
+                >
+                  <DropdownItem title="筛选" v-model={state.showSearchStatus} ref={dropdownItemRef}>
+                    <div class={styles.searchContainer}>
+                      {state.actions.length > 0 && (
+                        <>
+                          <div class={styles.searchTitle}>乐团</div>
+                          <div class={[styles.searchTypeGroup, styles.searchTypeFlex]}>
+                            {state.actions.map((item: any) => (
+                              <div
+                                class={[
+                                  styles.searchTypeItem,
+                                  item.value === forms.orchestraId && styles['is-active']
+                                ]}
+                                onClick={() => checkOrchestra(item)}
+                              >
+                                {formatLength(item.name)}
+                              </div>
+                            ))}
+                          </div>
+                        </>
+                      )}
+
+                      <div class={styles.searchTitle}>时间段</div>
+                      <div class={[styles.searchTypeGroup, styles.searchTypeFlex2]}>
+                        <div
+                          class={styles.searchTypeItem}
+                          onClick={() => (state.showPopoverTime = true)}
+                        >
+                          {forms.startTime}
+                        </div>
+                        <div
+                          class={styles.searchTypeItemLine}
+                          onClick={() => (state.showPopoverTime = true)}
+                        ></div>
+                        <div
+                          class={styles.searchTypeItem}
+                          onClick={() => (state.showPopoverTime = true)}
+                        >
+                          {forms.endTime}
+                        </div>
+                      </div>
+                      <div class={styles.searchTitle}>课程类型</div>
+                      <div class={[styles.searchTypeGroup, styles.searchTypeFlex1]}>
+                        {Object.keys(courseEmnu).map((subject: any) => (
+                          <div
+                            class={[
+                              styles.searchTypeItem,
+                              subject === forms.courseType && styles['is-active']
+                            ]}
+                            onClick={() => {
+                              checkSubject({
+                                name: courseEmnu[subject],
+                                value: subject
+                              })
+                            }}
+                          >
+                            {courseEmnu[subject]}
+                          </div>
+                        ))}
+                      </div>
+                      <div class={styles.searchTitle}>课件使用状态</div>
+                      <div class={[styles.searchTypeGroup, styles.searchTypeFlex]}>
+                        {[
+                          {
+                            name: '正常',
+                            value: false
+                          },
+                          {
+                            name: '异常',
+                            value: true
+                          }
+                        ].map((item: any) => (
+                          <div
+                            class={[
+                              styles.searchTypeItem,
+                              forms.coursewareErr === item.value && styles['is-active']
+                            ]}
+                            onClick={() => checkSort(item)}
+                          >
+                            {item.name}
+                          </div>
+                        ))}
+                      </div>
+                    </div>
+                    <div class={[styles.searchMoreGroup, 'van-hairline--top']}>
+                      <Button type="default" block round size="large" onClick={onSearchReset}>
+                        重置
+                      </Button>
+                      <Button type="primary" block round size="large" onClick={onSearchConfirm}>
+                        查询
+                      </Button>
+                    </div>
+                  </DropdownItem>
+                </DropdownMenu>
+              )
+            }}
+          </OHeader>
+          <div
+            style={{
+              backgroundColor: '#fff'
+            }}
+          >
+            <div class={styles.searchPreview}>
+              <div
+                class={styles.searchPreviewItem}
+                onClick={() => {
+                  // console.log(dropdownItemRef.value, dropdownMenuRef.value)
+                  //
+                  // dropdownItemRef.value?.toggle()
+                  // dropdownMenuRef.value?.click()
+                  // const dropDown = document.querySelector('.van-dropdown-item--down')
+                  // console.log(dropDown.style.top, '1')
+                  // document.querySelector('.van-dropdown-item--down').style.top =
+                  // 'var(--van-nav-bar-height) !important'
+                }}
+              >
+                {forms.startTime}~{forms.endTime}
+              </div>
+              {forms.orchestraId && (
+                <div class={styles.searchPreviewItem}>
+                  {formatLength(forms.orchestraName)}
+                  <Icon
+                    name="cross"
+                    class={styles.cross}
+                    onClick={(e: any) => {
+                      forms.orchestraId = ''
+                      forms.orchestraName = ''
+                      e.stopPropagation()
+                      refreshing.value = true
+                      getList()
+                    }}
+                  />
+                </div>
+              )}
+              {forms.courseType && (
+                <div class={styles.searchPreviewItem}>
+                  {forms.courseTypeName}
+                  <Icon
+                    name="cross"
+                    class={styles.cross}
+                    onClick={(e: any) => {
+                      forms.courseType = ''
+                      forms.courseTypeName = ''
+                      e.stopPropagation()
+                      refreshing.value = true
+                      getList()
+                    }}
+                  />
+                </div>
+              )}
+              {forms.coursewareErr != null && (
+                <div class={styles.searchPreviewItem}>
+                  {forms.coursewareErrName}
+                  <Icon
+                    name="cross"
+                    class={styles.cross}
+                    onClick={(e: any) => {
+                      forms.coursewareErr = null
+                      forms.coursewareErrName = ''
+                      e.stopPropagation()
+                      refreshing.value = true
+                      getList()
+                    }}
+                  />
+                </div>
+              )}
+            </div>
+          </div>
+        </OSticky>
+        {showContact.value ? (
+          <OFullRefresh
+            v-model:modelValue={refreshing.value}
+            onRefresh={onRefresh}
+            style="min-height: 100vh;"
+          >
+            <List
+              loading-text=" "
+              // v-model:loading={loading.value}
+              finished={finished.value}
+              style={{
+                paddingTop: '12px'
+              }}
+              finished-text=" "
+              immediateCheck={false}
+              onLoad={getList}
+            >
+              {list.value.map((item: any) => (
+                <CellGroup inset class={styles.coursewareGroup}>
+                  <Cell class={styles.top1}>
+                    {{
+                      title: () => (
+                        <>
+                          {dayjs(item.startTime).format('YYYY-MM-DD HH:mm')}~
+                          {dayjs(item.endTime).format('HH:mm')}
+                        </>
+                      ),
+                      value: () => <>{formatLength(item.orchestraName)}</>
+                    }}
+                  </Cell>
+                  <Cell class={styles.top2}>
+                    {{
+                      icon: () => (
+                        <Image class={styles.userLogo} src={item.teacherAvatar || iconTeacher} />
+                      ),
+                      title: () => (
+                        <div class={styles.userInfo}>
+                          <p class={[styles.userName, 'van-ellipsis']}>{item.teacherName}</p>
+                          <p class={[styles.courseName, 'van-ellipsis']}>{item.className}</p>
+                        </div>
+                      ),
+                      value: () => (
+                        <div class={styles.courseCount}>
+                          <p class={styles.courseRange}>
+                            <span>{formatTime2Mins(item.coursewarePlayTime)}</span>/
+                            {formatTime2Mins(item.adviseStudyTimeSecond)}
+                          </p>
+                          <p class={styles.courseRangeTips}>使用时长/标准时长</p>
+                        </div>
+                      )
+                    }}
+                  </Cell>
+                </CellGroup>
+              ))}
+            </List>
+          </OFullRefresh>
+        ) : (
+          <OEmpty tips="暂无课件记录" />
+        )}
+
+        <Calendar
+          v-model:show={state.showPopoverTime}
+          firstDayOfWeek={1}
+          showConfirm={false}
+          type="range"
+          maxRange={7}
+          minDate={new Date('2023-02-27')}
+          defaultDate={[dayjs(forms.startTime).toDate(), dayjs(forms.endTime).toDate()]}
+          style={{
+            height: '70%'
+          }}
+          onSelect={(item: any) => {
+            forms.startTime = ''
+            forms.endTime = ''
+            if (!dayjs(item[0]).isBetween(dayjs(forms.startTime), dayjs(forms.endTime))) {
+              forms.startTime = dayjs(item[0]).day(1).format('YYYY-MM-DD')
+              forms.endTime = dayjs(item[0]).day(7).format('YYYY-MM-DD')
+            }
+            state.showPopoverTime = false
+          }}
+        />
+      </div>
+    )
+  }
+})

BIN
src/views/exercise-record/images/icon-member.png


+ 29 - 33
src/views/exercise-record/index.tsx

@@ -5,38 +5,20 @@ import OEmpty from '@/components/o-empty'
 import dayjs from 'dayjs'
 import isBetween from 'dayjs/plugin/isBetween'
 dayjs.extend(isBetween)
-import {
-  Cell,
-  Icon,
-  Popover,
-  Tag,
-  DatePicker,
-  DatePickerColumnType,
-  Popup,
-  List,
-  PullRefresh,
-  ActionSheet,
-  showToast,
-  Picker,
-  DropdownMenu,
-  DropdownItem,
-  Button,
-  Calendar
-} from 'vant'
+import { Icon, List, showToast, DropdownMenu, DropdownItem, Button, Calendar } from 'vant'
 import OFullRefresh from '@/components/o-full-refresh'
 import StudentItem from './modals/student-item'
 import { defineComponent, reactive, ref, onMounted } from 'vue'
 import { state as globalState } from '@/state'
-import { useRouter } from 'vue-router'
+import { useRoute, useRouter } from 'vue-router'
 import styles from './index.module.less'
 import request from '@/helpers/request'
-import OActionSheet from '@/components/o-action-sheet'
-import { formatterDatePicker } from '@/helpers/utils'
 export default defineComponent({
   name: 'exercise-record',
   setup() {
     const platformApi = ref(globalState.platformApi)
     const router = useRouter()
+    const route = useRoute()
     const state = reactive({
       showSearchStatus: true,
       showPopoverTime: false,
@@ -59,7 +41,7 @@ export default defineComponent({
     const refreshing = ref(false)
     const loading = ref(false)
     const finished = ref(false)
-    const showContact = ref(false)
+    const showContact = ref(true)
     const list = ref([])
     const getList = async () => {
       loading.value = true
@@ -69,10 +51,12 @@ export default defineComponent({
           list.value = []
           refreshing.value = false
         }
+        const { endTime, startTime, ...re } = forms
+        // , endTime: endTime + ' 23:59:59', startTime: endTime + ' 00:00:00'
         const res = await request.post(
           `${platformApi.value}/musicPracticeRecord/school/studentPracticeThisWeek`,
           {
-            data: { ...forms }
+            data: { ...re }
           }
         )
 
@@ -95,10 +79,10 @@ export default defineComponent({
         finished.value = true
       }
     }
-    onMounted(() => {
-      getList()
-      getOrchestraList()
-      getSubjects()
+    onMounted(async () => {
+      await getSubjects()
+      await getOrchestraList()
+      await getList()
     })
 
     const onBack = () => {
@@ -142,6 +126,10 @@ export default defineComponent({
           data: { page: 1, rows: 9999 }
         })
         state.subjects = res.data.rows.map((item) => {
+          if (route.query.subjectId == item.subjectId) {
+            forms.subjectId = item.subjectId
+            forms.subjectName = item.subjectName
+          }
           return {
             name: item.subjectName,
             value: item.subjectId as string
@@ -162,9 +150,9 @@ export default defineComponent({
 
     // 名称
     const formatLength = (name: string) => {
-      if (name.length > 9) {
+      if (name.length > 11) {
         const fristName = name.substring(0, 6)
-        const lastName = name.substring(name.length - 6, name.length - 1)
+        const lastName = name.substring(name.length - 5, name.length)
         return fristName + '...' + lastName
       } else {
         return name
@@ -183,7 +171,7 @@ export default defineComponent({
       forms.subjectName = ''
       forms.sortType = ''
       forms.sortTypeName = ''
-      state.showSearchStatus = false
+      dropdownItemRef.value?.toggle()
       refreshing.value = true
       getList()
     }
@@ -197,7 +185,13 @@ export default defineComponent({
 
     return () => (
       <div class={[!showContact.value ? 'emptyRootContainer' : '', styles.exerciseRecord]}>
-        <OSticky position="top" background="#F8F8F8">
+        <OSticky
+          position="top"
+          background="#F8F8F8"
+          onGetHeight={(height: any) => {
+            document.documentElement.style.setProperty('--header-height', height + 'px')
+          }}
+        >
           <OHeader border={false}>
             {{
               right: () => (
@@ -378,7 +372,7 @@ export default defineComponent({
           <OFullRefresh
             v-model:modelValue={refreshing.value}
             onRefresh={onRefresh}
-            style="min-height: 100vh;"
+            style="min-height: calc(100vh - var(--header-height))"
           >
             <List
               loading-text=" "
@@ -387,8 +381,9 @@ export default defineComponent({
               style={{
                 paddingTop: '12px'
               }}
-              finished-text="没有更多了"
+              finished-text=" "
               onLoad={getList}
+              immediateCheck={false}
             >
               {list.value.map((item: any) => (
                 <StudentItem item={item} forms={forms} />
@@ -417,6 +412,7 @@ export default defineComponent({
               forms.startTime = dayjs(item[0]).day(1).format('YYYY-MM-DD')
               forms.endTime = dayjs(item[0]).day(7).format('YYYY-MM-DD')
             }
+            state.showPopoverTime = false
           }}
         />
       </div>

+ 51 - 0
src/views/exercise-record/modals/student-item.module.less

@@ -177,3 +177,54 @@
     align-items: center;
   }
 }
+
+.recordItem {
+  width: auto;
+  margin: 0 13px;
+  padding: 20px 12px;
+  border-radius: 10px;
+  .imgContainer {
+    position: relative;
+    margin-right: 15px;
+    box-sizing: border-box;
+
+    i {
+      width: 18px;
+      height: 15px;
+      position: absolute;
+      right: -4px;
+      top: 35px;
+      background: url('../images/icon-member.png') no-repeat center center;
+      background-size: contain;
+    }
+  }
+  .img {
+    width: 50px;
+    height: 50px;
+    border-radius: 50%;
+    overflow: hidden;
+  }
+
+  .userInfo {
+    display: flex;
+    align-items: center;
+    .userItem {
+      flex: 1;
+      font-size: 12px;
+      color: #777;
+    }
+    .topText {
+      font-size: 16px;
+      color: #333333;
+      line-height: 22px;
+      white-space: nowrap;
+      overflow: hidden;
+      text-overflow: ellipsis;
+      max-width: 70px;
+      span {
+        font-weight: bold;
+        font-family: DINA;
+      }
+    }
+  }
+}

+ 42 - 3
src/views/exercise-record/modals/student-item.tsx

@@ -5,7 +5,7 @@ import msgIcon from '@/school/images/msg-icon.png'
 import sendmsgIcon from '@/school/images/sendmsg-icon.png'
 import phoneIcon from '@/school/images/phone-icon.png'
 import { postMessage } from '@/helpers/native-message'
-import { Icon, ActionSheet, showToast } from 'vant'
+import { Icon, ActionSheet, showToast, Image, Cell } from 'vant'
 import { useRouter } from 'vue-router'
 const myForms = ref({}) as any
 export default defineComponent({
@@ -62,6 +62,7 @@ export default defineComponent({
     }
     const gotoDetail = () => {
       console.log(myForms.value.practiceMonth, myForms.value.practiceMonthName)
+      console.log(props.item)
       router.push({
         path: '/exercis-detail',
         query: {
@@ -73,7 +74,45 @@ export default defineComponent({
     }
     return () => (
       <>
-        <div>
+        <Cell isLink class={styles.recordItem} center onClick={gotoDetail}>
+          {{
+            icon: () => (
+              <div class={styles.imgContainer}>
+                <Image
+                  class={styles.img}
+                  src={props.item.avatar ? props.item.avatar : defaultIcon}
+                  alt=""
+                />
+                <i class={styles.iconMember}></i>
+              </div>
+            ),
+            title: () => (
+              <div class={styles.userInfo}>
+                <div class={styles.userItem}>
+                  <p class={styles.topText} style="font-weight: bold;">
+                    李小明
+                  </p>
+                  <p class={styles.bottomText}>
+                    {props.item.subjectNames ? props.item.subjectNames : '暂无声部'}
+                  </p>
+                </div>
+                <div class={styles.userItem}>
+                  <p class={styles.topText}>
+                    <span>{props.item.trainingTime ? props.item.trainingTime : 0}</span>分钟
+                  </p>
+                  <p class={styles.bottomText}>练习时长</p>
+                </div>
+                <div class={styles.userItem}>
+                  <p class={styles.topText}>
+                    <span>{props.item.trainingDays ? props.item.trainingDays : 0}</span>天
+                  </p>
+                  <p class={styles.bottomText}>练习天数</p>
+                </div>
+              </div>
+            )
+          }}
+        </Cell>
+        {/* <div>
           <div class={styles.itemWrap} onClick={gotoDetail}>
             <div class={styles.itemTop}>
               <div class={styles.itemTopLeft}>
@@ -116,7 +155,7 @@ export default defineComponent({
               </div>
             </div>
           </div>
-        </div>
+        </div> */}
         <ActionSheet
           class="bottomSheet"
           v-model:show={showContact.value}