Files
xdd-uniapp-new/pkg_user/views/myFavorite.vue
T
2024-02-06 12:42:46 +08:00

551 lines
13 KiB
Vue

<template>
<view v-if="ready">
<view class="my-tabs flex jc-between">
<view class="tab-item flex jc-center ai-center"
:class="{'active':tabsIndex===index,'has-vertical':tabsVertical===index}" v-for="(item,index) in tabs"
:key="index" @click="changeTabs(index)">
{{ item.label }}
</view>
</view>
<view class="manage flex jc-end ai-center" @click="changeManage">
<image :src="webUrl+'/20240107224533038041.png'" mode="scaleToFill"/>
管理
</view>
<!--商品列表-->
<scroll-view class="scroll-box" :class="{'manage-state':isManage}" scroll-y
@scrolltolower="fetchProduct()" v-if="tabsIndex===0">
<view class="tc" style="margin-top: 200rpx;" v-if="productList.length===0">
<image style="width: 305rpx;height: 332rpx" :src="webUrl+'/20240102232718644743.png'" mode="scaleToFill"/>
</view>
<view class="item-commodity flex ai-center" v-for="(item,index) in productList" :key="index">
<CheckboxIcon style="margin-right: 16rpx;" :default-state="item['state']" :mark="[index]" @change="onChange"
v-if="isManage"/>
<image class="cover flex-0" :src="item['image']" mode="scaleToFill" @click="navToGoods(item['productId'])"/>
<view>
<view class="more-t bold">{{ item['storeName'] }}</view>
<view class="sub">规格:{{ item['sku'] }}</view>
<view class="flex jc-between ai-end">
<view class="price flex ai-end">
<text style="color:#E92727"></text>
<text style="color:#E92727;font-size: 32rpx;line-height: 48rpx">{{ item['price'] }}</text>
<text style="margin-left: 10rpx;text-decoration:line-through">{{ item['otPrice'] }}</text>
</view>
<view class="cart flex jc-center ai-center">
<image :src="webUrl+'/20240107224527293534.png'" mode="scaleToFill" @click="addCart(item)"/>
</view>
</view>
</view>
</view>
</scroll-view>
<!--店铺列表-->
<scroll-view class="scroll-box" :class="{'manage-state':isManage}" scroll-y @scrolltolower="fetchStore()"
v-if="tabsIndex===1">
<view class="tc" style="margin-top: 200rpx;" v-if="storeList.length===0">
<image style="width: 305rpx;height: 332rpx" :src="webUrl+'/20240102232718644743.png'" mode="scaleToFill"/>
</view>
<view class="item-store flex ai-start" v-for="(item,index) in storeList" :key="index">
<view class="flex flex-0 ai-center">
<CheckboxIcon style="margin-right: 16rpx;" :default-state="item['state']" :mark="[index]" @change="onChange"
v-if="isManage"/>
<image class="cover" :src="item['logo']" mode="scaleToFill"/>
</view>
<view>
<view class="flex jc-between ai-start" style="width: 594rpx">
<view>
<view class="one-t bold">{{ item['name'] }}</view>
<view class="sub">{{ item['time'] === '刚刚' ? '刚刚' : item['time'] + '前收藏' }}</view>
</view>
<view class="btn-nav flex jc-center ai-center" :style="{'margin-right':(isManage?48:0)+'rpx'}"
@click="navToStore(item['hotelId'])">进店逛逛
<image style="width: 24rpx;height: 24rpx" :src="webUrl+'/20240109234706982937.png'" mode="scaleToFill"/>
</view>
</view>
<view class="flex jc-start ai-center" style="margin-top: 16rpx;">
<image class="img-product" :src="img" mode="scaleToFill"
v-for="(img,i) in item['productImages']" :key="i"/>
</view>
</view>
</view>
</scroll-view>
<!--视频列表-->
<scroll-view class="scroll-box" :class="{'manage-state':isManage}" scroll-y
@scrolltolower="fetchVideo()"
v-if="tabsIndex===2">
<view class="tc" style="margin-top: 200rpx;" v-if="videoList.length===0">
<image style="width: 305rpx;height: 332rpx" :src="webUrl+'/20240102232718644743.png'" mode="scaleToFill"/>
</view>
<view class="flex flex-wrap" style="padding:40rpx 32rpx">
<view class="item-video" v-for="(item,index) in videoList" :key="index">
<CheckboxIcon class="video-check" :default-state="item['state']" :mark="[index]" @change="onChange"
v-if="isManage"/>
<image class="img-cover" :src="item['cover']" mode="scaleToFill" @click="navToVideo(item['videoId'])"/>
<view class="item-mask flex jc-end ai-end">
<image class="icon-heart" :src="webUrl+'/20240113205505660820.png'" mode="scaleToFill"/>
<text>{{ item['favoriteCount'] }}</text>
</view>
</view>
</view>
</scroll-view>
<view class="fixed-manage flex jc-between ai-center" v-if="isManage">
<view style="margin-left: 30rpx;">
<CheckboxIcon style="margin-right: 16rpx;" :default-state="isAll" :mark="[-1]" @change="onChange"/>
<text style="color:#666666;font-size: 32rpx">全选({{ selectCount }})</text>
</view>
<view class="flex ai-center">
<view class="btn flex jc-center ai-center" style="background: #D5D7D8" @click="changeManage">返回</view>
<view class="btn flex jc-center ai-center" style="background: #FD5749" @click="remove">删除</view>
</view>
</view>
</view>
</template>
<script setup>
import {computed, onMounted, ref} from '@vue/composition-api'
import {VUE_APP_RESOURCES_URL as webUrl} from '../../config/index'
import {getProductList, getStoreList, getVideoList, removeFavorite} from "@/api/favorite";
import {postCartAdd} from "@/api/store";
import CheckboxIcon from "@/components/CheckboxIcon.vue";
const ready = ref(false)
onMounted(() => {
ready.value = true
fetchProduct()
})
const navToGoods = (id) => {
uni.navigateTo({
url: "/pages/shop/GoodsCon/index?id=" + id
})
}
// 导航
const tabs = ref([
{key: 0, label: '商品'},
{key: 1, label: '店铺'},
{key: 2, label: '视频'},
])
const tabsIndex = ref(0)
const changeTabs = (index) => {
if (isManage.value) return
tabsIndex.value = index
page.value = 1
fetchData()
}
const fetchData = () => {
switch (tabsIndex.value) {
case 0:
fetchProduct()
break
case 1:
fetchStore()
break
case 2:
fetchVideo()
break
}
}
const tabsVertical = computed(() => {
if (tabsIndex.value === 0) return 1
if (tabsIndex.value === 1) return -1
if (tabsIndex.value === 2) return 0
})
// 管理
const isManage = ref(false)
const changeManage = () => {
isManage.value = !isManage.value
}
const isAll = ref(false)
const selectCount = computed(() => {
let count = 0, countList
switch (tabsIndex.value) {
case 0:
countList = productList.value
break
case 1:
countList = storeList.value
break
case 2:
countList = videoList.value
break
}
countList.forEach(item => {
if (item['state']) count++
})
return count
})
const onChange = (state, mark) => {
if (mark[0] === -1) {
isAll.value = state
handleAll(state)
return
}
switch (tabsIndex.value) {
case 0:
productList.value[mark]['state'] = state
break
case 1:
storeList.value[mark]['state'] = state
break
case 2:
videoList.value[mark]['state'] = state
break
}
}
const handleAll = (state) => {
switch (tabsIndex.value) {
case 0:
productList.value.forEach(item => item.state = state)
break
case 1:
storeList.value.forEach(item => item.state = state)
break
case 2:
videoList.value.forEach(item => item.state = state)
break
}
}
const remove = async () => {
uni.showModal({
title: '确认删除已选项目?',
content: '',
showCancel: true,
cancelText: '取消',
confirmText: '确认',
success: async (r) => {
if (r.confirm) {
let ids = []
switch (tabsIndex.value) {
case 0:
productList.value.forEach(item => {
if (item['state']) ids.push(item.id)
})
break
case 1:
storeList.value.forEach(item => {
if (item['state']) ids.push(item.id)
})
break
case 2:
videoList.value.forEach(item => {
if (item['state']) ids.push(item.id)
})
break
}
const res = await removeFavorite(ids)
if (res.success) {
isAll.value = false
fetchData()
removeAfter()
}
}
}
})
}
const removeAfter = () => {
switch (tabsIndex.value) {
case 0:
productList.value = productList.value.filter(item => !item['state'])
break
case 1:
storeList.value = storeList.value.filter(item => !item['state'])
break
case 2:
videoList.value = videoList.value.filter(item => !item['state'])
break
}
}
// 列表
const page = ref(1)
const productList = ref([])
const fetchProduct = async () => {
if (page.value === 1) {
productList.value = []
}
const res = await getProductList(page.value)
if (res.success) {
res.data['records']?.forEach(item => {
item.state = false
productList.value.push(item)
})
page.value++
}
}
const storeList = ref([])
const fetchStore = async () => {
if (page.value === 1) {
storeList.value = []
}
const res = await getStoreList(page.value)
if (res.success) {
res.data['records']?.forEach(item => {
item.time = uni.$u.timeFrom(Date.parse(item.createTime))
item.state = false
storeList.value.push(item)
})
page.value++
}
}
const videoList = ref([])
const fetchVideo = async () => {
if (page.value === 1) {
videoList.value = []
}
const res = await getVideoList(page.value)
if (res.success) {
res.data['records']?.forEach(item => {
item.state = false
videoList.value.push(item)
})
page.value++
}
}
const addCart = async (item) => {
const param = {
productId: item.productId,
uniqueId: item.uniqueId,
cartNum: 1
}
const res = await postCartAdd(param)
if (res.success) {
uni.showToast({
title: res.msg,
icon: 'none'
}).catch()
}
}
const navToStore = (id) => {
uni.navigateTo({
url: '/pagesInn/inn/innHome?id=' + id
})
}
const navToVideo = (videoId) => {
if (isManage.value) return
uni.navigateTo({
url: '/pkg-video/views/watch?videoId=' + videoId
})
}
</script>
<style scoped lang="less">
.my-tabs {
width: 686rpx;
height: 72rpx;
margin: 24rpx 32rpx 0;
border-radius: 40rpx;
background: rgba(255, 255, 255, 1);
.tab-item {
position: relative;
width: 230rpx;
color: #333333;
font-size: 32rpx;
}
.active {
border-radius: 40rpx;
background: #C41617;
color: #FFFFFF;
font-size: 34rpx;
font-weight: bold;
}
.has-vertical:after {
content: '';
position: absolute;
right: 0;
width: 2rpx;
height: 36rpx;
background: #999999;
}
}
.manage {
margin: 12rpx 32rpx 18rpx;
color: #333333;
font-size: 28rpx;
image {
width: 32rpx;
height: 32rpx;
}
}
.scroll-box {
height: calc(100vh - 168rpx);
box-sizing: border-box;
background: #FFFFFF;
}
.manage-state {
height: calc(100vh - 268rpx);
}
.item-commodity {
padding: 24rpx 30rpx;
.cover {
width: 220rpx;
height: 220rpx;
margin-right: 8rpx;
border-radius: 8rpx;
}
.more-t {
color: #333333;
font-size: 34rpx;
line-height: 50rpx;
}
.sub {
margin-top: 10rpx;
color: #999999;
font-size: 28rpx;
line-height: 40rpx;
}
.price {
color: #666666;
font-size: 28rpx;
line-height: 40rpx;
}
.cart {
width: 84rpx;
height: 56rpx;
margin-top: 14rpx;
background: #C41617;
border-radius: 28rpx;
image {
width: 48rpx;
height: 48rpx;
}
}
}
.item-store {
padding: 34rpx 32rpx 34rpx 30rpx;
.cover {
width: 80rpx;
height: 80rpx;
margin-right: 10rpx;
border: 4rpx solid #E92727;
border-radius: 50%;
}
.one-t {
width: 360rpx;
color: #333333;
font-size: 36rpx;
}
.sub {
margin-top: 8rpx;
color: #999999;
font-size: 24rpx;
}
.btn-nav {
width: 172rpx;
height: 56rpx;
border: 2px solid #E92727;
border-radius: 28rpx;
color: #E92727;
font-size: 28rpx;
}
.img-product {
width: 140rpx;
height: 140rpx;
margin-right: 10rpx;
border-radius: 8rpx;
}
}
.item-video {
position: relative;
width: 218rpx;
height: 320rpx;
margin-right: 16rpx;
margin-bottom: 20rpx;
border-radius: 8rpx;
overflow: hidden;
.video-check {
position: absolute;
left: 20rpx;
top: 20rpx;
}
.img-cover {
width: 100%;
height: 100%;
}
.item-mask {
position: absolute;
left: 0;
bottom: 0;
width: 218rpx;
height: 96rpx;
box-sizing: border-box;
padding: 8rpx;
background: linear-gradient(180deg, rgba(0, 0, 0, 0) 0%, #000000 100%);
color: #FFFFFF;
font-size: 28rpx;
.icon-heart {
width: 32rpx;
height: 32rpx;
margin-right: 4rpx;
}
}
}
.item-video:nth-child(3n) {
margin-right: 0;
}
.fixed-manage {
position: fixed;
left: 0;
bottom: 0;
width: 100vw;
height: 100rpx;
background: #FFFFFF;
box-shadow: 0 6rpx 12rpx rgba(0, 0, 0, 0.16);
.btn {
width: 196rpx;
height: 100rpx;
color: #FFFFFF;
font-size: 32rpx;
}
}
</style>