在线商城页面

This commit is contained in:
weiwei committed 2025-06-05 16:12:34 +08:00
1 parent 220af7f756
commit bab4a4383b
10 files changed
+211 -142

No files matched your search

+14 -9
View File
@@ -2,11 +2,6 @@
import { isDevEnv, getLaunchParam } from "@/sheep/utils/app"
import { getAppInfo } from "@@/utils/app"
// import { onLaunch, onShow, onHide } from "@dcloudio/uni-app"
// onLaunch(() => {})
// onShow(() => {})
// onHide(() => {})
interface TabbarItem {
pagePath: string
text: string
@@ -33,6 +28,14 @@ const tabbar: TabbarItem[] = [
launchParam: ["selfMerchant"],
auth: ["user"],
},
{
pagePath: "pages/tabbar/onlineShop",
text: "在线商城",
iconPath: "/static/tabbar/online-shop.png",
selectedIconPath: "/static/tabbar/online-shop-select.png",
launchParam: ["onlineShop"],
auth: ["user"],
},
{
pagePath: "pages/tabbar/coopMerchant",
text: "合作商家",
@@ -54,7 +57,7 @@ const tabbar: TabbarItem[] = [
text: "订单",
iconPath: "/static/tabbar/order.png",
selectedIconPath: "/static/tabbar/order-select.png",
launchParam: ["canteen", "selfMerchant", "coopMerchant"],
launchParam: ["canteen", "selfMerchant", "onlineShop", "coopMerchant"],
auth: ["user"],
},
{
@@ -94,7 +97,7 @@ const tabbar: TabbarItem[] = [
text: "我的",
iconPath: "/static/tabbar/my.png",
selectedIconPath: "/static/tabbar/my-select.png",
launchParam: ["canteen", "selfMerchant", "coopMerchant"],
launchParam: ["canteen", "selfMerchant", "onlineShop", "coopMerchant"],
auth: ["user", "merchant", "doorAdmin"],
},
]
@@ -149,6 +152,9 @@ export default {
case "selfMerchant":
uni.switchTab({ url: "/pages/tabbar/selfMerchant" })
break
case "onlineShop":
uni.switchTab({ url: "/pages/tabbar/onlineShop" })
break
case "coopMerchant":
uni.switchTab({ url: "/pages/tabbar/coopMerchant" })
break
@@ -169,6 +175,7 @@ export default {
return uni.switchTab({ url: "/pages/tabbar/individualOrder" })
case "user":
if (launchParam === "selfMerchant") return uni.switchTab({ url: "/pages/tabbar/selfMerchant" })
if (launchParam === "onlineShop") return uni.reLaunch({ url: "/pages/tabbar/onlineShop" })
if (launchParam === "coopMerchant") return uni.reLaunch({ url: "/pages/tabbar/coopMerchant" })
return
case "doorAdmin":
@@ -179,8 +186,6 @@ export default {
}
}
},
onShow() {},
onHide() {},
methods: {
async _initTabbar() {
+14 -1
View File
@@ -23,6 +23,13 @@
"disableScroll": true
}
},
{
"path": "pages/tabbar/onlineShop",
"style": {
"navigationBarTitleText": "在线商城",
"disableScroll": true
}
},
{
"path": "pages/tabbar/coopMerchant",
"style": {
@@ -483,6 +490,12 @@
"iconPath": "",
"selectedIconPath": ""
},
{
"pagePath": "pages/tabbar/onlineShop",
"text": "",
"iconPath": "",
"selectedIconPath": ""
},
{
"pagePath": "pages/tabbar/coopMerchant",
"text": "",
@@ -521,7 +534,7 @@
},
{
"pagePath": "pages/tabbar/message",
"text": "通知公告",
"text": "",
"iconPath": "",
"selectedIconPath": ""
},
+1 -1
View File
@@ -156,7 +156,7 @@ const updateFoodCart = async () => {
}
// 打开购物车
const doVisibleCart = async () => (state.showCartDialog = true)
const doVisibleCart = () => (state.showCartDialog = true)
// 购买 打开预订时间段选择对话框
const doBuy = async () => {
@@ -1,13 +1,5 @@
<template>
<tm-drawer
z-index="100"
v-model:show="props.modelValue"
showFooter
title="购物车"
position="bottom"
size="60%"
@close="doClose"
>
<tm-drawer z-index="100" v-model:show="props.modelValue" showFooter title="购物车" position="bottom" size="60%" @close="doClose">
<product-row
mode="car"
v-for="v in carList"
@@ -38,7 +30,7 @@ import { filePath } from "@/sheep/utils/file"
import ProductRow from "./product-row.vue"
import ProductTotal from "./product-total.vue"
const emit = defineEmits(["update:modelValue", "doPay", "add", "remove"])
const emit = defineEmits(["update:modelValue", "submit", "add", "remove"])
export type Props = {
modelValue: any
@@ -58,7 +50,7 @@ const doClose = () => {
}
const doPay = () => {
emit("doPay")
emit("submit")
}
const getHeight = () => {
+24 -24
View File
@@ -1,37 +1,37 @@
<template>
<view class="flex-row items-center justify-between" @tap="doContainer">
<view class="flex-row-center">
<view class="flex-row-center size-80rpx rounded-full bg-#0094ff">
<view class="color-white! iconfont icon-biaoqianlanicon_gouwuche-xuanzhong" />
</view>
<view class="flex-col m-l-30rpx">
<view class="second-text-36 color-#f63a36!">¥ {{ hooks.useMoney(props.totPrice) }}</view>
<view class="sub-text-28">共{{ props.totCount }}件</view>
</view>
</view>
<view class="bg-#0094ff p-x-30rpx p-y-20rpx text-center rounded-40rpx sub-text-30 color-white!" @tap.stop="doPay">提交订单</view>
</view>
<view class="flex-row items-center justify-between" @tap="doContainer">
<view class="flex-row-center">
<view class="flex-row-center size-80rpx rounded-full bg-#0094ff">
<view class="color-white! iconfont icon-biaoqianlanicon_gouwuche-xuanzhong" />
</view>
<view class="flex-col m-l-30rpx">
<view class="second-text-36 color-#f63a36!">¥ {{ hooks.useMoney(props.totPrice) }}</view>
<view class="sub-text-28">共{{ props.totCount }}件</view>
</view>
</view>
<view class="bg-#0094ff p-x-30rpx p-y-20rpx text-center rounded-40rpx sub-text-30 color-white!" @tap.stop="doPay">提交订单</view>
</view>
</template>
<script setup lang="ts">
import { hooks } from "@@/index";
import { hooks } from "@@/index"
const emit = defineEmits(["doPay", "doContainer"]);
const emit = defineEmits(["submit", "doContainer"])
export type Props = {
totPrice: number;
totCount: number;
};
totPrice: number
totCount: number
}
const props = withDefaults(defineProps<Props>(), {
totPrice: 0,
totCount: 0,
});
totPrice: 0,
totCount: 0,
})
const doPay = () => {
emit("doPay");
};
emit("submit")
}
const doContainer = () => {
emit("doContainer");
};
emit("doContainer")
}
</script>
+71 -94
View File
@@ -66,9 +66,6 @@
:borderColor="['#89c0ff']"
>
<marquee class="flex items-center">{{ state.detail.discountsPrice }}</marquee>
<!-- <view class="w-full overflow-hidden flex">
<view class="scroll-text">{{ state.detail.discountsPrice }}</view>
</view> -->
</tm-alert>
<!-- start 商品 start -->
@@ -112,24 +109,24 @@
<!-- end 商品 end -->
<template #main-bottom>
<view v-if="false" class="car z-200 p-x-30rpx p-y-30rpx bg-white">
<product-total :totPrice="state.totPrice" :totCount="state.totCount" @doContainer="doVisibleCar" @doPay="doPay" />
<view class="car z-200 p-x-30rpx p-y-30rpx bg-white">
<product-total :totPrice="state.totPrice" :totCount="state.totCount" @doContainer="doVisibleCart" @submit="doBuy" />
</view>
</template>
</crud-layout>
<!-- 购物车弹框 -->
<product-car
v-model="showCarDialog"
:carList="state.carList"
<product-cart
v-model="state.showCartDialog"
:carList="state.cartList"
:totPrice="state.totPrice"
:totCount="state.totCount"
@add="doAdd"
@remove="doRemove"
@doPay="doPay"
@submit="doBuy"
/>
<!-- 规格与数量弹框 -->
<product-sku v-model="showSkuDialog" :productData="curProductData" @onAddToCar="onAddToCar" />
<product-sku v-model="state.showSkuDialog" :productData="state.curProductData" @onAddToCar="onAddToCar" />
</template>
<script setup lang="ts">
@@ -140,7 +137,7 @@ import { router } from "@@/index"
import { foodApi, merchantApi, payApi, shopApi } from "@@/api"
import { filePath } from "@@/utils/file"
import ProductRow from "./components/product-row.vue"
import ProductCar from "./components/product-car.vue"
import ProductCart from "./components/product-cart.vue"
import ProductTotal from "./components/product-total.vue"
import ProductSku from "./components/product-sku.vue"
@@ -148,20 +145,21 @@ import ProductSku from "./components/product-sku.vue"
const state = reactive<any>({
merchantId: null,
detail: {},
classifyList: [],
currentClassifyIndex: 0,
goodsList: [],
carList: [],
cartList: [],
totPrice: 0,
totCount: 0,
classifyList: [],
currentClassifyIndex: 0,
showCartDialog: false,
showSkuDialog: false,
curProductData: {}, // 当前点击加入购物车的商品信息
})
// 当前点击加入购物车的商品信息
const curProductData = ref<any>({})
// sku 弹框开关
const showSkuDialog = ref<boolean>(false)
// 购物车弹框开关
const showCarDialog = ref<boolean>(false)
onLoad((options: any) => {
if (!isEmpty(options.id)) {
@@ -169,57 +167,53 @@ onLoad((options: any) => {
}
})
onShow(async () => {
uni.showLoading({
title: "加载中...",
mask: true,
})
uni.showLoading({ title: "加载中...", mask: true })
try {
await getDetail()
await getShopCar()
await getShopCart()
await getClassify()
} catch (error) {
uni.showToast({
title: "加载失败",
icon: "error",
})
} catch (error: any) {
uni.showToast({ title: error, icon: "error" })
} finally {
uni.hideLoading()
}
})
const getHeight = () => {
const rpx = 480
return `calc(100vh - ${uni.upx2px(rpx)}px)`
}
const getDetail = async () => {
const { data } = await merchantApi.detail({ id: state.merchantId })
state.detail = data
}
const getShopCar = async () => {
const getShopCart = async () => {
const { data } = await foodApi.getShopCar({ merchantId: state.merchantId, hardType: "SUPERMARKET" })
state.carList = data ?? []
console.log("car", data)
state.totPrice = state.carList.map((v) => Number(v.totalPrice)).reduce((pre, next) => pre + next, 0)
state.totCount = state.carList.map((v) => v.productCount).reduce((pre, next) => pre + next, 0)
state.cartList = data ?? []
state.totPrice = state.cartList.map((v) => Number(v.totalPrice)).reduce((pre, next) => pre + next, 0)
state.totCount = state.cartList.map((v) => v.productCount).reduce((pre, next) => pre + next, 0)
}
const getClassify = async () => {
if (state.merchantId) {
const { data } = await shopApi.getSupermarketSpuCategory({ merchantId: state.merchantId })
state.classifyList = [{ id: null, name: "全部" }, ...data]
state.classifyList = data ?? []
if (!isEmpty(state.classifyList)) {
getPorduct()
getPorductList()
}
}
}
const getPorduct = async () => {
const getPorductList = async () => {
const target = state.classifyList[state.currentClassifyIndex]
if (isEmpty(target)) return
const { data } = await shopApi.getSupermarketSpuList({
// productName: target.name,
merchantId: state.merchantId,
categoryId: target.id,
current: 1,
size: 9999,
size: 99999,
})
state.goodsList = data.records
state.goodsList.forEach((i) => {
@@ -231,11 +225,11 @@ const getPorduct = async () => {
}
})
const goodsList = [...state.goodsList]
if (state.carList && state.carList.length) {
if (state.cartList && state.cartList.length) {
for (const i of goodsList) {
if (i && i.foodSPURespVOList && i.foodSPURespVOList.length) {
for (const j of i.foodSPURespVOList) {
for (const v of state.carList) {
for (const v of state.cartList) {
if (j.id == v.productId) {
j.productCount = v.productCount
}
@@ -249,45 +243,23 @@ const getPorduct = async () => {
const doClassify = async (i: any) => {
state.currentClassifyIndex = i
await getPorduct()
}
// 联系商家
const doPhone = () => {
if (isEmpty(state.detail.phone)) {
uni.showToast({ title: "该商户没有预留电话", icon: "none" })
return
}
;(window as any).dd
.callPhone({
phoneNum: state.detail.phone,
})
.then((res) => {})
.catch((err) => {})
}
const getHeight = () => {
const rpx = 480
return `calc(100vh - ${uni.upx2px(rpx)}px)`
await getPorductList()
}
// 点击添加到购物车
const onClickAddToCar = (productData: any) => {
showSkuDialog.value = true
curProductData.value = productData
state.showSkuDialog = true
state.curProductData = productData
}
// 加入到购物车时
const onAddToCar = (cartGoodsInfo: any) => {
console.log("cartGoodsInfo", cartGoodsInfo)
chentProductCount(cartGoodsInfo) // 同步购物车中的数据
showSkuDialog.value = false
state.showSkuDialog = false
}
// 同步购物车中的数据
const chentProductCount = async (item: any, type?: "add" | "remove") => {
try {
console.log("ccccccc", item)
uni.showLoading({ title: "加载中", mask: true })
let count = item.productCount
if (type === "add") {
@@ -298,16 +270,14 @@ const chentProductCount = async (item: any, type?: "add" | "remove") => {
count -= 1
}
const target = state.carList.find((v) => v.productId == item.productId)
const targetIndex = state.carList.findIndex((v) => v.productId == item.productId)
const target = state.cartList.find((v) => v.productId == item.productId)
const targetIndex = state.cartList.findIndex((v) => v.productId == item.productId)
if (target) {
state.carList[targetIndex].productCount = count
state.carList[targetIndex].productId = item.productId
state.carList[targetIndex].spuId = item.spuId
console.log("1", state.carList[targetIndex])
state.cartList[targetIndex].productCount = count
state.cartList[targetIndex].productId = item.productId
state.cartList[targetIndex].spuId = item.spuId
} else {
state.carList.push(item)
console.log("2", item)
state.cartList.push(item)
}
nextTick(async () => await updateShopCar()) // 保存购物车数据
@@ -319,10 +289,8 @@ const chentProductCount = async (item: any, type?: "add" | "remove") => {
}
// 保存购物车数据
const updateShopCar = async () => {
console.log("carList", state.carList)
if (isEmpty(state.carList)) return
const list = state.carList
if (isEmpty(state.cartList)) return
const list = state.cartList
.filter((v) => v.productCount && v.productCount > 0)
.map((v) => {
return {
@@ -336,13 +304,11 @@ const updateShopCar = async () => {
list,
hardType: "SUPERMARKET",
})
if (code == 0) await getShopCar()
if (code == 0) await getShopCart()
}
// 显示购物车
const doVisibleCar = async () => {
showCarDialog.value = true
}
// 打开购物车
const doVisibleCart = () => (state.showCartDialog = true)
const doAdd = async (item: any) => {
chentProductCount(item, "add")
@@ -352,7 +318,7 @@ const doRemove = async (item: any) => {
}
// 点击提交订单
const doPay = async () => {
const doBuy = async () => {
uni.showModal({
title: "提示",
content: "是否确定购买",
@@ -367,25 +333,36 @@ const doPay = async () => {
// 提交订单
const submitPay = async () => {
try {
if (isEmpty(state.carList)) {
if (isEmpty(state.cartList)) {
uni.showToast({ title: "请至少购买一件商品", icon: "none" })
return
}
uni.showLoading({ title: "支付中...", mask: false })
const _createtPa = {
carParentId: state.carList[0].parentCarId,
const { data, code }: any = await payApi.createHardwareOrder({
carParentId: state.cartList[0].parentCarId,
hardType: "SUPERMARKET",
}
const { data: createData, code: createCode }: any = await payApi.createHardwareOrder(_createtPa)
if (createCode != 0) throw new Error()
uni.hideLoading()
})
if (code != 0) throw new Error()
uni.showToast({ title: "支付成功", icon: "none" })
router.jump("/pages/tabbar/order")
} catch (error) {
console.error(error)
} finally {
uni.hideLoading()
}
}
// 联系商家
const doPhone = () => {
if (isEmpty(state.detail.phone)) {
uni.showToast({ title: "该商户没有预留电话", icon: "none" })
return
}
;(window as any).dd
.callPhone({ phoneNum: state.detail.phone })
.then((res) => {})
.catch((err) => {})
}
</script>
<style lang="scss" scoped>
+82
View File
@@ -0,0 +1,82 @@
<script setup lang="ts">
import { ref, reactive } from "vue"
import { onLoad, onShow } from "@dcloudio/uni-app"
import { merchantApi } from "@@/api"
import { hooks, router } from "@@/index"
import { filePath } from "@@/utils/file"
const pageRef = ref<any>(null)
const state = reactive<any>({
pa: {
pagesize: 10,
type: "3",
},
})
const pageHook = hooks.usePage(pageRef, merchantApi.page, null, "data.records")
const query = (page: number, pagesize: number) => {
pageHook.startPage(page, pagesize, false, state.pa)
}
onLoad(() => {
const app = getApp()
app._initTabbar()
})
onShow(() => {
pageRef.value && pageRef.value.reload(false)
})
const toDetail = async (v: any) => {
const isLogin = await hooks.useCheckLogin()
if (isLogin) {
router.jump("/pages/shop/detail", { id: v.id })
}
}
</script>
<template>
<crud-layout bg="/static/page/blue-bg.png">
<z-paging
ref="pageRef"
height="100%"
:fixed="false"
v-model="pageHook.list"
:default-page-size="10"
safe-area-inset-bottom
:loading-more-enabled="true"
:show-scrollbar="false"
@query="query"
:auto-clean-list-when-reload="false"
:auto-scroll-to-top-when-reload="false"
@scroll="pageHook.scroll"
>
<view class="h-20rpx" />
<view class="flex-row card p-20rpx m-b-20rpx" v-for="(v, i) in pageHook.list" :key="i" @tap="toDetail(v)">
<image class="w-180rpx h-180rpx rounded-12rpx" :src="filePath(v.logoPic)" mode="aspectFill" />
<view class="flex-1 flex-col m-l-20rpx">
<view class="flex-row items-center justify-between">
<view class="flex-row-center sub-text-34">{{ v.name }}</view>
<tm-tag size="s">{{ v.deptName }}</tm-tag>
</view>
<view class="m-t-10rpx">
<view v-if="v.phone" class="flex items-center">
<image class="w-24rpx h-24rpx" src="@/static/page/phone.svg" />
<view class="second-text-28 ml-1">{{ v.phone }}</view>
</view>
<view class="second-text-24">
<view class="second-text-28">{{ v.jobTime == ",," ? "" : "营业时间:" }}</view>
<view>{{ v.jobTime?.split(",")[0] ? "早:" + v.jobTime?.split(",")[0] : "" }}</view>
<view>{{ v.jobTime?.split(",")[1] ? "中:" + v.jobTime?.split(",")[1] : "" }}</view>
<view>{{ v.jobTime?.split(",")[2] ? "晚:" + v.jobTime?.split(",")[2] : "" }}</view>
</view>
<view v-if="v.addressDes" class="second-text-28">
<image class="w-24rpx h-24rpx" src="@/static/page/address.svg" />
<text class="ml-1">地址:</text>
<text class="m-l-10rpx">{{ v.addressDes }}</text>
</view>
</view>
</view>
</view>
</z-paging>
</crud-layout>
</template>
+2 -2
View File
@@ -3,8 +3,8 @@
export const isDevEnv = (): boolean => process.env.NODE_ENV === "development"
// 获取启动参数
export const getLaunchParam = (): "canteen" | "selfMerchant" | "coopMerchant" | "thirdApp" | null => {
return new URL(window.location.href).searchParams.get("launchParam") as "canteen" | "selfMerchant" | "coopMerchant" | "thirdApp" | null
export const getLaunchParam = (): string => {
return new URL(window.location.href).searchParams.get("launchParam") || ""
}
// 检测app信息判断运行环境
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB