React-native 钉钉登录

很久没写了 我们来改个钉钉登录吧
网上文章很少
目前测试已经跑通了
服务端还没对接
大家自己看吧

iOS:

钉钉官网
https://open.dingtalk.com/document/orgapp/procedures-for-authorized-logon-to-ios-applications

image.png

其他的按照步骤走就行了

image.png

这东西一定要导入到工程 官方Demo 里是有的
你也可以通过pod 下载
下面是具体代码:

//appdelegate.m
#import <DTShareKit/DTOpenKit.h>
#import "RNDingTalkShareModule.h"//这个文件是自己的 我后面会贴出来

//在下面方法区添加
//钉钉登录配置

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
//...
   [DTOpenAPI registerApp:@"这里是你钉钉的key"];
//...
}

- (BOOL)application:(UIApplication *)application
            openURL:(nonnull NSURL *)url
  sourceApplication:(nullable NSString *)sourceApplication
         annotation:(nonnull id)annotation {
  if ([DTOpenAPI handleOpenURL:url delegate:self]) {
        return YES;
    }
    return NO;
}

- (void)onResp:(DTBaseResp *)resp {
  RCTPromiseResolveBlock resolver = [RNDingTalkShareModule getSendLoginResolverStatic];
    RCTPromiseRejectBlock rejecter = [RNDingTalkShareModule getSendLoginRejecterStatic];
    if (resp.errorCode == DTOpenAPISuccess) {
        DTAuthorizeResp *authResp = (DTAuthorizeResp *)resp;
      //WeChat_Resp
    
        NSString *accessCode = authResp.accessCode;
      resolver([NSDictionary dictionaryWithObjectsAndKeys:accessCode,@"code", nil]);
    } else {
      
    }
}


下面文件创建好后添加到你的项目
这个是和 RN通讯的文件

RNDingTalkShareModule.h

#if __has_include("RCTBridge.h")
#import "RCTBridge.h"
#else
#import <React/RCTBridge.h>
#endif

#if __has_include("DTOpenKit.h")
#import "DTOpenKit.h"
#else
#import <DTShareKit/DTOpenKit.h>
#endif

@interface RNDingTalkShareModule : NSObject <RCTBridgeModule, DTOpenAPIDelegate>
+ (RCTPromiseResolveBlock)getSendLoginResolverStatic;

+ (RCTPromiseRejectBlock) getSendLoginRejecterStatic;

@end

//RNDingTalkShareModule.m


#import <React/RCTEventDispatcher.h>
#import <React/RCTBridge.h>
#import "RNDingTalkShareModule.h"

// 登录
static RCTPromiseResolveBlock sendLoginResolverStatic = nil;

static RCTPromiseRejectBlock sendLoginRejecterStatic = nil;

static NSString *const DING_TALK_NOT_INSTALLED_CODE = @"NOT_INSTALLED";
static NSString *const DING_TALK_NOT_SUPPORTED_CODE = @"NOT_SUPPORTED";
static NSString *const DING_TALK_SHARE_FAILED_CODE = @"SHARE_FAILED";

@interface RNDingTalkShareModule ()

@property (nonatomic, strong) NSString *appId;
@property (nonatomic, copy) RCTPromiseResolveBlock resolveBlock;
@property (nonatomic, copy) RCTPromiseRejectBlock rejectBlock;

@end

@implementation RNDingTalkShareModule

RCT_EXPORT_MODULE()

- (dispatch_queue_t)methodQueue {
    return dispatch_get_main_queue();
}

+ (RCTPromiseResolveBlock)getSendLoginResolverStatic {
    return sendLoginResolverStatic;
}

+ (RCTPromiseRejectBlock) getSendLoginRejecterStatic {
    return sendLoginRejecterStatic;
}

- (instancetype)init {
    self = [super init];
    if (self) {
        [self initDingTalkShare];
        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(handleOpenURLNotification:)
                                                     name:@"RCTOpenURLNotification"
                                                   object:nil];
    }
    return self;
}

RCT_EXPORT_METHOD(isInstalled
                  : (RCTPromiseResolveBlock)resolve
                  : (RCTPromiseRejectBlock)reject) {
    resolve(@([DTOpenAPI isDingTalkInstalled]));
}

RCT_EXPORT_METHOD(isSupported
                  : (RCTPromiseResolveBlock)resolve
                  : (RCTPromiseRejectBlock)reject) {
    resolve(@([DTOpenAPI isDingTalkSupportOpenAPI]));
}

RCT_EXPORT_METHOD(registerApp
                  : (NSString *)appId
                  : (NSString *)appDescription
                  : (RCTPromiseResolveBlock)resolve
                  : (RCTPromiseRejectBlock)reject) {
    BOOL result;
    if (appDescription) {
        result = [DTOpenAPI registerApp:appId appDescription:appDescription];
    } else {
        result = [DTOpenAPI registerApp:appId];
    }
    resolve(@(result));
}

RCT_EXPORT_METHOD(shareImage
                  : (NSString *)image
                  : (RCTPromiseResolveBlock)resolve
                  : (RCTPromiseRejectBlock)reject) {
    self.resolveBlock = resolve;
    self.rejectBlock = reject;
    if (![self checkSupport]) {
        return;
    }
    DTSendMessageToDingTalkReq *sendMessageReq = [[DTSendMessageToDingTalkReq alloc] init];
    DTMediaMessage *mediaMessage = [[DTMediaMessage alloc] init];
    DTMediaImageObject *imageObject = [[DTMediaImageObject alloc] init];
    imageObject.imageURL = image;
    mediaMessage.mediaObject = imageObject;
    sendMessageReq.message = mediaMessage;

    if (![DTOpenAPI sendReq:sendMessageReq]) {
        reject(DING_TALK_SHARE_FAILED_CODE, @"分享失败", nil);
    }
}

RCT_EXPORT_METHOD(shareWebPage
                  : (NSString *)url
                  : (NSString *)thumbImage
                  : (NSString *)title
                  : (NSString *)content
                  : (RCTPromiseResolveBlock)resolve
                  : (RCTPromiseRejectBlock)reject) {
    self.resolveBlock = resolve;
    self.rejectBlock = reject;
    if (![self checkSupport]) {
        return;
    }
    DTSendMessageToDingTalkReq *sendMessageReq = [[DTSendMessageToDingTalkReq alloc] init];
    DTMediaMessage *mediaMessage = [[DTMediaMessage alloc] init];
    DTMediaWebObject *webObject = [[DTMediaWebObject alloc] init];
    webObject.pageURL = url;
    mediaMessage.title = title;
    mediaMessage.thumbURL = thumbImage;
    mediaMessage.messageDescription = content;
    mediaMessage.mediaObject = webObject;
    sendMessageReq.message = mediaMessage;

    if (![DTOpenAPI sendReq:sendMessageReq]) {
        reject(DING_TALK_SHARE_FAILED_CODE, @"分享失败", nil);
    }
}

RCT_EXPORT_METHOD(getAuthCode
                  : (RCTPromiseResolveBlock)resolve
                  : (RCTPromiseRejectBlock)reject) {
  __weak __typeof(self)weakSelf = self;
  weakSelf.resolveBlock = resolve;
  weakSelf.rejectBlock = reject;
  sendLoginResolverStatic = resolve;
    sendLoginRejecterStatic = reject;
    if (![self checkSupport]) {
        return;
    }
  
    DTAuthorizeReq *authReq = [DTAuthorizeReq new];
    authReq.bundleId = [[NSBundle mainBundle] bundleIdentifier];

    if (![DTOpenAPI sendReq:authReq]) {
        reject(DING_TALK_SHARE_FAILED_CODE, @"获取授权码失败", nil);
    }
}

#pragma mark - DTOpenAPIDelegate

/**
 收到一个来自钉钉的请求, 第三方APP处理完成后要调用 +[DTOpenAPI sendResp:] 将处理结果返回给钉钉.
 
 @param req 来自钉钉具体的请求.
 */
- (void)onReq:(DTBaseReq *)req {
}

/**
 第三方APP使用 +[DTOpenAPI sendReq:] 向钉钉发送消息后, 钉钉会处理完请求后会回调该接口.
 
 @param resp 来自钉钉具体的响应.
 */
- (void)onResp:(DTBaseResp *)resp {
 
    if (resp.errorCode == DTOpenAPISuccess) {
        DTAuthorizeResp *authResp = (DTAuthorizeResp *)resp;
        NSString *accessCode = authResp.accessCode;
      self.resolveBlock([NSDictionary dictionaryWithObjectsAndKeys:accessCode,@"code", nil]);
    } else {
        self.rejectBlock([NSString stringWithFormat:@"%@", @(resp.errorCode)], resp.errorMessage, nil);
    }
}

#pragma mark - Private

- (void)initDingTalkShare {
    NSArray *urlTypes = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleURLTypes"];
    for (id type in urlTypes) {
        NSArray *urlSchemes = [type objectForKey:@"CFBundleURLSchemes"];
        for (id scheme in urlSchemes) {
            if ([scheme isKindOfClass:[NSString class]]) {
                NSString *value = (NSString *)scheme;
                if ([value hasPrefix:@"ding"] && (nil == _appId)) {
                    _appId = value;
                    [DTOpenAPI registerApp:_appId];
                    break;
                }
            }
        }
    }
}

- (void)handleOpenURLNotification:(NSNotification *)notification {
    NSURL *url = [NSURL URLWithString:[notification userInfo][@"url"]];
    NSString *schemaPrefix = _appId;
    if ([url isKindOfClass:[NSURL class]] && [[url absoluteString] hasPrefix:[schemaPrefix stringByAppendingString:@"://"]]) {
        [DTOpenAPI handleOpenURL:url delegate:self];
    }
}

- (BOOL)checkSupport {
    if (![DTOpenAPI isDingTalkInstalled] && self.rejectBlock) {
        self.rejectBlock(DING_TALK_NOT_INSTALLED_CODE, @"请安装钉钉客户端", nil);
        return NO;
    }
    if (![DTOpenAPI isDingTalkSupportOpenAPI] && self.rejectBlock) {
        self.rejectBlock(DING_TALK_NOT_INSTALLED_CODE, @"请升级钉钉客户端", nil);
        return NO;
    }
    return YES;
}

@end

记得添加这个:


image.png

Android

安卓文档在这 自己去看,看不懂的话我也帮不了你
https://open.dingtalk.com/document/orgapp/android-platform-application-authorization-login-access

这些要做好


image.png

build.gradle文件里添加

dependencies{

implementation 'com.alibaba.android:ddsharesdk:1.1.0'//钉钉  1.5.0.8貌似不好用
}

新建文件路径(小白的话自己研究怎么穿件package)
首先在工程中创建 ddshare 的 package,然后在在该 package 下创建 DDShareActivity

//DDShareActivity.java

import android.app.Activity;
import android.os.Bundle;

import com.android.dingtalk.share.ddsharemodule.IDDShareApi;
import com.vyungapps.rn.vy.Native.DingTalkShareModule;

public class DDShareActivity extends Activity{
    private IDDShareApi mIDDShareApi;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        try {
            DingTalkShareModule.handleIntent(getIntent());
        } catch (Exception e) {
            e.printStackTrace();
        }
        finish();
    }

}

再创建DingTalkShareModule.java


import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.util.Log;
import android.widget.Toast;

import com.android.dingtalk.share.ddsharemodule.DDShareApiFactory;
import com.android.dingtalk.share.ddsharemodule.IDDAPIEventHandler;
import com.android.dingtalk.share.ddsharemodule.IDDShareApi;
import com.android.dingtalk.share.ddsharemodule.message.BaseReq;
import com.android.dingtalk.share.ddsharemodule.message.BaseResp;
import com.android.dingtalk.share.ddsharemodule.message.DDImageMessage;
import com.android.dingtalk.share.ddsharemodule.message.DDMediaMessage;
import com.android.dingtalk.share.ddsharemodule.message.DDWebpageMessage;
import com.android.dingtalk.share.ddsharemodule.message.SendMessageToDD;
import com.android.dingtalk.share.ddsharemodule.message.SendAuth;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.WritableMap;

public class DingTalkShareModule extends ReactContextBaseJavaModule implements IDDAPIEventHandler {
    private static final String TAG = "DingTalkShare";

    private static final String NOT_INSTALLED_CODE = "NOT_INSTALLED";
    private static final String NOT_SUPPORTED_CODE = "NOT_SUPPORTED";
    private static final String SHARE_FAILED_CODE = "SHARE_FAILED";
    static final String DING_SCOPE = SendAuth.Req.SNS_LOGIN;

    private static DingTalkShareModule mInstance;
    // 不能在构造方法里初始化,因为构造方法获取不到需要的 Activity.
    private static IDDShareApi mDDShareApi;
    private Promise mPromise;

    public DingTalkShareModule(ReactApplicationContext reactContext) {
        super(reactContext);
    }

    public static IDDShareApi getDdShareApi(Context context) {
        if (mDDShareApi == null) {
            String appId = getAppID(context);
            mDDShareApi = DDShareApiFactory.createDDShareApi(context, appId, true);
        }
        return mDDShareApi;
    }

    public static DingTalkShareModule getInstance(ReactApplicationContext reactContext) {
        if (mInstance == null) {
            mInstance = new DingTalkShareModule(reactContext);
        }
        return mInstance;
    }

    @Override
    public String getName() {
        return "RNDingTalkShareModule";
    }

    @ReactMethod
    public void isInstalled(Promise promise) {
        IDDShareApi ddShareApi = getDdShareApi(getCurrentActivity());
        promise.resolve(ddShareApi.isDDAppInstalled());
    }

    @ReactMethod
    public void isSupported(Promise promise) {
        IDDShareApi ddShareApi = getDdShareApi(getCurrentActivity());
        promise.resolve(ddShareApi.isDDSupportAPI());
    }

    @ReactMethod
    public void shareWebPage(String url, String thumbImage, String title, String content, Promise promise) {
        mPromise = promise;
        if (!checkSupport()) {
            return;
        }
        // 初始化一个DDWebpageMessage并填充网页链接地址
        DDWebpageMessage webPageObject = new DDWebpageMessage();
        webPageObject.mUrl = url;

        // 构造一个DDMediaMessage对象
        DDMediaMessage webMessage = new DDMediaMessage();
        webMessage.mMediaObject = webPageObject;

        // 填充网页分享必需参数,开发者需按照自己的数据进行填充
        webMessage.mTitle = title;
        webMessage.mContent = content;
        webMessage.mThumbUrl = thumbImage;
        // 构造一个Req
        SendMessageToDD.Req webReq = new SendMessageToDD.Req();
        webReq.mMediaMessage = webMessage;

        // 调用api接口发送消息到支付宝
        if (!getDdShareApi(getCurrentActivity()).sendReq(webReq)) {
            mPromise.reject(SHARE_FAILED_CODE, "分享失败");
        }
    }

    /**
     * 分享图片
     */
    @ReactMethod
    private void shareImage(String image, Promise promise) {
        mPromise = promise;
        if (!checkSupport()) {
            return;
        }
        // 初始化一个DDImageMessage
        DDImageMessage imageObject = new DDImageMessage();
        if (isLocalResource(image)) {
            imageObject.mImagePath = image;
        } else {
            imageObject.mImageUrl = image;
        }

        // 构造一个mMediaObject对象
        DDMediaMessage mediaMessage = new DDMediaMessage();
        mediaMessage.mMediaObject = imageObject;

        // 构造一个Req
        SendMessageToDD.Req req = new SendMessageToDD.Req();
        req.mMediaMessage = mediaMessage;

        // 调用api接口发送消息到支付宝
        if (!getDdShareApi(getCurrentActivity()).sendReq(req)) {
            mPromise.reject(SHARE_FAILED_CODE, "分享失败");
        }
    }

    @ReactMethod
    public void getAuthCode(Promise promise) {
        mPromise = promise;
        if (!checkSupport()) {
            return;
        }
        // WritableMap map = Arguments.createMap();
        // map.putInt("errCode", 1);
        // map.putString("msg", "ddd");
        // DDModule.promise.resolve(map);

        SendAuth.Req req = new SendAuth.Req();
        req.scope = SendAuth.Req.SNS_LOGIN;
        req.state = "CESHI";

        // 调用api接口发送消息到支付宝
//        if (!getDdShareApi(getCurrentActivity()).sendReq(req)) {
//            mPromise.reject(SHARE_FAILED_CODE, "分享失败");
//        }
        mDDShareApi.sendReq(req);
    }

    @Override
    public void onReq(BaseReq baseReq) {
        Log.d(TAG, "onReq");
    }

    // @Override
    // public void onResp(BaseResp baseResp) {
    // Log.d(TAG, baseResp.mErrStr);
    // int errCode = baseResp.mErrCode;
    // switch (errCode) {
    // case BaseResp.ErrCode.ERR_OK:
    // mPromise.resolve(true);
    // break;
    // default:
    // mPromise.reject(errCode + "", baseResp.mErrStr);
    // break;
    // }
    // }

    @Override
    public void onResp(BaseResp baseResp) {
        int errCode = baseResp.mErrCode;
        String errMsg = baseResp.mErrStr;

        Log.d("DingTalkShare", "errCode=============>" + errCode);
        Log.d("DingTalkShare", "errMsg=============>" + errMsg);

        switch (errCode) {
            case BaseResp.ErrCode.ERR_OK:
                SendAuth.Resp authResp = (SendAuth.Resp) baseResp;
                WritableMap mapSuccess = Arguments.createMap();
                mapSuccess.putString("code", authResp.code);
                mPromise.resolve(mapSuccess);

                break;
            default:
                mPromise.reject(String.valueOf((errCode)) + ": " + errMsg);
        }
    }

    /**
     * 获取钉钉 App ID
     *
     * @param context
     * @return
     */
    public static String getAppID(Context context) {
        ApplicationInfo appInfo = null;
        try {
            appInfo = context.getPackageManager().getApplicationInfo(context.getPackageName(),
                    PackageManager.GET_META_DATA);
            return appInfo.metaData.get("DT_APP_ID").toString();
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static void handleIntent(Intent intent) {
        if (mInstance != null && mDDShareApi != null) {
            mDDShareApi.handleIntent(intent, mInstance);
        }
    }

    private boolean isLocalResource(String url) {
        Uri thumbUri = Uri.parse(url);
        // Verify scheme is set, so that relative uri (used by static resources) are not
        // handled.
        String scheme = thumbUri.getScheme();
        return (scheme == null || scheme.equals("file"));
    }

    private boolean checkSupport() {
        IDDShareApi ddShareApi = getDdShareApi(getCurrentActivity());
        if (!ddShareApi.isDDAppInstalled()) {
             mPromise.reject(NOT_INSTALLED_CODE, "请安装钉钉客户端");

            Toast toast=Toast.makeText(getReactApplicationContext(), "请安装钉钉客户端", Toast.LENGTH_SHORT);

            toast.show();
            return false;
        } else if (!ddShareApi.isDDSupportAPI()) {
            mPromise.reject(NOT_SUPPORTED_CODE, "请升级钉钉客户端");
            return false;
        }
        return true;
    }
}

DingTalkSharePackage.java

  package com.vyungapps.rn.vy.Native;

import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.JavaScriptModule;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class DingTalkSharePackage implements ReactPackage {
    @Override
    public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
        List<NativeModule> modules = new ArrayList<>();
        modules.add(DingTalkShareModule.getInstance(reactContext));
        return modules;
    }

    public List<Class<? extends JavaScriptModule>> createJSModules() {
        return Collections.emptyList();
    }

    @Override
    public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
        return Collections.emptyList();
    }
}

mainapplication.java添加如下

 packages.add(new DingTalkSharePackage());

androidmainfest.xml文件添加下面代码

  <activity
            android:exported="true"
            android:theme="@android:style/Theme.Translucent.NoTitleBar"
            android:launchMode="singleInstance"
            android:name=".ddshare.DDShareActivity">
            <intent-filter>
                <action android:name="android.intent.action.VIEW"></action>
                <category android:name="android.intent.category.DEFAULT"></category>
            </intent-filter>
        </activity>

好了 OK了 API 自己去看 不想多说了 也不是写给新手的
同样 也适用于flutter 但是代码格式需要改改 方法和理论都相同

JS

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

推荐阅读更多精彩内容