detail.tsx 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. import {
  2. NBreadcrumb,
  3. NBreadcrumbItem,
  4. NButton,
  5. NImage,
  6. NSlider,
  7. NSpace,
  8. NSpin
  9. } from 'naive-ui';
  10. import { computed, defineComponent, onMounted, reactive } from 'vue';
  11. import styles from './detail.module.less';
  12. import icon_back from '../../xiaoku-music/images/icon_back.png';
  13. import icon_arrow from '../../xiaoku-music/images/icon_arrow.png';
  14. import icon_play from '../../xiaoku-music/images/icon_play.png';
  15. import icon_pause from '../../xiaoku-music/images/icon_pause.png';
  16. import icon_default from '../../xiaoku-music/images/icon_default.png';
  17. import icon_separator from '../../xiaoku-music/images/icon_separator.png';
  18. import iconT from '../images/icon-t.png';
  19. import iconAddT from '../images/icon-add-t.png';
  20. import iconPlusT from '../images/icon-plus-t.png';
  21. import { useRoute, useRouter } from 'vue-router';
  22. import PlayLoading from '../../xiaoku-music/component/play-loading';
  23. import TheNoticeBar from '/src/components/TheNoticeBar';
  24. import TheEmpty from '/src/components/TheEmpty';
  25. import PlayItem from '../../xiaoku-music/component/play-item';
  26. import { api_knowledgeWiki_detail } from '../api';
  27. import { state } from '/src/state';
  28. export default defineComponent({
  29. name: 'instrument-detail',
  30. setup() {
  31. const route = useRoute();
  32. const router = useRouter();
  33. const forms = reactive({
  34. page: 1,
  35. rows: 20,
  36. status: true,
  37. name: '', // 关键词
  38. type: route.query.type
  39. });
  40. const data = reactive({
  41. loading: false,
  42. finshed: false,
  43. reshing: false,
  44. details: {} as any,
  45. list: [] as any,
  46. listActive: 0,
  47. playState: 'pause' as 'play' | 'pause',
  48. showPlayer: false,
  49. showPreivew: false,
  50. previewUrl: '',
  51. showCloseBtn: true,
  52. fontSize: 18 // 默认18
  53. });
  54. /** 选中的item */
  55. const activeItem = computed(() => {
  56. return data.list[data.listActive] || {};
  57. });
  58. /** 播放曲目 */
  59. const handlePlay = (item: any) => {
  60. const index = data.list.findIndex((_item: any) => _item.id === item.id);
  61. if (index > -1) {
  62. if (data.listActive === index) {
  63. data.playState = data.playState === 'play' ? 'pause' : 'play';
  64. } else {
  65. data.playState = 'play';
  66. }
  67. data.showPlayer = true;
  68. data.listActive = index;
  69. }
  70. };
  71. /** 音频控制 */
  72. const handleChangeAudio = (
  73. type: 'play' | 'pause' | 'pre' | 'next' | 'favitor'
  74. ) => {
  75. if (type === 'play') {
  76. data.playState = 'play';
  77. } else if (type === 'pause') {
  78. data.playState = 'pause';
  79. } else if (type === 'pre') {
  80. if (data.list[data.listActive - 1]) {
  81. handlePlay(data.list[data.listActive - 1]);
  82. }
  83. } else if (type === 'next') {
  84. if (data.list[data.listActive + 1]) {
  85. handlePlay(data.list[data.listActive + 1]);
  86. }
  87. }
  88. };
  89. const getDetail = async () => {
  90. data.loading = true;
  91. let res = {} as any;
  92. try {
  93. res = await api_knowledgeWiki_detail({ id: route.query.id });
  94. } catch (error) {
  95. console.log(error);
  96. }
  97. if (data.reshing) {
  98. data.list = [];
  99. data.reshing = false;
  100. }
  101. data.finshed = true;
  102. data.list = res.data.knowledgeWikiResources || [];
  103. data.list.forEach((item: any) => {
  104. item.audioFileUrl = item.url;
  105. });
  106. const knowledgeWikiCategories = res.data.knowledgeWikiCategories || [];
  107. res.data.knowledgeName =
  108. knowledgeWikiCategories.length > 0
  109. ? knowledgeWikiCategories[0].name
  110. : '';
  111. res.data.intros = res.data.intros.replace(
  112. /<video/gi,
  113. '<video style="width: 100% !important;" controlslist="nodownload"'
  114. );
  115. data.details = res.data;
  116. data.loading = false;
  117. };
  118. onMounted(() => {
  119. getDetail();
  120. });
  121. return () => (
  122. <div class={styles.container}>
  123. <NSpace align="center" wrapItem={false} size={16}>
  124. <img
  125. style={{ cursor: 'pointer' }}
  126. src={icon_back}
  127. class={styles.iconBack}
  128. onClick={() => {
  129. const path =
  130. forms.type === 'MUSICIAN'
  131. ? '/content-musician'
  132. : '/content-instruments';
  133. router.push({ path });
  134. }}
  135. />
  136. <NBreadcrumb separator="">
  137. <NBreadcrumbItem
  138. onClick={() => {
  139. const path =
  140. forms.type === 'MUSICIAN'
  141. ? '/content-musician'
  142. : '/content-instruments';
  143. router.push({ path });
  144. }}>
  145. {forms.type === 'MUSICIAN' ? '音乐家' : '乐器百科'}
  146. </NBreadcrumbItem>
  147. <img class={styles.separator} src={icon_separator} />
  148. <NBreadcrumbItem>{route.query.name}</NBreadcrumbItem>
  149. </NBreadcrumb>
  150. </NSpace>
  151. <div class={[styles.wrap, data.showPlayer ? styles.wrapBottom : '']}>
  152. <div class={styles.content}>
  153. <div class={styles.contentWrap}>
  154. <div class={[styles.musicList, 'musicList-container']}>
  155. <div class={styles.wrapList}>
  156. <div class={styles.instrumentGroup}>
  157. <NImage
  158. class={styles.instrumentImg}
  159. src={data.details?.avatar}
  160. />
  161. <p class={styles.instrumentName}>{data.details.name}</p>
  162. <p class={styles.instrumentTag}>
  163. {data.details.knowledgeName}
  164. </p>
  165. </div>
  166. <div class={styles.titlec}>代表作</div>
  167. {data.list.map((item: any, index: any) => {
  168. return (
  169. <div class={styles.itemContainer}>
  170. <div
  171. class={[
  172. styles.item
  173. // data.listActive === index && styles.active
  174. ]}
  175. onClick={(e: Event) => {
  176. e.stopPropagation();
  177. handlePlay(item);
  178. }}>
  179. <div class={styles.img}>
  180. <NImage
  181. lazy
  182. objectFit="cover"
  183. previewDisabled={true}
  184. src={item.titleImg || icon_default}
  185. onLoad={e => {
  186. (e.target as any).dataset.loaded = 'true';
  187. }}
  188. />
  189. <PlayLoading
  190. class={[
  191. data.listActive === index &&
  192. data.playState === 'play'
  193. ? ''
  194. : styles.showPlayLoading
  195. ]}
  196. />
  197. </div>
  198. <div class={styles.title}>
  199. <div class={styles.titleName}>
  200. <TheNoticeBar text={item.name} />
  201. </div>
  202. </div>
  203. <NButton
  204. color="#259CFE"
  205. textColor="#fff"
  206. round
  207. class={styles.btn}
  208. type="primary"
  209. onClick={(e: Event) => {
  210. e.stopPropagation();
  211. handlePlay(item);
  212. }}>
  213. 播放
  214. <img
  215. src={
  216. data.listActive === index &&
  217. data.playState === 'play'
  218. ? icon_pause
  219. : icon_play
  220. }
  221. />
  222. </NButton>
  223. <img class={styles.arrow} src={icon_arrow} />
  224. </div>
  225. </div>
  226. );
  227. })}
  228. {!data.finshed && (
  229. <div class={styles.loadingWrap}>
  230. <NSpin show={true}></NSpin>
  231. </div>
  232. )}
  233. {!data.loading && data.list.length === 0 && (
  234. <div class={styles.empty}>
  235. <TheEmpty description="暂无代表作"></TheEmpty>
  236. </div>
  237. )}
  238. </div>
  239. </div>
  240. <div class={styles.musicStaff}>
  241. <div class={styles.musicTitle}>乐器简介</div>
  242. <div
  243. class={styles.musicContent}
  244. v-html={data.details?.intros}
  245. style={{ fontSize: data.fontSize + 'px' }}></div>
  246. </div>
  247. <div class={styles.changeSizeSection}>
  248. <img src={iconT} class={styles.iconT} />
  249. <img
  250. src={iconAddT}
  251. class={styles.iconAddT}
  252. onClick={() => {
  253. if (data.fontSize >= 32) return;
  254. data.fontSize += 1;
  255. }}
  256. />
  257. <NSlider
  258. v-model:value={data.fontSize}
  259. vertical
  260. min={12}
  261. max={32}
  262. />
  263. <img
  264. src={iconPlusT}
  265. class={styles.iconPlusT}
  266. onClick={() => {
  267. if (data.fontSize <= 12) return;
  268. data.fontSize -= 1;
  269. }}
  270. />
  271. </div>
  272. </div>
  273. </div>
  274. </div>
  275. {data.list.length !== 0 && (
  276. <PlayItem
  277. show={data.showPlayer}
  278. playState={data.playState}
  279. item={activeItem.value}
  280. onChange={value => handleChangeAudio(value)}
  281. />
  282. )}
  283. </div>
  284. );
  285. }
  286. });