签章位置固定功能
This commit is contained in:
@@ -0,0 +1,598 @@
|
||||
<template>
|
||||
<a-modal
|
||||
v-model:visible="visible"
|
||||
centered
|
||||
title="公路运输产品新增"
|
||||
width="800px"
|
||||
@cancel="handleCancel"
|
||||
@ok="handleOk"
|
||||
>
|
||||
<a-form
|
||||
ref="formRef"
|
||||
:model="formState"
|
||||
:label-col="{ span: 8 }"
|
||||
:wrapper-col="{ span: 16 }"
|
||||
:rules="rules"
|
||||
>
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="短驳类型" name="busType">
|
||||
<a-select
|
||||
v-model:value="formState.busType"
|
||||
placeholder="请选择短驳类型"
|
||||
allow-clear
|
||||
>
|
||||
<a-select-option value="集装箱">集装箱</a-select-option>
|
||||
<a-select-option value="散杂货">散杂货</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="计价方式" name="priceType">
|
||||
<a-select
|
||||
v-model:value="formState.priceType"
|
||||
placeholder="请选择计价方式"
|
||||
allow-clear
|
||||
>
|
||||
<a-select-option value="1">一口价</a-select-option>
|
||||
<a-select-option value="2">阶梯价</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
|
||||
<!-- 一口价时显示起运地类型和目的地类型 -->
|
||||
<template v-if="formState.priceType === '1'">
|
||||
<!-- 起运地类型(前端UI控制字段) -->
|
||||
<a-col :span="12">
|
||||
<a-form-item label="起运地类型" name="fromLocUIType">
|
||||
<a-select
|
||||
v-model:value="formState.fromLocUIType"
|
||||
placeholder="请选择起运地类型"
|
||||
allow-clear
|
||||
@change="handleFromLocTypeChange"
|
||||
>
|
||||
<a-select-option value="railway">铁路场站</a-select-option>
|
||||
<a-select-option value="dock">码头</a-select-option>
|
||||
<a-select-option value="door">门点地址</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
|
||||
<!-- 目的地类型(前端UI控制字段) -->
|
||||
<a-col :span="12">
|
||||
<a-form-item label="目的地类型" name="toLocUIType">
|
||||
<a-select
|
||||
v-model:value="formState.toLocUIType"
|
||||
placeholder="请选择目的地类型"
|
||||
allow-clear
|
||||
@change="handleToLocTypeChange"
|
||||
>
|
||||
<a-select-option value="railway">铁路场站</a-select-option>
|
||||
<a-select-option value="dock">码头</a-select-option>
|
||||
<a-select-option value="door">门点地址</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
|
||||
<!-- 起运地 -->
|
||||
<a-col :span="12">
|
||||
<a-form-item label="起运地" name="fromLoc">
|
||||
<a-form-item-rest>
|
||||
<div style="display: flex; align-items: center">
|
||||
<!-- 铁路场站或码头:使用下拉选择 -->
|
||||
<a-select
|
||||
v-if="formState.fromLocUIType === 'railway' || formState.fromLocUIType === 'dock'"
|
||||
v-model:value="formState.fromLoc"
|
||||
:placeholder="formState.fromLocUIType === 'railway' ? '请选择铁路场站' : '请选择码头'"
|
||||
allow-clear
|
||||
show-search
|
||||
:filter-option="filterOption"
|
||||
@change="handleFromStationSelect"
|
||||
>
|
||||
<a-select-option
|
||||
v-for="item in fromStationOptions"
|
||||
:key="item.value"
|
||||
:value="item.value"
|
||||
>
|
||||
{{ item.label }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
|
||||
<!-- 门点地址:使用搜索选择 -->
|
||||
<a-select
|
||||
v-else
|
||||
v-model:value="formState.fromLoc"
|
||||
show-search
|
||||
placeholder="请输入地点"
|
||||
:filter-option="false"
|
||||
@search="handleLocationSearch"
|
||||
@select="handleRealSelect"
|
||||
@dropdownVisibleChange="handleDropdownHide"
|
||||
>
|
||||
<a-select-option
|
||||
v-for="item in locationOptions"
|
||||
:key="item.uid"
|
||||
:value="item.title"
|
||||
:data="item"
|
||||
>
|
||||
<div>{{ item.title }}</div>
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
|
||||
<!-- 只有门点地址才显示定位按钮 -->
|
||||
<a-button
|
||||
v-if="formState.fromLocUIType === 'door'"
|
||||
@click="handleSelectCoordinates('from')"
|
||||
type="primary"
|
||||
shape="circle"
|
||||
size="small"
|
||||
style="margin-left: 8px"
|
||||
>
|
||||
<template #icon>
|
||||
<EnvironmentOutlined />
|
||||
</template>
|
||||
</a-button>
|
||||
</div>
|
||||
</a-form-item-rest>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
|
||||
<!-- 目的地 -->
|
||||
<a-col :span="12">
|
||||
<a-form-item label="目的地" name="toLoc">
|
||||
<a-form-item-rest>
|
||||
<div style="display: flex; align-items: center">
|
||||
<!-- 铁路场站或码头:使用下拉选择 -->
|
||||
<a-select
|
||||
v-if="formState.toLocUIType === 'railway' || formState.toLocUIType === 'dock'"
|
||||
v-model:value="formState.toLoc"
|
||||
:placeholder="formState.toLocUIType === 'railway' ? '请选择铁路场站' : '请选择码头'"
|
||||
allow-clear
|
||||
show-search
|
||||
:filter-option="filterOption"
|
||||
@change="handleToStationSelect"
|
||||
>
|
||||
<a-select-option
|
||||
v-for="item in toStationOptions"
|
||||
:key="item.value"
|
||||
:value="item.value"
|
||||
>
|
||||
{{ item.label }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
|
||||
<!-- 门点地址:使用下拉选择 -->
|
||||
<a-select
|
||||
v-else
|
||||
v-model:value="formState.toLoc"
|
||||
placeholder="请选择目的地"
|
||||
allow-clear
|
||||
show-search
|
||||
:filter-option="filterOption"
|
||||
@change="handleDoorToChange"
|
||||
>
|
||||
<a-select-option
|
||||
v-for="item in stationOptions"
|
||||
:key="item.value"
|
||||
:value="item.value"
|
||||
>
|
||||
{{ item.label }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
|
||||
<!-- 只有门点地址才显示定位按钮 -->
|
||||
<a-button
|
||||
v-if="formState.toLocUIType === 'door'"
|
||||
@click="handleSelectCoordinates('to')"
|
||||
type="primary"
|
||||
shape="circle"
|
||||
size="small"
|
||||
style="margin-left: 8px"
|
||||
>
|
||||
<template #icon>
|
||||
<EnvironmentOutlined />
|
||||
</template>
|
||||
</a-button>
|
||||
</div>
|
||||
</a-form-item-rest>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
|
||||
<!-- 价格 -->
|
||||
<a-col :span="12">
|
||||
<a-form-item label="价格(元)" name="basePrice">
|
||||
<a-form-item-rest>
|
||||
<div style="display: flex; align-items: center">
|
||||
<a-input-number
|
||||
style="flex: 1; margin-right: 10px"
|
||||
v-model:value="formState.basePrice"
|
||||
placeholder="请输入运价"
|
||||
/>
|
||||
<a-input disabled value="元" style="width: 100px" placeholder="单位" />
|
||||
</div>
|
||||
</a-form-item-rest>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</template>
|
||||
|
||||
<a-col :span="12">
|
||||
<a-form-item label="运价备注" name="priceRemark">
|
||||
<a-input
|
||||
style="width: 100%"
|
||||
v-model:value="formState.priceRemark"
|
||||
placeholder="请输入运价备注"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
|
||||
<a-col :span="12">
|
||||
<a-form-item label="预计时长" name="estTime">
|
||||
<a-form-item-rest>
|
||||
<div style="display: flex; align-items: center">
|
||||
<a-input-number
|
||||
style="flex: 1; margin-right: 10px"
|
||||
v-model:value="formState.estTime"
|
||||
type="number"
|
||||
min="0"
|
||||
placeholder="请输入预计时长"
|
||||
/>
|
||||
<a-select
|
||||
v-model:value="formState.estTimeUnit"
|
||||
style="width: 100px"
|
||||
placeholder="单位"
|
||||
>
|
||||
<a-select-option value="D">天</a-select-option>
|
||||
<a-select-option value="H">小时</a-select-option>
|
||||
</a-select>
|
||||
</div>
|
||||
</a-form-item-rest>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
|
||||
<a-col :span="12">
|
||||
<a-form-item label="揽货备注" name="cargoRemark">
|
||||
<a-input v-model:value="formState.cargoRemark" placeholder="请输入揽货备注" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
|
||||
<gridientComp
|
||||
v-if="formState.priceType === '2'"
|
||||
v-model:modelValue="formState.elementTieredPriceList"
|
||||
/>
|
||||
</a-row>
|
||||
</a-form>
|
||||
|
||||
<locationSelect ref="locationSelectRef" @location-selected="changeLocation" />
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive, nextTick, watch } from 'vue';
|
||||
import { FormInstance, message } from 'ant-design-vue';
|
||||
import locationSelect from '../../../commonComponents/locationSelect.vue';
|
||||
import { addTruckInfo, editTruckInfo, getTruckInfoById, getBaiduPlace } from '@/api/qcwl';
|
||||
import * as commonApi from '@/api/common';
|
||||
import type { RuleObject } from 'ant-design-vue/lib/form/interface';
|
||||
import { noLoginlistStation } from '@/api/sysOrgRelation';
|
||||
import { commonMixin } from '@/mixins/commonMixin';
|
||||
import gridientComp from './gridientComp.vue';
|
||||
|
||||
const { filterOption } = commonMixin();
|
||||
|
||||
interface StationOption {
|
||||
label: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
const emit = defineEmits(['refresh']);
|
||||
|
||||
const formRef = ref<FormInstance>();
|
||||
const locationSelectRef = ref();
|
||||
const visible = ref(false);
|
||||
|
||||
// 起运地站点选项(铁路场站/码头)
|
||||
const fromStationOptions = ref<StationOption[]>([]);
|
||||
// 目的地站点选项(铁路场站/码头)
|
||||
const toStationOptions = ref<StationOption[]>([]);
|
||||
// 门点地址选项
|
||||
const stationOptions = ref<StationOption[]>([]);
|
||||
|
||||
// 位置搜索选项
|
||||
const locationOptions = ref<any[]>([]);
|
||||
let realInputValue = '';
|
||||
|
||||
// 当前操作的坐标类型(用于定位回调)
|
||||
let currentCoordType: 'from' | 'to' = 'from';
|
||||
|
||||
const formState = reactive<any>({
|
||||
id: undefined,
|
||||
busType: '集装箱',
|
||||
// 后端接口字段(保持不变)
|
||||
fromLocType: 'city',
|
||||
toLocType: 'railway',
|
||||
// 前端UI控制字段(不提交给后端)
|
||||
fromLocUIType: 'door',
|
||||
toLocUIType: 'door',
|
||||
fromLoc: undefined,
|
||||
toLoc: undefined,
|
||||
basePrice: undefined,
|
||||
priceRemark: undefined,
|
||||
estTime: undefined,
|
||||
estTimeUnit: 'D',
|
||||
cargoRemark: undefined,
|
||||
fromLocPosition: '',
|
||||
toLocPosition: '',
|
||||
priceType: '1',
|
||||
elementTieredPriceList: [],
|
||||
});
|
||||
|
||||
const rules: Record<string, RuleObject | RuleObject[]> = {
|
||||
busType: [{ required: true, message: '请选择短驳类型' }],
|
||||
fromLocUIType: [{ required: true, message: '请选择起运地类型' }],
|
||||
toLocUIType: [{ required: true, message: '请选择目的地类型' }],
|
||||
fromLoc: [{ required: true, message: '请选择起运地' }],
|
||||
toLoc: [{ required: true, message: '请选择目的地' }],
|
||||
basePrice: [{ required: true, message: '请输入运价' }],
|
||||
estTime: [{ required: true, message: '请输入预计时长' }],
|
||||
estTimeUnit: [{ required: true, message: '请选择预计时长单位' }],
|
||||
priceType: [{ required: true, message: '请选择计价方式' }],
|
||||
};
|
||||
|
||||
const showModal = (row?: any) => {
|
||||
// 初始化站点选项
|
||||
initStationOptions();
|
||||
// 初始化门点地址选项
|
||||
initDoorStationOptions();
|
||||
// 清空位置搜索
|
||||
locationOptions.value = [];
|
||||
|
||||
if (row && row.id) {
|
||||
getTruckInfoById(row.id).then((res) => {
|
||||
// 从后端获取数据,UI类型默认设置为door
|
||||
Object.assign(formState, {
|
||||
...res.result,
|
||||
fromLocUIType: 'door',
|
||||
toLocUIType: 'door',
|
||||
});
|
||||
});
|
||||
} else {
|
||||
Object.assign(formState, {
|
||||
id: undefined,
|
||||
busType: '集装箱',
|
||||
fromLocType: 'city',
|
||||
toLocType: 'railway',
|
||||
fromLocUIType: 'door',
|
||||
toLocUIType: 'door',
|
||||
fromLoc: undefined,
|
||||
toLoc: undefined,
|
||||
basePrice: undefined,
|
||||
priceRemark: undefined,
|
||||
estTime: undefined,
|
||||
estTimeUnit: 'D',
|
||||
cargoRemark: undefined,
|
||||
fromLocPosition: '',
|
||||
toLocPosition: '',
|
||||
priceType: '1',
|
||||
elementTieredPriceList: [],
|
||||
});
|
||||
}
|
||||
visible.value = true;
|
||||
};
|
||||
|
||||
// 初始化站点选项(铁路场站和码头)- 占位数据
|
||||
const initStationOptions = () => {
|
||||
// 铁路场站占位数据(待接口对接)
|
||||
const railwayStations = [
|
||||
{ label: '上海铁路场站', value: 'shanghai_railway' },
|
||||
{ label: '北京铁路场站', value: 'beijing_railway' },
|
||||
{ label: '广州铁路场站', value: 'guangzhou_railway' },
|
||||
{ label: '深圳铁路场站', value: 'shenzhen_railway' },
|
||||
{ label: '成都铁路场站', value: 'chengdu_railway' },
|
||||
{ label: '武汉铁路场站', value: 'wuhan_railway' },
|
||||
];
|
||||
|
||||
// 码头占位数据(待接口对接)
|
||||
const dockStations = [
|
||||
{ label: '上海港码头', value: 'shanghai_dock' },
|
||||
{ label: '宁波港码头', value: 'ningbo_dock' },
|
||||
{ label: '深圳港码头', value: 'shenzhen_dock' },
|
||||
{ label: '广州港码头', value: 'guangzhou_dock' },
|
||||
{ label: '青岛港码头', value: 'qingdao_dock' },
|
||||
{ label: '天津港码头', value: 'tianjin_dock' },
|
||||
];
|
||||
|
||||
// 合并所有站点供选择(实际使用时可区分铁路和码头)
|
||||
fromStationOptions.value = [...railwayStations, ...dockStations];
|
||||
toStationOptions.value = [...railwayStations, ...dockStations];
|
||||
};
|
||||
|
||||
// 初始化门点地址选项
|
||||
const initDoorStationOptions = () => {
|
||||
noLoginlistStation({}).then((res) => {
|
||||
if (res.code !== '0000') {
|
||||
return Promise.reject(res);
|
||||
}
|
||||
stationOptions.value = res.result.map((item: any) => ({
|
||||
label: item,
|
||||
value: item,
|
||||
}));
|
||||
});
|
||||
};
|
||||
|
||||
// 起运地类型变更
|
||||
const handleFromLocTypeChange = (val: string) => {
|
||||
formState.fromLoc = undefined;
|
||||
formState.fromLocPosition = '';
|
||||
// 如果不是门点地址,清空搜索选项
|
||||
if (val !== 'door') {
|
||||
locationOptions.value = [];
|
||||
}
|
||||
};
|
||||
|
||||
// 目的地类型变更
|
||||
const handleToLocTypeChange = (val: string) => {
|
||||
formState.toLoc = undefined;
|
||||
formState.toLocPosition = '';
|
||||
};
|
||||
|
||||
// 起运地铁路/码头选择
|
||||
const handleFromStationSelect = (val: string) => {
|
||||
// 从站点列表中查找对应的经纬度
|
||||
const found = fromStationOptions.value.find((item) => item.value === val);
|
||||
if (found) {
|
||||
// TODO: 从后端获取对应站点的经纬度,目前使用示例坐标
|
||||
formState.fromLocPosition = '121.4737,31.2304';
|
||||
}
|
||||
};
|
||||
|
||||
// 目的地铁路/码头选择
|
||||
const handleToStationSelect = (val: string) => {
|
||||
const found = toStationOptions.value.find((item) => item.value === val);
|
||||
if (found) {
|
||||
// TODO: 从后端获取对应站点的经纬度,目前使用示例坐标
|
||||
formState.toLocPosition = '121.4737,31.2304';
|
||||
}
|
||||
};
|
||||
|
||||
// 起运地门点地址搜索选择
|
||||
const handleRealSelect = (selectedTitle: string, option: any) => {
|
||||
const selectedItem = option.data;
|
||||
formState.fromLocPosition = `${selectedItem.point.lng},${selectedItem.point.lat}`;
|
||||
};
|
||||
|
||||
// 起运地搜索
|
||||
const handleLocationSearch = async (value: string) => {
|
||||
realInputValue = value;
|
||||
if (!value.trim()) {
|
||||
locationOptions.value = [];
|
||||
return;
|
||||
}
|
||||
try {
|
||||
getBaiduPlace(value).then((res) => {
|
||||
locationOptions.value = res.result.results.map((item: any) => ({
|
||||
title: item.name,
|
||||
point: { lat: item.location.lat, lng: item.location.lng },
|
||||
uid: item.uid,
|
||||
}));
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('搜索失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 起运地下拉隐藏
|
||||
const handleDropdownHide = (visible: boolean) => {
|
||||
if (!visible) {
|
||||
nextTick(() => {
|
||||
formState.fromLoc = realInputValue;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// 目的地门点地址选择
|
||||
const handleDoorToChange = (val: string) => {
|
||||
if (val) {
|
||||
// TODO: 根据实际数据获取经纬度
|
||||
formState.toLocPosition = '121.4737,31.2304';
|
||||
}
|
||||
};
|
||||
|
||||
// 选择坐标(起运地或目的地)
|
||||
const handleSelectCoordinates = (type: 'from' | 'to') => {
|
||||
currentCoordType = type;
|
||||
locationSelectRef.value.showModal(formState);
|
||||
};
|
||||
|
||||
// 坐标选择回调
|
||||
const changeLocation = (data: { lng: number; lat: number }) => {
|
||||
const position = `${data.lng},${data.lat}`;
|
||||
if (currentCoordType === 'from') {
|
||||
formState.fromLocPosition = position;
|
||||
} else {
|
||||
formState.toLocPosition = position;
|
||||
}
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
visible.value = false;
|
||||
};
|
||||
|
||||
// 提交前处理:将UI类型映射到后端字段
|
||||
const prepareSubmitData = () => {
|
||||
const submitData = { ...formState };
|
||||
|
||||
// 根据UI类型设置后端字段(如果需要映射)
|
||||
// 注意:fromLocType 和 toLocType 保持后端支持的值
|
||||
// 如果 UI 类型是 railway,可以设置 fromLocType = 'railway' 等
|
||||
// 但根据你的说明,后端不支持其他值,所以保持原样
|
||||
|
||||
// 删除前端UI控制字段(不提交给后端)
|
||||
delete submitData.fromLocUIType;
|
||||
delete submitData.toLocUIType;
|
||||
|
||||
return submitData;
|
||||
};
|
||||
|
||||
const handleOk = async () => {
|
||||
// 阶梯价校验
|
||||
if (formState.priceType === '2') {
|
||||
if (!formState.elementTieredPriceList || formState.elementTieredPriceList.length === 0) {
|
||||
message.error('请设置梯度价格');
|
||||
return;
|
||||
}
|
||||
const invalidRows = formState.elementTieredPriceList
|
||||
.map((item: any, index: number) => ({
|
||||
index: index + 1,
|
||||
hasError: !item.distance || !item.price,
|
||||
}))
|
||||
.filter((item: any) => item.hasError);
|
||||
if (invalidRows.length > 0) {
|
||||
message.error(`第 ${invalidRows.map((r: any) => r.index).join(', ')} 行的距离或价格未填写`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 一口价校验
|
||||
if (formState.priceType === '1') {
|
||||
if (!formState.fromLocPosition) {
|
||||
message.error('请选择起运地经纬度');
|
||||
return;
|
||||
}
|
||||
if (!formState.toLocPosition) {
|
||||
message.error('请选择目的地经纬度');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
await formRef.value?.validateFields();
|
||||
|
||||
const submitData = prepareSubmitData();
|
||||
|
||||
if (formState.id) {
|
||||
editTruckInfo(submitData).then((res) => {
|
||||
if (res.code === '0000') {
|
||||
message.success('更新成功');
|
||||
emit('refresh');
|
||||
visible.value = false;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
addTruckInfo(submitData).then((res) => {
|
||||
if (res.code === '0000') {
|
||||
message.success('新增成功');
|
||||
emit('refresh');
|
||||
visible.value = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('验证失败:', error);
|
||||
message.error('请检查表单数据');
|
||||
}
|
||||
};
|
||||
|
||||
defineExpose({ showModal });
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
Reference in New Issue
Block a user