# lucy_portal_vue
**Repository Path**: lucyliang01/lucy_portal_vue
## Basic Information
- **Project Name**: lucy_portal_vue
- **Description**: 以vue开发的lucy_portal的前端页面
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 1
- **Forks**: 0
- **Created**: 2020-11-19
- **Last Updated**: 2024-03-15
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
### Vue后台管理项目
#### Vue项目搭建
###### 1.根据vue ui向导搭建新项目
* 在命令提示符中输入vue ui 命令,打开vue项目管理器

* 选择项目目录

* 包管理器选择 npm,初始化git仓库填写 init project就可以了

* 选择手动配置

* 选择预设安装的插件

* 选择vue版本为2.x,选择eslint配置为Eslint+standard config

* 根据自己的情况选择保存或者不保存预设

* 项目创建完成进入项目的管理界面

###### 2. 插件安装
在本项目中要使用到element-ui,在这里安装插件vue-cli-plugin-element
* 点击添加插件,选择vue-cli-plugin-element 插件进行安装


* 插件安装中我们要对element按需导入,等待到安装完成

###### 3. 项目依赖安装
* 在项目中我们会使用less语法,所以我们需要安装less和less-loader依赖

* 在项目中使用axios进行http请求,需要安装axios依赖

##### 4. 项目创建成功
* 进入任务面板,在serve选项卡中点击运行,项目会进入编译
* 编译完成后,点击启动app按钮,进入项目首页,就表示项目初始化创建成功


查看本地的项目目录文件夹


###### 5. 对初始化的项目,不需要的项进行清除
* 删除views文件夹
* 删除HelloWorld.vue文件
* 打开App.vue ,清除原先的代码,至这样的
```
根节点
```
* 打开index.js文件,清除不必要的代码
```
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
const routes = []
const router = new VueRouter({
base: process.env.BASE_URL,
routes
})
export default router
```
* 再到管理器面板中下,对项目进行编译,启动
出现下面的界面说明,编译成功

#### 代码编辑器设置
###### 1. vs code格式化代码配置
* 安装格式化代码插件prettier+eslint

* shift+ctrl+p 打开命令面板,搜索配置项,打开settings.json,将下面的配置代码拷贝进去


```js
{
// tab 大小为2个空格
"editor.tabSize": 2,
// 编辑器换行
"editor.wordWrap": "off",
// 保存时格式化
"editor.formatOnSave": true,
// 开启 vscode 文件路径导航
"breadcrumbs.enabled": true,
// prettier 设置语句末尾不加分号
"prettier.semi": false,
// prettier 设置强制单引号
"prettier.singleQuote": true,
// 选择 vue 文件中 template 的格式化工具
"vetur.format.defaultFormatter.html": "js-beautify-html",
// vetur 的自定义设置
"vetur.format.defaultFormatterOptions": {
"js-beautify-html": {
"wrap_line_length": 30,
"wrap_attributes": "auto",
"end_with_newline": false
},
"prettier": {
"singleQuote": true,
"semi": false,
"printWidth": 100,
"wrapAttributes": false,
"sortAttributes": false
}
},
"[vue]": {
"editor.defaultFormatter": "octref.vetur"
}
}
```
###### 2. eslint规则配置
参考如下的eslint规则进行配置
```
rules: {
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
'key-spacing': 0,
'space-before-function-paren': 0,
'keyword-spacing': 0,
'space-infix-ops': 0,
'no-trailing-spaces': 0,
'no-multi-spaces': 0,
'import/no-duplicates': 0,
'indent': 0,
'spaced-comment': 0,
'comma-spacing': 0,
'object-curly-spacing': 0,
'comma-dangle': 0,
'arrow-spacing': 0,
'space-before-blocks': 0,
'eol-last': 0,
'no-multiple-empty-lines': 0,
'vue/no-parsing-error': [
2,
{
'x-invalid-end-tag': false
}
]
}
```
#### 登录页面
###### 1. Login.vue创建
* 在compoments文件夹中创建Login.vue
```vue
登录
```
###### 2. 路由配置
```
import Vue from 'vue'
import VueRouter from 'vue-router'
import Login from '../components/Login.vue'
Vue.use(VueRouter)
const routes = [
{
path: '/',
redirect: '/login'
},
{
path: '/login',
component: Login
}
]
const router = new VueRouter({
routes
})
export default router
```
###### 3. 编译并且启动项目,看到登录二字,表示配置成功

###### 4.登录页面布局
* 全局css
新建assets/css文件夹下创建gloable.css文件 设置html,body,#app高度百分百
```
html,
body,
#app {
height: 100%;
}
```
* 全局css在main.js中的引入
```js
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import './plugins/element.js'
//引入全局样式
import './assets/css/gloable.css'
Vue.config.productionTip = false
new Vue({
router,
render: h => h(App)
}).$mount('#app')
```
* element-ui组件按需导入
在plugin的element.js替换为如下的代码,如下是官方提供的全部的按需导入的组件,可以按照实际情况删除一些不需要的组件
```
import Vue from 'vue'
import {
Pagination,
Dialog,
Autocomplete,
Dropdown,
DropdownMenu,
DropdownItem,
Menu,
Submenu,
MenuItem,
MenuItemGroup,
Input,
InputNumber,
Radio,
RadioGroup,
RadioButton,
Checkbox,
CheckboxButton,
CheckboxGroup,
Switch,
Select,
Option,
OptionGroup,
Button,
ButtonGroup,
Table,
TableColumn,
DatePicker,
TimeSelect,
TimePicker,
Popover,
Tooltip,
Breadcrumb,
BreadcrumbItem,
Form,
FormItem,
Tabs,
TabPane,
Tag,
Tree,
Alert,
Slider,
Icon,
Row,
Col,
Upload,
Progress,
Spinner,
Badge,
Card,
Rate,
Steps,
Step,
Carousel,
CarouselItem,
Collapse,
CollapseItem,
Cascader,
ColorPicker,
Transfer,
Container,
Header,
Aside,
Main,
Footer,
Timeline,
TimelineItem,
Link,
Divider,
Image,
Calendar,
Backtop,
PageHeader,
CascaderPanel,
Loading,
MessageBox,
Message,
Notification
} from 'element-ui'
Vue.use(Pagination)
Vue.use(Dialog)
Vue.use(Autocomplete)
Vue.use(Dropdown)
Vue.use(DropdownMenu)
Vue.use(DropdownItem)
Vue.use(Menu)
Vue.use(Submenu)
Vue.use(MenuItem)
Vue.use(MenuItemGroup)
Vue.use(Input)
Vue.use(InputNumber)
Vue.use(Radio)
Vue.use(RadioGroup)
Vue.use(RadioButton)
Vue.use(Checkbox)
Vue.use(CheckboxButton)
Vue.use(CheckboxGroup)
Vue.use(Switch)
Vue.use(Select)
Vue.use(Option)
Vue.use(OptionGroup)
Vue.use(Button)
Vue.use(ButtonGroup)
Vue.use(Table)
Vue.use(TableColumn)
Vue.use(DatePicker)
Vue.use(TimeSelect)
Vue.use(TimePicker)
Vue.use(Popover)
Vue.use(Tooltip)
Vue.use(Breadcrumb)
Vue.use(BreadcrumbItem)
Vue.use(Form)
Vue.use(FormItem)
Vue.use(Tabs)
Vue.use(TabPane)
Vue.use(Tag)
Vue.use(Tree)
Vue.use(Alert)
Vue.use(Slider)
Vue.use(Icon)
Vue.use(Row)
Vue.use(Col)
Vue.use(Upload)
Vue.use(Progress)
Vue.use(Spinner)
Vue.use(Badge)
Vue.use(Card)
Vue.use(Rate)
Vue.use(Steps)
Vue.use(Step)
Vue.use(Carousel)
Vue.use(CarouselItem)
Vue.use(Collapse)
Vue.use(CollapseItem)
Vue.use(Cascader)
Vue.use(ColorPicker)
Vue.use(Transfer)
Vue.use(Container)
Vue.use(Header)
Vue.use(Aside)
Vue.use(Main)
Vue.use(Footer)
Vue.use(Timeline)
Vue.use(TimelineItem)
Vue.use(Link)
Vue.use(Divider)
Vue.use(Image)
Vue.use(Calendar)
Vue.use(Backtop)
Vue.use(PageHeader)
Vue.use(CascaderPanel)
Vue.use(Loading.directive)
Vue.prototype.$loading = Loading.service
Vue.prototype.$msgbox = MessageBox
Vue.prototype.$alert = MessageBox.alert
Vue.prototype.$confirm = MessageBox.confirm
Vue.prototype.$prompt = MessageBox.prompt
Vue.prototype.$notify = Notification
Vue.prototype.$message = Message
```
* Login.vue页面的布局和css
登录页面进行布局和css的完成,对于必须的数据进行初始化
```vue
```
###### 5.登录页面数据绑定
* axios配置,在main.js中替换如下页面
```js
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import './plugins/element.js'
import axios from 'axios'
//引入全局样式
import './assets/css/gloable.css'
//设置请求统一路径
axios.defaults.baseURL = 'https://192.168.2.60:12400/api/'
// 添加请求拦截器
axios.interceptors.request.use(function(config) {
//从sessionStorage中后去token
config.headers.Authorization = window.sessionStorage.getItem('token')
return config
})
// 添加响应拦截器
axios.interceptors.response.use(function(response) {
return response.data
})
Vue.use(axios)
Vue.config.productionTip = false
new Vue({
router,
render: h => h(App)
}).$mount('#app')
```
* 登录接口调用

页面代码如下
```vue
```
* 跳转到/home路径
在components中创建Home.vue
```
首页
```
路径添加/home,router/index.js代码替换如下
```
import Vue from 'vue'
import VueRouter from 'vue-router'
import Login from '../components/Login.vue'
import Home from '../components/Home.vue'
Vue.use(VueRouter)
const routes = [
{
path: '/',
redirect: '/login'
},
{
path: '/login',
component: Login
},
{
path: '/home',
component: Home
}
]
const router = new VueRouter({
routes
})
export default router
```
至此登录相关的功能完成,登录完成后如下

#### 后台主页布局
###### 1.后台页面布局
* 页面布局
```vue
```

* header布局
```
我的工作台
个人信息
修改密码
退出
```

###### 2.菜单布局
```
我的工作台
个人信息
修改密码
退出
收起
展开
导航一
导航一一
```

###### 3.获取菜单数据
```
我的工作台
个人信息
修改密码
退出
收起
展开
{{item.MenuName}}
{{item.MenuName}}
```

###### 4.导入外部的icon-font
* 从iconfont上选择需要的icon图标,并且下载代码放在assets/icon文件夹中
* 在main.js入口文件中添加iconfont.css的引入
```
//引入iconfont
import './assets/icon/iconfont.css'
```
* 在Home.vue中替换菜单的图标为iconfont图标
```
我的工作台
个人信息
修改密码
退出
收起
展开
{{item.MenuName}}
{{item.MenuName}}
```
###### 4.菜单导航
* 菜单开启路由,并且替换子菜单的index属性为菜单的路径
在el-menu节点上开启router
```
:router="true"
```
并且设置子菜单的index属性
```
:index="item.MenuPath"
```
在el-main内容更改为
```
```
这样就可以实现点击子菜单跳转到指定的路径

* 更改路由
1.添加Welcome.Vue
添加一个Welcome.vue文件
```
Welcome
```
2.添加Users.vue
```
用户列表
```
3. 更改路由配置 router/index.js
为home路由添加子路由
```
import Vue from 'vue'
import VueRouter from 'vue-router'
import Login from '../components/Login.vue'
import Home from '../components/Home.vue'
import Welcome from '../components/Welcome.vue'
import Users from '../components/Users.vue'
Vue.use(VueRouter)
const routes = [{
path: '/',
redirect: '/login'
},
{
path: '/login',
component: Login
},
{
path: '/home',
component: Home,
children: [{
path: '/',
component: Welcome
},
{
path: '/users',
component: Users
}
]
}
]
const router = new VueRouter({
routes
})
export default router
```

###### 5.导航守卫
在main.js中设置导航守卫,在用户没有登录的时候,访问除/login路径外都会跳转到/login进行登录,如果用户已经登录,则放行访问
在router/index.js下添加如下代码
```
//导航守卫
router.beforeEach((to ,from,next)=>{
if(to.path==='/login') return next()
const tokenStr=window.sessionStorage.getItem('token')
if(!tokenStr) return next('/login')
next()
})
```

##### 用户列表页面布局
###### 1.页面布局
* 页面布局
```
首页
用户管理
用户列表
```

###### 2.获取分页数据
* 接口调用

###### 3.绑定页面
* 数据绑定
通过接口获取分页用户的数据,并且绑定在列表上
```
首页
用户管理
用户列表
```

* 创建dateformate过滤器
在plugins文件夹下添加文件filter.js
```
import Vue from 'vue'
Vue.filter('dateformate',function(value){
var d= new Date(value)
const year= d.getFullYear()
const month= (d.getMonth()+1+'').padStart(2,'0')
const day=(d.getDate()+'').padStart(2,'0')
const hour=(d.getHours()+'').padStart(2,'0')
const minutes=(d.getMinutes()+'').padStart(2,'0')
const seconds=(d.getSeconds()+'').padStart(2,'0')
return `${year}-${month}-${day} ${hour}:${minutes}:${seconds}`
})
```
在main.js中引入filter.js
```
//导入过滤器
import './plugins/filter.js'
```
* 列表优化
对于用户类型使用tag标签进行显示优化,对于是否启用使用switch组件进行优化,对于创建时间使用filter进行格式化
```
首页
用户管理
用户列表
系统管理员
普通用户
{{scope.row.CreateTime|dateformate}}
```

* 分页绑定
```
```
函数的绑定
```
//改变显示页码触发
handleSizeChange(newSize) {
this.pageSize = newSize
this.getList()
},
//跳转页面触发
handleCurrentChange(newPage) {
this.pageIndex = newPage
this.getList()
},
```
* 查询
在el-table上面添加查询条件行
```
新增
```

#### 修改用户状态

###### 1.创建修改状态的函数 changeState
```
async changeState(info) {
const res = await this.$http.post(`/user/edit/${info.Id}`, info)
if (!res.flag) {
info.IsValid = !info.IsValid
return this.$message.error(res.msg)
}
this.$message.success('修改状态成功')
}
```
###### 2.在状态列绑定该函数
```
```
#### 新增用户
###### 1.dialog布局
在el-card下添加对话框
```
取 消
确 定
```
并提供相关的数据绑定
```
addDialogVisible: false,
addModel: {},
addModelRules: {
UserName: [{ required: true, message: '请输入活动名称', trigger: 'blur' }, { min: 3, max: 5, message: '长度在 3 到 5 个字符', trigger: 'blur' }],
UserType: [{ required: true, message: '请输入活动名称', trigger: 'blur' }]
},
userTypeList: [{ value: '1', label: '系统管理员' }, { value: '2', label: '普通用户' }]
```
###### 2.提交新增

在methods中添加add方法绑定在确定按钮上
```
add() {
//表单与预验证
this.$refs.addFormRef.validate(async valid => {
if (!valid) return
const res = await this.$http.post('user/add', this.addModel)
if (!res.flag) return this.$message.error(res.msg)
this.$message.success('新增用户成功')
//关闭弹窗
this.addDialogVisible=false
//刷新页面
this.getList()
})
}
```
###### 3.表单重置
在el-dialog中的close方法绑定重置表单的方法
```
resetAddForm(){
this.$refs.addFormRef.resetFields()
}
```
###### 4.用户列表整体效果和代码
* 整体的页面效果演示如下

* Users.vue组件的整体代码
```
首页
用户管理
用户列表
新增
系统管理员
普通用户
{{ scope.row.CreateTime | dateformate }}
取 消
确 定
```
#### Vue项目优化
###### 1.生成项目报告
* 通过UI的任务面板中,进行打包,通过查看控制台和分析生成打包报告
打包后进入的控制台面板

输出面板

分析面板

* 从报告中查看本项目所存在的问题
主要的问题是
css/chunk-vendors.e90756f2.css
js/chunk-vendors.299c40ed.js
css/app.80898f24.css
js/app.3faa412c.js
这几个的文件过大,那么之后的打包优化也会针对打包文件过大进行优化处理
###### 2.vue.config.js修改webpack配置
项目隐藏了webpack的默认配置,如果需要修改webpack的默认配置,在根目录下创建vue.config.js
###### 3.打包入口文件
现在的开发模式和打包模式的共用了同一个打包入口文件main.js。
为两种模式分别创建不同的打包入口文件。main-dev.js和main-prod.js
###### 4.配置chainWebpack更改webpack的配置
在vue.config.js的导出对象中,配置chainWebpack修改webpack的默认配置
chainWebpack是通过链式编程的方式修改webpack的默认配置
* 修改不同模式下的打包入口文件,在vue.config.js中配置如下的代码
```
module.exports = {
chainWebpack: config => {
config.when(process.env.NODE_ENV == 'production', config => {
config.entry('app').clear().add('./src/main-prod.js')
})
config.when(process.env.NODE_ENV == 'development', config => {
config.entry('app').clear().add('./src/main-dev.js')
})
}
}
```
###### 5.通过externals配置加载外部cdn资源
通过import导入的依赖项,最后都会被打包在chunk-verdos.js文件中,导致单文件体积过大
* 在config.js的生产模式中针对开发的依赖项进行下面的配置

把axios,vue,vue-router进行外部配置,以包名:对象名的对应方式进行配置
```
config.set('externals',{
vue:'Vue',
'vue-router':'VueRouter',
axios:'axios'
})
```
* 在/public/index.html中,引入如下的cdn配置
引入cdn的时候要对应依赖项的版本,做到版本一致
```
```
* 重新在build面板中执行,发现打包文件小了一些,但是大小还是处于报警的状态,我们要进行进一步的优化
###### 6.element-ui的优化
* 在main-prod.js中删除element-ui按需加载的代码
* 在public/index.html引入相关的css和js资源
```
```
* 在build中运行后,发现项目文件的大小瞬间降下来了。对chunk-verdos.js文件大小的优化就到位了。

###### 7.首页内容定制
首页我们首页引入的cdn资源,并没有区分生产模式还是开发模式,其实应该是在生产模式下才对首页的cdn资源引入。
* 在web.config.js中加入如下的配置,设置一个isProd的参数区分是生产模式还是开发模式
```
//生产模式
config.plugin('html').tap(args=>{
args[0].isProd=true
return args
})
//开发模式
config.plugin('html').tap(args=>{
args[0].isProd=false
return args
})
```
* 对public/index.html进行改造
```
<%= htmlWebpackPlugin.options.isProd?'':'dev-' %>后台管理系统
<%if(htmlWebpackPlugin.options.isProd){%>
<%}%>
```
再次运行build,检查在生产模式和开发模式下代码的异同
###### 8.路由懒加载
在打包的过程中,项目中的js代码会被打包在app.xxxxxxx.js中,随着开发的进行app.xxxxxx.js文件的体积会越来越大。
路由懒加载会把js文件分组打包在不同的文件中,只有相关的组件被访问到的时候才会加载对应的js文件,从而提高网站的访问速度。
具体的配置步骤:
* 安装开发依赖@babel/plugin-syntax-dynamic-import
* 在babel.config.js中声明该插件
```
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset'
],
plugins: [
[
'component',
{
libraryName: 'element-ui',
styleLibraryName: 'theme-chalk'
}
],
'@babel/plugin-syntax-dynamic-import'
]
}
```
* 将路由改成按需加载的形式
```js
const Foo = () => import(/* webpackChunkName: "group-foo" */ './Foo.vue')
```
router/index.js代码修改如下,把引入组件的代码修改如下
```
import Vue from 'vue'
import VueRouter from 'vue-router'
// import Login from '../components/Login.vue'
// import Home from '../components/Home.vue'
// import Welcome from '../components/Welcome.vue'
const Login = () => import(/* webpackChunkName: "login_home_welcome" */ '../components/Login.vue')
const Home = () => import(/* webpackChunkName: "login_home_welcome" */ '../components/Home.vue')
const Welcome = () => import(/* webpackChunkName: "login_home_welcome" */ '../components/Welcome.vue')
// import Users from '../components/Users.vue'
const Users = () => import(/* webpackChunkName: "users" */ '../components/Users.vue')
```
修改完成后重新进行build打包
发现app.xxxxx.js变小很多,同时出现了login_home_welcome.js和users.js
###### 9.开发模式下console.log的删除
* 安装插件babel-plugin-transform-remove-console
* 配置babel.config.js,配置在生产模式 下需要的插件,并且以对象展开的方式放在需要的插件的地方
```
// 所有生产环境需要的依赖
const prodPlugin = []
if (process.env.NODE_ENV === 'production') {
// 如果是生产环境,则自动清理掉打印的日志,但保留error 与 warn
prodPlugin.push([
'transform-remove-console',
{
// 保留 console.error 与 console.warn
exclude: ['error', 'warn']
}
])
}
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset'
],
plugins: [
[
'component',
{
libraryName: 'element-ui',
styleLibraryName: 'theme-chalk'
}
],
'@babel/plugin-syntax-dynamic-import',
...prodPlugin
]
}
```
#### Vue项目部署
###### 1.ngnix映射配置
在ngnix.conf文件做如下的映射配置
```
#html文件
server {
listen 9001; #映射端口
server_name 127.0.0.1 localhost; #映射ip
location / {
root J:/codes/lucy_portal/lucy_portal_vue/dist; #物理路径
index index.html index.htm; #首页
}
}
```
配置后打开ngnix,浏览器中访问localhost:9001
整体的效果如下

#### 项目总结
###### 1.本项目可以达到的学习目标
* ElementUI使用
* 路由使用
* 路由拦截
* 导航守卫
* 异步请求
* Vue项目的常见的打包优化方法
* Vue项目的部署
* 在短时间内学习Vue后台项目从创建到部署
* 对于中小型的项目来说,基本点已经涉及到了
###### 2.项目的缺点
* 项目在业务功能上只完成了用户列表,还有其他的管理有待完成
* 项目功能相对基础,是否适合真正的生产模式,还有待思考
#### TodoList
###### 1.根据教程自己做一遍,查漏补缺
###### 2.把服务端的接口和接口文档暴露出来
###### 3.进一步完善功能