
H5+ zebra打印机(ZPL指令)打印标签
公司新增一台打印机需求,与之前的打印机指令不一致,需要用到斑马的ZPL指令
之前的打印机由前同事开发,用到的是JPL的打印机,封装的jar包
本次参考了网上的教程,结合之前打印机获取蓝牙设备
可以直接打印结果
打印机:Zebra 斑马打印机 ZD421
手机:PDA - C500 (安卓12)
功能:扫描周围蓝牙设备加入列表,点击未配对设备,自动配对设备,点击已配对设备,进行打印测试
先上html
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>打印测试</title>
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
<link href="../../css/mui.min.css" rel="stylesheet" />
<link rel="stylesheet" type="text/css" href="../../css/GLBcustom.css" />
<link rel="stylesheet" type="text/css" href="../../css/iconfont-download.css" />
<link rel="stylesheet" type="text/css" href="../../css/icons-extra.css" />
<style type="text/css">
#scroll1 {
position: absolute;
z-index: 2;
bottom: 44px;
left: 0;
overflow: hidden;
width: 100%;
}
#scroll2 {
position: absolute;
z-index: 2;
bottom: 44px;
left: 0;
overflow: hidden;
width: 100%;
}
#scroll3 {
position: absolute;
z-index: 2;
bottom: 44px;
left: 0;
overflow: hidden;
width: 100%;
}
/*扫描记录 单独定制样式,不需要分组*/
/* .mui-btn-green, .mui-btn-positive, .mui-btn-success {
border: 1px solid #4cd964 !important;
padding: 2px;
}*/
.mui-popup {
position: fixed;
z-index: 10000;
top: 50%;
left: 50%;
display: none;
overflow: hidden;
width: 80%;
-webkit-transition-property: -webkit-transform,opacity;
transition-property: transform,opacity;
-webkit-transform: translate3d(-50%,-50%,0) scale(1.185);
transform: translate3d(-50%,-50%,0) scale(1.185);
text-align: left;
opacity: 0;
color: #000000;
border-radius: 13px;
}
.mui-popup-inner {
position: relative;
padding:15px;
border-radius: 13px 13px 0 0;
background: rgba(255,255,255,1);
}
.mui-popup-title {
font-size: 15px;
font-weight: 500;
text-align: center;
}
.mui-popup-title+.mui-popup-text {
font-family: inherit;
font-size: 14px;
margin: 5px 0 0;
}
.mui-popup-buttons {
position: relative;
display: -webkit-box;
display: -webkit-flex;
display: flex;
height: 30px;
-webkit-box-pack: center;
-webkit-justify-content: center;
justify-content: center;
}
.mui-popup-button {
font-size: 15px;
line-height: 30px;
position: relative;
display: block;
overflow: hidden;
box-sizing: border-box;
width: 100%;
height: 30px;
padding: 0 5px;
cursor: pointer;
text-align: center;
white-space: nowrap;
text-overflow: ellipsis;
color: #000000;
/*background: rgba(49,112,143,1);*/
background: rgba(255,255,255,1);
-webkit-box-flex: 1;
}
.mui-popup-title{
background-color: transparent !important;
}
.mui-popup-text{
background-color: #FFFFFF;
min-height: 30px;
line-height: 30px;
}
.mui-table-view-cell-a{
padding-left: 10px !important;
}
</style>
</head>
<body>
<div id="bledevice" v-show="hide" class="mui-popup mui-popup-in" style="display: block;">
<div class="mui-popup-inner" style="height: 300px;">
<div class="mui-popup-title">
<span lang='zh-cn' class="i18n" data-textid="选择打印机">选择打印机</span>
</div>
<div class="mui-popup-text" style="position: relative;height: 259px;">
<div id="scroll6" class="mui-scroll-wrapper">
<div class="mui-scroll">
<div class="mui-popup-text">
<span lang='zh-cn' class="i18n" data-textid="已配对设备">已配对设备</span>
</div>
<div class="mui-popup-select">
<ul class="mui-table-view">
<li v-for="obj in bound" class="mui-table-view-cell">
<a class="mui-table-view-cell-a" @click="connect" v-bind:mac="obj.value" v-text="obj.text+' -- '+obj.value">
obj.text
</a>
</li>
</ul>
</div>
<div class="mui-popup-text">
<span lang='zh-cn' class="i18n" data-textid="未配对设备">未配对设备</span>
</div>
<div class="mui-popup-select">
<ul class="mui-table-view">
<li v-for="obj in unbound" class="mui-table-view-cell">
<a class="mui-table-view-cell-a" @click="pairconnect" v-bind:mac="obj.value" v-text="obj.text+' -- '+obj.value">
ss
</a>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
<div class="mui-popup-buttons">
<span class="mui-popup-button mui-popup-button-bold i18n" data-textid="重新扫描" @click="rescan">重新扫描</span>
<span class="mui-popup-button mui-popup-button-bold i18n" data-textid="关闭" @click="close">关闭</span>
</div>
</div>
<script type="text/javascript" src="../../js/i18n/zh_cn.js" ></script>
<script src="../../js/mui.min.js"></script>
<script src="../../js/mui.picker.js"></script>
<script type="text/javascript" src="../../js/jquery-3.2.1.min.js"></script>
<script type="text/javascript" src="../../js/vue.js"></script>
<script type="text/javascript" src="../../js/app.js"></script>
<script src="../../js/mui.poppicker.js"></script>
<script src="../../js/JPL.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript" src="js/print.js"></script>
<script type="text/javascript">
</script>
</body>
</html>
下面是js文件
mui.init({
swipeBack: false //启用右滑关闭功能
});
var menban = false;
var mask = mui.createMask(function() {
return menban;
}); //callback为用户点击蒙版时自动执行的回调;
//mask.show(); //显示遮罩
var hybird = true; //表示当前是混合开发状态,可以调用java,true,false状态表示是H5,不调用蓝牙
mui.plusReady(function() {
if(hybird){
plus.nativeUI.showWaiting("加载已绑定蓝牙设备");
//获取已绑定设备
var bounded = plus.JPLplugin.getBoundedBleDevice();
bluetooth.bound = bounded.bounded;
plus.nativeUI.closeWaiting();
}
});
var bluetooth = new Vue({
el: '#bledevice',
data: {
hide: true,
mac_address: '',
bound: [],
unbound: [],
},
methods: {
close: function() {
if(hybird){
if(this.mac_address == ''){
mui.toast('请选择一个蓝牙设备');
return;
}else{
this.hide = false;
menban = true;
mask.close();
}
}else{
this.hide = false;
menban = true;
mask.close();
}
},
rescan: function(e) {
//扫描未绑定设备
plus.nativeUI.showWaiting("正在搜索蓝牙设备");
var list = plus.JPLplugin.scanBleDevice();
this.unbound = list.unbonded;
plus.nativeUI.closeWaiting();
},
connect: function(e) {
var target = e.target;
console.log(target.getAttribute('mac'));
this.mac_address = target.getAttribute('mac');
//连接打印机
//plus.nativeUI.showWaiting('正在连接打印机');
plus.nativeUI.showWaiting(_DEVICE_PRINTER_);
setTimeout(function(){
if(!plus.JPLplugin.OpenPrinter(bluetooth.mac_address)) {
//mui.toast("打印机连接失败,请再试一次");
mui.toast(_DEVICE_AGAIN_);
plus.nativeUI.closeWaiting();
return;
}else{
//mui.toast("打印机连接成功");
mui.toast(_DEVICE_SUCCESS_);
plus.nativeUI.closeWaiting();
//document.getElementById('bledevice').style.display = "none";
bluetooth.hide = false;
menban = true;
mask.close();
//连接成功后,获取打印机状态
if(plus.JPLplugin.getPrinterState()){
print();
}
}
},200);
},
pairconnect: function(e) {
var target = e.target;
var flag = plus.JPLplugin.pairBle($(target).attr('mac'));
if(flag) {
this.mac_address = target.getAttribute('mac');
this.hide = false;
menban = true;
mask.close();
$('#search').focus();
} else {
//mui.toast("配对失败");
mui.toast(_DEVICE_FAIL_);
}
}
}
});
function print() {
if (!bluetooth.mac_address) {
mui.toast('请选择蓝牙打印机');
return;
}
main = plus.android.runtimeMainActivity();
BluetoothAdapter = plus.android.importClass("android.bluetooth.BluetoothAdapter");
UUID = plus.android.importClass("java.util.UUID");
uuid = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
BAdapter = BluetoothAdapter.getDefaultAdapter();
device = BAdapter.getRemoteDevice(bluetooth.mac_address);
plus.android.importClass(device);
bluetoothSocket = device.createInsecureRfcommSocketToServiceRecord(uuid);
plus.android.importClass(bluetoothSocket);
if (!bluetoothSocket.isConnected()) {
console.log('检测到设备未连接,尝试连接....');
bluetoothSocket.connect();
}
console.log(bluetoothSocket.isConnected());
if (bluetoothSocket.isConnected()) {
var outputStream = bluetoothSocket.getOutputStream();
plus.android.importClass(outputStream);
var string = "^XA" + //开始标签
"^JMA^LL1800^PW1000^MD10^PR2^PON^LRN^LH0,0" + "\r\n" + //毫米单位 宽
"^FO0,100" + "\r\n" + "^A0,N,50,50" + "\r\n" + "^FDDescription:^FS" + "\r\n" +
"^FO0,180" + "\r\n" + "^A0,N,50,50" + "\r\n" + "^FDGW 40V 2.0Ah 8-in. GEN II Polesaw w/ B&C^FS" + "\r\n" +
"^FO0,350" + "\r\n" + "^A0,N,50,50" + "\r\n" + "^FDITEM#: ^FS" + "\r\n" +
"^FO300,350" + "\r\n" + "^A0,N,50,50" + "\r\n" + "^FD1403702^FS" + "\r\n" +
"^FO00,410^BY3" + "\r\n" + "^BCN,100,Y,N,N" + "\r\n" + "^FD 1403702 ^FS" + "\r\n" +
"^FO0,600" + "\r\n" + "^A0,N,50,50" + "\r\n" + "^FDQTY: ^FS" + "\r\n" +
"^FO400,600" + "\r\n" + "^A0,N,50,50" + "\r\n" + "^FDPCS: ^FS" + "\r\n" +
"^FO00,700" + "\r\n" + "^A0,N,50,50" + "\r\n" + "^FDG.W: ^FS" + "\r\n" +
"^FO400,700" + "\r\n" + "^A0,N,50,50" + "\r\n" + "^FDKGS ^FS" + "\r\n" +
"^FO550,700" + "\r\n" + "^A0,N,50,50" + "\r\n" + "^FDN.W: ^FS" + "\r\n" +
"^FO910,700" + "\r\n" + "^A0,N,50,50" + "\r\n" + "^FDKGS ^FS" + "\r\n" +
"^FO00,800" + "\r\n" + "^A0,N,50,50" + "\r\n" + "^FDMade in China ^FS" + "\r\n" +
"^FO00,900" + "\r\n" + "^A0,N,50,50" + "\r\n" + "^FDPallet Code: PA1122061500000011^FS" + "\r\n" +
"^FO00,1000^BY3" + "\r\n" + "^BCN,200,Y,N,N" + "\r\n" + "^FD PA1122061500000011 ^FS" + "\r\n" +
"^XZ"; //横线
var bytes = plus.android.invoke(string, 'getBytes', 'UTF-8');
outputStream.write(bytes);
outputStream.flush();
device = null //这里关键
bluetoothSocket.close(); //必须关闭蓝牙连接否则意外断开的话打印错误
}
};
mui.back = function() {
var flag = false;
for(var key in item_one.records) {
flag = true;
break;
}
if(flag) {
var btnArray = [_NO_, _YES_];
mui.confirm(_WDDCH_QUIT_CONFIRM_, _NULL_TITLE_, btnArray, function(e) {
if(e.index == 1) { //退出
plus.webview.currentWebview().close();
} else {
//return false;
}
}, 'div');
} else {
plus.webview.currentWebview().close();
}
}
公司新增一台打印机需求,与之前的打印机指令不一致,需要用到斑马的ZPL指令
之前的打印机由前同事开发,用到的是JPL的打印机,封装的jar包
本次参考了网上的教程,结合之前打印机获取蓝牙设备
可以直接打印结果
打印机:Zebra 斑马打印机 ZD421
手机:PDA - C500 (安卓12)
功能:扫描周围蓝牙设备加入列表,点击未配对设备,自动配对设备,点击已配对设备,进行打印测试
先上html
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>打印测试</title>
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
<link href="../../css/mui.min.css" rel="stylesheet" />
<link rel="stylesheet" type="text/css" href="../../css/GLBcustom.css" />
<link rel="stylesheet" type="text/css" href="../../css/iconfont-download.css" />
<link rel="stylesheet" type="text/css" href="../../css/icons-extra.css" />
<style type="text/css">
#scroll1 {
position: absolute;
z-index: 2;
bottom: 44px;
left: 0;
overflow: hidden;
width: 100%;
}
#scroll2 {
position: absolute;
z-index: 2;
bottom: 44px;
left: 0;
overflow: hidden;
width: 100%;
}
#scroll3 {
position: absolute;
z-index: 2;
bottom: 44px;
left: 0;
overflow: hidden;
width: 100%;
}
/*扫描记录 单独定制样式,不需要分组*/
/* .mui-btn-green, .mui-btn-positive, .mui-btn-success {
border: 1px solid #4cd964 !important;
padding: 2px;
}*/
.mui-popup {
position: fixed;
z-index: 10000;
top: 50%;
left: 50%;
display: none;
overflow: hidden;
width: 80%;
-webkit-transition-property: -webkit-transform,opacity;
transition-property: transform,opacity;
-webkit-transform: translate3d(-50%,-50%,0) scale(1.185);
transform: translate3d(-50%,-50%,0) scale(1.185);
text-align: left;
opacity: 0;
color: #000000;
border-radius: 13px;
}
.mui-popup-inner {
position: relative;
padding:15px;
border-radius: 13px 13px 0 0;
background: rgba(255,255,255,1);
}
.mui-popup-title {
font-size: 15px;
font-weight: 500;
text-align: center;
}
.mui-popup-title+.mui-popup-text {
font-family: inherit;
font-size: 14px;
margin: 5px 0 0;
}
.mui-popup-buttons {
position: relative;
display: -webkit-box;
display: -webkit-flex;
display: flex;
height: 30px;
-webkit-box-pack: center;
-webkit-justify-content: center;
justify-content: center;
}
.mui-popup-button {
font-size: 15px;
line-height: 30px;
position: relative;
display: block;
overflow: hidden;
box-sizing: border-box;
width: 100%;
height: 30px;
padding: 0 5px;
cursor: pointer;
text-align: center;
white-space: nowrap;
text-overflow: ellipsis;
color: #000000;
/*background: rgba(49,112,143,1);*/
background: rgba(255,255,255,1);
-webkit-box-flex: 1;
}
.mui-popup-title{
background-color: transparent !important;
}
.mui-popup-text{
background-color: #FFFFFF;
min-height: 30px;
line-height: 30px;
}
.mui-table-view-cell-a{
padding-left: 10px !important;
}
</style>
</head>
<body>
<div id="bledevice" v-show="hide" class="mui-popup mui-popup-in" style="display: block;">
<div class="mui-popup-inner" style="height: 300px;">
<div class="mui-popup-title">
<span lang='zh-cn' class="i18n" data-textid="选择打印机">选择打印机</span>
</div>
<div class="mui-popup-text" style="position: relative;height: 259px;">
<div id="scroll6" class="mui-scroll-wrapper">
<div class="mui-scroll">
<div class="mui-popup-text">
<span lang='zh-cn' class="i18n" data-textid="已配对设备">已配对设备</span>
</div>
<div class="mui-popup-select">
<ul class="mui-table-view">
<li v-for="obj in bound" class="mui-table-view-cell">
<a class="mui-table-view-cell-a" @click="connect" v-bind:mac="obj.value" v-text="obj.text+' -- '+obj.value">
obj.text
</a>
</li>
</ul>
</div>
<div class="mui-popup-text">
<span lang='zh-cn' class="i18n" data-textid="未配对设备">未配对设备</span>
</div>
<div class="mui-popup-select">
<ul class="mui-table-view">
<li v-for="obj in unbound" class="mui-table-view-cell">
<a class="mui-table-view-cell-a" @click="pairconnect" v-bind:mac="obj.value" v-text="obj.text+' -- '+obj.value">
ss
</a>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
<div class="mui-popup-buttons">
<span class="mui-popup-button mui-popup-button-bold i18n" data-textid="重新扫描" @click="rescan">重新扫描</span>
<span class="mui-popup-button mui-popup-button-bold i18n" data-textid="关闭" @click="close">关闭</span>
</div>
</div>
<script type="text/javascript" src="../../js/i18n/zh_cn.js" ></script>
<script src="../../js/mui.min.js"></script>
<script src="../../js/mui.picker.js"></script>
<script type="text/javascript" src="../../js/jquery-3.2.1.min.js"></script>
<script type="text/javascript" src="../../js/vue.js"></script>
<script type="text/javascript" src="../../js/app.js"></script>
<script src="../../js/mui.poppicker.js"></script>
<script src="../../js/JPL.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript" src="js/print.js"></script>
<script type="text/javascript">
</script>
</body>
</html>
下面是js文件
mui.init({
swipeBack: false //启用右滑关闭功能
});
var menban = false;
var mask = mui.createMask(function() {
return menban;
}); //callback为用户点击蒙版时自动执行的回调;
//mask.show(); //显示遮罩
var hybird = true; //表示当前是混合开发状态,可以调用java,true,false状态表示是H5,不调用蓝牙
mui.plusReady(function() {
if(hybird){
plus.nativeUI.showWaiting("加载已绑定蓝牙设备");
//获取已绑定设备
var bounded = plus.JPLplugin.getBoundedBleDevice();
bluetooth.bound = bounded.bounded;
plus.nativeUI.closeWaiting();
}
});
var bluetooth = new Vue({
el: '#bledevice',
data: {
hide: true,
mac_address: '',
bound: [],
unbound: [],
},
methods: {
close: function() {
if(hybird){
if(this.mac_address == ''){
mui.toast('请选择一个蓝牙设备');
return;
}else{
this.hide = false;
menban = true;
mask.close();
}
}else{
this.hide = false;
menban = true;
mask.close();
}
},
rescan: function(e) {
//扫描未绑定设备
plus.nativeUI.showWaiting("正在搜索蓝牙设备");
var list = plus.JPLplugin.scanBleDevice();
this.unbound = list.unbonded;
plus.nativeUI.closeWaiting();
},
connect: function(e) {
var target = e.target;
console.log(target.getAttribute('mac'));
this.mac_address = target.getAttribute('mac');
//连接打印机
//plus.nativeUI.showWaiting('正在连接打印机');
plus.nativeUI.showWaiting(_DEVICE_PRINTER_);
setTimeout(function(){
if(!plus.JPLplugin.OpenPrinter(bluetooth.mac_address)) {
//mui.toast("打印机连接失败,请再试一次");
mui.toast(_DEVICE_AGAIN_);
plus.nativeUI.closeWaiting();
return;
}else{
//mui.toast("打印机连接成功");
mui.toast(_DEVICE_SUCCESS_);
plus.nativeUI.closeWaiting();
//document.getElementById('bledevice').style.display = "none";
bluetooth.hide = false;
menban = true;
mask.close();
//连接成功后,获取打印机状态
if(plus.JPLplugin.getPrinterState()){
print();
}
}
},200);
},
pairconnect: function(e) {
var target = e.target;
var flag = plus.JPLplugin.pairBle($(target).attr('mac'));
if(flag) {
this.mac_address = target.getAttribute('mac');
this.hide = false;
menban = true;
mask.close();
$('#search').focus();
} else {
//mui.toast("配对失败");
mui.toast(_DEVICE_FAIL_);
}
}
}
});
function print() {
if (!bluetooth.mac_address) {
mui.toast('请选择蓝牙打印机');
return;
}
main = plus.android.runtimeMainActivity();
BluetoothAdapter = plus.android.importClass("android.bluetooth.BluetoothAdapter");
UUID = plus.android.importClass("java.util.UUID");
uuid = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
BAdapter = BluetoothAdapter.getDefaultAdapter();
device = BAdapter.getRemoteDevice(bluetooth.mac_address);
plus.android.importClass(device);
bluetoothSocket = device.createInsecureRfcommSocketToServiceRecord(uuid);
plus.android.importClass(bluetoothSocket);
if (!bluetoothSocket.isConnected()) {
console.log('检测到设备未连接,尝试连接....');
bluetoothSocket.connect();
}
console.log(bluetoothSocket.isConnected());
if (bluetoothSocket.isConnected()) {
var outputStream = bluetoothSocket.getOutputStream();
plus.android.importClass(outputStream);
var string = "^XA" + //开始标签
"^JMA^LL1800^PW1000^MD10^PR2^PON^LRN^LH0,0" + "\r\n" + //毫米单位 宽
"^FO0,100" + "\r\n" + "^A0,N,50,50" + "\r\n" + "^FDDescription:^FS" + "\r\n" +
"^FO0,180" + "\r\n" + "^A0,N,50,50" + "\r\n" + "^FDGW 40V 2.0Ah 8-in. GEN II Polesaw w/ B&C^FS" + "\r\n" +
"^FO0,350" + "\r\n" + "^A0,N,50,50" + "\r\n" + "^FDITEM#: ^FS" + "\r\n" +
"^FO300,350" + "\r\n" + "^A0,N,50,50" + "\r\n" + "^FD1403702^FS" + "\r\n" +
"^FO00,410^BY3" + "\r\n" + "^BCN,100,Y,N,N" + "\r\n" + "^FD 1403702 ^FS" + "\r\n" +
"^FO0,600" + "\r\n" + "^A0,N,50,50" + "\r\n" + "^FDQTY: ^FS" + "\r\n" +
"^FO400,600" + "\r\n" + "^A0,N,50,50" + "\r\n" + "^FDPCS: ^FS" + "\r\n" +
"^FO00,700" + "\r\n" + "^A0,N,50,50" + "\r\n" + "^FDG.W: ^FS" + "\r\n" +
"^FO400,700" + "\r\n" + "^A0,N,50,50" + "\r\n" + "^FDKGS ^FS" + "\r\n" +
"^FO550,700" + "\r\n" + "^A0,N,50,50" + "\r\n" + "^FDN.W: ^FS" + "\r\n" +
"^FO910,700" + "\r\n" + "^A0,N,50,50" + "\r\n" + "^FDKGS ^FS" + "\r\n" +
"^FO00,800" + "\r\n" + "^A0,N,50,50" + "\r\n" + "^FDMade in China ^FS" + "\r\n" +
"^FO00,900" + "\r\n" + "^A0,N,50,50" + "\r\n" + "^FDPallet Code: PA1122061500000011^FS" + "\r\n" +
"^FO00,1000^BY3" + "\r\n" + "^BCN,200,Y,N,N" + "\r\n" + "^FD PA1122061500000011 ^FS" + "\r\n" +
"^XZ"; //横线
var bytes = plus.android.invoke(string, 'getBytes', 'UTF-8');
outputStream.write(bytes);
outputStream.flush();
device = null //这里关键
bluetoothSocket.close(); //必须关闭蓝牙连接否则意外断开的话打印错误
}
};
mui.back = function() {
var flag = false;
for(var key in item_one.records) {
flag = true;
break;
}
if(flag) {
var btnArray = [_NO_, _YES_];
mui.confirm(_WDDCH_QUIT_CONFIRM_, _NULL_TITLE_, btnArray, function(e) {
if(e.index == 1) { //退出
plus.webview.currentWebview().close();
} else {
//return false;
}
}, 'div');
} else {
plus.webview.currentWebview().close();
}
}
收起阅读 »

uni-simple-router 优雅实现服务端控制页面权限动态路由
权限访问管理在日常开发中扮演着重要角色。无论是控制不同角色访问特定页面、模块,还是管理按钮级别的权限,都需要一个灵活的方案来实现。在uni-app
中,我们可以结合uni-simple-router来达成这个目标。
现在我们假设系统中我们有三个不同的角色,分别是:管理员(admin)、经理(manager)和员工(employee)。然后根据不同角色拥有不同的权限来举例详细说明,假设我们有以下三个页面:Dashboard
、UserManagement
和Settings
。
角色权限定义
- 管理员(admin):拥有访问所有页面的权限。
- 经理(manager):可以访问
Dashboard
和UserManagement
页面。 - 员工(employee):只能访问
Dashboard
页面。
定义路由表
当准备让后台控制所有页面权限时,首先需要与后台人员约定好权限数据结构,以便有效管理和控制用户权限。为了简化示例并降低复杂度,我们可以采用一个最简单的示例来演示整个流程。
首先我们可观察到三个用户之间都共享一个名为Dashboard
的页面,但每个用户还有各自专属的页面。
为了实现页面权限的动态管理,我们可以将Dashboard
作为应用的起始静态页面,这意味着所有用户都可以访问它。其他用户专属页面可以在用户完成登录后,通过异步加载的方式动态添加到路由管理器中,从而实现按需加载的效果。
我们先构建最基本的路由表及插件配置如下:
routes.js
import {
__dynamicImportComponent__
} from '@/uni-simple-router'
const Dashboard = __dynamicImportComponent__(`@/pages/dashboard.vue`,{ pageType:`top` })
const Settings = __dynamicImportComponent__(`@/pages/settings.vue`,{ pageType:`top` })
const UserManagement = __dynamicImportComponent__(`@/pages/userManagement.vue`,{ pageType:`top` })
export const NotFound404 = __dynamicImportComponent__(`@/pages/404.vue`,{ pageType:`top` })
export const Login = __dynamicImportComponent__(`@/pages/login.vue`,{ pageType:`top` })
export const routesMap = {
'dashboard':{
path:`/dashboard`,
name:`dashboard`,
component:Dashboard,
},
'settings':{
path:`/settings`,
name:`settings`,
component:Settings,
},
'userManagement':{
path:`/userManagement`,
name:`userManagement`,
component:UserManagement,
},
}
router.js
import {
createRouter
} from '@/uni-simple-router'
import {
NotFound404,
Login
} from '@/router/routes.js'
/**
* 实例化路由插件
*/
const router = createRouter({
platform: process.env.VUE_APP_PLATFORM,
routeNotFound:(to)=> {
return {name:`404`}
},
routes:[
{
path:`/login`,
name:`login`,
component:Login
},
{
path:`/404`,
name:`404`,
component:NotFound404
}
]
})
export default router
在上述配置中,我们实例化了路由插件,并创建了两个基本的路由表:dashboard
和404
。这里,dashboard
是用户登录后访问的起始页面,而404
页面用于展示未授权或无匹配权限时的访问状态,以提供更直观的提示信息。
通过这种设置,当用户成功登录后,系统将自动跳转到dashboard
页面,因为它是应用的起始静态页面,对所有用户开放。而其他用户专属页面将在用户登录后,通过异步加载的方式动态添加到路由管理器中。
当用户尝试访问没有权限的页面时,系统将显示404
页面,提示用户当前操作未被授权或者访问的页面不存在。这样的用户体验能够帮助用户清楚地知道他们的权限范围,并且减少混淆和不必要的请求。
与后端人员约定数据结构
在实际开发中,权限数据结构的复杂程度通常会根据项目的具体情况和需求来进行约定。如果项目涉及多个角色和嵌套路由页面,那么约定的权限数据结构可能会更加复杂。然而,为了简化示例,我们仍然采用最简单的方式来呈现。根据上一步的路由定义,我们可以让后端人员在登录完成后返回如下数据结构:
- 管理员(admin):拥有访问所有页面的权限。
auth:[`dashboard`,`settings`,`userManagement`]
- 经理(manager):可以访问
Dashboard
和UserManagement
页面。auth:[`dashboard`,`userManagement`]
- 员工(employee):只能访问
Dashboard
页面。auth:[`dashboard`]
上面的权限列表是根据路由表的name
字段约定而成,根据不同角色拥有的权限来生成对应的auth
字段。
根据权限名生成对应的路由
如上,我们已经与后台约定好了数据结构,并且也定义了基础的路由结构。现在,我们只需要将对应的路由表根据权限动态加载进路由插件即可。
router.js
import {
createRouter
} from '@/uni-simple-router'
import {
currentUserInfo
} from '@/state.js'
import {
NotFound404,
Login,
addAuthList,
routesMap
} from '@/router/routes.js'
/**
* 实例化路由插件
*/
const router = createRouter({
platform: process.env.VUE_APP_PLATFORM,
routeNotFound:(to)=> {
const authList = currentUserInfo.value.auth
if(authList){
const {locationRawToMatched} = router.initEnvOptions.matcher
addAuthList(router,authList);
const matcher = locationRawToMatched(to)
if( Object.prototype.toString.call(matcher) === `[object Array]` ){
return {...to}
}
}
return {name:`404`}
},
routes:[
{
path:`/login`,
name:`login`,
component:Login
},
{
path:`/404`,
name:`404`,
component:NotFound404
}
]
})
export default router
routes.js
routes.js
中的配置非常简单,我们只需要再额外添加一个函数即可,如下:
export function addAuthList (router,authList){
const {nameToRecordMatcher} = router.initEnvOptions.matcher
for(let i = 0 ;i<authList.length;i++){
const name = authList[i];
const route = routesMap[name];
if(!nameToRecordMatcher(name)){
router.addRoute(route);
}
}
}
uni-simple-router
相比vue-router
确实在动态添加路由方面更加简单,我们已经尽可能地抹平了一些潜在的陷阱,以确保在开发过程中不会因为这些细小问题而扰乱心智。在示例中,我们展示了常规的动态路由页面,包括添加页面、删除页面以及404页面捕捉等功能。如果需要了解更多细节,可以参考该demo,里面包含了完整的演示代码。
最终示例效果展示

权限访问管理在日常开发中扮演着重要角色。无论是控制不同角色访问特定页面、模块,还是管理按钮级别的权限,都需要一个灵活的方案来实现。在uni-app
中,我们可以结合uni-simple-router来达成这个目标。
现在我们假设系统中我们有三个不同的角色,分别是:管理员(admin)、经理(manager)和员工(employee)。然后根据不同角色拥有不同的权限来举例详细说明,假设我们有以下三个页面:Dashboard
、UserManagement
和Settings
。
角色权限定义
- 管理员(admin):拥有访问所有页面的权限。
- 经理(manager):可以访问
Dashboard
和UserManagement
页面。 - 员工(employee):只能访问
Dashboard
页面。
定义路由表
当准备让后台控制所有页面权限时,首先需要与后台人员约定好权限数据结构,以便有效管理和控制用户权限。为了简化示例并降低复杂度,我们可以采用一个最简单的示例来演示整个流程。
首先我们可观察到三个用户之间都共享一个名为Dashboard
的页面,但每个用户还有各自专属的页面。
为了实现页面权限的动态管理,我们可以将Dashboard
作为应用的起始静态页面,这意味着所有用户都可以访问它。其他用户专属页面可以在用户完成登录后,通过异步加载的方式动态添加到路由管理器中,从而实现按需加载的效果。
我们先构建最基本的路由表及插件配置如下:
routes.js
import {
__dynamicImportComponent__
} from '@/uni-simple-router'
const Dashboard = __dynamicImportComponent__(`@/pages/dashboard.vue`,{ pageType:`top` })
const Settings = __dynamicImportComponent__(`@/pages/settings.vue`,{ pageType:`top` })
const UserManagement = __dynamicImportComponent__(`@/pages/userManagement.vue`,{ pageType:`top` })
export const NotFound404 = __dynamicImportComponent__(`@/pages/404.vue`,{ pageType:`top` })
export const Login = __dynamicImportComponent__(`@/pages/login.vue`,{ pageType:`top` })
export const routesMap = {
'dashboard':{
path:`/dashboard`,
name:`dashboard`,
component:Dashboard,
},
'settings':{
path:`/settings`,
name:`settings`,
component:Settings,
},
'userManagement':{
path:`/userManagement`,
name:`userManagement`,
component:UserManagement,
},
}
router.js
import {
createRouter
} from '@/uni-simple-router'
import {
NotFound404,
Login
} from '@/router/routes.js'
/**
* 实例化路由插件
*/
const router = createRouter({
platform: process.env.VUE_APP_PLATFORM,
routeNotFound:(to)=> {
return {name:`404`}
},
routes:[
{
path:`/login`,
name:`login`,
component:Login
},
{
path:`/404`,
name:`404`,
component:NotFound404
}
]
})
export default router
在上述配置中,我们实例化了路由插件,并创建了两个基本的路由表:dashboard
和404
。这里,dashboard
是用户登录后访问的起始页面,而404
页面用于展示未授权或无匹配权限时的访问状态,以提供更直观的提示信息。
通过这种设置,当用户成功登录后,系统将自动跳转到dashboard
页面,因为它是应用的起始静态页面,对所有用户开放。而其他用户专属页面将在用户登录后,通过异步加载的方式动态添加到路由管理器中。
当用户尝试访问没有权限的页面时,系统将显示404
页面,提示用户当前操作未被授权或者访问的页面不存在。这样的用户体验能够帮助用户清楚地知道他们的权限范围,并且减少混淆和不必要的请求。
与后端人员约定数据结构
在实际开发中,权限数据结构的复杂程度通常会根据项目的具体情况和需求来进行约定。如果项目涉及多个角色和嵌套路由页面,那么约定的权限数据结构可能会更加复杂。然而,为了简化示例,我们仍然采用最简单的方式来呈现。根据上一步的路由定义,我们可以让后端人员在登录完成后返回如下数据结构:
- 管理员(admin):拥有访问所有页面的权限。
auth:[`dashboard`,`settings`,`userManagement`]
- 经理(manager):可以访问
Dashboard
和UserManagement
页面。auth:[`dashboard`,`userManagement`]
- 员工(employee):只能访问
Dashboard
页面。auth:[`dashboard`]
上面的权限列表是根据路由表的name
字段约定而成,根据不同角色拥有的权限来生成对应的auth
字段。
根据权限名生成对应的路由
如上,我们已经与后台约定好了数据结构,并且也定义了基础的路由结构。现在,我们只需要将对应的路由表根据权限动态加载进路由插件即可。
router.js
import {
createRouter
} from '@/uni-simple-router'
import {
currentUserInfo
} from '@/state.js'
import {
NotFound404,
Login,
addAuthList,
routesMap
} from '@/router/routes.js'
/**
* 实例化路由插件
*/
const router = createRouter({
platform: process.env.VUE_APP_PLATFORM,
routeNotFound:(to)=> {
const authList = currentUserInfo.value.auth
if(authList){
const {locationRawToMatched} = router.initEnvOptions.matcher
addAuthList(router,authList);
const matcher = locationRawToMatched(to)
if( Object.prototype.toString.call(matcher) === `[object Array]` ){
return {...to}
}
}
return {name:`404`}
},
routes:[
{
path:`/login`,
name:`login`,
component:Login
},
{
path:`/404`,
name:`404`,
component:NotFound404
}
]
})
export default router
routes.js
routes.js
中的配置非常简单,我们只需要再额外添加一个函数即可,如下:
export function addAuthList (router,authList){
const {nameToRecordMatcher} = router.initEnvOptions.matcher
for(let i = 0 ;i<authList.length;i++){
const name = authList[i];
const route = routesMap[name];
if(!nameToRecordMatcher(name)){
router.addRoute(route);
}
}
}
uni-simple-router
相比vue-router
确实在动态添加路由方面更加简单,我们已经尽可能地抹平了一些潜在的陷阱,以确保在开发过程中不会因为这些细小问题而扰乱心智。在示例中,我们展示了常规的动态路由页面,包括添加页面、删除页面以及404页面捕捉等功能。如果需要了解更多细节,可以参考该demo,里面包含了完整的演示代码。
最终示例效果展示
收起阅读 »
我觉得老版的搜索功能要好用一些
不知道为啥现在的搜索是一个很小很小的logo,不说触发很难点击,就算用快捷键搜索了之后,切换回项目列表都变得很难受,老版本的搜索功能不是挺好用的么,不知道为什么非得改的这么不好使,多少有点恶心人了
不知道为啥现在的搜索是一个很小很小的logo,不说触发很难点击,就算用快捷键搜索了之后,切换回项目列表都变得很难受,老版本的搜索功能不是挺好用的么,不知道为什么非得改的这么不好使,多少有点恶心人了

Hbuildx编辑器的问题,望Hbuildx团队能看到
我说Hbuildx的开发团队门,你们能不能把测试周期延长,把问题解决再上线,一个问题解决了,又出现新问题,你们好歹也算是知名企业了,就做出来个这个ide给开发人用????一段代码给我卡崩ide 5-7次,敲个回车换行,光标能给我跑本行代码上边再敲一下跑代码的下一行,你们研发团队写的代码神操作啊!ide代码提示很随意,想出来的时候就出,不想出就不出!要么你就别有这功能!我靠脑子记能记得住!对待自己产品严谨点行不!!!实在不行你们把hbuildx测试交给第三来做也行!咱把用户习惯先不表,望Hbuildx能出来一款能抗能打的产品!现在版本实在是受够一堆堆更新,一堆堆问题!不更吧,安卓这还出问题,更新吧,写代码习惯硬生生被ide问题牵着走!别等全体开发人员只把你当成运行的工具,用vscode编辑的uniapp的时候,就是说明你已经彻底在开发人的心理只是沦为车架子了!
我说Hbuildx的开发团队门,你们能不能把测试周期延长,把问题解决再上线,一个问题解决了,又出现新问题,你们好歹也算是知名企业了,就做出来个这个ide给开发人用????一段代码给我卡崩ide 5-7次,敲个回车换行,光标能给我跑本行代码上边再敲一下跑代码的下一行,你们研发团队写的代码神操作啊!ide代码提示很随意,想出来的时候就出,不想出就不出!要么你就别有这功能!我靠脑子记能记得住!对待自己产品严谨点行不!!!实在不行你们把hbuildx测试交给第三来做也行!咱把用户习惯先不表,望Hbuildx能出来一款能抗能打的产品!现在版本实在是受够一堆堆更新,一堆堆问题!不更吧,安卓这还出问题,更新吧,写代码习惯硬生生被ide问题牵着走!别等全体开发人员只把你当成运行的工具,用vscode编辑的uniapp的时候,就是说明你已经彻底在开发人的心理只是沦为车架子了!
收起阅读 »
app base64 转本地文件
//将base64编码转换成录音文件
dataURL2Audio (base64Str, callback) {
var myArray=new Array();
var myArray = base64Str.split(";base64,");
// console.log(myArray[1]+"base64Str");
base64Str=myArray[1];
// var base64Str = base64Str.replace('data:audio/amr;base64,', '');
var audioName = (new Date()).valueOf() + '.jpg'; // 替换为要转换的文件名即可
plus.io.requestFileSystem(plus.io.PRIVATE_DOC, function(fs) {
fs.root.getFile(audioName, {
create: true
}, function(entry) {
// 获得平台绝对路径
var fullPath = entry.fullPath;
if(false) {
// 读取音频
var Base64 = plus.android.importClass("android.util.Base64");
var FileOutputStream = plus.android.importClass("java.io.FileOutputStream");
try {
var out = new FileOutputStream(fullPath);
var bytes = Base64.decode(base64Str, Base64.DEFAULT);
console.log(bytes+"-------")
out.write(bytes);
out.close();
// 回调
callback && callback(entry);
} catch(e) {
console.log(e.message);
}
} else if(true) {
var NSData = plus.ios.importClass('NSData');
var nsData = new NSData();
nsData = nsData.initWithBase64EncodedStringoptions(base64Str,0);
nsData.plusCallMethod({writeToFile:fullPath,atomically:true});
plus.ios.deleteObject(nsData);
// 回调
callback && callback(entry);
}
})
})
},
aaa () {
// let base64Str = ''
let base64Str = ''
this.dataURL2Audio(base64Str, (entry) => {
var content = entry.toURL();
console.log(content) // 直接使用
})
},
//将base64编码转换成录音文件
dataURL2Audio (base64Str, callback) {
var myArray=new Array();
var myArray = base64Str.split(";base64,");
// console.log(myArray[1]+"base64Str");
base64Str=myArray[1];
// var base64Str = base64Str.replace('data:audio/amr;base64,', '');
var audioName = (new Date()).valueOf() + '.jpg'; // 替换为要转换的文件名即可
plus.io.requestFileSystem(plus.io.PRIVATE_DOC, function(fs) {
fs.root.getFile(audioName, {
create: true
}, function(entry) {
// 获得平台绝对路径
var fullPath = entry.fullPath;
if(false) {
// 读取音频
var Base64 = plus.android.importClass("android.util.Base64");
var FileOutputStream = plus.android.importClass("java.io.FileOutputStream");
try {
var out = new FileOutputStream(fullPath);
var bytes = Base64.decode(base64Str, Base64.DEFAULT);
console.log(bytes+"-------")
out.write(bytes);
out.close();
// 回调
callback && callback(entry);
} catch(e) {
console.log(e.message);
}
} else if(true) {
var NSData = plus.ios.importClass('NSData');
var nsData = new NSData();
nsData = nsData.initWithBase64EncodedStringoptions(base64Str,0);
nsData.plusCallMethod({writeToFile:fullPath,atomically:true});
plus.ios.deleteObject(nsData);
// 回调
callback && callback(entry);
}
})
})
},
aaa () {
// let base64Str = ''
let base64Str = ''
this.dataURL2Audio(base64Str, (entry) => {
var content = entry.toURL();
console.log(content) // 直接使用
})
},
收起阅读 »

uniapp 集成方法,请求类,分页请求,详情请求,数据渲染,验签请求等方式,让你的开发更简单
文件存储于根目录下common里,当然大家也可以按照自己的想法存储在不同的位置
本集成方法,共设置三个文件依赖md5加密插件
请求接口API模块【api.js】单文件管理接口后期维护成本降低,如更换同一接口地址不用每个文件挨个找了
export const api=($api)=>{
const api={
index:'接口具体参数'
}
return api[$api];
}
集成方法模块【func.js】
此处可以根据个人需求增加所需的方法,让方法可以在多处使用
/* 输出json */
function toJson(option) {
if (typeof(option) == 'object') {
return option;
} else if (typeof(option) == 'string') {
try {
return JSON.parse(option);
} catch (e) {
return option;
}
} else {
return option;
}
}
/** 获取列表数据,支持分页加载*/
function list(othis, param, loading = {
is_loading: false,
loading_title: ''
}, k = 'list', p = 'page', i = 'is_list') {
if (typeof(loading) == 'object' && loading.is_loading == true) {
uni.showLoading({
title: loading.loading_title
})
}
othis.request(param.url, param.data).then(res => {
uni.hideLoading();
othis['is_loading'] = 1;
if (res.code == 1) {
var data = res.data.data;
var page = othis[p];
if (data.length < 1) othis[i] = 0;
var list = page < 2 ? [] : othis[k];
list = list.concat(data);
console.log(list);
othis[k] = list;
uni.stopPullDownRefresh();
} else {
uni.showModal({
title: res.info,
icon: 'none'
})
}
})
}
/**获取详细数据*/
function details(othis, param, k = null, loading = {
is_loading: false,
loading_title: ''
}) {
if (typeof(loading) == 'object' && loading.is_loading == true) {
uni.showLoading({
title: loading.loading_title
})
}
othis.request(param.url, param.data).then(res => {
uni.hideLoading();
othis['is_loading'] = 1;
if (res.code == 1) {
var data = res.data;
if (k == null) {
for (var i in data) {
othis[i] = data[i];
}
} else {
othis[k] = data;
}
} else {
uni.showModal({
title: res.info,
icon: 'none'
})
}
})
}
/**操作请求*/
function action(othis, param, type, confirm, loading = {
is_loading: true,
loading_title: ''
}) {
return new Promise((resolve, reject) => {
if (typeof(confirm) == 'object') {
uni.showModal({
title: confirm.title || '提醒',
content: confirm.content || '确定要操作吗?',
cancelColor: confirm.cancelColor || '#000',
cancelText: confirm.cancelText || '取消',
confirmColor: confirm.confirmColor || '#000',
confirmText: confirm.confirmText || '确认',
success: function(r) {
if (r.confirm) {
do_action(othis, param, type, loading, resolve);
}
}
})
} else {
do_action(othis, param, type, loading, resolve);
}
})
}
function do_action(othis, param, type, loading = {
is_loading: 1,
loading_title: ''
}, resolve) {
if (typeof(loading) == 'object' && loading.is_loading == true) {
uni.showLoading({
title: loading.loading_title
})
}
othis.request(param.url, param.data).then(res => {
uni.hideLoading();
othis['is_loading'] = 1;
if (res.code == 1) {
if (type == undefined || type == '') type = 'back'
var action = ['back', 'remind'];
if (typeof(type) != 'string' || action.indexOf(type) < 0) {
if (typeof(type) != 'object') type = {
type: type
};
var result = Object.assign(type, param, res)
resolve(result);
} else {
if (type == 'back') {
uni.showToast({
title: res.info,
icon: 'none'
})
setTimeout(function() {
uni.navigateBack();
}, 1500)
}
if (type == 'remind') {
uni.showToast({
title: res.info,
icon: 'none'
})
}
}
} else {
uni.showToast({
title: res.info,
icon: 'none'
})
}
})
}
/* 登录 */
function login(othis, link = 'login') {
// #ifdef H5
var pages = getCurrentPages();
var curr_page = pages[pages.length - 1].route;
uni.navigateTo({
url: link
})
uni.setStorageSync('history_page', pages);
// #endif
// #ifdef APP-PLUS
// #endif
// #ifdef MP-WEIXIN
return new Promise((resolve, reject) => {
if(uni.getStorageSync('access_token').length>10){
resolve({code:1,is_login:1})
uni.hideLoading()
return false;
}
var code = uni.getStorageSync('login_code');
uni.login({
success:function(res){
if(code.length<10) code=res.code;
action(othis, {
url: othis.api('wx_' + link),
data: {
code: code
}
}, 'then').then(res => {
uni.showToast({
title: res.info,
icon: 'none'
})
resolve(res);
if (res.code == 1) {
uni.setStorageSync('access_token', res.data.user_token);
}
})
}
})
})
// #endif
}
/* h5 页面登录成功跳转 */
function h5_login(link) {
link = link == undefined ? uni.getStorageSync('history_page') : link;
uni.navigateTo({
url: link
})
}
module.exports = {
toJson: toJson,
fetch: fetch,
list: list,
details: details,
action: action,
login: login
}
请求类库【支持后台验签,让您的数据更安全】
// #ifdef H5
var server = "/";
// #endif
// #ifndef H5
var server = "请求服务器主域";
// #endif
//服务器通过此两个参数验证接口的正确性
const session_id = '服务器加密session_id';
const code = '服务器加密code';
import md5 from '@/js_sdk/js-md5/build/md5.min.js';
import func from '@/common/func.js';
import {api} from '@/common/api.js';
export const _md5=md5;
export const _func=func;
export const _api=api
export const request = (url, data, method) => {
return new Promise((resolve, reject) => {
//生成验签参数
let sign = get_sign(data, code, session_id);
var access_token = uni.getStorageSync('access_token');
var header = {
'content-type': 'application/x-www-form-urlencoded',
sign: sign,
accesstoken: access_token
};
var form_data = data || {};
console.log(form_data);
form_data['at'] = (new Date()).getTime();
uni.request({
url: server + url,
method: method || 'POST',
data: form_data,
header: header,
success: (res) => {
resolve(func.toJson(res.data));
},
fail: (err) => {
console.log(err);
uni.showToast({
title: '嘤嘤嘤!!!网络出错了哦',
icon: 'none'
})
reject(err)
}
})
})
}
export const uploadFile = (url, file, data) => {
return new Promise((resolve, reject) => {
let sign = get_sign(data, code, session_id);
var access_token = uni.getStorageSync('access_token');
var header = {
'Content-Type': 'multipart/form-data',
sign: sign,
accesstoken: access_token
};
uni.uploadFile({
url: server + url,
method: "POST",
header: header,
filePath: file['value'],
name: file['field'],
success: (res) => {
resolve(func.toJson(res.data));
},
fail: (err) => {
uni.showToast({
title: '嘤嘤嘤!!!网络出错了哦',
icon: 'none'
})
reject(err)
}
})
})
}
function get_sign(data, code, session_id) {
var str = '';
for (var a in data) {
str += a + '=' + data[a] + '&';
}
str +=code;
var sign = md5(str);
return session_id + '&' + sign;
}
main.js 设置以下内容
import { request,uploadFile,_md5,_func,_api } from './common/request.js';
Vue.prototype.request= request;
Vue.prototype.uploadFile=uploadFile;
Vue.prototype.md5=_md5;
Vue.prototype.func=_func;
Vue.prototype.api=_api;
这样即可在其他页面调用
请求服务器分页数据仅需要简单代码即可完成
export default {
data() {
return {
page:1,
list:[],
is_list:1
}
},
onLoad() {
this.func.list(this,this.api('index'),{page:this.page})
},
methods: {
}
}
其他方法也是如此请求带弹窗的请求如下
this.func.action(this,
{url:this.api('test'),
data:{abc:'abc'}},'then',{title:'确认提交',content:'提交楼'}).then(res=>{
console.log(res);
})
请求详细数据如下
//接收参数abc直接渲染即可
this.func.details(this,{id:1},'abc');
登录代码,方便开发,也方便直接验证登录操作,如已经登录会直接执行then内方法
this.func.login(this).then(res=>{
console.log(res);
})
文件存储于根目录下common里,当然大家也可以按照自己的想法存储在不同的位置
本集成方法,共设置三个文件依赖md5加密插件
请求接口API模块【api.js】单文件管理接口后期维护成本降低,如更换同一接口地址不用每个文件挨个找了
export const api=($api)=>{
const api={
index:'接口具体参数'
}
return api[$api];
}
集成方法模块【func.js】
此处可以根据个人需求增加所需的方法,让方法可以在多处使用
/* 输出json */
function toJson(option) {
if (typeof(option) == 'object') {
return option;
} else if (typeof(option) == 'string') {
try {
return JSON.parse(option);
} catch (e) {
return option;
}
} else {
return option;
}
}
/** 获取列表数据,支持分页加载*/
function list(othis, param, loading = {
is_loading: false,
loading_title: ''
}, k = 'list', p = 'page', i = 'is_list') {
if (typeof(loading) == 'object' && loading.is_loading == true) {
uni.showLoading({
title: loading.loading_title
})
}
othis.request(param.url, param.data).then(res => {
uni.hideLoading();
othis['is_loading'] = 1;
if (res.code == 1) {
var data = res.data.data;
var page = othis[p];
if (data.length < 1) othis[i] = 0;
var list = page < 2 ? [] : othis[k];
list = list.concat(data);
console.log(list);
othis[k] = list;
uni.stopPullDownRefresh();
} else {
uni.showModal({
title: res.info,
icon: 'none'
})
}
})
}
/**获取详细数据*/
function details(othis, param, k = null, loading = {
is_loading: false,
loading_title: ''
}) {
if (typeof(loading) == 'object' && loading.is_loading == true) {
uni.showLoading({
title: loading.loading_title
})
}
othis.request(param.url, param.data).then(res => {
uni.hideLoading();
othis['is_loading'] = 1;
if (res.code == 1) {
var data = res.data;
if (k == null) {
for (var i in data) {
othis[i] = data[i];
}
} else {
othis[k] = data;
}
} else {
uni.showModal({
title: res.info,
icon: 'none'
})
}
})
}
/**操作请求*/
function action(othis, param, type, confirm, loading = {
is_loading: true,
loading_title: ''
}) {
return new Promise((resolve, reject) => {
if (typeof(confirm) == 'object') {
uni.showModal({
title: confirm.title || '提醒',
content: confirm.content || '确定要操作吗?',
cancelColor: confirm.cancelColor || '#000',
cancelText: confirm.cancelText || '取消',
confirmColor: confirm.confirmColor || '#000',
confirmText: confirm.confirmText || '确认',
success: function(r) {
if (r.confirm) {
do_action(othis, param, type, loading, resolve);
}
}
})
} else {
do_action(othis, param, type, loading, resolve);
}
})
}
function do_action(othis, param, type, loading = {
is_loading: 1,
loading_title: ''
}, resolve) {
if (typeof(loading) == 'object' && loading.is_loading == true) {
uni.showLoading({
title: loading.loading_title
})
}
othis.request(param.url, param.data).then(res => {
uni.hideLoading();
othis['is_loading'] = 1;
if (res.code == 1) {
if (type == undefined || type == '') type = 'back'
var action = ['back', 'remind'];
if (typeof(type) != 'string' || action.indexOf(type) < 0) {
if (typeof(type) != 'object') type = {
type: type
};
var result = Object.assign(type, param, res)
resolve(result);
} else {
if (type == 'back') {
uni.showToast({
title: res.info,
icon: 'none'
})
setTimeout(function() {
uni.navigateBack();
}, 1500)
}
if (type == 'remind') {
uni.showToast({
title: res.info,
icon: 'none'
})
}
}
} else {
uni.showToast({
title: res.info,
icon: 'none'
})
}
})
}
/* 登录 */
function login(othis, link = 'login') {
// #ifdef H5
var pages = getCurrentPages();
var curr_page = pages[pages.length - 1].route;
uni.navigateTo({
url: link
})
uni.setStorageSync('history_page', pages);
// #endif
// #ifdef APP-PLUS
// #endif
// #ifdef MP-WEIXIN
return new Promise((resolve, reject) => {
if(uni.getStorageSync('access_token').length>10){
resolve({code:1,is_login:1})
uni.hideLoading()
return false;
}
var code = uni.getStorageSync('login_code');
uni.login({
success:function(res){
if(code.length<10) code=res.code;
action(othis, {
url: othis.api('wx_' + link),
data: {
code: code
}
}, 'then').then(res => {
uni.showToast({
title: res.info,
icon: 'none'
})
resolve(res);
if (res.code == 1) {
uni.setStorageSync('access_token', res.data.user_token);
}
})
}
})
})
// #endif
}
/* h5 页面登录成功跳转 */
function h5_login(link) {
link = link == undefined ? uni.getStorageSync('history_page') : link;
uni.navigateTo({
url: link
})
}
module.exports = {
toJson: toJson,
fetch: fetch,
list: list,
details: details,
action: action,
login: login
}
请求类库【支持后台验签,让您的数据更安全】
// #ifdef H5
var server = "/";
// #endif
// #ifndef H5
var server = "请求服务器主域";
// #endif
//服务器通过此两个参数验证接口的正确性
const session_id = '服务器加密session_id';
const code = '服务器加密code';
import md5 from '@/js_sdk/js-md5/build/md5.min.js';
import func from '@/common/func.js';
import {api} from '@/common/api.js';
export const _md5=md5;
export const _func=func;
export const _api=api
export const request = (url, data, method) => {
return new Promise((resolve, reject) => {
//生成验签参数
let sign = get_sign(data, code, session_id);
var access_token = uni.getStorageSync('access_token');
var header = {
'content-type': 'application/x-www-form-urlencoded',
sign: sign,
accesstoken: access_token
};
var form_data = data || {};
console.log(form_data);
form_data['at'] = (new Date()).getTime();
uni.request({
url: server + url,
method: method || 'POST',
data: form_data,
header: header,
success: (res) => {
resolve(func.toJson(res.data));
},
fail: (err) => {
console.log(err);
uni.showToast({
title: '嘤嘤嘤!!!网络出错了哦',
icon: 'none'
})
reject(err)
}
})
})
}
export const uploadFile = (url, file, data) => {
return new Promise((resolve, reject) => {
let sign = get_sign(data, code, session_id);
var access_token = uni.getStorageSync('access_token');
var header = {
'Content-Type': 'multipart/form-data',
sign: sign,
accesstoken: access_token
};
uni.uploadFile({
url: server + url,
method: "POST",
header: header,
filePath: file['value'],
name: file['field'],
success: (res) => {
resolve(func.toJson(res.data));
},
fail: (err) => {
uni.showToast({
title: '嘤嘤嘤!!!网络出错了哦',
icon: 'none'
})
reject(err)
}
})
})
}
function get_sign(data, code, session_id) {
var str = '';
for (var a in data) {
str += a + '=' + data[a] + '&';
}
str +=code;
var sign = md5(str);
return session_id + '&' + sign;
}
main.js 设置以下内容
import { request,uploadFile,_md5,_func,_api } from './common/request.js';
Vue.prototype.request= request;
Vue.prototype.uploadFile=uploadFile;
Vue.prototype.md5=_md5;
Vue.prototype.func=_func;
Vue.prototype.api=_api;
这样即可在其他页面调用
请求服务器分页数据仅需要简单代码即可完成
export default {
data() {
return {
page:1,
list:[],
is_list:1
}
},
onLoad() {
this.func.list(this,this.api('index'),{page:this.page})
},
methods: {
}
}
其他方法也是如此请求带弹窗的请求如下
this.func.action(this,
{url:this.api('test'),
data:{abc:'abc'}},'then',{title:'确认提交',content:'提交楼'}).then(res=>{
console.log(res);
})
请求详细数据如下
//接收参数abc直接渲染即可
this.func.details(this,{id:1},'abc');
登录代码,方便开发,也方便直接验证登录操作,如已经登录会直接执行then内方法
this.func.login(this).then(res=>{
console.log(res);
})
收起阅读 »

ios打包后,<image>标签读取不到本地上传的临时图片地址,报404 Not Found
<image :src="imageUrl" @error="loadImgError" @click="choseImage" />
vue-cli 命令创建的项目
@dcloudio/uvm版本 --- 3.8.11 alpha
根据uni.chooseImage上传图片,拿到临时路径放到image标签展示,ios打包后在某些操作下,报如下错误,见附件, @error事件捕获(本地基座调试无发复现)
复现1:一开始给与全部权限,可上传成功,杀掉进程后,重新进入APP上传,image报404
复现2:一开始基于部分图片权限,可上传成功,后续从上传提示中进入手机设置给予全部权限,image报404
解决方法:把@dcloudio/uvm降级到3.8.7
<image :src="imageUrl" @error="loadImgError" @click="choseImage" />
vue-cli 命令创建的项目
@dcloudio/uvm版本 --- 3.8.11 alpha
根据uni.chooseImage上传图片,拿到临时路径放到image标签展示,ios打包后在某些操作下,报如下错误,见附件, @error事件捕获(本地基座调试无发复现)
复现1:一开始给与全部权限,可上传成功,杀掉进程后,重新进入APP上传,image报404
复现2:一开始基于部分图片权限,可上传成功,后续从上传提示中进入手机设置给予全部权限,image报404
解决方法:把@dcloudio/uvm降级到3.8.7
收起阅读 »
6年全栈开发_全职接单_寻求合作机会
个人全职全栈开发,拥有6年丰富开发经验。目前时间充裕,怀着诚意寻求合作机会。
作为个人全职开发者,我熟练掌握vue、uniapp、thinkphp、fastadmin等框架。
另外:还比较擅长原型及UI设计,技能全面覆盖
如果您有开发各端小程序、APP、网页登需求,欢迎随时联系我细聊:V:yzhua006。
个人全职全栈开发,拥有6年丰富开发经验。目前时间充裕,怀着诚意寻求合作机会。
作为个人全职开发者,我熟练掌握vue、uniapp、thinkphp、fastadmin等框架。
另外:还比较擅长原型及UI设计,技能全面覆盖
如果您有开发各端小程序、APP、网页登需求,欢迎随时联系我细聊:V:yzhua006。
收起阅读 »
聚合查询,多表统计数据的demo
个人也是小白,想实现多表统计功能,一直看不懂官方文档,今天找了大佬写了一个demo,有需要的可以了解一下。
我想实现的效果
user表
[
{
"_id": "64c22b3e189f866d65c3d0eb",
"name": "张三",
"sex": 1
},
{
"_id": "64c22b5a337a9f4db7882114",
"name": "王五",
"sex": 1
},
{
"_id": "64c22b5055b337825753d977",
"name": "李娇",
"sex": 0
},
{
"_id": "64c22b6bbd0220bf8c7051c5",
"name": "六花",
"sex": 0
}
]
sp表
[
{
"_id": "64c22cbe99c6246543bbfe9d",
"je": 20,
"name_id": "64c22b5055b337825753d977",
"sp": "李娇的商品1"
},
{
"_id": "64c22ce36e5d2d8f64af3f8e",
"je": 20,
"name_id": "64c22b5a337a9f4db7882114",
"sp": "王五的商品4"
},
{
"_id": "64c22cd97ad52ddc6482f40a",
"je": 20,
"name_id": "64c22b5a337a9f4db7882114",
"sp": "王五的商品2"
},
{
"_id": "64c22c766e5d2d8f64af2804",
"je": 20,
"name_id": "64c22b3e189f866d65c3d0eb",
"sp": "张三的商品2"
},
{
"_id": "64c22cf7f08210d515b35a86",
"je": 20,
"name_id": "64c22b6bbd0220bf8c7051c5",
"sp": "六花的商品1"
},
{
"_id": "64c22cd421821b2af5b6b48a",
"je": 20,
"name_id": "64c22b5a337a9f4db7882114",
"sp": "王五的商品1"
},
{
"_id": "64c22cdf337a9f4db78875c8",
"je": 20,
"name_id": "64c22b5a337a9f4db7882114",
"sp": "王五的商品3"
},
{
"_id": "64c22c71e0ec19bea1b13e15",
"je": 20,
"name_id": "64c22b3e189f866d65c3d0eb",
"sp": "张三的商品1"
},
{
"_id": "64c22cc27ad52ddc6482eecb",
"je": 20,
"name_id": "64c22b5055b337825753d977",
"sp": "李娇的商品2"
},
{
"_id": "64c22c7aa09a9bd68ba01823",
"je": 20,
"name_id": "64c22b3e189f866d65c3d0eb",
"sp": "张三的商品3"
}
]
wz表
[
{
"_id": "64c22f43337a9f4db788fdd8",
"name_id": "64c22b3e189f866d65c3d0eb",
"wzdz": 12,
"wzname": "六花的文章2"
},
{
"_id": "64c22f5ffe975fba5a5e51ef",
"name_id": "64c22b5055b337825753d977",
"wzdz": 34,
"wzname": "李娇的文章2"
},
{
"_id": "64c22f57337a9f4db7890253",
"name_id": "64c22b5055b337825753d977",
"wzdz": 3,
"wzname": "李娇的文章1"
},
{
"_id": "64c22f3ef08210d515b3e212",
"name_id": "64c22b3e189f866d65c3d0eb",
"wzdz": 12,
"wzname": "六花的文章1"
},
{
"_id": "64c22f7b6e5d2d8f64afdba0",
"name_id": "64c22b5a337a9f4db7882114",
"wzdz": 54,
"wzname": "王五的文章2"
},
{
"_id": "64c22f739755e344ab9c51ce",
"name_id": "64c22b5a337a9f4db7882114",
"wzdz": 11,
"wzname": "王五的文章1"
},
{
"_id": "64c235cd466d416d30844f4e",
"name_id": "64c22b5a337a9f4db7882114",
"wzdz": 13,
"wzname": "王五的文章3"
}
]
最终结果
张三 商品3 点赞24
李 商品2 点赞37
王五 商品4 点赞65
六花 商品1 点赞0
聚合查询代码
const db = uniCloud.database()
const dbCmd = db.command
const $ = dbCmd.aggregate
exports.main = async (event, context) => {
const {data}=await db.collection(`user`).aggregate()
.lookup({
from:`sp`,
let:{
user_id:`$_id`,
},
pipeline:$.pipeline()
.match(
dbCmd.expr(
$.eq(['$name_id','$$user_id'])
)
)
.group({
_id:'$name_id',
count: $.sum(1),
})
.done(),
as: 'sp_list',
})
.lookup({
from:`wz`,
let:{
user_id:`$_id`,
},
pipeline:$.pipeline()
.match(
dbCmd.expr(
$.eq(['$name_id','$$user_id'])
)
)
.group({
_id:'$name_id',
wzdz:$.sum('$wzdz'),
})
.done(),
as: 'wz_list',
})
.end();
return {
data:data
}
};
个人也是小白,想实现多表统计功能,一直看不懂官方文档,今天找了大佬写了一个demo,有需要的可以了解一下。
我想实现的效果
user表
[
{
"_id": "64c22b3e189f866d65c3d0eb",
"name": "张三",
"sex": 1
},
{
"_id": "64c22b5a337a9f4db7882114",
"name": "王五",
"sex": 1
},
{
"_id": "64c22b5055b337825753d977",
"name": "李娇",
"sex": 0
},
{
"_id": "64c22b6bbd0220bf8c7051c5",
"name": "六花",
"sex": 0
}
]
sp表
[
{
"_id": "64c22cbe99c6246543bbfe9d",
"je": 20,
"name_id": "64c22b5055b337825753d977",
"sp": "李娇的商品1"
},
{
"_id": "64c22ce36e5d2d8f64af3f8e",
"je": 20,
"name_id": "64c22b5a337a9f4db7882114",
"sp": "王五的商品4"
},
{
"_id": "64c22cd97ad52ddc6482f40a",
"je": 20,
"name_id": "64c22b5a337a9f4db7882114",
"sp": "王五的商品2"
},
{
"_id": "64c22c766e5d2d8f64af2804",
"je": 20,
"name_id": "64c22b3e189f866d65c3d0eb",
"sp": "张三的商品2"
},
{
"_id": "64c22cf7f08210d515b35a86",
"je": 20,
"name_id": "64c22b6bbd0220bf8c7051c5",
"sp": "六花的商品1"
},
{
"_id": "64c22cd421821b2af5b6b48a",
"je": 20,
"name_id": "64c22b5a337a9f4db7882114",
"sp": "王五的商品1"
},
{
"_id": "64c22cdf337a9f4db78875c8",
"je": 20,
"name_id": "64c22b5a337a9f4db7882114",
"sp": "王五的商品3"
},
{
"_id": "64c22c71e0ec19bea1b13e15",
"je": 20,
"name_id": "64c22b3e189f866d65c3d0eb",
"sp": "张三的商品1"
},
{
"_id": "64c22cc27ad52ddc6482eecb",
"je": 20,
"name_id": "64c22b5055b337825753d977",
"sp": "李娇的商品2"
},
{
"_id": "64c22c7aa09a9bd68ba01823",
"je": 20,
"name_id": "64c22b3e189f866d65c3d0eb",
"sp": "张三的商品3"
}
]
wz表
[
{
"_id": "64c22f43337a9f4db788fdd8",
"name_id": "64c22b3e189f866d65c3d0eb",
"wzdz": 12,
"wzname": "六花的文章2"
},
{
"_id": "64c22f5ffe975fba5a5e51ef",
"name_id": "64c22b5055b337825753d977",
"wzdz": 34,
"wzname": "李娇的文章2"
},
{
"_id": "64c22f57337a9f4db7890253",
"name_id": "64c22b5055b337825753d977",
"wzdz": 3,
"wzname": "李娇的文章1"
},
{
"_id": "64c22f3ef08210d515b3e212",
"name_id": "64c22b3e189f866d65c3d0eb",
"wzdz": 12,
"wzname": "六花的文章1"
},
{
"_id": "64c22f7b6e5d2d8f64afdba0",
"name_id": "64c22b5a337a9f4db7882114",
"wzdz": 54,
"wzname": "王五的文章2"
},
{
"_id": "64c22f739755e344ab9c51ce",
"name_id": "64c22b5a337a9f4db7882114",
"wzdz": 11,
"wzname": "王五的文章1"
},
{
"_id": "64c235cd466d416d30844f4e",
"name_id": "64c22b5a337a9f4db7882114",
"wzdz": 13,
"wzname": "王五的文章3"
}
]
最终结果
张三 商品3 点赞24
李 商品2 点赞37
王五 商品4 点赞65
六花 商品1 点赞0
聚合查询代码
const db = uniCloud.database()
const dbCmd = db.command
const $ = dbCmd.aggregate
exports.main = async (event, context) => {
const {data}=await db.collection(`user`).aggregate()
.lookup({
from:`sp`,
let:{
user_id:`$_id`,
},
pipeline:$.pipeline()
.match(
dbCmd.expr(
$.eq(['$name_id','$$user_id'])
)
)
.group({
_id:'$name_id',
count: $.sum(1),
})
.done(),
as: 'sp_list',
})
.lookup({
from:`wz`,
let:{
user_id:`$_id`,
},
pipeline:$.pipeline()
.match(
dbCmd.expr(
$.eq(['$name_id','$$user_id'])
)
)
.group({
_id:'$name_id',
wzdz:$.sum('$wzdz'),
})
.done(),
as: 'wz_list',
})
.end();
return {
data:data
}
};
收起阅读 »