diff --git a/packages/uni-app-plus/dist/uni-app-service.es.js b/packages/uni-app-plus/dist/uni-app-service.es.js index 8ce8266b46efa3e693bb44fe606dbf592d5edb16..598f5f5fbb86865b42e56c52d2fe0d9ca4180394 100644 --- a/packages/uni-app-plus/dist/uni-app-service.es.js +++ b/packages/uni-app-plus/dist/uni-app-service.es.js @@ -1007,6 +1007,19 @@ var serviceContext = (function (vue) { } } + function debounce(fn, delay) { + let timeout; + const newFn = function () { + clearTimeout(timeout); + const timerFn = () => fn.apply(this, arguments); + timeout = setTimeout(timerFn, delay); + }; + newFn.cancel = function () { + clearTimeout(timeout); + }; + return newFn; + } + const NAVBAR_HEIGHT = 44; const TABBAR_HEIGHT = 50; const ON_REACH_BOTTOM_DISTANCE = 50; @@ -1031,8 +1044,13 @@ var serviceContext = (function (vue) { const ON_PAGE_SCROLL = 'onPageScroll'; const ON_TAB_ITEM_TAP = 'onTabItemTap'; const ON_REACH_BOTTOM = 'onReachBottom'; + const ON_PULL_DOWN_REFRESH = 'onPullDownRefresh'; // navigationBar const ON_NAVIGATION_BAR_BUTTON_TAP = 'onNavigationBarButtonTap'; + const ON_NAVIGATION_BAR_SEARCH_INPUT_CLICKED = 'onNavigationBarSearchInputClicked'; + const ON_NAVIGATION_BAR_SEARCH_INPUT_CHANGED = 'onNavigationBarSearchInputChanged'; + const ON_NAVIGATION_BAR_SEARCH_INPUT_CONFIRMED = 'onNavigationBarSearchInputConfirmed'; + const ON_NAVIGATION_BAR_SEARCH_INPUT_FOCUS_CHANGED = 'onNavigationBarSearchInputFocusChanged'; // framework const ON_APP_ENTER_FOREGROUND = 'onAppEnterForeground'; const ON_APP_ENTER_BACKGROUND = 'onAppEnterBackground'; @@ -4803,6 +4821,10 @@ var serviceContext = (function (vue) { }, }; + const API_START_PULL_DOWN_REFRESH = 'startPullDownRefresh'; + + const API_STOP_PULL_DOWN_REFRESH = 'stopPullDownRefresh'; + const API_GET_PROVIDER = 'getProvider'; const GetProviderProtocol = { service: { @@ -7952,6 +7974,45 @@ var serviceContext = (function (vue) { return callbacks && callbacks.resolve(); } + function getCurrentWebview() { + const page = getCurrentPage(); + if (page) { + return page.$getAppWebview(); + } + return null; + } + let pullDownRefreshWebview = null; + function getPullDownRefreshWebview() { + return pullDownRefreshWebview; + } + function setPullDownRefreshWebview(webview) { + pullDownRefreshWebview = webview; + } + + const startPullDownRefresh = defineAsyncApi(API_START_PULL_DOWN_REFRESH, (_args, { resolve, reject }) => { + let webview = getPullDownRefreshWebview(); + if (webview) { + webview.endPullToRefresh(); + } + webview = getCurrentWebview(); + if (!webview) { + return reject(); + } + webview.beginPullToRefresh(); + setPullDownRefreshWebview(webview); + resolve(); + }); + + const stopPullDownRefresh = defineAsyncApi(API_STOP_PULL_DOWN_REFRESH, (_args, { resolve, reject }) => { + const webview = getPullDownRefreshWebview() || getCurrentWebview(); + if (!webview) { + return reject(); + } + webview.endPullToRefresh(); + setPullDownRefreshWebview(null); + resolve(); + }); + const providers = { oauth(callback) { plus.oauth.getServices((services) => { @@ -8929,6 +8990,160 @@ var serviceContext = (function (vue) { return preloadWebview; } + function onWebviewClose(webview) { + const { popupSubNVueWebviews } = webview; + if (!popupSubNVueWebviews) { + return; + } + webview.addEventListener('close', () => { + Object.keys(popupSubNVueWebviews).forEach((id) => { + if ((process.env.NODE_ENV !== 'production')) { + console.log(formatLog('onWebviewClose', webview.id, 'popupSubNVueWebview', id, 'close')); + } + popupSubNVueWebviews[id].close('none'); + }); + }); + } + + let lastStatusBarStyle; + let oldSetStatusBarStyle = plus.navigator.setStatusBarStyle; + function newSetStatusBarStyle(style) { + lastStatusBarStyle = style; + oldSetStatusBarStyle(style); + } + plus.navigator.setStatusBarStyle = newSetStatusBarStyle; + function setStatusBarStyle(statusBarStyle) { + if (!statusBarStyle) { + const pages = getCurrentPages(); + if (!pages.length) { + return; + } + statusBarStyle = pages[pages.length - 1].$page + .statusBarStyle; + if (!statusBarStyle || statusBarStyle === lastStatusBarStyle) { + return; + } + } + if (statusBarStyle === lastStatusBarStyle) { + return; + } + if ((process.env.NODE_ENV !== 'production')) { + console.log(formatLog('setStatusBarStyle', statusBarStyle)); + } + lastStatusBarStyle = statusBarStyle; + plus.navigator.setStatusBarStyle(statusBarStyle); + } + + const pages = []; + function addCurrentPage(page) { + pages.push(page); + } + function getAllPages() { + return pages; + } + function getCurrentPages$1() { + const curPages = []; + pages.forEach((page) => { + if (page.__isTabBar) { + if (page.$.__isActive) { + curPages.push(page); + } + } + else { + curPages.push(page); + } + }); + return curPages; + } + function removeCurrentPage() { + const page = getCurrentPage(); + if (!page) { + return; + } + removePage(page); + } + function removePage(curPage) { + const index = pages.findIndex((page) => page === curPage); + if (index === -1) { + return; + } + if (!curPage.$page.meta.isNVue) { + getVueApp().unmountPage(curPage); + } + pages.splice(index, 1); + if ((process.env.NODE_ENV !== 'production')) { + console.log(formatLog('removePage', curPage.$page)); + } + } + + function onWebviewPopGesture(webview) { + let popStartStatusBarStyle; + webview.addEventListener('popGesture', (e) => { + if (e.type === 'start') { + // 设置下一个页面的 statusBarStyle + const pages = getCurrentPages(); + const page = pages[pages.length - 2]; + popStartStatusBarStyle = lastStatusBarStyle; + const statusBarStyle = page && page.$page.statusBarStyle; + statusBarStyle && setStatusBarStyle(statusBarStyle); + } + else if (e.type === 'end' && !e.result) { + // 拖拽未完成,设置为当前状态栏前景色 + setStatusBarStyle(popStartStatusBarStyle); + } + else if (e.type === 'end' && e.result) { + removeCurrentPage(); + setStatusBarStyle(); + // 触发前一个页面 onShow + invokeHook(ON_SHOW); + } + }); + } + + function onWebviewResize(webview) { + const { emit } = UniServiceJSBridge; + const onResize = function ({ width, height, }) { + const landscape = Math.abs(plus.navigator.getOrientation()) === 90; + const res = { + deviceOrientation: landscape ? 'landscape' : 'portrait', + size: { + windowWidth: Math.ceil(width), + windowHeight: Math.ceil(height), + }, + }; + emit(ON_RESIZE, res, parseInt(webview.id)); // Page lifecycle + }; + webview.addEventListener('resize', debounce(onResize, 50)); + } + + const WEBVIEW_LISTENERS = { + pullToRefresh: ON_PULL_DOWN_REFRESH, + titleNViewSearchInputChanged: ON_NAVIGATION_BAR_SEARCH_INPUT_CHANGED, + titleNViewSearchInputConfirmed: ON_NAVIGATION_BAR_SEARCH_INPUT_CONFIRMED, + titleNViewSearchInputClicked: ON_NAVIGATION_BAR_SEARCH_INPUT_CLICKED, + titleNViewSearchInputFocusChanged: ON_NAVIGATION_BAR_SEARCH_INPUT_FOCUS_CHANGED, + }; + function initWebviewEvent(webview) { + const id = parseInt(webview.id); + Object.keys(WEBVIEW_LISTENERS).forEach((name) => { + const hook = WEBVIEW_LISTENERS[name]; + webview.addEventListener(name, (e) => { + if (hook === ON_PULL_DOWN_REFRESH) { + // 设置当前正在下拉刷新的webview + setPullDownRefreshWebview(webview); + } + invokeHook(id, hook, e); + }); + }); + onWebviewClose(webview); + onWebviewResize(webview); + // TODO + if (plus.os.name === 'iOS') { + // !(webview as any).nvue && onWebviewRecovery(webview, routeOptions) + onWebviewPopGesture(webview); + } + } + function initWebviewStyle(webview, path, query, routeMeta) { const webviewStyle = parseWebviewStyle(path, routeMeta); webviewStyle.uniPageUrl = initUniPageUrl(path, query); @@ -8951,6 +9166,7 @@ var serviceContext = (function (vue) { if (webview.id === '1' || !routeMeta.isNVue) { initWebviewStyle(webview, path, query, routeMeta); } + initWebviewEvent(webview); } function createWebview(options) { @@ -9501,70 +9717,6 @@ var serviceContext = (function (vue) { return new InteractiveAd(options); }, CreateInteractiveAdProtocol, CreateInteractiveAdOptions)); - const pages = []; - function addCurrentPage(page) { - pages.push(page); - } - function getAllPages() { - return pages; - } - function getCurrentPages$1() { - const curPages = []; - pages.forEach((page) => { - if (page.__isTabBar) { - if (page.$.__isActive) { - curPages.push(page); - } - } - else { - curPages.push(page); - } - }); - return curPages; - } - function removePage(curPage) { - const index = pages.findIndex((page) => page === curPage); - if (index === -1) { - return; - } - if (!curPage.$page.meta.isNVue) { - getVueApp().unmountPage(curPage); - } - pages.splice(index, 1); - if ((process.env.NODE_ENV !== 'production')) { - console.log(formatLog('removePage', curPage.$page)); - } - } - - let lastStatusBarStyle; - let oldSetStatusBarStyle = plus.navigator.setStatusBarStyle; - function newSetStatusBarStyle(style) { - lastStatusBarStyle = style; - oldSetStatusBarStyle(style); - } - plus.navigator.setStatusBarStyle = newSetStatusBarStyle; - function setStatusBarStyle(statusBarStyle) { - if (!statusBarStyle) { - const pages = getCurrentPages(); - if (!pages.length) { - return; - } - statusBarStyle = pages[pages.length - 1].$page - .statusBarStyle; - if (!statusBarStyle || statusBarStyle === lastStatusBarStyle) { - return; - } - } - if (statusBarStyle === lastStatusBarStyle) { - return; - } - if ((process.env.NODE_ENV !== 'production')) { - console.log(formatLog('setStatusBarStyle', statusBarStyle)); - } - lastStatusBarStyle = statusBarStyle; - plus.navigator.setStatusBarStyle(statusBarStyle); - } - let pendingNavigator = false; function setPendingNavigator(path, callback, msg) { pendingNavigator = { @@ -10498,6 +10650,8 @@ var serviceContext = (function (vue) { showToast: showToast, hideToast: hideToast, hideLoading: hideLoading, + startPullDownRefresh: startPullDownRefresh, + stopPullDownRefresh: stopPullDownRefresh, getProvider: getProvider, login: login, getUserInfo: getUserInfo, diff --git a/packages/uni-app-plus/src/service/api/index.ts b/packages/uni-app-plus/src/service/api/index.ts index 120ac77a768ec38cabe26d3384392174e6ef818c..ee517c48236c7baf92a1c1477dd7e36e871e7a94 100644 --- a/packages/uni-app-plus/src/service/api/index.ts +++ b/packages/uni-app-plus/src/service/api/index.ts @@ -41,6 +41,8 @@ export * from './location/openLocation' export * from './ui/popup/showModal' export * from './ui/popup/showActionSheet' export * from './ui/popup/showToast' +export * from './ui/startPullDownRefresh' +export * from './ui/stopPullDownRefresh' export * from './plugin/getProvider' export * from './plugin/oauth' diff --git a/packages/uni-app-plus/src/service/api/ui/startPullDownRefresh.ts b/packages/uni-app-plus/src/service/api/ui/startPullDownRefresh.ts new file mode 100644 index 0000000000000000000000000000000000000000..bdcbe18068c7db267f07eb50e7cd850fc714fb09 --- /dev/null +++ b/packages/uni-app-plus/src/service/api/ui/startPullDownRefresh.ts @@ -0,0 +1,28 @@ +import { + API_START_PULL_DOWN_REFRESH, + API_TYPE_START_PULL_DOWN_REFRESH, + defineAsyncApi, +} from '@dcloudio/uni-api' +import { + getCurrentWebview, + getPullDownRefreshWebview, + setPullDownRefreshWebview, +} from '../../utils' + +export const startPullDownRefresh = + defineAsyncApi( + API_START_PULL_DOWN_REFRESH, + (_args, { resolve, reject }) => { + let webview = getPullDownRefreshWebview() + if (webview) { + webview.endPullToRefresh() + } + webview = getCurrentWebview() + if (!webview) { + return reject() + } + webview.beginPullToRefresh() + setPullDownRefreshWebview(webview) + resolve() + } + ) diff --git a/packages/uni-app-plus/src/service/api/ui/stopPullDownRefresh.ts b/packages/uni-app-plus/src/service/api/ui/stopPullDownRefresh.ts new file mode 100644 index 0000000000000000000000000000000000000000..9cdcb346efdb0b00564f724a0910a69531815c15 --- /dev/null +++ b/packages/uni-app-plus/src/service/api/ui/stopPullDownRefresh.ts @@ -0,0 +1,24 @@ +import { + API_STOP_PULL_DOWN_REFRESH, + API_TYPE_STOP_PULL_DOWN_REFRESH, + defineAsyncApi, +} from '@dcloudio/uni-api' +import { + getCurrentWebview, + getPullDownRefreshWebview, + setPullDownRefreshWebview, +} from '../../utils' + +export const stopPullDownRefresh = + defineAsyncApi( + API_STOP_PULL_DOWN_REFRESH, + (_args, { resolve, reject }) => { + const webview = getPullDownRefreshWebview() || getCurrentWebview() + if (!webview) { + return reject() + } + webview.endPullToRefresh() + setPullDownRefreshWebview(null) + resolve() + } + ) diff --git a/packages/uni-app-plus/src/service/framework/webview/init/event/index.ts b/packages/uni-app-plus/src/service/framework/webview/init/event/index.ts index 53b70f430d1fa0b967bf432ea4aac0acce2e379a..766a322771c311c6d777931d284d53e53f09cee0 100644 --- a/packages/uni-app-plus/src/service/framework/webview/init/event/index.ts +++ b/packages/uni-app-plus/src/service/framework/webview/init/event/index.ts @@ -1,3 +1,4 @@ +import { invokeHook } from '@dcloudio/uni-core' import { ON_NAVIGATION_BAR_SEARCH_INPUT_CHANGED, ON_NAVIGATION_BAR_SEARCH_INPUT_CLICKED, @@ -5,6 +6,7 @@ import { ON_NAVIGATION_BAR_SEARCH_INPUT_FOCUS_CHANGED, ON_PULL_DOWN_REFRESH, } from '@dcloudio/uni-shared' +import { setPullDownRefreshWebview } from 'packages/uni-app-plus/src/service/utils' import { onWebviewClose } from './close' import { onWebviewPopGesture } from './popGesture' import { onWebviewResize } from './resize' @@ -19,11 +21,15 @@ const WEBVIEW_LISTENERS = { } as const export function initWebviewEvent(webview: PlusWebviewWebviewObject) { - const { emit } = UniServiceJSBridge const id = parseInt(webview.id!) Object.keys(WEBVIEW_LISTENERS).forEach((name) => { + const hook = WEBVIEW_LISTENERS[name as keyof typeof WEBVIEW_LISTENERS] webview.addEventListener(name as any, (e) => { - emit(WEBVIEW_LISTENERS[name as keyof typeof WEBVIEW_LISTENERS], e, id) + if (hook === ON_PULL_DOWN_REFRESH) { + // 设置当前正在下拉刷新的webview + setPullDownRefreshWebview(webview) + } + invokeHook(id, hook, e) }) }) onWebviewClose(webview) diff --git a/packages/uni-app-plus/src/service/framework/webview/init/index.ts b/packages/uni-app-plus/src/service/framework/webview/init/index.ts index 80e9b495990edbfa98f9ee43f68690b8b4d5b9f3..80f0adbb8010723dfd1a096a26ed74214c12e6f9 100644 --- a/packages/uni-app-plus/src/service/framework/webview/init/index.ts +++ b/packages/uni-app-plus/src/service/framework/webview/init/index.ts @@ -1,3 +1,4 @@ +import { initWebviewEvent } from './event' import { initWebviewStyle } from './style' import { initSubNVues } from './subNVues' @@ -12,4 +13,5 @@ export function initWebview( initWebviewStyle(webview, path, query, routeMeta) } initSubNVues(webview, path, routeMeta) + initWebviewEvent(webview) } diff --git a/packages/uni-app-plus/src/service/utils.ts b/packages/uni-app-plus/src/service/utils.ts new file mode 100644 index 0000000000000000000000000000000000000000..83111749a993b678edaf674477101d6f7d2c03a2 --- /dev/null +++ b/packages/uni-app-plus/src/service/utils.ts @@ -0,0 +1,22 @@ +import { getCurrentPage } from '@dcloudio/uni-core' +import { ComponentPublicInstance } from 'vue' + +export function getCurrentWebview() { + const page = getCurrentPage() + if (page) { + return (page as ComponentPublicInstance).$getAppWebview!() + } + return null +} + +let pullDownRefreshWebview: PlusWebviewWebviewObject | null = null + +export function getPullDownRefreshWebview() { + return pullDownRefreshWebview +} + +export function setPullDownRefreshWebview( + webview: PlusWebviewWebviewObject | null +) { + pullDownRefreshWebview = webview +} diff --git a/packages/uni-app-vite/src/plugin/build.ts b/packages/uni-app-vite/src/plugin/build.ts index 9a6e878d2ccfdfe501311b1a39595132448b8167..70df2589accc484eb048892775b16ada82be155e 100644 --- a/packages/uni-app-vite/src/plugin/build.ts +++ b/packages/uni-app-vite/src/plugin/build.ts @@ -4,29 +4,31 @@ import { UserConfig } from 'vite' import { resolveMainPathOnce } from '@dcloudio/uni-cli-shared' -export const buildOptions: UserConfig['build'] = { - rollupOptions: { - input: resolveMainPathOnce(process.env.UNI_INPUT_DIR), - external: ['vue'], - output: { - name: 'AppService', - format: process.env.UNI_APP_CODE_SPLITING ? 'amd' : 'iife', - entryFileNames: 'app-service.js', - manualChunks: {}, - chunkFileNames(chunk) { - if (chunk.isDynamicEntry && chunk.facadeModuleId) { - const filepath = path.relative( - process.env.UNI_INPUT_DIR, - chunk.facadeModuleId - ) - return slash(filepath.replace(path.extname(filepath), '.js')) - } - return '[name].js' - }, - assetFileNames: '[name][extname]', - globals: { - vue: 'Vue', +export function buildOptions(): UserConfig['build'] { + return { + rollupOptions: { + input: resolveMainPathOnce(process.env.UNI_INPUT_DIR), + external: ['vue'], + output: { + name: 'AppService', + format: process.env.UNI_APP_CODE_SPLITING ? 'amd' : 'iife', + entryFileNames: 'app-service.js', + manualChunks: {}, + chunkFileNames(chunk) { + if (chunk.isDynamicEntry && chunk.facadeModuleId) { + const filepath = path.relative( + process.env.UNI_INPUT_DIR, + chunk.facadeModuleId + ) + return slash(filepath.replace(path.extname(filepath), '.js')) + } + return '[name].js' + }, + assetFileNames: '[name][extname]', + globals: { + vue: 'Vue', + }, }, }, - }, + } } diff --git a/packages/uni-app-vite/src/plugin/index.ts b/packages/uni-app-vite/src/plugin/index.ts index 9a7d7d3b7ae142b347407af98a150cff64f028fb..0a17c892ad6fbc6a7b81d0e411521cfffada2b3d 100644 --- a/packages/uni-app-vite/src/plugin/index.ts +++ b/packages/uni-app-vite/src/plugin/index.ts @@ -9,7 +9,7 @@ export const UniAppPlugin: UniVitePlugin = { uni: uniOptions, config() { return { - build: buildOptions, + build: buildOptions(), } }, configResolved, diff --git a/tsconfig.json b/tsconfig.json index bd4ef991884df0c41491b19080b96f46142d6d16..ded4fa3d676183d6b8b42338f00c9351f6374a8c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -41,6 +41,7 @@ "packages/*/__tests__", "test-dts", "packages/uni-h5/types", + "packages/uni-app-vite/types", "node_modules/mini-types/types/api/index.d.ts", "node_modules/mini-types/types/page.d.ts", "node_modules/mini-types/types/component.d.ts",