国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

Taro微信小程序?qū)崿F(xiàn) 美團(tuán)購(gòu)物車(chē)小紅點(diǎn)動(dòng)畫(huà)效果

這篇具有很好參考價(jià)值的文章主要介紹了Taro微信小程序?qū)崿F(xiàn) 美團(tuán)購(gòu)物車(chē)小紅點(diǎn)動(dòng)畫(huà)效果。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問(wèn)。

Taro微信小程序?qū)崿F(xiàn) 美團(tuán)購(gòu)物車(chē)小紅點(diǎn)動(dòng)畫(huà)效果文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-497584.html

1、mall.vue

<template>
	<view class="container">
		<!-- 頭部背景 -->
		<view class="fixed-header">
			<NavBar
				titleTxt=""
				showLeft
				bgColor="transparent"
				textColor="#FFFFFF"
				leftArrowColor="#FFFFFF"
			/>
			<image
				:src="(mallInfo.imgs && mallInfo.imgs[0]) ? mallInfo.imgs[0].imgUrl : `${baseImgHost}/default-img1.png`"
				mode="aspectFill"
				@tap="toMallImgs"
			/>
		</view>
		<view class="relative-container">
			<!-- 商家信息 -->
			<view class="mall-info">
				<view
					class="name-phone flex f-ai-c f-jc-sb"
					@tap="makeCall"
				>
					<view class="left flex f-d-c">
						<text class="fz-40">
							{{ mallInfo.merchantName }}
						</text>
						<text class="fz-24">
							{{ mallInfo.phone }}
						</text>
					</view>
					<image
						:src="`${baseImgHost}/phone3-g.png`"
						mode="aspectFill"
					/>
				</view>
				<view
					class="address flex f-d-c"
					@tap="switchLocation"
				>
					<text class="fz-24 p-name">
						{{ mallInfo.address }}
					</text>
					<text class="fz-24 p-dis">
						距您{{ shortenNumber(mallInfo.meter) }}{{ mallInfo.meter > 1000 ? 'km' : 'm' }}
					</text>
					<image
						class="nav-bg"
						:src="`${baseImgHost}/navigation-bg.png`"
						mode="aspectFill"
					/>
					<image
						class="nav icon-box"
						:src="`${baseImgHost}/navigation.png`"
						mode="aspectFill"
					/>
				</view>
			</view>
			<!-- 商品分類列表+商品列表 -->
			<view class="goods-type-g">
				<BgTitle
					title="商品列表"
					:fontSize="30"
					style="margin-left: 20rpx;"
				/>
				<view class="type-goods-container flex">
					<!-- 商品類別滾動(dòng)區(qū)域 -->
					<view class="goods-type">
						<view
							v-for="item in Object.values(goodsTypeList)"
							:key="item.id"
							class="goods-type-item flex f-jc-c f-ai-c"
							:class="{active: item.id === activeTypeId}"
							@tap="goodsTypeClickHandle(item)"
						>
							<view class="fz-24 type-name flex f-jc-c f-ai-c">
								{{ item.typeName }}
								<view
									v-if="item.count"
									class="badge fz-20"
									:class="[item.count > 9 ? 'rectangle' : 'circle']"
								>
									{{ item.count }}
								</view>
							</view>
						</view>
					</view>
					<!-- 商品滾動(dòng)區(qū)域 -->
					<view
						v-if="goodsList.length"
						class="flex flex1 f-d-c"
						style="height: 100%; position: relative;"
					>
						<view
							v-for="item in goodsList"
							:key="item.id"
							class="goods-item flex"
							@tap.stop="switchGoods(item.id)"
						>
							<image
								class="goods-img"
								:src="item.imgs[0]?.imgUrl || `${baseImgHost}/default-img2.png`"
								mode="aspectFill"
							/>
							<view class="flex f-d-c f-jc-sb goods-info">
								<text class="goods-name fz-28">
									{{ item.goodsName }}
								</text>
								<view class="flex f-jc-sb f-ai-fe">
									<view class="flex f-d-c">
										<text class="goods-sell fz-24">
											月銷 {{ item.monthSell }} 單
										</text>
										<text class="goods-price fz-24"><text class="fz-34">
												{{ item.specifications[0].price }}
											</text>
										</text>
									</view>
									<!-- 選規(guī)格 -->
									<view
										v-if="item.specifications.length > 1"
										class=" count-opt specification fz-26"
									>
										<view
											class="gg"
											@tap.stop="specificationClick(item)"
										>
											選規(guī)格
											<text
												v-if="item.count"
												class="spec-item-count fz-20"
												:class="[item.count > 9 ? 'rectangle' : 'circle']"
											>
												{{ item.count }}
											</text>
										</view>
									</view>
									<!-- 增減數(shù)量 -->
									<view
										v-else
										class="flex f-ai-c count-opt"
									>
										<view
											v-if="item.count"
											class="opt-b reduce fz-36"
											@tap.stop="goodsCountOpt($event, item)"
										>
											-
										</view>
										<text
											v-if="item.count"
											class="fz-28 choosed"
										>
											{{ item.count }}
										</text>
										<view
											class="opt-b add fz-36"
											@tap.stop="goodsCountOpt($event, item, 1)"
										></view>
									</view>
								</view>
							</view>
						</view>
					</view>
					<NoData
						v-else
						style="margin-top: 20rpx; height: 290rpx;"
					/>
				</view>
				<view class="fixed-bottom" />
			</view>
		</view>
		<!-- 底部下單、購(gòu)物袋等 -->
		<view class="func-bar flex f-ai-c f-jc-sb">
			<view class="flex f-ai-c">
				<view
					class="shopping-bag"
					@tap.stop="bagClickHandle($event)"
				>
					<image
						id="bagIcon"
						:src="`${baseImgHost}/bag.png`"
						mode="aspectFie"
					/>
					<view
						v-if="total"
						class="badge fz-20"
						:class="[total > 9 ? 'rectangle' : 'circle']"
					>
						{{ total }}
					</view>
					<view
						v-else
						class="badge-hidden"
					/>
				</view>
				<text class="fz-24 total-price"><text class="fz-34">
						{{ totalPrice }}
					</text>
				</text>
				<text class="fz-22 total">
					共 {{ total }} 件
				</text>
			</view>
			<view
				class="place-order fz-30"
				@tap="confirmOrder"
			>
				去下單
			</view>
		</view>
		<!-- 選規(guī)格彈框 -->
		<Overlay
			:show="showSpecification"
			@overlayClick="closeSpecDialog"
		/>
		<view
			class="spec"
			:class="showSpecContainer ? 'show' : 'hidden'"
		>
			<view class="spec-container flex f-d-c">
				<view class="goods-name fz-34">
					{{ specGoods?.goodsName }}
				</view>
				<!-- 規(guī)格信息 -->
				<view class="flex flex1 f-d-c spec-con">
					<text class="fz-24">
						規(guī)格
					</text>
					<view class="specs flex">
						<view
							v-for="(item, index) in specGoods?.specifications"
							:key="item.id"
							class="spec-item fz-24"
							:class="{active: index === activeSpecIndex}"
							@tap.stop="specClick(item, index)"
						>
							{{ item.name }}
						</view>
					</view>
				</view>
				<text class="spec-choosed fz-24">
					已選擇規(guī)格:{{ specGoods?.specNames?.join(',') }}
				</text>
				<!-- 購(gòu)買(mǎi)數(shù)量 -->
				<view class="flex f-jc-sb f-ai-c spec-count-opt">
					<view class="unit-price flex f-ai-c">
						<text
							class="fz-28"
							style="font-weight: 600;"
						>
							單價(jià)
						</text>
						<view style="color: #FF4200;">
							<text class="fz-24"></text>
							<text
								class="fz-38"
								style="font-weight: 600;"
							>
								{{ specGoods?.specifications[activeSpecIndex].price }}
							</text>
						</view>
					</view>
					<view
						v-if="!specGoods?.specifications[activeSpecIndex].count"
						class="add-bag flex f-jc-c f-ai-c"
						@tap.stop="specCountOptThrottle($event, specGoods?.specifications[activeSpecIndex], specGoods, 1)"
					>
						<text class="fz-26">
							+
						</text>
						<text class="fz-26">
							加入購(gòu)物袋
						</text>
					</view>
					<view
						v-else
						class="flex f-ai-c count-opt"
					>
						<view
							class="opt-b reduce fz-28"
							@tap.stop="specCountOptThrottle($event, specGoods?.specifications[activeSpecIndex], specGoods)"
						>
							-
						</view>
						<text class="fz-28 choosed">
							{{ specGoods?.specifications[activeSpecIndex].count || 0 }}
						</text>
						<view
							class="opt-b add fz-28"
							@tap.stop="specCountOptThrottle($event, specGoods?.specifications[activeSpecIndex], specGoods, 1)"
						></view>
					</view>
				</view>
				<image
					class="close-btn"
					:src="`${baseImgHost}/close-2.png`"
					mode="aspectFill"
					@tap.stop="closeSpecDialog"
				/>
			</view>
		</view>
		<!-- 購(gòu)物袋 -->
		<PageContainer
			:show="showBag"
			height="70vh"
			:round="true"
			:z-index="9990"
			:showShadow="true"
			@overlayClick="showBag = false;"
		>
			<view class="bag-container">
				<view class="flex f-ai-c f-jc-sb bag-header">
					<view>
						<text class="fz-28">
							購(gòu)物袋
						</text>
						<text class="fz-24 total">
							(共{{ total }}件商品)
						</text>
					</view>
					<view class="flex f-ai-c">
						<image :src="`${baseImgHost}/del.png`" />
						<text
							class="fz-24 total"
							@tap.stop="clearBagHandle"
						>
							清空購(gòu)物袋
						</text>
					</view>
				</view>
				<view class="bg-list">
					<view
						v-for="item in Object.values(shoppingBag)"
						:key="item.id"
						class="bg-item flex f-jc-sb"
					>
						<view class="flex">
							<image :src="(item.goodsImgs ?? [])[0]?.imgUrl || `${baseImgHost}/default-img2.png`" />
							<view class="flex f-d-c">
								<text class="fz-28">
									{{ item.goodsName }}
								</text>
								<text
									class="fz-24"
									style="color: #828180;"
								>
									{{ item.specificationName }}
								</text>
								<view
									class="fz-20"
									style="color: #FF4200"
								><text class="fz-28">
										{{ item.price }}
									</text>
								</view>
							</view>
						</view>
						<view
							class="flex f-ai-c count-opt"
						>
							<view
								class="opt-b reduce fz-36"
								@tap.stop="bagCoutOpt(item)"
							>
								-
							</view>
							<text
								class="fz-28 choosed"
							>
								{{ item.count }}
							</text>
							<view
								class="opt-b add fz-36"
								@tap.stop="bagCoutOpt(item, 1)"
							></view>
						</view>
					</view>
				</view>
			</view>
		</PageContainer>
		<view
			class="dot"
			:style="{ opacity: dotOpacity, left: dotLeft, top: dotTop }"
		/>
	</view>
</template>
<script>
import './mall.less';
import { NavBar, BgTitle, Overlay, PageContainer, NoData } from '@/components';
import Taro from '@tarojs/taro';
import { throttle, twoBezier } from '@/utils/util';
import { shortenNumber } from '@/utils/number';
import { getGoodsTypeList, getGoodsList, merchantDetail, bagAdd, bagList, bagDelete, bagClear } from '@/apis/goods';

export default {
	name: 'MallDetail',
	components: { NavBar, BgTitle, Overlay, PageContainer, NoData },
	provide () {
		return {
			activeBackground: 'linear-gradient(90deg, #FFA00C, rgba(255,159,10,0)) !important'
		};
	},
	data () {
		return {
			isInit: true, // 是否是頁(yè)面數(shù)據(jù)初始化
			showBag: false,
			merchantId: null, // 商家ID
			keyword: '',
			mallInfo: {}, // 商家信息
			goodsList: [], // 商品列表
			goodsTypeList: {}, // 商品類型列表
			shortenNumber,
			activeTypeId: 0, // 當(dāng)前選中的商品類型id
			activeType: null, // 當(dāng)前選中的商品類型
			showSpecification: false, // 是否展示選規(guī)格彈框
			showSpecContainer: false, // 是否展示選規(guī)格容器
			activeSpecIndex: 0, // 任何一個(gè)規(guī)格彈框中,選中的規(guī)格的索引
			specGoods: null, // 當(dāng)前查看規(guī)格的商品
			/**
			 * 購(gòu)物袋, key: 某個(gè)規(guī)格的某個(gè)商品的記錄id;
			 */
			shoppingBag: {},
			totalPrice: 0, // 總價(jià)
			total: 0, // 總數(shù)量
			pager: {
				currentPage: 1,
				pageSize: 10,
				pageCount: 0
			},
			specCountOptThrottle: null,
			dotTop: 0,
			dotLeft: 0,
			endLeft: 0,
			endTop: 0,
			dotOpacity: 0
		};
	},
	onLoad (options) {
		this.merchantId = options.id;
	},
	async onShow () {
		this.pager = {
			currentPage: 1,
			pageSize: 10,
			pageCount: 0
		};
		this.goodsList = [];
		this.wxToken = Taro.getStorageSync('token');
		this.initLngLat(this.getMallInfo);
		const storageActiveTypeId = await Taro.getStorageSync('activeGoodsType');
		if (storageActiveTypeId) Taro.removeStorageSync('activeGoodsType');
		await this.getGoodsTypeList(storageActiveTypeId);
		await this.getGoodsList(this.activeTypeId);
		this.specCountOptThrottle = throttle(this.specCountOpt, 500);
		// 初始化購(gòu)物袋的位置坐標(biāo)
		const query = Taro.createSelectorQuery();
		query.select('#bagIcon').boundingClientRect((res) => {
			this.endLeft = res.left + 12.5;
			this.endTop = res.top;
		}).exec();
	},
	onHide () {
		this.showBag = false;
	},
	methods: {
		makeCall () {
			if (this.mallInfo.phone) {
				Taro.makePhoneCall({
					phoneNumber: this.mallInfo.phone
				}).catch(() => {});
			}
		},
		switchLocation () {
			const that = this;
			Taro.openLocation({
				latitude: Number(that.mallInfo.lat),
				longitude: Number(that.mallInfo.lng),
				scale: 18,
				name: that.mallInfo.merchantName,
				address: that.mallInfo.address
			});
		},
		clearBagHandle () {
			bagClear({ merchantId: this.merchantId }, this.wxToken, false).then(async res => {
				if (res) {
					Taro.showToast({
						title: '操作成功',
						icon: 'none',
						duration: 1000
					});
					await this.getGoodsTypeList();
					await this.getGoodsList(this.activeTypeId);
				}
			});
		},
		toMallImgs () {
			Taro.navigateTo({ url: `/pages/mallImgs/mallImgs?type=mall&merchantId=${this.merchantId}` });
		},
		confirmOrder () {
			if (this.total) {
				Taro.navigateTo({ url: `/pages/orderConfirm/orderConfirm?merchantId=${this.merchantId}` });
			}
		},
		// 購(gòu)物袋被點(diǎn)擊
		bagClickHandle () {
			if (!this.total) return;
			if (this.showBag) {
				this.showBag = false;
				return;
			}
			this.showBag = true;
			this.getBagList();
		},
		// 獲取商家信息
		getMallInfo () {
			merchantDetail(this.merchantId, { lat: this.lat, lng: this.lng }, this.wxToken).then(res => {
				this.mallInfo = res;
			});
		},
		// 商品類型列表
		async getGoodsTypeList (storageActiveTypeId) {
			const res = await getGoodsTypeList({ merchantId: this.merchantId, page: 1, limit: 100 }, this.wxToken);
			if (res && res.list.length) {
				if (storageActiveTypeId) {
					this.activeTypeId = storageActiveTypeId;
					this.activeType = res.list.find(l => l.id === storageActiveTypeId);
				} else {
					this.activeTypeId = res.list[0].id;
					this.activeType = res.list[0];
				}
				res.list.map(l => {
					this.goodsTypeList[l.id] = { ...l, count: 0 };
				});
			}
		},
		// 商品列表
		async getGoodsList (id, showLoading) {
			const res = await getGoodsList({
				merchantId: this.merchantId,
				typeId: id,
				page: this.pager.currentPage,
				limit: this.pager.pageSize
			}, this.wxToken, showLoading);
			if (this.triggered) {
				this.triggered = false;
				Taro.stopPullDownRefresh();
			}
			if (res && res.list) {
				if (this.pager.currentPage === 1) {
					this.goodsList = res.list;
				} else {
					this.goodsList = this.goodsList.concat(res.list);
				}
				this.pager = res.page;
			}
			this.getBagList();
		},
		// 獲取購(gòu)物袋中物品列表
		getBagList () {
			this.goodsList = this.goodsList.map(gl => {
				gl.count = 0;
				gl.specifications = gl.specifications.map(sp => {
					sp.count = 0;
					return sp;
				});
				return gl;
			});
			bagList({ merchantId: this.merchantId }, this.wxToken).then(res => {
				this.shoppingBag = {};
				// 購(gòu)物袋每條記錄中帶有該商品的類別id,字段名為: typeId
				if (res && res.length) {
					const _goodsTypeList = {};
					const { count, price } = res.reduce((pre, curr, index, arr) => {
						// 填充購(gòu)物袋物品
						this.shoppingBag[curr.id] = curr;
						// 各商品類型回顯已選中的商品總數(shù)
						if (_goodsTypeList[curr.goodsTypeId]) {
							_goodsTypeList[curr.goodsTypeId].count += curr.count;
						} else {
							_goodsTypeList[curr.goodsTypeId] = { count: curr.count };
						}
						const goods = this.goodsList.find(g => g.id === curr.goodsId);
						if (goods) {
							// 各商品回顯已選中的商品總數(shù)
							goods.count = (goods.count ?? 0) + curr.count;
							// 某商品中的各個(gè)規(guī)格回顯總數(shù)
							const spec = goods.specifications.find(s => s.id === curr.specificationId);
							if (spec) {
								spec.count = curr.count;
							}
						}
						return {
							count: pre.count + curr.count,
							price: +(pre.price + (curr.price || 0)).toFixed(2)
						};
					}, { count: 0, price: 0 });
					// 各商品類型回顯已選中的商品總數(shù)
					for (const gt in _goodsTypeList) {
						this.goodsTypeList[gt].count = _goodsTypeList[gt].count;
					}
					this.total = count;
					this.totalPrice = price;
				} else {
					this.showBag = false; // 如果購(gòu)物袋中物品數(shù)為0時(shí)關(guān)閉購(gòu)物袋
					this.total = 0;
					this.totalPrice = 0;
					for (const key in this.goodsTypeList) {
						this.goodsTypeList[key].count = 0;
					}
				}
			});
		},
		tabClickHandle (id) {
			Taro.navigateTo({ url: `/pages/mallDetail/mallDetail?id=${id}` });
		},
		// 某個(gè)商品類別被點(diǎn)擊
		goodsTypeClickHandle (item) {
			this.activeTypeId = item.id;
			this.activeType = item;
			this.pager.currentPage = 1;
			this.getGoodsList(item.id, true);
		},
		dotAnimate (event) {
			// 設(shè)置小紅點(diǎn)初始位置
			const { clientX, clientY } = event.changedTouches[0];
			this.dotLeft = clientX + 'px';
			this.dotTop = clientY + 'px';
			const num = 30;
			const track = [];
			for (let i = 0; i < num + 1; i++) {
				const [x, y] = twoBezier(i / num, [clientX, clientY], [clientX - 60, clientY - 120], [this.endLeft, this.endTop]);
				track.push({ opacity: 1, left: `${x}px`, top: `${y}px` });
			}
			track[num].opacity = 0;
			let i = 0;
			const inter = setInterval(() => {
				if (i === num + 1) {
					clearInterval(inter);
					return;
				}
				const { opacity, left, top } = track[i];
				this.dotOpacity = opacity;
				this.dotLeft = left;
				this.dotTop = top;
				i++;
			}, 10);
		},
		// 商品列表中的加減號(hào)
		goodsCountOpt (event, item, type) {
			if (!type && !item.count) return;
			(type ? bagAdd : bagDelete)({
				merchantId: this.merchantId, // 商戶id
				goodsId: item.id, // 商品id
				specificationId: item.specifications[0].id // 規(guī)格id
			}, this.wxToken).then(res => {
				if (res) {
					if (type) {
						this.dotAnimate(event);
						item.count = (item.count ?? 0) + 1;
					} else {
						item.count--;
					}
					this.updateOther(type, item.specifications[0].price);
					this.updateShoppingBagDatas(item.id, item.specifications[0].id, type, res);
				}
			});
		},
		// 更新總數(shù)、總價(jià)、各商品類型已選購(gòu)總數(shù)
		updateOther (type, price) {
			if (type) {
				this.total++;
				this.goodsTypeList[this.activeTypeId].count = (this.goodsTypeList[this.activeTypeId].count ?? 0) + 1;
				this.totalPrice = +(this.totalPrice + price).toFixed(2);
			} else {
				if (this.total) {
					this.goodsTypeList[this.activeTypeId].count--;
					this.total--;
					this.totalPrice = +(this.totalPrice - price).toFixed(2);
				}
			}
		},
		// 更新購(gòu)物袋中的物品
		updateShoppingBagDatas (goodsId, specificationId, type, res) {
			for (const i in this.shoppingBag) {
				if (this.shoppingBag[i].goodsId === goodsId && this.shoppingBag[i].specificationId === specificationId) {
					// 如果是添加物品
					if (type) {
						this.shoppingBag[i].count++;
					} else {
						this.shoppingBag[i].count--;
						if (!this.shoppingBag[i].count) {
							delete this.shoppingBag[i];
						}
					}
				}
			}
			// 將購(gòu)物袋中沒(méi)有的物品同步進(jìn)去
			if (type && !this.shoppingBag[res.id]) {
				this.shoppingBag[res.id] = res;
			}
		},
		// 點(diǎn)擊某個(gè)貨物
		switchGoods (id) {
			Taro.setStorageSync('activeGoodsType', this.activeTypeId);
			Taro.navigateTo({
				url: `/pages/mallGoodsDetail/mallGoodsDetail?id=${id}&merchantId=${this.merchantId}`
			});
		},
		// 商品列表中的規(guī)格按鈕點(diǎn)擊
		specificationClick (item) {
			item.specNames = [];
			this.showSpecification = true;
			this.showSpecContainer = true;
			item.count = 0;
			Object.values(this.shoppingBag).forEach(gr => {
				const spec = item.specifications.find(s => {
					if (item.id === gr.goodsId && s.id === gr.specificationId) return s;
				});
				if (spec) {
					spec.count = gr.count; // 將購(gòu)物袋中該商品的某個(gè)規(guī)格的購(gòu)買(mǎi)數(shù)量賦值給該商品的該規(guī)格
					item.count = (item.count ?? 0) + gr.count;
					item.specNames.push(spec.name);
				}
			});
			this.specGoods = item;
		},
		// 關(guān)閉規(guī)格彈框
		closeSpecDialog () {
			this.activeSpecIndex = 0;
			this.showSpecContainer = false;
			this.showSpecification = false;
		},
		// 規(guī)格彈框中的某個(gè)規(guī)格點(diǎn)擊
		specClick (item, index) {
			this.activeSpecIndex = index;
		},
		// 商品規(guī)格彈框中的加減號(hào)
		specCountOpt (event, item, specGoods, type) {
			(type ? bagAdd : bagDelete)({
				merchantId: this.merchantId, // 商戶id
				goodsId: specGoods.id, // 商品id
				specificationId: item.id // 規(guī)格id
			}, this.wxToken).then(res => {
				if (res) {
					if (type) {
						this.dotAnimate(event);
						if (!item.count) {
							specGoods.specNames = Array.from(new Set([...specGoods.specNames, item.name]));
						}
						item.count = (item.count ?? 0) + 1; // 該商品選中的某規(guī)格的總數(shù)
						specGoods.count = (specGoods.count ?? 0) + 1; // 該商品選中的各規(guī)格的總數(shù)
					} else {
						item.count--;
						if (!item.count) {
							specGoods.specNames.splice(specGoods.specNames.indexOf(item.name), 1);
						}
						specGoods.count--;
					}
					this.updateOther(type, item.price);
					this.updateShoppingBagDatas(specGoods.id, item.id, type, res);
				}
			});
		},
		// 購(gòu)物袋彈框中的加減號(hào)
		bagCoutOpt (item, type) {
			(type ? bagAdd : bagDelete)({
				merchantId: this.merchantId, // 商戶id
				goodsId: item.goodsId, // 商品id
				specificationId: item.specificationId // 規(guī)格id
			}, this.wxToken).then(res => {
				if (res) {
					// 如果是減號(hào),且該商品選購(gòu)數(shù)量==1,則將其從購(gòu)物袋中清除
					if (!type && item.count === 1) {
						delete this.shoppingBag[item.id];
					}
					this.getBagList();
				}
			});
		}
	},
	async onPullDownRefresh () {
		if (this.triggered) return;
		this.triggered = true;
		await this.getGoodsTypeList();
		this.pager = {
			currentPage: 1,
			pageSize: 10,
			pageCount: 0
		};
		await this.getGoodsList(this.activeTypeId);
	},
	async onReachBottom () {
		this.onTolowerMixin(() => this.getGoodsList(this.activeTypeId));
	}
};
</script>


2、mall.less

page {
	height: 100vh;
	overflow-y: scroll;
	.container {
		padding-top: 400rpx;
		.fixed-header {
			position: fixed;
			z-index: 998;
			top: 0;
			width: 100vw;
			height: 434rpx;
			image {
				width: 100vw;
			}
		}
		.relative-container {
			width: 100vw;
			z-index: 998;
    		background-color: #FFFFFF;
			border-radius: 35rpx 35rpx 0 0;
			.mall-info {
				margin-top: 38rpx;
				padding: 0 20rpx;
				box-sizing: border-box;
				.name-phone {
					.left {
						text:nth-child(1) {
							font-family: PingFang-SC-Bold;
							font-weight: bold;
							color: #333333;
						}
						text:nth-child(2n) {
							display: inline-block;
							margin-top: 25rpx;
							font-family: PingFangSC-Light;
							font-weight: 300;
							color: #666666;
						}
					}
					image {
						width: 50rpx;
						height: 50rpx;
					}
				}
				.address {
					position: relative;
					margin-top: 30rpx;
					height: 130rpx;
					padding-top: 14rpx;
					box-sizing: border-box;
					.p-name {
						width: 500rpx;
						color: #333333;
						font-weight: bold;
						font-family: PingFang-SC-Bold;
					}
					.p-dis {
						margin-top: 21rpx;
						color: #666666;
						font-family: PingFangSC-Regular;
						font-weight: 400;
					}
					image {
						position: absolute;
					}
					.nav-bg {
						width: 290rpx;
						height: 129rpx;
						right: 0;
						top: 0;
						z-index: -1;
					}
					.nav {
						right: 0;
						top: 50%;
						transform: translateY(-50%);
					}
				}
			}
			.goods-type-g {
				margin-top: 45rpx;
				.type-goods-container {
					padding-bottom: 200rpx;
					box-sizing: border-box;
					.goods-type {
						width: 152rpx;
						background-color: #F8F8F8;
						&-item {
							.type-name {
								position: relative;
								width: 152rpx;
								height: 61rpx;
								color: #9B9B9B;
								background-color: #F8F8F8;
								text-align: center;
								margin: 18rpx 0;
								.badge {
									position: absolute;
									top: 0;
									right: 0rpx;
									min-width: 30rpx;
									height: 30rpx;
									line-height: 30rpx;
									text-align: center;
									color: #FFFFFF;
									background: #FE3A46;
									&.rectangle {
										padding: 0 8rpx;
										border-radius: 16rpx;
									}
									&.circle {
										border-radius: 50%;
									}
								}
								.badge-hidden {
									opacity: 0;
									position: absolute;
									top: 0;
									right: 0rpx;
									width: 30rpx;
									height: 30rpx;
								}
							}
							&.active {
								.type-name {
									background-color: #FFFFFF !important;
									color: #010101 !important;
									&::after {
										content: '';
										position: absolute;
										width: 6rpx;
										height: 45rpx;
										left: 0;
										top: 50%;
										transform: translateY(-50%);
										background: #FFA00C;
										border-radius: 0 7rpx 7rpx 0;
									}
								}
							}
						}
					}
					.goods-item {
						position: relative;
						height: 232rpx;
						padding: 25rpx 21rpx;
						box-sizing: border-box;
						background-color: #FFFFFF;
						.goods-img {
							width: 183rpx;
							height: 183rpx;
							border-radius: 30rpx;
							margin-right: 21rpx;
						}
						.goods-info {
							// width: 353rpx;
							.goods-name {
								width: 335rpx;
								display: -webkit-box;
								overflow: hidden;
								text-overflow: ellipsis;
								-webkit-line-clamp: 2;
								-webkit-box-orient: vertical;
								font-weight: 800;
								color: #3E3A39;
								font-family: PingFang-SC-Heavy;
							}
							.goods-sell {
								display: inline-block;
								margin: 10rpx 0 22rpx 0;
								font-family: PingFang-SC-Medium;
								font-weight: 500;
								color: #828180;
							}
							.goods-price {
								width: 115rpx;
								color: #FF4200;
								font-family: PingFang-SC-Bold;
								font-weight: bold;
							}
						}
						.specification {
							.gg {
								position: relative;
								width: 93rpx;
								height: 41rpx;
								line-height: 41rpx;
								font-family: PingFang-SC-Medium;
								text-align: center;
								background: #FFA00C;
								border-radius: 10rpx;
								color: #FFFFFF;
								.spec-item-count {
									display: inline-block;
									position: absolute;
									top: -16rpx;
									right: -13rpx;
									min-width: 30rpx;
									height: 30rpx;
									line-height: 30rpx;
									text-align: center;
									color: #FFFFFF;
									background: #FE3A46;
									&.rectangle {
										padding: 0 8rpx;
										border-radius: 16rpx;
									}
									&.circle {
										border-radius: 50%;
									}
								}
							}
						}
						.count-opt {
							.opt-b {
								width: 43rpx;
								height: 43rpx;
								border-radius: 50%;
								line-height: 43rpx;
								text-align: center;
							}
							.reduce {
								border: 1rpx solid #FFA00C;
							}
							.choosed {
								display: inline-block;
								margin: 0 16rpx;
							}
							.add {
								border: 1rpx solid #FFEBCB;
								background: #FFEBCB;
								color: #FFA00C;
							}
						}
					}
				}
				.fixed-bottom {
					position: fixed;
					width: 100vw;
					height: 74rpx;
					bottom: 0;
					background-color: #FFFFFF;
				}
			}
		}
		.func-bar {
			position: fixed;
			z-index: 9999;
			width: 708rpx;
			height: 98rpx;
			left: 50%;
			transform: translateX(-50%);
			top: 90vh;
			// bottom: 50rpx;
			overflow: hidden;
			background: #FFFFFF;
			box-shadow: 0 2rpx 10rpx 0 rgba(255, 160, 12, 0.2);
			border-radius: 49rpx;
			.shopping-bag {
				position: relative;
				width: 52rpx;
				height: 60rpx;
				margin-left: 42rpx;
				margin-right: 38rpx;
				image {
					width: 52rpx;
					height: 60rpx;
				}
				.badge {
					position: absolute;
					top: 0;
					right: -13rpx;
					min-width: 30rpx;
					height: 30rpx;
					line-height: 30rpx;
					text-align: center;
					color: #FFFFFF;
					background: #FE3A46;
					&.rectangle {
						padding: 0 8rpx;
						border-radius: 16rpx;
					}
					&.circle {
						border-radius: 50%;
					}
				}
			}
			.total-price {
				display: inline-block;
				margin-right: 15rpx;
				color: #FF4200;
				font-weight: bold;
			}
			.total {
				font-weight: 400;
				color: #828180;
			}
			.place-order {
				width: 207rpx;
				height: 98rpx;
				line-height: 98rpx;
				text-align: center;
				background-color: #FE3A46;
				color: #FFFFFF;
			}
		}
		.spec {
			position: fixed;
			z-index: 9999;
			top: 16vh;
			opacity: 0;
			width: 710rpx;
			height: 58vh;
			left: 50%;
			transition: all 0.2s ease-in;
			transform: translateX(-50%) scale(0);
			&.show {
				opacity: 1;
				transform: translateX(-50%) scale(1) !important;
			}
			&.hidden {
				opacity: 0;
				transform: translateX(-50%) scale(0) !important;
			}
			.spec-container {
				position: relative;
				width: 100%;
				// height: 644rpx;
				height: 50vh;
				border-radius: 10rpx;
				padding-top: 30rpx;
				box-sizing: border-box;
				background-color: #FFFFFF;
				.goods-name {
					padding: 0 23rpx;
					font-family: PingFangSC-Semibold;
					font-weight: 600;
					color: #000000;
				}
				.spec-con {
					margin-top: 30rpx;
					text {
						padding: 0 23rpx;
						font-family: PingFangSC-Regular;
						font-weight: 400;
						color: #8A8A8A;
					}
					.specs {
						margin-top: 20rpx;
						padding: 0 23rpx;
						height: 100%;
						flex-wrap: wrap;
						overflow-y: scroll;
						.spec-item {
							height: 58rpx;
							padding: 0 25rpx;
							margin-right: 30rpx;
							margin-bottom: 20rpx;
							line-height: 58rpx;
							text-align: center;
							background: #F2F2F4;
							border: 1rpx solid #F2F2F4;
							border-radius: 10rpx;
							&.active {
								border-color: #FFA00C;
								background-color: #FFF6E9;
								color: #FFA00C;
							}
						}
					}
				}
				.spec-choosed {
					display: inline-block;
					width: 100%;
					padding: 22rpx 30rpx;
					box-sizing: border-box;
					background: #FAFAFA;
					color: #656565;
				}
				.spec-count-opt {
					// position: relative;
					height: 110rpx;
					margin-top: 20rpx;
					padding: 5rpx 23rpx;
					.add-bag {
						position: relative;
						width: 185rpx;
						height: 61rpx;
						line-height: 61rpx;
						text-align: center;
						background: #FFA00C;
						border-radius: 10rpx;
						font-family: PingFang SC;
						font-weight: 400;
						color: #FFFFFF;
						text:nth-child(1) {
							display: inline-block;
							margin-right: 10rpx;
							transform: scale(1.5);
						}
					}
					.count-opt {
						// position: absolute;
						// top: 50%;
						// transform: translateY(-50%);
						right: 22rpx;
						.opt-b {
							width: 43rpx;
							height: 43rpx;
							border-radius: 50%;
							line-height: 43rpx;
							text-align: center;
						}
						.reduce {
							border: 1rpx solid #FFA00C;
						}
						.choosed {
							display: inline-block;
							margin: 0 16rpx;
						}
						.add {
							border: 1rpx solid #FFEBCB;
							background: #FFEBCB;
							color: #FFA00C;
						}
					}
				}
				.close-btn {
					position: absolute;
					width: 77rpx;
					height: 77rpx;
					bottom: -130rpx;
					left: 50%;
					border-radius: 50%;
					transform: translateX(-50%);
				}
			}
		}
		.bag-container {
			position: relative;
			height: 100%;
			padding: 90rpx 20rpx 170rpx 29rpx;
			box-sizing: border-box;
			background-color: #FFFFFF;
			.bag-header {
				position: absolute;
				padding: 0 20rpx 0 29rpx;
				top: 43rpx;
				left: 0;
				right: 0;
				.total {
					color: #A09F9E;
				}
				image {
					width: 24rpx;
					height: 27rpx;
					margin-right: 13rpx;
				}
			}
			.bg-list {
				height: 100%;
				overflow-y: scroll;
				.bg-item {
					position: relative;
					padding: 20rpx 0;
					image {
						width: 113rpx;
						height: 113rpx;
						border-radius: 20rpx;
						margin-right: 20rpx;
					}
					.count-opt {
						position: absolute;
						bottom: 28rpx;
						right: 22rpx;
						.opt-b {
							width: 43rpx;
							height: 43rpx;
							border-radius: 50%;
							line-height: 43rpx;
							text-align: center;
						}
						.reduce {
							border: 1rpx solid #FFA00C;
						}
						.choosed {
							display: inline-block;
							margin: 0 16rpx;
						}
						.add {
							border: 1rpx solid #FFEBCB;
							background: #FFEBCB;
							color: #FFA00C;
						}
					}
				}
			}
		}
		.dot {
			position: fixed;
			opacity: 0;
			width: 20rpx;
			height: 20rpx;
			z-index: 9999;
			border-radius: 50%;
			background-color: #FF4200;
		}
	}
}

3、mall.config.js

export default definePageConfig({
	'navigationStyle': 'custom',
	'navigationBarTextStyle': 'white',
	'enablePullDownRefresh': true, // 當(dāng)前頁(yè)
	'backgroundTextStyle': 'dark', // 頂部顯示顏色為深色的三個(gè)點(diǎn)
	onReachBottomDistance: 10
});

4、 twoBezier

/**
     * @desc 二階貝塞爾
     * @param {number} t 當(dāng)前百分比
     * @param {Array} p1 起點(diǎn)坐標(biāo)
     * @param {Array} cp 控制點(diǎn)
     * @param {Array} p2 終點(diǎn)坐標(biāo)
     */
const twoBezier = (t, p1, cp, p2) => {
	const [x1, y1] = p1;
	const [cx, cy] = cp;
	const [x2, y2] = p2;
	const x = (1 - t) * (1 - t) * x1 + 2 * t * (1 - t) * cx + t * t * x2;
	const y = (1 - t) * (1 - t) * y1 + 2 * t * (1 - t) * cy + t * t * y2;
	return [x, y];
};

到了這里,關(guān)于Taro微信小程序?qū)崿F(xiàn) 美團(tuán)購(gòu)物車(chē)小紅點(diǎn)動(dòng)畫(huà)效果的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來(lái)自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • 微信小程序購(gòu)物車(chē)頁(yè)面實(shí)現(xiàn)

    微信小程序購(gòu)物車(chē)頁(yè)面實(shí)現(xiàn)

    目錄 32.商品加入購(gòu)物車(chē)邏輯實(shí)現(xiàn)(前端) 33.購(gòu)物車(chē)頁(yè)面收貨地址實(shí)現(xiàn) 34.購(gòu)物車(chē)商品列表顯示實(shí)現(xiàn) 37.購(gòu)物車(chē)商品復(fù)選框選中業(yè)務(wù)處理 38.購(gòu)物車(chē)全選復(fù)選框選中業(yè)務(wù)處理 39.購(gòu)物車(chē)商品數(shù)量編輯實(shí)現(xiàn) 40.購(gòu)物車(chē)商品數(shù)量為0判定是否刪除 42.商品詳情立即購(gòu)買(mǎi)邏輯實(shí)現(xiàn) 1.先在produc

    2024年02月11日
    瀏覽(21)
  • 微信小程序?qū)崿F(xiàn)購(gòu)物商城(附源碼)

    微信小程序?qū)崿F(xiàn)購(gòu)物商城(附源碼)

    2018年本人做了一個(gè)淘寶購(gòu)物返利的微信公眾號(hào),截至目前已運(yùn)營(yíng)了近5年的時(shí)間,也陸續(xù)積累了不少粉絲。近日,有部分用戶反饋是否可以在公眾號(hào)上展示促銷商品列表,而且要具備搜索功能。為感謝粉絲朋友們的長(zhǎng)期支持,筆者耗時(shí)一周,利用茶余飯后時(shí)間,開(kāi)發(fā)了一個(gè)微

    2024年02月11日
    瀏覽(27)
  • 微信小程序?qū)崿F(xiàn)商品加入購(gòu)物車(chē)案例

    微信小程序?qū)崿F(xiàn)商品加入購(gòu)物車(chē)案例

    思考: 購(gòu)物車(chē)中的數(shù)據(jù)保存在哪里?用哪種數(shù)據(jù)結(jié)構(gòu)進(jìn)行保存? 小程序中可能有多個(gè)頁(yè)面需要對(duì)購(gòu)物車(chē)中的數(shù)據(jù)進(jìn)行操作,因此我們想到把數(shù)據(jù)存到全局中??梢允褂?wx.setStorageSync() 儲(chǔ)存,用 wx.getStorageSync() 進(jìn)行獲取,以數(shù)組格式方便對(duì)數(shù)據(jù)進(jìn)行操作。 一、商品加入購(gòu)物車(chē)

    2024年02月10日
    瀏覽(19)
  • 微信小程序在線商城購(gòu)物系統(tǒng)設(shè)計(jì)與實(shí)現(xiàn)

    微信小程序在線商城購(gòu)物系統(tǒng)設(shè)計(jì)與實(shí)現(xiàn)

    ?博主介紹 :黃菊華老師《Vue.js入門(mén)與商城開(kāi)發(fā)實(shí)戰(zhàn)》《微信小程序商城開(kāi)發(fā)》圖書(shū)作者,CSDN博客專家,在線教育專家,CSDN鉆石講師;專注大學(xué)生畢業(yè)設(shè)計(jì)教育和輔導(dǎo)。 所有項(xiàng)目都配有從入門(mén)到精通的基礎(chǔ)知識(shí)視頻課程,免費(fèi) 項(xiàng)目配有對(duì)應(yīng)開(kāi)發(fā)文檔、開(kāi)題報(bào)告、任務(wù)書(shū)、

    2024年02月04日
    瀏覽(31)
  • 微信小程序畢業(yè)設(shè)計(jì)作品成品(39)微信小程序在線購(gòu)物商城系統(tǒng)設(shè)計(jì)與實(shí)現(xiàn)

    微信小程序畢業(yè)設(shè)計(jì)作品成品(39)微信小程序在線購(gòu)物商城系統(tǒng)設(shè)計(jì)與實(shí)現(xiàn)

    博主介紹: 《Vue.js入門(mén)與商城開(kāi)發(fā)實(shí)戰(zhàn)》《微信小程序商城開(kāi)發(fā)》圖書(shū)作者,CSDN博客專家,在線教育專家,CSDN鉆石講師;專注大學(xué)生畢業(yè)設(shè)計(jì)教育和輔導(dǎo)。 所有項(xiàng)目都配有從入門(mén)到精通的基礎(chǔ)知識(shí)視頻課程,免費(fèi) 項(xiàng)目配有對(duì)應(yīng)開(kāi)發(fā)文檔、開(kāi)題報(bào)告、任務(wù)書(shū)、PPT、論文模版

    2024年02月08日
    瀏覽(29)
  • 基于微信小程序的家具購(gòu)物小程序的設(shè)計(jì)與實(shí)現(xiàn)

    基于微信小程序的家具購(gòu)物小程序的設(shè)計(jì)與實(shí)現(xiàn)

    隨著信息技術(shù)在管理上越來(lái)越深入而廣泛的應(yīng)用,管理信息系統(tǒng)的實(shí)施在技術(shù)上已逐步成熟。本文介紹了基于微信小程序的家具購(gòu)物小程序的設(shè)計(jì)與實(shí)現(xiàn)的開(kāi)發(fā)全過(guò)程。通過(guò)分析基于微信小程序的家具購(gòu)物小程序的設(shè)計(jì)與實(shí)現(xiàn)管理的不足,創(chuàng)建了一個(gè)計(jì)算機(jī)管理基于微信小程

    2024年03月16日
    瀏覽(37)
  • 微信小程序畢業(yè)設(shè)計(jì)作品成品(60)微信小程序網(wǎng)上在線購(gòu)物商城系統(tǒng)設(shè)計(jì)與實(shí)現(xiàn)

    微信小程序畢業(yè)設(shè)計(jì)作品成品(60)微信小程序網(wǎng)上在線購(gòu)物商城系統(tǒng)設(shè)計(jì)與實(shí)現(xiàn)

    博主介紹: 《Vue.js入門(mén)與商城開(kāi)發(fā)實(shí)戰(zhàn)》《微信小程序商城開(kāi)發(fā)》圖書(shū)作者,CSDN博客專家,在線教育專家,CSDN鉆石講師;專注大學(xué)生畢業(yè)設(shè)計(jì)教育和輔導(dǎo)。 所有項(xiàng)目都配有從入門(mén)到精通的基礎(chǔ)知識(shí)視頻課程,免費(fèi) 項(xiàng)目配有對(duì)應(yīng)開(kāi)發(fā)文檔、開(kāi)題報(bào)告、任務(wù)書(shū)、PPT、論文模版

    2024年02月08日
    瀏覽(23)
  • 微信小程序畢業(yè)設(shè)計(jì)作品成品(77)微信小程序網(wǎng)絡(luò)書(shū)城圖書(shū)購(gòu)物商城系統(tǒng)設(shè)計(jì)與實(shí)現(xiàn)

    微信小程序畢業(yè)設(shè)計(jì)作品成品(77)微信小程序網(wǎng)絡(luò)書(shū)城圖書(shū)購(gòu)物商城系統(tǒng)設(shè)計(jì)與實(shí)現(xiàn)

    博主介紹: 《Vue.js入門(mén)與商城開(kāi)發(fā)實(shí)戰(zhàn)》《微信小程序商城開(kāi)發(fā)》圖書(shū)作者,CSDN博客專家,在線教育專家,CSDN鉆石講師;專注大學(xué)生畢業(yè)設(shè)計(jì)教育和輔導(dǎo)。 所有項(xiàng)目都配有從入門(mén)到精通的基礎(chǔ)知識(shí)視頻課程,免費(fèi) 項(xiàng)目配有對(duì)應(yīng)開(kāi)發(fā)文檔、開(kāi)題報(bào)告、任務(wù)書(shū)、PPT、論文模版

    2024年02月08日
    瀏覽(35)
  • 微信小程序畢業(yè)設(shè)計(jì)作品成品(55)微信小程序網(wǎng)上書(shū)城圖書(shū)購(gòu)物商城系統(tǒng)設(shè)計(jì)與實(shí)現(xiàn)

    微信小程序畢業(yè)設(shè)計(jì)作品成品(55)微信小程序網(wǎng)上書(shū)城圖書(shū)購(gòu)物商城系統(tǒng)設(shè)計(jì)與實(shí)現(xiàn)

    博主介紹: 《Vue.js入門(mén)與商城開(kāi)發(fā)實(shí)戰(zhàn)》《微信小程序商城開(kāi)發(fā)》圖書(shū)作者,CSDN博客專家,在線教育專家,CSDN鉆石講師;專注大學(xué)生畢業(yè)設(shè)計(jì)教育和輔導(dǎo)。 所有項(xiàng)目都配有從入門(mén)到精通的基礎(chǔ)知識(shí)視頻課程,免費(fèi) 項(xiàng)目配有對(duì)應(yīng)開(kāi)發(fā)文檔、開(kāi)題報(bào)告、任務(wù)書(shū)、PPT、論文模版

    2024年02月07日
    瀏覽(23)
  • 基于Java+Vue+uniapp微信小程序購(gòu)物系統(tǒng)設(shè)計(jì)和實(shí)現(xiàn)

    基于Java+Vue+uniapp微信小程序購(gòu)物系統(tǒng)設(shè)計(jì)和實(shí)現(xiàn)

    博主介紹 : ? 全網(wǎng)粉絲30W+,csdn特邀作者、博客專家、CSDN新星計(jì)劃導(dǎo)師、Java領(lǐng)域優(yōu)質(zhì)創(chuàng)作者,博客之星、掘金/華為云/阿里云/InfoQ等平臺(tái)優(yōu)質(zhì)作者、專注于Java技術(shù)領(lǐng)域和學(xué)生畢業(yè)項(xiàng)目實(shí)戰(zhàn),高校老師/講師/同行交流合作 ? ?? 文末獲取源碼聯(lián)系 ?? ?????精彩專欄 推薦訂閱

    2024年02月03日
    瀏覽(36)

覺(jué)得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包