XWebview.ets 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405
  1. import web_webview from '@ohos.web.webview';
  2. import webview from '@ohos.web.webview';
  3. import { router, WebHeader } from '@kit.ArkUI';
  4. import { XDialogController } from '../dialog/XDialogController';
  5. import { XDialogList } from '../dialog/XDialogList';
  6. import { picker } from '@kit.CoreFileKit';
  7. import { BusinessError, pasteboard } from '@kit.BasicServicesKit';
  8. import { ToolsHelper } from '../utils/ToolsHelper';
  9. import { JsParams, XWebController, XWebParams } from '../utils/XWebHelper';
  10. import { WindowHelper } from '../utils/WindowHelper';
  11. import { SZYXLocalStorageHelper } from '../utils/SZYXLocalStorageHelper';
  12. import { SZYXLocalStorageKeys } from '../utils/SZYXLocalStorageKeys';
  13. import { XWebManager } from '../utils/XWebManager';
  14. import { TitleBarBtn } from '../view/SafeView';
  15. @Entry({ routeName: 'XWebview' })
  16. @Component
  17. export struct XWebview {
  18. @LocalStorageLink('refresh_web_base') @Watch('onRefresh') refresh_web_base: number = -1
  19. // 手机号
  20. @State headers?: Array<WebHeader> = (router.getParams() as XWebParams).headers
  21. @State url?: string = (router.getParams() as XWebParams).url
  22. @State jsParams?: JsParams = (router.getParams() as XWebParams).jsParams
  23. @State zoomAccess?: boolean = (router.getParams() as XWebParams).zoomAccess
  24. @State content?: string = (router.getParams() as XWebParams).content
  25. @State title?: string = (router.getParams() as XWebParams).title
  26. @State xController?: XWebController = (router.getParams() as XWebParams).controller
  27. // @State xJsController?: XWebController = (router.getParams() as XWebParams).jsController
  28. @State closeTag?: string = (router.getParams() as XWebParams).closeTag
  29. @State showMenu: boolean = (router.getParams() as XWebParams).showMenu ?? false
  30. @State clickMenu?: TitleBarBtn = (router.getParams() as XWebParams).clickMenu
  31. @State errorInfo: string | null = null
  32. @State progress: number = 0
  33. @State _uuid?: string = (router.getParams() as XWebParams)._uuid
  34. @State _uuidToHtml?: string = (router.getParams() as XWebParams)._uuidToHtml
  35. controller: web_webview.WebviewController = new web_webview.WebviewController();
  36. dialogController: XDialogController = {} as XDialogController
  37. ports: webview.WebMessagePort[] = [];
  38. onRefresh() {
  39. if (this.refresh_web_base !== -1) {
  40. this.controller.refresh()
  41. }
  42. }
  43. aboutToAppear(): void {
  44. XWebManager.onClose(() => {
  45. router.back()
  46. })
  47. if (this.xController) {
  48. this.xController.getUrl = () => {
  49. return this.controller.getUrl()
  50. }
  51. this.xController.getTitle = () => {
  52. return this.controller.getTitle()
  53. }
  54. this.xController.refresh = () => {
  55. this.controller.refresh()
  56. }
  57. }
  58. SZYXLocalStorageHelper.storage.setOrCreate('refresh_web_base', -1)
  59. SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.XWebViewCLose, undefined)
  60. webview.WebviewController.setWebDebuggingAccess(true);
  61. if (this._uuidToHtml) {
  62. XWebManager.addOnMessageToHtml(this._uuidToHtml, (msg) => {
  63. if (this.ports && this.ports[1]) {
  64. this.ports[1].postMessageEvent(msg);
  65. }
  66. })
  67. }
  68. }
  69. aboutToDisappear(): void {
  70. if (this.closeTag !== undefined) {
  71. SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.XWebViewCLose, this.closeTag)
  72. }
  73. XWebManager.removeOnMessageToWeb(this._uuidToHtml)
  74. XWebManager.removeOnMessage(this._uuid)
  75. XWebManager.removeOnMessageToHtml(this._uuidToHtml)
  76. if (this._uuidToHtml) {
  77. XWebManager.objs.delete(this._uuidToHtml)
  78. }
  79. try {
  80. this.controller.deleteJavaScriptRegister(this.jsParams?.name);
  81. } catch (error) {
  82. console.error(`ErrorCode: ${(error as BusinessError).code}, Message: ${(error as BusinessError).message}`);
  83. }
  84. }
  85. onBackPress(): boolean | void {
  86. if (this.controller.accessBackward()) {
  87. this.controller.backward()
  88. } else {
  89. ToolsHelper.doubleAndExit(() => {
  90. router.back()
  91. })
  92. }
  93. return true
  94. }
  95. build() {
  96. Column() {
  97. Row() {
  98. Row() {
  99. Button({ buttonStyle: ButtonStyleMode.TEXTUAL }) {
  100. Image($r('sys.media.ohos_ic_back'))
  101. .width(26).height(26)
  102. }.onClick(() => {
  103. this.onBackPress()
  104. })
  105. Button({ buttonStyle: ButtonStyleMode.TEXTUAL }) {
  106. Image($r('sys.media.ohos_ic_public_close'))
  107. .width(26).height(26)
  108. }.margin({ left: 12 })
  109. .onClick(() => {
  110. router.back()
  111. })
  112. }.width(65)
  113. Text(this.title)
  114. .maxLines(1)
  115. .fontColor('#222222')
  116. .fontSize(18)
  117. .textAlign(TextAlign.Center)
  118. .width('50%')
  119. .ellipsisMode(EllipsisMode.END)
  120. .textOverflow({
  121. overflow: TextOverflow.Ellipsis
  122. })
  123. Button({ buttonStyle: ButtonStyleMode.TEXTUAL }) {
  124. if (this.clickMenu?.img) {
  125. Image(this.clickMenu?.img)
  126. .width(15).height(15)
  127. } else if (this.clickMenu?.text) {
  128. Text(this.clickMenu.text)
  129. .fontColor(this.clickMenu?.color ?? '#17171A')
  130. .fontSize(13)
  131. .textAlign(TextAlign.Center)
  132. .ellipsisMode(EllipsisMode.END)
  133. .textOverflow({
  134. overflow: TextOverflow.Ellipsis
  135. })
  136. } else {
  137. Image($r('sys.media.ohos_ic_public_more'))
  138. .width(26).height(26)
  139. }
  140. }.width(65)
  141. .onClick(() => {
  142. if (this.clickMenu) {
  143. this.clickMenu?.onClick && this.clickMenu?.onClick()
  144. return
  145. }
  146. if (this.dialogController != null) {
  147. this.dialogController.open()
  148. }
  149. }).visibility(this.showMenu || this.clickMenu ? Visibility.Visible : Visibility.Hidden)
  150. }
  151. .width('100%')
  152. .height(45)
  153. .justifyContent(FlexAlign.SpaceBetween)
  154. .padding({ left: 15, right: 15 })
  155. Row().backgroundColor('#CCCCCC').height(2).width('100%')
  156. Progress({ value: this.progress, type: ProgressType.Linear })
  157. .visibility(this.progress > 95 || this.progress == 0 ? Visibility.None : Visibility.Visible)
  158. .width('100%')
  159. Web({ src: this.url ?? '', controller: this.controller })
  160. .javaScriptAccess(true)
  161. .javaScriptProxy({
  162. object: XWebManager.objs.get(this._uuidToHtml),
  163. name: this.jsParams?.name,
  164. methodList: this.jsParams?.methodList,
  165. controller: this.controller
  166. })
  167. .domStorageAccess(true)
  168. .geolocationAccess(true)
  169. .width('100%')
  170. .height('100%')
  171. .visibility(this.errorInfo == null ? Visibility.Visible : Visibility.None)
  172. .mixedMode(MixedMode.All)//允许加载HTTP和HTTPS混合内容
  173. .zoomAccess(this.zoomAccess ?? false)//不支持手势进行缩放
  174. .mediaPlayGestureAccess(false)//有声视频播放不需要用户手动点击
  175. .cacheMode(CacheMode.None)//设置缓存模式
  176. .onConfirm((event) => { // 自定义Confirm弹窗
  177. if (event) {
  178. console.log("event.url:" + event.url)
  179. console.log("event.message:" + event.message)
  180. AlertDialog.show({
  181. title: '提示',
  182. message: event.message,
  183. primaryButton: {
  184. value: '取消',
  185. action: () => {
  186. event.result.handleCancel()
  187. }
  188. },
  189. secondaryButton: {
  190. value: '确定',
  191. action: () => {
  192. event.result.handleConfirm()
  193. }
  194. },
  195. cancel: () => {
  196. event.result.handleCancel()
  197. }
  198. })
  199. }
  200. return true
  201. })
  202. .onAlert((event) => { // 自定义Alert弹窗
  203. if (event) {
  204. console.log("event.url:" + event.url)
  205. console.log("event.message:" + event.message)
  206. AlertDialog.show({
  207. title: '提示',
  208. message: event.message,
  209. secondaryButton: {
  210. value: '确定',
  211. action: () => {
  212. event.result.handleConfirm()
  213. }
  214. }
  215. })
  216. }
  217. return true
  218. })
  219. .onDownloadStart((event) => { // 下载文件
  220. if (event) {
  221. console.log('url:' + event.url)
  222. console.log('userAgent:' + event.userAgent)
  223. console.log('contentDisposition:' + event.contentDisposition)
  224. console.log('contentLength:' + event.contentLength)
  225. console.log('mimetype:' + event.mimetype)
  226. }
  227. })
  228. .onPageEnd((url) => {
  229. ToolsHelper.log(url.url)
  230. // 1、创建两个消息端口。
  231. this.ports = this.controller.createWebMessagePorts();
  232. this.ports[1].onMessageEvent((result: webview.WebMessage) => {
  233. if (typeof (result) === 'string') {
  234. // ToolsHelper.log(result, typeof (result))
  235. XWebManager.sendMessage(result)
  236. }
  237. })
  238. this.controller.postMessage('__init_port__', [this.ports[0]], '*');
  239. })
  240. .onControllerAttached(() => {
  241. if (this.jsParams?.controller && this._uuidToHtml) {
  242. XWebManager.addOnMessageToWeb(this._uuidToHtml, (msg) => {
  243. this.controller.runJavaScript(msg)
  244. })
  245. }
  246. if (this.content) {
  247. try {
  248. this.controller.loadData(this.content, 'text/html', 'UTF-8', " ", " ")
  249. } catch (e) {
  250. ToolsHelper.showAlertDialog({
  251. title: '警告',
  252. msg: e.message ?? '加载内容失败',
  253. action: {
  254. onClick: () => {
  255. router.back()
  256. }
  257. }
  258. })
  259. }
  260. } else if (this.url) {
  261. if (this.headers) {
  262. this.controller.loadUrl(this.url, this.headers);
  263. } else {
  264. this.controller.loadUrl(this.url);
  265. }
  266. } else {
  267. ToolsHelper.showAlertDialog({
  268. title: '警告',
  269. msg: '请传入url或content',
  270. action: {
  271. onClick: () => {
  272. router.back()
  273. }
  274. }
  275. })
  276. }
  277. try {
  278. let userAgent = this.controller.getUserAgent() + '/szyx_sdk';
  279. this.controller.setCustomUserAgent(userAgent);
  280. } catch (error) {
  281. console.error(`ErrorCode: ${(error as BusinessError).code}, Message: ${(error as BusinessError).message}`);
  282. }
  283. })
  284. .onErrorReceive((event) => { // 加载失败
  285. if (this.progress > 65) {
  286. return
  287. }
  288. if (event) {
  289. this.errorInfo = `错误码:${event.error.getErrorCode()}\n${event.error.getErrorInfo()}`
  290. } else {
  291. this.errorInfo = '错误码:-1\n未知错误'
  292. }
  293. ToolsHelper.log(JSON.stringify(event), this.url)
  294. })
  295. .onHttpErrorReceive((event) => { // 加载失败
  296. if (this.progress > 65) {
  297. return
  298. }
  299. if (event) {
  300. this.errorInfo = `错误码:${event.response.getResponseCode()}\n${event.response.getReasonMessage()}`
  301. } else {
  302. this.errorInfo = '错误码:-1\n未知错误'
  303. }
  304. ToolsHelper.log(this.errorInfo, this.url)
  305. })
  306. .onProgressChange((event) => { // 加载进度
  307. if (event) {
  308. console.log('newProgress:' + event.newProgress)
  309. this.progress = event.newProgress
  310. }
  311. })
  312. .onTitleReceive((event) => {
  313. // 如果没有传输title,则从H5获取title赋值
  314. if (event && !this.title) {
  315. this.title = event.title
  316. }
  317. })
  318. .onShowFileSelector((event) => { // 选择文件
  319. console.log('MyFileUploader onShowFileSelector invoked')
  320. const documentSelectOptions = new picker.DocumentSelectOptions()
  321. let uri: string | null = null;
  322. const documentViewPicker = new picker.DocumentViewPicker();
  323. documentViewPicker.select(documentSelectOptions).then((documentSelectResult) => {
  324. uri = documentSelectResult[0];
  325. console.info('documentViewPicker.select to file succeed and uri is:' + uri);
  326. if (event) {
  327. event.result.handleFileList([uri]);
  328. }
  329. }).catch((err: BusinessError) => {
  330. if (event) {
  331. event.result.handleFileList([])
  332. }
  333. ToolsHelper.showMessage(`Invoke documentViewPicker.select failed, code is ${err.code}, message is ${err.message}`)
  334. console.error(`Invoke documentViewPicker.select failed, code is ${err.code}, message is ${err.message}`);
  335. })
  336. return true
  337. })
  338. Column() {
  339. Text(this.errorInfo)
  340. Button('点击重试')
  341. .onClick(() => {
  342. this.controller.refresh()
  343. this.errorInfo = null
  344. this.progress = 0
  345. })
  346. .margin({ top: 30 })
  347. }
  348. .visibility(this.errorInfo == null ? Visibility.None : Visibility.Visible)
  349. .width('100%')
  350. .height('100%')
  351. .justifyContent(FlexAlign.Center)
  352. .padding({ bottom: 80 })
  353. XDialogList({
  354. // 控制器
  355. controller: this.dialogController,
  356. // 标题(可选)
  357. title: '选择您的操作',
  358. // 选择内容列表
  359. values: ['刷新', '浏览器打开', '复制地址'],
  360. // 用户选择事件
  361. onSelected: (index: number, value: string) => {
  362. if (index === 0) {
  363. this.controller.refresh()
  364. } else if (index === 1) {
  365. } else {
  366. pasteboard.getSystemPasteboard()
  367. .setData(pasteboard.createData(pasteboard.MIMETYPE_TEXT_PLAIN, this.controller.getUrl()), () => {
  368. ToolsHelper.showMessage('已复制到剪切板')
  369. })
  370. }
  371. },
  372. // 用户取消事件
  373. onCancel: () => {
  374. // ToolsHelper.showMessage('用户取消操作')
  375. },
  376. // 是否可取消(点击空白处,或者物理返回键)
  377. autoCancel: true
  378. })
  379. }.width('100%').height('100%')
  380. .padding({
  381. top: WindowHelper.topRectHeight,
  382. bottom: WindowHelper.bottomRectHeight
  383. })
  384. }
  385. }