码迷,mamicode.com
首页 > 其他好文 > 详细

【vue】vue-znly

时间:2019-07-10 20:12:29      阅读:92      评论:0      收藏:0      [点我收藏+]

标签:webpack   use   通过   点击   改变   null   中心   position   command   

老规矩,放下博主的项目地址: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">&#xe679;</i>
        </span>
        <span class="left-icon side-bar" v-if="sideBar" @click="showSideBar">
            <i title="主菜单" class="iconfont">&#xe602;</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">&#xe641;</i>景区介绍</router-link>
            <router-link :to="{name: 'service', params: {type: 3}}"><i class="iconfont">&#xe64c;</i>景区公告</router-link>
            <router-link :to="{name: 'service', params: {type: 15}}"><i class="iconfont">&#xe69b;</i>景区服务</router-link>
            <router-link :to="{name: 'service', params: {type: 13}}"><i class="iconfont">&#xe6b2;</i>预订门票</router-link>
            <router-link :to="{name: 'service', params: {type: 14}}"><i class="iconfont">&#xe6af;</i>特色购物</router-link>
            <router-link to="/travelBox"><i class="iconfont">&#xe603;</i>旅行百宝箱</router-link>
            <router-link :to="{name: 'dropBox', params: {url: vrUrl, title: vrTitle}}"><i class="iconfont">&#xe73d;</i>虚拟旅游</router-link>
             <router-link :to="{name: 'service', params: {type: 6}}"><i class="iconfont">&#xe7f1;</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">&#xe69b;</i><span>景区服务</span>
                </router-link>
            </li>
            <li>
                <router-link to="/home" :class=" pathName == navUrl[1] ? 'active' : ''">
                    <i class="iconfont">&#xe6b8;</i><span>主页</span>
                </router-link>
            </li>
            <li>
                <router-link :to="{name: 'service', params: {type: 6}}" :class=" pathName == navUrl[2] ? 'active' : ''">
                    <i class="iconfont">&#xe7f1;</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">&#xe656;</i>
                    <span>旅游线路</span>
                </router-link>
                <router-link :to="{name: 'externalMap'}">
                    <i class="iconfont">&#xe621;</i>
                    <span>外部交通</span>
                </router-link>
                <router-link :to="{name: 'listDetail', params: {id: 4, type: 21, identifier: 0}}">
                    <i class="iconfont">&#xe638;</i>
                    <span>景区地图</span>
                </router-link>
                <router-link :to="{name: 'service', params: {type: 7}}">
                    <i class="iconfont">&#xe600;</i>
                    <span>周边景点</span>
                </router-link>
                <router-link :to="{name: 'service', params: {type: 6}}">
                    <i class="iconfont">&#xe7f1;</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">&#xe66b;&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 旅行百宝箱模块为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">&#xe73d;</i>{{ visitCount }}</li>
                <li @click.once="upVote" :class="{active: isActive }"><i class="iconfont">&#xe644;</i>{{ goodCount }}</li>
                <li @click="showReviewBox"><i class="iconfont">&#xe761;</i>写评论</li>
                <li @click="showCommentBox"><i class="iconfont">&#xe649;</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>

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

【vue】vue-znly

标签:webpack   use   通过   点击   改变   null   中心   position   command   

原文地址:https://www.cnblogs.com/smart-girl/p/11166096.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!