|
@@ -1,517 +1,536 @@
|
|
|
-import { PropType, Transition, defineComponent, ref } from 'vue';
|
|
|
-import styles from './index.module.less';
|
|
|
-import { NButton, NCard, NImage, NModal, NSpin, useMessage } from 'naive-ui';
|
|
|
-import iconImage from '@common/images/icon-image.png';
|
|
|
-import iconVideo from '@common/images/icon-video.png';
|
|
|
-import iconAudio from '@common/images/icon-audio.png';
|
|
|
-import iconMusic from '@common/images/icon-music.png';
|
|
|
-import iconPPT from '@common/images/icon-ppt.png';
|
|
|
-import iconOther from '@common/images/icon-other.png';
|
|
|
-import iconCollectDefault from '@common/images/icon-collect-default.png';
|
|
|
-import iconCollectActive from '@common/images/icon-collect-active.png';
|
|
|
-import iconDownload from '@common/images/icon-download.png';
|
|
|
-import TheNoticeBar from '../TheNoticeBar';
|
|
|
-import AudioPlayer from './audio-player';
|
|
|
-import VideoPlayer from './video-player';
|
|
|
-import { PageEnum } from '/src/enums/pageEnum';
|
|
|
-import { api_musicSheetDetail } from '/src/api/user';
|
|
|
-import JSZip, { file } from 'jszip';
|
|
|
-import { saveAs } from 'file-saver';
|
|
|
-
|
|
|
-// LISTEN:听音,RHYTHM:节奏,THEORY:乐理知识,MUSIC_WIKI:曲目 INSTRUMENT:乐器 MUSICIAN:音乐家)
|
|
|
-type itemType = {
|
|
|
- id: string | number;
|
|
|
- type:
|
|
|
- | 'IMG'
|
|
|
- | 'VIDEO'
|
|
|
- | 'SONG'
|
|
|
- | 'MUSIC'
|
|
|
- | 'PPT'
|
|
|
- | 'LISTEN'
|
|
|
- | 'RHYTHM'
|
|
|
- | 'THEORY'
|
|
|
- | 'MUSIC_WIKI'
|
|
|
- | 'INSTRUMENT'
|
|
|
- | 'MUSICIAN';
|
|
|
- coverImg: string;
|
|
|
- content?: string;
|
|
|
- title: string;
|
|
|
- isCollect: boolean;
|
|
|
- isSelected: boolean; // 精选
|
|
|
- exist?: boolean; // 是否已经选
|
|
|
-};
|
|
|
-
|
|
|
-export default defineComponent({
|
|
|
- name: 'card-type',
|
|
|
- props: {
|
|
|
- // 是否是选中状态
|
|
|
- isActive: {
|
|
|
- type: Boolean,
|
|
|
- default: false
|
|
|
- },
|
|
|
- /** 是否可以拖拽 */
|
|
|
- draggable: {
|
|
|
- type: Boolean,
|
|
|
- default: false
|
|
|
- },
|
|
|
- // 是否可以收藏
|
|
|
- isCollect: {
|
|
|
- type: Boolean,
|
|
|
- default: true
|
|
|
- },
|
|
|
- // 是否显示收藏
|
|
|
- isShowCollect: {
|
|
|
- type: Boolean,
|
|
|
- default: true
|
|
|
- },
|
|
|
- // 是否显示添加按钮
|
|
|
- isShowAdd: {
|
|
|
- type: Boolean,
|
|
|
- default: false
|
|
|
- },
|
|
|
- // 是否禁用添加按钮
|
|
|
- isShowAddDisabled: {
|
|
|
- type: Boolean,
|
|
|
- default: false
|
|
|
- },
|
|
|
- // 鼠标移动上面的时候是否自动播放,或者可以点击
|
|
|
- disabledMouseHover: {
|
|
|
- type: Boolean,
|
|
|
- default: true
|
|
|
- },
|
|
|
- // 是否预览
|
|
|
- isPreview: {
|
|
|
- type: Boolean,
|
|
|
- default: true
|
|
|
- },
|
|
|
- item: {
|
|
|
- type: Object as PropType<itemType>,
|
|
|
- default: () => ({})
|
|
|
- },
|
|
|
- /** 是否下架 */
|
|
|
- offShelf: {
|
|
|
- type: Boolean,
|
|
|
- default: false
|
|
|
- },
|
|
|
- /** 是否可以下载 */
|
|
|
- isDownload: {
|
|
|
- type: Boolean,
|
|
|
- default: false
|
|
|
- }
|
|
|
- },
|
|
|
- /**
|
|
|
- * @type {string} click 点击事件
|
|
|
- * @type {string} collect 收藏
|
|
|
- * @type {string} add 添加
|
|
|
- * @type {string} offShelf 下架
|
|
|
- */
|
|
|
- emits: ['click', 'collect', 'add', 'offShelf'],
|
|
|
- setup(props, { emit }) {
|
|
|
- const message = useMessage();
|
|
|
- const isAnimation = ref(false);
|
|
|
- const downloadStatus = ref(false);
|
|
|
- const formatType = (type: string) => {
|
|
|
- let typeImg = iconOther;
|
|
|
- switch (type) {
|
|
|
- case 'IMG':
|
|
|
- typeImg = iconImage;
|
|
|
- break;
|
|
|
- case 'VIDEO':
|
|
|
- typeImg = iconVideo;
|
|
|
- break;
|
|
|
- case 'SONG':
|
|
|
- typeImg = iconAudio;
|
|
|
- break;
|
|
|
- case 'MUSIC':
|
|
|
- typeImg = iconMusic;
|
|
|
- break;
|
|
|
- case 'PPT':
|
|
|
- typeImg = iconPPT;
|
|
|
- break;
|
|
|
- }
|
|
|
- return typeImg;
|
|
|
- };
|
|
|
-
|
|
|
- // 获取文件blob格式
|
|
|
- const getFileBlob = (url: string) => {
|
|
|
- return new Promise((resolve, reject) => {
|
|
|
- const request = new XMLHttpRequest();
|
|
|
- request.open('GET', url, true);
|
|
|
- request.responseType = 'blob';
|
|
|
- request.onload = (res: any) => {
|
|
|
- if (res.target.status == 200) {
|
|
|
- resolve(res.target.response);
|
|
|
- } else {
|
|
|
- reject(res);
|
|
|
- }
|
|
|
- };
|
|
|
- request.send();
|
|
|
- });
|
|
|
- };
|
|
|
-
|
|
|
- // 多个文件下载
|
|
|
- const downLoadMultiFile = (files: any, filesName: string) => {
|
|
|
- const zip = new JSZip();
|
|
|
- const result = [];
|
|
|
- for (const i in files) {
|
|
|
- const promise = getFileBlob(files[i].url).then((res: any) => {
|
|
|
- zip.file(files[i].name, res, { binary: true });
|
|
|
- });
|
|
|
- result.push(promise);
|
|
|
- }
|
|
|
- Promise.all(result)
|
|
|
- .then(() => {
|
|
|
- zip.generateAsync({ type: 'blob' }).then(res => {
|
|
|
- saveAs(
|
|
|
- res,
|
|
|
- filesName
|
|
|
- ? filesName + Date.now() + '.zip'
|
|
|
- : `文件夹${Date.now()}.zip`
|
|
|
- );
|
|
|
- });
|
|
|
- })
|
|
|
- .catch(() => {
|
|
|
- message.error('下载失败');
|
|
|
- });
|
|
|
-
|
|
|
- downloadStatus.value = false;
|
|
|
- };
|
|
|
-
|
|
|
- const downloadFile = (filename: string, fileUrl: string) => {
|
|
|
- // 发起Fetch请求
|
|
|
- fetch(fileUrl)
|
|
|
- .then(response => response.blob())
|
|
|
- .then(blob => {
|
|
|
- saveAs(blob, filename);
|
|
|
- setTimeout(() => {
|
|
|
- downloadStatus.value = false;
|
|
|
- }, 100);
|
|
|
- })
|
|
|
- .catch(() => {
|
|
|
- message.error('下载失败');
|
|
|
- });
|
|
|
-
|
|
|
- downloadStatus.value = false;
|
|
|
- };
|
|
|
-
|
|
|
- const getFileName = (url: any) => {
|
|
|
- // 使用正则表达式获取文件名
|
|
|
- const tempUrl = url.split('?');
|
|
|
- const fileNameRegex = /\/([^\\/]+)$/; // 匹配最后一个斜杠后的内容
|
|
|
- const match = tempUrl[0].match(fileNameRegex);
|
|
|
-
|
|
|
- if (match) {
|
|
|
- return match[1];
|
|
|
- } else {
|
|
|
- return '';
|
|
|
- }
|
|
|
- };
|
|
|
- const onDownload = async (e: MouseEvent) => {
|
|
|
- e.stopPropagation();
|
|
|
- e.preventDefault();
|
|
|
- const item = props.item;
|
|
|
- if (!item.content) {
|
|
|
- message.error('下载失败');
|
|
|
- return;
|
|
|
- }
|
|
|
- if (downloadStatus.value) return false;
|
|
|
- downloadStatus.value = true;
|
|
|
- const suffix: any = item.content?.split('.');
|
|
|
- const fileName = item.title + '.' + suffix[suffix?.length - 1];
|
|
|
- if (item.type === 'MUSIC') {
|
|
|
- const { data } = await api_musicSheetDetail(item.content);
|
|
|
- const urls = [];
|
|
|
- if (data.xmlFileUrl) {
|
|
|
- urls.push({
|
|
|
- url: data.xmlFileUrl,
|
|
|
- name: getFileName(data.xmlFileUrl)
|
|
|
- });
|
|
|
- }
|
|
|
- if (data.background && data.background.length > 0) {
|
|
|
- data.background.forEach((item: any) => {
|
|
|
- urls.push({
|
|
|
- url: item.audioFileUrl,
|
|
|
- name: getFileName(item.audioFileUrl)
|
|
|
- });
|
|
|
- });
|
|
|
- }
|
|
|
- downLoadMultiFile(urls, item.title);
|
|
|
-
|
|
|
- // setTimeout(() => {
|
|
|
- // downloadStatus.value = false;
|
|
|
- // }, 1000);
|
|
|
- } else {
|
|
|
- downloadFile(fileName, item.content);
|
|
|
- }
|
|
|
- };
|
|
|
-
|
|
|
- return () => (
|
|
|
- <div
|
|
|
- onClick={() => emit('click', props.item)}
|
|
|
- key={props.item.id}
|
|
|
- draggable={!props.draggable ? false : props.item.exist ? false : true}
|
|
|
- class={[
|
|
|
- styles['card-section'],
|
|
|
- 'card-section-container',
|
|
|
- !props.draggable ? '' : props.item.exist ? '' : styles.cardDrag
|
|
|
- ]}
|
|
|
- onMouseenter={() => {
|
|
|
- isAnimation.value = true;
|
|
|
- }}
|
|
|
- onMouseleave={() => {
|
|
|
- isAnimation.value = false;
|
|
|
- }}
|
|
|
- onDragstart={(e: any) => {
|
|
|
- console.log('dragstart', Date.now());
|
|
|
- e.dataTransfer.setData('text', JSON.stringify(props.item));
|
|
|
- }}>
|
|
|
- {/* 判断是否下架 */}
|
|
|
- {props.offShelf && (
|
|
|
- <div class={styles.offShelfBg}>
|
|
|
- <p class={styles.offShelfTips}>该资源已被下架</p>
|
|
|
- <NButton
|
|
|
- type="primary"
|
|
|
- class={styles.offShelfBtn}
|
|
|
- onClick={(e: MouseEvent) => {
|
|
|
- e.stopPropagation();
|
|
|
- emit('offShelf');
|
|
|
- }}>
|
|
|
- 确认
|
|
|
- </NButton>
|
|
|
- </div>
|
|
|
- )}
|
|
|
- <NCard
|
|
|
- class={[
|
|
|
- styles['card-section-content'],
|
|
|
- props.isShowAdd ? '' : styles.course,
|
|
|
- props.isActive ? styles.isActive : '',
|
|
|
- props.item.exist ? styles.showAddBtn : '' // 是否已添加
|
|
|
- ]}
|
|
|
- style={{ cursor: 'pointer' }}>
|
|
|
- {{
|
|
|
- cover: () => (
|
|
|
- <>
|
|
|
- {/* 图片 */}
|
|
|
- {props.item.type === 'IMG' && (
|
|
|
- <NImage
|
|
|
- class={[styles.cover, styles.image]}
|
|
|
- lazy
|
|
|
- previewDisabled={props.disabledMouseHover}
|
|
|
- objectFit="cover"
|
|
|
- src={props.item.coverImg}
|
|
|
- previewSrc={props.item.content}
|
|
|
- />
|
|
|
- )}
|
|
|
- {/* 乐谱 */}
|
|
|
- {props.item.type === 'MUSIC' && (
|
|
|
- <NImage
|
|
|
- class={[styles.cover, styles.image]}
|
|
|
- lazy
|
|
|
- previewDisabled={true}
|
|
|
- objectFit="contain"
|
|
|
- src={props.item.coverImg}
|
|
|
- />
|
|
|
- )}
|
|
|
- {/* 音频 */}
|
|
|
- {props.item.type === 'SONG' && (
|
|
|
- <AudioPlayer
|
|
|
- content={props.item.content}
|
|
|
- cover={props.item.coverImg}
|
|
|
- previewDisabled={props.disabledMouseHover}
|
|
|
- />
|
|
|
- )}
|
|
|
- {/* 视频 */}
|
|
|
- {props.item.type === 'VIDEO' && (
|
|
|
- <VideoPlayer
|
|
|
- cover={props.item.coverImg}
|
|
|
- content={props.item.content}
|
|
|
- previewDisabled={props.disabledMouseHover}
|
|
|
- />
|
|
|
- )}
|
|
|
- {/* ppt */}
|
|
|
- {props.item.type === 'PPT' && (
|
|
|
- <NImage
|
|
|
- class={[styles.cover, styles.image]}
|
|
|
- lazy
|
|
|
- previewDisabled={true}
|
|
|
- objectFit="cover"
|
|
|
- src={props.item.coverImg || PageEnum.PPT_DEFAULT_COVER}
|
|
|
- />
|
|
|
- )}
|
|
|
- {/* 节奏练习 */}
|
|
|
- {props.item.type === 'RHYTHM' && (
|
|
|
- <NImage
|
|
|
- class={[styles.cover, styles.image]}
|
|
|
- lazy
|
|
|
- previewDisabled={true}
|
|
|
- objectFit="cover"
|
|
|
- src={props.item.coverImg || PageEnum.RHYTHM_DEFAULT_COVER}
|
|
|
- />
|
|
|
- )}
|
|
|
-
|
|
|
- {/* 听音练习 */}
|
|
|
- {props.item.type === 'LISTEN' && (
|
|
|
- <NImage
|
|
|
- class={[styles.cover, styles.image]}
|
|
|
- lazy
|
|
|
- previewDisabled={true}
|
|
|
- objectFit="cover"
|
|
|
- src={props.item.coverImg}
|
|
|
- />
|
|
|
- )}
|
|
|
- {/* 乐理 */}
|
|
|
- {props.item.type === 'THEORY' && (
|
|
|
- <NImage
|
|
|
- class={[styles.cover, styles.image]}
|
|
|
- lazy
|
|
|
- previewDisabled={true}
|
|
|
- objectFit="cover"
|
|
|
- src={props.item.coverImg || PageEnum.THEORY_DEFAULT_COVER}
|
|
|
- />
|
|
|
- )}
|
|
|
- {/* 名曲 */}
|
|
|
- {props.item.type === 'MUSIC_WIKI' && (
|
|
|
- <NImage
|
|
|
- class={[styles.cover, styles.image]}
|
|
|
- lazy
|
|
|
- previewDisabled={true}
|
|
|
- objectFit="cover"
|
|
|
- src={props.item.coverImg || PageEnum.MUSIC_DEFAULT_COVER}
|
|
|
- />
|
|
|
- )}
|
|
|
- {/* 乐器 */}
|
|
|
- {props.item.type === 'INSTRUMENT' && (
|
|
|
- <NImage
|
|
|
- class={[styles.cover, styles.image]}
|
|
|
- lazy
|
|
|
- previewDisabled={true}
|
|
|
- objectFit="cover"
|
|
|
- src={
|
|
|
- props.item.coverImg || PageEnum.INSTRUMENT_DEFAULT_COVER
|
|
|
- }
|
|
|
- />
|
|
|
- )}
|
|
|
- {/* 音乐家 */}
|
|
|
- {props.item.type === 'MUSICIAN' && (
|
|
|
- <NImage
|
|
|
- class={[styles.cover, styles.image]}
|
|
|
- lazy
|
|
|
- previewDisabled={true}
|
|
|
- objectFit="cover"
|
|
|
- src={props.item.coverImg || PageEnum.MUSICIAN_DEFAULT_COVER}
|
|
|
- />
|
|
|
- )}
|
|
|
- </>
|
|
|
- ),
|
|
|
- footer: () => (
|
|
|
- <div class={styles.footer}>
|
|
|
- <div class={[styles.title, 'footerTitle']}>
|
|
|
- <NImage
|
|
|
- class={[styles.titleType]}
|
|
|
- src={formatType(props.item.type)}
|
|
|
- objectFit="cover"
|
|
|
- />
|
|
|
- <span class={[styles.titleContent, 'titleContent']}>
|
|
|
- <TheNoticeBar
|
|
|
- isAnimation={isAnimation.value}
|
|
|
- text={props.item.title}
|
|
|
- />
|
|
|
- </span>
|
|
|
- </div>
|
|
|
- {/* 收藏 */}
|
|
|
- <div class={styles.btnGroup}>
|
|
|
- {props.isDownload && (
|
|
|
- <div class={styles.btnItem} onClick={onDownload}>
|
|
|
- <NSpin show={downloadStatus.value} size={'small'}>
|
|
|
- <img
|
|
|
- src={iconDownload}
|
|
|
- key="3"
|
|
|
- class={[styles.iconCollect]}
|
|
|
- />
|
|
|
- </NSpin>
|
|
|
- </div>
|
|
|
- )}
|
|
|
- {props.isShowCollect && (
|
|
|
- <div
|
|
|
- class={[styles.iconCollect, styles.btnItem]}
|
|
|
- onClick={(e: MouseEvent) => {
|
|
|
- e.stopPropagation();
|
|
|
- e.preventDefault();
|
|
|
- // 判断是否可以收藏
|
|
|
- if (props.isCollect) {
|
|
|
- emit('collect', props.item);
|
|
|
- }
|
|
|
- }}>
|
|
|
- <Transition name="favitor" mode="out-in">
|
|
|
- {props.item.isCollect ? (
|
|
|
- <img
|
|
|
- src={iconCollectActive}
|
|
|
- key="1"
|
|
|
- class={[
|
|
|
- styles.iconCollect,
|
|
|
- props.isCollect ? styles.isCollect : ''
|
|
|
- ]}
|
|
|
- />
|
|
|
- ) : (
|
|
|
- <img
|
|
|
- src={iconCollectDefault}
|
|
|
- key="2"
|
|
|
- class={[
|
|
|
- styles.iconCollect,
|
|
|
- props.isCollect ? styles.isCollect : ''
|
|
|
- ]}
|
|
|
- />
|
|
|
- )}
|
|
|
- </Transition>
|
|
|
- </div>
|
|
|
- )}
|
|
|
- </div>
|
|
|
-
|
|
|
- {/* 精选 */}
|
|
|
- {props.item.isSelected && (
|
|
|
- <span class={styles.iconSelected}></span>
|
|
|
- )}
|
|
|
-
|
|
|
- {/* 添加按钮 */}
|
|
|
- {props.isShowAdd &&
|
|
|
- (props.item.exist ? (
|
|
|
- <NButton
|
|
|
- type="primary"
|
|
|
- class={[
|
|
|
- styles.addBtn,
|
|
|
- props.item.exist ? styles.addBtnDisabled : ''
|
|
|
- ]}
|
|
|
- disabled={props.item.exist || props.isShowAddDisabled}
|
|
|
- onClick={(e: MouseEvent) => {
|
|
|
- e.stopPropagation();
|
|
|
- e.preventDefault();
|
|
|
- emit('add', props.item);
|
|
|
- }}>
|
|
|
- {props.item.exist ? '已添加' : '添加'}
|
|
|
- </NButton>
|
|
|
- ) : (
|
|
|
- !props.isShowAddDisabled && (
|
|
|
- <NButton
|
|
|
- type="primary"
|
|
|
- class={[
|
|
|
- styles.addBtn,
|
|
|
- props.item.exist ? styles.addBtnDisabled : ''
|
|
|
- ]}
|
|
|
- disabled={props.item.exist || props.isShowAddDisabled}
|
|
|
- onClick={(e: MouseEvent) => {
|
|
|
- e.stopPropagation();
|
|
|
- e.preventDefault();
|
|
|
- emit('add', props.item);
|
|
|
- }}>
|
|
|
- {props.item.exist ? '已添加' : '添加'}
|
|
|
- </NButton>
|
|
|
- )
|
|
|
- ))}
|
|
|
- </div>
|
|
|
- )
|
|
|
- }}
|
|
|
- </NCard>
|
|
|
- </div>
|
|
|
- );
|
|
|
- }
|
|
|
-});
|
|
|
+import { PropType, Transition, defineComponent, ref } from 'vue';
|
|
|
+import styles from './index.module.less';
|
|
|
+import {
|
|
|
+ ImageRenderToolbarProps,
|
|
|
+ NButton,
|
|
|
+ NCard,
|
|
|
+ NImage,
|
|
|
+ NModal,
|
|
|
+ NSpin,
|
|
|
+ useMessage
|
|
|
+} from 'naive-ui';
|
|
|
+import iconImage from '@common/images/icon-image.png';
|
|
|
+import iconVideo from '@common/images/icon-video.png';
|
|
|
+import iconAudio from '@common/images/icon-audio.png';
|
|
|
+import iconMusic from '@common/images/icon-music.png';
|
|
|
+import iconPPT from '@common/images/icon-ppt.png';
|
|
|
+import iconOther from '@common/images/icon-other.png';
|
|
|
+import iconCollectDefault from '@common/images/icon-collect-default.png';
|
|
|
+import iconCollectActive from '@common/images/icon-collect-active.png';
|
|
|
+import iconDownload from '@common/images/icon-download.png';
|
|
|
+import TheNoticeBar from '../TheNoticeBar';
|
|
|
+import AudioPlayer from './audio-player';
|
|
|
+import VideoPlayer from './video-player';
|
|
|
+import { PageEnum } from '/src/enums/pageEnum';
|
|
|
+import { api_musicSheetDetail } from '/src/api/user';
|
|
|
+import JSZip, { file } from 'jszip';
|
|
|
+import { saveAs } from 'file-saver';
|
|
|
+
|
|
|
+// LISTEN:听音,RHYTHM:节奏,THEORY:乐理知识,MUSIC_WIKI:曲目 INSTRUMENT:乐器 MUSICIAN:音乐家)
|
|
|
+type itemType = {
|
|
|
+ id: string | number;
|
|
|
+ type:
|
|
|
+ | 'IMG'
|
|
|
+ | 'VIDEO'
|
|
|
+ | 'SONG'
|
|
|
+ | 'MUSIC'
|
|
|
+ | 'PPT'
|
|
|
+ | 'LISTEN'
|
|
|
+ | 'RHYTHM'
|
|
|
+ | 'THEORY'
|
|
|
+ | 'MUSIC_WIKI'
|
|
|
+ | 'INSTRUMENT'
|
|
|
+ | 'MUSICIAN';
|
|
|
+ coverImg: string;
|
|
|
+ content?: string;
|
|
|
+ title: string;
|
|
|
+ isCollect: boolean;
|
|
|
+ isSelected: boolean; // 精选
|
|
|
+ exist?: boolean; // 是否已经选
|
|
|
+};
|
|
|
+
|
|
|
+export default defineComponent({
|
|
|
+ name: 'card-type',
|
|
|
+ props: {
|
|
|
+ // 是否是选中状态
|
|
|
+ isActive: {
|
|
|
+ type: Boolean,
|
|
|
+ default: false
|
|
|
+ },
|
|
|
+ /** 是否可以拖拽 */
|
|
|
+ draggable: {
|
|
|
+ type: Boolean,
|
|
|
+ default: false
|
|
|
+ },
|
|
|
+ // 是否可以收藏
|
|
|
+ isCollect: {
|
|
|
+ type: Boolean,
|
|
|
+ default: true
|
|
|
+ },
|
|
|
+ // 是否显示收藏
|
|
|
+ isShowCollect: {
|
|
|
+ type: Boolean,
|
|
|
+ default: true
|
|
|
+ },
|
|
|
+ // 是否显示添加按钮
|
|
|
+ isShowAdd: {
|
|
|
+ type: Boolean,
|
|
|
+ default: false
|
|
|
+ },
|
|
|
+ // 是否禁用添加按钮
|
|
|
+ isShowAddDisabled: {
|
|
|
+ type: Boolean,
|
|
|
+ default: false
|
|
|
+ },
|
|
|
+ // 鼠标移动上面的时候是否自动播放,或者可以点击
|
|
|
+ disabledMouseHover: {
|
|
|
+ type: Boolean,
|
|
|
+ default: true
|
|
|
+ },
|
|
|
+ // 是否预览
|
|
|
+ isPreview: {
|
|
|
+ type: Boolean,
|
|
|
+ default: true
|
|
|
+ },
|
|
|
+ item: {
|
|
|
+ type: Object as PropType<itemType>,
|
|
|
+ default: () => ({})
|
|
|
+ },
|
|
|
+ /** 是否下架 */
|
|
|
+ offShelf: {
|
|
|
+ type: Boolean,
|
|
|
+ default: false
|
|
|
+ },
|
|
|
+ /** 是否可以下载 */
|
|
|
+ isDownload: {
|
|
|
+ type: Boolean,
|
|
|
+ default: false
|
|
|
+ }
|
|
|
+ },
|
|
|
+ /**
|
|
|
+ * @type {string} click 点击事件
|
|
|
+ * @type {string} collect 收藏
|
|
|
+ * @type {string} add 添加
|
|
|
+ * @type {string} offShelf 下架
|
|
|
+ */
|
|
|
+ emits: ['click', 'collect', 'add', 'offShelf'],
|
|
|
+ setup(props, { emit }) {
|
|
|
+ const message = useMessage();
|
|
|
+ const isAnimation = ref(false);
|
|
|
+ const downloadStatus = ref(false);
|
|
|
+ const formatType = (type: string) => {
|
|
|
+ let typeImg = iconOther;
|
|
|
+ switch (type) {
|
|
|
+ case 'IMG':
|
|
|
+ typeImg = iconImage;
|
|
|
+ break;
|
|
|
+ case 'VIDEO':
|
|
|
+ typeImg = iconVideo;
|
|
|
+ break;
|
|
|
+ case 'SONG':
|
|
|
+ typeImg = iconAudio;
|
|
|
+ break;
|
|
|
+ case 'MUSIC':
|
|
|
+ typeImg = iconMusic;
|
|
|
+ break;
|
|
|
+ case 'PPT':
|
|
|
+ typeImg = iconPPT;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ return typeImg;
|
|
|
+ };
|
|
|
+
|
|
|
+ // 获取文件blob格式
|
|
|
+ const getFileBlob = (url: string) => {
|
|
|
+ return new Promise((resolve, reject) => {
|
|
|
+ const request = new XMLHttpRequest();
|
|
|
+ request.open('GET', url, true);
|
|
|
+ request.responseType = 'blob';
|
|
|
+ request.onload = (res: any) => {
|
|
|
+ if (res.target.status == 200) {
|
|
|
+ resolve(res.target.response);
|
|
|
+ } else {
|
|
|
+ reject(res);
|
|
|
+ }
|
|
|
+ };
|
|
|
+ request.send();
|
|
|
+ });
|
|
|
+ };
|
|
|
+
|
|
|
+ // 多个文件下载
|
|
|
+ const downLoadMultiFile = (files: any, filesName: string) => {
|
|
|
+ const zip = new JSZip();
|
|
|
+ const result = [];
|
|
|
+ for (const i in files) {
|
|
|
+ const promise = getFileBlob(files[i].url).then((res: any) => {
|
|
|
+ zip.file(files[i].name, res, { binary: true });
|
|
|
+ });
|
|
|
+ result.push(promise);
|
|
|
+ }
|
|
|
+ Promise.all(result)
|
|
|
+ .then(() => {
|
|
|
+ zip.generateAsync({ type: 'blob' }).then(res => {
|
|
|
+ saveAs(
|
|
|
+ res,
|
|
|
+ filesName
|
|
|
+ ? filesName + Date.now() + '.zip'
|
|
|
+ : `文件夹${Date.now()}.zip`
|
|
|
+ );
|
|
|
+ });
|
|
|
+ })
|
|
|
+ .catch(() => {
|
|
|
+ message.error('下载失败');
|
|
|
+ });
|
|
|
+
|
|
|
+ downloadStatus.value = false;
|
|
|
+ };
|
|
|
+
|
|
|
+ const downloadFile = (filename: string, fileUrl: string) => {
|
|
|
+ // 发起Fetch请求
|
|
|
+ fetch(fileUrl)
|
|
|
+ .then(response => response.blob())
|
|
|
+ .then(blob => {
|
|
|
+ saveAs(blob, filename);
|
|
|
+ setTimeout(() => {
|
|
|
+ downloadStatus.value = false;
|
|
|
+ }, 100);
|
|
|
+ })
|
|
|
+ .catch(() => {
|
|
|
+ message.error('下载失败');
|
|
|
+ });
|
|
|
+
|
|
|
+ downloadStatus.value = false;
|
|
|
+ };
|
|
|
+
|
|
|
+ const getFileName = (url: any) => {
|
|
|
+ // 使用正则表达式获取文件名
|
|
|
+ const tempUrl = url.split('?');
|
|
|
+ const fileNameRegex = /\/([^\\/]+)$/; // 匹配最后一个斜杠后的内容
|
|
|
+ const match = tempUrl[0].match(fileNameRegex);
|
|
|
+
|
|
|
+ if (match) {
|
|
|
+ return match[1];
|
|
|
+ } else {
|
|
|
+ return '';
|
|
|
+ }
|
|
|
+ };
|
|
|
+ const onDownload = async (e: MouseEvent) => {
|
|
|
+ e.stopPropagation();
|
|
|
+ e.preventDefault();
|
|
|
+ const item = props.item;
|
|
|
+ if (!item.content) {
|
|
|
+ message.error('下载失败');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (downloadStatus.value) return false;
|
|
|
+ downloadStatus.value = true;
|
|
|
+ const suffix: any = item.content?.split('.');
|
|
|
+ const fileName = item.title + '.' + suffix[suffix?.length - 1];
|
|
|
+ if (item.type === 'MUSIC') {
|
|
|
+ const { data } = await api_musicSheetDetail(item.content);
|
|
|
+ const urls = [];
|
|
|
+ if (data.xmlFileUrl) {
|
|
|
+ urls.push({
|
|
|
+ url: data.xmlFileUrl,
|
|
|
+ name: getFileName(data.xmlFileUrl)
|
|
|
+ });
|
|
|
+ }
|
|
|
+ if (data.background && data.background.length > 0) {
|
|
|
+ data.background.forEach((item: any) => {
|
|
|
+ urls.push({
|
|
|
+ url: item.audioFileUrl,
|
|
|
+ name: getFileName(item.audioFileUrl)
|
|
|
+ });
|
|
|
+ });
|
|
|
+ }
|
|
|
+ downLoadMultiFile(urls, item.title);
|
|
|
+
|
|
|
+ // setTimeout(() => {
|
|
|
+ // downloadStatus.value = false;
|
|
|
+ // }, 1000);
|
|
|
+ } else {
|
|
|
+ downloadFile(fileName, item.content);
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ return () => (
|
|
|
+ <div
|
|
|
+ onClick={() => emit('click', props.item)}
|
|
|
+ key={props.item.id}
|
|
|
+ draggable={!props.draggable ? false : props.item.exist ? false : true}
|
|
|
+ class={[
|
|
|
+ styles['card-section'],
|
|
|
+ 'card-section-container',
|
|
|
+ !props.draggable ? '' : props.item.exist ? '' : styles.cardDrag
|
|
|
+ ]}
|
|
|
+ onMouseenter={() => {
|
|
|
+ isAnimation.value = true;
|
|
|
+ }}
|
|
|
+ onMouseleave={() => {
|
|
|
+ isAnimation.value = false;
|
|
|
+ }}
|
|
|
+ onDragstart={(e: any) => {
|
|
|
+ console.log('dragstart', Date.now());
|
|
|
+ e.dataTransfer.setData('text', JSON.stringify(props.item));
|
|
|
+ }}>
|
|
|
+ {/* 判断是否下架 */}
|
|
|
+ {props.offShelf && (
|
|
|
+ <div class={styles.offShelfBg}>
|
|
|
+ <p class={styles.offShelfTips}>该资源已被下架</p>
|
|
|
+ <NButton
|
|
|
+ type="primary"
|
|
|
+ class={styles.offShelfBtn}
|
|
|
+ onClick={(e: MouseEvent) => {
|
|
|
+ e.stopPropagation();
|
|
|
+ emit('offShelf');
|
|
|
+ }}>
|
|
|
+ 确认
|
|
|
+ </NButton>
|
|
|
+ </div>
|
|
|
+ )}
|
|
|
+ <NCard
|
|
|
+ class={[
|
|
|
+ styles['card-section-content'],
|
|
|
+ props.isShowAdd ? '' : styles.course,
|
|
|
+ props.isActive ? styles.isActive : '',
|
|
|
+ props.item.exist ? styles.showAddBtn : '' // 是否已添加
|
|
|
+ ]}
|
|
|
+ style={{ cursor: 'pointer' }}>
|
|
|
+ {{
|
|
|
+ cover: () => (
|
|
|
+ <>
|
|
|
+ {/* 图片 */}
|
|
|
+ {props.item.type === 'IMG' && (
|
|
|
+ <NImage
|
|
|
+ class={[styles.cover, styles.image]}
|
|
|
+ lazy
|
|
|
+ previewDisabled={props.disabledMouseHover}
|
|
|
+ objectFit="cover"
|
|
|
+ src={props.item.coverImg}
|
|
|
+ previewSrc={props.item.content}
|
|
|
+ renderToolbar={({ nodes }: ImageRenderToolbarProps) => {
|
|
|
+ return [
|
|
|
+ nodes.prev,
|
|
|
+ nodes.next,
|
|
|
+ nodes.rotateCounterclockwise,
|
|
|
+ nodes.rotateClockwise,
|
|
|
+ nodes.resizeToOriginalSize,
|
|
|
+ nodes.zoomOut,
|
|
|
+ nodes.close
|
|
|
+ ];
|
|
|
+ }}
|
|
|
+ />
|
|
|
+ )}
|
|
|
+ {/* 乐谱 */}
|
|
|
+ {props.item.type === 'MUSIC' && (
|
|
|
+ <NImage
|
|
|
+ class={[styles.cover, styles.image]}
|
|
|
+ lazy
|
|
|
+ previewDisabled={true}
|
|
|
+ objectFit="contain"
|
|
|
+ src={props.item.coverImg}
|
|
|
+ />
|
|
|
+ )}
|
|
|
+ {/* 音频 */}
|
|
|
+ {props.item.type === 'SONG' && (
|
|
|
+ <AudioPlayer
|
|
|
+ content={props.item.content}
|
|
|
+ cover={props.item.coverImg}
|
|
|
+ previewDisabled={props.disabledMouseHover}
|
|
|
+ />
|
|
|
+ )}
|
|
|
+ {/* 视频 */}
|
|
|
+ {props.item.type === 'VIDEO' && (
|
|
|
+ <VideoPlayer
|
|
|
+ cover={props.item.coverImg}
|
|
|
+ content={props.item.content}
|
|
|
+ previewDisabled={props.disabledMouseHover}
|
|
|
+ />
|
|
|
+ )}
|
|
|
+ {/* ppt */}
|
|
|
+ {props.item.type === 'PPT' && (
|
|
|
+ <NImage
|
|
|
+ class={[styles.cover, styles.image]}
|
|
|
+ lazy
|
|
|
+ previewDisabled={true}
|
|
|
+ objectFit="cover"
|
|
|
+ src={props.item.coverImg || PageEnum.PPT_DEFAULT_COVER}
|
|
|
+ />
|
|
|
+ )}
|
|
|
+ {/* 节奏练习 */}
|
|
|
+ {props.item.type === 'RHYTHM' && (
|
|
|
+ <NImage
|
|
|
+ class={[styles.cover, styles.image]}
|
|
|
+ lazy
|
|
|
+ previewDisabled={true}
|
|
|
+ objectFit="cover"
|
|
|
+ src={props.item.coverImg || PageEnum.RHYTHM_DEFAULT_COVER}
|
|
|
+ />
|
|
|
+ )}
|
|
|
+
|
|
|
+ {/* 听音练习 */}
|
|
|
+ {props.item.type === 'LISTEN' && (
|
|
|
+ <NImage
|
|
|
+ class={[styles.cover, styles.image]}
|
|
|
+ lazy
|
|
|
+ previewDisabled={true}
|
|
|
+ objectFit="cover"
|
|
|
+ src={props.item.coverImg}
|
|
|
+ />
|
|
|
+ )}
|
|
|
+ {/* 乐理 */}
|
|
|
+ {props.item.type === 'THEORY' && (
|
|
|
+ <NImage
|
|
|
+ class={[styles.cover, styles.image]}
|
|
|
+ lazy
|
|
|
+ previewDisabled={true}
|
|
|
+ objectFit="cover"
|
|
|
+ src={props.item.coverImg || PageEnum.THEORY_DEFAULT_COVER}
|
|
|
+ />
|
|
|
+ )}
|
|
|
+ {/* 名曲 */}
|
|
|
+ {props.item.type === 'MUSIC_WIKI' && (
|
|
|
+ <NImage
|
|
|
+ class={[styles.cover, styles.image]}
|
|
|
+ lazy
|
|
|
+ previewDisabled={true}
|
|
|
+ objectFit="cover"
|
|
|
+ src={props.item.coverImg || PageEnum.MUSIC_DEFAULT_COVER}
|
|
|
+ />
|
|
|
+ )}
|
|
|
+ {/* 乐器 */}
|
|
|
+ {props.item.type === 'INSTRUMENT' && (
|
|
|
+ <NImage
|
|
|
+ class={[styles.cover, styles.image]}
|
|
|
+ lazy
|
|
|
+ previewDisabled={true}
|
|
|
+ objectFit="cover"
|
|
|
+ src={
|
|
|
+ props.item.coverImg || PageEnum.INSTRUMENT_DEFAULT_COVER
|
|
|
+ }
|
|
|
+ />
|
|
|
+ )}
|
|
|
+ {/* 音乐家 */}
|
|
|
+ {props.item.type === 'MUSICIAN' && (
|
|
|
+ <NImage
|
|
|
+ class={[styles.cover, styles.image]}
|
|
|
+ lazy
|
|
|
+ previewDisabled={true}
|
|
|
+ objectFit="cover"
|
|
|
+ src={props.item.coverImg || PageEnum.MUSICIAN_DEFAULT_COVER}
|
|
|
+ />
|
|
|
+ )}
|
|
|
+ </>
|
|
|
+ ),
|
|
|
+ footer: () => (
|
|
|
+ <div class={styles.footer}>
|
|
|
+ <div class={[styles.title, 'footerTitle']}>
|
|
|
+ <NImage
|
|
|
+ class={[styles.titleType]}
|
|
|
+ src={formatType(props.item.type)}
|
|
|
+ objectFit="cover"
|
|
|
+ />
|
|
|
+ <span class={[styles.titleContent, 'titleContent']}>
|
|
|
+ <TheNoticeBar
|
|
|
+ isAnimation={isAnimation.value}
|
|
|
+ text={props.item.title}
|
|
|
+ />
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+ {/* 收藏 */}
|
|
|
+ <div class={styles.btnGroup}>
|
|
|
+ {props.isDownload && (
|
|
|
+ <div class={styles.btnItem} onClick={onDownload}>
|
|
|
+ <NSpin show={downloadStatus.value} size={'small'}>
|
|
|
+ <img
|
|
|
+ src={iconDownload}
|
|
|
+ key="3"
|
|
|
+ class={[styles.iconCollect]}
|
|
|
+ />
|
|
|
+ </NSpin>
|
|
|
+ </div>
|
|
|
+ )}
|
|
|
+ {props.isShowCollect && (
|
|
|
+ <div
|
|
|
+ class={[styles.iconCollect, styles.btnItem]}
|
|
|
+ onClick={(e: MouseEvent) => {
|
|
|
+ e.stopPropagation();
|
|
|
+ e.preventDefault();
|
|
|
+ // 判断是否可以收藏
|
|
|
+ if (props.isCollect) {
|
|
|
+ emit('collect', props.item);
|
|
|
+ }
|
|
|
+ }}>
|
|
|
+ <Transition name="favitor" mode="out-in">
|
|
|
+ {props.item.isCollect ? (
|
|
|
+ <img
|
|
|
+ src={iconCollectActive}
|
|
|
+ key="1"
|
|
|
+ class={[
|
|
|
+ styles.iconCollect,
|
|
|
+ props.isCollect ? styles.isCollect : ''
|
|
|
+ ]}
|
|
|
+ />
|
|
|
+ ) : (
|
|
|
+ <img
|
|
|
+ src={iconCollectDefault}
|
|
|
+ key="2"
|
|
|
+ class={[
|
|
|
+ styles.iconCollect,
|
|
|
+ props.isCollect ? styles.isCollect : ''
|
|
|
+ ]}
|
|
|
+ />
|
|
|
+ )}
|
|
|
+ </Transition>
|
|
|
+ </div>
|
|
|
+ )}
|
|
|
+ </div>
|
|
|
+
|
|
|
+ {/* 精选 */}
|
|
|
+ {props.item.isSelected && (
|
|
|
+ <span class={styles.iconSelected}></span>
|
|
|
+ )}
|
|
|
+
|
|
|
+ {/* 添加按钮 */}
|
|
|
+ {props.isShowAdd &&
|
|
|
+ (props.item.exist ? (
|
|
|
+ <NButton
|
|
|
+ type="primary"
|
|
|
+ class={[
|
|
|
+ styles.addBtn,
|
|
|
+ props.item.exist ? styles.addBtnDisabled : ''
|
|
|
+ ]}
|
|
|
+ disabled={props.item.exist || props.isShowAddDisabled}
|
|
|
+ onClick={(e: MouseEvent) => {
|
|
|
+ e.stopPropagation();
|
|
|
+ e.preventDefault();
|
|
|
+ emit('add', props.item);
|
|
|
+ }}>
|
|
|
+ {props.item.exist ? '已添加' : '添加'}
|
|
|
+ </NButton>
|
|
|
+ ) : (
|
|
|
+ !props.isShowAddDisabled && (
|
|
|
+ <NButton
|
|
|
+ type="primary"
|
|
|
+ class={[
|
|
|
+ styles.addBtn,
|
|
|
+ props.item.exist ? styles.addBtnDisabled : ''
|
|
|
+ ]}
|
|
|
+ disabled={props.item.exist || props.isShowAddDisabled}
|
|
|
+ onClick={(e: MouseEvent) => {
|
|
|
+ e.stopPropagation();
|
|
|
+ e.preventDefault();
|
|
|
+ emit('add', props.item);
|
|
|
+ }}>
|
|
|
+ {props.item.exist ? '已添加' : '添加'}
|
|
|
+ </NButton>
|
|
|
+ )
|
|
|
+ ))}
|
|
|
+ </div>
|
|
|
+ )
|
|
|
+ }}
|
|
|
+ </NCard>
|
|
|
+ </div>
|
|
|
+ );
|
|
|
+ }
|
|
|
+});
|