常用JS工具类函数 | 上

工作中常常很多数据处理操作需要公用,于是抽离出来变为一个个函数,用起来很香,但是由于比较多,手写呢嫌麻烦,这就记一记,以便日后用时ctrl cv一把嗦。

原来只有本篇,再补充一篇

【CSDN】常用JS工具类函数 | 上 (本文)
【CSDN】常用JS工具类函数 | 下

大家都是面向百度编程(手动狗头)🐶,来来来,要就拿走


时间

dayjs 或者 moment 能做很多事情了

时间戳转化为天时分,倒计时时可使用

说明: time为时间戳

const timeDef = end.getTime() - nowTime.getTime()
const dates = formatDuring(timeDef)
function formatDuring(time){
  const st = Number(time);
  const day = parseInt((st / (1000 * 3600 * 24)).toString());
  const hour = parseInt(((st % (1000 * 3600 * 24)) / (1000 * 3600)).toString())
  const minute = parseInt(((st % (1000 * 3600))/(1000 * 60)).toString())
  return [day,hour, minute]
}
// formatDuring(new Date('2020-07-18 20:00').getTime() - new Date('2020-07-17 19:10').getTime())
// [1, 0, 50]

时间转为年月日时分

//传入new Date()
function convertTime(date) {
  let res = '';
  if(date.getFullYear()) {
    const y = date.getFullYear();
    const mo = date.getMonth() + 1 > 10 ? date.getMonth() + 1 : '0'+(date.getMonth()+1)
    const d = date.getDate();
    const h = date.getHours() > 10 ? date.getHours(): '0'+date.getHours();
    const m = date.getMinutes() > 10 ? date.getMinutes(): '0'+date.getMinutes();
    res = y+"/"+mo+"/"+d+" "+h+":"+m;
  }
  return res;
}
// convertTime(new Date()) "2021/07/17 20:24"

格式化时间

function dateFormater(formater, t){
    let date = t ? new Date(t) : new Date(),
        Y = date.getFullYear() + '',
        M = date.getMonth() + 1,
        D = date.getDate(),
        H = date.getHours(),
        m = date.getMinutes(),
        s = date.getSeconds();
    return formater.replace(/YYYY|yyyy/g,Y)
        .replace(/YY|yy/g,Y.substr(2,2))
        .replace(/MM/g,(M<10?'0':'') + M)
        .replace(/DD/g,(D<10?'0':'') + D)
        .replace(/HH|hh/g,(H<10?'0':'') + H)
        .replace(/mm/g,(m<10?'0':'') + m)
        .replace(/ss/g,(s<10?'0':'') + s)
}
// dateFormater('YYYY-MM-DD HH:mm', t) ==> 2019-06-26 18:30
// dateFormater('YYYYMMDDHHmm', t) ==> 201906261830

当前范围值直至多少天后(前)的值

说明: range 前面给’-'返回多少天前,否则多少天后,Delimiter 分隔符

function getDateTime(range, Delimiter = "-") {
  const date = [];
  const now = new Date();
  const rangeStr = range.toString();
  if(rangeStr.indexOf("-") === -1){
    date[0] = now.toLocaleDateString().replace(/\//g, Delimiter);
    date[1] = new Date(now.setDate(now.getDate() + Number(rangeStr)))
      .toLocaleDateString().replace(/\//g, Delimiter);
  }
  else {
    date[0] = new Date(now.setDate(now.getDate() - Number(rangeStr.split("-")[1])))
      .toLocaleDateString().replace(/\//g, Delimiter)
    date[1] = new Date().toLocaleDateString().replace(/\//g, Delimiter);
  }
  return date;
}
// 今天到未来五天
// getDateTime(5, '-=') 
// ["2021-=7-=17", "2021-=7-=22"]

返回两个时间戳相差的天数

这个与 时间戳转化为天时分,倒计时时可使用 有点重复

function dateDiffs(start, end){
  const sTime = new Date(start).getTime(),
        eTime = new Date(end).getTime();
  if(sTime && eTime) {
    return Math.floor((eTime - sTime)/(1000*3600*24))
  }
  else {
    throw new Error("传入的日期格式错误")
  }
}
// dateDiffs('2021-07-1', '2021-07-08')
// 7

转换时间(今日,明日,几月几日转为标准时间)

这是我在思否一个问题下的 回答

function formatTime(dateStr = '') {
      if(dateStr) {
        const dateMatch = dateStr.replace(/^(([月\d]{3,5})|([前今明后]))日(\s[0-9:]{8})$/, (match, p1, p2, p3, time) => {
          let date = ''
          if(p1.length === 1) {
            const dateMap = {
              '前': -2,
              '昨': -1,
              '明': 1
            };
            const count = dateMap[p1] || 0;
            const today = new Date().getDate()
            const dateNum = today + count;
            const current = new Date();
            current.setDate(dateNum);
            date = new Date(current).toLocaleDateString();
          }else {
            const year = new Date().getFullYear();
            const mon = p1.split('月')[0];
            const day = p1.split('月')[1];

            const current = new Date()
            current.setMonth(mon - 1);
            current.setDate(day);

            date = new Date(current).toLocaleDateString();
          }
          const resStr = date + time;
          const str = new Date(date + time).toLocaleString()
          console.log('end: ', dateStr, ' => ', str);
          return str;
        })
        return dateMatch
      }
    }
    console.log(formatTime('明日 10:30:40')); // 2023/6/2 10:30:40
    console.log(formatTime('今日 14:00:08')); // 2023/6/1 14:00:08
    console.log(formatTime('6月8日 17:07:16')); // 2023/6/8 17:07:16
    
    // 稍微有些特殊的情况
    console.log(formatTime('0月1日 17:07:16')); //2022/12/1 17:07:16
    console.log(formatTime('12月35日 17:07:16')); // 2024/1/4 17:07:16
    console.log(formatTime('13月3日 17:07:16')); // 2024/1/3 17:07:16

获取当前日期的周一、周末的日期

export function getWeeekdata(cdate){  //cdate 传来当前的时间
   let now = new Date(cdate);
   let year = now.getFullYear();
   let month = now.getMonth() + 1;
   let date = now.getDate();
   let nowTime = now.getTime();
   let day = now.getDay();
   let oneDayTime = 24 * 60 * 60 * 1000;
   if(day==0){
    var MondayTime = nowTime - 6 * oneDayTime;
    var SundayTime = nowTime;
   }else if(day==1){
     var MondayTime = nowTime;
     var SundayTime = nowTime + (7 - day) * oneDayTime;
   }else{
     var MondayTime = nowTime - (day - 1) * oneDayTime;
     //显示周日
     var SundayTime = nowTime + (7 - day) * oneDayTime;
   }
   //初始化日期时间
   var monday = new Date(MondayTime);
   var sunday = new Date(SundayTime);
   return [format(monday), format(sunday)];
 }
 function format(date) {
   var time = new Date(date);
   var y = time.getFullYear();
   var m =
     time.getMonth() + 1 < 10 ? "0" + (time.getMonth() + 1) : time.getMonth() + 1;
   var d = time.getDate() < 10 ? "0" + time.getDate() : time.getDate();
   //var h = time.getHours();
   //var mm = time.getMinutes();
   //var s = time.getSeconds();
   return y + "-" + m + "-" + d;
 }

根据当前时间获取当月的1号和最后一号

export function getcurentMonth(cdate) { //cdate传来的当前的时间
  
   // 当天
   let thatDay = "";
   // 当月第一天
   let oneDayTime = "";
   // 当月最后一天
   let zDay = "";
   let date = new Date(cdate);
   let curr_date = date.getDate();
   let curr_month = date.getMonth() + 1;
   let curr_year = date.getFullYear();
   String(curr_month).length < 2 ? (curr_month = "0" + curr_month) : curr_month;
   String(curr_date).length < 2 ? (curr_date = "0" + curr_date) : curr_date;
   thatDay = curr_year + "-" + curr_month + "-" + curr_date;

   String(curr_year).length < 2 ? (curr_year = "0" + curr_year) : curr_year;
   var m = date.getMonth() + 1;
   String(m).length < 2 ? (m = "0" + m) : m;
   var d = "01";
   oneDayTime = curr_year + "-" + m + "-" + d;
   //结束时间
   var currentMonth = date.getMonth();
   var nextMonth = ++currentMonth;
   var nextMonthFirstDay = new Date(date.getFullYear(), nextMonth, 1);
   var oneDay = 1000 * 60 * 60 * 24;
   var date1 = new Date(nextMonthFirstDay - oneDay);
   var yy = date1.getFullYear();
   String(yy).length < 2 ? (yy = "0" + yy) : yy;
   var mm = date1.getMonth() + 1;
   String(mm).length < 2 ? (mm = "0" + mm) : mm;
   var dd = date1.getDate();
   String(dd).length < 2 ? (dd = "0" + dd) : dd;
   zDay = yy + "-" + mm + "-" + dd;
   return [thatDay, oneDayTime, zDay];
 }

对象

深度克隆(仅支持数组、对象等)

//深克隆的递归实现
export function deepClone(target) {
  //判断拷贝的数据类型
  //初始化变量result 成为最终克隆的数据
  function checkedType(targets: objectData | arrayData): string {
    return Object.prototype.toString.call(targets).slice(8, -1)
  }
  let result, targetType: string = checkedType(target)

  if (targetType === 'Object') {
    result = {}
  } else if (targetType === 'Array') {
    result = [];
  } else {
    return target
  }
  //遍历目标数据
  for (let i in target) {
    let value = target[i]
    if (checkedType(value) === 'Object' ||
      checkedType(value) === 'Array') {
      result[i] = deepClone(value)
    } else {
      result[i] = value;
    }
  }
  return result
}

清空对象值

// 这个有点鸡肋
export function clearForm(obj: objectData): objectData {
  const object: objectData = new Object();
  for(let k in obj) {
    if(Object.prototype.toString.call(obj[k]) === '[object Object]')
      object[k] = {};
    else if(Object.prototype.toString.call(obj[k]) === '[object Array]')
      object[k] = [];
    else object[k] = "";
  }
  return object;
}

其他

去驼峰

推荐 JS 正则迷你书⇲

function reverseHump(k){
	return k.replace(/([A-Z])/g, '_$1').replace(/[-_\s]+/g, '_').toLowerCase();
}
// reverseHump('airBox') => "air_box"

驼峰化

function hump(k){ 
	return k.replace(/[-_\s]+(.)?/g, function(match,c) {
  		return c? c.toUpperCase() : "";
	});
}
// hump('air_Box') =>  "airBox"

转换

kb转更大单位(Mb, Gb等)

function formatSizeUnits(kb) {
    let units = ['KB', 'MB', 'GB', 'TB', 'PB'];
    let unitIndex = 0;

    while (kb >= 1024 && unitIndex < units.length - 1) {
        kb /= 1024;
        unitIndex++;
    }

    return `${kb.toFixed(2)} ${units[unitIndex]}`;
}

防抖

想要了解更多防抖节流 可参考

防抖01

短时间多次执行只执行一次

/**
	func 回调函数
	wait 等待时间
	immediate {boolean} 是否立即值执行
*/
function debounce(func, wait, immediate) {
    var timeout, result;
    
    var debounced = function () {
        var context = this;
        var args = arguments;
        
        if (timeout) clearTimeout(timeout);
        if (immediate) {
            // 如果已经执行过,不再执行
            var callNow = !timeout;
            timeout = setTimeout(function(){
                timeout = null;
            }, wait)
            if (callNow) result = func.apply(context, args)
        } else {
            timeout = setTimeout(function(){
                func.apply(context, args)
            }, wait);
        }
        return result;
    };

    debounced.cancel = function() {
        clearTimeout(timeout);
        timeout = null;
    };

    return debounced;
}

防抖02 vue装饰器使用版本

/**
	func 回调函数
	wait 等待时间
	immediate {boolean} 是否立即值执行
*/
export class Debounced {
  /**
   * @param func 需要包装的函数
   * @param delay 延迟时间,单位ms
   * @param immediate 是否默认执行一次(第一次不延迟)
   */
  public use = (func: Function, delay: number, immediate: boolean = false): Function => {
    let timer: number | undefined
    return ( ...args: any) => {
      if (immediate) {
        func.apply(this, args) // 确保引用函数的指向正确,并且函数的参数也不变
        immediate = false
        return
      }
      clearTimeout(timer)
      timer = setTimeout(() => {
        func.apply(this, args)
      }, delay)
    }
  }
}

xxx.vue
使用

private debouncedBySatus = new Debounced().use(this.getOrders, 1000, true);

// 用@click = “handleEvent”
private handleEvent(){
	//.....
  this.debouncedBySatus();
}

节流

频繁触发,单位时间 wait 内执行一次, 支持

  • 支持立即执行;
  • 函数可能有返回值;
  • 支持取消功能;
/**
	func 回调函数
	wait 等待时间
	options { leading , trailing } 立即执行,尾调用
*/
function throttle(func, wait, options) {
    var timeout, context, args, result;
    var previous = 0;
    if (!options) options = {};

    var later = function() {
        previous = options.leading === false ? 0 : new Date().getTime();
        timeout = null;
        func.apply(context, args);
        if (!timeout) context = args = null;
    };

    var throttled = function() {
        var now = new Date().getTime();
        if (!previous && options.leading === false) previous = now;
        var remaining = wait - (now - previous);
        context = this;
        args = arguments;
        if (remaining <= 0 || remaining > wait) {
            if (timeout) {
                clearTimeout(timeout);
                timeout = null;
            }
            previous = now;
            func.apply(context, args);
            if (!timeout) context = args = null;
        } else if (!timeout && options.trailing !== false) {
            timeout = setTimeout(later, remaining);
        }
    };
    
    throttled.cancel = function() {
        clearTimeout(timeout);
        previous = 0;
        timeout = null;
    }
    return throttled;
}

检测数据类型

// 返回 Object/Array
function checkedType(targets) {
  return Object.prototype.toString.call(targets).slice(8, -1)
}
// checkedType({}) "Object"
// checkedType([]) "Array"

cookie处理

const getCookie = (key) => {
  const reg = new RegExp('\\b'+key+'=([^;]+)(;|$)','i');// '/\b'+key+'=([^;]+)'+value+'(;|$)'/i'
  let val = document.cookie.match(reg);
  return val ? val[1]: '';
};

const setCookie = (c_name,value,expiredays) => {
  let exdate=new Date();
  exdate.setDate(exdate.getDate()+expiredays);
  document.cookie=c_name+ "=" +value+
    ((expiredays==null) ? "" : ";expires="+exdate.toDateString())
};

const delCookie = (name) => {
  setCookie(name,'',-1);
};

setTimeOut定时任务的实现

模拟 setinterval ,但是此次任务完成才执行下一次任务。

//setTimeOut模拟定时器
let timer = null;
let count = 0;
function plus(){ count++; conole.log('任务被执行 ', count) }
function t1(){
    // alert(1)
    console.log('done', count);
    return new Promise((resolve) => {
        setTimeout(() => { 
            plus() // resolve可能在plus内部,确定plus结束,才会重新等待执行下一次任务
            resolve(1)
        }, 6000);
    })
}
async function  mocker(){
    const res = await t1();
    if(timer !== null) clearTimeout(timer);
    if(res) timer = setTimeout(mocker, 5000);
}
mocker();

首字母大写

function firstUpper(str = ''){
  return str.toLowerCase().replace(/( |^)[a-z]/g, (s) => s.toUpperCase());
}

图片压缩

function compressImg(file) {
  return new Promise(resolve => {
    let reader = new FileReader();
    let ndata;
    // 将图片2将转成 base64 格式
    reader.readAsDataURL(file);
    // 读取成功后的回调
    reader.onloadend = function () {
      let result = this.result;
      let img = new Image();
      img.src = result;

      img.onload = function () {
        ndata = compress(img);
        //BASE64转图片
        const arr = ndata.split(',');
        const mime = arr[0].match(/:(.*?);/)[1];
        const bstr = atob(arr[1]);
        let n = bstr.length;
        const u8arr = new Uint8Array(n);

        while (n--) {
          u8arr[n] = bstr.charCodeAt(n);
        }
        ndata = new File([u8arr], file.name, { type: mime })
        resolve({ flag: 1, file: ndata })
      }
    }
  })
}
function compress(img) {
  let canvas = document.createElement("canvas");
  let ctx = canvas.getContext('2d');
  //瓦片canvas
  let tCanvas = document.createElement("canvas");
  let tctx = tCanvas.getContext("2d");
  let initSize = img.src.length;
  let width = img.width;
  let height = img.height;
  //如果图片大于四百万像素,计算压缩比并将大小压至400万以下
  let ratio;
  if ((ratio = width * height / 4000000) > 1) {
    ratio = Math.sqrt(ratio);
    width /= ratio;
    height /= ratio;
  } else {
    ratio = 1;
  }
  canvas.width = width;
  canvas.height = height;
  // 铺底色
  ctx.fillStyle = "#fff";
  ctx.fillRect(0, 0, canvas.width, canvas.height);
  //如果图片像素大于100万则使用瓦片绘制
  let count;
  if ((count = width * height / 1000000) > 1) { // 超过100W像素
    count = ~~(Math.sqrt(count) + 1); //计算要分成多少块瓦片
    // 计算每块瓦片的宽和高
    let nw = ~~(width / count);
    let nh = ~~(height / count);
    tCanvas.width = nw;
    tCanvas.height = nh;
    for (let i = 0; i < count; i++) {
      for (let j = 0; j < count; j++) {
        tctx.drawImage(img, i * nw * ratio, j * nh * ratio, nw * ratio, nh * ratio, 0, 0, nw, nh);
        ctx.drawImage(tCanvas, i * nw, j * nh, nw, nh);
      }
    }
  } else {
    ctx.drawImage(img, 0, 0, width, height);
  }
  //进行最小压缩
  let ndata = canvas.toDataURL('image/jpeg', 0.1);
  console.log('压缩前:' + initSize);
  console.log('压缩后kb:' + ndata.length/1024);
  console.log("ndata:" + ndata)
  console.log('压缩率:' + ~~(100 * (initSize - ndata.length) / initSize) + "%");
  tCanvas.width = tCanvas.height = canvas.width = canvas.height = 0;
  return ndata;
}

图片懒加载

与常规的相比增加了

  • 图片全部加载完成后移除事件监听;
  • 加载完的图片,从 imgList 移除;
let imgList = [...document.querySelectorAll('img')]
let length = imgList.length

const imgLazyLoad = function() {
    let count = 0
    return (function() {
        let deleteIndexList = []
        imgList.forEach((img, index) => {
            let rect = img.getBoundingClientRect()
            if (rect.top < window.innerHeight) {
                img.src = img.dataset.src
                deleteIndexList.push(index)
                count++
                if (count === length) {
                    document.removeEventListener('scroll', imgLazyLoad)
                }
            }
        })
        imgList = imgList.filter((img, index) => !deleteIndexList.includes(index))
    })()
}

// 这里最好加上防抖处理
document.addEventListener('scroll', imgLazyLoad)

将url解析为对象

function parseParam(url) {
    const paramsStr = /.+\?(.+)$/.exec(url)[1]; // 将 ? 后面的字符串取出来
    const paramsArr = paramsStr.split('&'); // 将字符串以 & 分割后存到数组中
    let paramsObj = {};
    // 将 params 存到对象中
    paramsArr.forEach(param => {
        if (/=/.test(param)) { // 处理有 value 的参数
            let [key, val] = param.split('='); // 分割 key 和 value
            val = decodeURIComponent(val); // 解码
            val = /^\d+$/.test(val) ? parseFloat(val) : val; // 判断是否转为数字
    
            if (paramsObj.hasOwnProperty(key)) { // 如果对象有 key,则添加一个值
                paramsObj[key] = [].concat(paramsObj[key], val);
            } else { // 如果对象没有这个 key,创建 key 并设置值
                paramsObj[key] = val;
            }
        } else { // 处理没有 value 的参数
            paramsObj[param] = true;
        }
    })
    return paramsObj;
}
// parseParam('http://www.baidu.com?name=李四&age=46&from=china')
// {name: "李四", age: 46, from: "china"}

或者当前 url 获取 url 指定的参数

function getQueryVariable(variable) {
  const query = window.location.search.substring(1);
  const vars = query.split("&");
  for (let i = 0; i < vars.length; i++) {
    const pair = vars[i].split("=");
    if (pair[0] == variable) {
      return pair[1];
    }
  }
  return undefined;
}

其实一般工具类库 lodash underscore(记得好像是这个) 都有的 尽 量 用库。

文件

判断文件上传的类型

/**
 * @param: fileName - 文件名称
 * @param: 数据返回 1) 无后缀匹配 - false
 * @param: 数据返回 2) 匹配图片 - image
 * @param: 数据返回 3) 匹配 txt - txt
 * @param: 数据返回 4) 匹配 excel - excel
 * @param: 数据返回 5) 匹配 word - word
 * @param: 数据返回 6) 匹配 pdf - pdf
 * @param: 数据返回 7) 匹配 ppt - ppt
 * @param: 数据返回 8) 匹配 视频 - video
 * @param: 数据返回 9) 匹配 音频 - radio
 * @param: 数据返回 10) 其他匹配项 - other
 * @author: ljw
 **/

export function fileSuffixTypeUtil(fileName){
      // 后缀获取
    var suffix = "";
    // 获取类型结果
    var result = "";
    try {
      var flieArr = fileName.split(".");
      suffix = flieArr[flieArr.length - 1];
    } catch (err) {
      suffix = "";
    }
    // fileName无后缀返回 false
    if (!suffix) {
      result = false;
      return result;
    }
    // 图片格式
    var imglist = ["png", "jpg", "jpeg", "bmp", "gif"];
    // 进行图片匹配
    result = imglist.some(function (item) {
      return item == suffix;
    });
    if (result) {
      result = "image";
      return result;
    }
    // 匹配txt
    var txtlist = ["txt"];
    result = txtlist.some(function (item) {
      return item == suffix;
    });
    if (result) {
      result = "txt";
      return result;
    }
    // 匹配 excel
    var excelist = ["xls", "xlsx"];
    result = excelist.some(function (item) {
      return item == suffix;
    });
    if (result) {
      result = "excel";
      return result;
    }
    // 匹配 word
    var wordlist = ["doc", "docx"];
    result = wordlist.some(function (item) {
      return item == suffix;
    });
    if (result) {
      result = "word";
      return result;
    }
    // 匹配 pdf
    var pdflist = ["pdf"];
    result = pdflist.some(function (item) {
      return item == suffix;
    });
    if (result) {
      result = "pdf";
      return result;
    }
    // 匹配 ppt
    var pptlist = ["ppt"];
    result = pptlist.some(function (item) {
      return item == suffix;
    });
    if (result) {
      result = "ppt";
      return result;
    }
    // 匹配 视频
    var videolist = ["mp4", "m2v", "mkv"];
    result = videolist.some(function (item) {
      return item == suffix;
    });
    if (result) {
      result = "video";
      return result;
    }
    // 匹配 音频
    var radiolist = ["mp3", "wav", "wmv"];
    result = radiolist.some(function (item) {
      return item == suffix;
    });
    if (result) {
      result = "radio";
      return result;
    }
    // 其他 文件类型
    result = "other";
    return result;
};

文件下载

/**
 * @param: fileType - 文件类型
 * @param: fileName - 文件名称
 * @param: data  - 数据流文件
 **/
export function download(fileType, bucketName,data) {
  let downType = "";
  let downName = "";
  if (fileType == "image") {
    downType = "image/png";
    downName = fileName + ".png";
  } else if (fileType == "word") {
    downType = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
    downName = fileName + ".docx";
  } else if (fileType == "video") {
    downType = "video/mpeg4";
    downName = fileName + ".mp4";
  } else if (fileType == "radio") {
    downType = "audio/mpeg";
    downName = fileName + ".mp3";
  } else if (fileType == "pdf") {
    downType = "application/pdf";
    downName = fileName + ".pdf";
  }
  let blob = new Blob([data], { type: downType });
  let downloadElement = document.createElement("a");
  let href = window.URL.createObjectURL(blob);
  downloadElement.href = href;
  document.body.appendChild(downloadElement);
  //downloadElement.setAttribute("download", downName);//设置下载名称
  downloadElement.download = downName; //设置下载文件名称
  downloadElement.click();
  document.body.removeChild(downloadElement); //移除元素;防止连续点击创建多个a标签
  window.URL.revokeObjectURL(href);
}

参考

站在别人肩膀上能看的更远,感谢💖💖💖以下文章作者

【掘金】| JS兵法36 计,你会多少?⇲
js常用技巧汇总

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值