Note

技术复盘

毁灭性bug

  1. 随便下载库

随便下载库

https://github.com/WAGFS/vue-zxing

下载了没有人用的库,因为代码互相依赖,依赖版本不统一,导致了项目打开后显现不出来任何内容。接着我使用npm fix audit语句尝试修复,结果仍无法显现任何内容,之后一时心急使用npm fix audit —force修复,导致项目直接崩溃,无法启动。

功能介绍和业务细节

  1. 模板的选择
  2. 模板的初始化
  3. 模板的项目结构分析
  4. 登录(注册)
  5. 面包屑导航
  6. 图表的使用
  7. element自定义样式修改
  8. 样式scope问题
  9. 上传
  10. 下载(导出)
  11. 增删查改
  12. 多权限路由
  13. 分页逻辑

模板文档

模板预览

推荐12 个超棒的开源后台管理系统(强烈推荐收藏)

模板的选择

后台管理系统很多,这里选择了基于vue2的vue-element-admin

模板的初始化

模板文档中:

建议

你可以把 vue-element-admin当做工具箱或者集成方案仓库,在 vue-admin-template 的基础上进行二次开发,想要什么功能或者组件就去 vue-element-admin 那里复制过来。

但我个人开发的感受是还是直接使用vue-element-admin更加方便,删比增简单。有时因为要引用一个功能,需要整个项目找他的入口和引用。

两个例子:

  1. 在组件error-log中,引用的资源有:

    image-20240303142837011

    处理起来比较困难,大致的逻辑是在设置settings.js中确定errorlog的使用环境,在error-log.js中判断是否需要使用……

  2. 多权限路由中,也有很多关于roles判断的地方

模板初始化成功后,可以运行项目和code业务代码

模板的项目结构分析

Vue-element-admin之目录结构及相关链接

登录(注册)

在上两个项目中,登录是比较困扰我的业务逻辑,主要改动的地方有:

api/login.js 中重写登录接口

1
2
3
4
5
// url: '/vue-admin-template/user/info',
url: '/user/info',
method: 'get',
params: { token }
})

src/main.js 中取消mock服务

src/utils/request.js 中重写登录逻辑和token名

1
2
3
4
5
// config.headers['X-Token'] = getToken()
config.headers['Authorization'] = getToken()

// if (res.code !== 20000) {
if (res.code !== 200) {

vue.config.js 中取消mock服务,新增跨域设置

1
2
3
4
5
6
7
8
9
10
 proxy: { // 配置反向代理
[process.env.VUE_APP_BASE_API]: { // "/api"自定义的请求路径地址
target: 'your-service.com:port',
changeOrigin: true, // 是否进行跨域 ture是 false不是
pathRewrite: { // 路径重写
['^' + process.env.VUE_APP_BASE_API]: '/'
},
}
},
// before: require('./mock/mock-server.js')

.env.development,.env.production 中配置域名

1
2
# VUE_APP_BASE_API = '/dev-api'
VUE_APP_BASE_API = 'your-service.com:port'

src/utils/auth.js 中重写tokenkey

1
2
// const TokenKey = 'vue_admin_template_token'
const TokenKey = 'Authorization'

还有自己处理一下response的data,有的请求不用解构

面包屑导航

image-20240303145647192

image-20240303150038689

我记得出现过一个bug,全部的dashboard字样重新设置会显示第一张图,具体的问题我还没有搞明白。

图表的使用

用echart和模板自带的图表组件就好了

element自定义样式修改

src\styles\element-ui.scss 下重新设置,样式的类名去查html元素

样式scope问题

image-20240303150633155

在表格中,如果需要根据某一条的状态设置特殊样式,此时在element-ui.scss全局设置样式是不可行的,因此需要单独设置特殊样式

1
2
3
4
5
6
7
8
9
10
11
12
13
// 判断状态的逻辑写到这里面,这个是上色
statusClassName({ row, column, rowIndex, columnIndex }) {
if (row.deleted === 1 && column.label === '笔记状态') {
return 'deleted'
}
if (row.shielded === 1 && column.label === '笔记状态') {
return 'shielded'
}
if (row.star === 1 && column.label === '笔记状态') {
return 'star'
}
return 'normal'
},

然后需要style设置非scoped,才能穿透样式,为了防止样式穿透影响其他元素,应当设置一个特殊的container装载这些样式

1
2
3
4
5
6
7
8
9
10
11
12
13
<style lang="scss">
.very-special-container {
.deleted {
background: #e2a0a9 !important;
}
.shielded {
background: #bbb4b4 !important;
}
.star {
background: rgb(255, 242, 213) !important;
}
}
</style>

上传

一般都用自定义上传,el-upload配置

两个项目中涉及了:

  1. 自定义上传规则
  2. 最多允许一张图片的上传
  3. 必须有一张图片的上传
  4. 至少上传一张,最多允许九张图片的上传
自定义上传规则
1
2
3
4
5
6
7
8
9
10
11
12
13
<el-upload 
class="upload-demo"
ref="upload"
action="#"
drag
accept=".jpg,.jpeg,.png,.bmp"
:auto-upload="false"
:limit="1"
:on-change="changeFileLength"
:http-request="uploadRequest"
list-type="picture">
<div slot="tip" class="el-upload__tip">图片推荐尺寸长宽比为3:1</div>
</el-upload>

然后在下面js中写上传逻辑,需要注意的点其实是怎么验证(图片一定要上传时)

最多允许一张图片的上传

设置limit属性

必须有一张图片的上传
1
2
3
4
5
6
7
8
9
if (this.dynamicValidateForm.urlList.length === 0) {
if (this.filesLength === 0) {
this.$message({
message: '没有上传图片',
type: 'info'
})
return
}
}
至少有一张图片,最多有九张图片的上传

前二者的结合

下载(导出)

根据vue-element-admin改写,添加一下特殊的配置项即可

性能考虑,需要分包引入:

1
2
3
4
5
6
7
8
9
handleDownload() {
this.downloadLoading = true
// 分包引入
import('@/vendor/Export2Excel').then(excel => {
// const data = filterVal
// your downloading method
this.downloadLoading = false
})
},

处理配置项

1
2
3
4
5
6
7
8
9
10
11
12
// multiHeader:一级表头
// tHeader:二级表头
// filterVal:匹配的字段,与tHeader一一对应
// list:获得原始数据
// data = formaJson:处理原始数据
// merges:一级表头和二级表头的合并
const multiHeader = [['笔记ID', '笔记信息', '', '', '', '', '', '', '', '笔记内容', '笔记热度', '', '笔记数据', '', '', '', '运营信息', '', '', '', '笔记状态', '', '']]
const tHeader = ['', '笔记标题', '笔记图片/视频地址', '笔记链接', '标签', '作者小红书昵称', '笔记发布时间', 'IP', '作者小红书号', '', '级别', '火爆程度', '点赞数', '评论数', '收藏数', '分享数', '组名', '微信群名', '作者微信昵称', '作者真实姓名', '是否屏蔽', '是否删除', '是否收藏']
const filterVal = ['id', 'title', 'picture', 'url', 'tags', 'bookletUserName', 'publishTime', 'ip', 'redBookletNumber', 'description', 'degree', 'hotDegree', 'likes', 'comments', 'bookmarks', 'shares', 'groupName', 'wxGroupName', 'wxName', 'authorName', 'shielded', 'deleted', 'star']
const list = this.list
const data = this.formatJson(filterVal, list)
const merges = ['A1:A2', 'B1:I1', 'J1:J2', 'K1:L1', 'M1:P1', 'Q1:T1', 'U1:W1']

调用函数

1
2
3
4
5
6
7
8
9
import('@/vendor/Export2Excel').then(excel => {
excel.export_json_to_excel({
multiHeader,
header: tHeader,
merges,
data,
filename: '笔记查询结果'
})
})

增删查改

多权限路由

见chuang-system的写法

分页逻辑

当时写分页的时候比较乱,现在可以总结一下分页。

Elasticsearch分页的三种方式:from+size、scroll、search_after

  1. 前端分页
  2. 服务器分页
    1. form + size
    2. scroll
前端分页

后台返回所有数据,前端进行分页,维护list和show_list

服务器分页——form + size

前端搜索体验最好,但只能用于后台小数据集

前端维护total,currentPage,pageSize、后台返回的list即可

服务器分页——scroll

维护scollIdArray,page,total即可,total由后台返回。

page的变化由prevClick和nextClick决定

scollIdArray,更新于后台返回数据,点击prevClick和nextClick,搜索条件发生变化时

在后台返回数据时:

1
this.scollIdArray[this.page - 1] = response.data.scrollId

点击prevClick和nextClick时:

1
2
3
4
5
6
7
8
9
if (event === 'prev') {
data.afterId = this.scollIdArray[this.page - 3]
this.page--
}
if (event === 'next') {
data.afterId = this.scollIdArray[this.page - 1]
this.page++
}

搜索条件发生变化时,清空

一些bug的处理思路

首先一点,前端没多少很难的东西。很多问题是后台给你制造的,揪出对接后台问题。

  1. 空数据(数据为null)问题
  2. element组件dialogdata读取问题
  3. element-dialog和echart不太兼容的问题
空数据(数据为null)问题

后台传来null时(往往是爬虫没有给后台数据,后台只建了表但是没有数据),很多代码会跑出异常,需要增强健壮性。

在使用后台数据进行操作之前,需要判断后台数据是否为null,然后进行处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
watch: {
'list': {
deep: true,
handler(newList) {
// 遍历数组中的对象
newList.forEach((obj, index) => {
Object.keys(obj).forEach(key => {
if (key === 'tags' && obj[key] === null) {
obj[key] = ['未知']
}
if (key === 'degree' && obj[key] === null) {

obj[key] = 1
}
if (key === 'publishTime' && obj[key] === null) {
obj[key] = 'XXXX-XX-XX XX:XX:XX'
}
if (obj[key] === null) {

obj[key] = '未知'
}
})
})
}
}
},
element组件dialogdata读取问题

业务上,有一个dialog需要点开之后再请求后台然后渲染数据,但是element-dialog是当即渲染而不是点开之后再去获得,因此需要默认的设置

1
return this.list && this.list.length > 0 ? this.list[this.showIndex] : {}
element-dialog和echart不太兼容的问题

echart中很喜欢使用width:100%,但是dialog默认的width是自动计算的,因此会导致dialog非常小,echart不能正常显示的问题。

需要将dialog设置宽度,如果仍不行需要重新启动服务,因为有时候echart在我的代码里是只初始化一次,且dialog在页面加载的时候就会自动加载。

如果图表仍不正常显示,考虑是前者数据读取的问题

登录问题

当时卡了我很久,登录的思路是:取消mock服务,业务逻辑:配置域名,配置代理,调用自己的接口,返回数据处理。

参考资料

Vue-element-admin之目录结构及相关链接

https://github.com/WAGFS/vue-zxing

模板文档

模板预览

推荐12 个超棒的开源后台管理系统(强烈推荐收藏)

Elasticsearch分页的三种方式:from+size、scroll、search_after

手摸手,带你用vue撸后台 系列一(基础篇)