记录一下我在做公司后台管理系统的难点与心得
公司的项目大致就长这个样子刚进入页面的时候是两个请求,左侧的内容跟右侧的内容分别是两个不同的请求,这里我使用的是aioxs的并发请求,axios.all()axios提供了两个并发请求 axios.all(iterable)axios.spread(callback) 两个辅助函数用于处理同时发送多个请求,可以实现在多个请求都完成后再执行一些逻辑。因为我的想法是在两个请求都结束以后再关闭loadi
公司的项目大致就长这个样子
刚进入页面的时候是两个请求,左侧的内容跟右侧的内容分别是两个不同的请求,这里我使用的是aioxs的并发请求,axios.all()
axios提供了两个并发请求 axios.all(iterable) axios.spread(callback) 两个辅助函数用于处理同时发送多个请求,可以实现在多个请求都完成后再执行一些逻辑。
因为我的想法是在两个请求都结束以后再关闭loading,显示出来,否在会出现白屏的情况,
我这里是这样使用的
async getOperatorinfo () {
const res = await axios.all([this.$api.vipManage.operatorinfo({
params: {
oiid: this.$local.getList('userdata').data.oiid
}
}), this.$api.vipManage.dashboard({
params: {
type1: this.$store.state.Service.dictions,
oiid: this.$local.getList('userdata').data.oiid,
spid: this.$local.getList('userdata').spid,
days: this.value
},
showLoading: true
})
])
// 一系列处理....
})
异步请求,返回的是一个数组,数组中第一项是第一个请求的值,第二个是第二个请求的。
整个管理系统使用的是element的标签页,使用keep-alive进行缓存,使用component来显示组件。所有的标签页都以组件的形式显示
component是一个占位符,:is属性可以用来指定要展示的组件名称 加v-if是因为我在写的过程中出现的问题,如果不加v-if的话,当我开了多个会员的详情,或者服务的详情情况下,刷新以后,所有的会员详情以及服务详情全部变成同一个人的了,加个v-if进行判断,会从新加载。每次点击以后都会从新请求
<el-tabs v-model="editableTabsValue"
type="card"
:closable='isClosable'
@tab-remove="removeTab"
@tab-click="handleClick"
:lazy='true'>
<el-tab-pane v-for="item in editableTabs"
:key="item.name"
:label="item.title"
:name="checkName(item)">
<keep-alive>
<component :is="item.view"
v-if="item.flag"></component>
</keep-alive>
</el-tab-pane>
</el-tabs>
并且在做这个页面的时候,需要有的页面一次开多个,有的页面只能开一个,二级菜单都是只能打开一个,当前二级菜单下的所有三级标签,都是可以开多个的存在。所以在这里我使用了多层判断,
- 第一,判断是不是二级菜单,二级菜单跟三级标签页唯一的差别就是没有少一个id参数,(会员id或者服务id之类的),新增服务除外,因为他有两种可能
- 第二,如果不是二级菜单,那么那么就要判断它不是新增服务页面,并且他的id唯一的时候,这个时候就添加一个标签页,随后还要在进行判断,判断传进来的值中当id或者name其中一个不唯一的时候,比如我打开了一个会员的不同服务,(因为我有个服务前跟服务后。)这个时候如果还按唯一来判断就出只显示一个。在这个时候我还要在判断是不是新增服务页面,如果是的话,那就说明之前以及存在了一个新增服务页面,这个时候就是需要使用findindex获取name为新增服务的返回的值(i),将标签数组中的下标为i的值进行替换。如果不替换的话就会出现两个新增服务,如果不是新增服务,那么就是使用findindex获取id为当前传过来的值所返回的值(a),替换数组中下标为a的数。
- 第三,判断是否为新增服务,是的话就添加数组中进去。这个时候经过了第一步判断是不是二级菜单,万一是含有id的新增服务。那么就会到第二个判断,在判断name是不存在的,这个时候就到了第三个,添加了新增服务的页面。如果新增页面以及存在,那么就会在第二个判断那里进行替换。
- 第四,判断是否为二级菜单,如果是那么就直接添加到标签数组中
- 第五,因为刚进入页面的时候进入的是欢迎页这个页面只有在第一次进入页面的时候才有,当我单击左侧任何的标签的时候,他都会消失。
if (this.editableTabs[0].title === '欢迎页') { this.editableTabs.splice(0, 1) }
- 第六,当我每次单击标签或者左侧二级标签的时候都要循环加判断,将当前点击的标签的flag值改为true,否则整个页面为空白。
for (var i = 0; i < this.editableTabs.length; i++) {
if (tab.label === this.editableTabs[i].title) {
this.editableTabs[i].flag = true
}
}
- 第七,在第一次进入页面的时候,也进行了循环判断,除了欢迎页其余所有的页面flag全部设置为false,不进行加载,否则,在第一次进入页面的时候,假如有五个页面(刷新后),那么这五个页面的请求都会进行。
- 第八,进行点击的监听点击值的变化,将不是当前显示的页面的flag值全部设置为false
handler (newVal, oldName) {
_.forEach(this.editableTabs, (item) => {
if (item.miid === newVal.split('-')[1]) {
item.flag = true
} else if (newVal === item.name) {
item.flag = true
} else {
item.flag = false
}
})
}
在这个项目中我使用了一个空的Vue实例作为中央事件总线使用bus.$ emit跟bus.$ on在不同组件之间进行传值,
// utils.js
import Vue from 'vue'
// 使用一个空的 Vue 实例作为事件总线
export default new Vue()
.在main.js中注册全局的bus
// main.js
import event from './utils'
// 把 event-bus 注册到全局
Vue.prototype.$bus = event
传:
this.$bus.$emit('removeTab', '2-2')
// 或者
this.$bus.$emit('Tab')
收:
this.$bus.$on('removeTab', params => {
console.log(params)
})
// 或者
this.$bus.$on('removeTab', () => {
})
本项目用到了websocket连接,因为在热疗界面,
这个是之前的bug截图在这个页面使用到了websocket连接。
mounted () {
this.initWebSocket()
},
methods:{
initWebSocket () {
// 创建一个构造函数返回一个websocket对象
const wsurl = `ws://${this.url}${this.types}${this.token}?deviceid=${this.deviceid}`
this.websock = new WebSocket(wsurl)
// 接受到信息后的回调函数
this.websock.onmessage = this.websocketonmessage
// 连接成功后的回调函数
this.websock.onopen = this.websocketonopen
// 连接失败后的回调函数
this.websock.onerror = this.websocketonerror
// 用于指定关闭后的回调函数
this.websock.onclose = this.websocketclose
},
// 连接建立失败重连
websocketonerror () {
this.initWebSocket()
console.log('重连中...')
},
// 数据接收
websocketonmessage (e) {
console.log(e)
...
// 处理业务逻辑
}
// 数据发送
websocketnd (Data) {
this.websock.send(Data)
console.log(Data, '已经发送数据')
},
// 关闭
websocketclose (e) {
console.log(e, '连接已经断开')
},
}
为什么会选用websocket连接呢,因为之前的选用是axios轮询,因为每分钟都需要最新的数据,而心跳脉搏却是每秒都要更新的,
缺点:
-
脉搏需要每秒进行请求,温度是每分钟请求。
-
对服务器造成的压力比较大,耗费资源
-
请求太多太频繁,如果是访问量比较大的网站,就会造成压力了
-
会有延迟,数据的实时性不高
-
并不是数据刚更新就能拿到并更新的,需要请求正好能拿到数据
-
数据看起来可能会有紊乱,同一时间你看到的数据和别人的不一样
后来决定还是使用websocket连接让客户端与服务端建立长连接,它会等待接收服务端的信息,当收到信息后会自动触发其绑定的函数,也就是服务端主动推送信息
优点:
- 解决了同步有延迟的问题
- 解决服务器上消耗资源的问题
- 由于Websocket只需要一次HTTP握手,所以说整个通讯过程是建立在一次连接/状态中,也就避免了HTTP的非状态性,服务端会一直知道你的信息,直到你关闭请求
还有这个大数据展示,全部都是echarts图表
我的写法是所有的echarts图表都使用同一个组件,通过父子组件之前传值,将传过来的值赋予不同的方法。
<!-- 父组件 -->
<template>
<serviceDataV :data="dataModel"
v-if='serviceDatav'
ref="RightLine"></serviceDataV>
<serviceDataV :data="dataModel1"
v-if='serviceDatav'
ref="RightLine1"></serviceDataV>
<serviceDataV :data="dataModel2"
v-if='serviceDatav'
ref="RightLine2"></serviceDataV>
</template>
<script>
import serviceDataV from 'com/serviceDatav/EchartsDatav.vue'
export default {
components: {
serviceDataV
},
data(){
return{
dataModel: {
// 调色盘颜色列表
color: ['#C23D3E'],
// 直角坐标系内绘图网格
grid: {
// 距离上下左右的距离
left: '6%',
right: '10%',
top: '35%',
bottom: '4%',
containLabel: true
},
tooltip: {
trigger: 'axis',
formatter: '热疗人数{c},机器{b}台'
},
// 用于区域缩放
dataZoom: {
show: false,
// 数据窗口范围的起始百分比。
start: 0,
// 数据窗口范围的结束百分比
end: 100
},
// 直角坐标系 grid 中的 x 轴
xAxis: [
{
type: 'category',
name: '台',
// boundaryGap值为true的时候,刻度作为分隔线,标签和数据点都会在两个刻度之间的地带
boundaryGap: true,
// data: this.testXaxis
// 设置类目数据
data: [10, 20, 30, 40, 14, 50, 35],
// 坐标轴在 grid 区域中的分隔线。表示是否显示分隔线。当值为false的时候,默认数值轴不显示,类目轴显示。
splitLine: {
show: false
},
nameTextStyle: {
color: '#ffffff'
},
axisLabel: {
color: '#ffffff'
},
// 坐标轴的相关设置,lineStyle属性表示坐标轴的颜色。
axisLine: {
// 设置x轴颜色
lineStyle: {
color: '#31333a'
}
}
}
],
// 直角坐标系 grid 中的 y 轴
yAxis: [
{
type: 'value',
// 数值(单位)
name: '人数',
// 设置y轴刻度的最小值
// min: 100,
// // 设置y轴刻度的最大值
// max: 600,
// // 设置y轴刻度间隔个数
// interval: 100,
// 坐标轴在 grid 区域中的分隔线。表示是否显示分隔线。当值为false的时候,默认数值轴不显示,类目轴显示。
splitLine: {
show: false
},
// 坐标轴的相关设置,lineStyle属性表示坐标轴的颜色。
axisLine: {
show: true,
// 设置y轴颜色
lineStyle: {
color: '#31333a'
}
},
axisLabel: {
color: '#7f8b96'
},
nameTextStyle: {
color: '#7f8b96'
}
}
],
// 系列列表。每个系列通过 type 决定自己的图表类型
series: [
{
// 系列名称
// line折线图
type: 'bar',
// 是否平滑曲线显示。取值范围 0 到 1。true 时相当于设为 0.5。
smooth: false,
data: [300, 200, 300, 200, 400],
showSymbol: false,
barWidth: 10,
itemStyle: {
normal: {
color: function (params) {
var colorListr = [
'#367afc',
'#3bc5ff',
'#ffa623',
'#f53059',
'#fe2ed9',
'#a3f6ff',
'#edd36d'
]
return colorListr[params.dataIndex]
},
label: {
show: true,
position: 'insideBottom',
rotate: -20,
formatter: function (params) {
var value = []
return value[params.dataIndex].split('').join('\n')
},
color: '#ffffff',
fontSize: 12
}
}
}
}
]
},
dataModel1:{
xxxxx
},
dataModel2:{
xxxxx
}
}
}
methods: {
async DataV () {
// .....页面处理逻辑
if (this.$refs.RightLine.drawGraph) {
this.$refs.RightLine.drawGraph()
}
if (this.$refs.RightLine1.drawGraph1) {
this.$refs.RightLine1.drawGraph1()
}
if (this.$refs.RightLine2.drawGraph2) {
this.$refs.RightLine2.drawGraph2()
}
}
}
}
</script>
<!-- 子组件 -->
<template>
<div ref="echarts"></div>
</template>
<script>
import echarts from 'echarts'
import _ from 'lodash'
// 词云图
import 'echarts-wordcloud'
// 中国地图
import '../../../node_modules/echarts/map/js/china.js'
export default {
props: ['data'],
data () {
return {
chart: '',
}
},
methods: {
drawGraph () {
this.chart = echarts.init(this.$refs.echarts)
this.chart.setOption(this.data)
window.addEventListener('resize', () => {
this.$_handleResizeChart()
})
this.$once('hook:beforeDestroy', () => {
window.removeEventListener('resize', this.$_handleResizeChart())
})
},
drawGraph1 () {
this.chart = echarts.init(this.$refs.echarts)
this.chart.setOption(this.data)
window.addEventListener('resize', () => {
this.$_handleResizeChart()
})
this.$once('hook:beforeDestroy', () => {
window.removeEventListener('resize', this.$_handleResizeChart())
})
},
drawGraph2 () {
this.chart = echarts.init(this.$refs.echarts)
this.chart.setOption(this.data)
window.addEventListener('resize', () => {
this.$_handleResizeChart()
})
this.$once('hook:beforeDestroy', () => {
window.removeEventListener('resize', this.$_handleResizeChart())
})
},
$_handleResizeChart () {
this.chart.resize()
}
}
}
</script>
更多推荐