Quellcode durchsuchen

feat: 编辑区支持表单套件

王家程 vor 3 Jahren
Ursprung
Commit
3a970c7b3f

+ 19 - 2
src/components/generator/wisdomUiComp.js

@@ -2,7 +2,7 @@
  * @Author: wjc
  * @Date: 2021-09-15 17:46:28
  * @LastEditors: wjc
- * @LastEditTime: 2021-09-17 14:39:20
+ * @LastEditTime: 2021-09-18 14:20:04
  * @Description: wisdom ui 组件
  */
 
@@ -48,7 +48,7 @@ export const wLayoutComponents = [
       label: '页头',
       tag: 'w-header',
       tagIcon: 'wheader',
-      layout: 'auto',
+      layout: 'raw',
       span: 24
     },
     // 组件的插槽属性
@@ -58,5 +58,22 @@ export const wLayoutComponents = [
     // 其余的为可直接写在组件标签上的属性
     desc: '描述文字', // 描述
     back: () => {}
+  },
+  {
+    // 组件的自定义配置
+    __config__: {
+      label: '表单容器',
+      tag: 'el-form',
+      tagIcon: 'row',
+      layout: 'form',
+      span: 24,
+      layoutTree: true
+    },
+    // 其余的为可直接写在组件标签上的属性
+    class: 'form-container',
+    size: '',
+    inline: true,
+    labelWidth: '',
+    type: 'default'
   }
 ]

+ 4 - 4
src/layouts/DefaultLayout/index.vue

@@ -1,8 +1,8 @@
 <!--
  * @Author: WangJiaCheng
  * @Date: 2021-05-06 17:24:30
- * @LastEditors: WangJiaCheng
- * @LastEditTime: 2021-08-20 15:36:08
+ * @LastEditors: wjc
+ * @LastEditTime: 2021-09-18 09:37:37
  * @Description:
 -->
 <template>
@@ -55,8 +55,8 @@ export default {
   .layout-main {
     width: 100%;
     display: flex;
-    justify-self: space-between;
-    height: calc(100% - 60px);
+    justify-content: space-between;
+    /* height: calc(100% - 60px); */
     padding: 0;
     background: #fff;
   }

+ 2 - 0
src/views/Form/FormDesign/DraggableItem.vue

@@ -24,6 +24,7 @@ const layouts = {
     const { activeItem } = this.$listeners
     const config = currentItem.__config__
     const child = renderChildren.apply(this, arguments)
+    console.log('col-layout----', child, config)
     let className = this.activeId === config.formId ? 'drawing-item active-from-item' : 'drawing-item'
     if (this.formConf.unFocusedComponentBorder) className += ' unfocus-bordered'
     let labelWidth = config.labelWidth ? `${config.labelWidth}px` : null
@@ -50,6 +51,7 @@ const layouts = {
       ? 'drawing-row-item active-from-item'
       : 'drawing-row-item'
     let child = renderChildren.apply(this, arguments)
+    console.log('2222', child, config)
     if (currentItem.type === 'flex') {
       child = <el-row type={currentItem.type} justify={currentItem.justify} align={currentItem.align}>
               {child}

+ 11 - 3
src/views/app/components/AttributePanel/AttributePanel.vue

@@ -2,13 +2,13 @@
  * @Author: WangJiaCheng
  * @Date: 2021-09-14 16:24:04
  * @LastEditors: wjc
- * @LastEditTime: 2021-09-17 16:03:16
+ * @LastEditTime: 2021-09-18 10:04:28
  * @Description: 属性面板
 -->
 <template>
   <div class="attribute-panel">
     <form-attribute-panel
-      :show-field="activeData.__config__"
+      :show-field="!!dragList.length"
       :active-data="activeData"
       :form-conf="formConf"
       @fetch-data="fetchData"
@@ -25,10 +25,18 @@ export default {
     FormAttributePanel
   },
   props: {
+    dragList: {
+      type: Array,
+      default() {
+        return []
+      }
+    },
     activeData: {
       type: Object,
       default() {
-        return {}
+        return {
+          __config__: {}
+        }
       }
     }
   },

+ 17 - 3
src/views/app/components/AttributePanel/FormAttributePanel.vue

@@ -2,7 +2,7 @@
  * @Author: wjc
  * @Date: 2021-09-17 15:19:48
  * @LastEditors: wjc
- * @LastEditTime: 2021-09-17 16:24:56
+ * @LastEditTime: 2021-09-18 10:07:12
  * @Description: 表单属性面包
 -->
 <template>
@@ -14,7 +14,7 @@
     <div class="field-box">
       <el-scrollbar class="right-scrollbar">
         <!-- 组件属性 -->
-        <el-form v-show="currentTab==='field' && showField" size="small" label-width="90px">
+        <el-form v-if="currentTab==='field' && showField" size="small" label-width="90px">
           <el-form-item v-if="activeData.__vModel__!==undefined" label="字段名">
             <el-input v-model="activeData.__vModel__" placeholder="请输入字段名(v-model)" />
           </el-form-item>
@@ -635,7 +635,21 @@ export default {
   components: {
     TreeNodeDialog
   },
-  props: ['showField', 'activeData', 'formConf'],
+  props: {
+    showField: Boolean,
+    activeData: {
+      type: Object,
+      default() {
+        return {}
+      }
+    },
+    formConf: {
+      type: Object,
+      default() {
+        return {}
+      }
+    }
+  },
   data() {
     return {
       currentTab: 'field',

+ 38 - 11
src/views/app/components/DraggableItem/DraggableItem.vue

@@ -24,20 +24,25 @@ const layouts = {
     const { activeItem } = this.$listeners
     const config = currentItem.__config__
     const child = renderChildren.apply(this, arguments)
+    console.log('col-layout--2222--', child, config)
     let className = this.activeId === config.formId ? 'drawing-item active-from-item' : 'drawing-item'
     if (this.formConf.unFocusedComponentBorder) className += ' unfocus-bordered'
     let labelWidth = config.labelWidth ? `${config.labelWidth}px` : null
     if (config.showLabel === false) labelWidth = '0'
     return (
-      <el-col span={config.span} class={className}
-        nativeOnClick={event => { activeItem(currentItem); event.stopPropagation() }}>
+        <el-form-item
+          class={className}
+          label-width={labelWidth}
+          label={config.showLabel ? config.label : ''} required={config.required}
+          nativeOnClick={event => { activeItem(currentItem); event.stopPropagation() }}
+        >
           <render key={config.renderKey} conf={currentItem} onInput={ event => {
             this.$set(config, 'defaultValue', event)
           }}>
             {child}
           </render>
-        {components.itemBtns.apply(this, arguments)}
-      </el-col>
+          {components.itemBtns.apply(this, arguments)}
+        </el-form-item>
     )
   },
   rowFormItem(h, currentItem, index, list) {
@@ -66,16 +71,35 @@ const layouts = {
       </el-col>
     )
   },
-  raw(h, currentItem, index, list) {
+  form(h, currentItem, index, list) {
+    const { activeItem } = this.$listeners
     const config = currentItem.__config__
+    const className = this.activeId === config.formId
+      ? 'drawing-row-item active-from-item'
+      : 'drawing-row-item'
     const child = renderChildren.apply(this, arguments)
-    return <render key={config.renderKey} conf={currentItem} onInput={ event => {
-      this.$set(config, 'defaultValue', event)
-    }}>
-      {child}
-    </render>
+    console.log('child---', child, config, config.children, arguments)
+    return (
+      <el-form
+        size={currentItem.size}
+        inline={currentItem.inline}
+        class={`${currentItem.class} ${className}`}
+        nativeOnClick={event => { activeItem(currentItem); event.stopPropagation() }}
+      >
+        <span class="component-name">{config.componentName}</span>
+        <draggable
+          list={config.children || []}
+          animation={340}
+          class="drag-wrapper"
+          group="componentsGroup"
+        >
+          {child}
+        </draggable>
+        {components.itemBtns.apply(this, arguments)}
+      </el-form>
+    )
   },
-  auto(h, currentItem, index, list) {
+  raw(h, currentItem, index, list) {
     const config = currentItem.__config__
     const child = renderChildren.apply(this, arguments)
     return <render key={config.renderKey} conf={currentItem} onInput={ event => {
@@ -88,9 +112,12 @@ const layouts = {
 
 function renderChildren(h, currentItem, index, list) {
   const config = currentItem.__config__
+  console.log('render child 1', config)
   if (!Array.isArray(config.children)) return null
+  console.log('render child 2', config.children)
   return config.children.map((el, i) => {
     const layout = layouts[el.__config__.layout]
+    console.log('render child 3', layout)
     if (layout) {
       return layout.call(this, h, el, i, config.children)
     }

+ 25 - 7
src/views/app/components/WisdomUiPanel/WisdomUiPanel.vue

@@ -2,7 +2,7 @@
  * @Author: wjc
  * @Date: 2021-09-15 16:03:17
  * @LastEditors: wjc
- * @LastEditTime: 2021-09-17 15:13:22
+ * @LastEditTime: 2021-09-18 15:14:43
  * @Description:
 -->
 <template>
@@ -62,6 +62,7 @@ export default {
   },
   data() {
     return {
+      activeId: 1,
       wcComponents: [
         {
           title: '表单型组件',
@@ -75,27 +76,44 @@ export default {
     }
   },
   methods: {
+    // 快捷操作,点击组件时,让编辑区添加该组件
     addComponent(item) {
       // const clone = this.cloneComponent(item)
       // this.fetchData(clone)
       // this.drawingList.push(clone)
       // this.activeFormItem(clone)
     },
+    // 拖动组件后,给组件数据添加额外属性
+    createIdAndKey(item) {
+      const config = item.__config__
+      config.formId = this.activeId
+      config.renderKey = `${config.formId}${+new Date()}` // 改变renderKey后可以实现强制更新组件
+      if (config.layout === 'colFormItem') {
+        item.__vModel__ = `field${this.activeId}`
+      } else if (config.layout === 'form') {
+        config.componentName = `form${this.activeId}`
+        !Array.isArray(config.children) && (config.children = [])
+        delete config.label // rowFormItem无需配置label属性
+      }
+      if (Array.isArray(config.children)) {
+        config.children = config.children.map(childItem => this.createIdAndKey(childItem))
+      }
+      return item
+    },
     cloneComponent(origin) {
       const clone = deepClone(origin)
       const config = clone.__config__
       // config.span = this.formConf.span // 生成代码时,会根据span做精简判断
-      // this.createIdAndKey(clone)
+      this.createIdAndKey(clone)
       clone.placeholder !== undefined && (clone.placeholder += config.label)
       tempActiveData = clone
       return tempActiveData
     },
     onEnd(obj) {
-      // if (obj.from !== obj.to) {
-      //   this.fetchData(tempActiveData)
-      //   this.activeData = tempActiveData
-      //   this.activeId = this.idGlobal
-      // }
+      if (obj.from !== obj.to) {
+        this.activeId = new Date().getTime()
+        this.$emit('drag-end', tempActiveData, this.activeId)
+      }
     }
   }
 }

+ 15 - 13
src/views/app/editor/EditorArea/EditorArea.vue

@@ -2,7 +2,7 @@
  * @Author: wjc
  * @Date: 2021-09-16 10:55:40
  * @LastEditors: wjc
- * @LastEditTime: 2021-09-17 15:59:50
+ * @LastEditTime: 2021-09-18 14:54:56
  * @Description:
 -->
 <template>
@@ -19,21 +19,12 @@
         :drawing-list="editorList"
         :current-item="item"
         :index="index"
-        :active-id="activeId"
+        :active-id="activeDragId"
         :form-conf="formConf"
         @activeItem="activeFormItem"
         @deleteItem="drawingItemDelete"
       />
     </draggable>
-    <!-- <div class="header-area">
-      header
-    </div>
-    <div class="form-area">
-      form
-    </div>
-    <div class="table-area">
-      table
-    </div> -->
   </div>
 </template>
 
@@ -48,6 +39,12 @@ export default {
     draggable,
     DraggableItem
   },
+  props: {
+    activeId: {
+      type: Number,
+      default: 1
+    }
+  },
   data() {
     return {
       formConf: {
@@ -63,16 +60,21 @@ export default {
         span: 24,
         formBtns: true
       },
-      activeId: 1,
+      activeDragId: 1,
       activeData: [],
       editorList: []
     }
   },
+  watch: {
+    activeId(n) {
+      this.activeDragId = n
+    }
+  },
   methods: {
     // 当前选中的组件
     activeFormItem(currentItem) {
       this.activeData = currentItem
-      this.activeId = currentItem.__config__.formId
+      this.activeDragId = currentItem.__config__.formId
       this.$emit('active-item', this.activeData)
     },
     drawingItemDelete(index, list) {

+ 93 - 3
src/views/app/editor/index.vue

@@ -2,7 +2,7 @@
  * @Author: WangJiaCheng
  * @Date: 2021-08-20 09:10:36
  * @LastEditors: wjc
- * @LastEditTime: 2021-09-17 16:22:23
+ * @LastEditTime: 2021-09-18 15:31:48
  * @Description: 编辑器
 -->
 <template>
@@ -16,7 +16,9 @@
           <span slot="label" style="text-align: center">
             <img :src="wcIconImg" alt="wc" style="width: 16px; vertical-align: middle">
           </span>
-          <wisdom-ui-panel />
+          <wisdom-ui-panel
+            @drag-end="handleDragEnd"
+          />
         </el-tab-pane>
         <el-tab-pane>
           <span slot="label" style="text-align: center">
@@ -34,11 +36,13 @@
     </div>
     <div class="editor-main">
       <editor-area
+        :active-id="activeId"
         @active-item="handleActiveItem"
       />
     </div>
     <div class="props-panel">
       <attribute-panel
+        :drag-list="dragList"
         :active-data="activeData"
       />
     </div>
@@ -64,7 +68,9 @@ export default {
   },
   data() {
     return {
-      activeData: {}
+      activeId: 1,
+      activeData: {},
+      dragList: []
     }
   },
   computed: {
@@ -75,6 +81,10 @@ export default {
   methods: {
     handleActiveItem(data) {
       this.activeData = data
+    },
+    handleDragEnd(data, id) {
+      this.activeData = data
+      this.activeId = id
     }
   }
 }
@@ -103,4 +113,84 @@ export default {
       width: 20%;
     }
   }
+  .form-container {
+    margin: 0 20px;
+  }
+  .drawing-row-item {
+    position: relative;
+    cursor: move;
+    box-sizing: border-box;
+    border: 1px dashed #ccc;
+    border-radius: 3px;
+    padding: 0 2px;
+    margin-bottom: 15px;
+    .drawing-row-item {
+      margin-bottom: 2px;
+    }
+    .el-col{
+      margin-top: 22px;
+    }
+    .el-form-item{
+      margin-bottom: 0;
+    }
+    .drag-wrapper{
+      min-height: 80px;
+    }
+    &.active-from-item{
+      border: 1px dashed @lighterBlue;
+    }
+    .component-name{
+      top: 0;
+      left: 0;
+      font-size: 12px;
+      color: #bbb;
+      display: inline-block;
+      padding: 0 6px;
+    }
+}
+.drawing-item, .drawing-row-item{
+  &:hover {
+    & > .el-form-item{
+      background: @selectedColor;
+      border-radius: 6px;
+    }
+    & > .drawing-item-copy, & > .drawing-item-delete{
+      display: initial;
+    }
+  }
+  & > .drawing-item-copy, & > .drawing-item-delete{
+    display: none;
+    position: absolute;
+    top: -10px;
+    width: 22px;
+    height: 22px;
+    line-height: 22px;
+    text-align: center;
+    border-radius: 50%;
+    font-size: 12px;
+    border: 1px solid;
+    cursor: pointer;
+    z-index: 1;
+  }
+  & > .drawing-item-copy{
+    right: 56px;
+    border-color: @lighterBlue;
+    color: @lighterBlue;
+    background: #fff;
+    &:hover{
+      background: @lighterBlue;
+      color: #fff;
+    }
+  }
+  & > .drawing-item-delete{
+    right: 24px;
+    border-color: #F56C6C;
+    color: #F56C6C;
+    background: #fff;
+    &:hover{
+      background: #F56C6C;
+      color: #fff;
+    }
+  }
+}
 </style>