1.项目结构
2.目录和代码分析
- build的文件夹:主要放置webpack的打包文件的,webpack自动生成不用管。
- config目录,用于配置请求地址前缀的。
peod.env.js:
'use strict'
module.exports = {
NODE_ENV: '"production"',
BASE_API: '"https://easy-mock.com/mock/5950a2419adc231f356a6636/vue-admin"',
}
dev.env.js:
'use strict'
const merge = require('webpack-merge')
const prodEnv = require('./prod.env')
module.exports = merge(prodEnv, {
NODE_ENV: '"development"',
// BASE_API: '"https://easy-mock.com/mock/5950a2419adc231f356a6636/vue-admin"'
BASE_API: '"http://localhost:9000"',
OSS_PATH: '"https://grain-online-education.oss-cn-shenzhen.aliyuncs.com"'
})
- src目录主要放置js,css,.vue页面文件。
import request from '@/utils/request'
const api_name = '/chapter'
export default {
getChapterAndVideoByCourseId(courseId) {
return request({
url: `${api_name}/${courseId}`,
method: 'get'
})
},
save(chapter) {
return request({
url: `${api_name}/save`,
method: 'post',
data: chapter
})
},
getChapterById(id) {
return request({
url: `${api_name}/get/${id}`,
method: 'get'
})
},
updateById(chapter) {
return request({
url: `${api_name}/update`,
method: 'put',
data: chapter
})
},
removeById(id) {
return request({
url: `${api_name}/${id}`,
method: 'delete'
})
}
}
<template>
<el-breadcrumb class="app-breadcrumb" separator="/">
<transition-group name="breadcrumb">
<el-breadcrumb-item v-for="(item,index) in levelList" v-if="item.meta.title" :key="item.path">
<span v-if="item.redirect==='noredirect'||index==levelList.length-1" class="no-redirect">{{ item.meta.title }}</span>
<a v-else @click.prevent="handleLink(item)">{{ item.meta.title }}</a>
</el-breadcrumb-item>
</transition-group>
</el-breadcrumb>
</template>
<script>
import pathToRegexp from 'path-to-regexp'
export default {
data() {
return {
levelList: null
}
},
watch: {
$route() {
this.getBreadcrumb()
}
},
created() {
this.getBreadcrumb()
},
methods: {
getBreadcrumb() {
let matched = this.$route.matched.filter(item => {
if (item.name) {
return true
}
})
const first = matched[0]
if (first && first.name !== 'dashboard') {
matched = [{ path: '/dashboard', meta: { title: '首页' }}].concat(matched)
}
this.levelList = matched
},
pathCompile(path) {
// To solve this problem https://github.com/PanJiaChen/vue-element-admin/issues/561
const { params } = this.$route
var toPath = pathToRegexp.compile(path)
return toPath(params)
},
handleLink(item) {
const { redirect, path } = item
if (redirect) {
this.$router.push(redirect)
return
}
this.$router.push(this.pathCompile(path))
}
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
.app-breadcrumb.el-breadcrumb {
display: inline-block;
font-size: 14px;
line-height: 50px;
margin-left: 10px;
.no-redirect {
color: #97a8be;
cursor: text;
}
}
</style>
放图标文件夹。
import Vue from 'vue'
import Router from 'vue-router'
// in development-env not use lazy-loading, because lazy-loading too many pages will cause webpack hot update too slow. so only in production use lazy-loading;
// detail: https://panjiachen.github.io/vue-element-admin-site/#/lazy-loading
Vue.use(Router)
/* Layout */
import Layout from '../views/layout/Layout'
/**
* hidden: true if `hidden:true` will not show in the sidebar(default is false)
* alwaysShow: true if set true, will always show the root menu, whatever its child routes length
* if not set alwaysShow, only more than one route under the children
* it will becomes nested mode, otherwise not show the root menu
* redirect: noredirect if `redirect:noredirect` will no redirect in the breadcrumb
* name:'router-name' the name is used by <keep-alive> (must set!!!)
* meta : {
title: 'title' the name show in submenu and breadcrumb (recommend set)
icon: 'svg-name' the icon show in the sidebar,
}
**/
export const constantRouterMap = [
{ path: '/login', component: () => import('@/views/login/index'), hidden: true },
{ path: '/404', component: () => import('@/views/404'), hidden: true },
// 首页
{
path: '/',
component: Layout,
redirect: '/dashboard',
name: 'Dashboard',
children: [{
path: 'dashboard',
component: () => import('@/views/dashboard/index'),
meta: { title: '谷粒学院后台首页', icon: 'dashboard' }
}]
},
// 讲师管理
{
path: '/teacher',
component: Layout,
redirect: '/teacher/list',
name: '讲师管理',
meta: { title: '讲师管理', icon: 'users' },
children: [
{
path: 'list',
name: '讲师列表',
component: () => import('@/views/teacher/list'),
meta: { title: '讲师列表', icon: 'list' }
},
{
path: 'add',
name: '讲师新增',
component: () => import('@/views/teacher/form'),
meta: { title: '讲师新增', icon: 'addUser' }
},
{
path: 'edit/:id', // :用来传递参数
name: '讲师修改',
component: () => import('@/views/teacher/form'),
meta: { title: '讲师修改', icon: 'tree' },
hidden: true
}
]
},
// 课程分类管理
{
path: '/subject',
component: Layout,
redirect: '/subject/list',
name: '课程分类管理',
meta: { title: '课程分类管理', icon: 'classify_select' },
children: [
{
path: 'list',
name: '课程分类列表',
component: () => import('@/views/subject/list'),
meta: { title: '课程分类列表', icon: 'list' }
},
{
path: 'import',
name: '课程分类导入',
component: () => import('@/views/subject/import'),
meta: { title: '课程分类导入', icon: 'import' }
}
]
},
// 课程管理
{
path: '/course',
component: Layout,
redirect: '/course/list',
name: '课程管理',
meta: { title: '课程管理', icon: 'manage' },
children: [
{
path: 'list',
name: '课程列表',
component: () => import('@/views/course/list'),
meta: { title: '课程列表', icon: 'list' }
},
{
path: 'info',
name: '发布课程',
component: () => import('@/views/course/info'),
meta: { title: '发布课程', icon: 'publish' }
},
{
path: 'info/:id',
name: 'EduCourseInfoEdit',
component: () => import('@/views/course/info'),
meta: { title: '编辑课程基本信息', noCache: true },
hidden: true
},
{
path: 'chapter/:id',
name: 'EduCourseChapterEdit',
component: () => import('@/views/course/chapter'),
meta: { title: '编辑课程大纲', noCache: true },
hidden: true
},
{
path: 'publish/:id',
name: 'EduCoursePublishEdit',
component: () => import('@/views/course/publish'),
meta: { title: '发布课程', noCache: true },
hidden: true
}
]
},
// 统计分析
{
path: '/statistics',
component: Layout,
redirect: '/statistics/chart',
name: '统计分析',
meta: { title: '统计分析', icon: 'chart' },
children: [
{
path: 'create',
name: '生成数据',
component: () => import('@/views/statistics/create'),
meta: { title: '生成数据', icon: 'data' }
},
{
path: 'chart',
name: '图表显示',
component: () => import('@/views/statistics/chart'),
meta: { title: '图表显示', icon: 'report' }
}
]
},
{ path: '*', redirect: '/404', hidden: true }
]
export default new Router({
// mode: 'history', //后端支持可开
scrollBehavior: () => ({ y: 0 }),
routes: constantRouterMap
})
import { login, logout, getInfo } from '@/api/login'
import { getToken, setToken, removeToken } from '@/utils/auth'
const user = {
state: {
token: getToken(),
name: '',
avatar: '',
roles: []
},
mutations: {
SET_TOKEN: (state, token) => {
state.token = token
},
SET_NAME: (state, name) => {
state.name = name
},
SET_AVATAR: (state, avatar) => {
state.avatar = avatar
},
SET_ROLES: (state, roles) => {
state.roles = roles
}
},
actions: {
// 登录
Login({ commit }, userInfo) {
const username = userInfo.username.trim()
return new Promise((resolve, reject) => {
login(username, userInfo.password).then(response => {
const data = response.data
setToken(data.token)
commit('SET_TOKEN', data.token)
resolve()
}).catch(error => {
reject(error)
})
})
},
// 获取用户信息
GetInfo({ commit, state }) {
return new Promise((resolve, reject) => {
getInfo(state.token).then(response => {
const data = response.data
if (data.roles && data.roles.length > 0) { // 验证返回的roles是否是一个非空数组
commit('SET_ROLES', data.roles)
} else {
reject('getInfo: roles must be a non-null array !')
}
commit('SET_NAME', data.name)
commit('SET_AVATAR', data.avatar)
resolve(response)
}).catch(error => {
reject(error)
})
})
},
// 登出
LogOut({ commit, state }) {
return new Promise((resolve, reject) => {
logout(state.token).then(() => {
commit('SET_TOKEN', '')
commit('SET_ROLES', [])
removeToken()
resolve()
}).catch(error => {
reject(error)
})
})
},
// 前端 登出
FedLogOut({ commit }) {
return new Promise(resolve => {
commit('SET_TOKEN', '')
removeToken()
resolve()
})
}
}
}
export default user
其实就是触发事件,当state为某个值时,先在mutations中赋值,然后最后执行下面的commit事件。