浏览代码

Merge branch 'master' of http://60.205.254.193:3000/GCLS/eep_page

dsy 4 天之前
父节点
当前提交
5efa90d6cc

+ 41 - 5
src/components/PinyinText.vue

@@ -22,13 +22,14 @@
               :title="isPreview ? '' : '点击校对'"
               :style="{
                 cursor: isPreview ? '' : 'pointer',
-                'align-items': wIndex == 0 ? 'flex-start' : 'center',
+                'align-items': 'flex-start',
               }"
               @click="correctPinyin(word, block.paragraphIndex, block.oldIndex, wIndex)"
             >
               <span
                 v-if="pinyinPosition === 'top' && hasPinyinInParagraphNeVersion(block.paragraphIndex)"
                 class="pinyin"
+                :style="getPinyinStyle(word)"
               >
                 {{ getPinyinText(word) }}</span
               >
@@ -36,6 +37,7 @@
               <span
                 v-if="pinyinPosition !== 'top' && hasPinyinInParagraphNeVersion(block.paragraphIndex)"
                 class="pinyin"
+                :style="getPinyinStyle(word)"
               >
                 {{ getPinyinText(word) }}</span
               >
@@ -385,6 +387,39 @@ export default {
   },
 
   methods: {
+    getPinyinText(item) {
+      return this.checkShowPinyin(item.showPinyin) ? item.pinyin.replace(/\s+/g, '') : '\u200B';
+    },
+    // 拼音固定为拼音字体,跟随汉字的字号、颜色、粗细的样式
+    getPinyinStyle(item) {
+      const styles = {};
+
+      // 固定使用 League 字体
+      styles['font-family'] = 'League';
+
+      // 只继承字号和颜色
+      if (item.activeTextStyle) {
+        if (item.activeTextStyle.fontSize) {
+          styles['font-size'] = item.activeTextStyle.fontSize;
+        }
+        if (item.activeTextStyle.color) {
+          styles['color'] = item.activeTextStyle.color;
+        }
+      }
+
+      // 如果设置了全局字号,优先使用全局字号
+      if (this.isAllSetting && this.fontSize) {
+        styles['font-size'] = this.fontSize;
+        this.isAllSetting = false;
+      }
+
+      // 明确重置不应该继承的样式
+      styles['text-decoration'] = 'none';
+      styles['font-style'] = 'normal';
+      styles['letter-spacing'] = '0';
+
+      return styles;
+    },
     removeTagFromStack(tagStack, tagName) {
       for (let i = tagStack.length - 1; i >= 0; i--) {
         if (tagStack[i].tag === tagName) {
@@ -496,9 +531,6 @@ export default {
       }
       return this.isEnable(showPinyin);
     },
-    getPinyinText(item) {
-      return this.checkShowPinyin(item.showPinyin) ? item.pinyin.replace(/\s+/g, '') : '\u200B';
-    },
   },
 };
 </script>
@@ -550,7 +582,6 @@ export default {
     > span {
       display: inline-flex;
       flex-direction: column;
-      align-items: center;
     }
 
     .py-char {
@@ -561,6 +592,7 @@ export default {
     .pinyin {
       display: inline-block;
       min-height: 12px;
+      font-family: 'League';
 
       // font-family: 'PINYIN-B';
       // font-size: 12px;
@@ -568,6 +600,10 @@ export default {
       // line-height: 12px;
       // color: $font-color;
       line-height: 1em;
+      text-decoration: none !important;
+
+      // 防止继承父元素的删除线
+      text-decoration-skip-ink: none;
     }
   }
 

+ 22 - 18
src/components/RichText.vue

@@ -1,16 +1,7 @@
 <template>
   <div ref="richArea" class="rich-wrapper">
-    <Editor
-      v-bind="$attrs"
-      :id="id"
-      ref="richText"
-      model-events="change keyup undo redo setContent"
-      :value="value"
-      :class="['rich-text', isBorder ? 'is-border' : '']"
-      :init="init"
-      v-on="$listeners"
-      @onBlur="handleRichTextBlur"
-    />
+    <Editor v-bind="$attrs" :id="id" ref="richText" model-events="change keyup undo redo setContent" :value="value"
+      :class="['rich-text', isBorder ? 'is-border' : '']" :init="init" v-on="$listeners" @onBlur="handleRichTextBlur" />
     <div v-show="isShow" :style="contentmenu" class="contentmenu">
       <div v-if="isViewNote" @click="openExplanatoryNoteDialog">
         <SvgIcon icon-class="mark" size="14" />
@@ -402,9 +393,19 @@ export default {
             },
           });
 
+          // 注册自定义字间距图标
+          editor.ui.registry.addIcon(
+            'letter-spacing-icon',
+            `<svg viewBox="4 4 18 18" width="44" height="24">
+              <text x="17" y="18" text-anchor="middle" font-size="14" font-weight="bold" fill="#000">A</text>
+              <path d="M12 10 L6 10 M6 10 L7.5 8.5 M6 10 L7.5 11.5" stroke="#1E90FF" stroke-width="1.2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
+              <path d="M22 10 L28 10 M28 10 L26.5 8.5 M28 10 L26.5 11.5" stroke="#1E90FF" stroke-width="1.2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
+            </svg>`,
+          );
+
           // 添加字间距下拉菜单
           editor.ui.registry.addMenuButton('letterSpacing', {
-            icon: 'border-width',
+            icon: 'letter-spacing-icon',
             // text: '字间距',
             tooltip: '字间距',
             fetch: (callback) => {
@@ -652,6 +653,9 @@ export default {
     getRichContent() {
       return tinymce.get(this.id).getContent();
     },
+    getRichSelectionContent() {
+      return tinymce.get(this.id).selection.getContent();
+    },
     displayToolbar(isTitle, isInit) {
       if (!this.editorIsInited) {
         this.editorBeforeInitConfig['isTitle'] = isTitle;
@@ -995,7 +999,7 @@ export default {
       let start = editor.selection.getStart();
       if (isNodeType(start, 'span')) {
         let textContent = start.textContent;
-        let content = this.getRichContent();
+        let content = this.getRichSelectionContent();
         let str = textContent.split(content);
         start.remove();
         editor.selection.setContent(str.join(content));
@@ -1007,7 +1011,7 @@ export default {
     setContent() {
       let editor = tinymce.get(this.id);
       let start = editor.selection.getStart();
-      let content = this.getRichContent();
+      let content = this.getRichSelectionContent();
       if (isNodeType(start, 'span')) {
         let textContent = start.textContent;
         let str = textContent.split(content);
@@ -1174,7 +1178,7 @@ export default {
       let noteId = '';
       let editor = tinymce.get(this.id);
       let start = editor.selection.getStart();
-      let content = this.getRichContent();
+      let content = this.getRichSelectionContent();
       if (isNodeType(start, 'span')) {
         noteId = start.getAttribute('data-annotation-id');
       } else {
@@ -1194,7 +1198,7 @@ export default {
       let start = editor.selection.getStart();
       if (isNodeType(start, 'span')) {
         let textContent = start.textContent;
-        let content = this.getRichContent();
+        let content = this.getRichSelectionContent();
         let str = textContent.split(content);
         start.remove();
         editor.selection.setContent(str.join(content));
@@ -1316,7 +1320,7 @@ export default {
 
 <style lang="scss" scoped>
 .rich-text {
-  :deep + .tox {
+  :deep+.tox {
     .tox-sidebar-wrap {
       border: 1px solid $fill-color;
       border-radius: 4px;
@@ -1341,7 +1345,7 @@ export default {
   }
 
   &.is-border {
-    :deep + .tox.tox-tinymce {
+    :deep+.tox.tox-tinymce {
       border: $border;
       border-radius: 4px;
     }

+ 31 - 16
src/views/book/courseware/create/components/base/rich_text/RichText.vue

@@ -77,15 +77,17 @@
           <CheckWord :data="wordData" @saveWord="saveWord" />
         </el-dialog>
 
-        <el-button
-          v-show="isEnable(data.property.view_pinyin)"
-          style="margin-left: 10px"
-          class="btn"
-          @click.native="generatPinyin"
-          >生成拼音</el-button
-        >
-
-        <el-divider v-if="isEnable(data.property.view_pinyin)" content-position="left">拼音效果</el-divider>
+        <el-divider v-if="isEnable(data.property.view_pinyin)" content-position="left">
+          拼音效果
+          <el-button
+            v-show="isEnable(data.property.view_pinyin)"
+            type="text"
+            icon="el-icon-refresh"
+            title="刷新"
+            class="refresh-pinyin-btn"
+            @click.native="generatPinyin"
+          />
+        </el-divider>
         <PinyinText
           v-if="isEnable(data.property.view_pinyin)"
           :id="richId + '_pinyin_text'"
@@ -197,21 +199,21 @@ export default {
     },
   },
   methods: {
-    uploads(file_id, index, file_url) {
+    uploads(file_id, file_url) {
       this.data.audio_file_id = file_id;
     },
-    deleteFiles(index) {
+    deleteFiles() {
       this.data.audio_file_id = '';
     },
     // 生成音频
-    handleMatic(con, index) {
+    handleMatic(con) {
       TextToAudioFile({
         text: con.replace(/<[^>]+>/g, ''),
         voice_type: this.data.property.voice_type,
         emotion: this.data.property.emotion,
         speed_ratio: this.data.property.speed_ratio,
       })
-        .then(({ status, file_id, file_url }) => {
+        .then(({ status, file_id }) => {
           if (status === 1) {
             this.data.audio_file_id = file_id;
           }
@@ -220,7 +222,7 @@ export default {
     },
 
     async saveWord(saveArr) {
-      const text = this.data.content.replace(/<[^>]+>/g, '');
+      // const text = this.data.content.replace(/<[^>]+>/g, '');
       await this.createParsedTextInfoPinyin(null, null, saveArr);
       this.showWordFlag = false;
     },
@@ -248,8 +250,8 @@ export default {
         return;
       }
       let styles = this.$refs.richText.getFirstCharStyles();
-      let content = this.$refs.richText.getRichContent();
-      let text = content.replace(/<[^>]+>/g, '');
+      // let content = this.$refs.richText.getRichContent();
+      // let text = content.replace(/<[^>]+>/g, '');
       this.createParsedTextInfoPinyin(null, styles);
     },
     showSetting() {
@@ -498,4 +500,17 @@ export default {
     cursor: pointer;
   }
 }
+
+.refresh-pinyin-btn {
+  cursor: pointer;
+  outline: none;
+
+  &:focus {
+    outline: none;
+  }
+
+  &:active {
+    outline: none;
+  }
+}
 </style>

+ 148 - 0
src/views/book/courseware/create/components/common/ResourcesOperate.vue

@@ -0,0 +1,148 @@
+<template>
+  <div class="resources-operate">
+    <el-tooltip effect="dark" placement="top" content="提交到资源库">
+      <SvgIcon
+        v-if="isEnable(projectResourcePopedom.is_can_upload) && data.file_id"
+        icon-class="upload"
+        @click="handleSubmitToResource(data)"
+      />
+    </el-tooltip>
+    <SvgIcon
+      v-if="isEnable(projectResourcePopedom.is_can_download) && data.file_id"
+      icon-class="download"
+      @click="downLoad(data)"
+    />
+    <el-dialog
+      :visible.sync="visibleSubmitResource"
+      width="500px"
+      append-to-body
+      :show-close="true"
+      title="提交到资源库"
+      :close-on-click-modal="false"
+    >
+      <el-form ref="resourceForm" :model="resourceForm" :rules="resourceRules" label-width="60px">
+        <el-form-item prop="position" label="位置">
+          <el-radio-group v-model="resourceForm.position" size="medium">
+            <el-radio border v-for="node in node_list" :label="node.node_id">{{ node.node_name }}</el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item prop="name" label="名称">
+          <el-input v-model="resourceForm.name" placeholder="请输入名称" />
+        </el-form-item>
+        <el-form-item prop="label" label="标签">
+          <el-input v-model="resourceForm.label" placeholder="请输入标签" />
+        </el-form-item>
+        <el-form-item prop="intro" label="简介">
+          <el-input v-model="resourceForm.intro" type="textarea" :rows="4" placeholder="请输入简介" />
+        </el-form-item>
+      </el-form>
+
+      <template #footer>
+        <el-button @click="visibleSubmitResource = false">取 消</el-button>
+        <el-button :loading="loading" type="primary" @click="SubmitToResource">确 定</el-button>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { SubmitFileToResourceStore, GetBookCoursewarePath } from '@/api/book';
+import { getToken } from '@/utils/auth';
+import { isEnable } from '@/views/book/courseware/data/common';
+
+export default {
+  name: 'ResourcesOperate',
+  props: ['data'],
+  inject: ['courseware_id', 'project_id', 'getProjectResourcePopedom'],
+
+  data() {
+    return {
+      isEnable,
+      visibleSubmitResource: false,
+      node_list: [],
+      resourceForm: {
+        position: '',
+        name: '',
+        label: '',
+        intro: '',
+      },
+      resourceRules: {
+        position: [{ required: true, message: '请选择位置', trigger: 'blur' }],
+        name: [{ required: true, message: '请输入名称', trigger: 'blur' }],
+      },
+      loading: false,
+    };
+  },
+  computed: {
+    projectResourcePopedom() {
+      return this.getProjectResourcePopedom();
+    },
+  },
+  methods: {
+    // 提交到资源库
+    handleSubmitToResource(file) {
+      this.visibleSubmitResource = true;
+      this.curFile = file;
+      this.resourceForm = {
+        position: '',
+        name: file.name || '',
+        label: file.label || '',
+        intro: file.intro || '',
+      };
+      GetBookCoursewarePath({ id: this.courseware_id }).then((res) => {
+        this.node_list = res.path_list;
+      });
+    },
+    SubmitToResource() {
+      this.$refs.resourceForm.validate((valid) => {
+        if (valid) {
+          this.loading = true;
+          let data = {
+            project_id: this.project_id,
+            book_chapter_node_id: this.resourceForm.position || '',
+            file_id: this.curFile.file_id,
+            resource_info: {
+              name: this.resourceForm.name,
+              label: this.resourceForm.label,
+              intro: this.resourceForm.intro,
+            },
+          };
+
+          SubmitFileToResourceStore(data)
+            .then((res) => {
+              this.$message.success('操作成功!');
+              this.visibleSubmitResource = false;
+            })
+            .catch(() => {
+              this.loading = false;
+            })
+            .finally(() => {
+              this.loading = false;
+            });
+        }
+      });
+    },
+    // 下载文件
+    downLoad(file) {
+      let userInfor = getToken();
+      let AccessToken = '';
+      if (userInfor) {
+        AccessToken = userInfor.access_token;
+      }
+      let FileID = file.file_id;
+      let data = {
+        AccessToken,
+        FileID,
+      };
+      location.href = `${process.env.VUE_APP_EEP}/FileServer/WebFileDownload?AccessToken=${data.AccessToken}&FileID=${data.FileID}`;
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+.resources-operate {
+  display: flex;
+  column-gap: 12px;
+}
+</style>

+ 14 - 3
src/views/book/courseware/create/components/question/article/Article.vue

@@ -5,7 +5,7 @@
       <div v-loading="loading" class="article-wrapper">
         <el-input v-model="data.content" placeholder="输入" type="textarea" />
         <div class="btn-box">
-          <SelectUpload label="课文音频" type="audio" width="500px" @uploadSuccess="uploadAudioSuccess" />
+          <SelectUpload label="课文音频" type="audio" @uploadSuccess="uploadAudioSuccess" />
           <el-button :loading="autoLoading" @click="handleAutoAudio">自动生成音频</el-button>
         </div>
         <div v-if="data.mp3_list.length > 0" class="upload-file">
@@ -16,6 +16,7 @@
             </span>
           </div>
           <SvgIcon icon-class="delete-black" size="12" @click="removeFile" />
+          <ResourcesOperate :data="data.mp3_list[0]"></ResourcesOperate>
         </div>
         <span v-if="data.content" class="tips">说明:需先点击“生成分词”按钮后,再进行文章校对或生成字幕节点</span>
         <div v-if="data.content" class="btn-box">
@@ -132,6 +133,7 @@ import NewWord from './NewWord.vue';
 import Notes from './Notes.vue';
 import CheckPic from './CheckPic.vue';
 import CheckSubtitles from '@/views/book/courseware/create/components/question/voice_matrix/CheckSubtitles.vue';
+import ResourcesOperate from '../../common/ResourcesOperate.vue';
 
 import { getArticleData } from '@/views/book/courseware/data/article';
 import { TextToAudioFile } from '@/api/app';
@@ -156,6 +158,7 @@ export default {
     Notes,
     CheckPic,
     CheckSubtitles,
+    ResourcesOperate,
   },
   mixins: [ModuleMixin],
   data() {
@@ -367,8 +370,16 @@ export default {
       }
     },
     removeFile() {
-      this.data.mp3_list = [];
-      this.data.file_id_list = [];
+      this.$confirm('是否删除当前文件?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning',
+      })
+        .then(() => {
+          this.data.mp3_list = [];
+          this.data.file_id_list = [];
+        })
+        .catch(() => {});
     },
     // 校对文章
     checkArticle() {

+ 33 - 21
src/views/book/courseware/create/components/question/article/NewWord.vue

@@ -205,7 +205,6 @@
       <SelectUpload
         label="生词音频"
         type="audio"
-        width="500px"
         :style="{ marginBottom: data.audio_data.url.length === 0 ? '5px' : '' }"
         @uploadSuccess="uploadAudioSuccess"
       />
@@ -219,8 +218,9 @@
         </span>
       </div>
       <SvgIcon icon-class="delete-black" size="12" @click="removeFile('audio')" />
+      <ResourcesOperate :data="data.audio_data"></ResourcesOperate>
     </div>
-    <SelectUpload label="音频字幕(lrc)文件" :limit="1" type="lrc" width="500px" @uploadSuccess="uploadLrcSuccess" />
+    <SelectUpload label="音频字幕(lrc)文件" :limit="1" type="lrc" @uploadSuccess="uploadLrcSuccess" />
     <div v-if="data.lrc_data.url.length > 0" class="upload-file">
       <div class="file-name">
         <span>
@@ -229,6 +229,7 @@
         </span>
       </div>
       <SvgIcon icon-class="delete-black" size="12" @click="removeFile('lrc')" />
+      <ResourcesOperate :data="data.lrc_data"></ResourcesOperate>
     </div>
     <div class="lrc-box" style="margin: 5px 0">
       <div v-if="data.lrc_arr && data.lrc_arr.length > 0" class="lrc-box">
@@ -314,6 +315,8 @@ import SoundRecord from '@/views/book/courseware/create/components/question/fill
 import UploadAudio from '@/views/book/courseware/create/components/question/fill/components/UploadAudio.vue';
 import SelectUpload from '@/views/book/courseware/create/components/common/SelectUpload.vue';
 import UploadPicture from '../new_word/components/UploadPicture.vue';
+import ResourcesOperate from '../../common/ResourcesOperate.vue';
+
 import { getWordTime, prepareTranscribe, fileToBase64Text, getWordTimes } from '@/api/article';
 import { GetStaticResources, TextToAudioFile, GetTextToAudioConfParamList } from '@/api/app';
 import { PinyinBuild_OldFormat } from '@/api/book';
@@ -338,6 +341,7 @@ export default {
     RichText,
     UploadPicture,
     CheckSubtitles,
+    ResourcesOperate,
   },
   props: ['dataNewWord', 'unifiedAttrib'],
   mixins: [ModuleMixin],
@@ -473,25 +477,33 @@ export default {
      * @param {'audio' | 'lrc'} type
      */
     removeFile(type) {
-      if (type === 'audio') {
-        this.data.file_id_list = this.data.file_id_list.filter((item) => item !== this.data.audio_data.file_id);
-        this.data.audio_data = {
-          name: '',
-          media_duration: 0,
-          temporary_url: '',
-          url: '',
-          file_id: '',
-        };
-      } else if (type === 'lrc') {
-        this.data.file_id_list = this.data.file_id_list.filter((item) => item !== this.data.lrc_data.file_id);
-        this.data.lrc_data = {
-          name: '',
-          url: '',
-          id: '',
-          file_id: '',
-        };
-      }
-      this.data.lrc_arr = [];
+      this.$confirm('是否删除当前文件?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning',
+      })
+        .then(() => {
+          if (type === 'audio') {
+            this.data.file_id_list = this.data.file_id_list.filter((item) => item !== this.data.audio_data.file_id);
+            this.data.audio_data = {
+              name: '',
+              media_duration: 0,
+              temporary_url: '',
+              url: '',
+              file_id: '',
+            };
+          } else if (type === 'lrc') {
+            this.data.file_id_list = this.data.file_id_list.filter((item) => item !== this.data.lrc_data.file_id);
+            this.data.lrc_data = {
+              name: '',
+              url: '',
+              id: '',
+              file_id: '',
+            };
+          }
+          this.data.lrc_arr = [];
+        })
+        .catch(() => {});
     },
     uploads(file_id, index) {
       this.data.new_word_list[index].mp3_list = file_id;

+ 13 - 2
src/views/book/courseware/create/components/question/dialogue_article/Article.vue

@@ -129,7 +129,7 @@
           <el-button type="primary" size="small" @click="handleNotice">插入语境</el-button>
         </div>
         <div class="btn-box">
-          <SelectUpload label="课文音频" type="audio" width="500px" @uploadSuccess="uploadAudioSuccess" />
+          <SelectUpload label="课文音频" type="audio" @uploadSuccess="uploadAudioSuccess" />
           <el-button :loading="autoLoading" @click="handleAutoAudio">自动生成音频</el-button>
         </div>
         <div v-if="data.mp3_list.length > 0" class="upload-file">
@@ -140,6 +140,7 @@
             </span>
           </div>
           <SvgIcon icon-class="delete-black" size="12" @click="removeFile" />
+          <ResourcesOperate :data="data.mp3_list[0]"></ResourcesOperate>
         </div>
         <span class="tips">说明:需先点击“生成分词”按钮后,再进行文章校对或生成字幕节点</span>
         <div v-if="data.detail.length > 0" class="btn-box">
@@ -344,6 +345,7 @@ import CompareTime from '../article/CompareTime.vue';
 import NewWord from '../article/NewWord.vue';
 import Notes from '../article/Notes.vue';
 import CheckSubtitles from '@/views/book/courseware/create/components/question/voice_matrix/CheckSubtitles.vue';
+import ResourcesOperate from '../../common/ResourcesOperate.vue';
 
 import { getArticleData } from '@/views/book/courseware/data/dialogueArticle';
 import { fileUpload, TextToAudioFile } from '@/api/app';
@@ -367,6 +369,7 @@ export default {
     NewWord,
     Notes,
     CheckSubtitles,
+    ResourcesOperate,
   },
   mixins: [ModuleMixin],
   data() {
@@ -732,7 +735,15 @@ export default {
       }
     },
     removeFile() {
-      this.data.mp3_list = [];
+      this.$confirm('是否删除当前文件?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning',
+      })
+        .then(() => {
+          this.data.mp3_list = [];
+        })
+        .catch(() => {});
     },
     // 校对文章
     checkArticle() {

+ 14 - 4
src/views/book/courseware/create/components/question/image_text/ImageText.vue

@@ -24,7 +24,7 @@
           :max="800"
         />
       </div>
-      <SelectUpload label="音频" type="audio" width="500px" @uploadSuccess="uploadAudioSuccess" />
+      <SelectUpload label="音频" type="audio" @uploadSuccess="uploadAudioSuccess" />
       <div v-if="data.mp3_list.length > 0" class="upload-files">
         <div class="file-name">
           <span>
@@ -34,6 +34,7 @@
           <span> 完成 </span>
         </div>
         <SvgIcon icon-class="delete-black" size="12" @click="removeFile" />
+        <ResourcesOperate :data="data.mp3_list[0]"></ResourcesOperate>
       </div>
       <el-button v-if="data.mp3_list.length > 0" type="primary" size="small" @click="handleTimes">{{
         data.word_time.length === 0 ? '自动生成字幕时间' : '重新生成字幕时间'
@@ -278,6 +279,7 @@ import { getImageTextData, isEnable } from '@/views/book/courseware/data/imageTe
 import SelectUpload from '@/views/book/courseware/create/components/common/SelectUpload.vue';
 import CompareTime from '../article/CompareTime.vue';
 import CheckArticle from './CheckArticle.vue';
+import ResourcesOperate from '../../common/ResourcesOperate.vue';
 
 import { GetFileURLMap } from '@/api/app';
 import { fileToBase64Text, prepareTranscribe, getWordTime, getWordTimes } from '@/api/article';
@@ -285,7 +287,7 @@ import cnchar from 'cnchar';
 
 export default {
   name: 'ImageTextPage',
-  components: { UploadFile, SelectUpload, CompareTime, CheckArticle },
+  components: { UploadFile, SelectUpload, CompareTime, CheckArticle, ResourcesOperate },
   mixins: [ModuleMixin],
   data() {
     return {
@@ -368,8 +370,16 @@ export default {
       }
     },
     removeFile() {
-      this.data.file_id_list = this.data.file_id_list.filter((item) => item !== this.data.mp3_list[0].file_id);
-      this.data.mp3_list = [];
+      this.$confirm('是否删除当前文件?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning',
+      })
+        .then(() => {
+          this.data.file_id_list = this.data.file_id_list.filter((item) => item !== this.data.mp3_list[0].file_id);
+          this.data.mp3_list = [];
+        })
+        .catch(() => {});
     },
     startSelection(event) {
       this.isSelecting = true;

+ 33 - 21
src/views/book/courseware/create/components/question/new_word/NewWord.vue

@@ -350,7 +350,6 @@
         <SelectUpload
           label="生词音频"
           type="audio"
-          width="500px"
           :style="{ marginBottom: data.audio_data.url.length === 0 ? '5px' : '' }"
           @uploadSuccess="uploadAudioSuccess"
         />
@@ -365,8 +364,9 @@
           </span>
         </div>
         <SvgIcon icon-class="delete-black" size="12" @click="removeFile('audio')" />
+        <ResourcesOperate :data="data.audio_data"></ResourcesOperate>
       </div>
-      <SelectUpload label="音频字幕(lrc)文件" :limit="1" type="lrc" width="500px" @uploadSuccess="uploadLrcSuccess" />
+      <SelectUpload label="音频字幕(lrc)文件" :limit="1" type="lrc" @uploadSuccess="uploadLrcSuccess" />
       <div v-if="data.lrc_data.url.length > 0" class="upload-file">
         <div class="file-name">
           <span>
@@ -375,6 +375,7 @@
           </span>
         </div>
         <SvgIcon icon-class="delete-black" size="12" @click="removeFile('lrc')" />
+        <ResourcesOperate :data="data.lrc_data"></ResourcesOperate>
       </div>
       <div class="lrc-box" style="margin: 5px 0">
         <div v-if="data.lrc_arr && data.lrc_arr.length > 0" class="lrc-box">
@@ -419,6 +420,8 @@ import CheckSubtitles from '@/views/book/courseware/create/components/question/v
 
 import { getNewWordData, getOption } from '@/views/book/courseware/data/newWord';
 import SelectUpload from '@/views/book/courseware/create/components/common/SelectUpload.vue';
+import ResourcesOperate from '../../common/ResourcesOperate.vue';
+
 import { GetStaticResources, TextToAudioFile } from '@/api/app';
 import cnchar from 'cnchar';
 import { ChapterGetBookChapterStruct, PinyinBuild_OldFormat } from '@/api/book';
@@ -432,6 +435,7 @@ export default {
     UploadAudio,
     UploadPicture,
     CheckSubtitles,
+    ResourcesOperate,
   },
   mixins: [ModuleMixin],
   data() {
@@ -548,25 +552,33 @@ export default {
      * @param {'audio' | 'lrc'} type
      */
     removeFile(type) {
-      if (type === 'audio') {
-        this.data.file_id_list = this.data.file_id_list.filter((item) => item !== this.data.audio_data.file_id);
-        this.data.audio_data = {
-          name: '',
-          media_duration: 0,
-          temporary_url: '',
-          url: '',
-          file_id: '',
-        };
-      } else if (type === 'lrc') {
-        this.data.file_id_list = this.data.file_id_list.filter((item) => item !== this.data.lrc_data.file_id);
-        this.data.lrc_data = {
-          name: '',
-          url: '',
-          id: '',
-          file_id: '',
-        };
-      }
-      this.data.lrc_arr = [];
+      this.$confirm('是否删除当前文件?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning',
+      })
+        .then(() => {
+          if (type === 'audio') {
+            this.data.file_id_list = this.data.file_id_list.filter((item) => item !== this.data.audio_data.file_id);
+            this.data.audio_data = {
+              name: '',
+              media_duration: 0,
+              temporary_url: '',
+              url: '',
+              file_id: '',
+            };
+          } else if (type === 'lrc') {
+            this.data.file_id_list = this.data.file_id_list.filter((item) => item !== this.data.lrc_data.file_id);
+            this.data.lrc_data = {
+              name: '',
+              url: '',
+              id: '',
+              file_id: '',
+            };
+          }
+          this.data.lrc_arr = [];
+        })
+        .catch(() => {});
     },
     uploads(file_id, index) {
       this.data.new_word_list[index].mp3_list = file_id;

+ 29 - 24
src/views/book/courseware/create/components/question/voice_matrix/VoiceMatrix.vue

@@ -29,13 +29,7 @@
           </div>
         </div>
 
-        <SelectUpload
-          label="矩阵音频"
-          type="audio"
-          width="600px"
-          :is-show-resource="false"
-          @uploadSuccess="uploadAudioSuccess"
-        />
+        <SelectUpload label="矩阵音频" type="audio" :is-show-resource="false" @uploadSuccess="uploadAudioSuccess" />
         <div v-if="data.audio_data.url.length > 0" class="upload-file">
           <div class="file-name">
             <span>
@@ -44,6 +38,7 @@
             </span>
           </div>
           <SvgIcon icon-class="delete-black" size="12" @click="removeFile('audio')" />
+          <ResourcesOperate :data="data.audio_data"></ResourcesOperate>
         </div>
 
         <div class="upload-lrc">
@@ -51,7 +46,6 @@
             label="音频字幕(lrc)文件"
             :limit="1"
             type="lrc"
-            width="500px"
             :is-show-resource="false"
             @uploadSuccess="uploadLrcSuccess"
           />
@@ -66,6 +60,7 @@
             </span>
           </div>
           <SvgIcon icon-class="delete-black" size="12" @click="removeFile('lrc')" />
+          <ResourcesOperate :data="data.lrc_data"></ResourcesOperate>
         </div>
       </div>
 
@@ -100,6 +95,7 @@
 import ModuleMixin from '../../common/ModuleMixin';
 import SelectUpload from '@/views/book/courseware/create/components/common/SelectUpload.vue';
 import CheckSubtitles from './CheckSubtitles.vue';
+import ResourcesOperate from '../../common/ResourcesOperate.vue';
 
 import { getVoiceMatrixData, getOption } from '@/views/book/courseware/data/voiceMatrix';
 import { GetStaticResources } from '@/api/app';
@@ -109,6 +105,7 @@ export default {
   components: {
     SelectUpload,
     CheckSubtitles,
+    ResourcesOperate,
   },
   mixins: [ModuleMixin],
   data() {
@@ -210,22 +207,30 @@ export default {
      * @param {'audio' | 'lrc'} type
      */
     removeFile(type) {
-      if (type === 'audio') {
-        this.data.audio_data = {
-          name: '',
-          media_duration: 0,
-          temporary_url: '',
-          url: '',
-          file_id: '',
-        };
-      } else if (type === 'lrc') {
-        this.data.lrc_data = {
-          name: '',
-          url: '',
-          id: '',
-          file_id: '',
-        };
-      }
+      this.$confirm('是否删除当前文件?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning',
+      })
+        .then(() => {
+          if (type === 'audio') {
+            this.data.audio_data = {
+              name: '',
+              media_duration: 0,
+              temporary_url: '',
+              url: '',
+              file_id: '',
+            };
+          } else if (type === 'lrc') {
+            this.data.lrc_data = {
+              name: '',
+              url: '',
+              id: '',
+              file_id: '',
+            };
+          }
+        })
+        .catch(() => {});
     },
     BatchSetFormat(text) {
       this.$refs.richText.forEach((richText) => {

+ 6 - 1
src/views/book/courseware/preview/components/common/VideoPlay.vue

@@ -47,7 +47,12 @@
           <h2>您的浏览器<strong>不支持video标签</strong></h2>
         </video>
       </template>
-      <SvgIcon v-show="originPlay || showPlay" :class="{ hidden: hidden }" icon-class="paused" size="16" />
+      <SvgIcon
+        v-show="viewMethod === 'independent' && (originPlay || showPlay)"
+        :class="{ hidden: hidden }"
+        icon-class="paused"
+        size="16"
+      />
     </div>
   </div>
 </template>