├── src ├── store │ ├── getters.js │ ├── index.js │ └── modules │ │ ├── collect.js │ │ ├── searchHistory.js │ │ ├── song.js │ │ └── location.js ├── scss │ ├── index.scss │ ├── base.scss │ ├── variable.scss │ ├── mixin.scss │ └── reset.scss ├── assets │ ├── logo.png │ ├── avatar.jpeg │ ├── img-error.jpg │ ├── img-error.png │ ├── douban-logo.png │ ├── img-loading.gif │ └── wangyi-logo.gif ├── components │ ├── my-ui │ │ ├── packages │ │ │ ├── tabs │ │ │ │ ├── index.js │ │ │ │ └── src │ │ │ │ │ └── main.vue │ │ │ ├── swipe │ │ │ │ ├── index.js │ │ │ │ └── src │ │ │ │ │ └── main.vue │ │ │ ├── loading │ │ │ │ ├── index.js │ │ │ │ └── src │ │ │ │ │ └── main.vue │ │ │ ├── tab-item │ │ │ │ ├── index.js │ │ │ │ └── src │ │ │ │ │ └── main.vue │ │ │ └── swipe-item │ │ │ │ ├── index.js │ │ │ │ └── src │ │ │ │ └── main.vue │ │ └── src │ │ │ ├── mixins │ │ │ ├── findParent.js │ │ │ └── emitter.js │ │ │ └── index.js │ ├── wy-skeleton │ │ └── index.vue │ ├── header │ │ └── index.vue │ ├── horizontal-list │ │ └── index.vue │ ├── vertical-list │ │ └── index.vue │ ├── song-list │ │ └── index.vue │ └── progress │ │ └── index.vue ├── directive │ ├── loading │ │ ├── loading.gif │ │ └── index.js │ ├── index.js │ └── drag │ │ └── index.js ├── views │ ├── rank │ │ └── index.vue │ ├── layout │ │ ├── index.vue │ │ └── components │ │ │ ├── AppMain.vue │ │ │ └── AppTabs.vue │ ├── movie-detail │ │ ├── components │ │ │ ├── MovieInfo.vue │ │ │ ├── MovieComment.vue │ │ │ ├── Operate.vue │ │ │ └── CastDetail.vue │ │ └── index.vue │ ├── user │ │ └── index.vue │ ├── welcome │ │ └── index.vue │ ├── song-list-detail │ │ └── index.vue │ ├── music │ │ └── index.vue │ ├── search │ │ └── index.vue │ ├── movie-list │ │ └── index.vue │ └── music-player │ │ └── index.vue ├── createApi.js ├── mixins │ ├── popup.js │ ├── showMovieDetail.js │ └── location.js ├── App.vue ├── vant.js ├── main.js ├── utils │ ├── searchHistory.js │ ├── request.js │ ├── song.js │ ├── common.js │ ├── collect.js │ └── dom.js ├── router.js └── api │ ├── wangyi.js │ └── douban.js ├── public ├── favicon.ico └── index.html ├── babel.config.js ├── .gitignore ├── vue.config.js ├── package.json └── README.md /src/store/getters.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/scss/index.scss: -------------------------------------------------------------------------------- 1 | @import 'base.scss'; 2 | @import 'reset.scss'; -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OuZuYu/vue-vant-douban-wangyiyun/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OuZuYu/vue-vant-douban-wangyiyun/HEAD/src/assets/logo.png -------------------------------------------------------------------------------- /src/components/my-ui/packages/tabs/index.js: -------------------------------------------------------------------------------- 1 | import MyTabs from './src/main'; 2 | 3 | export default MyTabs; -------------------------------------------------------------------------------- /src/assets/avatar.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OuZuYu/vue-vant-douban-wangyiyun/HEAD/src/assets/avatar.jpeg -------------------------------------------------------------------------------- /src/components/my-ui/packages/swipe/index.js: -------------------------------------------------------------------------------- 1 | import MySwipe from './src/main'; 2 | 3 | export default MySwipe; -------------------------------------------------------------------------------- /src/assets/img-error.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OuZuYu/vue-vant-douban-wangyiyun/HEAD/src/assets/img-error.jpg -------------------------------------------------------------------------------- /src/assets/img-error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OuZuYu/vue-vant-douban-wangyiyun/HEAD/src/assets/img-error.png -------------------------------------------------------------------------------- /src/components/my-ui/packages/loading/index.js: -------------------------------------------------------------------------------- 1 | import MyLoading from './src/main'; 2 | 3 | export default MyLoading; -------------------------------------------------------------------------------- /src/components/my-ui/packages/tab-item/index.js: -------------------------------------------------------------------------------- 1 | import MyTabItem from './src/main'; 2 | 3 | export default MyTabItem; -------------------------------------------------------------------------------- /src/assets/douban-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OuZuYu/vue-vant-douban-wangyiyun/HEAD/src/assets/douban-logo.png -------------------------------------------------------------------------------- /src/assets/img-loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OuZuYu/vue-vant-douban-wangyiyun/HEAD/src/assets/img-loading.gif -------------------------------------------------------------------------------- /src/assets/wangyi-logo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OuZuYu/vue-vant-douban-wangyiyun/HEAD/src/assets/wangyi-logo.gif -------------------------------------------------------------------------------- /src/components/my-ui/packages/swipe-item/index.js: -------------------------------------------------------------------------------- 1 | import MySwipeItem from './src/main'; 2 | 3 | export default MySwipeItem; -------------------------------------------------------------------------------- /src/directive/loading/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OuZuYu/vue-vant-douban-wangyiyun/HEAD/src/directive/loading/loading.gif -------------------------------------------------------------------------------- /src/views/rank/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | 13 | 16 | -------------------------------------------------------------------------------- /src/scss/base.scss: -------------------------------------------------------------------------------- 1 | .iconfont{ 2 | font-family:"iconfont" !important; 3 | font-size:16px;font-style:normal; 4 | -webkit-font-smoothing: antialiased; 5 | -webkit-text-stroke-width: 0.2px; 6 | -moz-osx-font-smoothing: grayscale; 7 | } -------------------------------------------------------------------------------- /src/scss/variable.scss: -------------------------------------------------------------------------------- 1 | $white: #fff; 2 | $theme: #42bd56; 3 | $red: #FF8C69; 4 | $green: #00CD00; 5 | $yellow: #FFB90F; 6 | $gray-light: #eee; 7 | $gray-deep: #777; 8 | $black-light: #333; 9 | $wrap-padding: 16px; 10 | 11 | $theme-wy: #58C976; -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/app' 4 | ], 5 | 6 | plugins: [ 7 | ['import', { 8 | libraryName: 'vant', 9 | libraryDirectory: 'es', 10 | style: true 11 | }, 'vant'] 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | # local env files 6 | .env.local 7 | .env.*.local 8 | 9 | # Log files 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | 14 | # Editor directories and files 15 | .idea 16 | .vscode 17 | *.suo 18 | *.ntvs* 19 | *.njsproj 20 | *.sln 21 | *.sw* 22 | -------------------------------------------------------------------------------- /src/directive/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | 3 | import loading from './loading'; 4 | import drag from './drag'; 5 | 6 | const directive = [ 7 | { name: 'loading', content: loading }, 8 | { name: 'drag', content: drag } 9 | ]; 10 | 11 | directive.forEach(directive => { 12 | Vue.directive(directive.name, directive.content); 13 | }); -------------------------------------------------------------------------------- /src/createApi.js: -------------------------------------------------------------------------------- 1 | import CreateAPI from 'vue-create-api'; 2 | import Vue from 'vue'; 3 | import MovieDetail from '@/views/movie-detail'; 4 | import CastDetail from '@/views/movie-detail/components/CastDetail'; 5 | import SongList from '@/views/song-list-detail'; 6 | 7 | Vue.use(CreateAPI); 8 | 9 | Vue.createAPI(MovieDetail) 10 | Vue.createAPI(CastDetail) 11 | Vue.createAPI(SongList) -------------------------------------------------------------------------------- /src/mixins/popup.js: -------------------------------------------------------------------------------- 1 | const EVENT_SHOW = 'show' 2 | const EVENT_HIDE = 'hide' 3 | 4 | export default { 5 | data() { 6 | return { 7 | visible: false 8 | } 9 | }, 10 | methods: { 11 | show() { 12 | this.visible = true 13 | this.$emit(EVENT_SHOW) 14 | }, 15 | hide() { 16 | this.visible = false 17 | this.$emit(EVENT_HIDE) 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/views/layout/index.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 16 | 17 | -------------------------------------------------------------------------------- /vue.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | baseUrl: './', 3 | css: { 4 | loaderOptions: { 5 | sass: { 6 | // 引入全局变量 7 | data: `@import "@/scss/variable.scss";` 8 | } 9 | } 10 | }, 11 | devServer: { 12 | proxy: { 13 | '/v2': { 14 | target: 'https://api.douban.com', 15 | changeOrigin: true 16 | } 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /src/store/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuex from 'vuex' 3 | import getters from './getters'; 4 | import location from './modules/location' 5 | import collect from './modules/collect' 6 | import searchHistory from './modules/searchHistory' 7 | import song from './modules/song' 8 | 9 | Vue.use(Vuex) 10 | 11 | export default new Vuex.Store({ 12 | modules: { 13 | location, 14 | collect, 15 | searchHistory, 16 | song 17 | }, 18 | 19 | getters 20 | }); -------------------------------------------------------------------------------- /src/views/layout/components/AppMain.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 14 | 15 | -------------------------------------------------------------------------------- /src/components/my-ui/src/mixins/findParent.js: -------------------------------------------------------------------------------- 1 | export default { 2 | methods: { 3 | findParent (componentName) { 4 | let parent = this.$parent || this.$root; 5 | let name = parent.$options.name; 6 | 7 | while (parent && (!name || name !== componentName)) { 8 | parent = parent.$parent; 9 | 10 | if (parent) { 11 | name = parent.$options.name; 12 | } 13 | } 14 | 15 | if (parent) return parent; 16 | } 17 | } 18 | }; -------------------------------------------------------------------------------- /src/components/my-ui/src/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | 3 | import MyTabs from '../packages/tabs/index.js'; 4 | import MyTabItem from '../packages/tab-item/index.js'; 5 | import MySwipe from '../packages/swipe/index.js'; 6 | import MySwipeItem from '../packages/swipe-item/index.js'; 7 | import MyLoading from '../packages/loading'; 8 | 9 | const components = [ 10 | MyTabs, 11 | MyTabItem, 12 | MySwipe, 13 | MySwipeItem, 14 | MyLoading 15 | ]; 16 | 17 | components.map(component => { 18 | Vue.component(component.name, component); 19 | }); -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 25 | 26 | 32 | -------------------------------------------------------------------------------- /src/vant.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import { Cell, Row, Col, Notify, Tag, Lazyload, List, Rate, Tab, Tabs, button, Field, Icon, Toast, Dialog } from 'vant'; 3 | import errImg from './assets/img-error.png'; 4 | import loadingImg from './assets/img-loading.gif'; 5 | 6 | [ 7 | Cell, 8 | Row, 9 | Col, 10 | Notify, 11 | Tag, 12 | List, 13 | Rate, 14 | Tab, 15 | Tabs, 16 | button, 17 | Field, 18 | Icon, 19 | Toast, 20 | Dialog 21 | ].forEach(component => { 22 | Vue.use(component); 23 | }); 24 | 25 | Vue.use(Lazyload, { 26 | preLoad: 1.3, 27 | error: errImg, 28 | loading: loadingImg 29 | }) -------------------------------------------------------------------------------- /src/scss/mixin.scss: -------------------------------------------------------------------------------- 1 | @mixin fixedLayout($top: 0, $right: 0, $bottom: 0, $left: 0, $bg: #fff) { 2 | position: fixed; 3 | left: $left; 4 | top: $top; 5 | right: $right; 6 | bottom: $bottom; 7 | background: $bg; 8 | overflow: auto; 9 | } 10 | 11 | @mixin absoluteLayout($top: 0, $right: 0, $bottom: 0, $left: 0) { 12 | position: absolute; 13 | left: $left; 14 | top: $top; 15 | right: $right; 16 | bottom: $bottom; 17 | } 18 | 19 | @mixin heightLineHeight($height) { 20 | height: $height; 21 | line-height: $height; 22 | } 23 | 24 | @mixin textEllipsis () { 25 | white-space: nowrap; 26 | overflow: hidden; 27 | text-overflow: ellipsis; 28 | } -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import App from './App.vue' 3 | import router from './router' 4 | import store from './store/index' 5 | import 'lib-flexible/flexible.js' 6 | import { delLoading } from '@/utils/dom' 7 | 8 | import './scss/index.scss'; 9 | 10 | import './directive'; 11 | 12 | import './vant'; 13 | 14 | import './createApi'; 15 | 16 | // 引入自定义ui组件 17 | import '@/components/my-ui/src'; 18 | 19 | Vue.config.productionTip = false 20 | 21 | // 这个是删除loading指令生成的元素的,现已弃用loading指令,改用loading组件了。 22 | router.beforeEach((to, from, next) => { 23 | delLoading(); 24 | next(); 25 | }) 26 | 27 | new Vue({ 28 | router, 29 | store, 30 | render: h => h(App) 31 | }).$mount('#app') 32 | -------------------------------------------------------------------------------- /src/components/my-ui/packages/tab-item/src/main.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 30 | 31 | 34 | -------------------------------------------------------------------------------- /src/mixins/showMovieDetail.js: -------------------------------------------------------------------------------- 1 | export default { 2 | data () { 3 | return { 4 | detailComp: null, 5 | selectedMovie: {} 6 | } 7 | }, 8 | 9 | methods: { 10 | handleMovieSelect(movie) { 11 | this.selectedMovie = movie; 12 | this.showDetail(); 13 | }, 14 | 15 | showDetail() { 16 | 17 | // 创建movieDetail组件api 18 | this.detailComp = this.detailComp || this.$createMovieDetail({ 19 | $props: { 20 | movie: 'selectedMovie' 21 | } 22 | }) 23 | this.detailComp.show() 24 | this.$nextTick(_ => { 25 | this.detailComp.init() 26 | }) 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /src/utils/searchHistory.js: -------------------------------------------------------------------------------- 1 | export function getSearchHistory () { 2 | return localStorage.searchHistory ? JSON.parse(localStorage.searchHistory) : []; 3 | } 4 | 5 | export function setSearchHistory (keyword) { 6 | let searchHistory = getSearchHistory(); 7 | 8 | if (!searchHistory.includes(keyword)) { 9 | searchHistory.unshift(keyword); 10 | localStorage.searchHistory = JSON.stringify(searchHistory); 11 | return searchHistory; 12 | } else { 13 | return false; 14 | } 15 | } 16 | 17 | export function delSearchHistory (keyword) { 18 | let searchHistory = getSearchHistory(), 19 | index = searchHistory.findIndex(val => val === keyword); 20 | 21 | searchHistory.splice(index, 1); 22 | localStorage.searchHistory = JSON.stringify(searchHistory); 23 | return searchHistory; 24 | } -------------------------------------------------------------------------------- /src/store/modules/collect.js: -------------------------------------------------------------------------------- 1 | import { 2 | getWantSeeMovies, 3 | setWantSeeMovie, 4 | getHaveSeenMovies, 5 | setHaveSeenMovie } from '@/utils/collect'; 6 | 7 | const collect = { 8 | state: { 9 | wantSeeMovies: getWantSeeMovies(), 10 | haveSeenMovies: getHaveSeenMovies() 11 | }, 12 | 13 | mutations: { 14 | SET_WANT_SEE: (state, wantSeeMovies) => { 15 | state.wantSeeMovies = wantSeeMovies; 16 | }, 17 | 18 | SET_HAVE_SEEN: (state, haveSeenMovies) => { 19 | state.haveSeenMovies = haveSeenMovies; 20 | } 21 | }, 22 | 23 | actions: { 24 | SetWantSee({ commit }, movie) { 25 | commit('SET_WANT_SEE', setWantSeeMovie(movie)) 26 | }, 27 | 28 | SetHaveSeen({ commit }, movie) { 29 | commit('SET_HAVE_SEEN', setHaveSeenMovie(movie)) 30 | } 31 | } 32 | } 33 | 34 | export default collect; -------------------------------------------------------------------------------- /src/router.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Router from 'vue-router' 3 | 4 | Vue.use(Router) 5 | 6 | export default new Router({ 7 | // mode: 'history', 8 | base: process.env.BASE_URL, 9 | routes: [ 10 | { 11 | path: '/', 12 | redirect: '/douban' 13 | }, 14 | { 15 | path: '/douban', 16 | component: () => import('@/views/layout/index.vue'), // layout component 17 | redirect: '/douban/movie-list', 18 | children: [{ 19 | path: 'movie-list', 20 | component: () => import('@/views/movie-list/index.vue') 21 | }, 22 | { 23 | path: 'my-profile', 24 | component: () => import('@/views/user/index.vue') 25 | }] 26 | }, 27 | { 28 | path: '/douban/search', 29 | component: () => import('@/views/search/index.vue') 30 | }, 31 | { 32 | path: '/wangyi', 33 | component: () => import('@/views/layout/index.vue'), 34 | children: [{ 35 | path: 'music', 36 | component: () => import('@/views/music/index.vue') 37 | }] 38 | } 39 | ] 40 | }) 41 | -------------------------------------------------------------------------------- /src/directive/drag/index.js: -------------------------------------------------------------------------------- 1 | function drag (el, binding) { 2 | el.ontouchstart = function (ev) { 3 | let touches = ev.touches[0]; 4 | let documentW = document.body.clientWidth; 5 | let documenth = document.body.clientHeight; 6 | let maxLeft = documentW - el.clientWidth; 7 | let maxTop = documenth - el.clientHeight; 8 | let disX = touches.clientX - el.offsetLeft; 9 | let disY = touches.clientY - el.offsetTop; 10 | 11 | document.ontouchmove = function (ev) { 12 | let touches = ev.touches[0]; 13 | let l = touches.clientX - disX; 14 | let t = touches.clientY - disY; 15 | el.style.left = l < 0 ? 0 : l > maxLeft ? maxLeft + 'px' : l + 'px'; 16 | el.style.top = t < 0 ? 0 : t > maxTop ? maxTop + 'px' : t + 'px'; 17 | }; 18 | document.ontouchend = function () { 19 | document.ontouchmove = null; 20 | document.ontouchend = null; 21 | }; 22 | }; 23 | } 24 | 25 | export default { 26 | bind (el, binding) { 27 | drag(el, binding); 28 | } 29 | } -------------------------------------------------------------------------------- /src/store/modules/searchHistory.js: -------------------------------------------------------------------------------- 1 | import { 2 | getSearchHistory, 3 | setSearchHistory, 4 | delSearchHistory 5 | } from '@/utils/searchHistory'; 6 | 7 | const searchHistory = { 8 | state: { 9 | history: getSearchHistory(), 10 | }, 11 | 12 | mutations: { 13 | SET_SEARCH_HISTORY(state, history) { 14 | state.history = history; 15 | }, 16 | 17 | CLEAR_SEARCH_HISTORY (state) { 18 | state.history = []; 19 | } 20 | }, 21 | 22 | actions: { 23 | SetSearchHistory ({ commit }, keyword) { 24 | let searchHistory = setSearchHistory(keyword); 25 | if (!searchHistory) return; 26 | commit('SET_SEARCH_HISTORY', searchHistory) 27 | }, 28 | 29 | DelSearchHistory ({ commit }, keyword) { 30 | commit('SET_SEARCH_HISTORY', delSearchHistory(keyword)); 31 | }, 32 | 33 | ClearSearchHistory ({ commit }) { 34 | localStorage.removeItem('searchHistory'); 35 | commit('CLEAR_SEARCH_HISTORY'); 36 | } 37 | } 38 | } 39 | 40 | export default searchHistory; -------------------------------------------------------------------------------- /src/components/my-ui/packages/swipe-item/src/main.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 42 | 43 | 56 | -------------------------------------------------------------------------------- /src/utils/request.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | 3 | // 取消前面请求 4 | let pending = []; 5 | let cancelToken = axios.CancelToken; 6 | let removePending = (config) => { 7 | for(let p in pending){ 8 | if(pending[p].u === config.url + '&' + config.method) { 9 | pending[p].f(); 10 | pending.splice(p, 1); 11 | } 12 | } 13 | } 14 | 15 | const service = axios.create({ 16 | baseURL: '', 17 | timeout: 50000 18 | }) 19 | 20 | // 请求拦截 21 | service.interceptors.request.use( 22 | config => { 23 | removePending(config); //在一个ajax发送前执行一下取消操作 24 | config.cancelToken = new cancelToken((c)=>{ 25 | pending.push({ u: config.url + '&' + config.method, f: c }); 26 | }); 27 | return config; 28 | }, 29 | error => { 30 | Promise.reject(error) 31 | } 32 | ) 33 | 34 | // 响应拦截 35 | service.interceptors.response.use( 36 | response => { 37 | removePending(response.config); 38 | return response.data 39 | }, 40 | error => { 41 | console.log('err' + error) 42 | return Promise.reject(error) 43 | } 44 | ) 45 | 46 | export default service -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vant-shop", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build" 8 | }, 9 | "dependencies": { 10 | "axios": "^0.18.0", 11 | "lib-flexible": "^0.3.2", 12 | "vant": "^1.5.1", 13 | "vue": "^2.5.21", 14 | "vue-create-api": "^0.2.0", 15 | "vue-router": "^3.0.1", 16 | "vuex": "^3.0.1" 17 | }, 18 | "devDependencies": { 19 | "@vue/cli-plugin-babel": "^3.2.0", 20 | "@vue/cli-service": "^3.2.0", 21 | "babel-plugin-import": "^1.11.0", 22 | "node-sass": "^4.9.0", 23 | "postcss-pxtorem": "^4.0.1", 24 | "sass-loader": "^7.0.1", 25 | "vue-template-compiler": "^2.5.21" 26 | }, 27 | "postcss": { 28 | "plugins": { 29 | "autoprefixer": { 30 | "browsers": [ 31 | "Android >= 4.0", 32 | "iOS >= 7" 33 | ] 34 | }, 35 | "postcss-pxtorem": { 36 | "rootValue": 37.5, 37 | "propList": [ 38 | "*" 39 | ] 40 | } 41 | } 42 | }, 43 | "browserslist": [ 44 | "> 1%", 45 | "last 2 versions", 46 | "not ie <= 8" 47 | ] 48 | } 49 | -------------------------------------------------------------------------------- /src/components/my-ui/src/mixins/emitter.js: -------------------------------------------------------------------------------- 1 | function broadcast(componentName, eventName, params) { 2 | this.$children.forEach(child => { 3 | var name = child.$options.name; 4 | 5 | if (name === componentName) { 6 | child.$emit.apply(child, [eventName].concat(params)); 7 | } else { 8 | broadcast.apply(child, [componentName, eventName].concat([params])); 9 | } 10 | }); 11 | } 12 | export default { 13 | methods: { 14 | dispatch(componentName, eventName, params) { 15 | var parent = this.$parent || this.$root; 16 | var name = parent.$options.name; 17 | 18 | while (parent && (!name || name !== componentName)) { 19 | parent = parent.$parent; 20 | 21 | if (parent) { 22 | name = parent.$options.name; 23 | } 24 | } 25 | if (parent) { 26 | parent.$emit.apply(parent, [eventName].concat(params)); 27 | } 28 | }, 29 | 30 | broadcast(componentName, eventName, params) { 31 | broadcast.call(this, componentName, eventName, params); 32 | } 33 | } 34 | }; 35 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | vue-douban-wangyi 18 | 19 | 20 | 23 |
24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/components/wy-skeleton/index.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 18 | 19 | 62 | -------------------------------------------------------------------------------- /src/scss/reset.scss: -------------------------------------------------------------------------------- 1 | /* http://meyerweb.com/eric/tools/css/reset/ 2 | v2.0 | 20110126 3 | License: none (public domain) 4 | */ 5 | 6 | html, body, div, span, applet, object, iframe, 7 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 8 | a, abbr, acronym, address, big, cite, code, 9 | del, dfn, em, img, ins, kbd, q, s, samp, 10 | small, strike, strong, sub, sup, tt, var, 11 | b, u, i, center, 12 | dl, dt, dd, ol, ul, li, 13 | fieldset, form, label, legend, 14 | table, caption, tbody, tfoot, thead, tr, th, td, 15 | article, aside, canvas, details, embed, 16 | figure, figcaption, footer, header, hgroup, 17 | menu, nav, output, ruby, section, summary, 18 | time, mark, audio, video { 19 | margin: 0; 20 | padding: 0; 21 | border: 0; 22 | font-size: 100%; 23 | font: inherit; 24 | vertical-align: baseline; 25 | } 26 | /* HTML5 display-role reset for older browsers */ 27 | article, aside, details, figcaption, figure, 28 | footer, header, hgroup, menu, nav, section { 29 | display: block; 30 | } 31 | body { 32 | line-height: 1; 33 | } 34 | ol, ul { 35 | list-style: none; 36 | } 37 | blockquote, q { 38 | quotes: none; 39 | } 40 | blockquote:before, blockquote:after, 41 | q:before, q:after { 42 | content: ''; 43 | content: none; 44 | } 45 | table { 46 | border-collapse: collapse; 47 | border-spacing: 0; 48 | } -------------------------------------------------------------------------------- /src/components/header/index.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 19 | 20 | 61 | -------------------------------------------------------------------------------- /src/utils/song.js: -------------------------------------------------------------------------------- 1 | class Song { 2 | constructor({ id, name, coverImgUrl, tracks, trackIds }) { 3 | this.id = id; 4 | this.name = name; 5 | this.coverImgUrl = coverImgUrl; 6 | this.tracks = tracks.map(val => new Track(val)); 7 | this.trackIds = trackIds; 8 | } 9 | } 10 | 11 | class Track { 12 | constructor ({ id, name, ar, al, publishTime }) { 13 | this.id = id; 14 | this.name = name; 15 | this.artists = ar; 16 | this.album = al; 17 | this.publishTime = publishTime; 18 | } 19 | } 20 | 21 | export function setSongData (data) { 22 | return new Song(data); 23 | } 24 | 25 | // 歌词转换 26 | export function lrc2Json (lrc) { 27 | var arr = lrc.split('\n') 28 | let timeReg = /^\[.*\](.*)/ 29 | let json = [] 30 | arr = arr.slice(0, arr.length - 1); 31 | arr.forEach(item => { 32 | let match = item.match(timeReg); 33 | let time = match ? match[0].substr(1, 8) : ''; 34 | let minute = time.substr(0, 2) 35 | let second = time.substr(3, 2) 36 | let ms = time.substr(6, 2) 37 | 38 | json.push({ 39 | time, 40 | ms: parseInt(minute) * 60 * 1000 + parseInt(second) * 1000 + parseInt(ms) * 10, 41 | content: match ? match[1] : '' 42 | }) 43 | }) 44 | return json; 45 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vue+vant实现豆瓣电影加网易云音乐webapp 2 | 3 | ## 说明 4 | 做这个项目最主要的目的是为了**练习与巩固vue**,所以个人是本着**怎么折腾怎么来**的原则做的。怎么折腾法呢?比如豆瓣部分我使用了vant,网易云部分则没有,网易的ui组件则是自己造的轮子,当然在实际开发中肯定不会这么搞啦。。还有个人平时看过的知识,像createApi啥的会应用上,毕竟看过不如写过嘛,所以通过这么“折腾”的项目,能够学习到很多东西哦! 5 | 6 | ## 预览 7 | [点我预览](http://67.216.223.155/dbwy) 或者手机扫描以下二维码 (pc请在移动端模式下预览)。 8 | 9 | ![二维码](https://user-gold-cdn.xitu.io/2019/3/3/16944327605f8e52?imageView2/0/w/1280/h/960/format/webp/ignore-error/1) 10 | 11 | ## 运行 12 | ``` 13 | git clone git@github.com:OuZuYu/vue-vant-douban-wangyiyun.git 14 | ``` 15 | ``` 16 | npm install 17 | ``` 18 | ``` 19 | npm run serve 20 | ``` 21 | 22 | ## 动图预览 23 | ##### 豆瓣电影 24 | ![豆瓣gif](https://i.loli.net/2019/03/22/5c945e20f32be.gif) 25 | 26 | ##### 网易云音乐 27 | ![网易gif](https://i.loli.net/2019/03/22/5c947c1dbd9c5.gif) 28 | 29 | ##### 收藏 30 | ![收藏gif](https://i.loli.net/2019/03/22/5c945e6850ba0.gif) 31 | 32 | 33 | ## 主要功能: 34 | 35 | 1. rem适配 36 | 37 | 2. 欢迎页 骨架屏 38 | 39 | 3. 地区定位 加载地区热映电影 即将上映 top250 40 | 41 | 4. 电影搜索 42 | 43 | 5. 电影详情 44 | 45 | 6. 影人详情 46 | 47 | 7. 跳转到电影网站观看电影 48 | 49 | 8. 电影收藏(想看,看过) 50 | 51 | 9. 歌单查看 52 | 53 | 10. 全屏播放器 可拖拽mini播放器 歌词滚动 54 | 55 | 11. ...... 56 | 57 | ### 2019-3-7更新: 58 | - 定位:先使用搜狐,若失败再使用百度地图。 59 | - 优化loading,从loading指令改成了loading组件。 60 | 61 | ### 2019-3-9更新 62 | - 发现歌单背景高斯模糊效果,在小米自带浏览器下过度时卡顿,所以去掉了改了下歌单样式。 63 | - 还是小米自带的浏览器。。修复了获取不了歌曲总长度的bug。 64 | - 修复了歌词过长时文字重叠bug。 65 | - 从一个歌单快速切换到另一个歌单时,可能会出现显示的是前一个歌单的歌曲的情况,所以axios增加请求前先取消前面请求的配置。 66 | 67 | ### 2019-3-22更新 68 | - 加快电影详情滑动速度 69 | - 增加手动输入城市 -------------------------------------------------------------------------------- /src/views/layout/components/AppTabs.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 23 | 24 | 56 | -------------------------------------------------------------------------------- /src/api/wangyi.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request'; 2 | import axios from 'axios'; 3 | 4 | const BASE = 'http://67.216.223.155:3000'; 5 | 6 | export function getBanner (params) { 7 | return request({ 8 | url: BASE + '/banner', 9 | method: 'get', 10 | params 11 | }) 12 | } 13 | 14 | export function getPersonalized (params) { 15 | return request({ 16 | url: BASE + '/personalized', 17 | method: 'get', 18 | params 19 | }) 20 | } 21 | 22 | export function getRecommonData (apiNames) { 23 | var api = { 24 | personalizedData: getBanner(), 25 | bannerData: getPersonalized() 26 | }, 27 | apiNames = apiNames || ['personalizedData', 'bannerData'], 28 | apiArr = apiNames.map(item => api[item]); 29 | 30 | return axios.all(apiArr).then(axios.spread(function (banner, personalized) { 31 | return Promise.resolve([banner, personalized]) 32 | })).catch(err => { 33 | return Promise.reject(err) 34 | }) 35 | } 36 | 37 | export function getPlayList (id) { 38 | return request({ 39 | url: BASE + '/playlist/detail', 40 | method: 'get', 41 | params: { 42 | id, 43 | t: Date.now() 44 | } 45 | }) 46 | } 47 | 48 | export function getMusicUrl (id) { 49 | return request({ 50 | url: BASE + '/song/url', 51 | method: 'get', 52 | params: { 53 | id 54 | } 55 | }) 56 | } 57 | 58 | export function getLyric (id) { 59 | return request({ 60 | url: BASE + '/lyric', 61 | method: 'get', 62 | params: { 63 | id 64 | } 65 | }) 66 | } -------------------------------------------------------------------------------- /src/mixins/location.js: -------------------------------------------------------------------------------- 1 | import { mapState, mapActions, mapMutations } from 'vuex' 2 | 3 | export default { 4 | computed: { 5 | ...mapState({ 6 | myCity: state => state.location.myCity 7 | }) 8 | }, 9 | 10 | methods: { 11 | ...mapActions([ 12 | 'GetCity' 13 | ]), 14 | 15 | ...mapMutations({ 16 | setCity: 'SET_CITY' 17 | }) 18 | 19 | // 直接用百度地图api获取城市 20 | /* getCity() { 21 | return new Promise((resolve, reject) => { 22 | if (this.$store.state.location.hasCity) { 23 | resolve(); 24 | } 25 | let myCity = new BMap.LocalCity(); 26 | myCity.get(res => { 27 | this.storeCity(res.name); 28 | resolve(res.name); 29 | }) 30 | }) 31 | }, */ 32 | 33 | /* 获取位置h5版 34 | getLocationData () { 35 | if (navigator.geolocation) { 36 | navigator.geolocation.getCurrentPosition(position => { 37 | this.setLocation(position); 38 | }, error => { 39 | this.$notify({ 40 | message: '获取位置失败,请稍后再试!', 41 | duration: 2000, 42 | background: '#FF8C69' 43 | }); 44 | }); 45 | } else { 46 | this.$notify({ 47 | message: '您的设备不支持navigator!', 48 | duration: 2000, 49 | background: '#FF8C69' 50 | }); 51 | } 52 | }, 53 | setLocation (position) { 54 | this.SaveLocation(position); 55 | }*/ 56 | } 57 | } -------------------------------------------------------------------------------- /src/utils/common.js: -------------------------------------------------------------------------------- 1 | /* 2 | @param dom 要滚动的元素 3 | @param fromY 开始位置 4 | @param toY 结束位置 5 | @param duration 滚动速度 6 | */ 7 | export function scrollTo (dom, fromY, toY, duration = 500) { 8 | let from = fromY, 9 | to = toY, 10 | s = Math.abs(from - to), // 滚动路程 11 | v = Math.ceil(s / duration * 50); // 速度 12 | 13 | // requestAnimationFrame性能更好更加顺滑 14 | if (!window.requestAnimationFrame) { 15 | window.requestAnimationFrame = (window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.msRequestAnimationFrame || function (callback) { return window.setTimeout(callback, 1000 / 60); }); 16 | } 17 | 18 | function scroll (start, end, step) { 19 | if (start === end) return; 20 | 21 | let d = (start + step > end) ? end : start + step; 22 | if (start > end) { 23 | d = (start - step < end) ? end : start - step; 24 | } 25 | 26 | dom.scrollTo(d, d); 27 | 28 | window.requestAnimationFrame(() => scroll(d, end, step)); 29 | } 30 | scroll(from, to, v); 31 | } 32 | 33 | // 节流 34 | export function throttle (fn, wait) { 35 | let timmer = null; 36 | 37 | return function () { 38 | if (!timmer) { // 只有当上一个定时器完成,才把执行fn添加到队列。 39 | timmer = setTimeout(() => { 40 | fn.apply(this, arguments); 41 | timmer = null; 42 | }, wait); 43 | } 44 | } 45 | } 46 | 47 | // 防抖 48 | export function debounce (fn, wait) { 49 | let timmer = null; 50 | 51 | return function () { 52 | if (timmer) clearTimeout(timmer); 53 | 54 | timmer = setTimeout(() => { 55 | fn.apply(this, arguments); 56 | }, wait); 57 | } 58 | } -------------------------------------------------------------------------------- /src/store/modules/song.js: -------------------------------------------------------------------------------- 1 | const song = { 2 | state: { 3 | tracks: [], // 歌单 4 | curSongIdx: 0, 5 | isFullscreen: false 6 | }, 7 | 8 | getters: { 9 | curSong: state => state.tracks[state.curSongIdx] 10 | }, 11 | 12 | mutations: { 13 | SET_TRACKS (state, tracks) { 14 | state.tracks = tracks; 15 | state.curSongIdx = 0; 16 | }, 17 | 18 | SET_SONG_INDEX (state, index) { 19 | state.curSongIdx = index; 20 | }, 21 | 22 | CHANGE_ISFULLSCREEN (state, isFullscreen) { 23 | state.isFullscreen = isFullscreen; 24 | } 25 | }, 26 | 27 | actions: { 28 | SetTracks ({ commit }, tracks) { 29 | commit('SET_TRACKS', tracks); 30 | }, 31 | 32 | SetSongIndex ({ commit }, index) { 33 | commit('SET_SONG_INDEX', index); 34 | }, 35 | 36 | ChangeTrack({ commit, state }, { direction, random }) { 37 | let curIndex = state.curSongIdx, 38 | length = state.tracks.length, 39 | lastIndex = length - 1, 40 | newIndex = 0; 41 | 42 | if (random) { 43 | newIndex = Math.floor(Math.random() * length) 44 | } else { 45 | if (direction === 'next') { 46 | newIndex = curIndex === lastIndex ? 0 : ++curIndex; 47 | } else if (direction === 'prev') { 48 | newIndex = curIndex === 0 ? lastIndex : --curIndex; 49 | } 50 | } 51 | 52 | commit('SET_SONG_INDEX', newIndex); 53 | }, 54 | 55 | ChangeIsFullScreen ({ commit }, fullScreen) { 56 | commit('CHANGE_ISFULLSCREEN', fullScreen); 57 | } 58 | } 59 | } 60 | 61 | export default song; -------------------------------------------------------------------------------- /src/api/douban.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request'; 2 | 3 | export function getMovie(params) { 4 | params.apikey = '0b2bdeda43b5688921839c8ecb20399b'; 5 | 6 | return request({ 7 | url: '/v2/movie/in_theaters', 8 | method: 'get', 9 | params 10 | }) 11 | } 12 | 13 | export function getTop250Movie(start, count) { 14 | const params = { 15 | apikey: '0b2bdeda43b5688921839c8ecb20399b', 16 | start, 17 | count 18 | } 19 | return request({ 20 | url: '/v2/movie/top250', 21 | method: 'get', 22 | params 23 | }) 24 | } 25 | 26 | export function getComingSoonMovie(start, count) { 27 | const params = { 28 | apikey: '0b2bdeda43b5688921839c8ecb20399b', 29 | start, 30 | count 31 | } 32 | return request({ 33 | url: '/v2/movie/coming_soon', 34 | method: 'get', 35 | params 36 | }) 37 | } 38 | 39 | export function getMovieDetail(id) { 40 | const params = { 41 | apikey: '0b2bdeda43b5688921839c8ecb20399b' 42 | } 43 | return request({ 44 | url: '/v2/movie/subject/' + id, 45 | method: 'get', 46 | params 47 | }) 48 | } 49 | 50 | export function searchMovie (keyword, tag, start, count) { 51 | const params = { 52 | apikey: '0b2bdeda43b5688921839c8ecb20399b', 53 | q: keyword, 54 | tag, 55 | start, 56 | count 57 | } 58 | return request({ 59 | url: '/v2/movie/search', 60 | method: 'get', 61 | params 62 | }) 63 | } 64 | 65 | export function getCastDetail (id) { 66 | const params = { 67 | apikey: '0b2bdeda43b5688921839c8ecb20399b', 68 | } 69 | return request({ 70 | url: '/v2/movie/celebrity/' + id, 71 | method: 'get', 72 | params 73 | }) 74 | } -------------------------------------------------------------------------------- /src/components/horizontal-list/index.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 40 | 41 | 86 | -------------------------------------------------------------------------------- /src/utils/collect.js: -------------------------------------------------------------------------------- 1 | export function getWantSeeMovies () { 2 | return localStorage.wantSeeMovies ? JSON.parse(localStorage.wantSeeMovies) : []; 3 | } 4 | 5 | // 在想看数组中添加或移除电影 6 | export function setWantSeeMovie (movie) { 7 | let wantSeeMoviesArr = getWantSeeMovies(); 8 | let index = wantSeeMoviesArr.findIndex(movieItem => movieItem.id === movie.id); 9 | if (index > -1) { 10 | wantSeeMoviesArr.splice(index, 1); 11 | } else { 12 | wantSeeMoviesArr.unshift(movie); 13 | } 14 | localStorage.wantSeeMovies = JSON.stringify(wantSeeMoviesArr); 15 | return wantSeeMoviesArr; 16 | } 17 | 18 | export function getHaveSeenMovies() { 19 | return localStorage.haveSeenMovies ? JSON.parse(localStorage.haveSeenMovies) : []; 20 | } 21 | 22 | // 在已看数组中添加或移除电影 23 | export function setHaveSeenMovie(movie) { 24 | let haveSeenMoviesArr = getHaveSeenMovies(); 25 | let index = haveSeenMoviesArr.findIndex(movieItem => movieItem.id === movie.id); 26 | 27 | if (index > -1) { 28 | haveSeenMoviesArr.splice(index, 1); 29 | } else { 30 | haveSeenMoviesArr.unshift(movie); 31 | } 32 | localStorage.haveSeenMovies = JSON.stringify(haveSeenMoviesArr); 33 | return haveSeenMoviesArr; 34 | } 35 | 36 | // 电影对象工厂 37 | export function setMovieObj (movie) { 38 | return { 39 | id: movie.id, 40 | title: movie.title, 41 | images: movie.images, 42 | rating: movie.rating, 43 | directors: movie.directors, 44 | casts: movie.casts 45 | } 46 | } 47 | 48 | // 检查是否已经存到了想看 49 | export function checkWantSee (id) { 50 | let wantSeeMovies = getWantSeeMovies(); 51 | let index = wantSeeMovies.findIndex(movie => movie.id === id); 52 | return index > -1; 53 | } 54 | 55 | // 检查是否已经存到了已看数组 56 | export function checkHaveSeen (id) { 57 | let wantSeeMovies = getHaveSeenMovies(); 58 | let index = wantSeeMovies.findIndex(movie => movie.id === id); 59 | return index > -1; 60 | } -------------------------------------------------------------------------------- /src/utils/dom.js: -------------------------------------------------------------------------------- 1 | const SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g; 2 | const MOZ_HACK_REGEXP = /^moz([A-Z])/; 3 | 4 | // 例:-moz-transition 替换为 -Moz-TRANSITION 5 | const camelCase = function (name) { 6 | return name.replace(SPECIAL_CHARS_REGEXP, function (_, separator, letter, offset) { 7 | /** 8 | * _:匹配文本 9 | * separator:第一个匹配组 10 | * letter:第二个匹配组 11 | * offset:匹配文本下标index 12 | */ 13 | return offset ? letter.toUpperCase() : letter; 14 | }).replace(MOZ_HACK_REGEXP, 'Moz$1'); 15 | }; 16 | 17 | export let cssUtils = { 18 | setStyle(el, prop, val) { 19 | el.style[prop] = val; 20 | }, 21 | 22 | setCss(el, styles) { 23 | for (let key of Object.keys(styles)) { 24 | this.setStyle(el, key, styles[key]); 25 | } 26 | } 27 | } 28 | 29 | // 获取dom到文档顶部或左边的距离 30 | export function getDistance(element, type) { 31 | let direction = type === 'top' ? 'offsetTop' : type === 'left' ? 'offsetLeft' : ''; 32 | let distance = element[direction]; 33 | let current = element.offsetParent; 34 | while (current !== null) { 35 | distance += current[direction]; 36 | current = current.offsetParent; 37 | } 38 | return distance; 39 | } 40 | 41 | export function delLoading () { 42 | let loadingDoms = document.querySelectorAll('.loading-wrap'); 43 | if (loadingDoms) { 44 | for (let loadingDom of loadingDoms) { 45 | document.body.removeChild(loadingDom); 46 | } 47 | } 48 | } 49 | 50 | /* istanbul ignore next */ 51 | export const getStyle = function (element, styleName) { 52 | if (!element || !styleName) return null; 53 | styleName = camelCase(styleName); 54 | if (styleName === 'float') { 55 | styleName = 'cssFloat'; 56 | } 57 | try { 58 | var computed = document.defaultView.getComputedStyle(element, ''); 59 | return element.style[styleName] || computed ? computed[styleName] : null; 60 | } catch (e) { 61 | return element.style[styleName]; 62 | } 63 | }; -------------------------------------------------------------------------------- /src/store/modules/location.js: -------------------------------------------------------------------------------- 1 | const SOHU_GETCITY_API = 'http://pv.sohu.com/cityjson?ie=utf-8'; 2 | const SCRIPT_ID = 'location'; 3 | 4 | function locationBySouhu (commit, state) { 5 | let oldScript = document.getElementById(SCRIPT_ID); 6 | let body = document.getElementsByTagName('body')[0]; 7 | let script = document.createElement('script'); 8 | 9 | if (oldScript) body.removeChild(oldScript); 10 | 11 | script.id = SCRIPT_ID; 12 | script.type = 'text/javascript'; 13 | script.src = SOHU_GETCITY_API + `&t=${Date.now()}`; 14 | body.appendChild(script); 15 | 16 | return new Promise((resolve, reject) => { 17 | script.onload = () => { 18 | if (returnCitySN && returnCitySN.cname && returnCitySN.cname !== '国内未能识别的地区') { 19 | console.log('搜狐定位:' + returnCitySN.cname) 20 | let city = returnCitySN.cname.match(/.+?(省|市|自治区|自治州|县|区)/g); 21 | let index = city.findIndex(val => val.indexOf('市') !== -1); // 找到第一个市 22 | resolve(city[index]); 23 | } 24 | } 25 | }); 26 | } 27 | 28 | function locationByBaidu (commit, state) { 29 | let city = new BMap.LocalCity(); 30 | return new Promise((resolve, reject) => { 31 | city.get(res => { 32 | console.log('百度定位:' + res.name) 33 | resolve(res.name); 34 | }) 35 | }); 36 | } 37 | 38 | 39 | const location = { 40 | state: { 41 | longitude: '', 42 | latitude: '', 43 | myCity: '定位中...', 44 | flag: true 45 | }, 46 | 47 | mutations: { 48 | SET_CITY: (state, city) => { 49 | state.myCity = city || '广州'; 50 | }, 51 | 52 | SET_FLAG (state) { 53 | state.flag = !state.flag; 54 | } 55 | }, 56 | 57 | actions: { 58 | async GetCity({ commit, state }) { 59 | 60 | // 轮流使用搜狐或百度定位 61 | let city = state.flag ? await locationBySouhu() : await locationByBaidu(); 62 | console.log(city) 63 | if (/^.+市$/.test(city)) { 64 | city = city.slice(0, -1) 65 | } 66 | commit('SET_CITY', city); 67 | commit('SET_FLAG'); 68 | return city; 69 | }, 70 | } 71 | } 72 | 73 | export default location; -------------------------------------------------------------------------------- /src/directive/loading/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 这个指令操作dom,性能不好,已改用 my-loadingu 组件。 3 | */ 4 | 5 | import { getDistance, cssUtils } from '@/utils/dom'; 6 | import loadingGif from './loading.gif'; 7 | 8 | const LOADING_WRAP_ID = 'loadingWrap'; 9 | const LOADING_WRAP_CLASS = 'loading-wrap'; 10 | const LOADING_ID = 'loadingGif' 11 | let loadingCount = 1; 12 | 13 | function createLoading(left, top, width, height,) { 14 | let mask = document.createElement('div'); 15 | let loading = document.createElement('img'); 16 | 17 | mask.id = LOADING_WRAP_ID + loadingCount; 18 | mask.className = LOADING_WRAP_CLASS; 19 | loadingCount++; 20 | cssUtils.setCss(mask, { 21 | 'zIndex': '10000', 22 | 'display': 'flex', 23 | 'justify-content': 'center', 24 | 'align-items': 'center', 25 | 'position': 'fixed', 26 | 'left': left + 'px', 27 | 'top': top + 'px', 28 | 'width': width + 'px', 29 | 'height': height + 'px', 30 | 'background': 'rgba(173, 173, 173, .8)', 31 | 'text-align': 'center' 32 | }); 33 | 34 | loading.id = LOADING_ID; 35 | loading.src = loadingGif; 36 | cssUtils.setCss(loading, { 37 | width: '40%', 38 | height: '40%', 39 | 'min-height': '30px' 40 | }); 41 | 42 | mask.appendChild(loading); 43 | return mask; 44 | } 45 | 46 | function toggleLoading (el, binding) { 47 | setTimeout(() => { 48 | if (binding.value) { // 开启 49 | let top = getDistance(el, 'top'); 50 | let left = getDistance(el, 'left'); 51 | let width = el.clientWidth; 52 | let height = el.clientHeight; 53 | let loadingDom = createLoading(left, top, width, height); 54 | document.body.appendChild(loadingDom); 55 | } else { // 关闭 56 | for (let i = 0; i < loadingCount; i++) { 57 | let loadingDom = document.getElementById(LOADING_WRAP_ID + i); 58 | if (loadingDom) document.body.removeChild(loadingDom); 59 | } 60 | loadingCount = 1; 61 | } 62 | }, 0); 63 | } 64 | 65 | export default { 66 | bind(el, binding) { 67 | toggleLoading(el, binding); 68 | }, 69 | 70 | update (el, binding) { 71 | toggleLoading(el, binding); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/components/vertical-list/index.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 50 | 51 | 95 | -------------------------------------------------------------------------------- /src/views/movie-detail/components/MovieInfo.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 53 | 54 | 94 | -------------------------------------------------------------------------------- /src/components/song-list/index.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 40 | 41 | 101 | -------------------------------------------------------------------------------- /src/views/user/index.vue: -------------------------------------------------------------------------------- 1 | 31 | 32 | 58 | 59 | 107 | -------------------------------------------------------------------------------- /src/views/welcome/index.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 68 | 69 | 111 | -------------------------------------------------------------------------------- /src/components/progress/index.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 67 | 68 | 109 | -------------------------------------------------------------------------------- /src/components/my-ui/packages/tabs/src/main.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 92 | 93 | 127 | 128 | -------------------------------------------------------------------------------- /src/views/movie-detail/components/MovieComment.vue: -------------------------------------------------------------------------------- 1 | 33 | 34 | 49 | 50 | 117 | -------------------------------------------------------------------------------- /src/views/movie-detail/components/Operate.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 115 | 116 | 138 | -------------------------------------------------------------------------------- /src/views/song-list-detail/index.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 96 | 97 | 159 | 160 | -------------------------------------------------------------------------------- /src/views/music/index.vue: -------------------------------------------------------------------------------- 1 | 42 | 43 | 107 | 108 | 184 | -------------------------------------------------------------------------------- /src/views/movie-detail/components/CastDetail.vue: -------------------------------------------------------------------------------- 1 | 47 | 48 | 130 | 131 | 214 | -------------------------------------------------------------------------------- /src/views/movie-detail/index.vue: -------------------------------------------------------------------------------- 1 | 41 | 42 | 143 | 144 | 222 | -------------------------------------------------------------------------------- /src/views/search/index.vue: -------------------------------------------------------------------------------- 1 | 52 | 53 | 147 | 148 | 231 | -------------------------------------------------------------------------------- /src/components/my-ui/packages/loading/src/main.vue: -------------------------------------------------------------------------------- 1 | 31 | 32 | 114 | 115 | 273 | 274 | 279 | -------------------------------------------------------------------------------- /src/views/movie-list/index.vue: -------------------------------------------------------------------------------- 1 | 58 | 59 | 179 | 180 | 243 | -------------------------------------------------------------------------------- /src/components/my-ui/packages/swipe/src/main.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 255 | 256 | 287 | 288 | -------------------------------------------------------------------------------- /src/views/music-player/index.vue: -------------------------------------------------------------------------------- 1 | 93 | 94 | 294 | 295 | 492 | --------------------------------------------------------------------------------