一些奇奇怪怪的需求解决方案记录(一)

224 阅读1分钟
1、复制内容到剪切板
private allCopy(list:any) { // 全部复制
    let input = document.createElement('textarea')
     // let input = document.createElement('input') // 复制单行时用input,要折行时用textarea
    input.setAttribute('readonly', 'readonly') // 防止手机上弹出软键盘
    const value = list.account + '\n' + list.password + '\n' + list.msg
    input.value = value
    document.body.appendChild(input)
    input.select()
    var res = document.execCommand('copy');
    document.body.removeChild(input);
    Toast('复制成功')
  }
2、导出
// InvoiceApplyInteractor.infoExport 封装的promise方法
InvoiceApplyInteractor.infoExport(params).then((res:any) => {
      console.info(res, 'res')
      if (!res || res.code === 1) {
        this.$message.error('导出接口错误!')
        return
      }
      const blob = new Blob([res.data])
      const contentDisposition = res.headers['content-disposition']
      const pattern = new RegExp('filename=([^;]+\\.[^\\.;]+);*')
      const result = pattern.exec(contentDisposition)
      const fileName = decodeURI(result[1]) // 使用decodeURI对名字进行解码
      const downloadElement = document.createElement('a')
      const href = window.URL.createObjectURL(blob) // 创建下载的链接
      // console.info(href, 'href')
      downloadElement.style.display = 'none'
      downloadElement.href = href
      // 下载后文件名
      downloadElement.download = fileName
      document.body.appendChild(downloadElement)
      // 点击下载
      downloadElement.click()
      // 下载完成移除元素
      document.body.removeChild(downloadElement)
      // 释放掉blob对象
      window.URL.revokeObjectURL(href)
    })
    // 在请求header.config中 加入 responseType: 'blob'
3、H5支付宝支付奇怪问题
const div = document.createElement('div')
      div.innerHTML = res.data // 这个res.data 就是接口返回的form
      document.body.appendChild(div)
    
      // document.forms[0].setAttribute('target', '_blank') // 当在Sarfai浏览器时,加上这行带回会无法唤起。日了狗
      
      document.forms[0].submit()
4、vant van-field禁止输入空格
  // 直接加入formatter属性
  :formatter="formatter"
  
  private formatter(val:string) { // 不让输入空格
    return val.replace(/\s+/g, '')
  }
5、H5页面引入微信分享
  • 问题1、分享的路径域名需要在公众平台配置安全域名;
  • 问题2、vue中不能使用.push() 跳转至需要分享的页面,使用window.location.href = url
private async wxInit() {
    const url = window.location.href
    const { data } = await SignUpInteractor.wxInit({ // 这是个请求后端接口
      url: url
    })
    console.log(data)
    if (data.code === 0) {
      wx.config({
        debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
        appId: data.data.appId, // 必填,公众号的唯一标识
        timestamp: data.data.timestamp, // 必填,生成签名的时间戳
        nonceStr: data.data.nonceStr, // 必填,生成签名的随机串
        signature: data.data.signature, // 必填,签名
        jsApiList: [
          'updateTimelineShareData',
          'updateAppMessageShareData',
          'hideMenuItems'
        ] // 必填,需要使用的JS接口列表
      })
      wx.checkJsApi({
        jsApiList: ['updateTimelineShareData', 'updateAppMessageShareData'], // 需要检测的JS接口列表,所有JS接口列表见附录2,
        success: function(res:any) {
          console.log(res)
          // 以键值对的形式返回,可用的api值true,不可用为false
          // 如:{"checkResult":{"chooseImage":true},"errMsg":"checkJsApi:ok"}
        }
      })
    } else {
      Toast('微信sdk初始化失败')
    }
  }

  private async wxReady() {
    const _this = this
    wx.ready (function () {
      console.log('shifouzhixingready')
      //分享微信朋友圈
      wx.updateTimelineShareData({
        imgUrl : '',
        link : _this.shareLink,
        title : _this.headerInfo.title,
        success : function () { // 分享成功可以做相应的数据处理
            Toast('分享朋友圈成功!')
        }
      })
      //分享给朋友
      wx.updateAppMessageShareData({
        title: _this.headerInfo.title, // 分享标题
        desc: '这是一个' + _this.headerInfo.title, // 分享描述
        link: _this.shareLink,
        imgUrl: '', // 分享图标
        success: function () {
          Toast('分享成功!')
        }
      })
      wx.hideMenuItems({
        menuList: [] // 要隐藏的菜单项,只能隐藏“传播类”和“保护类”按钮,所有menu项见附录3
      })
    })
  }
6、app内嵌入h5微信支付
  • 问题1、正常encodeURIComponent redirect_url回调地址后,如果需要拼接的参数较多可能会有参数丢失的情况,解决方法:可将参数都放到一个对象里转成字符串(这样差不多就行),但是我这加上了base64编码。回调回来时解析base64然后取值判断即可。
  • 问题2、一般情况请听从官方文档,不要使用H5微信支付内嵌到app,在安卓情况下,系统自带的回退hirstory.back()会造成跳转至唤起支付(也可能是我太菜,没有捋清楚跳转逻辑)
7、关于使用React antDesign 做后台管理系统遇到的问题

——当前开发版和build后的test上渲染标签样式及交互效果不同。在各种找原因后,发现是因为没有锁定版本的问题,然后项目中.gitignore忽略了package-lock.json的提交。 日了

8、阿拉伯数字转汉字(这是copy大佬的代码,blog.csdn.net/TKP666/arti…
export function toChinesNum(num) { // 阿拉伯数字转汉字
  let changeNum = ['零', '一', '二', '三', '四', '五', '六', '七', '八', '九']
  let unit = ['', '十', '百', '千', '万']
  num = parseInt(num)
  let getWan = (temp) => {
    let strArr = temp.toString().split('').reverse()
    let newNum = ''
    let newArr = []
    strArr.forEach((item, index) => {
      newArr.unshift(item === '0' ? changeNum[item] : changeNum[item] + unit[index])
    })
    let numArr = []
    newArr.forEach((m, n) => {
      if (m !== '零') numArr.push(n)
    })
    if (newArr.length > 1) {
      newArr.forEach((m, n) => {
        if (newArr[newArr.length - 1] === '零') {
          if (n <= numArr[numArr.length - 1]) {
            newNum += m
          }
        } else {
          newNum += m
        }
      })
    } else {
      newNum = newArr[0]
    }
    return newNum
  }
  let overWan = Math.floor(num / 10000)
  let noWan = num % 10000
  if (noWan.toString().length < 4) {
    noWan = '0' + noWan
  }
  return overWan ? getWan(overWan) + '万' + getWan(noWan) : getWan(num)
}

9、将秒数转成时分秒
export function formatSeconds(value=0) { // 将秒数转成时分秒
  var secondTime = parseInt(value);// 秒
  var minuteTime = 0;// 分
  var hourTime = 0;// 小时
  if(secondTime > 60) {//如果秒数大于60,将秒数转换成整数
      //获取分钟,除以60取整数,得到整数分钟
      minuteTime = parseInt(secondTime / 60);
      //获取秒数,秒数取余,得到整数秒数
      secondTime = parseInt(secondTime % 60);
      //如果分钟大于60,将分钟转换成小时
      if(minuteTime > 60) {
          //获取小时,获取分钟除以60,得到整数小时
          hourTime = parseInt(minuteTime / 60);
          //获取小时后取余的分,获取分钟除以60取余的分
          minuteTime = parseInt(minuteTime % 60);
      }
  }
  var result = {
    hour: parseInt(hourTime),
    minute: parseInt(minuteTime),
    second: parseInt(secondTime),
    seconds: value
  }
  return result;
}
10、elementUI树节点选择转成 按照 省市区 排列的二维数组中文
入参选择的数据: [[2,50,51],[2,50,52],[2,53,54],[2,53,55],[56,74,75],[56,74,76]]
入参tree书结构数据 部分:
[{"id":2,"dataName":"江西省行政区域","option":"赣州市","isUsed":1,"label":0,"comment":"","parent":"1","childrenIds":"3,4,5,6,7,8,11,14,15,16,17,20,23,26,29,32,35,38,41,44,47,50,53","createdBy":"admin","createdAt":1667791569000,"updatedBy":"admin","updatedAt":1667791573000,"children":[
{"id":3,"dataName":"江西省行政区域","option":"赣州经济技术开发区(国家级)","isUsed":1,"label":1,"comment":"","parent":"2","childrenIds":"","createdBy":"admin","createdAt":1667791569000,"updatedBy":"admin","updatedAt":1667791569000,"children":[]},{"id":4,"dataName":"江西省行政区域","option":"赣州高新技术产业开发区(国家级)","isUsed":1,"label":1,"comment":"","parent":"2","childrenIds":"","createdBy":"admin","createdAt":1667791569000,"updatedBy":"admin","updatedAt":1667791569000,"children":[]}]}]   

输出:[["赣州市","南昌市"],["宁都县","石城县","进贤县"],["宁都工业园","宁都县开发区外","石城产业园","石城县开发区外","江西进贤经济开发区","进贤县开发区外"]]
const tkitreeConfirmText = (data: Array<any>) => { // 将选择value转成label
  filterLabel.value = []
  const T = cloneDeep(treeWrap(filterOptions.value))
  const A = dataMapWrap(data)

  const a: Array<string[]> = []
  for (let i = 0; i < A.length; i++) {
    a[i] = []
  }
  // let a: any = new Array(Array(A.length), () => Array()) // 此处不要用这个,会因为指针问题导致数据被替换
  A.forEach((f: Array<number>, i: number) => {
    T.forEach(item => {
      if (f.indexOf(item.id) !== -1) {
        a[i].push(item.option)
      }
    })
  })
  filterLabel.value = a
}

const dataMapWrap = function (data: Array<number[]>) {
  let aLen: number = 0
  data?.forEach((len) => {
    if (len.length > aLen) { aLen = len.length }
  })
  // let a: Array<number[]> = new Array(aLen).fill([])
  const a: Array<number[]> = []
  for (let i = 0; i < aLen; i++) {
    a[i] = []
  }
  // console.log(a)
  // let a: Array<number[]> = [[], [], []]
  data.forEach((first) => {
    for (let i = 0; i < first.length; i++) {
      if (first[i] && a[i].indexOf(first[i]) === -1) {
        a[i].push(first[i])
      }
    }
  })
  return a
}

const treeWrap = function (dataTree: any[], newTree: any[] = []) { // 将树结构整成一级
  dataTree.forEach((tree) => {
    const ct = JSON.parse(JSON.stringify(tree))
    delete ct.children
    newTree.push(ct)
    if (tree.children.length) { treeWrap(tree.children, newTree) }
  })
  return newTree
}

11、Element ui-plus Tree节点获取半选节点及回显问题

// 我这里是动态的tree

// getHalfCheckedKeys()获取半选的父节点 getCheckedKeys()获取选中节点
const groupList = treeArrRefs.value[currentNodeKey.value + list.groupKey]?.getCheckedKeys().concat(treeArrRefs.value[currentNodeKey.value + list.groupKey]?.getHalfCheckedKeys())


// 回显时遍历返回携带父节点数据,先获取节点node,再使用setChecked设置。直接setCheckedKeys()会使半选父节点也处于勾选状态
list.forEach(i => {
    const node = treeArrRefs.value[currentNodeKey.value + list.groupKey]?.getNode(i);
    treeArrRefs.value[currentNodeKey.value + list.groupKey]?.setChecked(node, true);
})

12、前端实现发版后页面弹出提示

大致思路就是创建web worker线程循环请求项目版本号作对比。

1、定义初始方法

const fs = require('fs')
const path = require('path')

export function GenerateVersionFile(options) {
  return {
    name: 'vite-plugin-generate-version',
    load() {
      if (options && options.isGenerate) {
        this.options = {
          versionFileName: 'generate_version.json', // json 版本文件名称
          keyName: 'UPDATE_VERSION', // json key 值
        }
        this.version = `${Date.now()}.0.0`
        const filePath = path.resolve(__dirname, '../public', this.options.versionFileName) // __dirname当前执行的文件的根目录路径
        // 生成文件
        fs.writeFileSync(filePath, `{"${this.options.keyName}": "${this.version}"}`)
      }
    },
  }
}

2、在vite.config.ts中引入使用

import { GenerateVersionFile } from './scripts/vite-plugin-generate-version.js'
const viteConfig = defineConfig((mode: ConfigEnv) => {
  const env = loadEnv(mode.mode, process.cwd())
  return {
    plugins: [
      GenerateVersionFile({ isGenerate: env.VITE_ENV !== 'localhost' }),
      VueDevTools(),
      vue(),
      vueSetupExtend(),
      viteCompression(),
    ],
    ...
  }
 })
 
export default viteConfig

3、在项目public文件夹中创建worker.js

const UPDATE_VERSION = 'UPDATE_VERSION'

function to(promise, errorExt = '读取文件错误!') {
  return promise
    .then(data => [null, data])
    .catch(err => {
      if (errorExt) {
        const parsedError = Object.assign({}, err, errorExt)
        return [parsedError, undefined]
      }
      return [err, undefined]
    })
}

const fetchUpdateVersionFile = () => {
  return new Promise((resolve, reject) => {
    // 注意:文件请求路径 /generate_version.json,是相对于在 public 文件下的 index.html 的位置而言的,/generate_version.json 代表 generate_version.json 文件与 index.html 文件夹同目录。
    fetch('/generate_version.json')
      .then(res => {
        return res.json()
      })
      .then(json => {
        resolve(json)
      })
      .catch(err => {
        reject(err)
      })
  })
}

self.onmessage = function ({ data }) {
  fetchUpdateVersionFile().then(
    res => {
      if (!res[UPDATE_VERSION]) return
      const inter = setInterval(async () => {
        const [err, res] = await to(fetchUpdateVersionFile())
        if (err) return
        const currentVersion = data[UPDATE_VERSION]
        if (res[UPDATE_VERSION] !== currentVersion) {
          self.postMessage({ message: '版本更新了', isUpdate: true, version: res[UPDATE_VERSION] })
          clearInterval(inter)
        }
      }, 1000 * 60)
    },
    err => {
      console.log('更新版本:', err)
    },
  )
}

self.onerror = function (err) {
  console.log(err)
}

4、在public中创建版本json文件generate_version.json

{ "UPDATE_VERSION": "1702610347668.0.0" }