Commit b2a8d572 authored by 罗超's avatar 罗超

优化内容

parent a380fcba
......@@ -201,6 +201,7 @@
<!-- 其他页Header -->
<div v-else class="PreviewNotice-header small">
{{ noticeInfo.tripName }}
<div class="header-line"></div>
</div>
<!-- 渲染当前页的内容块 -->
......@@ -405,7 +406,9 @@
</div>
</template>
<div class="PreviewNotice-footer"></div>
<div class="PreviewNotice-footer">
<div class="footer-img"></div>
</div>
</div>
</div>
</div>
......@@ -619,21 +622,13 @@ export default {
try {
this.downloadLoading = true;
// 等待字体加载
await this.loadFonts();
// 收集所有需要截图的元素
const elements = this.collectElements();
console.log('收集到的元素:', elements);
if (elements.length === 0) {
this.$message.error('没有找到需要导出的内容');
this.downloadLoading = false;
return;
}
console.log(`准备截图 ${elements.length} 个页面`);
// 截图所有元素
const images = await this.captureElements(elements);
......@@ -657,24 +652,6 @@ export default {
}
},
// 加载字体
async loadFonts() {
try {
await Promise.all([
document.fonts.load('1em SourceHanSerifCN-Regular'),
document.fonts.load('1em SourceHanSerifCN-Bold'),
document.fonts.load('400 1em SourceHanSerifCN-Regular'),
document.fonts.load('700 1em SourceHanSerifCN-Bold'),
document.fonts.load('bold 1em SourceHanSerifCN-Bold')
]);
console.log('字体加载完成');
await new Promise(resolve => setTimeout(resolve, 500));
} catch (error) {
console.warn('字体加载失败:', error);
await new Promise(resolve => setTimeout(resolve, 500));
}
},
// 收集所有需要截图的元素
collectElements() {
const elements = [];
......@@ -700,85 +677,6 @@ export default {
return elements;
},
// 将伪元素转换为真实 DOM 元素
convertPseudoElements(element) {
const converted = [];
// 处理所有带有伪元素的节点
const processNode = (node) => {
if (node.nodeType !== 1) return; // 只处理元素节点
const beforeStyle = window.getComputedStyle(node, '::before');
const afterStyle = window.getComputedStyle(node, '::after');
// 处理 ::before
if (beforeStyle && beforeStyle.content && beforeStyle.content !== 'none' && beforeStyle.content !== '""') {
const beforeDiv = document.createElement('div');
beforeDiv.className = '__pseudo-before__';
// 复制样式
this.copyPseudoStyles(beforeStyle, beforeDiv.style);
// 插入到节点开头
node.insertBefore(beforeDiv, node.firstChild);
converted.push({ node, element: beforeDiv, type: 'before' });
}
// 处理 ::after
if (afterStyle && afterStyle.content && afterStyle.content !== 'none' && afterStyle.content !== '""') {
const afterDiv = document.createElement('div');
afterDiv.className = '__pseudo-after__';
// 复制样式
this.copyPseudoStyles(afterStyle, afterDiv.style);
// 插入到节点末尾
node.appendChild(afterDiv);
converted.push({ node, element: afterDiv, type: 'after' });
}
// 递归处理子节点
Array.from(node.children).forEach(child => {
if (!child.className?.includes('__pseudo-')) {
processNode(child);
}
});
};
processNode(element);
return converted;
},
// 复制伪元素样式到真实元素
copyPseudoStyles(computedStyle, targetStyle) {
const importantStyles = [
'width', 'height', 'background', 'backgroundImage', 'backgroundSize',
'backgroundPosition', 'backgroundRepeat', 'position', 'top', 'right',
'bottom', 'left', 'display', 'content', 'transform', 'margin', 'padding'
];
importantStyles.forEach(prop => {
const value = computedStyle.getPropertyValue(prop);
if (value && value !== 'none' && value !== 'auto') {
targetStyle.setProperty(prop, value);
}
});
// 处理 content 属性
if (computedStyle.content && computedStyle.content !== 'none') {
targetStyle.content = computedStyle.content;
}
},
// 移除转换的伪元素
removePseudoElements(converted) {
converted.forEach(({ element }) => {
if (element && element.parentNode) {
element.parentNode.removeChild(element);
}
});
},
// 截图所有元素
async captureElements(elements) {
const images = [];
......@@ -787,10 +685,10 @@ export default {
const { element, name } = elements[i];
console.log(`正在截图: ${name} (${i + 1}/${elements.length})`);
// 转换伪元素
const convertedPseudos = this.convertPseudoElements(element);
try {
// 截图前仅临时禁用可能导致跨域读取错误的第三方样式(如 mapbox)
this.__problemStylesDisabled = this.disableProblematicExternalStyles()
// 临时移除 margin
const originalMargin = element.style.margin;
element.style.margin = '0';
......@@ -802,69 +700,32 @@ export default {
// 等待一下,确保样式生效
await new Promise(resolve => setTimeout(resolve, 50));
const fontEmbedCSS = await htmlToImage.getFontEmbedCSS(element);
// 截图
const dataUrl = await htmlToImage.toJpeg(element, {
quality: 0.95,
pixelRatio: 1,
quality: 1,
cacheBust: true,
skipFonts: false, // 保留字体
// 过滤掉外部资源,避免 CORS 错误和性能问题
filter: (node) => {
// 排除外部样式表链接
if (node.tagName === 'LINK' && node.rel === 'stylesheet') {
const href = node.href || '';
// 排除所有外部域名的样式表(特别是 mapbox)
if (href.includes('mapbox') ||
(href.startsWith('http') && !href.includes(window.location.hostname))) {
return false;
}
}
// 排除可能引起 CORS 问题的外部资源
if (node.tagName === 'SCRIPT' || node.tagName === 'IFRAME') {
return false;
}
// skipFonts: true,
// useCors:true,
fontEmbedCSS:fontEmbedCSS
return true;
},
// 忽略 CSS 处理错误,继续生成图片
onclone: (clonedDoc) => {
// 移除可能导致问题的外部样式表
const externalLinks = clonedDoc.querySelectorAll('link[rel="stylesheet"]');
externalLinks.forEach(link => {
const href = link.href || '';
if (href.includes('mapbox') ||
(href.startsWith('http') && !href.includes(window.location.hostname))) {
link.remove();
}
});
}
});
// 恢复 margin
element.style.margin = originalMargin;
// 获取元素尺寸
const rect = element.getBoundingClientRect();
images.push({
dataUrl,
width: rect.width,
height: rect.height,
width: 1280,
height: 1703,
name
});
console.log(`${name} 截图完成 (${rect.width}x${rect.height})`);
// 短暂延迟,避免浏览器卡顿
await new Promise(resolve => setTimeout(resolve, 100));
} catch (error) {
console.error(`${name} 截图失败:`, error);
throw new Error(`${name} 截图失败`);
//throw new Error(`${name} 截图失败`);
} finally {
// 移除转换的伪元素
this.removePseudoElements(convertedPseudos);
// 恢复被禁用的第三方样式
this.restoreProblematicExternalStyles()
}
}
......@@ -1504,13 +1365,35 @@ export default {
this.$message.error(res.data.message)
}
})
},
// 仅禁用会触发跨域 cssRules 读取错误的外部样式(如 mapbox)
disableProblematicExternalStyles() {
const disabled = []
const links = Array.from(document.querySelectorAll('link[rel="stylesheet"]'))
links.forEach(link => {
const href = link && link.href ? String(link.href) : ''
// 精确匹配 mapbox 样式来源
const isMapbox = /(^https?:\/\/)?api\.mapbox\.com\/mapbox-gl-js\//.test(href)
if (isMapbox && !link.disabled) {
link.disabled = true
disabled.push(link)
}
})
return disabled
},
restoreProblematicExternalStyles() {
if (!this.__problemStylesDisabled || this.__problemStylesDisabled.length === 0) return
this.__problemStylesDisabled.forEach(link => {
if (link) link.disabled = false
})
this.__problemStylesDisabled = []
}
}
}
</script>
<style>
@font-face {
/* @font-face {
font-family: 'SourceHanSerifCN-Regular';
src: url('https://im.oytour.com/tripfont/SourceHanSerifCN-Regular.otf') format(opentype);
}
......@@ -1518,6 +1401,22 @@ export default {
@font-face {
font-family: 'SourceHanSerifCN-Bold';
src: url('https://im.oytour.com/tripfont/SourceHanSerifCN-Bold.otf') format(opentype);
} */
@font-face {
font-family: 'SourceHanSerifCN';
src: url('https://im.oytour.com/tripfont/SourceHanSerifCN-Regular.otf') format(opentype);
font-weight: 400;
font-style: normal;
font-display: swap;
}
/* Bold */
@font-face {
font-family: 'SourceHanSerifCN';
src: url('https://im.oytour.com/tripfont/SourceHanSerifCN-Bold.otf') format(opentype);
font-weight: 700;
font-style: normal;
font-display: swap;
}
.preview-notice-tools{
height: 54px;
......@@ -1540,7 +1439,7 @@ export default {
background-size: 100% 100%;
height: 1703px;
position: relative;
font-family: 'SourceHanSerifCN-Regular';
font-family: 'SourceHanSerifCN';
/* padding-bottom: 57px; */
overflow: hidden;
box-sizing: border-box;
......@@ -1550,6 +1449,10 @@ export default {
.PreviewNotice * {
line-height: 1;
}
.cover-page *,
.PreviewNotice * {
font-family: 'SourceHanSerifCN' !important;
}
.cover-page {
background: url('https://im.oytour.com/tripImg/638957870917977223.jpg') no-repeat 0% 0%;
......@@ -1560,7 +1463,7 @@ export default {
font-weight: 800;
font-size: 104px;
color: #101519;
font-family: 'SourceHanSerifCN-Bold';
/* font-family: 'SourceHanSerifCN-Bold'; */
}
.cover-page .qr-code {
......@@ -1640,7 +1543,8 @@ export default {
.PreviewNotice .trip-list .date-td {
color: #0F2350;
font-family: 'SourceHanSerifCN-Bold';
font-weight: bold;
/* font-family: 'SourceHanSerifCN-Bold'; */
}
.PreviewNotice .trip-list .date-td .date-no {
......@@ -1661,7 +1565,7 @@ export default {
font-weight: 400;
font-size: 18px;
color: #000000;
font-family: 'SourceHanSerifCN-Regular';
/* font-family: 'SourceHanSerifCN-Regular'; */
}
.PreviewNotice .trip-list td:last-child {
......@@ -1669,7 +1573,8 @@ export default {
}
.PreviewNotice .title {
font-family: 'SourceHanSerifCN-Bold';
/* font-family: 'SourceHanSerifCN-Bold'; */
font-weight: bold;
}
.PreviewNotice .tc-no {
......@@ -1697,7 +1602,8 @@ export default {
color: #FFFFFF;
margin-top: 50px;
text-align: center;
font-family: 'SourceHanSerifCN-Bold';
/* font-family: 'SourceHanSerifCN-Bold'; */
font-weight: bold;
}
.PreviewNotice .split-line {
......@@ -1749,7 +1655,7 @@ export default {
margin-bottom: 39px;
}
.PreviewNotice-header.small::before {
.PreviewNotice-header.small .header-line {
background: url('https://viitto-1301420277.cos.ap-chengdu.myqcloud.com/Upload/Goods/638956940819119044.png') no-repeat center center;
background-size: 100% 100%;
position: absolute;
......@@ -1758,7 +1664,6 @@ export default {
width: 444px;
height: 12px;
display: inline-block;
content: ' ';
}
.PreviewNotice .base-info {
......@@ -1793,14 +1698,15 @@ export default {
.PreviewNotice .base-info .base-info-item .info-content .info-title {
font-size: 28px;
color: #0F2350;
font-family: 'SourceHanSerifCN-Bold';
/* font-family: 'SourceHanSerifCN-Bold'; */
font-weight: bold;
}
.PreviewNotice .base-info .base-info-item .info-content .info-content {
font-weight: 400;
font-size: 26px;
color: #000000;
font-family: 'SourceHanSerifCN-Regular';
/* font-family: 'SourceHanSerifCN-Regular'; */
margin-top: 14px;
}
......@@ -1856,7 +1762,8 @@ export default {
font-weight: 800;
font-size: 32px;
color: #FFFFFF;
font-family: 'SourceHanSerifCN-Bold';
/* font-family: 'SourceHanSerifCN-Bold'; */
font-weight: bold;
display: inline-block;
/* margin-bottom: 20px; */
}
......@@ -1865,7 +1772,8 @@ export default {
font-weight: 800;
font-size: 28px;
color: #0F2350;
font-family: 'SourceHanSerifCN-Bold';
/* font-family: 'SourceHanSerifCN-Bold'; */
font-weight: bold;
padding: 32px 0px 20px 0px;
}
......@@ -1875,7 +1783,7 @@ export default {
color: #000000;
line-height: 42px;
padding-bottom:10px;
font-family: 'SourceHanSerifCN-Regular';
/* font-family: 'SourceHanSerifCN-Regular'; */
}
/* 图片容器样式 */
......@@ -1911,9 +1819,7 @@ export default {
right: 0;
}
.PreviewNotice-footer::after {
content: ' ';
display: block;
.PreviewNotice-footer .footer-img {
width: 612px;
height: 34px;
background: url('https://viitto-1301420277.cos.ap-chengdu.myqcloud.com/Upload/Goods/638956342477463092.png') no-repeat center center;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment