information.tsx 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387
  1. import OSticky from '@/components/o-sticky'
  2. import {
  3. Button,
  4. closeToast,
  5. DatePicker,
  6. Grid,
  7. GridItem,
  8. Icon,
  9. Image,
  10. List,
  11. Picker,
  12. Popover,
  13. Popup,
  14. showFailToast,
  15. showLoadingToast,
  16. showSuccessToast,
  17. showToast
  18. } from 'vant'
  19. import { computed, defineComponent, nextTick, onMounted, reactive } from 'vue'
  20. import styles from './information.module.less'
  21. import iconSaveImage from '../images/icon-save-image.png'
  22. import iconWechat from '../images/icon-wechat.png'
  23. import OQrcode from '@/components/o-qrcode'
  24. import request from '@/helpers/request'
  25. import { useRoute, useRouter } from 'vue-router'
  26. import { CountUp } from 'countup.js'
  27. import OEmpty from '@/components/o-empty'
  28. import dayjs from 'dayjs'
  29. import isSameOrBefore from 'dayjs/plugin/isSameOrBefore'
  30. import isSameOrAfter from 'dayjs/plugin/isSameOrAfter'
  31. dayjs.extend(isSameOrBefore, isSameOrAfter)
  32. import { promisefiyPostMessage, postMessage } from '@/helpers/native-message'
  33. import html2canvas from 'html2canvas'
  34. export default defineComponent({
  35. name: 'detail-information',
  36. props: {
  37. termTimes: {
  38. type: Object,
  39. default: {}
  40. }
  41. },
  42. setup(props) {
  43. const startTime = computed(() => props.termTimes.start)
  44. const endTime = computed(() => props.termTimes.end)
  45. const route = useRoute()
  46. const router = useRouter()
  47. const state = reactive({
  48. timeShow: false,
  49. currentData: [dayjs().year() + ''],
  50. actionText: '上学期',
  51. actionType: 'up',
  52. actionTerm: [
  53. { text: '上学期', color: 'var(--van-primary-color)', value: 'up' },
  54. { text: '下学期', value: 'down' }
  55. ],
  56. oPopover: false,
  57. check: [],
  58. checkboxRefs: [] as any,
  59. isLoading: false,
  60. list: [] as any,
  61. listState: {
  62. dataShow: true, // 判断是否有数据
  63. loading: false,
  64. finished: false
  65. },
  66. params: {
  67. startTime: dayjs(dayjs().year() + startTime.value).format('YYYY-MM-DD HH:mm:ss'),
  68. endTime: dayjs(dayjs().year() + endTime.value)
  69. .add(1, 'year')
  70. .subtract(1, 'day')
  71. .format('YYYY-MM-DD HH:mm:ss'),
  72. page: 1,
  73. rows: 20
  74. },
  75. statistics: {} as any,
  76. orchestraInfo: {} as any // 乐团详情
  77. })
  78. // 选择学期
  79. const onSelect = (val: any) => {
  80. state.actionTerm.forEach((item: any) => {
  81. item.color = null
  82. })
  83. val.color = 'var(--van-primary-color)'
  84. state.actionText = val.text
  85. state.actionType = val.value
  86. if (val.value === 'up') {
  87. state.params.startTime = dayjs(Number(state.currentData[0]) + startTime.value).format(
  88. 'YYYY-MM-DD HH:mm:ss'
  89. )
  90. state.params.endTime = dayjs(Number(state.currentData[0]) + endTime.value)
  91. .add(1, 'year')
  92. .subtract(1, 'day')
  93. .format('YYYY-MM-DD HH:mm:ss')
  94. } else if (val.value === 'down') {
  95. state.params.startTime = dayjs(Number(state.currentData[0]) + endTime.value)
  96. .add(1, 'year')
  97. .format('YYYY-MM-DD HH:mm:ss')
  98. state.params.endTime = dayjs(Number(state.currentData[0]) + startTime.value)
  99. .add(1, 'year')
  100. .subtract(1, 'day')
  101. .format('YYYY-MM-DD HH:mm:ss')
  102. }
  103. onSearch()
  104. }
  105. const onConfirmDate = (date: any) => {
  106. state.currentData = date.selectedValues
  107. const year = Number(state.currentData[0]) + 1
  108. if (state.actionType === 'up') {
  109. state.params.startTime = dayjs(year + startTime.value).format('YYYY-MM-DD HH:mm:ss')
  110. state.params.endTime = dayjs(year + endTime.value)
  111. .add(1, 'year')
  112. .subtract(1, 'day')
  113. .format('YYYY-MM-DD HH:mm:ss')
  114. } else if (state.actionType === 'down') {
  115. state.params.startTime = dayjs(year + endTime.value).format('YYYY-MM-DD HH:mm:ss')
  116. state.params.endTime = dayjs(year + startTime.value)
  117. .subtract(1, 'day')
  118. .format('YYYY-MM-DD HH:mm:ss')
  119. }
  120. state.timeShow = false
  121. onSearch()
  122. }
  123. const getDetails = async () => {
  124. try {
  125. const { data } = await request.get('/api-school/orchestra/detail/' + route.query.id)
  126. state.orchestraInfo = data || {}
  127. } catch {
  128. //
  129. }
  130. }
  131. const getStatistics = async () => {
  132. try {
  133. const { data } = await request.post('/api-school/school/schoolSummaryStat', {
  134. data: {
  135. orchestraId: route.query.id
  136. }
  137. })
  138. state.statistics = data || {}
  139. initNumCountUp()
  140. } catch {
  141. //
  142. }
  143. }
  144. // 班级列表
  145. const getList = async () => {
  146. try {
  147. if (state.isLoading) return
  148. state.isLoading = true
  149. const res = await request.post('/api-school/classGroup/page', {
  150. data: {
  151. ...state.params,
  152. orchestraId: route.query.id
  153. }
  154. })
  155. state.listState.loading = false
  156. const result = res.data || {}
  157. // 处理重复请求数据
  158. if (state.list.length > 0 && result.current === 1) {
  159. return
  160. }
  161. const rows = result.rows || []
  162. state.list = state.list.concat(rows)
  163. state.listState.finished = result.current >= result.pages
  164. state.params.page = result.current + 1
  165. state.listState.dataShow = state.list.length > 0
  166. state.isLoading = false
  167. } catch {
  168. state.listState.dataShow = false
  169. state.listState.finished = true
  170. state.isLoading = false
  171. }
  172. }
  173. const onSearch = () => {
  174. state.params.page = 1
  175. state.list = []
  176. state.listState.dataShow = true // 判断是否有数据
  177. state.listState.loading = false
  178. state.listState.finished = false
  179. getList()
  180. }
  181. const initNumCountUp = () => {
  182. nextTick(() => {
  183. // 在读学生
  184. const statistics = state.statistics
  185. new CountUp('currentStudentNum', statistics.currentStudent || 0).start()
  186. new CountUp('time1', statistics.attendanceRate * 100 || 0).start()
  187. new CountUp('time2', statistics.homeworkSubmissionRate * 100 || 0).start()
  188. new CountUp('time3', statistics.practicePassRate * 100 || 0).start()
  189. })
  190. }
  191. onMounted(async () => {
  192. const sysStartTime = dayjs(dayjs().year() + startTime.value).format('YYYY-MM-DD')
  193. const sysEndTime = dayjs(dayjs().year() + endTime.value).format('YYYY-MM-DD')
  194. const nowTime = dayjs().format('YYYY-MM-DD')
  195. console.log(nowTime, sysStartTime)
  196. const before = dayjs(nowTime).isBefore(dayjs(sysStartTime))
  197. const after = dayjs(nowTime).isBefore(dayjs(sysEndTime))
  198. const year = dayjs().year()
  199. // console.log(before, after, year)
  200. if (before && after) {
  201. state.currentData = [year - 1 + '']
  202. state.params.startTime = dayjs(year - 1 + startTime.value).format('YYYY-MM-DD HH:mm:ss')
  203. state.params.endTime = dayjs(dayjs().year() + endTime.value)
  204. .subtract(1, 'day')
  205. .format('YYYY-MM-DD HH:mm:ss')
  206. // 上学期
  207. }
  208. if (!before && !after) {
  209. state.params.startTime = dayjs(dayjs().year() + startTime.value).format(
  210. 'YYYY-MM-DD HH:mm:ss'
  211. )
  212. state.params.endTime = dayjs(dayjs().year() + endTime.value)
  213. .add(1, 'year')
  214. .subtract(1, 'day')
  215. .format('YYYY-MM-DD HH:mm:ss')
  216. // 下一年的上学期
  217. }
  218. if (before && !after) {
  219. state.params.startTime = dayjs(year + endTime.value).format('YYYY-MM-DD HH:mm:ss')
  220. state.params.endTime = dayjs(year + startTime.value)
  221. .subtract(1, 'day')
  222. .format('YYYY-MM-DD HH:mm:ss')
  223. // 下学期
  224. state.actionTerm.forEach((item: any) => {
  225. if (item.value === 'down') {
  226. item.color = 'var(--van-primary-color)'
  227. state.actionText = item.text
  228. state.actionType = item.value
  229. } else {
  230. item.color = ''
  231. }
  232. })
  233. state.currentData = [year - 1 + '']
  234. }
  235. await getDetails()
  236. await getStatistics()
  237. await getList()
  238. })
  239. return () => (
  240. <>
  241. <div style={{ padding: '12px 13px 16px', background: '#F8F8F8' }}>
  242. <div class={styles.searchBand} onClick={() => (state.timeShow = true)}>
  243. {state.currentData[0]}年 <Icon name={state.timeShow ? 'arrow-up' : 'arrow-down'} />
  244. </div>
  245. <Popover
  246. v-model:show={state.oPopover}
  247. actions={state.actionTerm}
  248. showArrow={false}
  249. placement="bottom"
  250. offset={[0, 12]}
  251. style={{ zIndex: '9999' }}
  252. onSelect={onSelect}
  253. >
  254. {{
  255. reference: () => (
  256. <div class={styles.searchBand} style="margin-left: 16px">
  257. {state.actionText} <Icon name={state.oPopover ? 'arrow-up' : 'arrow-down'} />
  258. </div>
  259. )
  260. }}
  261. </Popover>
  262. </div>
  263. <Grid border={false} class={styles.gridContainer}>
  264. <GridItem>
  265. <p class={[styles.title, styles.red]}>
  266. <span id="currentStudentNum">{state.statistics.studentNum || 0}</span>
  267. <i> 名</i>
  268. </p>
  269. <p class={styles.name}>在读学生</p>
  270. </GridItem>
  271. <GridItem>
  272. <p class={[styles.title, styles.red]}>
  273. <span id="time1">{state.statistics.attendanceRate || 0}</span>%
  274. </p>
  275. <p class={styles.name}>到课率</p>
  276. </GridItem>
  277. <GridItem>
  278. <p class={[styles.title, styles.red]}>
  279. <span id="time2">{state.statistics.homeworkRate || 0}</span>%
  280. </p>
  281. <p class={styles.name}>作业提交率</p>
  282. </GridItem>
  283. <GridItem>
  284. <p class={[styles.title, styles.red]}>
  285. <span id="time3">{state.statistics.homeworkQualifiedRate || 0}</span>%
  286. </p>
  287. <p class={styles.name}>练习合格率</p>
  288. </GridItem>
  289. </Grid>
  290. {state.listState.dataShow ? (
  291. <List
  292. // v-model:loading={state.listState.loading}
  293. finished={state.listState.finished}
  294. finishedText=" "
  295. class={[styles.liveList]}
  296. onLoad={getList}
  297. immediateCheck={false}
  298. >
  299. {state.list.map((item: any) => (
  300. <div class={[styles.gridContainer, styles.gridClass]}>
  301. <div class={styles.className}>
  302. <i class={styles.line}></i>
  303. {item.name}
  304. </div>
  305. <Grid border={false} columnNum={3}>
  306. <GridItem>
  307. <p class={styles.title}>{item.preStudentNum || 0}</p>
  308. <p class={styles.name}>在读学生</p>
  309. </GridItem>
  310. <GridItem>
  311. <p class={[styles.title, styles.teacher, 'van-ellipsis']}>
  312. {item.teacherName || '-'}
  313. </p>
  314. <p class={styles.name}>伴学指导</p>
  315. </GridItem>
  316. <GridItem>
  317. <p class={styles.title}>
  318. {item.completeCourseScheduleNum || 0}/{item.courseScheduleNum || 0}
  319. </p>
  320. <p class={styles.name}>课时</p>
  321. </GridItem>
  322. </Grid>
  323. </div>
  324. ))}
  325. </List>
  326. ) : (
  327. <OEmpty btnStatus={false} tips="暂无班级" />
  328. )}
  329. {/* */}
  330. {state.orchestraInfo.canSignUp && (
  331. <OSticky position="bottom">
  332. <div class={'btnGroup'}>
  333. <Button
  334. round
  335. block
  336. type="primary"
  337. onClick={() => {
  338. router.push({
  339. path: 'save-share-image',
  340. query: {
  341. type: 'orchestra',
  342. id: route.query.id
  343. }
  344. })
  345. }}
  346. >
  347. 报名二维码
  348. </Button>
  349. </div>
  350. </OSticky>
  351. )}
  352. <Popup v-model:show={state.timeShow} position="bottom" round class={'popupBottomSearch'}>
  353. <DatePicker
  354. v-model={state.currentData}
  355. columnsType={['year']}
  356. onConfirm={onConfirmDate}
  357. onCancel={() => (state.timeShow = false)}
  358. />
  359. </Popup>
  360. </>
  361. )
  362. }
  363. })