index.tsx 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  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 || !props.item.fileList?.fileType) &&
  66. styles['work-content-disabled']
  67. ]}>
  68. <div
  69. class={[styles['work-content']]}
  70. style={{
  71. cursor: !props.item.fileList?.fileType ? 'default' : 'pointer'
  72. }}>
  73. {/* ("文件类型:评测:EVALUATION,IMG:图片,SOUND:音频,VIDEO:视频")
  74. private String fileType; */}
  75. {!props.item.fileList?.fileType && (
  76. <NImage
  77. src={nodata}
  78. class={styles.nodata}
  79. previewDisabled
  80. objectFit="contain"
  81. />
  82. )}
  83. {props.item.fileList?.fileType === 'IMG' && (
  84. <NImage
  85. src={props.item.fileList?.filePath}
  86. objectFit="contain"
  87. renderToolbar={({ nodes }: ImageRenderToolbarProps) => {
  88. return [
  89. nodes.prev,
  90. nodes.next,
  91. nodes.rotateCounterclockwise,
  92. nodes.rotateClockwise,
  93. nodes.resizeToOriginalSize,
  94. nodes.zoomOut,
  95. <div
  96. class={'n-base-icon'}
  97. onClick={() => onDownload(props.item.fileList?.filePath)}>
  98. <svg
  99. viewBox="0 0 16 16"
  100. version="1.1"
  101. xmlns="http://www.w3.org/2000/svg">
  102. <g
  103. stroke="none"
  104. stroke-width="1"
  105. fill="none"
  106. fill-rule="evenodd">
  107. <g fill="currentColor" fill-rule="nonzero">
  108. <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>
  109. </g>
  110. </g>
  111. </svg>
  112. </div>,
  113. nodes.close
  114. ];
  115. }}
  116. />
  117. )}
  118. {props.item.fileList?.fileType === 'SOUND' && (
  119. <div
  120. onClick={() => {
  121. preivewItem.value.content = props.item.fileList?.filePath;
  122. preivewItem.value.title = props.item.musicName;
  123. preivewItem.value.type = 'SONG';
  124. previewShow.value = true;
  125. }}>
  126. <NImage
  127. src={PageEnum.SONG_DEFAULT_COVER}
  128. previewDisabled
  129. objectFit="contain"
  130. />
  131. </div>
  132. )}
  133. {props.item.fileList?.fileType === 'EVALUATION' &&
  134. (checkUrlType(props.item.fileList?.content) === 'video' ? (
  135. <div
  136. class={styles.videoSection}
  137. onClick={() => {
  138. preivewItem.value.content = props.item.fileList?.content;
  139. preivewItem.value.title = props.item.musicName;
  140. preivewItem.value.type = 'VIDEO';
  141. previewShow.value = true;
  142. }}>
  143. <video
  144. style={{ height: '100%' }}
  145. src={props.item.fileList?.content}
  146. />
  147. </div>
  148. ) : (
  149. <div
  150. onClick={() => {
  151. preivewItem.value.content = props.item.fileList?.content;
  152. preivewItem.value.title = props.item.musicName;
  153. preivewItem.value.type = 'SONG';
  154. previewShow.value = true;
  155. }}>
  156. <NImage
  157. src={PageEnum.SONG_DEFAULT_COVER}
  158. previewDisabled
  159. objectFit="contain"
  160. />
  161. </div>
  162. ))}
  163. {/* 'https://oss.dayaedu.com/ktqy/1715586967518b42c4fe5.mp4' */}
  164. {props.item.fileList?.fileType === 'VIDEO' && (
  165. <div
  166. class={styles.videoSection}
  167. onClick={() => {
  168. preivewItem.value.content = props.item.fileList?.filePath;
  169. preivewItem.value.title = props.item.musicName;
  170. preivewItem.value.type = 'VIDEO';
  171. previewShow.value = true;
  172. }}>
  173. <video
  174. style={{ height: '100%' }}
  175. src={props.item.fileList?.filePath}
  176. />
  177. </div>
  178. )}
  179. {/* 判断是否过期 */}
  180. {props.item.fileList?.expireFlag && (
  181. <div class={styles.expireBg}>文件已过期</div>
  182. )}
  183. {props.item.recordId && (
  184. <NButton
  185. color="rgba(0,0,0,0.4)"
  186. textColor="#fff"
  187. disabled={props.item.fileList?.expireFlag}
  188. class={styles.reportBtn}
  189. onClick={() => {
  190. if (!props.item.recordId) {
  191. message.error('暂无评测记录');
  192. return;
  193. }
  194. const tockn = userStore.getToken;
  195. reportSrc.value =
  196. vaildMusicScoreUrl() +
  197. `/instrument/#/evaluat-report?id=${props.item.recordId}&Authorization=${tockn}`;
  198. detailVisiable.value = true;
  199. }}>
  200. 评测报告
  201. </NButton>
  202. )}
  203. </div>
  204. <div class={styles['work-footer']}>
  205. <div class={styles.trainInfo}>
  206. <div class={styles.trainName}>
  207. <span class={[styles.type, styles[props.item.trainingType]]}>
  208. {props.item.trainingType === 'EVALUATION' ? '评测' : '练习'}
  209. </span>
  210. <div class={styles['title-text']}>
  211. <TheNoticeBar text={props.item.musicName} />
  212. </div>
  213. </div>
  214. <div class={styles.tagList}>
  215. {props.item.typeList?.map((type: string, index: number) => (
  216. <>
  217. <span>{type}</span>
  218. {props.item.typeList.length - 1 > index && (
  219. <NDivider vertical />
  220. )}
  221. </>
  222. ))}
  223. </div>
  224. </div>
  225. {props.item.trainingType === 'EVALUATION' ? (
  226. <div class={[styles.scoreGroup, styles.scoreGroupEval]}>
  227. {props.item.trainingTimes}
  228. <span>分</span>
  229. </div>
  230. ) : (
  231. <div class={[styles.scoreGroup]}>
  232. {props.item.trainingTimes
  233. ? parseInt(props.item.trainingTimes / 60 + '')
  234. : 0}
  235. <span>分钟</span>
  236. </div>
  237. )}
  238. </div>
  239. <CardPreview
  240. v-model:show={previewShow.value}
  241. item={preivewItem.value}
  242. />
  243. <NModal
  244. v-model:show={detailVisiable.value}
  245. preset="card"
  246. class={['modalTitle background', styles.reportModel]}
  247. title={'评测报告'}>
  248. <div class={styles.reportContainer} style={{ lineHeight: 0 }}>
  249. <iframe
  250. width={'100%'}
  251. height={'450px'}
  252. frameborder="0"
  253. onLoad={(val: any) => {
  254. iframeDislableKeyboard(val.target);
  255. }}
  256. src={reportSrc.value}></iframe>
  257. </div>
  258. </NModal>
  259. </div>
  260. );
  261. }
  262. });