??我用Tauri開發(fā)的待辦效率工具開源了!

開源倉庫地址 gitee

Git倉庫地址:https://gitee.com/zhanhongzhu/zhanhongzhu.git

應(yīng)用地址

windows應(yīng)用地址下載 https://kestrel-task.cn

具體內(nèi)容

也可以看??使用Tauri+vite+koa2+mysql開發(fā)了一款待辦效率應(yīng)用 這篇文章。

??技術(shù)棧

  • Tauri: Tauri是一個用于構(gòu)建現(xiàn)代桌面應(yīng)用程序的工具,結(jié)合了Rust、Vue.js和Web技術(shù),提供了強大的跨平臺能力。
  • Vue3: Vue3是流行的JavaScript框架Vue.js的最新版本,具有更好的性能、更好的TypeScript支持和更多的特性。
  • Vite5: Vite是一個現(xiàn)代化的構(gòu)建工具,Vite5是其最新版本,具有快速的冷啟動、熱模塊替換和原生ES模塊支持。
  • Koa2: Koa2是一個基于Node.js的輕量級Web框架,使用異步函數(shù)處理中間件,提供了簡潔而強大的Web開發(fā)體驗。
  • MySQL: MySQL是一個流行的關(guān)系型數(shù)據(jù)庫管理系統(tǒng),具有高性能、可靠性和廣泛的應(yīng)用領(lǐng)域,適用于各種規(guī)模的應(yīng)用程序。

我的待辦

快速添加待辦任務(wù),快速查看任務(wù)進(jìn)度,摘要等。新增標(biāo)簽,分類,更好管理待辦任務(wù)。通過標(biāo)簽、分類篩選待辦任務(wù),方便快捷。

[圖片上傳失敗...(image-c98267-1721834837063)]

[圖片上傳失敗...(image-683b1c-1721834837063)]

OKR目標(biāo)管理

我的想法是通過OKR管理系列的任務(wù),這樣每完成一個小任務(wù),就可以關(guān)閉一個小任務(wù),直觀又方便,等到所有關(guān)鍵的小任務(wù)都完成了,整個任務(wù)也就完成了。

[圖片上傳失敗...(image-a61a3-1721834837063)]

番茄工作法

主要是一個計時的時鐘,可以在專注計時的時候,專注地完成某項任務(wù),快捷方便,使用番茄工作法,選擇一個待完成的任務(wù),將番茄時間設(shè)為25分鐘,也可以選擇其他的區(qū)間,專注工作,中途不允許做任何與該任務(wù)無關(guān)的事。時刻保持專注。

[圖片上傳失敗...(image-9bd314-1721834837063)]

日歷視圖

打開日歷界面,通過視圖的形式查看公歷或農(nóng)歷日歷下每個日期的待辦提醒或任務(wù)事項。也可以在日歷視圖,添加任務(wù)。

[圖片上傳失敗...(image-8b539b-1721834837063)]

MEMO快速記錄

為了更好地幫你捕捉想法與靈感,提供了快速記錄的輸入框。專注記錄想法,無需思考標(biāo)題和排版。控制記錄長度,降低記錄壓力,快速捕捉。

[圖片上傳失敗...(image-8e11af-1721834837063)]

統(tǒng)計功能

展示了每天的待辦數(shù),以及每天新增的待辦數(shù)量。

[圖片上傳失敗...(image-317334-1721834837063)]

展示功能點

打包發(fā)布版本

腳本命令

  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "preview": "vite preview",
    "tauri": "tauri",
    "pub": "cd build && node ./updateVersion.js && pnpm tauri build && node ./publish.js"
  }
npm run pub

更新版本號以及更新publicKey

//build/publish.js
import fs from 'fs'
// 讀取 tauri.conf.json
const tauriConf = JSON.parse(fs.readFileSync('../src-tauri/tauri.conf.json', 'utf8'));
let newVersion = tauriConf.package.version; //更新的版本號

// 讀取 update.json
let updateJson = JSON.parse(fs.readFileSync('update.json', 'utf8'));
// 更新 update.json version
updateJson.version = newVersion;

//獲取版本更新的內(nèi)容
//簽名、版本路徑、發(fā)版日期
const signature =  fs.readFileSync(`../src-tauri/target/release/bundle/msi/kestrel-task_${newVersion}_x64_zh-CN.msi.zip.sig`, 'utf8');
updateJson.platforms['windows-x86_64'].signature = signature;
updateJson.platforms['windows-x86_64'].url = `https://kestrel-task.cn/kestrel-task_${newVersion}_x64_zh-CN.msi.zip`
updateJson.pub_date = new Date();
fs.writeFileSync('update.json', JSON.stringify(updateJson, null, 2));

版本json信息

//build/update.json

{
  "version": "1.0.8",
  "notes": "kestrel-task",
  "pub_date": "2024-03-23T04:23:39.799Z",
  "platforms": {
    "windows-x86_64": {
      "signature": "dW50cnVzdGVkIGNvbW1lbnQ6IHNpZ25hdHVyZSBmcm9tIHRhdXJpIHNlY3JldCBrZXkKUlVSNVRCUit5Zjc1Y3JLV085djl6eTMza2NqMXFIV0paNkl2ckgrTGZTRm9wcEJwcUlkaTBhM2hvN3pSVkRUZXlTZ2NSejJremg2Vklja041VkZmdGlZZ0hxTGVVM2xlL3dFPQp0cnVzdGVkIGNvbW1lbnQ6IHRpbWVzdGFtcDoxNzExMTY3ODE5CWZpbGU6a2VzdHJlbC10YXNrXzEuMC44X3g2NF96aC1DTi5tc2kuemlwCldSWVdwb0dwRU1aQUJ2ckFra2FTMjBkcnZtL0FWU3grd3MzeHZVTDhWRFFFUC9QWkpzdUNvUG9HZXBrVmhWMkoxTkpGc2pkYU5rRHYwcVdHdlk5dkFBPT0K",
      "url": "https://kestrel-task.cn/kestrel-task_1.0.8_x64_zh-CN.msi.zip"
    }
  }
}

更新tauri.conf.json版本信息

// build/updateVersion.js
import fs from 'fs' // 讀取 tauri.conf.json
const tauriConf = JSON.parse(fs.readFileSync('../src-tauri/tauri.conf.json', 'utf8'));
let version = tauriConf.package.version; //更新的版本號

//更新版本號
let versionParts = version.split('.').map(Number);
versionParts[2] += 1;
let newVersion =versionParts.join('.');
// 更新 tauri.conf.json version
tauriConf.package.version = newVersion;
fs.writeFileSync('../src-tauri/tauri.conf.json', JSON.stringify(tauriConf, null, 2));

使用tauri的http模塊

此處進(jìn)行簡單的封裝,接口請求在控制臺無法被查看到。如果覺得不方便,完全可以使用axios庫。也是可以的。

import { http } from "@tauri-apps/api";

export function request(config) {
  return new Promise((resolve, reject) => {
    http
      .fetch("https://kestrel-task.cn" + config.url, {
        method: "POST",
        body: http.Body.json(config.data),
        headers: {
          Authorization: token,
        },
      })
      .then((res) => {
        resolve(res.data.data);
      })
      .catch((err) => {
        reject(err);
      });
  });
}

http封裝get請求

export function requestGet(config, d) {
  let url = d ? config.url : "https://kestrel-task.cn" + config.url;
  return new Promise((resolve, reject) => {
    http
      .fetch(url, {
        method: "get",
        headers: {
          Authorization: token,
        },
      })
      .then((res) => {
        resolve(d ? res.data : res.data.data);
      })
      .catch((err) => {
        reject(err);
      });
  });
}

使用封裝的request函數(shù)

export const login = (data) => {
  return request({
    url: "/web/login",
    method: "post",
    data,
  });
};

使用WebviewWindow封裝公共的窗口

import { WebviewWindow } from '@tauri-apps/api/window'
import { emit } from '@tauri-apps/api/event'

// 創(chuàng)建新窗口
export async function createWin(args) {
    await emit('win-create', args)
}

// 獲取窗口
export async function getWin(label) {
    return await WebviewWindow.getByLabel(label)
}

/**
 * @desc 設(shè)置窗口
 * @param type {string} 'show'|'hide'|'close'|'min'|'max'|'max2min'|'exit'|'relaunch'
 */
export async function setWin(type) {
    await emit('win-' + type)
}

// 登錄窗口
export async function loginWin() {
    await createWin({
        label: 'Login',
        title: '登錄',
        url: '/login',
        width: 320,
        height: 420,
        resizable: false,
        alwaysOnTop: true,
    })
}

// ...
/**
 * @desc    封裝新開多窗體
 */

import {
  WebviewWindow,
  appWindow,
  getAll,
  getCurrent,
} from "@tauri-apps/api/window";
import { relaunch, exit } from "@tauri-apps/api/process";
import { emit, listen } from "@tauri-apps/api/event";

import { setWin } from "./actions.js";

// 系統(tǒng)參數(shù)配置
export const windowConfig = {
  label: null, // 窗口唯一label
  title: "", // 窗口標(biāo)題
  url: "", // 路由地址url
  width: 900, // 窗口寬度
  height: 640, // 窗口高度
  minWidth: null, // 窗口最小寬度
  minHeight: null, // 窗口最小高度
  x: null, // 窗口相對于屏幕左側(cè)坐標(biāo)
  y: null, // 窗口相對于屏幕頂端坐標(biāo)
  center: true, // 窗口居中顯示
  resizable: true, // 是否支持縮放
  maximized: false, // 最大化窗口
  decorations: true, // 窗口是否無邊框及導(dǎo)航條
  alwaysOnTop: false, // 置頂窗口
};

class Windows {
  constructor() {
    this.mainWin = null;
  }

  // 獲取窗口
  getWin(label) {
    return WebviewWindow.getByLabel(label);
  }

  // 獲取全部窗口
  getAllWin() {
    return getAll();
  }

  // 創(chuàng)建新窗口
  async createWin(options) {
    const args = Object.assign({}, windowConfig, options);

    // 判斷窗口是否存在
    const existWin = getAll().find((w) => w.label == args.label);
    if (existWin) {
      if (existWin.label.indexOf("main") == -1) {
        await existWin?.unminimize();
        await existWin?.setFocus();
        return;
      }
      await existWin?.close();
    }

    // 創(chuàng)建窗口對象
    let win = new WebviewWindow(args.label, args);

    // 是否最大化
    if (args.maximized && args.resizable) {
      win.maximize();
    }

    // 窗口創(chuàng)建完畢/失敗
    win.once("tauri://created", async () => {
      console.log("window create success!");
    });

    win.once("tauri://error", async () => {
      console.log("window create error!");
    });
  }

  // 開啟主進(jìn)程監(jiān)聽事件
  async listen() {
    // 創(chuàng)建新窗體
    await listen("win-create", (event) => {
      this.createWin(JSON.parse(event.payload));
    });

    // 顯示窗體
    await listen("win-show", async (event) => {
      if (appWindow.label.indexOf("main") == -1) return;
      await appWindow.show();
      await appWindow.unminimize();
      await appWindow.setFocus();
    });

    // 隱藏窗體
    await listen("win-hide", async (event) => {
      if (appWindow.label.indexOf("main") == -1) return;
      await appWindow.hide();
    });

    // 退出應(yīng)用
    await listen("win-exit", async (event) => {
      setWin("logout");
      await exit();
    });

    // 重啟應(yīng)用
    await listen("win-relaunch", async (event) => {
      await relaunch();
    });

    // 主/渲染進(jìn)程傳參
    await listen("win-setdata", async (event) => {
      await emit("win-postdata", JSON.parse(event.payload));
    });
  }
}

export default Windows;

封裝Echart組件,便于使用

[圖片上傳失敗...(image-e7e272-1721834837063)]

<template>
    <div ref="MyEcharts" :style="{ height: height, width: width }"></div>
  </template>
  
  <script>
  import * as echarts from 'echarts'
  import T from './echarts-theme-T.js'
  echarts.registerTheme('T', T)
  const unwarp = obj => obj && (obj.__v_raw || obj.valueOf() || obj)
  export default {
      ...echarts,
      name: 'Charts',
      props: {
          // 高度
          height: { type: String, default: '100%' },
          // 寬度
          width: { type: String, default: '100%' },
          // 是否無數(shù)據(jù)
          nodata: { type: Boolean, default: false },
          // 配置項
          option: { type: Object, default: () => {} }
      },
      data() {
          return {
              isActivat: false,
              myChart: null,
              MyEcharts:null
          }
      },
      watch: {
          option: {
              deep: true,
              handler(v) {
                  unwarp(this.myChart).setOption(v)
              }
          }
      },
      computed: {
          myOptions: function() {
              return this.option || {}
          }
      },
      activated() {
          if (!this.isActivat) {
              this.$nextTick(() => {
                  this.myChart.resize()
              })
          }
      },
      deactivated() {
          this.isActivat = false
      },
      mounted() {
          this.isActivat = true
          this.$nextTick(() => {
              this.draw()
          })
      },
      methods: {
          draw() {
              const myChart = echarts.init(this.$refs.MyEcharts, 'T')
              myChart.setOption(this.myOptions)
              this.myChart = myChart
              window.addEventListener('resize', () => myChart.resize())
          }
      }
  }
  </script>

echart主題模塊

//echarts-theme-T.js
const T = {
    color: ['#409EFF', '#36CE9E', '#f56e6a', '#626c91', '#edb00d', '#909399'], // 顏色數(shù)組
    grid: { // 網(wǎng)格
        left: '3%', // 左邊距
        right: '3%', // 右邊距
        bottom: '10', // 下邊距
        top: '40', // 上邊距
        containLabel: true // 包含標(biāo)簽
    },
    legend: { // 圖例
        textStyle: { // 文本樣式
            color: '#999' // 顏色
        },
        inactiveColor: 'rgba(128,128,128,0.4)' // 不活躍顏色
    },
    categoryAxis: { // 類別軸
        axisLine: { // 軸線
            show: true, // 顯示
            lineStyle: { // 線條樣式
                color: 'rgba(128,128,128,0.2)', // 顏色
                width: 1 // 寬度
            }
        },
        axisTick: { // 刻度線
            show: false, // 不顯示
            lineStyle: { // 線條樣式
                color: '#000' // 顏色
            }
        },
        axisLabel: { // 軸標(biāo)簽
            color: '#999' // 顏色
        },
        splitLine: { // 分隔線
            show: false, // 不顯示
            lineStyle: { // 線條樣式
                color: ['#eee'] // 顏色
            }
        },
        splitArea: { // 分隔區(qū)域
            show: false, // 不顯示
            areaStyle: { // 區(qū)域樣式
                color: ['rgba(255,255,255,0.01)', 'rgba(0,0,0,0.01)'] // 顏色
            }
        }
    },
    valueAxis: { // 數(shù)值軸
        axisLine: { // 軸線
            show: false, // 不顯示
            lineStyle: { // 線條樣式
                color: '#999' // 顏色
            }
        },
        splitLine: { // 分隔線
            show: true, // 顯示
            lineStyle: { // 線條樣式
                color: 'rgba(128,128,128,0.2)' // 顏色
            }
        }
    }
}

export default T

封裝公共的彈窗組件

[圖片上傳失敗...(image-f08344-1721834837063)]

<template>
  <el-dialog class="my-dialog" draggable v-bind="$attrs" v-model="modelValue" :modal-append-to-body="modalAppendToBody"
    :append-to-body="appendToBody" :fullscreen="fullscreen" :close-on-click-modal="closeOnClickModal"
    :close-on-press-escape="closeOnPressEscape" :width="comWidth" :top="top" @closed="closed">
    <template v-slot:title>
      <slot name="title">
        <span class="my-dialog-title">{{ dialogTitle || ''}}</span>
      </slot>
    </template>
    <div v-loading="loading" class="body-content" :style="{'height':comHeight}">
      <slot></slot>
    </div>
    <template #footer>
      <div class="dialog-footer" v-if="closeBtn">
        <el-button type="close" size="small1" @click="closed">關(guān)閉</el-button>
      </div>
    </template>
  </el-dialog>
</template>
<script setup>
import { computed } from 'vue'
const props = defineProps(
  {
    visible: { type: Boolean, default: false }, // 是否可見
    loading: { type: Boolean, default: false }, // 是否加載中
    top: { type: String, default: '20vh' }, // 距離頂部的距離
    fullscreen: { type: Boolean, default: false }, // 是否全屏
    size: { type: String, default: 'big' }, // 大小
    width: { type: [Number, String], default: 0 }, // 寬度
    height: { type: [Number, String], default: '55vh' }, // 寬度
    dialogTitle: { type: String, default: '' }, // 彈出框標(biāo)題
    modalAppendToBody: { type: Boolean, default: false }, // 是否將彈出框插入到body中
    appendToBody: { type: Boolean, default: false }, // 是否將內(nèi)容插入到body中
    closeOnClickModal: { type: Boolean, default: false }, // 是否在點擊模態(tài)框時關(guān)閉
    closeOnPressEscape: { type: Boolean, default: false }, // 是否在按下ESC鍵時關(guān)閉
    dblclickDisabled: { type: Boolean, default: false }, // 是否禁用雙擊放大
    closeBtn: { type: Boolean, default: false }, // 關(guān)閉按鈕
  },
  ['modelValue']
)

const comWidth = computed(() => {
  if (props.size === 'small') {
    return props.width || '30%'
  } else if (props.size === 'middle') {
    return props.width || '40%'
  } else if (props.size === 'big') {
    return props.width || '60%'
  }
  return props.width || '40%'
})

const comHeight = computed(() => {
  return props.height || '55vh'
})

const emit = defineEmits(['update:modelValue','closed'])
const closed = () => {
  emit('update:modelValue')
  emit('closed',false)
}
</script>

封裝ResizeObserver函數(shù)

主要是監(jiān)聽元素的變化或者窗口的變化。有一部分用到了可以拉伸的左右布局的模塊。

//directive/index.js
// 監(jiān)聽元素大小變化的指令
const map = new WeakMap();
const ob = new ResizeObserver((entries) => {
  for (const entry of entries) {
    // 獲取dom元素的回調(diào)
    const handler = map.get(entry.target);
    //存在回調(diào)函數(shù)
    if (handler) {
      // 將監(jiān)聽的值給回調(diào)函數(shù)
      handler({
        width: entry.borderBoxSize[0].inlineSize,
        height: entry.borderBoxSize[0].blockSize,
      });
    }
  }
});

export const Resize = {
  mounted(el, binding) {
    //將dom與回調(diào)的關(guān)系塞入map
    map.set(el, binding.value);
    //監(jiān)聽el元素的變化
    ob.observe(el);
  },
  unmounted(el) {
    //取消監(jiān)聽
    ob.unobserve(el);
  },
};
const directives = { Resize };

const registerDirective = (app) => {
  Object.keys(directives).forEach((key) => {
    app.directive(key, directives[key]);
  });
};
export default registerDirective;

公共拖曳布局的函數(shù)

主要是用于左右布局寬度的變化,可以使用鼠標(biāo)進(jìn)行拖曳,改變左右盒子的高度。

export const useCommon = ()=>{
    function setLayoutDrag(dragId) {
        const resize = document.getElementById(dragId)
        let previousElement = resize.previousSibling
        let nextElement = resize.nextSibling
        let previousTag = previousElement.tagName
        let nextTag = nextElement.tagName
        resize.onmousedown = (e) => {
          const startX = e.clientX
          const startY = e.clientY
          let type = ''
          if (previousTag === 'ASIDE' && nextTag === 'MAIN') {
            type = 'ASIDE-MAIN'
          } else if (previousTag === 'MAIN' && nextTag === 'ASIDE') {
            type = 'MAIN-ASIDE'
          } else if (
            (previousTag === 'HEADER' && nextTag === 'MAIN') ||
            (previousTag === 'FOOTER' && nextTag === 'MAIN')
          ) {
            type = 'HEADER-MAIN'
          } else if (
            (previousTag === 'MAIN' && nextTag === 'HEADER') ||
            (previousTag === 'MAIN' && nextTag === 'FOOTER')
          ) {
            type = 'MAIN-HEADER'
          }
          let initWidth = 0,
            initHeight = 0
          if (type === 'ASIDE-MAIN') {
            initWidth = previousElement.clientWidth // 初始位置
          } else if (type === 'MAIN-ASIDE') {
            initWidth = nextElement.clientWidth // 初始位置
          } else if (type === 'HEADER-MAIN') {
            initHeight = previousElement.clientHeight
          } else if (type === 'MAIN-HEADER') {
            initHeight = nextElement.clientHeight
          }
          document.onmousemove = (k) => {
            const endX = k.clientX
            const endY = k.clientY
            let moveLen = endX - startX // 橫向移動寬度
            let moveHeight = endY - startY // 縱向移動高度
            switch (type) {
              case 'ASIDE-MAIN':
                let asideMainWidth = initWidth + moveLen
                if (moveLen < 0) {
                  // 向左移
                  if (asideMainWidth > 400) {
                    // 左側(cè)剩90
                    previousElement.style.width = asideMainWidth + 'px'
                  }
                } else {
                  // 向右移動
                  if (nextElement.clientWidth > 400) {
                    // 右側(cè)剩90
                    previousElement.style.width = asideMainWidth + 'px'
                  }
                }
                break
              case 'MAIN-ASIDE':
                let mainAsideWidth = initWidth - moveLen
                if (moveLen < 0) {
                  // 向左移
                  if (previousElement.clientWidth > 400) {
                    // 左側(cè)剩90
                    nextElement.style.width = mainAsideWidth + 'px'
                  }
                } else {
                  // 向右移動
                  if (mainAsideWidth > 400) {
                    nextElement.style.width = mainAsideWidth + 'px'
                  }
                }
                break
              case 'HEADER-MAIN': {
                let headerMainHeight = initHeight + moveHeight
                if (moveHeight < 0) {
                  // 向上移
                  if (headerMainHeight > 60) {
                    // 上側(cè)剩90
                    previousElement.style.height = headerMainHeight + 'px'
                  }
                } else {
                  // 向下移動
                  if (nextElement.clientHeight > 60) {
                    // 下側(cè)剩90
                    previousElement.style.height = headerMainHeight + 'px'
                  }
                }
                break
              }
              case 'MAIN-HEADER': {
                let mainHeaderHeight = initHeight - moveHeight
                if (moveHeight < 0) {
                  // 向上移
                  if (previousElement.clientHeight > 60) {
                    // 左側(cè)剩90
                    nextElement.style.height = mainHeaderHeight + 'px'
                  }
                } else {
                  // 向下移動
                  if (mainHeaderHeight > 60) {
                    nextElement.style.height = mainHeaderHeight + 'px'
                  }
                }
                break
              }
      
              default:
            }
          }
          document.onmouseup = (evt) => {
            document.onmousemove = null
            document.onmouseup = null
            resize.releaseCapture && resize.releaseCapture()
          }
          resize.setCapture && resize.setCapture()
          return false
        }
      }

      return {
        setLayoutDrag
      }
}

公共布局

此處的可以自己查看代碼。

[圖片上傳失敗...(image-be7369-1721834837063)]

invoke調(diào)用rust函數(shù),關(guān)閉splash

import { invoke } from '@tauri-apps/api/tauri'

onMounted(() => {
  // window.addEventListener('contextmenu', (e) => e.preventDefault(), false)
  document.addEventListener('DOMContentLoaded', () => {
    // This will wait for the window to load, but you could
    // run this function on whatever trigger you want
    setTimeout(() => {
      invoke('close_splashscreen')
    }, 1000)
  })
})

??結(jié)語 感興趣的可以試試,有不清楚的問題,關(guān)于tauri開發(fā)方面的問題,也可以一起交流。歡迎加我:zhan_1337608148。一起成長,一起進(jìn)步。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,345評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,494評論 3 416
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,283評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,953評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 71,714評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,186評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,255評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,410評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,940評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,776評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,976評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,518評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,210評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,642評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,878評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,654評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 47,958評論 2 373

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