Ant Design Pro Vue使用心得

目錄結(jié)構(gòu)

├── public
│   └── logo.png             # LOGO
|   └── index.html           # Vue 入口模板
├── src
│   ├── api                  # Api ajax 等
│   ├── assets               # 本地靜態(tài)資源
│   ├── config               # 項目基礎(chǔ)配置,包含路由秕狰,全局設(shè)置
│   ├── components           # 業(yè)務(wù)通用組件
│   ├── core                 # 項目引導(dǎo), 全局配置初始化,依賴包引入等
│   ├── router               # Vue-Router
│   ├── store                # Vuex
│   ├── utils                # 工具庫
│   ├── locales              # 國際化資源
│   ├── views                # 業(yè)務(wù)頁面入口和常用模板
│   ├── App.vue              # Vue 模板入口
│   └── main.js              # Vue 入口 JS
│   └── permission.js        # 路由守衛(wèi)(路由權(quán)限控制)
├── tests                    # 測試工具
├── README.md
└── package.json

路由和菜單

基本結(jié)構(gòu)

路由和菜單是組織起一個應(yīng)用的關(guān)鍵骨架躁染,pro 中的路由為了方便管理鸣哀,使用了中心化的方式,在 ==router.config.js== 統(tǒng)一配置和管理吞彤。

  • 路由管理 通過約定的語法根據(jù)在==router.config.js==中配置路由我衬。
  • 菜單生成 根據(jù)路由配置來生成菜單。菜單項名稱饰恕,嵌套路徑與路由高度耦合挠羔。
  • 面包屑 組件 ==PageHeader== 中內(nèi)置的面包屑也可由腳手架提供的配置信息自動生成。
路由

目前腳手架中所有的路由都通過 ==router.config.js== 來統(tǒng)一管理埋嵌,在 ==vue-router== 的配置中我們增加了一些參數(shù)破加,如 ==hideChildrenInMenu==,==meta.title==,==meta.icon==,==meta.permission==,來輔助生成菜單雹嗦。其中:

  • hideChildrenInMenu 用于隱藏不需要在菜單中展示的子路由范舀。用法可以查看 分步表單 的配置。
  • hidden 可以在菜單中不展示這個路由了罪,包括子路由锭环。效果可以查看 other 下的路由配置。
  • meta.title 和 meta.icon分別代表生成菜單項的文本和圖標(biāo)泊藕。
  • meta.permission 用來配置這個路由的權(quán)限辅辩,如果配置了將會驗證當(dāng)前用戶的權(quán)限,并決定是否展示 *(默認情況下)娃圆。
  • meta.hidden 可以強制子菜單不顯示在菜單上(和父級 hideChildrenInMenu 配合)
  • meta.hiddenHeaderContent 可以強制當(dāng)前頁面不顯示 PageHeader 組件中的頁面帶的 面包屑和頁面標(biāo)題欄
路由配置項
/**
 * 路由配置說明:
 * 建議:sider menu 請不要超過三級菜單玫锋,若超過三級菜單,則應(yīng)該設(shè)計為頂部主菜單 配合左側(cè)次級菜單
 *
 **/
 {
  redirect: noredirect,  //重定向
  name: 'router-name',   //路由名稱
  hidden: true,          //可以在菜單中不展示這個路由踊餐,包括子路由景醇。效果可以查看 other 下的路由配置。
  meta: {
    title: 'title',      //菜單項名稱
    icon: 'a-icon',      //菜單項圖標(biāo)
    keepAlive: true,     //緩存頁面
    permission:[string]   //用來配置這個路由的權(quán)限吝岭,如果配置了將會驗證當(dāng)前用戶的權(quán)限三痰,并決定是否展示 *(默認情況下)
    hiddenHeaderContent: true,   //可以強制當(dāng)前頁面不顯示 PageHeader 組件中的頁面帶的 面包屑和頁面標(biāo)題欄
  }
}

具體請參考 https://pro.loacg.com/docs/router-and-nav

菜單

菜單根據(jù) ==router.config.js== 生成吧寺,具體邏輯在 ==src/store/modules/permission.js== 中的 ==actions.GenerateRoutes== 方法實現(xiàn)。

Ant Design Pro 的布局

在 Ant Design Pro 中散劫,我們抽離了使用過程中的通用布局稚机,都放在 ==/components/layouts== 目錄中,分別為:

  • BasicLayout:基礎(chǔ)頁面布局获搏,包含了頭部導(dǎo)航赖条,側(cè)邊欄和通知欄:
  • UserLayout:抽離出用于登陸注冊頁面的通用布局
  • PageView:基礎(chǔ)布局,包含了面包屑常熙,和中間內(nèi)容區(qū) (slot)
  • RouterView:空布局纬乍,專門為了二級菜單內(nèi)容區(qū)自定義
  • BlankLayout:空白的布局

定義全局樣式

/* 定義全局樣式 */
:global(.text) {
  font-size: 16px;
}

/* 定義多個全局樣式 */
:global {
  .footer {
    color: #ccc;
  }
  .sider {
    background: #ebebeb;
  }
}
//覆蓋組件樣式
// 使用 css 時可以用 >>> 進行樣式穿透
.test-wrapper >>> .ant-select {
    font-size: 16px;
}

// 使用 scss, less 時,可以用 /deep/ 進行樣式穿透
.test-wrapper /deep/ .ant-select {
    font-size: 16px;
}

// less CSS modules 時亦可用 :global 進行覆蓋
.test-wrapper {
    :global {
        .ant-select {
            font-size: 16px;
        }
    }
}

與服務(wù)器交互

在 Ant Design Pro 中裸卫,一個完整的前端 UI 交互到服務(wù)端處理流程是這樣的:

  1. UI 組件交互操作仿贬;
  2. 調(diào)用統(tǒng)一管理的 api service 請求函數(shù);
  3. 使用封裝的 request.js 發(fā)送請求墓贿;
  4. 獲取服務(wù)端返回茧泪;
  5. 更新 data。

從上面的流程可以看出聋袋,為了方便管理維護队伟,統(tǒng)一的請求處理都放在 @/src/api 文件夾中,并且一般按照 model 緯度進行拆分文件幽勒,如:

api/
  user.js
  permission.js
  goods.js
  ...

其中嗜侮,==@/src/utils/request.js== 是基于 ==axios== 的封裝,便于統(tǒng)一處理 ==POST==代嗤,==GET== 等請求參數(shù)棘钞,請求頭缠借,以及錯誤提示信息等干毅。具體可以參看 ==request.js==。 它封裝了全局 request 攔截器泼返、response 攔截器硝逢、統(tǒng)一的錯誤處理、baseURL 設(shè)置等绅喉。

例如在 api 中的一個請求用戶信息的例子:

// api/user.js
import { axios } fromm '@/utils/request'

const api = {
    info: '/user',
    list: '/users'
}

// 根據(jù)用戶 id 獲取用戶信息
export function getUser (id) {
    return axios({
        url: `${api.user}/${id}`,
        method: 'get'
    })
}

// 增加用戶
export function addUser (parameter) {
    return axios({
        url: api.user,
        method: 'post',
        data: parameter
    })
}

// 更新用戶 // or (id, parameter)
export function updateUser (parameter) {
    return axios({
        url: `${api.user}/${parameter.id}`, // or `${api.user}/${id}`
        method: 'put',
        data: parameter
    })
}

// 刪除用戶
export function deleteUser (id) {
    return axios({
        url: `${api.user}/${id}`,
        method: 'delete',
        data: parameter
    })
}

// 獲取用戶列表 parameter: { pageSize: 10, pageNo: 1 }
export function getUsers (parameter) {
    return axios({
        url: api.list,
        method: 'get',
        params: parameter
    })
}
<template>
    <div>
        <a-button @click="queryUser"></a-button>

        <a-table :dataSource="list">
        </a-table>
    </div>
</template>

<script>
import { getUser, getUsers } from '@/api/user'

export default {
    data () {
        return {
            id: 0,
            queryParam: {
                pageSize: 10,
                pageNo: 1,
                username: ''
            },
            info: {},
            list: []
        }
    },
    methods: {
        queryUser () {
            const { $message } = this
            getUser(this.id).then(res => {
                this.info = res.data
            }).catch(err => {
                $message.error(`load user err: ${err.message}`)
            })
        },
        queryUsers () {
            getUsers(this.queryParam).then(res => {
                this.list = res
            })
        }
    }
}
</script>
**
     * 獲取裁剪后的圖片
     */
    cropImage () {
      this.form.cropimg = this.$refs.cropper.getCroppedCanvas().toDataURL();
    },
    /**
     * 確認裁剪
     */
    sureCrop () {
      this.dialogVisible = false
    },
    /**
     * 上傳裁剪后的圖片到服務(wù)器
     */
    upCropImg () {
      //判斷是否是新增還是編輯
      if (this.$route.query.id && this.$route.query.id != '') {
        //如果是編輯的就直接提交
        this.onSubmit()
      } else {
        //否則先上傳裁剪圖片渠鸽,將64位圖片轉(zhuǎn)換為二進制數(shù)據(jù)流
        var formdata1 = new FormData();// 創(chuàng)建form對象
        formdata1.append('file', convertBase64UrlToBlob(this.form.cropimg), 'aaa.png');//
        this.$ajax
          .post(this.$api + "/upload/singleUploadImg", formdata1, { headers: { 'Content-Type': 'multipart/form-data' } })
          .then(response => {
            if (response.data.msg == "success" && response.data.code == 1) {
              this.form.imgUrl = response.data.data.imgUrl
              this.onSubmit()
            } else {
              console.log(response)
              this.$message.error(response.data.msg);
            }
          })
          .catch(function (error) {
            console.log(error);
          });
      }

    },

引入外部模塊

$ npm install '組件名字' --save
使用
//全局引入
import Vue from 'vue'
import VueQuillEditor from 'vue-quill-editor'

// require styles
import 'quill/dist/quill.core.css'
import 'quill/dist/quill.snow.css'
import 'quill/dist/quill.bubble.css'

Vue.use(VueQuillEditor, /* { default global options } */)
<template>
    <div>
        <quill-editor ref="myTextEditor"
                      v-model="content"
                      :config="editorOption"
                      @blur="onEditorBlur($event)"
                      @focus="onEditorFocus($event)"
                      @ready="onEditorReady($event)">
        </quill-editor>
    </div>
</template>

<script>
//按需加載
import 'quill/dist/quill.core.css'
import 'quill/dist/quill.snow.css'
import 'quill/dist/quill.bubble.css'
import { quillEditor } from 'vue-quill-editor'

export default {
  components: {
    quillEditor
  },
  data () {
      return {
          content: '<h2>I am Example</h2>',
          editorOption: {
           // something config
          }
      }
  },
  // 如果需要手動控制數(shù)據(jù)同步,父組件需要顯式地處理changed事件
  methods: {
    onEditorBlur(editor) {
      console.log('editor blur!', editor)
    },
    onEditorFocus(editor) {
      console.log('editor focus!', editor)
    },
    onEditorReady(editor) {
      console.log('editor ready!', editor)
    },
    onEditorChange({ editor, html, text }) {
      // console.log('editor change!', editor, html, text)
      this.content = html
    }
  },
  // 如果你需要得到當(dāng)前的editor對象來做一些事情柴罐,你可以像下面這樣定義一個方法屬性來獲取當(dāng)前的editor對象徽缚,實際上這里的$refs對應(yīng)的是當(dāng)前組件內(nèi)所有關(guān)聯(lián)了ref屬性的組件元素對象
  computed: {
    editor() {
      return this.$refs.myTextEditor.quillEditor
    }
  },
  mounted() {
    // you can use current editor object to do something(editor methods)
    console.log('this is my editor', this.editor)
    // this.editor to do something...
  }
}
</script>

引入業(yè)務(wù)圖標(biāo)

參考:https://pro.loacg.com/docs/biz-icon

國際化

參考:https://pro.loacg.com/docs/i18n

權(quán)限管理

參考:https://pro.loacg.com/docs/authority-management

自定義使用規(guī)則

  • 修改網(wǎng)站icon的文件地址在 ==public文件夾==中把logo.png換成自定義的革屠,也可在==public/index.html==自定義
<!DOCTYPE html>
<html lang="zh-cmn-Hans">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <link rel="icon" href="<%= BASE_URL %>logo.png">
    <title>共享云店</title>
    <style>#loading-mask{position:fixed;left:0;top:0;height:100%;width:100%;background:#fff;user-select:none;z-index:9999;overflow:hidden}.loading-wrapper{position:absolute;top:50%;left:50%;transform:translate(-50%,-100%)}.loading-dot{animation:antRotate 1.2s infinite linear;transform:rotate(45deg);position:relative;display:inline-block;font-size:64px;width:64px;height:64px;box-sizing:border-box}.loading-dot i{width:22px;height:22px;position:absolute;display:block;background-color:#1890ff;border-radius:100%;transform:scale(.75);transform-origin:50% 50%;opacity:.3;animation:antSpinMove 1s infinite linear alternate}.loading-dot i:nth-child(1){top:0;left:0}.loading-dot i:nth-child(2){top:0;right:0;-webkit-animation-delay:.4s;animation-delay:.4s}.loading-dot i:nth-child(3){right:0;bottom:0;-webkit-animation-delay:.8s;animation-delay:.8s}.loading-dot i:nth-child(4){bottom:0;left:0;-webkit-animation-delay:1.2s;animation-delay:1.2s}@keyframes antRotate{to{-webkit-transform:rotate(405deg);transform:rotate(405deg)}}@-webkit-keyframes antRotate{to{-webkit-transform:rotate(405deg);transform:rotate(405deg)}}@keyframes antSpinMove{to{opacity:1}}@-webkit-keyframes antSpinMove{to{opacity:1}}</style>
  </head>
  <body>
    <noscript>
      <strong>We're sorry but vue-antd-pro doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
    </noscript>
    <div id="app">
      <div id="loading-mask">
          <div class="loading-wrapper">
            <span class="loading-dot loading-dot-spin"><i></i><i></i><i></i><i></i></span>
          </div>
        </div>
    </div>
    <!-- built files will be auto injected -->
  </body>
</html>

-更換logo在==src\components\tools\Logo.vue==中更換

<template>
  <div class="logo">
    <router-link :to="{name:'dashboard'}">
      <LogoSvg alt="logo" />   //這是logo
      <h1 v-if="showTitle">{{ title }}</h1>   //這是網(wǎng)站標(biāo)題
    </router-link>
  </div>
</template>

<script>
import LogoSvg from '@/assets/logo.svg?inline';
export default {
  name: 'Logo',
  components: {
    LogoSvg
  },
  props: {
    title: {
      type: String,
      default: 'Admin For Ok',    //網(wǎng)站默認標(biāo)題
      required: false
    },
    showTitle: {                  //是否顯示網(wǎng)站標(biāo)題凿试,默認不顯示
      type: Boolean,
      default: true,
      required: false
    }
  }
}
</script>

pro權(quán)限管理和路由控制思路分析(粗略分析)

  • 主要是通過三個文件實現(xiàn),==src\mock\services\user.js==文件通過登陸的角色獲取對應(yīng)的鑒權(quán)規(guī)則排宰,具體可查看該文件下的源碼
  • ==src\config\router.config.js==文件為路由配置文件,可增加路由取消路由等那婉,變量asyncRouterMap為主要路由數(shù)組集合板甘,可配置鑒權(quán)權(quán)限,變量constantRouterMap為基礎(chǔ)路由详炬,不參與鑒權(quán)
  • ==src\permission.js==文件為動態(tài)配置路由的主要邏輯盐类,代碼如下
import Vue from 'vue'
import router from './router'
import store from './store'

import NProgress from 'nprogress' // progress bar
import 'nprogress/nprogress.css' // progress bar style
import notification from 'ant-design-vue/es/notification'
import { setDocumentTitle, domTitle } from '@/utils/domUtil'
import { ACCESS_TOKEN } from '@/store/mutation-types'

NProgress.configure({ showSpinner: false }) // NProgress Configuration

const whiteList = ['login', 'register', 'registerResult'] // no redirect whitelist配置白名單

router.beforeEach((to, from, next) => {
  NProgress.start() // start progress bar
  //生成動態(tài)網(wǎng)站標(biāo)題
  to.meta && (typeof to.meta.title !== 'undefined' && setDocumentTitle(`${to.meta.title} - ${domTitle}`))
  if (Vue.ls.get(ACCESS_TOKEN)) {
    /* has token 如果有token并且是從登錄頁來的就直接跳到工作空間*/
    if (to.path === '/user/login') {
      next({ path: '/dashboard/workplace' })
      NProgress.done()
    } else {
    //否則檢測是不是沒有檢測到規(guī)則,請求獲取用戶信息呛谜,獲取用戶權(quán)限
      if (store.getters.roles.length === 0) {
      //請求mock模擬數(shù)據(jù)獲取用戶權(quán)限
        store
          .dispatch('GetInfo')
          .then(res => {
            const roles = res.result && res.result.role
            //調(diào)用src\store\modules\permission.js里面的GenerateRoutes方法在跳,處理數(shù)據(jù)
            store.dispatch('GenerateRoutes', { roles }).then(() => {
              // 根據(jù)roles權(quán)限生成可訪問的路由表
              // 動態(tài)添加可訪問路由表
              router.addRoutes(store.getters.addRouters)
              const redirect = decodeURIComponent(from.query.redirect || to.path)
              if (to.path === redirect) {
                // hack方法 確保addRoutes已完成 ,set the replace: true so the navigation will not leave a history record
                next({ ...to, replace: true })
              } else {
                // 跳轉(zhuǎn)到目的路由
                next({ path: redirect })
              }
            })
          })
          .catch(() => {
            notification.error({
              message: '錯誤',
              description: '請求用戶信息失敗,請重試'
            })
            store.dispatch('Logout').then(() => {
              next({ path: '/user/login', query: { redirect: to.fullPath } })
            })
          })
      } else {
        next()
      }
    }
  } else {
    if (whiteList.includes(to.name)) {
      // 在免登錄白名單隐岛,直接進入
      next()
    } else {
      next({ path: '/user/login', query: { redirect: to.fullPath } })
      NProgress.done() // if current page is login will not trigger afterEach hook, so manually handle it
    }
  }
})

router.afterEach(() => {
  NProgress.done() // finish progress bar
})

  • ==src\store\modules\permission.js==文件為路由數(shù)據(jù)的詳細處理邏輯硬毕,配合src\permission.js文件使用,代碼如下:
import { asyncRouterMap, constantRouterMap } from '@/config/router.config'

/**
 * 過濾賬戶是否擁有某一個權(quán)限礼仗,并將菜單從加載列表移除
 *
 * @param permission
 * @param route
 * @returns {boolean}
 */
function hasPermission (permission, route) {
  if (route.meta && route.meta.permission) {
    let flag = false
    for (let i = 0, len = permission.length; i < len; i++) {
      flag = route.meta.permission.includes(permission[i])
      if (flag) {
        return true
      }
    }
    return false
  }
  return true
}

/**
 * 單賬戶多角色時吐咳,使用該方法可過濾角色不存在的菜單
 *
 * @param roles
 * @param route
 * @returns {*}
 */
// eslint-disable-next-line
function hasRole(roles, route) {
  if (route.meta && route.meta.roles) {
    return route.meta.roles.includes(roles.id)
  } else {
    return true
  }
}

function filterAsyncRouter (routerMap, roles) {
  const accessedRouters = routerMap.filter(route => {
    if (hasPermission(roles.permissionList, route)) {
      if (route.children && route.children.length) {
        route.children = filterAsyncRouter(route.children, roles)
      }
      return true
    }
    return false
  })
  return accessedRouters
}

const permission = {
  state: {
    routers: constantRouterMap,
    addRouters: []
  },
  mutations: {
    SET_ROUTERS: (state, routers) => {
      state.addRouters = routers
      state.routers = constantRouterMap.concat(routers)
    }
  },
  actions: {
    GenerateRoutes ({ commit }, data) {
      return new Promise(resolve => {
        const { roles } = data
        const accessedRouters = filterAsyncRouter(asyncRouterMap, roles)
        commit('SET_ROUTERS', accessedRouters)
        resolve()
      })
    }
  }
}

export default permission

跨域請求設(shè)置

在==vue.config.js==文件中修改

 // 配置跨域
  devServer: {
    // development server port 8000
    // port: 8000,
    proxy: {
      '/apis': {
        // target: 'https://mock.ihx.me/mock/5baf3052f7da7e07e04a5116/antd-pro',
        target: 'http://192.168.1.73:8092/okcloud/',
        // target: 'http://39.107.78.120:8083/okcloud/',

        ws: false,
        changeOrigin: true,  //是否允許跨域
        pathRewrite: {
          '^/apis': ''
        }
      }
    }

axios攔截器

在文件==src\utils\request.js==中設(shè)置

// request interceptor
service.interceptors.request.use(config => {
  const token = Vue.ls.get(ACCESS_TOKEN)
  if (token) {
    config.headers['okcloud_token'] = token // 讓每個請求攜帶自定義 token 請根據(jù)實際情況自行修改
  }
  return config
}, err)

// response interceptor
service.interceptors.response.use((response) => {
  if (response.data.code === 10000) {
    notification.warning({
      message: '提示',
      description: response.data.message
    })
  } else {
    return response.data
  }
}, err)

自定義角色的菜單權(quán)限

  • 在==src\main.js==文件中注釋掉"http:// import './permission' // permission control 權(quán)限控制"
  • 自定義路由權(quán)限文件==src\routeGuard.js==,代碼如下
import Vue from 'vue'
import router from './router'
// import store from './store'

import NProgress from 'nprogress' // progress bar
import 'nprogress/nprogress.css' // progress bar style
import { setDocumentTitle, domTitle } from '@/utils/domUtil'
import { ACCESS_TOKEN } from '@/store/mutation-types'

NProgress.configure({ showSpinner: false }) // NProgress Configuration

router.beforeEach((to, from, next) => {
  NProgress.start() // start progress bar
  to.meta && (typeof to.meta.title !== 'undefined' && setDocumentTitle(`${to.meta.title} - ${domTitle}`))
  if (to.path === '/user/login' && Vue.ls.get(ACCESS_TOKEN)) {
    next({ path: '/dashboard/workplace' })
    NProgress.done()
  } else if (to.path !== '/user/login' && !Vue.ls.get(ACCESS_TOKEN)) {
    next({ path: '/user/login' })
    NProgress.done()
  } else {
    next()
    NProgress.done()
  }
})

router.afterEach(() => {
  NProgress.done() // finish progress bar
})

  • 在==main.js==中引入import './routeGuard'
  • 對==src\components\Menu\menu.js==文件進行自定義菜單改造
  • 在==src\store\modules\app.js==文件中store加入menu,在actions中進行獲取菜單的異步操作元践,獲取菜單信息韭脊,然后進行全局變量動態(tài)獲取
  • 在==src\layouts\BasicLayout.vue==中進行全局變量的引用
    ...mapState({
      // 動態(tài)主路由
      menus: state => state.app.menu
    }),

動態(tài)方法的引用

...mapActions(['setSidebar', 'setMenu']),

調(diào)用獲取動態(tài)方法

this.setMenu()
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市单旁,隨后出現(xiàn)的幾起案子沪羔,更是在濱河造成了極大的恐慌,老刑警劉巖象浑,帶你破解...
    沈念sama閱讀 212,599評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蔫饰,死亡現(xiàn)場離奇詭異,居然都是意外死亡愉豺,警方通過查閱死者的電腦和手機篓吁,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,629評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蚪拦,“玉大人杖剪,你說我怎么就攤上這事〕鄞” “怎么了盛嘿?”我有些...
    開封第一講書人閱讀 158,084評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長括袒。 經(jīng)常有香客問我次兆,道長,這世上最難降的妖魔是什么锹锰? 我笑而不...
    開封第一講書人閱讀 56,708評論 1 284
  • 正文 為了忘掉前任芥炭,我火速辦了婚禮狈邑,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘蚤认。我一直安慰自己米苹,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,813評論 6 386
  • 文/花漫 我一把揭開白布砰琢。 她就那樣靜靜地躺著蘸嘶,像睡著了一般。 火紅的嫁衣襯著肌膚如雪陪汽。 梳的紋絲不亂的頭發(fā)上训唱,一...
    開封第一講書人閱讀 50,021評論 1 291
  • 那天,我揣著相機與錄音挚冤,去河邊找鬼况增。 笑死,一個胖子當(dāng)著我的面吹牛训挡,可吹牛的內(nèi)容都是我干的澳骤。 我是一名探鬼主播,決...
    沈念sama閱讀 39,120評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼澜薄,長吁一口氣:“原來是場噩夢啊……” “哼为肮!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起肤京,我...
    開封第一講書人閱讀 37,866評論 0 268
  • 序言:老撾萬榮一對情侶失蹤颊艳,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后忘分,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體棋枕,經(jīng)...
    沈念sama閱讀 44,308評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,633評論 2 327
  • 正文 我和宋清朗相戀三年妒峦,在試婚紗的時候發(fā)現(xiàn)自己被綠了重斑。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,768評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡舟山,死狀恐怖绸狐,靈堂內(nèi)的尸體忽然破棺而出卤恳,到底是詐尸還是另有隱情累盗,我是刑警寧澤,帶...
    沈念sama閱讀 34,461評論 4 333
  • 正文 年R本政府宣布突琳,位于F島的核電站若债,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏拆融。R本人自食惡果不足惜蠢琳,卻給世界環(huán)境...
    茶點故事閱讀 40,094評論 3 317
  • 文/蒙蒙 一啊终、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧傲须,春花似錦蓝牲、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,850評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至已卸,卻和暖如春佛玄,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背累澡。 一陣腳步聲響...
    開封第一講書人閱讀 32,082評論 1 267
  • 我被黑心中介騙來泰國打工梦抢, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人愧哟。 一個月前我還...
    沈念sama閱讀 46,571評論 2 362
  • 正文 我出身青樓奥吩,卻偏偏與公主長得像,于是被迫代替她去往敵國和親蕊梧。 傳聞我的和親對象是個殘疾皇子圈驼,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,666評論 2 350

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