You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
354 lines
9.4 KiB
354 lines
9.4 KiB
<template>
|
|
<Dialog @close="close">
|
|
<div class="bar">
|
|
<div class="icon" :style="{ backgroundImage: `url(${theme.images.searchIcon})` }"></div>
|
|
<div class="stick"></div>
|
|
<div v-if="!keywords" class="placeholder">请输入中英文<span class="meta">首字母</span>查询,例如 XBK (星巴克)</div>
|
|
<input v-else @click="setSearch" v-model="keywords" type="text" readonly class="input" />
|
|
<img :src="theme.images.searchClear" class="clear" v-if="keywords" @click="clear" />
|
|
</div>
|
|
<div class="search-content">
|
|
<div class="top" v-if="!showVoice">
|
|
<div class="facs">
|
|
<h1 class="title">公共设施</h1>
|
|
<div class="facility-list">
|
|
<FacilityItem @click="_handleFacility(item)" class="margin" :facility="item" v-for="item of facilityList" :key="item.title" />
|
|
</div>
|
|
</div>
|
|
<div class="keyboard-wrapper">
|
|
<div class="tabs tabs-wrapper">
|
|
<div class="tab" :class="{ active: tabIdx === index }" @click="handleTab(index)" v-for="(item, index) of list" :key="item.name">
|
|
<img :src="tabIdx === index ? item.iconActive : item.icon" alt="" />
|
|
{{ item.name }}
|
|
</div>
|
|
</div>
|
|
<KeyboardByLetter @del="del" @handle-letter="handleLetter" v-if="tabIdx === 0" />
|
|
<KeyboardByWritten @del="del" @handle-word="handleLetter" v-else />
|
|
</div>
|
|
</div>
|
|
|
|
<div class="recs" v-show="!keywords.length && !showVoice">
|
|
<h1 class="title">大家都在找</h1>
|
|
<div class="hot-scroll">
|
|
<div class="item" v-for="(item, i) of hotRecommend" :key="item.shopId" @click="handleShop(item)">
|
|
<div class="text">{{ item.shopName }}</div>
|
|
<img class="medal" v-if="i === 0" src="@/views/Index/fir.svg" />
|
|
<img class="medal" v-if="i === 1" src="@/views/Index/sec.svg" />
|
|
<img class="medal" v-if="i === 2" src="@/views/Index/thi.svg" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<SearchResultList @handle-shop="handleShop" @handle-activity="handleActivity" :list="searchShopListRef" :config="config" v-if="keywords.length && !showVoice" />
|
|
|
|
<Voice @handle-question="showClassify = true" v-if="showVoice" />
|
|
</div>
|
|
<img :src="theme.images.searchClose" class="search-btn" @click="close" />
|
|
</Dialog>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { computed, defineAsyncComponent, ref, onBeforeUnmount } from 'vue'
|
|
import { useRouter } from 'vue-router'
|
|
import { storeToRefs } from 'pinia'
|
|
import { useStore } from '@/store/root'
|
|
|
|
import { useSearchShop } from '@/composables/useSearchShop'
|
|
import { useFacilityNav } from '@/composables/useFacilityNav'
|
|
import { useStatistics } from '@/composables/useStatistics'
|
|
import Dialog from '@/layouts/Dialog.vue'
|
|
import FacilityItem from '@/base/FacilityItem/FacilityItem.vue'
|
|
import Tabs from '@/components/Tabs/Tabs.vue'
|
|
import SearchResultList from '@/components/SearchResultList/SearchResultList.vue'
|
|
|
|
const KeyboardByLetter = defineAsyncComponent(() => import('@/components/KeyboardByLetter/KeyboardByLetter.vue'))
|
|
const KeyboardByWritten = defineAsyncComponent(() => import('@/components/KeyboardByWritten/KeyboardByWritten.vue'))
|
|
const Voice = defineAsyncComponent(() => import('@/components/Voice/Voice.vue'))
|
|
|
|
const MAX_LENGTH = 7
|
|
|
|
const { _handleFacility } = useFacilityNav()
|
|
|
|
const router = useRouter()
|
|
|
|
const store = useStore()
|
|
const { indexList, shopList, facilityList, config, showVoice, theme } = storeToRefs(store)
|
|
|
|
const list = computed(() =>
|
|
theme.value
|
|
? [
|
|
{
|
|
name: '键盘输入',
|
|
nameEn: 'keyboard input',
|
|
icon: theme.value.images.keyboard,
|
|
iconActive: theme.value.images.keyboard_active
|
|
},
|
|
{
|
|
name: '手写输入',
|
|
nameEn: 'handwriting input',
|
|
icon: theme.value.images.write,
|
|
iconActive: theme.value.images.write_active
|
|
}
|
|
]
|
|
: []
|
|
)
|
|
|
|
const hotRecommend = computed(() => indexList.value.hotSearch ?? [])
|
|
const showClassify = ref(false)
|
|
|
|
async function handleShop(item) {
|
|
useStatistics('brandSearch')
|
|
const shop = shopList.value.find(_shop => _shop.shopId === item.shopId)
|
|
store.SET_SHOP(shop)
|
|
if (router.currentRoute.value.fullPath !== '/guide') {
|
|
store.SET_SELECTED_MODULE('Guide')
|
|
await router.push('/guide')
|
|
}
|
|
store.SET_SHOW_SEARCH(false)
|
|
}
|
|
async function handleActivity() {
|
|
await router.push('/activity')
|
|
store.SET_SHOW_SEARCH(false)
|
|
}
|
|
|
|
const tabIdx = ref(0)
|
|
function handleTab(index) {
|
|
tabIdx.value = index
|
|
}
|
|
|
|
const keywords = ref('')
|
|
const searchType = ref(0) //0: 键盘 1: 手写
|
|
const { searchShopListRef } = useSearchShop(keywords, searchType)
|
|
function handleLetter(item) {
|
|
if (keywords.value.length >= MAX_LENGTH) return
|
|
keywords.value += item
|
|
}
|
|
function del() {
|
|
keywords.value = keywords.value.substring(0, keywords.value.length - 1)
|
|
}
|
|
const clear = () => {
|
|
keywords.value = ''
|
|
}
|
|
|
|
function setSearch() {
|
|
showVoice.value && store.SET_SHOW_VOICE(false)
|
|
}
|
|
|
|
function close() {
|
|
store.SET_SHOW_SEARCH(false)
|
|
}
|
|
|
|
onBeforeUnmount(() => {
|
|
showVoice.value && store.SET_SHOW_VOICE(false)
|
|
})
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
:deep(.header-left) {
|
|
padding-left: 80px;
|
|
}
|
|
:deep(.view-container) {
|
|
position: relative;
|
|
z-index: 50;
|
|
}
|
|
.bar {
|
|
position: fixed;
|
|
top: 260px;
|
|
left: 68px;
|
|
right: 68px;
|
|
background: var(--search-barBackground);
|
|
border: var(--searchBar-border);
|
|
box-shadow: 0px 15px 24px rgba(0, 0, 0, 0.05);
|
|
border-radius: var(--searchBar-borderRadius);
|
|
display: flex;
|
|
align-items: center;
|
|
padding-right: 10px;
|
|
padding-left: 40px;
|
|
height: 100px;
|
|
z-index: 52;
|
|
.icon {
|
|
width: 56px;
|
|
height: 56px;
|
|
background: center / cover no-repeat;
|
|
}
|
|
.stick {
|
|
width: 4px;
|
|
height: 38px;
|
|
background: var(--searchBar-stickBg);
|
|
margin: 0 32px;
|
|
}
|
|
.placeholder {
|
|
font-weight: 700;
|
|
font-size: 20px;
|
|
line-height: 23px;
|
|
color: var(--searchBar-placeholderColor);
|
|
.meta {
|
|
color: var(--search-placeholderMetaColor);
|
|
}
|
|
}
|
|
.input {
|
|
flex: 1;
|
|
border: none;
|
|
outline: none;
|
|
width: 262px;
|
|
background: transparent;
|
|
font-weight: 700;
|
|
font-size: 48px;
|
|
line-height: 59px;
|
|
color: var(--searchBar-color);
|
|
}
|
|
.clear {
|
|
display: flex;
|
|
width: 80px;
|
|
height: 80px;
|
|
}
|
|
.voice-icon {
|
|
width: 190px;
|
|
}
|
|
}
|
|
.search-content {
|
|
position: absolute;
|
|
top: 315px;
|
|
right: 40px;
|
|
bottom: 302px;
|
|
left: 40px;
|
|
background: var(--search-background);
|
|
backdrop-filter: blur(10px);
|
|
border-radius: var(--searchBar-borderRadius);
|
|
z-index: 2;
|
|
overflow: hidden;
|
|
|
|
.top {
|
|
height: 460px;
|
|
background: var(--search-topBg, rgba(255, 255, 255, 0.6));
|
|
border-radius: var(--searchBar-borderRadius);
|
|
margin: 10px 10px 48px 10px;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
padding-left: 46px;
|
|
overflow: hidden;
|
|
.facs {
|
|
.title {
|
|
font-weight: 900;
|
|
font-size: 24px;
|
|
line-height: 28px;
|
|
color: var(--search-facTitleColor);
|
|
}
|
|
width: 300px;
|
|
padding-top: 111px;
|
|
.facility-list {
|
|
display: grid;
|
|
grid-template-columns: 1fr 1fr 1fr 1fr;
|
|
width: 264px;
|
|
margin-top: 40px;
|
|
gap: 12px 24px;
|
|
}
|
|
}
|
|
.tabs-wrapper {
|
|
display: flex;
|
|
width: 100%;
|
|
height: 64px;
|
|
background: var(--search-tabsBg, rgba(0, 0, 0, 0.05));
|
|
border-radius: var(--global-radius, 12px);
|
|
overflow: hidden;
|
|
|
|
.tab {
|
|
height: 100%;
|
|
flex: 1;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
color: var(--search-tabColor, rgba(0, 0, 0, 0.6));
|
|
border-radius: var(--global-radius, 12px);
|
|
&.active {
|
|
background: var(--search-tabActiveBg, #ffffff);
|
|
color: var(--search-tabActiveColor, rgba(0, 0, 0, 0.8));
|
|
}
|
|
}
|
|
|
|
img {
|
|
width: 24px;
|
|
height: 24px;
|
|
margin-right: 12px;
|
|
}
|
|
}
|
|
.keyboard-wrapper {
|
|
flex: 1;
|
|
padding-top: 75px;
|
|
padding-left: 36px;
|
|
background: rgba(0, 0, 0, 0.05);
|
|
padding-right: 38px;
|
|
.tabs {
|
|
padding-right: 8px;
|
|
}
|
|
}
|
|
}
|
|
.recs {
|
|
padding-left: 56px;
|
|
.title {
|
|
color: var(--search-hotSearchTitleColor);
|
|
}
|
|
.hot-scroll {
|
|
padding-top: 16px;
|
|
white-space: nowrap;
|
|
width: 944px;
|
|
|
|
.item {
|
|
position: relative;
|
|
display: inline-block;
|
|
max-width: 170px;
|
|
height: 52px;
|
|
background: var(--search-hotSearchBg);
|
|
border-radius: var(--global-radius, 100px);
|
|
.text {
|
|
text-align: center;
|
|
font-weight: 900;
|
|
font-size: 16px;
|
|
line-height: 52px;
|
|
color: var(--search-hotSearchColor);
|
|
overflow: hidden;
|
|
white-space: nowrap;
|
|
text-overflow: ellipsis;
|
|
padding: 0 32px;
|
|
}
|
|
.medal {
|
|
position: absolute;
|
|
width: 32px;
|
|
height: 32px;
|
|
left: 4px;
|
|
top: -10px;
|
|
}
|
|
}
|
|
.item + .item {
|
|
margin-left: 24px;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
.search-right {
|
|
position: relative;
|
|
background: rgba(255, 255, 255, 0.6);
|
|
padding-top: 180px;
|
|
padding-left: 64px;
|
|
width: 696px;
|
|
|
|
.title,
|
|
.title-en {
|
|
margin-right: 76px;
|
|
}
|
|
}
|
|
.search-btn {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
position: fixed;
|
|
left: 0;
|
|
right: 0;
|
|
margin: auto;
|
|
bottom: 242px;
|
|
width: 120px;
|
|
height: 120px;
|
|
z-index: 52;
|
|
}
|
|
</style>
|
|
|