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"){
@@ -260,21 +284,21 @@ layout("/mobile/platform.html"){ + @confirm="confirmCampus" + @cancel="closeCampus"> - - - + - -
+
+
+
- +
您确定要选择{{chooseOptionName}}吗?
@@ -322,10 +346,59 @@ layout("/mobile/platform.html"){ noticeText: '', } }, + watch: { + descVisible(value) { + this.syncPopupHistory('descVisible', value) + }, + campusVisible(value) { + this.syncPopupHistory('campusVisible', value) + }, + confirmShow(value) { + this.syncPopupHistory('confirmShow', value) + }, + }, methods: { + // visible 为弹框显示状态;关闭回调仅同步状态,历史回退由全局管理器负责。 + syncPopupHistory(key, visible) { + this.popupHistory.sync(key, visible, () => { + this.$set(this, key, false) + }) + }, + closeDescription() { + this.popupHistory.close('descVisible') + }, + openCampus() { + this.$set(this, 'campusVisible', true) + }, + closeCampus() { + this.popupHistory.close('campusVisible') + }, + // value 为院区选项,包含 campusName(显示名称)和 id(提交值)。 + confirmCampus(value) { + this.$set(this.formData, 'campusName', value.campusName) + this.$set(this.formData, 'campus', value.id) + this.closeCampus() + }, + // 条件校验提示也占一层历史;系统返回只关闭提示,不离开选择页面。 + showConditionMessage(title, message) { + this.popupHistory.sync('conditionMessage', true, () => vant.Dialog.close()) + vant.Dialog.alert({ + title: title, + message: message, + confirmButtonColor: '#1867b0', + closeOnPopstate: false, + }).then(() => this.popupHistory.close('conditionMessage')) + .catch(() => this.popupHistory.close('conditionMessage')) + }, descClick(e) { if (e.target.tagName === 'IMG') { - vant.ImagePreview([e.target.src]) + // 图片预览叠加在详情之上,返回时先关闭预览,再关闭详情。 + const preview = vant.ImagePreview({ + images: [e.target.src], + closeOnPopstate: false, + onClose: () => this.popupHistory.close('descriptionImage'), + }) + this.popupHistory.sync('descriptionImage', true, () => preview.close()) } }, async doChangeHealthProjectSubject(item) { @@ -333,11 +406,7 @@ layout("/mobile/platform.html"){ cndId: item.conditionStructureId, }) if (res.code !== 0) { - vant.Dialog.alert({ - title: '提示', - message: res.msg, - confirmButtonColor: '#1867b0' - }) + this.showConditionMessage('提示', res.msg) this.formData.subjectId = '' } else { this.$nextTick(() => { @@ -355,6 +424,11 @@ layout("/mobile/platform.html"){ if (o.description) { this.desc = o.description this.descVisible = true + this.$nextTick(() => { + if (this.$refs.descContent) { + this.$refs.descContent.scrollTop = 0 + } + }) } else { vant.Toast('暂无详细说明') } @@ -404,7 +478,9 @@ layout("/mobile/platform.html"){ toast.clear() }, 500) if (resp.code === 0) { - location.href = '/mobile/healthCheckup/mine' + this.popupHistory.clear().then(() => { + location.href = '/mobile/healthCheckup/mine' + }) } }, 1000) }, @@ -418,11 +494,7 @@ layout("/mobile/platform.html"){ cndId: p.conditionStructureId, }) if (res.code !== 0) { - vant.Dialog.alert({ - title: '温馨提醒', - message: res.msg, - confirmButtonColor: '#1867b0' - }) + this.showConditionMessage('温馨提醒', res.msg) return } this.chooseOptionName = p.optionName @@ -484,6 +556,7 @@ layout("/mobile/platform.html"){ }, }, async created() { + this.popupHistory = mobilePopupHistory.register() this.id = GetQueryString('projectId') this.selectId = GetQueryString('selectId') await this.getCampus() @@ -492,6 +565,9 @@ layout("/mobile/platform.html"){ await this.getSelectInfo() } }, + beforeDestroy() { + this.popupHistory.dispose() + }, }) window.onload = () => { window.addEventListener('pageshow', e => {