detail.tsx 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  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. item.musicSheetName = item.name;
  106. });
  107. const knowledgeWikiCategories = res.data.knowledgeWikiCategories || [];
  108. res.data.knowledgeName =
  109. knowledgeWikiCategories.length > 0
  110. ? knowledgeWikiCategories[0].knowledgeWikiCategoryTypeName
  111. : '';
  112. res.data.intros = res.data.intros.replace(
  113. /<video/gi,
  114. '<video style="width: 100% !important;" controlslist="nodownload"'
  115. );
  116. data.details = res.data;
  117. data.loading = false;
  118. };
  119. onMounted(() => {
  120. getDetail();
  121. });
  122. return () => (
  123. <div class={styles.container}>
  124. <NSpace align="center" wrapItem={false} size={16}>
  125. <img
  126. style={{ cursor: 'pointer' }}
  127. src={icon_back}
  128. class={styles.iconBack}
  129. onClick={() => {
  130. const path =
  131. forms.type === 'MUSICIAN'
  132. ? '/content-musician'
  133. : '/content-instruments';
  134. router.push({ path });
  135. }}
  136. />
  137. <NBreadcrumb separator="">
  138. <NBreadcrumbItem
  139. onClick={() => {
  140. const path =
  141. forms.type === 'MUSICIAN'
  142. ? '/content-musician'
  143. : '/content-instruments';
  144. router.push({ path });
  145. }}>
  146. {forms.type === 'MUSICIAN' ? '音乐家' : '乐器百科'}
  147. </NBreadcrumbItem>
  148. <img class={styles.separator} src={icon_separator} />
  149. <NBreadcrumbItem>{route.query.name}</NBreadcrumbItem>
  150. </NBreadcrumb>
  151. </NSpace>
  152. <div class={[styles.wrap, data.showPlayer ? styles.wrapBottom : '']}>
  153. <div class={styles.content}>
  154. <div class={styles.contentWrap}>
  155. <div class={[styles.musicList, 'musicList-container']}>
  156. <div class={styles.wrapList}>
  157. <div class={styles.instrumentGroup}>
  158. <NImage
  159. class={[
  160. styles.instrumentImg,
  161. forms.type === 'MUSICIAN' && styles.otherImg
  162. ]}
  163. src={data.details?.avatar}
  164. objectFit="cover"
  165. />
  166. <p class={styles.instrumentName}>{data.details.name}</p>
  167. <p class={styles.instrumentTag}>
  168. {data.details.knowledgeName}
  169. </p>
  170. </div>
  171. <div class={styles.titlec}>
  172. <i class={styles.icon2}></i>代表作
  173. </div>
  174. {data.list.map((item: any, index: any) => {
  175. return (
  176. <div class={styles.itemContainer}>
  177. <div
  178. class={[
  179. styles.item
  180. // data.listActive === index && styles.active
  181. ]}
  182. onClick={(e: Event) => {
  183. e.stopPropagation();
  184. handlePlay(item);
  185. }}>
  186. <div class={styles.img}>
  187. <NImage
  188. lazy
  189. objectFit="cover"
  190. previewDisabled={true}
  191. src={item.titleImg || icon_default}
  192. onLoad={e => {
  193. (e.target as any).dataset.loaded = 'true';
  194. }}
  195. />
  196. <PlayLoading
  197. class={[
  198. data.listActive === index &&
  199. data.playState === 'play'
  200. ? ''
  201. : styles.showPlayLoading
  202. ]}
  203. />
  204. </div>
  205. <div class={styles.title}>
  206. <div class={styles.titleName}>
  207. <TheNoticeBar
  208. text={item.name}
  209. style={{ marginRight: '12px' }}
  210. />
  211. </div>
  212. </div>
  213. <NButton
  214. color="#259CFE"
  215. textColor="#fff"
  216. round
  217. class={styles.btn}
  218. type="primary"
  219. onClick={(e: Event) => {
  220. e.stopPropagation();
  221. handlePlay(item);
  222. }}>
  223. 播放
  224. <img
  225. src={
  226. data.listActive === index &&
  227. data.playState === 'play'
  228. ? icon_pause
  229. : icon_play
  230. }
  231. />
  232. </NButton>
  233. <img class={styles.arrow} src={icon_arrow} />
  234. </div>
  235. </div>
  236. );
  237. })}
  238. {!data.finshed && (
  239. <div class={styles.loadingWrap}>
  240. <NSpin show={true}></NSpin>
  241. </div>
  242. )}
  243. {!data.loading && data.list.length === 0 && (
  244. <div class={styles.empty}>
  245. <TheEmpty description="暂无代表作"></TheEmpty>
  246. </div>
  247. )}
  248. </div>
  249. </div>
  250. <div class={styles.musicStaff}>
  251. <div class={styles.musicTitle}>
  252. <i
  253. class={
  254. forms.type === 'MUSICIAN' ? styles.icon3 : styles.icon1
  255. }></i>
  256. {forms.type === 'MUSICIAN' ? '个人简介' : '乐器简介'}
  257. </div>
  258. <div
  259. class={styles.musicContent}
  260. v-html={data.details?.intros}
  261. style={{ fontSize: data.fontSize + 'px' }}></div>
  262. </div>
  263. <div class={styles.changeSizeSection}>
  264. <img src={iconT} class={styles.iconT} />
  265. <img
  266. src={iconAddT}
  267. class={styles.iconAddT}
  268. onClick={() => {
  269. if (data.fontSize >= 32) return;
  270. data.fontSize += 1;
  271. }}
  272. />
  273. <NSlider
  274. v-model:value={data.fontSize}
  275. vertical
  276. min={12}
  277. max={32}
  278. />
  279. <img
  280. src={iconPlusT}
  281. class={styles.iconPlusT}
  282. onClick={() => {
  283. if (data.fontSize <= 12) return;
  284. data.fontSize -= 1;
  285. }}
  286. />
  287. </div>
  288. </div>
  289. </div>
  290. </div>
  291. {data.list.length !== 0 && (
  292. <PlayItem
  293. show={data.showPlayer}
  294. playState={data.playState}
  295. item={activeItem.value}
  296. onChange={value => handleChangeAudio(value)}
  297. />
  298. )}
  299. </div>
  300. );
  301. }
  302. });