Przeglądaj źródła

编辑器-基础-背景音乐

任一存 3 lat temu
rodzic
commit
1336825ad9

+ 18 - 0
src/assets/images/icons/pause_for_editor.svg

@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="8px" height="8px" viewBox="0 0 8 8" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>pause</title>
+    <g id="管理中心" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="管理中心-音频" transform="translate(-617.000000, -833.000000)" fill="#0076F6">
+            <g id="编组-7备份-7" transform="translate(580.000000, 796.000000)">
+                <g id="编组-8" transform="translate(16.000000, 10.000000)">
+                    <g id="编组-9" transform="translate(16.000000, 22.000000)">
+                        <g id="pause" transform="translate(5.000000, 5.000000)">
+                            <rect id="矩形" x="1" y="1" width="2" height="6" rx="1"></rect>
+                            <rect id="矩形" x="5" y="1" width="2" height="6" rx="1"></rect>
+                        </g>
+                    </g>
+                </g>
+            </g>
+        </g>
+    </g>
+</svg>

Plik diff jest za duży
+ 17 - 0
src/assets/images/icons/play_for_editor.svg


+ 166 - 0
src/components/audio/audioForEditor.vue

@@ -0,0 +1,166 @@
+<template>
+  <div class="audio-wrapper">
+    <audio ref="audio">
+      <source :src="myAudioUrl" type="audio/mp3" />
+    </audio>
+    <div class="UI" @click="switchPlayPause">
+      <div ref="circle-left" class="left"></div>
+      <div ref="circle-right" class="right"></div>
+      <div class="circle" :style="{background: backgroundColor}"></div>
+      <img v-if="notPlaying" :src="require('@/assets/images/icons/play_for_editor.svg')" />
+      <img v-if="!notPlaying" :src="require('@/assets/images/icons/pause_for_editor.svg')" />
+    </div>
+  </div>
+</template>
+ 
+<script>
+export default {
+  name: "myAudioForEditor",
+  props: [
+    "myAudioUrl",
+    "backgroundColor",
+  ],
+  data() {
+    return {
+      audio: "",
+      notPlaying: true,
+      progress: 0,
+    };
+  },
+  mounted() {
+    this.init()
+  },
+  methods: {
+    init() {
+      this.$nextTick(() => { // props的变化并不会立刻反映到html element的attribute上
+        if (this.audio) {
+          this.audio.pause()
+        }
+        if (this.$refs.audio) {
+          this.audio = this.$refs.audio;
+          this.audio.load();
+          this.audio.pause();
+          this.updateProgress();
+          this.notPlaying = true;
+          let self = this;
+          this.audio.addEventListener(
+            "timeupdate",
+            function () {
+              self.updateProgress();
+            },
+            false
+          );
+          this.audio.addEventListener(
+            "ended",
+            function () {
+              self.audioEnded();
+            },
+            false
+          );
+        }
+      })
+    },
+    loadCircle() {
+      let leftEl = this.$refs['circle-left']
+      let rightEl = this.$refs['circle-right']
+      if (!rightEl && !leftEl) {
+        return
+      }
+      if (this.progress) {
+        if (this.progress > 0 && this.progress < 50) {
+          rightEl.style.transform = "rotate(" + 3.6 * this.progress + "deg)";
+        } else {
+          rightEl.style.transform = "rotate(0)";
+          rightEl.style.background = "#033d7c";
+          leftEl.style.transform =
+            "rotate(" + 3.6 * (this.progress - 50) + "deg)";
+        }
+      } else {
+        rightEl.style.transform = "rotate(0deg)";
+        leftEl.style.transform = "rotate(0deg)";
+        rightEl.style.background = "#0076F6";
+      }
+    },
+    switchPlayPause() {
+      setTimeout(() => {
+        if (this.audio.paused) {
+          // 开始播放当前点击的音频
+          this.audio.play();
+          this.notPlaying = false;
+        } else {
+          this.audio.pause();
+          this.notPlaying = true;
+        }
+        this.updateProgress();
+      });
+    },
+    // 更新进度条与当前播放时间
+    updateProgress() {
+      let value = this.audio.currentTime / this.audio.duration;
+      this.progress = value * 100;
+      this.loadCircle();
+    },
+    // 播放结束时
+    audioEnded() {
+      this.progress = 0;
+      this.notPlaying = true;
+      this.$emit('audioEnded')
+      this.loadCircle();
+    },
+  },
+  watch: {
+    myAudioUrl: {
+      handler: function () {
+        this.init()
+      },
+    },
+  },
+};
+</script>
+ 
+<style lang="less" scoped>
+@radiusOuter: 16px;
+@pcolor: #0076F6;
+@radiusInner: 12px;
+
+.audio-wrapper {
+  width: @radiusOuter;
+  height: @radiusOuter;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+}
+
+.UI {
+  position: relative;
+  width: @radiusOuter;
+  height: @radiusOuter;
+  min-width: @radiusOuter;
+  background: #033d7c;
+
+  border-radius: 50%;
+  .left {
+    border-radius: 50%;
+    background: @pcolor;
+    clip: rect(0, @radiusOuter / 2, @radiusOuter, 0);
+  }
+  .right {
+    border-radius: 50%;
+    background: @pcolor;
+    clip: rect(0, @radiusOuter, @radiusOuter, @radiusOuter / 2);
+  }
+  .circle {
+    width: @radiusInner;
+    height: @radiusInner;
+    border-radius: 50%;
+  }
+  * {
+    position: absolute;
+    left: 0;
+    top: 0;
+    bottom: 0;
+    right: 0;
+    margin: auto;
+  }
+}
+</style>

+ 131 - 8
src/components/tableSelect2.vue

@@ -12,6 +12,10 @@
         <span class="text">全景图</span>
         <span class="text">全景图</span>
         <div v-if="currentMaterialType === 'pano'" class="bottom-line"></div>
         <div v-if="currentMaterialType === 'pano'" class="bottom-line"></div>
       </a>
       </a>
+      <a v-if="selectableType.includes('audio')" class="material-tab-item" @click.prevent="currentMaterialType = 'audio'">
+        <span class="text">音频</span>
+        <div v-if="currentMaterialType === 'pano'" class="bottom-line"></div>
+      </a>
       <a v-if="selectableType.includes('3D')" class="material-tab-item" @click.prevent="currentMaterialType = '3D'">
       <a v-if="selectableType.includes('3D')" class="material-tab-item" @click.prevent="currentMaterialType = '3D'">
         <span class="text">三维场景</span>
         <span class="text">三维场景</span>
         <div v-if="currentMaterialType === '3D'" class="bottom-line"></div>
         <div v-if="currentMaterialType === '3D'" class="bottom-line"></div>
@@ -116,6 +120,56 @@
       </div>
       </div>
     </div>
     </div>
 
 
+    <div class="table table-audio" v-show="currentMaterialType === 'audio'">
+      <div class="table-head-row">
+        <span class="table-head">1</span>
+        <span class="table-head" v-for="(item,i) in tableHeadersForAudio" :key="i">{{item.name}}</span>
+      </div>
+      <div
+        v-if="audioList.length !== 0 || hasMoreAudioData"
+        class="table-body"
+        v-infinite-scroll="requestMoreAudioData"
+        :infinite-scroll-disabled="!hasMoreAudioData || isRequestingMoreAudioData"
+      >
+        <div class="table-body-row" v-for="(item,i) in audioList" :key="i">
+          <span class="table-data">
+            <div class="checkbox">
+              <!-- 负责功能 -->
+              <input
+                type="checkbox"
+                @change="e => selectItem(item, e)"
+                :checked="select.some(i => i[primaryKey] === item[primaryKey])"
+              >
+              <!-- 负责外观 -->
+              <span class="for-outer-circle"></span>
+              <span class="for-inner-circle"></span>
+            </div>
+          </span>
+          <span class="table-data" v-for="(sub,idx) in tableHeadersForAudio" :key="idx">
+            <div v-if="sub.type=='audio'" class="list-img">
+              <img
+                :src="require('@/assets/images/icons/icon-music@2x.png')"
+                style="object-fit: contain;"
+                alt=""
+              >
+            </div>
+            <span class="ellipsis" v-else v-title="sub.key === 'name' ? item[sub.key] : ''">{{item[sub.key]}}</span>
+          </span>
+        </div>
+      </div>
+      <!-- 无数据时的提示 -->
+      <div v-if="audioList.length === 0 && !hasMoreAudioData" class="no-data">
+        <div v-if="latestUsedSearchKey">
+          <img :src="require('@/assets/images/default/empty_04_search.png')" alt="">
+          <span>{{'未搜索到结果~'}}</span>
+        </div>
+        <div v-if="!latestUsedSearchKey">
+          <img :src="require('@/assets/images/default/empty_04.png')" alt="">
+          <span>{{'暂无素材~'}}</span>
+        </div>
+      </div>
+    </div>
+
     <div class="btns">
     <div class="btns">
       <button class="ui-button upload-btn">
       <button class="ui-button upload-btn">
         <span>上传素材</span>
         <span>上传素材</span>
@@ -163,10 +217,15 @@ export default {
         return [
         return [
           'image',
           'image',
           'pano',
           'pano',
+          'audio',
           '3D',
           '3D',
         ]
         ]
       },
       },
     },
     },
+    initialMaterialType: {
+      type: String,
+      default: 'image',
+    },
   },
   },
   components:{
   components:{
     TooltipInEditor,
     TooltipInEditor,
@@ -184,38 +243,49 @@ export default {
           this.refreshMaterialList('image')
           this.refreshMaterialList('image')
         } else if (newVal === 'pano' && this.panoList.length === 0) {
         } else if (newVal === 'pano' && this.panoList.length === 0) {
           this.refreshMaterialList('pano')
           this.refreshMaterialList('pano')
+        } else if (newVal === 'audio' && this.audioList.length === 0) {
+          this.refreshMaterialList('audio')
         }
         }
       },
       },
       immediate: false,
       immediate: false,
     }
     }
   },
   },
   computed:{
   computed:{
-    tableHeadersForImage(){
-      return this.$MAPTABLEHEADER['image'].filter(item=>{
+    tableHeadersForImage() {
+      return this.$MAPTABLEHEADER['image'].filter(item => {
         return ['icon', 'name', 'fileSize', 'dpi'].includes(item.key)
         return ['icon', 'name', 'fileSize', 'dpi'].includes(item.key)
       })
       })
     },
     },
-    tableHeadersForPano(){
-      return this.$MAPTABLEHEADER['pano'].filter(item=>{
+    tableHeadersForPano() {
+      return this.$MAPTABLEHEADER['pano'].filter(item => {
         return ['icon', 'name', 'fileSize'].includes(item.key)
         return ['icon', 'name', 'fileSize'].includes(item.key)
       })
       })
     },
     },
+    tableHeadersForAudio() {
+      console.log(this.$MAPTABLEHEADER);
+      return this.$MAPTABLEHEADER['audio'].filter(item => {
+        return ['ossPath', 'name', 'fileSize'].includes(item.key)
+      })
+    },
   },
   },
   data () {
   data () {
     return {
     return {
       imageList: [],
       imageList: [],
       panoList: [],
       panoList: [],
+      audioList: [],
       
       
       select: [],
       select: [],
       searchKey:'', // 搜索关键词
       searchKey:'', // 搜索关键词
       latestUsedSearchKey: '',
       latestUsedSearchKey: '',
 
 
-      currentMaterialType: 'image',
+      currentMaterialType: this.initialMaterialType,
       
       
       isRequestingMoreImageData: false,
       isRequestingMoreImageData: false,
       isRequestingMorePanoData: false,
       isRequestingMorePanoData: false,
+      isRequestingMoreAudioData: false,
       hasMoreImageData: true,
       hasMoreImageData: true,
       hasMorePanoData: true,
       hasMorePanoData: true,
+      hasMoreAudioData: true,
     }
     }
   },
   },
 
 
@@ -283,13 +353,43 @@ export default {
           });
           });
           this.panoList = this.panoList.concat(newData)
           this.panoList = this.panoList.concat(newData)
           if (this.panoList.length === data.data.total) {
           if (this.panoList.length === data.data.total) {
-            this.hasMorePanoeData = false
+            this.hasMorePanoData = false
           }
           }
-          this.isRequestingMorePanoeData = false
+          this.isRequestingMorePanoData = false
           this.latestUsedSearchKey = latestUsedSearchKey
           this.latestUsedSearchKey = latestUsedSearchKey
         },
         },
         () => {
         () => {
-          this.isRequestingMorePanoeData = false
+          this.isRequestingMorePanoData = false
+          this.latestUsedSearchKey = latestUsedSearchKey
+        }
+      );
+    },
+    requestMoreAudioData() {
+      this.isRequestingMoreAudioData = true
+      const latestUsedSearchKey = this.searchKey
+      getMaterialList(
+        {
+          pageNum: Math.floor(this.audioList.length / config.PAGE_SIZE) + 1,
+          pageSize: config.PAGE_SIZE,
+          searchKey: this.searchKey,
+          type: 'audio',
+        },
+        (data) => {
+          const newData = data.data.list.map((i) => {
+            i.fileSize = changeByteUnit(Number(i.fileSize));
+            i.createTime = i.createTime.substring(0, i.createTime.length - 3)
+            i.updateTime = i.updateTime.substring(0, i.updateTime.length - 3)
+            return i;
+          });
+          this.audioList = this.audioList.concat(newData)
+          if (this.audioList.length === data.data.total) {
+            this.hasMoreAudioData = false
+          }
+          this.isRequestingMoreAudioData = false
+          this.latestUsedSearchKey = latestUsedSearchKey
+        },
+        () => {
+          this.isRequestingMoreAudioData = false
           this.latestUsedSearchKey = latestUsedSearchKey
           this.latestUsedSearchKey = latestUsedSearchKey
         }
         }
       );
       );
@@ -305,6 +405,11 @@ export default {
         this.hasMorePanoData = true
         this.hasMorePanoData = true
         this.panoList = []
         this.panoList = []
         this.requestMorePanoData()
         this.requestMorePanoData()
+      } else if (type === 'audio') {
+        this.isRequestingMoreAudioData = false
+        this.hasMoreAudioData = true
+        this.audioList = []
+        this.requestMoreAudioData()
       }
       }
     }, 700, false),
     }, 700, false),
   },
   },
@@ -540,6 +645,24 @@ export default {
   }
   }
 }
 }
 
 
+.table-audio .table-head,
+.table-audio .table-data {
+  &:nth-of-type(1) {
+    width: 50px;
+    color: transparent;
+  }
+  &:nth-of-type(2) {
+    width: calc(116px - 50px);
+  }
+  &:nth-of-type(3) {
+    width: calc(416px - 116px);
+    padding-right: 30px;
+  }
+  &:nth-of-type(4) {
+    width: calc(100% - 416px);
+  }
+}
+
 .checkbox {
 .checkbox {
   position: relative;
   position: relative;
   width: 100%;
   width: 100%;

+ 3 - 0
src/views/base/Toolbar.vue

@@ -63,6 +63,7 @@
         <OpeningAnimationSettings v-show="activeTab === '开场动画'"></OpeningAnimationSettings>
         <OpeningAnimationSettings v-show="activeTab === '开场动画'"></OpeningAnimationSettings>
         <PasswordSettings v-show="activeTab === '访问密码'"></PasswordSettings>
         <PasswordSettings v-show="activeTab === '访问密码'"></PasswordSettings>
         <AutoCruiseSettings v-show="activeTab === '自动巡游'"></AutoCruiseSettings>
         <AutoCruiseSettings v-show="activeTab === '自动巡游'"></AutoCruiseSettings>
+        <BackgroundMusicSettings v-show="activeTab === '背景音乐'"></BackgroundMusicSettings>
         <CustomLogoSettings v-show="activeTab === '自定义LOGO'"></CustomLogoSettings>
         <CustomLogoSettings v-show="activeTab === '自定义LOGO'"></CustomLogoSettings>
       </div>
       </div>
     </div>
     </div>
@@ -106,6 +107,7 @@ import OpeningTipSettings from '@/views/base/openingTipSettings.vue'
 import OpeningAnimationSettings from '@/views/base/openingAnimationSettings.vue'
 import OpeningAnimationSettings from '@/views/base/openingAnimationSettings.vue'
 import PasswordSettings from "@/views/base/passwordSettings.vue";
 import PasswordSettings from "@/views/base/passwordSettings.vue";
 import AutoCruiseSettings from '@/views/base/autoCruiseSettings.vue'
 import AutoCruiseSettings from '@/views/base/autoCruiseSettings.vue'
+import BackgroundMusicSettings from "@/views/base/backgroundMusicSettings.vue";
 import CustomLogoSettings from "@/views/base/customLogoSettings.vue";
 import CustomLogoSettings from "@/views/base/customLogoSettings.vue";
 import Table from "@/components/tableSelect";
 import Table from "@/components/tableSelect";
 import Table2 from "@/components/tableSelect2";
 import Table2 from "@/components/tableSelect2";
@@ -123,6 +125,7 @@ export default {
     OpeningAnimationSettings,
     OpeningAnimationSettings,
     PasswordSettings,
     PasswordSettings,
     AutoCruiseSettings,
     AutoCruiseSettings,
+    BackgroundMusicSettings,
     CustomLogoSettings,
     CustomLogoSettings,
   },
   },
   data() {
   data() {

+ 153 - 0
src/views/base/backgroundMusicSettings.vue

@@ -0,0 +1,153 @@
+<template>
+  <div class="background-music-settings">
+    <span class="title">设置背景音乐</span>
+    <br>
+    <button v-if="!(info && info.backgroundMusic && info.backgroundMusic.name)" @click="onClickMusicSelectionBtn">
+      <i class="iconfont icon-editor_add"></i>
+      添加音频
+    </button>
+    <template v-else>
+      <div class="music-display" @click.self="onClickCurrentMusic">
+        <Audio ref="my-audio" class="audio-control" :backgroundColor="'#1A1B1D'" :myAudioUrl="info.backgroundMusic.ossPath"></Audio>
+        <div class="name" v-title="info.backgroundMusic.name" @click="onClickCurrentMusic">{{info.backgroundMusic.name}}</div>
+        <i class="iconfont icon-editor_list_delete" @click.stop="onClickDeleteMusicBtn"></i>
+      </div>
+      <button @click="onClickMusicSelectionBtn">
+        <i class="iconfont icon-editor_update"></i>
+        更换音频
+      </button>
+    </template>
+
+    <div class="dialog" style="z-index: 2000" v-if="isShowSelectionWindow">
+      <Table2
+        title="选择素材"
+        @cancle="isShowSelectionWindow = false"
+        @submit="handleSubmitFromTableSelect2"
+        :selectableType="['audio']"
+        initialMaterialType="audio"
+      />
+    </div>
+  </div>
+</template>
+
+<script>
+import { mapGetters } from "vuex";
+import Table2 from "@/components/tableSelect2.vue";
+import Audio from "@/components/audio/audioForEditor.vue";
+
+export default {
+  components: {
+    Table2,
+    Audio,
+  },
+  data() {
+    return {
+      isShowSelectionWindow: false,
+    }
+  },
+  computed: {
+    ...mapGetters({
+      info:'info'
+    }),
+  },
+  methods: {
+    onClickMusicSelectionBtn() {
+      this.isShowSelectionWindow = true
+    },
+    onClickDeleteMusicBtn() {
+      this.info.backgroundMusic.id = null
+      this.info.backgroundMusic.name = null
+      this.info.backgroundMusic.ossPath = null
+    },
+    handleSubmitFromTableSelect2(selected) {
+      this.isShowSelectionWindow = false
+      this.info.backgroundMusic.id = selected[0].id
+      this.info.backgroundMusic.name = selected[0].name
+      this.info.backgroundMusic.ossPath = selected[0].ossPath
+    },
+    onClickCurrentMusic() {
+      if (this.$refs['my-audio']) {
+        this.$refs['my-audio'].switchPlayPause()
+      }
+    }
+  },
+  mounted() {
+  }
+}
+</script>
+
+<style lang="less" scoped>
+.background-music-settings {
+  padding: 24px 30px;
+  background: #252526;
+  height: 546px;
+  > .title {
+    font-size: 18px;
+    color: #FFFFFF;
+  }
+  > button {
+    margin-top: 16px;
+    width: 234px;
+    height: 40px;
+    background: #1A1B1D;
+    border-radius: 2px;
+    border: 1px solid #404040;
+    display: block;
+    color: #0076F6;
+    font-size: 14px;
+    cursor: pointer;
+    i {
+      font-size: 14px;
+    }
+  }
+  > .music-display {
+    cursor: pointer;
+    margin-top: 16px;
+    width: 234px;
+    height: 40px;
+    background: #1A1B1D;
+    border-radius: 2px;
+    border: 1px solid #404040;
+    color: #fff;
+    font-size: 14px;
+    position: relative;
+    &:hover {
+      color: #0076F6;
+      > .audio-control {
+        display: inline-block;
+      }
+      > i {
+        display: inline-block;
+      }
+    }
+    > .audio-control {
+      position: absolute;
+      top: 50%;
+      transform: translateY(-50%);
+      left: 18px;
+      display: none;
+    }
+    > .name {
+      position: absolute;
+      left: 50%;
+      top: 50%;
+      transform: translate(-50%, -50%);
+      width: 65%;
+      text-overflow: ellipsis;
+      overflow: hidden;
+      white-space: nowrap;
+      display: inline-block;
+    }
+    > i {
+      display: none;
+      position: absolute;
+      top: 50%;
+      transform: translateY(-50%);
+      right: 18px;
+      &:hover {
+        color: #FA5555;
+      }
+    }
+  }
+}
+</style>