Files
lilishop-ui/buyer/src/pages/home/memberCenter/Favorites.vue
pikachu1995@126.com 4777eb2dd6 feat(buyer): 新增功能并优化样式,提升用户体验
- 新增 `getMemberMessage` 接口方法,用于获取会员消息
- 调整全局及商品列表样式,提升可读性与一致性
- 精简 `Search.vue`,移除不必要的店铺搜索切换
- 放宽 `addressManage` 中地址别名的输入长度
- 优化 `ShowGoods.vue` 和 `ShowGoodsDetail.vue` 的布局与样式
- 更新 `Cart.vue` 中的购物车步骤展示,让流程导航更清晰
- 调整多个组件,统一字号并优化界面元素
2026-09-29 18:11:31 +08:00

363 lines
9.5 KiB
Vue

<template>
<div class="wrapper">
<UserCenterLayout
v-if="!homePage"
:tabs="favoriteWay"
:active-tab="activeTabIndex"
@tab-change="change"
>
<empty v-if="!list.length" />
<div v-else class="fav-list" :class="{ 'is-store': params.type === 'STORE' }">
<div class="fav-cols">
<span>{{ params.type === 'GOODS' ? '商品' : '店铺' }}</span>
<span v-if="params.type === 'GOODS'">金额</span>
<span>操作</span>
</div>
<div class="fav-row" v-for="(item, index) in list" :key="item.id || item.skuId || index">
<div class="fav-goods">
<div class="fav-thumb" @click="openItem(item)">
<img
:src="params.type === 'GOODS' ? item.image : item.storeLogo"
alt=""
@error="$event.target.style.visibility = 'hidden'"
/>
</div>
<div class="fav-info">
<div class="fav-name" @click="openItem(item)">
{{ params.type === 'GOODS' ? item.goodsName : item.storeName }}
</div>
<span v-if="params.type === 'STORE' && item.selfOperated" class="self-tag">商家自营</span>
</div>
</div>
<div v-if="params.type === 'GOODS'" class="fav-price">{{ $filters.unitPrice(item.price, '¥') }}</div>
<div class="fav-ops">
<button v-if="params.type === 'GOODS'" type="button" class="op-btn" @click="buynow(item.skuId, item.goodsId)">立即购买</button>
<button v-else type="button" class="op-btn" @click="goShop(item.id)">查看店铺</button>
<button v-if="params.type === 'GOODS'" type="button" class="text-link" @click="cancel(item.skuId)">取消收藏</button>
<button v-else type="button" class="text-link" @click="cancelStore(item.id)">取消收藏</button>
</div>
</div>
<el-skeleton size="large" fix v-if="spinShow"></el-skeleton>
</div>
</UserCenterLayout>
<template v-else>
<card _Title="近期收藏" :_Size="16" :_Tabs="favoriteWay" :_ActiveTab="activeTabIndex" @_Change="change" _More="全部收藏" _Src="/home/Favorites"></card>
<empty v-if="!list.length" />
<div v-else class="fav-list" :class="{ 'is-store': params.type === 'STORE' }">
<div class="fav-cols">
<span>{{ params.type === 'GOODS' ? '商品' : '店铺' }}</span>
<span v-if="params.type === 'GOODS'">金额</span>
<span>操作</span>
</div>
<div class="fav-row" v-for="(item, index) in list" :key="item.id || item.skuId || index">
<div class="fav-goods">
<div class="fav-thumb" @click="openItem(item)">
<img
:src="params.type === 'GOODS' ? item.image : item.storeLogo"
alt=""
@error="$event.target.style.visibility = 'hidden'"
/>
</div>
<div class="fav-info">
<div class="fav-name" @click="openItem(item)">
{{ params.type === 'GOODS' ? item.goodsName : item.storeName }}
</div>
<span v-if="params.type === 'STORE' && item.selfOperated" class="self-tag">商家自营</span>
</div>
</div>
<div v-if="params.type === 'GOODS'" class="fav-price">{{ $filters.unitPrice(item.price, '¥') }}</div>
<div class="fav-ops">
<button v-if="params.type === 'GOODS'" type="button" class="op-btn" @click="buynow(item.skuId, item.goodsId)">立即购买</button>
<button v-else type="button" class="op-btn" @click="goShop(item.id)">查看店铺</button>
<button v-if="params.type === 'GOODS'" type="button" class="text-link" @click="cancel(item.skuId)">取消收藏</button>
<button v-else type="button" class="text-link" @click="cancelStore(item.id)">取消收藏</button>
</div>
</div>
<el-skeleton size="large" fix v-if="spinShow"></el-skeleton>
</div>
</template>
</div>
</template>
<script>
import { Modal } from "@/utils/message";
import { collectList, cancelCollect, storeCollectList, cancelStoreCollect } from '@/api/member.js'
export default {
name: 'Favorites',
props: {
homePage: {
type: Boolean,
default: false
}
},
data() {
return {
favoriteWay: ['收藏商品', '收藏店铺'], // 收藏分类
list: [], // 收藏列表
total: 0, // 收藏总数
params: { // 请求参数
pageNumber: 1,
pageSize: 100,
type: this.$route.query.type || 'GOODS'
},
spinShow: false // 加载状态
};
},
computed: {
activeTabIndex() {
return this.params.type === 'STORE' ? 1 : 0;
},
},
methods: {
loadFavorites() {
if (this.params.type === 'STORE') {
this.getStoreList();
} else {
this.getList();
}
},
getList() { // 获取商品收藏列表
this.spinShow = true
collectList(this.params).then(res => {
if (res.success && res.result) {
this.list = res.result.records || [];
}
}).catch(() => {
this.list = [];
}).finally(() => {
this.spinShow = false
})
},
getStoreList() { // 获取店铺收藏列表
this.spinShow = true
storeCollectList(this.params).then(res => {
if (res.success && res.result) {
this.list = res.result.records || [];
}
}).catch(() => {
this.list = [];
}).finally(() => {
this.spinShow = false
})
},
change(index) { // tab栏切换
if (index === 0) { this.params.type = 'GOODS', this.getList() }
if (index === 1) { this.params.type = 'STORE', this.getStoreList() }
},
cancel(id) { // 取消收藏
let typeName = this.params.type === 'GOODS' ? '商品' : '店铺'
Modal.confirm({
title: '取消收藏',
content: `确定取消收藏该${typeName}吗?`,
onOk: () => {
cancelCollect(this.params.type, id).then(res => {
if (res.success) {
this.getList();
}
})
}
});
},
cancelStore(id) { // 取消收藏
let typeName = this.params.type === 'GOODS' ? '商品' : '店铺'
Modal.confirm({
title: '取消收藏',
content: `确定取消收藏该${typeName}吗?`,
onOk: () => {
cancelStoreCollect(this.params.type, id).then(res => {
if (res.success) {
this.getStoreList();
}
})
}
});
},
openItem (item) {
if (this.params.type === 'STORE') {
this.goShop(item.id)
return
}
this.buynow(item.skuId, item.goodsId)
},
buynow(skuId, goodsId,storeId) { // 跳转详情
console.log(this.params.type)
let url
if (this.params.type === 'STORE') {
url = this.$router.resolve({
path: '/merchant',
query: { 'id': storeId }
})
} else {
url = this.$router.resolve({
path: '/goodsDetail',
query: { skuId, goodsId }
})
}
window.open(url.href, '_blank')
},
goShop(id) { // 跳转店铺页面
let url = this.$router.resolve({
path: '/merchant',
query: { id }
})
window.open(url.href, '_blank')
}
},
mounted() {
if (this.homePage) this.params.pageSize = 5;
this.loadFavorites();
}
};
</script>
<style scoped lang="scss">
.wrapper {
margin-bottom: 40px;
}
.fav-list {
--fav-cols: minmax(0, 1fr) 140px 200px;
--fav-gap: 12px;
padding-top: 8px;
&.is-store {
--fav-cols: minmax(0, 1fr) 200px;
}
}
.fav-cols,
.fav-row {
display: grid;
grid-template-columns: var(--fav-cols);
column-gap: var(--fav-gap);
align-items: center;
}
.fav-cols {
padding: 0 8px 10px;
color: #999;
font-size: 13px;
span:first-child {
padding-left: 72px;
}
span:nth-child(n + 2) {
text-align: center;
}
}
.fav-row {
padding: 14px 8px;
border-top: 1px solid #f0f0f0;
}
.fav-goods {
display: grid;
grid-template-columns: 60px minmax(0, 1fr);
column-gap: 12px;
align-items: center;
min-width: 0;
}
.fav-thumb {
width: 60px;
height: 60px;
border: 1px solid #f0f0f0;
overflow: hidden;
background: #f5f5f5;
cursor: pointer;
img {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
}
}
.fav-info {
min-width: 0;
}
.fav-name {
color: #333;
font-size: 16px;
line-height: 22px;
max-height: 44px;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
word-break: break-all;
cursor: pointer;
&:hover {
color: $theme_color;
}
}
.self-tag {
display: inline-block;
margin-top: 6px;
padding: 0 6px;
border-radius: 4px;
background: #fff1f0;
color: $theme_color;
font-size: 12px;
line-height: 20px;
}
.fav-price {
text-align: center;
color: #333;
font-size: 16px;
font-weight: 600;
line-height: 1.4;
}
.fav-ops {
display: flex;
align-items: center;
justify-content: center;
gap: 16px;
}
.op-btn {
min-width: 96px;
height: 32px;
padding: 0 12px;
border: 1px solid $theme_color;
border-radius: 4px;
background: $theme_color;
color: #fff;
font-family: inherit;
font-size: 14px;
line-height: 30px;
cursor: pointer;
&:hover {
opacity: 0.9;
}
}
.text-link {
border: none;
background: none;
padding: 0;
color: #171A26;
font-family: inherit;
font-size: 14px;
line-height: 22px;
cursor: pointer;
&:hover {
color: $theme_color;
}
}
</style>