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

组件背景图、背景色、边框、边框颜色

dsy 2 месяцев назад
Родитель
Сommit
3b9b984c4a
87 измененных файлов с 445 добавлено и 114 удалено
  1. 1 1
      .env
  2. 1 1
      package.json
  3. 1 0
      src/styles/mixin.scss
  4. 99 30
      src/views/book/courseware/create/components/SelectBackground.vue
  5. 1 1
      src/views/book/courseware/create/components/base/3d_model/3DModelSetting.vue
  6. 4 2
      src/views/book/courseware/create/components/base/audio/AudioSetting.vue
  7. 1 1
      src/views/book/courseware/create/components/base/character_base/CharacterBaseSetting.vue
  8. 1 1
      src/views/book/courseware/create/components/base/h5_games/H5GamesSetting.vue
  9. 1 1
      src/views/book/courseware/create/components/base/label/LabelSetting.vue
  10. 1 1
      src/views/book/courseware/create/components/base/picture/PictureSetting.vue
  11. 3 1
      src/views/book/courseware/create/components/base/rich_text/RichTextSetting.vue
  12. 1 1
      src/views/book/courseware/create/components/base/stem/StemSetting.vue
  13. 1 1
      src/views/book/courseware/create/components/base/upload_control/UploadControlSetting.vue
  14. 1 1
      src/views/book/courseware/create/components/base/upload_preview/UploadRreviewSetting.vue
  15. 3 1
      src/views/book/courseware/create/components/base/video/VideoSetting.vue
  16. 1 1
      src/views/book/courseware/create/components/base/write_base/WriteBaseSetting.vue
  17. 134 0
      src/views/book/courseware/create/components/common/BackgroundSet.vue
  18. 4 2
      src/views/book/courseware/create/components/common/SettingMixin.js
  19. 3 2
      src/views/book/courseware/create/components/question/article/ArticleSetting.vue
  20. 5 4
      src/views/book/courseware/create/components/question/character/CharacterSetting.vue
  21. 2 1
      src/views/book/courseware/create/components/question/character_structure/CharacterStructureSetting.vue
  22. 5 4
      src/views/book/courseware/create/components/question/dialogue_article/ArticleSetting.vue
  23. 2 1
      src/views/book/courseware/create/components/question/drawing/DrawingSetting.vue
  24. 5 5
      src/views/book/courseware/create/components/question/fill/FillSetting.vue
  25. 2 1
      src/views/book/courseware/create/components/question/image_text/ImageTextSetting.vue
  26. 1 1
      src/views/book/courseware/create/components/question/input/InputSetting.vue
  27. 2 1
      src/views/book/courseware/create/components/question/judge/JudgeSetting.vue
  28. 3 1
      src/views/book/courseware/create/components/question/matching/MatchingSetting.vue
  29. 1 1
      src/views/book/courseware/create/components/question/math/MathSetting.vue
  30. 2 1
      src/views/book/courseware/create/components/question/newWord_template/NewWordTemplateSetting.vue
  31. 2 1
      src/views/book/courseware/create/components/question/new_word/NewWordSetting.vue
  32. 2 1
      src/views/book/courseware/create/components/question/notes/NotesSetting.vue
  33. 1 1
      src/views/book/courseware/create/components/question/other_word/OtherWordSetting.vue
  34. 5 4
      src/views/book/courseware/create/components/question/pinyin_base/PinyinBaseSetting.vue
  35. 2 1
      src/views/book/courseware/create/components/question/record_input/RecordInputSetting.vue
  36. 2 2
      src/views/book/courseware/create/components/question/select/SelectSetting.vue
  37. 2 2
      src/views/book/courseware/create/components/question/sort/SortSetting.vue
  38. 2 1
      src/views/book/courseware/create/components/question/table/TableSetting.vue
  39. 2 1
      src/views/book/courseware/create/components/question/video_interaction/VideoInteractionSetting.vue
  40. 2 2
      src/views/book/courseware/create/components/question/voice_matrix/VoiceMatrixSetting.vue
  41. 1 1
      src/views/book/courseware/create/components/question/write/WriteSetting.vue
  42. 5 2
      src/views/book/courseware/data/article.js
  43. 7 1
      src/views/book/courseware/data/audio.js
  44. 2 0
      src/views/book/courseware/data/character.js
  45. 3 0
      src/views/book/courseware/data/characterStructure.js
  46. 24 0
      src/views/book/courseware/data/common.js
  47. 4 1
      src/views/book/courseware/data/dialogueArticle.js
  48. 2 0
      src/views/book/courseware/data/drawing.js
  49. 2 0
      src/views/book/courseware/data/fill.js
  50. 3 0
      src/views/book/courseware/data/imageText.js
  51. 2 0
      src/views/book/courseware/data/judge.js
  52. 2 0
      src/views/book/courseware/data/matching.js
  53. 2 0
      src/views/book/courseware/data/newWord.js
  54. 3 0
      src/views/book/courseware/data/newWordTemplate.js
  55. 2 0
      src/views/book/courseware/data/notes.js
  56. 3 0
      src/views/book/courseware/data/pinyinBase.js
  57. 2 0
      src/views/book/courseware/data/recordInput.js
  58. 2 0
      src/views/book/courseware/data/richText.js
  59. 2 0
      src/views/book/courseware/data/select.js
  60. 2 0
      src/views/book/courseware/data/sort.js
  61. 3 0
      src/views/book/courseware/data/table.js
  62. 2 0
      src/views/book/courseware/data/video.js
  63. 2 0
      src/views/book/courseware/data/videoInteraction.js
  64. 2 0
      src/views/book/courseware/data/voiceMatrix.js
  65. 1 1
      src/views/book/courseware/preview/components/article/index.vue
  66. 2 2
      src/views/book/courseware/preview/components/audio/AudioPreview.vue
  67. 1 1
      src/views/book/courseware/preview/components/character/CharacterPreview.vue
  68. 1 1
      src/views/book/courseware/preview/components/character_structure/CharacterStructurePreview.vue
  69. 24 0
      src/views/book/courseware/preview/components/common/PreviewMixin.js
  70. 1 1
      src/views/book/courseware/preview/components/dialogue_article/index.vue
  71. 1 1
      src/views/book/courseware/preview/components/drawing/DrawingPreview.vue
  72. 1 1
      src/views/book/courseware/preview/components/fill/FillPreview.vue
  73. 1 1
      src/views/book/courseware/preview/components/image_text/ImageTextPreview.vue
  74. 1 1
      src/views/book/courseware/preview/components/judge/JudgePreview.vue
  75. 1 1
      src/views/book/courseware/preview/components/matching/MatchingPreview.vue
  76. 1 1
      src/views/book/courseware/preview/components/newWord_template/NewWordTemplatePreview.vue
  77. 1 1
      src/views/book/courseware/preview/components/new_word/NewWordPreview.vue
  78. 1 1
      src/views/book/courseware/preview/components/notes/NotesPreview.vue
  79. 1 1
      src/views/book/courseware/preview/components/pinyin_base/PinyinBasePreview.vue
  80. 1 1
      src/views/book/courseware/preview/components/record_input/RecordInputPreview.vue
  81. 1 1
      src/views/book/courseware/preview/components/rich_text/RichTextPreview.vue
  82. 1 1
      src/views/book/courseware/preview/components/select/SelectPreview.vue
  83. 1 1
      src/views/book/courseware/preview/components/sort/SortPreview.vue
  84. 1 1
      src/views/book/courseware/preview/components/table/TablePreview.vue
  85. 1 1
      src/views/book/courseware/preview/components/video/VideoPreview.vue
  86. 1 1
      src/views/book/courseware/preview/components/video_interaction/VideoInteractionPreview.vue
  87. 1 1
      src/views/book/courseware/preview/components/voice_matrix/VoiceMatrixPreview.vue

+ 1 - 1
.env

@@ -11,4 +11,4 @@ VUE_APP_BookWebSI = '/GCLSBookWebSI/ServiceInterface'
 VUE_APP_EepServer = '/EEPServer/SI'
 
 #version
-VUE_APP_VERSION = '2026.01.07'
+VUE_APP_VERSION = '2026.01.12'

+ 1 - 1
package.json

@@ -1,6 +1,6 @@
 {
   "name": "eep_page",
-  "version": "2026.01.07",
+  "version": "2026.01.12",
   "private": true,
   "main": "main.js",
   "description": "智慧梧桐数字教材编辑器",

+ 1 - 0
src/styles/mixin.scss

@@ -41,6 +41,7 @@
 @mixin preview-base {
   display: grid;
   gap: 6px;
+  background-repeat: no-repeat;
 
   :deep .rich-text {
     @include rich-text;

+ 99 - 30
src/views/book/courseware/create/components/SelectBackground.vue

@@ -8,8 +8,8 @@
   >
     <div class="select-background">
       <div class="select-background-top">
-        <span class="tab">背景图</span>
-        <SelectUpload type="image" @uploadSuccess="uploadSuccess" />
+        <span class="tab">{{ title }}</span>
+        <SelectUpload :is-show-resource="isResource" type="image" @uploadSuccess="uploadSuccess" />
       </div>
       <div class="background-img">
         <div v-if="file_url" class="img-set" :style="{ top: `${imgData.top - 9}px`, left: `${imgData.left}px` }">
@@ -20,7 +20,7 @@
           <img
             :src="file_url"
             draggable="false"
-            alt="背景图"
+            :alt="title"
             :style="{ width: `${imgData.width}px`, height: `${imgData.height}px` }"
             @mousedown="dragStart($event, 'move', 'move')"
           />
@@ -52,32 +52,75 @@ export default {
       type: Boolean,
       required: true,
     },
+    title: {
+      type: String,
+      default: '背景图',
+    },
+    url: {
+      type: String,
+      default: '',
+    },
+    position: {
+      type: Object,
+      default: () => ({ width: 0, height: 0, top: 0, left: 0 }),
+    },
+    // 是否使用资源
+    isResource: {
+      type: Boolean,
+      default: true,
+    },
+    // 图片填充模式
+    imageFillMode: {
+      type: String,
+      default: 'normal', // normal:正常,cover:覆盖
+    },
   },
   data() {
     return {
       maxWidth: 576,
       maxHeight: 310,
-      file_url: '',
+      file_url: this.url,
       drag: {
         dragging: false,
         startX: 0,
         startY: 0,
         type: '',
       },
-      imgData: {
-        width: 0,
-        height: 0,
-        top: 0,
-        left: 0,
-      },
+      imgData: this.position,
     };
   },
+  watch: {
+    visible(val) {
+      if (val) {
+        this.$nextTick(() => {
+          document.querySelector('.background-img').addEventListener('mousemove', this.mouseMove);
+        });
+      } else {
+        document.querySelector('.background-img')?.removeEventListener('mousemove', this.mouseMove);
+      }
+    },
+    url(val) {
+      this.file_url = val;
+    },
+    position: {
+      handler(val) {
+        if (val) {
+          this.imgData = {
+            width: (val.width * this.maxWidth) / 100,
+            height: (val.height * this.maxHeight) / 100,
+            top: (val.top * this.maxHeight) / 100,
+            left: (val.left * this.maxWidth) / 100,
+          };
+        }
+      },
+      deep: true,
+    },
+  },
   mounted() {
-    document.querySelector('.el-dialog__wrapper').addEventListener('mousemove', this.mouseMove);
     document.body.addEventListener('mouseup', this.mouseUp);
   },
   beforeDestroy() {
-    document.querySelector('.el-dialog__wrapper')?.removeEventListener('mousemove', this.mouseMove);
+    document.querySelector('.background-img')?.removeEventListener('mousemove', this.mouseMove);
     document.body.removeEventListener('mouseup', this.mouseUp);
   },
   methods: {
@@ -185,29 +228,55 @@ export default {
         img.onload = () => {
           const { width, height } = img;
 
-          if (width > this.maxWidth || height > this.maxHeight) {
-            const wScale = width / this.maxWidth;
-            const hScale = height / this.maxHeight;
-            const scale = wScale > hScale ? this.maxWidth / 2 / width : this.maxHeight / 2 / height;
-
-            this.imgData = {
-              width: width * scale,
-              height: height * scale,
-              top: 0,
-              left: 0,
-            };
-          } else {
-            this.imgData = {
-              width,
-              height,
-              top: 0,
-              left: 0,
-            };
+          if (this.imageFillMode === 'cover') {
+            this.coverComputed(width, height);
+          } else if (this.imageFillMode === 'normal') {
+            this.normalComputed(width, height);
           }
+
           this.file_url = fileList[0].file_url_open;
         };
       }
     },
+    /**
+     * 正常填充
+     * @param {number} width 图片宽度
+     * @param {number} height 图片高度
+     */
+    normalComputed(width, height) {
+      if (width > this.maxWidth || height > this.maxHeight) {
+        const wScale = width / this.maxWidth;
+        const hScale = height / this.maxHeight;
+        const scale = wScale > hScale ? this.maxWidth / 2 / width : this.maxHeight / 2 / height;
+
+        this.imgData = {
+          width: width * scale,
+          height: height * scale,
+          top: 0,
+          left: 0,
+        };
+      } else {
+        this.imgData = {
+          width,
+          height,
+          top: 0,
+          left: 0,
+        };
+      }
+    },
+    /**
+     * 覆盖填充
+     * @param {number} width 图片宽度
+     * @param {number} height 图片高度
+     */
+    coverComputed(width, height) {
+      this.imgData = {
+        width: width * (this.maxWidth / width),
+        height: height * (this.maxHeight / height),
+        top: 0,
+        left: 0,
+      };
+    },
   },
 };
 </script>

+ 1 - 1
src/views/book/courseware/create/components/base/3d_model/3DModelSetting.vue

@@ -1,7 +1,7 @@
 <template>
   <div>
     <el-form :model="property" label-width="72px" label-position="left">
-      <SerailNumber :property="property" />
+      <SerialNumber :property="property" />
     </el-form>
   </div>
 </template>

+ 4 - 2
src/views/book/courseware/create/components/base/audio/AudioSetting.vue

@@ -1,9 +1,11 @@
 <template>
   <div>
     <el-form :model="property" :label-position="labelPosition" label-width="90px">
-      <SerailNumber :property="property" />
+      <SerialNumber :property="property" />
 
-      <el-form-item label="文件名称" v-if="false">
+      <BackgroundSet :property="property" />
+
+      <el-form-item v-if="false" label="文件名称">
         <el-radio
           v-for="{ value, label } in displayList"
           :key="value"

+ 1 - 1
src/views/book/courseware/create/components/base/character_base/CharacterBaseSetting.vue

@@ -1,7 +1,7 @@
 <template>
   <div>
     <el-form :model="property" label-width="72px" label-position="left">
-      <SerailNumber :property="property" />
+      <SerialNumber :property="property" />
       <el-form-item label="功能">
         <el-select v-model="property.fun_type" placeholder="请选择">
           <el-option v-for="{ value, label } in funList" :key="value" :label="label" :value="value" />

+ 1 - 1
src/views/book/courseware/create/components/base/h5_games/H5GamesSetting.vue

@@ -1,7 +1,7 @@
 <template>
   <div>
     <el-form :model="property" label-width="72px" label-position="left">
-      <SerailNumber :property="property" />
+      <SerialNumber :property="property" />
     </el-form>
   </div>
 </template>

+ 1 - 1
src/views/book/courseware/create/components/base/label/LabelSetting.vue

@@ -1,7 +1,7 @@
 <template>
   <div>
     <el-form :model="property" :label-position="labelPosition" label-width="72px">
-      <SerailNumber :property="property" />
+      <SerialNumber :property="property" />
 
       <el-form-item label="标签颜色" :style="cssVars">
         <el-select v-model="property.label_color" class="label-color">

+ 1 - 1
src/views/book/courseware/create/components/base/picture/PictureSetting.vue

@@ -1,7 +1,7 @@
 <template>
   <div>
     <el-form :model="property" :label-position="labelPosition" label-width="72px">
-      <SerailNumber :property="property" />
+      <SerialNumber :property="property" />
 
       <el-form-item label="查看方式">
         <el-radio v-for="{ value, label } in viewMethodList" :key="value" v-model="property.view_method" :label="value">

+ 3 - 1
src/views/book/courseware/create/components/base/rich_text/RichTextSetting.vue

@@ -1,7 +1,9 @@
 <template>
   <div>
     <el-form :model="property" :label-position="labelPosition" label-width="72px">
-      <SerailNumber :property="property" />
+      <SerialNumber :property="property" />
+
+      <BackgroundSet :property="property" />
 
       <el-form-item label="拼音">
         <el-switch v-model="property.view_pinyin" active-value="true" inactive-value="false" />

+ 1 - 1
src/views/book/courseware/create/components/base/stem/StemSetting.vue

@@ -1,7 +1,7 @@
 <template>
   <div>
     <el-form :model="property" :label-position="labelPosition" label-width="72px">
-      <SerailNumber :property="property" />
+      <SerialNumber :property="property" />
 
       <el-form-item label="拼音">
         <el-switch v-model="property.view_pinyin" active-value="true" inactive-value="false" />

+ 1 - 1
src/views/book/courseware/create/components/base/upload_control/UploadControlSetting.vue

@@ -1,7 +1,7 @@
 <template>
   <div>
     <el-form :model="property" :label-position="labelPosition" label-width="72px">
-      <SerailNumber :property="property" />
+      <SerialNumber :property="property" />
     </el-form>
   </div>
 </template>

+ 1 - 1
src/views/book/courseware/create/components/base/upload_preview/UploadRreviewSetting.vue

@@ -1,7 +1,7 @@
 <template>
   <div>
     <el-form :model="property" :label-position="labelPosition" label-width="72px">
-      <SerailNumber :property="property" />
+      <SerialNumber :property="property" />
       <el-form-item label="下载">
         <el-radio-group v-model="property.is_enable_download">
           <el-radio v-for="{ value, label } in switchOption" :key="value" :label="value">

+ 3 - 1
src/views/book/courseware/create/components/base/video/VideoSetting.vue

@@ -1,7 +1,9 @@
 <template>
   <div>
     <el-form :model="property" :label-position="labelPosition" label-width="72px">
-      <SerailNumber :property="property" />
+      <SerialNumber :property="property" />
+
+      <BackgroundSet :property="property" />
 
       <el-form-item label="查看方式">
         <el-radio v-for="{ value, label } in viewMethodList" :key="value" v-model="property.view_method" :label="value">

+ 1 - 1
src/views/book/courseware/create/components/base/write_base/WriteBaseSetting.vue

@@ -1,7 +1,7 @@
 <template>
   <div>
     <el-form :model="property" label-width="72px" label-position="left">
-      <SerailNumber :property="property" />
+      <SerialNumber :property="property" />
     </el-form>
   </div>
 </template>

+ 134 - 0
src/views/book/courseware/create/components/common/BackgroundSet.vue

@@ -0,0 +1,134 @@
+<template>
+  <div>
+    <el-form-item label="背景图" label-width="60px">
+      <el-button type="primary" size="mini" @click="openSetBackgroundImage">设置背景图</el-button>
+    </el-form-item>
+    <el-form-item label="背景色" label-width="60px">
+      <el-color-picker v-model="propertyObj.background_color" :show-alpha="true" @change="changeBackgroundColor" />
+    </el-form-item>
+    <el-form-item label="边框" label-width="60px" class="border-setting">
+      <el-switch v-model="propertyObj.is_border" active-value="true" inactive-value="false" @change="changeIsBorder" />
+      <el-select
+        v-model="propertyObj.border_style"
+        :disabled="!isEnable(propertyObj.is_border)"
+        placeholder="请选择边框样式"
+        style="width: 120px; margin-left: 10px"
+        @change="changeBorderStyle"
+      >
+        <el-option v-for="item in borderStyleList" :key="item.value" :label="item.label" :value="item.value" />
+      </el-select>
+      <el-color-picker
+        v-model="propertyObj.border_color"
+        :disabled="!isEnable(propertyObj.is_border)"
+        @change="changeBorderColor"
+      />
+    </el-form-item>
+    <el-divider />
+
+    <SelectBackground
+      :visible.sync="visible"
+      image-fill-mode="cover"
+      :is-resource="false"
+      :position="propertyObj.background_image_position"
+      title="组件背景图"
+      :url="propertyObj.background_image_url"
+      @setBackgroundImage="setBackgroundImage"
+    />
+  </div>
+</template>
+
+<script>
+import { borderStyleList, isEnable } from '@/views/book/courseware/data/common';
+
+import SelectBackground from '@/views/book/courseware/create/components/SelectBackground.vue';
+
+export default {
+  name: 'BackgroundSet',
+  components: {
+    SelectBackground,
+  },
+  inject: ['updateProperty'],
+  props: {
+    property: {
+      type: Object,
+      required: true,
+    },
+  },
+  data() {
+    return {
+      propertyObj: this.property,
+      borderStyleList,
+      isEnable,
+      visible: false,
+    };
+  },
+  watch: {
+    property: {
+      handler(val) {
+        this.propertyObj = val;
+      },
+      deep: true,
+    },
+  },
+  methods: {
+    /**
+     * 改变背景色
+     * @param {string} color 颜色值
+     */
+    changeBackgroundColor(color) {
+      this.propertyObj.background_color = color;
+      this.updateProperty('background_color', color);
+    },
+    /**
+     * 改变边框状态
+     * @param {string} value 边框状态
+     */
+    changeIsBorder(value) {
+      this.propertyObj.is_border = value;
+      this.updateProperty('is_border', value);
+    },
+    /**
+     * 改变边框样式
+     * @param {string} style 边框样式
+     */
+    changeBorderStyle(style) {
+      this.propertyObj.border_style = style;
+      this.updateProperty('border_style', style);
+    },
+    /**
+     * 改变边框颜色
+     * @param {string} color 颜色值
+     */
+    changeBorderColor(color) {
+      this.propertyObj.border_color = color;
+      this.updateProperty('border_color', color);
+    },
+    openSetBackgroundImage() {
+      this.visible = true;
+    },
+    /**
+     * 设置背景图
+     * @param {string} url 图片地址
+     * @param {string} position 图片位置
+     */
+    setBackgroundImage(url, position) {
+      this.propertyObj.background_image_url = url;
+      this.updateProperty('background_image_url', url);
+      this.propertyObj.background_position = position;
+      this.updateProperty('background_position', position);
+
+      this.$message.success('设置背景图成功');
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+.border-setting {
+  :deep .el-form-item__content {
+    display: flex;
+    column-gap: 4px;
+    align-items: center;
+  }
+}
+</style>

+ 4 - 2
src/views/book/courseware/create/components/common/SettingMixin.js

@@ -1,6 +1,7 @@
 import { checkString, isEnable, pinyinPositionList } from '@/views/book/courseware/data/common';
 
-import SerailNumber from '@/views/book/courseware/create/components/common/SerialNumber.vue';
+import SerialNumber from '@/views/book/courseware/create/components/common/SerialNumber.vue';
+import BackgroundSet from '@/views/book/courseware/create/components/common/BackgroundSet.vue';
 
 const mixin = {
   data() {
@@ -12,7 +13,8 @@ const mixin = {
     };
   },
   components: {
-    SerailNumber,
+    SerialNumber,
+    BackgroundSet,
   },
   watch: {
     'property.serial_number': {

+ 3 - 2
src/views/book/courseware/create/components/question/article/ArticleSetting.vue

@@ -1,7 +1,8 @@
 <template>
   <div>
     <el-form :model="property" label-width="72px" label-position="left">
-      <SerailNumber :property="property" />
+      <SerialNumber :property="property" />
+      <BackgroundSet :property="property" />
       <el-form-item label="音频位置">
         <el-radio-group v-model="property.mp3_position">
           <el-radio v-for="{ value, label } in positionList" :key="value" :label="value" :value="value">
@@ -65,7 +66,7 @@
         </el-radio-group>
       </el-form-item>
       <el-form-item label="内容高度">
-        <el-input v-model="property.content_height" type="number"></el-input>
+        <el-input v-model="property.content_height" type="number" />
       </el-form-item>
     </el-form>
   </div>

+ 5 - 4
src/views/book/courseware/create/components/question/character/CharacterSetting.vue

@@ -1,7 +1,8 @@
 <template>
   <div>
     <el-form :model="property" label-width="72px" label-position="left">
-      <SerailNumber :property="property" />
+      <SerialNumber :property="property" />
+      <BackgroundSet :property="property" />
       <el-form-item label="汉字框">
         <el-radio-group v-model="property.frame_type">
           <el-radio v-for="{ value, label } in frameList" :key="value" :label="value" :value="value">
@@ -112,6 +113,9 @@ export default {
       emotion_list: [],
     };
   },
+  created() {
+    this.getTextToAudioConfParamList();
+  },
   methods: {
     // 得到文本转音频的配置参数列表
     getTextToAudioConfParamList() {
@@ -125,9 +129,6 @@ export default {
         .catch(() => {});
     },
   },
-  created() {
-    this.getTextToAudioConfParamList();
-  },
 };
 </script>
 

+ 2 - 1
src/views/book/courseware/create/components/question/character_structure/CharacterStructureSetting.vue

@@ -1,7 +1,8 @@
 <template>
   <div>
     <el-form :model="property" label-width="72px" label-position="left">
-      <SerailNumber :property="property" />
+      <SerialNumber :property="property" />
+      <BackgroundSet :property="property" />
       <el-form-item label="拼音显示">
         <el-radio-group v-model="property.view_pinyin">
           <el-radio v-for="{ value, label } in showList" :key="value" :label="value">

+ 5 - 4
src/views/book/courseware/create/components/question/dialogue_article/ArticleSetting.vue

@@ -1,7 +1,8 @@
 <template>
   <div>
     <el-form :model="property" label-width="72px" label-position="left">
-      <SerailNumber :property="property" />
+      <SerialNumber :property="property" />
+      <BackgroundSet :property="property" />
       <el-form-item label="音频位置">
         <el-radio-group v-model="property.mp3_position">
           <el-radio v-for="{ value, label } in positionList" :key="value" :label="value" :value="value">
@@ -87,7 +88,7 @@
             <div class="user-seg-list">
               <div v-for="(items, indexs) in item.segList" :key="indexs">
                 <label>{{ items.chs }}:</label>
-                <el-input v-model="items.matchWords"></el-input>
+                <el-input v-model="items.matchWords" />
               </div>
             </div>
           </div>
@@ -130,7 +131,7 @@
         </el-radio-group>
       </el-form-item>
       <el-form-item label="内容高度">
-        <el-input v-model="property.content_height" type="number"></el-input>
+        <el-input v-model="property.content_height" type="number" />
       </el-form-item>
     </el-form>
   </div>
@@ -240,7 +241,7 @@ export default {
     },
     // 上传
     onSuccess(res, i) {
-      if (res.status == -1) {
+      if (res.status === -1) {
         this.$message.error(res.error);
         return;
       }

+ 2 - 1
src/views/book/courseware/create/components/question/drawing/DrawingSetting.vue

@@ -1,7 +1,8 @@
 <template>
   <div>
     <el-form :model="property" label-width="72px" label-position="left">
-      <SerailNumber :property="property" />
+      <SerialNumber :property="property" />
+      <BackgroundSet :property="property" />
     </el-form>
   </div>
 </template>

+ 5 - 5
src/views/book/courseware/create/components/question/fill/FillSetting.vue

@@ -1,8 +1,8 @@
 <template>
   <div>
     <el-form :model="property" label-width="72px" label-position="left">
-      <SerailNumber :property="property" />
-
+      <SerialNumber :property="property" />
+      <BackgroundSet :property="property" />
       <el-form-item label="排列">
         <el-radio-group v-model="property.arrange_type">
           <el-radio v-for="{ value, label } in arrangeTypeList" :key="value" :label="value">
@@ -120,6 +120,9 @@ export default {
       emotion_list: [],
     };
   },
+  created() {
+    this.getTextToAudioConfParamList();
+  },
   methods: {
     // 得到文本转音频的配置参数列表
     getTextToAudioConfParamList() {
@@ -133,9 +136,6 @@ export default {
         .catch(() => {});
     },
   },
-  created() {
-    this.getTextToAudioConfParamList();
-  },
 };
 </script>
 

+ 2 - 1
src/views/book/courseware/create/components/question/image_text/ImageTextSetting.vue

@@ -1,7 +1,8 @@
 <template>
   <div>
     <el-form :model="property" label-width="72px" label-position="left">
-      <SerailNumber :property="property" />
+      <SerialNumber :property="property" />
+      <BackgroundSet :property="property" />
 
       <el-form-item label="拼音">
         <el-switch v-model="property.view_pinyin" active-value="true" inactive-value="false" />

+ 1 - 1
src/views/book/courseware/create/components/question/input/InputSetting.vue

@@ -1,7 +1,7 @@
 <template>
   <div>
     <el-form :model="property" :label-position="labelPosition" label-width="72px">
-      <SerailNumber :property="property" />
+      <SerialNumber :property="property" />
 
       <el-form-item label="拼音">
         <el-switch v-model="property.is_enable_pinyin" active-text="" inactive-text="" />

+ 2 - 1
src/views/book/courseware/create/components/question/judge/JudgeSetting.vue

@@ -1,7 +1,8 @@
 <template>
   <div>
     <el-form :model="property" label-width="72px" label-position="left">
-      <SerailNumber :property="property" />
+      <SerialNumber :property="property" />
+      <BackgroundSet :property="property" />
 
       <el-form-item label="选项序号" class="serial-number">
         <el-input v-model="optionSerialType" disabled />

+ 3 - 1
src/views/book/courseware/create/components/question/matching/MatchingSetting.vue

@@ -1,7 +1,9 @@
 <template>
   <div>
     <el-form :model="property" label-width="42px">
-      <SerailNumber :property="property" />
+      <SerialNumber :property="property" />
+
+      <BackgroundSet :property="property" />
 
       <el-form-item class="option-serial-number" label-width="0">
         <div>选项序号</div>

+ 1 - 1
src/views/book/courseware/create/components/question/math/MathSetting.vue

@@ -1,7 +1,7 @@
 <template>
   <div>
     <el-form :model="property" label-width="42px">
-      <SerailNumber :property="property" />
+      <SerialNumber :property="property" />
     </el-form>
   </div>
 </template>

+ 2 - 1
src/views/book/courseware/create/components/question/newWord_template/NewWordTemplateSetting.vue

@@ -1,7 +1,8 @@
 <template>
   <div>
     <el-form :model="property" label-width="72px" label-position="left">
-      <SerailNumber :property="property" />
+      <SerialNumber :property="property" />
+      <BackgroundSet :property="property" />
       <el-form-item label="笔画动画">
         <el-radio-group v-model="property.is_enable_play_structure">
           <el-radio v-for="{ value, label } in showList" :key="value" :label="value">

+ 2 - 1
src/views/book/courseware/create/components/question/new_word/NewWordSetting.vue

@@ -1,7 +1,8 @@
 <template>
   <div>
     <el-form :model="property" label-width="72px" label-position="left">
-      <SerailNumber :property="property" />
+      <SerialNumber :property="property" />
+      <BackgroundSet :property="property" />
       <el-form-item label="详细信息">
         <el-radio-group v-model="property.is_has_infor">
           <el-radio v-for="{ value, label } in inforList" :key="value" :label="value">

+ 2 - 1
src/views/book/courseware/create/components/question/notes/NotesSetting.vue

@@ -1,7 +1,8 @@
 <template>
   <div>
     <el-form :model="property" label-width="72px" label-position="left">
-      <SerailNumber :property="property" />
+      <SerialNumber :property="property" />
+      <BackgroundSet :property="property" />
       <!-- <el-form-item label="预览展开">
         <el-radio-group v-model="property.is_word_show">
           <el-radio v-for="{ value, label } in wordShowList" :key="value" :label="value">

+ 1 - 1
src/views/book/courseware/create/components/question/other_word/OtherWordSetting.vue

@@ -1,7 +1,7 @@
 <template>
   <div>
     <el-form :model="property" label-width="72px" label-position="left">
-      <SerailNumber :property="property" />
+      <SerialNumber :property="property" />
       <el-form-item label="读音">
         <el-select v-model="property.audio_generation_method" placeholder="请选择">
           <el-option v-for="{ value, label } in audioGenerationMethodList" :key="value" :label="label" :value="value" />

+ 5 - 4
src/views/book/courseware/create/components/question/pinyin_base/PinyinBaseSetting.vue

@@ -1,7 +1,8 @@
 <template>
   <div>
     <el-form :model="property" label-width="72px" label-position="left">
-      <SerailNumber :property="property" />
+      <SerialNumber :property="property" />
+      <BackgroundSet :property="property" />
       <!-- <el-form-item label="功能">
         <el-select v-model="property.fun_type" placeholder="请选择">
           <el-option v-for="{ value, label } in funList" :key="value" :label="label" :value="value" />
@@ -95,6 +96,9 @@ export default {
       emotion_list: [],
     };
   },
+  created() {
+    this.getTextToAudioConfParamList();
+  },
   methods: {
     // 得到文本转音频的配置参数列表
     getTextToAudioConfParamList() {
@@ -108,9 +112,6 @@ export default {
         .catch(() => {});
     },
   },
-  created() {
-    this.getTextToAudioConfParamList();
-  },
 };
 </script>
 

+ 2 - 1
src/views/book/courseware/create/components/question/record_input/RecordInputSetting.vue

@@ -1,7 +1,8 @@
 <template>
   <div>
     <el-form :model="property" label-width="72px" label-position="left">
-      <SerailNumber :property="property" />
+      <SerialNumber :property="property" />
+      <BackgroundSet :property="property" />
     </el-form>
   </div>
 </template>

+ 2 - 2
src/views/book/courseware/create/components/question/select/SelectSetting.vue

@@ -1,8 +1,8 @@
 <template>
   <div>
     <el-form :model="property" label-width="72px">
-      <SerailNumber :property="property" />
-
+      <SerialNumber :property="property" />
+      <BackgroundSet :property="property" />
       <el-form-item label="选项序号" class="serial-number">
         <el-input v-model="optionSerialType" disabled />
         <SvgIcon icon-class="switch" height="16" width="16" @click="switchSNType" />

+ 2 - 2
src/views/book/courseware/create/components/question/sort/SortSetting.vue

@@ -1,8 +1,8 @@
 <template>
   <div>
     <el-form :model="property" label-width="72px">
-      <SerailNumber :property="property" />
-
+      <SerialNumber :property="property" />
+      <BackgroundSet :property="property" />
       <el-form-item label="选项数">
         <el-input-number v-model="property.option_count" :min="1" />
       </el-form-item>

+ 2 - 1
src/views/book/courseware/create/components/question/table/TableSetting.vue

@@ -1,7 +1,8 @@
 <template>
   <div>
     <el-form :model="property" label-width="72px">
-      <SerailNumber :property="property" />
+      <SerialNumber :property="property" />
+      <BackgroundSet :property="property" />
 
       <el-form-item label="宽度">
         <el-input-number v-model="property.width" :min="1" :step="50" />

+ 2 - 1
src/views/book/courseware/create/components/question/video_interaction/VideoInteractionSetting.vue

@@ -1,7 +1,8 @@
 <template>
   <div>
     <el-form :model="property" label-width="72px" label-position="left">
-      <SerailNumber :property="property" />
+      <SerialNumber :property="property" />
+      <BackgroundSet :property="property" />
     </el-form>
   </div>
 </template>

+ 2 - 2
src/views/book/courseware/create/components/question/voice_matrix/VoiceMatrixSetting.vue

@@ -1,8 +1,8 @@
 <template>
   <div>
     <el-form :model="property" label-width="72px">
-      <SerailNumber :property="property" />
-
+      <SerialNumber :property="property" />
+      <BackgroundSet :property="property" />
       <el-form-item label="行数">
         <el-input-number v-model="property.row_count" :min="1" />
       </el-form-item>

+ 1 - 1
src/views/book/courseware/create/components/question/write/WriteSetting.vue

@@ -1,7 +1,7 @@
 <template>
   <div>
     <el-form :model="property" label-width="72px" label-position="left">
-      <SerailNumber :property="property" />
+      <SerialNumber :property="property" />
       <el-form-item label="汉字框">
         <el-radio-group v-model="property.frame_type">
           <el-radio v-for="{ value, label } in frameList" :key="value" :label="value" :value="value">

+ 5 - 2
src/views/book/courseware/data/article.js

@@ -6,6 +6,7 @@ import {
   switchOption,
   isEnable,
   pinyinPositionList,
+  commonComponentProperty,
 } from '@/views/book/courseware/data/common';
 
 export { arrangeTypeList, switchOption, isEnable, pinyinPositionList };
@@ -80,13 +81,15 @@ export function getArticleProperty() {
     content_height: '', // 内容高度
     voice_type: '', // 音色
     emotion: '', // 风格,情感
+
+    ...commonComponentProperty,
   };
 }
 
 export function getArticleData() {
   return {
     type: 'article',
-    title: '课文组件',
+    title: '文章',
     property: getArticleProperty(),
     content: '', // 课文内容
     mp3_list: [], // 音频列表
@@ -206,7 +209,7 @@ export function getArticleData() {
         view_pinyin: 'false', // 显示拼音
         pinyin_position: pinyinPositionLists[1].value, // top bottom
         is_first_sentence_first_hz_pinyin_first_char_upper_case: 'true', // 句首大写
-      }
+      },
     },
     sentence_list_mp: [], // 句子+分词数组
     pinyin_type: 'pinyin',

+ 7 - 1
src/views/book/courseware/data/audio.js

@@ -1,4 +1,9 @@
-import { serialNumberTypeList, serialNumberPositionList, displayList } from '@/views/book/courseware/data/common';
+import {
+  serialNumberTypeList,
+  serialNumberPositionList,
+  displayList,
+  commonComponentProperty,
+} from '@/views/book/courseware/data/common';
 
 // 音频查看方式
 export const audioViewMethodList = [
@@ -20,6 +25,7 @@ export function getAudioProperty() {
     view_method: audioViewMethodList[0].value,
     file_name_display_mode: displayList[1].value,
     style_mode: audioViewStyleList[0].value,
+    ...commonComponentProperty,
   };
 }
 

+ 2 - 0
src/views/book/courseware/data/character.js

@@ -5,6 +5,7 @@ import {
   arrangeTypeList,
   switchOption,
   isEnable,
+  commonComponentProperty,
 } from '@/views/book/courseware/data/common';
 import { getRandomNumber } from '@/utils';
 
@@ -87,6 +88,7 @@ export function getCharacterProperty() {
     is_enable_error: showList[0].value,
     voice_type: '', // 音色
     emotion: '', // 风格,情感
+    ...commonComponentProperty,
   };
 }
 

+ 3 - 0
src/views/book/courseware/data/characterStructure.js

@@ -5,6 +5,7 @@ import {
   arrangeTypeList,
   switchOption,
   isEnable,
+  commonComponentProperty,
 } from '@/views/book/courseware/data/common';
 import { getRandomNumber } from '@/utils';
 
@@ -120,6 +121,8 @@ export function getCharacterStructureProperty() {
     sn_display_mode: displayList[1].value,
 
     view_pinyin: showList[0].value,
+
+    ...commonComponentProperty,
   };
 }
 

+ 24 - 0
src/views/book/courseware/data/common.js

@@ -42,6 +42,30 @@ export let commonSetProperty = {
   sn_display_mode: displayList[1].value, // 序号显示方式:true显示 false隐藏
 };
 
+// 公用组件设置
+export let commonComponentProperty = {
+  background_color: 'rgba(255, 255, 255, 1)', // 背景颜色
+  background_image_url: '',
+  background_position: {
+    width: 100,
+    height: 100,
+    top: 0,
+    left: 0,
+  },
+  // 是否开启边框
+  is_border: 'false',
+  border_style: 'solid', // 边框样式 solid实线 dashed虚线 dotted点状 double双线
+  border_color: '#000000', // 边框颜色
+};
+
+// 边框样式
+export const borderStyleList = [
+  { value: 'solid', label: '实线' },
+  { value: 'dashed', label: '虚线' },
+  { value: 'dotted', label: '点状' },
+  { value: 'double', label: '双线' },
+];
+
 // 序号样式
 export const serialNumberStyleList = [
   { value: 'solidBlockStyle' }, // 实心方块

+ 4 - 1
src/views/book/courseware/data/dialogueArticle.js

@@ -6,6 +6,7 @@ import {
   switchOption,
   isEnable,
   pinyinPositionList,
+  commonComponentProperty,
 } from '@/views/book/courseware/data/common';
 import { getRandomNumber } from '@/utils';
 
@@ -120,6 +121,8 @@ export function getArticleProperty() {
     content_height: '', // 内容高度
     voice_type: '', // 音色
     emotion: '', // 风格,情感
+
+    ...commonComponentProperty,
   };
 }
 
@@ -244,7 +247,7 @@ export function getArticleData() {
         view_pinyin: 'false', // 显示拼音
         pinyin_position: pinyinPositionLists[1].value, // top bottom
         is_first_sentence_first_hz_pinyin_first_char_upper_case: 'true', // 句首大写
-      }
+      },
     },
     sentence_list_mp: [], // 句子+分词数组
     pinyin_type: 'pinyin', // 拼音类型

+ 2 - 0
src/views/book/courseware/data/drawing.js

@@ -3,6 +3,7 @@ import {
   serialNumberTypeList,
   serialNumberPositionList,
   isEnable,
+  commonComponentProperty,
 } from '@/views/book/courseware/data/common';
 
 export { isEnable };
@@ -13,6 +14,7 @@ export function getDrawingProperty() {
     sn_type: serialNumberTypeList[0].value,
     sn_position: serialNumberPositionList[3].value,
     sn_display_mode: displayList[1].value,
+    ...commonComponentProperty,
   };
 }
 

+ 2 - 0
src/views/book/courseware/data/fill.js

@@ -5,6 +5,7 @@ import {
   arrangeTypeList,
   switchOption,
   pinyinPositionList,
+  commonComponentProperty,
 } from '@/views/book/courseware/data/common';
 
 export { arrangeTypeList, switchOption };
@@ -63,6 +64,7 @@ export function getFillProperty() {
     is_first_sentence_first_hz_pinyin_first_char_upper_case: displayList[0].value, // 句首大写
     voice_type: '', // 音色
     emotion: '', // 风格,情感
+    ...commonComponentProperty,
   };
 }
 

+ 3 - 0
src/views/book/courseware/data/imageText.js

@@ -4,6 +4,7 @@ import {
   serialNumberPositionList,
   isEnable,
   pinyinPositionList,
+  commonComponentProperty,
 } from '@/views/book/courseware/data/common';
 
 export { isEnable };
@@ -18,6 +19,8 @@ export function getImageTextProperty() {
     view_pinyin: 'true', // 显示拼音
     pinyin_position: pinyinPositionList[0].value, // top bottom
     is_first_sentence_first_hz_pinyin_first_char_upper_case: 'true', // 句首大写
+
+    ...commonComponentProperty,
   };
 }
 

+ 2 - 0
src/views/book/courseware/data/judge.js

@@ -5,6 +5,7 @@ import {
   switchOption,
   isEnable,
   pinyinPositionList,
+  commonComponentProperty,
 } from '@/views/book/courseware/data/common';
 import { getRandomNumber } from '@/utils';
 
@@ -29,6 +30,7 @@ export function getJudgeProperty() {
     view_pinyin: 'false', // 显示拼音
     pinyin_position: pinyinPositionList[0].value,
     is_first_sentence_first_hz_pinyin_first_char_upper_case: displayList[0].value, // 句首大写
+    ...commonComponentProperty,
   };
 }
 

+ 2 - 0
src/views/book/courseware/data/matching.js

@@ -3,6 +3,7 @@ import {
   displayList,
   serialNumberPositionList,
   pinyinPositionList,
+  commonComponentProperty,
 } from '@/views/book/courseware/data/common';
 import { getRandomNumber } from '@/utils';
 
@@ -26,6 +27,7 @@ export function getMatchingProperty(column_num = 2) {
     view_pinyin: 'false', // 显示拼音
     pinyin_position: pinyinPositionList[0].value,
     is_first_sentence_first_hz_pinyin_first_char_upper_case: displayList[0].value, // 句首大写
+    ...commonComponentProperty,
   };
 }
 

+ 2 - 0
src/views/book/courseware/data/newWord.js

@@ -5,6 +5,7 @@ import {
   arrangeTypeList,
   switchOption,
   isEnable,
+  commonComponentProperty,
 } from '@/views/book/courseware/data/common';
 
 export { arrangeTypeList, switchOption, isEnable };
@@ -74,6 +75,7 @@ export function getNewWordProperty() {
     is_has_infor: inforList[0].value,
     voice_type: '', // 音色
     emotion: '', // 风格,情感
+    ...commonComponentProperty,
   };
 }
 

+ 3 - 0
src/views/book/courseware/data/newWordTemplate.js

@@ -5,6 +5,7 @@ import {
   arrangeTypeList,
   switchOption,
   isEnable,
+  commonComponentProperty,
 } from '@/views/book/courseware/data/common';
 import { getRandomNumber } from '@/utils';
 
@@ -96,6 +97,8 @@ export function getNewWordTemplateProperty() {
 
     // input模式
     is_enable_shiyi: showList[0].value,
+
+    ...commonComponentProperty,
   };
 }
 

+ 2 - 0
src/views/book/courseware/data/notes.js

@@ -5,6 +5,7 @@ import {
   arrangeTypeList,
   switchOption,
   isEnable,
+  commonComponentProperty,
 } from '@/views/book/courseware/data/common';
 
 export { arrangeTypeList, switchOption, isEnable };
@@ -42,6 +43,7 @@ export function getNotesProperty() {
     view_pinyin: 'false', // 显示拼音
     pinyin_position: pinyinPositionLists[1].value, // top bottom
     is_first_sentence_first_hz_pinyin_first_char_upper_case: 'true', // 句首大写
+    ...commonComponentProperty,
   };
 }
 

+ 3 - 0
src/views/book/courseware/data/pinyinBase.js

@@ -4,6 +4,7 @@ import {
   serialNumberPositionList,
   arrangeTypeList,
   switchOption,
+  commonComponentProperty,
 } from '@/views/book/courseware/data/common';
 import { getRandomNumber } from '@/utils';
 
@@ -75,6 +76,8 @@ export function getPinyinBaseProperty() {
 
     voice_type: '', // 音色
     emotion: '', // 风格,情感
+
+    ...commonComponentProperty,
   };
 }
 

+ 2 - 0
src/views/book/courseware/data/recordInput.js

@@ -3,6 +3,7 @@ import {
   serialNumberTypeList,
   serialNumberPositionList,
   switchOption,
+  commonComponentProperty,
 } from '@/views/book/courseware/data/common';
 
 export { switchOption };
@@ -21,6 +22,7 @@ export function getRecordInputProperty() {
     sn_type: serialNumberTypeList[0].value,
     sn_position: serialNumberPositionList[3].value,
     sn_display_mode: displayList[1].value,
+    ...commonComponentProperty,
   };
 }
 

+ 2 - 0
src/views/book/courseware/data/richText.js

@@ -5,6 +5,7 @@ import {
   serialNumberPositionList,
   pinyinPositionList,
   pinyinOverallPositionList,
+  commonComponentProperty,
 } from '@/views/book/courseware/data/common';
 
 // 按用途分类的数学公式宏列表
@@ -462,6 +463,7 @@ export function getRichTextProperty() {
     pinyin_position: pinyinPositionList[0].value, // top bottom
     pinyin_overall_position: pinyinOverallPositionList[0].value, // left center right
     is_first_sentence_first_hz_pinyin_first_char_upper_case: 'true', // 句首大写
+    ...commonComponentProperty,
   };
 }
 

+ 2 - 0
src/views/book/courseware/data/select.js

@@ -4,6 +4,7 @@ import {
   serialNumberPositionList,
   arrangeTypeList,
   pinyinPositionList,
+  commonComponentProperty,
 } from '@/views/book/courseware/data/common';
 import { getRandomNumber } from '@/utils';
 
@@ -29,6 +30,7 @@ export function getSelectProperty() {
     view_pinyin: 'false', // 显示拼音
     pinyin_position: pinyinPositionList[0].value,
     is_first_sentence_first_hz_pinyin_first_char_upper_case: displayList[0].value, // 句首大写
+    ...commonComponentProperty,
   };
 }
 

+ 2 - 0
src/views/book/courseware/data/sort.js

@@ -4,6 +4,7 @@ import {
   serialNumberPositionList,
   displayList,
   pinyinPositionList,
+  commonComponentProperty,
 } from '@/views/book/courseware/data/common';
 import { getRandomNumber } from '@/utils';
 export { arrangeTypeList };
@@ -40,6 +41,7 @@ export function getSortProperty() {
     view_pinyin: 'false', // 显示拼音
     pinyin_position: pinyinPositionList[0].value,
     is_first_sentence_first_hz_pinyin_first_char_upper_case: displayList[0].value, // 句首大写
+    ...commonComponentProperty,
   };
 }
 

+ 3 - 0
src/views/book/courseware/data/table.js

@@ -4,6 +4,7 @@ import {
   serialNumberPositionList,
   switchOption,
   pinyinPositionList,
+  commonComponentProperty,
 } from '@/views/book/courseware/data/common';
 import { getRandomNumber } from '@/utils';
 export { switchOption };
@@ -51,6 +52,8 @@ export function getTableProperty() {
     view_pinyin: 'false', // 显示拼音
     pinyin_position: pinyinPositionList[0].value, // top bottom
     is_first_sentence_first_hz_pinyin_first_char_upper_case: 'true', // 句首大写
+
+    ...commonComponentProperty,
   };
 }
 

+ 2 - 0
src/views/book/courseware/data/video.js

@@ -3,6 +3,7 @@ import {
   viewMethodList,
   serialNumberTypeList,
   serialNumberPositionList,
+  commonComponentProperty,
 } from '@/views/book/courseware/data/common';
 
 export function getVideoProperty() {
@@ -12,6 +13,7 @@ export function getVideoProperty() {
     sn_position: serialNumberPositionList[3].value, // 序号位置:top-start top top-end,left-start left left-end等
     sn_display_mode: displayList[1].value,
     view_method: viewMethodList[0].value, // 查看方式:independent独立 list列表
+    ...commonComponentProperty,
   };
 }
 

+ 2 - 0
src/views/book/courseware/data/videoInteraction.js

@@ -3,6 +3,7 @@ import {
   serialNumberTypeList,
   serialNumberPositionList,
   isEnable,
+  commonComponentProperty,
 } from '@/views/book/courseware/data/common';
 
 export { isEnable };
@@ -13,6 +14,7 @@ export function getVideoInteractionProperty() {
     sn_type: serialNumberTypeList[0].value,
     sn_position: serialNumberPositionList[3].value,
     sn_display_mode: displayList[1].value,
+    ...commonComponentProperty,
   };
 }
 

+ 2 - 0
src/views/book/courseware/data/voiceMatrix.js

@@ -3,6 +3,7 @@ import {
   serialNumberTypeList,
   serialNumberPositionList,
   switchOption,
+  commonComponentProperty,
 } from '@/views/book/courseware/data/common';
 import { getRandomNumber } from '@/utils';
 export { switchOption };
@@ -26,6 +27,7 @@ export function getVoiceMatrixProperty() {
     is_enable_record: switchOption[0].value, // 是否开启录音
     row_count: 2,
     column_count: 5,
+    ...commonComponentProperty,
   };
 }
 

+ 1 - 1
src/views/book/courseware/preview/components/article/index.vue

@@ -1,6 +1,6 @@
 <!-- eslint-disable vue/no-v-html -->
 <template>
-  <div class="article-preview" :style="getAreaStyle()">
+  <div class="article-preview" :style="[getAreaStyle(), getComponentStyle()]">
     <SerialNumberPosition v-if="isEnable(data.property.sn_display_mode)" :property="data.property" />
 
     <div class="main">

+ 2 - 2
src/views/book/courseware/preview/components/audio/AudioPreview.vue

@@ -1,5 +1,5 @@
 <template>
-  <div ref="audioArea" class="audio-preview" :style="getAreaStyle()">
+  <div ref="audioArea" class="audio-preview" :style="[getAreaStyle(), getComponentStyle()]">
     <SerialNumberPosition v-if="isEnable(data.property.sn_display_mode)" :property="data.property" />
 
     <div ref="audioAreaBox" class="main">
@@ -188,7 +188,7 @@ export default {
       // 切换到对应索引的文件
       carousel.setActiveItem(index);
       this.curAudioIndex = index;
-      if (this.$refs[id]&&this.$refs[id][0]) this.$refs[id][0].changeAudio(id);
+      if (this.$refs[id] && this.$refs[id][0]) this.$refs[id][0].changeAudio(id);
     },
     changeFile(type) {
       // 获取 Carousel 实例

+ 1 - 1
src/views/book/courseware/preview/components/character/CharacterPreview.vue

@@ -1,6 +1,6 @@
 <!-- eslint-disable vue/no-v-html -->
 <template>
-  <div class="character-preview" :style="getAreaStyle()">
+  <div class="character-preview" :style="[getAreaStyle(), getComponentStyle()]">
     <SerialNumberPosition v-if="isEnable(data.property.sn_display_mode)" :property="data.property" />
 
     <div class="main">

+ 1 - 1
src/views/book/courseware/preview/components/character_structure/CharacterStructurePreview.vue

@@ -1,6 +1,6 @@
 <!-- eslint-disable vue/no-v-html -->
 <template>
-  <div class="structure-preview" :style="getAreaStyle()">
+  <div class="structure-preview" :style="[getAreaStyle(), getComponentStyle()]">
     <SerialNumberPosition v-if="isEnable(data.property.sn_display_mode)" :property="data.property" />
 
     <div class="main">

+ 24 - 0
src/views/book/courseware/preview/components/common/PreviewMixin.js

@@ -145,6 +145,30 @@ const mixin = {
         grid,
       };
     },
+    /**
+     * 得到背景图、背景色及边框样式
+     */
+    getComponentStyle() {
+      if (!Object.hasOwn(this.data.property, 'background_image_url')) return {};
+
+      // 保护性读取位置/大小值,避免 undefined 导致字符串 "undefined%"
+      const { background_position: pos } = this.data.property;
+      const widthPct = typeof pos.width === 'undefined' ? '' : pos.width;
+      const heightPct = typeof pos.height === 'undefined' ? '' : pos.height;
+      const leftPct = typeof pos.left === 'undefined' ? '' : pos.left;
+      const topPct = typeof pos.top === 'undefined' ? '' : pos.top;
+
+      let style = {
+        backgroundColor: this.data.property.background_color,
+        backgroundImage: `url(${this.data.property.background_image_url})`,
+        backgroundSize: `${widthPct}% ${heightPct}%`,
+        backgroundPosition: `${leftPct}% ${topPct}%`,
+        borderWidth: isEnable(this.data.property.is_border) ? '1px' : '0px',
+        borderStyle: isEnable(this.data.property.is_border) ? this.data.property.border_style : 'none',
+        borderColor: isEnable(this.data.property.is_border) ? this.data.property.border_color : 'transparent',
+      };
+      return style;
+    },
   },
 };
 

+ 1 - 1
src/views/book/courseware/preview/components/dialogue_article/index.vue

@@ -1,6 +1,6 @@
 <!-- eslint-disable vue/no-v-html -->
 <template>
-  <div class="article-preview" :style="getAreaStyle()">
+  <div class="article-preview" :style="[getAreaStyle(), getComponentStyle()]">
     <SerialNumberPosition v-if="isEnable(data.property.sn_display_mode)" :property="data.property" />
 
     <div class="main">

+ 1 - 1
src/views/book/courseware/preview/components/drawing/DrawingPreview.vue

@@ -1,6 +1,6 @@
 <!-- eslint-disable vue/no-v-html -->
 <template>
-  <div class="drawimg-preview" :style="getAreaStyle()">
+  <div class="drawimg-preview" :style="[getAreaStyle(), getComponentStyle()]">
     <SerialNumberPosition v-if="isEnable(data.property.sn_display_mode)" :property="data.property" />
 
     <div

+ 1 - 1
src/views/book/courseware/preview/components/fill/FillPreview.vue

@@ -1,6 +1,6 @@
 <!-- eslint-disable vue/no-v-html -->
 <template>
-  <div class="select-preview" :style="getAreaStyle()">
+  <div class="select-preview" :style="[getAreaStyle(), getComponentStyle()]">
     <SerialNumberPosition v-if="isEnable(data.property.sn_display_mode)" :property="data.property" />
 
     <div class="main" :style="getMainStyle()">

+ 1 - 1
src/views/book/courseware/preview/components/image_text/ImageTextPreview.vue

@@ -1,6 +1,6 @@
 <!-- eslint-disable vue/no-v-html -->
 <template>
-  <div class="imageText-preview" :style="getAreaStyle()">
+  <div class="imageText-preview" :style="[getAreaStyle(), getComponentStyle()]">
     <SerialNumberPosition v-if="isEnable(data.property.sn_display_mode)" :property="data.property" />
     <template v-if="data.mp3_list && data.mp3_list.length > 0 && mp3_url">
       <AudioLine

+ 1 - 1
src/views/book/courseware/preview/components/judge/JudgePreview.vue

@@ -1,6 +1,6 @@
 <!-- eslint-disable vue/no-v-html -->
 <template>
-  <div class="judge-preview" :style="getAreaStyle()">
+  <div class="judge-preview" :style="[getAreaStyle(), getComponentStyle()]">
     <SerialNumberPosition v-if="isEnable(data.property.sn_display_mode)" :property="data.property" />
     <div class="main">
       <ul class="option-list">

+ 1 - 1
src/views/book/courseware/preview/components/matching/MatchingPreview.vue

@@ -1,6 +1,6 @@
 <!-- eslint-disable vue/no-v-html -->
 <template>
-  <div class="matching-preview" :style="getAreaStyle()">
+  <div class="matching-preview" :style="[getAreaStyle(), getComponentStyle()]">
     <SerialNumberPosition v-if="isEnable(data.property.sn_display_mode)" :property="data.property" />
 
     <div class="main">

+ 1 - 1
src/views/book/courseware/preview/components/newWord_template/NewWordTemplatePreview.vue

@@ -1,6 +1,6 @@
 <!-- eslint-disable vue/no-v-html -->
 <template>
-  <div class="newWord-template-preview" :style="getAreaStyle()">
+  <div class="newWord-template-preview" :style="[getAreaStyle(), getComponentStyle()]">
     <SerialNumberPosition v-if="isEnable(data.property.sn_display_mode)" :property="data.property" />
     <div class="main">
       <div

+ 1 - 1
src/views/book/courseware/preview/components/new_word/NewWordPreview.vue

@@ -1,6 +1,6 @@
 <!-- eslint-disable vue/no-v-html -->
 <template>
-  <div ref="previewContainer" class="newWord-preview" :style="getAreaStyle()">
+  <div ref="previewContainer" class="newWord-preview" :style="[getAreaStyle(), getComponentStyle()]">
     <SerialNumberPosition
       v-if="isEnable(data.property.hasOwnProperty('sn_display_mode') ? data.property.sn_display_mode : 'false')"
       :property="data.property"

+ 1 - 1
src/views/book/courseware/preview/components/notes/NotesPreview.vue

@@ -1,6 +1,6 @@
 <!-- eslint-disable vue/no-v-html -->
 <template>
-  <div class="notes-preview" :style="getAreaStyle()">
+  <div class="notes-preview" :style="[getAreaStyle(), getComponentStyle()]">
     <SerialNumberPosition
       v-if="isEnable(data.property.hasOwnProperty('sn_display_mode') ? data.property.sn_display_mode : 'false')"
       :property="data.property"

+ 1 - 1
src/views/book/courseware/preview/components/pinyin_base/PinyinBasePreview.vue

@@ -1,6 +1,6 @@
 <!-- eslint-disable vue/no-v-html -->
 <template>
-  <div class="pinyin-preview" :style="getAreaStyle()">
+  <div class="pinyin-preview" :style="[getAreaStyle(), getComponentStyle()]">
     <SerialNumberPosition v-if="isEnable(data.property.sn_display_mode)" :property="data.property" />
 
     <div class="main">

+ 1 - 1
src/views/book/courseware/preview/components/record_input/RecordInputPreview.vue

@@ -1,6 +1,6 @@
 <!-- eslint-disable vue/no-v-html -->
 <template>
-  <div class="select-preview" :style="getAreaStyle()">
+  <div class="select-preview" :style="[getAreaStyle(), getComponentStyle()]">
     <SerialNumberPosition v-if="isEnable(data.property.sn_display_mode)" :property="data.property" />
 
     <div class="main">

+ 1 - 1
src/views/book/courseware/preview/components/rich_text/RichTextPreview.vue

@@ -1,5 +1,5 @@
 <template>
-  <div :class="['describe-preview']" :style="getAreaStyle()">
+  <div :class="['describe-preview']" :style="[getAreaStyle(), getComponentStyle()]">
     <SerialNumberPosition v-if="isEnable(data.property.sn_display_mode)" :property="data.property" />
 
     <div class="main">

+ 1 - 1
src/views/book/courseware/preview/components/select/SelectPreview.vue

@@ -1,6 +1,6 @@
 <!-- eslint-disable vue/no-v-html -->
 <template>
-  <div class="select-preview" :style="getAreaStyle()">
+  <div class="select-preview" :style="[getAreaStyle(), getComponentStyle()]">
     <SerialNumberPosition v-if="isEnable(data.property.sn_display_mode)" :property="data.property" />
 
     <div class="main">

+ 1 - 1
src/views/book/courseware/preview/components/sort/SortPreview.vue

@@ -1,6 +1,6 @@
 <!-- eslint-disable vue/no-v-html -->
 <template>
-  <div class="select-preview" :style="getAreaStyle()">
+  <div class="select-preview" :style="[getAreaStyle(), getComponentStyle()]">
     <SerialNumberPosition v-if="isEnable(data.property.sn_display_mode)" :property="data.property" />
 
     <div class="main">

+ 1 - 1
src/views/book/courseware/preview/components/table/TablePreview.vue

@@ -1,6 +1,6 @@
 <!-- eslint-disable vue/no-v-html -->
 <template>
-  <div class="table-preview" :style="getAreaStyle()">
+  <div class="table-preview" :style="[getAreaStyle(), getComponentStyle()]">
     <SerialNumberPosition v-if="isEnable(data.property.sn_display_mode)" :property="data.property" />
 
     <div class="main">

+ 1 - 1
src/views/book/courseware/preview/components/video/VideoPreview.vue

@@ -1,5 +1,5 @@
 <template>
-  <div ref="videoArea" class="video-preview" :style="getAreaStyle()">
+  <div ref="videoArea" class="video-preview" :style="[getAreaStyle(), getComponentStyle()]">
     <SerialNumberPosition v-if="isEnable(data.property.sn_display_mode)" :property="data.property" />
     <div ref="videoAreaBox" class="main">
       <template v-if="isMore">

+ 1 - 1
src/views/book/courseware/preview/components/video_interaction/VideoInteractionPreview.vue

@@ -1,6 +1,6 @@
 <!-- eslint-disable vue/no-v-html -->
 <template>
-  <div class="imageText-preview" :style="getAreaStyle()">
+  <div class="imageText-preview" :style="[getAreaStyle(), getComponentStyle()]">
     <SerialNumberPosition v-if="isEnable(data.property.sn_display_mode)" :property="data.property" />
     <div v-if="data.video_list.length > 0" class="interaction-box">
       <video

+ 1 - 1
src/views/book/courseware/preview/components/voice_matrix/VoiceMatrixPreview.vue

@@ -1,6 +1,6 @@
 <!-- eslint-disable vue/no-v-html -->
 <template>
-  <div class="voice-matrix-preview" :style="getAreaStyle()">
+  <div class="voice-matrix-preview" :style="[getAreaStyle(), getComponentStyle()]">
     <SerialNumberPosition v-if="isEnable(data.property.sn_display_mode)" :property="data.property" />
 
     <div class="main">