zn-admin-vue3-wcs/src/views/board/carBoard/createEditDialog.vue
2025-04-22 09:20:31 +08:00

521 lines
15 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.

<template>
<Dialog v-model="dialogVisible" :title="title" width="1000" style="padding: 0">
<el-form :model="formData" label-width="110" ref="formRef" :rules="formRules">
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="车辆类型" prop="robotModelId" required>
<el-select
v-model="formData.robotModelId"
placeholder="请选择车辆类型"
required
:disabled="formData.id"
>
<el-option
:label="item.robotModelNumber"
:value="item.id"
v-for="item in carModelList"
:key="item.id"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item required label="车辆编号" prop="robotNo">
<el-input
v-model="formData.robotNo"
:disabled="false"
placeholder="请输入车辆编号"
maxlength="10"
show-word-limit
/>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item required label="车辆状态">
<el-select
v-model="formData.robotTaskModel"
placeholder="请选择车辆类型"
required
:disabled="!formData.id"
>
<el-option :label="'锁定'" :value="0" />
<el-option :label="'正常'" :value="1" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item required label="自动充电电量" prop="autoCharge">
<el-input-number
style="width: 130px"
v-model="formData.autoCharge"
:disabled="false"
:controls="false"
:precision="0"
:step="1"
type="number"
:min="0"
:max="99"
placeholder="请输入自动充电电量"
>
<template #append>%</template>
</el-input-number>
<span style="margin-left: 5px">%</span>
<span style="margin-left: 4px; color: #c60606; font-size: 12px"
>建议自动充电电量大于30%</span
>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item required label="Mac地址" prop="macAddress">
<el-input
v-model="formData.macAddress"
:disabled="false"
placeholder="请输入Mac地址"
maxlength="25"
show-word-limit
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item required label="选择范围" placeholder="请选择范围">
<el-cascader
:options="floorAreaList"
:props="props"
clearable
:collapse-tags="false"
v-model="floorAreaJsonData"
@change="floorAreaChange"
style="width: 100%"
/>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="车辆ip" prop="robotIp">
<el-input
v-model="formData.robotIp"
:disabled="false"
placeholder="请输入车辆ip"
maxlength="20"
show-word-limit
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="车辆端口" prop="robotPort">
<el-input
v-model="formData.robotPort"
:disabled="false"
placeholder="请输入车辆端口"
maxlength="6"
show-word-limit
/>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-form-item label="摄像头配置">
<el-button type="primary" @click="addCameraConfig">添加</el-button>
</el-form-item>
</el-row>
<template v-if="formData.cameraAddVOList && formData.cameraAddVOList.length > 0">
<div v-for="(item, index) in formData.cameraAddVOList" :key="index" class="camera-item">
<el-row :gutter="20">
<el-col :span="8">
<el-form-item
label="摄像头位置"
required
:prop="`cameraAddVOList[${index}].cameraPosition`"
:rules="{ required: true, message: '摄像头位置不能为空', trigger: 'change' }"
>
<el-select v-model="item.cameraPosition" placeholder="请选择" style="width: 100%">
<el-option
v-for="(type, typeIndex) in cameraType"
:key="typeIndex"
:label="type.label"
:value="type.value"
:disabled="cameraPositionDisable(type)"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item
label="摄像头IP"
:prop="`cameraAddVOList[${index}].cameraIp`"
:rules="{ required: true, message: '摄像头IP不能为空', trigger: 'change' }"
>
<el-input
v-model="item.cameraIp"
placeholder="请输入摄像头IP"
maxlength="20"
show-word-limit
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item
label="摄像头端口"
:prop="`cameraAddVOList[${index}].cameraPort`"
:rules="{ required: true, message: '摄像头端口不能为空', trigger: 'change' }"
>
<el-input
v-model="item.cameraPort"
placeholder="请输入摄像头端口"
maxlength="10"
show-word-limit
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item
label="摄像头账号"
:prop="`cameraAddVOList[${index}].cameraAccount`"
:rules="{ required: true, message: '摄像头账号不能为空', trigger: 'change' }"
>
<el-input
v-model="item.cameraAccount"
placeholder="请输入摄像头账号"
maxlength="30"
show-word-limit
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item
label="摄像头密码"
:prop="`cameraAddVOList[${index}].cameraPassword`"
:rules="{ required: true, message: '摄像头密码不能为空', trigger: 'change' }"
>
<el-input
v-model="item.cameraPassword"
placeholder="请输入摄像头密码"
maxlength="40"
show-word-limit
/>
</el-form-item>
</el-col>
<el-col :span="8">
<div class="delete-btn">
<el-button type="danger" @click="deleteItem(index)">删除</el-button>
</div>
</el-col>
</el-row>
</div>
</template>
</el-form>
<template #footer>
<div style="padding: 0 10px 10px 0">
<el-button @click="dialogVisible = false"> </el-button>
<el-button :disabled="formLoading" type="primary" @click="submitForm"> </el-button>
</div>
</template>
</Dialog>
</template>
<script lang="ts" setup>
const { t } = useI18n() // 国际化
const message = useMessage() // 消息弹窗
import * as MapTaskAPi from '@/api/map/mapTask'
import * as CarApi from '@/api/car/index'
const dialogVisible = ref(false) // 弹窗的是否展示
const formLoading = ref(false) // 表单的加载中1修改时的数据加载2提交的按钮禁用
const title = ref('新建') // form表单
const formData = ref({
robotModelId: undefined, //车辆类型id
robotNo: undefined, //车辆编号
macAddress: undefined, //mac地址
floorAreaJson: [],
autoCharge: undefined,
robotIp: undefined,
robotPort: undefined,
cameraAddVOList: []
})
const carModelList = ref([])
const floorAreaList = ref([])
const props = { multiple: true }
const floorAreaJsonData = ref([])
//添加摄像头配置
const cameraType = ref([
{
label: '上',
value: 0
},
{
label: '左',
value: 1
},
{
label: '右',
value: 2
},
{
label: '下',
value: 3
},
{
label: '后',
value: 4
}
])
const addCameraConfig = () => {
if (formData.value.cameraAddVOList.length === cameraType.value.length) {
message.warning('已达到最大摄像头数量')
return
}
formData.value.cameraAddVOList.push({
cameraPosition: '', //摄像头位置(0:上, 1:左, 2:右, 3:下, 4:后)
cameraIp: '', //摄像头IP--长度20
cameraPort: '', //摄像头端口--长度10
cameraAccount: '', //摄像头账号--长度30", example = "24863
cameraPassword: '' //摄像头密码--长度40
})
}
//删除
const deleteItem = (index) => {
formData.value.cameraAddVOList.splice(index, 1)
}
//切换位置
const cameraPositionDisable = (typeItem) => {
return formData.value.cameraAddVOList.find((item) => item.cameraPosition === typeItem.value)
}
const getCarModelList = async () => {
const res = await CarApi.robotGetAllModel()
carModelList.value = res
}
const getFloorArea = async () => {
const res = await CarApi.robotPositionGetMapAllNew()
let data = []
let floor = []
data = res
for (let key in res) {
let obj = {
value: key,
label: key + '层',
children: res[key].map((item) => {
return { label: item.area, value: item.id, ...item, children: [] }
})
}
floor.push(obj)
}
console.log(floor)
floorAreaList.value = floor
}
const floorAreaChange = (value) => {
console.log(value)
formData.value.floorAreaJson = []
value.forEach((item) => {
formData.value.floorAreaJson.push(item[1])
})
console.log(formData.value.floorAreaJson)
}
const formRules = reactive({
robotModelId: [{ required: true, message: '车辆类型不能为空', trigger: 'change' }],
robotNo: [{ required: true, message: '车辆编号不能为空', trigger: 'change' }],
macAddress: [{ required: true, message: 'Mac地址不能为空', trigger: 'change' }],
autoCharge: [{ required: true, message: '自动充电电量不能为空', trigger: 'change' }]
})
const formRef = ref() // 表单 Ref
/** 打开弹窗 */
const open = async (type, id) => {
floorAreaJsonData.value = []
getCarModelList()
getFloorArea()
dialogVisible.value = true
resetForm()
if (id) {
title.value = '编辑'
const data = await CarApi.getRobotInformation({ id })
formData.value = data
if (data.positionMapList.length) {
data.positionMapList.forEach((item) => {
floorAreaJsonData.value.push([item.floor, item.id])
})
}
console.log(data)
} else {
title.value = '新建'
}
}
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
/** 提交表单 */
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
const submitForm = async () => {
// 校验表单
if (!formRef) return
const valid = await formRef.value.validate()
if (!valid) return
if (!formData.value.floorAreaJson.length) {
message.warning('请选择范围')
return
}
// 提交请求
formLoading.value = true
try {
if (formData.value.id) {
await CarApi.robotInformationUpdate(formData.value)
message.success(t('common.updateSuccess'))
dialogVisible.value = false
emit('success')
} else {
await CarApi.robotInformationCreate(formData.value)
message.success(t('common.createSuccess'))
dialogVisible.value = false
emit('success')
}
} finally {
formLoading.value = false
}
}
//前往任务管理页面
const { push } = useRouter()
const taskManagement = () => {
push({ name: 'taskManagementCreateTask' })
}
//查询能用的车辆
const robotList = ref([])
const getCanUseRobotList = async () => {
robotList.value = await MapTaskAPi.getCanUseRobot()
}
//获取任务号
const getTaskNo = async () => {
formData.value.taskNo = await MapTaskAPi.getTaskNo()
}
//获取取货位置可选的列表
const getLocationList = async (type, locationNo) => {
return await MapTaskAPi.getLocationByName({
type, // 放货类型1库位、2线库、 3区域
locationNo
})
}
//放货的选择列表
const loading = ref(false)
const releaseRemoteMethod = async (query, item) => {
if (query) {
loading.value = true
item.releaseList = await getLocationList(item.releaseType, query)
loading.value = false
} else {
item.releaseList = []
}
}
//取货的选择列表
const takeRemoteMethod = async (query, item) => {
if (query) {
item.takeList = await getLocationList(item.takeType, query)
} else {
item.takeList = []
}
}
/** 重置表单 */
const resetForm = () => {
formData.value = {
robotModelId: undefined, //车辆类型id
robotNo: undefined, //车辆编号
macAddress: undefined, //mac地址
floorAreaJson: [],
autoCharge: undefined,
robotIp: undefined,
robotPort: undefined,
cameraAddVOList: []
}
floorAreaJsonData.value = []
formRef.value?.resetFields()
}
</script>
<style lang="scss" scoped>
::v-deep .el-dialog {
padding: 0 !important;
}
::v-deep .el-dialog__header {
border-bottom: 1px solid #e8e8e8 !important;
}
.task-tips {
display: flex;
align-items: center;
justify-content: flex-end;
}
.el-select-dropdown__loading {
display: flex;
justify-content: center;
align-items: center;
height: 100px;
font-size: 20px;
}
.circular {
display: inline;
height: 30px;
width: 30px;
animation: loading-rotate 2s linear infinite;
}
.path {
animation: loading-dash 1.5s ease-in-out infinite;
stroke-dasharray: 90, 150;
stroke-dashoffset: 0;
stroke-width: 2;
stroke: var(--el-color-primary);
stroke-linecap: round;
}
@keyframes loading-rotate {
to {
transform: rotate(360deg);
}
}
@keyframes loading-dash {
0% {
stroke-dasharray: 1, 200;
stroke-dashoffset: 0;
}
50% {
stroke-dasharray: 90, 150;
stroke-dashoffset: -40px;
}
100% {
stroke-dasharray: 90, 150;
stroke-dashoffset: -120px;
}
}
@keyframes custom-spin-move {
to {
opacity: 1;
}
}
.camera-item {
box-shadow: rgba(0, 0, 0, 0.03) 0px 0px 0px 1px;
padding: 16px 8px 0 0;
border-radius: 6px;
margin-bottom: 20px;
border: 1px solid #efefef;
.delete-btn {
width: 100%;
display: flex;
justify-content: flex-end;
}
}
</style>