Browse Source

feat: 1、推荐功能品牌列表,点击logo后,logo翻转展示二维码和“立即导航”按钮;

2、点击导航后,竖版品牌列表下沉,上方出现该品牌的地图导航。横版品牌列表右拉,左侧出现该品牌的地图导航。点击收起,列表重新铺满页面。
3、如果再次点击该品牌的立即导航,导航动画重新播放即可;
4、在有地图的情况下,列表点击其他品牌,地图直接导航到该品牌(以免出现地图和品牌不对应的情况);
5、在有地图的情况下,地图点击其他店铺box无法导航,因为品牌列表不一定有对应(该功能展示的是推荐店铺)
6、被选中的品牌展示在品牌列表上方;
ShangHai_LongXiang
jiannibang 2 years ago
parent
commit
5e47a52174
  1. 8
      src/App.vue
  2. 63
      src/base/ShopItem/ShopItem.vue
  3. 56
      src/components/BrandScroll/BrandScroll.vue
  4. 14
      src/components/Map/Map.vue
  5. 89
      src/components/MapControl/MapControl.vue
  6. 0
      src/components/MapControl/list.js
  7. 15
      src/components/PublicComponent/PublicComponent.vue
  8. 4
      src/components/PublicComponent/SearchBar.vue
  9. 6
      src/components/PublicComponent/Tabs.vue
  10. 4
      src/components/Search/Search.vue
  11. 10
      src/composables/useHandleScreen.js
  12. 1
      src/composables/useInitMap.js
  13. 6
      src/router/routes.js
  14. 6
      src/store/root/actions.js
  15. 4
      src/store/root/state.js
  16. 183
      src/views/Brand/Brand.vue
  17. 40
      src/views/Foods/Foods.vue
  18. 70
      src/views/Guide/Guide.vue

8
src/App.vue

@ -8,6 +8,14 @@
<script setup>
import PublicComponent from '@/components/PublicComponent/PublicComponent.vue'
import { useRoute } from 'vue-router'
import { watch } from 'vue'
import { useStore } from './store/root'
const route = useRoute()
const store = useStore()
watch(route, to => {
store.SET_PATH(to.fullPath)
})
</script>
<style>
body,

63
src/base/ShopItem/ShopItem.vue

@ -1,5 +1,5 @@
<template>
<div :id="shop.houseNumber" class="group-item" :class="{ isRow, isFood, isGuide, isActive }" @click="handleShop">
<div :id="shop.houseNumber" class="group-item" :class="{ isRow, isFood, isGuide, isActive, isBrandShowMap }" @click="handleShop">
<div class="logo-wrapper" :class="{ hasFood: isFood && shop.foodMaterialList && shop.foodMaterialList.length }">
<img
loading="lazy"
@ -10,22 +10,23 @@
</div>
<p class="name">
<span class="shop-name">{{ switchLanguage(shop, 'shopName') }}</span>
<span class="name-right" v-if="$route.path === '/guide'"> {{ shop.distance ? shop.distance + '' : '' }}<img v-if="shop.dir" class="dir" :src="shop.dir" /> </span>
<span class="name-right" v-if="path === '/guide'"> {{ shop.distance ? shop.distance + '' : '' }}<img v-if="shop.dir" class="dir" :src="shop.dir" /> </span>
<span class="name-right" v-else>{{ shop.floor }}</span>
</p>
<Transition appear enter-active-class="animate__animated animate__fadeIn" leave-active-class="animate__animated animate__fadeOut">
<div v-if="isActive && !isRow" class="qrcode-wrapper">
<div v-if="isActive && !isRow" class="qrcode-wrapper animate__animated animate__fadeIn">
<div class="qrcode">
<QRCodeFromText
class="qrcode"
:size="120"
:text="`${config.mobileNav}?code=${currentFloor.projectCode}&s=${currentFloor.floorOrder}_${currentFloor.location}_屏幕的位置&e=${shop.houseNumber}`"
></QRCodeFromText>
<p class="name">
<span class="shop-name">{{ switchLanguage(shop, 'shopName') }}</span>
<span class="qrcode-tag">扫码导航</span>
</p>
<div class="qrcode-meta1">手机扫码导航</div>
</div>
</Transition>
<div v-if="isFood" class="foodActiveName">立即导航</div>
<div class="name" v-else>
<div class="shop-name">立即导航</div>
</div>
</div>
<Teleport to="body">
<Transition appear enter-active-class="animate__animated animate__fadeIn" leave-active-class="animate__animated animate__fadeOut">
<div v-if="isActive && isRow" class="qrcode-modal" @click="deactivate">
@ -54,9 +55,10 @@
import QRCodeFromText from '@/components/QRCodeFromText/QRCodeFromText.vue'
import { storeToRefs } from 'pinia'
import { useStore } from '@/store/root'
import { computed } from 'vue'
const store = useStore()
const { currentFloor, config, theme } = storeToRefs(store)
const { currentFloor, config, theme, showMap, path } = storeToRefs(store)
const isBrandShowMap = computed(() => showMap.value && (path.value === '/brand' || path.value === '/foods'))
const props = defineProps({
shop: Object,
@ -202,6 +204,19 @@ const deactivate = () => store.SET_SHOP(null)
margin-top: 12px;
text-align: center;
}
.foodActiveName {
display: flex;
justify-content: center;
align-items: center;
width: calc(100% - 32px);
height: 45px;
border-radius: 100px;
background: var(--VI--, linear-gradient(99deg, #f0b92b 0%, #f9d556 100%));
color: var(--b-80, rgba(0, 0, 0, 0.8));
text-align: center;
font-size: 14px;
font-weight: 700;
}
.name-right {
display: block;
text-align: center;
@ -230,9 +245,18 @@ const deactivate = () => store.SET_SHOP(null)
background: #ffffff;
border-radius: var(--global-radius, 8px);
display: flex;
justify-content: center;
flex-direction: column;
padding-top: 12px;
align-items: center;
}
.qrcode-meta1 {
color: var(--b-80, rgba(0, 0, 0, 0.8));
font-size: 14px;
font-style: normal;
font-weight: 700;
line-height: normal;
margin-top: 6px;
}
.qrcode-meta {
font-weight: 700;
font-size: 14px;
@ -240,6 +264,9 @@ const deactivate = () => store.SET_SHOP(null)
text-align: center;
line-height: 40px;
}
.shop-name {
text-align: center;
}
&.isFood {
.qrcode-wrapper {
background: #ffffff;
@ -249,6 +276,7 @@ const deactivate = () => store.SET_SHOP(null)
}
.qrcode {
height: 180px;
padding-top: 30px;
}
.qrcode-meta {
width: 194px;
@ -261,6 +289,9 @@ const deactivate = () => store.SET_SHOP(null)
}
&.isRow {
background: linear-gradient(99.5deg, #f0b92b 0%, #f9d556 100%);
.shop-name {
text-align: left;
}
}
}
}
@ -321,6 +352,12 @@ const deactivate = () => store.SET_SHOP(null)
position: relative;
}
}
&.isBrandShowMap {
width: 190px;
&.isActive {
position: relative;
}
}
&.isRow {
width: 388px;
left: auto;

56
src/components/BrandScroll/BrandScroll.vue

@ -1,5 +1,5 @@
<template>
<ScrollView ref="scroll" :class="['brand-scroll', $route.path === '/guide' ? 'guide' : 'brand']" scrollbar>
<ScrollView ref="scroll" class="brand-scroll" scrollbar>
<div class="brand-content" ref="content">
<div
class="groups"
@ -19,7 +19,7 @@
<div class="group" :class="{ isRow }">
<ShopItem
:config="config"
:isGuide="$route.path === '/guide' && !isRow"
:isGuide="path === '/guide' && !isRow"
:isRow="isRow"
:isFood="isFood"
:shop="el"
@ -47,12 +47,12 @@ import { useElementSize, useMutationObserver, useThrottleFn } from '@vueuse/core
import { getTranslateValues } from './getTranslateValues'
const store = useStore()
const { currentFloor } = storeToRefs(store)
const { currentFloor, path } = storeToRefs(store)
const indicatorWrapper = ref(null)
const scroll = ref(null)
const content = ref(null)
const groups = ref([])
const { height: containerHeight } = useElementSize(scroll)
const { height: containerHeight, width: containerWidth } = useElementSize(scroll)
const { height: contentHeight } = useElementSize(content)
const filteredList = computed(() => props.list.filter(({ shopList }) => shopList.length > 0))
@ -96,6 +96,44 @@ function handleShop(item) {
emits('click', item)
}
watch(containerHeight, () =>
nextTick(() => {
scroll.value.refresh()
if (props.shop) {
const el = document.getElementById(props.shop.houseNumber)
if (!el) return
nextTick(() => {
scroll.value.scrollToElement(el, 500, 0, -100)
})
} else {
const el = document.getElementById('current')
if (!el) return
nextTick(() => {
scroll.value.scrollToElement(el, 500, 0, -100)
})
}
})
)
watch(containerWidth, () =>
nextTick(() => {
scroll.value.refresh()
if (props.shop) {
const el = document.getElementById(props.shop.houseNumber)
if (!el) return
nextTick(() => {
scroll.value.scrollToElement(el, 500, 0, -100)
})
} else {
const el = document.getElementById('current')
if (!el) return
nextTick(() => {
scroll.value.scrollToElement(el, 500, 0, -100)
})
}
})
)
watch(
() => props.isRow,
() =>
@ -169,9 +207,6 @@ watch([scroll, () => props.shop], () => {
&.guide {
height: 770px;
}
&.brand {
height: 1512px;
}
:deep(.bscroll-vertical-scrollbar) {
top: 102px !important;
width: 48px !important;
@ -253,6 +288,13 @@ watch([scroll, () => props.shop], () => {
height: calc(100vh - 280px);
&.brand {
height: calc(100vh - 280px);
&.showMap {
margin-left: 54px;
.group {
grid-template-columns: 1fr 1fr;
gap: 24px 8px;
}
}
}
&.guide {
height: calc(100vh - 280px);

14
src/components/Map/Map.vue

@ -1,7 +1,12 @@
<template>
<div>
<!-- 地图容器 -->
<div id="mapContainer" :class="$route.path === '/billboard' ? 'billboard' : $route.path === '/guide' ? 'guide' : ''" />
<div
id="mapContainer"
:class="`${(path === '/brand' || path === '/foods') && !showMap ? 'hidden' : ''} ${path === '/billboard' ? 'unclickable' : ''} ${
path === '/guide' || path === '/brand' || path === '/foods' ? 'guide' : ''
}`"
></div>
<!-- 地图弹框 -->
<div id="shopInfo" class="boxShop">
@ -21,7 +26,7 @@ import { storeToRefs } from 'pinia'
import { useStore } from '@/store/root'
const store = useStore()
const { shop } = storeToRefs(store)
const { shop, showMap, path } = storeToRefs(store)
</script>
<style lang="scss" scoped>
@ -36,12 +41,15 @@ const { shop } = storeToRefs(store)
z-index: 10;
overflow: hidden;
background: var(--map-background);
&.billboard {
&.unclickable {
pointer-events: none;
}
&.guide {
top: 408px;
}
&.hidden {
visibility: hidden;
}
}
#fac {
position: absolute;

89
src/components/MapControl/MapControl.vue

@ -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>

0
src/views/Guide/list.js → src/components/MapControl/list.js

15
src/components/PublicComponent/PublicComponent.vue

@ -1,7 +1,7 @@
<template>
<div class="box" @click="addTotalClick"></div>
<!-- 地图容器 -->
<Map v-show="$route.meta.showMap" @handle-GO="handleGO" @handle-Detail="handleDetail" />
<Map v-show="$route.meta.showMap" @handle-GO="handleGO" @handle-Detail="handleDetail"></Map>
<!-- 倒计时返回首页提示 -->
<AutoBackNotification v-if="countDownGif" :title="title" :delay="isWall ? countDownToWall : countDownNum" />
@ -10,8 +10,8 @@
<Search v-if="showSearch" />
</Transition>
<Temperature />
<Time />
<SearchBar v-if="$route.path !== '/billboard'" />
<Time></Time>
<SearchBar v-if="path !== '/billboard'" />
<Tabs />
</template>
@ -19,7 +19,7 @@
import { defineAsyncComponent, watch, onMounted, ref, onBeforeUnmount } from 'vue'
import { storeToRefs } from 'pinia'
import { useStore } from '@/store/root'
import { useRouter, useRoute } from 'vue-router'
import { useRouter } from 'vue-router'
import { useHandleScreen } from '@/composables/useHandleScreen'
import { useLogout } from '@/composables/useLogout'
import { useInitMap } from '@/composables/useInitMap'
@ -32,9 +32,8 @@ const SearchBar = defineAsyncComponent(() => import('./SearchBar.vue'))
const Tabs = defineAsyncComponent(() => import('./Tabs.vue'))
const router = useRouter()
const route = useRoute()
const store = useStore()
const { shop, showSearch, language } = storeToRefs(store)
const { shop, showSearch, language, path } = storeToRefs(store)
const { resetClickNumber, setLogoutRef, addTotalClick } = useLogout()
@ -77,8 +76,8 @@ onBeforeUnmount(() => {
window.removeEventListener('touchend', checkHandleScreen)
})
watch(route, to => {
if (to.fullPath === '/guide' || to.fullPath === '/nav' || to.fullPath === '/billboard') {
watch(path, nxtPath => {
if (nxtPath === '/guide' || nxtPath === '/nav' || nxtPath === '/billboard') {
window?.Map_QM?.startRender()
} else {
window?.Map_QM?.cancelRender()

4
src/components/PublicComponent/SearchBar.vue

@ -1,6 +1,6 @@
<template>
<div class="row">
<div class="back" :style="{ backgroundImage: `url(${theme.images.back})` }" v-if="$route.path !== '/index'" @click="() => $router.push('/index')"></div>
<div class="back" :style="{ backgroundImage: `url(${theme.images.back})` }" v-if="path !== '/index'" @click="() => $router.push('/index')"></div>
<div class="bar" @click="handleSearch">
<div class="icon" :style="{ backgroundImage: `url(${theme.images.searchIcon})` }"></div>
<div class="stick"></div>
@ -14,7 +14,7 @@ import { useStore } from '@/store/root'
import { storeToRefs } from 'pinia'
const store = useStore()
const handleSearch = () => store.SET_SHOW_SEARCH(true)
const { theme } = storeToRefs(store)
const { theme, path } = storeToRefs(store)
</script>
<style lang="scss" scoped>

6
src/components/PublicComponent/Tabs.vue

@ -1,6 +1,6 @@
<template>
<div class="tabs" v-if="menuList.find(({ routePath }) => routePath === $route.path)">
<div :class="['item', tab.routePath === $route.path ? 'active' : '']" v-for="(tab, i) of menuList" :key="tab.moduleCode" @click="goPage(tab)">
<div class="tabs" v-if="menuList.find(({ routePath }) => routePath === path)">
<div :class="['item', tab.routePath === path ? 'active' : '']" v-for="(tab, i) of menuList" :key="tab.moduleCode" @click="goPage(tab)">
<div class="icon"><img :src="theme.images[tab.routePath.slice(1)]" /></div>
<div class="title">{{ tab.moduleName }}</div>
<div class="count" v-if="i === 0 && activityCount">{{ activityCount }}</div>
@ -17,7 +17,7 @@ import { getActivityList } from '@/http/api'
const activityCount = ref(0)
const router = useRouter()
const store = useStore()
const { menuList, theme } = storeToRefs(store)
const { menuList, theme, path } = storeToRefs(store)
const goPage = item => {
router.push(item.routePath)
}

4
src/components/Search/Search.vue

@ -68,7 +68,7 @@ const { _handleFacility } = useFacilityNav()
const router = useRouter()
const store = useStore()
const { indexList, shopList, facilityList, config, theme } = storeToRefs(store)
const { indexList, shopList, facilityList, config, theme, path } = storeToRefs(store)
const list = computed(() =>
theme.value
@ -95,7 +95,7 @@ async function handleShop(item) {
useStatistics('brandSearch', { shopCode: item.shopCode })
const shop = shopList.value.find(_shop => _shop.shopCode === item.shopCode)
store.SET_SHOP(shop)
if (router.currentRoute.value.fullPath !== '/guide') {
if (path.value !== '/guide') {
await router.push('/guide')
}
store.SET_SHOW_SEARCH(false)

10
src/composables/useHandleScreen.js

@ -1,6 +1,5 @@
import { reactive, onMounted, toRefs, computed, watch } from 'vue'
import { getBackTime } from '@/http/api'
import { useRouter } from 'vue-router'
import { useStatistics } from '@/composables/useStatistics'
import { useStore } from '@/store/root'
const DEVICE = {
@ -8,9 +7,8 @@ const DEVICE = {
ANDROID: 'android'
}
export const useHandleScreen = (callback, screenSaveCallback) => {
const router = useRouter()
const store = useStore()
const { currentFloor: device, mapStatus } = toRefs(store)
const { currentFloor: device, mapStatus, path } = toRefs(store)
const nativeMethods = computed(() => {
return !device.value ? {} : device.value.label === DEVICE.ANDROID ? window.android : window.chrome.webview.hostObjects.sync.csobj
@ -113,13 +111,13 @@ export const useHandleScreen = (callback, screenSaveCallback) => {
state.countDownGif = false
state.isWall = false
state.autoTimer = setTimeout(async () => {
if (router.currentRoute.value.fullPath === '/billboard') return
if (path.value === '/billboard') return
if (state.times[0] !== 0) {
if (router.currentRoute.value.fullPath !== '/index') {
if (path.value !== '/index') {
await indexPromise()
}
if ((state.times[1] === 0 && router.currentRoute.value.fullPath === '/index') || (state.times[0] === 0 && state.times[1] === 0)) {
if ((state.times[1] === 0 && path.value === '/index') || (state.times[0] === 0 && state.times[1] === 0)) {
await rootPromise()
callback && callback()
}

1
src/composables/useInitMap.js

@ -52,6 +52,7 @@ function onClickShop(event) {
console.log(event)
const store = useStore()
store.SET_FACILITY(null)
if (store.path.includes('/brand') || store.path.includes('/foods')) return
if (!event.data) {
store.SET_SHOP(null)
bounceShopAndChangeColor(null)

6
src/router/routes.js

@ -32,16 +32,16 @@ export const staticRoutes = [
component: () => import(/* webpackChunkName: "brand" */ '@/views/Brand/Brand'),
meta: {
showMenu: true,
showMap: false
showMap: true
}
},
{
path: '/foods',
name: 'Foods',
component: () => import(/* webpackChunkName: "foods" */ '@/views/Foods/Foods'),
component: () => import(/* webpackChunkName: "foods" */ '@/views/Brand/Brand'),
meta: {
showMenu: true,
showMap: false
showMap: true
}
},
{

6
src/store/root/actions.js

@ -56,5 +56,11 @@ export const actions = {
},
SET_MAP_STATUS(flag) {
this.mapStatus = flag
},
SET_SHOW_MAP(v) {
this.showMap = v
},
SET_PATH(v) {
this.path = v
}
}

4
src/store/root/state.js

@ -17,7 +17,9 @@ export const state = () => ({
theme: null,
facility: null,
foodIndustryMap: {},
mapStatus: false
mapStatus: false,
showMap: false,
path: '/'
})
export const is4k = () => {

183
src/views/Brand/Brand.vue

@ -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
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>

40
src/views/Foods/Foods.vue

@ -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>

70
src/views/Guide/Guide.vue

@ -69,20 +69,10 @@
</div>
</ScrollView>
</Teleport>
<Teleport to="body">
<div class="control-area">
<div class="map-control-wrapper animate__fast animate__animated animate__fadeInUp">
<div class="map-item" @click="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>
<MapControl @handleMapIcon="handleMapIcon" :mapIdx="mapIdx"></MapControl>
<div class="shop-list-wrapper">
<BrandScroll v-if="initiated" :shop="shop" :need-focus="true" @click="handleShop" :list="selectedShopList" :isRow="isRow" :config="config" />
<BrandScroll v-if="initiated" class="guide" :shop="shop" :need-focus="true" @click="handleShop" :list="selectedShopList" :isRow="isRow" :config="config" />
<div class="switch">
<img class="btn" v-if="isRow" :src="theme.images.grid" @click="isRow = false" />
<img class="btn" v-else :src="theme.images.gridActive" @click="isRow = false" />
@ -97,13 +87,14 @@
import { ref, watch, onBeforeUnmount, computed, nextTick } from 'vue'
import { storeToRefs } from 'pinia'
import { useStore } from '@/store/root'
import { RESET, DIRECTION, list as buttonList } from './list'
import View from '@/layouts/View.vue'
import { hideMapDialog, setShopActive, setShopInactive } from '@/composables/useInitMap'
import { getBrandListByFormat, getBrandListByFloor } from '@/http/brand/api'
import BrandScroll from '@/components/BrandScroll/BrandScroll.vue'
import ScrollView from '@/base/ScrollView/ScrollView.vue'
import { useMediaQuery } from '@vueuse/core'
import MapControl from '@/components/MapControl/MapControl.vue'
import { RESET, DIRECTION } from '@/components/MapControl/list'
const isH = useMediaQuery('(min-aspect-ratio: 1/1)')
@ -602,47 +593,6 @@ const handleFacility = fac => {
}
}
.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;
@ -659,17 +609,5 @@ const handleFacility = fac => {
margin-left: 54px;
}
}
.control-area {
left: 68px;
bottom: 40px;
right: auto;
top: auto;
height: auto;
.hands {
top: 280px;
left: 68px;
right: auto;
}
}
}
</style>

Loading…
Cancel
Save