From bd0ed358307279a179b8452bf925912db87fc965 Mon Sep 17 00:00:00 2001 From: API Converter Bot Date: Tue, 13 Jan 2026 14:26:48 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0ShutoApi=20Cordova?= =?UTF-8?q?=E6=8F=92=E4=BB=B6=E5=9F=BA=E7=A1=80=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 实现ShutoApi Cordova插件的基础功能,包括: - 添加.gitignore忽略.history目录 - 创建package.json定义插件基本信息 - 编写plugin.xml配置插件 - 实现前端JavaScript接口 - 添加Android和iOS原生实现 - 编写README文档说明使用方法 --- .gitignore | 1 + README.md | 148 ++++++++++++++++++++++++++++++++++++++ package.json | 34 +++++++++ plugin.xml | 2 + src/android/ShutoApi.java | 84 ++++++++++++++++++++++ src/ios/ShutoApi.m | 103 ++++++++++++++++++++++++++ www/ShutoApi.js | 59 +++++++++++++++ 7 files changed, 431 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 package.json create mode 100644 plugin.xml create mode 100644 src/android/ShutoApi.java create mode 100644 src/ios/ShutoApi.m create mode 100644 www/ShutoApi.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c138f46 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.history/ \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..c58aded --- /dev/null +++ b/README.md @@ -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. 事件监听是持久化的,只要插件处于活动状态,就会保持监听。 diff --git a/package.json b/package.json new file mode 100644 index 0000000..423a4cf --- /dev/null +++ b/package.json @@ -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" + } + } + } +} \ No newline at end of file diff --git a/plugin.xml b/plugin.xml new file mode 100644 index 0000000..027798d --- /dev/null +++ b/plugin.xml @@ -0,0 +1,2 @@ + +ShutoApi \ No newline at end of file diff --git a/src/android/ShutoApi.java b/src/android/ShutoApi.java new file mode 100644 index 0000000..8b7df63 --- /dev/null +++ b/src/android/ShutoApi.java @@ -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(); + } + } +} diff --git a/src/ios/ShutoApi.m b/src/ios/ShutoApi.m new file mode 100644 index 0000000..f00e996 --- /dev/null +++ b/src/ios/ShutoApi.m @@ -0,0 +1,103 @@ +/********* ShutoApi.m Cordova Plugin Implementation *******/ + +#import +#import + +@interface ShutoApi : CDVPlugin { + // 保存事件回调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 diff --git a/www/ShutoApi.js b/www/ShutoApi.js new file mode 100644 index 0000000..1c6cbc2 --- /dev/null +++ b/www/ShutoApi.js @@ -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;