Просмотр исходного кода

新增用户反馈,修正拼音效果注释弹窗输入问题,笔记可以加图片

zq 2 дней назад
Родитель
Сommit
b8d4577ac3

+ 22 - 0
src/api/book.js

@@ -540,3 +540,25 @@ export function SaveCoursewareComponentBackground(data) {
     data,
   );
 }
+
+/**
+ *@description 添加课件反馈
+ * @param {object} data
+ */
+export function AddCoursewareFeedback(data) {
+  return http.post(`${process.env.VUE_APP_EepServer}?MethodName=book_preview_manager-AddCoursewareFeedback`, data);
+}
+/**
+ * @description 得到课件反馈列表
+ * @param {object} data
+ */
+export function GetCoursewareFeedbackList(data) {
+  return http.post(`${process.env.VUE_APP_EepServer}?MethodName=book_preview_manager-GetCoursewareFeedbackList`, data);
+}
+/**
+ * @description 删除课件反馈
+ * @param {object} data
+ */
+export function DeleteCoursewareFeedback(data) {
+  return http.post(`${process.env.VUE_APP_EepServer}?MethodName=book_preview_manager-DeleteCoursewareFeedback`, data);
+}

+ 1 - 0
src/assets/icon/sidebar-feedback.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1776823029085" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3716" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16"><path d="M512 616m-40 0a40 40 0 1 0 80 0 40 40 0 1 0-80 0Z" fill="#1E2129" p-id="3717"></path><path d="M890 128H744a40 40 0 0 0-40 40 40 40 0 0 0 40 40h96a40 40 0 0 1 40 40v416a40 40 0 0 1-40 40H648.88a70 70 0 0 0-49.5 20.5l-59.5 59.5L512 811.88 484.12 784l-40-40-19.5-19.5a70 70 0 0 0-49.5-20.5H184a40 40 0 0 1-40-40V248a40 40 0 0 1 40-40h96a40 40 0 0 0 40-40 40 40 0 0 0-40-40H134a70 70 0 0 0-70 70v516a70 70 0 0 0 70 70h237l112.72 112.74a40 40 0 0 0 56.56 0L653 784h237a70 70 0 0 0 70-70V198a70 70 0 0 0-70-70z" fill="#1E2129" p-id="3718"></path><path d="M512 512a40 40 0 0 0 40-40V168a40 40 0 0 0-80 0v304a40 40 0 0 0 40 40z" fill="#1E2129" p-id="3719"></path></svg>

+ 147 - 9
src/components/CommonPreview.vue

@@ -140,6 +140,7 @@
             @editNote="handEditNote"
             @saveCollect="saveCollect"
             @getTranslate="getTranslate"
+            @editFeedback="handEditFeedback"
             @selectedComponent="$emit('selectedComponent', $event)"
           />
           <!-- <div
@@ -379,6 +380,26 @@
             </div>
           </div>
 
+          <div v-if="curToolbarIcon === 'feedback'" class="resource_box">
+            <h5>{{ drawerTitle }}</h5>
+            <div style="height: 40px"></div>
+            <ul v-if="allFeedbackList.length > 0" class="card-box">
+              <li v-for="item in allFeedbackList" :key="item.id">
+                <span class="el-icon-notebook-2"> 原文</span>
+                <span>{{ item.text }}</span>
+                <el-divider class="mt10" />
+                <span v-html="item.feedBack"></span>
+                <div>
+                  <el-button type="text" class="el-icon-edit" @click="handEditFeedback(item)"> 编辑</el-button>
+                  <el-divider direction="vertical" />
+                  <el-button type="text" class="el-icon-delete" @click="handDelFeedback(item.id)"> 删除</el-button>
+                  <el-divider direction="vertical" />
+                  <el-button type="text" class="el-icon-place" @click="handleLocation(item, 13)"> 定位</el-button>
+                </div>
+              </li>
+            </ul>
+          </div>
+
           <template v-if="curToolbarIcon === 'audit'">
             <AuditRemark
               :remark-list="remark_list"
@@ -410,7 +431,7 @@
 
     <el-dialog
       title="添加课件审核批注"
-      :visible="visible"
+      :visible="visibleRemark"
       width="680px"
       :close-on-click-modal="false"
       class="remark-dialog"
@@ -450,9 +471,9 @@
       ref="explanatoryNote"
       :open.sync="editDialogOpen"
       :init-data="oldRichData"
-      title-text="笔记"
-      @confirm="saveNote"
-      @cancel="delNote"
+      :title-text="dialogTitleText"
+      @confirm="dialogSave"
+      @cancel="dialogCancel"
     />
 
     <TranslateDialog :open.sync="showTranslate" :init-text="translateText" :book-id="projectId" title-text="翻译" />
@@ -501,6 +522,9 @@ import {
   SearchBookContentText,
   SetBookResourceHide,
   SubmitChapterAllCoursewareToAuditFlow,
+  AddCoursewareFeedback,
+  GetCoursewareFeedbackList,
+  DeleteCoursewareFeedback,
 } from '@/api/book';
 import { toggleFullScreen } from '@/utils/common';
 import * as OpenCC from 'opencc-js';
@@ -582,6 +606,7 @@ export default {
       { icon: 'note', title: '笔记', handle: 'getNote', param: { type: '12' } },
       { icon: 'translate', title: '多语言', handle: 'openTranslate', param: { type: '21' } },
       { icon: 'setting', title: '设置', handle: 'openSetting', param: { type: 6 } },
+      { icon: 'feedback', title: '用户反馈', handle: 'getFeedback', param: { type: 22 } },
     ];
 
     if (this.isShowAudit) {
@@ -619,7 +644,7 @@ export default {
       remark_list_obj: {}, // 存放以组件为对象的批注数组
       searchList: [],
       searchContent: '',
-      visible: false,
+      visibleRemark: false,
       remark: {
         remark_content: '',
         file_id_list: [],
@@ -656,6 +681,7 @@ export default {
         12: '笔记列表',
         13: '搜索结果',
         21: '多语言',
+        22: '用户反馈',
       },
       page_capacity: 10,
       cur_page: 1,
@@ -686,9 +712,11 @@ export default {
       showTranslate: false,
       translateText: '',
       oldRichData: {},
+      dialogType: 1,
       newSelectedInfo: null,
       allCottectList: [],
       bookChapterList: [],
+      allFeedbackList: [],
       book_id: '',
       activeBookChapterId: '',
       multimediaLoadingStates: true,
@@ -724,6 +752,10 @@ export default {
         return this.activeBookChapterId === chapter.id && item && item.file_id;
       };
     },
+    dialogTitleText() {
+      if (this.dialogType === 2) return '反馈';
+      return '笔记';
+    },
   },
   watch: {
     isJudgeCorrect(newVal) {
@@ -740,6 +772,8 @@ export default {
         this.getNote();
       } else if (this.curToolbarIcon === 'collect') {
         this.getCollect();
+      } else if (this.curToolbarIcon === 'feedback') {
+        this.getFeedback();
       }
     },
     multimediaIsAllShow() {
@@ -940,7 +974,7 @@ export default {
         remark_content: '',
         file_id_list: [],
       };
-      this.visible = true;
+      this.visibleRemark = true;
       if (selectNode) {
         this.menuPosition = {
           x,
@@ -974,7 +1008,7 @@ export default {
       })
         .then(() => {
           this.submit_loading = false;
-          this.visible = false;
+          this.visibleRemark = false;
           this.getCoursewareAuditRemarkList(id || this.id);
         })
         .catch(() => {
@@ -1341,7 +1375,7 @@ export default {
     /**
      * 定位到对应位置
      * @param {Object} item - 位置对象
-     * @param {number} type - 定位类型(11: 笔记定位, 12: 收藏定位, 0: 资源定位)
+     * @param {number} type - 定位类型(11: 笔记定位, 12: 收藏定位, 13:反馈定位 0: 资源定位)
      */
     handleLocation(item, type) {
       if (type === 0) {
@@ -1363,6 +1397,14 @@ export default {
       }
     },
 
+    dialogSave(obj) {
+      if (this.dialogType === 1) return this.saveNote(obj);
+      if (this.dialogType === 2) return this.saveFeedback(obj);
+    },
+    dialogCancel(id) {
+      if (this.dialogType === 1) return this.delNote(id);
+      if (this.dialogType === 2) return this.delFeedback(id);
+    },
     /**
      * 获取笔记列表
      * @param {Object} params - 参数对象
@@ -1404,16 +1446,18 @@ export default {
       );
       if (old) {
         this.oldRichData = old;
+        this.oldRichData.dataStr = old.note;
       }
       this.newSelectedInfo = note;
       this.editDialogOpen = true;
+      this.dialogType = 1;
     },
     saveNote(note) {
       let noteInfo = {
         blockId: this.newSelectedInfo.blockId,
         startIndex: this.newSelectedInfo.startIndex,
         endIndex: this.newSelectedInfo.endIndex,
-        note: note.note,
+        note: note.dataStr,
         text: this.newSelectedInfo.text,
       };
 
@@ -1544,6 +1588,100 @@ export default {
       this.showTranslate = true;
       this.translateText = info.text;
     },
+
+    async getFeedback(params) {
+      if (params && params.type) this.drawerType = Number(params.type);
+      this.allFeedbackList = [];
+      await GetCoursewareFeedbackList({ courseware_id: this.curSelectId }).then((res) => {
+        if (res.status === 1 && res.feedback_list) {
+          res.feedback_list.forEach((x) => {
+            if (x.content) {
+              let n = JSON.parse(x.content);
+              let obj = {
+                coursewareId: x.courseware_id,
+                id: x.id,
+                blockId: n.blockId,
+                startIndex: n.startIndex,
+                endIndex: n.endIndex,
+                text: n.text,
+                feedBack: n.feedBack,
+              };
+              this.allFeedbackList.push(obj);
+            }
+          });
+        }
+      });
+    },
+    async handEditFeedback(feedBack) {
+      this.oldRichData = {};
+      if (this.allFeedbackList.length === 0) {
+        await this.getFeedback();
+      }
+      let old = this.allFeedbackList.find(
+        (x) =>
+          x.coursewareId === feedBack.coursewareId &&
+          x.blockId === feedBack.blockId &&
+          x.startIndex === feedBack.startIndex &&
+          x.endIndex === feedBack.endIndex,
+      );
+      if (old) {
+        this.oldRichData = old;
+        this.oldRichData.dataStr = old.feedBack;
+      }
+      this.newSelectedInfo = feedBack;
+      this.editDialogOpen = true;
+      this.dialogType = 2;
+    },
+    saveFeedback(feedBack) {
+      if (feedBack.id) {
+        this.delFeedback(feedBack.id);
+      }
+
+      if (feedBack.dataStr) {
+        let feedBackInfo = {
+          blockId: this.newSelectedInfo.blockId,
+          startIndex: this.newSelectedInfo.startIndex,
+          endIndex: this.newSelectedInfo.endIndex,
+          feedBack: feedBack.dataStr,
+          text: this.newSelectedInfo.text,
+        };
+
+        let reqData = {
+          courseware_id: this.newSelectedInfo.coursewareId, // 课件 ID
+          component_id: 'WHOLE',
+          content: JSON.stringify(feedBackInfo),
+          content_select: this.newSelectedInfo.text,
+        };
+
+        AddCoursewareFeedback(reqData).then(() => {
+          this.getFeedback();
+        });
+      }
+      this.editDialogOpen = false;
+      this.newSelectedInfo = null;
+      this.selectedInfo = null;
+    },
+    handDelFeedback(id) {
+      this.$confirm('确定要删除此条反馈吗?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning',
+      })
+        .then(() => {
+          this.delFeedback(id);
+        })
+        .catch(() => {});
+    },
+    delFeedback(id) {
+      const feedBackId = id || (this.oldRichData && this.oldRichData.id);
+      if (!feedBackId) return;
+      DeleteCoursewareFeedback({ id: feedBackId })
+        .then(() => {
+          this.allFeedbackList = this.allFeedbackList.filter((x) => x.id !== feedBackId);
+        })
+        .catch((err) => {});
+    },
+
     getSearch(params) {
       if (params && params.type) this.drawerType = Number(params.type);
     },

+ 2 - 2
src/components/ExplanatoryNoteDialog.vue

@@ -7,8 +7,8 @@
     @close="dialogClose()"
   >
     <RichText
-      v-model="richData.note"
-      toolbar="fontselect fontsizeselect forecolor backcolor | underline | bold italic strikethrough alignleft aligncenter alignright"
+      v-model="richData.dataStr"
+      toolbar="fontselect fontsizeselect forecolor backcolor | underline | bold italic strikethrough alignleft aligncenter alignright image"
       :wordlimit-num="false"
       :height="240"
       placeholder="输入内容"

+ 111 - 0
src/components/FeedbackDialog.vue

@@ -0,0 +1,111 @@
+<template>
+  <el-dialog
+    :title="'编辑' + titleText"
+    :visible.sync="visible"
+    width="680px"
+    :close-on-click-modal="false"
+    @close="dialogClose()"
+  >
+    <RichText
+      v-model="richData.note"
+      toolbar="fontselect fontsizeselect forecolor backcolor | underline | bold italic strikethrough alignleft aligncenter alignright image"
+      :wordlimit-num="false"
+      :height="240"
+      placeholder="输入内容"
+      page-from="audit"
+    />
+    <template slot="footer">
+      <el-button size="medium" @click="deleteNote">删除</el-button>
+      <el-button type="primary" size="medium" @click="confirm">确定</el-button>
+    </template>
+  </el-dialog>
+</template>
+
+<script>
+import RichText from './RichText';
+
+export default {
+  name: 'FeedbackDialog',
+  components: {
+    RichText,
+  },
+  props: {
+    open: {
+      type: Boolean,
+      default: false,
+      required: true,
+    },
+    initData: {
+      type: Object,
+      default: () => ({}),
+    },
+    titleText: {
+      type: String,
+      default: '注释',
+    },
+  },
+  data() {
+    return {
+      visible: false,
+      richData: this.initData,
+    };
+  },
+  watch: {
+    open(newVal) {
+      this.visible = newVal;
+      this.richData = this.initData;
+    },
+    visible(newVal) {
+      if (!newVal) {
+        this.$emit('update:open', false);
+        this.richData = {};
+      }
+    },
+  },
+  methods: {
+    // 关闭弹窗,调用富文本组件中的
+    dialogClose() {
+      this.visible = false;
+    },
+    deleteNote() {
+      this.$confirm(`确定要删除此条${this.titleText}吗?`, '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning',
+      })
+        .then(() => {
+          this.visible = false;
+          this.$emit('cancel');
+        })
+        .catch(() => {});
+    },
+    confirm() {
+      this.$emit('confirm', this.richData);
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+.el-dialog {
+  :deep &__body {
+    display: flex;
+    flex-direction: column;
+    row-gap: 8px;
+    padding: 8px 8px 0;
+
+    .el-textarea__inner {
+      height: 108px;
+    }
+  }
+
+  :deep &__footer {
+    display: flex;
+    padding: 8px;
+
+    .el-button {
+      flex: 1;
+    }
+  }
+}
+</style>

+ 1 - 0
src/icons/svg/sibebar/sidebar-feedback.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1776823029085" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3716" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16"><path d="M512 616m-40 0a40 40 0 1 0 80 0 40 40 0 1 0-80 0Z" fill="#1E2129" p-id="3717"></path><path d="M890 128H744a40 40 0 0 0-40 40 40 40 0 0 0 40 40h96a40 40 0 0 1 40 40v416a40 40 0 0 1-40 40H648.88a70 70 0 0 0-49.5 20.5l-59.5 59.5L512 811.88 484.12 784l-40-40-19.5-19.5a70 70 0 0 0-49.5-20.5H184a40 40 0 0 1-40-40V248a40 40 0 0 1 40-40h96a40 40 0 0 0 40-40 40 40 0 0 0-40-40H134a70 70 0 0 0-70 70v516a70 70 0 0 0 70 70h237l112.72 112.74a40 40 0 0 0 56.56 0L653 784h237a70 70 0 0 0 70-70V198a70 70 0 0 0-70-70z" fill="#1E2129" p-id="3718"></path><path d="M512 512a40 40 0 0 0 40-40V168a40 40 0 0 0-80 0v304a40 40 0 0 0 40 40z" fill="#1E2129" p-id="3719"></path></svg>

+ 6 - 0
src/icons/svg/sibebar/sidebar-translate.svg

@@ -0,0 +1,6 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 24" width="24" height="24" style="" filter="none">
+    
+    <g>
+    <path d="M20 5h-9.12L10 2H4c-1.1 0-2 .9-2 2v13c0 1.1.9 2 2 2h7l1 3h8c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zM7.17 14.59c-2.25 0-4.09-1.83-4.09-4.09s1.83-4.09 4.09-4.09c1.04 0 1.99.37 2.74 1.07l.07.06-1.23 1.18-.06-.05c-.29-.27-.78-.59-1.52-.59-1.31 0-2.38 1.09-2.38 2.42s1.07 2.42 2.38 2.42c1.37 0 1.96-.87 2.12-1.46H7.08V9.91h3.95l.01.07c.04.21.05.4.05.61 0 2.35-1.61 4-3.92 4zm6.03-1.71c.33.6.74 1.18 1.19 1.7l-.54.53-.65-2.23zm.77-.76h-.99l-.31-1.04h3.99s-.34 1.31-1.56 2.74a9.18 9.18 0 0 1-1.13-1.7zM21 20c0 .55-.45 1-1 1h-7l2-2-.81-2.77.92-.92L17.79 18l.73-.73-2.71-2.68c.9-1.03 1.6-2.25 1.92-3.51H19v-1.04h-3.64V9h-1.04v1.04h-1.96L11.18 6H20c.55 0 1 .45 1 1v13z" fill="rgba(51,51,51,1)"></path>
+    </g>
+  </svg>

+ 1 - 1
src/views/book/courseware/create/components/base/common/CorrectPinyin.vue

@@ -76,7 +76,7 @@
     >
 
     <RichText
-      v-if="componentType === 'richtext'"
+      v-if="componentType === 'richtext' && visible"
       v-model="dataContent.note"
       toolbar="fontselect fontsizeselect forecolor backcolor|underline|bold italic strikethrough alignleft aligncenter alignright"
       :wordlimit-num="false"

+ 2 - 1
src/views/book/courseware/create/components/base/rich_text/RichText.vue

@@ -359,6 +359,7 @@ export default {
       if (newVersion) {
         if (pinyin) this.data.rich_text_list[j].word_list[k].pinyin = pinyin;
         this.data.rich_text_list[j].word_list[k].showPinyin = showPinyin;
+        if (note !== null) this.data.rich_text_list[j].word_list[k].note = note;
       } else {
         // 兼容历史数据
         this.data.paragraph_list_parameter.pinyin_proofread_word_list.push({
@@ -371,7 +372,7 @@ export default {
         });
         if (pinyin) this.data.paragraph_list[i][j][k].pinyin = pinyin;
         if (activeTextStyle) this.data.paragraph_list[i][j][k].activeTextStyle = activeTextStyle;
-        if (note) this.data.paragraph_list[i][j][k].note = note;
+        if (note !== null) this.data.paragraph_list[i][j][k].note = note;
         this.data.paragraph_list[i][j][k].showPinyin = showPinyin;
       }
     },

+ 14 - 3
src/views/book/courseware/preview/CoursewarePreview.vue

@@ -83,9 +83,9 @@
         <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>
+        <span class="button" @click="setTranslate"> <SvgIcon icon-class="sidebar-translate" size="14" /> 翻译</span>
+        <span class="line"></span>
+        <span class="button" @click="setFeedback"> <SvgIcon icon-class="sidebar-feedback" size="14" /> 用户反馈</span>
       </template>
     </div>
   </div>
@@ -729,6 +729,7 @@ export default {
     },
     // 笔记
     setNote() {
+      debugger;
       this.showToolbar = false;
       this.oldRichData = {};
       let info = this.selectHandleInfo;
@@ -759,6 +760,16 @@ export default {
       this.$emit('getTranslate', info);
       this.selectedInfo = null;
     },
+    // 反馈
+    setFeedback() {
+      this.showToolbar = false;
+      this.oldRichData = {};
+      let info = this.selectHandleInfo;
+      if (!info) return;
+      info.coursewareId = this.courseware_id;
+      this.$emit('editFeedback', info);
+      this.selectedInfo = null;
+    },
     // 定位
     handleLocation(item) {
       this.scrollToDataId(item.blockId);