Browse Source

提交

master
chenkainan 1 year ago
parent
commit
f5d36467a9
  1. 5
      .env.development
  2. 5
      .env.production
  3. 5
      .env.test
  4. 23
      .gitignore
  5. 5
      babel.config.js
  6. 19
      jsconfig.json
  7. 10182
      package-lock.json
  8. 63
      package.json
  9. 16
      postcss.config.js
  10. BIN
      public/favicon.ico
  11. 24
      public/index.html
  12. 59
      src/App.vue
  13. BIN
      src/assets/logo.png
  14. 91
      src/libs/axios.js
  15. 55
      src/libs/storage.js
  16. 18
      src/libs/tools.js
  17. 76
      src/libs/utils.js
  18. 30
      src/main.js
  19. 27
      src/router/index.js
  20. 19
      src/store/index.js
  21. 19
      src/store/modules/user.js
  22. 566
      src/views/Index.vue
  23. 100
      src/views/LineList.vue
  24. 245
      src/views/compoents/lineRoute.vue
  25. 176
      src/views/compoents/productDetail.vue
  26. 79
      vue.config.js

5
.env.development

@ -0,0 +1,5 @@
NODE_ENV='development'
# 请求域名前缀
VUE_APP_URL='https://tongli.sz-trip.com'
# 打包后输出目录
VUE_APP_OUTPUTDIR='dist_dev'

5
.env.production

@ -0,0 +1,5 @@
NODE_ENV='production'
# 请求域名前缀
VUE_APP_URL='https://tongli.sz-trip.com'
# 打包后输出目录
VUE_APP_OUTPUTDIR='dist'

5
.env.test

@ -0,0 +1,5 @@
NODE_ENV='production'
# 请求域名前缀
VUE_APP_URL='https://tongli.sz-trip.com'
# 打包后输出目录
VUE_APP_OUTPUTDIR='dist_test'

23
.gitignore

@ -0,0 +1,23 @@
.DS_Store
node_modules
/dist
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

5
babel.config.js

@ -0,0 +1,5 @@
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset'
]
}

19
jsconfig.json

@ -0,0 +1,19 @@
{
"compilerOptions": {
"target": "es5",
"module": "esnext",
"baseUrl": "./",
"moduleResolution": "node",
"paths": {
"@/*": [
"src/*"
]
},
"lib": [
"esnext",
"dom",
"dom.iterable",
"scripthost"
]
}
}

10182
package-lock.json

File diff suppressed because it is too large

63
package.json

@ -0,0 +1,63 @@
{
"name": "taihulake",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve --open",
"build": "vue-cli-service build",
"test": "vue-cli-service build --mode test",
"lint": "vue-cli-service lint"
},
"dependencies": {
"amfe-flexible": "^2.2.1",
"axios": "^1.3.2",
"core-js": "^3.8.3",
"html-webpack-plugin": "^5.5.1",
"moment": "^2.30.1",
"postcss-px2rem-exclude": "0.0.6",
"postcss-pxtorem": "^6.1.0",
"qs": "^6.11.0",
"vant": "^2.12.53",
"vue": "^2.6.14",
"vue-router": "^3.5.3",
"vuex": "^3.6.2",
"webpack-bundle-analyzer": "^4.8.0"
},
"devDependencies": {
"@babel/core": "^7.12.16",
"@babel/eslint-parser": "^7.12.16",
"@vue/cli-plugin-babel": "~5.0.0",
"@vue/cli-plugin-eslint": "~5.0.0",
"@vue/cli-service": "~5.0.0",
"babel-plugin-import": "^1.13.6",
"eslint": "^7.32.0",
"eslint-plugin-vue": "^8.0.3",
"node-sass": "^4.14.1",
"postcss": "^8.4.45",
"postcss-loader": "^3.0.0",
"px2rem-loader": "^0.1.9",
"sass-loader": "^7.3.1",
"vue-template-compiler": "^2.6.14"
},
"eslintConfig": {
"root": true,
"env": {
"node": true
},
"extends": [
"plugin:vue/essential",
"eslint:recommended"
],
"parserOptions": {
"parser": "@babel/eslint-parser"
},
"rules": {
"no-mixed-spaces-and-tabs": 0
}
},
"browserslist": [
"> 1%",
"last 2 versions",
"not dead"
]
}

16
postcss.config.js

@ -0,0 +1,16 @@
const autoprefixer = require('autoprefixer');
const px2rem = require('postcss-pxtorem');
module.exports = {
plugins: [
autoprefixer(),
px2rem({
rootValue(res) {
return res.file.indexOf('vant') !== -1 ? 37.5 : 75 //换算基数,1rem相当于75px
},
unitPrecision: 5, //保留rem小数点多少位
propList: ['*'],
minPixelValue: 12 //px小于12的不会被转换
})
]
}

BIN
public/favicon.ico

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

24
public/index.html

@ -0,0 +1,24 @@
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= htmlWebpackPlugin.options.url %>favicon.ico">
<title>地图</title>
<!-- 腾讯地图 -->
<script charset="utf-8" src="https://map.qq.com/api/gljs?v=1.exp&key=YVOBZ-MWJ3Z-34IXK-7J2GL-O33US-QLF5X">
</script>
<script src="https://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
<!-- 引入uniSDK -->
<script type="text/javascript" src="https://js.cdn.aliyun.dcloud.net.cn/dev/uni-app/uni.webview.1.5.2.js"></script>
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript
enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>

59
src/App.vue

@ -0,0 +1,59 @@
<template>
<div id="app">
<keep-alive>
<router-view v-if="$route.meta.keepAlive"></router-view>
</keep-alive>
<router-view v-if="!$route.meta.keepAlive"></router-view>
</div>
</template>
<script>
export default {
name: 'App',
mounted() {
}
}
</script>
<style>
div {
box-sizing: border-box;
}
.flex-between {
display: flex;
justify-content: space-between;
align-items: center;
}
.flex-around {
display: flex;
justify-content: space-around;
align-items: center;
}
.flex-center {
display: flex;
justify-content: center;
align-items: center;
}
/*单行隐藏*/
.text-overflow {
overflow-x: hidden;
overflow-y: inherit;
text-overflow: ellipsis;
white-space: nowrap;
}
/* 两行隐藏 */
.text-overflowRows {
overflow: hidden;
text-overflow: ellipsis;
-webkit-line-clamp: 2;
word-break: break-all;
display: -webkit-box;
-webkit-box-orient: vertical;
}
</style>

BIN
src/assets/logo.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

91
src/libs/axios.js

@ -0,0 +1,91 @@
import Vue from 'vue'
/**引入axios*/
import axios from "axios";
/**引入参数处理*/
// import Qs from 'qs';
import {Dialog, Toast} from "vant";
import store from '@/store';
const http = axios.create({
baseURL: process.env.VUE_APP_URL,
timeout: 6000 // 请求超时时间 当请求时间超过`秒还未取得结果时 提示用户请求超时
})
//添加请求拦截器
http.interceptors.request.use((config) => {
const token = store.state.user.userInfo.token;
config.headers['token'] = token
config.headers['Content-Type'] = 'application/json;charset=UTF-8'
// if(config.loading === true){
Toast.loading({
message: '加载中...',
forbidClick: true,
duration: 0
});
// }
return config;
}, (error) => {
return Promise.reject(error);
});
// 添加响应拦截器
http.interceptors.response.use(response => {
Toast.clear()
if(response.status === 200 || response.status === 1){
return response.data;
}
},error => {
if(error.response && error.response.status){
switch(error.response.status){
case 401:
Dialog.confirm({
title: '提示',
message: '请登录后操作',
confirmButtonText: '去登录'
}).then(() => {
// 登录操作
});
break;
case 404:
Toast.fail({
type: "fail",
message: '网络繁忙,请刷新再试',
forbidClick: true,
duration: 2000
});
break;
default:
Toast.fail({
type: "fail",
message: '网络繁忙,请刷新再试',
forbidClick: true,
duration: 2000
});
break;
}
}
})
// 请求
Vue.prototype.get = (params, url, loading) => {
return new Promise((resolve, reject) => {
http.get(url, params, loading)
.then(res => {
resolve(res);
})
.catch(err => {
reject(err);
});
})
}
Vue.prototype.post = (data, url, loading) => {
return new Promise((resolve, reject) => {
http.post(url, data, loading)
.then(res => {
resolve(res);
})
.catch(err => {
reject(err);
});
})
}

55
src/libs/storage.js

@ -0,0 +1,55 @@
import {
validatenull
} from '@/libs/tools';
const keyName = '' + '-'; //随便写个自己的标识
//存储sessionStorage
export const setStore = (params = {}) => {
let {
name,
content,
type,
} = params;
name = keyName + name
let obj = {
dataType: typeof(content),
content: content,
type: type,
datetime: new Date().getTime()
}
if (type) window.sessionStorage.setItem(name, JSON.stringify(obj));
else window.localStorage.setItem(name, JSON.stringify(obj));
}
//获取sessionStorage
export const getStore = (params = {}) => {
let {
name,
debug
} = params;
name = keyName + name
let obj = {},
content;
obj = window.sessionStorage.getItem(name);
if (validatenull(obj)) obj = window.localStorage.getItem(name);
if (validatenull(obj)) return;
try {
obj = JSON.parse(obj);
} catch {
return obj;
}
if (debug) {
return obj;
}
if (obj.dataType == 'string') {
content = obj.content;
} else if (obj.dataType == 'number') {
content = Number(obj.content);
} else if (obj.dataType == 'boolean') {
content = eval(obj.content);
} else if (obj.dataType == 'object') {
content = obj.content;
}
return content;
}

18
src/libs/tools.js

@ -0,0 +1,18 @@
//判断是否为空
export function validatenull(val) {
if (typeof val == 'boolean') {
return false;
}
if (typeof val == 'number') {
return false;
}
if (val instanceof Array) {
if (val.length == 0) return true;
} else if (val instanceof Object) {
if (JSON.stringify(val) === '{}') return true;
} else {
if (val == 'null' || val == null || val == 'undefined' || val == undefined || val == '') return true;
return false;
}
return false;
}

76
src/libs/utils.js

@ -0,0 +1,76 @@
export default {
install(Vue) {
Vue.prototype.util = {
// 格式化富文本
formateRichText(str) {
if (!str) return "";
var reg = new RegExp("<img", "g");
str = str.replace(reg, "<img class='sz-xcx-fwb-img' width='100%'")
reg = new RegExp("<IMG", "g");
str = str.replace(reg, "<img class='sz-xcx-fwb-img' width='100%'")
reg = new RegExp("&nbsp;", "g");
str = str.replace(reg, '<span style="width: 8rpx;display: inline-block;"></span>')
reg = new RegExp("section", "g");
str = str.replace(reg, 'div');
reg = new RegExp("↵", "g");
str = str.replace(reg, '<br />');
str = str.replace(/<table/g, '<table border="1" cellspacing="0" style="border-collapse:collapse"')
return str;
},
// 手机号验证规则
mobileValid(val) {
return /^(13[0-9]|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\d{8}$/.test(val);
},
// 身份证验证规则
idNumberValid(val) {
return /^\d{17}(\d{1}|[X|x])$/.test(val);
},
// 护照验证正则
passportValid(val) {
return /^([a-zA-z]|[0-9]){5,17}$/.test(val);
},
// 台胞证正则
taiwanValid(val) {
return /^\d{8}|^[a-zA-Z0-9]{10}|^\d{18}$/.test(val);
},
// 港澳通行证正则
gangaoValid(val) {
return /^([A-Z]\d{6,10}(\(\w{1}\))?)$/.test(val);
},
// 外国人永久居留证正则
foreignerValid(val) {
return /(^[A-Za-z]{3})([0-9]{12}$)/.test(val);
},
// 军官证正则
officerValid(val) {
return /^[\u4E00-\u9FA5](字第)([0-9a-zA-Z]{4,8})(号?)$/.test(val);
},
// 邮箱验证正则
emailValid(val) {
return /^[A-Za-z0-9\u4e00-\u9fa5]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/.test(val)
},
// 获取路径参数
getUrlPara(url) {
let arrUrl = url.split("?");
let para = arrUrl[1];
return para ? para.split('&') : false;
},
openMap(item) {
uni.openLocation({
longitude: item.lon, // 经度
latitude: item.lat, //纬度
name: item.address || item.title || item.name, // 位置名称
address: item.address, //详细地址
})
},
showImg(img) {
if(!img) return;
if (img.indexOf('https://') != -1 || img.indexOf('http://') != -1) {
return img;
} else {
return 'https://tongli.sz-trip.com' + img;
}
}
}
}
}

30
src/main.js

@ -0,0 +1,30 @@
import Vue from 'vue'
import App from './App.vue'
import store from './store'
import '../src/libs/axios.js' // axios处理
// 引入amfe-flexible做rem适配
import "amfe-flexible"
import router from "./router"
import utils from './libs/utils.js'
Vue.use(utils)
import Vant from 'vant';
import 'vant/lib/index.css';
Vue.use(Vant);
// Vue.prototype.ShowLoading = (title) => {
// Vue.prototype.$toast.loading({
// message: title ? title : '请求中',
// forbidClick: true,
// duration: 0
// })
// }
Vue.config.productionTip = false
new Vue({
store,
router,
render: h => h(App),
}).$mount('#app')

27
src/router/index.js

@ -0,0 +1,27 @@
import Vue from "vue";
import VueRouter from "vue-router";
import Index from "../views/Index.vue";
Vue.use(VueRouter);
const routes = [{
path: "/",
name: "index",
meta: {title: "首页", keepAlive: true},
component: Index,
},
{
path: "/lineList",
name: "lineList",
meta: {title: "", keepAlive: true},
component: () =>import("../views/LineList.vue"),
},
];
const router = new VueRouter({
mode: "history",
base: process.env.BASE_URL,
routes,
});
export default router;

19
src/store/index.js

@ -0,0 +1,19 @@
import Vue from 'vue'
import Vuex from 'vuex'
import user from './modules/user'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
},
mutations: {
},
actions: {},
modules: {
namespaced: true, // 为了解决不同模块命名冲突的问题
user
}
})

19
src/store/modules/user.js

@ -0,0 +1,19 @@
import { setStore, getStore } from '@/libs/storage'
export default {
state: {
userInfo: getStore({ name: 'userInfo' }) || '', // 保存用户登录信息
},
mutations: {
changeUserInfo(state, data) {
state.userInfo = data
setStore({ name: 'userInfo', content: state.userInfo, type: 'session' })
if(data && data.token)localStorage.setItem('userInfo', JSON.stringify(data))
},
},
getters: {
},
actions: {
}
}

566
src/views/Index.vue

@ -0,0 +1,566 @@
<template>
<div>
<div id="mapContainer" class="mapContainer"></div>
<!-- 点位分类 -->
<div class="type-box">
<div :class="['type-item',{'type-active': index == typeIndex}]" v-for="(item,index) in mapType"
@click="getSpotsByCategory(index)">{{item.name}}</div>
</div>
<div class="area-box">
<!-- 区域分类 -->
<div>
<div class="area-item area-items" @click="areaMore = !areaMore">
<img :src="'https://static.ticket.sz-trip.com/yandu/images/map/'+ (areaMore ? 'topIcon.png' : 'bottomIcon.png')"
alt="" />
{{areaMore ? '收起' : '展开'}}
</div>
<div v-if="areaMore">
<div v-for="(item,index) in areaList" :key="index"
:class="['area-item',{'area-active':index == areaIndex}]" @click="changeArea(index)">
{{item.name}}
</div>
</div>
</div>
<!-- 行程线路 -->
<div>
<div class="area-item area-items" @click="addLine">
<img src="https://static.ticket.sz-trip.com/yandu/images/map/addLine.png" alt="" />
添加<br>行程
</div>
<div class="area-item area-items" @click="gotoLine">
<img src="https://static.ticket.sz-trip.com/yandu/images/map/line.png" alt="" />
线路<br>推荐
</div>
</div>
</div>
<!-- 详情弹框 -->
<van-popup v-model="detailShow" @close="audioPause" :overlay-style="{'background-color': 'rgba(0, 0, 0, 0)'}"
position="bottom" round>
<ProductDetail ref="detailRef" />
</van-popup>
<!-- 输入线路信息弹框 -->
<van-popup v-model="addLineShow" position="center" round>
<div class="addLine-box">
添加行程
<div class="addLine-item">
线路名称: <input type="text" v-model="lineName" placeholder="请输入线路名称" />
</div>
<div class="addLine-item">
开始时间: <input type="text" v-model="lineDate" placeholder="请选择出发时间" readonly
@click="lineDateShow = true" />
<img src="https://static.ticket.sz-trip.com/yandu/images/map/dateRight.png" alt="">
</div>
<div class="addLine-btn flex-around">
<div @click="addLineShow = false">取消</div>
<div @click="lineClick">下一步</div>
</div>
</div>
</van-popup>
<!-- 日期选择 -->
<van-popup v-model="lineDateShow" round position="bottom">
<van-datetime-picker v-model="currentDate" type="date" title="选择年月日" @confirm="lineDateConfirm" />
</van-popup>
<!-- 添加线路行程弹框 -->
<van-popup v-model="lineRouteShow" round position="bottom">
<LineRoute :id="lineId" :lineDate="lineDate" ref="lineRouteRef" />
</van-popup>
</div>
</template>
<script>
import moment from 'moment'
import ProductDetail from './compoents/productDetail'
import LineRoute from './compoents/lineRoute'
export default {
components: {
ProductDetail,
LineRoute
},
data() {
return {
mapObj: null,
mapMarker: null,
basics: {},
mapType: [],
typeIndex: -1,
areaList: [],
areaIndex: 0,
areaMore: true,
detailShow: false, //
addLineShow: false, // 线
lineName: '',
lineDate: '',
lineDateShow: false, //
currentDate: new Date(),
lineRouteShow: false, // 线
lineAddStatus: false, // true
lineId: '',
maps: [], //
};
},
mounted() {
this.getAreaList()
},
methods: {
//
getAreaList() {
this.post({}, '/api/emap/get_map_list').then(res => {
this.areaList = res.data
if (res.data.length > 0) {
this.getAreaDetail()
this.getCategory()
}
})
},
//
changeArea(index) {
this.areaIndex = index
this.getAreaDetail()
},
// code
getAreaDetail() {
this.post({
code: this.areaList[this.areaIndex].code
}, '/api/emap/get_init').then(res => {
this.basics = res.data
this.basics.center_poi = JSON.parse(this.basics.center_poi)
this.basics.lb_poi = JSON.parse(this.basics.lb_poi)
this.basics.rt_poi = JSON.parse(this.basics.rt_poi)
this.$nextTick(() => {
this.initMap()
})
})
},
// code
getCategory() {
this.post({
code: this.areaList[this.areaIndex].code
}, '/api/emap/getByCategory').then(res => {
this.mapType = Array.from(res.data)
if(this.mapType.length > 0) this.getSpotsByCategory(0)
})
},
// code
getSpotsByCategory(index) {
this.typeIndex = index
this.post({
code: this.areaList[this.areaIndex].code,
category_id: this.mapType[this.typeIndex].id
},'/api/emap/getSpotsByCategory').then(res => {
console.log(res)
this.maps = res.data
//
this.clearMarkers()
if(this.maps.length > 0) this.setMarkers()
})
},
initMap() {
let centerLat = this.basics.center_poi[1]
let centerLon = this.basics.center_poi[0]
var center = new TMap.LatLng(this.basics.center_poi[1], this.basics.center_poi[0]) //
var sw = new TMap.LatLng(this.basics.rt_poi[1], this.basics.rt_poi[0]); //,
var ne = new TMap.LatLng(this.basics.lb_poi[1], this.basics.lb_poi[0]); //西,
var latlngBounds = new TMap.LatLngBounds(ne, sw);
//
this.mapObj = new TMap.Map("mapContainer", {
center: center,
boundary: latlngBounds,
zoom: 12, //
minZoom: 12,
maxZoom: 15,
showControl: true, //
// viewMode: '2D', // 2D3D3D2D0
rotation: 0, //
});
this.mapObj.removeControl(TMap.constants.DEFAULT_CONTROL_ID.ZOOM); //
this.mapObj.removeControl(TMap.constants.DEFAULT_CONTROL_ID.ROTATION); //
//
//
// x,y,z
new TMap.ImageTileLayer({
getTileUrl: function(x, y, z) {
console.log(x, y, z)
//URL
let url =
`https://static.ticket.sz-trip.com/yandu/images/maps/` +
z +
"/" +
x +
"/" +
y +
".png";
return url;
},
tileSize: 256, //
minZoom: 12, //
maxZoom: 15, //
visible: true, //
zIndex: 5000, //z
opacity: 1, //10
map: this.mapObj, //
});
//
// this.markerCluster = new TMap.MarkerCluster({
// id: "cluster",
// map: this.mapObj,
// enableDefaultStyle: true, //
// minimumClusterSize: 5, //
// geometries: [], //
// zoomOnClick: true,
// gridSize: 60,
// averageCenter: true,
// });
},
//
setMarkers() {
//
// this.markerCluster.setGeometries([
// {
// position: new TMap.LatLng(33.332682, 120.16451)
// },
// {
// position: new TMap.LatLng(33.329281, 120.161117)
// }
// ]);
let markers = []
let labels = []
this.maps.forEach(item => {
markers.push({
"styleId": 'marker',
text: item.name,
position: new TMap.LatLng(item.poi_gcj02[1], item.poi_gcj02[0])
})
labels.push({
id: 'label', //
styleId: 'label', // id
position: new TMap.LatLng(item.poi_gcj02[1], item.poi_gcj02[0]), //
content: item.name, //
properties: {
//
title: 'label',
},
})
})
// marker
this.multiMarker = new TMap.MultiMarker({
id: 'marker-layer',
map: this.mapObj,
styles: {
"marker": new TMap.MarkerStyle({
"width": 24,
"height": 30,
"src": 'https://static.ticket.sz-trip.com/yandu/images/map/scenic.png',
"src": this.util.showImg(this.mapType.find(i => {return i.id == this.maps[0].category_id}).icon_image)
})
},
//
geometries: markers,
})
//
this.multiMarker.on("click", this.markerClick)
//
this.multiLabel = new TMap.MultiLabel({
id: 'label-layer',
map: this.mapObj,
collisionOptions: {
sameSource: true,
}, //
styles: {
label: new TMap.LabelStyle({
color: '#FFF', //
size: 13, //
offset: {
x: 0,
y: 15
}, //
angle: 0, //
alignment: 'center', //
verticalAlignment: 'middle', //
backgroundColor: 'rgba(0, 0, 0, .5)',
borderRadius: 7,
padding: '2px 8px',
}),
},
geometries: labels,
})
//
// this.multiLabel.on("click", this.markerClick)
},
//
clearMarkers() {
if (this.multiMarker) {
this.multiMarker.setMap(null);
this.multiMarker = null;
}
if(this.multiLabel) {
this.multiLabel.setMap(null);
this.multiLabel = null;
}
},
//
changeMapCenter() {
this.mapObj.panTo(new TMap.LatLng(33.37307, 120.18467));
},
//
markerClick(evt) {
console.log(evt, evt.geometry.text)
if (this.lineAddStatus) {
//
this.$dialog.confirm({
title: '',
message: '是否将' + evt.geometry.text + '加入行程?'
}).then(() => {
this.lineId = 1
this.$refs.lineRouteRef.addLineList()
}).catch(() => {
})
} else {
this.detailShow = true
}
},
// 线
gotoLine() {
// this.MultiPolyline = new TMap.MultiPolyline({
// id: 'polyline-layer',
// map: this.mapObj,
// styles: {
// "style_blue": new TMap.PolylineStyle({
// "width": 4,
// 'color': '#3777FF', //线
// 'borderWidth': 2, //线
// 'borderColor': '#FFF', //线
// 'lineCap': 'butt' //线
// })
// },
// // 线
// geometries: [{
// "id": 'style_blue',
// "styleId": 'style_blue',
// "paths": [new TMap.LatLng(33.347305, 120.136504), new TMap.LatLng(33.323448,
// 120.157053), new TMap.LatLng(33.338249, 120.1832)]
// }]
// })
this.$router.push({
path: '/lineList',
query: {
code: this.areaList[this.areaIndex].code
}
})
},
//
audioPause() {
this.$refs.detailRef.audioPlay(false)
},
// 线
addLine() {
this.lineName = ''
this.lineDate = ''
this.addLineShow = true
},
//
lineDateConfirm() {
this.lineDate = moment(this.currentDate).format('YYYY-MM-DD')
this.lineDateShow = false
},
// 线,
lineClick() {
if (this.lineName.trim().length == 0 || this.lineDate.trim().length == 0) {
this.$toast('请输入行程信息')
} else {
this.addLineShow = false
this.lineRouteShow = true
}
},
//
addlineRoute(status) {
this.lineRouteShow = false
if (status) {
// marker
this.multiMarker.setStyles({
"marker": new TMap.MarkerStyle({
"width": 24,
"height": 30,
"src": 'https://static.ticket.sz-trip.com/yandu/images/map/add.png'
})
})
// true
this.lineAddStatus = true
} else {
this.typeIndex = -1
this.setMarkers(0)
}
}
},
};
</script>
<style scoped lang="scss">
div {
box-sizing: border-box;
}
.mapContainer {
width: 100%;
height: 100vh;
}
.type-box {
position: fixed;
z-index: 2000;
width: 723px;
height: 53px;
top: 22px;
left: 27px;
display: flex;
overflow-x: auto;
.type-item {
padding: 0 26px;
line-height: 53px;
background: #FFFFFF;
border-radius: 13px;
font-weight: 500;
font-size: 27px;
color: #000000;
margin-right: 27px;
flex-shrink: 0;
}
.type-active {
background: linear-gradient(130deg, #9EE4FE, #7FD491);
font-weight: bold;
}
}
.type-box::-webkit-scrollbar {
display: none;
}
.area-box {
position: fixed;
z-index: 2000;
top: 153px;
right: 17px;
&>div {
width: 87px;
height: auto;
background: #FFFFFF;
border-radius: 43px;
border: 2px solid #FFFFFF;
overflow: hidden;
}
&>div:last-child {
margin-top: 24px;
.area-items {
height: 133px !important;
}
}
.area-item {
height: 93px;
display: flex;
align-items: center;
justify-content: center;
text-align: center;
border-top: 1px solid #D8D8D8;
font-weight: 500;
font-size: 22px;
color: #666666;
}
.area-items {
flex-direction: column;
justify-content: space-around;
height: 100px;
img {
width: 38px;
height: 38px;
}
}
.area-active {
background: linear-gradient(130deg, #9EE4FE, #7FD491);
color: #000000;
font-weight: bold;
}
}
// 线
.addLine-box {
width: 673px;
height: 413px;
background: #FFFFFF;
border-radius: 27px;
padding: 32px 33px 0 37px;
font-weight: bold;
font-size: 40px;
color: #000000;
.addLine-item {
font-weight: 400;
font-size: 29px;
margin-top: 40px;
input {
outline: none;
border: none;
height: 27px;
width: 380px;
margin-left: 30px;
}
input::placeholder {
color: #BEBEBE;
}
img {
width: 20px;
height: 20px;
}
}
.addLine-btn {
margin-top: 50px;
div {
width: 233px;
line-height: 67px;
background: #EAEAEA;
border-radius: 13px;
text-align: center;
font-weight: 500;
font-size: 29px;
color: #000000;
}
div:last-child {
background: #71B580;
color: #FFFFFF;
}
}
}
</style>

100
src/views/LineList.vue

@ -0,0 +1,100 @@
<template>
<div class="bg">
<div class="item" v-for="(item,index) in list" :key="index">
<img :src="util.showImg(item.image)" alt="" />
<div class="content">
<div class="title">{{item.name}}</div>
<div class="subtitle">{{item.points.length}}个景点</div>
<div class="tags" v-if="item.goods_new_tag">
<div class="tag" v-for="(tagItem,tagIndex) in item.goods_new_tag.split(',').slice(0,2)">{{tagItem}}</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
list: []
}
},
mounted() {
this.getList()
},
methods: {
// code线
getList() {
this.post({
code: this.$route.query.code
},'/api/emap/getLineByCode').then(res => {
this.list = res.data
})
}
}
}
</script>
<style lang="scss" scoped>
.bg {
width: 100%;
min-height: 100vh;
background: #F7F7F7;
padding-top: 31px;
}
.item {
width: 697px;
height: 240px;
background: #FFFFFF;
box-shadow: 0px 1px 16px 0px rgba(153,153,153,0.35);
border-radius: 20px;
margin: 0 auto 33px;
padding: 13px;
display: flex;
img {
object-fit: cover;
width: 213px;
height: 213px;
border-radius: 13px;
}
.content {
margin-left: 25px;
padding: 15px 0;
width: 410px;
.title {
font-weight: bold;
font-size: 33px;
color: #111111;
}
.subtitle {
margin-top: 8px;
font-weight: 500;
font-size: 27px;
color: #666666;
}
.tags {
display: flex;
margin-top: 15px;
.tag {
font-weight: 500;
font-size: 23px;
color: #71B580;
line-height: 40px;
border-radius: 5px;
border: 1px solid #69AF78;
padding: 0 12px;
margin-right: 13px;
}
}
}
}
</style>

245
src/views/compoents/lineRoute.vue

@ -0,0 +1,245 @@
<template>
<div class="bg">
<div class="top">
线路标题
<span>{{lineDate}}</span>
</div>
<div class="center">
<div class="center-items flex-between" v-for="(item,index) in list" :key="index">
<div class="center-line"></div>
<div class="center-num">{{index + 1}}</div>
<div class="center-item">
<img :src="util.showImg(item.image)" class="center-img">
<div class="center-content">
<div class="title text-overflow">{{item.title}}{{index}}</div>
<div class="subtitle text-overflow">营业时间{{item.open_time}}-{{item.close_time}}</div>
<div class="subtitle text-overflow">地址{{item.address}}</div>
<div class="location" @click="util.openMap(item)">
<img src="https://static.ticket.sz-trip.com/yandu/images/map/lineLocation.png" alt="">
导航
</div>
</div>
</div>
<img src="https://static.ticket.sz-trip.com/yandu/images/map/delLine.png" alt="" @click="delLine(index)">
</div>
<div class="center-btns flex-between" @click="addLine">
<div class="center-line" :style="{top: list.length == 0 ? '20px' : ''}" v-if="list.length == 0"></div>
<div class="center-num">{{list.length == 0 ? '1' : ''}}</div>
<div class="center-btn">
+添加行程
</div>
<div></div>
</div>
</div>
<div class="btns flex-around">
<div @click="submit(0)">取消</div>
<div @click="submit(1)">保存</div>
</div>
</div>
</template>
<script>
export default {
props: ['id','lineDate'],
data() {
return {
list: [],
itemObj: {
image: '/uploads/20240826/a87488f6225789aa19dbb437671d388d.png',
title: '线路推荐-测试',
open_time: '',
close_time: '',
address: '地址'
}
}
},
mounted() {
},
methods: {
addLine() {
this.$parent.$parent.addlineRoute(1)
},
addLineList() {
this.list.push(this.itemObj)
this.$parent.$parent.lineRouteShow = true
},
// 线
delLine(index) {
this.list.splice(index,1)
},
submit(status) {
if(status) {
//
}else {
//
this.$parent.$parent.addlineRoute(0)
}
}
}
}
</script>
<style lang="scss" scoped>
.bg {
width: 750px;
max-height: 60vh;
overflow-x: hidden;
overflow-y: auto;
padding: 40px 27px 180px 27px;
position: relative;
}
.top {
font-weight: 500;
font-size: 40px;
color: #000000;
span {
font-weight: 400;
font-size: 24px;
color: #666666;
margin-left: 20px;
vertical-align: text-bottom;
}
}
.center {
height: 650px;
overflow-y: auto;
margin-top: 50px;
.center-items {
position: relative;
padding-bottom: 27px;
.center-item {
width: 573px;
height: 201px;
background: #FFFFFF;
box-shadow: 0px 0px 20px 0px rgba(142,142,142,0.3);
border-radius: 13px;
padding: 7px;
display: flex;
.center-img {
width: 187px;
height: 187px;
border-radius: 7px;
}
.center-content {
padding: 7px 0;
margin-left: 15px;
display: flex;
flex-direction: column;
justify-content: space-between;
width: 344px;
.title {
font-weight: bold;
font-size: 29px;
color: #333333;
width: 344px;
}
.subtitle {
font-weight: 500;
font-size: 23px;
color: #666666;
width: 344px;
}
.location {
font-weight: bold;
font-size: 23px;
color: #71B580;
display: flex;
align-items: center;
margin-left: auto;
img {
width: 32px;
height: 32px;
margin-right: 5px;
}
}
}
}
img {
width: 33px;
height: 33px;
}
}
.center-btns {
position: relative;
.center-btn {
width: 573px;
line-height: 73px;
background: #71B580;
border-radius: 13px;
text-align: center;
font-weight: 400;
font-size: 29px;
color: #FFFFFF;
}
div:last-child {
width: 33px;
}
}
.center-num {
width: 38px;
line-height: 38px;
background: #71B580;
border-radius: 50%;
text-align: center;
font-weight: 500;
font-size: 32px;
color: #FFFFFF;
position: relative;
}
.center-line {
position: absolute;
width: 2px;
height: 100%;
background: #71B580;
top: 85px;
left: 18px;
}
}
.btns {
width: 750px;
height: 173px;
background: #FFFFFF;
box-shadow: 0px 0px 20px 0px rgba(142,142,142,0.3);
padding: 0 20px;
position: absolute;
left: 0;
div {
width: 233px;
line-height: 67px;
background: #EAEAEA;
border-radius: 13px;
text-align: center;
font-weight: 400;
font-size: 27px;
color: #000000;
}
div:last-child {
background: #71B580;
color: #FFFFFF;
}
}
</style>

176
src/views/compoents/productDetail.vue

@ -0,0 +1,176 @@
<template>
<div class="bg">
<div class="top flex-between">
标题标题
<div class="top-btn" v-if="type">购买</div>
</div>
<div class="img-box" v-if="type">
<img src="https://tongli.sz-trip.com/uploads/20240912/bfc1df32e7bc7cd53781ef6671f88cda.png" alt="" />
<img src="https://tongli.sz-trip.com/uploads/20240912/bfc1df32e7bc7cd53781ef6671f88cda.png" alt="" />
<img src="https://tongli.sz-trip.com/uploads/20240912/bfc1df32e7bc7cd53781ef6671f88cda.png" alt="" />
<img src="https://tongli.sz-trip.com/uploads/20240912/bfc1df32e7bc7cd53781ef6671f88cda.png" alt="" />
<img src="https://tongli.sz-trip.com/uploads/20240912/bfc1df32e7bc7cd53781ef6671f88cda.png" alt="" />
<img src="https://tongli.sz-trip.com/uploads/20240912/bfc1df32e7bc7cd53781ef6671f88cda.png" alt="" />
</div>
<div class="scenic-detail flex-between" :style="{marginTop: type ? '' : '0'}">
<div class="scenic-left flex-between">
<div class="text-overflow" v-if="type">营业时间0900-1700</div>
<div :class="[type ? 'text-overflow' : 'text-overflowRows']">地址盐城市盐都区学富镇周伙盐城市盐都区学富镇周伙</div>
</div>
<div class="scenic-right flex-between">
<div v-if="type">
<img src="https://static.ticket.sz-trip.com/yandu/images/map/pause.png" v-if="isAudioPlay" @click="audioPlay(false)">
<img src="https://static.ticket.sz-trip.com/yandu/images/map/play.png" v-else @click="audioPlay(true)">
<div>讲解</div>
</div>
<div v-else></div>
<div @click="util.openMap()">
<img src="https://static.ticket.sz-trip.com/yandu/images/map/navigation.png" alt="">
<div>导航</div>
</div>
</div>
</div>
<div class="scenic-content" v-if="type">
简介:
<div>
草房子乐园是童话文学的IP实景还原是国内较早儿童文学实景体验基地较早乡野童话体验基地和儿童文学研学基地致力于打造集旅游住宿餐饮服务休闲娱乐文化传播研学美育亲子互动青少年实践爱心公益于一体的社会化服务新平台研学赋能景区瞄准研学+新领域建设标准化研学场景葳蕤的田园风光推出亲子研学个性化的消费体验产品和游玩项目
</div>
</div>
<!-- 音频讲解 -->
<audio src="" ref="audio" controls v-show="false"></audio>
</div>
</template>
<script>
export default {
props: {
type: {
type: Boolean,
default: true
}
},
data() {
return {
isAudioPlay: false,
}
},
mounted() {
this.$refs.audio.src = "https://static.ticket.sz-trip.com/uploads/20220419/47fcfa763aa0d3283a2d47e3cce71428.mp3"
},
methods: {
audioPlay(status) {
console.log(this.$refs.audio.paused)
if(status) {
this.$refs.audio.play()
this.isAudioPlay = true
}else {
this.$refs.audio.pause()
this.isAudioPlay = false
}
}
}
}
</script>
<style lang="scss" scoped>
.bg {
width: 750px;
max-height: 60vh;
overflow-x: hidden;
overflow-y: auto;
padding: 0 40px 40px;
}
.top {
height: 156px;
font-weight: 500;
font-size: 40px;
color: #000000;
.top-btn {
width: 133px;
line-height: 59px;
background: #F74A57;
border-radius: 13px;
text-align: center;
font-size: 32px;
color: #FFFFFF;
}
}
.img-box {
overflow-x: auto;
display: flex;
overflow-x: auto;
img {
width: 180px;
height: 180px;
border-radius: 27px;
margin-right: 27px;
object-fit: cover;
}
}
.img-box::-webkit-scrollbar {
display: none;
}
.scenic-detail {
width: 670px;
height: 145px;
background: #E8F6EB;
border-radius: 13px;
margin-top: 36px;
padding: 30px 20px 30px 25px;
font-weight: 500;
font-size: 27px;
color: #000000;
.scenic-left {
flex-direction: column;
align-items: left;
width: 450px;
height: 100%;
div {
width: 450px;
}
}
.scenic-right {
width: 150px;
font-weight: 500;
font-size: 24px;
color: #71B580;
img {
width: 37px;
height: 37px;
margin-bottom: 5px;
}
div {
text-align: center;
}
}
}
.scenic-content {
margin-top: 42px;
font-weight: bold;
font-size: 32px;
color: #000000;
div {
font-weight: 500;
font-size: 27px;
color: #333333;
line-height: 44px;
margin-top: 10px;
}
}
</style>

79
vue.config.js

@ -0,0 +1,79 @@
const path = require('path') // 引入path模块
// const HtmlWebpackPlugin = require('html-webpack-plugin');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
function resolve(dir) {
return path.join(__dirname, dir) // path.join(__dirname)设置绝对路径
}
const {
defineConfig
} = require('@vue/cli-service')
module.exports = defineConfig({
lintOnSave: false, // 关闭eslint校验
transpileDependencies: true,
publicPath: '/',
// 将构建好的文件输出到哪里
outputDir: process.env.VUE_APP_OUTPUTDIR,
// 放置生成的静态资源(js、css、img、fonts)的目录
assetsDir: 'static',
// 指定生成的 index.html 的输出路径
indexPath: 'index.html',
// 是否在构建生产包时生成 sourceMap 文件,false将提高构建速度
productionSourceMap: false,
// 是一个函数,允许对内部的 webpack 配置进行更细粒度的修改。
chainWebpack: (config) => {
// 配置别名
config.resolve.alias
.set('@', resolve('src'))
.set('assets', resolve('src/assets'))
.set('components', resolve('src/components'))
.set('views', resolve('src/views'))
config.optimization.minimizer('terser').tap((args) => {
// 去除生产环境console
args[0].terserOptions.compress.drop_console = true
return args
})
},
// 配置 Webpack 相关的配置项
configureWebpack: config => {
// 配置 Webpack 插件
// config.plugins.push(new HtmlWebpackPlugin({
// template: './public/index.html'
// }))
if(process.env.NODE_ENV == 'production'){
config.plugins.push(new BundleAnalyzerPlugin({
analyzerMode: 'server',
analyzerHost: '127.0.0.1',
analyzerPort: 8888, //注意是否有端口冲突
reportFilename: 'report.html',
defaultSizes: 'parsed',
openAnalyzer: true,
generateStatsFile: false,
statsFilename: 'stats.json',
statsOptions: null,
logLevel: 'info'
}))
}
},
// 配置开发服务器
// 本地项目运行时的环境配置
devServer: {
host: 'localhost',
port: 8080, // 端口号
https: false,
open: false, // 配置自动启动浏览器 open: 'Google Chrome'-默认启动谷歌
hot: true, // 热更新
proxy: {
'/api': {
target: 'http://localhost:3000', // 要访问的跨域的域名
ws: true, // 是否启用websockets
changeOrigin: true, // 开启代理:在本地会创建一个虚拟服务端,然后发送请求的数据,并同时接收请求的数据,这样客户端端和服务端进行数据的交互就不会有跨域问题
pathRewrite: {
'^/api': ''
}
}
}
}
})
Loading…
Cancel
Save