feat:新增功能

This commit is contained in:
5g0Wp7Zy 2025-10-31 11:29:01 +08:00
parent bc19c99ade
commit 8981806b90
23 changed files with 764 additions and 135 deletions

5
env/.env vendored
View File

@ -30,4 +30,7 @@ VITE_AUTH_MODE = 'single'
VITE_COPY_NATIVE_RES_ENABLE = false
# sm2加密key
VITE_SM2_KEY = '04f5084ee12767d932f293508e30e3b0100185042ec0f061dedaf92b793b93f79fd6179d5e47e25b7aec98e00cf90dd56df1f8191012537187e7bbfd2d1de299fc'
VITE_SM2_KEY = '04f5084ee12767d932f293508e30e3b0100185042ec0f061dedaf92b793b93f79fd6179d5e47e25b7aec98e00cf90dd56df1f8191012537187e7bbfd2d1de299fc'
# 高德地图key
VITE_AMAP_KEY = '1f8f159583f28dfba6eb06deb55e3b56'

View File

@ -18,6 +18,7 @@ onShow((options) => {
else {
navigateToInterceptor.invoke({ url: '/' })
}
// appStore.autoLogin()
})
onHide(() => {

8
src/api/centerControl.ts Normal file
View File

@ -0,0 +1,8 @@
import { http } from '@/http/http'
/**
* SN或BikeCode
*/
export function checkSnOrBikeCodeAPI(query: { ecuSn: string, bikeCode: string }) {
return http.get<any>('/operations/ebikeEcuInfo/checkSnOrBikeCode', query)
}

View File

@ -1,4 +1,4 @@
import type { IAuthLoginRes, ICaptcha, IDoubleTokenRes, IUpdateInfo, IUpdatePassword, IUserInfoRes } from './types/login'
import type { IAuthLoginRes, ICaptcha, IDoubleTokenRes, IUpdateInfo, IUpdatePassword } from './types/login'
import { http } from '@/http/http'
/**
@ -34,17 +34,17 @@ export function refreshToken(refreshToken: string) {
}
/**
*
*
*/
export function getUserInfo() {
return http.get<IUserInfoRes>('/user/info')
return http.get<any>('/staff/ebikeOperatorStaff/getStaffInfo')
}
/**
* 退
*/
export function logout() {
return http.get<void>('/auth/logout')
return http.get<any>('/staff/ebikeOperatorStaff/logout')
}
/**

View File

View File

@ -0,0 +1,162 @@
<script lang="ts" setup>
import { useCenterControlStore } from '@/store'
//
const props = defineProps(['code', 'type'])
const centerStore = useCenterControlStore()
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 type = ref<'car' | 'sn' | ''>('')
const code = ref<string>('')
const list = reactive({
online: {
msg: 'asdas',
name: '在线测试',
key: 'online',
status: 'none', // none,loading,success,fail
tip: '',
btnText: '执行',
btnLoadingText: '执行中',
btnLoading: false,
},
findEbike: {
msg: 'asdas',
name: '在线测试',
key: 'findEbike',
status: 'none', // none,loading,success,fail
tip: '',
btnText: '执行',
btnLoadingText: '执行中',
btnLoading: false,
},
})
//
function initListItem(key: string) {
list[key].status = 'none'
list[key].msg = ''
}
function handleBtnClick(value: any, key: string, index: number) {
initListItem(key)
list[key].btnLoading = true
list[key].status = 'loading'
setTimeout(() => {
list[key].btnLoading = false
list[key].status = 'success'
list[key].msg = '执行成功'
}, 2000)
}
watch(() => props.code, (newVal) => {
code.value = newVal
}, { immediate: true })
watch(() => props.type, (newVal) => {
type.value = newVal
}, { immediate: true })
</script>
<template>
<view class="list-main">
<uv-list>
<uv-list-item
v-for="(value, key, index) in list"
:key="key"
:custom-style="list_item_style"
>
<view class="list-item">
<view class="text_bar">
<view>{{ value.name }} </view>
<view class="result">
<uv-transition
:custom-style="{
display: 'flex',
alignItems: 'center',
}"
:duration="200"
:show="value.status === 'fail'"
>
<uv-icon name="info-circle" color="red" />
<text style="color: red;">{{ value.msg }}</text>
</uv-transition>
<uv-transition
:custom-style="{
display: 'flex',
alignItems: 'center',
}"
:duration="200"
:show="value.status === 'success'"
>
<uv-icon name="checkmark-circle" color="#07c160" />
<text style="color: #07c160;">{{ value.msg }}</text>
</uv-transition>
</view>
</view>
<view class="btn_bar">
<uv-button
:loading="value.btnLoading"
:loading-text="value.btnLoadingText"
:disabled="centerStore.isOnline !== 1"
type="primary" :text="value.btnText"
:custom-style="list_item_btn_style"
@click="handleBtnClick(value, key, index)"
/>
</view>
</view>
</uv-list-item>
</uv-list>
</view>
</template>
<style lang="scss" scoped>
.list-main {
margin-top: 20rpx;
width: calc(100% - 40rpx);
padding: 20rpx;
background-color: white;
border-radius: 10rpx;
box-shadow: 0 0 10rpx rgba(0, 0, 0, 0.1);
.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;
}
}
.btn_bar {
}
}
}
.transition {
display: flex;
align-items: center;
}
</style>

View File

@ -0,0 +1,124 @@
<script lang="ts" setup>
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)
function handleType() {
code.value = ''
store.setCode(store.codeType === 'car' ? 'sn' : 'car', code.value)
store.setOnline(-1)
}
//
function handleScan() {
scanCode({
isSN: store.codeType === 'sn',
success: (res: any) => {
},
fail: () => {
uni.showToast({
title: '扫码失败',
icon: 'none',
})
},
})
}
function handleInput(val: string) {
store.setCode(store.codeType, val)
store.setOnline(-1)
}
//
async function handleConnect() {
btnLoading.value = true
try {
const isOnline = await checkSnOrBikeCodeAPI(store.queryByBike)
isOnline ? store.setOnline(1) : store.setOnline(0)
btnLoading.value = false
}
catch (e) {
console.error('连接----->', e)
btnLoading.value = false
}
}
</script>
<template>
<view class="main">
<view class="search">
<uv-button plain style="margin-right: 25rpx;" @click="handleType">
<uv-icon
name="jiantou_shangxiaqiehuan"
custom-prefix="custom-icon"
size="20"
/>
<text>{{ store.codeType === 'car' ? '车辆编号' : "中控SN" }}</text>
</uv-button>
<uv-input
v-model="code"
placeholder="请输入或扫码"
style="width: 100%;"
@input="handleInput"
>
<template #suffix>
<uv-icon name="scan" size="22" @click="handleScan" />
</template>
</uv-input>
</view>
<view class="online">
<view>
<text style="color: #6a6a6a;">设备在线状态{{ store.isOnline ? '' : '' }}</text>
<text
:style="{
color: store.isOnline === 1 ? '#00c7c7' : store.isOnline === 0 ? '#ff0000' : '',
}"
>
{{ store.isOnline === 1 ? '在线' : store.isOnline === 0 ? '离线' : '未连接' }}
</text>
</view>
<uv-button
type="primary"
text="连接"
:loading="btnLoading"
loading-text="连接中"
:custom-style="{
height: '35px',
fontSize: '24rpx',
}"
@click="handleConnect"
/>
</view>
</view>
</template>
<style lang="scss" scoped>
.main {
width: calc(100% - 80rpx);
padding: 20rpx 40rpx;
background-color: white;
border-radius: 10rpx;
box-shadow: 0 0 10rpx rgba(0, 0, 0, 0.1);
.search {
display: flex;
align-items: center;
justify-content: center;
}
.online {
margin-top: 50rpx;
font-size: 28rpx;
display: flex;
justify-content: space-between;
align-items: center;
}
}
</style>

View File

@ -0,0 +1,40 @@
<script lang="ts" setup>
import executionList from './components/executionList.vue'
import scanByTest from './components/scanByTest.vue'
definePage({
style: {
navigationBarTitleText: '测试中控',
navigationBarBackgroundColor: '#1488f5',
navigationBarTextStyle: 'white',
},
})
</script>
<template>
<z-paging
ref="paging"
bg-color="#f3f3f3"
>
<view class="container">
<!-- 测试中控内容输入框 -->
<scan-by-test />
<!-- 测试中控执行列表 -->
<executionList />
</view>
<template #bottom>
<view class="bottom-button-zaping">
<uv-button type="primary" text="自动化测试" />
</view>
</template>
</z-paging>
</template>
<style lang="scss" scoped>
.container {
width: calc(100% - 40rpx);
padding: 10rpx 20rpx;
}
</style>

View File

@ -0,0 +1,54 @@
<script lang="ts" setup>
import { useAppStore } from '@/store'
definePage({
style: {
navigationBarTitleText: '个人信息',
navigationBarBackgroundColor: '#1488f5',
navigationBarTextStyle: 'white',
},
})
const appStore = useAppStore()
const userInfo = ref<any>({})
async function logout() {
uni.showModal({
title: '提示',
content: '确定要退出登录吗?',
success(res) {
if (res.confirm) {
logout()
}
},
})
}
onMounted(() => {
//
userInfo.value = appStore.getUserInfo()
})
</script>
<template>
<z-paging
ref="paging"
:default-page-no="Number('1')"
:default-page-size="Number('100')"
:auto-show-back-to-top="Boolean(true)"
>
<uv-cell-group>
<uv-cell title="账号" :value="userInfo.username" />
</uv-cell-group>
<template #bottom>
<view class="bottom-button-zaping">
<uv-button type="error" text="退出登录" @click="logout()" />
</view>
</template>
</z-paging>
</template>
<style lang="scss" scoped>
//
</style>

View File

@ -69,7 +69,7 @@ const rules = {
//
function codeScan(type: string) {
scanCode({
isBikeCode: type === 'ecuCode',
isSN: type === 'ecuCode',
success: (res: any) => {
console.log(res)

191
src/pages/home/home.vue Normal file
View File

@ -0,0 +1,191 @@
<script lang="ts" setup>
import { getLoalcation, getMapContext } from '@/utils/map'
import { systemInfo } from '@/utils/systemInfo'
const navBarHeight = systemInfo.statusBarHeight * 2 + 80 || 200//
const height = (systemInfo.screenHeight - navBarHeight)
definePage({
style: {
navigationStyle: 'custom',
},
})
const list = ref([
{
name: '关注',
},
{
name: '推荐',
},
{
name: '热榜',
},
])
/**
* 地图引用
*/
const mapContext = ref<any>(null)
const mapRef = ref<any>()
const mapConfig = reactive({
scale: 14,
latitude: 0,
longitude: 0,
})
const visionCenter = reactive({
latitude: 0,
longitude: 0,
})
//
function orientation() {
if (!mapContext.value) {
return console.error('地图未初始化')
}
getLoalcation(
{
mapContext: mapContext.value,
moveCenter: true,
success: (res: any) => {
console.log('定位成功', res)
const { latitude, longitude } = res
mapConfig.latitude = latitude
mapConfig.longitude = longitude
},
},
)
}
function regionchange(e: any) {
console.log('regionchange', e)
const { centerLocation, type } = e.detail
if (type !== 'end') {
return
}
// TUDO: ()
// visionCenter.latitude = centerLocation.latitude
// visionCenter.longitude = centerLocation.longitude
}
function initMap() {
mapContext.value = getMapContext('mapRef', getCurrentInstance())
orientation()
}
function onZoom(lx: 'in' | 'out') {
mapConfig.latitude = visionCenter.latitude
mapConfig.longitude = visionCenter.longitude
switch (lx) {
case 'out':
mapConfig.scale += 1
break
case 'in':
mapConfig.scale -= 1
break
}
}
onMounted(() => {
initMap()
})
</script>
<template>
<view class="container">
<!-- 导航栏 -->
<view class="nav" :style="{ height: `${navBarHeight}rpx` }">
<view class="tabs">
<uv-tabs
line-color="#ffffff"
:list="list" :active-style="{
color: '#ffffff',
fontWeight: 'bold',
transform: 'scale(1.05)',
}"
:inactive-style="{
color: '#cccccc',
transform: 'scale(1)',
}"
/>
</view>
</view>
<!-- 地图 -->
<map
id="mapRef"
ref="mapRef"
:style="{ width: '100%', height: `${height}px` }"
:show-location="true"
:scale="mapConfig.scale"
:latitude="mapConfig.latitude"
:longitude="mapConfig.longitude"
@regionchange="regionchange"
/>
<!-- 左侧工具栏 -->
<view class="left-toolbar">
<!-- <uv-button
icon="plus" class="m-b_10"
:custom-style="{
width: '70rpx',
height: '70rpx',
}"
@click="onZoom('out')"
/>
<uv-button
icon="minus"
class="m-b_10"
:custom-style="{
width: '70rpx',
height: '70rpx',
}"
@click="onZoom('in')"
/> -->
<uv-button
:custom-style="{
width: '70rpx',
height: '70rpx',
}"
@click="orientation"
>
<template #suffix>
<uv-icon
name="ditu_dingwei"
custom-prefix="custom-icon"
size="28"
/>
</template>
</uv-button>
</view>
</view>
</template>
<style lang="scss" scoped>
.container {
width: 100%;
height: 100vh;
position: relative;
overflow: hidden;
.nav {
width: 100%;
background-color: #1488f5;
position: relative;
.tabs {
width: 70%;
position: absolute;
bottom: 15rpx;
left: 0;
}
}
.left-toolbar {
position: fixed;
left: 20rpx;
top: 50%;
background-color: #bcb9b966;
z-index: 999;
padding: 10rpx;
border-radius: 10rpx;
}
}
</style>

View File

@ -1,113 +0,0 @@
<script lang="ts" setup>
import { getBikecodeAPI } from '@/api/test'
import { safeAreaInsets } from '@/utils/systemInfo'
defineOptions({
name: 'Home',
})
definePage({
style: {
// 'custom' 'default'
navigationStyle: 'custom',
navigationBarTitleText: '首页',
},
})
const description = ref(
'unibest 是一个集成了多种工具和技术的 uniapp 开发模板,由 uniapp + Vue3 + Ts + Vite5 + UnoCss + VSCode 构建,模板具有代码提示、自动格式化、统一配置、代码片段等功能,并内置了许多常用的基本组件和基本功能,让你编写 uniapp 拥有 best 体验。',
)
console.log('index/index 首页打印了')
onLoad(() => {
console.log('测试 uni API 自动引入: onLoad')
getBikecodeAPI('060002').then((res) => {
console.log('getBikecodeAPI res: ', res)
})
})
// #region gotoAbout
function gotoAbout() {
uni.navigateTo({
url: '/pages-sub/about/about',
})
}
// #endregion
</script>
<template>
<view class="bg-white px-4 pt-2" :style="{ marginTop: `${safeAreaInsets?.top}px` }">
<view class="mt-10">
<image src="/static/logo.svg" alt="" class="mx-auto block h-28 w-28" />
</view>
<view class="main-title-color mt-4 text-center text-4xl">
unibest
</view>
<view class="mb-8 mt-2 text-center text-2xl">
最好用的 uniapp 开发模板
</view>
<view class="m-auto mb-2 max-w-100 text-justify indent text-4">
{{ description }}
</view>
<view class="mt-4 text-center">
作者
<text class="text-green-500">
菲鸽
</text>
</view>
<view class="mt-4 text-center">
官网地址
<text class="text-green-500">
https://unibest.tech
</text>
</view>
<!-- #ifdef H5 -->
<view class="mt-4 text-center">
<a href="https://unibest.tech/base/3-plugin" target="_blank" class="text-green-500">
新手请看必看章节1
</a>
</view>
<!-- #endif -->
<!-- #ifdef MP-WEIXIN -->
<view class="mt-4 text-center">
新手请看必看章节1
<text class="text-green-500">
https://unibest.tech/base/3-plugin
</text>
</view>
<!-- #endif -->
<!-- #ifdef H5 -->
<view class="mt-4 text-center">
<a href="https://unibest.tech/base/14-faq" target="_blank" class="text-green-500">
新手请看必看章节2
</a>
</view>
<!-- #endif -->
<!-- #ifdef MP-WEIXIN -->
<view class="mt-4 text-center">
新手请看必看章节2
<text class="text-green-500">
https://unibest.tech/base/14-faq
</text>
</view>
<!-- #endif -->
<view class="mt-4 text-center">
<uv-button type="primary">
UI组件按钮
</uv-button>
</view>
<view class="mt-4 text-center">
UI组件官网<text class="text-green-500">
https://www.uvui.cn
</text>
</view>
<view class="mt-4 text-center">
<wd-button type="primary" class="ml-2" @click="gotoAbout">
前往示例页
</wd-button>
</view>
<view class="h-6" />
</view>
</template>

View File

@ -1,5 +1,5 @@
<script lang="ts" setup>
import { loginAPI } from '@/api/login'
import { getUserInfo, loginAPI } from '@/api/login'
import { useAppStore } from '@/store'
import CacheManager from '@/utils/CacheManager'
@ -35,6 +35,8 @@ async function login() {
const token = await loginAPI(formData)
CacheManager.set('token', token)
isLogin.value = false
const userInfo = await getUserInfo()
appStore.setUserInfo(userInfo) //
appStore.setDictData() //
appStore.loginJump()
}

View File

@ -1,4 +1,6 @@
<script lang="ts" setup>
import { useAppStore } from '@/store'
interface IClass {
key: string
name: string
@ -12,9 +14,10 @@ definePage({
},
})
const isSeleid = ref<string>('')
const appStore = useAppStore()
const userInfo = ref<any>({})
//
const isSeleid = ref<string>('')
const btnList = ref([
{
key: 'maintenance',
@ -95,7 +98,7 @@ const btnList = ref([
{
key: 'detectionecu',
name: '测试中控',
path: '/pages/warehouse/detectionecu/detectionecu',
path: '/pages-sub/centerControl/testControl/testControl',
customsrc: 'ceshi',
},
{
@ -148,7 +151,7 @@ const btnList = ref([
},
],
},
])
])//
function onClikcList(icon: IClass, item: IClass) {
const ikey = item.key
@ -166,6 +169,18 @@ function onClikcList(icon: IClass, item: IClass) {
isSeleid.value = ''
}, 200) // 100
}
function goSetting() {
console.log('goSetting')
uni.navigateTo({
url: '/pages-sub/setting/index',
})
}
onMounted(() => {
//
userInfo.value = appStore.getUserInfo()
})
</script>
<template>
@ -177,7 +192,7 @@ function onClikcList(icon: IClass, item: IClass) {
<uv-avatar text="李" font-size="22" />
<view class="info">
<view class="classname">
姓名
{{ userInfo.username || '--' }}
</view>
<view class="tags">
<uv-tags text="股东" size="mini" />
@ -185,7 +200,7 @@ function onClikcList(icon: IClass, item: IClass) {
</view>
</view>
<view class="setting">
<uv-icon name="setting-fill" size="22" />
<uv-icon name="setting-fill" size="22" @click="goSetting" />
</view>
</view>
</view>

View File

@ -1,6 +1,6 @@
import { defineStore } from 'pinia'
import { logout } from '@/api/login'
import { getCode } from '@/api/system'
import { tabbarStore } from '@/tabbar/store'
import CacheManager from '../utils/CacheManager'
// 码表类型
@ -25,9 +25,12 @@ export const useAppStore = defineStore(
*
*/
const loginJump = () => {
tabbarStore.setAutoCurIdx('/pages/index/index')
// tabbarStore.setAutoCurIdx('/pages/index/index')
// /pages/index/index
uni.switchTab({ url: '/pages/me/me' })
// /pages/me/me
setTimeout(() => {
uni.switchTab({ url: '/pages/home/home' })
}, 100)
}
/**
*
@ -50,6 +53,46 @@ export const useAppStore = defineStore(
}
}
/**
*
*/
const setUserInfo = async (userInfo: any) => {
if (userInfo) {
CacheManager.set('userInfo', userInfo)
}
}
/**
* 退
*/
const Logout = async () => {
try {
await logout()
CacheManager.remove('userInfo')
CacheManager.remove('token')
CacheManager.remove('dictData')
uni.reLaunch({
url: '/pages/login/login',
})
}
catch (e) {
console.error(e)
uni.showToast({
title: '退出登录失败',
icon: 'error',
})
}
}
/**
*
*/
const getUserInfo = () => {
const user = CacheManager.get('userInfo')
return user || {}
}
/**
* code查找字典数据
*/
@ -67,6 +110,9 @@ export const useAppStore = defineStore(
loginJump,
setDictData,
getDictByCode,
setUserInfo,
getUserInfo,
Logout,
}
},
)

View File

@ -0,0 +1,48 @@
import { defineStore } from 'pinia'
export const useCenterControlStore = defineStore(
'centerControl',
() => {
const isOnline = ref(-1) // 是否在线 -1未连接 0离线 1在线
const codeType = ref<'car' | 'sn'>('car')
const queryByBike = reactive({
ecuSn: '',
bikeCode: '',
})
// 设置code类型
function setCode(type: 'car' | 'sn', code: string) {
codeType.value = type
queryByBike.bikeCode = ''
queryByBike.ecuSn = ''
if (type === 'car') {
queryByBike.bikeCode = code
}
else {
queryByBike.ecuSn = code
}
}
// 设置在线状态
function setOnline(status: number) {
isOnline.value = status
}
// 初始化状态
onMounted(() => {
setOnline(-1)
})
return {
isOnline,
queryByBike,
codeType,
setOnline,
setCode,
}
},
{
persist: true,
},
)

View File

@ -15,6 +15,7 @@ export default store
// 模块统一导出
export * from './app'
export * from './centerControl'
export * from './operator'
export * from './scan'
export * from './token'

View File

@ -280,7 +280,7 @@
.m-b-5 {
margin-bottom: 5rpx;
}
.m-b-10 {
.m-b_10 {
margin-bottom: 10rpx;
}
.m-b-15 {

File diff suppressed because one or more lines are too long

View File

@ -57,7 +57,7 @@ export interface CustomTabBarItem {
export const customTabbarList: CustomTabBarItem[] = [
{
text: '首页',
pagePath: 'pages/index/index',
pagePath: 'pages/home/home',
// 注意 unocss 图标需要如下处理:(二选一)
// 1在fg-tabbar.vue页面上引入一下并注释掉见tabbar/index.vue代码第2行
// 2配置到 unocss.config.ts 的 safelist 中

View File

@ -157,7 +157,7 @@ export const isDoubleTokenMode = import.meta.env.VITE_AUTH_MODE === 'double'
* /pages/index/index
*/
// export const HOME_PAGE = `/${(pages as PageMetaDatum[]).find(page => page.type === 'home')?.path || (pages as PageMetaDatum[])[0].path}`
export const HOME_PAGE = `/pages/index/index`
export const HOME_PAGE = `/pages/home/home`
/**
* url
*/

43
src/utils/map.ts Normal file
View File

@ -0,0 +1,43 @@
// 高德地图key
export const GAODE_MAP_KEY = import.meta.env.VITE_AMAP_KEY
// 获取地图实例
export function getMapContext(mapid: string, instance: any) {
return uni.createMapContext(mapid, {
this: instance.proxy,
})
}
// 获取当前定位
export function getLoalcation({
mapContext,
success = null,
fail = null,
moveCenter = false,
}) {
uni.getLocation({
type: 'gcj02',
geocode: true,
success(res) {
console.log('定位成功', res)
const { latitude, longitude } = res
if (moveCenter && mapContext) {
moveToLocation(mapContext, longitude, latitude)
}
success && success({ latitude, longitude })
},
fail(err) {
console.error('uni.getLocation-------', err)
fail && fail(err)
},
})
}
// 移至中心点
export function moveToLocation(mapContext: any, longitude: number, latitude: number) {
const point = {
latitude,
longitude,
}
mapContext.moveToLocation(point)
}

View File

@ -4,14 +4,14 @@
* @param {Function} options.success -
* @param {Function} options.fail -
*/
export function scanCode({ isBikeCode = false, success, fail }) {
export function scanCode({ isSN = false, success, fail }) {
uni.scanCode({
success: (res) => {
const codeStr = res.result
console.log('扫码结果:', codeStr)
// 解析车牌编号
if (isBikeCode) {
if (isSN) {
const codeObj = {
IMEI: '',
SN: '',