feat:新增功能

This commit is contained in:
5g0Wp7Zy 2025-12-19 17:18:47 +08:00
parent 7868527a39
commit 363862c0ad
10 changed files with 397 additions and 24 deletions

2
env/.env vendored
View File

@ -12,7 +12,7 @@ VITE_APP_PUBLIC_BASE=/
# 后台请求地址
# http://192.168.101.20:10010
# https://www.cdzhuojing.cn/ebike 外网
VITE_SERVER_BASEURL = 'http://192.168.101.18:10010'
VITE_SERVER_BASEURL = 'https://www.cdzhuojing.cn/ebike'
# 备注如果后台带统一前缀则也要加到后面eg: https://ukw0y1.laf.run/api
# 注意,如果是微信小程序,还有一套请求地址的配置,根据 develop、trial、release 分别设置上传地址,见 `src/utils/index.ts`。

View File

@ -19,7 +19,7 @@ onShow((options) => {
navigateToInterceptor.invoke({ url: '/' })
}
// appStore.autoLogin()
appStore.autoLogin()
})
onHide(() => {
console.log('App Hide')

View File

@ -1,4 +1,13 @@
import type { AddInventoryRecordParams, AddSite, BatchAddInventoryParams, BindEbikeParams, CompleteInspectionWorkParams, CompleteMaintainType, CurrentLocationType } from './types/operator'
import type {
AddInventoryRecordParams,
AddSite,
BatchAddInventoryParams,
BindEbikeParams,
completeDispatchWorkType,
CompleteInspectionWorkParams,
CompleteMaintainType,
CurrentLocationType,
} from './types/operator'
import { http } from '@/http/http'
/**
@ -138,3 +147,24 @@ export function updateSiteAPI(data: AddSite) {
export function deleteSiteAPI(query: { siteId: string }) {
return http.get<any>(`/operations/ebikeSite/remove`, query)
}
/**
*
*/
export function completeDispatchWorkAPI(data: completeDispatchWorkType) {
return http.post<any>(`/operations/ebikeBikeOrder/bikeDispatch`, data)
}
/**
*
*/
export function createChangeOrderAPI(ecuSn: string) {
return http.get<any>(`/operations/ebikeBikeOrder/batterySwapOrder`, { ecuSn })
}
/**
*
*/
export function createDispatchOrderAPI(bikeCode: string) {
return http.get<any>(`/operations/ebikeBikeOrder/dispatchSwapOrder`, { bikeCode })
}

View File

@ -67,3 +67,11 @@ export interface AddSite {
siteType: number | string
sitePolygon: CurrentLocationType
}
//
export interface completeDispatchWorkType {
bikeCode: string
remarks?: string
fileUrls?: string[]
siteId: string
}

View File

@ -1,4 +1,5 @@
<script lang="ts" setup>
import { completeDispatchWorkAPI, getEbikeDetailForWorkAPI } from '@/api/operator'
import { faultUrl, uploadImagesStrict } from '@/api/upload'
import instructTools from '@/components/instructTools/instructTools.vue'
import siteSelectList from '@/components/siteSelectList/siteSelectList.vue'
@ -27,15 +28,28 @@ const orderDetail = ref<any>({
},
soc: 0,
})
const formData = reactive({
bikeCode: '',
value: '',
siteId: '',
fileUrls: [],
remarks: '',
})
function handleSiteClick() {
console.log(123)
async function getEbikeDetailForWork() {
uni.showLoading({
title: '查询中',
})
try {
const res = await getEbikeDetailForWorkAPI(formData.bikeCode)
if (res) {
orderDetail.value = res
}
}
catch (e) {
console.error(e)
}
finally {
uni.hideLoading()
}
}
//
@ -43,7 +57,7 @@ function selecSite() {
popupRef.value.open()
}
//
//
function findCar() {
const params = {
elng: orderDetail.value.location.longitude,
@ -58,6 +72,7 @@ function scan() {
scanCode({
success: (res: any) => {
formData.bikeCode = res.code
getEbikeDetailForWork()
},
fail: () => {
uni.showToast({
@ -68,8 +83,49 @@ function scan() {
})
}
function handleComplete() {
function resetForm() {
formData.bikeCode = ''
formData.fileUrls = []
formData.remarks = ''
formData.siteId = ''
orderDetail.value = {
location: {
latitude: 0,
longitude: 0,
},
soc: 0,
}
}
//
async function handleComplete() {
if (!formData.siteId) {
uni.showToast({
title: '请选择站点',
icon: 'none',
})
return
}
btnConfig.loading = true
try {
const res = await completeDispatchWorkAPI(formData)
resetForm()
uni.showToast({
title: '调度成功',
icon: 'none',
})
setTimeout(() => {
uni.navigateBack()
}, 1000)
}
catch (e: any) {
console.error(e)
}
finally {
btnConfig.loading = false
}
}
async function afterRead(event: any) {
@ -127,15 +183,22 @@ function popupBack() {
}
function popupChage(val: any) {
const { siteName } = val
console.log(val)
const { siteName, siteId } = val
formData.siteId = siteId
popupBack()
console.log(val, 'popupChage')
selctSiteName.value = siteName
}
onMounted(() => {
onLoad((query) => {
const { bikeCode } = query
if (bikeCode) {
formData.bikeCode = bikeCode
getEbikeDetailForWork()
}
})
onMounted(() => {})
</script>
<template>
@ -210,7 +273,7 @@ onMounted(() => {
<view class="u-m-b-15">
调度原因
</view>
<uv-textarea v-model="formData.value" placeholder="请输入调度原因" />
<uv-textarea v-model="formData.remarks" placeholder="请输入调度原因" />
</view>
<!-- 调度图片 -->

View File

@ -1,5 +1,6 @@
<script lang="ts" setup>
import { executeCommandAPI } from '@/api/centerControl'
import { createChangeOrderAPI, createDispatchOrderAPI } from '@/api/operator'
import { getBatteryColor } from '@/utils/car'
const props = defineProps({
@ -7,13 +8,39 @@ const props = defineProps({
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 {
@ -178,13 +205,123 @@ function completeChangeBattery() {
}, 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>
@ -267,8 +404,11 @@ watch(() => props.data, (newVal) => {
/>
</view>
</view>
<!-- 车辆信息-按钮区域 -->
<view class="button-area">
<!-- 车辆信息-按钮区域-换电 -->
<view
v-if="btnType === 'charge'"
class="button-area"
>
<uv-button
:custom-style="btnCustomStyle2"
text="车辆详情"
@ -287,6 +427,32 @@ watch(() => props.data, (newVal) => {
@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>

View File

@ -42,6 +42,10 @@ const tablist = ref<any>([
name: '维修',
key: 'maintain',
},
{
name: '全部',
key: 'none',
},
])
const bikeList = ref<any>([]) //
const toolsType = ref<ToolsType>(tablist.value[0].key)//
@ -52,6 +56,7 @@ const carDetailsShow = reactive<any>({ // 车辆详情卡片配置
show: false,
key: tablist.value[0].key, //
})
const carDetailsToolsRef = ref(null)
/**
* 工单工具栏
@ -89,6 +94,7 @@ const mapPolylines = ref([]) // 地图线
function tabClick(item: any) {
toolsType.value = item.key
carDetailsShow.show = false
carDetailsShow.key = item.key
mapPolylines.value = []
drawMapPoint(toolsType.value)
}
@ -130,6 +136,7 @@ function regionchange(e: any) {
//
async function getEbikeList(regionId: string) {
bikeList.value = []
try {
const res: any = await getEbikeListForWorkAPI(regionId)
if (Array.isArray(res) && res.length > 0) {
@ -149,10 +156,10 @@ async function getEbikeList(regionId: string) {
//
function drawMapPoint(orderType: any) {
mapMarkers.value = []
if (!bikeList.value || bikeList.value.length === 0) {
return
}
mapMarkers.value = []
let list = []
//
@ -172,6 +179,10 @@ function drawMapPoint(orderType: any) {
if (orderType === tablist.value[3].key) {
list = bikeList.value.filter((item: any) => item.hasRepairOrder)
}
//
if (orderType === tablist.value[4].key) {
list = bikeList.value
}
//
if (list.length > 0) {
@ -236,12 +247,24 @@ async function handleMarkertap(e: any) {
//
const item = mapMarkers.value.find(item => item.id === id)
try {
const res: any = await getEbikeDetailForWorkAPI(item.id.toString())
if (res) {
carDetaile.value = res
}
const car = bikeList.value.find(it => it.bikeCode === id.toString())
if (car) {
carDetailsToolsRef.value?.handleBtnConfig({
hasInspectionOrder: car.hasInspectionOrder,
hasChangeBatteryOrder: car.hasChangeBatteryOrder,
hasDispatchOrder: car.hasDispatchOrder,
hasRepairOrder: car.hasRepairOrder,
})
}
//
carDetailsShow.key = toolsType.value
toolsType.value = 'none'
carDetailsShow.show = true
@ -525,7 +548,6 @@ function chargeChange(res: any) {
function changeDispatch(res: any) {
const { seletType, data, sliderValue } = res
const list = bikeList.value.filter((item: any) => item.hasDispatchOrder)
console.log(res)
if (list.length <= 0) {
return
@ -676,6 +698,7 @@ async function handleRegion() {
drawMapPolygons(selectRegionMiddle.value.regionId)
drawMapPoint(toolsType.value)
backRegion()
console.log(mapMarkers.value)
}
function handleRegionClick(item: any) {
selectRegionMiddle.value = item
@ -786,6 +809,8 @@ onMounted(() => {
<!-- 车辆详情 -->
<carDetailsTools
v-show="carDetailsShow.show"
ref="carDetailsToolsRef"
:tab-type="carDetailsShow.key"
:data="carDetaile"
@close="carDetailsClose"
/>

View File

@ -17,8 +17,8 @@ const TITLE_NAME = import.meta.env.VITE_APP_TITLE
const showMm = ref(false)
//
const formData = reactive({
username: 'admin',
password: '123456',
username: '',
password: '',
})
//
const isLogin = ref(false)
@ -29,6 +29,14 @@ const notifyRef = ref<any>(null)
* 登录
*/
async function login() {
if (formData.username.trim() === '') {
notifyRef.value?.show({ safeAreaInsetTop: true, type: 'error', message: '请输入用户名' })
return
}
if (formData.password.trim() === '') {
notifyRef.value?.show({ safeAreaInsetTop: true, type: 'error', message: '请输入密码' })
return
}
isLogin.value = true
try {

View File

@ -43,7 +43,7 @@ function goDetail(type: number) {
url = '/pages-sub/changebatteries/changebatteries'
break
case 3:
url = ''
url = '/pages-sub/warehouse/dispatch/dispatch'
break
case 4:
url = '/pages-sub/work/inspectionWorkOrder/inspectionWorkOrder'

View File

@ -280,7 +280,7 @@ export function drawPoint({
option = {
width: 35,
height: 35,
iconPath: '/static/images/markerG.png',
iconPath: '/static/images/carIcon.png',
},
success,
fail,
@ -319,6 +319,18 @@ export function drawPoint({
obj = setDispatchPoint(point, option)
break
}
case 'maintain': {
obj = repairPoint(point, option)
break
}
case 'polling': {
obj = inspectionPoint(point, option)
break
}
case 'none': {
obj = allCarPoint(point, option)
break
}
default: {
obj = commonPoint(point, option)
break
@ -414,6 +426,67 @@ export function setDispatchPoint(point: any, option: any) {
return obj
}
// 维修点位设置
export function repairPoint(point: any, option: any) {
const { bikeInfo } = point
console.log(bikeInfo)
const obj = {
id: Number(point.bikeCode),
latitude: point.latitude,
longitude: point.longitude,
width: 43,
height: 43,
iconPath: '/static/images/markerG.png',
label: {
content: bikeInfo.content,
anchorY: -32,
anchorX: -12,
fontSize: 9,
color: '#0e82ff',
},
}
return obj
}
// 巡检点位设置
export function inspectionPoint(point: any, option: any) {
const { bikeInfo } = point
console.log(bikeInfo)
const obj = {
id: Number(point.bikeCode),
latitude: point.latitude,
longitude: point.longitude,
width: 43,
height: 43,
iconPath: '/static/images/markerG.png',
label: {
content: bikeInfo.content,
anchorY: -32,
anchorX: -12,
fontSize: 9,
color: '#0e82ff',
},
}
return obj
}
// 所有车辆的点位
export function allCarPoint(point: any, option: any) {
const { bikeInfo } = point
const obj = {
id: Number(point.bikeCode),
latitude: point.latitude,
longitude: point.longitude,
...option,
}
return obj
}
// 通用点位设置
export function commonPoint(point: any, option: any) {
return {