/**
*  私人JS常用静态方法类库
*  版本信息：v1.0.1
*  编译日期：2021-5-12
*  更新日期：2022-9-7
*/

/**
 * 颜色处理RGB与16进制互转：{ colorHex, hexColor }
 * 时间处理：{ 时间截取方法:timeArray, 取当前日期和时间: getCurrentTime， 根据时间计算节气的方法：getjq}
 * 数组操作：{ 对象数组属性删除：removeListByValue,  数组去重:arrayRepetition, 插入法对象数组排序：insertionSortArray}
 * 对象操作：{ 过滤对象中为空的属性： filterObj， }
 * 字符串操作：{ 字符串拼接并去重：delSameStr，中文字符串转url编码格式: strToUnicode, 1位数字补0显示：replenishZero}
 * 根据URL下载不用打开新页面的方法：handleDownload
 * 随机数字或字符串{ 随机生成数字：randomNumber，随机生成字符串：randomString}
 * 导出表中数据为xlsx文件： exportElsx
 * 导出对象数据为json文件： exportJson
 * 平面n个点的最短连线算法----最短路径排序: shortPathRank,
*/

// 导出Excel的引入，需要安装js-export-excel插件 -> yarn add js-export-excel -S
// import ExportJsonExcel from 'js-export-excel'

/**
 * 颜色值RGB转换为16进制
 * @param rgbColor 颜色字符串-'rgb(255,255,255)'或'RGB(255,255,255)'
 */
export function colorHex(rgbColor) {
  // RGB颜色值的正则
  const reg = /^(rgb|RGB)/

  if (reg.test(rgbColor)) {
    let strHex = '#'
    // 把RGB的3个数值变成数组
    const colorArr = rgbColor.replace(/(?:\(|\)|rgb|RGB)*/g, '').split(',')
    // 转成16进制

    for (let i = 0; i < colorArr.length; i++) {
      const hexn = Number(colorArr[i])

      let hex = hexn.toString(16)

      if (hexn < 16) {
        hex = '0' + hex
      }
      strHex += hex
    }
    return strHex
  }
  return String(rgbColor)

}

/**
   * 颜色值16进制转换为RGB
   * @param hexColor 颜色字符串-'#fff'或'#FFFFFF'
   */
export function colorRgb(hexColor) {
  // 16进制颜色值的正则
  const reg = /^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/
  // 把颜色值变成小写

  let color = hexColor.toLowerCase()

  if (reg.test(color)) {
    // 如果只有三位的值，需变成六位，如：#fff => #ffffff
    if (color.length === 4) {
      let colorNew = '#'

      for (let i = 1; i < 4; i += 1) {
        colorNew += color.slice(i, i + 1).concat(color.slice(i, i + 1))
      }
      color = colorNew
    }
    // 处理六位的颜色值，转为RGB
    const colorChange = []

    for (let i = 1; i < 7; i += 2) {
      colorChange.push(parseInt('0x' + color.slice(i, i + 2)))
    }
    return 'RGB(' + colorChange.join(',') + ')'
  }
  return color

}

/**
   * 时间截取方法
   * @param time 时间字符串-'2021-1-10 10:25:30'或'2021-1-10T10:25:30'
   */
export function timeArray(time) {
  if (typeof time === 'string') {
    if (time.indexOf(' ') !== -1) {
      return time.split(' ')
    } else if (time.indexOf('T') !== -1) {
      return time.split('T')
    }
  }
}

/**
   * 取当前日期和时间
   * @return String '2020-10-10 10:10:10'
   */
export function getCurrentTime() {
  const date = new Date()
  const year = date.getFullYear()

  let month = date.getMonth() + 1

  let day = date.getDate()

  let hour = date.getHours()

  let minute = date.getMinutes()

  let second = date.getSeconds()

  month = month > 9 ? month : '0' + month
  day = day > 9 ? day : '0' + day
  hour = hour > 9 ? hour : '0' + hour
  minute = minute > 9 ? minute : '0' + minute
  second = second > 9 ? second : '0' + second
  return `${year}-${month}-${day} ${hour}:${minute}:${second}`
}

/**
   * 根据数组中的对象的属性的值来删除对象元素
   * @param arrylist 原数组
   * @param attribute 要删除对象所对应的属性
   * @param value 要删除的值
   */
export function removeListByValue(arrylist, attribute, value) {
  for (let i = 0; i < arrylist.length; i++) {
    if (arrylist[i][attribute] === value) {
      arrylist.splice(i, 1)
      break
    }
  }
}

/**
   * 过滤对象中为空的属性
   * @param obj
   * @returns {*}
   */
export function filterObj(obj) {
  if (!(typeof obj === 'object')) {
    return
  }

  for (const key in obj) {
    if (Object.prototype.hasOwnProperty.call(obj, key) &&
              (obj[key] === null || obj[key] === undefined || obj[key] === '')) {
      delete obj[key]
    }
  }
  return obj
}

/**
   * 根据新字符串拼接旧字符串中没有的元素---字符串去重
   * @param oldStr 旧字符串
   * @param newStr 新字符串
   */
export function delSameStr(oldStr, newStr) {
  const spliceStr = newStr.split('；')

  for (let i = 0; i < spliceStr.length; i++) {
    if (oldStr.indexOf(spliceStr[i]) === -1) {
      oldStr = oldStr + '；' + spliceStr[i]
    }
  }
  return oldStr
}

/**
   * 中文字符串转url编码格式
   * @param str 中文字符串
   */
export function strToUnicode(str) {
  const es = []

  for (let i = 0; i < str.length; i++) {
    es[i] = ('00' + str.charCodeAt(i).toString(16)).slice(-4)
  }
  return '\\u' + es.join('\\u')
}

/**
  * 数字小于10补0
  * @param Number 数字
  * @return string
  */
export function replenishZero(num) {
  if (num < 10) {
    return '0' + num
  }
  return num.toString()

}

/**
   * 根据URL下载不用打开新页面的方法
   * @param url 要下载的地址
   */
export function handleDownload(url) {
  const iframe = document.createElement('iframe')

  iframe.style.display = 'none' // 防止影响页面
  iframe.style.height = 0 // 防止影响页面
  iframe.src = url
  document.body.appendChild(iframe) // 这一行必须，iframe挂在到dom树上才会发请求
  setTimeout(() => {
    document.body.removeChild(iframe)
  }, 100)
}

/**
   * 随机生成数字
   *
   * 示例：生成长度为 12 的随机数：randomNumber(12)
   * 示例：生成 3~23 之间的随机数：randomNumber(3, 23)
   *
   * @param1 最小值 | 长度
   * @param2 最大值
   * @return int 生成后的数字
   */
export function randomNumber() {
  // 生成 最小值 到 最大值 区间的随机数
  const random = (min, max) => {
    return Math.floor(Math.random() * (max - min + 1) + min)
  }

  if (arguments.length === 1) {
    const [length] = arguments
    // 生成指定长度的随机数字，首位一定不是 0
    const nums = [...Array(length).keys()].map((i) => i > 0 ? random(0, 9) : random(1, 9))

    return parseInt(nums.join(''))
  } else if (arguments.length >= 2) {
    const [min, max] = arguments

    return random(min, max)
  }
  return Number.NaN

}

/**
   * 随机生成字符串
   * @param length 字符串的长度
   * @param chats 可选字符串区间（只会生成传入的字符串中的字符）
   * @return string 生成的字符串
   */
export function randomString(length, chats) {
  if (!length) {
    length = 1
  }
  if (!chats) {
    chats = '0123456789qwertyuioplkjhgfdsazxcvbnm'
  }
  let str = ''

  for (let i = 0; i < length; i++) {
    const num = randomNumber(0, chats.length - 1)

    str += chats[num]
  }
  return str
}

/**
   * 数组去重
   * @param {Object} array
   */
export function arrayRepetition(array) {
  const temp = [] // 一个新的临时数组

  for (let i = 0; i < array.length; i++) {
    if (temp.indexOf(array[i]) === -1) {
      temp.push(array[i])
    }
  }
  return temp
}

/**
   * 插入法数组排序---升序
   * @param {Object} arr 排序数组
   * @param {Object} att 根据那个属性排序
   */
export function insertionSortArray(arr, att) {
  let preIndex, current

  for (let i = 1; i < arr.length; i++) {
    preIndex = i - 1
    current = arr[i]
    while (preIndex >= 0 && arr[preIndex][att] > current[att]) {
      arr[preIndex + 1] = arr[preIndex]
      preIndex--
    }
    arr[preIndex + 1] = current
  }
}

/**
   * 根据时间计算节气的方法
   * @param yyyy 年
   * @param mm 月
   * @param dd 日
   */
export function getjq(yyyy, mm, dd) {
  mm = mm - 1
  const sTermInfo = [0, 21208, 42467, 63836, 85337, 107014, 128867, 150921, 173149, 195551, 218072, 240693, 263343, 285989, 308563, 331033, 353350, 375494, 397447, 419210, 440795, 462224, 483532, 504758]

  const solarTerm = ['小寒', '大寒', '立春', '雨水', '惊蛰', '春分', '清明', '谷雨', '立夏', '小满', '芒种', '夏至', '小暑', '大暑', '立秋', '处暑', '白露', '秋分', '寒露', '霜降', '立冬', '小雪', '大雪', '冬至']

  let solarTerms = ''

  while (solarTerms === '') {
    let tmp1 = new Date(31556925974.7 * (yyyy - 1900) + sTermInfo[mm * 2 + 1] * 60000 + Date.UTC(1900, 0, 6, 2, 5))

    let tmp2 = tmp1.getUTCDate()

    if (tmp2 === dd) {
      solarTerms = solarTerm[mm * 2 + 1]
    }
    tmp1 = new Date(31556925974.7 * (yyyy - 1900) + sTermInfo[mm * 2] * 60000 + Date.UTC(1900, 0, 6, 2, 5))
    tmp2 = tmp1.getUTCDate()
    if (tmp2 === dd) {
      solarTerms = solarTerm[mm * 2]
    }
    if (dd > 1) {
      dd = dd - 1
    } else {
      mm = mm - 1
      if (mm < 0) {
        yyyy = yyyy - 1
        mm = 11
      }
      dd = 31
    }
  }
  return solarTerms
}

/**
   * 到出表中数据为xlsx文件
   * @param dataSource 表中数据源
   * @param column 表头信息
   * @param fileName 文件名称
   */
// export function exportElsx(dataSource, column, fileName) {
// const ReqDetailList = dataSource
// const columns = column
// const option = {}
// option.fileName = fileName // Excel文件名称
// option.datas = [{
// sheetData: ReqDetailList.map(item => {
// const result = {}
// columns.forEach(c => {
// result[c.property] = item[c.property]
// })
// return result
// }),
// sheetName: '',
// sheetFilter: columns.map(item => item.property),
// sheetHeader: columns.map(item => item.label),
// columnWidths: columns.map(() => 10)
// }]
// const toExcel = new ExportJsonExcel(option)
// toExcel.saveExcel()
// }

/**
   * 到出对象中数据为json文件
   * @param data 表中数据源
   * @param fileName 文件名称
   */
export function exportJson(data, filename) {
  if (!data) {
    alert('保存的数据为空')
    return
  }
  if (!filename) {
    filename = 'json.json'
  }
  if (typeof data === 'object') {
    data = JSON.stringify(data, undefined, 2)
  }
  const blob = new Blob([data], { type: 'text/json' })
  const e = document.createEvent('MouseEvents')
  const a = document.createElement('a')

  a.download = filename
  a.href = window.URL.createObjectURL(blob)
  a.dataset.downloadurl = ['text/json', a.download, a.href].join(':')
  e.initMouseEvent(
    'click',
    true,
    false,
    window,
    0,
    0,
    0,
    0,
    0,
    false,
    false,
    false,
    false,
    0,
    null
  )
  a.dispatchEvent(e)
}

/**
   * 平面n个点的最短连线算法----最短路径排序
   * 平面内有多个点，要用一根线逐个连接所有点，每个点不重复。返回排序好的点数组
   * @param pathArray 平面内有点坐标数组
   */
export function shortPathRank(pathArray) {
  let newArray = [] // 排序好的点数组

  if (pathArray.length > 2) { // 只有点数大于2排序才有意义
    const pathLengths = [] // 所用组合构成的路线长度数组

    const newArrays = [] // 所有组合构成的点排序好的数组

    let changeArray = [] // 用来做排序用的改变点数组

    let minPoint // 用来比较那个点距离最短的中间值
    // 循环改变计算的第一个点

    for (let z = 0; z < pathArray.length; z++) {
      newArray = []
      newArray.push(pathArray[z]) // 赋值路线的起始点
      // 嵌套循环计算其他点与起始点的最短距离的点
      for (let i = 0; i < pathArray.length - 2; i++) {
        changeArray = []
        // 初始化改变数组---将已经排序过的点排除
        pathArray.forEach(item => {
          if (!newArray.includes(item)) {
            changeArray.push(item)
          }
        })
        minPoint = changeArray[0] // 为最小距离点赋初始值
        // 循环比较排序好的最后一个点与其他没有排序点的距离
        for (let j = 0; j < changeArray.length - 1; j++) {
          if (dist(newArray[i], minPoint) > dist(newArray[i], changeArray[j + 1])) {
            minPoint = changeArray[j + 1]
          }
        }
        // 比较完后得到最短的那个点加到排序的点数组中
        newArray.push(minPoint)
      }
      // 改变的点数组中剩下最后一个点，不需要比较，直接去重加到排序数组中
      pathArray.forEach(item => {
        if (!newArray.includes(item)) {
          newArray.push(item)
        }
      })
      // 将这种点排列路径长度计算出来加到线长度数组中
      pathLengths.push(totalDist(newArray))
      // 将这种点排列情况加到点排序好的数组
      newArrays.push(newArray)
    }
    // 计算出线长度数组中路线最短的那个可能的下标去点排序好的数组取对应的点排序可能
    newArray = newArrays[getMinIndex(pathLengths)]
  } else {
    newArray = [...pathArray]
  }
  return newArray
}

// 求两点之间距离
export function dist(p1, p2) {
  const a = p2[0] - p1[0]

  const b = p2[1] - p1[1]

  return Math.sqrt(a * a + b * b)
}

// 求所有点连线的总长度
export function totalDist(pointArray) {
  let totalLength = 0

  for (let i = 0; i < pointArray.length - 1; i++) {
    totalLength += dist(pointArray[i], pointArray[i + 1])
  }
  return totalLength
}

// 求数组中最小值的下标
export function getMinIndex(arr) {
  let min = arr[0]
  // 声明了个变量 保存下标值

  let index = 0

  for (let i = 0; i < arr.length; i++) {
    if (min > arr[i]) {
      min = arr[i]
      index = i
    }
  }
  return index
}



// 深拷贝
export function cloneDeep(value) {
  if (Array.isArray(value)) {
      return value.map((child)=>cloneDeep(child));
  }
  if (typeof value === "object" && value !== null) {
      return Object.fromEntries(Object.entries(value).map(([k, v])=>[
              k,
              cloneDeep(v)
          ]));
  }
  return value;
}