227 lines
6.3 KiB
Vue
227 lines
6.3 KiB
Vue
<template>
|
||
<view class="index-page">
|
||
<!-- Hero 首屏 -->
|
||
<HeroBanner :stats="trustStats" />
|
||
|
||
<!-- 4步流程(active 由页面统一管理,跟下面 PlatformFeatures 共用同一个定时器) -->
|
||
<StepsFlow
|
||
:steps="stepList"
|
||
:active="currentStep"
|
||
@change="onStepChange"
|
||
/>
|
||
|
||
<!-- 全球POD货盘 -->
|
||
<PodCatalog
|
||
:countries="podCountries"
|
||
:products="podProducts"
|
||
@countryChange="onPodCountryChange"
|
||
@moreTap="onGoProducts"
|
||
@productTap="onGoDetail"
|
||
/>
|
||
|
||
<!-- 平台功能 + 集成平台(active 由页面统一管理,跟上面 StepsFlow 共用同一个定时器) -->
|
||
<PlatformFeatures
|
||
:features="featureList"
|
||
:active="currentFeat"
|
||
@change="onFeatChange"
|
||
/>
|
||
|
||
<!-- 为什么选择 -->
|
||
<WhyChoose :reasons="reasonList" />
|
||
|
||
<!-- 公司简介 + 底部CTA -->
|
||
<CompanyIntro :abilities="companyFeatures" />
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
import HeroBanner from './components/HeroBanner.vue'
|
||
import StepsFlow from './components/StepsFlow.vue'
|
||
import PodCatalog from './components/PodCatalog.vue'
|
||
import PlatformFeatures from './components/PlatformFeatures.vue'
|
||
import WhyChoose from './components/WhyChoose.vue'
|
||
import CompanyIntro from './components/CompanyIntro.vue'
|
||
|
||
import { getCountries, getGoods } from '@/api/public.js'
|
||
import { buildUrl } from '@/api/request.js'
|
||
import { truncateProductTitle } from '@/utils/product.js'
|
||
|
||
import {
|
||
trustStats,
|
||
stepList,
|
||
featureList,
|
||
reasonList,
|
||
companyFeatures
|
||
} from './mock.js'
|
||
|
||
// 统一轮播间隔:3 秒推进一次,同时驱动 StepsFlow + PlatformFeatures
|
||
const AUTO_PLAY_INTERVAL = 3000
|
||
|
||
export default {
|
||
components: {
|
||
HeroBanner,
|
||
StepsFlow,
|
||
PodCatalog,
|
||
PlatformFeatures,
|
||
WhyChoose,
|
||
CompanyIntro
|
||
},
|
||
data() {
|
||
return {
|
||
trustStats,
|
||
stepList,
|
||
featureList,
|
||
reasonList,
|
||
companyFeatures,
|
||
podCountries: [],
|
||
podProducts: [],
|
||
// 两个区块共用一个定时器,但各自独立轮播(互不干扰)
|
||
currentStep: 0,
|
||
currentFeat: 0,
|
||
_autoPlayTimer: null // 唯一一个定时器
|
||
}
|
||
},
|
||
async onLoad() {
|
||
this.loadGlobalFont()
|
||
await this.loadPodCountries()
|
||
await this.loadPodProducts('')
|
||
},
|
||
onShow() {
|
||
// 页面显示 → 启动轮播
|
||
this.startAutoPlay()
|
||
},
|
||
onHide() {
|
||
// 页面隐藏 → 回收定时器(切 Tab、跳详情、退后台等)
|
||
this.stopAutoPlay()
|
||
},
|
||
onUnload() {
|
||
this.stopAutoPlay()
|
||
},
|
||
methods: {
|
||
// 全局加载苹方子集字体(常用3500字+数字英文,约700KB)
|
||
// 必须在页面加载后调用(App onLaunch 里页面未创建会报 not font page)
|
||
// family 用自定义名 PingFangSC(避免与系统苹方重名),已加到 app.css 字体栈首位;global: true 全局生效
|
||
// 加载失败自动回退系统字体,不影响使用
|
||
loadGlobalFont() {
|
||
uni.loadFontFace({
|
||
global: true,
|
||
family: 'CusPingFangSC',
|
||
source: `url("${buildUrl('/assets/miniprogram/fonts/PingFang-Regular.subset.ttf')}")`,
|
||
// source: 'url("https://raw.githubusercontent.com/googlefonts/zcool-kuaile/main/fonts/ttf/ZCOOLKuaiLe-Regular.ttf")' , // ✅ 正确
|
||
success: () => console.log('字体加载成功'),
|
||
fail: err => console.warn('字体加载失败,回退系统字体', err)
|
||
})
|
||
},
|
||
async loadPodCountries() {
|
||
try {
|
||
const res = await getCountries()
|
||
const data = (res.data && res.data.data) || []
|
||
const countries = [{ id: '', name: '全部' }]
|
||
data.forEach((c) => {
|
||
countries.push({ id: c.id, name: c.countryName })
|
||
})
|
||
this.podCountries = countries
|
||
} catch (err) {
|
||
console.error('[loadPodCountries Error]', err)
|
||
}
|
||
},
|
||
|
||
async loadPodProducts(countryId) {
|
||
try {
|
||
const params = { page: 1, pageSize: 4 }
|
||
if (countryId) {
|
||
params.countryId = countryId
|
||
}
|
||
const res = await getGoods(params)
|
||
const data = (res.data && res.data.data) || { items: [] }
|
||
const items = (data.items || []).slice(0, 4)
|
||
this.podProducts = items.map((item) => ({
|
||
id: item.goodId,
|
||
name: truncateProductTitle(item.goodName),
|
||
image: item.image
|
||
}))
|
||
} catch (err) {
|
||
console.error('[loadPodProducts Error]', err)
|
||
this.podProducts = []
|
||
}
|
||
},
|
||
|
||
async onPodCountryChange(id) {
|
||
await this.loadPodProducts(id)
|
||
},
|
||
|
||
onGoProducts() {
|
||
uni.switchTab({
|
||
url: '/pages/products/products'
|
||
})
|
||
},
|
||
onGoDetail(item) {
|
||
uni.navigateTo({
|
||
url: '/pages/product-detail/product-detail?id=' + item.id
|
||
})
|
||
},
|
||
|
||
/* ============================
|
||
* 统一轮播控制(单一入口)
|
||
* ============================ */
|
||
|
||
/**
|
||
* 启动统一轮播:单一 setInterval,3 秒一次,
|
||
* 同时推进 StepsFlow 的 currentStep 和 PlatformFeatures 的 currentFeat。
|
||
* 两个组件的 active 各自 mod 自己的 length,互不干扰。
|
||
*/
|
||
startAutoPlay() {
|
||
this.stopAutoPlay()
|
||
this._autoPlayTimer = setInterval(() => {
|
||
const stepTotal = (this.stepList || []).length
|
||
const featTotal = (this.featureList || []).length
|
||
if (stepTotal > 1) {
|
||
this.currentStep = (this.currentStep + 1) % stepTotal
|
||
}
|
||
if (featTotal > 1) {
|
||
this.currentFeat = (this.currentFeat + 1) % featTotal
|
||
}
|
||
}, AUTO_PLAY_INTERVAL)
|
||
},
|
||
|
||
/**
|
||
* 回收定时器:onHide / onUnload 时调用,避免后台运行浪费资源
|
||
*/
|
||
stopAutoPlay() {
|
||
if (this._autoPlayTimer) {
|
||
clearInterval(this._autoPlayTimer)
|
||
this._autoPlayTimer = null
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 用户点击 StepsFlow 卡片:
|
||
* 先停定时器 → 更新 currentStep → 重启 3s 计数
|
||
* (同时 currentFeat 的计数被重置也无妨,体验上完全可接受)
|
||
*/
|
||
onStepChange(idx) {
|
||
this.stopAutoPlay()
|
||
this.currentStep = idx
|
||
this.startAutoPlay()
|
||
},
|
||
|
||
/**
|
||
* 用户点击 PlatformFeatures 卡片:同上逻辑,函数复用
|
||
*/
|
||
onFeatChange(idx) {
|
||
this.stopAutoPlay()
|
||
this.currentFeat = idx
|
||
this.startAutoPlay()
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style>
|
||
.index-page {
|
||
width: 100%;
|
||
min-height: 100vh;
|
||
background: #ffffff;
|
||
}
|
||
</style>
|