修改内容
This commit is contained in:
@@ -15,12 +15,26 @@
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="发货方" prop="shipper">
|
||||
<el-input v-model="formData.shipper" placeholder="请输入发货方" />
|
||||
<el-select v-model="formData.shipper" placeholder="请选择发货方" clearable filterable style="width: 100%">
|
||||
<el-option
|
||||
v-for="item in supplierList"
|
||||
:key="item.id"
|
||||
:label="item.username || item.mobile"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="采购方" prop="buyer">
|
||||
<el-input v-model="formData.buyer" placeholder="请输入采购方" />
|
||||
<el-select v-model="formData.buyer" placeholder="请选择采购方" clearable filterable style="width: 100%">
|
||||
<el-option
|
||||
v-for="item in buyerList"
|
||||
:key="item.id"
|
||||
:label="item.username || item.mobile"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
@@ -28,12 +42,26 @@
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="车牌号" prop="plateNumber">
|
||||
<el-input v-model="formData.plateNumber" placeholder="如:京A12345" />
|
||||
<el-select v-model="formData.plateNumber" placeholder="请选择车牌号" clearable filterable style="width: 100%">
|
||||
<el-option
|
||||
v-for="item in vehicleOptions"
|
||||
:key="item.id"
|
||||
:label="item.licensePlate"
|
||||
:value="item.licensePlate"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="司机姓名" prop="driverName">
|
||||
<el-input v-model="formData.driverName" placeholder="请输入司机姓名" />
|
||||
<el-select v-model="formData.driverName" placeholder="请选择司机" clearable filterable style="width: 100%" @change="handleDriverChange">
|
||||
<el-option
|
||||
v-for="item in driverOptions"
|
||||
:key="item.id"
|
||||
:label="item.username"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
@@ -55,9 +83,9 @@
|
||||
>
|
||||
<el-option
|
||||
v-for="item in serverList"
|
||||
:key="item.id"
|
||||
:label="item.deviceNo || item.deviceId"
|
||||
:value="item.id"
|
||||
:key="item.deviceId"
|
||||
:label="item.deviceId + (item.name ? ' - ' + item.name : '')"
|
||||
:value="item.deviceId"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
@@ -77,9 +105,9 @@
|
||||
>
|
||||
<el-option
|
||||
v-for="item in eartagList"
|
||||
:key="item.id"
|
||||
:label="item.deviceNo || item.deviceId"
|
||||
:value="item.id"
|
||||
:key="item.deviceId"
|
||||
:label="item.deviceId + (item.name ? ' - ' + item.name : '')"
|
||||
:value="item.deviceId"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
@@ -96,9 +124,9 @@
|
||||
>
|
||||
<el-option
|
||||
v-for="item in collarList"
|
||||
:key="item.id"
|
||||
:label="item.deviceNo || item.deviceId"
|
||||
:value="item.id"
|
||||
:key="item.deviceId"
|
||||
:label="item.deviceId + (item.name ? ' - ' + item.name : '')"
|
||||
:value="item.deviceId"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
@@ -131,14 +159,36 @@
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-col :span="24">
|
||||
<el-form-item label="起点地址" prop="startLocation">
|
||||
<el-input v-model="formData.startLocation" placeholder="请输入起点地址" />
|
||||
<el-input
|
||||
v-model="formData.startLocation"
|
||||
placeholder="请输入或在地图上选择起点地址"
|
||||
@click="showStartLocationMap = true"
|
||||
readonly
|
||||
style="cursor: pointer;"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="showStartLocationMap = true">在地图上选择</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="24">
|
||||
<el-form-item label="目的地地址" prop="endLocation">
|
||||
<el-input v-model="formData.endLocation" placeholder="请输入目的地地址" />
|
||||
<el-input
|
||||
v-model="formData.endLocation"
|
||||
placeholder="请输入或在地图上选择目的地地址"
|
||||
@click="showEndLocationMap = true"
|
||||
readonly
|
||||
style="cursor: pointer;"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="showEndLocationMap = true">在地图上选择</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
@@ -197,12 +247,57 @@
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 起点地址选择地图 -->
|
||||
<el-dialog v-model="showStartLocationMap" title="选择起点地址" width="900px">
|
||||
<baidu-map
|
||||
class="map"
|
||||
:center="{lng: 116.404, lat: 39.915}"
|
||||
:zoom="15"
|
||||
:scroll-wheel-zoom="true"
|
||||
@click="handleStartLocationClick"
|
||||
style="height: 500px"
|
||||
>
|
||||
<bm-marker v-if="formData.startLon && formData.startLat" :position="{lng: parseFloat(formData.startLon), lat: parseFloat(formData.startLat)}" :dragging="true" @dragging="handleStartMarkerDrag" />
|
||||
<bm-map-type :map-types="['BMAP_NORMAL_MAP', 'BMAP_HYBRID_MAP']"></bm-map-type>
|
||||
</baidu-map>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="showStartLocationMap = false">取消</el-button>
|
||||
<el-button type="primary" @click="showStartLocationMap = false">确定</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 目的地地址选择地图 -->
|
||||
<el-dialog v-model="showEndLocationMap" title="选择目的地地址" width="900px">
|
||||
<baidu-map
|
||||
class="map"
|
||||
:center="{lng: 116.404, lat: 39.915}"
|
||||
:zoom="15"
|
||||
:scroll-wheel-zoom="true"
|
||||
@click="handleEndLocationClick"
|
||||
style="height: 500px"
|
||||
>
|
||||
<bm-marker v-if="formData.endLon && formData.endLat" :position="{lng: parseFloat(formData.endLon), lat: parseFloat(formData.endLat)}" :dragging="true" @dragging="handleEndMarkerDrag" />
|
||||
<bm-map-type :map-types="['BMAP_NORMAL_MAP', 'BMAP_HYBRID_MAP']"></bm-map-type>
|
||||
</baidu-map>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="showEndLocationMap = false">取消</el-button>
|
||||
<el-button type="primary" @click="showEndLocationMap = false">确定</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive } from 'vue';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { createDelivery, getAvailableServers, getAvailableEartags, getAvailableCollars } from '@/api/shipping.js';
|
||||
import { createDelivery, updateDeviceDeliveryId } from '@/api/shipping.js';
|
||||
import { memberListByType, driverList as fetchDriverList, vehicleList as fetchVehicleList } from '@/api/userManage.js';
|
||||
import { iotDeviceQueryList } from '@/api/hardware.js';
|
||||
import { BaiduMap, BmMapType, BmMarker } from 'vue-baidu-map-3x';
|
||||
|
||||
const dialogVisible = ref(false);
|
||||
const formRef = ref(null);
|
||||
@@ -210,12 +305,18 @@ const submitLoading = ref(false);
|
||||
const serverList = ref([]);
|
||||
const eartagList = ref([]);
|
||||
const collarList = ref([]);
|
||||
const supplierList = ref([]);
|
||||
const buyerList = ref([]);
|
||||
const driverOptions = ref([]);
|
||||
const vehicleOptions = ref([]);
|
||||
const showStartLocationMap = ref(false);
|
||||
const showEndLocationMap = ref(false);
|
||||
|
||||
const formData = reactive({
|
||||
shipper: '',
|
||||
buyer: '',
|
||||
plateNumber: '',
|
||||
driverName: '',
|
||||
shipper: null,
|
||||
buyer: null,
|
||||
plateNumber: null,
|
||||
driverName: null,
|
||||
driverPhone: '',
|
||||
serverId: null,
|
||||
eartagIds: [],
|
||||
@@ -224,6 +325,10 @@ const formData = reactive({
|
||||
estimatedArrivalTime: '',
|
||||
startLocation: '',
|
||||
endLocation: '',
|
||||
startLat: '',
|
||||
startLon: '',
|
||||
endLat: '',
|
||||
endLon: '',
|
||||
cattleCount: 1,
|
||||
estimatedWeight: null,
|
||||
quarantineCertNo: '',
|
||||
@@ -266,8 +371,8 @@ const validateArrivalTime = (rule, value, callback) => {
|
||||
};
|
||||
|
||||
const rules = {
|
||||
shipper: [{ required: true, message: '请输入发货方', trigger: 'blur' }],
|
||||
buyer: [{ required: true, message: '请输入采购方', trigger: 'blur' }],
|
||||
shipper: [{ required: true, message: '请选择发货方', trigger: 'change' }],
|
||||
buyer: [{ required: true, message: '请选择采购方', trigger: 'change' }],
|
||||
plateNumber: [{ required: true, validator: validatePlateNumber, trigger: 'blur' }],
|
||||
driverName: [{ required: true, message: '请输入司机姓名', trigger: 'blur' }],
|
||||
driverPhone: [{ required: true, validator: validatePhone, trigger: 'blur' }],
|
||||
@@ -279,45 +384,173 @@ const rules = {
|
||||
estimatedWeight: [{ required: true, message: '请输入预估重量', trigger: 'blur' }],
|
||||
};
|
||||
|
||||
// 完善提交数据
|
||||
const buildSubmitData = () => {
|
||||
const data = { ...formData };
|
||||
// 确保经纬度是字符串格式
|
||||
if (data.startLat) data.startLat = String(data.startLat);
|
||||
if (data.startLon) data.startLon = String(data.startLon);
|
||||
if (data.endLat) data.endLat = String(data.endLat);
|
||||
if (data.endLon) data.endLon = String(data.endLon);
|
||||
return data;
|
||||
};
|
||||
|
||||
// 打开弹窗
|
||||
const open = () => {
|
||||
dialogVisible.value = true;
|
||||
loadSupplierAndBuyerList();
|
||||
loadDeviceOptions();
|
||||
loadDriverList();
|
||||
loadVehicleList();
|
||||
};
|
||||
|
||||
// 加载供应商和采购方列表
|
||||
const loadSupplierAndBuyerList = async () => {
|
||||
try {
|
||||
// 加载供应商列表 (type=2)
|
||||
const supplierRes = await memberListByType({ type: 2, pageNum: 1, pageSize: 9999 });
|
||||
if (supplierRes.code === 200) {
|
||||
supplierList.value = supplierRes.data?.rows || supplierRes.data || [];
|
||||
}
|
||||
|
||||
// 加载采购方列表 (type=4)
|
||||
const buyerRes = await memberListByType({ type: 4, pageNum: 1, pageSize: 9999 });
|
||||
if (buyerRes.code === 200) {
|
||||
buyerList.value = buyerRes.data?.rows || buyerRes.data || [];
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载供应商/采购方列表失败', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 加载设备选项
|
||||
const loadDeviceOptions = async () => {
|
||||
try {
|
||||
// 加载主机设备
|
||||
const serverRes = await getAvailableServers({ pageNum: 1, pageSize: 9999 });
|
||||
// 统一使用 iotDeviceQueryList 接口,通过 type 参数区分设备类型
|
||||
// type: 1-主机, 2-耳标, 4-项圈
|
||||
const [serverRes, eartagRes, collarRes] = await Promise.all([
|
||||
iotDeviceQueryList({ pageNum: 1, pageSize: 9999, type: 1 }), // 主机
|
||||
iotDeviceQueryList({ pageNum: 1, pageSize: 9999, type: 2 }), // 耳标
|
||||
iotDeviceQueryList({ pageNum: 1, pageSize: 9999, type: 4 }) // 项圈
|
||||
]);
|
||||
|
||||
if (serverRes.code === 200) {
|
||||
serverList.value = serverRes.data?.rows || serverRes.data || [];
|
||||
// 过滤出主机类型设备 (type === 1)
|
||||
const allServers = serverRes.data?.rows || serverRes.data || [];
|
||||
serverList.value = allServers.filter(item => item.type === 1 || item.type === '1');
|
||||
}
|
||||
|
||||
// 加载耳标设备
|
||||
const eartagRes = await getAvailableEartags({ pageNum: 1, pageSize: 9999 });
|
||||
|
||||
if (eartagRes.code === 200) {
|
||||
eartagList.value = eartagRes.data?.rows || eartagRes.data || [];
|
||||
// 过滤出耳标类型设备 (type === 2)
|
||||
const allEartags = eartagRes.data?.rows || eartagRes.data || [];
|
||||
eartagList.value = allEartags.filter(item => item.type === 2 || item.type === '2');
|
||||
}
|
||||
|
||||
// 加载项圈设备
|
||||
const collarRes = await getAvailableCollars({ pageNum: 1, pageSize: 9999 });
|
||||
|
||||
if (collarRes.code === 200) {
|
||||
collarList.value = collarRes.data?.rows || collarRes.data || [];
|
||||
// 过滤出项圈类型设备 (type === 4)
|
||||
const allCollars = collarRes.data?.rows || collarRes.data || [];
|
||||
collarList.value = allCollars.filter(item => item.type === 4 || item.type === '4');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载设备列表失败', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 加载司机列表
|
||||
const loadDriverList = async () => {
|
||||
try {
|
||||
const res = await fetchDriverList({ pageNum: 1, pageSize: 9999 });
|
||||
if (res.code === 200) {
|
||||
driverOptions.value = res.data?.rows || res.data || [];
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载司机列表失败', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 加载车辆列表
|
||||
const loadVehicleList = async () => {
|
||||
try {
|
||||
const res = await fetchVehicleList({ pageNum: 1, pageSize: 9999 });
|
||||
if (res.code === 200) {
|
||||
vehicleOptions.value = res.data?.data?.rows || res.data?.rows || res.data || [];
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载车辆列表失败', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 司机选择变化时自动填充电话
|
||||
const handleDriverChange = (driverId) => {
|
||||
if (driverId) {
|
||||
const driver = driverOptions.value.find(item => item.id === driverId);
|
||||
if (driver && driver.mobile) {
|
||||
formData.driverPhone = driver.mobile;
|
||||
}
|
||||
} else {
|
||||
formData.driverPhone = '';
|
||||
}
|
||||
};
|
||||
|
||||
// 更新选中设备的delivery_id
|
||||
const updateSelectedDevicesDeliveryId = async (deliveryId) => {
|
||||
try {
|
||||
const devicesToUpdate = [];
|
||||
|
||||
// 收集所有选中的设备
|
||||
if (formData.serverId) {
|
||||
devicesToUpdate.push(formData.serverId);
|
||||
}
|
||||
if (formData.eartagIds && formData.eartagIds.length > 0) {
|
||||
devicesToUpdate.push(...formData.eartagIds);
|
||||
}
|
||||
if (formData.collarIds && formData.collarIds.length > 0) {
|
||||
devicesToUpdate.push(...formData.collarIds);
|
||||
}
|
||||
|
||||
// 批量更新设备的delivery_id
|
||||
for (const deviceId of devicesToUpdate) {
|
||||
await updateDeviceDeliveryId({
|
||||
deviceId: deviceId,
|
||||
deliveryId: deliveryId
|
||||
});
|
||||
}
|
||||
|
||||
console.log(`成功更新 ${devicesToUpdate.length} 个设备的delivery_id`);
|
||||
} catch (error) {
|
||||
console.error('更新设备delivery_id失败:', error);
|
||||
// 不阻止流程,只记录错误
|
||||
}
|
||||
};
|
||||
|
||||
// 提交表单
|
||||
const handleSubmit = () => {
|
||||
formRef.value.validate(async (valid) => {
|
||||
if (valid) {
|
||||
// 验证地址经纬度
|
||||
if (!formData.startLat || !formData.startLon) {
|
||||
ElMessage.warning('请在地图上选择起点位置');
|
||||
return;
|
||||
}
|
||||
if (!formData.endLat || !formData.endLon) {
|
||||
ElMessage.warning('请在地图上选择目的地位置');
|
||||
return;
|
||||
}
|
||||
|
||||
submitLoading.value = true;
|
||||
try {
|
||||
const res = await createDelivery(formData);
|
||||
const submitData = buildSubmitData();
|
||||
console.log('提交的数据:', submitData);
|
||||
const res = await createDelivery(submitData);
|
||||
if (res.code === 200) {
|
||||
// 获取新创建的运送清单ID
|
||||
const newDeliveryId = res.data?.id;
|
||||
|
||||
if (newDeliveryId) {
|
||||
// 更新设备的delivery_id
|
||||
await updateSelectedDevicesDeliveryId(newDeliveryId);
|
||||
}
|
||||
|
||||
ElMessage.success('创建成功');
|
||||
dialogVisible.value = false;
|
||||
emit('success');
|
||||
@@ -325,6 +558,7 @@ const handleSubmit = () => {
|
||||
ElMessage.error(res.msg || '创建失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('创建失败:', error);
|
||||
ElMessage.error('创建失败,请稍后重试');
|
||||
} finally {
|
||||
submitLoading.value = false;
|
||||
@@ -333,10 +567,64 @@ const handleSubmit = () => {
|
||||
});
|
||||
};
|
||||
|
||||
// 地图点击事件 - 起点
|
||||
const handleStartLocationClick = (e) => {
|
||||
formData.startLon = e.point.lng;
|
||||
formData.startLat = e.point.lat;
|
||||
// 反向地理编码获取地址
|
||||
const geocoder = new window.BMap.Geocoder();
|
||||
geocoder.getLocation(e.point, (res) => {
|
||||
if (res) {
|
||||
formData.startLocation = res.address;
|
||||
ElMessage.success('已设置起点地址');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 起点标记拖拽事件
|
||||
const handleStartMarkerDrag = (e) => {
|
||||
formData.startLon = e.point.lng;
|
||||
formData.startLat = e.point.lat;
|
||||
const geocoder = new window.BMap.Geocoder();
|
||||
geocoder.getLocation(e.point, (res) => {
|
||||
if (res) {
|
||||
formData.startLocation = res.address;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 地图点击事件 - 目的地
|
||||
const handleEndLocationClick = (e) => {
|
||||
formData.endLon = e.point.lng;
|
||||
formData.endLat = e.point.lat;
|
||||
// 反向地理编码获取地址
|
||||
const geocoder = new window.BMap.Geocoder();
|
||||
geocoder.getLocation(e.point, (res) => {
|
||||
if (res) {
|
||||
formData.endLocation = res.address;
|
||||
ElMessage.success('已设置目的地地址');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 目的地标记拖拽事件
|
||||
const handleEndMarkerDrag = (e) => {
|
||||
formData.endLon = e.point.lng;
|
||||
formData.endLat = e.point.lat;
|
||||
const geocoder = new window.BMap.Geocoder();
|
||||
geocoder.getLocation(e.point, (res) => {
|
||||
if (res) {
|
||||
formData.endLocation = res.address;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 关闭弹窗
|
||||
const handleClose = () => {
|
||||
formRef.value?.resetFields();
|
||||
dialogVisible.value = false;
|
||||
showStartLocationMap.value = false;
|
||||
showEndLocationMap.value = false;
|
||||
};
|
||||
|
||||
// 暴露方法给父组件
|
||||
@@ -348,6 +636,11 @@ const emit = defineEmits(['success']);
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.map {
|
||||
width: 100%;
|
||||
height: 500px;
|
||||
}
|
||||
|
||||
.dialog-footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
|
||||
Reference in New Issue
Block a user