feat: 20250711

This commit is contained in:
mac·ufutx 2025-07-11 14:51:12 +08:00
parent 1c99cb6c75
commit c1c60a5313
21 changed files with 628 additions and 525 deletions

View File

@ -1,2 +1,4 @@
# .env.development (开发环境)
VITE_API_BASE_URL = 'http://health.ufutx.net'
# VITE_API_BASE_URL = 'http://health.ufutx.net'
VITE_API_BASE_URL = 'https://health.ufutx.com'

View File

@ -32,9 +32,7 @@
"axios": "^1.9.0",
"echarts": "^5.6.0",
"element-plus": "^2.10.1",
"motion-v": "^1.3.1",
"pinia": "^2.1.7",
"postcss-px-to-viewport": "^1.1.1",
"swiper": "^11.2.10",
"vue": "^3.5.13",
"vue-i18n": "^9.8.0",
@ -65,6 +63,7 @@
"postcss": "^8.4.24",
"postcss-px-to-viewport-8-plugin": "^1.2.5",
"prettier": "^3.5.3",
"rollup-plugin-visualizer": "^6.0.3",
"sass": "^1.89.2",
"tailwind-merge": "^3.3.1",
"tw-animate-css": "^1.3.4",

View File

@ -64,22 +64,50 @@ const updateAnimation = () => {
const style = document.createElement('style')
style.id = 'marquee-keyframes'
style.textContent = `
@keyframes marquee {
0% { transform: translateX(var(--initial-offset)); }
100% { transform: translateX(calc(var(--initial-offset) - ${originalWidth.value}px)); }
}
`
@keyframes marquee {
0% { transform: translateX(var(--initial-offset)); }
/* 滚动距离 = 原始内容宽度,确保克隆内容衔接 */
100% { transform: translateX(calc(var(--initial-offset) - ${originalWidth.value}px)); }
}
`
//
const existingStyle = document.head.querySelector('#marquee-keyframes')
if (existingStyle) document.head.removeChild(existingStyle)
document.head.appendChild(style)
}
//
const watchImagesLoad = () => {
if (!originalRef.value) return
//
const images = originalRef.value.querySelectorAll('img')
if (images.length === 0) return
//
let loadedCount = 0
images.forEach(img => {
//
if (img.complete) {
loadedCount++
} else {
//
img.addEventListener('load', () => {
loadedCount++
if (loadedCount === images.length) {
nextTick(calculateWidth) //
}
})
}
})
}
//
onMounted(() => {
calculateWidth()
updateAnimation()
nextTick(() => {
// DOM
calculateWidth()
watchImagesLoad() //
updateAnimation()
})
})
watchEffect(() => {
calculateWidth()

View File

@ -6,8 +6,10 @@ import routes from './routes'
// const history = isProduction
// ? createWebHashHistory() // 线上:带#
// : createWebHistory() // 本地:无#
const routerBase = import.meta.env.MODE === 'production' ? '/web/' : '/'
const router = createRouter({
history: createWebHashHistory(),
history: createWebHashHistory(routerBase),
routes,
scrollBehavior(_to, _from, savedPosition) {
// 添加下划线标记未使用参数

View File

@ -33,7 +33,9 @@ export const openNewWindow = (to: RouteLocationRaw) => {
// 使用导入的 router 实例调用 resolve
const routeLocation = router.resolve(to)
// 拼接完整URL当前域名 + 路由路径)
const fullUrl = `${window.location.origin}${routeLocation.href}`
const fullUrl = `${window.location.origin}/${import.meta.env.MODE === 'production' ? '/web/' : '/'}${routeLocation.href}`
console.log(window.location)
console.log(fullUrl)
// 新窗口打开
window.open(fullUrl, '_blank', 'noopener,noreferrer')
} catch (error) {

View File

@ -1,15 +1,13 @@
<template>
<section class="app-scenes">
<h3 class="app-title">应用场景</h3>
<!-- <div class="scenes-tabs">-->
<!-- <span v-for="(tab, i) in tabs" :key="i" :class="{ active: currentTab === i }" @click="currentTab = i">{{-->
<!-- tab-->
<!-- }}</span>-->
<!-- </div>-->
<!-- 标签切换友福动态 / 媒体报道 -->
<!-- 标签切换 -->
<el-tabs v-model="activeTab" class="news-tabs" @tab-click="handleTabClick">
<el-tab-pane v-for="(tab, index) in valueTabs" :key="index" :label="tab.title" :name="tab.name">
<template #label>
<p class="scene-label">{{ tab.title }}</p>
</template>
<div v-show="currentTabIndex === index" class="scenes-content">
<div class="scene-desc">
<div class="speech-title">
@ -74,34 +72,32 @@ const valueTabs = [
image: 'https://images.health.ufutx.com/202506/20/ba5d18e65df7d1fef4d9be2fdb185c23.png'
}
]
const activeTab = ref(valueTabs[0].name) //
const currentTabIndex = ref(0) //
//
// const currentTab = computed(() => {
// return valueTabs[currentTabIndex.value] || valueTabs[0]
// })
const handleTabClick = (tab: any, event: Event) => {
console.log(tab.props.name, event)
currentTabIndex.value = valueTabs.findIndex(item => item.name === tab.props.name)
const activeTab = ref(valueTabs[0].name)
const currentTabIndex = ref(0)
console.log(currentTabIndex.value)
// currentPage.value = 1 //
const handleTabClick = (tab: any) => {
currentTabIndex.value = valueTabs.findIndex(item => item.name === tab.props.name)
}
</script>
<style scoped lang="less">
/* 标签切换样式(还原设计的蓝色下划线) */
/* 基础样式保持不变 */
.news-tabs {
.pt(50px);
padding-top: 50px;
:deep(.el-tabs__header) {
width: 100%;
.el-tabs__nav {
justify-content: center;
}
}
:deep(.el-tabs__nav-wrap) {
//margin-bottom: 1.04167vw;
&:after {
content: '';
position: absolute;
left: 0;
bottom: 0;
width: 100vw;
width: 100%;
height: 0.5px;
background-color: #b5b5b5;
z-index: var(--el-index-normal);
@ -109,10 +105,9 @@ const handleTabClick = (tab: any, event: Event) => {
}
:deep(.el-tabs__active-bar) {
background-color: @primary-dark; // 线
background-color: @primary-dark;
height: 3px;
bottom: -10px !important;
margin-top: 10px;
}
:deep(.el-tabs__header) {
@ -122,30 +117,20 @@ const handleTabClick = (tab: any, event: Event) => {
width: 100%;
margin-bottom: 10px;
display: flex;
gap: 100px;
gap: 100px; /* 桌面端标签间距 */
.el-tabs__item {
font-size: @font-size-lg;
color: @text-color-secondary;
&.is-active {
color: @primary-dark; //
color: @primary-dark;
}
}
}
}
}
.news-tabs {
//
:deep(.el-tabs__header) {
width: 100%; //
.el-tabs__nav {
justify-content: center; //
}
}
}
.app-scenes {
text-align: center;
padding: 50px 0;
@ -154,62 +139,32 @@ const handleTabClick = (tab: any, event: Event) => {
color: @text-color;
font-size: 32px;
font-weight: 600;
line-height: 32px; /* 100% */
line-height: 32px;
letter-spacing: 0.32px;
//.mb(50px);
}
.scenes-tabs {
margin-bottom: 40px;
span {
margin: 0 20px;
font-size: @font-size-md;
color: @text-color-secondary;
cursor: pointer;
padding-bottom: 5px;
border-bottom: 2px solid transparent;
&.active {
color: @primary-color;
border-bottom: 2px solid @primary-color;
}
}
}
.scenes-content {
display: flex;
align-items: center;
justify-content: space-between;
margin: 0 192px;
margin: 0 192px; /* 桌面端左右边距 */
padding: 80px 30px 30px 30px;
position: relative;
border-radius: 10px;
background: linear-gradient(180deg, rgba(250, 250, 250, 0) 0%, #fafafa 100%);
//background: #00acc1;
&:after {
position: absolute;
left: 500px;
bottom: 7px;
content: ' ';
width: 360px;
height: 360px;
background-image: url('https://images.health.ufutx.com/202506/20/fc55ef830c38d03501fb37bc9f111b71.png');
background-size: cover;
}
.scene-desc {
width: 600px;
width: 600px; /* 桌面端描述宽度 */
text-align: left;
//padding: 0 40px;
.speech-title {
color: @text-color;
font-size: 18px;
font-weight: 600;
display: flex; // Flex
align-items: center; //
display: flex;
align-items: center;
gap: 10px;
.mb(20px);
margin-bottom: 20px;
.icon {
width: 18px;
@ -221,32 +176,86 @@ const handleTabClick = (tab: any, event: Event) => {
.speech-subtitle {
color: @text-color;
font-size: 14px;
}
}
._text {
margin-bottom: 10px;
color: @text-color;
font-size: 14px;
._text {
margin-bottom: 10px;
}
}
}
.scene-img {
text-align: center;
width: 600px;
width: 600px; /* 桌面端图片宽度 */
img {
width: 100%;
}
}
@media (max-width: @tablet-breakpoint) {
flex-direction: column;
.scene-desc {
text-align: center;
padding: 0;
margin-bottom: 20px;
height: auto;
}
}
}
}
/* 移动端适配768px以下 */
//@media (max-width: 768px) {
// .app-scenes {
// //padding: 30px 15px; /* */
// .app-title {
// font-size: 24px; /* */
// line-height: 1.5;
// }
//
// .scenes-content {
// flex-direction: column; /* */
// //margin: 0 15px; /* */
// padding: 30px 0px; /* */
// margin: 0;
// //padding: 0;
// //background: red;
// box-sizing: border-box;
// //width: 100%;
// .scene-desc {
// width: 80%; /* */
// margin-bottom: 30px; /* */
//
// .speech-title {
// font-size: 16px; /* */
// }
//
// .speech-subtitle {
// font-size: 13px; /* */
// }
// }
//
// .scene-img {
// width: 100%; /* */
// //max-width: 400px; /* */
// margin: 0 auto; /* */
// }
// }
// }
//
// /* */
// .news-tabs {
// padding-top: 30px;
// .scene-label {
// font-size: 12px;
// }
// :deep(.el-tabs__nav) {
// gap: 20px; /* */
// overflow-x: auto; /* */
// padding: 0 10px; /* */
// scrollbar-width: none; /* */
// &::-webkit-scrollbar {
// display: none;
// }
// }
//
// :deep(.el-tabs__item) {
// font-size: 14px; /* */
// white-space: nowrap; /* */
// }
//
// :deep(.el-tabs__active-bar) {
// bottom: -8px !important; /* 线 */
// }
// }
//}
</style>

View File

@ -1,4 +1,5 @@
<template>
<!-- 模板部分保持不变 -->
<section class="qualification">
<h2 class="section-title">资质认证</h2>
<p class="section-subtitle">成立至今获得多项荣誉</p>
@ -7,7 +8,8 @@
<div class="certificate-section">
<div class="certificate-list">
<Marquee
:duration="60"
v-if="certificateImgs.length > 0"
:duration="300"
:reverse="false"
repeatCount="3"
pause-on-hover
@ -18,15 +20,12 @@
v-for="(src, index) in certificateImgs"
:key="index"
class="certificate-item"
:class="{ 'scale-up': activeIndex === index }"
@mouseenter="activeIndex = index"
@mouseleave="activeIndex = -1"
>
<el-image
:ref="
(el: any) => {
imageRefs[index] = el
}
"
:ref="(el: any) => (imageRefs[index] = el)"
:src="src"
alt="certificate"
class="certificate-image"
@ -34,8 +33,8 @@
:preview-src-list="certificateImgs"
show-progress
preview-teleported
fit="cover"
:initial-index="index"
:class="{ 'scale-up': activeIndex === index }"
/>
<div
v-show="activeIndex === index"
@ -55,33 +54,53 @@
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { ref, onMounted } from 'vue'
import Marquee from '@/components/Marquee.vue'
import type { ImageInstance } from 'element-plus'
// ref
import request from '@/utils/request.ts'
const certificateImgs = ref<string[]>([])
const imageRefs = ref<(ImageInstance | null)[]>([])
const certificateImgs = [
'https://images.health.ufutx.com/202506/20/6972bf0f5da9af6ec4f2b8fba404d59e.png',
'https://images.health.ufutx.com/202506/20/6972bf0f5da9af6ec4f2b8fba404d59e.png',
'https://images.health.ufutx.com/202506/20/6972bf0f5da9af6ec4f2b8fba404d59e.png', //
'https://images.health.ufutx.com/202506/20/6972bf0f5da9af6ec4f2b8fba404d59e.png',
'https://images.health.ufutx.com/202506/20/6972bf0f5da9af6ec4f2b8fba404d59e.png',
'https://images.health.ufutx.com/202506/20/6972bf0f5da9af6ec4f2b8fba404d59e.png',
'https://images.health.ufutx.com/202506/20/6972bf0f5da9af6ec4f2b8fba404d59e.png',
'https://images.health.ufutx.com/202506/20/6972bf0f5da9af6ec4f2b8fba404d59e.png'
]
const activeIndex = ref(-1)
//
// YYYY-MM-DD
const getCurrentDate = () => {
const date = new Date()
const year = date.getFullYear()
const month = String(date.getMonth() + 1).padStart(2, '0') // 0
const day = String(date.getDate()).padStart(2, '0') //
return `${year}-${month}-${day}`
}
onMounted(async () => {
try {
// secret +
const currentDate = getCurrentDate()
const secret = `${currentDate}-ufutx`
// :platweb
const plat = 'web' //
const response = await request.get(`/go/api/${plat}/v1/assets/list?secret=${secret}`)
//
const imgList = response.data.data
.filter((item: any) => item.file) // filefile
.map((item: any) => item.file) // file
certificateImgs.value = imgList
console.log(imgList)
} catch (error) {
console.error('资质图片加载失败:', error)
}
})
const handleClick = (index: number) => {
const image = imageRefs.value[index]
if (image) {
image.showPreview() //
image.showPreview()
}
}
</script>
<style scoped lang="less">
.qualification {
text-align: center;
@ -139,7 +158,7 @@ const handleClick = (index: number) => {
//background: red;
}
.certificate-section {
padding: 122px 0px;
padding: 100px 0px;
&&::before {
content: '';
@ -161,6 +180,7 @@ const handleClick = (index: number) => {
height: 460px;
align-items: center;
//background: red;
padding-top: 32px;
margin: 0 auto;
//margin-left: 100px;
box-sizing: border-box;
@ -172,6 +192,7 @@ const handleClick = (index: number) => {
}
.certificate-item {
width: 288px; /* 固定宽度,确保能在一行放下多个 */
width: 288px; /* 固定宽度,确保能在一行放下多个 */
flex-shrink: 0; /* 禁止收缩,保证宽度不变 */
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05); /* 增加轻微阴影,提升视觉层次 */
@ -179,12 +200,25 @@ const handleClick = (index: number) => {
cursor: pointer; /* 提示可交互 */
position: relative;
transition: all 0.4s cubic-bezier(0.34, 1.56, 0.64, 1); /* 卡片整体过渡 */
width: 288px;
height: 384px;
background-image: url('https://images.health.ufutx.com/202507/08/e83e520b3e3098d5b7331f627042a1f2.png');
background-size: cover;
transform-origin: bottom center; /* &#x6838;&#x5FC3;&#xFF1A;&#x8BBE;&#x7F6E;&#x7F29;&#x653E;&#x539F;&#x70B9;&#x4E3A;&#x5E95;&#x90E8;&#x4E2D;&#x5FC3; */
transition:
transform 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94),
border-color 0.3s ease;
.certificate-image {
width: 288px;
transform-origin: bottom center; /* &#x6838;&#x5FC3;&#xFF1A;&#x8BBE;&#x7F6E;&#x7F29;&#x653E;&#x539F;&#x70B9;&#x4E3A;&#x5E95;&#x90E8;&#x4E2D;&#x5FC3; */
transition:
transform 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94),
border-color 0.3s ease;
width: 100%;
height: 100%;
//opacity: 0;
width: 260px;
height: 356px;
margin-top: 14px;
//transform: scale(0.9);
//border: 12px solid #fff;
}
.certificate-preview {
position: absolute;

View File

@ -10,6 +10,9 @@
<!-- 标签切换友福动态 / 媒体报道 -->
<el-tabs v-model="activeTab" class="news-tabs">
<el-tab-pane v-for="scene in scenes" :key="scene.name" :label="scene.title" :name="scene.name">
<template #label>
<p class="scene-label">{{ scene.title }}</p>
</template>
<div class="scenes-content">
<!-- 文字描述区 -->
<div class="scene-desc">
@ -131,6 +134,30 @@ const activeTab = ref(scenes.value[0].name)
justify-content: center; //
}
}
@media (max-width: @tablet-breakpoint) {
//flex-direction: column;
.scene-label {
font-size: @font-size-sm;
}
.speech-title {
p {
font-size: @font-size-sm;
}
}
.speech-content li {
font-size: 14px;
color: @text-color-secondary;
line-height: 25px;
position: relative;
padding-left: 12px;
white-space: pre-wrap;
}
.scene-desc {
text-align: center;
padding: 0;
margin-bottom: 20px;
}
}
}
.app-scenes {
@ -213,15 +240,6 @@ const activeTab = ref(scenes.value[0].name)
width: 100%;
}
}
@media (max-width: @tablet-breakpoint) {
flex-direction: column;
.scene-desc {
text-align: center;
padding: 0;
margin-bottom: 20px;
}
}
}
}
.speech-content {

View File

@ -261,14 +261,14 @@ const handleSelect = (index: number) => {
/* 响应式适配 */
@media (max-width: @tablet-breakpoint) {
.feature-nav {
gap: 40px;
gap: 20px;
.nav-item {
&:not(:first-child)::before {
left: -20px;
left: -12px;
height: 8px;
}
.nav-text {
font-size: @font-size-sm;
font-size: @font-size-xs;
}
}
}

View File

@ -90,8 +90,8 @@ const openReport = () => {
}
@media (max-width: @tablet-breakpoint) {
flex-direction: column;
padding: 40px 20px;
//flex-direction: column;
//padding: 40px 20px;
.speech-content {
flex-direction: column;

View File

@ -77,12 +77,12 @@ const points = [
gap: 20px;
margin: 0 auto;
.pb(20px);
@media (max-width: @tablet-breakpoint) {
grid-template-columns: repeat(2, 1fr);
}
@media (max-width: @mobile-breakpoint) {
grid-template-columns: 1fr;
}
//@media (max-width: @tablet-breakpoint) {
// grid-template-columns: repeat(2, 1fr);
//}
//@media (max-width: @mobile-breakpoint) {
// grid-template-columns: 1fr;
//}
}
.point-card {

View File

@ -2,7 +2,7 @@
<section class="banner">
<div v-motion-fade-visible class="banner-bg">
<!-- 替换为实际背景图路径 -->
<img src="https://images.health.ufutx.com/202506/12/e6ea04327d2b5dbd9e4ae441431018df.png" alt="Banner背景" />
<img src="https://images.health.ufutx.com/202507/08/a8e9e05faedce4f60b6a97e3bbcb6d1d.png" alt="Banner背景" />
</div>
<div class="news-panel">
<div
@ -145,6 +145,18 @@ const newsList = [
//
@media (max-width: 768px) {
.banner-bg {
width: 120%;
height: 830px;
overflow: hidden;
img {
width: 100%;
height: auto;
display: block;
}
}
.banner-content {
.main-title {
font-size: 32px;

View File

@ -8,6 +8,23 @@
<!-- 循环渲染所有模块 -->
<div v-for="item in modules" :key="item.id" :class="['module', `module-${item.id}`]">
<div
class="center-module"
:class="{
'hover-delay': isHover === item.id
}"
@mouseenter="handleMouseEnter(item.id)"
@mouseleave="handleMouseLeave(item.id)"
>
<div class="center-icon">
<img :src="item.icon" alt="center icon" />
</div>
<div class="center-title">{{ item.title }}</div>
<div class="center-desc">
{{ item.description }}
</div>
</div>
<div
v-if="isHover !== item.id"
class="module-content"
:class="{
hover: isHover === item.id,
@ -20,14 +37,11 @@
<img :src="item.icon" :alt="item.title" />
</div>
<div class="text">{{ item.title }}</div>
<div
v-if="isHover === item.id && isDelay[item.id]"
class="desc"
:class="{ show: isHover === item.id && isDelay[item.id] }"
>
<div v-if="isHover === item.id && isDelay[item.id]" class="desc">
{{ item.description }}
</div>
</div>
<!-- &lt;!&ndash; 中心模块 &ndash;&gt;-->
</div>
</div>
</div>
@ -158,7 +172,7 @@ const handleMouseLeave = (moduleId: number) => {
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
transform-origin: center center;
transition:
height 0.2s ease,
opacity 0.1s ease,
width 0.3s ease,
transform 1s ease;
@ -206,28 +220,19 @@ const handleMouseLeave = (moduleId: number) => {
// hover
&.hover {
width: 300px;
height: auto;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1);
transform: translateX(-20%);
.text {
overflow: auto;
text-overflow: ellipsis;
white-space: pre-wrap;
width: 100%;
}
opacity: 0;
}
//
&.hover-delay {
display: grid;
grid-template-rows: auto 1fr auto;
align-items: start;
justify-items: center;
border-radius: 16px;
background: #fff;
padding: 20px;
}
//&.hover-delay {
// display: grid;
// grid-template-rows: auto 1fr auto;
// align-items: start;
// justify-items: center;
// border-radius: 16px;
// background: #fff;
// padding: 20px;
//}
}
}
@ -259,43 +264,98 @@ const handleMouseLeave = (moduleId: number) => {
}
}
}
.center-module {
transform: translateX(-20%) !important;
text-align: center;
background-color: rgba(255, 255, 255, 1);
border-radius: 12px;
padding: 24px;
width: 300px;
max-height: 0;
opacity: 0;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
transform-origin: center center;
overflow: hidden;
position: relative;
z-index: 99;
//
@media (max-width: 768px) {
.core-value {
padding: 60px 0;
transition:
max-height 0.6s cubic-bezier(0.34, 1.56, 0.64, 1),
opacity 0.6s cubic-bezier(0.34, 1.56, 0.64, 1),
transform 0.6s cubic-bezier(0.34, 1.56, 0.64, 1);
align-content: center;
justify-content: center;
align-items: center;
// .hover :hover
&.hover-delay {
max-height: 500px; /* 设一个足够大的值(大于内容实际高度) */
height: auto !important;
opacity: 1;
}
.center-icon {
width: 100px;
height: 100px;
margin: 0 auto 10px auto;
.container {
.section-title {
font-size: 28px;
}
.diagram-wrapper {
height: auto;
padding-bottom: 150%;
.module {
.module-content {
width: 150px;
height: 60px;
.icon {
width: 40px;
height: 40px;
}
.text {
font-size: 16px;
}
&.hover {
width: 250px;
height: auto;
transform: scale(1.05);
}
}
}
}
img {
width: 100%;
height: 100%;
object-fit: contain;
}
}
.center-title {
font-size: 18px;
font-weight: bold;
color: @text-color;
margin-bottom: 10px;
}
.center-desc {
color: @text-color-secondary;
text-align: center;
font-size: 14px;
font-style: normal;
font-weight: 400;
line-height: 25px; /* 178.571% */
}
}
//
//@media (max-width: 768px) {
// .core-value {
// padding: 60px 0;
//
// .container {
// .section-title {
// font-size: 28px;
// }
//
// .diagram-wrapper {
// height: auto;
// padding-bottom: 150%;
//
// .module {
// .module-content {
// width: 150px;
// height: 60px;
//
// .icon {
// width: 40px;
// height: 40px;
// }
// .text {
// font-size: 16px;
// }
//
// &.hover {
// width: 250px;
// height: auto;
// transform: scale(1.05);
// }
// }
// }
// }
// }
// }
//}
</style>

View File

@ -5,130 +5,30 @@
<p class="section-desc">友福同享AI健康解决方案应用场景</p>
<div class="diagram-wrapper" :style="{ backgroundImage: `url(${diagramBg})` }">
<!-- 模块1AI赋能精准决策 -->
<div class="module module-1">
<!-- 循环渲染所有模块 -->
<div v-for="item in modules" :key="item.id" :class="['module', `module-${item.id}`]">
<div
class="module-content"
:class="{ hover: isHover === 1, 'hover-delay': isHover === 1 && isDelay[1] }"
@mouseenter="handleMouseEnter(1)"
@mouseleave="handleMouseLeave(1)"
:class="{
hover: isHover === item.id,
'hover-delay': isHover === item.id && isDelay[item.id]
}"
@mouseenter="handleMouseEnter(item.id)"
@mouseleave="handleMouseLeave(item.id)"
>
<div class="icon">
<img src="https://images.health.ufutx.com/202507/04/4a47a0db6e60853dedfcfdf08a5ca249.png" alt="icon" />
<img :src="item.icon" :alt="item.title" />
</div>
<div class="text">AI赋能精准决策</div>
<div v-if="isHover === 1 && isDelay[1]" class="desc" :class="{ show: isHover === 1 && isDelay[1] }">
基于海量数据分析提供精准的健康风险评估与干预建议
</div>
<!-- <div class="desc" :class="{ show: isHover === 1 && isDelay[1] }" v-show="isHover === 1">-->
<!-- 基于海量数据分析提供精准的健康风险评估与干预建议-->
<!-- </div>-->
</div>
</div>
<!-- 模块2创新七大核心 -->
<div class="module module-2">
<div
class="module-content"
:class="{ hover: isHover === 2, 'hover-delay': isHover === 2 && isDelay[2] }"
@mouseenter="handleMouseEnter(2)"
@mouseleave="handleMouseLeave(2)"
>
<div class="icon">
<img src="https://images.health.ufutx.com/202507/04/4a47a0db6e60853dedfcfdf08a5ca249.png" alt="icon" />
</div>
<div class="text">创新七大核心...</div>
<div class="desc" :class="{ show: isHover === 2 && isDelay[2] }">
七大核心技术支撑构建全场景健康服务体系
<div class="text">{{ item.title }}</div>
<div
v-if="isHover === item.id && isDelay[item.id]"
class="desc"
:class="{ show: isHover === item.id && isDelay[item.id] }"
>
{{ item.description }}
</div>
</div>
</div>
<!-- 模块3全方位的AI健... -->
<div class="module module-3">
<div
class="module-content"
:class="{ hover: isHover === 3, 'hover-delay': isHover === 3 && isDelay[3] }"
@mouseenter="handleMouseEnter(3)"
@mouseleave="handleMouseLeave(3)"
>
<div class="icon">
<img src="https://images.health.ufutx.com/202507/04/4a47a0db6e60853dedfcfdf08a5ca249.png" alt="icon" />
</div>
<div class="text">全方位的AI健...</div>
<div class="desc" :class="{ show: isHover === 3 && isDelay[3] }">
覆盖全生命周期提供一站式AI健康管理服务
</div>
</div>
</div>
<!-- 模块4双重认证教练... -->
<div class="module module-4">
<div
class="module-content"
:class="{ hover: isHover === 4, 'hover-delay': isHover === 4 && isDelay[4] }"
@mouseenter="handleMouseEnter(4)"
@mouseleave="handleMouseLeave(4)"
>
<div class="icon">
<img src="https://images.health.ufutx.com/202507/04/4a47a0db6e60853dedfcfdf08a5ca249.png" alt="icon" />
</div>
<div class="text">双重认证教练...</div>
<div class="desc" :class="{ show: isHover === 4 && isDelay[4] }">
专业认证教练团队提供个性化健康指导方案
</div>
</div>
</div>
<!-- 模块5构建健康产业... -->
<div class="module module-5">
<div
class="module-content"
:class="{ hover: isHover === 5, 'hover-delay': isHover === 5 && isDelay[5] }"
@mouseenter="handleMouseEnter(5)"
@mouseleave="handleMouseLeave(5)"
>
<div class="icon">
<img src="https://images.health.ufutx.com/202507/04/4a47a0db6e60853dedfcfdf08a5ca249.png" alt="icon" />
</div>
<div class="text">构建健康产业...</div>
<div class="desc" :class="{ show: isHover === 5 && isDelay[5] }">
整合产业资源打造闭环健康服务生态系统
</div>
</div>
</div>
<!-- 模块6中间模块 -->
<div class="module module-6">
<div
class="module-content"
:class="{ hover: isHover === 6, 'hover-delay': isHover === 6 && isDelay[6] }"
@mouseenter="handleMouseEnter(6)"
@mouseleave="handleMouseLeave(6)"
>
<div class="icon">
<img src="https://images.health.ufutx.com/202507/04/4a47a0db6e60853dedfcfdf08a5ca249.png" alt="icon" />
</div>
<div class="text">创新模式-身心双指标评估</div>
<div class="desc" :class="{ show: isHover === 6 && isDelay[6] }">
结合生理与心理健康数据生成个性化评估方案
</div>
</div>
</div>
<!-- &lt;!&ndash; 中心模块 &ndash;&gt;-->
<!-- <div class="center-module">-->
<!-- <div class="center-icon">-->
<!-- <img-->
<!-- src="https://images.health.ufutx.com/202507/04/4a47a0db6e60853dedfcfdf08a5ca249.png"-->
<!-- alt="center icon"-->
<!-- />-->
<!-- </div>-->
<!-- <div class="center-title">创新模式-身心双指标评估生成精准个性化方案</div>-->
<!-- <div class="center-desc">-->
<!-- 整合个人身理+心理健康数据健康行为个人及家族疾病史及生活方式等多维度数据全方面分析精准个性化方案-->
<!-- </div>-->
<!-- </div>-->
</div>
</div>
</section>
@ -140,6 +40,47 @@ import { ref } from 'vue'
//
const diagramBg = 'https://images.health.ufutx.com/202507/04/3b6a1b49d79c1cd13a59187e58c614a7.png'
//
const modules = [
{
id: 1,
title: 'AI赋能精准决策',
description: '基于海量数据分析,提供精准的健康风险评估与干预建议。',
icon: 'https://images.health.ufutx.com/202507/04/4a47a0db6e60853dedfcfdf08a5ca249.png'
},
{
id: 2,
title: '创新七大核心要素更新人体全局系统',
description: '自愈力、免疫力、抵抗力、平衡力、代谢力、情绪力及认知力。',
icon: 'https://images.health.ufutx.com/202507/04/4a47a0db6e60853dedfcfdf08a5ca249.png'
},
{
id: 3,
title: '全方位的AI健康管理系统',
description: '精准营养方案+心理疏导陪伴+饮食方案+关键要素指导。',
icon: 'https://images.health.ufutx.com/202507/04/4a47a0db6e60853dedfcfdf08a5ca249.png'
},
{
id: 4,
title: '双重认证教练团队全程支持',
description: '国家卫健委《营养指导员》+营养师协会《身心健康管理师》双证教练服务指导。',
icon: 'https://images.health.ufutx.com/202507/04/4a47a0db6e60853dedfcfdf08a5ca249.png'
},
{
id: 5,
title: '构建健康产业生态',
description: '整合产业链协同,链接多方资源,打造开放共赢的智慧健康生态系统。',
icon: 'https://images.health.ufutx.com/202507/04/4a47a0db6e60853dedfcfdf08a5ca249.png'
},
{
id: 6,
title: '创新模式-身心双指标评估,生成精准个性化方案',
description:
'整合个人身理+心理健康数据、健康行为、个人及家族疾病史及生活方式等多维度数据,全方面分析精准个性化方案。',
icon: 'https://images.health.ufutx.com/202507/04/4a47a0db6e60853dedfcfdf08a5ca249.png'
}
]
// 0
const isHover = ref<number>(0)
//
@ -203,8 +144,7 @@ const handleMouseLeave = (moduleId: number) => {
.module {
position: absolute;
cursor: pointer;
/* 移除模块本身的偏移,避免影响子元素定位 */
//
.module-content {
display: flex;
align-items: center;
@ -216,31 +156,19 @@ const handleMouseLeave = (moduleId: number) => {
width: 190px;
height: 70px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
transform-origin: center center; /* 基于自身中心变换 */
transition: all 0.8s ease;
// hover
&.hover {
width: 300px;
height: auto;
transform: translateX(-20%); /* 基于中心放大1.1倍,无偏移 */
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1);
}
transform-origin: center center;
transition:
height 0.2s ease,
width 0.3s ease,
transform 1s ease;
// hover0.4s
&.hover-delay {
display: grid;
grid-template-rows: auto 1fr auto; /* 图标区 | 空白区 | 描述区 */
align-items: start;
justify-items: center;
border-radius: 16px;
background: #fff;
padding: 20px; /* 增加内边距,避免内容贴边 */
}
/* 统一过渡时间和缓动函数 */
//transition: all 0.6s cubic-bezier(0.4, 0, 0.2, 1);
//
.icon {
width: 50px;
height: 50px;
flex-shrink: 0; /* 防止图标缩小 */
flex-shrink: 0;
img {
width: 100%;
@ -253,150 +181,121 @@ const handleMouseLeave = (moduleId: number) => {
.text {
width: 130px;
font-size: 18px;
color: @text-color;
color: #333;
font-weight: 600;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
// hover
//
.desc {
width: 90%; /* 限制宽度,避免超出容器 */
width: 90%;
opacity: 1;
visibility: hidden;
font-size: 14px;
color: @text-color-secondary;
color: #666;
line-height: 25px;
text-align: center;
//transition:
//opacity 0.3s ease 0.2s,
//visibility 0.3s ease 0.2s;
&.show {
opacity: 1;
visibility: visible;
}
}
// hover
&.hover {
width: 300px;
height: auto;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1);
transform: translateX(-20%);
.text {
overflow: auto;
text-overflow: ellipsis;
white-space: pre-wrap;
width: 100%;
}
}
//
&.hover-delay {
display: grid;
grid-template-rows: auto 1fr auto;
align-items: start;
justify-items: center;
border-radius: 16px;
background: #fff;
padding: 20px;
}
}
}
//
// ID
.module-1 {
bottom: 482px;
left: 521px;
}
.module-2 {
top: 288px;
right: 521px;
bottom: 482px;
left: 1185px;
}
.module-3 {
top: 530px;
bottom: 240px;
left: 376px;
}
.module-4 {
top: 530px;
right: 376px;
bottom: 240px;
left: 1328px;
}
.module-5 {
top: 687px;
left: 50%;
transform: translateX(-50%); /* 水平居中 */
bottom: 83px;
left: 852px;
}
.module-6 {
bottom: 381px;
left: 50%;
transform: translateX(-50%); /* 水平居中 */
&.hover {
width: 300px;
height: auto;
transform: translateX(-50%) !important; /* 基于中心放大1.1倍,无偏移 */
}
}
//
.center-module {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
text-align: center;
background-color: rgba(255, 255, 255, 0.95);
border-radius: 12px;
padding: 24px;
width: 300px;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
.center-icon {
width: 64px;
height: 64px;
margin: 0 auto 16px;
img {
width: 100%;
height: 100%;
object-fit: contain;
}
}
.center-title {
font-size: 18px;
font-weight: bold;
color: #333;
margin-bottom: 8px;
}
.center-desc {
font-size: 14px;
color: #666;
line-height: 1.6;
}
left: 842px;
}
}
}
}
//
@media (max-width: 768px) {
.core-value {
padding: 60px 0;
.container {
.section-title {
font-size: 28px;
}
.diagram-wrapper {
height: auto;
padding-bottom: 150%; /* 自适应高度 */
.module {
.module-content {
width: 150px;
height: 60px;
.icon {
width: 40px;
height: 40px;
}
.text {
font-size: 16px;
}
&.hover {
width: 250px;
height: 220px;
transform: scale(1.05);
}
}
}
.center-module {
width: 80%;
padding: 16px;
}
}
}
}
}
//@media (max-width: 768px) {
// .core-value {
// padding: 60px 0;
//
// .container {
// .section-title {
// font-size: 28px;
// }
//
// .diagram-wrapper {
// height: auto;
// padding-bottom: 150%;
//
// .module {
// .module-content {
// width: 150px;
// height: 60px;
//
// .icon {
// width: 40px;
// height: 40px;
// }
// .text {
// font-size: 16px;
// }
//
// &.hover {
// width: 250px;
// height: auto;
// transform: scale(1.05);
// }
// }
// }
// }
// }
// }
//}
</style>

View File

@ -216,14 +216,14 @@ console.log('数组1:', feedbackList[0]) // 例如: [3, 7, 2, 5]
}
}
//
@media (max-width: 768px) {
.feedback-section {
padding: 0 20px 60px; /* 缩小移动端内边距 */
}
.feedback-card {
width: 220px; /* 移动端缩小卡片宽度 */
}
}
//@media (max-width: 768px) {
// .feedback-section {
// padding: 0 20px 60px; /* */
// }
// .feedback-card {
// width: 220px; /* */
// }
//}
</style>
<style lang="less">

View File

@ -2,7 +2,7 @@
<section class="global-service">
<div class="container">
<h3 class="section-title">我们全球服务覆盖范围</h3>
<p class="section-desc">覆盖国家/地区65+ · 客户产业覆盖范围18+</p>
<p class="section-desc">覆盖国家/地区65+ · 客户产业覆盖范围13+</p>
<!-- 替换为实际地图路径 -->
<img
src="https://images.health.ufutx.com/202506/12/2acedc9535b108d6cd079b34bb01e78e.jpeg"

View File

@ -41,6 +41,9 @@ const openReport = () => {
.pt(36px);
.pb(36px);
background-image: url('https://images.health.ufutx.com/202506/18/e403f857ad7385ea660987cbcbdcf198.png');
background-size: cover;
background-position: center;
background-repeat: no-repeat;
}
.section-title {
font-weight: bold;
@ -123,4 +126,14 @@ const openReport = () => {
width: 60px;
height: 60px;
}
@media (max-width: @tablet-breakpoint) {
.scene-list {
grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
gap: @space-lg;
}
.scene-item {
height: 280px;
}
}
</style>

View File

@ -161,20 +161,14 @@ onMounted(() => {
}
}
@media (max-width: @tablet-breakpoint) {
.scene-list {
grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
gap: @space-lg;
}
.scene-item {
height: 280px;
}
}
@media (max-width: @mobile-breakpoint) {
.scene-list {
grid-template-columns: 1fr;
}
}
//@media (max-width: @tablet-breakpoint) {
// .scene-list {
// grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
// gap: @space-lg;
// }
// .scene-item {
// height: 280px;
// }
//}
}
</style>

View File

@ -202,7 +202,31 @@ onMounted(() => {
//
@media (max-width: 768px) {
padding: 40px 20px;
.article-body {
font-weight: 400;
line-height: 35px;
p {
font-size: 16px;
color: #666;
line-height: 1.8;
margin-bottom: 24px;
text-indent: 2em;
}
//
img {
max-width: 100%;
height: auto;
margin: 16px 0;
}
//
h2,
h3 {
margin: 24px 0 16px;
color: @text-color;
}
}
.article-header {
.article-title {
font-size: 24px;

View File

@ -271,6 +271,9 @@ onMounted(async () => {
//
@media (max-width: @tablet-breakpoint) {
.categories-label {
font-size: @font-size-lg;
}
:deep(.el-tabs__item) {
margin-right: @space-md;
font-size: @font-size-lg;
@ -409,7 +412,20 @@ onMounted(async () => {
}
.news-info .news-label {
margin-bottom: @space-md;
font-size: @font-size-sm;
}
.news-info .view-btn {
font-size: @font-size-xs;
}
}
.news-date {
.icon {
width: 40px;
height: 40px;
}
p {
font-size: @font-size-xs;
}
}
}

View File

@ -1,22 +1,27 @@
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import path from 'path' // 引入 path 模块
import path from 'path'
import viteImagemin from 'vite-plugin-imagemin' // 新插件
import viteImagemin from 'vite-plugin-imagemin'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
// 引入 ElementPlus 相关解析器
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
// import ElementPlus from 'unplugin-element-plus/vite'
import { visualizer } from 'rollup-plugin-visualizer'
import legacy from '@vitejs/plugin-legacy'
export default defineConfig({
plugins: [
// ElementPlus(),
vue(),
export default defineConfig(({ mode }) => ({
// 新增:指定打包基础路径为 /web/
// base: '/web/', // 所有资源引用会以 /web/ 为前缀(如静态资源路径变为 /web/static/js/xxx.js
base: mode === 'production' ? '/web/' : '/',
// 仅在生产环境启用 legacy 插件
plugins: [
visualizer({
filename: './node_modules/.cache/visualizer/stats.html',
open: true,
gzipSize: true,
brotliSize: true
}),
vue(),
...(process.env.NODE_ENV === 'production'
? [
legacy({
@ -27,38 +32,25 @@ export default defineConfig({
]
: []),
AutoImport({
imports: ['vue', 'vue-router'], // 自动导入 Vue、Vue Router API
// 自动导入 Vue 相关函数,以及 ElementPlus 的相关函数等
imports: ['vue', 'vue-router'],
resolvers: [
ElementPlusResolver({
importStyle: false, // 关闭自动导入样式(手动引入 index.css
directives: true // 导入 Element Plus 指令(如 v-loading
importStyle: false,
directives: true
})
// ElementPlusResolver({
// importStyle: 'sass',
// directives: true // 导入指令
// })
],
dts: true // 生成 auto-imports.d.ts让 ESLint 识别自动导入的 API
dts: true
}),
Components({
resolvers: [
ElementPlusResolver({
importStyle: false // 同上,避免与手动引入的样式冲突
importStyle: false
})
], // 自动解析 Element Plus 组件
dts: true, // 生成 components.d.ts关键让 ESLint 识别组件)
include: [/\.vue$/, /\.vue\?vue/, /\.tsx$/], // 确保覆盖所有组件文件
// 自动导入 ElementPlus 的组件
// resolvers: [
// ElementPlusResolver({
// importStyle: 'sass',
// directives: true
// })
// ],
dirs: ['src/components'] // 自动扫描组件目录
],
dts: true,
include: [/\.vue$/, /\.vue\?vue/, /\.tsx$/],
dirs: ['src/components']
}),
// 开发环境暂时禁用图片压缩插件
...(process.env.NODE_ENV !== 'development'
? [
viteImagemin({
@ -86,46 +78,45 @@ export default defineConfig({
rewrite: path => path.replace(/^\/api/, '')
}
}
// proxy: {
// '^/dev-api': {
// target: ''
// }
// }
},
optimizeDeps: {
// include: ['element-plus'] // 预构建依赖
// include: ['element-plus']
},
build: {
chunkSizeWarningLimit: 1000, // 增大分块警告阈值
chunkSizeWarningLimit: 1000,
rollupOptions: {
// external: ['element-plus', 'element-plus/dist/index.css'], // 排除 Element Plus
// external: ['element-plus'], // 排除 Element Plus 从打包中
output: {
// manualChunks: undefined // 取消手动分割,使用 Vite 自动策略
manualChunks(id) {
if (id.includes('node_modules')) {
// 将第三方库单独分包(如 axios、vue 等)
return id.split('node_modules/')[1].split('/')[0]
}
}
// chunkFileNames: 'static/js/[name]-[hash].js',
// entryFileNames: 'static/js/[name]-[hash].js',
// assetFileNames: 'static/[ext]/[name]-[hash].[ext]'
}
}
},
css: {
preprocessorOptions: {
scss: {
// 关键配置:强制 Sass 使用现代 API二选一推荐 modern-compiler
api: 'modern-compiler'
// additionalData: `@import "@/styles/element-variables.scss";`
// api: 'modern' // 轻量版,适合简单场景
},
less: {
// 关键配置:自动注入 global.less
additionalData: `@import "@/styles/global.less";`,
// 可选:如果需要自定义 Less 选项
javascriptEnabled: true
}
}
},
resolve: {
alias: {
'@': path.resolve(__dirname, 'src') // 添加路径别名
'@': path.resolve(__dirname, 'src')
}
}
})
}))