vue点击上传图片,vue上传oss,vue-cropper图片裁剪功能

很多刚入门的小伙伴上传图片都会使用现成的框架,然后根据框架提供的api进行上传,感觉这样是最简单的,其实上传图片是一个很简单的功能,

点击上传图片

1.将input的设为透明然后定位到按钮上面,通过点击实现上传

<template>
  <div class="page" ref="page">
    <div class="uploadImg">
      <div class="btn">点击上传</div>
      <input type="file" ref="inputFile"  accept="image/*"  @change="UploadImg" class="input" />
      <img :src="ImgUrl" alt="">
    </div>
  </div>
</template>
<script>
methods: {
    UploadImg(e, code) {
      // 选择原图之后
      if (e.target.files[0]) {
        // 及时回显到页面
        let file = e.target.files[0];
        this.ImgUrl = window.URL.createObjectURL(file);
        // 上传到服务器
        let fileFormData = new FormData();
        fileFormData.append("file", file);
        // 将fileFormData 通过axios 传到后台即可 
        // 注意要将header 进行修改  Content-Type = "multipart/form-data" 
        axios({
            url: url,
            header: {
                'Content-Type': 'multipart/form-data',
            },
            method: 'post',
            data: fileFormData
        })
        .then(response => {
          // 返回结果
          // 此处不会返回图片地址,需要我们自己使用请求地址+文件名进行拼接
          let imgUrl = url + filename
           
        })

        .catch(({ data }) => {
           
        });
        //每一次提交上传图片后都清空当前input的file,防止同一张图片上传失败不能连续上传
        this.$refs.inputFile.value=""
      }
    },
   
  },
</script>
<style>
.uploadImg{
  position:relative;
  width:100px;height:30px;
}
.btn{
  width:100%;height:100%;
  position:absolute;top:0;left:0;
  text-align: center;
  line-height:30px;
  border:1px solid #ccc;
}
.input{
  width:100%;height:100%;position:absolute;top:0;left:0;opacity:0;
  z-index:1;
}
</style>

2.点击按钮,通过处发input的点击事件后处发change,达到唤起上传图片的效果

<template>
  <div>
    <div class="btn" @click="triggerChange">点击上传</div>
    <input type="file" ref="inputFile" accept="image/*"  @change="UploadImg" @click="UploadImg" ref="uploadImage" class="input" />
  </div>
</template>
<script>
methods: {
    triggerChange() {
      this.$refs.uploadImage.click();
    },
    UploadImg(e, code) {
      // 选择原图之后
      if (e.target.files[0]) {
        // 及时回显到页面
        let file = e.target.files[0];
        this.ImgUrl = window.URL.createObjectURL(file);
        // 上传到服务器
        let fileFormData = new FormData();
        fileFormData.append("file", file);
        // 将fileFormData 通过axios 传到后台即可 
        // 注意要将header 进行修改  Content-Type = "multipart/form-data" 
        //axios 请求在第一种上传方法中,如有需要在上面拿一下即可,这里就不在写了
         axios({})
      //每一次提交上传图片后都清空当前input的file,防止同一张图片上传失败不能连续上传
        this.$refs.inputFile.value=""
      }
    }
  },
</script>
<style lang="less" scoped>
.btn{
  width:100%;height:100%;
  text-align: center;
  line-height:30px;
  border:1px solid #ccc;
}
</style>

vue 上传传oss

先通过npm安装oss依赖

npm install ali-oss --save
cnpm install ali-oss --save

oss官方文档
1.前端拿到 accessKeyId,accessKeySecret直接上传oss
这种用法会直接暴露id及secret,相当于把自己家门的钥匙告诉别人,不推荐使用

    // file 从上线方法中获取到的file 传进来就可以
  updateOss(file[0])
  methods: {
    updateOss(file){
      // 请求接口得到对应的参数,这个参数不会变,项目中请求一次即可
        let aliData = {
          region: "oss-cn-beijing", 
          accessKeyId: 'your accessKeyId',
          accessKeySecret: 'your accessKeySecret',
          bucket: 'xx'
        }

        const client = new OSS(aliData);
        const arr = file.name.split(".");
        const suffix = arr[arr.length - 1];
        const storeAs = `appupload/${file.lastModified}${Math.round( Math.random() * 10000 )}.${suffix}`;
        client.multipartUpload(storeAs, file).then(({ res }) => {
          if (res.status === 200) {
            console.log('上传成功')
          } else {
            console.log('请重新上传');
          }
        });
    },
  }


2.通过后台转换,拿到对应的参数,进行上传

 // file 将点击上传获取到的file传进来即可
  this.updateOss(file[0])
  methods: {
    updateOss(file){
      // 请求接口得到对应的参数
        let aliData = {
          accessKeyId:'',
          policy:'',
          signature:'',
          success_action_status:'',
        }

        const Name = file.name
        const suffix = Name.substr(Name.indexOf('.'))              // 文件后缀
        let filename = Date.parse(new Date()) + suffix           // 组成新文件名,防止文件名冲突
        let formDataKey = `文件夹/${filename}`                    // 文件夹是要提交的图片在oss上面的文件目录,根据项目自行配置
        let formData = new FormData();
        formData.append('OSSAccessKeyId', aliData.accessKeyId);
        formData.append('policy', aliData.policy);
        formData.append('signature', aliData.signature);
        formData.append('success_action_status', aliData.success_action_status);
        formData.append('key', `${formDataKey}`);             //组成的文件名
        formData.append('file', file);
        //通过axios 请求将图片传到oss
        let url="oss地址"
        axios({
            url: url,
            header: {
                'Content-Type': 'multipart/form-data',
            },
            method: 'post',
            data: fileFormData
        })
        .then(response => {
          // 返回结果
          // 此处不会返回图片地址,需要我们自己使用请求地址+文件名进行拼接
          let imgUrl = url + filename
           
        })
        .catch(({ data }) => {
           
        });
    },
}

图片裁剪

图片裁剪的插件,先通过npm安装依赖
vue-cropper 高版本解决在ios上图片拍照旋转问题

npm i vue-cropper --save

父组件

<template>
  <div>
    <div class="uploadImg">
      <div class="btn">点击上传</div>
      <input type="file"   accept="image/*"  @change="UploadImg" class="input" />
    </div>
    <img :src="ImgUrl" alt="" style="width:100%;height:auto;">
    <cropperModule ref="cropperRef" @UploadCompleted="UploadCompleted" ></cropperModule>
  </div>
</template>
<script>
import cropperModule from "@/components/cropper";
export default {
  name: "Home",
  components: {
    cropperModule
  },
  data() {
    return {
      ImgUrl:'',
    }
  },
  methods: {
    UploadImg(e, code) {
      if (e.target.files[0]) {
        this.$refs.cropperRef.upphoto(e);
      }
    },
    // 拿到上传完成后的图片进行渲染
    UploadCompleted(imgUrl){
        this.ImgUrl = imgUrl
    },
  },
  
};
</script>
<style lang="less" scoped>
.uploadImg{
  position:relative;
  width:100px;height:30px;
}
.btn{
  width:100%;height:100%;
  position:absolute;top:0;left:0;
  text-align: center;
  line-height:30px;
  border:1px solid #ccc;
}
.input{
  width:100%;height:100%;position:absolute;top:0;left:0;opacity:0;
  z-index:1;
}
</style>

子组件,在项目components文件中新建一个cropper文件,在父组件中引入

<template>
  <div class="upbtn">
    <div class="bg" v-if="img != ''">
      <div class="btndiv" v-if="config.ceilbutton">
        <div
          class="btn"
          @click="canceltailor"
          :style="
            `backgroundColor:${config.cancelButtonBackgroundColor};color:${config.cancelButtonTextColor}`
          "
        >
          {{ config.cancelButtonText }}
        </div>
        <div class="img" @click="rotating"></div>
        <div
          class="btn"
          @click="tailoring"
          :style="
            `backgroundColor:${config.confirmButtonBackgroundColor};color:${config.confirmButtonTextColor}`
          "
        >
          {{ config.confirmButtonText }}
        </div>
      </div>
      <div class="wrapper">
        <vueCropper
          id="cropper"
          ref="cropper"
          :img="img"
          :outputSize="config.outputSize"
          :outputType="config.outputType"
          :info="config.info"
          :canScale="config.canScale"
          :autoCrop="config.autoCrop"
          :autoCropWidth="config.autoCropWidth"
          :autoCropHeight="config.autoCropHeight"
          :fixed="config.fixed"
          :fixedNumber="config.fixedNumber"
          :full="config.full"
          :fixedBox="config.fixedBox"
          :canMove="config.canMove"
          :canMoveBox="config.canMoveBox"
          :original="config.original"
          :centerBox="config.centerBox"
          :high="config.high"
          :infoTrue="config.infoTrue"
          :maxImgSize="config.maxImgSize"
          :enlarge="config.enlarge"
          :mode="config.mode"
          @cropMoving="moving($event)"
          @imgMoving="moving($event)"
        ></vueCropper>
      </div>
      <div class="btndiv" v-if="!config.ceilbutton">
        <div
          class="btn"
          @click="canceltailor"
          :style="
            `backgroundColor:${config.cancelButtonBackgroundColor};color:${config.cancelButtonTextColor}`
          "
        >
          {{ config.cancelButtonText }}
        </div>
        <div class="img" @click="rotating"></div>
        <div
          class="btn"
          @click="tailoring"
          :style="
            `backgroundColor:${config.confirmButtonBackgroundColor};color:${config.confirmButtonTextColor}`
          "
        >
          {{ config.confirmButtonText }}
        </div>
      </div>
    </div>
  </div>
</template>
<script>
import { VueCropper } from "vue-cropper";
export default {
  name: "H5Cropper",
  components: { VueCropper },
  props: {
    hideInput: {
      type: Boolean,
      default: false
    },
    fromType: {
      type: Number,
      default: 2
    },
    option: {
      type: Object,
      default() {
        return {};
      }
    }
  },
  data() {
    return {
      img: "",
      config: {},
      aliData:""
    };
  },
  watch: {
    option: {
      handler: function() {
        //do something
        delete this.option.autoCrop; // TODO: 不开放权限
        if (
          typeof this.option.outputType === "string" &&
          ["jpeg", "png", "webp"].indexOf(this.option.outputType) === -1
        ) {
          console.warn("Option.outputType is not [jpeg, png, webp]");
          delete this.option.outputType; // TODO: 改回默认属性不影响调用
        }
        this.config = Object.assign(this.config, this.option);
      },
      deep: true
    }
  },
  created() {
    // Event getFile 需要明确 MIME 类型
    delete this.option.autoCrop; // TODO: 不开放权限
    if (
      typeof this.option.outputType === "string" &&
      ["jpeg", "png", "webp"].indexOf(this.option.outputType) === -1
    ) {
      console.warn("Option.outputType is not [jpeg, png, webp]");
      delete this.option.outputType; // TODO: 改回默认属性不影响调用
    }

    this.config = Object.assign(
      {
        ceilbutton: false, //顶部按钮,默认底部
        outputSize: 1, //裁剪生成图片的质量
        outputType: "png", //裁剪生成图片的格式,默认png
        info: false, //裁剪框的大小信息
        canScale: false, //图片是否允许滚轮缩放
        autoCrop: false, //是否默认生成截图框
        autoCropWidth: 347, //默认生成截图框宽度
        autoCropHeight: 347, //默认生成截图框高度
        fixed: true, //是否开启截图框宽高固定比例
        fixedNumber: [1, 1], //截图框的宽高比例
        full: false, //是否输出原图比例的截图
        fixedBox: true, //固定截图框大小 不允许改变
        canMove: true, //上传图片是否可以移动
        canMoveBox: false, //截图框能否拖动
        original: false, //上传图片按照原始比例渲染
        centerBox: true, //截图框是否被限制在图片里面
        high: true, //是否按照设备的dpr 输出等比例图片
        infoTrue: true, //true 为展示真实输出图片宽高 false 展示看到的截图框宽高
        maxImgSize: 2000, //限制图片最大宽度和高度
        enlarge: 1, //图片根据截图框输出比例倍数
        mode: "contain", //图片默认渲染方式
        cancelButtonText: "取消", //取消按钮文本
        confirmButtonText: "使用", //确定按钮文本
        cancelButtonBackgroundColor: "#606266", //取消按钮背景色
        confirmButtonBackgroundColor: "#ed594c", //确定按钮背景色
        cancelButtonTextColor: "#ffffff", //取消按钮字体色
        confirmButtonTextColor: "#ffffff" //确定按钮字体色
      },
      this.option
    );
  },
  methods: {
    //取消裁剪
    canceltailor() {
      this.img = "";
      this.$emit("canceltailor");
    },
    //选择照片
    async upphoto(e) {
      let photourl = e.target.files[0];
      if (photourl != undefined) {
        this.$emit("imgorigoinf", photourl);
        this.img = await this.onloadimg(photourl);
        this.config.autoCrop = true;
        setTimeout(() => {
          this.addsolide();
        }, 10);
      }
    },
    //异步onload图片
    onloadimg(photourl) {
      return new Promise(function(resolve, reject) {
        let reader = new FileReader();
        reader.readAsDataURL(photourl);
        reader.onload = e => {
          resolve(e.target["result"]);
        };
      });
    },
   
    
    //确定裁剪
    tailoring() {
      // 获取截图的base64数据
      this.$refs.cropper.getCropData(data => {
        this.img = "";
        this.config.autoCrop = false;
      });
      // 获取截图的blob数据
      this.$refs.cropper.getCropBlob(data => {
        // Blob 转 File
        const suffix = {
          jpeg: "jpg",
          png: "png",
          webp: "webp"
        }[this.config.outputType];
        const time = new Date().getTime();
        const file = new File([data], `${time}.${suffix}`, {
          type: `image/${this.config.outputType}`
        });
        // 此处获取到裁剪完成后的file
        this.getFile(file);
        this.img = "";
        this.config.autoCrop = false;
      });
    },
    // 获取到裁剪后的参数
    getFile(file) {
      let that = this;
      // 此处不再赘述上传的方式,直接将file显示到页面了,上传方式在上面已经介绍过了,根据自己的项目需求引入即可
      var windowURL=window.url || window.webkitURL;
      let imgUrl = windowURL.createObjectURL(file);
      this.$emit('UploadCompleted',imgUrl)
      // axios({})
    },
    
    //旋转照片
    rotating() {
      this.$refs.cropper.rotateRight();
      document.getElementsByClassName("cropper-modal")[0].style =
        "background-color: rgba(0,0,0,0.5);transition: 0.88s";
    },
    //裁剪框移动
    moving(e) {
      if (e.moving) {
        document.getElementsByClassName("cropper-modal")[0].style =
          "background-color: rgba(0,0,0,0.5);transition: 0.88s";
      } else {
        document.getElementsByClassName("cropper-modal")[0].style =
          "background-color: rgba(0,0,0,0.8);transition: 0.88s";
      }
    },
    //添加网格线
    addsolide() {
      if (document.getElementById("vertical") == null) {
        let box = document.getElementsByClassName("cropper-crop-box")[0];
        //左网格线
        let verticalLeft = document.createElement("div");
        verticalLeft.id = "vertical";
        verticalLeft.style.width = "1px";
        verticalLeft.style.height = "100%";
        verticalLeft.style.top = "0px";
        verticalLeft.style.left = "33%";
        verticalLeft.style.position = "absolute";
        verticalLeft.style.backgroundColor = "#fff";
        verticalLeft.style.zIndex = "522";
        verticalLeft.style.opacity = "0.5";
        //右网格线
        let verticalRight = document.createElement("div");
        verticalRight.style.width = "1px";
        verticalRight.style.height = "100%";
        verticalRight.style.top = "0px";
        verticalRight.style.right = "33%";
        verticalRight.style.position = "absolute";
        verticalRight.style.backgroundColor = "#fff";
        verticalRight.style.zIndex = "522";
        verticalRight.style.opacity = "0.5";
        //上网格线
        let verticalTop = document.createElement("div");
        verticalTop.style.width = "100%";
        verticalTop.style.height = "1px";
        verticalTop.style.top = "33%";
        verticalTop.style.left = "0px";
        verticalTop.style.position = "absolute";
        verticalTop.style.backgroundColor = "#fff";
        verticalTop.style.zIndex = "522";
        verticalTop.style.opacity = "0.5";
        //下网格线
        let verticalBottom = document.createElement("div");
        verticalBottom.style.width = "100%";
        verticalBottom.style.height = "1px";
        verticalBottom.style.bottom = "33%";
        verticalBottom.style.left = "0px";
        verticalBottom.style.position = "absolute";
        verticalBottom.style.backgroundColor = "#fff";
        verticalBottom.style.zIndex = "522";
        verticalBottom.style.opacity = "0.5";
        //左上边线
        let LeftTopSide = document.createElement("div");
        LeftTopSide.style.width = "30px";
        LeftTopSide.style.height = "4px";
        LeftTopSide.style.top = "-4px";
        LeftTopSide.style.left = "-4px";
        LeftTopSide.style.position = "absolute";
        LeftTopSide.style.backgroundColor = "#fff";
        LeftTopSide.style.zIndex = "522";
        LeftTopSide.style.opacity = "1";
        //上左边线
        let TopListSide = document.createElement("div");
        TopListSide.style.width = "4px";
        TopListSide.style.height = "30px";
        TopListSide.style.top = "-4px";
        TopListSide.style.left = "-4px";
        TopListSide.style.position = "absolute";
        TopListSide.style.backgroundColor = "#fff";
        TopListSide.style.zIndex = "522";
        TopListSide.style.opacity = "1";
        //右上边线
        let RightTopSide = document.createElement("div");
        RightTopSide.style.width = "30px";
        RightTopSide.style.height = "4px";
        RightTopSide.style.top = "-4px";
        RightTopSide.style.right = "-4px";
        RightTopSide.style.position = "absolute";
        RightTopSide.style.backgroundColor = "#fff";
        RightTopSide.style.zIndex = "522";
        RightTopSide.style.opacity = "1";
        //上右边线
        let TopRightSide = document.createElement("div");
        TopRightSide.style.width = "4px";
        TopRightSide.style.height = "30px";
        TopRightSide.style.top = "-4px";
        TopRightSide.style.right = "-4px";
        TopRightSide.style.position = "absolute";
        TopRightSide.style.backgroundColor = "#fff";
        TopRightSide.style.zIndex = "522";
        TopRightSide.style.opacity = "1";
        //左下边线
        let LeftBottomSide = document.createElement("div");
        LeftBottomSide.style.width = "30px";
        LeftBottomSide.style.height = "4px";
        LeftBottomSide.style.bottom = "-4px";
        LeftBottomSide.style.left = "-4px";
        LeftBottomSide.style.position = "absolute";
        LeftBottomSide.style.backgroundColor = "#fff";
        LeftBottomSide.style.zIndex = "522";
        LeftBottomSide.style.opacity = "1";
        //下左边线
        let BottomListSide = document.createElement("div");
        BottomListSide.style.width = "4px";
        BottomListSide.style.height = "30px";
        BottomListSide.style.bottom = "-4px";
        BottomListSide.style.left = "-4px";
        BottomListSide.style.position = "absolute";
        BottomListSide.style.backgroundColor = "#fff";
        BottomListSide.style.zIndex = "522";
        BottomListSide.style.opacity = "1";
        //右下边线
        let RightBottomSide = document.createElement("div");
        RightBottomSide.style.width = "30px";
        RightBottomSide.style.height = "4px";
        RightBottomSide.style.bottom = "-4px";
        RightBottomSide.style.right = "-4px";
        RightBottomSide.style.position = "absolute";
        RightBottomSide.style.backgroundColor = "#fff";
        RightBottomSide.style.zIndex = "522";
        RightBottomSide.style.opacity = "1";
        //下右边线
        let BottomRightSide = document.createElement("div");
        BottomRightSide.style.width = "4px";
        BottomRightSide.style.height = "30px";
        BottomRightSide.style.bottom = "-4px";
        BottomRightSide.style.right = "-4px";
        BottomRightSide.style.position = "absolute";
        BottomRightSide.style.backgroundColor = "#fff";
        BottomRightSide.style.zIndex = "522";
        BottomRightSide.style.opacity = "1";
        //一起生成
        box.appendChild(verticalLeft);
        box.appendChild(verticalRight);
        box.appendChild(verticalTop);
        box.appendChild(verticalBottom);
        box.appendChild(LeftTopSide);
        box.appendChild(TopListSide);
        box.appendChild(RightTopSide);
        box.appendChild(TopRightSide);
        box.appendChild(LeftBottomSide);
        box.appendChild(BottomListSide);
        box.appendChild(RightBottomSide);
        box.appendChild(BottomRightSide);
      }
    },
  }
};
</script>
<style lang="less" scoped>
.bg {
  position: fixed;
  top: 0;
  height: 100vh;
  width: 100%;
  background-color: #000;
  left: 0;
  z-index: 521;
}
.btn {
  height: 8vw;
  padding: 0;
  line-height: 8vw;
  font-size: 4vw;
  padding: 0 3.5vw;
  border-radius: 1.333vw;
  text-align: center;
}
.btn1 {
  height: 8vw;
  line-height: 8vw;
  font-size: 4vw;
  padding: 0 4vw;
  border-radius: 1.333vw;
  text-align: center;
  background-color: #5b6e96;
}
.img {
  height: 8vw;
  width: 8vw;
  position: absolute;
  left: calc(50% - 4vw);
  background-image: url("data:image/png;base64,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");
  background-size: 100% 100%;
}
.btndiv {
  height: 13.333vw;
  color: #fff;
  justify-content: space-between;
  display: flex;
  align-items: center;
  padding: 0 4vw;
  line-height: 13.333vw;
  font-size: 4vw;
  width: 80vw;
  position: absolute;
  bottom: 18vh;
  left: 0;
  right: 0;
  margin: 0 auto;
  .btn {
    width: 111px;
    height: 36px;
    padding: 0;
    font-size: 16px;
    line-height: 36px;
    text-align: center;
    border-radius: 18px;
    &:nth-of-type(1) {
      background: linear-gradient(180deg, #ffffff 0%, #b1daff 99%);
      color: #2c3850 !important;
    }
    &:nth-of-type(2) {
      background: linear-gradient(180deg, #ff8865, #ff4920);
    }
  }
  .img {
    height: 16vw;
    width: 16vw;
    left: calc(50% - 8vw);
    top: 20vw;
  }
}
.wrapper /deep/ .crop-point {
  opacity: 0;
  z-index: 523;
}
.wrapper /deep/ .cropper-view-box {
  outline: 1px solid #fff;
  border: 1px solid #fff;
}
.wrapper /deep/ .vue-cropper {
  background-color: #000;
  background-image: none;
}
.wrapper {
  height: calc(100vh - 33vw);
  padding: 4vw;
}
</style>

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

推荐阅读更多精彩内容