前端准备
vue-element-admin
vue-element-admin 是一个后台前端解决方案,它基于 vue
和 element-ui
实现的。它使用了最新的前端技术栈,内置了 i18
国际化解决方案,动态路由
,权限验证
,提炼了典型的业务模型,提供了丰富的功能组件,它可以帮助你快速搭建企业级中后台产品原型。
以上两种方式都可以下载一个是使用 ssh 的方式,一个是下载zip包的形式自己喜欢那种就用那种看个人。
Node安装和配置
前端工具我还是使用的 IDEA,IDEA 基本上可以达到,使用 WebStorm 也可以,还有就是 VSCode。
这里我说一下 VSCode,VSCode 直接百度搜索这几个关键词就行了找到官网下载安装包无脑下一步即可,只不过需要安装一些插件在下面我会把需要装的插件一一截图放在这里的,下载 VSCode 自己根据自己的操作系统版本位数下载,现在基本上都是 64 了。
导入准备好的配置
配置下载地址:配置.zip
下载了之后直接覆盖进去即可,以上是给使用 VSCode 的人看的使用 WebStorm 的自行略过。
可以删除也可以不删除,我是删除了哈没啥用,说明文档后面在自己加也可以
目录说明
├── build # 构建相关,构造文件夹【不管】
├── mock # 项目mock 模拟数据,可以删除
├── plop-templates # 基本模板,可以删除
├── node_modules # 项目下载的的依赖模块【现在还没有,因为还没有下载依赖】
├── public # 静态资源,公共资源
│ │── favicon.ico # favicon图标
│ └── index.html # html模板
├── src # 源代码,我们写代码的地方
│ ├── api # 所有请求,后台请求接口
│ ├── assets # 主题 字体等静态资源,项目资源
│ ├── components # 全局公用组件,小组件
│ ├── directive # 全局指令,自定义指令
│ ├── filters # 全局 filter,过滤器
│ ├── icons # 项目所有 svg icons,图标
│ ├── lang # 国际化 language
│ ├── layout # 全局 layout,布局
│ ├── router # 路由,自定义路由
│ ├── store # 全局 store管理,vuex全局变量
│ ├── styles # 全局样式,自定义样式
│ └── utils # 全局公用方法,工具包
│ ├── auth.js # 登陆认证相关
│ ├── permissions.js # 权限相关
│ ├── request.js # 发请求及请求拦击器相关
│ ├── vendor # 公用vendor,第三方服务
│ ├── views # views 所有页面,大组件
│ ├── App.vue # 入口页面,项目主程序
│ ├── main.js # 入口文件 加载组件 初始化等,项目入口
│ └── permission.js # 权限管理
├── tests # 测试
├── .env.development # 开发环境配置
├── .env.production # 生产环境配置
├── . # 环境变量配置
├── .eslintrc.js # eslint 配置项
├── .babelrc # babel-loader 配置
├── .travis.yml # 自动化CI配置
├── permission.js # 项目路由管理
├── setting.js # 项目整体设置如页的title
├── vue.config.js # vue-cli 配置,主配置[我们后面要修改,例如配置跨域代理什么的]
├── postcss.config.js # postcss 配置
└── package.json # 依赖配置
配置镜像加速
其实我在上面的那篇 Node.js 文章中已经写过了我还是在这里说明一下吧配置了可以下载的更快,没配置网速不好也就是下载的比较慢而已,根据自己的网络情况来吧,我反正不管好还是不好都配置了。
npm config set registry https://
执行 npm install
下载依赖。
npm install
在把项目跑起来之前,我们先来改一点东西,修改路由菜单改为我想要的。
修改 router/index.js
,我这里配置好了。
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
/* Layout */
import Layout from '@/layout'
/**
* constantRoutes 常量路由,这些路由由后台的数据控制是否显示
*/
export const constantRoutes = [
{
path: '/redirect',
component: Layout,
hidden: true,
children: [
{
path: '/redirect/:path*',
component: () => import('@/views/redirect/index')
}
]
},
{
path: '/login',
component: () => import('@/views/login/index'),
hidden: true
},
{
path: '/auth-redirect',
component: () => import('@/views/login/auth-redirect'),
hidden: true
},
{
path: '/404',
component: () => import('@/views/error-page/404'),
hidden: true
},
{
path: '/401',
component: () => import('@/views/error-page/401'),
hidden: true
},
{
path: '/',
component: Layout,
redirect: '/dashboard',
children: [
{
path: 'dashboard',
component: () => import('@/views/dashboard/index'),
name: '首页',
meta: { title: '首页', icon: 'dashboard', affix: true }
}
]
}
]
export const asyncRoutes = [
{
path: '/system',
component: Layout,
redirect: 'noRedirect',
alwaysShow: true,
name: '/system',
meta: {
title: '系统管理',
icon: 'lock'
},
children: [
{
path: 'dept',
component: () => import('@/views/error-page/404'),
name: '/system/dept',
meta: {
title: '科室管理',
icon: 'edit'
}
},
{
path: 'user',
component: () => import('@/views/error-page/404'),
name: '/system/user',
meta: {
title: '用户管理',
icon: 'list'
}
},
{
path: 'role',
component: () => import('@/views/error-page/404'),
name: '/system/role',
meta: {
title: '角色管理',
icon: 'tab'
}
},
{
path: 'menu',
component: () => import('@/views/error-page/404'),
name: '/system/menu',
meta: {
title: '菜单管理',
icon: 'bug'
}
},
{
path: 'dict',
component: () => import('@/views/error-page/404'),
name: '/system/dict',
meta: {
title: '字典管理',
icon: 'zip'
}
},
{
path: 'notice',
component: () => import('@/views/error-page/404'),
name: '/system/notice',
meta: {
title: '通知公告',
icon: 'theme'
}
},
{
path: 'log_login',
component: () => import('@/views/error-page/404'),
name: '/system/log_login',
meta: {
title: '登陆日志管理',
icon: 'clipboard'
}
},
{
path: 'log_opt',
component: () => import('@/views/error-page/404'),
name: '/system/log_opt',
meta: {
title: '操作日志管理',
icon: 'clipboard'
}
},
{
path: 'ins_fee',
component: () => import('@/views/error-page/404'),
name: '/system/ins_fee',
meta: {
title: '检查费用设置',
icon: 'international'
}
},
{
path: 'reg_fee',
component: () => import('@/views/error-page/404'),
name: '/system/reg_fee',
meta: {
title: '挂号费用设置',
icon: 'international'
}
}
]
},
{
path: '/statistics',
component: Layout,
redirect: 'noRedirect',
name: '/statistics',
meta: {
title: '数据统计',
icon: 'example'
},
children: [
{
path: 'revenue',
component: () => import('@/views/error-page/404'),
name: '/statistics/revenue',
meta: { title: '收支统计', icon: 'edit' }
},
{
path: 'sales',
component: () => import('@/views/error-page/404'),
name: '/statistics/sales',
meta: { title: '药品销售统计', icon: 'list' },
hidden: true
},
{
path: 'check',
component: () => import('@/views/error-page/404'),
name: '/statistics/check',
meta: { title: '检查项目统计', icon: 'list' }
},
{
path: 'workload',
component: () => import('@/views/error-page/404'),
name: '/statistics/workload',
meta: { title: '工作量统计', icon: 'list' }
}
]
},
{
path: '/stock',
component: Layout,
redirect: 'noRedirect',
name: '/stock',
meta: {
title: '药品进销存',
icon: '404'
},
children: [
{
path: 'producter',
component: () => import('@/views/error-page/404'),
name: '/stock/producter',
meta: { title: '生产厂家维护', icon: 'list' }
},
{
path: 'medicinal',
component: () => import('@/views/error-page/404'),
name: '/stock/medicinal',
meta: { title: '药品信息维护', icon: 'list' }
},
{
path: 'provider',
component: () => import('@/views/error-page/404'),
name: '/stock/provider',
meta: { title: '供应商维护', icon: 'list' }
},
{
path: 'purchase',
component: () => import('@/views/error-page/404'),
name: '/stock/purchase',
meta: { title: '采购入库列表', icon: 'list' }
},
{
path: 'examine',
component: () => import('@/views/error-page/404'),
name: '/stock/examine',
meta: { title: '入库审核', icon: 'list' }
},
{
path: 'inventory',
component: () => import('@/views/error-page/404'),
name: '/stock/inventory',
meta: { title: '库存查询', icon: 'list' }
}
]
},
{
path: '/charge',
component: Layout,
redirect: 'noRedirect',
name: '/charge',
meta: {
title: '收费管理',
icon: 'excel'
},
children: [
{
path: 'charge',
component: () => import('@/views/error-page/404'),
name: '/charge/docharge',
meta: { title: '处方收费', icon: 'list' }
},
{
path: 'chargelist',
component: () => import('@/views/error-page/404'),
name: '/charge/chargelist',
meta: { title: '收费查询', icon: 'list' }
},
{
path: 'backfee',
component: () => import('@/views/error-page/404'),
name: '/charge/backfee',
meta: { title: '处方退费', icon: 'list' }
},
{
path: 'backfeelist',
component: () => import('@/views/error-page/404'),
name: '/charge/backfeelist',
meta: { title: '退费查询', icon: 'list' }
},
{
path: 'dispensing',
component: () => import('@/views/error-page/404'),
name: '/charge/dispensing',
meta: { title: '处方发药', icon: 'list' }
}
]
},
{
path: '/check',
component: Layout,
redirect: 'noRedirect',
name: '/check',
meta: {
title: '检查管理',
icon: 'excel'
},
children: [
{
path: 'docheck',
component: () => import('@/views/error-page/404'),
name: '/check/docheck',
meta: { title: '新开检查', icon: 'list' }
},
{
path: 'checkresult',
component: () => import('@/views/error-page/404'),
name: '/check/checkresult',
meta: { title: '检查结果录入', icon: 'list' }
},
{
path: 'checklist',
component: () => import('@/views/error-page/404'),
name: '/check/checklist',
meta: { title: '检查结果查询', icon: 'list' }
}
]
},
{
path: '/doctor',
component: Layout,
redirect: 'noRedirect',
name: '/doctor',
meta: {
title: '看病就诊',
icon: 'excel'
},
children: [
{
path: 'registered',
component: () => import('@/views/error-page/404'),
name: '/doctor/registered',
meta: { title: '门诊挂号', icon: 'list' }
},
{
path: 'registeredlist',
component: () => import('@/views/error-page/404'),
name: '/doctor/registeredlist',
meta: { title: '挂号列表', icon: 'list' }
},
{
path: 'newcare',
component: () => import('@/views/error-page/404'),
name: '/doctor/newcare',
meta: { title: '新开就诊', icon: 'list' }
},
{
path: 'myscheduling',
component: () => import('@/views/error-page/404'),
name: '/doctor/myscheduling',
meta: { title: '我的排班', icon: 'list' }
},
{
path: 'scheduling',
component: () => import('@/views/error-page/404'),
name: '/doctor/scheduling',
meta: { title: '医生排班', icon: 'list' }
},
{
path: 'patient',
component: () => import('@/views/error-page/404'),
name: '/doctor/patient',
meta: { title: '患者库', icon: 'list' }
}
]
}
]
export const lastRoute = [
{
path: '/dict',
component: Layout,
hidden: true,
children: [
{
path: 'type/data/:dictId(\\d+)',
component: () => import('@/views/error-page/404'),
name: '/dict',
meta: { title: '数据字典' }
}
]
},
{
path: '/stock/purchase',
component: Layout,
hidden: true,
children: [
{
path: 'insert',
component: () => import('@/views/error-page/404'),
name: '/stock/purchase/insert',
meta: { title: '采购入库', icon: 'list' }
},
{
path: 'update/:purchaseId',
component: () => import('@/views/error-page/404'),
name: '/stock/purchase/update',
meta: { title: '采购入库修改', icon: 'list' }
}
]
},
// 404 page must be placed at the end !!!
{ path: '*', redirect: '/404', hidden: true }
]
const createRouter = () => new Router({
// mode: 'history', // require service support
scrollBehavior: () => ({ y: 0 }),
// 初始化时将所有路由都加载上,否则会出现刷新页面404的情况
routes: constantRoutes
})
const router = createRouter()
// Detail see: https:///vuejs/vue-router/issues/1234#issuecomment-357941465
export function resetRouter() {
const newRouter = createRouter()
router.matcher = newRouter.matcher // reset router
}
export default router
执行 npm run dev
运行脚本,把项目跑起来。
npm run dev
默认登陆和加载菜单流程
路由守卫的流程 permission.js
,也就是说每次进行页面跳转之前都会经过这个路由守卫进行校验。
import router from './router'
import store from './store'
import {Message} from 'element-ui'
// 进度条控件
import NProgress from 'nprogress'
// 进度条样式
import 'nprogress/nprogress.css'
// 从cookie中获取token
import {getToken} from '@/utils/auth';
import getPageTitle from '@/utils/get-page-title';
// 进度条控件,NProgress配置
NProgress.configure({showSpinner: true});
// 没有重定向白名单
const whiteList = ['/login', '/auth-redirect'];
// 前置路由守卫
router.beforeEach(async (to, from, next) => {
// 开始进度条
NProgress.start();
// 设置页面标题
document.title = getPageTitle(to.meta.title);
// 从vuex里面得到用户登陆的token
const hasToken = getToken();
// 判断是否有token
if (hasToken) {
if (to.path === '/login') {
// 如果已登陆了,跳转到首页
next({path: '/'});
// 进度条效果关闭
NProgress.done();
} else {
// 确定用户是否已通过getInfo获得其权限角色
const hasRoles = store.getters.roles && store.getters.roles.length > 0;
if (hasRoles) {
next();
} else {
try {
// 如果没有得到权限则再去请求后台得到用户信息及权限信息
const { roles } = await store.dispatch('user/getInfo');
// 绑定动态路由【后面我们要修改】
const accessRoutes = await store.dispatch('permission/generateRoutes', roles);
// 添加动态路由到主路由
router.addRoutes(accessRoutes);
next({...to, replace: true});
} catch (error) {
// 如果出现异常,请求后台重置用户的token 并跳转到登陆页
await store.dispatch('user/resetToken');
Message.error(error || 'Has Error');
next(`/login?redirect=${to.path}`);
NProgress.done();
}
}
}
} else {
// 如果没有token就跳转到登陆页
if (whiteList.indexOf(to.path) !== -1) {
next();
} else {
next(`/login?redirect=${to.path}`)
NProgress.done();
}
}
});
router.afterEach(() => {
NProgress.done();
});
在登陆页面点击登陆的登陆逻辑。
找到 store/modules/user.js。
找到 api/user 里面有一个 login 方法。
完成之后加载主页面, 加载 permission.js 去请求用户数据及加载路由。
修改 9527 端口改成 80,修改 vue.config.js 文件,其他的样式都是可以自己定义的,我就不在一一说明了。
登陆接口准备说明
从上面可以看出, 登陆的时候使用了这三个接口,所以我们为了避免修改很多前端代码,我们去写后端接口的时候就直接把这三个接口写出来。
它的逻辑是, 先登陆 → 登陆成功之后得到用户信息 [用户信息, 权限信息, 角色信息],退出 → logout方法
到此,我们前端的框架搭建完成,当然,如果要真正开发,那么前端我们还有很多东西要修改。但是别担心,我们后面都会一个一个的修改完善的。