diff --git a/src/main/resources/static/assets/mobile/js/basic.js b/src/main/resources/static/assets/mobile/js/basic.js index c76ad5d..b57d642 100644 --- a/src/main/resources/static/assets/mobile/js/basic.js +++ b/src/main/resources/static/assets/mobile/js/basic.js @@ -119,3 +119,103 @@ const modal = { } } Vue.prototype.$modal = modal + +// H5 弹框共享一个 popstate 监听器;仅主动注册的页面会写入历史记录。 +const mobilePopupHistory = (() => { + const marker = '__mobilePopupId' + const entries = [] + let sequence = 0 + let owners = 0 + let pending = null + const session = Date.now().toString(36) + + // 先移除栈项再同步界面,避免 v-model 的 watcher 再次触发历史回退。 + const removeFrom = (index) => { + const removed = entries.splice(index) + removed.reverse().forEach(entry => entry.close()) + } + const onPopState = (event) => { + const id = event.state && event.state[marker] + const index = entries.findIndex(entry => entry.id === id) + removeFrom(index + 1) + if (pending) { + const resolve = pending.resolve + pending = null + resolve() + } + } + const backFrom = (index) => { + if (index < 0) { + return Promise.resolve() + } + const count = entries.length - index + let resolveBack + const promise = new Promise(resolve => { resolveBack = resolve }) + pending = {promise: promise, resolve: resolveBack} + removeFrom(index) + window.history.go(-count) + return promise + } + + return { + /** + * 注册当前页面,无参数。返回 sync/close/clear/dispose 方法。 + * sync(key, visible, close):key 为页面内唯一弹框名,visible 为显示状态, + * close 为无参关闭回调。close(key) 和 clear() 返回历史回退完成的 Promise, + * 页面跳转必须等待 clear() 完成;dispose() 用于销毁时注销回调和监听器。 + */ + register() { + const owner = {} + let disposed = false + if (owners++ === 0) { + window.addEventListener('popstate', onPopState) + } + const handle = { + sync(key, visible, close) { + if (disposed) { + return + } + if (pending) { + pending.promise.then(() => handle.sync(key, visible, close)) + return + } + const index = entries.findIndex(entry => entry.owner === owner && entry.key === key) + if (!visible) { + handle.close(key) + } else if (index < 0) { + const id = session + '-' + (++sequence) + const state = Object.assign({}, window.history.state) + state[marker] = id + window.history.pushState(state, '', window.location.href) + entries.push({id: id, key: key, owner: owner, close: close}) + } + }, + close(key) { + if (pending) { + return pending.promise.then(() => handle.close(key)) + } + // 关闭下层弹框时一并关闭其上层,保持浏览器历史与可见层级一致。 + return backFrom(entries.findIndex(entry => entry.owner === owner && entry.key === key)) + }, + clear() { + if (pending) { + return pending.promise.then(() => handle.clear()) + } + return backFrom(entries.findIndex(entry => entry.owner === owner)) + }, + dispose() { + if (disposed) { + return + } + disposed = true + handle.clear().then(() => { + if (--owners === 0) { + window.removeEventListener('popstate', onPopState) + } + }) + }, + } + return handle + }, + } +})() diff --git a/src/main/resources/views/mobile/healthCheckup/checkUp.html b/src/main/resources/views/mobile/healthCheckup/checkUp.html index b512097..3965409 100644 --- a/src/main/resources/views/mobile/healthCheckup/checkUp.html +++ b/src/main/resources/views/mobile/healthCheckup/checkUp.html @@ -113,6 +113,30 @@ layout("/mobile/platform.html"){ /*margin-top: 30px;*/ } + /* 标题不参与滚动,说明正文独占弹框剩余高度。 */ + .checkup-desc-popup { + display: flex; + flex-direction: column; + overflow: hidden; + } + + .checkup-desc-popup > .van-nav-bar { + flex-shrink: 0; + } + + .checkup-desc-content { + flex: 1; + min-height: 0; + overflow-y: auto; + -webkit-overflow-scrolling: touch; + overscroll-behavior-y: contain; + } + + .checkup-desc-content img { + max-width: 100%; + height: auto; + } + .van-dropdown-menu__bar { display: flex; @@ -183,7 +207,7 @@ layout("/mobile/platform.html"){