<template>
|
<scroll-view class="create-normal-task-container" scroll-y="true">
|
<view class="form-header">
|
<view class="back-btn" @click="goBack">
|
<uni-icons type="arrowleft" size="20"></uni-icons>
|
</view>
|
<view class="title">创建{{ categoryName }}任务</view>
|
</view>
|
|
<view class="form-section">
|
<view class="form-item">
|
<view class="form-label">任务车辆</view>
|
<picker mode="selector" :range="vehicles" @change="onVehicleChange">
|
<view class="form-input picker-input">
|
{{ selectedVehicle || '请选择任务车辆' }}
|
<uni-icons type="arrowright" size="16" color="#999"></uni-icons>
|
</view>
|
</picker>
|
</view>
|
|
<view class="form-item">
|
<view class="form-label">任务类型</view>
|
<picker mode="selector" :range="taskTypeLabels" :value="getTaskTypeIndex()" @change="onTaskTypeChange">
|
<view class="form-input picker-input">
|
{{ selectedTaskTypeLabel || '请选择任务类型' }}
|
<uni-icons type="arrowright" size="16" color="#999"></uni-icons>
|
</view>
|
</picker>
|
</view>
|
|
<view class="form-item">
|
<view class="form-label">任务描述</view>
|
<textarea
|
class="form-textarea"
|
placeholder="请输入任务描述"
|
v-model="taskForm.taskDescription"
|
/>
|
</view>
|
|
<view class="form-item">
|
<view class="form-label">任务出发地</view>
|
<view class="address-input-container">
|
<input
|
class="form-input"
|
placeholder="请输入任务出发地"
|
v-model="taskForm.startLocation"
|
@input="onStartLocationInput"
|
@focus="onStartLocationFocus"
|
/>
|
<view class="address-suggestions" v-if="showStartSuggestions && startSuggestions.length > 0">
|
<view
|
class="address-suggestion-item"
|
v-for="(item, index) in startSuggestions"
|
:key="index"
|
@click="selectStartSuggestion(item)"
|
>
|
<view class="suggestion-name">{{ item.name }}</view>
|
<view class="suggestion-address">{{ item.address }}</view>
|
</view>
|
</view>
|
</view>
|
</view>
|
|
<view class="form-item">
|
<view class="form-label">任务目的地</view>
|
<view class="address-input-container">
|
<input
|
class="form-input"
|
placeholder="请输入任务目的地"
|
v-model="taskForm.endLocation"
|
@input="onEndLocationInput"
|
@focus="onEndLocationFocus"
|
/>
|
<view class="address-suggestions" v-if="showEndSuggestions && endSuggestions.length > 0">
|
<view
|
class="address-suggestion-item"
|
v-for="(item, index) in endSuggestions"
|
:key="index"
|
@click="selectEndSuggestion(item)"
|
>
|
<view class="suggestion-name">{{ item.name }}</view>
|
<view class="suggestion-address">{{ item.address }}</view>
|
</view>
|
</view>
|
</view>
|
</view>
|
|
<view class="form-item">
|
<view class="form-label">行驶公里数</view>
|
<input
|
class="form-input"
|
type="digit"
|
placeholder="请输入行驶公里数"
|
v-model="taskForm.distance"
|
/>
|
</view>
|
|
<view class="form-item">
|
<view class="form-label">计划开始时间</view>
|
<uni-datetime-picker
|
v-model="taskForm.plannedStartTime"
|
type="datetime"
|
:placeholder="'请选择计划开始时间'"
|
class="form-input"
|
/>
|
</view>
|
|
<view class="form-item">
|
<view class="form-label">计划结束时间</view>
|
<uni-datetime-picker
|
v-model="taskForm.plannedEndTime"
|
type="datetime"
|
:placeholder="'请选择计划结束时间'"
|
class="form-input"
|
/>
|
</view>
|
|
<view class="form-item">
|
<view class="form-label">执行人</view>
|
<input
|
class="form-input"
|
:value="currentUser.name"
|
disabled
|
/>
|
</view>
|
|
<view class="form-item">
|
<view class="form-label">备注</view>
|
<textarea
|
class="form-textarea"
|
placeholder="请输入备注信息(最多200字)"
|
v-model="taskForm.remark"
|
maxlength="200"
|
/>
|
</view>
|
|
<view class="form-actions">
|
<button class="submit-btn" @click="submitTask" :disabled="loading">
|
{{ loading ? '保存中...' : '保存' }}
|
</button>
|
</view>
|
</view>
|
|
|
</scroll-view>
|
</template>
|
|
<script>
|
import { mapState } from 'vuex'
|
import uniDatetimePicker from '@/uni_modules/uni-datetime-picker/components/uni-datetime-picker/uni-datetime-picker.vue'
|
import { getUserProfile } from "@/api/system/user"
|
import { addTask } from "@/api/task"
|
import { listAvailableVehicles } from "@/api/vehicle"
|
import { baiduPlaceSuggestion, baiduGeocoding, baiduDistanceByAddress } from "@/api/map"
|
import { getDicts } from "@/api/dict"
|
|
export default {
|
components: {
|
uniDatetimePicker
|
},
|
data() {
|
return {
|
categoryName: '',
|
categoryType: '',
|
defaultTaskType: '',
|
selectedVehicle: '',
|
selectedVehicleId: null,
|
selectedTaskType: '',
|
selectedTaskTypeLabel: '',
|
boundVehicle: '',
|
boundVehicleId: null,
|
taskTypeOptions: [],
|
taskTypeLabels: [],
|
// 地址搜索相关
|
startSuggestions: [],
|
endSuggestions: [],
|
showStartSuggestions: false,
|
showEndSuggestions: false,
|
startSearchTimer: null,
|
endSearchTimer: null,
|
taskForm: {
|
taskDescription: '',
|
taskType: '',
|
vehicleId: null,
|
plannedStartTime: '',
|
plannedEndTime: '',
|
startLocation: '',
|
endLocation: '',
|
distance: '',
|
remark: ''
|
},
|
vehicles: [],
|
vehicleOptions: [],
|
loading: false,
|
addressCoordinates: {
|
startLocation: { lon: null, lat: null },
|
endLocation: { lon: null, lat: null }
|
},
|
// 搜索区域(可根据用户所在城市调整)
|
searchRegion: '广州市'
|
}
|
},
|
computed: {
|
...mapState({
|
currentUser: state => ({
|
id: state.user.userId,
|
name: state.user.nickName || '张三',
|
position: '司机',
|
deptId: state.user.deptId || 100
|
})
|
})
|
},
|
onLoad(options) {
|
// 获取任务类型信息
|
if (options.categoryName) {
|
this.categoryName = options.categoryName
|
}
|
if (options.categoryType) {
|
this.categoryType = options.categoryType
|
}
|
if (options.taskType) {
|
this.defaultTaskType = options.taskType
|
this.selectedTaskType = options.taskType
|
}
|
|
// 加载数据
|
this.loadTaskTypes().then(() => {
|
this.getAvailableVehicles().then(() => {
|
this.getUserBoundVehicle()
|
})
|
})
|
},
|
methods: {
|
// 加载任务类型字典
|
loadTaskTypes() {
|
return getDicts('sys_task_type').then(response => {
|
const dictData = response.data || []
|
this.taskTypeOptions = dictData.map(item => ({
|
label: item.dictLabel,
|
value: item.dictValue
|
}))
|
this.taskTypeLabels = this.taskTypeOptions.map(item => item.label)
|
|
if (this.selectedTaskType) {
|
const taskTypeOption = this.taskTypeOptions.find(item => item.value === this.selectedTaskType)
|
if (taskTypeOption) {
|
this.selectedTaskTypeLabel = taskTypeOption.label
|
this.taskForm.taskType = taskTypeOption.value
|
}
|
}
|
}).catch(error => {
|
console.error('加载任务类型字典失败:', error)
|
this.taskTypeOptions = [
|
{ label: '维修保养', value: 'MAINTENANCE' },
|
{ label: '加油任务', value: 'FUEL' },
|
{ label: '其他', value: 'OTHER' }
|
]
|
this.taskTypeLabels = this.taskTypeOptions.map(item => item.label)
|
})
|
},
|
|
// 获取任务类型在列表中的索引
|
getTaskTypeIndex() {
|
if (!this.selectedTaskType) return -1
|
return this.taskTypeOptions.findIndex(item => item.value === this.selectedTaskType)
|
},
|
|
// 任务类型选择变化
|
onTaskTypeChange(e) {
|
const index = e.detail.value
|
const selectedOption = this.taskTypeOptions[index]
|
if (selectedOption) {
|
this.selectedTaskType = selectedOption.value
|
this.selectedTaskTypeLabel = selectedOption.label
|
this.taskForm.taskType = selectedOption.value
|
}
|
},
|
|
// 获取用户绑定的车辆信息
|
getUserBoundVehicle() {
|
getUserProfile().then(response => {
|
const userInfo = response.data || response
|
if (userInfo.boundVehicle) {
|
const boundVehicleNo = userInfo.boundVehicle.vehicleNumber
|
const boundVehicleId = userInfo.boundVehicle.vehicleId
|
|
const vehicleIndex = this.vehicleOptions.findIndex(v => v.id === boundVehicleId || v.name === boundVehicleNo)
|
|
if (vehicleIndex !== -1) {
|
this.boundVehicle = this.vehicleOptions[vehicleIndex].name
|
this.boundVehicleId = this.vehicleOptions[vehicleIndex].id
|
this.selectedVehicle = this.boundVehicle
|
this.selectedVehicleId = this.boundVehicleId
|
this.taskForm.vehicleId = this.boundVehicleId
|
}
|
}
|
}).catch(error => {
|
console.error('获取用户绑定车辆信息失败:', error)
|
})
|
},
|
|
// 获取可用车辆列表
|
getAvailableVehicles() {
|
const deptId = this.currentUser.deptId
|
return listAvailableVehicles(deptId, 'GENERAL').then(response => {
|
const vehicleList = response.data || response.rows || []
|
this.vehicleOptions = vehicleList.map(vehicle => ({
|
id: vehicle.vehicleId,
|
name: vehicle.vehicleNo,
|
type: vehicle.vehicleType,
|
status: vehicle.status
|
}))
|
this.vehicles = this.vehicleOptions.map(v => v.name)
|
}).catch(() => {
|
this.vehicles = []
|
})
|
},
|
|
onVehicleChange(e) {
|
const index = e.detail.value
|
this.selectedVehicle = this.vehicles[index]
|
this.selectedVehicleId = this.vehicleOptions[index]?.id
|
this.taskForm.vehicleId = this.selectedVehicleId
|
},
|
|
// 出发地输入监听
|
onStartLocationInput(e) {
|
const keyword = e.detail.value
|
this.taskForm.startLocation = keyword
|
|
if (this.startSearchTimer) {
|
clearTimeout(this.startSearchTimer)
|
}
|
|
if (!keyword || keyword.trim() === '') {
|
this.startSuggestions = []
|
this.showStartSuggestions = false
|
return
|
}
|
|
// 防抖处理
|
this.startSearchTimer = setTimeout(() => {
|
this.searchStartAddress(keyword)
|
}, 300)
|
},
|
|
// 搜索出发地地址
|
searchStartAddress(keyword) {
|
baiduPlaceSuggestion(keyword, this.searchRegion).then(response => {
|
if (response.code === 200 && response.data) {
|
this.startSuggestions = response.data
|
this.showStartSuggestions = true
|
} else {
|
this.startSuggestions = []
|
this.showStartSuggestions = false
|
}
|
}).catch(error => {
|
console.error('搜索出发地地址失败:', error)
|
this.startSuggestions = []
|
this.showStartSuggestions = false
|
})
|
},
|
|
// 出发地输入框获得焦点
|
onStartLocationFocus() {
|
if (this.taskForm.startLocation && this.startSuggestions.length > 0) {
|
this.showStartSuggestions = true
|
}
|
},
|
|
// 选择出发地地址建议
|
selectStartSuggestion(item) {
|
this.taskForm.startLocation = item.name
|
this.showStartSuggestions = false
|
this.startSuggestions = []
|
|
// 获取地址坐标
|
if (item.location && item.location.lng && item.location.lat) {
|
this.addressCoordinates.startLocation = {
|
lon: item.location.lng,
|
lat: item.location.lat
|
}
|
} else {
|
// 如果没有坐标,需要通过地址获取坐标
|
this.getCoordinatesByAddress(item.name, 'start')
|
}
|
|
// 如果两个地址都已选择,自动计算距离
|
if (this.taskForm.endLocation && this.addressCoordinates.endLocation.lon) {
|
this.calculateDistance()
|
}
|
},
|
|
// 目的地输入监听
|
onEndLocationInput(e) {
|
const keyword = e.detail.value
|
this.taskForm.endLocation = keyword
|
|
if (this.endSearchTimer) {
|
clearTimeout(this.endSearchTimer)
|
}
|
|
if (!keyword || keyword.trim() === '') {
|
this.endSuggestions = []
|
this.showEndSuggestions = false
|
return
|
}
|
|
// 防抖处理
|
this.endSearchTimer = setTimeout(() => {
|
this.searchEndAddress(keyword)
|
}, 300)
|
},
|
|
// 搜索目的地地址
|
searchEndAddress(keyword) {
|
baiduPlaceSuggestion(keyword, this.searchRegion).then(response => {
|
if (response.code === 200 && response.data) {
|
this.endSuggestions = response.data
|
this.showEndSuggestions = true
|
} else {
|
this.endSuggestions = []
|
this.showEndSuggestions = false
|
}
|
}).catch(error => {
|
console.error('搜索目的地地址失败:', error)
|
this.endSuggestions = []
|
this.showEndSuggestions = false
|
})
|
},
|
|
// 目的地输入框获得焦点
|
onEndLocationFocus() {
|
if (this.taskForm.endLocation && this.endSuggestions.length > 0) {
|
this.showEndSuggestions = true
|
}
|
},
|
|
// 选择目的地地址建议
|
selectEndSuggestion(item) {
|
this.taskForm.endLocation = item.name
|
this.showEndSuggestions = false
|
this.endSuggestions = []
|
|
// 获取地址坐标
|
if (item.location && item.location.lng && item.location.lat) {
|
this.addressCoordinates.endLocation = {
|
lon: item.location.lng,
|
lat: item.location.lat
|
}
|
} else {
|
// 如果没有坐标,需要通过地址获取坐标
|
this.getCoordinatesByAddress(item.name, 'end')
|
}
|
|
// 如果两个地址都已选择,自动计算距离
|
if (this.taskForm.startLocation && this.addressCoordinates.startLocation.lon) {
|
this.calculateDistance()
|
}
|
},
|
|
// 通过地址获取坐标
|
getCoordinatesByAddress(address, type) {
|
baiduGeocoding(address, this.searchRegion).then(response => {
|
if (response.code === 200 && response.data && response.data.location) {
|
if (type === 'start') {
|
this.addressCoordinates.startLocation = {
|
lon: response.data.location.lng,
|
lat: response.data.location.lat
|
}
|
} else if (type === 'end') {
|
this.addressCoordinates.endLocation = {
|
lon: response.data.location.lng,
|
lat: response.data.location.lat
|
}
|
}
|
|
// 如果两个地址都已有坐标,自动计算距离
|
if (this.addressCoordinates.startLocation.lon && this.addressCoordinates.endLocation.lon) {
|
this.calculateDistance()
|
}
|
}
|
}).catch(error => {
|
console.error('获取地址坐标失败:', error)
|
})
|
},
|
|
// 计算两地之间的距离
|
calculateDistance() {
|
if (!this.taskForm.startLocation || !this.taskForm.endLocation) {
|
return
|
}
|
|
// 使用百度地图计算距离(组合接口)
|
baiduDistanceByAddress(this.taskForm.startLocation, this.searchRegion, this.taskForm.endLocation, this.searchRegion).then(response => {
|
if (response.code === 200 && response.data) {
|
// 百度地图返回的距离单位是米,需要转换为公里
|
const distanceInMeters = response.data.distance
|
const distanceInKm = distanceInMeters / 1000
|
this.taskForm.distance = distanceInKm.toFixed(2)
|
|
console.log('距离计算成功:', distanceInMeters, '米 =', distanceInKm, '公里')
|
|
// 同时更新坐标信息
|
if (response.data.fromLocation) {
|
this.addressCoordinates.startLocation = {
|
lon: response.data.fromLocation.lng,
|
lat: response.data.fromLocation.lat
|
}
|
}
|
if (response.data.toLocation) {
|
this.addressCoordinates.endLocation = {
|
lon: response.data.toLocation.lng,
|
lat: response.data.toLocation.lat
|
}
|
}
|
}
|
}).catch(error => {
|
console.error('计算距离失败:', error)
|
this.$modal.showToast('计算距离失败,请手动输入')
|
})
|
},
|
|
validateForm() {
|
if (!this.taskForm.vehicleId) {
|
this.$modal.showToast('请选择任务车辆')
|
return false
|
}
|
|
if (!this.taskForm.taskType) {
|
this.$modal.showToast('请选择任务类型')
|
return false
|
}
|
|
if (!this.taskForm.taskDescription) {
|
this.$modal.showToast('请输入任务描述')
|
return false
|
}
|
|
if (!this.taskForm.startLocation) {
|
this.$modal.showToast('请选择任务出发地')
|
return false
|
}
|
|
if (!this.taskForm.endLocation) {
|
this.$modal.showToast('请选择任务目的地')
|
return false
|
}
|
|
if (!this.taskForm.plannedStartTime) {
|
this.$modal.showToast('请选择开始时间')
|
return false
|
}
|
|
if (!this.taskForm.plannedEndTime) {
|
this.$modal.showToast('请选择结束时间')
|
return false
|
}
|
|
return true
|
},
|
|
buildSubmitData() {
|
// 调试:打印当前用户信息
|
console.log('当前用户信息:', this.currentUser)
|
console.log('用户ID:', this.currentUser.id)
|
console.log('Vuex State:', this.$store.state.user)
|
|
const submitData = {
|
taskDescription: this.taskForm.taskDescription,
|
taskType: this.taskForm.taskType,
|
vehicleIds: this.taskForm.vehicleId ? [this.taskForm.vehicleId] : [],
|
assigneeId: this.currentUser.id || this.$store.state.user.userId, // 主要执行人
|
assigneeIds: (this.currentUser.id || this.$store.state.user.userId) ? [this.currentUser.id || this.$store.state.user.userId] : [], // 执行人员ID列表
|
plannedStartTime: this.taskForm.plannedStartTime,
|
plannedEndTime: this.taskForm.plannedEndTime,
|
departureAddress: this.taskForm.startLocation,
|
destinationAddress: this.taskForm.endLocation,
|
estimatedDistance: this.taskForm.distance ? parseFloat(this.taskForm.distance) : null,
|
remark: this.taskForm.remark
|
}
|
|
// 调试:打印提交数据
|
console.log('提交数据:', submitData)
|
|
if (this.addressCoordinates.startLocation && this.addressCoordinates.startLocation.lon) {
|
submitData.departureLongitude = this.addressCoordinates.startLocation.lon
|
submitData.departureLatitude = this.addressCoordinates.startLocation.lat
|
}
|
|
if (this.addressCoordinates.endLocation && this.addressCoordinates.endLocation.lon) {
|
submitData.destinationLongitude = this.addressCoordinates.endLocation.lon
|
submitData.destinationLatitude = this.addressCoordinates.endLocation.lat
|
}
|
|
return submitData
|
},
|
|
submitTask() {
|
if (!this.validateForm()) {
|
return
|
}
|
|
this.$modal.confirm('确定要保存任务吗?').then(() => {
|
this.loading = true
|
const submitData = this.buildSubmitData()
|
|
addTask(submitData).then(response => {
|
this.loading = false
|
this.$modal.showToast('任务创建成功')
|
|
// 延迟跳转,让用户看到成功提示
|
setTimeout(() => {
|
// 跳转到任务列表并触发刷新
|
uni.switchTab({
|
url: '/pages/task/index',
|
success: () => {
|
// 使用事件总线通知任务列表页面刷新
|
uni.$emit('refreshTaskList')
|
}
|
})
|
}, 1000)
|
}).catch(error => {
|
this.loading = false
|
console.error('任务创建失败:', error)
|
this.$modal.showToast('任务创建失败,请重试')
|
})
|
}).catch(() => {})
|
},
|
|
goBack() {
|
uni.navigateBack()
|
}
|
}
|
}
|
</script>
|
|
<style lang="scss" scoped>
|
.create-normal-task-container {
|
padding: 20rpx;
|
background-color: #f5f5f5;
|
min-height: 100vh;
|
|
.form-header {
|
display: flex;
|
align-items: center;
|
padding: 20rpx 0;
|
margin-bottom: 30rpx;
|
|
.back-btn {
|
width: 60rpx;
|
height: 60rpx;
|
border-radius: 50%;
|
background-color: #f0f0f0;
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
margin-right: 20rpx;
|
}
|
|
.title {
|
font-size: 36rpx;
|
font-weight: bold;
|
color: #333;
|
}
|
}
|
|
.form-section {
|
background-color: white;
|
border-radius: 15rpx;
|
padding: 30rpx;
|
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
|
|
.form-item {
|
margin-bottom: 40rpx;
|
|
.form-label {
|
font-size: 28rpx;
|
margin-bottom: 15rpx;
|
color: #333;
|
}
|
|
.form-input {
|
height: 70rpx;
|
padding: 0 20rpx;
|
border: 1rpx solid #eee;
|
border-radius: 10rpx;
|
font-size: 28rpx;
|
|
&.picker-input {
|
display: flex;
|
align-items: center;
|
justify-content: space-between;
|
}
|
|
&[disabled] {
|
background-color: #f5f5f5;
|
color: #999;
|
}
|
}
|
|
.form-textarea {
|
width: 100%;
|
min-height: 150rpx;
|
padding: 20rpx;
|
border: 1rpx solid #eee;
|
border-radius: 10rpx;
|
font-size: 28rpx;
|
}
|
|
// 地址输入容器
|
.address-input-container {
|
position: relative;
|
|
.form-input {
|
width: 100%;
|
height: 70rpx;
|
padding: 0 20rpx;
|
border: 1rpx solid #eee;
|
border-radius: 10rpx;
|
font-size: 28rpx;
|
box-sizing: border-box;
|
}
|
|
.address-suggestions {
|
position: absolute;
|
top: 75rpx;
|
left: 0;
|
right: 0;
|
max-height: 400rpx;
|
overflow-y: auto;
|
background-color: white;
|
border: 1rpx solid #eee;
|
border-radius: 10rpx;
|
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1);
|
z-index: 100;
|
|
.address-suggestion-item {
|
padding: 20rpx;
|
border-bottom: 1rpx solid #f0f0f0;
|
|
&:last-child {
|
border-bottom: none;
|
}
|
|
&:active {
|
background-color: #f5f5f5;
|
}
|
|
.suggestion-name {
|
font-size: 28rpx;
|
color: #333;
|
margin-bottom: 8rpx;
|
font-weight: 500;
|
}
|
|
.suggestion-address {
|
font-size: 24rpx;
|
color: #999;
|
}
|
}
|
}
|
}
|
}
|
|
.form-actions {
|
margin-top: 50rpx;
|
text-align: center;
|
|
.submit-btn {
|
width: 80%;
|
height: 80rpx;
|
background-color: #007AFF;
|
color: white;
|
border-radius: 10rpx;
|
font-size: 32rpx;
|
|
&[disabled] {
|
background-color: #ccc;
|
color: #999;
|
}
|
}
|
}
|
}
|
|
|
}
|
</style>
|