web-zk-h5/pages/shareArea/hiList.vue

1079 lines
26 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="contain">
<view class="tab-container">
<view class="tab-wrapper">
<view
v-for="tab in tabs"
:key="tab.value"
class="tab-item"
:class="{ 'tab-active': activeTab === tab.value }"
@click="switchTab(tab.value)"
>
<text class="tab-text">{{ tab.label }}</text>
<view class="tab-line" v-if="activeTab === tab.value"></view>
</view>
</view>
</view>
<!-- 分类和搜索区域 - 仅在北大甄选时显示 -->
<view class="category-container" v-if="activeTab === 43">
<view class="category-section">
<view class="category-left">
<view
v-for="item in oneList"
:class="[
'category-item',
oneId == item.pkId ? 'category-active' : '',
]"
@click="selectOneCategory(item)"
:key="item.pkId"
>
{{ item.classifyName }}
</view>
</view>
<view class="category-right">
<!-- 二级分类 -->
<scroll-view
class="category-scroll"
scroll-x="true"
:show-scrollbar="false"
v-if="twoList.length > 0"
>
<view class="category-tabs">
<view
v-for="(item, index) in twoList"
:key="index"
@click="selectTwoCategory(item)"
:class="[twoId == item.pkId ? 'tab-active' : 'tab-item']"
>
{{ item.classifyName }}
</view>
</view>
</scroll-view>
<!-- 搜索框 -->
<view class="search-container">
<view class="search-input-wrapper">
<u--input
class="search-input"
placeholder="请输入商品名称"
placeholderStyle="font-size:14px;"
prefixIcon="search"
shape="circle"
v-model="waresName"
border="none"
prefixIconStyle="font-size: 22px;color: #909399"
@confirm="searchGoods"
/>
</view>
</view>
<!-- 北大甄选的商品列表 -->
<view class="goods-list-container">
<scroll-view
class="goodList"
scroll-y="true"
enhanced
:show-scrollbar="false"
>
<view
v-for="item in goodList"
:key="item.waresCode"
class="goodList_i"
>
<img :src="item.cover1" class="cover" alt="" />
<view class="goodList_ir">
<view class="tit1">
{{ item.waresName }}
</view>
<view class="goodList_ib">
<view class="price-container">
<!-- <text class="currency">¥</text> -->
<text class="price">{{
formatCurrency(item.waresPrice)
}}</text>
<text v-if="item.isSale === 1" class="sale-status"
>已售罄</text
>
</view>
<!-- <img @click.stop="addCar(item)"
src="@/static/images/cart.png"
alt=""> -->
</view>
<!-- 数量控制区域 -->
<view class="quantity-section" v-if="item.isSale !== 1">
<view class="quantity-control">
<view
class="quantity-btn minus"
:class="{ disabled: item.quantity <= 1 }"
@click.stop="updateQuantity(item, -1)"
>
<text class="quantity-icon"></text>
</view>
<view class="quantity-display">
<text class="quantity-text">{{
item.quantity || 1
}}</text>
</view>
<view
class="quantity-btn plus"
@click.stop="updateQuantity(item, 1)"
>
<text class="quantity-icon">+</text>
</view>
</view>
<view class="toBuy" @click.stop="goBuy(item)">购买</view>
</view>
<!-- 禁售状态 -->
<view class="sold-out-section" v-if="item.isSale === 1">
<view class="sold-out-btn">暂时缺货</view>
</view>
</view>
</view>
</scroll-view>
</view>
</view>
</view>
</view>
<!-- 精品专区的商品容器 -->
<view class="goods-container" v-if="activeTab !== 43">
<scroll-view
class="goodList"
scroll-y="true"
enhanced
:show-scrollbar="false"
>
<view v-for="item in goodList" :key="item.waresCode" class="goodList_i">
<img :src="item.cover1" class="cover" alt="" />
<view class="goodList_ir">
<view class="tit1">
{{ item.waresName }}
</view>
<view class="goodList_ib">
<view class="price-container">
<!-- <text class="currency">¥</text> -->
<text class="price">{{ formatCurrency(item.waresPrice) }}</text>
<text v-if="item.isSale === 1" class="sale-status">已售罄</text>
</view>
<!-- <img @click.stop="addCar(item)"
src="@/static/images/cart.png"
alt=""> -->
</view>
<!-- 数量控制区域 -->
<view class="quantity-section" v-if="item.isSale !== 1">
<view class="quantity-control">
<view
class="quantity-btn minus"
:class="{ disabled: item.quantity <= 1 }"
@click.stop="updateQuantity(item, -1)"
>
<text class="quantity-icon"></text>
</view>
<view class="quantity-display">
<text class="quantity-text">{{ item.quantity || 1 }}</text>
</view>
<view
class="quantity-btn plus"
@click.stop="updateQuantity(item, 1)"
>
<text class="quantity-icon">+</text>
</view>
</view>
<view class="toBuy" @click.stop="goBuy(item)">购买</view>
</view>
<!-- 禁售状态 -->
<view class="sold-out-section" v-if="item.isSale === 1">
<view class="sold-out-btn">暂时缺货</view>
</view>
</view>
</view>
<view class="list-bottom" v-if="activeTab !== 43">
<text class="bottom-text"> 已显示全部商品 </text>
</view>
</scroll-view>
</view>
</view>
</template>
<script>
import * as api from '@/config/login.js'
import * as goodsApi from '@/config/goods.js'
import { getAreaGoods } from '@/config/special-area.js'
import { setToken } from '@/config/auth.js'
import { formatCurrency } from '@/util/index.js'
export default {
data() {
return {
pkParent: '',
goodList: [],
placeParent: '',
activeTab: 43, // 默认选中北大甄选
tabs: [
{
label: '北大甄选',
value: 43,
},
{
label: '精品专区',
value: 41,
},
],
// 分类相关数据
oneList: [], // 一级分类列表
oneId: '', // 当前选中的一级分类ID
twoList: [], // 二级分类列表
twoId: '', // 当前选中的二级分类ID
waresName: '', // 搜索关键词
userInfo: {}, // 用户信息
pkCountry: 1, // 国家ID
categoryLoaded: false, // 分类是否已加载
}
},
onLoad(options) {
this.pkParent = options.pkParent
this.userInfo = uni.getStorageSync('User') || {}
this.pkCountry = this.userInfo.pkCountry || 1
this.getToken()
},
methods: {
formatCurrency,
getToken() {
api
.autoLogin({
pkParent: this.pkParent,
})
.then(res => {
setToken(res.data.access_token, res.data.expires_in)
uni.setStorageSync('regiest-login', '1')
// 根据当前tab决定加载方式
if (this.activeTab === 43) {
// 北大甄选 - 需要分类
this.getClassIfy()
} else {
// 精品专区 - 直接获取商品列表
this.getList()
}
this.getCode()
})
},
getCode() {
api.fansConvertCode(this.pkParent).then(res => {
this.placeParent = res.data
uni.setStorageSync('placeParent', this.placeParent)
})
},
getList() {
api
.queryWares({
shareMemberCode: this.pkParent,
specialArea: this.activeTab,
})
.then(res => {
// 为每个商品初始化quantity字段
this.goodList = res.data.map(item => ({
...item,
quantity: item.quantity || 1,
}))
})
},
switchTab(value) {
if (this.activeTab !== value) {
this.activeTab = value
this.goodList = [] // 清空当前列表
// 清空搜索和分类条件
this.clearSearchAndCategory()
// 根据不同tab加载不同数据
if (value === 43) {
// 北大甄选 - 需要分类
this.getClassIfy()
} else {
// 精品专区 - 直接获取商品列表
this.getList()
}
}
},
updateQuantity(item, change) {
const newQuantity = (item.quantity || 1) + change
if (newQuantity >= 1) {
// 使用Vue.set或直接赋值来确保响应式更新
this.$set(item, 'quantity', newQuantity)
}
},
goBuy(item) {
const params = {
...item,
pkParent: this.pkParent,
specialArea: this.activeTab,
}
uni.navigateTo({
url: '/pages/shareArea/hiOrder?allData=' + JSON.stringify(item),
})
},
// 获取分类列表(一级)
getClassIfy() {
goodsApi
.classifyList({
pkCountry: this.pkCountry,
specialArea: this.activeTab,
hierarchy: 0,
})
.then(res => {
this.oneList = res.data || []
this.categoryLoaded = true
if (this.oneList.length > 0) {
this.oneId = this.oneList[0].pkId
this.getClassIfyTwo(this.oneList[0].pkId)
this.getAllGoodsWithCategory(this.oneList[0].pkId)
}
})
.catch(err => {
console.error('获取分类失败:', err)
})
},
// 获取分类二级
getClassIfyTwo(pkId) {
goodsApi
.classifyList({
specialArea: this.activeTab,
pkParent: pkId,
hierarchy: 1,
pkCountry: this.pkCountry,
})
.then(res => {
const twoListData = res.data || []
twoListData.unshift({
classifyName: '全部',
pkId: pkId,
})
this.twoList = twoListData
this.twoId = pkId
})
.catch(err => {
console.error('获取二级分类失败:', err)
})
},
// 根据分类获取商品(带搜索功能)
getAllGoodsWithCategory(id) {
const params = {
pkCountry: this.pkCountry,
specialArea: this.activeTab,
pkAreaClassify: id || this.oneId,
shareMemberCode: this.pkParent,
waresName: '',
}
// 如果有搜索关键词,添加到参数中
if (this.waresName.trim()) {
params.waresName = this.waresName.trim()
}
api
.queryWares(params)
.then(res => {
// 为每个商品初始化quantity字段
this.goodList = (res.data || []).map(item => ({
...item,
quantity: item.quantity || 1,
}))
})
.catch(err => {
console.log(err, '...err')
console.error('获取商品列表失败:', err)
this.goodList = []
})
},
// 选择一级分类
selectOneCategory(item) {
this.oneId = item.pkId
this.getAllGoodsWithCategory(item.pkId)
this.getClassIfyTwo(item.pkId)
},
// 选择二级分类
selectTwoCategory(item) {
this.twoId = item.pkId
this.getAllGoodsWithCategory(item.pkId)
},
// 搜索商品
searchGoods() {
this.getAllGoodsWithCategory()
},
// 清空搜索和分类
clearSearchAndCategory() {
this.waresName = ''
this.oneId = ''
this.twoId = ''
this.oneList = []
this.twoList = []
},
},
}
</script>
<style lang="scss" scoped>
.contain {
background: #f8fafc;
min-height: 100vh;
height: 100vh;
display: flex;
flex-direction: column;
}
// 分类和搜索区域样式
.category-container {
background: #ffffff;
border-bottom: 1rpx solid #e2e8f0;
flex: 1;
height: 0;
display: flex;
flex-direction: column;
.category-section {
flex: 1;
display: flex;
height: 0rpx;
.category-left {
width: 200rpx;
background: #f8fafc;
border-right: 1rpx solid #e2e8f0;
overflow-y: auto;
flex-shrink: 0;
.category-item {
padding: 24rpx 20rpx;
font-size: 26rpx;
color: #64748b;
text-align: center;
border-bottom: 1rpx solid #f1f5f9;
transition: all 0.2s ease;
&.category-active {
background: #005bac;
color: #ffffff;
font-weight: 600;
}
&:active {
background: rgba(0, 91, 172, 0.1);
}
}
}
.category-right {
flex: 1;
width: 0;
display: flex;
flex-direction: column;
background: #fff;
.category-scroll {
padding: 20rpx 24rpx;
border-bottom: 1rpx solid #f1f5f9;
.category-tabs {
display: flex;
white-space: nowrap;
.tab-item,
.tab-active {
padding: 12rpx 24rpx;
margin-right: 20rpx;
border-radius: 20rpx;
font-size: 26rpx;
white-space: nowrap;
transition: all 0.2s ease;
}
.tab-item {
background: #f8fafc;
color: #64748b;
border: 1rpx solid #e2e8f0;
}
.tab-active {
background: #005bac;
color: #ffffff;
font-weight: 600;
}
}
}
.search-container {
padding: 20rpx 24rpx;
border-bottom: 1rpx solid #f1f5f9;
.search-input-wrapper {
display: flex;
align-items: center;
background: #f8fafc;
border-radius: 24rpx;
border: 1rpx solid #e2e8f0;
padding: 0 20rpx;
.search-input {
flex: 1;
height: 72rpx;
font-size: 28rpx;
color: #1f2937;
&::placeholder {
color: #9ca3af;
}
}
.search-icon {
width: 60rpx;
height: 60rpx;
display: flex;
align-items: center;
justify-content: center;
background: #005bac;
border-radius: 50%;
margin-left: 12rpx;
.search-text {
color: #ffffff;
font-size: 24rpx;
}
&:active {
background: #004494;
transform: scale(0.95);
}
}
}
}
// 北大甄选的商品列表容器
.goods-list-container {
flex: 1;
overflow: hidden;
background: #fff;
.goodList {
padding: 0;
.goodList_i {
padding: 8rpx;
img {
height: 140rpx;
width: 140rpx;
border-radius: 12rpx;
}
.goodList_ir {
margin-left: 8rpx;
.tit1 {
font-size: 26rpx;
margin-bottom: 8rpx;
}
.goodList_ib {
.price-container {
.price {
font-size: 28rpx;
color: #f82c1a;
}
}
}
.quantity-section {
.quantity-control {
.quantity-btn {
width: 40rpx;
height: 40rpx;
.quantity-icon {
font-size: 24rpx;
}
}
.quantity-display {
height: 40rpx;
}
}
.toBuy {
font-size: 24rpx;
color: #ffffff;
padding: 8rpx 16rpx;
border-radius: 12rpx;
}
.sold-out-btn {
font-size: 24rpx;
color: #ffffff;
padding: 8rpx 16rpx;
border-radius: 12rpx;
}
}
}
}
}
}
}
}
}
// Tab选项卡样式 - 简约大气设计
.tab-container {
// background: #ffffff;
padding: 0;
position: relative;
z-index: 4;
// 顶部装饰线
&::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 3rpx;
background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
}
.tab-wrapper {
display: flex;
position: relative;
// background: #ffffff;
// padding: 32rpx 24rpx 24rpx;
.tab-item {
flex: 1;
position: relative;
cursor: pointer;
padding: 24rpx 32rpx;
text-align: center;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
.tab-text {
font-size: 32rpx;
font-weight: 500;
color: #64748b;
transition: all 0.3s ease;
position: relative;
z-index: 2;
}
.tab-line {
display: none;
}
// 悬停效果
&:hover:not(.tab-active) {
.tab-text {
color: #475569;
transform: translateY(-1rpx);
}
}
&.tab-active {
background: rgba(102, 126, 234, 0.08);
.tab-text {
color: #667eea;
font-weight: 600;
}
// 选中指示器 - 底部完整宽度线条
&::after {
content: '';
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 3rpx;
background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
}
}
&:active {
transform: scale(0.98);
}
}
}
}
// 商品容器区域
.goods-container {
flex: 1;
background: #f8fafc;
padding: 24rpx 24rpx 0;
position: relative;
z-index: 3;
overflow: hidden;
.section-title {
text-align: center;
margin-bottom: 24rpx;
padding: 20rpx 0;
.title-text {
display: block;
color: #1e293b;
font-size: 32rpx;
font-weight: 600;
margin-bottom: 8rpx;
}
.title-desc {
display: block;
color: #64748b;
font-size: 24rpx;
}
}
}
// 商品列表滚动区域
.goodList {
height: 100%;
padding: 0 8rpx;
.goodList_i {
display: flex;
background: #ffffff;
border-radius: 24rpx;
margin-bottom: 24rpx;
padding: 32rpx;
box-shadow: 0 4rpx 20rpx rgba(102, 126, 234, 0.08);
border: 1rpx solid rgba(102, 126, 234, 0.08);
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
animation: slideInUp 0.6s ease-out both;
opacity: 0;
&:hover {
transform: translateY(-6rpx);
box-shadow: 0 16rpx 40rpx rgba(102, 126, 234, 0.2);
border-color: rgba(102, 126, 234, 0.15);
}
.cover {
width: 240rpx;
height: 240rpx;
background: #f8fafc;
border: 1rpx solid #e2e8f0;
border-radius: 16rpx;
flex-shrink: 0;
overflow: hidden;
}
.goodList_ir {
margin-left: 24rpx;
flex: 1;
display: flex;
flex-direction: column;
justify-content: space-between;
.tit1 {
color: #1e293b;
font-size: 32rpx;
font-weight: 600;
line-height: 1.4;
margin-bottom: 16rpx;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.sales {
color: #64748b;
font-size: 24rpx;
font-weight: 400;
margin-bottom: 16rpx;
}
.goodList_ib {
margin-bottom: 24rpx;
.price-container {
display: flex;
align-items: baseline;
justify-content: space-between;
.currency {
color: #ff6b35;
font-size: 24rpx;
font-weight: 600;
margin-right: 4rpx;
}
.price {
color: #ff6b35;
font-size: 36rpx;
font-weight: 700;
}
.sale-status {
color: #ef4444;
font-size: 24rpx;
font-weight: 500;
background: rgba(239, 68, 68, 0.1);
padding: 4rpx 12rpx;
border-radius: 12rpx;
border: 1rpx solid rgba(239, 68, 68, 0.2);
}
}
img {
width: 56rpx;
height: 56rpx;
}
}
// 数量控制区域
.quantity-section {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16rpx;
.quantity-control {
display: flex;
align-items: center;
background: #f8fafc;
border-radius: 20rpx;
border: 1rpx solid #e2e8f0;
overflow: hidden;
.quantity-btn {
width: 60rpx;
height: 60rpx;
display: flex;
align-items: center;
justify-content: center;
background: #ffffff;
transition: all 0.2s ease;
cursor: pointer;
.quantity-icon {
font-size: 32rpx;
font-weight: 600;
color: #667eea;
}
&.minus {
&.disabled {
opacity: 0.3;
cursor: not-allowed;
.quantity-icon {
color: #9ca3af;
}
}
}
&:not(.disabled):active {
background: #f1f5f9;
transform: scale(0.95);
}
}
.quantity-display {
min-width: 80rpx;
height: 60rpx;
display: flex;
align-items: center;
justify-content: center;
background: #ffffff;
border-left: 1rpx solid #e2e8f0;
border-right: 1rpx solid #e2e8f0;
.quantity-text {
font-size: 28rpx;
font-weight: 600;
color: #1f2937;
}
}
}
}
// 禁售状态区域
.sold-out-section {
display: flex;
justify-content: flex-end;
.sold-out-btn {
background: #f3f4f6;
color: #9ca3af;
padding: 16rpx 32rpx;
text-align: center;
border-radius: 12rpx;
font-size: 24rpx;
font-weight: 500;
border: 1rpx solid #e5e7eb;
cursor: not-allowed;
}
}
.toBuy {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: #ffffff;
padding: 16rpx 24rpx;
text-align: center;
border-radius: 12rpx;
font-size: 24rpx;
font-weight: 600;
border: none;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
position: relative;
overflow: hidden;
box-shadow: 0 2rpx 8rpx rgba(102, 126, 234, 0.2);
flex-shrink: 0;
min-width: 120rpx;
&::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(
90deg,
transparent,
rgba(255, 255, 255, 0.2),
transparent
);
transition: left 0.5s;
}
&:hover {
background: linear-gradient(135deg, #5a67d8 0%, #6b46c1 100%);
transform: translateY(-1rpx);
box-shadow: 0 4rpx 12rpx rgba(102, 126, 234, 0.3);
&::before {
left: 100%;
}
}
&:active {
transform: scale(0.95) translateY(0);
}
}
}
}
.list-bottom {
text-align: center;
padding: 40rpx 0 60rpx;
.bottom-text {
color: #64748b;
font-size: 24rpx;
}
}
}
// 动画定义
@keyframes slideIn {
from {
transform: scaleX(0);
opacity: 0;
}
to {
transform: scaleX(1);
opacity: 1;
}
}
@keyframes slideDown {
from {
opacity: 0;
transform: translateY(-50rpx);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(30rpx);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes slideInUp {
from {
opacity: 0;
transform: translateY(40rpx);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes pulse {
0%,
100% {
transform: scale(1);
}
50% {
transform: scale(1.02);
}
}
@keyframes promoBreathe {
0%,
100% {
transform: scale(1);
box-shadow: 0 8rpx 24rpx rgba(255, 107, 53, 0.4);
}
50% {
transform: scale(1.05);
box-shadow: 0 12rpx 32rpx rgba(255, 107, 53, 0.6);
}
}
@keyframes shimmer {
0% {
left: -100%;
}
50% {
left: -100%;
}
100% {
left: 100%;
}
}
// 商品卡片延迟动画
.goodList_i:nth-child(1) {
animation-delay: 0.1s;
}
.goodList_i:nth-child(2) {
animation-delay: 0.2s;
}
.goodList_i:nth-child(3) {
animation-delay: 0.3s;
}
.goodList_i:nth-child(4) {
animation-delay: 0.4s;
}
.goodList_i:nth-child(5) {
animation-delay: 0.5s;
}
.goodList_i:nth-child(n + 6) {
animation-delay: 0.6s;
}
</style>