train-detail.tsx 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405
  1. import { defineComponent, onMounted, reactive } from 'vue';
  2. import styles from './train-detail.module.less';
  3. import MSticky from '@/components/m-sticky';
  4. import MHeader from '@/components/m-header';
  5. import {
  6. Cell,
  7. CellGroup,
  8. Col,
  9. DatePicker,
  10. Grid,
  11. GridItem,
  12. Icon,
  13. List,
  14. Popup,
  15. Row,
  16. showToast
  17. } from 'vant';
  18. import icon_student from '@common/images/icon-student-default.png';
  19. import arrowIcon from './images/arrow_down.png';
  20. import score1 from './images/1.png';
  21. import score2 from './images/2.png';
  22. import score3 from './images/3.png';
  23. import { useRoute } from 'vue-router';
  24. import { formatterDatePicker } from '@/helpers/utils';
  25. import dayjs from 'dayjs';
  26. import MFullRefresh from '@/components/m-full-refresh';
  27. import request from '@/helpers/request';
  28. import MEmpty from '@/components/m-empty';
  29. import SkeletionDetailModal from './skeletion-detail-modal';
  30. import { postMessage } from '@/helpers/native-message';
  31. export default defineComponent({
  32. name: 'train-detail',
  33. setup() {
  34. const route = useRoute();
  35. const state = reactive({
  36. headerStatus: true,
  37. show: true,
  38. avatar: route.query.avatar as any,
  39. username: route.query.username as any,
  40. musicGroupName: route.query.musicGroupName as any,
  41. visitFlag: Number(route.query.visitFlag) || 0,
  42. dataForm: {
  43. // 时间下拉框
  44. type: null as any,
  45. status: false,
  46. minDate: new Date(2000, 0, 1),
  47. maxDate: new Date(2030, 11, 31),
  48. currentDate: [
  49. dayjs().format('YYYY'),
  50. dayjs().format('MM'),
  51. dayjs().format('DD')
  52. ] as any
  53. },
  54. dataList: [],
  55. loading: false,
  56. finished: false
  57. });
  58. const forms = reactive({
  59. isClick: false,
  60. listState: {
  61. dataShow: true, // 判断是否有数据
  62. loading: true,
  63. finished: false,
  64. refreshing: false
  65. },
  66. params: {
  67. startTime: null as any,
  68. endTime: null as any,
  69. userId: route.query.userId as any,
  70. page: 1,
  71. rows: 20
  72. },
  73. list: []
  74. });
  75. const onChangeDate = (type: string) => {
  76. if (type == 'showEnd' && forms.params.startTime) {
  77. state.dataForm.minDate = new Date(
  78. dayjs(forms.params.startTime).valueOf()
  79. );
  80. state.dataForm.maxDate = new Date(2030, 11, 31);
  81. setTimeout(() => {
  82. state.dataForm.currentDate = forms.params.endTime
  83. ? new Date(dayjs(forms.params.endTime).valueOf())
  84. : new Date();
  85. }, 500);
  86. } else if (type == 'showStart' && forms.params.endTime) {
  87. state.dataForm.minDate = new Date(2000, 0, 1);
  88. state.dataForm.maxDate = new Date(
  89. dayjs(forms.params.endTime).valueOf()
  90. );
  91. setTimeout(() => {
  92. state.dataForm.currentDate = forms.params.startTime
  93. ? new Date(dayjs(forms.params.startTime).valueOf())
  94. : new Date();
  95. }, 500);
  96. }
  97. state.dataForm.status = true;
  98. state.dataForm.type = type;
  99. };
  100. const getList = async () => {
  101. try {
  102. if (forms.isClick) return;
  103. forms.isClick = true;
  104. const { data } = await request.get(
  105. '/api-web/schoolCloudStudy/queryMusicCompareRecord',
  106. {
  107. params: {
  108. ...forms.params
  109. }
  110. }
  111. );
  112. const result = data || {};
  113. // 判断是否有数据
  114. if (forms.listState.refreshing) {
  115. forms.list = result.rows || [];
  116. } else {
  117. forms.list = forms.list.concat(result.rows || []);
  118. }
  119. forms.listState.finished = result.pageNo >= result.totalPage;
  120. forms.params.page = result.pageNo + 1;
  121. } catch {
  122. forms.listState.finished = true;
  123. } finally {
  124. setTimeout(() => {
  125. forms.listState.dataShow = forms.list.length > 0;
  126. forms.listState.refreshing = false;
  127. forms.listState.loading = false;
  128. forms.isClick = false;
  129. }, 500);
  130. }
  131. };
  132. const getLevelByScore = (score: number) => {
  133. let level = 3;
  134. if (score > 60 && score <= 80) {
  135. level = 2;
  136. } else if (score > 80) {
  137. level = 1;
  138. }
  139. return level;
  140. };
  141. const onRefresh = () => {
  142. forms.params.page = 1;
  143. getList();
  144. };
  145. const onResetList = () => {
  146. forms.list = [];
  147. forms.params.page = 1;
  148. forms.listState.dataShow = true;
  149. forms.listState.loading = true;
  150. forms.listState.finished = false;
  151. forms.listState.refreshing = true;
  152. onRefresh();
  153. };
  154. const showDetail = (item: any) => {
  155. if (item.notesDataIndex > 0) {
  156. postMessage({
  157. api: 'openWebView',
  158. content: {
  159. url:
  160. location.origin + `/accompany-web/?school=1/#/report/${item.id}`,
  161. orientation: 1,
  162. isHideTitle: true,
  163. statusBarTextColor: false,
  164. isOpenLight: true
  165. }
  166. });
  167. } else {
  168. showToast('本次评测未生成报告');
  169. }
  170. };
  171. onMounted(() => {
  172. getList();
  173. });
  174. return () => (
  175. <div class={styles.trainDetail}>
  176. <MSticky position="top">
  177. <MHeader />
  178. <div style={{ backgroundColor: '#f5f5f5', overflow: 'hidden' }}>
  179. <div class={styles.evaluation}>
  180. <Cell
  181. style="padding: 16px 12px; background-color: transparent"
  182. center>
  183. {{
  184. title: () => (
  185. <div class={styles.teacher_info}>
  186. <img
  187. class={styles.logo}
  188. src={state.avatar || icon_student}
  189. alt=""
  190. />
  191. <div class={styles.teacher_content}>
  192. <p class={styles.username}>{state.username}</p>
  193. <p class={styles.musicGroupName}>
  194. {state.musicGroupName}
  195. </p>
  196. </div>
  197. </div>
  198. )
  199. }}
  200. </Cell>
  201. </div>
  202. {!state.visitFlag && (
  203. <Row class={styles.dataSearch} align="center" justify="center">
  204. <Col span={9} offset={1}>
  205. <Cell isLink onClick={() => onChangeDate('showStart')}>
  206. {{
  207. 'right-icon': () => (
  208. <Icon
  209. name={arrowIcon}
  210. class={styles['search-icon']}
  211. size={10}
  212. />
  213. ),
  214. title: () =>
  215. forms.params.startTime ? (
  216. forms.params.startTime
  217. ) : (
  218. <span style={{ color: '#adadad' }}>开始日期</span>
  219. )
  220. }}
  221. </Cell>
  222. </Col>
  223. <Col span={1} style={{ 'text-align': 'center' }}>
  224. -
  225. </Col>
  226. <Col span={9}>
  227. <Cell onClick={() => onChangeDate('showEnd')}>
  228. {{
  229. 'right-icon': () => (
  230. <Icon
  231. name={arrowIcon}
  232. class={styles['search-icon']}
  233. size={10}
  234. />
  235. ),
  236. title: () =>
  237. forms.params.endTime ? (
  238. forms.params.endTime
  239. ) : (
  240. <span style={{ color: '#adadad' }}>结束日期</span>
  241. )
  242. }}
  243. </Cell>
  244. </Col>
  245. <Col span={4}>
  246. {/* onSearch */}
  247. <span
  248. class={styles['btn-search']}
  249. onClick={() => onResetList()}>
  250. 搜索
  251. </span>
  252. </Col>
  253. </Row>
  254. )}
  255. </div>
  256. </MSticky>
  257. <Popup v-model:show={state.dataForm.status} position="bottom" round>
  258. <DatePicker
  259. v-model={state.dataForm.currentDate}
  260. formatter={formatterDatePicker}
  261. minDate={state.dataForm.minDate}
  262. maxDate={state.dataForm.maxDate}
  263. onCancel={() => (state.dataForm.status = false)}
  264. onConfirm={({ selectedValues }) => {
  265. let dataForm = state.dataForm;
  266. if (dataForm.type == 'showStart') {
  267. forms.params.startTime = selectedValues.join('-');
  268. } else if (dataForm.type == 'showEnd') {
  269. forms.params.endTime = selectedValues.join('-');
  270. }
  271. state.dataForm.status = false;
  272. }}
  273. />
  274. </Popup>
  275. <SkeletionDetailModal v-model:show={forms.listState.loading}>
  276. <MFullRefresh
  277. v-model:modelValue={forms.listState.refreshing}
  278. onRefresh={() => onRefresh()}
  279. style={{
  280. minHeight: `calc(100vh - var(--header-height))`
  281. }}>
  282. <List
  283. finished={forms.listState.finished}
  284. finishedText=" "
  285. style={{ overflow: 'hidden', marginBottom: '18px' }}
  286. onLoad={getList}
  287. immediateCheck={false}>
  288. {forms.listState.dataShow ? (
  289. forms.list.map((item: any) => (
  290. <CellGroup
  291. class={styles['data-content']}
  292. border={false}
  293. onClick={() => showDetail(item)}>
  294. <Cell
  295. style={{ padding: '10px 12px 12px' }}
  296. border
  297. center
  298. title-style="flex-basis: 45%;">
  299. {{
  300. title: () => (
  301. <>
  302. <span class={styles.scoreName}>
  303. {item.sysMusicScoreName}
  304. </span>
  305. <p style="font-size: 14px; color: #808080;">
  306. {item.createTime}
  307. </p>
  308. </>
  309. ),
  310. value: () => (
  311. <p>
  312. {getLevelByScore(item.score) === 1 && (
  313. <img
  314. class={styles.scoreImg}
  315. src={score1}
  316. alt=""></img>
  317. )}
  318. {getLevelByScore(item.score) === 2 && (
  319. <img
  320. class={styles.scoreImg}
  321. src={score2}
  322. alt=""></img>
  323. )}
  324. {getLevelByScore(item.score) === 3 && (
  325. <img
  326. class={styles.scoreImg}
  327. src={score3}
  328. alt=""></img>
  329. )}
  330. </p>
  331. )
  332. }}
  333. </Cell>
  334. <Cell
  335. isLink
  336. clickable={false}
  337. center
  338. style="padding: 12px 12px 16px">
  339. {{
  340. title: () => (
  341. <Grid border={false} columnNum={4} clickable>
  342. <GridItem text="综合得分">
  343. {{ icon: () => <span>{item.score}分</span> }}
  344. </GridItem>
  345. <GridItem text="音准">
  346. {{
  347. icon: () => (
  348. <span style="color: #01C1B5">
  349. {item.intonation}分
  350. </span>
  351. )
  352. }}
  353. </GridItem>
  354. <GridItem text="节奏">
  355. {{
  356. icon: () => (
  357. <span style="color: #FF802C">
  358. {item.cadence}分
  359. </span>
  360. )
  361. }}
  362. </GridItem>
  363. <GridItem text="完成度">
  364. {{
  365. icon: () => (
  366. <span style="color: #F79C00">
  367. {item.integrity}分
  368. </span>
  369. )
  370. }}
  371. </GridItem>
  372. </Grid>
  373. )
  374. }}
  375. </Cell>
  376. </CellGroup>
  377. ))
  378. ) : (
  379. <MEmpty
  380. style={{
  381. minHeight: `calc(100vh - var(--header-height))`
  382. }}
  383. description="暂无训练详情"
  384. />
  385. )}
  386. </List>
  387. </MFullRefresh>
  388. </SkeletionDetailModal>
  389. </div>
  390. );
  391. }
  392. });