记录那些工作中提升效率的函数方法

555 阅读8分钟

我们大部分开发同学,90%时间都与业务在打交道,所以我整理了些我工作中那些减少工作量的函数和方法,分享给各位!(持续补充中...)

一、 处理金额的千分位

背景:由于去年的时候换了新的单位,开始进入金融领域,所以需求上,跟金额打交道也越来越多,需求:在处理数据的过程中,我们拿到的金额常常是以分为单位的一串数字,根据需求我们要换算成单位为‘元’的千分位数据,下面就几种场景分别出了不同的方案:

  1. 简版,保留两位小数,接收一个number,返回 number
const handleThousands = (amount: any) => {
  let num;
  const value = parseFloat(amount).toFixed(2);

  if (value.indexOf('.') === -1) {
    num = value.replace(/\d{1,3}(?=(\d{3})+$)/g, s => `${s},`);
  } else {
    num = value.replace(/(\d)(?=(\d{3})+\.)/g, s => `${s},`);
  }

  return num;
};
/**
 *
 * @param amountValue 需要格式化的数值
 * @param isCents 默认数据是以'分'为单位,如果数据是以'元'为单位,传第二个字段为'false'即可
 */

export const handleAmount = (amountValue = 0, isCents = true): number => {
  if (amountValue) {
    const normalAmountValue = isCents ?  amountValue / 100 :  amountValue;

    return handleThousands(normalAmountValue);
  }
  return 0;
};

a. 以为单位(默认不需要传第二个参数):

console.log(handleAmount(788000));      // 7,880.00

b. 以为单位(第二个参数给 false):

console.log(handleAmount(788000, false));  // 788,000.00
  1. 升级版,产品经理有特殊要求的,根据情况保留小数位,添加相应单位,接收number,返回string
  • 具体需求:
  • 1、【小于100万】使用单位(元),保留0位小数。示例:¥ 234,500元
  • 2、【大于等于100万,小于1亿】使用单位(万),保留2位小数。示例:¥ 4,500.25万
  • 3、【大于等于1亿】使用单位(亿),保留4位小数。示例:¥ 5.3541亿
const handleThousands = (amount, val) => {
  let result, num;
  let value = parseFloat(amount).toFixed(val);

  if (value.indexOf('.') === -1) {
    num = value.replace(/\d{1,3}(?=(\d{3})+$)/g, function(s) {
      return s + ',';
    });
  } else {
    num = value.replace(/(\d)(?=(\d{3})+\.)/g, function(s) {
      return s + ',';
    });
  }

  if (val === 2) {
    result = `¥ ${num}万`;
  } else if (val === 4) {
    result = `¥ ${num}亿`;
  } else {
    result = `¥ ${num}元`;
  }
  return result;
};

/**
 *
 * @param {number} amountValue  需要格式化的数值,
 * @return {string} 带货币单位和货币符号 例如:`¥ 100元`;
 */
export const handleAmount = (amountValue: number): string => {
  if (amountValue) {
    const oldAmountValue = amountValue;
    const normalAmountValue = oldAmountValue / 100;
    if (normalAmountValue > 100000000) {
      return handleThousands(normalAmountValue / 100000000, 4);
    } else if (normalAmountValue > 1000000) {
      return handleThousands(normalAmountValue / 10000, 2);
    } else {
      return handleThousands(normalAmountValue, 0);
    }
  } else {
    return '¥ 0';
  }
};

a. 以元为单位

console.log(handleAmount(788000));   // ¥ 7,880元

b. 以万为单位

console.log(handleAmount(788000000));  //  ¥ 788.00万

c. 以亿为单位

console.log(handleAmount(788000000000));  // ¥ 78.8000亿

二、小数转百分比数值

我根据使用场景给了两种模式:

  • 严格模式:向下取舍,保留两位小数;
  • 非严格自由模式:四舍五入,保留小数位数可自定义
/**
 * 小数转百分比,默认使用精确计量(不进行四舍五入),并保留两位小数
 * @param {number} value  传入的小数值 例如:0.223455
 * @param {boolean} percent 是否添加单位 ‘%’,默认添加,如不需‘%’,给false 即可 例如:false
 * @param {number} precision 四舍五入并可设置精确的小数位,默认不使用;  例如:2
 */
 
export const getDecimalplaces2percent = (
  value = 0,
  percent = true,
  precision = 0,
): string | number => {
  const count = precision ? (value * 100).toFixed(precision) : Math.floor(value * 100 * 100) / 100;
  if (!percent) {
    return count;
  }
  return `${count}%`;
};

调用:

a. 默认四舍五入,保留两位小数

console.log(getDecimalplaces2percent(0.234343434));    //  23.43%

b. 纯数字,不带百分号

console.log(getDecimalplaces2percent(0.234343434, false));   //  23.43

c. 设置精确的小数位

console.log(getDecimalplaces2percent(0.234343434, true, 4));  // 23.4343%

三、多环境的配置

我们的项目一般情况下会有多余1个或2个环境,那对于不同环境的配置我们如何来优雅的取到不同的参数?

const { ENV } = window;

const settingsDict = {
  local: {
    adminUrl: 'https://admin-qa.xxxxxxxxx.com',
    courseUrl: 'https://app-qa.xxxxxxxxx.com',
    insuranceUrl: 'https://insurance-qa.xxxxxxxxx.com',
  },
  qa: {
    adminUrl: 'https://admin-qa.xxxxxxxxx.com',
    courseUrl: 'https://app-qa.xxxxxxxxx.com',
    insuranceUrl: 'https://insurance-qa.xxxxxxxxx.com',
  },
  staging: {
    adminUrl: 'https://admin-staging.xxxxxxxxx.com',
    courseUrl: 'https://app-staging.xxxxxxxxx.com',
    insuranceUrl: 'https://insurance-staging.xxxxxxxxx.com',
  },
  production: {
    adminUrl: 'https://admin.xxxxxxxxx.com',
    courseUrl: 'https://app.xxxxxxxxx.com',
    insuranceUrl: 'https://i.xxxxxxxxx.com',
  },
};

const config = settingsDict[ENV] || settingsDict.local;

const settings = {
  ...config,

  getAdminUrl: () => config.adminUrl,

  getInsuranceUrl: (pathname) =>`config.insuranceUrl${pathname}`,
};

export default settings;

把对应的配置写成一个配置文件,通过拿到当前环境,返回对应url,同样也可传需要的参数,拼接成想要的地址。

方法调用:

settings.getInsuranceUrl('detail')   => https://insurance-qa.xxxxxxxxx.com/detail

四、多区间条件匹配的优化

多区间逻辑判断:把每个条件和返回的message信息用对象的方式整合,然后用循环的方式选取出来。

export const getGreetingMessage = name => {
  const hour = new Date().getHours();

  const messages = [
    {
      check: () => hour < 9 && hour >= 5,
      format: () => `早安,${name},每天都是新的一天!`,
    },
    {
      check: () => hour < 12 && hour >= 9,
      format: () => `上午好,${name},开始一天新的工作吧!`,
    },
    {
      check: () => hour < 14 && hour >= 12,
      format: () => `中午好,${name},吃顿好的犒劳一下自己吧!`,
    },
    {
      check: () => hour < 19 && hour >= 14,
      format: () => `下午好,${name},集中精力向目标前进!`,
    },
    {
      check: () => hour < 23 && hour >= 19,
      format: () => `晚上好,${name},又向目标前进了一步哦!`,
    },
    {
      check: () => hour >= 23,
      format: () => '夜深喽,早些休息吧!',
    },
  ];

  let message = '你好';
  
   messages.forEach(item => {
    if (item.check()) {
      message = item.format(name);
    }
  });

  return message;
};

五、给img 拼接宽高

imgurl 地址拼接宽高

interface IImageResponse {
  width: number;
  height: number;
  url: string;
}

/**
 * 处理img,拼接宽、高、url
 * @param {string[]} imgArr  接受一个包含图片链接的img数组
 * @return {IImageResponse[]} 返回一个拼接宽、高 、url链接的对象数组
 */

export const handleImgInfos = (imgArr: string[]): IImageResponse[] => {
  const images = [];
  // 创建 img
  imgArr.forEach(item => {
    const image = new Image();
    image.src = item;
    image.onload = () => {
      const { width, height } = image;
      images.push({ width, height, url: item });
    };
  });
  return images;
};

调用方式:

// 入参包含图片链接的数组
const imgStr='http://f3.v.veimg.cn/meadincms/1/2015/1123/20151123081509321.jpg'

console.log(
    handleImgInfos(['http://f3.v.veimg.cn/meadincms/1/2015/1123/20151123081509321.jpg'])
  );
  // [{width: 500, height: 280, url: "http://f3.v.veimg.cn/meadincms/1/2015/1123/20151123081509321.jpg"}]

六、动态增减表单项的解决方案

我们正常的表单都是每个item绑定一个key,当我们有需求需要动态增减表单项,就需要单独处理,此处就是应对表单处理过程中的的场景,有两种方式推荐给大家:

  • 外部隔离:通过创建多个子form表单,用form表单来实现隔离
  • 内部接力:通过绑定不同的key来实现每个item绑定不同id来区分

基础场景:

image.png

1. 外部隔离

外部隔离的方式,是通过创建多个子form,来实现子form 和 父form隔离,在最后提交步骤,拿到子组件formformList及父组件的form

上代码--------->

父组件:

import React, { useState, useCallback } from 'react';
import useForm from 'rc-form-hooks';
import { Form, Input, Button, Divider } from 'antd';
import { v4 as uuidv4 } from 'uuid';
import CardLayout from '@xb/layouts/CardLayout';
import Form1 from './widgets/Form1';

export const formLayout = {
  labelCol: { span: 5 },
  wrapperCol: { span: 15 },
};
const TestForm = () => {
  const form = useForm();
  const { getFieldDecorator, validateFields } = form;
  const [formList, setFormList] = useState([]);
  const [msgList, setMsgList] = useState([{ id: uuidv4() }]);

  // 子组件添加formList中
  const getForm = useCallback(
    item => {
      setFormList(r => [...r, item]);
    },
    [formList],
  );

  // 添加
  const onAdd = useCallback(() => {
    setMsgList([...msgList, { id: uuidv4() }]);
  }, [msgList]);

  //删除
  const onDel = useCallback(
    i => {
      //页面 UI 删除
      const tmp = JSON.parse(JSON.stringify(msgList));
      tmp.splice(i, 1);
      setMsgList([...tmp]);

      // formList 数据删除
      formList.splice(i, 1);
      setFormList(formList);
    },
    [msgList, formList],
  );

  // 提交保存
  const handleSubmit = () => {
    Promise.all([...formList.map(item => item.validateFields()), validateFields()]).then(data => {
      console.log(data, '默认返回参数');
      const list = data.slice(0, formList.length);

      console.log({ ...data[data.length - 1], list }, '拼接后参数');
    });
  };

  return (
    <CardLayout>
      <Form {...formLayout}>
        <Form.Item label="名称">
          {getFieldDecorator('name', {
            rules: [
              {
                required: true,
                message: '请输入名称',
              },
            ],
          })(<Input />)}
        </Form.Item>
        <Form.Item label="类型">
          {getFieldDecorator('type', {
            rules: [
              {
                required: true,
                message: '请输入类型',
              },
            ],
          })(<Input />)}
        </Form.Item>
        <Button onClick={onAdd}>添加</Button>

        {msgList.map((item, index) => {
          return (
            <Form1
              key={item.id}
              msgIndex={index}
              value={item}
              onDel={() => {
                onDel(index);
              }}
              getForm={getForm}
            />
          );
        })}

        <Divider />

        <Divider />
        <Form.Item style={{ marginTop: 30, marginLeft: '30%' }}>
          <Button type="primary" block onClick={handleSubmit}>
            查询
          </Button>
        </Form.Item>
      </Form>
    </CardLayout>
  );
};
export default TestForm;


子组件:

import React, { useEffect } from 'react';
import useForm from 'rc-form-hooks';
import { Form, Input, Row, Col, Icon } from 'antd';

interface IProps {
  msgIndex: number;
  value: any;
  onDel: () => void;
  getForm: (f) => void;
}
const formLayout = {
  labelCol: { span: 6 },
  wrapperCol: { span: 17 },
};
const Form1: React.FC<IProps> = props => {
  const { msgIndex, onDel } = props;
  const form = useForm();
  const { getFieldDecorator } = form;

  useEffect(() => {
    props.getForm(form);
  }, []);

  return (
    <Form {...formLayout}>
      <Row>
        <Col span={23}>
          <Form.Item label={`策略${msgIndex + 1}`}>
            {getFieldDecorator('strategy', {
              rules: [
                {
                  required: true,
                  message: '请输入策略',
                },
              ],
            })(<Input />)}
          </Form.Item>
        </Col>
        <Col span={1}>
          {msgIndex !== 0 && (
            <Icon
              type="minus-circle"
              style={{ fontSize: 16, marginTop: 10, marginLeft: -14 }}
              onClick={onDel}
            />
          )}
        </Col>
      </Row>
    </Form>
  );
};
export default Form1;

提交表单:

image.png

提交后的参数获取:

image.png

2. 内部接力

内部接力的方式,是通过绑定不同的key 来区分不同的item,不会创建多个form,最终提交时,还是同一个form,缺点就是还需要根据规则把每个item 拆分出来

上代码--------->

import React, { useCallback } from 'react';
import useForm from 'rc-form-hooks';
import { Form, Input, Button, Divider, Row, Col, Icon } from 'antd';
import { v4 as uuidv4 } from 'uuid';
import CardLayout from '@xb/layouts/CardLayout';

export const formLayout = {
  labelCol: { span: 5 },
  wrapperCol: { span: 15 },
};
const TestForm = () => {
  const form = useForm();
  const { getFieldDecorator, validateFields, getFieldValue, setFieldsValue } = form;

  getFieldDecorator('strategy', { initialValue: [{ id: uuidv4() }] });
  const strategy = getFieldValue('strategy');

  // 添加
  const onAddMsg = useCallback(() => {
    const strategyList = getFieldValue('strategy');
    const nextKeys = [...strategyList, { id: uuidv4() }];
    setFieldsValue({
      strategy: nextKeys,
    });
  }, [getFieldValue, setFieldsValue]);

  // 删除
  const onDel = useCallback(
    i => {
      const strategyList = getFieldValue('strategy');
      strategyList.splice(i, 1);
      setFieldsValue({
        strategy: strategyList,
      });
    },
    [getFieldValue, setFieldsValue],
  );

  // 表单提交
  const handleSubmit = () => {
    validateFields().then(data => {
      console.log(data, '默认返回参数');
      let list = [];
      const { name, type, strategy, ...strategyList } = data;
      strategy.forEach(item => {
        list.push({ strategy: strategyList[`strategy[${item.id}]`] });
      });
      console.log({ name, type, list }, '拼接后参数');
    });
  };

  return (
    <CardLayout>
      <Form {...formLayout}>
        <Form.Item label="名称">
          {getFieldDecorator('name', {
            rules: [
              {
                required: true,
                message: '请输入名称',
              },
            ],
          })(<Input />)}
        </Form.Item>
        <Form.Item label="类型">
          {getFieldDecorator('type', {
            rules: [
              {
                required: true,
                message: '请输入类型',
              },
            ],
          })(<Input />)}
        </Form.Item>
        <Button onClick={onAddMsg}>添加</Button>

        <Divider />

        {strategy.map((k, index) => {
          return (
            <Row>
              <Col span={23}>
                <Form.Item label={`策略${index + 1}`}>
                  {getFieldDecorator(`strategy[${k.id}]`, {
                    rules: [
                      {
                        required: true,
                        message: '请输入策略',
                      },
                    ],
                  })(<Input />)}
                </Form.Item>
              </Col>
              <Col span={1}>
                {index !== 0 && (
                  <Icon
                    type="minus-circle"
                    style={{ fontSize: 16, marginTop: 10, marginLeft: -14 }}
                    onClick={onDel}
                  />
                )}
              </Col>
            </Row>
          );
        })}

        <Form.Item style={{ marginTop: 30, marginLeft: '30%' }}>
          <Button type="primary" block onClick={handleSubmit}>
            查询
          </Button>
        </Form.Item>
      </Form>
    </CardLayout>
  );
};
export default TestForm;


提交表单:

image.png

提交后的参数获取:

image.png

升级场景:

在实现完成上面的场景后,产品提出了新的需求,在原来的动态表单上又新增了一层,实现群组的表单动态增减,如下图:

image.png image.png

首先我们上面讲过了单层的动态增减表单,下面我们就重点看一下动态增减一个嵌套的form怎么实现:

最外层的组件 index.tsx

import React, { useState, useCallback } from 'react';
import useForm from 'rc-form-hooks';
import { Form, Input, Button, Divider } from 'antd';
import { v4 as uuidv4 } from 'uuid';
import CardLayout from '@xb/layouts/CardLayout';
import Form1 from './widgets/Form1';
import Form2 from './widgets/Form2';

export const formLayout = {
  labelCol: { span: 5 },
  wrapperCol: { span: 15 },
};
const TestForm = () => {
  const form = useForm();
  const { getFieldDecorator, validateFields } = form;
  const [formList, setFormList] = useState([]);
  const [groupFormList, setGroupFormList] = useState([]);
  const [msgList, setMsgList] = useState([{ id: uuidv4() }]);
  const [msgGroupList, setGroupMsgList] = useState([{ id: uuidv4() }]);
  const [subFormList, setSubFormList] = useState([{ id: uuidv4(), form: [] }]);

  // 添加formList
  const getForm = useCallback(
    item => {
      setFormList(r => [...r, item]);
    },
    [formList],
  );
  // 添加groupFormList
  const getGroupForm = useCallback(item => {
    setGroupFormList(r => [...r, item]);
  }, []);

  // 添加单个
  const onAdd = useCallback(() => {
    setMsgList([...msgList, { id: uuidv4() }]);
  }, [msgList]);

  //添加群组
  const onAddGroup = useCallback(() => {
    const id = uuidv4();
    setGroupMsgList([...msgGroupList, { id }]);
    setSubFormList([...subFormList, { id, form: [] }]);
  }, [msgGroupList, subFormList]);

  //删除
  const onDel = useCallback(
    i => {
      //页面 UI 删除
      const tmp = JSON.parse(JSON.stringify(msgList));
      tmp.splice(i, 1);
      setMsgList([...tmp]);

      // formList 数据删除
      formList.splice(i, 1);
      setFormList(formList);
    },
    [msgList, formList],
  );

  // 删除群组
  const onDelGroup = (i: number) => {
    //页面 UI 删除
    const tmp = JSON.parse(JSON.stringify(msgGroupList));
    tmp.splice(i, 1);
    setGroupMsgList([...tmp]);
    // 数据删除
    groupFormList.splice(i, 1);
    subFormList.splice(i, 1);
    setGroupFormList(groupFormList);
    setSubFormList(subFormList);
  };

  const handleSubmit = () => {
    const list = subFormList.map(item => item.form);
    Promise.all([
      ...formList.map(item => item.validateFields()),
      ...groupFormList.map(item => item.validateFields()),
      ...list.flat().map(item => item.validateFields()),
      validateFields(),
    ]).then(data => {
      console.log(data, '结果');
      const singleData = data.slice(0, formList.length);
      const groupData = [...data.slice(formList.length, groupFormList.length + formList.length)];

      console.log(singleData, groupData);
      groupFormList.map((item, index) => {
        Promise.all([...list[index].map(i => i.validateFields())]).then(value => {
          groupData[index]['list'] = value;
        });
      });

      const params = {
        ...data[data.length - 1],
        list: singleData,
        groupData,
      };
      console.log(params, '参数');
    });
  };

  return (
    <CardLayout>
      <Form {...formLayout}>
        <Form.Item label="名称">
          {getFieldDecorator('name', {
            rules: [
              {
                required: true,
                message: '请输入名称',
              },
            ],
          })(<Input />)}
        </Form.Item>
        <Form.Item label="类型">
          {getFieldDecorator('type', {
            rules: [
              {
                required: true,
                message: '请输入类型',
              },
            ],
          })(<Input />)}
        </Form.Item>
        <Button onClick={onAdd}>添加</Button>

        <Divider />
        {/* 单层策略 */}
        {msgList.map((item, index) => {
          return (
            <Form1
              key={item.id}
              msgIndex={index}
              onDel={() => {
                onDel(index);
              }}
              getForm={getForm}
            />
          );
        })}

        <Divider />

        <Button onClick={onAddGroup}>添加群组</Button>
        {/* 双层策略 */}
        {msgGroupList.map((item, index) => {
          return (
            <Form2
              key={item.id}
              msgIndex={index}
              onDelGroup={() => {
                onDelGroup(index);
              }}
              getGroupForm={getGroupForm}
              subFormList={subFormList}
              setSubFormList={setSubFormList}
            />
          );
        })}

        <Divider />
        <Form.Item style={{ marginTop: 30, marginLeft: '30%' }}>
          <Button type="primary" block onClick={handleSubmit}>
            查询
          </Button>
        </Form.Item>
      </Form>
    </CardLayout>
  );
};
export default TestForm;

单层策略组件 Form1.tsx:

import React, { useEffect } from 'react';
import useForm from 'rc-form-hooks';
import { Form, Input, Row, Col, Icon } from 'antd';
import { FormMethods } from 'rc-form-hooks/dist/formHooks';

interface IProps {
  msgIndex: number;
  onDel: () => void;
  getForm: (f: FormMethods<any>) => void;
}
const formLayout = {
  labelCol: { span: 6 },
  wrapperCol: { span: 17 },
};
const Form1: React.FC<IProps> = props => {
  const { msgIndex, onDel } = props;
  const form = useForm();
  const { getFieldDecorator } = form;

  useEffect(() => {
    props.getForm(form);
  }, []);

  return (
    <Form {...formLayout}>
      <Row>
        <Col span={23}>
          <Form.Item label={`策略${msgIndex + 1}`}>
            {getFieldDecorator('strategy', {
              rules: [
                {
                  required: true,
                  message: '请输入策略',
                },
              ],
            })(<Input />)}
          </Form.Item>
        </Col>
        <Col span={1}>
          {msgIndex !== 0 && (
            <Icon
              type="minus-circle"
              style={{ fontSize: 16, marginTop: 10, marginLeft: -14 }}
              onClick={onDel}
            />
          )}
        </Col>
      </Row>
    </Form>
  );
};
export default Form1;


双层策略组件 Form2->index.tsx

import React, { useEffect, useState, useCallback } from 'react';
import useForm from 'rc-form-hooks';
import { Form, Input, Button } from 'antd';
import { v4 as uuidv4 } from 'uuid';
import { FormMethods } from 'rc-form-hooks/dist/formHooks';
import SubPage from './SubPage';

interface IProps {
  msgIndex: number;
  onDelGroup: () => void;
  getGroupForm: (f: FormMethods<any>) => void;
  subFormList: any;
  setSubFormList: any;
}
const formLayout = {
  labelCol: { span: 5 },
  wrapperCol: { span: 15 },
};

const Form2: React.FC<IProps> = props => {
  const { msgIndex, onDelGroup, subFormList, setSubFormList } = props;
  const form = useForm();
  const { getFieldDecorator } = form;
  const [msgList, setMsgList] = useState([]);
  const [formList, setFormList] = useState([]);

  // 添加formList
  const getForm = useCallback(
    (item, groupIndex) => {
      const list = subFormList.slice();
      setFormList([...formList, item]);
      console.log(list, '添加群组的子策略');
      // 把子组件的form存起来
      list[groupIndex].form = [...list[groupIndex].form, item];
      setSubFormList(list);
    },
    [formList, subFormList],
  );

  // 添加单个
  const onAdd = useCallback(() => {
    const id = uuidv4();
    setMsgList([...msgList, { id }]);
  }, [msgList]);

  //删除
  const onDel = useCallback(
    i => {
      //页面 UI 删除
      const tmp = JSON.parse(JSON.stringify(msgList));
      tmp.splice(i, 1);
      setMsgList([...tmp]);
      // 数据删除
      formList.splice(i, 1);
      setFormList(formList);
    },
    [msgList, formList],
  );

  useEffect(() => {
    props.getGroupForm(form);
  }, []);

  return (
    <Form {...formLayout}>
      <div
        style={{
          height: '100%',
          width: '100%',
          border: '1px solid pink',
          marginTop: 20,
          paddingLeft: 10,
        }}
      >
        群组{msgIndex + 1}
        <Form.Item label="类型">
          {getFieldDecorator('type', {
            rules: [
              {
                required: true,
                message: '请输入类型',
              },
            ],
          })(<Input />)}
        </Form.Item>
        <Button onClick={onAdd}>添加</Button>
        {msgIndex !== 0 && <Button onClick={onDelGroup}>删除群组</Button>}
        {msgList.map((item, index) => {
          return (
            <SubPage
              key={item.id}
              msgIndex={index}
              groupIndex={msgIndex}
              onDel={() => {
                onDel(index);
              }}
              getForm={getForm}
            />
          );
        })}
      </div>
    </Form>
  );
};
export default Form2;


双层嵌套的子组件 Form->Subpage.tsx

侧重点: 双层嵌套的内层的form如何和绑定外层,返回一个form列表

import React, { useEffect } from 'react';
import useForm from 'rc-form-hooks';
import { Form, Input, Row, Col, Icon } from 'antd';
import { FormMethods } from 'rc-form-hooks/dist/formHooks';

interface IProps {
  msgIndex: number;
  groupIndex: number;
  onDel: (i: number) => void;
  getForm: (f: FormMethods<any>, id: number) => void;
}

const formLayout = {
  labelCol: { span: 5 },
  wrapperCol: { span: 16 },
};

const SubPage: React.FC<IProps> = props => {
  const { msgIndex, groupIndex, onDel } = props;
  const form = useForm();
  const { getFieldDecorator } = form;

  useEffect(() => {
    props.getForm(form, groupIndex);
  }, [groupIndex]);

  return (
    <Form {...formLayout}>
      <Row>
        <Col span={23}>
          <Form.Item label={`策略${msgIndex + 1}`}>
            {getFieldDecorator('strategy', {
              rules: [
                {
                  required: true,
                  message: '请输入策略',
                },
              ],
            })(<Input />)}
          </Form.Item>
        </Col>
        <Col span={1}>
          {msgIndex !== 0 && (
            <Icon
              type="minus-circle"
              style={{ fontSize: 16, marginTop: 10, marginLeft: -50 }}
              onClick={() => {
                onDel(msgIndex);
              }}
            />
          )}
        </Col>
      </Row>
    </Form>
  );
};
export default SubPage;

七、把字符串作为 URI 组件进行编码、解码

  1. encodeURIComponent() 函数可把字符串作为 URI 组件进行编码。
encodeURIComponent("中文")

//   "%E4%B8%AD%E6%96%87"
  1. decodeURIComponent()函数可把encodeURIComponent编码完的数据进行编码
decodeURIComponent("%E4%B8%AD%E6%96%87")

//  "中文"

八、uuid生成随机字符串(比随机数高级的随机字符串)

平常我们用到随机数可能会想到的是如下:

Math.random() ⇨  0.3920174387754096

今天就推荐一个高逼格的生成随机字符串的方法:UUID

UUID 是通用唯一识别码(Universally Unique Identifier)的缩写,是一种软件建构的标准,亦为开放软件基金会组织在分布式计算环境领域的一部,其目的是让分布式系统中的所有元素,都能有唯一的辨识信息,而不需要通过中央控制端来做辨识信息的指定。如此一来,每个人都可以创建不与其它人冲突的UUID。在这样的情况下,就不需考虑数据库创建时的名称重复问题。

UUID是由一组32位数的16进制数字所构成,所以UUID理论上的总数为16^32=2^128,约等于3.4 x 10^38。也就是说若每纳秒产生1兆个UUID要花100亿年才会将所有UUID用完。

UUID的标准型式包含32个16进制数字,以连字号分为五段,形式为8-4-4-4-12的32个字符。

示例:

550e8400-e29b-41d4-a716-446655440000

uuid 的 npm 地址

线上引入方式:

版本4:

<script src="http://wzrd.in/standalone/uuid%2Fv4@latest"></script>

<script>

    uuidv4(); // ⇨ v4 UUID
    
</script>

版本5:

<script src="http://wzrd.in/standalone/uuid%2Fv5@latest"></script>

<script>

    uuidv5('http://example.com/hello', uuidv5.URL); // ⇨ v5 UUID

</script> 

npm 下载方式:

npm install uuid

根据版本号按需引入:

<!--推荐使用版本4-->

const uuidv4 = require('uuid/v4');

uuidv4(); // ⇨ '10ba038e-48da-487b-96e8-8d3b99b6d18a'

九、clipboard.js:复制文字功能

今天再推荐一个比较常使用的功能,就是复制、剪切功能,不依赖flash, 不依赖其他框架,gzip压缩后只有3kb大小,下面一起来试试吧!

ClipboardJS官网地址

1. 复制

复制:一个很常见的用例是从另一个元素复制内容。你可以给目标元素添加一个 data-clipboard-target 属性来实现这个功能。这个属性的值就是能匹配到另一个元素的选择器。

  1. 第三方CDN引入方式:github.com/zenorocha/c…

下面是在原生html场景的使用方式:

<html>
  <head>
    <script src="https://cdn.jsdelivr.net/npm/clipboard@2/dist/clipboard.min.js"></script>
  </head>

  <body>
    <script>
      new ClipboardJS('.btn');
    </script>

    <input id="foo"    value="我是内容5555"  style='width:300px'>

    <button class="btn" data-clipboard-target="#foo">按钮按钮</button>
    
  </body>

</html>

  1. npm 下载方式:
npm install clipboard --save

下面是在react组件的使用方式:

import React, { useEffect } from 'react';
import ClipboardJS from 'clipboard'

const App = () => {

  useEffect(() => {
    const clipboard = new ClipboardJS('.btn')
    
    // 这里是在组件销毁的时候把 clipboard 销毁
    return () => clipboard.destroy()
  }, [])

  return (
    <>
      <input id="copy-text" value={'内容'} />

      <button className="btn" data-clipboard-target="#copy-text">复制</button>

    </>
  )
}

export default App;

2. 剪切

剪切:你可以定义一个 data-clipboard-action='cut' 属性来指明你想要复制还是剪切内容,使用方式如下:

<html>
  <head>
    <script src="https://cdn.jsdelivr.net/npm/clipboard@2/dist/clipboard.min.js"></script>
  </head>

  <body>
    <script>
      new ClipboardJS('.btn');
    </script>

    <input id="foo"    value="我是内容5555"  style='width:300px'>

    <button class="btn" data-clipboard-action="cut"  data-clipboard-target="#foo">按钮按钮</button>

  </body>

</html>

3. 从属性复制文本

从属性复制文本,简单说就是说不用必须从另一个元素来复制,你仅需要给目标元素设置一个data-clipboard-text 属性即可

<html>
  <head>
    <script src="https://cdn.jsdelivr.net/npm/clipboard@2/dist/clipboard.min.js"></script>
  </head>

  <body>
    <script>
      new ClipboardJS('.btn');
    </script>
    
    // 这里点击按钮,复制到粘贴板的内容就是data-clipboard-text上的值
    <button class="btn" data-clipboard-text="这里是我要的内容">按钮按钮</button>

  </body>

</html>

4. 复制 / 剪切 后的回调:success / error

有时我们复制 / 剪切后,需要给用户一些反馈,这里我们就用到事件的回调,如下

<html>
  <head>
    <script src="https://cdn.jsdelivr.net/npm/clipboard@2/dist/clipboard.min.js"></script>
  </head>

  <body>
    <script>

      var clipboard = new ClipboardJS('.btn');

       // 成功后的回调
      clipboard.on('success', function(e) {
          console.info('Action:', e.action);
          console.info('Text:', e.text);
          console.info('Trigger:', e.trigger);

          e.clearSelection();
      });
      // 失败后的回调
      clipboard.on('error', function(e) {
          console.error('Action:', e.action);
          console.error('Trigger:', e.trigger);
      });

    </script>

    <button class="btn" data-clipboard-text="这里是我要的内容">按钮按钮</button>

  </body>

</html>


如果亲感觉我的文章还不错的话,可以一下添加关注哦!

内容还在持续更新中,怕错过更新的点个关注再走呗!点个关注不迷路哦!😄

求内推

注:如果有小伙伴公司正在招聘,可联系我哦!技术栈:react,ts,antd,webpack...,可以参考我的文章。地点:北京,联系电话:15210667658