|
@@ -1,587 +1,587 @@
|
|
|
-import {
|
|
|
- defineComponent,
|
|
|
- nextTick,
|
|
|
- onMounted,
|
|
|
- onUnmounted,
|
|
|
- reactive,
|
|
|
- ref
|
|
|
-} from 'vue';
|
|
|
-import styles from './index.module.less';
|
|
|
-import TheSearch from '/src/components/TheSearch';
|
|
|
-import recommendTitle from './images/reommon_title.png';
|
|
|
-import newTitle from './images/new_title.png';
|
|
|
-import hotTitle from './images/hot_title.png';
|
|
|
-import {
|
|
|
- NButton,
|
|
|
- NFormItem,
|
|
|
- NPopselect,
|
|
|
- NScrollbar,
|
|
|
- NSpace,
|
|
|
- NSpin
|
|
|
-} from 'naive-ui';
|
|
|
-import TheNoticeBar from '/src/components/TheNoticeBar';
|
|
|
-import { useRouter } from 'vue-router';
|
|
|
-import { api_musicSheetQueryByTag } from './api';
|
|
|
-import { api_musicSheetPage } from '../xiaoku-ai/api';
|
|
|
-import TheEmpty from '/src/components/TheEmpty';
|
|
|
-import titleBg from './images/title-bg.png';
|
|
|
-import { vaildMusicScoreUrl } from '/src/utils/urlUtils';
|
|
|
-import PreviewWindow from '../preview-window';
|
|
|
-import { useUserStore } from '/src/store/modules/users';
|
|
|
-import { state as baseState } from '/src/state';
|
|
|
-import { exitFullscreen, fscreen } from '/src/utils';
|
|
|
-import { useCatchStore } from '/src/store/modules/catchData';
|
|
|
-
|
|
|
-export const formatUsedNum = (num: number) => {
|
|
|
- if (num < 10000) {
|
|
|
- return num;
|
|
|
- } else {
|
|
|
- const n = num / 10000;
|
|
|
- return Number(n.toFixed(1)) + '万';
|
|
|
- }
|
|
|
-};
|
|
|
-
|
|
|
-export default defineComponent({
|
|
|
- name: 'xiaoku-list',
|
|
|
- setup() {
|
|
|
- const catchStore = useCatchStore();
|
|
|
- const router = useRouter();
|
|
|
- const userStore = useUserStore();
|
|
|
- const state = reactive({
|
|
|
- HOT: [] as any,
|
|
|
- NEW: [] as any,
|
|
|
- RECOMMEND: [] as any,
|
|
|
- isSearch: false, // 是否点击搜索
|
|
|
- keyword: null as any,
|
|
|
- detailLoading: false,
|
|
|
- list: [] as any,
|
|
|
- loading: false as any,
|
|
|
- finshed: false,
|
|
|
- instrumentId: null as any,
|
|
|
- page: 1,
|
|
|
- rows: 36,
|
|
|
- previewModal: false,
|
|
|
- previewParams: {
|
|
|
- type: '',
|
|
|
- src: ''
|
|
|
- } as any
|
|
|
- });
|
|
|
-
|
|
|
- const data = reactive({
|
|
|
- tags: [] as any[],
|
|
|
- tagIndex: 0,
|
|
|
- list: [] as any,
|
|
|
- loading: false
|
|
|
- });
|
|
|
-
|
|
|
- /** 获取三个模块 */
|
|
|
- const getDetail = async () => {
|
|
|
- state.detailLoading = true;
|
|
|
- try {
|
|
|
- const { data } = await api_musicSheetQueryByTag({
|
|
|
- rows: 20,
|
|
|
- musicalInstrumentId: state.instrumentId
|
|
|
- });
|
|
|
- state.RECOMMEND = data.RECOMMEND || [];
|
|
|
- state.HOT = data.HOT || [];
|
|
|
- state.NEW = data.NEW || [];
|
|
|
- } catch {
|
|
|
- //
|
|
|
- }
|
|
|
- state.detailLoading = false;
|
|
|
- };
|
|
|
-
|
|
|
- const selectChildObj = (item: any) => {
|
|
|
- const obj: any = {};
|
|
|
- item?.forEach((child: any) => {
|
|
|
- if (child.id === data.tagIndex) {
|
|
|
- obj.selected = true;
|
|
|
- obj.name = child.name;
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- return obj;
|
|
|
- };
|
|
|
-
|
|
|
- /** 获取更多 */
|
|
|
- const getList = async () => {
|
|
|
- if (state.page === 1) {
|
|
|
- state.loading = true;
|
|
|
- }
|
|
|
- try {
|
|
|
- const { data } = await api_musicSheetPage({
|
|
|
- name: state.keyword,
|
|
|
- musicalInstrumentId: state.instrumentId,
|
|
|
- page: state.page,
|
|
|
- rows: state.rows
|
|
|
- });
|
|
|
- const result = data.rows || [];
|
|
|
- result.forEach((item: any) => {
|
|
|
- if (item.musicSheetName) {
|
|
|
- const regex = new RegExp(state.keyword, 'gi');
|
|
|
- const highlightedText = item.musicSheetName.replace(
|
|
|
- regex,
|
|
|
- `<span>$&</span>`
|
|
|
- );
|
|
|
- item.musicNameReg = highlightedText;
|
|
|
- }
|
|
|
- });
|
|
|
- state.list = [...state.list, ...data.rows];
|
|
|
- state.finshed = state.page >= data.pages;
|
|
|
- } catch (e) {
|
|
|
- //
|
|
|
- console.log(e, 'e');
|
|
|
- }
|
|
|
- state.loading = false;
|
|
|
- };
|
|
|
-
|
|
|
- const spinRef = ref();
|
|
|
- const handleResh = () => {
|
|
|
- if (state.loading || state.finshed) return;
|
|
|
- state.page = state.page + 1;
|
|
|
- getList();
|
|
|
- };
|
|
|
-
|
|
|
- const __initSpin = () => {
|
|
|
- // const obv = new IntersectionObserver(entries => {
|
|
|
- // if (entries[0].intersectionRatio > 0) {
|
|
|
- // handleResh();
|
|
|
- // }
|
|
|
- // });
|
|
|
- // obv.observe(spinRef.value);
|
|
|
- };
|
|
|
-
|
|
|
- // 查看更多
|
|
|
- const onMore = (type?: '' | 'RECOMMEND' | 'NEW' | 'HOT') => {
|
|
|
- router.push({
|
|
|
- path: '/xiaoku-detail',
|
|
|
- query: {
|
|
|
- type: type
|
|
|
- }
|
|
|
- });
|
|
|
- };
|
|
|
-
|
|
|
- // 查看详情
|
|
|
- const onDetail = (item: any) => {
|
|
|
- // 默认进页面显示对应的曲谱
|
|
|
- const lineType = item.scoreType === 'FIRST'
|
|
|
- ? 'firstTone'
|
|
|
- : item.scoreType === 'JIAN'
|
|
|
- ? 'fixedTone'
|
|
|
- : item.scoreType === 'STAVE'
|
|
|
- ? 'staff'
|
|
|
- : 'firstTone';
|
|
|
- let src = `${vaildMusicScoreUrl()}/instrument?v=${+new Date()}&platform=pc&id=${
|
|
|
- item.id
|
|
|
- }&Authorization=${
|
|
|
- userStore.getToken
|
|
|
- }&musicRenderType=${lineType}&showGuide=true&part-index=${0}`;
|
|
|
-
|
|
|
- if (state.instrumentId) {
|
|
|
- src += '&instrumentId=' + state.instrumentId;
|
|
|
- }
|
|
|
-
|
|
|
- if (window.matchMedia('(display-mode: standalone)').matches) {
|
|
|
- baseState.application = window.matchMedia(
|
|
|
- '(display-mode: standalone)'
|
|
|
- ).matches;
|
|
|
- state.previewModal = true;
|
|
|
- fscreen();
|
|
|
- state.previewParams = {
|
|
|
- type: 'music',
|
|
|
- src
|
|
|
- };
|
|
|
- } else {
|
|
|
- window.open(src, +new Date() + '');
|
|
|
- }
|
|
|
- };
|
|
|
-
|
|
|
- const iframeHandle = (ev: MessageEvent) => {
|
|
|
- console.log(ev.data)
|
|
|
- if (ev.data?.api === 'back') {
|
|
|
- exitFullscreen();
|
|
|
- state.previewModal = !state.previewModal;
|
|
|
- }
|
|
|
- };
|
|
|
-
|
|
|
- const onSearch = () => {
|
|
|
- state.isSearch = true;
|
|
|
- state.loading = true;
|
|
|
- state.finshed = false;
|
|
|
- state.list = [];
|
|
|
- getList();
|
|
|
- nextTick(() => {
|
|
|
- __initSpin();
|
|
|
- });
|
|
|
- };
|
|
|
-
|
|
|
- /** 默认选中第一个声部 */
|
|
|
- const formatFirstSubject = () => {
|
|
|
- const tempSubjects = catchStore.getSubjectInstrumentOnly;
|
|
|
- if (tempSubjects.length > 0) {
|
|
|
- const firstSubject = tempSubjects[0];
|
|
|
- if (firstSubject.instruments && firstSubject.instruments.length > 1) {
|
|
|
- data.tagIndex = firstSubject.instruments[0]?.value;
|
|
|
- state.instrumentId = firstSubject.instruments[0]?.value;
|
|
|
- } else {
|
|
|
- data.tagIndex = firstSubject.instruments[0]?.value;
|
|
|
- state.instrumentId = firstSubject.value;
|
|
|
- }
|
|
|
- }
|
|
|
- };
|
|
|
-
|
|
|
- onMounted(async () => {
|
|
|
- // 获取教材分类列表
|
|
|
- await catchStore.getSubjects();
|
|
|
- formatFirstSubject();
|
|
|
-
|
|
|
- getDetail();
|
|
|
-
|
|
|
- window.addEventListener('message', iframeHandle);
|
|
|
- });
|
|
|
-
|
|
|
- onUnmounted(() => {
|
|
|
- window.removeEventListener('message', iframeHandle);
|
|
|
- });
|
|
|
- return () => (
|
|
|
- <div class={styles.list}>
|
|
|
- <h2 class={styles.topTitle}>
|
|
|
- <img src={titleBg} />
|
|
|
-
|
|
|
- <div class={styles.moreMusic} onClick={() => onMore()}>
|
|
|
- 全部曲目
|
|
|
- </div>
|
|
|
- </h2>
|
|
|
-
|
|
|
- <div class={styles.searchSection}>
|
|
|
- <NFormItem class={styles.tags} showLabel={false}>
|
|
|
- <NSpace size={[12, 20]}>
|
|
|
- {catchStore.getSubjectInstrumentOnly.map((item: any) =>
|
|
|
- item.instruments && item.instruments.length > 1 ? (
|
|
|
- <NPopselect
|
|
|
- options={item.instruments}
|
|
|
- trigger="hover"
|
|
|
- v-model:value={data.tagIndex}
|
|
|
- scrollable
|
|
|
- onUpdate:value={() => {
|
|
|
- state.instrumentId = data.tagIndex;
|
|
|
- state.isSearch = state.keyword ? true : false
|
|
|
- if (state.isSearch) {
|
|
|
- onSearch();
|
|
|
- } else {
|
|
|
- getDetail();
|
|
|
- }
|
|
|
- }}
|
|
|
- key={item.value}
|
|
|
- class={[styles.popSelect1]}>
|
|
|
- <NButton
|
|
|
- round
|
|
|
- class={[
|
|
|
- styles.textBtn,
|
|
|
- selectChildObj(item.instruments).selected &&
|
|
|
- styles.textBtnActive
|
|
|
- ]}>
|
|
|
- {selectChildObj(item.instruments).name || item.name}
|
|
|
- <i class={styles.iconArrow}></i>
|
|
|
- </NButton>
|
|
|
- </NPopselect>
|
|
|
- ) : (
|
|
|
- <NButton
|
|
|
- round
|
|
|
- class={[
|
|
|
- styles.textBtn,
|
|
|
- state.instrumentId === (item.value || 0) &&
|
|
|
- styles.textBtnActive
|
|
|
- ]}
|
|
|
- onClick={() => {
|
|
|
- data.tagIndex = item.value || 0;
|
|
|
- state.instrumentId = item.value;
|
|
|
- state.isSearch = state.keyword ? true : false
|
|
|
- if (state.isSearch) {
|
|
|
- onSearch();
|
|
|
- } else {
|
|
|
- getDetail();
|
|
|
- }
|
|
|
- }}>
|
|
|
- {item.name}
|
|
|
- </NButton>
|
|
|
- )
|
|
|
- )}
|
|
|
- </NSpace>
|
|
|
- </NFormItem>
|
|
|
- <TheSearch
|
|
|
- round
|
|
|
- class={styles.searchInput}
|
|
|
- placeholder="请输入曲目名称"
|
|
|
- value={state.keyword}
|
|
|
- onUpdate:value={(val: string) => {
|
|
|
- state.keyword = val
|
|
|
- }}
|
|
|
- onSearch={val => {
|
|
|
- if (val && val.trim()) {
|
|
|
- state.keyword = val.trim();
|
|
|
- onSearch();
|
|
|
- } else {
|
|
|
- state.isSearch = false;
|
|
|
- getDetail();
|
|
|
- }
|
|
|
- }}
|
|
|
- />
|
|
|
- </div>
|
|
|
-
|
|
|
- {state.isSearch ? (
|
|
|
- <div class={styles.searchContainer}>
|
|
|
- <NScrollbar
|
|
|
- class={
|
|
|
- !state.loading && state.list.length === 0
|
|
|
- ? styles.emptyScrollBar
|
|
|
- : ''
|
|
|
- }
|
|
|
- onScroll={async (e: any) => {
|
|
|
- const clientHeight = e.target?.clientHeight;
|
|
|
- const scrollTop = e.target?.scrollTop;
|
|
|
- const scrollHeight = e.target?.scrollHeight;
|
|
|
- // 是否到底,是否加载完
|
|
|
- if (
|
|
|
- clientHeight + scrollTop + 20 >= scrollHeight &&
|
|
|
- !state.finshed &&
|
|
|
- !state.loading
|
|
|
- ) {
|
|
|
- state.page = state.page + 1;
|
|
|
- await getList();
|
|
|
- }
|
|
|
- }}>
|
|
|
- <NSpin show={state.loading}>
|
|
|
- <div class={styles.sectionContainer}>
|
|
|
- <div>
|
|
|
- {state.list.map((item: any) => (
|
|
|
- <div
|
|
|
- class={styles.sectionItem}
|
|
|
- onClick={() => onDetail(item)}>
|
|
|
- <div class={styles.img} style={{ marginLeft: 0 }}>
|
|
|
- <img
|
|
|
- referrerpolicy="no-referrer"
|
|
|
- src={item.titleImg}
|
|
|
- />
|
|
|
- </div>
|
|
|
- <div class={styles.infos}>
|
|
|
- <div
|
|
|
- class={styles.topName}
|
|
|
- v-html={item.musicNameReg}></div>
|
|
|
- <div class={styles.types}>
|
|
|
- <div class={styles.hot}>
|
|
|
- <span>{formatUsedNum(item.usedNum)}</span>
|
|
|
- </div>
|
|
|
- {item.audioPlayTypes?.includes('SING') && (
|
|
|
- <div class={styles.sing}>演唱</div>
|
|
|
- )}
|
|
|
- {item.audioPlayTypes?.includes('PLAY') && (
|
|
|
- <div class={styles.song}>演奏</div>
|
|
|
- )}
|
|
|
-
|
|
|
- <div class={styles.author}>{item.composer}</div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- ))}
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </NSpin>
|
|
|
- {/* <div
|
|
|
- // ref={spinRef}
|
|
|
- class={[
|
|
|
- styles.loadingWrap,
|
|
|
- state.finshed && !state.loading && styles.showLoading
|
|
|
- ]}>
|
|
|
- <NSpin show={true}></NSpin>
|
|
|
- </div> */}
|
|
|
- </NScrollbar>
|
|
|
-
|
|
|
- {!state.loading && state.list.length === 0 && (
|
|
|
- <div class={styles.empty}>
|
|
|
- <TheEmpty description="暂无曲目"></TheEmpty>
|
|
|
- </div>
|
|
|
- )}
|
|
|
- </div>
|
|
|
- ) : (
|
|
|
- <div class={styles.container}>
|
|
|
- <NSpin show={state.detailLoading}>
|
|
|
- <div class={[styles.section, styles.recommendSection]}>
|
|
|
- <div class={styles.sectionTitle}>
|
|
|
- <img src={recommendTitle} class={styles.imgTitle} />
|
|
|
-
|
|
|
- <div
|
|
|
- class={styles.moreBtn}
|
|
|
- onClick={() => onMore('RECOMMEND')}>
|
|
|
- 更多
|
|
|
- </div>
|
|
|
- </div>
|
|
|
-
|
|
|
- <NScrollbar>
|
|
|
- {state.RECOMMEND.map((item: any, index: number) => (
|
|
|
- <div
|
|
|
- class={styles.item}
|
|
|
- onClick={() => {
|
|
|
- onDetail(item);
|
|
|
- }}>
|
|
|
- <div
|
|
|
- class={[
|
|
|
- styles.num,
|
|
|
- index === 0 ? styles.num1 : '',
|
|
|
- index === 1 ? styles.num2 : '',
|
|
|
- index === 2 ? styles.num3 : ''
|
|
|
- ]}>
|
|
|
- {(index + 1).toString().padStart(2, '0')}
|
|
|
- </div>
|
|
|
- <div class={styles.img}>
|
|
|
- <img referrerpolicy="no-referrer" src={item.titleImg} />
|
|
|
- </div>
|
|
|
- <div class={styles.infos}>
|
|
|
- <div class={styles.topName}>
|
|
|
- <TheNoticeBar text={item.musicSheetName} />
|
|
|
- </div>
|
|
|
- <div class={styles.types}>
|
|
|
- <div class={styles.hot}>
|
|
|
- <span>{formatUsedNum(item.usedNum)}</span>
|
|
|
- </div>
|
|
|
- {item.audioPlayTypes?.includes('SING') && (
|
|
|
- <div class={styles.sing}>演唱</div>
|
|
|
- )}
|
|
|
- {item.audioPlayTypes?.includes('PLAY') && (
|
|
|
- <div class={styles.song}>演奏</div>
|
|
|
- )}
|
|
|
-
|
|
|
- <div class={styles.author}>{item.composer}</div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- ))}
|
|
|
-
|
|
|
- {!state.detailLoading && state.RECOMMEND.length === 0 && (
|
|
|
- <div class={styles.empty}>
|
|
|
- <TheEmpty description="暂无曲目"></TheEmpty>
|
|
|
- </div>
|
|
|
- )}
|
|
|
- </NScrollbar>
|
|
|
- </div>
|
|
|
- <div class={[styles.section, styles.hotSection]}>
|
|
|
- <div class={styles.sectionTitle}>
|
|
|
- <img src={hotTitle} class={styles.imgTitle} />
|
|
|
- <div class={styles.moreBtn} onClick={() => onMore('HOT')}>
|
|
|
- 更多
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- <NScrollbar>
|
|
|
- {state.HOT.map((item: any, index: number) => (
|
|
|
- <div
|
|
|
- class={styles.item}
|
|
|
- onClick={() => {
|
|
|
- onDetail(item);
|
|
|
- }}>
|
|
|
- <div
|
|
|
- class={[
|
|
|
- styles.num,
|
|
|
- index === 0 ? styles.num1 : '',
|
|
|
- index === 1 ? styles.num2 : '',
|
|
|
- index === 2 ? styles.num3 : ''
|
|
|
- ]}>
|
|
|
- {(index + 1).toString().padStart(2, '0')}
|
|
|
- </div>
|
|
|
- <div class={styles.img}>
|
|
|
- <img referrerpolicy="no-referrer" src={item.titleImg} />
|
|
|
- </div>
|
|
|
- <div class={styles.infos}>
|
|
|
- <div class={styles.topName}>
|
|
|
- <TheNoticeBar text={item.musicSheetName} />
|
|
|
- </div>
|
|
|
- <div class={styles.types}>
|
|
|
- <div class={styles.hot}>
|
|
|
- <span>{formatUsedNum(item.usedNum)}</span>
|
|
|
- </div>
|
|
|
- {item.audioPlayTypes?.includes('SING') && (
|
|
|
- <div class={styles.sing}>演唱</div>
|
|
|
- )}
|
|
|
- {item.audioPlayTypes?.includes('PLAY') && (
|
|
|
- <div class={styles.song}>演奏</div>
|
|
|
- )}
|
|
|
-
|
|
|
- <div class={styles.author}>{item.composer}</div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- ))}
|
|
|
- {!state.detailLoading && state.HOT.length === 0 && (
|
|
|
- <div class={styles.empty}>
|
|
|
- <TheEmpty description="暂无曲目"></TheEmpty>
|
|
|
- </div>
|
|
|
- )}
|
|
|
- </NScrollbar>
|
|
|
- </div>
|
|
|
- <div class={[styles.section, styles.newSection]}>
|
|
|
- <div class={styles.sectionTitle}>
|
|
|
- <img src={newTitle} class={styles.imgTitle} />
|
|
|
-
|
|
|
- <div class={styles.moreBtn} onClick={() => onMore('NEW')}>
|
|
|
- 更多
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- <NScrollbar>
|
|
|
- {state.NEW.map((item: any, index: number) => (
|
|
|
- <div
|
|
|
- class={styles.item}
|
|
|
- onClick={() => {
|
|
|
- onDetail(item);
|
|
|
- }}>
|
|
|
- <div
|
|
|
- class={[
|
|
|
- styles.num,
|
|
|
- index === 0 ? styles.num1 : '',
|
|
|
- index === 1 ? styles.num2 : '',
|
|
|
- index === 2 ? styles.num3 : ''
|
|
|
- ]}>
|
|
|
- {(index + 1).toString().padStart(2, '0')}
|
|
|
- </div>
|
|
|
- <div class={styles.img}>
|
|
|
- <img referrerpolicy="no-referrer" src={item.titleImg} />
|
|
|
- </div>
|
|
|
- <div class={styles.infos}>
|
|
|
- <div class={styles.topName}>
|
|
|
- <TheNoticeBar text={item.musicSheetName} />
|
|
|
- </div>
|
|
|
- <div class={styles.types}>
|
|
|
- <div class={styles.hot}>
|
|
|
- <span>{formatUsedNum(item.usedNum)}</span>
|
|
|
- </div>
|
|
|
- {item.audioPlayTypes?.includes('SING') && (
|
|
|
- <div class={styles.sing}>演唱</div>
|
|
|
- )}
|
|
|
- {item.audioPlayTypes?.includes('PLAY') && (
|
|
|
- <div class={styles.song}>演奏</div>
|
|
|
- )}
|
|
|
-
|
|
|
- <div class={styles.author}>{item.composer}</div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- ))}
|
|
|
- {!state.detailLoading && state.NEW.length === 0 && (
|
|
|
- <div class={styles.empty}>
|
|
|
- <TheEmpty description="暂无曲目"></TheEmpty>
|
|
|
- </div>
|
|
|
- )}
|
|
|
- </NScrollbar>
|
|
|
- </div>
|
|
|
- </NSpin>
|
|
|
- </div>
|
|
|
- )}
|
|
|
-
|
|
|
- {/* 应用内预览*/}
|
|
|
- <PreviewWindow
|
|
|
- v-model:show={state.previewModal}
|
|
|
- type="music"
|
|
|
- params={state.previewParams}
|
|
|
- />
|
|
|
- </div>
|
|
|
- );
|
|
|
- }
|
|
|
-});
|
|
|
+import {
|
|
|
+ defineComponent,
|
|
|
+ nextTick,
|
|
|
+ onMounted,
|
|
|
+ onUnmounted,
|
|
|
+ reactive,
|
|
|
+ ref
|
|
|
+} from 'vue';
|
|
|
+import styles from './index.module.less';
|
|
|
+import TheSearch from '/src/components/TheSearch';
|
|
|
+import recommendTitle from './images/reommon_title.png';
|
|
|
+import newTitle from './images/new_title.png';
|
|
|
+import hotTitle from './images/hot_title.png';
|
|
|
+import {
|
|
|
+ NButton,
|
|
|
+ NFormItem,
|
|
|
+ NPopselect,
|
|
|
+ NScrollbar,
|
|
|
+ NSpace,
|
|
|
+ NSpin
|
|
|
+} from 'naive-ui';
|
|
|
+import TheNoticeBar from '/src/components/TheNoticeBar';
|
|
|
+import { useRouter } from 'vue-router';
|
|
|
+import { api_musicSheetQueryByTag } from './api';
|
|
|
+import { api_musicSheetPage } from '../xiaoku-ai/api';
|
|
|
+import TheEmpty from '/src/components/TheEmpty';
|
|
|
+import titleBg from './images/title-bg.png';
|
|
|
+import { vaildMusicScoreUrl } from '/src/utils/urlUtils';
|
|
|
+import PreviewWindow from '../preview-window';
|
|
|
+import { useUserStore } from '/src/store/modules/users';
|
|
|
+import { state as baseState } from '/src/state';
|
|
|
+import { exitFullscreen, fscreen } from '/src/utils';
|
|
|
+import { useCatchStore } from '/src/store/modules/catchData';
|
|
|
+
|
|
|
+export const formatUsedNum = (num: number) => {
|
|
|
+ if (num < 10000) {
|
|
|
+ return num;
|
|
|
+ } else {
|
|
|
+ const n = num / 10000;
|
|
|
+ return Number(n.toFixed(1)) + '万';
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+export default defineComponent({
|
|
|
+ name: 'xiaoku-list',
|
|
|
+ setup() {
|
|
|
+ const catchStore = useCatchStore();
|
|
|
+ const router = useRouter();
|
|
|
+ const userStore = useUserStore();
|
|
|
+ const state = reactive({
|
|
|
+ HOT: [] as any,
|
|
|
+ NEW: [] as any,
|
|
|
+ RECOMMEND: [] as any,
|
|
|
+ isSearch: false, // 是否点击搜索
|
|
|
+ keyword: null as any,
|
|
|
+ detailLoading: false,
|
|
|
+ list: [] as any,
|
|
|
+ loading: false as any,
|
|
|
+ finshed: false,
|
|
|
+ instrumentId: null as any,
|
|
|
+ page: 1,
|
|
|
+ rows: 36,
|
|
|
+ previewModal: false,
|
|
|
+ previewParams: {
|
|
|
+ type: '',
|
|
|
+ src: ''
|
|
|
+ } as any
|
|
|
+ });
|
|
|
+
|
|
|
+ const data = reactive({
|
|
|
+ tags: [] as any[],
|
|
|
+ tagIndex: 0,
|
|
|
+ list: [] as any,
|
|
|
+ loading: false
|
|
|
+ });
|
|
|
+
|
|
|
+ /** 获取三个模块 */
|
|
|
+ const getDetail = async () => {
|
|
|
+ state.detailLoading = true;
|
|
|
+ try {
|
|
|
+ const { data } = await api_musicSheetQueryByTag({
|
|
|
+ rows: 20,
|
|
|
+ musicalInstrumentId: state.instrumentId
|
|
|
+ });
|
|
|
+ state.RECOMMEND = data.RECOMMEND || [];
|
|
|
+ state.HOT = data.HOT || [];
|
|
|
+ state.NEW = data.NEW || [];
|
|
|
+ } catch {
|
|
|
+ //
|
|
|
+ }
|
|
|
+ state.detailLoading = false;
|
|
|
+ };
|
|
|
+
|
|
|
+ const selectChildObj = (item: any) => {
|
|
|
+ const obj: any = {};
|
|
|
+ item?.forEach((child: any) => {
|
|
|
+ if (child.id === data.tagIndex) {
|
|
|
+ obj.selected = true;
|
|
|
+ obj.name = child.name;
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ return obj;
|
|
|
+ };
|
|
|
+
|
|
|
+ /** 获取更多 */
|
|
|
+ const getList = async () => {
|
|
|
+ if (state.page === 1) {
|
|
|
+ state.loading = true;
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ const { data } = await api_musicSheetPage({
|
|
|
+ name: state.keyword,
|
|
|
+ musicalInstrumentId: state.instrumentId,
|
|
|
+ page: state.page,
|
|
|
+ rows: state.rows
|
|
|
+ });
|
|
|
+ const result = data.rows || [];
|
|
|
+ result.forEach((item: any) => {
|
|
|
+ if (item.musicSheetName) {
|
|
|
+ const regex = new RegExp(state.keyword, 'gi');
|
|
|
+ const highlightedText = item.musicSheetName.replace(
|
|
|
+ regex,
|
|
|
+ `<span>$&</span>`
|
|
|
+ );
|
|
|
+ item.musicNameReg = highlightedText;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ state.list = [...state.list, ...data.rows];
|
|
|
+ state.finshed = state.page >= data.pages;
|
|
|
+ } catch (e) {
|
|
|
+ //
|
|
|
+ console.log(e, 'e');
|
|
|
+ }
|
|
|
+ state.loading = false;
|
|
|
+ };
|
|
|
+
|
|
|
+ const spinRef = ref();
|
|
|
+ const handleResh = () => {
|
|
|
+ if (state.loading || state.finshed) return;
|
|
|
+ state.page = state.page + 1;
|
|
|
+ getList();
|
|
|
+ };
|
|
|
+
|
|
|
+ const __initSpin = () => {
|
|
|
+ // const obv = new IntersectionObserver(entries => {
|
|
|
+ // if (entries[0].intersectionRatio > 0) {
|
|
|
+ // handleResh();
|
|
|
+ // }
|
|
|
+ // });
|
|
|
+ // obv.observe(spinRef.value);
|
|
|
+ };
|
|
|
+
|
|
|
+ // 查看更多
|
|
|
+ const onMore = (type?: '' | 'RECOMMEND' | 'NEW' | 'HOT') => {
|
|
|
+ router.push({
|
|
|
+ path: '/xiaoku-detail',
|
|
|
+ query: {
|
|
|
+ type: type
|
|
|
+ }
|
|
|
+ });
|
|
|
+ };
|
|
|
+
|
|
|
+ // 查看详情
|
|
|
+ const onDetail = (item: any) => {
|
|
|
+ // 默认进页面显示对应的曲谱
|
|
|
+ const lineType = item.scoreType === 'FIRST'
|
|
|
+ ? 'firstTone'
|
|
|
+ : item.scoreType === 'JIAN'
|
|
|
+ ? 'fixedTone'
|
|
|
+ : item.scoreType === 'STAVE'
|
|
|
+ ? 'staff'
|
|
|
+ : 'firstTone';
|
|
|
+ let src = `${vaildMusicScoreUrl()}/instrument?v=${+new Date()}&platform=pc&id=${
|
|
|
+ item.id
|
|
|
+ }&Authorization=${
|
|
|
+ userStore.getToken
|
|
|
+ }&musicRenderType=${lineType}&showGuide=true`;
|
|
|
+
|
|
|
+ if (state.instrumentId) {
|
|
|
+ src += '&instrumentId=' + state.instrumentId;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (window.matchMedia('(display-mode: standalone)').matches) {
|
|
|
+ baseState.application = window.matchMedia(
|
|
|
+ '(display-mode: standalone)'
|
|
|
+ ).matches;
|
|
|
+ state.previewModal = true;
|
|
|
+ fscreen();
|
|
|
+ state.previewParams = {
|
|
|
+ type: 'music',
|
|
|
+ src
|
|
|
+ };
|
|
|
+ } else {
|
|
|
+ window.open(src, +new Date() + '');
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ const iframeHandle = (ev: MessageEvent) => {
|
|
|
+ console.log(ev.data)
|
|
|
+ if (ev.data?.api === 'back') {
|
|
|
+ exitFullscreen();
|
|
|
+ state.previewModal = !state.previewModal;
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ const onSearch = () => {
|
|
|
+ state.isSearch = true;
|
|
|
+ state.loading = true;
|
|
|
+ state.finshed = false;
|
|
|
+ state.list = [];
|
|
|
+ getList();
|
|
|
+ nextTick(() => {
|
|
|
+ __initSpin();
|
|
|
+ });
|
|
|
+ };
|
|
|
+
|
|
|
+ /** 默认选中第一个声部 */
|
|
|
+ const formatFirstSubject = () => {
|
|
|
+ const tempSubjects = catchStore.getSubjectInstrumentOnly;
|
|
|
+ if (tempSubjects.length > 0) {
|
|
|
+ const firstSubject = tempSubjects[0];
|
|
|
+ if (firstSubject.instruments && firstSubject.instruments.length > 1) {
|
|
|
+ data.tagIndex = firstSubject.instruments[0]?.value;
|
|
|
+ state.instrumentId = firstSubject.instruments[0]?.value;
|
|
|
+ } else {
|
|
|
+ data.tagIndex = firstSubject.instruments[0]?.value;
|
|
|
+ state.instrumentId = firstSubject.value;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ onMounted(async () => {
|
|
|
+ // 获取教材分类列表
|
|
|
+ await catchStore.getSubjects();
|
|
|
+ formatFirstSubject();
|
|
|
+
|
|
|
+ getDetail();
|
|
|
+
|
|
|
+ window.addEventListener('message', iframeHandle);
|
|
|
+ });
|
|
|
+
|
|
|
+ onUnmounted(() => {
|
|
|
+ window.removeEventListener('message', iframeHandle);
|
|
|
+ });
|
|
|
+ return () => (
|
|
|
+ <div class={styles.list}>
|
|
|
+ <h2 class={styles.topTitle}>
|
|
|
+ <img src={titleBg} />
|
|
|
+
|
|
|
+ <div class={styles.moreMusic} onClick={() => onMore()}>
|
|
|
+ 全部曲目
|
|
|
+ </div>
|
|
|
+ </h2>
|
|
|
+
|
|
|
+ <div class={styles.searchSection}>
|
|
|
+ <NFormItem class={styles.tags} showLabel={false}>
|
|
|
+ <NSpace size={[12, 20]}>
|
|
|
+ {catchStore.getSubjectInstrumentOnly.map((item: any) =>
|
|
|
+ item.instruments && item.instruments.length > 1 ? (
|
|
|
+ <NPopselect
|
|
|
+ options={item.instruments}
|
|
|
+ trigger="hover"
|
|
|
+ v-model:value={data.tagIndex}
|
|
|
+ scrollable
|
|
|
+ onUpdate:value={() => {
|
|
|
+ state.instrumentId = data.tagIndex;
|
|
|
+ state.isSearch = state.keyword ? true : false
|
|
|
+ if (state.isSearch) {
|
|
|
+ onSearch();
|
|
|
+ } else {
|
|
|
+ getDetail();
|
|
|
+ }
|
|
|
+ }}
|
|
|
+ key={item.value}
|
|
|
+ class={[styles.popSelect1]}>
|
|
|
+ <NButton
|
|
|
+ round
|
|
|
+ class={[
|
|
|
+ styles.textBtn,
|
|
|
+ selectChildObj(item.instruments).selected &&
|
|
|
+ styles.textBtnActive
|
|
|
+ ]}>
|
|
|
+ {selectChildObj(item.instruments).name || item.name}
|
|
|
+ <i class={styles.iconArrow}></i>
|
|
|
+ </NButton>
|
|
|
+ </NPopselect>
|
|
|
+ ) : (
|
|
|
+ <NButton
|
|
|
+ round
|
|
|
+ class={[
|
|
|
+ styles.textBtn,
|
|
|
+ state.instrumentId === (item.value || 0) &&
|
|
|
+ styles.textBtnActive
|
|
|
+ ]}
|
|
|
+ onClick={() => {
|
|
|
+ data.tagIndex = item.value || 0;
|
|
|
+ state.instrumentId = item.value;
|
|
|
+ state.isSearch = state.keyword ? true : false
|
|
|
+ if (state.isSearch) {
|
|
|
+ onSearch();
|
|
|
+ } else {
|
|
|
+ getDetail();
|
|
|
+ }
|
|
|
+ }}>
|
|
|
+ {item.name}
|
|
|
+ </NButton>
|
|
|
+ )
|
|
|
+ )}
|
|
|
+ </NSpace>
|
|
|
+ </NFormItem>
|
|
|
+ <TheSearch
|
|
|
+ round
|
|
|
+ class={styles.searchInput}
|
|
|
+ placeholder="请输入曲目名称"
|
|
|
+ value={state.keyword}
|
|
|
+ onUpdate:value={(val: string) => {
|
|
|
+ state.keyword = val
|
|
|
+ }}
|
|
|
+ onSearch={val => {
|
|
|
+ if (val && val.trim()) {
|
|
|
+ state.keyword = val.trim();
|
|
|
+ onSearch();
|
|
|
+ } else {
|
|
|
+ state.isSearch = false;
|
|
|
+ getDetail();
|
|
|
+ }
|
|
|
+ }}
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+
|
|
|
+ {state.isSearch ? (
|
|
|
+ <div class={styles.searchContainer}>
|
|
|
+ <NScrollbar
|
|
|
+ class={
|
|
|
+ !state.loading && state.list.length === 0
|
|
|
+ ? styles.emptyScrollBar
|
|
|
+ : ''
|
|
|
+ }
|
|
|
+ onScroll={async (e: any) => {
|
|
|
+ const clientHeight = e.target?.clientHeight;
|
|
|
+ const scrollTop = e.target?.scrollTop;
|
|
|
+ const scrollHeight = e.target?.scrollHeight;
|
|
|
+ // 是否到底,是否加载完
|
|
|
+ if (
|
|
|
+ clientHeight + scrollTop + 20 >= scrollHeight &&
|
|
|
+ !state.finshed &&
|
|
|
+ !state.loading
|
|
|
+ ) {
|
|
|
+ state.page = state.page + 1;
|
|
|
+ await getList();
|
|
|
+ }
|
|
|
+ }}>
|
|
|
+ <NSpin show={state.loading}>
|
|
|
+ <div class={styles.sectionContainer}>
|
|
|
+ <div>
|
|
|
+ {state.list.map((item: any) => (
|
|
|
+ <div
|
|
|
+ class={styles.sectionItem}
|
|
|
+ onClick={() => onDetail(item)}>
|
|
|
+ <div class={styles.img} style={{ marginLeft: 0 }}>
|
|
|
+ <img
|
|
|
+ referrerpolicy="no-referrer"
|
|
|
+ src={item.titleImg}
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ <div class={styles.infos}>
|
|
|
+ <div
|
|
|
+ class={styles.topName}
|
|
|
+ v-html={item.musicNameReg}></div>
|
|
|
+ <div class={styles.types}>
|
|
|
+ <div class={styles.hot}>
|
|
|
+ <span>{formatUsedNum(item.usedNum)}</span>
|
|
|
+ </div>
|
|
|
+ {item.audioPlayTypes?.includes('SING') && (
|
|
|
+ <div class={styles.sing}>演唱</div>
|
|
|
+ )}
|
|
|
+ {item.audioPlayTypes?.includes('PLAY') && (
|
|
|
+ <div class={styles.song}>演奏</div>
|
|
|
+ )}
|
|
|
+
|
|
|
+ <div class={styles.author}>{item.composer}</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ ))}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </NSpin>
|
|
|
+ {/* <div
|
|
|
+ // ref={spinRef}
|
|
|
+ class={[
|
|
|
+ styles.loadingWrap,
|
|
|
+ state.finshed && !state.loading && styles.showLoading
|
|
|
+ ]}>
|
|
|
+ <NSpin show={true}></NSpin>
|
|
|
+ </div> */}
|
|
|
+ </NScrollbar>
|
|
|
+
|
|
|
+ {!state.loading && state.list.length === 0 && (
|
|
|
+ <div class={styles.empty}>
|
|
|
+ <TheEmpty description="暂无曲目"></TheEmpty>
|
|
|
+ </div>
|
|
|
+ )}
|
|
|
+ </div>
|
|
|
+ ) : (
|
|
|
+ <div class={styles.container}>
|
|
|
+ <NSpin show={state.detailLoading}>
|
|
|
+ <div class={[styles.section, styles.recommendSection]}>
|
|
|
+ <div class={styles.sectionTitle}>
|
|
|
+ <img src={recommendTitle} class={styles.imgTitle} />
|
|
|
+
|
|
|
+ <div
|
|
|
+ class={styles.moreBtn}
|
|
|
+ onClick={() => onMore('RECOMMEND')}>
|
|
|
+ 更多
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <NScrollbar>
|
|
|
+ {state.RECOMMEND.map((item: any, index: number) => (
|
|
|
+ <div
|
|
|
+ class={styles.item}
|
|
|
+ onClick={() => {
|
|
|
+ onDetail(item);
|
|
|
+ }}>
|
|
|
+ <div
|
|
|
+ class={[
|
|
|
+ styles.num,
|
|
|
+ index === 0 ? styles.num1 : '',
|
|
|
+ index === 1 ? styles.num2 : '',
|
|
|
+ index === 2 ? styles.num3 : ''
|
|
|
+ ]}>
|
|
|
+ {(index + 1).toString().padStart(2, '0')}
|
|
|
+ </div>
|
|
|
+ <div class={styles.img}>
|
|
|
+ <img referrerpolicy="no-referrer" src={item.titleImg} />
|
|
|
+ </div>
|
|
|
+ <div class={styles.infos}>
|
|
|
+ <div class={styles.topName}>
|
|
|
+ <TheNoticeBar text={item.musicSheetName} />
|
|
|
+ </div>
|
|
|
+ <div class={styles.types}>
|
|
|
+ <div class={styles.hot}>
|
|
|
+ <span>{formatUsedNum(item.usedNum)}</span>
|
|
|
+ </div>
|
|
|
+ {item.audioPlayTypes?.includes('SING') && (
|
|
|
+ <div class={styles.sing}>演唱</div>
|
|
|
+ )}
|
|
|
+ {item.audioPlayTypes?.includes('PLAY') && (
|
|
|
+ <div class={styles.song}>演奏</div>
|
|
|
+ )}
|
|
|
+
|
|
|
+ <div class={styles.author}>{item.composer}</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ ))}
|
|
|
+
|
|
|
+ {!state.detailLoading && state.RECOMMEND.length === 0 && (
|
|
|
+ <div class={styles.empty}>
|
|
|
+ <TheEmpty description="暂无曲目"></TheEmpty>
|
|
|
+ </div>
|
|
|
+ )}
|
|
|
+ </NScrollbar>
|
|
|
+ </div>
|
|
|
+ <div class={[styles.section, styles.hotSection]}>
|
|
|
+ <div class={styles.sectionTitle}>
|
|
|
+ <img src={hotTitle} class={styles.imgTitle} />
|
|
|
+ <div class={styles.moreBtn} onClick={() => onMore('HOT')}>
|
|
|
+ 更多
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <NScrollbar>
|
|
|
+ {state.HOT.map((item: any, index: number) => (
|
|
|
+ <div
|
|
|
+ class={styles.item}
|
|
|
+ onClick={() => {
|
|
|
+ onDetail(item);
|
|
|
+ }}>
|
|
|
+ <div
|
|
|
+ class={[
|
|
|
+ styles.num,
|
|
|
+ index === 0 ? styles.num1 : '',
|
|
|
+ index === 1 ? styles.num2 : '',
|
|
|
+ index === 2 ? styles.num3 : ''
|
|
|
+ ]}>
|
|
|
+ {(index + 1).toString().padStart(2, '0')}
|
|
|
+ </div>
|
|
|
+ <div class={styles.img}>
|
|
|
+ <img referrerpolicy="no-referrer" src={item.titleImg} />
|
|
|
+ </div>
|
|
|
+ <div class={styles.infos}>
|
|
|
+ <div class={styles.topName}>
|
|
|
+ <TheNoticeBar text={item.musicSheetName} />
|
|
|
+ </div>
|
|
|
+ <div class={styles.types}>
|
|
|
+ <div class={styles.hot}>
|
|
|
+ <span>{formatUsedNum(item.usedNum)}</span>
|
|
|
+ </div>
|
|
|
+ {item.audioPlayTypes?.includes('SING') && (
|
|
|
+ <div class={styles.sing}>演唱</div>
|
|
|
+ )}
|
|
|
+ {item.audioPlayTypes?.includes('PLAY') && (
|
|
|
+ <div class={styles.song}>演奏</div>
|
|
|
+ )}
|
|
|
+
|
|
|
+ <div class={styles.author}>{item.composer}</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ ))}
|
|
|
+ {!state.detailLoading && state.HOT.length === 0 && (
|
|
|
+ <div class={styles.empty}>
|
|
|
+ <TheEmpty description="暂无曲目"></TheEmpty>
|
|
|
+ </div>
|
|
|
+ )}
|
|
|
+ </NScrollbar>
|
|
|
+ </div>
|
|
|
+ <div class={[styles.section, styles.newSection]}>
|
|
|
+ <div class={styles.sectionTitle}>
|
|
|
+ <img src={newTitle} class={styles.imgTitle} />
|
|
|
+
|
|
|
+ <div class={styles.moreBtn} onClick={() => onMore('NEW')}>
|
|
|
+ 更多
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <NScrollbar>
|
|
|
+ {state.NEW.map((item: any, index: number) => (
|
|
|
+ <div
|
|
|
+ class={styles.item}
|
|
|
+ onClick={() => {
|
|
|
+ onDetail(item);
|
|
|
+ }}>
|
|
|
+ <div
|
|
|
+ class={[
|
|
|
+ styles.num,
|
|
|
+ index === 0 ? styles.num1 : '',
|
|
|
+ index === 1 ? styles.num2 : '',
|
|
|
+ index === 2 ? styles.num3 : ''
|
|
|
+ ]}>
|
|
|
+ {(index + 1).toString().padStart(2, '0')}
|
|
|
+ </div>
|
|
|
+ <div class={styles.img}>
|
|
|
+ <img referrerpolicy="no-referrer" src={item.titleImg} />
|
|
|
+ </div>
|
|
|
+ <div class={styles.infos}>
|
|
|
+ <div class={styles.topName}>
|
|
|
+ <TheNoticeBar text={item.musicSheetName} />
|
|
|
+ </div>
|
|
|
+ <div class={styles.types}>
|
|
|
+ <div class={styles.hot}>
|
|
|
+ <span>{formatUsedNum(item.usedNum)}</span>
|
|
|
+ </div>
|
|
|
+ {item.audioPlayTypes?.includes('SING') && (
|
|
|
+ <div class={styles.sing}>演唱</div>
|
|
|
+ )}
|
|
|
+ {item.audioPlayTypes?.includes('PLAY') && (
|
|
|
+ <div class={styles.song}>演奏</div>
|
|
|
+ )}
|
|
|
+
|
|
|
+ <div class={styles.author}>{item.composer}</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ ))}
|
|
|
+ {!state.detailLoading && state.NEW.length === 0 && (
|
|
|
+ <div class={styles.empty}>
|
|
|
+ <TheEmpty description="暂无曲目"></TheEmpty>
|
|
|
+ </div>
|
|
|
+ )}
|
|
|
+ </NScrollbar>
|
|
|
+ </div>
|
|
|
+ </NSpin>
|
|
|
+ </div>
|
|
|
+ )}
|
|
|
+
|
|
|
+ {/* 应用内预览*/}
|
|
|
+ <PreviewWindow
|
|
|
+ v-model:show={state.previewModal}
|
|
|
+ type="music"
|
|
|
+ params={state.previewParams}
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ );
|
|
|
+ }
|
|
|
+});
|