新增商品详情页小程序快速跳转,以及修改楼层装修样式

master
lemon橪 2021-06-08 09:22:00 +08:00
parent 3cc2f1e019
commit 1e53287927
6 changed files with 514 additions and 79 deletions

View File

@ -0,0 +1,343 @@
<template>
<view class="shadow" :class="!show?'':'shadow-show'" :style="{backgroundColor:show?maskBg:'rgba(0,0,0,0)'}" @tap="tapMask">
<view class="popups" :class="[theme]" :style="{top: popupsTop ,left: popupsLeft,flexDirection:direction}">
<text :class="dynPlace" :style="{width:'0px',height:'0px'}" v-if="triangle"></text>
<view v-for="(item,index) in popData" :key="index" @tap.stop="tapItem(item)" class="itemChild view" :class="[direction=='row'?'solid-right':'solid-bottom',item.disabled?'disabledColor':'']">
<u-icon size="35" :name="item.icon" v-if="item.icon"></u-icon><span class="title">{{item.title}}</span>
</view>
<slot></slot>
</view>
</view>
</template>
<script>
export default {
props: {
maskBg: {
type: String,
default: "rgba(0,0,0,0)",
},
placement: {
type: String,
default: "default", //default top-start top-end bottom-start bottom-end
},
direction: {
type: String,
default: "column", //column row
},
x: {
type: Number,
default: 0,
},
y: {
type: Number,
default: 0,
},
value: {
type: Boolean,
default: false,
},
popData: {
type: Array,
default: () => [],
},
theme: {
type: String,
default: "light", //light dark
},
dynamic: {
type: Boolean,
default: false,
},
gap: {
type: Number,
default: 20,
},
triangle: {
type: Boolean,
default: true,
},
},
data() {
return {
popupsTop: "0rpx",
popupsLeft: "0rpx",
show: false,
dynPlace: "",
};
},
mounted() {
this.popupsPosition();
},
methods: {
tapMask() {
this.$emit("input", !this.value);
},
tapItem(item) {
if (item.disabled) return;
this.$emit("tapPopup", item);
this.$emit("input", !this.value);
},
getStatusBar() {
let promise = new Promise((resolve, reject) => {
uni.getSystemInfo({
success: function (e) {
let customBar;
// #ifdef H5
customBar = e.statusBarHeight + e.windowTop;
// #endif
resolve(customBar);
},
});
});
return promise;
},
async popupsPosition() {
let statusBar = await this.getStatusBar();
let promise = new Promise((resolve, reject) => {
let popupsDom = uni.createSelectorQuery().in(this).select(".popups");
popupsDom
.fields(
{
size: true,
},
(data) => {
let width = data.width;
let height = data.height;
let y = this.dynamic
? this.dynamicGetY(this.y, this.gap)
: this.transformRpx(this.y);
let x = this.dynamic
? this.dynamicGetX(this.x, this.gap)
: this.transformRpx(this.x);
// #ifdef H5
y = this.dynamic
? this.y + statusBar
: this.transformRpx(this.y + statusBar);
// #endif
this.dynPlace =
this.placement == "default"
? this.getPlacement(x, y)
: this.placement;
switch (this.dynPlace) {
case "top-start":
this.popupsTop = `${y + 9}rpx`;
this.popupsLeft = `${x - 15}rpx`;
break;
case "top-end":
this.popupsTop = `${y + 9}rpx`;
this.popupsLeft = `${x + 15 - width}rpx`;
break;
case "bottom-start":
this.popupsTop = `${y - 18 - height}rpx`;
this.popupsLeft = `${x - 15}rpx`;
break;
case "bottom-end":
this.popupsTop = `${y - 9 - height}rpx`;
this.popupsLeft = `${x + 15 - width}rpx`;
break;
}
resolve();
}
)
.exec();
});
return promise;
},
getPlacement(x, y) {
let width = uni.getSystemInfoSync().windowWidth;
let height = uni.getSystemInfoSync().windowHeight;
if (x > width / 2 && y > height / 2) {
return "bottom-end";
} else if (x < width / 2 && y < height / 2) {
return "top-start";
} else if (x > width / 2 && y < height / 2) {
return "top-end";
} else if (x < width / 2 && y > height / 2) {
return "bottom-start";
} else if (x > width / 2) {
return "top-end";
} else {
return "top-start";
}
},
dynamicGetY(y, gap) {
let height = uni.getSystemInfoSync().windowHeight;
y = y < gap ? gap : y;
y = height - y < gap ? height - gap : y;
return y;
},
dynamicGetX(x, gap) {
let width = uni.getSystemInfoSync().windowWidth;
x = x < gap ? gap : x;
x = width - x < gap ? width - gap : x;
return x;
},
transformRpx(params) {
return (params * uni.getSystemInfoSync().screenWidth) / 375;
},
},
watch: {
value: {
immediate: true,
handler: async function (newVal, oldVal) {
if (newVal) await this.popupsPosition();
this.show = newVal;
},
},
placement: {
immediate: true,
handler(newVal, oldVal) {
this.dynPlace = newVal;
},
},
},
};
</script>
<style lang="scss" scoped>
.title {
margin-left: 20rpx;
}
.shadow {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
height: 100%;
z-index: 9999;
transition: background 0.3s ease-in-out;
visibility: hidden;
&.shadow-show {
visibility: visible;
}
}
.popups {
position: absolute;
padding: 20rpx;
border-radius: 5px;
display: flex;
.view {
display: flex;
align-items: center;
padding: 15rpx 10rpx;
font-size: 25rpx;
}
.image {
display: inline-block;
vertical-align: middle;
width: 40rpx;
height: 40rpx;
margin-right: 20rpx;
}
}
.dark {
background-color: #4c4c4c;
color: #fff;
.top-start:after {
content: "";
position: absolute;
top: -18rpx;
left: 10rpx;
border-width: 0 20rpx 20rpx;
border-style: solid;
border-color: transparent transparent #4c4c4c;
}
.top-end:after {
content: "";
position: absolute;
top: -18rpx;
right: 10rpx;
border-width: 0 20rpx 20rpx;
border-style: solid;
border-color: transparent transparent #4c4c4c;
}
.bottom-start:after {
content: "";
position: absolute;
bottom: -18rpx;
left: 10rpx;
border-width: 20rpx 20rpx 0;
border-style: solid;
border-color: #4c4c4c transparent transparent;
}
.bottom-end:after {
content: "";
position: absolute;
bottom: -18rpx;
right: 10rpx;
border-width: 20rpx 20rpx 0;
border-style: solid;
border-color: #4c4c4c transparent transparent;
}
.disabledColor {
color: #c5c8ce;
}
}
.light {
color: #515a6e;
box-shadow: 0upx 0upx 30upx rgba(0, 0, 0, 0.2);
background: #fff;
.top-start:after {
content: "";
position: absolute;
top: -18rpx;
left: 10rpx;
border-width: 0 20rpx 20rpx;
border-style: solid;
border-color: transparent transparent #fff;
}
.top-end:after {
content: "";
position: absolute;
top: -18rpx;
right: 10rpx;
border-width: 0 20rpx 20rpx;
border-style: solid;
border-color: transparent transparent #fff;
}
.bottom-start:after {
content: "";
position: absolute;
bottom: -18rpx;
left: 10rpx;
border-width: 20rpx 20rpx 0;
border-style: solid;
border-color: #fff transparent transparent;
}
.bottom-end:after {
content: "";
position: absolute;
bottom: -18rpx;
right: 10rpx;
border-width: 20rpx 20rpx 0;
border-style: solid;
border-color: #fff transparent transparent;
}
.disabledColor {
color: #c5c8ce;
}
}
.solid-bottom {
border-bottom: 1px solid #f3f5f7;
}
.solid-right {
border-right: 1px solid #ccc;
}
.popups .itemChild:last-child {
border: none;
}
</style>

View File

@ -9,42 +9,40 @@
<!-- 分享 --> <!-- 分享 -->
<shares v-if="shareFlage && goodsDetail.id" :skuId="this.routerVal.id" :goodsId="this.routerVal.goodsId" :link="'/pages/product/goods?id='+this.routerVal.id+'&goodsId='+this.routerVal.goodsId" <shares v-if="shareFlage && goodsDetail.id" :skuId="this.routerVal.id" :goodsId="this.routerVal.goodsId" :link="'/pages/product/goods?id='+this.routerVal.id+'&goodsId='+this.routerVal.goodsId"
:thumbnail="goodsDetail.thumbnail" :goodsName="goodsDetail.goodsName" type="goods" @close="shareFlage = false" /> :thumbnail="goodsDetail.thumbnail" :goodsName="goodsDetail.goodsName" type="goods" @close="shareFlage = false" />
<popups v-model="popupsSwitch" @tapPopup="handleNavbarList" :popData="navbarListData" :x="navbarListX" :y="navbarListY" placement="top-start" />
<view class="index"> <view class="index">
<u-navbar :background="navbar" :is-back="false" :class="headerFlag ? 'header' : 'header bg-none scoll-hide'">
<!-- topBar -->
<u-navbar :background="navbar" :is-back="false" :class="headerFlag ? 'header' : 'header bg-none scroll-hide'">
<div class="headerRow"> <div class="headerRow">
<div class="backs" @click="back()"> <div class="backs">
<u-icon name="arrow-left" color="#8e8e8e"></u-icon> <u-icon @click="back()" name="arrow-left" class="icon-back"></u-icon>
<u-icon name="list" @click="popupsSwitch = !popupsSwitch" class="icon-list"></u-icon>
</div> </div>
<div class="headerList" :class="headerFlag ? 'tab-bar' : 'tab-bar scoll-hide'"> <div class="headerList" :class="headerFlag ? 'tab-bar' : 'tab-bar scroll-hide'">
<div class="headerRow"> <div class="headerRow">
<div style="text-align: center" :span="3" v-for="header in headerList" :key="header.id" :class="{ cur: scrollId === header.id }" @click="headerTab(header.id)"> <div class="nav-item" v-for="header in headerList" :key="header.id" :class="{ cur: scrollId === header.id }" @click="headerTab(header.id)">
{{ header.text }} {{ header.text }}
</div> </div>
</div> </div>
</div> </div>
<div class="share" @click="shareChange()">
<image class="shareImg" src="/static/share.png" alt=""></image>
</div>
</div> </div>
</u-navbar> </u-navbar>
<view class="showBack" v-show="!headerFlag"> <u-navbar :border-bottom="false" v-show="!headerFlag" class="header-only-back" :background="navbarOnlyBack" :is-back="false">
<u-row> <div>
<u-col :span="2" @click="back()"> <div class="bg-back">
<div class="iconBag" style="text-align: center"> <u-icon size="40" @click="back()" name="arrow-left" class="icon-back"></u-icon>
<image class="headerImg" src="/static/bagBack.png" alt=""></image> <u-icon size="40" @click="popupsSwitch = !popupsSwitch" name="list" class="icon-list"></u-icon>
</div>
</u-col>
<u-col :span="8"></u-col>
<u-col :span="2" class="share" style="text-align: center" @click="shareChange()"> </div>
<image class="headerImg" src="/static/bagShare.png" alt=""></image> </div>
</u-col> </u-navbar>
</u-row>
</view>
</view> </view>
<view class="product-container" :style="{ height: productRefHeight }" ref="productRef" id="productRef"> <view class="product-container" :style="{ height: productRefHeight }" ref="productRef" id="productRef">
<scroll-view scroll-anchoring enableBackToTop="true" scroll-with-animation scroll-y class="scoll-page" :scroll-top="tabScrollTop" @scroll="pageScroll"> <scroll-view scroll-anchoring enableBackToTop="true" scroll-with-animation scroll-y class="scroll-page" :scroll-top="tabScrollTop" @scroll="pageScroll">
<view> <view>
<!-- 轮播图 --> <!-- 轮播图 -->
<GoodsSwiper id="main1" :res="imgList" /> <GoodsSwiper id="main1" :res="imgList" />
@ -54,9 +52,9 @@
<view class="card-box top-radius-0" id="main2"> <view class="card-box top-radius-0" id="main2">
<!-- 活动不显示价钱 --> <!-- 活动不显示价钱 -->
<view v-if="!is_promotion" class="desc-blod -goods-msg"> <view v-if="!isPromotion" class="desc-bold -goods-msg">
<view class="-goods-flex"> <view class="-goods-flex">
<view class="desc-blod"> <view class="desc-bold">
{{ goodsDetail.goodsName || "" }} {{ goodsDetail.goodsName || "" }}
</view> </view>
<view class="favorite" @click="clickFavorite(goodsDetail.id)"> <view class="favorite" @click="clickFavorite(goodsDetail.id)">
@ -84,12 +82,17 @@
}} </span> }} </span>
</view> </view>
<view class="-goods-price" v-else> ¥<span class="price">0 </span>.00 </view> <view class="-goods-price" v-else> ¥<span class="price">0 </span>.00 </view>
<view class="favorite" @click="clickFavorite(goodsDetail.id)">
<view class="icons share" @click="shareChange()">
<u-icon size="30" name="share-fill"></u-icon>
<view>分享</view>
</view>
<view class="icons" @click="clickFavorite(goodsDetail.id)">
<u-icon size="30" :color="favorite ? '#f2270c' : '#262626'" :name="favorite ? 'heart-fill' : 'heart'"></u-icon> <u-icon size="30" :color="favorite ? '#f2270c' : '#262626'" :name="favorite ? 'heart-fill' : 'heart'"></u-icon>
<view :style="{ color: favorite ? '#f2270c' : '#262626' }">{{ favorite ? "已收藏" : "收藏" }}</view> <view :style="{ color: favorite ? '#f2270c' : '#262626' }">{{ favorite ? "已收藏" : "收藏" }}</view>
</view> </view>
</view> </view>
<view class="-goods-name desc-blod"> <view class="-goods-name desc-bold">
{{ goodsDetail.goodsName || "" }} {{ goodsDetail.goodsName || "" }}
</view> </view>
<view class="-goods-desc"> <view class="-goods-desc">
@ -232,7 +235,7 @@ import {
import * as API_trade from "@/api/trade.js"; import * as API_trade from "@/api/trade.js";
import * as API_Members from "@/api/members.js"; import * as API_Members from "@/api/members.js";
import * as API_store from "@/api/store.js"; import * as API_store from "@/api/store.js";
import { modelNavigateTo } from "@/pages/tabbar/home/template/tpl.js";
/************请求存储***************/ /************请求存储***************/
import storage from "@/utils/storage.js"; import storage from "@/utils/storage.js";
@ -250,8 +253,10 @@ import GoodsSwiper from "./product/goods/-goods-swiper"; //轮播图组件
import popupGoods from "./product/popup/goods"; // import popupGoods from "./product/popup/goods"; //
import popupAddress from "./product/popup/address"; // import popupAddress from "./product/popup/address"; //
import shares from "@/components/m-share/index"; // import shares from "@/components/m-share/index"; //
import popups from "@/components/popups/popups"; //
export default { export default {
components: { components: {
popups,
shares, shares,
PromotionLayout, PromotionLayout,
PromotionDetailsLayout, PromotionDetailsLayout,
@ -268,13 +273,51 @@ export default {
}, },
data() { data() {
return { return {
// #ifdef H5
navbarListX: 110, //x
navbarListY: 80, //y
// #endif
// #ifdef MP-WEIXIN
navbarListX: 100, //x
navbarListY: 140, //y
// #endif
// #ifdef APP-PLUS
navbarListX: 120, //x
navbarListY: 170, //y
// #endif
navbarListData: [
//
{
title: "首页",
icon: "home-fill",
___type: "other",
},
{
title: "购物车",
icon: "bag-fill",
___type: "other",
},
{
title: "搜索",
icon: "search",
___type: "category",
},
{
title: "个人中心",
icon: "account-fill",
___type: "other",
},
],
popupsSwitch: false, //
shareFlage: false, shareFlage: false,
selectedGoods: "", // selectedGoods: "", //
is_promotion: true, // isPromotion: true, //
isGroup: false, // isGroup: false, //
pointDetail: "", // pointDetail: "", //
assemble: "", //sku assemble: "", //sku
scroll_mask_height: 0, // navbarOnlyBack: {
background: "transparent",
},
navbar: { navbar: {
background: "#fff", background: "#fff",
}, },
@ -286,7 +329,7 @@ export default {
}, },
headerFlag: false, // headerFlag: false, //
headerList: [ headerList: [
// //
{ {
text: "商品", text: "商品",
id: "1", id: "1",
@ -359,15 +402,14 @@ export default {
clearInterval(timer); clearInterval(timer);
}, 100); }, 100);
this.is_promotion = false; this.isPromotion = false;
} else { } else {
this.is_promotion = true; this.isPromotion = true;
this.$refs.popupGoods.buyType = ""; this.$refs.popupGoods.buyType = "";
} }
}, },
}, },
mounted() { mounted() {
const { windowHeight } = uni.getSystemInfoSync(); const { windowHeight } = uni.getSystemInfoSync();
let bottomHeight = 0; let bottomHeight = 0;
let topHeight = 0; let topHeight = 0;
@ -420,7 +462,16 @@ export default {
} }
}, },
methods: { methods: {
// /**
* 导航栏列表栏
*/
handleNavbarList(val) {
modelNavigateTo({ url: val });
},
/**
* 循环出当前促销是否为空
*/
emptyPromotion() { emptyPromotion() {
if ( if (
this.PromotionList == "" || this.PromotionList == "" ||
@ -430,7 +481,10 @@ export default {
return true; return true;
} }
}, },
/**初始化信息 */
/**
* 初始化信息
*/
async init(id, goodsId, distributionId) { async init(id, goodsId, distributionId) {
this.isGroup = false; // this.isGroup = false; //
this.productId = id; // skuId this.productId = id; // skuId
@ -489,8 +543,8 @@ export default {
}, },
linkMsgDetail() { linkMsgDetail() {
uni.navigateTo({ uni.navigateTo({
url: '/pages/product/customerservice/index' url: "/pages/product/customerservice/index",
}) });
}, },
// 1999 --> [1999,00] // 1999 --> [1999,00]
formatPrice(val) { formatPrice(val) {

View File

@ -48,7 +48,7 @@ page{
font-size: 22rpx; font-size: 22rpx;
} }
} }
.desc-blod { .desc-bold {
flex: 8; flex: 8;
font-weight: 700; font-weight: 700;
color: #262626; color: #262626;
@ -72,7 +72,7 @@ page{
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
} }
.favorite { .icons,.favorite {
flex: 1; flex: 1;
font-size: 22rpx; font-size: 22rpx;
color: #262626; color: #262626;
@ -127,45 +127,68 @@ page{
} }
.headerRow { .headerRow {
height: 44px; //uni navbar44px
display: flex; display: flex;
align-items: center;
width: 100%; width: 100%;
background: #fff !important; background: #fff !important;
> div {
flex: 1;
}
> .headerList { > .headerList {
flex: 8; flex: 8;
} }
justify-content: space-between; justify-content: space-between;
} }
.scoll-hide { .scroll-hide {
opacity: 0; opacity: 0;
transition: all 0.5s; transition: all 0.5s;
} }
.cur { .cur {
color: #333; color: $main-color;
border-bottom: 6rpx solid $main-color;
} }
.header { .cur::after {
content: "";
height: 6rpx;
background: $main-color;
width: 100%;
position: absolute;
bottom: 0;
left: 0;
}
.header-only-back {
background: transparent;
}
.header,
.header-only-back {
padding-left: 10rpx;
position: fixed; position: fixed;
top: var(--status-bar-height); top: var(--status-bar-height);
width: 100%; width: 100%;
z-index: 8; z-index: 8;
height: 90rpx; height: 90rpx;
line-height: 90rpx;
font-size: 30rpx; font-size: 30rpx;
transition: all 0.5s; transition: all 0.5s;
}
/deep/ .u-navbar {
padding-left: 10rpx;
}
.nav-item {
flex: 2;
position: relative;
justify-content: center;
height: 100%;
display: flex;
align-items: center;
}
.header {
color: #666666; color: #666666;
background-color: #fff; background-color: #fff;
.tab-bar { .tab-bar {
width: 100%; width: 100%;
color: #666; color: #666;
font-weight: 400; font-weight: 400;
view { view {
padding: 0 3px; padding: 0 3px;
} }
@ -217,11 +240,29 @@ page{
height: calc(100% - var(--status-bar-height)); height: calc(100% - var(--status-bar-height));
overflow: hidden; overflow: hidden;
} }
.icon-back {
padding-right: 10rpx;
}
.icon-list {
border-left: 2rpx solid rgb(194, 194, 194);
padding-left: 10rpx;
}
.backs,
.bg-back {
width: 150rpx;
border: 2rpx solid #e8e8e8;
border-radius: 100px;
display: flex;
align-items: center;
justify-content: center;
padding: 8rpx 0;
}
.bg-back {
background: rgba($color: #fff, $alpha: 0.8);
}
.backs { .backs {
// width: 150rpx;
text-align: center; text-align: center;
font-size: 42rpx; font-size: 42rpx;
line-height: 90rpx;
} }
.headerImg { .headerImg {
@ -253,8 +294,6 @@ page {
} }
.product-container { .product-container {
// background: #f0f0f0;
.header-line { .header-line {
height: 1px; height: 1px;
background: #f2f2f2; background: #f2f2f2;
@ -265,12 +304,12 @@ page {
z-index: 999; z-index: 999;
transition: all 0.5s; transition: all 0.5s;
&.scoll-hide { &.scroll-hide {
background: none; background: none;
} }
} }
.scoll-page { .scroll-page {
width: 100%; width: 100%;
height: 100%; height: 100%;
} }
@ -337,12 +376,6 @@ page {
} }
} }
.u-mode-light-error {
background: #ffebec !important;
padding: 8rpx 16rpx;
font-size: 20rpx;
}
.showBox { .showBox {
width: 100%; width: 100%;
height: 90rpx; height: 90rpx;

View File

@ -1,6 +1,5 @@
/** 配置楼层模块的跳转 */ /** 配置楼层模块的跳转 */
export function modelNavigateTo(item) { export function modelNavigateTo(item) {
let val = item.url; let val = item.url;
switch (val.___type) { switch (val.___type) {
case "goods": case "goods":
@ -9,9 +8,15 @@ export function modelNavigateTo(item) {
}); });
break; break;
case "category": case "category":
if (val.id) {
uni.navigateTo({ uni.navigateTo({
url: `/pages/navigation/search/searchPage?category=${val.id}`, url: `/pages/navigation/search/searchPage?category=${val.id}`,
}); });
} else {
uni.navigateTo({
url: `/pages/navigation/search/searchPage`,
});
}
break; break;
case "stores": case "stores":
uni.navigateTo({ uni.navigateTo({
@ -33,7 +38,7 @@ export function modelNavigateTo(item) {
switch (val.title) { switch (val.title) {
case "首页": case "首页":
uni.switchTab({ uni.switchTab({
url: `/`, url: `/pages/tabbar/home/index`,
}); });
break; break;
case "购物车": case "购物车":

View File

@ -2,7 +2,7 @@
max-width: 100%; max-width: 100%;
height: auto; height: auto;
display: block; display: block;
padding: 2rpx;
} }
.layout { .layout {
padding: 16rpx; padding: 16rpx;

View File

@ -33,7 +33,7 @@ export default {
<style lang="scss" scoped> <style lang="scss" scoped>
@import "./tpl.scss"; @import "./tpl.scss";
.layout { .layout {
height: 167px; height: 300rpx;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;