feat: 疗休养优化移动端

This commit is contained in:
2026-11-05 08:37:27 +08:00
parent fd117b4fc3
commit 4d9fd57d6c
7 changed files with 421 additions and 112 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 738 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

@@ -0,0 +1,128 @@
<template>
<van-popup :value="value" position="right" class="therapy-notice-preview" :close-on-click-overlay="false"
:close-on-popstate="true" @input="$emit('input', $event)" @opened="loadPdf" @closed="releasePdf">
<van-nav-bar :title="title" left-arrow left-text="返回" @click-left="$emit('input', false)"></van-nav-bar>
<div class="therapy-notice-preview__body">
<div v-if="src" ref="viewer" class="therapy-notice-preview__viewer"></div>
<div v-else class="therapy-notice-preview__content" v-html="content"></div>
<div v-if="loading" class="therapy-notice-preview__status"><van-loading vertical>正在加载须知...</van-loading></div>
<div v-if="failed" class="therapy-notice-preview__status">
<van-empty image="error" description="PDF 加载失败,请重试">
<van-button round type="primary" size="small" @click="loadPdf">重新加载</van-button>
</van-empty>
</div>
</div>
</van-popup>
</template>
<script>
module.exports = {
name: 'TherapyNoticePreview',
props: {
value: { type: Boolean, default: false },
src: { type: String, default: '' },
title: { type: String, default: '疗休养须知' },
content: { type: String, default: '' }
},
data() {
return { loading: false, failed: false }
},
methods: {
loadPdf() {
this.releasePdf()
if (!this.value || !this.src) return
this.loading = true
try {
const viewer = new Pdfh5(this.$refs.viewer, { pdfurl: this.src, URIenable: false })
this._pdfViewer = viewer
viewer.on('complete', (status) => {
if (this._pdfViewer !== viewer) return
this.loading = false
this.failed = status !== 'success'
})
viewer.on('error', () => {
if (this._pdfViewer !== viewer) return
this.loading = false
this.failed = true
})
} catch (error) {
this.loading = false
this.failed = true
}
},
releasePdf() {
const viewer = this._pdfViewer
this._pdfViewer = null
if (viewer) viewer.destroy()
this.loading = false
this.failed = false
}
},
beforeDestroy() {
this.releasePdf()
}
}
</script>
<style>
.therapy-notice-preview.van-popup {
display: flex;
flex-direction: column;
width: 100vw;
max-width: none;
border-radius: 0;
height: 100%;
height: 100dvh;
overflow: hidden;
background: #f3f7fd;
box-sizing: border-box;
padding-top: env(safe-area-inset-top);
padding-bottom: env(safe-area-inset-bottom);
}
.therapy-notice-preview > .van-nav-bar {
flex-shrink: 0;
background: #fff;
}
.therapy-notice-preview .van-nav-bar__title {
max-width: 60%;
}
.therapy-notice-preview__body {
position: relative;
flex: 1;
min-height: 0;
overflow: hidden;
}
.therapy-notice-preview__viewer {
width: 100%;
height: 100%;
}
.therapy-notice-preview__content {
height: 100%;
padding: 16px;
overflow: auto;
box-sizing: border-box;
background: #fff;
line-height: 1.8;
overflow-wrap: anywhere;
}
.therapy-notice-preview__content img,
.therapy-notice-preview__content video {
max-width: 100% !important;
height: auto !important;
}
.therapy-notice-preview__status {
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
background: #f3f7fd;
}
</style>
@@ -2,80 +2,232 @@
layout("/platform/zhgh/staffbenefit/therapyRecuperation/layouts/mobile.html"){
#-->
<style>
.therapy-home {
padding-bottom: calc(16px + env(safe-area-inset-bottom));
color: #323233;
#app.therapy-home {
padding-bottom: calc(20px + env(safe-area-inset-bottom));
background: #edf4ff !important;
color: #142544;
}
.therapy-home .van-nav-bar--fixed {
left: 50%;
width: 100%;
max-width: 640px;
transform: translateX(-50%);
#app.therapy-home .van-nav-bar {
background: #f0f6ff;
}
.therapy-home .van-doc-card {
#app.therapy-home .van-nav-bar__title {
color: #142544;
font-size: 19px;
font-weight: 700;
}
.therapy-home .therapy-hero {
margin: 12px;
padding: 16px;
background-color: #fff;
border-radius: 12px;
line-height: 24px;
overflow: hidden;
border-radius: 18px;
background: #e5efff;
}
.therapy-home .therapy-slide {
position: relative;
aspect-ratio: 1.88;
}
.therapy-home .therapy-banner {
display: block;
width: calc(100% - 24px);
margin: 12px;
height: clamp(140px, 42vw, 220px);
overflow: hidden;
border-radius: 12px;
position: absolute;
inset: 0;
width: 100%;
height: 100%;
}
.therapy-home .van-grid-item__content {
padding: 12px 8px;
border-radius: 8px;
.therapy-home .therapy-hero-copy {
position: absolute;
inset: 20px 22px 28px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: flex-start;
pointer-events: none;
}
.therapy-home .van-grid-item__content:active {
background-color: #f2f7fc;
.therapy-home .therapy-hero-badge {
padding: 3px 12px;
border-radius: 20px;
background: #fff0d6;
color: #b94025;
font-size: 12px;
font-weight: 600;
line-height: 20px;
}
.therapy-home .choose_button .button_text {
font-size: 14px;
margin-top: 10px;
font-weight: 500;
.therapy-home .therapy-hero-title {
margin: 10px 0;
font-size: clamp(26px, 7.2vw, 42px);
font-weight: 800;
line-height: 1.3;
letter-spacing: 1px;
white-space: pre-line;
color: #102b54;
}
.therapy-home .therapy-hero-subtitle {
margin: 0;
font-size: clamp(11px, 3vw, 16px);
line-height: 1.6;
color: #253e60;
}
.therapy-home .van-swipe__indicator {
width: 6px;
height: 6px;
border: 1px solid #fff;
background: #a6caff;
opacity: 1;
}
#app.therapy-home .van-doc-card {
margin: 14px 12px 0;
padding: 16px 14px;
border: 1px solid #fff;
border-radius: 18px;
background: #fbfdff;
box-shadow: 0 7px 22px rgba(65, 115, 183, .06);
}
.therapy-home .therapy-section-heading {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
padding: 2px 0 15px;
margin-bottom: 14px;
border-bottom: 1px solid #dce8fb;
}
.therapy-home .notice-title {
margin: 0 0 12px;
padding-left: 10px;
border-left: 3px solid #1867b0;
font-size: 16px;
font-weight: 600;
display: flex;
align-items: center;
gap: 8px;
flex-shrink: 0;
margin: 0;
font-size: 18px;
font-weight: 700;
line-height: 26px;
}
.therapy-home .pre_text {
white-space: break-spaces;
overflow-wrap: anywhere;
.therapy-home .notice-title .van-icon {
color: #087bdc;
font-size: 23px;
}
.therapy-home .therapy-section-caption {
color: #6e7d95;
font-size: 11px;
line-height: 18px;
}
.therapy-home .therapy-route-grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 12px;
}
.therapy-home .therapy-route {
display: flex;
flex-direction: column;
align-items: flex-start;
min-width: 0;
padding: 12px 14px 16px;
border: 0;
border-radius: 14px;
background: #e7f1ff;
color: #183766;
text-align: left;
font: inherit;
cursor: pointer;
-webkit-tap-highlight-color: transparent;
}
.therapy-home .therapy-route--outside {
background: #fff0da;
color: #663a1a;
}
.therapy-home .therapy-route:active { opacity: .8; }
.therapy-home .therapy-route:focus-visible { outline: 2px solid #147ee9; outline-offset: 3px; }
.therapy-home .therapy-route-icon {
display: block;
width: 84px;
height: 76px;
object-fit: contain;
margin-bottom: 4px;
}
.therapy-home .therapy-route-label {
display: flex;
align-items: center;
gap: 8px;
font-size: 19px;
font-weight: 700;
line-height: 28px;
}
.therapy-home .therapy-route-label .van-icon {
color: #398bec;
font-size: 20px;
}
.therapy-home .therapy-route--outside .van-icon { color: #f3a331; }
.therapy-home .therapy-notice-file {
display: block;
width: 100%;
padding: 0;
overflow: hidden;
border: 1px solid #e1e9f6;
border-radius: 12px;
background: #fff;
text-align: left;
font: inherit;
cursor: pointer;
box-shadow: 0 4px 12px rgba(65, 115, 183, .07);
}
.therapy-home .therapy-notice-cover {
display: block;
width: 100%;
height: clamp(116px, 34vw, 190px);
object-fit: cover;
}
.therapy-home .therapy-notice-file-info {
display: flex;
align-items: center;
gap: 12px;
padding: 14px;
}
.therapy-home .therapy-notice-file-copy { flex: 1; min-width: 0; }
.therapy-home .therapy-notice-filename {
display: block;
margin: 0 0 5px;
color: #23334d;
font-size: 14px;
line-height: 1.8;
overflow-x: auto;
font-weight: 600;
line-height: 22px;
overflow-wrap: anywhere;
}
.therapy-home .pre_text img,
.therapy-home .pre_text video {
max-width: 100% !important;
height: auto !important;
}
.therapy-home .therapy-pdf-icon { width: 30px; height: 36px; object-fit: contain; }
.therapy-home .therapy-notice-empty { padding: 12px 0; }
.therapy-home .therapy-notice-loading { padding: 25px 0; text-align: center; }
.therapy-home .pdf_div {
height: 70vh;
min-height: 320px;
overflow: auto;
@media (max-width: 360px) {
.therapy-home .therapy-hero-copy { inset: 15px 16px 24px; }
.therapy-home .therapy-hero-title { margin: 6px 0; }
.therapy-home .therapy-hero-badge { font-size: 11px; line-height: 17px; }
.therapy-home .therapy-section-heading { flex-wrap: wrap; }
.therapy-home .therapy-route { padding: 10px 12px 14px; }
.therapy-home .therapy-route-label { font-size: 17px; }
}
</style>
<div id="app" class="therapy-home therapy-mobile" v-cloak>
@@ -85,28 +237,61 @@ layout("/platform/zhgh/staffbenefit/therapyRecuperation/layouts/mobile.html"){
</van-sticky>
<!--顶部轮播图-->
<van-image class="therapy-banner" fit="cover" alt="疗休养风景" src="/assets/staffbenefit/therapyRecuperation/img/index1.jpeg"></van-image>
<van-swipe class="therapy-hero" :autoplay="5000" indicator-color="#147ee9">
<van-swipe-item v-for="banner in banners" :key="banner.image">
<div class="therapy-slide">
<van-image class="therapy-banner" fit="cover" :alt="banner.alt" :src="banner.image"></van-image>
<div class="therapy-hero-copy">
<span class="therapy-hero-badge">工会关怀 · 健康同行</span>
<h1 class="therapy-hero-title">{{banner.title}}</h1>
<p class="therapy-hero-subtitle">{{banner.subtitle}}</p>
</div>
</div>
</van-swipe-item>
</van-swipe>
<!--按钮导航-->
<div class="choose_button van-doc-card">
<van-grid :column-num="Math.max(chooseButton.length, 1)" :border="false">
<van-grid-item @click="toLineList(item.value)" v-for="item in chooseButton" :key="item.value">
<van-image width="46" height="46" :src="item.imgUrl"></van-image>
<div class="button_text">{{item.label}}</div>
</van-grid-item>
</van-grid>
<div class="therapy-section-heading">
<h2 class="notice-title"><van-icon name="calendar-o"></van-icon>疗休养报名</h2>
<span class="therapy-section-caption">选择出行类型,查看可报线路</span>
</div>
<div class="therapy-route-grid">
<button type="button" class="therapy-route" :class="{'therapy-route--outside': Number(item.value) === 1}"
@click="toLineList(item.value)" v-for="item in chooseButton" :key="item.value">
<img class="therapy-route-icon" :src="Number(item.value) === 0 ? '/assets/staffbenefit/therapyRecuperation/img/home-province-in-v2.png' : '/assets/staffbenefit/therapyRecuperation/img/home-province-out-v2.png'" alt="">
<span class="therapy-route-label">{{item.label}}<van-icon name="arrow"></van-icon></span>
</button>
</div>
</div>
<!--疗休养描述-->
<div class="van-doc-card">
<h2 class="notice-title">疗休养须知</h2>
<div v-if="isPdf" class="pre_text" v-html="configData.notice"></div>
<div v-else id="demo" class="pdf_div"></div>
<div class="therapy-section-heading">
<h2 class="notice-title"><van-icon name="description"></van-icon>疗休养须知</h2>
<span class="therapy-section-caption">报名前请仔细阅读</span>
</div>
<div v-if="configLoading" class="therapy-notice-loading"><van-loading>正在加载须知...</van-loading></div>
<van-empty v-else-if="configError" class="therapy-notice-empty" image="error" description="须知加载失败">
<van-button size="small" round type="primary" @click="getConfigData">重新加载</van-button>
</van-empty>
<button v-else-if="configData.notice" type="button" class="therapy-notice-file" @click="noticeVisible = true">
<img class="therapy-notice-cover" src="/assets/staffbenefit/therapyRecuperation/img/home-notice-cover-v2.png" alt="疗休养须知文件封面">
<span class="therapy-notice-file-info">
<span class="therapy-notice-file-copy">
<span class="therapy-notice-filename">{{noticeTitle}}</span>
<span class="therapy-section-caption">{{noticePdfUrl ? 'PDF 文档 · 点击预览' : '通知文件 · 点击查看'}}</span>
</span>
<img class="therapy-pdf-icon" src="/assets/platform/img/svg/pdf.svg" alt="PDF" v-if="noticePdfUrl">
<van-icon name="arrow" v-else></van-icon>
</span>
</button>
<van-empty v-else class="therapy-notice-empty" description="暂无疗休养须知"></van-empty>
</div>
<therapy-notice-preview v-model="noticeVisible" :src="noticePdfUrl" :title="noticeTitle" :content="configData.notice || ''"></therapy-notice-preview>
</div>
<script>
let pdfh5 = null
const vue = new Vue({
el: '#app',
mixins: [mobileMixins],
@@ -114,11 +299,36 @@ layout("/platform/zhgh/staffbenefit/therapyRecuperation/layouts/mobile.html"){
return {
chooseButton: [],
configData: {},
isPdf: false,
configLoading: true,
configError: false,
noticeVisible: false,
noticePdfUrl: '',
noticeTitle: '疗休养工作相关安排及注意事项',
banners: [
{
image: '/assets/staffbenefit/therapyRecuperation/img/home-mountain-v2.png',
alt: '青山晨光与疗休养度假风景',
title: '普惠疗休养\n乐享健康时光',
subtitle: '亲近自然 · 放松身心 · 开启美好旅程'
},
{
image: '/assets/staffbenefit/therapyRecuperation/img/home-lake-v2.png',
alt: '湖畔山水与疗休养度假风景',
title: '相约山水间\n共赴好时光',
subtitle: '走进湖光山色 · 感受自然之美'
}
],
}
},
components: {
'therapy-notice-preview': httpVueLoader('/components/module/therapyRecuperation/NoticePreview.vue')
},
methods: {
historyBack() {
if (this.noticeVisible) {
this.noticeVisible = false
return
}
window.history.back()
},
async toLineList(index) {
@@ -135,8 +345,28 @@ layout("/platform/zhgh/staffbenefit/therapyRecuperation/layouts/mobile.html"){
location.href = '/platform/inclusiveTherapyV3/mobile/mobileLineListPage'
},
async getConfigData() {
const resp = await $.get('/platform/inclusiveTherapyV3/TheRapyConfig/findOne')
this.configData = resp.data
this.configLoading = true
this.configError = false
try {
const resp = await $.get('/platform/inclusiveTherapyV3/TheRapyConfig/findOne')
if (resp.code !== 0) throw new Error(resp.msg || '须知加载失败')
this.configData = resp.data || {}
this.noticePdfUrl = ''
const notice = this.configData.notice || ''
const doc = new DOMParser().parseFromString(notice, 'text/html')
const links = Array.from(doc.querySelectorAll('a[href]'))
const link = links.find(item => /\.pdf(?:[?#]|$)/i.test(item.getAttribute('href')) || /pdf/i.test(item.textContent)) || links[0]
const href = link ? link.getAttribute('href') : notice.trim()
if (link || /^https?:\/\/.*\.pdf(?:[?#]|$)/i.test(href) || /^\/[^<>]*\.pdf(?:[?#]|$)/i.test(href)) {
const url = new URL(href, location.href)
if (['http:', 'https:'].includes(url.protocol)) this.noticePdfUrl = url.href
}
this.noticeTitle = link && link.textContent.trim() ? link.textContent.trim() : '疗休养工作相关安排及注意事项'
} catch (error) {
this.configError = true
} finally {
this.configLoading = false
}
},
},
async created() {
@@ -144,55 +374,6 @@ layout("/platform/zhgh/staffbenefit/therapyRecuperation/layouts/mobile.html"){
this.chooseButton = this.chooseButton.filter(o => o.value !== 3 && o.value !== 2)
await this.getConfigData()
try {
const parser = new DOMParser();
const doc = parser.parseFromString(this.configData.notice, 'text/html');
const link = doc.querySelector('a');
const href = link.getAttribute('href');
this.$nextTick(() => {
pdfh5 = new Pdfh5('#demo', {
pdfurl: href,
});
})
}catch (e) {
this.isPdf = true
document.addEventListener('click', function (event) {
// 打印被点击的元素标签名和 class
if (event.target.tagName === 'IMG' && !['van-image__img', 'top_icon'].includes(event.target.className)) {
vant.ImagePreview({
images: [event.target.src],
closeable: true,
})
}
})
}
this.$nextTick(() => {
if (pdfh5) pdfh5.on("complete", function () {
const imgDoc = document.querySelectorAll('img')
let array = []
let classArray = []
imgDoc.forEach(o => {
if(!['van-image__img', 'top_icon'].includes(o.getAttribute('class'))) {
classArray.push(o.getAttribute('class'))
array.push(o.getAttribute('src'))
}
})
document.addEventListener('click', function (event) {
// 打印被点击的元素标签名和 class
if (event.target.tagName === 'IMG' && !['van-image__img', 'top_icon'].includes(event.target.className)) {
const index = classArray.indexOf(event.target.className)
if(index !== -1) {
vant.ImagePreview({
images: array,
startPosition: index,
closeable: true,
})
}
}
})
})
})
}
})
</script>