修改地图配置信息,增加蓝牙帮助类

This commit is contained in:
LHG 2025-04-21 14:20:20 +08:00
parent e7ce5a2eeb
commit 8058af50e4
8 changed files with 692 additions and 47 deletions

View File

@ -0,0 +1,360 @@
import { AES128ECBEncrypt } from '@/utils/aes_util.js';
export default bluetooth = {
serviceId: '0000FFF0-0000-1000-8000-00805F9B34FB',
characteristicId: '0000FFF6-0000-1000-8000-00805F9B34FB',
deviceId: '',
blueSignal:'',
ecuSn:'',
blueState: 0,
blueMessage: '',
discovery:() => {
this.blueState = 0;
this.blueMessage = '';
uni.startBluetoothDevicesDiscovery({
success(res) {
if(res.errno == 0){
uni.onBluetoothDeviceFound(function (res) {
const devices = res.devices;
for(let i=0;i < devices.length; i++){
if(devices[i].localName == "ECU-" + this.ecuSn){
// 停止搜索
this.stopdiscovery();
this.blueState = 1;
this.blueMessage = '连接成功';
this.deviceId = devices[i].deviceId;
this.blueSignal = devices[i].RSSI;
this.bleconnection();
break;
}
}
});
setTimeout(function () {
if(this.blueState == 0) {
this.blueMessage = "未发现蓝牙设备";
this.blueState = -1;
}
//停止搜索
this.stopdiscovery();
}, 120000);
}
},
fail(err) {
this.blueMessage = "未发现蓝牙设备";
this.blueState = -1;
}
});
},
stopdiscovery:() =>{
uni.stopBluetoothDevicesDiscovery({
success(res) {
// if(res.errno != 0) {
// this.showmessage("停止蓝牙搜索失败", 'stopdiscovery');
// }
},
fail(){
// this.showmessage("停止蓝牙搜索失败", 'stopdiscovery');
}
});
},
bleconnection:() =>{
uni.createBLEConnection({
deviceId: this.deviceId,
success(res) {
this.startnotify();
},
fail(err) {
this.blueMessage = "蓝牙连接失败";
this.blueState = -1;
}
});
},
startnotify:() =>{
uni.notifyBLECharacteristicValueChange({
state:true,
deviceId: this.deviceId,
serviceId: this.serviceId,
characteristicId: this.characteristicId,
success(res) {
uni.onBLECharacteristicValueChange(function(res){
var hex = this.buftohex(res.value);
if (hex.length > 10) {
var header = hex.substring(0, 4);
if (header == '6774') {
var contentL1 = hex.substring(4, 6);
var contentL = parseInt(contentL1, 16);
if ((contentL + 5) * 2 == hex.length) {
//console.log('完整的一条数据contentL=', contentL)
this.distributecommandswithdatastr(hex)
} else if ((contentL + 5) * 2 > hex.length) { //需要分包接受数据
// 第一次接受分包数据
self.responseStr = hex;
//console.log('分包接受数据未实现', hex)
//console.log('第一次接受分包数据contentL2=', contentL)
} else { //数据异常 ,查看
//console.log('数据异常 ,查看1', hex)
}
return;
}
} else {
//console.log('分包接受数据未实现', hex)
}
});
this.verification();
},
fail(err) {
this.blueMessage = "蓝牙连接失败";
this.blueState = -1;
}
});
},
verification = (mac, key1, key2) => {
if (mac && key1 && key2) {
//数据总长度为21第一天长度20
var buffer = new ArrayBuffer(20)
var dataView = new DataView(buffer)
dataView.setUint32(0, 0X6774108a)
var sourceKey1 = mac + key1
var AESEncrypt = AES128ECBEncrypt(sourceKey1, key2)
if (AESEncrypt.length != 32) {
return;
}
for (var i = 0; i < 4; i++) {
var subStr = AESEncrypt.substring(i * 8, i * 8 + 8, 16);
dataView.setUint32(i * 4 + 4, parseInt(subStr, 16)); //4字节01523947(16进制)-->>10进制数字-->放入dateView
}
var i;
var bcc;//17为数据长度16+1
for (i = 0; i < 17; i++) {
bcc ^= dataView.getInt8(i + 3);
}
var buffer2 = new ArrayBuffer(1)
var dataView2 = new DataView(buffer2)
dataView2.setUint8(0, bcc)
console.log('验证', this.buftohex(buffer));
this.writecharacteristicvalue(buffer);
setTimeout(() => {
this.writecharacteristicvalue(buffer2);
}, 300);
} else {
var buffer = new ArrayBuffer(5)
var dataView = new DataView(buffer)
dataView.setUint32(0, 0X6774008a)
dataView.setUint8(4, 0x8a)
this.writecharacteristicvalue(buffer);
}
},
writecharacteristicvalue = (buffer) => {
uni.writeBLECharacteristicValue({
deviceId: this.deviceId,
serviceId: this.serviceId,
characteristicId: this.characteristicId,
value: buffer,
success: function (res) {
console.log('writeBLECharacteristicValue success', res.errMsg)
},
fail: function (res) {
console.log('writeBLECharacteristicValue fail', res.errMsg)
this.blueMessage = "蓝牙连接失败";
this.blueState = -1;
},
complete: function (res) {
}
});
},
verificationTimes:0,
distributecommandswithdatastr:(hex) => {
var cmd = hex.substring(6, 8);
var code = hex.substring(8, 10);
var errCode = code
var desDic = {}
var des = '';
if (code == '00') {
des = '成功'
errCode = 0
} else if (code == 'ff') { //-1
des = '锁已打开'
errCode = -1
} else if (code == 'fe') { //-2
des = '电量不足'
errCode = -2
} else if (code == 'fd') { //-3
des = '加密验证失败'
errCode = -3
} else if (code == 'fc') { //-4
des = '内存空间不足'
errCode = -4
} else if (code == 'fb') { //-5
des = '格式错误'
errCode = -5
} else if (code == 'fa') { //-6
des = '没有验证或验证失败'
errCode = -6
desDic['mac'] = hex.substring(10, 22);
desDic['key'] = hex.substring(22, 30);
} else if (code == 'f9') { //-7
des = 'flash写入失败'
errCode = -7
} else if (code == 'f8') { //-8
des = '数据空'
errCode = -8
} else if (code == 'f7') { //-9
des = '操作失败'
errCode = -9
} else if (code == 'f6') { //-10
des = '运输模式下无法执行该指令'
errCode = -10
} else if (code == 'f5') { //-11
des = '锁车失败,车辆未停止'
errCode = -11
} else if (code == 'f4') { //-12
des = '未检测到道钉信息'
errCode = -12
} else if (parseInt(code,16) == 149) { //
des = '摄像头正在工作中'
errCode = parseInt(code,16)
} else if (parseInt(code,16) == 150) { //
des = '摄像头离线'
errCode = parseInt(code,16)
} else if (parseInt(code,16) == 151) { //
des = '车辆不在使用中(临时停车或是还车中)无法使用摄像头还车'
errCode = parseInt(code,16)
} else if (parseInt(code,16) == 152) { //
des = '执行命令超时'
errCode = parseInt(code,16)
} else if (parseInt(code,16) == 153) { //
des = '摄像头执行失败'
errCode = parseInt(code,16)
} else if (parseInt(code,16) == 154) { //
des = '不在站点中或没有垂直停车'
errCode = parseInt(code,16)
} else {
des = '未知错误码' + code
errCode = code
}
if (cmd == '4a') { //验证
if (verificationTimes >= 1) {
verificationTimes = 0
if (errCode == -6) {
verificationTimes = 1;
this.verification(desDic.mac, desDic.key, '6666660000888888')
return;
}
//验证成功
this.blueState = 10;
this.blueMessage = '验证成功';
return;
} else {
verificationTimes += 1;
}
if (errCode == -6) {
this.verification(desDic.mac, desDic.key, '6666660000888888')
}
}
},
buftohex: (buffer) => {
return Array.prototype.map.call(new Uint8Array(buffer), x => ('00' + x.toString(16)).slice(-2)).join('');
},
closeconn: () =>{
uni.closeBLEConnection({
deviceId: this.deviceId,
success(res) {
}
});
},
execcommon = (comm) => {
switch(comm){
case "openLock": //开锁
var buffer = new ArrayBuffer(5);
var dataView = new DataView(buffer);
dataView.setUint32(0, 0X67740082);
dataView.setUint8(4, 0x82);
this.writecharacteristicvalue(buffer);
break;
case "closeLock": //关锁
var buffer = new ArrayBuffer(5);
var dataView = new DataView(buffer);
dataView.setUint32(0, 0X67740081);
dataView.setUint8(4, 0x81);
this.writecharacteristicvalue(buffer);
break;
// case "openBatteryHouse": //打开电池仓
// var buffer = new ArrayBuffer(6);
// var dataView = new DataView(buffer);
// dataView.setUint32(0, 0X67740189);
// dataView.setUint8(4, 0);
// var i;
// var bcc;
// for (i = 0; i < 2; i++) {
// bcc ^= dataView.getInt8(i + 3);
// }
// dataView.setInt8(5, bcc);
// this.writecharacteristicvalue(buffer);
// break;
// case "closeBatteryHouse": //关闭电池仓
// var buffer = new ArrayBuffer(6);
// var dataView = new DataView(buffer);
// dataView.setUint32(0, 0X67740189);
// dataView.setUint8(4, 1);
// var i;
// var bcc;
// for (i = 0; i < 2; i++) {
// bcc ^= dataView.getInt8(i + 3);
// }
// dataView.setInt8(5, bcc);
// this.writecharacteristicvalue(buffer);
// break;
}
},
openbluetooth:() => {
uni.openBluetoothAdapter({
success(res) {
if(res.errno == 0){
this.discovery();
}
else
{
this.blueMessage = "蓝牙未开启或未授权";
this.blueState = -1;
}
},
fail(err) {
let errmsg = err.errMsg;
if(err.errCode == 10001){
}
this.blueMessage = "蓝牙未开启或未授权";
this.blueState = -1;
}
});
uni.onBluetoothAdapterStateChange(function (res) {
if(res["available"]){
this.discovery();
}
else {
this.blueMessage = "蓝牙未开启或未授权";
this.blueState = -1;
}
});
},
openlock:() =>{
if(this.ecuSn == ''){
this.blueMessage = "开锁失败";
}
this.openbluetooth();
let connTime = 0;
let timer = setInterval(() =>{
if(this.blueState == 10){
clearInterval(timer);
this.execcommon("openLock");
}
else if(connTime > 6){
clearInterval(timer);
this.blueMessage = "开锁失败";
}
connTime += 1;
}, 10000);
}
}

View File

@ -1,5 +1,12 @@
const config = {
sm2PublicKey: "04f5084ee12767d932f293508e30e3b0100185042ec0f061dedaf92b793b93f79fd6179d5e47e25b7aec98e00cf90dd56df1f8191012537187e7bbfd2d1de299fc"
sm2PublicKey: "04f5084ee12767d932f293508e30e3b0100185042ec0f061dedaf92b793b93f79fd6179d5e47e25b7aec98e00cf90dd56df1f8191012537187e7bbfd2d1de299fc",
map: {
apiKey: "BECBZ-EJIEQ-LUU5N-B5ISQ-3TLMZ-BXFLG",
center:{
lat: 30.633158,
lng: 103.974997
}
}
,pageParam: {
pageNum: 1,
pageSize: 10,

View File

@ -49,23 +49,42 @@
{{ record.dealState == '0' ? '未处理' : record.dealState == '1' ? '处理中' : record.dealState == '2' ? '已处理' : '' }}
</template>
<template v-if="column.key === 'action'">
<a-space>
<a @click="handleDeal(record)">处理</a>
<a-space v-if="record.dealState == '0'">
<a @click="handleDeal(record)">处理</a>
</a-space>
<a-space v-if="record.dealState == '1'">
<a @click="handleDeal(record)">完成处理</a>
</a-space>
<a-divider type="vertical" />
</template>
</template>
</a-table>
</a-spin>
<!-- 处理停车点申请 -->
<a-modal
v-model:open="open"
:title="openTitle"
@ok="handleOkModal"
width="800px"
:maskClosable="false"
>
<BackSiteForm ref="formModel"></BackSiteForm>
</a-modal>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue';
import { ref, reactive, onMounted, nextTick } from 'vue';
import { callOrder } from '@/apis/call.js'
import { message } from 'ant-design-vue'
import config from '@/utils/config.js'
import { dataFormat } from '@/utils/tools'
import BackSiteForm from '@/views/form/backsite/BackSiteForm.vue'
const activeKey = ref('');
const open = ref(false);
const openTitle = ref('处理停车点申请');
const formModel = ref(null);
const spinning = ref(false);
const tipContent = ref("加载中...");
//
@ -226,6 +245,22 @@
}
const handleDeal = async (record) => {
console.log(record)
open.value = true;
nextTick(() => {
if (formModel.value) {
formModel.value.openForm({ backsiteid: record['id'] });
} else {
console.log('formModel is not ready yet');
}
});
}
</script>
/**
* 处理停车点申请modal确定
*/
const handleOkModal = () => {
formModel.value.formSave((data => {
open.value = false;
}));
}
</script>

View File

@ -51,10 +51,11 @@
</a-button> -->
<!-- 登录 -->
<div class="topAvatar">
<!-- <a-avatar
class="avatar"
src="https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png"
/> -->
<a>
当前区域{{ userdfrname }} <DownOutlined />
</a>
<a-divider type="vertical" />
<UserOutlined style="margin: 0px 5px;" />
<a-dropdown>
<a
class="ant-dropdown-link"
@ -119,6 +120,7 @@ const router = useRouter()
//
const isLogin = ref(false)
const username = ref('')
const userdfrname = ref('')
onMounted(() => {
isLogin.value = getCache('ebike-token') ? true : false
@ -131,7 +133,11 @@ onMounted(() => {
//
const userinfo = getCache('ebike-userinfo');
console.log('userinfo', userinfo)
username.value = userinfo.username ? userinfo.username : '无'
username.value = userinfo.username ? userinfo.username : 'X'
//
const userdefultoperation = getCache('ebike-userdefultoperation');
userdfrname.value = userdefultoperation.regionName ? userdefultoperation.regionName : '';
const menus = getMenus()
items.value = menus
@ -215,6 +221,7 @@ const goBack = () => {
}
.topAvatar {
font-size: 14px;
float: right;
margin-right: 40px;
div {

View File

@ -265,11 +265,11 @@ const columns = ref([
dataIndex: 'index'
},
{
key: 'zoneId',
title: '所属',
key: 'zoneName',
title: '所属行政区',
width: 200,
align: 'center',
dataIndex: 'zoneId'
dataIndex: 'zoneName'
},
{
key: 'regionName',
@ -409,7 +409,7 @@ const feeConfigInfo = (record) => {
openTitle.value = "费用信息 - " + record['regionName']
open.value = true
nextTick(() => {
if (formModel) {
if (formModel.value) {
formModel.value.openForm({ regionId: record['regionId'] });
} else {
console.log('formModel is not ready yet');

View File

@ -16,7 +16,7 @@
<div class="map-container" style="width: auto; height: auto;">
<tlbs-map
ref="mapRef"
api-key="BECBZ-EJIEQ-LUU5N-B5ISQ-3TLMZ-BXFLG"
:api-key="config.map.apiKey"
:center="center"
zoom="16"
:control="control"
@ -44,6 +44,7 @@
import { callOperate } from '@/apis/call.js'
import { message } from 'ant-design-vue'
import { isNullOrEmpty } from '@/utils/tools.js'
import config from '@/utils/config.js'
const props = defineProps({
onCallBack: {
type: Function,
@ -178,7 +179,7 @@
}
const mapRef = ref(null);
const center = ref({ lat: 30.633158, lng: 103.974997 });
const center = ref( config.map.center );
const control = ref({ scale: {}, zoom: { position: 'topRight',},});
const options = ref({ renderOptions: { renderOptions: true, }});
const editorRef = ref(null);

View File

@ -1,31 +1,114 @@
<template>
<a-form
:model="form"
:label-col="{sm: { span: 8 } }"
:wrapper-col="{ span:24}"
ref="formRef"
label-align="left"
:label-col="{ span: 6 }"
:wrapper-col="{ span: 18 }"
>
<a-row :gutter="8">
<a-col :xs="12">
<a-row>
<a-col :span="12">
<a-form-item
label="上报来源"
name="startupCost"
>
<span class="ant-form-text">{{ }}</span>
<span class="ant-form-text">{{ form.applySource }}</span>
</a-form-item>
</a-col>
<a-col :xs="12">
<a-col :span="12">
<a-form-item
label="起步时长(分钟)"
required
name="startupDuration"
:rules="[{ required: true, message: '起步时长' }]"
label="运营区域"
>
<a-input
v-model:value="form.startupDuration"
placeholder="起步时长"
type="number"
/>
<span class="ant-form-text">{{ form.regionName }}</span>
</a-form-item>
</a-col>
</a-row>
<a-row>
<a-col :span="12">
<a-form-item
label="昵称"
>
<span class="ant-form-text">{{ form.applyNickname }}</span>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item
label="用户手机"
>
<span class="ant-form-text">{{ form.applyPhone }}</span>
</a-form-item>
</a-col>
</a-row>
<a-row>
<a-col :span="12">
<a-form-item
label="反馈时间"
>
<span class="ant-form-text">{{ form.applyTime }}</span>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item
label="申请原因"
>
<span class="ant-form-text">{{ form.applyReason }}</span>
</a-form-item>
</a-col>
</a-row>
<a-row>
<a-col :span="24">
<a-form-item
label="反馈位置"
:label-col="{ span: 3 }"
:wrapper-col="{ span: 21 }"
>
<span class="ant-form-text">{{ form.applyPosition }}</span>
</a-form-item>
</a-col>
</a-row>
<a-row>
<a-col :span="24">
<div class="map-container" style="width: auto; height: 300px;margin: 0px 0px 30px 0px;border: 1px solid #a1a1a1;">
<tlbs-map
ref="mapRef"
:api-key="config.map.apiKey"
:center="center"
zoom="16"
:control="control"
:options="options"
@map_inited="onMapInited"
>
<tlbs-multi-polygon v-if="polygonGeometries.length > 0" ref="polygonRef" :geometries="polygonGeometries" />
<tlbs-multi-circle v-if="circleGeometries.length > 0" ref="circleRef" :geometries="circleGeometries" />
</tlbs-map>
</div>
</a-col>
</a-row>
<a-row>
<a-col :span="24">
<a-form-item
label="处理状态"
:label-col="{ span: 3 }"
:wrapper-col="{ span: 21 }"
required
name="dealState"
:rules="[{ required: true, message: '请选择处理状态' }]"
>
<a-select v-model:value="form.dealState" placeholder="请选择处理状态">
<a-select-option value="1">处理中</a-select-option>
<a-select-option value="2">已处理</a-select-option>
</a-select>
</a-form-item>
</a-col>
</a-row>
<a-row>
<a-col :span="24">
<a-form-item
label="备注"
:label-col="{ span: 3 }"
:wrapper-col="{ span: 21 }"
name="dealNotes"
>
<a-textarea v-model:value="form.dealNotes" placeholder="请输入备注"/>
</a-form-item>
</a-col>
</a-row>
@ -34,14 +117,166 @@
<script setup>
import { ref } from 'vue'
import _ from 'lodash'
import { callOrder, callOperate } from '@/apis/call.js'
import { message } from 'ant-design-vue'
import config from '@/utils/config.js'
const formRef = ref();
const form = ref({
const formData = () => ({
id: "",
applySource: "",
applyPosition: "",
outageNotice: ""
applyLng: 0,
applyLat: 0,
applyNickname: "",
applyPhone: "",
applyTime: "",
applyReason: "",
regionId: "267781609627852800",
regionName: "",
dealState: "",
dealUser: "",
dealTime: "",
dealNotes: ""
});
const form = ref(formData());
const mapRef = ref(null);
const center = ref(config.map.center);
const control = ref({ scale: {}, zoom: { position: 'topRight',},});
const options = ref({ renderOptions: { renderOptions: true, }});
const dataRegion = ref({});
const onMapInited = () => {
if (mapRef.value) {
const calculatedWidth = document.getElementsByClassName("map-container")[0].offsetWidth;
const calculatedHeight = document.getElementsByClassName("map-container")[0].offsetHeight-4;
// resize
const map = mapRef.value.map;
if (map) {
map.resize(calculatedWidth, calculatedHeight);
}
}
console.log(mapRef.value);
};
const openForm = (params = {}) => {
Object.assign(form.value, formData());
formRef.value.resetFields();
if (params['backsiteid']) {
form.value.id = params['backsiteid']
callOrder("/ebikeUserBacksite/getInfo?id="+ params['backsiteid'], {}, "get").then(res => {
if (res.code == 200) {
if (res.data) {
form.value = {
id: res.data.id,
applySource: res.data.applySource,
applyPosition: res.data.applyPosition,
applyLng: res.data.applyLng,
applyLat: res.data.applyLat,
applyNickname: res.data.applyNickname,
applyPhone: res.data.applyPhone,
applyTime: res.data.applyTime,
applyReason: res.data.applyReason,
};
}
} else {
message.error(res.message);
}
});
}
if(form.value.regionId != ""){
callOperate("/ebikeRegion/getRegionInfo/" + form.value.regionId, {}, "get").then((res) => {
if (res.code != 200) {
message.error(res.message)
return
}
dataRegion.value = res.data;
loadMap();
});
}
};
const polygonRef = ref(null);
const circleRef = ref(null);
const polygonGeometries = ref([]);
const circleGeometries = ref([]);
let timer = null;
const loadMap = () => {
if (mapRef.value && typeof(TMap) !== 'undefined') {
clearTimeout(timer);
if (dataRegion.value) {
const { regionId ,points, shapeType, radius } = dataRegion.value;
let shapeId = 'polygon' + regionId;
if (shapeType == 1) {
shapeId = 'circle' + regionId;
}
if (points) {
if(shapeType == 1){
//
circleGeometries.value = [];
circleGeometries.value.push({
id: shapeId, //
styleId: 'circle', // id
radius: radius, //
center: new TMap.LatLng(points[0].latitude, points[0].longitude),
properties: {
//
title: 'circle',
},
});
}
else if(shapeType == 2){
const paths = [];
points.forEach((point) => {
paths.push(new TMap.LatLng(point.latitude, point.longitude));
});
//
polygonGeometries.value = [];
polygonGeometries.value.push({
id: shapeId, //
styleId: 'polygon', // id
paths, //
properties: {
//
title: 'polygon',
},
});
}
}
}
}
else
{
timer = setTimeout(() => {
loadMap(dataRegion);
}, 1000)
}
};
const formSave = (callBack) => {
// formRef.value.validate().then(() => {
// callOperate("/ebikeRegion/saveOperation", form.value).then(res => {
// if (res.code != 200) {
// message.error(res.message);
// if (callBack) {
// callBack(false, res);
// }
// return;
// }
// if (callBack) {
// callBack(true, res);
// }
// formRef.value.resetFields()
// })
// }).catch(error => {
// if (callBack) {
// callBack(false, error);
// }
// });
};
defineExpose({ openForm, formSave });
</script>

View File

@ -28,7 +28,7 @@
</a-col>
</a-row>
<a-row :gutter="8">
<a-col :xs="12">
<!-- <a-col :xs="12">
<a-form-item
label="免费时长模式"
required
@ -44,7 +44,7 @@
<a-radio value="2"></a-radio>
</a-radio-group>
</a-form-item>
</a-col>
</a-col> -->
<a-col :xs="12">
<a-form-item
:label="freeDuration"
@ -377,7 +377,7 @@ const formData = () => ({
regionId: "",
chargingMode: "",
freeDurationMode: "1",
freeDuration: "",
freeDuration: "免费时长(分钟)",
timeDivisionCharging: "1",
ebikeSysRcostsetTimePeriodDtos: [],
ebikeSysRcostsetWeekDtos: [],
@ -399,14 +399,14 @@ const form = ref(formData());
//
const columns = ref([])
const changeFreeDurationMode = (data) => {
const value = data.target.value;
if (value == "1") {
freeDuration.value = "免费时长(分钟)";
} else if (value == "2") {
freeDuration.value = "免费时长(秒)";
}
}
// const changeFreeDurationMode = (data) => {
// const value = data.target.value;
// if (value == "1") {
// freeDuration.value = "()";
// } else if (value == "2") {
// freeDuration.value = "()";
// }
// }
const timePeriodColumns = [
{