month-report.tsx 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392
  1. import OHeader from '@/components/o-header'
  2. import { defineComponent, nextTick, onMounted, reactive } from 'vue'
  3. import styles from './report.module.less'
  4. import iconOrchestra from '@/views/mine-orchestra/images/icon-or.png'
  5. import {
  6. closeToast,
  7. Grid,
  8. GridItem,
  9. Icon,
  10. Image,
  11. Popup,
  12. showFailToast,
  13. showLoadingToast,
  14. showSuccessToast,
  15. showToast
  16. } from 'vant'
  17. import trainWeek from './images/month/icon-train-month.png'
  18. import OrchestraNum from './modal/orchestra-num'
  19. import TrainClass from './modal/train-class'
  20. import StudentAttendance from './modal/student-attendance'
  21. import TeacherAttendance from './modal/teacher-attendance'
  22. import TrainProgress from './modal/train-progress'
  23. import iconPhoto from './images/icon-photo.png'
  24. import iconClass from './images/icon-class.png'
  25. import iconSaveImage from '@/school/orchestra/images/icon-save-image.png'
  26. import iconWechat from '@/school/orchestra/images/icon-wechat.png'
  27. import iconWeekPoint from './images/month/teacher-icon-point.png'
  28. import popupWeekBanner from './images/month/popup-week-banner.png'
  29. import popupQrcodeBg from './images/popup-qrcode-bg.png'
  30. import OQrcode from '@/components/o-qrcode'
  31. import request from '@/helpers/request'
  32. import { useRoute, useRouter } from 'vue-router'
  33. import { postMessage, promisefiyPostMessage } from '@/helpers/native-message'
  34. import html2canvas from 'html2canvas'
  35. import iconArrow from './images/icon-arrow.png'
  36. import { useRect } from '@vant/use'
  37. import TrainOverWork from './modal/train-over-work'
  38. export const reportCourseType = {
  39. PERCUSSION: '打击乐',
  40. FLUTE: '长笛',
  41. SAX: '萨克斯',
  42. CLARINET: '单簧管',
  43. TRUMPET: '小号',
  44. TROMBONE: '长号',
  45. HORN: '圆号',
  46. BARITONE_TUBA: '上低音号-大号',
  47. EUPHONIUM: '上低音号',
  48. TUBA: '大号',
  49. MUSIC_THEORY: '乐理',
  50. INSTRUMENTAL_ENSEMBLE: '合奏'
  51. }
  52. export default defineComponent({
  53. name: 'train-report',
  54. setup() {
  55. const router = useRouter()
  56. const route = useRoute()
  57. const forms = reactive({
  58. id: route.query.id,
  59. share: route.query.share as any,
  60. showQrcode: false,
  61. url: window.location.href + '&share=1',
  62. width: 0,
  63. height: 0
  64. })
  65. const reportData = reactive({
  66. orchestraName: null,
  67. monthlyTime: null,
  68. startTime: null,
  69. endTime: null,
  70. COURSEWARE: {},
  71. coursewareList: [] as any,
  72. COURSE_SCHEDULE: {},
  73. KNOWLEDGE: {},
  74. ORCHESTRA: {},
  75. PHOTO: {} as any,
  76. STUDENT_ATTENDANCE: {},
  77. TEACHER_ATTENDANCE: {},
  78. HOMEWORK_TARGET: {} as any, // 作业完成率
  79. HOMEWORK_SUBMITTED: {} as any, // 作业提交率
  80. TEACHER_NOT_ATTENDANCE: {} as any
  81. })
  82. const getDetail = async () => {
  83. try {
  84. const { data } = await request.get('/api-school/open/orchestraReport/detail/' + forms.id)
  85. reportData.COURSEWARE = data.reportItem.COURSEWARE || {}
  86. reportData.COURSE_SCHEDULE = data.reportItem.COURSE_SCHEDULE || {}
  87. reportData.KNOWLEDGE = data.reportItem.KNOWLEDGE || {}
  88. reportData.ORCHESTRA = data.reportItem.ORCHESTRA || {}
  89. reportData.HOMEWORK_TARGET = data.reportItem.HOMEWORK_TARGET || {}
  90. reportData.HOMEWORK_SUBMITTED = data.reportItem.HOMEWORK_SUBMITTED || {}
  91. reportData.PHOTO = data.reportItem.PHOTO || {}
  92. reportData.STUDENT_ATTENDANCE = data.reportItem.STUDENT_ATTENDANCE || {}
  93. reportData.TEACHER_ATTENDANCE = data.reportItem.TEACHER_ATTENDANCE || {}
  94. reportData.TEACHER_NOT_ATTENDANCE = data.reportItem.TEACHER_NOT_ATTENDANCE || {}
  95. reportData.orchestraName = data.orchestraName || ''
  96. reportData.monthlyTime = data.monthlyTime || ''
  97. reportData.startTime = data.startTime || ''
  98. reportData.endTime = data.endTime || ''
  99. const courseware = data.notTargetClassList
  100. for (const i in courseware) {
  101. reportData.coursewareList.push(courseware[i].classGroupName)
  102. }
  103. } catch {
  104. //
  105. }
  106. }
  107. const imgs = reactive({
  108. saveLoading: false,
  109. image: null as any,
  110. shareLoading: false
  111. })
  112. const onSaveImg = async () => {
  113. // 判断是否在保存中...
  114. if (imgs.saveLoading) {
  115. return
  116. }
  117. imgs.saveLoading = true
  118. // 判断是否已经生成图片
  119. if (imgs.image) {
  120. saveImg()
  121. } else {
  122. const container: any = document.getElementById(`preview-container`)
  123. html2canvas(container, {
  124. allowTaint: true,
  125. useCORS: true,
  126. backgroundColor: null
  127. })
  128. .then(async (canvas) => {
  129. const url = canvas.toDataURL('image/png')
  130. imgs.image = url
  131. saveImg()
  132. })
  133. .catch(() => {
  134. closeToast()
  135. imgs.saveLoading = false
  136. })
  137. }
  138. }
  139. const onShare = () => {
  140. if (imgs.shareLoading) {
  141. return
  142. }
  143. imgs.shareLoading = true
  144. if (imgs.image) {
  145. openShare()
  146. } else {
  147. const container: any = document.getElementById(`preview-container`)
  148. html2canvas(container, {
  149. allowTaint: true,
  150. useCORS: true,
  151. backgroundColor: null
  152. })
  153. .then(async (canvas) => {
  154. const url = canvas.toDataURL('image/png')
  155. imgs.image = url
  156. openShare()
  157. })
  158. .catch(() => {
  159. closeToast()
  160. imgs.shareLoading = false
  161. })
  162. }
  163. }
  164. const openShare = () => {
  165. const image = imgs.image
  166. setTimeout(() => {
  167. imgs.shareLoading = false
  168. }, 100)
  169. if (image) {
  170. postMessage(
  171. {
  172. api: 'shareTripartite',
  173. content: {
  174. title: '',
  175. desc: '',
  176. image,
  177. video: '',
  178. type: 'image',
  179. // button: ['copy']
  180. shareType: 'wechat'
  181. }
  182. },
  183. (res: any) => {
  184. if (res && res.content) {
  185. showToast(res.content.message || (res.content.status ? '分享成功' : '分享失败'))
  186. }
  187. }
  188. )
  189. }
  190. }
  191. const saveImg = async () => {
  192. showLoadingToast({ message: '图片生成中...', forbidClick: true })
  193. setTimeout(() => {
  194. imgs.saveLoading = false
  195. }, 100)
  196. const res = await promisefiyPostMessage({
  197. api: 'savePicture',
  198. content: {
  199. base64: imgs.image
  200. }
  201. })
  202. if (res?.content?.status === 'success') {
  203. showSuccessToast('已保存到相册')
  204. } else {
  205. showFailToast('保存失败')
  206. }
  207. }
  208. onMounted(() => {
  209. getDetail()
  210. })
  211. return () => (
  212. <div
  213. class={[
  214. styles.trainWeek,
  215. styles.trainMonth,
  216. forms.share == 1 ? styles.trasinMonthShare : ''
  217. ]}
  218. >
  219. <div class={styles.trainContainer}></div>
  220. <OHeader
  221. background="transparent"
  222. border={false}
  223. title=" "
  224. color="white"
  225. isBack={forms.share != 1 ? true : false}
  226. >
  227. {{
  228. right: () =>
  229. forms.share != 1 && (
  230. <i
  231. class={styles.iconShare}
  232. onClick={() => {
  233. forms.showQrcode = true
  234. nextTick(() => {
  235. const previewContainer = document.querySelector(
  236. '#preview-container'
  237. ) as HTMLElement
  238. const share = useRect(previewContainer)
  239. forms.width = share.width
  240. forms.height = share.height
  241. if (share.width > 0 && share.height > 0) {
  242. previewContainer.style.width = Math.round(share.width) + 'px'
  243. previewContainer.style.height = Math.round(share.height) + 'px'
  244. }
  245. })
  246. }}
  247. ></i>
  248. )
  249. }}
  250. </OHeader>
  251. <div class={[styles.headerContant, styles.teacherHeaderContant]}>
  252. <div class={styles.orchestra}>
  253. <Image src={iconOrchestra} class={styles.iconOrchestra} />
  254. <span>{reportData.orchestraName}</span>
  255. </div>
  256. <div>
  257. <Image src={trainWeek} class={styles.iconTrainWeek} />
  258. </div>
  259. <div class={styles.trainTimer}>{reportData.monthlyTime}</div>
  260. </div>
  261. <OrchestraNum type="month" reportData={reportData.ORCHESTRA} />
  262. <TrainClass type="month" reportData={reportData.COURSE_SCHEDULE} />
  263. {/* {reportData.HOMEWORK_TARGET.total && ( */}
  264. <TrainOverWork
  265. type="month"
  266. reportData={{
  267. HOMEWORK_SUBMITTED: reportData.HOMEWORK_SUBMITTED,
  268. HOMEWORK_TARGET: reportData.HOMEWORK_TARGET
  269. }}
  270. />
  271. {/* )} */}
  272. <div class={[styles.trainPhoto, styles.teacherTrainPhoto]}>
  273. <Image src={iconPhoto} class={styles.iconPhoto} />
  274. <p
  275. onClick={() => {
  276. if (forms.share == 1) return
  277. router.push({
  278. path: '/school-photo'
  279. })
  280. }}
  281. style={{
  282. display: 'flex',
  283. alignItems: 'center'
  284. }}
  285. >
  286. 本月上传<span>{reportData.PHOTO.TOTAL || 0}</span>张训练照片
  287. {forms.share != 1 && <Icon name={iconArrow} size="10" style={{ marginLeft: '10px' }} />}
  288. </p>
  289. </div>
  290. <StudentAttendance type="month" reportData={reportData.STUDENT_ATTENDANCE} />
  291. <TeacherAttendance
  292. type="month"
  293. reportData={reportData.TEACHER_ATTENDANCE}
  294. reportDataNot={reportData.TEACHER_NOT_ATTENDANCE}
  295. />
  296. <div class={[styles.trainClass, styles.teacherTrainClass]}>
  297. <Image src={iconClass} class={styles.iconPhoto} />
  298. <div>
  299. <p class={styles.subjectTips}>课件使用未达标班级</p>
  300. <p class={styles.subjectNames} style="color: #FF99A2">
  301. {reportData.coursewareList.map((item: string) => item + ' ')}
  302. </p>
  303. </div>
  304. </div>
  305. <TrainProgress type="month" reportData={reportData.KNOWLEDGE} />
  306. <Popup
  307. v-model:show={forms.showQrcode}
  308. position="bottom"
  309. style={{ background: 'transparent' }}
  310. >
  311. <div class={styles.codeContainer}>
  312. <div class={[styles.codeImg, styles.teacherCodeImg]} id="preview-container">
  313. <Image src={popupWeekBanner} class={styles.popupWeekBanner} />
  314. <div class={styles.codeContent}>
  315. <div
  316. class={[
  317. styles.headerContant,
  318. styles.teacherHeaderContant,
  319. styles.headerContantPopup
  320. ]}
  321. >
  322. <div class={styles.orchestra}>
  323. <Image src={iconOrchestra} class={styles.iconOrchestra} />
  324. <span>{reportData.orchestraName}</span>
  325. </div>
  326. <div>
  327. <Image src={trainWeek} class={styles.iconTrainWeek} />
  328. </div>
  329. <div class={styles.trainTimer}>
  330. <Image class={styles.point} src={iconWeekPoint} />
  331. {reportData.monthlyTime}
  332. <Image class={styles.point} src={iconWeekPoint} />
  333. </div>
  334. </div>
  335. <div class={styles.codeQr}>
  336. <Image src={popupQrcodeBg} class={styles.popupQrcodeBg} />
  337. <OQrcode text={forms.url} size={'100%'} logoSize="small" />
  338. </div>
  339. <div style={{ textAlign: 'center' }}>
  340. <span class={styles.codeBtnText}>
  341. 扫描上方二维码<span>查看训练月报</span>
  342. </span>
  343. </div>
  344. </div>
  345. </div>
  346. <div class={styles.codeBottom}>
  347. <Icon
  348. name="cross"
  349. size={22}
  350. class={styles.close}
  351. color="#666"
  352. onClick={() => (forms.showQrcode = false)}
  353. />
  354. <h3 class={styles.title}>
  355. <i></i>分享方式
  356. </h3>
  357. <Grid columnNum={2} border={false}>
  358. <GridItem onClick={onSaveImg}>
  359. {{
  360. icon: () => <Image class={styles.shareImg} src={iconSaveImage} />,
  361. text: () => <div class={styles.shareText}>保存图片</div>
  362. }}
  363. </GridItem>
  364. <GridItem onClick={onShare}>
  365. {{
  366. icon: () => <Image class={styles.shareImg} src={iconWechat} />,
  367. text: () => <div class={styles.shareText}>微信</div>
  368. }}
  369. </GridItem>
  370. </Grid>
  371. </div>
  372. </div>
  373. </Popup>
  374. </div>
  375. )
  376. }
  377. })