import {onLoad} from "@dcloudio/uni-app" import {computed, onUnmounted, readonly, ref, nextTick} from "vue" import {array, empty, object, string} from "@/uni_modules/uv-ui-tools/libs/function/test"; import mxConst, {getTabRoutes} from "@/common/mxConst"; import {guid, sleep} from "@/uni_modules/uv-ui-tools/libs/function"; import baseConfig from "@/config"; import mxConfig from "@/common/mxConfig"; import {useH5BackHome} from "@/components/mx-nav-bar/useH5BackHome"; import {createEventHook, injectLocal, provideLocal} from "@vueuse/core"; import _ from 'lodash'; export const useTransfer = function () { // data const cacheKey = ref('') const prevData = ref({}) const usingCacheTransfer = computed(() => !!cacheKey.value) // 回传事件,只要在transferTo中定义一个callback字符串标识即可响应回调事件 // 用uni.$on/$off/$emit来实现,如果要通用的话,可以考虑自行实现一个全局的eventBus const pageCallback = createEventHook() // 使用hook给transfer外部用户提供钩子 const eventName = ref('') // 给发起跳转的页面记录事件注册, 用于自动注销 const callbackEventData = ref(null) // 给当前页面存放数据,用于回传 // hooks onLoad((opt) => { if (opt.cacheKey) cacheKey.value = opt.cacheKey, prevData.value = uni.getStorageSync(opt.cacheKey) else prevData.value = JSON.parse(decodeURIComponent(opt.data || '{}')) }) onUnmounted(() => { if (usingCacheTransfer.value) uni.removeStorageSync(cacheKey.value) // 发起跳转的页面注销事件 if (eventName.value) uni.$off(eventName.value) }) // methods /* * main function for route, it keeps params type and supports big data transfer. * Note: if you defined `callback` in params, 2 pages will auto support onPageCallback event. * @param {String|Object} urlOrOptions - route url or options * @param {Object} props - indicates props to pickup from source data * @param {Array} source - source data array, default is [prevData.value] * @param {Boolean} usingCache - whether to use cache, default is false, for big data transfer * */ const transferTo = function (urlOrOptions, props = {}, source = null, usingCache = false) { const options = object(urlOrOptions) ? urlOrOptions : {url: urlOrOptions, type: getTabRoutes().includes(urlOrOptions) ? 'tab' : 'to'} // confirm params let params = options.params || {} let extra = {} // prevData 自动包含是名学测评系统的逻辑,暂时不用去掉,以免引起一些数值覆盖的问题 const extraSource = [/*prevData.value*/].concat(source) if (array(props)) extra = _.merge({}, ..._.map(extraSource, obj => _.pick(obj, props))) else if (object(props)) extra = _.merge(props, ..._.map(extraSource, obj => _.pick(obj, _.keys(props)))) params = _.merge(params, extra) // handle params, using JSON.stringify or uni.setStorageSync let formatParams = params if (!empty(params)) { if (usingCache) { const storageKey = mxConst.keyCacheUniquePrefix + guid() uni.setStorageSync(storageKey, params) formatParams = {cacheKey: storageKey} } else { formatParams = {data: encodeURIComponent(JSON.stringify(params))} } } // page callback event // 注意这是在发起跳转的页面监听 if (string(params.callback) && params.callback) { eventName.value = params.callback uni.$off(eventName.value) // 先清除其它注册,只注册一次 uni.$on(eventName.value, handleCallback) } // 这里并没有干涉其它参数,所以$uv.route支持的功能这里应该都能用 options.params = formatParams uni.$uv.route(options) console.log('transferTo:', options, params) } const handleCallback = async function (event) { await pageCallback.trigger(event) } const transferBack = function (options = {type: 'back'}) { // 主动调用transferBack的地方自动处理回调 if (prevData.value.callback) { // 这里做了一个延迟处理,让回调发生在页面跳转之后, // 这样的话,页面因为数据变化引起的滚动条重置问题看起来平滑一些。 // 如entry-single页面,换选学校之后滚动条由于缓存机制会停在选择学校之前的位置, // 即使设置了scrollTop也不起作用,这里延迟可以保证重置行为发生在滚动条缓存机制之后。 const e = prevData.value.callback, arg = callbackEventData.value sleep().then(() => uni.$emit(e, arg)) } const {hasPreviousPage} = useH5BackHome() if (hasPreviousPage.value) transferTo(options) else relaunch() } const transferToIndex = function () { transferTo(mxConst.routes.index) } const relaunch = function () { transferTo({...mxConst.routes.index, type: 'launch'}) } const transferToWebView = function (url, title = '') { const nextPage = '/pages/h5/h5' transferTo(nextPage, {url, title}) } const transferToProtocolUser = function () { const path = baseConfig.inlineSiteBaseUrl + mxConfig.inlineSiteFunctionPaths.protocolUser transferToWebView(path, '用户协议') } const transferToProtocolPrivacy = function () { const path = baseConfig.inlineSiteBaseUrl + mxConfig.inlineSiteFunctionPaths.protocolPrivacy transferToWebView(path, '隐私协议') } const transferToFAQ = function () { const path = baseConfig.inlineSiteBaseUrl + mxConfig.inlineSiteFunctionPaths.FAQ transferToWebView(path, '常见问题') } const playVideo = function (idOrSrc, aliIdType, title, callback = false) { transferTo(mxConst.routes.videoPlay, {aliId: idOrSrc, aliIdType, title, callback}) } /* * tool function for clean all transfer cache data, * 这里提供了一个清除所有缓存数据的工具函数, * 可以用在一些管理性功能页中,如果页面非正常中断可能会累积一些缓存, * 这个函数可以用来清除所有缓存数据。 * 注意:这个函数只是清除 transferTo 中 usingCache=true 产生缓存数据. * */ const cleanAllTransferCacheData = function () { // 根据mxConst.keyCacheUniquePrefix清除所有缓存 // 提供给某些管理性功能页使用,如果页面非正常中断可能会累积一些缓存 const keys = uni.getStorageInfoSync().keys keys.forEach(key => { if (key.startsWith(mxConst.keyCacheUniquePrefix)) uni.removeStorageSync(key) }) } return { cacheKey: readonly(cacheKey), // 安全考虑,不允许外部修改 prevData, usingCacheTransfer, eventName: readonly(eventName), onPageCallback: pageCallback.on, callbackEventData, transferTo, transferBack, transferToIndex, relaunch, transferToWebView, transferToProtocolUser, transferToProtocolPrivacy, transferToFAQ, playVideo, cleanAllTransferCacheData } } const key = Symbol('TRANSFER_SERVICE') export const useProvideTransfer = function () { const options = useTransfer() provideLocal(key, options) return options } export const useInjectTransfer = function () { return injectLocal(key) }