import 'dart:convert'; import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_inappwebview/flutter_inappwebview.dart'; import 'package:get/get.dart'; import 'package:image_picker/image_picker.dart'; // import 'package:open_file/open_file.dart'; import '../../../../common/api/index.dart'; import '../../../../common/models/index.dart'; import '../../../../common/routers/index.dart'; import '../../../../common/utils/index.dart'; import '../../../../common/values/index.dart'; import '../../../../common/widgets/index.dart'; import '../../../common/create_form/index.dart'; import '../../../common/preview_image/index.dart'; import '../../../common/process_webview/index.dart'; import '../../../common/video_player/index.dart'; import '../cms_app/index.dart'; import 'index.dart'; class CmsDocumentController extends GetxController implements JsNavigationInterface { CmsDocumentController(); final state = CmsDocumentState(); final channel = O2FlutterMethodChannelUtils(); // webview控件的控制器 final GlobalKey webViewKey = GlobalKey(); InAppWebViewController? webviewController; // webview 通用方法 final webviewHelper = WebviewHelper(); // 安装转化js var isInstallJsName = false; // 拍照 图片选择 final ImagePicker _imagePicker = ImagePicker(); final _eventBus = EventBus(); String? docId; // cms 创建文档使用的对象 CmsAppData? _app; CmsCategoryData? _category; Map? _cmsMessageData; /// 在 widget 内存中分配后立即调用。 @override void onInit() { super.onInit(); } /// 在 onInit() 之后调用 1 帧。这是进入的理想场所 @override void onReady() { var map = Get.arguments; if (map != null) { state.title = map["title"] ?? "cms_document_title".tr; OLogger.d("文档标题 title: ${state.title} "); String id = map["documentId"] ?? ""; OLogger.d("文档id: $id "); if (id.isNotEmpty) { docId = id; _initCmsUrl(id, map["options"]); } } super.onReady(); } @override void closeWindow() { OLogger.d('执行了 jsapi closeWindow '); _eventBus.emit(EventBus.cmsDocumentCloseMsg, 'close'); Get.back(); } @override void goBack() { OLogger.d('执行了 jsapi goBack '); tapBackBtn(); } @override void setNavigationTitle(String title) { OLogger.d('执行了 jsapi setNavigationTitle $title '); state.title = title; } void _initCmsUrl(String id, Map? options) async { var url = O2ApiManager.instance.getCmsDocumentUrl(id) ?? ''; if (options != null) { var readonly = options['readonly']; if (readonly != null && (readonly == 'false' || readonly == false)) { url = O2ApiManager.instance.getCmsDocumentEditUrl(id) ?? ''; } for (var element in options.entries) { OLogger.i('参数 ${element.key} : ${element.value}'); url += '&${element.key}=${Uri.encodeComponent('${element.value}')}'; } } if (url.isNotEmpty) { final uurl = Uri.parse(url); var host = O2ApiManager.instance.getWebHost(); var domain = uurl.host; var tokenName = O2ApiManager.instance.tokenName; var token = O2ApiManager.instance.o2User?.token ?? ''; OLogger.d( "加载webview cookie,url: $url domain: $domain tokenName: $tokenName token: $token"); CookieManager cookieManager = CookieManager.instance(); await cookieManager.deleteAllCookies(); await cookieManager.setCookie( url: uurl, name: tokenName, value: token, domain: (domain.isEmpty ? host : domain)); state.url = url; } OLogger.d("打开网址: $url"); } /// /// 点击appbar 返回按钮 /// void tapBackBtn() async { OLogger.d('执行了 tapBackBtn '); if (await webviewController?.canGoBack() == true) { webviewController?.goBack(); } else { closeWindow(); } } /// 加载js通道 void setupWebviewJsHandler(InAppWebViewController c) async { webviewController = c; webviewController?.addJavaScriptHandler( handlerName: O2.webviewChannelNameCommonKey, callback: (msgs) { OLogger.d( "js 通信, name: ${O2.webviewChannelNameCommonKey} msg: $msgs"); if (msgs.isNotEmpty) { String msg = msgs[0] as String? ?? ""; _jsChannelMessageReceived(msg); } }); webviewHelper.setupWebviewJsHandler(c); webviewHelper.setupJsNavigationInterface(this); } /// webview 加载进度 void progressChanged(InAppWebViewController c, int p) { OLogger.d("o2Webview process progress: $p"); // 这里把 inappwebview的 js handler 方式修改成 我们自定义的 if (p == 100 && !isInstallJsName) { isInstallJsName = true; // o2android var js = ''' if (window.flutter_inappwebview && window.flutter_inappwebview.callHandler) { window.o2android = {}; window.o2android.postMessage = function(message){ window.flutter_inappwebview.callHandler('o2android', message); }; } '''; c.evaluateJavascript(source: js); OLogger.i("执行o2android转化js完成。。。"); webviewHelper.changeJsHandlerFunName(c); } } /// 打开附件 void _openAttachmentFile(String filePath, String fileName) { if (filePath.isImageFileName) { PreviewImagePage.open(filePath, fileName); } else if (filePath.isVideoFileName) { VideoPlayerPage.openLocalVideo(filePath, title: fileName); } else { channel.openLocalFile(filePath); } } /// 接收js通道返回数据 /// h5上调用js执行flutter这边的原生方法 /// // void jsChannelMessageReceived(JavascriptMessage message) { // if (message.message.isNotEmpty) { void _jsChannelMessageReceived(String message) { OLogger.d("h5执行原生方法,message: $message"); if (message.isEmpty) { return; } var jsMessage = JsMessage.fromJson(O2Utils.parseStringToJson(message)); switch (jsMessage.type) { case "closeDocumentWindow": OLogger.d("关闭表单"); closeWindow(); break; case "downloadAttachment": _downloadAttachment(jsMessage.data); break; case "uploadAttachment": _uploadAttachment(jsMessage.data); break; case "uploadAttachmentForDatagrid": _uploadAttachment(jsMessage.data, forGrid: true); break; case "replaceAttachment": _replaceAttachment(jsMessage.data); break; case "replaceAttachmentForDatagrid": _replaceAttachment(jsMessage.data, forGrid: true); break; case "uploadImage2FileStorage": // imageclipper 图片上传控件 _uploadImage2FileStorage(jsMessage.data); break; case 'openO2Work': _openWork(jsMessage.data); break; case 'createO2CmsDocument': _createO2CmsDocument(jsMessage.data); break; case 'openO2CmsApplication': _openO2CmsApplication(jsMessage.data); break; case 'openO2CmsDocument': _openO2CmsDocument(jsMessage.data); break; default: OLogger.e('错误的类型,$message'); break; } _executeCallbackJs(jsMessage.callback, null); } /// 选择图片 或者 拍照 /// 图片控件使用 Future _pickImageByType(int type, Map data) async { XFile? file; if (type == 0) { // 相册 file = await _imagePicker.pickImage(source: ImageSource.gallery); } else if (type == 1) { // 拍照 if (!await O2Utils.cameraPermission()) { return; } file = await _imagePicker.pickImage(source: ImageSource.camera); } if (file != null) { // var mwfId: String = "",//控件id // var callback: String = "",//回调函数 // var referencetype: String = "",//上传文件服务器的业务类型名称 // var reference: String = "",//关联业务id // var fileId: String = ""//上传返回的文件id String referencetype = data['referencetype'] ?? ''; String reference = data['reference'] ?? ''; String callback = data['callback'] ?? ''; if (referencetype.isEmpty || reference.isEmpty || callback.isEmpty) { Loading.showError('args_error'.tr); return; } Loading.show(); var id = await FileAssembleService.to.uploadImageWithReferencetype( referencetype, reference, File(file.path)); if (id != null && id.id != null) { data['fileId'] = id.id; final back = json.encode(data); final js = '$callback(\'$back\')'; OLogger.d('执行js :$js'); await webviewController?.evaluateJavascript(source: js); Loading.dismiss(); } } } /// /// 图片上传控件 /// void _uploadImage2FileStorage(Map? data) { final myContext = Get.context; if (data != null && myContext != null) { O2UI.showBottomSheetWithCancel(myContext, [ ListTile( onTap: () { Navigator.pop(myContext); _pickImageByType(0, data); }, title: Align( alignment: Alignment.center, child: Text('album'.tr, style: Theme.of(myContext).textTheme.bodyMedium), ), ), const Divider(height: 1), ListTile( onTap: () { Navigator.pop(myContext); _pickImageByType(1, data); }, title: Align( alignment: Alignment.center, child: Text('take_photo'.tr, style: Theme.of(myContext).textTheme.bodyMedium), ), ), ]); } } /// /// 下载附件 /// void _downloadAttachment(Map? data) async { OLogger.d("下载附件"); if (data != null) { String attachmentId = data["attachmentId"] ?? ""; if (attachmentId.isNotEmpty && docId != null) { Loading.show(); // 请求附件对象信息 DocAttachmentInfo? info = await CmsAssembleControlService.to .getAttachmentInfoByDocId(docId!, attachmentId); if (info != null && info.name != null && info.id != null) { String? filePath = await O2FilePathUtil.getCmsFileDownloadLocalPath( info.id!, info.name!); if (filePath == null || filePath.isEmpty) { Loading.showError('process_work_download_file_no_path'.tr); return; } var file = File(filePath); if (file.existsSync()) { Loading.dismiss(); _openAttachmentFile(filePath, info.name!); } else { // 下载附件 var donwloadUrl = CmsAssembleControlService.to .downloadAttachmentUrl(attachmentId); try { await O2HttpClient.instance.downloadFile(donwloadUrl, filePath); Loading.dismiss(); _openAttachmentFile(filePath, info.name!); } on Exception catch (e) { Loading.showError(e.toString()); } } } else { Loading.showError('process_work_download_get_attachmentinfo_fail'.tr); } } } } /// /// 上传附件 /// void _uploadAttachment(Map? data, {bool forGrid = false}) async { if (data != null && docId != null) { String site = data["site"] ?? ""; String param = data["param"] ?? ""; OLogger.d('上传附件 site $site , param $param'); O2Utils.pickerFileOrImage((paths) { _uploadAttachmentBegin(paths, site, param, forGrid); }, allowMultiple: true); } } Future _uploadAttachmentBegin(List paths, String site, String param, bool forGrid) async { Loading.show(); int errorNumber = 0; for (var element in paths) { if (element != null && element.isNotEmpty) { var resId = await CmsAssembleControlService.to .uploadAttachment(docId!, site, File(element)); if (resId != null && resId.id != null) { if (forGrid) { webviewController?.evaluateJavascript( source: "layout.app.appForm.uploadedAttachmentDatagrid(\"$site\", \"${resId.id}\", \"$param\")"); } else { webviewController?.evaluateJavascript( source: "layout.app.appForm.uploadedAttachment(\"$site\", \"${resId.id}\")"); } OLogger.d('上传附件成功 ${resId.id} '); } else { errorNumber++; } } } if (errorNumber > 0) { Loading.showError( 'process_attachment_upload_error'.trArgs(['$errorNumber'])); } else { Loading.dismiss(); } } /// /// 替换附件 /// void _replaceAttachment(Map? data, {bool forGrid = false}) async { if (data != null && docId != null) { String site = data["site"] ?? ""; String attachmentId = data["attachmentId"] ?? ""; String param = data["param"] ?? ""; OLogger.d('替换附件 site $site , attachmentId $attachmentId , param $param'); O2Utils.pickerFileOrImage((paths) { _replaceAttachmentBegin(paths, attachmentId, site, param, forGrid); }); } } Future _replaceAttachmentBegin(List paths, String attachmentId, String site, String param, bool forGrid) async { if (paths.isEmpty) { return; } String path = paths[0] ?? ''; if (path.isEmpty) { return; } Loading.show(); OLogger.d('选择了文件:$path'); var resId = await ProcessSurfaceService.to .replaceAttachment(docId!, attachmentId, File(path)); if (resId != null && resId.id != null) { if (forGrid) { webviewController?.evaluateJavascript( source: "layout.app.appForm.replacedAttachmentDatagrid(\"$site\", \"${resId.id}\", \"$param\")"); } else { webviewController?.evaluateJavascript( source: "layout.app.appForm.replacedAttachment(\"$site\", \"${resId.id}\")"); } OLogger.d('替换附件成功 ${resId.id} '); Loading.dismiss(); } } /// 打开工作 /// workId: String, workCompletedId: String, title: String void _openWork(Map? data) { OLogger.d('===> _openWork, data: $data'); if (data != null) { String workid = data['workId'] ?? ''; if (workid.isEmpty) { workid = data['workCompletedId'] ?? ''; } String darftId = data['draftId'] ?? ''; if (workid.isNotEmpty) { ProcessWebviewPage.open(workid, title: data['title'] ?? 'process_work_no_title_no_process'.tr); } else if (darftId.isNotEmpty) { ProcessWebviewPage.openDraftById(darftId, title: data['title'] ?? 'process_work_no_title_no_process'.tr); } } } /// 打开信息文档 /// docId: String, title: String, options: Map? void _openO2CmsDocument(Map? data) { if (data != null) { String docId = data['docId'] ?? ''; if (docId.isNotEmpty) { CmsDocumentPage.open(docId, title: data['title'] ?? '', options: data['options']); } } } /// 打开信息中心 /// appId: String, title: String void _openO2CmsApplication(Map? data) async { if (data != null && data['appId'] != null && data['appId'] is String) { var list = await CmsAssembleControlService.to.listAppWithCategoryUserCanView(); if (list != null) { var app = list.firstWhereOrNull((element) => element.id == data['appId'] || element.appAlias == data['appId'] || element.appName == data['appId']); if (app != null) { CmsAppPage.open(app); // 打开对应的 return; } } } // 没有参数 直接打开cms Get.toNamed(O2OARoutes.appCms); } /// /// 创建文档 /// * 创建文档 目前只有 column 和 category 有效果 /* { "column" : column, //(string)可选,内容管理应用(栏目)的名称、别名或ID "category" : category, //(string)可选,要创建的文档所属的分类的名称、别名或ID "data" : data, //(json object)可选,创建文档时默认的业务数据 "identity" : identity, //(string)可选,创建文档所使用的身份。如果此参数为空,且当前人有多个身份的情况下,会弹出身份选择对话框;否则使用默认身份。 "callback" : callback, //(funcation)可选,文档创建后的回调函数。 "target" : target, //(boolean)可选,为true时,在当前页面打开创建的文档;否则打开新窗口。默认false。 "latest" : latest, //(boolean)可选,为true时,如果当前用户已经创建了此分类的文档,并且没有发布过,直接调用此文档为新文档;否则创建一个新文档。默认true。 "selectColumnEnable" : selectColumnEnable, //(boolean)可选,是否可以选择应用和分类进行创建文档。有category参数时为默认false,否则默认为true。 "ignoreTitle" : ignoreTitle //(boolean)可选,值为false时,创建的时候需要强制填写标题,默认为false。 "restrictToColumn" : restrictToColumn //(boolean)可选,值为true时,会限制在传入的栏目中选择分类,默认为false。 } */ /// void _createO2CmsDocument(Map? data) async { OLogger.d('===> _createO2CmsDocument, data: $data'); _app = null; _category = null; if (data != null) { _cmsMessageData = data; var categoryId = _cmsMessageData?['category']; if (categoryId != null && categoryId is String && categoryId.isNotEmpty) { // 根据分类id查询 分类和应用 _category = await CmsAssembleControlService.to.getCategory(categoryId); if (_category != null && _category?.appId != null && _app == null) { _app = await CmsAssembleControlService.to.getApp(_category!.appId!); } } else { var appId = _cmsMessageData?['column']; if (appId != null && appId is String && appId.isNotEmpty) { // 只有应用id的情况 弹出分类选择器 _app = await CmsAssembleControlService.to .getAppCanPublishCategories(appId); if (_app != null && _app?.wrapOutCategoryList != null && _app!.wrapOutCategoryList!.isNotEmpty) { var cList = _app!.wrapOutCategoryList!; if (cList.length == 1) { _category = cList[0]; } else { _showCmsCategoryChoose(cList); return; } } } } } // 传入参数不足,选择分类 if (_app == null || _category == null) { var category = await Get.toNamed(O2OARoutes.appCmsCategoryPicker); if (category != null && category is CmsCategoryData) { OLogger.d('选择了分类: ${category.toJson()}'); _app = category.withApp; _category = category; } } _startCreateDocument(); } /// 选择分类 void _showCmsCategoryChoose(List list) { final c = Get.context; if (c != null) { O2UI.showBottomSheetWithCancel( c, list .map((e) => ListTile( onTap: () { Navigator.pop(c); _category = e; _startCreateDocument(); }, title: Align( alignment: Alignment.center, child: Text(e.categoryName ?? '', style: Theme.of(c).textTheme.bodyMedium), ), )) .toList()); } } /// /// 参数查询完成后开始创建文档 /// _app、_category 都赋值后 /// void _startCreateDocument() async { if (_app == null || _category == null) { Loading.toast('cms_create_document_no_args'.tr); return; } OLogger.d('创建文档,app:${_app?.toJson()}'); OLogger.d('创建文档,category:${_category?.toJson()}'); final config = _app?.config ?? '{}'; final configMap = O2Utils.parseStringToJson(config); final messageData = _cmsMessageData ?? {}; bool ignoreTitle = false; // 是否忽略标题 bool latest = true; // 是否查询草稿 if (messageData.containsKey('ignoreTitle')) { ignoreTitle = messageData['ignoreTitle'] ?? false; } else if (configMap.containsKey('ignoreTitle')) { ignoreTitle = configMap['ignoreTitle'] ?? false; } if (messageData.containsKey('latest')) { latest = messageData['latest'] ?? true; } else if (configMap.containsKey('latest')) { latest = configMap['latest'] ?? true; } if (latest) { var drafts = await CmsAssembleControlService.to.listDocumentDraft(_category!.id!); if (drafts != null && drafts.isNotEmpty) { OLogger.d('有草稿。。。。'); CmsDocumentPage.open(drafts[0].id!, title: drafts[0].title ?? '', options: {"readonly": false}); return; } } CreateFormPage.startCmsDoc(ignoreTitle, category: _category!, cmsData: messageData['data']); } /// /// 如果有callback函数 就执行 /// void _executeCallbackJs(String? callback, dynamic result) { if (callback != null && callback.isNotEmpty) { if (result != null) { webviewController?.evaluateJavascript(source: '$callback($result)'); } else { webviewController?.evaluateJavascript(source: '$callback()'); } } } /// 复制当前链接 void copyLink() async { final url = await webviewController?.getUrl(); if (url != null) { Clipboard.setData(ClipboardData(text: url.toString())); Loading.toast('im_chat_success_copy'.tr); } } }