02020 / vite-kit

0 stars 0 forks source link

Grade-Render-1.1 #7

Open 02020 opened 3 years ago

02020 commented 3 years ago

grade-builder 无限极嵌套 - 核心代码 1.1

01.core-render-渲染部分

// 通过 template 来渲染
export const slotsTemplate = (slots, scopedSlots) => {
  if (!slots) return;
  return Object.keys(slots).reduce((acc, key) => {
    acc[key] = (props) => {
      if (Array.isArray(slots[key])) {
        return slots[key].map(scopedSlots[key]);
      } else {
        return scopedSlots[key](slots[key]);
      }
    };
    return acc;
  }, {});
};

// render 函数封装-层级渲染
export const renderC = (h, { context, scopedSlots }, x) => {
  // 通过 Render 来渲染
  const slotsC = (slots) => {
    if (!slots) return;
    return Object.keys(slots).reduce((acc, key) => {
      acc[key] = (props) => {
        if (Array.isArray(slots[key])) {
          return slots[key].map(renderG);
        } else {
          // console.log(slots[key]);
          return renderG(slots[key]);
        }
      };
      return acc;
    }, {});
  };

  // 递归渲染
  const renderG = (x) => {
    if (!x || typeof x === 'string') {
      return x;
    }

    let child = Array.isArray(x.child)
      ? x.child.map(renderG)
      : [renderG(x.child)];

    const _scopedSlots = {
      ...slotsTemplate(x.slotsT, scopedSlots),
      ...slotsC(x.slots),
    };

    // 事件采用分发的方式
    const on = !x.on
      ? {}
      : x.on.reduce((acc, key) => {
          const _key = Array.isArray(key)
            ? key
            : [key, x.key ? key + '-' + x.key : key];
          acc[_key[0]] = (...args) => {
            context.$emit('cmd', _key[1], x, ...args);
          };
          return acc;
        }, {});

    // console.log(x.component, _scopedSlots);
    const resp = h(
      x.component || 'div',
      {
        class: x.class,
        style: x.style,
        props: x.props,
        slot: x.slot, // 子组件渲染
        on,
        scopedSlots: _scopedSlots,
      },
      child
    );
    return resp;
  };
  return renderG(x);
};

export default {
  name: 'grade-builder',
  props: {
    config: {
      type: Object,
    },
  },
  render(h) {
    const scopedSlots = this.$scopedSlots;
    const context = this;
    return renderC(h, { context, scopedSlots }, this.config);
  },
};

02.builder-组件构建

import { Icon, Collapse, Panel, PanelCard, CardTitle, List, ListItem } from '.';

export const RR = {
  /**
   *
   */
  o: (f) => (g) => (x) => f(g(x)),

  of: (x) => [x],
};

/**
 *
 * @param {*} cb
 * @param {*} option 默认配置
 * @param {*} f 对默认配置option进行处理
 * @returns {Array} [option, option]
 * @demo 渲染本级
 */
export const fMap = (f, cb = (x) => x) => (list) => cb(list.map(f));

/**
 * 带卡片样式的折叠面板
 * @param {*} props
 */
export const toCollapseCard = (props, cb = (x) => x) =>
  fMap((x, index) => {
    const fc = PanelCard(x.name || '' + index, x.group);
    const title = CardTitle(x.title, x.extra);
    title.push(x);
    return cb(fc(title), index);
  }, Collapse(props.value));

/**
 * 折叠面板
 * @param {*} props
 */
export const toCollapse = (props, cb = (x) => x) =>
  fMap((x, index) => {
    const fc = Panel(x.name || '' + index, x.group || x.title);
    return cb(fc(x), index);
  }, Collapse(props.value));

/**
 * 列表
 * @param {*} fStyle
 */
export const toList = (props, fStyle) =>
  fMap((x) => ListItem(fStyle(x)), List(props));

/**
 *
 * @param {*} item
 * @param {*} _class
 */
const toStyle = (item, _class = '') => (child) => {
  return {
    class: (item.class || item) + _class,
    style: item.style,
    child: child,
  };
};

/**
 * 1-表格
 * @param {*} param0
 */
export const toTable = ({ table, row, col }) =>
  fMap(toRow({ row, col }), toStyle(table));

/**
 * 2-表格-行
 * @param {*} param0
 */
export const toRow = ({ row, col }) => fMap(toCol(col), toStyle(row));

/**
 * 3-表格-列
 * 待完善与优化
 * @param {*} col
 */
export const toCol = (col) => (cell) => {
  // 数据格式转换, x为数组
  const [key, _class, child] = cell;
  return {
    on: ['click'],
    key,
    style: col.style,
    class: col.class || col + _class,
    child,
  };
};

03.config-组件的配置

// 组件的配置清单,供Render使用

/**
 * iview-图标
 * @param {*} type
 * @param {*} size
 * @param {*} color
 */
export const Icon = (type, size = 30, color = '#2d8cf0') => {
  return {
    component: 'Icon',
    props: {
      type,
      size,
      color,
    },
  };
};

/**
 * iview-图标-主要供map函数使用
 * @param {*} size
 * @param {*} color
 */
export const IconC = (size = 30, color = '#2d8cf0') => (type) =>
  Icon(type, size, color);

/**
 * iview-卡片标题
 * @param {*} title
 * @param {*} extra
 */
export const CardTitle = (title, extra) => {
  title = title || Icon('ios-attach');
  extra = extra || Icon('md-add');
  return [
    {
      slot: 'title',
      child: title,
      // 原事件名称->新事件名称
      on: [['click', 'click-extra']],
    },
    {
      slot: 'extra',
      child: extra,
      on: [['click', 'click-extra']],
    },
  ];
};
/**
 * iview-折叠面板
 * @param {*} value
 */
export const Collapse = (value = []) => (child) => {
  return {
    component: 'Collapse',
    props: {
      value,
    },
    on: ['on-change', ['input', 'input-collapse']],
    child: child,
  };
};

/**
 * iview-折叠面板-项
 * @param {*} name
 * @param {*} title
 */
export const Panel = (name = '', title = '标题') => (child) => {
  return {
    component: 'Panel',
    props: {
      name: name,
    },
    child: title,
    slots: {
      content: child,
    },
  };
};
/**
 * iview-带卡片样式的折叠面板
 * @param {*} name
 * @param {*} title
 */
export const PanelCard = (name = '', title = '标题') => (child) => {
  return {
    component: 'Panel',
    props: {
      name,
    },
    child: title,
    slots: {
      content: {
        component: 'Card',
        child: child,
      },
    },
  };
};
/**
 * iview-列表
 * @param {*} props
 */
export const List = (props) => (child) => {
  return {
    component: 'List',
    style: props.style,
    class: props.class,
    props: {
      itemLayout: 'vertical',
      ...props,
    },
    child: child,
  };
};
/**
 * iview-列表-列表项
 * @param {*} param0
 */
export const ListItem = ({ action, extra, avatar, title, description }) => {
  return {
    component: 'ListItem',
    slots: {
      action: {
        child: action,
      },
      extra: {
        child: extra,
        on: [['click', 'click-extra']],
      },
    },
    child: [
      { slot: 'action' }, // 只能这样配置
      { slot: 'extra' }, // 只能这样配置
      {
        component: 'ListItemMeta',
        child: [
          {
            slot: 'avatar',
            child: avatar,
          },
          { slot: 'title', child: title },
          { slot: 'description', child: description },
        ],
      },
    ],
  };
};

const index = {   Icon,  Collapse,  Panel,  PanelCard};

export default index;

Originally posted by @02020 in https://github.com/02020/vite-kit/issues/6#issuecomment-742277268

02020 commented 3 years ago

定义函数式组件

踩坑

// 写法1: 正确写法
export const toComponent = (name, f) => {
  return {
    functional: true,
    name,
    render: toRenderC(f),
  };
};

// 写法2: 以下为错误示例, 会导致全局响应
const bug = () => {
  return {
    functional: true,
    render(h, context) {
      return toRenderC(f(context.props))(h, context);
    },
  };
};