feat: 添加ShutoApi Cordova插件基础实现

实现ShutoApi Cordova插件的基础功能,包括:
- 添加.gitignore忽略.history目录
- 创建package.json定义插件基本信息
- 编写plugin.xml配置插件
- 实现前端JavaScript接口
- 添加Android和iOS原生实现
- 编写README文档说明使用方法
This commit is contained in:
API Converter Bot
2026-01-13 14:26:48 +08:00
commit bd0ed35830
7 changed files with 431 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
.history/

148
README.md Normal file
View 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
View 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
View 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
View 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
View 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
View 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;