Ver código fonte

用户端的选词自动翻译

zq 4 dias atrás
pai
commit
85971c611d

+ 8 - 0
src/api/book.js

@@ -9,6 +9,14 @@ export function PinyinBuild_OldFormat(data) {
 }
 
 /**
+ * @description 文本翻译
+ * @param {object} data
+ */
+export function Texttrans(data) {
+  return http.post(`${process.env.VUE_APP_EepServer}?MethodName=tool-Texttrans`, data);
+}
+
+/**
  * @description 得到教材章节结构
  * @param {object} data
  */

+ 93 - 162
src/components/CommonPreview.vue

@@ -17,24 +17,15 @@
         </div>
         <div v-if="isShowGroup">
           <span class="link" @click="isShowGroup = false">取消显示分组</span>
-          <span
-            class="link"
-            @click="
-              groupShowAll = false;
-              isShowGroup = false;
-            "
-            >完成选择</span
-          >
+          <span class="link" @click="
+            groupShowAll = false;
+          isShowGroup = false;
+          ">完成选择</span>
         </div>
-        <span
-          v-else
-          class="link"
-          @click="
-            isShowGroup = true;
-            groupShowAll = true;
-          "
-          >显示分组</span
-        >
+        <span v-else class="link" @click="
+          isShowGroup = true;
+        groupShowAll = true;
+        ">显示分组</span>
         <span class="link">
           <el-checkbox v-model="chinese" true-label="zh-Hant" false-label="zh-Hans">繁体</el-checkbox>
         </span>
@@ -70,13 +61,10 @@
         </div>
         <!-- 教材章节树 -->
         <div class="courseware-tree">
-          <div
-            v-for="{ id: nodeId, name, status_name, deep, is_leaf_chapter } in node_list"
-            :key="nodeId"
+          <div v-for="{ id: nodeId, name, status_name, deep, is_leaf_chapter } in node_list" :key="nodeId"
             :class="['menu-item', { active: curSelectId === nodeId }, { courseware: isTrue(is_leaf_chapter) }]"
             :style="computedNameStyle(deep, isTrue(is_leaf_chapter))"
-            @click="selectChapterNode(nodeId, isTrue(is_leaf_chapter))"
-          >
+            @click="selectChapterNode(nodeId, isTrue(is_leaf_chapter))">
             <span class="name nowrap-ellipsis" :title="name">
               {{ name }}
             </span>
@@ -85,14 +73,10 @@
         </div>
       </aside>
 
-      <div
-        ref="previewMain"
-        class="main-container"
-        :style="{
-          paddingLeft: !isFullScreen && navigationShow ? '15px' : '315px',
-          paddingRight: !isFullScreen && sidebarShow ? '15px' : '315px',
-        }"
-      >
+      <div ref="previewMain" class="main-container" :style="{
+        paddingLeft: !isFullScreen && navigationShow ? '15px' : '315px',
+        paddingRight: !isFullScreen && sidebarShow ? '15px' : '315px',
+      }">
         <!-- 左侧菜单栏 - 收缩 -->
         <div v-if="!navigationShow && !isFullScreen" class="catalogue-bar" @click="toggleNavigationShow">
           <SvgIcon icon-class="catalogue" size="54" />
@@ -107,25 +91,13 @@
             @mouseup="endSelection"
             @mouseleave="endSelection"
           > -->
-          <CoursewarePreview
-            v-if="courseware_info.book_name"
-            ref="courserware"
-            :is-show-group="isShowGroup"
-            :group-show-all="groupShowAll"
-            :group-row-list="content_group_row_list"
-            :data="data"
-            :courseware-id="curSelectId"
-            :component-list="component_list"
-            :background="background"
+          <CoursewarePreview v-if="courseware_info.book_name" ref="courserware" :is-show-group="isShowGroup"
+            :group-show-all="groupShowAll" :group-row-list="content_group_row_list" :data="data"
+            :courseware-id="curSelectId" :component-list="component_list" :background="background"
             :can-remark="isTrue(courseware_info.is_my_audit_task) && isTrue(courseware_info.is_can_add_audit_remark)"
-            :show-remark="isShowAudit"
-            :component-remark-obj="remark_list_obj"
-            :project="project"
-            @computeScroll="computeScroll"
-            @addRemark="addRemark"
-            @editNote="handEditNote"
-            @saveCollect="saveCollect"
-          />
+            :show-remark="isShowAudit" :component-remark-obj="remark_list_obj" :project="project"
+            @computeScroll="computeScroll" @addRemark="addRemark" @editNote="handEditNote" @saveCollect="saveCollect"
+            @getTranslate="getTranslate" />
           <!-- <div
               v-if="isSelecting"
               :style="{
@@ -165,20 +137,13 @@
             <img :src="require('@/assets/icon/sidebar-toolkit.png')" alt="工具箱" />
           </div>
           <div v-if="sidebarShow" class="toolbar-list">
-            <div
-              v-for="{ icon, title, handle, param, children } in sidebarIconList"
-              :key="icon"
-              :class="['sidebar-item', { active: curToolbarIcon === icon }]"
-              :title="title"
-              @click="handleSidebarClick(handle, param, icon, children)"
-            >
-              <div
-                class="sidebar-icon icon-mask"
-                :style="{
-                  backgroundColor: curToolbarIcon === icon ? '#fff' : '#1E2129',
-                  maskImage: `url(${require(`@/assets/icon/sidebar-${icon}.svg`)})`,
-                }"
-              ></div>
+            <div v-for="{ icon, title, handle, param, children } in sidebarIconList" :key="icon"
+              :class="['sidebar-item', { active: curToolbarIcon === icon }]" :title="title"
+              @click="handleSidebarClick(handle, param, icon, children)">
+              <div class="sidebar-icon icon-mask" :style="{
+                backgroundColor: curToolbarIcon === icon ? '#fff' : '#1E2129',
+                maskImage: `url(${require(`@/assets/icon/sidebar-${icon}.svg`)})`,
+              }"></div>
             </div>
           </div>
           <div class="adjustable" @click="toggleSidebarShow">
@@ -217,20 +182,13 @@
           <!-- <div v-if="curToolbarIcon === 'totalResources'" class="resource_box"></div> -->
           <div v-if="['image', 'audio', 'video', 'text'].includes(twoCurToolbarIcon)" class="resource_box">
             <div class="source-toolbar-list">
-              <div
-                v-for="{ icon, title, handle, param } in twoSidebarList"
-                :key="icon"
-                :class="['sidebar-item', { active: twoCurToolbarIcon === icon }]"
-                :title="title"
-                @click="handleSidebarClick(handle, param, icon, '', 2)"
-              >
-                <div
-                  class="sidebar-icon icon-mask"
-                  :style="{
-                    backgroundColor: twoCurToolbarIcon === icon ? '#fff' : '#1E2129',
-                    maskImage: `url(${require(`@/assets/icon/sidebar-${icon}.svg`)})`,
-                  }"
-                ></div>
+              <div v-for="{ icon, title, handle, param } in twoSidebarList" :key="icon"
+                :class="['sidebar-item', { active: twoCurToolbarIcon === icon }]" :title="title"
+                @click="handleSidebarClick(handle, param, icon, '', 2)">
+                <div class="sidebar-icon icon-mask" :style="{
+                  backgroundColor: twoCurToolbarIcon === icon ? '#fff' : '#1E2129',
+                  maskImage: `url(${require(`@/assets/icon/sidebar-${icon}.svg`)})`,
+                }"></div>
               </div>
             </div>
             <div style="height: 40px"></div>
@@ -240,43 +198,27 @@
               <el-switch v-model="multimediaIsAllShow" :active-value="true" :inactive-value="false" />
             </div>
             <el-collapse v-model="activeBookChapterId" accordion @change="multimediaHandleChange">
-              <el-collapse-item
-                v-for="chapter in bookChapterList"
-                :key="chapter.id"
-                :name="chapter.id"
-                :title="chapter.name"
-              >
+              <el-collapse-item v-for="chapter in bookChapterList" :key="chapter.id" :name="chapter.id"
+                :title="chapter.name">
                 <!-- 加载状态 -->
                 <div v-if="multimediaLoadingStates" class="loading-text">加载中...</div>
 
                 <!-- 加载完成显示数据 -->
                 <div v-else-if="chapter.data">
-                  <ul
-                    :class="parseInt(drawerType) == 5 ? 'scroll-container file-list' : 'scroll-container'"
-                    infinite-scroll-disabled="disabled"
-                    :infinite-scroll-immediate="false"
-                  >
+                  <ul :class="parseInt(drawerType) == 5 ? 'scroll-container file-list' : 'scroll-container'"
+                    infinite-scroll-disabled="disabled" :infinite-scroll-immediate="false">
                     <li v-for="(item, index) in chapter.data" :key="`${chapter.id}-${index}`" class="list-item">
                       <template v-if="parseInt(drawerType) === 0">
                         <el-image v-if="shouldMediaShowItem(chapter, item)" :src="item.file_url" fit="contain" />
                       </template>
                       <template v-else-if="parseInt(drawerType) === 1">
-                        <AudioPlay
-                          v-if="shouldMediaShowItem(chapter, item)"
-                          view-size="middle"
-                          :file-id="item.file_id"
-                          :file-name="item.file_name.slice(0, item.file_name.lastIndexOf('.'))"
-                          :show-slider="true"
-                          :audio-index="index"
-                        />
+                        <AudioPlay v-if="shouldMediaShowItem(chapter, item)" view-size="middle" :file-id="item.file_id"
+                          :file-name="item.file_name.slice(0, item.file_name.lastIndexOf('.'))" :show-slider="true"
+                          :audio-index="index" />
                       </template>
                       <template v-else-if="parseInt(drawerType) === 2">
-                        <VideoPlay
-                          v-if="shouldMediaShowItem(chapter, item)"
-                          view-size="small"
-                          :file-id="item.file_id"
-                          :video-index="index"
-                        />
+                        <VideoPlay v-if="shouldMediaShowItem(chapter, item)" view-size="small" :file-id="item.file_id"
+                          :video-index="index" />
                       </template>
                       <template v-else-if="parseInt(drawerType) === 3"> </template>
                       <template v-else-if="parseInt(drawerType) === 4"> </template>
@@ -296,12 +238,8 @@
                       <div class="file-handle-info">
                         <span v-if="![1, 5].includes(parseInt(drawerType))" class="word">{{ item.file_name }}</span>
                         <div class="mark">
-                          <el-checkbox
-                            v-model="item.is_hided"
-                            :disabled="!item.is_can_edit"
-                            size="small"
-                            @change="handleMediaShowChange(item)"
-                          >
+                          <el-checkbox v-model="item.is_hided" :disabled="!item.is_can_edit" size="small"
+                            @change="handleMediaShowChange(item)">
                             隐藏
                           </el-checkbox>
                           <el-link type="primary" class="el-icon-place linkLocation" @click="handleLocation(item, 0)" />
@@ -351,13 +289,18 @@
               </li>
             </ul>
           </div>
+          <div v-if="curToolbarIcon === 'translate'" class="resource_box">
+            <h5>{{ drawerTitle }}</h5>
+            <div style="height: 40px"></div>
+            <div style="padding:10px">
+              <el-select v-model="lang" placeholder="请选择语言" size="mini" class="lang-select">
+                <el-option v-for="item in langList" :key="item.type" :label="item.name" :value="item.type" />
+              </el-select>
+            </div>
+          </div>
           <template v-if="curToolbarIcon === 'audit'">
-            <AuditRemark
-              :remark-list="remark_list"
-              :is-audit="isShowAudit"
-              @deleteRemarks="deleteRemarks"
-              @handleLocationRemarks="handleLocationRemarks"
-            />
+            <AuditRemark :remark-list="remark_list" :is-audit="isShowAudit" @deleteRemarks="deleteRemarks"
+              @handleLocationRemarks="handleLocationRemarks" />
           </template>
         </div>
 
@@ -367,14 +310,8 @@
       </aside>
     </div>
 
-    <el-dialog
-      title="添加课件审核批注"
-      :visible="visible"
-      width="680px"
-      :close-on-click-modal="false"
-      class="remark-dialog"
-      @close="dialogClose('')"
-    >
+    <el-dialog title="添加课件审核批注" :visible="visible" width="680px" :close-on-click-modal="false" class="remark-dialog"
+      @close="dialogClose('')">
       <Remark :remark="remark" :project-id="projectId" :courseware-id="select_node ? select_node : id" />
       <div slot="footer">
         <el-button @click="dialogClose('')">取消</el-button>
@@ -385,44 +322,25 @@
     </el-dialog>
 
     <el-dialog title="" :visible="visibleMindMap" width="1100px" class="audit-dialog" @close="dialogClose('MindMap')">
-      <MindMap
-        v-if="isChildDataLoad"
-        ref="mindMapRef"
-        :project-id="projectId"
-        :mind-map-json-data="mindMapJsonData"
-        @child-click="handleNodeClick"
-      />
+      <MindMap v-if="isChildDataLoad" ref="mindMapRef" :project-id="projectId" :mind-map-json-data="mindMapJsonData"
+        @child-click="handleNodeClick" />
     </el-dialog>
 
-    <el-dialog
-      title=""
-      :visible="visibleVisNetwork"
-      width="1100px"
-      class="audit-dialog"
-      :close-on-click-modal="false"
-      @close="dialogClose('VisNetwork')"
-    >
+    <el-dialog title="" :visible="visibleVisNetwork" width="1100px" class="audit-dialog" :close-on-click-modal="false"
+      @close="dialogClose('VisNetwork')">
       <VisNetwork ref="visNetworkRef" :book-id="projectId" @child-click="handleNodeClick" />
     </el-dialog>
 
-    <ExplanatoryNoteDialog
-      ref="explanatoryNote"
-      :open.sync="editDialogOpen"
-      :init-data="oldRichData"
-      title-text="笔记"
-      @confirm="saveNote"
-      @cancel="delNote"
-    />
+    <ExplanatoryNoteDialog ref="explanatoryNote" :open.sync="editDialogOpen" :init-data="oldRichData" title-text="笔记"
+      @confirm="saveNote" @cancel="delNote" />
+
+    <TranslateDialog :open.sync="showTranslate" :init-text="translateText" :book-id="projectId" title-text="翻译" />
+
     <SimAnswerPermissionControl :visible.sync="visiblePermissionControl" :permission-control.sync="permissionControl" />
     <PreviewURL :url="preview_url" :visible.sync="visiblePreviewURL" />
 
-    <el-dialog
-      title="翻译"
-      width="240px"
-      :close-on-click-modal="false"
-      :visible="visibleTranslate"
-      @close="dialogClose('Translate')"
-    >
+    <el-dialog title="翻译" width="240px" :close-on-click-modal="false" :visible="visibleTranslate"
+      @close="dialogClose('Translate')">
       <el-select v-model="lang" placeholder="请选择语言" size="mini" class="lang-select">
         <el-option v-for="item in langList" :key="item.type" :label="item.name" :value="item.type" />
       </el-select>
@@ -438,6 +356,7 @@ import VideoPlay from '@/views/book/courseware/preview/components/common/VideoPl
 import AudioPlay from '@/views/book/courseware/preview/components/common/AudioPlay.vue';
 import AuditRemark from '@/components/AuditRemark.vue';
 import ExplanatoryNoteDialog from '@/components/ExplanatoryNoteDialog.vue';
+import TranslateDialog from '@/components/TranslateDialog.vue';
 import SimAnswerPermissionControl from '@/components/SimAnswerPermissionControl.vue';
 import PreviewURL from '@/views/project_manage/common/PreviewURL.vue';
 import Remark from './Remark.vue';
@@ -486,6 +405,7 @@ export default {
     SimAnswerPermissionControl,
     PreviewURL,
     Remark,
+    TranslateDialog,
   },
   provide() {
     return {
@@ -545,7 +465,7 @@ export default {
       },
       { icon: 'collect', title: '收藏', handle: 'getCollect', param: { type: '11' } },
       { icon: 'note', title: '笔记', handle: 'getNote', param: { type: '12' } },
-      { icon: 'translate', title: '翻译', handle: 'openTranslate', param: {} },
+      { icon: 'translate', title: '翻译', handle: 'openTranslate', param: { type: '21' } },
       { icon: 'setting', title: '设置', handle: '', param: {} },
     ];
 
@@ -638,6 +558,8 @@ export default {
       },
       allNoteList: [],
       editDialogOpen: false,
+      showTranslate: false,
+      translateText: '',
       oldRichData: {},
       newSelectedInfo: null,
       allCottectList: [],
@@ -678,6 +600,7 @@ export default {
         11: '收藏列表',
         12: '笔记列表',
         13: '搜索结果',
+        21: '翻译',
       };
       return titleMap[this.drawerType] || '资源列表';
     },
@@ -736,7 +659,7 @@ export default {
     });
   },
   beforeDestroy() {
-    document.removeEventListener('fullscreenchange', () => {});
+    document.removeEventListener('fullscreenchange', () => { });
   },
   methods: {
     getProjectBaseInfo() {
@@ -956,7 +879,7 @@ export default {
             this.$message.success('删除成功!');
           });
         })
-        .catch(() => {});
+        .catch(() => { });
     },
     // 定位批注
     async handleLocationRemarks(componentId) {
@@ -1039,8 +962,9 @@ export default {
     /**
      * 打开选择语言弹窗
      */
-    openTranslate() {
-      this.visibleTranslate = true;
+    openTranslate(params) {
+      //this.visibleTranslate = true;
+      if (params && params.type) this.drawerType = Number(params.type);
     },
     /**
      * 打开抽屉并初始化加载
@@ -1379,7 +1303,7 @@ export default {
         .then(() => {
           this.delNote(id);
         })
-        .catch(() => {});
+        .catch(() => { });
     },
     delNote(id) {
       const noteId = id || (this.oldRichData && this.oldRichData.id);
@@ -1465,7 +1389,11 @@ export default {
             this.allCottectList = this.allCottectList.filter((x) => x.id !== id);
           });
         })
-        .catch(() => {});
+        .catch(() => { });
+    },
+    async getTranslate(info) {
+      this.showTranslate = true;
+      this.translateText = info.text;
     },
     getSearch(params) {
       if (params && params.type) this.drawerType = Number(params.type);
@@ -1582,7 +1510,7 @@ $total-width: $courseware-width + $courseware-left-margin + $courseware-right-ma
     border-top: $border;
     border-bottom: $border;
 
-    > .menu-container {
+    >.menu-container {
       display: flex;
       justify-content: space-between;
       width: 360px;
@@ -1590,7 +1518,7 @@ $total-width: $courseware-width + $courseware-left-margin + $courseware-right-ma
       border-right: $border;
     }
 
-    > .courseware {
+    >.courseware {
       display: flex;
       flex-grow: 1;
       column-gap: 16px;
@@ -1638,7 +1566,7 @@ $total-width: $courseware-width + $courseware-left-margin + $courseware-right-ma
         align-items: center;
 
         .link {
-          + .link {
+          +.link {
             margin-left: 0;
 
             &::before {
@@ -1819,7 +1747,9 @@ $total-width: $courseware-width + $courseware-left-margin + $courseware-right-ma
               text-overflow: ellipsis;
               word-break: break-word;
               white-space: normal;
-              -webkit-line-clamp: 2; /* 多行省略行数,按需调整 */
+              -webkit-line-clamp: 2;
+
+              /* 多行省略行数,按需调整 */
               -webkit-box-orient: vertical;
             }
           }
@@ -1845,6 +1775,7 @@ $total-width: $courseware-width + $courseware-left-margin + $courseware-right-ma
 
           &.courseware {
             &:hover {
+
               .name,
               .status {
                 background-color: #f3f3f3;

+ 203 - 0
src/components/TranslateDialog.vue

@@ -0,0 +1,203 @@
+<template>
+  <el-dialog :title="titleText" :visible.sync="visible" width="600px" :close-on-click-modal="false"
+    @close="dialogClose()">
+    <div class="translate-container">
+      <div class="translate-section">
+        <div class="section-label">原文:</div>
+        <div class="text-content original-text">{{ initText }}</div>
+      </div>
+
+      <div class="translate-section lang-selector">
+        <span class="section-label">目标语种:</span>
+        <el-select v-model="lang" placeholder="请选择语言" size="mini" class="lang-select" @change="handleLangChange">
+          <el-option v-for="item in langList" :key="item.type" :label="item.name" :value="item.type" />
+        </el-select>
+      </div>
+
+      <div class="translate-section">
+        <div class="section-label">译文:</div>
+        <div class="text-content translated-text" :style="{ lineHeight: lang === 'ZH' ? '1' : '1.5' }">{{
+          translateResultText }}</div>
+      </div>
+    </div>
+
+    <div slot="footer" class="dialog-footer">
+      <el-button @click="dialogClose">关闭</el-button>
+    </div>
+  </el-dialog>
+</template>
+
+<script>
+import { GetLanguageTypeList, Texttrans } from '@/api/book';
+
+export default {
+  name: 'TranslateDialog',
+  components: {
+  },
+  props: {
+    open: {
+      type: Boolean,
+      default: false,
+      required: true,
+    },
+    initText: {
+      type: String,
+      default: '',
+    },
+    titleText: {
+      type: String,
+      default: '翻译',
+    },
+    bookId: {
+      type: String,
+      default: '',
+    },
+  },
+  data() {
+    return {
+      visible: false,
+      lang: 'ZH',
+      langList: [],
+      translateResultList: [],
+    };
+  },
+  computed: {
+    // 计算最终显示的译文字符串
+    translateResultText() {
+      if (!this.translateResultList || this.translateResultList.length === 0) {
+        return '';
+      }
+      let resultText = this.translateResultList.map(item => item.dst);
+      return resultText.join('\n');
+    },
+  },
+  watch: {
+    open(newVal) {
+      this.visible = newVal;
+      if (newVal) {
+        this.getLangList();
+        this.translateResultList = [];
+      }
+    },
+    visible(newVal) {
+      if (!newVal) {
+        this.$emit('update:open', false);
+        this.translateResultList = [];
+        this.lang = 'ZH';
+      }
+    },
+  },
+  methods: {
+    dialogClose() {
+      this.visible = false;
+    },
+
+    /**
+     * 获取语言列表
+     */
+    getLangList() {
+      if (!this.bookId) return;
+      GetLanguageTypeList({ book_id: this.bookId, is_contain_zh: 'true' }).then(
+        ({ language_type_list }) => {
+          this.langList = language_type_list;
+        },
+      );
+    },
+
+    /**
+     * 语种改变时触发
+     */
+    handleLangChange(val) {
+      if (!this.initText) {
+        this.$message.warning('暂无可翻译内容');
+        return;
+      }
+      this.fetchTranslation();
+    },
+
+    /**
+     * 调用翻译接口
+     */
+    fetchTranslation() {
+      this.translateResultList = [];
+      const params = {
+        text: this.initText,
+        from: 'zh',
+        to: this.lang
+      }
+      Texttrans(params).then(res => {
+        if (res.status == 1) {
+          this.translateResultList = res.trans_list || [];
+        }
+      }).catch(err => {
+        this.$message.error('翻译失败');
+      });
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+.translate-container {
+  padding: 10px;
+
+  .translate-section {
+    margin-bottom: 15px;
+
+    .section-label {
+      display: block;
+      margin-bottom: 8px;
+      font-size: 14px;
+      font-weight: bold;
+      color: #606266;
+    }
+
+    // 核心样式:保留换行符,自动换行
+    .text-content {
+      min-height: 40px;
+      max-height: 300px;
+      padding: 10px;
+      overflow-y: auto;
+      font-size: 14px;
+      line-height: 1;
+      color: #303133;
+      word-break: break-all;
+      white-space: pre-wrap;
+
+      /* 关键:保留空格和换行符 */
+      background-color: #f5f7fa;
+      border: 1px solid #e4e7ed;
+      border-radius: 4px;
+    }
+
+    .translated-text {
+      line-height: 1.5;
+    }
+
+    &.lang-selector {
+      display: flex;
+      align-items: center;
+
+      .section-label {
+        margin-right: 10px;
+        margin-bottom: 0;
+      }
+    }
+  }
+}
+
+.el-dialog {
+  :deep &__body {
+    display: flex;
+    flex-direction: column;
+    row-gap: 8px;
+    padding: 10pX;
+
+    .el-textarea__inner {
+      height: 108px;
+    }
+  }
+
+
+}
+</style>

+ 13 - 0
src/views/book/courseware/preview/CoursewarePreview.vue

@@ -80,6 +80,8 @@
         <span class="button" @click="setNote"><SvgIcon icon-class="sidebar-text" size="14" /> 笔记</span>
         <span class="line"></span>
         <span class="button" @click="setCollect"><SvgIcon icon-class="sidebar-collect" size="14" /> 收藏 </span>
+        <span class="line"></span>
+        <span class="button" @click="setTranslate"><img style="width: 14px; height: 14px;" :src="require('@/assets/icon/sidebar-translate.png')" /> 翻译 </span>
       </template>
     </div>
   </div>
@@ -714,6 +716,17 @@ export default {
       this.$emit('saveCollect', info);
       this.selectedInfo = null;
     },
+// 翻译
+    setTranslate() {
+      this.showToolbar = false;
+
+      let info = this.selectHandleInfo;
+      if (!info) return;
+      info.coursewareId = this.courseware_id;
+
+      this.$emit('getTranslate', info);
+      this.selectedInfo = null;
+    },
     // 定位
     handleLocation(item) {
       this.scrollToDataId(item.blockId);