first commit

This commit is contained in:
wtq
2025-11-28 10:10:21 +08:00
commit 7e09df72c1
263 changed files with 35495 additions and 0 deletions

View File

@@ -0,0 +1,74 @@
<template>
<div class="my-bone">
<div class="top">
</div>
<div class="list">
<div class="list-item" v-for="item in arr" :key="item">
<div class="left"></div>
<div class="right">
<div class="right_div"></div>
<div class="right_div"></div>
<div class="right_div"></div>
<div class="right_div"></div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
data () {
return {
arr: [1,2,3,4,5,6,7,8,9,10]
}
}
}
</script>
<style lang="scss">
.my-bone {
position: fixed;
left: 0;
top: 0;
bottom: 0;
right: 0;
z-index: 9999;
background: white;
padding: 20rpx;
overflow: hidden;
.top {
height: 260rpx;
background: #eee;
}
.list {
margin-top: 40rpx;
.list-item {
margin-top: 40rpx;
display: flex;
height: 260rpx;
}
.left {
flex: none;
width: 200rpx;
margin-right: 20rpx;
background: #eee;
}
.right {
flex: auto;
display: flex;
flex-flow: column;
justify-content: space-between;
.right_div{
height: 40rpx;
background: #eee;
}
// div {
// height: 40rpx;
// background: #eee;
// }
}
}
}
</style>

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,48 @@
<template>
<div class="zy-loading" v-if="loadingShow" @touchmove.stop.prevent="moveHandle" :animation="animationData">
<div class="zy-loading-mask"></div>
<div class="zy-loading-border"></div>
<div class="zy-loading-img"></div>
</div>
</template>
<script>
export default {
name: 'ZYLoading',
data () {
return {
animationData: {}
}
},
computed: {
loadingShow() {
// console.log('this.$store',this.$store.getters.loading)
return this.$store.getters.loading
}
},
methods: {
show () {
const animation = uni.createAnimation({
duration: 500,
timingFunction: 'ease-in-out'
})
animation.opacity(0).step()
this.animationData = animation.export()
setTimeout(() => {
animation.opacity(1).step()
this.animationData = animation.export()
}, 50)
},
moveHandle () {}
},
watch: {
loadingShow (to) {
// if (to) this.show()
}
}
}
</script>
<style lang="scss">
@use './loading';
</style>

View File

@@ -0,0 +1,113 @@
@use '@/assets/bundle.scss';
.y-modal {
position: fixed;
z-index: 900;
left: 0;
top: 0;
width: 100vw;
height: 100vh;
background: rgba(black, .6);
animation: bounceInUp .8s ease-in-out;
.y-modal-wrapper {
@extend %flex-center;
height: 80vh;
}
}
.y-promopt {
@extend %bgwhite;
width: 600rpx;
padding: 40rpx;
border-radius: 10rpx;
box-shadow: 0 2rpx 4rpx rgba(black, .3);
border: 1rpx solid #4EB331;
@extend %ft30;
color: #333;
box-sizing: border-box;
// .title {
// }
.textarea {
border: 1rpx solid #DCDFE6;
@extend %pd20;
@extend %ft30;
height: 150rpx;
border-radius: 10rpx;
margin: 40rpx 0 80rpx;
width: 100%;
box-sizing: border-box;
}
.btn-group {
@extend %flex-between;
}
.btn-cancel, .btn-confirm {
@extend %btn-base;
}
.btn-cancel {
margin-right: 20rpx;
background: #F56C6C;
}
}
.y-confirm-check {
@extend %ft32;
@extend %bgwhite;
border-radius: 6rpx;
width: 600rpx;
.title {
@extend %ft36;
padding: 32rpx 0 20rpx;
text-align: center;
}
.icon-text {
color: #909399;
@extend %flex-center;
// line-height: 0;
margin-top: 20rpx;
.ipt{
margin-bottom: 5rpx;
border: 1rpx solid #cdcdcd;
border-radius: 10rpx;
}
}
.icon-check {
@extend %bg-no-repeat-center;
background-size: 60%;
width: 30rpx;
height: 30rpx;
border: 1rpx solid #DCDFE6;
border-radius: 50%;
margin-right: 10rpx;
}
.checked {
@extend %icon-checked-main;
border-color: #4EB331;
}
.body {
padding-bottom: 48rpx;
}
.text {
@extend %ft30;
color: #909399;
text-align: center;
}
.btn-group {
box-sizing: border-box;
border-top: 1rpx solid #eee;
@extend %flex-left;
}
.btn-cancel, .btn-confirm {
flex: 1;
@extend %ft36;
height: 100rpx;
@extend %flex-center;
}
.btn-cancel {
color: #909399;
border-right: 1rpx solid #eee;
}
.btn-confirm {
color: #4EB331;
}
}

View File

@@ -0,0 +1,18 @@
<template>
<div class="y-modal" @click.stop @touchmove.stop.prevent>
<!-- <div class="h5-placeholder"></div> -->
<div class="y-modal-wrapper">
<slot></slot>
</div>
</div>
</template>
<script>
export default {
methods: {},
};
</script>
<style lang="scss">
@use "./modal.scss";
</style>

View File

@@ -0,0 +1,53 @@
<template>
<Modal>
<div class="y-promopt">
<!-- <div class="title">{{title}}:</div> -->
<textarea v-model.trim="text" class="textarea" :placeholder="title" auto-focus></textarea>
<div class="btn-group">
<div @click="cancel" class="btn-cancel">取消</div>
<div @click="confirm" class="btn-confirm">确定</div>
</div>
</div>
</Modal>
</template>
<script>
import Modal from './modal'
export default {
components: {
Modal
},
props: {
title: {
type: String,
default: ''
},
type: {
type: String,
default: 'textarea'
}
},
data () {
return {
text: ''
}
},
methods: {
cancel () {
this.$emit('close')
},
confirm () {
if (!this.text) {
this.$toast(this.title)
return false
}
this.$emit('confirm', this.text)
this.$emit('close')
}
}
}
</script>
<style lang="scss">
@use "./modal.scss";
</style>

View File

@@ -0,0 +1,17 @@
@use '@/assets/bundle.scss';
.y-badge {
position: relative;
.y-badge-dot {
position: absolute;
border-radius: 30rpx;
background-color: #F56C6C;
right: 5rpx;
top: 5rpx;
font-size: 20rpx;
color: white;
padding: 6rpx;
min-width: 26rpx;
@extend %flex-center;
}
}

View File

@@ -0,0 +1,21 @@
<template>
<div class="y-badge">
<div class="y-badge-dot" v-if="count>0">{{count}}</div>
<slot></slot>
</div>
</template>
<script>
export default {
props: {
count: {
type: Number,
default: 0
}
}
}
</script>
<style lang="scss">
@use './badge.scss';
</style>

View File

@@ -0,0 +1,91 @@
@use '@/assets/bundle.scss';
.car-group {
overflow-y: auto;
margin: 20rpx auto;
background: #eee;
// width: 750rpx;
// box-sizing: border-box;
@extend %pdlr;
.car-item{
background:#fff;
padding:0 20rpx;
overflow: hidden;
border-radius: 20rpx;
}
.store-info {
@extend %flex-between;
font-size: 28rpx;
color: #333;
height: 100rpx;
border-bottom: 1rpx solid #E4E7ED;
// padding-left: 100rpx;
// border: 1rpx solid greenyellow;
background-color: #fff;
}
.circle{
width: 30rpx;
height: 30rpx;
border-radius: 50%;
margin-right: 20rpx;
background-color:#eee;
}
// .waybillSLG{
// border: 1rpx solid red;
// width: 150rpx;
// padding: 10rpx;
// white-space: nowrap;
// color: #4EB331;
// }
.icon-right-aw {
flex: none;
@extend %icon-aw-right-slim;
width: 30rpx;
background-position: right center;
background-repeat: no-repeat;
background-size: 30rpx;
}
.store-name {
flex: 1;
@extend %oneline;
}
.store-mobile {
flex: none;
}
.skuName-item{
display:flex;
// width: 90vw;
// border:1rpx solid red;
}
.skuName-cell {
@extend %flex-left;
// border-bottom: 1rpx solid #E4E7ED;
border: 1rpx solid #e2dfdf;
margin-bottom: 20rpx;
margin-top: 20rpx;
border-radius: 20rpx;
background-color: #fff;
padding: 10rpx;
// display: flex;
// width: 100vw;
}
.delButton{
// height: 240rpx;
line-height: 140rpx;
background: #ff0000;
// border: 1rpx solid green;
color: #fff;
margin-top: 10rpx;
margin-left:30rpx;
font-size: 30rpx;
// width: 100rpx;
padding:10rpx 20rpx;
}
.flex-auto {
@extend %flex-auto;
}
// .skuName-item:last-child{
// // margin-bottom: 194rpx;
// }
}

View File

@@ -0,0 +1,241 @@
<template>
<div class="car-group">
<div class="car-item">
<div class="store-info">
<div style="display:flex">
<div class="circle"></div>
<!-- isMaterial -->
<div class="store-name" v-if="isMaterial">物料店</div>
<!-- v-if="storeID!=='666666' && storeMap" -->
<div class="store-name" v-else >{{storeMap[storeID].name}}</div>
<!-- <div class="store-name" v-else="storeID!=='666666'">物料店</div> -->
</div>
<!-- <div class="waybillSLG">
<span >购满{{ waybillSLG.satisfy }}{{waybillSLG.reduce}}运费</span>
</div>
<div style="display:flex">
<div>去凑单</div>
<div class="icon-right-aw"></div>
</div> -->
<!-- <div class="store-mobile" @click="call(storeMap[storeID].mobile)">{{storeMap[storeID].mobile}}</div> -->
</div>
<!-- style="border:1rpx solid red;" -->
<div
v-for="(skuName, index) in skuNamesNew"
:key="index"
class="skuName-item"
:style="{'transform': 'translateX(' + skuName.offset + 'px)'}"
@touchmove="touchmove_cart"
@touchstart="touchstart_cart($event,index)"
@touchend="touchend_cart"
>
<div class="skuName-cell">
<check-item
v-if="!isMaterial"
@on-click="checkSkuName(skuName, index)"
:checked="skuName.checked"
></check-item>
<good-cell
:titleWidth="isMaterial?'482rpx':'412rpx'"
ref="goodCell"
class="flex-auto"
:index="index"
:skuName="skuName"
:noBorder="true"
:waitShow="false"
type="cart"
@handleSkuNamePlus="handleSkuNamePlus"
@handleSkuNameMinus="handleSkuNameMinus($event, skuName)"
@handleSkuNameChange="handleSkuNameChange"
></good-cell>
</div>
<div class="delButton" @click.stop="del(index)" :style="delBtnHenght">删除</div>
</div>
</div>
</div>
</template>
<script>
import GoodCell from "@/components/goodCmp/good-cell/good-cell.vue";
import CheckItem from "@/components/checkItem/check-item.vue";
import { errToast, modal } from "@/utils/uniapi.js";
import { mapGetters } from "vuex";
// import { msgData } from "@/config";
export default {
components: {
GoodCell,
CheckItem,
},
provide() {
return {
shopCar: true,
};
},
props: {
storeID: {
type: String,
},
skuNames: {
type: Array,
}
},
data() {
return {
checked: true,
currentIndex: -1,
skuNamesNew: [],
moveX: 0,
offsetDistance: 50,
delBtnHenght:'height: 260rpx;' // 140rpx
};
},
created() {
// console.log('打印手机系统信息', this.iosOrAndroid, '宽度', this.iosOrAndroid.screenWidth)
if (this.iosOrAndroid && this.iosOrAndroid.screenWidth) {
this.offsetDistance = (this.iosOrAndroid.screenWidth * 50) / 375
// console.log('查看偏移的宽度',this.offsetDistance)
}
// 750 260*2
// 1080 x
// x = (this.iosOrAndroid.screenHeight *260*2 )/ (750 * 2)
},
watch: {
skuNames: {
handler(val) {
this.skuNamesNew = []
val.forEach((item,index) => {
// #ifdef MP-WEIXIN
if (item.checked === false && item.storeID === 666666 ) {
this.checkSkuName(item,index)
}
// #endif
this.skuNamesNew.push({
...item,
offset:0
})
});
},
immediate: true,
deep:true
}
},
computed: {
...mapGetters({
storeMap:"indexPage/storeMap",
isMaterial: "indexPage/isMaterial",
iosOrAndroid: "iosOrAndroid"
})
},
methods: {
// 删除商品
async del(index) {
this.handleSkuNameChange({index,count:0})
},
touchstart_cart(e, index) {
// console.log('打印移动开始时的信息',e)
if (this.currentIndex !== -1 && this.currentIndex !== index) {
if (this.skuNamesNew[this.currentIndex]) {
this.skuNamesNew[this.currentIndex].offset = 0
}
}
this.moveX = e.changedTouches[0].pageX
// this.skuNamesNew[index].offset = -50
this.currentIndex = index
},
touchmove_cart(e) {
let distance = 0
// 向右滑动
if (e.changedTouches[0].pageX - this.moveX >= 0) {
distance = 0
// if (e.changedTouches[0].pageX - this.moveX >= 50) {
// distance = 50
// } else {
// distance = e.changedTouches[0].pageX - this.moveX
// }
}
// 向左滑动
if (e.changedTouches[0].pageX - this.moveX < 0) {
if (Math.abs(e.changedTouches[0].pageX - this.moveX) >= this.offsetDistance) {
// distance = -50
distance = - this.offsetDistance
} else {
distance = 0 - Math.abs(e.changedTouches[0].pageX - this.moveX)
}
}
// 偏移
this.skuNamesNew[this.currentIndex].offset = distance
},
// 手指松开后
touchend_cart(e) {
let distance = e.changedTouches[0].pageX - this.moveX
if (distance >0) {
// this.skuNamesNew[this.currentIndex].offset = distance >= 50?50: 0
this.skuNamesNew[this.currentIndex].offset = 0
}
if(distance<0) {
this.skuNamesNew[this.currentIndex].offset = Math.abs(distance) >= this.offsetDistance?0-this.offsetDistance: 0
}
},
// 商品 +
handleSkuNamePlus(index) {
this.$emit("handleSkuNamePlus", {
index,
key: this.storeID,
});
},
// 商品 -
handleSkuNameMinus(index, skuName, count = 2) {
// console.log('商品减少--------')
this.$emit("handleSkuNameMinus", {
index,
key: this.storeID,
countNew:count
});
},
handleSkuNameChange({ index, count }) {
this.$emit("handleSkuNameChange", {
index,
count,
key: this.storeID,
});
},
// check商品
async checkSkuName(skuName, index) {
try {
// 修改store里面
await this.$store.dispatch("cartPage/checkSkuName", {
skuName,
checkStatus: !skuName.checked,
});
// 通知父级修改
this.$emit("handleSkuNameCheck", {
index,
key: this.storeID,
checkStatus: !skuName.checked,
});
} catch (e) {
console.error(e);
errToast(e);
}
},
// 打电话
async call(phoneNumber) {
if (phoneNumber) {
let confirm = await modal("拨打电话", "是否拨打门店电话");
if (confirm) uni.makePhoneCall({ phoneNumber });
}
},
},
};
</script>
<style lang="scss">
@use "./car-group.scss";
</style>

View File

@@ -0,0 +1,24 @@
@use '@/assets/bundle.scss';
.y-check-box {
flex: none;
@extend %flex-left;
.check-cirle {
width: 50rpx;
height: 50rpx;
border-radius: 50%;
box-sizing: border-box;
border: 1rpx solid #DCDFE6;
margin-right: 20rpx;
}
.checked {
@extend %icon-checked;
background-color: #4EB331;
@extend %bg-no-repeat-center;
background-size: 52%;
}
.y-checkbox-text {
font-size: 32rpx;
color: #333;
}
}

View File

@@ -0,0 +1,53 @@
<template>
<div class="y-check-box" @click="handleClick">
<div
:class="{
'check-cirle': true,
'checked': checked
}"
:style="{
width: width + 'rpx',
height: height + 'rpx'
}"
>
</div>
<div class="y-checkbox-text" :style="{'font-size': fontSize + 'rpx'}" v-if="text">{{text}}</div>
</div>
</template>
<script>
export default {
name: 'checkItem',
props: {
checked: {
type: Boolean,
default: false
},
text: {
type: String,
default: ''
},
width: {
type: Number,
default: 50
},
height: {
type: Number,
default: 50
},
fontSize: {
type: Number,
default: 32
}
},
methods: {
handleClick () {
this.$emit('on-click')
}
}
}
</script>
<style lang="scss">
@use './check-item.scss'
</style>

View File

@@ -0,0 +1,31 @@
@use '@/assets/bundle.scss';
.img-lazy-load {
position: relative;
width: 100%;
height: 100%;
.img-thumb, .img-origin {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
filter: blur(1vw);
object-fit: fill;
}
.img-origin {
animation: fadeIn .2s;
animation-fill-mode: both;
}
}
@keyframes fadeIn {
from {
filter: blur(1vw);
// transform: scale(1.05);
}
to {
filter: blur(0);
// transform: scale(1);
}
}

View File

@@ -0,0 +1,52 @@
<template>
<div class="img-lazy-load">
<!-- | img150type2 -->
<image class="img-thumb" :mode="mode" :src="src" @load="imgOnload" @error="errorImg"></image>
<image v-if="originSrc" class="img-origin" :mode="mode" :src="url"></image>
</div>
</template>
<script>
export default {
props: {
mode: {
type: String,
default: 'aspectFill'
},
src: {
type: String,
required: true
},
clip: {
type: String
}
},
data () {
return {
originSrc: '',
url:''
}
},
methods: {
errorImg(e) {
console.log('图片加载错误',e)
},
// 图片加载完成
imgOnload () {
this.url = this.src.includes('https') ? this.src : this.src.replace('http','https')
uni.getImageInfo({
// src: this.src.replace('http://', 'https://') + `?` + this.clip,
src: this.url,
success: ({path}) => {
// console.log('path',path)
this.originSrc = path
}
})
}
}
}
</script>
<style lang="scss">
@use './img-lazy.scss';
</style>

View File

@@ -0,0 +1,29 @@
@use '@/assets/bundle.scss';
.cmp-list-none {
@extend %flex-center;
flex-flow: column;
.list-none-img {
width: 300rpx;
height: 300rpx;
@extend %bg-no-repeat-center;
@extend %bg-size-100;
}
.img-noGoods {
@extend %img-noGoods;
}
.img-noOrders {
@extend %img-noOrders;
}
.img-noShopCar {
@extend %img-noShopCar;
}
.img-noPosition {
@extend %icon-no-position;
}
.list-none-msg {
font-size: 32rpx;
color: #909399;
padding-top: 30rpx;
}
}

View File

@@ -0,0 +1,32 @@
<template>
<div class="cmp-list-none">
<div :class="{
'list-none-img': true,
'img-noGoods': img === 'img-noGoods',
'img-noShopCar': img === 'img-noShopCar',
'img-noOrders': img === 'img-noOrders',
'img-noPosition': img === 'img-noPosition'
}"></div>
<div class="list-none-msg">{{msg}}</div>
</div>
</template>
<script>
export default {
props: {
img: {
type: String,
required: true,
default: 'img-noGoods'
},
msg: {
type: String,
default: '没有查询到东西'
}
}
}
</script>
<style lang="scss">
@use './list-none.scss';
</style>

View File

@@ -0,0 +1,53 @@
@use '@/assets/bundle.scss';
.cmp-search-input {
@extend %flex-center;
.input-wrapper {
// position: relative;
flex: 1;
background-color: #efefef;
// padding: 10rpx;
box-sizing: border-box;
border-radius: 50rpx;
@extend %flex-center;
height: 60rpx;
}
.input {
flex: auto;
color: #333;
width: 100%;
font-size: 30rpx;
padding: 10rpx;
}
.input-placeholder {
color: #666;
}
.icon-search, .icon-delete {
flex: none;
// position: absolute;
top: 50%;
// transform: translateY(-50%);
width: 50rpx;
height: 50rpx;
@extend %bg-no-repeat-center;
background-size: 30rpx;
z-index: 10;
}
.icon-search {
left: 10rpx;
@extend %icon-search;
}
.icon-delete {
right: 10rpx;
@extend %icon-delete;
}
.btn-search {
font-size: 30rpx;
margin-left:20rpx;
color: #fff;
border-radius: 70rpx;
padding: 10rpx 20rpx;
background-color: rgb(129, 190, 78);
text-align: center;
}
}

View File

@@ -0,0 +1,67 @@
<template>
<div class="cmp-search-input">
<div class="input-wrapper">
<div class="icon-search"></div>
<input
v-model.trim="text"
class="input"
type="text"
:placeholder="placeholder"
placeholder-class="input-placeholder"
confirm-type="search"
@input="handleInput"
@confirm="handleConfirm"
:focus="focus"
>
<div v-if="text" class="icon-delete" @click="handleDelete"></div>
</div>
<div v-if="searchBtnShow" class="btn-search" @click="handleConfirm">搜索</div>
</div>
</template>
<script>
import {debounce} from '@/utils'
export default {
props: {
searchBtnShow: {
type: Boolean,
default: true
},
placeholder: {
type: String,
default: '请输入商品名称'
},
focus: {
type: Boolean,
default: true
}
},
data () {
return {
text: ''
}
},
methods: {
handleInput: debounce(function (e) {
// console.log(e.detail.value)
let value = e.detail.value
// console.log(value)
this.$emit('on-change', value)
}, 500),
handleConfirm (e) {
// console.log(e.detail.value)
let value = e.detail.value || this.text
// console.log(value)
this.$emit('on-change', value)
},
handleDelete () {
this.text = ''
this.$emit('on-change', '')
}
}
}
</script>
<style lang="scss">
@use './search-input.scss';
</style>

View File

@@ -0,0 +1,38 @@
<template>
<div class="zy-search-btn" :class="{'showBtn':isShowBtn===true}" @click="handleClick" :style="{background: background, color: color}">
<div class="zy-search-btn" >
<div class="icon"></div>
<div class="placeholder" >{{name}}</div>
</div>
<div v-if="isShowBtn" class="searchBtn" >搜索</div>
</div>
</template>
<script>
export default {
name: 'ZySearchBtn',
props: {
name: {
default: '请输入商品名称'
},
background: {
default: '#f5f5f5'
},
color: {
default: '#cccccc'
},
isShowBtn: {
default:false
}
},
methods: {
handleClick () {
this.$emit('click')
}
}
}
</script>
<style lang="scss">
@use "./search.scss";
</style>

View File

@@ -0,0 +1,31 @@
@use '@/assets/bundle.scss';
.zy-search-btn {
height: 70rpx;
@extend %flex-center;
font-size: 30rpx;
border-radius: 70rpx;
.icon {
// 放大镜
// @include svg_icon(search, a8a8a8);
@extend %icon-search;
@extend %bg-no-repeat-center;
@extend %bg-size-100;
width: 36rpx;
height: 36rpx;
margin-right: 10rpx;
margin-left:10rpx;
}
}
.showBtn{
display: flex;
justify-content: space-between;
}
.searchBtn{
// 搜索按钮
padding:10rpx 20rpx;
border-radius: 25rpx;
margin-right: 10rpx;
background: rgb(129, 190, 78);
color:#fff;
}

View File

@@ -0,0 +1,120 @@
@use '@/assets/bundle.scss';
.good-cell-big {
@extend %pd20;
@extend %bgwhite;
box-sizing: border-box;
box-shadow: 0 0 4rpx rgba(#DCDFE6, 1);
border-radius: 10rpx;
.good-img {
width: 300rpx;
height: 300rpx;
.good-img-size{
width: 100%;
height: 100%;
}
}
.good-name {
font-size: 28rpx;
margin-top: 20rpx;
color: #333;
height: 4em;
overflow: hidden;
text-align: justify;
line-height: 1.2;
}
.good-price {
display: flex;
align-items: center;
justify-content: space-between;
}
.btn-wait{
flex: 1;
text-align: right;
}
.btn-self{
color: #fff;
background-color: rgba(78, 179, 49, 0.6);
}
.countTime-wrap{
height: 17rpx;
width: 100%;
margin-bottom: 5rpx;
}
.countTime{
color: #E6A23C;
font-size: 26rpx;
}
.stock-and-waitPirce{
display: flex;
}
.price {
display: flex;
align-items: baseline;
.unit{
font-size:28rpx;
color:#fe6263;
}
}
.ori-price {
font-size: 20rpx;
color: #b2b2b2;
margin-left: 10rpx;
text-decoration: line-through;
&:before {
content: "";
}
}
.act-type-wrapper {
height: 20rpx;
overflow: hidden;
}
.act-type {
@extend %act-type-text;
}
.check-detail{
font-size:28rpx;
color:rgba(78, 179, 49, 0.6);
}
.handle-shopcar {
margin-top: 20rpx;
height: 50rpx;
}
.add-cart{
height: 60rpx;
line-height: 60rpx;
font-size:30rpx;
background:#81be4e;
color:#fff;
text-align:center;
border-radius:20rpx;
margin-top:10rpx;
}
}
// #ifdef MP-KUAISHOU
.viewDetail{
// border:1px solid red;
text-align: center;
background: rgba(78, 179, 49, 0.8);
border-radius: 20rpx;
height: 50rpx;
line-height: 50rpx;
color:#fff;
}
// #endif
// .good-cell-big{
// box-sizing: border-box;
// box-shadow: 0 0 4rpx rgba(#DCDFE6, 1);
// border-radius: 10rpx;
// .good-img {
// width: 300rpx;
// height: 300rpx;
// img {
// width: 100%;
// height: 100%;
// }
// }
// }

View File

@@ -0,0 +1,221 @@
<template>
<div class="good-cell-big" ref="" @click="toGoodDetail" >
<div class="good-img" v-if="skuName.img">
<img class="good-img-size" :src="skuName.img" alt="加载中..." />
</div>
<div class="good-name" v-if="skuName.name">{{skuName.name}}</div>
<div class="countTime-wrap">
<div class="countTime" v-if="skuName.actType===5 && skuName.actPrice!==0 && skuName.trendType !==0 && timeTen">{{skuName.trendType===2 ? '降价' :'升价'}}还剩 {{msg}}</div>
</div>
<div class="priceStyle" v-if="skuName.unit">
<span></span>
<!-- <span v-if="isB2B"></span> -->
<span v-if="!isB2B || isMaterial">{{skuName.unit}}</span>
<span v-else></span>
<span>约为</span>
<!-- <span class="priceColor" v-if="isMaterial || isB2B">{{$filters.toFixed2(price*100)}}</span> -->
<span class="priceColor" v-if="isMaterial">{{$filters.toFixed2(skuName.curPrice)}}</span>
<span class="priceColor" v-else-if="isB2B">{{$filters.toFixed2(skuName.price*100)}}</span>
<span class="priceColor" v-else>{{$filters.toFixed2(skuName.curPrice)}}</span>
<span>(以实际价格为准)</span>
</div>
<div class="good-price" v-if="newPrice">
<div class="price">
<div class="good-price" >
<span></span>
<span class="good-price-font" >{{newPrice }}</span>
</div>
<span class="unit" v-if="isMaterial || isB2B">/{{skuName.unit}}</span>
<!-- <span class="unit" v-if="isB2B">/{{skuName.unit}}</span> -->
<span class="unit" v-else>/</span>
<div class="ori-price" v-if="skuName.oriPrice && !isMaterial" >{{$filters.toFixed2(skuName.oriPrice)}}</div>
</div>
<div class="act-type-wrapper">
<div class="act-type" v-if="skuName.actType">{{actTypeArr[skuName.actType]}}</div>
</div>
</div>
<div class="add-cart" @click.stop="addCart" v-if="skuName.count===0 && isShowShop">加入购物车</div>
<template v-if="skuName.count!==0 && isShowShop">
<div class="handle-shopcar" @click.stop="clickStop" v-if="skuName.id !== 6031118">
<plus-minus :skuName="skuName" :count="skuName.count" type="input" @clickPlus="handlePlus" @clickMinus="handleMinus" @countChange="countChange"></plus-minus>
</div>
</template>
</div>
</template>
<script>
import { mapActions, mapGetters } from "vuex";
import { toast, modal, errToast,jumpPage } from "@/utils/uniapi.js";
import PlusMinus from "@/components/goodCmp/plus-minus/plus-minus";
import { msgData, actTypeArr } from "@/config";
// import orderPage from '@/apis/orderPage'
// import { handleCreatePay } from '@/utils/uniapi.js'
export default {
components: {
PlusMinus
},
props: {
skuName: Object,
index: {
type: Number,
}
},
data() {
return {
actTypeArr,
fromTime: null,
toTime: null,
modalShow: false,
msg: "",
num: null,
// price: null,
price: 0,
checked: false,
preCreateData: {},
highPrice: null,
lowPrice: null
// imgUrl:'https://image.jxc4.com/image/8a98a93946f9f70dd84960c06725f4fb.jpg'
};
},
computed: {
...mapGetters({
storeID:"indexPage/storeID",
defaultAddress: "addressVue/defaultAddress",
isB2B:'indexPage/isB2B',
isLogin: "login/isLogin",
isShowShop: "isShowShop",
isMaterial: "indexPage/isMaterial",
materialInfo:"indexPage/materialInfo"
}),
newPrice(){
let priceStr = ''
// let curPrice = this.isB2B ? this.skuName.curPrice : this.price * 100
let curPrice = this.isMaterial ? this.skuName.curPrice : this.isB2B ? this.skuName.curPrice : this.price * 100
// let curPrice = this.isB2B ? this.skuName.curPrice : this.price
// console.log(this.isB2B,'打印当前的价格',curPrice,'skuName',this.skuName.curPrice,'this.price',this.price)
if (curPrice !== 0) {
priceStr = ((curPrice/100).toFixed(2)).split('.')[0] + '.' + ((curPrice/100).toFixed(2)).split('.')[1]
}
return priceStr
}
},
created() {
let weight = this.skuName.weight ? this.skuName.weight : 1
this.price = ((this.skuName.curPrice / 100) / weight) * 500;
// if (this.skuName.id === 6041958 ) {
// console.log(this.skuName.curPrice / 100,'this.price',this.price,'weight')
// }
},
methods: {
// // 设置图片信息
// setImgInfo() { // val
// this.imgUrl = this.skuName.img
// },
// 加入购物车
addCart(){
// console.log('添加购物车')
if(!this.isLogin){
toast('请登录')
jumpPage('switchTab','/pages/mine/index',800)
return
}
this.handlePlus()
},
// 阻止点击事传播
clickStop(){
// console.log('阻止点击事件传播')
},
//守家
waitPriceDown(x) {
this.modalShow = true;
console.log(x);
},
close() {
this.modalShow = false;
},
async handlePlus() {
// #ifdef MP-WEIXIN
// if (this.isMaterial && this.skuName.id === 6039382 && this.materialInfo && this.materialInfo.fromStoreInfo && this.materialInfo.fromStoreInfo.packageSwitch === 1) {
// toast('禁止购买物料袋,请联系运营')
// return
// }
// #endif
this.$emit("handleSkuNamePlus", this.index);
},
async handleMinus() {
if (this.skuName.count === 1) {
let confirm = await modal("温馨提示", msgData.delGoods);
if (!confirm) return
}
this.$emit("handleSkuNameMinus", this.index);
},
async countChange(count) {
try {
// await this.changeCar({
// skuName: this.skuName,
// count,
// });
this.$emit("handleSkuNameChange", {
index: this.index,
count,
});
} catch (e) {
console.error(e);
errToast(e);
}
},
// 跳转到商品详情页
toGoodDetail() {
if (this.shopCar) return false;
jumpPage('navigateTo',`/pagesGoods/good-detail/index?skuID=${this.skuName.id}&storeID=${this.skuName.storeID}`)
},
},
};
</script>
<style lang="scss">
@use "./good-cell-big.scss";
.red {
flex: 1;
color: #fe6263;
margin: 10rpx 0;
font-size: 28rpx;
}
.gray {
flex: 1;
color: #999;
margin: 10rpx 0;
font-size: 28rpx;
}
.priceStyle{
color: rgb(149, 209,131);
font-size: 25rpx;
margin-bottom:10rpx;
}
.priceColor{
color:#fe6263;
}
.good-price {
font-size: 32rpx;
color: #fe6263;
font-weight: 500;
// &:before {
// content: "¥";
// font-size: 28rpx;
// }
&-font{
font-size: 38rpx;
&:after{
// content: ".";
font-size: 28rpx;
padding:0 5rpx;
}
}
}
</style>

View File

@@ -0,0 +1,129 @@
@use '@/assets/bundle.scss';
.good-cell {
// @extend %pd20;
padding: 30rpx 0;
display: flex;
// border-bottom: 1rpx solid #F2F6FC;
border-bottom: 1rpx solid #dadada;
.good-img {
flex: none;
width: 150rpx;
height: 150rpx;
.good-img-size{
width: 100%;
height: 100%;
}
// img {
// width: 100%;
// height: 100%;
// }
}
.good-info {
flex: auto;
margin-left: 20rpx;
}
.lookDetail{
height: 60rpx;
line-height: 60rpx;
font-size:30rpx;
background:#81be4e;
color:#fff;
text-align:center;
border-radius:20rpx;
margin-top:10rpx;
}
.good-name {
font-size: 30rpx;
font-weight: 400;
color: #333;
min-height: 80rpx;
line-height: 1.1;
}
.btn-wait{
flex: 1;
}
.btn-self{
color: #fff;
background-color: rgba(78, 179, 49, 0.6);
}
.countTime-wrap{
height: 17px;
width: 100%;
margin-bottom: 5rpx;
}
.countTime{
color: #E6A23C;
font-size: 26rpx;
}
.stock-and-waitPirce{
display: flex;
}
.act-type-wrapper {
height: 20rpx;
overflow: hidden;
}
.act-type {
@extend %act-type-text;
}
.good-price {
@extend %flex-between;
flex-wrap: wrap;
}
.price {
display: flex;
align-items: baseline;
}
.cur-price {
font-size: 32rpx;
color: #fe6263;
font-weight: 700;;
&:before {
content: "";
font-size: 24rpx;
}
}
.ori-price {
font-size: 20rpx;
color: #b2b2b2;
margin-left: 10rpx;
text-decoration: line-through;
&:before {
content: "";
}
}
.handle-shopcar {
flex: none;
// background: red;
margin-left: auto;
height: 60rpx;
@extend %flex-center;
}
.cart-act-count {
font-size: 24rpx;
color: #606266;
display: flex;
flex-flow: column;
align-items: flex-end;
line-height: 1;
.act-count-show {
width: 220rpx;
@extend %flex-between;
}
}
.add-cart{
width: 200rpx;
height: 60rpx;
line-height: 60rpx;
font-size:30rpx;
background:#81be4e;
color:#fff;
text-align:center;
border-radius:20rpx;
// border:1rpx solid red;
}
}
.no-border{
border-bottom: 1rpx solid #fff !important;
}

View File

@@ -0,0 +1,212 @@
<template>
<div @click="toGoodDetail" :class="{
'good-cell': true,
'no-border': noBorder
}">
<div class="good-img" v-if="skuName && skuName.img">
<img :src="$filters.urlToHttps(skuName.img)" alt="加载中..." class="good-img-size"/>
</div>
<div class="good-info">
<!-- style="width: 412rpx;" 193px-->
<!-- width:fit-content -->
<div class="good-name" :style="{'width':titleWidth}">{{skuName.name}}</div>
<div class="countTime-wrap" v-if="skuName.actType===5 &&skuName.actPrice!==0 &&skuName.trendType !==0 && timeTen && waitShow">
<div class="countTime" >{{skuName.trendType===2 ? '降价' :'升价'}}还剩 {{msg}}</div>
</div>
<div class="act-type-wrapper">
<div class="act-type" v-if="skuName.actType">{{actTypeArr[skuName.actType]}}</div>
</div>
<!-- v-if="isShowShop" -->
<div class="good-price">
<div class="price">
<good-price :price="skuName.curPrice"></good-price>
<div class="ori-price" v-if="skuName && skuName.oriPrice && !isMaterial">{{$filters.toFixed2(skuName.oriPrice)}}</div>
</div>
<div class="add-cart" @click.stop="addCart" v-if="skuName.count===0 && isShowShop">加入购物车</div>
<!-- #ifdef MP-TOUTIAO || APP-PLUS -->
<!-- @click.stop="addCart" v-if="skuName.count===0" -->
<div class="add-cart" v-if="!isShowShop">查看详情</div>
<!-- #endif -->
<!-- v-if="skuName.count!==0 && isShowShop" -->
<template v-if="skuName.count!==0 && isShowShop">
<div class="handle-shopcar" @click.stop="clickStop" v-if="skuName.id !== 6031118" >
<plus-minus type="input" :skuName="skuName" :count="skuName.count" @clickPlus="handlePlus" @clickMinus="handleMinus" @countChange="countChange"></plus-minus>
</div>
</template>
<!-- <div v-if="!isShowShop" class="lookDetail" style="border:1rpx solid red">查看详情</div> -->
</div>
<!-- 购物车活动商品普通商品分层 -->
<div class="cart-act-count" v-if="type === 'cart' && skuName.actType && skuName.count > 1">
<div class="act-count-show">
<span>{{$filters.toFixed2(skuName.curPrice)}}</span>
<span>x1</span>
</div>
<div class="act-count-show">
<span>{{$filters.toFixed2(skuName.oriPrice)}}</span>
<span>x{{skuName.count - 1}}</span>
</div>
</div>
</div>
</div>
</template>
<script>
import { mapActions, mapGetters } from "vuex";
import { toast, modal, errToast,jumpPage } from "@/utils/uniapi.js";
import PlusMinus from "@/components/goodCmp/plus-minus/plus-minus";
import { actTypeArr } from "@/config";
import GoodPrice from '@/components/goodCmp/good-price/good-price'
import orderPage from '@/apis/orderPage'
import { handleCreatePay } from '@/utils/uniapi.js'
export default {
name: "goodCell",
inject: {
shopCar: {
default: false,
},
},
components: {
PlusMinus,
GoodPrice
},
props: {
skuName: {
type: Object,
// default() {
// return {};
// },
},
index: {
type: Number,
},
noBorder: {
type: Boolean,
default: false,
},
type: {
type: String,
default: "",
},
waitShow: {
type: Boolean,
default: true,
},
titleWidth: {
type: String,
default:'390rpx'
}
},
computed: {
...mapGetters({
countTime: "countTime",
storeID:"indexPage/storeID",
defaultAddress: "addressVue/defaultAddress",
isLogin:"login/isLogin",
isShowShop: "isShowShop",
isMaterial: "indexPage/isMaterial"
}),
},
data() {
return {
actTypeArr,
msg: "",
modalShow: false,
fromTime: null,
toTime: null,
num: null,
price: null,
beforeCreateData: {},
timer: null,
highPrice: null,
lowPrice: null,
timeTen: true,
imgUrl:'https://image.jxc4.com/image/8a98a93946f9f70dd84960c06725f4fb.jpg'
};
},
beforeDestroy() {
clearInterval(this.timer);
},
methods: {
// 加入购物车
addCart() {
if (!this.isLogin) {
errToast('请登录')
jumpPage('switchTab', '/pages/mine/index', 800)
return
}
this.handlePlus()
},
clickStop(){
// console.log('阻止点击事件传递')
},
waitPriceDown(x) {
this.modalShow = true;
console.log(x);
},
close() {
this.modalShow = false;
},
//倒计时
countDown(time1) {
if (this.toTime > new Date()) {
let res = ((new Date(time1) - new Date()) / (1000 * 60)) % 10;
let minutes = Math.floor(res);
let seconds = Math.floor((res * 60) % 60);
//console.log(seconds.toString().length);
if (seconds.toString().length < 2) {
seconds = "0" + seconds;
}
this.msg = "0" + minutes + "分 : " + seconds + "秒";
}
},
//销量
stock() {
let num = this.skuName.stock;
if (num >= 1000) {
return "库存:1000+";
} else if (num < 1000 && num > 0) {
return "库存:" + num;
} else if (num === 0) {
return "卖完了";
}
},
async handlePlus() {
this.$emit("handleSkuNamePlus", this.index)
},
async handleMinus() {
this.$emit("handleSkuNameMinus", this.index)
},
async countChange(count) {
this.$emit("handleSkuNameChange", {
index: this.index,
count,
});
},
// 跳转到商品详情页
toGoodDetail() {
jumpPage('navigateTo',`/pagesGoods/good-detail/index?skuID=${this.skuName.id}&storeID=${this.skuName.storeID}`)
},
},
};
</script>
<style lang="scss">
@use "./good-cell.scss";
.red {
flex: 1;
color: #fe6263;
margin: 10rpx 0;
}
.gray {
flex: 1;
color: #999;
margin: 10rpx 0;
}
</style>

View File

@@ -0,0 +1,20 @@
@use '@/assets/bundle.scss';
.good-price {
font-size: 32rpx;
color: #fe6263;
font-weight: 500;
// &:before {
// content: "¥";
// font-size: 28rpx;
// }
&-font{
font-size: 38rpx;
&:after{
// content: ".";
font-size: 28rpx;
padding:0 5rpx;
}
}
}

View File

@@ -0,0 +1,31 @@
<template>
<div class="good-price">
<span></span>
<span class="good-price-font" >{{newPrice }}</span>
</div>
</template>
<script>
export default {
// props: {
// price:{
// type:Number,
// default:0
// }
// },
props:['price'],
computed: {
newPrice(){
let str = ''
if(this.price!==0){
str = ((this.price/100).toFixed(2)).split('.')[0] + '.' + ((this.price/100).toFixed(2)).split('.')[1]
}
return str
}
}
}
</script>
<style lang="scss">
@use './good-price.scss';
</style>

View File

@@ -0,0 +1,33 @@
@use '@/assets/bundle.scss';
.left-cats {
// height: calc(100vh - 90rpx);
/* #ifndef H5 */
height: calc(100vh - 90rpx);
/* #endif */
/* #ifdef H5 */
height: calc(100vh - 270rpx);
/* #endif */
.cats-item {
height: 110rpx;
font-size: 28rpx;
color: #666;
@extend %flex-center;
box-sizing: border-box;
}
.cat-name {
border-left: 6rpx solid transparent;
flex: 1;
height: 60rpx;
@extend %flex-center;
}
.active {
background: white;
color: #4EB331;
.cat-name {
border-left-color: #4EB331;
}
}
}

View File

@@ -0,0 +1,52 @@
<template>
<scroll-view class="left-cats" scroll-y :scroll-into-view="intoID" scroll-with-animation>
<div
:class="{
'cats-item': true,
active: cat.id === current
}"
v-for="(cat, index) in catL1"
:key="cat.id"
:id="'l' + cat.id"
@click="handleClick(cat.id, index)"
>
<text class="cat-name">{{cat.name}}</text>
</div>
</scroll-view>
</template>
<script>
export default {
name: 'LeftCats',
props: {
catL1: {
default: []
},
current: {
default: null
}
},
data () {
return {
intoID: ''
}
},
methods: {
handleClick (id, index) {
this.$emit('catChange', {
id,
index
})
// this.intoID = 'l' + id
},
scrollInToID (id) {
this.intoID = 'l' + id
}
}
}
</script>
<style lang="scss">
@use './left-cats.scss';
</style>

View File

@@ -0,0 +1,70 @@
@use '@/assets/bundle.scss';
.cmp-minus-plus, .cmp-minus-plus2 {
@extend %flex-center;
.minus-plus-wrapper {
@extend %flex-center;
}
.btn-minus, .btn-plus {
flex: none;
@extend %bg-no-repeat-center;
@extend %bgwhite;
background-size: 24rpx;
width: 60rpx;
height: 60rpx;
border-radius: 50%;
border: 1rpx solid #DCDFE6;
position: relative;
z-index: 1;
padding: 0;
box-sizing: content-box;
}
.btn-minus {
@extend %icon-minus;
}
.btn-plus {
@extend %icon-plus;
}
.count {
flex: none;
color: #333;
font-size: 34rpx;
width: 70rpx;
text-align: center;
margin: 0 10rpx;
color: #606266;
}
.input-count {
// background :red;
color: #606266;
font-size: 34rpx;
}
// .type-shopCar {
// background: #DCDFE6;
// border-radius: 10rpx;
// margin: 0 10rpx;
// }
.cannot-buy {
color: #909399;
font-size: 28rpx;
}
}
.cmp-minus-plus2 {
.disabled {
opacity: .2;
}
}
.add-cart{
// padding:0 20rpx;
// width: calc(100%-125px);
width: 100vw;
height: 64rpx;
line-height: 64rpx;
font-size:30rpx;
background:#81be4e;
color:#fff;
text-align:center;
border-radius:20rpx;
}

View File

@@ -0,0 +1,85 @@
<template>
<div class="cmp-minus-plus">
<div class="add-cart" v-if="!count" @click.stop="handlePlus">加入购物车</div>
<div class="minus-plus-wrapper" v-else>
<div class="btn-minus" @click.stop="handleMinus"></div>
<div :class="{count: true, 'type-shopCar': typeShopCar}">
<span v-if="type !== 'input'">{{count}}</span>
<input v-if="type === 'input'" type="number" class="input-count" @blur="countChange" v-model.number="inputCount">
</div>
<div class="btn-plus" @click.stop="handlePlus"></div>
</div>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
import { errToast,jumpPage } from '@/utils/uniapi.js'
import { msgData } from '@/config.js'
import {debounce} from '@/utils'
export default {
inject: {
shopCar: {
default: ''
}
},
props:['count','type','skuName'],
data() {
return {
typeShopCar: false,
inputCount: this.count
}
},
computed: {
...mapGetters({
isLogin: 'login/isLogin',
storeMap: "indexPage/storeMap",
storeID: "indexPage/storeID",
isMaterial: "indexPage/isMaterial",
})
},
mounted() {
this.typeShopCar = this.shopCar
this.inputCount = this.count
},
watch: {
count(count) {
this.inputCount = +count
}
},
methods: {
// minus
handleMinus:debounce(function() {
if (!this.isMaterial && this.storeMap[this.storeID[0]].status !== 1) return errToast(msgData.storeIsClose) // 判断当前门店是否营业
this.$emit('clickMinus')
},1000),
// plus
handlePlus:debounce(function() {
if (!this.isMaterial && this.storeMap[this.storeID[0]].status !== 1) return errToast(msgData.storeIsClose) // 判断当前门店是否营业
// 判断用的登录状态
if (!this.isLogin) {
errToast('请登录')
jumpPage('switchTab', '/pages/mine/index', 800)
return
}
this.$emit('clickPlus')
},1000),
// count改变
countChange:debounce(function(e) {
if(+e.target.value > this.skuName.stock) {
this.inputCount = this.skuName.count
return errToast('暂时没有那么多商品喔~')
}
const count = +e.target.value
if (!count) this.inputCount = 0
if (count !== this.count) this.$emit('countChange', count ? count : 0)
},1000)
}
}
</script>
<style lang="scss">
@use "./plus-minus.scss";
</style>

View File

@@ -0,0 +1,59 @@
@use '@/assets/bundle.scss';
// 顶部二级菜单
.top-cats {
display: flex;
align-items: center;
position: relative;
// 遮罩
&:after {
content: "";
display: block;
position: absolute;
background: linear-gradient(to left, white, rgba(white, 0));
right: 15%;
width: 80rpx;
height: 100%;
}
.scroll-x {
// white-space: nowrap;
width: 85%;
// background: red;
.scroll-area {
display: flex;
align-items: center;
}
.scroll-item {
// display: inline-block;
flex: none;
font-size: 28rpx;
padding: 20rpx;
}
.scroll-item-placeholder {
flex: none;
width: 80rpx;
height: 50rpx;
}
.cat-active {
color: #4EB331;
border: 1rpx solid #4EB331;
padding: 4rpx 10rpx;
background: rgb(234, 240, 227);
border-radius: 10rpx;
}
}
.btn-up-down {
width: 15%;
height: 79rpx;
// @include svg_icon(arraw-up, 9a9a9a);
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAANRJREFUWEft1MENwjAMBVB7AlZhhLJJOcRnRuGcHOgmdARWYQKjSI0UFUKSn0Mvzr35L1+1mQ4+fHA+GcAasAasgaEGvPdzXGQisqALDQZs4Y8t+IoiIEAKV9V3BDDziYggRDcgD2fmKQJUdUURXYB9uIi8IsB7f0YRzYBSePr5UEQToBY+gqgCWsNRxF9AbziCKALQ8F7ET8BoeA/iCxBCmFT1GZdMnPM0auiqzaeDmS/OuTW/q9TAnYiW0fC8CSKaReS2f0h1CtCXt35nAGvAGrAGPjJjsyGUY8cAAAAAAElFTkSuQmCC");
background-size: 20rpx;
@extend %bg-no-repeat-center;
transform: rotateX(180deg);
transition: all .5s;
}
.arrow-active {
transform: rotateX(0);
}
}

View File

@@ -0,0 +1,77 @@
<template>
<div class="top-cats">
<scroll-view class="scroll-x" :scroll-left="scrollLeft" scroll-x="true" :scroll-into-view="intoID" @scroll="scroll">
<div class="scroll-area">
<div
:class="{
'scroll-item': true,
'cat-active': current === cat.id
}"
v-for="(cat, index) in cats"
:key="cat.id"
:id="'c' + cat.id"
@click="clickCat(cat.id, index)"
>
{{cat.name}}
</div>
<div class="scroll-item-placeholder"></div>
</div>
</scroll-view>
<div :class="{'btn-up-down': true, 'arrow-active': up}" @click="clickArrow"></div>
</div>
</template>
<script>
import {debounce} from '@/utils'
export default {
name: 'TopCats',
props: ['cats', 'current', 'wallShow', 'up'],
data () {
return {
intoID: 'c0',
scrollLeft: 0,
oldScroll: 0
}
},
methods: {
// 点击箭头
clickArrow () {
this.$emit('toggleCat2')
},
// 点击分类
clickCat (id, index) {
this.$emit('cat2Change', {
id,
index
})
},
scrollInToIndex (index) {
this.intoID = 'c' + this.cats[index - 1 >= 0 ? index - 1 : 0].id
},
// 设置位置
// setScroll (id) {
// let index = this.cats.findIndex(item => item.id === id)
// this.intoID = 'c' + this.cats[index - 1 >= 0 ? index - 1 : 0].id
// },
// 滚动防抖
scroll: debounce(function (e) {
this.oldScroll = e.detail.scrollLeft
}, 50)
},
watch: {
cats (to, from) {
if (from[1] && to[1] && to[1].id !== from[1].id) {
// 回到原点
this.scrollLeft = this.oldScroll
this.$nextTick(() => {
this.scrollLeft = 0
})
}
}
}
}
</script>
<style lang="scss">
@use "./top-cats.scss";
</style>

View File

@@ -0,0 +1,23 @@
@use '@/assets/bundle.scss';
.top-cats-wall {
background: white;
font-size: 28rpx;
display: flex;
flex-flow: row wrap;
padding: 20rpx 0 40rpx;
.wall-item {
width: 30%;
// margin-left: (10% / 5);
margin-top: 4%;
box-sizing: border-box;
border: 2rpx solid #ddd;
height: 50rpx;
@extend %flex-center;
border-radius: 30rpx;
}
.active {
border-color: #4EB331;
background: rgba(#4EB331, .2);
}
}

View File

@@ -0,0 +1,35 @@
<template>
<div class="top-cats-wall">
<div
:class="{
'wall-item': true,
'active': current === cat.id
}"
v-for="(cat, index) in cats"
:key="cat.id"
@click="handleClick(cat.id, index)"
>
{{cat.name}}
</div>
</div>
</template>
<script>
export default {
name: 'TopCatsWall',
props: ['cats', 'current'],
methods: {
// 点击分类
handleClick (id, index) {
this.$emit('clickCat2', {
id,
index
})
}
}
}
</script>
<style lang="scss">
@use './top-cats-wall.scss';
</style>

View File

@@ -0,0 +1,65 @@
<template>
<view class="empty-root">
<image
class="img"
:src="src"
mode="scaleToFill"
/>
<text class="text">{{ title }}</text>
<slot></slot>
</view>
</template>
<script>
export default {
props:{
icon: {
type:String,
default:'kongzhuangtai',
},
size:{
type:Number,
default:150
},
title:{
type:String,
// default:'暂无数据'
default:'暂无门店列表'
},
color:{
type:String,
default:'#7fc86c'
},
src:{
type:String,
default:'https://image.jxc4.com/image/d0adecd3671e8a1c2294783b22a46426.png'
}
}
}
</script>
<style lang="scss" scoped>
.empty-root {
position: absolute;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
z-index: 999999;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
.img {
width: 400rpx;
height: 400rpx;
}
.text {
color: v-bind(color);
text-decoration: none;
margin-top: -70rpx;
text-align: center;
}
}
</style>

View File

@@ -0,0 +1,32 @@
@use '@/assets/bundle.scss';
.left-right-cell {
// width: 100vw;
@extend %flex-between;
@extend %pdlr;
min-height: 100rpx;
box-sizing: border-box;
font-size: 32rpx;
@extend %bgwhite;
.left, .right {
color: #333;
}
.left {
flex: none;
margin-right: 20rpx;
}
.right {
@extend %flex-right;
flex: auto;
@extend %oneline;
.icon-right-aw {
flex: none;
@extend %icon-aw-right-slim;
width: 50rpx;
height: 30rpx;
background-position: right center;
background-repeat: no-repeat;
background-size: 30rpx;
}
}
}

View File

@@ -0,0 +1,26 @@
<template>
<view class="left-right-cell">
<view class="left">
<slot name="left"></slot>
</view>
<view class="right">
<slot name="right"></slot>
<view v-if="!noClick" class="icon-right-aw"></view>
</view>
</view>
</template>
<script>
export default {
props: {
noClick: {
type: Boolean,
default: false
}
}
}
</script>
<style lang="scss">
@use './left-right-cell.scss';
</style>

View File

@@ -0,0 +1,23 @@
<template>
<div class="material-info">
<div>单量: {{preSaleCount}}已选袋子件数/数量: {{totalBag.unitCount}}/{{totalBag.totalCount}}</div>
<div :class="{danger: bagOverload !== '未超出购买限制'}">{{bagOverload}}</div>
</div>
</template>
<script>
import {mapGetters} from 'vuex'
export default {
computed: {
...mapGetters({
preSaleCount: 'indexPage/preSaleCount',
totalBag: 'indexPage/totalBag',
bagOverload: 'indexPage/bagOverload',
})
}
}
</script>
<style lang="scss">
@use "./material-info.scss"
</style>

View File

@@ -0,0 +1,11 @@
@use '@/assets/bundle.scss';
.material-info {
font-size: 30rpx;
color: #606266;
text-align: center;
.danger {
color: #F56C6C;
font-weight: 500;
}
}

View File

@@ -0,0 +1,188 @@
@use '@/assets/bundle.scss';
.nearlyStores{
margin-top:20rpx;
background:#fff;
border-radius:10rpx;
padding:10rpx;
// border: 1rpx solid red;
box-shadow: 0rpx 2rpx 10rpx rgb(207, 207, 207);
}
.storeInfo{
display:flex;
font-size:30rpx;
position:relative;
}
.storeImg{
width:120rpx;
height:120rpx;
}
.storeStatus{
position:absolute;
// top:80rpx;
bottom: 0;
left:0;
width:120rpx;
text-align:center;
background:rgba(0,0,0,0.5);
color:#fff;
}
.storeRight{
margin-left:20rpx;
}
.storeName{
display:flex;
font-weight:bold;
margin-bottom:4rpx;
}
.storeTime,.storeTime1,.storeTime2{
font-size: 24rpx;
}
.storeTime2{
margin-left:20rpx;
}
.storeRate{
display:flex !important;
font-size: 24rpx;
margin-top:6rpx
}
.fullReduce{
display:flex;
margin-bottom:20rpx;
justify-content:space-between;
font-size: 24rpx;
width:566rpx;
margin-top:6rpx;
&Fee{
border:1rpx solid #f16a6a;
text-align:center;
border-radius:10rpx;
padding:6rpx;
// background: #fff;
color: #f16a6a;
}
}
.storeGood{
// border: 1rpx solid red;
display:flex;
// margin-left:142rpx;
margin-left:20rpx;
overflow:hidden;
overflow-x:auto;
width:98%;
white-space:nowrap;
&Item:not(:last-child){
margin-right:20rpx;
// width: 100%;
}
// .storeGood:not(:last-child){
// // margin-left: 0;
// border: 1rpx solid red;
// }
&Img{
width:146rpx;
height:146rpx;
}
&Name{
width:120rpx;
white-space:nowrap;
overflow:hidden;
text-overflow:ellipsis;
font-size:24rpx;
}
&Price{
color:#ff0000;
font-size:24rpx;
}
&Price::before{
content: '';
font-size:20rpx;
}
&More{
// border: 1rpx solid green;
// position: relative;
// right: 0;
position:sticky;
right:0;
writing-mode:vertical-lr;
text-align:center;
background:rgba(132, 132, 132, 0.5);
color:#fff;
padding:0 12rpx;
}
}
.top-title_near ,.top-title{
background: #fff;
padding: 20rpx 0;
border-radius: 10rpx;
line-height: 1;
box-shadow: 5rpx 5rpx 4rpx rgba(194, 193, 192, 0.2);
}
.top-title{
@extend %flex-left;
}
.top-title_near{
display: flex;
justify-content: space-between !important;
}
.icon-border {
width: 6rpx;
background-color: #4EB331;
height: 30rpx;
margin-right: 10rpx;
}
.title {
font-weight: bold;
color: #333;
font-size: 36rpx;
}
.ranking_left,.ranking_right{
display:flex;
border:0
}
.operationRaking,.optimalScore,.nearlyDistance{
padding:4px 10px;
font-size:28rpx;
}
.active_status{
border-bottom:2rpx solid #4EB331;
color:#4EB331;
font-weight: bold;
}
.top-goods-list {
display: flex;
box-sizing: border-box;
flex-flow: row wrap;
justify-content: space-between;
.goods-item {
margin-top: 20rpx;
width: 345rpx;
flex: none ;
}
}

View File

@@ -0,0 +1,198 @@
<template>
<div class="top-title_near">
<div class="ranking_left">
<slot>
<div class="icon-border"></div>
<div class="title">附近商家</div>
</slot>
</div>
<div class="ranking_right">
<div class="operationRaking" :class="activeID === 1 ?'active_status' :''" @click="optionSort(1)">综合排序</div>
<div class="optimalScore" :class="activeID === 2 ?'active_status' :''" @click="optionSort(2)">评分最优</div>
<div class="nearlyDistance" :class="activeID === 3 ?'active_status' :''" @click="optionSort(3)">距离最近</div>
</div>
</div>
<div v-show="nearStatus === 'noMore'">
<div
class="nearlyStores"
v-for="(item, index) in nearlyStores"
:key="index"
@click="clickMore(item)"
>
<!-- 商家信息 -->
<div class="storeInfo">
<div style="display: flex;align-items: center;">
<image :src="item.storeInfo.brandLogo" class="storeImg" alt="加载中1111111" />
</div>
<div v-if="item.storeInfo.status===-1" class="storeStatus">休息中</div>
<div class="storeRight">
<div class="storeName" style="display: flex;">
<div>{{item.storeInfo.storeName }}</div>
<div v-if="item.storeInfo.brandName">--</div>
<div>{{ item.storeInfo.brandName }}</div>
</div>
<!-- 营业时间 -->
<div style="text-align: left;display: flex;">
<div class="storeTime">营业时间</div>
<div class="storeTime1" v-if="item.storeInfo.openTime1 && item.storeInfo.closeTime1">{{$filters.processingHours(item.storeInfo.openTime1)}}-{{$filters.processingHours(item.storeInfo.closeTime1)}}</div>
<div class="storeTime2" v-if="item.storeInfo.openTime2 && item.storeInfo.closeTime2">{{$filters.processingHours(item.storeInfo.openTime2)}}-{{$filters.processingHours(item.storeInfo.closeTime2)}}</div>
</div>
<!-- 评分 -->
<div class="storeRate">
<div>门店评分</div>
<div v-if="item.storeInfo && item.storeInfo.storeWeeklyScore">
<uni-rate :size="20" allow-half :value="(item.storeInfo.storeWeeklyScore *5) /100" />
</div>
<div v-else>暂无评分</div>
</div>
<!-- 满减 -->
<div class="fullReduce">
<div v-if="item.StoreDeductionInfo[0].deliveryFeeDeductionFee && item.StoreDeductionInfo[0].deliveryFeeDeductionSill" class="fullReduceFee">
{{item.StoreDeductionInfo[0].deliveryFeeDeductionSill/100}}{{item.StoreDeductionInfo[0].deliveryFeeDeductionFee/100}}
</div>
<!-- 距离 -->
<div v-if="String(item.storeInfo.distance).length<=3">距您{{item.storeInfo.distance}}m</div>
<div v-else>距您{{(item.storeInfo.distance/1000).toFixed(2)}}km</div>
</div>
</div>
</div>
<!-- 商品 -->
<div class="storeGood" v-if="item.StoreSkuInfo!==null && !isSearch" @click.stop="clickStop">
<div v-for="(ite,i) in item.StoreSkuInfo" :key="i" class="storeGoodItem" @click="toDetail(ite)">
<image :src="$filters.urlToHttps(ite.img)" class="storeGoodImg" alt="" />
<div class="storeGoodName" >{{ite.skuName}}</div>
<div class="storeGoodPrice" >{{(ite.price/100).toFixed(2)}}/{{ite.unit}}</div>
</div>
<div class="storeGoodMore" @click="clickMore(item)">查看更多</div>
</div>
<!-- 搜索商品 -->
<div class="storeGood" v-if="isSearch" @click.stop="clickStop">
<div v-for="(ite,i) in item.searchSku" :key="i" class="storeGoodItem" @click="toDetail(ite)">
<image :src="$filters.urlToHttps(ite.img)" class="storeGoodImg" alt="" />
<div class="storeGoodName" >{{ite.skuName}}</div>
<!-- <div class="storeGoodPrice" >{{(ite.price/100).toFixed(2)}}/{{ite.unit}}</div> -->
<div class="storeGoodPrice" >{{(ite.curPrice/100).toFixed(2)}}/{{ite.unit}}</div>
</div>
<div class="storeGoodMore" @click="clickMore(item)">查看更多</div>
</div>
<!-- <div class="enterStore" v-else >
<div style="padding:20rpx;background:rgba(78, 179, 49, 0.9);color:#fff;border-radius: 10rpx;border:1rpx solid red;"> </div>
</div> -->
</div>
</div>
<!-- 加载更多 -->
<div style="background:#fff;margin-top:20rpx" v-if="!isSearch">
<uni-load-more :status="nearStatus" ></uni-load-more>
</div>
</template>
<script>
// import apiStore from '@/apis/apiStore';
import { mapGetters, mapActions } from "vuex";
import {debounce} from '@/utils'
import {store} from "@/store";
import { jumpPage } from "@/utils/uniapi.js";
export default {
props:{
storeList:{
type:Array,
default:() => []
},
isSearch:{
type:Boolean,
default:false
}
},
data(){
return {
// nearlyStores:[],
nearStatus:'loading',
originalNearlyStores:[],
activeID: 1,
}
},
// watch:{
// storeList:{
// handleError(val){
// console.log('打印val的值',val)
// }
// }
// },
computed:{
...mapGetters({
myLocation:"indexPage/myLocation",
reLocation: "indexPage/reLocation",
storeID: "indexPage/storeID",
isLogin: "login/isLogin",
}),
nearlyStores(){
if(this.storeList && this.storeList.length>0) {
this.nearStatus = 'loading'
this.originalNearlyStores = [...this.storeList] // 原始数据
this.activeID = this.activeID !==1 ? this.activeID : 1 // 获取推荐商家时,为综合排序
this.nearStatus = 'noMore'
}
return this.storeList
}
},
methods:{
// 查看更多
async clickMore(item) {
if(item.dnsType) store.commit('indexPage/changeDNS',item.dnsType) // 更换域名
else store.commit('indexPage/changeDNS','wxC4') // 更换域名
// console.log('item.storeInfo.storeIDn,,,99999999',item.storeInfo.storeID)
store.commit("indexPage/changeStoreID", [+item.storeInfo.storeID])
// if(this.isLogin ) await store.dispatch('cartPage/getCarList', store.getters['indexPage/storeID'])
if(this.isLogin ) {
await store.dispatch('cartPage/getCarList', this.storeID) // 获取购物车列表
await store.dispatch('addressVue/getList') // 加载地址列表
}
if(!(this.storeMap && this.storeMap[this.storeID[0]])) await store.dispatch('indexPage/getStoreInfo', { storeID: this.storeID[0] }) // 获取当前门店的信息
jumpPage('navigateTo','/pagesGoods/good/index')
},
// 跳转到详情页
toDetail(ite) {
// if(ite.storeID !== this.storeID[0])
store.commit("indexPage/changeStoreID", [+ite.storeID])
jumpPage('navigateTo', `/pagesGoods/good-detail/index?skuID=${ite.skuID}&storeID=${ite.storeID}`)
},
clickStop(){
// console.log('阻止默认事件')
},
optionSort: debounce(function (e) {
this.activeID = e
this.nearStatus = 'loading'
switch (e) {
case 1:
this.nearlyStores = [...this.originalNearlyStores]
break
case 2:
this.nearlyStores.sort(this.ArraySort('storeWeeklyScore','L2S'))
break
case 3:
this.nearlyStores.sort(this.ArraySort('distance', 'S2L'))
break
}
this.nearStatus = 'noMore'
},500),
// 数组排序 type L2S 大 ——> 小 S2L 小 ——> 大
ArraySort(property,type){
return function (a,b){
let value1=a.storeInfo[property]
let value2 = b.storeInfo[property]
return type === 'L2S'? value2 - value1 : value1 - value2
}
},
}
}
</script>
<style lang="scss" scoped>
@use './index.scss';
</style>

View File

@@ -0,0 +1,59 @@
@use '@/assets/bundle.scss';
.btn-toShopCar-new {
// position: fixed;
// left: 40rpx;
// bottom: 100rpx;
background-color: rgba(black, .6);
width: 120rpx;
height: 120rpx;
border-radius: 50%;
z-index: 10;
@extend %flex-center;
}
.icon-shopcar {
@extend %icon-shopcar-white;
@extend %bg-no-repeat-center;
width: 100rpx;
height: 100rpx;
background-size: 50rpx;
border-radius: 50%;
}
.fixed-box {
pointer-events: none; // 这里是重点,盒子可穿透操作
width: 100vw;
height: 90vh;
position: fixed;
top: 100rpx;
left: 0;
z-index: 100000;
}
.fixed-button {
opacity: 1;
// background: #ccc;
border-radius: 100%;
pointer-events: auto;
// width: max-content;
// height: auto;
width: 120rpx;
height: 120rpx;
overflow: hidden;
z-index: 9999;
display: flex;
align-items: center;
justify-content: center;
left: 20rpx;
top: 85vh;
.imger{
width: 52px;
height: 52px;
overflow: none;
}
}
/* 适配iphonex 有底部横条的 */
@supports (bottom: constant(safe-area-inset-bottom)) or (bottom: env(safe-area-inset-bottom)) {
.fixed-box {
bottom: constant(safe-area-inset-bottom);
bottom: env(safe-area-inset-bottom);
}
}

View File

@@ -0,0 +1,39 @@
<template>
<movable-area class="fixed-box">
<movable-view @click="switchToShopCar" class="fixed-button" direction="all" :inertia="true">
<div class="btn-toShopCar-new" >
<badge :count="shopCount">
<div class="icon-shopcar"></div>
</badge>
</div>
</movable-view>
</movable-area>
</template>
<script>
import {mapGetters, mapActions} from 'vuex'
import { jumpPage,errToast } from "@/utils/uniapi.js";
export default {
computed: {
...mapGetters({
shopCount: 'cartPage/carCount',
isLogin: "login/isLogin",
})
},
methods: {
switchToShopCar() {
// #ifndef MP-TOUTIAO || APP-PLUS
jumpPage('switchTab','/pages/cart/index')
// #endif
// #ifdef MP-TOUTIAO || APP-PLUS
jumpPage('navigateTo','/pages/cart/index')
// #endif
}
}
}
</script>
<style lang="scss">
@use "./index.scss";
</style>

View File

@@ -0,0 +1,121 @@
@use "@/assets/bundle.scss";
.waterfalls-flow {
overflow: hidden;
padding: 10rpx;
/* #ifdef MP-ALIPAY */
background-color: #f4f4f4;
/* #endif */
&-column {
float: left;
}
}
.column-value {
width: 100%;
font-size: 0;
overflow: hidden;
transition: opacity .4s;
opacity: 0;
border:1rpx solid #e6e1e1;
// box-shadow: 1rpx 1rpx 1rpx 1rpx #8c8c8c;
padding: 20rpx;
box-sizing: border-box;
&-show {
opacity: 1;
}
.inner {
font-size: 30rpx;
}
.img {
width: 100%;
&-hide {
display: none;
}
&-error {
background: #f2f2f2 url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAC4AAAAiAQMAAAAatXkPAAAABlBMVEUAAADMzMzIT8AyAAAAAXRSTlMAQObYZgAAAIZJREFUCNdlzjEKwkAUBNAfEGyCuYBkLyLuxRYW2SKlV1JSeA2tUiZg4YrLjv9PGsHqNTPMSAQuyAJgRDHSyvBPwtZoSJXakeJI9iuRLGDygdl6V0yKDtyMAeMPZySj8yfD+UapvRPj2JOwkyAooSV5IwdDjPdCPspe8LyTl9IKJvDETKKRv6vnlUasgg0fAAAAAElFTkSuQmCC) no-repeat center center;
}
}
.good-name {
font-size: 28rpx;
margin-top: 20rpx;
color: #333;
overflow: hidden;
text-align: justify;
line-height: 1.2;
margin-bottom: 10rpx;
}
.good-price {
// border:1rpx solid red;
display: flex;
align-items: center;
justify-content: space-between;
margin:10rpx 0;
font-weight: 500;
color: #fe6263;
}
.price {
display: flex;
align-items: baseline;
.price-text{
font-size: 28rpx;
}
.price-text::before{
content: "";
font-size: 28rpx;
}
.unit{
font-size:28rpx;
}
}
.ori-price {
font-size: 20rpx;
color: #b2b2b2;
margin-left: 10rpx;
text-decoration: line-through;
&:before {
content: "";
}
}
.act-type-wrapper {
height: 20rpx;
overflow: hidden;
}
.act-type {
@extend %act-type-text;
}
.countTime-wrap{
height: 17rpx;
width: 100%;
margin-bottom: 5rpx;
}
.countTime{
color: #E6A23C;
font-size: 26rpx;
}
.priceStyle{
color: rgb(149, 209,131);
font-size: 25rpx;
margin-bottom:10rpx;
}
.priceColor{
color:#fe6263;
}
}

View File

@@ -0,0 +1,404 @@
<template>
<view class="waterfalls-flow">
<view v-for="(item, index) in data.column" :key="index" class="waterfalls-flow-column"
:id="`waterfalls_flow_column_${index + 1}`" :msg="msg" :style="{ 'width': w, 'margin-left': index == 0 ? 0 : m }">
<view :class="['column-value', { 'column-value-show': item2.o }]" v-for="(item2, index2) in columnValue(index)"
:key="index2" :style="[s1]" @click.stop="wapperClick(item2)">
<image
:class="['img', { 'img-hide': item2[hideImageKey] == true || item2[hideImageKey] == 1 }, { 'img-error': !item2[data.imageKey] }]"
:src="item2[data.imageKey]" mode="widthFix" @load="imgLoad(item2, index + 1)"
@error="imgError(item2, index + 1)" @click.stop="imageClick(item2)">
</image>
<view class="inner">
<view>
<view class="good-name">{{ item2.name }}</view>
<view class="countTime-wrap">
<view class="countTime"
v-if="item2.actType === 5 && item2.actPrice !== 0 && item2.trendType !== 0 && timeTen">
{{ item2.trendType === 2 ? '降价' : '升价' }}还剩 {{ skunameMsg }}</view>
</view>
<view class="priceStyle" v-if="item2.unit">
<text></text>
<text v-if="isB2B && storeID[0] !== 666666"></text>
<span v-else>{{ item2.unit }}</span>
<text>约为</text>
<span class="priceColor" >{{ $filters.toFixed2(item2.curPrice) }}</span>
<text>(以实际价格为准)</text>
</view>
<view class="good-price">
<view class="price">
<view class="price-text">
<span class="good-price-font" v-if="isB2B || storeID[0] === 666666">{{((item2.curPrice/100).toFixed(2)).split('.')[0] + '.' + ((item2.curPrice/100).toFixed(2)).split('.')[1] }}</span>
<span class="good-price-font" v-else-if="item2.weight">{{(((((item2.curPrice/100)/item2.weight)*500)/100).toFixed(2)).split('.')[0] + '.' + (((((item2.curPrice/100)/item2.weight)*500)/100).toFixed(2)).split('.')[1] }}</span>
<span class="good-price-font" v-else>{{(((((item2.curPrice/100))*500)/100).toFixed(2)).split('.')[0] + '.' + (((((item2.curPrice/100))*500)/100).toFixed(2)).split('.')[1] }}</span>
</view>
<span class="unit" v-if="storeID[0] === 666666 || isB2B">/{{item2.unit}}</span>
<text class="unit" v-else>/</text>
<view class="ori-price" v-if="item2.oriPrice && !isMaterial" >{{$filters.toFixed2(item2.oriPrice)}}</view>
</view>
<view class="act-type-wrapper">
<view class="act-type" v-if="item2.actType">{{actTypeArr[item2.actType]}}</view>
</view>
</view>
<view v-if="isShowShop">
<PlusMinus :type="'input'" :skuName="item2" :count="item2.count" @clickPlus="skuNamePlusRecommend(item2.index)" @clickMinus="skuNameMinusRecommend(item2.index)" @countChange="skuNameChangeRecommend($event,item2.index)" ></PlusMinus>
</view>
</view>
</view>
</view>
</view>
</view>
<!-- 加载更多 -->
<view v-if="!isMaterial" style="background:#fff;margin-top:20rpx" :style="{'background-color':topGoodsStatus === 'loading'?'#f4f4f4':'#fff'}">
<uni-load-more :status="topGoodsStatus" ></uni-load-more>
</view>
<!-- <div
class="nearlyStores"
v-for="(item, index) in nearlyStores"
:key="index"
@click="clickMore(item)"
>
商家信息
<div class="storeInfo">
<div style="display: flex;align-items: center;">
<image :src="$filters.urlToHttps(item.storeInfo.brandLogo)" class="storeImg" alt="" />
</div>
<div v-if="item.storeInfo.status===-1" class="storeStatus">休息中</div>
<div class="storeRight">
<div class="storeName" style="display: flex;">
<div >{{item.storeInfo.brandName}}</div>
<div>{{item.storeInfo.storeName }}</div>
<div>--</div>
<div>{{ item.storeInfo.brandName }}</div>
</div>
营业时间
<span class="storeTime">营业时间</span>
<span class="storeTime1" v-if="item.storeInfo.openTime1 && item.storeInfo.closeTime1">{{$filters.processingHours(item.storeInfo.openTime1)}}-{{$filters.processingHours(item.storeInfo.closeTime1)}}</span>
<span class="storeTime2" v-if="item.storeInfo.openTime2 && item.storeInfo.closeTime2">{{$filters.processingHours(item.storeInfo.openTime2)}}-{{$filters.processingHours(item.storeInfo.closeTime2)}}</span>
评分
<div class="storeRate">
<div>门店评分</div>
<div v-if="item.storeInfo && item.storeInfo.storeWeeklyScore">
<uni-rate :size="20" allow-half :value="(item.storeInfo.storeWeeklyScore *5) /100" />
</div>
<div v-else>暂无评分</div>
</div>
满减
<div class="fullReduce">
<div v-if="item.StoreDeductionInfo[0].deliveryFeeDeductionFee && item.StoreDeductionInfo[0].deliveryFeeDeductionSill" class="fullReduceFee">
{{item.StoreDeductionInfo[0].deliveryFeeDeductionSill/100}}{{item.StoreDeductionInfo[0].deliveryFeeDeductionFee/100}}
</div>
距离
<div v-if="String(item.storeInfo.distance).length<=3">距您{{item.storeInfo.distance}}m</div>
<div v-else>距您{{(item.storeInfo.distance/1000).toFixed(2)}}km</div>
</div>
</div>
</div>
商品
<div class="storeGood" v-if="item.StoreSkuInfo!==null" @click.stop="clickStop">
<div v-for="(ite,i) in item.StoreSkuInfo" :key="i" class="storeGoodItem" @click="toDetail(ite)">
<img :src="$filters.urlToHttps(ite.img,'img120')" class="storeGoodImg">
<image :src="$filters.urlToHttps(ite.img)" class="storeGoodImg" alt="" />
<div class="storeGoodName" >{{ite.skuName}}</div>
<div class="storeGoodPrice" >{{(ite.price/100).toFixed(2)}}/{{ite.unit}}</div>
</div>
<div class="storeGoodMore" @click="clickMore(item)">
查看更多
</div>
</div>
<div class="enterStore" v-else >
<div style="padding:20rpx;background:rgba(78, 179, 49, 0.9);color:#fff;border-radius: 10rpx;border:1rpx solid red;"> </div>
</div>
</div> -->
</template>
<script>
import PlusMinus from "@/components/goodCmp/plus-minus/plus-minus";
import { mapGetters } from "vuex";
import { jumpPage } from "@/utils/uniapi.js";
export default {
props: {
value: Array,
column: { // 列的数量
type: [String, Number],
default: 2
},
maxColumn: { // 最大列数
type: [String, Number],
default: 5
},
columnSpace: { // 列之间的间距 百分比
type: [String, Number],
default: 2
},
imageKey: { // 图片key
type: [String],
default: 'image'
},
hideImageKey: { // 隐藏图片key
type: [String],
default: 'hide'
},
seat: { // 文本的位置1图片之上 2图片之下
type: [String, Number],
default: 2
},
listStyle: { // 单个展示项的样式eg:{'background':'red'}
type: Object
}
},
components: {
PlusMinus
},
data() {
return {
data: {
list: this.value ? this.value : [],
column: this.column < 2 ? 2 : this.column,
columnSpace: this.columnSpace <= 5 ? this.columnSpace : 5,
imageKey: this.imageKey,
seat: this.seat
},
msg: 0,
listInitStyle: {
'border-radius': '12rpx',
'margin-bottom': '20rpx',
'background-color': '#fff'
},
adds: [], //预置数据
isLoaded: true,
curIndex: 0,
isRefresh: true,
flag: false,
refreshDatas: [],
skunameMsg: '',
topGoodsStatus:'loading',
actTypeArr:['','','','直降','秒杀','折扣']
}
},
computed: {
...mapGetters({
isB2B: 'indexPage/isB2B',
isMaterial: "indexPage/isMaterial",
storeID:"indexPage/storeID",
isShowShop: "isShowShop"
}),
// 计算列宽
w() {
const column_rate = `${100 / this.data.column - (+this.data.columnSpace)}%`;
return column_rate;
},
// 计算margin
m() {
const column_margin = `${(100 - (100 / this.data.column - (+this.data.columnSpace)).toFixed(5) * this.data.column) / (this.data.column - 1)}%`;
return column_margin;
},
// list样式
s1() {
return { ...this.listInitStyle, ...this.listStyle };
}
},
created() {
// 初始化
this.refresh();
},
methods: {
// 添加
skuNamePlusRecommend(index) {
this.$emit('clickPlus',index)
},
// 减少
skuNameMinusRecommend(index) {
this.$emit('clickMinus',index)
},
// 商品数量发生改变
skuNameChangeRecommend(e,index) {
this.$emit('countChange',e,index)
},
// 预加载图片
loadImages(idx = 0) {
let count = 0;
const newList = this.data.list.filter((item, index) => index >= idx);
for (let i = 0; i < newList.length; i++) {
// #ifndef APP-PLUS || APP
uni.getImageInfo({
src: `${newList[i][this.imageKey]}.jpg`,
complete: res => {
count++;
if (count == newList.length) this.initValue(idx);
}
})
// #endif
// #ifdef APP-PLUS || APP
plus.io.getImageInfo({
src: `${newList[i][this.imageKey]}.jpg`,
complete: res => {
count++;
if (count == newList.length) this.initValue(idx);
}
})
// #endif
}
},
// 刷新
refresh() {
if (!this.isLoaded) {
this.refreshDatas = this.value;
return false;
};
this.topGoodsStatus = 'loading'
this.refreshDatas = [];
this.isRefresh = true;
this.adds = [];
this.data.list = this.value ? this.value : [];
this.data.column = this.column < 2 ? 2 : this.column >= this.maxColumn ? this.maxColumn : this.column;
this.data.columnSpace = this.columnSpace <= 5 ? this.columnSpace : 5;
this.data.imageKey = this.imageKey;
this.data.seat = this.seat;
this.curIndex = 0;
// 每列的数据初始化
for (let i = 1; i <= this.data.column; i++) {
this.data[`column_${i}_values`] = [];
this.msg++;
}
this.$nextTick(() => {
this.initValue(this.curIndex, 'refresh==>');
})
},
columnValue(index) {
return this.data[`column_${index + 1}_values`]
},
change(newValue) {
console.log('this.data9999999999',this.data)
for (let i = 0; i < this.data.list.length; i++) {
console.log(i,'i',this.data.list[i],'属于哪一列',this.data.list[i].column)
const cv = this.data[`column_${this.data.list[i].column}_values`];
for (let j = 0; j < cv.length; j++) {
if (newValue[i] && i === cv[j].index) {
this.data[`column_${this.data.list[i].column}_values`][j] = Object.assign(cv[j], newValue[i]);
this.msg++;
break;
}
}
}
},
getMin(a, s) {
let m = a[0][s];
let mo = a[0];
for (var i = a.length - 1; i >= 0; i--) {
if (a[i][s] < m) {
m = a[i][s];
}
}
mo = a.filter(i => i[s] == m);
return mo[0];
},
// 计算每列的高度
getMinColumnHeight() {
return new Promise(resolve => {
const heightArr = [];
for (let i = 1; i <= this.data.column; i++) {
const query = uni.createSelectorQuery().in(this);
query.select(`#waterfalls_flow_column_${i}`).boundingClientRect(data => {
heightArr.push({ column: i, height: data.height });
}).exec(() => {
if (this.data.column <= heightArr.length) {
resolve(this.getMin(heightArr, 'height'));
}
});
}
})
},
async initValue(i, from) {
this.isLoaded = false;
if (i >= this.data.list.length || this.refreshDatas.length) {
this.msg++;
this.loaded();
return false;
}
const minHeightRes = await this.getMinColumnHeight();
const c = this.data[`column_${minHeightRes.column}_values`];
this.data.list[i].column = minHeightRes.column;
c.push({ ...this.data.list[i], cIndex: c.length, index: i, o: 0 });
this.msg++;
this.topGoodsStatus = 'noMore'
},
// 图片加载完成
imgLoad(item, c) {
const i = item.index;
item.o = 1;
this.$set(this.data[`column_${c}_values`], item.cIndex, JSON.parse(JSON.stringify(item)));
this.initValue(i + 1);
},
// 图片加载失败
imgError(item, c) {
const i = item.index;
item.o = 1;
item[this.data.imageKey] = null;
this.$set(this.data[`column_${c}_values`], item.cIndex, JSON.parse(JSON.stringify(item)));
this.initValue(i + 1);
},
// 渲染结束
loaded() {
if (this.refreshDatas.length) {
this.isLoaded = true;
this.refresh();
return false;
}
this.curIndex = this.data.list.length;
if (this.adds.length) {
this.data.list = this.adds[0];
this.adds.splice(0, 1);
this.initValue(this.curIndex);
} else {
if (this.data.list.length) this.$emit('loaded');
this.isLoaded = true;
this.isRefresh = false;
}
},
// 单项点击事件
wapperClick(item) {
jumpPage('navigateTo',`/pagesGoods/good-detail/index?skuID=${item.id}&storeID=${this.storeID[0]}`)
// this.$emit('wapperClick', item);
},
// 图片点击事件
imageClick(item) {
jumpPage('navigateTo',`/pagesGoods/good-detail/index?skuID=${item.id}&storeID=${this.storeID[0]}`)
// this.$emit('imageClick', item);
}
},
watch: {
value: {
deep: true,
handler(newValue, oldValue) {
setTimeout(() => {
this.$nextTick(() => {
if (this.isRefresh) return false;
if (this.isLoaded) {
// if (newValue.length <= this.curIndex) return this.refresh();
if (newValue.length <= this.curIndex) return this.change(newValue);
this.data.list = newValue;
this.$nextTick(() => {
this.initValue(this.curIndex, 'watch==>');
})
} else {
this.adds.push(newValue);
}
})
}, 1000)
}
},
column(newValue) {
this.refresh();
}
}
}
</script>
<style lang="scss" scoped>
@use "./index.scss";
</style>