@ -0,0 +1,34 @@ |
|||
{ |
|||
"code": 200, |
|||
"msg": "操作成功", |
|||
"data": [ |
|||
{ |
|||
"regionCode": "mPfaxPdRDVg6CLhdyiIUT", |
|||
"regionName": "SKP-S", |
|||
"regionNameEn": "SKP-S", |
|||
"regionNameJa": "", |
|||
"regionNameOl": "" |
|||
}, |
|||
{ |
|||
"regionCode": "L-qsFUgjr8v2vTTpBGVGF", |
|||
"regionName": "美食大道", |
|||
"regionNameEn": "美食大道英文", |
|||
"regionNameJa": "", |
|||
"regionNameOl": "" |
|||
}, |
|||
{ |
|||
"regionCode": "N7jMkq7sak-vKNK9rdH7Z", |
|||
"regionName": "K大道", |
|||
"regionNameEn": "K大道英文", |
|||
"regionNameJa": "", |
|||
"regionNameOl": "" |
|||
}, |
|||
{ |
|||
"regionCode": "5LD1gMoB4eZoMmIFe0dug", |
|||
"regionName": "SKP", |
|||
"regionNameEn": "SKP", |
|||
"regionNameJa": "", |
|||
"regionNameOl": "" |
|||
} |
|||
] |
|||
} |
|||
|
After Width: | Height: | Size: 391 B |
|
After Width: | Height: | Size: 918 B |
|
After Width: | Height: | Size: 450 B |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 476 B |
|
After Width: | Height: | Size: 476 B |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 3.4 KiB |
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 5.7 KiB |
|
After Width: | Height: | Size: 4.8 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 696 B |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 947 B |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 983 B |
|
After Width: | Height: | Size: 654 B |
|
After Width: | Height: | Size: 681 B |
|
After Width: | Height: | Size: 571 B |
|
After Width: | Height: | Size: 478 B |
|
After Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 975 B |
@ -0,0 +1,60 @@ |
|||
<template> |
|||
<transition appear enter-active-class="animate__animated animate__fadeIn" leave-active-class="animate__animated animate__fadeOut"> |
|||
<div class="global-masker" @click.self="handleClick"> |
|||
<div class="bg-img" @click.self="handleClick" /> |
|||
<slot class="mask-content"></slot> |
|||
</div> |
|||
</transition> |
|||
</template> |
|||
|
|||
<script setup lang="ts"> |
|||
import { ref } from 'vue' |
|||
defineProps({ |
|||
type: { |
|||
type: Number, |
|||
default: 0 |
|||
} |
|||
}) |
|||
const emit = defineEmits(['click']) |
|||
const flag = ref(true) |
|||
function handleClick() { |
|||
flag.value = false |
|||
const timerId = setTimeout(() => { |
|||
clearTimeout(timerId) |
|||
emit('click') |
|||
}, 20) |
|||
} |
|||
</script> |
|||
<style lang="scss" scoped> |
|||
.global-masker { |
|||
position: fixed; |
|||
top: 0; |
|||
right: 0; |
|||
bottom: 0; |
|||
left: 0; |
|||
z-index: 9900; |
|||
background: rgb(142 144 145 / 50%); |
|||
animation-duration: 0.3s; |
|||
backdrop-filter: blur(50px); |
|||
.bg-img { |
|||
position: absolute; |
|||
z-index: 0; |
|||
// background: rgba(0, 0, 0, 0.3) url('@/assets/images/home/inner_bgNormal.jpg'); |
|||
width: 100vw; |
|||
height: 100vh; |
|||
// backdrop-filter: blur(30px); |
|||
filter: blur(0); |
|||
} |
|||
.blur-wrapper { |
|||
position: absolute; |
|||
top: 0; |
|||
right: 0; |
|||
bottom: 0; |
|||
left: 0; |
|||
z-index: 9901; |
|||
} |
|||
.mask-content { |
|||
z-index: 9902; |
|||
} |
|||
} |
|||
</style> |
|||
@ -0,0 +1,401 @@ |
|||
<template> |
|||
<transition appear enter-active-class="animate__animated animate__fadeIn" leave-active-class="animate__animated animate__fadeOut"> |
|||
<masker @click="close"> |
|||
<transition |
|||
appear |
|||
:enter-active-class="'animate__animated animate__fadeInUp'" |
|||
leave-active-class="animate__animated animate__zoomOut" |
|||
> |
|||
<div class="shop-detail-wrapper"> |
|||
<div class="intro-container"> |
|||
<!-- 图片 --> |
|||
<div v-if="false" class="carousel"> |
|||
<swiper |
|||
:modules="modules" |
|||
:autoplay="{ |
|||
delay: 5000 |
|||
}" |
|||
:observer="true" |
|||
:observe-parents="true" |
|||
:effect="'fade'" |
|||
:centered-slides="true" |
|||
:pagination="{ el: '.detail' }" |
|||
:dynamic-bullets="true" |
|||
> |
|||
<swiper-slide v-for="(item, index) in shop.doorMaterialList" :key="index" class="slide"> |
|||
<img :src="item" class="banner" /> |
|||
</swiper-slide> |
|||
</swiper> |
|||
<img v-if="!shop.doorMaterialList?.length" src="../../assets/images/empty_big.svg" class="banner no-data" alt="" /> |
|||
<div class="swiper-pagination detail"></div> |
|||
</div> |
|||
<!-- logo 名称 属性--> |
|||
<div class="name-wrapper"> |
|||
<div class="logo-wrapper"> |
|||
<img |
|||
v-if="shop.logoUrl" |
|||
class="shop-logo" |
|||
:src="shop.logoUrl.search(config.sourceUrl) >= 0 ? shop.logoUrl : config.sourceUrl + shop.logoUrl" |
|||
alt="" |
|||
/> |
|||
<img v-else class="shop-logo" src="@/assets/images/empty_small.svg" alt="" /> |
|||
</div> |
|||
<div class="marquee-wrapper"> |
|||
<span v-if="marqueesRef?.state?.duration === 0" style="width: 100%" class="name">{{ |
|||
switchLanguage(shop, 'shopName') |
|||
}}</span> |
|||
|
|||
<marquees |
|||
ref="marqueesRef" |
|||
:style="{ height: marqueesRef?.state?.duration === 0 ? '0px' : '' }" |
|||
:speed="40" |
|||
:delay="0.8" |
|||
class="name" |
|||
:content="switchLanguage(shop, 'shopName')" |
|||
>{{ switchLanguage(shop, 'shopName') }} |
|||
</marquees> |
|||
</div> |
|||
<div class="shop-attr-group"> |
|||
<div v-if="shop.industryFatherName" class="working-item"> |
|||
<!-- shop.industryUrl --> |
|||
<img v-if="shop.industryFatherName" src="@/assets/images/shopDetail/icon_format.svg" alt="" /> |
|||
<span class="title">{{ switchLanguage(shop, 'industryFatherName') }}</span> |
|||
</div> |
|||
<div v-if="shop.houseNumber" class="working-item"> |
|||
<img src="../../assets/images/shopDetail/icon_address.svg" alt="" /> |
|||
<span class="title">{{ shop.floor + '-' + shop.houseNumber }}</span> |
|||
</div> |
|||
<div v-if="shop.businessHours" class="working-item"> |
|||
<img src="../../assets/images/shopDetail/icon_time.svg" alt="" /> |
|||
<span class="title">{{ shop.businessHours }}</span> |
|||
</div> |
|||
<div v-if="shop.contact" class="working-item"> |
|||
<img src="../../assets/images/shopDetail/icon_tel.svg" alt="" /> |
|||
<span class="title">{{ shop.contact }}</span> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="line"></div> |
|||
<!-- 详情 --> |
|||
<scroll-view |
|||
:list="[switchLanguage(shop, 'intro')]" |
|||
class="intro-scroll" |
|||
:scrollbar="true" |
|||
:pull-up="false" |
|||
:refresh-delay="500" |
|||
> |
|||
<p class="intro" v-html="switchLanguage(shop, 'intro')"></p> |
|||
</scroll-view> |
|||
<!-- 导航按钮 --> |
|||
<div v-if="shop.yaxis" class="go" @click="handleGo"> |
|||
<img src="@/assets/images/shopDetail/go.svg" alt="" /> |
|||
</div> |
|||
</div> |
|||
|
|||
<!-- 关闭按钮 --> |
|||
<div class="exit" @click="close"> |
|||
<img src="@/assets/images/shopDetail/close.svg" class="go_bg" alt="" /> |
|||
</div> |
|||
</div> |
|||
</transition> </masker |
|||
></transition> |
|||
</template> |
|||
|
|||
<script setup lang="ts"> |
|||
import { Autoplay, Pagination, EffectFade } from 'swiper' |
|||
import { Swiper, SwiperSlide } from 'swiper/vue' |
|||
import 'swiper/css' |
|||
import 'swiper/css/pagination' |
|||
import 'swiper/css/effect-fade' |
|||
import scrollView from '@/base/ScrollView/ScrollView.vue' |
|||
import masker from '@/base/Masker/Masker.vue' |
|||
import marquees from '@/base/Marquees/Marquees.vue' |
|||
import { useRouter } from 'vue-router' |
|||
import { useStatistics } from '@/composables/useStatistics' |
|||
import { storeToRefs } from 'pinia' |
|||
import { useRootStore } from '@/store/root' |
|||
import { computed, ref } from 'vue' |
|||
import { useSwitchLanguage } from '@/composables/useSwitchLanguage' |
|||
const { switchLanguage } = useSwitchLanguage() |
|||
const store = useRootStore() |
|||
const { shop, config } = storeToRefs(store) |
|||
const router = useRouter() |
|||
|
|||
const modules = [Autoplay, Pagination, EffectFade] |
|||
|
|||
const marqueesRef = ref() |
|||
const activityList = computed(() => shop.value.activityList) |
|||
//关闭窗口 |
|||
function close() { |
|||
store.SET_SHOW_DETAIL(false) |
|||
} |
|||
//导航 |
|||
function handleGo() { |
|||
store.SET_SHOW_DETAIL(false) |
|||
store.SET_SHOW_SEARCH(false) |
|||
router.push('/nav') |
|||
} |
|||
|
|||
useStatistics({ tag: 'shop', shopCode: shop.value.shopCode }) |
|||
</script> |
|||
|
|||
<style scoped lang="scss"> |
|||
:deep(.bscroll-vertical-scrollbar) { |
|||
z-index: 5 !important; |
|||
overflow: visible !important; |
|||
// width: 1px !important; |
|||
background: rgb(0 0 0 / 6%); |
|||
border-radius: 8px; |
|||
opacity: 1 !important; |
|||
.bscroll-indicator { |
|||
// width: 7px !important; |
|||
background: #d0b186 !important; |
|||
border: none !important; |
|||
border-radius: 8px !important; |
|||
box-shadow: 0 8px 20px rgb(0 0 0 / 10%); |
|||
} |
|||
} |
|||
:deep(.swiper-pagination-bullet) { |
|||
border-radius: 4px !important; |
|||
} |
|||
.shop-detail-wrapper { |
|||
position: relative; |
|||
z-index: 9003; |
|||
// margin-right: 0px; |
|||
// margin-left: 0px; |
|||
display: flex; |
|||
justify-content: center; |
|||
width: 864px; |
|||
height: 648px; |
|||
margin-top: 216px; |
|||
margin-left: 528px; |
|||
pointer-events: none; |
|||
background-color: #fff; |
|||
border-radius: 16px; |
|||
|
|||
& > * { |
|||
pointer-events: visible; |
|||
} |
|||
|
|||
// --swiper-theme-color: #d7ba92; |
|||
// --swiper-pagination-bullet-width: 28px; |
|||
// --swiper-pagination-bullet-height: 4px; |
|||
// --swiper-pagination-bullet-inactive-color: rgb(0 0 0 / 60%); |
|||
|
|||
.intro-container { |
|||
position: relative; |
|||
width: 100%; |
|||
height: 100%; |
|||
background: linear-gradient(to right, rgb(0 0 0 / 3%) 0%, rgb(0 0 0 / 3%) 37%, #fff 37%, #fff 100%); |
|||
border-radius: 16px; |
|||
} |
|||
|
|||
.carousel { |
|||
position: relative; |
|||
z-index: 1; |
|||
display: inline-block; |
|||
overflow: hidden; |
|||
width: 800px; |
|||
height: 450px; |
|||
// min-height: 450px; |
|||
// max-height: 450px; |
|||
margin-top: 0; |
|||
margin-left: 0; |
|||
// background: #d5d5d5; |
|||
background: rgb(255 255 255); |
|||
border-radius: 8px; |
|||
:deep(.swiper-slide) { |
|||
height: 450px; |
|||
border-radius: inherit; |
|||
} |
|||
.banner { |
|||
position: relative; |
|||
z-index: 1; |
|||
width: 100%; |
|||
height: inherit; |
|||
object-fit: cover; |
|||
border-radius: 8px; |
|||
&.no-data { |
|||
position: absolute; |
|||
right: 0; |
|||
left: 0; |
|||
// width: 300px; |
|||
margin: 0 auto; |
|||
object-fit: scale-down; |
|||
} |
|||
} |
|||
:deep(.swiper-pagination-bullet) { |
|||
border-radius: 59px; |
|||
} |
|||
.detail { |
|||
bottom: 18px; |
|||
left: 80%; |
|||
display: flex; |
|||
width: 20%; |
|||
:deep(.swiper-pagination-bullet) { |
|||
--swiper-pagination-bullet-width: 0px; |
|||
--swiper-pagination-bullet-horizontal-gap: 2px; |
|||
|
|||
flex-shrink: 0; |
|||
&.swiper-pagination-bullet-active { |
|||
--swiper-pagination-bullet-horizontal-gap: 4px; |
|||
--swiper-pagination-bullet-width: 24px; |
|||
} |
|||
&:not(.swiper-pagination-bullet-active) { |
|||
--swiper-pagination-bullet-horizontal-gap: 4px; |
|||
--swiper-pagination-bullet-width: 10px; |
|||
--swiper-pagination-bullet-height: 5px; |
|||
} |
|||
// &.swiper-pagination-bullet-active + span { |
|||
// --swiper-pagination-bullet-horizontal-gap: 8px; |
|||
// --swiper-pagination-bullet-width: 20px; |
|||
// } |
|||
} |
|||
span + .swiper-pagination-bullet-active { |
|||
--swiper-pagination-bullet-horizontal-gap: 4px; |
|||
--swiper-pagination-bullet-width: 24px; |
|||
} |
|||
:not(span + .swiper-pagination-bullet-active) { |
|||
--swiper-pagination-bullet-horizontal-gap: 4px; |
|||
--swiper-pagination-bullet-width: 2px; |
|||
} |
|||
} |
|||
} |
|||
.name-wrapper { |
|||
position: absolute; |
|||
top: 0; |
|||
left: 0; |
|||
z-index: 1; |
|||
width: 320px; |
|||
height: 648px; |
|||
.logo-wrapper { |
|||
position: absolute; |
|||
top: 64px; |
|||
left: 85px; |
|||
z-index: 1; |
|||
width: 150px; |
|||
height: 150px; |
|||
padding: 15px; |
|||
background: #fff; |
|||
border-radius: 8px; |
|||
box-shadow: 0 8px 20px 0 rgb(0 0 0 / 5%); |
|||
|
|||
img { |
|||
width: 120px; |
|||
height: 120px; |
|||
object-fit: scale-down; |
|||
} |
|||
} |
|||
|
|||
.marquee-wrapper { |
|||
overflow: hidden; |
|||
width: 280px; |
|||
margin-top: 234px; |
|||
margin-bottom: 0; |
|||
margin-left: 20px; |
|||
white-space: nowrap; |
|||
.name { |
|||
display: inline-block; |
|||
height: 36px; |
|||
font-size: 28px; |
|||
font-family: 'font_bold'; |
|||
text-align: center; |
|||
color: #534f46; |
|||
font-weight: 700; |
|||
} |
|||
} |
|||
.shop-attr-group { |
|||
position: absolute; |
|||
top: 64px; |
|||
left: 368px; |
|||
display: flex; |
|||
justify-content: flex-start; |
|||
width: 448px; |
|||
height: 60px; |
|||
flex-wrap: wrap; |
|||
.working-item { |
|||
display: flex; |
|||
align-items: center; |
|||
min-width: 224px; |
|||
height: 22px; |
|||
&:nth-child(odd) { |
|||
max-width: 224px; |
|||
} |
|||
&:nth-child(even) { |
|||
max-width: 224px; |
|||
} |
|||
img { |
|||
width: 20px; |
|||
height: 20px; |
|||
padding: 0; |
|||
margin-right: 16px; |
|||
background: rgb(0 0 0 / 0%); |
|||
border-radius: 0; |
|||
} |
|||
span { |
|||
padding-right: 5px; |
|||
font-size: 14px; |
|||
font-family: 'font_bold'; |
|||
color: #615c59; |
|||
font-weight: 700; |
|||
|
|||
@include no-wrap(); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
.line { |
|||
position: absolute; |
|||
top: 164px; |
|||
right: 64px; |
|||
width: 448px; |
|||
height: 1px; |
|||
background: rgb(0 0 0 / 6%); |
|||
} |
|||
.intro-scroll { |
|||
position: absolute; |
|||
top: 205px; |
|||
right: 26px; |
|||
z-index: 1; |
|||
overflow: hidden; |
|||
width: 470px; |
|||
height: 350px; |
|||
padding-right: 16px; |
|||
.intro { |
|||
font-size: 14px; |
|||
font-family: 'font_medium'; |
|||
text-align: justify; |
|||
white-space: pre-wrap; |
|||
color: #615c59; |
|||
line-height: 200%; |
|||
:deep(img) { |
|||
width: 100%; |
|||
} |
|||
} |
|||
} |
|||
.go { |
|||
position: absolute; |
|||
top: 422px; |
|||
left: 76px; |
|||
z-index: 1; |
|||
width: 168px; |
|||
} |
|||
.exit { |
|||
position: absolute; |
|||
top: -40px; |
|||
right: -40px; |
|||
z-index: 1; |
|||
width: 100px; |
|||
height: 100px; |
|||
padding: 26px; |
|||
background: var(--w-60, rgb(255 255 255 / 60%)); |
|||
border: 2px solid var(--w-100, #fff); |
|||
border-radius: 50px; |
|||
backdrop-filter: blur(20px); |
|||
// margin: auto; |
|||
img { |
|||
width: 48px; |
|||
} |
|||
} |
|||
} |
|||
</style> |
|||
@ -0,0 +1,210 @@ |
|||
<template> |
|||
<ul |
|||
ref="ulRef" |
|||
class="switch-tabs" |
|||
:style="{ |
|||
width: props.wrapperWidth + 'px', |
|||
height: props.wrapperHeight + 'px', |
|||
'flex-direction': props.direction! + '', |
|||
'background-color': props.background + '' |
|||
}" |
|||
> |
|||
<li |
|||
v-for="(item, index) of list" |
|||
:key="index" |
|||
:style="styles" |
|||
class="switch-tab-item" |
|||
:class="{ active: index === copyTabIdx }" |
|||
@click="switchTab(index)" |
|||
> |
|||
<!-- :style="{ |
|||
filter: 'brightness(0) invert(100%)', |
|||
opacity: '1' |
|||
}" --> |
|||
<img v-if="index === copyTabIdx" :src="item.iconSel" class="intro-icon" alt="" /> |
|||
<img v-else :src="item.icon" class="intro-icon" alt="" /> |
|||
|
|||
<div class="intro-wrapper"> |
|||
<span class="intro-name"> |
|||
{{ switchLanguage(item, 'name') }} |
|||
</span> |
|||
<span v-if="item.en" class="intro-name-en"> |
|||
{{ item.en }} |
|||
</span> |
|||
</div> |
|||
</li> |
|||
<li |
|||
ref="scrollBoxRef" |
|||
class="scroll-box" |
|||
:style="{ |
|||
top: props.direction === 'column' ? copyTabIdx * props.height + 'px' : '', |
|||
left: props.direction === 'row' ? copyTabIdx * props.width + 'px' : '', |
|||
width: props.width + 'px', |
|||
height: props.height + 'px' |
|||
}" |
|||
> |
|||
<!-- <svg width="180" height="80" viewBox="0 0 180 80" fill="none" xmlns="http://www.w3.org/2000/svg"> |
|||
<path |
|||
d="M0 16C0 7.16344 7.16344 0 16 0H164C172.837 0 180 7.16344 180 16V56C180 64.8366 172.837 72 164 72H114.38C112.475 72 110.586 72.3402 108.801 73.0045L95.5162 77.9474C91.9554 79.2724 88.0392 79.287 84.4686 77.9886L70.6491 72.9633C68.8965 72.326 67.046 72 65.1812 72H16C7.16344 72 0 64.8366 0 56V16Z" |
|||
fill="url(#paint0_linear_616_449)" |
|||
/> |
|||
<defs> |
|||
<linearGradient id="paint0_linear_616_449" x1="14" y1="4" x2="176.063" y2="22.7587" gradientUnits="userSpaceOnUse"> |
|||
<stop :stop-color="gradientColor.startColor" /> |
|||
<stop offset="1" :stop-color="gradientColor.endColor" /> |
|||
</linearGradient> |
|||
</defs> |
|||
</svg> --> |
|||
</li> |
|||
</ul> |
|||
</template> |
|||
|
|||
<script setup lang="ts"> |
|||
import { useSwitchLanguage } from '@/composables/useSwitchLanguage' |
|||
import { computed, ref, onMounted, watch, nextTick } from 'vue' |
|||
|
|||
const { switchLanguage } = useSwitchLanguage() |
|||
|
|||
type ListItem = { |
|||
name: string |
|||
en: string |
|||
icon: string |
|||
iconSel: string |
|||
} |
|||
type Props = { |
|||
list: ListItem[] |
|||
wrapperHeight?: number |
|||
wrapperWidth?: number |
|||
width?: number |
|||
height?: number |
|||
background?: string |
|||
direction?: string |
|||
tabIdx?: number |
|||
} |
|||
const props = withDefaults(defineProps<Props>(), { |
|||
list: () => [], |
|||
wrapperWidth: 200, |
|||
wrapperHeight: 192, |
|||
width: 200, |
|||
height: 192, |
|||
background: 'rgba(0, 0, 0, 0.03)', |
|||
direction: 'row', |
|||
tabIdx: 0 |
|||
}) |
|||
|
|||
// gradientColor: { |
|||
// type: Object, |
|||
// default: () => { |
|||
// return { |
|||
// startColor: '#A88AFD', |
|||
// endColor: '#CCB0FF' |
|||
// } |
|||
// } |
|||
// } |
|||
|
|||
// const padding = ref(0) |
|||
const copyTabIdx = ref(0) |
|||
const styles = computed(() => { |
|||
return { width: props.width + 'px', height: props.height + 'px' } |
|||
}) |
|||
|
|||
const emit = defineEmits(['click']) |
|||
onMounted(() => { |
|||
const timeId = setTimeout(() => { |
|||
clearTimeout(timeId) |
|||
nextTick(() => { |
|||
// scrollBox(props.tabIdx) |
|||
switchTab(0) |
|||
}) |
|||
}, 100) |
|||
}) |
|||
const ulRef = ref() |
|||
const scrollBoxRef = ref() |
|||
|
|||
function switchTab(index: number) { |
|||
copyTabIdx.value = index |
|||
emit('click', index) |
|||
} |
|||
|
|||
watch( |
|||
() => props.list, |
|||
() => { |
|||
nextTick(() => {}) |
|||
}, |
|||
{ |
|||
immediate: true |
|||
} |
|||
) |
|||
</script> |
|||
|
|||
<style lang="scss" scoped> |
|||
.switch-tabs { |
|||
position: relative; |
|||
display: flex; |
|||
align-items: center; |
|||
padding: 0; |
|||
border-radius: 16px; |
|||
flex-wrap: nowrap; |
|||
.switch-tab-item { |
|||
position: relative; |
|||
z-index: 2; |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: center; |
|||
flex-shrink: 0; |
|||
margin-right: 0; |
|||
margin-bottom: 0; |
|||
border-radius: 8px; |
|||
transition: all 0.3s linear; |
|||
|
|||
.intro-icon { |
|||
width: 32px; |
|||
height: 32px; |
|||
margin-right: 16px; |
|||
} |
|||
.intro-wrapper { |
|||
display: flex; |
|||
flex-direction: column; |
|||
font-family: 'font_bold'; |
|||
.intro-name { |
|||
font-size: 16px; |
|||
font-family: 'font_bold'; |
|||
color: #534f46; |
|||
font-weight: bold; |
|||
} |
|||
.intro-name-en { |
|||
padding-top: 6px; |
|||
font-size: 10px; |
|||
font-family: 'font_bold'; |
|||
color: #8e9090; |
|||
} |
|||
} |
|||
&.active { |
|||
background: #fff; |
|||
border-radius: 16px; |
|||
|
|||
// box-shadow: 0 -4px 0 0 rgb(177 189 220 / 10%) inset; |
|||
.intro-icon { |
|||
// filter: grayscale(1) brightness(300%); |
|||
} |
|||
.intro-wrapper { |
|||
.intro-name { |
|||
color: rgb(0 0 0 / 90%); |
|||
} |
|||
.intro-name-en { |
|||
color: rgb(0 0 0 / 90%); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
.scroll-box { |
|||
position: absolute; |
|||
top: 0; |
|||
left: 0; |
|||
z-index: 1; |
|||
display: none; |
|||
border-radius: 0; |
|||
transition: all 0.3s linear; |
|||
} |
|||
} |
|||
</style> |
|||
@ -0,0 +1,91 @@ |
|||
<template> |
|||
<div class="btn-title-group"> |
|||
<div |
|||
v-for="(item, index) in list" |
|||
:key="index" |
|||
class="btn-item" |
|||
:class="{ active: currentTypeId === index, 'btn-en': language === 'en' }" |
|||
@click="changeType(item, index)" |
|||
> |
|||
<h4> |
|||
{{ switchLanguage(item, 'title') }} |
|||
</h4> |
|||
<div v-if="false && list.length > 1" class="saw"></div> |
|||
</div> |
|||
</div> |
|||
</template> |
|||
|
|||
<script setup lang="ts"> |
|||
import { ref } from 'vue' |
|||
import { storeToRefs } from 'pinia' |
|||
import { useRootStore } from '@/store/root' |
|||
import { useSwitchLanguage } from '@/composables/useSwitchLanguage' |
|||
|
|||
const { switchLanguage } = useSwitchLanguage() |
|||
const store = useRootStore() |
|||
const { language } = storeToRefs(store) |
|||
const currentTypeId = ref(0) |
|||
|
|||
const emit = defineEmits(['change-type']) |
|||
type TypeItem = { |
|||
title: string |
|||
titleEn: string |
|||
} |
|||
defineProps<{ |
|||
list: TypeItem[] |
|||
}>() |
|||
|
|||
function changeType(item: TypeItem, index: number) { |
|||
currentTypeId.value = index |
|||
emit('change-type', { ...item, order: currentTypeId.value }) |
|||
} |
|||
</script> |
|||
|
|||
<style scoped lang="scss"> |
|||
.btn-title-group { |
|||
z-index: 1; |
|||
display: flex; |
|||
justify-content: space-between; |
|||
width: fit-content; |
|||
.btn-item { |
|||
display: flex; |
|||
h4 { |
|||
height: 81px; |
|||
// width: 96px; |
|||
padding-bottom: 18px; |
|||
font-size: 32px; |
|||
font-family: 'font_bold'; |
|||
color: rgb(0 0 0 / 40%); |
|||
line-height: 57px; |
|||
font-style: normal; |
|||
font-weight: 400; |
|||
} |
|||
&.btn-en { |
|||
h4 { |
|||
font-size: 19px; |
|||
} |
|||
} |
|||
&:nth-child(odd) { |
|||
.saw { |
|||
width: 0; |
|||
height: 26px; |
|||
margin-right: 40px; |
|||
margin-left: 40px; |
|||
background: #84754e; |
|||
} |
|||
} |
|||
&.active { |
|||
h4 { |
|||
font-size: 40px; |
|||
color: rgb(0 0 0 / 80%); |
|||
border-bottom: 6px solid #d7ba92; |
|||
} |
|||
&.btn-en { |
|||
h4 { |
|||
font-size: 21px; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
</style> |
|||
@ -1,9 +1,9 @@ |
|||
declare interface Industry { |
|||
isSpecial: 0 | 1 //1特殊业态 反之不是
|
|||
industryId: number //唯一标识
|
|||
industryCode: string //唯一标识
|
|||
industryName: string //业态名称
|
|||
industryNameEn: string //业态英文
|
|||
shopNum: number //店铺总数
|
|||
industryList: Array<{ industryId: number; industryName: string; shopNum: number }> |
|||
industryList: Array<{ industryCode: string; industryName: string; shopNum: number }> |
|||
fileUrl?: string |
|||
} |
|||
|
|||
@ -0,0 +1,22 @@ |
|||
declare interface Region { |
|||
/** |
|||
* 区域编码 |
|||
*/ |
|||
regionCode: string |
|||
/** |
|||
* 区域名称 |
|||
*/ |
|||
regionName: string |
|||
/** |
|||
* 区域名称英文 |
|||
*/ |
|||
regionNameEn: string |
|||
/** |
|||
* 区域名称日文 |
|||
*/ |
|||
regionNameJa?: string |
|||
/** |
|||
* 区域名称其他语言 |
|||
*/ |
|||
regionNameOl?: string |
|||
} |
|||
@ -0,0 +1,29 @@ |
|||
<template> |
|||
<div class="brand-container"> |
|||
<!-- 店铺列表(业态和楼层控制) --> |
|||
<shopList></shopList> |
|||
<!-- 推荐店铺 --> |
|||
<recommendShop></recommendShop> |
|||
</div> |
|||
</template> |
|||
<script setup lang="ts"> |
|||
import recommendShop from './recommendShop.vue' |
|||
import shopList from './shopList.vue' |
|||
</script> |
|||
<style lang="scss" scoped> |
|||
:deep(.swiper-wrapper) { |
|||
transition-timing-function: linear !important; /* 之前是ease-out */ |
|||
} |
|||
.brand-container { |
|||
position: relative; |
|||
display: inline-block; |
|||
display: flex; |
|||
justify-content: space-between; |
|||
padding-top: 152px; |
|||
padding-right: 0; |
|||
padding-left: 56px; |
|||
|
|||
--animate-duration: 0.6s; |
|||
--animate-delay: 1s; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,407 @@ |
|||
<template> |
|||
<scrollView ref="shopScroll" class="format__scroll" :refresh-delay="200" :list="list"> |
|||
<div class="filter-wrapper"> |
|||
<div |
|||
v-for="(item, index) of copyList" |
|||
:key="item.industryCode" |
|||
:style="{ height: index === active ? 'fit-content' : '' }" |
|||
class="filter-div-f" |
|||
> |
|||
<div |
|||
ref="filterDivEl" |
|||
class="filter-div" |
|||
:class="{ 'filter-active': active == index && activeSon === -1 }" |
|||
:style="{ |
|||
background: activeSon === -1 ? getBackground(index) : '' |
|||
}" |
|||
@click.self="clickFormat(item, index, $event)" |
|||
> |
|||
<div class="left"> |
|||
<!-- :style="{ opacity: item.fileUrl ? 1 : 0 }" --> |
|||
<img :src="item.fileUrl ? item.fileUrl : normalIcon" class="format-img" alt="" /> |
|||
<p class="format-name">{{ switchLanguage(item, 'industryName') }}</p> |
|||
</div> |
|||
|
|||
<div class="right"> |
|||
<p class="format-number">{{ item.shopNum }}</p> |
|||
<img |
|||
v-if="item.industryList.length > 0" |
|||
class="saw-img" |
|||
src="@/assets/images/brand/down.svg" |
|||
:style="{ |
|||
transform: active === index ? 'rotate(180deg)' : '' |
|||
}" |
|||
alt="" |
|||
/> |
|||
<p v-if="false && item.shopNum" style="margin-right: 5px"> |
|||
<svg v-show="active != index" width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"> |
|||
<path |
|||
d="M3.52851 5.52876C3.78886 5.26841 4.21097 5.26841 4.47132 5.52876L7.99992 9.05735L11.5285 5.52876C11.7889 5.26841 12.211 5.26841 12.4713 5.52876C12.7317 5.78911 12.7317 6.21122 12.4713 6.47157L8.47132 10.4716C8.21097 10.7319 7.78886 10.7319 7.52851 10.4716L3.52851 6.47157C3.26816 6.21122 3.26816 5.78911 3.52851 5.52876Z" |
|||
fill="black" |
|||
/> |
|||
</svg> |
|||
|
|||
<svg v-show="active == index" width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"> |
|||
<path |
|||
d="M7.52851 5.52876C7.78886 5.26841 8.21097 5.26841 8.47132 5.52876L12.4713 9.52876C12.7317 9.78911 12.7317 10.2112 12.4713 10.4716C12.211 10.7319 11.7889 10.7319 11.5285 10.4716L7.99992 6.94297L4.47132 10.4716C4.21097 10.7319 3.78886 10.7319 3.52851 10.4716C3.26816 10.2112 3.26816 9.78911 3.52851 9.52876L7.52851 5.52876Z" |
|||
fill="white" |
|||
/> |
|||
</svg> |
|||
</p> |
|||
|
|||
<p v-else-if="false && !isFood" style="margin-right: 5px"> |
|||
<svg v-show="active != index" width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"> |
|||
<path |
|||
d="M3.52851 5.52876C3.78886 5.26841 4.21097 5.26841 4.47132 5.52876L7.99992 9.05735L11.5285 5.52876C11.7889 5.26841 12.211 5.26841 12.4713 5.52876C12.7317 5.78911 12.7317 6.21122 12.4713 6.47157L8.47132 10.4716C8.21097 10.7319 7.78886 10.7319 7.52851 10.4716L3.52851 6.47157C3.26816 6.21122 3.26816 5.78911 3.52851 5.52876Z" |
|||
fill="black" |
|||
/> |
|||
</svg> |
|||
|
|||
<svg v-show="active == index" width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"> |
|||
<path |
|||
d="M7.52851 5.52876C7.78886 5.26841 8.21097 5.26841 8.47132 5.52876L12.4713 9.52876C12.7317 9.78911 12.7317 10.2112 12.4713 10.4716C12.211 10.7319 11.7889 10.7319 11.5285 10.4716L7.99992 6.94297L4.47132 10.4716C4.21097 10.7319 3.78886 10.7319 3.52851 10.4716C3.26816 10.2112 3.26816 9.78911 3.52851 9.52876L7.52851 5.52876Z" |
|||
fill="white" |
|||
/> |
|||
</svg> |
|||
</p> |
|||
</div> |
|||
</div> |
|||
<div |
|||
v-show="item.shopNum" |
|||
:ref="el => (formatListEl = el)" |
|||
class="format-list" |
|||
:class="{ 'format-list1': active != index }" |
|||
:style="{ height: copyList[index].industryList.length ? copyList[index].industryList.length * 56 + 'px' : 0 }" |
|||
> |
|||
<div |
|||
v-for="(items, cindex) of item.industryList" |
|||
:key="items.industryCode" |
|||
class="format-item" |
|||
:class="{ 'filter-active-child': activeSon == cindex }" |
|||
@click="clickItem(cindex, items, $event)" |
|||
> |
|||
<p class="format-name">{{ switchLanguage(items, 'industryName') }}</p> |
|||
<p class="format-number">{{ items.shopNum }}</p> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</scrollView> |
|||
</template> |
|||
|
|||
<script setup lang="ts"> |
|||
import normalIcon from '@/assets/images/brand/format_normal.svg' |
|||
import { computed, ref, nextTick } from 'vue' |
|||
import scrollView from '@/base/ScrollView/ScrollView.vue' |
|||
import { useSwitchLanguage } from '@/composables/useSwitchLanguage' |
|||
|
|||
const { switchLanguage } = useSwitchLanguage() |
|||
type Props = { |
|||
list: Industry[] |
|||
isFood?: boolean |
|||
} |
|||
|
|||
const shopScroll = ref() |
|||
const props = withDefaults(defineProps<Props>(), { |
|||
list: () => [], |
|||
isFood: false |
|||
}) |
|||
const emits = defineEmits(['clickFormat', 'clickFormatItem']) |
|||
|
|||
const active = ref(0) |
|||
const activeSon = ref(-1) |
|||
const imgRotate = ref(false) |
|||
const formatListEl = ref() |
|||
const copyList = computed(() => { |
|||
return [ |
|||
{ |
|||
fileUrl: require('@/assets/images/brand/all.svg'), |
|||
isSpecial: 0, |
|||
industryCode: 0, |
|||
industryName: '全部品牌', |
|||
industryNameEn: '', |
|||
shopNum: 0, |
|||
industryList: [] |
|||
}, |
|||
...props.list |
|||
] |
|||
}) |
|||
|
|||
const clickFormat = (item: Industry, index: number, $event: Event) => { |
|||
// nextTick(() => { |
|||
// const domfilter: HTMLDivElement = document.querySelectorAll('.filter-div')[index] as HTMLDivElement |
|||
|
|||
// if (item.industryList.length > 0) { |
|||
// domfilter.style.borderRadius = '8px 8px 0 0' |
|||
// } else { |
|||
// domfilter.style.borderRadius = '8px' |
|||
// } |
|||
// }) |
|||
if (item.industryList.length > 0) { |
|||
// console.log('formatListEl :>> ', $event) |
|||
const target: any = $event.target |
|||
//关闭、打开 |
|||
if (active.value === index) { |
|||
imgRotate.value = !imgRotate.value |
|||
target?.classList.add('filter-active') |
|||
// console.dir('$event.target :>> ', $event.target.nextElementSibling) |
|||
nextTick(() => { |
|||
// console.log('formatListEl :>> ', formatListEl) |
|||
// const domClass = formatListEl.value.classList |
|||
// const domClass = $event.target.nextElementSibling.children[index].classList |
|||
// console.log('domClass :>> ', domClass) |
|||
|
|||
if (index === active.value) { |
|||
// $event.target.classList.toggle('format-list1') |
|||
const dom: HTMLDivElement = document.querySelectorAll('.format-list')[index] as HTMLDivElement |
|||
const domfilter: HTMLDivElement = document.querySelectorAll('.filter-div')[index] as HTMLDivElement |
|||
// console.log('dom :>> ', dom) |
|||
dom.classList.toggle('format-list1') |
|||
|
|||
// if (dom.classList.contains('format-list1')) { |
|||
// domfilter.style.borderRadius = '8px' |
|||
// } else { |
|||
// domfilter.style.borderRadius = '8px 8px 0px 0px' |
|||
// } |
|||
|
|||
// if ($event.target.classList.includes('format-list1')) { |
|||
// $event.target.classList.remove('format-list1') |
|||
// } else { |
|||
// $event.target.classList.add('format-list1') |
|||
// } |
|||
} |
|||
// if (typeof domClass[1] === 'undefined') { |
|||
// formatListEl.value.add('format-list1') |
|||
// } else { |
|||
// formatListEl.value.remove('format-list1') |
|||
// } |
|||
}) |
|||
} |
|||
setTimeout(() => { |
|||
shopScroll.value.refresh() |
|||
}, 500) |
|||
} |
|||
emits('clickFormat', item, index) |
|||
active.value = index |
|||
activeSon.value = -1 |
|||
} |
|||
//点击format |
|||
function clickItem( |
|||
index: number, |
|||
item: { |
|||
industryCode: string |
|||
industryName: string |
|||
shopNum: number |
|||
}, |
|||
$event: Event |
|||
) { |
|||
activeSon.value = index |
|||
console.dir('$event :>> ', $event) |
|||
const dom: any = $event.target |
|||
for (let i = 0; i < dom?.length; i++) { |
|||
dom[i]?.classList.remove('filter-active') |
|||
} |
|||
setTimeout(() => { |
|||
shopScroll.value.refresh() |
|||
}, 20) |
|||
emits('clickFormatItem', item) |
|||
} |
|||
|
|||
const backgroundList = [ |
|||
'linear-gradient(180deg, #3C74E6 0%, #7ACAF8 100%)', |
|||
'linear-gradient(180deg, #ED7D2B 0%, #F1C369 100%)', |
|||
'linear-gradient(180deg, #7959D2 0%, #93A3DC 100%)', |
|||
'linear-gradient(180deg, #3EA154 0%, #3AD79E 100%)', |
|||
'linear-gradient(180deg, #DB5C8A 0%, #F39292 100%)' |
|||
] |
|||
function getBackground(index: number) { |
|||
if (active.value === index) { |
|||
return '#e00068' |
|||
} |
|||
} |
|||
// const scrollTo = () => { |
|||
// shopScroll.value.scrollTo(0, 0) |
|||
// } |
|||
// const refresh = () => { |
|||
// shopScroll.value.refresh() |
|||
// } |
|||
// const initData = () => { |
|||
// active.value = 0 |
|||
// initItem() |
|||
// } |
|||
// const initItem = () => { |
|||
// activeSon.value = -1 |
|||
// } |
|||
|
|||
// onBeforeMount(() => {}) |
|||
// onMounted(() => {}) |
|||
</script> |
|||
|
|||
<style scoped lang="scss"> |
|||
.format__scroll { |
|||
overflow: hidden; |
|||
width: inherit; |
|||
height: inherit; |
|||
background-color: rgb(255 255 255 / 60%); |
|||
border-radius: 16px; |
|||
.filter-wrapper { |
|||
display: flex; |
|||
flex-direction: column; |
|||
// justify-content: flex-end; |
|||
// flex-wrap: wrap; |
|||
width: inherit; |
|||
height: fit-content; |
|||
} |
|||
.filter-div-f { |
|||
width: inherit; |
|||
height: 56px; |
|||
margin-bottom: 0; |
|||
&:nth-last-child(1) { |
|||
overflow: hidden; |
|||
border-radius: 0 0 16px 16px; |
|||
} |
|||
&:nth-child(1) { |
|||
overflow: hidden; |
|||
border-radius: 16px 16px 0 0; |
|||
} |
|||
.filter-div { |
|||
position: relative; |
|||
z-index: 2; |
|||
display: flex; |
|||
justify-content: space-between; |
|||
width: 100%; |
|||
height: 100%; |
|||
padding: 24px; |
|||
background: rgb(255 255 255 / 60%); |
|||
border: 0 solid rgb(255 255 255 / 60%); |
|||
border-radius: 16px; |
|||
flex-wrap: nowrap; |
|||
line-height: 22px; |
|||
|
|||
.left, |
|||
.right { |
|||
display: flex; |
|||
align-items: center; |
|||
pointer-events: none; |
|||
} |
|||
|
|||
.left { |
|||
margin-left: 0; |
|||
} |
|||
|
|||
.right { |
|||
position: relative; |
|||
// width: 52px; |
|||
padding-right: 0; |
|||
margin-left: -0; |
|||
} |
|||
|
|||
.format-img { |
|||
width: 16px; |
|||
height: 16px; |
|||
margin-right: 8px; |
|||
} |
|||
.format-name { |
|||
width: 111px; |
|||
height: 22px; |
|||
font-size: 14px; |
|||
font-family: 'font_bold'; |
|||
color: #534f46; |
|||
font-weight: 700; |
|||
line-height: 22px; |
|||
|
|||
@include no-wrap(); |
|||
} |
|||
|
|||
.format-number { |
|||
position: relative; |
|||
z-index: 1; |
|||
margin-right: 8px; |
|||
font-size: 14px; |
|||
font-family: 'font_bold'; |
|||
text-align: right; |
|||
color: #615c29; |
|||
font-style: normal; |
|||
line-height: 22px; |
|||
} |
|||
} |
|||
|
|||
.filter-active { |
|||
height: 56px; |
|||
background: #e00068; |
|||
p { |
|||
color: #fff !important; |
|||
} |
|||
|
|||
.format-img { |
|||
filter: brightness(0) invert(100%); |
|||
} |
|||
.right { |
|||
.saw-img { |
|||
filter: brightness(0) invert(100%); |
|||
} |
|||
} |
|||
} |
|||
|
|||
.filter-active-child { |
|||
background: #e000683d; |
|||
border-radius: 0 !important; |
|||
p { |
|||
color: #fff !important; |
|||
} |
|||
} |
|||
|
|||
.format-list1 { |
|||
height: 0 !important; |
|||
} |
|||
|
|||
.format-list { |
|||
z-index: 1; |
|||
overflow: hidden; |
|||
background: rgb(255 255 255 / 20%); |
|||
border-radius: 0; |
|||
transition: all 0.3s ease; |
|||
|
|||
.format-item { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
height: 56px; |
|||
padding-right: 48px; |
|||
padding-left: 48px; |
|||
margin-bottom: 0; |
|||
border-radius: 0; |
|||
flex-wrap: nowrap; |
|||
|
|||
.format-name { |
|||
z-index: 500; |
|||
width: 113px; |
|||
margin-left: 0; |
|||
font-size: 14px; |
|||
font-family: 'font_bold'; |
|||
color: rgb(0 0 0 / 60%); |
|||
line-height: 56px; |
|||
|
|||
@include no-wrap(); |
|||
} |
|||
|
|||
.format-number { |
|||
z-index: 500; |
|||
margin-right: 0; |
|||
font-size: 12px; |
|||
font-family: 'font_bold'; |
|||
color: rgb(0 0 0 / 60%); |
|||
line-height: 14px; |
|||
} |
|||
&.filter-active { |
|||
background: #e000683d; |
|||
p { |
|||
color: #fff !important; |
|||
} |
|||
|
|||
border-radius: 0; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
</style> |
|||
@ -0,0 +1,195 @@ |
|||
<template> |
|||
<div v-if="recList.length" class="rec-container"> |
|||
<!-- 推荐标题 --> |
|||
<div class="rec-title"> |
|||
<li>{{ switchLanguage({ name: '热门推荐' }, 'name') }}</li> |
|||
<li>POPULAR BRANDS</li> |
|||
</div> |
|||
<!-- 显示当前选择的信息 --> |
|||
<div class="recommand-group animate__animated animate__fadeInRight"> |
|||
<!-- |
|||
:freeMode="{ |
|||
freeMode: false, |
|||
momentumRatio: 0.3, |
|||
momentumVelocityRatio: 0.3 |
|||
}" |
|||
:observer="true" |
|||
:observe-parents="true" |
|||
--> |
|||
<swiper |
|||
v-if="showList" |
|||
direction="vertical" |
|||
:autoplay=" |
|||
recList.length > 3 |
|||
? { |
|||
disableOnInteraction: false, |
|||
delay: 1000 |
|||
} |
|||
: false |
|||
" |
|||
:free-mode="{ |
|||
enabled: true |
|||
}" |
|||
:enabled="recList.length > 3" |
|||
:loop="recList.length > 3" |
|||
:speed="1000" |
|||
:modules="modules" |
|||
:looped-slides="3" |
|||
:loop-additional-slides="3" |
|||
:width="267" |
|||
:height="150" |
|||
:slides-per-view="1" |
|||
:space-between="24" |
|||
> |
|||
<swiper-slide v-for="(item, index) in recList" :key="index"> |
|||
<div :key="item.shopCode" class="item" :shopInfo="item" @click="handleShop(item)"> |
|||
<img v-if="item?.doorMaterialList?.at(0)" :src="item?.doorMaterialList?.at(0)" class="rec-img" alt="" /> |
|||
<img v-else :src="require('@/assets/images/brand/no-rec.svg')" class="rec-img no-rec" alt="" /> |
|||
<p class="bottom-title"> |
|||
<!-- <img class="rec-logo" :src="item.logoUrl ? item.logoUrl : require('@/assets/images/empty_small.svg')" alt="" /> --> |
|||
<span class="rec-name"> {{ switchLanguage(item, 'shopName') }}</span> |
|||
<!-- <span class="rec-floor"> <img :src="item.industryUrl" alt="" />{{ item.floor }} </span> --> |
|||
</p> |
|||
</div> |
|||
</swiper-slide> |
|||
</swiper> |
|||
</div> |
|||
</div> |
|||
</template> |
|||
<script setup lang="ts"> |
|||
import { ref } from 'vue' |
|||
import { Autoplay, FreeMode } from 'swiper' |
|||
import { Swiper, SwiperSlide } from 'swiper/vue' |
|||
import 'swiper/css' |
|||
import 'swiper/css/free-mode' |
|||
import { getBrandInfo } from '@/http/api/brand/index' |
|||
import { useSwitchLanguage } from '@/composables/useSwitchLanguage' |
|||
import { useRootStore } from '@/store/root' |
|||
const store = useRootStore() |
|||
|
|||
const { switchLanguage } = useSwitchLanguage() |
|||
const recList = ref<Shop[]>([]) |
|||
const modules = [Autoplay, FreeMode] |
|||
const showList = ref(false) |
|||
|
|||
function handleShop(item: Shop) { |
|||
store.SET_SHOP(item) |
|||
store.SET_SHOW_DETAIL(true) |
|||
} |
|||
getBrandInfo() |
|||
.then(({ data }) => { |
|||
recList.value = data.recommendList |
|||
}) |
|||
.finally(() => { |
|||
showList.value = true |
|||
}) |
|||
</script> |
|||
|
|||
<style lang="scss" scoped> |
|||
:deep(.swiper-wrapper) { |
|||
transition-timing-function: linear !important; /* 之前是ease-out */ |
|||
} |
|||
.rec-container { |
|||
position: relative; |
|||
width: 268px; |
|||
height: 1080px; |
|||
padding-top: 172px; |
|||
padding-left: 48px; |
|||
margin-top: -152px; |
|||
margin-left: 28px; |
|||
background-image: linear-gradient(170deg, rgb(255 255 255 / 40%) 0%, rgb(255 255 255 / 8%) 80.56%, rgb(255 255 255 / 0%) 100%); |
|||
|
|||
--animate-duration: 0.6s; |
|||
--animate-delay: 1s; |
|||
.rec-title { |
|||
font-size: 24px; |
|||
font-family: 'font_bold'; |
|||
color: #534f46; |
|||
li:nth-child(2) { |
|||
margin-top: 5px; |
|||
font-size: 14px; |
|||
} |
|||
} |
|||
.recommand-group { |
|||
overflow: hidden; |
|||
width: 192px; |
|||
height: 696px; |
|||
margin-top: 24px; |
|||
:deep(.swiper-slide) { |
|||
width: 172px; |
|||
height: 146px; |
|||
} |
|||
.item { |
|||
width: 172px; |
|||
height: 146px; |
|||
padding: 8px auto 16px; |
|||
background-color: #fff; |
|||
border-radius: 16px; |
|||
.rec-img { |
|||
position: absolute; |
|||
top: 8px; |
|||
left: 38px; |
|||
overflow: hidden; |
|||
width: 96px; |
|||
height: 96px; |
|||
border-radius: 0; |
|||
object-fit: cover; |
|||
&.no-rec { |
|||
object-fit: scale-down; |
|||
} |
|||
} |
|||
.bottom-title { |
|||
position: absolute; |
|||
bottom: 16px; |
|||
left: 0; |
|||
display: flex; |
|||
justify-content: flex-start; |
|||
width: 100%; |
|||
height: 22px; |
|||
padding: 0; |
|||
background: rgb(0 0 0 / 0%); |
|||
border-radius: 0; |
|||
line-height: initial; |
|||
.rec-logo { |
|||
position: absolute; |
|||
top: -22px; |
|||
left: 12px; |
|||
width: 48px; |
|||
height: 48px; |
|||
padding: 0; |
|||
background: #fff; |
|||
border-radius: 4px; |
|||
object-fit: scale-down; |
|||
} |
|||
.rec-name { |
|||
width: 100%; |
|||
margin-top: 0; |
|||
margin-left: 0; |
|||
font-size: 14px; |
|||
font-family: 'font_bold'; |
|||
text-align: center; |
|||
color: #534f46; |
|||
|
|||
@include no-wrap(); |
|||
} |
|||
.rec-floor { |
|||
display: flex; |
|||
align-items: center; |
|||
height: 16px; |
|||
padding-right: 0; |
|||
margin-top: 5px; |
|||
margin-left: 0; |
|||
font-size: 14px; |
|||
color: rgb(255 255 255 / 80%); |
|||
img { |
|||
width: 16px; |
|||
height: 16px; |
|||
// filter: grayscale(100%) opacity(40%); |
|||
margin-right: 8px; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
</style> |
|||
@ -0,0 +1,533 @@ |
|||
<template> |
|||
<div class="shop-list"> |
|||
<!-- --> |
|||
<switchtab |
|||
class="switch-btn" |
|||
:width="364" |
|||
:height="68" |
|||
:wrapper-width="1092" |
|||
:wrapper-height="68" |
|||
direction="row" |
|||
:list="list" |
|||
@click="handleSwitch" |
|||
/> |
|||
<!-- 店铺 --> |
|||
<!-- <scroll-view |
|||
class="shop-scroll" |
|||
:style="{ |
|||
top: recList.length === 0 ? '360px' : '' |
|||
}" |
|||
:list="selectedShopList" |
|||
ref="shopScroll" |
|||
> --> |
|||
|
|||
<swiper |
|||
ref="myScroll" |
|||
class="shop-scroll animate__animated animate__fadeInUp" |
|||
:speed="300" |
|||
:direction="'vertical'" |
|||
:slides-per-view="'auto'" |
|||
:free-mode="{ |
|||
enabled: true, |
|||
momentumRatio: 0.3, |
|||
momentumVelocityRatio: 0.3 |
|||
}" |
|||
:observer="true" |
|||
:observe-parents="true" |
|||
:scrollbar="{ el: '.swiper-scrollbar' }" |
|||
:modules="modulesShop" |
|||
@after-init="getSwiper" |
|||
@reach-end="myScrollEnd" |
|||
> |
|||
<!-- |
|||
@observer-update="updateSwiper" --> |
|||
<swiper-slide v-for="(items, index) in pageList" :key="index" :data-floor-name="items.name"> |
|||
<ul class="shop-items"> |
|||
<div v-show="items.shopList && items.shopList.length" ref="shopDiv" class="shop-div" :data-floorname="items.name"> |
|||
<div class="shop-floor"> |
|||
{{ items.name }} |
|||
<div class="top-right-txt" :style="{ width: items.name !== device.floor ? '0px' : '' }"> |
|||
<img v-if="items.name === device.floor" src="../../assets/images/brand/pos.svg" class="pos-icon" alt="" /> |
|||
<span v-if="items.name === device.floor">{{ $t('brand.pos') }}</span> |
|||
</div> |
|||
</div> |
|||
<!-- :style="{ 'grid-template-columns': isH && recList.length === 0 ? 'repeat(5, 240px)' : null }" --> |
|||
<div class="shop-wrapper"> |
|||
<shopItem v-for="(item, idx) of items.shopList" :key="idx" class="item" :shop="item" @click="handleShop(item)" /> |
|||
</div> |
|||
</div> |
|||
</ul> |
|||
</swiper-slide> |
|||
<div class="swiper-scrollbar"></div> |
|||
</swiper> |
|||
|
|||
<!-- </scroll-view> --> |
|||
|
|||
<!-- 楼层和业态 --> |
|||
<div class="right-control-area animate__animated animate__fadeInRight"> |
|||
<transition |
|||
enter-active-class="animate__animated animate__fadeInRight" |
|||
leave-active-class="animate__animated animate__fadeOutRight" |
|||
mode="out-in" |
|||
> |
|||
<!-- 区域 --> |
|||
<div v-if="toggleFormatAndFloors === 0" class="floor-container"> |
|||
<ul class="floors-items"> |
|||
<li |
|||
v-for="(item, index) of regionList" |
|||
:key="item.regionCode" |
|||
class="floor-item" |
|||
:class="{ active: areaIdx === index }" |
|||
@click="handleArea(item, index)" |
|||
> |
|||
{{ switchLanguage(item, 'regionName') }} |
|||
</li> |
|||
</ul> |
|||
</div> |
|||
<!-- 业态 --> |
|||
<div v-else-if="toggleFormatAndFloors === 2" key="format" class="format-items"> |
|||
<filterFormatAndFloor |
|||
ref="collapseRef" |
|||
:list="shopFormatList" |
|||
@click-format-item="handleFormatChild" |
|||
@click-format="handleFormat" |
|||
/> |
|||
</div> |
|||
<!-- 楼层 scroll-view --> |
|||
<div v-else-if="toggleFormatAndFloors === 1" key="floors" class="floor-container" :refreshDelay="200"> |
|||
<ul class="floors-items"> |
|||
<li |
|||
v-for="(item, index) of floorsList" |
|||
:key="item.floorCode" |
|||
class="floor-item" |
|||
:class="{ active: floorIdx === index }" |
|||
@click="handleFloor(item, index)" |
|||
> |
|||
{{ item.floor }} |
|||
<img v-if="device.floor === item.floor" class="current-pos" src="@/assets/images/brand/currentPos.svg" alt="" /> |
|||
</li> |
|||
</ul> |
|||
</div> |
|||
</transition> |
|||
</div> |
|||
</div> |
|||
</template> |
|||
<script setup lang="ts"> |
|||
import { ref, computed, nextTick, shallowRef, onMounted } from 'vue' |
|||
import { FreeMode } from 'swiper' |
|||
import { Swiper, SwiperSlide } from 'swiper/vue' |
|||
import 'swiper/css' |
|||
import 'swiper/css/pagination' |
|||
import 'swiper/css/free-mode' |
|||
// import ScrollView from '@/base/ScrollView/ScrollView.vue' |
|||
// import CollapseVue from '@/base/Collapse/Collapse.vue' |
|||
|
|||
import { getBrandInfo } from '@/http/api/brand/index' |
|||
import { getRegionList } from '@/http/api/base/index' |
|||
|
|||
import switchtab from '@/components/SwitchTab/SwitchTab.vue' |
|||
import filterFormatAndFloor from '@/views/Brand/filterFormatAndFloor.vue' |
|||
import shopItem from '@/components/ShopItem/ShopItem.vue' |
|||
|
|||
import { useStatistics } from '@/composables/useStatistics' |
|||
|
|||
import { usePage } from '@/composables/usePage' |
|||
import { storeToRefs } from 'pinia' |
|||
import { useRootStore } from '@/store/root' |
|||
import { GroupList } from '@/http/api/brand/types' |
|||
import { useSwitchLanguage } from '@/composables/useSwitchLanguage' |
|||
|
|||
const store = useRootStore() |
|||
const { device, currentBuildingFloorsList, shopListGroupByFloor, shopListGroupByIndustry } = storeToRefs(store) |
|||
|
|||
const { switchLanguage } = useSwitchLanguage() |
|||
// const shopScroll = computed(() => document.querySelector('.shop-scroll>.swiper-wrapper')) |
|||
const swiperRef = shallowRef() |
|||
|
|||
const modulesShop = [FreeMode] |
|||
|
|||
const toggleFormatAndFloors = ref<0 | 1 | 2>(0) |
|||
|
|||
const list = [ |
|||
{ |
|||
name: '区域筛选', |
|||
en: 'AREA', |
|||
icon: require('../../assets/images/brand/icon_area.svg'), |
|||
iconSel: require('../../assets/images/brand/icon_area_sel.svg') |
|||
}, |
|||
{ |
|||
name: '楼层筛选', |
|||
en: 'FLOOR', |
|||
icon: require('../../assets/images/brand/icon_floor.svg'), |
|||
iconSel: require('../../assets/images/brand/icon_floor_sel.svg') |
|||
}, |
|||
{ |
|||
name: '业态筛选', |
|||
en: 'FORMAT', |
|||
icon: require('../../assets/images/brand/icon_format.svg'), |
|||
iconSel: require('../../assets/images/brand/icon_format_sel.svg') |
|||
} |
|||
] |
|||
|
|||
const regionList = ref<Region[]>([]) //区域列表 |
|||
const shopFormatList = ref<Industry[]>([]) //业态列表 |
|||
// const brandFloorsList = ref([]) //楼层列表 |
|||
const selectedShopList = shallowRef<GroupList>([]) //店铺列表 |
|||
const selfListByFloor = ref<GroupList>([]) |
|||
const selfListByIndustry = ref<GroupList>([]) |
|||
|
|||
const floorIdx = ref(0) //楼层索引 |
|||
const formatIdx = ref(0) //业态索引 |
|||
const areaIdx = ref(0) //区域索引 |
|||
const myScroll = ref() |
|||
// type PageList = { name: string; shopList: Shop[] } |
|||
const { scrollEnd, pageList } = usePage(selectedShopList, myScroll) |
|||
|
|||
//楼层列表 |
|||
const floorsList = computed(() => { |
|||
return [ |
|||
{ |
|||
floor: 'ALL', |
|||
floorCode: 0, |
|||
floorMapUrl: '', |
|||
floorOrder: -1, |
|||
floorMapCode: '' |
|||
}, |
|||
...currentBuildingFloorsList.value |
|||
] |
|||
}) |
|||
const recList = ref<Shop[]>([]) |
|||
|
|||
getBrandInfo().then(({ data }) => { |
|||
shopFormatList.value = [ |
|||
// { |
|||
// fileUrl: require('@/assets/images/brand/all.svg'), |
|||
// isSpecial: 0, |
|||
// industryCode: 0, |
|||
// industryName: '全部品牌', |
|||
// industryNameEn: '', |
|||
// shopNum: 0, |
|||
// industryList: [] |
|||
// }, |
|||
...data.industryFatherList |
|||
] |
|||
recList.value = data.recommendList |
|||
}) |
|||
getRegionList().then(({ data }) => { |
|||
if (data) { |
|||
regionList.value = [ |
|||
{ |
|||
regionCode: '-1', |
|||
regionName: '全部区域', |
|||
regionNameEn: 'ALL' |
|||
}, |
|||
...data |
|||
] |
|||
} |
|||
}) |
|||
selfListByFloor.value = [...shopListGroupByFloor.value] |
|||
selfListByIndustry.value = [...shopListGroupByIndustry.value] |
|||
|
|||
onMounted(() => { |
|||
selectedShopList.value = selfListByIndustry.value |
|||
}) |
|||
function getSwiper(obj: any) { |
|||
swiperRef.value = obj |
|||
swiperRef.value.update() |
|||
} |
|||
|
|||
function updateSwiper(time = 300, isToCurentFloor = false) { |
|||
nextTick(() => { |
|||
const timeId = setTimeout(() => { |
|||
clearTimeout(timeId) |
|||
// showList.value = true |
|||
if (swiperRef.value) { |
|||
// console.dir('swiperRef.value :>> ', swiperRef) |
|||
swiperRef.value.update() |
|||
if (isToCurentFloor && swiperRef.value) { |
|||
for (let t = 0; t < swiperRef.value.slides.length; t++) { |
|||
const element = swiperRef.value.slides[t] |
|||
if (element.dataset.floorName === device.value.floor) { |
|||
swiperRef.value.slideTo(t, 10) |
|||
} |
|||
} |
|||
} else { |
|||
swiperRef.value.slideTo(0, 30) |
|||
} |
|||
// shopScroll.value.style.transform = 'translate3d(0px, 0px, 0px)' |
|||
|
|||
// swiperRef.slideTo(0) |
|||
// swiperRef.value.children[0].style.transform = 'translate3d(0px, 0px, 0px)' |
|||
} |
|||
}, time) |
|||
}) |
|||
} |
|||
|
|||
//切换控制 |
|||
function handleSwitch(index: number) { |
|||
formatIdx.value = 0 |
|||
floorIdx.value = 0 |
|||
areaIdx.value = 0 |
|||
// console.log('swiperRef.value.swiper :>> ', swiperRef) |
|||
if (index === 0) { |
|||
toggleFormatAndFloors.value = 0 |
|||
selectedShopList.value = selfListByIndustry.value |
|||
updateSwiper() |
|||
} else if (index === 1) { |
|||
toggleFormatAndFloors.value = 1 |
|||
selectedShopList.value = selfListByIndustry.value |
|||
updateSwiper() |
|||
} else { |
|||
toggleFormatAndFloors.value = 2 |
|||
selectedShopList.value = selfListByFloor.value |
|||
updateSwiper() |
|||
} |
|||
} |
|||
//点击楼层 |
|||
function handleFloor(item: Floor, index: number) { |
|||
if (index === floorIdx.value) { |
|||
return |
|||
} |
|||
floorIdx.value = index |
|||
if (index === 0) { |
|||
selectedShopList.value = selfListByIndustry.value |
|||
updateSwiper() |
|||
|
|||
return |
|||
} |
|||
selectedShopList.value = selfListByIndustry.value.map(format => ({ |
|||
...format, |
|||
shopList: format.shopList.filter(shop => shop.floor === item.floor) |
|||
})) |
|||
updateSwiper() |
|||
} |
|||
//点击区域 |
|||
function handleArea(item: Region, index: number) { |
|||
if (index === areaIdx.value) { |
|||
return |
|||
} |
|||
areaIdx.value = index |
|||
if (index === 0) { |
|||
selectedShopList.value = selfListByIndustry.value |
|||
updateSwiper() |
|||
|
|||
return |
|||
} |
|||
selectedShopList.value = selfListByIndustry.value.map(format => ({ |
|||
...format, |
|||
shopList: format.shopList.filter(shop => shop.regionCode === item.regionCode) |
|||
})) |
|||
updateSwiper() |
|||
} |
|||
//业态 |
|||
function handleFormat(item: Industry, index: number) { |
|||
if (index === formatIdx.value) { |
|||
return |
|||
} |
|||
useStatistics({ tag: 'industry', industryCode: item.industryCode }) |
|||
|
|||
formatIdx.value = index |
|||
if (item.industryName === '全部品牌') { |
|||
selectedShopList.value = selfListByFloor.value |
|||
updateSwiper() |
|||
|
|||
return |
|||
} |
|||
selectedShopList.value = selfListByFloor.value.map(floor => ({ |
|||
...floor, |
|||
shopList: floor.shopList.filter(shop => shop.industryFatherName === item.industryName) |
|||
})) |
|||
updateSwiper() |
|||
} |
|||
function handleFormatChild(item: any) { |
|||
selectedShopList.value = selfListByFloor.value.map(floor => ({ |
|||
...floor, |
|||
shopList: floor.shopList.filter(shop => shop.industryCode === item.industryCode) |
|||
})) |
|||
updateSwiper() |
|||
} |
|||
//点击店铺 |
|||
function handleShop(item: Shop) { |
|||
store.SET_SHOP(item) |
|||
store.SET_SHOW_DETAIL(true) |
|||
} |
|||
function myScrollEnd() { |
|||
scrollEnd() |
|||
} |
|||
</script> |
|||
<style lang="scss" scoped> |
|||
:deep(.switch-tabs) { |
|||
padding: 0; |
|||
} |
|||
.switch-btn { |
|||
position: absolute; |
|||
top: 0; |
|||
left: 0; |
|||
} |
|||
.shop-list { |
|||
position: relative; |
|||
display: flex; |
|||
overflow: hidden; |
|||
width: 1598px; |
|||
height: 1080px; |
|||
padding-top: 0; |
|||
padding-left: 0; |
|||
.right-control-area { |
|||
position: relative; |
|||
width: 230px; |
|||
height: 622px; |
|||
margin-top: 144px; |
|||
margin-left: 28px; |
|||
|
|||
.format-items { |
|||
position: relative; |
|||
width: 230px; |
|||
height: 645px; |
|||
margin-top: 0; |
|||
margin-right: 0; |
|||
transition: all 0.3s linear; |
|||
} |
|||
.floor-container { |
|||
overflow: hidden; |
|||
width: 230px; |
|||
margin-top: 0; |
|||
margin-right: 0; |
|||
border-radius: 0; |
|||
// position: absolute; |
|||
// right: 0; |
|||
// top: 80px; |
|||
.floors-items { |
|||
// display: grid; |
|||
// grid-template-columns: repeat(6, 108px); |
|||
// column-gap: 8px; |
|||
// row-gap: 8px; |
|||
// place-items: start end; |
|||
// justify-items: end; |
|||
|
|||
// justify-content: flex-end; |
|||
display: flex; |
|||
overflow: hidden; |
|||
padding-bottom: 0; |
|||
margin-top: 0; |
|||
border-radius: 0; |
|||
// flex-direction: row; |
|||
// flex-wrap: wrap; |
|||
flex-flow: row wrap; |
|||
gap: 8px 6px; |
|||
// justify-content: flex-end; |
|||
// flex-wrap: wrap; |
|||
|
|||
animation-duration: 0.3s; |
|||
border-bottom: 0 solid rgb(0 0 0 / 0%); |
|||
.floor-item { |
|||
position: relative; |
|||
overflow: hidden; |
|||
width: 230px; |
|||
height: 72px; |
|||
font-size: 14px; |
|||
font-family: 'font_bold'; |
|||
text-align: center; |
|||
color: #534f46; |
|||
background: rgb(255 255 255 / 50%); |
|||
border: 2px solid #fff; |
|||
border-radius: 16px; |
|||
line-height: 72px; |
|||
backdrop-filter: blur(11px); |
|||
.current-pos { |
|||
position: absolute; |
|||
top: 28px; |
|||
right: 64px; |
|||
width: 16px; |
|||
} |
|||
&:first-of-type { |
|||
width: 230px; |
|||
// margin-bottom: 8px; |
|||
} |
|||
|
|||
// &:last-of-type { |
|||
// border-bottom-left-radius: 8px; |
|||
// border-bottom-right-radius: 8px; |
|||
// } |
|||
&.active { |
|||
&::after { |
|||
position: absolute; |
|||
top: 52px; |
|||
left: 0; |
|||
width: 224px; |
|||
height: 0; |
|||
content: ''; |
|||
// border-bottom: 4px rgba(255, 255, 255, 0) solid; |
|||
// border-image: linear-gradient(to right, #ea4b75, #ee8ea0) 1 10 round; |
|||
} |
|||
.current-pos { |
|||
filter: brightness(0) invert(100%); |
|||
} |
|||
|
|||
color: #fff; |
|||
background: #e00068; |
|||
// &:first-of-type { |
|||
// // border-top-left-radius: 8px; |
|||
// // border-top-right-radius: 8px; |
|||
// } |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
.shop-scroll { |
|||
width: 1308px; |
|||
height: 770px; |
|||
padding-bottom: 30px; |
|||
margin-top: 100px; |
|||
margin-left: 0; |
|||
.swiper-slide, |
|||
.swiper-wrapper { |
|||
height: auto; |
|||
} |
|||
.shop-items { |
|||
.shop-div { |
|||
margin-bottom: 40px; |
|||
.shop-floor { |
|||
display: flex; |
|||
align-items: center; |
|||
// vertical-align: text-bottom; |
|||
// justify-content: space-between; |
|||
padding-right: 0; |
|||
margin-bottom: 8px; |
|||
margin-left: 0; |
|||
font-size: 28px; |
|||
font-family: 'font_bold'; |
|||
color: #534f46; |
|||
font-style: normal; |
|||
font-weight: 700; |
|||
.top-right-txt { |
|||
display: flex; |
|||
align-items: center; |
|||
width: fit-content; |
|||
height: 14px; |
|||
padding: 0; |
|||
margin-right: 0; |
|||
margin-bottom: 3px; |
|||
font-size: 12px; |
|||
color: #615c59; |
|||
background: rgb(255 255 255 / 0%); |
|||
border-radius: 0; |
|||
|
|||
@include no-wrap(); |
|||
.pos-icon { |
|||
height: 14px; |
|||
margin-right: 6px; |
|||
margin-left: 24px; |
|||
} |
|||
} |
|||
} |
|||
.shop-wrapper { |
|||
display: grid; |
|||
grid-template-columns: repeat(6, 212px); |
|||
gap: 16px 8px; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
</style> |
|||