Browse Source

简易框架

dusenyao 1 year ago
parent
commit
b15ec0acd6

+ 12 - 0
src/icons/svg/components/describe.svg

@@ -0,0 +1,12 @@
+<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
+  <g clip-path="url(#clip0_271_12357)">
+    <path
+      d="M2.66634 2.66797V7.33464H7.33301V2.66797H8.66634V13.3346H7.33301V8.66797H2.66634V13.3346H1.33301V2.66797H2.66634ZM12.333 5.33464C13.7137 5.33464 14.833 6.45392 14.833 7.83464C14.833 8.40624 14.6412 8.93303 14.3184 9.3541L14.2198 9.47477L12.0225 12.0013H14.6663V13.3346H9.99967L9.99921 12.2973L13.2135 8.60004C13.3917 8.39517 13.4997 8.1275 13.4997 7.83464C13.4997 7.1903 12.9773 6.66797 12.333 6.66797C11.7209 6.66797 11.2189 7.13937 11.1702 7.73897L11.1663 7.83464H9.83301C9.83301 6.45392 10.9523 5.33464 12.333 5.33464Z"
+      fill="black" />
+  </g>
+  <defs>
+    <clipPath id="clip0_271_12357">
+      <rect width="16" height="16" fill="white" />
+    </clipPath>
+  </defs>
+</svg>

+ 12 - 0
src/icons/svg/components/divider.svg

@@ -0,0 +1,12 @@
+<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
+  <g clip-path="url(#clip0_271_12372)">
+    <path
+      d="M1.33301 7.33203H2.66634V8.66536H1.33301V7.33203ZM3.99967 7.33203H11.9997V8.66536H3.99967V7.33203ZM13.333 7.33203H14.6663V8.66536H13.333V7.33203Z"
+      fill="black" />
+  </g>
+  <defs>
+    <clipPath id="clip0_271_12372">
+      <rect width="16" height="16" fill="white" />
+    </clipPath>
+  </defs>
+</svg>

+ 12 - 0
src/icons/svg/components/stem.svg

@@ -0,0 +1,12 @@
+<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
+  <g clip-path="url(#clip0_271_12354)">
+    <path
+      d="M8.66634 13.3346H7.33301V8.66797H2.66634V13.3346H1.33301V2.66797H2.66634V7.33464H7.33301V2.66797H8.66634V13.3346ZM14 5.33464V13.3346H12.6667L12.6663 6.80397L11.333 7.1613V5.7813L13 5.33464H14Z"
+      fill="black" />
+  </g>
+  <defs>
+    <clipPath id="clip0_271_12354">
+      <rect width="16" height="16" fill="white" />
+    </clipPath>
+  </defs>
+</svg>

+ 13 - 0
src/router/modules/book.js

@@ -0,0 +1,13 @@
+import DEFAULT from '@/layouts/default';
+
+export const createBookPage = {
+  path: '/book',
+  component: DEFAULT,
+  redirect: '/book/create',
+  children: [
+    {
+      path: 'create',
+      component: () => import('@/views/book/create.vue'),
+    },
+  ],
+};

+ 1 - 1
src/router/modules/courseware.js

@@ -10,7 +10,7 @@ const CoursewareModulePage = {
   children: [
     {
       path: 'create',
-      component: () => import('@/views/courseware/create/index.vue'),
+      component: () => import('@/views/book/courseware/create/index.vue'),
     },
   ],
 };

+ 2 - 2
src/router/modules/index.js

@@ -1,5 +1,5 @@
 import { homePage, loginPage, NotFoundPage } from './basic';
-import { createTextbookPage } from './textbook';
+import { createBookPage } from './book';
 import CoursewareRouters from './courseware';
 
-export const routes = [homePage, loginPage, createTextbookPage, ...CoursewareRouters, NotFoundPage];
+export const routes = [homePage, loginPage, createBookPage, ...CoursewareRouters, NotFoundPage];

+ 0 - 13
src/router/modules/textbook.js

@@ -1,13 +0,0 @@
-import DEFAULT from '@/layouts/default';
-
-export const createTextbookPage = {
-  path: '/textbook',
-  component: DEFAULT,
-  redirect: '/textbook/create',
-  children: [
-    {
-      path: 'create',
-      component: () => import('@/views/textbook/create.vue'),
-    },
-  ],
-};

+ 73 - 0
src/utils/common.js

@@ -13,3 +13,76 @@ export function conversionSize(size) {
   }
   return `${_size.toFixed(2)}${units[factor]}`;
 }
+
+// 分割线
+const type = {
+  type: 'divider',
+  property: {
+    height: 40,
+    type: 'solid',
+  },
+};
+
+const data = {
+  id: '1',
+  background_image_url: 'https://file/1/1.jpg',
+  background_position: {
+    x: 0,
+    y: 0,
+    width: 100,
+    height: 100,
+  },
+  // 组件列表
+  component_list: [
+    // 第一行
+    [
+      // 第一行第一列
+      {
+        id: '1-1',
+        type: 'image',
+        width: '100%',
+        component_list: [],
+      },
+    ],
+    // 第二行
+    [
+      // 第二行第一列
+      {
+        id: '2-1',
+        type: 'video',
+        width: '30%',
+        component_list: [],
+      },
+      // 第二行第二列
+      {
+        id: '2-2',
+        type: 'audio',
+        width: '30%',
+        component_list: [],
+      },
+      // 第二行第三列
+      {
+        id: '',
+        type: '',
+        width: '40%',
+        // 组件列表
+        component_list: [
+          // 第二行第三列第一行
+          [
+            {
+              id: '3-1',
+              type: 'image',
+            },
+          ],
+          // 第二行第三列第二行
+          [
+            {
+              id: '3-2',
+              type: 'video',
+            },
+          ],
+        ],
+      },
+    ],
+  ],
+};

+ 1 - 1
src/views/textbook/create.vue → src/views/book/chapter.vue

@@ -4,7 +4,7 @@
 
 <script>
 export default {
-  name: 'CreateTextbookPage',
+  name: 'ChapterPage',
   data() {
     return {};
   },

+ 27 - 0
src/views/book/courseware/create/components/base/divider/Divider.vue

@@ -0,0 +1,27 @@
+<template>
+  <ModuleBase :type="data.type">
+    <template #content>
+      <el-divider />
+    </template>
+  </ModuleBase>
+</template>
+
+<script>
+import { DividerData } from '@/views/book/courseware/data/divider';
+
+import ModuleMixin from '../../common/ModuleMixin';
+
+export default {
+  name: 'Divider',
+  components: {},
+  mixins: [ModuleMixin],
+  data() {
+    return {
+      data: DividerData,
+    };
+  },
+  methods: {},
+};
+</script>
+
+<style lang="scss" scoped></style>

+ 52 - 0
src/views/book/courseware/create/components/base/divider/DividerSetting.vue

@@ -0,0 +1,52 @@
+<template>
+  <div>
+    <el-form :model="setting" :label-position="labelPosition" label-width="80px">
+      <el-form-item label="高度">
+        <el-input v-model="setting.height" />
+      </el-form-item>
+      <el-form-item label="类型">
+        <el-select v-model="setting.type">
+          <el-option label="实线" value="solid" />
+        </el-select>
+      </el-form-item>
+    </el-form>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'DividerSetting',
+  data() {
+    return {
+      labelPosition: 'right',
+      isSet: false, // 父组件是否已设置
+      setting: {
+        height: 100,
+        type: 'solid',
+      },
+    };
+  },
+  watch: {
+    setting: {
+      handler(val) {
+        if (this.isSet) {
+          this.$emit('updateSetting', val);
+        }
+      },
+      deep: true,
+    },
+  },
+  methods: {
+    /**
+     * @description 设置属性
+     * @param {Object} setting 属性
+     */
+    setSetting(setting) {
+      this.isSet = true;
+      this.setting = setting;
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped></style>

+ 14 - 13
src/views/courseware/create/components/common/ModuleBase.vue → src/views/book/courseware/create/components/common/ModuleBase.vue

@@ -1,7 +1,7 @@
 <template>
   <div class="module">
-    <div class="module-content">
-      <slot name="title"></slot>
+    <div class="module-top">
+      <span class="title">{{ componentNameList[type] }}</span>
       <div>
         <span><SvgIcon icon-class="copy" size="14" /></span>
         <span><SvgIcon icon-class="setup" size="14" /></span>
@@ -15,25 +15,20 @@
 </template>
 
 <script>
+import { componentNameList } from '@/views/book/courseware/data/bookType.js';
+
 export default {
   name: 'ModuleBase',
   props: {
-    title: {
+    type: {
       type: String,
-      default: '',
-    },
-    isChild: {
-      type: Boolean,
-      default: false,
-    },
-    isChange: {
-      type: Boolean,
-      default: false,
+      default: 'text',
     },
   },
   data() {
     return {
       isShow: false,
+      componentNameList,
     };
   },
 };
@@ -41,6 +36,12 @@ export default {
 
 <style lang="scss" scoped>
 .module {
-  border: 1px solid #EBEBEB;
+  padding: 8px;
+  border: 1px solid #272727;
+
+  &-top {
+    display: flex;
+    justify-content: space-between;
+  }
 }
 </style>

+ 37 - 0
src/views/book/courseware/create/components/common/ModuleMixin.js

@@ -0,0 +1,37 @@
+// 组件混入
+import ModuleBase from './ModuleBase.vue';
+
+const mixin = {
+  data() {
+    return {};
+  },
+  props: {
+    questionId: {
+      type: String,
+      default: '',
+    },
+  },
+  components: {
+    ModuleBase,
+  },
+  mounted() {
+    // TODO: 为了先显示设置页面,以后需要修改
+    this.$emit('showSetting', this.data.setting);
+  },
+  watch: {},
+  methods: {
+    // 显示设置
+    showSetting() {
+      this.$emit('showSetting', this.data.setting);
+    },
+    /**
+     * @description 更新属性
+     * @param {Object} setting 属性
+     */
+    updateSetting(setting) {
+      this.data.setting = setting;
+    },
+  },
+};
+
+export default mixin;

+ 0 - 0
src/views/courseware/create/components/common/ModuleProperty.vue → src/views/book/courseware/create/components/common/ModuleProperty.vue


+ 148 - 0
src/views/book/courseware/create/index.vue

@@ -0,0 +1,148 @@
+<template>
+  <div class="create">
+    <div class="create-left">
+      <div class="back-container">
+        <el-button class="back" @click="back"><i class="el-icon-arrow-left"></i> 返回</el-button>
+        <span class="title">Frame 1</span>
+      </div>
+      <div v-for="{ value, label, children } in bookTypeOption" :key="value" class="components">
+        <div class="components-title">{{ label }}</div>
+        <div
+          v-for="{ value: childValue, icon, label: childLabel } in children"
+          :key="childValue"
+          class="components-item"
+          @click="curType = childValue"
+        >
+          <SvgIcon v-if="icon" :icon-class="icon" />
+          <span>{{ childLabel }}</span>
+        </div>
+      </div>
+    </div>
+    <div class="create-middle">
+      <div></div>
+      <div class="canvas">
+        <component :is="componentList[curType]" ref="components" @showSetting="showSetting" />
+      </div>
+    </div>
+    <div class="create-right">
+      <div class="setting-tittle">设置</div>
+      <component :is="componentSettingList[curType]" ref="setting" @updateSetting="updateSetting" />
+    </div>
+  </div>
+</template>
+
+<script>
+import { bookTypeOption, componentList, componentSettingList } from '../data/bookType';
+
+export default {
+  name: 'CreatePage',
+  data() {
+    return {
+      curType: 'divider',
+      componentList,
+      componentSettingList,
+      bookTypeOption,
+    };
+  },
+  methods: {
+    back() {
+      this.$router.push('/');
+    },
+    showSetting(setting) {
+      this.$refs.setting.setSetting(setting);
+    },
+    updateSetting(setting) {
+      this.$refs.components.updateSetting(setting);
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+.create {
+  display: flex;
+  height: 100%;
+
+  &-left {
+    display: flex;
+    flex-direction: column;
+    width: 200px;
+    overflow: auto;
+    background-color: #fff;
+
+    .back-container {
+      display: flex;
+      flex-direction: column;
+      row-gap: 8px;
+      padding: 4px;
+
+      .back {
+        color: #000;
+        text-align: left;
+        background-color: #f4f4f4;
+      }
+
+      .title {
+        padding: 0 16px;
+        font-size: 14px;
+        font-weight: bold;
+      }
+    }
+
+    .components {
+      display: flex;
+      flex-direction: column;
+      padding: 24px 8px 4px;
+      margin-top: 12px;
+      border-top: 1px solid #e5e6eb;
+
+      .components-title {
+        padding-left: 8px;
+        font-size: 14px;
+        font-weight: bold;
+        color: #999;
+      }
+
+      .components-item {
+        display: flex;
+        align-items: center;
+        padding: 5px 16px;
+        cursor: pointer;
+
+        svg {
+          width: 16px;
+          height: 16px;
+          margin-right: 8px;
+        }
+      }
+    }
+  }
+
+  &-middle {
+    flex: 1;
+    padding: 24px 20px;
+    overflow: auto;
+    background-color: #ececec;
+
+    .canvas {
+      width: 100%;
+      min-height: 100%;
+      padding: 24px;
+      background-color: #fff;
+      border-radius: 4px;
+    }
+  }
+
+  &-right {
+    width: 200px;
+    padding: 16px 8px;
+    background-color: #fff;
+
+    .setting-tittle {
+      margin-bottom: 16px;
+      font-weight: bold;
+      color: #000;
+    }
+  }
+}
+</style>

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

@@ -0,0 +1,54 @@
+import DividerPage from '../create/components/base/divider/Divider.vue';
+import DividerSetting from '../create/components/base/divider/DividerSetting.vue';
+
+export const bookTypeOption = [
+  {
+    value: 'base',
+    label: '基础组件',
+    children: [
+      {
+        value: 'stem',
+        label: '题干',
+        icon: 'stem',
+        component: '',
+        // 设置页面
+        set: '',
+      },
+      {
+        value: 'divider',
+        label: '分割线',
+        icon: 'divider',
+        component: DividerPage,
+        set: DividerSetting,
+      },
+    ],
+  },
+  {
+    value: 'question',
+    label: '题型组件',
+    children: [
+      {
+        value: 'select',
+        label: '选择组件',
+        icon: '',
+        component: '',
+        set: '',
+      },
+    ],
+  },
+];
+
+// 组件列表
+export const componentList = bookTypeOption
+  .flatMap((item) => item.children)
+  .reduce((prev, { value, component }) => ({ ...prev, [value]: component }), {});
+
+// 组件设置列表
+export const componentSettingList = bookTypeOption
+  .flatMap((item) => item.children)
+  .reduce((prev, { value, set }) => ({ ...prev, [value]: set }), {});
+
+// 组件名称类别
+export const componentNameList = bookTypeOption
+  .flatMap((item) => item.children)
+  .reduce((prev, { value, label }) => ({ ...prev, [value]: label }), {});

+ 8 - 0
src/views/book/courseware/data/divider.js

@@ -0,0 +1,8 @@
+export let DividerData = {
+  type: 'divider',
+  title: '分割线',
+  setting: {
+    height: 100,
+    type: 'solid',
+  },
+};

+ 141 - 0
src/views/book/create.vue

@@ -0,0 +1,141 @@
+<template>
+  <div class="create">
+    <div class="breadcrumb">
+      <ul>
+        <li v-for="(item, i) in breadcrumbList" :key="item">
+          <span v-if="i !== 0" class="separator">></span>
+          <span class="breadcrumb-name">
+            {{ item }}
+          </span>
+        </li>
+      </ul>
+    </div>
+    <div class="basic-info">
+      <div class="basic-info-title">基本信息</div>
+      <el-form ref="form" :model="form" label-width="80px">
+        <el-form-item label="书籍名称" prop="name">
+          <el-input v-model="form.name" placeholder="请输入内容" />
+        </el-form-item>
+        <el-form-item label="作者" prop="author">
+          <el-input v-model="form.author" placeholder="请输入内容" />
+        </el-form-item>
+        <el-form-item label="现价" prop="current_price">
+          <el-input v-model="form.current_price" placeholder="请输入内容" />
+        </el-form-item>
+        <el-form-item label="原价" prop="original_price">
+          <el-input v-model="form.original_price" placeholder="请输入内容" />
+        </el-form-item>
+        <el-form-item label="标签" prop="tags">
+          <el-input v-model="form.tags" placeholder="请输入内容" />
+        </el-form-item>
+        <el-form-item label="分类" prop="classify">
+          <el-input v-model="form.classify" placeholder="请输入内容" />
+        </el-form-item>
+        <el-form-item label="简介" prop="brief_introduction">
+          <el-input
+            v-model="form.brief_introduction"
+            type="textarea"
+            :autosize="{ minRows: 6 }"
+            :maxlength="500"
+            show-word-limit
+            placeholder="请输入更多内容"
+          />
+        </el-form-item>
+        <el-form-item>
+          <el-button type="primary" @click="confirm">确定</el-button>
+          <el-button @click="$router.push('/')">取消</el-button>
+        </el-form-item>
+      </el-form>
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'CreateBookPage',
+  data() {
+    return {
+      form: {
+        name: '',
+        author: '',
+        current_price: '',
+        original_price: '',
+        tags: '',
+        classify: '',
+        brief_introduction: '',
+      },
+      breadcrumbList: ['教材管理', '新建教材', '基本信息'],
+    };
+  },
+  methods: {
+    confirm() {
+      this.$router.push('/courseware');
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+.create {
+  padding: 8px 24px;
+
+  .breadcrumb {
+    display: flex;
+    align-items: center;
+
+    > ul {
+      display: flex;
+
+      li {
+        font-weight: 400;
+        color: $font-light-color;
+
+        .separator {
+          margin: 0 8px;
+          color: #c9cdd4;
+        }
+
+        .breadcrumb-name {
+          font-size: 14px;
+        }
+
+        &:not(:last-child) {
+          cursor: pointer;
+        }
+
+        &:nth-last-child(1) {
+          font-weight: bold;
+          color: $font-color;
+        }
+      }
+    }
+  }
+
+  .basic-info {
+    width: 1200px;
+    padding: 24px;
+    margin: 24px auto 16px;
+    background-color: #fff;
+    border-radius: 4px;
+
+    &-title {
+      margin-bottom: 24px;
+      font-size: 18px;
+      font-weight: bold;
+    }
+
+    .el-form {
+      .el-input,
+      .el-textarea {
+        width: 400px;
+
+        :deep .el-input__inner,
+        :deep .el-textarea__inner {
+          background-color: #fff;
+          border-color: #dcdcdc;
+        }
+      }
+    }
+  }
+}
+</style>

+ 15 - 0
src/views/book/setting.vue

@@ -0,0 +1,15 @@
+<template>
+  <div></div>
+</template>
+
+<script>
+export default {
+  name: 'SettingPage',
+  data() {
+    return {};
+  },
+  methods: {},
+};
+</script>
+
+<style lang="scss" scoped></style>

+ 0 - 58
src/views/courseware/create/components/base/Divider.vue

@@ -1,58 +0,0 @@
-<template>
-  <div class="divider">
-    <ModuleBase>
-      <template #title>
-        <span>分割线</span>
-      </template>
-      <template #content>
-        <el-divider />
-      </template>
-    </ModuleBase>
-    <ModuleProperty>
-      <template #property>
-        <el-form :label-position="labelPosition" label-width="80px">
-          <el-form-item label="高度">
-            <el-input />
-          </el-form-item>
-          <el-form-item label="类型">
-            <el-select>
-              <el-option label="实线" />
-            </el-select>
-          </el-form-item>
-        </el-form>
-      </template>
-    </ModuleProperty>
-  </div>
-</template>
-
-<script>
-import ModuleMixin from '../common/ModuleMixin';
-export default {
-  name: 'Divider',
-  components: {},
-  mixins: [ModuleMixin],
-  data() {
-    return {
-      labelPosition: 'right',
-    };
-  },
-  methods: {},
-};
-</script>
-
-<style lang="scss" scoped>
-.divider{
-
-  .content {
-    display: flex;
-    justify-content: flex-start;
-    width: 50%;
-    height: 100px;
-    border: 1px solid red;
-
-    .property {
-      width: 200px;
-    }
-  }
-}
-</style>

+ 0 - 34
src/views/courseware/create/components/common/ModuleMixin.js

@@ -1,34 +0,0 @@
-// 组件混入
-import ModuleBase from './ModuleBase.vue';
-import ModuleProperty from './ModuleProperty.vue';
-
-const mixin = {
-  data() {
-    return {};
-  },
-  provide: [],
-  inject: [],
-  props: {
-    questionId: {
-      type: String,
-      default: '',
-    },
-    isChild: {
-      type: Boolean,
-      default: false,
-    },
-    isChange: {
-      type: Boolean,
-      default: false,
-    },
-  },
-  components: {
-    ModuleBase,
-    ModuleProperty,
-  },
-  created() {},
-  watch: {},
-  methods: {},
-};
-
-export default mixin;

+ 0 - 20
src/views/courseware/create/index.vue

@@ -1,20 +0,0 @@
-<template>
-  <div>
-    <Divider />
-  </div>
-</template>
-<script>
-import ModuleMixin from '../create/components/common/ModuleMixin.js';
-import Divider from '../create/components/base/Divider.vue';
-export default {
-  name: 'Create',
-  components: {
-    Divider,
-  },
-  mixins: [ModuleMixin],
-  data() {
-    return {};
-  },
-  methods: {},
-};
-</script>

+ 4 - 4
src/views/home/index.vue

@@ -2,7 +2,7 @@
   <div class="home">
     <div class="home-title">
       <span>教材列表</span>
-      <el-button type="primary" @click="createTextbook">创建教材</el-button>
+      <el-button type="primary" @click="createBook">创建教材</el-button>
     </div>
     <div class="home-tabs">
       <span
@@ -54,7 +54,7 @@ export default {
       cur_page: 1,
       page_capacity: 10,
       total: 0,
-      tab: 'draft',
+      tab: 'published',
       tabList: [
         {
           label: '已上架',
@@ -68,8 +68,8 @@ export default {
     };
   },
   methods: {
-    createTextbook() {
-      this.$router.push('/textbook/create');
+    createBook() {
+      this.$router.push('/book/create');
     },
     changePage(number) {
       this.cur_page = number;