feat:共富商城商品列表和商品详细信息
This commit is contained in:
1 parent
46c1c41331
commit
4ef150f57b
7 files changed
+200
-49
No files matched your search
@@ -1,3 +1,81 @@
|
||||
<template>11</template>
|
||||
<template>
|
||||
<crud-layout mainIsScroll bg="/static/page/blue-bg.png">
|
||||
<image :src="filePath(state.detail.coverImage)" class="w-full h-500rpx" mode="aspectFill" />
|
||||
<view v-if="state.detail.id" class="card p-20rpx mt-10rpx mx-20rpx bg-gradient-to-b from-#0094ff33 to-#fff">
|
||||
<view class="w-full flex justify-between items-center">
|
||||
<view class="flex items-center">
|
||||
<text class="second-text-36 line-through">¥</text>
|
||||
<text class="font-bold second-text-40 line-through mr-2">{{ state.detail.originalPrice.toFixed(2) }}</text>
|
||||
<text class="text-36rpx color-red! mr-1">¥</text>
|
||||
<text class="text-46rpx font-bold color-red!">{{ state.detail.spuPrice.toFixed(2) }}</text>
|
||||
<text class="view-clip px-2 text-22rpx c-white bg-#167aff ml-2 rounded-r-1 rounded-lt-1">到手价</text>
|
||||
</view>
|
||||
<view class="main-text-32 c-red! font-italic">
|
||||
<text>约省</text>
|
||||
<text class="text-44rpx">{{ Number(state.detail.originalPrice) - Number(state.detail.spuPrice) }}</text>
|
||||
<text>元</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="w-full sub-text-36 flex items-center mt-1">
|
||||
<text class="c-#0094ff! sub-text-24 px-2 bg-#0094ff22 rounded-1 mr-2 whitespace-nowrap">
|
||||
{{ state.detail.status == "0" ? "在售" : "未售" }}
|
||||
</text>
|
||||
<text class="truncate">{{ state.detail.title }}</text>
|
||||
</view>
|
||||
<view class="second-text-26 mt-1">售卖时间 {{ state.detail.saleStartTime }} ~ {{ state.detail.saleEndTime }}</view>
|
||||
<view class="second-text-26 truncate mt-1">
|
||||
<text class="mr-20rpx">售卖单位 {{ state.detail.unit }}</text>
|
||||
<text class="mr-20rpx">剩余库存 {{ state.detail.totalStock }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="mt-20rpx flex-col justify-between card p-20rpx mx-20rpx">
|
||||
<view class="prefix_title">商品描述</view>
|
||||
<view class="mt-20rpx overflow-auto text-26rpx!" v-html="state.detail.description"></view>
|
||||
</view>
|
||||
</crud-layout>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts"></script>
|
||||
<script setup lang="ts">
|
||||
import { onLoad } from "@dcloudio/uni-app"
|
||||
import { reactive } from "vue"
|
||||
import { prosperityShopApi } from "@/sheep/api"
|
||||
import { filePath } from "@/sheep/utils/file"
|
||||
|
||||
const state = reactive<any>({
|
||||
id: "",
|
||||
detail: {},
|
||||
})
|
||||
onLoad(async (option: any) => {
|
||||
if (!option.id) return uni.showToast({ title: "参数错误", icon: "error" })
|
||||
uni.showLoading({ title: "加载中..." })
|
||||
state.id = option.id
|
||||
await getGoodsDetail(option.id)
|
||||
uni.hideLoading()
|
||||
})
|
||||
|
||||
const getGoodsDetail = async (id: string) => {
|
||||
const { code, data, msg } = await prosperityShopApi.getGoodsDetail(id)
|
||||
if (code != 0) return uni.showToast({ title: msg || "商品详情获取失败", icon: "none" })
|
||||
state.detail = data
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
:deep() {
|
||||
.layout_main > scroll-view {
|
||||
padding: 0 !important;
|
||||
}
|
||||
}
|
||||
.view-clip {
|
||||
position: relative;
|
||||
&::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: -18rpx;
|
||||
bottom: 0rpx;
|
||||
width: 20rpx;
|
||||
height: 20rpx;
|
||||
background: radial-gradient(circle at 0 0, transparent 72%, #1879ff 0);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,16 +1,18 @@
|
||||
<template>
|
||||
<crud-layout mainIsScroll bg="/static/page/blue-bg.png">
|
||||
<image :src="filePath(state.detail.images)" class="w-full h-400rpx" mode="aspectFill" />
|
||||
<view class="card p-20rpx mt-10rpx mx-20rpx">
|
||||
<crud-layout mainIsScroll bg="/static/page/blue-bg.png" @scroll="scroll">
|
||||
<view class="w-full h-400rpx relative">
|
||||
<image :src="filePath(state.detail.images)" class="w-full h-full" mode="aspectFill" />
|
||||
<view class="absolute w-full h-full top-0 left-0" :style="filterStyle"></view>
|
||||
</view>
|
||||
<view v-if="state.detail.id" class="card p-20rpx mt-10rpx mx-20rpx">
|
||||
<view class="w-full sub-text-32 truncate">{{ state.detail.name }}</view>
|
||||
<view class="sub-text-24 mt-1">
|
||||
<text class="c-#0094ff! mr-20rpx px-2 bg-#0094ff22 rounded-2">{{ state.detail.status == "0" ? "开业中" : "未开业" }}</text>
|
||||
<text class="rounded-2 c-#0094ff px-2 bg-#0094ff22 rounded-2">{{ state.detail.businessHours }}</text>
|
||||
</view>
|
||||
<view class="second-text-24 truncate mt-1">
|
||||
<text class="mr-20rpx">营业人:{{ state.detail.contactName }}</text>
|
||||
<text class="mr-20rpx">联系:{{ state.detail.contactPhone }}</text>
|
||||
<text class="mr-20rpx">许可:{{ state.detail.licenseNo }}</text>
|
||||
<text class="mr-20rpx">营业人 {{ state.detail.contactName }}·{{ state.detail.contactPhone }}</text>
|
||||
<text class="mr-20rpx">许可 {{ state.detail.licenseNo }}</text>
|
||||
</view>
|
||||
<view class="second-text-26 mt-1">
|
||||
<image class="w-24rpx h-24rpx mr-1" src="@/static/page/address.svg" />
|
||||
@@ -19,8 +21,8 @@
|
||||
<marquee class="second-text-24 mt-1">{{ state.detail.remark }}</marquee>
|
||||
</view>
|
||||
|
||||
<view class="flex-row">
|
||||
<scroll-view scroll-y class="menu w-200rpx h-full pb-20rpx bg-#f6f6f6 pl--20rpx mt-2">
|
||||
<view class="flex mt-2">
|
||||
<scroll-view scroll-y class="menu w-200rpx mb-20rpx bg-#f6f6f6 pl-20rpx" style="height: calc(100vh - 400rpx)">
|
||||
<view
|
||||
class="menu_item flex-row-center"
|
||||
v-for="(v, i) in state.category"
|
||||
@@ -31,34 +33,43 @@
|
||||
<view class="menu_item_title show-line">{{ v.name }}</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
<scroll-view v-if="state.goodsList.length > 0" scroll-y class="flex-1 pb-20rpx">
|
||||
<!-- <view v-for="(pV, pI) in state.foodList" :key="pI">
|
||||
<view class="m-x-15rpx">
|
||||
<product-row
|
||||
v-model="pV.productCount"
|
||||
:productId="pV.id"
|
||||
:pic="filePath(pV.picUrl)"
|
||||
:name="pV.name"
|
||||
:price="pV.price"
|
||||
:marketPrice="pV.marketPrice"
|
||||
:salesCount="pV.salesCount"
|
||||
:stock="pV.stock"
|
||||
@add="chentProductCartCount(pV)"
|
||||
@remove="chentProductCartCount(pV)"
|
||||
/>
|
||||
<scroll-view v-if="state.goodsList.length > 0" scroll-y class="flex-1 overflow-hidden pb-20rpx flex">
|
||||
<view v-for="v in state.goodsList" :key="v.id" class="card flex mx-15rpx mb-2 p-2" @click="doGoods(v.id)">
|
||||
<image :src="filePath(v.coverImage)" class="size-180rpx rounded-12rpx" mode="aspectFill" />
|
||||
<view class="flex-1 ml-2 overflow-hidden">
|
||||
<view class="truncate sub-text-30">{{ v.title }}</view>
|
||||
<view>
|
||||
<text class="c-#0094ff! sub-text-24 px-2 bg-#0094ff22 rounded-2">{{ v.status == "0" ? "在售" : "未售" }}</text>
|
||||
</view>
|
||||
<view>
|
||||
<text class="second-text-26 mr-2">单位 {{ v.unit }}</text>
|
||||
<text class="second-text-26">库存 {{ v.totalStock }}</text>
|
||||
</view>
|
||||
<view>
|
||||
<text class="color-red mr-1">
|
||||
<text class="text-26rpx">¥</text>
|
||||
<text class="text-30rpx font-bold">{{ v.spuPrice.toFixed(2).split(".")[0] }}.</text>
|
||||
<text class="text-26rpx font-bold">{{ v.spuPrice.toFixed(2).split(".")[1] }}</text>
|
||||
</text>
|
||||
<text class="second-text-24 line-through">
|
||||
<text class="text-24rpx">¥</text>
|
||||
<text class="font-bold">{{ v.originalPrice.toFixed(2) }}</text>
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
</view> -->
|
||||
</view>
|
||||
</scroll-view>
|
||||
<view v-else class="mx-auto mt-10 second-text-28">暂无信息</view>
|
||||
<view v-else class="mx-auto mt-10 second-text-28">暂无商品</view>
|
||||
</view>
|
||||
</crud-layout>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { reactive } from "vue"
|
||||
import { computed, reactive, ref } from "vue"
|
||||
import { prosperityShopApi } from "@/sheep/api"
|
||||
import { onLoad } from "@dcloudio/uni-app"
|
||||
import { filePath } from "@/sheep/utils/file"
|
||||
import treeToList from "@/sheep/utils/tree-to-list"
|
||||
|
||||
const state = reactive<any>({
|
||||
id: "",
|
||||
@@ -67,11 +78,16 @@ const state = reactive<any>({
|
||||
curCategoryIndex: 0,
|
||||
goodsList: [],
|
||||
})
|
||||
const layoutScroll = ref(0)
|
||||
|
||||
onLoad(async (option: any) => {
|
||||
if (!option.id) return uni.showToast({ title: "参数错误", icon: "error" })
|
||||
uni.showLoading({ title: "加载中..." })
|
||||
state.id = option.id
|
||||
await getShopDetail(option.id)
|
||||
await getGoodsCategory(option.id)
|
||||
await getGoodsList()
|
||||
uni.hideLoading()
|
||||
})
|
||||
|
||||
const getShopDetail = async (id: string) => {
|
||||
@@ -83,8 +99,46 @@ const getShopDetail = async (id: string) => {
|
||||
const getGoodsCategory = async (id: string) => {
|
||||
const { code, data, msg } = await prosperityShopApi.getGoodsCategory({ merchantId: id })
|
||||
if (code != 0) return uni.showToast({ title: msg || "商品分类获取失败", icon: "none" })
|
||||
// state.category = data ?? []
|
||||
state.category = Array.from({ length: 10 }, (_, i) => ({ id: 0, name: "水果" }))
|
||||
state.category = treeToList(data) ?? []
|
||||
}
|
||||
|
||||
const getGoodsList = async () => {
|
||||
const params = {
|
||||
current: 1,
|
||||
size: 99999,
|
||||
merchantId: state.id,
|
||||
categoryId: state.category[state.curCategoryIndex].id,
|
||||
}
|
||||
const { code, data, msg } = await prosperityShopApi.getGoodsList(params)
|
||||
if (code != 0) return uni.showToast({ title: msg || "商品列表获取失败", icon: "none" })
|
||||
state.goodsList = data?.records ?? []
|
||||
}
|
||||
|
||||
const doCategory = async (index: number) => {
|
||||
if (state.curCategoryIndex == index) return
|
||||
state.curCategoryIndex = index
|
||||
uni.showLoading({ title: "加载中..." })
|
||||
await getGoodsList()
|
||||
uni.hideLoading()
|
||||
}
|
||||
|
||||
const scroll = (e: any) => {
|
||||
layoutScroll.value = e.detail.scrollTop
|
||||
}
|
||||
|
||||
const filterStyle = computed(() => {
|
||||
const scrollY = layoutScroll.value
|
||||
const blur = Math.min(1, scrollY / 400) * 20
|
||||
return {
|
||||
backdropFilter: `blur(${blur}rpx)`,
|
||||
}
|
||||
})
|
||||
|
||||
const doGoods = (id: string) => {
|
||||
if (!id) return uni.showToast({ title: "商品id不存在", icon: "none" })
|
||||
uni.navigateTo({
|
||||
url: `/pages/prosperity-shop/goods-detail?id=${id}`,
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
<scroll-view scroll-x>
|
||||
<view class="w-full flex my-2">
|
||||
<view
|
||||
class="mr-2 sub-text-28 py-1 px-2 rounded-1 bg-#fff border border-solid border-transparent"
|
||||
class="mr-2 sub-text-28 py-1 px-2 rounded-1 bg-#fff border border-solid border-transparent whitespace-nowrap"
|
||||
:class="{ 'bg-#0779FD22! c-#0779FD! border-#0779FD33!': state.params.categoryId === v.id }"
|
||||
v-for="(v, i) in state.classifyList"
|
||||
:key="i"
|
||||
@@ -152,6 +152,7 @@ import { merchantApi, onlineShopApi } from "@@/api"
|
||||
import { hooks, router } from "@@/index"
|
||||
import { useCartStore } from "@/sheep/stores/cart"
|
||||
import { storePrice } from "@/sheep/utils/formatMoney"
|
||||
import treeToList from "@/sheep/utils/tree-to-list"
|
||||
|
||||
const cartStore = useCartStore()
|
||||
|
||||
@@ -207,16 +208,6 @@ const getAllMerchant = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
const treeToList = <T extends { children?: T[] }>(tree: T[], result: T[] = []): T[] => {
|
||||
for (const node of tree) {
|
||||
result.push(node)
|
||||
if (node?.children?.length) {
|
||||
treeToList(node.children, result)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// 获取分类
|
||||
const getClassify = async () => {
|
||||
if (state.curMerchant.id) {
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
<scroll-view scroll-x>
|
||||
<view class="w-full flex my-2">
|
||||
<view
|
||||
class="mr-2 sub-text-28 py-1 px-2 rounded-1 bg-#fff border border-solid border-transparent"
|
||||
class="w-300rpx mr-2 sub-text-28 py-1 px-2 rounded-1 bg-#fff border border-solid border-transparent whitespace-nowrap"
|
||||
:class="{ 'bg-#0779FD22! c-#0779FD! border-#0779FD33!': state.params.categoryId === v.id }"
|
||||
v-for="(v, i) in state.classifyList"
|
||||
:key="i"
|
||||
@@ -68,8 +68,8 @@
|
||||
</text>
|
||||
<text>{{ v.email }}</text>
|
||||
</view>
|
||||
<view class="sub-text-26 truncate">
|
||||
<text class="bg-#0779FD11 py-1 rounded-2 c-#0094ff">营业:{{ v.businessHours }}</text>
|
||||
<view>
|
||||
<text class="c-#0094ff! sub-text-26 px-2 bg-#0094ff22 rounded-2">营业:{{ v.businessHours }}</text>
|
||||
</view>
|
||||
<view class="second-text-26">
|
||||
<image class="w-24rpx h-24rpx mr-1" src="@/static/page/address.svg" />
|
||||
|
||||
@@ -31,4 +31,19 @@ export default {
|
||||
params,
|
||||
})
|
||||
},
|
||||
// 获取商品列表
|
||||
getGoodsList: (params: any): Promise<any> => {
|
||||
return request({
|
||||
url: `/supermarket/app/mall/merchant/product/list`,
|
||||
method: "GET",
|
||||
params,
|
||||
})
|
||||
},
|
||||
// 获取商品详情
|
||||
getGoodsDetail: (id: string): Promise<any> => {
|
||||
return request({
|
||||
url: `/supermarket/app/mall/product/${id}`,
|
||||
method: "GET",
|
||||
})
|
||||
},
|
||||
}
|
||||
@@ -21,6 +21,7 @@ const props = withDefaults(defineProps<Props>(), {
|
||||
mainIsScroll: false,
|
||||
bg: "",
|
||||
})
|
||||
const emit = defineEmits(["scroll"])
|
||||
|
||||
const state = reactive({
|
||||
navBgTitleColor_: props.navBgTitleColor,
|
||||
@@ -46,10 +47,11 @@ onMounted(() => {
|
||||
})
|
||||
|
||||
function scroll(e) {
|
||||
if (!router.hasTabbar()) return
|
||||
if (!props.scrollHideTabbar) return
|
||||
const offset = 50
|
||||
const top = e.target.scrollTop
|
||||
// if (!router.hasTabbar()) return
|
||||
// if (!props.scrollHideTabbar) return
|
||||
// const offset = 50
|
||||
// const top = e.target.scrollTop
|
||||
emit("scroll", e)
|
||||
}
|
||||
|
||||
function defaultPage() {
|
||||
@@ -67,7 +69,7 @@ function initBackStyle() {
|
||||
break
|
||||
case "just-flot-back":
|
||||
state.visble.topSafe = true
|
||||
state.visble.topNav = false
|
||||
state.visble.topNav = true
|
||||
break
|
||||
default:
|
||||
break
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
const treeToList = <T extends { children?: T[] }>(tree: T[], result: T[] = []): T[] => {
|
||||
for (const node of tree) {
|
||||
result.push(node)
|
||||
if (node?.children?.length) {
|
||||
treeToList(node.children, result)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
export default treeToList
|
||||
Reference in new issue
Block a user