<template>
|
<div class="h5-task-create">
|
<!-- 头部 -->
|
<div class="header">
|
<div class="header-left" @click="goBack">
|
<i class="el-icon-arrow-left"></i>
|
<span>返回</span>
|
</div>
|
<div class="header-title">创建任务</div>
|
<div class="header-right"></div>
|
</div>
|
|
<!-- 表单内容 -->
|
<div class="form-container">
|
<el-form ref="taskForm" :model="taskForm" :rules="taskRules" label-width="100px">
|
<!-- 任务类型 -->
|
<div class="form-section">
|
<div class="section-title">任务类型</div>
|
<el-form-item prop="taskType">
|
<el-select v-model="taskForm.taskType" placeholder="请选择任务类型" style="width: 100%">
|
<el-option
|
v-for="dict in dict.type.sys_task_type"
|
:key="dict.value"
|
:label="dict.label"
|
:value="dict.value"
|
/>
|
</el-select>
|
</el-form-item>
|
</div>
|
|
<!-- 任务描述 -->
|
<div class="form-section">
|
<div class="section-title">任务描述</div>
|
<el-form-item prop="taskDescription">
|
<el-input
|
v-model="taskForm.taskDescription"
|
type="textarea"
|
:rows="3"
|
placeholder="请输入任务描述"
|
maxlength="200"
|
show-word-limit
|
/>
|
</el-form-item>
|
</div>
|
|
<!-- 车辆选择 -->
|
<div class="form-section">
|
<div class="section-title">选择车辆</div>
|
<el-form-item prop="vehicleIds">
|
<div class="vehicle-selector" @click="showVehicleSelector = true">
|
<div v-if="selectedVehicles.length === 0" class="placeholder">
|
请选择车辆
|
</div>
|
<div v-else class="selected-vehicles">
|
<div v-for="vehicle in selectedVehicles" :key="vehicle.vehicleId" class="vehicle-item">
|
<span class="vehicle-no">{{ vehicle.vehicleNo }}</span>
|
<span class="vehicle-type">{{ getVehicleTypeName(vehicle.vehicleType) }}</span>
|
<i class="el-icon-close" @click.stop="removeVehicle(vehicle.vehicleId)"></i>
|
</div>
|
</div>
|
<i class="el-icon-arrow-right"></i>
|
</div>
|
</el-form-item>
|
</div>
|
|
<!-- 地址选择 -->
|
<div class="form-section">
|
<div class="section-title">出发地址</div>
|
<el-form-item prop="departureAddress">
|
<div class="address-selector" @click="selectAddress('departure')">
|
<div v-if="!taskForm.departureAddress" class="placeholder">
|
点击选择出发地址
|
</div>
|
<div v-else class="address-content">
|
<i class="el-icon-location"></i>
|
<span>{{ taskForm.departureAddress }}</span>
|
</div>
|
<i class="el-icon-arrow-right"></i>
|
</div>
|
</el-form-item>
|
</div>
|
|
<div class="form-section">
|
<div class="section-title">目标地址</div>
|
<el-form-item prop="destinationAddress">
|
<div class="address-selector" @click="selectAddress('destination')">
|
<div v-if="!taskForm.destinationAddress" class="placeholder">
|
点击选择目标地址
|
</div>
|
<div v-else class="address-content">
|
<i class="el-icon-location"></i>
|
<span>{{ taskForm.destinationAddress }}</span>
|
</div>
|
<i class="el-icon-arrow-right"></i>
|
</div>
|
</el-form-item>
|
</div>
|
|
<!-- 时间选择 -->
|
<div class="form-section">
|
<div class="section-title">计划时间</div>
|
<el-form-item prop="plannedStartTime">
|
<div class="time-label">开始时间</div>
|
<el-date-picker
|
v-model="taskForm.plannedStartTime"
|
type="datetime"
|
placeholder="选择开始时间"
|
value-format="yyyy-MM-dd HH:mm:ss"
|
style="width: 100%"
|
/>
|
</el-form-item>
|
<el-form-item prop="plannedEndTime">
|
<div class="time-label">结束时间</div>
|
<el-date-picker
|
v-model="taskForm.plannedEndTime"
|
type="datetime"
|
placeholder="选择结束时间"
|
value-format="yyyy-MM-dd HH:mm:ss"
|
style="width: 100%"
|
/>
|
</el-form-item>
|
</div>
|
|
<!-- 执行人选择 -->
|
<div class="form-section">
|
<div class="section-title">执行人</div>
|
<el-form-item prop="assigneeId">
|
<el-select v-model="taskForm.assigneeId" placeholder="请选择执行人" style="width: 100%">
|
<el-option
|
v-for="user in userList"
|
:key="user.userId"
|
:label="user.nickName"
|
:value="user.userId"
|
/>
|
</el-select>
|
</el-form-item>
|
</div>
|
|
<!-- 备注 -->
|
<div class="form-section">
|
<div class="section-title">备注</div>
|
<el-form-item prop="remark">
|
<el-input
|
v-model="taskForm.remark"
|
type="textarea"
|
:rows="2"
|
placeholder="请输入备注信息"
|
maxlength="100"
|
show-word-limit
|
/>
|
</el-form-item>
|
</div>
|
</el-form>
|
</div>
|
|
<!-- 提交按钮 -->
|
<div class="submit-container">
|
<el-button type="primary" size="large" @click="submitTask" :loading="submitting" style="width: 100%">
|
创建任务
|
</el-button>
|
</div>
|
|
<!-- 车辆选择弹窗 -->
|
<el-dialog
|
title="选择车辆"
|
:visible.sync="showVehicleSelector"
|
width="90%"
|
:close-on-click-modal="false"
|
class="vehicle-dialog"
|
:modal="false"
|
:append-to-body="true"
|
:top="'10vh'"
|
>
|
<div class="vehicle-list">
|
<div
|
v-for="vehicle in availableVehicles"
|
:key="vehicle.vehicleId"
|
class="vehicle-item"
|
:class="{ selected: isVehicleSelected(vehicle.vehicleId) }"
|
@click="toggleVehicle(vehicle)"
|
>
|
<div class="vehicle-info">
|
<div class="vehicle-no">{{ vehicle.vehicleNo }}</div>
|
<div class="vehicle-details">
|
<span class="vehicle-type">{{ getVehicleTypeName(vehicle.vehicleType) }}</span>
|
<span class="vehicle-brand">{{ vehicle.vehicleBrand }} {{ vehicle.vehicleModel }}</span>
|
</div>
|
<div class="vehicle-dept">{{ vehicle.deptName }}</div>
|
</div>
|
<div class="vehicle-status">
|
<i v-if="isVehicleSelected(vehicle.vehicleId)" class="el-icon-check selected-icon"></i>
|
<i v-else class="el-icon-circle-check-outline unselected-icon"></i>
|
</div>
|
</div>
|
</div>
|
<div slot="footer" class="dialog-footer">
|
<el-button @click="showVehicleSelector = false">取消</el-button>
|
<el-button type="primary" @click="confirmVehicleSelection">确定</el-button>
|
</div>
|
</el-dialog>
|
|
<!-- 地图选择弹窗 -->
|
<el-dialog
|
:title="addressDialogTitle"
|
:visible.sync="showMapSelector"
|
width="95%"
|
:close-on-click-modal="false"
|
class="map-dialog"
|
:modal="false"
|
:append-to-body="true"
|
:top="'5vh'"
|
@close="closeMapSelector"
|
>
|
<div class="address-selector-container">
|
<!-- 地址搜索框 -->
|
<div class="address-search">
|
<el-input
|
v-model="searchKeyword"
|
placeholder="请输入地址进行搜索"
|
@input="searchAddress"
|
clearable
|
class="search-input"
|
>
|
<i slot="prefix" class="el-icon-search"></i>
|
</el-input>
|
</div>
|
|
<!-- 搜索结果列表 -->
|
<div v-if="searchResults.length > 0" class="search-results">
|
<div class="results-title">搜索结果</div>
|
<div class="result-list">
|
<div
|
v-for="(result, index) in searchResults"
|
:key="index"
|
class="result-item"
|
@click="selectSearchResult(result)"
|
>
|
<div class="result-address">{{ result.address }}</div>
|
<div class="result-detail">{{ result.detail }}</div>
|
</div>
|
</div>
|
</div>
|
|
<!-- 地图容器 -->
|
<div class="map-container">
|
<div id="mapContainer" class="map"></div>
|
<div class="map-controls">
|
<el-button type="primary" size="small" @click="getCurrentLocation">
|
<i class="el-icon-location"></i> 当前位置
|
</el-button>
|
</div>
|
</div>
|
|
<!-- 选中地址信息 -->
|
<div v-if="selectedLocation && selectedAddress" class="selected-address-info">
|
<div class="info-title">选中地址</div>
|
<div class="address-info">
|
<div class="address-text">{{ selectedAddress }}</div>
|
<div class="coordinates">坐标:{{ selectedLocation.lng.toFixed(6) }}, {{ selectedLocation.lat.toFixed(6) }}</div>
|
</div>
|
</div>
|
</div>
|
<div slot="footer" class="dialog-footer">
|
<el-button @click="closeMapSelector">取消</el-button>
|
<el-button type="primary" @click="confirmAddressSelection" :disabled="!selectedLocation">
|
确认选择
|
</el-button>
|
</div>
|
</el-dialog>
|
</div>
|
</template>
|
|
<script>
|
import { addTask, getAvailableVehicles } from "@/api/task";
|
import { listUser } from "@/api/system/user";
|
|
export default {
|
name: "H5TaskCreate",
|
dicts: ['sys_task_type', 'sys_vehicle_type'],
|
data() {
|
return {
|
// 表单数据
|
taskForm: {
|
taskType: '',
|
taskDescription: '',
|
vehicleIds: [],
|
departureAddress: '',
|
destinationAddress: '',
|
plannedStartTime: '',
|
plannedEndTime: '',
|
assigneeId: '',
|
remark: ''
|
},
|
// 表单验证规则
|
taskRules: {
|
taskType: [
|
{ required: true, message: "请选择任务类型", trigger: "change" }
|
],
|
taskDescription: [
|
{ required: true, message: "请输入任务描述", trigger: "blur" }
|
],
|
vehicleIds: [
|
{ required: true, message: "请选择车辆", trigger: "change" }
|
],
|
departureAddress: [
|
{ required: true, message: "请选择出发地址", trigger: "change" }
|
],
|
destinationAddress: [
|
{ required: true, message: "请选择目标地址", trigger: "change" }
|
],
|
plannedStartTime: [
|
{ required: true, message: "请选择开始时间", trigger: "change" }
|
],
|
plannedEndTime: [
|
{ required: true, message: "请选择结束时间", trigger: "change" }
|
],
|
assigneeId: [
|
{ required: true, message: "请选择执行人", trigger: "change" }
|
]
|
},
|
// 用户列表
|
userList: [],
|
// 可用车辆列表
|
availableVehicles: [],
|
// 选中的车辆
|
selectedVehicles: [],
|
// 弹窗控制
|
showVehicleSelector: false,
|
showMapSelector: false,
|
currentAddressType: '', // 'departure' 或 'destination'
|
// 地图相关
|
map: null,
|
marker: null,
|
selectedLocation: null,
|
selectedAddress: '',
|
// 地址搜索相关
|
searchKeyword: '',
|
searchResults: [],
|
searchTimer: null,
|
// 提交状态
|
submitting: false
|
};
|
},
|
computed: {
|
addressDialogTitle() {
|
return this.currentAddressType === 'departure' ? '选择出发地址' : '选择目标地址';
|
}
|
},
|
created() {
|
this.getUserList();
|
this.getAvailableVehicleList();
|
},
|
mounted() {
|
// 等待地图API加载完成后再初始化地图
|
this.waitForMapAPI();
|
},
|
methods: {
|
/** 等待地图API加载完成 */
|
waitForMapAPI() {
|
if (window.BMap) {
|
console.log('百度地图API已存在,直接初始化');
|
this.$nextTick(() => {
|
this.initMap();
|
});
|
return;
|
}
|
|
console.log('开始加载百度地图API...');
|
this.loadMapConfig();
|
},
|
|
/** 动态加载地图配置 */
|
loadMapConfig() {
|
// 检查是否已经加载过地图配置
|
if (window.BMapConfig) {
|
console.log('地图配置已存在,直接加载API');
|
this.loadBMapAPI();
|
return;
|
}
|
|
// 动态加载地图配置文件
|
const script = document.createElement('script');
|
script.type = 'text/javascript';
|
script.src = '/map-config.js';
|
script.onload = () => {
|
console.log('地图配置文件加载完成');
|
this.loadBMapAPI();
|
};
|
script.onerror = (error) => {
|
console.error('地图配置文件加载失败:', error);
|
this.$message.error('地图配置加载失败,请刷新页面重试');
|
};
|
document.head.appendChild(script);
|
},
|
|
/** 加载百度地图API */
|
loadBMapAPI() {
|
if (window.BMap) {
|
console.log('百度地图API已存在');
|
// 等待API完全初始化
|
this.waitForBMapReady();
|
return;
|
}
|
|
console.log('开始加载百度地图API...');
|
|
// 监听地图加载完成事件
|
const handleMapLoaded = () => {
|
console.log('收到地图加载完成事件');
|
this.waitForBMapReady();
|
window.removeEventListener('bmapLoaded', handleMapLoaded);
|
window.removeEventListener('bmapError', handleMapError);
|
};
|
|
// 监听地图加载错误事件
|
const handleMapError = (event) => {
|
console.error('地图API加载失败:', event.detail);
|
this.$message.error('地图服务加载失败,请刷新页面重试');
|
window.removeEventListener('bmapLoaded', handleMapLoaded);
|
window.removeEventListener('bmapError', handleMapError);
|
};
|
|
window.addEventListener('bmapLoaded', handleMapLoaded);
|
window.addEventListener('bmapError', handleMapError);
|
|
// 设置超时,如果10秒内没有加载完成,显示错误信息
|
setTimeout(() => {
|
if (!window.BMap) {
|
console.error('地图API加载超时');
|
this.$message.error('地图服务加载超时,请检查网络连接');
|
window.removeEventListener('bmapLoaded', handleMapLoaded);
|
window.removeEventListener('bmapError', handleMapError);
|
}
|
}, 10000);
|
},
|
|
/** 等待百度地图API完全准备就绪 */
|
waitForBMapReady() {
|
const checkReady = () => {
|
if (window.BMap && window.BMap.Map && window.BMap.Point && window.BMap.Marker) {
|
console.log('百度地图API完全准备就绪');
|
this.$nextTick(() => {
|
this.initMap();
|
});
|
} else {
|
console.log('等待百度地图API完全加载...');
|
setTimeout(checkReady, 100);
|
}
|
};
|
checkReady();
|
},
|
|
/** 返回上一页 */
|
goBack() {
|
this.$router.go(-1);
|
},
|
|
/** 获取用户列表 */
|
getUserList() {
|
listUser().then(response => {
|
this.userList = response.rows;
|
});
|
},
|
|
/** 获取可用车辆列表 */
|
getAvailableVehicleList() {
|
// 这里需要传入当前用户的部门ID,暂时使用默认值
|
const deptId = this.$store.getters.deptId || 1;
|
getAvailableVehicles(deptId, null).then(response => {
|
this.availableVehicles = response.data || [];
|
});
|
},
|
|
/** 获取车辆类型名称 */
|
getVehicleTypeName(vehicleType) {
|
const typeDict = this.dict.type.sys_vehicle_type;
|
if (typeDict && typeDict.length > 0) {
|
const typeItem = typeDict.find(item => item.value === vehicleType);
|
return typeItem ? typeItem.label : vehicleType;
|
}
|
return vehicleType;
|
},
|
|
/** 检查车辆是否已选中 */
|
isVehicleSelected(vehicleId) {
|
return this.selectedVehicles.some(v => v.vehicleId === vehicleId);
|
},
|
|
/** 切换车辆选择状态 */
|
toggleVehicle(vehicle) {
|
const index = this.selectedVehicles.findIndex(v => v.vehicleId === vehicle.vehicleId);
|
if (index > -1) {
|
this.selectedVehicles.splice(index, 1);
|
} else {
|
this.selectedVehicles.push(vehicle);
|
}
|
},
|
|
/** 移除车辆 */
|
removeVehicle(vehicleId) {
|
const index = this.selectedVehicles.findIndex(v => v.vehicleId === vehicleId);
|
if (index > -1) {
|
this.selectedVehicles.splice(index, 1);
|
this.updateVehicleIds();
|
}
|
},
|
|
/** 确认车辆选择 */
|
confirmVehicleSelection() {
|
this.updateVehicleIds();
|
this.showVehicleSelector = false;
|
},
|
|
/** 更新车辆ID列表 */
|
updateVehicleIds() {
|
this.taskForm.vehicleIds = this.selectedVehicles.map(v => v.vehicleId);
|
},
|
|
/** 选择地址 */
|
selectAddress(type) {
|
this.currentAddressType = type;
|
this.showMapSelector = true;
|
// 重置搜索状态
|
this.searchKeyword = '';
|
this.searchResults = [];
|
this.selectedLocation = null;
|
this.selectedAddress = '';
|
// 等待弹窗完全打开后再初始化地图
|
this.$nextTick(() => {
|
// 使用setTimeout确保弹窗动画完成
|
setTimeout(() => {
|
this.initMap();
|
}, 300);
|
});
|
},
|
|
/** 初始化地图 */
|
initMap() {
|
if (!window.BMap) {
|
console.error('百度地图API未加载');
|
this.$message.error('地图服务加载失败,请检查网络连接');
|
return;
|
}
|
|
// 检查地图容器是否存在,如果不存在则重试
|
const mapContainer = document.getElementById('mapContainer');
|
if (!mapContainer) {
|
console.log('地图容器不存在,500ms后重试...');
|
setTimeout(() => {
|
this.initMap();
|
}, 500);
|
return;
|
}
|
|
// 检查容器是否有尺寸
|
if (mapContainer.offsetWidth === 0 || mapContainer.offsetHeight === 0) {
|
console.log('地图容器尺寸为0,500ms后重试...');
|
setTimeout(() => {
|
this.initMap();
|
}, 500);
|
return;
|
}
|
|
try {
|
if (this.map) {
|
this.map.clearOverlays();
|
}
|
|
// 创建地图实例
|
this.map = new BMap.Map('mapContainer');
|
this.map.centerAndZoom(new BMap.Point(116.404, 39.915), 15);
|
|
// 添加地图控件
|
this.map.addControl(new BMap.NavigationControl());
|
this.map.addControl(new BMap.ScaleControl());
|
this.map.addControl(new BMap.OverviewMapControl());
|
this.map.addControl(new BMap.MapTypeControl());
|
|
// 启用滚轮放大缩小
|
this.map.enableScrollWheelZoom(true);
|
|
// 添加点击事件
|
this.map.addEventListener('click', (e) => {
|
this.handleMapClick(e);
|
});
|
|
// 添加标记
|
const center = this.map.getCenter();
|
this.marker = new BMap.Marker(center, {
|
enableDragging: true
|
});
|
this.map.addOverlay(this.marker);
|
|
// 标记拖拽事件
|
this.marker.addEventListener('dragend', (e) => {
|
this.handleMarkerDrag(e);
|
});
|
|
console.log('地图初始化成功');
|
} catch (error) {
|
console.error('地图初始化失败:', error);
|
this.$message.error('地图初始化失败,请重试');
|
}
|
},
|
|
/** 地图点击事件 */
|
handleMapClick(e) {
|
const point = e.latlng;
|
this.selectedLocation = { lng: point.lng, lat: point.lat };
|
this.marker.setPosition(point);
|
this.reverseGeocode(point.lng, point.lat);
|
},
|
|
/** 标记拖拽事件 */
|
handleMarkerDrag(e) {
|
const point = e.target.getPosition();
|
this.selectedLocation = { lng: point.lng, lat: point.lat };
|
this.reverseGeocode(point.lng, point.lat);
|
},
|
|
/** 逆地理编码 */
|
reverseGeocode(lng, lat) {
|
if (!window.BMap) {
|
console.error('百度地图API未加载');
|
return;
|
}
|
|
try {
|
const geocoder = new BMap.Geocoder();
|
const point = new BMap.Point(lng, lat);
|
geocoder.getLocation(point, (result) => {
|
if (result) {
|
this.selectedAddress = result.address;
|
}
|
});
|
} catch (error) {
|
console.error('逆地理编码失败:', error);
|
}
|
},
|
|
/** 获取当前位置 */
|
getCurrentLocation() {
|
if (!window.BMap) {
|
this.$message.error('地图服务未加载');
|
return;
|
}
|
|
if (!this.map) {
|
this.$message.error('地图未初始化');
|
return;
|
}
|
|
try {
|
const geolocation = new BMap.Geolocation();
|
geolocation.getCurrentPosition((result) => {
|
if (geolocation.getStatus() === BMAP_STATUS_SUCCESS) {
|
const point = result.point;
|
this.selectedLocation = { lng: point.lng, lat: point.lat };
|
if (this.marker) {
|
this.marker.setPosition(point);
|
}
|
this.map.setCenter(point);
|
this.map.setZoom(16);
|
this.reverseGeocode(point.lng, point.lat);
|
} else {
|
this.$message.error('定位失败,请手动选择位置');
|
}
|
});
|
} catch (error) {
|
console.error('获取当前位置失败:', error);
|
this.$message.error('定位失败,请手动选择位置');
|
}
|
},
|
|
/** 地址搜索 */
|
searchAddress() {
|
if (this.searchTimer) {
|
clearTimeout(this.searchTimer);
|
}
|
|
if (!this.searchKeyword.trim()) {
|
this.searchResults = [];
|
return;
|
}
|
|
this.searchTimer = setTimeout(() => {
|
this.performAddressSearch(this.searchKeyword);
|
}, 500);
|
},
|
|
/** 执行地址搜索 */
|
performAddressSearch(keyword) {
|
if (!window.BMap) {
|
this.$message.error('地图服务未加载');
|
return;
|
}
|
|
if (!this.map) {
|
this.$message.error('地图未初始化');
|
return;
|
}
|
|
try {
|
const localSearch = new BMap.LocalSearch(this.map, {
|
onSearchComplete: (results) => {
|
this.searchResults = [];
|
if (localSearch.getStatus() === BMAP_STATUS_SUCCESS) {
|
for (let i = 0; i < results.getCurrentNumPois(); i++) {
|
const poi = results.getPoi(i);
|
this.searchResults.push({
|
address: poi.title,
|
detail: poi.address,
|
point: poi.point,
|
lng: poi.point.lng,
|
lat: poi.point.lat
|
});
|
}
|
}
|
}
|
});
|
|
localSearch.search(keyword);
|
} catch (error) {
|
console.error('地址搜索失败:', error);
|
this.$message.error('地址搜索失败,请重试');
|
}
|
},
|
|
/** 选择搜索结果 */
|
selectSearchResult(result) {
|
if (!window.BMap) {
|
this.$message.error('地图服务未加载');
|
return;
|
}
|
|
try {
|
this.selectedLocation = {
|
lng: result.lng,
|
lat: result.lat
|
};
|
this.selectedAddress = result.address;
|
|
// 移动地图到选中位置
|
if (this.map) {
|
const point = new BMap.Point(result.lng, result.lat);
|
this.map.setCenter(point);
|
this.map.setZoom(16);
|
|
// 更新标记位置
|
if (this.marker) {
|
this.marker.setPosition(point);
|
}
|
}
|
|
// 清空搜索结果
|
this.searchResults = [];
|
this.searchKeyword = '';
|
} catch (error) {
|
console.error('选择搜索结果失败:', error);
|
this.$message.error('选择地址失败,请重试');
|
}
|
},
|
|
/** 确认地址选择 */
|
confirmAddressSelection() {
|
if (!this.selectedLocation || !this.selectedAddress) {
|
this.$message.warning('请先选择位置');
|
return;
|
}
|
|
// 设置选中的地址到表单
|
if (this.currentAddressType === 'departure') {
|
this.taskForm.departureAddress = this.selectedAddress;
|
} else {
|
this.taskForm.destinationAddress = this.selectedAddress;
|
}
|
|
this.closeMapSelector();
|
},
|
|
/** 关闭地图选择器 */
|
closeMapSelector() {
|
this.showMapSelector = false;
|
// 清理地图实例
|
if (this.map) {
|
this.map.clearOverlays();
|
this.map = null;
|
}
|
this.marker = null;
|
this.selectedLocation = null;
|
this.selectedAddress = '';
|
this.searchResults = [];
|
this.searchKeyword = '';
|
},
|
|
/** 提交任务 */
|
submitTask() {
|
this.$refs.taskForm.validate(valid => {
|
if (valid) {
|
this.submitting = true;
|
|
// 准备提交数据
|
const submitData = {
|
...this.taskForm,
|
vehicleIds: this.taskForm.vehicleIds // 直接传递数组,不需要转换为字符串
|
};
|
|
addTask(submitData).then(response => {
|
this.$message.success('任务创建成功');
|
this.submitting = false;
|
// 跳转到任务列表或详情页
|
this.$router.push('/task/general');
|
}).catch(error => {
|
this.$message.error('任务创建失败:' + (error.message || '未知错误'));
|
this.submitting = false;
|
});
|
}
|
});
|
}
|
}
|
};
|
</script>
|
|
<style scoped>
|
.h5-task-create {
|
min-height: 100vh;
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
padding-bottom: 100px;
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
}
|
|
/* 头部样式 */
|
.header {
|
display: flex;
|
align-items: center;
|
justify-content: space-between;
|
height: 60px;
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
color: white;
|
padding: 0 20px;
|
position: sticky;
|
top: 0;
|
z-index: 100;
|
box-shadow: 0 2px 20px rgba(0,0,0,0.1);
|
}
|
|
.header-left {
|
display: flex;
|
align-items: center;
|
cursor: pointer;
|
padding: 8px 12px;
|
border-radius: 20px;
|
background: rgba(255,255,255,0.2);
|
backdrop-filter: blur(10px);
|
transition: all 0.3s ease;
|
}
|
|
.header-left:hover {
|
background: rgba(255,255,255,0.3);
|
transform: translateX(-2px);
|
}
|
|
.header-left i {
|
margin-right: 6px;
|
font-size: 18px;
|
}
|
|
.header-title {
|
font-size: 20px;
|
font-weight: 600;
|
letter-spacing: 0.5px;
|
}
|
|
/* 表单容器 */
|
.form-container {
|
padding: 20px;
|
}
|
|
.form-section {
|
background: rgba(255,255,255,0.95);
|
backdrop-filter: blur(20px);
|
border-radius: 16px;
|
margin-bottom: 20px;
|
padding: 20px;
|
box-shadow: 0 8px 32px rgba(0,0,0,0.1);
|
border: 1px solid rgba(255,255,255,0.2);
|
transition: all 0.3s ease;
|
}
|
|
.form-section:hover {
|
transform: translateY(-2px);
|
box-shadow: 0 12px 40px rgba(0,0,0,0.15);
|
}
|
|
.section-title {
|
font-size: 18px;
|
font-weight: 600;
|
color: #2c3e50;
|
margin-bottom: 20px;
|
padding-bottom: 12px;
|
border-bottom: 2px solid #e8f4fd;
|
position: relative;
|
}
|
|
.section-title::after {
|
content: '';
|
position: absolute;
|
bottom: -2px;
|
left: 0;
|
width: 40px;
|
height: 2px;
|
background: linear-gradient(90deg, #667eea, #764ba2);
|
border-radius: 1px;
|
}
|
|
.time-label {
|
font-size: 15px;
|
color: #5a6c7d;
|
margin-bottom: 10px;
|
font-weight: 500;
|
}
|
|
/* 车辆选择器 */
|
.vehicle-selector {
|
display: flex;
|
align-items: center;
|
justify-content: space-between;
|
padding: 16px;
|
border: 2px solid #e8f4fd;
|
border-radius: 12px;
|
background: linear-gradient(135deg, #f8fbff 0%, #f0f9ff 100%);
|
cursor: pointer;
|
min-height: 24px;
|
transition: all 0.3s ease;
|
}
|
|
.vehicle-selector:hover {
|
border-color: #667eea;
|
background: linear-gradient(135deg, #f0f9ff 0%, #e8f4fd 100%);
|
transform: translateY(-1px);
|
}
|
|
.placeholder {
|
color: #a0aec0;
|
font-size: 15px;
|
}
|
|
.selected-vehicles {
|
flex: 1;
|
display: flex;
|
flex-wrap: wrap;
|
gap: 8px;
|
}
|
|
.vehicle-item {
|
display: inline-flex;
|
align-items: center;
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
color: white;
|
border-radius: 20px;
|
padding: 6px 12px;
|
font-size: 13px;
|
font-weight: 500;
|
box-shadow: 0 2px 8px rgba(102, 126, 234, 0.3);
|
transition: all 0.3s ease;
|
}
|
|
.vehicle-item:hover {
|
transform: scale(1.05);
|
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
|
}
|
|
.vehicle-no {
|
font-weight: 600;
|
margin-right: 6px;
|
}
|
|
.vehicle-type {
|
opacity: 0.9;
|
margin-right: 6px;
|
}
|
|
.vehicle-item .el-icon-close {
|
cursor: pointer;
|
margin-left: 6px;
|
padding: 2px;
|
border-radius: 50%;
|
background: rgba(255,255,255,0.2);
|
transition: all 0.3s ease;
|
}
|
|
.vehicle-item .el-icon-close:hover {
|
background: rgba(255,255,255,0.3);
|
transform: scale(1.1);
|
}
|
|
/* 地址选择器 */
|
.address-selector {
|
display: flex;
|
align-items: center;
|
justify-content: space-between;
|
padding: 16px;
|
border: 2px solid #e8f4fd;
|
border-radius: 12px;
|
background: linear-gradient(135deg, #f8fbff 0%, #f0f9ff 100%);
|
cursor: pointer;
|
min-height: 24px;
|
transition: all 0.3s ease;
|
}
|
|
.address-selector:hover {
|
border-color: #667eea;
|
background: linear-gradient(135deg, #f0f9ff 0%, #e8f4fd 100%);
|
transform: translateY(-1px);
|
}
|
|
.address-content {
|
display: flex;
|
align-items: center;
|
flex: 1;
|
}
|
|
.address-content i {
|
margin-right: 10px;
|
color: #667eea;
|
font-size: 16px;
|
}
|
|
/* 提交按钮容器 */
|
.submit-container {
|
position: fixed;
|
bottom: 0;
|
left: 0;
|
right: 0;
|
padding: 20px;
|
background: rgba(255,255,255,0.95);
|
backdrop-filter: blur(20px);
|
border-top: 1px solid rgba(255,255,255,0.2);
|
box-shadow: 0 -8px 32px rgba(0,0,0,0.1);
|
}
|
|
/* 车辆选择弹窗 */
|
.vehicle-dialog .el-dialog {
|
border-radius: 16px;
|
overflow: hidden;
|
max-height: 80vh;
|
display: flex;
|
flex-direction: column;
|
}
|
|
.vehicle-dialog .el-dialog__body {
|
padding: 0;
|
flex: 1;
|
overflow: hidden;
|
display: flex;
|
flex-direction: column;
|
}
|
|
.vehicle-dialog .el-dialog__footer {
|
padding: 15px 20px;
|
background: white;
|
border-top: 1px solid #e8f4fd;
|
flex-shrink: 0;
|
}
|
|
.vehicle-list {
|
flex: 1;
|
overflow-y: auto;
|
background: #f8fbff;
|
max-height: 60vh;
|
}
|
|
.vehicle-item {
|
display: flex;
|
align-items: center;
|
justify-content: space-between;
|
padding: 20px;
|
border-bottom: 1px solid #e8f4fd;
|
cursor: pointer;
|
transition: all 0.3s ease;
|
background: white;
|
margin: 0;
|
}
|
|
.vehicle-item:hover {
|
background: linear-gradient(135deg, #f0f9ff 0%, #e8f4fd 100%);
|
transform: translateX(4px);
|
}
|
|
.vehicle-item.selected {
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
color: white;
|
border-left: 4px solid #4facfe;
|
}
|
|
.vehicle-info {
|
flex: 1;
|
}
|
|
.vehicle-no {
|
font-size: 17px;
|
font-weight: 600;
|
color: #2c3e50;
|
margin-bottom: 6px;
|
background: #f0f9ff;
|
padding: 4px 8px;
|
border-radius: 4px;
|
display: inline-block;
|
border: 1px solid #e8f4fd;
|
}
|
|
.vehicle-item.selected .vehicle-no {
|
color: white;
|
background: rgba(255,255,255,0.2);
|
border-color: rgba(255,255,255,0.3);
|
}
|
|
.vehicle-details {
|
font-size: 14px;
|
color: #5a6c7d;
|
margin-bottom: 4px;
|
}
|
|
.vehicle-item.selected .vehicle-details {
|
color: rgba(255,255,255,0.9);
|
}
|
|
.vehicle-dept {
|
font-size: 12px;
|
color: #999;
|
}
|
|
.vehicle-item.selected .vehicle-dept {
|
color: rgba(255,255,255,0.7);
|
}
|
|
.vehicle-status {
|
margin-left: 15px;
|
}
|
|
.selected-icon {
|
color: #4facfe;
|
font-size: 24px;
|
}
|
|
.unselected-icon {
|
color: #c0c4cc;
|
font-size: 24px;
|
}
|
|
/* 地图弹窗 */
|
.map-dialog .el-dialog {
|
border-radius: 16px;
|
overflow: hidden;
|
max-height: 90vh;
|
display: flex;
|
flex-direction: column;
|
}
|
|
.map-dialog .el-dialog__body {
|
padding: 0;
|
flex: 1;
|
overflow: hidden;
|
display: flex;
|
flex-direction: column;
|
}
|
|
.map-dialog .el-dialog__footer {
|
padding: 15px 20px;
|
background: white;
|
border-top: 1px solid #e8f4fd;
|
flex-shrink: 0;
|
}
|
|
.address-selector-container {
|
background: #f8fbff;
|
flex: 1;
|
display: flex;
|
flex-direction: column;
|
overflow: hidden;
|
}
|
|
/* 地址搜索 */
|
.address-search {
|
padding: 20px;
|
background: white;
|
border-bottom: 1px solid #e8f4fd;
|
}
|
|
.search-input {
|
width: 100%;
|
}
|
|
.search-input .el-input__inner {
|
border-radius: 25px;
|
border: 2px solid #e8f4fd;
|
padding-left: 40px;
|
font-size: 15px;
|
transition: all 0.3s ease;
|
}
|
|
.search-input .el-input__inner:focus {
|
border-color: #667eea;
|
box-shadow: 0 0 0 2px rgba(102, 126, 234, 0.1);
|
}
|
|
.search-input .el-input__prefix {
|
left: 12px;
|
color: #667eea;
|
}
|
|
/* 搜索结果 */
|
.search-results {
|
background: white;
|
border-bottom: 1px solid #e8f4fd;
|
max-height: 30vh;
|
min-height: 100px;
|
overflow-y: auto;
|
flex-shrink: 0;
|
}
|
|
.results-title {
|
padding: 15px 20px 10px;
|
font-size: 14px;
|
font-weight: 600;
|
color: #2c3e50;
|
background: #f8fbff;
|
border-bottom: 1px solid #e8f4fd;
|
}
|
|
.result-list {
|
padding: 0;
|
}
|
|
.result-item {
|
padding: 15px 20px;
|
border-bottom: 1px solid #f0f9ff;
|
cursor: pointer;
|
transition: all 0.3s ease;
|
}
|
|
.result-item:hover {
|
background: linear-gradient(135deg, #f0f9ff 0%, #e8f4fd 100%);
|
transform: translateX(4px);
|
}
|
|
.result-item:last-child {
|
border-bottom: none;
|
}
|
|
.result-address {
|
font-size: 15px;
|
font-weight: 600;
|
color: #2c3e50;
|
margin-bottom: 4px;
|
}
|
|
.result-detail {
|
font-size: 13px;
|
color: #5a6c7d;
|
opacity: 0.8;
|
}
|
|
.map-container {
|
position: relative;
|
height: 40vh;
|
min-height: 300px;
|
max-height: 500px;
|
background: #f0f9ff;
|
flex-shrink: 0;
|
}
|
|
.map {
|
width: 100%;
|
height: 100%;
|
}
|
|
.map-controls {
|
position: absolute;
|
top: 15px;
|
right: 15px;
|
display: flex;
|
gap: 12px;
|
z-index: 1000;
|
}
|
|
.map-controls .el-button {
|
border-radius: 20px;
|
padding: 8px 16px;
|
font-weight: 500;
|
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
|
backdrop-filter: blur(10px);
|
}
|
|
/* 选中地址信息 */
|
.selected-address-info {
|
padding: 15px 20px;
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
color: white;
|
flex-shrink: 0;
|
max-height: 20vh;
|
overflow-y: auto;
|
}
|
|
.info-title {
|
font-size: 14px;
|
font-weight: 600;
|
margin-bottom: 10px;
|
opacity: 0.9;
|
}
|
|
.address-info {
|
background: rgba(255,255,255,0.1);
|
border-radius: 8px;
|
padding: 12px;
|
backdrop-filter: blur(10px);
|
}
|
|
.address-text {
|
font-size: 15px;
|
font-weight: 600;
|
margin-bottom: 6px;
|
line-height: 1.4;
|
}
|
|
.coordinates {
|
font-size: 12px;
|
opacity: 0.8;
|
font-family: 'Courier New', monospace;
|
}
|
|
/* 响应式设计 */
|
@media (max-width: 768px) {
|
.form-container {
|
padding: 15px;
|
}
|
|
.form-section {
|
padding: 16px;
|
margin-bottom: 16px;
|
border-radius: 12px;
|
}
|
|
.section-title {
|
font-size: 16px;
|
}
|
|
.header {
|
height: 56px;
|
padding: 0 16px;
|
}
|
|
.header-title {
|
font-size: 18px;
|
}
|
|
.submit-container {
|
padding: 16px;
|
}
|
|
/* 弹窗响应式 */
|
.vehicle-dialog .el-dialog,
|
.map-dialog .el-dialog {
|
width: 95% !important;
|
margin: 0 auto;
|
}
|
|
.vehicle-list {
|
max-height: 50vh;
|
}
|
|
.map-container {
|
height: 35vh;
|
min-height: 250px;
|
}
|
|
.search-results {
|
max-height: 25vh;
|
}
|
}
|
|
@media (max-width: 480px) {
|
.form-container {
|
padding: 12px;
|
}
|
|
.form-section {
|
padding: 14px;
|
margin-bottom: 14px;
|
}
|
|
.vehicle-selector,
|
.address-selector {
|
padding: 14px;
|
}
|
}
|
|
/* Element UI 样式覆盖 */
|
.el-form-item {
|
margin-bottom: 20px;
|
}
|
|
.el-form-item__label {
|
font-weight: 600;
|
color: #2c3e50;
|
font-size: 15px;
|
}
|
|
.el-textarea__inner {
|
border-radius: 8px;
|
border: 2px solid #e8f4fd;
|
transition: all 0.3s ease;
|
}
|
|
.el-textarea__inner:focus {
|
border-color: #667eea;
|
box-shadow: 0 0 0 2px rgba(102, 126, 234, 0.1);
|
}
|
|
.el-select {
|
width: 100%;
|
}
|
|
.el-select .el-input__inner {
|
border-radius: 8px;
|
border: 2px solid #e8f4fd;
|
transition: all 0.3s ease;
|
}
|
|
.el-select .el-input__inner:focus {
|
border-color: #667eea;
|
box-shadow: 0 0 0 2px rgba(102, 126, 234, 0.1);
|
}
|
|
.el-date-editor {
|
width: 100%;
|
}
|
|
.el-date-editor .el-input__inner {
|
border-radius: 8px;
|
border: 2px solid #e8f4fd;
|
transition: all 0.3s ease;
|
}
|
|
.el-date-editor .el-input__inner:focus {
|
border-color: #667eea;
|
box-shadow: 0 0 0 2px rgba(102, 126, 234, 0.1);
|
}
|
|
.el-button--primary {
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
border: none;
|
border-radius: 25px;
|
padding: 14px 30px;
|
font-size: 16px;
|
font-weight: 600;
|
letter-spacing: 0.5px;
|
box-shadow: 0 8px 25px rgba(102, 126, 234, 0.3);
|
transition: all 0.3s ease;
|
}
|
|
.el-button--primary:hover {
|
transform: translateY(-2px);
|
box-shadow: 0 12px 35px rgba(102, 126, 234, 0.4);
|
}
|
|
.el-button--primary:active {
|
transform: translateY(0);
|
}
|
|
/* 弹窗样式优化 */
|
.el-dialog__header {
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
color: white;
|
padding: 20px;
|
border-radius: 16px 16px 0 0;
|
}
|
|
.el-dialog__title {
|
font-weight: 600;
|
font-size: 18px;
|
}
|
|
.el-dialog__headerbtn .el-dialog__close {
|
color: white;
|
font-size: 20px;
|
}
|
|
.el-dialog__footer {
|
padding: 20px;
|
background: #f8fbff;
|
border-radius: 0 0 16px 16px;
|
}
|
|
/* 滚动条样式 */
|
.vehicle-list::-webkit-scrollbar {
|
width: 6px;
|
}
|
|
.vehicle-list::-webkit-scrollbar-track {
|
background: #f1f1f1;
|
border-radius: 3px;
|
}
|
|
.vehicle-list::-webkit-scrollbar-thumb {
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
border-radius: 3px;
|
}
|
|
.vehicle-list::-webkit-scrollbar-thumb:hover {
|
background: linear-gradient(135deg, #5a6fd8 0%, #6a4190 100%);
|
}
|
</style>
|