Commit ebb7dca2 authored by silver47gin's avatar silver47gin
Browse files

增加Taro-RN代码

parent fc039aac
{
"info" : {
"author" : "xcode",
"version" : 1
}
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleDisplayName</key>
<string>wolwo app</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>wework scheme</string>
<key>CFBundleURLSchemes</key>
<array>
<string>wwauth994c6f0045619f58000022</string>
</array>
</dict>
</array>
<key>CFBundleVersion</key>
<string>1</string>
<key>CodePushDeploymentKey</key>
<string>vOyJ4HjaC5CpC3nxUU8HHx1CcO-X9aoZpjI4O</string>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSExceptionDomains</key>
<dict>
<key>localhost</key>
<dict>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<true/>
</dict>
</dict>
</dict>
<key>NSCameraUsageDescription</key>
<string>Allow $(PRODUCT_NAME) to use your camera</string>
<key>NSContactsUsageDescription</key>
<string>Allow $(PRODUCT_NAME) to access your contacts</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>Allow $(PRODUCT_NAME) to use your location</string>
<key>NSMicrophoneUsageDescription</key>
<string>Allow $(PRODUCT_NAME) to access your microphone</string>
<key>NSMotionUsageDescription</key>
<string>Allow $(PRODUCT_NAME) to access your device's accelerometer</string>
<key>NSPhotoLibraryAddUsageDescription</key>
<string>Give $(PRODUCT_NAME) permission to save photos</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>Give $(PRODUCT_NAME) permission to access your photos</string>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
</dict>
</plist>
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="15702" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<device id="retina4_7" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15704"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="taroDemo" textAlignment="center" lineBreakMode="middleTruncation" baselineAdjustment="alignBaselines" minimumFontSize="18" translatesAutoresizingMaskIntoConstraints="NO" id="GJd-Yh-RWb">
<rect key="frame" x="0.0" y="202" width="375" height="43"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="36"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Powered by React Native" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="9" translatesAutoresizingMaskIntoConstraints="NO" id="MN2-I3-ftu">
<rect key="frame" x="0.0" y="626" width="375" height="21"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<constraints>
<constraint firstItem="Bcu-3y-fUS" firstAttribute="bottom" secondItem="MN2-I3-ftu" secondAttribute="bottom" constant="20" id="OZV-Vh-mqD"/>
<constraint firstItem="Bcu-3y-fUS" firstAttribute="centerX" secondItem="GJd-Yh-RWb" secondAttribute="centerX" id="Q3B-4B-g5h"/>
<constraint firstItem="MN2-I3-ftu" firstAttribute="centerX" secondItem="Bcu-3y-fUS" secondAttribute="centerX" id="akx-eg-2ui"/>
<constraint firstItem="MN2-I3-ftu" firstAttribute="leading" secondItem="Bcu-3y-fUS" secondAttribute="leading" id="i1E-0Y-4RG"/>
<constraint firstItem="GJd-Yh-RWb" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="bottom" multiplier="1/3" constant="1" id="moa-c2-u7t"/>
<constraint firstItem="GJd-Yh-RWb" firstAttribute="leading" secondItem="Bcu-3y-fUS" secondAttribute="leading" symbolic="YES" id="x7j-FC-K8j"/>
</constraints>
<viewLayoutGuide key="safeArea" id="Bcu-3y-fUS"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="52.173913043478265" y="375"/>
</scene>
</scenes>
</document>
\ No newline at end of file
// RCTCalendarModule.m
#import "RCTCalendarModule.h"
@implementation RCTCalendarModule
// To export a module named RCTCalendarModule
RCT_EXPORT_MODULE(CalendarModule);
RCT_EXPORT_METHOD(createCalendarEvent:(NSString *)name
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
{
resolve(@[name,@" is created"]);
}
@end
// RCTWWNativeModule.m
#import "RCTWWNativeModule.h"
#import "WWKApiObject.h"
@implementation RCTWWNativeModule
RCT_EXPORT_MODULE(WWNativeModule);
- (instancetype)init
{
self = [super init];
if (self) {
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleOpenURL:) name:@"RCTOpenURLNotification" object:nil];
}
return self;
}
@end
//
// WWKApi.h
// wxwork
//
// Created by WXWork on 16/5/25.
// Copyright © 2019年 Tencent. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import "WWKApiObject.h"
#pragma mark - WWKApiDelegate
/*! @brief 接收并处理来自企业微信终端程序的事件消息
*
* 接收并处理来自企业微信终端程序的事件消息,期间企业微信界面会切换到第三方应用程序。
* WWKApiDelegate 会在handleOpenURL:delegate:中使用并触发。
*/
@protocol WWKApiDelegate <NSObject>
@optional
// 企业微信展示对外名称.
typedef NS_ENUM(NSInteger, WWKDiplayNameType) {
WWKDefaultName = 0, // 企业微信
WWKLocalName = 1 // 政务微信
};
/*! @brief 收到一个来自企业微信的请求,第三方应用程序处理完后调用sendResp向企业微信发送结果
*
* 收到一个来自企业微信的请求,异步处理完成后必须调用sendResp发送处理结果给企业微信。
* 目前并未使用。
* @param req 具体请求内容,是自动释放的
*/
- (void)onReq:(WWKBaseReq *)req;
/*! @brief 发送一个sendReq后,收到企业微信的回应
*
* 收到一个来自企业微信的处理结果。调用一次sendReq后会收到onResp。
* 可能收到的处理结果有WWKSendMessageResp等。
* @param resp 具体的回应内容,是自动释放的
*/
- (void)onResp:(WWKBaseResp *)resp;
/*! @brief SessionKey更新回调
*
* 需要设置updateSessionKeyIfNeeded:YES,并且满足企业微信sessionKey更新策略才会调用
* @param
*/
- (void)onSessionKeyUpdate:(NSString *)newSessionKey;
@end
#pragma mark - WWKApi
/*! @brief 企业微信Api接口函数类
*
* 该类封装了企业微信终端SDK的所有接口
*/
@interface WWKApi : NSObject
/*! @brief WWKApi的成员函数,向企业微信终端程序注册第三方应用。
*
* 需要在每次启动第三方应用程序时调用。第一次调用后,会在企业微信的可用应用列表中出现。
* iOS7及以上系统需要调起一次企业微信才会出现在企业微信的可用应用列表中。
* @attention 请保证在主线程中调用此函数
* @param appid 企业微信开发者ID
* @return 成功返回YES,失败返回NO。
*/
+ (BOOL)registerApp:(NSString *)appid;
/*! @brief WWKApi的成员函数,向企业微信终端程序注册第三方应用。
*
* 需要在每次启动第三方应用程序时调用。第一次调用后,会在企业微信的可用应用列表中出现。
* iOS7及以上系统需要调起一次企业微信才会出现在企业微信的可用应用列表中。
* @attention 请保证在主线程中调用此函数
* @param appid 第三方应用的appid
* @param scheme 当前app的scheme
* @return 成功返回YES,失败返回NO。
*/
+ (BOOL)registerAppID:(NSString *)appid schema:(NSString *)schema;
/*! @brief WWKApi的成员函数,向企业微信终端程序注册企业应用。
*
* 需要在每次启动第三方应用程序时调用。第一次调用后,会在企业微信的可用应用列表中出现。
* @see registerApp
* @param appid 企业微信开发者ID
* @param corpid 企业微信企业ID
* @param agentid 企业微信企业应用ID
* @return 成功返回YES,失败返回NO。
*/
+ (BOOL)registerApp:(NSString *)appid corpId:(NSString *)corpid agentId:(NSString *)agentid;
/*! @brief 是否必要时更新sessionKey,默认为NO
*
* @param url 企业微信根据安全策略自动更新sessionKey,如果有更新,会触发onSessionKeyUpdate:回调
*/
+ (void)updateSessionKeyIfNeeded:(BOOL)enable;
/*! @brief 处理企业微信通过URL启动App时传递的数据
*
* 需要在 application:openURL:sourceApplication:annotation:或者application:handleOpenURL中调用。
* @param url 企业微信启动第三方应用时传递过来的URL
* @param delegate WWKApiDelegate对象,用来接收企业微信触发的消息。
* @return 成功返回YES,失败返回NO。
*/
+ (BOOL)handleOpenURL:(NSURL *)url delegate:(id<WWKApiDelegate>) delegate;
/*! @brief 检查企业微信是否已被用户安装
*
* @return 企业微信已安装返回YES,未安装返回NO。
* @note 由于iOS系统的限制,在iOS9及以上系统检测企业微信是否安装,需要将企业微信的scheme"wxwork"(云端版本)及"wxworklocal"(本地部署版本)添加到工程的Info.plist中的LSApplicationQueriesSchemes白名单里,否则此方法总是会返回NO。
* 详情参考 https://developer.apple.com/documentation/uikit/uiapplication/1622952-canopenurl
*/
+ (BOOL)isAppInstalled;
/*! @brief 获取企业微信的itunes安装地址
*
* @return 企业微信的安装地址字符串。
*/
+ (NSString *)getAppInstallUrl;
/*! @brief 获取当前企业微信SDK的版本号
*
* @return 返回当前企业微信SDK的版本号
*/
+ (NSString *)getApiVersion;
/*! @brief 打开企业微信
*
* @return 成功返回YES,失败返回NO。
*/
+ (BOOL)openApp;
/*!
* @brief 启动登录流程。
* @param navigationController 当前页面的导航栏控制器
* @param state 第三方应用传入的值,登录成功后会透传给第三方应用
* @see WWKSSOReq
*/
+ (void)startSSOLogin:(NSString *)state fromNavigationController:(UINavigationController *)navigationController;
/*! @brief 发送请求到企业微信,等待企业微信返回onResp
*
* 函数调用后,会切换到企业微信的界面。第三方应用程序等待企业微信返回onResp。企业微信在异步处理完成后一定会调用onResp。支持以下类型
* WWKSendMessageReq等。
* @param req 具体的发送请求,在调用函数后,请自己释放。
* @return 成功返回YES,失败返回NO。
*/
+ (BOOL)sendReq:(WWKBaseReq *)req;
/*! @brief 收到企业微信onReq的请求,发送对应的应答给企业微信,并切换到企业微信界面
*
* 函数调用后,会切换到企业微信的界面。第三方应用程序收到企业微信onReq的请求,异步处理该请求,完成后必须调用该函数。
* 目前暂未使用
* @param resp 具体的应答内容,调用函数后,请自己释放
* @return 成功返回YES,失败返回NO。
*/
+ (BOOL)sendResp:(WWKBaseResp *)resp;
/*! @brief 获取当前企业微信展示名称
*
* @return 返回当前企业微信展示名称
*/
+ (WWKDiplayNameType)displayNameType;
/*! @brief 设置登录态返回的sessionKey
*
*/
+ (void)setSessionKey:(NSString *)sessionKey;
/*! @brief 初始化OpenData
*
* @以block的形式返回成功或者失败
*/
+(void)initOpenData:(void (^)(BOOL isSucc))completion;
/*! @brief 获取成员或者部门的详细信息
*
* @param 传入WWKOpenDataItem输入,SDK会获取部门或用户名并以block的形式返回
* 需要initOpenData返回成功后才能调用
* @see WWKOpenDataItem
*/
+(void)getOpenData:(NSArray<WWKOpenDataItem *> *)dataList completion:(void (^)(NSError *error,NSArray<WWKOpenDataItem *> *dataArray))completion;
@end
//
// WWKApiObject.h
// wxwork
//
// Created by WXWork on 16/5/25.
// Copyright © 2019年 Tencent. All rights reserved.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@class WWKBaseResp;
@protocol WWKApiSerializable <NSObject>
- (BOOL)deserializeWithDictionary:(NSDictionary *)dict;
@end
@interface WWKBaseObject : NSObject
@property (nonatomic, copy) NSString *bundleID;
@property (nonatomic, copy) NSString *bundleName;
@property (nonatomic, assign) NSUInteger sequence;
@property (nonatomic, readonly, nullable) NSData *serializedData;
@property (nonatomic, readonly, nullable) NSMutableDictionary *serializedDict;
+ (instancetype)deserializeWithData:(NSData *)data appid:(NSString *)appid;
@end
#pragma mark - WWKBaseReq
/*! @brief 该类为企业微信终端SDK所有请求类的基类
*
*/
@interface WWKBaseReq : WWKBaseObject
- (WWKBaseResp *)respObj; // 对应返回的结构
@end
#pragma mark - WWKBaseResp
extern const int WWKBaseRespErrCodeOK; // 操作成功
extern const int WWKBaseRespErrCodeCancelled; // 操作被取消
extern const int WWKBaseRespErrCodeFailed; // 操作失败
extern const int WWKBaseRespErrCodeNotSupported; // 操作不支持(不能识别请求)
extern const int WWKBaseRespErrCodeLowAppVer; // 操作不支持(用户安装的App版本过低)
extern const int WWKBaseRespErrCodeNoPrivileges; // 没有此API的调用权限
extern const int WWKBaseRespErrCodeInvalidCall; // 调用出错(例如传入参数不正确)
extern const int WWKBaseRespErrCodeNetWork; // 网络出错(例如请求超时)
extern const int WWKBaseRespErrCodeSessonKeyTimeOut; // sessionkey超时
/*! @brief 该类为企业微信终端SDK所有响应类的基类
*
*/
@interface WWKBaseResp : WWKBaseObject
/** 错误码 */
@property (nonatomic, assign) int errCode;
/** 错误提示字符串 */
@property (nonatomic, retain, nullable) NSString *errStr;
@end
#pragma mark - WWKSendMessageReq
/*!
* @brief 带在 @c WWKSendMessageReq 中的消息对象基类。实际必须使用其子类
*/
@interface WWKMessageAttachment : NSObject
@end
/*!
* @brief 文本消息
*/
@interface WWKMessageTextAttachment : WWKMessageAttachment
@property (nonatomic, copy) NSString *text;
@end
/*!
* @brief 文件消息
*/
@interface WWKMessageFileAttachment : WWKMessageAttachment
@property (nonatomic, copy) NSString *filename;
@property (nonatomic, copy, nullable) NSString *path;
@property (nonatomic, retain, nullable) NSData *data;
@end
/*!
* @brief 图片消息
*/
@interface WWKMessageImageAttachment : WWKMessageFileAttachment
@end
/*!
* @brief 视频消息
*/
@interface WWKMessageVideoAttachment : WWKMessageFileAttachment
@end
/*!
* @brief 链接消息
*/
@interface WWKMessageLinkAttachment : WWKMessageAttachment
@property (nonatomic, copy, nullable) NSString *title; // 不能超过512bytes
@property (nonatomic, copy, nullable) NSString *summary; // 不能超过1k
@property (nonatomic, copy, nullable) NSString *url;
@property (nonatomic, copy, nullable) NSString *iconurl;
@property (nonatomic, retain, nullable) NSData *icon; // 不能超过32K
// 是否使用带 shareTicket 的转发,只有注册了企业应用的agentid才有效;只能为单条转发,放在WWKMessageGroupAttachment中会被过滤
@property (nonatomic, assign) BOOL withShareTicket;
@property (nonatomic, copy, nullable) NSString *shareTicketState;
@end
/*!
* @brief 小程序消息
*/
@interface WWKMessageMiniAppAttachment : WWKMessageAttachment
@property (nonatomic, copy) NSString *userName; // 小程序username
@property (nonatomic, copy, nullable) NSString *path; // 小程序页面的路径
@property (nonatomic, strong, nullable) NSData *hdImageData; // 小程序预览图 不能超过128k
@property (nonatomic, copy, nullable) NSString *title; // 小程序 当前页title 不能超过512bytes
@end
/*!
* @brief 位置消息
*/
@interface WWKMessageLocationAttachment : WWKMessageAttachment
@property (nonatomic, copy, nullable) NSString *title;
@property (nonatomic, copy, nullable) NSString *address;
@property (nonatomic, assign) double lat;
@property (nonatomic, assign) double lng;
@property (nonatomic, assign) double zoom;
@end
/*!
* @brief 单条聊天记录消息
*/
@interface WWKMessageAttachmentWrapper : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSDate *date;
@property (nonatomic, copy, nullable) NSString *avatarUrl;
@property (nonatomic, copy, nullable) NSData *avatarData;
@property (nonatomic, retain) WWKMessageAttachment *attachment;
@end
/*!
* @brief 聊天记录消息组
*/
@interface WWKMessageGroupAttachment : WWKMessageAttachment
@property (nonatomic, copy) NSArray<WWKMessageAttachmentWrapper *> *contents;
@property (nonatomic, copy) NSString *title;
@end
typedef NS_ENUM(NSUInteger, WWKOpenIdType) {
WWKOpenIdTypeUnKnown = 0,
WWKOpenIdTypeUser, // 成员
WWKOpenIdTypeDepartment //部门
};
@interface WWKOpenIdInfo : NSObject
@property (nonatomic, copy, nullable) NSString *openid;
@property (nonatomic, assign) WWKOpenIdType idType;
@end
/*! @brief 第三方程序发送消息至企业微信终端程序的消息结构体
*
* 第三方程序向企业微信发送信息需要传入WWKSendMessageReq结构体,信息类型包括文本消息和多媒体消息,
* 分别对应于text和message成员。调用该方法后,企业微信处理完信息会向第三方程序发送一个处理结果。
* @see WWKSendMessageResp
*/
@interface WWKSendMessageReq : WWKBaseReq <WWKApiSerializable>
/** 发送消息的多媒体内容
* @see WWKMessageAttachment
*/
@property (nonatomic, retain, nullable) WWKMessageAttachment *attachment;
@end
#pragma mark - WWKSendMessageResp
/*! @brief 企业微信终端向第三方程序返回的WWKSendMessageReq处理结果。
*
* 第三方程序向企业微信终端发送WWKSendMessageReq后,企业微信发送回来的处理结果,该结果用WWKSendMessageResp表示。
*/
@interface WWKSendMessageResp : WWKBaseResp <WWKApiSerializable>
@end
#pragma mark - WWKPickContactReq
/*! @brief 第三方程序从企业微信终端程序选人的消息结构体
*
* 本接口无需登录态,只能选择邮箱联系人。如果需要选择所有联系人,请使用WWKSelectContactReq接口
*
*
*/
@interface WWKPickContactReq : WWKBaseReq <WWKApiSerializable>
@property (nonatomic, copy) NSString *type;
@end
#pragma mark - WWKPickContactResp
@interface WWKContactInfo : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy, nullable) NSString *email;
@end
@interface WWKPickContactResp : WWKBaseResp <WWKApiSerializable>
@property (nonatomic, retain, nullable) NSArray<WWKContactInfo *> *contacts;
@end
#pragma mark - WWKOpenConversationReq
@interface WWKOpenConversationReq : WWKBaseReq <WWKApiSerializable>
@property (nonatomic, assign) uint64_t conversationType;
@property (nonatomic, assign) uint64_t conversationId;
@end
#pragma mark - WWKOpenConversationResp
@interface WWKOpenConversationResp : WWKBaseResp <WWKApiSerializable>
@end
#pragma mark - WWKSSOReq
@interface WWKSSOReq : WWKBaseReq <WWKApiSerializable>
@property (nonatomic, copy) NSString *state;
@end
@interface WWKSSOResp : WWKBaseResp <WWKApiSerializable>
@property (nonatomic, retain, nullable) NSString *state;
@property (nonatomic, retain, nullable) NSString *code;
@end
/*! @brief 第三方程序创建群聊并发送消息至企业微信终端程序的消息结构体(需登录态)
*
* 第三方程序向企业微信发送信息需要传入WWKSendMessageReq结构体,创建群聊成员的openid列表
* (只支持成员,不支持部门),当前登录态返回的openUserid,信息类型包括文本消息和多媒体消息,
* 分别对应于text和message成员。调用该方法后,企业微信处理完信息会向第三方程序发送一个处理结果。
* @see WWKOpenChatWithMessageResp
*/
@interface WWKOpenChatWithMessageReq : WWKBaseReq <WWKApiSerializable>
/** 发送消息的多媒体内容
* @see WWKMessageAttachment
*/
@property (nonatomic, retain, nullable) WWKMessageAttachment *attachment;
/** 发送消息的到新创建的群聊
* 暂不支持传入部门信息
*/
@property (nonatomic, retain, nullable) NSArray<NSString *> *userOpenidList;
/**
* 从后台登录获取的openUserid
*/
@property (nonatomic, retain, nullable) NSString *openUserid;
@end
#pragma mark - WWKOpenChatWithMessageResp
/*! @brief 企业微信终端向第三方程序返回的WWKOpenChatWithMessageReq处理结果。
*
* 第三方程序向企业微信终端发送WWKOpenChatWithMessageReq后,企业微信发送回来的处理结果,该结果用WWKOpenChatWithMessageResp表示。
*/
@interface WWKOpenChatWithMessageResp : WWKBaseResp <WWKApiSerializable>
@end
#pragma mark - WWKSelectContactReq
/*! @brief 第三方程序从企业微信终端程序选人的消息结构体(需登录态)
*
* 第三方程序向企业微信发送信息需要传入当前登录态返回的openUserid。调用该方法后,企业微信调起选人流程,结束后会向第三方程序发送一个处理结果。
* @see WWKSelectContactResp
*/
@interface WWKSelectContactReq : WWKBaseReq <WWKApiSerializable>
/**
* 从后台登录获取的openUserid,必须填写这个字段
*/
@property (nonatomic, retain) NSString *openUserid;
@end
#pragma mark - WWKSelectContactResp
/*! @brief 选人回包
*
* 从这个接口返回的数据不会带人名或部门名,需要调用getOpenData接口获取名字等详细信息
* 目前仅支持选择成员,暂不支持选择部门
*/
@interface WWKSelectContactResp : WWKBaseResp <WWKApiSerializable>
@property (nonatomic, retain, nullable) NSArray<WWKOpenIdInfo *> *openids;
@end
/*! @brief getOpenData接口使用的结构
* @param openid 部门或成员openid信息
* @param type 需要查询的类型,目前仅支持userName(用户名称)、userDepartment(用户主部门名称)以及userGender(用户性别)、departmentName(部门名称),其中userName、userDepartment、userGender在openid为用成员时有效,departmentName在openid为部门时有效
* @param data 传入时不需要填写,getOpenData返回会填充该字段
*/
@interface WWKOpenDataItem : NSObject
@property (nonatomic, copy) NSString *openid;
@property (nonatomic, copy) NSString *type;
@property (nonatomic, copy, nullable) NSString *data;
@end
#pragma mark - WWKSelectPrivilegedContactReq
/*! @brief 第三方程序从企业微信终端程序选人的消息结构体(需登录态)
*
* 选择范围限定为企业通讯录
* 只支持成员,若勾选部门,则对应展开勾选部门下的成员。
* 与WWKSelectContactReq接口的区分是,返回的选择范围,需要经过第三方权限过滤。只返回在可见范围的列表,而完整的列表存在ticket给第三方,用于jsapi/api接口调用。
* @see WWKSelectPrivilegedContactResp
*/
@interface WWKSelectPrivilegedContactReq : WWKBaseReq <WWKApiSerializable>
/**
* 从后台登录获取的loginOpenUserid,必须填写这个字段
* selectedOpenUserIds 预选用户openUserId列表
* selectedTickets 预选ticket列表
*/
@property (nonatomic, copy) NSString *loginOpenUserid;
@property (nonatomic, copy) NSArray<NSString *> *selectedOpenUserIds;
@property (nonatomic, copy) NSArray<NSString *> *selectedTickets;
@end
#pragma mark - WWKSelectPrivilegedContactResp
/*! @brief 选人回包
*
* openUserIdList 返回openid的列表
* selectedTicket 选择的集合Ticket,用于对该选择集合操作的调用凭证。
* expiresIn selectedTicket的有效期,单位为秒
* selectedUserCount 选择的集合Ticket包含的用户数
*/
@interface WWKSelectPrivilegedContactResp : WWKBaseResp <WWKApiSerializable>
@property (nonatomic, copy) NSArray<NSString *> *openUserIdList;
@property (nonatomic, copy) NSString *selectedTicket;
@property (nonatomic, assign) uint32_t expiresIn;
@property (nonatomic, assign) uint32_t selectedUserCount;
@end
#pragma mark - WWKCreateChatWithMsgReq
/*! @brief 新建群聊发消息(需登录态)
*
* 选人范围包括企业通讯录和外部联系人。
* 支持用户传入openUserId,和selectedTicket
* @see WWKCreateChatWithMsgResp
*/
@interface WWKCreateChatWithMsgReq : WWKBaseReq <WWKApiSerializable>
/**
* loginOpenUserid 从后台登录获取的loginOpenUserid,必须填写这个字段
* selectedOpenUserIdList 创建会话的企业通讯录成员信息,格式为字符串,内容为成员的openid列表(可以从WWKSelectPrivilegedContactResp获取),限定必须为应用的可见范围内。列表最多2000项。
* selectedTicketList 已选择的成员集合凭证列表,(可以从WWKSelectPrivilegedContactResp获取)。列表最多10个。
* attachment 消息内容结构体,支持文本、文件、图片、视频、链接、聊天内容,参考WWKMessageAttachment
* chatName 群聊名称
* state 透传参数,最长256,用于回调第三方应用创建群聊事件
*
*/
@property (nonatomic, copy) NSString *loginOpenUserid;
@property (nonatomic, copy) NSArray<NSString *> *selectedOpenUserIdList;
@property (nonatomic, copy) NSArray<NSString *> *selectedTicketList;
@property (nonatomic, retain) WWKMessageAttachment *attachment;
@property (nonatomic, copy, nullable) NSString *chatName;
@property (nonatomic, copy, nullable) NSString *state;
@end
#pragma mark - WWKCreateChatWithMsgResp
/*! @brief 新建群聊发消息回包
*
* errorCode 错误码
* errorMsg 错误码描述
* chatId 若创建群成功,返回群ID
*/
@interface WWKCreateChatWithMsgResp : WWKBaseResp <WWKApiSerializable>
@property (nonatomic, copy, nullable) NSString *chatId;
@end
#pragma mark - WWKOpenExistedChatWithMsgReq
/*! @brief 打开群聊发消息(需登录态)
*/
@interface WWKOpenExistedChatWithMsgReq : WWKBaseReq <WWKApiSerializable>
/**
* loginOpenUserid 从后台登录获取的loginOpenUserid,必须填写这个字段
* attachment 消息内容结构体,支持文本、文件、图片、视频、链接、聊天内容,参考WWKMessageAttachment attachment为可选。若不传参数,则无消息发送确认行为。
* chatId 打开群聊ID,chatId必须是当前用户所在的群聊
*
*/
@property (nonatomic, copy) NSString *loginOpenUserid;
@property (nonatomic, retain, nullable) WWKMessageAttachment *attachment;
@property (nonatomic, copy) NSString *chatId;
@end
#pragma mark - WWKOpenExistedChatWithMsgResp
/*! @brief 打开群聊发消息回包
*
* errorCode 错误码
* errorMsg 错误码描述
*/
@interface WWKOpenExistedChatWithMsgResp : WWKBaseResp <WWKApiSerializable>
@end
NS_ASSUME_NONNULL_END
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>
#import <UIKit/UIKit.h>
#import <XCTest/XCTest.h>
#import <React/RCTLog.h>
#import <React/RCTRootView.h>
#define TIMEOUT_SECONDS 600
#define TEXT_TO_LOOK_FOR @"Welcome to React"
@interface taroDemoTests : XCTestCase
@end
@implementation taroDemoTests
- (BOOL)findSubviewInView:(UIView *)view matching:(BOOL(^)(UIView *view))test
{
if (test(view)) {
return YES;
}
for (UIView *subview in [view subviews]) {
if ([self findSubviewInView:subview matching:test]) {
return YES;
}
}
return NO;
}
- (void)testRendersWelcomeScreen
{
UIViewController *vc = [[[RCTSharedApplication() delegate] window] rootViewController];
NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS];
BOOL foundElement = NO;
__block NSString *redboxError = nil;
#ifdef DEBUG
RCTSetLogFunction(^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) {
if (level >= RCTLogLevelError) {
redboxError = message;
}
});
#endif
while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) {
[[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
[[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
foundElement = [self findSubviewInView:vc.view matching:^BOOL(UIView *view) {
if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) {
return YES;
}
return NO;
}];
}
#ifdef DEBUG
RCTSetLogFunction(RCTDefaultLogFunction);
#endif
XCTAssertNil(redboxError, @"RedBox error: %@", redboxError);
XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS);
}
@end
{
"name": "HelloWorld",
"version": "1.0.0",
"main": "index.js",
"author": "GeorgeLin <silver47gin@gmail.com>",
"license": "MIT"
"version": "0.0.0",
"private": true,
"templateInfo": {
"name": "react-native",
"typescript": true,
"css": "sass"
},
"scripts": {
"prepare": "husky install",
"build:rn": "taro build --type rn",
"dev:rn": "npm run build:rn -- --watch --qr",
"android": "react-native run-android --no-packager",
"ios": "react-native run-ios --no-packager",
"start": "npm run dev:rn",
"podInstall": "pod-install",
"upgradePeerdeps": "install-peerdeps @tarojs/taro-rn@3.3.9 -o -Y && install-peerdeps @tarojs/components-rn@3.3.9 -o -Y && install-peerdeps @tarojs/router-rn@3.3.9 -o -Y && pod-install",
"server": "nodemon server/index.ts"
},
"browserslist": [
"last 3 versions",
"Android >= 4.1",
"ios >= 8"
],
"author": "",
"dependencies": {
"@babel/runtime": "^7.7.7",
"@react-native-async-storage/async-storage": "^1.15.9",
"@tarojs/cli": "^3.3.9",
"@tarojs/components": "3.3.9",
"@tarojs/react": "3.3.9",
"@tarojs/runtime": "3.3.9",
"@tarojs/taro": "3.3.9",
"@tarojs/taro-rn": "3.3.9",
"@types/express": "^4.17.13",
"dayjs": "^1.10.7",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^3.3.1",
"express": "^4.17.1",
"husky": "^7.0.2",
"mobx": "^6.3.3",
"mobx-react-lite": "^3.2.1",
"mobx-utils": "^6.0.4",
"nanoid": "^3.1.29",
"nodemon": "^2.0.15",
"prettier": "^2.3.2",
"react": "^17.0.0",
"react-dom": "^17.0.0",
"react-native": "^0.64.0",
"react-native-amap-geolocation": "^1.1.2",
"react-native-amap3d": "^3.0.1",
"react-native-code-push": "^7.0.4",
"react-native-gesture-handler": "^1.10.3",
"react-native-safe-area-context": "^3.3.2",
"react-native-svg": "^12.1.1",
"rxjs": "^6.6.3",
"ts-node": "^10.4.0"
},
"devDependencies": {
"@babel/core": "^7.8.0",
"@commitlint/cli": "^13.2.0",
"@commitlint/config-conventional": "^13.2.0",
"@tarojs/mini-runner": "3.3.9",
"@tarojs/rn-runner": "3.3.9",
"@tarojs/webpack-runner": "3.3.9",
"@types/react": "^17.0.2",
"@types/react-native": "^0.66.4",
"@types/webpack-env": "^1.13.6",
"@typescript-eslint/eslint-plugin": "^4.15.1",
"@typescript-eslint/parser": "^4.15.1",
"babel-plugin-module-resolver": "^4.1.0",
"babel-preset-taro": "3.3.9",
"eslint": "^6.8.0",
"eslint-config-taro": "3.3.9",
"eslint-plugin-import": "^2.12.0",
"eslint-plugin-react": "^7.8.2",
"eslint-plugin-react-hooks": "^4.2.0",
"install-peerdeps": "^3.0.3",
"pod-install": "^0.1.23",
"stylelint": "9.3.0",
"taro-iconfont-cli": "^3.3.0",
"typescript": "^4.1.0"
}
}
{
"miniprogramRoot": "./dist",
"projectname": "wolwo-app",
"description": "wolwo-app",
"appid": "touristappid",
"setting": {
"urlCheck": true,
"es6": false,
"postcss": false,
"minified": false
},
"compileType": "miniprogram"
}
{
"miniprogramRoot": "./",
"projectname": "wolwo-app",
"appid": "touristappid",
"setting": {
"es6": false,
"minified": false
}
}
### 编译以及打包流程
## 安装依赖
```bash
$ yarn install
$ yarn podInstall
```
## 安卓端调试
```bash
$ npx taro build --type rn --platform android --watch --reset-cache
$ yarn android
```
## 安卓端打包
```bash
$ npx taro build --type rn --platform android --reset-cache
$ cd android
$ ./gradlew assemble
$ adb install ./app/build/outputs/apk/release/app-release.apk
```
## 安卓端更新 bundle
```bash
$ npx taro build --type rn --platform android --reset-cache
$ appcenter codepush release -c android/app/build/generated/assets/react/release/index.android.bundle -a silver47gin-gmail/tools-android
```
## ios 端调试
```bash
$ npx taro build --type rn --platform --reset-cache --watch android
$ yarn ios
```
## ios 打包
- 使用 xcode 打开 ios 文件夹
- 选择 taroDemo 的 Scheme 和 Any Device 的 Destination
- 点击菜单上的 Product -> Archive
- 点击菜单中的 Window -> Organizar
- 选中生成的 Archive,点击 Distribute APP,按照需要选择后续
## ios 更新 bundle
```bash
$ npx taro build --type rn --platform ios --reset-cache
$ appcenter codepush release -c ios/main.jsbundle -a silver47gin-gmail/tools-ios
```
import { existsSync, mkdirSync, writeFileSync, readFileSync } from "fs";
import { join } from "path";
interface Cache {
access_token?: string;
}
class CacheManager {
cache: Cache = {};
TEMP_DIR = join(process.cwd(), ".temp");
TEMP_FILE = join(this.TEMP_DIR, "server_cache");
constructor() {
this.init();
}
init() {
if (!existsSync(this.TEMP_DIR)) {
mkdirSync(this.TEMP_DIR);
}
if (!existsSync(this.TEMP_FILE)) {
writeFileSync(this.TEMP_FILE, "{}", {
flag: "w",
});
}
}
load() {
this.cache = JSON.parse(readFileSync(this.TEMP_FILE).toString());
}
save() {
writeFileSync(this.TEMP_FILE, JSON.stringify(this.cache), {
flag: "w",
});
}
}
export const cacheManager = new CacheManager();
import * as express from "express";
import { getAccessToken, getUserInfo } from "./wexinapi";
import { cacheManager } from "./cache";
const corpid = "wx994c6f0045619f58";
const corpsecret = "UtBW2SVGKW0NF5PXSWI-mP8Dx9QM-FPW3_i9kKDQGcY";
const app = express();
cacheManager.load();
app.get("/getUserInfo", async (req, res) => {
try {
const code = req.query.code as string;
console.log(`code`, code);
if (!code) {
res.sendStatus(400);
return;
}
let accessToken: string;
if (!cacheManager.cache.access_token) {
console.log("get AccessToken from wexin");
accessToken = await getAccessToken(corpid, corpsecret);
} else {
console.log("get AccessToken from cache");
accessToken = cacheManager.cache.access_token;
}
console.log(`accessToken`, accessToken);
cacheManager.cache.access_token = accessToken;
cacheManager.save();
const userInfo = await getUserInfo(accessToken, code);
console.log(`userInfo`, userInfo);
res.json(userInfo);
} catch (error) {
console.log(error);
res.sendStatus(400);
}
});
app.get("*", function (_, res) {
res.send("Welcome Dev Server");
});
app.listen(8000, () => {
console.log(`server is listen on http://10.1.121.166:8000/`);
});
import * as https from "https";
export const getAccessToken = (corpid: string, corpsecret: string) =>
new Promise<string>((resolve, reject) => {
https.get(
`https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=${corpid}&corpsecret=${corpsecret}`,
(s) => {
s.on("data", (data: Buffer) => {
const wxRes = JSON.parse(data.toString());
if (wxRes.errcode === 0) {
resolve(wxRes.access_token);
} else {
reject(wxRes);
}
});
}
);
});
export const getUserInfo = (accessToken: string, code: string) =>
new Promise<any>((resolve, reject) => {
https.get(
`https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo?access_token=${accessToken}&code=${code}`,
(s) => {
s.on("data", (data: Buffer) => {
const wxRes = JSON.parse(data.toString());
if (wxRes.errcode === 0) {
resolve(wxRes);
} else {
reject(wxRes);
}
});
}
);
});
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment