Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
S
SuperMan
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
1
Issues
1
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
罗超
SuperMan
Commits
b2a8d572
Commit
b2a8d572
authored
Oct 14, 2025
by
罗超
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
优化内容
parent
a380fcba
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
80 additions
and
174 deletions
+80
-174
PreviewNotice.vue
src/pages/travel-notice/PreviewNotice.vue
+80
-174
No files found.
src/pages/travel-notice/PreviewNotice.vue
View file @
b2a8d572
...
...
@@ -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
::befor
e
{
.PreviewNotice-header.small
.header-lin
e
{
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
;
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment