521 lines
15 KiB
Vue
521 lines
15 KiB
Vue
<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>
|