Browse Source

添加训练报告功能

lex 2 years ago
parent
commit
c793e57e9f

+ 17 - 17
src/router/routes-school.ts

@@ -23,6 +23,22 @@ const noLoginRouter = [
     meta: {
       title: '乐团管理老师注册'
     }
+  },
+  {
+    path: '/train-week-report',
+    name: 'train-week-report',
+    component: () => import('@/school/train-report/week-report'),
+    meta: {
+      title: '训练周报'
+    }
+  },
+  {
+    path: '/train-month-report',
+    name: 'train-month-report',
+    component: () => import('@/school/train-report/month-report'),
+    meta: {
+      title: '训练月报'
+    }
   }
 ]
 
@@ -413,23 +429,7 @@ export default [
         meta: {
           title: '训练报告'
         }
-      },
-      {
-        path: '/train-week-report',
-        name: 'train-week-report',
-        component: () => import('@/school/train-report/week-report'),
-        meta: {
-          title: '训练周报'
-        }
-      },
-      {
-        path: '/train-month-report',
-        name: 'train-month-report',
-        component: () => import('@/school/train-report/month-report'),
-        meta: {
-          title: '训练月报'
-        }
-      },
+      }
       //
     ]
   },

+ 172 - 97
src/school/train-report/components/list.tsx

@@ -1,25 +1,44 @@
 import request from '@/helpers/request'
-import { Button, Cell, CellGroup, List, Picker, Popup } from 'vant'
-import { defineComponent, reactive } from 'vue'
+import {
+  Button,
+  Cell,
+  CellGroup,
+  DatePicker,
+  DatePickerColumnType,
+  List,
+  Picker,
+  Popup
+} from 'vant'
+import { defineComponent, onMounted, reactive, ref } from 'vue'
 import styles from '../index.module.less'
 import { state as baseState } from '@/state'
 import iconOrchestra from '@/views/mine-orchestra/images/icon-or.png'
 import OEmpty from '@/components/o-empty'
 import { useRouter } from 'vue-router'
+import dayjs from 'dayjs'
+import { formatterDatePicker } from '@/helpers/utils'
+import OFullRefresh from '@/components/o-full-refresh'
 
 export default defineComponent({
   name: 'train-list',
   props: {
     type: {
       type: String,
-      default: 'week'
+      default: 'WEEKLY'
+    },
+    orchestraList: {
+      type: Array,
+      default: () => []
     }
   },
   setup(props) {
+    const columnsType = ref<DatePickerColumnType[]>(['year', 'month'])
     const router = useRouter()
     const state = reactive({
+      showPopoverTime: false,
+      timeName: dayjs().format('YYYY') + '年' + dayjs().format('MM') + '月',
+      currentDate: [dayjs().format('YYYY'), dayjs().format('MM')],
       orchestraStatus: false,
-      orchestraList: [] as any,
       selectOrchestra: {} as any,
       isClick: false,
       list: [] as any,
@@ -35,59 +54,83 @@ export default defineComponent({
         rows: 20
       }
     })
-    // 获取乐团列表
-    const getOrchestras = async () => {
+
+    const getList = async () => {
       try {
-        const { data } = await request.post('/api-school/orchestra/page', {
+        if (state.isClick) return
+        state.isClick = true
+        const { data } = await request.post('/api-school/orchestraReport/page', {
           data: {
-            page: 1,
-            rows: 100,
-            schoolId: baseState.user.data.school.id,
-            status: 'DONE'
+            reportType: props.type,
+            page: state.params.page,
+            rows: state.params.rows,
+            orchestraId: state.selectOrchestra.value
           }
         })
-        const temps = data.rows || []
-        const s = [] as any
-        temps.forEach((item: any) => {
-          s.push({
-            text: item.name,
-            value: item.id
-          })
-        })
-        state.orchestraList = [...s]
 
-        // 判断是否有乐团
-        if (s.length > 0) {
-          const orchestraId = sessionStorage.getItem('orchestraStoryId')
-          if (orchestraId) {
-            const item = s.find((child: any) => child.value === orchestraId)
-            state.selectOrchestra = item || s[0]
-          } else {
-            state.selectOrchestra = s[0]
-          }
+        state.listState.loading = false
+        state.listState.refreshing = false
+        // 处理重复请求数据
+        if (state.list.length > 0 && data.current === 1) {
+          return
         }
-
-        await getList()
+        state.list = state.list.concat(data.rows || [])
+        state.listState.finished = data.current >= data.pages
+        state.params.page = data.current + 1
+        state.listState.dataShow = state.list.length > 0
+        state.isClick = false
       } catch {
-        //
+        state.listState.dataShow = false
+        state.listState.finished = true
+        state.listState.refreshing = false
+        state.isClick = false
       }
     }
 
-    getOrchestras()
+    const onRefresh = () => {
+      state.params.page = 1
+      state.list = []
+      state.listState.dataShow = true // 判断是否有数据
+      state.listState.loading = false
+      state.listState.finished = false
+      getList()
+    }
 
-    const getList = async () => {
+    const onDetail = (item: any) => {
+      const path = props.type === 'WEEKLY' ? '/train-week-report' : '/train-month-report'
+      router.push({
+        path,
+        query: {
+          id: item.id
+        }
+      })
+    }
+
+    const checkTimer = (val: any) => {
+      state.timeName = val.selectedValues[0] + '年' + val.selectedValues[1] + '月'
+      state.showPopoverTime = false
+      onRefresh()
+    }
+
+    onMounted(async () => {
       try {
-        await request.post('/api-school/orchestraReport/page', {
-          data: {
-            reportType: props.type,
-            page: state.params.page,
-            rows: state.params.rows
-          }
-        })
+        // 判断是否有乐团
+        if (props.orchestraList.length > 0) {
+          // const orchestraId = sessionStorage.getItem('orchestraStoryId')
+          // if (orchestraId) {
+          //   const item = props.orchestraList.find((child: any) => child.value === orchestraId)
+          //   state.selectOrchestra = item || props.orchestraList[0]
+          // } else {
+          state.selectOrchestra = props.orchestraList[0]
+          // }
+        }
+
+        await getList()
       } catch {
         //
       }
-    }
+    })
+
     return () => (
       <div
         style={{
@@ -95,62 +138,94 @@ export default defineComponent({
           overflow: 'hidden'
         }}
       >
-        {state.orchestraList.length > 0 && (
-          <div style={{ width: '100%' }}>
-            <CellGroup inset class={styles.cellGroup}>
-              <Cell
-                isLink
-                center
-                onClick={() => (state.orchestraStatus = true)}
-                class={styles.select}
-              >
-                {{
-                  icon: () => <img class={styles.icon} src={iconOrchestra} />,
-                  title: () => <div class="van-ellipsis">{state.selectOrchestra.text || ' '}</div>
-                }}
-              </Cell>
-            </CellGroup>
+        <div class={'searchGroup'}>
+          <div
+            class={['searchItem', state.showPopoverTime && 'searchItem-active']}
+            onClick={() => {
+              state.showPopoverTime = true
+            }}
+          >
+            {state.timeName}
+            <i class={'arrow'}></i>
           </div>
-        )}
-
-        {state.listState.dataShow ? (
-          // <OFullRefresh
-          //   v-model:modelValue={state.listState.refreshing}
-          //   onRefresh={onRefresh}
-          //   style={{
-          //     minHeight: `calc(100vh - ${state.listState.height}px)`
-          //   }}
-          // >
-          <List
-            // v-model:loading={state.listState.loading}
-            finished={state.listState.finished}
-            finishedText=" "
-            class={[styles.liveList]}
-            onLoad={getList}
-            immediateCheck={false}
+          <div
+            class={['searchItem', state.orchestraStatus && 'searchItem-active']}
+            onClick={() => {
+              state.orchestraStatus = true
+            }}
           >
-            <Cell center class={styles.reportList}>
-              {{
-                title: () => <div>武汉小学第一届乐团</div>,
-                value: () => (
-                  <Button
-                    type="primary"
-                    round
-                    size="small"
-                    onClick={() => {
-                      router.push('/train-week-report')
-                    }}
-                  >
-                    查看报告
-                  </Button>
-                ),
-                label: () => <div class={styles.time}>2023年3月</div>
+            <span>{state.selectOrchestra.text || ' '}</span>
+            <i class={'arrow'}></i>
+          </div>
+        </div>
+        <div
+          style={{
+            overflowY: 'auto',
+            height: 'calc(100vh - var(--van-tabs-line-height)  - 1.17333rem)'
+          }}
+        >
+          {state.listState.dataShow ? (
+            <OFullRefresh
+              v-model:modelValue={state.listState.refreshing}
+              onRefresh={onRefresh}
+              style={{
+                minHeight: 'calc(100vh - var(--van-tabs-line-height)  - 1.17333rem)'
               }}
-            </Cell>
-          </List>
-        ) : (
-          <OEmpty btnStatus={false} tips="暂无训练报告" />
-        )}
+            >
+              <List
+                // v-model:loading={state.listState.loading}
+                finished={state.listState.finished}
+                finishedText=" "
+                style={{
+                  overflow: 'hidden'
+                }}
+                onLoad={getList}
+                immediateCheck={false}
+              >
+                {state.list.map((item: any) => (
+                  <Cell center class={styles.reportList}>
+                    {{
+                      title: () => <div>{item.orchestraName}</div>,
+                      value: () => (
+                        <Button type="primary" round size="small" onClick={() => onDetail(item)}>
+                          查看报告
+                        </Button>
+                      ),
+                      label: () =>
+                        props.type === 'WEEKLY' ? (
+                          <div class={styles.time}>
+                            {item.startTime}~{item.endTime}
+                          </div>
+                        ) : (
+                          <div class={styles.time}>{item.monthlyTime}</div>
+                        )
+                    }}
+                  </Cell>
+                ))}
+              </List>
+            </OFullRefresh>
+          ) : (
+            <OEmpty btnStatus={false} tips="暂无训练报告" />
+          )}
+        </div>
+
+        <Popup
+          v-model:show={state.showPopoverTime}
+          position="bottom"
+          round
+          teleport={'body'}
+          class={'popupBottomSearch'}
+        >
+          <DatePicker
+            onCancel={() => {
+              state.showPopoverTime = false
+            }}
+            onConfirm={checkTimer}
+            v-model={state.currentDate}
+            formatter={formatterDatePicker}
+            columnsType={columnsType.value}
+          />
+        </Popup>
 
         <Popup
           v-model:show={state.orchestraStatus}
@@ -160,12 +235,12 @@ export default defineComponent({
           teleport="body"
         >
           <Picker
-            columns={state.orchestraList}
+            columns={props.orchestraList as any}
             onCancel={() => (state.orchestraStatus = false)}
             onConfirm={(val: any) => {
               state.selectOrchestra = val.selectedOptions[0]
               state.orchestraStatus = false
-              sessionStorage.setItem('orchestraStoryId', state.selectOrchestra.value)
+              onRefresh()
             }}
           />
         </Popup>

BIN
src/school/train-report/images/icon-class.png


BIN
src/school/train-report/images/icon-photo.png


BIN
src/school/train-report/images/month/icon-train-month.png


BIN
src/school/train-report/images/month/share-month-banner.png


BIN
src/school/train-report/images/small-icon.png


BIN
src/school/train-report/images/week/share-week-banner.png


+ 3 - 0
src/school/train-report/index.module.less

@@ -57,5 +57,8 @@
       font-size: 14px;
       color: #777;
     }
+    .van-cell__value {
+      flex: 0 auto;
+    }
   }
 }

+ 49 - 9
src/school/train-report/index.tsx

@@ -1,21 +1,61 @@
+import request from '@/helpers/request'
 import { Tab, Tabs } from 'vant'
-import { defineComponent } from 'vue'
+import { defineComponent, reactive } from 'vue'
 import List from './components/list'
 import styles from './index.module.less'
 
 export default defineComponent({
   name: 'train-report',
   setup() {
+    const state = reactive({
+      loading: true,
+      orchestraList: [] as any
+    })
+    // 获取乐团列表
+    const getOrchestras = async () => {
+      try {
+        state.loading = true
+        const { data } = await request.post('/api-school/orchestra/page', {
+          data: {
+            page: 1,
+            rows: 100,
+            status: 'DONE'
+          }
+        })
+        const temps = data.rows || []
+        const s = [] as any
+        temps.forEach((item: any) => {
+          s.push({
+            text: item.name,
+            value: item.id
+          })
+        })
+        s.unshift({
+          text: '全部乐团',
+          value: ''
+        })
+        state.orchestraList = [...s]
+
+        state.loading = false
+      } catch {
+        //
+        state.loading = false
+      }
+    }
+
+    getOrchestras()
     return () => (
       <div class={styles.train}>
-        <Tabs sticky lineWidth={20} lineHeight={4} swipeable animated>
-          <Tab title="周报" name="WEEKLY">
-            <List type="WEEKLY" />
-          </Tab>
-          <Tab title="月报" name="MONTHLY">
-            <List type="MONTHLY" />
-          </Tab>
-        </Tabs>
+        {!state.loading && (
+          <Tabs sticky lineWidth={20} lineHeight={4} swipeable animated>
+            <Tab title="周报" name="WEEKLY">
+              <List type="WEEKLY" orchestraList={state.orchestraList} />
+            </Tab>
+            <Tab title="月报" name="MONTHLY">
+              <List type="MONTHLY" orchestraList={state.orchestraList} />
+            </Tab>
+          </Tabs>
+        )}
       </div>
     )
   }

+ 2 - 1
src/school/train-report/modal/index.module.less

@@ -39,6 +39,7 @@
     }
     .arrowPoint {
       height: 5px;
+      width: 22px;
       margin-right: 15px;
     }
     .name {
@@ -145,7 +146,7 @@
     }
   }
   .classEcharts {
-    height: 230px;
+    height: 210px;
     width: 100%;
   }
 

+ 37 - 5
src/school/train-report/modal/orchestra-num.tsx

@@ -1,9 +1,10 @@
-import { defineComponent } from 'vue'
+import { defineComponent, reactive, watch } from 'vue'
 import { Image } from 'vant'
 import styles from './index.module.less'
 import arrowLine from '../images/week/icon-arrow-line.png'
 import arrowPoint from '../images/icon-arrow-point.png'
 import teacherArrowLine from '../images/month/teacher-icon-arrow-line.png'
+import { reportCourseType } from '../week-report'
 
 export default defineComponent({
   name: 'orchestra-num',
@@ -11,9 +12,39 @@ export default defineComponent({
     type: {
       type: String,
       default: 'week'
+    },
+    reportData: {
+      type: Object,
+      default: () => ({})
     }
   },
   setup(props) {
+    const forms = reactive({
+      total: 0,
+      list: [] as any
+    })
+    const formatCourse = () => {
+      const data = props.reportData || {}
+
+      for (const i in data) {
+        if (i === 'TOTAL') {
+          forms.total = data[i]
+        } else {
+          forms.list.push({
+            text: reportCourseType[i],
+            value: data[i]
+          })
+        }
+      }
+    }
+    formatCourse()
+
+    watch(
+      () => props.reportData,
+      () => {
+        formatCourse()
+      }
+    )
     return () => (
       <div class={[styles.trainSection, props.type === 'month' ? styles.teacherTrainSection : '']}>
         <div class={styles.trainTitle}>
@@ -26,18 +57,19 @@ export default defineComponent({
           </div>
           <div class={styles.countNums}>
             <Image src={arrowPoint} class={styles.arrowPoint} />
-            乐团总人数<span>272</span>人
+            乐团总人数<span>{forms.total}</span>人
           </div>
         </div>
 
         <div class={styles.trainSectionContent}>
           <div class={styles.tContent}>
-            {[1, 2, 3, 4, 5, 6].map((item, index) => (
+            {forms.list.map((item: any) => (
               <div class={styles.tItem}>
                 <p class={styles.pople}>
-                  20<span>人</span>
+                  {item.value}
+                  <span>人</span>
                 </p>
-                <p class={styles.subjectName}>长笛</p>
+                <p class={styles.subjectName}>{item.text}</p>
               </div>
             ))}
           </div>

+ 52 - 12
src/school/train-report/modal/student-attendance.tsx

@@ -1,4 +1,4 @@
-import { defineComponent, onMounted } from 'vue'
+import { defineComponent, onMounted, reactive, watch } from 'vue'
 import { Image } from 'vant'
 import styles from './index.module.less'
 import arrowLine from '../images/week/icon-arrow-line.png'
@@ -18,10 +18,10 @@ import {
   ToolboxComponent,
   DataZoomComponent
 } from 'echarts/components'
-import { BarChart } from 'echarts/charts'
-import { PieChart } from 'echarts/charts'
+import { LineChart } from 'echarts/charts'
 import { LabelLayout, UniversalTransition } from 'echarts/features'
 import { CanvasRenderer } from 'echarts/renderers'
+import { reportCourseType } from '../week-report'
 // type EChartsOption = echarts.EChartsOption
 
 // 注册必须的组件
@@ -31,14 +31,13 @@ echarts.use([
   GridComponent,
   DatasetComponent,
   TransformComponent,
-  BarChart,
   LabelLayout,
   UniversalTransition,
   CanvasRenderer,
-  PieChart,
   ToolboxComponent,
   LegendComponent,
-  DataZoomComponent
+  DataZoomComponent,
+  LineChart
 ])
 
 export default defineComponent({
@@ -47,21 +46,54 @@ export default defineComponent({
     type: {
       type: String,
       default: 'week'
+    },
+    reportData: {
+      type: Object,
+      default: () => ({})
     }
   },
   setup(props) {
+    const forms = reactive({
+      total: 0
+    })
+
+    watch(
+      () => props.reportData,
+      () => {
+        handleInit()
+      }
+    )
+
     let myChart: any
     const handleInit = () => {
       if (myChart) {
         myChart.dispose()
       }
+
+      const data = props.reportData || {}
+      const titleList: any = []
+      const valueList: any = []
+      let lineColor = '#356BF7'
+      let textColor = '#1B4FD7'
+      for (const i in data) {
+        if (i === 'TOTAL') {
+          forms.total = data[i] * 100
+          if (forms.total <= 59) {
+            lineColor = '#FF8057'
+            textColor = '#F67146'
+          }
+        } else {
+          titleList.push(reportCourseType[i])
+          valueList.push(data[i] * 100)
+        }
+      }
+
       const chartDom = document.getElementById('studentEcharts')
-      console.log(chartDom, 'chartDom')
       myChart = echarts.init(chartDom as HTMLDivElement)
       const option = {
         xAxis: {
           type: 'category',
-          data: ['Mon', 'Tue', 'Wed', '上低音号', 'Fri', 'Sat', 'Sun', 'Fri', 'Sat', 'Sun'],
+          data: titleList,
           axisLabel: {
             rotate: 45,
             fontSize: 10,
@@ -83,15 +115,15 @@ export default defineComponent({
             type: 'line',
             showBackground: false,
             itemStyle: {
-              color: '#FF8057'
+              color: lineColor
             },
             label: {
               show: true,
               position: 'top',
               fontSize: 10,
-              color: '#FF8057'
+              color: textColor
             },
-            data: [120, 200, 150, 80, 70, 110, 130, 70, 110, 130]
+            data: valueList
           }
         ]
       }
@@ -121,7 +153,15 @@ export default defineComponent({
           </div>
           <div class={styles.countNums}>
             <Image src={arrowPoint} class={styles.arrowPoint} />
-            总出勤率<span>59</span>%
+            总出勤率
+            <span
+              style={{
+                color: forms.total > 59 ? '#42FFE2' : '#FF7C88'
+              }}
+            >
+              {forms.total}
+            </span>
+            %
           </div>
         </div>
 

+ 50 - 22
src/school/train-report/modal/teacher-attendance.tsx

@@ -1,4 +1,4 @@
-import { defineComponent, onMounted } from 'vue'
+import { defineComponent, onMounted, reactive, watch } from 'vue'
 import { Image } from 'vant'
 import styles from './index.module.less'
 import arrowLine from '../images/week/icon-arrow-line.png'
@@ -7,31 +7,20 @@ import teacherArrowLine from '../images/month/teacher-icon-arrow-line.png'
 import * as echarts from 'echarts/core'
 import {
   TitleComponent,
-  // 组件类型的定义后缀都为 ComponentOption
-  TitleComponentOption,
   TooltipComponent,
-  TooltipComponentOption,
   GridComponent,
-  GridComponentOption,
   // 数据集组件
   DatasetComponent,
-  DatasetComponentOption,
   // 内置数据转换器组件 (filter, sort)
   TransformComponent,
   LegendComponent,
   ToolboxComponent,
   DataZoomComponent
 } from 'echarts/components'
-import {
-  BarChart,
-  // 系列类型的定义后缀都为 SeriesOption
-  BarSeriesOption,
-  LineChart,
-  LineSeriesOption
-} from 'echarts/charts'
-import { PieChart } from 'echarts/charts'
+import { LineChart } from 'echarts/charts'
 import { LabelLayout, UniversalTransition } from 'echarts/features'
 import { CanvasRenderer } from 'echarts/renderers'
+import { reportCourseType } from '../week-report'
 // type EChartsOption = echarts.EChartsOption
 
 // 注册必须的组件
@@ -41,11 +30,9 @@ echarts.use([
   GridComponent,
   DatasetComponent,
   TransformComponent,
-  BarChart,
   LabelLayout,
   UniversalTransition,
   CanvasRenderer,
-  PieChart,
   ToolboxComponent,
   LegendComponent,
   DataZoomComponent,
@@ -58,21 +45,54 @@ export default defineComponent({
     type: {
       type: String,
       default: 'week'
+    },
+    reportData: {
+      type: Object,
+      default: () => ({})
     }
   },
   setup(props) {
+    const forms = reactive({
+      total: 0
+    })
+
+    watch(
+      () => props.reportData,
+      () => {
+        handleInit()
+      }
+    )
+
     let myChart: any
     const handleInit = () => {
       if (myChart) {
         myChart.dispose()
       }
+
+      const data = props.reportData || {}
+      const titleList: any = []
+      const valueList: any = []
+      let lineColor = '#356BF7'
+      let textColor = '#1B4FD7'
+      for (const i in data) {
+        if (i === 'TOTAL') {
+          forms.total = data[i] * 100
+          if (forms.total <= 59) {
+            lineColor = '#FF8057'
+            textColor = '#F67146'
+          }
+        } else {
+          titleList.push(reportCourseType[i])
+          valueList.push(data[i] * 100)
+        }
+      }
+
       const chartDom = document.getElementById('teacherEcharts')
-      console.log(chartDom, 'chartDom')
       myChart = echarts.init(chartDom as HTMLDivElement)
       const option = {
         xAxis: {
           type: 'category',
-          data: ['Mon', 'Tue', 'Wed', '上低音号', 'Fri', 'Sat', 'Sun', 'Fri', 'Sat', 'Sun'],
+          data: titleList,
           axisLabel: {
             rotate: 45,
             fontSize: 10,
@@ -94,15 +114,15 @@ export default defineComponent({
             type: 'line',
             showBackground: false,
             itemStyle: {
-              color: '#FF8057'
+              color: lineColor
             },
             label: {
               show: true,
               position: 'top',
               fontSize: 10,
-              color: '#FF8057'
+              color: textColor
             },
-            data: [120, 200, 150, 80, 70, 110, 130, 70, 110, 130]
+            data: valueList
           }
         ]
       }
@@ -132,7 +152,15 @@ export default defineComponent({
           </div>
           <div class={styles.countNums}>
             <Image src={arrowPoint} class={styles.arrowPoint} />
-            总出勤率<span>59</span>%
+            总出勤率
+            <span
+              style={{
+                color: forms.total > 59 ? '#42FFE2' : '#FF7C88'
+              }}
+            >
+              {forms.total}
+            </span>
+            %
           </div>
         </div>
 

+ 33 - 5
src/school/train-report/modal/train-class.tsx

@@ -1,4 +1,4 @@
-import { defineComponent, onMounted } from 'vue'
+import { defineComponent, onMounted, reactive, watch } from 'vue'
 import { Image } from 'vant'
 import styles from './index.module.less'
 import arrowLine from '../images/week/icon-arrow-line.png'
@@ -22,6 +22,7 @@ import { BarChart } from 'echarts/charts'
 import { PieChart } from 'echarts/charts'
 import { LabelLayout, UniversalTransition } from 'echarts/features'
 import { CanvasRenderer } from 'echarts/renderers'
+import { reportCourseType } from '../week-report'
 // type EChartsOption = echarts.EChartsOption
 
 // 注册必须的组件
@@ -47,21 +48,48 @@ export default defineComponent({
     type: {
       type: String,
       default: 'week'
+    },
+    reportData: {
+      type: Object,
+      default: () => ({})
     }
   },
   setup(props) {
+    const forms = reactive({
+      total: 0
+    })
+
+    watch(
+      () => props.reportData,
+      () => {
+        handleInit()
+      }
+    )
+
     let myChart: any
     const handleInit = () => {
       if (myChart) {
         myChart.dispose()
       }
+
+      const data = props.reportData || {}
+      const titleList: any = []
+      const valueList: any = []
+      for (const i in data) {
+        if (i === 'TOTAL') {
+          forms.total = data[i]
+        } else {
+          titleList.push(reportCourseType[i])
+          valueList.push(data[i])
+        }
+      }
+
       const chartDom = document.getElementById('classEcharts')
-      console.log(chartDom, 'chartDom')
       myChart = echarts.init(chartDom as HTMLDivElement)
       const option = {
         xAxis: {
           type: 'category',
-          data: ['Mon', 'Tue', 'Wed', '上低音号', 'Fri', 'Sat', 'Sun', 'Fri', 'Sat', 'Sun'],
+          data: titleList,
           axisLabel: {
             rotate: 45,
             fontSize: 10,
@@ -82,6 +110,7 @@ export default defineComponent({
           {
             type: 'bar',
             showBackground: false,
+            barWidth: 15,
             itemStyle: {
               color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
                 { offset: 0, color: '#69DCE8' },
@@ -104,11 +133,10 @@ export default defineComponent({
               fontSize: 10,
               color: '#777'
             },
-            data: [120, 200, 150, 80, 70, 110, 130, 70, 110, 130]
+            data: valueList
           }
         ]
       }
-
       option && myChart.setOption(option)
     }
 

+ 34 - 5
src/school/train-report/modal/train-progress.tsx

@@ -1,18 +1,47 @@
-import { defineComponent } from 'vue'
+import { defineComponent, reactive, watch } from 'vue'
 import { Image, Progress } from 'vant'
 import styles from './index.module.less'
 import arrowLine from '../images/week/icon-arrow-line.png'
 import arrowPoint from '../images/icon-arrow-point.png'
 import teacherArrowLine from '../images/month/teacher-icon-arrow-line.png'
+import { reportCourseType } from '../week-report'
 export default defineComponent({
   name: 'orchestra-num',
   props: {
     type: {
       type: String,
       default: 'week'
+    },
+    reportData: {
+      type: Object,
+      default: () => ({})
     }
   },
   setup(props) {
+    const forms = reactive({
+      total: 0,
+      knowledgeList: [] as any
+    })
+
+    watch(
+      () => props.reportData,
+      () => {
+        formatCourse()
+      }
+    )
+
+    const formatCourse = () => {
+      const knowledge = props.reportData
+      for (const i in knowledge) {
+        i != 'TOTAL' &&
+          forms.knowledgeList.push({
+            courseName: reportCourseType[i],
+            ...knowledge[i]
+          })
+      }
+    }
+
+    formatCourse()
     return () => (
       <div class={[styles.trainSection, props.type === 'month' ? styles.teacherTrainSection : '']}>
         <div class={styles.trainTitle}>
@@ -30,18 +59,18 @@ export default defineComponent({
 
         <div class={styles.trainSectionContent}>
           <div class={[styles.tContent, styles.tProgress]}>
-            {[1, 2, 3, 4, 5, 6].map((item, index) => (
+            {forms.knowledgeList.map((item: any) => (
               <div class={styles.progressItem}>
                 <div class={styles.className}>
                   <i class={styles.line}></i>
-                  长笛班
+                  {item.courseName}
                 </div>
                 <div class={styles.classNum}>
                   <div class={styles.konowCount}>
-                    已学习知识点<span>33</span>个
+                    已学习知识点<span>{item.learned || 0}</span>个
                   </div>
                   <div class={styles.allCount}>
-                    共<span>33</span>个
+                    共<span>{item.total || 0}</span>个
                   </div>
                 </div>
 

+ 227 - 23
src/school/train-report/month-report.tsx

@@ -1,8 +1,19 @@
 import OHeader from '@/components/o-header'
-import { defineComponent, reactive } from 'vue'
+import { defineComponent, onMounted, reactive } from 'vue'
 import styles from './report.module.less'
 import iconOrchestra from '@/views/mine-orchestra/images/icon-or.png'
-import { Grid, GridItem, Icon, Image, Popup } from 'vant'
+import {
+  closeToast,
+  Grid,
+  GridItem,
+  Icon,
+  Image,
+  Popup,
+  showFailToast,
+  showLoadingToast,
+  showSuccessToast,
+  showToast
+} from 'vant'
 import trainWeek from './images/month/icon-train-month.png'
 import OrchestraNum from './modal/orchestra-num'
 import TrainClass from './modal/train-class'
@@ -14,52 +25,243 @@ import iconClass from './images/icon-class.png'
 import iconSaveImage from '@/school/orchestra/images/icon-save-image.png'
 import iconWechat from '@/school/orchestra/images/icon-wechat.png'
 import OQrcode from '@/components/o-qrcode'
+import request from '@/helpers/request'
+import { useRoute, useRouter } from 'vue-router'
+import { postMessage, promisefiyPostMessage } from '@/helpers/native-message'
+import html2canvas from 'html2canvas'
+
+export const reportCourseType = {
+  PERCUSSION: '打击乐',
+  FLUTE: '长笛',
+  SAX: '萨克斯',
+  CLARINET: '单簧管',
+  TRUMPET: '小号',
+  TROMBONE: '长号',
+  HORN: '圆号',
+  BARITONE_TUBA: '上低音号-大号',
+  EUPHONIUM: '上低音号',
+  TUBA: '大号',
+  MUSIC_THEORY: '乐理',
+  INSTRUMENTAL_ENSEMBLE: '合奏'
+}
 
 export default defineComponent({
   name: 'train-report',
   setup() {
+    const router = useRouter()
+    const route = useRoute()
     const forms = reactive({
+      id: route.query.id,
+      share: route.query.share as any,
       showQrcode: false,
-      url: 'http://'
+      url: window.location.href + '&share=1'
+    })
+    const reportData = reactive({
+      orchestraName: null,
+      monthlyTime: null,
+      startTime: null,
+      endTime: null,
+      COURSEWARE: {},
+      coursewareList: [] as any,
+      COURSE_SCHEDULE: {},
+      KNOWLEDGE: {},
+      ORCHESTRA: {},
+      PHOTO: {} as any,
+      STUDENT_ATTENDANCE: {},
+      TEACHER_ATTENDANCE: {}
+    })
+
+    const getDetail = async () => {
+      try {
+        const { data } = await request.get('/api-school/open/orchestraReport/detail/' + forms.id)
+        reportData.COURSEWARE = data.reportItem.COURSEWARE || {}
+        reportData.COURSE_SCHEDULE = data.reportItem.COURSE_SCHEDULE || {}
+        reportData.KNOWLEDGE = data.reportItem.KNOWLEDGE || {}
+        reportData.ORCHESTRA = data.reportItem.ORCHESTRA || {}
+        reportData.PHOTO = data.reportItem.PHOTO || {}
+        reportData.STUDENT_ATTENDANCE = data.reportItem.STUDENT_ATTENDANCE || {}
+        reportData.TEACHER_ATTENDANCE = data.reportItem.TEACHER_ATTENDANCE || {}
+        reportData.orchestraName = data.orchestraName || ''
+        reportData.monthlyTime = data.monthlyTime || ''
+        reportData.startTime = data.startTime || ''
+        reportData.endTime = data.endTime || ''
+
+        const courseware = reportData.COURSEWARE
+        for (const i in courseware) {
+          i != 'TOTAL' && reportData.coursewareList.push(reportCourseType[i])
+        }
+      } catch {
+        //
+      }
+    }
+
+    const imgs = reactive({
+      saveLoading: false,
+      image: null as any,
+      shareLoading: false
+    })
+    const onSaveImg = async () => {
+      // 判断是否在保存中...
+      if (imgs.saveLoading) {
+        return
+      }
+      imgs.saveLoading = true
+      // 判断是否已经生成图片
+      if (imgs.image) {
+        saveImg()
+      } else {
+        const container: any = document.getElementById(`preview-container`)
+        html2canvas(container, {
+          allowTaint: true,
+          useCORS: true,
+          backgroundColor: null
+        })
+          .then(async (canvas) => {
+            const url = canvas.toDataURL('image/png')
+            imgs.image = url
+            saveImg()
+          })
+          .catch(() => {
+            closeToast()
+            imgs.saveLoading = false
+          })
+      }
+    }
+    const onShare = () => {
+      if (imgs.shareLoading) {
+        return
+      }
+      imgs.shareLoading = true
+      if (imgs.image) {
+        openShare()
+      } else {
+        const container: any = document.getElementById(`preview-container`)
+        html2canvas(container, {
+          allowTaint: true,
+          useCORS: true,
+          backgroundColor: null
+        })
+          .then(async (canvas) => {
+            const url = canvas.toDataURL('image/png')
+            imgs.image = url
+            openShare()
+          })
+          .catch(() => {
+            closeToast()
+            imgs.shareLoading = false
+          })
+      }
+    }
+    const openShare = () => {
+      const image = imgs.image
+      setTimeout(() => {
+        imgs.shareLoading = false
+      }, 100)
+      if (image) {
+        postMessage(
+          {
+            api: 'shareTripartite',
+            content: {
+              title: '',
+              desc: '',
+              image,
+              video: '',
+              type: 'image',
+              // button: ['copy']
+              shareType: 'wechat'
+            }
+          },
+          (res: any) => {
+            if (res && res.content) {
+              showToast(res.content.message || (res.content.status ? '分享成功' : '分享失败'))
+            }
+          }
+        )
+      }
+    }
+    const saveImg = async () => {
+      showLoadingToast({ message: '图片生成中...', forbidClick: true })
+      setTimeout(() => {
+        imgs.saveLoading = false
+      }, 100)
+      const res = await promisefiyPostMessage({
+        api: 'savePicture',
+        content: {
+          base64: imgs.image
+        }
+      })
+      if (res?.content?.status === 'success') {
+        showSuccessToast('保存成功')
+      } else {
+        showFailToast('保存失败')
+      }
+    }
+
+    onMounted(() => {
+      getDetail()
     })
     return () => (
-      <div class={[styles.trainWeek, styles.trainMonth]}>
+      <div
+        class={[
+          styles.trainWeek,
+          styles.trainMonth,
+          forms.share == 1 ? styles.trasinMonthShare : ''
+        ]}
+      >
         <div class={styles.trainContainer}></div>
-        <OHeader background="transparent" border={false} title=" ">
+        <OHeader background="transparent" border={false} title=" " backIconColor="white">
           {{
-            right: () => <i class={styles.iconShare} onClick={() => (forms.showQrcode = true)}></i>
+            right: () =>
+              forms.share != 1 && (
+                <i class={styles.iconShare} onClick={() => (forms.showQrcode = true)}></i>
+              )
           }}
         </OHeader>
         <div class={[styles.headerContant, styles.teacherHeaderContant]}>
           <div class={styles.orchestra}>
             <Image src={iconOrchestra} class={styles.iconOrchestra} />
-            <span>武汉市洪山区实验小学第一届乐团</span>
+            <span>{reportData.orchestraName}</span>
+          </div>
+          <div>
+            <Image src={trainWeek} class={styles.iconTrainWeek} />
           </div>
-          <Image src={trainWeek} class={styles.iconTrainWeek} />
-          <div class={styles.trainTimer}>2023/1/16-2023/1/22</div>
+          <div class={styles.trainTimer}>{reportData.monthlyTime}</div>
         </div>
 
-        <OrchestraNum type="month" />
-        <TrainClass type="month" />
+        <OrchestraNum type="month" reportData={reportData.ORCHESTRA} />
+        <TrainClass type="month" reportData={reportData.COURSE_SCHEDULE} />
 
         <div class={[styles.trainPhoto, styles.teacherTrainPhoto]}>
           <Image src={iconPhoto} class={styles.iconPhoto} />
           <p>
-            本月上传<span>17</span>张训练照片
+            本月上传<span>{reportData.PHOTO.TOTAL || 0}</span>张训练照片
+          </p>
+          <p
+            onClick={() => {
+              if (forms.share == 1) return
+              router.push({
+                path: '/school-photo'
+              })
+            }}
+          >
+            本月上传<span>{reportData.PHOTO.TOTAL || 0}</span>张训练照片
+            {forms.share != 1 && <Icon name="arrow" />}
           </p>
         </div>
 
-        <StudentAttendance type="month" />
-        <TeacherAttendance type="month" />
+        <StudentAttendance type="month" reportData={reportData.STUDENT_ATTENDANCE} />
+        <TeacherAttendance type="month" reportData={reportData.TEACHER_ATTENDANCE} />
 
-        <div class={[styles.trainPhoto, styles.teacherTrainClass, styles.trainClass]}>
+        <div class={[styles.trainClass, styles.teacherTrainClass]}>
           <Image src={iconClass} class={styles.iconPhoto} />
           <div>
-            <p>课件使用未达标班级</p>
-            <p class={styles.subjectNames}>长笛班 单簧管班 萨克斯班 小号班 大号</p>
+            <p class={styles.subjectTips}>课件使用未达标班级</p>
+            <p class={styles.subjectNames}>
+              {reportData.coursewareList.map((item: string) => item + ' ')}
+            </p>
           </div>
         </div>
-        <TrainProgress type="month" />
+        <TrainProgress type="month" reportData={reportData.KNOWLEDGE} />
 
         <Popup
           v-model:show={forms.showQrcode}
@@ -78,10 +280,12 @@ export default defineComponent({
                 >
                   <div class={styles.orchestra}>
                     <Image src={iconOrchestra} class={styles.iconOrchestra} />
-                    <span>武汉市洪山区实验小学第一届乐团</span>
+                    <span>{reportData.orchestraName}</span>
+                  </div>
+                  <div>
+                    <Image src={trainWeek} class={styles.iconTrainWeek} />
                   </div>
-                  <Image src={trainWeek} class={styles.iconTrainWeek} />
-                  <div class={styles.trainTimer}>2023/1/16-2023/1/22</div>
+                  <div class={styles.trainTimer}>{reportData.monthlyTime}</div>
                 </div>
 
                 <div class={styles.codeQr}>
@@ -107,13 +311,13 @@ export default defineComponent({
                 <i></i>分享方式
               </h3>
               <Grid columnNum={2} border={false}>
-                <GridItem>
+                <GridItem onClick={onSaveImg}>
                   {{
                     icon: () => <Image class={styles.shareImg} src={iconSaveImage} />,
                     text: () => <div class={styles.shareText}>保存图片</div>
                   }}
                 </GridItem>
-                <GridItem>
+                <GridItem onClick={onShare}>
                   {{
                     icon: () => <Image class={styles.shareImg} src={iconWechat} />,
                     text: () => <div class={styles.shareText}>微信</div>

+ 63 - 9
src/school/train-report/report.module.less

@@ -22,7 +22,7 @@
     background-size: contain;
   }
 
-  &.trainWeek {
+  &.trainMonth {
     background: linear-gradient(180deg, #c7c7ff 0%, #9c9bff 100%);
 
     .trainContainer {
@@ -31,6 +31,22 @@
       height: 342px;
     }
   }
+
+  &.trasinMonthShare {
+    .trainContainer {
+      background: url('./images/month/share-month-banner.png') no-repeat top center;
+      background-size: contain;
+      height: 342px;
+    }
+  }
+
+  &.trasinWeekShare {
+    .trainContainer {
+      background: url('./images/week/share-week-banner.png') no-repeat top center;
+      background-size: contain;
+      height: 342px;
+    }
+  }
 }
 
 .headerContant {
@@ -71,6 +87,7 @@
   }
   .iconTrainWeek {
     height: 38px;
+    width: 156px;
     padding: 18px 0 8px;
   }
   .trainTimer {
@@ -109,18 +126,13 @@
   &.teacherTrainPhoto {
     background: url('./images/month/teacher-train-bg.png') no-repeat center;
     background-size: contain;
-  }
-
-  &.teacherTrainClass {
-    background: url('./images/month/teacher-train-bg1.png') no-repeat center;
-    background-size: contain;
-    height: 76px;
+    height: 60px;
   }
 
   .iconPhoto {
     width: 38px;
     height: 42px;
-    margin-right: 10px;
+    margin-right: 5px;
   }
   span {
     padding: 0 4px;
@@ -130,6 +142,44 @@
 }
 
 .trainClass {
+  position: relative;
+  font-size: 14px;
+  font-weight: 600;
+  color: #ffffff;
+  padding: 0 15px;
+  margin: 0 13px 10px;
+  display: flex;
+  align-items: center;
+  min-height: 76px;
+  background: rgba(66, 109, 255, 0.97) linear-gradient(276deg, #0a53d4 0%, #1133c0 100%);
+  box-shadow: inset 6px 6px 0px 0px rgba(15, 57, 204, 0.41);
+  border-radius: 16px 100px 100px 18px;
+  border: 3px solid #99ffdd;
+  overflow: hidden;
+  &::after {
+    position: absolute;
+    right: -40px;
+    top: -30px;
+    content: ' ';
+    background: url('./images/small-icon.png') no-repeat center;
+    background-size: cover;
+    height: 100%;
+    width: 63px;
+  }
+
+  .iconPhoto {
+    width: 38px;
+    height: 42px;
+    margin-right: 5px;
+  }
+  span {
+    padding: 0 4px;
+    color: #41ffe2;
+    font-family: DINA;
+  }
+  .subjectTips {
+    line-height: 20px;
+  }
   .subjectNames {
     padding-top: 2px;
     font-weight: 600;
@@ -138,12 +188,16 @@
     font-size: 14px;
   }
 }
+.teacherTrainClass {
+  background: rgba(92, 72, 239, 0.71);
+  box-shadow: inset 4px 4px 0px 0px #7765ef;
+  border: 3px solid #99ffdd;
+}
 
 .codeContainer {
   .codeImg {
     width: 315px;
     height: 440px;
-    // padding-left: 8px;
     background: url('./images/week/popup-week-banner.png') no-repeat top center;
     background-size: contain;
     margin: 0 auto;

+ 219 - 23
src/school/train-report/week-report.tsx

@@ -1,8 +1,19 @@
 import OHeader from '@/components/o-header'
-import { defineComponent, reactive } from 'vue'
+import { defineComponent, onMounted, reactive } from 'vue'
 import styles from './report.module.less'
 import iconOrchestra from '@/views/mine-orchestra/images/icon-or.png'
-import { Grid, GridItem, Icon, Image, Popup } from 'vant'
+import {
+  closeToast,
+  Grid,
+  GridItem,
+  Icon,
+  Image,
+  Popup,
+  showFailToast,
+  showLoadingToast,
+  showSuccessToast,
+  showToast
+} from 'vant'
 import trainWeek from './images/week/icon-train-week.png'
 import OrchestraNum from './modal/orchestra-num'
 import TrainClass from './modal/train-class'
@@ -14,52 +25,235 @@ import iconClass from './images/icon-class.png'
 import iconSaveImage from '@/school/orchestra/images/icon-save-image.png'
 import iconWechat from '@/school/orchestra/images/icon-wechat.png'
 import OQrcode from '@/components/o-qrcode'
+import { useRoute, useRouter } from 'vue-router'
+import request from '@/helpers/request'
+import { postMessage, promisefiyPostMessage } from '@/helpers/native-message'
+import html2canvas from 'html2canvas'
+
+export const reportCourseType = {
+  PERCUSSION: '打击乐',
+  FLUTE: '长笛',
+  SAX: '萨克斯',
+  CLARINET: '单簧管',
+  TRUMPET: '小号',
+  TROMBONE: '长号',
+  HORN: '圆号',
+  BARITONE_TUBA: '上低音号-大号',
+  EUPHONIUM: '上低音号',
+  TUBA: '大号',
+  MUSIC_THEORY: '乐理',
+  INSTRUMENTAL_ENSEMBLE: '合奏'
+}
 
 export default defineComponent({
   name: 'train-report',
   setup() {
+    const router = useRouter()
+    const route = useRoute()
     const forms = reactive({
+      id: route.query.id,
       showQrcode: false,
-      url: 'http://'
+      share: route.query.share as any,
+      url: window.location.href + '&share=1'
+    })
+    const reportData = reactive({
+      orchestraName: null,
+      startTime: null,
+      endTime: null,
+      COURSEWARE: {},
+      coursewareList: [] as any,
+      COURSE_SCHEDULE: {},
+      KNOWLEDGE: {},
+      ORCHESTRA: {},
+      PHOTO: {} as any,
+      STUDENT_ATTENDANCE: {},
+      TEACHER_ATTENDANCE: {}
     })
+
+    const getDetail = async () => {
+      try {
+        const { data } = await request.get('/api-school/open/orchestraReport/detail/' + forms.id)
+        reportData.COURSEWARE = data.reportItem.COURSEWARE || {}
+        reportData.COURSE_SCHEDULE = data.reportItem.COURSE_SCHEDULE || {}
+        reportData.KNOWLEDGE = data.reportItem.KNOWLEDGE || {}
+        reportData.ORCHESTRA = data.reportItem.ORCHESTRA || {}
+        reportData.PHOTO = data.reportItem.PHOTO || {}
+        reportData.STUDENT_ATTENDANCE = data.reportItem.STUDENT_ATTENDANCE || {}
+        reportData.TEACHER_ATTENDANCE = data.reportItem.TEACHER_ATTENDANCE || {}
+        reportData.orchestraName = data.orchestraName || ''
+        reportData.startTime = data.startTime || ''
+        reportData.endTime = data.endTime || ''
+
+        const courseware = reportData.COURSEWARE
+        for (const i in courseware) {
+          i != 'TOTAL' && reportData.coursewareList.push(reportCourseType[i])
+        }
+      } catch {
+        //
+      }
+    }
+
+    const imgs = reactive({
+      saveLoading: false,
+      image: null as any,
+      shareLoading: false
+    })
+    const onSaveImg = async () => {
+      // 判断是否在保存中...
+      if (imgs.saveLoading) {
+        return
+      }
+      imgs.saveLoading = true
+      // 判断是否已经生成图片
+      if (imgs.image) {
+        saveImg()
+      } else {
+        const container: any = document.getElementById(`preview-container`)
+        html2canvas(container, {
+          allowTaint: true,
+          useCORS: true,
+          backgroundColor: null
+        })
+          .then(async (canvas) => {
+            const url = canvas.toDataURL('image/png')
+            imgs.image = url
+            saveImg()
+          })
+          .catch(() => {
+            closeToast()
+            imgs.saveLoading = false
+          })
+      }
+    }
+    const onShare = () => {
+      if (imgs.shareLoading) {
+        return
+      }
+      imgs.shareLoading = true
+      if (imgs.image) {
+        openShare()
+      } else {
+        const container: any = document.getElementById(`preview-container`)
+        html2canvas(container, {
+          allowTaint: true,
+          useCORS: true,
+          backgroundColor: null
+        })
+          .then(async (canvas) => {
+            const url = canvas.toDataURL('image/png')
+            imgs.image = url
+            openShare()
+          })
+          .catch(() => {
+            closeToast()
+            imgs.shareLoading = false
+          })
+      }
+    }
+    const openShare = () => {
+      const image = imgs.image
+      setTimeout(() => {
+        imgs.shareLoading = false
+      }, 100)
+      if (image) {
+        postMessage(
+          {
+            api: 'shareTripartite',
+            content: {
+              title: '',
+              desc: '',
+              image,
+              video: '',
+              type: 'image',
+              // button: ['copy']
+              shareType: 'wechat'
+            }
+          },
+          (res: any) => {
+            if (res && res.content) {
+              showToast(res.content.message || (res.content.status ? '分享成功' : '分享失败'))
+            }
+          }
+        )
+      }
+    }
+    const saveImg = async () => {
+      showLoadingToast({ message: '图片生成中...', forbidClick: true })
+      setTimeout(() => {
+        imgs.saveLoading = false
+      }, 100)
+      const res = await promisefiyPostMessage({
+        api: 'savePicture',
+        content: {
+          base64: imgs.image
+        }
+      })
+      if (res?.content?.status === 'success') {
+        showSuccessToast('保存成功')
+      } else {
+        showFailToast('保存失败')
+      }
+    }
+
+    onMounted(() => {
+      getDetail()
+    })
+
     return () => (
-      <div class={styles.trainWeek}>
+      <div class={[styles.trainWeek, forms.share == 1 ? styles.trasinWeekShare : '']}>
         <div class={styles.trainContainer}></div>
-        <OHeader background="transparent" border={false} title=" ">
+        <OHeader background="transparent" border={false} title=" " backIconColor="white">
           {{
-            right: () => <i class={styles.iconShare} onClick={() => (forms.showQrcode = true)}></i>
+            right: () =>
+              forms.share != 1 && (
+                <i class={styles.iconShare} onClick={() => (forms.showQrcode = true)}></i>
+              )
           }}
         </OHeader>
         <div class={styles.headerContant}>
           <div class={styles.orchestra}>
             <Image src={iconOrchestra} class={styles.iconOrchestra} />
-            <span>武汉市洪山区实验小学第一届乐团</span>
+            <span>{reportData.orchestraName}</span>
+          </div>
+          <div>
+            <Image src={trainWeek} class={styles.iconTrainWeek} />
+          </div>
+          <div class={styles.trainTimer}>
+            {reportData.startTime}-{reportData.endTime}
           </div>
-          <Image src={trainWeek} class={styles.iconTrainWeek} />
-          <div class={styles.trainTimer}>2023/1/16-2023/1/22</div>
         </div>
 
-        <OrchestraNum />
-        <TrainClass />
+        <OrchestraNum reportData={reportData.ORCHESTRA} />
+        <TrainClass reportData={reportData.COURSE_SCHEDULE} />
 
         <div class={styles.trainPhoto}>
           <Image src={iconPhoto} class={styles.iconPhoto} />
-          <p>
-            本周上传<span>17</span>张训练照片
+          <p
+            onClick={() => {
+              if (forms.share == 1) return
+              router.push({
+                path: '/school-photo'
+              })
+            }}
+          >
+            本周上传<span>{reportData.PHOTO.TOTAL || 0}</span>张训练照片
+            {forms.share != 1 && <Icon name="arrow" />}
           </p>
         </div>
 
-        <StudentAttendance />
-        <TeacherAttendance />
+        <StudentAttendance reportData={reportData.STUDENT_ATTENDANCE} />
+        <TeacherAttendance reportData={reportData.TEACHER_ATTENDANCE} />
 
-        <div class={[styles.trainPhoto, styles.trainClass]}>
+        <div class={[styles.trainClass]}>
           <Image src={iconClass} class={styles.iconPhoto} />
           <div>
-            <p>课件使用未达标班级</p>
-            <p class={styles.subjectNames}>长笛班 单簧管班 萨克斯班 小号班 大号</p>
+            <p class={styles.subjectTips}>课件使用未达标班级</p>
+            <p class={styles.subjectNames}>
+              {reportData.coursewareList.map((item: string) => item + ' ')}
+            </p>
           </div>
         </div>
-        <TrainProgress />
+        <TrainProgress reportData={reportData.KNOWLEDGE} />
 
         <Popup
           v-model:show={forms.showQrcode}
@@ -72,10 +266,12 @@ export default defineComponent({
                 <div class={[styles.headerContant, styles.headerContantPopup]}>
                   <div class={styles.orchestra}>
                     <Image src={iconOrchestra} class={styles.iconOrchestra} />
-                    <span>武汉市洪山区实验小学第一届乐团</span>
+                    <span>{reportData.orchestraName}</span>
                   </div>
                   <Image src={trainWeek} class={styles.iconTrainWeek} />
-                  <div class={styles.trainTimer}>2023/1/16-2023/1/22</div>
+                  <div class={styles.trainTimer}>
+                    {reportData.startTime}-{reportData.endTime}
+                  </div>
                 </div>
 
                 <div class={styles.codeQr}>
@@ -101,13 +297,13 @@ export default defineComponent({
                 <i></i>分享方式
               </h3>
               <Grid columnNum={2} border={false}>
-                <GridItem>
+                <GridItem onClick={onSaveImg}>
                   {{
                     icon: () => <Image class={styles.shareImg} src={iconSaveImage} />,
                     text: () => <div class={styles.shareText}>保存图片</div>
                   }}
                 </GridItem>
-                <GridItem>
+                <GridItem onClick={onShare}>
                   {{
                     icon: () => <Image class={styles.shareImg} src={iconWechat} />,
                     text: () => <div class={styles.shareText}>微信</div>