Ver Fonte

修改预览组件列表

dusenyao há 11 meses atrás
pai
commit
315b4686a5

+ 1 - 0
src/main.js

@@ -7,6 +7,7 @@ import store from './store';
 import '@/icons';
 import '@/styles/index.scss';
 import '@/utils/filter';
+import '@/utils/directive';
 
 import ElementUI from 'element-ui';
 import '@/styles/element-variables.scss';

+ 13 - 0
src/utils/directive.js

@@ -0,0 +1,13 @@
+import Vue from 'vue';
+
+// 输入的只能是数字
+Vue.directive('numericOnly', {
+  bind(el) {
+    // 找到 el 下的 input 标签
+    const input = el.tagName === 'INPUT' ? el : el.querySelector('input');
+    input.addEventListener('input', (e) => {
+      // 如果输入的不是数字,则替换为空
+      e.target.value = e.target.value.replace(/[^\d]/g, '');
+    });
+  },
+});

+ 3 - 3
src/views/book/courseware/create/components/PreviewEdit.vue

@@ -25,7 +25,7 @@
                 ></span>
               </template>
               <component
-                :is="previewComponentMap[grid.type]"
+                :is="previewComponentList[grid.type]"
                 :id="grid.id"
                 ref="preview"
                 :key="k"
@@ -46,7 +46,7 @@
 </template>
 
 <script>
-import { previewComponentMap } from '@/views/book/courseware/preview/components/common/data';
+import { previewComponentList } from '@/views/book/courseware/data/bookType';
 
 export default {
   name: 'PreviewEdit',
@@ -62,7 +62,7 @@ export default {
   },
   data() {
     return {
-      previewComponentMap,
+      previewComponentList,
       drag: {
         dragging: false,
         i: -1,

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

@@ -3,12 +3,11 @@ import { snGenerationMethodList, checkString } from '@/views/book/courseware/dat
 
 import SerialNumberPosition from '@/views/book/courseware/create/components/common/SerialNumberPosition.vue';
 import SelectSerialNumberStyle from '@/views/book/courseware/create/components/base/common/SelectSerialNumberStyle.vue';
-import tinymce from 'tinymce/tinymce';
 
 const mixin = {
   data() {
     return {
-      param: {},
+      param: {}, // 组件设置时传过来的特有参数
       switchSerialNumber,
       computedQuestionNumber,
       snGenerationMethodList,

+ 150 - 4
src/views/book/courseware/create/components/question/sort/Sort.vue

@@ -1,13 +1,45 @@
 <template>
   <ModuleBase :type="data.type">
-    <template #content> </template>
+    <template #content>
+      <div class="sort-wrapper" :style="getSortWrapperStyle()">
+        <template v-for="(item, i) in data.option_list">
+          <div
+            :key="`rich-${item.mark}`"
+            class="sort-rich"
+            :style="{
+              gridArea: `rich-${i}`,
+              ...getRichStyle(i),
+            }"
+          >
+            <RichText v-model="item.content" :inline="true" placeholder="" />
+          </div>
+          <div
+            :key="item.mark"
+            class="custom-serial-number"
+            :style="{
+              gridArea: `number-${i}`,
+              ...getNumberStyle(i),
+            }"
+          >
+            <el-input
+              v-model="item.custom_serial_number"
+              v-numeric-only
+              :class="{
+                'text-center': data.property.arrange_direction === arrangeTypeList[0].value,
+              }"
+            />
+          </div>
+        </template>
+      </div>
+      <div class="tips">{{ tips }}</div>
+    </template>
   </ModuleBase>
 </template>
 
 <script>
 import ModuleMixin from '../../common/ModuleMixin';
 
-import { arrangeTypeList, getSortData } from '@/views/book/courseware/data/sort';
+import { arrangeTypeList, getSortData, getOption } from '@/views/book/courseware/data/sort';
 
 export default {
   name: 'SortPage',
@@ -18,8 +50,122 @@ export default {
       arrangeTypeList,
     };
   },
-  methods: {},
+  computed: {
+    tips() {
+      return this.data.property.arrange_direction === arrangeTypeList[0].value
+        ? '在下列输入顺序编号'
+        : '第一列中输入顺序编号,第二列输入对应的内容';
+    },
+  },
+  watch: {
+    'data.property.option_count': {
+      handler(val) {
+        if (val > this.data.option_list.length) {
+          for (let i = this.data.option_list.length; i < val; i++) {
+            this.data.option_list.push(getOption());
+          }
+          return;
+        }
+        if (val < this.data.option_list.length) {
+          this.data.option_list = this.data.option_list.slice(0, val);
+        }
+      },
+    },
+  },
+  methods: {
+    getSortWrapperStyle() {
+      let gridTemplateAreas = '';
+      let gridTemplateColumns = '';
+      let gridTemplateRows = '';
+
+      if (this.data.property.arrange_direction === arrangeTypeList[0].value) {
+        let rich = '';
+        let number = '';
+        for (let i = 0; i < this.data.option_list.length; i++) {
+          rich += `rich-${i} `;
+          number += `number-${i} `;
+        }
+        gridTemplateAreas = `'${rich.trim()}' '${number.trim()}'`;
+        gridTemplateColumns = `repeat(${this.data.option_list.length}, 1fr)`;
+        gridTemplateRows = '1fr 42px';
+      } else {
+        gridTemplateAreas = this.data.option_list.map((_, i) => `'number-${i} rich-${i}'`).join(' ');
+        gridTemplateColumns = '48px 1fr';
+      }
+
+      return {
+        gridTemplateAreas,
+        gridTemplateColumns,
+        gridTemplateRows,
+      };
+    },
+    getRichStyle(i) {
+      let isVertical = this.data.property.arrange_direction === arrangeTypeList[1].value; // 是否竖排
+      let isLast = i !== this.data.option_list.length - 1; // 是否最后一个
+
+      let borderBottom = !isVertical || (isVertical && isLast) ? 'none' : '1px solid #ccc';
+      let borderRight = !isVertical && isLast ? 'none' : '1px solid #ccc';
+
+      return {
+        borderBottom,
+        borderRight,
+      };
+    },
+    getNumberStyle(i) {
+      let isVertical = this.data.property.arrange_direction === arrangeTypeList[1].value; // 是否竖排
+      let isLast = i !== this.data.option_list.length - 1; // 是否最后一个
+
+      let borderRight = (!isVertical && isLast) || isVertical ? 'none' : '1px solid #ccc';
+      let borderBottom = isVertical && isLast ? 'none' : '1px solid #ccc';
+
+      return {
+        borderRight,
+        borderBottom,
+      };
+    },
+  },
 };
 </script>
 
-<style lang="scss" scoped></style>
+<style lang="scss" scoped>
+@use '@/styles/mixin.scss' as *;
+
+.sort-wrapper {
+  display: grid;
+
+  .sort-rich {
+    padding: 8px;
+    border: 1px solid #ccc;
+
+    :deep p {
+      margin: 0;
+    }
+  }
+
+  .custom-serial-number {
+    padding: 4px 0;
+    border: 1px solid #ccc;
+
+    .el-input {
+      :deep &__inner {
+        padding: 0 8px;
+        background-color: #fff;
+        border-width: 0;
+      }
+
+      &.text-center {
+        :deep .el-input__inner {
+          text-align: center;
+        }
+      }
+    }
+  }
+}
+
+.tips {
+  margin-top: 16px;
+  font-size: 12px;
+  line-height: 1.5;
+  color: #999;
+}
+</style>

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

@@ -26,7 +26,7 @@
         <el-radio
           v-for="{ value, label } in arrangeTypeList"
           :key="value"
-          v-model="property.arrange_type"
+          v-model="property.arrange_direction"
           :label="value"
         >
           {{ label }}

+ 37 - 0
src/views/book/courseware/data/bookType.js

@@ -18,6 +18,20 @@ import LabelPage from '../create/components/base/label/Label.vue';
 import LabelSetting from '../create/components/base/label/LabelSetting.vue';
 import MatchingPage from '../create/components/question/matching/Matching.vue';
 import MatchingSetting from '../create/components/question/matching/MatchingSetting.vue';
+import SortPage from '../create/components/question/sort/Sort.vue';
+import SortSetting from '../create/components/question/sort/SortSetting.vue';
+
+import AudioPreview from '@/views/book/courseware/preview/components/audio/AudioPreview.vue';
+import DividerPreview from '@/views/book/courseware/preview/components/divider/DividerPreview.vue';
+import SpacingPreview from '@/views/book/courseware/preview/components/spacing/SpacingPreview.vue';
+import PicturePreview from '@/views/book/courseware/preview/components/picture/PicturePreview.vue';
+import VideoPreview from '@/views/book/courseware/preview/components/video/VideoPreview.vue';
+import StemPreview from '@/views/book/courseware/preview/components/stem/StemPreview.vue';
+import DescribePreview from '@/views/book/courseware/preview/components/describe/DescribePreview.vue';
+import LabelPreview from '@/views/book/courseware/preview/components/label/LabelPreview.vue';
+import SelectPreview from '@/views/book/courseware/preview/components/select/SelectPreview.vue';
+import MatchingPreview from '@/views/book/courseware/preview/components/matching/MatchingPreview.vue';
+import SortPreview from '@/views/book/courseware/preview/components/sort/SortPreview.vue';
 
 export const bookTypeOption = [
   {
@@ -31,6 +45,7 @@ export const bookTypeOption = [
         component: StemPage,
         // 设置页面
         set: StemSetting,
+        preview: StemPreview, // 预览页面
       },
       {
         value: 'describe',
@@ -38,6 +53,7 @@ export const bookTypeOption = [
         icon: 'describe',
         component: DescribePage,
         set: DescribeSetting,
+        preview: DescribePreview,
       },
       {
         value: 'label',
@@ -45,6 +61,7 @@ export const bookTypeOption = [
         icon: 'label',
         component: LabelPage,
         set: LabelSetting,
+        preview: LabelPreview,
       },
       {
         value: 'audio',
@@ -52,6 +69,7 @@ export const bookTypeOption = [
         icon: 'audio',
         component: AudioPage,
         set: AudioSetting,
+        preview: AudioPreview,
       },
       {
         value: 'video',
@@ -59,6 +77,7 @@ export const bookTypeOption = [
         icon: 'video',
         component: VideoPage,
         set: VideoSetting,
+        preview: VideoPreview,
       },
       {
         value: 'picture',
@@ -66,6 +85,7 @@ export const bookTypeOption = [
         icon: 'picture',
         component: PicturePage,
         set: PictureSetting,
+        preview: PicturePreview,
       },
       {
         value: 'divider',
@@ -73,6 +93,7 @@ export const bookTypeOption = [
         icon: 'divider',
         component: DividerPage,
         set: DividerSetting,
+        preview: DividerPreview,
       },
       {
         value: 'spacing',
@@ -80,6 +101,7 @@ export const bookTypeOption = [
         icon: 'spacing',
         component: SpacingPage,
         set: SpacingSetting,
+        preview: SpacingPreview,
       },
     ],
   },
@@ -93,6 +115,7 @@ export const bookTypeOption = [
         icon: '',
         component: SelectPage,
         set: SelectSetting,
+        preview: SelectPreview,
       },
       {
         value: 'matching',
@@ -100,6 +123,15 @@ export const bookTypeOption = [
         icon: '',
         component: MatchingPage,
         set: MatchingSetting,
+        preview: MatchingPreview,
+      },
+      {
+        value: 'sort',
+        label: '排序组件',
+        icon: '',
+        component: SortPage,
+        set: SortSetting,
+        preview: SortPreview,
       },
     ],
   },
@@ -119,3 +151,8 @@ export const componentSettingList = bookTypeOption
 export const componentNameList = bookTypeOption
   .flatMap((item) => item.children)
   .reduce((prev, { value, label }) => ({ ...prev, [value]: label }), {});
+
+// 预览组件列表
+export const previewComponentList = bookTypeOption
+  .flatMap((item) => item.children)
+  .reduce((prev, { value, preview }) => ({ ...prev, [value]: preview }), {});

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

@@ -4,15 +4,24 @@ import {
   serialNumberPositionList,
   snGenerationMethodList,
 } from '@/views/book/courseware/data/common';
-
+import { getRandomNumber } from '@/utils';
 export { arrangeTypeList };
 
+export function getOption() {
+  return {
+    mark: getRandomNumber(),
+    content: '',
+    custom_serial_number: '', // 自定义序号
+  };
+}
+
 export function getSortProperty() {
   return {
     serial_number: 1,
     sn_type: serialNumberTypeList[0].value,
     sn_position: serialNumberPositionList[0].value,
     sn_generation_method: snGenerationMethodList[0].value,
+    option_count: 3,
     arrange_direction: arrangeTypeList[0].value,
   };
 }
@@ -21,7 +30,7 @@ export function getSortData() {
   return {
     type: 'sort',
     title: '排序',
-    option_list: [],
+    option_list: [getOption(), getOption(), getOption()],
     property: getSortProperty(),
   };
 }

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

@@ -21,7 +21,7 @@
             <!-- 网格 -->
             <template v-for="(grid, k) in col.grid_list">
               <component
-                :is="previewComponentMap[grid.type]"
+                :is="previewComponentList[grid.type]"
                 :id="grid.id"
                 ref="preview"
                 :key="k"
@@ -41,7 +41,7 @@
 </template>
 
 <script>
-import { previewComponentMap } from './components/common/data';
+import { previewComponentList } from '@/views/book/courseware/data/bookType';
 
 export default {
   name: 'CoursewarePreview',
@@ -53,7 +53,7 @@ export default {
   },
   data() {
     return {
-      previewComponentMap,
+      previewComponentList,
     };
   },
   methods: {

+ 0 - 23
src/views/book/courseware/preview/components/common/data.js

@@ -1,23 +0,0 @@
-import AudioPreview from '../audio/AudioPreview.vue';
-import DividerPreview from '../divider/DividerPreview.vue';
-import SpacingPreview from '../spacing/SpacingPreview.vue';
-import PicturePreview from '../picture/PicturePreview.vue';
-import VideoPreview from '../video/VideoPreview.vue';
-import StemPreview from '../stem/StemPreview.vue';
-import DescribePreview from '../describe/DescribePreview.vue';
-import LabelPreview from '../label/LabelPreview.vue';
-import SelectPreview from '../select/SelectPreview.vue';
-import MatchingPreview from '../matching/MatchingPreview.vue';
-
-export const previewComponentMap = {
-  audio: AudioPreview,
-  divider: DividerPreview,
-  spacing: SpacingPreview,
-  picture: PicturePreview,
-  video: VideoPreview,
-  stem: StemPreview,
-  describe: DescribePreview,
-  label: LabelPreview,
-  select: SelectPreview,
-  matching: MatchingPreview,
-};

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

@@ -0,0 +1,33 @@
+<template>
+  <div class="select-preview" :style="getAreaStyle()">
+    <SerialNumberPosition :property="data.property" />
+
+    <div class="main"></div>
+  </div>
+</template>
+
+<script>
+import { getSortData, arrangeTypeList } from '@/views/book/courseware/data/sort';
+
+import PreviewMixin from '../common/PreviewMixin';
+
+export default {
+  name: 'SortPreview',
+  mixins: [PreviewMixin],
+  data() {
+    return {
+      data: getSortData(),
+      arrangeTypeList,
+    };
+  },
+  methods: {},
+};
+</script>
+
+<style lang="scss" scoped>
+@use '@/styles/mixin.scss' as *;
+
+.select-preview {
+  @include preview-base;
+}
+</style>