Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

拖拽后台页面的设计思路 #13

Open
AnnVoV opened this issue Jul 4, 2018 · 0 comments
Open

拖拽后台页面的设计思路 #13

AnnVoV opened this issue Jul 4, 2018 · 0 comments

Comments

@AnnVoV
Copy link
Owner

AnnVoV commented Jul 4, 2018

设计及初步实现

希望我们的后台页面可以靠拖拽组件来生成,开发时只要处理js部分。拖拽后可以查看code直接复制代码。目前设计了一个大框子,大概长下面这个样子:
demo2

思路

先说下我在设计这个大框子时遇到的几个问题:

  • 1.如何判断组件被拖拽到了哪个组件的里面?如果是el-row这种,如何判断是el-row的哪一个col里?
  • 2.如何动态插入拖动元素
  • 3.最终我们要能够拿到生成的这个页面的代码?怎么获取代码?
  • 4.点击showCode, 代码如何展示?
  • 5.嵌套的组件(比如el-row的el-col里面嵌入了el-button)如何获取嵌套关系,以及获得最终的代码?

解决方法

  • 第一个问题,我通过比较当前鼠标位置是否在componet内部来找到最终的容器
  • 第二个问题主要利用了$slots 代码如下
    (1)通过$createElement生成vnode
    (2)把vnode push到container的$slots.default数组里
    (3)vnode.componentInstance 绑定了这个vnode 最终生成的vue实例,这样有了它,我们就能把修改的属性更新到其身上
handleNest(result) {
      const container = result.vueComp;
      const son = this.$createElement(`my-${this.compName.toLowerCase()}`);
      container.$slots.default.push(son);
      container.$mount();
      this.component = son.componentInstance;// 这样我们还可以使用this.component去拿到我们添加的方法和属性
    },
  • 第三个问题,真的是想了很久,后来参考了Vue-Layout 项目的做法,我觉得那个作者的思路特别好!我的组件库是Element-UI。我们本质需要能拿到template 字符串才能实现复制code的功能,所以我们把我们每一个需要用的组件再包裹一层,目的是为了让其有template属性,存储模板的字符串。举个例子,以el-button为例,代码如下:
import { getStringTypeAttr, getResultStringTypeAttr } from '@/components/template';
import mixins from './mixin.js';

const handle = function (_attr, _slots) {
  // 定义默认属性
  const attributes = {
    content: {
      type: 'text',
      valueType: 'text',
      value: 'el-button',
    },
    type: {
      type: 'select',
      valueType: 'text',
      items: ['primary', 'success', 'warning', 'danger', 'info', 'text'],
      value: 'primary',
    },
    size: {
      type: 'select',
      valueType: 'text',
      items: ['medium', 'small', 'mini'],
      value: 'medium',
    },
    disabled: {
      type: 'select',
      valueType: 'boolean',
      items: ['true', 'false'],
      value: 'false',
    },
  };
  const props = {
    content: {
      default: 'el-button',
    },
    type: {
      default: 'primary',
    },
    size: {
      default: 'medium',
    },
    disabled: {
      default: false,
    },
  };
  const slots = {
    default: [],
  };

  // 覆盖默认属性
  Object.assign(slots, _slots);
  Object.assign(attributes, _attr);
  // 获取组件模板
  const template = mixins.getStrTemplate(attributes, 'el-button');
  // 获取代码模板(组件模板 + 绑定数据值)
  const getTemplate = (attr, child) => mixins.getTemplateByTag(attributes, attr, 'el-button', child);

  return {
    template,
    props,
    attributes,
    slots,
    getTemplate,
  };
};
export default handle;
  • 第3个问题-第5个问题其实可以归为一类,都是生成组件后代码展示问题。
    我们利用我们添加的template属性,可以比较容易的获取到字符串模板。但也遇到了以下一些问题:
    (1)比如我们左边是可以修改组件的属性信息的,那么模板如何与属性信息进行绑定的?
    (2)组件之间是可以嵌套的,如何建立组件之间的父子关系?
    (3)我们最终得到的字符串在代码模式下如何格式化和高亮?
    让我们来依次回答下这些问题:
    (1)其实我们本质是要用下面这个方法,来获取最终模板, 里面核心的就是获取最终attributes字符串的方法
export default {
  getTemplateByTag(attributes, attr, tagName, child) {
    const newAttr = _.overwriteDeep(attributes, attr);
    const resStringAttr = getResultStringTypeAttr(newAttr);
    const resTemplate = `<${tagName}
                        ${resStringAttr}>
                        ${child || ''}
                    </${tagName}>`;
    return resTemplate;
  },
}
const getResultStringTypeAttr = function (attributes) {
  // value为空的不添加到模板中
  let stringAttr = '';
  Object.keys(attributes).forEach((key) => {
    let attrKey;
    const arr = ['text', 'selection', 'icon', 'ionicon', 'color']; // 这些类型都不用加bind
    if (arr.includes(attributes[key].valueType)) {
      attrKey = key;
    } else {
      attrKey = `:${key}`;
    }
    if (attrKey === 'value') {
      attrKey = 'v-model';
    }
    const attr = attributes[key].value ? `${attrKey}="${attributes[key].value}"\n` : '';
    stringAttr += attr;
  });
  return stringAttr;
};

(2)父子关系之间的查找其实很简单,当我们拖拽一个组件的时候,我们就生成一个id,并且遍历这个组件,为其子元素设置id和对应的parentId。当组件之间发生嵌套时,同样进行处理,这样我们最终只要找到根元素id就能遍历出整个dom结构。
(3)html 代码的格式化使用了pretty这个包, html语法高亮使用了vue-highlightjs 这个包

接下来要做的功能

  • 可以拖拽删除组件
  • 选中组件后可以再次编辑属性
  • column选项目前还未添加设置所占宽度百分比功能
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant