老规矩,放下博主的项目地址: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');// 浏览器本地存储是否是一体机// 判断本地缓存里面是否已经存在isAppif(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) => {// 遍历数据,改变数据结构,套用同一天模板listTplthis.$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) => {// 遍历数据,改变数据结构,套用同一套模板listTplresponse.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) => {// 遍历数据,改变数据结构,套用同一天模板listTplresponse.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">&nbsp;音频播放</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 旅行百宝箱模块为0shopUrl: '',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,             // 当前的景点idisActive: 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>

后记:我挺喜欢这个项目的代码的,哈哈哈,因为都看得懂,还能够猜到作者的意图。

转载于:https://www.cnblogs.com/smart-girl/p/11166096.html

【vue】vue-znly相关推荐

  1. 【Vue】—Vue拆分文件

    [Vue]-Vue拆分文件

  2. 【Vue】—Vue的模板语法

    [Vue]-Vue的模板语法 在template中写HTML时,如果需要渲染变量或者表达式,可以使用{{ }}形式来渲染,这个{{ }}就是模板语法,可以把变量或者表达式的结果渲染出来. 模板语法渲染 ...

  3. 【Vue】—Vue组件基本介绍

    [Vue]-Vue组件基本介绍 Vue组件是以.vue结尾的文件 Vue组件一般都是由三部分组成: template. script. style template写页面部分 script写js部分, ...

  4. 【Vue】—Vue脚手架创建项目时的 linter / formatter config配置选择

    [Vue]-Vue脚手架创建项目时的 linter / formatter config配置选择 ESLint with error prevention only 只进行报错提醒 ESLint + ...

  5. 【Vue】—Vue的基本介绍与插件安装

    [Vue]-Vue的基本介绍与插件安装 一.Vue的简介 Vue简介:Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架.与其它大型框架不同的是,Vue 被设计为可以 ...

  6. 【VUE】vue在vue-cli3环境下基于axios解决跨域问题

    [VUE]vue在vue-cli3环境下基于axios解决跨域问题 参考文章: (1)[VUE]vue在vue-cli3环境下基于axios解决跨域问题 (2)https://www.cnblogs. ...

  7. 【vue】---vue中使用async+await出现的问题及解决方案

    [vue]---vue中使用async+await出现的问题及解决方案 参考文章: (1)[vue]---vue中使用async+await出现的问题及解决方案 (2)https://www.cnbl ...

  8. 【前端】Vue+Element UI案例:通用后台管理系统-用户管理:Table表格增删查改、Pagination分页、搜索框

    文章目录 目标 代码 0.结构 1.按钮-删除 2.按钮-编辑 3.debug 4.样式 5.分页Pagination:功能 6.分页Pagination:样式 7.搜索框:功能 8.搜索框:样式 总 ...

  9. 【前端】Vue+Element UI案例:通用后台管理系统-用户管理:Form表单填写、Dialog对话框弹出

    文章目录 目标 代码 0.页面结构 1.新增按钮和弹出表单:结构 2.新增按钮和弹出表单:点击新增弹出表单 3.表单样式 4.表单验证 5.表单的提交和取消功能:接口.mock相关准备 6.表单的提交 ...

  10. 【前端】Vue项目:旅游App-博客总结

    博客 链接 [前端]Vue项目:旅游App-(1)搭建项目.重置css.配置router和store(pinia) https://blog.csdn.net/karshey/article/deta ...

最新文章

  1. php 启动管理工具下载,phpPgAdmin|PostgreSQL管理工具(phpPgAdmin)下载v5.0.4 开源版 - 欧普软件下载...
  2. 知识中台,驱动产业智能化升级
  3. Linux中source是什么指令?
  4. php新建文件在指定目录下,PHP在获取指定目录下的目录,在获取的目录下面再创建文件,多平台...
  5. 计算机怎么不用鼠标,技巧:如何仅用键盘而不用鼠标(包括任何指针触摸设备)优雅地使用计算机?...
  6. 蓝桥杯---特别数的和(C语言)
  7. 诺基亚的「翻身」之战
  8. MySQL延时更改数据_mysql数据库备份设置延时备份方法(mysql主从配置)
  9. 命令让手机临时root_Linux 最常用命令(简单易学,但能解决 95% 以上的问题)
  10. 小米开源文件管理器MiCodeFileExplorer-源码研究(1)-2个模型Model
  11. 线程--线程池--委托--task---async/await
  12. 三菱FX系列PLC-编程1
  13. 用Python画一个时钟---简易版
  14. AI十大数据挖掘算法
  15. ITextSharp 使用
  16. java:编写一个程序完成:完成在银行的存款和取款操作。在定义银行类时,若取款数大于余额则作为异常处理。
  17. less 或 scss 覆盖UI组件样式并集选择器使用
  18. 尝鲜云端地图服务 Azure Location Based Service Preview
  19. Python 通过微信控制实现app定位发送到个人服务器再转发微信服务器接收位置信息
  20. imagenet/ILSVRC2012数据集百度云及其具体分类

热门文章

  1. Java 获取当前时间之后的第一个周几,java获取当前日期的下一个周几
  2. 2022-2028年中国聚氨酯结构胶行业发展动态分析及投资机会预测报告
  3. Redis 笔记(02)— keys 键相关命令(查询数据库key数量、判断key是否存在、指定key过期时间、查看key类型、查看key剩余秒数、选择数据库、删除key、删除数据库)
  4. 用python快速画小猪佩奇
  5. nginx将泛解析的匹配域名绑定到子目录配置方法
  6. python 正则括号的使用及踩坑
  7. 网路摄像头技术参数介绍
  8. NSight Compute 用户手册(上)
  9. GPU端到端目标检测YOLOV3全过程(下)
  10. 自动驾驶关键技术分解和流程