商城分包相关内容
This commit is contained in:
parent
65c88c24eb
commit
6bf2a1ada9
@ -378,6 +378,7 @@
|
|||||||
}
|
}
|
||||||
}]
|
}]
|
||||||
},
|
},
|
||||||
|
// 企业相关分包
|
||||||
{
|
{
|
||||||
"root": "pageCompany",
|
"root": "pageCompany",
|
||||||
"pages": [{
|
"pages": [{
|
||||||
@ -390,6 +391,7 @@
|
|||||||
}
|
}
|
||||||
}]
|
}]
|
||||||
},
|
},
|
||||||
|
// 客户相关分包
|
||||||
{
|
{
|
||||||
"root": "pagesCustomer",
|
"root": "pagesCustomer",
|
||||||
"pages": [{
|
"pages": [{
|
||||||
@ -399,6 +401,77 @@
|
|||||||
"navigationStyle": "custom"
|
"navigationStyle": "custom"
|
||||||
}
|
}
|
||||||
}]
|
}]
|
||||||
|
},
|
||||||
|
// 商城相关分包
|
||||||
|
{
|
||||||
|
"root": "pagesShop",
|
||||||
|
"pages": [{
|
||||||
|
"path": "pages/shopMall/index",
|
||||||
|
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "大贸商城",
|
||||||
|
// #ifdef WEB
|
||||||
|
"navigationStyle": "default"
|
||||||
|
// #endif
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/shopGoods/goods/goods",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "商品详情",
|
||||||
|
// #ifdef WEB
|
||||||
|
"navigationStyle": "default"
|
||||||
|
// #endif
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/shopGoods/list/list",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "商品列表",
|
||||||
|
// #ifdef WEB
|
||||||
|
"navigationStyle": "default"
|
||||||
|
// #endif
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/shopGoods/evaluate/evaluate",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "商品评价",
|
||||||
|
// #ifdef WEB
|
||||||
|
"navigationStyle": "default"
|
||||||
|
// #endif
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/shopGoods/search/search",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "搜索商品",
|
||||||
|
// #ifdef WEB
|
||||||
|
"navigationStyle": "default"
|
||||||
|
// #endif
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 购物车相关
|
||||||
|
{
|
||||||
|
"path": "pages/shopCart/cart/cart",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "购物车",
|
||||||
|
// #ifdef WEB
|
||||||
|
"navigationStyle": "default"
|
||||||
|
// #endif
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 分类相关
|
||||||
|
{
|
||||||
|
"path": "pages/shopCategory/category/category",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "分类",
|
||||||
|
// #ifdef WEB
|
||||||
|
"navigationStyle": "default"
|
||||||
|
// #endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"preloadRule": {
|
"preloadRule": {
|
||||||
|
687
src/pagesShop/pages/shopCart/cart/cart.vue
Normal file
687
src/pagesShop/pages/shopCart/cart/cart.vue
Normal file
@ -0,0 +1,687 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { useCartStore, useMemberStore, useRefreshStore } from '@/stores'
|
||||||
|
import { nextTick, ref, watch } from 'vue'
|
||||||
|
import type { cartItem } from '@/types/cart'
|
||||||
|
import { deleteCartApi, getCartApi, putCartApi, putCartSelectedAllApi } from '@/api/cart'
|
||||||
|
import type { InputNumberBoxEvent } from '@/components/vk-data-input-number-box/vk-data-input-number-box'
|
||||||
|
import { computed } from 'vue'
|
||||||
|
import { debounce } from 'lodash'
|
||||||
|
import { executeWithLoading, fullUrl, onShowRefreshData } from '@/utils/common'
|
||||||
|
// zpaging必须导入需要用到的页面生命周期(即使在当前页面上没有直接使用到)
|
||||||
|
import useZPagingComp from '@/uni_modules/z-paging/components/z-paging/js/hooks/useZPagingComp.js'
|
||||||
|
import { pageUrl, safeBottom } from '@/utils/constants'
|
||||||
|
import { onLoad, onShow } from '@dcloudio/uni-app'
|
||||||
|
|
||||||
|
const memberStore = useMemberStore()
|
||||||
|
const cartStore = useCartStore()
|
||||||
|
|
||||||
|
const paging = ref()
|
||||||
|
// 子组件内使用useZPagingComp
|
||||||
|
defineExpose({ ...useZPagingComp(paging) })
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
type?: string
|
||||||
|
}>()
|
||||||
|
|
||||||
|
// 结算栏安全距离
|
||||||
|
const toolbarSafeAreaInsets = computed(() => {
|
||||||
|
if (props.type === 'tabBar') return 0
|
||||||
|
return safeBottom
|
||||||
|
})
|
||||||
|
|
||||||
|
// 商品列表
|
||||||
|
const cartList = ref<cartItem[]>()
|
||||||
|
const queryList = (page: number, page_size: number) => {
|
||||||
|
getCartApi({
|
||||||
|
page,
|
||||||
|
page_size,
|
||||||
|
}).then((res) => {
|
||||||
|
paging.value.complete(res.result.data)
|
||||||
|
cartStore.setCartTotalNum(res.result.total_num)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
//删除购物车商品
|
||||||
|
const onDeleteCart = (id: string[] | number[]) => {
|
||||||
|
const content = id.length > 1 ? '确定要删除选中的商品吗' : '确定要删除该商品吗'
|
||||||
|
|
||||||
|
uni.showModal({
|
||||||
|
title: '提示',
|
||||||
|
content: content,
|
||||||
|
showCancel: true,
|
||||||
|
success: async ({ confirm, cancel }) => {
|
||||||
|
if (confirm) {
|
||||||
|
await deleteCartApi({
|
||||||
|
id,
|
||||||
|
})
|
||||||
|
uni.showToast({
|
||||||
|
title: '删除成功',
|
||||||
|
icon: 'success',
|
||||||
|
mask: true,
|
||||||
|
})
|
||||||
|
setTimeout(() => {
|
||||||
|
paging.value.reload()
|
||||||
|
}, 1000)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 批量删除选择的商品
|
||||||
|
const onDeleteCartBatch = () => {
|
||||||
|
if (!selectedList.value || selectedList.value.length === 0) {
|
||||||
|
return uni.showToast({
|
||||||
|
icon: 'none',
|
||||||
|
title: '请选择要删除的商品',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
onDeleteCart(selectedList.value.map((item) => item.id))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 移除无效的商品
|
||||||
|
const removeInvalidCart = () => {
|
||||||
|
if (!cartList.value || cartList.value.length === 0) return
|
||||||
|
|
||||||
|
const invalidList = cartList.value.filter((item) => item.disabled)
|
||||||
|
if (invalidList.length === 0) {
|
||||||
|
return uni.showToast({
|
||||||
|
icon: 'none',
|
||||||
|
title: '购物车内商品都是有效的噢,快去结算吧~',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
uni.showModal({
|
||||||
|
title: '提示',
|
||||||
|
content: '是否快速清理无效商品',
|
||||||
|
showCancel: true,
|
||||||
|
success: ({ confirm, cancel }) => {
|
||||||
|
if (confirm) {
|
||||||
|
uni.showLoading({
|
||||||
|
title: '快速清理中…',
|
||||||
|
mask: true,
|
||||||
|
})
|
||||||
|
setTimeout(() => {
|
||||||
|
deleteCartApi({
|
||||||
|
id: invalidList.map((item) => item.id),
|
||||||
|
})
|
||||||
|
uni.showToast({
|
||||||
|
title: '清理完成',
|
||||||
|
icon: 'success',
|
||||||
|
mask: true,
|
||||||
|
})
|
||||||
|
uni.hideLoading()
|
||||||
|
paging.value.reload()
|
||||||
|
}, 1500)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
//修改购物车
|
||||||
|
const onChangeCart = debounce((event: InputNumberBoxEvent) => {
|
||||||
|
putCartApi({
|
||||||
|
id: Number(event.index),
|
||||||
|
num: event.value,
|
||||||
|
})
|
||||||
|
}, 500)
|
||||||
|
|
||||||
|
//修改选中状态
|
||||||
|
const onChangeSelected = debounce((item: cartItem) => {
|
||||||
|
if (item.disabled) return
|
||||||
|
//取反选中状态
|
||||||
|
item.selected = !item.selected
|
||||||
|
putCartApi({
|
||||||
|
id: item.id,
|
||||||
|
selected: item.selected,
|
||||||
|
})
|
||||||
|
}, 500)
|
||||||
|
|
||||||
|
//修改全选状态
|
||||||
|
const onChangeSelectedAll = debounce(() => {
|
||||||
|
if (!cartList.value || cartList.value.length === 0) return
|
||||||
|
if (!effectiveCartList.value || effectiveCartList.value.length === 0) return
|
||||||
|
|
||||||
|
//取反全选状态
|
||||||
|
const isSelectedAllOld = isSelectedAll.value
|
||||||
|
//更新每个商品选中状态
|
||||||
|
cartList.value?.forEach((item) => {
|
||||||
|
item.selected = !isSelectedAllOld
|
||||||
|
})
|
||||||
|
//更新到服务器
|
||||||
|
putCartSelectedAllApi({
|
||||||
|
selected: !isSelectedAllOld,
|
||||||
|
})
|
||||||
|
}, 500)
|
||||||
|
|
||||||
|
//选中的购物车商品
|
||||||
|
const selectedList = computed(() => {
|
||||||
|
return cartList.value?.filter((item) => item.selected && !item.disabled)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 有效的购物车商品列表
|
||||||
|
const effectiveCartList = computed(() => {
|
||||||
|
return cartList.value?.filter((item) => !item.disabled)
|
||||||
|
})
|
||||||
|
|
||||||
|
//购物车商品全选状态
|
||||||
|
const isSelectedAll = computed(() => {
|
||||||
|
if (!selectedList.value || !cartList.value || selectedList.value.length <= 0) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return selectedList.value.length === effectiveCartList.value?.length
|
||||||
|
})
|
||||||
|
|
||||||
|
//选中商品数量
|
||||||
|
const selectedCount = computed(() => {
|
||||||
|
if (!selectedList.value) return 0
|
||||||
|
|
||||||
|
const count = selectedList.value?.reduce((sum, item) => (sum += item.num), 0)
|
||||||
|
|
||||||
|
return count > 99 ? '99+' : count
|
||||||
|
})
|
||||||
|
|
||||||
|
//选中商品总价
|
||||||
|
const selectedPrice = computed(() => {
|
||||||
|
return selectedList.value?.reduce((sum, item) => (sum += item.sku.price * item.num), 0).toFixed(2)
|
||||||
|
})
|
||||||
|
|
||||||
|
//跳转支付
|
||||||
|
const gotoPayment = () => {
|
||||||
|
if (selectedCount.value === 0) {
|
||||||
|
return uni.showToast({
|
||||||
|
icon: 'none',
|
||||||
|
title: '请选择结算商品',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
uni.navigateTo({ url: `${pageUrl['order-create']}` })
|
||||||
|
}
|
||||||
|
|
||||||
|
const formatPrice = (price: string | number) => {
|
||||||
|
let [integer, decimal] = price.toString().split('.')
|
||||||
|
|
||||||
|
return {
|
||||||
|
integer,
|
||||||
|
decimal,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 跳转首页
|
||||||
|
const goToIndex = (event: any) => {
|
||||||
|
event(false)
|
||||||
|
uni.redirectTo({
|
||||||
|
url: `${pageUrl['shopping-mall']}`,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const totalNum = computed(() => {
|
||||||
|
return cartList.value?.reduce((sum, item) => (sum += item.num), 0) || 0
|
||||||
|
})
|
||||||
|
|
||||||
|
onShow(() => {
|
||||||
|
if (memberStore.profile) {
|
||||||
|
nextTick(() => {
|
||||||
|
paging.value?.refresh()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => totalNum.value,
|
||||||
|
(newVal) => {
|
||||||
|
cartStore.setCartTotalNum(newVal)
|
||||||
|
// 设置购物车角标
|
||||||
|
cartStore.setCartTabBadge()
|
||||||
|
},
|
||||||
|
{ immediate: true, deep: true },
|
||||||
|
)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<template v-if="memberStore.profile">
|
||||||
|
<view class="cart-list">
|
||||||
|
<z-paging
|
||||||
|
ref="paging"
|
||||||
|
v-model="cartList"
|
||||||
|
:auto="false"
|
||||||
|
@query="queryList"
|
||||||
|
empty-view-text="购物车空空如也,快去逛逛看吧~"
|
||||||
|
show-empty-view-reload
|
||||||
|
empty-view-reload-text="去逛逛"
|
||||||
|
@emptyViewReload="goToIndex"
|
||||||
|
:paging-style="{
|
||||||
|
backgroundColor: '#f7f7f8',
|
||||||
|
}"
|
||||||
|
:empty-view-reload-style="{
|
||||||
|
backgroundColor: '#ff5555',
|
||||||
|
color: 'white',
|
||||||
|
borderRadius: '10px',
|
||||||
|
padding: '10px 20px',
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<view class="cart-operate" v-if="cartList && cartList.length > 0">
|
||||||
|
<text class="operate-item icon icon-delete" @tap="onDeleteCartBatch">批量删除</text>
|
||||||
|
<text class="operate-item icon icon-clear" @tap="removeInvalidCart">快速清理</text>
|
||||||
|
</view>
|
||||||
|
<view class="cart-list-item">
|
||||||
|
<uni-swipe-action>
|
||||||
|
<uni-swipe-action-item
|
||||||
|
v-for="item in cartList"
|
||||||
|
:key="item.sku_id"
|
||||||
|
class="cart-swipe"
|
||||||
|
:class="{ disabled: item.disabled }"
|
||||||
|
>
|
||||||
|
<view class="goods">
|
||||||
|
<text v-if="item.disabled" class="disabled">{{ item.disabled_text }}</text>
|
||||||
|
<text
|
||||||
|
v-else
|
||||||
|
class="checkbox"
|
||||||
|
:class="{ checked: item.selected }"
|
||||||
|
@tap="onChangeSelected(item)"
|
||||||
|
></text>
|
||||||
|
<navigator
|
||||||
|
:url="`${pageUrl['shopping-mall-goods-detail']}?id=${item.goods_id}`"
|
||||||
|
hover-class="none"
|
||||||
|
class="navigator"
|
||||||
|
>
|
||||||
|
<image mode="aspectFill" class="picture" :src="fullUrl(item.sku.image)"></image>
|
||||||
|
<view class="meta">
|
||||||
|
<view class="name ellipsis">{{ item.goods.name }}</view>
|
||||||
|
<view class="attrsText ellipsis">{{ item.sku.spec_text }}</view>
|
||||||
|
<view class="price">
|
||||||
|
<span class="integer">{{ formatPrice(item.sku.price).integer }}</span>
|
||||||
|
<span class="decimal">.{{ formatPrice(item.sku.price).decimal }}</span>
|
||||||
|
</view>
|
||||||
|
<view
|
||||||
|
class="price-diff"
|
||||||
|
v-if="item.old_price && item.old_price - item.sku.price > 0"
|
||||||
|
>
|
||||||
|
<text>加购后降</text>
|
||||||
|
<text class="diff-amount">
|
||||||
|
¥{{ (item.old_price - item.sku.price).toFixed(2) }}
|
||||||
|
</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</navigator>
|
||||||
|
<view class="num" v-if="!item.disabled">
|
||||||
|
<vk-data-input-number-box
|
||||||
|
v-model="item.num"
|
||||||
|
:index="item.id"
|
||||||
|
:min="1"
|
||||||
|
:max="item.sku.stock"
|
||||||
|
@plus="onChangeCart($event)"
|
||||||
|
@minus="onChangeCart($event)"
|
||||||
|
@blur="onChangeCart($event)"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<template #right>
|
||||||
|
<view class="cart-swipe-right">
|
||||||
|
<button class="button delete-button" @tap="onDeleteCart([item.id])">删除</button>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
</uni-swipe-action-item>
|
||||||
|
</uni-swipe-action>
|
||||||
|
</view>
|
||||||
|
<template #bottom>
|
||||||
|
<view class="toolbar" :style="{ paddingBottom: toolbarSafeAreaInsets + 'px' }">
|
||||||
|
<text class="all" :class="{ checked: isSelectedAll }" @tap="onChangeSelectedAll"
|
||||||
|
>全选</text
|
||||||
|
>
|
||||||
|
<view class="total-price">
|
||||||
|
<text class="text">总计:</text>
|
||||||
|
<text class="amount">{{ selectedPrice ?? '0.00' }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="button-grounp">
|
||||||
|
<view
|
||||||
|
class="button payment-button"
|
||||||
|
:class="{ disabled: selectedCount === 0 }"
|
||||||
|
@tap="gotoPayment"
|
||||||
|
>
|
||||||
|
去结算({{ selectedCount }})
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="toolbar-height"></view>
|
||||||
|
</template>
|
||||||
|
</z-paging>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
<!-- 未登录: 提示登录 -->
|
||||||
|
<view class="login-blank" v-else>
|
||||||
|
<text class="text">登录后可查看购物车中的商品</text>
|
||||||
|
<navigator :url="pageUrl['login']" hover-class="none">
|
||||||
|
<button class="button">去登录</button>
|
||||||
|
</navigator>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
// 根元素
|
||||||
|
:host {
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
background-color: #f7f7f8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-operate {
|
||||||
|
padding-top: 20rpx;
|
||||||
|
padding-left: 20rpx;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
|
||||||
|
.operate-item {
|
||||||
|
padding-right: 20rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-list {
|
||||||
|
.tips {
|
||||||
|
// display: flex;
|
||||||
|
align-items: center;
|
||||||
|
line-height: 1;
|
||||||
|
margin: 30rpx 10rpx;
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #666;
|
||||||
|
|
||||||
|
.label {
|
||||||
|
color: #fff;
|
||||||
|
padding: 7rpx 15rpx 5rpx;
|
||||||
|
border-radius: 4rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
background-color: #ff5f3c;
|
||||||
|
margin-right: 10rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-list-item {
|
||||||
|
padding: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-swipe.disabled {
|
||||||
|
opacity: 0.5;
|
||||||
|
filter: grayscale(100%);
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 购物车商品
|
||||||
|
.goods {
|
||||||
|
display: flex;
|
||||||
|
padding: 3% 5% 3% 8%;
|
||||||
|
// border-radius: 10rpx;
|
||||||
|
background-color: #fff;
|
||||||
|
position: relative;
|
||||||
|
border-bottom: 1rpx solid #ededed;
|
||||||
|
|
||||||
|
.navigator {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.disabled {
|
||||||
|
position: absolute;
|
||||||
|
top: 40%;
|
||||||
|
left: 1%;
|
||||||
|
width: 50rpx;
|
||||||
|
font-size: 18rpx;
|
||||||
|
text-align: center;
|
||||||
|
background-color: #000000;
|
||||||
|
color: white;
|
||||||
|
padding: 3px;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 50rpx;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '\e6cd';
|
||||||
|
font-family: 'iconfont' !important;
|
||||||
|
font-size: 40rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.checked::before {
|
||||||
|
content: '\e6cc';
|
||||||
|
color: #ff5f3c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.picture {
|
||||||
|
width: 180rpx;
|
||||||
|
height: 180rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.meta {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-around;
|
||||||
|
margin-left: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.name {
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #444;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.attrsText {
|
||||||
|
line-height: 1.8;
|
||||||
|
margin-top: 10rpx;
|
||||||
|
padding: 0 15rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
align-self: flex-start;
|
||||||
|
border-radius: 4rpx;
|
||||||
|
color: #888;
|
||||||
|
background-color: #f7f7f8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.price {
|
||||||
|
line-height: 2;
|
||||||
|
color: #e33333;
|
||||||
|
margin-bottom: 2rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '¥';
|
||||||
|
font-size: 20rpx;
|
||||||
|
vertical-align: sub;
|
||||||
|
}
|
||||||
|
|
||||||
|
.integer {
|
||||||
|
font-size: 30rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.decimal {
|
||||||
|
font-size: 20rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.price-diff {
|
||||||
|
line-height: 1;
|
||||||
|
color: orange; // 红色显示差价
|
||||||
|
font-size: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 商品数量
|
||||||
|
.num {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 25rpx;
|
||||||
|
right: 10rpx;
|
||||||
|
|
||||||
|
.text {
|
||||||
|
height: 100%;
|
||||||
|
padding: 0 20rpx;
|
||||||
|
font-size: 32rpx;
|
||||||
|
color: #444;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input {
|
||||||
|
height: 100%;
|
||||||
|
text-align: center;
|
||||||
|
border-radius: 4rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #444;
|
||||||
|
background-color: #f6f6f6;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-swipe {
|
||||||
|
display: block;
|
||||||
|
margin: 20rpx 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-swipe-right {
|
||||||
|
display: flex;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
.button {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
width: 50px;
|
||||||
|
padding: 6px;
|
||||||
|
line-height: 1.5;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 26rpx;
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.delete-button {
|
||||||
|
background-color: #cf4444;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 空状态
|
||||||
|
.cart-blank,
|
||||||
|
.login-blank {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 60vh;
|
||||||
|
.image {
|
||||||
|
width: 400rpx;
|
||||||
|
height: 281rpx;
|
||||||
|
}
|
||||||
|
.text {
|
||||||
|
color: #444;
|
||||||
|
font-size: 26rpx;
|
||||||
|
margin: 20rpx 0;
|
||||||
|
}
|
||||||
|
.button {
|
||||||
|
width: 240rpx !important;
|
||||||
|
height: 60rpx;
|
||||||
|
line-height: 60rpx;
|
||||||
|
margin-top: 20rpx;
|
||||||
|
font-size: 26rpx;
|
||||||
|
border-radius: 60rpx;
|
||||||
|
color: #fff;
|
||||||
|
background-color: #ff5f3c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 吸底工具栏
|
||||||
|
.toolbar {
|
||||||
|
position: fixed;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: var(--window-bottom);
|
||||||
|
z-index: 100;
|
||||||
|
|
||||||
|
height: 100rpx;
|
||||||
|
// padding: 0 20rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
border-top: 1rpx solid #ededed;
|
||||||
|
border-bottom: 1rpx solid #ededed;
|
||||||
|
background-color: #fff;
|
||||||
|
box-sizing: content-box;
|
||||||
|
|
||||||
|
.all {
|
||||||
|
margin-left: 15rpx;
|
||||||
|
font-size: 15px;
|
||||||
|
color: #444;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.all::before {
|
||||||
|
font-family: 'iconfont' !important;
|
||||||
|
content: '\e6cd';
|
||||||
|
font-size: 40rpx;
|
||||||
|
margin-right: 8rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checked::before {
|
||||||
|
content: '\e6cc';
|
||||||
|
color: #ff5f3c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text {
|
||||||
|
margin-right: 8rpx;
|
||||||
|
margin-left: 12rpx;
|
||||||
|
color: #444;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.amount {
|
||||||
|
font-size: 20px;
|
||||||
|
color: #cf4444;
|
||||||
|
|
||||||
|
.decimal {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '¥';
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-grounp {
|
||||||
|
// margin-left: auto;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 72rpx;
|
||||||
|
font-size: 13px;
|
||||||
|
color: #fff;
|
||||||
|
|
||||||
|
.button {
|
||||||
|
width: 240rpx;
|
||||||
|
margin: 0 10rpx;
|
||||||
|
border-radius: 72rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.payment-button {
|
||||||
|
background-color: #ff5f3c;
|
||||||
|
|
||||||
|
&.disabled {
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.total-price {
|
||||||
|
//靠右对齐
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 底部占位空盒子
|
||||||
|
.toolbar-height {
|
||||||
|
height: 100rpx;
|
||||||
|
}
|
||||||
|
</style>
|
378
src/pagesShop/pages/shopCategory/category/category.vue
Normal file
378
src/pagesShop/pages/shopCategory/category/category.vue
Normal file
@ -0,0 +1,378 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { getCategoryApi, getClassifyDetailApi } from '@/api/catogory'
|
||||||
|
import type { categoryTopItem } from '@/types/category'
|
||||||
|
import { onLoad, onShow } from '@dcloudio/uni-app'
|
||||||
|
import { computed, nextTick, watch } from 'vue'
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { fullUrl } from '@/utils/common'
|
||||||
|
import { pageUrl } from '@/utils/constants'
|
||||||
|
|
||||||
|
//一级tab标识
|
||||||
|
const activeIndex = ref(0)
|
||||||
|
//分类数据
|
||||||
|
const categoryList = ref<categoryTopItem[]>([])
|
||||||
|
//获取顶级分类数据
|
||||||
|
const fetchTopCategory = async () => {
|
||||||
|
const res = await getCategoryApi()
|
||||||
|
categoryList.value = res.result
|
||||||
|
}
|
||||||
|
//切换一级分类
|
||||||
|
const onChangeCategory = (index: number) => {
|
||||||
|
activeIndex.value = index
|
||||||
|
//重置下标防止错位
|
||||||
|
secondaryTabIndex.value = 0
|
||||||
|
uni.setStorageSync('activeCategoryIndex', index)
|
||||||
|
}
|
||||||
|
//二级所有分类弹窗ref
|
||||||
|
const allSecondaryRef = ref()
|
||||||
|
const showAllSecondary = () => {
|
||||||
|
allSecondaryRef.value.open()
|
||||||
|
}
|
||||||
|
const hiddenAllSecondary = () => {
|
||||||
|
allSecondaryRef.value.close()
|
||||||
|
}
|
||||||
|
//二级分类tab标识
|
||||||
|
const secondaryTabIndex = ref(0)
|
||||||
|
const onChangeSecondary = (index: number) => {
|
||||||
|
secondaryTabIndex.value = index
|
||||||
|
}
|
||||||
|
const selectSecondary = (index: number) => {
|
||||||
|
if (categoryList.value[activeIndex.value].children.length <= 0) return
|
||||||
|
secondaryTabIndex.value = index
|
||||||
|
allSecondaryRef.value.close()
|
||||||
|
}
|
||||||
|
//分类商品列表
|
||||||
|
const paging = ref()
|
||||||
|
const secondaryData = ref()
|
||||||
|
const querySecondary = (page: number, page_size: number) => {
|
||||||
|
if (categoryList.value.length <= 0) return
|
||||||
|
if (categoryList.value[activeIndex.value]?.children.length <= 0) return
|
||||||
|
|
||||||
|
const item = categoryList.value[activeIndex.value].children[secondaryTabIndex.value]
|
||||||
|
if (item?.classify_id === undefined || item.classify_id === '') {
|
||||||
|
console.warn('没有分类ID')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
getClassifyDetailApi({
|
||||||
|
page,
|
||||||
|
page_size,
|
||||||
|
classify_id: item.classify_id,
|
||||||
|
}).then((res) => {
|
||||||
|
paging.value.complete(res.result.data)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const activeTabIndex = computed(() => {
|
||||||
|
return activeIndex.value + '-' + secondaryTabIndex.value
|
||||||
|
})
|
||||||
|
// 监听分类切换
|
||||||
|
watch(activeIndex, () => {
|
||||||
|
secondaryTabIndex.value = 0
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(activeTabIndex, (newVal) => {
|
||||||
|
if (newVal) {
|
||||||
|
nextTick(() => {
|
||||||
|
paging.value?.reload()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
onLoad(() => {
|
||||||
|
uni.setStorageSync('activeCategoryIndex', 0)
|
||||||
|
})
|
||||||
|
|
||||||
|
onShow(() => {
|
||||||
|
activeIndex.value = uni.getStorageSync('activeCategoryIndex') || 0
|
||||||
|
fetchTopCategory()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<view class="viewport">
|
||||||
|
<z-paging
|
||||||
|
:pagingStyle="{
|
||||||
|
backgroud: '#fff',
|
||||||
|
}"
|
||||||
|
empty-view-text="该分类下暂无商品,看看别的分类吧"
|
||||||
|
:empty-view-fixed="false"
|
||||||
|
>
|
||||||
|
<template #top>
|
||||||
|
<shop-goods-search />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #left>
|
||||||
|
<view class="categories">
|
||||||
|
<!-- 左侧:一级分类 -->
|
||||||
|
<scroll-view class="primary" scroll-y>
|
||||||
|
<view
|
||||||
|
v-for="(item, index) in categoryList"
|
||||||
|
:key="index"
|
||||||
|
class="item"
|
||||||
|
@tap="onChangeCategory(index)"
|
||||||
|
:class="{ active: index === activeIndex }"
|
||||||
|
>
|
||||||
|
<text class="name"> {{ item.name }} </text>
|
||||||
|
</view>
|
||||||
|
</scroll-view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<scroll-view class="secondary" scroll-y v-if="categoryList[activeIndex]?.children.length > 0">
|
||||||
|
<z-tabs
|
||||||
|
:list="categoryList[activeIndex].children"
|
||||||
|
@change="onChangeSecondary"
|
||||||
|
:current="secondaryTabIndex"
|
||||||
|
:scroll-count="1"
|
||||||
|
>
|
||||||
|
<template #right>
|
||||||
|
<view class="all" @tap="showAllSecondary">
|
||||||
|
<text class="icon icon-more"></text>
|
||||||
|
<text class="text">全部</text>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
</z-tabs>
|
||||||
|
<!-- 展开全部二级 -->
|
||||||
|
<view class="all-secondary">
|
||||||
|
<uni-drawer ref="allSecondaryRef" mode="right" :mask-click="false">
|
||||||
|
<view class="all-secondary-title">
|
||||||
|
{{ categoryList[activeIndex].name }}
|
||||||
|
</view>
|
||||||
|
<scroll-view style="height: 100%" scroll-y>
|
||||||
|
<view class="seconday-panel">
|
||||||
|
<view
|
||||||
|
v-for="(item, index) in categoryList[activeIndex].children"
|
||||||
|
class="seconday-panel-item"
|
||||||
|
:class="{ active: index === secondaryTabIndex }"
|
||||||
|
:key="index"
|
||||||
|
@tap="selectSecondary(index)"
|
||||||
|
>
|
||||||
|
<view class="title">{{ item.name }}</view>
|
||||||
|
<image class="icon" :src="fullUrl(item.icon)" />
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<button class="seconday-panel-button" type="warn" @tap="hiddenAllSecondary">
|
||||||
|
点击收起
|
||||||
|
</button>
|
||||||
|
</scroll-view>
|
||||||
|
</uni-drawer>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<z-paging
|
||||||
|
ref="paging"
|
||||||
|
v-model="secondaryData"
|
||||||
|
@query="querySecondary"
|
||||||
|
:use-page-scroll="true"
|
||||||
|
:pagingStyle="{
|
||||||
|
backgroud: '#fff',
|
||||||
|
}"
|
||||||
|
empty-view-text="该分类下暂无商品,看看别的分类吧"
|
||||||
|
>
|
||||||
|
<view class="panel">
|
||||||
|
<view class="section" v-for="(item, index) in secondaryData" :key="index">
|
||||||
|
<navigator
|
||||||
|
class="goods"
|
||||||
|
hover-class="none"
|
||||||
|
:url="`${pageUrl['shopping-mall-goods-detail']}?id=${item.id}`"
|
||||||
|
>
|
||||||
|
<view>
|
||||||
|
<image class="image" :src="fullUrl(item.images[0])" />
|
||||||
|
</view>
|
||||||
|
<view class="info">
|
||||||
|
<view class="name ellipsis">{{ item.name }}</view>
|
||||||
|
<view class="price">
|
||||||
|
<view class="origin">
|
||||||
|
<text class="symbol">¥</text>
|
||||||
|
<text class="number">{{ item.max_origin_price }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="now">
|
||||||
|
<text class="symbol">¥</text>
|
||||||
|
<text class="number">{{ item.min_price }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</navigator>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</z-paging>
|
||||||
|
</scroll-view>
|
||||||
|
<z-paging-empty-view v-else empty-view-text="该分类下暂无商品,看看别的分类吧" />
|
||||||
|
</z-paging>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
page {
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.viewport {
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
/* 分类 */
|
||||||
|
.categories {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
/* 一级分类 */
|
||||||
|
.primary {
|
||||||
|
overflow: hidden;
|
||||||
|
width: 180rpx;
|
||||||
|
flex: none;
|
||||||
|
background-color: #f6f6f6;
|
||||||
|
.item {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
height: 96rpx;
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #595c63;
|
||||||
|
position: relative;
|
||||||
|
&::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
left: 42rpx;
|
||||||
|
bottom: 0;
|
||||||
|
width: 96rpx;
|
||||||
|
border-top: 1rpx solid #e3e4e7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.active {
|
||||||
|
background-color: #fff;
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
width: 8rpx;
|
||||||
|
height: 100%;
|
||||||
|
background-color: #002fa8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.primary .item:last-child::after,
|
||||||
|
.primary .active::after {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
/* 二级分类 */
|
||||||
|
.secondary {
|
||||||
|
background-color: #fff;
|
||||||
|
.carousel {
|
||||||
|
height: 200rpx;
|
||||||
|
margin: 0 30rpx 20rpx;
|
||||||
|
border-radius: 4rpx;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.panel {
|
||||||
|
margin: 0 30rpx 0rpx;
|
||||||
|
}
|
||||||
|
.title {
|
||||||
|
height: 60rpx;
|
||||||
|
line-height: 60rpx;
|
||||||
|
color: #333;
|
||||||
|
font-size: 28rpx;
|
||||||
|
|
||||||
|
.more {
|
||||||
|
float: right;
|
||||||
|
padding-left: 20rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.all {
|
||||||
|
color: #939393;
|
||||||
|
.text {
|
||||||
|
font-size: 30rpx;
|
||||||
|
padding: 10rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.all-secondary-title {
|
||||||
|
text-align: center;
|
||||||
|
margin: 40rpx;
|
||||||
|
}
|
||||||
|
.seconday-panel {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
padding-left: 10rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.seconday-panel-item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
min-width: 180rpx;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
border: 5rpx solid #f00;
|
||||||
|
border-radius: 10rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.seconday-panel-item .title {
|
||||||
|
font-size: 28rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.seconday-panel-item .icon {
|
||||||
|
height: 100rpx;
|
||||||
|
width: 100rpx;
|
||||||
|
margin-bottom: 10rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.seconday-panel-button {
|
||||||
|
font-size: 20rpx;
|
||||||
|
width: 40%;
|
||||||
|
margin-top: 50rpx;
|
||||||
|
border-radius: 30rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
padding: 20rpx 0;
|
||||||
|
.goods {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
.image {
|
||||||
|
width: 150rpx;
|
||||||
|
height: 150rpx;
|
||||||
|
margin-right: 10rpx;
|
||||||
|
}
|
||||||
|
.info {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-around;
|
||||||
|
width: 70%;
|
||||||
|
.name {
|
||||||
|
font-size: 24rpx;
|
||||||
|
}
|
||||||
|
.price {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
margin-top: 10rpx;
|
||||||
|
.origin {
|
||||||
|
font-size: 22rpx;
|
||||||
|
margin-right: 10rpx;
|
||||||
|
text-decoration: line-through;
|
||||||
|
}
|
||||||
|
.now {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
.symbol {
|
||||||
|
font-size: 18rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.z-tabs-item-title {
|
||||||
|
font-size: 24rpx !important;
|
||||||
|
}
|
||||||
|
</style>
|
219
src/pagesShop/pages/shopGoods/evaluate/evaluate.vue
Normal file
219
src/pagesShop/pages/shopGoods/evaluate/evaluate.vue
Normal file
@ -0,0 +1,219 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { getOrderEvaluateListApi, getOrderEvaluateTabsApi } from '@/api/order'
|
||||||
|
import { ref } from 'vue'
|
||||||
|
// 必须导入需要用到的页面生命周期(即使在当前页面上没有直接使用到)
|
||||||
|
import { onPageScroll, onReachBottom } from '@dcloudio/uni-app'
|
||||||
|
import useZPaging from '@/uni_modules/z-paging/components/z-paging/js/hooks/useZPaging.js'
|
||||||
|
import type { orderEvaluateListResult } from '@/types/order'
|
||||||
|
import type { TabItem } from '@/types/global'
|
||||||
|
import { fullUrl, onShowRefreshData, previewImg } from '@/utils/common'
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
goods_id: string
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const paging = ref()
|
||||||
|
// 类似mixins,如果是页面滚动务必要写这一行,并传入当前ref绑定的paging,注意此处是paging,而非paging.value
|
||||||
|
useZPaging(paging)
|
||||||
|
|
||||||
|
const tabList = ref<TabItem[]>([])
|
||||||
|
const tabIndex = ref(0)
|
||||||
|
const tabsChange = (index: number) => {
|
||||||
|
tabIndex.value = index
|
||||||
|
paging.value.reload()
|
||||||
|
}
|
||||||
|
|
||||||
|
const dataList = ref<orderEvaluateListResult[]>([])
|
||||||
|
const queryList = async (page: number, page_size: number) => {
|
||||||
|
const res = await getOrderEvaluateListApi({
|
||||||
|
page,
|
||||||
|
page_size,
|
||||||
|
goods_id: props.goods_id,
|
||||||
|
type: tabList.value[tabIndex.value]?.value ?? '',
|
||||||
|
})
|
||||||
|
paging.value.complete(res.result.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
const getTabList = async () => {
|
||||||
|
const res = await getOrderEvaluateTabsApi({
|
||||||
|
goods_id: props.goods_id,
|
||||||
|
})
|
||||||
|
tabList.value = res.result.map((item) => ({
|
||||||
|
...item,
|
||||||
|
disabled: false,
|
||||||
|
badge: { count: item.count },
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
const groupedImages = (imageList: string[]) => {
|
||||||
|
if (imageList.length <= 0) return []
|
||||||
|
|
||||||
|
const groups = []
|
||||||
|
const images = imageList.map((item) => fullUrl(item))
|
||||||
|
|
||||||
|
for (let i = 0; i < images.length; i += 3) {
|
||||||
|
groups.push(images.slice(i, i + 3))
|
||||||
|
}
|
||||||
|
|
||||||
|
return groups
|
||||||
|
}
|
||||||
|
|
||||||
|
onShowRefreshData(() => {
|
||||||
|
getTabList()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<z-paging ref="paging" v-model="dataList" @query="queryList" :safe-area-inset-bottom="true">
|
||||||
|
<template #top>
|
||||||
|
<z-tabs
|
||||||
|
:list="tabList"
|
||||||
|
@change="tabsChange"
|
||||||
|
:current="tabIndex"
|
||||||
|
:badge-style="{ backgroundColor: '#c3c3c3' }"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<view class="evaluate panel" v-for="(item, index) in dataList" :key="index">
|
||||||
|
<view class="top">
|
||||||
|
<shop-user-avatar class="portrait" :url="item.user_avatar" width="70" />
|
||||||
|
<text class="name">{{ item.user_name }}</text>
|
||||||
|
<text class="time">{{ item.create_time }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="rate">
|
||||||
|
<uni-rate :value="item.score" :size="18" readonly />
|
||||||
|
<text class="spec">{{ item.goods_spec }}</text>
|
||||||
|
</view>
|
||||||
|
<text class="content">{{ item.content }}</text>
|
||||||
|
<view class="content-img">
|
||||||
|
<view class="image-group" v-for="(group, index) in groupedImages(item.images)" :key="index">
|
||||||
|
<image
|
||||||
|
v-for="(img, imgIndex) in group"
|
||||||
|
:key="imgIndex"
|
||||||
|
:src="img"
|
||||||
|
mode="aspectFill"
|
||||||
|
@click="previewImg(img, item.images)"
|
||||||
|
:class="{
|
||||||
|
'first-image': imgIndex === 0 && group.length > 1,
|
||||||
|
'last-image': imgIndex === group.length - 1 && group.length > 1,
|
||||||
|
'single-image': group.length === 1,
|
||||||
|
'middle-image': imgIndex !== 0 && imgIndex !== group.length - 1,
|
||||||
|
}"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="bot"> </view>
|
||||||
|
</view>
|
||||||
|
</z-paging>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
page {
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
background-color: #f4f4f4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel {
|
||||||
|
margin-top: 10rpx;
|
||||||
|
background-color: #fff;
|
||||||
|
padding: 20rpx;
|
||||||
|
margin: 20rpx;
|
||||||
|
border-radius: 20rpx;
|
||||||
|
|
||||||
|
.top {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 10rpx;
|
||||||
|
|
||||||
|
.portrait {
|
||||||
|
width: 70rpx;
|
||||||
|
height: 70rpx;
|
||||||
|
border-radius: 50%;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.name {
|
||||||
|
flex: 1;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
font-size: 28rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time {
|
||||||
|
font-size: 24rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.rate {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 10rpx;
|
||||||
|
|
||||||
|
.spec {
|
||||||
|
::before {
|
||||||
|
content: '|';
|
||||||
|
margin: 0 10rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
color: #666;
|
||||||
|
font-size: 20rpx;
|
||||||
|
overflow: hidden;
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: 1;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
display: flex;
|
||||||
|
padding-left: 7rpx;
|
||||||
|
font-size: 26rpx;
|
||||||
|
margin: 20rpx 0rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-img {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
margin-top: 10rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-group {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-group image {
|
||||||
|
height: 200rpx;
|
||||||
|
width: 33%;
|
||||||
|
margin-right: 1.5%;
|
||||||
|
margin-bottom: 10rpx;
|
||||||
|
object-fit: cover;
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-group image.single-image {
|
||||||
|
border-radius: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-group image.first-image {
|
||||||
|
border-top-left-radius: 20rpx;
|
||||||
|
border-bottom-left-radius: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-group image.last-image {
|
||||||
|
border-top-right-radius: 20rpx;
|
||||||
|
border-bottom-right-radius: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-group image.middle-image {
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-group image:last-child {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
162
src/pagesShop/pages/shopGoods/goods/components/addressPanel.vue
Normal file
162
src/pagesShop/pages/shopGoods/goods/components/addressPanel.vue
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { useAddressStore } from '@/stores/modules/address'
|
||||||
|
import type { addressItem } from '@/types/address'
|
||||||
|
import { pageUrl } from '@/utils/constants'
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(event: 'close'): void
|
||||||
|
}>()
|
||||||
|
|
||||||
|
//获取地址信息
|
||||||
|
const props = defineProps<{
|
||||||
|
list?: addressItem[]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const addressStore = useAddressStore()
|
||||||
|
|
||||||
|
//选择收货地址
|
||||||
|
const onSelectAddress = (item: addressItem) => {
|
||||||
|
addressStore.changeSelectedAddress(item)
|
||||||
|
emit('close')
|
||||||
|
}
|
||||||
|
|
||||||
|
const toAddressList = () => {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: `${pageUrl['address-form']}`,
|
||||||
|
})
|
||||||
|
|
||||||
|
emit('close')
|
||||||
|
}
|
||||||
|
|
||||||
|
useAddressStore().changeScene('order')
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<view class="address-panel">
|
||||||
|
<text class="close icon-close" @tap="emit('close')"></text>
|
||||||
|
<view class="title">配送至</view>
|
||||||
|
<view class="content">
|
||||||
|
<view v-if="props.list!.length > 0">
|
||||||
|
<view class="item" v-for="item in props.list" :key="item.id" @tap="onSelectAddress(item)">
|
||||||
|
<view class="user">{{ item.name }} {{ item.phone }}</view>
|
||||||
|
<view class="address">{{ item.full_location }} {{ item.address }}</view>
|
||||||
|
<text
|
||||||
|
class="icon"
|
||||||
|
:class="item.id === addressStore.selectedAddress?.id ? 'icon-checked' : 'icon-check'"
|
||||||
|
></text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view v-else>
|
||||||
|
<z-paging-empty-view
|
||||||
|
empty-view-text="暂无收货地址"
|
||||||
|
:empty-view-style="{
|
||||||
|
top: '50rpx',
|
||||||
|
}"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="footer">
|
||||||
|
<view class="button primary">
|
||||||
|
<view open-type="navigate" hover-class="navigator-hover" @tap="toAddressList">
|
||||||
|
新增收货地址
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.address-panel {
|
||||||
|
padding: 0 30rpx;
|
||||||
|
border-radius: 10rpx 10rpx 0 0;
|
||||||
|
position: relative;
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
line-height: 1;
|
||||||
|
padding: 40rpx 0;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: normal;
|
||||||
|
border-bottom: 1rpx solid #ddd;
|
||||||
|
color: #444;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close {
|
||||||
|
position: absolute;
|
||||||
|
right: 24rpx;
|
||||||
|
top: 24rpx;
|
||||||
|
font-size: 40rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
min-height: 300rpx;
|
||||||
|
max-height: 540rpx;
|
||||||
|
overflow: auto;
|
||||||
|
padding: 20rpx;
|
||||||
|
|
||||||
|
.item {
|
||||||
|
padding: 30rpx 50rpx 30rpx 60rpx;
|
||||||
|
background-size: 40rpx;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: 0 center;
|
||||||
|
background-image: url('~@/static/images/locate.png');
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.icon {
|
||||||
|
color: #999;
|
||||||
|
font-size: 40rpx;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
.icon-checked {
|
||||||
|
color: #ff5f3c;
|
||||||
|
}
|
||||||
|
.icon-ring {
|
||||||
|
color: #444;
|
||||||
|
}
|
||||||
|
.user {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #444;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
.address {
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
.empty-text {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 20rpx 0 40rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #444;
|
||||||
|
|
||||||
|
.button {
|
||||||
|
flex: 1;
|
||||||
|
height: 72rpx;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 72rpx;
|
||||||
|
margin: 0 20rpx;
|
||||||
|
color: #fff;
|
||||||
|
border-radius: 72rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.primary {
|
||||||
|
color: #fff;
|
||||||
|
background-color: #ff5f3c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.secondary {
|
||||||
|
background-color: #ffa868;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -0,0 +1,87 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { goodsServiceItem } from '@/types/goods'
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(event: 'close'): void
|
||||||
|
}>()
|
||||||
|
|
||||||
|
//获取服务与承诺信息
|
||||||
|
const props = defineProps<{
|
||||||
|
list?: goodsServiceItem[]
|
||||||
|
}>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<view class="service-panel">
|
||||||
|
<text class="close icon-close" @tap="emit('close')"></text>
|
||||||
|
<view class="title">服务说明</view>
|
||||||
|
<view class="content">
|
||||||
|
<view class="item" v-for="item in props.list" :key="item.id">
|
||||||
|
<view class="dt">{{ item.name }}</view>
|
||||||
|
<view class="dd">
|
||||||
|
{{ item.content }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.service-panel {
|
||||||
|
padding: 0 30rpx;
|
||||||
|
border-radius: 10rpx 10rpx 0 0;
|
||||||
|
position: relative;
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
line-height: 1;
|
||||||
|
padding: 40rpx 0;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: normal;
|
||||||
|
border-bottom: 1rpx solid #ddd;
|
||||||
|
color: #444;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close {
|
||||||
|
position: absolute;
|
||||||
|
right: 24rpx;
|
||||||
|
top: 24rpx;
|
||||||
|
font-size: 40rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
padding: 20rpx 20rpx 150rpx 20rpx;
|
||||||
|
|
||||||
|
.item {
|
||||||
|
margin-top: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dt {
|
||||||
|
margin-bottom: 10rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #333;
|
||||||
|
font-weight: 500;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
width: 10rpx;
|
||||||
|
height: 10rpx;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: #eaeaea;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: -20rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dd {
|
||||||
|
line-height: 1.6;
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
153
src/pagesShop/pages/shopGoods/goods/components/sharePanel.vue
Normal file
153
src/pagesShop/pages/shopGoods/goods/components/sharePanel.vue
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { goodsListItem } from '@/types/goods'
|
||||||
|
import { getShareUrl } from './sharePoster'
|
||||||
|
|
||||||
|
const emits = defineEmits<{
|
||||||
|
(event: 'close'): void
|
||||||
|
(event: 'showPoster'): void
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
goods: goodsListItem
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const onCpoyLink = () => {
|
||||||
|
const shareUrl = getShareUrl(props.goods.id)
|
||||||
|
|
||||||
|
uni.setClipboardData({
|
||||||
|
data: shareUrl,
|
||||||
|
success: function () {
|
||||||
|
uni.showToast({
|
||||||
|
title: '复制成功',
|
||||||
|
icon: 'none',
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
emits('close')
|
||||||
|
}
|
||||||
|
const onShowSharePoster = () => {
|
||||||
|
emits('close')
|
||||||
|
emits('showPoster')
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template v-if="path == ''">
|
||||||
|
<view class="panel">
|
||||||
|
<view class="content">
|
||||||
|
<!-- #ifdef MP-WEIXIN -->
|
||||||
|
<button class="content-item" open-type="share">
|
||||||
|
<image class="icon" src="/static/icons/wechat.svg" />
|
||||||
|
<view class="text">微信好友</view>
|
||||||
|
</button>
|
||||||
|
<!-- #endif -->
|
||||||
|
<button class="content-item" @tap="onShowSharePoster">
|
||||||
|
<image class="icon" src="/static/icons/poster.svg" />
|
||||||
|
<view class="text">生成海报</view>
|
||||||
|
</button>
|
||||||
|
<button class="content-item" @tap="onCpoyLink">
|
||||||
|
<image class="icon" src="/static/icons/link.svg" />
|
||||||
|
<view class="text">复制链接</view>
|
||||||
|
</button>
|
||||||
|
</view>
|
||||||
|
<view class="footer">
|
||||||
|
<view class="button" @tap="emits('close')"> 取消分享 </view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.panel {
|
||||||
|
padding: 0 30rpx;
|
||||||
|
border-radius: 10rpx 10rpx 0 0;
|
||||||
|
position: relative;
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
line-height: 1;
|
||||||
|
padding: 40rpx 0;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: normal;
|
||||||
|
border-bottom: 1rpx solid #ddd;
|
||||||
|
color: #444;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close {
|
||||||
|
position: absolute;
|
||||||
|
right: 24rpx;
|
||||||
|
top: 24rpx;
|
||||||
|
font-size: 40rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
display: flex;
|
||||||
|
justify-content: cneter;
|
||||||
|
|
||||||
|
.content-item {
|
||||||
|
margin-top: 20rpx;
|
||||||
|
background-color: transparent;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
::after {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
width: 80rpx;
|
||||||
|
height: 80rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text {
|
||||||
|
font-size: 30rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 20rpx 0 20px;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #444;
|
||||||
|
|
||||||
|
.button {
|
||||||
|
flex: 1;
|
||||||
|
height: 72rpx;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 72rpx;
|
||||||
|
margin: 0 20rpx;
|
||||||
|
border-radius: 72rpx;
|
||||||
|
color: #fff;
|
||||||
|
background-color: #ff5f3c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.action {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 20rpx;
|
||||||
|
background: #f5f5f5;
|
||||||
|
|
||||||
|
.button {
|
||||||
|
padding: 16rpx 32rpx;
|
||||||
|
margin: 0 10rpx;
|
||||||
|
|
||||||
|
background: #ff784d;
|
||||||
|
color: #ffffff;
|
||||||
|
|
||||||
|
border-radius: 40rpx;
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.cancel {
|
||||||
|
background: #b3b3b3;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -0,0 +1,9 @@
|
|||||||
|
import { useMemberStore, useSettingStore } from '@/stores'
|
||||||
|
import { pageUrl } from '@/utils/constants'
|
||||||
|
|
||||||
|
export const getShareUrl = (goodsId: number | string) => {
|
||||||
|
const settingData = useSettingStore().data
|
||||||
|
const profileData = useMemberStore().profile
|
||||||
|
|
||||||
|
return `${settingData.h5_domain}${pageUrl['shopping-mall-goods-detail']}?id=${goodsId}&sharer=${profileData?.id}`
|
||||||
|
}
|
231
src/pagesShop/pages/shopGoods/goods/components/sharePoster.vue
Normal file
231
src/pagesShop/pages/shopGoods/goods/components/sharePoster.vue
Normal file
@ -0,0 +1,231 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { useMemberStore } from '@/stores'
|
||||||
|
import type { goodsListItem } from '@/types/goods'
|
||||||
|
import { ref, reactive } from 'vue'
|
||||||
|
import { fullUrl, checkImage, getUserDefaultAvatar } from '@/utils/common'
|
||||||
|
import { getShareInfoApi } from '@/api/goods'
|
||||||
|
import { getShareUrl } from './sharePoster'
|
||||||
|
import { usePopupStore } from '@/stores'
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
goods: goodsListItem
|
||||||
|
currentPrice: string
|
||||||
|
currentOriginPrice: string
|
||||||
|
goodsImage: string
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const sharePosterRef = ref()
|
||||||
|
const painter = ref()
|
||||||
|
|
||||||
|
const state = reactive({
|
||||||
|
posterImgPath: '',
|
||||||
|
showPoster: false,
|
||||||
|
userAvatarImg: useMemberStore().profile!.avatar,
|
||||||
|
qrcodeUrl: '',
|
||||||
|
})
|
||||||
|
|
||||||
|
const userImage = ref('')
|
||||||
|
const open = async () => {
|
||||||
|
userImage.value = fullUrl(state.userAvatarImg)
|
||||||
|
await checkImage(userImage.value)
|
||||||
|
.then((url) => {
|
||||||
|
userImage.value = url
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
userImage.value = getUserDefaultAvatar()
|
||||||
|
})
|
||||||
|
|
||||||
|
// #ifdef MP-WEIXIN
|
||||||
|
const shareInfo = await getShareInfoApi(props.goods.id)
|
||||||
|
state.qrcodeUrl = shareInfo.result.qrcode_url
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
await sharePosterRef.value?.open()
|
||||||
|
state.showPoster = true
|
||||||
|
state.posterImgPath = ''
|
||||||
|
await uni.showLoading({
|
||||||
|
title: '海报生成中…',
|
||||||
|
mask: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const close = () => {
|
||||||
|
sharePosterRef.value.close()
|
||||||
|
|
||||||
|
state.showPoster = false
|
||||||
|
}
|
||||||
|
|
||||||
|
const onDone = () => {}
|
||||||
|
|
||||||
|
const onSuccess = (imgPath: string) => {
|
||||||
|
state.posterImgPath = imgPath
|
||||||
|
uni.hideLoading()
|
||||||
|
}
|
||||||
|
|
||||||
|
const onSave = () => {
|
||||||
|
// #ifdef MP-WEIXIN
|
||||||
|
painter.value.canvasToTempFilePathSync({
|
||||||
|
fileType: 'jpg',
|
||||||
|
pathType: 'url',
|
||||||
|
quality: 1,
|
||||||
|
success: (res: any) => {
|
||||||
|
console.log(res.tempFilePath)
|
||||||
|
uni.saveImageToPhotosAlbum({
|
||||||
|
filePath: res.tempFilePath,
|
||||||
|
success: () => {
|
||||||
|
uni.showToast({
|
||||||
|
title: '保存成功',
|
||||||
|
icon: 'none',
|
||||||
|
})
|
||||||
|
},
|
||||||
|
fail: (err) => {
|
||||||
|
console.log(err)
|
||||||
|
if (err.errMsg == 'saveImageToPhotosAlbum:fail cancel') {
|
||||||
|
return uni.showToast({
|
||||||
|
title: '取消保存',
|
||||||
|
icon: 'none',
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
uni.showToast({
|
||||||
|
title: '保存失败',
|
||||||
|
icon: 'none',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
complete: () => {
|
||||||
|
close()
|
||||||
|
},
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
// #endif
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({ sharePosterRef, open, close })
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<uni-popup
|
||||||
|
ref="sharePosterRef"
|
||||||
|
type="center"
|
||||||
|
:animation="false"
|
||||||
|
:mask-click="false"
|
||||||
|
@change="usePopupStore().onChangePopupProps"
|
||||||
|
>
|
||||||
|
<image :src="state.posterImgPath" mode="widthFix" style="width: 600rpx" />
|
||||||
|
<view>
|
||||||
|
<view class="poster">
|
||||||
|
<l-painter
|
||||||
|
ref="painter"
|
||||||
|
isCanvasToTempFilePath
|
||||||
|
@success="onSuccess"
|
||||||
|
css="width: 750rpx; padding-bottom: 40rpx; background: #edca88; object-fit: contain;"
|
||||||
|
hidden
|
||||||
|
@done="onDone"
|
||||||
|
:after-delay="500"
|
||||||
|
>
|
||||||
|
<l-painter-image
|
||||||
|
:src="userImage"
|
||||||
|
css="margin-left: 40rpx; margin-top: 40rpx; width: 100rpx; height: 100rpx; border-radius: 50%;"
|
||||||
|
/>
|
||||||
|
<l-painter-view css="margin-top: 40rpx; padding-left: 20rpx; display: inline-block">
|
||||||
|
<l-painter-text
|
||||||
|
:text="useMemberStore().profile?.nickname || ''"
|
||||||
|
css="display: block; padding-bottom: 20rpx; color: #000; font-size: 32rpx; fontWeight: bold"
|
||||||
|
/>
|
||||||
|
<l-painter-text text="推荐了一个好物给您" css="color: #000; font-size: 24rpx" />
|
||||||
|
</l-painter-view>
|
||||||
|
<l-painter-view
|
||||||
|
css="margin-left: 40rpx; margin-top: 30rpx; padding: 32rpx; box-sizing: border-box; background: #fff; border-radius: 16rpx; width: 670rpx; box-shadow: 0 20rpx 58rpx rgba(0,0,0,.15)"
|
||||||
|
>
|
||||||
|
<l-painter-image
|
||||||
|
:src="fullUrl(props.goodsImage)"
|
||||||
|
css="object-fit: fill; width: 600rpx; max-height: 550rpx; border-radius: 12rpx;"
|
||||||
|
/>
|
||||||
|
<l-painter-view
|
||||||
|
css="margin-top: 32rpx; color: #FF0000; font-weight: bold; font-size: 28rpx; line-height: 1em;"
|
||||||
|
>
|
||||||
|
<l-painter-text text="¥" css="vertical-align: bottom" />
|
||||||
|
<l-painter-text
|
||||||
|
:text="`${props.currentPrice.split('.')[0]}`"
|
||||||
|
css="vertical-align: bottom; font-size: 58rpx"
|
||||||
|
/>
|
||||||
|
<l-painter-text
|
||||||
|
:text="`.${props.currentPrice.split('.')[1]}`"
|
||||||
|
css="vertical-align: bottom"
|
||||||
|
/>
|
||||||
|
<l-painter-text
|
||||||
|
:text="`¥${props.currentOriginPrice}`"
|
||||||
|
css="vertical-align: bottom; padding-left: 10rpx; font-weight: normal; text-decoration: line-through; color: #999999"
|
||||||
|
/>
|
||||||
|
</l-painter-view>
|
||||||
|
<l-painter-view css="margin-top: 30rpx">
|
||||||
|
<l-painter-text
|
||||||
|
:text="props.goods.name"
|
||||||
|
css="line-clamp: 2; color: #333333; line-height: 1.5em; width: 620rpx; font-size: 30rpx; padding-right:32rpx; box-sizing: border-box"
|
||||||
|
></l-painter-text>
|
||||||
|
</l-painter-view>
|
||||||
|
<l-painter-view css="margin-top: 30rpx; display: flex; flex-wrap: nowrap;">
|
||||||
|
<l-painter-text
|
||||||
|
text="长按或扫一扫识别二维码"
|
||||||
|
css="float: left; padding-bottom: 10rpx; color: #999999; font-size: 26rpx; margin-top: 60rpx; margin-right: 160rpx"
|
||||||
|
/>
|
||||||
|
<!-- #ifdef MP-WEIXIN -->
|
||||||
|
<l-painter-image
|
||||||
|
v-if="state.qrcodeUrl"
|
||||||
|
:src="state.qrcodeUrl"
|
||||||
|
css="float: right; width: 150rpx; height: 150rpx;"
|
||||||
|
/>
|
||||||
|
<!-- #endif -->
|
||||||
|
<!-- #ifdef WEB -->
|
||||||
|
<l-painter-qrcode
|
||||||
|
:text="getShareUrl(props.goods.id)"
|
||||||
|
css="float: right; width: 150rpx; height: 150rpx;"
|
||||||
|
/>
|
||||||
|
<!-- #endif -->
|
||||||
|
</l-painter-view>
|
||||||
|
</l-painter-view>
|
||||||
|
</l-painter>
|
||||||
|
</view>
|
||||||
|
<view class="action" v-if="state.posterImgPath">
|
||||||
|
<view class="button cancel" @tap="close()">取消分享</view>
|
||||||
|
<!-- #ifdef WEB -->
|
||||||
|
<view>长按图片进行保存</view>
|
||||||
|
<!-- #endif -->
|
||||||
|
<!-- #ifdef MP-WEIXIN -->
|
||||||
|
<view class="button" @tap="onSave()">保存图片</view>
|
||||||
|
<!-- #endif -->
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</uni-popup>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.action {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 20rpx;
|
||||||
|
background: #f5f5f5;
|
||||||
|
|
||||||
|
.button {
|
||||||
|
padding: 16rpx 32rpx;
|
||||||
|
margin: 0 10rpx;
|
||||||
|
|
||||||
|
background: #ff784d;
|
||||||
|
color: #ffffff;
|
||||||
|
|
||||||
|
border-radius: 40rpx;
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.cancel {
|
||||||
|
background: #b3b3b3;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
864
src/pagesShop/pages/shopGoods/goods/goods.vue
Normal file
864
src/pagesShop/pages/shopGoods/goods/goods.vue
Normal file
@ -0,0 +1,864 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { getGoodsByIdApi } from '@/api/goods'
|
||||||
|
import { onLoad } from '@dcloudio/uni-app'
|
||||||
|
import { ref, watchEffect, reactive, computed } from 'vue'
|
||||||
|
import type { goodsResult, goodsServiceItem, skuItem } from '@/types/goods'
|
||||||
|
import type {
|
||||||
|
SkuPopupEvent,
|
||||||
|
SkuPopupInstance,
|
||||||
|
SkuPopupLocaldata,
|
||||||
|
} from '@/components/vk-data-goods-sku-popup/vk-data-goods-sku-popup'
|
||||||
|
import { addCartApi } from '@/api/cart'
|
||||||
|
import { useAddressStore } from '@/stores/modules/address'
|
||||||
|
import { previewImg, fullUrl, arrayFullUrl, onShowRefreshData } from '@/utils/common'
|
||||||
|
import { pageUrl, safeBottom, pageMateStyle, isIOSWithHomeIndicator } from '@/utils/constants'
|
||||||
|
import { useCartStore } from '@/stores'
|
||||||
|
import { postAddFavoriteApi, postCancelFavoriteApi } from '@/api/favorite'
|
||||||
|
import _, { debounce } from 'lodash'
|
||||||
|
import { onShareAppMessage } from '@dcloudio/uni-app'
|
||||||
|
import { useMemberStore } from '@/stores'
|
||||||
|
import { getAddressApi } from '@/api/address'
|
||||||
|
import type { addressItem } from '@/types/address'
|
||||||
|
import { computeConversion } from '@/utils/common'
|
||||||
|
import servicePanel from './components/servicePanel.vue'
|
||||||
|
import addressPanel from './components/addressPanel.vue'
|
||||||
|
import sharePanel from './components/sharePanel.vue'
|
||||||
|
import sharePoster from './components/sharePoster.vue'
|
||||||
|
import { usePopupStore } from '@/stores'
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
id: string | number
|
||||||
|
scene?: string
|
||||||
|
}>()
|
||||||
|
|
||||||
|
//接收参数
|
||||||
|
const query = reactive({
|
||||||
|
id: props.id,
|
||||||
|
scene: props.scene,
|
||||||
|
})
|
||||||
|
|
||||||
|
const cartStore = useCartStore()
|
||||||
|
|
||||||
|
//商品sku弹窗
|
||||||
|
const isShowSku = ref(false)
|
||||||
|
//商品sku数据
|
||||||
|
const skuData = ref({} as SkuPopupLocaldata)
|
||||||
|
//sku按钮模式
|
||||||
|
enum skuskuModeType {
|
||||||
|
both = 1,
|
||||||
|
cart = 2,
|
||||||
|
buy = 3,
|
||||||
|
}
|
||||||
|
const skuMode = ref<skuskuModeType>(skuskuModeType.both)
|
||||||
|
//sku弹窗ref实例
|
||||||
|
const skuPopupRef = ref<SkuPopupInstance>()
|
||||||
|
//sku中被选中的值
|
||||||
|
const selectAttrText = computed(() => {
|
||||||
|
return skuPopupRef.value?.selectArr?.join(' ').trim() || '请选择商品规格'
|
||||||
|
})
|
||||||
|
// 计算当前显示的价格
|
||||||
|
const selectedPrice = ref(0)
|
||||||
|
const currentPrice = computed(() => selectedPrice.value || goods.value?.min_price || 0)
|
||||||
|
// 计算当前显示的划线价
|
||||||
|
const selectedOriginPrice = ref(0)
|
||||||
|
const currentOriginPrice = computed(
|
||||||
|
() => selectedOriginPrice.value || goods.value?.max_origin_price || 0,
|
||||||
|
)
|
||||||
|
|
||||||
|
//打开sku弹窗
|
||||||
|
const openSkuPopup = (mode: skuskuModeType) => {
|
||||||
|
isShowSku.value = true
|
||||||
|
skuMode.value = mode
|
||||||
|
}
|
||||||
|
|
||||||
|
const goodsServiceList = ref<goodsServiceItem[]>([])
|
||||||
|
const goodsServiceText = ref('')
|
||||||
|
//商品详情
|
||||||
|
const goods = ref<goodsResult>()
|
||||||
|
const getGoodsDataById = async () => {
|
||||||
|
const res = await getGoodsByIdApi(Number(query.id))
|
||||||
|
goods.value = res.result
|
||||||
|
|
||||||
|
//设置sku组件所需数据
|
||||||
|
skuData.value = {
|
||||||
|
_id: res.result.id.toString(),
|
||||||
|
name: res.result.name,
|
||||||
|
goods_thumb: fullUrl(res.result.images[0]),
|
||||||
|
spec_list: res.result.spec_list.map((spec) => {
|
||||||
|
return {
|
||||||
|
name: spec.name,
|
||||||
|
list: spec.value,
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
sku_list: res.result.sku_list.map((sku) => {
|
||||||
|
return {
|
||||||
|
_id: sku.id.toString(),
|
||||||
|
goods_id: res.result.id.toString(),
|
||||||
|
goods_name: res.result.name,
|
||||||
|
image: fullUrl(sku.image),
|
||||||
|
price: sku.price,
|
||||||
|
origin_price: sku.origin_price,
|
||||||
|
sku_name_arr: sku.spec?.map((sku_name) => sku_name.value),
|
||||||
|
stock: sku.stock,
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
//服务与承诺
|
||||||
|
goodsServiceList.value = goods.value.goods_service
|
||||||
|
goodsServiceText.value = goods.value.goods_service.map((item) => item.name).join(' ')
|
||||||
|
}
|
||||||
|
|
||||||
|
//更新轮播图下标
|
||||||
|
const currentIndex = ref(0)
|
||||||
|
const updateIndex: UniHelper.SwiperOnChange = (event) => {
|
||||||
|
currentIndex.value = event.detail.current
|
||||||
|
}
|
||||||
|
//弹出层ref
|
||||||
|
const popup = ref<{
|
||||||
|
open: (type?: UniHelper.UniPopupType) => void
|
||||||
|
close: () => void
|
||||||
|
}>()
|
||||||
|
//调用弹出层
|
||||||
|
const popupName = ref<'address' | 'service' | 'share' | 'sharePoster'>()
|
||||||
|
const openPopup = (name: typeof popupName.value) => {
|
||||||
|
popupName.value = name
|
||||||
|
popup.value?.open()
|
||||||
|
}
|
||||||
|
|
||||||
|
//添加购物车
|
||||||
|
const onAddCart = (event: SkuPopupEvent) => {
|
||||||
|
addCartApi({
|
||||||
|
sku_id: event._id,
|
||||||
|
num: event.buy_num,
|
||||||
|
}).then((res) => {
|
||||||
|
cartStore.setCartTotalNum(res.result.total_num)
|
||||||
|
cartInfo.value = res.result.total_num
|
||||||
|
isShowSku.value = false
|
||||||
|
uni.showToast({
|
||||||
|
title: '加入购物车成功',
|
||||||
|
icon: 'success',
|
||||||
|
mask: true,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
//跳转下单页
|
||||||
|
const onBuyNow = (event: SkuPopupEvent) => {
|
||||||
|
console.log(event)
|
||||||
|
isShowSku.value = false
|
||||||
|
uni.navigateTo({
|
||||||
|
url: `${pageUrl['order-create']}?sku_id=${event._id}&count=${event.buy_num}`,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
//规格选择后处理
|
||||||
|
const onSpecSelected = (skuData: skuItem) => {
|
||||||
|
selectedPrice.value = skuData.price
|
||||||
|
selectedOriginPrice.value = skuData.origin_price
|
||||||
|
}
|
||||||
|
|
||||||
|
// 收货地址
|
||||||
|
const addressStore = useAddressStore()
|
||||||
|
const selectAddressText = computed(() => {
|
||||||
|
return addressStore.selectedAddress
|
||||||
|
? addressStore.selectedAddress.full_location
|
||||||
|
: '请选择收货地址'
|
||||||
|
})
|
||||||
|
|
||||||
|
const addressList = ref<addressItem[]>([])
|
||||||
|
const openAddressPopup = () => {
|
||||||
|
getAddressApi().then((res) => {
|
||||||
|
addressList.value = res.result.data
|
||||||
|
openPopup('address')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const getMoreEvaluate = () => {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: `${pageUrl['shopping-mall-goods-evaluate']}?goods_id=${query.id}`,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const cartInfo = ref(0)
|
||||||
|
const menuButtons = computed<UniHelper.UniGoodsNavOption[]>(() => [
|
||||||
|
{
|
||||||
|
icon: 'shop',
|
||||||
|
text: '商城',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: 'cart',
|
||||||
|
text: '购物车',
|
||||||
|
info: cartInfo.value,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: 'redo',
|
||||||
|
text: '分享',
|
||||||
|
},
|
||||||
|
])
|
||||||
|
const menuClick = (event: UniHelper.UniGoodsNavOnClickEvent) => {
|
||||||
|
switch (event.index) {
|
||||||
|
case 0:
|
||||||
|
uni.redirectTo({
|
||||||
|
url: `${pageUrl['shopping-mall']}`,
|
||||||
|
})
|
||||||
|
break
|
||||||
|
case 1:
|
||||||
|
uni.redirectTo({
|
||||||
|
url: `${pageUrl['shopping-mall-cart']}`,
|
||||||
|
})
|
||||||
|
break
|
||||||
|
case 2:
|
||||||
|
showSharePopup()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const operateButtons: UniHelper.UniGoodsNavButton[] = [
|
||||||
|
{
|
||||||
|
text: '加入购物车',
|
||||||
|
backgroundColor: '#ffa200',
|
||||||
|
color: '#fff',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: '立即购买',
|
||||||
|
backgroundColor: '#ff0000',
|
||||||
|
color: '#fff',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
const buttonClick = (event: UniHelper.UniGoodsNavOnButtonClickEvent) => {
|
||||||
|
switch (event.index) {
|
||||||
|
case 0:
|
||||||
|
openSkuPopup(skuskuModeType.cart)
|
||||||
|
break
|
||||||
|
case 1:
|
||||||
|
openSkuPopup(skuskuModeType.buy)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const changeFavoriteStatus = debounce((goods) => {
|
||||||
|
if (goods.favorite === true) {
|
||||||
|
postCancelFavoriteApi({
|
||||||
|
goods_id: goods.id,
|
||||||
|
}).then((res) => {
|
||||||
|
if (res.result) {
|
||||||
|
goods.favorite = false
|
||||||
|
uni.showToast({
|
||||||
|
title: '商品取消收藏',
|
||||||
|
icon: 'none',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
postAddFavoriteApi({
|
||||||
|
goods_id: goods.id,
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
if (res.result) {
|
||||||
|
goods.favorite = true
|
||||||
|
uni.showToast({
|
||||||
|
title: '商品收藏成功',
|
||||||
|
icon: 'none',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.log(err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, 500)
|
||||||
|
|
||||||
|
watchEffect(() => {
|
||||||
|
cartInfo.value = useCartStore().cartTotalNum
|
||||||
|
})
|
||||||
|
|
||||||
|
const sharePopupRef = ref()
|
||||||
|
// 分享弹窗
|
||||||
|
const showSharePopup = () => {
|
||||||
|
if (!useMemberStore().profile?.id) {
|
||||||
|
return uni.showModal({
|
||||||
|
title: '温馨提示',
|
||||||
|
content: '登录解锁更多精彩,是否继续?',
|
||||||
|
confirmText: '去登录',
|
||||||
|
cancelText: '再看看',
|
||||||
|
success: function (res) {
|
||||||
|
if (res.confirm) {
|
||||||
|
uni.navigateTo({ url: pageUrl['login'] })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
openPopup('share')
|
||||||
|
}
|
||||||
|
|
||||||
|
const sharePosterRef = ref()
|
||||||
|
const goodsImage = ref('')
|
||||||
|
const onShowPoster = () => {
|
||||||
|
sharePosterRef.value.open()
|
||||||
|
goodsImage.value = goods.value!.images[currentIndex.value] || goods.value!.images[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
onShareAppMessage(() => {
|
||||||
|
sharePopupRef.value.close()
|
||||||
|
|
||||||
|
const sharePath = `${pageUrl['shopping-mall-goods-detail']}?id=${goods.value?.id}&sharer=${
|
||||||
|
useMemberStore().profile?.id
|
||||||
|
}`
|
||||||
|
console.log(sharePath)
|
||||||
|
|
||||||
|
return {
|
||||||
|
title: goods.value?.name,
|
||||||
|
path: sharePath,
|
||||||
|
imageUrl: fullUrl(goods.value!.images[0]),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
onShowRefreshData(() => {
|
||||||
|
if (props.scene) {
|
||||||
|
const params = decodeURIComponent(props.scene)
|
||||||
|
.split('&')
|
||||||
|
.reduce((acc: { [key: string]: any }, curr) => {
|
||||||
|
const [key, value] = curr.split('=')
|
||||||
|
acc[key] = value
|
||||||
|
return acc
|
||||||
|
}, {})
|
||||||
|
console.log('解析scene', params)
|
||||||
|
if (params.id) {
|
||||||
|
query.id = params.id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getGoodsDataById()
|
||||||
|
})
|
||||||
|
|
||||||
|
onLoad(() => {
|
||||||
|
// #ifdef WEB
|
||||||
|
// 兼容带有底部导航的IOS设备
|
||||||
|
if (isIOSWithHomeIndicator()) {
|
||||||
|
document.documentElement.classList.add('ios-device')
|
||||||
|
}
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
// #ifdef MP-WEIXIN
|
||||||
|
uni.showShareMenu({
|
||||||
|
withShareTicket: true,
|
||||||
|
menus: ['shareAppMessage'],
|
||||||
|
})
|
||||||
|
// #endif
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<!-- #ifdef MP-WEIXIN -->
|
||||||
|
<page-meta :page-style="pageMateStyle">
|
||||||
|
<!-- #endif -->
|
||||||
|
<scroll-view scroll-y class="viewport" :style="safeBottom" v-if="goods">
|
||||||
|
<!-- 基本信息 -->
|
||||||
|
<view class="goods">
|
||||||
|
<!-- 商品主图 -->
|
||||||
|
<view class="preview">
|
||||||
|
<swiper circular @change="updateIndex" autoplay>
|
||||||
|
<swiper-item v-for="item in goods?.images" :key="item">
|
||||||
|
<image
|
||||||
|
class="image"
|
||||||
|
:src="fullUrl(item)"
|
||||||
|
@tap="previewImg(item, arrayFullUrl(goods!.images))"
|
||||||
|
/>
|
||||||
|
</swiper-item>
|
||||||
|
</swiper>
|
||||||
|
<view class="indicator">
|
||||||
|
<text class="current">{{ currentIndex + 1 }}</text>
|
||||||
|
<text class="split">/</text>
|
||||||
|
<text class="total">{{ goods?.images.length }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="panel">
|
||||||
|
<view class="meta">
|
||||||
|
<view class="meta-top">
|
||||||
|
<view class="price">
|
||||||
|
<view class="origin">¥{{ currentOriginPrice }} </view>
|
||||||
|
<view class="current">
|
||||||
|
<text class="symbol">¥</text>
|
||||||
|
<text class="number">{{ currentPrice }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="operate">
|
||||||
|
<view class="operate-item" @tap="changeFavoriteStatus(goods!)" v-if="goods.favorite">
|
||||||
|
<button class="operate-item-btn">
|
||||||
|
<text class="icon icon-favorite-checked" />
|
||||||
|
</button>
|
||||||
|
<text class="icon-text">已收藏</text>
|
||||||
|
</view>
|
||||||
|
<view class="operate-item" @tap="changeFavoriteStatus(goods!)" v-else>
|
||||||
|
<button class="operate-item-btn">
|
||||||
|
<text class="icon icon-favorite-default" />
|
||||||
|
</button>
|
||||||
|
<text class="icon-text">收藏</text>
|
||||||
|
</view>
|
||||||
|
<!-- #ifdef MP-WEIXIN -->
|
||||||
|
<view class="operate-item">
|
||||||
|
<button open-type="contact" class="operate-item-btn">
|
||||||
|
<text class="icon-handset"></text>
|
||||||
|
</button>
|
||||||
|
<view class="icon-text">
|
||||||
|
<text>客服</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<!-- #endif -->
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="name-box">
|
||||||
|
<view class="name ellipsis">{{ goods?.name }} </view>
|
||||||
|
<view class="sales">
|
||||||
|
已售{{ computeConversion(goods.sales_init + goods.sales_real) }}</view
|
||||||
|
>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="panel">
|
||||||
|
<view class="action">
|
||||||
|
<view class="item arrow" @tap="openSkuPopup(skuskuModeType.both)">
|
||||||
|
<text class="label">已选</text>
|
||||||
|
<text class="text ellipsis">{{ selectAttrText }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="item arrow" @tap="openAddressPopup">
|
||||||
|
<text class="label">送至</text>
|
||||||
|
<text class="text ellipsis">{{ selectAddressText }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="item arrow" @tap="openPopup('service')">
|
||||||
|
<text class="label">服务</text>
|
||||||
|
<text class="text ellipsis">{{ goodsServiceText }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<!-- 弹出层 -->
|
||||||
|
<uni-popup
|
||||||
|
ref="popup"
|
||||||
|
type="bottom"
|
||||||
|
background-color="#fff"
|
||||||
|
@change="usePopupStore().onChangePopupProps"
|
||||||
|
>
|
||||||
|
<address-panel
|
||||||
|
v-if="popupName === 'address'"
|
||||||
|
@close="popup?.close()"
|
||||||
|
:list="addressList"
|
||||||
|
/>
|
||||||
|
<service-panel
|
||||||
|
v-if="popupName === 'service'"
|
||||||
|
:list="goodsServiceList"
|
||||||
|
@close="popup?.close()"
|
||||||
|
/>
|
||||||
|
<share-panel
|
||||||
|
v-if="popupName === 'share'"
|
||||||
|
@close="popup?.close()"
|
||||||
|
@showPoster="onShowPoster"
|
||||||
|
:goods="goods"
|
||||||
|
/>
|
||||||
|
</uni-popup>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 商品评价 -->
|
||||||
|
<view class="panel">
|
||||||
|
<view v-if="goods?.evaluate_count > 0" @click="getMoreEvaluate">
|
||||||
|
<uni-section :title="`评价 (${computeConversion(goods?.evaluate_count)})`" type="line">
|
||||||
|
<template #right>
|
||||||
|
<view class="tip">
|
||||||
|
<text> 好评率 {{ goods?.positive_rate }}%</text>
|
||||||
|
<uni-icons type="forward" size="14" color="#909399" class="icon"></uni-icons>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
<view class="eva-box" v-for="(evaluate, index) in goods?.evaluate" :key="index">
|
||||||
|
<view class="top">
|
||||||
|
<shop-user-avatar
|
||||||
|
:url="evaluate.user_avatar"
|
||||||
|
width="50"
|
||||||
|
style="padding-right: 10rpx"
|
||||||
|
/>
|
||||||
|
<text class="name">{{ evaluate.user_name }}</text>
|
||||||
|
<view class="rate">
|
||||||
|
<uni-rate :value="evaluate.score" :size="20" readonly />
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<text class="content">{{ evaluate.content }}</text>
|
||||||
|
<scroll-view class="scroll-view-container" :scroll-x="true">
|
||||||
|
<image
|
||||||
|
class="content-img"
|
||||||
|
mode="aspectFill"
|
||||||
|
v-for="(item, index) in arrayFullUrl(evaluate.images)"
|
||||||
|
:key="index"
|
||||||
|
:src="item"
|
||||||
|
@click.stop="previewImg(item, evaluate.images)"
|
||||||
|
/>
|
||||||
|
</scroll-view>
|
||||||
|
<view class="bot"> </view>
|
||||||
|
</view>
|
||||||
|
</uni-section>
|
||||||
|
</view>
|
||||||
|
<view v-else>
|
||||||
|
<uni-section title="评价 (0)" type="line">
|
||||||
|
<z-paging-empty-view empty-view-text="期待您的评价" :empty-view-fixed="false" />
|
||||||
|
</uni-section>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="panel">
|
||||||
|
<uni-section title="详情" type="line">
|
||||||
|
<view class="properties">
|
||||||
|
<view class="item" v-for="item in goods?.params" :key="item.key">
|
||||||
|
<text class="label">{{ item.key }}</text>
|
||||||
|
<text class="value">{{ item.value }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</uni-section>
|
||||||
|
<view v-if="goods.detail_images.length > 0">
|
||||||
|
<shop-image :src="goods?.detail_images" />
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view :style="{ paddingBottom: '5rpx' }"> </view>
|
||||||
|
|
||||||
|
<!-- sku弹窗 -->
|
||||||
|
<vk-data-goods-sku-popup
|
||||||
|
v-model="isShowSku"
|
||||||
|
:localdata="skuData"
|
||||||
|
:mode="skuMode"
|
||||||
|
buy-now-background-color="#ff0000"
|
||||||
|
add-cart-background-color="#ffa200"
|
||||||
|
:amountType="0"
|
||||||
|
ref="skuPopupRef"
|
||||||
|
:actived-style="{
|
||||||
|
color: '#ff0000',
|
||||||
|
backgroundColor: '#fee8e6',
|
||||||
|
borderColor: '#ff0000',
|
||||||
|
}"
|
||||||
|
@add-cart="onAddCart"
|
||||||
|
@buy-now="onBuyNow"
|
||||||
|
@spec-selected="onSpecSelected"
|
||||||
|
class="sku-popup"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- 海报弹窗 -->
|
||||||
|
<sharePoster
|
||||||
|
ref="sharePosterRef"
|
||||||
|
:goods="goods"
|
||||||
|
:currentPrice="currentPrice"
|
||||||
|
:currentOriginPrice="currentOriginPrice"
|
||||||
|
:goodsImage="goodsImage"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- 用户操作 -->
|
||||||
|
<view class="goods-nav" :style="safeBottom" v-show="goods">
|
||||||
|
<uni-goods-nav
|
||||||
|
:fill="true"
|
||||||
|
:options="menuButtons"
|
||||||
|
:buttonGroup="operateButtons"
|
||||||
|
@click="menuClick"
|
||||||
|
@buttonClick="buttonClick"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
</scroll-view>
|
||||||
|
<!-- #ifdef MP-WEIXIN -->
|
||||||
|
</page-meta>
|
||||||
|
<!-- #endif -->
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
swiper {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
page {
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.viewport {
|
||||||
|
background-color: #f4f4f4;
|
||||||
|
z-index: 999;
|
||||||
|
margin-bottom: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel {
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 10rpx;
|
||||||
|
margin: 15rpx 15rpx 20rpx;
|
||||||
|
padding: 10rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.arrow {
|
||||||
|
&::after {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
right: 30rpx;
|
||||||
|
content: '\e6c2';
|
||||||
|
color: #ccc;
|
||||||
|
font-family: 'iconfont' !important;
|
||||||
|
font-size: 32rpx;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 商品信息 */
|
||||||
|
.goods {
|
||||||
|
background-color: #fff;
|
||||||
|
margin: 15rpx 15rpx 20rpx;
|
||||||
|
|
||||||
|
.preview {
|
||||||
|
height: 750rpx;
|
||||||
|
position: relative;
|
||||||
|
.image {
|
||||||
|
width: 750rpx;
|
||||||
|
height: 750rpx;
|
||||||
|
}
|
||||||
|
.indicator {
|
||||||
|
height: 40rpx;
|
||||||
|
padding: 0 24rpx;
|
||||||
|
line-height: 40rpx;
|
||||||
|
border-radius: 30rpx;
|
||||||
|
color: #fff;
|
||||||
|
font-family: Arial, Helvetica, sans-serif;
|
||||||
|
background-color: rgba(0, 0, 0, 0.3);
|
||||||
|
position: absolute;
|
||||||
|
bottom: 30rpx;
|
||||||
|
right: 30rpx;
|
||||||
|
.current {
|
||||||
|
font-size: 26rpx;
|
||||||
|
}
|
||||||
|
.split {
|
||||||
|
font-size: 24rpx;
|
||||||
|
margin: 0 1rpx 0 2rpx;
|
||||||
|
}
|
||||||
|
.total {
|
||||||
|
font-size: 24rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.meta {
|
||||||
|
.meta-top {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.operate {
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
.operate-item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.operate-item-btn {
|
||||||
|
width: 80rpx;
|
||||||
|
height: 80rpx;
|
||||||
|
background-color: transparent;
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-text {
|
||||||
|
font-size: 20rpx;
|
||||||
|
color: #9a9a9a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-favorite-checked {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.price {
|
||||||
|
height: 130rpx;
|
||||||
|
padding: 0rpx 20rpx;
|
||||||
|
color: red;
|
||||||
|
font-size: 34rpx;
|
||||||
|
box-sizing: border-box;
|
||||||
|
background-color: #fff;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.origin {
|
||||||
|
margin-top: 15rpx;
|
||||||
|
color: #888;
|
||||||
|
text-decoration: line-through;
|
||||||
|
|
||||||
|
::after {
|
||||||
|
content: ¥;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.number {
|
||||||
|
font-size: 50rpx;
|
||||||
|
}
|
||||||
|
.brand {
|
||||||
|
width: 160rpx;
|
||||||
|
height: 80rpx;
|
||||||
|
overflow: hidden;
|
||||||
|
position: absolute;
|
||||||
|
top: 26rpx;
|
||||||
|
right: 30rpx;
|
||||||
|
}
|
||||||
|
.name {
|
||||||
|
max-height: 88rpx;
|
||||||
|
line-height: 1.4;
|
||||||
|
margin: 20rpx;
|
||||||
|
font-size: 32rpx;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
.desc {
|
||||||
|
line-height: 1;
|
||||||
|
padding: 0 20rpx 30rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #cf4444;
|
||||||
|
}
|
||||||
|
.sales {
|
||||||
|
margin: 20rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #888;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.action {
|
||||||
|
padding-left: 10rpx;
|
||||||
|
background: #fff;
|
||||||
|
|
||||||
|
.item {
|
||||||
|
height: 90rpx;
|
||||||
|
padding-right: 60rpx;
|
||||||
|
border-bottom: 1rpx solid #eaeaea;
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #333;
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
&:last-child {
|
||||||
|
border-bottom: 0 none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.label {
|
||||||
|
width: 60rpx;
|
||||||
|
color: #898b94;
|
||||||
|
margin: 0 16rpx 0 10rpx;
|
||||||
|
}
|
||||||
|
.text {
|
||||||
|
flex: 1;
|
||||||
|
-webkit-line-clamp: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tip {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #9a9a9a;
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
padding-left: 10rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.eva-box {
|
||||||
|
padding: 10rpx;
|
||||||
|
|
||||||
|
.top {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding-bottom: 20rpx;
|
||||||
|
|
||||||
|
.name {
|
||||||
|
flex: 1;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
font-size: 26rpx;
|
||||||
|
padding-bottom: 10rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.rate {
|
||||||
|
margin-right: 10rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
margin: 0rpx 10rpx 10rpx;
|
||||||
|
max-height: 100rpx;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: 2;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
font-size: 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scroll-view-container {
|
||||||
|
width: 100%;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow-x: scroll;
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
|
||||||
|
.content-img {
|
||||||
|
margin-left: 10rpx;
|
||||||
|
height: 150rpx;
|
||||||
|
width: 150rpx;
|
||||||
|
margin-bottom: 10rpx;
|
||||||
|
margin-right: 10rpx;
|
||||||
|
border-radius: 10rpx;
|
||||||
|
margin-top: 10rpx;
|
||||||
|
scroll-snap-align: start;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.properties {
|
||||||
|
padding: 0 20rpx;
|
||||||
|
|
||||||
|
.item {
|
||||||
|
display: flex;
|
||||||
|
line-height: 2;
|
||||||
|
padding: 10rpx;
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #333;
|
||||||
|
border-bottom: 1rpx dashed #ccc;
|
||||||
|
}
|
||||||
|
.label {
|
||||||
|
width: 200rpx;
|
||||||
|
}
|
||||||
|
.value {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 底部工具栏 */
|
||||||
|
.goods-nav {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 0;
|
||||||
|
width: 100%;
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* #ifdef WEB */
|
||||||
|
// 当根元素有 ios-device 类时,兼容底部样式
|
||||||
|
.ios-device {
|
||||||
|
.viewport {
|
||||||
|
padding-bottom: 34px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.goods-nav {
|
||||||
|
padding-bottom: 34px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* #endif */
|
||||||
|
</style>
|
107
src/pagesShop/pages/shopGoods/list/list.vue
Normal file
107
src/pagesShop/pages/shopGoods/list/list.vue
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { reactive, ref } from 'vue'
|
||||||
|
import type { shopGoodsListInsatnce } from '@/types/component'
|
||||||
|
|
||||||
|
//接收参数
|
||||||
|
const props = withDefaults(
|
||||||
|
defineProps<{
|
||||||
|
classify_id?: string
|
||||||
|
name?: string
|
||||||
|
coupon_id?: string
|
||||||
|
empty_text?: string
|
||||||
|
}>(),
|
||||||
|
{
|
||||||
|
classify_id: '',
|
||||||
|
name: '',
|
||||||
|
coupon_id: '',
|
||||||
|
empty_text: '暂无商品数据',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
const query = reactive({
|
||||||
|
classify_id: props.classify_id,
|
||||||
|
name: props.name,
|
||||||
|
coupon_id: props.coupon_id,
|
||||||
|
sort_field: '',
|
||||||
|
sort_by: '',
|
||||||
|
})
|
||||||
|
|
||||||
|
const shopGoodsListRef = ref<shopGoodsListInsatnce>()
|
||||||
|
|
||||||
|
const queryOptions = ref([
|
||||||
|
{ title: '综合排序', value: 'all', type: 'click' },
|
||||||
|
{ title: '销量优先', value: 'sales', type: 'click' },
|
||||||
|
{
|
||||||
|
title: '价格排序',
|
||||||
|
value: '',
|
||||||
|
type: 'sort',
|
||||||
|
options: [{ value: 'asc' }, { value: 'desc' }],
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
const onChange = (data: any, index: number) => {
|
||||||
|
console.log(data, index)
|
||||||
|
}
|
||||||
|
const onConfirm = (data: any) => {
|
||||||
|
const { value, type } = data
|
||||||
|
if (value === 'all') {
|
||||||
|
Object.assign(query, { sort_field: data.value })
|
||||||
|
} else if (value === 'sales') {
|
||||||
|
Object.assign(query, { sort_field: data.value, sort_by: 'desc' })
|
||||||
|
} else if (type === 'sort') {
|
||||||
|
Object.assign(query, { sort_field: 'price', sort_by: value })
|
||||||
|
}
|
||||||
|
|
||||||
|
shopGoodsListRef.value?.reload()
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<shop-goods-list
|
||||||
|
:query="query"
|
||||||
|
:safe-area-inset-bottom="true"
|
||||||
|
ref="shopGoodsListRef"
|
||||||
|
:options="{
|
||||||
|
emptyViewText: empty_text,
|
||||||
|
backGroundColor: '#fff',
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<template #top>
|
||||||
|
<view v-if="query">
|
||||||
|
<le-dropdown
|
||||||
|
v-model:menuList="queryOptions"
|
||||||
|
themeColor="#3185FF"
|
||||||
|
:duration="300"
|
||||||
|
:isCeiling="false"
|
||||||
|
@onConfirm="onConfirm"
|
||||||
|
@onChange="onChange"
|
||||||
|
></le-dropdown>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
</shop-goods-list>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
page {
|
||||||
|
background-color: #ececec;
|
||||||
|
}
|
||||||
|
.toolbar {
|
||||||
|
position: fixed;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
z-index: 1;
|
||||||
|
background-color: #fff;
|
||||||
|
height: 100rpx;
|
||||||
|
padding: 0 20rpx var(--window-bottom);
|
||||||
|
border-top: 1rpx solid #eaeaea;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
box-sizing: content-box;
|
||||||
|
}
|
||||||
|
// 底部占位空盒子
|
||||||
|
.toolbar-height {
|
||||||
|
height: 190rpx;
|
||||||
|
}
|
||||||
|
</style>
|
147
src/pagesShop/pages/shopGoods/search/search.vue
Normal file
147
src/pagesShop/pages/shopGoods/search/search.vue
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { pageUrl } from '@/utils/constants'
|
||||||
|
import { ref } from 'vue'
|
||||||
|
|
||||||
|
let name = ref('')
|
||||||
|
|
||||||
|
const history = ref<string[]>(uni.getStorageSync('search') || [])
|
||||||
|
|
||||||
|
const storeHistroy = () => {
|
||||||
|
name.value = name.value.trim()
|
||||||
|
if (name.value == '') return
|
||||||
|
// 缓存搜索关键词
|
||||||
|
const index = history.value.findIndex((item) => item === name.value)
|
||||||
|
if (index !== -1) {
|
||||||
|
history.value.splice(index, 1)
|
||||||
|
}
|
||||||
|
history.value.unshift(name.value)
|
||||||
|
uni.setStorageSync('search', history.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
const search = async () => {
|
||||||
|
await uni.navigateTo({
|
||||||
|
url: `${pageUrl['shopping-mall-goods-list']}?name=${name.value}`,
|
||||||
|
})
|
||||||
|
storeHistroy()
|
||||||
|
}
|
||||||
|
|
||||||
|
const onTapHistory = async (keyword: string) => {
|
||||||
|
await uni.navigateTo({
|
||||||
|
url: `${pageUrl['shopping-mall-goods-list']}?name=${keyword}`,
|
||||||
|
})
|
||||||
|
name.value = keyword
|
||||||
|
storeHistroy()
|
||||||
|
}
|
||||||
|
|
||||||
|
const onCancel = () => {
|
||||||
|
uni.navigateBack()
|
||||||
|
}
|
||||||
|
|
||||||
|
const onClearHistory = () => {
|
||||||
|
uni.showModal({
|
||||||
|
title: '',
|
||||||
|
content: '确定要清空历史搜索吗',
|
||||||
|
showCancel: true,
|
||||||
|
success: ({ confirm, cancel }) => {
|
||||||
|
if (confirm) {
|
||||||
|
uni.removeStorageSync('search')
|
||||||
|
history.value = []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const onDeleteHistory = (name: string) => {
|
||||||
|
uni.showModal({
|
||||||
|
title: '',
|
||||||
|
content: '确定要删除该条历史搜索吗',
|
||||||
|
showCancel: true,
|
||||||
|
success: ({ confirm, cancel }) => {
|
||||||
|
if (confirm) {
|
||||||
|
const index = history.value.findIndex((item) => item === name)
|
||||||
|
if (index !== -1) {
|
||||||
|
history.value.splice(index, 1)
|
||||||
|
uni.setStorageSync('search', history.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<view class="search">
|
||||||
|
<uni-search-bar
|
||||||
|
@confirm="search"
|
||||||
|
:focus="true"
|
||||||
|
v-model="name"
|
||||||
|
radius="100"
|
||||||
|
@cancel="onCancel"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
<view class="search-history">
|
||||||
|
<text>历史搜索</text>
|
||||||
|
<view class="trash">
|
||||||
|
<uni-icons type="trash" color="" size="24" @tap="onClearHistory" />
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="keyword">
|
||||||
|
<button
|
||||||
|
v-for="(item, index) in history"
|
||||||
|
:key="index"
|
||||||
|
@tap="onTapHistory(item)"
|
||||||
|
class="keyword-button"
|
||||||
|
aria-readonly
|
||||||
|
@longtap="onDeleteHistory(item)"
|
||||||
|
>
|
||||||
|
{{ item }}
|
||||||
|
</button>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
page {
|
||||||
|
padding-left: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search {
|
||||||
|
padding-right: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-history {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-top: 10px;
|
||||||
|
font-size: 14px;
|
||||||
|
|
||||||
|
.trash {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-right: 50rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.keyword {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
margin-top: 20px;
|
||||||
|
float: left;
|
||||||
|
|
||||||
|
button {
|
||||||
|
background-color: #f2f2f2;
|
||||||
|
border-radius: 25rpx;
|
||||||
|
color: #333333;
|
||||||
|
font-size: 24rpx;
|
||||||
|
align-items: center;
|
||||||
|
margin: 0 10rpx 15rpx 15rpx;
|
||||||
|
justify-content: flex-start;
|
||||||
|
// padding-left: 10px;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
.delete-icon {
|
||||||
|
margin-left: 5px;
|
||||||
|
margin-right: -10rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
90
src/pagesShop/pages/shopMall/components/categoryGroup.vue
Normal file
90
src/pagesShop/pages/shopMall/components/categoryGroup.vue
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { computed } from 'vue'
|
||||||
|
import { fullUrl } from '@/utils/common'
|
||||||
|
import type { categoryPanelItem } from '@/types/home'
|
||||||
|
import { pageUrl } from '@/utils/constants'
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
list: categoryPanelItem[]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const change = (event: UniHelper.UniGridOnChangeEvent) => {
|
||||||
|
uni.setStorageSync('activeCategoryIndex', event.detail.index)
|
||||||
|
uni.navigateTo({
|
||||||
|
url: pageUrl['shopping-mall-category'],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const groupedCategoryList = computed(() => {
|
||||||
|
if (!props.list) return []
|
||||||
|
const chunkSize = 5
|
||||||
|
const groups = []
|
||||||
|
for (let i = 0; i < props.list.length; i += chunkSize) {
|
||||||
|
groups.push(props.list.slice(i, i + chunkSize))
|
||||||
|
}
|
||||||
|
return groups
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<swiper class="swiper" :indicator-dots="true" v-if="props.list.length > 0">
|
||||||
|
<swiper-item v-for="(group, groupIndex) in groupedCategoryList" :key="groupIndex">
|
||||||
|
<uni-grid :column="5" :highlight="true" @change="change" :showBorder="false" :square="false">
|
||||||
|
<uni-grid-item v-for="(item, index) in group" :index="index" :key="index">
|
||||||
|
<view class="grid-item-box">
|
||||||
|
<image :src="fullUrl(item.icon)" class="image" mode="aspectFill" />
|
||||||
|
<text class="text">{{ item.name }}</text>
|
||||||
|
</view>
|
||||||
|
</uni-grid-item>
|
||||||
|
</uni-grid>
|
||||||
|
</swiper-item>
|
||||||
|
</swiper>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.swiper {
|
||||||
|
background-color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image {
|
||||||
|
width: 75rpx;
|
||||||
|
height: 75rpx;
|
||||||
|
margin-top: 10rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text {
|
||||||
|
font-size: 24rpx;
|
||||||
|
margin-top: 10rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-dynamic-box {
|
||||||
|
margin-bottom: 10rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-item-box {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 10rpx 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-item-box-row {
|
||||||
|
flex: 1;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 10rpx 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-dot {
|
||||||
|
position: absolute;
|
||||||
|
top: 5rpx;
|
||||||
|
right: 15rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.swiper {
|
||||||
|
height: 190rpx;
|
||||||
|
margin-top: 15rpx;
|
||||||
|
}
|
||||||
|
</style>
|
137
src/pagesShop/pages/shopMall/components/hotRecommend.vue
Normal file
137
src/pagesShop/pages/shopMall/components/hotRecommend.vue
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { hotRecommendItem } from '@/types/home'
|
||||||
|
import { fullUrl } from '@/utils/common'
|
||||||
|
import { pageUrl } from '@/utils/constants'
|
||||||
|
|
||||||
|
const props = withDefaults(
|
||||||
|
defineProps<{
|
||||||
|
list: hotRecommendItem[]
|
||||||
|
}>(),
|
||||||
|
{
|
||||||
|
list: () => [],
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
// 跳转
|
||||||
|
const jumpTo = (item: hotRecommendItem) => {
|
||||||
|
if (item.goods_id) {
|
||||||
|
return uni.navigateTo({
|
||||||
|
url: `${pageUrl['shopping-mall-goods-detail']}?id=${item.goods_id}`,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (item.classify_id) {
|
||||||
|
return uni.navigateTo({
|
||||||
|
url: `${pageUrl['shopping-mall-goods-list']}?classify_id=${item.classify_id}`,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<view v-if="props.list.length > 0" class="recommend">
|
||||||
|
<uni-section title="热卖推荐" type="line">
|
||||||
|
<view class="section">
|
||||||
|
<uni-grid :column="2" :showBorder="false" :square="false" :highlight="false">
|
||||||
|
<uni-grid-item v-for="(item, index) in props.list" :key="index">
|
||||||
|
<view class="grid-item" @tap="jumpTo(item)">
|
||||||
|
<view class="titles">
|
||||||
|
<text class="title ellipsis">{{ item.title }}</text>
|
||||||
|
<text class="sub-title ellipsis">{{ item.sub_title }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="content">
|
||||||
|
<view class="tag-container">
|
||||||
|
<view class="tag" v-for="(tag, tagIndex) in item.tags" :key="tagIndex">
|
||||||
|
<text>{{ tag }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view>
|
||||||
|
<image class="image" :src="fullUrl(item.image)"></image>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</uni-grid-item>
|
||||||
|
</uni-grid>
|
||||||
|
</view>
|
||||||
|
</uni-section>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.recommend {
|
||||||
|
margin-top: 10rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section {
|
||||||
|
background-color: #f9f9f9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-item {
|
||||||
|
margin: 0rpx 5rpx 10rpx;
|
||||||
|
padding: 0rpx 10rpx;
|
||||||
|
flex-direction: column;
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.titles {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding-bottom: 20rpx;
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-size: 30rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sub-title {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #666;
|
||||||
|
min-height: 35rpx;
|
||||||
|
line-height: 40rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.content {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
.tag-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 5rpx;
|
||||||
|
}
|
||||||
|
.tag {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 5rpx 15rpx;
|
||||||
|
font-size: 20rpx;
|
||||||
|
color: #fff;
|
||||||
|
background-color: #ff6347;
|
||||||
|
border-radius: 10rpx;
|
||||||
|
border: none;
|
||||||
|
box-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.1);
|
||||||
|
height: 40rpx;
|
||||||
|
max-width: 150rpx;
|
||||||
|
margin-bottom: 10rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tag {
|
||||||
|
text {
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.image {
|
||||||
|
width: 150rpx;
|
||||||
|
height: 150rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ellipsis {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: 1;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
}
|
||||||
|
</style>
|
130
src/pagesShop/pages/shopMall/components/notice-bar.vue
Normal file
130
src/pagesShop/pages/shopMall/components/notice-bar.vue
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { pageUrl } from '@/utils/constants'
|
||||||
|
|
||||||
|
type list = {
|
||||||
|
title: string
|
||||||
|
content: string
|
||||||
|
}[]
|
||||||
|
|
||||||
|
const props = withDefaults(
|
||||||
|
defineProps<{
|
||||||
|
list: list
|
||||||
|
color?: string
|
||||||
|
bgColor?: string
|
||||||
|
switchTime?: number
|
||||||
|
}>(),
|
||||||
|
{
|
||||||
|
list: () => [],
|
||||||
|
color: '#000',
|
||||||
|
bgColor: '#fff',
|
||||||
|
switchTime: 3,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
const showContent = (content: string) => {
|
||||||
|
if (!content) return
|
||||||
|
content = encodeURIComponent(content)
|
||||||
|
uni.navigateTo({ url: `${pageUrl['rich-text']}?title=消息公告&content=${content}` })
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<view v-if="props.list.length > 0" style="margin-top: 20rpx">
|
||||||
|
<view class="notice-box" :style="'background-color: ' + bgColor + ';'">
|
||||||
|
<view class="notice-icon">
|
||||||
|
<image src="/static/images/notice_bar.png" style="width: 200rpx" />
|
||||||
|
</view>
|
||||||
|
<scroll-view class="notice-content">
|
||||||
|
<swiper
|
||||||
|
class="swiper"
|
||||||
|
:autoplay="true"
|
||||||
|
:interval="switchTime * 1000"
|
||||||
|
:duration="1500"
|
||||||
|
:circular="true"
|
||||||
|
:vertical="true"
|
||||||
|
>
|
||||||
|
<swiper-item v-for="(item, index) in list" :key="index" class="notice-content-item">
|
||||||
|
<view class="swiper-item">
|
||||||
|
<view
|
||||||
|
class="notice-content-item-text-wrapper"
|
||||||
|
:style="'color: ' + color + ';'"
|
||||||
|
@tap="showContent(item.content)"
|
||||||
|
>
|
||||||
|
<text class="notice-content-item-text">{{ item.title }}</text>
|
||||||
|
<text class="icon-right"></text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</swiper-item>
|
||||||
|
</swiper>
|
||||||
|
</scroll-view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.swiper {
|
||||||
|
height: 60rpx !important;
|
||||||
|
padding-bottom: 20rpx;
|
||||||
|
}
|
||||||
|
scroll-view {
|
||||||
|
flex: 1;
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.notice-box {
|
||||||
|
width: 100%;
|
||||||
|
height: 60rpx;
|
||||||
|
padding: 0 10rpx;
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
|
.notice-icon {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
width: 140rpx;
|
||||||
|
margin-left: 10rpx;
|
||||||
|
}
|
||||||
|
.notice-content {
|
||||||
|
width: calc(100% - 220rpx);
|
||||||
|
position: relative;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
.notice-content-item {
|
||||||
|
width: 100%;
|
||||||
|
height: 60rpx;
|
||||||
|
text-align: left;
|
||||||
|
line-height: 60rpx;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
.notice-content-item-text-wrapper {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notice-content-item-text {
|
||||||
|
flex: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes anotice {
|
||||||
|
0% {
|
||||||
|
transform: translateY(100%);
|
||||||
|
}
|
||||||
|
30% {
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
70% {
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: translateY(-100%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
105
src/pagesShop/pages/shopMall/index.vue
Normal file
105
src/pagesShop/pages/shopMall/index.vue
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { nextTick, ref } from 'vue'
|
||||||
|
import { getBannerApi, getHotRecommendApi, getNoticeBartApi } from '@/api/home'
|
||||||
|
import type { bannerItem, noticeBarItem } from '@/types/home'
|
||||||
|
import { onLoad, onShow } from '@dcloudio/uni-app'
|
||||||
|
import categoryGroup from './components/categoryGroup.vue'
|
||||||
|
import noticeBar from './components/notice-bar.vue'
|
||||||
|
import hotRecommend from './components/hotRecommend.vue'
|
||||||
|
import { getTopCategoryApi } from '@/api/catogory'
|
||||||
|
import { useCartStore, useMemberStore } from '@/stores'
|
||||||
|
import { getCartTotalNumApi } from '@/api/cart'
|
||||||
|
|
||||||
|
//轮播图
|
||||||
|
const bannerList = ref<bannerItem[]>([])
|
||||||
|
const getBannerListData = async () => {
|
||||||
|
const res = await getBannerApi()
|
||||||
|
bannerList.value = res.result
|
||||||
|
}
|
||||||
|
//公告
|
||||||
|
const noticeBarList = ref<noticeBarItem[]>([])
|
||||||
|
const getNoticeBarData = async () => {
|
||||||
|
const res = await getNoticeBartApi()
|
||||||
|
noticeBarList.value = res.result
|
||||||
|
}
|
||||||
|
//分类
|
||||||
|
const categoryList = ref()
|
||||||
|
const getTopCategoryListData = async () => {
|
||||||
|
const res = await getTopCategoryApi()
|
||||||
|
categoryList.value = res.result
|
||||||
|
}
|
||||||
|
//热卖推荐
|
||||||
|
const hotRecommendList = ref()
|
||||||
|
const getHotRecommend = async () => {
|
||||||
|
const res = await getHotRecommendApi()
|
||||||
|
hotRecommendList.value = res.result
|
||||||
|
}
|
||||||
|
//商品列表
|
||||||
|
const shopGoodsListRef = ref()
|
||||||
|
|
||||||
|
const getIndexData = () => {
|
||||||
|
return Promise.all([
|
||||||
|
getBannerListData(),
|
||||||
|
getNoticeBarData(),
|
||||||
|
getTopCategoryListData(),
|
||||||
|
getHotRecommend(),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
onLoad(() => {
|
||||||
|
getIndexData()
|
||||||
|
// 首页分享
|
||||||
|
// #ifdef MP-WEIXIN
|
||||||
|
uni.showShareMenu({
|
||||||
|
withShareTicket: true,
|
||||||
|
menus: ['shareAppMessage'],
|
||||||
|
})
|
||||||
|
// #endif
|
||||||
|
})
|
||||||
|
|
||||||
|
onShow(() => {
|
||||||
|
nextTick(() => {
|
||||||
|
shopGoodsListRef.value.refresh()
|
||||||
|
})
|
||||||
|
|
||||||
|
const cartStore = useCartStore()
|
||||||
|
//首页初始化设置购物车角标
|
||||||
|
if (useMemberStore().profile) {
|
||||||
|
getCartTotalNumApi().then((res) => {
|
||||||
|
cartStore.setCartTotalNum(res.result)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
cartStore.setCartTotalNum(0)
|
||||||
|
}
|
||||||
|
cartStore.setCartTabBadge()
|
||||||
|
})
|
||||||
|
|
||||||
|
const handleRefresh = async (status : number) => {
|
||||||
|
//触发刷新状态
|
||||||
|
if (status === 2) {
|
||||||
|
getIndexData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<shop-goods-list ref="shopGoodsListRef" @refresher-status-changed="handleRefresh" :options="{
|
||||||
|
auto: false,
|
||||||
|
}">
|
||||||
|
<template #top>
|
||||||
|
<shop-goods-search />
|
||||||
|
</template>
|
||||||
|
<template #middle>
|
||||||
|
<shop-swiper :list="bannerList" v-if="bannerList" />
|
||||||
|
<noticeBar :list="noticeBarList" />
|
||||||
|
<category-group :list="categoryList" v-if="categoryList" />
|
||||||
|
<hot-recommend :list="hotRecommendList" />
|
||||||
|
<uni-section title="商品列表" type="line"></uni-section>
|
||||||
|
</template>
|
||||||
|
</shop-goods-list>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
page {
|
||||||
|
background-color: #f9f9f9;
|
||||||
|
}
|
||||||
|
</style>
|
Loading…
Reference in New Issue
Block a user