2025-12-19 17:18:47 +08:00

553 lines
11 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<script lang="ts" setup>
import { executeCommandAPI } from '@/api/centerControl'
import { createChangeOrderAPI, createDispatchOrderAPI } from '@/api/operator'
import { getBatteryColor } from '@/utils/car'
const props = defineProps({
data: {
type: Object,
default: () => ({}),
},
tabType: {
type: String,
default: '',
},
})
const emits = defineEmits(['close'])
const btnType = ref('')
const carDetail = ref<any>({
bikeCode: '',
distance: '',
soc: 0,
})
const ActionSheetList = ref([
{
name: '换单工单',
key: 'hasChangeBatteryOrder',
disabled: false,
loading: false,
},
{
name: '调度工单',
key: 'hasDispatchOrder',
disabled: false,
loading: false,
},
{
name: '巡检工单',
key: 'hasInspectionOrder',
disabled: false,
loading: false,
},
])
const actionSheet = ref<any>(null)
const btnNum = ref(3)
const customStyle = computed(() => {
return {
background: '#e4f7fe',
color: '#0a84f9',
border: 'none',
height: '30px',
fontSize: '26rpx',
}
})
const btnCustomStyle = computed(() => {
return {
width: `${320 / btnNum.value}px`,
height: '35px',
border: 'none',
borderRadius: '8px',
}
})
const btnCustomStyle2 = computed(() => {
return {
width: `${320 / btnNum.value}px`,
height: '35px',
borderRadius: '8px',
border: 'none',
background: '#eeeeee',
color: '#4a4a4a',
}
})
// 测试项列表
const list = reactive({
findBike: {
msg: '',
name: '寻车铃',
key: 'FIND_BIKE',
status: 'none', // none,loading,success,fail
tip: '',
btnText: '执行',
btnLoadingText: '执行中',
btnLoading: false,
},
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: '执行中',
btnLoading: false,
},
resetBattery: {
msg: '',
name: '复位电池仓锁',
key: 'CLOSE_BATTERY_LOCK',
status: 'none',
tip: '',
btnText: '执行',
btnLoadingText: '执行中',
btnLoading: false,
},
unlocking: {
msg: '',
name: '开锁',
key: 'UNLOCK',
status: 'none',
tip: '',
btnText: '执行',
btnLoadingText: '执行中',
btnLoading: false,
},
Locking: {
msg: '',
name: '关锁',
key: 'LOCK',
status: 'none',
tip: '',
btnText: '执行',
btnLoadingText: '执行中',
btnLoading: false,
},
openHelmet: {
msg: '',
name: '打开头盔锁',
key: 'OPEN_HELMET',
status: 'none',
tip: '',
btnText: '执行',
btnLoadingText: '执行中',
btnLoading: false,
},
offPower: {
msg: '',
name: '断电',
key: 'POWER_OFF',
status: 'none',
tip: '',
btnText: '执行',
btnLoadingText: '执行中',
btnLoading: false,
},
})
// 执行按钮点击
async function handleBtnClick(value: any, key: string) {
uni.showLoading({
title: '执行中...',
})
list[key].btnLoading = true
try {
const result: boolean = await executeCommandAPI({
bikeCode: carDetail.value.bikeCode,
commandCode: value,
})
if (result) {
uni.showToast({
title: '执行成功',
icon: 'success',
})
}
else {
uni.showToast({
title: '执行失败',
icon: 'error',
})
}
}
catch (e) {
uni.showToast({
title: '执行异常',
icon: 'error',
})
}
finally {
uni.hideLoading()
list[key].btnLoading = false
}
}
// 详情
function handleDetail() {
uni.navigateTo({
url: `/pages-sub/common/carDetail?bikeCode=${carDetail.value.bikeCode}`,
})
}
// 跳转到换电
function completeChangeBattery() {
setTimeout(() => {
uni.navigateTo({
url: `/pages-sub/changebatteries/changebatteries?bikeCode=${carDetail.value.bikeCode}`,
})
}, 100)
}
// 关闭
function handleClose() {
emits('close')
}
// 按钮
function handleBtnConfig(res: any) {
ActionSheetList.value = ActionSheetList.value.map(item => ({
...item,
disabled: res[item.key],
}))
}
// 生成工单
function generateWorkOrder() {
actionSheet.value?.open()
}
// 工单选择
async function ActionSheetSelect(e: any) {
const { key } = e
ActionSheetList.value.forEach((item) => {
if (item.key === key) {
item.loading = true
}
})
switch (key) {
case 'hasInspectionOrder':
break
case 'hasChangeBatteryOrder':
createChangeOrder(key)
break
case 'hasDispatchOrder':
createDispatchOrder(key)
break
case 'hasRepairOrder':
break
}
}
async function createChangeOrder(key: string) {
try {
const res = await createChangeOrderAPI(carDetail.value.ecuSn)
uni.showToast({
title: '工单创建成功',
icon: 'success',
})
ActionSheetList.value.forEach((item) => {
if (item.key === key) {
item.loading = false
item.disabled = false
}
})
actionSheet.value?.close()
}
catch (err) {
console.error(err)
ActionSheetList.value.forEach((item) => {
if (item.key === key) {
item.loading = false
}
})
}
}
async function createDispatchOrder(key: string) {
try {
const res = await createDispatchOrderAPI(carDetail.value.bikeCode)
uni.showToast({
title: '工单创建成功',
icon: 'success',
})
ActionSheetList.value.forEach((item) => {
if (item.key === key) {
item.loading = false
item.disabled = false
}
})
actionSheet.value?.close()
}
catch (err) {
console.error(err)
ActionSheetList.value.forEach((item) => {
if (item.key === key) {
item.loading = false
}
})
}
}
watch(() => props.data, (newVal) => {
carDetail.value = newVal
}, { deep: true })
watch(() => props.tabType, (newVal) => {
btnType.value = newVal
console.log(newVal, 'tabType')
switch (newVal) {
case 'charge':
btnNum.value = 3
break
case 'dispatch':
btnNum.value = 3
break
case 'none':
btnNum.value = 2
break
default:
btnNum.value = 3
break
}
}, { immediate: true })
defineExpose({
handleBtnConfig,
})
</script>
<template>
<view class="container">
<!-- 关闭按钮 -->
<view class="close_icon" @click="handleClose">
<uv-icon name="close" color="#ffffff" size="14" />
</view>
<!-- 车辆信息-标题 -->
<view class="car_info_title">
<view class="title">
{{ carDetail.bikeCode || '--' }}
</view>
<scroll-view
class="tags_bar"
:scroll-x="true"
:scroll-with-animation="true"
:style="{ 'white-space': 'nowrap' }"
>
<view
class="tag"
:style="{
background: carDetail.online ? '#00c854' : '#e93b40',
}"
>
{{ carDetail.online ? '在线' : '离线' }}
</view>
</scroll-view>
</view>
<!-- 车辆信息-电量刷新 -->
<view class="electric_time">
电量刷新时间:{{ '--' }}
</view>
<!-- 车辆信息-距离 -->
<view class="distance">
<view class="distance_left">
<view class="electric">
<uv-icon
:name="getBatteryColor({
soc: carDetail.soc,
}).icon"
custom-prefix="custom-icon"
size="18"
color="#f84a42"
/>
<text
:style="{
color: getBatteryColor({
soc: carDetail.soc,
}).color,
}"
>
{{ carDetail.soc || '--' }}%
</text>
</view>
<view class="distance">
<uv-icon
style="margin-left: 8rpx;"
name="zuchecheliang"
custom-prefix="custom-icon"
size="18"
color="#2eb385"
/>
<text style="margin: 0 8rpx;"> {{ carDetail.distance || '--' }}</text>
</view>
</view>
<view class="distance_right">
<uv-button
:custom-style="customStyle"
type="primary"
text="寻车铃"
shape="circle"
size="normal"
icon="volume"
:disabled="list.findBike.btnLoading"
icon-color="#0984fb"
@click="handleBtnClick(list.findBike.key, 'findBike')"
/>
</view>
</view>
<!-- 车辆信息-按钮区域-换电 -->
<view
v-if="btnType === 'charge'"
class="button-area"
>
<uv-button
:custom-style="btnCustomStyle2"
text="车辆详情"
@click="handleDetail"
/>
<uv-button
:custom-style="btnCustomStyle2"
text="开电池仓"
:disabled="list.openLock.btnLoading"
@click="handleBtnClick(list.openLock.key, 'openLock')"
/>
<uv-button
:custom-style="btnCustomStyle"
type="primary"
text="完成换电"
@click="completeChangeBattery"
/>
</view>
<!-- 车辆信息-按钮区域-全部 -->
<view
v-if="btnType === 'none'"
class="button-area"
>
<uv-button
:custom-style="btnCustomStyle2"
text="车辆详情"
@click="handleDetail"
/>
<uv-button
:custom-style="btnCustomStyle"
type="primary"
text="生成工单"
@click="generateWorkOrder"
/>
</view>
<!-- ActionSheet 操作菜单 -->
<uv-action-sheet
ref="actionSheet"
:actions="ActionSheetList"
title="生成工单类型"
:close-on-click-action="false"
@select="ActionSheetSelect"
/>
</view>
</template>
<style lang="scss" scoped>
.container {
position: relative;
width: calc(100% - 60rpx);
padding: 20rpx 30rpx;
background-color: white;
border-radius: 15rpx;
box-shadow: 0 0 10rpx rgba(0, 0, 0, 0.1);
.close_icon {
position: absolute;
right: 0;
top: 0;
z-index: 1;
width: 40rpx;
height: 40rpx;
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
background-color: #3b3b3bca;
transform: translate(26%, -50%);
}
.car_info_title {
width: 100%;
display: flex;
margin-top: 20rpx;
align-items: center;
justify-content: space-between;
.title {
font-size: 36rpx;
font-weight: bold;
color: #343434;
margin-right: 20rpx;
}
.tags_bar {
display: flex;
align-items: center;
.tag {
display: inline-block;
padding: 6rpx 12rpx;
font-size: 24rpx;
color: #fff;
border-radius: 30rpx;
margin-right: 10rpx;
white-space: nowrap;
}
}
}
.electric_time {
font-size: 28rpx;
color: #b2b2b2;
margin: 15rpx 0;
}
.distance {
width: 100%;
display: flex;
align-items: center;
justify-content: space-between;
.distance_left {
display: flex;
align-items: center;
.electric {
display: flex;
align-items: center;
font-size: 28rpx;
}
.distance {
display: flex;
align-items: center;
font-size: 28rpx;
color: #2eb385;
}
}
}
.button-area {
margin-top: 35rpx;
margin-bottom: 20rpx;
display: flex;
align-items: center;
justify-content: space-between;
}
}
</style>