工作中常用的需求总结(2)

307 阅读4分钟

一周又过去了,10月的最后一天,时间真的过的好快,最近自己好懒干什么都没动力,感觉自己技术越来越菜了,也不知道如何提升,大伙茫期的时候都是如何度过呢,每天看掘金各种大佬开源各种新项目,自己连公司项目之前的代码好像还都没弄明白,工作俩月了业务也没搞明白,心里的落差有点大,希望下周能调整过来吧!

推荐一下阿里开源的ahooks,用React的小伙伴可以了解一下,里面有很多好用的自定义hook!

1、判断类型

不要用type of,不够准确,例如用type of判断null的类型是对象。

export const isType = (obj, type) => Object.prototype.toString.call(obj) === `[object ${type}]`;

//Object.prototype.toString.call是最准确判断类型的方法

2、文本省略

一般可能用在表单、表格里文字超出部分,其实如果想简单的话只是超出省略号的话css三行就能搞定 但是可能遇到想显示前面和后面省略中间类似于银行卡号的,可以用这个组件

const Ellipsis = (props) => {
    const {title, length = 6, style = {}, ext =false, ellipsis= '...'} = props
    //title:文本,length:限制长度,style:文字样式,ext:中间省略号后面的是否显示
    let affix = ''
    if(ext){
        const titleArr = title.splice('.')
        const extName = titleArr[titleArr.length -1] //获取最后一部分
        const lastSecondName = titleArr[titleArr.length - 2] //获取倒数第二部分
        const llast = lastSecondName[lastSecondName.length -1] //获取倒数第二部分的最后一个字符
        affix = titleArr.length >= 2 ? `${llast}.${extName}` : ''
    }
    if(title.length <= length){ //实际的长度小于设定的
           return (
              <span style={style}>
                {title}
                {affix}
              </span>
    }
  	return (
        <Tooltip title={title}>
          <span style={style}>
            {title.slice(0, length)}
            {ellipsis}
            {affix}
          </span>
        </Tooltip>
  );
    
}

简易版文字省略

const Ellipsis = (props) => {
  const { title, length = 6 } = props;
  if (!title || title === '-') return '-';
  if (title.length <= length) {
    return title;
  }
  return (
    <Tooltip title={title}>
      <span>{title.slice(0, length)}...</span>
    </Tooltip>
  );
};

export default Ellipsis;

3、获取每一级产品的数量

当时有个需求是统计1、2、3、4级及4级以下的企业数量渲染到页面,后端返回的是树形结构的数据,需要用递归进行处理,我这里只是到四层,如果有更多children,自己再声明个变量就行了,递归是个比较常用的处理数据的思路尤其是在树结构、下拉框这种,但是我感觉后端应该也能处理hhhhh,如果你们后端能处理就让他处理好了嘿嘿嘿

const getInfoData =( 
    data=[], level=1, infoDt={level1:0, level2:0, level3:0, level4:0, level4_down:0} 
) => {
    data.forEach(item => {
        if(item.level > 4) {
            infoDt.level4_down +=1
        }else{
            infoDt[`level${level}`] +=1
        }
    })
    if(item.children && item.children.length > 0){
       infoDt = {...infoDt, ...getInfoData(item.children, level+1, infoDt)}
    }
	return infoDt
}

4、平级转树

这种要看后端返回的数据是什么结构的,这里的场景是后端传来的每一条企业数据他有一个parentId的字段属性,需要转换为Tree数据,一个children字段属性是一个数组,里面有他的所有子企业。

1、先把所有的数据遍历,把id放到map里当key保存

2、先把每一条数据的孩子都设置为空

3、再次遍历1588条数据,每一条当前数据为 node

4、展开把第二个参数合并到新的对象 newNode

5、判断当前node节点是否有父节点id,找出对应的索引,放入他的子节点中

map ={ //1000条
    "1002672720837609472": 0
    "1002672721315760128": 1
    "1002672722255284224": 2
    ...
}
const refactorTreeData(treeData, refactorNode) {
    const map = {} //保存节点的id
    let node	//遍历的每一条临时数据
    const children = []
    const roots = []
    let i
    const newList = list
    for(i=0, i < newList.length, i++){
        map[newList[i].id] = i
        newList[i].children = []
    }
    for(i=0, i < newList.length, i++){
        node = newList[i]
        const newNode = { ...node, ...refactorNode(node) }
        if(newList[map[node.parentId]] && newList[map[node.parentId]].children){
           newList[map[node.parentId]].children.push(newNode)
        }else{
            roots.push(newNode)
        }
        }
    return roots
}

5、Table表格组件的滚动条样式

当columns的属性比较多时肯定要向右滚动,默认滚动条有点丑....

//Antd中的Table表格组件的滚动条样式
  :global {
    .ant-space-align-center {
      align-items: flex-start;
    }
    .ant-table-wrapper {
      .ant-table-content {
        &::-webkit-scrollbar {
          width: 10px;
          height: 10px;
          background-color: #f5f5f5;
        }

        /*定义滚动条轨道 内阴影+圆角*/
        &::-webkit-scrollbar-track {
          box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
          border-radius: 10px;
          background-color: #fff;
        }

        /*定义滑块 内阴影+圆角*/
        &::-webkit-scrollbar-thumb {
          border-radius: 10px;
          box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
          background-color: #aaa;
        }
      }
    }
  }

6、Excel导出

import XLSX from 'xlsx';

const exportExcel = ({ headers, data, fileName = 'default.xlsx' }, refactorWorkbook) => {
  const xlsxHeaders = headers
    .map((item, i) => ({
      key: item.key,
      title: item.title,
      position: String.fromCharCode(65 + i) + 1,
    }))
    .reduce(
      (prev, next) => ({
        ...prev,
        [next.position]: { key: next.key, v: next.title },
      }),
      {},
    );

  const xlsxData = data
    .map((item, i) =>
      headers.map((key, j) => ({
        content: item[key.key],
        position: String.fromCharCode(65 + j) + (i + 2),
      })),
    )
    .reduce((prev, next) => [...prev, ...next])
    .reduce(
      (prev, next) => ({
        ...prev,
        [next.position]: { v: next.content },
      }),
      {},
    );

  const output = { ...xlsxHeaders, ...xlsxData }; //横排的数据和纵排的数据

  const outputPos = Object.keys(output);

  const ref = `${outputPos[0]}:${outputPos[outputPos.length - 1]}`;
    //这个是定义一个字符串 也就是表的范围[A1:C5]

  if (!refactorWorkbook) throw new Error('Require refactorWorkbook function to refactor workbook.');
  if (!isType(refactorWorkbook, 'Function'))
    throw new Error('refactorWorkbook should be a function with object value.');

  const wb = refactorWorkbook(output, ref);

  XLSX.writeFile(wb, fileName);
};

export default exportExcel;

组件内使用:

//使用excel导出组件
 IExcel(
        {
          headers: columns, //这是表格头横排的数据
          data: dataSource.map((item, i) => ({ //单元格具体的数据
            index: i + 1,
            name:item.name,
            age:item.age,
            createTime:moment(item.createTime).formatter()
          })),
          fileName: `表.xlsx`,
        },
        (output, ref) => {
          return {
            SheetNames: ['明细表'],
            Sheets: {
              明细表: {
                ...output,
                '!ref': ref,
                '!cols': [ //每一列的宽度
                  { wpx: 60 },
                  { wpx: 150 },
                  { wpx: 250 },
                  { wpx: 250 },
                ],
              },
            },
          };
        },
      );

7、文本差异

用的是 diff-match-patch 库,使用于变更前变更后对比的这种业务

import TextDiff from '@/components/TextDiff/index.min.js';
const diffText = (oldTxt, newTxt) => {
  const tDiff = new TextDiff();
  const diff = tDiff.diff_main(oldTxt, newTxt);
  tDiff.diff_cleanupSemantic(diff);
  return tDiff.diff_prettyHtml(diff);
};
//表单columns对应的render项
  render: (text, record) => {
      return (
        // eslint-disable-next-line react/no-danger
        <span dangerouslySetInnerHTML={{ __html: diffText(record?.bgjlBgqxx || '', text) }} />
      );
},

8、切换页码滚动到顶部

当页面的内容比较多,一般来说点击下一页会从上面找数据或操作,此时需要在请求数据时加返回顶部的动画

前面是获取数据用async和await也行都一样,主要是requestAnimationFrame这个Api,

listRef指的是滚动列表最外层的div标签绑定的ref


.then(res => {
        if(res?.code === '200' && listRef && listRef.current && !firstRender){ //外层标签的ref
           window.requestAnimationFrame(() => {
               listRef.current.scrollIntoView({
                    behavior: 'smooth'
               })
           })
       }
    })

9、 封装GET请求的自定义hook

一般获取数据用的最多的是get请求,而且每次有参数改变例如分页页码,需要根据依赖调用useEffect,此时可以封装一个useRequest,根据传过来的里面的changeParams,这是一个布尔值,如果需要重新根据参数调用接口获取新的数据,把changeParams直接取反,由于useRequest的依赖为changeParams,会直接重新调用接口,省去写很多useEffect

const useRequest = (props: any) => {
    const { url, changeParams, ...rest } = props;
    const [loading, setLoading] = useState<boolean>(false);
    const [res, setRes] = useState<{}>({});
    useEffect(() => {
      setLoading(true);
      try {
        querySingle({
          path: `${url}`,
          ...rest,
        }).then((response) => {
          if (response?.code === "200") {
            setRes(response?.data);
          }
          setLoading(false);
        });
      } catch (error) {
        setLoading(false);
      }
    }, [changeParams]);
    return { res, loading };
};

举例使用:

//组件内举例使用,这个页面是一个带分页的组件
  const [params, setParams] = useState({
    pageSize: '10',
    pageNumber: '0',
    title: '',
  });
  const [changeIt, seChangeIt] = useState<boolean>(false); //useEffect的依赖
  const { res, loading } = useRequest({
    url: 'data/channelList', //接口地址,前面的部分地址省去了
    changeParams: changeIt,
    ...params,
  });
//这是分页页码变换的回调
    const onPageChange = (pagination: any) => {
    const { current } = pagination;
    setParams({
      ...params,
      pageNumber: `${current - 1}`,
    });
	seChangeIt(!changeIt); // 此时会重新调用接口
};

10、qs库常用Api

这个库特别好用,尤其是在封装网络请求方法的参数需要拼到url里的时候 stringify是对象转url, parse是url转对象

qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'indices' })
// 'a[0]=b&a[1]=c'
 qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'brackets' })
// 'a[]=b&a[]=c'
 qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'repeat' })  
// 'a=b&a=c'
 qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'comma' })
// 'a=b,c'
export async function getData(params) {
  return request(`/api/channel?${stringify(params)}`, {
    method: 'GET',
    data: params,
  });
}

2021/10/31 15:43

希望对你有帮助!