Browse Source

Merge branch 'dev' of https://git.1000my.com/huangmh/client_iot-daoshi-template-v into dev

ShangHai_LongXiang
高志龙 2 years ago
parent
commit
b712cc9d2b
  1. 17
      public/static/offline/JSON/config.json
  2. 2
      public/static/offline/JSON/getBackTime.json
  3. 2
      public/static/offline/JSON/getBrandShopList.json
  4. 2
      public/static/offline/JSON/getBrandShopListByFloor.json
  5. 2
      public/static/offline/JSON/getBrandShopListByIndustryId.json
  6. 2
      public/static/offline/JSON/getCinemaInfo.json
  7. 26
      public/static/offline/JSON/getDevCoordinateByIP.json
  8. 2
      public/static/offline/JSON/getMap.json
  9. 2
      public/static/offline/JSON/getMapInfo.json
  10. 2
      public/static/offline/JSON/getQMGoShopData.json
  11. 8
      src/App.vue
  12. 63
      src/base/ShopItem/ShopItem.vue
  13. 56
      src/components/BrandScroll/BrandScroll.vue
  14. 14
      src/components/Map/Map.vue
  15. 89
      src/components/MapControl/MapControl.vue
  16. 0
      src/components/MapControl/list.js
  17. 15
      src/components/PublicComponent/PublicComponent.vue
  18. 4
      src/components/PublicComponent/SearchBar.vue
  19. 6
      src/components/PublicComponent/Tabs.vue
  20. 4
      src/components/Search/Search.vue
  21. 10
      src/composables/useHandleScreen.js
  22. 1
      src/composables/useInitMap.js
  23. 6
      src/router/routes.js
  24. 6
      src/store/root/actions.js
  25. 4
      src/store/root/state.js
  26. 181
      src/views/Brand/Brand.vue
  27. 40
      src/views/Foods/Foods.vue
  28. 70
      src/views/Guide/Guide.vue

17
public/static/offline/JSON/config.json

@ -1,16 +1 @@
{
"code": 200,
"msg": "操作成功",
"data": [
{
"id": 80,
"title": "默认配置",
"content": {
"interfaceUrl": "https://project-iot.test.1000my.com/api",
"handWriteUrl": "http://saas.1000my.com:8014/words",
"mobileNav": "https://qianmu-iot.1000my.com/base_qmgo_react-test/index.html#/",
"clickUploadUrl": "/analysis/v1/web/deviceUseClickDataUpload"
}
}
]
}
{"code":200,"msg":"操作成功","data":[{"id":115,"entryCode":"d3c94406-310b-11ee-8890-d68ef55ef3ac","title":"默认配置","content":{"interfaceUrl":"https://project-iot.test.1000my.com/api","handWriteUrl":"http://saas.1000my.com:8014/words","mobileNav":"https://qianmu-iot.1000my.com/base_qmgo_react-test/index.html#/","clickUploadUrl":"/analysis/v1/web/deviceUseClickDataUpload","debug":"debug"}}]}

2
public/static/offline/JSON/getBackTime.json

@ -1 +1 @@
{"code":200,"msg":"操作成功","data":[40,10]}
{"code":200,"msg":"操作成功","data":[20,20]}

2
public/static/offline/JSON/getBrandShopList.json

File diff suppressed because one or more lines are too long

2
public/static/offline/JSON/getBrandShopListByFloor.json

File diff suppressed because one or more lines are too long

2
public/static/offline/JSON/getBrandShopListByIndustryId.json

File diff suppressed because one or more lines are too long

2
public/static/offline/JSON/getCinemaInfo.json

@ -1 +1 @@
{"code":200,"msg":"操作成功","data":{"shopCode":"xW9QI6-094omWrnD0d9Nq","shopName":"视觉VR","shopNameEn":"","alias":"","logoUrl":"/iotFile/2022/06/21/73ebef539b65446eb1eac5ddfcf1aa14.jpg","houseNumber":"Y112","intro":"tico teco VR 作为温州视觉商贸有限公司的快时尚品牌,主营位于各大商业中心的全国连锁性的VR(虚拟现实)体验门店, 以VR+泛娱乐体验为主体,融入AR、MR、主机游戏、裸眼3D、智能机器人、电竞等新型的科技元素,让全年龄段客户都能获得热门、新鲜的科技体验 \r\n","introEn":"","materialList":["/iotFile/2022/06/21/81c8e216dbc442fe9f213ef406f91e9d.png"]}}
{"code":200,"msg":"操作成功","data":{"industryName":"影院超市","industryNameEn":"","industryFatherName":"休闲娱乐","industryFatherNameEn":"","shopCode":"_fp63UgYnqvU5p9CXhZyx","shopName":"奥斯卡国际影城","shopNameEn":"","alias":"","logoUrl":"/iotFile/project-zert3dski8fqmgr4zhusea/20230620/pEwa2kGCL2cHA6f57sayz.jpg","industryFatherUrl":"/iotFile/project-zert3dski8fqmgr4zhusea/20230714/V7vNxOmKc21EVpnN-LzXE.png","industryUrl":"","building":"A栋","floor":"4F","houseNumber":"F412","intro":"奥斯卡金地影城位于金地广场四、五层,2015年2月11日开始营业。影城占地面积4800平米,拥有8个国际化标准数字放映厅,1455个坐席,影城采用国际先进放映设备,超清晰画质金属荧幕,以高坡度,大落差,宽排距的座椅设计,保证每位观众绝佳观影效果。","businessHours":"10:00-22:00","contact":"029-88725666","materialList":["/iotFile/project-zert3dski8fqmgr4zhusea/20231115/rHmfW2Rgtj4InTeAdoVLg.png","/iotFile/project-zert3dski8fqmgr4zhusea/20231115/AfoeLqAI7EqHh1GAvgNYT.png"],"xaxis":"[1542, 30, -133]","yaxis":"26","navXaxis":"1283","navYaxis":"93","buildingOrder":0,"floorOrder":5}}

26
public/static/offline/JSON/getDevCoordinateByIP.json

@ -1,25 +1 @@
{
"code": 200,
"msg": "success",
"data": {
"machineCode": "GpaJWduZp-n5jLARQwIhx",
"machineName": "千目GO",
"machineTypeName": "导视",
"label": "windows",
"screenAttribute": "1920*1080横屏",
"building": "A栋",
"buildingCode": "ocRrNO47oiA3itmmdwHzm",
"buildingOrder": 0,
"floor": "L1",
"floorCode": "7Lxdij-Q9NnccCu2bVJMs",
"floorOrder": 2,
"ip": "192.168.56.1",
"mac": "0A0027000011",
"location": "50",
"angle": "0",
"projectCode": "project-200",
"regionCode": "",
"lensCoordinate": "",
"orientationCoordinate": ""
}
}
{"code":200,"msg":"success","data":{"machineCode":"MLnMzBjQdwVKysWyz1RG0","machineName":"111","machineTypeName":"导视","label":"windows","screenAttribute":"1080*1920竖屏","building":"A栋","buildingCode":"DLvmtzN2qodUq_oYr7vyM","buildingOrder":0,"floor":"1F","floorCode":"nL3E-j25zxxkag_HMSGsR","floorOrder":2,"ip":"192.168.1.21","mac":"E0D55E1B2B17","location":"16","angle":"0","projectCode":"project-zert3dski8fqmgr4zhusea","regionCode":"","lensCoordinate":"","orientationCoordinate":"","deployType":"saas","xfyunIp":"","xfyunPort":"","style":"0"}}

2
public/static/offline/JSON/getMap.json

File diff suppressed because one or more lines are too long

2
public/static/offline/JSON/getMapInfo.json

File diff suppressed because one or more lines are too long

2
public/static/offline/JSON/getQMGoShopData.json

@ -1 +1 @@
{"code":200,"msg":"操作成功","data":{"recommendList":[{"logoCode":"af555d42c0254232b54204520cc6db83","logoUrl":"/iotFile/2022/05/25/af555d42c0254232b54204520cc6db83.png","shopCode":"AN_o_vxiBlmgF--8CRQY-","shopName":"海南椰子鸡"},{"logoCode":"65098ac9d5764573a3f7470d9c48d580","logoUrl":"/iotFile/2022/06/21/65098ac9d5764573a3f7470d9c48d580.jpg","shopCode":"RxCziYVSgPMYchzy7hej0","shopName":"希色"},{"logoCode":"bff3193e743d4bfc8d30c3ac287dcdbf","logoUrl":"/iotFile/2022/06/21/bff3193e743d4bfc8d30c3ac287dcdbf.jpg","shopCode":"6t2GslN9_U8kBPke2bTmz","shopName":"星巴克"},{"logoCode":"f9f5cd482140458dbd04778d4790e249","logoUrl":"/iotFile/2022/06/21/f9f5cd482140458dbd04778d4790e249.jpg","shopCode":"gdL9jLxYwrD5rY_q-XWOh","shopName":"荔滋烫捞"},{"logoCode":"896cc12fbb214fc7af8e938e004d6513","logoUrl":"/iotFile/2022/06/21/896cc12fbb214fc7af8e938e004d6513.jpg","shopCode":"NjiFuPS2UCMh8h5i_05Mi","shopName":"优衣库"},{"logoCode":"1da1c24714974f6bb3bfce4bcfddf568","logoUrl":"/iotFile/2022/06/21/1da1c24714974f6bb3bfce4bcfddf568.jpg","shopCode":"5CfJMt_5s20sA1jVKzLwa","shopName":"中国李宁"},{"logoCode":"84a1d1a7232e459cb1c377b8f4e00ccf","logoUrl":"/iotFile/2022/06/21/84a1d1a7232e459cb1c377b8f4e00ccf.png","shopCode":"IsY2OG4Od-Rp4TL00qahU","shopName":"热风"},{"logoCode":"dd4ca9946f0c455ebebe2022ebcefa9b","logoUrl":"/iotFile/2022/06/21/dd4ca9946f0c455ebebe2022ebcefa9b.jpg","shopCode":"ljJsnoUp781-adEPydqRU","shopName":"VERO MODA"},{"logoCode":"eeb85afd46da4d83abbf467f41a27135","logoUrl":"/iotFile/2022/06/20/eeb85afd46da4d83abbf467f41a27135.jpg","shopCode":"pPOk0_lzMknf5YbpTvnRC","shopName":"十一日后"},{"logoCode":"da476d20bb824d039e93c83173d18107","logoUrl":"/iotFile/2022/06/21/da476d20bb824d039e93c83173d18107.jpg","shopCode":"OFKIMeKfwiex78jAxk2cU","shopName":"芬狄诗"},{"logoCode":"4814f6c971ee448c9f9af4dd65876b13","logoUrl":"/iotFile/2022/06/21/4814f6c971ee448c9f9af4dd65876b13.jpg","shopCode":"VBuvd4EdiQm6P8QveXHlF","shopName":"大疆"},{"logoCode":"db2dbfacaf7b447dadb9e75d0448afc1","logoUrl":"/iotFile/2022/06/21/db2dbfacaf7b447dadb9e75d0448afc1.jpg","shopCode":"X1RxLtQxx3IJmeZ_zgp7k","shopName":"周大福"},{"logoCode":"24782b42319941838641042c0f7c7690","logoUrl":"/iotFile/2022/06/21/24782b42319941838641042c0f7c7690.png","shopCode":"KHaG0tQAmUSWMGW2SFRKm","shopName":"大孚飞跃"},{"logoCode":"e4dfe55d6e6341959fab5ca235fa6e6f","logoUrl":"/iotFile/2022/06/21/e4dfe55d6e6341959fab5ca235fa6e6f.png","shopCode":"Bkfr6aY7Ut0QS-d5SpRFN","shopName":"JUPITER&CANDY"},{"logoCode":"7064a8b67e544cd3857b896dc1142da3","logoUrl":"/iotFile/2022/06/21/7064a8b67e544cd3857b896dc1142da3.jpg","shopCode":"OjDHDq6Zna2npFrv141s8","shopName":"JEWELRIA"},{"logoCode":"066cb31027334eeb8c5c102543dc9a83","logoUrl":"/iotFile/2022/04/02/066cb31027334eeb8c5c102543dc9a83.jpg","shopCode":"DvM4nNbetDT9tYA-gTrd8","shopName":"屈臣氏"},{"logoCode":"e42a1a763add4ce095341ae335a84734","logoUrl":"/iotFile/2022/06/21/e42a1a763add4ce095341ae335a84734.jpg","shopCode":"NIV5CgXPKCWTa39gxyoqZ","shopName":"谭木匠"}],"searchList":[{"shopNum":2,"shopCode":"a6JxLK8cQqDLQ2piug8Gn","shopName":"书亦烧仙草"},{"shopNum":2,"shopCode":"DvM4nNbetDT9tYA-gTrd8","shopName":"屈臣氏"},{"shopNum":1,"shopCode":"fT5cvNAf91n_tN99fTAsq","shopName":"好的"},{"shopNum":1,"shopCode":"7twEQl8ooydzBRFAcLluR","shopName":"爱回收"},{"shopNum":1,"shopCode":"NjiFuPS2UCMh8h5i_05Mi","shopName":"优衣库"}]}}
{"code":200,"msg":"操作成功","data":{"recommendList":[{"logoCode":"jdLRcaBQaoRMcsbaXpsMm","logoUrl":"/iotFile/project-zert3dski8fqmgr4zhusea/20230612/jdLRcaBQaoRMcsbaXpsMm.jpg","shopCode":"ptmeCKcDksiWQxI6ocQpn","shopName":"宝马"},{"logoCode":"pLr26UEBLMUpOdMnQhPbQ","logoUrl":"/iotFile/project-zert3dski8fqmgr4zhusea/20230714/pLr26UEBLMUpOdMnQhPbQ.png","shopCode":"iOoSnwfAGrKut2dHT9YWq","shopName":"华为"},{"logoCode":"NWaFow0HA7ChS5f5-f7er","logoUrl":"/iotFile/project-zert3dski8fqmgr4zhusea/20230612/NWaFow0HA7ChS5f5-f7er.jpg","shopCode":"24fD2WMoOuYanvSvyODLY","shopName":"ECCO"},{"logoCode":"Yg4AQeg2LdoR4bi9R7zaB","logoUrl":"/iotFile/project-zert3dski8fqmgr4zhusea/20230612/Yg4AQeg2LdoR4bi9R7zaB.jpg","shopCode":"WRg5W8kMRQumYQh3gFh5Q","shopName":"有兰"},{"logoCode":"jJO6QIlkPCS7yVmf0rPT9","logoUrl":"/iotFile/project-zert3dski8fqmgr4zhusea/20230612/jJO6QIlkPCS7yVmf0rPT9.jpg","shopCode":"ER5b93KkblRaTCUpxW6CM","shopName":"NEXY.CO"}],"searchList":[{"shopNum":72,"shopCode":"pyv212DLXi3X-a4zF-9oE","shopName":"UNIQLO"},{"shopNum":41,"shopCode":"sw9gukxkbv8yk_Ge7WVJb","shopName":"M STAND"},{"shopNum":39,"shopCode":"hm-mV58hy21EO53DJxk38","shopName":"星巴克"},{"shopNum":39,"shopCode":"xEr6y6m7nw1t8_Ja_PUSt","shopName":"喜茶"},{"shopNum":29,"shopCode":"5kjjVbC3rmUtBz2HWQeiL","shopName":"略味"}]}}

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 = () => {

181
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>
.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 {
position: relative;
flex: 1;
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