index.vue 8.44 KB
<!--
 * @Date: 2023-07-27 11:04:04
 * @LastEditors: hookehuyr hookehuyr@gmail.com
 * @LastEditTime: 2023-08-04 16:57:15
 * @FilePath: /map-demo/src/components/Floor/index.vue
 * @Description: 文件描述
-->
<template>
	<div>
		<div class="container">
			<div class="main">
				<header class="codrops-header">
					<h1>3D导航地图</h1>
				</header>
				<div class="tool-bar">
					<div @click="onClose" class="close icon">
						<van-icon name="cross" size="1.25rem" />
					</div>
					<div @click="onSearch" class="search-icon icon">
						<van-icon name="search" size="1.25rem" />
					</div>
					<div v-if="level_show" class="switch-left icon">
						<span v-if="level_show < 4" @click="switchFloor('left')">
							<van-icon name="arrow-up" size="1.25rem" />
						</span>
					</div>
					<div v-if="level_show" class="close-level icon" @click.native="onCloseLevel">
						<span>
							<van-icon name="points" size="1.25rem" />
						</span>
					</div>
					<div v-if="level_show" class="switch-right icon">
						<span v-if="level_show > 1" @click="switchFloor('right')">
							<van-icon name="arrow-down" size="1.25rem" />
						</span>
					</div>
				</div>
				<div class="mall">
					<!-- <div class="surroundings">
            <img class="surroundings__map" src="./surroundings.svg" alt="Surroundings" />
          </div> -->
					<div class="levels">
						<div v-for="(level, index) in level_list" :key="index" @click="onFloorClick(index + 1)"
							:class="['level', 'level--' + (index + 1)]">
							<div v-html="level.svg"></div>
							<div class="level__pins">
								<a @click="clickPin(item, $event)" v-for="(item, index) in level.pin" :key="index" class="pin" :style="item.style"
									:data-category="item.category" :data-space="item.space">
									<span class="pin__icon">
										<svg class="icon icon--pin">
											<use xlink:href="#icon-pin"></use>
										</svg>
										<svg :class="['icon', 'icon--logo', `icon--${item.icon}`]">
											<use :xlink:href='"#icon-" + item.icon'></use>
										</svg>
									</span>
								</a>
							</div>
							<div class="level_after">L{{ index + 1 }}</div>
						</div>
					</div>
				</div>
			</div>
		</div>

		<van-popup v-model:show="show_popup" position="bottom" :overlay="false"
			:style="{ height: '50%', background: '#FFF' }">
			<div @click="onClosePopup" style="text-align: right; padding: 1rem;">
				<van-icon name="cross" size="1.5rem" />
			</div>
			<div style="margin: 0 1rem">
				<div style="font-size: 1.25rem; margin-bottom: 1rem;">{{ popup_title }}</div>
				<div> {{ popup_content }} </div>
			</div>
		</van-popup>

		<svg-icon></svg-icon>

		<van-popup v-model:show="show_search_popup" position="right" :overlay="true"
			:style="{ height: '100%', width: '80%', background: '#FFF' }">
			<div style="background-color: #F7f8fa; padding: 1rem;">
				<van-field v-model="search_value" @update:model-value="onSearchUpdate" placeholder="请输入搜索关键词" style="border-radius: 5px; padding: 0.5rem 1rem;" />
			</div>
			<div v-for="(item, index) in search_list" :key="index" v-show="item.show" class="search_box">
				<div class="search_box_title">{{ item.title }}</div>
				<van-row v-show="x.show" v-for="(x, idx) in item.list" :key="idx" @click="onSearchRow(x.space)"
					style="color: #aaa; margin-bottom: 0.5rem;">
					<van-col span="20">{{ x.text }}</van-col>
					<van-col span="4" style="text-align: right;">{{ x.floor }}</van-col>
				</van-row>
			</div>
		</van-popup>
	</div>
</template>

<script>
import './floor.css';
import $ from 'jquery';
import SvgIcon from './svgIcon.vue'
import Data from './pin'

export default {
	data() {
		return {
			level_show: '',
			search_value: '',
			show_popup: false,
			show_search_popup: false,
			level_list: Data,
			popup_title: '',
			popup_content: '',
			search_list: [
				{
					title: '1-客房',
					list: [
						{
							space: 1.01,
							text: '101~110室',
							floor: 'L1',
							show: true,
						},
						{
							space: 1.02,
							text: '111~119室',
							floor: 'L1',
							show: true,
						},
					],
					show: true
				},
				{
					title: '2-客房',
					list: [
						{
							space: 2.01,
							text: '201~210室',
							floor: 'L2',
							show: true,
						},
						{
							space: 2.02,
							text: '211~219室',
							floor: 'L2',
							show: true,
						},
					],
					show: true
				},
			]
		}
	},
	async mounted() {
	},
	methods: {
		clearPinShow() {
			$('.level__pins').children('.pin').each((index, ele) => {
				$(ele).removeClass('pin--active');
			})
		},
		onFloorClick(level) {
			this.level_show = level;
			$('.mall').addClass('mall--content-open');
			$('.levels').addClass('levels--open').addClass(`levels--selected-${level}`);
			$(`.level--${level}`).addClass('level--current');
			$(`.level--${level} .level__pins`).addClass('level__pins--active');
		},
		onCloseLevel() {
			this.show_popup = false;
			let level = this.level_show;
			$('.mall').removeClass('mall--content-open');
			$('.levels').removeClass('levels--open').removeClass(`levels--selected-${level}`);
			$(`.level--${level}`).removeClass('level--current');
			$(`.level--${level} .level__pins`).removeClass('level__pins--active');
			this.level_show = '';
			this.clearPinShow();
		},
		onClose() {
			this.onCloseLevel();
			this.$emit('close');
		},
		onClickCloseIcon() {
			this.clearPinShow();
		},
		switchFloor(type) { // 切换楼层
			let level = this.level_show;
			if (type === 'right') {
				level = level - 1 < 1 ? 1 : level - 1;
			}
			if (type === 'left') {
				level = level + 1 > 4 ? 4 : level + 1;
			}
			this.onCloseLevel();
			this.onFloorClick(level);
		},
		onSearch() {
			this.show_search_popup = true;
		},
		onSearchRow(val) {
			// 清空图层显示
			this.onCloseLevel();
			this.show_popup = false;
			// 测试锚点
			let currentTarget = null;
			let category = null;
			$('.level__pins').children('.pin').each((index, ele) => {
				let space = $(ele).data('space'); // 锚点标记
				if (space === val) { // 匹配符合的dom节点
					category = parseInt(space);// 获取图层标记
					currentTarget = ele;
				}
				$(ele).removeClass('pin--active');
			});
			// 选择图层
			this.onFloorClick(category);
			// 选择锚点样式
			$(currentTarget).addClass('pin--active');
			setTimeout(() => {
				// 打开锚点详情
				this.show_popup = true;
			}, 500);
			// 关闭搜索弹框
			this.show_search_popup = false;
		},
		onClosePopup() {
			this.show_popup = false;
			this.clearPinShow();
		},
		clickPin (item, evt) { // 点击pin操作
			this.clearPinShow();
			$(evt.target).parents('a.pin').addClass('pin--active');
			this.show_popup = true;
			// 打开 pin 详情信息
			this.popup_title = item?.affix?.title;
			this.popup_content = item?.affix?.content;
		},
		onSearchUpdate () {
			this.search_list.forEach((item) => {
				item.list.forEach((x) => {
					if (x.text.indexOf(this.search_value) >= 0) {
						x.show = true;
					} else {
						x.show = false;
					}
				});
				// 如果子项里面都为空,隐藏整个项
				let show_num = item.list.filter((x) => x.show === true);
				if (show_num.length) {
					item.show = true;
				} else {
					item.show = false;
				}
			});
		}
	}
}
</script>

<style lang="less" scoped>
.test {
	opacity: 0;
}

.search_box {
	padding: 1rem;

	.search_box_title {
		color: #000;
		margin-bottom: 1rem;
		font-size: 1.15rem;
	}
}

.tool-bar {
	position: relative;

	.icon {
		position: absolute;
		z-index: 100;
		display: -webkit-flex;
		display: flex;
		-webkit-align-items: center;
		align-items: center;
		text-align: center;
	}

	.close {
		top: 1rem;
		right: 1rem;
	}

	.search-icon {
		top: 2.75rem;
		right: 1rem;
	}

	.switch-left {
		top: 4.5rem;
		right: 1rem;
	}

	.close-level {
		top: 6rem;
		right: 1rem;
	}

	.switch-right {
		top: 8rem;
		right: 1rem;
	}
}

.level_after {
	font-size: 0.75rem;
	line-height: 0;
	position: absolute;
	z-index: 100;
	top: -2em;
	left: 3.5em;
	white-space: nowrap;
	color: #7d7d86;
	-webkit-transform: rotateZ(45deg) rotateX(-70deg) translateZ(5vmin);
	transform: rotateZ(45deg) rotateX(-70deg) translateZ(5vmin);
	-webkit-transition: -webkit-transform 1s, color 0.3s;
	transition: transform 1s, color 0.3s;
	-webkit-transition-timing-function: cubic-bezier(0.7, 0, 0.3, 1);
	transition-timing-function: cubic-bezier(0.7, 0, 0.3, 1);
}

.level--current {
	.level_after {
		font-size: 1rem;
		-webkit-transform: rotateZ(25deg) rotateX(-60deg) translateZ(15vmin);
		transform: rotateZ(25deg) rotateX(-60deg) translateZ(15vmin);
	}
}
</style>