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

View File

@ -19,7 +19,7 @@ onShow((options) => {
navigateToInterceptor.invoke({ url: '/' }) navigateToInterceptor.invoke({ url: '/' })
} }
// appStore.autoLogin() appStore.autoLogin()
}) })
onHide(() => { onHide(() => {
console.log('App Hide') 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' import { http } from '@/http/http'
/** /**
@ -138,3 +147,24 @@ export function updateSiteAPI(data: AddSite) {
export function deleteSiteAPI(query: { siteId: string }) { export function deleteSiteAPI(query: { siteId: string }) {
return http.get<any>(`/operations/ebikeSite/remove`, query) 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 siteType: number | string
sitePolygon: CurrentLocationType sitePolygon: CurrentLocationType
} }
//
export interface completeDispatchWorkType {
bikeCode: string
remarks?: string
fileUrls?: string[]
siteId: string
}

View File

@ -1,4 +1,5 @@
<script lang="ts" setup> <script lang="ts" setup>
import { completeDispatchWorkAPI, getEbikeDetailForWorkAPI } from '@/api/operator'
import { faultUrl, uploadImagesStrict } from '@/api/upload' import { faultUrl, uploadImagesStrict } from '@/api/upload'
import instructTools from '@/components/instructTools/instructTools.vue' import instructTools from '@/components/instructTools/instructTools.vue'
import siteSelectList from '@/components/siteSelectList/siteSelectList.vue' import siteSelectList from '@/components/siteSelectList/siteSelectList.vue'
@ -27,15 +28,28 @@ const orderDetail = ref<any>({
}, },
soc: 0, soc: 0,
}) })
const formData = reactive({ const formData = reactive({
bikeCode: '', bikeCode: '',
value: '', siteId: '',
fileUrls: [], fileUrls: [],
remarks: '',
}) })
async function getEbikeDetailForWork() {
function handleSiteClick() { uni.showLoading({
console.log(123) 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() popupRef.value.open()
} }
// //
function findCar() { function findCar() {
const params = { const params = {
elng: orderDetail.value.location.longitude, elng: orderDetail.value.location.longitude,
@ -58,6 +72,7 @@ function scan() {
scanCode({ scanCode({
success: (res: any) => { success: (res: any) => {
formData.bikeCode = res.code formData.bikeCode = res.code
getEbikeDetailForWork()
}, },
fail: () => { fail: () => {
uni.showToast({ 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) { async function afterRead(event: any) {
@ -127,15 +183,22 @@ function popupBack() {
} }
function popupChage(val: any) { function popupChage(val: any) {
const { siteName } = val console.log(val)
const { siteName, siteId } = val
formData.siteId = siteId
popupBack() popupBack()
console.log(val, 'popupChage')
selctSiteName.value = siteName selctSiteName.value = siteName
} }
onMounted(() => { onLoad((query) => {
const { bikeCode } = query
if (bikeCode) {
formData.bikeCode = bikeCode
getEbikeDetailForWork()
}
}) })
onMounted(() => {})
</script> </script>
<template> <template>
@ -210,7 +273,7 @@ onMounted(() => {
<view class="u-m-b-15"> <view class="u-m-b-15">
调度原因 调度原因
</view> </view>
<uv-textarea v-model="formData.value" placeholder="请输入调度原因" /> <uv-textarea v-model="formData.remarks" placeholder="请输入调度原因" />
</view> </view>
<!-- 调度图片 --> <!-- 调度图片 -->

View File

@ -1,5 +1,6 @@
<script lang="ts" setup> <script lang="ts" setup>
import { executeCommandAPI } from '@/api/centerControl' import { executeCommandAPI } from '@/api/centerControl'
import { createChangeOrderAPI, createDispatchOrderAPI } from '@/api/operator'
import { getBatteryColor } from '@/utils/car' import { getBatteryColor } from '@/utils/car'
const props = defineProps({ const props = defineProps({
@ -7,13 +8,39 @@ const props = defineProps({
type: Object, type: Object,
default: () => ({}), default: () => ({}),
}, },
tabType: {
type: String,
default: '',
},
}) })
const emits = defineEmits(['close']) const emits = defineEmits(['close'])
const btnType = ref('')
const carDetail = ref<any>({ const carDetail = ref<any>({
bikeCode: '', bikeCode: '',
distance: '', distance: '',
soc: 0, 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 btnNum = ref(3)
const customStyle = computed(() => { const customStyle = computed(() => {
return { return {
@ -178,13 +205,123 @@ function completeChangeBattery() {
}, 100) }, 100)
} }
// //
function handleClose() { function handleClose() {
emits('close') 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) => { watch(() => props.data, (newVal) => {
carDetail.value = newVal carDetail.value = newVal
}, { deep: true }) }, { 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> </script>
<template> <template>
@ -267,8 +404,11 @@ watch(() => props.data, (newVal) => {
/> />
</view> </view>
</view> </view>
<!-- 车辆信息-按钮区域 --> <!-- 车辆信息-按钮区域-换电 -->
<view class="button-area"> <view
v-if="btnType === 'charge'"
class="button-area"
>
<uv-button <uv-button
:custom-style="btnCustomStyle2" :custom-style="btnCustomStyle2"
text="车辆详情" text="车辆详情"
@ -287,6 +427,32 @@ watch(() => props.data, (newVal) => {
@click="completeChangeBattery" @click="completeChangeBattery"
/> />
</view> </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> </view>
</template> </template>

View File

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

View File

@ -17,8 +17,8 @@ const TITLE_NAME = import.meta.env.VITE_APP_TITLE
const showMm = ref(false) const showMm = ref(false)
// //
const formData = reactive({ const formData = reactive({
username: 'admin', username: '',
password: '123456', password: '',
}) })
// //
const isLogin = ref(false) const isLogin = ref(false)
@ -29,6 +29,14 @@ const notifyRef = ref<any>(null)
* 登录 * 登录
*/ */
async function login() { 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 isLogin.value = true
try { try {

View File

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

View File

@ -280,7 +280,7 @@ export function drawPoint({
option = { option = {
width: 35, width: 35,
height: 35, height: 35,
iconPath: '/static/images/markerG.png', iconPath: '/static/images/carIcon.png',
}, },
success, success,
fail, fail,
@ -319,6 +319,18 @@ export function drawPoint({
obj = setDispatchPoint(point, option) obj = setDispatchPoint(point, option)
break break
} }
case 'maintain': {
obj = repairPoint(point, option)
break
}
case 'polling': {
obj = inspectionPoint(point, option)
break
}
case 'none': {
obj = allCarPoint(point, option)
break
}
default: { default: {
obj = commonPoint(point, option) obj = commonPoint(point, option)
break break
@ -414,6 +426,67 @@ export function setDispatchPoint(point: any, option: any) {
return obj 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) { export function commonPoint(point: any, option: any) {
return { return {