低代码动态属性和在线执行脚本——低代码知识点详解(三)

前言

这一篇我们来实现一下组件属性绑定变量修改变量在线执行脚本低代码常用功能。

往期回顾

《保姆级》低代码知识点详解(一)
低代码事件绑定和组件联动------低代码知识点详解(二)

组件属性绑定变量

前言

为了能做出更复杂的页面,组件属性支持绑定变量是低代码必不可少的功能,下面我们实现一下这个功能。

定义变量

就像写代码一样,想使用变量,需要先定义变量,所以我们先实现定义变量功能。

变量数据结构

ts 复制代码
interface Variable {
  // 变量名
  name: string,
  // 变量类型
  type: string,
  // 默认值
  defaultValue: string;
  // 备注
  remark: string;
}

定义一个存放变量数据的store

ts 复制代码
// src/editor/stores/variable.ts

import {create} from 'zustand';

export interface Variable {
  /**
   * 变量名
   */
  name: string;
  /**
   * 默认值
   */
  defaultValue: string;
  /**
   * 备注
   */
  remark: string;
}

interface State {
  variables: Variable[];
}

interface Action {
  /**
   * 添加组件
   * @param component 组件属性
   * @param parentId 上级组件id
   * @returns
   */
  setVariables: (variables: Variable[]) => void;
}

export const useVariablesStore = create<State & Action>((set) => ({
  variables: [],
  setVariables: (variables) => set({variables}),
}));

这里使用antdForm.List组件快速实现功能,type先写死一个string类型,后面可以拓展很多类型,比如bool、number和更高级的json和接口等。

tsx 复制代码
// src/editor/layouts/header/define-variable.tsx

import { MinusCircleOutlined, PlusOutlined } from '@ant-design/icons';
import { Button, Form, Input, Modal, Select, Space } from 'antd';
import React, { useEffect } from 'react';
import { useVariablesStore } from '../../stores/variable';

interface Props {
  open: boolean,
  onCancel: () => void
}

interface Variable {
  // 变量名
  name: string,
  // 变量类型
  type: string,
  // 默认值
  defaultValue: string;
  // 备注
  remark: string;
}

const DefineVariable: React.FC<Props> = ({ open, onCancel }) => {

  const [form] = Form.useForm();
  const { setVariables, variables } = useVariablesStore();


  function onFinish(values: { variables: Variable[] }) {
    setVariables(values.variables);
    onCancel && onCancel();
  }

  useEffect(() => {
    if (open) {
      form.setFieldsValue({ variables });
    }
  }, [open])

  return (
    <Modal
      open={open}
      title="定义变量"
      onCancel={onCancel}
      destroyOnClose
      onOk={() => { form.submit() }}
      width={700}
    >
      <Form<{ variables: Variable[] }>
        onFinish={onFinish}
        autoComplete="off"
        className='py-[20px]'
        form={form}
        initialValues={{ variables }}
      >
        <Form.List name="variables">
          {(fields, { add, remove }) => (
            <>
              {fields.map(({ key, name, ...restField }) => (
                <Space key={key} style={{ display: 'flex', marginBottom: 8 }} align="baseline">
                  <Form.Item
                    {...restField}
                    name={[name, 'name']}
                    rules={[{ required: true, message: '变量名不能为空' }]}
                  >
                    <Input placeholder="变量名" />
                  </Form.Item>
                  <Form.Item
                    {...restField}
                    name={[name, 'type']}
                  >
                    <Select style={{ width: 140 }} options={[{ label: '字符串', value: 'string' }]} placeholder="类型" />
                  </Form.Item>
                  <Form.Item
                    {...restField}
                    name={[name, 'defaultValue']}
                  >
                    <Input placeholder="默认值" />
                  </Form.Item>
                  <Form.Item
                    {...restField}
                    name={[name, 'remark']}
                  >
                    <Input placeholder="备注" />
                  </Form.Item>
                  <MinusCircleOutlined onClick={() => remove(name)} />
                </Space>
              ))}
              <Form.Item>
                <Button type="dashed" onClick={() => add({ type: 'string' })} block icon={<PlusOutlined />}>
                  添加变量
                </Button>
              </Form.Item>
            </>
          )}
        </Form.List>
      </Form>
    </Modal>
  )
};

export default DefineVariable;

头上工具栏再加一个定义变量的按钮控制定义变量弹框显示和隐藏。

效果展示

绑定变量

所有组件属性都需要支持绑定变量,所以需要让属性配置的表单元素都支持选择变量,我们给这些表单组件封装一下,方便使用。

下面是组件属性新数据结构,分为静态和变量两种类型。

ts 复制代码
interface Value {
  type: 'static' | 'variable';
  value: any;
}

封装支持设置变量的输入框

tsx 复制代码
// src/editor/common/setting-form-item/input.tsx

import { SettingOutlined } from '@ant-design/icons';
import { Input } from 'antd';
import { useState } from 'react';
import SelectVariableModal from '../select-variable-modal';

interface Value {
  type: 'static' | 'variable';
  value: any;
}

interface Props {
  value?: Value,
  onChange?: (value: Value) => void;
}

const SettingFormItemInput: React.FC<Props> = ({ value, onChange }) => {

  const [visible, setVisible] = useState(false);

  function valueChange(e: any) {
    onChange && onChange({
      type: 'static',
      value: e?.target?.value,
    });
  }

  function select(record: any) {
    onChange && onChange({
      type: 'variable',
      value: record.name,
    });

    setVisible(false);
  }


  return (
    <div className='flex gap-[8px]'>
      <Input
        disabled={value?.type === 'variable'}
        value={(value?.type === 'static' || !value) ? value?.value : ''}
        onChange={valueChange}
      />
      <SettingOutlined
        onClick={() => { setVisible(true) }}
        className='cursor-pointer'
        style={{ color: value?.type === 'variable' ? 'blue' : '' }}
      />
      <SelectVariableModal
        open={visible}
        onCancel={() => { setVisible(false) }}
        onSelect={select}
      />
    </div>
  )
}

export default SettingFormItemInput;

属性设置组件里antdInput改成刚封装SettingFormItemInput组件

效果展示

渲染的时候,根据类型处理props

效果展示

设置变量

事件再添加一个设置变量的动作类型,选择设置变量后,先选择一个要设置的变量,然后输入变量的值。

效果展示

先定义一个存放变量值的store,然后在点击事件里调用setData给变量设置值。

tsx 复制代码
// src/editor/stores/page-data.ts

import {create} from 'zustand';

interface State {
  data: any;
}

interface Action {
  /**
   * 设置变量值
   * @param component key
   * @param parentId 值
   * @returns
   */
  setData: (key: string, value: any) => void;
  /**
   * 重置数据
   * @returns
   */
  resetData: () => void;
}

export const usePageDataStore = create<State & Action>((set) => ({
  data: {},
  setData: (key, value) =>
    set((state) => ({data: {...state.data, [key]: value}})),
  resetData: () => set({data: {}}),
}));

事件处理那里再加一个动作类型判断,这一块其实可以用策略模式优化一下代码,但是这是demo,先不优化了。

前面渲染那里改造一下,先从data中取值,取不到再使用变量的默认值。

效果展示

动态执行脚本

前言

js中常用的动态执行脚本方式有两种,一个是使用eval,另外一个是new Function,这里我们使用new Function,传参方便一点。

实战

动态执行脚本依然是由事件触发的,所以给事件添加一个执行脚本的动作类型。

脚本最好使用代码编辑器来编写,因为这里是demo,先用文本输入框代替,后面实战时会用代码编辑器的。

我们把一些常用的方法注入到ctx中,可以在脚本里调用我们注入的方法,比如设置变量值方法,和调用某个组件方法等。

封装执行脚本方法,把设置变量值方法和获取组件实例方法注入到ctx中,代码很简单。

事件那里加一个执行脚本动作

设置变量值

执行组件方法

最后

到此我们简单的实现了组件属性绑定变量修改变量在线执行脚本低代码常用功能,下一篇我们来实现动态加载远程组件

demo体验地址:dbfu.github.io/lowcode-dem...

demo仓库地址:github.com/dbfu/lowcod...

相关推荐
学习ing小白24 分钟前
JavaWeb - 5 - 前端工程化
前端·elementui·vue
真的很上进1 小时前
【Git必看系列】—— Git巨好用的神器之git stash篇
java·前端·javascript·数据结构·git·react.js
胖虎哥er1 小时前
Html&Css 基础总结(基础好了才是最能打的)三
前端·css·html
qq_278063711 小时前
css scrollbar-width: none 隐藏默认滚动条
开发语言·前端·javascript
.ccl1 小时前
web开发 之 HTML、CSS、JavaScript、以及JavaScript的高级框架Vue(学习版2)
前端·javascript·vue.js
小徐不会写代码1 小时前
vue 实现tab菜单切换
前端·javascript·vue.js
2301_765347541 小时前
Vue3 Day7-全局组件、指令以及pinia
前端·javascript·vue.js
喝旺仔la1 小时前
VSCode的使用
java·开发语言·javascript
ch_s_t2 小时前
新峰商城之分类三级联动实现
前端·html
辛-夷2 小时前
VUE面试题(单页应用及其首屏加载速度慢的问题)
前端·javascript·vue.js