ElasticSearch_Hotel

This commit is contained in:
Zerroi 2024-04-13 13:54:14 +08:00
commit 39cbd32dad
49 changed files with 24508 additions and 0 deletions

8
.idea/.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

19
.idea/compiler.xml Normal file
View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<annotationProcessing>
<profile default="true" name="Default" enabled="true" />
<profile name="Maven default annotation processors profile" enabled="true">
<sourceOutputDir name="target/generated-sources/annotations" />
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
<outputRelativeToContentRoot value="true" />
<module name="hotel-demo" />
</profile>
</annotationProcessing>
</component>
<component name="JavacSettings">
<option name="ADDITIONAL_OPTIONS_OVERRIDE">
<module name="hotel-demo" options="-parameters" />
</option>
</component>
</project>

19
.idea/dataSources.xml Normal file
View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
<data-source source="LOCAL" name="192.168.249.131" uuid="07907899-228a-437a-ad4a-03689c67d5ba">
<driver-ref>mysql.8</driver-ref>
<synchronize>true</synchronize>
<imported>true</imported>
<remarks>192.168.249.131</remarks>
<jdbc-driver>com.mysql.cj.jdbc.Driver</jdbc-driver>
<jdbc-url>jdbc:mysql://192.168.249.131:3306/hotel_demo?useSSL=false</jdbc-url>
<jdbc-additional-properties>
<property name="com.intellij.clouds.kubernetes.db.host.port" />
<property name="com.intellij.clouds.kubernetes.db.enabled" value="false" />
<property name="com.intellij.clouds.kubernetes.db.container.port" />
</jdbc-additional-properties>
<working-dir>$ProjectFileDir$</working-dir>
</data-source>
</component>
</project>

7
.idea/encodings.xml Normal file
View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding" defaultCharsetForPropertiesFiles="UTF-8">
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
<file url="PROJECT" charset="UTF-8" />
</component>
</project>

20
.idea/jarRepositories.xml Normal file
View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RemoteRepositoriesConfiguration">
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Maven Central repository" />
<option name="url" value="https://repo1.maven.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="jboss.community" />
<option name="name" value="JBoss Community repository" />
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
</remote-repository>
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Central Repository" />
<option name="url" value="https://maven.aliyun.com/repository/public" />
</remote-repository>
</component>
</project>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JavaScriptLibraryMappings">
<file url="file://$PROJECT_DIR$" libraries="{demo-center}" />
</component>
</project>

12
.idea/misc.xml Normal file
View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="MavenProjectsManager">
<option name="originalFiles">
<list>
<option value="$PROJECT_DIR$/pom.xml" />
</list>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK" />
</project>

6
.idea/vcs.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

85
pom.xml Normal file
View File

@ -0,0 +1,85 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.10.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>cn.itcast.demo</groupId>
<artifactId>hotel-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>hotel-demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<elasticsearch.version>7.12.1</elasticsearch.version>
</properties>
<dependencies>
<!--elasticsearch-->
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--FastJson-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.71</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,19 @@
package cn.itcast.hotel;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
@MapperScan("cn.itcast.hotel.mapper")
@SpringBootApplication
public class HotelDemoApplication {
public static void main(String[] args) {
SpringApplication.run(HotelDemoApplication.class, args);
}
}

View File

@ -0,0 +1,50 @@
package cn.itcast.hotel.constants;
public class HotelIndexConstants {
public static final String MAPPING_TEMPLATE =
"{\n" +
" \"mappings\": {\n" +
" \"properties\": {\n" +
" \"id\": {\n" +
" \"type\": \"keyword\"\n" +
" },\n" +
" \"name\": {\n" +
" \"type\": \"text\", \n" +
" \"analyzer\": \"ik_max_word\",\n" +
" \"copy_to\": \"all\"\n" +
" },\n" +
" \"address\": {\n" +
" \"type\": \"keyword\",\n" +
" \"index\": false\n" +
" },\n" +
" \"price\": {\n" +
" \"type\": \"integer\"\n" +
" },\n" +
" \"score\": {\n" +
" \"type\": \"integer\"\n" +
" },\n" +
" \"brand\": {\n" +
" \"type\": \"keyword\",\n" +
" \"copy_to\": \"all\"\n" +
" },\n" +
" \"city\": {\n" +
" \"type\": \"keyword\"\n" +
" },\n" +
" \"startName\": {\n" +
" \"type\": \"keyword\"\n" +
" },\n" +
" \"business\": {\n" +
" \"type\": \"keyword\",\n" +
" \"copy_to\": \"all\"\n" +
" },\n" +
" \"location\": {\n" +
" \"type\": \"geo_point\"\n" +
" },\n" +
" \"pic\": {\n" +
" \"type\": \"keyword\",\n" +
" \"index\": false\n" +
" }\n" +
" }\n" +
" }\n" +
"}";
}

View File

@ -0,0 +1,7 @@
package cn.itcast.hotel.mapper;
import cn.itcast.hotel.pojo.Hotel;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
public interface HotelMapper extends BaseMapper<Hotel> {
}

View File

@ -0,0 +1,24 @@
package cn.itcast.hotel.pojo;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
@Data
@TableName("tb_hotel")
public class Hotel {
@TableId(type = IdType.INPUT)
private Long id;
private String name;
private String address;
private Integer price;
private Integer score;
private String brand;
private String city;
private String starName;
private String business;
private String longitude;
private String latitude;
private String pic;
}

View File

@ -0,0 +1,34 @@
package cn.itcast.hotel.pojo;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
public class HotelDoc {
private Long id;
private String name;
private String address;
private Integer price;
private Integer score;
private String brand;
private String city;
private String starName;
private String business;
private String location;
private String pic;
public HotelDoc(Hotel hotel) {
this.id = hotel.getId();
this.name = hotel.getName();
this.address = hotel.getAddress();
this.price = hotel.getPrice();
this.score = hotel.getScore();
this.brand = hotel.getBrand();
this.city = hotel.getCity();
this.starName = hotel.getStarName();
this.business = hotel.getBusiness();
this.location = hotel.getLatitude() + ", " + hotel.getLongitude();
this.pic = hotel.getPic();
}
}

View File

@ -0,0 +1,7 @@
package cn.itcast.hotel.service;
import cn.itcast.hotel.pojo.Hotel;
import com.baomidou.mybatisplus.extension.service.IService;
public interface IHotelService extends IService<Hotel> {
}

View File

@ -0,0 +1,11 @@
package cn.itcast.hotel.service.impl;
import cn.itcast.hotel.mapper.HotelMapper;
import cn.itcast.hotel.pojo.Hotel;
import cn.itcast.hotel.service.IHotelService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
@Service
public class HotelService extends ServiceImpl<HotelMapper, Hotel> implements IHotelService {
}

View File

@ -0,0 +1,17 @@
server:
port: 8089
spring:
datasource:
url: jdbc:mysql://192.168.249.131:3306/hotel_demo?useSSL=false
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
logging:
level:
cn.itcast: debug
pattern:
dateformat: HH:mm:ss:SSS
mybatis-plus:
configuration:
map-underscore-to-camel-case: true
type-aliases-package: cn.itcast.hotel.pojo

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<appender name="consoleLog" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>
%d{MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n
</pattern>
</encoder>
</appender>
<root level="info">
<appender-ref ref="consoleLog" />
</root>
</configuration>

View File

@ -0,0 +1,96 @@
#toggle-button{
display: none;
}
.button-label{
position: relative;
display: inline-block;
width: 68px;
background-color: #ccc;
border: #ccc;
border-radius: 30px;
cursor: pointer;
box-shadow:inset 0 0 3px 1px rgba(0, 0, 0, 0.4);
}
.circle{
position: absolute;
top: 0;
left: 0;
width: 20px;
height: 20px;
border-radius: 50%;
background-color: #fff;
box-shadow: 0 0 3px 1px rgba(0, 0, 0, 0.5);
}
.button-label .text {
line-height: 20px;
font-size: 12px;
/*
用来阻止页面文字被选中出现蓝色
可以将下面两行代码注释掉来查看区别
*/
-webkit-user-select: none;
user-select: none;
}
.on {
color: #fff;
display: none;
text-indent: 0;
}
.off {
color: #fff;
display: inline-block;
text-indent: 0;
}
.button-label .circle{
left: 0;
transition: all 0.3s;/*transition过度时间为0.3秒*/
}
/*
以下是checked被选中后紧跟checked标签后面label的样式
例如div+p 选择所有紧接着<div>元素之后的<p>元素
*/
#toggle-button:checked + label.button-label .circle{
left: 49px;
}
#toggle-button:checked + label.button-label .on{
display: inline-block;
}
#toggle-button:checked + label.button-label .off{
display: none;
}
#toggle-button:checked + label.button-label{
background-color: #FF8800;
}
.banner{
width: 100%;
display: flex;
justify-content: space-between;
padding-top: 10px;
}
.logo {
display: flex;
justify-items: left;
}
.logo img{
width: 40px;
}
.logo span {
width: 80px;
}
.logo-ch {
line-height: 26px;
font-size: 14px;
color: #333;
font-weight: bold;
font-family: "Helvetica Neue", "Arial", "PingFang SC", "Microsoft Yahei", "SimSun", sans-serif;
}
.logo-en {
line-height: 14px;
font-size: 8px;
}

View File

@ -0,0 +1,372 @@
.search-bar{
display: flex;
flex-direction: column;
align-items: center;
}
.input-box{
display: flex;
width: 35%;
}
.search-bar input {
border: 1px solid #F80;
width: 80%;
height: 28px;
line-height: 28px;
vertical-align: top;
border-radius: .25rem 0 0 .25rem;
}
.search-bar button {
width: 20%;
border: 1px solid #F80;
background-color: #F80;
color: #fff;
line-height: 28px;
margin-left: -6px;
border-radius: 0 .25rem .25rem 0;
height: 28px;
}
#complete-box{
margin-top: 28px;
position: absolute;
z-index: 99;
text-align: left;
border: 1px solid #f1f1f2;
width: 336px;
height: 120px;
background-color: #fff;
}
#complete-box div{
padding-left: 7px;
}
.btn {
height: 34px;
line-height: 34px;
padding: 0 12px;
font-size: 16px;
font-family: "Arial", "PingFang SC", "Microsoft Yahei", "SimSun", sans-serif;
color: #FFF;
background: #F80;
border-color: #F80;
vertical-align: top;
text-align: center;
display: inline-block;
box-sizing: content-box;
cursor: pointer;
border-radius: 3px;
}
em {
color: red;
font-style: normal;
}
.selected {
color: red;
}
.filter-list {
padding: 5px 0;
background: #fff;
border-radius: 3px;
box-shadow: 0 1px 3px 1px rgba(0, 0, 0, 0.2);
}
.filter-box {
display: flex;
align-content: center;
position: relative;
line-height: 24px;
}
.f-key {
font-size: 12px;
color: #666;
width: 10%;
text-align: center;
margin: auto;
line-height: 100%;
}
.column-divider{
width: 2px;
border-radius: 1px;
box-shadow: 1px 0 0 rgba(0,0,0,.2) inset,-1px 0 0 rgba(255,255,255,.2) inset;
}
.row-divider{
margin: auto;
width: 98%;
border-radius: 1px;
height: 3px;
box-shadow:0 1px 0 rgba(0,0,0,.2) inset,0 -1px 0 rgba(255,255,255,.2) inset;
}
a {
text-decoration: none;
color: #999;
}
a:hover {
color: #F80;
}
.f-items {
width: 85%;
display: flex;
flex-wrap: wrap;
align-content: center;
}
.f-item {
width: 80px;
line-height: 30px;
font-size: 12px;
}
.btn-arrow{
border-radius: 3px;
}
.btn-arrow,
.btn-arrow:visited,
.btn-arrow:link,
.btn-arrow:active {
width: 46px;
height: 23px;
border: 1px solid #DDD;
background: #FFF;
line-height: 23px;
font-family: "\5b8b\4f53";
text-align: center;
font-size: 16px;
color: #AAA;
text-decoration: none;
out-line: none
}
.btn-arrow:hover {
background-color: #FF8800;
color: whitesmoke;
}
.sort-item {
display: inline;
width: 50px;
float: left;
font-size: 13px;
}
.selected-ops {
display: flex;
align-items: center;
}
.open {
font-size: 12px;
margin-left: 10px;
line-height: 24px;
margin-bottom: 3px;
}
.selected-op {
border: 1px solid #eee;
border-radius: 3px;
font-size: 12px;
margin-left: 10px;
line-height: 16px;
background: #fff;
padding: 0px 5px 1px;
}
.selected-op:hover {
box-shadow: 1px 1px 2px 1px rgba(0, 0, 0, 0.1);
}
.selected-op span {
color: red;
cursor: pointer;
}
.close {
margin-left: 8px;
font-size: 16px;
font-weight: 800;
}
.top-ban {
margin-top: 20px;
display: flex;
justify-content: space-between;
}
.top-pagination {
padding: 3px 15px;
font-size: 11px;
font-weight: 700;
line-height: 18px;
color: #999;
text-shadow: 0 1px 0 rgba(255, 255, 255, .5);
text-transform: uppercase;
}
.top-pagination span {
margin-right: 10px;
}
body {
background-color: #fcfcfc;
}
#app {
width: 100%;
display: flex;
justify-content: center;
}
.star-name {
width: 50px;
text-align: center;
font: 12px/1.5 tahoma, arial, 'pingfang sc', 'Hiragino Sans GB', \5b8b\4f53, sans-serif;
background-color: #fe7a6b;
color: #fff;
margin-bottom: 10px;
border-radius: 3px;
padding: 2px 5px;
zoom: 1;
}
.hotel-info {
text-align: left;
width: 50%;
display: flex;
flex-direction: column;
justify-content: space-around;
}
#hotel-price {
font-weight: bold;
font-size: 24px;
color: #f60;
padding-right: 2px;
font-family: 'Helvetica Neue', 'Arial', 'PingFang SC', 'Microsoft Yahei', 'SimSun', sans-serif;
}
.hotel-name {
font-size: 18px;
color: #333;
font-weight: bold;
font-family: "Helvetica Neue", "Arial", "PingFang SC", "Microsoft Yahei", "SimSun", sans-serif;
line-height: 26px;
}
.hotel-score {
font-size: 14px;
color: #365873;
font-weight: 700;
font: arial, 'pingfang sc', 'Hiragino Sans GB', \5b8b\4f53, sans-serif
}
.hotel-list {
display: flex;
flex-direction: column;
width: 640px;
height: 100%;
}
.hotel-list span{
font-size: 12px;font: arial,'pingfang sc','Hiragino Sans GB',\5b8b\4f53,sans-serif
}
.order {
color: #2bba9e; cursor: default; font: 12px/1.5 tahoma,arial,'pingfang sc','Hiragino Sans GB',\5b8b\4f53,sans-serif;
}
.address {
margin-bottom: 10px;font: 12px/1.5 tahoma,arial,'pingfang sc','Hiragino Sans GB',\5b8b\4f53,sans-serif;
}
.hotel-box {
padding: 20px;
margin-bottom: 10px;
position: relative;
background: #fff;
border-radius: 5px;
box-shadow: 0 1px 3px 1px rgba(0, 0, 0, 0.2);
}
.hotel-box:hover {
box-shadow: 0 1px 3px 1px rgba(245, 131, 8, 0.3);
}
.fixed-map {
position: fixed;
top: 0;
}
.map-box {
background-color: #fff;
color: #666;
width: 360px;
height: 400px;
padding: 5px;
border-radius: 5px;
box-shadow: 0 1px 3px 1px rgba(0, 0, 0, 0.2);
}
.map-head {
line-height: 35px;
}
.amap {
width: 100%;
height: 350px;
}
.marker-label{
font-size: 12px;
}
.marker {
position: absolute;
top: -25px;
right: -100px;
color: #fff;
padding: 4px 10px;
box-shadow: 1px 1px 1px rgba(10, 10, 10, .2);
white-space: nowrap;
font-size: 12px;
font-family: "";
background-color: #25A5F7;
border-radius: 3px;
}
em{
color: red;
}
.ad-mark {
position: absolute;
left: 5px;
top: 0;
}
.ad-mark img{
filter: drop-shadow(1px 2px 1px rgba(0,0,0,.3))
}
.map-geo{
position: relative;
bottom: 35px;
left: 310px;
width: 32px;
height: 32px;
border-radius: 50%;
cursor: pointer;
background-image: url(https://a.amap.com/jsapi/static/image/plugin/locate.png);
background-size: 24px;
background-repeat: no-repeat;
background-position: 50%;
background-color: #fff;
box-shadow: 0 0 5px silver;
}
.map-geo img {
height: 24px;
width: 24px;
background-color: #fff;
margin: 4px;
border-radius: 50%;
-webkit-animation: rotate 2s linear infinite;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

View File

@ -0,0 +1,540 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>黑马旅游</title>
<link rel="stylesheet" href="https://a.amap.com/jsapi_demos/static/demo-center/css/demo-center.css"/>
<link rel="stylesheet" href="./css/index.css"/>
<link rel="stylesheet" href="./css/banner.css"/>
</head>
<body>
<div id="app" @click="showOps=false">
<div style="width: 70%;text-align: center;margin: auto;">
<div class="banner">
<div class="logo">
<img src="./img/heima.png" alt="1"/>
<span>
<div class="logo-ch">黑马旅游</div>
<div class="logo-en">www.itheima.com</div>
</span>
</div>
<div >
<input type="checkbox" id="toggle-button" v-model="isTest">
<!--label中的for跟input的id绑定。作用是在点击label时选中input或者取消选中input-->
<label for="toggle-button" class="button-label">
<span class="circle"></span>
<span class="text on">测试</span>
<span class="text off">正式</span>
</label>
</div>
</div>
<!--关键字搜索-->
<div class="search-bar">
<div class="input-box">
<input title="输入关键字搜索酒店" v-model="params.key" type="text" @click.stop="" @focus="showOps=true"
@keyup="handleKeyUp($event)">
<button @click="handleSearch">搜索</button>
</div>
<div id="complete-box" v-show="showOps && ops.length > 0">
<div v-for="(item, i) in ops" :key="i" @click="handleSearch" @mouseover="opsIndex = i"
:style='{"background-color": opsIndex===i ? "#EEE" : "#fff"}'>{{item}}
</div>
</div>
</div>
<!-- 已选择过滤项 -->
<div class="selected-ops">
<div class="open">全部结果:</div>
<div class="selected-op" v-for="(v, k) in params.filters" :key="k" @click="deleteFilter(k)">
{{filterNames[k]}}<span>{{v}} <span class='close'>×</span></span>
</div>
</div>
<!--过滤项-->
<div class="filter-list">
<div v-for="(v, k) in remainFilter" :key="k">
<div class="filter-box">
<div class="f-key"><strong>{{filterNames[k]}}</strong></div>
<div class="column-divider"></div>
<div class="f-items">
<div class="f-item" @click="clickFilter(k, o)" v-for="(o, j) in v" :key="j"><a href="javascript:void(0)">
{{o}}</a></div>
</div>
</div>
<div class="row-divider"></div>
</div>
<div class="filter-box">
<div class="f-key"><strong>价格</strong></div>
<div class="column-divider"></div>
<div class="f-items">
<div class="f-item" @click="clickFilter('price','0-100')"><a href="javascript:void(0)">100元以下</a></div>
<div class="f-item" @click="clickFilter('price','100-300')"><a href="javascript:void(0)">100-300元</a></div>
<div class="f-item" @click="clickFilter('price','300-600')"><a href="javascript:void(0)">300-600元</a></div>
<div class="f-item" @click="clickFilter('price','600-1500')"><a href="javascript:void(0)">600-1500元</a></div>
<div class="f-item" @click="clickFilter('price','1500-0')"><a href="javascript:void(0)">1500元以上</a></div>
</div>
</div>
</div>
<!-- 排序 -->
<div class="top-ban">
<!--排序条件-->
<div class="sort-items">
<div class="sort-item" v-for="(item, i) in sortItems" :key="i">
<a :class="{selected: params.sortBy===item.key}" @click="params.sortBy=item.key"
href="javascript:void(0)">{{item.text}}</a>
|
</div>
</div>
<!--分页条-->
<div class="top-pagination">
<span><i style="color: #222;">{{total}}</i> 家酒店</span>
<span><i style="color: red;">{{params.page}}</i>/{{totalPage}}</span>
<a class="btn-arrow" href="#" style="display: inline-block" @click="prePage">&lt;</a>
<a class="btn-arrow" href="#" style="display: inline-block" @click="nextPage">&gt;</a>
</div>
</div>
<div class="row-divider" style="margin-bottom: 5px; width: 100%"></div>
<!--酒店列表-->
<div style="display: flex; justify-content: space-between;">
<div class="hotel-list">
<div class="hotel-box" v-for="(hotel,i) in hotels" :key="i"
style="display: flex;justify-content: space-between;" @mouseover="handleMarkerFocus(hotel)">
<div class="ad-mark" v-if="hotel.isAD">
<img src="./img/ad.png" width="25"/>
</div>
<div style="width: 0;"></div>
<div style="width: 25%"><img width="100%" :src="hotel.pic"></div>
<div class="hotel-info">
<div class="hotel-name" v-html="hotel.name">
</div>
<div class="star-name"> {{hotel.starName}}</div>
<div class="address">
位于 <span style="color: #BC8516;">{{hotel.business}}</span> 周边,{{hotel.address}}
</div>
<div class="order"> 1分钟前有人预订了该酒店</div>
<div v-if="hotel.distance" class="address">距离您 {{hotel.distance.toFixed(2)}} km</div>
</div>
<div style="text-align: left; width: 15%;">
<div>
<b style="color: #f60;"></b> <span id='hotel-price'>{{hotel.price}}</span> <span
style="font-size: 0.2em; color: #999;">起</span>
</div>
<div class='btn'>立即预定</div>
<div>
<span class="hotel-score">{{hotel.score / 10}}分</span> /<span>5分</span>
</div>
</div>
</div>
</div>
<div :class='{"map-box": true, "fixed-map": isFixed}' :style="{left: ml + 'px'}">
<div class="map-head">地图预览</div>
<div class="amap" id="container"></div>
<div class="map-geo" @click="getGeoLoc">
<img src="https://a.amap.com/jsapi/static/image/plugin/waite.png" v-show="loading">
</div>
</div>
</div>
</div>
</div>
<script src="./js/vue.js"></script>
<script src="./js/axios.min.js"></script>
<script src="./js/amap.min.js"></script>
<script type="text/javascript" src="https://webapi.amap.com/maps?v=2.0&key=ddd292c88aa1bad9c04891a47724f40a"></script>
<script>
// 设置后台服务地址
axios.defaults.baseURL = "http://localhost:8089";
axios.defaults.timeout = 3000;
let app = new Vue({
el: "#app",
data: {
isTest: false,
filterNames: {
brand: "品牌",
city: "城市",
starName: "星级",
price: "价格",
},
filterList: {},// 过滤项的假数据
sortItems: [
{
key: "default",
text: "默认",
},
{
key: "score",
text: "评价",
},
{
key: "price",
text: "价格",
}
],// 排序字段的假数据
hotels: [],// 酒店数据
total: 0, // 总条数
totalPage: 0, // 总页数
params: {
key: "", // 搜索关键字
page: 1, // 当前页码
size: 5, // 每页大小
sortBy: "default",// 排序字段
filters: {}, // 过滤字段
},
map: {},// 地图对象
loc: "", // 地图标记
ml: 0, // 控制地图位置
geolocation:{}, // 定位系统
loading: false, // 是否在定位
currentHotel: {},
ops: [],
showOps: false,
opsIndex: -1,
isFixed: false,
testFilterData: {
"city": ["上海", "北京", "深圳", "杭州"],
"starName": ["四星","五星","二钻","三钻","四钻","五钻"],
"brand": ["7天酒店", "如家","速8", "皇冠假日","华美达","万怡","喜来登","万豪","和颐","希尔顿"],
},
},
watch: {
"params.sortBy"() {
// 每当page改变当前代码就会执行。搜索一下
this.search(this.loc);
},
"params.page"() {
// 每当page改变当前代码就会执行。搜索一下
this.search(this.loc);
},
"params.filters": {
deep: true,
handler() {
this.search();
// 获取过滤项
this.getFilter();
}
},
opsIndex() {
if (this.opsIndex !== -1) {
this.params.key = this.ops[this.opsIndex]
}
}
},
created() {
// 页面加载时,先搜索一下
this.search();
// 获取过滤项
this.getFilter();
},
mounted() {
//初始化地图
this.map = new AMap.Map('container', {
resizeEnable: true, //是否监控地图容器尺寸变化
zoom: 11, //初始地图级别
center: [120.0, 32.0], //初始地图中心点
});
// 初始化定位系统
AMap.plugin('AMap.Geolocation', () => {
this.geolocation = new AMap.Geolocation({
position: 'RT', //定位按钮的停靠位置
buttonOffset: new AMap.Pixel(10, 20),//定位按钮与设置的停靠位置的偏移量默认Pixel(10, 20)
zoomToAccuracy: true, //定位成功后是否自动调整地图视野到定位点
});
// AMap.Event.addListener(geolocation, 'complete', result => {
// if (this.params.page !== 1) {
// this.params.page = 1;
// this.loc = result.position.lat + ", " + result.position.lng;
// } else {
// this.searchByMap(result.position.lat + ", " + result.position.lng);
// }
//
// });//返回定位信息
// AMap.Event.addListener(geolocation, 'error', err => console.log(err)); //返回定位出错信息
});
let oDiv = document.querySelector(".map-box"),
H = 0,
Y = oDiv;
while (Y) {
H += Y.offsetTop;
Y = Y.offsetParent;
}
window.addEventListener('scroll', () => this.handleScroll(H, oDiv));
},
methods: {
handleScroll(h, o) {
let s = document.body.scrollTop || document.documentElement.scrollTop
this.isFixed = s > h;
this.ml = o.offsetLeft;
},
handleKeyUp(e) {
if ((e.keyCode >= 48 && e.keyCode <= 57) || (e.keyCode >= 65 && e.keyCode <= 90) || e.keyCode === 8) {
// 用户输入的字符,需要自动补全
this.getSuggestion();
} else if (e.keyCode === 13) {
// 用户按回车,需要搜索
this.search();
this.getFilter();
} else if (e.keyCode === 38) {
if (this.opsIndex > 0) {
this.opsIndex--;
} else {
this.opsIndex = this.ops.length - 1;
}
} else if (e.keyCode === 40) {
this.opsIndex = (this.opsIndex + 1) % this.ops.length;
} else if (e.keyCode === 27) {
this.show = false
}
},
handleSearch(){
this.search();
this.getFilter();
},
getSuggestion() { // 查询自动补全
if (!this.params.key) {
// key没有值不去搜索了
this.ops = [];
return;
}
axios.get("/hotel/suggestion?key=" + this.params.key)
.then(resp => {
this.ops = resp.data;
})
.catch(e => {
if(this.isTest){
this.ops = ["万豪", "如家", "喜来登"]
}else{
this.ops = []
}
console.log(e);
})
},
getFilter() {
if(this.isTest){
this.filterList = this.testFilterData;
return;
}
// 准备参数
const {filters: {price: ps, ... fs}, ...params} = this.params;
for( _k in fs){
params[_k] = fs[_k];
}
// 处理价格
if(ps){
let pArr = ps.split("-");
params.minPrice = parseInt(pArr[0]);
let max = parseInt(pArr[1]);
params.maxPrice = max === 0 ? 999999 : max;
}
axios.post("/hotel/filters", params)
.then(resp => {
this.filterList = resp.data;
})
.catch(err => {
console.log(err);
this.filterList = this.testFilterData;
})
},
searchByMap(location) {
// 准备参数
const {filters: {price: ps, ... fs}, ...params} = this.params;
for( _k in fs){
params[_k] = fs[_k];
}
// 处理价格
if(ps){
let pArr = ps.split("-");
params.minPrice = parseInt(pArr[0]);
let max = parseInt(pArr[1]);
params.maxPrice = max === 0 ? 999999 : max;
}
// 准备地址
if (location) {
params.location = location;
this.loc = location;
}
axios.post("/hotel/list", params)
.then(resp => {
if(!resp.data.hotels){
this.hotels = resp.data;
this.total = 271;
this.totalPage = 28;
}else{
this.hotels = resp.data.hotels;
this.total = resp.data.total;
this.totalPage = Math.ceil((this.total + 5 - 1) / 5);
}
if (location) {
this.setMapCenter(location);
} else {
this.setMapCenter(this.hotels[0].location);
}
this.initMarker();
})
.catch(err => {
console.log(err)
this.hotels = [{"id":60223,"name":"上海希尔顿酒店","address":"静安华山路250号","price":2688,"score":37,"brand":"希尔顿","city":"上海","starName":"五星级","business":"静安寺地区","location":"31.219306, 121.445427","pic":"https://m.tuniucdn.com/filebroker/cdn/res/92/10/9210e74442aceceaf6e196d61fc3b6b1_w200_h200_c1_t0.jpg"},{"id":60922,"name":"上海虹桥祥源希尔顿酒店","address":"红松东路1116号","price":1108,"score":45,"brand":"希尔顿","city":"上海","starName":"五钻","business":"虹桥地区","location":"31.18746, 121.395312","pic":"https://m.tuniucdn.com/fb3/s1/2n9c/tQRqDTFkHnHzMZiDKjcGV81ekvc_w200_h200_c1_t0.jpg"},{"id":309208,"name":"北京王府井希尔顿酒店","address":"王府井东街8号","price":1679,"score":46,"brand":"希尔顿","city":"北京","starName":"五钻","business":"天安门/王府井地区","location":"39.914539, 116.413392","pic":"https://m.tuniucdn.com/fb2/t1/G6/M00/52/10/Cii-TF3ePt2IX9UEAALb6VYBSmoAAGKMgGsuW8AAtwB147_w200_h200_c1_t0.jpg"},{"id":395434,"name":"北京希尔顿酒店","address":"东三环北路东方路1号","price":350,"score":45,"brand":"希尔顿","city":"北京","starName":"五星级","business":"燕莎/朝阳公园商业区","location":"39.952703, 116.462387","pic":"https://m.tuniucdn.com/fb3/s1/2n9c/3fwNbKGhk6XCrkdVyxwhC5uGpLVy_w200_h200_c1_t0.jpg"},{"id":395702,"name":"北京首都机场希尔顿酒店","address":"首都机场3号航站楼三经路1号","price":222,"score":46,"brand":"希尔顿","city":"北京","starName":"五钻","business":"首都机场/新国展地区","location":"40.048969, 116.619566","pic":"https://m.tuniucdn.com/fb2/t1/G6/M00/52/10/Cii-U13ePtuIMRSjAAFZ58NGQrMAAGKMgADZ1QAAVn_167_w200_h200_c1_t0.jpg"},{"id":615175,"name":"千岛湖滨江希尔顿度假酒店","address":"环湖北路600号","price":1265,"score":47,"brand":"希尔顿","city":"杭州","starName":"五钻","business":"千岛湖镇","location":"29.603634, 119.077596","pic":"https://m.tuniucdn.com/fb3/s1/2n9c/6qzYeUrrXsH5H3cd9bMXLz8MJtT_w200_h200_c1_t0.jpg"},{"id":2351601,"name":"深圳蛇口希尔顿南海酒店","address":"望海路1177号","price":509,"score":47,"brand":"希尔顿","city":"深圳","starName":"五钻","business":"深圳湾口岸/蛇口","location":"22.479373, 113.916013","pic":"https://m.tuniucdn.com/fb2/t1/G6/M00/45/EA/Cii-TF3ZpXOIfa6fAAJjiUOiuYgAAFrtgDtgpQAAmOh799_w200_h200_c1_t0.jpg"},{"id":368701368,"name":"深圳大中华希尔顿酒店","address":"福田深南大道1003号","price":1666,"score":46,"brand":"希尔顿","city":"深圳","starName":"五钻","business":"会展中心/CBD","location":"22.539313, 114.069763","pic":"https://m.tuniucdn.com/fb3/s1/2n9c/4EnHseZ73LXdFJY7DSdJ8xqAcjXe_w200_h200_c1_t0.jpg"},{"id":2048042240,"name":"北京大兴希尔顿酒店","address":"高米店南里18号楼","price":1283,"score":48,"brand":"希尔顿","city":"北京","starName":"五钻","business":"大兴北京新机场地区","location":"39.76875, 116.339199","pic":"https://m.tuniucdn.com/fb3/s1/2n9c/3B32F8zSU2CJCWzs1hoH2o4WcquR_w200_h200_c1_t0.jpg"},{"id":2056105938,"name":"北京通州北投希尔顿酒店","address":"新华东街289号2号楼","price":1068,"score":48,"brand":"希尔顿","city":"北京","starName":"五钻","business":"果园环岛/通州区","location":"39.908805, 116.659748","pic":"https://m.tuniucdn.com/fb3/s1/2n9c/NGKdpec3tZJNUUNWJ5pd67Cp5AY_w200_h200_c1_t0.png"}]
this.total = 271;
this.totalPage = 28;
})
},
search(location) {
// 发送ajax
this.searchByMap(location);
},
prePage() {
if (this.params.page > 1) {
this.params.page--
}
},
nextPage() {
if (this.params.page < this.totalPage) {
this.params.page++
}
},
clickPrice(min, max) {
this.params.minPrice = min;
this.params.maxPrice = max;
},
clickFilter(key, option) {
this.loc = "";
const {...obj} = this.params.filters;
obj[key] = option;
this.params.filters = obj;
},
deleteFilter(k) {
this.loc = "";
const {...obj} = this.params.filters;
delete obj[k];
this.params.filters = obj;
},
location(loc) {
let arr = loc.split(", ");
let json = '[' + arr[1] + ', ' + arr[0] + ']'
return JSON.parse(json);
},
initMarker() {
if (!this.loc) {
this.map.clearMap();
}
this.markHotels();
},
setMapCenter(location){
this.map.setCenter(this.location(location));
},
markHotels(){
this.hotels.forEach((h, i) => {
// 将创建的点标记添加到已有的地图实例:
let marker = new AMap.Marker({
position: this.location(h.location), // 经纬度构成的一维数组[116.39, 39.9]
title: h.name,
offset: new AMap.Pixel(0, 0),
anchor:'bottom-center'
});
marker.vid = h.id;
this.map.add(marker);
});
},
handleMarkerFocus(h) {
this.map.setCenter(this.location(h.location));
let old = this.currentHotel;
let oldMarker = this.map.getAllOverlays("marker").find(m => m.vid === old.id);
if (oldMarker) {
this.updateMarker(oldMarker, "//a.amap.com/jsapi_demos/static/demo-center/icons/poi-marker-default.png", "", this.location(old.location), old.id);
}
this.currentHotel = h;
let marker = this.map.getAllOverlays("marker").find(m => m.vid === h.id);
this.updateMarker(marker, "//a.amap.com/jsapi_demos/static/demo-center/icons/poi-marker-red.png", h.name, this.location(h.location), h.id);
},
updateMarker(marker, icon, text, position, id) {
// 自定义点标记内容
var markerContent = document.createElement("div");
// 点标记中的图标
var markerImg = document.createElement("img");
markerImg.className = "markerlnglat";
markerImg.src = icon;
markerImg.setAttribute('width', '25px');
markerImg.setAttribute('height', '34px');
markerContent.appendChild(markerImg);
// 点标记中的文本
if (text) {
var markerSpan = document.createElement("span");
markerSpan.className = 'marker';
markerSpan.innerHTML = text;
markerContent.appendChild(markerSpan);
}
marker.setContent(markerContent);
marker.setPosition(position);
marker.vid = id;
},
getGeoLoc(){
this.loading = true;
this.geolocation.getCurrentPosition((status,result) => {
this.loading = false;
if(status === 'complete'){
console.log("successs")
// https://a.amap.com/jsapi/static/image/plugin/point.png
this.addMaker('//a.amap.com/jsapi/static/image/plugin/point.png', result.position.lng,result.position.lat);
if (this.params.page !== 1) {
this.params.page = 1;
this.loc = result.position.lat + ", " + result.position.lng;
} else {
this.searchByMap(result.position.lat + ", " + result.position.lng);
}
}else{
console.log("err", status)
}
});
},
addMaker(iconUrl, lng, lat){
// 创建 AMap.Icon 实例:
let icon = new AMap.Icon({
size: new AMap.Size(25, 25), // 图标尺寸
image: iconUrl, // Icon的图像
imageOffset: new AMap.Pixel(0, 0), // 图像相对展示区域的偏移量,适于雪碧图等
imageSize: new AMap.Size(25, 25) // 根据所设置的大小拉伸或压缩图片
});
// 将 Icon 实例添加到 marker 上:
let marker = new AMap.Marker({
position: new AMap.LngLat(lng, lat),
offset: new AMap.Pixel(0, 0),
icon: icon, // 添加 Icon 实例
title: '北京',
zoom: 13,
anchor:"center"
});
this.map.add(marker);
}
},
computed: {
remainFilter() {
let keys = Object.keys(this.params.filters);
let obj = {};
Object.keys(this.filterList).forEach(key => {
if (!keys.includes(key) && this.filterList[key].length > 1) {
obj[key] = this.filterList[key];
}
})
return obj;
}
}
})
</script>
</body>
</html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,120 @@
package cn.itcast.hotel;
import cn.itcast.hotel.pojo.Hotel;
import cn.itcast.hotel.pojo.HotelDoc;
import cn.itcast.hotel.service.IHotelService;
import com.alibaba.fastjson.JSON;
import org.apache.http.HttpHost;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.xcontent.XContentType;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.io.IOException;
import java.util.List;
@SpringBootTest
class HotelDocumentTest {
private RestHighLevelClient client;
@Autowired
private IHotelService hotelService;
@Test
void testAddDocument() throws IOException {
// 1.查询数据库hotel数据
Hotel hotel = hotelService.getById(61083L);
// 2.转换为HotelDoc
HotelDoc hotelDoc = new HotelDoc(hotel);
// 3.转JSON
String json = JSON.toJSONString(hotelDoc);
// 1.准备Request
IndexRequest request = new IndexRequest("hotel").id(hotelDoc.getId().toString());
// 2.准备请求参数DSL其实就是文档的JSON字符串
request.source(json, XContentType.JSON);
// 3.发送请求
client.index(request, RequestOptions.DEFAULT);
}
@Test
void testGetDocumentById() throws IOException {
// 1.准备Request // GET /hotel/_doc/{id}
GetRequest request = new GetRequest("hotel", "61083");
// 2.发送请求
GetResponse response = client.get(request, RequestOptions.DEFAULT);
// 3.解析响应结果
String json = response.getSourceAsString();
HotelDoc hotelDoc = JSON.parseObject(json, HotelDoc.class);
System.out.println("hotelDoc = " + hotelDoc);
}
@Test
void testDeleteDocumentById() throws IOException {
// 1.准备Request // DELETE /hotel/_doc/{id}
DeleteRequest request = new DeleteRequest("hotel", "61083");
// 2.发送请求
client.delete(request, RequestOptions.DEFAULT);
}
@Test
void testUpdateById() throws IOException {
// 1.准备Request
UpdateRequest request = new UpdateRequest("hotel", "61083");
// 2.准备参数
request.doc(
"price", "870"
);
// 3.发送请求
client.update(request, RequestOptions.DEFAULT);
}
@Test
void testBulkRequest() throws IOException {
// 查询所有的酒店数据
List<Hotel> list = hotelService.list();
// 1.准备Request
BulkRequest request = new BulkRequest();
// 2.准备参数
for (Hotel hotel : list) {
// 2.1.转为HotelDoc
HotelDoc hotelDoc = new HotelDoc(hotel);
// 2.2.转json
String json = JSON.toJSONString(hotelDoc);
// 2.3.添加请求
request.add(new IndexRequest("hotel").id(hotel.getId().toString()).source(json, XContentType.JSON));
}
// 3.发送请求
client.bulk(request, RequestOptions.DEFAULT);
}
@BeforeEach
void setUp() {
client = new RestHighLevelClient(RestClient.builder(
HttpHost.create("http://192.168.150.101:9200")
));
}
@AfterEach
void tearDown() throws IOException {
client.close();
}
}

View File

@ -0,0 +1,60 @@
package cn.itcast.hotel;
import cn.itcast.hotel.constants.HotelIndexConstants;
import org.apache.http.HttpHost;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.common.xcontent.XContentType;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import java.io.IOException;
@SpringBootTest
class HotelIndexTest {
private RestHighLevelClient client;
@Test
void testCreateHotelIndex() throws IOException {
CreateIndexRequest request = new CreateIndexRequest("hotel");
request.source(HotelIndexConstants.MAPPING_TEMPLATE, XContentType.JSON);
client.indices().create(request, RequestOptions.DEFAULT);
}
@Test
void testDeleteIndex() throws IOException {
DeleteIndexRequest request = new DeleteIndexRequest("hotel");
client.indices().delete(request, RequestOptions.DEFAULT);
}
@Test
void testExistIndex() throws IOException {
GetIndexRequest request = new GetIndexRequest("hotel");
boolean exists = client.indices().exists(request, RequestOptions.DEFAULT);
System.err.println(exists ? "索引库存在" : "索引库不存在");
}
@BeforeEach
void setUp() {
client = new RestHighLevelClient(RestClient.builder(
HttpHost.create("http://192.168.249.131:9200")
));
}
@AfterEach
void tearDown() throws IOException {
client.close();
}
}

View File

@ -0,0 +1,17 @@
server:
port: 8089
spring:
datasource:
url: jdbc:mysql://192.168.249.131:3306/hotel_demo?useSSL=false
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
logging:
level:
cn.itcast: debug
pattern:
dateformat: HH:mm:ss:SSS
mybatis-plus:
configuration:
map-underscore-to-camel-case: true
type-aliases-package: cn.itcast.hotel.pojo

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<appender name="consoleLog" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>
%d{MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n
</pattern>
</encoder>
</appender>
<root level="info">
<appender-ref ref="consoleLog" />
</root>
</configuration>

View File

@ -0,0 +1,96 @@
#toggle-button{
display: none;
}
.button-label{
position: relative;
display: inline-block;
width: 68px;
background-color: #ccc;
border: #ccc;
border-radius: 30px;
cursor: pointer;
box-shadow:inset 0 0 3px 1px rgba(0, 0, 0, 0.4);
}
.circle{
position: absolute;
top: 0;
left: 0;
width: 20px;
height: 20px;
border-radius: 50%;
background-color: #fff;
box-shadow: 0 0 3px 1px rgba(0, 0, 0, 0.5);
}
.button-label .text {
line-height: 20px;
font-size: 12px;
/*
用来阻止页面文字被选中出现蓝色
可以将下面两行代码注释掉来查看区别
*/
-webkit-user-select: none;
user-select: none;
}
.on {
color: #fff;
display: none;
text-indent: 0;
}
.off {
color: #fff;
display: inline-block;
text-indent: 0;
}
.button-label .circle{
left: 0;
transition: all 0.3s;/*transition过度时间为0.3秒*/
}
/*
以下是checked被选中后紧跟checked标签后面label的样式
例如div+p 选择所有紧接着<div>元素之后的<p>元素
*/
#toggle-button:checked + label.button-label .circle{
left: 49px;
}
#toggle-button:checked + label.button-label .on{
display: inline-block;
}
#toggle-button:checked + label.button-label .off{
display: none;
}
#toggle-button:checked + label.button-label{
background-color: #FF8800;
}
.banner{
width: 100%;
display: flex;
justify-content: space-between;
padding-top: 10px;
}
.logo {
display: flex;
justify-items: left;
}
.logo img{
width: 40px;
}
.logo span {
width: 80px;
}
.logo-ch {
line-height: 26px;
font-size: 14px;
color: #333;
font-weight: bold;
font-family: "Helvetica Neue", "Arial", "PingFang SC", "Microsoft Yahei", "SimSun", sans-serif;
}
.logo-en {
line-height: 14px;
font-size: 8px;
}

View File

@ -0,0 +1,372 @@
.search-bar{
display: flex;
flex-direction: column;
align-items: center;
}
.input-box{
display: flex;
width: 35%;
}
.search-bar input {
border: 1px solid #F80;
width: 80%;
height: 28px;
line-height: 28px;
vertical-align: top;
border-radius: .25rem 0 0 .25rem;
}
.search-bar button {
width: 20%;
border: 1px solid #F80;
background-color: #F80;
color: #fff;
line-height: 28px;
margin-left: -6px;
border-radius: 0 .25rem .25rem 0;
height: 28px;
}
#complete-box{
margin-top: 28px;
position: absolute;
z-index: 99;
text-align: left;
border: 1px solid #f1f1f2;
width: 336px;
height: 120px;
background-color: #fff;
}
#complete-box div{
padding-left: 7px;
}
.btn {
height: 34px;
line-height: 34px;
padding: 0 12px;
font-size: 16px;
font-family: "Arial", "PingFang SC", "Microsoft Yahei", "SimSun", sans-serif;
color: #FFF;
background: #F80;
border-color: #F80;
vertical-align: top;
text-align: center;
display: inline-block;
box-sizing: content-box;
cursor: pointer;
border-radius: 3px;
}
em {
color: red;
font-style: normal;
}
.selected {
color: red;
}
.filter-list {
padding: 5px 0;
background: #fff;
border-radius: 3px;
box-shadow: 0 1px 3px 1px rgba(0, 0, 0, 0.2);
}
.filter-box {
display: flex;
align-content: center;
position: relative;
line-height: 24px;
}
.f-key {
font-size: 12px;
color: #666;
width: 10%;
text-align: center;
margin: auto;
line-height: 100%;
}
.column-divider{
width: 2px;
border-radius: 1px;
box-shadow: 1px 0 0 rgba(0,0,0,.2) inset,-1px 0 0 rgba(255,255,255,.2) inset;
}
.row-divider{
margin: auto;
width: 98%;
border-radius: 1px;
height: 3px;
box-shadow:0 1px 0 rgba(0,0,0,.2) inset,0 -1px 0 rgba(255,255,255,.2) inset;
}
a {
text-decoration: none;
color: #999;
}
a:hover {
color: #F80;
}
.f-items {
width: 85%;
display: flex;
flex-wrap: wrap;
align-content: center;
}
.f-item {
width: 80px;
line-height: 30px;
font-size: 12px;
}
.btn-arrow{
border-radius: 3px;
}
.btn-arrow,
.btn-arrow:visited,
.btn-arrow:link,
.btn-arrow:active {
width: 46px;
height: 23px;
border: 1px solid #DDD;
background: #FFF;
line-height: 23px;
font-family: "\5b8b\4f53";
text-align: center;
font-size: 16px;
color: #AAA;
text-decoration: none;
out-line: none
}
.btn-arrow:hover {
background-color: #FF8800;
color: whitesmoke;
}
.sort-item {
display: inline;
width: 50px;
float: left;
font-size: 13px;
}
.selected-ops {
display: flex;
align-items: center;
}
.open {
font-size: 12px;
margin-left: 10px;
line-height: 24px;
margin-bottom: 3px;
}
.selected-op {
border: 1px solid #eee;
border-radius: 3px;
font-size: 12px;
margin-left: 10px;
line-height: 16px;
background: #fff;
padding: 0px 5px 1px;
}
.selected-op:hover {
box-shadow: 1px 1px 2px 1px rgba(0, 0, 0, 0.1);
}
.selected-op span {
color: red;
cursor: pointer;
}
.close {
margin-left: 8px;
font-size: 16px;
font-weight: 800;
}
.top-ban {
margin-top: 20px;
display: flex;
justify-content: space-between;
}
.top-pagination {
padding: 3px 15px;
font-size: 11px;
font-weight: 700;
line-height: 18px;
color: #999;
text-shadow: 0 1px 0 rgba(255, 255, 255, .5);
text-transform: uppercase;
}
.top-pagination span {
margin-right: 10px;
}
body {
background-color: #fcfcfc;
}
#app {
width: 100%;
display: flex;
justify-content: center;
}
.star-name {
width: 50px;
text-align: center;
font: 12px/1.5 tahoma, arial, 'pingfang sc', 'Hiragino Sans GB', \5b8b\4f53, sans-serif;
background-color: #fe7a6b;
color: #fff;
margin-bottom: 10px;
border-radius: 3px;
padding: 2px 5px;
zoom: 1;
}
.hotel-info {
text-align: left;
width: 50%;
display: flex;
flex-direction: column;
justify-content: space-around;
}
#hotel-price {
font-weight: bold;
font-size: 24px;
color: #f60;
padding-right: 2px;
font-family: 'Helvetica Neue', 'Arial', 'PingFang SC', 'Microsoft Yahei', 'SimSun', sans-serif;
}
.hotel-name {
font-size: 18px;
color: #333;
font-weight: bold;
font-family: "Helvetica Neue", "Arial", "PingFang SC", "Microsoft Yahei", "SimSun", sans-serif;
line-height: 26px;
}
.hotel-score {
font-size: 14px;
color: #365873;
font-weight: 700;
font: arial, 'pingfang sc', 'Hiragino Sans GB', \5b8b\4f53, sans-serif
}
.hotel-list {
display: flex;
flex-direction: column;
width: 640px;
height: 100%;
}
.hotel-list span{
font-size: 12px;font: arial,'pingfang sc','Hiragino Sans GB',\5b8b\4f53,sans-serif
}
.order {
color: #2bba9e; cursor: default; font: 12px/1.5 tahoma,arial,'pingfang sc','Hiragino Sans GB',\5b8b\4f53,sans-serif;
}
.address {
margin-bottom: 10px;font: 12px/1.5 tahoma,arial,'pingfang sc','Hiragino Sans GB',\5b8b\4f53,sans-serif;
}
.hotel-box {
padding: 20px;
margin-bottom: 10px;
position: relative;
background: #fff;
border-radius: 5px;
box-shadow: 0 1px 3px 1px rgba(0, 0, 0, 0.2);
}
.hotel-box:hover {
box-shadow: 0 1px 3px 1px rgba(245, 131, 8, 0.3);
}
.fixed-map {
position: fixed;
top: 0;
}
.map-box {
background-color: #fff;
color: #666;
width: 360px;
height: 400px;
padding: 5px;
border-radius: 5px;
box-shadow: 0 1px 3px 1px rgba(0, 0, 0, 0.2);
}
.map-head {
line-height: 35px;
}
.amap {
width: 100%;
height: 350px;
}
.marker-label{
font-size: 12px;
}
.marker {
position: absolute;
top: -25px;
right: -100px;
color: #fff;
padding: 4px 10px;
box-shadow: 1px 1px 1px rgba(10, 10, 10, .2);
white-space: nowrap;
font-size: 12px;
font-family: "";
background-color: #25A5F7;
border-radius: 3px;
}
em{
color: red;
}
.ad-mark {
position: absolute;
left: 5px;
top: 0;
}
.ad-mark img{
filter: drop-shadow(1px 2px 1px rgba(0,0,0,.3))
}
.map-geo{
position: relative;
bottom: 35px;
left: 310px;
width: 32px;
height: 32px;
border-radius: 50%;
cursor: pointer;
background-image: url(https://a.amap.com/jsapi/static/image/plugin/locate.png);
background-size: 24px;
background-repeat: no-repeat;
background-position: 50%;
background-color: #fff;
box-shadow: 0 0 5px silver;
}
.map-geo img {
height: 24px;
width: 24px;
background-color: #fff;
margin: 4px;
border-radius: 50%;
-webkit-animation: rotate 2s linear infinite;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

View File

@ -0,0 +1,540 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>黑马旅游</title>
<link rel="stylesheet" href="https://a.amap.com/jsapi_demos/static/demo-center/css/demo-center.css"/>
<link rel="stylesheet" href="./css/index.css"/>
<link rel="stylesheet" href="./css/banner.css"/>
</head>
<body>
<div id="app" @click="showOps=false">
<div style="width: 70%;text-align: center;margin: auto;">
<div class="banner">
<div class="logo">
<img src="./img/heima.png" alt="1"/>
<span>
<div class="logo-ch">黑马旅游</div>
<div class="logo-en">www.itheima.com</div>
</span>
</div>
<div >
<input type="checkbox" id="toggle-button" v-model="isTest">
<!--label中的for跟input的id绑定。作用是在点击label时选中input或者取消选中input-->
<label for="toggle-button" class="button-label">
<span class="circle"></span>
<span class="text on">测试</span>
<span class="text off">正式</span>
</label>
</div>
</div>
<!--关键字搜索-->
<div class="search-bar">
<div class="input-box">
<input title="输入关键字搜索酒店" v-model="params.key" type="text" @click.stop="" @focus="showOps=true"
@keyup="handleKeyUp($event)">
<button @click="handleSearch">搜索</button>
</div>
<div id="complete-box" v-show="showOps && ops.length > 0">
<div v-for="(item, i) in ops" :key="i" @click="handleSearch" @mouseover="opsIndex = i"
:style='{"background-color": opsIndex===i ? "#EEE" : "#fff"}'>{{item}}
</div>
</div>
</div>
<!-- 已选择过滤项 -->
<div class="selected-ops">
<div class="open">全部结果:</div>
<div class="selected-op" v-for="(v, k) in params.filters" :key="k" @click="deleteFilter(k)">
{{filterNames[k]}}<span>{{v}} <span class='close'>×</span></span>
</div>
</div>
<!--过滤项-->
<div class="filter-list">
<div v-for="(v, k) in remainFilter" :key="k">
<div class="filter-box">
<div class="f-key"><strong>{{filterNames[k]}}</strong></div>
<div class="column-divider"></div>
<div class="f-items">
<div class="f-item" @click="clickFilter(k, o)" v-for="(o, j) in v" :key="j"><a href="javascript:void(0)">
{{o}}</a></div>
</div>
</div>
<div class="row-divider"></div>
</div>
<div class="filter-box">
<div class="f-key"><strong>价格</strong></div>
<div class="column-divider"></div>
<div class="f-items">
<div class="f-item" @click="clickFilter('price','0-100')"><a href="javascript:void(0)">100元以下</a></div>
<div class="f-item" @click="clickFilter('price','100-300')"><a href="javascript:void(0)">100-300元</a></div>
<div class="f-item" @click="clickFilter('price','300-600')"><a href="javascript:void(0)">300-600元</a></div>
<div class="f-item" @click="clickFilter('price','600-1500')"><a href="javascript:void(0)">600-1500元</a></div>
<div class="f-item" @click="clickFilter('price','1500-0')"><a href="javascript:void(0)">1500元以上</a></div>
</div>
</div>
</div>
<!-- 排序 -->
<div class="top-ban">
<!--排序条件-->
<div class="sort-items">
<div class="sort-item" v-for="(item, i) in sortItems" :key="i">
<a :class="{selected: params.sortBy===item.key}" @click="params.sortBy=item.key"
href="javascript:void(0)">{{item.text}}</a>
|
</div>
</div>
<!--分页条-->
<div class="top-pagination">
<span><i style="color: #222;">{{total}}</i> 家酒店</span>
<span><i style="color: red;">{{params.page}}</i>/{{totalPage}}</span>
<a class="btn-arrow" href="#" style="display: inline-block" @click="prePage">&lt;</a>
<a class="btn-arrow" href="#" style="display: inline-block" @click="nextPage">&gt;</a>
</div>
</div>
<div class="row-divider" style="margin-bottom: 5px; width: 100%"></div>
<!--酒店列表-->
<div style="display: flex; justify-content: space-between;">
<div class="hotel-list">
<div class="hotel-box" v-for="(hotel,i) in hotels" :key="i"
style="display: flex;justify-content: space-between;" @mouseover="handleMarkerFocus(hotel)">
<div class="ad-mark" v-if="hotel.isAD">
<img src="./img/ad.png" width="25"/>
</div>
<div style="width: 0;"></div>
<div style="width: 25%"><img width="100%" :src="hotel.pic"></div>
<div class="hotel-info">
<div class="hotel-name" v-html="hotel.name">
</div>
<div class="star-name"> {{hotel.starName}}</div>
<div class="address">
位于 <span style="color: #BC8516;">{{hotel.business}}</span> 周边,{{hotel.address}}
</div>
<div class="order"> 1分钟前有人预订了该酒店</div>
<div v-if="hotel.distance" class="address">距离您 {{hotel.distance.toFixed(2)}} km</div>
</div>
<div style="text-align: left; width: 15%;">
<div>
<b style="color: #f60;"></b> <span id='hotel-price'>{{hotel.price}}</span> <span
style="font-size: 0.2em; color: #999;">起</span>
</div>
<div class='btn'>立即预定</div>
<div>
<span class="hotel-score">{{hotel.score / 10}}分</span> /<span>5分</span>
</div>
</div>
</div>
</div>
<div :class='{"map-box": true, "fixed-map": isFixed}' :style="{left: ml + 'px'}">
<div class="map-head">地图预览</div>
<div class="amap" id="container"></div>
<div class="map-geo" @click="getGeoLoc">
<img src="https://a.amap.com/jsapi/static/image/plugin/waite.png" v-show="loading">
</div>
</div>
</div>
</div>
</div>
<script src="./js/vue.js"></script>
<script src="./js/axios.min.js"></script>
<script src="./js/amap.min.js"></script>
<script type="text/javascript" src="https://webapi.amap.com/maps?v=2.0&key=ddd292c88aa1bad9c04891a47724f40a"></script>
<script>
// 设置后台服务地址
axios.defaults.baseURL = "http://localhost:8089";
axios.defaults.timeout = 3000;
let app = new Vue({
el: "#app",
data: {
isTest: false,
filterNames: {
brand: "品牌",
city: "城市",
starName: "星级",
price: "价格",
},
filterList: {},// 过滤项的假数据
sortItems: [
{
key: "default",
text: "默认",
},
{
key: "score",
text: "评价",
},
{
key: "price",
text: "价格",
}
],// 排序字段的假数据
hotels: [],// 酒店数据
total: 0, // 总条数
totalPage: 0, // 总页数
params: {
key: "", // 搜索关键字
page: 1, // 当前页码
size: 5, // 每页大小
sortBy: "default",// 排序字段
filters: {}, // 过滤字段
},
map: {},// 地图对象
loc: "", // 地图标记
ml: 0, // 控制地图位置
geolocation:{}, // 定位系统
loading: false, // 是否在定位
currentHotel: {},
ops: [],
showOps: false,
opsIndex: -1,
isFixed: false,
testFilterData: {
"city": ["上海", "北京", "深圳", "杭州"],
"starName": ["四星","五星","二钻","三钻","四钻","五钻"],
"brand": ["7天酒店", "如家","速8", "皇冠假日","华美达","万怡","喜来登","万豪","和颐","希尔顿"],
},
},
watch: {
"params.sortBy"() {
// 每当page改变当前代码就会执行。搜索一下
this.search(this.loc);
},
"params.page"() {
// 每当page改变当前代码就会执行。搜索一下
this.search(this.loc);
},
"params.filters": {
deep: true,
handler() {
this.search();
// 获取过滤项
this.getFilter();
}
},
opsIndex() {
if (this.opsIndex !== -1) {
this.params.key = this.ops[this.opsIndex]
}
}
},
created() {
// 页面加载时,先搜索一下
this.search();
// 获取过滤项
this.getFilter();
},
mounted() {
//初始化地图
this.map = new AMap.Map('container', {
resizeEnable: true, //是否监控地图容器尺寸变化
zoom: 11, //初始地图级别
center: [120.0, 32.0], //初始地图中心点
});
// 初始化定位系统
AMap.plugin('AMap.Geolocation', () => {
this.geolocation = new AMap.Geolocation({
position: 'RT', //定位按钮的停靠位置
buttonOffset: new AMap.Pixel(10, 20),//定位按钮与设置的停靠位置的偏移量默认Pixel(10, 20)
zoomToAccuracy: true, //定位成功后是否自动调整地图视野到定位点
});
// AMap.Event.addListener(geolocation, 'complete', result => {
// if (this.params.page !== 1) {
// this.params.page = 1;
// this.loc = result.position.lat + ", " + result.position.lng;
// } else {
// this.searchByMap(result.position.lat + ", " + result.position.lng);
// }
//
// });//返回定位信息
// AMap.Event.addListener(geolocation, 'error', err => console.log(err)); //返回定位出错信息
});
let oDiv = document.querySelector(".map-box"),
H = 0,
Y = oDiv;
while (Y) {
H += Y.offsetTop;
Y = Y.offsetParent;
}
window.addEventListener('scroll', () => this.handleScroll(H, oDiv));
},
methods: {
handleScroll(h, o) {
let s = document.body.scrollTop || document.documentElement.scrollTop
this.isFixed = s > h;
this.ml = o.offsetLeft;
},
handleKeyUp(e) {
if ((e.keyCode >= 48 && e.keyCode <= 57) || (e.keyCode >= 65 && e.keyCode <= 90) || e.keyCode === 8) {
// 用户输入的字符,需要自动补全
this.getSuggestion();
} else if (e.keyCode === 13) {
// 用户按回车,需要搜索
this.search();
this.getFilter();
} else if (e.keyCode === 38) {
if (this.opsIndex > 0) {
this.opsIndex--;
} else {
this.opsIndex = this.ops.length - 1;
}
} else if (e.keyCode === 40) {
this.opsIndex = (this.opsIndex + 1) % this.ops.length;
} else if (e.keyCode === 27) {
this.show = false
}
},
handleSearch(){
this.search();
this.getFilter();
},
getSuggestion() { // 查询自动补全
if (!this.params.key) {
// key没有值不去搜索了
this.ops = [];
return;
}
axios.get("/hotel/suggestion?key=" + this.params.key)
.then(resp => {
this.ops = resp.data;
})
.catch(e => {
if(this.isTest){
this.ops = ["万豪", "如家", "喜来登"]
}else{
this.ops = []
}
console.log(e);
})
},
getFilter() {
if(this.isTest){
this.filterList = this.testFilterData;
return;
}
// 准备参数
const {filters: {price: ps, ... fs}, ...params} = this.params;
for( _k in fs){
params[_k] = fs[_k];
}
// 处理价格
if(ps){
let pArr = ps.split("-");
params.minPrice = parseInt(pArr[0]);
let max = parseInt(pArr[1]);
params.maxPrice = max === 0 ? 999999 : max;
}
axios.post("/hotel/filters", params)
.then(resp => {
this.filterList = resp.data;
})
.catch(err => {
console.log(err);
this.filterList = this.testFilterData;
})
},
searchByMap(location) {
// 准备参数
const {filters: {price: ps, ... fs}, ...params} = this.params;
for( _k in fs){
params[_k] = fs[_k];
}
// 处理价格
if(ps){
let pArr = ps.split("-");
params.minPrice = parseInt(pArr[0]);
let max = parseInt(pArr[1]);
params.maxPrice = max === 0 ? 999999 : max;
}
// 准备地址
if (location) {
params.location = location;
this.loc = location;
}
axios.post("/hotel/list", params)
.then(resp => {
if(!resp.data.hotels){
this.hotels = resp.data;
this.total = 271;
this.totalPage = 28;
}else{
this.hotels = resp.data.hotels;
this.total = resp.data.total;
this.totalPage = Math.ceil((this.total + 5 - 1) / 5);
}
if (location) {
this.setMapCenter(location);
} else {
this.setMapCenter(this.hotels[0].location);
}
this.initMarker();
})
.catch(err => {
console.log(err)
this.hotels = [{"id":60223,"name":"上海希尔顿酒店","address":"静安华山路250号","price":2688,"score":37,"brand":"希尔顿","city":"上海","starName":"五星级","business":"静安寺地区","location":"31.219306, 121.445427","pic":"https://m.tuniucdn.com/filebroker/cdn/res/92/10/9210e74442aceceaf6e196d61fc3b6b1_w200_h200_c1_t0.jpg"},{"id":60922,"name":"上海虹桥祥源希尔顿酒店","address":"红松东路1116号","price":1108,"score":45,"brand":"希尔顿","city":"上海","starName":"五钻","business":"虹桥地区","location":"31.18746, 121.395312","pic":"https://m.tuniucdn.com/fb3/s1/2n9c/tQRqDTFkHnHzMZiDKjcGV81ekvc_w200_h200_c1_t0.jpg"},{"id":309208,"name":"北京王府井希尔顿酒店","address":"王府井东街8号","price":1679,"score":46,"brand":"希尔顿","city":"北京","starName":"五钻","business":"天安门/王府井地区","location":"39.914539, 116.413392","pic":"https://m.tuniucdn.com/fb2/t1/G6/M00/52/10/Cii-TF3ePt2IX9UEAALb6VYBSmoAAGKMgGsuW8AAtwB147_w200_h200_c1_t0.jpg"},{"id":395434,"name":"北京希尔顿酒店","address":"东三环北路东方路1号","price":350,"score":45,"brand":"希尔顿","city":"北京","starName":"五星级","business":"燕莎/朝阳公园商业区","location":"39.952703, 116.462387","pic":"https://m.tuniucdn.com/fb3/s1/2n9c/3fwNbKGhk6XCrkdVyxwhC5uGpLVy_w200_h200_c1_t0.jpg"},{"id":395702,"name":"北京首都机场希尔顿酒店","address":"首都机场3号航站楼三经路1号","price":222,"score":46,"brand":"希尔顿","city":"北京","starName":"五钻","business":"首都机场/新国展地区","location":"40.048969, 116.619566","pic":"https://m.tuniucdn.com/fb2/t1/G6/M00/52/10/Cii-U13ePtuIMRSjAAFZ58NGQrMAAGKMgADZ1QAAVn_167_w200_h200_c1_t0.jpg"},{"id":615175,"name":"千岛湖滨江希尔顿度假酒店","address":"环湖北路600号","price":1265,"score":47,"brand":"希尔顿","city":"杭州","starName":"五钻","business":"千岛湖镇","location":"29.603634, 119.077596","pic":"https://m.tuniucdn.com/fb3/s1/2n9c/6qzYeUrrXsH5H3cd9bMXLz8MJtT_w200_h200_c1_t0.jpg"},{"id":2351601,"name":"深圳蛇口希尔顿南海酒店","address":"望海路1177号","price":509,"score":47,"brand":"希尔顿","city":"深圳","starName":"五钻","business":"深圳湾口岸/蛇口","location":"22.479373, 113.916013","pic":"https://m.tuniucdn.com/fb2/t1/G6/M00/45/EA/Cii-TF3ZpXOIfa6fAAJjiUOiuYgAAFrtgDtgpQAAmOh799_w200_h200_c1_t0.jpg"},{"id":368701368,"name":"深圳大中华希尔顿酒店","address":"福田深南大道1003号","price":1666,"score":46,"brand":"希尔顿","city":"深圳","starName":"五钻","business":"会展中心/CBD","location":"22.539313, 114.069763","pic":"https://m.tuniucdn.com/fb3/s1/2n9c/4EnHseZ73LXdFJY7DSdJ8xqAcjXe_w200_h200_c1_t0.jpg"},{"id":2048042240,"name":"北京大兴希尔顿酒店","address":"高米店南里18号楼","price":1283,"score":48,"brand":"希尔顿","city":"北京","starName":"五钻","business":"大兴北京新机场地区","location":"39.76875, 116.339199","pic":"https://m.tuniucdn.com/fb3/s1/2n9c/3B32F8zSU2CJCWzs1hoH2o4WcquR_w200_h200_c1_t0.jpg"},{"id":2056105938,"name":"北京通州北投希尔顿酒店","address":"新华东街289号2号楼","price":1068,"score":48,"brand":"希尔顿","city":"北京","starName":"五钻","business":"果园环岛/通州区","location":"39.908805, 116.659748","pic":"https://m.tuniucdn.com/fb3/s1/2n9c/NGKdpec3tZJNUUNWJ5pd67Cp5AY_w200_h200_c1_t0.png"}]
this.total = 271;
this.totalPage = 28;
})
},
search(location) {
// 发送ajax
this.searchByMap(location);
},
prePage() {
if (this.params.page > 1) {
this.params.page--
}
},
nextPage() {
if (this.params.page < this.totalPage) {
this.params.page++
}
},
clickPrice(min, max) {
this.params.minPrice = min;
this.params.maxPrice = max;
},
clickFilter(key, option) {
this.loc = "";
const {...obj} = this.params.filters;
obj[key] = option;
this.params.filters = obj;
},
deleteFilter(k) {
this.loc = "";
const {...obj} = this.params.filters;
delete obj[k];
this.params.filters = obj;
},
location(loc) {
let arr = loc.split(", ");
let json = '[' + arr[1] + ', ' + arr[0] + ']'
return JSON.parse(json);
},
initMarker() {
if (!this.loc) {
this.map.clearMap();
}
this.markHotels();
},
setMapCenter(location){
this.map.setCenter(this.location(location));
},
markHotels(){
this.hotels.forEach((h, i) => {
// 将创建的点标记添加到已有的地图实例:
let marker = new AMap.Marker({
position: this.location(h.location), // 经纬度构成的一维数组[116.39, 39.9]
title: h.name,
offset: new AMap.Pixel(0, 0),
anchor:'bottom-center'
});
marker.vid = h.id;
this.map.add(marker);
});
},
handleMarkerFocus(h) {
this.map.setCenter(this.location(h.location));
let old = this.currentHotel;
let oldMarker = this.map.getAllOverlays("marker").find(m => m.vid === old.id);
if (oldMarker) {
this.updateMarker(oldMarker, "//a.amap.com/jsapi_demos/static/demo-center/icons/poi-marker-default.png", "", this.location(old.location), old.id);
}
this.currentHotel = h;
let marker = this.map.getAllOverlays("marker").find(m => m.vid === h.id);
this.updateMarker(marker, "//a.amap.com/jsapi_demos/static/demo-center/icons/poi-marker-red.png", h.name, this.location(h.location), h.id);
},
updateMarker(marker, icon, text, position, id) {
// 自定义点标记内容
var markerContent = document.createElement("div");
// 点标记中的图标
var markerImg = document.createElement("img");
markerImg.className = "markerlnglat";
markerImg.src = icon;
markerImg.setAttribute('width', '25px');
markerImg.setAttribute('height', '34px');
markerContent.appendChild(markerImg);
// 点标记中的文本
if (text) {
var markerSpan = document.createElement("span");
markerSpan.className = 'marker';
markerSpan.innerHTML = text;
markerContent.appendChild(markerSpan);
}
marker.setContent(markerContent);
marker.setPosition(position);
marker.vid = id;
},
getGeoLoc(){
this.loading = true;
this.geolocation.getCurrentPosition((status,result) => {
this.loading = false;
if(status === 'complete'){
console.log("successs")
// https://a.amap.com/jsapi/static/image/plugin/point.png
this.addMaker('//a.amap.com/jsapi/static/image/plugin/point.png', result.position.lng,result.position.lat);
if (this.params.page !== 1) {
this.params.page = 1;
this.loc = result.position.lat + ", " + result.position.lng;
} else {
this.searchByMap(result.position.lat + ", " + result.position.lng);
}
}else{
console.log("err", status)
}
});
},
addMaker(iconUrl, lng, lat){
// 创建 AMap.Icon 实例:
let icon = new AMap.Icon({
size: new AMap.Size(25, 25), // 图标尺寸
image: iconUrl, // Icon的图像
imageOffset: new AMap.Pixel(0, 0), // 图像相对展示区域的偏移量,适于雪碧图等
imageSize: new AMap.Size(25, 25) // 根据所设置的大小拉伸或压缩图片
});
// 将 Icon 实例添加到 marker 上:
let marker = new AMap.Marker({
position: new AMap.LngLat(lng, lat),
offset: new AMap.Pixel(0, 0),
icon: icon, // 添加 Icon 实例
title: '北京',
zoom: 13,
anchor:"center"
});
this.map.add(marker);
}
},
computed: {
remainFilter() {
let keys = Object.keys(this.params.filters);
let obj = {};
Object.keys(this.filterList).forEach(key => {
if (!keys.includes(key) && this.filterList[key].length > 1) {
obj[key] = this.filterList[key];
}
})
return obj;
}
}
})
</script>
</body>
</html>

2
target/classes/static/js/amap.min.js vendored Normal file

File diff suppressed because one or more lines are too long

9
target/classes/static/js/axios.min.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff