feat:修复碳证中心部分问题

This commit is contained in:
liulong 2026-05-26 22:29:37 +08:00
parent 692173e654
commit a141806d73
2 changed files with 21 additions and 278 deletions

View File

@ -1,10 +1,8 @@
/** 与 tzzx 页 VIEWPORT_HEIGHT_OFFSET 保持一致 */
const VIEWPORT_HEIGHT_OFFSET = 100;
export function getViewportIframeFallbackHeight() { export function getViewportIframeFallbackHeight() {
const navOffset = parseInt( return Math.max(window.innerHeight - VIEWPORT_HEIGHT_OFFSET, 480);
getComputedStyle(document.documentElement).getPropertyValue('--page-offset-top'),
10
);
const offset = Number.isFinite(navOffset) ? navOffset : 76;
return Math.max(window.innerHeight - offset, 480);
} }
export function isAllowedIframeMessageOrigin(origin) { export function isAllowedIframeMessageOrigin(origin) {

View File

@ -1,504 +1,249 @@
<template> <template>
<div class="tzzx-page"> <div class="tzzx-page">
<div v-if="loading" class="loading">加载中...</div> <div v-if="loading" class="loading">加载中...</div>
<template v-else-if="iframeUrl"> <template v-else-if="iframeUrl">
<iframe <iframe
ref="tzzxIframe" ref="tzzxIframe"
:src="iframeUrl" :src="iframeUrl"
class="tzzx-iframe" class="tzzx-iframe"
frameborder="0" frameborder="0"
scrolling="auto"
:scrolling="iframeScrolling"
:style="iframeStyle" :style="iframeStyle"
@load="onIframeLoad" @load="onIframeLoad"
></iframe> ></iframe>
</template> </template>
<div v-else class="empty">链接错误</div> <div v-else class="empty">链接错误</div>
</div> </div>
</template> </template>
<script> <script>
import { import {
getViewportIframeFallbackHeight,
isAllowedIframeMessageOrigin, isAllowedIframeMessageOrigin,
} from '@/pages/index/utils/tzzx-iframe'; } from '@/pages/index/utils/tzzx-iframe';
/** 视口降级时 iframe 高度 = 视口高度 - 该偏移(预留顶栏/底边距) */
const VIEWPORT_HEIGHT_OFFSET = 100;
const DEFAULT_IFRAME_HEIGHT = 800;
export default { export default {
name: 'tzzx', name: 'tzzx',
data() { data() {
return { return {
iframeUrl: '', iframeUrl: '',
loading: true, loading: true,
iframeHeight: 0,
iframeHeight: DEFAULT_IFRAME_HEIGHT,
iframeScrolling: 'no',
heightMode: 'default', heightMode: 'default',
resizeObserver: null, resizeObserver: null,
heightCheckTimer: null, heightCheckTimer: null,
_messageHandler: null, _messageHandler: null,
_iframeLoadGeneration: 0, _iframeLoadGeneration: 0,
}; };
}, },
computed: { computed: {
iframeStyle() { iframeStyle() {
const base = {
return {
width: '100%', width: '100%',
height: `${this.iframeHeight}px`,
border: 'none', border: 'none',
display: 'block', display: 'block',
}; };
if (this.heightMode === 'same-origin' || this.heightMode === 'post-message') {
return {
...base,
height: `${this.iframeHeight}px`,
};
}
return {
...base,
height: `calc(100vh - ${VIEWPORT_HEIGHT_OFFSET}px)`,
minHeight: '480px',
};
}, },
}, },
mounted() { mounted() {
this.fetchPage(); this.fetchPage();
this.$watch(() => this.$route.query.page, () => this.fetchPage()); this.$watch(() => this.$route.query.page, () => this.fetchPage());
window.addEventListener('resize', this.onWindowResize);
}, },
activated() { activated() {
this.fetchPage(); this.fetchPage();
}, },
beforeDestroy() { beforeDestroy() {
window.removeEventListener('resize', this.onWindowResize);
this.cleanup(); this.cleanup();
}, },
deactivated() { deactivated() {
this.cleanup(); this.cleanup();
}, },
methods: { methods: {
fetchPage() { fetchPage() {
const rawPage = this.$route.query.page; const rawPage = this.$route.query.page;
const page = Array.isArray(rawPage) ? rawPage[0] : rawPage; const page = Array.isArray(rawPage) ? rawPage[0] : rawPage;
const nextUrl = page ? String(page).trim() : ''; const nextUrl = page ? String(page).trim() : '';
if (nextUrl === this.iframeUrl && !this.loading) { if (nextUrl === this.iframeUrl && !this.loading) {
return; return;
} }
this.cleanup(); this.cleanup();
this.resetIframeMetrics(); this.resetIframeMetrics();
if (nextUrl) { if (nextUrl) {
this.iframeUrl = nextUrl; this.iframeUrl = nextUrl;
this.loading = false; this.loading = false;
} else { } else {
this.iframeUrl = ''; this.iframeUrl = '';
this.loading = false; this.loading = false;
} }
}, },
resetIframeMetrics() { resetIframeMetrics() {
this.iframeHeight = 0;
this.iframeHeight = DEFAULT_IFRAME_HEIGHT;
this.iframeScrolling = 'no';
this.heightMode = 'default'; this.heightMode = 'default';
this._iframeLoadGeneration += 1; this._iframeLoadGeneration += 1;
}, },
onWindowResize() {
if (this.heightMode === 'viewport-fallback') {
this.iframeHeight = getViewportIframeFallbackHeight();
}
},
onIframeLoad() { onIframeLoad() {
const iframe = this.$refs.tzzxIframe; const iframe = this.$refs.tzzxIframe;
if (!iframe) return; if (!iframe) return;
const loadGeneration = this._iframeLoadGeneration; const loadGeneration = this._iframeLoadGeneration;
this.cleanupObserversOnly(); this.cleanupObserversOnly();
const isSameOrigin = this.trySameOrigin(iframe, loadGeneration); const isSameOrigin = this.trySameOrigin(iframe, loadGeneration);
if (!isSameOrigin) { if (!isSameOrigin) {
this.setupPostMessage(iframe, loadGeneration); this.setupPostMessage(iframe, loadGeneration);
} }
}, },
trySameOrigin(iframe, loadGeneration) { trySameOrigin(iframe, loadGeneration) {
try { try {
const iframeDoc = iframe.contentDocument || iframe.contentWindow.document; const iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
if (!iframeDoc || !iframeDoc.body) { if (!iframeDoc || !iframeDoc.body) {
return false; return false;
} }
const updateHeight = () => { const updateHeight = () => {
if (loadGeneration !== this._iframeLoadGeneration) return; if (loadGeneration !== this._iframeLoadGeneration) return;
const height = Math.max( const height = Math.max(
iframeDoc.body.scrollHeight, iframeDoc.body.scrollHeight,
iframeDoc.body.offsetHeight, iframeDoc.body.offsetHeight,
iframeDoc.documentElement.scrollHeight, iframeDoc.documentElement.scrollHeight,
iframeDoc.documentElement.offsetHeight iframeDoc.documentElement.offsetHeight
); );
if (height > 0) { if (height > 0) {
this.iframeHeight = height + 20; this.iframeHeight = height + 20;
this.heightMode = 'same-origin'; this.heightMode = 'same-origin';
this.iframeScrolling = 'no';
} }
}; };
updateHeight(); updateHeight();
if (window.ResizeObserver) { if (window.ResizeObserver) {
this.resizeObserver = new ResizeObserver(updateHeight); this.resizeObserver = new ResizeObserver(updateHeight);
this.resizeObserver.observe(iframeDoc.body); this.resizeObserver.observe(iframeDoc.body);
this.resizeObserver.observe(iframeDoc.documentElement); this.resizeObserver.observe(iframeDoc.documentElement);
} else { } else {
this.heightCheckTimer = setInterval(updateHeight, 500); this.heightCheckTimer = setInterval(updateHeight, 500);
} }
const images = iframeDoc.querySelectorAll('img'); const images = iframeDoc.querySelectorAll('img');
images.forEach((img) => { images.forEach((img) => {
if (!img.complete) { if (!img.complete) {
img.addEventListener('load', updateHeight); img.addEventListener('load', updateHeight);
img.addEventListener('error', updateHeight); img.addEventListener('error', updateHeight);
} }
}); });
return true; return true;
} catch (e) { } catch (e) {
console.log('[tzzx] 检测到跨域 iframe将使用 postMessage / 视口降级方案'); console.log('[tzzx] 检测到跨域 iframe将使用 postMessage / 视口降级方案');
return false; return false;
} }
}, },
setupPostMessage(iframe, loadGeneration) { setupPostMessage(iframe, loadGeneration) {
this._messageHandler = (event) => { this._messageHandler = (event) => {
if (loadGeneration !== this._iframeLoadGeneration) return; if (loadGeneration !== this._iframeLoadGeneration) return;
if (!isAllowedIframeMessageOrigin(event.origin)) return; if (!isAllowedIframeMessageOrigin(event.origin)) return;
if (event.source !== iframe.contentWindow) return; if (event.source !== iframe.contentWindow) return;
const data = event.data; const data = event.data;
if (!data || data.type !== 'iframeHeight') return; if (!data || data.type !== 'iframeHeight') return;
const height = Number(data.height); const height = Number(data.height);
if (!Number.isFinite(height) || height <= 0) return; if (!Number.isFinite(height) || height <= 0) return;
this.iframeHeight = height + 20; this.iframeHeight = height + 20;
this.heightMode = 'post-message'; this.heightMode = 'post-message';
this.iframeScrolling = 'no';
}; };
window.addEventListener('message', this._messageHandler); window.addEventListener('message', this._messageHandler);
const requestHeight = () => { const requestHeight = () => {
if (loadGeneration !== this._iframeLoadGeneration) return; if (loadGeneration !== this._iframeLoadGeneration) return;
try { try {
iframe.contentWindow.postMessage({ type: 'REQUEST_HEIGHT' }, '*'); iframe.contentWindow.postMessage({ type: 'REQUEST_HEIGHT' }, '*');
} catch (e) {} } catch (e) {}
}; };
requestHeight(); requestHeight();
let retryCount = 0; let retryCount = 0;
this.heightCheckTimer = setInterval(() => { this.heightCheckTimer = setInterval(() => {
requestHeight(); requestHeight();
retryCount += 1; retryCount += 1;
if (retryCount >= 5) { if (retryCount >= 5) {
clearInterval(this.heightCheckTimer); clearInterval(this.heightCheckTimer);
this.heightCheckTimer = null; this.heightCheckTimer = null;
if (loadGeneration === this._iframeLoadGeneration && this.heightMode === 'default') { if (loadGeneration === this._iframeLoadGeneration && this.heightMode === 'default') {
this.applyViewportFallback(); this.applyViewportFallback();
} }
} }
}, 1000); }, 1000);
}, },
applyViewportFallback() { applyViewportFallback() {
this.iframeHeight = getViewportIframeFallbackHeight();
this.heightMode = 'viewport-fallback'; this.heightMode = 'viewport-fallback';
this.iframeScrolling = 'auto';
}, },
cleanupObserversOnly() { cleanupObserversOnly() {
if (this.resizeObserver) { if (this.resizeObserver) {
this.resizeObserver.disconnect(); this.resizeObserver.disconnect();
this.resizeObserver = null; this.resizeObserver = null;
} }
if (this.heightCheckTimer) { if (this.heightCheckTimer) {
clearInterval(this.heightCheckTimer); clearInterval(this.heightCheckTimer);
this.heightCheckTimer = null; this.heightCheckTimer = null;
} }
if (this._messageHandler) { if (this._messageHandler) {
window.removeEventListener('message', this._messageHandler); window.removeEventListener('message', this._messageHandler);
this._messageHandler = null; this._messageHandler = null;
} }
}, },
cleanup() { cleanup() {
this.cleanupObserversOnly(); this.cleanupObserversOnly();
}, },
}, },
}; };
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
.tzzx-page { .tzzx-page {
width: 100%; width: 100%;
.tzzx-iframe { .tzzx-iframe {
display: block; display: block;
width: 100%; width: 100%;
} }
.loading, .loading,
.empty { .empty {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
width: 100%; width: 100%;
min-height: 480px; min-height: 480px;
font-size: 16px; font-size: 16px;
color: #999; color: #999;
} }
} }
</style> </style>