需求:点击Tab实现自动定位到对应的内容高度,页面滚动时候,tab自定切换,自动吸顶功能,废话不多说,直接上代码。
<template>
<view class="full-container">
<u-navbar></u-navbar>
<view class="swiper-box hight-swiper-box">
<u-swiper @click="onSwiper" :list="detailContent.miediaList" name="mediaPath" border-radius="0" mode="rect" indicator-pos="bottomRight" img-mode="aspectFill"></u-swiper>
</view>
<view class="main-box">
<view class="scienc-info">
<view class="desc ellipsis">{{ detailContent.routeName }}</view>
<view class="tip two-ellipsis">{{ detailContent.description }}</view>
<view class="extension-vontainer" @click="getExtension">
<view class="flex">
<image class="notice-icon" :src="$IMG + '/images/notice-icon.png'"></image>
<view class="common-tip puoduct">推广计划产品</view>
<view class="common-tip plan">本产品已参与推广计划</view>
</view>
<image class="two-right-icon" :src="$IMG + '/images/two-right-icon.png'"></image>
</view>
<view class="price-box">
<view class="money">
<view class="hot-flex">
<view class="symbol">¥</view>
<view class="money">{{ detailContent.price }}</view>
<view class="text">起/人</view>
</view>
</view>
<view class="person" v-if="detailContent.joinNumber > 0">{{ detailContent.joinNumber }}人参加</view>
</view>
<view class="operate-box">
<view class="flex">
<view class="flex">
<image class="icon-img" src="/static/images/adress-icon.png"></image>
<view class="text">{{ detailContent.destination }}</view>
</view>
<view class="flex">
<image class="icon-img" src="/static/images/time-icon.png"></image>
<view class="text">{{ detailContent.days }}天</view>
</view>
<view class="flex">
<image class="icon-img" src="/static/images/person-icon.png"></image>
<view class="text">{{ detailContent.groupNumber }} 人</view>
</view>
</view>
<view class="flex" @click="getShowShare">
<image class="icon-img share-icon" src="/static/images/share.png"></image>
<view class="text">分享</view>
</view>
</view>
</view>
<!-- <view class="package-info common-box">
<view class="nav-tit">
<text class="tit">套餐信息</text>
<text class="desc">(进入拼团后再选择)</text>
</view>
<view class="flex-container">
<view class="package-list" v-for="(item, index) in detailContent.packageList" :key="index">
<text class="name" v-if="!item.tip">{{ item.name }}:¥{{ item.price }}</text>
<text class="name" v-else>{{ item.name }}({{ item.tip }}):¥{{ item.price }}</text>
</view>
</view>
</view> -->
<view class="set-out-time common-box">
<view class="nav-tit">出发日期</view>
<ticketInfo v-if="showStatus" :dateVoList="dateVoList" :classSyle="classSyle" :pageType="pageType" @getSelectPackage="getSelectPackage"></ticketInfo>
</view>
<view class="aggregate-info common-box">
<view class="nav-tit">集合信息</view>
<view class="list-item" v-for="(item, index) in detailContent.rendezvousList" :key="index">
</block>
<view class="default-icon">
<image class="icon-img" src="/static/images/default-icon.png"></image>
<text class="icon-index">{{ index + 1}}</text>
</view>
<text class="text-desc">{{ item }}</text>
</view>
</view>
<view v-if="detailContent.joinNumber > 0" class="person-info common-box">
<view class="nav-tit flex" @click="getMorePerson">
<view class="left">已参加的人</view>
<view class="flex">
<view class="number">{{ detailContent.joinNumber }}</view>
<view class="person">人</view>
<image class="more-arr-img" src="/static/images/more-right.png"></image>
</view>
</view>
<view class="recommend-scroll">
<scroll-view class="wrap-scroller" scroll-x="true">
<view class="list-item" v-for="(item, index) in detailContent.personVos" :key="index">
<image class="avatar-z" :src="item.avatar" mode="aspectFill"></image>
<view class="name"> {{ item.customerName }} </view>
</view>
</scroll-view>
</view>
</view>
<view class="bottom-operate">
<view class="flex">
<view class="ico-box home-icon" @click.stop="getHome">
<image class="icon-img" src="/static/images/detail-home.png"></image>
<view class="text">首页</view>
</view>
<view class="ico-box">
<button open-type="contact" hover-class='none' class='item tui-skeleton-rect' >
<image class="icon-img" src="/static/images/customer-service.png"></image>
</button>
<view class="text">客服</view>
</view>
</view>
<view class="btn-customer-bg" @click="getJoinGroup">加入该团</view>
</view>
<lf-popup v-model="showPersonPopup" class="customer-popup">
<div class="titile-box" @click="onClose">
<view></view>
<view class="popup-tit">人员名单</view>
<image src="/static/images/login-close.png"></image>
</div>
<view class="sign-up">已报名</view>
<view class="list-box">
<view class="list-item" v-for="(item, index) in detailContent.personVos" :key="index">
<image class="avatar-z" :src="item.avatar" mode="aspectFill"></image>
<view class="name"> {{ item.customerName }} </view>
</view>
</view>
<view class="bottom">只显示部分报名用户信息</view>
</lf-popup>
<!-- 分享选择弹窗 -->
<lf-popup v-model="showShare" height="240rpx">
<view class="share-wrap" @click="closeShare">
<!-- #ifdef MP-WEIXIN -->
<button open-type="share" class="share-item u-reset-button">
<image style="width: 86rpx; height: 86rpx; margin-top: 1rpx;" src="/static/wechat.png"></image>
<text>微信好友</text>
</button>
<!-- #endif -->
<!-- #ifdef H5 -->
<view class="share-item2" @click="copyPageUrl">
<image src="/static/images/share.png"></image>
<text>分享链接</text>
</view>
<!-- #endif -->
<!-- #ifdef H5 || MP-WEIXIN -->
<view class="share-item" @click="shareCanvas" style="margin-top: -7rpx;">
<image src="/static/images/share2.png"></image>
<text style="margin-top: 8rpx;">分享海报</text>
</view>
<!-- #endif -->
</view>
</lf-popup>
<toast v-if="loadingFlag" color="#fff" type="rotate3"></toast>
</view>
<view class="tab-content">
<Anchor
:tabList="tabs"
:barFixed="true"
:transitionShow="true"
barHeight="50"
:barTop="barTop"
barId="0"
@clickTabs="clickTabs"
ref="barTabNav"
>
<!-- id与数据tabs->navTarget对应 -->
<view id="item1">
<view class="box">
<mp-html :content="detailContent.routeExtendList[0].details" selectable="true" />
</view>
</view>
<view id="item2">
<view class="box">
<mp-html :content="detailContent.routeExtendList[1].details" selectable="true" />
</view>
</view>
<view id="item3">
<view class="box">
<mp-html :content="detailContent.routeExtendList[2].details" selectable="true" />
</view>
</view>
<view id="item4">
<view class="box">
<mp-html :content="detailContent.routeExtendList[3].details" selectable="true" />
</view>
<!-- 热门推荐-->
<group-buying ref="groupBuying" :pageType="pageType" :tittle="groupTittle" :classStyle="classStyle"></group-buying>
</view>
</Anchor>
</view>
<lf-back-top :show-tag="showTag"></lf-back-top>
<!-- 生成海报组件 -->
<poster v-if="showCanvas" ref="posterRef" :configData="configData" @close="closePopup"/>
<phone-login ref="phoneLogin"></phone-login>
<extension-popup ref="extensionPopup" @getShare="getShare"></extension-popup>
</view>
</template>
<script>
import poster from "./poster.vue";
import Anchor from './anchor.vue'
import extensionPopup from './components/extension-popup.vue'
import ticketInfo from '@/subpages/content/components/ticketInfo/index.vue'
export default {
components:{
poster,
Anchor,
ticketInfo,
extensionPopup
},
data() {
return {
tabs: [
{
text: "详情亮点",//名称
navTarget: "#item1",//锚点
value: 'one'
},
{
text: "行程准备",
navTarget: "#item2",
value: 'two'
},
{
text: "费用须知",
navTarget: "#item3",
value: 'three'
},
{
text: "注意事项",
navTarget: "#item4",
value: 'four'
}
],
$IMG: this.$IMG,
configData: {},
posterUrl: '',
showTag: false,
current: 'one',
show: false,
show: true,
tabCount: '',
dateVoList: [],
goTimeOpenId: '',
showShare: false,
barTop: 0,
postId: 0,
page: 1,
form: {
pid: 0,
type: 1,
toUid: '',
toCid: 0,
postId: '',
content: ''
},
showCanvas: false,
showStatus: false,
classSyle: 'buy-ticket',
postDetail: {},
commentList: [],
groupTittle: '热门推荐',
postingUser: {},
pageType: 'buyTicket',
loadingFlag: true,
detailContent: {
days: 0,
price: 0,
groupNumber: 0,
routeName: '',
joinNumber: 0,
description: '',
destination: ''
},
classStyle: 'buy-ticket',
tabList: [
{ name: '详情亮点' , id: '0' },
{ name: '行程准备' , id: '1' },
{ name: '费用须知' , id: '2' },
{ name: '注意事项' , id: '3' }
],
clickFlag: false,
myScroll: 0,
currentItem: 0,
currentTabs: 0,
showPersonPopup: false,
shareCover: '',
}
},
onShareAppMessage(res) {
let imgURL = this.shareCover;
if (this.detailContent.miediaList.length > 0) {
imgURL = this.detailContent.miediaList[0].mediaPath
}
let customerPushCode = uni.getStorageSync("userInfo").pushCode || ''
return {
title: this.detailContent.routeName ? this.detailContent.routeName : this.$c.miniappName,
path: `/subpages/content/buyTicket/index?routeId=${this.postId}&pushCode=${customerPushCode}`,
imageUrl: imgURL
};
},
onShareTimeline(res) {
let imgURL = this.shareCover;
if (this.detailContent.miediaList.length > 0) {
imgURL = this.detailContent.miediaList[0].mediaPath
}
let customerPushCode = uni.getStorageSync("userInfo").pushCode || ''
return {
title: this.detailContent.routeName ? this.detailContent.routeName : this.$c.miniappName,
query: `routeId=${this.postId}&pushCode=${customerPushCode}`,
imageUrl: imgURL
};
},
onShow() {
uni.removeStorageSync('getDetailFlag')
},
onLoad(options) {
if ('q' in options) {
const q = decodeURIComponent(options.q);
const querys = q
.split('?')[1]
.split('&')
.reduce((acc, it) => {
let r = it.split(/=/);
return Object.assign(acc, {
[r[0]]: r[1]
})
}, {});
if ('routeId' in querys) {
this.postId = querys.routeId.trim();
}
}else{
this.postId = options.routeId.trim();
}
this.form.postId = this.postId;
this.loadingFlag = true
this.$loading(true)
this.getInit()
this.getSysInfo();
// this.getCommentList()
uni.setStorageSync('clickTabFlag', false)
uni.setStorageSync('scrollBtttom', false)
uni.setStorageSync('clickTabIndex', '0')
uni.getSystemInfo({
success: (res) => {
this.barTop = (res.statusBarHeight) + (res.system.indexOf('iOS') > -1 ? 44 : 48)
}
})
this.postingUser = uni.getStorageSync('userInfo')
//#ifdef MP-WEIXIN
wx.showShareMenu({
withShareTicket: true,
menus: ['shareAppMessage', 'shareTimeline']
});
//#endif
},
// 1. 点击并且滚动页面,采用点击切换Tab
onPageScroll(e) {
this.showTag = e.scrollTop > 750
uni.setStorageSync('scrollPageFlag', true)
this.$refs.barTabNav.getSelectedTab(e.scrollTop, 'scrollPage');
},
onReachBottom() {
uni.setStorageSync('scrollBtttom', true)
this.$refs.groupBuying.getReachBottom()
this.$refs.barTabNav.getSelectedTab();
},
onPullDownRefresh() {
setTimeout(() => {
uni.stopPullDownRefresh();
}, 500)
},
methods: {
getSysInfo() {
this.$H.get('system/basic').then(res => {
this.shareCover = res.shareImg;
});
},
// 获取二维码路径参数
GetWxMiniProgramUrlParam(url) {
let theRequest = {};
if (url.indexOf("#") != -1) {
const str = url.split("#")[1];
const strs = str.split("&");
for (let i = 0; i < strs.length; i++) {
theRequest[strs[i].split("=")[0]] = decodeURI(strs[i].split("=")[1]);
}
} else if (url.indexOf("?") != -1) {
const str = url.split("?")[1];
const strs = str.split("&");
for (let i = 0; i < strs.length; i++) {
theRequest[strs[i].split("=")[0]] = decodeURI(strs[i].split("=")[1]);
}
}
return theRequest;
},
onSwiper(index) {
let arr = []
this.detailContent.miediaList.forEach((item) => {
arr.push(item.mediaPath)
})
let currnetImg = this.detailContent.miediaList[index].mediaPath
uni.previewImage({
urls: arr, //预览图片列表
current: currnetImg //图片路径
})
},
clickTabs(row, status) {
console.log(row, '当前点击row')
// 点击Tab标识
this.clickFlag = status
// 避免重复点击
if(this.current == row.value) return false;
// 操作处理
this.current = row.value
},
itemChange(id) {
this.currentItem = id;
},
getInit() {
this.$H.get('/business/app/tripTourRoute/info/' + this.postId).then(res => {
if (res.code == 0) {
this.detailContent = res.result
if(res.result.personVos !== null) {
if (res.result.personVos.length > 0) {
res.result.personVos.forEach((item) => {
item.customerName = item.nickname.slice(0, 1) + '**'
})
}
}
this.dateVoList = res.result.goDateVoList
this.tabCount = this.detailContent.routeExtendList[0].details
this.showStatus = true
this.loadingFlag = false
}
})
},
tabChange(index) {
this.currentTabs = index;
this.tabCount = this.detailContent.routeExtendList[index].details
},
getMorePerson() {
this.showPersonPopup = true
},
onClose() {
this.showPersonPopup = false
},
getHome() {
uni.reLaunch({
url: '/pages/index/index'
})
},
getSelectPackage(data) {
this.goTimeOpenId = data.openId
// 清除出行人信息
uni.setStorageSync('travelersAddFlag', false)
uni.setStorageSync('finallTravelersUserIds', [])
uni.setStorageSync('defaultArrData', [])
uni.setStorageSync('OriginExistenceIds', [])
},
getJoinGroup() {
if (!uni.getStorageSync('hasLogin')) {
setTimeout(() => {
uni.showModal({
title: '提示',
content: '您还未登录,登录体验更多精彩',
confirmText: '去登录',
cancelText: '随便逛逛',
success: (res) => {
if (res.confirm) {
this.$refs.phoneLogin.getShow()
} else if (res.cancel) {
console.log('用户点击取消');
}
}
});
}, 500)
} else {
if (this.goTimeOpenId) {
uni.setStorageSync('currentOpenId', this.goTimeOpenId)
uni.setStorageSync('resortArr', this.detailContent.rendezvousList)
uni.navigateTo({
url: `/subpages/content/joinGroup/index?openId=${this.goTimeOpenId}`
})
} else {
this.$u.toast('请选择出发日期');
}
}
},
getShare() {
this.showShare = true
},
getShowShare() {
if (!uni.getStorageSync('hasLogin')) {
setTimeout(() => {
uni.showModal({
title: '提示',
content: '您还未登录,登录体验更多精彩',
confirmText: '去登录',
cancelText: '随便逛逛',
success: (res) => {
if (res.confirm) {
this.$refs.phoneLogin.getShow()
} else if (res.cancel) {
console.log('用户点击取消');
}
}
});
}, 500)
} else {
this.showShare = true
}
},
closeShare() {
this.showShare = false
},
getReplyMore(index) {
this.commentList[index].copyChildren = this.commentList[index].children
this.commentList[index].openReplyStatus = false
},
// 回复评论
onReply(e) {
this.placeholder = '回复:' + e.userInfo.username;
this.focus = true;
// toCid: 如果是一级评论传0,如果是针对某一条评论进行回复,传当前评论的id
this.form.toCid = e.id ? e.id : 0
// pid: 如果是针对帖子评论传0,如果是针对某一条评论进行回复,传当前评论的id
this.form.pid = e.pid ? e.pid : e.id
this.form.toUid = e.userInfo.uid;
this.form.postId = this.postId;
this.focusFlag = true
},
jumpUser(uid) {
uni.navigateTo({
url: '/subpages/childrenPackge/user/home?uid=' + uid
});
},
getCommentList() {
this.$H.get('comment/list', {
postId: '1814259461133684738',
page: this.page
}).then(res => {
if (res.code ==0) {
res.result.data.forEach((item) => {
if (item.children.length > 1) {
// item.openReplyStatus = true
// item.copyChildren = item.children.slice(0, 1)
} else {
// item.openReplyStatus = false
// item.copyChildren = item.children
}
})
this.commentList = this.commentList.concat(res.result.data);
}
})
},
getExtension() {
if (!uni.getStorageSync('hasLogin')) {
setTimeout(() => {
uni.showModal({
title: '提示',
content: '您还未登录,登录体验更多精彩',
confirmText: '去登录',
cancelText: '随便逛逛',
success: (res) => {
if (res.confirm) {
this.$refs.phoneLogin.getShow()
} else if (res.cancel) {
console.log('用户点击取消');
}
}
});
}, 500)
} else {
// 1是推广人
const userInfo = uni.getStorageSync('userInfo');
if (userInfo.isPromoter) {
if (userInfo.isPromoter === 0) {
uni.navigateTo({
url: '/subpages/content/promotion/illustrate'
})
} else {
this.$refs.extensionPopup.getShow(this.postId);
}
} else {
uni.navigateTo({
url: '/subpages/content/promotion/illustrate'
})
}
}
},
handleInput() {
if (this.form.content == '') {
this.$u.toast('评论不能为空');
return;
}
if (val.detail.cursor == 50) {
this.$u.toast('内容字数最多为50个字符');
return;
}
uni.showLoading({
mask: true,
title: '发布中'
});
this.$H.post('post/addComment', this.form).then(res => {
uni.hideLoading();
if (res.code == 0) {
if(res.check){
uni.showModal({
title: '提示',
content: '评论审核通过后发布哦,请耐心等待',
showCancel: false,
success: function (res) {
if (res.confirm) {
}
}
});
}else{
this.$u.toast('评论成功');
this.postDetail.commentCount+=1
}
this.form.content = '';
this.page = 1;
this.commentList = [];
this.form.pid = 0;
// this.getCommentList();
}else if(res.code==500){
this.$u.toast(res.msg);
}
});
},
// 长按 删除评论
delComment(e, index, index2) {
let that = this;
let user = uni.getStorageSync('userInfo');
//如果是子评论需要遍历子评论查询是否存在自己回复的评论消息
var flag = false; //这个用来确定父评论下是否存在用户自己的子评论
var i = 0; //这个用来锁定子评论楼层位置
e.children.map((item) => {
if (item.uid == user.uid) {
flag = true;
e.id = item.id;
if (index2) {
index2 = i;
}
}
i++;
});
if (e.uid != user.uid) {
if (!flag) {
return;
}
}
uni.showModal({
title: '提示',
content: '确定删除自己的评论嘛?',
success: function(res) {
if (res.confirm) {
that.$H
.post('comment/del', {
id: e.id
})
.then(res2 => {
if (res2.code == 0) {
// if (index2 || index2 === 0) {
// that.commentList[index].children.splice(index2, 1);
// } else {
// that.commentList.splice(index, 1);
// }
that.postDetail.commentCount --
// that.getCommentList('delete');
}
});
}
}
});
},
// 生成分享海报
shareCanvas() {
// this.showCanvas = true;
uni.showLoading({
mask: true,
title: '正在生成海报'
});////
// #ifdef H5
let origin = "h5";
let url = this.$c.shareH5Url + "subpages/content/buyTicket/index?routeId=" + this.postId
// #endif
// #ifdef MP-WEIXIN
let origin = "weixin";
let customerPushCode = uni.getStorageSync("userInfo").pushCode || ''
let url = `subpages/content/buyTicket/index?routeId=${this.postId}&pushCode=${customerPushCode}`
// #endif
this.$H.get('/business/app/tripTourRoute/qrWxCode', {
// routeId: this.postId,
// pushCode: customerPushCode,
url: url
})
.then(res => {
if (res.code == 0) {
this.configData = {
posterQrcode: res.result,
routeName: this.detailContent.routeName,
posterImg: this.detailContent.miediaList[0].mediaPath
}
this.showCanvas = true;
}
uni.hideLoading();
});
},
// 保存海报到相册
saveImg() {
// #ifdef MP-WEIXIN
// errMsg: "saveImageToPhotosAlbum: fail api scope is not declared in the privacy agreement"
uni.getImageInfo({
src: this.posterUrl,
success: function(image) {
uni.saveImageToPhotosAlbum({
filePath: image.path,
success: function(sucess) {
uni.showToast({
title: '图片保存成功'
});
},
fail: function(error) {
uni.showModal({
title: '图片保存失败',
content: '请确认是否已开启授权',
confirmText: '开启授权',
success(res) {
if (res.confirm) {
uni.openSetting({
success(settingdata) {
if (settingdata.authSetting[
"scope.writePhotosAlbum"
]) {
uni.showToast({
title: '授权成功,请重试哦~',
icon: "none"
});
} else {
uni.showToast({
title: '请确定已打开保存权限',
icon: "none"
});
}
}
})
}
}
})
},
});
},
fail() {}
});
// #endif
// #ifdef H5
var oA = document.createElement('a');
oA.download = ''; // 设置下载的文件名,默认是’下载’
oA.href = this.posterUrl;
document.body.appendChild(oA);
oA.click();
oA.remove(); // 下载之后把创建的元素删除
// #endif
},
// 销毁组件
closePopup() {
this.showCanvas = false;
},
}
}
</script>
<style scoped lang="scss">
.tab-content {
padding-bottom: 125rpx;
}
/* 占位内容 */
.seize {
padding: 40px;
text-align: center;
}
/* END */
/* 选项卡样式 */
.tabs {
background: #fff;
width: 100%;
}
/* END */
.box {
padding: 20px;
background-color: #fff;
border-bottom: 20rpx solid #F6F7F9;
}
.btn-customer-bg {
width: 408rpx;
}
.full-container {
background-color: #F6F7F9;
.common-box {
background: #fff;
margin: 10rpx 0;
padding: 0 40rpx 20rpx 40rpx;
}
.flex {
display: flex;
align-items: center;
}
.list-item {
text-align: center;
display: inline-block;
margin-right: 80rpx;
.avatar-z {
width: 70rpx;
height: 70rpx;
border-radius: 50%;
}
.name {
color: #000000;
margin-top: -6rpx;
font-size: 24rpx;
}
}
.list-item:nth-child(5n) {
margin-right: 0;
}
.banner-img {
width: 750rpx;
height: 560rpx;
display: block;
}
.main-box {
// padding-bottom: 149rpx;
.scienc-info {
padding: 0 40rpx;
background: #fff;
.desc {
font-weight: 600;
color: #333333;
font-size: 30rpx;
padding: 20rpx 0 10rpx 0;
}
.tip {
color: #333333;
font-size: 28rpx;
margin-bottom: 20rpx;
}
.price-box {
display: flex;
align-items: center;
justify-content: space-between;
.person {
color: #666666;
font-size: 24rpx;
}
}
.extension-vontainer {
display: flex;
width: 670rpx;
height: 46rpx;
background: #E4E3FF;
align-items: center;
margin-bottom: 20rpx;
border-radius: 23rpx;
padding: 0 20rpx 0 10rpx;
justify-content: space-between;
.notice-icon {
width: 34rpx;
height: 34rpx;
margin-right: 10rpx;
}
.common-tip {
color: #817EF7;
font-size: 24rpx;
}
.puoduct {
padding-top: 9rpx;
}
.plan {
padding-top: 8rpx;
}
.puoduct::after {
content: '';
height: 38rpx;
width: 2rpx;
display: inline-block;
background-color: #817EF7;
vertical-align: middle;
margin: 0 10rpx 8rpx 10rpx;
}
.two-right-icon {
width: 20rpx;
height: 20rpx;
}
}
.operate-box {
padding: 30rpx 0 20rpx 0;
.flex {
display: flex;
align-items: center;
margin-right: 20rpx;
}
display: flex;
justify-content: space-between;
.icon-img {
width: 40rpx;
height: 40rpx;
}
.text {
color: #666666;
font-size: 24rpx;
margin-left: 8rpx;
}
}
}
.package-info {
.nav-tit {
display: flex;
align-items: center;
padding: 20rpx 0 0 0;
.tit {
font-size: 30rpx;
font-weight: 600;
}
.desc {
font-size: 24rpx;
margin-top: 5rpx;
}
}
.flex-container {
display: flex;
flex-wrap: wrap;
.package-list {
margin-bottom: 10rpx;
.name {
font-size: 26rpx;
color: #000000;
padding: 10rpx 16rpx;
background: #F6F7F9;
margin: 0 20rpx 10rpx 0;
border-radius: 4rpx 4rpx 4rpx 4rpx;
}
.name:last-child {
margin: 0 20rpx 0 0;
}
.desc {
color: #333333;
font-size: 24rpx;
margin: 5prx 0 0 10rpx;
}
}
}
}
.share-icon{
animation: name 0.75s ease infinite;
}
@keyframes name{
0% {transform: scale(1);}
50% {transform: scale(1.35);}
100% {transform: scale(1)}
}
.set-out-time {
padding: 0 40rpx 20rpx 40rpx;
.nav-tit {
// padding: 20rpx 0 20rpx 40rpx;
}
}
.aggregate-info {
.list-item {
display: flex;
align-items: center;
margin-bottom: 26rpx;
.text-desc {
font-size: 28rpx;
}
.icon-img {
width: 42rpx;
height: 42rpx;
margin-right: 12rpx;
}
.name {
color: #333333;
font-size: 24rpx;
}
.default-icon {
display: flex;
position: relative;
.icon-index {
top: 50%;
left: 40%;
color: #FFFFFF;
font-size: 20rpx;
transform: translate(-50%, -50%);
position: absolute;
}
}
}
.list-item:last-child {
margin-bottom: 0;
}
}
.person-info {
.nav-tit {
justify-content: space-between;
.number {
color: #817EF7;
font-size: 24rpx;
}
.person {
font-size: 24rpx;
color: #333333;
margin-right: 8rpx;
}
}
}
.tab-info {
.tab-content {
padding-top: 15rpx;
padding-left: 40rpx;
background-color: #fff;
.tabs-item {
margin-right: 60rpx;
.flex {
margin-left: 28rpx;
.line-bottom {
width: 50rpx;
}
}
}
}
.conent {
color: #666666;
font-size: 28rpx;
padding: 40rpx;
background: #fff;
}
}
.comment-box {
padding-bottom: 180rpx;
.avatar {
width: 80rpx;
height: 80rpx;
border-radius: 50%;
margin-right: 10rpx;
}
.comment-tool {
display: flex;
align-items: center;
margin-bottom: 40rpx;
.send-input {
height: 36rpx;
color: #666666;
font-size: 26rpx;
background-color: #F6F6F6;
padding: 27rpx 30rpx 16rpx 30rpx;
border-radius: 34rpx 34rpx 34rpx 34rpx;
}
}
.comment-item {
.flex {
.user {
.user-name {
color: #333333;
font-size: 28rpx;
font-weight: 600;
margin-right: 10rpx;
}
.user-time {
color: #666666;
font-size: 26rpx;
}
}
}
.reply-desc {
color: #666666;
font-size: 26rpx;
margin-left: 90rpx;
}
}
.reply-comment-list {
background: #F6F7F9;
margin: 20rpx 0 0 90rpx;
padding: 20rpx 30rpx;
border-radius: 10rpx 10rpx 10rpx 10rpx;
.sub-comment-item {
margin-bottom: 10rpx;
.flex {
color: #333333;
font-size: 24rpx;
.name {
color: #817EF7;
}
.content {
word-break: break-all;
}
}
.open-more {
color: #6F97CB;
font-size: 24rpx;
margin: 10rpx 0 0 52rpx;
}
}
.sub-comment-item:last-child {
margin-bottom: 0;
}
}
}
}
.bottom-operate {
.flex {
display: flex;
.home-icon {
margin: 0 35rpx 0 20rpx;
}
.ico-box {
text-align: center;
.tui-skeleton-rect {
line-height: 34rpx;
background-color: #fff;
}
.icon-img {
width: 52rpx;
height: 52rpx;
display: block;
}
.text {
color: #666666;
font-size: 18rpx;
}
}
}
.play-group {
width: 408rpx;
}
}
}
</style>
Anchor.vue文件
<!-- 可根据现有需求,"适当"更改组件源码! -->
<template>
<view :id="'luBarTabNav'+barId" class="lu-bar-tab-nav">
<view v-if="!!barFixed" id="luTabFixed" class="lu-bar-tab lu-bar-tab-fixed" :style="{ top: barTopStyles,height:barHeightStyles,display:barShowStyles }">
<!-- <view class="lu-tab-item" v-for="(item,index) in tabList" :key="index"
:class="[selectedIndex==index? 'lu-active' : '',!!iconShow? 'lu-icon-show' : '']"
:style="selectedIndex==index?tabActiveStyles:tabStyles"
@tap="getScrollToTarget(index, item)">
<view :class="selectedIndex==index? 'lu-tab-text lu-tab-text-active' : 'lu-tab-text'">
{{item.text}}
</view>
</view> -->
<view class="customer-tabs order-tabs">
<view class="tabs-item lu-tab-item" :class="selectedIndex == index ? 'activeStyle' : ''" v-for="(item, index) in tabList" :key="index" :style="selectedIndex==index?tabActiveStyles:tabStyles" @click="getScrollToTarget(index, item)">
<view>{{ item.text }}</view>
<view class="flex" v-show="selectedIndex == index">
<view class="cricel-line"></view>
<view class="line-bottom"></view>
</view>
</view>
</view>
</view>
<view id="luTabStatic" class="lu-bar-tab lu-bar-tab-static" :style="{height:barHeightStyles}">
<view class="customer-tabs order-tabs">
<view class="tabs-item lu-tab-item" :class="selectedIndex == index ? 'activeStyle' : ''" v-for="(item, index) in tabList" :key="index" :style="selectedIndex==index?tabActiveStyles:tabStyles" @click="getScrollToTarget(index, item)">
<view>{{ item.text }}</view>
<view class="flex" v-show="selectedIndex == index">
<view class="cricel-line"></view>
<view class="line-bottom"></view>
</view>
</view>
</view>
</view>
<view class="lu-tab-content"><slot></slot></view>
</view>
</template>
<script>
export default {
props: {
barFixed:{//选项卡是否启用浮动功能(可选)
type:Boolean,
default:true
},
iconShow:{//是否启用选项卡图标(可选)
type:Boolean,
default:false
},
transitionShow:{
type:Boolean,
default:false
},
barHeight:{
type:[String,Number],
default:44
},
barTop:{
type:[String,Number],
default:0
},
barId:{
type:[String,Number],
default:0
},
tabList: {
type:Array,
default:function () {
return []
}
}
},
data() {
return {
clickTab: false,
barShow:false,
selectedIndex:0,
};
},
computed:{
tabStyles:function () {
return (!!this.color?'color:'+this.color+';':'')+(!!this.backgroundColor?'backgroundColor:'+this.backgroundColor+';':'');
},
tabActiveStyles:function () {
return (!!this.selectedColor?'color:'+this.selectedColor+';':'')+(!!this.selectedBackgroundColor?'backgroundColor:'+this.selectedBackgroundColor+';':'');
},
barTopStyles:function () {
// #ifndef H5
return 'calc('+this.barTop+'px);';
// #endif
// #ifdef H5
return 'calc('+this.barTop+'px + var(--window-top));';
// #endif
},
barHeightStyles:function () {
return this.barHeight?this.barHeight+'px':'44px';
},
barShowStyles:function () {
return !this.barShow?'none':'';
},
},
methods: {
_barInit:async function (index){
let navTargetTop = [];
let duration = 0;
let viewScrollTop = 0;
let viewHeight = 0;
for (let i = 0,len=this.tabList.length; i < len; i++) {
navTargetTop[i]= await this._queryMultipleNodes(this.tabList[i]["navTarget"]).then(res => {
let navTarget = res[0],
viewPort = res[1];
viewHeight = viewPort.height;
viewScrollTop = viewPort.scrollTop;
const scrollTop = parseInt(navTarget.top) + viewPort.scrollTop - this.barTop - this.barHeight;
return scrollTop;
});
}
if (!!this.transitionShow) {
duration = 200;
}
return {
navTargetTop:navTargetTop,
duration:duration,
viewHeight:viewHeight,
viewScrollTop:viewScrollTop
};
},
getPageScroll:async function(i){
const elment = await this._barInit(i);
let scrollTop = elment["navTargetTop"][i];
let duration = elment["duration"];
let viewHeight = elment["viewHeight"];
let viewScrollTop = elment["viewScrollTop"];
if (Math.abs(scrollTop-viewScrollTop)>viewHeight) {
if (scrollTop>viewScrollTop) {
// 向下移
await uni.pageScrollTo({
scrollTop:(scrollTop-viewHeight),
duration:0
});
}else{
// 向上移
await uni.pageScrollTo({
scrollTop:(scrollTop+viewHeight),
duration:0
});
}
}
await uni.pageScrollTo({
scrollTop:(scrollTop - 10),
duration:duration
});
this.selectedIndex = i
},
// 点击切换Tab
getScrollToTarget:function(i, item) {
this.clickTabFlag = true
uni.setStorageSync('clickTabFlag', true)
uni.setStorageSync('clickTabIndex', i)
this.getPageScroll(i, 'clickCurrnetTab');
this.$emit('clickTabs',item)
},
_queryMultipleNodes:function (e,notThis) {
return new Promise((resolve, reject) => {
let view = uni.createSelectorQuery();
if (!!notThis) {
view.in(this);
}
if (!!e) {
view.select(e).boundingClientRect();
}
view.selectViewport().fields({size: true,scrollOffset: true});
view.exec(function(res) {
resolve(res);
});
});
},
_showBarFixed:function () {
this._queryMultipleNodes("#luTabStatic",true).then(res => {
let tabNav = res[0];
if (tabNav.top<=this.barTop) {
this.barShow=true;
}else{
this.barShow=false;
}
});
},
getSelectedTab:function (y, type) {
this._barInit().then((res)=>{
let itemIndex = 0;
for (let i = 0,len=res["navTargetTop"].length; i < len; i++) {
if (y >=res["navTargetTop"][i] - 12) {
itemIndex = i;
}
}
// 如果到底,则直接重置最后一个Tab
if (uni.getStorageSync('scrollBtttom')) {
this.selectedIndex = this.tabList.length - 1
setTimeout(() => {
uni.setStorageSync('scrollBtttom', false)
}, 800)
return
} else {
// 如果是点击Tab,采用点击切换tab
if (uni.getStorageSync('clickTabFlag') && uni.getStorageSync('scrollPageFlag')) {
this.selectedIndex = uni.getStorageSync('clickTabIndex')
setTimeout(() => {
uni.setStorageSync('clickTabFlag', false)
}, 800)
return
} else {
this.selectedIndex = itemIndex;
}
}
uni.setStorageSync('clickTabFlag', false)
uni.setStorageSync('clickTabIndex', '0')
});
if (!!this.barFixed) {
this._showBarFixed();
}
}
}
};
</script>
<style lang="scss" >
.order-tabs {
.flex {
margin-left: -4rpx;
}
}
lu-bar-tab-nav{
position:relative;
width: 100%;
}
.lu-bar-tab-nav{
position:relative;
width: 100%;
.lu-bar-tab{
width: 100%;
display: flex;
flex-flow: row wrap;
justify-content: space-around;
align-items:center;
background-color: #fff;
height: 44px;
.lu-tab-item{
//默认状态
position: relative;
flex: 1 1 auto;
text-align: center;
color: #333;
display: flex;
flex-flow: column nowrap;
justify-content: center;
align-items:center;
// &::before{
// position: absolute;
// top: calc(50% - 15px);
// left: 0px;
// content: " ";
// width: 1px;
// height: 30px;
// background-color: #eee;
// }
&:first-child::before{
display: none;
}
.lu-tab-icon{
font-size: inherit;
color: inherit;
}
// 选项卡默认样式
.lu-tab-text{
font-size: inherit;
color: inherit;
// border: 1px solid red;
padding-bottom: 6px;
// 加个默认白色,防止高度不统一
border-bottom: 3px solid #fff;
}
// 激活的选项卡-下划线
.lu-tab-text-active {
border-bottom: 3px solid #2979ff;
}
// 显示图标
&.lu-icon-show{
.lu-tab-icon{
height: 24px;
width: 24px;
background-position: center center;
background-repeat: no-repeat;
background-size: 100% 100%;
}
.lu-tab-text{
font-size: 12px;
line-height: 16px;
}
}
// 选中状态
&.lu-active{
color: #2979ff;
// font-weight: bold;
.lu-tab-icon{
background-position: center center;
background-repeat: no-repeat;
background-size: 100% 100%;
}
}
}
}
.lu-bar-tab-fixed{
position:fixed;
z-index: 1;
top:calc(0px + var(--window-top));
}
.lu-bar-tab-static{
position:static;
z-index: 0;
}
}
</style>