Browse Source

feat: 对接商户端数据

ShangHai_LongXiang
jiannibang 2 years ago
parent
commit
f5ad1f5ab3
  1. 245
      src/base/ShopItem/ShopItem.vue
  2. BIN
      src/base/ShopItem/arrow.png
  3. BIN
      src/base/ShopItem/close.png
  4. BIN
      src/base/ShopItem/loc.png
  5. BIN
      src/base/ShopItem/pictag.png
  6. 41
      src/composables/useInitConfigAndMallInfo.js
  7. 4
      src/http/api.js
  8. 3
      src/http/config.js
  9. 6
      src/store/root/actions.js
  10. 1
      src/store/root/getters.js
  11. 2
      src/store/root/state.js
  12. 7
      src/views/Brand/Brand.vue

245
src/base/ShopItem/ShopItem.vue

@ -8,6 +8,7 @@
class="shop-logo"
/>
</div>
<img v-if="pics" src="./pictag.png" class="pictag" alt="" />
<p class="name">
<span class="shop-name">{{ switchLanguage(shop, 'shopName') }}</span>
<span class="name-right" v-if="path === '/guide'"> {{ shop.distance ? shop.distance + '' : '' }}<img v-if="shop.dir" class="dir" :src="shop.dir" /> </span>
@ -33,7 +34,7 @@
<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">
<div v-if="isActive && isRow && !(pics && pics.length)" class="qrcode-modal" @click="deactivate">
<div class="content" @click.stop="void 0">
<div
class="close"
@ -52,6 +53,32 @@
</div>
</Transition></Teleport
>
<Teleport to="body">
<Transition appear enter-active-class="animate__animated animate__fadeIn" leave-active-class="animate__animated animate__fadeOut">
<div v-if="isActive && pics && pics.length" class="pic-modal" @click="deactivate">
<div class="content" @click.stop="void 0">
<img loading="lazy" :src="'./static/offline' + shop.logoUrl" alt="" class="logo" />
<div class="name">{{ switchLanguage(shop, 'shopName') }}</div>
<div class="meta"><img src="./loc.png" class="loc" />{{ shop.floor }}</div>
<img class="close" src="./close.png" @click="deactivate" />
<QRCodeFromText
class="qrcode"
:size="135"
:text="`${config.mobileNav}?code=${currentFloor.projectCode}&s=${currentFloor.floorOrder}_${currentFloor.location}_屏幕的位置&e=${shop.houseNumber}`"
></QRCodeFromText>
<div class="qrcode-meta">扫码导航<img src="./arrow.png" class="arrow" /></div>
<div class="pics">
<EffectFade :list="pics || []">
<template v-slot="{ item }">
<div class="pics-wrapper">
<img class="banner" :src="item" alt="" />
</div>
</template>
</EffectFade>
</div>
</div></div></Transition
></Teleport>
</div>
</template>
@ -60,9 +87,11 @@ import QRCodeFromText from '@/components/QRCodeFromText/QRCodeFromText.vue'
import { storeToRefs } from 'pinia'
import { useStore } from '@/store/root'
import { computed } from 'vue'
import EffectFade from '@/components/EffectFade/EffectFade.vue'
const store = useStore()
const { currentFloor, config, theme, showMap, path } = storeToRefs(store)
const { currentFloor, config, theme, showMap, path, shopPicsMap } = storeToRefs(store)
const isBrandShowMap = computed(() => showMap.value && (path.value === '/brand' || path.value === '/foods'))
const pics = computed(() => shopPicsMap.value[props.shop.shopCode])
const props = defineProps({
shop: Object,
@ -82,11 +111,19 @@ const deactivate = () => store.SET_SHOP(null)
<style lang="scss" scoped>
.group-item {
position: relative;
width: 230px;
height: 200px;
background: var(--brand-background);
border-radius: var(--global-radius, 8px);
overflow: hidden;
.pictag {
position: absolute;
left: 4px;
top: 4px;
width: 24px;
height: 24px;
}
.logo-wrapper {
width: 100%;
height: 160px;
@ -186,6 +223,7 @@ const deactivate = () => store.SET_SHOP(null)
background: var(--food-background);
border-radius: var(--global-radius, 40px);
align-items: center;
overflow: visible;
.logo-wrapper {
width: 180px;
height: 180px;
@ -310,6 +348,7 @@ const deactivate = () => store.SET_SHOP(null)
display: flex;
flex-direction: column;
align-items: center;
border-radius: var(--global-radius, 40px);
}
.qrcode {
height: 180px;
@ -378,6 +417,141 @@ const deactivate = () => store.SET_SHOP(null)
}
}
}
.pic-modal {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 1000;
.content {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 870px;
background: #fff;
box-shadow: 0px 15px 24px 0px rgba(0, 0, 0, 0.05);
.logo {
position: absolute;
top: 20px;
left: 68px;
width: 80px;
height: 80px;
border-radius: 12px;
border: 1px solid var(--b-10, rgba(0, 0, 0, 0.1));
background: var(--w-100, #fff);
padding: 4px;
}
.name {
position: absolute;
top: 30px;
left: 172px;
color: var(--b-80, rgba(0, 0, 0, 0.8));
font-size: 24px;
font-style: normal;
font-weight: 700;
line-height: 28px;
}
.meta {
position: absolute;
top: 66px;
left: 172px;
height: 23px;
display: inline-flex;
color: var(--b-60, rgba(0, 0, 0, 0.6));
font-size: 20px;
font-style: normal;
font-weight: 700;
align-items: center;
.loc {
width: 20px;
height: 20px;
margin-right: 8px;
}
}
.qrcode {
position: absolute;
top: -50px;
right: 168px;
width: 150px;
height: 150px;
display: flex;
justify-content: center;
align-items: center;
border-radius: 12px;
border: 1px solid var(--b-10, rgba(0, 0, 0, 0.1));
background: var(--w-100, #fff);
}
.qrcode-meta {
position: absolute;
display: inline-flex;
top: 65px;
right: 334px;
color: var(--b-60, rgba(0, 0, 0, 0.6));
font-size: 20px;
font-style: normal;
font-weight: 700;
line-height: 24px;
align-items: center;
.arrow {
width: 24px;
height: 24px;
margin-left: 4px;
}
}
.close {
position: absolute;
width: 80px;
height: 80px;
top: 20px;
right: 68px;
}
.pics {
position: absolute;
left: 68px;
right: 68px;
top: 120px;
:deep(.swiper) {
.swiper-pagination {
top: auto;
right: 0;
bottom: 0;
left: 0;
width: auto;
margin: auto;
.swiper-pagination-bullet {
width: 34px !important;
height: 4px !important;
background: var(--b-10, rgba(0, 0, 0, 0.1));
border-radius: 6px !important;
opacity: inherit !important;
margin: 0;
&.swiper-pagination-bullet-active {
background: var(--VI--, #516dd8);
border-radius: 6px !important;
}
}
}
}
.pics-wrapper {
position: relative;
width: 944px;
height: 724px;
border-radius: 16px;
overflow: hidden;
.banner {
width: 100%;
height: 708px;
object-fit: cover;
border-radius: 16px;
}
}
}
}
}
@media (min-aspect-ratio: 1/1) {
.group-item {
.logo-wrapper {
@ -412,5 +586,72 @@ const deactivate = () => store.SET_SHOP(null)
bottom: 280px;
}
}
.pic-modal {
.content {
left: auto;
right: 0;
width: 610px;
height: 914px;
box-shadow: 0px 15px 24px 0px rgba(0, 0, 0, 0.05);
border-radius: 24px;
.logo {
top: 40px;
left: 40px;
width: 150px;
height: 150px;
padding: 15px;
}
.name {
top: 74px;
left: 222px;
}
.meta {
top: 128px;
left: 222px;
height: 28px;
font-size: 24px;
}
.qrcode {
left: 40px;
bottom: 40px;
top: auto;
}
.qrcode-meta {
left: 222px;
bottom: 40px;
right: auto;
top: auto;
flex-direction: row-reverse;
.arrow {
margin-left: 0;
margin-right: 4px;
transform: rotate(180deg);
}
}
.close {
right: 40px;
bottom: 40px;
top: auto;
}
.pics {
position: absolute;
left: 15px;
right: 15px;
top: 230px;
.pics-wrapper {
width: 580px;
height: 451px;
border-radius: 16px;
overflow: hidden;
.banner {
width: 100%;
height: 435px;
object-fit: cover;
border-radius: 16px;
}
}
}
}
}
}
</style>

BIN
src/base/ShopItem/arrow.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 332 B

BIN
src/base/ShopItem/close.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

BIN
src/base/ShopItem/loc.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 912 B

BIN
src/base/ShopItem/pictag.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

41
src/composables/useInitConfigAndMallInfo.js

@ -1,4 +1,4 @@
import { getConfig, getCurrentFloor, getGuideList, getTheme, getQMGoShopData, getMenuList, getFoodIIndustryList, getWeather } from '@/http/api'
import { getConfig, getCurrentFloor, getGuideList, getTheme, getQMGoShopData, getMenuList, getFoodIIndustryList, getWeather, getPicsUrl, getPics } from '@/http/api'
import { useStore } from '@/store/root'
import VConsole from 'vconsole'
const matchPXExp = /([0-9.]+px)/g
@ -67,6 +67,43 @@ export const useInitConfigAndMallInfo = async () => {
data: { searchList, recommendList }
} = await getQMGoShopData()
const shopMap = shopList.reduce((acc, nxt) => ({ ...acc, [nxt.shopCode]: nxt }), {})
const hasPic = getPicsUrl()
if (hasPic) handlePics()
store.SET_INDEX_LIST({ ...weather, recommendList, hotSearch: searchList.filter(({ shopName }) => shopName).map(item => ({ ...item, ...shopMap[item.shopCode] })) })
}
const fetchPics = async () => {
const store = useStore()
try {
const { data } = await getPics()
if (Array.isArray(data)) {
store.SET_SHOP_PICS(data)
setPics()
}
} catch (error) {
console.log(error)
}
}
const setPics = () => {
const store = useStore()
const now = Date.now()
const shopPicsMap = store.shopPics.reduce((acc, { shopCode, fileUrl, startTime, endTime }) => {
const s = new Date(startTime).getTime()
const e = new Date(endTime).getTime()
if (s < now && e > now) {
acc[shopCode] = acc[shopCode] ? [...acc[shopCode], fileUrl] : [fileUrl]
}
return { ...acc }
}, {})
store.SET_SHOP_PICS_MAP(shopPicsMap)
}
const handlePics = () => {
fetchPics()
setInterval(() => {
fetchPics()
}, 30 * 60 * 1000)
setInterval(() => {
setPics()
}, 60 * 1000)
}

4
src/http/api.js

@ -54,3 +54,7 @@ export const getMenuList = () => get(`./static/offline/JSON/menuList.json`)
export const getFoodIIndustryList = () => get(`./static/offline/JSON/foodIIndustryList.json`)
export const getFacilityList = () => get(`./static/offline/JSON/getFacilityList.json`)
export const getPicsUrl = () => url().picsUrl
export const getPics = () => post(url().picsUrl, {})

3
src/http/config.js

@ -16,5 +16,6 @@ export function url() {
const sourceUrl = './static/offline'
const handWriteUrl = store.config.handWriteUrl
const clickUploadUrl = store.config.clickUploadUrl
return { interfaceUrl, sourceUrl, handWriteUrl, clickUploadUrl }
const picsUrl = !interfaceUrl ? null : !interfaceUrl.startsWith('https') ? null : `${interfaceUrl}/data/v1/web/getShopPictureList`
return { interfaceUrl, sourceUrl, handWriteUrl, clickUploadUrl, picsUrl }
}

6
src/store/root/actions.js

@ -1,6 +1,12 @@
import { i18n } from '@/i18n'
import { useStatistics } from '@/composables/useStatistics'
export const actions = {
SET_SHOP_PICS(list) {
this.shopPics = list
},
SET_SHOP_PICS_MAP(map) {
this.shopPicsMap = map
},
SET_SHOP_LIST(list) {
this.shopList = list
},

1
src/store/root/getters.js

@ -40,7 +40,6 @@ export const currentFloorShopMap = ({ currentFloor: device, shopList, mapData })
let angle = (Math.atan2(deviceY - xaxis[2], xaxis[0] - deviceX) / Math.PI) * 180 + device.angle
if (angle < 0) angle += 360
if (angle > 360) angle -= 360
console.log(angle)
angle = (angle / 180) * Math.PI
result.distance = Math.ceil(getDistance({ x: deviceX, y: deviceY }, { x: xaxis[0], y: xaxis[2] }) / building.scale)

2
src/store/root/state.js

@ -1,4 +1,6 @@
export const state = () => ({
shopPicsMap: {},
shopPics: [],
menuList: [],
is4k: is4k(),
shopList: [], //店铺列表

7
src/views/Brand/Brand.vue

@ -37,7 +37,7 @@ import { useMediaQuery } from '@vueuse/core'
const store = useStore()
const storeRefs = storeToRefs(store)
const { config, indexList, foodIndustryMap, shop, currentFloor, showMap, showSearch, path } = storeRefs
const { config, indexList, foodIndustryMap, shop, currentFloor, showMap, showSearch, path, shopPicsMap } = storeRefs
const mapIdx = ref(-1)
const mapTimer = ref(null)
const mapIconTimer = ref(null)
@ -67,9 +67,10 @@ onBeforeUnmount(() => {
if (!showSearch.value) store.SET_SHOP(null)
})
function handleShop(item) {
if (shop.value?.shopCode === item.shopCode) {
if (shop.value?.shopCode === item.shopCode || shopPicsMap.value[item.shopCode]) {
if (showMap.value) {
window.Map_QM.pathNode({ floor: shop.value.floorOrder, node: shop.value.yaxis })
if (shop.value) window.Map_QM.pathNode({ floor: shop.value.floorOrder, node: shop.value.yaxis })
else window.Map_QM.pathNode({ floor: item.floorOrder, node: item.yaxis })
} else store.SET_SHOW_MAP(true)
}
mapIdx.value = -1

Loading…
Cancel
Save