1.VueRouter 配置
创建 router 的 js 文件
const frameIn = [
{
path: '/head1',
redirect: { name: 'head1-1' },
component: layoutHeaderAside,
children: [
// 首页
{
path: 'page1-1',
name: 'head1-1',
meta: {
auth: true,
title: '第一页'
},
component: _import('demo/page1')
}
]
},
// 重新组织后导出
export default [...frameIn, ...frameOut, ...errorPage]
实例化 VueRouter 对象
import Vue from 'vue'
import VueRouter from 'vue-router'
// 路由数据
import routes from './routes'
// 导出路由 在 main.js 里使用
const router = new VueRouter({
routes
})
在 main.js 中注册 router
import router from './router'
new Vue({
router,
store,
i18n,
render: (h) => h(App),
封装路由守卫
router.beforeEach(async (to, from, next) => {
// 确认已经加载多标签页数据 https://github.com/d2-projects/d2-admin/issues/201
await store.dispatch('d2admin/page/isLoaded')
// 确认已经加载组件尺寸设置 https://github.com/d2-projects/d2-admin/issues/198
await store.dispatch('d2admin/size/isLoaded')
// 进度条
NProgress.start()
// 关闭搜索面板
store.commit('d2admin/search/set', false)
// 验证当前路由所有的匹配中是否需要有登录验证的
if (to.matched.some((r) => r.meta.auth)) {
// 这里暂时将cookie里是否存有token作为验证是否登录的条件
// 请根据自身业务需要修改
const token = util.cookies.get('token')
if (token && token !== 'undefined') {
next()
} else {
// 没有登录的时候跳转到登录界面
// 携带上登陆成功之后需要跳转的页面完整路径
next()
/* next({
name: 'login',
query: {
redirect: to.fullPath
}
}) */
// https://github.com/d2-projects/d2-admin/issues/138
NProgress.done()
}
} else {
// 不需要身份校验 直接通过
next()
}
})
router.afterEach((to) => {
// 进度条
NProgress.done()
// 多页控制 打开新的页面
store.dispatch('d2admin/page/open', to)
// 更改标题
util.title(to.meta.title)
})
2.侧边栏配置
menu.js 中配置菜单的 json 数据
export const menuAside = supplementPath([
{ path: '/index', title: '首页', icon: 'home' },
{
title: '/head1',
icon: 'folder-o',
children: [
{ path: '/page1', title: '页面 1-1' },
{ path: '/page2', title: '页面 1-2' },
{ path: '/page3', title: '页面 1-3' }
]
},
监听路由变化动态生成侧边栏
watch: {
'$route.matched': {
handler(val) {
const menuAsideTem = menuAside.find((item) => {
return item.title === val[0].path
})
let aide = []
if (menuAsideTem && menuAsideTem.children) {
aide = menuAsideTem.children
}
this.$store.commit('d2admin/menu/asideSet', aide)
}
}
}
效果如下
菜单顶栏也是相同的原理
3.vuex 统一状态管理
state => 基本数据
// 设置文件
import setting from '@/setting.js'
export default {
namespaced: true,
state: {
// 顶栏菜单
header: [],
// 侧栏菜单
aside: [],
官方推荐用计算属性访问属性, 用mapStatus 辅助函数进一步简化写法,因为采用了 module,所以会带上路径 'd2admin/menu',同时需要对 namespace 的支持
import { mapState } from 'vuex'
computed: {
...mapState('d2admin/menu', [
'aside',
'asideCollapse',
'asideTransition'
])
},
// 等效于:
computed: {
aside() {
return this.$store.state('d2admin/menu', aside)
},
// 'd2admin/menu' 是因为使用了 modules,需要对 namespace 的支持
export default {
namespaced: true,
modules
}
// 在其它地方使用 state 中的数据
this.aside.map(menu => createMenu.call(this, h, menu))
mutations => 提交同步修改
mutations: {
asideSet (state, menu) {
// store 赋值
state.aside = menu
}
}
// 在别处提交修改
this.$store.commit('d2admin/menu/asideSet', menuAsideTem.children)
modules => 统一管理
const moduleA = {
state: { ... },
mutations: { ... },
actions: { ... },
getters: { ... }
}
const moduleB = {
state: { ... },
mutations: { ... },
actions: { ... }
}
const store = new Vuex.Store({
modules: {
a: moduleA,
b: moduleB
}
})
get => 对数据做加工处理
const getters = {
getToDo (state) {
return state.todos.filter(item => item.done === true)
// filter 迭代过滤器 将每个item的值 item.done == true 挑出来, 返回的是一个数组
}
}
...mapGetters({
todosALise: 'getToDo' // getToDo 不是字符串,对应的是getter里面的一个方法名字
//取一个别名 todosALise
})
新建并导出 vuex 对象,方便在全局注册
import Vue from 'vue'
import Vuex from 'vuex'
// 导入 vuex 的 json 文件
import d2admin from './modules/d2admin'
// 注册 vuex
Vue.use(Vuex)
// 新建 vuex 对象 引入 d2admin
export default new Vuex.Store({
modules: {
d2admin
}
})
4.axios
请求封装
function createService () {
// 创建一个 axios 实例
const service = axios.create()
// 请求拦截
service.interceptors.request.use(
(config) => config,
(error) => {
// 发送失败
console.log(error)
return Promise.reject(error)
}
)
// 响应拦截
service.interceptors.response.use(
(response) => {
// dataAxios 是 axios 返回数据中的 data
const dataAxios = response.data
// 这个状态码是和后端约定的
const { code } = dataAxios
// 根据 code 进行判断
if (code === undefined) {
// 如果没有 code 代表这不是项目后端开发的接口 比如可能是 D2Admin 请求最新版本
return dataAxios
} else {
// 有 code 代表这是一个后端接口 可以进行进一步的判断
switch (code) {
case 0:
// [ 示例 ] code === 0 代表没有错误
return dataAxios.data
case 'xxx':
// [ 示例 ] 其它和后台约定的 code
errorCreate(`[ code: xxx ] ${dataAxios.msg}: ${response.config.url}`)
break
default:
// 不是正确的 code
errorCreate(`${dataAxios.msg}: ${response.config.url}`)
break
}
}
},
(error) => {
const status = get(error, 'response.status')
/* switch (status) {
case 400: error.message = '请求错误'; break
case 401: error.message = '未授权,请登录'; break
case 403: error.message = '拒绝访问'; break
case 404: error.message = `请求地址出错: ${error.response.config.url}`; break
case 408: error.message = '请求超时'; break
case 500: error.message = '服务器内部错误'; break
case 501: error.message = '服务未实现'; break
case 502: error.message = '网关错误'; break
case 503: error.message = '服务不可用'; break
case 504: error.message = '网关超时'; break
case 505: error.message = 'HTTP版本不受支持'; break
default: break
} */
errorLog(error)
return Promise.reject(error)
}
)
return service
}
function createRequestFunction (service) {
return function (config) {
// cookies 中获取 token
const token = util.cookies.get('token')
const configDefault = {
headers: {
Authorization: token,
'Content-Type': get(config, 'headers.Content-Type', 'application/json')
},
timeout: 3000,
baseURL: process.env.VUE_APP_API,
data: {}
}
return service(Object.assign(configDefault, config))
}
}
5.跨域问题
proxy: {
'/api': {
target: 'http://192.168.8.11:8080',
ws: true,
changeOrigin: true,
// 重写的代码
pathRewrite: {
'^/api/*': ''
}
}
}
6.scss 语法的使用
$ 符号用于声明可被引用的变量
$red: red;
$g: green;
body {
p {
color: $red;
&:hover {
color: $g;
}
}
}
% 用于声明可被继承的变量
// 全局设置一个水平垂直居中的样式
%flex-center-row {
display: flex;
justify-content: center;
align-items: center;
flex-direction: row;
}
// 在需要的地方继承它
.contain {
width: 100%;
height: 100%;
padding: 15px;
box-sizing: border-box;
@extend %flex-center-row;
}
7. 基于 el-table 进行二次封装
封装 el-table, 通过传 JSON 的方法去维护表格;
<template>
<div>
<el-table :data="tableData">
<template v-for="column in columns">
<el-table-column :prop="column.prop" :label="column.label" v-if="!column.slotName" :key="column.label">
</el-table-column>
<el-table-column :label="column.label" v-if="column.slotName" :key="column.label">
// 作用域插槽, 拿到行上数据
<template v-slot="slotProps">
// 具名插槽, 用于回显自定义的内容
<slot :name="column.slotName" :scope="slotProps"></slot>
</template>
</el-table-column>
</template>
</el-table>
</div>
</template>
<script>
export default {
props: {
columns: {
type: Array
},
tableData: {
type: Array
}
},
data() {
return {}
}
}
</script>
在页面中使用
<template>
<div class="main">
<h1>MyTable</h1>
<MyTable :columns="columns" :tableData="tableData">
<template v-slot:type="slotProps">
<!-- 自定义模板 -->
<span v-if="slotProps.scope.row.type === 1">
<el-tag>singer</el-tag>
</span>
<span v-else>
<el-tag>player</el-tag>
</span>
<!-- 自定义格式化函数 -->
{{formatterType(slotProps.scope.row)}}
</template>
<template v-slot:operate="slotProps">
<el-button @click="handleClick(slotProps.scope.row.date)">编辑</el-button>
</template>
</MyTable>
</div>
</template>
<script>
import MyTable from '@/components/my-table'
import { columns, tableData } from './index'
export default {
components: { MyTable },
data() {
return {
columns,
tableData
}
},
methods: {
handleClick(msg) {
this.$message.success(msg)
},
formatterType(row) {
if (row.type === 1) {
return '歌手'
} else {
return '玩家'
}
}
}
}
</script>
<style scoped>
.main {
margin-top: 20px;
padding: 30px;
box-sizing: border-box;
background: #fff;
}
</style>
// index.js
export const columns = [
{ prop: 'date', label: '日期' },
{ prop: 'name', label: '姓名' },
{ label: '类型', slotName: 'type', prop: 'type' },
{ prop: 'address', label: '地址' },
{ label: '操作', slotName: 'operate' }
]
export const tableData = [
{
date: '2016-05-02',
name: '王小虎',
address: '上海市普陀区金沙江路 1518 弄'
},
{
date: '2016-05-04',
name: '王er虎',
address: '上海市普陀区金沙江路 1517 弄'
},
{
date: '2016-05-01',
name: '王小虎bb',
address: '上海市普陀区金沙江路 1519 弄',
type: 1
},
{
date: '2016-05-03',
name: 'zz',
address: '上海市普陀区金沙江路 1516 弄'
}
]
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。