低代码平台技术点 - 3: 拖拽

1,401 阅读2分钟

本文仅为总结技术点,阅读需要中高的前端基础、和一定的低代码基础

拖拽

采用react-dnd进行拖拽

把画布stage设置为需要的样式(demo固定为375设计稿),并添加drop用于放置拖拽过来的物料:

const Stage: FC = () => {
  
  const components = useComponentSchema((s) => s.components)
  const renderNodes = Array.isArray(components)
    ? components.map((c) => render(c))
    : render(components)
  // 添加drop逻辑
  const [{ canDrop }, drop] = useDrop({
    accept: COMPONENT_TYPES,
    collect: (monitor) => ({
      canDrop: monitor.canDrop()
    })
  })
  return (
    <div className="flex-1 bg-[#edeff3] h-full flex items-center justify-center p-10">
      <div
        className={C('w-[375px] h-full bg-white', {
          'border border-dashed border-[#3333ff]': canDrop
        })}
        ref={drop}
      >
        {renderNodes}
      </div>
    </div>
  )
}

其中useComponentSchema采用zustandschema的状态管理:

type State = {
  components: AllComponentType[]
}

type Action = {
  addComponents: (component: AllComponentType) => void
}

const useComponentSchema = create<State & Action>((set) => ({
  components: [],
  addComponents: (component: AllComponentType) => {
    set((state) => ({
      components: [...state.components, component]
    }))
  }
}))

但是最好还是封装一个componentHydrate,用来处理整个componentadd delete upgrade

 const addComponents = (
  type: AllComponentType['type'],
  customProps?: AllComponentType['props']
) => {
  const { addComponents } = useComponentSchema.getState()
  // 通过getDefaultComponentSchema获取某个type的组件的默认配置
  const schema = getDefaultComponentSchema(
    type,
    customProps
  ) as AllComponentType
  if (!schema) {
    return
  }
  addComponents(schema)
}

material.gif

实现容器拖拽

同理,实现一个容器物料,比如div,接收被拖拽的物料,则需要实现容器物料能够被放置。在前面我们渲染原生标签用的是React.createElement,则现在需要自己写物料组件,返回JSX。

    // 实现物料 div容器
type Props = Omit<BoxComponentProps['props'], 'children'> & { 'data-id': ID }
const Div: FC<PropsWithChildren<Props>> = (props) => {
  const { children, ...divProps } = props
  const [{ canDrop }, drop] = useDrop({
    accept: COMPONENT_TYPES,
    collect: (monitor) => ({
      canDrop: monitor.canDrop()
    }),
    drop(): DROP_RESULT_TYPE {
      console.log('drop div')
      return {
        type: NATIVE_COMPONENT_ENUM.BOX,
        id: divProps['data-id']
      }
    }
  })
  return (
    <div
      ref={drop}
      {...divProps}
      className={C('border border-solid min-h-[50px] ', {
        'border-[#3333ff] border-dashed': canDrop
      })}
    >
      {children}
    </div>
  )
}

export default Div

但是多个drop层叠的时候,需要确定好drag到哪一个,这是一个复杂的问题(在下面),目前就先放在schema最深的能接受这个drag typedrop上。

几个问题:

  • 怎么确定被拖拽的物料是要放在哪个已经在画布上的容器?
  • 怎么勾画出能够放置被拖拽的物料位置提示?
  • 是否能写一个通用dragwrapper和通用dropwrapper分别处理新增和编辑的情况