index.tsx 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. import { computed, defineComponent, ref } from 'vue';
  2. import {
  3. NImage,
  4. NDivider,
  5. NButton,
  6. NModal,
  7. useMessage,
  8. ImageRenderToolbarProps
  9. } from 'naive-ui';
  10. import TheNoticeBar from '/src/components/TheNoticeBar';
  11. import styles from './index.module.less';
  12. import { PageEnum } from '/src/enums/pageEnum';
  13. import nodata from '../images/nomore.png';
  14. import CardPreview from '/src/components/card-preview';
  15. import { checkUrlType, iframeDislableKeyboard } from '/src/utils';
  16. import { useUserStore } from '/src/store/modules/users';
  17. import { vaildMusicScoreUrl } from '/src/utils/urlUtils';
  18. import { saveAs } from 'file-saver';
  19. export default defineComponent({
  20. name: 'work-item',
  21. props: {
  22. item: {
  23. type: Object,
  24. default: () => ({})
  25. }
  26. },
  27. setup(props) {
  28. const userStore = useUserStore();
  29. const message = useMessage();
  30. const previewShow = ref(false);
  31. const preivewItem = ref({
  32. type: 'MUSIC',
  33. content: props.item.musicId,
  34. title: props.item.musicName,
  35. studentName: props.item.studentName
  36. });
  37. const reportSrc = ref('');
  38. const detailVisiable = ref(false);
  39. // 下载资源
  40. const onDownload = (src: any) => {
  41. if (!src) {
  42. message.error('下载失败');
  43. return;
  44. }
  45. const fileUrl = src;
  46. // props.item.studentName
  47. const title =
  48. props.item.musicName +
  49. (props.item.studentName ? '-' + props.item.studentName : '');
  50. const suffix = src.substring(src.lastIndexOf('.'));
  51. // 发起Fetch请求
  52. fetch(fileUrl)
  53. .then(response => response.blob())
  54. .then(blob => {
  55. saveAs(blob, (title || new Date().getTime() + '') + suffix);
  56. })
  57. .catch(() => {
  58. message.error('下载失败');
  59. });
  60. };
  61. return () => (
  62. <div
  63. class={[
  64. styles.workItem,
  65. (props.item.fileList?.expireFlag ||
  66. props.item.trainingStatus === 'UNSUBMITTED') &&
  67. styles['work-content-disabled']
  68. ]}>
  69. <div
  70. class={[styles['work-content']]}
  71. style={{
  72. cursor:
  73. props.item.trainingStatus === 'UNSUBMITTED'
  74. ? 'default'
  75. : 'pointer'
  76. }}>
  77. {/* ("文件类型:评测:EVALUATION,IMG:图片,SOUND:音频,VIDEO:视频")
  78. private String fileType; */}
  79. {!props.item.fileList?.fileType && (
  80. <NImage
  81. src={nodata}
  82. class={styles.nodata}
  83. previewDisabled
  84. objectFit="contain"
  85. />
  86. )}
  87. {props.item.fileList?.fileType === 'IMG' && (
  88. <NImage
  89. src={props.item.fileList?.filePath}
  90. objectFit="contain"
  91. renderToolbar={({ nodes }: ImageRenderToolbarProps) => {
  92. return [
  93. nodes.prev,
  94. nodes.next,
  95. nodes.rotateCounterclockwise,
  96. nodes.rotateClockwise,
  97. nodes.resizeToOriginalSize,
  98. nodes.zoomOut,
  99. <div
  100. class={'n-base-icon'}
  101. onClick={() => onDownload(props.item.fileList?.filePath)}>
  102. <svg
  103. viewBox="0 0 16 16"
  104. version="1.1"
  105. xmlns="http://www.w3.org/2000/svg">
  106. <g
  107. stroke="none"
  108. stroke-width="1"
  109. fill="none"
  110. fill-rule="evenodd">
  111. <g fill="currentColor" fill-rule="nonzero">
  112. <path d="M3.5,13 L12.5,13 C12.7761424,13 13,13.2238576 13,13.5 C13,13.7454599 12.8231248,13.9496084 12.5898756,13.9919443 L12.5,14 L3.5,14 C3.22385763,14 3,13.7761424 3,13.5 C3,13.2545401 3.17687516,13.0503916 3.41012437,13.0080557 L3.5,13 L12.5,13 L3.5,13 Z M7.91012437,1.00805567 L8,1 C8.24545989,1 8.44960837,1.17687516 8.49194433,1.41012437 L8.5,1.5 L8.5,10.292 L11.1819805,7.6109127 C11.3555469,7.43734635 11.6249713,7.4180612 11.8198394,7.55305725 L11.8890873,7.6109127 C12.0626536,7.78447906 12.0819388,8.05390346 11.9469427,8.2487716 L11.8890873,8.31801948 L8.35355339,11.8535534 C8.17998704,12.0271197 7.91056264,12.0464049 7.7156945,11.9114088 L7.64644661,11.8535534 L4.1109127,8.31801948 C3.91565056,8.12275734 3.91565056,7.80617485 4.1109127,7.6109127 C4.28447906,7.43734635 4.55390346,7.4180612 4.7487716,7.55305725 L4.81801948,7.6109127 L7.5,10.292 L7.5,1.5 C7.5,1.25454011 7.67687516,1.05039163 7.91012437,1.00805567 L8,1 L7.91012437,1.00805567 Z"></path>
  113. </g>
  114. </g>
  115. </svg>
  116. </div>,
  117. nodes.close
  118. ];
  119. }}
  120. />
  121. )}
  122. {props.item.fileList?.fileType === 'SOUND' && (
  123. <div
  124. onClick={() => {
  125. preivewItem.value.content = props.item.fileList?.filePath;
  126. preivewItem.value.title = props.item.musicName;
  127. preivewItem.value.type = 'SONG';
  128. previewShow.value = true;
  129. }}>
  130. <NImage
  131. src={PageEnum.SONG_DEFAULT_COVER}
  132. previewDisabled
  133. objectFit="contain"
  134. />
  135. </div>
  136. )}
  137. {props.item.fileList?.fileType === 'EVALUATION' &&
  138. (checkUrlType(props.item.fileList?.content) === 'video' ? (
  139. <div
  140. class={styles.videoSection}
  141. onClick={() => {
  142. preivewItem.value.content = props.item.fileList?.content;
  143. preivewItem.value.title = props.item.musicName;
  144. preivewItem.value.type = 'VIDEO';
  145. previewShow.value = true;
  146. }}>
  147. <video
  148. style={{ height: '100%' }}
  149. src={props.item.fileList?.content}
  150. />
  151. </div>
  152. ) : (
  153. <div
  154. onClick={() => {
  155. preivewItem.value.content = props.item.fileList?.content;
  156. preivewItem.value.title = props.item.musicName;
  157. preivewItem.value.type = 'SONG';
  158. previewShow.value = true;
  159. }}>
  160. <NImage
  161. src={PageEnum.SONG_DEFAULT_COVER}
  162. previewDisabled
  163. objectFit="contain"
  164. />
  165. </div>
  166. ))}
  167. {/* 'https://oss.dayaedu.com/ktqy/1715586967518b42c4fe5.mp4' */}
  168. {props.item.fileList?.fileType === 'VIDEO' && (
  169. <div
  170. class={styles.videoSection}
  171. onClick={() => {
  172. preivewItem.value.content = props.item.fileList?.filePath;
  173. preivewItem.value.title = props.item.musicName;
  174. preivewItem.value.type = 'VIDEO';
  175. previewShow.value = true;
  176. }}>
  177. <video
  178. style={{ height: '100%' }}
  179. src={props.item.fileList?.filePath}
  180. />
  181. </div>
  182. )}
  183. {/* 判断是否过期 */}
  184. {props.item.fileList?.expireFlag && (
  185. <div class={styles.expireBg}>文件已过期</div>
  186. )}
  187. {props.item.recordId && (
  188. <NButton
  189. color="rgba(0,0,0,0.4)"
  190. textColor="#fff"
  191. disabled={props.item.fileList?.expireFlag}
  192. class={styles.reportBtn}
  193. onClick={() => {
  194. if (!props.item.recordId) {
  195. message.error('暂无评测记录');
  196. return;
  197. }
  198. const tockn = userStore.getToken;
  199. reportSrc.value =
  200. vaildMusicScoreUrl() +
  201. `/instrument/#/evaluat-report?id=${props.item.recordId}&Authorization=${tockn}`;
  202. detailVisiable.value = true;
  203. }}>
  204. 评测报告
  205. </NButton>
  206. )}
  207. </div>
  208. <div class={styles['work-footer']}>
  209. <div class={styles.trainInfo}>
  210. <div class={styles.trainName}>
  211. <span class={[styles.type, styles[props.item.trainingType]]}>
  212. {props.item.trainingType === 'EVALUATION' ? '评测' : '练习'}
  213. </span>
  214. <div class={styles['title-text']}>
  215. <TheNoticeBar text={props.item.musicName} />
  216. </div>
  217. </div>
  218. <div class={styles.tagList}>
  219. {props.item.typeList?.map((type: string, index: number) => (
  220. <>
  221. <span>{type}</span>
  222. {props.item.typeList.length - 1 > index && (
  223. <NDivider vertical />
  224. )}
  225. </>
  226. ))}
  227. </div>
  228. </div>
  229. {props.item.trainingType === 'EVALUATION' ? (
  230. <div class={[styles.scoreGroup, styles.scoreGroupEval]}>
  231. {props.item.trainingStatus !== 'UNSUBMITTED' ? (
  232. <>
  233. {props.item.trainingTimes}
  234. <span>分</span>
  235. </>
  236. ) : (
  237. <span class={styles.noSubmit}>未提交</span>
  238. )}
  239. </div>
  240. ) : (
  241. <div class={[styles.scoreGroup]}>
  242. {props.item.trainingStatus !== 'UNSUBMITTED' ? (
  243. <>
  244. {props.item.trainingTimes
  245. ? parseInt(props.item.trainingTimes / 60 + '')
  246. : 0}
  247. <span>分钟</span>
  248. </>
  249. ) : (
  250. <span class={styles.noSubmit}>未提交</span>
  251. )}
  252. </div>
  253. )}
  254. </div>
  255. <CardPreview
  256. v-model:show={previewShow.value}
  257. item={preivewItem.value}
  258. />
  259. <NModal
  260. v-model:show={detailVisiable.value}
  261. preset="card"
  262. class={['modalTitle background', styles.reportModel]}
  263. title={'评测报告'}>
  264. <div class={styles.reportContainer} style={{ lineHeight: 0 }}>
  265. <iframe
  266. width={'100%'}
  267. height={'450px'}
  268. frameborder="0"
  269. onLoad={(val: any) => {
  270. iframeDislableKeyboard(val.target);
  271. }}
  272. src={reportSrc.value}></iframe>
  273. </div>
  274. </NModal>
  275. </div>
  276. );
  277. }
  278. });