Browse Source

播放器控件修改

黄琪勇 1 month ago
parent
commit
bfab324247

+ 2 - 2
src/components/Modal.vue

@@ -79,7 +79,7 @@ const onClickMask = () => {
   left: 0;
   width: 100%;
   height: 100%;
-  z-index: 5000;
+  z-index: 1999;
 }
 
 .modal {
@@ -151,4 +151,4 @@ const onClickMask = () => {
     transform: scale3d(.3, .3, .3);
   }
 }
-</style>
+</style>

+ 28 - 2
src/hooks/useCreateElement.ts

@@ -320,7 +320,6 @@ export default () => {
 
   /**
    * 创建云教练元素
-   * @param url 云教练地址
    */
   const createCloudCoachElement = (sid: string, title: string) => {
     createElement({
@@ -337,6 +336,32 @@ export default () => {
     })
   }
 
+  /**
+   * 音频播放器控件
+   */
+  const createEnjoyElement = (sid: string, title: string, src: string) => {
+    createElement({
+      type: "elf",
+      subtype: "elf-enjoy",
+      sid,
+      title,
+      src,
+      enjoyList: [
+        {
+          id: sid,
+          title,
+          src
+        }
+      ],
+      id: nanoid(10),
+      width: 670,
+      height: 102,
+      rotate: 0,
+      left: 629,
+      top: 900
+    })
+  }
+
   return {
     createImageElement,
     createChartElement,
@@ -347,6 +372,7 @@ export default () => {
     createLatexElement,
     createVideoElement,
     createAudioElement,
-    createCloudCoachElement
+    createCloudCoachElement,
+    createEnjoyElement
   }
 }

+ 2 - 1
src/types/slides.ts

@@ -34,7 +34,8 @@ export const enum ElementTypes {
 export const enum ElementSubtypeTypes {
   VIDEO = "elf-video",
   AUDIO = "elf-audio",
-  SING_PLAY = "elf-sing-play"
+  SING_PLAY = "elf-sing-play",
+  ENJOY = "elf-enjoy"
 }
 
 /**

+ 3 - 1
src/views/Editor/Canvas/EditableElement.vue

@@ -36,6 +36,7 @@ import LatexElement from "@/views/components/element/LatexElement/index.vue"
 import VideoElement from "@/views/components/element/VideoElement/index.vue"
 import AudioElement from "@/views/components/element/AudioElement/index.vue"
 import cloudCoachElement from "@/views/components/element/cloudCoachElement"
+import enjoyElement from "@/views/components/element/enjoyElement"
 
 const props = defineProps<{
   elementInfo: PPTElement
@@ -59,7 +60,8 @@ const currentElementComponent = computed<unknown>(() => {
   const elementSubtypeMap = {
     [ElementSubtypeTypes.AUDIO]: AudioElement,
     [ElementSubtypeTypes.VIDEO]: VideoElement,
-    [ElementSubtypeTypes.SING_PLAY]: cloudCoachElement
+    [ElementSubtypeTypes.SING_PLAY]: cloudCoachElement,
+    [ElementSubtypeTypes.ENJOY]: enjoyElement
   }
   return elementTypeMap[props.elementInfo.type] || elementSubtypeMap[props.elementInfo.subtype] || null
 })

+ 2 - 1
src/views/Editor/Canvas/index.vue

@@ -158,7 +158,8 @@ watchEffect(setLocalElementList)
 const canvasRef = ref<HTMLElement>()
 const { dragViewport, viewportStyles } = useViewportSize(canvasRef)
 
-useDropImageOrText(canvasRef)
+/* 这个功能和 enjoyPlayer 冲突,所以禁用掉 */
+//useDropImageOrText(canvasRef)
 
 const { mouseSelection, mouseSelectionVisible, mouseSelectionQuadrant, updateMouseSelection } = useMouseSelection(elementList, viewportRef)
 

+ 49 - 3
src/views/Editor/CanvasTool/index.vue

@@ -259,6 +259,7 @@
       v-model:visible="resourcesListVisible"
     >
       <resourcesList
+        @update="handleResources"
         @close="
           () => {
             resourcesListVisible = false
@@ -272,7 +273,7 @@
 <script lang="ts" setup>
 import { ref } from "vue"
 import { storeToRefs } from "pinia"
-import { useMainStore, useSnapshotStore } from "@/store"
+import { useMainStore, useSnapshotStore, useSlidesStore } from "@/store"
 import { getImageDataURL } from "@/utils/image"
 import type { ShapePoolItem } from "@/configs/shapes"
 import type { LinePoolItem } from "@/configs/lines"
@@ -296,6 +297,8 @@ import resourcesList from "@/views/components/element/enjoyElement/resourcesList
 import fileUpload from "@/utils/oss-file-upload"
 import usePptWork from "@/store/pptWork"
 
+const useSlidesHook = useSlidesStore()
+
 const usePptWorkHook = usePptWork()
 const mainStore = useMainStore()
 const { creatingElement, creatingCustomShape, showSelectPanel, showSearchPanel, showNotesPanel } = storeToRefs(mainStore)
@@ -322,7 +325,8 @@ const {
   createLatexElement,
   createVideoElement,
   createAudioElement,
-  createCloudCoachElement
+  createCloudCoachElement,
+  createEnjoyElement
 } = useCreateElement()
 
 const insertImageElement = (files: FileList) => {
@@ -367,7 +371,49 @@ function handleCloudCoach(id: string, name: string) {
   createCloudCoachElement(id, name)
   cloudCoachVisible.value = false
 }
-
+// 处理资源创建
+function handleResources(item: Record<string, any>) {
+  if (item.type === "SONG") {
+    // 当前页没有enjoy的时候新增,有的时候添加
+    const enjoyElement = useSlidesHook.currentSlide.elements.find(itemV => {
+      return itemV.type === "elf" && itemV.subtype === "elf-enjoy"
+    })
+    if (enjoyElement) {
+      if (enjoyElement.enjoyList.length >= 10) {
+        ElMessage({
+          showClose: true,
+          message: "单个播放器最多添加10首曲目!",
+          type: "warning"
+        })
+        return
+      }
+      const enjoyData = enjoyElement.enjoyList.find(itemV => {
+        return itemV.id === item.id
+      })
+      if (enjoyData) {
+        ElMessage({
+          showClose: true,
+          message: "请勿重复添加曲目!",
+          type: "warning"
+        })
+      } else {
+        enjoyElement.enjoyList.push({
+          id: item.id,
+          title: item.name,
+          src: item.content
+        })
+      }
+    } else {
+      createEnjoyElement(item.id, item.name, item.content)
+    }
+  } else if (item.type === "IMG") {
+    createImageElement(item.content)
+    resourcesListVisible.value = false
+  } else if (item.type === "VIDEO") {
+    createVideoElement(item.content)
+    resourcesListVisible.value = false
+  }
+}
 // 绘制文字范围
 const drawText = (vertical = false) => {
   mainStore.setCreatingElement({

+ 102 - 0
src/views/components/element/enjoyElement/enjoyElement.vue

@@ -0,0 +1,102 @@
+<template>
+  <div
+    class="editable-element-enjoyElement"
+    :class="{ lock: elementInfo.lock }"
+    :style="{
+      top: elementInfo.top + 'px',
+      left: elementInfo.left + 'px',
+      width: elementInfo.width + 'px',
+      height: elementInfo.height + 'px'
+    }"
+  >
+    <div class="rotate-wrapper" :style="{ transform: `rotate(${elementInfo.rotate}deg)` }">
+      <div
+        class="element-content"
+        v-contextmenu="contextmenus"
+        @mousedown="$event => handleSelectElement($event, false)"
+        @touchstart="$event => handleSelectElement($event, false)"
+      >
+        <enjoyPlayer :elementInfo="elementInfo" :scale="canvasScale" />
+        <div
+          :class="['handler-border', item]"
+          v-for="item in ['t', 'b', 'l', 'r']"
+          :key="item"
+          @mousedown="$event => handleSelectElement($event)"
+          @touchstart="$event => handleSelectElement($event)"
+        ></div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import type { PPTEnjoyElement } from "@/types/slides"
+import type { ContextmenuItem } from "@/components/Contextmenu/types"
+import enjoyPlayer from "./enjoyPlayer.vue"
+import { storeToRefs } from "pinia"
+import { useMainStore } from "@/store"
+
+const props = defineProps<{
+  elementInfo: PPTEnjoyElement
+  selectElement: (e: MouseEvent | TouchEvent, element: PPTEnjoyElement, canMove?: boolean) => void
+  contextmenus: () => ContextmenuItem[] | null
+}>()
+
+const mainStore = useMainStore()
+const { canvasScale } = storeToRefs(mainStore)
+
+const handleSelectElement = (e: MouseEvent | TouchEvent, canMove = true) => {
+  if (props.elementInfo.lock) return
+  e.stopPropagation()
+
+  props.selectElement(e, props.elementInfo, canMove)
+}
+</script>
+
+<style lang="scss" scoped>
+.editable-element-enjoyElement {
+  position: absolute;
+
+  &.lock .handler-border {
+    cursor: default;
+  }
+}
+.rotate-wrapper {
+  width: 100%;
+  height: 100%;
+}
+.element-content {
+  width: 100%;
+  height: 100%;
+  position: relative;
+}
+.handler-border {
+  position: absolute;
+  cursor: move;
+
+  &.t {
+    width: 100%;
+    height: 20px;
+    top: 0;
+    left: 0;
+  }
+  &.b {
+    width: 100%;
+    height: 5px;
+    bottom: 0;
+    left: 0;
+  }
+  &.l {
+    width: 10px;
+    height: 100%;
+    left: 0;
+    top: 0;
+  }
+  &.r {
+    width: 10px;
+    height: 100%;
+    right: 0;
+    top: 0;
+  }
+}
+</style>

+ 527 - 0
src/views/components/element/enjoyElement/enjoyPlayer.vue

@@ -0,0 +1,527 @@
+<template>
+  <div class="enjoyPlayer">
+    <div v-show="isShowEnjoyPlayerList" class="enjoyPlayerList">
+      <div class="titNameCon">
+        <div class="titName">
+          {{ `音频列表 (${elementInfo.enjoyList?.length || 0})` }}
+        </div>
+      </div>
+      <div class="enjoyPlayerListCon">
+        <draggable
+          :handle="'.drag-handle_enjoyPlayer'"
+          v-model="elementInfo.enjoyList"
+          itemKey="id"
+          :animation="200"
+          :scroll="true"
+          :scrollSensitivity="50"
+          @end="handleDragEnd"
+        >
+          <template #item="{ element }">
+            <div class="playerItem" :class="{ active: element.id === elementInfo.sid }" @click="handlePlayMusic(element)">
+              <div class="itemLeft">
+                {{ element.title }}
+              </div>
+              <div class="itemRight">
+                <div class="itemListBtn drag-handle_enjoyPlayer" @click.stop></div>
+                <div class="itemCloseBtn" @click.stop="handleEnjoyDel(element.id)"></div>
+              </div>
+            </div>
+          </template>
+        </draggable>
+      </div>
+    </div>
+    <audio
+      ref="audioRef"
+      :src="elementInfo.src"
+      @durationchange="handleDurationchange()"
+      @timeupdate="handleTimeupdate()"
+      @play="handlePlayed()"
+      @pause="handlePaused()"
+      @ended="handleEnded()"
+      @progress="handleProgress()"
+      @error="handleError()"
+    ></audio>
+    <img class="tipImg" src="./imgs/tip.png" alt="" />
+    <div class="operateBtn" :class="{ paused: paused }" @click="toggle"></div>
+    <div class="operateMidCon">
+      <div class="titleCon">
+        <div class="title">{{ elementInfo.title }}</div>
+        <div class="timesCon">
+          {{ `${ptime}/${dtime}` }}
+        </div>
+      </div>
+      <div class="bar-wrap" ref="playBarWrap" @mousedown="handleMousedownPlayBar()" @touchstart="handleMousedownPlayBar()">
+        <div class="bar">
+          <div class="loaded" :style="{ width: loadedBarWidth }"></div>
+          <div class="played" :style="{ width: playedBarWidth }">
+            <div class="thumb"></div>
+          </div>
+        </div>
+      </div>
+    </div>
+    <div class="operateRightBtn">
+      <div class="preBtn" @click="handleChangeMusic('pre')"></div>
+      <div class="nextBtn" @click="handleChangeMusic('next')"></div>
+      <div class="listBtn" @click="isShowEnjoyPlayerList = !isShowEnjoyPlayerList"></div>
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { computed, ref } from "vue"
+import message from "@/utils/message"
+import type { PPTEnjoyElement } from "@/types/slides"
+import { useSlidesStore } from "@/store"
+import draggable from "vuedraggable"
+
+const slidesStore = useSlidesStore()
+const props = withDefaults(
+  defineProps<{
+    scale?: number
+    elementInfo: PPTEnjoyElement
+  }>(),
+  {
+    scale: 1
+  }
+)
+
+const isShowEnjoyPlayerList = ref(false)
+
+function handleDragEnd() {
+  slidesStore.updateElement({
+    id: props.elementInfo.id,
+    props: {
+      enjoyList: props.elementInfo.enjoyList
+    }
+  })
+}
+
+function handleEnjoyDel(id: string) {
+  if (id === props.elementInfo.sid) {
+    //删除组件
+    if (props.elementInfo.enjoyList.length === 1) {
+      slidesStore.deleteElement(props.elementInfo.id)
+    } else {
+      const index = props.elementInfo.enjoyList.findIndex(item => {
+        return item.id === id
+      })
+      // eslint-disable-next-line vue/no-mutating-props
+      props.elementInfo.enjoyList.splice(index, 1)
+      const enjoyList = props.elementInfo.enjoyList
+      slidesStore.updateElement({
+        id: props.elementInfo.id,
+        props: {
+          sid: enjoyList[0].id,
+          title: enjoyList[0].title,
+          src: enjoyList[0].src,
+          enjoyList: enjoyList
+        }
+      })
+      // 关闭播放按钮
+      paused.value = true
+    }
+  } else {
+    const index = props.elementInfo.enjoyList.findIndex(item => {
+      return item.id === id
+    })
+    // eslint-disable-next-line vue/no-mutating-props
+    props.elementInfo.enjoyList.splice(index, 1)
+    slidesStore.updateElement({
+      id: props.elementInfo.id,
+      props: {
+        enjoyList: props.elementInfo.enjoyList
+      }
+    })
+  }
+}
+
+function handleChangeMusic(type: "pre" | "next") {
+  if (props.elementInfo.enjoyList.length === 1) {
+    seek(0)
+  } else {
+    let index = props.elementInfo.enjoyList.findIndex(item => {
+      return item.id === props.elementInfo.sid
+    })
+    index += type === "next" ? 1 : -1
+    if (index > props.elementInfo.enjoyList.length - 1) {
+      index = 0
+    } else if (index < 0) {
+      index = props.elementInfo.enjoyList.length - 1
+    }
+    const enjoyData = props.elementInfo.enjoyList[index]
+    slidesStore.updateElement({
+      id: props.elementInfo.id,
+      props: {
+        sid: enjoyData.id,
+        title: enjoyData.title,
+        src: enjoyData.src
+      }
+    })
+  }
+}
+
+function handlePlayMusic(item: Record<string, any>) {
+  if (item.id === props.elementInfo.sid) {
+    toggle()
+  } else {
+    slidesStore.updateElement({
+      id: props.elementInfo.id,
+      props: {
+        sid: item.id,
+        title: item.title,
+        src: item.src
+      }
+    })
+  }
+}
+
+const secondToTime = (second = 0) => {
+  if (second === 0 || isNaN(second)) return "00:00"
+
+  const add0 = (num: number) => (num < 10 ? "0" + num : "" + num)
+  const hour = Math.floor(second / 3600)
+  const min = Math.floor((second - hour * 3600) / 60)
+  const sec = Math.floor(second - hour * 3600 - min * 60)
+  return (hour > 0 ? [hour, min, sec] : [min, sec]).map(add0).join(":")
+}
+
+const getBoundingClientRectViewLeft = (element: HTMLElement) => {
+  return element.getBoundingClientRect().left
+}
+
+const audioRef = ref<HTMLAudioElement>()
+const playBarWrap = ref<HTMLElement>()
+
+const paused = ref(true)
+const currentTime = ref(0)
+const duration = ref(0)
+const loaded = ref(0)
+
+const isThumbDown = ref(false)
+
+const ptime = computed(() => secondToTime(currentTime.value))
+const dtime = computed(() => secondToTime(duration.value))
+const playedBarWidth = computed(() => (currentTime.value / duration.value) * 100 + "%")
+const loadedBarWidth = computed(() => (loaded.value / duration.value) * 100 + "%")
+
+const seek = (time: number) => {
+  if (!audioRef.value) return
+
+  time = Math.max(time, 0)
+  time = Math.min(time, duration.value)
+
+  audioRef.value.currentTime = time
+  currentTime.value = time
+}
+
+const play = () => {
+  if (!audioRef.value) return
+
+  paused.value = false
+  audioRef.value.play()
+}
+
+const pause = () => {
+  if (!audioRef.value) return
+
+  paused.value = true
+  audioRef.value.pause()
+}
+
+const toggle = () => {
+  if (paused.value) play()
+  else pause()
+}
+
+const handleDurationchange = () => {
+  duration.value = audioRef.value?.duration || 0
+}
+
+const handleTimeupdate = () => {
+  currentTime.value = audioRef.value?.currentTime || 0
+}
+
+const handlePlayed = () => {
+  paused.value = false
+}
+
+const handlePaused = () => {
+  paused.value = true
+}
+const handleEnded = () => {
+  return
+  if (isThumbDown.value) {
+    return
+  }
+  seek(0)
+  play()
+}
+
+const handleProgress = () => {
+  loaded.value = audioRef.value?.buffered.length ? audioRef.value.buffered.end(audioRef.value.buffered.length - 1) : 0
+  if (!paused.value) {
+    play()
+  }
+}
+
+const handleError = () => message.error("音频加载失败")
+
+const thumbMove = (e: MouseEvent | TouchEvent) => {
+  if (!audioRef.value || !playBarWrap.value) return
+  const clientX = "clientX" in e ? e.clientX : e.changedTouches[0].clientX
+  let percentage = (clientX - getBoundingClientRectViewLeft(playBarWrap.value)) / playBarWrap.value.clientWidth / props.scale
+  percentage = Math.max(percentage, 0)
+  percentage = Math.min(percentage, 1)
+  const time = percentage * duration.value
+
+  audioRef.value.currentTime = time
+  currentTime.value = time
+}
+
+const thumbUp = (e: MouseEvent | TouchEvent) => {
+  if (!audioRef.value || !playBarWrap.value) return
+
+  const clientX = "clientX" in e ? e.clientX : e.changedTouches[0].clientX
+  let percentage = (clientX - getBoundingClientRectViewLeft(playBarWrap.value)) / playBarWrap.value.clientWidth / props.scale
+  percentage = Math.max(percentage, 0)
+  percentage = Math.min(percentage, 1)
+  const time = percentage * duration.value
+
+  audioRef.value.currentTime = time
+  currentTime.value = time
+  const _time = setTimeout(() => {
+    clearTimeout(_time)
+    isThumbDown.value = false
+  }, 500)
+  document.removeEventListener("mousemove", thumbMove)
+  document.removeEventListener("touchmove", thumbMove)
+  document.removeEventListener("mouseup", thumbUp)
+  document.removeEventListener("touchend", thumbUp)
+}
+
+const handleMousedownPlayBar = () => {
+  isThumbDown.value = true
+  document.addEventListener("mousemove", thumbMove)
+  document.addEventListener("touchmove", thumbMove)
+  document.addEventListener("mouseup", thumbUp)
+  document.addEventListener("touchend", thumbUp)
+}
+</script>
+
+<style lang="scss" scoped>
+.enjoyPlayer {
+  transform-origin: left top;
+  background: linear-gradient(180deg, #ffffff 0%, #dfdfdf 100%);
+  box-shadow:
+    0px 3px 16px 0px rgba(0, 0, 0, 0.08),
+    inset 0px -6px 6px 0px rgba(0, 0, 0, 0.08);
+  border-radius: 112px;
+  outline: 2px solid rgba(0, 0, 0, 0.03);
+  padding: 16px 21px;
+  display: flex;
+  align-items: center;
+  width: 670px;
+  position: relative;
+  .tipImg {
+    position: absolute;
+    width: 44px;
+    height: 12px;
+    right: 47px;
+    top: 7px;
+  }
+  .operateBtn {
+    flex-shrink: 0;
+    background: url("./imgs/pause.png") no-repeat;
+    background-size: 100% 100%;
+    width: 70px;
+    height: 70px;
+    cursor: pointer;
+    &.paused {
+      background: url("./imgs/play.png") no-repeat;
+      background-size: 100% 100%;
+    }
+  }
+  .operateMidCon {
+    margin: 0 21px;
+    flex-grow: 1;
+    .titleCon {
+      display: flex;
+      align-items: center;
+      justify-content: space-between;
+      .title {
+        width: 200px;
+        font-weight: 600;
+        font-size: 22px;
+        color: #131415;
+        line-height: 30px;
+        white-space: nowrap;
+        overflow: hidden;
+        text-overflow: ellipsis;
+      }
+      .timesCon {
+        flex-shrink: 0;
+        font-weight: 400;
+        font-size: 18px;
+        color: #999999;
+        line-height: 25px;
+      }
+    }
+    .bar-wrap {
+      position: relative;
+      cursor: pointer;
+      width: 100%;
+      margin-top: 12px;
+      .bar {
+        position: relative;
+        height: 10px;
+        width: 100%;
+        background: #dcdcdc;
+        box-shadow: inset 0px 2px 3px 0px #a9a9a9;
+        border-radius: 5px;
+        outline: 2px solid rgba(255, 255, 255, 0.5);
+        .loaded {
+          position: absolute;
+          left: 0;
+          top: 0;
+          bottom: 0;
+          background: rgba(255, 255, 255, 0.4);
+          height: 10px;
+          transition: all 0.5s ease;
+          will-change: width;
+          border-radius: 6px;
+        }
+        .played {
+          position: absolute;
+          left: 0;
+          top: 0;
+          bottom: 0;
+          height: 10px;
+          will-change: width;
+          background: linear-gradient(270deg, #97d1fd 0%, #0d93ff 100%);
+          border-radius: 6px;
+
+          .thumb {
+            position: absolute;
+            top: -12px;
+            right: -26px;
+            cursor: pointer;
+            width: 52px;
+            height: 38px;
+            background: url("./imgs/td.png") no-repeat;
+            background-size: 100% 100%;
+          }
+        }
+      }
+    }
+  }
+  .operateRightBtn {
+    flex-shrink: 0;
+    display: flex;
+    align-items: center;
+    .preBtn {
+      background: url("./imgs/pre.png") no-repeat;
+      background-size: 100% 100%;
+      width: 56px;
+      height: 56px;
+      margin-right: 14px;
+      cursor: pointer;
+    }
+    .nextBtn {
+      background: url("./imgs/next.png") no-repeat;
+      background-size: 100% 100%;
+      width: 56px;
+      height: 56px;
+      margin-right: 14px;
+      cursor: pointer;
+    }
+    .listBtn {
+      background: url("./imgs/list.png") no-repeat;
+      background-size: 100% 100%;
+      width: 56px;
+      height: 56px;
+      cursor: pointer;
+    }
+  }
+  .enjoyPlayerList {
+    width: 100%;
+    height: 520px;
+    position: absolute;
+    left: 0;
+    bottom: 51px;
+    box-shadow: 0px 3px 22px 0px rgba(0, 0, 0, 0.12);
+    border-radius: 20px 20px 0px 0px;
+    background-color: rgba(255, 255, 255, 0.9);
+    z-index: -1;
+    padding: 20px 0 61px 10px;
+    .titNameCon {
+      padding-left: 10px;
+      display: flex;
+      align-items: center;
+      height: 38px;
+      .titName {
+        font-weight: 600;
+        font-size: 22px;
+        color: #131415;
+        line-height: 30px;
+        position: relative;
+        &::after {
+          position: absolute;
+          z-index: -1;
+          left: 0;
+          bottom: 0;
+          content: "";
+          width: 100%;
+          height: 12px;
+          background: linear-gradient(90deg, #77bbff 0%, rgba(163, 231, 255, 0.22) 100%);
+        }
+      }
+    }
+    .enjoyPlayerListCon {
+      margin-top: 20px;
+      overflow-y: auto;
+      height: calc(100% - 58px);
+      padding-right: 10px;
+      .playerItem {
+        padding: 18px 5px 18px 10px;
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        &.active {
+          background: rgba(85, 176, 255, 0.15);
+          border-radius: 10px;
+          .itemLeft {
+            color: #198cfe;
+            font-weight: 600;
+          }
+        }
+        .itemLeft {
+          font-weight: 400;
+          font-size: 20px;
+          color: #131415;
+          line-height: 28px;
+        }
+        .itemRight {
+          display: flex;
+          .itemListBtn {
+            width: 34px;
+            height: 34px;
+            margin-right: 10px;
+            background: url("./imgs/td2.png") no-repeat;
+            background-size: 24px 24px;
+            background-position: center;
+            cursor: grab;
+          }
+          .itemCloseBtn {
+            width: 34px;
+            height: 34px;
+            background: url("./imgs/del.png") no-repeat;
+            background-size: 24px 24px;
+            background-position: center;
+            cursor: pointer;
+          }
+        }
+      }
+    }
+  }
+}
+</style>

BIN
src/views/components/element/enjoyElement/imgs/del.png


BIN
src/views/components/element/enjoyElement/imgs/list.png


BIN
src/views/components/element/enjoyElement/imgs/next.png


BIN
src/views/components/element/enjoyElement/imgs/pause.png


BIN
src/views/components/element/enjoyElement/imgs/play.png


BIN
src/views/components/element/enjoyElement/imgs/pre.png


BIN
src/views/components/element/enjoyElement/imgs/td.png


BIN
src/views/components/element/enjoyElement/imgs/td2.png


BIN
src/views/components/element/enjoyElement/imgs/tip.png


+ 2 - 0
src/views/components/element/enjoyElement/index.ts

@@ -0,0 +1,2 @@
+import enjoyElement from "./enjoyElement.vue"
+export default enjoyElement

+ 0 - 1
src/views/components/element/enjoyElement/resourcesList/resourcesList.vue

@@ -165,7 +165,6 @@ const emits = defineEmits<{
 
 function handleAddResources(item: Record<string, any>) {
   emits("update", item)
-  emits("close")
 }
 
 const tabData =