使用技术
Vue3.0全家桶(Vue-Cli、Vue-Router、Vuex、Axios、Webpack)
Element Plus
TypeScript
Echarts
代码规范
配置husky、commitizen、eslint、prettier,制定前端开发规范(组件命名、文件存放位置),便于后期维护
文件存放位置:
assets是资源文件img/css
views是属于页面的组件
common是多个项目公用的组件
components是业务相关的组件
router是路由
stroe是Vuex仓库
service是axios请求
utils是工具(保存localstrage数据)
项目搭建流程
1.0. axios拦截器封装
①只有对应的实例才有的拦截器;
②所有的实例都有的拦截器(全局的拦截器);
③单独请求的拦截器;
1.2. loading组件封装
1.3. UI组件库按需引入
1.4. normalize.css样式初始化
(1)在assets中创建css文件夹,创建base.less和index.less,在base.less中写好初始化代码,在index.less中通过@inport导入base.less
(2)在main.ts中导入normalize.css和入口样式文件index.less
1.5. 搭建登录页面的结构
整个登录界面是login.vue,登录面板是个大的组件login-panel.vue,里面的大标题、Tabs标签切换、记住密码和登录按钮都是写在login-panel.vue组件中,Tabs切换里面的输入框分别封装账号登录组件login-account.vue和手机登录组件login-phone.vue,再各自单独封装输入框校验规则。
1.5.1. 拿到输入框中的内容
- 从vue中导入
reactive方法(实现响应式数据的方法),在setup函数中定义个acount属性等于reactive()里面传一个对象,定义username和password的初始化值,再去return中把acount返回出去 - 在
el-input标签中分别用v-model="acount.name"和v-model="acount.password"的方式实现双向绑定数据
1.5.2. 表单验证规则
对表单配置一些规则再把规则告诉表单,表单会自动验证规则,开发中常用的规则是写一个对象里面有各个属性的规则,再把整个规则传给el-from标签,让它自己去找每个item进行验证。
- 在login文件夹下面创建config文件夹,再分别创建账号登录的规则和手机登录的规则,在里面定义
rules规则并export导出,比如账号的输入框对应的是数组(因为有多个验证规则),数组里面的规则写成对象形式,规则1验证是否必填项,规则2验证通过正则表达式验证输入的内容和长度是否正确,如果输错了都会有红色文字提示,都会在输入框失去焦点的时候触发验证 - 回到
login-account.vue组件中import导入rules,并且在return中把rules返回出去,然后在el-from标签上用:rules="rules"绑定规则,因为里面的el-from-item无法匹配对应的规则,所以需要给el-from-item标签分别用prop="username"和prop="password"的方式绑定对应的规则 - 因为
el-from拿不到el-from-item最新的值,还需要在el-from标签上面通过:model="account"拿到最新的值
1.6. 勾选记住密码绑定输入框
从vue中导入ref方法,在setup函数中定义isKeepPassword属性,值为ref(true),然后在el-checkbox标签上面通过v-model="isKeepPassword"绑定。
1.7. 登录功能
在大的login-panel.vue组件中,此时点击立即登录按钮拿不到账号和密码。
方法一:可以在login-panel.vue组件上面定义ref,再把ref绑定到里面的表单小组件上面,就可以通过ref拿到账号和密码了
方法二:账号的登录逻辑分别写在账号登录组件login-account.vue和手机登录组件login-phone.vue上面
做法:在大的login-panel.vue组件中点击立即登录拿到账号密码,通知内部组件去执行操作(验证密码是否正确和记住密码需要把账号密码保存起来放到localstroage里面),然后去向服务器发送请求验证密码是否正确,正确后返回对应的用户信息给我保存起来,这套逻辑会写到vuex里面,封装一个actions,actions再向服务器发送请求。大的login-panel.vue组件只负责页面东西的搭建,以及告诉login-account.vue我现在点击了立即登录,至于内部该做什么事情你自己去验证自己去做登录
1.7.1. 登录按钮绑定点击事件
在大的login-panel.vue组件中,在登录按钮上面绑定一个handleLoginClick点击事件,在setup函数中定义handleLoginClick箭头函数,并且在return中把handleLoginClick返回出去。
1.7.2. 登录验证
需要告诉login-account.vue组件我点击了立即登录,然后去login-account.vue组件中获取组件对象,在大的login-panel.vue组件中直接调用组件方法。
- 在
login-account.vue中定义一个loginAction方法并return返回出去 - 怎么拿到组件对象呢?在大的
login-panel.vue组件中定义accountRef,值为ref(),并通过return把accountRef返回出去,再去login-account.vue组件标签上绑定ref="accountRef",拿到accountRef.value组件对象之后执行.loginAction()方法
1.7.3. validate登录判断,表单验证错误就不能正常登录
如果表单输入错误虽然表单会提示红色文字错误信息,但是点立即登录按钮还是会执行登录操作,所以应该在验证没有通过的时候既要提示红色文字错误信息,也要让我在点击登录按钮的时候不能让我执行登录逻辑不能去请求登录的接口。
所以真正的验证逻辑是要保证表单是验证通过的。那我怎么知道验证有没有通过呢?
- 在
login-account.vue组件的el-from标签上面再绑定一个ref="formRef",从vue导入ref方法,在setup函数中定义formRef属性,值为ref(),这里需要导入一个ElForm类型,然后在reruen中把formRef返回出去, - 在
loginAction方法中通过formRef.value.validate获取组件对象,用.validate方法判断,里面有个回调会返回布尔类型,通过valid参数来判断有没有验证通过,此时如果验证没有通过点击登录按钮打印valid会返回false
如果valid验证通过了,再看看是否需要记住密码,账号密码通过验证了并且勾选记住密码了,就把账号密码记录下来(好处是下次刷新浏览器会自动填充账号密码)
- 把
login-panel.vue组件中记住密码的状态isKeepPassword的值传递到loginAction方法中,然后在login-account.vue组件中的loginAction方法的参数接收这个值 - 在
valid中判断如果为true就要记住密码,然后做一个本地缓存,本地缓存的方法是通过localstroage,创建一个untis文件夹里面新建一个cache.ts写相关的缓存代码,在login-account.vue组件中导入cache.ts,通过LocalCache.setCache('username', account.username)方法拿到信息做本地缓存,如果没有记住密码就LocalCache.removeCache('username')清除缓存就可以了 - 此时点完立即登录账号密码都保存到本地浏览器了,但是一刷新的话输入框的账号密码的内容就没了,因为之前默认
accountd的username和password内容是为空的字符串,需要改成从本地缓存中获取,如果获取的值是undefined就给一个空的字符串username: LocalCache.getCache('username') ?? ''。
1.7.5. 登录逻辑步骤
发送网络请求,拿到数据后的处理;把数据保存到Vuex中;发送其他的请求(请求当前用户信息);拿到用户的菜单;跳到首页。
1.7.5. 数据保存到Vuex中
- store文件夹里面原本有个index.ts,需要分模块来写,新建个login.ts模块,然后在index.ts导入在
modules模块中注册 - 在login.ts里面从vuex中导入
Module模块,然后创建一个loginModule对象并且export导出,对象里面写个state并return一个token为空的字符串,userInfo为空的对象,userMenus为空的数组,用于接收请求到的数据 - 再给一个命名空间
namespaced: true属性。默认情况下,模块内部的action、mutation和getter是注册在全局命名空间的,这样使得多个模块能够对同一mutation或action作出响应。如果希望你的模块具有更高的封装度和复用性,此时就用到了命名空间这个概念。 - 在actions中定义
accountLoginAction方法(state状态中有值的话就需要加payload),然后在login-account.vue组件中的valid验证里面触发accountLoginAction方法,通过store.dispatch('login/accountLoginAction', { ...account })进行验证,需要先从vuex中导入useStore(为了获取store),在setup中定义store拿到useStore(),调用store中的dispatch方法并且通过解构的方式传入账号密码,然后点击登录就可以执行accountLoginAction方法并且获取到账号和密码
1.7.5. 请求账号密码和token
accountLoginAction方法中实现请求账号密码和token,在service文件夹中创建login文件夹和login.ts文件,先import导入封装好的ttRequest,再写一个accountLoginRequest方法传入账号密码的参数account,并export导出,accountLoginRequest方法里面返回ttRequest并调用post请求;为了方便url统一管理,在外面定义一个LoginAPI,里面定义单个请求的url地址,请求方法中获取url就可以写成url: LoginAPI.AccountLogin,post请求需要把获取到的account参数放到data里面- 回到store的login.ts中,从service中的login中导入刚刚写的
accountLoginRequest请求方法,在accountLoginAction方法中通过async/await发送请求 - 定义一个
loginResult等于accountLoginRequest(payload)方法,然后打印loginResult就可以返回账号密码和token了 - 去
loginResult的值里面取出id和token,把对应的token保存到上面去,在mutations里面定义changeToken方法传入state和token参数,里面让state.token = token - 在actions中
commit提交changeToken方法并且传入token参数 - store里的index.ts中先导入localCache,请求账户密码token的方法里面再做一个token的本地缓存
localCache.setCache('token', token),此时点击登录按钮vue插件的vuex就保存到token信息了
1.7.5. 请求用户信息
accountLoginAction方法中实现请求用户信息,在service的login.ts中定义LoginUserInfo的url,写一个requestUserInfoById方法传入id,并export导出,里面返回ttRequest调用get请求,请求方法中获取url并且拼接id就可以写成url: LoginAPI.LoginUserInfo + id- 回到store的login.ts中,从service中的login中导入
requestUserInfoById方法,和loginResult写在同一个位置,定义一个userInfoResult等于await requestUserInfoById(id)方法,在mutations里面定义changeUserInfo方法传入state和userInfo参数,里面让state.userInfo = userInfo - 在service的index.ts中每次发送请求都需要在请求头中加上token,定义的token之前是为空的字符串,把token改成从本地缓存中获取,需要先导入localCache(这里会报一个
config.headers错误,去请求类型的TTRequestConfig里面加上headers?: any,报错位置的requestInterceptor的config加一个TTRequestConfig类型) - 在actions中
commit提交下changeUserInfo方法并且传入userInfo参数 - 请求用户信息方法里面再做一个userInfo的本地缓存
localCache.setCache('userInfo', userInfo),此时点击登录按钮vue插件的vuex就保存到userInfo信息了
1.7.5. 请求用户菜单
accountLoginAction方法中实现请求用户菜单(根据用户角色请求用户菜单),在service的login.ts中定义UserMenus的url,写一个requestUserMenusByRoleId方法传入id,并export导出,里面返回ttRequest调用post请求,请求方法中获取url就可以写成url: LoginAPI.UserMenus- 回到store的login.ts中,从service中的login中导入
requestUserMenusByRoleId方法,和loginResult写在同一个位置,定义一个userMenusResult等于await requestUserMenusByRoleId(userInfo.id)方法,在mutations里面定义changeUserMenus方法传入state和userMenus参数,里面让state.userMenus= userMenus - 在actions中
commit提交下changeUserMenus方法并且传入userMenus参数 - 请求用户菜单方法里面再做一个userMenus的本地缓存
localCache.setCache('userMenus', userMenus),此时点击登录按钮vue插件的vuex就保存到userMenus信息了
1.7.5. 跳到首页,全局导航守卫判断有无token
- 在store的login.ts中import导入router,
accountLoginAction方法中实现跳到首页router.push('./main') - 默认情况下router的index.ts中的
redirect是直接跳到首页,这个时候就需要全局导航守卫判断用户有没有登录,如果没登录让他去登录,如果登录了就跳到首页,在当前文件下面写个beforeEach导航守卫判断当前要去的路径是不是登录页,只有你不是登录页的时候,再判断你有没有token,先去缓存里获取token,如果没有token值,就跳转到登录页,有token就跳转到main页
1.7.5. Vuex防止用户刷新数据消失
vuex数据是在内存里的,只要用户一刷新,里面的数据就会消失了。原来的逻辑是在用户发送请求之后,给他对应的数据,再把数据通过mutations保存到vuex中。
- 在store的index.ts中封装
setupStore函数,调用loadLocalLogin方法store.dispatch('login/loadLocalLogin') - 在main.ts中导入
setupStore函数并在app.mount('#app')之后调用这个方法setupStore(),意思是每次重新运行这个代码都会调用setupStore方法 - 在store的login.ts中的actions中封装
loadLocalLogin方法(与accountLoginAction方法平级),先从本地获取token,判断token有没有值,有值的时候再去commit下changeToken方法,并把token传进去,用户信息和用户菜单也是同样的方法来判断保存提交数据
1.7.5. 解决跨域
开发阶段,可以利用webpack配置devServe里面配置proxy,加个changeOrigin: true;部署阶段在Nginx里面配置。
1.7. 登录功能
1.8 登录逻辑概括
(1)在登录页面点击登录的时候,前端带着用户名和密码去调用后端的登录接口
(2)后端收到请求,验证用户名和密码
(3)验证失败,会返回错误信息,前端提示相关错误信息
(4)验证成功,就给前端返回一个token,前端拿到token,将token存储到localStorage和Vuex中,并跳转路由页面,即登录成功
(5)前端每次跳转页面时都需要判断localStroage中有无token ,没有就跳转到登录页,有则跳转到对应路由页面(通常封装在全局路由导航守卫中)
(6)每次在向后台发送请求时,都需要在请求头中加上token(通常封装在请求拦截器中),后端判断请求头中有无token,有则拿到并验证该token,验证成功就正常返回数据;验证失败(没有token/token过期)则返回401,前端拿到状态码为401,就清除token信息并跳转到登录页
UI组件库的使用
el-tabs标签上面写个stretch可以让标签的宽度自撑开- 输入框前面的文字显示是在
el-from-item标签上面写个label="账号",文字的宽度设置是在el-from标签上面写个label-width="60px - 表单规则中加上
required: true会自动加必填项的*号 - 隐藏密码在
el-input标签上面写个show-password是否显示切换密码图标 - Tab切换里面的账号登录和手机登录分开验证,记录当前选中了哪个,setup函数里面定义一个currentTab属性值默认为
ref('account'),再return出去,el-tabs标签上面写个v-model="currentTab",有个name属性是与选项卡绑定值value对应的标识符,在两个el-tab-pane标签上面分别写个name="account"和name="phone"属性,然后就可以切换了,可以在handleLoginClick方法中做一个判断,如果currentTab.value === 'account’等于account就执行账号登录accountRef的方法,如果选中的是phone就执行手机登录phoneRef的方法。 - 日历框和分页是英文,要做一个国际化,在app.vue里面包裹
ElConfigProvider el-tag样式类似于el-button,如果鼠标滑过按钮不需要点击的事件和小手样式就可以用el-tag标签- 日期时间选择器选择日期后后台打印接收到的是utc(2022-09-07T16:00:00.000Z)格式的字符串,可以在
el-date-picker表单上面加上两个属性format="YYYY-MM-DD HH:mm:ss"和value-format="YYYY-MM-DD HH:mm:ss",就会返回正常的日期格式,再加上type="datetimerange"属性可以选择具体时间范围;也可以做一个格式化,如果是返回的是时间戳也可以转换 el-option选择后下拉框显示的还是value值(比如0,1,2),可以在el-option上面加个:label="option.title",就可以正常显示value值了el-crad加间距可以在el-row上加gutter属性image组件:当开启图片预览功能,即设置了preview-src-list属性时,若预览时元素层级出现错乱,需设置preview-teleported属性值为true
退出登录
- 点击登录按钮清空token
- 跳转到main页
合并本地路由方法
- 创建好2个组件页面和对于的router文件
- router文件夹里的index.ts里面,在main路由下的chidlren配置好两个路由,然后再把首页重定向改成第一个菜单
- untils里的menu-maps.ts封装个staticRouter函数
- login.ts里面导入staticRouter方法,把这两个本地的路由和接口里的一起拼接到userMenus里面去,保存在vuex中
引用外部svg方法
npm install svg-sprite-loader- vue.config.js配置
svg-sprite-loader - 封装
svg-icon组件 - 准备好本地的svg图片文件放到src下icons文件夹里
- untils里的
menu-maps.ts封装个getIconsSvgName函数 - untils里封装个
validate.ts
echarts使用
- 安装
npm install echarts - 组件中导入
import * as echarts from 'echarts' - 初始化echarts对象
echarts.init(dom, theme, options)(第三个参数是渲染器的意思,可选canvas或svg, canvas 适合绘制图形元素数量非常大的例如热力图、地理坐标系大规模线图、散点图等;svg有重要优势就是内存占用更低 对移动端很重要,渲染性能略高,使用浏览器的缩放功能不会模糊) - 缩小浏览器的宽度部分图表会被遮挡,做个响应式缩放
注意事项
- 数据模块方法等必须return出去,否则template中不能使用
- 定义布尔类型会有一个习惯通过is开头
- proxy代理是组件对象
- 遍历都用template模板遍历
- setup函数只会调用一次
- 两个!!可以转成布尔类型
- 搜索是拿到关键字,后端做模糊搜索的
- 升级vue版本
npm install vue@next - 项目下载版本运行问题:^和~表示指定的是不明确的版本。package-lock.json是记录详细版本的,如果没有共享这个文件,新下载项目安装依赖可能会报错,解决方法:先卸载
npm uninstall vue,再npm install vue@next --force - Vue3.2版本的优化,script setup正式版,之前是vbase-3-ts自动生成的代码,可以把script里面定义的东西直接全部删除,然后直接在script上面加一个setup属性,就不再需要defineComponent/setup函数这些东西了,里面该导入什么还和之前的写法一样,然后也不需要return了,如何定义props,需要导入deFineProps,再写一个deFineProps函数,导入withDefaults可以设置默认值(第43节课1小时13分)
- 部署不一定是前端来做,可能是运维或项目经理
- 先安装
tree,再在终端输入tree会生成所有目录,敲tree -I "node_modules|dist"忽略文件 - 平时把代码封装的尽可能能够复用,以后写代码就会变得非常轻松
- 以前的写法是optionsAPI(methods:{},computed:{}, …)
- 远程仓库:github/gitlab/自己搭建git服务器,本地项目提交到远程仓库之后,需要提交给测试让他的浏览器打开项目访问,jenkins服务器测试(还要安装java)
- 在组件的style中导入样式表的写法是:
@import “./assets/css/base.css” - 移动端中一般顶部
navbar高度是44px;底部tabbar高度是49px - 组件命名使用大驼峰式写法:例如
TabBar、BackTop - 封装组件:本组件相关样式及代码全部写在本组件中,方便复用
- 图片资源按功能分类,例如只和tabbar组件有关的图片就建在tabbar文件夹里,放在img里面,方便查找
- 跳转路由:安装vue-router,写好路由相关js搭好结构,配置映射关系跳转页面,创建的大视图组件放在views文件夹里面(views是大的视图页面) , views里面再创建相应的文件夹装对应大页面的相关组件,js通过懒加载的方式导入组件(路由懒加载)。
<router-view> </router-view>占位- 当前页面高亮显示:通过v-bind动态绑定style,把style的东西抽到计算属性里面去
- 配置别名:webpack.base.conf.js的resolve里的alias配置,src属性的assets路径前要加~(相当于
~@/components/assets/img/logo.png) noscript标签(当页面打不开时,显示一句话)- 动态获取插槽:跟页面相关的插槽不要放到公共的插槽一起,从
tableConfig.propList里获取所有插槽名称slotName,动态放到公共slot里面去,然后是if判断下如果是公用的插槽就过滤排除掉,剩下的就是与页面相关的插槽来作为动态插槽 - 获取接口里面嵌套的数组对象userRoles:[{},{}]需要用v-for遍历,json里面嵌套的如果只是对象就不需要遍历,也需要用插槽获取内容
- 封装的逻辑:表格序号列,可能有的页面会显示有的页面不显示,有没有应该有外界来决定的
- 页码:有些页面需要显示有些不需要显示,在table组件的props里
写个showPageNum,默认为true显示,再v-if="showPageNum"判断,config里面的showPageNum写个false就不会显示页码 - 接口返回的数据是0、1、2,需要转换成一级二级三级,可以先把他们用new Map([[],[],[]])定义成一个双层数组,然后用map.get方法获取
- 封装搜索表单公共组件的过程:先在公共文件夹下创建一个公共组件form.vue,props要求别人传递一个参数,比如formItems
- payload:state状态中有值的话就需要加payload
- 所有的页面组件放到views文件下;所有公共组件放到baseui文件下(面包屑、用户信息的下拉菜单)(主要是对element-plus的原生组件二次封装 );所有与项目相关的组件放到components文件下(echarts)(page-xx主要是做请求层业务逻辑)
- 父组件修改子组件里面某个属性的值(场景:控制子组件里面的对话框)
- 发送网络请求在actions里面操作
- 表格,替换一列的数据,用到了作用域插槽,插槽的名字可以用动态插槽名字,在配置里面用了soltName
- 页面封装好之后,都可以通过配置文件写出来,在其他地方使用就直接修改配置
- store里面写好module框架,再定义好state里每一个接口存储的地方;service里写API请求接口数据,组件里面写store.dispatch一下(点到当前组件触发这个请求,每次都要从vuex导入useStore);回到store里面在actions中通过async await获取接口数据,修改state数据要通过mutations里面定义好一个提交的方法,再在actions中通过commit提交数据( commit两个参数,mutations里定义好change方法和await获取到的数据)到state中
问题
-
翻页问题:先点击到第二页,再搜索禁用状态或性别,会重新跳到第一页去;剩余的搜索结果在第二页的,翻过去不会显示剩余的(不会带着条件查询),会重新查询列表;在第二页新增用户成功之后没有刷新跳到第一页。
-
用户管理:搜用户角色,不管选哪个下拉框都显示最后一个值;然后再去选择其他条件搜索(2个条件),搜出不来结果,搜索后associateVal的值的是Object。
-
添加功能:右上角头像展示功能、列表多选批量删除功能、用户列表加头像展示和上传修改头像功能;删除用户列表时添加弹窗确定删除和 删除 成功提示、编辑用户列表之后的更新成功提示。
-
角色管理:创建完的角色不能回显
-
自己创建的用户,登录的时候权限有问题(普通用户或测试会显示全部菜单,有些列表里面没数据)
-
折叠完菜单做个图表setResize
webpack很多配置 再听一下 把一些方便的功能加上项目,比如打包项目之前删除旧的打包,serve之后自动打开页面
代码压缩相关
启用gzip压缩
webpack3中,可以使用UglifyJS压缩代码,但是它是单线程的,因此可以使用webpack-parallel-uglify-plugin来运行UglifyJS,但在webpack4中只要启动了mode为production就默认开启了该配置
压缩html和css代码,通过配置删除console.log和debugger等,防止可能造成的内存泄漏
本文档详述了一个基于Vue3、ElementPlus和TypeScript的前端项目搭建过程,包括代码规范、项目结构、axios拦截器、loading组件封装、UI组件库按需引入、登录页面的实现细节,如表单验证、记住密码功能、登录逻辑等。同时介绍了如何使用Echarts、处理跨域问题以及Vuex状态管理,确保登录数据持久化。

4197

被折叠的 条评论
为什么被折叠?



