wlzboy
2025-12-03 7c790c248c137a2fa5525bf66ed04c25043cded7
feat:用户附加同步
1个文件已添加
4个文件已修改
998 ■■■■■ 已修改文件
app/pagesTask/components/StaffSelector.vue 661 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/pagesTask/create-emergency.vue 315 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/LegacySystemSyncServiceImpl.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/LegacyTransferSyncServiceImpl.java 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysTaskServiceImpl.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/pagesTask/components/StaffSelector.vue
New file
@@ -0,0 +1,661 @@
<template>
  <view class="staff-selector-component">
    <view class="form-item">
      <view class="form-label" :class="{ required: required }">{{ label }}</view>
      <view class="staff-list">
        <view class="staff-item" v-for="(staff, index) in selectedStaff" :key="staff.userId">
          <view class="staff-info">
            <text class="staff-name">{{ staff.nickName }}</text>
            <view class="staff-roles">
              <text class="staff-role" v-for="(roleType, idx) in staff.types" :key="idx">
                {{ getUserTypeName(roleType) }}
              </text>
            </view>
          </view>
          <uni-icons
            v-if="canRemove(index)"
            type="closeempty"
            size="20"
            color="#ff4d4f"
            @click="removeStaff(index)"
          ></uni-icons>
          <uni-icons
            v-else
            type="checkmarkempty"
            size="20"
            color="#007AFF"
          ></uni-icons>
        </view>
        <view class="add-staff" @click="showStaffSelector">
          <uni-icons type="plusempty" size="20" color="#007AFF"></uni-icons>
          <text>添加人员</text>
        </view>
      </view>
    </view>
    <!-- 人员选择弹窗 -->
    <uni-popup ref="staffPopup" type="bottom" :safe-area="true">
      <view class="staff-selector-popup">
        <view class="popup-header">
          <view class="popup-title">选择执行人员</view>
          <view class="popup-close" @click="closeStaffSelector">
            <uni-icons type="closeempty" size="24" color="#333"></uni-icons>
          </view>
        </view>
        <view class="search-box">
          <uni-icons type="search" size="18" color="#999"></uni-icons>
          <input
            class="search-input"
            placeholder="搜索姓名、手机号"
            v-model="staffSearchKeyword"
            @input="onStaffSearch"
          />
        </view>
        <view class="staff-filter">
          <view
            class="filter-item"
            :class="{ active: staffFilterType === 'all' }"
            @click="filterStaff('all')"
          >全部</view>
          <view
            class="filter-item"
            :class="{ active: staffFilterType === 'driver' }"
            @click="filterStaff('driver')"
          >司机</view>
          <view
            class="filter-item"
            :class="{ active: staffFilterType === 'doctor' }"
            @click="filterStaff('doctor')"
          >医生</view>
          <view
            class="filter-item"
            :class="{ active: staffFilterType === 'nurse' }"
            @click="filterStaff('nurse')"
          >护士</view>
        </view>
        <scroll-view class="staff-list-popup" scroll-y="true">
          <view
            class="staff-item-popup"
            v-for="staff in filteredStaffList"
            :key="staff.userId"
            @click="toggleStaffSelection(staff)"
          >
            <view class="staff-info">
              <view class="staff-name-row">
                <text class="staff-name">{{ staff.nickName }}</text>
                <text class="staff-phone">{{ staff.phonenumber }}</text>
              </view>
              <view class="staff-detail-row">
                <text class="staff-dept">{{ staff.deptName }}</text>
                <view class="staff-types">
                  <text
                    class="type-tag"
                    :class="'type-' + type"
                    v-for="(type, idx) in staff.types"
                    :key="idx"
                  >
                    {{ getUserTypeName(type) }}
                  </text>
                </view>
              </view>
            </view>
            <uni-icons
              v-if="isStaffSelected(staff.userId)"
              type="checkmarkempty"
              size="24"
              color="#007AFF"
            ></uni-icons>
            <view v-else class="checkbox-empty"></view>
          </view>
          <view class="no-data" v-if="filteredStaffList.length === 0">
            <uni-icons type="info" size="40" color="#ccc"></uni-icons>
            <text>暂无人员数据</text>
          </view>
        </scroll-view>
        <view class="popup-footer">
          <button class="cancel-btn" @click="closeStaffSelector">取消</button>
          <button class="confirm-btn" @click="confirmStaffSelection">确定(已选{{ selectedStaff.length }})</button>
        </view>
      </view>
    </uni-popup>
  </view>
</template>
<script>
import { mapState } from 'vuex'
import uniPopup from '@/uni_modules/uni-popup/components/uni-popup/uni-popup.vue'
import { listBranchUsers } from "@/api/system/user"
export default {
  name: 'StaffSelector',
  components: {
    uniPopup
  },
  props: {
    // 已选择的人员列表
    value: {
      type: Array,
      default: () => []
    },
    // 标签文本
    label: {
      type: String,
      default: '执行任务人员'
    },
    // 是否必填
    required: {
      type: Boolean,
      default: false
    },
    // 是否自动添加当前用户
    autoAddCurrentUser: {
      type: Boolean,
      default: true
    },
    // 当前用户是否可移除
    currentUserRemovable: {
      type: Boolean,
      default: false
    }
  },
  data() {
    return {
      selectedStaff: [],
      allStaffList: [],
      filteredStaffList: [],
      staffSearchKeyword: '',
      staffFilterType: 'all'
    }
  },
  computed: {
    ...mapState({
      currentUser: state => ({
        userId: state.user.userId,
        nickName: state.user.nickName || '张三',
        phonenumber: state.user.phonenumber || '',
        deptId: state.user.deptId || 100,
        posts: state.user.posts || [],
        roles: state.user.roles || [],
        dept: state.user.dept || null
      })
    })
  },
  watch: {
    value: {
      handler(newVal) {
        if (newVal && Array.isArray(newVal)) {
          this.selectedStaff = [...newVal]
        }
      },
      immediate: true,
      deep: true
    }
  },
  mounted() {
    this.loadStaffList()
    // 如果需要自动添加当前用户且选中人员为空
    if (this.autoAddCurrentUser && this.selectedStaff.length === 0) {
      this.initWithCurrentUser()
    }
  },
  methods: {
    // 初始化选中的人员(默认包含当前用户)
    initWithCurrentUser() {
      const currentUserStaff = {
        userId: this.currentUser.userId,
        nickName: this.currentUser.nickName,
        phonenumber: this.currentUser.phonenumber,
        deptId: this.currentUser.deptId,
        posts: this.currentUser.posts || [],
        roles: this.currentUser.roles || [],
        dept: this.currentUser.dept || null
      }
      // 获取当前用户的所有角色类型(可能有多个)
      currentUserStaff.types = this.getUserTypes(currentUserStaff)
      currentUserStaff.type = currentUserStaff.types[0] || 'driver' // 主要类型
      this.selectedStaff = [currentUserStaff]
      this.emitChange()
    },
    // 加载人员列表
    loadStaffList() {
      listBranchUsers().then(response => {
        const userList = response.data || []
        this.allStaffList = userList.map(user => ({
          userId: user.userId,
          nickName: user.nickName,
          phonenumber: user.phonenumber,
          deptName: user.dept?.deptName || '',
          postName: user.posts && user.posts.length > 0 ? user.posts[0].postName : '',
          roleName: user.roles && user.roles.length > 0 ? user.roles[0].roleName : '',
          posts: user.posts || [],
          roles: user.roles || [],
          dept: user.dept || null,
          // 支持多种类型
          types: this.getUserTypes(user),
          type: this.getUserTypes(user)[0] || 'driver' // 主要类型(用于向后兼容)
        }))
        this.filterStaffList()
      }).catch(error => {
        console.error('加载人员列表失败:', error)
        this.$modal.showToast('加载人员列表失败')
      })
    },
    // 根据用户的岗位或角色判断所有类型(支持多种身份)
    getUserTypes(user) {
      const types = []
      const postNames = user.posts ? user.posts.map(p => p.postName).join('') : ''
      const roleNames = user.roles ? user.roles.map(r => r.roleName).join('') : ''
      const deptName = user.dept?.deptName || ''
      // 判断是否为司机
      if (postNames.includes('司机') || roleNames.includes('司机') ||
          deptName.includes('车队') || deptName.includes('司机')) {
        types.push('driver')
      }
      // 判断是否为医生
      if (postNames.includes('医生') || roleNames.includes('医生') ||
          deptName.includes('医生') || deptName.includes('医护')) {
        types.push('doctor')
      }
      // 判断是否为护士
      if (postNames.includes('护士') || roleNames.includes('护士') || deptName.includes('护士')) {
        types.push('nurse')
      }
      // 如果没有匹配到任何类型,默认为司机
      if (types.length === 0) {
        types.push('driver')
      }
      return types
    },
    // 获取类型名称
    getUserTypeName(staffType) {
      const typeMap = {
        'driver': '司机',
        'doctor': '医生',
        'nurse': '护士'
      }
      return typeMap[staffType] || staffType || '司机'
    },
    // 显示人员选择弹窗
    showStaffSelector() {
      this.$refs.staffPopup.open()
      this.filterStaffList()
    },
    // 关闭人员选择弹窗
    closeStaffSelector() {
      this.$refs.staffPopup.close()
      this.staffSearchKeyword = ''
      this.staffFilterType = 'all'
    },
    // 人员搜索
    onStaffSearch(e) {
      this.staffSearchKeyword = e.detail.value
      this.filterStaffList()
    },
    // 筛选人员类型
    filterStaff(type) {
      this.staffFilterType = type
      this.filterStaffList()
    },
    // 过滤人员列表
    filterStaffList() {
      let list = [...this.allStaffList]
      // 按类型过滤(支持多类型)
      if (this.staffFilterType !== 'all') {
        list = list.filter(staff => staff.types.includes(this.staffFilterType))
      }
      // 按关键词搜索
      if (this.staffSearchKeyword && this.staffSearchKeyword.trim() !== '') {
        const keyword = this.staffSearchKeyword.trim().toLowerCase()
        list = list.filter(staff => {
          return staff.nickName.toLowerCase().includes(keyword) ||
                 (staff.phonenumber && staff.phonenumber.includes(keyword))
        })
      }
      this.filteredStaffList = list
    },
    // 切换人员选中状态
    toggleStaffSelection(staff) {
      const index = this.selectedStaff.findIndex(s => s.userId === staff.userId)
      if (index > -1) {
        // 如果是第一个且不可移除(当前用户),不允许移除
        if (!this.canRemove(index)) {
          this.$modal.showToast('当前用户不能移除')
          return
        }
        // 已选中,移除
        this.selectedStaff.splice(index, 1)
      } else {
        // 未选中,添加
        this.selectedStaff.push(staff)
      }
    },
    // 判断人员是否已选中
    isStaffSelected(userId) {
      return this.selectedStaff.some(staff => staff.userId === userId)
    },
    // 判断是否可以移除
    canRemove(index) {
      // 如果是第一个且当前用户不可移除
      if (index === 0 && !this.currentUserRemovable) {
        return false
      }
      return true
    },
    // 确认人员选择
    confirmStaffSelection() {
      if (this.selectedStaff.length === 0) {
        this.$modal.showToast('请至少选择一名人员')
        return
      }
      this.emitChange()
      this.closeStaffSelector()
    },
    // 移除人员
    removeStaff(index) {
      if (!this.canRemove(index)) {
        this.$modal.showToast('当前用户不能移除')
        return
      }
      this.selectedStaff.splice(index, 1)
      this.emitChange()
    },
    // 触发change事件
    emitChange() {
      this.$emit('input', this.selectedStaff)
      this.$emit('change', this.selectedStaff)
    }
  }
}
</script>
<style lang="scss" scoped>
.staff-selector-component {
  .form-item {
    margin-bottom: 40rpx;
    .form-label {
      font-size: 28rpx;
      margin-bottom: 15rpx;
      color: #333;
      &.required::before {
        content: '*';
        color: #ff4d4f;
        margin-right: 4rpx;
        font-weight: bold;
      }
    }
    .staff-list {
      .staff-item {
        display: flex;
        justify-content: space-between;
        align-items: center;
        padding: 20rpx;
        background-color: #f9f9f9;
        border-radius: 10rpx;
        margin-bottom: 20rpx;
        .staff-info {
          flex: 1;
          .staff-name {
            font-size: 28rpx;
            color: #333;
            margin-right: 10rpx;
            display: block;
            margin-bottom: 8rpx;
          }
          .staff-roles {
            display: flex;
            flex-wrap: wrap;
            gap: 8rpx;
            .staff-role {
              font-size: 22rpx;
              color: white;
              background-color: #007AFF;
              padding: 4rpx 12rpx;
              border-radius: 6rpx;
            }
          }
        }
      }
      .add-staff {
        display: flex;
        align-items: center;
        justify-content: center;
        padding: 20rpx;
        border: 1rpx dashed #007AFF;
        border-radius: 10rpx;
        color: #007AFF;
        text {
          margin-left: 10rpx;
        }
      }
    }
  }
}
// 人员选择弹窗样式
.staff-selector-popup {
  background-color: white;
  border-radius: 20rpx 20rpx 0 0;
  max-height: 80vh;
  display: flex;
  flex-direction: column;
  .popup-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 30rpx;
    border-bottom: 1rpx solid #f0f0f0;
    flex-shrink: 0;
    .popup-title {
      font-size: 32rpx;
      font-weight: bold;
      color: #333;
    }
    .popup-close {
      padding: 10rpx;
    }
  }
  .search-box {
    display: flex;
    align-items: center;
    margin: 20rpx 30rpx;
    padding: 15rpx 20rpx;
    background-color: #f5f5f5;
    border-radius: 10rpx;
    flex-shrink: 0;
    .search-input {
      flex: 1;
      margin-left: 10rpx;
      font-size: 28rpx;
    }
  }
  .staff-filter {
    display: flex;
    padding: 0 30rpx 20rpx;
    gap: 15rpx;
    flex-shrink: 0;
    .filter-item {
      flex: 1;
      text-align: center;
      padding: 15rpx 0;
      background-color: #f5f5f5;
      border-radius: 10rpx;
      font-size: 26rpx;
      color: #666;
      &.active {
        background-color: #007AFF;
        color: white;
      }
    }
  }
  .staff-list-popup {
    flex: 1;
    overflow-y: auto;
    padding: 0 30rpx;
    .staff-item-popup {
      display: flex;
      justify-content: space-between;
      align-items: center;
      padding: 25rpx 20rpx;
      border-bottom: 1rpx solid #f0f0f0;
      &:active {
        background-color: #f5f5f5;
      }
      .staff-info {
        flex: 1;
        .staff-name-row {
          display: flex;
          align-items: center;
          margin-bottom: 10rpx;
          .staff-name {
            font-size: 30rpx;
            font-weight: bold;
            color: #333;
            margin-right: 20rpx;
          }
          .staff-phone {
            font-size: 24rpx;
            color: #999;
          }
        }
        .staff-detail-row {
          display: flex;
          align-items: center;
          flex-wrap: wrap;
          .staff-dept {
            font-size: 24rpx;
            color: #666;
            margin-right: 15rpx;
          }
          .staff-types {
            display: flex;
            gap: 8rpx;
            .type-tag {
              font-size: 20rpx;
              color: white;
              padding: 4rpx 10rpx;
              border-radius: 6rpx;
              &.type-driver {
                background-color: #007AFF;
              }
              &.type-doctor {
                background-color: #34C759;
              }
              &.type-nurse {
                background-color: #AF52DE;
              }
            }
          }
        }
      }
      .checkbox-empty {
        width: 40rpx;
        height: 40rpx;
        border: 2rpx solid #ddd;
        border-radius: 50%;
      }
    }
    .no-data {
      text-align: center;
      padding: 100rpx 0;
      color: #999;
      text {
        display: block;
        margin-top: 20rpx;
        font-size: 28rpx;
      }
    }
  }
  .popup-footer {
    display: flex;
    padding: 20rpx 30rpx;
    border-top: 1rpx solid #f0f0f0;
    gap: 20rpx;
    flex-shrink: 0;
    button {
      flex: 1;
      height: 80rpx;
      border-radius: 10rpx;
      font-size: 30rpx;
    }
    .cancel-btn {
      background-color: #f5f5f5;
      color: #666;
    }
    .confirm-btn {
      background-color: #007AFF;
      color: white;
    }
  }
}
</style>
app/pagesTask/create-emergency.vue
@@ -59,34 +59,14 @@
          </view>
        </picker>
      </view>
      <view class="form-item">
        <view class="form-label">执行任务人员</view>
        <view class="staff-list">
          <view class="staff-item" v-for="(staff, index) in selectedStaff" :key="staff.userId">
            <view class="staff-info">
              <text class="staff-name">{{ staff.nickName }}</text>
              <text class="staff-role">({{ getUserTypeName(staff.type) || '未知职位' }})</text>
            </view>
            <uni-icons
              v-if="index > 0"
              type="closeempty"
              size="20"
              color="#ff4d4f"
              @click="removeStaff(index)"
            ></uni-icons>
            <uni-icons
              v-else
              type="checkmarkempty"
              size="20"
              color="#007AFF"
            ></uni-icons>
          </view>
          <view class="add-staff" @click="showStaffSelector">
            <uni-icons type="plusempty" size="20" color="#007AFF"></uni-icons>
            <text>添加人员</text>
          </view>
        </view>
      </view>
      <StaffSelector
        v-model="selectedStaff"
        label="执行任务人员"
        :required="false"
        :auto-add-current-user="true"
        :current-user-removable="false"
        @change="onStaffChange"
      />
      
    
      
@@ -213,82 +193,6 @@
      </view>
    </view>
    
    <!-- 人员选择弹窗 -->
    <uni-popup ref="staffPopup" type="bottom" :safe-area="true">
      <view class="staff-selector-popup">
        <view class="popup-header">
          <view class="popup-title">选择执行人员</view>
          <view class="popup-close" @click="closeStaffSelector">
            <uni-icons type="closeempty" size="24" color="#333"></uni-icons>
          </view>
        </view>
        <view class="search-box">
          <uni-icons type="search" size="18" color="#999"></uni-icons>
          <input
            class="search-input"
            placeholder="搜索姓名、手机号"
            v-model="staffSearchKeyword"
            @input="onStaffSearch"
          />
        </view>
        <view class="staff-filter">
          <view
            class="filter-item"
            :class="{ active: staffFilterType === 'driver' }"
            @click="filterStaff('driver')"
          >司机</view>
          <view
            class="filter-item"
            :class="{ active: staffFilterType === 'doctor' }"
            @click="filterStaff('doctor')"
          >医生</view>
          <view
            class="filter-item"
            :class="{ active: staffFilterType === 'nurse' }"
            @click="filterStaff('nurse')"
          >护士</view>
        </view>
        <scroll-view class="staff-list-popup" scroll-y="true">
          <view
            class="staff-item-popup"
            v-for="staff in filteredStaffList"
            :key="staff.userId"
            @click="toggleStaffSelection(staff)"
          >
            <view class="staff-info">
              <view class="staff-name-row">
                <text class="staff-name">{{ staff.nickName }}</text>
                <text class="staff-phone">{{ staff.phonenumber }}</text>
              </view>
              <view class="staff-detail-row">
                <text class="staff-dept">{{ staff.deptName }}</text>
                <text class="staff-post">{{ staff.postName || staff.roleName  || '未知职位' }}</text>
              </view>
            </view>
            <uni-icons
              v-if="isStaffSelected(staff.userId)"
              type="checkmarkempty"
              size="24"
              color="#007AFF"
            ></uni-icons>
            <view v-else class="checkbox-empty"></view>
          </view>
          <view class="no-data" v-if="filteredStaffList.length === 0">
            <uni-icons type="info" size="40" color="#ccc"></uni-icons>
            <text>暂无人员数据</text>
          </view>
        </scroll-view>
        <view class="popup-footer">
          <button class="cancel-btn" @click="closeStaffSelector">取消</button>
          <button class="confirm-btn" @click="confirmStaffSelection">确定(已选{{ selectedStaff.length }})</button>
        </view>
      </view>
    </uni-popup>
    
   <!-- 智能识别弹窗 -->
    <uni-popup ref="smartParsePopup" type="bottom" :safe-area="true">
@@ -345,6 +249,7 @@
import OrganizationSelector from './components/OrganizationSelector.vue'
import HospitalSelector from './components/HospitalSelector.vue'
import DiseaseSelector from './components/DiseaseSelector.vue'
import StaffSelector from './components/StaffSelector.vue'
export default {
  components: {
@@ -354,7 +259,8 @@
    OrganizationSelector,
    HospitalSelector,
    DiseaseSelector,
    DepartureSelector
    DepartureSelector,
    StaffSelector
  },
  data() {
    return {
@@ -373,10 +279,6 @@
      mapSelectorType: '',
      // 人员选择相关
      selectedStaff: [], // 已选择的人员列表
      allStaffList: [], // 所有人员列表
      filteredStaffList: [], // 过滤后的人员列表
      staffSearchKeyword: '', // 人员搜索关键词
      staffFilterType: 'driver', // 人员筛选类型:driver/doctor/nurse,默认选中司机
      // 病情选择相关
      selectedDiseases: [], // 已选择的病情列表
      taskForm: {
@@ -452,8 +354,6 @@
    this.getAvailableVehicles().then(() => {
      this.getUserBoundVehicleInfo()
    })
    this.initSelectedStaff()
    this.loadDeptStaff()
    // 加载科室字典数据
    this.loadDepartments()
    // 加载任务类型数据
@@ -592,18 +492,7 @@
      this.selectedEmergencyTaskType = selected.text
      this.selectedEmergencyTaskTypeId = selected.id
    },
    getUserTypeName(staffType){
      switch(staffType){
        case "nurse":
          return "护士";
        case "doctor":
          return "医生";
        case "driver":
          return "司机";
        default:
          return "司机";
      }
    },
    
    // 加载单据类型数据
    loadDocumentTypes() {
@@ -684,182 +573,10 @@
      }
    },
    
    // 初始化选中的人员(默认包含当前用户)
    initSelectedStaff() {
      // 构建当前用户对象,包含完整的角色信息
      const currentUserStaff = {
        userId: this.currentUser.userId,
        nickName: this.currentUser.nickName,
        phonenumber: this.currentUser.phonenumber,
        postName: this.currentUser.position,
        deptId: this.currentUser.deptId,
        posts: this.currentUser.posts || [],
        roles: this.currentUser.roles || [],
        dept: this.currentUser.dept || null
      }
      // 为当前用户设置角色类型
      currentUserStaff.type = this.getUserType(currentUserStaff)
      this.selectedStaff = [currentUserStaff]
    },
    // 加载当前用户所在分公司的所有人员
    loadDeptStaff() {
      console.log('开始加载人员列表')
      // 调用新接口,自动根据当前用户的oaOrderClass获取分公司下的用户
      listBranchUsers().then(response => {
        console.log('人员列表API响应:', response)
        const userList = response.data || []
        console.log('解析出的用户列表:', userList, '数量:', userList.length)
        this.allStaffList = userList.map(user => ({
          userId: user.userId,
          nickName: user.nickName,
          phonenumber: user.phonenumber,
          deptName: user.dept?.deptName || '',
          postName: user.posts && user.posts.length > 0 ? user.posts[0].postName : '',
          roleName: user.roles && user.roles.length > 0 ? user.roles[0].roleName : '',
          // 根据岗位名称或角色名称判断类型
          type: this.getUserType(user)
        }))
        console.log('处理后的人员列表:', this.allStaffList, '数量:', this.allStaffList.length)
        // 初始化过滤列表
        this.filterStaffList()
      }).catch(error => {
        console.error('加载人员列表失败:', error)
        this.$modal.showToast('加载人员列表失败')
      })
    },
    // 根据用户的岗位或角色判断类型
    getUserType(user) {
      const postName = user.posts && user.posts.length > 0 ? user.posts[0].postName : ''
      const roleName = user.roles && user.roles.length > 0 ? user.roles[0].roleName : ''
      const deptName = user.dept?.deptName || ''
      // console.log("获取用户类型:", postName, roleName,user)
      // 判断是否为司机
      if (postName.includes('司机') || roleName.includes('司机') || deptName.includes('车队') || deptName.includes('司机')) {
        return 'driver'
      }
      // 判断是否为护士
      if (postName.includes('护士') || roleName.includes('护士') || deptName.includes('护士')) {
        return 'nurse'
      }
      // 判断是否为医生
      if (postName.includes('医生') || roleName.includes('医生') || deptName.includes('医生') ) {
        return 'doctor'
      }
      if( deptName.includes("医护")){
        return 'doctor'
      }
      // 其他类型,默认为司机
      return 'driver'
    },
    // 显示人员选择弹窗
    showStaffSelector() {
      this.$refs.staffPopup.open()
      this.filterStaffList()
    },
    // 关闭人员选择弹窗
    closeStaffSelector() {
      this.$refs.staffPopup.close()
      this.staffSearchKeyword = ''
      this.staffFilterType = 'driver' // 重置为默认的司机类型
    },
    // 人员搜索
    onStaffSearch(e) {
      this.staffSearchKeyword = e.detail.value
      this.filterStaffList()
    },
    // 筛选人员类型
    filterStaff(type) {
      this.staffFilterType = type
      this.filterStaffList()
    },
    // 过滤人员列表
    filterStaffList() {
      console.log('开始过滤人员列表,原始数量:', this.allStaffList.length)
      let list = [...this.allStaffList]
      // 按类型过滤
      if (this.staffFilterType === 'driver') {
        list = list.filter(staff => staff.type === 'driver')
      } else if (this.staffFilterType === 'doctor') {
        list = list.filter(staff => staff.type === 'doctor')
      } else if (this.staffFilterType === 'nurse') {
        list = list.filter(staff => staff.type === 'nurse')
      }
      console.log('按类型过滤后:', this.staffFilterType, '数量:', list.length)
      // 按关键词搜索
      if (this.staffSearchKeyword && this.staffSearchKeyword.trim() !== '') {
        const keyword = this.staffSearchKeyword.trim().toLowerCase()
        list = list.filter(staff => {
          return staff.nickName.toLowerCase().includes(keyword) ||
                 (staff.phonenumber && staff.phonenumber.includes(keyword))
        })
      }
      console.log('按关键词过滤后,数量:', list.length)
      this.filteredStaffList = list
      // console.log('最终过滤结果:', this.filteredStaffList)
    },
    // 切换人员选中状态
    toggleStaffSelection(staff) {
      const index = this.selectedStaff.findIndex(s => s.userId === staff.userId)
      if (index > -1) {
        // 如果是第一个(当前用户),不允许移除
        if (index === 0) {
          this.$modal.showToast('当前用户不能移除')
          return
        }
        // 已选中,移除
        this.selectedStaff.splice(index, 1)
      } else {
        // 未选中,添加
        this.selectedStaff.push(staff)
      }
    },
    // 判断人员是否已选中
    isStaffSelected(userId) {
      return this.selectedStaff.some(staff => staff.userId === userId)
    },
    // 确认人员选择
    confirmStaffSelection() {
      if (this.selectedStaff.length === 0) {
        this.$modal.showToast('请至少选择一名人员')
        return
      }
      this.closeStaffSelector()
    },
    // 移除人员
    removeStaff(index) {
      if (index === 0) {
        this.$modal.showToast('当前用户不能移除')
        return
      }
      this.selectedStaff.splice(index, 1)
    },
    addStaff() {
      this.showStaffSelector()
    // 人员变化事件
    onStaffChange(staff) {
      console.log('选中人员变化:', staff)
      // 组件已经通过 v-model 更新了 selectedStaff
    },
    calculateDistanceByManualAddress() {
      const fromAddress = this.taskForm.hospitalOut.address
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/LegacySystemSyncServiceImpl.java
@@ -1193,16 +1193,16 @@
            String response = sendHttpPost(legacyConfig.getDispatchUpdateUrl(), params);
            log.info("重新同步调度单到旧系统响应: ServiceOrdID:{},DispatchOrdId:{},Result: {}",emergency.getLegacyServiceOrdId(),emergency.getLegacyDispatchOrdId(), response);
            // 解析响应
            Long dispatchOrdId = parseResponse(response);
//            Long dispatchOrdId = parseResponse(response);
            
            if (dispatchOrdId != null && dispatchOrdId > 0) {
            if (response != null && response.equals("OK")) {
                // 重新同步成功,清除重新同步标记
                emergency.setNeedResync(0);
                emergency.setDispatchSyncTime(new Date());
                emergency.setDispatchSyncErrorMsg(null);
                sysTaskEmergencyService.updateSysTaskEmergency(emergency);
                
                log.info("调度单重新同步成功,任务ID: {}, DispatchOrdID: {}", taskId, dispatchOrdId);
                log.info("调度单重新同步成功,任务ID: {}, DispatchOrdID: {}", taskId, emergency.getLegacyDispatchOrdId());
                return true;
            } else {
                // 重新同步失败
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/LegacyTransferSyncServiceImpl.java
@@ -424,17 +424,9 @@
            
            // 设置区域类型
            String serviceOrdAreaType = getStringValue(order, "ServiceOrdAreaType");
            if (StringUtils.isNotEmpty(serviceOrdAreaType)) {
                // 可以根据需要将区域类型映射到TaskCreateVO的其他字段
                log.debug("区域类型: {}", serviceOrdAreaType);
            }
            
            // 设置用户ID
            Long serviceOrdUserID = getLongValue(order, "ServiceOrdUserID");
            if (serviceOrdUserID != null) {
                // 可以根据需要将用户ID映射到TaskCreateVO的其他字段
                log.debug("用户ID: {}", serviceOrdUserID);
            }
            
            // 设置患者信息
            TaskCreateVO.PatientInfo patientInfo = new TaskCreateVO.PatientInfo();
@@ -525,7 +517,7 @@
            createTaskVo.setHospitalIn(hospitalInInfo);
            
            // 设置地址信息
            createTaskVo.setDepartureAddress(getStringValue(order, "ServiceOrdTraVia"));
            createTaskVo.setDepartureAddress(getStringValue(order, "ServiceOrdTraStreet"));
            createTaskVo.setDestinationAddress(getStringValue(order, "ServiceOrdTraEnd"));
            
            // 设置价格和距离信息
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysTaskServiceImpl.java
@@ -633,11 +633,11 @@
                }
            }
        }
        // 用于跟踪是否需要重新同步(车辆、人员、地址、成交价变更)
        boolean needResync = true;
        int result = sysTaskMapper.updateSysTask(task);
        
        // 用于跟踪是否需要重新同步(车辆、人员、地址、成交价变更)
        boolean needResync = false;
        
        // 更新车辆关联
        if (result > 0 && updateVO.getVehicleIds() != null && !updateVO.getVehicleIds().isEmpty()) {