feat:新增功能

This commit is contained in:
5g0Wp7Zy 2025-12-05 16:57:20 +08:00
parent 9c65fcb697
commit 87b4a35ac7
12 changed files with 1061 additions and 49 deletions

View File

@ -1,4 +1,4 @@
import type { AddInventoryRecordParams, BatchAddInventoryParams, BindEbikeParams } from './types/operator'
import type { AddInventoryRecordParams, BatchAddInventoryParams, BindEbikeParams, CompleteInspectionWorkParams } from './types/operator'
import { http } from '@/http/http'
/**
@ -75,3 +75,17 @@ export function getEbikeDetailForWorkAPI(bikeCode: string) {
export function getAllOperatorRegionAPI() {
return http.get<any>(`/operations/ebikeRegion/list`)
}
/**
*
*/
export function getEbikeListForWorkAPI(regionId: string) {
return http.get<any>(`/operations/ebikeBikeOrder/bikeList`, { regionId })
}
/**
*
*/
export function completeInspectionWorkAPI(params: CompleteInspectionWorkParams) {
return http.post<any>(`/operations/ebikeBikeOrder/doInspection`, params)
}

View File

@ -33,3 +33,12 @@ export interface BindEbikeParams {
hasHelme: boolean // 是否有头盔
remarks?: string // 备注
}
// 完成巡检工单
export interface CompleteInspectionWorkParams {
bikeCode: string // 车辆编号
beforeInspectionFileUrls: string[] // 巡检前图片
afterInspectionFileUrls: string[] // 巡检后图片
isFault: boolean
remarks: string
}

View File

@ -99,6 +99,7 @@ async function handleBtnClick(value: any, key: string, index: number) {
}
watch(() => props.bikeCode, (val) => {
console.log(val, '2222')
bikecode.value = val
})
</script>

View File

@ -86,10 +86,12 @@ async function afterRead(event: any) {
},
})
formData.value.fileUrls.push(result[0])
console.log(result)
fileList.value.splice(fileListLen, 1, Object.assign(item, {
status: 'success',
message: '',
url: result[0].fileUrl,
url: result[0],
}))
fileListLen++
}

View File

@ -0,0 +1,484 @@
<script lang="ts" setup>
import { completeInspectionWorkAPI, getEbikeDetailForWorkAPI } from '@/api/operator'
import { faultUrl, uploadImagesStrict } from '@/api/upload'
import instructTools from '@/components/instructTools/instructTools.vue'
import { useAppStore } from '@/store'
import CacheManager from '@/utils/CacheManager'
import { scanCode } from '@/utils/tools'
definePage({
style: {
navigationBarTitleText: '车辆巡检',
navigationBarBackgroundColor: '#1488f5',
navigationBarTextStyle: 'white',
},
})
const appStore = useAppStore()
const paging = ref<any>(null)
const carIds = ref<any>('')
const formRef = ref<any>(null)
const carDetails = ref<any>({
bikeCode: '',
})
const customStyle = computed(() => {
return {
height: '60rpx',
fontSize: '26rpx',
padding: '20rpx 15rpx',
}
})
const radiolist = ref([
{
name: '苹果',
},
{
name: '香蕉',
},
])
/**
* 表单数据
*/
const isSubmit = ref(false)
const limit = ref(4)
const fileList_before = ref([])
const fileList_after = ref([])
const formData = ref({
bikeCode: '',
beforeInspectionFileUrls: [],
afterInspectionFileUrls: [],
isFault: false,
remarks: '',
})
const rules = {
isFault: {
type: 'boolean',
required: true,
message: '请选择',
trigger: 'change',
},
}
//
function handleScan() {
scanCode({
success: (res: any) => {
carIds.value = res.code
getEbikeDetail()
},
fail: () => {
uni.showToast({
title: '扫码失败',
icon: 'none',
})
},
})
}
//
async function getEbikeDetail() {
uni.showLoading({
title: '查询中...',
})
try {
const res = await getEbikeDetailForWorkAPI(carIds.value)
if (res) {
carDetails.value = res
}
}
catch (e) {
console.error('查询车辆详情失败')
}
finally {
uni.hideLoading()
}
}
//
function handleMapClick() {
const { latitude, longitude } = carDetails.value.location
const params = {
elng: longitude,
elat: latitude,
}
uni.navigateTo({
url: `/pages-sub/map/findCar/findCar?params=${encodeURIComponent(JSON.stringify(params))}`,
})
}
//
async function afterRead(event: any, type: string) {
const lists = [].concat(event.file)
let fileListLen = type === 'before' ? fileList_before.value.length : fileList_after.value.length
const token = CacheManager.get('token')
lists.forEach((item) => {
if (type === 'before') {
fileList_before.value.push({
...item,
status: 'uploading',
message: '上传中',
})
}
else {
fileList_after.value.push({
...item,
status: 'uploading',
message: '上传中',
})
}
})
for (let i = 0; i < lists.length; i++) {
const item = type === 'before' ? fileList_before.value[fileListLen] : fileList_after.value[fileListLen]
try {
const result: any = await uploadImagesStrict([lists[i].url], faultUrl, {
name: 'file',
header: {
'Authorization': token,
'Content-Type': 'multipart/form-data',
},
})
console.log(result)
//
if (type === 'before') {
formData.value.beforeInspectionFileUrls.push(result[0])
}
else {
formData.value.afterInspectionFileUrls.push(result[0])
}
if (type === 'before') {
fileList_before.value.splice(fileListLen, 1, Object.assign(item, {
status: 'success',
message: '',
url: result[0],
}))
}
else {
fileList_after.value.splice(fileListLen, 1, Object.assign(item, {
status: 'success',
message: '',
url: result[0],
}))
}
fileListLen++
}
catch (err) {
console.log('错误')
if (type === 'before') {
fileList_before.value.splice(fileListLen, 1, Object.assign(item, {
status: 'failed',
message: '',
url: '',
}))
}
else {
fileList_after.value.splice(fileListLen, 1, Object.assign(item, {
status: 'failed',
message: '',
url: '',
}))
}
}
}
}
//
function deletePic(e: any, type: string) {
if (type === 'before') {
fileList_before.value.splice(e.index, 1)
formData.value.beforeInspectionFileUrls.splice(e.index, 1)
}
else {
fileList_after.value.splice(e.index, 1)
formData.value.afterInspectionFileUrls.splice(e.index, 1)
}
}
//
function resetForm() {
formData.value = {
bikeCode: '',
beforeInspectionFileUrls: [],
afterInspectionFileUrls: [],
isFault: false,
remarks: '',
}
fileList_before.value = []
fileList_after.value = []
if (formRef.value) {
formRef.value.resetFields()
}
}
//
async function handleComplete() {
if (!carIds.value) {
return uni.showToast({
title: '请输入车牌号',
icon: 'none',
})
}
isSubmit.value = true
try {
const status = await formRef.value.validate()
if (status) {
const res: any = await completeInspectionWorkAPI({
...formData.value,
bikeCode: carIds.value,
})
uni.showToast({
title: '提交成功',
icon: 'none',
})
resetForm()
}
}
catch (e: any) {
console.error('提交失败')
}
finally {
isSubmit.value = false
}
}
onLoad((query) => {
const { bikeCode } = query
if (bikeCode) {
carIds.value = bikeCode
getEbikeDetail()
}
})
</script>
<template>
<z-paging
ref="paging"
bg-color="#f3f3f3"
>
<view class="y_card2_wrapper">
<!-- 详情 -->
<view class="card_box">
<!-- 车辆编号 -->
<view class="card_row_long_3">
<view class="key">
车辆编号
</view>
<view class="value">
<uv-input v-model="carIds" placeholder="请输入或扫码" border="none" />
</view>
<view class="icon">
<uv-icon name="scan" color="#1488f5" size="24" @click="handleScan" />
</view>
</view>
<!-- 第1行 -->
<view class="card_row">
<view class="row_item">
<text class="label">在线状态</text>
<text
class="value"
:style="{
color: carDetails.online ? '#85c262' : carIds ? 'red' : '',
}"
>
{{ carDetails.online ? '在线' : carIds ? '离线' : '--' }}
</text>
</view>
<view class="row_item">
<text class="label">实时电量</text>
<text class="value">{{ carDetails.soc || '--' }}%</text>
</view>
</view>
<!-- 第2行 -->
<view class="card_row">
<view class="row_item">
<text class="label">车辆状态</text>
<text class="value">{{ appStore.translateDictValue(carDetails.usageStatus, 'BikeUsageStatus') || '--' }}</text>
</view>
<view class="row_item">
<text class="label">实时电压</text>
<text class="value">{{ carDetails.voltage || '--' }}V</text>
</view>
</view>
<!-- 第3行 -->
<view class="card_row">
<view class="row_item">
<text class="label">电门状态</text>
<text class="value">{{ carDetails.accOn || '--' }}</text>
</view>
<view class="row_item">
<text class="label">轮锁状态</text>
<text class="value">{{ carDetails.wheelLocked || '--' }}</text>
</view>
</view>
<!-- 第4行 -->
<view class="card_row">
<view class="row_item">
<text class="label">电池仓状态</text>
<text class="value">{{ carDetails.seatLocked || '--' }}</text>
</view>
<view class="row_item">
<text class="label">网络信号</text>
<text class="value">{{ carDetails.gsm || '--' }}</text>
</view>
</view>
<!-- 第5行 -->
<view class="card_row">
<view class="row_item">
<text class="label">运动状态</text>
<text class="value">{{ carDetails.isMoving || '--' }}</text>
</view>
<view class="row_item">
<text class="label">车辆速度</text>
<text class="value">{{ carDetails.speed || '--' }}</text>
</view>
</view>
<!-- 第6行 -->
<view class="card_row">
<view class="row_item">
<text class="label">头盔锁状态</text>
<text class="value">{{ carDetails.isHelmetLocked || '--' }}</text>
</view>
<view class="row_item">
<text class="label">头盔是否在位</text>
<text class="value">{{ carDetails.helmetExit || '--' }}</text>
</view>
</view>
<!-- 车辆编号 -->
<view class="card_row_long_3">
<view class="key">
最新定位
</view>
<view class="value">
{{ carDetails.chineseLocation || '--' }}
</view>
<view class="icon">
<uv-button
type="primary"
:plain="true"
text="地图找车"
icon-color="#1488f5"
icon="map"
shape="circle"
:custom-style="customStyle"
@click="handleMapClick"
/>
</view>
</view>
</view>
<!-- 操作 -->
<view class="card_box">
<instructTools :bike-code="carIds" />
</view>
<!-- 表单填写 -->
<view class="card_box">
<uv-form
ref="formRef"
label-position="top"
:model="formData"
:rules="rules"
:label-width="300"
:label-style="{
fontSize: '28rpx',
color: '#969696',
}"
>
<!-- TUDU:没有确定 -->
<!-- <uv-form-item label="巡检类型" prop="">
<uv-radio-group v-model="formData.radiovalue">
<uv-radio
v-for="(item, index) in radiolist"
:key="index"
:custom-style="{ margin: '8px' }"
:label="item.name"
:name="item.name"
/>
</uv-radio-group>
</uv-form-item> -->
<uv-form-item label="是否直接上报故障" prop="isFault" required>
<uv-radio-group v-model="formData.isFault">
<uv-radio
:custom-style="{ margin: '8px' }"
label="否"
:name="false"
/>
<uv-radio
:custom-style="{ margin: '8px' }"
label="是"
:name="true"
/>
</uv-radio-group>
</uv-form-item>
<uv-form-item label="巡检前照片(最多4张)" prop="beforeInspectionFileUrls">
<uv-upload
:file-list="fileList_before"
name="before"
multiple
:max-count="limit"
:preview-full-image="true"
@after-read="(e:any) => afterRead(e, 'before')"
@delete="(e:any) => deletePic(e, 'before')"
/>
</uv-form-item>
<uv-form-item label="巡检后照片(最多4张)" prop="afterInspectionFileUrls">
<uv-upload
:file-list="fileList_after"
name="after"
multiple
:max-count="limit"
:preview-full-image="true"
@after-read="(e:any) => afterRead(e, 'after')"
@delete="(e:any) => deletePic(e, 'after')"
/>
</uv-form-item>
<uv-form-item label="巡检备注" prop="remarks">
<uv-textarea v-model="formData.remarks" placeholder="请输入备注内容" />
</uv-form-item>
</uv-form>
<!-- 提示 -->
<view class="tips_bar">
<view class="title">
温馨提示
</view>
<view class="text">
1请根据要求提交照片将以此作为审核参考凭证
</view>
</view>
</view>
</view>
<template #bottom>
<view class="bottom-button-zaping">
<uv-button
type="primary"
:loading="isSubmit"
text="确认提交"
loading-text="提交中..."
@click="handleComplete"
/>
</view>
</template>
</z-paging>
</template>
<style lang="scss" scoped>
.tips_bar {
color: #b3b3b3;
.title {
font-size: 32rpx;
margin: 15rpx 0;
}
.text {
font-size: 28rpx;
margin: 10rpx 0;
}
}
</style>

View File

@ -1,5 +1,5 @@
<script lang="ts" setup>
import { getAllOperatorRegionAPI, getEbikeDetailAPI } from '@/api/operator'
import { getAllOperatorRegionAPI, getEbikeDetailForWorkAPI, getEbikeListForWorkAPI } from '@/api/operator'
import AMapWX from '@/plugIns/amap-wx.130.js'
import { addLine, drawPoint, drawRegion, getLoalcation, getMapContext, getRoutePlan } from '@/utils/map'
import { systemInfo } from '@/utils/systemInfo'
@ -14,7 +14,7 @@ const amapsdk = new AMapWX({
const navBarHeight = systemInfo.statusBarHeight * 2 + 80 || 200//
const height = (systemInfo.screenHeight - navBarHeight)
type ToolsType = 'dispatch' | 'charge' | 'none'
type ToolsType = 'dispatch' | 'charge' | 'none' | 'polling' | 'maintain'
definePage({
style: {
@ -22,12 +22,7 @@ definePage({
},
})
//
const carDetailsShow = reactive<any>({
show: false,
key: 'charge', //
})
const list = ref([
const tablist = ref<any>([
{
name: '换电',
key: 'charge',
@ -36,14 +31,28 @@ const list = ref([
name: '调度',
key: 'dispatch',
},
{
name: '巡检',
key: 'polling',
},
{
name: '维修',
key: 'maintain',
},
])
//
const toolsType = ref<ToolsType>('charge')
//
const carDetaile = ref<any>({
const bikeList = ref<any>([]) //
const toolsType = ref<ToolsType>(tablist.value[0].key)//
const carDetaile = ref<any>({ //
distance: '',
})
const carDetailsShow = reactive<any>({ //
show: false,
key: tablist.value[0].key, //
})
/**
* 弹窗配置
*/
const popupRef = ref<any>(null) //
const regionList = ref<any>([]) //
const selectRegion = ref<any>({}) //
@ -72,6 +81,7 @@ function tabClick(item: any) {
toolsType.value = item.key
carDetailsShow.show = false
mapPolylines.value = []
drawMapPoint(toolsType.value)
}
//
function orientation() {
@ -108,10 +118,74 @@ function regionchange(e: any) {
// visionCenter.longitude = centerLocation.longitude
}
//
async function getEbikeList(regionId: string) {
try {
const res: any = await getEbikeListForWorkAPI(regionId)
if (Array.isArray(res) && res.length > 0) {
bikeList.value = res
}
return Promise.resolve(res)
}
catch (e) {
uni.showToast({
title: '获取车辆列表失败',
icon: 'none',
})
console.error('获取车辆列表失败')
}
}
//
function drawMapPoint(orderType: string) {
if (!bikeList.value || bikeList.value.length === 0) {
return
}
mapMarkers.value = []
let list = []
//
if (orderType === tablist.value[0].key) {
list = bikeList.value.filter((item: any) => item.hasChangeBatteryOrder)
}
//
if (orderType === tablist.value[1].key) {
list = bikeList.value.filter((item: any) => item.hasDispatchOrder)
}
//
if (orderType === tablist.value[2].key) {
list = bikeList.value.filter((item: any) => item.hasInspectionOrder)
}
//
if (orderType === tablist.value[3].key) {
list = bikeList.value.filter((item: any) => item.hasRepairOrder)
}
//
if (list.length > 0) {
list.forEach((bike: any) => {
drawPoint({
point: {
bikeCode: bike.bikeCode,
longitude: bike.location.longitude,
latitude: bike.location.latitude,
},
success(res) {
mapMarkers.value.push(res)
},
fail(res) {
console.error(res)
},
})
})
}
}
//
async function initMap() {
console.log(getCurrentInstance())
mapContext.value = getMapContext('mapRef', getCurrentInstance())
if (!mapContext.value) {
mapContext.value = getMapContext('mapRef', getCurrentInstance())
}
orientation()
//
@ -119,22 +193,9 @@ async function initMap() {
//
drawMapPolygons(regionList.value[0].regionId)
setSelectedRegion(regionList.value[0].regionId)
drawPoint({
point: {
bikeCode: '43497692725',
longitude: 104.07,
latitude: 30.61,
},
success(res) {
console.log(res)
mapMarkers.value = [res]
console.log(mapMarkers.value)
},
fail(res) {
console.log(res)
},
})
//
await getEbikeList(regionList.value[0].regionId)
drawMapPoint(toolsType.value)
}
// marker
@ -144,9 +205,7 @@ async function handleMarkertap(e: any) {
//
const item = mapMarkers.value.find(item => item.id === id)
try {
const res: any = await getEbikeDetailAPI(item.id.toString())
console.log(res)
const res: any = await getEbikeDetailForWorkAPI(item.id.toString())
if (res) {
carDetaile.value = res
}
@ -245,18 +304,14 @@ function drawMapPolygons(regionId: string) {
if (!regionId) {
return
}
const region = regionList.value.find(item => item.regionId === regionId)
console.log(region)
const region = regionList.value.find((item: any) => item.regionId === regionId)
if (region) {
const { coordinates } = region.regionPolygon
drawRegion({
point: coordinates,
isFormat: true,
success(res) {
console.log(res, '地图绘制成功')
mapPolygons.value = [res]
console.log(mapPolygons.value, '地图绘制相关方法')
},
fail(err) {
console.error(err)
@ -290,9 +345,11 @@ function backRegion() {
}
//
function handleRegion() {
async function handleRegion() {
await getEbikeList(selectRegionMiddle.value.regionId)
setSelectedRegion(selectRegionMiddle.value.regionId)
drawMapPolygons(selectRegionMiddle.value.regionId)
drawMapPoint(toolsType.value)
backRegion()
}
function handleRegionClick(item: any) {
@ -311,7 +368,7 @@ onMounted(() => {
<view class="tabs">
<uv-tabs
line-color="#ffffff"
:list="list" :active-style="{
:list="tablist" :active-style="{
color: '#ffffff',
fontWeight: 'bold',
transform: 'scale(1.05)',

View File

@ -43,9 +43,9 @@ const btnList = ref([
},
{
key: 'checkdevice',
name: '设备检查',
path: '/pages/devops/checkDevice/checkDevice',
customsrc: 'btn_tongyong_shebeijiancha',
name: '巡检',
path: '/pages-sub/warehouse/polling/polling',
customsrc: 'xunjianrenwu',
},
{
key: 'faultreport',

View File

@ -117,9 +117,7 @@ export const useAppStore = defineStore(
if (list.length > 0) {
const values = list[0].values
if (values && values.length > 0) {
console.log(values)
console.log(value)
return values.find((item: any) => item.dicValue === value.toString())?.dicValueName || value
return values.find((item: any) => item.dicValue === value?.toString())?.dicValueName || value
}
}
return value

File diff suppressed because one or more lines are too long

View File

@ -132,3 +132,64 @@ border-t-1
}
}
}
/* 卡片样式2-一行2列的形式 */
.y_card2_wrapper {
padding: 20rpx 0;
width: 100%;
.card_box {
padding: 30rpx;
width: calc(100% - 60rpx);
background-color: white;
box-shadow: 0 0 6rpx rgba(0, 0, 0, 0.1);
margin-bottom: 30rpx;
.card_row {
display: flex;
justify-content: space-between;
margin: 25rpx 0;
.row_item {
flex: 1;
display: flex;
.label {
font-size: 28rpx;
color: #989898;
width: 150rpx;
}
.value {
font-size: 28rpx;
color: #141414;
}
}
}
.card_row_long_3 {
width: 100%;
display: flex;
justify-content: space-between;
margin: 10rpx 0;
.key {
font-size: 28rpx;
color: #989898;
width: 150rpx;
}
.value {
font-size: 28rpx;
color: #141414;
width: calc(100% - 255rpx);
}
.icon {
width: 120rpx;
display: flex;
justify-content: flex-end;
}
}
}
}

View File

@ -296,6 +296,7 @@ export function drawPoint({
success: (res: any) => void
fail: (res: any) => void
}) {
// console.log(point, '------------>point')
let obj = {}
if (!point) {

File diff suppressed because one or more lines are too long