文件
yjyf/YJYF/YJYF.vue
linda 508d2c7318 style(home): 首页轮播高度压缩三分之一
轮播高度 380rpx → 254rpx,标题/副标题/按钮/平台实力数据卡/关闭按钮/指示点同步紧凑,保持后端 theme、action_text、stats 数据字段不变
2026-10-29 09:25:17 +08:00

1290 行
34 KiB
Vue

此文件含有模棱两可的 Unicode 字符

此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。

<!--
优匠优服 · 首页(UI 设计稿)
主题:橙金体系 主橙 #F0710D / 亮橙 #FFA93F / 线稿棕橙 #B85E2A
说明:全部图标由纯 CSS 绘制(数据驱动),无图片资源依赖,小程序端 100% 兼容
-->
<template>
<view class="page">
<!-- ==================== 橙色渐变头部 ==================== -->
<view class="header">
<view class="header-bg">
<view class="deco deco-1"></view>
<view class="deco deco-2"></view>
<view class="deco deco-3"></view>
</view>
<!-- 自定义导航 -->
<view class="nav" :style="{ paddingTop: statusBarHeight + 'px' }">
<view class="nav-inner" :style="{ height: navBarHeight + 'px' }">
<view class="nav-home" @tap="onHome">
<view class="nh-roof"></view>
<view class="nh-body"></view>
</view>
<text class="nav-title">优匠优服</text>
</view>
</view>
<!-- 问候 + 优惠券 -->
<view class="greet">
<view class="greet-l">
<view class="avatar-ring">
<view class="avatar">
<view class="av-head"></view>
<view class="av-body"></view>
</view>
</view>
<view class="greet-txt">
<text class="greet-hi">{{ greeting }},欢迎回来</text>
<text class="greet-name">qbdjsj1308160</text>
</view>
</view>
<view class="coupon" @tap="onCoupon">
<text class="coupon-tag">券</text>
<text class="coupon-text">优惠券待领取</text>
<view class="coupon-btn">领取</view>
</view>
</view>
</view>
<!-- ==================== 悬浮搜索 ==================== -->
<view class="search-wrap">
<view class="search">
<view class="search-ic">
<view class="si-ring"></view>
<view class="si-handle"></view>
</view>
<text class="search-ph">搜索服务 / 师傅 / 品类</text>
</view>
<view class="search-btn" @tap="onSearch">搜索</view>
</view>
<!-- ==================== 内容区 ==================== -->
<view class="content">
<!-- 服务保障 -->
<view class="assure">
<view class="assure-item" v-for="(a, i) in assures" :key="i">
<view class="assure-dot"></view>
<text class="assure-txt">{{ a }}</text>
</view>
</view>
<!-- 全部服务 -->
<view class="panel">
<view class="panel-head">
<view class="panel-title-box">
<view class="title-bar"></view>
<text class="panel-title">全部服务</text>
</view>
<view class="panel-more" @tap="onMore">
<text class="panel-more-txt">查看全部</text>
<view class="arrow-r gray"></view>
</view>
</view>
<view class="cats">
<view class="cat" v-for="(c, i) in cats" :key="c.id" @tap="onCat(c)">
<view class="cat-circle">
<image class="cat-img" :src="'/static/icons/' + iconOf(c.icon) + '.png'" mode="aspectFit"></image>
</view>
<text class="cat-name">{{ c.name }}</text>
</view>
</view>
</view>
<!-- 下单教程(文案由后端 fa_config 下发) -->
<view class="tutorial" @tap="onLearn">
<view class="tutorial-ic">
<view class="ti-play"></view>
</view>
<view class="tutorial-txt">
<text class="tutorial-title">{{ tutorial.title }}</text>
<text class="tutorial-sub">{{ tutorial.sub }}</text>
</view>
<view class="tutorial-btn">
<text class="tutorial-btn-txt">立即学习</text>
<view class="arrow-r blue"></view>
</view>
</view>
<!-- 品牌 / 服务主题轮播(数据由后端 fa_banner 下发) -->
<view class="banner-wrap" v-if="showBanners && banners.length">
<swiper
class="banner-swiper"
:current="bannerIndex"
:autoplay="banners.length > 1"
:interval="4500"
:duration="420"
circular
@change="onBannerChange"
>
<swiper-item v-for="b in banners" :key="b.id">
<view class="banner-slide" :class="'theme-' + b.theme + (b.image ? ' has-image' : '')" @tap="onBanner(b)">
<image v-if="b.image" class="slide-bg" :src="b.image" mode="aspectFill"></image>
<view v-if="b.image" class="slide-mask"></view>
<view class="slide-deco d1"></view>
<view class="slide-deco d2"></view>
<!-- 平台实力主题 -->
<block v-if="b.theme === 'brand'">
<view class="brand-head">
<view class="brand-line"></view>
<text class="brand-title">{{ b.title || '平台实力' }}</text>
</view>
<view class="brand-grid">
<view class="brand-cell" v-for="(s, si) in b.stats" :key="si">
<text class="brand-value">{{ s.value }}</text>
<text class="brand-label">{{ s.label }}</text>
</view>
</view>
<view class="brand-foot">
<text class="brand-foot-txt">{{ b.subtitle }}</text>
<view class="brand-seal" v-if="b.date_text">{{ b.date_text }}</view>
</view>
</block>
<!-- 活动 / 服务主题 -->
<block v-else>
<view class="slide-close" v-if="b.theme === 'promo'" @tap.stop="onCloseBanner">×</view>
<view class="slide-tags">
<view class="slide-tag">{{ b.badge }}</view>
<view class="slide-tag ghost" v-if="b.theme === 'promo'">限时</view>
</view>
<text class="slide-title">{{ b.title }}</text>
<text class="slide-sub">{{ b.subtitle }}</text>
<view class="slide-bottom">
<view class="slide-date" v-if="b.date_text">{{ b.date_text }}</view>
<view class="slide-cta" v-if="b.action_text" @tap.stop="onBanner(b)">{{ b.action_text }}</view>
</view>
<text class="slide-watermark" v-if="b.theme === 'service'">{{ (b.badge || '匠').slice(0, 1) }}</text>
</block>
</view>
</swiper-item>
</swiper>
<view class="banner-dots" v-if="banners.length > 1">
<view
v-for="(b, bi) in banners"
:key="'dot-' + b.id"
class="banner-dot"
:class="{ on: bannerIndex === bi }"
@tap="goBanner(bi)"
></view>
</view>
</view>
<view class="foot-tip">优匠优服 · 让手艺人有活干,让家有温度</view>
</view>
<!-- ==================== 自定义底部导航 ==================== -->
<view class="tabbar">
<view class="tab" :class="{ on: tabIndex === 0 }" @tap="switchTab(0)">
<view class="tic">
<view class="tr" style="left:10rpx;top:4rpx"></view>
<view class="ol" style="left:13rpx;top:16rpx;width:18rpx;height:18rpx;border-top:none"></view>
</view>
<text class="tab-txt">首页</text>
</view>
<view class="tab" :class="{ on: tabIndex === 1 }" @tap="switchTab(1)">
<view class="tic">
<view class="ol" style="left:11rpx;top:5rpx;width:22rpx;height:30rpx;border-radius:3rpx"></view>
<view class="ln" style="left:16rpx;top:13rpx;width:12rpx;height:3rpx"></view>
<view class="ln" style="left:16rpx;top:21rpx;width:12rpx;height:3rpx"></view>
</view>
<text class="tab-txt">订单</text>
</view>
<view class="tab tab-center">
<view class="fab" @tap="onOrder">
<view class="fic">
<view class="fo" style="left:5rpx;top:2rpx;width:32rpx;height:32rpx;border-radius:5rpx"></view>
<view class="fl" style="left:11rpx;top:11rpx;width:14rpx;height:3rpx"></view>
<view class="fl" style="left:11rpx;top:19rpx;width:14rpx;height:3rpx"></view>
<view class="fp" style="left:25rpx;top:22rpx;width:21rpx;height:6rpx"></view>
</view>
<text class="fab-txt">立即下单</text>
</view>
</view>
<view class="tab" :class="{ on: tabIndex === 2 }" @tap="switchTab(2)">
<view class="tic">
<view class="ol" style="left:6rpx;top:7rpx;width:32rpx;height:24rpx;border-radius:12rpx"></view>
<view class="tr-d" style="left:14rpx;top:29rpx"></view>
</view>
<text class="tab-txt">消息</text>
</view>
<view class="tab" :class="{ on: tabIndex === 3 }" @tap="switchTab(3)">
<view class="tic">
<view class="ol ct" style="left:14rpx;top:3rpx;width:16rpx;height:16rpx"></view>
<view class="ol" style="left:7rpx;top:21rpx;width:30rpx;height:16rpx;border-radius:15rpx 15rpx 0 0;border-bottom:none"></view>
</view>
<text class="tab-txt">我的</text>
</view>
</view>
</view>
</template>
<script setup>
import { ref } from 'vue'
import { onShow } from '@dcloudio/uni-app'
import api from '@/utils/api.js'
import { isLogin, toLogin } from '@/utils/request.js'
import { ensureSilentLogin } from '@/utils/wxlogin.js'
/* ---------- 系统适配:状态栏 / 导航栏高度(刘海屏 + 胶囊按钮) ---------- */
const statusBarHeight = ref(20)
const navBarHeight = ref(44)
const tabIndex = ref(0)
try {
const info = uni.getSystemInfoSync()
if (info && info.statusBarHeight) statusBarHeight.value = info.statusBarHeight
if (typeof uni.getMenuButtonBoundingClientRect === 'function') {
const menu = uni.getMenuButtonBoundingClientRect()
if (menu && menu.height) navBarHeight.value = (menu.top - statusBarHeight.value) * 2 + menu.height
}
} catch (e) {}
/* ---------- 问候语 ---------- */
const hh = new Date().getHours()
const greeting = hh < 6 ? '夜深了' : hh < 11 ? '上午好' : hh < 14 ? '中午好' : hh < 18 ? '下午好' : '晚上好'
/* ---------- 服务保障 ---------- */
/* ---------- 服务保障 / 教程 / Banner(均由后端下发) ---------- */
const assures = ref(['平台认证', '先行赔付', '极速上门', '售后无忧'])
const banners = ref([])
const bannerIndex = ref(0)
const showBanners = ref(true)
const tutorial = ref({ title: '下单找师傅教程', sub: '3 分钟看懂,下单不踩坑' })
/* ---------- 服务图标:纯 CSS 图元,零图片依赖
o=描边框 l=实心线 d=圆形描边 t=三角
格式 [类型, left, top, w, h, radius, rotate],画布 56×56rpx */
/* 服务图标资源:static/icons/<key>.png(双色扁平,由 SVG 渲染) */
const ICON_KEYS = ['sofa','bath','lamp','lock','curtain','frame','water','rack','door','appliance','tv','shower','kitchen','fitness','more']
const iconOf = (key) => (ICON_KEYS.indexOf(key) > -1 ? key : 'more')
/* 服务类目(真实接口) */
const cats = ref([])
const badge = ref({ coupon: 0, unread: 0 })
/**
* 进主页即尝试静默微信登录(单飞 + 失效自动重试),失败保持游客态
*/
async function loadHome() {
await ensureSilentLogin()
try {
const res = await api.home()
cats.value = res.cats || []
badge.value = res.badge || { coupon: 0, unread: 0 }
if (res.assures && res.assures.length) assures.value = res.assures
if (res.banners && res.banners.length) banners.value = res.banners
if (bannerIndex.value >= banners.value.length) bannerIndex.value = 0
if (res.tutorial) tutorial.value = res.tutorial
} catch (e) {}
}
/* ---------- 交互 ---------- */
const toast = (title) => uni.showToast({ title, icon: 'none' })
function needLogin() {
if (isLogin()) return true
toLogin()
return false
}
const onHome = () => toast('已是首页')
const onCoupon = () => toast(badge.value.coupon > 0 ? ('您有 ' + badge.value.coupon + ' 张优惠券待使用') : '暂无可用优惠券')
const onSearch = () => uni.navigateTo({ url: '/pages/service/list?keyword=' + encodeURIComponent('坐便器') })
const onCat = (c) => uni.navigateTo({
url: '/pages/service/list?cat_id=' + c.id + '&cat_name=' + encodeURIComponent(c.name)
})
const onMore = () => uni.navigateTo({ url: '/pages/service/list?cat_id=15&cat_name=' + encodeURIComponent('更多服务') })
const onLearn = () => toast('教程模块开发中')
const onBannerChange = (e) => {
bannerIndex.value = (e && e.detail && e.detail.current) ? e.detail.current : 0
}
const goBanner = (i) => { bannerIndex.value = i }
const onBanner = (b) => {
if (!b || !b.link) {
if (b && b.theme === 'brand') return
toast('活动详情敬请期待')
return
}
const link = b.link
if (/^https?:\/\//i.test(link)) {
uni.setClipboardData({ data: link, success: () => toast('链接已复制') })
return
}
if (link.indexOf('/YJYF/') === 0) {
uni.reLaunch({ url: link })
return
}
uni.navigateTo({ url: link })
}
const onCloseBanner = () => { showBanners.value = false }
const onOrder = () => {
if (!needLogin()) return
uni.navigateTo({ url: '/pages/order/quick' })
}
const onUser = () => {
if (!needLogin()) return
uni.navigateTo({ url: '/pages/mine/mine' })
}
const switchTab = (i) => {
if (i === 1) {
if (!needLogin()) return
uni.navigateTo({ url: '/pages/order/list' })
return
}
if (i === 2) {
if (!needLogin()) return
uni.navigateTo({ url: '/pages/message/message' })
return
}
if (i === 3) {
onUser()
return
}
tabIndex.value = i
}
onShow(() => loadHome())
</script>
<style scoped>
/* ============================================================
优匠优服 · 设计令牌
主橙 #F0710D / 亮橙 #FFA93F / 深橙 #EC6E0C
线稿棕橙 #B85E2A / 圆形底 #FDF4EC / 页面底 #F5F6F8
============================================================ */
.page {
min-height: 100vh;
background: #F5F6F8;
padding-bottom: 200rpx;
}
/* ---------- 橙色渐变头部 ---------- */
.header {
position: relative;
padding-bottom: 100rpx;
}
.header-bg {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
border-radius: 0 0 48rpx 48rpx;
overflow: hidden;
background: linear-gradient(160deg, #FFB85C 0%, #F98D25 46%, #ED6F0E 100%);
}
.deco {
position: absolute;
border-radius: 50%;
}
.deco-1 {
width: 360rpx;
height: 360rpx;
right: -130rpx;
top: -150rpx;
background: rgba(255, 255, 255, 0.14);
}
.deco-2 {
width: 220rpx;
height: 220rpx;
left: -100rpx;
top: 110rpx;
background: rgba(255, 255, 255, 0.10);
}
.deco-3 {
width: 120rpx;
height: 120rpx;
right: 110rpx;
bottom: -70rpx;
background: rgba(255, 255, 255, 0.09);
}
/* ---------- 导航 ---------- */
.nav {
position: relative;
z-index: 3;
}
.nav-inner {
position: relative;
display: flex;
align-items: center;
justify-content: center;
}
.nav-title {
font-size: 36rpx;
font-weight: 700;
color: #ffffff;
letter-spacing: 6rpx;
text-shadow: 0 2rpx 10rpx rgba(180, 80, 10, 0.30);
}
.nav-home {
position: absolute;
left: 24rpx;
top: 50%;
width: 64rpx;
height: 64rpx;
margin-top: -32rpx;
border-radius: 50%;
background: rgba(255, 255, 255, 0.18);
}
.nh-roof {
position: absolute;
left: 17rpx;
top: 16rpx;
width: 0;
height: 0;
border-left: 15rpx solid transparent;
border-right: 15rpx solid transparent;
border-bottom: 14rpx solid #ffffff;
}
.nh-body {
position: absolute;
left: 21rpx;
top: 30rpx;
width: 22rpx;
height: 17rpx;
border: 3rpx solid #ffffff;
border-top: none;
box-sizing: border-box;
}
/* ---------- 问候行 ---------- */
.greet {
position: relative;
z-index: 3;
display: flex;
align-items: center;
justify-content: space-between;
padding: 6rpx 28rpx 0;
}
.greet-l {
display: flex;
align-items: center;
flex: 1;
min-width: 0;
}
.avatar-ring {
width: 96rpx;
height: 96rpx;
border-radius: 50%;
background: rgba(255, 255, 255, 0.30);
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
box-shadow: 0 6rpx 16rpx rgba(170, 70, 10, 0.18);
}
.avatar {
position: relative;
width: 84rpx;
height: 84rpx;
border-radius: 50%;
background: #FFF4E8;
overflow: hidden;
}
.av-head {
position: absolute;
left: 27rpx;
top: 17rpx;
width: 30rpx;
height: 30rpx;
border-radius: 50%;
background: #EDA162;
}
.av-body {
position: absolute;
left: 15rpx;
top: 53rpx;
width: 54rpx;
height: 40rpx;
border-radius: 28rpx 28rpx 0 0;
background: #EDA162;
}
.greet-txt {
display: flex;
flex-direction: column;
margin-left: 18rpx;
flex: 1;
min-width: 0;
}
.greet-hi {
font-size: 22rpx;
line-height: 30rpx;
color: rgba(255, 255, 255, 0.86);
}
.greet-name {
font-size: 28rpx;
line-height: 38rpx;
font-weight: 600;
color: #ffffff;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.coupon {
display: flex;
align-items: center;
height: 64rpx;
padding: 0 8rpx;
border-radius: 32rpx;
background: linear-gradient(135deg, #FFFAF3 0%, #FFE5C4 100%);
box-shadow: 0 6rpx 16rpx rgba(170, 70, 10, 0.20);
flex-shrink: 0;
}
.coupon-tag {
width: 32rpx;
height: 32rpx;
line-height: 32rpx;
text-align: center;
border-radius: 8rpx;
background: linear-gradient(135deg, #FFB24D 0%, #F0740F 100%);
color: #ffffff;
font-size: 18rpx;
}
.coupon-text {
margin: 0 10rpx;
font-size: 22rpx;
font-weight: 600;
color: #BF5507;
}
.coupon-btn {
height: 48rpx;
line-height: 48rpx;
padding: 0 18rpx;
border-radius: 24rpx;
background: linear-gradient(135deg, #FFA93F 0%, #F0710D 100%);
color: #ffffff;
font-size: 22rpx;
}
/* ---------- 悬浮搜索 ---------- */
.search-wrap {
position: relative;
z-index: 4;
margin: -80rpx 28rpx 0;
height: 100rpx;
}
.search {
display: flex;
align-items: center;
height: 100rpx;
padding: 0 170rpx 0 30rpx;
box-sizing: border-box;
border-radius: 50rpx;
background: #ffffff;
box-shadow: 0 12rpx 32rpx rgba(226, 120, 26, 0.22);
}
.search-ic {
position: relative;
width: 34rpx;
height: 34rpx;
flex-shrink: 0;
}
.si-ring {
position: absolute;
left: 0;
top: 0;
width: 24rpx;
height: 24rpx;
border: 3rpx solid #C2C2C2;
border-radius: 50%;
box-sizing: border-box;
}
.si-handle {
position: absolute;
left: 22rpx;
top: 21rpx;
width: 12rpx;
height: 3rpx;
background: #C2C2C2;
transform: rotate(45deg);
transform-origin: 0 50%;
}
.search-ph {
margin-left: 16rpx;
font-size: 28rpx;
color: #A8A8A8;
}
.search-btn {
position: absolute;
right: 10rpx;
top: 10rpx;
width: 150rpx;
height: 80rpx;
display: flex;
align-items: center;
justify-content: center;
border-radius: 40rpx;
background: linear-gradient(135deg, #FFA93F 0%, #F0710D 100%);
color: #ffffff;
font-size: 30rpx;
font-weight: 600;
letter-spacing: 2rpx;
box-shadow: 0 6rpx 16rpx rgba(240, 113, 13, 0.34);
}
/* ---------- 内容区 ---------- */
.content {
padding: 40rpx 28rpx 0;
}
/* ---------- 服务保障 ---------- */
.assure {
display: flex;
align-items: center;
justify-content: space-between;
padding: 22rpx 24rpx;
background: #ffffff;
border-radius: 20rpx;
box-shadow: 0 6rpx 20rpx rgba(240, 129, 31, 0.07);
margin-bottom: 24rpx;
}
.assure-item {
display: flex;
align-items: center;
}
.assure-dot {
width: 10rpx;
height: 10rpx;
border-radius: 50%;
background: linear-gradient(135deg, #FFB24D 0%, #F0740F 100%);
margin-right: 10rpx;
}
.assure-txt {
font-size: 22rpx;
color: #7A7A7A;
}
/* ---------- 全部服务面板 ---------- */
.panel {
background: #ffffff;
border-radius: 24rpx;
padding: 26rpx 16rpx 8rpx;
box-shadow: 0 8rpx 24rpx rgba(240, 129, 31, 0.07);
}
.panel-head {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 12rpx 14rpx;
}
.panel-title-box {
display: flex;
align-items: center;
}
.title-bar {
width: 8rpx;
height: 30rpx;
border-radius: 4rpx;
background: linear-gradient(180deg, #FFB24D 0%, #F0740F 100%);
margin-right: 12rpx;
}
.panel-title {
font-size: 32rpx;
font-weight: 700;
color: #1F1F1F;
letter-spacing: 1rpx;
}
.panel-more {
display: flex;
align-items: center;
}
.panel-more-txt {
font-size: 24rpx;
color: #9A9A9A;
}
/* ---------- 分类宫格 ---------- */
.cats {
display: flex;
flex-wrap: wrap;
padding-bottom: 10rpx;
}
.cat {
width: 20%;
display: flex;
flex-direction: column;
align-items: center;
padding: 10rpx 0 16rpx;
}
.cat-circle {
width: 96rpx;
height: 96rpx;
border-radius: 50%;
background: linear-gradient(160deg, #FFF7F0 0%, #FCE9D6 100%);
box-shadow: inset 0 0 0 2rpx rgba(255, 255, 255, 0.9), 0 4rpx 10rpx rgba(240, 150, 60, 0.10);
display: flex;
align-items: center;
justify-content: center;
}
.cat-img {
width: 58rpx;
height: 58rpx;
}
.cat-name {
display: block;
margin-top: 12rpx;
font-size: 24rpx;
line-height: 30rpx;
color: #333333;
text-align: center;
white-space: nowrap;
}
/* ---------- 纯 CSS 图标绘制原语 ---------- */
.ic {
position: relative;
width: 56rpx;
height: 56rpx;
}
.ic view {
position: absolute;
box-sizing: border-box;
}
.ic .ol {
border: 3rpx solid #B85E2A;
border-radius: 3rpx;
}
.ic .ln {
background: #B85E2A;
}
.ic .ct {
border-radius: 50%;
}
.tri {
width: 0;
height: 0;
}
/* ---------- 下单教程 ---------- */
.tutorial {
display: flex;
align-items: center;
margin-top: 24rpx;
padding: 24rpx;
background: #ffffff;
border-radius: 20rpx;
box-shadow: 0 6rpx 20rpx rgba(240, 129, 31, 0.07);
}
.tutorial-ic {
width: 64rpx;
height: 64rpx;
border-radius: 20rpx;
background: linear-gradient(135deg, #EAF2FF 0%, #D6E6FF 100%);
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.ti-play {
width: 0;
height: 0;
border-top: 11rpx solid transparent;
border-bottom: 11rpx solid transparent;
border-left: 17rpx solid #4A7CFF;
margin-left: 6rpx;
}
.tutorial-txt {
display: flex;
flex-direction: column;
flex: 1;
min-width: 0;
margin-left: 18rpx;
}
.tutorial-title {
font-size: 30rpx;
font-weight: 600;
color: #1F1F1F;
}
.tutorial-sub {
margin-top: 4rpx;
font-size: 22rpx;
color: #9A9A9A;
}
.tutorial-btn {
display: flex;
align-items: center;
height: 56rpx;
padding: 0 20rpx;
border-radius: 28rpx;
background: linear-gradient(90deg, #EDF3FF 0%, #DCE8FF 100%);
flex-shrink: 0;
}
.tutorial-btn-txt {
font-size: 24rpx;
color: #4A7CFF;
}
.arrow-r {
width: 12rpx;
height: 12rpx;
margin-left: 10rpx;
border-top: 3rpx solid #4A7CFF;
border-right: 3rpx solid #4A7CFF;
transform: rotate(45deg);
box-sizing: border-box;
}
.arrow-r.blue {
border-top-color: #4A7CFF;
border-right-color: #4A7CFF;
}
.arrow-r.gray {
border-top-color: #BFBFBF;
border-right-color: #BFBFBF;
}
/* ---------- 大促 Banner ---------- */
.banner {
position: relative;
height: 300rpx;
margin-top: 24rpx;
border-radius: 24rpx;
overflow: hidden;
background: linear-gradient(125deg, #FFF3E2 0%, #FFE0BE 42%, #FFC894 100%);
box-shadow: 0 10rpx 26rpx rgba(240, 150, 60, 0.16);
}
.banner-deco {
position: absolute;
border-radius: 50%;
}
.bd1 {
width: 300rpx;
height: 300rpx;
right: -90rpx;
top: -120rpx;
background: rgba(255, 255, 255, 0.55);
}
.bd2 {
width: 170rpx;
height: 170rpx;
left: -70rpx;
bottom: -80rpx;
background: rgba(255, 138, 60, 0.16);
}
.bd3 {
width: 90rpx;
height: 90rpx;
right: 160rpx;
bottom: 26rpx;
background: rgba(255, 138, 60, 0.14);
}
.banner-inner {
position: relative;
z-index: 2;
padding: 28rpx 0 0 30rpx;
}
.banner-tags {
display: flex;
}
.banner-tag {
padding: 0 14rpx;
line-height: 34rpx;
border-radius: 17rpx;
background: linear-gradient(135deg, #F5452B 0%, #D92210 100%);
color: #FFE9A8;
font-size: 20rpx;
letter-spacing: 1rpx;
}
.banner-tag.ghost {
margin-left: 10rpx;
background: rgba(232, 51, 27, 0.12);
color: #C0392B;
}
.banner-title {
display: block;
margin-top: 14rpx;
font-size: 52rpx;
font-weight: 800;
color: #E8331B;
letter-spacing: 2rpx;
text-shadow: 0 3rpx 0 #FFE9A8;
}
.banner-desc {
display: block;
margin-top: 8rpx;
font-size: 24rpx;
color: #C0392B;
}
.banner-date {
display: inline-block;
margin-top: 14rpx;
padding: 0 16rpx;
line-height: 34rpx;
border-radius: 17rpx;
background: rgba(232, 51, 27, 0.10);
color: #C0392B;
font-size: 21rpx;
}
.banner-cta {
position: absolute;
right: 26rpx;
bottom: 30rpx;
z-index: 3;
height: 60rpx;
padding: 0 26rpx;
display: flex;
align-items: center;
border-radius: 30rpx;
background: linear-gradient(135deg, #FF9E2E 0%, #EE6C05 100%);
color: #ffffff;
font-size: 24rpx;
font-weight: 600;
box-shadow: 0 6rpx 16rpx rgba(238, 108, 5, 0.32);
}
.banner-close {
position: absolute;
right: 20rpx;
top: 20rpx;
z-index: 3;
width: 40rpx;
height: 40rpx;
line-height: 36rpx;
text-align: center;
border-radius: 50%;
background: rgba(0, 0, 0, 0.22);
color: #ffffff;
font-size: 28rpx;
}
/* ---------- 平台实力 ---------- */
.stats {
position: relative;
margin-top: 24rpx;
padding: 28rpx 26rpx 22rpx;
border-radius: 24rpx;
overflow: hidden;
background: linear-gradient(125deg, #FFB062 0%, #F68A22 48%, #EC6E0C 100%);
box-shadow: 0 12rpx 28rpx rgba(236, 110, 12, 0.28);
}
.stats-glare {
position: absolute;
right: -110rpx;
top: -130rpx;
width: 340rpx;
height: 340rpx;
border-radius: 50%;
background: rgba(255, 255, 255, 0.12);
}
.stats-head {
position: relative;
z-index: 2;
display: flex;
align-items: center;
}
.stats-line {
width: 6rpx;
height: 26rpx;
border-radius: 3rpx;
background: #FFF0C9;
margin-right: 10rpx;
}
.stats-title {
font-size: 26rpx;
font-weight: 600;
color: #FFF3DF;
letter-spacing: 1rpx;
}
.stats-grid {
position: relative;
z-index: 2;
display: flex;
align-items: flex-end;
margin-top: 20rpx;
}
.stats-cell {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
}
.stats-num {
font-size: 46rpx;
line-height: 56rpx;
font-weight: 800;
color: #ffffff;
letter-spacing: 1rpx;
}
.stats-unit {
font-size: 24rpx;
font-weight: 600;
}
.stats-label {
margin-top: 6rpx;
font-size: 22rpx;
color: rgba(255, 255, 255, 0.85);
}
.stats-div {
width: 2rpx;
height: 56rpx;
background: rgba(255, 255, 255, 0.28);
margin-bottom: 26rpx;
}
.stats-foot {
position: relative;
z-index: 2;
display: flex;
align-items: center;
justify-content: space-between;
margin-top: 20rpx;
padding-top: 18rpx;
border-top: 2rpx solid rgba(255, 255, 255, 0.22);
}
.stats-foot-txt {
font-size: 22rpx;
color: rgba(255, 255, 255, 0.90);
}
.stats-seal {
padding: 6rpx 14rpx;
border-radius: 8rpx;
background: #FFF4E3;
color: #E8331B;
font-size: 20rpx;
font-weight: 700;
}
/* ---------- 品牌 / 服务主题轮播 ---------- */
.banner-wrap { margin-top: 24rpx; }
.banner-swiper {
height: 254rpx;
border-radius: 24rpx;
overflow: hidden;
box-shadow: 0 12rpx 28rpx rgba(236, 110, 12, 0.20);
}
.banner-slide {
position: relative;
width: 100%;
height: 100%;
box-sizing: border-box;
padding: 22rpx 26rpx 18rpx;
overflow: hidden;
border-radius: 24rpx;
}
.slide-bg { position: absolute; left: 0; top: 0; width: 100%; height: 100%; z-index: 0; }
.slide-mask {
position: absolute; left: 0; top: 0; width: 100%; height: 100%; z-index: 1;
background: linear-gradient(90deg, rgba(0,0,0,.38) 0%, rgba(0,0,0,.08) 100%);
}
.slide-deco { position: absolute; border-radius: 50%; z-index: 1; }
.theme-promo { background: linear-gradient(125deg, #FFF3E2 0%, #FFE0BE 45%, #FFC894 100%); }
.theme-promo .slide-deco.d1 { width: 300rpx; height: 300rpx; right: -90rpx; top: -120rpx; background: rgba(255,255,255,.55); }
.theme-promo .slide-deco.d2 { width: 170rpx; height: 170rpx; left: -70rpx; bottom: -80rpx; background: rgba(255,138,60,.16); }
.theme-service { background: linear-gradient(125deg, #FFB062 0%, #F68A22 52%, #EC6E0C 100%); }
.theme-brand { background: linear-gradient(125deg, #FFB062 0%, #F68A22 48%, #EC6E0C 100%); }
.theme-service .slide-deco.d1, .theme-brand .slide-deco.d1 {
width: 320rpx; height: 320rpx; right: -110rpx; top: -130rpx; background: rgba(255,255,255,.12);
}
.theme-service .slide-deco.d2, .theme-brand .slide-deco.d2 {
width: 160rpx; height: 160rpx; left: -60rpx; bottom: -70rpx; background: rgba(255,255,255,.08);
}
.slide-tags { position: relative; z-index: 2; display: flex; }
.slide-tag {
padding: 0 12rpx; line-height: 30rpx; border-radius: 15rpx;
font-size: 18rpx; letter-spacing: 1rpx;
}
.theme-promo .slide-tag { background: linear-gradient(135deg, #F5452B 0%, #D92210 100%); color: #FFE9A8; }
.theme-promo .slide-tag.ghost { margin-left: 10rpx; background: rgba(232,51,27,.12); color: #C0392B; }
.theme-service .slide-tag { background: rgba(255,255,255,.22); color: #fff; }
.slide-title {
position: relative; z-index: 2; display: block; margin-top: 10rpx;
font-size: 36rpx; font-weight: 800; letter-spacing: 1rpx; line-height: 1.16;
overflow: hidden; white-space: nowrap; text-overflow: ellipsis;
}
.theme-promo .slide-title { color: #E8331B; text-shadow: 0 3rpx 0 #FFE9A8; }
.theme-service .slide-title { color: #fff; }
.slide-sub { position: relative; z-index: 2; display: block; margin-top: 6rpx; font-size: 21rpx; line-height: 1.35; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.theme-promo .slide-sub { color: #C0392B; }
.theme-service .slide-sub { color: rgba(255,255,255,.92); }
.slide-bottom {
position: absolute; left: 26rpx; right: 26rpx; bottom: 18rpx; z-index: 3;
display: flex; align-items: center; justify-content: space-between;
}
.slide-date { padding: 0 14rpx; line-height: 30rpx; border-radius: 15rpx; font-size: 19rpx; }
.theme-promo .slide-date { background: rgba(232,51,27,.10); color: #C0392B; }
.theme-service .slide-date { background: rgba(255,255,255,.18); color: #fff; }
.slide-cta {
height: 52rpx; padding: 0 22rpx; display: flex; align-items: center;
border-radius: 26rpx; font-size: 21rpx; font-weight: 600;
}
.theme-promo .slide-cta {
background: linear-gradient(135deg, #FF9E2E 0%, #EE6C05 100%); color: #fff;
box-shadow: 0 6rpx 16rpx rgba(238,108,5,.32);
}
.theme-service .slide-cta {
background: #fff; color: #E36B0C;
box-shadow: 0 6rpx 16rpx rgba(0,0,0,.12);
}
.slide-close {
position: absolute; right: 14rpx; top: 14rpx; z-index: 4;
width: 34rpx; height: 34rpx; line-height: 30rpx; text-align: center;
border-radius: 50%; font-size: 24rpx;
}
.theme-promo .slide-close { background: rgba(0,0,0,.10); color: #B85E2A; }
.slide-watermark {
position: absolute; right: 18rpx; bottom: -12rpx; z-index: 1;
font-size: 160rpx; line-height: 1; font-weight: 800; color: rgba(255,255,255,.09);
}
.brand-head { position: relative; z-index: 2; display: flex; align-items: center; }
.brand-line { width: 5rpx; height: 22rpx; border-radius: 3rpx; background: #FFF0C9; margin-right: 8rpx; }
.brand-title { font-size: 24rpx; font-weight: 600; color: #FFF3DF; letter-spacing: 1rpx; }
.brand-grid { position: relative; z-index: 2; display: flex; align-items: flex-end; margin-top: 14rpx; }
.brand-cell { flex: 1; display: flex; flex-direction: column; align-items: center; border-left: 2rpx solid rgba(255,255,255,.28); }
.brand-cell:first-child { border-left: none; }
.brand-value { font-size: 36rpx; line-height: 42rpx; font-weight: 800; color: #fff; letter-spacing: 1rpx; }
.brand-label { margin-top: 3rpx; font-size: 19rpx; color: rgba(255,255,255,.85); }
.brand-foot {
position: relative; z-index: 2; display: flex; align-items: center; justify-content: space-between;
margin-top: 14rpx; padding-top: 12rpx; border-top: 2rpx solid rgba(255,255,255,.22);
}
.brand-foot-txt { font-size: 19rpx; color: rgba(255,255,255,.90); }
.brand-seal { padding: 4rpx 12rpx; border-radius: 7rpx; background: #FFF4E3; color: #E8331B; font-size: 18rpx; font-weight: 700; }
.banner-slide.has-image .slide-title,
.banner-slide.has-image .slide-sub,
.banner-slide.has-image .brand-title,
.banner-slide.has-image .brand-value,
.banner-slide.has-image .brand-label,
.banner-slide.has-image .brand-foot-txt,
.banner-slide.has-image .slide-tag,
.banner-slide.has-image .slide-date { color: #fff; }
.banner-dots { display: flex; align-items: center; justify-content: center; margin-top: 10rpx; }
.banner-dot { width: 10rpx; height: 5rpx; border-radius: 2.5rpx; background: #FFD4B5; margin: 0 4rpx; transition: all .25s; }
.banner-dot.on { width: 24rpx; background: #F0710D; }
.foot-tip {
padding: 36rpx 0 10rpx;
text-align: center;
font-size: 20rpx;
color: #BDBDBD;
letter-spacing: 1rpx;
}
/* ---------- 自定义底部导航 ---------- */
.tabbar {
position: fixed;
left: 0;
right: 0;
bottom: 0;
z-index: 99;
height: 112rpx;
padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
display: flex;
background: #ffffff;
box-shadow: 0 -6rpx 24rpx rgba(0, 0, 0, 0.07);
}
.tab {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding-bottom: 8rpx;
}
.tab-txt {
margin-top: 8rpx;
font-size: 22rpx;
color: #9A9A9A;
}
.tab.on .tab-txt {
color: #F0710D;
font-weight: 600;
}
/* 底部图标 */
.tic {
position: relative;
width: 44rpx;
height: 40rpx;
}
.tic view {
position: absolute;
box-sizing: border-box;
}
.tic .ol {
border: 3rpx solid #9A9A9A;
border-radius: 3rpx;
}
.tic .ln {
background: #9A9A9A;
}
.tic .ct {
border-radius: 50%;
}
.tic .tr {
width: 0;
height: 0;
border-left: 11rpx solid transparent;
border-right: 11rpx solid transparent;
border-bottom: 11rpx solid #9A9A9A;
}
.tic .tr-d {
width: 0;
height: 0;
border-left: 7rpx solid transparent;
border-right: 7rpx solid transparent;
border-top: 7rpx solid #9A9A9A;
}
.tab.on .tic .ol {
border-color: #F0710D;
}
.tab.on .tic .ln {
background: #F0710D;
}
.tab.on .tic .tr {
border-bottom-color: #F0710D;
}
.tab.on .tic .tr-d {
border-top-color: #F0710D;
}
/* ---------- 中央上浮主按钮 ---------- */
.tab-center {
position: relative;
}
.fab {
position: absolute;
left: 50%;
top: -52rpx;
width: 128rpx;
height: 128rpx;
margin-left: -64rpx;
border-radius: 50%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background: linear-gradient(180deg, #FFB65E 0%, #EE7412 100%);
box-shadow: 0 10rpx 26rpx rgba(238, 116, 18, 0.45);
}
.fic {
position: relative;
width: 44rpx;
height: 36rpx;
}
.fic view {
position: absolute;
box-sizing: border-box;
}
.fo {
border: 3rpx solid #ffffff;
border-radius: 5rpx;
}
.fl {
background: #ffffff;
}
.fp {
background: #ffffff;
border-radius: 3rpx;
transform: rotate(-45deg);
}
.fab-txt {
margin-top: 4rpx;
font-size: 22rpx;
color: #ffffff;
}
</style>