设置区:让产品经理‘点到手软’的秘密武器!

324 阅读4分钟

引言

本文将带你深入低代码平台设置区的实现细节,聚焦属性、样式、事件三大功能模块,结合完整代码与原理分析,揭秘如何让右侧面板成为组件的“万能遥控器”!

  • 想要获取该项目的全部代码?可直接访问此仓库链接: 内附项目体验地址
  • 🔗 github.com/LZY-Ricardo…

1. 设置区整体结构与分工

设置区(Setting)是组件属性、样式和事件的集中管理地。它通过分段切换(Segmented)实现三大功能区的自由切换:

  • 属性:展示并编辑当前选中组件的业务属性
  • 外观:支持样式的可视化与代码编辑
  • 事件:预留事件绑定能力

核心入口代码如下:

import { useState } from 'react'
import { useComponentsStore } from '../../stores/components'
import { Segmented } from 'antd';
import ComponentAttr from './ComponentAttr'
import ComponentStyle from './ComponentStyle'
import ComponentEvent from './ComponentEvent'

export default function Setting() {
  const { components } = useComponentsStore()
  const [key, setKey] = useState('属性')
  return (
    <div>
      <Segmented value={key} options={['属性', '外观', '事件']} block onChange={setKey}/>
      <div className='pt-[20px]'>
        {key === '属性' && <ComponentAttr/>}
        {key === '外观' && <ComponentStyle/>}
        {key === '事件' && <ComponentEvent/>}
      </div>
    </div>
  )
}

2. 属性面板(ComponentAttr):组件属性的“魔法编辑器”

属性面板通过动态渲染表单,支持不同类型的属性编辑(如输入框、下拉框等),并与组件仓库实时联动。

import { useEffect } from 'react'
import { Form, Input, Select } from 'antd';
import { useComponentsStore } from '../../stores/components'
import { useComponentConfigStore } from '../../stores/component-config';
import type { ComponentSetter } from '../../stores/component-config';

export default function ComponentAttr() {
  const [form] = Form.useForm();
  const { curComponentId, curComponent, updateComponentProps } = useComponentsStore()
  const { componentConfig } = useComponentConfigStore()

  // 回显
  useEffect(() => {
    const data = form.getFieldsValue()
    form.setFieldsValue({...data, ...curComponent?.props})
  }, [curComponent])

  if (!curComponent || !curComponentId) {
    return null
  }

  function renderFormElement(setter: ComponentSetter) {
    const { type, options } = setter
    if (type === 'select') { // 下拉框
      return <Select options={options} />
    } else if (type === 'input') {
      return <Input />
    }
  }

  const valueChange = (values: any) => {
    console.log(values)
  }

  return (
    <Form form={form} onValuesChange={valueChange} labelCol={{ span: 8 }} wrapperCol={{ span: 14 }}>
      <Form.Item label="组件id">
        <Input disabled value={curComponentId}/>
      </Form.Item>
      <Form.Item label="组件名称">
        <Input disabled value={curComponent.name}/>
      </Form.Item>
      <Form.Item label="组件描述">
        <Input disabled value={curComponent.desc}/>
      </Form.Item>
      {/* 当前被选中的组件,允许修改的属性 */}
      {
        componentConfig[curComponent.name].setter?.map(setter => {
          return (
            <Form.Item name={setter.name} label={setter.label} key={setter.name}>
              {renderFormElement(setter)}
            </Form.Item>
          )
        })
      }
    </Form>
  )
}

原理分析

  • 组件属性通过 setter 配置动态生成,支持灵活扩展。
  • 表单回显与仓库数据实时同步,保证编辑体验流畅。
  • 支持多类型表单控件,满足不同属性需求。

效果展示:

image.png


3. 样式面板(ComponentStyle):让组件“颜值爆表”

样式面板不仅支持表单化样式编辑,还集成了 Monaco Editor,支持 CSS 代码直编直改,极大提升开发者自由度。

import { Form, Input, InputNumber, Select } from 'antd';
import { useEffect, useState } from 'react';
import type { CSSProperties } from 'react';
import { useComponentConfigStore } from '../../stores/component-config';
import type { ComponentSetter } from '../../stores/component-config';
import { useComponentsStore } from '../../stores/components';
import CssEditor from './CssEditor';
import { debounce } from 'lodash-es';
import styleToObject from 'style-to-object';

export default function ComponentStyle() {
  const [form] = Form.useForm();
  const { curComponentId, curComponent, updateComponentStyles } = useComponentsStore();
  const { componentConfig } = useComponentConfigStore();
  const [css, setCss] = useState<string>(`.comp{\n\n}`);

  useEffect(() => {
    form.resetFields();
    const data = form.getFieldsValue();
    form.setFieldsValue({...data, ...curComponent?.styles});
    setCss(toCSSStr(curComponent?.styles!))
  }, [curComponent])

  function toCSSStr(css: Record<string, any>) {
    let str = `.comp {\n`;
    for(let key in css) {
        let value = css[key];
        if(!value) {
            continue;
        }
        if(['width', 'height'].includes(key) &&  !value.toString().endsWith('px')) {
            value += 'px';
        }
        str += `\t${key}: ${value};\n`
    }
    str += `}`;
    return str;
  }

  if (!curComponentId || !curComponent) return null;

  function renderFormElememt(setting: ComponentSetter) {
    const { type, options } = setting;
    if (type === 'select') {
      return <Select options={options} />
    } else if (type === 'input') {
      return <Input />
    } else if (type === 'inputNumber') {
        return <InputNumber />
    }
  }

  function valueChange(changeValues: CSSProperties) {
    if (curComponentId) {
        updateComponentStyles(curComponentId, changeValues);
    }
  }

  const handleEditorChange = debounce((value) => {
    setCss(value);
    let css: Record<string, any> = {};
    try {
        const cssStr = value.replace(/\/\*.*\*\//, '') // 去掉注释 /** */
            .replace(/(\.?[^{]+{)/, '') // 去掉 .comp {
            .replace('}', '');// 去掉 }
        styleToObject(cssStr, (name, value) => {
            css[name.replace(/-\w/, (item) => item.toUpperCase().replace('-', ''))] = value;
        });
        updateComponentStyles(curComponentId, {...form.getFieldsValue(), ...css});
    } catch(e) {}
  }, 500);

  return (
    <Form
      form={form}
      onValuesChange={valueChange}
      labelCol={{ span: 8 }}
      wrapperCol={{ span: 14 }}
    >
      {
        componentConfig[curComponent.name]?.stylesSetter?.map(setter => (
          <Form.Item key={setter.name} name={setter.name} label={setter.label}>
            {renderFormElememt(setter)}
          </Form.Item>
        ))
      }
      <div className='h-[200px] border-[1px] border-[#ccc] z-10'>
        <CssEditor value={ css } onChange={handleEditorChange}/>
      </div>
    </Form>
  )
}

原理分析

  • 支持表单化与代码化双模式编辑,满足不同开发者习惯。
  • 样式 setter 配置灵活,支持扩展多种样式属性。
  • CSS 代码编辑区与表单数据实时同步,提升体验。

效果展示:

image.png

4. 事件面板(ComponentEvent):扩展能力预留

目前事件面板为占位符,后续可扩展为事件绑定、交互逻辑配置等高级功能。

import React from 'react'
export default function ComponentEvent() {
  return (
    <div>ComponentEvent</div>
  )
}

5. 设置区数据驱动机制

设置区的属性和样式编辑能力,均依赖于组件仓库的 setter 和 stylesSetter 配置: 当点击选中画布区域的组件时,右侧设置面板(setting)会展示该组件的属性,具体实现方式如下:

  1. 为每个组件对象新增了setter属性,其类型为数组,用于存储该组件所有可修改的属性。
  2. 为每组对象新增了stylesSetter数组,专门用于存放用户自定义的样式。这些自定义样式会通过style-to-object工具转换为对象(obj)格式,并嵌入到仓库的 JSON 数据中。

6. 总结

设置区通过属性、样式、事件三大模块,结合 setter/stylesSetter 配置,实现了灵活、可扩展的组件编辑体验。无论是业务属性、样式美化还是事件扩展,都能一站式搞定,让你的低代码平台“遥控器”更强大!(๑•̀ㅂ•́)و✧

项目所有代码均已整理至以下仓库,方便大家查看与使用:
→ 代码仓库地址内附项目体验地址