index.tsx 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716
  1. import {
  2. defineComponent,
  3. nextTick,
  4. onMounted,
  5. onUnmounted,
  6. reactive,
  7. watch,
  8. ref
  9. } from 'vue';
  10. // import WaveSurfer from 'wavesurfer.js';
  11. import styles from './index.module.less';
  12. import MSticky from '@/components/m-sticky';
  13. import MHeader from '@/components/m-header';
  14. import {
  15. Button,
  16. Cell,
  17. Image,
  18. List,
  19. Popup,
  20. Slider,
  21. showDialog,
  22. showToast,
  23. Sticky,
  24. TextEllipsis
  25. } from 'vant';
  26. import iconDownload from './images/icon-download.png';
  27. import iconShare from './images/icon-share.png';
  28. import iconDelete from './images/icon-delete.png';
  29. import iconEdit from './images/edit.png';
  30. import iconUpward from './images/upward.png';
  31. import iconMember from './images/icon-member.png';
  32. import iconZan from './images/icon-zan.png';
  33. import promptImg from './images/prompt.png';
  34. import confirmImg from './images/confirm.png';
  35. import canceImg from './images/cance.png';
  36. import iconZanActive from './images/icon-zan-active.png';
  37. import iconPlay from './images/icon-play.png';
  38. import iconPause from './images/icon-pause.png';
  39. import { postMessage, promisefiyPostMessage } from '@/helpers/native-message';
  40. import { browser, getGradeCh, getSecondRPM, vaildMusicScoreUrl } from '@/helpers/utils';
  41. import { useRoute, useRouter } from 'vue-router';
  42. import {
  43. api_userMusicDetail,
  44. api_userMusicRemove,
  45. api_userMusicStarPage
  46. } from './api';
  47. import MEmpty from '@/components/m-empty';
  48. import dayjs from 'dayjs';
  49. import MVideo from '@/components/m-video';
  50. import ShareModel from './share-model';
  51. import { usePageVisibility, useEventListener } from '@vant/use';
  52. import "plyr/dist/plyr.css";
  53. import Plyr from "plyr";
  54. import { Vue3Lottie } from "vue3-lottie";
  55. import audioBga from "./images/audioBga.json";
  56. import videobg from "./images/videobg.png";
  57. export default defineComponent({
  58. name: 'creation-detail',
  59. setup() {
  60. const route = useRoute();
  61. const router = useRouter();
  62. const isScreenScroll = ref(false)
  63. const lottieDom = ref()
  64. const state = reactive({
  65. id: route.query.id,
  66. deleteStatus: false,
  67. shareStatus: false,
  68. playType: '' as 'Audio' | 'Video' | '', // 播放类型
  69. musicDetail: {} as any,
  70. isClick: false,
  71. list: [] as any,
  72. listState: {
  73. dataShow: true, // 判断是否有数据
  74. loading: false,
  75. finished: false
  76. },
  77. params: {
  78. page: 1,
  79. rows: 20
  80. },
  81. _plrl: null as any,
  82. heightV:0,
  83. heightB:0
  84. });
  85. const plyrState = reactive({
  86. duration: 0,
  87. currentTime: 0,
  88. mediaTimeShow: false,
  89. playIngShow: true
  90. })
  91. // 谱面
  92. const staffState = reactive({
  93. staffSrc: "",
  94. isShow: false,
  95. height:"initial",
  96. speedRate:1,
  97. musicRenderType:"staff",
  98. partIndex: 0
  99. })
  100. const staffDom= ref<HTMLIFrameElement>()
  101. const {playStaff, pauseStaff, updateProgressStaff} = staffMoveInstance()
  102. // 获取列表
  103. const getStarList = async () => {
  104. try {
  105. if (state.isClick) return;
  106. state.isClick = true;
  107. const res = await api_userMusicStarPage({
  108. userMusicId: state.id,
  109. ...state.params
  110. });
  111. state.listState.loading = false;
  112. const result = res.data || {};
  113. // 处理重复请求数据
  114. if (state.list.length > 0 && result.current === 1) {
  115. return;
  116. }
  117. state.list = state.list.concat(result.rows || []);
  118. state.listState.finished = result.current >= result.pages;
  119. state.params.page = result.current + 1;
  120. state.listState.dataShow = state.list.length > 0;
  121. state.isClick = false;
  122. } catch {
  123. state.listState.dataShow = false;
  124. state.listState.finished = true;
  125. state.isClick = false;
  126. }
  127. };
  128. // 删除作品
  129. const onDelete = async () => {
  130. try {
  131. await api_userMusicRemove({ id: state.id });
  132. setTimeout(() => {
  133. state.deleteStatus = false;
  134. showToast('删除成功');
  135. }, 100);
  136. setTimeout(() => {
  137. if (browser().isApp) {
  138. postMessage({
  139. api: 'goBack'
  140. });
  141. } else {
  142. router.back();
  143. }
  144. }, 1200);
  145. } catch {
  146. //
  147. }
  148. };
  149. // 下载
  150. const onDownload = async () => {
  151. await promisefiyPostMessage({
  152. api: 'saveFile',
  153. content: {
  154. url: state.musicDetail.videoUrl
  155. }
  156. });
  157. };
  158. // 滚动事件
  159. const cleanScrollEvent = useEventListener('scroll', () => {
  160. const height =
  161. window.scrollY ||
  162. document.documentElement.scrollTop
  163. // 防止多次调用
  164. if(height > 0 && isScreenScroll.value === false){
  165. isScreenScroll.value = true
  166. setStatusBarTextColor(false)
  167. }
  168. if(height <= 0){
  169. isScreenScroll.value = false
  170. setStatusBarTextColor(true)
  171. }
  172. })
  173. // 设置导航栏颜色
  174. function setStatusBarTextColor(isWhite:boolean){
  175. postMessage({
  176. api: 'setStatusBarTextColor',
  177. content: { statusBarTextColor: isWhite }
  178. })
  179. }
  180. // 初始化 媒体播放
  181. function initMediaPlay(){
  182. const id = state.playType === "Audio" ? "#audioMediaSrc" : "#videoMediaSrc";
  183. state._plrl = new Plyr(id, {
  184. controls: ["progress"],
  185. fullscreen: { enabled: false },
  186. });
  187. const player = state._plrl
  188. // 创建音波数据
  189. if(state.playType === "Audio"){
  190. const audioDom = document.querySelector("#audioMediaSrc") as HTMLAudioElement
  191. const canvasDom = document.querySelector("#audioVisualizer") as HTMLCanvasElement
  192. const { pauseVisualDraw, playVisualDraw } = audioVisualDraw(audioDom, canvasDom)
  193. player.on('play', () => {
  194. lottieDom.value.play()
  195. playVisualDraw()
  196. });
  197. player.on('pause', () => {
  198. lottieDom.value.pause()
  199. pauseVisualDraw()
  200. });
  201. }
  202. player.on("timeupdate", ()=>{
  203. plyrState.currentTime = player.currentTime
  204. })
  205. player.on('play', () => {
  206. plyrState.playIngShow = false
  207. playStaff()
  208. });
  209. player.on('pause', () => {
  210. plyrState.playIngShow = true
  211. pauseStaff()
  212. });
  213. // 处理按压事件
  214. const handleStart = () => {
  215. plyrState.duration = player.duration
  216. plyrState.mediaTimeShow = true
  217. };
  218. // 处理松开事件
  219. const handleEnd = () => {
  220. plyrState.mediaTimeShow = false
  221. // 暂停的时候调用
  222. if(!player.playing){
  223. updateProgressStaff(player.currentTime)
  224. }
  225. };
  226. const progressDom = document.querySelector("#playMediaSection .plyr__controls .plyr__progress__container") as HTMLElement
  227. progressDom.addEventListener('mousedown', handleStart);
  228. progressDom.addEventListener('touchstart', handleStart);
  229. progressDom.addEventListener('mouseup', handleEnd);
  230. progressDom.addEventListener('touchend', handleEnd);
  231. }
  232. //点击改变播放状态
  233. function handlerClickPlay(){
  234. if (state._plrl.playing) {
  235. state._plrl.pause();
  236. } else {
  237. state._plrl.play();
  238. }
  239. }
  240. /**
  241. * 音频可视化
  242. * @param audioDom
  243. * @param canvasDom
  244. * @param fftSize 2的幂数,最小为32
  245. */
  246. function audioVisualDraw(audioDom: HTMLAudioElement, canvasDom: HTMLCanvasElement, fftSize = 128) {
  247. type propsType = { canvWidth: number; canvHeight: number; canvFillColor: string; lineColor: string; lineGap: number }
  248. // canvas
  249. const canvasCtx = canvasDom.getContext("2d")!
  250. const { width, height } = canvasDom.getBoundingClientRect()
  251. canvasDom.width = width
  252. canvasDom.height = height
  253. // audio
  254. let audioCtx : AudioContext | null = null
  255. let analyser : AnalyserNode | null = null
  256. let source : MediaElementAudioSourceNode | null = null
  257. const dataArray = new Uint8Array(fftSize / 2)
  258. const draw = (data: Uint8Array, ctx: CanvasRenderingContext2D, { lineGap, canvWidth, canvHeight, canvFillColor, lineColor }: propsType) => {
  259. if (!ctx) return
  260. const w = canvWidth
  261. const h = canvHeight
  262. fillCanvasBackground(ctx, w, h, canvFillColor)
  263. // 可视化
  264. const dataLen = data.length
  265. let step = (w / 2 - lineGap * dataLen) / dataLen
  266. step < 1 && (step = 1)
  267. const midX = w / 2
  268. const midY = h / 2
  269. let xLeft = midX
  270. for (let i = 0; i < dataLen; i++) {
  271. const value = data[i]
  272. const percent = value / 255 // 最大值为255
  273. const barHeight = percent * midY
  274. canvasCtx.fillStyle = lineColor
  275. // 中间加间隙
  276. if (i === 0) {
  277. xLeft -= lineGap / 2
  278. }
  279. canvasCtx.fillRect(xLeft - step, midY - barHeight, step, barHeight)
  280. canvasCtx.fillRect(xLeft - step, midY, step, barHeight)
  281. xLeft -= step + lineGap
  282. }
  283. let xRight = midX
  284. for (let i = 0; i < dataLen; i++) {
  285. const value = data[i]
  286. const percent = value / 255 // 最大值为255
  287. const barHeight = percent * midY
  288. canvasCtx.fillStyle = lineColor
  289. if (i === 0) {
  290. xRight += lineGap / 2
  291. }
  292. canvasCtx.fillRect(xRight, midY - barHeight, step, barHeight)
  293. canvasCtx.fillRect(xRight, midY, step, barHeight)
  294. xRight += step + lineGap
  295. }
  296. }
  297. const fillCanvasBackground = (ctx: CanvasRenderingContext2D, w: number, h: number, colors: string) => {
  298. ctx.clearRect(0, 0, w, h)
  299. ctx.fillStyle = colors
  300. ctx.fillRect(0, 0, w, h)
  301. }
  302. const requestAnimationFrameFun = () => {
  303. requestAnimationFrame(() => {
  304. analyser?.getByteFrequencyData(dataArray)
  305. draw(dataArray, canvasCtx, {
  306. lineGap: 2,
  307. canvWidth: width,
  308. canvHeight: height,
  309. canvFillColor: "transparent",
  310. lineColor: "rgba(255, 255, 255, 0.7)"
  311. })
  312. if (!isPause) {
  313. requestAnimationFrameFun()
  314. }
  315. })
  316. }
  317. let isPause = true
  318. const playVisualDraw = () => {
  319. if (!audioCtx) {
  320. audioCtx = new AudioContext()
  321. source = audioCtx.createMediaElementSource(audioDom)
  322. analyser = audioCtx.createAnalyser()
  323. analyser.fftSize = fftSize
  324. source?.connect(analyser)
  325. analyser.connect(audioCtx.destination)
  326. }
  327. //audioCtx.resume() // 重新更新状态 加了暂停和恢复音频音质发生了变化 所以这里取消了
  328. isPause = false
  329. requestAnimationFrameFun()
  330. }
  331. const pauseVisualDraw = () => {
  332. isPause = true
  333. //audioCtx?.suspend() // 暂停 加了暂停和恢复音频音质发生了变化 所以这里取消了
  334. // source?.disconnect()
  335. // analyser?.disconnect()
  336. }
  337. return {
  338. playVisualDraw,
  339. pauseVisualDraw
  340. }
  341. }
  342. function handlerLandscapeScreen(event:any){
  343. event.stopPropagation()
  344. router.push({
  345. path:"/playCreation",
  346. query:{
  347. resourceUrl:encodeURIComponent(state.musicDetail?.videoUrl),
  348. musicSheetName:encodeURIComponent(state.musicDetail?.musicSheetName),
  349. username:encodeURIComponent(state.musicDetail?.username),
  350. musicSheetId:encodeURIComponent(state.musicDetail?.musicSheetId),
  351. speedRate:encodeURIComponent(staffState.speedRate),
  352. musicRenderType:encodeURIComponent(staffState.musicRenderType),
  353. partIndex:encodeURIComponent(staffState.partIndex),
  354. }
  355. })
  356. }
  357. // 初始化五线谱
  358. function initStaff(){
  359. const src = `${vaildMusicScoreUrl()}/instrument/#/simple-detail?id=${state.musicDetail.musicSheetId}&musicRenderType=${staffState.musicRenderType}&part-index=${staffState.partIndex}`;
  360. //const src = `https://dev.kt.colexiu.com/instrument/#/simple-detail?id=${state.musicDetail.musicSheetId}&musicRenderType=${staffState.musicRenderType}&part-index=${staffState.partIndex}`;
  361. staffState.staffSrc = src
  362. window.addEventListener('message', (event) => {
  363. const { api, height } = event.data;
  364. if (api === 'api_musicPage') {
  365. staffState.isShow = true
  366. staffState.height = height + "px"
  367. // 如果是播放中自动开始 播放
  368. if(state._plrl.playing){
  369. playStaff()
  370. }
  371. }
  372. });
  373. }
  374. function staffMoveInstance(){
  375. let isPause = true
  376. const requestAnimationFrameFun = () => {
  377. requestAnimationFrame(() => {
  378. staffDom.value?.contentWindow?.postMessage(
  379. {
  380. api: 'api_playProgress',
  381. content: {
  382. currentTime: state._plrl.currentTime * staffState.speedRate
  383. }
  384. },
  385. "*"
  386. )
  387. if (!isPause) {
  388. requestAnimationFrameFun()
  389. }
  390. })
  391. }
  392. const playStaff = () => {
  393. // 没渲染不执行
  394. if(!staffState.isShow) return
  395. isPause = false
  396. staffDom.value?.contentWindow?.postMessage(
  397. {
  398. api: 'api_play'
  399. },
  400. "*"
  401. )
  402. requestAnimationFrameFun()
  403. }
  404. const pauseStaff = () => {
  405. // 没渲染不执行
  406. if(!staffState.isShow) return
  407. isPause = true
  408. staffDom.value?.contentWindow?.postMessage(
  409. {
  410. api: 'api_paused'
  411. },
  412. "*"
  413. )
  414. }
  415. const updateProgressStaff = (currentTime: number) => {
  416. // 没渲染不执行
  417. if(!staffState.isShow) return
  418. staffDom.value?.contentWindow?.postMessage(
  419. {
  420. api: 'api_updateProgress',
  421. content: {
  422. currentTime: currentTime * staffState.speedRate
  423. }
  424. },
  425. "*"
  426. )
  427. }
  428. return {
  429. playStaff,
  430. pauseStaff,
  431. updateProgressStaff
  432. }
  433. }
  434. onMounted(async () => {
  435. setStatusBarTextColor(true)
  436. try {
  437. const res = await api_userMusicDetail(state.id);
  438. // console.log(res);
  439. if (res.code === 999) {
  440. showDialog({
  441. message: res.message,
  442. theme: 'round-button',
  443. confirmButtonColor:
  444. 'linear-gradient(73deg, #5BECFF 0%, #259CFE 100%)'
  445. }).then(() => {
  446. if (browser().isApp) {
  447. postMessage({
  448. api: 'goBack'
  449. });
  450. } else {
  451. router.back();
  452. }
  453. });
  454. return;
  455. }
  456. state.musicDetail = res.data || {};
  457. try{
  458. const jsonConfig = JSON.parse(res.data.jsonConfig)
  459. jsonConfig.speedRate && (staffState.speedRate = jsonConfig.speedRate)
  460. jsonConfig.musicRenderType && (staffState.musicRenderType = jsonConfig.musicRenderType)
  461. jsonConfig.partIndex && (staffState.partIndex = jsonConfig.partIndex)
  462. }catch{
  463. }
  464. // 五线谱
  465. initStaff()
  466. getStarList();
  467. // 判断是视频还是音频
  468. if (res.data.videoUrl.lastIndexOf('mp4') !== -1) {
  469. state.playType = 'Video';
  470. } else {
  471. state.playType = 'Audio';
  472. }
  473. nextTick(()=>{
  474. initMediaPlay()
  475. })
  476. } catch {
  477. //
  478. }
  479. });
  480. onUnmounted(() => {
  481. setStatusBarTextColor(false)
  482. cleanScrollEvent()
  483. });
  484. return () => (
  485. <div
  486. style={
  487. {
  488. '--barheight':state.heightV + "px"
  489. }
  490. }
  491. class={[
  492. styles.creation,
  493. browser().isTablet && styles.creationTablet,
  494. isScreenScroll.value && styles.isScreenScroll
  495. ]}>
  496. <div class={styles.creationBg}></div>
  497. <MSticky position="top"
  498. onBarHeight={(height: any) => {
  499. console.log(height, 'height', height)
  500. state.heightV = height
  501. }}
  502. >
  503. <MHeader
  504. color={isScreenScroll.value ? "#333333" : "#ffffff"}
  505. background={isScreenScroll.value ? `rgb(255,255,255` : "transparent"}
  506. title={state.musicDetail?.musicSheetName}
  507. border={false}
  508. isBack={route.query.platformType != 'ANALYSIS'}
  509. />
  510. </MSticky>
  511. <div class={styles.singer}>
  512. 演奏:{state.musicDetail?.username}
  513. </div>
  514. <Sticky offsetTop={state.heightV - 1 + "px"}>
  515. <div class={[styles.playSection, plyrState.mediaTimeShow && styles.mediaTimeShow]} id="playMediaSection" onClick={handlerClickPlay}>
  516. {
  517. state.playType === 'Audio' &&
  518. <div class={styles.audioBox}>
  519. <canvas class={styles.audioVisualizer} id="audioVisualizer"></canvas>
  520. <Vue3Lottie ref={lottieDom} class={styles.audioBga} animationData={audioBga} autoPlay={false} loop={true}></Vue3Lottie>
  521. <audio
  522. crossorigin="anonymous"
  523. id="audioMediaSrc"
  524. src={state.musicDetail?.videoUrl}
  525. controls="false"
  526. preload="metadata"
  527. playsinline
  528. />
  529. </div>
  530. }
  531. {
  532. state.playType === 'Video' &&
  533. <video
  534. id="videoMediaSrc"
  535. class={styles.videoBox}
  536. src={state.musicDetail?.videoUrl}
  537. data-poster={videobg}
  538. preload="metadata"
  539. playsinline
  540. />
  541. }
  542. <div class={[styles.playLarge, plyrState.playIngShow && styles.playIngShow]}></div>
  543. <div class={styles.mediaTime}>
  544. <div>
  545. {getSecondRPM(plyrState.currentTime)}
  546. </div>
  547. <div class={styles.note}>/</div>
  548. <div class={styles.duration}>
  549. {getSecondRPM(plyrState.duration)}
  550. </div>
  551. </div>
  552. <div class={styles.landscapeScreen} onClick={handlerLandscapeScreen}></div>
  553. {/* 谱面 */}
  554. {
  555. staffState.staffSrc &&
  556. <div
  557. class={[styles.staffBox, staffState.isShow && styles.staffBoxShow]}
  558. style={
  559. {
  560. '--staffBoxHeight':staffState.height
  561. }
  562. }
  563. >
  564. <div class={styles.mask}></div>
  565. <iframe
  566. ref={staffDom}
  567. class={styles.staff}
  568. frameborder="0"
  569. src={staffState.staffSrc}>
  570. </iframe>
  571. </div>
  572. }
  573. </div>
  574. </Sticky>
  575. <div class={styles.musicSection}>
  576. <div class={styles.avatarInfoBox}>
  577. <div class={styles.avatar}>
  578. <Image class={styles.userLogo} src={state.musicDetail.avatar} />
  579. <div class={styles.infoCon}>
  580. <div class={styles.info}>
  581. <span class={styles.userName}>{state.musicDetail?.username}</span>
  582. {state.musicDetail.vipFlag && (
  583. <img src={iconMember} class={styles.iconMember} />
  584. )}
  585. </div>
  586. <div class={styles.sub}>
  587. {state.musicDetail.subjectName}{' '}
  588. {getGradeCh(state.musicDetail.currentGradeNum - 1)}
  589. </div>
  590. </div>
  591. </div>
  592. <div class={styles.linkes}>
  593. <img src={iconZan} class={styles.iconZan} />
  594. <span>{state.musicDetail.likeNum}</span>
  595. </div>
  596. </div>
  597. <TextEllipsis class={styles.textEllipsis} rows={2} content={state.musicDetail?.desc} expand-text="展开" collapse-text="收起" />
  598. </div>
  599. <div class={styles.likeSection}>
  600. <div class={styles.likeTitle}>点赞记录</div>
  601. {state.listState.dataShow ? (
  602. <List
  603. finished={state.listState.finished}
  604. finishedText=" "
  605. onLoad={getStarList}
  606. immediateCheck={false}>
  607. {state.list.map((item: any, index: number) => (
  608. <Cell
  609. class={[styles.likeItem, index===state.list.length-1&&styles.likeItemLast]}
  610. border={false}
  611. >
  612. {{
  613. icon: () => (
  614. <Image src={item.userAvatar} class={styles.userLogo} />
  615. ),
  616. title: () => (
  617. <div class={styles.userInfo}>
  618. <p class={styles.name}>{item.userName}</p>
  619. <p class={styles.sub}>
  620. {item.subjectName}{' '}
  621. {getGradeCh(item.currentGradeNum - 1)}
  622. </p>
  623. </div>
  624. ),
  625. value: () => (
  626. <div class={styles.time}>
  627. {dayjs(item.createTime).format('YYYY-MM-DD HH:mm')}
  628. </div>
  629. )
  630. }}
  631. </Cell>
  632. ))}
  633. </List>
  634. ) : (
  635. <MEmpty class={styles.mEmpty} description="暂无数据" />
  636. )}
  637. </div>
  638. {
  639. !isScreenScroll.value &&
  640. <MSticky position="bottom" offsetBottom={state.heightB - 1 + "px"} >
  641. <div class={styles.upward}>
  642. <img src={iconUpward} />
  643. </div>
  644. </MSticky>
  645. }
  646. <MSticky position="bottom" onBarHeight={(height: any) => {
  647. console.log(height, 'height', height)
  648. state.heightB = height
  649. }}>
  650. <div class={styles.bottomSection}>
  651. <div class={styles.bottomShare}>
  652. <p onClick={onDownload}>
  653. <img src={iconDownload} />
  654. <span>下载</span>
  655. </p>
  656. <p onClick={() => (state.shareStatus = true)}>
  657. <img src={iconShare} />
  658. <span>分享</span>
  659. </p>
  660. <p onClick={() => (state.deleteStatus = true)}>
  661. <img src={iconDelete} />
  662. <span>删除</span>
  663. </p>
  664. </div>
  665. <img src={iconEdit}
  666. class={styles.btnEdit}
  667. onClick={() => {
  668. router.push({
  669. path: '/creation-edit',
  670. query: {
  671. id: state.id
  672. }
  673. });
  674. }}
  675. />
  676. </div>
  677. </MSticky>
  678. <Popup
  679. v-model:show={state.deleteStatus}
  680. overlay-style={
  681. {
  682. backgroundColor:"rgba(0,0,0,.5)"
  683. }
  684. }
  685. round
  686. class={styles.popupContainer}>
  687. <img class={styles.prompt} src={promptImg} />
  688. <div class={styles.deleteBox}>
  689. <p class={styles.popupContent}>确定删除吗?</p>
  690. <div class={styles.popupBtnGroup}>
  691. <img src={canceImg} onClick={() => (state.deleteStatus = false)} />
  692. <img src={confirmImg} onClick={onDelete} />
  693. </div>
  694. </div>
  695. </Popup>
  696. <Popup
  697. position="bottom"
  698. v-model:show={state.shareStatus}
  699. style={{ background: 'transparent' }}>
  700. <ShareModel
  701. playType={state.playType}
  702. musicDetail={state.musicDetail}
  703. onClose={() => (state.shareStatus = false)}
  704. />
  705. </Popup>
  706. </div>
  707. );
  708. }
  709. });