# vue-base-demo **Repository Path**: zhkumsg/vue-base-demo ## Basic Information - **Project Name**: vue-base-demo - **Description**: 手摸手,教你用vue搭建最小管理系统 - **Primary Language**: JavaScript - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 6 - **Forks**: 1 - **Created**: 2018-09-22 - **Last Updated**: 2022-07-22 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # vue-base-demo ![alt vue](https://www.runoob.com/wp-content/uploads/2017/01/vue.png) ![alt element](https://camo.githubusercontent.com/462f24153b8e8739c8ea71f7102585c4cb0e1575/68747470733a2f2f63646e2e7261776769742e636f6d2f456c656d6546452f656c656d656e742f6465762f656c656d656e745f6c6f676f2e737667) ## 项目介绍 > 手摸手,教你用 vue 搭建最小管理系统 基于 vue 框架,采用饿了么团队的 element-ui 库,具备登录、`权限维护`、数据维护等管理系统(中后台)常见内容,操作方式为列表-详情-维护,适用于多种业务场景,如 CRM、ERP 等,可快速开发和方便迭代。(SPA`单页面应用`,暂未使用多入口方式) ui 库使用 vue 下非常流行的 element-ui,页面风格可自由配置;路由使用官方推荐的 vue-router;http 服务支持 axios 与 vue-resource 两种;使用 vue 官方推荐的 vuex 状态管理 为了方便 http 请求与响应信息的统一处理,已封装 http 拦截器。后端可使用 nodejs、C#、java、php 等。 - react [https://gitee.com/zhkumsg/react-base-demo](https://gitee.com/zhkumsg/react-base-demo) - node [https://gitee.com/zhkumsg/node-base-demo](https://gitee.com/zhkumsg/node-base-demo) --- ## 开发环境 - window or linux or max - vue - element - npm - webpack ~~-sample~~ - visual studio code or others --- ## 目录结构 ``` +-- dist | |-- *.* 打包后的静态资源 +-- src | +-- assets | |-- *.* 静态资源 | +-- components | | |-- Backtotop.vue 返回顶级组件 | | |-- Canvasclock.vue 时钟组件 | | |-- Error.vue 错误页面 | | |-- Home.vue 页面主体 | | |-- Information.vue 文章维护 | | |-- Investment.vue 投融事件维护 | | |-- Investor.vue 投资人维护 | | |-- Login.vue 登录页面 | | |-- Main.vue 控制台 | | |-- Navmenu.vue 导航组件(递归) | | |-- Params.vue 系统参数维护 | | |-- Permit.vue 权限维护 | | |-- Permitconfig.vue 权限分配维护 | | |-- Role.vue 角色维护 | | |-- Routermenu.vue 路由组件(递归) | | |-- User.vue 用户维护 | |-- component.config.js 全局组件配置 | |-- javascript.common.js 通用方法 | |-- main.js 入口文件 | |-- router.config.js 路由配置(动态生成) | |-- vue.config.js vue文件配置 | |-- vueeditor.config.js 富文本配置 | |-- axios.config.js http拦截器 | |-- vueresource.config.js http拦截器 | |-- vuex.config.js 状态管理配置 |-- gitignore git忽略规则 |-- favicon.ico 网站图标 |-- index.html 首页 |-- package.json 项目配置 |-- webpack.config.js webpack配置 ``` ### 简要说明 1. dist 打包后静态资源放在根目录 dist 目录下,与 webpack 标准打包不同的是,index.html 依旧是在根目录下,不在 dist 文件夹下,所以发布路径是根目录 2. src/components/Backtotop.vue 监听 dom 滚动事件,当滚动高度大于指定值时自动出现 3. src/components/Canvasclock.vue 采用 canvas 做的一个时钟组件 4. src/components/Error.vue 没有权限时统一跳转到该页面 5. src/components/Home.vue 主体部分,采用 flex 布局(上下-左右),在这里做菜单和导航跳转 6. src/components/Login.vue vue 在创建实例前会根据是否已登录,如果没有登录,会创建一个临时的 vue 实例,里面只有一个登录路由,登录成功后会刷新页面,正常进入系统(非多入口) 7. src/component.config.js 全局组件配置,每一个全局组件都需要在这里添加,格式如 `{ key: "canvas-clock", value: Canvasclock }` 8. src/axios.config.js & src/vueresource.config.js http 拦截器,在请求前和响应后对信息进行拦截处理,如在发起请求前添加 token 和 http 前缀,在响应后对响应信息进行判断,如是否显示警告信息,是否登录超时。统一处理,服务已挂载到 Vue 实例中,使用`this.$http`即可调用。(axios 和 vueresource 功能一样,引入任一个即可) 9. src/vue.config.js vue 单文件组件配置,为了方便路由动态生成,故把系统的全部组件(页面)统一封装成一个文件导出,格式如 `Main: Main` 10. src/vueeditor.config.js 本系统使用的富文本是简单的 vue-editor,是一个轻量级的富文本,后期改为百度富文本 11. src/vuex.config.js vuex 状态管理配置,这里只共享了 userinfo(也可使用组件间通讯,不过不好管理) --- ## Installation ```bash $ npm install $ npm run dev ``` - 网络不好的童鞋可以使用`cnpm` --- ## 使用说明 ### 如何查看脚本命令? 在 npm 下,我们往往是使用 npm run xxx 来实现某一项任务,我们可以通过查看 package.json 项目配置文件的 scripts 节点来知道有哪些命令,我们也可以使用`npm run`命令来查看当前项目有哪些命令,就和 npm install 一样,会自动安装所需依赖,npm run 也会自动把所具备的命令列出来,现在该项目有 dev 和 build 两个命令 ```bash $ npm run ``` ### 端口冲突怎么办? 默认情况下,执行`npm run dev`后会自动在`8080`端口启动服务,并且`自动在浏览器中打开`。如果发生端口冲突的异常,只需要把 package.json 下的 dev 脚本`--port`修改成其他端口即可,如 8081。完整命令如下: ```cmd cross-env NODE_ENV=development webpack-dev-server --open --hot --port 8081 ``` - 如果不希望每次都自动打开浏览器,只需要把该命令行中的`--open`删除掉即可 ### 登陆流程是什么? 在页面初始化的时候,会根据登录状态决定使用哪一种路由,当没有登录的时候,会直接打开登录页面。在登录页面中,用户需要填写账号、密码、验证码才能完成登录。 验证码采用图片验证码的形式,请求`/api/start/captcha`接口后,后端会返回一个`svg`的标签和一个`密钥`,效果如下:

``` 88a64f23aba978b554b2ebc87e9cdb23 ``` 这时候前端需要把该 svg 显示到页面上,并在保存好密钥,在提交时携带用户输入的验证码和刚刚的密钥。 除此之外,登录时需要把用户输入的密码进行`md5加密`。 登录成功后,会在响应中返回`access-token`和`permit-token`,前端需要缓存好该数据(30 分钟内有效),如缓存到 localStorage 中 ```json { "access-token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9................VJTT1VSQ0UiOiLmma7pgJrms6jlhowiLCJpYXQiOjE1NDQ0OTQxMjIsImV4cCI6MTU0NDQ5NzcyMn0.ikfFiDwjQf_gu0VGF1k2U5bB__m3v2oWg7qV5yX1sXo", "permit-token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.................eyJfX3NlbGZfXyI6W3siWktfSUQiOiIwMDMxNTIxODc3ODM1MlcpfozdBQsB38Rye_2c7pRDIu_y97K-9ZbmB5SDE-Ug" } ``` 这里是分别缓存到 localStorage 的`SysTokenSession.accessToken`和`SysTokenSession.permitToken`中。 然后刷新页面,重新初始化,这时如果 localStorage 中存在前面提到的两个内容,会自动在请求头中带过去,后端解析`token`的有效性并返回相应的数据 完成登录。 ### 权限判断是如何实现的? 在页面初始化的时候,会在路由配置中对`/api/home/menutree`接口发起请求,如果携带有效的 token,后端将返回对应的`权限数组`,前端根据该数组递归构建`菜单树`和`路由`,构建完成后 vue 实例才开始创建。 此时用户的菜单和路由完全按照权限来构建,实现了前端权限的差异显示,对于后端,在响应请求前会根据当前用户的`token`判断是否能执行该请求,如果不存在则返回异常 - 登录后的 token 非常重要,需要在每次请求的时候在请求头中带上,否者后端无法验证用户身份 - 获取到`权限数组`后,前端会缓存到 localStorage 中,避免每次刷新都要重新请求一次 ### 存在跨域怎么办? 在前后端分离的状态下,前端请求后端接口往往会存在跨域,这时候有很多种解决方案,比较简单的有两种,第一种是前端使用 webpack 开启代理,第二种是服务端实现 cors。 > 使用代理: - _webpack-sample_ _当前项目使用的是 ~~webpack-sample~~ (已升级为 webpack)_,配置代理形式与 webpack 不太一样,webpack-sample 的设置在`webpack.config.js`的`devServer`节点下,配置如下: ```js devServer:{ ... proxy: { '/api': { target: 'http://localhost:3000', changeOrigin: true, } } } ``` - webpack 如果后期把 webpack-sample 改成 webpack,代理跨域就更加简单了,只需要在`config/index.js`下修改`proxyTable`节点 ```js proxyTable:{ '/api': { target: 'http://localhost:3000', changeOrigin: true, } } ``` > 服务端实现 cors > 服务端配置会更加方便,因为只需要加一句代码就可以解决跨域问题, ```js res.header('Access-Control-Allow-Origin', '*'); ``` 星号(`*`)代表全匹配,从安全角度来说这里应该配置成指定域名;如果需要添加多个域名白名单,需要检测当前请求的 Origin 请求头,然后动态添加即可。(webpack 和 webpack-sample 代理情况下,Origin 为空,将会被拒绝请求,如果需要调试,请本地运行 node 服务端) ### 为什么会发起两个一样请求? 在存在跨域的情况下,从控制台的网络请求中可以看到会发起两个`一样`的请求。其实这种情况是正常的,这两个请求并不一样,这就是 http 简单请求和复杂请求的区别。在跨域时,浏览器会发起一个 option 请求给服务器,服务器会根据当前域名判断是否允许后面的正式请求,如果没有配置 cors,那么第二次的正式请求就会被`浏览器`拦截。所以第一次请求 option 为浏览器的预请求,只要配置好了 cors,不影响后续操作。 ### 如何注册用户? 本系统暂未开放注册功能,注册需要联系管理员,由管理员在用户维护中新增一条数据。 ### 如何分配权限? 本系统的权限是很重要的一部分,在用户登录时就会返回当前用户的权限列表,然后动态生成路由和菜单,每次请求时都会对接口权限就行判断。 目前的权限是角色权限,只区分角色不区分用户和数据。权限配置与页面配置一致,如果新增了一个页面,需要相应的在`权限配置页面`添加一条记录。 ### 如何维护? 目前大部分页面都是使用 table -> form 的形式展示,即通过 table 来展示数据列表,通过 form 来展示数据详情,最后实现增删改查。每个页面的结果都是类似的,所以掌握一个页面的写法即可(可参考`角色维护页面`) ### 如何部署上线? 服务器部署需要的是打包后的静态资源,可以打包后再上传,也可以在服务器打包。 ```bash $ npm run build ``` - web 服务器有很多,可以实现 iis、nginx、tomcat 等 --- ## PrerenderSPAPlugin 预渲染 `单页应用(spa)`对 seo 不友好,爬虫爬取页面信息时只会爬取到原始的 html 源码,无法查看到动态加载的内容,因此存在几种解决方案: 1. 使用隐藏元素 2. 使用预渲染 3. 使用 SSR 其中,第二第三种方案较为可行,而预渲染改动又是最小的。先安装`prerender-spa-plugin`模块,然后修改路由为 history 模式,紧接着在打包配置中添加`PrerenderSPAPlugin`插件,最后在入口文件中触发相关事件即可。插件配置如下: ```js const PrerenderSPAPlugin = require('prerender-spa-plugin'); // 预渲染 const Renderer = PrerenderSPAPlugin.PuppeteerRenderer; // plugins // 配置PrerenderSPAPlugin new PrerenderSPAPlugin({ staticDir: path.join(__dirname, '../dist'), // 对应自己的路由文件,比如index有参数,就需要写成 /index/param1。 routes: ['/'], // 因为该系统操作都是基于登录后的,所以只做登录页面的预渲染就行了 renderer: new Renderer({ inject: { foo: 'bar', }, headless: false, // 在 main.js 中 document.dispatchEvent(new Event('render-event')),两者的事件名称要对应上。 renderAfterDocumentEvent: 'render-event', }), }); ``` `main.js`入口文件在 vue 实例完成后执行 ```js document.dispatchEvent(new Event('render-event')); // 预渲染 ``` ### 预渲染注意事项 - vue-cli 2.x 和 vue-cli 3.x 配置差不多 - 路由需要使用 history 模式 - 预渲染配置 routes 属性可以填写路由对应的 url,如果需要参数需要配置合适的参数 - renderAfterDocumentEvent 需要对应 - 预渲染打包前会自动打开浏览器从而获取到相关 dom,如果没有浏览器软件将打包失败(建议把 dist 包括在 git 项目内) - nginx 需要相应的修改,避免 404 --- ## 前端埋点(声明式埋点) _可使用通用指令进行前端埋点_ ```xml
...
``` --- ## 参考链接 1. [跨域 https://www.jianshu.com/p/5805fd1027eb](https://www.jianshu.com/p/5805fd1027eb) 2. [node 服务端 https://gitee.com/zhkumsg/node-base-demo](https://gitee.com/zhkumsg/node-base-demo) 3. [nginx 部署 https://www.jianshu.com/p/86d6f074e6fc](https://www.jianshu.com/p/86d6f074e6fc) 4. [通用指令 https://gitee.com/zhkumsg/common-directive](https://gitee.com/zhkumsg/common-directive) 5. [通用组件 https://gitee.com/zhkumsg/common-component](https://gitee.com/zhkumsg/common-component)