前言
大半年的时间,项目从秋天到春天,从管理后台到APP再到数据大屏,技术栈从vue3到uniApp再到nuxt3,需求不停的改,注释掉代码都快到项目总体的三分之一。
一,项目技术栈分析
1.1 项目框架
当前,我正参与的项目框架采用uniApp Vue2,这是一个建立在vue.js之上的前端框架,它使开发者能够通过编写一套代码即可兼容多个平台。在项目的初期阶段,主要涉及一些基础页面的复现,这时候框架没有显著的不足之处。然而,随着项目的深入,特别是在需要引入更多原生功能的阶段,uniApp Vue的局限性开始显现,存在许多不支持的功能。如果此时你不熟悉如何在vue中引入nvue文件,你可能就会像我一般,不得不求助于HTML5+的API来实现对安卓原生能力的调用。基于我的有限经验,如果你的项目需求涉及到较多的原生功能,我建议采用uniApp Nvue来构建你的项目,它更加适合这类需求。
1.2 UI组件库
项目使用UI组件库是 uView
多平台快速开发的UI框架
uView UI,是全面兼容nvue的uni-app生态框架,全面的组件和便捷的工具会让您信手拈来,如鱼得水
1.3 可视化库
项目使用的可视化库是 秋云
uCharts是一款基于canvas API开发的适用于所有前端应用的图表库,开发者编写一套代码,可运行到 Web、iOS、Android(基于 uni-app / taro )、以及各种小程序(微信/支付宝/百度/头条/飞书/QQ/快手/钉钉/淘宝/京东/360)、快应用等更多支持 canvas API 的平台。
二,一些问题的解决和API的使用
2.1 如何请求两个不同的后端服务器
拦截器: 项目使用uView封装好的拦截器
请求拦截器通过uni.$u.http.interceptors.request.use
实现,可以进行如下配置
uni.$u.http.interceptors.request.use((config) => { // 可使用async await 做异步操作
config.header = {
...config.header,
a: 1 // 演示拦截器header加参
}
return config
}, config => { // 可使用async await 做异步操作
return Promise.reject(config)
})
响应拦截器则通过uni.$u.http.interceptors.response.use
进行配置,如下所示:
uni.$u.http.interceptors.response.use((response) => {
console.log(response)
return response
}, (response) => {
/* 对响应错误做点什么 (statusCode !== 200)*/
console.log(response)
return Promise.reject(response)
})
在本项目中,由于整合了第三方技术解决方案,需要前端在进行请求时对不同的API地址引导至不同的后端服务器处理。解决方法如下,在请求拦截器中,根据请求地址匹配相应的代理服务器地址以实现请求的分流处理。
if (config.url.includes('/api/') ) {
consloe.log("111")
config.baseURL = service; // 设置代理服务器1的地址
} else {
console.log("222")
config.baseURL = service1; // 设置代理服务器2的地址
// 根据custom参数中配置的是否需要token,添加对应的请求头
if (config?.custom?.auth) {
if (!getToLocalSync('token')) { // 如果token不存在,return Promise.reject(config) 会取消本次请求
return Promise.reject(config)
}
config.header['Authorization-Token'] = getToLocalSync('token')
}
}
此方法使得前端能够灵活地控制请求分派至合适的后端服务,从而确保数据处理的正确归属和流畅的服务体验。
2.2 ucharts引入及使用
ucharts的官方文档中提供了两种引入方式:npm i @qiun/ucharts
和组件引入。在本项目中,我采用的是第二种组件引入方法,在uniApp插件市场下载并导入ucharts到项目中uni_modules文件夹下,完成以后的目录结构如下:
完成上述步骤以后,就可以直接在项目中使用ucharts了,使用示例:
<view class="content-f3" v-if="qiunShow">
<qiun-data-charts type="line" :opts="opts" :chartData="chartData" :ontouch="true" />
</view>
其中type
表示图表类型,这里使用的是折线图(line),opts
图表配置,与Echarts的option相似,在这里因为手机屏幕比较窄,如果是竖屏展示图表的话,建议在xAxis的配置中开启滚动scrollShow: true
,chartData
就是图表的数据源。
2.3 调用相机能力
2.3.1 uni.chooseImage
从本地相册选择图片或使用相机拍照。 官方文档
2.3.2 plus.camera
Camera模块管理设备的摄像头,可用于拍照、摄像操作,通过plus.camera获取摄像头管理对象。
getCamera() {
let _that = this;
camera = plus.camera.getCamera(); // 获取摄像头管理对象
camera.captureImage(
function(p) {
plus.io.resolveLocalFileSystemURL(
p,
function(entry) {
// 获取到摄像头返回数据
consle.log(entry.toLocalURL())
},
function(e) {
plus.nativeUI.toast('读取拍照文件错误:' + e.message);
}
);
}
);
},
2.4 扫码
在本项目中集成扫码功能,用户可以通过扫一扫即刻获取信息,大大提高交互的便捷性,减少填写的操作。
2.4.1 uni.scanCode
调起客户端扫码界面,扫码成功后返回对应的结果。
uniApp的官方API,功能比较完善,但是不能自定义扫码界面。官方推荐使用H5 Plus的API:plus.barcode
2.4.2 plus.barcode
Barcode模块管理条码(一维码和二维码)扫描识别,支持常见的一维码(如EAN13码)及二维码(如QR码)。通过调用设备的摄像头对条码进行扫描识别,扫描到条码后进行解码并返回码数据内容及码类型。
Barcode模块可使得Web开发人员能快速方便调用设备的摄像头进行条码扫描识别,而不需要安装额外的扫描插件。规范建议条码识别引擎的支持规范定义的所有条码常量类型。
plus.barcode.create
用于创建扫码识别控件对象;[plus.barcode.QR, ...]
这里表示用于识别什么类型的码,QR
表示用于识别二维码。
barcode = plus.barcode.create('barcode', [plus.barcode.QR, plus.barcode.EAN13, plus.barcode.EAN8, plus.barcode
.CODE128
], {
top: '0',
left: '0',
width: '100%',
height: '100%',
scanbarColor: '#46B81E',
position: 'static',
frameColor: '#46B81E',
display: 'flex',
justifyContent: 'center',
alignItems: 'center'
});
2.5 海康SDK使用
在接入海康摄像头视频的过程中,我遇到了多个阶段的挑战。第一个阶段,依照海康提供的文档,我成功通过其接口获取了视频流,并试图在我们的系统中进行播放。然而,这里涉及到两种视频协议:RTSP协议
和RTMP协议
,这两者都不能直接在我们的系统上播放。为了解决这一问题,我们首先考虑的方案是在服务器端搭建一个转码服务,将RTSP和RTMP视频流转换为FLV
格式,随后利用Fly.js
库来在系统中播放视频。这样的处理方式,会导致视频会存在延时,并且对web服务器造成极大的压力。在项目的第二阶段,我们寻求了海康的技术支持,并采用了官方提供的SDK包。根据官方开发指南,我们较为轻松地实现了视频在系统中的集成。然而,在App打包发布后,我们遇到了视频无法正常播放的问题。经过仔细排查,我们发现问题源于SDK仅支持Android浏览器的限制,我们采取了将视频播放功能转移到一个单独的网页应用的解决办法,并通过iframe及webView技术将视频嵌入到系统中,以确保视频能在不同环境下顺畅播放。
插件推荐
海康视频H5播放器组件
2.6 webView
后续单独更新一篇文章
2.7 组件递归调用
用于展示项目中的树形数据,在组件中合适的节点调用组件本身。下面是一个简单的示例:
<!--treeNode.vue-->
<template>
<li>
{{ node.name }}
<ul v-if="node.children && node.children.length">
<tree-node
v-for="(child, index) in node.children"
:key="index"
:node="child">
</tree-node>
</ul>
</li>
</template>
2.8 APP发布及更新
后续单独更新一篇文章