mrr.sj.front/pages/jingxuan/brand-detail.vue

1104 lines
32 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="brand-detail-page">
<custom-navbar
:title="info.name || '品牌详情'"
:leftImg="'https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/60ae3383-6ff6-4f42-818b-d9b09a9bd0e0.png'"
:showUser="true"
backgroundColor="#ffffff"
titleColor="#333"
borderBottom="none"
:show-headle="true"
:headleSrc="isCollected ? iconCollectOn : iconCollectOff"
@onHeadleClick="toggleCollectStatus"
>
</custom-navbar>
<!-- 顶部导航栏扩展动态计算位置的拨号图标 -->
<view class="nav-phone-wrap" :style="{ top: phoneIconTop + 'px', right: phoneIconRight + 'px' }" @click="makeCall">
<image src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/c164280f-1ab9-41b4-9d60-846368364e9c.png" class="nav-phone-icon"></image>
</view>
<view class="mock-switch" @click="toggleMockData">
<text>{{ useMockData ? '关闭模拟数据' : '开启模拟数据' }}</text>
</view>
<view class="banner-section" v-if="bannerList && bannerList.length || info.video">
<!-- 原生视频组件 (就地播放) -->
<video
v-if="info.video && isVideoPlaying"
id="myVideo"
class="banner-video"
:src="info.video"
autoplay
controls
object-fit="cover"
></video>
<!-- 轮播图区域 (当视频播放时隐藏) -->
<swiper v-else class="banner-swiper" circular @change="onSwiperChange" :autoplay="!info.video">
<swiper-item v-for="(item, index) in bannerList" :key="index">
<image class="banner-img" :src="item" mode="aspectFill"></image>
</swiper-item>
</swiper>
<!-- 居中的视频播放大图标 (当视频播放时隐藏) -->
<image
v-if="info.video && !isVideoPlaying"
class="center-play-icon"
src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/bb720bb0-4ada-49f6-9ce5-38abfd613d5d.png"
@click="playVideoInPlace"
></image>
<!-- 视频/图片 相册切换组件 (当视频播放时隐藏) -->
<view class="album" v-if="(info.video || bannerList.length > 0) && !isVideoPlaying">
<view class="album-left" @click="info.video ? playVideoInPlace() : goAlbum()">
{{ info.video ? "视频" : "图片" }}
</view>
<view class="album-line"></view>
<view class="album-right" @click="goAlbum">
<view class="album-right-text">相册</view>
<image src="/static/images/icons/right7_13.png" class="album-right-img"></image>
</view>
</view>
<!-- 指示器 (当视频播放时隐藏) -->
<view class="indicator" v-if="!isVideoPlaying && bannerList.length > 0">
<text>{{ currentBannerIndex + 1 }} / {{ bannerList.length }}</text>
</view>
</view>
<view class="intro-section" v-if="info.name">
<view class="section-title">品牌介绍</view>
<view class="intro-content" :class="{ 'is-expanded': isExpanded }">
<text v-html="info.detail || '暂无品牌介绍'" class="intro-text"></text>
</view>
<view class="expand-btn" @click="toggleExpand" v-if="info.detail && info.detail.length > 100">
<text class="expand-text">{{ isExpanded ? '收起查看' : '展开查看' }}</text>
<view class="arrow" :class="{ 'up': isExpanded }"></view>
</view>
</view>
<block v-if="showTabSection">
<view class="divider-bg"></view>
<view class="tab-section">
<view class="tab-header">
<view
class="tab-item"
v-for="(tab, index) in availableTabs"
:key="tab.key"
:class="{ active: activeTab === index }"
@click="switchTab(index)"
>
<!-- 加上 class="tab-text" 以便控制层级 -->
<text class="tab-text">{{ tab.name }}</text>
<view v-if="activeTab === index" class="active-line"></view>
</view>
</view>
<view class="tab-content" v-if="availableTabs[activeTab]">
<rich-text :nodes="availableTabs[activeTab].content"></rich-text>
</view>
</view>
</block>
<view class="divider-bg"></view>
<view class="form-section">
<!-- 动态标题:当选中学员招募时显示"立即咨询",否则显示"在线留言" -->
<view class="section-title">{{ activeTabKey === 'student' ? '立即咨询' : '在线留言' }}</view>
<view class="form-list">
<view class="form-item">
<view class="label"><text class="required">*</text>姓名</view>
<input class="input-el" v-model="formData.contact_name" placeholder="请输入您的姓名" placeholder-class="ph-style" />
</view>
<view class="form-item">
<view class="label"><text class="required">*</text>手机号码</view>
<input class="input-el" type="number" maxlength="11" v-model="formData.contact_phone" placeholder="请输入您的手机号码" placeholder-class="ph-style" />
</view>
<view class="form-item">
<view class="label"><text class="required">*</text>所在城市</view>
<!-- 替换为复用的弹窗选择器 -->
<view class="picker-el" @click="chooseLocation">
<view class="picker-value" :class="{ 'has-val': cityNameText }">
{{ cityNameText || '请选择省、市/区' }}
<view class="arrow-right"></view>
</view>
</view>
</view>
<view class="form-item">
<view class="label"><text class="required">*</text>意向</view>
<picker class="picker-el" :range="intentionOptions" range-key="label" @change="onIntentionChange">
<view class="picker-value" :class="{ 'has-val': intentionText }">
{{ intentionText || '请选择您的意向' }}
<view class="arrow-right"></view>
</view>
</picker>
</view>
<view class="form-item border-none" v-if="formData.intention === 1">
<view class="label"><text class="required">*</text>其他意向</view>
<input class="input-el" v-model="formData.intention_other" placeholder="请填写您的其他意向" placeholder-class="ph-style" />
</view>
</view>
<button class="submit-btn" :class="{ 'is-active': canSubmit }" @click="submitForm">提交</button>
</view>
<!-- 所在地省市区选择复用弹窗 -->
<view class="popup" :class="{ show: showLocationPopupFlag }">
<view class="popup-mask" @click="hideLocationPopup"></view>
<view class="popup-content">
<view class="popup-header">
<text class="popup-title">选择所在地</text>
<text class="popup-close" @click="hideLocationPopup">×</text>
</view>
<view class="region-body">
<picker-view @change="bindChangeLocation"
class="picker-scroll"
:indicator-style="indicatorStyle"
:value="locationValue">
<picker-view-column>
<view class="picker-item" v-for="(item,index) in ChinaCitys" :key="index">{{item.province}}</view>
</picker-view-column>
<picker-view-column>
<view class="picker-item" v-for="(item,index) in locatinCitys" :key="index">{{item.city}}</view>
</picker-view-column>
<picker-view-column>
<view class="picker-item" v-for="(item,index) in locationAreas" :key="index">{{item.area}}</view>
</picker-view-column>
</picker-view>
</view>
<view class="popup-footer">
<view class="popup-btn cancel" @click="hideLocationPopup">
<text class="btn-text">取消</text>
</view>
<view class="popup-btn confirm" @click="confirmLocationSelect">
<text class="btn-text">确定</text>
</view>
</view>
</view>
</view>
<tipsPopup2 :show="tipShow" @closePopup="closePopup" sureText="确认" @okBtn="okBtn">
{{ tipsText }}
</tipsPopup2>
</view>
</template>
<script>
import request from "@/utils/request";
import CustomNavbar from "@/components/custom-navbar/custom-navbar.vue";
import tipsPopup2 from "@/components/tips-popup/tips-popup2";
import ChinaCitys from "@/static/data/ChinaCitys.json";
export default {
components: {
CustomNavbar,
tipsPopup2 // 注册弹窗组件
},
data() {
return {
// 模拟数据开关
useMockData: false,
brandId: null,
info: {},
bannerList: [],
// ====== 收藏和登录相关的状态 ======
isLogin: false, // 登录状态
tipShow: false, // 弹窗控制
tipsText: "", // 弹窗提示文本
tipType: "", // 弹窗类型判断
isCollected: false, // 是否已收藏
// 未收藏时的灰色星星
iconCollectOff: 'https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/561715a5-3de0-49f3-80e4-2662febec500.png',
// 已收藏时的粉色星星
iconCollectOn: 'https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/fe2011b0-a34d-46ee-911a-0ea6f099ad77.png',
// ==================================
isVideoPlaying: false, // 是否正在就地播放视频
phoneIconTop: 20, // 顶部电话图标初始Top
phoneIconRight: 50, // 顶部电话图标初始Right
currentBannerIndex: 0,
isExpanded: false,
activeTab: 0,
// 省市区选择器相关数据
showLocationPopupFlag: false,
indicatorStyle: 'height: 68rpx;',
ChinaCitys: [],
locatinCitys: [],
locationAreas: [],
locationValue: [0, 0, 0],
formData: {
id: null,
intention: '',
intention_other: '',
contact_name: '',
contact_phone: '',
province_code: '',
city_code: '',
area_code: ''
},
cityNameText: '',
intentionText: '',
intentionOptions: [
{ value: 2, label: '品牌加盟' },
{ value: 3, label: '购买产品' },
{ value: 4, label: '技能学习' },
{ value: 1, label: '其它意向' }
]
};
},
computed: {
// 获取当前选中的 tab 的 key用于判断标题
activeTabKey() {
return this.availableTabs[this.activeTab]?.key || '';
},
availableTabs() {
const tabs = [];
// 处理富文本图片和小程序样式丢失的绝佳方法
const formatRichText = (html) => {
if (!html) return '<div style="color:#999;font-size:13px;text-align:center;">暂无详细说明</div>';
let newHtml = html.replace(/<img[^>]*>/gi, function(match) {
match = match.replace(/style\s*=\s*["'][^"']*["']/gi, '');
return match.replace(/<img/gi, '<img style="max-width:100%; height:auto; display:block; border-radius:20rpx; margin:8px 0;"');
});
newHtml = newHtml.replace(/<p[^>]*>/gi, '<p style="margin-bottom: 0px; line-height: 1.6; word-break: break-all;">');
return newHtml;
};
if (this.info.franchise_state === 1) {
tabs.push({
key: 'franchise',
name: '加盟商招募',
content: formatRichText(this.info.franchise_recruit)
});
}
if (this.info.student_state === 1) {
tabs.push({
key: 'student',
name: '学员招募',
content: formatRichText(this.info.student_recruit)
});
}
return tabs;
},
showTabSection() {
return this.availableTabs.length > 0;
},
canSubmit() {
const d = this.formData;
const baseValid = d.contact_name && d.contact_phone && d.province_code && d.city_code && d.area_code && d.intention;
if (d.intention === 1) {
return baseValid && d.intention_other;
}
return baseValid;
}
},
onLoad(options) {
if (options && options.id) {
this.brandId = parseInt(options.id, 10);
this.formData.id = this.brandId;
}
// 初始化省市区数据
this.ChinaCitys = ChinaCitys;
if (this.ChinaCitys && this.ChinaCitys.length > 0) {
this.locatinCitys = this.ChinaCitys[0].citys;
this.locationAreas = this.locatinCitys[0].areas;
}
this.initNavLayout();
this.getBrandDetails();
},
onShow() {
// 每次进入页面时,根据 accessToken 刷新登录状态,就像 my-page 里面写的一样
this.isLogin = uni.getStorageSync("accessToken") ? true : false;
},
methods: {
// 弹窗组件相关方法
closePopup() {
this.tipShow = false;
},
okBtn() {
if (this.tipType == "login") {
uni.navigateTo({
url: "/pages/blogPopup/blogPopup", // 你们项目真实的登录页
});
}
this.tipShow = false; // 点击确认后关闭弹窗
},
// 点击右上角星星触发收藏/取消收藏
toggleCollectStatus() {
// 1. 调用你们自己的未登录弹窗逻辑
if (!this.isLogin) {
this.tipsText = "您现在还未登录,是否确定登录?";
this.tipShow = true;
this.tipType = "login";
return; // 拦截请求,不再往下执行
}
// 2. 逻辑判断
if (!this.brandId) return;
const targetState = this.isCollected ? 2 : 1;
// 3. 模拟环境 (Mock)
if (this.useMockData) {
this.isCollected = !this.isCollected;
uni.showToast({ title: this.isCollected ? '收藏成功(Mock)' : '已取消收藏(Mock)', icon: 'none' });
return;
}
// 4. 发送真实请求
uni.showLoading({ title: '处理中...', mask: true });
request.post("/sj/brand/collect", {
id: this.brandId,
state: targetState
}).then((res) => {
if (res.code == 200) {
this.isCollected = !this.isCollected;
uni.showToast({
title: this.isCollected ? '收藏成功' : '已取消收藏',
icon: 'none'
});
} else {
uni.showToast({ title: res.msg || '操作失败', icon: 'none' });
}
}).catch((err) => {
console.error('收藏接口异常', err);
// 既然拦截成功了,如果这里还是报错,那才是真正的网络不顺畅
uni.showToast({ title: '网络不太顺畅,请稍后再试', icon: 'none' });
}).finally(() => {
uni.hideLoading();
});
},
// 地区选择器相关方法
chooseLocation() {
this.showLocationPopupFlag = true;
},
hideLocationPopup() {
this.showLocationPopupFlag = false;
},
bindChangeLocation(e) {
this.locationValue = e.detail.value;
let index1 = e.detail.value[0] || 0;
let index2 = e.detail.value[1] || 0;
this.locatinCitys = this.ChinaCitys[index1].citys;
this.locationAreas = this.locatinCitys[index2].areas;
},
confirmLocationSelect() {
let valueArr = this.locationValue;
let index1 = valueArr[0] || 0;
let index2 = valueArr[1] || 0;
let index3 = valueArr[2] || 0;
let province = this.ChinaCitys[index1].province;
let city = this.locatinCitys[index2].city;
let area = this.locationAreas[index3].area;
// 更新展示文本及表单数据
this.cityNameText = `${province} / ${city} / ${area}`;
this.formData.province_code = province;
this.formData.city_code = city;
this.formData.area_code = area;
this.showLocationPopupFlag = false;
},
initNavLayout() {
let sysInfo = uni.getSystemInfoSync();
// NavBar 通常高度是 44px悬浮容器设置为 40px 高,居中 Y 为 +2px
let top = (sysInfo.statusBarHeight || 0) + 2;
// 默认距离右侧边距
let right = 50;
// #ifdef MP-WEIXIN
let menuBtn = uni.getMenuButtonBoundingClientRect();
if (menuBtn) {
top = menuBtn.top + (menuBtn.height / 2) - 20;
// 算出胶囊左侧的可用空间,再留出右侧搜索图标的距离(通常再向左偏移 45px 左右)
right = sysInfo.windowWidth - menuBtn.left + 45;
}
// #endif
this.phoneIconTop = top;
this.phoneIconRight = right;
},
// 拨打电话面板
makeCall() {
// 兼容可能存在的多种手机号字段
const phone = this.info.contact_phone || '';
if (phone) {
uni.makePhoneCall({
phoneNumber: String(phone),
fail: (err) => {
console.log('取消拨打或拨打失败', err);
}
});
} else {
uni.showToast({ title: '暂无联系电话', icon: 'none' });
}
},
goAlbum() {
uni.navigateTo({
url: `/pages/album/index?id=${this.brandId}&user_type=2`
});
},
// 就地播放视频
playVideoInPlace() {
this.isVideoPlaying = true;
},
// 悬浮按钮切换 Mock 数据
toggleMockData() {
this.useMockData = !this.useMockData;
uni.showToast({ title: this.useMockData ? 'Mock已开启' : 'Mock已关闭', icon: 'none' });
this.getBrandDetails();
},
getBrandDetails() {
this.isVideoPlaying = false; // 每次刷新数据重置播放状态
if (this.useMockData) {
this.injectMockData();
return;
}
uni.showLoading({ title: '加载中...' });
request.post("/sj/brand/details", {
id: this.brandId
}).then((res) => {
if (res.code == 200 && res.data) {
this.info = res.data;
// 初始化收藏状态 (假设后端返回 1 为已收藏)
this.isCollected = res.data.collect_state === 1;
this.parseBanners();
} else {
uni.showToast({ title: res.msg || '获取详情失败', icon: 'none' });
}
}).catch((err) => {
console.error('获取品牌详情异常', err);
}).finally(() => {
uni.hideLoading();
});
},
injectMockData() {
this.info = {
name: '蜜丝卡伦美甲美睫',
contact_phone: '400-888-8888',
video: 'https://vjs.zencdn.net/v/oceans.mp4',
banner_imgs: [
'https://images.unsplash.com/photo-1610992015732-2449b76344bc?ixlib=rb-4.0.3&auto=format&fit=crop&w=750&q=80',
'https://images.unsplash.com/photo-1522337660859-02fbefca4702?ixlib=rb-4.0.3&auto=format&fit=crop&w=750&q=80'
],
detail: '内容支持文本/图片上传(后台动态配置)——右侧支持图文/上传图片后转轮播。品牌优势、产品详情、常见问题、发展历程、品牌案例展示区等位。\n\n本品牌深耕行业多年秉持匠心初心不断为行业核心发展赋能专注打造高品质、高性价比、契合大众需求的优质产品。\n品牌经验和可复制性开店闭环模型从培训、选址、开店扶持到后期运营展现老牌一站式辅导并提供职业赋能成就门店长效盈利、持久发展的秘诀。\n强大的自有生产供应链与专业团队研发高端前沿技术保证出新频率与前沿审美满足各年龄段、覆盖不同场景下的多元使用诉求。\n自创立以来品牌以真诚口碑立足市场坚守诚信经营初心。积极致力于打造消费者、加盟商和社会大众三方信任的美业服务管家获得广大用户信赖与认可。\n未来我们将持续坚守初心优质服务升级以更高标准打造全线产品致力成为行业内极具竞争力与用户信赖的国民品牌为广大用户传递更自信、更优质的变美体验。\n自创立以来品牌以真诚口碑立足市场坚守诚信经营初心用心服务每一位消费者凭借稳定出众的产品实力与贴心完善的售后保障收获万千用户信赖与认可。自创立以来品牌以真诚口碑立足市场坚守诚信经营初心用心服务每一位消费者凭借稳定出众的产品实力与贴心完善的销售后保障收获万千用户信赖与认可。',
franchise_state: 1,
franchise_recruit: `
<p>内容支持文本/图片上传(由后台动态化配置)——支持右侧上传视频/上传图片转轮播。品牌优势、产品详情、常见问题、发展历程、品牌案例/展示区等位。</p>
<img src="https://images.unsplash.com/photo-1604654894610-df63bc536371?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&q=80" />
`,
student_state: 1,
student_recruit: `<p>学员招募介绍区域...</p>`
};
this.parseBanners();
},
parseBanners() {
if (!this.info.banner_imgs) {
this.bannerList = this.info.cover_img ? [this.info.cover_img] : ['https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/6744da30-1cef-40ec-8274-f6395b530fe8.png'];
return;
}
try {
if (typeof this.info.banner_imgs === 'string') {
if (this.info.banner_imgs.includes(',')) {
this.bannerList = this.info.banner_imgs.split(',');
} else {
this.bannerList = JSON.parse(this.info.banner_imgs);
}
} else if (Array.isArray(this.info.banner_imgs)) {
this.bannerList = this.info.banner_imgs;
}
} catch (e) {
this.bannerList = [this.info.cover_img];
}
},
onSwiperChange(e) {
this.currentBannerIndex = e.detail.current;
},
toggleExpand() {
this.isExpanded = !this.isExpanded;
},
switchTab(index) {
this.activeTab = index;
},
onIntentionChange(e) {
const selectItem = this.intentionOptions[e.detail.value];
this.intentionText = selectItem.label;
this.formData.intention = selectItem.value;
},
submitForm() {
if (!this.canSubmit) {
uni.showToast({ title: '请填写完整必填信息', icon: 'none' });
return;
}
if (!/^1[3-9]\d{9}$/.test(this.formData.contact_phone)) {
uni.showToast({ title: '请输入合法的手机号码', icon: 'none' });
return;
}
if (this.useMockData) {
uni.showToast({ title: '模拟环境下留言提交成功!', icon: 'success' });
this.resetForm();
return;
}
uni.showLoading({ title: '正在提交...', mask: true });
request.post("/sj/brand/consult", this.formData).then((res) => {
if (res.code == 200) {
uni.showToast({ title: '留言提交成功!', icon: 'success', duration: 2000 });
this.resetForm();
} else {
uni.showToast({ title: res.msg || '提交失败', icon: 'none' });
}
}).catch((err) => {
uni.showToast({ title: '网络不太顺畅,请稍后再试', icon: 'none' });
}).finally(() => {
uni.hideLoading();
});
},
resetForm() {
this.formData = {
id: this.brandId,
intention: '',
intention_other: '',
contact_name: '',
contact_phone: '',
province_code: '',
city_code: '',
area_code: ''
};
this.cityNameText = '';
this.intentionText = '';
}
}
};
</script>
<style lang="scss" scoped>
.brand-detail-page {
background-color: #ffffff;
min-height: 100vh;
padding-bottom: 80rpx;
box-sizing: border-box;
position: relative;
}
/* 导航栏左侧新增动态拨号图标 */
.nav-phone-wrap {
position: fixed;
z-index: 9999;
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
transition: opacity 0.2s;
&:active {
opacity: 0.7;
}
.nav-phone-icon {
width: 44rpx;
height: 44rpx;
}
}
/* 模拟数据悬浮按钮 */
.mock-switch {
position: fixed;
right: 20rpx;
top: 200rpx;
background: #E5505F;
color: #fff;
font-size: 24rpx;
padding: 10rpx 20rpx;
border-radius: 30rpx;
z-index: 999;
box-shadow: 0 4rpx 10rpx rgba(229, 80, 95, 0.4);
}
/* 1. 轮播图 / 视频 */
.banner-section {
position: relative;
width: 100%;
height: 421rpx;
background-color: #f5f5f5;
.banner-video {
width: 100%;
height: 100%;
display: block;
}
.banner-swiper {
width: 100%;
height: 100%;
}
.banner-img {
width: 100%;
height: 100%;
}
.center-play-icon {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 120rpx;
height: 120rpx;
z-index: 20;
transition: opacity 0.3s;
&:active {
opacity: 0.8;
}
}
.album {
position: absolute;
bottom: 10rpx;
left: 20rpx;
height: 44rpx;
width: 184rpx;
background: rgba(0, 0, 0, 0.4);
border-radius: 23rpx;
display: flex;
align-items: center;
z-index: 10;
.album-left {
width: 73rpx;
height: 44rpx;
background: #FFFFFF;
border-radius: 23rpx;
display: flex;
align-items: center;
justify-content: center;
font-weight: 400;
font-size: 22rpx;
color: #333333;
}
.album-line {
width: 2rpx;
height: 20rpx;
background: #A09C9E;
border-radius: 1rpx;
margin: 0 18rpx;
}
.album-right {
display: flex;
align-items: center;
height: 44rpx;
.album-right-text {
font-weight: 400;
font-size: 22rpx;
color: #FFFFFF;
}
.album-right-img {
width: 8rpx;
height: 16rpx;
margin-left: 8rpx;
}
}
}
.indicator {
position: absolute;
right: 20rpx;
bottom: 10rpx;
background: #0000003b;
color: #ffffff;
font-size: 22rpx;
font-weight: 500;
padding: 8rpx 24rpx;
border-radius: 26rpx;
display: flex;
justify-content: center;
align-items: center;
}
}
/* 2. 品牌介绍 */
.intro-section {
padding: 40rpx 30rpx;
.section-title {
font-weight: 500;
font-size: 30rpx;
color: #333333;
line-height: 42rpx;
margin-bottom: 24rpx;
}
.intro-content {
font-weight: 400;
font-size: 26rpx;
color: #333333;
line-height: 37rpx;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 19;
overflow: hidden;
white-space: pre-line;
&.is-expanded {
-webkit-line-clamp: 9999;
}
}
.expand-btn {
display: flex;
justify-content: center;
align-items: center;
margin-top: 30rpx;
padding: 10rpx 40rpx;
border-radius: 27rpx;
border: 1rpx solid #C4C4C4;
width: fit-content;
margin-left: auto;
margin-right: auto;
.expand-text {
font-weight: 400;
font-size: 24rpx;
color: #333333;
line-height: 33rpx;
margin-right: 8rpx;
}
.arrow {
margin-bottom: -2rpx;
width: 10rpx;
height: 10rpx;
border-right: 2rpx solid #999999;
border-bottom: 2rpx solid #999999;
transform: rotate(45deg) translateY(-2rpx);
transition: transform 0.2s ease;
&.up {
transform: rotate(-135deg) translateY(2rpx);
}
}
}
}
.divider-bg {
width: 100%;
height: 16rpx;
background-color: #F8F8F8;
}
/* 3. 招募详情 Tab */
.tab-section {
padding: 0 30rpx 40rpx;
.tab-header {
display: flex;
justify-content: center;
align-items: center;
padding: 30rpx 0 0 0;
gap: 120rpx;
margin-bottom: 10rpx;
.tab-item {
font-weight: 400;
font-size: 28rpx;
color: #666666;
line-height: 40rpx;
position: relative;
padding-bottom: 16rpx;
transition: all 0.2s;
&.active {
font-weight: 500;
font-size: 30rpx;
color: #000000;
}
.tab-text {
position: relative;
z-index: 2;
}
.active-line {
position: absolute;
bottom: 20rpx;
left: 50%;
transform: translateX(-50%);
width: 100%;
height: 14rpx;
background: url('https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/6ea51769-911f-44e7-ae22-55f62e8bfdb9.png') no-repeat center center;
background-size: 100% 100%;
border-radius: 4rpx;
z-index: 1;
}
}
}
.tab-content {
font-weight: 400;
font-size: 26rpx;
color: #333333;
line-height: 37rpx;
::v-deep img {
max-width: 100% !important;
height: auto !important;
display: block;
border-radius: 12rpx;
margin: 20rpx 0;
}
::v-deep p, ::v-deep div {
margin-bottom: 24rpx;
line-height: 1.6;
}
}
}
/* 4. 在线留言表单 */
.form-section {
padding: 40rpx 30rpx;
.section-title {
font-weight: 500;
font-size: 30rpx;
color: #333333;
line-height: 42rpx;
margin-bottom: 20rpx;
}
.form-list {
.form-item {
display: flex;
align-items: center;
min-height: 104rpx;
border-bottom: 1rpx solid #F5F5F5;
&.border-none {
border-bottom: none;
}
.label {
width: 190rpx;
font-size: 28rpx;
color: #333333;
line-height: 40rpx;
.required {
color: #E5505F;
margin-right: 3rpx;
}
}
.input-el, .picker-el {
flex: 1;
font-size: 28rpx;
color: #333333;
}
.ph-style {
color: #C4C4C4;
}
.picker-value {
color: #CCCCCC;
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
&.has-val {
color: #333333;
}
.arrow-right {
width: 12rpx;
height: 12rpx;
border-top: 2rpx solid #CCCCCC;
border-right: 2rpx solid #CCCCCC;
transform: rotate(45deg);
}
}
}
}
.submit-btn {
margin-top: 60rpx;
width: 100%;
height: 88rpx;
line-height: 88rpx;
background-color: #DBDBDB;
color: #fff;
font-size: 30rpx;
border-radius: 44rpx;
text-align: center;
border: none;
transition: all 0.2s ease;
&::after {
border: none;
}
&.is-active {
background-color: #FF4767;
color: #FFFFFF;
box-shadow: 0 8rpx 16rpx rgba(229, 80, 95, 0.15);
}
}
}
/* ================= 地区选择弹窗复用样式 ================= */
.popup {
position: fixed;
left: 0;
right: 0;
bottom: 0;
z-index: 9999; /* 层级调高,防止被挡住 */
visibility: hidden;
opacity: 0;
transition: all 0.3s ease;
}
.popup.show {
visibility: visible;
opacity: 1;
}
.popup-mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
}
.popup-content {
position: relative;
background-color: #FFFFFF;
border-radius: 24rpx 24rpx 0 0;
padding: 32rpx;
transform: translateY(100%);
transition: transform 0.3s ease;
}
.popup.show .popup-content {
transform: translateY(0);
}
.popup-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 20rpx;
}
.popup-title {
font-size: 32rpx;
color: #333333;
font-weight: 500;
}
.popup-close {
font-size: 40rpx;
color: #999999;
padding: 16rpx;
}
.region-body {
flex: 1;
overflow: hidden;
}
.picker-scroll {
height: 400rpx;
}
.picker-item {
height: 68rpx;
line-height: 68rpx;
text-align: center;
font-size: 28rpx;
color: #333;
}
.popup-footer {
display: flex;
margin-top: 32rpx;
}
.popup-btn {
flex: 1;
height: 88rpx;
border-radius: 44rpx;
display: flex;
align-items: center;
justify-content: center;
margin: 0 16rpx;
}
.popup-btn.cancel {
background-color: #F5F5F5;
}
.popup-btn.confirm {
background: linear-gradient(90deg, #ff6d77 0%, #FF618F 100%);
}
.popup-btn .btn-text {
font-size: 32rpx;
font-weight: 500;
}
.popup-btn.cancel .btn-text {
color: #666666;
}
.popup-btn.confirm .btn-text {
color: #FFFFFF;
}
</style>