├── static └── .gitkeep ├── src ├── assets │ ├── .gitkeep │ ├── icon │ │ ├── iconfont.eot │ │ ├── iconfont.ttf │ │ ├── iconfont.woff │ │ ├── iconfont.css │ │ └── iconfont.svg │ ├── header.sass │ ├── common.sass │ ├── detail.sass │ ├── sidebar.sass │ ├── film.sass │ ├── loading.sass │ ├── home.sass │ ├── reset.sass │ └── cinema.sass ├── components │ ├── footer.vue │ ├── loading.vue │ ├── header.vue │ └── sidebar.vue ├── main.js ├── vuex │ ├── index.js │ ├── modules │ │ ├── detail.js │ │ ├── com.js │ │ ├── cinema.js │ │ ├── home.js │ │ └── film.js │ ├── types.js │ └── api.js ├── config │ └── index.js ├── routers.js ├── pages │ ├── detail.vue │ ├── cinema.vue │ ├── home.vue │ └── film.vue └── App.vue ├── .gitignore ├── config ├── prod.env.js ├── dev.env.js └── index.js ├── readme_img ├── 1.png ├── film.png ├── home.png ├── cinema.png ├── detail.png ├── home_2.png └── cinema_2.png ├── .babelrc ├── .editorconfig ├── index.html ├── package.json └── README.md /static/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/footer.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | dist/ 4 | npm-debug.log 5 | -------------------------------------------------------------------------------- /config/prod.env.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | NODE_ENV: '"production"' 3 | } 4 | -------------------------------------------------------------------------------- /readme_img/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChuckCZC/vue-demo-maizuo/HEAD/readme_img/1.png -------------------------------------------------------------------------------- /readme_img/film.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChuckCZC/vue-demo-maizuo/HEAD/readme_img/film.png -------------------------------------------------------------------------------- /readme_img/home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChuckCZC/vue-demo-maizuo/HEAD/readme_img/home.png -------------------------------------------------------------------------------- /readme_img/cinema.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChuckCZC/vue-demo-maizuo/HEAD/readme_img/cinema.png -------------------------------------------------------------------------------- /readme_img/detail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChuckCZC/vue-demo-maizuo/HEAD/readme_img/detail.png -------------------------------------------------------------------------------- /readme_img/home_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChuckCZC/vue-demo-maizuo/HEAD/readme_img/home_2.png -------------------------------------------------------------------------------- /readme_img/cinema_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChuckCZC/vue-demo-maizuo/HEAD/readme_img/cinema_2.png -------------------------------------------------------------------------------- /src/assets/icon/iconfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChuckCZC/vue-demo-maizuo/HEAD/src/assets/icon/iconfont.eot -------------------------------------------------------------------------------- /src/assets/icon/iconfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChuckCZC/vue-demo-maizuo/HEAD/src/assets/icon/iconfont.ttf -------------------------------------------------------------------------------- /src/assets/icon/iconfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChuckCZC/vue-demo-maizuo/HEAD/src/assets/icon/iconfont.woff -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015", "stage-2"], 3 | "plugins": ["transform-runtime"], 4 | "comments": false 5 | } 6 | -------------------------------------------------------------------------------- /config/dev.env.js: -------------------------------------------------------------------------------- 1 | var merge = require('webpack-merge') 2 | var prodEnv = require('./prod.env') 3 | 4 | module.exports = merge(prodEnv, { 5 | NODE_ENV: '"development"' 6 | }) 7 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /src/components/loading.vue: -------------------------------------------------------------------------------- 1 | 9 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Create by zechun.chen on 2016/12/22 3 | */ 4 | 5 | import Vue from 'vue' 6 | import router from './routers' 7 | import store from './vuex' 8 | import './config' 9 | 10 | Vue.config.debug = true; 11 | //自定义滚动指令 12 | Vue.directive('scroll',{ 13 | bind:function(el,binding){ 14 | window.addEventListener('scroll',() => { 15 | let fnc = binding.value; 16 | fnc(el); 17 | }) 18 | } 19 | }) 20 | const app = new Vue({ 21 | router, 22 | store, 23 | }).$mount('#app'); -------------------------------------------------------------------------------- /src/vuex/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Create by zechun.chen on 2016/12/22 3 | */ 4 | 5 | import Vue from 'vue' 6 | import Vuex from 'vuex' 7 | import com from './modules/com' 8 | import home from './modules/home' 9 | import detail from './modules/detail' 10 | import cinema from './modules/cinema' 11 | import film from './modules/film' 12 | 13 | Vue.use(Vuex) 14 | const store = new Vuex.Store({ 15 | modules:{ 16 | com, 17 | home, 18 | detail, 19 | cinema, 20 | film 21 | }, 22 | strict:process.env.NODE_ENV !== 'production' 23 | }) 24 | 25 | export default store; -------------------------------------------------------------------------------- /src/assets/header.sass: -------------------------------------------------------------------------------- 1 | @import 'common' 2 | #header 3 | background: #282828 4 | color: #999 5 | position: fixed 6 | z-index: 500 7 | width: 100% 8 | height: 50*$px 9 | line-height: 50*$px 10 | @include box(justify,center) 11 | a 12 | display: block 13 | .go-menu,.go-mine 14 | width: 50*$px 15 | text-align: center 16 | height: 48*$px 17 | .go-menu 18 | border-right: 1px solid #222 19 | box-shadow: 1px 0 1px #363636 20 | .title 21 | width: 274*$px 22 | font-size: 16*$px 23 | @include box(justify,center) 24 | a 25 | i 26 | margin-left: 5*$px 27 | font-size: 12*$px 28 | p 29 | color: #fff 30 | -------------------------------------------------------------------------------- /src/assets/common.sass: -------------------------------------------------------------------------------- 1 | $assetPath : "../img" 2 | $v : 5 3 | //px转rem 4 | $px: 1rem/20 5 | 6 | //公共 基础类 7 | @mixin clearFix 8 | &:before 9 | content: "" 10 | display: table 11 | &:after 12 | content: "" 13 | display: table 14 | clear: both 15 | overflow: hidden 16 | zoom: 1 17 | .clearfix 18 | @include clearFix 19 | //box布局 20 | @mixin box($horizontal: initial, $vertical: initial) 21 | display: -webkit-box 22 | display: box 23 | -webkit-box-pack: $horizontal 24 | box-pack: $horizontal 25 | -webkit-box-align: $vertical 26 | box-align: $vertical 27 | @mixin nowrap 28 | display: block 29 | white-space: nowrap 30 | overflow: hidden 31 | text-overflow: ellipsis 32 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | vue-demo-maizuo 13 | 14 | 15 |
16 | 17 |
18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/components/header.vue: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /src/assets/detail.sass: -------------------------------------------------------------------------------- 1 | @import 'common' 2 | #detail 3 | .cover 4 | img 5 | width: 100% 6 | .desc 7 | font-size: 13*$px 8 | line-height: 230% 9 | padding: 15*$px 10 | .title 11 | border-left: 16*$px solid #e4c89c 12 | font-size: 16*$px 13 | padding-left: 5*$px 14 | margin-bottom: 20*$px 15 | margin-left: -15*$px 16 | .info 17 | p 18 | @include nowrap 19 | span 20 | display: inline-block 21 | margin-right: 1*$px 22 | &:after 23 | content: '|' 24 | &:last-child:after 25 | content: '' 26 | &.p 27 | line-height: 150% 28 | margin-top: 5*$px 29 | .go-pay 30 | width: 160*$px 31 | height: 35*$px 32 | background: #e38700 33 | border-radius: 35*$px 34 | line-height: 35*$px 35 | color: #fff 36 | text-align: center 37 | font-size: 14*$px 38 | margin: 10*$px auto 39 | display: block -------------------------------------------------------------------------------- /src/vuex/modules/detail.js: -------------------------------------------------------------------------------- 1 | import api from '../api' 2 | import * as types from '../types' 3 | 4 | const state = { 5 | detail:null, 6 | // title:'' 7 | } 8 | 9 | const actions = { 10 | //获取影片详情并设置标题 11 | getFilmDetail:function({commit},id){ 12 | commit(types.COM_LOADING_STATUS,true); 13 | api.getFilmDetail(id,function(res){ 14 | commit(types.COM_CONF,{ 15 | title: res.data.film.name 16 | }) 17 | commit(types.DETAIL_GET_INFO,res.data); 18 | commit(types.COM_LOADING_STATUS,false); 19 | }) 20 | }, 21 | } 22 | const getters = { 23 | getFilmDetail: state => state.detail, 24 | } 25 | 26 | const mutations = { 27 | [types.DETAIL_GET_INFO](state,res){ 28 | state.detail = res.film 29 | }, 30 | } 31 | 32 | export default { 33 | state, 34 | actions, 35 | getters, 36 | mutations 37 | } -------------------------------------------------------------------------------- /src/vuex/types.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Create by zechun.chen on 2016/12/22 3 | */ 4 | 5 | //公共 6 | export const COM_LOADING_STATUS = 'COM_LOADING_STATUS' 7 | export const COM_CONF = 'COM_CONF' 8 | export const CHANGE_LEFTNAV_STATUS = 'CHANGE_LEFTNAV_STATUS' 9 | //home 10 | export const HOME_GET_BANNER_LIST = 'HOME_GET_BANNER_LIST' //获取banner 11 | export const HOME_GET_NOWPLAYING_LIST = 'HOME_GET_NOWPLAYING_LIST' //获取首页热映电影 12 | export const HOME_GET_COMINGSOON_LIST = 'HOME_GET_COMINGSOON_LIST' //获取首页即将上映电影 13 | //detail 14 | export const DETAIL_GET_INFO = 'DETAIL_GET_INFO' //获取影片详情 15 | //cinema 16 | export const CINEMA_GET_LIST = 'CINEMA_GET_LIST' //获取相关影院 17 | //film 18 | export const FILM_GET_NOWPLAYING = 'FILM_GET_NOWPLAYING' //获取热映电影 19 | export const FILE_NOWPLAYING_NUM = 'FILE_NOWPLAYING_NUM' //热映页数 20 | export const FILM_GET_COMINGSOON = 'FILM_GET_COMINGSOON' //获取即将上映电影 21 | export const FILM_COMINGSOON_NUM = 'FILM_COMINGSOON_NUM' // 即将上映页数 -------------------------------------------------------------------------------- /src/assets/icon/iconfont.css: -------------------------------------------------------------------------------- 1 | 2 | @font-face {font-family: "iconfont"; 3 | src: url('iconfont.eot?t=1482391322636'); /* IE9*/ 4 | src: url('iconfont.eot?t=1482391322636#iefix') format('embedded-opentype'), /* IE6-IE8 */ 5 | url('iconfont.woff?t=1482391322636') format('woff'), /* chrome, firefox */ 6 | url('iconfont.ttf?t=1482391322636') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/ 7 | url('iconfont.svg?t=1482391322636#iconfont') format('svg'); /* iOS 4.1- */ 8 | } 9 | 10 | .iconfont { 11 | font-family:"iconfont" !important; 12 | font-size:16px; 13 | font-style:normal; 14 | -webkit-font-smoothing: antialiased; 15 | -moz-osx-font-smoothing: grayscale; 16 | } 17 | 18 | .icon-menu:before { content: "\e65b"; } 19 | 20 | .icon-people:before { content: "\e61b"; } 21 | 22 | .icon-right:before { content: "\e650"; } 23 | 24 | .icon-bottom:before { content: "\e625"; } 25 | 26 | .icon-top-copy:before { content: "\e60d"; } 27 | 28 | -------------------------------------------------------------------------------- /src/config/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Create by zechun.chen on 2016/12/22 3 | * 基础配置 4 | */ 5 | 6 | import FastClick from 'fastclick' 7 | 8 | ((doc, win) => { 9 | const docEl = doc.documentElement, 10 | resizeEvt = 'orientationchange' in window ? 'orientationchange' : 'resize', 11 | recalc = () => { 12 | let clientWidth = docEl.clientWidth; 13 | if (!clientWidth) return; 14 | docEl.style.fontSize = 16 * (clientWidth / 320) + 'px'; 15 | }; 16 | if (!doc.addEventListener) return; 17 | win.addEventListener(resizeEvt, recalc, false); 18 | doc.addEventListener('DOMContentLoaded', recalc, false); 19 | //当dom加载完成时,或者 屏幕垂直、水平方向有改变进行html的根元素计算 20 | })(document, window); 21 | 22 | 23 | if ('addEventListener' in document) { 24 | document.addEventListener('DOMContentLoaded', function() { 25 | FastClick.attach(document.body); 26 | }, false); 27 | } 28 | 29 | /** 30 | * 对象数组去重,根据str属性 31 | */ 32 | Array.prototype.unique = function(str){ 33 | const seen = new Map() 34 | return this.filter((a) => { 35 | return !seen.has(a['name']) && seen.set(a['name'], 1) 36 | }) 37 | } 38 | -------------------------------------------------------------------------------- /src/components/sidebar.vue: -------------------------------------------------------------------------------- 1 | 21 | -------------------------------------------------------------------------------- /src/routers.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Create by zechun.chen on 2016/12/22 3 | * 路由规则 4 | */ 5 | 6 | import Vue from 'vue' 7 | import VueRouter from 'vue-router' 8 | 9 | Vue.use(VueRouter); 10 | const routes = [ 11 | { 12 | path:'/', 13 | component:require('./App'), 14 | children:[ 15 | { 16 | path:'/home', 17 | name:'home', 18 | component:require('./pages/home') 19 | }, 20 | { 21 | path:'/detail/:id', 22 | name:'detail', 23 | component:require('./pages/detail') 24 | }, 25 | { 26 | path:'/cinema/:id', 27 | name:'cinema', 28 | component:require('./pages/cinema') 29 | }, 30 | { 31 | path:'/film/:type', 32 | name:'film', 33 | component:require('./pages/film') 34 | } 35 | ] 36 | } 37 | ]; 38 | 39 | const router = new VueRouter({ 40 | routes:routes, 41 | history:true, 42 | linkActiveClass:'active', //如果有底部导航栏,这个属性可以为被选中的路由增加相应的选中状态class 43 | }); 44 | 45 | export default router; -------------------------------------------------------------------------------- /config/index.js: -------------------------------------------------------------------------------- 1 | // see http://vuejs-templates.github.io/webpack for documentation. 2 | var path = require('path') 3 | 4 | module.exports = { 5 | build: { 6 | env: require('./prod.env'), 7 | index: path.resolve(__dirname, '../dist/index.html'), 8 | assetsRoot: path.resolve(__dirname, '../dist'), 9 | assetsSubDirectory: 'static', 10 | assetsPublicPath: './', 11 | productionSourceMap: true, 12 | // Gzip off by default as many popular static hosts such as 13 | // Surge or Netlify already gzip all static assets for you. 14 | // Before setting to `true`, make sure to: 15 | // npm install --save-dev compression-webpack-plugin 16 | productionGzip: false, 17 | productionGzipExtensions: ['js', 'css'] 18 | }, 19 | dev: { 20 | env: require('./dev.env'), 21 | port: 8080, 22 | assetsSubDirectory: 'static', 23 | assetsPublicPath: '/', 24 | proxyTable: {}, 25 | // CSS Sourcemaps off by default because relative paths are "buggy" 26 | // with this option, according to the CSS-Loader README 27 | // (https://github.com/webpack/css-loader#sourcemaps) 28 | // In our experience, they generally work as expected, 29 | // just be aware of this issue when enabling this option. 30 | cssSourceMap: false 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/assets/sidebar.sass: -------------------------------------------------------------------------------- 1 | @import 'common' 2 | #sidebar 3 | transition: all ease .4s 4 | .sidebar-enter-active,.sidebar-leave-active 5 | transition: all ease .4s 6 | .sidebar-enter,.sidebar-leave-active 7 | opacity: 0 8 | .leftNav-enter-active,.leftNav-leave-active 9 | transition: right ease .4s 10 | .leftNav-enter,.leftNav-leave-active 11 | right: 380*$px !important 12 | .sidebar-container 13 | position: fixed 14 | top: 0 15 | left: 0 16 | right: 0 17 | bottom: 0 18 | color: #9a9a9a 19 | z-index: 501 20 | a 21 | color: #9a9a9a 22 | font-size: 14*$px 23 | .sidebar-overlay 24 | background: rgba(0,0,0,.5) 25 | position: absolute 26 | top: 50*$px 27 | right: 0 28 | bottom: 0 29 | left: 0 30 | nav 31 | border-top: 1px solid #222 32 | box-shadow: 0 1px 1px #363636 inset 33 | background: #282828 34 | position: absolute 35 | display: block 36 | top: 0 37 | right: 110*$px 38 | bottom: 0 39 | left: 0 40 | li 41 | overflow: hidden 42 | line-height: 50*$px 43 | height: 51*$px 44 | a 45 | display: block 46 | border-bottom: 1px dotted #333 47 | width: 90% 48 | margin: 0 auto 49 | i 50 | float: right 51 | color: #666 52 | font-size: 14*$px 53 | 54 | -------------------------------------------------------------------------------- /src/assets/film.sass: -------------------------------------------------------------------------------- 1 | @import 'common' 2 | #film 3 | background: #f9f9f9 4 | .tabs 5 | margin: 0 15*$px 6 | border-bottom: 1px solid #fe6e00 7 | @include box(justify,center) 8 | .item 9 | width: 50% 10 | text-align: center 11 | font-size: 16*$px 12 | height: 40*$px 13 | line-height: 40*$px 14 | color: #6a6a6a 15 | &.active 16 | border-bottom: 3px solid #fe6e00 17 | color: #fe6e00 18 | .tab-content 19 | margin: 0 15*$px 20 | .now-playing-list,.coming-soon-list 21 | .item 22 | @include box(justify,start) 23 | padding: 15*$px 0 24 | // display: block 25 | border-bottom: 1px dashed #8e8e8e 26 | &:last-child 27 | border-bottom: none 28 | .avater 29 | width: 60*$px 30 | margin-right: 15*$px 31 | img 32 | width: 60*$px 33 | height: 82*$px 34 | object-fit: contain 35 | .info 36 | width: 250*$px 37 | font-size: 14*$px 38 | h3 39 | font-weight: 400 40 | font-size: 16*$px 41 | margin-top: 5*$px 42 | p 43 | font-size: 13*$px 44 | height: 28*$px 45 | line-height: 28*$px 46 | color: #8e8e8e 47 | span 48 | color: #8aa2bf 49 | .time 50 | color: #fe6e00 51 | .count 52 | width: 50*$px 53 | padding-top: 5*$px 54 | color: #fe6e00 -------------------------------------------------------------------------------- /src/vuex/modules/com.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Create by zechun.chen on 2016/12/22 3 | */ 4 | import * as types from '../types' 5 | /** 6 | * App通用配置 7 | */ 8 | const state = { 9 | title:'', 10 | loading:0, 11 | leftNavState:false 12 | // isBack:false, 13 | // isShare:false, 14 | // isFooter:false, //本程序没有底部导航栏 15 | } 16 | 17 | const actions = { 18 | //本程序主要设置标题栏,对于通用程序,则可以设置返回按钮,底部导航栏 19 | comConf({commit},settings){ 20 | commit(types.COM_CONF,settings) 21 | }, 22 | //左侧导航栏的开关 23 | changeLeftNavState({commit},status){ 24 | commit(types.CHANGE_LEFTNAV_STATUS,status) 25 | } 26 | } 27 | const getters = { 28 | comConf: state => state, 29 | loading: state => state.loading, 30 | title: state => state.title, 31 | leftNavState: state => state.leftNavState 32 | } 33 | const mutations = { 34 | [types.COM_CONF](state,settings){ 35 | state = Object.assign(state,settings) 36 | }, 37 | [types.COM_LOADING_STATUS](state,status){ 38 | if(state.loading == 0 && !status){ 39 | return ; 40 | } 41 | state.loading = status ? ++state.loading : --state.loading; 42 | }, 43 | [types.CHANGE_LEFTNAV_STATUS](state,status){ 44 | state.leftNavState = status 45 | } 46 | } 47 | 48 | export default { 49 | state, 50 | actions, 51 | getters, 52 | mutations 53 | } -------------------------------------------------------------------------------- /src/assets/loading.sass: -------------------------------------------------------------------------------- 1 | //loading 2 | @keyframes rotate 3 | 0% 4 | -webkit-transform: rotate(0deg) 5 | transform: rotate(0deg) 6 | 50% 7 | -webkit-transform: rotate(180deg) 8 | transform: rotate(180deg) 9 | 100% 10 | -webkit-transform: rotate(360deg) 11 | transform: rotate(360deg) 12 | .loading-rotate 13 | position: fixed 14 | top: 0 15 | left: 0 16 | right: 0 17 | bottom: 0 18 | background: rgba(0,0,0,.4) 19 | .inner 20 | width: 80px 21 | height: 80px 22 | position: absolute 23 | top: 50% 24 | left: 50% 25 | margin-left: -40px 26 | margin-top: -40px 27 | &>div 28 | animation-fill-mode: both 29 | position: absolute 30 | left: 0px 31 | top: 0px 32 | border: 2px solid #fff 33 | border-bottom-color: transparent 34 | border-top-color: transparent 35 | border-radius: 100% 36 | height: 80px 37 | width: 80px 38 | animation: rotate 1s 0s ease-in-out infinite 39 | &:last-child 40 | display: inline-block 41 | top: 15px 42 | left: 15px 43 | width: 50px 44 | height: 50px 45 | animation-duration: 0.5s 46 | border-color: #fff transparent #fff transparent 47 | animation-direction: reverse -------------------------------------------------------------------------------- /src/vuex/modules/cinema.js: -------------------------------------------------------------------------------- 1 | import api from '../api' 2 | import * as types from '../types' 3 | 4 | const state = { 5 | list:[], 6 | district:[] 7 | } 8 | 9 | const actions = { 10 | //获取相关影院列表 11 | getCinemaList:function({commit},id){ 12 | commit(types.COM_LOADING_STATUS,true); 13 | api.getCinemaList(id,function(res){ 14 | commit(types.CINEMA_GET_LIST,res.data); 15 | commit(types.COM_LOADING_STATUS,false); 16 | }) 17 | }, 18 | } 19 | const getters = { 20 | getCinemaList: state => state.list, 21 | getDistrict: state => state.district 22 | } 23 | 24 | const mutations = { 25 | [types.CINEMA_GET_LIST](state,res){ 26 | /** 27 | * 接口没有的区域影院没有进行区分就返回,故只能前端进行分类 28 | * 先将列表遍历一遍,将地区相关归入一个数组 29 | * 进行数组去重并按照拼音首字母进行排列 30 | * 去重unique()在config/index.js中定义 31 | */ 32 | let district = [],_json={}; 33 | for(let item of res.cinemas){ 34 | _json = { 35 | name:item.district.name, 36 | pinyin:item.district.pinyin 37 | } 38 | district.push(_json) 39 | } 40 | district = district.unique('name').sort(function(a,b){ 41 | return a.pinyin.localeCompare(b.pinyin.charAt(0)) 42 | }); 43 | state.district = district; 44 | state.list = res.cinemas; 45 | }, 46 | } 47 | 48 | export default { 49 | state, 50 | actions, 51 | getters, 52 | mutations 53 | } -------------------------------------------------------------------------------- /src/assets/home.sass: -------------------------------------------------------------------------------- 1 | @import 'common' 2 | #home 3 | background: #e1e1e1 4 | .banner-swiper 5 | height: 232*$px 6 | color: #fff 7 | font-size: 30*$px 8 | text-align: center 9 | overflow: hidden 10 | .slide img 11 | width: 100% 12 | .go-more 13 | width: 150*$px 14 | height: 30*$px 15 | line-height: 30*$px 16 | text-align: center 17 | color: #616161 18 | border: 1px solid #aaa 19 | border-radius: 30*$px 20 | font-size: 13*$px 21 | margin: 0 auto 22 | margin-bottom: 15*$px 23 | display: block 24 | .now-playing,.coming-soon 25 | padding: 15*$px 26 | .item 27 | background: #f9f9f9 28 | margin-bottom: 15*$px 29 | box-shadow: 0 0 4px rgba(0,0,0,.4) 30 | img 31 | width: 100% 32 | .desc 33 | @include box(justify,center) 34 | padding: 5*$px 10*$px 35 | .info 36 | font-size: 12*$px 37 | font-weight: 100 38 | p 39 | font-size: 10*$px 40 | color: #9a9a9a 41 | .count 42 | color: #f78360 43 | font-weight: 500 44 | .time 45 | font-size: 12*$px 46 | color: #f78360 47 | .coming-title 48 | width: 100% 49 | height: 1px 50 | background: #a8a8a8 51 | position: relative 52 | margin-bottom: 20*$px 53 | div 54 | position: absolute 55 | color: #eee 56 | background: #a7a7a7 57 | font-size: 14*$px 58 | width: 80*$px 59 | height: 22*$px 60 | text-align: center 61 | line-height: 22*$px 62 | border-radius: 5*$px 63 | top: 50% 64 | left: 50% 65 | margin-top: -11*$px 66 | margin-left: -40*$px 67 | -------------------------------------------------------------------------------- /src/pages/detail.vue: -------------------------------------------------------------------------------- 1 | 18 | -------------------------------------------------------------------------------- /src/vuex/modules/home.js: -------------------------------------------------------------------------------- 1 | import api from '../api' 2 | import * as types from '../types' 3 | 4 | const state = { 5 | banner:[], 6 | nowplay:[], 7 | coming:[], 8 | } 9 | 10 | const actions = { 11 | //获取banner列表 12 | getBannerList:function({commit}){ 13 | commit(types.COM_LOADING_STATUS,true); 14 | api.getBannerList(function(res){ 15 | commit(types.HOME_GET_BANNER_LIST,res.data); 16 | commit(types.COM_LOADING_STATUS,false); 17 | }) 18 | }, 19 | // 获取热映 20 | getNowPlaying:function({commit}){ 21 | commit(types.COM_LOADING_STATUS,true) 22 | api.getNowPlaying(function(res){ 23 | commit(types.HOME_GET_NOWPLAYING_LIST,res.data) 24 | commit(types.COM_LOADING_STATUS,false) 25 | }) 26 | }, 27 | // 获取即将上映 28 | getComingSoon:function({commit}){ 29 | commit(types.COM_LOADING_STATUS,true) 30 | api.getComingSoon(function(res){ 31 | commit(types.HOME_GET_COMINGSOON_LIST,res.data) 32 | commit(types.COM_LOADING_STATUS,false) 33 | }) 34 | } 35 | } 36 | const getters = { 37 | getBannerList: state => state.banner, 38 | getNowPlaying: state => state.nowplay, 39 | getComingSoon: state => state.coming, 40 | } 41 | 42 | const mutations = { 43 | [types.HOME_GET_BANNER_LIST](state,res){ 44 | state.banner = res.billboards 45 | }, 46 | [types.HOME_GET_NOWPLAYING_LIST](state,res){ 47 | state.nowplay = res.films 48 | }, 49 | [types.HOME_GET_COMINGSOON_LIST](state,res){ 50 | state.coming = res.films 51 | } 52 | } 53 | 54 | export default { 55 | state, 56 | actions, 57 | getters, 58 | mutations 59 | } -------------------------------------------------------------------------------- /src/vuex/api.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Create bu zechun.chen on 2016/12/22 3 | * api配置 4 | */ 5 | import axios from 'axios' 6 | 7 | let url = process.env.NODE_ENV !== 'production' ? '/api/' : 'http://m.maizuo.com/v4/api/'; 8 | 9 | let func_axios = (api,cb) => { 10 | axios.get(api).then(function(res){ 11 | if(res.status >= 200 && res.status <300){ 12 | cb(res.data) 13 | } 14 | }).catch((error) => { 15 | // new Error('desc'); 16 | return Promise.reject(error) 17 | }) 18 | } 19 | 20 | export default { 21 | /** 22 | * 根据请求的时间戳获取banner列表 23 | */ 24 | getBannerList:(cb)=>func_axios(url + 'billboard/home?t=' + new Date()*1,cb), 25 | /** 26 | * 获取首页热映电影 27 | */ 28 | getNowPlaying:(cb)=>func_axios(url + 'film/now-playing?_t=' + new Date()*1 +'&page=1&count=5',cb), 29 | /** 30 | * 获取热映列表 31 | */ 32 | getNowPlayList:(page,cb)=>func_axios(url + 'film/now-playing?page=' + page + '&count=10',cb), 33 | /** 34 | * 获取首页即将上映电影 35 | */ 36 | getComingSoon:(cb)=>func_axios(url + 'film/coming-soon?__t=' + new Date()*1 +'&page=1&count=3',cb), 37 | /** 38 | * 获取即将上映列表 39 | */ 40 | getComingList:(page,cb)=>func_axios(url + 'film/coming-soon?page=' + page +'&count=10',cb), 41 | /** 42 | * 根据id获取相关影片信息 43 | */ 44 | getFilmDetail:(id,cb)=>func_axios(url + 'film/' + id + '?__t=' + new Date()*1,cb), 45 | /** 46 | * 获取相关影院 47 | */ 48 | getCinemaList:(id,cb)=>func_axios(url + 'film/' + id + '/cinema?__t=' + new Date()*1,cb), 49 | /** 50 | * 根据影片id跟影院id获取相关电影票时段 51 | */ 52 | getScheduleList:(filmid,cinemaid,cb)=>func_axios(url + 'schedule?__t=' + new Date()*1 + '&film=' + filmid + '&cinema=' + cinemaid,cb), 53 | } 54 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-demo-maizuo", 3 | "version": "1.0.0", 4 | "description": "vue-demo-maizuo", 5 | "author": "shadow <822599633@qq.com>", 6 | "private": true, 7 | "scripts": { 8 | "dev": "node build/dev-server.js", 9 | "build": "node build/build.js" 10 | }, 11 | "dependencies": { 12 | "axios": "^0.15.3", 13 | "fastclick": "^1.0.6", 14 | "vue": "^2.1.0", 15 | "vue-awesome-swiper": "^2.2.6", 16 | "vue-router": "^2.1.1", 17 | "vuex": "^2.1.1" 18 | }, 19 | "devDependencies": { 20 | "autoprefixer": "^6.4.0", 21 | "babel-core": "^6.0.0", 22 | "babel-loader": "^6.0.0", 23 | "babel-plugin-transform-runtime": "^6.0.0", 24 | "babel-preset-es2015": "^6.0.0", 25 | "babel-preset-stage-2": "^6.0.0", 26 | "babel-register": "^6.0.0", 27 | "chalk": "^1.1.3", 28 | "connect-history-api-fallback": "^1.1.0", 29 | "css-loader": "^0.25.0", 30 | "eventsource-polyfill": "^0.9.6", 31 | "express": "^4.13.3", 32 | "extract-text-webpack-plugin": "^1.0.1", 33 | "file-loader": "^0.9.0", 34 | "function-bind": "^1.0.2", 35 | "html-webpack-plugin": "^2.8.1", 36 | "http-proxy-middleware": "^0.17.2", 37 | "json-loader": "^0.5.4", 38 | "node-sass": "^4.1.1", 39 | "opn": "^4.0.2", 40 | "ora": "^0.3.0", 41 | "postcss": "^5.2.6", 42 | "postcss-loader": "^1.2.1", 43 | "sass-loader": "^4.1.1", 44 | "semver": "^5.3.0", 45 | "shelljs": "^0.7.4", 46 | "url-loader": "^0.5.7", 47 | "vue-loader": "^10.0.0", 48 | "vue-style-loader": "^1.0.0", 49 | "vue-template-compiler": "^2.1.0", 50 | "webpack": "^1.13.2", 51 | "webpack-dev-middleware": "^1.8.3", 52 | "webpack-hot-middleware": "^2.12.2", 53 | "webpack-merge": "^0.14.1" 54 | }, 55 | "engines": { 56 | "node": ">= 4.0.0", 57 | "npm": ">= 3.0.0" 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/pages/cinema.vue: -------------------------------------------------------------------------------- 1 | 24 | -------------------------------------------------------------------------------- /src/assets/reset.sass: -------------------------------------------------------------------------------- 1 | /*reset*/ 2 | * 3 | margin: 0 4 | padding: 0 5 | box-sizing: border-box 6 | body,button,input,select,textarea 7 | font-family: 'Microsoft YaHei', Tahoma, Helvetica, Arial, sans-serif 8 | // -webkit-font-smoothing: antialiased 9 | 10 | h1, h2, h3, h4, h5, h6 11 | font-size: 100% 12 | address, cite, dfn, em, var 13 | font-style: normal 14 | code, kbd, pre, samp 15 | // font-family: couriernew, courier, monospace 16 | small 17 | font-size: 12px 18 | ul, ol 19 | list-style: none 20 | a 21 | text-decoration: none 22 | outline: none 23 | sup 24 | vertical-align: text-top 25 | sub 26 | vertical-align: text-bottom 27 | legend 28 | color: #000 29 | fieldset, img 30 | border: 0 31 | button, input, select, textarea 32 | font-size: 100% 33 | outline: none 34 | table 35 | border-collapse: collapse 36 | border-spacing: 0 37 | a 38 | color: inherit 39 | 40 | @import 'common' 41 | 42 | html,body 43 | height: 100% 44 | background: #e1e1e1 45 | 46 | .go-top 47 | color: #fff 48 | background: rgba(0,0,0,.7) 49 | width: 50*$px 50 | text-align: center 51 | height: 50*$px 52 | line-height: 50*$px 53 | border-radius: 50% 54 | position: fixed 55 | z-index: 499 56 | bottom: -100*$px 57 | right: 20*$px 58 | transition: .3s linear 59 | &.active 60 | bottom: 20*$px 61 | transition: .3s linear 62 | i 63 | font-size: 24*$px 64 | .container 65 | padding-top: 50*$px 66 | .child-view 67 | // position: absolute 68 | width: 100% 69 | transition: all .2s cubic-bezier(.55,0,.1,1) 70 | .slide-left-enter, .slide-right-leave-active 71 | opacity: 0 72 | transform: translate(30*$px,0) 73 | .slide-left-leave-active, .slide-right-enter 74 | opacity: 0 75 | transform: translate(-30*$px 0) 76 | 77 | .list-complete-item 78 | transition: all .6s 79 | display: inline-block 80 | margin-right: 10px 81 | .list-complete-enter, .list-complete-leave-active 82 | opacity: 0 83 | transform: translateY(30px) 84 | 85 | .list-complete-leave-active 86 | position: absolute 87 | 88 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Vue2实战:模仿卖座电影 2 | > 使用Vue2.0全家桶仿制[卖座电影](http://m.maizuo.com/v4/?co=maizuo) 3 | > 4 | > 仓库:[https://github.com/ChuckCZC/vue-demo-maizuo](https://github.com/ChuckCZC/vue-demo-maizuo) 5 | 6 | ## 声明 7 | 8 | 该项目所用到的API均来自 [卖座电影](http://m.maizuo.com/v4/?co=maizuo) ,采用chrome 开发者工具分析出来的,只是为了得到数据进行排版练习,完全出于学习用途,并无恶意,请卖座电影官方海涵。 9 | 10 | ## 参考资料 11 | 12 | vue2.0中文文档 : [https://vuefe.cn/v2/guide/](https://vuefe.cn/v2/guide/) 13 | 14 | vue-router文档 : [https://router.vuejs.org/](https://router.vuejs.org/) 15 | 16 | vuex文档 : [https://vuex.vuejs.org/](https://vuex.vuejs.org/) 17 | 18 | 轮播插件swiper : [https://github.com/surmon-china/vue-awesome-swiper](https://github.com/surmon-china/vue-awesome-swiper) 19 | 20 | 数据请求axios : [https://www.npmjs.com/package/axios](https://www.npmjs.com/package/axios) 21 | 22 | 项目参考 : [https://github.com/zhengguorong/maizuo/tree/master/vuexMaizuo2](https://github.com/zhengguorong/maizuo/tree/master/vuexMaizuo2) 23 | 24 | ## 项目结构 25 | 26 | ![项目结构](readme_img/1.png) 27 | 28 | ​ 项目由vue-cli直接生成手脚架,之后调整`config/index.js`的build>assetsPublicPath为'./',该属性跟打包后index.html的css和js的引入路径有关;开发过程中在build/dev-server.js配置相关代码以跨域 29 | 30 | ```javascript 31 | app.use('/api',proxyMiddleware({ 32 | target:'http://m.maizuo.com/v4', 33 | changeOrigin:true 34 | })) 35 | ``` 36 | 37 | ## 界面预览 38 | 39 | ![首页](readme_img/home.png) 40 | 41 | ![首页](readme_img/home_2.png) 42 | 43 | ![首页](readme_img/film.png) 44 | 45 | ![首页](readme_img/detail.png) 46 | 47 | ![首页](readme_img/cinema.png) 48 | 49 | ![首页](readme_img/cinema_2.png) 50 | 51 | > ps:只是模仿了以上页面,这几个页面感觉已经包括了vue的常用功能了,后面的页面则是跳去卖座电影的官网了;由于只是几个页面,故各个页面没有将相关的子项分离成组件,如果项目庞大,建议分离成组件形式 52 | 53 | ##### 最后,如果该demo对你学习vue有帮助,麻烦给个star,谢谢##### 54 | 55 | 56 | ## Build 57 | 58 | ``` bash 59 | # install dependencies 60 | npm install 61 | 62 | # serve with hot reload at localhost:8080 63 | npm run dev 64 | 65 | # build for production with minification 66 | npm run build 67 | ``` 68 | 69 | For detailed explanation on how things work, checkout the [guide](http://vuejs-templates.github.io/webpack/) and [docs for vue-loader](http://vuejs.github.io/vue-loader). 70 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 14 | 72 | -------------------------------------------------------------------------------- /src/vuex/modules/film.js: -------------------------------------------------------------------------------- 1 | import api from '../api' 2 | import * as types from '../types' 3 | 4 | const state = { 5 | nowPlayingList:[], 6 | comingSoonList:[], 7 | nowPage:0, 8 | comingPage:0, 9 | getNowMore:true, 10 | getComingMore:true, 11 | } 12 | 13 | const actions = { 14 | //获取热映列表 15 | //偷懒没有弄加载更多的效果,故去掉loading 16 | getNowPlayList:function({commit}){ 17 | if(state.getNowMore){ 18 | // commit(types.COM_LOADING_STATUS,true); 19 | commit(types.FILE_NOWPLAYING_NUM); 20 | api.getNowPlayList(state.nowPage,function(res){ 21 | commit(types.FILM_GET_NOWPLAYING,res.data) 22 | // commit(types.COM_LOADING_STATUS,false); 23 | }) 24 | } 25 | 26 | }, 27 | //获取即将上映列表 28 | getComingList:function({commit}){ 29 | if(state.getComingMore){ 30 | // commit(types.COM_LOADING_STATUS,true); 31 | commit(types.FILM_COMINGSOON_NUM); 32 | api.getComingList(state.comingPage,function(res){ 33 | commit(types.FILM_GET_COMINGSOON,res.data) 34 | // commit(types.COM_LOADING_STATUS,false); 35 | }) 36 | } 37 | } 38 | } 39 | const getters = { 40 | getNowPlayList: state => state.nowPlayingList, 41 | getComingList: state => state.comingSoonList, 42 | } 43 | 44 | const mutations = { 45 | [types.FILM_GET_NOWPLAYING](state,res){ 46 | console.log(state.getNowMore,res.page) 47 | state.nowPlayingList = state.nowPlayingList.concat(res.films) 48 | //根据current跟total的关系判断是否还有下一页 49 | state.getNowMore = res.page.current.title 5 | width: 100% 6 | height: 40*$px 7 | line-height: 40*$px 8 | border-bottom: 1px solid #fff 9 | color: #636363 10 | font-size: 14*$px 11 | padding-left: 15*$px 12 | &:last-child 13 | .title 14 | border-bottom: none 15 | .list 16 | .shop 17 | background: #fff 18 | border-bottom: 1px solid #e1e1e1 19 | font-size: 14*$px 20 | padding: 15*$px 21 | line-height: 180% 22 | @include box(justify,start) 23 | .desc 24 | width: 300*$px 25 | .title 26 | font-size: 16*$px 27 | span 28 | font-size: 10*$px 29 | display: inline-block 30 | vertical-align: middle 31 | border-radius: 50% 32 | width: 16*$px 33 | height: 16*$px 34 | text-align: center 35 | line-height: 14*$px 36 | color: #fd9c77 37 | border: 1px solid #fd9c77 38 | margin-left: 5*$px 39 | span:last-child 40 | color: #a5c1d5 41 | border: 1px solid #a5c1d5 42 | .tip 43 | padding: 5*$px 10*$px 44 | span 45 | display: inline-block 46 | color: #fff 47 | font-size: 11*$px 48 | padding: 0*$px 4*$px 49 | border-radius: 4*$px 50 | &.sundry 51 | background: #51add0 52 | margin-right: 8*$px 53 | &.discount 54 | background: #ff9658 55 | .area 56 | color: #ccc 57 | font-size: 13*$px 58 | @include nowrap 59 | .extra 60 | color: #ccc 61 | font-size: 13*$px 62 | .price 63 | width: 45*$px 64 | color: #fc8637 65 | font-size: 16*$px 66 | 67 | 68 | -------------------------------------------------------------------------------- /src/pages/home.vue: -------------------------------------------------------------------------------- 1 | 51 | 97 | -------------------------------------------------------------------------------- /src/assets/icon/iconfont.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Created by FontForge 20120731 at Thu Dec 22 15:22:02 2016 6 | By admin 7 | 8 | 9 | 10 | 24 | 26 | 28 | 30 | 32 | 34 | 38 | 40 | 43 | 45 | 47 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /src/pages/film.vue: -------------------------------------------------------------------------------- 1 | 38 | --------------------------------------------------------------------------------