Browse Source
2、点击导航后,竖版品牌列表下沉,上方出现该品牌的地图导航。横版品牌列表右拉,左侧出现该品牌的地图导航。点击收起,列表重新铺满页面。 3、如果再次点击该品牌的立即导航,导航动画重新播放即可; 4、在有地图的情况下,列表点击其他品牌,地图直接导航到该品牌(以免出现地图和品牌不对应的情况); 5、在有地图的情况下,地图点击其他店铺box无法导航,因为品牌列表不一定有对应(该功能展示的是推荐店铺) 6、被选中的品牌展示在品牌列表上方;ShangHai_LongXiang
18 changed files with 420 additions and 165 deletions
@ -0,0 +1,89 @@ |
|||
<script setup> |
|||
import { list as buttonList } from './list' |
|||
defineProps(['mapIdx']) |
|||
const emit = defineEmits(['handleMapIcon']) |
|||
</script> |
|||
<template> |
|||
<Teleport to="body"> |
|||
<div class="control-area"> |
|||
<div class="map-control-wrapper animate__fast animate__animated animate__fadeInUp"> |
|||
<div class="map-item" @click="emit('handleMapIcon', item, index)" v-for="(item, index) of buttonList" :key="item.name"> |
|||
<img :src="mapIdx === index ? item.iconActive : item.icon" alt="" class="map-icon" /> |
|||
<span class="map-name">{{ switchLanguage(item, 'name') }}</span> |
|||
</div> |
|||
</div> |
|||
<img src="../../assets/images/map/hands.svg" alt="" class="hands" /> |
|||
</div> |
|||
</Teleport> |
|||
</template> |
|||
<style lang="scss" scoped> |
|||
.control-area { |
|||
position: absolute; |
|||
display: flex; |
|||
align-items: flex-start; |
|||
justify-content: space-between; |
|||
height: 104px; |
|||
left: 68px; |
|||
top: 952px; |
|||
z-index: 51; |
|||
|
|||
.hands { |
|||
position: fixed; |
|||
top: 408px; |
|||
left: 68px; |
|||
width: 246px; |
|||
height: 80px; |
|||
z-index: 51; |
|||
} |
|||
} |
|||
.map-control-wrapper { |
|||
display: flex; |
|||
} |
|||
.map-item { |
|||
display: flex; |
|||
flex-direction: column; |
|||
align-items: center; |
|||
justify-content: center; |
|||
margin-left: 12px; |
|||
} |
|||
.map-icon { |
|||
margin-bottom: 8px; |
|||
width: 52px; |
|||
} |
|||
.map-name { |
|||
font-weight: 700; |
|||
font-size: 12px; |
|||
line-height: 14px; |
|||
text-align: center; |
|||
color: rgba(0, 0, 0, 0.6); |
|||
} |
|||
@media (min-aspect-ratio: 1/1) { |
|||
.shop-list-wrapper { |
|||
width: 510px; |
|||
height: calc(100vh - 280px); |
|||
right: 0px; |
|||
bottom: 0; |
|||
left: auto; |
|||
top: auto; |
|||
.switch { |
|||
top: 24px; |
|||
} |
|||
.shop-scroll { |
|||
height: 100%; |
|||
margin-left: 54px; |
|||
} |
|||
} |
|||
.control-area { |
|||
left: 68px; |
|||
bottom: 40px; |
|||
right: auto; |
|||
top: auto; |
|||
height: auto; |
|||
.hands { |
|||
top: 280px; |
|||
left: 68px; |
|||
right: auto; |
|||
} |
|||
} |
|||
} |
|||
</style> |
|||
@ -1,38 +1,205 @@ |
|||
<template> |
|||
<View> |
|||
<BrandScroll class="brand" :shop="shop" @click="handleShop" :list="selectedList" :config="config" /> |
|||
<MapControl @handleMapIcon="handleMapIcon" :mapIdx="mapIdx" v-if="showMap"></MapControl> |
|||
<Teleport to="body"> |
|||
<Transition appear enter-active-class="animate__animated animate__fadeIn" leave-active-class="animate__animated animate__fadeOut"> |
|||
<div class="hide-map" v-if="showMap" @click="showMap = false"> |
|||
<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg"> |
|||
<g> |
|||
<path id="Vector" d="M24 22.1L33.9 32L36.728 29.172L24 16.444L11.272 29.172L14.1 32L24 22.1Z" fill="currentColor" /> |
|||
</g> |
|||
</svg> |
|||
<div v-if="isH"> |
|||
<div>点击</div> |
|||
<div>收起地图</div> |
|||
</div> |
|||
<div v-else>点击收起地图</div> |
|||
</div></Transition |
|||
> |
|||
<div class="brand-wrapper" :class="showMap ? 'showMap' : ''"> |
|||
<BrandScroll class="brand" :class="showMap ? 'showMap' : ''" :shop="shop" @click="handleShop" :list="selectedList" :isFood="isFood" :config="config" /> |
|||
</div> |
|||
</Teleport> |
|||
</View> |
|||
</template> |
|||
|
|||
<script setup> |
|||
import { ref } from 'vue' |
|||
import { ref, computed, watch, onBeforeMount, onBeforeUnmount } from 'vue' |
|||
import { storeToRefs } from 'pinia' |
|||
import { useStore } from '@/store/root' |
|||
import { getBrandListByFloor } from '@/http/brand/api' |
|||
import View from '@/layouts/View.vue' |
|||
import BrandScroll from '@/components/BrandScroll/BrandScroll.vue' |
|||
import { hideMapDialog, setShopActive, setShopInactive } from '@/composables/useInitMap' |
|||
import MapControl from '@/components/MapControl/MapControl.vue' |
|||
import { RESET, DIRECTION } from '@/components/MapControl/list' |
|||
import { useMediaQuery } from '@vueuse/core' |
|||
|
|||
const store = useStore() |
|||
const storeRefs = storeToRefs(store) |
|||
const { config, shopList, indexList } = storeRefs |
|||
const shop = ref(null) |
|||
const { config, indexList, foodIndustryMap, shop, currentFloor, showMap, showSearch, path } = storeRefs |
|||
const mapIdx = ref(-1) |
|||
const mapTimer = ref(null) |
|||
const mapIconTimer = ref(null) |
|||
const selectedList = ref([]) |
|||
const isFood = computed(() => path.value === '/foods') |
|||
const isH = useMediaQuery('(min-aspect-ratio: 1/1)') |
|||
|
|||
Promise.all([getBrandListByFloor()]).then(([_brandList]) => { |
|||
if (storeRefs.shop.value) shop.value = storeRefs.shop.value |
|||
const recMap = indexList.value.recommendList.reduce((acc, { shopCode }) => ({ ...acc, [shopCode]: true }), {}) |
|||
const list = _brandList.data.list |
|||
selectedList.value = list.map(({ name, shopList: _shopList }) => ({ name, shopList: _shopList.filter(({ shopCode }) => recMap[shopCode]) })) |
|||
if (isFood.value) { |
|||
const list = _brandList.data.list.map(item => ({ |
|||
name: item.name, |
|||
shopList: item.shopList.filter(({ industryFatherCode }) => foodIndustryMap.value[industryFatherCode]) |
|||
})) |
|||
selectedList.value = list |
|||
} else { |
|||
const recMap = indexList.value.recommendList.reduce((acc, { shopCode }) => ({ ...acc, [shopCode]: true }), {}) |
|||
const list = _brandList.data.list |
|||
selectedList.value = list.map(({ name, shopList: _shopList }) => ({ name, shopList: _shopList.filter(({ shopCode }) => recMap[shopCode]) })) |
|||
} |
|||
}) |
|||
onBeforeMount(() => { |
|||
store.SET_SHOW_MAP(false) |
|||
}) |
|||
|
|||
onBeforeUnmount(() => { |
|||
if (!showSearch.value) store.SET_SHOP(null) |
|||
}) |
|||
function handleShop(item) { |
|||
shop.value = shopList.value.find(_shop => _shop.shopCode === item.shopCode) |
|||
if (shop.value?.shopCode === item.shopCode) { |
|||
if (showMap.value) { |
|||
window.Map_QM.pathNode({ floor: shop.value.floorOrder, node: shop.value.yaxis }) |
|||
} else store.SET_SHOW_MAP(true) |
|||
} |
|||
mapIdx.value = -1 |
|||
store.SET_SHOP(item) |
|||
} |
|||
|
|||
//我的位置 |
|||
function onClickDeviceSite() { |
|||
hideMapDialog() |
|||
window.Map_QM.onShowDeviceSite() |
|||
} |
|||
function handleMapIcon(item, index) { |
|||
store.SET_SHOP(null) |
|||
clearTimeout(mapIconTimer.value) |
|||
window.Map_QM.changeStateShopPro(false) |
|||
mapIdx.value = index |
|||
switch (item.name) { |
|||
case RESET: |
|||
onClickDeviceSite() |
|||
window.Map_QM.showFloor(currentFloor.value.floorOrder) |
|||
break |
|||
case DIRECTION: |
|||
window.Map_QM.onShowMeDir() |
|||
break |
|||
default: |
|||
break |
|||
} |
|||
clearTimeout(mapTimer.value) |
|||
if (item.name === RESET) { |
|||
mapTimer.value = setTimeout(() => { |
|||
mapIdx.value = -1 |
|||
mapTimer.value = -1 |
|||
clearTimeout(mapTimer.value) |
|||
}, 300) |
|||
} |
|||
} |
|||
|
|||
watch(showMap, v => { |
|||
if (v) { |
|||
window?.Map_QM?.startRender() |
|||
if (shop.value) { |
|||
store.SET_SHOW_MAP(true) |
|||
window.Map_QM.pathNode({ floor: shop.value.floorOrder, node: shop.value.yaxis }) |
|||
} |
|||
} else { |
|||
window?.Map_QM?.cancelRender() |
|||
} |
|||
}) |
|||
|
|||
watch(shop, nxt => { |
|||
if (!nxt) { |
|||
setShopInactive() |
|||
} else if (nxt && showMap.value) { |
|||
setShopActive(nxt) |
|||
window.Map_QM.pathNode({ floor: nxt.floorOrder, node: nxt.yaxis }) |
|||
} |
|||
}) |
|||
</script> |
|||
|
|||
<style lang="scss" scoped> |
|||
.brand { |
|||
position: relative; |
|||
flex: 1; |
|||
.hide-map { |
|||
position: absolute; |
|||
display: flex; |
|||
flex-direction: column; |
|||
align-items: center; |
|||
padding-top: 4px; |
|||
height: 100px; |
|||
left: 0; |
|||
right: 0; |
|||
background: var(--guide-floorBg); |
|||
color: var(--guide-floorColor); |
|||
top: 1050px; |
|||
font-size: 24px; |
|||
font-weight: 700; |
|||
line-height: normal; |
|||
svg { |
|||
width: 48px; |
|||
height: 48px; |
|||
} |
|||
} |
|||
.brand-wrapper { |
|||
position: absolute; |
|||
top: 408px; |
|||
height: 1512px; |
|||
z-index: 20; |
|||
background: var(--global-background); |
|||
transition: all 0.5s ease-in-out; |
|||
&.showMap { |
|||
height: 770px; |
|||
top: 1150px; |
|||
.brand { |
|||
height: 770px; |
|||
} |
|||
} |
|||
.brand { |
|||
height: 1512px; |
|||
} |
|||
} |
|||
@media (min-aspect-ratio: 1/1) { |
|||
.hide-map { |
|||
left: 1310px; |
|||
right: 510px; |
|||
height: 800px; |
|||
top: 280px; |
|||
align-items: center; |
|||
text-align: center; |
|||
justify-content: center; |
|||
font-size: 20px; |
|||
svg { |
|||
transform: rotate(-90deg); |
|||
margin-bottom: 27px; |
|||
} |
|||
} |
|||
.brand-wrapper { |
|||
top: 280px; |
|||
width: 100vw; |
|||
right: 0; |
|||
height: 800px; |
|||
.brand { |
|||
height: 800px; |
|||
} |
|||
&.showMap { |
|||
width: 510px; |
|||
top: 280px; |
|||
height: 800px; |
|||
.brand { |
|||
width: 456px; |
|||
height: 800px; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
</style> |
|||
|
|||
@ -1,40 +0,0 @@ |
|||
<template> |
|||
<View> |
|||
<BrandScroll :isFood="true" class="foods" :shop="shop" @click="handleShop" :list="selectedList" :config="config" /> |
|||
</View> |
|||
</template> |
|||
|
|||
<script setup> |
|||
import { ref } from 'vue' |
|||
import { storeToRefs } from 'pinia' |
|||
import { useStore } from '@/store/root' |
|||
import { getBrandListByFloor } from '@/http/brand/api' |
|||
import View from '@/layouts/View.vue' |
|||
import BrandScroll from '@/components/BrandScroll/BrandScroll.vue' |
|||
|
|||
const store = useStore() |
|||
const storeRefs = storeToRefs(store) |
|||
const { config, shopList, foodIndustryMap } = storeRefs |
|||
const shop = ref(null) |
|||
const selectedList = ref([]) |
|||
|
|||
Promise.all([getBrandListByFloor()]).then(([_brandList]) => { |
|||
if (storeRefs.shop.value) shop.value = storeRefs.shop.value |
|||
const list = _brandList.data.list.map(item => ({ |
|||
name: item.name, |
|||
shopList: item.shopList.filter(({ industryFatherCode }) => foodIndustryMap.value[industryFatherCode]) |
|||
})) |
|||
selectedList.value = list |
|||
}) |
|||
|
|||
function handleShop(item) { |
|||
shop.value = shopList.value.find(_shop => _shop.shopCode === item.shopCode) |
|||
} |
|||
</script> |
|||
|
|||
<style lang="scss" scoped> |
|||
.foods { |
|||
position: relative; |
|||
flex: 1; |
|||
} |
|||
</style> |
|||
Loading…
Reference in new issue