[iOS] MUI-WebView模式集成到iOS應(yīng)用

前言

最近在研究 嘗試把h5+環(huán)境單頁(yè)面成到iOS端 也就是官方所說(shuō)的WebView集成模式 但是當(dāng)你照著官方文檔 重新開(kāi)一個(gè)新項(xiàng)目 把里面的靜態(tài)庫(kù)和系統(tǒng)庫(kù)一個(gè)一個(gè)的導(dǎo)入進(jìn)去 解決了所有報(bào)錯(cuò)問(wèn)題后 你會(huì)得到一片空白 每當(dāng)我看到官方文檔那不嚴(yán)謹(jǐn)不規(guī)范的集成方式后 都?xì)獾臏喩戆l(fā)抖 所以在這里寫(xiě)一篇文章來(lái)記錄一下 因?yàn)槠赡鼙容^大 我會(huì)從最基本的開(kāi)始說(shuō)起

一.開(kāi)始集成

在開(kāi)始之前需要先準(zhǔn)備一批網(wǎng)址 這里可以下載MUI官方Demo

1.新建一個(gè)工程或使用已有的老工程

步驟省略.

2.在工程中導(dǎo)入MUI基礎(chǔ)靜態(tài)庫(kù)

靜態(tài)庫(kù)請(qǐng)前往官方網(wǎng)站下載iOS端的demo解壓后找到SDK -> Libs在里面耐心搜索找到

liblibUI.a
libcoreSupport.a
liblibPDRCore.a

除了導(dǎo)入這四個(gè)基礎(chǔ)靜態(tài)庫(kù)以外還需要導(dǎo)入一個(gè).bundle文件也就是資源文件 可以在SDK -> Bundles中找到

PandoraApi.bundle

接下來(lái)導(dǎo)入靜態(tài)庫(kù)的頭文件(.h)

SDK -> inc 里面直接拖拽到項(xiàng)目中來(lái)

接下來(lái)導(dǎo)入頁(yè)面資源 在官方demo中的Pandora文件夾以folder的形式引入 這個(gè)就是頁(yè)面資源了 這里為了測(cè)試使用就都引入進(jìn)來(lái)了

之后嘗試cmd+b編譯一下項(xiàng)目發(fā)現(xiàn)并沒(méi)有報(bào)錯(cuò)
之后我們按照官方demo在AppDelegate中初始化5+環(huán)境

#import "PDRCore.h"

[PDRCore initEngineWihtOptions:launchOptions withRunMode:PDRCoreRunModeWebviewClient];

溫馨提示:Xcode10的同學(xué)請(qǐng)?jiān)?code>file -> Project Settings中把編譯系統(tǒng)改成 Legacy Build System否則引入文件的時(shí)候沒(méi)有代碼提示

在編譯一下發(fā)現(xiàn)有密密麻麻的31處錯(cuò)誤

接下來(lái)就是導(dǎo)入系統(tǒng)庫(kù)了 官方文檔中所寫(xiě)的并不準(zhǔn)確 經(jīng)本人測(cè)試這些系統(tǒng)庫(kù)可滿足項(xiàng)目不會(huì)報(bào)錯(cuò)

libc++.tbd
StoreKit.framework
QuickLook.framework
AudioToolbox.framework
CoreTelephony.framework
MobileCoreServices.framework
JavaScriptCore.framework
MediaPlayer.framework
WebKit.framework

這里說(shuō)明一下 因?yàn)?code>Xcode10棄用libstdc++.tbd所以需要使用libc++.tbd代替

3.修改工程配置

1.在Build Phases -> Other Linker Flags 添加 -ObjC 注意O和C需要大寫(xiě)
2.修改 bitcodeNO (否則你打包的時(shí)候會(huì)報(bào)錯(cuò))

4.代碼部分

代碼我們使用官方demo提供的示例 對(duì)應(yīng)工程文件為HBuilder-Integrate 如果沒(méi)有demo的可以在文章最開(kāi)始的地方下載

首先打開(kāi) HBuilder-Integrate 之后注釋掉AppDelegate中的PDRCoreRunModeAppClient所對(duì)應(yīng)的這行代碼或者把該枚舉改成PDRCoreRunModeWebviewClient然后直接運(yùn)行項(xiàng)目

看似正常的東西 我們點(diǎn)點(diǎn)看


經(jīng)測(cè)試除了第四個(gè)功能可以使用 其余均有問(wèn)題 而且沒(méi)有任何錯(cuò)誤提示

有的人會(huì)說(shuō) 只有真機(jī)上有指紋 你真機(jī)測(cè)試一下

好跟著我們的鏡頭一起來(lái)看吧


沒(méi)錯(cuò) 這就是你們看到的真機(jī)運(yùn)行出來(lái)的效果 到這里你一定有幾個(gè)疑問(wèn)
1.為什么模擬器上和真機(jī)跑出來(lái)的效果不一樣(導(dǎo)航欄不見(jiàn)了)
2.為什么官方demo會(huì)提示缺失組件

容我吐槽一句 官方的demo質(zhì)量真是垃圾的一批!!!

問(wèn)題解決方案

好了我們從這里開(kāi)始解決問(wèn)題 首先我們看一下官方代碼

- (void)viewDidLoad
{
    PDRCore*  pCoreHandle = [PDRCore Instance];
    if (pCoreHandle != nil)
    {
        
        NSString* pFilePath = [NSString stringWithFormat:@"file://%@/%@", [NSBundle mainBundle].bundlePath, @"Pandora/apps/HelloH5/www/plugin.html"];
        [pCoreHandle start];
        // 如果路徑中包含中文,或Xcode工程的targets名為中文則需要對(duì)路徑進(jìn)行編碼
        //NSString* pFilePath =  (NSString *)CFURLCreateStringByAddingPercentEscapes( kCFAllocatorDefault, (CFStringRef)pTempString, NULL, NULL,  kCFStringEncodingUTF8 );
        
        // 單頁(yè)面集成時(shí)可以設(shè)置打開(kāi)的頁(yè)面是本地文件或者是網(wǎng)絡(luò)路徑
        // NSString* pFilePath = @"http://www.163.com";
        
        
        // 用戶在集成5+SDK時(shí),需要在5+內(nèi)核初始化時(shí)設(shè)置當(dāng)前的集成方式榛鼎,
        // 請(qǐng)參考AppDelegate.m文件的- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions方法
        
        CGRect StRect = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height);
        
        appFrame = [[PDRCoreAppFrame alloc] initWithName:@"WebViewID1" loadURL:pFilePath frame:StRect];
        if (appFrame) {
            [pCoreHandle.appManager.activeApp.appWindow registerFrame:appFrame];
            [self.view  addSubview:appFrame];
            [appFrame release];
        }
  
    }
}

我們可以看到 先初始化一個(gè)單例PDRCore這個(gè)東西是管理5+環(huán)境的核心組件 然后創(chuàng)建一個(gè)webView 也就是PDRCoreAppFrame 之后添加到self.view上 如果使用arc模式 就是去掉 release retain 關(guān)鍵字就可以了 這里不一一贅述了

基本原理是這樣 我們開(kāi)始解決模擬器和真機(jī)跑出來(lái)效果不同的問(wèn)題 (導(dǎo)航欄會(huì)產(chǎn)生縮進(jìn)問(wèn)題) 這里的解決方案是把導(dǎo)航欄設(shè)置為不透明色

 self.navigationController.navigationBar.translucent = NO;
#import "TestWebViewController.h"
#import "PDRCoreAppFrame.h"
#import "PDRCoreAppManager.h"

@interface TestWebViewController ()
@property (strong, nonatomic) PDRCoreAppFrame *appFrame;
@property (strong, nonatomic) NSString *url;
@end

@implementation TestWebViewController

- (instancetype)initWithTitle:(NSString *)title URL:(NSString *)URL {
    self = [super init];
    if (self) {
        self.navigationItem.title = title;
        self.url = URL;
    }
    return self;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    self.view.backgroundColor = [UIColor whiteColor];
    
    PDRCore *pCoreHandle = [PDRCore Instance];
    if (pCoreHandle) {
        [pCoreHandle start];
        self.appFrame = [[PDRCoreAppFrame alloc] initWithName:@"WebViewID1" loadURL:self.url frame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height - 64)];
        if (self.appFrame) {
            [pCoreHandle.appManager.activeApp.appWindow registerFrame:self.appFrame];
            [pCoreHandle regPluginWithName:@"plugintest" impClassName:@"PGPluginTest" type:PDRExendPluginTypeFrame javaScript:nil];
            [self.view addSubview:self.appFrame];
        }
    }
    NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
    [center addObserver:self selector:@selector(received:) name:@"SendDataToNative" object:nil];
}

- (void)received:(NSNotification *)noti {
    UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"原生界面收到了通知" message:@"" preferredStyle:UIAlertControllerStyleAlert];
    UIAlertAction *determin = [UIAlertAction actionWithTitle:@"確定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {}];
    [alert addAction:determin];
    [self presentViewController:alert animated:YES completion:nil];
}

- (void)dealloc {
    [[PDRCore Instance] setContainerView:nil];
}
@end

注意這里指定的路徑為

NSString *pFilePath = [NSString stringWithFormat:@"file://%@/%@", [NSBundle mainBundle].bundlePath, @"Pandora/apps/HelloH5/www/plugin.html"];

這個(gè)路徑是官方示例中的交互demo 你可以查看該文件中的js代碼來(lái)了解前后端交互需要調(diào)用的一些方法

之后我們開(kāi)始解決官方demo組件丟失的問(wèn)題
上面的提示為plugintest模塊 所以我們就從如何找回這個(gè)模塊開(kāi)始入手 經(jīng)過(guò)一番周折 查到了官方相關(guān)的頁(yè)面
http://ask.dcloud.net.cn/article/67
如果你有耐心可以自己看看 如果沒(méi)有就算了 總之了一句話 使用交互之前需要先注冊(cè)一下 直接上代碼

[pCoreHandle regPluginWithName:@"plugintest" impClassName:@"PGPluginTest" type:PDRExendPluginTypeFrame javaScript:nil];

只有這一行代碼還不夠 還需要引入一個(gè)叫PGPluginTest的自定義類 這里強(qiáng)調(diào)自定義是你用任何一個(gè)新建的類都可以承擔(dān)這個(gè)角色 我們搜索一下官方demo發(fā)現(xiàn)剛好有這個(gè)類 把它放入你的新工程 重新運(yùn)行項(xiàng)目 發(fā)現(xiàn)終于可以交互了!!!

先別急著高興 交互可以使用了 但是NJS發(fā)送消息到原生層在新工程中仍無(wú)法使用

所以我們需要導(dǎo)入相應(yīng)的靜態(tài)庫(kù)

liblibPGInvocation.a

之后我們添加通知測(cè)試一下

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(received:) name:@"SendDataToNative" object:nil];

- (void)received:(NSNotification *)noti {
    NSLog(@"原生界面收到了通知");
}

到這里h5+基本交互功能的web已經(jīng)搭建完成了

下面我會(huì)介紹一下基礎(chǔ)交互的方法

在上文中我已經(jīng)提到了 h5+ 已經(jīng)把交互的方法封裝在了靜態(tài)庫(kù)中 我們使用的時(shí)候需要兩個(gè)步驟

  1. 在代碼中注冊(cè)交互實(shí)例
  2. 在js中調(diào)用交互代碼
  3. 在原生自定義類中實(shí)現(xiàn)交互代碼并處理事件

本文的交互實(shí)例名稱為 plugintest 負(fù)責(zé)交互的類是 PGPluginTest 交互的html頁(yè)面是 plugin.html

接下來(lái)我們打開(kāi)plugin.html來(lái)查看具體的交互方法 我們可以查看到這樣一段代碼

plus.plugintest.PluginTestFunction

plus為5+環(huán)境的實(shí)例
plugintest為我們注冊(cè)的交互實(shí)例
PluginTestFunction則是實(shí)例調(diào)用的方法

與此同時(shí)在 PGPluginTest 中存在一個(gè)叫 PluginTestFunction 的方法

- (void)PluginTestFunction:(PGMethod *)commands

當(dāng)js中調(diào)用該方法的同時(shí) 原生類中的方法也隨著執(zhí)行 你可以在這個(gè)原生類中做一些自定義操作 這就是所謂的js與原生交互

同樣的交互過(guò)程中需要傳遞一下參數(shù) 我們把上文中的js補(bǔ)全一下 傳遞一些參數(shù)

var a = {
    "name": "第四個(gè)參數(shù) - 名字",
    "age": "第四個(gè)參數(shù) - 年齡"
}

plus.plugintest.PluginTestFunction("第一個(gè)參數(shù)", "第二個(gè)參數(shù)", "第三個(gè)參數(shù)", a, function (result) {
    alert(result[0] + "\n" + result[1] + "\n" + result[2] + "\n" + result[3].name + "\n" + result[3].age);
}, function (result) {
    alert(result)
});

這里需要說(shuō)一下 官方這種交互方式支持用戶自己傳遞4個(gè)參數(shù) 超出數(shù)量的參數(shù)會(huì)被舍棄 所以如果參數(shù)個(gè)數(shù)超過(guò)4個(gè)則可以使用對(duì)象的方式傳遞(例如代碼中定義的a) 這樣不僅可以節(jié)省參數(shù)空間 而且方便

我們可以看到方法中有兩個(gè)function這兩個(gè)均為異步回調(diào) 其中第一個(gè)function表示成功后的回調(diào) 第二個(gè)function表示失敗后的回調(diào)

我們?cè)倩氐皆?code>PGPluginTest的代碼中查看一下響應(yīng)方法

- (void)PluginTestFunction:(PGMethod *)commands {
    if (commands) {
        // 異步方法的回調(diào)id,H5+ 會(huì)根據(jù)回調(diào)ID通知JS層運(yùn)行結(jié)果成功或者失敗
        NSString *cbId = [commands.arguments objectAtIndex:0];

        /**
         用戶的參數(shù)會(huì)在第二個(gè)參數(shù)開(kāi)始傳回
         這里說(shuō)一下 通過(guò)觀察控制臺(tái)可以發(fā)現(xiàn) 返回的arguments實(shí)際上是一個(gè)數(shù)組 無(wú)論你是否傳值 都只有五個(gè)參數(shù)
         第一個(gè)參數(shù)為對(duì)調(diào)id是自動(dòng)生成的
         所以用戶可以控制的參數(shù)為實(shí)際上為4個(gè) 無(wú)論是否傳值 均存在 若不傳值 默認(rèn)為 NSNull
         */
        NSString *pArgument1 = [commands.arguments objectAtIndex:1];
        NSString *pArgument2 = [commands.arguments objectAtIndex:2];
        NSString *pArgument3 = [commands.arguments objectAtIndex:3];
        NSDictionary *pArgument4 = [commands.arguments objectAtIndex:4];
        
        // 如果使用Array方式傳遞參數(shù)
        NSArray *pResultArray = [NSArray arrayWithObjects:pArgument1, pArgument2, pArgument3, pArgument4, nil];

        PDRPluginResult *result = [PDRPluginResult resultWithStatus:PDRCommandStatusOK messageAsArray: pResultArray];
        
        // 通知JS層Native層運(yùn)行結(jié)果
        [self toCallback:cbId withReslut:[result toJSONString]];
    }
}

我們可以看到與js中相對(duì)應(yīng)的方法中接收參數(shù)只有一個(gè)commands 在這個(gè)對(duì)象中 我們可以獲取到傳遞參數(shù)的arguments

我們來(lái)看一下接收參數(shù)時(shí)的具體表現(xiàn)形式

可以看到arguments其實(shí)是一個(gè)數(shù)組 空間為6 我在里面?zhèn)鬟f的四個(gè)參數(shù)分別在它的 1 2 3 4 索引處 索引0所在的參數(shù)實(shí)際上是一個(gè)回調(diào)id 通過(guò)這個(gè)id可以回調(diào)到匿名的function中 索引5 指向一個(gè)NSNull對(duì)象 也就是說(shuō)我們最多只能傳遞4個(gè)參數(shù) 如果再加一個(gè)參數(shù)是不會(huì)出現(xiàn)任何效果的
PDRCommandStatusOK是代表成功的枚舉
toCallback: withReslut:就是回調(diào)方法 傳遞一個(gè)id和需要傳遞的內(nèi)容就可以回調(diào)給js頁(yè)面 withReslut參數(shù)是一個(gè)json類型的字符串 到j(luò)s頁(yè)面后會(huì)自動(dòng)轉(zhuǎn)化成js中的對(duì)象

溫馨提示:在實(shí)際開(kāi)發(fā)中可能并不需要那么多的參數(shù) 所以請(qǐng)酌情使用

PDRPluginResult *result = [PDRPluginResult resultWithStatus:PDRCommandStatusOK messageAsArray: pResultArray];
 [self toCallback:cbId withReslut:[result toJSONString]];

這兩句話代碼是回調(diào)一個(gè)數(shù)組 同樣的你想回調(diào)一個(gè)字典對(duì)象也是可以的 如此即可 在另一面接收的result.key就可以接收到傳遞的值了

PDRPluginResult *result = [PDRPluginResult resultWithStatus:PDRCommandStatusOK messageAsDictionary:@{@"key": @"value"}];

到此js交互這一部分內(nèi)容完結(jié) 不再一一贅述

二.功能拓展

經(jīng)過(guò)上邊的實(shí)踐 我們已經(jīng)可以調(diào)用最基本的交互了 h5+平臺(tái)最大的特色就是可以調(diào)用封裝好的原生交互 比如相機(jī) 二維碼掃描 系統(tǒng)相冊(cè) 錄音播放 調(diào)用原生界面 等 在今后的時(shí)間里我會(huì)一一列舉這些功能和導(dǎo)入的方式

因此學(xué)會(huì)功能拓展是非常重要的 功能拓展的思路就是
1.查看官方demo尋找需要的功能
2.導(dǎo)入功能所需要的靜態(tài)庫(kù)(需要耐心尋找)
3.使用官方實(shí)例html進(jìn)行調(diào)試即可

下面我會(huì)挑選幾個(gè)功能 列舉一下 如何使用

1.照相/錄像

這個(gè)模塊需要我們導(dǎo)入

liblibCamera.a

并導(dǎo)入系統(tǒng)動(dòng)態(tài)庫(kù)

Photos.framework
CoreMedia.framework

然后在Info.plist中開(kāi)啟拍照和麥克風(fēng)權(quán)限

Privacy - Camera Usage Description
Privacy - Microphone Usage Description

然后指定路徑為

[NSString stringWithFormat:@"file://%@/%@", path, @"Pandora/apps/HelloH5/www/plus/camera.html"]

運(yùn)行之后發(fā)現(xiàn)提示file模塊缺失 不要慌 導(dǎo)入下面靜態(tài)庫(kù)即可

liblibIO.a

運(yùn)行之后發(fā)現(xiàn)拍照和錄像都正常 但是照片和錄像均無(wú)法播放 所以如果想實(shí)現(xiàn)在網(wǎng)頁(yè)上播放的效果 需要自己實(shí)現(xiàn)點(diǎn)擊方法

三.個(gè)人demo

個(gè)人demo未成品 只包含基礎(chǔ)功能 持續(xù)更新中...
https://github.com/objcat/MUI-WebView-Demo

finally enjoy it.

write by objcat

2018.10.24

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末瓦堵,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子歌亲,更是在濱河造成了極大的恐慌,老刑警劉巖澜驮,帶你破解...
    沈念sama閱讀 218,122評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件陷揪,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡杂穷,警方通過(guò)查閱死者的電腦和手機(jī)悍缠,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)耐量,“玉大人飞蚓,你說(shuō)我怎么就攤上這事±妊眩” “怎么了趴拧?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,491評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)山叮。 經(jīng)常有香客問(wèn)我著榴,道長(zhǎng),這世上最難降的妖魔是什么屁倔? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,636評(píng)論 1 293
  • 正文 為了忘掉前任脑又,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘问麸。我一直安慰自己往衷,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,676評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布严卖。 她就那樣靜靜地躺著炼绘,像睡著了一般。 火紅的嫁衣襯著肌膚如雪妄田。 梳的紋絲不亂的頭發(fā)上俺亮,一...
    開(kāi)封第一講書(shū)人閱讀 51,541評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音疟呐,去河邊找鬼脚曾。 笑死,一個(gè)胖子當(dāng)著我的面吹牛启具,可吹牛的內(nèi)容都是我干的本讥。 我是一名探鬼主播,決...
    沈念sama閱讀 40,292評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼鲁冯,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼拷沸!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起薯演,我...
    開(kāi)封第一講書(shū)人閱讀 39,211評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤撞芍,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后跨扮,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體序无,經(jīng)...
    沈念sama閱讀 45,655評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,846評(píng)論 3 336
  • 正文 我和宋清朗相戀三年衡创,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了帝嗡。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,965評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡璃氢,死狀恐怖哟玷,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情一也,我是刑警寧澤巢寡,帶...
    沈念sama閱讀 35,684評(píng)論 5 347
  • 正文 年R本政府宣布,位于F島的核電站塘秦,受9級(jí)特大地震影響讼渊,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜尊剔,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,295評(píng)論 3 329
  • 文/蒙蒙 一爪幻、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦挨稿、人聲如沸仇轻。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,894評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)篷店。三九已至,卻和暖如春臭家,著一層夾襖步出監(jiān)牢的瞬間疲陕,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,012評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工钉赁, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蹄殃,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,126評(píng)論 3 370
  • 正文 我出身青樓你踩,卻偏偏與公主長(zhǎng)得像诅岩,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子带膜,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,914評(píng)論 2 355

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