wlzboy
2025-11-22 fd047fa7234dc11643dab8ecbf38e8d7a8ba0854
app/pages/task/create-normal.vue
@@ -39,17 +39,49 @@
      
      <view class="form-item">
        <view class="form-label">任务出发地</view>
        <view class="form-input picker-input" @click="selectStartLocation">
          {{ taskForm.startLocation || '请选择任务出发地' }}
          <uni-icons type="arrowright" size="16" color="#999"></uni-icons>
        <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="form-input picker-input" @click="selectEndLocation">
          {{ taskForm.endLocation || '请选择任务目的地' }}
          <uni-icons type="arrowright" size="16" color="#999"></uni-icons>
        <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>
      
@@ -109,40 +141,22 @@
      </view>
    </view>
    
    <!-- 地图选择器弹窗 -->
    <uni-popup ref="mapPopup" type="bottom" :mask-click="false">
      <view class="map-popup-container">
        <view class="popup-header">
          <view class="popup-title">选择地址</view>
          <view class="close-btn" @click="closeMapSelector">
            <uni-icons type="closeempty" size="20" color="#999"></uni-icons>
          </view>
        </view>
        <map-selector
          :initial-address="getInitialAddress()"
          @addressSelected="onAddressSelected"
        ></map-selector>
      </view>
    </uni-popup>
  </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 uniPopup from '@/uni_modules/uni-popup/components/uni-popup/uni-popup.vue'
import { getUserProfile } from "@/api/system/user"
import { addTask } from "@/api/task"
import { listAvailableVehicles } from "@/api/vehicle"
import { calculateDistance } from "@/api/map"
import { baiduPlaceSuggestion, baiduGeocoding, baiduDistanceByAddress } from "@/api/map"
import { getDicts } from "@/api/dict"
import MapSelector from '@/components/map-selector.vue'
export default {
  components: {
    uniDatetimePicker,
    uniPopup,
    MapSelector
    uniDatetimePicker
  },
  data() {
    return {
@@ -157,7 +171,13 @@
      boundVehicleId: null,
      taskTypeOptions: [],
      taskTypeLabels: [],
      mapSelectorType: '',
      // 地址搜索相关
      startSuggestions: [],
      endSuggestions: [],
      showStartSuggestions: false,
      showEndSuggestions: false,
      startSearchTimer: null,
      endSearchTimer: null,
      taskForm: {
        taskDescription: '',
        taskType: '',
@@ -173,14 +193,17 @@
      vehicleOptions: [],
      loading: false,
      addressCoordinates: {
        startLocation: null,
        endLocation: null
      }
        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
@@ -300,77 +323,202 @@
      this.taskForm.vehicleId = this.selectedVehicleId
    },
    
    selectStartLocation() {
      this.mapSelectorType = 'startLocation'
      this.$refs.mapPopup.open()
    },
    selectEndLocation() {
      this.mapSelectorType = 'endLocation'
      this.$refs.mapPopup.open()
    },
    getInitialAddress() {
      return this.mapSelectorType === 'startLocation' ? this.taskForm.startLocation : this.taskForm.endLocation
    },
    onAddressSelected(address) {
      if (this.mapSelectorType === 'startLocation') {
        this.taskForm.startLocation = address.title + ' - ' + address.address
        this.addressCoordinates.startLocation = {
          lat: address.lat,
          lng: address.lng
        }
      } else if (this.mapSelectorType === 'endLocation') {
        this.taskForm.endLocation = address.title + ' - ' + address.address
        this.addressCoordinates.endLocation = {
          lat: address.lat,
          lng: address.lng
        }
    // 出发地输入监听
    onStartLocationInput(e) {
      const keyword = e.detail.value
      this.taskForm.startLocation = keyword
      if (this.startSearchTimer) {
        clearTimeout(this.startSearchTimer)
      }
      
      this.calculateDistance()
      this.closeMapSelector()
    },
    calculateDistance() {
      if (this.addressCoordinates.startLocation && this.addressCoordinates.endLocation) {
        this.getDistanceBetweenPoints(
          this.addressCoordinates.startLocation.lat,
          this.addressCoordinates.startLocation.lng,
          this.addressCoordinates.endLocation.lat,
          this.addressCoordinates.endLocation.lng
        ).then(distance => {
          this.taskForm.distance = distance.toFixed(2)
        }).catch(error => {
          console.error('距离计算失败:', error)
        })
      if (!keyword || keyword.trim() === '') {
        this.startSuggestions = []
        this.showStartSuggestions = false
        return
      }
      // 防抖处理
      this.startSearchTimer = setTimeout(() => {
        this.searchStartAddress(keyword)
      }, 300)
    },
    
    getDistanceBetweenPoints(lat1, lng1, lat2, lng2) {
      return new Promise((resolve, reject) => {
        calculateDistance(lat1, lng1, lat2, lng2).then(response => {
          if (response.code === 200) {
            const responseData = typeof response.data === 'string' ? JSON.parse(response.data) : response.data
            if (responseData && responseData.status === 0 && responseData.result && responseData.result.elements && responseData.result.elements.length > 0) {
              const distanceInKm = responseData.result.elements[0].distance / 1000
              resolve(distanceInKm)
            } else {
              reject(new Error('距离计算接口返回数据格式不正确'))
            }
          } else {
            reject(new Error('距离计算接口调用失败'))
          }
        }).catch(error => {
          reject(error)
        })
    // 搜索出发地地址
    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
      })
    },
    
    closeMapSelector() {
      this.$refs.mapPopup.close()
      this.mapSelectorType = ''
    // 出发地输入框获得焦点
    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() {
@@ -413,10 +561,17 @@
    },
    
    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,
@@ -425,13 +580,16 @@
        remark: this.taskForm.remark
      }
      
      if (this.addressCoordinates.startLocation) {
        submitData.departureLongitude = this.addressCoordinates.startLocation.lng
      // 调试:打印提交数据
      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) {
        submitData.destinationLongitude = this.addressCoordinates.endLocation.lng
      if (this.addressCoordinates.endLocation && this.addressCoordinates.endLocation.lon) {
        submitData.destinationLongitude = this.addressCoordinates.endLocation.lon
        submitData.destinationLatitude = this.addressCoordinates.endLocation.lat
      }
      
@@ -450,9 +608,18 @@
        addTask(submitData).then(response => {
          this.loading = false
          this.$modal.showToast('任务创建成功')
          // 延迟跳转,让用户看到成功提示
          setTimeout(() => {
            this.$tab.navigateTo('/pages/task/index')
          }, 1500)
            // 跳转到任务列表并触发刷新
            uni.switchTab({
              url: '/pages/task/index',
              success: () => {
                // 使用事件总线通知任务列表页面刷新
                uni.$emit('refreshTaskList')
              }
            })
          }, 1000)
        }).catch(error => {
          this.loading = false
          console.error('任务创建失败:', error)
@@ -540,6 +707,60 @@
        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 {
@@ -562,34 +783,6 @@
    }
  }
  
  .map-popup-container {
    height: 80vh;
    background-color: white;
    border-top-left-radius: 20rpx;
    border-top-right-radius: 20rpx;
    overflow: hidden;
    .popup-header {
      display: flex;
      justify-content: space-between;
      align-items: center;
      padding: 20rpx 30rpx;
      border-bottom: 1rpx solid #f0f0f0;
      .popup-title {
        font-size: 32rpx;
        font-weight: bold;
        color: #333;
      }
      .close-btn {
        width: 50rpx;
        height: 50rpx;
        display: flex;
        align-items: center;
        justify-content: center;
      }
    }
  }
}
</style>