DesformView.vue 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  1. <template>
  2. <view>
  3. <!-- #ifdef H5 -->
  4. <iframe v-if="show" v-bind="iframeProps"></iframe>
  5. <!-- #endif -->
  6. <!-- #ifdef MP-WEIXIN -->
  7. <web-view
  8. :src="iframeSrc"
  9. @message="handleMessage"
  10. @load="handleLoad"
  11. @error="handleError"
  12. ></web-view>
  13. <!-- #endif -->
  14. </view>
  15. </template>
  16. <script lang="ts" setup>
  17. import { ref, reactive, nextTick, watch } from 'vue'
  18. import { http } from '@/utils/http'
  19. import { useToast, useMessage, useNotify, dayjs } from 'wot-design-uni'
  20. import { useRouter } from '@/plugin/uni-mini-router'
  21. import { randomString } from '@/common/uitls'
  22. import { useUserStore } from '@/store/user'
  23. import { isH5, isApp, isMp } from '@/utils/platform'
  24. const api = {
  25. add: '/desform/data/add',
  26. edit: '/desform/data/edit',
  27. online: '/online/cgform/api/crazyForm',
  28. }
  29. const props = defineProps({
  30. // add = 新增,edit = 修改,detail = 只读查看
  31. mode: {
  32. type: String,
  33. required: true,
  34. },
  35. desformCode: {
  36. type: String,
  37. default: '',
  38. },
  39. dataId: {
  40. type: String,
  41. default: null,
  42. },
  43. alert: {
  44. type: Boolean,
  45. default: true,
  46. },
  47. height: {
  48. type: [String, Number],
  49. default: null,
  50. },
  51. pageTitle: {
  52. type: String,
  53. default: '',
  54. },
  55. })
  56. const emit = defineEmits(['dialogChange', 'success', 'error'])
  57. const userStore = useUserStore()
  58. const toast = useToast()
  59. const show = ref(true)
  60. const pageLoading = ref(true)
  61. const loading = ref(true)
  62. const messageId = ref(randomString('16'))
  63. const iframeHeight = ref(0)
  64. const latitude = ref()
  65. const longitude = ref()
  66. // 小程序webview
  67. const iframeSrc = computed(() => {
  68. let domainURL = import.meta.env.VITE_SERVER_BASEURL
  69. let dataId = props.dataId == null ? 'add' : props.dataId
  70. // 拼接 iframe 的 src 属性
  71. let src = `${domainURL}/desform/${props.mode}/${props.desformCode}`
  72. // - 修改和查看都需要传递 dataId 参数
  73. if (props.mode === 'edit' || props.mode === 'detail') {
  74. src += `/${dataId}`
  75. }
  76. let token = userStore.userInfo.token
  77. let tenantId = userStore.userInfo.tenantId
  78. src += `?messageId=${messageId.value}&token=${token}&tenantId=${tenantId}&platform=mini_program`
  79. if (latitude.value && longitude.value) {
  80. src += `&latitude=${latitude.value}&longitude=${longitude.value}`
  81. }
  82. src += `&innerRequest=false` // 关闭iframe的内部请求
  83. src += `&disableScroll=false` // 关闭iframe的内部滚动
  84. src += `&pageTitle=${encodeURIComponent(props.pageTitle)}`
  85. console.log('src======', src)
  86. return src
  87. })
  88. // H5平台
  89. const iframeProps = computed(() => {
  90. let domainURL = import.meta.env.VITE_SERVER_BASEURL
  91. let dataId = props.dataId == null ? 'add' : props.dataId
  92. // 拼接 iframe 的 src 属性
  93. let src = `${domainURL}/desform/${props.mode}/${props.desformCode}`
  94. // - 修改和查看都需要传递 dataId 参数
  95. if (props.mode === 'edit' || props.mode === 'detail') {
  96. src += `/${dataId}`
  97. }
  98. let token = userStore.userInfo.token
  99. let tenantId = userStore.userInfo.tenantId
  100. src += `?messageId=${messageId.value}&token=${token}&tenantId=${tenantId}`
  101. if (isApp) {
  102. src += `&platform=android_app`
  103. } else {
  104. src += `&platform=h5`
  105. }
  106. if (latitude.value && longitude.value) {
  107. src += `&latitude=${latitude.value}&longitude=${longitude.value}`
  108. }
  109. src += `&innerRequest=false` // 关闭iframe的内部请求
  110. src += `&disableScroll=false` // 关闭iframe的内部滚动
  111. console.log('src======', src)
  112. return {
  113. src,
  114. style: {
  115. width: '100%',
  116. height: typeof props.height === 'number' ? `${props.height}px` : props.height,
  117. overflow: 'hidden',
  118. transition: props.height ? null : 'height 0.3s',
  119. },
  120. frameborder: '0',
  121. }
  122. })
  123. const handleLoad = () => {
  124. uni.hideLoading()
  125. console.log('小程序webview 加载成功...')
  126. }
  127. const handleError = () => {
  128. console.log('小程序webview 加载失败...')
  129. }
  130. // 小程序中 handleMessage 方法必须是返回(iframe内调用uni.navigateBack)才会触发
  131. const handleMessage = (res) => {
  132. console.log('小程序回调的信息', res)
  133. let data: any = {}
  134. if (res?.mp?.type) {
  135. data = data.mp
  136. } else if (res.type) {
  137. data = res
  138. }
  139. if (data.type) {
  140. switch (data.type) {
  141. // case 'close':
  142. // handleClose();
  143. // break;
  144. case 'message':
  145. saveAllData(data.detail.data[data.detail.data.length - 1])
  146. break
  147. case 'height-change':
  148. iframeHeight.value = data + 10
  149. pageLoading.value = false
  150. break
  151. case 'dialog-change':
  152. emit('dialogChange', data)
  153. break
  154. }
  155. }
  156. }
  157. // app平台 webview
  158. const webview = () => {
  159. const navBarHeight = 44 // 导航栏高度
  160. const statusBarHeight = uni.getSystemInfoSync().statusBarHeight || 0
  161. let wv = plus.webview.create(
  162. '',
  163. '/hybrid/html/iframe_index.html',
  164. {
  165. top: `${statusBarHeight + navBarHeight}px`,
  166. background: 'transparent',
  167. },
  168. {
  169. height: props.height + 'upx',
  170. src: iframeProps.value.src,
  171. mid: messageId.value,
  172. },
  173. )
  174. wv.loadURL('/hybrid/html/iframe_index.html')
  175. // 获取当前webview的正确方式(Vue3兼容写法)
  176. const pages = getCurrentPages()
  177. if (pages.length === 0) {
  178. console.error('No current page found')
  179. return
  180. }
  181. const currentPage = pages[pages.length - 1]
  182. const currentWebview = currentPage.$getAppWebview?.() || plus.webview.currentWebview()
  183. if (currentWebview) {
  184. currentWebview.append(wv)
  185. } else {
  186. console.error('Failed to get current webview')
  187. }
  188. wv.overrideUrlLoading({ mode: 'reject' }, (e) => {
  189. let { data, type } = getRequest(e.url)
  190. console.log('data=====>>>>>', data)
  191. console.log('type=====>>>>>', type)
  192. //data = unescape(data);
  193. //type = unescape(type);
  194. data = data.replace(/"\[/g, '[').replace(/\]"/g, ']')
  195. switch (type) {
  196. case 'save':
  197. saveAllData(JSON.parse(data))
  198. break
  199. case 'height-change':
  200. iframeHeight.value = data + 10
  201. pageLoading.value = false
  202. break
  203. case 'dialog-change':
  204. emit('dialogChange', data)
  205. break
  206. }
  207. //plus.webview.currentWebview().close();
  208. })
  209. }
  210. const getRequest = (url): any => {
  211. let theRequest = new Object()
  212. let index = url.indexOf('?')
  213. console.log('theRequest', theRequest)
  214. if (index != -1) {
  215. let str = url.substring(index + 1)
  216. let strs = str.split('&')
  217. for (let i = 0; i < strs.length; i++) {
  218. theRequest[strs[i].split('=')[0]] = unescape(strs[i].split('=')[1])
  219. }
  220. }
  221. console.log('theRequest-->after', theRequest)
  222. return theRequest
  223. }
  224. const httpAction = (url, param, method) => {
  225. if (method === 'POST') {
  226. return http.post(url, param)
  227. } else {
  228. return http.put(url, param)
  229. }
  230. }
  231. const saveAllData = (params) => {
  232. let url = api.add,
  233. method = 'POST'
  234. let formData: any = {
  235. desformCode: props.desformCode,
  236. desformDataJson: JSON.stringify(params.json),
  237. }
  238. if (params.onlineForm) {
  239. formData['onlineFormCode'] = params.onlineForm
  240. formData['onlineFormDataId'] = params.onlineDataId
  241. }
  242. if (props.dataId && props.dataId != null) {
  243. url = api.edit
  244. method = 'PUT'
  245. formData['id'] = props.dataId
  246. }
  247. loading.value = true
  248. ;(() => {
  249. // 提交到数据表
  250. return httpAction(url, formData, method)
  251. })()
  252. .then((res: any) => {
  253. console.log('表单提交到数据表结果', res)
  254. if (res.success) {
  255. // 保存到表单设计器,的用户自定义url
  256. let {
  257. formConfig: { customRequestURL: curl },
  258. } = params
  259. if (curl instanceof Array && curl[0] && curl[0].url) {
  260. formData.dataId = res.result.dataId
  261. httpAction(curl[0].url, formData, method)
  262. }
  263. }
  264. return res
  265. })
  266. .then((res) => {
  267. console.log('表单设计器提交结果', res)
  268. if (res.success) {
  269. if (isMp || isApp) {
  270. emit('success', { res: res, dataId: res.result.dataId })
  271. } else {
  272. uni.$emit('success', { res: res, dataId: res.result.dataId })
  273. if (props.alert === true) {
  274. toast.success('保存成功')
  275. }
  276. }
  277. } else {
  278. emit('error', { res: res })
  279. if (props.alert === true && res.result.message) {
  280. toast.error(res.result.message)
  281. }
  282. }
  283. })
  284. .finally(() => {
  285. loading.value = false
  286. })
  287. }
  288. watch(
  289. () => pageLoading.value,
  290. (newValue, oldValue) => {
  291. if (pageLoading.value === false) {
  292. uni.hideLoading()
  293. }
  294. },
  295. )
  296. const init = () => {
  297. uni.showLoading({
  298. title: '表单加载中',
  299. })
  300. if (isApp) {
  301. webview()
  302. } else if (isH5) {
  303. window.addEventListener(
  304. 'message',
  305. function (event) {
  306. let { messageId: mssage_id, type, data } = event.data
  307. if (messageId.value !== mssage_id) {
  308. return
  309. }
  310. switch (type) {
  311. case 'save':
  312. saveAllData(data)
  313. break
  314. case 'height-change':
  315. iframeHeight.value = data + 10
  316. pageLoading.value = false
  317. break
  318. case 'dialog-change':
  319. emit('dialogChange', data)
  320. break
  321. }
  322. },
  323. false,
  324. )
  325. }
  326. setTimeout(() => {
  327. if (pageLoading.value) {
  328. pageLoading.value = false
  329. }
  330. }, 6000)
  331. }
  332. init()
  333. </script>
  334. <style lang="scss" scoped></style>