ソースを参照

富文本组件 添加音频模块

DESKTOP-SVI9JE1\muzen 2 年 前
コミット
355dcaac1e
2 ファイル変更295 行追加25 行削除
  1. 256 25
      src/components/Editor/index.vue
  2. 39 0
      src/store/modules/editor.js

+ 256 - 25
src/components/Editor/index.vue

@@ -4,7 +4,75 @@
       :on-error="handleUploadError" :data="data" name="file" :show-file-list="false" :headers="headers"
       style="display: none" ref="upload" v-if="isShow()">
     </el-upload>
+    <el-upload :action="audioUrl" :before-upload="handleBeforeAudio" :on-success="handleAudioSuccess"
+      :on-error="handleAudioError" :data="audioData" :show-file-list="false" :headers="headers" style="display: none"
+      ref="audio" v-if="isShow()" v-loading.fullscreen.lock="fullScreenLoading">
+    </el-upload>
     <div class="editor" ref="editor" :style="styles"></div>
+
+    <!-- 弹窗 -->
+    <el-dialog :visible.sync="dialogVisible" title="选择音频" width="1000px">
+      <el-form inline size="mini">
+        <el-form-item label="音频类型:">
+          <el-select v-model="dialogForm.audioType" placeholder="请选择音频类型">
+            <el-option v-for="item in audioTypeOptions" :key="item.value" :value="item.value" :label="item.label" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="资源平台:">
+          <el-select v-model="dialogForm.platformId" placeholder="请选择资源平台" clearable>
+            <el-option v-for="item in platformOptions" :key="item.value" :value="item.value" :label="item.label" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="付费状态:">
+          <el-select v-model="dialogForm.isFree" placeholder="请选择付费类型" clearable>
+            <el-option v-for="item in freeOptions" :key="item.value" :label="item.label" :value="item.value" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="内容名称:">
+          <el-input v-model="dialogForm.keyword" placeholder="请输入内容名称" clearable />
+        </el-form-item>
+        <el-form-item>
+          <el-button type="primary" icon="el-icon-search" @click="getSearch">搜索</el-button>
+          <el-button icon="el-icon-refresh" @click="getRefresh">重置</el-button>
+          <el-button type="primary" icon="el-icon-upload2"
+            @click="$refs.audio.$children[0].$refs.input.click();">上传音频</el-button>
+        </el-form-item>
+      </el-form>
+      <el-table :data="tableData" v-loading="loading">
+        <el-table-column label="音频ID" prop="audioId" align="center" show-overflow-tooltip />
+        <el-table-column label="音频名称" prop="audioName" align="center" show-overflow-tooltip />
+        <el-table-column label="音频封面" align="center" width="100px">
+          <template slot-scope="scope">
+            <el-image v-if="scope.row.audioPic" :src="scope.row.audioPic" />
+          </template>
+        </el-table-column>
+        <el-table-column label="音频作者" align="center" show-overflow-tooltip>
+          <template slot-scope="scope">
+            <span>
+              {{ scope.row.singerName ? scope.row.singerName : '-' }}
+            </span>
+          </template>
+        </el-table-column>
+        <el-table-column label="专辑名称" prop="songName" align="center" show-overflow-tooltip>
+          <template slot-scope="scope">
+            <span>
+              {{ scope.row.songName ? scope.row.songName : '-' }}
+            </span>
+          </template>
+        </el-table-column>
+        <el-table-column label="付费类型" prop="isFree" align="center" :formatter="freeFormatter" />
+        <el-table-column label="资源平台" align="center" :formatter="platfromFormatter" />
+        <el-table-column label="操作" align="center">
+          <template slot-scope="scope">
+            <el-button type="text" @click="handleChecked(scope.row)">选择</el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+      <div slot="footer">
+        <pagination v-show="total > 0" :total="total" :page.sync="dialogForm.pageNum" :limit.sync="dialogForm.pageSize"
+          @pagination="getList" />
+      </div>
+    </el-dialog>
   </div>
 </template>
 
@@ -14,8 +82,13 @@ import "quill/dist/quill.core.css";
 import "quill/dist/quill.snow.css";
 import "quill/dist/quill.bubble.css";
 import { getToken } from "@/utils/auth";
+import Audio from "@/store/modules/editor";
+Quill.register(Audio, true);
 
+import { list } from '@/api/operation/channel'
+import { platformMixin, isFreeMixin, channelMixin } from '@/mixin/index'
 export default {
+  mixins: [platformMixin, isFreeMixin, channelMixin],
   name: "Editor",
   props: {
     /* 编辑器的内容 */
@@ -51,11 +124,16 @@ export default {
   },
   data() {
     return {
+      // 遮罩层
+      fullScreenLoading: false,
+      loading: false,
       uploadUrl: process.env.VUE_APP_BASE_API + "/system/file/picture/upload", // 上传的图片服务器地址
+      audioUrl: process.env.VUE_APP_BASE_API + "/system/file/mp3/upload", // 上传的音频服务器地址
       headers: {
         Authorization: "Bearer " + getToken()
       },
       data: {},
+      audioData: {},
       Quill: null,
       currentValue: "",
       options: {
@@ -64,22 +142,52 @@ export default {
         debug: "warn",
         modules: {
           // 工具栏配置
-          toolbar: [
-            ["bold", "italic", "underline", "strike"],       // 加粗 斜体 下划线 删除线
-            ["blockquote", "code-block"],                    // 引用  代码块
-            [{ list: "ordered" }, { list: "bullet" }],       // 有序、无序列表
-            [{ indent: "-1" }, { indent: "+1" }],            // 缩进
-            [{ size: ["small", false, "large", "huge"] }],   // 字体大小
-            [{ header: [1, 2, 3, 4, 5, 6, false] }],         // 标题
-            [{ color: [] }, { background: [] }],             // 字体颜色、字体背景颜色
-            [{ align: [] }],                                 // 对齐方式
-            ["clean"],                                       // 清除文本格式
-            ["link", "image", "video"]                       // 链接、图片、视频
-          ],
+          toolbar: {
+            container: [
+              ["bold", "italic", "underline", "strike"],       // 加粗 斜体 下划线 删除线
+              ["blockquote", "code-block"],                    // 引用  代码块
+              [{ list: "ordered" }, { list: "bullet" }],       // 有序、无序列表
+              // [{ indent: "-1" }, { indent: "+1" }],            // 缩进
+              [{ size: ["small", false, "large", "huge"] }],   // 字体大小
+              [{ header: [1, 2, 3, 4, 5, 6, false] }],         // 标题
+              [{ color: [] }, { background: [] }],             // 字体颜色、字体背景颜色
+              [{ align: [] }],                                 // 对齐方式
+              ["clean"],                                       // 清除文本格式
+              ["link", "image", "video", "audio"]              // 链接、图片、视频
+            ]
+          }
+        },
+        initAudioButton: function () {
+          const AudioButton = document.querySelector('.ql-audio')
+          AudioButton.classList.add('el-icon-headset')
+          AudioButton.style.cssText = "font-size: 16px; font-weight: bold"
         },
         placeholder: "请输入内容",
         readOnly: this.readOnly,
       },
+
+      // 弹窗
+      dialogVisible: false,
+      dialogForm: {
+        pageNum: 1,
+        pageSize: 10,
+        status: 1,
+        audioType: 2
+      },
+      // 列表
+      tableData: [],
+      total: 0,
+      // 音频类型
+      audioTypeOptions: [{
+        value: 2,
+        label: '广播电台'
+      }, {
+        value: 6,
+        label: '节目'
+      }, {
+        value: 11,
+        label: '歌曲'
+      }]
     };
   },
   computed: {
@@ -105,6 +213,14 @@ export default {
         }
       },
       immediate: true,
+    },
+    'dialogForm.audioType'(val) {
+      if (val) {
+        this.getPlatform({
+          audioType: val
+        })
+        this.getList()
+      }
     }
   },
   mounted() {
@@ -117,18 +233,9 @@ export default {
     init() {
       const editor = this.$refs.editor;
       this.Quill = new Quill(editor, this.options);
-      // 如果设置了上传地址则自定义图片上传事件
-      if (this.type == 'url') {
-        let toolbar = this.Quill.getModule("toolbar");
-        toolbar.addHandler("image", (value) => {
-          this.uploadType = "image";
-          if (value) {
-            this.$refs.upload.$children[0].$refs.input.click();
-          } else {
-            this.quill.format("image", false);
-          }
-        });
-      }
+      this.editorImage()
+      this.editorAudio()
+      this.options.initAudioButton()
       this.Quill.pasteHTML(this.currentValue);
       this.Quill.on("text-change", (delta, oldDelta, source) => {
         const html = this.$refs.editor.children[0].innerHTML;
@@ -148,6 +255,20 @@ export default {
         this.$emit("on-editor-change", eventName, ...args);
       });
     },
+    editorImage() {
+      // 如果设置了上传地址则自定义图片上传事件
+      if (this.type == 'url') {
+        let toolbar = this.Quill.getModule("toolbar");
+        toolbar.addHandler("image", (value) => {
+          this.uploadType = "image";
+          if (value) {
+            this.$refs.upload.$children[0].$refs.input.click();
+          } else {
+            this.quill.format("image", false);
+          }
+        });
+      }
+    },
     handleBeforeUpload(file) {
       this.data.multipartFile = file
     },
@@ -173,7 +294,103 @@ export default {
       if (!this.readOnly) {
         return this.type == 'url'
       }
-    }
+    },
+
+    // 音频
+    editorAudio() {
+      let toolbar = this.Quill.getModule("toolbar");
+      toolbar.addHandler("audio", (value) => {
+        if (value) {
+          this.dialogVisible = true
+          this.getPlatform({
+            audioType: 2
+          })
+          this.getList()
+        } else {
+          this.quill.format("audio", false);
+        }
+      });
+    },
+
+    handleBeforeAudio(file) {
+      this.fullScreenLoading = true
+      this.audioData.multipartFile = file
+    },
+
+    handleAudioSuccess(res) {
+      this.fullScreenLoading = false
+      let quill = this.Quill
+      if (res.code == 0) {
+        let length = quill.getSelection().index;
+        quill.insertEmbed(
+          length + 1,
+          'audio',
+          { src: res.data.url, name: res.data.realName },
+          "api"
+        );
+        quill.insertText(length + 2, "");
+        quill.setSelection(length + 2);
+      } else {
+        this.$message.error(res.message)
+      }
+    },
+
+    handleAudioError() {
+      this.fullScreenLoading = false
+      this.$message.error('插入失败')
+    },
+
+    // 列表
+    getList() {
+      this.loading = true
+      list(this.dialogForm).then(res => {
+        if (res.code === 0) {
+          this.tableData = res.data.records
+          this.total = res.data.total
+          this.loading = false
+        }
+      })
+    },
+
+    // 搜索
+    getSearch() {
+      this.dialogForm.pageNum = 1
+      this.getList()
+    },
+
+    // 重置
+    getRefresh() {
+      this.dialogForm = {
+        pageNum: 1,
+        pageSize: 10,
+        status: 1,
+        audioType: 2
+      }
+      this.getList()
+    },
+
+    // 选择音频
+    handleChecked(row) {
+      let quill = this.Quill
+      let length = quill.getSelection().index;
+      quill.insertEmbed(
+        length + 1,
+        'audio',
+        { src: '', name: row.audioName, poster: row.audioPic, id: row.audioId },
+        "api"
+      );
+      quill.insertText(length + 2, "");
+      quill.setSelection(length + 2);
+      this.$message.success('选择成功!')
+    },
+
+    // 字典翻译
+    freeFormatter(row) {
+      return this.selectDictLabel(this.freeOptions, row.isFree)
+    },
+    platfromFormatter(row) {
+      return this.selectDictLabel(this.platformOptions, row.platformId)
+    },
   },
 };
 </script>
@@ -184,12 +401,15 @@ export default {
   white-space: pre-wrap !important;
   line-height: normal !important;
 }
+
 .quill-img {
   display: none;
 }
+
 .ql-snow .ql-tooltip[data-mode='link']::before {
   content: '请输入链接地址:';
 }
+
 .ql-snow .ql-tooltip.ql-editing a.ql-action::after {
   border-right: 0px;
   content: '保存';
@@ -204,14 +424,17 @@ export default {
 .ql-snow .ql-picker.ql-size .ql-picker-item::before {
   content: '14px';
 }
+
 .ql-snow .ql-picker.ql-size .ql-picker-label[data-value='small']::before,
 .ql-snow .ql-picker.ql-size .ql-picker-item[data-value='small']::before {
   content: '10px';
 }
+
 .ql-snow .ql-picker.ql-size .ql-picker-label[data-value='large']::before,
 .ql-snow .ql-picker.ql-size .ql-picker-item[data-value='large']::before {
   content: '18px';
 }
+
 .ql-snow .ql-picker.ql-size .ql-picker-label[data-value='huge']::before,
 .ql-snow .ql-picker.ql-size .ql-picker-item[data-value='huge']::before {
   content: '32px';
@@ -221,26 +444,32 @@ export default {
 .ql-snow .ql-picker.ql-header .ql-picker-item::before {
   content: '文本';
 }
+
 .ql-snow .ql-picker.ql-header .ql-picker-label[data-value='1']::before,
 .ql-snow .ql-picker.ql-header .ql-picker-item[data-value='1']::before {
   content: '标题1';
 }
+
 .ql-snow .ql-picker.ql-header .ql-picker-label[data-value='2']::before,
 .ql-snow .ql-picker.ql-header .ql-picker-item[data-value='2']::before {
   content: '标题2';
 }
+
 .ql-snow .ql-picker.ql-header .ql-picker-label[data-value='3']::before,
 .ql-snow .ql-picker.ql-header .ql-picker-item[data-value='3']::before {
   content: '标题3';
 }
+
 .ql-snow .ql-picker.ql-header .ql-picker-label[data-value='4']::before,
 .ql-snow .ql-picker.ql-header .ql-picker-item[data-value='4']::before {
   content: '标题4';
 }
+
 .ql-snow .ql-picker.ql-header .ql-picker-label[data-value='5']::before,
 .ql-snow .ql-picker.ql-header .ql-picker-item[data-value='5']::before {
   content: '标题5';
 }
+
 .ql-snow .ql-picker.ql-header .ql-picker-label[data-value='6']::before,
 .ql-snow .ql-picker.ql-header .ql-picker-item[data-value='6']::before {
   content: '标题6';
@@ -250,10 +479,12 @@ export default {
 .ql-snow .ql-picker.ql-font .ql-picker-item::before {
   content: '标准字体';
 }
+
 .ql-snow .ql-picker.ql-font .ql-picker-label[data-value='serif']::before,
 .ql-snow .ql-picker.ql-font .ql-picker-item[data-value='serif']::before {
   content: '衬线字体';
 }
+
 .ql-snow .ql-picker.ql-font .ql-picker-label[data-value='monospace']::before,
 .ql-snow .ql-picker.ql-font .ql-picker-item[data-value='monospace']::before {
   content: '等宽字体';

+ 39 - 0
src/store/modules/editor.js

@@ -0,0 +1,39 @@
+import Quill from 'quill';
+
+const BlockEmbed = Quill.import('blots/block/embed')
+
+class Audio extends BlockEmbed {
+  static create(value) {
+    // console.log(value, 'value')
+    const node = super.create(value);
+    node.setAttribute('src', value.src);
+    node.setAttribute('controls', true);
+    node.setAttribute('name', value.name.replace(' ', ''));
+    node.setAttribute('id', value.id);
+    node.setAttribute('poster', value.poster)
+    return node;
+  }
+  // 添加value获取当前的audio元素。拿到audio元素的属性。
+  static value(domNode) {
+    // console.log(domNode, 'domNode');
+    const value = {
+      src: '',
+      name: '',
+      id: '',
+      controls: true,
+      poster: ''
+    };
+    // 这里要加判断。不然会显示undefined
+    value.src = domNode.getAttribute('src');
+    value.name = domNode.getAttribute('name');
+    value.poster = domNode.getAttribute('poster');
+    value.id = domNode.getAttribute('id');
+    return value;
+  }
+}
+
+Audio.blotName = 'audio'
+Audio.className = 'ql-audio'
+Audio.tagName = 'audio'
+
+export default Audio