Merge branch 'master' of https://gitea.zianxin.com/zax/biz-mobile-react-cloud
This commit is contained in:
commit
79000cbb4a
18 files changed
+1022
-132
No files matched your search
+2
-2
@@ -66,7 +66,7 @@ const tabbar: TabbarItem[] = [
|
||||
text: "共富商城",
|
||||
iconPath: "/static/tabbar/prosperity-shop.png",
|
||||
selectedIconPath: "/static/tabbar/prosperity-shop-select.png",
|
||||
launchParam: ["onlineShop"],
|
||||
launchParam: ["prosperityShop"],
|
||||
auth: ["user"],
|
||||
},
|
||||
{
|
||||
@@ -114,7 +114,7 @@ const tabbar: TabbarItem[] = [
|
||||
text: "我的",
|
||||
iconPath: "/static/tabbar/my.png",
|
||||
selectedIconPath: "/static/tabbar/my-select.png",
|
||||
launchParam: ["canteen", "selfMerchant", "onlineShop", "coopMerchant", "integrated"],
|
||||
launchParam: ["canteen", "selfMerchant", "onlineShop", "coopMerchant", "integrated", "prosperityShop"],
|
||||
auth: ["user", "merchant", "doorAdmin"],
|
||||
},
|
||||
]
|
||||
|
||||
@@ -135,6 +135,46 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"root": "pages/prosperity-shop",
|
||||
"pages": [
|
||||
{
|
||||
"path": "goods-detail",
|
||||
"style": {
|
||||
"navigationBarTitleText": "商品详情",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "shop-detail",
|
||||
"style": {
|
||||
"navigationBarTitleText": "超市详情",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "goods-search",
|
||||
"style": {
|
||||
"navigationBarTitleText": "商品搜索",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "goods-manage",
|
||||
"style": {
|
||||
"navigationBarTitleText": "商品管理",
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "goods-form",
|
||||
"style": {
|
||||
"navigationBarTitleText": "商品信息管理",
|
||||
"disableScroll": true
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"root": "pages/apply",
|
||||
"pages": [
|
||||
|
||||
@@ -251,7 +251,7 @@ const doFood = () => {
|
||||
<view class="m-x-30rpx">
|
||||
<!-- start 简介 start -->
|
||||
<view class="h-15rpx" />
|
||||
<view flex-row card p-20rpx>
|
||||
<view class="flex-row card p-20rpx">
|
||||
<image class="w-180rpx h-180rpx rounded-12rpx" :src="filePath(state.canteenDetail.logoPic)" mode="aspectFill" />
|
||||
<view class="flex-1 flex-col m-l-20rpx">
|
||||
<view class="flex-row items-center justify-between">
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
<template>
|
||||
<crud-layout mainIsScroll bg="/static/page/blue-bg.png">
|
||||
<image :src="filePath(state.detail.coverImage)" class="w-full h-500rpx bg-#eeeeee" 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-34 line-through">¥</text>
|
||||
<text class="font-bold second-text-40 line-through mr-2">{{ state.detail.originalPrice.toFixed(2) }}</text>
|
||||
<text class="text-34rpx color-red!">¥</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-red 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 mx-1">{{ 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 card p-20rpx mx-20rpx">
|
||||
<view class="prefix_title">包装单位</view>
|
||||
<view class="bg-#1879ff09 rounded-1 my-2 p-3" v-for="(item, index) in state.detail.skuList" :key="index">
|
||||
<view class="sub-text-30">{{ item.skuName }}</view>
|
||||
<view class="second-text-28 mt-1 flex justify-between items-center">
|
||||
<text>售价 {{ displayPrice(item.price) }}</text>
|
||||
<text>重量 {{ item.weightG }}克</text>
|
||||
<text>库存 {{ item.stock }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="mt-20rpx 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>
|
||||
<view class="h-30rpx" />
|
||||
</crud-layout>
|
||||
</template>
|
||||
|
||||
<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"
|
||||
import { displayPrice } from "@/sheep/utils/formatMoney"
|
||||
|
||||
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 80%, red 0);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,104 @@
|
||||
<template>
|
||||
<crud-layout title="" mainIsScroll bg="/static/page/blue-bg.png">
|
||||
<view class="h-30rpx" />
|
||||
<tm-form @submit="onsubmit" v-model="form" :rules="rules" direction="vertical">
|
||||
<view class="card p-20rpx mb-20rpx">
|
||||
<view class="prefix_title mb-2">商品基本信息</view>
|
||||
<tm-form-item name="title" label="商品标题" direction="horizontal">
|
||||
<tm-input v-model="form.title" type="text" :showClear="true" placeholder="请输入商品标题"></tm-input>
|
||||
</tm-form-item>
|
||||
<tm-form-item name="unit" label="计量单位" direction="horizontal">
|
||||
<tm-input v-model="form.unit" type="text" :showClear="true" placeholder="请输入计量单位"></tm-input>
|
||||
</tm-form-item>
|
||||
<tm-form-item name="status" label="状态" direction="horizontal">
|
||||
<tm-radio-group v-model="form.status" align="flex-end">
|
||||
<tm-radio :value="index.toString()" :label="item" v-for="(item, index) in ['上架', '下架']" :key="index"></tm-radio>
|
||||
</tm-radio-group>
|
||||
</tm-form-item>
|
||||
<tm-form-item name="saleStartTime" label="开售时间" direction="horizontal">
|
||||
<tm-picker-date v-model="form.saleStartTime" v-model:modelStr="_saleStartTimeStr">
|
||||
<tm-input
|
||||
:modelValue="_saleStartTimeStr"
|
||||
placeholder="请选择开售时间"
|
||||
style="pointer-events: none"
|
||||
disabled
|
||||
align="right"
|
||||
color="transparent"
|
||||
>
|
||||
<template v-slot:rightIcon>
|
||||
<tm-icon name="arrow-right-s-line"></tm-icon>
|
||||
</template>
|
||||
</tm-input>
|
||||
</tm-picker-date>
|
||||
</tm-form-item>
|
||||
<tm-form-item name="saleEndTime" label="停售时间" direction="horizontal">
|
||||
<tm-picker-date v-model="form.saleEndTime" v-model:modelStr="_saleEndTimeStr">
|
||||
<tm-input
|
||||
:modelValue="_saleEndTimeStr"
|
||||
placeholder="请选择停售时间"
|
||||
style="pointer-events: none"
|
||||
disabled
|
||||
align="right"
|
||||
color="transparent"
|
||||
>
|
||||
<template v-slot:rightIcon>
|
||||
<tm-icon name="arrow-right-s-line"></tm-icon>
|
||||
</template>
|
||||
</tm-input>
|
||||
</tm-picker-date>
|
||||
</tm-form-item>
|
||||
<tm-form-item name="coverImage" label="商品图">
|
||||
<tm-upload-photo
|
||||
v-model="form.coverImage"
|
||||
:maxFileSize="1024 * 1024 * 5"
|
||||
:url="'/api/admin/sys-file/upload'"
|
||||
:header="hooks.useDefaultHeader()"
|
||||
:column="2"
|
||||
img-height="300"
|
||||
:max-count="1"
|
||||
/>
|
||||
</tm-form-item>
|
||||
<view class="sub-text-20 c-red! mb-2">注:图片大小不超过5M</view>
|
||||
<tm-form-item name="description" label="详情描述">
|
||||
<tm-input v-model="form.description" type="textarea" :showClear="false" placeholder="请输入详情描述"></tm-input>
|
||||
</tm-form-item>
|
||||
</view>
|
||||
</tm-form>
|
||||
|
||||
<view class="card p-x-20rpx p-y-30rpx m-b-20rpx">
|
||||
<tm-picker
|
||||
rang-key="title"
|
||||
rang-text="title"
|
||||
v-model="applyForm.reason"
|
||||
v-model:model-str="applyForm.reasonStr"
|
||||
:list="state.selectList"
|
||||
>
|
||||
<view class="flex-row items-center justify-between">
|
||||
<view class="sub-text-28">申请原因</view>
|
||||
<view class="flex-row items-center second-text-24">
|
||||
<text>{{ applyForm.reasonStr || "请选择申请原因" }} </text>
|
||||
<text class="m-l-10rpx color-#b1b1b1 iconfont icon-xiangxiajiantouxiao" />
|
||||
</view>
|
||||
</view>
|
||||
</tm-picker>
|
||||
</view>
|
||||
|
||||
<view class="h-30rpx" />
|
||||
|
||||
<template #main-bottom>
|
||||
<view class="bg-#0094ff py-20rpx text-center rounded-40rpx m-30rpx sub-text-30 color-white!" @tap="doSubmit"> 保存 </view>
|
||||
</template>
|
||||
</crud-layout>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { reactive, ref } from "vue"
|
||||
import { hooks } from "@@/index"
|
||||
|
||||
const form = reactive<any>({})
|
||||
const _saleStartTimeStr = ref("")
|
||||
const _saleEndTimeStr = ref("")
|
||||
|
||||
// 保存
|
||||
const doSubmit = async () => {}
|
||||
</script>
|
||||
@@ -0,0 +1,206 @@
|
||||
<template>
|
||||
<crud-layout mainIsScroll 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"
|
||||
>
|
||||
<template #top>
|
||||
<view class="w-full mt-2 flex items-center gap-2">
|
||||
<tm-button size="m" @click="addGoods">新 增</tm-button>
|
||||
<tm-button size="m">分 类</tm-button>
|
||||
<tm-input
|
||||
class="flex-1"
|
||||
v-model="state.params.keyword"
|
||||
type="text"
|
||||
height="72"
|
||||
minHeight="72"
|
||||
fontSize="28"
|
||||
:showClear="true"
|
||||
:focus="false"
|
||||
placeholder="请输入商品名称"
|
||||
style="border: 4rpx solid #0779fd; border-radius: 14rpx"
|
||||
:focus-highlight="false"
|
||||
color="#ECF4F9"
|
||||
@clear="inputClear"
|
||||
>
|
||||
<template v-slot:rightIcon>
|
||||
<tm-button class="mr-1 px-3 py-26rpx text-30rpx!" size="xs" @click="search">搜 索</tm-button>
|
||||
</template>
|
||||
</tm-input>
|
||||
</view>
|
||||
<scroll-view scroll-x>
|
||||
<view class="w-full flex my-2">
|
||||
<view
|
||||
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.category"
|
||||
:key="i"
|
||||
@tap="doClassify(v.id)"
|
||||
>
|
||||
{{ v.name }}
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</template>
|
||||
|
||||
<view v-for="v in pageHook.list" :key="v.id" class="card mx-20rpx mb-2 px-4 py-2">
|
||||
<view class="flex">
|
||||
<image
|
||||
:src="filePath(v.coverImage)"
|
||||
class="size-180rpx rounded-12rpx bg-#eeeeee"
|
||||
mode="aspectFill"
|
||||
@click.stop="hooks.usePreviewImage(filePath(v.coverImage))"
|
||||
/>
|
||||
<view class="flex-1 ml-2 overflow-hidden">
|
||||
<view class="truncate sub-text-34">{{ v.title }}</view>
|
||||
<view class="sub-text-26 mt-4rpx">
|
||||
<text class="c-#0094ff! px-2 bg-#0094ff22 rounded-2 mr-2">
|
||||
{{ state.category.find((item) => item.id == v.categoryId)?.name || "未知分类" }}
|
||||
</text>
|
||||
<text class="c-#0094ff! px-2 bg-#0094ff22 rounded-2">{{ v.status == "0" ? "在售" : "未售" }}</text>
|
||||
</view>
|
||||
<view class="mt-4rpx">
|
||||
<text class="second-text-28 mr-2">单位 {{ v.unit }}</text>
|
||||
<text class="second-text-28">库存 {{ v.totalStock }}</text>
|
||||
</view>
|
||||
<view>
|
||||
<text class="second-text-32 line-through mr-1">
|
||||
<text class="text-30rpx">¥</text>
|
||||
<text class="font-bold">{{ v.originalPrice.toFixed(2) }}</text>
|
||||
</text>
|
||||
<text class="color-red">
|
||||
<text class="text-30rpx">¥</text>
|
||||
<text class="text-46rpx font-bold">{{ v.spuPrice.toFixed(2).split(".")[0] }}.</text>
|
||||
<text class="text-38rpx font-bold">{{ v.spuPrice.toFixed(2).split(".")[1] }}</text>
|
||||
</text>
|
||||
<text class="view-clip px-1 text-20rpx c-white bg-red ml-1 rounded-r-1 rounded-lt-1">到手价</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="my-2 h-1rpx bg-#A0A0A044"></view>
|
||||
<view class="flex gap-2 justify-end">
|
||||
<tm-button class="px-2" size="s" :linear="['to right', '#FFC328', '#E2000099']" @click.stop="deleteGoods(v.id)">
|
||||
删除商品
|
||||
</tm-button>
|
||||
<tm-button class="px-2" size="s" :linear="['to right', '#62abff', '#0779FD99']" @click.stop="editGoods(v.id)">
|
||||
编辑商品
|
||||
</tm-button>
|
||||
<tm-button class="px-2" size="s" :linear="['to right', '#00B960', '#00552C99']" @click.stop="doGoodsDetail(v.id)">
|
||||
查看详情
|
||||
</tm-button>
|
||||
</view>
|
||||
</view>
|
||||
</z-paging>
|
||||
</crud-layout>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { reactive, ref } from "vue"
|
||||
import { prosperityShopApi } from "@/sheep/api"
|
||||
import { hooks } from "@/sheep"
|
||||
import { filePath } from "@/sheep/utils/file"
|
||||
import { onLoad } from "@dcloudio/uni-app"
|
||||
|
||||
const state = reactive<any>({
|
||||
params: {
|
||||
keyword: "",
|
||||
categoryId: "",
|
||||
},
|
||||
category: [],
|
||||
curCategoryIndex: 0,
|
||||
})
|
||||
|
||||
const pageRef = ref<any>(null)
|
||||
const pageHook = hooks.usePage(pageRef, prosperityShopApi.getGoodsList, null, "data.records")
|
||||
|
||||
const query = (page: number, pagesize: number) => {
|
||||
pageHook.startPage(page, pagesize, false, state.params)
|
||||
}
|
||||
|
||||
onLoad(async () => {
|
||||
await getGoodsCategory()
|
||||
})
|
||||
|
||||
const getGoodsCategory = async () => {
|
||||
const { code, data, msg } = await prosperityShopApi.getGoodsCategory()
|
||||
if (code != 0) return uni.showToast({ title: msg || "商品分类获取失败", icon: "none" })
|
||||
state.category = data ?? []
|
||||
state.category.unshift({ id: "", name: "全部分类" })
|
||||
}
|
||||
|
||||
// 切换分类
|
||||
const doClassify = async (categoryId: string) => {
|
||||
if (state.params.categoryId == categoryId) return
|
||||
state.params.categoryId = categoryId
|
||||
pageRef.value && pageRef.value.reload(false)
|
||||
}
|
||||
|
||||
// 点击搜索
|
||||
const search = () => {
|
||||
pageRef.value && pageRef.value.reload(false)
|
||||
}
|
||||
|
||||
const inputClear = () => {
|
||||
pageRef.value && pageRef.value.reload(false)
|
||||
}
|
||||
|
||||
const doGoodsDetail = (id: string) => {
|
||||
if (!id) return uni.showToast({ title: "商品id不存在", icon: "none" })
|
||||
uni.navigateTo({
|
||||
url: `/pages/prosperity-shop/goods-detail?id=${id}`,
|
||||
})
|
||||
}
|
||||
|
||||
// 新增商品
|
||||
const addGoods = () => {
|
||||
uni.navigateTo({ url: `/pages/prosperity-shop/goods-form` })
|
||||
}
|
||||
|
||||
// 编辑商品
|
||||
const editGoods = (id: string) => {
|
||||
if (!id) return uni.showToast({ title: "商品id不存在", icon: "none" })
|
||||
uni.navigateTo({ url: `/pages/prosperity-shop/goods-form?id=${id}` })
|
||||
}
|
||||
|
||||
// 删除商品
|
||||
const deleteGoods = (id: string) => {
|
||||
if (!id) return uni.showToast({ title: "商品id不存在", icon: "none" })
|
||||
uni.showModal({
|
||||
title: "确定删除商品吗?",
|
||||
content: "",
|
||||
success: async (res) => {
|
||||
if (res.confirm) {
|
||||
const { code } = await prosperityShopApi.deleteGoods(id)
|
||||
if (code != 0) return uni.showToast({ title: "删除失败", icon: "error" })
|
||||
uni.showToast({ title: "删除成功", icon: "none" })
|
||||
pageRef.value && pageRef.value.reload(false)
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.view-clip {
|
||||
position: relative;
|
||||
&::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: -18rpx;
|
||||
bottom: 0rpx;
|
||||
width: 20rpx;
|
||||
height: 20rpx;
|
||||
background: radial-gradient(circle at 0 0, transparent 80%, red 0);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,106 @@
|
||||
<template>
|
||||
<crud-layout mainIsScroll 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"
|
||||
>
|
||||
<template #top>
|
||||
<view class="w-full my-2">
|
||||
<tm-input
|
||||
class="flex-1"
|
||||
v-model="state.params.keyword"
|
||||
type="text"
|
||||
height="72"
|
||||
minHeight="72"
|
||||
fontSize="28"
|
||||
:showClear="true"
|
||||
:focus="false"
|
||||
placeholder="请输入商品名称"
|
||||
style="border: 4rpx solid #0779fd; border-radius: 14rpx"
|
||||
:focus-highlight="false"
|
||||
color="#ECF4F9"
|
||||
>
|
||||
<template v-slot:rightIcon>
|
||||
<tm-button class="mr-1 px-3 py-26rpx text-30rpx!" size="xs" @click="search">搜 索</tm-button>
|
||||
</template>
|
||||
</tm-input>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<view v-for="v in pageHook.list" :key="v.id" class="card flex mx-20rpx mb-2 p-2" @click="doGoodsDetail(v.id)">
|
||||
<image :src="filePath(v.coverImage)" class="size-160rpx rounded-12rpx bg-#eeeeee" 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="second-text-28 line-through mr-1">
|
||||
<text class="text-26rpx">¥</text>
|
||||
<text class="font-bold">{{ v.originalPrice.toFixed(2) }}</text>
|
||||
</text>
|
||||
<text class="color-red">
|
||||
<text class="text-26rpx">¥</text>
|
||||
<text class="text-32rpx font-bold">{{ v.spuPrice.toFixed(2).split(".")[0] }}.</text>
|
||||
<text class="text-30rpx font-bold">{{ v.spuPrice.toFixed(2).split(".")[1] }}</text>
|
||||
</text>
|
||||
<text class="view-clip px-1 text-18rpx c-white bg-red ml-1 rounded-r-1 rounded-lt-1">到手价</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</z-paging>
|
||||
</crud-layout>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { hooks } from "@/sheep"
|
||||
import { prosperityShopApi } from "@/sheep/api"
|
||||
import { reactive, ref } from "vue"
|
||||
import { filePath } from "@/sheep/utils/file"
|
||||
import { onLoad } from "@dcloudio/uni-app"
|
||||
|
||||
const state = reactive<any>({
|
||||
params: {
|
||||
keyword: "",
|
||||
merchantId: "",
|
||||
},
|
||||
})
|
||||
const pageRef = ref<any>(null)
|
||||
const pageHook = hooks.usePage(pageRef, prosperityShopApi.getGoodsList, null, "data.records")
|
||||
|
||||
const query = (page: number, pagesize: number) => {
|
||||
pageHook.startPage(page, pagesize, false, state.params)
|
||||
}
|
||||
|
||||
onLoad((options: any) => {
|
||||
const { merchantId } = options
|
||||
if (!merchantId) return uni.showToast({ title: "参数错误", icon: "none" })
|
||||
state.params.merchantId = merchantId
|
||||
})
|
||||
|
||||
// 点击搜索
|
||||
const search = () => {
|
||||
pageRef.value && pageRef.value.reload(false)
|
||||
}
|
||||
|
||||
const doGoodsDetail = (id: string) => {
|
||||
if (!id) return uni.showToast({ title: "商品id不存在", icon: "none" })
|
||||
uni.navigateTo({
|
||||
url: `/pages/prosperity-shop/goods-detail?id=${id}`,
|
||||
})
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,271 @@
|
||||
<template>
|
||||
<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 bg-#eeeeee" mode="aspectFill" />
|
||||
<view class="absolute w-full h-full top-0 left-0" :style="filterStyle"></view>
|
||||
</view>
|
||||
<view
|
||||
class="fixed right-20 h-72rpx bg-#ECF4F9cc px-20rpx top-20 flex items-center flex-nowrap overflow-hidden rounded-20rpx"
|
||||
:style="searchStyle"
|
||||
@click="goSearch"
|
||||
>
|
||||
<image class="w-36rpx h-36rpx" src="@/static/page/shop-goods-search.png" />
|
||||
<text v-if="scrollDetail.scrollTop > 30" class="bg-#eeeeee44 whitespace-nowrap overflow-hidden second-text-26 ml-2">搜索商品</text>
|
||||
</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 }}·{{ 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" />
|
||||
<text>{{ state.detail.address }}</text>
|
||||
</view>
|
||||
<marquee class="second-text-24 mt-1">{{ state.detail.remark }}</marquee>
|
||||
</view>
|
||||
|
||||
<view class="flex mt-2">
|
||||
<scroll-view scroll-y class="menu w-200rpx mb-20rpx bg-#f6f6f6 pl-20rpx" style="height: calc(100vh - 420rpx)">
|
||||
<view
|
||||
class="menu_item flex-row-center"
|
||||
v-for="(v, i) in state.category"
|
||||
:key="v.id"
|
||||
:class="[{ menu_item_active: i == state.curCategoryIndex }]"
|
||||
@tap="doCategory(i)"
|
||||
>
|
||||
<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 overflow-hidden pb-20rpx flex"
|
||||
style="height: calc(100vh - 420rpx)"
|
||||
>
|
||||
<view v-for="v in state.goodsList" :key="v.id" class="card flex mx-15rpx mb-2 p-2" @click="doGoodsDetail(v.id)">
|
||||
<image :src="filePath(v.coverImage)" class="size-160rpx rounded-12rpx bg-#eeeeee" 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="second-text-28 line-through mr-1">
|
||||
<text class="text-26rpx">¥</text>
|
||||
<text class="font-bold">{{ v.originalPrice.toFixed(2) }}</text>
|
||||
</text>
|
||||
<text class="color-red">
|
||||
<text class="text-26rpx">¥</text>
|
||||
<text class="text-32rpx font-bold">{{ v.spuPrice.toFixed(2).split(".")[0] }}.</text>
|
||||
<text class="text-30rpx font-bold">{{ v.spuPrice.toFixed(2).split(".")[1] }}</text>
|
||||
</text>
|
||||
<text class="view-clip px-1 text-18rpx c-white bg-red ml-1 rounded-r-1 rounded-lt-1">到手价</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
<view v-else class="mx-auto mt-10 second-text-28">暂无商品</view>
|
||||
</view>
|
||||
</crud-layout>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
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"
|
||||
|
||||
interface scrollDetail {
|
||||
deltaX: number
|
||||
deltaY: number
|
||||
scrollHeight: number
|
||||
scrollLeft: number
|
||||
scrollTop: number
|
||||
scrollWidth: number
|
||||
}
|
||||
|
||||
const state = reactive<any>({
|
||||
id: "",
|
||||
detail: {},
|
||||
category: [],
|
||||
curCategoryIndex: 0,
|
||||
goodsList: [],
|
||||
})
|
||||
const scrollDetail = ref<scrollDetail>({} as scrollDetail)
|
||||
|
||||
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 getGoodsCategoryTree(option.id)
|
||||
await getGoodsList()
|
||||
uni.hideLoading()
|
||||
})
|
||||
|
||||
const getShopDetail = async (id: string) => {
|
||||
const { code, data, msg } = await prosperityShopApi.getShopDetail(id)
|
||||
if (code != 0) return uni.showToast({ title: msg || "超市详情获取失败", icon: "none" })
|
||||
state.detail = data
|
||||
}
|
||||
|
||||
const getGoodsCategoryTree = async (id: string) => {
|
||||
const { code, data, msg } = await prosperityShopApi.getGoodsCategoryTree({ merchantId: id })
|
||||
if (code != 0) return uni.showToast({ title: msg || "商品分类获取失败", icon: "none" })
|
||||
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) => {
|
||||
scrollDetail.value = e.detail
|
||||
}
|
||||
|
||||
const filterStyle = computed(() => {
|
||||
const scrollY = scrollDetail.value.scrollTop
|
||||
const blur = Math.min(1, scrollY / 400) * 20
|
||||
return {
|
||||
backdropFilter: `blur(${blur}rpx)`,
|
||||
}
|
||||
})
|
||||
|
||||
const searchStyle = computed(() => {
|
||||
const minWidth = 10
|
||||
const scrollY = scrollDetail.value.scrollTop
|
||||
const scrollHeight = scrollDetail.value.scrollHeight
|
||||
const scrollWidth = scrollDetail.value.scrollWidth
|
||||
const calcWidth = (scrollY / scrollHeight) * scrollWidth
|
||||
if (isNaN(calcWidth)) return { width: `${minWidth}%` }
|
||||
if (calcWidth < 2) return { width: `${calcWidth + minWidth}%` }
|
||||
return {
|
||||
width: `${calcWidth + minWidth}%`,
|
||||
border: `2rpx solid #1879ff`,
|
||||
opacity: (calcWidth / 100) * 2,
|
||||
}
|
||||
})
|
||||
|
||||
const doGoodsDetail = (id: string) => {
|
||||
if (!id) return uni.showToast({ title: "商品id不存在", icon: "none" })
|
||||
uni.navigateTo({
|
||||
url: `/pages/prosperity-shop/goods-detail?id=${id}`,
|
||||
})
|
||||
}
|
||||
|
||||
const goSearch = () => {
|
||||
if (!state.detail.id) return uni.showToast({ title: "超市id不存在", icon: "none" })
|
||||
uni.navigateTo({ url: `/pages/prosperity-shop/goods-search?merchantId=${state.detail.id}` })
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep() {
|
||||
.layout_main > scroll-view {
|
||||
padding: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.menu {
|
||||
&_item {
|
||||
width: 100%;
|
||||
height: 88rpx;
|
||||
position: relative;
|
||||
transition: all linear 0.2s;
|
||||
|
||||
&_title {
|
||||
width: 100%;
|
||||
line-height: 32rpx;
|
||||
font-size: 30rpx;
|
||||
font-weight: 400;
|
||||
color: #333;
|
||||
margin-left: 28rpx;
|
||||
position: relative;
|
||||
z-index: 0;
|
||||
|
||||
&::before {
|
||||
content: "";
|
||||
width: 64rpx;
|
||||
height: 12rpx;
|
||||
background: linear-gradient(90deg, #0094ff, #e9f5fe) !important;
|
||||
position: absolute;
|
||||
left: -64rpx;
|
||||
bottom: 0;
|
||||
z-index: -1;
|
||||
transition: all linear 0.2s;
|
||||
}
|
||||
}
|
||||
|
||||
&.menu_item_active {
|
||||
background-color: #fff;
|
||||
border-radius: 20rpx 0 0 20rpx;
|
||||
|
||||
&::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: -20rpx;
|
||||
width: 20rpx;
|
||||
height: 20rpx;
|
||||
background: radial-gradient(circle at 0 100%, transparent 20rpx, #fff 0);
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: -20rpx;
|
||||
right: 0;
|
||||
width: 20rpx;
|
||||
height: 20rpx;
|
||||
background: radial-gradient(circle at 0% 0%, transparent 20rpx, #fff 0);
|
||||
}
|
||||
|
||||
.menu_item_title {
|
||||
font-weight: 600;
|
||||
|
||||
&::before {
|
||||
left: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.view-clip {
|
||||
position: relative;
|
||||
&::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: -18rpx;
|
||||
bottom: 0rpx;
|
||||
width: 20rpx;
|
||||
height: 20rpx;
|
||||
background: radial-gradient(circle at 0 0, transparent 80%, red 0);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -28,6 +28,12 @@ const moduleList = [
|
||||
iconPath: "/static/tabbar/online-shop-select.png",
|
||||
launchParam: "onlineShop",
|
||||
},
|
||||
{
|
||||
pagePath: "/pages/tabbar/prosperityShop",
|
||||
text: "共富商城",
|
||||
iconPath: "/static/tabbar/prosperity-shop-select.png",
|
||||
launchParam: "prosperityShop",
|
||||
},
|
||||
{
|
||||
pagePath: "/pages/tabbar/cabinet",
|
||||
text: "智能柜",
|
||||
@@ -106,6 +112,10 @@ const jumpByParam = async () => {
|
||||
uni.setStorageSync("launchParam", launchParam)
|
||||
return uni.reLaunch({ url: "/pages/tabbar/onlineShop" })
|
||||
}
|
||||
if (launchParam === "prosperityShop") {
|
||||
uni.setStorageSync("launchParam", launchParam)
|
||||
return uni.reLaunch({ url: "/pages/tabbar/prosperityShop" })
|
||||
}
|
||||
if (launchParam === "coopMerchant") {
|
||||
uni.setStorageSync("launchParam", launchParam)
|
||||
return uni.reLaunch({ url: "/pages/tabbar/coopMerchant" })
|
||||
|
||||
+18
-10
@@ -3,10 +3,13 @@
|
||||
<tm-sticky>
|
||||
<tm-sticky-header>
|
||||
<view class="flex-row items-center bg-#ECF4F9 py-3">
|
||||
<image v-if="state.userInfo?.sysUser?.avatar" class="w-100rpx h-100rpx rounded-full"
|
||||
:src="filePath(state.userInfo.sysUser.avatar)" mode="aspectFill" />
|
||||
<image v-else class="w-100rpx h-100rpx rounded-full" src="/static/icon/default-avatar.png"
|
||||
mode="aspectFill" />
|
||||
<image
|
||||
v-if="state.userInfo?.sysUser?.avatar"
|
||||
class="w-100rpx h-100rpx rounded-full"
|
||||
:src="filePath(state.userInfo.sysUser.avatar)"
|
||||
mode="aspectFill"
|
||||
/>
|
||||
<image v-else class="w-100rpx h-100rpx rounded-full" src="/static/icon/default-avatar.png" mode="aspectFill" />
|
||||
<view class="m-l-30rpx" v-if="state.userInfo?.sysUser?.userId">
|
||||
<view class="flex-row items-center">
|
||||
<text class="sub-text-30">{{ state.userInfo?.sysUser?.name }}</text>
|
||||
@@ -22,8 +25,7 @@
|
||||
</tm-sticky-header>
|
||||
|
||||
<view class="m-t-10rpx rounded-20rpx shadow-main p-30rpx p-b-40rpx bg-white!">
|
||||
<view class="service_item flex-row items-center justify-between" v-for="(v, i) in state.menuList"
|
||||
:key="i" @tap="onTabMenu(v)">
|
||||
<view class="service_item flex-row items-center justify-between" v-for="(v, i) in state.menuList" :key="i" @tap="onTabMenu(v)">
|
||||
<view class="flex-row-center">
|
||||
<image class="w-40rpx h-40rpx" :src="v.icon" mode="aspectFill" />
|
||||
<text class="sub-text-28 m-l-20rpx">{{ v.text }}</text>
|
||||
@@ -33,15 +35,13 @@
|
||||
</view>
|
||||
|
||||
<view v-if="isDevEnv()" class="flex-col-center m-t-40rpx m-x-30rpx">
|
||||
<view class="w-full text-center sub-text-28 p-y-20rpx rounded-40rpx border-solid border border-#0094ff"
|
||||
@tap="doLogout">
|
||||
<view class="w-full text-center sub-text-28 p-y-20rpx rounded-40rpx border-solid border border-#0094ff" @tap="doLogout">
|
||||
退出登录
|
||||
</view>
|
||||
</view>
|
||||
<view class="h-30rpx" />
|
||||
<view v-if="state.appInfo == APPENV.SH" class="flex-col-center m-t-40rpx m-x-30rpx">
|
||||
<view class="w-full text-center sub-text-28 p-y-20rpx rounded-40rpx border-solid border border-#0094ff"
|
||||
@tap="doChangeRole">
|
||||
<view class="w-full text-center sub-text-28 p-y-20rpx rounded-40rpx border-solid border border-#0094ff" @tap="doChangeRole">
|
||||
切换角色
|
||||
</view>
|
||||
</view>
|
||||
@@ -426,10 +426,18 @@ onLoad(async () => {
|
||||
|
||||
await userApi.userRole()
|
||||
|
||||
await isMerchant()
|
||||
|
||||
// 获取存储中的用户信息
|
||||
storage.getUserInfo()
|
||||
})
|
||||
|
||||
const isMerchant = async () => {
|
||||
const { data } = await userApi.isMerchant()
|
||||
if (data)
|
||||
state.menuList.unshift({ text: "共富商城商品管理", icon: "/static/icon/service_4.png", router: "/pages/prosperity-shop/goods-manage" })
|
||||
}
|
||||
|
||||
onShow(() => {
|
||||
// 获取存储中的用户信息
|
||||
storage.getUserInfo()
|
||||
|
||||
@@ -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) {
|
||||
@@ -225,7 +216,7 @@ const getClassify = async () => {
|
||||
state.classifyList.unshift({ id: "", name: "全部分类" })
|
||||
if (!isEmpty(state.classifyList)) {
|
||||
state.params.categoryId = state.classifyList[0].id
|
||||
pageRef.value && pageRef.value.reload(true)
|
||||
pageRef.value && pageRef.value.reload(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -243,20 +234,20 @@ const confirmRadioMerchant = async () => {
|
||||
state.params.merchantId = state.curMerchant.id
|
||||
cartStore.saveShopMerchantId(state.curMerchant.id)
|
||||
await cartStore.getShopCart()
|
||||
pageRef.value && pageRef.value.reload(true)
|
||||
pageRef.value && pageRef.value.reload(false)
|
||||
}
|
||||
|
||||
// 点击搜索
|
||||
const searchProduct = () => {
|
||||
state.params.categoryId = ""
|
||||
pageRef.value && pageRef.value.reload(true)
|
||||
pageRef.value && pageRef.value.reload(false)
|
||||
}
|
||||
|
||||
// 切换分类
|
||||
const doClassify = async (categoryId: string) => {
|
||||
if (state.params.categoryId == categoryId) return
|
||||
state.params.categoryId = categoryId
|
||||
pageRef.value && pageRef.value.reload(true)
|
||||
pageRef.value && pageRef.value.reload(false)
|
||||
}
|
||||
|
||||
const pageHook = hooks.usePage(pageRef, onlineShopApi.getSpuList, null, "data.records")
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
<tm-text font-size="40" color="#0779FD" class="font-bold">共富商城</tm-text>
|
||||
<tm-text font-size="30" color="#0779FD">优质商户,品质商品,欢迎联系购买</tm-text>
|
||||
</view>
|
||||
<view class="w-full flex items-center">
|
||||
<view class="w-full flex items-center mb-2">
|
||||
<tm-input
|
||||
class="flex-1"
|
||||
v-model="state.params.keyword"
|
||||
@@ -40,10 +40,10 @@
|
||||
</tm-input>
|
||||
</view>
|
||||
|
||||
<scroll-view scroll-x>
|
||||
<!-- <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"
|
||||
@@ -52,24 +52,24 @@
|
||||
{{ v.name }}
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</scroll-view> -->
|
||||
</template>
|
||||
|
||||
<view class="card p-20rpx mb-20rpx" v-for="(v, i) in pageHook.list" :key="i" @tap="toDetail(v)">
|
||||
<view class="flex">
|
||||
<image class="w-150rpx h-150rpx rounded-12rpx" :src="filePath(v.images)" mode="aspectFill" />
|
||||
<image class="w-150rpx h-150rpx bg-#eeeeee rounded-12rpx" :src="filePath(v.images)" mode="aspectFill" />
|
||||
<view class="ml-20rpx flex-1 overflow-hidden">
|
||||
<view class="sub-text-30 truncate">{{ v.name }}</view>
|
||||
<view class="second-text-26 truncate">
|
||||
<text class="mr-20rpx">{{ v.contactName }}</text>
|
||||
<text class="mr-20rpx">
|
||||
<image class="w-24rpx h-24rpx mr-1" src="@/static/page/phone.svg" />
|
||||
<text @tap="doPhone(v.contactPhone)">{{ v.contactPhone }}</text>
|
||||
<text @tap.stop="doPhone(v.contactPhone)">{{ v.contactPhone }}</text>
|
||||
</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" />
|
||||
@@ -80,7 +80,7 @@
|
||||
<scroll-view scroll-x>
|
||||
<view class="flex mt-2">
|
||||
<view class="w-160rpx flex-col items-center mr-20rpx" v-for="(pV, pI) in v.featuredProducts" :key="pI">
|
||||
<image class="w-160rpx h-160rpx rounded-12rpx" :src="filePath(pV.coverImage)" mode="aspectFill" />
|
||||
<image class="w-160rpx h-160rpx rounded-12rpx bg-#eeeeee" :src="filePath(pV.coverImage)" mode="aspectFill" />
|
||||
<view class="w-full mt-4rpx sub-text-24 truncate">{{ pV.title }}</view>
|
||||
<view class="w-full flex justify-between pr-1">
|
||||
<text class="main-text-26 color-red!">
|
||||
@@ -106,7 +106,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { hooks, router } from "@/sheep"
|
||||
import { onlineShopApi, prosperityShopApi } from "@/sheep/api"
|
||||
import { prosperityShopApi } from "@/sheep/api"
|
||||
import { onLoad } from "@dcloudio/uni-app"
|
||||
import { isEmpty } from "radash"
|
||||
import { reactive, ref } from "vue"
|
||||
@@ -116,10 +116,8 @@ import { APPENV } from "@/sheep/constant"
|
||||
|
||||
const state = reactive<any>({
|
||||
params: {
|
||||
categoryId: "",
|
||||
keyword: "",
|
||||
},
|
||||
classifyList: [],
|
||||
shopList: [],
|
||||
})
|
||||
const pageRef = ref<any>(null)
|
||||
@@ -132,40 +130,19 @@ const query = (page: number, pagesize: number) => {
|
||||
onLoad(async () => {
|
||||
const app = getApp()
|
||||
app._initTabbar()
|
||||
|
||||
await getClassify()
|
||||
})
|
||||
|
||||
// 获取分类
|
||||
const getClassify = async () => {
|
||||
const { data } = await onlineShopApi.getSpuCategory()
|
||||
state.classifyList = treeToList(data) ?? []
|
||||
state.classifyList.unshift({ id: "", name: "全部分类" })
|
||||
if (!isEmpty(state.classifyList)) {
|
||||
state.params.categoryId = state.classifyList[0].id
|
||||
}
|
||||
}
|
||||
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 doClassify = async (categoryId: string) => {
|
||||
if (state.params.categoryId == categoryId) return
|
||||
state.params.categoryId = categoryId
|
||||
pageRef.value && pageRef.value.reload(true)
|
||||
}
|
||||
// const doClassify = async (categoryId: string) => {
|
||||
// if (state.params.categoryId == categoryId) return
|
||||
// state.params.categoryId = categoryId
|
||||
// pageRef.value && pageRef.value.reload(false)
|
||||
// }
|
||||
|
||||
// 点击搜索
|
||||
const search = () => {
|
||||
state.params.categoryId = ""
|
||||
pageRef.value && pageRef.value.reload(true)
|
||||
// state.params.categoryId = ""
|
||||
pageRef.value && pageRef.value.reload(false)
|
||||
}
|
||||
|
||||
// 联系商家
|
||||
@@ -188,7 +165,7 @@ const doPhone = async (item: string) => {
|
||||
const toDetail = async (v: any) => {
|
||||
const isLogin = await hooks.useCheckLogin()
|
||||
if (isLogin) {
|
||||
// router.jump("/pages/shop/detail", { id: v.id })
|
||||
router.jump("/pages/prosperity-shop/shop-detail", { id: v.id })
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -27,7 +27,7 @@
|
||||
<view class="w-64 h-64 rounded-full flex-col-center mr-8">
|
||||
<image src="@/static/page/merchantRole.svg" />
|
||||
</view>
|
||||
<view class="main-text-32 mb-1">我是商户</view>
|
||||
<view class="main-text-32 mb-1">我是合作商户</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
|
||||
@@ -9,4 +9,49 @@ export default {
|
||||
params: { productLimit: 5 },
|
||||
})
|
||||
},
|
||||
// 获取商品分类列表
|
||||
getGoodsCategory: (params?: any): Promise<any> => {
|
||||
return request({
|
||||
url: "/supermarket/app/mall/merchant/category/list",
|
||||
method: "GET",
|
||||
params,
|
||||
})
|
||||
},
|
||||
// 获取超市详情
|
||||
getShopDetail: (id: string): Promise<any> => {
|
||||
return request({
|
||||
url: `/supermarket/app/mall/merchant/${id}`,
|
||||
method: "GET",
|
||||
})
|
||||
},
|
||||
// 获取超市商品分类列表树
|
||||
getGoodsCategoryTree: (params: { merchantId: string }): Promise<any> => {
|
||||
return request({
|
||||
url: `/supermarket/app/mall/category/tree`,
|
||||
method: "GET",
|
||||
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",
|
||||
})
|
||||
},
|
||||
// 删除商品
|
||||
deleteGoods: (id: string): Promise<any> => {
|
||||
return request({
|
||||
url: `/supermarket/app/mall/merchant/product/${id}`,
|
||||
method: "DELETE",
|
||||
})
|
||||
},
|
||||
}
|
||||
@@ -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
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 8.8 KiB |
@@ -1,54 +1,72 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, onMounted, onBeforeUnmount, watch, type PropType, getCurrentInstance, type ComponentInstance, onUpdated, nextTick, VueElement, inject } from 'vue';
|
||||
import { arrayNumberValid, arrayNumberValidByStyleMP, covetUniNumber, arrayNumberValidByStyleBorderColor, linearValid, getUnit, getUid } from "../../libs/tool";
|
||||
import { getDefaultColor, getDefaultColorObj, getOutlineColorObj, getTextColorObj, getThinColorObj } from "../../libs/colors";
|
||||
import { useTmConfig } from "../../libs/config";
|
||||
import pickerItem from './picker-item.vue';
|
||||
import {
|
||||
ref,
|
||||
computed,
|
||||
onMounted,
|
||||
onBeforeUnmount,
|
||||
watch,
|
||||
type PropType,
|
||||
getCurrentInstance,
|
||||
type ComponentInstance,
|
||||
onUpdated,
|
||||
nextTick,
|
||||
VueElement,
|
||||
inject,
|
||||
} from "vue"
|
||||
import {
|
||||
arrayNumberValid,
|
||||
arrayNumberValidByStyleMP,
|
||||
covetUniNumber,
|
||||
arrayNumberValidByStyleBorderColor,
|
||||
linearValid,
|
||||
getUnit,
|
||||
getUid,
|
||||
} from "../../libs/tool"
|
||||
import { getDefaultColor, getDefaultColorObj, getOutlineColorObj, getTextColorObj, getThinColorObj } from "../../libs/colors"
|
||||
import { useTmConfig } from "../../libs/config"
|
||||
import pickerItem from "./picker-item.vue"
|
||||
|
||||
const { config } = useTmConfig()
|
||||
type TM_PICKER_X_ITEM = Record<string, any>
|
||||
|
||||
|
||||
const boxHeight = ref(0)
|
||||
const id = ('tmPickerItem-' + getUid())
|
||||
const id = "tmPickerItem-" + getUid()
|
||||
const nowCurrentIndex = ref([0])
|
||||
let tid = 0
|
||||
let tid2 = 1
|
||||
|
||||
const emits = defineEmits(['changeDeep'])
|
||||
const emits = defineEmits(["changeDeep"])
|
||||
const props = defineProps({
|
||||
|
||||
list: {
|
||||
type: Array as PropType<TM_PICKER_X_ITEM[][]>,
|
||||
default: (): TM_PICKER_X_ITEM[] => [] as TM_PICKER_X_ITEM[]
|
||||
default: (): TM_PICKER_X_ITEM[] => [] as TM_PICKER_X_ITEM[],
|
||||
},
|
||||
|
||||
|
||||
selectedIndex: {
|
||||
type: Array as PropType<number[]>,
|
||||
default: (): number[] => [] as number[]
|
||||
default: (): number[] => [] as number[],
|
||||
},
|
||||
cellUnits: {
|
||||
type: Array as PropType<string[]>,
|
||||
default: (): string[] => [] as string[]
|
||||
default: (): string[] => [] as string[],
|
||||
},
|
||||
fontSize: {
|
||||
type: String,
|
||||
default: "30"
|
||||
default: "30",
|
||||
},
|
||||
/**
|
||||
* 自定义标识id
|
||||
*/
|
||||
rangKey: {
|
||||
type: String,
|
||||
default: "id"
|
||||
default: "id",
|
||||
},
|
||||
/**
|
||||
* 自定义标识文本字段名
|
||||
*/
|
||||
rangText: {
|
||||
type: String,
|
||||
default: "title"
|
||||
default: "title",
|
||||
},
|
||||
})
|
||||
const _fontSize = computed((): string => {
|
||||
@@ -62,34 +80,28 @@ const _fontSize = computed((): string => {
|
||||
return (sizeNumber * config.fontSizeScale).toString() + getUnit(props.fontSize)
|
||||
})
|
||||
|
||||
|
||||
|
||||
const refreshKey = ref(12)
|
||||
|
||||
const _cellUnits = computed((): string[] => props.cellUnits as string[])
|
||||
|
||||
const _list = computed((): TM_PICKER_X_ITEM[][] => props.list.slice(0))
|
||||
|
||||
|
||||
const _isDark = computed((): boolean => config.mode == 'dark')
|
||||
const _isDark = computed((): boolean => config.mode == "dark")
|
||||
|
||||
const _maskStyle = computed((): string => {
|
||||
if (_isDark.value) {
|
||||
return 'background-image:linear-gradient(180deg,rgba(0, 0, 0, 0),rgba(0, 0, 0, 0)),linear-gradient(0deg, rgba(0, 0, 0, 0),rgba(0, 0, 0, 0))'
|
||||
return "background-image:linear-gradient(180deg,rgba(0, 0, 0, 0),rgba(0, 0, 0, 0)),linear-gradient(0deg, rgba(0, 0, 0, 0),rgba(0, 0, 0, 0))"
|
||||
}
|
||||
return 'background-image:linear-gradient(180deg,rgba(255,255,255,0),rgba(255,255,255,0)),linear-gradient(0deg, rgba(255,255,255,0),rgba(255,255,255,0))'
|
||||
return "background-image:linear-gradient(180deg,rgba(255,255,255,0),rgba(255,255,255,0)),linear-gradient(0deg, rgba(255,255,255,0),rgba(255,255,255,0))"
|
||||
})
|
||||
const _selectedMaskStyle = computed((): string => {
|
||||
return _isDark.value ? 'rgba(255,255,255,0.05)' : 'rgba(0,0,0,0.05)'
|
||||
return _isDark.value ? "rgba(255,255,255,0.05)" : "rgba(0,0,0,0.05)"
|
||||
})
|
||||
|
||||
|
||||
|
||||
const onChange = (event: any) => {
|
||||
|
||||
if (event.detail.value.length == 0 || _list.value.length == 0) return;
|
||||
let indexs = event.detail.value!;
|
||||
if (indexs.join('') == nowCurrentIndex.value.join('')) return;
|
||||
if (event.detail.value.length == 0 || _list.value.length == 0) return
|
||||
let indexs = event.detail.value!
|
||||
if (indexs.join("") == nowCurrentIndex.value.join("")) return
|
||||
nowCurrentIndex.value = indexs
|
||||
emits("changeDeep", indexs)
|
||||
}
|
||||
@@ -102,18 +114,21 @@ onBeforeUnmount(() => {
|
||||
clearTimeout(tid2)
|
||||
})
|
||||
|
||||
watch(() => props.selectedIndex, (newvalue: number[]) => {
|
||||
if (newvalue.join('') == nowCurrentIndex.value.join('')) return;
|
||||
nowCurrentIndex.value = newvalue.slice(0)
|
||||
}, { deep: true })
|
||||
watch(_list, (newvalue) => {
|
||||
refreshKey.value += 1;
|
||||
|
||||
}, { deep: true })
|
||||
|
||||
|
||||
|
||||
|
||||
watch(
|
||||
() => props.selectedIndex,
|
||||
(newvalue: number[]) => {
|
||||
if (newvalue.join("") == nowCurrentIndex.value.join("")) return
|
||||
nowCurrentIndex.value = newvalue.slice(0)
|
||||
},
|
||||
{ deep: true },
|
||||
)
|
||||
watch(
|
||||
_list,
|
||||
(newvalue) => {
|
||||
refreshKey.value += 1
|
||||
},
|
||||
{ deep: true },
|
||||
)
|
||||
</script>
|
||||
<script lang="ts">
|
||||
export default {
|
||||
@@ -123,29 +138,41 @@ export default {
|
||||
addGlobalClass: true,
|
||||
multipleSlots: true,
|
||||
},
|
||||
};
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<view>
|
||||
<view class="tmPickerViewUnit" v-if="_cellUnits.length > 0">
|
||||
<view v-for="(item, index) in _cellUnits" :key="index" style="flex: 1;text-align: center;">
|
||||
<view v-for="(item, index) in _cellUnits" :key="index" style="flex: 1; text-align: center">
|
||||
<tm-text font-size="24" class="tmPickerViewUnitText">{{ item }}</tm-text>
|
||||
</view>
|
||||
</view>
|
||||
<picker-view :key="refreshKey" :immediate-change="true" :value="nowCurrentIndex" @change="onChange"
|
||||
:style="{ height: '250px', width: '100%' }" :mask-style="`${_maskStyle}`"
|
||||
:indicator-style="`height:50px;border:none;border-radius:10px`">
|
||||
<picker-view
|
||||
:key="refreshKey"
|
||||
:immediate-change="true"
|
||||
:value="nowCurrentIndex"
|
||||
@change="onChange"
|
||||
:style="{ height: '250px', width: '100%' }"
|
||||
:mask-style="`${_maskStyle}`"
|
||||
:indicator-style="`height:50px;border:none;border-radius:10px`"
|
||||
>
|
||||
<picker-view-column v-for="(item2, index2) in _list" :key="index2">
|
||||
<view
|
||||
:style="{ background: nowCurrentIndex[index2] == index ? _selectedMaskStyle : 'none', margin: '0 4px' }"
|
||||
v-for="(item, index) in item2" :key="index" class="tmPickerViewWrapCoumn">
|
||||
<text class="tmPickerViewWrapCoumnText" :style="{
|
||||
fontSize: _fontSize,
|
||||
lineHeight: 1,
|
||||
fontWeight: nowCurrentIndex[index2] == index ? 'bold' : 'inherit',
|
||||
opacity: item.disabled ? 0.4 : (nowCurrentIndex[index2] == index ? 1 : 0.6),
|
||||
color: _isDark ? 'rgba(255,255,255,0.8)' : '#000000'
|
||||
}">
|
||||
v-for="(item, index) in item2"
|
||||
:key="index"
|
||||
class="tmPickerViewWrapCoumn"
|
||||
>
|
||||
<text
|
||||
class="tmPickerViewWrapCoumnText"
|
||||
:style="{
|
||||
fontSize: _fontSize,
|
||||
lineHeight: 1,
|
||||
fontWeight: nowCurrentIndex[index2] == index ? 'bold' : 'inherit',
|
||||
opacity: item.disabled ? 0.4 : nowCurrentIndex[index2] == index ? 1 : 0.6,
|
||||
color: _isDark ? 'rgba(255,255,255,0.8)' : '#000000',
|
||||
}"
|
||||
>
|
||||
{{ item[rangText] || "" }}
|
||||
</text>
|
||||
</view>
|
||||
@@ -183,7 +210,6 @@ export default {
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
word-break: break-all;
|
||||
|
||||
}
|
||||
|
||||
.tmPickerView {
|
||||
@@ -221,7 +247,6 @@ export default {
|
||||
border-radius: 10px;
|
||||
margin: 0 3px;
|
||||
flex: 1;
|
||||
|
||||
}
|
||||
|
||||
.tmPickerContent {
|
||||
@@ -239,4 +264,4 @@ export default {
|
||||
border-radius: 10px;
|
||||
/* background-color: #f5f5f5; */
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
Reference in new issue
Block a user