React实现插入变量输入框

React实现变量输入框

    • 组件功能描述
    • 组件效果
    • 组件使用示例
    • 组件实现代码
    • 查看视频效果图代码

组件功能描述

本组件结合antd中的Form和Input组件实现一个变量输入框,

  1. 该输入框可插入多个变量,其中变量格式为[X1]、[X2]、[X3]、[X4]、[X5]
  2. 删除变量右侧的‘]’符号可整个删除该变量;
  3. 删除变量后,再次插入变量时会计算变量模版中的最小变量值,插入最小变量值,如目前输入框内有变量[X1]、[X2]、[X3],删除变量[X2]后再次插入变量会插入[X2],而不是[X4];
  4. 插入变量和输入框输入操作会进行格式校验,不允许在变量内插入其他值,会报错提示:不能对变量标志中进行插入变量!;
  5. 插入变量时进行数量校验,不可超过最大变量数,超出会提示:最多插入5个变量!;
  6. 该组件主要提供实现思路,大家可根据自己的需求调整组件中变量格式和个数,需注意必须明确变量格式和数量;

组件效果

视频效果

插入变量效果图

图片示意图
静态图片

组件使用示例

// 在标题内插入变量
 <VariableInput
    label="标题"
    name="title"
    value='[X1]啦,这里是[X2]'
    variableChange={(value: string) => console.log(value)}
  />

组件实现代码

import { PlusCircleOutlined } from "@ant-design/icons";
import { Input, Form, message } from "antd";
import { TextAreaRef } from "antd/lib/input/TextArea";
import React, { FC, useEffect, useRef, useState } from "react";
import s from './index.module.less';

interface VariableInputProps {
  value: string; // 输入框初始默认值
  label: string; // FormItem 标题
  name: string; // FormItem 参数名称
  variableChange: (value: string) => void; // 输入框变化的回调函数
}

export const VARIABLE_REG = /\[X(.*?)\]/g; // 正则匹配变量[X..]
export const VARIABLE_ARRAY = ['[X1]', '[X2]', '[X3]', '[X4]', '[X5]']; // 明确的所有变量模版

const VariableInput: FC<VariableInputProps> = ({ value, label, name, variableChange }) => {
  const [inputValue, setInputValue] = useState<string>('');
  const inputRef = useRef<TextAreaRef>(null);
  const [variableArray, setVariableArray] = useState<string[]>([]);

  useEffect(() => {
    const variableArr = value.match(VARIABLE_REG) || [];
    setInputValue(value);
    setVariableArray(variableArr);
  }, [value]);

  // 获取插入变量的起始Index
  const getInsertInitialIndex = () => {
    if (variableArray.length === 0) return 1;
    const variableIndexArray = variableArray.map(itm => {
      const index = itm.match(/\d/);
      if (Array.isArray(index) && index.length > 0) {
        return Number(index[0]);
      }
    });
    const arr = [1, 2, 3, 4, 5].filter(itm => !variableIndexArray.includes(itm));
    return arr[0]
  }

  // 插入变量
  const insertVariable = () => {
    const initialIndex = getInsertInitialIndex()
    const textareaElement = inputRef?.current?.resizableTextArea?.textArea; // 获取textArea元素
    const cursorPosition = textareaElement?.selectionStart || 0; // 获取光标起始位置
    const previousText = inputValue.substr(0, cursorPosition); // 获取变量前文本
    const followingText = inputValue.substr(cursorPosition); // 获取变量后文本
    const initialValues = `${previousText}[X${initialIndex}]${followingText}`;
    // 获取插入变量
    const variableArr = initialValues.match(VARIABLE_REG) || [];
    if (variableArr.length > 5) {
      return message.error('最多插入5个变量!');
    }
    const isCompliant = variableArr.every(itm => VARIABLE_ARRAY.includes(itm))
    if (!isCompliant) {
      return message.error('不能对变量标志中进行插入变量!');
    }

    setInputValue(initialValues);
    setVariableArray(variableArr);
    variableChange?.(initialValues);

    // 插入操作完成后 将光标定位到插入内容的后面
    setTimeout(() => {
      inputRef?.current?.focus();
    });
  };

  // 键盘输入
  const handleChangeInput = (e: any) => {
    const initialValues = e?.target?.value;
    const reg = /\[X([^[\]]*?)\]/g;
    // 当前输入框内匹配的插入变量
    const variableArr = initialValues.match(reg) || [];
    // 判断是否合规
    const isCompliant = variableArr.every((itm: string) => VARIABLE_ARRAY.includes(itm))
    if (!isCompliant) {
      return message.error('不能对变量标志中进行插入变量!');
    }
    // 判断是否手动删除变量,当前输入框内的变量数组与原变量数组对比,如果缺了就说明删除了一个变量
    const deleteVariable = variableArray.filter((itm: string) => !variableArr.includes(itm));
    let deletedString = initialValues;
    if (deleteVariable.length > 0) {
      deletedString = inputValue.replace(deleteVariable[0], '');
    }
    setVariableArray(variableArr);
    setInputValue(deletedString);
    variableChange?.(deletedString);
  }

  return (
    <React.Fragment>
      <Form.Item label={label} name={name}>
        <div className={s.variableModule}>
          <div className={s.insertBtnBox}>
            <PlusCircleOutlined style={{ cursor: 'pointer' }} onClick={insertVariable} />
            <span style={{ cursor: 'pointer' }} onClick={insertVariable}>插入变量</span>
          </div>
          <Input.TextArea value={inputValue} ref={inputRef} onChange={handleChangeInput} />
        </div>
      </Form.Item>
    </React.Fragment>

  );
}

export default VariableInput;
.variableModule {
  width: 600px;
  :global {
    .ant-input {
      border: 1px solid #eceef1;
      border-top: none;
    }
  }
}

.insertBtnBox {
  display: flex;
  align-items: center;
  padding: 4px 12px;
  gap: 4px;
  height: 32px;
  background: #f8f9fa;
  border-bottom: 1px solid #eceef1;
  color: #0084FF;
}

查看视频效果图代码

视频效果图中效果更加丰满一点,输入框下还包括一个与输入框内的变量联动的表格,表格内可实现删除变量,设置变量对应的值,右侧还有一个预览效果,由于实现功能比较丰满,逻辑更为复杂,包括一个变量输入组件+可编辑表格组件 = 组合效果,具体效果图中的实现代码可至我的git仓库内查看,有问题欢迎私信,我会在空闲时间100%解答奥~♥️

你可能感兴趣的:(组件,react.js,前端,typescript,less)