624 lines
15 KiB
Vue
624 lines
15 KiB
Vue
<template>
|
||
<view class="exchange-page">
|
||
<view class="page-hero">
|
||
<AppNavbar title="药兑换"/>
|
||
</view>
|
||
|
||
<view class="content">
|
||
<scroll-view class="category-list" scroll-y>
|
||
<view v-for="cat in categories" :key="cat.code" class="category-item"
|
||
:class="{'active': cat.code === activeCategory}" @tap="selectCategory(cat.code)">
|
||
<view class="category-name">{{ cat.name }}</view>
|
||
<view class="category-type">{{ cat.type }}</view>
|
||
</view>
|
||
</scroll-view>
|
||
|
||
<scroll-view class="product-list" scroll-y>
|
||
<!-- <view class="sub-category-wrap">-->
|
||
<!-- <scroll-view class="sub-category-scroll" scroll-x>-->
|
||
<!-- <view-->
|
||
<!-- v-for="sub in secondaryCategories"-->
|
||
<!-- :key="sub"-->
|
||
<!-- class="sub-category-chip"-->
|
||
<!-- :class="{'active': sub === activeSubCategory}"-->
|
||
<!-- @tap="selectSubCategory(sub)"-->
|
||
<!-- >-->
|
||
<!-- {{ sub }}-->
|
||
<!-- </view>-->
|
||
<!-- </scroll-view>-->
|
||
<!-- </view>-->
|
||
<view class="product-card card" v-for="item in filteredProducts" :key="item.id" @tap="openDetail(item)">
|
||
<view class="product-head">
|
||
<view class="product-name">{{ item.name }}</view>
|
||
</view>
|
||
<view class="product-meta">分类:{{ item.categoryName }}</view>
|
||
<view class="price-row">
|
||
<view class="price">¥{{ item.price }}</view>
|
||
<view class="origin">¥{{ item.origin }}</view>
|
||
<!-- <view class="sell-point">{{ item.sellPoint }}</view>-->
|
||
</view>
|
||
<view class="product-tags">
|
||
<!-- <view class="tag" v-for="tag in item.tags" :key="tag">{{ tag }}</view>-->
|
||
<view class="coupon" @tap.stop="openCoupon(item)">领券</view>
|
||
</view>
|
||
<view class="actions">
|
||
<!-- <view class="hot">热卖推荐</view>-->
|
||
<view class="btn-group">
|
||
<!-- <button size="mini" type="default" plain @tap.stop="addToCart(item)">加入购物车</button>-->
|
||
<u-icon name="/static/images/addCart.png" width="60rpx" height="60rpx"/>
|
||
<button size="mini" class="warn-btn" style="margin-left: 20rpx" @tap.stop="buyNow(item)">购买</button>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</scroll-view>
|
||
</view>
|
||
|
||
<view class="cart-entry" @tap="openCartPopup">
|
||
<u-icon name="/static/images/addCart.png" width="56rpx" height="56rpx"/>
|
||
<view class="cart-entry-text">购物车</view>
|
||
<view v-if="cartCount" class="cart-entry-badge">{{ cartCount }}</view>
|
||
</view>
|
||
|
||
<view v-if="showCartPopup" class="cart-popup-mask" @tap="closeCartPopup"></view>
|
||
<view v-if="showCartPopup" class="cart-popup card" @tap.stop>
|
||
<view class="cart-popup-head">
|
||
<view class="section-title">购物车</view>
|
||
<view class="close-btn" @tap="closeCartPopup">关闭</view>
|
||
</view>
|
||
<!-- <view class="cart-note">不同商家商品分开显示,订单分开推送结算。</view>-->
|
||
<view v-if="cartLoading" class="cart-empty">加载中...</view>
|
||
<view v-else-if="cart.length === 0" class="cart-empty">购物车为空</view>
|
||
<view v-else>
|
||
<view class="cart-vendor" v-for="(vendor, index) in cart" :key="index">
|
||
<view class="cart-vendor-head">
|
||
<view class="vendor-name">{{ vendor.goods.goodsName }}</view>
|
||
<!-- <view class="chip">商品 {{ (vendor.items || []).length }}</view>-->
|
||
</view>
|
||
<!-- <view class="cart-item-meta">规格 {{ prod.specs }}</view>-->
|
||
<view class="cart-item-price">¥{{ vendor.goods.salePrice }} x {{ vendor.num }}</view>
|
||
</view>
|
||
<view class="cart-summary">
|
||
<view>总价:
|
||
<text class="price">¥{{ cartTotal }}</text>
|
||
</view>
|
||
<view class="primary-btn checkout" @tap="checkout">结算</view>
|
||
<!-- <view class="primary-btn checkout" style="margin-left:12rpx;" @tap="goCart">查看购物车页</view>-->
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
import {cartListReq, goodsCategoryListReq, goodsListReq} from "@/api/goods";
|
||
import AppNavbar from "@/components/AppNavbar.vue";
|
||
|
||
export default {
|
||
components: {AppNavbar},
|
||
data() {
|
||
return {
|
||
categories: [],
|
||
secondaryCategories: [
|
||
'全部',
|
||
'儿童用药',
|
||
'男科用药',
|
||
'胃肠用药',
|
||
'医疗器械',
|
||
'中药参茸',
|
||
'五官用药',
|
||
'心脑血管用药',
|
||
'神经用药',
|
||
'组合用药',
|
||
'三高用药',
|
||
'计生用品',
|
||
'泌尿系统',
|
||
'滋补调养',
|
||
'维矿用药',
|
||
'处方药',
|
||
'抗菌消炎',
|
||
'皮肤用药',
|
||
'风湿骨伤',
|
||
'保健食品',
|
||
'敢买用药',
|
||
'呼吸系统',
|
||
'妇科用药'
|
||
],
|
||
activeCategory: null,
|
||
activeSubCategory: '全部',
|
||
products: [],
|
||
cart: [],
|
||
cartLoading: false,
|
||
showCartPopup: false
|
||
}
|
||
},
|
||
async onShow() {
|
||
await this.fetchCategories()
|
||
await this.fetchProducts()
|
||
},
|
||
computed: {
|
||
filteredProducts() {
|
||
return this.products.filter(p => {
|
||
const matchCategory = p.category === this.activeCategory || p.categoryName === this.activeCategory
|
||
const matchSub = this.activeSubCategory === '全部' || p.subCategory === this.activeSubCategory
|
||
return matchCategory && matchSub
|
||
})
|
||
},
|
||
cartTotal() {
|
||
let total = 0
|
||
this.cart.forEach(vendor => {
|
||
// const items = vendor.items || []
|
||
// items.forEach(item => {
|
||
total += Number(vendor.goods.salePrice || 0) * Number(vendor.num || 0)
|
||
// })
|
||
})
|
||
return total.toFixed(2)
|
||
},
|
||
cartCount() {
|
||
let count = 0
|
||
this.cart.forEach(vendor => {
|
||
count += vendor.num
|
||
})
|
||
return count
|
||
}
|
||
},
|
||
methods: {
|
||
async fetchCategories() {
|
||
try {
|
||
const {data} = await goodsCategoryListReq()
|
||
const list = Array.isArray(data) ? data : (data && data.list ? data.list : [])
|
||
this.categories = list.map(item => ({
|
||
code: item.code || item.id || item.categoryId || item.value || item.name,
|
||
name: item.name || item.title || '',
|
||
type: item.type || item.groupName || ''
|
||
}))
|
||
if (!this.activeCategory && this.categories.length) {
|
||
this.activeCategory = this.categories[0].code
|
||
}
|
||
} catch (err) {
|
||
uni.showToast({
|
||
title: '分类加载失败',
|
||
icon: 'none'
|
||
})
|
||
}
|
||
},
|
||
async fetchProducts() {
|
||
try {
|
||
const {data} = await goodsListReq()
|
||
const list = Array.isArray(data) ? data : (data && data.list ? data.list : [])
|
||
this.products = list.map(item => {
|
||
const rawTags = item.tags || item.tag || []
|
||
const tags = Array.isArray(rawTags) ? rawTags : String(rawTags).split(',').filter(Boolean)
|
||
return {
|
||
id: item.goodsId,
|
||
name: item.goodsName,
|
||
category: item.categoryCode || item.categoryId || item.cateId || item.category,
|
||
categoryName: item.categoryName || item.cateName || '',
|
||
subCategory: item.subCategoryName || item.subCategory || item.subCateName || '全部',
|
||
specs: item.specs || item.specification || '',
|
||
price: item.salePrice || 0,
|
||
origin: item.originPrice || 0,
|
||
sellPoint: item.sellPoint || item.subTitle || '',
|
||
tags,
|
||
vendor: item.vendor || item.brand || item.manufacturer || '',
|
||
coupon: item.coupon || item.couponDesc || ''
|
||
}
|
||
})
|
||
} catch (err) {
|
||
uni.showToast({
|
||
title: '商品加载失败',
|
||
icon: 'none'
|
||
})
|
||
}
|
||
},
|
||
selectCategory(code) {
|
||
this.activeCategory = code
|
||
},
|
||
selectSubCategory(name) {
|
||
this.activeSubCategory = name
|
||
},
|
||
addToCart(product) {
|
||
const vendorIndex = this.cart.findIndex(v => v.name === product.vendor)
|
||
if (vendorIndex > -1) {
|
||
const itemIndex = this.cart[vendorIndex].items.findIndex(i => i.name === product.name)
|
||
if (itemIndex > -1) {
|
||
this.cart[vendorIndex].items[itemIndex].qty += 1
|
||
} else {
|
||
this.cart[vendorIndex].items.push({
|
||
name: product.name,
|
||
specs: product.specs,
|
||
price: product.price,
|
||
qty: 1
|
||
})
|
||
}
|
||
} else {
|
||
this.cart.push({
|
||
name: product.vendor,
|
||
items: [{
|
||
name: product.name,
|
||
specs: product.specs,
|
||
price: product.price,
|
||
qty: 1
|
||
}]
|
||
})
|
||
}
|
||
this.cart = [...this.cart]
|
||
uni.showToast({
|
||
title: '已加入购物车',
|
||
icon: 'success'
|
||
})
|
||
},
|
||
buyNow(product) {
|
||
const goodsDataList = [{
|
||
goodsId: product.id,
|
||
num: 1
|
||
}]
|
||
uni.navigateTo({
|
||
url: `/pages/order/checkout?goodsDataList=${JSON.stringify(goodsDataList)}`
|
||
})
|
||
},
|
||
openCoupon(product) {
|
||
uni.navigateTo({
|
||
url: '/packageUser/coupon/index'
|
||
})
|
||
},
|
||
async openCartPopup() {
|
||
this.showCartPopup = true
|
||
await this.loadCartList()
|
||
},
|
||
closeCartPopup() {
|
||
this.showCartPopup = false
|
||
},
|
||
async loadCartList() {
|
||
this.cartLoading = true
|
||
try {
|
||
const {data} = await cartListReq()
|
||
this.cart = data
|
||
console.log(this.cart)
|
||
} catch (err) {
|
||
uni.showToast({
|
||
title: '购物车加载失败',
|
||
icon: 'none'
|
||
})
|
||
} finally {
|
||
this.cartLoading = false
|
||
}
|
||
},
|
||
checkout() {
|
||
// uni.showToast({
|
||
// title: '提交订单到各商家',
|
||
// icon: 'none'
|
||
// })
|
||
const goodsDataList = []
|
||
this.cart.forEach(vendor => {
|
||
goodsDataList.push({
|
||
goodsId: vendor.goodsId,
|
||
num: vendor.num
|
||
})
|
||
})
|
||
uni.navigateTo({
|
||
url: `/pages/order/checkout?goodsDataList=${JSON.stringify(goodsDataList)}`
|
||
})
|
||
},
|
||
openDetail(item) {
|
||
uni.navigateTo({
|
||
url: `/pages/product/detail?id=${item.id}`
|
||
})
|
||
},
|
||
goCart() {
|
||
uni.navigateTo({
|
||
url: '/pages/cart/index'
|
||
})
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.exchange-page {
|
||
min-height: 100vh;
|
||
//padding: 0 20rpx 40rpx;
|
||
background: #f5f5f5;
|
||
}
|
||
|
||
.page-hero {
|
||
//padding: 30rpx 6rpx 18rpx;
|
||
background: url('https://yaodongxiao.oss-cn-guangzhou.aliyuncs.com/20260126/462bfa04cf0eff8698884c38b9d17519.png') no-repeat
|
||
top center / 100% auto;
|
||
}
|
||
|
||
.page-navbar {
|
||
padding: 0 6rpx;
|
||
}
|
||
|
||
.content {
|
||
display: flex;
|
||
height: 960rpx;
|
||
padding: 10rpx 6rpx;
|
||
gap: 10rpx;
|
||
}
|
||
|
||
.category-list {
|
||
flex: 0 0 200rpx;
|
||
background: #f2f6fc;
|
||
border-radius: 18rpx;
|
||
padding: 10rpx;
|
||
}
|
||
|
||
.category-item {
|
||
background: #ffffff;
|
||
border-radius: 14rpx;
|
||
margin-bottom: 12rpx;
|
||
padding: 16rpx 12rpx;
|
||
color: var(--text-main);
|
||
box-shadow: 0 8rpx 18rpx rgba(31, 111, 232, 0.05);
|
||
border: 1rpx solid transparent;
|
||
}
|
||
|
||
.category-item.active {
|
||
border: 1rpx solid var(--brand-primary);
|
||
box-shadow: 0 8rpx 22rpx rgba(31, 111, 232, 0.12);
|
||
}
|
||
|
||
.category-name {
|
||
|
||
}
|
||
|
||
.category-type {
|
||
color: var(--text-sub);
|
||
font-size: 22rpx;
|
||
margin-top: 4rpx;
|
||
}
|
||
|
||
.product-list {
|
||
flex: 1;
|
||
}
|
||
|
||
.sub-category-wrap {
|
||
margin-bottom: 16rpx;
|
||
padding: 12rpx;
|
||
}
|
||
|
||
.sub-category-scroll {
|
||
display: flex;
|
||
gap: 12rpx;
|
||
margin-top: 10rpx;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.sub-category-chip {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
padding: 10rpx 18rpx;
|
||
border-radius: 999rpx;
|
||
background: #f5f8ff;
|
||
color: var(--text-sub);
|
||
font-size: 24rpx;
|
||
border: 1rpx solid #e1e8f5;
|
||
margin-right: 15rpx;
|
||
}
|
||
|
||
.sub-category-chip.active {
|
||
color: #ffffff;
|
||
background: var(--brand-primary);
|
||
border-color: var(--brand-primary);
|
||
}
|
||
|
||
.product-card {
|
||
margin-bottom: 16rpx;
|
||
width: 90%;
|
||
border: 1rpx solid #e7eef8;
|
||
}
|
||
|
||
.product-head {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
}
|
||
|
||
.product-name {
|
||
font-size: 30rpx;
|
||
|
||
color: var(--text-main);
|
||
}
|
||
|
||
.product-meta {
|
||
color: var(--text-sub);
|
||
margin: 6rpx 0 10rpx 0;
|
||
}
|
||
|
||
.price-row {
|
||
display: flex;
|
||
align-items: baseline;
|
||
gap: 14rpx;
|
||
}
|
||
|
||
.price {
|
||
color: #ff3b30;
|
||
font-size: 34rpx;
|
||
font-weight: 800;
|
||
}
|
||
|
||
.origin {
|
||
text-decoration: line-through;
|
||
color: #a1aabc;
|
||
font-size: 24rpx;
|
||
}
|
||
|
||
.sell-point {
|
||
color: #ff8354;
|
||
font-size: 24rpx;
|
||
}
|
||
|
||
.product-tags {
|
||
display: flex;
|
||
gap: 10rpx;
|
||
margin-top: 12rpx;
|
||
align-items: center;
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
.tag {
|
||
background: #e9f1ff;
|
||
color: var(--brand-primary);
|
||
border-radius: 999rpx;
|
||
padding: 8rpx 14rpx;
|
||
font-size: 22rpx;
|
||
}
|
||
|
||
.coupon {
|
||
color: #ff8354;
|
||
background: #fff1e9;
|
||
border-radius: 999rpx;
|
||
padding: 8rpx 14rpx;
|
||
font-size: 22rpx;
|
||
border: 1rpx dashed #ffb08a;
|
||
}
|
||
|
||
.actions {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-top: 14rpx;
|
||
}
|
||
|
||
.hot {
|
||
color: var(--brand-primary);
|
||
|
||
}
|
||
|
||
.btn-group {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: flex-start;
|
||
}
|
||
|
||
.cart-entry {
|
||
position: fixed;
|
||
right: 24rpx;
|
||
bottom: 120rpx;
|
||
background: #ffffff;
|
||
border-radius: 999rpx;
|
||
padding: 10rpx 16rpx 10rpx 12rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8rpx;
|
||
box-shadow: 0 12rpx 24rpx rgba(31, 111, 232, 0.18);
|
||
z-index: 20;
|
||
}
|
||
|
||
.cart-entry-text {
|
||
font-size: 24rpx;
|
||
color: var(--text-main);
|
||
}
|
||
|
||
.cart-entry-badge {
|
||
position: absolute;
|
||
top: -10rpx;
|
||
right: -10rpx;
|
||
background: #ff3b30;
|
||
color: #ffffff;
|
||
min-width: 32rpx;
|
||
height: 32rpx;
|
||
border-radius: 999rpx;
|
||
font-size: 20rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 0 8rpx;
|
||
}
|
||
|
||
.cart-popup-mask {
|
||
position: fixed;
|
||
left: 0;
|
||
right: 0;
|
||
top: 0;
|
||
bottom: 0;
|
||
background: rgba(0, 0, 0, 0.45);
|
||
z-index: 30;
|
||
}
|
||
|
||
.cart-popup {
|
||
position: fixed;
|
||
left: 50%;
|
||
bottom: 40rpx;
|
||
transform: translateX(-50%);
|
||
width: 88%;
|
||
max-height: 70vh;
|
||
overflow: auto;
|
||
z-index: 40;
|
||
}
|
||
|
||
.cart-popup-head {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
}
|
||
|
||
.close-btn {
|
||
color: var(--text-sub);
|
||
font-size: 24rpx;
|
||
}
|
||
|
||
.cart-note {
|
||
color: var(--text-sub);
|
||
margin-bottom: 12rpx;
|
||
}
|
||
|
||
.cart-empty {
|
||
color: var(--text-sub);
|
||
text-align: center;
|
||
padding: 24rpx 0;
|
||
}
|
||
|
||
.cart-vendor {
|
||
border: 1rpx dashed #dfe8f5;
|
||
border-radius: 14rpx;
|
||
padding: 12rpx;
|
||
margin-bottom: 12rpx;
|
||
}
|
||
|
||
.cart-vendor-head {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 8rpx;
|
||
}
|
||
|
||
.vendor-name {
|
||
|
||
color: var(--text-main);
|
||
}
|
||
|
||
.cart-item {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
margin-bottom: 6rpx;
|
||
}
|
||
|
||
.cart-item-name {
|
||
font-size: 24rpx;
|
||
}
|
||
|
||
.cart-item-meta {
|
||
color: var(--text-sub);
|
||
font-size: 22rpx;
|
||
}
|
||
|
||
.cart-item-price {
|
||
color: #ff3b30;
|
||
|
||
}
|
||
|
||
.cart-summary {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding-top: 12rpx;
|
||
border-top: 1rpx solid #e7eef8;
|
||
}
|
||
|
||
.checkout {
|
||
padding: 14rpx 24rpx;
|
||
}
|
||
</style>
|