|
@@ -1,4 +1,12 @@
|
|
-import { computed, defineComponent, nextTick, onMounted, onUnmounted, reactive, ref } from 'vue';
|
|
|
|
|
|
+import {
|
|
|
|
+ computed,
|
|
|
|
+ defineComponent,
|
|
|
|
+ nextTick,
|
|
|
|
+ onMounted,
|
|
|
|
+ onUnmounted,
|
|
|
|
+ reactive,
|
|
|
|
+ ref
|
|
|
|
+} from 'vue';
|
|
import styles from './detail.module.less';
|
|
import styles from './detail.module.less';
|
|
import CBreadcrumb from '/src/components/CBreadcrumb';
|
|
import CBreadcrumb from '/src/components/CBreadcrumb';
|
|
import { useRoute } from 'vue-router';
|
|
import { useRoute } from 'vue-router';
|
|
@@ -10,7 +18,7 @@ import { api_musicSheetPage } from '../xiaoku-ai/api';
|
|
import { formatUsedNum } from '.';
|
|
import { formatUsedNum } from '.';
|
|
import { vaildMusicScoreUrl } from '/src/utils/urlUtils';
|
|
import { vaildMusicScoreUrl } from '/src/utils/urlUtils';
|
|
import { exitFullscreen, fscreen } from '/src/utils';
|
|
import { exitFullscreen, fscreen } from '/src/utils';
|
|
-import {state as baseState} from '/src/state';
|
|
|
|
|
|
+import { state as baseState } from '/src/state';
|
|
import { useUserStore } from '/src/store/modules/users';
|
|
import { useUserStore } from '/src/store/modules/users';
|
|
import PreviewWindow from '../preview-window';
|
|
import PreviewWindow from '../preview-window';
|
|
|
|
|
|
@@ -21,19 +29,19 @@ export default defineComponent({
|
|
const userStore = useUserStore();
|
|
const userStore = useUserStore();
|
|
const routerList = ref([
|
|
const routerList = ref([
|
|
{ name: 'AI学练', path: '/xiaoku-list' },
|
|
{ name: 'AI学练', path: '/xiaoku-list' },
|
|
- { name: '全部曲目', path: '' }
|
|
|
|
|
|
+ { name: '曲目列表', path: '' }
|
|
]);
|
|
]);
|
|
const forms = reactive({
|
|
const forms = reactive({
|
|
page: 1,
|
|
page: 1,
|
|
rows: 32,
|
|
rows: 32,
|
|
status: true,
|
|
status: true,
|
|
- searchType: '',
|
|
|
|
|
|
+ searchType: ''
|
|
});
|
|
});
|
|
const state = reactive({
|
|
const state = reactive({
|
|
loading: true,
|
|
loading: true,
|
|
finshed: false,
|
|
finshed: false,
|
|
reshing: false,
|
|
reshing: false,
|
|
- tabName: '' as any,
|
|
|
|
|
|
+ tabName: '' as '' | 'RECOMMEND' | 'HOT' | 'NEW',
|
|
list: [] as any,
|
|
list: [] as any,
|
|
allSearch: {
|
|
allSearch: {
|
|
name: '',
|
|
name: '',
|
|
@@ -45,31 +53,45 @@ export default defineComponent({
|
|
},
|
|
},
|
|
hotSearch: {
|
|
hotSearch: {
|
|
name: '',
|
|
name: '',
|
|
|
|
+ musicalInstrumentId: null as any
|
|
},
|
|
},
|
|
newSearch: {
|
|
newSearch: {
|
|
name: '',
|
|
name: '',
|
|
|
|
+ musicalInstrumentId: null as any
|
|
},
|
|
},
|
|
recommendSearch: {
|
|
recommendSearch: {
|
|
name: '',
|
|
name: '',
|
|
|
|
+ musicalInstrumentId: null as any
|
|
},
|
|
},
|
|
previewModal: false,
|
|
previewModal: false,
|
|
previewParams: {
|
|
previewParams: {
|
|
type: '',
|
|
type: '',
|
|
src: ''
|
|
src: ''
|
|
- } as any,
|
|
|
|
|
|
+ } as any
|
|
});
|
|
});
|
|
|
|
|
|
const searchValue = computed(() => {
|
|
const searchValue = computed(() => {
|
|
- if(state.tabName === 'RECOMMEND') {
|
|
|
|
- return state.recommendSearch.name
|
|
|
|
- } else if(state.tabName === 'HOT') {
|
|
|
|
- return state.hotSearch.name
|
|
|
|
- } else if(state.tabName === 'NEW') {
|
|
|
|
- return state.newSearch.name
|
|
|
|
|
|
+ if (state.tabName === 'RECOMMEND') {
|
|
|
|
+ return state.recommendSearch.name;
|
|
|
|
+ } else if (state.tabName === 'HOT') {
|
|
|
|
+ return state.hotSearch.name;
|
|
|
|
+ } else if (state.tabName === 'NEW') {
|
|
|
|
+ return state.newSearch.name;
|
|
} else {
|
|
} else {
|
|
- return state.allSearch.name
|
|
|
|
|
|
+ return state.allSearch.name;
|
|
}
|
|
}
|
|
- })
|
|
|
|
|
|
+ });
|
|
|
|
+ const musicalInstrumentId = computed(() => {
|
|
|
|
+ let id = state.allSearch.musicalInstrumentId;
|
|
|
|
+ if (state.tabName === 'RECOMMEND') {
|
|
|
|
+ id = state.recommendSearch.musicalInstrumentId;
|
|
|
|
+ } else if (state.tabName === 'HOT') {
|
|
|
|
+ id = state.hotSearch.musicalInstrumentId;
|
|
|
|
+ } else if (state.tabName === 'NEW') {
|
|
|
|
+ id = state.newSearch.musicalInstrumentId;
|
|
|
|
+ }
|
|
|
|
+ return id;
|
|
|
|
+ });
|
|
|
|
|
|
const onSearch = async (item: any) => {
|
|
const onSearch = async (item: any) => {
|
|
forms.page = 1;
|
|
forms.page = 1;
|
|
@@ -77,20 +99,32 @@ export default defineComponent({
|
|
state.finshed = false;
|
|
state.finshed = false;
|
|
|
|
|
|
const { subjectId, ...res } = item;
|
|
const { subjectId, ...res } = item;
|
|
-
|
|
|
|
- state.allSearch = Object.assign(state.allSearch, {
|
|
|
|
- ...res,
|
|
|
|
- musicalInstrumentId: subjectId,
|
|
|
|
- subjectId: null
|
|
|
|
- });
|
|
|
|
|
|
+ if (state.tabName === 'HOT') {
|
|
|
|
+ state.hotSearch = Object.assign(state.hotSearch, {
|
|
|
|
+ musicalInstrumentId: subjectId
|
|
|
|
+ });
|
|
|
|
+ } else if (state.tabName == 'NEW') {
|
|
|
|
+ state.newSearch = Object.assign(state.newSearch, {
|
|
|
|
+ musicalInstrumentId: subjectId
|
|
|
|
+ });
|
|
|
|
+ } else if (state.tabName === 'RECOMMEND') {
|
|
|
|
+ state.recommendSearch = Object.assign(state.recommendSearch, {
|
|
|
|
+ musicalInstrumentId: subjectId
|
|
|
|
+ });
|
|
|
|
+ } else {
|
|
|
|
+ state.allSearch = Object.assign(state.allSearch, {
|
|
|
|
+ ...res,
|
|
|
|
+ musicalInstrumentId: subjectId,
|
|
|
|
+ subjectId: null
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
|
|
getList();
|
|
getList();
|
|
nextTick(() => {
|
|
nextTick(() => {
|
|
- __initSpin()
|
|
|
|
- })
|
|
|
|
|
|
+ __initSpin();
|
|
|
|
+ });
|
|
};
|
|
};
|
|
|
|
|
|
-
|
|
|
|
const spinRef = ref();
|
|
const spinRef = ref();
|
|
const handleResh = () => {
|
|
const handleResh = () => {
|
|
if (state.loading || state.finshed) return;
|
|
if (state.loading || state.finshed) return;
|
|
@@ -99,26 +133,27 @@ export default defineComponent({
|
|
};
|
|
};
|
|
|
|
|
|
const getList = async () => {
|
|
const getList = async () => {
|
|
- state.loading = true;
|
|
|
|
|
|
+ if(forms.page == 1) {
|
|
|
|
+ state.loading = true;
|
|
|
|
+ }
|
|
let res = {} as any;
|
|
let res = {} as any;
|
|
|
|
+ const { ...result } = forms;
|
|
|
|
+ let params = {
|
|
|
|
+ ...result,
|
|
|
|
+ searchType: state.tabName
|
|
|
|
+ } as any;
|
|
|
|
+ if (state.tabName === 'RECOMMEND') {
|
|
|
|
+ params = Object.assign(params, state.recommendSearch);
|
|
|
|
+ } else if (state.tabName === 'HOT') {
|
|
|
|
+ params = Object.assign(params, state.hotSearch);
|
|
|
|
+ } else if (state.tabName === 'NEW') {
|
|
|
|
+ params = Object.assign(params, state.newSearch);
|
|
|
|
+ } else {
|
|
|
|
+ params.name = state.allSearch.name;
|
|
|
|
+ const { ...more } = state.allSearch;
|
|
|
|
+ params = Object.assign(params, { ...more });
|
|
|
|
+ }
|
|
try {
|
|
try {
|
|
- const { ...result } = forms;
|
|
|
|
- let params = {
|
|
|
|
- ...result,
|
|
|
|
- searchType: state.tabName
|
|
|
|
- } as any
|
|
|
|
- if(state.tabName === 'RECOMMEND') {
|
|
|
|
- params = Object.assign(params, state.recommendSearch);
|
|
|
|
- } else if(state.tabName === 'HOT') {
|
|
|
|
- params = Object.assign(params, state.hotSearch);
|
|
|
|
- } else if(state.tabName === 'NEW') {
|
|
|
|
- params = Object.assign(params, state.newSearch);
|
|
|
|
- } else {
|
|
|
|
- params.name = state.allSearch.name
|
|
|
|
- const { ...more } = state.allSearch
|
|
|
|
- params = Object.assign(params, { ...more });
|
|
|
|
- // params.audioPlayTypes = audioPlayTypes
|
|
|
|
- }
|
|
|
|
res = await api_musicSheetPage(params);
|
|
res = await api_musicSheetPage(params);
|
|
} catch (error) {
|
|
} catch (error) {
|
|
console.log(error);
|
|
console.log(error);
|
|
@@ -134,9 +169,18 @@ export default defineComponent({
|
|
item.audioPlayTypeArray = item.audioPlayTypes
|
|
item.audioPlayTypeArray = item.audioPlayTypes
|
|
? item.audioPlayTypes.split(',')
|
|
? item.audioPlayTypes.split(',')
|
|
: [];
|
|
: [];
|
|
|
|
+
|
|
|
|
+ if (item.musicSheetName) {
|
|
|
|
+ const regex = new RegExp(params.name, 'gi');
|
|
|
|
+ const highlightedText = item.musicSheetName.replace(
|
|
|
|
+ regex,
|
|
|
|
+ `<span>$&</span>`
|
|
|
|
+ );
|
|
|
|
+ item.musicNameReg = highlightedText;
|
|
|
|
+ }
|
|
});
|
|
});
|
|
state.list = [...state.list, ...res.data.rows];
|
|
state.list = [...state.list, ...res.data.rows];
|
|
- state.finshed = forms.page >= res.data.pages;
|
|
|
|
|
|
+ state.finshed = forms.page >= res.data.pages;
|
|
} else {
|
|
} else {
|
|
state.finshed = true;
|
|
state.finshed = true;
|
|
}
|
|
}
|
|
@@ -144,13 +188,13 @@ export default defineComponent({
|
|
};
|
|
};
|
|
|
|
|
|
const __initSpin = () => {
|
|
const __initSpin = () => {
|
|
- const obv = new IntersectionObserver(entries => {
|
|
|
|
- if (entries[0].intersectionRatio > 0) {
|
|
|
|
- handleResh();
|
|
|
|
- }
|
|
|
|
- });
|
|
|
|
- obv.observe(spinRef.value);
|
|
|
|
- }
|
|
|
|
|
|
+ // const obv = new IntersectionObserver(entries => {
|
|
|
|
+ // if (entries[0].intersectionRatio > 0) {
|
|
|
|
+ // handleResh();
|
|
|
|
+ // }
|
|
|
|
+ // });
|
|
|
|
+ // obv.observe(spinRef.value);
|
|
|
|
+ };
|
|
|
|
|
|
// 查看详情
|
|
// 查看详情
|
|
const onDetail = (item: any) => {
|
|
const onDetail = (item: any) => {
|
|
@@ -160,9 +204,7 @@ export default defineComponent({
|
|
item.id
|
|
item.id
|
|
}&Authorization=${
|
|
}&Authorization=${
|
|
userStore.getToken
|
|
userStore.getToken
|
|
- }&musicRenderType=${lineType}&showGuide=true&part-index=${
|
|
|
|
- 0
|
|
|
|
- }`;
|
|
|
|
|
|
+ }&musicRenderType=${lineType}&showGuide=true&part-index=${0}`;
|
|
|
|
|
|
if (state.allSearch.musicalInstrumentId && !state.tabName) {
|
|
if (state.allSearch.musicalInstrumentId && !state.tabName) {
|
|
src += '&instrumentId=' + state.allSearch.musicalInstrumentId;
|
|
src += '&instrumentId=' + state.allSearch.musicalInstrumentId;
|
|
@@ -181,28 +223,28 @@ export default defineComponent({
|
|
} else {
|
|
} else {
|
|
window.open(src, +new Date() + '');
|
|
window.open(src, +new Date() + '');
|
|
}
|
|
}
|
|
- }
|
|
|
|
|
|
+ };
|
|
|
|
|
|
const iframeHandle = (ev: MessageEvent) => {
|
|
const iframeHandle = (ev: MessageEvent) => {
|
|
if (ev.data?.api === 'onAttendToggleMenu') {
|
|
if (ev.data?.api === 'onAttendToggleMenu') {
|
|
- exitFullscreen()
|
|
|
|
|
|
+ exitFullscreen();
|
|
state.previewModal = !state.previewModal;
|
|
state.previewModal = !state.previewModal;
|
|
}
|
|
}
|
|
- }
|
|
|
|
|
|
+ };
|
|
|
|
|
|
onMounted(async () => {
|
|
onMounted(async () => {
|
|
- if(route.query.type) {
|
|
|
|
- state.tabName = route.query.type
|
|
|
|
|
|
+ if (route.query.type) {
|
|
|
|
+ state.tabName = route.query.type as any;
|
|
}
|
|
}
|
|
|
|
|
|
// getList();
|
|
// getList();
|
|
- __initSpin()
|
|
|
|
|
|
+ __initSpin();
|
|
window.addEventListener('message', iframeHandle);
|
|
window.addEventListener('message', iframeHandle);
|
|
});
|
|
});
|
|
|
|
|
|
onUnmounted(() => {
|
|
onUnmounted(() => {
|
|
- window.removeEventListener('message', iframeHandle)
|
|
|
|
- })
|
|
|
|
|
|
+ window.removeEventListener('message', iframeHandle);
|
|
|
|
+ });
|
|
return () => (
|
|
return () => (
|
|
<div class={styles.xiaokuDetail}>
|
|
<div class={styles.xiaokuDetail}>
|
|
<CBreadcrumb list={routerList.value}></CBreadcrumb>
|
|
<CBreadcrumb list={routerList.value}></CBreadcrumb>
|
|
@@ -217,31 +259,31 @@ export default defineComponent({
|
|
forms.page = 1;
|
|
forms.page = 1;
|
|
state.finshed = false;
|
|
state.finshed = false;
|
|
state.reshing = true;
|
|
state.reshing = true;
|
|
- state.list = []
|
|
|
|
|
|
+ state.list = [];
|
|
getList();
|
|
getList();
|
|
|
|
|
|
- __initSpin()
|
|
|
|
|
|
+ __initSpin();
|
|
}}
|
|
}}
|
|
v-slots={{
|
|
v-slots={{
|
|
suffix: () => (
|
|
suffix: () => (
|
|
<TheSearch
|
|
<TheSearch
|
|
- placeholder='请输入曲目名称'
|
|
|
|
|
|
+ placeholder="请输入曲目名称"
|
|
round
|
|
round
|
|
value={searchValue.value}
|
|
value={searchValue.value}
|
|
class={styles.inputSearch}
|
|
class={styles.inputSearch}
|
|
onSearch={val => {
|
|
onSearch={val => {
|
|
- if(state.tabName === 'RECOMMEND') {
|
|
|
|
- state.recommendSearch.name = val
|
|
|
|
- } else if(state.tabName === 'HOT') {
|
|
|
|
- state.hotSearch.name = val
|
|
|
|
- } else if(state.tabName === 'NEW') {
|
|
|
|
- state.newSearch.name = val
|
|
|
|
|
|
+ if (state.tabName === 'RECOMMEND') {
|
|
|
|
+ state.recommendSearch.name = val;
|
|
|
|
+ } else if (state.tabName === 'HOT') {
|
|
|
|
+ state.hotSearch.name = val;
|
|
|
|
+ } else if (state.tabName === 'NEW') {
|
|
|
|
+ state.newSearch.name = val;
|
|
} else {
|
|
} else {
|
|
- state.allSearch.name = val
|
|
|
|
|
|
+ state.allSearch.name = val;
|
|
}
|
|
}
|
|
forms.page = 1;
|
|
forms.page = 1;
|
|
state.finshed = false;
|
|
state.finshed = false;
|
|
- state.list = []
|
|
|
|
|
|
+ state.list = [];
|
|
getList();
|
|
getList();
|
|
}}
|
|
}}
|
|
/>
|
|
/>
|
|
@@ -253,60 +295,95 @@ export default defineComponent({
|
|
<NTabPane name={`NEW`} tab={'最新曲目'}></NTabPane>
|
|
<NTabPane name={`NEW`} tab={'最新曲目'}></NTabPane>
|
|
</NTabs>
|
|
</NTabs>
|
|
|
|
|
|
- <NScrollbar class={[[styles.wrapList, !state.loading && state.list.length === 0 && styles.wrapListEmpty]]}>
|
|
|
|
- <div class={[styles.searchSection, state.tabName ? styles.searchSectionHide : '']}>
|
|
|
|
- <SearchGroupResources onSearch={(val: any) => {
|
|
|
|
- onSearch(val)
|
|
|
|
- }} />
|
|
|
|
- </div>
|
|
|
|
-
|
|
|
|
- {state.list.length > 0 && <div
|
|
|
|
- class={[
|
|
|
|
- styles.sectionContainer,
|
|
|
|
- state.tabName && styles.noSearchContainer
|
|
|
|
- ]}>
|
|
|
|
- {state.list.map((item: any) => (
|
|
|
|
- <div class={styles.sectionItem} onClick={() => onDetail(item)}>
|
|
|
|
- <div class={styles.img}>
|
|
|
|
- <img referrerpolicy="no-referrer" src={item.titleImg} />
|
|
|
|
- </div>
|
|
|
|
- <div class={styles.infos}>
|
|
|
|
- <div class={styles.topName}>{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>
|
|
|
|
- )}
|
|
|
|
|
|
+ <NScrollbar
|
|
|
|
+ class={[
|
|
|
|
+ [
|
|
|
|
+ styles.wrapList,
|
|
|
|
+ !state.loading &&
|
|
|
|
+ state.list.length === 0 &&
|
|
|
|
+ styles.wrapListEmpty
|
|
|
|
+ ]
|
|
|
|
+ ]}
|
|
|
|
+ onScroll={(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
|
|
|
|
+ ) {
|
|
|
|
+ forms.page = forms.page + 1;
|
|
|
|
+ getList();
|
|
|
|
+ }
|
|
|
|
+ }}
|
|
|
|
+ >
|
|
|
|
+ {/* , state.tabName ? styles.searchSectionHide : '' */}
|
|
|
|
+ <NSpin show={state.loading}>
|
|
|
|
+ <div class={styles.loadingSection}>
|
|
|
|
+ <div class={[styles.searchSection]}>
|
|
|
|
+ <SearchGroupResources
|
|
|
|
+ type={state.tabName}
|
|
|
|
+ musicalInstrumentId={musicalInstrumentId.value}
|
|
|
|
+ onSearch={(val: any) => {
|
|
|
|
+ onSearch(val);
|
|
|
|
+ }}
|
|
|
|
+ />
|
|
|
|
+ </div>
|
|
|
|
+
|
|
|
|
+ {state.list.length > 0 && (
|
|
|
|
+ <div
|
|
|
|
+ class={[
|
|
|
|
+ styles.sectionContainer
|
|
|
|
+ // state.tabName && styles.noSearchContainer
|
|
|
|
+ ]}>
|
|
|
|
+ {state.list.map((item: any) => (
|
|
|
|
+ <div
|
|
|
|
+ class={styles.sectionItem}
|
|
|
|
+ onClick={() => onDetail(item)}>
|
|
|
|
+ <div class={styles.img}>
|
|
|
|
+ <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 class={styles.author}>{item.composer}</div>
|
|
|
|
+ </div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
- </div>
|
|
|
|
- ))}
|
|
|
|
- </div>}
|
|
|
|
-
|
|
|
|
|
|
+ ))}
|
|
|
|
+ </div>
|
|
|
|
+ )}
|
|
|
|
|
|
- <div
|
|
|
|
- ref={spinRef}
|
|
|
|
- class={[styles.loadingWrap, state.finshed && styles.showLoading]}>
|
|
|
|
|
|
+ {/* <div
|
|
|
|
+ // ref={spinRef}
|
|
|
|
+ class={[styles.loadingWrap, (state.finshed || !state.loading) && styles.showLoading]}>
|
|
<NSpin show={true}></NSpin>
|
|
<NSpin show={true}></NSpin>
|
|
- </div>
|
|
|
|
|
|
+ </div> */}
|
|
|
|
|
|
{!state.loading && state.list.length === 0 && (
|
|
{!state.loading && state.list.length === 0 && (
|
|
<div class={styles.empty}>
|
|
<div class={styles.empty}>
|
|
<TheEmpty></TheEmpty>
|
|
<TheEmpty></TheEmpty>
|
|
</div>
|
|
</div>
|
|
)}
|
|
)}
|
|
|
|
+ </div>
|
|
|
|
+ </NSpin>
|
|
</NScrollbar>
|
|
</NScrollbar>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
{/* 应用内预览或上课 */}
|
|
{/* 应用内预览或上课 */}
|
|
- <PreviewWindow
|
|
|
|
|
|
+ <PreviewWindow
|
|
v-model:show={state.previewModal}
|
|
v-model:show={state.previewModal}
|
|
type="music"
|
|
type="music"
|
|
params={state.previewParams}
|
|
params={state.previewParams}
|