feat:新增蓝牙功能和车辆上下架功能
This commit is contained in:
parent
8981806b90
commit
c342fea9a5
@ -1,8 +1,16 @@
|
||||
import type { IExecuteCommand } from '@/api/types/centerControl'
|
||||
import { http } from '@/http/http'
|
||||
|
||||
/**
|
||||
* 校验SN或BikeCode
|
||||
* 设备是否在线
|
||||
*/
|
||||
export function checkSnOrBikeCodeAPI(query: { ecuSn: string, bikeCode: string }) {
|
||||
return http.get<any>('/operations/ebikeEcuInfo/checkSnOrBikeCode', query)
|
||||
return http.get<any>('/operations/ebikeEcuInfo/online', query)
|
||||
}
|
||||
|
||||
/**
|
||||
* 中控执行命令
|
||||
*/
|
||||
export function executeCommandAPI(query: IExecuteCommand) {
|
||||
return http.get<any>('/operations/ebikeEcuInfo/executeCommand', query)
|
||||
}
|
||||
|
||||
@ -0,0 +1,7 @@
|
||||
// 中控执行命令
|
||||
|
||||
export interface IExecuteCommand {
|
||||
ecuSn: string
|
||||
bikeCode: string
|
||||
commandCode: 'FIND_BIKE' | 'GPS' | 'OPEN_BATTERY_LOCK'
|
||||
}
|
||||
26
src/api/warehouse.ts
Normal file
26
src/api/warehouse.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import { http } from '@/http/http'
|
||||
|
||||
// 批量上架
|
||||
export function batchAddInventoryAPI(data: { bikeCodes: string[], regionId: string }) {
|
||||
return http.post<any>('/operations/ebikeBikeInfo/batchLaunch', data)
|
||||
}
|
||||
|
||||
// 批量下架
|
||||
export function batchRemoveInventoryAPI(data: { bikeCodes: string[] }) {
|
||||
return http.post<any>('/operations/ebikeBikeInfo/batchUnLaunch', data)
|
||||
}
|
||||
|
||||
// 未上架车辆分页查询
|
||||
export function getUnInventoryListAPI(query: { pageSize: number, pageNum: number }) {
|
||||
return http.get<any>('/operations/ebikeBikeInfo/unLaunchPage', query)
|
||||
}
|
||||
|
||||
// 上架车辆分页查询
|
||||
export function getInventoryListAPI(query: { pageSize: number, pageNum: number }) {
|
||||
return http.get<any>('/operations/ebikeBikeInfo/launchPage', query)
|
||||
}
|
||||
|
||||
// 查询所有运营中区域表
|
||||
export function getOperatoringAllListAPI() {
|
||||
return http.get<any>('/operations/ebikeRegion/operationList')
|
||||
}
|
||||
847
src/pages-sub/bluetooth/bluetoothscan/bluetoothscan.vue
Normal file
847
src/pages-sub/bluetooth/bluetoothscan/bluetoothscan.vue
Normal file
@ -0,0 +1,847 @@
|
||||
<script lang="ts" setup>
|
||||
import { AES128ECBEncrypt, bufTohex } from '@/utils/aesTools'
|
||||
|
||||
definePage({
|
||||
style: {
|
||||
navigationBarTitleText: '蓝牙连接',
|
||||
navigationBarBackgroundColor: '#1488f5',
|
||||
navigationBarTextStyle: 'white',
|
||||
},
|
||||
})
|
||||
|
||||
const list_item_style = computed(() => {
|
||||
return {
|
||||
padding: '20rpx 0',
|
||||
borderBottom: '2rpx solid #cccccc',
|
||||
fontSize: '28rpx',
|
||||
color: '#6a6a6a',
|
||||
}
|
||||
})
|
||||
|
||||
const list_item_btn_style = computed(() => {
|
||||
return {
|
||||
height: '35px',
|
||||
fontSize: '24rpx',
|
||||
}
|
||||
})
|
||||
|
||||
// 配置
|
||||
const config = reactive({
|
||||
TIMEOUT: 5000, // 搜索超时时间
|
||||
ISOPEN: false, // 是否打开蓝牙
|
||||
})
|
||||
// 蓝牙配置
|
||||
const bluetoothConfig = reactive({
|
||||
deviceId: '', // 设备id
|
||||
status: -1, // -1 未初始化 0 连接失败 1 连接成功
|
||||
RSSI: 0, // 信号强度
|
||||
localName: '', /// / 设备名称
|
||||
serviceId: '', // 服务id
|
||||
characteristicId: '', // 读特征值id
|
||||
writeCharacteristicId: '', // 写入特征值id
|
||||
btnIsLoading: false, // 按钮加载中
|
||||
isCloseBlue: false, // 是否关闭蓝牙(用于监听蓝牙状态自动连接使用,手动关闭不监听)
|
||||
currentInstruct: '', // 当前正在操作的指令
|
||||
})
|
||||
|
||||
//
|
||||
const notifyRef = ref(null)
|
||||
const notifyConfig = reactive({
|
||||
message: 'asdasdas',
|
||||
type: 'primary', // primary,success,warning,error
|
||||
})
|
||||
|
||||
// 指令列表配置
|
||||
const commandList = reactive({
|
||||
// 开锁
|
||||
openLock: {
|
||||
title: '车锁开',
|
||||
key: 'openLock',
|
||||
status: 'none', // none,loading,success,fail
|
||||
btnText: '执行',
|
||||
btnLoadingText: '执行中',
|
||||
btnLoading: false,
|
||||
msg: '执行车锁开命令:',
|
||||
},
|
||||
})
|
||||
const codeType = ref<'car' | 'sn'>('car')
|
||||
const code = ref<string>('')
|
||||
|
||||
function handleType() {
|
||||
code.value = ''
|
||||
codeType.value = codeType.value === 'car' ? 'sn' : 'car'
|
||||
}
|
||||
|
||||
function handleScan() {
|
||||
openBluetoothAdapter()
|
||||
}
|
||||
|
||||
// 打开化蓝牙适配器
|
||||
function openBluetoothAdapter() {
|
||||
return new Promise((resolve, reject) => {
|
||||
uni.showLoading({
|
||||
title: '正在打开蓝牙适配器中。。。',
|
||||
})
|
||||
uni.openBluetoothAdapter({
|
||||
success(res) {
|
||||
const { errno } = res
|
||||
uni.hideLoading()
|
||||
if (errno !== 0) {
|
||||
reject(new Error('蓝牙适配器打开失败'))
|
||||
}
|
||||
config.ISOPEN = true
|
||||
resolve(res)
|
||||
console.log('蓝牙适配器------>', res)
|
||||
},
|
||||
fail(err) {
|
||||
uni.hideLoading()
|
||||
console.error(err)
|
||||
reject(new Error('打开蓝牙适配器失败'))
|
||||
},
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// 停止蓝牙适配器
|
||||
function closeBluetoothAdapter() {
|
||||
config.ISOPEN = false
|
||||
bluetoothConfig.deviceId = ''
|
||||
uni.closeBluetoothAdapter({
|
||||
success(res) {
|
||||
console.log(res)
|
||||
},
|
||||
fail(err) {
|
||||
console.error(err)
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// 设置蓝牙设备信息
|
||||
function setBluetoothDevice(device: any) {
|
||||
bluetoothConfig.RSSI = device.RSSI
|
||||
bluetoothConfig.localName = device.localName
|
||||
}
|
||||
|
||||
// 停止搜寻附近的蓝牙外围设备
|
||||
function stopBluetoothDevicesDiscovery() {
|
||||
uni.stopBluetoothDevicesDiscovery({
|
||||
success(res) {
|
||||
console.log(res)
|
||||
},
|
||||
fail(err) {
|
||||
console.error(err)
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 搜索蓝牙设备⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️
|
||||
*/
|
||||
async function handleSearch() {
|
||||
try {
|
||||
bluetoothConfig.btnIsLoading = true
|
||||
bluetoothConfig.isCloseBlue = false
|
||||
// 判断蓝牙是否打开
|
||||
if (!config.ISOPEN) {
|
||||
await openBluetoothAdapter()
|
||||
}
|
||||
const device = await startBluetoothDevicesDiscovery()
|
||||
console.log('搜索成功的设备:', device)
|
||||
await createBLEConnection()
|
||||
await getServiceAndCharacteristicId()
|
||||
bluetoothConfig.status = 1
|
||||
setBluetoothDevice(device)
|
||||
console.log(bluetoothConfig)
|
||||
bluetoothConfig.btnIsLoading = false
|
||||
}
|
||||
catch (error) {
|
||||
console.error(error)
|
||||
bluetoothConfig.btnIsLoading = false
|
||||
closeBluetoothAdapter()
|
||||
clearBuletoothConfig()
|
||||
uni.showToast({
|
||||
title: error.message || '未知错误',
|
||||
icon: 'none',
|
||||
duration: 1500,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 获取蓝牙设备
|
||||
async function startBluetoothDevicesDiscovery(targetName = 'ECU-2370171956', timeout = config.TIMEOUT): Promise<any> {
|
||||
return new Promise((resolve, reject) => {
|
||||
let isCompleted = false // 防止多次 resolve/reject
|
||||
|
||||
const cleanup = () => {
|
||||
if (!isCompleted) {
|
||||
isCompleted = true
|
||||
stopBluetoothDevicesDiscovery()
|
||||
uni.hideLoading()
|
||||
}
|
||||
}
|
||||
|
||||
// 启动搜索
|
||||
uni.startBluetoothDevicesDiscovery({
|
||||
success: (res) => {
|
||||
// 搜索异常
|
||||
if (res.errno !== 0) {
|
||||
cleanup()
|
||||
return reject(new Error('启动蓝牙搜索失败'))
|
||||
}
|
||||
|
||||
// 搜索成功
|
||||
uni.showLoading({ title: '正在搜索蓝牙设备...' })
|
||||
uni.onBluetoothDeviceFound((e: any) => {
|
||||
if (isCompleted) {
|
||||
return
|
||||
};
|
||||
const devices = e.devices
|
||||
for (let i = 0; i < devices.length; i++) {
|
||||
if (devices[i].localName === targetName) {
|
||||
bluetoothConfig.deviceId = devices[i].deviceId
|
||||
cleanup()
|
||||
return resolve(devices[i]) // 找到设备,成功返回
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// 设置超时
|
||||
setTimeout(() => {
|
||||
if (!isCompleted) {
|
||||
cleanup()
|
||||
reject(new Error('设备搜索超时'))
|
||||
}
|
||||
}, timeout)
|
||||
},
|
||||
fail: (err) => {
|
||||
cleanup()
|
||||
reject(new Error(`获取蓝牙设备失败: ${err.errMsg || err}`))
|
||||
},
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// 创建低功率蓝牙
|
||||
function createBLEConnection() {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!bluetoothConfig.deviceId) {
|
||||
return reject(new Error('蓝牙设备id未知'))
|
||||
}
|
||||
|
||||
// 创建低功率蓝牙
|
||||
uni.createBLEConnection({
|
||||
deviceId: bluetoothConfig.deviceId,
|
||||
success: (res: any) => {
|
||||
const { errCode } = res
|
||||
if (errCode === 0) {
|
||||
console.log('创建低功率蓝牙成功', res)
|
||||
return resolve(res)
|
||||
}
|
||||
else {
|
||||
bluetoothConfig.status = 0
|
||||
reject(new Error(res.errMsg || '创建低功率蓝牙失败'))
|
||||
}
|
||||
},
|
||||
fail: (err) => {
|
||||
bluetoothConfig.status = 0
|
||||
reject(new Error(`创建低功率蓝牙失败: ${err.errMsg || err}`))
|
||||
},
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 寻找匹配的ids
|
||||
* @param services 服务列表
|
||||
* @param targetSubServiceIds 目标子服务ID
|
||||
* @param returnFirst 是否返回第一个匹配项
|
||||
*/
|
||||
function findMatchingIds(services: any[] = [], targetSubServiceIds: string[] = [], returnFirst = true) {
|
||||
const matches = []
|
||||
|
||||
for (let i = 0; i < services.length; i++) {
|
||||
const serviceId = services[i].uuid
|
||||
const subServiceId = serviceId.substring(0, 8).toUpperCase() // 统一转大写避免大小写问题
|
||||
|
||||
if (targetSubServiceIds.includes(subServiceId)) {
|
||||
matches.push(services[i])
|
||||
if (returnFirst) {
|
||||
return matches[0] // 返回第一个匹配项
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return returnFirst ? null : matches // 如果没找到,返回 null 或空数组
|
||||
}
|
||||
|
||||
// 获取服务id和特征值id
|
||||
function getServiceAndCharacteristicId() {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!bluetoothConfig.deviceId) {
|
||||
reject(new Error('初始化服务id和特征值id失败'))
|
||||
}
|
||||
|
||||
// 获取蓝牙设备所有服务(service)。
|
||||
uni.getBLEDeviceServices({
|
||||
deviceId: bluetoothConfig.deviceId,
|
||||
success: (res: any) => {
|
||||
const { services } = res
|
||||
|
||||
if (services && services.length > 0) {
|
||||
const serviceObj = findMatchingIds(services, ['0000FFF0', '6E400001'])
|
||||
|
||||
if (serviceObj) {
|
||||
bluetoothConfig.serviceId = serviceObj.uuid
|
||||
console.log('服务id:', bluetoothConfig.serviceId)
|
||||
|
||||
// 获取蓝牙设备某个服务中所有特征值(characteristic)。
|
||||
uni.getBLEDeviceCharacteristics({
|
||||
deviceId: bluetoothConfig.deviceId,
|
||||
serviceId: bluetoothConfig.serviceId,
|
||||
success: (rs: any) => {
|
||||
const { characteristics } = rs
|
||||
if (characteristics && characteristics.length > 0) {
|
||||
// 获取读取特征值
|
||||
const characteristicIdObj = findMatchingIds(characteristics, ['0000FFF6', '6E400003'])
|
||||
if (characteristicIdObj) {
|
||||
bluetoothConfig.characteristicId = characteristicIdObj.uuid
|
||||
}
|
||||
|
||||
// 获取写入特征值
|
||||
const writeCharacteristicIdObj = findMatchingIds(characteristics, ['6E400002'])
|
||||
if (writeCharacteristicIdObj) {
|
||||
bluetoothConfig.writeCharacteristicId = writeCharacteristicIdObj.uuid
|
||||
}
|
||||
console.log('特征值获取', bluetoothConfig.characteristicId, bluetoothConfig.writeCharacteristicId)
|
||||
}
|
||||
startNotify()
|
||||
resolve({
|
||||
serviceId: bluetoothConfig.serviceId,
|
||||
characteristicId: bluetoothConfig.characteristicId,
|
||||
writeCharacteristicId: bluetoothConfig.writeCharacteristicId,
|
||||
})
|
||||
},
|
||||
fail: () => {
|
||||
reject(new Error('获取蓝牙设备所有特征值失败------'))
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
fail: () => {
|
||||
reject(new Error('获取蓝牙设备所有服务失败------'))
|
||||
},
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// 清除蓝牙配置
|
||||
function clearBuletoothConfig() {
|
||||
bluetoothConfig.status = -1
|
||||
bluetoothConfig.RSSI = 0
|
||||
bluetoothConfig.characteristicId = ''
|
||||
bluetoothConfig.serviceId = ''
|
||||
bluetoothConfig.deviceId = ''
|
||||
bluetoothConfig.localName = ''
|
||||
bluetoothConfig.writeCharacteristicId = ''
|
||||
bluetoothConfig.isCloseBlue = false
|
||||
bluetoothConfig.currentInstruct = ''
|
||||
}
|
||||
|
||||
// 关闭低功率蓝牙
|
||||
function closeBLEConnection() {
|
||||
if (!bluetoothConfig.deviceId)
|
||||
return
|
||||
uni.closeBLEConnection({
|
||||
deviceId: bluetoothConfig.deviceId,
|
||||
success: (res: any) => {
|
||||
console.log('关闭低功率蓝牙成功', res)
|
||||
bluetoothConfig.isCloseBlue = true
|
||||
clearBuletoothConfig()
|
||||
},
|
||||
fail: (err) => {
|
||||
clearBuletoothConfig()
|
||||
console.log('关闭低功率蓝牙失败', err)
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// 点击指令
|
||||
async function handleCommand(value: any, key: string, index: number) {
|
||||
const comm = value.key
|
||||
bluetoothConfig.currentInstruct = key
|
||||
try {
|
||||
commandList[key].btnLoading = true
|
||||
switch (comm) {
|
||||
case 'openLock': { // 开锁
|
||||
const buffer = new ArrayBuffer(5)
|
||||
const dataView = new DataView(buffer)
|
||||
dataView.setUint32(0, 0x67740082)
|
||||
dataView.setUint8(4, 0x82)
|
||||
await writeBLECharacteristicValueWithBuff(buffer)
|
||||
break
|
||||
}
|
||||
}
|
||||
commandList[key].btnLoading = false
|
||||
}
|
||||
catch (err) {
|
||||
commandList[key].btnLoading = false
|
||||
console.error(err)
|
||||
}
|
||||
}
|
||||
|
||||
function writeBLECharacteristicValueWithBuff(buffer: any) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (
|
||||
!buffer
|
||||
&& !bluetoothConfig.writeCharacteristicId
|
||||
&& !bluetoothConfig.serviceId
|
||||
&& !bluetoothConfig.deviceId) {
|
||||
return
|
||||
}
|
||||
console.log('writeBLECharacteristicValueWithBuff', buffer)
|
||||
|
||||
uni.writeBLECharacteristicValue({
|
||||
deviceId: bluetoothConfig.deviceId,
|
||||
serviceId: bluetoothConfig.serviceId,
|
||||
characteristicId: bluetoothConfig.writeCharacteristicId,
|
||||
value: buffer,
|
||||
success(res) {
|
||||
resolve(res)
|
||||
console.log('writeBLECharacteristicValue success', res.errMsg)
|
||||
},
|
||||
fail(err: any) {
|
||||
reject(new Error(err.errMsg))
|
||||
},
|
||||
complete(res) {},
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// 启用低功耗蓝牙设备特征值变化时的 notify 功能
|
||||
function startNotify() {
|
||||
uni.notifyBLECharacteristicValueChange({
|
||||
state: true,
|
||||
deviceId: bluetoothConfig.deviceId,
|
||||
serviceId: bluetoothConfig.serviceId,
|
||||
characteristicId: bluetoothConfig.characteristicId,
|
||||
success() {
|
||||
console.log('notifyBLECharacteristicValueChange success')
|
||||
verification()
|
||||
},
|
||||
fail(err) {
|
||||
console.log('notifyBLECharacteristicValueChange fail', err)
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
async function verification(mac: any = undefined, key1: any = undefined, key2: any = undefined) {
|
||||
if (mac && key1 && key2) {
|
||||
// 数据总长度为21,第一天长度20
|
||||
const buffer = new ArrayBuffer(20)
|
||||
const dataView = new DataView(buffer)
|
||||
dataView.setUint32(0, 0x6774108A)
|
||||
const sourceKey1 = mac + key1
|
||||
const AESEncrypt = AES128ECBEncrypt(sourceKey1, key2)
|
||||
if (AESEncrypt.length !== 32) {
|
||||
return
|
||||
}
|
||||
for (let i = 0; i < 4; i++) {
|
||||
const subStr = AESEncrypt.substring(i * 8, i * 8 + 8, 16)
|
||||
dataView.setUint32(i * 4 + 4, Number.parseInt(subStr, 16)) // 4字节01523947(16进制)-->>10进制数字-->放入dateView
|
||||
}
|
||||
let i
|
||||
let bcc // 17为数据长度16+1
|
||||
for (i = 0; i < 17; i++) {
|
||||
bcc ^= dataView.getInt8(i + 3)
|
||||
}
|
||||
const buffer2 = new ArrayBuffer(1)
|
||||
const dataView2 = new DataView(buffer2)
|
||||
dataView2.setUint8(0, bcc)
|
||||
console.log('验证---------------->', bufTohex(buffer))
|
||||
writeBLECharacteristicValueWithBuff(buffer)
|
||||
setTimeout(() => {
|
||||
writeBLECharacteristicValueWithBuff(buffer2)
|
||||
}, 300)
|
||||
}
|
||||
else {
|
||||
const buffer = new ArrayBuffer(5)
|
||||
const dataView = new DataView(buffer)
|
||||
dataView.setUint32(0, 0x6774008A)
|
||||
dataView.setUint8(4, 0x8A)
|
||||
await writeBLECharacteristicValueWithBuff(buffer)
|
||||
}
|
||||
}
|
||||
|
||||
let verificationTimes = 0
|
||||
function distributeCommandsWithDataStr(hex: string) {
|
||||
const cmd = hex.substring(6, 8)
|
||||
const code = hex.substring(8, 10)
|
||||
let errCode: any = code
|
||||
const desDic: any = {}
|
||||
let 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 (Number.parseInt(code, 16) === 149) {
|
||||
//
|
||||
des = '摄像头正在工作中'
|
||||
errCode = Number.parseInt(code, 16)
|
||||
}
|
||||
else if (Number.parseInt(code, 16) === 150) {
|
||||
//
|
||||
des = '摄像头离线'
|
||||
errCode = Number.parseInt(code, 16)
|
||||
}
|
||||
else if (Number.parseInt(code, 16) === 151) {
|
||||
//
|
||||
des = '车辆不在使用中(临时停车或是还车中)无法使用摄像头还车'
|
||||
errCode = Number.parseInt(code, 16)
|
||||
}
|
||||
else if (Number.parseInt(code, 16) === 152) {
|
||||
//
|
||||
des = '执行命令超时'
|
||||
errCode = Number.parseInt(code, 16)
|
||||
}
|
||||
else if (Number.parseInt(code, 16) === 153) {
|
||||
//
|
||||
des = '摄像头执行失败'
|
||||
errCode = Number.parseInt(code, 16)
|
||||
}
|
||||
else if (Number.parseInt(code, 16) === 154) {
|
||||
//
|
||||
des = '不在站点中或没有垂直停车'
|
||||
errCode = Number.parseInt(code, 16)
|
||||
}
|
||||
else {
|
||||
des = `未知错误码${code}`
|
||||
errCode = code
|
||||
}
|
||||
|
||||
console.log('distributeCommandsWithDataStr', hex)
|
||||
desDic.errCode = errCode
|
||||
desDic.des = des
|
||||
|
||||
// 提示显示
|
||||
if (bluetoothConfig.currentInstruct) {
|
||||
const message = commandList[bluetoothConfig.currentInstruct].msg + desDic.des
|
||||
notifyRef.value.show({
|
||||
type: 'success',
|
||||
message,
|
||||
safeAreaInsetTop: true,
|
||||
})
|
||||
}
|
||||
|
||||
if (cmd === '4a') {
|
||||
// 验证
|
||||
if (verificationTimes >= 1) {
|
||||
verificationTimes = 0
|
||||
|
||||
if (errCode === -6) {
|
||||
verificationTimes = 1
|
||||
verification(desDic.mac, desDic.key, '6666660000888888')
|
||||
return
|
||||
}
|
||||
}
|
||||
else {
|
||||
verificationTimes += 1
|
||||
}
|
||||
if (errCode === -6) {
|
||||
verification(desDic.mac, desDic.key, '6666660000888888')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 监听蓝牙连接状态改变
|
||||
uni.onBLEConnectionStateChange(async (res) => {
|
||||
console.log('蓝牙连接状态改变', res)
|
||||
try {
|
||||
const { connected } = res
|
||||
if (!connected && !bluetoothConfig.isCloseBlue) {
|
||||
bluetoothConfig.status = 0
|
||||
|
||||
await createBLEConnection()
|
||||
await getServiceAndCharacteristicId()
|
||||
bluetoothConfig.status = 1
|
||||
|
||||
// if (reconnectNum < 3) {
|
||||
// await createBLEConnection()
|
||||
// reconnectNum += 1
|
||||
// }
|
||||
// else {
|
||||
// closeBLEConnection()
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
console.log('蓝牙连接状态改变失败', err)
|
||||
}
|
||||
})
|
||||
|
||||
// 监听低功耗蓝牙设备的特征值变化事件
|
||||
uni.onBLECharacteristicValueChange((res) => {
|
||||
console.log('onBLECharacteristicValueChange-----------》', res)
|
||||
const hex = bufTohex(res.value)
|
||||
if (hex.length >= 10) {
|
||||
const header = hex.substring(0, 4)
|
||||
if (header === '6774') {
|
||||
const contentL1 = hex.substring(4, 6)
|
||||
const contentL = Number.parseInt(contentL1, 16)
|
||||
if ((contentL + 5) * 2 === hex.length) {
|
||||
distributeCommandsWithDataStr(hex)
|
||||
}
|
||||
else if ((contentL + 5) * 2 > hex.length) {
|
||||
// self.responseStr = hex
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
if (config.ISOPEN) {
|
||||
closeBluetoothAdapter()
|
||||
}
|
||||
closeBLEConnection()
|
||||
clearBuletoothConfig()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<z-paging
|
||||
ref="paging"
|
||||
bg-color="#f3f3f3"
|
||||
>
|
||||
<view class="container">
|
||||
<!-- 编号输入框 -->
|
||||
<view class="card_bar">
|
||||
<!-- 车辆编号 -->
|
||||
<view class="card_item card_border">
|
||||
<view class="card_item_title">
|
||||
<uv-button
|
||||
plain style="margin-right: 25rpx;"
|
||||
@click="handleType"
|
||||
>
|
||||
<uv-icon
|
||||
name="jiantou_shangxiaqiehuan"
|
||||
custom-prefix="custom-icon"
|
||||
size="20"
|
||||
/>
|
||||
<text>{{ codeType === 'car' ? '车辆编号' : "中控SN" }}</text>
|
||||
</uv-button>
|
||||
</view>
|
||||
<view class="card_item_value">
|
||||
<uv-input
|
||||
v-model="code"
|
||||
placeholder="请输入或扫码"
|
||||
>
|
||||
<template #suffix>
|
||||
<uv-icon name="scan" size="22" @click="handleScan" />
|
||||
</template>
|
||||
</uv-input>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 设备信息 -->
|
||||
<view class="card_item">
|
||||
<view class="card_item_title">
|
||||
设备编号:
|
||||
</view>
|
||||
<view class="card_item_value">
|
||||
{{ bluetoothConfig.localName || '--' }}
|
||||
</view>
|
||||
</view>
|
||||
<view class="device_bar">
|
||||
<text
|
||||
:style="{
|
||||
color: bluetoothConfig.status === 0 ? '#f00' : bluetoothConfig.status === 1 ? '#04b604' : '',
|
||||
}"
|
||||
>
|
||||
状态:{{ bluetoothConfig.status === -1 ? '未连接' : bluetoothConfig.status === 0 ? '连接失败' : '连接成功' }}
|
||||
</text>
|
||||
<text>信号强度:{{ bluetoothConfig.RSSI || 0 }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 配置信息栏 -->
|
||||
<view class="card_bar u-m-t-30">
|
||||
<uv-list>
|
||||
<uv-list-item
|
||||
v-for="(value, key, index) in commandList"
|
||||
:key="key"
|
||||
:custom-style="list_item_style"
|
||||
>
|
||||
<view class="list-item">
|
||||
<view class="text_bar">
|
||||
<view>{{ value.title }} </view>
|
||||
</view>
|
||||
<view class="btn_bar">
|
||||
<uv-button
|
||||
type="primary"
|
||||
:loading="value.btnLoading"
|
||||
:loading-text="value.btnLoadingText"
|
||||
:text="value.btnText"
|
||||
:disabled="bluetoothConfig.status !== 1"
|
||||
:custom-style="list_item_btn_style"
|
||||
@click="handleCommand(value, key, index)"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</uv-list-item>
|
||||
</uv-list>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<template #bottom>
|
||||
<view class="bottom-button-zaping">
|
||||
<uv-button
|
||||
v-if="bluetoothConfig.status === -1"
|
||||
type="primary"
|
||||
text="连接设备"
|
||||
:loading="bluetoothConfig.btnIsLoading"
|
||||
loading-text="正在连接中"
|
||||
@click="handleSearch"
|
||||
/>
|
||||
<uv-button
|
||||
v-if="bluetoothConfig.status === 1"
|
||||
type="error"
|
||||
text="断开连接"
|
||||
@click="closeBLEConnection"
|
||||
/>
|
||||
<uv-button
|
||||
v-if="bluetoothConfig.status === 0"
|
||||
type="primary"
|
||||
text="连接设备"
|
||||
:loading="true"
|
||||
loading-text="正在重连"
|
||||
/>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<uv-notify ref="notifyRef" />
|
||||
</z-paging>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.container {
|
||||
width: calc(100% - 40rpx);
|
||||
padding: 10rpx 20rpx;
|
||||
|
||||
.card_bar {
|
||||
width: calc(100% - 40rpx);
|
||||
background-color: #fff;
|
||||
border-radius: 10rpx;
|
||||
box-shadow: 0 0 10rpx rgba(0, 0, 0, 0.1);
|
||||
padding: 20rpx;
|
||||
|
||||
.card_item {
|
||||
padding: 20rpx 0;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.card_item_title {
|
||||
width: 100rpx;
|
||||
font-size: 30rpx;
|
||||
color: $font-gray;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.card_item_value {
|
||||
flex: 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
.device_bar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-size: 28rpx;
|
||||
color: $font-gray;
|
||||
}
|
||||
}
|
||||
.card_border {
|
||||
border-bottom: 2rpx solid #d5d0d0;
|
||||
}
|
||||
|
||||
.list-item {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.text_bar {
|
||||
font-size: 28rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.result {
|
||||
margin-left: 30rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -1,4 +1,5 @@
|
||||
<script lang="ts" setup>
|
||||
import { executeCommandAPI } from '@/api/centerControl'
|
||||
import { useCenterControlStore } from '@/store'
|
||||
|
||||
// 列表数据
|
||||
@ -20,25 +21,32 @@ const list_item_btn_style = computed(() => {
|
||||
}
|
||||
})
|
||||
|
||||
const type = ref<'car' | 'sn' | ''>('')
|
||||
const code = ref<string>('')
|
||||
|
||||
const list = reactive({
|
||||
online: {
|
||||
msg: 'asdas',
|
||||
name: '在线测试',
|
||||
key: 'online',
|
||||
findBike: {
|
||||
msg: '',
|
||||
name: '寻车铃',
|
||||
key: 'FIND_BIKE',
|
||||
status: 'none', // none,loading,success,fail
|
||||
tip: '',
|
||||
btnText: '执行',
|
||||
btnLoadingText: '执行中',
|
||||
btnLoading: false,
|
||||
},
|
||||
findEbike: {
|
||||
msg: 'asdas',
|
||||
name: '在线测试',
|
||||
key: 'findEbike',
|
||||
status: 'none', // none,loading,success,fail
|
||||
gps: {
|
||||
msg: '',
|
||||
name: 'gps测试',
|
||||
key: 'GPS',
|
||||
status: 'none',
|
||||
tip: '',
|
||||
btnText: '执行',
|
||||
btnLoadingText: '执行中',
|
||||
btnLoading: false,
|
||||
},
|
||||
openLock: {
|
||||
msg: '',
|
||||
name: '打开电池锁',
|
||||
key: 'OPEN_BATTERY_LOCK',
|
||||
status: 'none',
|
||||
tip: '',
|
||||
btnText: '执行',
|
||||
btnLoadingText: '执行中',
|
||||
@ -52,24 +60,64 @@ function initListItem(key: string) {
|
||||
list[key].msg = ''
|
||||
}
|
||||
|
||||
function handleBtnClick(value: any, key: string, index: number) {
|
||||
// 执行按钮点击
|
||||
async function handleBtnClick(value: any, key: string, index: number) {
|
||||
initListItem(key)
|
||||
list[key].btnLoading = true
|
||||
list[key].status = 'loading'
|
||||
setTimeout(() => {
|
||||
|
||||
try {
|
||||
const res = await executeCommandAPI({
|
||||
ecuSn: centerStore.queryByBike.ecuSn,
|
||||
bikeCode: centerStore.queryByBike.bikeCode,
|
||||
commandCode: value.key,
|
||||
})
|
||||
}
|
||||
catch (e) {
|
||||
console.error('中控测试报错------>', e)
|
||||
list[key].status = 'fail'
|
||||
list[key].msg = e.message
|
||||
list[key].btnLoading = false
|
||||
list[key].status = 'success'
|
||||
list[key].msg = '执行成功'
|
||||
}, 2000)
|
||||
}
|
||||
}
|
||||
|
||||
watch(() => props.code, (newVal) => {
|
||||
code.value = newVal
|
||||
}, { immediate: true })
|
||||
// 初始化所有测试项
|
||||
async function initAllTest() {
|
||||
for (const key in list) {
|
||||
list[key].status = 'none'
|
||||
list[key].msg = ''
|
||||
}
|
||||
|
||||
watch(() => props.type, (newVal) => {
|
||||
type.value = newVal
|
||||
}, { immediate: true })
|
||||
// 循环测试项(目前点击后所有命令同时执行,后面根据实际问题考虑改为点击后一个执行完再执行下一个)
|
||||
for (const key in list) {
|
||||
console.log(key)
|
||||
list[key].btnLoading = true
|
||||
list[key].status = 'loading'
|
||||
|
||||
try {
|
||||
const res = await executeCommandAPI({
|
||||
ecuSn: centerStore.queryByBike.ecuSn,
|
||||
bikeCode: centerStore.queryByBike.bikeCode,
|
||||
commandCode: list[key].key,
|
||||
})
|
||||
}
|
||||
catch (e) {
|
||||
console.error('中控测试报错------>', e)
|
||||
list[key].status = 'fail'
|
||||
list[key].msg = e.message
|
||||
list[key].btnLoading = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 循环执行命令
|
||||
function loopExecuteCommand() {
|
||||
initAllTest()
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
loopExecuteCommand,
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -113,8 +161,9 @@ watch(() => props.type, (newVal) => {
|
||||
<uv-button
|
||||
:loading="value.btnLoading"
|
||||
:loading-text="value.btnLoadingText"
|
||||
type="primary"
|
||||
:text="value.btnText"
|
||||
:disabled="centerStore.isOnline !== 1"
|
||||
type="primary" :text="value.btnText"
|
||||
:custom-style="list_item_btn_style"
|
||||
@click="handleBtnClick(value, key, index)"
|
||||
/>
|
||||
|
||||
@ -3,8 +3,6 @@ import { checkSnOrBikeCodeAPI } from '@/api/centerControl'
|
||||
import { useCenterControlStore } from '@/store'
|
||||
import { scanCode } from '@/utils/tools'
|
||||
|
||||
const props = defineProps(['code', 'type'])
|
||||
const emits = defineEmits(['change', 'update:code', 'update:type'])
|
||||
const store = useCenterControlStore()
|
||||
const code = ref('')
|
||||
const btnLoading = ref(false)
|
||||
@ -38,6 +36,14 @@ function handleInput(val: string) {
|
||||
|
||||
// 连接
|
||||
async function handleConnect() {
|
||||
if (!code.value) {
|
||||
uni.showToast({
|
||||
title: '请输入或扫描编号',
|
||||
icon: 'none',
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
btnLoading.value = true
|
||||
try {
|
||||
const isOnline = await checkSnOrBikeCodeAPI(store.queryByBike)
|
||||
@ -49,6 +55,16 @@ async function handleConnect() {
|
||||
btnLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
watch(() => store.queryByBike, (newVal) => {
|
||||
console.log('queryByBike', newVal)
|
||||
if (newVal.bikeCode) {
|
||||
return code.value = newVal.bikeCode
|
||||
}
|
||||
if (newVal.ecuSn) {
|
||||
return code.value = newVal.ecuSn
|
||||
}
|
||||
}, { immediate: true, deep: true })
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
<script lang="ts" setup>
|
||||
import { useCenterControlStore } from '@/store'
|
||||
import executionList from './components/executionList.vue'
|
||||
import scanByTest from './components/scanByTest.vue'
|
||||
|
||||
@ -9,6 +10,13 @@ definePage({
|
||||
navigationBarTextStyle: 'white',
|
||||
},
|
||||
})
|
||||
|
||||
const executionListRef = ref(null)
|
||||
const store = useCenterControlStore()
|
||||
|
||||
function handleTestClick() {
|
||||
executionListRef.value?.loopExecuteCommand()
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -21,12 +29,17 @@ definePage({
|
||||
<scan-by-test />
|
||||
|
||||
<!-- 测试中控执行列表 -->
|
||||
<executionList />
|
||||
<executionList ref="executionListRef" />
|
||||
</view>
|
||||
|
||||
<template #bottom>
|
||||
<view class="bottom-button-zaping">
|
||||
<uv-button type="primary" text="自动化测试" />
|
||||
<uv-button
|
||||
:disabled="store.isOnline !== 1"
|
||||
type="primary"
|
||||
text="自动化测试"
|
||||
@click="handleTestClick"
|
||||
/>
|
||||
</view>
|
||||
</template>
|
||||
</z-paging>
|
||||
|
||||
212
src/pages-sub/operation/carOperation/carOperation.vue
Normal file
212
src/pages-sub/operation/carOperation/carOperation.vue
Normal file
@ -0,0 +1,212 @@
|
||||
<script lang="ts" setup>
|
||||
import { batchRemoveInventoryAPI, getInventoryListAPI, getOperatoringAllListAPI } from '@/api/warehouse'
|
||||
import { useAppStore, useOperatorStore } from '@/store'
|
||||
|
||||
definePage({
|
||||
style: {
|
||||
navigationBarTitleText: '运营车辆',
|
||||
navigationBarBackgroundColor: '#1488f5',
|
||||
navigationBarTextStyle: 'white',
|
||||
},
|
||||
})
|
||||
|
||||
const appStore = useAppStore()
|
||||
const operatorStore = useOperatorStore()
|
||||
const paging = ref<any>(null)
|
||||
const list = ref<any>([])
|
||||
const checkboxValue = ref<any>([])
|
||||
const operatorAllList = ref<any>([])
|
||||
const regionId_list = ref<any>([])
|
||||
|
||||
const modalRef = ref<any>(null)
|
||||
|
||||
// 刷新列表
|
||||
function refreshList() {
|
||||
list.value = []
|
||||
}
|
||||
|
||||
// 获取列表
|
||||
async function queryList(pageNo: number, pageSize: number) {
|
||||
try {
|
||||
uni.showLoading({
|
||||
title: '加载中',
|
||||
})
|
||||
const { records } = await getInventoryListAPI({
|
||||
pageNum: pageNo,
|
||||
pageSize,
|
||||
})
|
||||
uni.hideLoading()
|
||||
paging.value.complete(records)
|
||||
}
|
||||
catch (e) {
|
||||
uni.hideLoading()
|
||||
paging.value.complete(false)
|
||||
}
|
||||
}
|
||||
|
||||
// 车辆上架
|
||||
function vehicleHoisting() {
|
||||
modalRef.value.open()
|
||||
}
|
||||
|
||||
// 获取列表
|
||||
async function getOperatoringAllList() {
|
||||
try {
|
||||
const res = await getOperatoringAllListAPI()
|
||||
if (res && res.length > 0) {
|
||||
regionId_list.value = res.map((item) => {
|
||||
return {
|
||||
text: item.regionName,
|
||||
value: item.regionId,
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
console.error(new Error('获取运营区'))
|
||||
}
|
||||
}
|
||||
|
||||
// 提交
|
||||
async function submit() {
|
||||
try {
|
||||
uni.showLoading({
|
||||
title: '正在提交中。。。',
|
||||
})
|
||||
const res = await batchRemoveInventoryAPI({
|
||||
bikeCodes: checkboxValue.value,
|
||||
})
|
||||
uni.hideLoading()
|
||||
modalRef.value.close()
|
||||
modalRef.value.closeLoading()
|
||||
paging.value.reload()
|
||||
checkboxValue.value = []
|
||||
}
|
||||
catch (e) {
|
||||
modalRef.value.closeLoading()
|
||||
uni.hideLoading()
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
operatorAllList.value = await operatorStore.getOperatorList()
|
||||
getOperatoringAllList()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view>
|
||||
<z-paging
|
||||
ref="paging"
|
||||
v-model="list"
|
||||
:default-page-no="Number('1')"
|
||||
:default-page-size="Number('10')"
|
||||
:auto-show-back-to-top="Boolean(true)"
|
||||
bg-color="#f3f3f3"
|
||||
@query="queryList"
|
||||
@refresher-touchend="refreshList"
|
||||
>
|
||||
<view class="container">
|
||||
<uv-checkbox-group
|
||||
v-model="checkboxValue"
|
||||
size="20"
|
||||
>
|
||||
<view class="list-panel">
|
||||
<view
|
||||
v-for="(item) in list"
|
||||
:key="item.bikeInfoId"
|
||||
class="list-item"
|
||||
>
|
||||
<view class="header d-flex align-center justify-between">
|
||||
<view class="d-flex align-center">
|
||||
<uv-checkbox
|
||||
:name="item.bikeCode"
|
||||
/>
|
||||
</view>
|
||||
<uv-tags :text="appStore.translateDictValue(item.status, 'BIKE_STATUS')" size="mini" />
|
||||
</view>
|
||||
<view class="body">
|
||||
<view class="icon">
|
||||
<uv-icon
|
||||
name="dianpingche"
|
||||
custom-prefix="custom-icon"
|
||||
size="34"
|
||||
color="#1488f5"
|
||||
/>
|
||||
</view>
|
||||
<view class="content fz-28">
|
||||
<view class="brand">
|
||||
车辆编号:{{ item.bikeCode }}
|
||||
</view>
|
||||
<view class="brand">
|
||||
运营商:{{ operatorStore.translateOperatorId(item.operatorId, operatorAllList) }}
|
||||
</view>
|
||||
<view class="brand">
|
||||
运营区域:{{ item.regionName }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</uv-checkbox-group>
|
||||
</view>
|
||||
<template #bottom>
|
||||
<view class="bottom-button-zaping">
|
||||
<uv-button :disabled="checkboxValue.length <= 0" type="primary" text="车辆下架" @click="vehicleHoisting" />
|
||||
</view>
|
||||
</template>
|
||||
</z-paging>
|
||||
|
||||
<uv-modal ref="modalRef" title="车辆下架" confirm-text="下架" show-cancel-button :async-close="true" @confirm="submit">
|
||||
<view class="tips">
|
||||
当前选择下架的车辆:{{ checkboxValue.join(",") }}
|
||||
</view>
|
||||
</uv-modal>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.container {
|
||||
.list-panel {
|
||||
padding: 10px 5px 0px 0px;
|
||||
width: 100%;
|
||||
|
||||
.list-item {
|
||||
padding: 10px;
|
||||
margin: 10px;
|
||||
border-radius: 5px;
|
||||
background-color: #fff;
|
||||
box-shadow: 2px 2px 4px 2px rgba(212, 212, 212, 0.6);
|
||||
|
||||
.header {
|
||||
width: 100%;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.body {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
margin-left: 20rpx;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
padding-bottom: 10rpx;
|
||||
|
||||
.brand {
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tips {
|
||||
width: 100%;
|
||||
font-size: 28rpx;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
</style>
|
||||
236
src/pages-sub/warehouse/vehicleWarehouse/vehicleWarehouse.vue
Normal file
236
src/pages-sub/warehouse/vehicleWarehouse/vehicleWarehouse.vue
Normal file
@ -0,0 +1,236 @@
|
||||
<script lang="ts" setup>
|
||||
import { batchAddInventoryAPI, getOperatoringAllListAPI, getUnInventoryListAPI } from '@/api/warehouse'
|
||||
import { useAppStore, useOperatorStore } from '@/store'
|
||||
|
||||
definePage({
|
||||
style: {
|
||||
navigationBarTitleText: '整车仓库',
|
||||
navigationBarBackgroundColor: '#1488f5',
|
||||
navigationBarTextStyle: 'white',
|
||||
},
|
||||
})
|
||||
|
||||
const appStore = useAppStore()
|
||||
const operatorStore = useOperatorStore()
|
||||
const paging = ref<any>(null)
|
||||
const list = ref<any>([])
|
||||
const checkboxValue = ref<any>([])
|
||||
const operatorAllList = ref<any>([])
|
||||
const regionId_list = ref<any>([])
|
||||
|
||||
const modalRef = ref<any>(null)
|
||||
const form = ref<any>(null)
|
||||
const formData = reactive<any>({
|
||||
regionId: '',
|
||||
})
|
||||
const rules = {
|
||||
regionId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
message: '请选择运营区域',
|
||||
trigger: ['change'],
|
||||
},
|
||||
}
|
||||
|
||||
// 刷新列表
|
||||
function refreshList() {
|
||||
list.value = []
|
||||
}
|
||||
|
||||
// 获取列表
|
||||
async function queryList(pageNo: number, pageSize: number) {
|
||||
try {
|
||||
uni.showLoading({
|
||||
title: '加载中',
|
||||
})
|
||||
const { records } = await getUnInventoryListAPI({
|
||||
pageNum: pageNo,
|
||||
pageSize,
|
||||
})
|
||||
uni.hideLoading()
|
||||
paging.value.complete(records)
|
||||
}
|
||||
catch (e) {
|
||||
uni.hideLoading()
|
||||
paging.value.complete(false)
|
||||
}
|
||||
}
|
||||
|
||||
// 车辆上架
|
||||
function vehicleHoisting() {
|
||||
modalRef.value.open()
|
||||
}
|
||||
|
||||
// 获取列表
|
||||
async function getOperatoringAllList() {
|
||||
try {
|
||||
const res = await getOperatoringAllListAPI()
|
||||
if (res && res.length > 0) {
|
||||
regionId_list.value = res.map((item) => {
|
||||
return {
|
||||
text: item.regionName,
|
||||
value: item.regionId,
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
console.error(new Error('获取运营区'))
|
||||
}
|
||||
}
|
||||
|
||||
// 提交
|
||||
async function submit() {
|
||||
try {
|
||||
const status = await form.value.validate()
|
||||
if (!status) {
|
||||
return
|
||||
}
|
||||
uni.showLoading({
|
||||
title: '正在提交中。。。',
|
||||
})
|
||||
const res = await batchAddInventoryAPI({
|
||||
bikeCodes: checkboxValue.value,
|
||||
regionId: formData.regionId,
|
||||
})
|
||||
uni.hideLoading()
|
||||
modalRef.value.close()
|
||||
modalRef.value.closeLoading()
|
||||
paging.value.reload()
|
||||
checkboxValue.value = []
|
||||
form.value.resetFields()
|
||||
}
|
||||
catch (e) {
|
||||
modalRef.value.closeLoading()
|
||||
uni.hideLoading()
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
operatorAllList.value = await operatorStore.getOperatorList()
|
||||
getOperatoringAllList()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view>
|
||||
<z-paging
|
||||
ref="paging"
|
||||
v-model="list"
|
||||
:default-page-no="Number('1')"
|
||||
:default-page-size="Number('10')"
|
||||
:auto-show-back-to-top="Boolean(true)"
|
||||
bg-color="#f3f3f3"
|
||||
@query="queryList"
|
||||
@refresher-touchend="refreshList"
|
||||
>
|
||||
<view class="container">
|
||||
<uv-checkbox-group
|
||||
v-model="checkboxValue"
|
||||
size="20"
|
||||
>
|
||||
<view class="list-panel">
|
||||
<view
|
||||
v-for="(item) in list"
|
||||
:key="item.bikeInfoId"
|
||||
class="list-item"
|
||||
>
|
||||
<view class="header d-flex align-center justify-between">
|
||||
<view class="d-flex align-center">
|
||||
<uv-checkbox
|
||||
:name="item.bikeCode"
|
||||
/>
|
||||
</view>
|
||||
<uv-tags :text="appStore.translateDictValue(item.status, 'BIKE_STATUS')" type="warning" size="mini" />
|
||||
</view>
|
||||
<view class="body">
|
||||
<view class="icon">
|
||||
<uv-icon
|
||||
name="dianpingche"
|
||||
custom-prefix="custom-icon"
|
||||
size="34"
|
||||
color="#1488f5"
|
||||
/>
|
||||
</view>
|
||||
<view class="content fz-28">
|
||||
<view class="brand">
|
||||
车辆编号:{{ item.bikeCode }}
|
||||
</view>
|
||||
<view class="num">
|
||||
运营商:{{ operatorStore.translateOperatorId(item.operatorId, operatorAllList) }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</uv-checkbox-group>
|
||||
</view>
|
||||
<template #bottom>
|
||||
<view class="bottom-button-zaping">
|
||||
<uv-button :disabled="checkboxValue.length <= 0" type="primary" text="车辆上架" @click="vehicleHoisting" />
|
||||
</view>
|
||||
</template>
|
||||
</z-paging>
|
||||
|
||||
<uv-modal ref="modalRef" title="车辆上架" confirm-text="上架" show-cancel-button :async-close="true" @confirm="submit">
|
||||
<view style="width: 100%;">
|
||||
<uv-form ref="form" label-position="left" :model="formData" :rules="rules" label-width="100">
|
||||
<uv-form-item label="车辆运营区:" prop="regionId" required>
|
||||
<uni-data-select
|
||||
v-model="formData.regionId"
|
||||
:localdata="regionId_list"
|
||||
placeholder="请选择"
|
||||
clear
|
||||
/>
|
||||
</uv-form-item>
|
||||
</uv-form>
|
||||
</view>
|
||||
</uv-modal>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.container {
|
||||
.list-panel {
|
||||
padding: 10px 5px 0px 0px;
|
||||
width: 100%;
|
||||
|
||||
.list-item {
|
||||
padding: 10px;
|
||||
margin: 10px;
|
||||
border-radius: 5px;
|
||||
background-color: #fff;
|
||||
box-shadow: 2px 2px 4px 2px rgba(212, 212, 212, 0.6);
|
||||
|
||||
.header {
|
||||
width: 100%;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.body {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
margin-left: 20rpx;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
padding-bottom: 10rpx;
|
||||
|
||||
.brand {
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tips {
|
||||
width: 100%;
|
||||
font-size: 28rpx;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
</style>
|
||||
@ -52,12 +52,12 @@ const rules = {
|
||||
message: '请输入中控SN',
|
||||
trigger: ['change', 'blur'],
|
||||
},
|
||||
batteryCode: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
message: '请输入电池编号',
|
||||
trigger: ['change', 'blur'],
|
||||
},
|
||||
// batteryCode: {
|
||||
// type: 'string',
|
||||
// required: true,
|
||||
// message: '请输入电池编号',
|
||||
// trigger: ['change', 'blur'],
|
||||
// },
|
||||
hasHelme: {
|
||||
type: 'boolean',
|
||||
required: true,
|
||||
@ -191,7 +191,7 @@ getOperatorList()
|
||||
</uni-section>
|
||||
<!-- 电池 -->
|
||||
<uni-section title="电池" type="line">
|
||||
<uv-form-item label="电池" prop="batteryCode" required>
|
||||
<uv-form-item label="电池" prop="batteryCode">
|
||||
<uv-input v-model="formData.batteryCode" placeholder="请扫描或输入电池" border="surround">
|
||||
<template #suffix>
|
||||
<uv-icon name="scan" color="#cbcad0" size="28" @click="codeScan('batteryCode')" />
|
||||
|
||||
@ -56,7 +56,7 @@ const btnList = ref([
|
||||
{
|
||||
key: 'bluetoothscan',
|
||||
name: '蓝牙扫码',
|
||||
path: '/pages/warehouse/bluetooth/bluetoothscan',
|
||||
path: '/pages-sub/bluetooth/bluetoothscan/bluetoothscan',
|
||||
customsrc: 'Bluetooth1',
|
||||
},
|
||||
{
|
||||
@ -86,7 +86,7 @@ const btnList = ref([
|
||||
{
|
||||
key: 'ebikehouse',
|
||||
name: '整车仓库',
|
||||
path: '/pages/warehouse/ebikehouse/ebikehouse',
|
||||
path: '/pages-sub/warehouse/vehicleWarehouse/vehicleWarehouse',
|
||||
customsrc: 'wode-wodecheku',
|
||||
},
|
||||
{
|
||||
@ -146,7 +146,7 @@ const btnList = ref([
|
||||
{
|
||||
key: 'vehicleoperation',
|
||||
name: '运营车辆',
|
||||
path: '/pages/warehouse/ebikehouse/vehicleoperation',
|
||||
path: '/pages-sub/operation/carOperation/carOperation',
|
||||
customsrc: 'daohang',
|
||||
},
|
||||
],
|
||||
|
||||
@ -105,6 +105,27 @@ export const useAppStore = defineStore(
|
||||
return list.length > 0 ? list[0].values : []
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据value值对字典数据进行翻译
|
||||
* @param value 值
|
||||
* @param code 字典code
|
||||
* @returns 字典值
|
||||
*/
|
||||
const translateDictValue = (value: any, code: string) => {
|
||||
const dictData: any = CacheManager.get('dictData')
|
||||
if (!dictData) {
|
||||
return value
|
||||
}
|
||||
const list: dicType = dictData.filter((item: any) => item.dicCode === code)
|
||||
if (list.length > 0) {
|
||||
const values = list[0].values
|
||||
if (values && values.length > 0) {
|
||||
return values.find((item: any) => item.dicValueName === value.toString())?.dicValue || value
|
||||
}
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
return {
|
||||
autoLogin,
|
||||
loginJump,
|
||||
@ -113,6 +134,7 @@ export const useAppStore = defineStore(
|
||||
setUserInfo,
|
||||
getUserInfo,
|
||||
Logout,
|
||||
translateDictValue,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
@ -42,7 +42,4 @@ export const useCenterControlStore = defineStore(
|
||||
setCode,
|
||||
}
|
||||
},
|
||||
{
|
||||
persist: true,
|
||||
},
|
||||
)
|
||||
|
||||
@ -1,27 +1,27 @@
|
||||
/*
|
||||
* UniApp 通用基础样式类 (基于 rpx)
|
||||
* 命名规则:
|
||||
* [p|m]-[t|r|b|l|a|h|v]-[size] => padding/margin - top/right/bottom/left/all/horizontal/vertical - size(rpx)
|
||||
* [u][p|m]-[t|r|b|l|a|h|v]-[size] => padding/margin - top/right/bottom/left/all/horizontal/vertical - size(rpx)
|
||||
* 示例: p-t-5 => padding-top: 5rpx;
|
||||
*/
|
||||
|
||||
/* === Padding Classes === */
|
||||
.p-a-0 {
|
||||
.u-p-a-0 {
|
||||
padding: 0;
|
||||
}
|
||||
.p-a-5 {
|
||||
.u-p-a-5 {
|
||||
padding: 5rpx;
|
||||
}
|
||||
.p-a-10 {
|
||||
.u-p-a-10 {
|
||||
padding: 10rpx;
|
||||
}
|
||||
.p-a-15 {
|
||||
.u-p-a-15 {
|
||||
padding: 15rpx;
|
||||
}
|
||||
.p-a-20 {
|
||||
.u-p-a-20 {
|
||||
padding: 20rpx;
|
||||
}
|
||||
.p-a-30 {
|
||||
.u-p-a-30 {
|
||||
padding: 30rpx;
|
||||
}
|
||||
.p-a-40 {
|
||||
@ -235,10 +235,10 @@
|
||||
.m-t-15 {
|
||||
margin-top: 15rpx;
|
||||
}
|
||||
.m-t-20 {
|
||||
.u-m-t-20 {
|
||||
margin-top: 20rpx;
|
||||
}
|
||||
.m-t-30 {
|
||||
.u-m-t-30 {
|
||||
margin-top: 30rpx;
|
||||
}
|
||||
.m-t-40 {
|
||||
|
||||
@ -9,3 +9,10 @@ $border-babyblue: #c5d0f0; //浅蓝色
|
||||
//字体颜色
|
||||
$font-blue: #5892e3; //蓝色
|
||||
$font-gray: #999; //灰色
|
||||
$font-black: #333; //黑色
|
||||
$font-white: #fff; //白色
|
||||
$font-red: #f00; //红色
|
||||
$font-orange: #ffa500; //橙色
|
||||
$font-green: #04b604; //绿色
|
||||
$font-yellow: #ffff00; //黄色
|
||||
$font-a1: #a1a1a1;
|
||||
|
||||
49
src/utils/aesTools.ts
Normal file
49
src/utils/aesTools.ts
Normal file
@ -0,0 +1,49 @@
|
||||
import CryptoJS from 'crypto-js'
|
||||
|
||||
/**
|
||||
* AES128ECBEncrypt 加密方法
|
||||
*/
|
||||
export function AES128ECBEncrypt(inputText: any, key: any) {
|
||||
const inputArr = []
|
||||
while (inputText.length < 32) {
|
||||
inputText += '0'
|
||||
}
|
||||
for (let i = 0; i < inputText.length / 2; i++) {
|
||||
inputArr.push(Number.parseInt(inputText.substring(i * 2, i * 2 + 2), 16))
|
||||
}
|
||||
const inputParse = int8arrayParse(inputArr)
|
||||
const keyParse = CryptoJS.enc.Utf8.parse(key)
|
||||
const n = CryptoJS.AES.encrypt(inputParse, keyParse, {
|
||||
iv: [],
|
||||
mode: CryptoJS.mode.ECB,
|
||||
padding: CryptoJS.pad.NoPadding,
|
||||
})
|
||||
const w = int8arrayStringify(n.ciphertext)
|
||||
const encryptStr = bufTohex(w.buffer)
|
||||
return encryptStr
|
||||
}
|
||||
|
||||
export function bufTohex(buffer) {
|
||||
return Array.prototype.map.call(new Uint8Array(buffer), x => (`00${x.toString(16)}`).slice(-2)).join('')
|
||||
}
|
||||
|
||||
export function int8arrayParse(e: any) {
|
||||
const r = e.length
|
||||
const i = []
|
||||
let n = 0
|
||||
for (; n < r; n++) {
|
||||
i[n >>> 2] |= (255 & e[n]) << 24 - n % 4 * 8
|
||||
}
|
||||
return CryptoJS.lib.WordArray.create(i, r)
|
||||
}
|
||||
|
||||
export function int8arrayStringify(t: any) {
|
||||
const e = t.words
|
||||
const r = t.sigBytes
|
||||
const i = new Int8Array(r)
|
||||
let n = 0
|
||||
for (; n < r; n++) {
|
||||
i[n] = e[n >>> 2] >> 24 - n % 4 * 8 & 255
|
||||
}
|
||||
return i
|
||||
}
|
||||
@ -153,6 +153,11 @@ export default defineConfig(({ command, mode }) => {
|
||||
__VITE_APP_PROXY__: JSON.stringify(VITE_APP_PROXY_ENABLE),
|
||||
},
|
||||
css: {
|
||||
preprocessorOptions: {
|
||||
scss: {
|
||||
additionalData: `@import "@/style/color.scss";`,
|
||||
},
|
||||
},
|
||||
postcss: {
|
||||
plugins: [
|
||||
// autoprefixer({
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user