Compare commits

...

10 Commits

Author SHA1 Message Date
91bb3cd5d8 feat: 20260330 2026-03-30 11:34:46 +08:00
f4c896fefd feat: 20250928 2025-09-28 16:09:59 +08:00
7ee5633d14 feat: 20250926 2025-09-26 15:26:14 +08:00
4e5d6980eb feat: 20250925 2025-09-25 17:29:16 +08:00
05ed7dad8a feat: 20250925 2025-09-25 17:00:48 +08:00
0582339969 feat: 20250922 2025-09-22 13:57:20 +08:00
6853a60112 feat: 20250919 2025-09-19 19:01:15 +08:00
ddd8f091e0 feat: 20250730 2025-07-30 17:36:16 +08:00
e11e20790d feat: 20250724 2025-07-24 18:26:24 +08:00
c1c60a5313 feat: 20250711 2025-07-11 14:51:12 +08:00
52 changed files with 1744 additions and 748 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

@ -1,4 +1,4 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
npx lint-staged
lint-staged

2
components.d.ts vendored
View File

@ -9,6 +9,8 @@ export {}
declare module 'vue' {
export interface GlobalComponents {
ElButton: typeof import('element-plus/es')['ElButton']
ElCarousel: typeof import('element-plus/es')['ElCarousel']
ElCarouselItem: typeof import('element-plus/es')['ElCarouselItem']
ElDialog: typeof import('element-plus/es')['ElDialog']
ElForm: typeof import('element-plus/es')['ElForm']
ElFormItem: typeof import('element-plus/es')['ElFormItem']

View File

@ -6,6 +6,31 @@
<!-- <link href="https://cdn.jsdelivr.net/npm/element-plus@2.4.10/dist/index.min.css" rel="stylesheet">-->
<!-- <link href="https://cdn.jsdelivr.net/npm/element-plus@2.4.10/dist/index.min.css" rel="stylesheet">-->
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<!-- 基础信息:标题(分享卡片标题) -->
<meta property="og:title" content="这是分享的标题" />
<!-- 描述(分享卡片副标题) -->
<meta property="og:description" content="这是分享的详细描述,会显示在标题下方" />
<!-- 分享图片(卡片主图) -->
<meta property="og:image" content="https://images.health.ufutx.com/202507/14/cc651222ac2e5f63185dec1f31d176ae.png" />
<!-- 分享链接(点击卡片跳转的 URL默认是当前页面 URL -->
<meta property="og:url" content="https://www.ufutx.com/web/#/" />
<!-- 类型(固定为 website -->
<meta property="og:type" content="website" />
<!-- 微信特定标签(增强兼容性) -->
<meta name="description" content="这是分享的详细描述(微信 fallback 用)" />
<meta name="thumbnail" content="https://images.health.ufutx.com/202507/14/be87f139c2c1a98f259db1fb3c82de6b.png" /> <!-- 微信缩略图 -->
<!-- index.html 的 <head> 中添加 -->
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no">
<meta name="format-detection" content="telephone=no"> <!-- 避免手机号被默认识别为链接 -->
<meta name="apple-mobile-web-app-capable" content="yes"> <!-- 适配 iOS 微信 -->
<!-- 修复后的viewport适配安卓X5内核 -->
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no, target-densitydpi=device-dpi">
<title>友福同享官网</title>
</head>
<body>
@ -14,5 +39,6 @@
<!--<script src="https://cdn.jsdelivr.net/npm/element-plus@2.4.10/dist/index.full.min.js"></script>-->
<script type="module" src="/src/main.ts"></script>
</body>
</html>

View File

@ -30,11 +30,11 @@
"@vueuse/core": "^13.4.0",
"@vueuse/motion": "^3.0.3",
"axios": "^1.9.0",
"core-js": "^3.45.1",
"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",
"regenerator-runtime": "^0.14.1",
"swiper": "^11.2.10",
"vue": "^3.5.13",
"vue-i18n": "^9.8.0",
@ -65,6 +65,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",
@ -74,7 +75,6 @@
"unplugin-vue-components": "^28.7.0",
"vite": "^5.4.19",
"vite-plugin-html": "^3.2.2",
"vite-plugin-imagemin": "^0.6.1",
"vite-ssg": "^27.0.1",
"vue-tsc": "1.8.14"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

After

Width:  |  Height:  |  Size: 11 KiB

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

@ -3,7 +3,8 @@
<div class="navbar-container">
<!-- Logo -->
<div class="navbar-logo">
<img src="@/assets/images/logo.png" alt="友福同享Logo" />
<img src="@/assets/images/logo.png" alt="友福同享Logo" class="navbar-logo-icon" />
<img src="@/assets/images/logo-mini.png" alt="友福同享Logo" class="navbar-logo-mini" />
<!-- <span>友福同享</span>-->
</div>
@ -23,7 +24,7 @@
<!-- 语言切换 -->
<!-- <div class="language-switch"><span>中文</span></div>-->
<div class="language-switch" @click="changeLanguage">
<span>English</span> | <span>中文</span> | <span>繁體</span>
<!-- <span>English</span> | <span>中文</span> | <span>繁體</span>-->
</div>
</div>
</header>
@ -92,6 +93,15 @@ const isActive = (path: string) => {
//left: 0;
}
.navbar-logo-mini {
display: none;
//width: 72px;
//height: 72px;
//position: absolute;
//top: 0;
//left: 0;
}
//
.navbar-container {
width: 100%;
@ -157,9 +167,22 @@ const isActive = (path: string) => {
}
//
@media (max-width: 1200px) {
//@media (max-width: @tablet-breakpoint) {
@media (max-width: 768px) {
.navbar-container {
width: 90%;
height: 56px;
.navbar-logo-icon {
display: none;
}
.navbar-logo-mini {
display: inline-block;
}
.navbar-logo {
width: 36px;
height: 36px;
}
}
}
</style>

122
src/data/caseImage.ts Normal file
View File

@ -0,0 +1,122 @@
export interface RealCase {
username: string
case: string // 案例描述
avatar: string // 用户头像
result: string // 案例结果
caseImage: string // 新增:案例相关图片(如场景图、产品使用图等)
}
export const newRealCasesWithImage: RealCase[] = [
{
username: '林婉清',
case: '高中班主任用「课堂专注度」功能分析学生听课数据AI识别走神高频时段并推送互动课件。商城买的护嗓麦克风收音清晰课后用「作业批改助手」自动统计错题率节省2小时工作时间。',
avatar: 'https://picsum.photos/id/601/200/200',
caseImage: 'https://images.health.ufutx.com/202509/18/f3ccdd27d2000e3f9255a7e3e2c48800.jpeg', // 课堂场景图(学生听课+课件屏幕)
result: '学生课堂走神率从35%降至12%班级月考平均分提升15分个人嗓子嘶哑频率减少80%。'
},
{
username: '陈浩宇',
case: '糖尿病患者用「血糖波动曲线」记录餐前餐后数据App智能提醒胰岛素注射时间。商城买的血糖仪免采血且误差0.5mmol/L同步推送低糖食谱和「餐后散步20分钟」计划。',
avatar: 'https://picsum.photos/id/602/200/200',
caseImage: 'https://images.health.ufutx.com/202509/18/156005c5baf40ff51a327f1c34f2975b.jpeg', // 血糖仪+低糖食谱场景图
result: '空腹血糖从8.5mmol/L降至6.2mmol/L低血糖发作次数从每月4次降至0次糖化血红蛋白从7.8%降至6.5%。'
},
{
username: '苏雨薇',
case: '留学生在海外用「翻译问诊」功能与当地医生沟通AI实时翻译症状描述。商城买的便携体温计支持多语言播报感冒时通过「海外药房导航」找到附近中文服务药店。',
avatar: 'https://picsum.photos/id/603/200/200',
caseImage: 'https://images.health.ufutx.com/202509/18/799bad5a3b514f096e69bbc4a7896cd9.jpeg', // 海外诊所+翻译App界面图
result: '就医沟通效率提升90%感冒康复周期缩短2天成功适配3种语言的医疗服务场景。'
},
{
username: '赵健峰',
case: '健身教练用「体成分分析」功能为学员定制增肌计划AI识别肌肉量薄弱部位。商城买的弹力带防滑且承重50kg通过「班级打卡」功能监督学员训练同步推送高蛋白食谱。',
avatar: 'https://picsum.photos/id/604/200/200',
caseImage: 'https://images.health.ufutx.com/202509/18/032b2cc936860b03048302d991c3498f.jpeg', // 健身房训练+弹力带使用图
result: '学员平均肌肉量增加3.2kg体脂率下降5.8%课程续费率从65%提升至92%。'
},
{
username: '吴欣怡',
case: '花店店主用「花粉过敏预警」功能调整进货品类避开高致敏花卉。商城买的保鲜喷雾延长玫瑰花期5天通过「会员健康标签」为过敏顾客推荐低敏花束减少退货率。',
avatar: 'https://picsum.photos/id/605/200/200',
caseImage: 'https://images.health.ufutx.com/202509/18/d0096ec6c83575373e3a21d129ff8fef.jpeg', // 花店陈列+低敏花束图
result: '顾客过敏投诉从每月8起降至1起花卉损耗率减少30%会员复购率提升45%。'
},
{
username: '马子轩',
case: '程序员用「久坐提醒」功能每50分钟弹窗拉伸提示App分析代码时长与颈椎压力关联。商城买的人体工学椅腰托可调节配合「眼疲劳监测」每2小时强制远眺同步记录睡眠质量。',
avatar: 'https://picsum.photos/id/606/200/200',
caseImage: 'https://images.health.ufutx.com/202509/18/28c03d3961c2e936cc6234f52d82e965.jpeg', // 程序员工位+工学椅+拉伸提醒图
result: '颈椎疼痛频率从每周5次降至1次视力从4.7恢复至4.9深度睡眠时间延长1.5小时。'
},
{
username: '郑思琪',
case: '哺乳期妈妈用「奶量预测」功能根据宝宝月龄调整吸奶时间AI识别供需失衡风险。商城买的吸奶器静音且吸力可调通过「辅食营养库」匹配宝宝过敏体质推送低敏食谱。',
avatar: 'https://picsum.photos/id/607/200/200',
caseImage: 'https://images.health.ufutx.com/202509/18/af9b82a1b9683d5734db895886c002c0.jpeg', // 母婴场景+吸奶器+辅食图
result: '奶量供需平衡率从60%提升至95%宝宝湿疹发作次数减少70%夜间连续睡眠时长从3小时增至6小时。'
},
{
username: '王浩明',
case: '退休老人用「语音记事」功能记录血压、用药时间App自动生成周健康报告。商城买的智能水杯提醒每日饮水1500ml通过「子女共享」功能让子女实时查看健康数据减少担忧。',
avatar: 'https://picsum.photos/id/608/200/200',
caseImage: 'https://images.health.ufutx.com/202509/18/59aa235ebd97e78a23210c5a618e3633.jpeg', // 老人居家+智能水杯+血压仪图
result: '每日饮水量达标率从40%提升至90%漏服药次数从每月6次降至0次子女远程关怀响应时间缩短至5分钟内。'
},
{
username: '李雨桐',
case: '舞蹈老师用「动作矫正」功能通过手机摄像头识别学员肢体角度AI标注错误动作。商城买的舞蹈把杆稳固且可折叠通过「课程回放」功能让学员课后复习重点标注纠错片段。',
avatar: 'https://picsum.photos/id/609/200/200',
caseImage: 'https://images.health.ufutx.com/202509/18/b89c4cc90e26a826ef04a7adfea8c40d.jpeg', // 舞蹈教室+把杆+手机矫正界面图
result: '学员动作标准率从55%提升至88%课程学习效率提升40%家长满意度评分从3.2分升至4.8分。'
},
{
username: '张博文',
case: '货车司机用「疲劳驾驶监测」通过方向盘震动提醒连续驾驶4小时强制休息。商城买的车载腰靠贴合腰椎曲线离线地图提前预警施工路段同步记录油耗与胎压数据。',
avatar: 'https://picsum.photos/id/610/200/200',
caseImage: 'https://images.health.ufutx.com/202509/18/734a4c7ff726c7e7aeadf15773e060ff.jpeg', // 货车驾驶舱+腰靠+导航界面图
result: '疲劳驾驶风险事件从每月3次降至0次油耗降低12%轮胎异常磨损率减少60%。'
},
{
username: '刘雅菲',
case: '鼻炎患者用「花粉浓度地图」避开高风险区域App推送每日鼻腔冲洗提醒。商城买的洗鼻器喷头柔软且水流可调通过「过敏原记录」识别尘螨过敏推荐防螨床品。',
avatar: 'https://picsum.photos/id/611/200/200',
caseImage: 'https://images.health.ufutx.com/202509/18/280a6001ab93c8eea794f0fdf899c4ad.jpeg', // 洗鼻器+花粉地图+防螨床品图
result: '鼻炎发作频率从每周4次降至1次打喷嚏次数从每日20次减至5次睡眠质量评分从60分升至85分。'
},
{
username: '陈宇轩',
case: '咖啡店店主用「员工健康打卡」功能记录体温、核酸状态,到期自动提醒体检。商城买的手部消毒机感应灵敏,通过「客流健康分析」调整高峰时段员工排班,减少交叉感染风险。',
avatar: 'https://picsum.photos/id/612/200/200',
caseImage: 'https://images.health.ufutx.com/202509/18/c7b9fe05ece7c3068309677eb788d520.jpeg', // 咖啡店吧台+消毒机+打卡界面图
result: '员工健康异常率从8%降至1%顾客投诉率减少50%门店卫生评分从B级升至A级。'
},
{
username: '高思远',
case: '备考公务员用「专注计时」功能屏蔽娱乐软件AI分析学习效率高峰时段。商城买的护眼台灯可调节色温通过「错题本同步」功能整理高频考点推送相似题型练习。',
avatar: 'https://picsum.photos/id/613/200/200',
caseImage: 'https://images.health.ufutx.com/202509/18/45fca35bf2e29ddb2e49305da1c813d6.jpeg', // 学习桌+护眼台灯+错题本App图
result: '日均有效学习时长从5小时增至7.5小时错题重复率从40%降至15%笔试成绩提升28分。'
},
{
username: '周雨薇',
case: '早产儿妈妈用「生长曲线监测」每周记录宝宝体重、身高AI对比标准曲线预警异常。商城买的婴儿体重秤精度达10g通过「母乳成分分析」调整饮食提升乳汁营养密度。',
avatar: 'https://picsum.photos/id/614/200/200',
caseImage: 'https://images.health.ufutx.com/202509/18/15a7bfcf08d4a38fad6d798e02461165.jpeg', // 婴儿体重秤+生长曲线App+母乳检测图
result: '宝宝生长达标率从65%提升至98%纠正月龄后体重追平同龄婴儿医生随访满意度达100%。'
},
{
username: '吴浩明',
case: '工地安全员用「噪音监测」功能记录施工环境分贝超过85dB自动推送防护提醒。商城买的防噪音耳塞降噪率达35dB通过「安全培训视频」定期考核工人同步记录防护装备佩戴情况。',
avatar: 'https://picsum.photos/id/615/200/200',
caseImage: 'https://images.health.ufutx.com/202509/18/e47f1edd5ca04227b115fe4b47786b85.jpeg', // 工地场景+噪音监测仪+耳塞图
result: '工人听力损伤风险降低70%安全考核通过率从75%升至96%工伤事故次数减少80%。'
},
{
username: '郑雅琳',
case: '瑜伽教练用「呼吸节奏监测」功能纠正学员呼吸与动作配合AI识别憋气时段。商城买的瑜伽垫防滑系数达0.8,通过「会员体态报告」生成个性化矫正计划,推送居家练习视频。',
avatar: 'https://picsum.photos/id/616/200/200',
caseImage: 'https://images.health.ufutx.com/202509/18/72eec5023144990dc4b80e31d8a0a6e0.jpeg', // 瑜伽馆+瑜伽垫+呼吸监测App图
result: '学员呼吸配合准确率从50%提升至90%体态不良改善率达85%私教课程续费率提升50%。'
}
]

22
src/lib/device.ts Normal file
View File

@ -0,0 +1,22 @@
/**
*
* @returns boolean
*/
export const isMobile = (): boolean => {
// 1. UA 检测:匹配常见移动设备关键词
const mobileUA = /Android|iPhone|iPad|iPod|BlackBerry|Windows Phone|SymbianOS/i.test(navigator.userAgent)
// 2. 屏幕宽度辅助判断(适配部分平板/PC 小窗口场景)
const mobileWidth = window.innerWidth < 768
// 满足任一条件即视为移动端
return mobileUA || mobileWidth
}
/**
* H5
* @param mobileDomain 'm.xxx.com'
* @returns boolean
*/
export const isInMobilePage = (mobileDomain: string): boolean => {
return window.location.hostname === mobileDomain
}

View File

@ -1,4 +1,7 @@
// src/main.ts
import 'core-js/stable/structured-clone' // 专门为 structuredClone 打补丁
import 'regenerator-runtime/runtime' // 若项目用了 async/await需加这行
import { ViteSSG } from 'vite-ssg'
import { createWebHashHistory } from 'vue-router'
import App from './App.vue'

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

@ -16,7 +16,7 @@
<MissionVision />
<!-- 合作伙伴-->
<Partners />
<!-- <FooterContact />-->
<!-- <FooterContact />-->
</div>
</template>

View File

@ -139,6 +139,9 @@
//
@media (max-width: 768px) {
.banner-bg {
height: auto !important;
}
.banner-content {
.main-title {
font-size: 32px;

View File

@ -275,22 +275,219 @@ watch(activeIndex, () => {
position: relative; /* 作为地图的定位容器 */
z-index: 10; /* 父容器层级,控制整体显示优先级 */
}
</style>
<style scoped lang="less">
.branch-section {
background-color: #fff;
padding: 50px 192px 50px;
border-top: 50px solid #f6f8fe;
/* 响应式适配 */
@media (max-width: 1200px) {
.branch-section {
padding: 60px 40px;
//
@media (max-width: 768px) {
padding: 30px 16px; // 16px
border-top-width: 20px; //
}
.branch-wrapper {
flex-direction: column;
}
.section-title {
font-size: @font-size-xxl;
font-weight: bold;
color: @text-color;
margin-bottom: 10px;
//
@media (max-width: 768px) {
font-size: 24px; // xxl32px+24px
}
}
.section-subtitle {
font-size: 20px;
color: @text-color-secondary;
margin-bottom: 40px;
// +
@media (max-width: 768px) {
font-size: 14px;
margin-bottom: 20px;
}
}
.branch-wrapper {
display: flex;
align-items: flex-start;
justify-content: space-between;
margin-top: 50px;
//
@media (max-width: 768px) {
flex-direction: column; //
align-items: center;
margin-top: 20px; //
}
}
/* 左侧切换区 - 移动端适配 */
.branch-left {
margin-top: 110px;
padding: 16px 16px 16px 0;
text-align: left;
width: 496px;
@media (max-width: 768px) {
width: 100%; //
margin-top: 0; //
padding: 0; //
}
.branch-icon {
width: 56px;
height: 52px;
margin-bottom: 20px;
@media (max-width: 768px) {
width: 40px;
height: 38px;
margin-bottom: 12px;
}
}
}
.branch-title {
font-size: 32px;
font-weight: 600;
color: @text-color;
margin-bottom: 24px;
@media (max-width: 768px) {
font-size: 20px;
margin-bottom: 16px;
}
}
/* Tabs容器 - 移动端关键优化(避免超出屏幕) */
.branch-tabs {
display: flex;
gap: 10px;
margin-bottom: 20px;
background: #f6f8fe;
padding: 6px;
width: 463px; //
border-radius: 100px;
position: relative;
@media (max-width: 768px) {
width: 100%; //
gap: 5px; // tab
padding: 4px; //
margin-bottom: 16px;
}
}
/* 平移滑块 - 适配移动端Tabs尺寸 */
.tab-slider {
position: absolute;
top: 6px;
bottom: 6px;
border-radius: 100px;
color: #fff;
background-color: #165dff;
z-index: 1;
transition: all 0.2s cubic-bezier(0.34, 1.56, 0.64, 1);
@media (max-width: 768px) {
top: 4px; // Tabspadding
bottom: 4px;
}
}
/* Tab按钮 - 移动端缩小尺寸,避免换行 */
.branch-tab {
padding: 12px 30px;
border-radius: 100px;
color: @text-color-secondary;
cursor: pointer;
font-size: 20px;
position: relative;
z-index: 2;
transition: color 0.3s ease;
@media (max-width: 768px) {
padding: 8px 15px; //
font-size: 14px; //
}
&.active,
.branch-tabs:hover &:hover {
color: #fff;
}
}
/* 详情区 - 移动端缩小字体和行高 */
.branch-details {
.detail-item {
font-size: 20px;
color: @text-color-secondary;
line-height: 40px;
margin-bottom: 8px;
@media (max-width: 768px) {
font-size: 14px;
line-height: 28px; //
margin-bottom: 6px;
}
}
.detail-icon {
width: 20px;
height: 18px;
margin-bottom: 2px;
@media (max-width: 768px) {
width: 16px;
height: 14px;
margin-bottom: 1px;
vertical-align: middle; //
}
}
}
/* 右侧地图区 - 移动端关键适配(高度自适应) */
.branch-right {
width: 870px;
height: 850px; //
flex-shrink: 0;
overflow: hidden;
margin-left: 60px;
position: relative;
z-index: 10;
@media (max-width: 768px) {
width: 100%; //
height: 300px; // min-height: 50vh
margin-left: 0; //
margin-top: 20px; //
}
}
/* 平板过渡态768px~1200px- 补充中间尺寸适配 */
@media (min-width: 769px) and (max-width: 1200px) {
.branch-section {
padding: 40px 30px;
}
.branch-left {
margin-bottom: 40px;
width: 350px;
}
.branch-tabs {
width: 320px;
}
.branch-right {
margin: 0;
width: 100%;
width: 600px;
height: 600px;
margin-left: 30px;
}
}
</style>

View File

@ -6,7 +6,7 @@
<p class="card-desc">友福同享<br />AI精准健康的领航者</p>
<p class="card-text">
友福同享深圳智能科技有限公司以人工智能为核心驱动力专注于生命科学与AI技术融合打造主动式精准健康管理平台<br />
公司依托先进的算法模型与多维度数据体系为个人提供精准预测与个性化健康方案业务覆盖全球20+国家和地区同时深度赋能企业政府及医疗健康机构提供标准化场景化的AI健康解决方案构建健康产业新生态
公司依托先进的算法模型与多维度数据体系为个人提供精准预测与个性化健康方案业务覆盖全球13+国家和70+城市同时深度赋能企业政府及医疗健康机构提供标准化场景化的AI健康解决方案构建健康产业新生态
</p>
</div>
<div class="team-info">
@ -95,16 +95,22 @@
@media (max-width: @tablet-breakpoint) {
padding: 40px 20px;
.intro-content {
flex-direction: column;
.intro-card {
order: 2;
.card-text {
line-height: 12px !important;
}
.team-info {
order: 1;
margin-bottom: 40px;
.team-list {
line-height: 14px !important;
}
//flex-direction: column;
//
//.intro-card {
// order: 2;
//}
//
//.team-info {
// order: 1;
// margin-bottom: 40px;
//}
}
}
}

View File

@ -8,6 +8,11 @@
<!-- 时间轴容器控制曲线飞机阶段定位 -->
<section class="timeline-section">
<img
src="https://images.health.ufutx.com/202506/20/26691a92ed7a9b632cd635c08e35fb17.png"
class="timeline-section-bgImage"
alt=""
/>
<div class="timeline-container">
<!-- 发展阶段 -->
<div v-for="(item, index) in timelineData" :key="index" class="timeline-stage" :class="`stage-${index}`">
@ -70,7 +75,7 @@ const timelineData = [
padding: 80px 20px;
border-top: 50px solid #f5f7fe;
text-align: center;
//height: auto;
//
.timeline-header {
margin-bottom: 50px;
@ -89,15 +94,23 @@ const timelineData = [
.timeline-section {
padding: 50px 0px;
margin: 0 192px;
background-image: url('https://images.health.ufutx.com/202506/20/26691a92ed7a9b632cd635c08e35fb17.png');
background-position: top;
height: 955px; //
background-size: contain;
//background-image: url('https://images.health.ufutx.com/202506/20/26691a92ed7a9b632cd635c08e35fb17.png');
//background-position: top;
//background-repeat: no-repeat;
height: auto; //
//background-size: contain;
position: relative;
.timeline-section-bgImage {
position: absolute;
left: 0;
top: 0;
//background: red;
}
//
.timeline-container {
position: relative;
width: 100%;
height: 100%;
height: 944px;
margin: 0 auto;
overflow: hidden;
//
@ -219,21 +232,100 @@ const timelineData = [
}
//
//
@media (max-width: 768px) {
.timeline-container {
height: auto; //
padding: 40px 0;
// 1.
.company-timeline {
padding: 40px 16px;
border-top-width: 20px; //
}
//.timeline-stage {
// position: relative; //
// margin: 40px auto; //
// width: 80%; //
// left: auto;
// right: auto;
// top: auto;
// bottom: auto;
// transform: none; //
//}
// 2. .timeline-section
::v-deep .timeline-section {
margin: 0 16px !important;
padding: 20px 0 !important;
height: auto !important; /* 强制覆盖桌面端的955px固定高度 */
min-height: auto !important; /* 清除可能存在的最小高度限制 */
background-size: 100% auto !important;
background-position: top center !important;
background-repeat: no-repeat !important;
overflow: visible !important; /* 确保内容不被截断 */
}
// 3.
::v-deep .timeline-container {
position: relative !important;
width: 100% !important;
height: auto !important; /* 彻底取消100%继承的固定高度 */
padding: 20px 0 !important;
overflow: visible !important; /* 允许内容撑开高度 */
display: block !important; /* 确保是块级元素,能计算内容高度 */
}
// 4.
::v-deep .timeline-stage {
position: static !important; /* 强制取消绝对定位,必须带!important */
width: 100% !important;
max-width: 300px !important;
margin: 0 auto 60px !important; /* 增加底部间距,确保内容不重叠 */
padding: 10px 0 !important; /* 增加内边距,避免内容紧贴 */
display: grid !important;
justify-items: center !important;
text-align: center !important;
}
// 5. 线
::v-deep .stage-0:after,
::v-deep .stage-1:after,
::v-deep .stage-2:after,
::v-deep .stage-3:after {
display: none !important;
}
// 6.
::v-deep .stage-period {
font-size: 28px !important;
margin-bottom: 8px !important;
}
::v-deep .stage-line {
margin: 0 auto 12px !important; /* 居中显示线条,增加间距 */
}
::v-deep .stage-title {
font-size: 24px !important;
margin: 12px 0 16px !important; /* 增加上下间距,提升高度贡献 */
}
::v-deep .stage-content {
width: 100% !important;
padding: 0 10px !important; /* 增加左右内边距,避免内容过宽 */
li {
font-size: 14px !important;
line-height: 26px !important; /* 增加行高,确保内容高度 */
margin-bottom: 8px !important; /* 增加列表项间距 */
text-align: left !important;
padding-left: 16px !important;
&::before {
content: '·';
position: absolute;
left: 0;
color: #4b89dc;
}
}
}
// 7.
.timeline-header {
margin-bottom: 30px !important;
.main-title {
font-size: 24px !important;
margin-bottom: 10px !important;
}
.sub-title {
font-size: 16px !important;
margin-bottom: 0 !important;
}
}
}
}

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,33 @@ 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);
width: 93vw;
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 +106,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 +118,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 +140,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 +177,116 @@ 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; /* 优化行高,避免拥挤 */
margin-bottom: 15px;
}
.scenes-content {
flex-direction: column; /* 垂直堆叠布局,替代左右分栏 */
margin: 0; /* 取消桌面端192px边距 */
padding: 30px 15px; /* 内边距适配小屏 */
gap: 20px; /* 描述与图片间距 */
.scene-desc {
width: 100%; /* 占满屏幕宽度 */
margin-bottom: 0; /* 取消冗余间距用gap控制 */
.speech-title {
font-size: 16px; /* 缩小标题 */
margin-bottom: 15px;
}
.speech-subtitle {
font-size: 13px; /* 缩小内容字体 */
._text {
margin-bottom: 8px;
line-height: 1.5; /* 适配小屏行高 */
}
}
}
.scene-img {
width: 100%; /* 图片占满宽度 */
max-width: 400px; /* 限制最大宽度,避免大屏拉伸 */
margin: 0 auto; /* 居中显示 */
}
}
}
/* 标签页核心适配(解决溢出问题) */
.news-tabs {
padding-top: 20px;
:deep(.el-tabs__header) {
.el-tabs__nav {
gap: 25px; /* 缩小标签间距,避免溢出 */
overflow-x: auto; /* 允许横向滚动,适配多标签 */
padding: 0 5px; /* 左右留白,避免贴边 */
scrollbar-width: none; /* 隐藏火狐滚动条 */
&::-webkit-scrollbar {
/* 隐藏Chrome滚动条 */
display: none;
}
.el-tabs__item {
font-size: 14px; /* 缩小标签字体 */
white-space: nowrap; /* 防止标签换行 */
padding: 0 5px; /* 标签内边距优化 */
}
}
}
:deep(.el-tabs__active-bar) {
bottom: -8px !important; /* 调整下划线位置,避免错位 */
height: 2px; /* 缩小下划线,适配小屏 */
}
/* 自定义标签文本适配 */
.scene-label {
font-size: 14px; /* 与el-tabs__item字体统一 */
}
}
}
/* 平板过渡态769px~1024px- 避免中间尺寸断裂 */
@media (min-width: 769px) and (max-width: 1024px) {
.app-scenes .scenes-content {
margin: 0 50px;
gap: 30px;
.scene-desc,
.scene-img {
width: 45%; /* 左右分栏但缩小宽度 */
}
}
.news-tabs :deep(.el-tabs__nav) {
gap: 50px; /* 标签间距过渡 */
}
}
</style>

View File

@ -3,7 +3,7 @@
<div class="mv-content">
<div class="mv-text">
<h2 class="mv-title">使命与愿景</h2>
<p class="mv-desc">使命-提升人类生命质量 愿景-成为健康生活方式的引领者</p>
<p class="mv-desc">使命-提升人类生命质量 愿景-健康生活方式的引领者</p>
</div>
<!-- <div class="mv-img">-->
<!-- <img-->

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;
@ -231,6 +265,9 @@ const handleClick = (index: number) => {
}
//
@media (max-width: 768px) {
.qualification {
height: auto;
}
.certificate-list {
flex-direction: column;
gap: 20px;

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 {
@ -244,4 +262,100 @@ const activeTab = ref(scenes.value[0].name)
}
}
}
/* ------------------- 移动端适配max-width: 768px ------------------- */
@media (max-width: 768px) {
/* 页面整体适配 */
.app-scenes {
padding: 0 15px; /* 取消192px边距改为小屏留白 */
.app-title {
font-size: 24px; /* 缩小标题字体 */
line-height: 1.5;
margin: 20px 0;
}
/* 场景内容区:垂直堆叠 */
.scenes-content {
flex-direction: column; /* 横向→垂直 */
padding: 20px 10px;
gap: 20px; /* 缩小间距 */
/* 隐藏移动端不需要的装饰伪元素 */
&:after {
display: none !important;
}
/* 文字描述区:占满宽度 */
.scene-desc {
width: 100%; /* 取消600px固定宽度 */
text-align: left;
padding: 0 5px;
.speech-title {
font-size: 16px; /* 缩小标题 */
margin-bottom: 15px;
.icon {
width: 16px;
height: 16px;
}
}
}
/* 图片区:占满宽度+限制最大尺寸 */
.scene-img {
width: 100%; /* 取消600px固定宽度 */
max-width: 400px; /* 避免大屏手机拉伸 */
margin: 0 auto; /* 居中 */
}
}
}
/* 标签栏:解决溢出+优化交互 */
.news-tabs {
padding: 30px 0 20px;
:deep(.el-tabs__header) {
.el-tabs__nav {
gap: 30px; /* 缩小标签间距,避免溢出 */
overflow-x: auto; /* 允许横向滚动 */
padding: 0 5px; /* 左右留白 */
scrollbar-width: none; /* 隐藏火狐滚动条 */
&::-webkit-scrollbar {
/* 隐藏Chrome滚动条 */
display: none;
}
.el-tabs__item {
font-size: 14px; /* 缩小标签字体 */
white-space: nowrap; /* 防止标签换行 */
padding: 0 5px;
}
}
}
/* 调整下划线位置,避免错位 */
:deep(.el-tabs__active-bar) {
bottom: -8px !important;
height: 2px; /* 缩小下划线 */
}
/* 自定义标签文本适配 */
.scene-label {
font-size: 14px;
}
}
/* 列表样式:优化小屏阅读 */
.speech-content li {
line-height: 28px; /* 略微增加行高,提升可读性 */
padding-left: 15px;
&::before {
font-size: 16px;
}
}
}
</style>

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/13/19ee6e89c397511fef5ba05d9798cc76.png" alt="Banner背景" />
<img src="https://images.health.ufutx.com/202509/26/9986d47439b87108c3cbb01ddf2a853d.jpeg" alt="Banner背景" />
</div>
<div class="news-panel">
<div
@ -139,6 +139,9 @@ const newsList = [
//
@media (max-width: 768px) {
.banner {
height: auto !important;
}
.banner-content {
.main-title {
font-size: 32px;
@ -151,5 +154,8 @@ const newsList = [
padding: 10px 24px;
}
}
.news-panel {
display: none;
}
}
</style>

View File

@ -117,4 +117,18 @@
width: 60px;
height: 60px;
}
/* -------------- 移动端适配768px以下-------------- */
@media (max-width: 768px) {
.app-promotion {
//padding-top: 30px; /* */
.app-download {
}
.app-item {
margin-top: 50px;
height: 52px;
width: 120px;
}
}
}
</style>

View File

@ -253,6 +253,7 @@ const handleSelect = (index: number) => {
.device-info {
.device-desc {
max-width: 100%;
line-height: 12px;
}
}
}
@ -261,14 +262,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: 10px;
}
}
}

View File

@ -128,6 +128,7 @@ const openReport = () => {
.device-info {
.device-desc {
max-width: 100%;
line-height: 12px !important;
}
}
}

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;
@ -100,6 +100,7 @@ const openReport = () => {
.speech-info {
.speech-desc {
max-width: 100%;
line-height: 12px !important;
}
}
}

View File

@ -139,6 +139,9 @@
//
@media (max-width: 768px) {
.banner {
height: auto !important;
}
.banner-content {
.main-title {
font-size: 32px;

View File

@ -58,7 +58,7 @@ const points = [
align-items: center;
gap: 20px;
border-radius: 16px;
background: rgba(255, 255, 255, 0.6);
//background: rgba(255, 255, 255, 0.6);
/* 底部导航 0.8透模糊 */
backdrop-filter: blur(25px);
@ -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

@ -139,6 +139,9 @@
//
@media (max-width: 768px) {
.banner-bg {
height: auto !important;
}
.banner-content {
.main-title {
font-size: 32px;

View File

@ -67,7 +67,7 @@ const navItems = [
title: '共享万亿市场机遇',
desc:
'携手友福同享共同分享AI智能健康产业的巨大红利实现区域市场的快速增长。\n' +
'我们期待与您携手将AI智能健康理念带到更多城市共同提升人类生命质量成为健康生活方式的引领者。',
'我们期待与您携手将AI智能健康理念带到更多城市共同提升人类生命质量健康生活方式的引领者。',
icon: 'https://images.health.ufutx.com/202506/20/d780da8996bc4a771e70300456e4eb5d.png',
illustration: 'https://images.health.ufutx.com/202506/23/152ab9d119dbab87bbfc8bc496cc3e98.png'
}
@ -231,7 +231,12 @@ const currentIdx = ref(0)
//.nav-bar {
// gap: 30px;
//}
.text {
margin-top: 22px !important;
}
.desc {
line-height: 12px !important;
}
.content {
flex-direction: column;
text-align: center;

View File

@ -38,6 +38,7 @@ const openCooperationDialog = () => {
text-align: left;
background-image: url('https://images.health.ufutx.com/202506/20/c60d98038ab065c2e92dc67b938d45e2.png');
background-size: cover;
background-position: top;
.consult-content {
max-width: 1066px;
@ -64,4 +65,12 @@ const openCooperationDialog = () => {
}
}
}
/* 移动端适配768px以下 */
@media (max-width: 768px) {
.consult-btn {
width: 60px !important;
text-align: center;
}
}
</style>

View File

@ -100,11 +100,11 @@ defineExpose({
display: flex;
padding: 16px 50px;
justify-content: center;
border-radius: 10px;
border-radius: 4px;
border: 0.5px solid var(--1060-ff, #1060ff);
color: var(--1060-ff, #1060ff);
text-align: center;
font-size: 18px;
font-size: 16px;
font-weight: 500;
}
@ -112,6 +112,16 @@ defineExpose({
background: var(--1060-ff, #1060ff);
color: #fff;
}
@media (max-width: 768px) {
.confirmButtonClass {
font-size: 16px;
}
.confirmButtonClass {
background: var(--1060-ff, #1060ff);
color: #fff;
}
}
</style>
<style scoped lang="less">
:deep(.el-dialog) {
@ -197,4 +207,96 @@ defineExpose({
color: #fff;
}
}
// 768px
@media (max-width: 768px) {
:deep(.el-dialog) {
border-radius: 20px;
padding: 50px 70px;
.form-container {
padding: 0;
}
.el-form-item {
margin-bottom: 30px;
}
.el-form-item__label {
font-size: 14px;
font-weight: bold;
line-height: 14px;
margin-bottom: 6px;
}
.el-input__wrapper,
.el-textarea__inner {
display: flex;
padding: 18px 16px;
align-items: center;
gap: 10px;
align-self: stretch;
border-radius: 10px;
border: 0.5px solid #b2b3b5;
box-shadow: none;
}
/* 在你的全局样式文件中 */
.el-input__inner::placeholder,
.el-textarea__inner::placeholder {
color: var(--b-2-b-3-b-5, #b2b3b5);
text-align: left;
font-size: 14px;
font-weight: 400;
}
.el-textarea__inner {
min-height: 142px !important;
}
}
:deep(.el-dialog) {
width: 90% !important; /* 弹框宽度占屏幕90% */
padding: 20px 15px; /* 减小内边距 */
.el-form-item {
margin-bottom: 15px; /* 减小表单项间距 */
}
.el-input__wrapper,
.el-textarea__inner {
padding: 12px 14px; /* 减小输入框内边距 */
}
.el-textarea__inner {
min-height: 100px !important; /* 减小文本域高度 */
}
}
.dialog-header {
font-size: 18px; /* 减小标题字体 */
padding-bottom: 25px; /* 减小标题底部间距 */
}
.dialog-footer {
gap: 15px; /* 减小按钮间距 */
.cancel-btn,
.confirm-btn {
width: 45%; /* 按钮宽度平分 */
height: 44px; /* 减小按钮高度 */
padding: 10px 0; /* 垂直居中文字 */
font-size: 14px; /* 减小按钮文字 */
}
}
//
:deep(.el-input),
:deep(.el-textarea),
.dialog-footer > div {
max-width: 100%;
box-sizing: border-box;
}
}
</style>

View File

@ -197,6 +197,9 @@ const resetHighlight = () => {
gap: 20px;
.health-item {
border-radius: 8px !important;
.item-desc {
line-height: 12px !important;
}
}
}
.sector-img-container {

View File

@ -24,7 +24,7 @@ const cooperations = [
{
icon: 'https://images.health.ufutx.com/202506/19/f3c8ac06eca308002736e3e42e026265.png',
title: '丰富的市场验证与成功案例',
desc: '已服务16+国家、70+地区客户,数千例客户成功案例'
desc: '已服务13+国家、70+地区客户,数千例客户成功案例'
},
{
icon: 'https://images.health.ufutx.com/202506/19/f3c8ac06eca308002736e3e42e026265.png',

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
@ -142,9 +142,11 @@ const newsList = [
}
}
}
//
@media (max-width: 768px) {
.banner {
height: auto !important;
}
.banner-content {
.main-title {
font-size: 32px;
@ -157,5 +159,8 @@ const newsList = [
padding: 10px 24px;
}
}
.news-panel {
display: none;
}
}
</style>

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>
@ -80,6 +94,7 @@ const modules = [
icon: 'https://images.health.ufutx.com/202507/04/4a47a0db6e60853dedfcfdf08a5ca249.png'
}
]
// const date = new Date()
// 0
const isHover = ref<number>(0)
@ -158,7 +173,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 +221,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 +265,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">
@ -265,4 +265,6 @@ console.log('数组1:', feedbackList[0]) // 例如: [3, 7, 2, 5]
color: @text-color-secondary;
}
}
//@media (max-width: @tablet-breakpoint) { }
</style>

View File

@ -2,7 +2,8 @@
<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>-->
<p class="section-desc">业务已拓展至 13+ 个国家覆盖全国 70+ 个城市</p>
<!-- 替换为实际地图路径 -->
<img
src="https://images.health.ufutx.com/202506/12/2acedc9535b108d6cd079b34bb01e78e.jpeg"

View File

@ -177,6 +177,9 @@ const selectedIndex = ref(defaultIndex)
}
@media (max-width: @tablet-breakpoint) {
.scene-section {
margin-bottom: 50px;
}
.scene-item {
width: 100%;
max-width: 280px;

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,24 @@ 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;
}
.app-promotion {
//padding-top: 30px; /* */
.app-download {
}
.app-item {
margin-top: 50px;
height: 52px;
width: 120px;
}
}
}
</style>

View File

@ -178,6 +178,9 @@ const newsList = [
}
//
@media (max-width: 768px) {
.banner {
height: auto !important;
}
.banner-content {
.main-title {
font-size: 32px;
@ -190,5 +193,8 @@ const newsList = [
padding: 10px 24px;
}
}
.news-panel {
display: none;
}
}
</style>

View File

@ -1,117 +1,106 @@
<template>
<section class="feedback-section">
<div class="feedback-title">真实客户案例</div>
<div class="feedback-desc">友福智能健康-用AI重塑人类健康未来</div>
<div class="feedback-list">
<Marquee
:duration="360"
:reverse="false"
repeatCount="3"
pause-on-hover
container-class="h-56 bg-white rounded-lg shadow-sm p-4"
:initial-offset="0"
<el-carousel
ref="carouselRef"
trigger="click"
height="460px"
arrow="always"
interval="3800"
@change="carouselChange"
>
<!-- 内容卡片直接作为Marquee的子元素不嵌套额外div -->
<div v-for="(item, index) in feedbackList[0]" :key="`${item.username}-${index}`" class="feedback-card">
<el-popover placement="right-end" :width="320" trigger="hover" popper-class="custom-popover">
<template #default>
<div class="_userinfo">
<div class="_userPic"><img :src="item.avatar" alt="avatar" class="avatar" /></div>
<div class="_username">{{ item.username }}</div>
<el-carousel-item v-for="(itemList, index) in feedbackList" :key="index" :name="index">
<div v-for="(item, idx) in itemList" :key="`${item.username}-${idx}`" class="feedback-card">
<div class="popover-container">
<div class="avatar-container">
<el-image
:ref="(el: any) => (imageRefs[idx] = el)"
:src="item.caseImage"
alt="certificate"
class="certificate-image"
:zoom-rate="1"
scale="0.8"
:hide-on-click-modal="true"
:preview-src-list="[item.caseImage]"
show-progress
preview-teleported
fit="contain"
:initial-index="idx"
/>
</div>
<div class="_comment">{{ item.case }}</div>
</template>
<template #reference>
<div class="popover-container">
<div class="avatar-container">
<img :src="item.avatar" alt="avatar" class="avatar" />
</div>
<div class="feedback-info">
<!-- <p class="username">{{ item.username }}</p>-->
<p class="comment">{{ item.result }}</p>
</div>
</div>
</template>
</el-popover>
</div>
</Marquee>
</div>
</div>
</el-carousel-item>
</el-carousel>
</div>
<div class="feedback-list" style="margin-top: 40px">
<Marquee
:duration="360"
:reverse="false"
repeatCount="3"
pause-on-hover
container-class="h-56 bg-white rounded-lg shadow-sm p-4"
:initial-offset="-300"
>
<!-- 内容卡片直接作为Marquee的子元素不嵌套额外div -->
<div v-for="(item, index) in feedbackList[1]" :key="`${item.username}-${index}`" class="feedback-card">
<el-popover placement="right-end" :width="320" trigger="hover" popper-class="custom-popover">
<template #default>
<div class="_userinfo">
<div class="_userPic"><img :src="item.avatar" alt="avatar" class="avatar" /></div>
<div class="_username">{{ item.username }}</div>
</div>
<div class="_comment">{{ item.case }}</div>
</template>
<template #reference>
<div class="popover-container">
<div class="avatar-container">
<img :src="item.avatar" alt="avatar" class="avatar" />
</div>
<div class="feedback-info">
<!-- <p class="username">{{ item.username }}</p>-->
<p class="comment">{{ item.result }}</p>
</div>
</div>
</template>
</el-popover>
</div>
</Marquee>
<div class="el-timeline__dot">
<div
v-for="(_, index) in feedbackList"
:key="index"
class="el-item__dot"
:class="{ 'el-item__dot--active': index === currentIndex }"
@click="handleSwitch(index)"
></div>
</div>
</section>
</template>
<script setup lang="ts">
// 使
// import TextGenerateEffect from '@/components/TextGenerateEffect.vue'
import Marquee from '@/components/Marquee.vue'
import { realCases } from '@/data/realCases.ts'
import { ref, reactive } from 'vue'
import type { CarouselInstance, ImageInstance } from 'element-plus'
import { newRealCasesWithImage } from '@/data/caseImage.ts'
// T
function splitArrayRandomly<T>(arr: T[], ratio = 0.5): [T[], T[]] {
//
//
type CaseItem = {
username: string
case: string
avatar: string
result: string
caseImage: string
}
//
const imageRefs = ref<(ImageInstance | null)[]>([])
// Carousel
const carouselRef = ref<CarouselInstance | null>(null)
//
const currentIndex = ref(0)
//
const handleSwitch = (target: number) => {
if (carouselRef.value) {
carouselRef.value.setActiveItem(target)
}
}
//
const carouselChange = (newIndex: number) => {
currentIndex.value = newIndex
}
// 3
function splitIntoGroupsOfThree<T>(arr: T[]): T[][] {
const shuffled = [...arr]
// Fisher-Yates
// Fisher-Yates
for (let i = shuffled.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1))
;[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]]
}
// 50%
const splitIndex = Math.round(shuffled.length * ratio)
//
return [shuffled.slice(0, splitIndex), shuffled.slice(splitIndex)]
// 3
const groups: T[][] = []
for (let i = 0; i < shuffled.length; i += 3) {
groups.push(shuffled.slice(i, i + 3))
}
return groups
}
const feedbackList = reactive(splitArrayRandomly(realCases))
console.log('数组1:', feedbackList[0]) // : [3, 7, 2, 5]
//
const feedbackList = reactive<CaseItem[][]>(splitIntoGroupsOfThree(newRealCasesWithImage))
</script>
<style scoped lang="less">
//
@bg-color: #f9fbff;
@card-bg: #ffffff;
@radius: 12px;
@padding: 16px;
@gap: 50px;
@avatar-size: 60px;
@subtext-color: #999;
@transition: all 0.3s ease;
//
@bg-color: #f9fbff;
@card-bg: #ffffff;
@ -120,14 +109,13 @@ console.log('数组1:', feedbackList[0]) // 例如: [3, 7, 2, 5]
@avatar-size: 60px;
@subtext-color: #999;
@transition: all 0.3s ease;
.feedback-section {
background-color: @bg-color;
padding: 0 0 100px 0; /* 改用标准padding写法避免自定义.px()可能的问题 */
padding: 0 0 100px 0;
text-align: center;
//background: bisque;
position: relative;
&&::before {
&::before {
content: '';
position: absolute;
top: 0;
@ -135,76 +123,87 @@ console.log('数组1:', feedbackList[0]) // 例如: [3, 7, 2, 5]
width: 60px;
z-index: 1;
pointer-events: none;
}
&&::before {
left: 100px;
background: linear-gradient(to right, #f9fbff, transparent);
}
.feedback-title {
font-size: 28px;
font-weight: 600;
padding: 100px 0 60px;
}
//
.feedback-title {
padding: 50px 0 0;
font-size: 32px;
font-weight: bold;
margin-bottom: 20px;
color: @text-color;
}
.feedback-desc {
font-size: 20px;
color: @text-color-secondary;
}
.el-timeline__dot {
width: auto;
display: inline-flex;
height: 36px;
padding: 8px 20px;
align-items: center;
gap: 20px;
border-radius: 100px;
background: #f5f7fe;
margin-top: 20px;
.el-item__dot {
width: 10px;
height: 10px;
border-radius: 100px;
opacity: 0.2;
background: var(--313-fa-8, #313fa8);
}
.el-item__dot--active {
width: 20px;
border-radius: 100px;
opacity: 1;
background: var(--313-fa-8, #313fa8);
}
}
.feedback-list {
//width: 100vw;
max-width: 1820px;
margin: 0 auto;
margin-left: 100px;
box-sizing: border-box;
//
padding: 10px;
display: flex;
flex-direction: row; /* 明确水平方向 */
flex-wrap: nowrap; /* 禁止换行 */
overflow: hidden; /* 隐藏超出容器的部分 */
flex-direction: row;
}
//
.feedback-card {
width: 362px; /* 固定宽度,确保能在一行放下多个 */
//max-width: 320px;
flex-shrink: 0; /* 禁止收缩,保证宽度不变 */
border-radius: 100px;
background: #f3f5f7;
//transition: @transition;
//box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05); /* */
margin-right: 60px;
cursor: pointer; /* 提示可交互 */
transition: all 0.4s cubic-bezier(0.34, 1.56, 0.64, 1); /* 卡片整体过渡 */
margin-top: 50px;
display: inline-block;
&:hover {
transform: translateY(-4px);
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
}
.popover-container {
padding: @padding;
display: flex; /* 卡片内部水平排列头像和文字 */
flex-direction: row; /* 明确水平方向 */
align-items: center;
width: 492px;
height: 352px;
display: inline-block;
margin-right: 30px;
}
.avatar-container {
flex-shrink: 0;
margin-right: 20px;
.avatar {
width: @avatar-size;
height: @avatar-size;
border-radius: 50%;
.avatar-container {
.certificate-image {
width: 100%;
height: 100%;
object-fit: cover;
border: 2px solid #f0f0f0;
}
}
.feedback-info {
text-align: left;
max-width: calc(100% - @avatar-size - 12px); /* 限制文本区域宽度 */
max-width: calc(100% - @avatar-size - 12px);
.username {
font-size: 16px;
font-weight: 500;
margin-bottom: 6px;
white-space: nowrap; /* 用户名不换行 */
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
@ -214,18 +213,20 @@ console.log('数组1:', feedbackList[0]) // 例如: [3, 7, 2, 5]
color: @text-color;
line-height: 1.5;
display: -webkit-box;
-webkit-line-clamp: 2; /* 固定2行文本 */
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
/* 过渡动画重点控制max-height */
max-height: 42px; /* 14px*1.5*2=42px刚好2行高度 */
max-height: 42px;
transition: max-height 0.6s cubic-bezier(0.34, 1.56, 0.64, 1);
}
}
}
}
//
:deep(.el-image-viewer__canvas) {
height: 80vh;
}
@media (max-width: 768px) {
.feedback-card {
width: 100%;
@ -236,22 +237,17 @@ console.log('数组1:', feedbackList[0]) // 例如: [3, 7, 2, 5]
<style lang="less">
.custom-popover {
/* 修改圆角(核心) */
border-radius: 18px !important; /* 根据需求调整数值如8px、16px */
/* 可选:修改边框 */
border-radius: 18px !important;
border: 1px solid #e5e7eb !important;
/* 可选:修改阴影 */
box-shadow: 0 4px 16px rgba(79, 79, 79, 0.08) !important;
/* 可选:修改内部边距 */
padding: 22px !important;
._userinfo {
.flex();
display: flex;
align-items: center;
.mb(12px);
margin-bottom: 12px;
}
._userPic {
width: 62px;
height: 62px;
@ -259,16 +255,33 @@ console.log('数组1:', feedbackList[0]) // 例如: [3, 7, 2, 5]
overflow: hidden;
border: 4px solid #f3f5f7;
box-shadow: 0 0 16px rgba(79, 79, 79, 0.08);
.mr(10px);
margin-right: 10px;
}
._username {
font-size: 20px;
font-weight: 600;
color: @text-color;
}
._comment {
font-size: 16px;
color: @text-color-secondary;
}
}
.el-carousel {
width: 100%;
}
.demonstration {
color: var(--el-text-color-secondary);
}
.el-carousel__item h3 {
color: #475669;
opacity: 0.75;
margin: 0;
text-align: center;
}
</style>

View File

@ -160,20 +160,61 @@ onMounted(() => {
transform: translateY(-5px); //
}
}
@media (max-width: @tablet-breakpoint) {
.scene-list {
grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
gap: @space-lg;
}
.scene-item {
height: 280px;
}
}
/* ------------------- 移动端适配max-width: 768px ------------------- */
@media (max-width: 768px) {
.scene-section {
padding-top: 40px; //
height: auto; //
padding-bottom: 40px; //
overflow: visible; //
}
@media (max-width: @mobile-breakpoint) {
.scene-list {
grid-template-columns: 1fr;
//
.scene-list {
grid-template-columns: 1fr; // item
gap: 30px; //
padding: 0 15px; //
max-width: 350px; //
}
//
.scene-item {
//
&:active {
transform: scale(0.98);
}
.scene-inner {
height: 120px;
padding: 15px 0 20px; //
//
.scene-icon {
width: 100%;
max-width: 200px; //
height: auto; //
}
//
.scene-name {
line-height: 12px !important;
}
//
.scene-desc {
line-height: 12px !important;
}
}
//
&.active .scene-inner {
transform: translateY(-20px); // -42px -20px
}
//
&.active .scene-desc {
max-height: 80px; //
}
}
}

View File

@ -30,7 +30,6 @@
</div>
<!-- 正文内容富文本渲染 -->
<!-- eslint-disable-next-line vue/no-v-html -->
<div class="article-body" v-html="article.content"></div>
</div>
@ -59,7 +58,33 @@ const article = ref({
pic: '', // URL
content: '' // HTML
})
const TinyMceFormatFn = (data: any) => {
if (!data) return ''
// 1. img width/height style
//
data = data.replace(/<img([^>]*)>/gi, (_: any, attrs: any) => {
// _ match
// width height
attrs = attrs.replace(/\s*width\s*=\s*["'][^"']*["']/gi, '')
attrs = attrs.replace(/\s*height\s*=\s*["'][^"']*["']/gi, '')
// align
attrs = attrs.replace(/\s*align\s*=\s*["'](left|right|center)["']/gi, '')
// style
const newStyle = 'max-width:100%;height:auto;display: block;margin:16px 0;'
if (attrs.match(/\s*style\s*=\s*["']/i)) {
// _ m使
attrs = attrs.replace(/\s*style\s*=\s*["']([^"']*)["']/i, (_: any, existingStyle: any) => {
return ` style="${existingStyle}${newStyle}"`
})
} else {
attrs = ` style="${newStyle}"${attrs}`
}
return `<img${attrs}>`
})
// 2. video
data = data.replace(/<video([^>]*)>/gi, '<video style="max-width:100%;height:auto;display: block;"$1>')
return data
}
//
const fetchArticleDetail = async () => {
loading.value = true
@ -73,6 +98,7 @@ const fetchArticleDetail = async () => {
const data = res.data
//
article.value = data
data.content = TinyMceFormatFn(data.content)
} else {
throw new Error(res.message || '获取文章详情失败')
}
@ -174,27 +200,30 @@ onMounted(() => {
//
.article-body {
font-weight: 400;
line-height: 35px;
p {
//font-weight: 400;
//line-height: 35px;
:deep(p) {
font-size: 16px;
color: #666;
line-height: 1.8;
line-height: 1.2;
margin-bottom: 24px;
text-indent: 2em;
:deep img {
margin: auto;
}
}
//
img {
:deep(img) {
max-width: 100%;
height: auto;
margin: 16px 0;
display: block !important;
}
//
h2,
h3 {
margin: 24px 0 16px;
//margin: 24px 0 16px;
color: @text-color;
}
}
@ -202,7 +231,32 @@ onMounted(() => {
//
@media (max-width: 768px) {
padding: 40px 20px;
.article-body {
:deep(p) {
font-size: 16px;
color: #666;
line-height: 1.8;
margin-bottom: 24px;
text-indent: 2em;
:deep img {
margin: auto;
}
}
//
:deep img {
max-width: 100%; //
height: auto;
display: block !important;
}
//
h2,
h3 {
margin: 24px 0 16px;
color: @text-color;
}
}
.article-header {
.article-title {
font-size: 24px;

View File

@ -112,7 +112,7 @@ const fetchCategories = async () => {
activeTab.value = categories.value[0].name
}
} else {
ElMessage.error(res.message || '获取分类失败')
// ElMessage.error(res.message || '')
}
} catch (error: any) {
console.error('获取分类列表失败', error)
@ -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;
@ -311,6 +314,7 @@ onMounted(async () => {
height: 260px;
object-fit: cover;
border-radius: @border-radius-md;
flex-shrink: 0; /* 禁止收缩,保证宽度不变 */
margin-right: 30px;
}
@ -409,7 +413,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

@ -139,6 +139,12 @@ const newsList = [
//
@media (max-width: 768px) {
.banner {
height: auto !important;
}
.news-panel {
display: none;
}
.banner-content {
.main-title {
font-size: 32px;

View File

@ -23,7 +23,7 @@
"src/**/*.ts",
"src/**/*.d.ts",
"src/**/*.tsx",
"src/**/*.vue",
"src/**/*.vue", "src/auto-imports.d.ts", //
"vite.config.ts",
"src/main.ts"
]

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,47 +32,39 @@ export default defineConfig({
]
: []),
AutoImport({
imports: ['vue', 'vue-router'], // 自动导入 Vue、Vue Router API
// 自动导入 Vue 相关函数,以及 ElementPlus 的相关函数等
resolvers: [
ElementPlusResolver({
importStyle: false, // 关闭自动导入样式(手动引入 index.css
directives: true // 导入 Element Plus 指令(如 v-loading
})
// ElementPlusResolver({
// importStyle: 'sass',
// directives: true // 导入指令
// })
// 1. 自动导入的 API 来源Vue 核心 API + Vue Router 等)
imports: [
'vue', // 自动导入 Vue 组合式 APIref、reactive、onMounted 等)
'vue-router' // 可选:自动导入 Vue Router APIuseRouter 等)
// 其他来源(如 Pinia可按需添加
],
dts: true // 生成 auto-imports.d.ts让 ESLint 识别自动导入的 API
// 2. 生成类型声明文件(让 TypeScript 识别自动导入的 API
dts: 'src/auto-imports.d.ts', // 生成的类型文件路径(必须存在)
// 3. 解决 ESLint 报错(可选,若 ESLint 提示“未定义变量”)
eslintrc: {
enabled: true, // 生成 ESLint 配置片段
filepath: './.eslintrc-auto-import.json' // 生成的 ESLint 配置文件
}
}),
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({
gifsicle: { optimizationLevel: 7 },
optipng: { optimizationLevel: 7 },
mozjpeg: { quality: 80 },
pngquant: { quality: [0.8, 0.9] },
svgo: { plugins: [{ name: 'removeViewBox' }] }
})
// viteImagemin({
// gifsicle: { optimizationLevel: 7 },
// optipng: { optimizationLevel: 7 },
// mozjpeg: { quality: 80 },
// pngquant: { quality: [0.8, 0.9] },
// svgo: { plugins: [{ name: 'removeViewBox' }] }
// })
]
: [])
],
@ -86,46 +83,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')
}
}
})
}))