项目中常见问题及解决方法

105 阅读6分钟

前言

新人报道,定期分享自己遇到的问题跟常用的方法,有更好的写法可以留言😅😅

1、分组

// 按照oaCode进行数据分组
let list = [
    {
      oaCode: 'zhangfei',
      age: 12,
      text: '我是1号'
    },
    {
      oaCode: 'zhangfei',
      age: 120,
      text: '我是2号'
    },
    {
      oaCode: 'zhangfei',
      age: 1200,
      text: '我是2号'
    },
    {
      oaCode: 'guanyu',
      age: 11,
      text: '我是关羽1号'
    },
    {
      oaCode: 'guanyu',
      age: 111,
      text: '我是关羽2号'
    },
    {
      oaCode: 'guanyu',
      age: 1111,
      text: '我是关羽2号'
    }
  ]
  let map = {}
  let dest = []
  for(let i = 0; i < list.length; i++) {
    let ai = list[i]
    if(!map[ai.oaCode]) {
      dest.push({
        oaCode: ai.oaCode,
        data: [ai]
      })
      map[ai.oaCode] = ai
    } else {
      console.log(dest, map)
      for(let j = 0; j < dest.length; j++) {
        let dj = dest[j]
        if(dj.oaCode == ai.oaCode) {
          dj.data.push(ai)
          break
        }
      }
    }
  }
  console.log('dest', dest)

2、简单实现antd面包屑

import {withRouter} from ‘react-router-dom’
import { Breadcrumb } from 'antd'
let list = {
    ‘/home’: ’首页’,
    ….
}
export default withRouter(props => {
    let { location, userInfo } = props
    let pathList = [] // 切割url
    let urlKey = location.pathname // 拿到当前页面的url
    if(urlKey === ‘/’){
    …
    } else {
        pathList = location.pathname.split(‘/’).filter(I=>i)
    }
    const showListUrl = pathList.map((ele,index) =>{
        let url = `/${pathList.split(0, index +1).join(‘/‘)}/`
        let urlName = list[url] // 对象根据键名查找值
        if (urlName) {
        return (
            <Breadcrumb.Item key={url}>
          	    {urlName}
            </Breadcrumb.Item>
        )
    }
    }).filter(i=>i)
    return (
        <Breadcrumb separator=>’>
            {showListUrl}
        </Breadcrumb>
    )
})
// 在app中引入次组件

3、antd3.x的日期输入框的年份选择

this.state = {
  timeYear: null,
  isOpen: false
}
let handlePanelChange = (e) => {
  this.setState({ isOpen: false, timeYear: e})
}
let handleOpenChange = (e) => {
  if(e) {
      this.setState({ isOpen: true })    
  } else {
      this.setState({ isOpen: false })    
  }
}
<DatePicker
  mode='year'
  allowClear={false}
  value={timeYear}
  open={isOpen}
  format='YYYY'
  // 如果设置月份 'month' 的话,可以如下设置月份为数组类型
  // monthCellContentRender={date => {
     // return `${moment(date).format('M')}月`;  // 修改中文显示为数字
  // }}
  onPanelChange={this.handlePanelChange}
  onOpenChange={this.handleOpenChange}
>            
</DatePicker>

4、水印实现

function createwater (oaName,oaCode) {
  // 创建水印浮层
  const watermark = document.createElement('div')
  if ('pointerEvents' in watermark.style) {
  //判断是否支持pointerEvents 属性,避免 IE8,9
    watermark.style.cssText="position:fixed;width:100%;height:100%;top:0;left:0;z-index:99999;pointer-events:none;"
    document.body.appendChild(watermark)// 初始化 画布
    const canvas = document.createElement('canvas')
    canvas.width = 200
    canvas.height = 200
    const ctx=canvas.getContext('2d')
    ctx.globalAlpha= 0.2
    ctx.fill()// 处理图片
    const image = new window.Image
    image.src =''
// 登录状态下不会出现这行文字,点击页面右上角一键登录'
    image.setAttribute('crossOrigin', 'anonymous')
    if(oaCode) {
      ctx.font = 'lighter 10px SimHei'
      ctx.fillstvle ='RGBA(0,0,0,0.085)'
      // ctx.fillText(oaCode,0,25)
      // ctx.fillText(oaCode,0,50)
      // ctx.fillText(oaCode,0,75)
      // ctx.fillText(oaCode,0,100)
      // ctx.fillText(oaCode,0,125)
      image.onload =()=>{
        ctx.fillstyle='#666'
        ctx.font="lighter 16px SimHei"
        ctx.rotate(-Math.PI/8)
        ctx.drawImage(image,-10,70,canvas.width,image.height * canvas.width / image.width)
        ctx.fillText(oaName,20,120)
        watermark.style.backgroundImage = `url(${canvas.toDataURL('image/png')})`
      }
    } 
  } 
}
createwater('张飞', 'zhangfei')

5、循环异步调用(promise)

// all()方法,如果有一个Promise对象报错了,则all()无法执行,会报错你的错误,无法获得其他成功的数据。
let list = [1,2,3,4,5,2]
  let promiseList = []
  // 模拟异步接口调用
  let timeOut = (data) => {
    return new Promise((resolve) => {
      setTimeout(()=>{
        resolve(data + '号')
      },)
    })
  }
  // 循环数组调用接口, all只适合调用都成功
  list.map(ele => {
    promiseList.push(timeOut(ele))
  })
  Promise.all(promiseList).then(res => {
    console.log('el', res)
  })
// allSettled()方法是不管有没有报错,把所有的Promise实例的数据都返回回来,放入到一个对象中。
let list = [1, 2, 3, 4, 5]
let newList = list.map(ele => {
  return new Promise(resolve => {
    setTimeout(()=> {
      resolve(ele+'嘿嘿')
    }, 1)
  })
})
let fun = async() => {
  await Promise.allSettled(newList).then(res => {
    console.log('res', res)
  })
  console.log('9999')
}
fun()

6、原生图片上传

<body>
  <input type='file' id='inp' onchange="onImgUpload()" />
  <img src='' id='img' />
</div>
</body>
</html>
<script>
  let onImgUpload = () => {
    let fileDom = document.getElementById('inp')
    let file = fileDom.files[0]
    let Img = document.getElementById('img')
    console.log(file)
    if(!file) return
    if(file.type === 'image/jpeg' || file.type === 'image/png') {
      const reader = new window.FileReader()
      reader.addEventListener('load', e => {
        const imgData = e.target.result
        const image = new window.Image()
        image.onload = () => {
          console.log('image', file, imgData)
          // 异步操作调接口....
          file.value = ''
        }
        image.src = imgData
        Img.src = imgData
      })
      reader.readAsDataURL(file)
    } else {
    console.log('图片格式有误,请重新上传')
    }
  }
</script>

7、滚动到顶部

export const scrollToTop = () => {
  const c = document.documentElement.scrollTop || document.body.scrollTop;
  if (c > 0) {
      window.requestAnimationFrame(scrollToTop);
      window.scrollTo(0, c - c / 8);
  }
}

8、qrcode包二维码生成器并下载

// 引入qrocde包
const Qrcode = require('qrcode')
// 使用
getCode = () => {
    Qrcode.toDataURL(rul) // 设置二维码的链接
        .then(res => {
            this.setState({ img: res }) // 二维码图片的url        
        })
        .catch(err => {
            console.log(err)        
        })
    // 调用接口 dispatch....
} 
// 使用a标签的download属性下载,将二维码的url给a标签的herf属性赋值,之后使用download属性定义下载的名称
<a herf='' id='aId' onClick={this.onDownload}>下载<a>
let onDownload = () => {
    let aLink = document.getElementById('aId')
    aLink.herf = img
    aLink.download = '下载'
}

9、打印

<body >
  <div id='container'>
    <div class='print-content' onclick='textPrint()'>
        <img />
    </div>
  </div>
  </body>
  </html>
  <script>
    // 当前窗口打印
    let textPrint = () => {
      // 可以打印文本,也可以将二维码图片打印
      window.document.body.innerHTML = document.querySelector('.print-content').innerHTML; // 需要打印的内容
      window.print();
      window.location.reload(); // 打印完成后重新加载页面
    }
    // 打开新窗口打印
    let textPrint = () => {
      const printHtml = document.querySelector('.print-content').innerHTML; // 需要打印的内容
      const newWin = window.open('', 'newwindow');
      newWin.document.write('<html><head><title>Print title!</title><style>// 添加样式内容</style></head><body>')
      newWin.document.write(printHtml);
      newWin.document.write('</body></html>');
      newWin.print();
      newWin.close(); // 打印完成后关闭
    }
  </script>

10、下载二进制流文件

let onDownload = () => {
  axios({
    method: 'post',
    url: '/export',
    responseType: 'arraybuffer',
    headers: {}
  })
  .then(res => {
    // 假设 data 是返回来的二进制数据
    const fileName = '下载名称.xlx'
    const data = res.data
    const blob = new Blob([data], {type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"})
    // 通过 FileReader 读取这个 blob
    const reader = new FileReader()
    reader.onload = e => {
      const res = e.target.result
      // 此处对fileReader读出的结果进行JSON解析
      // 可能会出现错误,需要进行捕获
      try {
        const json = JSON.parse(res)
        if (json) {
          // 解析成功说明后端导出出错,进行导出失败的操作,并直接返回
          return message.error('失败,格式错误')
        }
      } catch (err) {
        // 该异常为无法将字符串转为json,说明返回的数据是一个流文件
        // IE和EDGE上不兼容,因为这两款浏览器无法通过a标签直接下载blob对象,可直接通过window.navigator.msSaveBlob下载
        if (window.navigator.msSaveBlob) {
          let url = fileName // ie需要引用一下才能获取值 
          window.navigator.msSaveBlob(blob, url)
        } else {
          const link = document.createElement('a')
          // 兼容不同浏览器的URL对象
          const url = window.URL || window.webkitURL || window.moxURL
          // 创建下载链接
          const downloadHref = url.createObjectURL(blob)
          // a标签添加属性
          link.style.display = 'none'
          link.href = downloadHref
          link.setAttribute('download', fileName)
          document.body.appendChild(link)
          link.click()
          document.body.removeChild(link)
        }
      }
    }
    // 将blob对象以文本的方式读出,读出完成后将会执行 onload 方法
    reader.readAsText(tempBlob)
  })
}

11、immutable 第三方深复制包

// 1、使用 ...运算符只能对基本数据类型进行复制,无法复制复杂数据类型
// 2、使用JSON.parse(JSON.stringify()) 无法对undefined进行复制,如果强行复制会出现问题
// 3、immutable可解决上面的缺点

// 1、cnpm i immutable -S
import { Map } from 'immutable'
let obj = {
  name: '章三',
  list: [1, 2, 3]
}
let oldObj = Map(obj) // 旧的数据
// 方式一
let newObj = olbObj.set('name', 'hahha') // 设置新的值
console.log(oldObj.get('name'), newObj.get('name')) // 获取值
// 方式二
let objectObj = oldObj.toJS()  // 上面的方式不好查看,这样可以将值转为常用的对象的方式
console.log(objectObj)

12、node的eventss事件触发器

cnpm i events -S
import { EventEmitter } from 'events'
// 封装
class MyEmitter extends EventEmitter {};
exprot default new MyEmitter()
// 使用,可用在某个事件中,在其他地方可用addlistener监听
MyEmitter.emit('btn') // 标示
// 在其他组件可用
MyEmitter.addListener('btn', ()=>{})  // 监听
// 文档:http://nodejs.cn/api/events.html#events

13、递归tree结构

// 根据orgId拿到父节点的id跟orgName
let list = [
  {
    orgId: 1,
    orgName: '我是节点1',
    subNode: [
      {
        orgId: 11,
        orgName: '我是节点1的子节点1',
        subNode: [
          {
            orgId: 111,
            orgName: '我是节点1的子节点1的子节点'
          }
        ]
      },
      {
        orgId: 12,
        orgName: '我是节点1的子节点2',
      }
    ]
  },
  {
    orgId: 2,
    orgName: '我是节点2',
    subNode: [
      {
        orgId: 21,
        orgName: '我是节点2的子节点1',
        subNode: [
          {
            orgId: 211,
            orgName: '我是节点2的子节点1的子节点'
          }
        ]
      },
      {
        orgId: 22,
        orgName: '我是节点2的子节点2',
      }
    ]
  }
]
function headlData(treeData, id) {
  const targetData = {}
  function loops(data = [], parent) {
    return data.map(({ subNode, orgId: orgId, orgName: orgName }) => {
        const node = {
            orgId,
            orgName,
            parent        
        }
        targetData[orgId] = node
        node.subNode = loops(subNode, node)
    })
  }
  function getNode(orgId) {
    let node = []
    let currentNode = targetData[orgId]
    //node.push(currentNode.orgName)  获取父节点的orgName
    node.push(currentNode.orgId)  // 获取父节点的orgId
    if(currentNode.parent) {
      node = [...getNode(currentNode.parent.orgId), ...node]
    }
    return node
  }
  loops(treeData)
  return getNode(id)
}
let a = headlData(list, 22)
console.log('a', a)

14、js实现图片懒加载

<body>
  <div>
    <img src="./2.jpg" data-src='./1.jpg' alt="pig">
    <img src="./2.jpg" data-src='./1.jpg' alt="pig">
    <img src="./2.jpg" data-src='./1.jpg' alt="pig">
    <img src="./2.jpg" data-src='./1.jpg' alt="pig">
  </div>
</body>

function imgonload() {
    let img = document.querySelectorAll("img");
    for(let i=0; i<img.length; i++) {
      if(img[i].getBoundingClientRect().top < window.innerHeight) {
        //图片一旦有src就会加载出来,所以图片的路径不会放在src中,而是一个自定义的属性data-src中
        // dataset 获取自定义属性
        console.log(img[i].dataset.src)
        img[i].src = img[i].dataset.src;
      }
    }
  }
  function scollImg(fn) {
    let timer = null;
    let context = this;
    return function () {
      clearTimeout(timer);
      timer = setTimeout(() => {
        fn.apply(context);
      }, 500)
    }
  }
  window.onload = imgonload;
  window.onscroll = scollImg(imgonload);

15、关于Antd Table组件selectRows 翻页后不保留上一页已选items的解决方案

class Parts extends React.Component {
  state = {
    selectedRowKeys: [],
    doubleArr: [], // 存放双数组的数组
    filterRows: [], // 存放拼接后的一维数组的变量
    page: 1 // 页码
  }
  const rowSelection => {
    selectedRowKeys: this.state.selectedRowKeys,
    onChange: (selectedRowKeys, selectedRows) => {
      const { doubleArr, page } = this.state
      // 勾选生成二维数组
      doubleArr[page ? page - 1 : 0] = selectedRows
      // 这块扁平化成为一位数组,并过滤空
      const filterRows = doubleArr.flat().filter(Boolean)
      this.setState({ 
        selectedRowKeys: selectedRowKeys,
        filterRows: filterRows // 存放拼接好的一维数组
      });
    }
  }
  render() {
    return (
      <Table
         rowKey={record => record.componentId}
         rowSelection={rowSelection}
      />
    )
  }
}

16、日期的判断(根据天数判断)

// 获取两个日期相差的月跟天数
  let month = moment('结束日期').diff(moment('开始日期'), 'moth')
  let day = moment('结束日期').diff(moment('开始日期', 'day'))
  // 获取两个日期之间的月份
  const getMonth = (start, end) => {
    let startData = moment(start)
    let endData = moment(end)
    const allYearMonth = []
      while(endData > startData || startData.format('M') === endData.format('M')){
        allYearMonth.push(startData.format('YYYY-MM'))
        startData.add(1, 'month')
    }
    return allYearMonth
  }
  let list = getMonth('开始日期', '结束日期')
  // 处理月份天数不同的情况
  let thirtyOne = 0
  let thirty = 0
  list.map(ele => {
    if(moment(ele).daysInMonth() === 31) {
      thirtyOne++
    } else if(moment(ele).daysInMonth() === 28) {
      thirty = -3
    }
  })
  // 最后一个月如果是31天的话需要减1
  let num = 0
  moment(list[list.length -1]).daysInMonth() === 31 ? num = -1 : ''
  // 场景:结束时间需要在开始时间 2022-03-05 后3个月
  // 相差的天数
  let dayNum = 60 + thirtyOne + thirty + num
  if(dayNum > day) {
    console.log('满足')
  }

17、深克隆

// 无法正确克隆undefined
function deepClone(obj = {}, map = new Map()) {
    if (typeof obj !== "object") {
      return obj;
    }
    console.log('--', map.get(obj), obj)
    if (map.get(obj)) {
      return map.get(obj);
    }
    let result = {};
    // 初始化返回结果
    if (
      obj instanceof Array ||
      // 加 || 的原因是为了防止 Array 的 prototype 被重写,Array.isArray 也是如此
      Object.prototype.toString(obj) === "[object Array]"
    ) {
      result = [];
    }
    // 防止循环引用
    map.set(obj, result);
    console.log('map',map)
    for (const key in obj) {
      // 保证 key 不是原型属性
      if (obj.hasOwnProperty(key)) {
        // 递归调用
        result[key] = deepClone(obj[key], map);
      }
    }
    // 返回结果
    return result;
  }

18、过滤数组中的空值跟空对象

function distinctArrObj(arr) {
  let MyShow=(typeof arr!="object")? [arr] : arr  //确保参数总是数组
  for (let i = 0; i < MyShow.length; i++) {
      if (MyShow[i] == null || MyShow[i] == "" || JSON.stringify(MyShow[i]) == "{}") {
          MyShow.splice(i, 1);
          i = i - 1;
      }
  }
  return MyShow
}
console.log(distinctArrObj([{},{},1,0,2,null,undefined,{a:1}]))