create common utils.js


/*
* author:w候人兮猗(ahwgs)
* url:https://www.ahwgs.cn
* github:https://github.com/ahwgs/common-utils/blob/master/utils
* */

/**
 * 是否是数组
 * @param value
 * @returns {boolean}
 */
export const isArray = (value) => {
    return toString.call(value) === '[object Array]';
}

/**
 * 是否是ArrayBuffer
 * @param val
 * @returns {boolean}
 */
export const isArrayBuffer = (val) => {
    return toString.call(val) === '[object ArrayBuffer]';
}

/**
 * 是否是FormData
 * @param val
 * @returns {boolean}
 */
export const isFormData = (val) => {
    return (typeof FormData !== 'undefined') && (val instanceof FormData);
}

/**
 * 确定值是否为ArrayBuffer上的视图
 *
 * @param {Object} val The value to test
 * @returns {boolean} True if value is a view on an ArrayBuffer, otherwise false
 */
export const isArrayBufferView = (val) => {
    let result;
    if ((typeof ArrayBuffer !== 'undefined') && (ArrayBuffer.isView)) {
        result = ArrayBuffer.isView(val);
    } else {
        result = (val) && (val.buffer) && (val.buffer instanceof ArrayBuffer);
    }
    return result;
}

/**
 * 是否是字符串
 * @param val
 * @returns {boolean}
 */
export const isString = (val) => {
    return typeof val === 'string';
}

/**
 * 是否是数字
 * @param val
 * @returns {boolean}
 */
export const isNumber = (val) => {
    return typeof val === 'number';
}

/**
 *  是否是undefined
 * @param val
 * @returns {boolean}
 */
export const isUndefined = (val) => {
    return typeof val === 'undefined';
}

/**
 * 是否是对象
 * @param val
 * @returns {boolean}
 */
export const isObject = (val) => {
    return val !== null && typeof val === 'object';
}

/**
 * 是否是时间对象
 * @param val
 * @returns {boolean}
 */
export const isDate = (val) => {
    return toString.call(val) === '[object Date]';
}

/**
 * 是否是文件对象
 * @param val
 * @returns {boolean}
 */
export const isFile = (val) => {
    return toString.call(val) === '[object File]';
}

/**
 * 是否是Blob对象
 * @param val
 * @returns {boolean}
 */
export const isBlob = (val) => {
    return toString.call(val) === '[object Blob]';
}

/**
 * 是否是方法
 * @param val
 * @returns {boolean}
 */
export const isFunction = (val) => {
    return toString.call(val) === '[object Function]';
}

/**
 * 是否是流
 * @param val
 * @returns {boolean}
 */
export const isStream = (val) => {
    return isObject(val) && isFunction(val.pipe);
}

/**
 * 是否是搜索参数
 * @param val
 * @returns {boolean}
 */
export const isURLSearchParams = (val) => {
    return typeof URLSearchParams !== 'undefined' && val instanceof URLSearchParams;
}

/**
 * trim 方法
 * @param str
 * @returns {string}
 */
export const trim = (str) => {
    return str.replace(/^\s*/, '').replace(/\s*$/, '');
}

/**
 * Determine if we're running in a standard browser environment
 *
 * This allows axios to run in a web worker, and react-native.
 * Both environments support XMLHttpRequest, but not fully standard globals.
 *
 * web workers:
 *  typeof window -> undefined
 *  typeof document -> undefined
 *
 * react-native:
 *  navigator.product -> 'ReactNative'
 */
export const isStandardBrowserEnv = () => {
    if (typeof navigator !== 'undefined' && navigator.product === 'ReactNative') {
        return false;
    }
    return (
        typeof window !== 'undefined' &&
        typeof document !== 'undefined'
    );
}

/**
 * Iterate over an Array or an Object invoking a function for each item.
 *
 * If `obj` is an Array callback will be called passing
 * the value, index, and complete array for each item.
 *
 * If 'obj' is an Object callback will be called passing
 * the value, key, and complete object for each property.
 *
 * @param {Object|Array} obj The object to iterate
 * @param {Function} fn The callback to invoke for each item
 */
export const forEach = (obj, fn) => {
    // Don't bother if no value provided
    if (obj === null || typeof obj === 'undefined') {
        return;
    }

    // Force an array if not already something iterable
    if (typeof obj !== 'object') {
        /*eslint no-param-reassign:0*/
        obj = [obj];
    }

    if (isArray(obj)) {
        // Iterate over array values
        for (var i = 0, l = obj.length; i < l; i++) {
            fn.call(null, obj[i], i, obj);
        }
    } else {
        // Iterate over object keys
        for (var key in obj) {
            if (Object.prototype.hasOwnProperty.call(obj, key)) {
                fn.call(null, obj[key], key, obj);
            }
        }
    }
}

/**
 * Accepts varargs expecting each argument to be an object, then
 * immutably merges the properties of each object and returns result.
 *
 * When multiple objects contain the same key the later object in
 * the arguments list will take precedence.
 *
 * Example:
 *
 * ```js
 * var result = merge({foo: 123}, {foo: 456});
 * console.log(result.foo); // outputs 456
 * ```
 *
 * @param {Object} obj1 Object to merge
 * @returns {Object} Result of all merge properties
 */
export const merge = (/* obj1, obj2, obj3, ... */) => {
    var result = {};

    function assignValue(val, key) {
        if (typeof result[key] === 'object' && typeof val === 'object') {
            result[key] = merge(result[key], val);
        } else {
            result[key] = val;
        }
    }

    for (var i = 0, l = arguments.length; i < l; i++) {
        forEach(arguments[i], assignValue);
    }
    return result;
}

/**
 * Extends object a by mutably adding to it the properties of object b.
 *
 * @param {Object} a The object to be extended
 * @param {Object} b The object to copy properties from
 * @param {Object} thisArg The object to bind function to
 * @return {Object} The resulting value of object a
 */
export const extend = (a, b, thisArg) => {
    forEach(b, function assignValue(val, key) {
        if (thisArg && typeof val === 'function') {
            a[key] = bind(val, thisArg);
        } else {
            a[key] = val;
        }
    });
    return a;
}

/**
 * 是否是绝对路径
 * @param url
 * @returns {boolean}
 */
export const isAbsoluteURL = (url) => {
    // A URL is considered absolute if it begins with "<scheme>://" or "//" (protocol-relative URL).
    // RFC 3986 defines scheme name as a sequence of characters beginning with a letter and followed
    // by any combination of letters, digits, plus, period, or hyphen.
    return /^([a-z][a-z\d\+\-\.]*:)?\/\//i.test(url);
};

/**
 * 防抖函数
 * @param method 事件触发的操作
 * @param delay 多少毫秒内连续触发事件,不会执行
 * @returns {Function}
 */
export const debounce = (method, delay) => {
    let timer = null;
    return function () {
        let self = this,
            args = arguments;
        timer && clearTimeout(timer);
        timer = setTimeout(function () {
            method.apply(self, args);
        }, delay);
    }
}

/**
 * 节流函数
 * @param method 事件触发的操作
 * @param mustRunDelay 间隔多少毫秒需要触发一次事件
 */
export const throttle = (method, mustRunDelay) => {
    let timer,
        args = arguments,
        start;
    return function loop() {
        let self = this;
        let now = Date.now();
        if (!start) {
            start = now;
        }
        if (timer) {
            clearTimeout(timer);
        }
        if (now - start >= mustRunDelay) {
            method.apply(self, args);
            start = now;
        } else {
            timer = setTimeout(function () {
                loop.apply(self, args);
            }, 50);
        }
    }
}

/**
 * 添加收藏功能
 * @param url
 * @param title
 */
export const addFavorite = (url, title) => {
    var ua = navigator.userAgent.toLowerCase();
    if (ua.indexOf("360se") > -1) {
        alert("由于360浏览器功能限制,请按手动收藏!");
    } else if (document.all) {
        // window.external.AddToFavoritesBar(url, title); //IE8
        window.external.addFavorite(url, title);
    } else if (document.all) {
        try {
            window.external.addFavorite(url, title);
        } catch (e) {
            alert('您的浏览器不支持自动添加收藏, 请使用浏览器菜单手动设置!');
        }
    } else if (window.sidebar) {
        window.sidebar.addPanel(title, url, "");
    } else if (window.opera && window.print) {
        var e = document.createElement('a');
        e.setAttribute('href', location.href);
        e.setAttribute('title', document.title);
        e.setAttribute('rel', 'sidebar');
        e.click();
    } else {
        alert('您的浏览器不支持自动添加收藏, 请使用浏览器菜单手动设置!');
    }
}

/**
 * 判断host是否是ip地址
 * @param host
 * @returns {boolean}
 */
export const isIp = (host) => {
    const ipv4Regex = /^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$/;
    const ipv6Regex = /^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/;
    const isIP = ipv4Regex.test(host) || ipv6Regex.test(host);
    return isIP;
}

/**
 * 个位数字补零
 * @param val
 * @returns {string}
 */
export function fixedZero(val) {
    return val * 1 < 10 ? `0${val}` : val;
}


/**
 * 金钱转大写
 * @param n
 * @returns {string}
 */
export function digitUppercase(n) {
    const fraction = ['角', '分'];
    const digit = ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖'];
    const unit = [['元', '万', '亿'], ['', '拾', '佰', '仟', '万']];
    let num = Math.abs(n);
    let s = '';
    fraction.forEach((item, index) => {
        s += (digit[Math.floor(accMul(num, 10 * 10 ** index)) % 10] + item).replace(/零./, '');
    });
    s = s || '整';
    num = Math.floor(num);
    for (let i = 0; i < unit[0].length && num > 0; i += 1) {
        let p = '';
        for (let j = 0; j < unit[1].length && num > 0; j += 1) {
            p = digit[num % 10] + unit[1][j] + p;
            num = Math.floor(num / 10);
        }
        s = p.replace(/(零.)*零$/, '').replace(/^$/, '零') + unit[0][i] + s;
    }

    return s
        .replace(/(零.)*零元/, '元')
        .replace(/(零.)+/g, '零')
        .replace(/^整$/, '零元整');
}

/**
 * 生成UUId
 * @returns {string}
 */
export const createUUID = () => {
    let s = [];
    let hexDigits = '0123456789abcdefghijklmnopqrstuvwxyz';
    for (let i = 0; i < 36; i++) {
        s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
    }
    s[14] = '4'; // bits 12-15 of the time_hi_and_version field to 0010
    s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1); // bits 6-7 of the clock_seq_hi_and_reserved to 01
    s[8] = s[13] = s[18] = s[23] = '-';

    return s.join('');
}
/**
 * 寻找数组下标
 * @param array
 * @param predicate (model => model.namespace === namespace)
 * @returns {number}
 */
export const findIndex = (array, predicate) => {
    for (let i = 0, length = array.length; i < length; i++) {
        if (predicate(array[i], i)) return i;
    }

    return -1;
};

/**
 * 设备判断
 * @type {string}
 */
const ua = window.navigator.userAgent.toLowerCase();

const isAndroid = /(Android);?[\s/]+([\d.]+)?/.test(ua)
const isIpad = /(iPad).*OS\s([\d_]+)/.test(ua)
const isIpod = /(iPod)(.*OS\s([\d_]+))?/.test(ua)
const isIphone = !isIpad && /(iPhone\sOS)\s([\d_]+)/.test(ua)
const isWechat = /micromessenger/i.test(ua)
const isAlipay = /alipayclient/i.test(ua)


/**
 * 是否是移动端
 * @returns {boolean}
 * @constructor
 */
export const IsMobile = () => {
    const sUserAgent = navigator.userAgent.toLowerCase();
    if ((sUserAgent.match(/(iphone|ipad|ipod|android|windows ce|windows mobile)/i))) {
        return true;
    }
    return false;
}

/**
 * 是否是Ie10以下
 * @returns {boolean}
 * @constructor
 */
export const LTIE10 = () => {
    const userAgent = navigator.userAgent;
    //判断是否IE浏览器
    const isIE = userAgent.indexOf("compatible") > -1
        && userAgent.indexOf("MSIE") > -1
        && userAgent.indexOf("Opera") == -1;

    if (isIE) {
        const reIE = new RegExp("MSIE (\\d+\\.\\d+);");
        reIE.test(userAgent);         //检测一个字符串是否匹配某个模式
        const fIEVersion = parseFloat(RegExp["$1"]);
        if (fIEVersion < 10)
            return true;
    }
    return false;
}

/**
 * 判断两个域名是否相等
 * @param mobileDomain
 * @param host
 * @returns {number}
 * @constructor
 */
export const IsSubDomain = (mobileDomain, host) => {
    this.getdomain = function (f) {
        var e = f.indexOf("://");
        if (e > 0) {
            var h = f.substr(e + 3)
        } else {
            var h = f
        }
        var g = /^www\./;
        if (g.test(h)) {
            h = h.substr(4)
        }
        return h
    };
    if (mobileDomain == host) {
        return 1 //相同
    } else {
        var mobileDomain = this.getdomain(mobileDomain);
        var b = this.getdomain(host);
        if (mobileDomain == b) {
            return 1 //相同
        } else {
            mobileDomain = mobileDomain.replace(".", "\\.");
            var a = new RegExp("\\." + mobileDomain + "$");
            if (b.match(a)) {
                return 2 //当前host包含mobileDomain
            } else {
                return 0 //不同
            }
        }
    }
}


/**
 * 将移动端自动重定向到pc
 * @param mobileDomain
 * @constructor
 */
export const RedirectMobile=(mobileDomain)=> {
    try {
        if (!IsMobile()) {  //先判断是否是手机端
            if (LTIE10())
                window.location.href = "/upgradebrowser"; //这里进行非ie10以上版本处理
            return;
        }

        var IsJump = false;
        var host = window.location.host;
        var vey = IsSubDomain(mobileDomain, host); //判断两个域名是否相同
        if (vey == 1 || vey == 2) { //相同或者包含
            IsJump = false
        } else {
            //取缓存中的值
            if (sessionStorage.IsJumpMobile == undefined)
            {
                if (confirm("检测到您使用的是移动设备,是否跳转到移动版?")) {
                    var path = window.location.pathname + window.location.search;
                    if (mobileDomain.indexOf("http://") == -1)
                        mobileDomain = "http://" + mobileDomain;
                    mobileDomain = mobileDomain + path;
                    IsJump = true;
                }
                else
                {
                    sessionStorage.IsJumpMobile = false;
                }
            }
        }

        if (IsJump) {
            location.replace(mobileDomain);
        }
    } catch (err) { }
}





  /**
 * 将一级的数据结构处理成树状数据结构
 * @param {Object} obj {key, pKey, data}
 *  @param obj.key  字段名称 比如id
 *  @param obj.pKey 父字段名称 比如 pid
 *  @param obj.rootPValue 根节点的父字段的值
 *  @param obj.data 需要处理的数据
 *  @param obj.jsonData 是否深复制数据(默认是true)
 * @return {Array} arr
   */
   
   // 使用示例代码:
    list: [{id: 1, pid: 0, name: 11}, {id: 2, pid: 1, name: 2}]
    getTreeArr({ key: 'id', pKey: 'pid', data: list })
    result: [
        {id: 1, pid: 0, name: 11, children: [
            {id: 2, pid: 1, name: 2}
        ]}
    ]   

  getTreeArr: (obj) => {
    if (!Array.isArray(obj.data)) {
      console.log('getTreeArr=>请传入数组')
      return []
    }
    obj.jsonData = obj.jsonData === false ? obj.jsonData : true
    const arr = obj.jsonData ? JSON.parse(JSON.stringify(obj.data)) : obj.data
    const arr1 = []
    // 将数据处理成数状结构
    arr.forEach(item => {
      let index = 0
      item.children = []
      arr.forEach(item1 => {
        // 得到树结构关系
        if (item[obj.key] === item1[obj.pKey]) {
          item.children.push(item1)
        }
        // 判断根节点
        if (item1[obj.key] !== item[obj.pKey]) {
          index++
        }
      })
      // 没传入根节点,根据当前数据结构得到根节点
      if (!('rootPValue' in obj) && index === arr.length) {
        arr1.push(item)
      }
    })
    // 传入根节点,根据传入的根节点组成树结构
    if ('rootPValue' in obj) {
      arr.forEach(item => {
        if (item[obj.pKey] === obj.rootPValue) {
          arr1.push(item)
        }
      })
    }
    return arr1
  }

//数组去重
//根据对象的属性不同去重
  handleRepeatArr ({ data, key }) {
    if (!Array.isArray(data)) {
      console.log('请传入数组')
      return
    }
    const arr = []; const obj = {}
    data.forEach((item, index) => {
      const attr = key ? item[key] : item
      if (!obj[attr]) {
        obj[attr] = index + 1
        arr.push(item)
      }
    })
    return arr
  }

//递归去重  会将数据默认排序
  handleRepeatArr ({ data, key }) {
    if (!Array.isArray(data)) {
      console.log('请传入数组')
      return
    }
    /** 1.递归去重,缺点,会将数据默认排序 */
    // 先对数据做排序处理
    data = data.sort((item, item1) => {
      if (key) {
        return item[key] - item1[key]
      }
      return item - item1
    })
    // 递归去重
    function getData (index) {
      if (index >= 1) {
        // 判断当前数据和下一条数据是否相等
        let result = key ? data[index][key] === data[index - 1][key] : data[index] === data[index - 1]
        if (result) {
          data.splice(index, 1)
        }
        getData(index - 1)
      }
    }
    getData(data.length - 1)
    return data
  }
  
  //利用indexOf以及forEach
  //适合处理数组,不适合处理对象数组
  handleRepeatArr ({ data, key }) {
    if (!Array.isArray(data)) {
      console.log('请传入数组')
      return
    }
    let arr = []
    data.forEach((item, index) => {
      // 如果当前元素在之后没有出现过(后面出现的数据会保留)
      // let result = data.indexOf(item, index + 1)
      // 如果当前元素在之前没有出现过(前面出现的数据会保留)
      let result = index === 0 ? -1 : data.lastIndexOf(item, index - 1)
      if (result === -1) {
        arr.push(item)
      }
    })
    return arr
  }
//双层循环去重
//占用内存高
  handleRepeatArr ({ data, key }) {
    if (!Array.isArray(data)) {
      console.log('请传入数组')
      return
    }
    for (let i = 0, len = data.length; i < len; i++) {
      for (let j = i + 1; j < len; j++) {
        let result = key ? data[i][key] === data[j][key] : data[i] === data[j]
        if (result) {
          data.splice(j, 1)
          len--
          j--
        }
      }
    }
    return data
  }


//复制内容
  /**
   * 复制
   * @param {String} value 要复制的值
   */
  copyData (value) {
    const inputDom = document.createElement('input')
    inputDom.value = value
    document.body.appendChild(inputDom)
    inputDom.select() // 选择对象
    document.execCommand('Copy') // 执行浏览器复制命令
    document.body.removeChild(inputDom) // 删除DOM
  }
 //a模拟window.open打开窗口
 /*
    因为有些浏览器会默认拦截window.open,当需要函数中打开窗口,可以使用a标签模拟window.open
 */
   /**
   * a模拟window.open,不会被浏览器拦截
   * @param {String} url        a标签打开的地址
   * @param {String} id         a标签的ID
   * @param {String} targetType a标签点击打开的方式(当前页面打开还是新窗口打开)
   */
  openWindow: (url, targetType = '_blank', id = 'open', download = false) => {
    // 如果存在则删除
    if (document.getElementById(id)) {
      document.body.removeChild(document.getElementById(id))
    }
    const a = document.createElement('a')
    a.setAttribute('href', url)
    if (download) {
      a.setAttribute('download', url)
    }
    a.setAttribute('target', targetType)
    a.setAttribute('id', id)
    document.body.appendChild(a)
    a.click()
  }

//文件大小显示转换
/*
// 使用示例代码:
bytesToSize(12) // 12.0 B
bytesToSize(683468) // 667 KB
bytesToSize(4544) // 4.44 KB
bytesToSize(98223445) // 93.7 MB
bytesToSize(9822344566) // 9.15 GB

*/
  bytesToSize (bytes) {
    if (bytes === 0) return '0 B'
    var k = 1024 // or 1024
    var sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
    var i = Math.floor(Math.log(bytes) / Math.log(k))
    return (bytes / Math.pow(k, i)).toPrecision(3) + ' ' + sizes[i]
  }

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,402评论 6 499
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,377评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,483评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,165评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,176评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,146评论 1 297
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,032评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,896评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,311评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,536评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,696评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,413评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,008评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,659评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,815评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,698评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,592评论 2 353

推荐阅读更多精彩内容