vuevue-znly
Posted smart-girl
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了vuevue-znly相关的知识,希望对你有一定的参考价值。
老规矩,放下博主的项目地址:https://github.com/wohaiwo/vue-znly
我一直在想给那些开源者取什么名字比较好,怎样才对得起他们开源项目的精神,后来想想,还是叫博主吧。有的人用语言表达技术,有的人用代码表达技术。
接下来我们还是来看项目效果吧
我们可以看到这个项目内容还是挺多的,里面缺少一些内容,但是不影响我们研究这个项目
我们看index.html可以发现,这个项目用到了vue和jquery结合做的。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, height=device-height, user-scalable=no, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0">
<meta name="keywords" content="今世缘 景区">
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-touch-fullscreen" content="yes">
<meta name="renderer" content="webkit">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="apple-mobile-web-app-capable" content="yes" />
<title>今世缘景区欢迎您</title>
<link rel="shortcut icon" href="/static/logo/favicon.ico" type="image/x-icon" />
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
<meta name="format-detection"content="telephone=no, email=no" />
<meta http-equiv="refresh" content="2000;url=http://www.baidu.com" />
</head>
<body>
<div id="app">
<router-view></router-view>
</div>
<script type="text/javascript" src="http://api.map.baidu.com/api?v=1.4"></script>
<script type="text/javascript" src="../static/lib/js/jquery-3.2.1.slim.min.js"></script>
</body>
</html>
main.js中引入入口文件App.vue
//main.js
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue';
import App from './App';
import VueRouter from 'vue-router';
import routes from './router/router.js';
import axios from 'axios';
// 解决30秒延迟问题
import FastClick from 'FastClick';
Vue.use(VueRouter); // 加载vue-router插件
Vue.prototype.$http = axios;
FastClick.attach(document.body);
// 创建 router 实例,然后传 `routes` 配置
const router = new VueRouter(
mode: 'hash',
routes
);
// 创建和挂载根实例
var vm = new Vue(
router,
components: App
).$mount('#app')
//app.vue中还加入了动画
<template>
<div>
<transition name="router-fade" mode="out-in">
<router-view></router-view>
</transition>
</div>
</template>
<script>
import './static/lib/css/main.css'
import './static/lib/css/reset.css'
export default
</script>
<style lang="scss">
.router-fade-enter-active, .router-fade-leave-active
transition: opacity .3s;
.router-fade-enter, .router-fade-leave-active
opacity: 0;
.router-slid-enter-active, .router-slid-leave-active
transition: all .4s;
.router-slid-enter, .router-slid-leave-to
transform: translate3d(-100px, 0, 0);
opacity: 0;
</style>
router中也是使用懒加载的模式,可以看到首先加载定向的是我们的home组件在app.vue中渲染出来
//router.js
import App from '../App.vue';
// Webpack 会将任何一个异步模块与相同的块名称组合到相同的异步块中。
const home = resolve => require(['../page/home.vue'], resolve); // 首页
const introduction = resolve => require(['../page/scenicIntroduction.vue'], resolve);
const listDetail = resolve => require(['../components/listDetail.vue'], resolve);
const travelBox = resolve => require(['../page/travelBox.vue'], resolve);
const externalMap = resolve => require(['../page/externalMap.vue'], resolve);
const service = resolve => require(['../page/service.vue'], resolve);
const dropBox = resolve => require(['../components/dropBox.vue'], resolve);
// 定义路由
const routes = [
path: '/',
component: App,
children: [
path: '/',
redirect: name: 'home'
,
path: '/home',
name: 'home',
component: home
,
path: '/scenic/introduction',
name: 'introduction',
component: introduction
,
path: '/scenic/detail/:id/:type/:identifier',
name: 'listDetail',
component: listDetail
,
path: '/travelBox',
name: 'travelBox',
component: travelBox
,
path: '/externalMap',
name: 'externalMap',
component: externalMap
,
path: '/service/:type',
name: 'service',
component: service
,
path: '/dropBox/:url/:title',
name: 'dropBox',
component: dropBox
]
]
export default routes;
我们来看home组件,里面的代码挺有意思的,
先看header组件
//header.vue
<template>
<header>
<slot name="logo"></slot>
<span class="left-icon" v-if="goBack" @click="$router.go(-1)">
<i title="返回" class="iconfont"></i>
</span>
<span class="left-icon side-bar" v-if="sideBar" @click="showSideBar">
<i title="主菜单" class="iconfont"></i>
</span>
<span class="title-text" v-if="headTitle">headTitle</span>
<transition name="slide-fade">
<nav v-show="isShowSideBar">
<router-link to="/scenic/introduction"><i class="iconfont"></i>景区介绍</router-link>
<router-link :to="name: 'service', params: type: 3"><i class="iconfont"></i>景区公告</router-link>
<router-link :to="name: 'service', params: type: 15"><i class="iconfont"></i>景区服务</router-link>
<router-link :to="name: 'service', params: type: 13"><i class="iconfont"></i>预订门票</router-link>
<router-link :to="name: 'service', params: type: 14"><i class="iconfont"></i>特色购物</router-link>
<router-link to="/travelBox"><i class="iconfont"></i>旅行百宝箱</router-link>
<router-link :to="name: 'dropBox', params: url: vrUrl, title: vrTitle"><i class="iconfont"></i>虚拟旅游</router-link>
<router-link :to="name: 'service', params: type: 6"><i class="iconfont"></i>餐饮住宿</router-link>
</nav>
</transition>
</header>
</template>
<script>
export default
data()
return
,
props: ['goBack', 'headTitle', 'sideBar', 'isShowSideBar', 'vrUrl', 'vrTitle'],
methods:
// 子组件通过emit向父组件传递事件的函数名
showSideBar()
this.$emit('breadcrumb');
</script>
<style scoped lang="scss">
$nav-color: #e60012;
header
position: fixed;
left: 0;
top: 0;
z-index: 100;
width: 100%;
height: 40px;
line-height: 40px;
color: $nav-color;
background: #fff ;
text-align: center;
border-bottom: 2px solid #ededed;
box-sizing: border-box;
span
font-size: 18px;
font-weight: bold;
.left-icon
position: absolute;
left: 0;
top: 50%;
width: 50px;
transform: translateY(-50%);
i
color: $nav-color;
.side-bar
left: 0;
width: 10%;
background: $nav-color;
i
color: #fff;
nav
position: fixed;
left: 0;
right: 0;
top: 40px;
bottom: 50px;
z-index: 20;
width: 140px;
background: #fff;
a
display: block;
height: 50px;
line-height: 50px;
text-align: left;
padding-left: 8%;
box-sizing: border-box;
color: #000;
&:not(:last-child)
border-bottom: 1px solid #e6e6e6;
i
color: $nav-color;
margin-right: 20px;
.slide-fade-enter-active, .slide-fade-leave-active
transition: all 0.3s ease-in;
.slide-fade-enter, .slide-fade-leave-to
opacity: 0;
transform: translate3d(-150px, 0, 0);
// 适配一体机样式
@media screen and (min-width: 1000px)
$header-height: 100px;
i
font-size: 36px;
header
font-size: 32px;
height: $header-height;
line-height: $header-height;
border-bottom: 4px solid #ededed;
span
font-size: 45px;
font-weight: bold;
.left-icon
width: $header-height;
nav
top: $header-height;
bottom: $header-height;
width: 300px;
a
height: $header-height;
line-height: $header-height;
border-bottom: 3px solid #e6e6e6;
</style>
userCount.vue不知道表达什么意思?
//userCount.vue
//src\\components\\userCount.vue
<template>
<div>
<p> msg </p>
</div>
</template>
<script>
export default
data()
return
msg: ''
,
created()
let url = '/JSY_H5/h5/statistics';
this.$http.get(url).then((response) =>
this.$data.msg = response.data.data.msg;
, (response) =>
console.log('oops, data is error');
);
</script>
<style scoped lang="scss">
div
position: relative;
width: 100%;
height: 20px;
padding: 0 4%;
margin-top: 40px;
font-size: 14px;
color: #ddd;
background: rgba(0, 0, 0, .4);
overflow: hidden;
z-index: 40;
user-select: none;
box-sizing: border-box;
p
left: 100%;
position: absolute;
z-index: 40;
white-space: nowrap;
animation-delay: 1s;
animation-name: slide;
animation-duration: 45s;
animation-iteration-count: infinite;
@media screen and (min-width: 1000px)
div
height: 60px;
margin-top: 100px;
font-size: 32px;
p
line-height: 60px;
@keyframes slide
0% left: 100%;
100% left: -120%;
</style>
//footer.vue
<template>
<footer>
<ul>
<li>
<router-link :to="name: 'service', params: type: 15" :class=" pathName == navUrl[0] ? 'active' : ''">
<i class="iconfont"></i><span>景区服务</span>
</router-link>
</li>
<li>
<router-link to="/home" :class=" pathName == navUrl[1] ? 'active' : ''">
<i class="iconfont"></i><span>主页</span>
</router-link>
</li>
<li>
<router-link :to="name: 'service', params: type: 6" :class=" pathName == navUrl[2] ? 'active' : ''">
<i class="iconfont"></i><span>餐饮住宿</span>
</router-link>
</li>
</ul>
</footer>
</template>
<script>
export default
data()
return
isShow: false,
navUrl : [0, 1, 2]
,
props: ['pathName'],
created()
,
methods:
showNav(state)
this.$data.isShow = state ? false : true;
</script>
<style scoped lang="scss">
footer
display: block;
width: 100%;
height: 50px;
position: fixed;
left: 0;
bottom: 0;
z-index: 100;
color: #000;
background: #fff;
ul
height: 100%;
overflow: hidden;
li
display: inline-block;
position: relative;
float: left;
width: 33.33%;
height: 100%;
box-sizing: border-box;
.iconfont
display: block;
margin-top: 4px;
font-size: 20px;
// 菜单栏选中点击样式
.active
i, span
color: #e60012;
a
display: block;
width: 100%;
height: 100%;
font-size: 14px;
color: #5D656B;
text-align: center;
.slide-fade-enter-active
transition: all .3s ease;
.slide-fade-leave-active
transition: all .8s cubic-bezier(1.0, 0.5, 0.8, 1.0);
.slide-fade-enter, .slide-fade-leave-to
transform: translate3d(0, 100%, 0);
opacity: 0;
@media screen and (min-width: 1000px)
footer
height: 100px;
ul
li
.iconfont
font-size: 48px;
a
font-size: 24px;
</style>
home.vue引入了这几个组件
//home.vue
<template>
<div id="main">
<v-header sideBar="true" :isShowSideBar="isShowSideBar" :vrUrl="vRinfo.jumpUrl" :vrTitle="vRinfo.title" @breadcrumb="showSideBar">
<span class="header-logo" slot="logo"><img class="logo" :src="logoImgUrl" alt="logo-title"></span>
</v-header>
<user-count></user-count>
<!-- 首页滚动banner -->
<div class="banner">
<div class="swiper-container" @click="closeSideBar">
<div class="swiper-wrapper">
<!-- 从后端取数据进行渲染的 -->
<div class="swiper-slide" v-for="item in imageDataArr">
<img :src="item.INFO_IMAGE_URL" :alt="item.INFO_TITLE">
</div>
</div>
<!-- 如果需要分页器 -->
<div class="swiper-pagination swiper-pagination-white"></div>
</div>
<nav class="right-side">
<router-link :to="name: 'service', params: type: 13"><span>预订</span><span>门票</span></router-link>
<router-link v-if="isApp" :to="name: 'dropBox', params: url: vRinfo.jumpUrl, title: vRinfo.title"><span>虚拟</span><span>旅游</span></router-link>
<a v-if="!isApp" target="_blank" :href = "vRinfo.jumpUrl"><span>虚拟</span><span>旅游</span></a>
<a @click="showSideBar"><span>更多</span><span>功能</span></a>
</nav>
</div>
<v-footer :pathName="1"></v-footer>
</div>
</template>
<script>
import Vue from 'vue';
import vHeader from '../components/header'
import userCount from '../components/userCount'
import vFooter from '../components/footer.vue'
import '../static/lib/js/swiper.min.js'
import '../static/lib/css/swiper.min.css'
export default
data()
return
isApp: false, // 是否是园区一体机
isShowSideBar: false,
imageDataArr: [], // 首页轮播图
vRinfo: // 虚拟旅游
title: '',
jumpUrl: ''
,
logoImgUrl: ''
,
components:
vHeader, userCount, vFooter
,
created()
,
mounted()
let isApp = window.localStorage ? localStorage.getItem('isApp') : Cookie.read('isApp');
// 浏览器本地存储是否是一体机
// 判断本地缓存里面是否已经存在isApp
if(isApp == 'true')
this.logoImgUrl = '../static/logo/logo-red-pc.png';
this.isApp = true;
else
// 判断是否是第一次进来首页,如果是,则获取params的参数
this.isApp = this.$route.query && this.$route.query.app;
if(this.isApp == 'true')
// 保存到全局变量中
if(window.localStorage)
localStorage.setItem('isApp', this.isApp);
else
Cookie.wirte('isApp', this.isApp);
this.logoImgUrl = '../static/logo/logo-red-pc.png';
else
this.logoImgUrl = '../static/logo/logo-red-h5.png';
this.initPage();
this.getVRTravel();
,
methods:
initPage()
let url = `/JSY_H5/h5/queryServiceList?type=1`;
this.$http.get(url).then((response) =>
this.imageDataArr = response.data.rows;
// vue.nextTick在页面初始挂载就要渲染好轮播
Vue.nextTick(function()
new Swiper('.swiper-container',
autoplay: 10000,
pagination: '.swiper-pagination',
loop: true
);
);
, (response) =>
console.log('oops, data is not found');
);
,
getVRTravel()
let url = '/JSY_H5/h5/queryServiceList?type=16';
this.$http.get(url).then((response) =>
// 遍历数据,改变数据结构,套用同一天模板listTpl
this.$data.vRinfo['title'] = response.data.rows[0]['INFO_TITLE'];
this.$data.vRinfo['jumpUrl'] = response.data.rows[0]['JUMP_URL'];
);
,
showSideBar()
this.isShowSideBar = !this.isShowSideBar;
console.log("this.isShowBar",this.isShowSideBar)
,
closeSideBar()
this.isShowSideBar = false;
</script>
<style scoped lang="scss">
#main
font-family: "Microsoft Yahei", 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
.header-logo
display: inline-block;
width: 100%;
height: 40px;
box-sizing: border-box;
.logo
height: 100%;
.banner
$right-side-size: 50px;
.swiper-container
position: fixed;
left: 0;
right: 0;
top: 40px;
bottom: 50px;
z-index: 10;
overflow: hidden;
.swiper-slide img
width: 100%;
height: 100%;
.right-side
position: fixed;
right: 4%;
bottom: 70px;
z-index: 10;
width: $right-side-size;
a
display: inline-block;
width: $right-side-size;
height: $right-side-size;
color: #fff;
background: #e60012;
border: 2px solid #fff;
padding: 5px;
border-radius: 50%;
box-shadow: 0 0 10px 0 rgba(0, 0, 0, .5);
box-sizing: border-box;
span
display: block;
font-size: 14px;
height: 18px;
line-height: 18px;
&:not(:last-child)
margin-bottom: 10px;
// 适配一体机样式
@media screen and (min-width: 1000px)
$right-side-size: 150px;
.header-logo
height: 100px;
.banner
.swiper-container
top: 100px;
bottom: 100px;
.right-side
right: 4%;
bottom: 50%;
width: $right-side-size;
transform: translate3d(0, 50%, 0);
a
width: $right-side-size;
height: $right-side-size;
padding: 12px;
border: 5px solid #fff;
span
font-size: 45px;
height: 55px;
line-height: 55px;
</style>
在景区页面中引入了listTpl.vue
//listTpl.vue
<template>
<div>
<div class="list-tpl" >
<ul v-if="!isType" class="list-item">
<li v-for="item in items">
<router-link :to="name: 'listDetail', params: id: item.id, type: type, identifier: identifier">
<div class="list-image">
<img :src="item.imageUrl">
</div>
<aside >
<h3> item.title </h3>
<article> item.description </article>
</aside>
</router-link>
</li>
</ul>
<ul v-if="isType" class="list-item">
<li v-for="item in items">
<div class="list-image">
<img :src="item.imageUrl">
</div>
<aside >
<h3> item.title </h3>
<article v-html= "item.description"></article>
<a class="jump-url" v-if="!isApp" :href="item.jumpUrl" target="_blank">去预订</a>
<a class="jump-url" v-if="isApp" @click="showQRCode(item.qrCode)">去预订</a>
</aside>
</li>
</ul>
</div>
<div v-if="isShowQrBox" id="qrcode" @click="closeQrcodeBox">
<div class="mask"></div>
<div id="qrcode-content"></div>
</div>
</div>
</template>
<script>
import Vue from 'vue';
import '../static/lib/js/jquery.qrcode.min.js';
export default
data()
return
isApp: false, // 判断是否使用不同的遍历模块, true => 调出二维码模板
isType: false, // 当type = 13 || 14时,显示去预定的模板
isShowQrBox: false
,
props: ['identifier', 'items', 'type'],
created()
// 在listTpl页面中 只在预订门票和特色购物里面调出而二维码
if(this.type == 13 || this.type == 14)
this.isType = true;
// 判断是否是园区一体机
let isApp = window.localStorage ? localStorage.getItem('isApp') : Cookie.read('isApp');
if(isApp == 'true')
this.isApp = true;
,
methods:
showQRCode(url)
this.isShowQrBox = true;
jQuery('#qrcode #qrcode-content').empty();
Vue.nextTick(function()
jQuery('#qrcode #qrcode-content').qrcode(url);
);
,
// 关闭二维码框
closeQrcodeBox()
this.isShowQrBox = false;
</script>
<style scoped lang="scss">
.list-tpl
margin-top: 45px;
.list-item
margin: 10px auto;
list-style: none;
height: 100vh;
overflow: auto;
background: #EDEDED;
li
display: inline-block;
width: 100%;
padding: 2%;
margin-bottom: 10px;
overflow: hidden;
box-sizing: border-box;
background: #fff;
a
display:inline-block;
.list-image
float: left;
width: 30vw;
height: 30vw;
margin-right: 3vw;
box-sizing: border-box;
img
width: 100%;
height: 100%;
aside
position: relative;
min-height: 30vw;
font-size: 14px;
text-align: left;
overflow: hidden;
text-overflow: ellipsis;
box-sizing: border-box;
h3
color: #CD1940;
padding: 2px 0 5px 0;
font-size: 16px;
font-weight: bold;
article
font-size: 14px;
color: #000;
line-height: 1.4;
text-align: justify;
.jump-url
position: absolute;
right: 0;
bottom: 0;
width: 60px;
height: 30px;
line-height: 30px;
text-align: center;
color: #FFF;
background: #e60012;
box-sizing: border-box;
#qrcode
position: relative;
.mask
position: fixed;
display: block;
left: 0;
right: 0;
top: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.4);
z-index: 10;
#qrcode-content
position: fixed;
left: 50%;
top: 50%;
padding: 15px;
text-align: center;
background: #fff;
z-index: 100;
transform: translate3d(-50%, -50%, 0);
&:after
content: '扫一扫上面的二维码图案';
display: block;
padding-top: 10px;
@media screen and (min-width: 1000px)
.list-tpl
margin-top: 100px;
.list-item
li
aside
h3
font-size: 38px;
article
font-size: 32px;
.jump-url
position: absolute;
right: 0;
bottom: 0;
width: 120px;
height: 60px;
line-height: 60px;
text-align: center;
color: #FFF;
font-size: 24px;
background: #e60012;
box-sizing: border-box;
</style>
/#/?app=true
我们其实可以猜测这个组件里面主要是展示景区相关图片,并且还有二维码扫码的功能,用jQuery做的
完整的景区介绍的代码
<template>
<div>
<v-header goBack="true" headTitle="景区介绍"></v-header>
<list-tpl :items="scenicInfo" :type="type" identifier="1"></list-tpl>
<loading :show="done"></loading>
</div>
</template>
<script>
import vHeader from '../components/header.vue';
import listTpl from '../components/listTpl.vue';
import loading from '../components/loading.vue';
export default
data()
return
done: false,
type: '0', // 这个类型应该是字符串,需要跟路由匹配到
scenicInfo: []
,
components:
vHeader, listTpl, loading
,
mounted()
// 页面初始化时加载数据
this.initPage();
,
methods:
initPage()
this.done = true;
let url = '/JSY_H5/h5/querySSSList';
this.$http.get(url).then((response) =>
// 遍历数据,改变数据结构,套用同一套模板listTpl
response.data.rows.forEach((item, index) =>
let tmp = ;
tmp['description'] = item['SS_DESCRIPTION'];
tmp['id'] = item['SS_NO'];
tmp['imageUrl'] = item['SS_IMAGE_URL'];
tmp['title'] = item['SS_TITLE'];
this.$data.scenicInfo.push(tmp);
);
this.$data.done = false;
, (response) =>
this.$data.done = false;
);
</script>
//loading.vue
<template>
<transition>
<svg class="spinner" :class=" show: show " v-show="show" width="68px" height="68px" viewBox="0 0 44 44">
<circle class="path" fill="none" stroke-width="4" stroke-linecap="round" cx="22" cy="22" r="20"></circle>
</svg>
</transition>
</template>
<script>
export default
props: ['show']
</script>
<style lang="scss">
$offset: 126;
$duration: 1.4s;
.spinner
position: fixed;
z-index: 999;
transition: opacity .15s ease;
animation: rotator $duration linear infinite;
animation-play-state: paused;
right: 50%;
top: 20%;
margin-right: -34px;
&.show
animation-play-state: running
&.v-enter, &.v-leave-active
opacity: 0;
&.v-enter-active, &.v-leave
opacity: 1;
@keyframes rotator
0%
transform: scale(0.5) rotate(0deg);
100%
transform: scale(0.5) rotate(270deg);
.spinner .path
stroke: #42b983;
stroke-dasharray: $offset;
stroke-dashoffset: 0;
transform-origin: center;
animation: dash $duration ease-in-out infinite;
@keyframes dash
0%
stroke-dashoffset: $offset;
50%
stroke-dashoffset: ($offset/2) transform rotate(135deg);
100%
stroke-dashoffset: $offset transform rotate(450deg);
</style>
//src\\page\\service.vue
<template>
<div>
<v-header goBack="true" :headTitle="headTitle"></v-header>
<list-tpl :items="serviceInfo" :type="type" identifier="2"></list-tpl>
<v-footer :pathName="index" v-show=" type == 6 || type == 15"></v-footer>
<loading :show="done"></loading>
</div>
</template>
<script>
import vHeader from '../components/header.vue';
import listTpl from '../components/listTpl.vue';
import loading from '../components/loading.vue';
import vFooter from '../components/footer.vue';
export default
data()
return
type: null,
done: false,
index: 0, // 动态显示footer导航栏显示位置
serviceInfo: []
,
computed:
headTitle: function()
let type = `$this.type`;
switch(type)
case '3':
type = '景区公告';
break;
case '6':
type = '餐饮住宿';
break;
case '7':
type = '周边景点';
break;
case '13':
type = '预订门票';
break;
case '14':
type = '特色购物';
break;
case '15':
type = '景区服务';
break;
return type;
,
,
components:
vHeader, listTpl, loading, vFooter
,
created()
this.type = this.$route.params.type;
,
mounted()
// 页面初始化时加载数据
this.initPage();
,
// 只在当前路由改变,但是该组件被复用时调用
// to 表示 route即将要进去的路由
// from 表示 route正要离开的路由
beforeRouteUpdate(to, from, next)
this.type = to.params.type;
next(this.initPage());
,
methods:
initPage()
// 判断footer底部导航栏的显示位置
if(this.type == 6)
this.index = 2;
else if (this.type == 15)
this.index = 0;
this.$data.serviceInfo = []; // 初始化数据,防止footer底部导航栏切换数据没有清空
this.done = true;
let url = `/JSY_H5/h5/queryServiceList?type=$this.type`;
this.$http.get(url).then((response) =>
// 遍历数据,改变数据结构,套用同一天模板listTpl
response.data.rows.forEach((item, index) =>
let tmp = ;
tmp['description'] = item['INFO_DESCRIPTION'];
tmp['id'] = item['INFO_NO'];
tmp['imageUrl'] = item['INFO_IMAGE_URL'];
tmp['title'] = item['INFO_TITLE'];
tmp['qrCode'] = item['QR_CORE_URL'];
tmp['jumpUrl'] = item['JUMP_URL'];
this.$data.serviceInfo.push(tmp);
);
this.$data.done = false;
, (response) =>
this.$data.done = false;
);
</script>
//src\\page\\travelBox.vue
<template>
<div>
<v-header goBack="true" headTitle="旅行百宝箱"></v-header>
<div class="travel-box">
<section class="item-box">
<router-link :to="name: 'listDetail', params: id: 5, type: 20, identifier: 0">
<i class="iconfont"></i>
<span>旅游线路</span>
</router-link>
<router-link :to="name: 'externalMap'">
<i class="iconfont"></i>
<span>外部交通</span>
</router-link>
<router-link :to="name: 'listDetail', params: id: 4, type: 21, identifier: 0">
<i class="iconfont"></i>
<span>景区地图</span>
</router-link>
<router-link :to="name: 'service', params: type: 7">
<i class="iconfont"></i>
<span>周边景点</span>
</router-link>
<router-link :to="name: 'service', params: type: 6">
<i class="iconfont"></i>
<span>餐饮,住宿</span>
</router-link>
</section>
</div>
</div>
</template>
<script>
import vHeader from '../components/header'
export default
data()
return
,
components:
vHeader
,
mounted()
</script>
<style scoped lang="scss">
.travel-box
margin-top: 42px;
height: 100vh;
background: #F5F5F5;
a
display: inline-block;
width: 32%;
height: 100px;
margin: 0 2% 2% 0;
color: #5D656B;
background: #fff;
text-align: center;
box-sizing: border-box;
&:nth-child(3n + 0)
margin-right: 0;
i
display: block;
font-size: 48px;
line-height: 60px;
margin-top: 10px;
span
font-size: 16px;
@media screen and (min-width: 1000px)
.travel-box
margin-top: 100px;
a
height: 200px;
i
font-size: 64px;
line-height: 100px;
span
font-size: 24px;
</style>
//listDetail
<template>
<div class="detail">
<v-header goBack="true" :headTitle="listDetail.title"></v-header>
<div class="audio-play" v-if="this.identifier == 1 && listDetail.audio">
<i v-on:click="playAudio" class="iconfont"> 音频播放</i>
<audio id="audio" :src="listDetail.audio" loop="true">
你的浏览器不支持 <code>audio</code> 音频播放功能.
</audio>
</div>
<div class="detail-body" v-show="isShow">
<section v-html="listDetail.content"></section>
<review :id="detailId" :qrCodeUrl="qrCodeUrl" v-if="needReview"></review>
</div>
<loading :show="done"></loading>
</div>
</template>
<script>
import loading from '../components/loading.vue';
import vHeader from '../components/header.vue';
import review from '../components/review.vue';
export default
data()
return
done: false,
isShow: false, // 只有当数据加载完成之后才能够实现出来
needReview: false, //是否需要显示评论(只有景点才需要,其他的地方都是不需要的,默认关闭)
detailId: '',
type: '', // 判断当前的模块信息
identifier: '', // 标识符 景点介绍模块为1 旅行百宝箱模块为0
shopUrl: '',
qrCodeUrl: '', // 二维码的生成地址
listDetail: // 详情列表信息
title: '',
content: '',
audio: null
,
components:
loading, vHeader, review
,
created()
this.detailId = this.$route.params.id;
this.identifier = this.$route.params.identifier || 0;
this.type = this.$route.params.type;
,
mounted()
this.initPage();
,
methods:
initPage()
let listDetailUrl = '';
this.$data.done = true;
if(this.identifier == 1)
listDetailUrl = `/JSY_H5/h5/querySSSOne?id=$this.detailId`; // 景点介绍调用的接口
else if(this.identifier == 2)
listDetailUrl = `/JSY_H5/h5/queryServiceOne?id=$this.detailId`; // service.vue下面过来调用接口
else
listDetailUrl = `/JSY_H5/h5/queryServiceList?type=$this.detailId`; // 旅游线路,景区地图调用的接口
this.$http.get(listDetailUrl).then((response) =>
this.done = false;
this.isShow = true;
let data = response.data.rows;
// 景点介绍
if(this.identifier == 1)
this.listDetail.title = data[0].SS_TITLE;
this.listDetail.content = data[0].SS_CONTENT;
this.listDetail.audio = data[0].SS_VIDEO_URL;
this.qrCodeUrl = data[0].QR_CORE_URL;
this.needReview = true;
else
// 资讯
this.listDetail.title = data[0].INFO_TITLE;
this.listDetail.content = data[0].INFO_CONTENT;
this.needReview = false;
// 由于后台传过来是一段字符串 需要使用正则来适配一体机文字大小
let isApp = window.localStorage ? localStorage.getItem('isApp') : Cookie.read('isApp');
if(isApp == 'true')
this.listDetail.content = this.listDetail.content.replace(/font-size:\\s*\\d+px;/g, 'font-size: 32px;');
, (response) =>
console.log('opps Is Error: ' + response);
this.done = false;
)
,
playAudio()
let audio = document.getElementById('audio');
var isPlaying = audio.currentTime > 0 && !audio.paused && !audio.ended
&& audio.readyState > 2;
if (!isPlaying)
audio.load();
audio.play();
</script>
<style lang="scss">
.detail
padding-top: 40px; // 移除头部header的高度
.audio-play
width: 100%;
color: #fff;
padding: 1% 4%;
text-align: right;
background: #000;
opacity: .4;
box-sizing: border-box;
i
display: inline-block;
width: 100px;
height: 30px;
line-height: 30px;
.detail-body
padding: 10px 10px 40px 10px ;
section
width:100%;
img
width:100%;
footer
position: absolute;
right: 0;
bottom: 0;
width: 100%;
height: 40px;
background: #f6f6f6;
text-align: right;
a
display: inline-block;
width: 80px;
height: 40px;
line-height: 40px;
padding: 2px;
color: #FFF;
text-align: center;
background: #e60012;
box-sizing: border-box;
@media screen and (min-width: 1000px)
.detail
padding-top: 100px;
.audio-play
i
font-size: 32px;
width: 200px;
height: 50px;
line-height: 50px;
footer
height: 100px;
</style>
这个组件中引用了review组件
<template>
<div>
<div class="reviews" v-show="isShow">
<ul>
<li><i class="iconfont"></i> visitCount </li>
<li @click.once="upVote" :class="active: isActive "><i class="iconfont"></i> goodCount </li>
<li @click="showReviewBox"><i class="iconfont"></i>写评论</li>
<li @click="showCommentBox"><i class="iconfont"></i> reviewCount </li>
</ul>
</div>
<transition name="slide-fade-down">
<div v-show="isShowReviewBox" class="reviews-box">
<div class="header">
<span @click="closeReviewBox">取消</span>
<span>评论</span>
<span @click="addReview" :class="isSend">发送</span>
</div>
<div class="body">
<textarea v-model="reviewContent" autofocus maxlength="120" required></textarea>
</div>
</div>
</transition>
<transition name="slide-fade-right">
<div v-show="isShowCommentBox" class="comment-box">
<div @click.stop.prevent="closeCommentBox" class="mask"></div>
<div class="comment-main">
<section v-for="(item, index) in reviewData">
<div class="reviews-author"><span>游客</span></div>
<div class="reviews-body">
<p class="reviews-content"> item.SSR_CONTENT </p>
<p> item.ENTRY_DATE_TIME | time</p>
</div>
</section>
</div>
</div>
</transition>
<div v-if="isShowQrBox" id="qrcode" @click="closeQrcodeBox">
<div class="mask"></div>
<div id="qrcode-content"></div>
</div>
<div v-if="isShowTipBox" class="tip-box">
您的评论已经提交,请等待审核通过...
</div>
</div>
</template>
<script>
import Vue from 'vue';
import '../static/lib/js/jquery.qrcode.min.js'
export default
data()
return
isShow: true,
SS_NO: this.id, // 当前的景点id
isActive: false,
reviewData: [], // 评论数
visitCount: 0, // 访问数
goodCount: 0, // 点赞数
reviewCount: 0, // 评论数
reviewContent: '', // 评论内容
isShowReviewBox: false, // 是否显示评论框
isShowCommentBox: false, // 是否显示评论列表
isShowQrBox: false, // 是否显示二维码
isShowTipBox: false // 是否显示评论成功提示框
,
props: ['id', 'qrCodeUrl'],
mounted()
this.initPage();
,
computed:
isSend: function ()
return
active: !!this.$data.reviewContent.length
,
filters:
// 格式化时间
time: function(date)
if(!date) return '';
var date = new Date(date);
var Y = date.getFullYear() + '-';
var M = (date.getMonth()+1 < 10 ? '0'+(date.getMonth()+1) : date.getMonth()+1) + '-';
var D = (date.getDate() < 10 ? '0' + (date.getDate()) : date.getDate()) + ' ';
return Y + M + D;
,
methods:
// 评分
showRate(rate)
if(!rate) rate = 5;
return "★★★★★☆☆☆☆☆".slice(5 - rate, 10 - rate);
,
// 判断是否显示评论界面
showReviewBox()
let isApp = window.localStorage ? localStorage.getItem('isApp') : Cookie.read('isApp');
if(isApp == 'true') // 如果当前是一体机访问 则无法添加评论 调出二维码
let url = this.qrCodeUrl;
this.$data.isShowReviewBox = false;
this.isShowQrBox = true;
jQuery('#qrcode #qrcode-content').empty();
Vue.nextTick(function()
jQuery('#qrcode #qrcode-content').qrcode(url); // 使用ES6来进行字符串转义
);
else
this.$data.isShowReviewBox = !this.$data.isShowReviewBox;
,
showCommentBox()
if(this.reviewCount == 0) return false; // 如果当前的评论数为0 则不显示评论列表
this.$data.isShowCommentBox = !this.$data.isShowCommentBox;
,
// 关闭评论界面
closeReviewBox()
this.$data.isShowReviewBox = false;
,
closeCommentBox()
this.$data.isShowCommentBox = false;
,
// 添加评论
addReview()
let url = `/JSY_H5/h5/saveSSR`;
this.$http.post(url,
SS_NO: this.$data.SS_NO,
SSR_CONTENT: this.$data.reviewContent
).then( (response) =>
this.closeReviewBox();
this.reviewContent = '';
this.isShowTipBox = true;
// 需要使用箭头函数来邦定this的值
setTimeout(() =>
this.isShowTipBox = false;
, 1000);
, (response) =>
console.log('opps Is Error: ' + response);
)
,
initPage()
let url = `/JSY_H5/h5/querySSRList?id=$this.$data.SS_NO`;
this.$http.get(url).then((response) =>
this.$data.reviewData = response.data.rows;
, (response) =>
console.log('opps Is Error: ' + response);
);
this.getUserVisit(); // 获取评论接口中 访问量和点赞数
,
// 获取当前景点的页面访问量点赞数以及评论数
getUserVisit()
let url = `/JSY_H5/h5/addInteractive?id=$this.$data.SS_NO`; // 游客访问量
this.$http.get(url).then((response) =>
this.$data.goodCount = response.data.GOODED_COUNT;
this.$data.visitCount = response.data.LOOKED_COUNT;
this.$data.isActive = response.data.IS_GOODED;
this.$data.reviewCount = response.data.REVIEW_COUNT;
, (response) =>
console.log('opps Is Error: ' + response);
);
,
// 添加点赞
upVote()
let url = `/JSY_H5/h5/addInteractive?id=$this.$data.SS_NO&ACTION="good"`; // 当前景点-点赞数
this.$http.get(url).then((response) =>
this.$data.goodCount = response.data.GOODED_COUNT;
this.$data.isActive = true;
, (response) =>
console.log('opps Is Error: ' + response);
);
,
// 关闭二维码框
closeQrcodeBox()
this.isShowQrBox = false;
</script>
<style scoped lang="scss">
/* 底部详情操作框 */
.reviews
position: fixed;
bottom: 0;
left: 0;
right: 0;
height: 60px;
z-index: 100;
background: #F6F6F6;
ul
padding: 0 5%;
li
display: inline-block;
width: 25%;
height: 30px;
line-height: 30px;
margin: 15px 4% 0 0;
text-align: center;
border-radius: 10%;
background: #fff;
box-sizing: border-box;
i
margin-right: 5px;
&:last-child
color: #e60012;
width: 13%;
margin-right: 0;
/* 选中样式 */
.active
color: #e60012;
pointer-events: none;
/* 评论框基本样式 */
.reviews-box
position: fixed;
left: 0;
right: 0;
bottom: 0;
z-index: 200;
width: 100%;
height: 100px;
padding: 2% 8% 5%;
background: #F6F6F6;
box-sizing: border-box;
.header
width: 100%;
padding-bottom: 5px;
text-align: center;
span
color: #000;
&:first-child
float: left;
&:last-child
float: right;
color: #333;
pointer-events: none;
&.active
pointer-events: auto;
color: #e60012;
/* 用户编辑框 */
.body
textarea
min-height: 40px;
width: 100%;
padding: 2%;
font-size: 14px;
border-radius: 5%;
border: 2px solid #F6F6F6;
background: #fff;
box-sizing: border-box;
resize: none;
box-shadow: none;
/* 动画效果 */
.slide-fade-down-enter-active, .slide-fade-down-leave-active
transition: all 1s ease-in;
.slide-fade-down-enter, .slide-fade-down-leave-to
transform: translate3d(0, 100px, 0);
.slide-fade-right-enter-active, .slide-fade-right-leave-active
transition: all 1s ease-in;
.slide-fade-right-enter, .slide-fade-right-leave-to
transform: translate3d(100%, 0, 0);
.tip-box
position: absolute;
left: 50%;
top: 50%;
transform: translate3d(-50%, -50%, 0);
width: 200px;
height: 100px;
font-size: 18px;
text-align: justify;
padding: 10px 20px;
background: #fff;
box-shadow: 1px 1px 10px rgba(0, 0, 0, .5);
box-sizing: border-box;
/* 右侧评论列表 */
.comment-box
position: fixed;
top: 40px;
bottom: 50px;
right: 0;
width: 80%;
overflow-y: auto;
background: #F6F6F6;
.mask
position: fixed;
display: block;
left: 0;
right: 0;
top: 0;
bottom: 0;
background: rgba(0, 0, 0, .5);
z-index: 100;
.comment-main
position: relative;
z-index: 100;
section
min-height: 80px;
padding: 2%;
background: #fff;
overflow: hidden;
text-align: left;
font-size: 14px;
border-bottom: 2px solid #F6F6F6;
box-sizing: border-box;
.reviews-author
float: left;
width: 25%;
height: 80px;
padding-left: 4%;
margin-right: 2%;
color: #333;
box-sizing: border-box;
.reviews-body
min-height: 80px;
overflow: hidden;
.reviews-content
min-height: 40px;
p
&:first-child span
color: #e60012;
&:last-child
font-size: 12px;
@media screen and (min-width: 1000px)
.comment-box
top: 100px;
bottom: 60px;
.comment-main
section
font-size: 32px;
.reviews-body
p
&:last-child
font-size: 24px;
#qrcode
position: relative;
.mask
position: fixed;
display: block;
left: 0;
right: 0;
top: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.4);
z-index: 10;
#qrcode-content
position: fixed;
left: 50%;
top: 50%;
padding: 15px;
text-align: center;
background: #fff;
z-index: 100;
transform: translate3d(-50%, -50%, 0);
&:after
content: '扫一扫上面的二维码图案';
display: block;
padding-top: 10px;
</style>
//src\\page\\externalMap.vue
<template>
<div>
<v-header goBack="true" headTitle="外部地图"></v-header>
<div class="travel-box">
<div id="allmap"></div>
</div>
</div>
</template>
<script>
import vHeader from '../components/header'
export default
data()
return
,
components:
vHeader
,
mounted()
//百度地图API功能
var map = new BMap.Map("allmap"); // 创建Map实例
map.centerAndZoom(new BMap.Point(119.199201,34.019519), 18); // 初始化地图,设置中心点坐标和地图级别
map.addControl(new BMap.MapTypeControl()); //添加地图类型控件
map.setCurrentCity("淮安国缘宾馆"); // 设置地图显示的城市 此项是必须设置的
map.enableScrollWheelZoom(true); //开启鼠标滚轮缩放
// 编写自定义函数,创建标注
function addMarker(point)
var marker = new BMap.Marker(point);
map.addOverlay(marker);
// 向指定地图里面添加标注
addMarker(new BMap.Point(119.199201,34.019519));
</script>
<style scoped lang="scss">
.travel-box
margin-top: 60px;
#allmap
width: 100%;
height: 400px;
@media screen and (min-width: 1000px)
.travel-box
margin-top: 100px;
#allmap
height: 600px;
</style>
后记:我挺喜欢这个项目的代码的,哈哈哈,因为都看得懂,还能够猜到作者的意图。
以上是关于vuevue-znly的主要内容,如果未能解决你的问题,请参考以下文章