Files
yxe-mp/pages/exchange/index.vue
T
2026-09-29 15:17:13 +08:00

624 lines
15 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>
<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>