日常开发项目功能点实现(未完待续..)

231 阅读3分钟

日常开发项目功能点实现

1.日常开发组件封装

1.1单组件封装及使用

// 比如封装个银行列表的组件
import React, { useState, useEffect, forwardRef } from 'react';
import { Select } from 'antd';
import { getBankGetBankListByKey } from '@/services/common';
/**
 * 银行下拉
 */
const { Option } = Select;
const AreaSelect = forwardRef((props: any, _ref) => {
  const [bankList, setbankList] = useState([]);
  useEffect(() => {
    getBankGetBankListByKey({
      pageNo: 1,
      pageSize: 100,
    }).then(res => {
      if (res.code === 0) {
        setbankList(res.data);
      }
    });
  }, []);
  return (
    <Select {...props} ref={_ref}>
      {(bankList || []).map((item: any) => (
        <Option key={item.bankID} data-item={item} value={`${item.bankID}|${item.name}`}>
          {item.name}
        </Option>
      ))}
    </Select>
  );
});

export default AreaSelect;

----------------------------------------------------------------------

vue和react一样,只是引用方式不同;vue需要components声明下
A文件: 
import BankSelect from '@/components/BankSelect';

 <FormItem {...formItemLayout} label="收款银行" className={styles.antFormItem}>
        {getFieldDecorator('bankIdAndText', {
          initialValue: getBankIdAndText(formObj),
          rules: [{ required: true, message: '请选择收款银行' }],
        })(
          <BankSelect
            filterOption={filterOption}
            showSearch
            placeholder="请选择收款银行"
            onChange={(e: any, item: any) => {
              onchange(e, 'bankIdAndText', item);
            }}
          />,
        )}
</FormItem>

image.png

1.2.inputNumber封装最大值、最小值组件,点击button进行查询

方法1
# 组件:
import React, { useRef } from 'react';
import { InputNumber, Button, message } from 'antd';

// 输入两个整数,最大值最小值

const BetweenNumber = ({
  defaultValue,
  onChange,
  placeholder,
  max,
}: {
  defaultValue: number[];
  onChange: any;
  placeholder: string[];
  max: number;
}) => {
  const data = useRef({
    max: 0,
    min: 0,
  });

  function change(e: any, type: string) {
    console.log(e);
    data.current[type] = e || 0;
  }

  function click() {
    console.log(data.current);
    if (data.current.max < data.current.min) {
      message.error('最大值不能小于最小值');
      return;
    }

    onChange([data.current.min, data.current.max]);
  }

  return (
    <React.Fragment>
      <InputNumber
        min={0}
        max={max}
        style={{ width: '42%', borderTopRightRadius: '0px', borderBottomRightRadius: '0px' }}
        defaultValue={defaultValue[0]}
        onChange={(e: any) => change(e, 'min')}
        placeholder={placeholder[0] || '最小值'}
      />
      <InputNumber
        min={0}
        max={max}
        style={{
          width: '42%',
          borderRadius: '0px',
          borderLeftWidth: '0px',
          borderRightWidth: '0px',
        }}
        defaultValue={defaultValue[1]}
        onChange={(e: any) => change(e, 'max')}
        placeholder={placeholder[1] || '最大值'}
      />
      <Button
        type="primary"
        style={{
          width: '16%',
          borderTopLeftRadius: '0px',
          borderBottomLeftRadius: '0px',
          position: 'relative',
          top: '-1px',
        }}
        icon="search"
        onClick={click}
      />
    </React.Fragment>
  );
};

export default BetweenNumber;


----------------------------------------------------------------------


A文件:
import BetweenNumber from '@/pages/kcmanage/component/BetweenNumber';
 <Col md={9} sm={12}>
          <BetweenNumber
            placeholder={['最小库存天数', '最大库存天数']}
            defaultValue={[]}
            onChange={(value: any) => handleFilter(value, 'my_age')}
            style={{ width: '100%' }}
          />
 </Col>
 
 
 // 筛选fn
  const handleFilter = (value: any, key: any, item: any) => {
    switch (key) {
    // 获取value数组的第一项和第二项也就是最小值和最大值。
      case 'my_age':
        info.startAge = value[0] || '';
        info.endAge = value[1] || '';
        break;
      default:
        info[key] = value;
    }
    dispatch({
      type: 'xxxx/yyyyy',
      payload: {
        ...info,
        pageIndex: 1,
        pageSize: 20,
      },
    });
  };
 

image.png

方法2
# 组件
import React, { useEffect, useState, useRef } from 'react';
import { InputNumber } from 'antd';
import { InputNumberProps } from 'antd/lib/input-number/index.d';

// 数值区间组件
// 组件限制较少,允许只输入最大值或者最小值,如果同时输入了最大最小会做判断,是否交换值

interface Props
  extends Omit<InputNumberProps, 'value' | 'defaultValue' | 'onChange' | 'placeholder'> {
  defaultValue?: number[];
  value?: number[];
  onChange?: (arg: any[]) => void;
  placeholder?: string[];
  max?: number;
}
interface Data {
  max?: number;
  min?: number;
}

const styles = { width: '50%', borderTopRightRadius: '0px', borderBottomRightRadius: '0px' };

// 验证数字区间
export const validatorNumberRange = (value, fieldName) => {
  if (!value) {
    return;
  }
  let arr = value;
  if (!Array.isArray(value)) {
    arr = [];
  }
  if (Number.isNaN(Number(arr[0]))||arr[0]===null) {
    return `请填写${fieldName}最小值`;
  }
  if (Number.isNaN(Number(arr[1]))||arr[1]===null) {
    return `请填写${fieldName}最大值`;
  }
};

const Index = ({
  defaultValue = [],
  onChange = () => {},
  placeholder = ['最小值', '最大值'],
  value = [],
  max,
}: Props) => {
  const [data, setdata] = useState<Data>({ max: undefined, min: undefined });
  const d = useRef<Data>({ max: undefined, min: undefined });

  useEffect(() => {
    if (Array.isArray(value)) {
      const D = { min: value[0], max: value[1] };
      setdata(D);
      d.current = D;
    }
  }, [JSON.stringify(value)]);

  function change(e: any, type: string) {
    const D = { ...data, [type]: !Number.isNaN(e) ? e : undefined };
    setdata(D);
    d.current = D;
  }

  function check() {
    // 最大最小都有值,判断一下俩值得大小就得了
    let { max: currentMax, min: currentMin } = d.current;
    currentMax = Number(currentMax);
    currentMin = Number(currentMin);
    if (!Number.isNaN(currentMax) && !Number.isNaN(currentMin)) {
      let D = null;
      // 最大值是0,就忽略比较
      if (currentMax === 0) {
        D = { max: undefined, min: currentMin };
      } else if (currentMax! < currentMin!) {
        // 大小互换
        D = { max: currentMin, min: currentMax };
      }
      if (D) {
        setdata(D);
        d.current = D;
      }
    }
  }
  const onBlur = () => {
    check();
    onChange([d.current.min, d.current.max]);
  };
  return (
    <React.Fragment>
      <InputNumber
        min={0}
        max={max}
        style={styles}
        defaultValue={defaultValue[0]}
        onChange={(e: any) => change(e, 'min')}
        placeholder={placeholder[0]}
        value={data.min}
        onBlur={onBlur}
      />
      <InputNumber
        min={0}
        max={max}
        style={{ ...styles, borderLeft: '0px' }}
        defaultValue={defaultValue[1]}
        onChange={(e: any) => change(e, 'max')}
        placeholder={placeholder[1]}
        value={data.max}
        onBlur={onBlur}
      />
    </React.Fragment>
  );
};

export default Index;


----------------------------------------------------------------------


# A文件:
import NumberRange, { validatorNumberRange } from '@/components/NumberRange';
# 使用:
    <FormItem labelCol={{ span: 6 }} wrapperCol={{ span: 15 }} label="允许修改区间">
        {form.getFieldDecorator('carOwnerPhone', {
          // initialValue: detailInfo.procurementDealer.dealerPhone,
          rules: [
            { required: true, message: '请输入允许修改区间!' },
            {
              validator: (_, value, callBack) => {
                callBack(validatorNumberRange(value, '允许修改区间'));
              },
            },
          ],
        })(<NumberRange placeholder={['最小修改值', '最大修改值']} />)}
      </FormItem>
      
      
      
  初始值:
  const {
      minStockAge,
      maxStockAge,
      ...fanalValues1
    } = fanalValues;
    fanalValues1.stockAge = (minStockAge || maxStockAge) ? [minStockAge, maxStockAge] : undefined;
    form.setFieldsValue(fanalValues1);
    
          <Tooltip title="库存天数">
          <Col {...cols}>
            <Form.Item >
              {form.getFieldDecorator('stockAge')(<NumberRange placeholder={['库存天数最小值', '库存天数最大值']} />)}
            </Form.Item>
          </Col>
        </Tooltip>
        

image.png

1.3.Antd结合upload实现文件上传

// service/common
export const uploadFileService = `${HOST['/base']}/apixxxx/yyyyy/uploadSave`;


A文件:
import { uploadFileService } from '@/services/common';
import { Button, Form, Modal, message, Upload, Spin } from 'antd';


const uploadProps = {
    name: 'file',
    action: uploadFileService,
    accept: '.xls,.xlsx,.excel', // 允许上传的文件格式
    showUploadList: false,
    data: { type }, // 除了file还需传的参数,在这个data里写
    headers: {
      Authorization: getCookieValue('activePassportID'),
    },
    onRemove: () => {},
    beforeUpload: () => {
  
      // //文件类型校验----由于我这里不需要做太多校验,所以格式上的判断包括文件大小的判断就不额外添加了
          // const fileType = info.name.split('.').pop();
          // if (fileType !== 'xlsx') {
          //   message.error(`上传失败:上传文件格式非.xslx`);
          // }
          // return false;
          
      RefModal = Modal.info({
        title: '批量导入中 请勿关闭弹窗...',
        content: (
          <div style={{ paddingTop: 20 }}>
            <Spin />
          </div>
        ),
      });
    },
    onChange: ({ file }) => {
      if (file.status === 'done' && file.response.code === 0) {
        RefModal.destroy();
        RefModal = null;
        onUpdate();// 上传接口成功后调刷新列表方法
        message.success(`${file.name} - 上传成功`);
      } else if (
        file.status === 'error' ||
        (file.status === 'done' && file?.response?.code !== 0)
      ) {
        message.error(`${file.name} - ${file?.response.message}`);
        RefModal.destroy();
        RefModal = null;
      }
    },
  };
  
  
<Upload {...uploadProps}>
    <Button style={styleButtonMargin}>
      <icon type="upload" /> 批量导入
    </Button>
</Upload>

在上传中会有modal友好提示,让用户稍后再操作 image.png

需要注意:上传这样的接口无需套request,否则会出现两个请求,一个是http://xxxx/api/xxx,另一个是localhost:8080/url路由/上传请求

2.图片预览及放大 缩放、旋转角度 实现

4.一键换肤实现

5.切换中英文(多语言)实现

6.长列表

7.良田高拍仪应用到React项目

8.前端监控

9.大文件上传

10.文件上传转成url

11.微前端

12.多页面开发

13.基础组件库搭建

14.生成项目系统搭建