950 lines
22 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 style="background-color: #fff;">
<view class="goods-bar" :class="color ? 'goods-box' : ''" :style="{'height': customVal.height + 'px;padding:' + customVal.top + 'px 15px ' + (customVal.top - statusBarHeight) + 'px;'}">
<view class="icon flex" v-if="pagesLength === 1" @click="toPage()">
<up-icon name="home" size="18" color="#333" />
</view>
<view class="icon flex" v-if="pagesLength > 1" @click="toBackPage()">
<up-icon name="arrow-left" size="18" color="#333" />
</view>
<view class="inputBox" :class="color ? 'input1' : ''">
<view :style="{backgroundImage: 'url(' + sp2 + ')'}" class="search"></view>
<input class="input" type="text" v-model="title" placeholder="搜索商品" @confirm="searchShopGoods" />
</view>
</view>
<view class="oneBox" :style="{'background': 'url(' + confidence.background_image + ')' + ' no-repeat;background-size: cover;background-position: center;'}">
<div class="cont">
<div class="logoBox flex1">
<div class="avatar"><img :src="confidence.logo" />{{confidence.name}}</div>
<button class="share flex" open-type="share">
<up-icon name="share" size="18" color="#fff" />&nbsp;分享
</button>
</div>
<div class="introBox flex1" @click="showIntro = true">
<text>{{ confidence.describe}}</text>
<up-icon name="arrow-right" />
</div>
</div>
</view>
<view class="pageBox" :class="confidence.actives && confidence.actives.length > 0 ? '' : 'marTop'">
<!-- 会场状态 0未开始销售 1销售中 2销售结束 -->
<up-sticky :offset-top="barHeight" v-if="confidence.actives && confidence.actives.length > 0">
<div class="move_tabs">
<scroll-view class="scroll_move" scroll-x="true" @scroll="scroll" scroll-left="0">
<view v-for="(item, index) in activeList" :key="item.id" :class="activeIndex === index ? 'tabs tabs-active' : 'tabs'"
@click="onTabAction(item, index)">
<view class="content_name">{{item.special_name}}</view>
<view class="content_text">{{item.name}}</view>
</view>
</scroll-view>
</div>
</up-sticky>
<div style="padding: 0 20rpx;box-sizing: border-box;">
<div class="endtime">
<div class="row"><view v-html="getTimeIng(confidence.sold_start_time, confidence.sold_off_time)"></view></div>
<div class="row" v-if="confidence.actives && confidence.actives.length > 0">
<text class="text_color">优惠</text>
<text :style="{color: Color}">{{ confidence.actives[activeIndex].name }}</text>
</div>
<div class="row"><text class="text_color">服务</text>假一赔十·七天无理由</div>
<div class="row">
<text class="text_color">发货</text>
<template v-if="confidence.ship_time_type && confidence.ship_time_type != 0">最晚于{{shipType[confidence.ship_time_type]}}发货
</template>停发地区发货时效不确定<template v-if="confidence.ship_address">{{confidence.ship_address}}发货</template>
</div>
</div>
</div>
<div style="padding: 0 20rpx;box-sizing: border-box;">
<div :class="role === 1 ? 'botBlock' : ''">
<up-sticky :offset-top="list.length > 1 ? barHeight : barHeight + 10">
<div v-if="list.length > 1" style="background-color: #fff;">
<up-tabs
:current="active"
keyName="name"
:list="list"
:lineColor="Color"
:activeStyle="{ color: Color }"
@click="scorllBottom">
</up-tabs>
</div>
<view class="sortBox">
<view class="item flex" v-for="(item, index) in sortList" :key="index" @click="chooseSort(index)">
<text :class="sortIndex === index ? 'choose' : ''">{{item}}</text>
<view class="icon" v-if="index === 2">
<up-icon name="arrow-up" size="10" :color="priceType && sortIndex === 2 ? Color : ''" />
<up-icon name="arrow-down" size="10" :color="!priceType && sortIndex === 2 ? Color : ''" />
</view>
</view>
</view>
</up-sticky>
<div class="list_box">
<div v-for="(item, index) in storelist" :key="index" class="neirong" @click="hanleJump1(item.id)">
<image :src="item.face_img + '?x-oss-process=image/resize,m_fill,w_400,h_400'" mode="aspectFill"></image>
<div class="text">{{item.title}}</div>
<div class='move_img' v-if='item.active && item.active.name && item.active.type !== 1'>
{{item.active.name}}
</div>
<div class="pricebox">
<view class="price">
<text class="icon">¥</text>
<block v-if="item.price > 0"><text>{{item.price}}</text></block>
<block v-else>
<text v-if="item.max_price == item.min_price">{{item.min_price}}</text>
<text v-else>{{item.min_price * 1}}~{{item.max_price}}</text>
</block>
</view>
<view v-if="(item.price > 0 && item.market_price > 0) || (item.market_price > 0 && item.max_price == item.min_price && item.min_price > 0)" class="market_price"><text class="icon">¥</text>{{item.market_price}}</view>
</div>
</div>
</div>
<view class="bottom" v-if="page >= lastPage">— 没有更多了 —</view>
</div>
</div>
</view>
<view class="goods-sale flex" v-if="role == 1" @click="showShare = true">
<text class="iconfont icon-share"></text>
<text>&nbsp;分享帮卖</text>
</view>
<view class="topBox" v-if="showSlider">
<view @click="toHomePage" class="home">
<up-icon name="bookmark" color="#000" size="24" />
<view>首页</view>
</view>
<view @click="scrollToTop" class="top flex">
<up-icon name="arrow-upward" color="#000" size="20" />
</view>
</view>
<van-share :show-share="showShare" @creatShare="creatShare" @getLink="getLink" @close="showShare = false" />
<creat-poster v-if="showImg" :posterImg="posterImg" @closeShow="showImg = false" />
<up-popup :show="showIntro" :round="10" mode="bottom" @close="showIntro = false">
<view class="popup">
<view class="tit">品牌故事</view>
<view class="text">{{confidence.describe}}</view>
<view class="cancel" @click="showIntro = false">好的</view>
</view>
</up-popup>
<!-- 隐私协议弹窗 -->
<privacy-popup :show-dialog="showPrivacy" @close="showPrivacy = false" @agree="showPrivacy = false" @refuse="showPrivacy = false" />
</view>
</template>
<script>
import { get, post } from '@/api/request.js'
import { ref, reactive, toRefs } from 'vue'
import { DownTime, UpTime, userBind, judgePrivacy } from '@/components/common.js'
import { sp2 } from '@/components/img.js'
import vanShare from '@/components/share/vanShare.vue'
import creatPoster from '@/components/share/creatPoster.vue'
import { Style } from '@/utils/list.js'
import privacyPopup from '@/components/privacyPopup/index.vue'
export default {
components: {
vanShare,
creatPoster,
privacyPopup
},
setup() {
const state = reactive({
activeList: [],
active_id: '',
activeIndex: 0,
sp2: sp2,
shipType: {
'1': '48小时内',
'2': '72小时内',
'3': '7天内'
}
})
const data = reactive({
showSlider: false,
showImgActive: false,
sortIndex: 0,
priceType: false,
sortList: ['综合', '销量', '价格', '上新'],
showIntro: false,
id: '',
confidence: {
background_image: ''
},
storelist: [],
list: [],
pagesLength: 0,
active: 0,
lastPage: 0,
color: false,
sale_id: '',
s: '',
u: '',
company_id: '',
shareUrl: '',
sold_status: '',
role: 0,
showShare: false,
showImg: false,
posterImg: '',
customVal: uni.getMenuButtonBoundingClientRect(),
Color: '',
Theme: '',
priceColor: '',
barHeight: 60,
statusBarHeight: uni.getSystemInfoSync().statusBarHeight,
showPrivacy: false
})
const search2 = reactive({
sales: '',
price: '',
score: 2,
new: ''
})
const search1 = reactive({
title: '', // 店铺名称
merchant_type: '', // 店铺类型
page: 1
})
let pages = getCurrentPages()
data.pagesLength = pages.length
const scorllBottom = (e) => {
data.active = e.index
search1.merchant_type = e.detail.name
search1.title = ''
data.storelist = []
search1.page = 1
getBoxlist()
}
const hanleJump1 = (id) => {
uni.navigateTo({
url: '/pages/productDetails/index?id=' + id
})
}
// 小店信息
async function getBoxtitle() {
let res = await get(`/api/v1/merchant/${data.id}`)
data.confidence = res.data
data.sold_status = res.data.sold_status
state.activeList = res.data.actives ? res.data.actives : []
if(!state.active_id) {
state.active_id = res.data.actives && res.data.actives.length > 0 ? res.data.actives[0].id : '' //有活动默认传第一个id
}
if(uni.getEnterOptionsSync().query && uni.getEnterOptionsSync().query.actionIndex) {
state.activeIndex = parseInt(uni.getEnterOptionsSync().query.actionIndex)
state.active_id = res.data.actives[state.activeIndex].id
} else if(state.active_id && res.data.actives.length != 0) {
res.data.actives.forEach((it, i) => {
if(it.id == state.active_id) {
state.activeIndex = i
}
})
}
}
const toHomePage = () => {
uni.reLaunch({
url: '/pages/index/index?number=1'
})
}
// 小店团购
async function getBoxlist() {
uni.showLoading({
title: '加载中...'
})
let parmas = {}
if (data.confidence.actives) {
parmas = {
...search1,
...search2,
active_id: state.active_id //活动id
}
} else {
parmas = {
...search1,
...search2,
active_id: '' //活动id
}
}
await get(`/api/v1/merchant/goods/${data.id}`, parmas).then(res => {
data.storelist = data.storelist.concat(res.data)
data.lastPage = res.meta.last_page
uni.hideLoading()
})
}
// 小店分类
async function getBoxlist1() {
let res = await get(`/api/v1/merchant/type/${data.id}`)
data.list = res.data
data.list.unshift({
id: ' ',
name: '全部'
})
}
// 回退上一级
const toBackPage = () => {
uni.navigateBack({
delta: 1
})
}
const searchShopGoods = () => {
data.storelist = []
search1.merchant_type = ''
search1.page = 1
getBoxlist()
}
function toPage() {
uni.switchTab({
url: '/pages/index/index'
})
}
const getShareUrl = () => {
post('/api/v1/user/share', {
page: 'pages/smallshop/index',
query: `id=${data.id}`
}).then((res) => {
data.shareUrl = res.data.path
})
}
// 排序
function chooseSort(index) {
data.sortIndex = index
if (index === 2) {
data.priceType = !data.priceType
search2.price = data.priceType ? 1 : 2
search2.new = ''
} else {
search2.price = ''
}
search2.sales = index === 1 ? 2 : ''
search2.score = index === 0 ? 2 : ''
search2.new = index === 3 ? 2 : ''
updateList()
}
const scrollToTop = () => {
uni.pageScrollTo({
scrollTop: 0,
duration: 300
})
}
function updateList() {
data.storelist = []
search1.page = 1
getBoxlist()
}
// 活动切换事件
const handle_move = () => {
data.storelist = []
search1.page = 1
getBoxlist()
}
// 分享海报
const handleImgActive = () => {
data.showImgActive = true
}
// 分享链接
const getLink = async () => {
if(await judgePrivacy()) {
data.showPrivacy = true
return false
}
post('/api/v1/user/link/short', {
page: 'pages/smallshop/index',
shop_goods_id: data.id
}).then((res) => {
data.link = res.data.link
uni.setClipboardData({
data: data.link
})
})
}
// 生成海报
const creatShare = () => {
uni.showLoading({
title: '海报生成中...'
})
get(`/api/v1/merchant/share/${data.confidence.id}`, {active_id: state.active_id}).then((res) => {
data.posterImg = res.data[0].image
uni.hideLoading()
data.showImg = true
})
}
function getTimeIng(startTime, endTime) {
// 如果开始时间存在,结束时间不存在,判断活动是否开始
if(startTime && !endTime){
return UpTime(startTime, 2)
}
// 如果开始时间不存在,结束时间存在,判断活动是否结束
if(!startTime && endTime) {
return DownTime(endTime, 2)
}
// 如果开始时间、结束时间存在
if(startTime && endTime) {
let nt = new Date().getTime()
let st = new Date(startTime.replace(/-/g, "/"))
let et = new Date(endTime.replace(/-/g, "/"))
if(nt <= st || st < nt < et) {
return UpTime(startTime, 2)
}
if(nt >= et) {
return DownTime(startTime, 2)
}
}
return ''
}
const onTabAction = (item, index) => {
state.active_id = item.id
state.activeIndex = index
handle_move()
}
return {
...toRefs(data),
getBoxtitle,
getBoxlist,
getBoxlist1,
toBackPage,
toPage,
scorllBottom,
...toRefs(search1),
searchShopGoods,
userBind,
getShareUrl,
hanleJump1,
chooseSort,
...toRefs(search2),
updateList,
toHomePage,
scrollToTop,
handle_move,
DownTime,
UpTime,
handleImgActive,
...toRefs(state),
getLink,
creatShare,
getTimeIng,
onTabAction
}
},
async onLoad(options) {
// await this.$onLaunched
const _this = this
let scenne = uni.getEnterOptionsSync().scene
if (options) {
Object.keys(options).map((key) => {
if (key == 'id') {
_this.id = options[key] || 0
} else if (key == 'from') {
_this.sale_id = options[key] || 0
} else if (key == 's') {
_this.s = options[key] || 0
} else if (key == 'u') {
_this.u = options[key] || 0
} else if (key == 'company_id') {
_this.company_id = options[key] || ''
}
})
} else {
_this.id = options.id
_this.sale_id = options.from || 0
_this.s = options.s || 0
_this.u = options.u || 0
}
if (options.scene) {
const scene = decodeURIComponent(options.scene)
_this.id = scene.split(',')[0] || 0
_this.sale_id = scene.split(',')[1] || 0
_this.s = scene.split(',')[2] || 0
_this.u = scene.split(',')[3] || 0
_this.active_id = scene.split(',')[4] || ''
}
await _this.getBoxtitle()
await _this.getBoxlist()
await _this.getBoxlist1()
this.Color = uni.getStorageSync('theme_color')
this.priceColor = Style[uni.getStorageSync('theme_index') * 1].priceColor
this.Theme = uni.getStorageSync('theme_index') * 1
let parmas = {
from: _this.sale_id,
s: _this.s,
u: _this.u,
group_id: _this.id,
scene: scenne,
company_id: _this.company_id
}
this.userBind(parmas)
this.getShareUrl()
this.role = uni.getStorageSync('role')
this.barHeight = this.customVal.top + this.customVal.height
},
onPageScroll(e) {
if (e.scrollTop > 50) {
this.color = true
this.showSlider = true
} else {
this.color = false
this.showSlider = false
}
},
onReachBottom() {
if (this.page < this.lastPage) {
this.page++
this.getBoxlist()
}
},
onShareAppMessage() {
let share_tit = this.confidence.share_title
let share_img = this.confidence.share_image
let share_url = this.shareUrl
if(this.confidence.actives && this.confidence.actives[this.activeIndex]) {
share_tit += '【全场' + this.confidence.actives[this.activeIndex].name + '】'
share_img = this.storelist[0] ? this.storelist[0].face_img : share_img
share_url += '&actionIndex=' + this.activeIndex
}
return {
title: share_tit,
imageUrl: share_img,
path: share_url
}
}
}
</script>
<style lang="scss" scoped>
.flex{
display: flex;
align-items: center;
justify-content: center;
}
.flex1{
display: flex;
align-items: center;
justify-content: space-between;
}
.topBox{
position: fixed;
bottom: 15%;
right: 4%;
text-align: center;
z-index: 90;
.home{
background: #fff;
font-size: 28rpx;
border-radius: 8rpx;
padding: 10rpx;
box-sizing: border-box;
opacity: 0.9;
box-shadow: 0 2px 10px 0 rgba(0, 0, 0, 0.2);
}
.top{
margin-top: 20rpx;
background: #fff;
width: 80rpx;
height: 80rpx;
line-height: 107rpx;
border-radius: 50%;
box-shadow: 0 2px 10px 0 rgba(0, 0, 0, 0.2);
}
}
.sortBox{
background-color: #fff;
height: 90rpx;
display: flex;
align-items: center;
justify-content: space-around;
.item{
flex: 1;
color: #666;
font-size: 28rpx;
.icon {
display: flex;
flex-direction: column;
margin-left: 10rpx;
.iconfont{
font-size: 24rpx;
line-height: 16rpx;
}
}
}
.choose {
color: v-bind('Color');
font-weight: bold;
}
}
.popup{
padding: 30rpx;
.tit{
font-size: 34rpx;
font-weight: 800;
}
.text{
margin:20rpx 0;
font-size: 30rpx;
line-height: 56rpx;
max-height: 60vh;
overflow-y: auto;
}
.cancel{
width: 690rpx;
height: 86rpx;
line-height: 86rpx;
background: v-bind('Color');
border-radius: 49rpx;
text-align: center;
color: #fff;
margin-top: 50rpx;
}
}
.bottom{
text-align: center;
font-size: 24rpx;
color: #999999;
margin: auto;
line-height: 80rpx;
}
.list_box{
display: flex;
flex-wrap: wrap;
justify-content: space-between;
.neirong {
width: 48%;
margin-bottom: 30rpx;
image {
width: 330rpx;
height: 330rpx;
border-radius: 10rpx;
}
}
.text {
font-size: 28rpx;
font-family: Source Han Sans CN;
font-weight: 400;
height: 40px;
color: #2c2c2c;
word-break: break-all;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
margin: 10rpx 0;
}
.move_img {
display: inline-block;
font-size: 24rpx;
color: #fff;
margin: 3px 0px;
background-image: url('@/static/image/move_img.png');
background-repeat: no-repeat;
padding: 0px 3px 0px 12px;
height: 40rpx;
background-size: 100% 100%;
line-height: 40rpx;
}
.pricebox{
display: flex;
align-items: flex-end;
.price {
font-size: 32rpx;
font-family: Source Han Sans CN;
font-weight: 500;
color: v-bind('priceColor');
.icon {
font-size: 25rpx;
}
}
.market_price {
margin-left: 20rpx;
font-size: 26rpx;
color: #676767;
text-decoration: line-through;
}
}
}
.goods-box {
background-color: #fff !important;
}
.goods-bar {
position: fixed;
top: 0;
width: 100%;
padding: 80rpx 15rpx 0;
background-color: rgba(255, 255, 255, 0);
z-index: 99;
height: 90rpx;
align-items: center;
display: flex;
.icon{
width: 60rpx;
height: 60rpx;
background-color: rgba(255, 255, 255, 0.7);
border-radius: 50%;
margin-right: 30rpx;
}
}
.oneBox{
height: 430rpx;
width: 100%;
background-size: 100%;
position: relative;
z-index: 98;
.cont{
position: absolute;
bottom: -58rpx;
left: 0;
width: 100%;
padding: 0 20rpx;
box-sizing: border-box;
.logoBox{
.avatar{
display: flex;
align-items: center;
font-size: 36rpx;
font-family: Source Han Sans CN;
color: #fff;
image{
width: 90rpx;
height: 90rpx;
border-radius: 15rpx;
margin-right: 20rpx;
}
}
.share{
width: 147rpx;
height: 61rpx;
line-height: 61rpx;
background: v-bind('Color');
opacity: 1;
border-radius: 31rpx;
color: #fff;
font-size: 24rpx;
}
}
}
}
.inputBox{
display: flex;
align-items: center;
height: 100%;
border-radius: 33rpx;
background-color: rgba(255, 255, 255, 0.6);
padding: 0 24rpx;
width: 300rpx;
&.input1{
background-color: #f1f1f1;
}
.search{
background-repeat: no-repeat;
background-size: 452px 408px;
height: 17px;
width: 17px;
background-position: -123px -136px;
transform: scale(0.85);
margin-right: 10rpx;
}
.input {
font-size: 28rpx;
width: calc(100% - 20px);
}
}
.introBox{
padding: 0 25rpx;
margin-top: 20rpx;
background: #fff;
box-shadow: 0 2px 10px 0 rgba(0, 0, 0, 0.1);
opacity: 1;
border-radius: 20rpx;
height: 116rpx;
box-sizing: border-box;
text{
word-break: break-all;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
padding-right: 20rpx;
color: #2c2c2c;
font-size: 26rpx;
line-height: 40rpx;
}
}
.advertise {
height: 226rpx;
background: rgba(222, 222, 222, 0.39);
opacity: 1;
border-radius: 20rpx;
margin-bottom: 20rpx;
image {
width: 100%;
height: 100%;
border-radius: 10rpx;
}
}
.theme-list {
margin-top: 12px;
}
.fixed-nav {
overflow-x: scroll;
-webkit-overflow-scrolling: touch;
}
.fixed-nav-content {
display: flex;
}
.tab-title {
padding: 0 13px;
margin-right: 12px;
color: #141414;
border-radius: 13px;
font-size: 12px;
flex-shrink: 0;
height: 0.52rem;
line-height: 0.52rem;
}
</style>
<style lang='scss' scoped>
.endtime{
width: 100%;
border-radius: 10px;
padding: 20rpx 30rpx;
color: #666;
background-color: #f5f5f5;
box-sizing: border-box;
.row{
font-size: 24rpx;
margin-bottom: 10rpx;
color: #939393;
.text_color {
font-size: 26rpx;
color: #2A2A2A;
}
text {
margin-right: 20rpx;
}
}
}
.goods-sale {
position: fixed;
bottom: 0;
width: 100%;
background-color: #fff;
border-top: 1px solid #ccc;
height: 110rpx;
z-index: 100;
font-size: 30rpx;
line-height: 1;
.iconfont{
font-size: 20px;
color: v-bind('Color');
}
}
.botBlock {
padding-bottom: 100rpx;
}
.pageBox{
margin-top: 58rpx;
position: relative;
z-index: 90;
&.marTop{
margin-top: 78rpx;
}
}
.move_tabs{
width: 100%;
overflow: hidden;
box-sizing: border-box;
border-radius: 20rpx;
background-color: #ffff;
padding: 0 20rpx;
box-sizing: border-box;
.scroll_move{
display: flex;
justify-content: flex-start;
align-items: center;
white-space: nowrap;
width: 100%;
padding: 10px 0;
.tabs{
display: inline-flex;
margin-right: 20rpx;
background-color: #f5f5f5;
border-radius: 8rpx;
align-items: center;
flex-direction: column;
justify-content: center;
padding: 0 10px;
height: 45px;
box-sizing: border-box;
.content_name{
font-size: 30rpx;
font-weight: 700;
}
.content_text{
margin-top: 5px;
font-size: 24rpx;
color: #66667f;
}
}
.tabs-active{
background-color: v-bind('Color');
color: #fff;
.content_text{
color: #fff;
}
}
}
}
</style>