最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • Javascript 高效开发工具函数

    正文概述 掘金(JayYuen)   2021-04-02   562

    数组

    • multArray二维数组转换
    • flatten扁平化数组
    • flattenDeep指定层级扁平化数组
    • isArrayEqual检查两个数组各项相等
    • allEqual检查数组各项相等
    • diffArray具有唯一array值的数组
    • haveArr具有共同array值的数组
    • uniqueArray数组去重
    • uniqueArrayObject数组对象去重
    • treeData生成树结构数据
    • ascArr数组升序
    • descArr数组降序
    • shuffle随机排序
    • takeArray截取数组开始指定的元素
    • takeLastArray截取数组最后指定的元素
    • cloneArray克隆数组
    • maxArray数组中最大值
    • validArray去除数组中的无效值

    对象

    • cloneObject克隆对象

    函数

    • debounce函数防抖
    • throttle函数节流
    • typeFn类型判断
    • calcFn 加减乘除运算

    字符串

    • isNil值是否是nullundefined
    • padStart遮住字符串
    • thousands数字每隔三位数加分号

    数字

    • randomNumber指定范围的随机整数
    • average求平均值
    • averageBy检查数组对象各项相等
    • aboutEqual两个值是否约等于
    • getLineSize计算两点之间的距离
    • sum数组中值总和

    浏览器

    • copyTextH5 复制文本
    • getCurrentURL获取当前的 URL 地址
    • scrollToTop返回顶部
    • smoothScroll平滑滚动页面
    • isCurrentPage是否是当前页面

    环境

    • isBrowser是否是浏览器
    • isWechatBrowser判断微信浏览器还是普通 h5
    • isMobile判断是否是移动端

    一、数组

    multArray二维数组转换

    将数组(array)拆分成多个子数组,并将这些子数组组成一个新数组。

    multArray(array, count)
    

    参数

    • array需要处理的数组
    • count = 8子数组需要的长度

    示例

    multArray([1, 2, 3, 4, 5, 6], 2)
    => [[1, 2], [3, 4], [5, 6]]
    
    multArray(['a', 'b', 'c', 'd'], 3)
    => [['a', 'b', 'c'], ['d']]
    

    源码

    const multArray = (arr, count = 2) => {
        let pages = [];
        arr.forEach((item, index) => {
            const page = Math.floor(index / count);
            (!pages[page]) && (pages[page] = []);
            pages[page].push(item);
        });
        return pages;
    };
    

    flatten扁平化数组

    将多层嵌套数组(array)拆分成一个数组

    flatten(array)
    

    参数

    • array多层嵌套数组

    示例

    flatten([1, [2], [3], [4, 5]])
    
    // [1, 2, 3, 4, 5]
    

    源码

    // 扁平化  Map 方法
    const flatten = arr => [].concat(...arr.map(item => (Array.isArray(item) ? flatten(item) : item)));
    
    // 扁平化  reduce 方法
    const flatten = arr => arr.reduce((a, i) => a.concat(Array.isArray(i) ? flatten(i) : i), []);
    

    flattenDeep指定层级扁平化数组

    将多层嵌套数组(array)拆分成指定层级数组

    flattenDeep(array, depth)
    

    参数

    • array多层嵌套数组 depth = 减少的嵌套层级数

    示例

    flattenDeep(['a', ['b', ['c', ['d']], 'e']], 1)
    // => ['a', 'b', ['c', ['d']], 'e']
    
    // ES6方法 `flat(depth)`
    ['a', ['b', ['c', ['d']], 'e']].flat(1)
    // => ['a', 'b', ['c', ['d']], 'e']
    

    源码

    const flattenDeep = (arr, depth = 1) => arr.reduce((a, i) => a.concat(depth > 1 && Array.isArray(i) ? flatten(i, depth - 1) : i), []);
    

    isArrayEqual检查两个数组各项相等

    比较两个数组内的各项值是否相等,返回一个Boolean

    isArrayEqual(arrOne, arrTwo)
    

    参数

    • arrOne 要检查的数组
    • arrTwo 要检查的数组

    示例

    isArrayEqual([6, 5, 2, 4, 1, 3], [1, 2, 3, 4, 5, 6])
    // => true
    
    isArrayEqual([6, 5, 2, 7, 1, 3], [1, 2, 3, 4, 5, 6])
    // => false
    

    源码

    const isArrayEqual = (a, b, res = true) => {
        if (a.length !== b.length) return (res = false);
        const s = new Set(b);
        if (a.find(x => !s.has(x))) return (res = false);
        return res;
    };
    

    allEqual检查数组各项相等

    allEqual(array)
    

    参数

    • array 要检查的数组

    示例

    allEqual([1, 2, 3, 4, 5, 6])
    // => false
    
    allEqual([1, 1, 1, 1])
    // => true
    

    源码

    const allEqual = arr => arr.every(item => item === arr[0]);
    

    diffArray具有唯一array值的数组

    创建一个具有唯一 array 值的数组,每个值不包含在其他给定的数组中

    diffArray(arrOne, arrTwo)
    

    参数

    • arrOne 要检查的数组
    • arrTwo要排除的数组

    示例

    diffArray(['a', 2, 6, 7], ['a', 2, 9, 'b'])
    // => [ 6, 7 ]
    

    源码

    const diffArray = (a, b) => {
        const s = new Set(b);
        let arr = a.filter(x => !s.has(x));
        return arr;
    }
    

    haveArr具有共同array值的数组

    创建一个具有共同 array 值的数组,每个值包含在其他给定的数组中

    haveArr(arrOne, arrTwo)
    

    参数

    • arrOne 要检查的数组
    • arrTwo要包含的数组

    示例

    haveArr([1, 2, 6, 7], [1, 2, 9, 5])
    // => [ 1, 2 ]
    

    源码

    const haveArr = (a, b) => {
        const s = new Set(b);
        return a.filter(x => s.has(x));
    }
    // ES6 includes
    const haveArr = (arr, values) => arr.filter(v => values.includes(v));
    

    uniqueArray数组去重

    创建一个去重后的 array 数组副本

    uniqueArray(array)
    

    参数

    • array 要去重的数组

    示例

    uniqueArray([1, 2, 2, 3, 4, 4, 5])
    // => [ 1, 2, 3, 4, 5 ]
    

    源码

    const uniqueArray = (...arr) => [...new Set(arr)];
    
    const uniqueArray = (...arr) => Array.from(new Set(arr));
    

    uniqueArrayObject数组对象去重

    创建一个去重后的 array 数组对象副本

    uniqueArrayObject(array)
    

    参数

    • array 要去重的数组
    • key 要去重的对象属性值

    示例

    const responseList = [
        { id: 1, a: 1 },
        { id: 2, a: 2 },
        { id: 3, a: 3 },
        { id: 1, a: 4 },
        { id: 2, a: 2 },
        { id: 3, a: 3 },
        { id: 1, a: 4 },
        { id: 2, a: 2 },
        { id: 3, a: 3 },
        { id: 1, a: 4 },
        { id: 2, a: 2 },
        { id: 3, a: 3 },
        { id: 1, a: 4 },
    ]
    
    uniqueArrayObject(responseList, 'id')
    
    // => [ { id: 1, a: 1 }, { id: 2, a: 2 }, { id: 3, a: 3 } ]
    

    源码

    const uniqueArrayObject = (arr, key) => {
        return arr.reduce((acc, cur) => {
            const ids = acc.map(item => item[key]);
            return ids.includes(cur[key]) ? acc : [...acc, cur];
        }, []);
    }
    

    treeData生成树结构数据

    该函数传入一个数组, 每项id对应其父级数据parent_id,返回一个树结构数组

    treeData(array, id, parent_id)
    

    参数

    • array 要生成树结构的数组
    • id 自定义属性名
    • parent_id 父级自定义属性名

    示例

    const comments = [
        { id: 1, parent_id: null },
        { id: 2, parent_id: 1 },
        { id: 3, parent_id: 1 },
        { id: 4, parent_id: 2 },
        { id: 5, parent_id: 4 },
    ]
    
    treeData(comments)
    
    // => [ { id: 1, parent_id: null, children: [ [Object], [Object] ] } ]
    

    源码

    const treeData = (arr, id = null, link = 'parent_id') => arr.filter(item => item[link] === id).map(item => ({ ...item, children: treeData(arr, item.id) }));
    

    ascArr数组升序

    返回升序后的新数组

    ascArr(array)
    

    参数

    • array 要检查的排序数组

    示例

    ascArr([3, 2, 3, 4, 1])
    // => [ 1, 2, 3, 3, 4 ]
    

    源码

    // 通过ES6 ...展开运算符浅拷贝一份新数组
    const ascArr = arr => [...arr].sort((a, b) => a - b);
    

    descArr数组降序

    返回降序后的新数组

    descArr(array)
    

    参数

    • array 要检查的排序数组

    示例

    descArr([3, 2, 3, 4, 1])
    // => [ 1, 2, 3, 3, 4 ]
    

    源码

    const descArr = arr => [...arr].sort((a, b) => b - a);
    

    shuffle随机排序

    创建一个随机的数组,使用Fisher-Yates算法随机排序数组的元素

    shuffle(array)
    

    参数

    • array 要随机的数组

    示例

    shuffle([2, 3, 1])
    // => [3, 1, 2]
    

    源码

    const shuffle = ([...arr]) => {
        let m = arr.length;
        while (m) {
            const i = Math.floor(Math.random() * m--);
            [arr[m], arr[i]] = [arr[i], arr[m]];
        };
        return arr;
    }
    

    takeArray截取数组开始指定的元素

    从 array 数组的最开始一个元素开始提取 n 个元素

    takeArray(array, n)
    

    参数

    • array要检索的数组。
    • n=要提取的元素n个数。

    示例

    takeArray([2, 3, 1], 2)
    // => [2, 3]
    

    源码

    const takeArray = (arr, n = 1) => arr.slice(0, n);
    

    takeLastArray截取数组最后指定的元素

    从 array 数组的最后一个元素开始提取 n 个元素

    takeLastArray(array, n)
    

    参数

    • array要检索的数组。
    • n=要提取的元素n个数。

    示例

    takeArray([2, 3, 1], 2)
    // => [3, 1]
    

    源码

    const takeLastArray = (arr, n = 1) => arr.slice(0, -n);
    

    cloneArray克隆数组

    浅拷贝一份数组副本

    cloneArray(array)
    

    参数

    • array要复制的数组

    示例

    cloneArray([1, 24])
    // => [1, 24]
    

    源码

    // ES6 ...
    const cloneArray = arr => [...arr];
    
    // ES6 Array.from
    const cloneArray = arr => Array.from(arr);
    
    // concat()
    const cloneArray = arr => [].concat(arr);
    
    // map()
    const cloneArray = arr => arr.map(x => x);
    

    maxArray数组中最大值

    过滤原数组中所有的非假值元素,返回数组中的最大值

    maxArray(array)
    

    参数

    • array待处理的数组

    示例

    maxArray([0, -1, -2, -3, false])
    // => 0
    

    源码

    const maxArray = arr => Math.max(...arr.filter(v => Boolean(v) || v === 0));
    

    minArray数组中最小值

    过滤原数组中所有的非假值元素,返回数组中的最小值

    minArray(array)
    

    参数

    • array待处理的数组

    示例

    minArray([0, -1, -2, -3, false])
    // => -3
    

    源码

    const minArray = arr => Math.min(...arr.filter(v => Boolean(v) || v === 0));
    

    validArray去除数组中的无效值

    创建一个新数组,包含原数组中所有的非假值元素。例如false, null,0, "", undefined, 和 NaN 都是被认为是“假值”。

    validArray(array)
    

    参数

    • array待处理的数组

    示例

    minArray([0, 1, false, 2, '', 3])
    // => [1, 2, 3]
    

    源码

    const validArray = arr => arr.filter(Boolean);
    

    二、对象

    cloneObject克隆对象

    浅拷贝一份对象副本

    cloneObject(object)
    

    参数

    • object要复制的对象

    示例

    const a = { x: 1, y: 1 }
    const b = cloneObject(a)
    // => a !== b
    

    源码

    // ES6 ...
    const cloneObject = (obj, temp = {}) => (temp = { ...obj });
    
    // Object.assign()
    const cloneObject = obj => Object.assign({}, obj);
    

    三、函数

    debounce函数防抖

    在事件被触发 n 秒后再执行回调,如果在这 n 秒内又被触发,则重新计时。

    debounce(fn, wait)
    

    参数

    • fn 要防抖动的函数
    • wait=500需要延迟的毫秒数

    示例

    debounce(()=> { console.log('debounce') }, 1000)
    // => 1秒后打印'debounce'
    

    源码

    /** *
     * 防抖
     * @parmas fn 回调函数
     * @parmas time 规定时间
     */
    const debounce = (() => {
        let timer = {};
        return function (func, wait = 500) {
            let context = this; // 注意 this 指向
            let args = arguments; // arguments中存着e
            let name = arguments[0].name || 'arrow'; // 箭头函数
            if (timer[name]) clearTimeout(timer[name]);
            timer[name] = setTimeout(() => {
                func.apply(this, args);
            }, wait);
        };
    })()
    

    throttle函数节流

    规定一个单位时间,在这个单位时间内,只能有一次触发事件的回调函数执行,如果在同一个单位时间内某事件被触发多次,只有一次能生效。

    throttle(fn, wait)
    

    参数

    • fn 要节流的函数
    • wait=500需要延迟的毫秒数

    示例

    throttle(() => {
        console.log('throttle')
    }, 1000)
    // 1秒多次触发打印一次`throttle`
    

    源码

    /** *
     * 节流(规定的时间才触发)
     * @parmas fn 结束完运行的回调
     * @parmas delay 规定时间
     */
    export const throttle = (() => {
        let timeout = null;
        return function (func, wait) {
            let context = this;
            let args = arguments;
            if (!timeout) {
                timeout = setTimeout(() => {
                    timeout = null;
                    func.apply(context, args);
                }, wait);
            };
        };
    })()
    
    throttle(fn, 300)
    

    typeFn类型判断

    判断是否是 Array Object String Number类型

    typeFn.type(value)
    

    参数

    • type 数据类型
    • value要检验的值

    示例

    typeFn.String('1')
    typeFn.Number(1)
    typeFn.Boolean(false)
    typeFn.Null(null)
    typeFn.Array([1, 2])
    typeFn.Object({ a: 1 })
    typeFn.Function(() => {})
    
    // => true
    

    源码

    let typeFn = {};
    const curring = (fn, arr = []) => {
        let len = fn.length;
        return (...args) => {
            arr = arr.concat(args);
            if (arr.length < len) {
                return curring(fn, arr);
            };
            return fn(...arr);
        };
    };
    
    const isType = (type, content) => Object.prototype.toString.call(content) === `[object ${type}]`;
    ['String', 'Number', 'Boolean', 'Null', 'Array', 'Object', 'Function'].forEach(item => {
        typeFn[item] = curring(isType)(item);
    });
    

    calcFn加减乘除运算

    因为 JavaScript 遵循 IEEE 754 数学标准,使用 64 位浮点数进行运算。在进行十进制运算时会导致精度丢失。

    calcFn.add(value1, value2, value3)
    

    参数

    • addsubmuldiv运算符
    • value要计算的值

    示例

    解决 0.1+0.2 !== 0.3 问题
    //加法
    calcFn.add(0.1, 0.2) // 0.3
    
    //减法
    calcFn.sub(0.1, 0.2) // 0.1
    
    //乘法
    calcFn.mul(0.2, 0.3) // 0.06
    
    // 乘法
    calcFn.div(0.1, 0.2) // 0.5
    

    源码

      const calcFn = {
        add() {
          let arg = Array.from(arguments);
          return arg.reduce((total, num) => accAdd(total, num));
        },
        sub() {
          let arg = Array.from(arguments);
          return arg.reduce((total, num) => accAdd(total, -num));
        },
        mul() {
          let arg = Array.from(arguments);
          return arg.reduce((total, num) => accMul(total, num));
        },
        div() {
          let arg = Array.from(arguments);
          return arg.reduce((total, num) => accDiv(total, num));
        },
      };
    
      const accAdd = (arg1, arg2) => {
        let r1, r2, m;
        try {
          r1 = arg1.toString().split('.')[1].length;
        } catch (e) {
          r1 = 0;
        }
        try {
          r2 = arg2.toString().split('.')[1].length;
        } catch (e) {
          r2 = 0;
        }
        m = Math.pow(10, Math.max(r1, r2));
        return (arg1 * m + arg2 * m) / m;
      };
    
      const accMul = (arg1, arg2) => {
        let m = 0;
        let s1 = arg1.toString();
        let s2 = arg2.toString();
        try {
          m += s1.split('.')[1].length;
        } catch (e) {}
        try {
          m += s2.split('.')[1].length;
        } catch (e) {}
        return Number(s1.replace('.', '')) * Number(s2.replace('.', '')) / Math.pow(10, m);
      };
    
      const accDiv = (arg1, arg2) => {
        let t1 = 0;
        let t2 = 0;
        let r1; let r2;
        try {
          t1 = arg1.toString().split('.')[1].length;
        } catch (e) {}
        try {
          t2 = arg2.toString().split('.')[1].length;
        } catch (e) {}
        r1 = Number(arg1.toString().replace('.', ''));
        r2 = Number(arg2.toString().replace('.', ''));
        return (r1 / r2) * Math.pow(10, t2 - t1);
      };
    

    四、字符串

    isNil值是否是nullundefined

    isNil(value)
    

    参数

    • value 要检验的值

    示例

    isNil(null)
    isNil(undefined)
    // => true
    

    源码

    const isNil = val => val === undefined || val === null;
    

    padStart遮住字符串

    padStart(value, n, maskChar)
    

    参数

    • value 要遮住字符串
    • n = 4 填充的长度
    • maskChar 填充字符

    示例

    padStart('18659808664')
    // => 1865*******
    

    源码

    const padStart = (str, n = 4, maskChar = '*') => str.slice(0, n).padStart(str.length, maskChar);
    

    thousands数字每隔三位数加分号

    thousands(number)
    

    参数

    • number 数字或者浮点数

    示例

    thousands(12255552323)
    // => 12,255,552,323
    

    源码

    const thousands = num => num.toString().replace(num.toString().indexOf('.') > -1 ? /(\d)(?=(\d{3})+\.)/g : /(\d)(?=(\d{3})+$)/g, '$1,');
    

    五、数字

    randomNumber指定范围的随机整数

    randomNumber(min, max)
    

    参数

    • min 指定范围最小值
    • max 指定范围最大值

    示例

    randomNumber(0, 10)
    // => 7
    // => 2
    

    源码

    const randomNumber = (min = 0, max = 10) => Math.floor(Math.random() * (max - min + 1)) + min;
    

    average求平均值

    average(value1, value2, value3)
    

    参数

    • value 数字

    示例

    average(...[1, 2, 3])
    average(1, 2, 3)
    // => 2
    

    源码

    const average = (...nums) => nums.reduce((acc, val) => acc + val, 0) / nums.length;
    

    averageBy检查数组对象各项相等

    averageBy(array, fn)
    

    参数

    • array 要迭代的数组
    • fn 迭代函数

    示例

    averageBy([{ n: 4 }, { n: 2 }, { n: 8 }, { n: 6 }], o => o.n)
    averageBy([{ n: 4 }, { n: 2 }, { n: 8 }, { n: 6 }], 'n')
    // => 5
    

    源码

    const averageBy = (arr, fn) => arr.map(typeof fn === 'function' ? fn : val => val[fn]).reduce((acc, val) => acc + val, 0) / arr.length;
    

    aboutEqual两个值是否约等于

    传入两个数字是否大致相等,误差在可接受范围内

    aboutEqual(n1, n2, epsilon)
    

    参数

    • n1 n2 要比较的数字
    • epsilon 误差可接受范围内

    示例

    aboutEqual(25, 2, 0.06)
    // => true
    

    源码

    const aboutEqual = (n1, n2, epsilon = 0.001) => Math.abs(n1 - n2) < epsilon;
    

    getLineSize计算两点之间的距离

    勾股定理计算两点之间的距离

    getLineSize = (x1, y1, x2, y2)
    

    参数

    • x1 y1 x2 y2坐标点

    示例

    getLineSize(0, 0, 3, 4)
    // => 5
    

    源码

    const getLineSize = (x1, y1, x2, y2) => Math.hypot(x2 - x1, y2 - y1);
    

    sum数组中值总和

    sum(value1, value2, value3)
    

    参数

    • value1 value2 value3要迭代的数字

    示例

    sum(1, 2, 3, 4)
    sum(...[1, 2, 3, 4])
    // => 10
    

    源码

    const sum = (...arr) => [...arr].reduce((acc, val) => acc + val, 0);
    

    六、浏览器

    copyTextH5复制文本

    copyText(content, callback)
    

    参数

    • content要复制文字
    • callback 回调用户提示

    示例

    copyText(content, text => {
        this.$toast(text)
    })
    

    源码

    function copyText(content, callback) {
        if (!document.queryCommandSupported('copy')) {
            // 为了兼容有些浏览器 queryCommandSupported 的判断
            console.log('浏览器不支持');
            return;
        };
        let textarea = document.createElement('textarea');
        textarea.value = content;
        textarea.readOnly = 'readOnly';
        document.body.appendChild(textarea);
        textarea.select(); // 选择对象
        textarea.setSelectionRange(0, content.length); // 核心
        let result = document.execCommand('copy'); // 执行浏览器复制命令
        callback && callback(result ? '复制成功~~' : '复制失败~~');
        textarea.remove();
    }
    

    getCurrentURL获取当前的 URL 地址

    该函数返回当前页面的 URL 地址。

    示例

    getCurrentURL()
    // =>
    

    源码

    const getCurrentURL = () => window.location.href;
    

    scrollToTop返回顶部

    平滑地滚动到当前页面的顶部。

    示例

    scrollToTop()
    // => 当前页面的顶部
    

    源码

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

    smoothScroll平滑滚动页面

    平滑滚动到浏览器窗口的可见区域

    示例

    smoothScroll('#fooBar');
    // => 平滑滚动到ID为fooBar的元素
    smoothScroll ('.fooBar' );
    // => 使用fooBar类平滑滚动到第一个元素
    

    源码

    const smoothScroll = element => document.querySelector(element).scrollIntoView({ behavior: 'smooth' });
    

    5.isCurrentPage是否是当前页面

    浏览器的选项卡是否是用户在浏览

    示例

    isCurrentPage()
    // => true
    

    源码

    isCurrentPage = () => !document.hidden;
    

    7.环境

    1.isBrowser是否是浏览器

    返回当前运行时环境是否为浏览器

    示例

    isBrowser()
    // => true (browser)
    // => false (Node)
    

    源码

    const isBrowser = () => ![typeof window, typeof document].includes('undefined');
    

    2.isWechatBrowser判断微信浏览器还是普通h5

    示例

    isWechatBrowser()
    // => true
    

    源码

    const isWechatBrowser = (() => {
        let ua = navigator.userAgent.toLowerCase();
        return /micromessenger/.test(ua);
    })()
    

    3.isMobile判断是否是移动端

    示例

    isMobile()
    // => true
    

    源码

    const isMobile = () => /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
    

    起源地下载网 » Javascript 高效开发工具函数

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元