Files
points-mall/supplied-h5/src/components/SupplierShop.vue
T

205 lines
7.9 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<main class="shop" :class="`shop--${shop.layout}`" :style="{ '--theme': shop.theme }">
<van-nav-bar :title="shop.name" left-arrow @click-left="$router.push('/')" />
<section class="hero" :style="{ background: shop.gradient }">
<div>
<span class="hero__eyebrow">官方品质供应商</span>
<h1>{{ shop.slogan }}</h1>
<p>满 {{ shop.freeShipping }} 元免配送费</p>
</div>
<van-icon :name="shop.icon" size="64" />
</section>
<van-notice-bar class="notice" left-icon="volume-o" :text="shop.notice" />
<van-search v-model="keyword" placeholder="搜索商品" shape="round" />
<van-tabs v-model:active="activeCategory" sticky :color="shop.theme" title-active-color="var(--theme)">
<van-tab v-for="category in categories" :key="category" :title="category" />
</van-tabs>
<van-empty v-if="filteredProducts.length === 0" description="暂无相关商品" />
<section v-else class="goods-list">
<van-card
v-for="item in filteredProducts"
:key="item.id"
:price="item.price.toFixed(2)"
:desc="item.desc"
:title="item.name"
:thumb="item.image"
:origin-price="item.originPrice.toFixed(2)"
>
<template #tags>
<van-tag round plain :color="shop.theme">{{ item.tag }}</van-tag>
</template>
<template #footer>
<van-stepper v-model="cart[item.id]" theme="round" :button-size="25" :disable-input="true" />
</template>
</van-card>
</section>
<div class="bottom-space" />
<van-submit-bar :price="totalPrice" :button-text="submitText" :disabled="totalCount === 0" @submit="openCheckout">
<van-icon name="shopping-cart-o" size="26" :badge="totalCount || ''" class="cart-icon" />
<span class="selected">已选 {{ totalCount }} 件</span>
</van-submit-bar>
<van-popup v-model:show="checkoutVisible" round position="bottom" :style="{ minHeight: '68%' }" closeable>
<section class="checkout">
<h2>确认订单</h2>
<van-contact-card type="add" name="商城收货人" tel="138****8888" @click="Toast('当前使用默认收货地址')" />
<van-cell-group inset title="商品清单">
<van-cell
v-for="item in selectedProducts"
:key="item.id"
:title="item.name"
:label="`¥ ${item.price.toFixed(2)} × ${cart[item.id]}`"
:value="`¥ ${(item.price * cart[item.id]).toFixed(2)}`"
/>
</van-cell-group>
<van-cell-group inset>
<van-cell title="配送方式" value="供应商配送" />
<van-cell title="配送费" :value="freight ? `¥ ${freight.toFixed(2)}` : '免配送费'" />
</van-cell-group>
<div class="checkout__total">合计 <b>¥ {{ (totalPrice / 100).toFixed(2) }}</b></div>
<van-button block round :color="shop.gradient" :loading="submitting" @click="submitOrder">提交订单</van-button>
</section>
</van-popup>
<van-popup v-model:show="successVisible" round closeable class="result-popup">
<van-icon name="checked" color="#23b65d" size="62" />
<h2>订单提交成功</h2>
<p>订单号:{{ submittedOrderId }}</p>
<van-button round block type="primary" @click="successVisible = false">继续选购</van-button>
</van-popup>
</main>
</template>
<script>
import { Toast } from 'vant'
const apiBase = process.env.VUE_APP_API_BASE || 'http://localhost:8050'
const formatTime = date => date.toISOString().slice(0, 19).replace('T', ' ')
export default {
props: {
shop: { type: Object, required: true }
},
data() {
return {
keyword: '',
activeCategory: 0,
cart: this.shop.products.reduce((result, item) => ({ ...result, [item.id]: 0 }), {}),
checkoutVisible: false,
successVisible: false,
submitting: false,
submittedOrderId: ''
}
},
computed: {
categories() {
return ['全部', ...this.shop.categories]
},
filteredProducts() {
const category = this.categories[this.activeCategory]
const keyword = this.keyword.trim()
return this.shop.products.filter(item => (category === '全部' || item.category === category) && (!keyword || item.name.includes(keyword)))
},
selectedProducts() {
return this.shop.products.filter(item => this.cart[item.id] > 0)
},
totalCount() {
return this.selectedProducts.reduce((sum, item) => sum + this.cart[item.id], 0)
},
goodsAmount() {
return this.selectedProducts.reduce((sum, item) => sum + item.price * this.cart[item.id], 0)
},
freight() {
return this.goodsAmount > 0 && this.goodsAmount < this.shop.freeShipping ? 12 : 0
},
totalPrice() {
return Math.round((this.goodsAmount + this.freight) * 100)
},
submitText() {
return this.totalCount ? `提交订单(${this.totalCount} 件)` : '提交订单'
}
},
methods: {
openCheckout() {
this.checkoutVisible = true
},
createOrderId() {
return `SUP${new Date().toISOString().replace(/\D/g, '').slice(0, 14)}${Math.floor(Math.random() * 9000 + 1000)}`
},
async submitOrder() {
const orderId = this.createOrderId()
const now = formatTime(new Date())
const orderInfo = {
orderId,
orderNumberId: `TRADE${Date.now()}`,
supplierId: this.shop.id,
aplName: this.shop.name,
createTime: now,
updateTime: now,
deliveryStatus: '0',
orderState: '0',
totalPrice: +(this.totalPrice / 100).toFixed(2),
pointsPrice: +(this.totalPrice / 100).toFixed(2),
wPayOrAPay: 0,
freightCost: this.freight,
orderProInfos: this.selectedProducts.map(item => ({
sku: item.sku,
number: this.cart[item.id],
price: +(item.price * this.cart[item.id]).toFixed(2),
name: item.name,
taxRate: 0.13,
picInfo: [item.image]
}))
}
this.submitting = true
try {
const response = await fetch(`${apiBase}/general/receive/pending`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ supplierId: this.shop.id, userId: 'h5-demo-user', token: 'h5-demo-token', orderInfo })
})
const result = await response.json()
if (!response.ok || result.resultCode !== 0) throw new Error(result.message || '订单提交失败')
this.submittedOrderId = result.data && result.data.orderId ? result.data.orderId : orderId
this.checkoutVisible = false
this.successVisible = true
this.selectedProducts.forEach(item => { this.cart[item.id] = 0 })
} catch (error) {
Toast.fail(error.message || '无法连接订单服务,请确认服务已启动')
} finally {
this.submitting = false
}
},
Toast
}
}
</script>
<style scoped lang="less">
.shop { min-height: 100vh; background: #f6f7f9; }
.hero { display: flex; align-items: center; justify-content: space-between; margin: 12px; padding: 22px; border-radius: 18px; color: #fff; box-shadow: 0 10px 22px rgba(30, 80, 90, .14); }
.hero h1 { margin: 7px 0; font-size: 23px; }
.hero p, .hero__eyebrow { margin: 0; font-size: 13px; opacity: .9; }
.notice { margin: 0 12px 8px; overflow: hidden; border-radius: 9px; }
.goods-list { padding: 0 12px; }
.van-card { margin: 10px 0; padding: 12px; border-radius: 12px; background: #fff; }
.van-card__thumb { overflow: hidden; border-radius: 8px; }
.bottom-space { height: 72px; }
.cart-icon { margin: 0 10px 0 4px; }
.selected { color: #646566; font-size: 13px; }
.checkout { padding: 22px 16px 28px; }
.checkout h2 { margin: 2px 0 18px; }
.checkout__total { padding: 18px 8px; text-align: right; }
.checkout__total b { color: #ee0a24; font-size: 22px; }
.result-popup { width: 78%; padding: 32px 22px 24px; text-align: center; }
.result-popup h2 { margin: 14px 0 8px; }
.result-popup p { margin: 0 0 22px; color: #969799; font-size: 12px; }
</style>