Przeglądaj źródła

课文组件新增备注信息

liuhaidi123 3 tygodni temu
rodzic
commit
5bcb3c3313

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

@@ -141,6 +141,13 @@ export default {
           isTitle: false,
           sentencesEn: [],
           paraAlign: 'left',
+          remark: {
+            chs: '',
+            en: '',
+            heightNumber: null,
+            img_list: [],
+            widthNumber: null,
+          },
         };
         // 分段
         contentArr.forEach((item, index) => {
@@ -316,6 +323,13 @@ export default {
             sentencesEn: [],
             sentenceStr,
             paraAlign: 'left',
+            remark: {
+              chs: '',
+              en: '',
+              heightNumber: null,
+              img_list: [],
+              widthNumber: null,
+            },
           };
           this.data.detail.push(obj);
         }

+ 135 - 0
src/views/book/courseware/create/components/question/article/CheckArticle.vue

@@ -19,6 +19,7 @@
             <el-radio label="right">右对齐</el-radio>
           </el-radio-group>
         </div>
+        <div class="remark-box" @click="showRemark(index)"><SvgIcon icon-class="edit-line" />添加备注</div>
         <div v-for="(items, indexs) in item.sentenceStr" :key="indexs + 'words'" class="sentence-box">
           <div class="sentence">
             <b>{{ indexs + 1 }}.</b>
@@ -66,6 +67,88 @@
     >
       <CheckStyle :data="data" @saveStyle="saveStyle" />
     </el-dialog>
+    <el-dialog title="标注" :visible.sync="remarkVisible" width="50%" :close-on-click-modal="false" append-to-body>
+      <div v-if="remark" class="remark">
+        <div class="adult-book-input-item">
+          <span class="adult-book-lable">中文:</span>
+          <!-- <el-input
+              v-model="remark.chs"
+              class="adult-book-input"
+              type="textarea"
+              :autosize="{ minRows: 2 }"
+              placeholder="请输入中文"
+              maxlength="200"
+              show-word-limit
+              @blur="onBlur(remark, 'chs')"
+            /> -->
+          <RichText
+            ref="richText"
+            v-model="remark.chs"
+            toolbar="fontselect fontsizeselect forecolor backcolor | underline | bold italic strikethrough alignleft aligncenter alignright"
+            :wordlimit-num="200"
+            :font-size="data?.unified_attrib?.font_size"
+            :font-family="data?.unified_attrib?.font"
+          />
+        </div>
+        <div class="adult-book-input-item">
+          <span class="adult-book-lable">英文:</span>
+          <!-- <el-input
+              class="adult-book-input"
+              type="textarea"
+              :autosize="{ minRows: 2 }"
+              placeholder="请输入英文"
+              v-model="remark.en"
+              @blur="onBlur(remark, 'en')"
+              maxlength="200"
+              show-word-limit
+            ></el-input> -->
+          <RichText
+            ref="richText"
+            v-model="remark.en"
+            toolbar="fontselect fontsizeselect forecolor backcolor | underline | bold italic strikethrough alignleft aligncenter alignright"
+            :wordlimit-num="200"
+            :font-size="data?.unified_attrib?.font_size"
+            :font-family="data?.unified_attrib?.font"
+          />
+        </div>
+        <div v-if="remark.img_list && remark.img_list.length == 0" class="adult-book-input-item">
+          <el-upload action="no" accept="image/*" :show-file-list="false" :http-request="changeImage">
+            <el-button>上传图片</el-button>
+          </el-upload>
+        </div>
+        <ul v-if="remark.img_list && remark.img_list.length > 0" class="uploadArt_list">
+          <li v-for="(artItem, artIndex) in remark.img_list" :key="'articleImgList' + artIndex">
+            <img :src="artItem.url" style="width: 26px; margin-right: 5px" />
+            <span class="art_name">{{ artItem.name }}</span>
+            <SvgIcon icon-class="delete-black" size="14" @click="delImage(artIndex)" />
+          </li>
+        </ul>
+        <div class="adult-book-input-item">
+          <span class="adult-book-lable">图片宽度:</span>
+          <el-input
+            v-model="remark.widthNumber"
+            class="adult-book-input"
+            placeholder="请输入宽度值"
+            maxlength="200"
+            style="width: 150px"
+            @blur="onBlur(remark, 'widthNumber')"
+          />
+          <span class="adult-book-lable">图片高度:</span>
+          <el-input
+            v-model="remark.heightNumber"
+            class="adult-book-input"
+            placeholder="请输入高度值"
+            maxlength="200"
+            style="width: 150px"
+            @blur="onBlur(remark, 'heightNumber')"
+          />
+        </div>
+      </div>
+      <span slot="footer" class="dialog-footer">
+        <el-button @click="remarkVisible = false">取 消</el-button>
+        <el-button type="primary" @click="saveRemark">保 存</el-button>
+      </span>
+    </el-dialog>
   </div>
 </template>
 
@@ -73,11 +156,15 @@
 import CheckWord from './CheckWord.vue';
 import CheckPinyin from './CheckPinyin.vue';
 import CheckStyle from './CheckStyle.vue';
+import RichText from '@/components/RichText.vue';
+import { fileUpload } from '@/api/app';
+
 export default {
   components: {
     CheckWord,
     CheckPinyin,
     CheckStyle,
+    RichText,
   },
   props: ['data'],
   data() {
@@ -86,6 +173,8 @@ export default {
       showWordFlag: false,
       showPinyinFlag: false,
       showStyleFlag: false,
+      remarkVisible: false,
+      remark: null,
     };
   },
   // 生命周期 - 创建完成(可以访问当前this实例)
@@ -120,6 +209,34 @@ export default {
     saveStyle(paraIndex, sentenceIndex, wordIndex, style) {
       this.$emit('saveStyle', paraIndex, sentenceIndex, wordIndex, style);
     },
+    changeImage(file) {
+      fileUpload('Mid', file, { isGlobalprogress: true }).then(({ file_info_list }) => {
+        if (file_info_list.length > 0) {
+          const { file_id, file_url_open, file_name } = file_info_list[0];
+          this.remark.img_list.push({
+            name: file_name,
+            url: file_url_open,
+            id: file_id,
+            imgNumber: 0,
+          });
+        }
+      });
+    },
+    delImage(index) {
+      this.remark.img_list.splice(index, 1);
+    },
+    showRemark(index) {
+      this.remark = this.data.detail[index].remark;
+      this.paraIndex = index;
+      this.remarkVisible = true;
+    },
+    saveRemark() {
+      this.data.detail[this.paraIndex].remark = JSON.parse(JSON.stringify(this.remark));
+      this.remarkVisible = false;
+    },
+    onBlur(item, field) {
+      item[field] = item[field] ? item[field].trim() : '';
+    },
   },
 };
 </script>
@@ -243,4 +360,22 @@ export default {
     text-align: left;
   }
 }
+
+.remark-box {
+  display: flex;
+  gap: 8px;
+  align-items: center;
+  justify-content: center;
+  width: fit-content;
+  height: 24px;
+  padding: 2px 12px;
+  margin: 16px 0 0;
+  font-size: 12px;
+  font-weight: 400;
+  line-height: 20px; /* 166.667% */
+  color: #165dff;
+  cursor: pointer;
+  border: 1px solid #165dff;
+  border-radius: 2px;
+}
 </style>

+ 95 - 3
src/views/book/courseware/preview/components/article/NormalModelChs.vue

@@ -49,6 +49,10 @@
     <template v-if="!config.isHasEN || (config.isHasEN && !config.isShowEN)">
       <template v-if="resArr.length > 0">
         <div class="NPC-sentences-list">
+          <div class="NPC-article-empty">
+            <div :class="['empty-left', isHasRemark ? 'hasRemark' : '']"></div>
+            <div class="empty-right"></div>
+          </div>
           <div
             v-for="(item, index) in resArr"
             :key="'detail' + index"
@@ -60,6 +64,8 @@
                 curQue.detail[index].paragraphAttr
                   ? 'wordsList-box-' + curQue.detail[index].paragraphAttr.paragraphAlign
                   : '',
+                'article-content',
+                isHasRemark ? 'hasRemark' : '',
               ]"
             >
               <img v-if="articleImg[0] && index == 0" :src="articleImg[index]" />
@@ -607,13 +613,16 @@
               </div>
               <img v-if="articleImg[index + 1]" :src="articleImg[index + 1]" />
             </div>
+            <div v-if="item.remarkDetail" :class="['remarkBox', 'remark-top-8']">
+              <RemarkChs :remark-detail="item.remarkDetail" :margin-top="8" />
+            </div>
           </div>
         </div>
       </template>
     </template>
     <template v-else>
       <template v-if="resObj">
-        <!--  -->
+        <!-- 段落对齐方式和备注在此模式里没有写,如果段落里添加了英文后需要在此添加段落对齐和备注 -->
         <div class="NPC-sentences-list">
           <div
             v-for="(item, index) in resObj.sentList"
@@ -1191,11 +1200,13 @@
 <script>
 import AudioLine from '../voice_matrix/components/AudioLine.vue';
 import Notecard from './components/Notecard.vue';
+import RemarkChs from '../dialogue_article/RemarkChs.vue';
 export default {
   name: 'NormalModelChs',
   components: {
     AudioLine,
     Notecard,
+    RemarkChs,
   },
   props: [
     'curQue',
@@ -1233,6 +1244,7 @@ export default {
       clientY: 0,
       contentWidth: 732,
       windowWidth: window.innerWidth,
+      isHasRemark: false,
     };
   },
   computed: {
@@ -1335,6 +1347,13 @@ export default {
       let dhaspinyin = false; // 每段是否有拼音
       curQue.detail.forEach((dItem, dIndex) => {
         dhaspinyin = false;
+        let remarkDetail = dItem.remark;
+        if (
+          remarkDetail &&
+          (remarkDetail.chs || remarkDetail.en || (remarkDetail.img_list && remarkDetail.img_list.length > 0))
+        ) {
+          this.isHasRemark = true;
+        }
         let paraArr = [];
         if (!dItem.isTitle) {
           paraArr = [
@@ -1404,6 +1423,7 @@ export default {
           dhaspinyin,
           enwords: dItem.sentencesEn ? dItem.sentencesEn : [],
           paraAlign: dItem.paraAlign,
+          remarkDetail,
         };
         resArr.push(paraObj);
       });
@@ -1619,12 +1639,46 @@ export default {
   }
 
   .NPC-sentences-list {
-    padding: 24px 0;
+    // padding: 24px 0;
     color: rgba(0, 0, 0, 85%);
+
+    .NPC-article-empty {
+      display: flex;
+      align-items: flex-start;
+      justify-content: flex-start;
+
+      > div {
+        height: 24px;
+
+        &.empty-left {
+          box-sizing: border-box;
+          width: 100%;
+
+          &.hasRemark {
+            box-sizing: border-box;
+            width: 60%;
+            border-right: 1px rgba(0, 0, 0, 10%) solid;
+          }
+        }
+
+        &.empty-right {
+          flex: 1;
+        }
+      }
+
+      &-bottom {
+        > div {
+          height: 40px;
+        }
+      }
+    }
   }
 
   .NNPE-detail {
-    overflow: hidden;
+    // overflow: hidden; // 为了不遮挡备注内容
+    display: flex;
+    align-items: flex-start;
+    justify-content: flex-start;
     clear: both;
 
     .para-center {
@@ -1828,6 +1882,44 @@ export default {
         margin: 8px auto;
       }
     }
+
+    .article-content {
+      box-sizing: border-box;
+      display: flex;
+      align-items: flex-start;
+      justify-content: flex-start;
+      width: 100%;
+
+      &.hasRemark {
+        width: 60%;
+        padding: 8px 0 8px 23px;
+        border-right: 1px rgba(0, 0, 0, 10%) solid;
+      }
+
+      &.paraLast {
+        padding-bottom: 24px;
+      }
+    }
+  }
+
+  .remarkBox {
+    position: relative;
+    display: flex;
+    flex: 1;
+    align-items: center;
+    justify-content: center;
+
+    &.remark72 {
+      padding-top: 72px;
+    }
+
+    &.remark-top {
+      padding-top: 44px;
+    }
+
+    &.remark-top-8 {
+      padding-top: 8px;
+    }
   }
 
   .NNPE-noteDetail {

+ 98 - 3
src/views/book/courseware/preview/components/article/PhraseModelChs.vue

@@ -51,6 +51,10 @@
     <template v-if="!config.isHasEN || (config.isHasEN && !config.isShowEN)">
       <template v-if="resArr.length > 0">
         <div class="NPC-sentences-list">
+          <div class="NPC-article-empty">
+            <div :class="['empty-left', isHasRemark ? 'hasRemark' : '']"></div>
+            <div class="empty-right"></div>
+          </div>
           <div
             v-for="(item, index) in resArr"
             :key="'detail' + index"
@@ -62,6 +66,8 @@
                 curQue.detail[index].paragraphAttr
                   ? 'wordsList-box-' + curQue.detail[index].paragraphAttr.paragraphAlign
                   : '',
+                'article-content',
+                isHasRemark ? 'hasRemark' : '',
               ]"
             >
               <img v-if="articleImg[0] && index == 0" :src="articleImg[index]" />
@@ -412,6 +418,9 @@
               </div>
               <img v-if="articleImg[index + 1]" :src="articleImg[index + 1]" />
             </div>
+            <div v-if="item.remarkDetail" :class="['remarkBox', 'remark-top-8']">
+              <RemarkChs :remark-detail="item.remarkDetail" :margin-top="8" />
+            </div>
           </div>
           <!-- <div class="multilingual" v-for="(items, indexs) in multilingualTextList" :key="indexs">
             {{ items }}
@@ -421,7 +430,8 @@
     </template>
     <template v-else>
       <template v-if="resObj">
-        <!--  -->
+        <!-- 段落对齐方式和备注在此模式里没有写,如果段落里添加了英文后需要在此添加段落对齐和备注 -->
+
         <div class="NPC-sentences-list">
           <div v-for="(item, index) in resObj.sentList" :key="'detail' + index" :class="['NNPE-detail-box']">
             <div :class="['NNPE-details']">
@@ -893,12 +903,15 @@
 import AudioLine from '../voice_matrix/components/AudioLine.vue';
 import Wordcard from './components/Wordcard.vue'; // 卡片
 import Notecard from './components/Notecard.vue'; // 注释
+import RemarkChs from '../dialogue_article/RemarkChs.vue';
+
 export default {
   name: 'PhraseModelChs',
   components: {
     AudioLine,
     Wordcard,
     Notecard,
+    RemarkChs,
   },
   props: [
     'curQue',
@@ -947,6 +960,7 @@ export default {
       highIndex: 0,
       activeWord: null,
       windowWidth: window.innerWidth,
+      isHasRemark: false,
     };
   },
   computed: {},
@@ -1052,6 +1066,14 @@ export default {
       let dhaspinyin = false; // 每段是否有拼音
       curQue.detail.forEach((dItem, dIndex) => {
         dhaspinyin = false;
+        let remarkDetail = dItem.remark;
+        if (
+          remarkDetail &&
+          (remarkDetail.chs || remarkDetail.en || (remarkDetail.img_list && remarkDetail.img_list.length > 0))
+        ) {
+          this.isHasRemark = true;
+        }
+
         let paraArr = [];
         if (!dItem.isTitle) {
           paraArr = [
@@ -1118,6 +1140,7 @@ export default {
           dhaspinyin,
           isTitle: dItem.isTitle,
           paraAlign: dItem.paraAlign,
+          remarkDetail,
         };
         resArr.push(paraObj);
       });
@@ -1552,7 +1575,38 @@ export default {
   }
 
   .NPC-sentences-list {
-    padding: 24px 0;
+    // padding: 24px 0;
+
+    .NPC-article-empty {
+      display: flex;
+      align-items: flex-start;
+      justify-content: flex-start;
+
+      > div {
+        height: 24px;
+
+        &.empty-left {
+          box-sizing: border-box;
+          width: 100%;
+
+          &.hasRemark {
+            box-sizing: border-box;
+            width: 60%;
+            border-right: 1px rgba(0, 0, 0, 10%) solid;
+          }
+        }
+
+        &.empty-right {
+          flex: 1;
+        }
+      }
+
+      &-bottom {
+        > div {
+          height: 40px;
+        }
+      }
+    }
   }
 
   .multilingual {
@@ -1561,7 +1615,10 @@ export default {
   }
 
   .NNPE-detail {
-    overflow: hidden;
+    // overflow: hidden; // 为了不遮挡备注内容
+    display: flex;
+    align-items: flex-start;
+    justify-content: flex-start;
     clear: both;
     color: rgba(0, 0, 0, 100%);
 
@@ -1721,6 +1778,44 @@ export default {
     }
   }
 
+  .article-content {
+    box-sizing: border-box;
+    display: flex;
+    align-items: flex-start;
+    justify-content: flex-start;
+    width: 100%;
+
+    &.hasRemark {
+      width: 60%;
+      padding: 8px 0 8px 23px;
+      border-right: 1px rgba(0, 0, 0, 10%) solid;
+    }
+
+    &.paraLast {
+      padding-bottom: 24px;
+    }
+  }
+
+  .remarkBox {
+    position: relative;
+    display: flex;
+    flex: 1;
+    align-items: center;
+    justify-content: center;
+
+    &.remark72 {
+      padding-top: 72px;
+    }
+
+    &.remark-top {
+      padding-top: 44px;
+    }
+
+    &.remark-top-8 {
+      padding-top: 8px;
+    }
+  }
+
   .NNPE-wordDetail {
     position: fixed;
     top: 50%;

+ 4 - 3
src/views/book/courseware/preview/components/dialogue_article/RemarkChs.vue

@@ -16,9 +16,9 @@
           width: remarkDetail.widthNumber ? remarkDetail.widthNumber + 'px' : '',
           height: remarkDetail.heightNumber ? remarkDetail.heightNumber + 'px' : '',
         }"
-        :src="remarkDetail.img_list[0].id"
+        :src="remarkDetail.img_list[0].url"
         fit="contain"
-        :preview-src-list="[remarkDetail.img_list[0].id]"
+        :preview-src-list="[remarkDetail.img_list[0].url]"
       />
     </div>
   </div>
@@ -56,6 +56,8 @@ export default {
   top: 0;
   box-sizing: border-box;
   width: 95%;
+
+  // border: 1px solid rgba(0, 0, 0, 10%);
   border-radius: 8px;
   box-shadow: 0 4px 8px rgba(0, 0, 0, 10%);
 
@@ -88,7 +90,6 @@ export default {
     color: #000;
     text-align: center;
     word-break: break-word;
-    border: 1px solid rgba(0, 0, 0, 10%);
     border-top: 0;
     border-radius: 0 0 8px 8px;