上下固定,中間滾動布局(FLEX)
<div id="app">
<div class="header"></div>
<div class="views"></div>
<div class="footer"></div>
</div>
<style>
#app{display: flex;flex-direction: column;height: 100%;}
.views{flex: 1; overflow-y: scroll;-webkit-overflow-scrolling: touch;} /*-webkit-overflow-scrolling: touch; 解決蘋果手機下網頁滑動不順暢問題*/
.header{} /*高度隨意設置*/
.footer{} /*高度隨意設置*/
</style>
Vue插件封裝(loading實例)
src/omponents/loading/Loading.vue
<template>
<div class="loading" v-show="show">
<i class="i-loading"></i>
</div>
</template>
<script>
export default {
props: {
show: Boolean
}
}
</script>
<style lang="scss" scoped>
.loading{
width: 200px;
height: 200px;
position: fixed;
left: 0;
right: 0;
bottom: 0;
top: 0;
margin: auto;
border-radius: 6px;
background: rgba(0,0,0,0.6);
display: flex;
justify-content: center;
align-items: center;
z-index: 999;
}
.i-loading {
width: 90px;
height: 90px;
display: inline-block;
vertical-align: middle;
-webkit-animation: loading 1s steps(12, end) infinite;
animation: loading 1s steps(12, end) infinite;
background: transparent url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAiIGhlaWdodD0iMTIwIiB2aWV3Qm94PSIwIDAgMTAwIDEwMCI+PHBhdGggZmlsbD0ibm9uZSIgZD0iTTAgMGgxMDB2MTAwSDB6Ii8+PHJlY3Qgd2lkdGg9IjciIGhlaWdodD0iMjAiIHg9IjQ2LjUiIHk9IjQwIiBmaWxsPSIjRTlFOUU5IiByeD0iNSIgcnk9IjUiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDAgLTMwKSIvPjxyZWN0IHdpZHRoPSI3IiBoZWlnaHQ9IjIwIiB4PSI0Ni41IiB5PSI0MCIgZmlsbD0iIzk4OTY5NyIgcng9IjUiIHJ5PSI1IiB0cmFuc2Zvcm09InJvdGF0ZSgzMCAxMDUuOTggNjUpIi8+PHJlY3Qgd2lkdGg9IjciIGhlaWdodD0iMjAiIHg9IjQ2LjUiIHk9IjQwIiBmaWxsPSIjOUI5OTlBIiByeD0iNSIgcnk9IjUiIHRyYW5zZm9ybT0icm90YXRlKDYwIDc1Ljk4IDY1KSIvPjxyZWN0IHdpZHRoPSI3IiBoZWlnaHQ9IjIwIiB4PSI0Ni41IiB5PSI0MCIgZmlsbD0iI0EzQTFBMiIgcng9IjUiIHJ5PSI1IiB0cmFuc2Zvcm09InJvdGF0ZSg5MCA2NSA2NSkiLz48cmVjdCB3aWR0aD0iNyIgaGVpZ2h0PSIyMCIgeD0iNDYuNSIgeT0iNDAiIGZpbGw9IiNBQkE5QUEiIHJ4PSI1IiByeT0iNSIgdHJhbnNmb3JtPSJyb3RhdGUoMTIwIDU4LjY2IDY1KSIvPjxyZWN0IHdpZHRoPSI3IiBoZWlnaHQ9IjIwIiB4PSI0Ni41IiB5PSI0MCIgZmlsbD0iI0IyQjJCMiIgcng9IjUiIHJ5PSI1IiB0cmFuc2Zvcm09InJvdGF0ZSgxNTAgNTQuMDIgNjUpIi8+PHJlY3Qgd2lkdGg9IjciIGhlaWdodD0iMjAiIHg9IjQ2LjUiIHk9IjQwIiBmaWxsPSIjQkFCOEI5IiByeD0iNSIgcnk9IjUiIHRyYW5zZm9ybT0icm90YXRlKDE4MCA1MCA2NSkiLz48cmVjdCB3aWR0aD0iNyIgaGVpZ2h0PSIyMCIgeD0iNDYuNSIgeT0iNDAiIGZpbGw9IiNDMkMwQzEiIHJ4PSI1IiByeT0iNSIgdHJhbnNmb3JtPSJyb3RhdGUoLTE1MCA0NS45OCA2NSkiLz48cmVjdCB3aWR0aD0iNyIgaGVpZ2h0PSIyMCIgeD0iNDYuNSIgeT0iNDAiIGZpbGw9IiNDQkNCQ0IiIHJ4PSI1IiByeT0iNSIgdHJhbnNmb3JtPSJyb3RhdGUoLTEyMCA0MS4zNCA2NSkiLz48cmVjdCB3aWR0aD0iNyIgaGVpZ2h0PSIyMCIgeD0iNDYuNSIgeT0iNDAiIGZpbGw9IiNEMkQyRDIiIHJ4PSI1IiByeT0iNSIgdHJhbnNmb3JtPSJyb3RhdGUoLTkwIDM1IDY1KSIvPjxyZWN0IHdpZHRoPSI3IiBoZWlnaHQ9IjIwIiB4PSI0Ni41IiB5PSI0MCIgZmlsbD0iI0RBREFEQSIgcng9IjUiIHJ5PSI1IiB0cmFuc2Zvcm09InJvdGF0ZSgtNjAgMjQuMDIgNjUpIi8+PHJlY3Qgd2lkdGg9IjciIGhlaWdodD0iMjAiIHg9IjQ2LjUiIHk9IjQwIiBmaWxsPSIjRTJFMkUyIiByeD0iNSIgcnk9IjUiIHRyYW5zZm9ybT0icm90YXRlKC0zMCAtNS45OCA2NSkiLz48L3N2Zz4=) no-repeat;
background-size: 100%;
}
@keyframes loading {
0% {
-webkit-transform: rotate3d(0, 0, 1, 0deg);
transform: rotate3d(0, 0, 1, 0deg);
}
100% {
-webkit-transform: rotate3d(0, 0, 1, 360deg);
transform: rotate3d(0, 0, 1, 360deg);
}
}
</style>
src/omponents/loading/index.js
<script>
import LoadingComponent from './loading'
let $vm
export default {
install (Vue, options) {
if (!$vm) {
const LoadingPlugin = Vue.extend(LoadingComponent)
$vm = new LoadingPlugin({
el: document.createElement('div')
})
console.log($vm)
}
$vm.show = false
let loading = {
show (text) {
$vm.show = true
$vm.text = text
document.body.appendChild($vm.$el)
},
hide () {
$vm.show = false
}
}
if (!Vue.$loading) {
Vue.$loading = loading
}
Vue.mixin({
created () {
this.$loading = Vue.$loading
}
})
}
}
//使用
import Loading from '@/components/loading/index.js' //loading 插件
Vue.use(Loading) //使用loading插件
Vue.$loading.show() //顯示
Vue.$loading.hide() //隱藏
</script>
axios全局路由攔截及結合promise對axios請求進行處理
src/utils/request.js
import axios from 'axios'
import {Notification, MessageBox} from 'element-ui'
import store from '@/store'
import {getToken} from '@/utils/auth'
axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8'
// 創建axios實例
const service = axios.create({
// axios中請求配置有baseURL選項,表示請求URL公共部分
baseURL: process.env.VUE_APP_BASE_API,
// 超時
timeout: 10000
})
// request攔截器
service.interceptors.request.use(
config => {
if (getToken()) {
config.headers['Authorization'] = 'Bearer ' + getToken() // 讓每個請求攜帶自定義token 請根據實際情況自行修改
}
return config
},
error => {
Promise.reject(error)
}
)
// 響應攔截器
service.interceptors.response.use(res => {
const code = res.data.code
if (code === 401) {
MessageBox.confirm(
'登錄狀態已過期,您可以繼續留在該頁面,或者重新登錄',
'系統提示',
{
confirmButtonText: '重新登錄',
cancelButtonText: '取消',
type: 'warning'
}
).then(() => {
store.dispatch('LogOut').then(() => {
location.reload() // 為了重新實例化vue-router對象 避免bug
})
})
} else if (code !== 200) {
Notification.error({
title: res.data.msg
})
return Promise.reject('error')
} else {
return res.data
}
},
error => {
Message({
message: error.message,
type: 'error',
duration: 5 * 1000
})
return Promise.reject(error)
}
)
export default service
src/api.js
import request from '@/utils/request'
// AXIOS GET請求
// get
export function listConfig(query) {
return request({
url: '/system/config/list',
method: 'get',
params: query
})
}
// post
export function addConfig(data) {
return request({
url: '/system/config',
method: 'post',
data: data
})
}
路由的其他一些配置
router.beforeEach((to, from, next) => {
// 動態更改頁面title
if (to.meta.title) {
document.title = to.meta.title
}
// 驗證是否需要登陸
if (to.meta.requireAuth && JSON.stringify(store.state.user) === '{}') {
next({ name: 'signIn' })
}
// 如果登錄狀態進入登錄頁面則,返回到個人中心頁面
if (JSON.stringify(store.state.user) !== '{}') {
if (to.name === 'signIn' || to.name === 'resetPassword' || to.name === 'mobileLogin') {
next({ name: 'personal', query: { type: 'records' } })
}
}
next()
})
解決移動端click300ms問題
安裝 npm install fastclick --save 使用 import fastclick from 'fastclick' fastclick.attach(document.body)
Vue父子組件通訊
父向子傳遞參數
Parent.vue(父組件)
<template>
<div>
<Child :name="name"></Child>
</div>
</template>
<script>
import Child from './Child'
export default{
components:{
Child
},
data(){
return{
name:'hello'
}
}
}
</script>
Child.vue(子組件)
<template>
<div>
<!-- 這里的name接收了父組件傳過來的參數,這里會變成hello -->
{{name}}
</div>
</template>
<script>
export default{
props:{
name:String
}
}
</script>
子向父傳遞參數
Child.vue(子組件)
<template>
<div>
<button @click="toParentMsg()">我要向父節點傳遞參數</button>
</div>
</template>
<script>
export default{
method:{
toParentMsg(){
this.$emit('listenToChildEvent','我是要向父組件傳送的數據') //listenToChildEvent 自定義事件,后面需要再父組件接收這個自定義事件
}
}
}
</script>
Parent.vue(父組件)
<template>
<div>
<Child @listenToChildEvent = "receiveChildMsg"></Child>
</div>
</template>
<script>
import Child from './Child'
export default{
components:{
Child
},
data(){
},
method:{
receiveChildMsg(val){
console.log(val) //我是要向父組件傳送的數據
}
}
}
</script>
對函數進行封裝全局使用
src/utils/global.js //公共方法寫在這
exports.install = function (Vue, options) {
/**
* 對小數位進行格式化
* @data 數據
* @num 格式位數
* */
Vue.filter('decimalPlaceFormat', function (data, num) {
if (!(data && num)) return ''
return Number(data).toFixed(num)
});
}
src/main.js //引用安裝
import global from './utils/global'
Vue.use(global)
對指令封裝全局使用
src/directive/hasPermiss.js
/**
* 按鈕權限控制
* */
import store from './../store'
export default {
inserted(el,binding) {
const {value} = binding;
const btnPermiss = store.getters.btnPermiss;
let status = btnPermiss.some(item => item == value);
if(!status){
el.parentNode && el.parentNode.removeChild(el);
}
}
}
src/directive/index.js
import hasPermiss from './hasPermiss'
const install = function(Vue) {
Vue.directive('hasPermiss',hasPermiss)
}
if (window.Vue) {
window['hasPermiss'] = hasPermiss;
Vue.use(install);
}
export default install
src/main.js
import install from './directive'
Vue.use(global);
VUEX模塊使用
Vuex數據操作及數據持久化
使用vuex-persistedstate插件
const store = new Vuex.Store({
state: {
// 用戶信息
user: {},
// 分類頁數據篩選
videoListFilterTerm: {
catId: 0,
courseId: 0,
software: 0,
diff: 0,
sort: 3,
page: 1
}
},
//獲取state數據
getters: {
getUser (state) {
return state.user
}
},
//操作state數據
mutations: {
setUser (state, user) {
state.user = user
},
setVideoListFilterTerm (state, videoListFilterTerm) {
state.videoListFilterTerm = videoListFilterTerm
}
},
//觸發mutations函數
actions: {
setUser ({ commit }, user) {
commit('setUser', user)
},
setVideoListFilterTerm ({ commit }, videoListFilterTerm) {
commit('setVideoListFilterTerm', videoListFilterTerm)
}
},
//插件配置
plugins: [createPersistedState({
storage: window.localStorage,
reducer (val) {
return {
user: val.user
}
}
})]
})
Vue m3u8視頻播放配置
播放m3u8視頻需要用到 videojs-contrib-hls插件 安裝 npm install videojs-contrib-hls --save 導入import 'videojs-contrib-hls'
Vue-router 實現模塊化加載
使用該方式導入組件,打包模塊會自動把組件進行模塊化打包
const xxx = () => import('@/views/xxx')
Vue路由切換增加動畫效果
90sheji-video/src/App.vue
<template>
<div id="app">
<transition name="fade" mode="out-in">
<router-view />
</transition>
</div>
</template>
<script>
export default {
name: 'App'
}
</script>
src/components/layout/Layout.vue
<template>
<div class="layout flex">
<Header/>
<transition name="fade-transform" mode="out-in">
<keep-alive>
<router-view :key="key"/>
</keep-alive>
</transition>
<Footer/>
</div>
</template>
<script>
import Footer from '@/components/common/Footer'
import Header from '@/components/common/Header'
export default {
data () {
return {
catId: ''
}
},
components: {
Footer,
Header
},
computed: {
key () {
return this.$route.name ? this.$route.name : this.$route.fullPath
}
}
}
</script>
<style>
/*********************動畫·start**************************/
/* fade */
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.2s;
}
.fade-enter,
.fade-leave-active {
opacity: 0;
}
/* fade-transform */
.fade-transform-leave-active,
.fade-transform-enter-active {
transition: all 0.2s;
}
.fade-transform-enter {
opacity: 0;
transform: translateX(-20px);
}
.fade-transform-leave-to {
opacity: 0;
transform: translateX(20px);
}
/* breadcrumb transition */
.breadcrumb-enter-active,
.breadcrumb-leave-active {
transition: all 0.2s;
}
.breadcrumb-enter,
.breadcrumb-leave-active {
opacity: 0;
transform: translateX(20px);
}
.breadcrumb-move {
transition: all 0.2s;
}
.breadcrumb-leave-active {
position: absolute;
}
/*********************動畫·end**************************/
</style>
組件切換有兩種情況,一種是兄弟與兄弟組件切換,一種是子組件與父組件之間切換,所以這里轉場動畫用的不一樣,所以貼出了兩個組件用法
<keep-alive> 增加這個標簽表示組件可以被緩存起來,強烈加上
<router-view :key="key"/> 前面用了組件緩存,所以路由視圖的key一定要加上,不然路由和頁面有些會不匹配
vue使用官方腳手架打包上線配置
/config/index.js
build: {
// Template for index.html
index: path.resolve(__dirname, '../dist/index.html'),//入口文件
// Paths
assetsRoot: path.resolve(__dirname, '../dist'),//編譯后所有需要部署的文件都放到了這里
assetsSubDirectory: 'public',//靜態資源存放的文件目錄
assetsPublicPath: './',// ./是相對路徑,/是絕對路徑,這里改為相對路徑,不然打包后上線圖片訪問不了
productionSourceMap: true,//是否開啟SourceMap壓縮
devtool: '#source-map',
productionGzip: false,//是否開啟Gzip壓縮
productionGzipExtensions: ['js', 'css'],//Gzip壓縮
bundleAnalyzerReport: process.env.npm_config_report
}
后端解決跨域配置
resp.setHeader("Access-Control-Allow-Origin", "*");
resp.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
package.json文件中dependencies與devDependencies的區別
dependencies:打包上線需要用到的插件 使用npm inatall --save xxx 安裝插件 devDependencies:開發環境需要用到的插件 使用npm install --save-dev xxx 安裝插件
