feat: 添加ShutoApi Cordova插件基础实现
实现ShutoApi Cordova插件的基础功能,包括: - 添加.gitignore忽略.history目录 - 创建package.json定义插件基本信息 - 编写plugin.xml配置插件 - 实现前端JavaScript接口 - 添加Android和iOS原生实现 - 编写README文档说明使用方法
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
.history/
|
||||
148
README.md
Normal file
148
README.md
Normal file
@@ -0,0 +1,148 @@
|
||||
# ShutoApi Cordova Plugin
|
||||
|
||||
这是一个Cordova插件,用于在前端和原生代码之间进行通信,特别是提供了后台触发前端导航的功能。
|
||||
|
||||
## 安装
|
||||
|
||||
```bash
|
||||
cordova plugin add /path/to/cordova-shuto-api
|
||||
```
|
||||
|
||||
## API参考
|
||||
|
||||
### 方法
|
||||
|
||||
#### close()
|
||||
关闭当前功能或视图。
|
||||
|
||||
```javascript
|
||||
cordova.plugins.ShutoApi.close(
|
||||
function() {
|
||||
console.log('Close success');
|
||||
},
|
||||
function(error) {
|
||||
console.error('Close error:', error);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
#### getUserInfo()
|
||||
获取用户信息。
|
||||
|
||||
```javascript
|
||||
cordova.plugins.ShutoApi.getUserInfo(
|
||||
function(userInfo) {
|
||||
console.log('User info:', userInfo);
|
||||
},
|
||||
function(error) {
|
||||
console.error('Get user info error:', error);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
### 事件
|
||||
|
||||
#### navigate
|
||||
当后台需要前端导航到指定路由时触发。
|
||||
|
||||
**事件数据:**
|
||||
- `route`: 需要导航到的路由路径
|
||||
- `params`: 导航参数(可选,对象类型)
|
||||
|
||||
## 事件监听示例
|
||||
|
||||
### 基本用法
|
||||
|
||||
```javascript
|
||||
// 监听导航事件
|
||||
cordova.plugins.ShutoApi.addEventListener('navigate', function(data) {
|
||||
console.log('需要导航到:', data.route);
|
||||
console.log('导航参数:', data.params);
|
||||
|
||||
// 在这里实现前端导航逻辑
|
||||
// 例如,如果使用Vue Router:
|
||||
// router.push({ path: data.route, query: data.params || {} });
|
||||
|
||||
// 例如,如果使用React Router:
|
||||
// history.push(data.route, data.params || {});
|
||||
|
||||
// 例如,如果使用React Router v6:
|
||||
// navigate(data.route, { state: data.params || {} });
|
||||
});
|
||||
```
|
||||
|
||||
### 移除事件监听
|
||||
|
||||
```javascript
|
||||
// 定义事件处理函数
|
||||
function handleNavigate(data) {
|
||||
console.log('需要导航到:', data.route);
|
||||
// 处理导航逻辑
|
||||
}
|
||||
|
||||
// 添加事件监听
|
||||
cordova.plugins.ShutoApi.addEventListener('navigate', handleNavigate);
|
||||
|
||||
// 移除事件监听
|
||||
cordova.plugins.ShutoApi.removeEventListener('navigate', handleNavigate);
|
||||
```
|
||||
|
||||
## 原生代码使用
|
||||
|
||||
### iOS
|
||||
|
||||
在iOS原生代码中,可以通过以下方式触发导航事件:
|
||||
|
||||
```objective-c
|
||||
// 假设你有一个ShutoApi实例
|
||||
ShutoApi* pluginInstance = [[ShutoApi alloc] init];
|
||||
|
||||
// 无参数调用
|
||||
[pluginInstance navigateToRoute:@"/home/dashboard" parameters:nil];
|
||||
|
||||
// 带参数调用
|
||||
NSDictionary* params = @{
|
||||
@"userId": @"12345",
|
||||
@"showDetails": @YES
|
||||
};
|
||||
[pluginInstance navigateToRoute:@"/user/profile" parameters:params];
|
||||
```
|
||||
|
||||
### Android
|
||||
|
||||
在Android原生代码中,可以通过以下方式触发导航事件:
|
||||
|
||||
```java
|
||||
// 假设你有一个ShutoApi实例
|
||||
ShutoApi pluginInstance = new ShutoApi();
|
||||
|
||||
try {
|
||||
// 无参数调用
|
||||
pluginInstance.navigateToRoute("/home/dashboard", null);
|
||||
|
||||
// 带参数调用
|
||||
JSONObject params = new JSONObject();
|
||||
params.put("userId", "12345");
|
||||
params.put("showDetails", true);
|
||||
pluginInstance.navigateToRoute("/user/profile", params);
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. 确保在使用事件监听之前,Cordova设备已经准备就绪。可以在`deviceready`事件后进行初始化:
|
||||
|
||||
```javascript
|
||||
document.addEventListener('deviceready', function() {
|
||||
// 在这里设置事件监听
|
||||
cordova.plugins.ShutoApi.addEventListener('navigate', function(data) {
|
||||
// 处理导航事件
|
||||
});
|
||||
}, false);
|
||||
```
|
||||
|
||||
2. 当不再需要事件监听时,记得移除它,以避免内存泄漏。
|
||||
|
||||
3. 事件监听是持久化的,只要插件处于活动状态,就会保持监听。
|
||||
34
package.json
Normal file
34
package.json
Normal file
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"name": "cordova-shuto-api",
|
||||
"version": "1.0.0",
|
||||
"description": "ShutoApi Cordova Plugin for navigation and communication between native and web",
|
||||
"main": "www/ShutoApi.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"keywords": [
|
||||
"cordova",
|
||||
"shuto",
|
||||
"navigation",
|
||||
"ecosystem:cordova",
|
||||
"cordova-android",
|
||||
"cordova-ios"
|
||||
],
|
||||
"cordova": {
|
||||
"id": "cordova-shuto-api",
|
||||
"platforms": [
|
||||
"android",
|
||||
"ios"
|
||||
]
|
||||
},
|
||||
"engines": {
|
||||
"cordovaDependencies": {
|
||||
"4.0.0": {
|
||||
"cordova-android": "^6.0.0",
|
||||
"cordova-ios": "^4.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
2
plugin.xml
Normal file
2
plugin.xml
Normal file
@@ -0,0 +1,2 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<plugin id="cordova-shuto-api" version="1.0.0" xmlns="http://apache.org/cordova/ns/plugins/1.0" xmlns:android="http://schemas.android.com/apk/res/android"><name>ShutoApi</name><js-module name="ShutoApi" src="www/ShutoApi.js"><clobbers target="cordova.plugins.ShutoApi" /></js-module><platform name="android"><config-file parent="/*" target="res/xml/config.xml"><feature name="ShutoApi"><param name="android-package" value="com.shuto.feishuapi.ShutoApi" /></feature></config-file><config-file parent="/*" target="AndroidManifest.xml" /><source-file src="src/android/ShutoApi.java" target-dir="src/com/shuto/feishuapi/ShutoApi" /></platform><platform name="ios"><config-file parent="/*" target="config.xml"><feature name="ShutoApi"><param name="ios-package" value="ShutoApi" /></feature></config-file><source-file src="src/ios/ShutoApi.m" /></platform></plugin>
|
||||
84
src/android/ShutoApi.java
Normal file
84
src/android/ShutoApi.java
Normal file
@@ -0,0 +1,84 @@
|
||||
package com.shuto.feishuapi;
|
||||
|
||||
import org.apache.cordova.CordovaPlugin;
|
||||
import org.apache.cordova.CallbackContext;
|
||||
import org.apache.cordova.PluginResult;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
* This class implements a Cordova plugin for Shuto API.
|
||||
*/
|
||||
public class ShutoApi extends CordovaPlugin {
|
||||
|
||||
// 保存事件回调上下文
|
||||
private CallbackContext eventCallbackContext;
|
||||
|
||||
@Override
|
||||
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
|
||||
if (action.equals("close")) {
|
||||
// 修复:close方法现在不需要参数
|
||||
this.close(callbackContext);
|
||||
return true;
|
||||
} else if (action.equals("registerEvent")) {
|
||||
this.registerEvent(callbackContext);
|
||||
return true;
|
||||
} else if (action.equals("getUserInfo")) {
|
||||
this.getUserInfo(callbackContext);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void close(CallbackContext callbackContext) {
|
||||
// TODO: Implement close method
|
||||
callbackContext.success();
|
||||
}
|
||||
|
||||
private void registerEvent(CallbackContext callbackContext) {
|
||||
// 保存事件回调上下文,以便后续发送事件
|
||||
this.eventCallbackContext = callbackContext;
|
||||
|
||||
// 返回插件结果,但保持回调通道打开
|
||||
PluginResult pluginResult = new PluginResult(PluginResult.Status.OK);
|
||||
pluginResult.setKeepCallback(true);
|
||||
callbackContext.sendPluginResult(pluginResult);
|
||||
}
|
||||
|
||||
private void getUserInfo(CallbackContext callbackContext) {
|
||||
// TODO: Implement getUserInfo method
|
||||
callbackContext.success();
|
||||
}
|
||||
|
||||
// 触发导航到指定路由的事件
|
||||
private void navigateToRoute(String route, JSONObject parameters) {
|
||||
if (this.eventCallbackContext == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 创建事件数据
|
||||
JSONObject eventData = new JSONObject();
|
||||
eventData.put("eventName", "navigate");
|
||||
|
||||
JSONObject data = new JSONObject();
|
||||
data.put("route", route);
|
||||
|
||||
// 添加参数(如果有)
|
||||
if (parameters != null) {
|
||||
data.put("params", parameters);
|
||||
}
|
||||
|
||||
eventData.put("data", data);
|
||||
|
||||
// 发送事件到前端
|
||||
PluginResult pluginResult = new PluginResult(PluginResult.Status.OK, eventData);
|
||||
pluginResult.setKeepCallback(true);
|
||||
this.eventCallbackContext.sendPluginResult(pluginResult);
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
103
src/ios/ShutoApi.m
Normal file
103
src/ios/ShutoApi.m
Normal file
@@ -0,0 +1,103 @@
|
||||
/********* ShutoApi.m Cordova Plugin Implementation *******/
|
||||
|
||||
#import <Cordova/CDV.h>
|
||||
#import <WebKit/WebKit.h>
|
||||
|
||||
@interface ShutoApi : CDVPlugin <NSObject> {
|
||||
// 保存事件回调ID
|
||||
NSString* _eventCallbackId;
|
||||
}
|
||||
|
||||
- (void)close:(CDVInvokedUrlCommand*)command;
|
||||
- (void)registerEvent:(CDVInvokedUrlCommand*)command;
|
||||
- (void)getUserInfo:(CDVInvokedUrlCommand*)command;
|
||||
- (void)navigateToRoute:(NSString*)route parameters:(NSDictionary*)parameters __attribute__((objc_method_family(none)));
|
||||
@end
|
||||
|
||||
@implementation ShutoApi
|
||||
|
||||
- (void)close:(CDVInvokedUrlCommand*)command
|
||||
{
|
||||
// 调用JS方法通知前端关闭
|
||||
if ([self.webView isKindOfClass:[WKWebView class]]) {
|
||||
WKWebView *wkWebView = (WKWebView *)self.webView;
|
||||
NSString *jsString = @"window.postMessage('closeApp', '*')";
|
||||
[wkWebView evaluateJavaScript:jsString completionHandler:^(id _Nullable result, NSError * _Nullable error) {
|
||||
if (error) {
|
||||
NSLog(@"执行关闭JS方法失败: %@", error.localizedDescription);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
// 关闭当前VC的正确逻辑,根据不同的展示方式使用不同的关闭方法
|
||||
if (self.viewController.navigationController) {
|
||||
// 如果是通过push添加到导航栈的,使用popViewController
|
||||
[self.viewController.navigationController popViewControllerAnimated:YES];
|
||||
NSLog(@"从导航栈弹出视图控制器完成");
|
||||
} else if (self.viewController.presentingViewController) {
|
||||
// 如果是模态呈现的,使用dismiss
|
||||
[self.viewController dismissViewControllerAnimated:YES completion:^{
|
||||
NSLog(@"关闭当前视图控制器完成");
|
||||
}];
|
||||
} else if (self.viewController.parentViewController) {
|
||||
// 如果是作为子视图控制器添加的,移除子视图控制器
|
||||
[self.viewController willMoveToParentViewController:nil];
|
||||
[self.viewController.view removeFromSuperview];
|
||||
[self.viewController removeFromParentViewController];
|
||||
NSLog(@"从父视图控制器移除视图控制器完成");
|
||||
} else {
|
||||
// 如果直接添加了视图,移除视图
|
||||
[self.viewController.view removeFromSuperview];
|
||||
NSLog(@"从父视图移除视图控制器的视图完成");
|
||||
}
|
||||
|
||||
CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
|
||||
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
|
||||
}
|
||||
|
||||
- (void)registerEvent:(CDVInvokedUrlCommand*)command
|
||||
{
|
||||
// 保存事件回调ID,以便后续发送事件
|
||||
_eventCallbackId = command.callbackId;
|
||||
|
||||
// 返回插件结果,但保持回调通道打开
|
||||
CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
|
||||
[pluginResult setKeepCallbackAsBool:YES];
|
||||
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
|
||||
}
|
||||
|
||||
- (void)getUserInfo:(CDVInvokedUrlCommand*)command
|
||||
{
|
||||
// TODO: Implement getUserInfo method
|
||||
CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
|
||||
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
|
||||
}
|
||||
|
||||
// 触发导航到指定路由的事件
|
||||
- (void)navigateToRoute:(NSString*)route parameters:(NSDictionary*)parameters {
|
||||
if (!_eventCallbackId) {
|
||||
NSLog(@"ShutoApi: No event callback registered");
|
||||
return;
|
||||
}
|
||||
|
||||
// 创建事件数据
|
||||
NSMutableDictionary* eventData = [NSMutableDictionary dictionary];
|
||||
[eventData setObject:@"navigate" forKey:@"eventName"];
|
||||
|
||||
NSMutableDictionary* data = [NSMutableDictionary dictionary];
|
||||
[data setObject:route forKey:@"route"];
|
||||
|
||||
// 添加参数(如果有)
|
||||
if (parameters) {
|
||||
[data setObject:parameters forKey:@"params"];
|
||||
}
|
||||
|
||||
[eventData setObject:data forKey:@"data"];
|
||||
|
||||
// 发送事件到前端
|
||||
CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:eventData];
|
||||
[pluginResult setKeepCallbackAsBool:YES];
|
||||
[self.commandDelegate sendPluginResult:pluginResult callbackId:_eventCallbackId];
|
||||
}
|
||||
|
||||
@end
|
||||
59
www/ShutoApi.js
Normal file
59
www/ShutoApi.js
Normal file
@@ -0,0 +1,59 @@
|
||||
var exec = require('cordova/exec');
|
||||
var channel = require('cordova/channel');
|
||||
|
||||
// 初始化插件对象
|
||||
var ShutoApi = {
|
||||
close: function (success, error) {
|
||||
exec(success, error, 'ShutoApi', 'close', []);
|
||||
},
|
||||
getUserInfo: function (success, error) {
|
||||
exec(success, error, 'ShutoApi', 'getUserInfo', []);
|
||||
}
|
||||
};
|
||||
|
||||
// 创建事件发射器
|
||||
var eventEmitter = {
|
||||
listeners: {},
|
||||
on: function (eventName, callback) {
|
||||
if (!this.listeners[eventName]) {
|
||||
this.listeners[eventName] = [];
|
||||
}
|
||||
this.listeners[eventName].push(callback);
|
||||
},
|
||||
off: function (eventName, callback) {
|
||||
if (!this.listeners[eventName]) return;
|
||||
this.listeners[eventName] = this.listeners[eventName].filter(cb => cb !== callback);
|
||||
},
|
||||
emit: function (eventName, data) {
|
||||
if (!this.listeners[eventName]) return;
|
||||
this.listeners[eventName].forEach(callback => {
|
||||
try {
|
||||
callback(data);
|
||||
} catch (error) {
|
||||
console.error('Error in event listener for ' + eventName + ': ' + error);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// 添加事件监听方法
|
||||
ShutoApi.addEventListener = function (eventName, callback) {
|
||||
eventEmitter.on(eventName, callback);
|
||||
};
|
||||
|
||||
// 添加事件取消监听方法
|
||||
ShutoApi.removeEventListener = function (eventName, callback) {
|
||||
eventEmitter.off(eventName, callback);
|
||||
};
|
||||
|
||||
// 内部事件处理函数,用于接收原生代码的事件通知
|
||||
function onEvent(eventData) {
|
||||
if (eventData && eventData.eventName) {
|
||||
eventEmitter.emit(eventData.eventName, eventData.data);
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化时注册事件通道
|
||||
exec(onEvent, null, 'ShutoApi', 'registerEvent', []);
|
||||
|
||||
module.exports = ShutoApi;
|
||||
Reference in New Issue
Block a user