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

前言

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

往期回顾

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

组件属性绑定变量

前言

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

定义变量

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

变量数据结构

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...

相关推荐
cs_dn_Jie7 分钟前
钉钉 H5 微应用 手机端调试
前端·javascript·vue.js·vue·钉钉
开心工作室_kaic41 分钟前
ssm068海鲜自助餐厅系统+vue(论文+源码)_kaic
前端·javascript·vue.js
有梦想的刺儿1 小时前
webWorker基本用法
前端·javascript·vue.js
cy玩具1 小时前
点击评论详情,跳到评论页面,携带对象参数写法:
前端
清灵xmf2 小时前
TypeScript 类型进阶指南
javascript·typescript·泛型·t·infer
小白学大数据2 小时前
JavaScript重定向对网络爬虫的影响及处理
开发语言·javascript·数据库·爬虫
qq_390161772 小时前
防抖函数--应用场景及示例
前端·javascript
334554323 小时前
element动态表头合并表格
开发语言·javascript·ecmascript
John.liu_Test3 小时前
js下载excel示例demo
前端·javascript·excel
Yaml43 小时前
智能化健身房管理:Spring Boot与Vue的创新解决方案
前端·spring boot·后端·mysql·vue·健身房管理