Commit 96d13733 authored by lixy's avatar lixy

旅游、车型详情定位

parent 1cbd37e1
......@@ -79,6 +79,7 @@ input[disabled] {
background: #fff;
color: #333;
width:100%;
border-bottom: 1px solid #dfdfdf;
}
.ellipsis {
overflow: hidden;white-space: nowrap;text-overflow:ellipsis;word-wrap:break-word;word-break:break-all;
......@@ -201,4 +202,5 @@ img {
}
.bt-line{
border-bottom: 1px solid #dfdfdf;
padding-bottom: 17px;
}
......@@ -172,7 +172,7 @@
transform: translateY(-50%);
}
.banner{
height:px2rem(500px);
height:250px;
overflow: hidden;
}
img{
......
......@@ -13,7 +13,6 @@
@next="next"
@change="changeBanner"
@click="bannerClick"/>
<div class="main-padding">{{ totalCount }}个活动</div>
<ul
v-if="dataDetail.length"
id="scroll-area"
......@@ -49,7 +48,7 @@
class="loading">加载中……</p>
<p
v-if="touchend"
class="empty_data">——没有更多了——</p>
class="empty_data">——{{ totalCount }}个活动——</p>
<go-top/>
<Footer v-if="touchend"/>
</div>
......
<template>
<div class="cartcontrol">
<svg
v-show="food.count>0"
class="inner"
fill="rgb(35, 149, 255)"
@click.stop.prevent="decreaseCart"><use xlink:href="#cart-minus"/></svg>
<div
v-show="food.count>0"
class="cart-count">{{ food.count }}</div>
<svg
width="24"
height="24"
fill="rgb(35, 149, 255)"
@click="addCart"><use xlink:href="#cart-add"/></svg>
</div>
</template>
<script>
import Vue from "vue";
export default {
props: ['food'],
methods: {
addCart(event) {
if (!this.food.count) {
Vue.set(this.food, "count", 1);
} else {
this.food.count++;
}
this.$emit("add", event.target);
},
decreaseCart(event) {
if (this.food.count) {
this.food.count--;
}
}
}
};
</script>
<style lang="scss">
.cartcontrol {
display: flex;
align-items: center;
.inner {
width: 24px;
height: 24px;
}
.cart-count {
display: inline-block;
vertical-align: top;
width: 25px;
text-align: center;
font-size: 15px;
color: #333;
}
.cart-add {
display: inline-block;
padding: 6px;
line-height: 24px;
font-size: 24px;
color: rgb(0, 160, 220);
}
}
</style>
<template>
<div class="goods-page">
<div class="menu-wrapper">
<ul>
<li
v-for="(item, index) in goods"
:key="index"
:class="{'current':currentIndex === index}"
class="menu-item"
@click="selectMenu(index,$event)">
<span class="text">
<!-- <span v-show="item.type>0" class="icon" :class="classMap[item.type]"></span> -->
{{ item.name }}
</span>
</li>
</ul>
</div>
<div class="foods-wrapper">
<ul>
<li
v-for="(item, index) in goods"
:key="index"
class="food-list food-list-hook">
<h1
:id="'a'+index"
class="title">{{ item.name }}</h1>
<ul>
<li
v-for="(food, index) in item.foods"
:key="index"
class="food-item border-1px">
<div class="icon">
<img :src="food.icon" >
</div>
<div class="content">
<h2 class="name">{{ food.name }}</h2>
<p class="desc">{{ food.description }}</p>
<div class="extra">
<span class="count">月售{{ food.sellCount }}</span><span>好评率{{ food.rating }}%</span>
</div>
<div class="price">
<span class="now"><b>{{ food.price }}</b></span>
<cartcontrol :food="food" />
</div>
</div>
</li>
</ul>
</li>
</ul>
</div>
<shopcart
ref="shopcart"
:select-foods="selectFoods"
:delivery-price="seller.deliveryPrice"
:min-price="seller.minPrice"/>
</div>
</template>
<script>
import shopcart from "./shopcart";
import cartcontrol from "./cartcontrol";
import * as shoppingApi from "~/assets/services/shopping";
export default {
components: {
shopcart,
cartcontrol
},
props: {
seller: {
default: {}
}
},
data() {
return {
goods: [],
listHeight: [],
scrollY: 0,
classMap: ["decrease", "discount", "special", "invoice", "guarantee"],
currentIndex: 0,
isScroll: false
};
},
computed: {
selectFoods() {
let foods = [];
this.goods.forEach(good => {
good.foods.forEach(food => {
if (food.count) {
foods.push(food);
}
});
});
return foods;
}
},
created() {},
mounted() {
shoppingApi.goods().then(res => {
if (res.code === 0) {
this.goods = res.data;
}
});
//菜品滚动选择类目
document.querySelector(".foods-wrapper").addEventListener(
"scroll",
this.throttle(() => {
// 防止手动选择的时候误操作
if (this.isScroll) {
return;
}
this.goods.map((item, index) => {
const rect_top = document
.querySelector("#a" + index)
.getBoundingClientRect().top;
const container_top = document
.querySelector(".foods-wrapper")
.getBoundingClientRect().top;
if (
rect_top - container_top < 100 &&
rect_top - container_top > -100
) {
this.currentIndex = index;
}
});
}, 50)
);
},
methods: {
selectMenu(index) {
if (index === this.currentIndex) {
return;
}
this.currentIndex = index;
this.animateScroll("#a" + index, ".foods-wrapper", 50);
},
// 平滑滚动方法
animateScroll(element, container, speed) {
this.isScroll = true;
let rect =
document.querySelector(element).getBoundingClientRect().top -
document.querySelector(container).getBoundingClientRect().top;
//获取元素相对窗口的top值,此处应加上窗口本身的偏移
let top = rect + document.querySelector(container).scrollTop;
let currentTop = 0;
let requestId;
//采用requestAnimationFrame,平滑动画
const step = timestamp => {
if (currentTop <= top) {
document.querySelector(container).scrollTo(0, currentTop);
requestId = window.requestAnimationFrame(step);
} else {
window.cancelAnimationFrame(requestId);
this.isScroll = false;
}
currentTop += speed;
};
window.requestAnimationFrame(step);
},
// 函数防抖
throttle(method, delay) {
var timer = null;
return function () {
clearTimeout(timer);
timer = setTimeout(() => {
method.apply(this, arguments);
}, delay);
};
}
},
};
</script>
<style lang="scss">
@import "../../../../assets/styles/mixin";
.goods-page {
display: flex;
background: #fff;
width: 100%;
.menu-wrapper {
flex: 0 0 80px;
width: 80px;
height: calc(100vh - 38px);
overflow-x: hidden;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
background: #f8f8f8;
.menu-item {
display: table;
height: 54px;
padding: 0 12px;
line-height: 14px;
&.current {
position: relative;
margin-top: -1px;
z-index: 10;
background: #ffffff;
.text {
color: #333;
}
}
.icon {
display: inline-block;
vertical-align: middle;
width: 12px;
height: 12px;
margin-right: 2px;
background-size: 12px 12px;
background-repeat: no-repeat;
// &.decrease {
// bg-image('decrease_3');
// }
// &.discount {
// bg-image('discount_3');
// }
// &.guarantee {
// bg-image('guarantee_3');
// }
// &.invoice {
// bg-image('invoice_3');
// }
// &.special {
// bg-image('special_3');
// }
}
.text {
display: table-cell;
width: 56px;
vertical-align: middle;
font-size: 12px;
position: relative;
color: #666;
}
}
}
.foods-wrapper {
width: calc(100vw - 80px);
height: calc(100vh - 38px);
overflow-x: hidden;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
padding-bottom: 60px;
.title {
padding-left: 14px;
height: 30px;
line-height: 30px;
font-size: 12px;
color: #666;
}
.food-item {
display: flex;
margin: 10px;
padding-bottom: 18px;
position: relative;
&:after {
display: block;
position: absolute;
left: 0;
bottom: 0;
width: 100%;
content: " ";
}
&:last-child {
margin-bottom: 0;
&:after {
display: none;
}
}
.icon {
flex: 0 0 80px;
margin-right: 10px;
}
.content {
flex: 1;
position: relative;
.name {
margin: 2px 0 8px 0px;
height: 14px;
line-height: 14px;
font-size: 14px;
color: rgb(7, 17, 27);
}
.desc,
.extra {
line-height: 10px;
font-size: 12px;
color: rgb(147, 153, 159);
transform: scale(0.9) translateX(-6%);
}
.desc {
margin-bottom: 8px;
line-height: 12px;
max-width: px2rem(350px);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.extra {
.count {
margin-right: 12px;
}
}
.price {
width: 100%;
font-weight: 700;
position: absolute;
bottom: 0;
@include fj(space-between);
.now {
margin-right: 8px;
font-size: 14px;
color: rgb(255, 83, 57);
}
.old {
text-decoration: line-through;
font-size: 10px;
color: rgb(147, 153, 159);
}
b {
font-weight: normal;
font-size: 18px;
}
}
}
}
}
}
</style>
<template>
<div class="shopcart">
<div
class="content"
@click="toggleList">
<div class="content-left">
<div class="logo-wrapper">
<div
:class="{'highlight':totalCount>0}"
class="logo">
<span
v-if="totalCount<=0"
class="icon-shopping_cart">
<img
src="~/assets/images/cart.svg"
alt="">
</span>
<span
v-else
class="icon-shopping_cart highlight">
<img
src="~/assets/images/cart2.svg"
alt="">
</span>
</div>
<div
v-show="totalCount>0"
class="num">{{ totalCount }}</div>
</div>
<div
:class="{'highlight':totalPrice>0}"
class="price">¥{{ totalPrice }}</div>
<div class="desc">另需配送费¥{{ deliveryPrice }}</div>
</div>
<div class="content-right">
<div
:class="payClass"
class="pay">{{ payDesc }}</div>
</div>
</div>
<div class="ball-container">
<div
v-for="(ball, index) in balls"
:key="index">
<transition
name="drop"
@before-enter="beforeDrop"
@enter="dropping"
@after-enter="afterDrop">
<div
v-show="ball.show"
class="ball">
<div class="inner inner-hook"/>
</div>
</transition>
</div>
</div>
<div
v-show="listShow"
class="mark"
@click="toggleList"/>
<transition name="fold">
<div
v-show="listShow"
class="shopcart-list">
<div class="list-header">
<h1 class="title">已选商品</h1>
<span
class="empty"
@click="empty">清空</span>
</div>
<div
ref="listContent"
class="list-content">
<ul>
<li
v-for="(food, index) in selectFoods"
:key="index"
class="food">
<span class="name">{{ food.name }}</span>
<div class="price">
<span>{{ food.price*food.count }}</span>
</div>
<div class="cartcontrol-wrapper">
<cartcontrol
:food="food"
@add="addFood"/>
</div>
</li>
</ul>
</div>
</div>
</transition>
</div>
</template>
<script>
import cartcontrol from "./cartcontrol";
export default {
components: {
cartcontrol
},
props: {
selectFoods: {
type: Array,
default () {
return [];
}
},
deliveryPrice: {
type: Number,
default: 0
},
minPrice: {
type: Number,
default: 0
}
},
data() {
return {
balls: [{
show: false
},
{
show: false
},
{
show: false
},
{
show: false
},
{
show: false
}
],
dropBalls: [],
fold: true
};
},
computed: {
totalPrice() {
let total = 0;
this.selectFoods.forEach(food => {
total += food.price * food.count;
});
return total;
},
totalCount() {
let count = 0;
this.selectFoods.forEach(food => {
count += food.count;
});
return count;
},
payDesc() {
if (this.totalPrice === 0) {
return ${this.minPrice}元起送`;
} else if (this.totalPrice < this.minPrice) {
let diff = this.minPrice - this.totalPrice;
return `还差¥${diff}元起送`;
} else {
return "去结算";
}
},
payClass() {
if (this.totalPrice < this.minPrice) {
return "not-enough";
} else {
return "enough";
}
},
listShow() {
if (!this.totalCount) {
return false;
}
return !this.fold;
}
},
methods: {
toggleList() {
if (!this.totalCount) {
return;
}
this.fold = !this.fold;
},
hideList() {
this.fold = true;
},
empty() {
this.selectFoods.forEach(food => {
food.count = 0;
});
},
pay() {
if (this.totalPrice < this.minPrice) {
return;
}
window.alert(`支付${this.totalPrice}元`);
},
addFood(target) {
this.drop(target);
},
drop(el) {
for (let i = 0; i < this.balls.length; i++) {
let ball = this.balls[i];
if (!ball.show) {
ball.show = true;
ball.el = el;
this.dropBalls.push(ball);
return;
}
}
},
beforeDrop(el) {
let count = this.balls.length;
while (count--) {
let ball = this.balls[count];
if (ball.show) {
let rect = ball.el.getBoundingClientRect();
let x = rect.left - 32;
let y = -(window.innerHeight - rect.top - 22);
el.style.display = "";
el.style.webkitTransform = `translate3d(0,${y}px,0)`;
el.style.transform = `translate3d(0,${y}px,0)`;
let inner = el.getElementsByClassName("inner-hook")[0];
inner.style.webkitTransform = `translate3d(${x}px,0,0)`;
inner.style.transform = `translate3d(${x}px,0,0)`;
}
}
},
dropping(el, done) {
let rf = el.offsetHeight;
this.$nextTick(() => {
el.style.webkitTransform = "translate3d(0,0,0)";
el.style.transform = "translate3d(0,0,0)";
let inner = el.getElementsByClassName("inner-hook")[0];
inner.style.webkitTransform = "translate3d(0,0,0)";
inner.style.transform = "translate3d(0,0,0)";
el.addEventListener("transitionend", done);
});
},
afterDrop(el) {
let ball = this.dropBalls.shift();
if (ball) {
ball.show = false;
el.style.display = "none";
}
}
},
};
</script>
<style lang="scss">
.shopcart {
position: fixed;
left: 0;
bottom: 0;
z-index: 50;
width: 100%;
height: 48px;
background: #000;
.content {
display: flex;
background: rgba(61, 61, 63, 0.9);
font-size: 0;
color: rgba(255, 255, 255, 0.4);
.content-left {
flex: 1;
.logo-wrapper {
display: inline-block;
vertical-align: top;
position: relative;
top: -10px;
margin: 0 12px;
padding: 6px;
width: 56px;
height: 56px;
box-sizing: border-box;
border-radius: 50%;
background-image: radial-gradient(circle, #363636 6.266667vw, #444 0);
.logo {
width: 100%;
height: 100%;
border-radius: 50%;
text-align: center;
background: #2b343c;
&.highlight {
background: rgb(0, 160, 220);
}
.icon-shopping_cart {
margin: 0 auto;
padding: 20% 0;
display: block;
width: 60%;
height: 60%;
color: #80858a;
}
}
.num {
position: absolute;
top: 0;
right: 0;
width: 24px;
height: 16px;
line-height: 16px;
text-align: center;
border-radius: 16px;
font-size: 9px;
font-weight: 700;
color: #ffffff;
box-shadow: 0px 4px 8px 0 rgba(0, 0, 0, 0.4);
background-image: linear-gradient(-90deg, #ff7416, #ff3c15 98%);
}
}
.price {
display: inline-block;
vertical-align: top;
margin-top: 12px;
line-height: 24px;
padding-right: 12px;
box-sizing: border-box;
border-right: 1px solid rgba(255, 255, 255, 0.1);
font-size: 16px;
font-weight: 700;
&.highlight {
color: #fff;
}
}
.desc {
display: inline-block;
vertical-align: top;
margin: 12px 0 0 12px;
line-height: 24px;
font-size: 10px;
}
}
.content-right {
flex: 0 0 105px;
width: 105px;
.pay {
height: 48px;
line-height: 48px;
text-align: center;
font-size: 14px;
font-weight: 700;
&.not-enough {
color: #fff;
}
&.enough {
background: #00b43c;
color: #fff;
}
}
}
}
.ball-container {
.ball {
position: fixed;
left: 32px;
bottom: 22px;
z-index: 200;
&.drop-transition {
transition: all 0.4s cubic-bezier(0.49, -0.29, 0.75, 0.41);
.inner {
width: 16px;
height: 16px;
border-radius: 50%;
background: rgb(0, 160, 220);
transition: all 0.4s;
}
}
}
}
.mark {
position: fixed;
top: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.4);
z-index: -2;
}
.shopcart-list {
position: absolute;
left: 0;
top: 0;
z-index: -1;
width: 100%;
transform: translate3d(0, -100%, 0);
&.fold-enter-active,
&.fold-leave-active {
transition: all 0.5s;
}
&.fold-enter,
&.fold-leave-active {
transform: translate3d(0, 0, 0);
}
.list-header {
height: 40px;
line-height: 40px;
padding: 0 18px;
background: #f3f5f7;
border-bottom: 1px solid rgba(7, 17, 27, 0.1);
.title {
float: left;
font-size: 14px;
color: rgb(7, 17, 27);
}
.empty {
float: right;
font-size: 12px;
color: rgb(0, 160, 220);
}
}
.list-content {
padding: 0 18px;
max-height: 217px;
overflow: hidden;
background: #fff;
.food {
display: flex;
align-items: center;
position: relative;
padding: 12px 0;
box-sizing: border-box;
position: relative;
&:after {
display: block;
position: absolute;
left: 0;
bottom: 0;
width: 100%;
border: 1px solid rgba(7, 17, 27, 0.1);
content: " ";
}
.name {
line-height: 24px;
font-size: 14px;
color: rgb(7, 17, 27);
}
.price {
position: absolute;
right: 90px;
bottom: 12px;
line-height: 24px;
font-size: 14px;
font-weight: 700;
color: rgb(240, 20, 20);
}
.cartcontrol-wrapper {
position: absolute;
right: 0;
}
}
}
}
}
</style>
<template>
<div class="shop-header">
<div class="head">
<nav :style="`background-image: url('${banner}');`">
<i
class="mintui mintui-back"
@click="$router.go(-1);"/>
<img
:src="seller.avatar"
class="shop-logo">
</nav>
</div>
</div>
</template>
<script>
import config from "~/config";
export default {
props: {
seller: {
default: {}
}
},
computed: {
banner: function () {
return config.IMG_URL + this.seller.banner;
}
}
};
</script>
<style lang="scss">
@import "../../../assets/styles/mixin";
.shop-header {
background: #fff;
.head {
height: px2rem(200px);
nav {
height: px2rem(200px);
background-position: 50%;
background-size: cover;
background-repeat: no-repeat;
padding: px2rem(10px);
position: relative;
.mintui-back {
font-size: px2rem(46px);
color: #fff;
}
.shop-logo {
width: px2rem(150px);
height: px2rem(150px);
position: absolute;
bottom: -10px;
left: 50%;
transform: translateX(-50%);
}
}
}
.content {
font-size: px2rem(24px);
margin-top: px2rem(50px);
text-align: center;
h2 {
font-size: px2rem(40px);
}
.info {
color: #666;
span {
margin: 0 px2rem(10px);
}
}
}
.foot {
padding: 0 px2rem(70px);
margin-top: 10px;
font-size: px2rem(24px);
.mint-badge {
transform: scale(0.8) translateX(-10%);
border-radius: 1px;
}
.announcement {
display: inline-block;
width: 100%;
color: #666;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
}
</style>
<template>
<div class="ratings-page">
<div class="ratings-content">
<div class="overview">
<div class="overview-left">
<h1 class="score">{{ seller.score }}</h1>
<div class="title">
商家评分
<rating-star :rating="seller.score" />
</div>
</div>
<div class="overview-right">
<div class="score-wrapper">
<span class="title">味道</span>
<p class="score">{{ seller.serviceScore }}</p>
</div>
<div class="score-wrapper">
<span class="title">包装</span>
<p class="score">{{ seller.foodScore }}</p>
</div>
<div class="score-wrapper">
<span class="title">配送</span>
<p class="score">{{ seller.deliveryTime }}</p>
</div>
</div>
</div>
<div class="placeholder"/>
<ratingselect
:select-type="selectType"
:only-content="onlyContent"
:ratings="ratings"
@select="selectRating"
@toggle="toggleContent" />
<div class="rating-wrapper">
<ul>
<li
v-for="(rating, index) in ratings"
v-show="needShow(rating.rateType, rating.text)"
:key="index"
class="rating-item">
<div class="avatar">
<span :style="'background-position: 0 '+rating.avatar"/>
</div>
<div class="content">
<h1 class="name">{{ rating.username }}</h1>
<div class="star-wrapper">
<rating-star
:size="24"
:rating="rating.score" />
</div>
<p class="text">{{ rating.text }}</p>
<div
v-show="rating.recommend && rating.recommend.length"
class="recommend">
<span
v-for="(item, index) in rating.recommend"
:key="index"
class="item">{{ item }}</span>
</div>
<div class="time">
{{ rating.rateTime }}
</div>
</div>
</li>
</ul>
</div>
</div>
</div>
</template>
<script>
import ratingStar from "~/components/ratingStar";
import ratingselect from "./ratingselect";
import * as shoppingApi from "~/assets/services/shopping";
import moment from "moment";
export default {
components: {
ratingStar,
ratingselect
},
props: {
seller: {
default: {}
}
},
data() {
return {
ratings: [],
selectType: 2,
onlyContent: true
};
},
mounted() {
shoppingApi.ratings().then(res => {
if (res.code === 0) {
this.ratings = res.data;
this.ratings.map(item => {
item.rateTime = moment(item.rateTime).format("YYYY/M/D");
});
}
});
},
methods: {
needShow(type, text) {
if (this.onlyContent && !text) {
return false;
}
if (this.selectType === 2) {
return true;
} else {
return type === this.selectType;
}
},
selectRating(type) {
this.selectType = type;
},
toggleContent() {
this.onlyContent = !this.onlyContent;
}
},
};
</script>
<style lang="scss">
@import "../../../../assets/styles/mixin";
.ratings-page {
background: #fff;
.placeholder {
height: px2rem(20px);
background: #f5f5f5;
}
.overview {
display: flex;
padding: 18px 0;
.overview-left {
width: px2rem(340px);
@include fj(center);
align-items: center;
text-align: center;
.score {
font-size: px2rem(80px);
margin-right: px2rem(20px);
color: #ff6000;
font-weight: 400;
}
.title {
font-size: 12px;
}
}
.overview-right {
flex: 1;
@include fj(space-around);
align-items: center;
text-align: center;
color: #666;
.score-wrapper {
.title {
display: inline-block;
}
.score {
font-size: px2rem(40px);
}
}
}
}
.rating-wrapper {
padding: 0 18px;
.rating-item {
display: flex;
padding: 18px 0;
.avatar {
flex: 0 0 28px;
width: 28px;
margin-right: 12px;
span {
border-radius: 50%;
display: inline-block;
width: 30px;
height: 30px;
background: url("../../../../assets/images/sprite-avatar.png");
background-position: 0 0;
background-size: 30px;
}
}
.content {
position: relative;
flex: 1;
font-size: px2rem(28px);
.name {
margin-bottom: 4px;
line-height: 12px;
color: rgb(7, 17, 27);
}
.star-wrapper {
margin-bottom: 6px;
.star {
display: inline-block;
margin-right: 6px;
vertical-align: top;
}
}
.text {
margin: 8px 0;
line-height: 18px;
color: rgb(7, 17, 27);
}
.recommend {
line-height: 16px;
font-size: 0;
.icon-thumb_up,
.item {
display: inline-block;
margin: 0 8px 4px 0;
font-size: 9px;
}
.icon-thumb_up {
color: rgb(0, 160, 220);
}
.item {
padding: 0 6px;
border: 1px solid rgba(7, 17, 27, 0.1);
border-radius: 1px;
color: rgb(147, 153, 159);
background: #fff;
}
}
.time {
position: absolute;
top: 0;
right: 0;
line-height: 12px;
font-size: 10px;
color: rgb(147, 153, 159);
}
}
}
}
}
</style>
<template>
<div class="ratingselect">
<div class="rating-type border-1px">
<span
:class="{'active':selectType===2}"
class="block positive"
@click="select(2,$event)">{{ desc.all }}
<span class="count">{{ ratings.length }}</span>
</span>
<span
:class="{'active':selectType===0}"
class="block positive"
@click="select(0,$event)">{{ desc.positive }}<span
class="count">{{ positives.length }}</span></span>
<span
:class="{'active':selectType===1}"
class="block negative"
@click="select(1,$event)">{{ desc.negative }}<span
class="count">{{ negatives.length }}</span></span>
</div>
<div
:class="{'on':onlyContent}"
class="switch"
@click="toggleContent">
<!-- <span class="icon-check_circle"></span> -->
<svg
width="12"
height="12"
class="check">
<use xlink:href="#select"/>
</svg>
<span class="text">只看有内容的评价</span>
</div>
</div>
</template>
<script>
export default {
props: {
ratings: {
type: Array,
default () {
return [];
}
},
selectType: {
type: Number,
default: 2
},
onlyContent: {
type: Boolean,
default: false
},
desc: {
type: Object,
default () {
return {
all: "全部",
positive: "满意",
negative: "不满意"
};
}
}
},
computed: {
positives() {
return this.ratings.filter(rating => {
return rating.rateType === 0;
});
},
negatives() {
return this.ratings.filter(rating => {
return rating.rateType === 1;
});
}
},
methods: {
select(type, event) {
this.$emit("select", type);
},
toggleContent(event) {
this.$emit("toggle");
}
}
};
</script>
<style lang="scss">
.ratingselect {
.rating-type {
padding: 18px 0;
margin: 0 18px;
font-size: 0;
.block {
display: inline-block;
padding: 8px 12px;
margin-right: 8px;
line-height: 16px;
border-radius: 1px;
font-size: 12px;
color: rgb(77, 85, 93);
&.active {
color: #fff;
}
.count {
margin-left: 2px;
font-size: 12px;
}
&.positive {
background: rgba(0, 160, 220, 0.2);
&.active {
background: rgb(0, 160, 220);
}
}
&.negative {
background: rgba(77, 85, 93, 0.2);
&.active {
background: rgb(77, 85, 93);
}
}
}
}
.switch {
display: flex;
align-items: center;
padding: 12px 18px;
line-height: 24px;
border-bottom: 1px solid rgba(7, 17, 27, 0.1);
color: rgb(147, 153, 159);
font-size: 0;
svg {
margin-right: 5px;
fill: #e8e8e8;
}
&.on {
.check {
fill: #76d572;
}
}
.icon-check_circle {
display: inline-block;
vertical-align: top;
margin-right: 4px;
font-size: 24px;
}
.text {
display: inline-block;
vertical-align: top;
font-size: 12px;
}
}
}
</style>
<template>
<div class="seller-page">
<div class="logo">
<img
:src="banner"
alt="">
</div>
<h2>{{ seller.name }}</h2>
<p>{{ seller.bulletin }}</p>
<a
:href="seller.brandstory"
class="brandstory">查看品牌故事</a>
<div class="placeholder"/>
<h2>配送信息</h2>
<p>由蜂鸟快送提供配送,约{{ seller.deliveryTime }}分钟送达,距离2.1km</p>
<p>配送费¥{{ seller.deliveryPrice }}</p>
</div>
</template>
<script>
import config from "~/config";
export default {
props: {
seller: {
default: {}
}
},
computed: {
banner: function () {
return config.IMG_URL + this.seller.banner;
}
}
};
</script>
<style lang="scss">
@import "../../../assets/styles/mixin";
.seller-page {
background: #fff;
padding-bottom: px2rem(30px);
.logo {
padding: px2rem(30px) px2rem(30px) 0 px2rem(30px);
}
h2 {
font-size: px2rem(32px);
margin-top: px2rem(40px);
padding: 0 px2rem(30px) px2rem(20px) px2rem(30px);
}
p {
padding: 0 px2rem(30px);
color: #666;
font-size: px2rem(26px);
line-height: 1.5;
}
.brandstory {
display: block;
line-height: px2rem(100px);
margin-top: px2rem(50px);
color: #666;
font-size: px2rem(28px);
text-align: center;
}
.placeholder {
height: px2rem(20px);
background: #f5f5f5;
}
}
</style>
......@@ -13,7 +13,6 @@
@next="next"
@change="changeBanner"
@click="bannerClick"/>
<div class="main-padding">{{ totalCount }}个营地</div>
<ul
v-if="dataDetail.length"
id="scroll-area"
......@@ -40,7 +39,7 @@
class="loading">加载中……</p>
<p
v-if="touchend"
class="empty_data">——没有更多了——</p>
class="empty_data">——{{ totalCount }}个房车营地——</p>
<go-top/>
<Footer v-if="touchend"/>
</div>
......
......@@ -42,7 +42,7 @@
class="loading">加载中……</p>
<p
v-if="touchend"
class="empty_data">——没有更多了——</p>
class="empty_data">——{{ totalCount }}篇新闻资讯——</p>
<go-top/>
<Footer v-if="touchend"/>
</div>
......
......@@ -13,7 +13,6 @@
@next="next"
@change="changeBanner"
@click="bannerClick"/>
<div class="main-padding total-div">{{ totalCount }}个旅行路线</div>
<ul
v-if="dataDetail.length"
id="scroll-area"
......@@ -38,7 +37,7 @@
class="loading">加载中……</p>
<p
v-if="touchend"
class="empty_data">——没有更多了——</p>
class="empty_data">——{{ totalCount }}个旅行路线——</p>
<go-top/>
<Footer v-if="touchend"/>
</div>
......
<template>
<div class="cartcontrol">
<svg
v-show="food.count>0"
class="inner"
fill="rgb(35, 149, 255)"
@click.stop.prevent="decreaseCart"><use xlink:href="#cart-minus"/></svg>
<div
v-show="food.count>0"
class="cart-count">{{ food.count }}</div>
<svg
width="24"
height="24"
fill="rgb(35, 149, 255)"
@click="addCart"><use xlink:href="#cart-add"/></svg>
</div>
</template>
<script>
import Vue from "vue";
export default {
props: ['food'],
methods: {
addCart(event) {
if (!this.food.count) {
Vue.set(this.food, "count", 1);
} else {
this.food.count++;
}
this.$emit("add", event.target);
},
decreaseCart(event) {
if (this.food.count) {
this.food.count--;
}
}
}
};
</script>
<style lang="scss">
.cartcontrol {
display: flex;
align-items: center;
.inner {
width: 24px;
height: 24px;
}
.cart-count {
display: inline-block;
vertical-align: top;
width: 25px;
text-align: center;
font-size: 15px;
color: #333;
}
.cart-add {
display: inline-block;
padding: 6px;
line-height: 24px;
font-size: 24px;
color: rgb(0, 160, 220);
}
}
</style>
<template>
<div class="goods-page">
<div class="menu-wrapper">
<ul>
<li
v-for="(item, index) in goods"
:key="index"
:class="{'current':currentIndex === index}"
class="menu-item"
@click="selectMenu(index,$event)">
<span class="text">
<!-- <span v-show="item.type>0" class="icon" :class="classMap[item.type]"></span> -->
{{ item.name }}
</span>
</li>
</ul>
</div>
<div class="foods-wrapper">
<ul>
<li
v-for="(item, index) in goods"
:key="index"
class="food-list food-list-hook">
<h1
:id="'a'+index"
class="title">{{ item.name }}</h1>
<ul>
<li
v-for="(food, index) in item.foods"
:key="index"
class="food-item border-1px">
<div class="icon">
<img :src="food.icon" >
</div>
<div class="content">
<h2 class="name">{{ food.name }}</h2>
<p class="desc">{{ food.description }}</p>
<div class="extra">
<span class="count">月售{{ food.sellCount }}</span><span>好评率{{ food.rating }}%</span>
</div>
<div class="price">
<span class="now"><b>{{ food.price }}</b></span>
<cartcontrol :food="food" />
</div>
</div>
</li>
</ul>
</li>
</ul>
</div>
<shopcart
ref="shopcart"
:select-foods="selectFoods"
:delivery-price="seller.deliveryPrice"
:min-price="seller.minPrice"/>
</div>
</template>
<script>
import shopcart from "./shopcart";
import cartcontrol from "./cartcontrol";
import * as shoppingApi from "~/assets/services/shopping";
export default {
components: {
shopcart,
cartcontrol
},
props: {
seller: {
default: {}
}
},
data() {
return {
goods: [],
listHeight: [],
scrollY: 0,
classMap: ["decrease", "discount", "special", "invoice", "guarantee"],
currentIndex: 0,
isScroll: false
};
},
computed: {
selectFoods() {
let foods = [];
this.goods.forEach(good => {
good.foods.forEach(food => {
if (food.count) {
foods.push(food);
}
});
});
return foods;
}
},
created() {},
mounted() {
shoppingApi.goods().then(res => {
if (res.code === 0) {
this.goods = res.data;
}
});
//菜品滚动选择类目
document.querySelector(".foods-wrapper").addEventListener(
"scroll",
this.throttle(() => {
// 防止手动选择的时候误操作
if (this.isScroll) {
return;
}
this.goods.map((item, index) => {
const rect_top = document
.querySelector("#a" + index)
.getBoundingClientRect().top;
const container_top = document
.querySelector(".foods-wrapper")
.getBoundingClientRect().top;
if (
rect_top - container_top < 100 &&
rect_top - container_top > -100
) {
this.currentIndex = index;
}
});
}, 50)
);
},
methods: {
selectMenu(index) {
if (index === this.currentIndex) {
return;
}
this.currentIndex = index;
this.animateScroll("#a" + index, ".foods-wrapper", 50);
},
// 平滑滚动方法
animateScroll(element, container, speed) {
this.isScroll = true;
let rect =
document.querySelector(element).getBoundingClientRect().top -
document.querySelector(container).getBoundingClientRect().top;
//获取元素相对窗口的top值,此处应加上窗口本身的偏移
let top = rect + document.querySelector(container).scrollTop;
let currentTop = 0;
let requestId;
//采用requestAnimationFrame,平滑动画
const step = timestamp => {
if (currentTop <= top) {
document.querySelector(container).scrollTo(0, currentTop);
requestId = window.requestAnimationFrame(step);
} else {
window.cancelAnimationFrame(requestId);
this.isScroll = false;
}
currentTop += speed;
};
window.requestAnimationFrame(step);
},
// 函数防抖
throttle(method, delay) {
var timer = null;
return function () {
clearTimeout(timer);
timer = setTimeout(() => {
method.apply(this, arguments);
}, delay);
};
}
},
};
</script>
<style lang="scss">
@import "../../../../assets/styles/mixin";
.goods-page {
display: flex;
background: #fff;
width: 100%;
.menu-wrapper {
flex: 0 0 80px;
width: 80px;
height: calc(100vh - 38px);
overflow-x: hidden;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
background: #f8f8f8;
.menu-item {
display: table;
height: 54px;
padding: 0 12px;
line-height: 14px;
&.current {
position: relative;
margin-top: -1px;
z-index: 10;
background: #ffffff;
.text {
color: #333;
}
}
.icon {
display: inline-block;
vertical-align: middle;
width: 12px;
height: 12px;
margin-right: 2px;
background-size: 12px 12px;
background-repeat: no-repeat;
// &.decrease {
// bg-image('decrease_3');
// }
// &.discount {
// bg-image('discount_3');
// }
// &.guarantee {
// bg-image('guarantee_3');
// }
// &.invoice {
// bg-image('invoice_3');
// }
// &.special {
// bg-image('special_3');
// }
}
.text {
display: table-cell;
width: 56px;
vertical-align: middle;
font-size: 12px;
position: relative;
color: #666;
}
}
}
.foods-wrapper {
width: calc(100vw - 80px);
height: calc(100vh - 38px);
overflow-x: hidden;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
padding-bottom: 60px;
.title {
padding-left: 14px;
height: 30px;
line-height: 30px;
font-size: 12px;
color: #666;
}
.food-item {
display: flex;
margin: 10px;
padding-bottom: 18px;
position: relative;
&:after {
display: block;
position: absolute;
left: 0;
bottom: 0;
width: 100%;
content: " ";
}
&:last-child {
margin-bottom: 0;
&:after {
display: none;
}
}
.icon {
flex: 0 0 80px;
margin-right: 10px;
}
.content {
flex: 1;
position: relative;
.name {
margin: 2px 0 8px 0px;
height: 14px;
line-height: 14px;
font-size: 14px;
color: rgb(7, 17, 27);
}
.desc,
.extra {
line-height: 10px;
font-size: 12px;
color: rgb(147, 153, 159);
transform: scale(0.9) translateX(-6%);
}
.desc {
margin-bottom: 8px;
line-height: 12px;
max-width: px2rem(350px);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.extra {
.count {
margin-right: 12px;
}
}
.price {
width: 100%;
font-weight: 700;
position: absolute;
bottom: 0;
@include fj(space-between);
.now {
margin-right: 8px;
font-size: 14px;
color: rgb(255, 83, 57);
}
.old {
text-decoration: line-through;
font-size: 10px;
color: rgb(147, 153, 159);
}
b {
font-weight: normal;
font-size: 18px;
}
}
}
}
}
}
</style>
<template>
<div class="shopcart">
<div
class="content"
@click="toggleList">
<div class="content-left">
<div class="logo-wrapper">
<div
:class="{'highlight':totalCount>0}"
class="logo">
<span
v-if="totalCount<=0"
class="icon-shopping_cart">
<img
src="~/assets/images/cart.svg"
alt="">
</span>
<span
v-else
class="icon-shopping_cart highlight">
<img
src="~/assets/images/cart2.svg"
alt="">
</span>
</div>
<div
v-show="totalCount>0"
class="num">{{ totalCount }}</div>
</div>
<div
:class="{'highlight':totalPrice>0}"
class="price">¥{{ totalPrice }}</div>
<div class="desc">另需配送费¥{{ deliveryPrice }}</div>
</div>
<div class="content-right">
<div
:class="payClass"
class="pay">{{ payDesc }}</div>
</div>
</div>
<div class="ball-container">
<div
v-for="(ball, index) in balls"
:key="index">
<transition
name="drop"
@before-enter="beforeDrop"
@enter="dropping"
@after-enter="afterDrop">
<div
v-show="ball.show"
class="ball">
<div class="inner inner-hook"/>
</div>
</transition>
</div>
</div>
<div
v-show="listShow"
class="mark"
@click="toggleList"/>
<transition name="fold">
<div
v-show="listShow"
class="shopcart-list">
<div class="list-header">
<h1 class="title">已选商品</h1>
<span
class="empty"
@click="empty">清空</span>
</div>
<div
ref="listContent"
class="list-content">
<ul>
<li
v-for="(food, index) in selectFoods"
:key="index"
class="food">
<span class="name">{{ food.name }}</span>
<div class="price">
<span>{{ food.price*food.count }}</span>
</div>
<div class="cartcontrol-wrapper">
<cartcontrol
:food="food"
@add="addFood"/>
</div>
</li>
</ul>
</div>
</div>
</transition>
</div>
</template>
<script>
import cartcontrol from "./cartcontrol";
export default {
components: {
cartcontrol
},
props: {
selectFoods: {
type: Array,
default () {
return [];
}
},
deliveryPrice: {
type: Number,
default: 0
},
minPrice: {
type: Number,
default: 0
}
},
data() {
return {
balls: [{
show: false
},
{
show: false
},
{
show: false
},
{
show: false
},
{
show: false
}
],
dropBalls: [],
fold: true
};
},
computed: {
totalPrice() {
let total = 0;
this.selectFoods.forEach(food => {
total += food.price * food.count;
});
return total;
},
totalCount() {
let count = 0;
this.selectFoods.forEach(food => {
count += food.count;
});
return count;
},
payDesc() {
if (this.totalPrice === 0) {
return ${this.minPrice}元起送`;
} else if (this.totalPrice < this.minPrice) {
let diff = this.minPrice - this.totalPrice;
return `还差¥${diff}元起送`;
} else {
return "去结算";
}
},
payClass() {
if (this.totalPrice < this.minPrice) {
return "not-enough";
} else {
return "enough";
}
},
listShow() {
if (!this.totalCount) {
return false;
}
return !this.fold;
}
},
methods: {
toggleList() {
if (!this.totalCount) {
return;
}
this.fold = !this.fold;
},
hideList() {
this.fold = true;
},
empty() {
this.selectFoods.forEach(food => {
food.count = 0;
});
},
pay() {
if (this.totalPrice < this.minPrice) {
return;
}
window.alert(`支付${this.totalPrice}元`);
},
addFood(target) {
this.drop(target);
},
drop(el) {
for (let i = 0; i < this.balls.length; i++) {
let ball = this.balls[i];
if (!ball.show) {
ball.show = true;
ball.el = el;
this.dropBalls.push(ball);
return;
}
}
},
beforeDrop(el) {
let count = this.balls.length;
while (count--) {
let ball = this.balls[count];
if (ball.show) {
let rect = ball.el.getBoundingClientRect();
let x = rect.left - 32;
let y = -(window.innerHeight - rect.top - 22);
el.style.display = "";
el.style.webkitTransform = `translate3d(0,${y}px,0)`;
el.style.transform = `translate3d(0,${y}px,0)`;
let inner = el.getElementsByClassName("inner-hook")[0];
inner.style.webkitTransform = `translate3d(${x}px,0,0)`;
inner.style.transform = `translate3d(${x}px,0,0)`;
}
}
},
dropping(el, done) {
let rf = el.offsetHeight;
this.$nextTick(() => {
el.style.webkitTransform = "translate3d(0,0,0)";
el.style.transform = "translate3d(0,0,0)";
let inner = el.getElementsByClassName("inner-hook")[0];
inner.style.webkitTransform = "translate3d(0,0,0)";
inner.style.transform = "translate3d(0,0,0)";
el.addEventListener("transitionend", done);
});
},
afterDrop(el) {
let ball = this.dropBalls.shift();
if (ball) {
ball.show = false;
el.style.display = "none";
}
}
},
};
</script>
<style lang="scss">
.shopcart {
position: fixed;
left: 0;
bottom: 0;
z-index: 50;
width: 100%;
height: 48px;
background: #000;
.content {
display: flex;
background: rgba(61, 61, 63, 0.9);
font-size: 0;
color: rgba(255, 255, 255, 0.4);
.content-left {
flex: 1;
.logo-wrapper {
display: inline-block;
vertical-align: top;
position: relative;
top: -10px;
margin: 0 12px;
padding: 6px;
width: 56px;
height: 56px;
box-sizing: border-box;
border-radius: 50%;
background-image: radial-gradient(circle, #363636 6.266667vw, #444 0);
.logo {
width: 100%;
height: 100%;
border-radius: 50%;
text-align: center;
background: #2b343c;
&.highlight {
background: rgb(0, 160, 220);
}
.icon-shopping_cart {
margin: 0 auto;
padding: 20% 0;
display: block;
width: 60%;
height: 60%;
color: #80858a;
}
}
.num {
position: absolute;
top: 0;
right: 0;
width: 24px;
height: 16px;
line-height: 16px;
text-align: center;
border-radius: 16px;
font-size: 9px;
font-weight: 700;
color: #ffffff;
box-shadow: 0px 4px 8px 0 rgba(0, 0, 0, 0.4);
background-image: linear-gradient(-90deg, #ff7416, #ff3c15 98%);
}
}
.price {
display: inline-block;
vertical-align: top;
margin-top: 12px;
line-height: 24px;
padding-right: 12px;
box-sizing: border-box;
border-right: 1px solid rgba(255, 255, 255, 0.1);
font-size: 16px;
font-weight: 700;
&.highlight {
color: #fff;
}
}
.desc {
display: inline-block;
vertical-align: top;
margin: 12px 0 0 12px;
line-height: 24px;
font-size: 10px;
}
}
.content-right {
flex: 0 0 105px;
width: 105px;
.pay {
height: 48px;
line-height: 48px;
text-align: center;
font-size: 14px;
font-weight: 700;
&.not-enough {
color: #fff;
}
&.enough {
background: #00b43c;
color: #fff;
}
}
}
}
.ball-container {
.ball {
position: fixed;
left: 32px;
bottom: 22px;
z-index: 200;
&.drop-transition {
transition: all 0.4s cubic-bezier(0.49, -0.29, 0.75, 0.41);
.inner {
width: 16px;
height: 16px;
border-radius: 50%;
background: rgb(0, 160, 220);
transition: all 0.4s;
}
}
}
}
.mark {
position: fixed;
top: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.4);
z-index: -2;
}
.shopcart-list {
position: absolute;
left: 0;
top: 0;
z-index: -1;
width: 100%;
transform: translate3d(0, -100%, 0);
&.fold-enter-active,
&.fold-leave-active {
transition: all 0.5s;
}
&.fold-enter,
&.fold-leave-active {
transform: translate3d(0, 0, 0);
}
.list-header {
height: 40px;
line-height: 40px;
padding: 0 18px;
background: #f3f5f7;
border-bottom: 1px solid rgba(7, 17, 27, 0.1);
.title {
float: left;
font-size: 14px;
color: rgb(7, 17, 27);
}
.empty {
float: right;
font-size: 12px;
color: rgb(0, 160, 220);
}
}
.list-content {
padding: 0 18px;
max-height: 217px;
overflow: hidden;
background: #fff;
.food {
display: flex;
align-items: center;
position: relative;
padding: 12px 0;
box-sizing: border-box;
position: relative;
&:after {
display: block;
position: absolute;
left: 0;
bottom: 0;
width: 100%;
border: 1px solid rgba(7, 17, 27, 0.1);
content: " ";
}
.name {
line-height: 24px;
font-size: 14px;
color: rgb(7, 17, 27);
}
.price {
position: absolute;
right: 90px;
bottom: 12px;
line-height: 24px;
font-size: 14px;
font-weight: 700;
color: rgb(240, 20, 20);
}
.cartcontrol-wrapper {
position: absolute;
right: 0;
}
}
}
}
}
</style>
<template>
<div class="shop-header">
<div class="head">
<nav :style="`background-image: url('${banner}');`">
<i
class="mintui mintui-back"
@click="$router.go(-1);"/>
<img
:src="seller.avatar"
class="shop-logo">
</nav>
</div>
</div>
</template>
<script>
import config from "~/config";
export default {
props: {
seller: {
default: {}
}
},
computed: {
banner: function () {
return config.IMG_URL + this.seller.banner;
}
}
};
</script>
<style lang="scss">
@import "../../../assets/styles/mixin";
.shop-header {
background: #fff;
.head {
height: px2rem(200px);
nav {
height: px2rem(200px);
background-position: 50%;
background-size: cover;
background-repeat: no-repeat;
padding: px2rem(10px);
position: relative;
.mintui-back {
font-size: px2rem(46px);
color: #fff;
}
.shop-logo {
width: px2rem(150px);
height: px2rem(150px);
position: absolute;
bottom: -10px;
left: 50%;
transform: translateX(-50%);
}
}
}
.content {
font-size: px2rem(24px);
margin-top: px2rem(50px);
text-align: center;
h2 {
font-size: px2rem(40px);
}
.info {
color: #666;
span {
margin: 0 px2rem(10px);
}
}
}
.foot {
padding: 0 px2rem(70px);
margin-top: 10px;
font-size: px2rem(24px);
.mint-badge {
transform: scale(0.8) translateX(-10%);
border-radius: 1px;
}
.announcement {
display: inline-block;
width: 100%;
color: #666;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
}
</style>
<template>
<div class="ratings-page">
<div class="ratings-content">
<div class="overview">
<div class="overview-left">
<h1 class="score">{{ seller.score }}</h1>
<div class="title">
商家评分
<rating-star :rating="seller.score" />
</div>
</div>
<div class="overview-right">
<div class="score-wrapper">
<span class="title">味道</span>
<p class="score">{{ seller.serviceScore }}</p>
</div>
<div class="score-wrapper">
<span class="title">包装</span>
<p class="score">{{ seller.foodScore }}</p>
</div>
<div class="score-wrapper">
<span class="title">配送</span>
<p class="score">{{ seller.deliveryTime }}</p>
</div>
</div>
</div>
<div class="placeholder"/>
<ratingselect
:select-type="selectType"
:only-content="onlyContent"
:ratings="ratings"
@select="selectRating"
@toggle="toggleContent" />
<div class="rating-wrapper">
<ul>
<li
v-for="(rating, index) in ratings"
v-show="needShow(rating.rateType, rating.text)"
:key="index"
class="rating-item">
<div class="avatar">
<span :style="'background-position: 0 '+rating.avatar"/>
</div>
<div class="content">
<h1 class="name">{{ rating.username }}</h1>
<div class="star-wrapper">
<rating-star
:size="24"
:rating="rating.score" />
</div>
<p class="text">{{ rating.text }}</p>
<div
v-show="rating.recommend && rating.recommend.length"
class="recommend">
<span
v-for="(item, index) in rating.recommend"
:key="index"
class="item">{{ item }}</span>
</div>
<div class="time">
{{ rating.rateTime }}
</div>
</div>
</li>
</ul>
</div>
</div>
</div>
</template>
<script>
import ratingStar from "~/components/ratingStar";
import ratingselect from "./ratingselect";
import * as shoppingApi from "~/assets/services/shopping";
import moment from "moment";
export default {
components: {
ratingStar,
ratingselect
},
props: {
seller: {
default: {}
}
},
data() {
return {
ratings: [],
selectType: 2,
onlyContent: true
};
},
mounted() {
shoppingApi.ratings().then(res => {
if (res.code === 0) {
this.ratings = res.data;
this.ratings.map(item => {
item.rateTime = moment(item.rateTime).format("YYYY/M/D");
});
}
});
},
methods: {
needShow(type, text) {
if (this.onlyContent && !text) {
return false;
}
if (this.selectType === 2) {
return true;
} else {
return type === this.selectType;
}
},
selectRating(type) {
this.selectType = type;
},
toggleContent() {
this.onlyContent = !this.onlyContent;
}
},
};
</script>
<style lang="scss">
@import "../../../../assets/styles/mixin";
.ratings-page {
background: #fff;
.placeholder {
height: px2rem(20px);
background: #f5f5f5;
}
.overview {
display: flex;
padding: 18px 0;
.overview-left {
width: px2rem(340px);
@include fj(center);
align-items: center;
text-align: center;
.score {
font-size: px2rem(80px);
margin-right: px2rem(20px);
color: #ff6000;
font-weight: 400;
}
.title {
font-size: 12px;
}
}
.overview-right {
flex: 1;
@include fj(space-around);
align-items: center;
text-align: center;
color: #666;
.score-wrapper {
.title {
display: inline-block;
}
.score {
font-size: px2rem(40px);
}
}
}
}
.rating-wrapper {
padding: 0 18px;
.rating-item {
display: flex;
padding: 18px 0;
.avatar {
flex: 0 0 28px;
width: 28px;
margin-right: 12px;
span {
border-radius: 50%;
display: inline-block;
width: 30px;
height: 30px;
background: url("../../../../assets/images/sprite-avatar.png");
background-position: 0 0;
background-size: 30px;
}
}
.content {
position: relative;
flex: 1;
font-size: px2rem(28px);
.name {
margin-bottom: 4px;
line-height: 12px;
color: rgb(7, 17, 27);
}
.star-wrapper {
margin-bottom: 6px;
.star {
display: inline-block;
margin-right: 6px;
vertical-align: top;
}
}
.text {
margin: 8px 0;
line-height: 18px;
color: rgb(7, 17, 27);
}
.recommend {
line-height: 16px;
font-size: 0;
.icon-thumb_up,
.item {
display: inline-block;
margin: 0 8px 4px 0;
font-size: 9px;
}
.icon-thumb_up {
color: rgb(0, 160, 220);
}
.item {
padding: 0 6px;
border: 1px solid rgba(7, 17, 27, 0.1);
border-radius: 1px;
color: rgb(147, 153, 159);
background: #fff;
}
}
.time {
position: absolute;
top: 0;
right: 0;
line-height: 12px;
font-size: 10px;
color: rgb(147, 153, 159);
}
}
}
}
}
</style>
<template>
<div class="ratingselect">
<div class="rating-type border-1px">
<span
:class="{'active':selectType===2}"
class="block positive"
@click="select(2,$event)">{{ desc.all }}
<span class="count">{{ ratings.length }}</span>
</span>
<span
:class="{'active':selectType===0}"
class="block positive"
@click="select(0,$event)">{{ desc.positive }}<span
class="count">{{ positives.length }}</span></span>
<span
:class="{'active':selectType===1}"
class="block negative"
@click="select(1,$event)">{{ desc.negative }}<span
class="count">{{ negatives.length }}</span></span>
</div>
<div
:class="{'on':onlyContent}"
class="switch"
@click="toggleContent">
<!-- <span class="icon-check_circle"></span> -->
<svg
width="12"
height="12"
class="check">
<use xlink:href="#select"/>
</svg>
<span class="text">只看有内容的评价</span>
</div>
</div>
</template>
<script>
export default {
props: {
ratings: {
type: Array,
default () {
return [];
}
},
selectType: {
type: Number,
default: 2
},
onlyContent: {
type: Boolean,
default: false
},
desc: {
type: Object,
default () {
return {
all: "全部",
positive: "满意",
negative: "不满意"
};
}
}
},
computed: {
positives() {
return this.ratings.filter(rating => {
return rating.rateType === 0;
});
},
negatives() {
return this.ratings.filter(rating => {
return rating.rateType === 1;
});
}
},
methods: {
select(type, event) {
this.$emit("select", type);
},
toggleContent(event) {
this.$emit("toggle");
}
}
};
</script>
<style lang="scss">
.ratingselect {
.rating-type {
padding: 18px 0;
margin: 0 18px;
font-size: 0;
.block {
display: inline-block;
padding: 8px 12px;
margin-right: 8px;
line-height: 16px;
border-radius: 1px;
font-size: 12px;
color: rgb(77, 85, 93);
&.active {
color: #fff;
}
.count {
margin-left: 2px;
font-size: 12px;
}
&.positive {
background: rgba(0, 160, 220, 0.2);
&.active {
background: rgb(0, 160, 220);
}
}
&.negative {
background: rgba(77, 85, 93, 0.2);
&.active {
background: rgb(77, 85, 93);
}
}
}
}
.switch {
display: flex;
align-items: center;
padding: 12px 18px;
line-height: 24px;
border-bottom: 1px solid rgba(7, 17, 27, 0.1);
color: rgb(147, 153, 159);
font-size: 0;
svg {
margin-right: 5px;
fill: #e8e8e8;
}
&.on {
.check {
fill: #76d572;
}
}
.icon-check_circle {
display: inline-block;
vertical-align: top;
margin-right: 4px;
font-size: 24px;
}
.text {
display: inline-block;
vertical-align: top;
font-size: 12px;
}
}
}
</style>
<template>
<div class="seller-page">
<div class="logo">
<img
:src="banner"
alt="">
</div>
<h2>{{ seller.name }}</h2>
<p>{{ seller.bulletin }}</p>
<a
:href="seller.brandstory"
class="brandstory">查看品牌故事</a>
<div class="placeholder"/>
<h2>配送信息</h2>
<p>由蜂鸟快送提供配送,约{{ seller.deliveryTime }}分钟送达,距离2.1km</p>
<p>配送费¥{{ seller.deliveryPrice }}</p>
</div>
</template>
<script>
import config from "~/config";
export default {
props: {
seller: {
default: {}
}
},
computed: {
banner: function () {
return config.IMG_URL + this.seller.banner;
}
}
};
</script>
<style lang="scss">
@import "../../../assets/styles/mixin";
.seller-page {
background: #fff;
padding-bottom: px2rem(30px);
.logo {
padding: px2rem(30px) px2rem(30px) 0 px2rem(30px);
}
h2 {
font-size: px2rem(32px);
margin-top: px2rem(40px);
padding: 0 px2rem(30px) px2rem(20px) px2rem(30px);
}
p {
padding: 0 px2rem(30px);
color: #666;
font-size: px2rem(26px);
line-height: 1.5;
}
.brandstory {
display: block;
line-height: px2rem(100px);
margin-top: px2rem(50px);
color: #666;
font-size: px2rem(28px);
text-align: center;
}
.placeholder {
height: px2rem(20px);
background: #f5f5f5;
}
}
</style>
<template>
<div class="shop-page">
<Header :seller="seller" />
<mt-navbar v-model="selected">
<mt-tab-item id="goods">点餐</mt-tab-item>
<mt-tab-item id="ratings">评价</mt-tab-item>
<mt-tab-item id="seller">商家</mt-tab-item>
</mt-navbar>
<mt-tab-container v-model="selected">
<mt-tab-container-item id="goods">
<Goods :seller="seller" />
</mt-tab-container-item>
<mt-tab-container-item id="ratings">
<Ratings :seller="seller" />
</mt-tab-container-item>
<mt-tab-container-item id="seller">
<Seller :seller="seller" />
</mt-tab-container-item>
</mt-tab-container>
<div
class="tour-detail-page"
style="margin-top:1.87733rem;">
<Head/>
<detail-banner
:list = "detail.pictureList"
:looptime="looptime"
:width="width"
:height="height"
:background="background"
:color="color"
class="mb-50"
@prev="prev"
@next="next"
@change="changeBanner"
@click="bannerClick"/>
<div class="detail-main-container">
<div>
<h1 class="tour-detail-name">{{ detail.name }}</h1>
<div class="tour-detail-info">
<div
class="flex-aic">
<span style="color: #666;font-size: 14px;">出发地</span>
<div>{{ detail.addressTo.provinceName }} {{ detail.addressTo.cityName }} {{ detail.addressTo.address }}</div>
</div>
<div
class="flex-aic"
style="margin: 13px 0;">
<span style="color: #666;font-size: 14px;">目的地</span>
<div> {{ detail.addressGo.provinceName }} {{ detail.addressGo.cityName }} {{ detail.addressGo.address }}</div>
</div>
<div class="flex-aic">
<span style="color: #666;font-size: 14px;">最近出发日期</span>
<div>{{ detail.time.startTime| formatDate('yyyy/MM/dd') }} {{ detail.time.weekStart }} {{ detail.addressTo.departTime | formatDate('hh:mm') }} ~ {{ detail.time.endTime| formatDate('yyyy/MM/dd') }} {{ detail.time.weekEnd }}</div>
</div>
</div>
</div>
<div>
<div style="height: 10px;background: #eee;"/>
</div>
<div
id="nav"
:class="searchBarFixed == true ? 'isFixed' :''"
class="scroll">
<div
style="border-bottom: 1px solid #dfdfdf;"
class="flex-aic-jcb detail-list" >
<ul class="select-tab flex-aic-jcb">
<li
:class="{active:active=='activity'}"
class="select-tab-li-l mui-control-item"
@click="activity()">行程亮点</li>
<li
:class="{ active:active=='lastest'}"
class="select-tab-li-l mui-control-item"
@click="lastest()">行程介绍</li>
<li
:class="{ active:active=='rank'}"
class="select-tab-li-l mui-control-item"
@click="rank()" >报名须知</li>
</ul>
</div>
</div>
<div id="segmentedControlContents">
<!--详细内容-->
<div
id="activityIntro"
class="scroll0" >
<div class="base-set">
行程亮点
</div>
<div
class="bt-line"
v-html="detail.content"/>
</div>
<div
id="lastestDubbing"
class="scroll1" >
<div class="base-set">
行程介绍
</div>
<div
class="bt-line"
v-html="detail.introduce"/>
</div>
<div
id="rankLlistDetail"
class="scroll3">
<div class="base-set" >
报名须知
</div>
<div
class="bt-line"
v-html="notice[0].value"/>
</div>
</div>
</div>
<go-top/>
</div>
</template>
<script>
import Header from "./components/header";
import Goods from "./components/goods";
import Ratings from "./components/ratings";
import Seller from "./components/seller";
import * as shoppingApi from "~/assets/services/shopping";
import axios from '../../plugins/axios'
import Head from "~/components/head";
import DetailBanner from '~/components/detailBanner'
import GoTop from "~/components/goTop";
export default {
components: {
Header,
Goods,
Ratings,
Seller
Head,
DetailBanner,
GoTop
},
data() {
return {
selected: "goods",
seller: {}
showBanner: !0,
looptime: 4000, // 循环时间
touchend: false,////没有更多数据
dataType: [],
totalCount: "",
dataDetail: [],
width: 400,
height:200,
background: 'red',
color: '#fff',
fontSize: '70px',
active: 'activity',
searchBarFixed:false,
};
},
async asyncData() {
const {
data
} = await shoppingApi.seller();
return {
seller: data
};
async asyncData({ route }) {
const { data } = await axios.get("/api/uccn/app/unauth/tour/detail/" + route.query.id);
let user = data.data;
let pic = [];
data.data.bannerDTOS.map(function(item){
pic.push(item.cover);
});
user.pictureList = pic;
user.pictureNum = pic.length;
let startAddress = data.data.tourDepartVo;
for(var i = 0;i<startAddress.length;i++){
if(startAddress[i].type == 0){
console.log(startAddress[i]);
user.addressTo = startAddress[i];
}
if(startAddress[i].type == 2){
console.log(startAddress[i]);
user.addressGo = startAddress[i];
}
}
let tourTimeVo = data.data.tourDepartTimeVo
for(var i = 0;i<tourTimeVo.length;i++){
if(tourTimeVo[i].overdue == 0){
console.log(tourTimeVo[i]);
user.time = tourTimeVo[i];
break
}else{
user.time = tourTimeVo[i];
}
}
var weekday=["周日","周一","周二","周三","周四","周五","周六"]
user.time.weekStart = weekday[user.time.startWeek];
user.time.weekEnd = weekday[user.time.endWeek];
if (!user) {
return error({ message: 'User not found', statusCode: 404 })
}
let noticeData = await axios.get('/api/app/cofig/app/unauth/types?types=14');
let notice = noticeData.data.data;
return {detail: user, notice: notice}
},
created() {
// vue resource
// this.$http.get('api/seller').then((res) => {
// res = res.body;
// if(res.errno === ERR_OK){
// this.seller = res.data;
// console.log(this.seller);
// }
// })
window.addEventListener('scroll', this.handleScroll);
},
methods: {
// 点击下一页回调
prev () {},
// 点击下一页回调
next () {},
// 鼠标移入状态点回调
changeBanner () {},
bannerClick () {},
handleScroll: function () {
let scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop;
let path = $nuxt.$route.path;
if(path == '/tourDetail'){
var menuTop = document.querySelector('#nav').offsetTop;
//滑动到指定位置菜单吸顶
if (scrollTop > menuTop + 180) {
this.searchBarFixed = true;
}
//滑动到指定位置相应菜单高亮
var top1 = document.querySelector('#activityIntro').offsetTop;
var top2 = document.querySelector('#lastestDubbing').offsetTop;
var top3 = document.querySelector('#rankLlistDetail').offsetTop;
console.log(top1+"-"+top2+"--"+top3+"--"+menuTop);
if (scrollTop < top1 + 180) {
this.active = 'activity';
this.searchBarFixed = false;
}
if (scrollTop < top2+180) {
this.active = 'activity';
} else if (scrollTop >= top2+180 && scrollTop < top3+180) {
this.active = 'lastest';
} else if (scrollTop >= top3+180) {
this.active = 'rank';
}
}
},
lastest(){//行程介绍
this.active = "lastest";
let top1 = document.querySelector('#lastestDubbing').offsetTop;
if(this.searchBarFixed){
document.documentElement.scrollTop = top1 + 180;
} else {
document.documentElement.scrollTop = top1+130;
}
this.searchBarFixed = true;
},
activity(){
//行程亮点
this.active = "activity";
let top = document.querySelector('#nav').offsetTop;
var top1 = document.querySelector('#activityIntro').offsetTop;
if(this.searchBarFixed){
document.documentElement.scrollTop = top1 + 180;
} else {
document.documentElement.scrollTop = top1+130;
}
this.searchBarFixed = true;
},
rank(){
//报名须知
this.active = "rank";
let top1 = document.querySelector('#rankLlistDetail').offsetTop;
if(this.searchBarFixed){
document.documentElement.scrollTop = top1 + 180;
} else {
document.documentElement.scrollTop = top1+130;
}
this.searchBarFixed = true;
},
},
};
......@@ -65,8 +250,107 @@
<style lang="scss">
@import "../../assets/styles/mixin";
.shop-page {
font-size: px2rem(24px);
.tour-detail-page {
font-size: px2rem(26px);
.tour-detail-name{
padding: 15px px2rem(20px);
font-size: px2rem(34px);
font-weight: bold;
border-bottom: 1px solid #dfdfdf;
}
.tour-detail-info{
padding: 13px px2rem(20px);
font-size: 14px;
span{
color: #666;
min-width: 60px;
margin-right: 13px;
}
}
}
.detail-main-container{
border-radius: 7px 7px 0 0;
box-shadow: 0px 0px 13px #666;
margin-top: -10px;
z-index: 99;
position: relative;
background: #fff;
.vehicle-detail-name{
padding: 14px px2rem(20px) 0 px2rem(20px);
font-size: px2rem(34px);
font-weight: bold;
}
.vehicle-detail-config{
padding: 0 px2rem(20px);
border-bottom: 1px solid #dfdfdf;
.config{
margin-right: 30px;
margin-bottom: 13px;
margin-top: 13px;
}
}
.vehicle-lower{
padding: 0 px2rem(20px);
font-size: px2rem(30px);
margin-top: px2rem(20px);
margin-bottom: px2rem(20px);
span{
color: #F25B5B;
font-size: px2rem(36px);
font-weight: bold;
margin-left: px2rem(30px);
margin-right: px2rem(4px);
}
}
.member-list{
padding: 0 px2rem(20px);
}
.member-list-c img{
width: 20px;
height: 20px;
margin-right: px2rem(20px);
}
.member-list-name{
color: #666;
font-size:px2rem(26px);
margin-bottom: px2rem(20px);
}
.market-price{
font-size: px2rem(30px);
margin: px2rem(34px) 0;
padding: 0 px2rem(20px);
}
.detail-list{
height: px2rem(100px);
}
.select-tab{
width: 100%;
.select-tab-li-l{
font-size:16px;
color: #999;
padding: 0 px2rem(20px);
}
.select-tab-li-l.active{
color: #333;
font-weight: bold;
height: px2rem(100px);
line-height: px2rem(100px);
border-bottom: 2px solid #1bbb9f;
}
}
.scroll0, .scroll1, .scroll2, .scroll3{
padding: 0 px2rem(20px);
.base-set{
margin: 17px 0;
font-size: 15px;
font-weight: bold;
}
.base-set-name{
font-size: 13px;
color: #666;
padding: 0 10px 6px 0;
}
}
.mint-navbar {
position: sticky;
......@@ -98,6 +382,27 @@
}
}
}
.left-item{
width: 30%;
border-left: 1px solid #c1c1c1;
border-right: 1px solid #c1c1c1;
height: 34px;
padding-left: 10px;
line-height: 34px;
}
.right-item{
width:70%;
border-right: 1px solid #ccc;
height: 34px;
line-height: 34px;
padding-left: 10px;
}
.modal-item{
border-top:1px solid #ccc;
}
.kr-article-article .modal-item:last-child{
border-bottom: 1px solid #ccc;
}
}
</style>
......@@ -13,7 +13,6 @@
@next="next"
@change="changeBanner"
@click="bannerClick"/>
<div class="main-padding">{{ totalCount }}款车型</div>
<ul
v-if="dataDetail.length"
id="scroll-area"
......@@ -59,7 +58,7 @@
class="loading">加载中……</p>
<p
v-if="touchend"
class="empty_data">——没有更多了——</p>
class="empty_data">——{{ totalCount }}款车型——</p>
<go-top/>
<Footer v-if="touchend"/>
</div>
......
......@@ -254,7 +254,7 @@
if(path == '/vehicleDetail'){
var menuTop = document.querySelector('#nav').offsetTop;
//滑动到指定位置菜单吸顶
if (scrollTop > menuTop-45) {
if (scrollTop > menuTop+180) {
this.searchBarFixed = true;
}
......@@ -263,17 +263,17 @@
var top2 = document.querySelector('#lastestDubbing').offsetTop;
var top3 = document.querySelector('#rankLlistDetail').offsetTop;
var top4 = document.querySelector('#modelsDetails').offsetTop;
if (scrollTop < top1-110) {
if (scrollTop < top1+180) {
this.active = 'activity';
this.searchBarFixed = false;
}
if (scrollTop < top2-110) {
if (scrollTop < top2+180) {
this.active = 'activity';
} else if (scrollTop >= top2-110 && scrollTop < top3-110) {
} else if (scrollTop >= top2+180 && scrollTop < top3+180) {
this.active = 'lastest';
} else if (scrollTop >= top3-110 && scrollTop < top4-110) {
} else if (scrollTop >= top3+180 && scrollTop < top4+180) {
this.active = 'rank';
} else if (scrollTop >= top4-110) {
} else if (scrollTop >= top4+180) {
this.active = 'model';
}
}
......@@ -283,9 +283,9 @@
let top1 = document.querySelector('#lastestDubbing').offsetTop;
let top = document.querySelector('#activityIntro').offsetTop;
if(this.searchBarFixed){
document.documentElement.scrollTop = top1 - 105;
document.documentElement.scrollTop = top1 +180;
} else {
document.documentElement.scrollTop = top-37;
document.documentElement.scrollTop = top1+130;
}
this.searchBarFixed = true;
},
......@@ -295,9 +295,9 @@
let top = document.querySelector('#nav').offsetTop;
var top1 = document.querySelector('#activityIntro').offsetTop;
if(this.searchBarFixed){
document.documentElement.scrollTop = top1-105;
document.documentElement.scrollTop = top1 +180;
} else {
document.documentElement.scrollTop = top-88;
document.documentElement.scrollTop = top1 +130;
}
this.searchBarFixed = true;
},
......@@ -306,9 +306,9 @@
this.active = "rank";
let top1 = document.querySelector('#rankLlistDetail').offsetTop;
if(this.searchBarFixed){
document.documentElement.scrollTop = top1 - 105;
document.documentElement.scrollTop = top1 +180;
} else {
document.documentElement.scrollTop = top1-160;
document.documentElement.scrollTop = top1 +130;
}
this.searchBarFixed = true;
},
......@@ -317,9 +317,9 @@
this.active = "model";
let top1 = document.querySelector('#modelsDetails').offsetTop;
if(this.searchBarFixed){
document.documentElement.scrollTop = top1 - 105;
document.documentElement.scrollTop = top1 +180;
} else {
document.documentElement.scrollTop = top1-160;
document.documentElement.scrollTop = top1+130;
}
this.searchBarFixed = true;
},
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment