記一次基于mpvue的小程序開(kāi)發(fā)及上線實(shí)戰(zhàn)

image.png

經(jīng)過(guò)為期兩個(gè)晚上下班時(shí)間的努力肉津,終于把我第一個(gè)小程序開(kāi)發(fā)完成并發(fā)布上線了施敢。整個(gè)過(guò)程還算順利样刷,由于使用了mpvue方案進(jìn)行開(kāi)發(fā),故可以享受和vue一致的流暢開(kāi)發(fā)體驗(yàn);后臺(tái)系統(tǒng)使用了python3+flask框架進(jìn)行岩遗,使用最少的代碼完成了小程序的后臺(tái)邏輯吩坝。除了開(kāi)發(fā)之外,還實(shí)實(shí)在在地體驗(yàn)了一把微信小程序的開(kāi)發(fā)流程缠诅,包括開(kāi)發(fā)者工具的使用溶浴、體驗(yàn)版的發(fā)布、上線的申請(qǐng)等等管引。這些開(kāi)發(fā)體驗(yàn)都非常值得被記錄下來(lái)士败,于是便趁熱打鐵,寫(xiě)下這篇文章褥伴。

一谅将、需求&功能

由于公司里有相當(dāng)多的同事都住在同一個(gè)小區(qū)漾狼,所以上下班的時(shí)候經(jīng)常會(huì)在公司群里組織拼車。但是由于完全依賴聊天記錄饥臂,且上下班拼車的同事也很多邦投,依賴群聊很容易把消息刷走,而且容易造成信息錯(cuò)亂擅笔。既然如此志衣,那么完全可以開(kāi)發(fā)一個(gè)小工具把這些問(wèn)題解決。

發(fā)起拼車的人把出發(fā)地點(diǎn)猛们、目的地點(diǎn)念脯、打車信息以卡片的形式分享出來(lái),參與拼車的人點(diǎn)擊卡片就能選擇參加拼車弯淘,并且能看到同車拼友是誰(shuí)绿店,拼單的信息等等內(nèi)容。

交互流程如下:

image.png

可以看到庐橙,邏輯是非常簡(jiǎn)單的假勿,我們只需要保證生成拼單、分享拼單态鳖、進(jìn)入拼單和退出拼單這四個(gè)功能就好转培。

需求和功能已經(jīng)確定好,首先按照小程序官網(wǎng)的介紹浆竭,注冊(cè)好小程序并拿到appId浸须,接下來(lái)可以開(kāi)始進(jìn)行后臺(tái)邏輯的開(kāi)發(fā)。

二邦泄、后臺(tái)邏輯開(kāi)發(fā)

由于時(shí)間倉(cāng)促删窒,功能又簡(jiǎn)單,所以并沒(méi)有考慮任何高并發(fā)等復(fù)雜場(chǎng)景顺囊,僅僅考慮功能的實(shí)現(xiàn)肌索。從需求的邏輯可以知道,其實(shí)后臺(tái)只需要維護(hù)兩個(gè)列表特碳,分別存儲(chǔ)當(dāng)前所有拼車單以及當(dāng)前所有參與了拼車的用戶即可诚亚,其數(shù)據(jù)結(jié)構(gòu)如下:

  • 當(dāng)前所有拼單列表billsList
image.png
  • 當(dāng)前所有參與了拼車的用戶列表inBillUsers
    image.png

當(dāng)用戶確定并分享了一個(gè)拼單之后,會(huì)直接新建一個(gè)拼單测萎,同時(shí)把該用戶添加到當(dāng)前所有參與了拼車的用戶列表列表里面亡电,并且添加到該拼單的成員列表當(dāng)中:

image.png

只要維護(hù)好這兩個(gè)列表,接下來(lái)就是具體的業(yè)務(wù)邏輯了硅瞧。

為了快速開(kāi)發(fā)份乒,這里我使用了python3+flask框架的方案。不懂python的讀者看到這里也不用緊張,代碼非常簡(jiǎn)單且直白或辖,看看也無(wú)妨瘾英。

首先新建一個(gè)BillController類:

class BillController:
    billsList = []
    inBillUsers = []

接下來(lái)會(huì)在這個(gè)類的內(nèi)部添加創(chuàng)建拼單獲取拼單颂暇、參與拼單缺谴、退出拼單判斷用戶是否在某一拼單中耳鸯、圖片上傳的功能湿蛔。

1、獲取拼單getBill()

該方法接收客戶端傳來(lái)的拼單ID县爬,然后拿這個(gè)ID去檢索是否存在對(duì)應(yīng)的拼單阳啥。若存在則返回對(duì)應(yīng)的拼單,否則報(bào)錯(cuò)給客戶端财喳。

    def getBill(self, ctx):
        ctxBody = ctx.form
        billId = ctxBody['billId']
        try: 
            return response([item for item in self.billsList if item['billId'] == billId][0])
        except IndexError:
            return response({
                'errMsg': '拼單不存在察迟!',
                'billsList': self.billsList,
            }, 1)

2、創(chuàng)建拼單createBill()

該方法會(huì)接收來(lái)自客戶端的用戶信息拼單信息耳高,分別添加到billsListinBillUsers當(dāng)中扎瓶。

    def createBill(self, ctx):
        ctxBody = ctx.form
        user = {
            'userId': ctxBody['userId'],
            'billId': ctxBody['billId'],
            'name': ctxBody['name'],
            'avatar': ctxBody['avatar']
        }
        bill = {
            'billId': ctxBody['billId'],
            'from': ctxBody['from'],
            'to': ctxBody['to'],
            'time': ctxBody['time'],
            'members': [user]
        }

        if ctxBody['userId'] in [item['userId'] for item in self.inBillUsers]:
            return response({
                'errMsg': '用戶已經(jīng)在拼單中!'
            }, 1)

        self.billsList.append(bill)
        self.inBillUsers.append(user)
        return response({
            'billsList': self.billsList,
            'inBillUsers': self.inBillUsers
        })

創(chuàng)建完成后泌枪,會(huì)返回當(dāng)前的billsListinBillUsers到客戶端概荷。

3、參與拼單joinBill()

接收客戶端傳來(lái)的用戶信息拼單ID工闺,把用戶添加到拼單和inBillUsers列表中乍赫。

    def joinBill(self, ctx):
        ctxBody = ctx.form
        billId = ctxBody['billId']
        user = {
            'userId': ctxBody['userId'],
            'name': ctxBody['name'],
            'avatar': ctxBody['avatar'],
            'billId': ctxBody['billId']
        }
        if ctxBody['userId'] in [item['userId'] for item in self.inBillUsers]:
            return response({
                'errMsg': '用戶已經(jīng)在拼單中!'
            }, 1)
        theBill = [item for item in self.billsList if item['billId'] == billId]
        if not theBill:
            return response({
                'errMsg': '拼單不存在'
            }, 1)
        theBill[0]['members'].append(user)
        self.inBillUsers.append(user)
        return response({
            'billsList': self.billsList,
            'inBillUsers': self.inBillUsers
        })

4陆蟆、退出拼單leaveBill()

接收客戶端傳來(lái)的用戶ID拼單ID,然后刪除掉兩個(gè)列表里面的該用戶惋增。

這個(gè)函數(shù)還有一個(gè)功能叠殷,如果判斷到這個(gè)拼單ID所對(duì)應(yīng)的拼單成員為空,會(huì)認(rèn)為該拼單已經(jīng)作廢诈皿,會(huì)直接刪除掉這個(gè)拼單以及所對(duì)應(yīng)的車輛信息圖片林束。

    def leaveBill(self, ctx):
        ctxBody = ctx.form
        billId = ctxBody['billId']
        userId = ctxBody['userId']
        indexOfUser = [i for i, member in enumerate(self.inBillUsers) if member['userId'] == userId][0]
        indexOfTheBill = [i for i, bill in enumerate(self.billsList) if bill['billId'] == billId][0]
        indexOfUserInBill = [i for i, member in enumerate(self.billsList[indexOfTheBill]['members']) if member['userId'] == userId][0]
        # 刪除拼單里面的該用戶
        self.billsList[indexOfTheBill]['members'].pop(indexOfUserInBill)
        # 刪除用戶列表里面的該用戶
        self.inBillUsers.pop(indexOfUser)
        # 如果拼單里面用戶為空,則直接刪除這筆拼單
        if len(self.billsList[indexOfTheBill]['members']) == 0:
            imgPath = './imgs/' + self.billsList[indexOfTheBill]['img'].split('/getImg')[1]
            if os.path.exists(imgPath):
                os.remove(imgPath)
            self.billsList.pop(indexOfTheBill)
        return response({
            'billsList': self.billsList,
            'inBillUsers': self.inBillUsers
        })

5稽亏、判斷用戶是否在某一拼單中inBill()

接收客戶端傳來(lái)的用戶ID壶冒,接下來(lái)會(huì)根據(jù)這個(gè)用戶ID去inBillUsers里面去檢索該用戶所對(duì)應(yīng)的拼單,如果能檢索到截歉,會(huì)返回其所在的拼單胖腾。

    def inBill(self, ctx):
        ctxBody = ctx.form
        userId = ctxBody['userId']
        if ctxBody['userId'] in [item['userId'] for item in self.inBillUsers]:
            return response({
                'inBill': [item for item in self.inBillUsers if ctxBody['userId'] == item['userId']][0],
                'billsList': self.billsList,
                'inBillUsers': self.inBillUsers
            })
        return response({
            'inBill': False,
            'billsList': self.billsList,
            'inBillUsers': self.inBillUsers
        })

6、圖片上傳uploadImg()

接收客戶端傳來(lái)的拼單ID圖片資源,先存儲(chǔ)圖片咸作,然后把該圖片的路徑寫(xiě)入對(duì)應(yīng)拼單ID的拼單當(dāng)中锨阿。

    def uploadImg(self, ctx):
        billId = ctx.form['billId']
        file = ctx.files['file']
        filename = file.filename
        file.save(os.path.join('./imgs', filename))
        # 把圖片信息掛載到對(duì)應(yīng)的拼單
        indexOfTheBill = [i for i, bill in enumerate(self.billsList) if bill['billId'] == billId][0]
        self.billsList[indexOfTheBill]['img'] = url_for('getImg', filename=filename)
        return response({
            'billsList': self.billsList
        })

完成了業(yè)務(wù)邏輯的功能,接下來(lái)就是把它們分發(fā)給不同的路由了:

@app.route('/create', methods = ['POST'])
def create():
    return controller.createBill(request)

@app.route('/join', methods = ['POST'])
def join():
    return controller.joinBill(request)

@app.route('/leave', methods = ['POST'])
def leave():
    return controller.leaveBill(request)

@app.route('/getBill', methods = ['POST'])
def getBill():
    return controller.getBill(request)

@app.route('/inBill', methods = ['POST'])
def inBill():
    return controller.inBill(request)

@app.route('/uploadImg', methods = ['POST'])
def uploadImg():
    return controller.uploadImg(request)

@app.route('/getImg/<filename>')
def getImg(filename):
  return send_from_directory('./imgs', filename)

完整的代碼可以直接到倉(cāng)庫(kù)查看记罚,這里僅展示關(guān)鍵的內(nèi)容墅诡。

三、前端業(yè)務(wù)開(kāi)發(fā)

前端借助vue-cli直接使用了mpvue的mpvue-quickstart來(lái)初始化項(xiàng)目桐智,具體過(guò)程不再細(xì)述末早,直接進(jìn)入業(yè)務(wù)開(kāi)發(fā)部分。

首先说庭,微信小程序的API都是callback風(fēng)格荐吉,為了使用方便,我把用到的小程序API都包裝成了Promise口渔,統(tǒng)一放在src/utils/wx.js內(nèi)部样屠,類似下面這樣:

export const request = obj => new Promise((resolve, reject) => {
  wx.request({
    url: obj.url,
    data: obj.data,
    header: { 'content-type': 'application/x-www-form-urlencoded', ...obj.header },
    method: obj.method,
    success (res) {
      resolve(res.data.data)
    },
    fail (e) {
      console.log(e)
      reject(e)
    }
  })
})

1、注冊(cè)全局Store

由于開(kāi)發(fā)習(xí)慣缺脉,我喜歡把所有接口請(qǐng)求都放在store里面的actions當(dāng)中痪欲,所以這個(gè)小程序也是需要用到Vuex。但由于小程序每一個(gè)Page都是一個(gè)新的Vue實(shí)例攻礼,所以按照Vue的方式业踢,用全局Vue.use(Vuex)是不會(huì)把$store注冊(cè)到實(shí)例當(dāng)中的,這一步要手動(dòng)來(lái)礁扮。

src/目錄下新建一個(gè)store.js文件知举,然后在里面進(jìn)行使用注冊(cè):

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({})

接下來(lái)在src/main.js當(dāng)中,手動(dòng)在Vue的原型里注冊(cè)一個(gè)$store

import Vue from 'vue'
import App from './App'
import Store from './store'

Vue.prototype.$store = Store

這樣太伊,以后在任何的Page里都可以通過(guò)this.$store來(lái)操作這個(gè)全局Store了雇锡。

2、構(gòu)建好請(qǐng)求的API接口

和后臺(tái)系統(tǒng)的邏輯對(duì)應(yīng)僚焦,前端也要構(gòu)造好各個(gè)請(qǐng)求的API接口锰提,這樣的做法能夠避免把API邏輯分散到頁(yè)面四處,具有清晰芳悲、易維護(hù)的優(yōu)勢(shì)立肘。

    /**
     * @param  {} {commit}
     * 獲取用戶公開(kāi)信息
     */
    async getUserInfo ({ commit }) {
      const { userInfo } = await getUserInfo({
        withCredenitals: false
      })
      userInfo.avatar = userInfo.avatarUrl
      userInfo.name = userInfo.nickName
      userInfo.userId = encodeURIComponent(userInfo.nickName + userInfo.city + userInfo.gender + userInfo.country)
      commit('GET_USER_INFO', userInfo)
      return userInfo
    },
    /**
     * @param  {} {commit}
     * @param  { String } userId 用戶ID
     * 檢查用戶是否已經(jīng)存在于某一拼單中
     */
    async checkInBill ({ commit }, userId) {
      const res = await request({
        method: 'post',
        url: `${apiDomain}/inBill`,
        data: {
          userId
        }
      })
      return res
    },
    /**
     * @param  {} {commit}
     * @param  { String } userId 用戶ID
     * @param  { String } name   用戶昵稱
     * @param  { String } avatar 用戶頭像
     * @param  { String } time   出發(fā)時(shí)間
     * @param  { String } from   出發(fā)地點(diǎn)
     * @param  { String } to     目的地點(diǎn)
     * @param  { String } billId 拼單ID
     * 創(chuàng)建拼單
     */
    async createBill ({ commit }, { userId, name, avatar, time, from, to, billId }) {
      const res = await request({
        method: 'post',
        url: `${apiDomain}/create`,
        data: {
          userId,
          name,
          avatar,
          time,
          from,
          to,
          billId
        }
      })
      commit('GET_BILL_INFO', res)
      return res
    },
    /**
     * @param  {} {commit}
     * @param  { String } billId 拼單ID
     * 獲取拼單信息
     */
    async getBillInfo ({ commit }, billId) {
      const res = await request({
        method: 'post',
        url: `${apiDomain}/getBill`,
        data: {
          billId
        }
      })
      return res
    },
    /**
     * @param  {} {commit}
     * @param  { String } userId 用戶ID
     * @param  { String } name   用戶昵稱
     * @param  { String } avatar 用戶頭像
     * @param  { String } billId 拼單ID
     * 參加拼單
     */
    async joinBill ({ commit }, { userId, name, avatar, billId }) {
      const res = await request({
        method: 'post',
        url: `${apiDomain}/join`,
        data: {
          userId,
          name,
          avatar,
          billId
        }
      })
      return res
    },
    /**
     * @param  {} {commit}
     * @param  { String } userId 用戶ID
     * @param  { String } billId 拼單ID
     * 退出拼單
     */
    async leaveBill ({ commit }, { userId, billId }) {
      const res = await request({
        method: 'post',
        url: `${apiDomain}/leave`,
        data: {
          userId,
          billId
        }
      })
      return res
    },
    /**
     * @param  {} {commit}
     * @param  { String } filePath 圖片路徑
     * @param  { String } billId   拼單ID
     * 參加拼單
     */
    async uploadImg ({ commit }, { filePath, billId }) {
      const res = await uploadFile({
        url: `${apiDomain}/uploadImg`,
        header: {
          'content-type': 'multipart/form-data'
        },
        filePath,
        name: 'file',
        formData: {
          'billId': billId
        }
      })
      return res
    }

3、填寫(xiě)拼單并實(shí)現(xiàn)分享功能實(shí)現(xiàn)

新建一個(gè)src/pages/index目錄名扛,作為小程序的首頁(yè)谅年。

該首頁(yè)的業(yè)務(wù)邏輯如下:

  1. 進(jìn)入首頁(yè)的時(shí)候先獲取用戶信息,得到userId
  2. 然后用userId去請(qǐng)求判斷是否已經(jīng)處于拼單
  3. 若是肮韧,則跳轉(zhuǎn)到對(duì)應(yīng)拼單Id的詳情頁(yè)
  4. 若否融蹂,才允許新建拼單

onShow的生命周期鉤子中實(shí)現(xiàn)上述邏輯:

  async onShow () {
    this.userInfo = await this.$store.dispatch('getUserInfo')
    const inBill = await this.$store.dispatch('checkInBill', this.userInfo.userId)

    if (inBill.inBill) {
      wx.redirectTo(`../join/main?billId=${inBill.inBill.billId}&fromIndex=true`)
    }
  },

當(dāng)用戶填寫(xiě)完拼單后旺订,會(huì)點(diǎn)擊一個(gè)帶有open-type="share"屬性的button,然后會(huì)觸發(fā)onShareAppMessage生命周期鉤子的邏輯把拼單構(gòu)造成卡片分享出去殿较。當(dāng)分享成功后會(huì)跳轉(zhuǎn)到對(duì)應(yīng)拼單ID的參加拼單頁(yè)耸峭。

  onShareAppMessage (result) {
    let title = '一起拼車'
    let path = '/pages/index'
    if (result.from === 'button') {
      this.billId = 'billId-' + new Date().getTime()
      title = '我發(fā)起了一個(gè)拼車'
      path = `pages/join/main?billId=${this.billId}`
    }
    return {
      title,
      path,
      success: async (res) => {
        await this.$store.dispatch('createBill', { ...this.userInfo, ...this.billInfo })

        // 上傳圖片
        await this.$store.dispatch('uploadImg', {
          filePath: this.imgSrc,
          billId: this.billId
        })

        // 分享成功后,會(huì)帶著billId跳轉(zhuǎn)到參加拼單頁(yè)
        wx.redirectTo(`../join/main?billId=${this.billId}`)
      },
      fail (e) {
        console.log(e)
      }
    }
  },

4淋纲、參與拼單&退出拼單功能實(shí)現(xiàn)

新建一個(gè)src/pages/join目錄劳闹,作為小程序的“參加拼單頁(yè)”。

該頁(yè)面的運(yùn)行邏輯如下:

  1. 首先會(huì)獲取從url里面帶來(lái)的billId
  2. 其次會(huì)請(qǐng)求一次userInfo洽瞬,獲取userId
  3. 然后拿這個(gè)userId去檢查該用戶是否已經(jīng)處于拼單
  4. 如果已經(jīng)處于拼單本涕,那么就會(huì)獲取一個(gè)新的billId代替從url獲取的
  5. 拿當(dāng)前的billId去查詢對(duì)應(yīng)的拼單信息
  6. 如果billId都無(wú)效,則redirect到首頁(yè)

由于要獲取url攜帶的內(nèi)容伙窃,親測(cè)onShow()是不行的菩颖,只能在onLoad()里面獲取:

  async onLoad (options) {
    // 1\. 首先會(huì)獲取從url里面帶來(lái)的billId
    this.billId = options.billId
    // 2\. 其次會(huì)請(qǐng)求一次userInfo为障,獲取userId
    this.userInfo = await this.$store.dispatch('getUserInfo')
    // 3\. 然后拿這個(gè)userId去檢查該用戶是否已經(jīng)處于拼單
    const inBill = await this.$store.dispatch('checkInBill', this.userInfo.userId)
    // 4\. 如果已經(jīng)處于拼單晦闰,那么就會(huì)有一個(gè)billId
    if (inBill.inBill) {
      this.billId = inBill.inBill.billId
    }
    // 5\. 如果沒(méi)有處于拼單,那么將請(qǐng)求當(dāng)前billId的拼單
    // 6\. 如果billId都無(wú)效鳍怨,則redirect到首頁(yè)呻右,否則檢查當(dāng)前用戶是否處于該拼單當(dāng)中
    await this.getBillInfo()
  }

此外,當(dāng)用戶點(diǎn)擊“參與拼車”后鞋喇,需要重新請(qǐng)求拼單信息声滥,以刷新視圖拼車人員列表;當(dāng)用戶點(diǎn)擊“退出拼車”后侦香,要重定向到首頁(yè)落塑。

經(jīng)過(guò)上面幾個(gè)步驟,客戶端的邏輯已經(jīng)完成罐韩,可以進(jìn)行預(yù)發(fā)布了憾赁。

四、預(yù)發(fā)布&申請(qǐng)上線

如果要發(fā)布預(yù)發(fā)布版本伴逸,需要運(yùn)行npm run build命令缠沈,打包出一個(gè)生產(chǎn)版本的包,然后通過(guò)小程序開(kāi)發(fā)者工具的上傳按鈕上傳代碼错蝴,并填寫(xiě)測(cè)試版本號(hào):

image.png

接下來(lái)可以在小程序管理后臺(tái)→開(kāi)發(fā)管理→開(kāi)發(fā)版本當(dāng)中看到體驗(yàn)版小程序的信息,然后選擇發(fā)布體驗(yàn)版即可:


image.png

當(dāng)確定預(yù)發(fā)布測(cè)試無(wú)誤之后颓芭,就可以點(diǎn)擊“提交審核”顷锰,正式把小程序提交給微信團(tuán)隊(duì)進(jìn)行審核。審核的時(shí)間非惩鑫剩快官紫,在3小時(shí)內(nèi)基本都能夠有答復(fù)肛宋。

值得注意的是,小程序所有請(qǐng)求的API束世,都必須經(jīng)過(guò)域名備案使用https證書(shū)酝陈,同時(shí)要在設(shè)置→開(kāi)發(fā)設(shè)置→服務(wù)器域名里面把API添加到白名單才可以正常使用。

五毁涉、后記

這個(gè)小程序現(xiàn)在已經(jīng)發(fā)布上線了沉帮,算是完整體驗(yàn)了一把小程序的開(kāi)發(fā)樂(lè)趣。小程序得到了微信團(tuán)隊(duì)的大力支持贫堰,以后的生態(tài)只會(huì)越來(lái)越繁榮穆壕。當(dāng)初小程序上線的時(shí)候我也對(duì)它有一些抵觸,但后來(lái)想了想其屏,這只不過(guò)是前端工程師所需面對(duì)的又一個(gè)“端“而已喇勋,沒(méi)有必要為它戴上有色眼鏡,多掌握一些總是好的偎行。

“一起打車吧”微信小程序依然是一個(gè)玩具般的存在川背,僅供自己學(xué)習(xí)和探索,當(dāng)然也歡迎各位讀者能夠貢獻(xiàn)代碼蛤袒,參與開(kāi)發(fā)~

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末熄云,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子汗盘,更是在濱河造成了極大的恐慌皱碘,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,383評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件隐孽,死亡現(xiàn)場(chǎng)離奇詭異癌椿,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)菱阵,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,522評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門踢俄,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人晴及,你說(shuō)我怎么就攤上這事都办。” “怎么了虑稼?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,852評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵琳钉,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我蛛倦,道長(zhǎng)歌懒,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,621評(píng)論 1 284
  • 正文 為了忘掉前任溯壶,我火速辦了婚禮及皂,結(jié)果婚禮上甫男,老公的妹妹穿的比我還像新娘。我一直安慰自己验烧,他們只是感情好板驳,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,741評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著碍拆,像睡著了一般若治。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上倔监,一...
    開(kāi)封第一講書(shū)人閱讀 49,929評(píng)論 1 290
  • 那天直砂,我揣著相機(jī)與錄音,去河邊找鬼浩习。 笑死静暂,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的谱秽。 我是一名探鬼主播洽蛀,決...
    沈念sama閱讀 39,076評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼疟赊!你這毒婦竟也來(lái)了郊供?” 一聲冷哼從身側(cè)響起兰伤,我...
    開(kāi)封第一講書(shū)人閱讀 37,803評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤曙强,失蹤者是張志新(化名)和其女友劉穎芽唇,沒(méi)想到半個(gè)月后跌造,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,265評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡怪蔑,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,582評(píng)論 2 327
  • 正文 我和宋清朗相戀三年歉糜,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了卵酪。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片戳玫。...
    茶點(diǎn)故事閱讀 38,716評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡熙掺,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出咕宿,到底是詐尸還是另有隱情币绩,我是刑警寧澤,帶...
    沈念sama閱讀 34,395評(píng)論 4 333
  • 正文 年R本政府宣布府阀,位于F島的核電站缆镣,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏试浙。R本人自食惡果不足惜费就,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,039評(píng)論 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望川队。 院中可真熱鬧力细,春花似錦、人聲如沸固额。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,798評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)斗躏。三九已至逝慧,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間啄糙,已是汗流浹背笛臣。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,027評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留隧饼,地道東北人沈堡。 一個(gè)月前我還...
    沈念sama閱讀 46,488評(píng)論 2 361
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像燕雁,于是被迫代替她去往敵國(guó)和親诞丽。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,612評(píng)論 2 350

推薦閱讀更多精彩內(nèi)容