taro

大纲
复习在react中使用mobx
在taro中如何使用echarts
在taro中如何使用 高德地图微信小程序api
where git
ssh keys
同一台电脑生成两个git账号,gitLab和gitHub账号分开
.gitignore规则
如何查看npm 全局安装的包
JSON.stringify()第二三个参数
vscode中的 => jsconfig.json 和 tsconfig.json
targetWindow.postMessage发送 // window.addEventListener('message')接收

1. 复习在react中使用mobx

1. 依赖安装
- @babel/plugin-proposal-decorators   装饰器插件,proposal是提案的意思
- mobx
- mobx-react



2. 装饰器插件如何配置
- 可以在.babelrc中配置,还可以在package.json中配置
- 在package.json中配置babel配置项
 "babel": {
    "presets": [
      "react-app"
    ],
    "plugins": [ 
      // plugin是一个数组
      // 具体的每一个插件也可以配成一个数组
      ["@babel/plugin-proposal-decorators", {
        "legacy": true
      }]
    ]
  },



3. 
- 如何建立全局store,以及module的概念
- 如何在组件中注入module,注入后就能在组件中使用module中的action,computed,observable等

// ----------------------------------------------------------------- store/home.js 【就是一个module】
import { observable, action, computed } from 'mobx'
class Home {
    @observable name = 'wang' // 可观测的数据
    @action changeName = (name) => { // action
        this.name = name 
    }
    @computed get getName() { // 计算属性
        return this.name + this.name + 3
    }
}
const home = new Home()
export default home // 实例化后导出

// ----------------------------------------------------------------- store/index.js
import home from './modules/home' // 一个module
const store = {
    home, // 这里导入每一个module到store,导出
}
export default store

// ----------------------------------------------------------------- 入口文件,用mobx-react中的Provider实现context
import { Provider } from 'mobx-react'
import store from './store/index'
ReactDOM.render(
    <Provider {...store} >
        <App />
    </Provider>        
    , document.getElementById('root'));

// ----------------------------------------------------------------- 在组件中注入module
import React from 'react';
import { observable } from 'mobx'
import { observer, inject } from 'mobx-react'


@inject('home') // 注入home这个moduel
@observer // 用mobx-react把组件包装成观察者
class Home extends React.Component {
    @observable count = 0
    changeCount() {
      console.log(1111111111111)
      this.count = this.count + 1
      this.props.home.changeName('zhang')
    }
    componentDidMount() {
      console.log(this.count)
      console.log(this.props.home.name, 'home') // 通过this.props.home获取module中的 name 属性
      console.log(this.props.home.getName, 'computed') // 获取action

    }
    render() {
        return (
            <div className="App">
              <div onClick={this.changeCount.bind(this)}>count++</div>
              <div>{this.count}</div>
              <div>{this.props.home.name}</div>
            </div>
          );
    }
}
export default Home;

全局store https://www.jianshu.com/p/0b500e044911
不同的modules https://blog.csdn.net/qq_40816649/article/details/100656796

2. taro中如何使用 echarts

1. 下载 echarts-for-weixin 项目到本地
2. 复制里面的 ec-canvas 文件夹到你的项目
3. 在你的page中做如下配置
import Taro, { Component } from '@tarojs/taro'
import { View, Button, Text } from '@tarojs/components'
import * as echarts from '../../ec-canvas/echarts.js' // 引入echarts源码
import './echarts.less'

class Echarts extends Component {
  config = {
    navigationBarTitleText: '首页',
    component: true, // 使用自定义组件
    usingComponents: { // 使用自定义组件
        'ec-canvas': '../../ec-canvas/ec-canvas'
    }
  }
  state = {
    ec: { // 设置ec属性,提供<ec-canvas/>的ec属性消费
        lazyLoad: true
    }
  }
  componentDidMount() {
    this.initChart()
  }
  initChart = () => {
    // 获取ec-canvas的dom 
    // init方法获取回调的各个参数
    this.chartRef.init((canvas, width, height) => {
        console.log(canvas, width, height)
        const chart = echarts.init(canvas, null, { // 初始化echarts实例
            width: width,
            height: height
          })
          // canvas.setChart(chart)
          var option = {.....echarts配置项}
          chart.setOption(option) // 实例配置项
          return chart
    })
  }
  componentWillReceiveProps (nextProps) {
    console.log(this.props, nextProps)
  }
  setChartRef = node => this.chartRef = node    // ref设置
  render () {
    return (
      <View className='echarts'>
        <View><Text>echarts</Text></View>
        <ec-canvas 
            id="canvas"
            canvas-id="canvas_id" 
            ec={this.state.ec}
            ref={this.setChartRef}
        ></ec-canvas>
      </View>
    )
  }
}e'ce'c
export default Echarts


4. echart的一些基本配置
var option = {
        tooltip: {// 提示框,黑色模态框
          show: false, // 是否显示
          trigger: 'item', // 触发类型item axis none,item是饼图,散点图中显示
          formatter: "{a} <br/>{b}: {c} ({d}%)"
        },
        legend: { // 底部的图示说明
          orient: 'horizontal',
          // align: 'top',
          x: '26%',
          y: '80%',
          padding: [20, 0, 0, 0],
          data:['闭眼','急刹车','遮挡物'],
          itemWidth: 18,
          itemHeight: 18,
          selected: { // 图列的选中情况
            '闭眼': true,
            '急刹车': true,
            '遮挡物': true
          }
        },
        title:{
          text: `${Math.floor(Math.random() * 300)}`, // 主标题
          textStyle: { // 主标题样式
            color: '#666666',
            width: 100,
            height: 100,
            fontWeight: 500,
            fontSize: 30,
          },
          subtext: '风险预警',  // 副标题
          subtextStyle: { // 副标题样式
            color: '#989898',
          },     
          itemGap: 6, // 主副标题之间的距离
          textAlign: 'center', // 文字对齐      
          left:"48%", // 整个文字的位置
          top:"42%",
          z: 999,
          // zlevel: 3, // 层级
        },
        series: [
          {
            name:'访问来源', // 用于tooltip时的title等
            type:'pie', // 饼图
            radius: ['40%', '52%'], 
            avoidLabelOverlap: false,
            hoverAnimation: false, // hover时的放大动画是否显示
            label: {
              align: 'left', 
              normal: {
                show: false,
                position: 'outside',
                formatter: '{b}: {c}'
                // formatter: function() {
                //   return '  200 \r\r\n 风险预警'
                // },
                // textStyle: {
                //   fontSize: 20,
                //   color: '#555555'
                // }
              },
              emphasis: { // 点击时候出现的详细内容
                show: true,
                textStyle: {
                  fontSize: '14',
                  fontWeight: 'bold'
                }
              },
            },
            labelLine: { // 连接线
              normal: {
                show: false,
                length: 2, // 第一段链接线的长度
                length2: 2, // 第二段链接线的长度
              }
            },
            data:[
              {value:700, name:'闭眼'},
              {value:200, name:'急刹车'},
              {value:100, name:'遮挡物'},
            ],
            color: [
              '#eecb5f',
              '#e3935d',
              '#e16757'
            ]
          }
        ]
      }

echarts-for-weixin https://github.com/ecomfe/echarts-for-weixin

3. 在taro中如何使用 高德地图微信小程序api

  • 注意:在微信小程序中
  • 注意:要真机预览地图中能定位的话,需要在公众平台中设置 request
  • 在 "设置"->"开发设置" 中设置 request 合法域名,将 https://restapi.amap.com 中添加进去
  • 注意:获取位置信息时,高德给的地理位置小程序的show-location无法显示,需要用微信的wx.getLocation获取数据,在taro中使用Taro.getLocation
  • 注意:微信小程序不能导航,因为小程序代码最多是1M,运行内存最多10M
  • 注意:cover-view做阴影
(1) 使用高德地图微信小程序api获取数据
1. 获取appkey
2. 下载小程序 sdk
3. 设置安全通信域名  //  "设置"->"开发设置" 中设置 request 合法域名,将 https://restapi.amap.com 中添加进去
4. 引入amap-wx.js   //  import AmapFile from '@/lib/amap-wx.js'
5. 在componentDidMount()中实例化  // new AmapFile.AMapWX({key})
6. 获取实例上的各种方法获取数据   // myAmapFun.getRegeo(success: res => .....)


(2) 获取位置信息
wx.getLocation   // Taro.getLocation({success: res => ...})然后即可赋值给Map组件的longitude,latitude


(3) 报错 authorize:fail auth deny
- 如果用户拒绝授权后,短期内调用不会出现弹窗,而是直接进入 fail 回调。
- 如果是开发环境,请点击开发工具左侧 缓存-清除授权数据;编译再授权
- 如果是手机,请进入小程序后点击右上菜单-关于xx-右上角菜单-设置中进行权限的手动设置,或删除小程序后重新添加。



(4) 打开地图显示位置
wx.chooseLocation(Object object)

高德for微信api教程: https://lbs.amap.com/api/wx/gettingstarted
show-location显示位置信息: https://blog.csdn.net/weixin_42123532/article/details/82968123
微信小程序如何获取地理位置和进行地图导:https://www.jianshu.com/p/dbe555ad1d8c
打开地图显示位置:https://developers.weixin.qq.com/miniprogram/dev/api/location/wx.chooseLocation.html
cover-view如何做阴影:(coverImage代替)https://juejin.im/post/5b1a104a5188257d9f24c7f9#heading-7

3.1 taro中的动画

import Taro, { Component } from '@tarojs/taro'
import { View, Picker, Text, ScrollView } from '@tarojs/components'
import { AtIcon } from 'taro-ui'
import * as echarts from '../../ec-canvas/echarts.js'
import RiskListDialog from './risk-list-dialog/index'

import './riskwaring.scss'

class RiskWaring extends Component {
  state = {
    screenHeight: 0,
    cardTop: 420,
    animationData: {}
  }
  getDate = () => {
    console.log(this.pickDom)
  }

  setRefPicker = (dom) => this.pickDom = dom;
  setBarRef = (dom) => this.barDom = dom;

  touchStart = (e) => {
    const { pageY } = e.changedTouches[0]
    this.setState({
      startPageY: pageY
    })
  }
  onTouchEnd = (e) => {
    // endPargeY 表示触摸结束的y坐标
    // offsetTop 偏移的距离(距离顶部的值)
    const { changedTouches: [{ pageY: endPargeY}], currentTarget: { offsetTop }} = e
    const { startPageY } = this.state
    let animation = Taro.createAnimation({
      duration: 200,
      timingFunction: "ease-in-out",
    })
    endPargeY < startPageY 
      ? animation.translateY(-offsetTop).step() 
      : animation.translateY(0).step()
    this.setState({
      animationData: animation.export()
    })
  }

  render() {
    const { animationData } = this.state
    const styleCard = {
      width: '100%', 
      height: `${screenHeight}px`, 
      background: 'white',
      // position: 'absolute',
      // top: `${cardTop}px`,
      // zIndex: 9999,
      // bacground: 'red'
    }
    const scrollViewStyle = {
      height: `${screenHeight}px`, 
    }
 
    return (
      <View 
        className='risk'
        style={scrollViewStyle}
      >
        <CoverView  
          className="dialogCard" 
          style={styleCard} 
          ref="cardDialog"
          animation={animationData}
          onTouchStart={e => this.touchStart(e)}
          onTouchEnd={e => this.onTouchEnd(e)}
        >
          <RiskListDialog />
        </CoverView>
      </View>
    )
  }
}
export default RiskWaring

4. where git

where git
- where git 命令能够显示 git 的安装路径

5. ssh keys

1. 如何查看是否存在 ssh-keys
- 在c盘用户的主目录下看是否存在 .ssh 文件夹
- 存在.ssh文件夹后,看里面是否存在 (id_rsa) 和 (id_rsa.pub) 文件 
- // id_rsa.pub 就是github或者gitlab需要的ssh-keys
- // 请确保可以浏览隐藏文件
- // 如:C:\Users\Administrator.PC-20191119DSKG\.ssh



2. 如何没有,使用在cmd中输入以下命令
- $ssh-keygen -t rsa -C "youremail@example.com"
- $ssh-keygen -t rsa -C "youremail@example.com" -f ~/.ssh/filename 生成多个ssh-key
// ( !!!!不加-f filename就会覆盖 )
-t 指定密钥类型,默认是 rsa ,可以省略。 // type 类型
-C 设置注释文字,比如邮箱。// commont 注释
-f 指定密钥文件存储文件名。


3. 如果在cmd中输入 ssh-keygen 不是系统内部命令,则需要配置环境变量
- 需要把ssh-keygen所在的文件路径添加到环境变量的 Path 中
- 可以使用 where git 查看git的安装路径
- // C:\Program Files\Git\usr\bin       默认路径

https://www.jianshu.com/p/6dbcbe9fd02e

6. 同一台电脑生成两个git账号,gitLab和gitHub账号分开

同一台电脑生成两个git账号,gitLab和gitHub账号分开


1. 设置golbal和local的user.name和user.email
github:
  git config --global user.name '...' && git config --global user.email '...' 
gitlab:
  git config --local user.name '...' && git config --local user.email '...' 


2. 生成ssh key
github
  ssh-keygen -t rsa -C '...'
gitlab
  ssh-keygen -t rsa -f C:/Users/Administrator.PC-20191119DSKG/.ssh/id_rsa.gitlab -C 'xia.wu@ideacome.com'."

7 .gitignore规则

# 规则说明
# #号:表示git会忽略该行的所有内容,即不生效,相当于注释
#  /:  结尾的斜杠表示 忽略的是 ( 目录 )
#  !:  表示 ( 不忽略 )

# 其他的和正则差不多
#  "*":  星号匹配零个或多个任意字符
#  []:   匹配任何一个列在方括号中的字符,如[ab]匹配a或者匹配b
#  "?":  问号匹配零个或者一个任意字符
#  [n-m]:匹配所有在这两个字符范围内的字符,如[0-9]表示匹配所有0到9的数字

https://www.jianshu.com/p/1c74f84e56b4

8. 如何查看npm 全局安装的包

  • 全局安装的包
  • 本地/远程包版本号
npm list -g --depth 0   查看本地全局安装过的包
npm uninstall -g xxx   卸载本地全局安装包
npm outdated -g --depth=0  查看需要更新的全局包

npm view xxxx versions   查看远程所有版本号
npm view xxxx version     查看远程最新的版本号
npm info xxxx version    查看远程所有版本号
// info是最全面的   有三种命令
// npm info xxxx
// npm info xxxx version
// npm info xxxx versions


npm ls xxxx 查看本地安装的版本号
npm ls xxxx -g 查看全局安装的包的版本号

9. JSON.stringify()第二三个参数

(1) 基本用法
- 如果参数是一个字符串,为了让JSON.parse()得到一个字符串,所以JSON.stringify()会在字符串外面再加一层双引号
- 参数是对象,对象的 ( 属性值 ) 是 ( undefined, function, xml ) 都会被JSON.stringify()过滤
- 参数是数组,数组成员是 ( undefined, function, xml )会被JSON.stringify()转成 ( null )
- JSON.stringify()会忽略不可遍历的属性,即通过 enumerable 设置的对象
JSON.stringify('abc')  // ""abc""
JSON.stringify([1, "false", false])  // '[1,"false",false]'
var obj = {    // "{}"
  a: undefined,
  b: function () {}
}; 
var arr = [undefined, function () {}];  // "[null,null]"


(2) 第二个参数
- JSON.stringify() 的第二个参数可以是 ( 数组或函数 )
- 数组:指对象参数中,需要转成字符串的key值,相当于数组白名单,注意只对对象参数有效
- 函数:用来更改JSON.stringify的返回值

- 函数:用来更改JSON.stringify的返回值
- 注意递归的是所有的key,如果参数对象嵌套,每一个层都会遍历
- 如果处理函数返回的是undefine或者没有返回值,则该值会被忽略
function f(key, value) { // 第一个参数是参数对象的key值,第二个是value值
  if (typeof value === "number") {
    value = 2 * value;
  }
  return value; // 需要返回value值
}
JSON.stringify({ a: 1, b: 2 }, f)
// '{"a": 2,"b": 4}'


(3) 第三个参数:增加可读性
-JSON.stringify还可以接受第三个参数,用于增加返回的 JSON 字符串的可读性。
- 如果是数字,表示每个属性前面添加的空格(最多不超过10个);
- 如果是字符串(不超过10个字符),则该字符串会添加在每行前面。




!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 第二个参数的应用  !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
const a = {
  add: function() {}, // 函数会直接被忽略掉,那么怎么使函数能不被忽略掉??????
  arr: [undefined, 1], // 数组中的undefined会被转成null
  obj: { name: 'woow-wu', age: undefined}, // 对象中的undefined会被直接忽略掉
}
const string = JSON.stringify(a)
console.log(string);
以上代码转化的结果是: {"arr":[null,1],"obj":{"name":"woow-wu"}}
---
注意:函数被忽略掉了,如何不让JSON.stringify忽略掉函数?用第二个参数可以做到
const string = JSON.stringify(a, function(key, value) {
  if (typeof value === 'function') {
    return `${value}` // 将函数转成一个字符串,可以用eval去执行
  }
  return value
})
结果:{"add":"function() {}","arr":[null,1],"obj":{"name":"woow-wu"}}

(10) vscode中的 => jsconfig.json 和 tsconfig.json

[ vscode中 ]

(1) jsconfig.json
- 目录中存在jsconfig.json文件表示该目录是JavaScript项目的根目录。
- jsconfig.json文件指定根文件和JavaScript语言服务提供的功能选项。
- jsconfig.json源于tsconfig.json,是TypeScript的配置文件。jsconfig.json相当于tsconfig.json的“allowJs”属性设置为true。


(2) jsconfig.json的作用:
  - JavaScript项目是通过jsconfig.json文件定义的。 目录中存在此类文件表示该目录是JavaScript项目的根目录。 
  - 文件本身可以选择列出属于项目的文件,要从项目中排除的文件,以及编译器选项

(3)
- exclude
- include
- Using webpack aliases: 需要指定global模式的 paths

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": { // 使 IntelliSense 使用 webpack的别名
      "ClientApp/*": ["./ClientApp/*"]
    }
  }
// import Something from 'ClientApp/foo'
// 或 import Something from '@/foo'

https://segmentfault.com/a/1190000018013282?utm_source=tag-newest

postMessage

  • 可以实现跨域信息交互
  • 两个web浏览器标签页之间的通信,IFrame通信等
  • otherWindow.postMessage(message, targetOrigin, [transfer]);
  • 注意:在targetWindow.open()后,要等到目标页面加载完成后才能进行 postMessage 跨域通信,但是在跨域的情况下,无法对目标窗口进行onload监听,所以能用setTimeou延时。对于iframe同理
otherWindow.postMessage(message, targetOrigin, [transfer]);

(1) otherWindow
- otherWindow:其他窗口的一个引用
- 1. iframe 的 contentWindow 属性
- 2. window.open() 返回的窗口对象
- 3. 命名过或数值索引的 window.frames
- 4. 两个窗口之间,a -> b, otherWindow是b窗口; b -> a,otherWindow是a窗口,即 ( top ) 或者 ( parent )。
(2) message
- message:简要发送给其他窗口的数据
- message会被序列化,所以无需自己序列化
! (3) targetOrigin // 一个字符串,可以是 '*'表示无限制
- targetOrigin:通过窗口的origin属性来指定哪些窗口能接收到消息事件
- 在发送消息的时候,如果目标窗口的协议域名端口任意一项不满足 targetOrigin 提供的值,消息就不会发送
- 三者全部匹配才会发送
(4) transfer 
- transfer 是一串和message同时传递的 Transferable 对象. 
- 这些对象的所有权将被转移给消息的接收方,而发送一方将不再保有所有权。




---------------------------------------------
发消息: otherWindow.postMessage(message, targetOrigin, [transfer]);
收消息: window.addEventListener("message", (data) => receiveMessage(data), false);
- 在接收端的监听函数的参数中,注意:orgin 和 source
- origin: 发送方的协议域名端口组成的字符串
- source: 发送方窗口对象的引用
window.addEventListener("message", receiveMessage, false);
function receiveMessage(event)
{
  // For Chrome, the origin property is in the event.originalEvent
  // object. 
  // 这里不准确,chrome没有这个属性
  // var origin = event.origin || event.originalEvent.origin; 
  var origin = event.origin
  if (origin !== "http://example.org:8080")
    return;

  // ...
}



---------------------------------------------
安全问题:
- 当您使用postMessage将数据发送到其他窗口时,始终指定精确的目标origin,而不是*
- 使用origin和source属性验证发件人的身份



---------------------------------------------
代码:
- a页面
function App() {
  var bb = null
  return (
    <div>
      <div>app</div>
      <Home />
      <Profile />
      <div onClick={() => { bb = window.open('http://localhost:3000')}}>a - 打开b的tab</div>
      <div onClick={() => { bb.postMessage('a给b的消息', '*') }}>a - 发送消息给b</div>
    </div>
  )
}

- b页面
  componentDidMount() {
    window.addEventListener('message', (res) => {
      console.log(res) // 注意res中的 origin 和 source
    })
  }
QQ截图20191207225540.jpg

https://developer.mozilla.org/zh-CN/docs/Web/API/Window/postMessage

https://juejin.im/entry/57d7c8005bbb50005bd0de1e

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

推荐阅读更多精彩内容