wlzboy
2025-11-05 37de2f4b0f732ca5c19582d4a340ad7c987925b5
feat: 部门管理多个车辆
28个文件已修改
2个文件已添加
1199 ■■■■ 已修改文件
app/api/hospital.js 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/api/system/dept.js 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/pages/task/create-emergency.vue 200 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/HospDataController.java 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDeptController.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/task/SysTaskController.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/task/SysTaskVehicleController.java 21 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysUser.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/domain/UserSyncDTO.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/domain/VehicleDept.java 67 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/domain/VehicleInfo.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/mapper/HospDataMapper.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysDeptMapper.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/mapper/VehicleInfoMapper.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDeptService.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/IVehicleInfoService.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDeptServiceImpl.java 93 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/UserSyncServiceImpl.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/VehicleInfoServiceImpl.java 101 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/VehicleSyncServiceImpl.java 139 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/resources/mapper/system/HospDataMapper.xml 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/resources/mapper/system/SysDeptMapper.xml 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/resources/mapper/system/UserSyncMapper.xml 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/resources/mapper/system/VehicleInfoMapper.xml 59 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/api/system/dept.js 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/views/system/user/index.vue 105 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/views/system/vehicle/index.vue 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
sql/vehicle_dept_relation.sql 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/api/hospital.js
@@ -29,3 +29,35 @@
    }
  })
}
/**
 * 获取常用转出医院列表
 * @param {string} serviceOrdClass 分公司编码(service_order_class)
 * @param {string} region 地域关键词(可选)
 */
export function getFrequentOutHospitals(serviceOrdClass, region) {
  return request({
    url: '/system/hospital/frequent/out',
    method: 'get',
    params: {
      serviceOrdClass: serviceOrdClass,
      region: region
    }
  })
}
/**
 * 获取常用转入医院列表
 * @param {string} serviceOrdClass 分公司编码(service_order_class)
 * @param {string} region 地域关键词(可选)
 */
export function getFrequentInHospitals(serviceOrdClass, region) {
  return request({
    url: '/system/hospital/frequent/in',
    method: 'get',
    params: {
      serviceOrdClass: serviceOrdClass,
      region: region
    }
  })
}
app/api/system/dept.js
@@ -17,11 +17,8 @@
 */
export function listBranchCompany() {
  return request({
    url: '/system/dept/list',
    method: 'get',
    params: {
      parentId: 100
    }
    url: '/system/dept/branch/by-oa',
    method: 'get'
  })
}
app/pages/task/create-emergency.vue
@@ -18,7 +18,7 @@
        </picker>
      </view>
        <view class="form-item">
        <view class="form-label">归属机构</view>
        <view class="form-label required">归属机构</view>
        <picker mode="selector" :range="organizations" @change="onOrganizationChange">
          <view class="form-input picker-input">
            {{ selectedOrganization || '请选择归属机构' }}
@@ -274,7 +274,7 @@
      </view>
      
      <view class="form-item">
        <view class="form-label">转运公里数</view>
        <view class="form-label required">转运公里数</view>
        <input 
          class="form-input" 
          type="digit" 
@@ -284,7 +284,7 @@
      </view>
      
      <view class="form-item">
        <view class="form-label">成交价</view>
        <view class="form-label required">成交价</view>
        <input 
          class="form-input" 
          type="digit" 
@@ -444,13 +444,13 @@
import { addTask } from "@/api/task"
import { listAvailableVehicles, getUserBoundVehicle } from "@/api/vehicle"
import { calculateDistance, baiduDistanceByAddress } from "@/api/map"
import { searchHospitals } from "@/api/hospital"
import { searchHospitals, getFrequentOutHospitals, getFrequentInHospitals } from "@/api/hospital"
import { listUser } from "@/api/system/user"
import { searchIcd10 } from "@/api/icd10"
import { getDicts } from "@/api/dict"
import { getServiceOrdAreaTypes, getServiceOrderTypes, getHospitalDepartments } from "@/api/dictionary"
import { listBranchCompany } from "@/api/system/dept"
import { listBranchCompany, getDept } from "@/api/system/dept"
import MapSelector from '@/components/map-selector.vue'
export default {
@@ -465,6 +465,7 @@
      selectedVehicleId: null,
      selectedOrganization: '',
      selectedOrganizationId: null, // 归属机构ID(部门ID)
      selectedOrganizationServiceOrderClass: '', // 归属机构的服务单编码
      selectedRegion: '', // 从归属机构中提取的地域信息(如:广州、深圳等)
      selectedEmergencyTaskType: '', // 选中的任务类型文本
      selectedEmergencyTaskTypeId: null, // 选中的任务类型ID
@@ -618,17 +619,26 @@
    },
    
    getAvailableVehicles() {
      const deptId = this.currentUser.deptId
      return listAvailableVehicles(deptId, 'EMERGENCY').then(response => {
        const vehicleList = response.data || response.rows || []
      // 根据用户有权限管理的分公司,查询所有可用车辆
      listAvailableVehicles(null, 'EMERGENCY').then(response => {
        const vehicleList = response.data || []
        this.vehicleOptions = vehicleList.map(vehicle => ({
          id: vehicle.vehicleId,
          name: vehicle.vehicleNo,
          type: vehicle.vehicleType,
          status: vehicle.status
          status: vehicle.status,
          deptNames: vehicle.deptNames || [] // 车辆归属的多个分公司
        }))
        this.vehicles = this.vehicleOptions.map(v => v.name)
      }).catch(() => {
        this.vehicles = this.vehicleOptions.map(v => {
          // 如果车辆归属多个分公司,在车牌号后面显示
          if (v.deptNames && v.deptNames.length > 0) {
            return `${v.name} (${v.deptNames.join('、')})`
          }
          return v.name
        })
        console.log('加载可用车辆数量:', this.vehicles.length)
      }).catch(error => {
        console.error('加载车辆列表失败:', error)
        this.vehicles = []
      })
    },
@@ -644,8 +654,9 @@
      const selected = this.organizationOptions[index]
      this.selectedOrganization = selected.deptName
      this.selectedOrganizationId = selected.deptId // 保存部门ID
      // 从归属机构中提取地域关键词(去除"分公司"后缀)
      // 例如:"广州分公司" -> "广州"
      this.selectedOrganizationServiceOrderClass = selected.serviceOrderClass || '' // 保存服务单编码
      // 从归属机构中提取地域关键词(去除“分公司”后缀)
      // 例如:“广州分公司” -> “广州”
      this.selectedRegion = selected.deptName.replace(/分公司$/g, '').trim()
      // 重新加载医院列表(带地域过滤)
      this.loadDefaultHospitals()
@@ -668,9 +679,10 @@
          if (index !== -1) {
            this.selectedOrganization = this.currentUser.branchCompanyName
            this.selectedOrganizationId = this.organizationOptions[index].deptId // 保存部门ID
            this.selectedOrganizationServiceOrderClass = this.organizationOptions[index].serviceOrderClass || '' // 保存服务单编码
            // 提取地域关键词
            this.selectedRegion = this.selectedOrganization.replace(/分公司$/g, '').trim()
            console.log('默认选中归属机构:', this.selectedOrganization, '部门ID:', this.selectedOrganizationId, '地域:', this.selectedRegion)
            console.log('默认选中归属机构:', this.selectedOrganization, '部门ID:', this.selectedOrganizationId, '服务单编码:', this.selectedOrganizationServiceOrderClass, '地域:', this.selectedRegion)
            // 加载医院列表(带地域过滤)
            this.loadDefaultHospitals()
          }
@@ -788,8 +800,63 @@
      this.taskForm.hospitalIn.departmentId = selected.id  // 保存科室ID
    },
    
    // 加载默认医院列表(前100条)
    // 加载默认医院列表(常用医院)
    loadDefaultHospitals() {
      // 检查是否有服务单编码
      if (!this.selectedOrganizationServiceOrderClass) {
        console.warn('未找到服务单编码,无法加载常用医院')
        // 如果没有服务单编码,降级为普通搜索(按地域过滤)
        this.loadDefaultHospitalsByRegion()
        return
      }
      // 转出医院:加载当前分公司的常用转出医院
      getFrequentOutHospitals(this.selectedOrganizationServiceOrderClass, this.selectedRegion).then(response => {
        this.hospitalOutResults = response.data || []
        console.log('加载常用转出医院:', this.selectedOrganizationServiceOrderClass, '地域:', this.selectedRegion, '数量:', this.hospitalOutResults.length)
        // 如果没有常用医院,降级为普通搜索
        if (this.hospitalOutResults.length === 0) {
          console.log('未找到常用转出医院,降级为地域搜索')
          searchHospitals('', this.selectedRegion).then(res => {
            this.hospitalOutResults = res.data || []
          })
        }
      }).catch(error => {
        console.error('加载常用转出医院失败:', error)
        // 失败后降级为普通搜索
        searchHospitals('', this.selectedRegion).then(res => {
          this.hospitalOutResults = res.data || []
        })
      })
      // 转入医院:加载当前分公司的常用转入医院(本地区域优先)
      getFrequentInHospitals(this.selectedOrganizationServiceOrderClass, '').then(response => {
        const allHospitals = response.data || []
        // 将医院按地域排序:本地区域优先
        this.hospitalInResults = this.sortHospitalsByRegion(allHospitals)
        console.log('加载常用转入医院:', this.selectedOrganizationServiceOrderClass, '数量:', this.hospitalInResults.length)
        // 如果没有常用医院,降级为普通搜索
        if (this.hospitalInResults.length === 0) {
          console.log('未找到常用转入医院,降级为全部医院')
          searchHospitals('', '').then(res => {
            const allHospitals = res.data || []
            this.hospitalInResults = this.sortHospitalsByRegion(allHospitals)
          })
        }
      }).catch(error => {
        console.error('加载常用转入医院失败:', error)
        // 失败后降级为普通搜索
        searchHospitals('', '').then(res => {
          const allHospitals = res.data || []
          this.hospitalInResults = this.sortHospitalsByRegion(allHospitals)
        })
      })
    },
    // 降级加载医院(按地域过滤)
    loadDefaultHospitalsByRegion() {
      // 转出医院:只加载当前区域的医院(带地域过滤)
      searchHospitals('', this.selectedRegion).then(response => {
        this.hospitalOutResults = response.data || []
@@ -800,7 +867,7 @@
      })
      
      // 转入医院:加载所有医院(不带地域过滤,后续会按地域排序)
      searchHospitals('', this.selectedRegion).then(response => {
      searchHospitals('', '').then(response => {
        const allHospitals = response.data || []
        // 将医院按地域排序:本地区域优先
        this.hospitalInResults = this.sortHospitalsByRegion(allHospitals)
@@ -841,14 +908,39 @@
    
    // 转出医院输入框获得焦点
    onHospitalOutFocus() {
      // 如果没有搜索关键词,只显示当前区域的医院
      // 如果没有搜索关键词,显示常用转出医院
      if (!this.hospitalOutSearchKeyword || this.hospitalOutSearchKeyword.trim() === '') {
        searchHospitals('', this.selectedRegion).then(response => {
          this.hospitalOutResults = response.data || []
        }).catch(error => {
          console.error('加载转出医院失败:', error)
          this.hospitalOutResults = []
        })
        // 如果已经加载过常用医院,直接显示
        if (this.hospitalOutResults.length > 0) {
          this.showHospitalOutResults = true
          return
        }
        // 否则重新加载常用医院
        if (this.selectedOrganizationServiceOrderClass) {
          getFrequentOutHospitals(this.selectedOrganizationServiceOrderClass, this.selectedRegion).then(response => {
            this.hospitalOutResults = response.data || []
            // 如果没有常用医院,降级为普通搜索
            if (this.hospitalOutResults.length === 0) {
              searchHospitals('', this.selectedRegion).then(res => {
                this.hospitalOutResults = res.data || []
              })
            }
          }).catch(error => {
            console.error('加载常用转出医院失败:', error)
            searchHospitals('', this.selectedRegion).then(res => {
              this.hospitalOutResults = res.data || []
            })
          })
        } else {
          // 没有服务单编码,使用普通搜索
          searchHospitals('', this.selectedRegion).then(response => {
            this.hospitalOutResults = response.data || []
          }).catch(error => {
            console.error('加载转出医院失败:', error)
            this.hospitalOutResults = []
          })
        }
      }
      this.showHospitalOutResults = true
    },
@@ -916,16 +1008,45 @@
    
    // 转入医院输入框获得焦点
    onHospitalInFocus() {
      // 如果没有搜索关键词,显示所有医院(本地区域优先)
      // 如果没有搜索关键词,显示常用转入医院
      if (!this.hospitalInSearchKeyword || this.hospitalInSearchKeyword.trim() === '') {
        searchHospitals('', '').then(response => {
          const allHospitals = response.data || []
          // 按地域排序:本地区域优先
          this.hospitalInResults = this.sortHospitalsByRegion(allHospitals)
        }).catch(error => {
          console.error('加载转入医院失败:', error)
          this.hospitalInResults = []
        })
        // 如果已经加载过常用医院,直接显示
        if (this.hospitalInResults.length > 0) {
          this.showHospitalInResults = true
          return
        }
        // 否则重新加载常用医院
        if (this.selectedOrganizationServiceOrderClass) {
          getFrequentInHospitals(this.selectedOrganizationServiceOrderClass, '').then(response => {
            const allHospitals = response.data || []
            // 按地域排序:本地区域优先
            this.hospitalInResults = this.sortHospitalsByRegion(allHospitals)
            // 如果没有常用医院,降级为普通搜索
            if (this.hospitalInResults.length === 0) {
              searchHospitals('', '').then(res => {
                const allHospitals = res.data || []
                this.hospitalInResults = this.sortHospitalsByRegion(allHospitals)
              })
            }
          }).catch(error => {
            console.error('加载常用转入医院失败:', error)
            searchHospitals('', '').then(res => {
              const allHospitals = res.data || []
              this.hospitalInResults = this.sortHospitalsByRegion(allHospitals)
            })
          })
        } else {
          // 没有服务单编码,使用普通搜索
          searchHospitals('', '').then(response => {
            const allHospitals = response.data || []
            // 按地域排序:本地区域优先
            this.hospitalInResults = this.sortHospitalsByRegion(allHospitals)
          }).catch(error => {
            console.error('加载转入医院失败:', error)
            this.hospitalInResults = []
          })
        }
      }
      this.showHospitalInResults = true
    },
@@ -1290,6 +1411,11 @@
        return false
      }
      
      if (!this.selectedOrganizationId) {
        this.$modal.showToast('请选择归属机构')
        return false
      }
      if (!this.selectedEmergencyTaskType) {
        this.$modal.showToast('请选择任务类型')
        return false
@@ -1340,6 +1466,16 @@
        return false
      }
      
      if (!this.taskForm.transferDistance) {
        this.$modal.showToast('请输入转运公里数')
        return false
      }
      if (!this.taskForm.price) {
        this.$modal.showToast('请输入成交价')
        return false
      }
      return true
    },
    
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/HospDataController.java
@@ -47,4 +47,42 @@
        HospData hospital = hospDataMapper.selectHospDataById(hospId);
        return success(hospital);
    }
    /**
     * 获取常用转出医院列表
     * @param serviceOrdClass 分公司编码(service_order_class)
     * @param region 地域关键词(可选)
     */
    @GetMapping("/frequent/out")
    public AjaxResult getFrequentOutHospitals(
            @RequestParam("serviceOrdClass") String serviceOrdClass,
            @RequestParam(value = "region", required = false) String region) {
        // 查询常用转出医院ID列表
        List<Integer> hospIds = hospDataMapper.selectFrequentOutHospitalIds(serviceOrdClass);
        if (hospIds == null || hospIds.isEmpty()) {
            return success();
        }
        // 根据ID列表查询医院详情
        List<HospData> hospitals = hospDataMapper.selectHospDataByIds(hospIds, region);
        return success(hospitals);
    }
    /**
     * 获取常用转入医院列表
     * @param serviceOrdClass 分公司编码(service_order_class)
     * @param region 地域关键词(可选)
     */
    @GetMapping("/frequent/in")
    public AjaxResult getFrequentInHospitals(
            @RequestParam("serviceOrdClass") String serviceOrdClass,
            @RequestParam(value = "region", required = false) String region) {
        // 查询常用转入医院ID列表
        List<Integer> hospIds = hospDataMapper.selectFrequentInHospitalIds(serviceOrdClass);
        if (hospIds == null || hospIds.isEmpty()) {
            return success();
        }
        // 根据ID列表查询医院详情
        List<HospData> hospitals = hospDataMapper.selectHospDataByIds(hospIds, region);
        return success(hospitals);
    }
}
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDeptController.java
@@ -1,6 +1,8 @@
package com.ruoyi.web.controller.system;
import java.util.List;
import com.ruoyi.common.core.domain.entity.SysUser;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
@@ -21,6 +23,7 @@
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.system.service.ISysDeptService;
import com.ruoyi.system.service.ISysUserService;
/**
 * 部门信息
@@ -34,6 +37,9 @@
    @Autowired
    private ISysDeptService deptService;
    @Autowired
    private ISysUserService userService;
    /**
     * 获取部门列表
     */
@@ -45,6 +51,29 @@
    }
    /**
     * 基于当前用户的 OA_OrderClass 返回分公司列表
     */
    @GetMapping("/branch/by-oa")
    public AjaxResult listBranchByOaOrderClass()
    {
        com.ruoyi.common.core.domain.model.LoginUser loginUser = com.ruoyi.common.utils.SecurityUtils.getLoginUser();
        com.ruoyi.common.core.domain.entity.SysUser user = loginUser.getUser();
        java.util.List<com.ruoyi.common.core.domain.entity.SysDept> result = deptService.computeBranchCompaniesForUser(user);
        return success(result);
    }
    /**
     * 基于指定用户ID的 OA_OrderClass 返回分公司列表,并附加该用户所属分公司
     */
    @GetMapping("/branch/by-user/{userId}")
    public AjaxResult listBranchByUser(@PathVariable("userId") Long userId)
    {
        SysUser user = userService.selectUserById(userId);
        List<SysDept> result = deptService.computeBranchCompaniesForUser(user);
        return success(result);
    }
    /**
     * 查询部门列表(排除节点)
     */
    @GetMapping("/list/exclude/{deptId}")
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java
@@ -71,6 +71,26 @@
    {
        LoginUser loginUser = SecurityUtils.getLoginUser();
        SysUser user = loginUser.getUser();
        // 计算可管理分公司列表(基于 OA_OrderClass 与 sys_dept.service/dispatch_order_class)
        java.util.List<SysDept> branchCompanies = new java.util.ArrayList<>();
        java.util.Set<Long> seen = new java.util.HashSet<>();
        if (com.ruoyi.common.utils.StringUtils.isNotEmpty(user.getOaOrderClass())) {
            String[] codes = user.getOaOrderClass().split(",");
            for (String raw : codes) {
                String code = raw.trim();
                if (code.isEmpty()) continue;
                SysDept cond1 = new SysDept();
                cond1.setParentId(100L);
                cond1.setServiceOrderClass(code);
                java.util.List<SysDept> list1 = deptService.selectDeptList(cond1);
                for (SysDept d : list1) { if (seen.add(d.getDeptId())) branchCompanies.add(d); }
                SysDept cond2 = new SysDept();
                cond2.setParentId(100L);
                cond2.setDispatchOrderClass(code);
                java.util.List<SysDept> list2 = deptService.selectDeptList(cond2);
                for (SysDept d : list2) { if (seen.add(d.getDeptId())) branchCompanies.add(d); }
            }
        }
        // 角色集合
        Set<String> roles = permissionService.getRolePermission(user);
        // 权限集合
@@ -132,6 +152,7 @@
        ajax.put("permissions", permissions);
        ajax.put("branchCompanyId", branchCompanyId);
        ajax.put("branchCompanyName", branchCompanyName);
        ajax.put("branchCompanies", branchCompanies);
        ajax.put("oaUserId", user.getOaUserId());
        return ajax;
    }
ruoyi-admin/src/main/java/com/ruoyi/web/controller/task/SysTaskController.java
@@ -11,6 +11,7 @@
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
@@ -18,12 +19,14 @@
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.system.domain.SysTask;
import com.ruoyi.system.domain.SysTaskLog;
import com.ruoyi.system.domain.VehicleInfo;
import com.ruoyi.system.domain.vo.TaskQueryVO;
import com.ruoyi.system.domain.vo.TaskCreateVO;
import com.ruoyi.system.domain.vo.TaskUpdateVO;
import com.ruoyi.system.domain.vo.TaskStatisticsVO;
import com.ruoyi.system.domain.enums.TaskStatus;
import com.ruoyi.system.service.ISysTaskService;
import com.ruoyi.system.service.IVehicleInfoService;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.common.core.page.TableDataInfo;
@@ -39,6 +42,9 @@
    
    @Autowired
    private ISysTaskService sysTaskService;
    @Autowired
    private IVehicleInfoService vehicleInfoService;
    /**
     * 查询任务管理列表(后台管理端)
ruoyi-admin/src/main/java/com/ruoyi/web/controller/task/SysTaskVehicleController.java
@@ -16,7 +16,9 @@
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.system.domain.SysTaskVehicle;
import com.ruoyi.system.domain.VehicleInfo;
import com.ruoyi.system.service.ISysTaskService;
import com.ruoyi.system.service.IVehicleInfoService;
/**
 * 任务车辆关联Controller
@@ -30,6 +32,9 @@
    
    @Autowired
    private ISysTaskService sysTaskService;
    @Autowired
    private IVehicleInfoService vehicleInfoService;
    /**
     * 查询任务关联的车辆列表
@@ -41,12 +46,20 @@
    }
    /**
     * 查询可用车辆列表
     * 查询可用车辆列表(根据用户权限)
     * 根据用户所在的分公司,通过车辆-分公司关联表查询所有可用车辆
     *
     * @param deptId 部门ID(可选,如果不传则使用当前用户的deptId)
     * @param taskType 任务类型(可选)
     * @return 车辆列表
     */
    @GetMapping("/available")
    public AjaxResult getAvailableVehicles(@RequestParam Long deptId, @RequestParam(required = false) String taskType) {
        List<SysTaskVehicle> list = sysTaskService.getAvailableVehicles(deptId, taskType);
        return success(list);
    public AjaxResult getAvailableVehicles(
            @RequestParam(required = false) Long deptId,
            @RequestParam(required = false) String taskType) {
        // 如果没有传deptId,使用当前用户的userId查询
        List<VehicleInfo> vehicles = vehicleInfoService.selectAvailableVehiclesByUser(getUserId());
        return success(vehicles);
    }
    /**
ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysUser.java
@@ -92,6 +92,9 @@
    /** SQL Server中的OA用户ID */
    private Integer oaUserId;
    /** OA系统的订单编码列表(如:BF,AB,SA) */
    private String oaOrderClass;
    public SysUser()
    {
@@ -310,6 +313,16 @@
        this.oaUserId = oaUserId;
    }
    public String getOaOrderClass()
    {
        return oaOrderClass;
    }
    public void setOaOrderClass(String oaOrderClass)
    {
        this.oaOrderClass = oaOrderClass;
    }
    @Override
    public String toString() {
        return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
@@ -333,6 +346,7 @@
            .append("remark", getRemark())
            .append("dept", getDept())
            .append("oaUserId", getOaUserId())
            .append("oaOrderClass", getOaOrderClass())
            .toString();
    }
}
ruoyi-system/src/main/java/com/ruoyi/system/domain/UserSyncDTO.java
@@ -20,6 +20,9 @@
    /** SQL Server中的部门ID */
    private Integer departmentId;
    /** 用户可用分公司编码列表(来源于OA_User.OA_OrderClass) */
    private String oaOrderClass;
    /** 用户性别(0=男,1=女,2=未知) */
    private String sex;
@@ -99,6 +102,16 @@
        this.phonenumber = phonenumber;
    }
    public String getOaOrderClass()
    {
        return oaOrderClass;
    }
    public void setOaOrderClass(String oaOrderClass)
    {
        this.oaOrderClass = oaOrderClass;
    }
    @Override
    public String toString()
    {
@@ -110,6 +123,7 @@
                ", sex='" + sex + '\'' +
                ", email='" + email + '\'' +
                ", phonenumber='" + phonenumber + '\'' +
                ", oaOrderClass='" + oaOrderClass + '\'' +
                '}';
    }
}
ruoyi-system/src/main/java/com/ruoyi/system/domain/VehicleDept.java
New file
@@ -0,0 +1,67 @@
package com.ruoyi.system.domain;
import com.ruoyi.common.core.domain.BaseEntity;
/**
 * 车辆-分公司关联对象 tb_vehicle_dept
 *
 * @author ruoyi
 * @date 2025-01-16
 */
public class VehicleDept extends BaseEntity {
    private static final long serialVersionUID = 1L;
    /** 关联ID */
    private Long id;
    /** 车辆ID */
    private Long vehicleId;
    /** 分公司ID */
    private Long deptId;
    /** 对应的分公司编码 */
    private String orderClass;
    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public Long getVehicleId() {
        return vehicleId;
    }
    public void setVehicleId(Long vehicleId) {
        this.vehicleId = vehicleId;
    }
    public Long getDeptId() {
        return deptId;
    }
    public void setDeptId(Long deptId) {
        this.deptId = deptId;
    }
    public String getOrderClass() {
        return orderClass;
    }
    public void setOrderClass(String orderClass) {
        this.orderClass = orderClass;
    }
    @Override
    public String toString() {
        return "VehicleDept{" +
                "id=" + id +
                ", vehicleId=" + vehicleId +
                ", deptId=" + deptId +
                ", orderClass='" + orderClass + '\'' +
                '}';
    }
}
ruoyi-system/src/main/java/com/ruoyi/system/domain/VehicleInfo.java
@@ -5,6 +5,8 @@
import com.ruoyi.common.annotation.Excel;
import com.ruoyi.common.core.domain.BaseEntity;
import java.util.List;
/**
 * 车辆信息对象 tb_vehicle_info
 */
@@ -53,6 +55,12 @@
    /** 归属部门名称 */
    @Excel(name = "归属部门")
    private String deptName;
    /** 归属的多个分公司ID列表(用于查询和显示) */
    private List<Long> deptIds;
    /** 归属的多个分公司名称列表(用于显示) */
    private List<String> deptNames;
    public void setVehicleId(Long vehicleId) {
        this.vehicleId = vehicleId;
@@ -141,6 +149,22 @@
    public void setDeptName(String deptName) {
        this.deptName = deptName;
    }
    public List<Long> getDeptIds() {
        return deptIds;
    }
    public void setDeptIds(List<Long> deptIds) {
        this.deptIds = deptIds;
    }
    public List<String> getDeptNames() {
        return deptNames;
    }
    public void setDeptNames(List<String> deptNames) {
        this.deptNames = deptNames;
    }
    @Override
    public String toString() {
ruoyi-system/src/main/java/com/ruoyi/system/mapper/HospDataMapper.java
@@ -32,4 +32,29 @@
     * @return 医院信息
     */
    HospData selectHospDataById(@Param("hospId") Integer hospId);
    /**
     * 查询常用转出医院ID列表
     *
     * @param serviceOrdClass 分公司编码(service_order_class)
     * @return 常用转出医院ID列表
     */
    List<Integer> selectFrequentOutHospitalIds(@Param("serviceOrdClass") String serviceOrdClass);
    /**
     * 查询常用转入医院ID列表
     *
     * @param serviceOrdClass 分公司编码(service_order_class)
     * @return 常用转入医院ID列表
     */
    List<Integer> selectFrequentInHospitalIds(@Param("serviceOrdClass") String serviceOrdClass);
    /**
     * 根据医院ID列表查询医院信息
     *
     * @param hospIds 医院ID列表
     * @param region 地域关键词(可选)
     * @return 医院列表
     */
    List<HospData> selectHospDataByIds(@Param("hospIds") List<Integer> hospIds, @Param("region") String region);
}
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysDeptMapper.java
@@ -141,4 +141,13 @@
     * @return 分公司ID,如果找不到则返回null
     */
    public Long selectBranchCompanyIdByDeptId(@Param("deptId") Long deptId);
    /**
     * 根据编码列表查询分公司
     * 在service_order_class或dispatch_order_class中匹配
     *
     * @param orderCodes 编码列表
     * @return 分公司列表
     */
    public List<SysDept> selectBranchCompaniesByOrderCodes(@Param("orderCodes") List<String> orderCodes);
}
ruoyi-system/src/main/java/com/ruoyi/system/mapper/VehicleInfoMapper.java
@@ -3,6 +3,7 @@
import java.util.List;
import org.apache.ibatis.annotations.Param;
import com.ruoyi.system.domain.VehicleInfo;
import com.ruoyi.system.domain.VehicleDept;
/**
 * 车辆信息Mapper接口
@@ -106,4 +107,20 @@
     * @return 车辆信息
     */
    public VehicleInfo getUserBoundVehicle(@Param("userId") Long userId);
    /**
     * 批量插入车辆-分公司关联
     *
     * @param list 车辆-分公司关联列表
     * @return 结果
     */
    public int batchInsertVehicleDept(@Param("list") List<VehicleDept> list);
    /**
     * 删除车辆的所有分公司关联
     *
     * @param vehicleId 车辆ID
     * @return 结果
     */
    public int deleteVehicleDeptByVehicleId(@Param("vehicleId") Long vehicleId);
ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDeptService.java
@@ -3,6 +3,7 @@
import java.util.List;
import com.ruoyi.common.core.domain.TreeSelect;
import com.ruoyi.common.core.domain.entity.SysDept;
import com.ruoyi.common.core.domain.entity.SysUser;
/**
 * 部门管理 服务层
@@ -121,4 +122,16 @@
     * @return 结果
     */
    public int deleteDeptById(Long deptId);
    /**
     * 计算指定用户的分公司列表
     * 逻辑:
     * 1. 根据用户的oaOrderClass匹配分公司(服务单编码/调度单编码)
     * 2. 附加用户所属的分公司(从deptId/ancestors解析)
     * 3. 去重后返回
     *
     * @param user 用户信息
     * @return 分公司列表
     */
    public List<SysDept> computeBranchCompaniesForUser(SysUser user);
}
ruoyi-system/src/main/java/com/ruoyi/system/service/IVehicleInfoService.java
@@ -88,4 +88,13 @@
     * @return 车辆信息
     */
    public VehicleInfo getUserBoundVehicle(Long userId);
    /**
     * 根据用户有权限管理的分公司,查询所有可用车辆
     * 通过车辆-分公司关联表查询
     *
     * @param userId 用户ID
     * @return 车辆列表
     */
    public List<VehicleInfo> selectAvailableVehiclesByUser(Long userId);
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDeptServiceImpl.java
@@ -1,8 +1,10 @@
package com.ruoyi.system.service.impl;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -335,4 +337,95 @@
    {
        return getChildList(list, t).size() > 0;
    }
    /**
     * 计算指定用户的分公司列表
     * 逻辑:
     * 1. 根据用户的oaOrderClass匹配分公司(服务单编码/调度单编码)
     * 2. 附加用户所属的分公司(从deptId/ancestors解析)
     * 3. 去重后返回
     *
     * @param user 用户信息
     * @return 分公司列表
     */
    @Override
    public List<SysDept> computeBranchCompaniesForUser(SysUser user)
    {
        List<SysDept> result = new ArrayList<>();
        Set<Long> seen = new HashSet<>();
        if (user == null) {
            return result;
        }
        // 1. 根据oaOrderClass匹配分公司(优化:一次查询)
        String orderClass = user.getOaOrderClass();
        if (StringUtils.isNotEmpty(orderClass))
        {
            // 解析orderClass为编码列表
            List<String> orderCodes = new ArrayList<>();
            for (String raw : orderClass.split(",")) {
                String code = raw.trim();
                if (!code.isEmpty()) {
                    orderCodes.add(code);
                }
            }
            // 一次性查询所有匹配的分公司
            if (!orderCodes.isEmpty()) {
                List<SysDept> matchedDepts = deptMapper.selectBranchCompaniesByOrderCodes(orderCodes);
                for (SysDept dept : matchedDepts) {
                    if (seen.add(dept.getDeptId())) {
                        result.add(dept);
                    }
                }
            }
        }
        // 2. 附加该用户所属分公司(从deptId/ancestors解析)
        if (user.getDeptId() != null)
        {
            SysDept userDept = selectDeptById(user.getDeptId());
            Long branchCompanyId = null;
            if (userDept != null)
            {
                // 判断是否本身就是分公司
                if (userDept.getParentId() != null && userDept.getParentId() == 100L)
                {
                    branchCompanyId = userDept.getDeptId();
                }
                // 否则从ancestors中查找
                else if (userDept.getAncestors() != null && !userDept.getAncestors().isEmpty())
                {
                    String[] ancestorIds = userDept.getAncestors().split(",");
                    for (int i = 0; i < ancestorIds.length; i++)
                    {
                        // 找到100的下一个就是分公司ID
                        if ("100".equals(ancestorIds[i]) && i + 1 < ancestorIds.length)
                        {
                            try {
                                branchCompanyId = Long.parseLong(ancestorIds[i + 1]);
                            } catch (NumberFormatException e) {
                                branchCompanyId = null;
                            }
                            break;
                        }
                    }
                }
            }
            // 如果找到了分公司ID,添加到结果中
            if (branchCompanyId != null)
            {
                SysDept branchCompany = selectDeptById(branchCompanyId);
                if (branchCompany != null && seen.add(branchCompany.getDeptId()))
                {
                    result.add(branchCompany);
                }
            }
        }
        return result;
    }
}
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/UserSyncServiceImpl.java
@@ -219,6 +219,10 @@
        }
        
        existingUser.setUpdateBy("sync");
        if (StringUtils.isNotEmpty(dto.getOaOrderClass()))
        {
            existingUser.setOaOrderClass(dto.getOaOrderClass());
        }
        sysUserMapper.updateUser(existingUser);
    }
@@ -231,6 +235,10 @@
        newUser.setUserName(dto.getUserName());
        newUser.setNickName(dto.getNickName());
        newUser.setOaUserId(dto.getOaUserId());
        if (StringUtils.isNotEmpty(dto.getOaOrderClass()))
        {
            newUser.setOaOrderClass(dto.getOaOrderClass());
        }
        
        if (deptId != null)
        {
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/VehicleInfoServiceImpl.java
@@ -1,14 +1,23 @@
package com.ruoyi.system.service.impl;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.Date;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.domain.entity.SysDept;
import com.ruoyi.system.mapper.VehicleInfoMapper;
import com.ruoyi.system.mapper.SysUserMapper;
import com.ruoyi.system.mapper.SysDeptMapper;
import com.ruoyi.system.domain.VehicleInfo;
import com.ruoyi.system.domain.VehicleDept;
import com.ruoyi.system.service.IVehicleInfoService;
import com.ruoyi.system.service.ISysDeptService;
/**
 * 车辆信息Service业务层处理
@@ -17,6 +26,15 @@
public class VehicleInfoServiceImpl implements IVehicleInfoService {
    @Autowired
    private VehicleInfoMapper vehicleInfoMapper;
    @Autowired
    private SysUserMapper sysUserMapper;
    @Autowired
    private SysDeptMapper sysDeptMapper;
    @Autowired
    private ISysDeptService sysDeptService;
    /**
     * 查询车辆信息
@@ -58,8 +76,16 @@
     * @return 结果
     */
    @Override
    @Transactional
    public int insertVehicleInfo(VehicleInfo vehicleInfo) {
        return vehicleInfoMapper.insertVehicleInfo(vehicleInfo);
        int rows = vehicleInfoMapper.insertVehicleInfo(vehicleInfo);
        // 如果选择了多个分公司,保存到关联表
        if (vehicleInfo.getDeptIds() != null && !vehicleInfo.getDeptIds().isEmpty()) {
            insertVehicleDept(vehicleInfo);
        }
        return rows;
    }
    /**
@@ -69,8 +95,34 @@
     * @return 结果
     */
    @Override
    @Transactional
    public int updateVehicleInfo(VehicleInfo vehicleInfo) {
        // 先删除旧的关联关系
        vehicleInfoMapper.deleteVehicleDeptByVehicleId(vehicleInfo.getVehicleId());
        // 如果选择了多个分公司,保存到关联表
        if (vehicleInfo.getDeptIds() != null && !vehicleInfo.getDeptIds().isEmpty()) {
            insertVehicleDept(vehicleInfo);
        }
        return vehicleInfoMapper.updateVehicleInfo(vehicleInfo);
    }
    /**
     * 插入车辆-分公司关联关系
     */
    private void insertVehicleDept(VehicleInfo vehicleInfo) {
        List<VehicleDept> vehicleDepts = new ArrayList<>();
        for (Long deptId : vehicleInfo.getDeptIds()) {
            VehicleDept vd = new VehicleDept();
            vd.setVehicleId(vehicleInfo.getVehicleId());
            vd.setDeptId(deptId);
            vd.setCreateBy(vehicleInfo.getCreateBy());
            vehicleDepts.add(vd);
        }
        if (!vehicleDepts.isEmpty()) {
            vehicleInfoMapper.batchInsertVehicleDept(vehicleDepts);
        }
    }
    /**
@@ -140,4 +192,51 @@
    public VehicleInfo getUserBoundVehicle(Long userId) {
        return vehicleInfoMapper.getUserBoundVehicle(userId);
    }
    /**
     * 根据用户有权限管理的分公司,查询所有可用车辆
     * 逻辑:
     * 1. 查询用户信息
     * 2. 调用sysDeptService.computeBranchCompaniesForUser获取用户管理的所有分公司
     * 3. 通过tb_vehicle_dept关联表查询这些分公司下的所有车辆
     *
     * @param userId 用户ID
     * @return 车辆列表
     */
    @Override
    public List<VehicleInfo> selectAvailableVehiclesByUser(Long userId) {
        // 1. 查询用户信息
        SysUser user = sysUserMapper.selectUserById(userId);
        if (user == null) {
            return new ArrayList<>();
        }
        // 2. 调用sysDeptService获取用户管理的所有分公司
        List<SysDept> branchCompanies = sysDeptService.computeBranchCompaniesForUser(user);
        if (branchCompanies.isEmpty()) {
            // 如果没有找到任何分公司,返回空列表
            return new ArrayList<>();
        }
        // 3. 根据分公司列表查询车辆,使用Set去重
        Set<Long> vehicleIdSet = new HashSet<>();
        List<VehicleInfo> allVehicles = new ArrayList<>();
        for (SysDept branchCompany : branchCompanies) {
            VehicleInfo query = new VehicleInfo();
            query.setDeptId(branchCompany.getDeptId());
            List<VehicleInfo> vehicles = vehicleInfoMapper.selectVehicleInfoList(query);
            // 去重添加
            for (VehicleInfo vehicle : vehicles) {
                if (!vehicleIdSet.contains(vehicle.getVehicleId())) {
                    vehicleIdSet.add(vehicle.getVehicleId());
                    allVehicles.add(vehicle);
                }
            }
        }
        return allVehicles;
    }
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/VehicleSyncServiceImpl.java
@@ -3,8 +3,10 @@
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.entity.SysDept;
import com.ruoyi.system.domain.VehicleInfo;
import com.ruoyi.system.domain.VehicleDept;
import com.ruoyi.system.domain.VehicleSyncDTO;
import com.ruoyi.system.mapper.SysDeptMapper;
import com.ruoyi.system.mapper.VehicleInfoMapper;
import com.ruoyi.system.service.IVehicleInfoService;
import com.ruoyi.system.service.IVehicleSyncDataService;
import com.ruoyi.system.service.IVehicleSyncService;
@@ -15,6 +17,7 @@
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -39,6 +42,9 @@
    @Autowired
    private SysDeptMapper sysDeptMapper;
    @Autowired
    private VehicleInfoMapper vehicleInfoMapper;
    /**
@@ -82,18 +88,28 @@
                    // 查询车辆是否存在
                    VehicleInfo existingVehicle = findVehicleByPlateNumber(plateNumber);
                    // 解析部门信息
                    Long deptId = parseDeptIdFromCarOrdClass(vehicleDTO.getCarOrdClass());
                    // 解析所有分公司ID(CarOrdClass可能包含多个编码,如:HB,TI)
                    List<VehicleDept> vehicleDepts = parseVehicleDepts(vehicleDTO.getCarOrdClass());
                    // 设置默认的主部门ID(第一个分公司)
                    Long primaryDeptId = null;
                    if (!vehicleDepts.isEmpty()) {
                        primaryDeptId = vehicleDepts.get(0).getDeptId();
                    }
                    if (existingVehicle != null)
                    {
                        // 更新车辆信息
                        existingVehicle.setCarId(vehicleDTO.getCarId());
                        existingVehicle.setDeptId(deptId);
                        // 可以选择是否更新其他字段
                        existingVehicle.setDeptId(primaryDeptId); // 设置主部门
                        vehicleInfoService.updateVehicleInfo(existingVehicle);
                        // 更新车辆-分公司关联
                        syncVehicleDepts(existingVehicle.getVehicleId(), vehicleDepts);
                        updateCount++;
                        log.debug("更新车辆: {} (CarID={}), 部门ID={}", plateNumber, vehicleDTO.getCarId(), deptId);
                        log.debug("更新车辆: {} (CarID={}), 关联分公司数: {}",
                                plateNumber, vehicleDTO.getCarId(), vehicleDepts.size());
                    }
                    else
                    {
@@ -101,13 +117,18 @@
                        VehicleInfo newVehicle = new VehicleInfo();
                        newVehicle.setVehicleNo(plateNumber);
                        newVehicle.setCarId(vehicleDTO.getCarId());
                        newVehicle.setDeptId(deptId);
                        newVehicle.setDeptId(primaryDeptId); // 设置主部门
                        newVehicle.setStatus("0");
                        newVehicle.setPlatformCode("LEGACY"); // 标记为旧系统同步
                        newVehicle.setRemark("从旧系统同步,CarID: " + vehicleDTO.getCarId());
                        vehicleInfoService.insertVehicleInfo(newVehicle);
                        // 新增车辆-分公司关联
                        syncVehicleDepts(newVehicle.getVehicleId(), vehicleDepts);
                        insertCount++;
                        log.debug("新增车辆: {} (CarID={}), 部门ID={}", plateNumber, vehicleDTO.getCarId(), deptId);
                        log.debug("新增车辆: {} (CarID={}), 关联分公司数: {}",
                                plateNumber, vehicleDTO.getCarId(), vehicleDepts.size());
                    }
                }
                catch (Exception e)
@@ -231,22 +252,23 @@
    }
    /**
     * 从 CarOrdClass 解析部门ID,并转换为分公司ID
     * CarOrdClass格式可能是:ZB、HB.TI等
     * 需要拆分并在sys_dept中匹配dispatch_order_class字段
     * 从 CarOrdClass 解析多个分公司关联
     * CarOrdClass格式可能是:HB、HB,TI、HB.TI等
     * 
     * @param carOrdClass 车辆单据类型编码
     * @return 分公司ID,如果未找到返回null
     * @return 车辆-分公司关联列表
     */
    private Long parseDeptIdFromCarOrdClass(String carOrdClass)
    private List<VehicleDept> parseVehicleDepts(String carOrdClass)
    {
        List<VehicleDept> vehicleDepts = new ArrayList<>();
        if (StringUtils.isBlank(carOrdClass))
        {
            log.debug("CarOrdClass为空,无法解析部门");
            return null;
            log.debug("CarOrdClass为空,无法解析分公司");
            return vehicleDepts;
        }
        // 拆分CarOrdClass,可能的分隔符:. , 空格
        // 拆分CarOrdClass,可能的分隔符:, . 空格
        String[] codes = carOrdClass.split("[.,\\s]+");
        
        for (String code : codes)
@@ -258,60 +280,95 @@
            
            code = code.trim();
            
            // 查询匹配dispatch_order_class的部门
            SysDept dept = findDeptByDispatchOrderClass(code);
            if (dept != null)
            // 查询匹配dispatch_order_class或service_order_class的部门
            SysDept dept = findDeptByOrderClass(code);
            if (dept != null && dept.getParentId() != null && dept.getParentId() == 100L)
            {
                log.debug("通过dispatch_order_class='{}' 找到部门: {} (ID={})",
                        code, dept.getDeptName(), dept.getDeptId());
                // 只处理分公司(parent_id=100)
                VehicleDept vehicleDept = new VehicleDept();
                vehicleDept.setDeptId(dept.getDeptId());
                vehicleDept.setOrderClass(code);
                vehicleDept.setCreateBy("system");
                vehicleDepts.add(vehicleDept);
                
                // 将部门ID转换为分公司ID
                Long branchCompanyId = sysDeptMapper.selectBranchCompanyIdByDeptId(dept.getDeptId());
                if (branchCompanyId != null)
                {
                    log.debug("将部门ID {} 转换为分公司ID: {}", dept.getDeptId(), branchCompanyId);
                    return branchCompanyId;
                }
                else
                {
                    log.warn("部门ID {} 无法转换为分公司ID,可能是总公司或数据异常", dept.getDeptId());
                    return null;
                }
                log.debug("通过order_class='{}' 找到分公司: {} (ID={})",
                        code, dept.getDeptName(), dept.getDeptId());
            }
            else
            {
                log.debug("未找到匹配order_class='{}' 的分公司", code);
            }
        }
        
        log.warn("未找到匹配CarOrdClass='{}' 的部门", carOrdClass);
        return null;
        return vehicleDepts;
    }
    /**
     * 同步车辆-分公司关联
     *
     * @param vehicleId 车辆ID
     * @param vehicleDepts 分公司关联列表
     */
    private void syncVehicleDepts(Long vehicleId, List<VehicleDept> vehicleDepts)
    {
        if (vehicleId == null || vehicleDepts == null)
        {
            return;
        }
        // 先删除旧的关联
        vehicleInfoMapper.deleteVehicleDeptByVehicleId(vehicleId);
        // 再插入新的关联
        if (!vehicleDepts.isEmpty())
        {
            for (VehicleDept vd : vehicleDepts)
            {
                vd.setVehicleId(vehicleId);
            }
            vehicleInfoMapper.batchInsertVehicleDept(vehicleDepts);
            log.debug("同步车辆ID={} 的分公司关联,数量: {}", vehicleId, vehicleDepts.size());
        }
    }
    /**
     * 根据dispatch_order_class查询部门
     * 根据order_class查询部门(同时匹配dispatch_order_class和service_order_class)
     * 
     * @param dispatchOrderClass 调度单编码
     * @param orderClass 编码
     * @return 部门信息
     */
    private SysDept findDeptByDispatchOrderClass(String dispatchOrderClass)
    private SysDept findDeptByOrderClass(String orderClass)
    {
        if (StringUtils.isBlank(dispatchOrderClass))
        if (StringUtils.isBlank(orderClass))
        {
            return null;
        }
        try
        {
            // 先尝试匹配dispatch_order_class
            SysDept query = new SysDept();
            query.setDispatchOrderClass(dispatchOrderClass);
            query.setDispatchOrderClass(orderClass);
            List<SysDept> depts = sysDeptMapper.selectDeptList(query);
            
            if (depts != null && !depts.isEmpty())
            {
                // 返回第一个匹配的部门
                return depts.get(0);
            }
            // 如果没有找到,尝试匹配service_order_class
            query = new SysDept();
            query.setServiceOrderClass(orderClass);
            depts = sysDeptMapper.selectDeptList(query);
            if (depts != null && !depts.isEmpty())
            {
                return depts.get(0);
            }
        }
        catch (Exception e)
        {
            log.error("查询dispatch_order_class='{}' 的部门失败", dispatchOrderClass, e);
            log.error("查询order_class='{}' 的部门失败", orderClass, e);
        }
        
        return null;
ruoyi-system/src/main/resources/mapper/system/HospDataMapper.xml
@@ -65,4 +65,47 @@
        FROM HospData
        WHERE HospID = #{hospId}
    </select>
    <!-- 查询常用转出医院ID列表 -->
    <select id="selectFrequentOutHospitalIds" resultType="java.lang.Integer">
        select ServiceOrdPtOutHospID from (
            select    ServiceOrdPtOutHospID,count(1) as InCount  from ServiceOrder
            where ServiceOrdClass=#{serviceOrdClass} and ServiceOrdPtOutHospID>0
            group by ServiceOrdPtOutHospID
        ) as t
        order by InCount desc
    </select>
    <!-- 查询常用转入医院ID列表 -->
    <select id="selectFrequentInHospitalIds" resultType="java.lang.Integer">
        select ServiceOrdPtInHospID from (
            select    ServiceOrdPtInHospID,count(1) as InCount  from ServiceOrder
            where ServiceOrdClass=#{serviceOrdClass} and ServiceOrdPtInHospID>0
            group by ServiceOrdPtInHospID
        ) as t
        order by InCount desc
    </select>
    <!-- 根据医院ID列表查询医院信息 -->
    <select id="selectHospDataByIds" resultMap="HospDataResult">
        SELECT
            HospID, HospName, HospCityID, HospShort,
            HopsProvince, HopsCity, HopsArea, HospAddress,
            HospTEL, HospUnitID, HospState, HospOAID,
            HospIntroducerID, HospIntroducerDate, HospLevel
        FROM HospData
        WHERE HospID IN
        <foreach collection="hospIds" item="hospId" open="(" separator="," close=")">
            #{hospId}
        </foreach>
        <if test="region != null and region != ''">
        AND (
            HopsProvince LIKE '%' + #{region} + '%'
                OR HopsCity LIKE '%' + #{region} + '%'
                OR HopsArea LIKE '%' + #{region} + '%'
        )
        </if>
        AND (HospState IS NULL OR HospState = 1)
        ORDER BY HospName
    </select>
</mapper>
ruoyi-system/src/main/resources/mapper/system/SysDeptMapper.xml
@@ -208,5 +208,18 @@
            )
        end
    </select>
    <!-- 根据编码列表查询分公司 -->
    <select id="selectBranchCompaniesByOrderCodes" resultMap="SysDeptResult">
        select dept_id, dept_name, parent_id, ancestors, service_order_class, dispatch_order_class
        from sys_dept
        where parent_id = 100
        and del_flag = '0'
        and (
            <foreach collection="orderCodes" item="code" separator=" or ">
                service_order_class = #{code} or dispatch_order_class = #{code}
            </foreach>
        )
    </select>
</mapper> 
ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml
@@ -19,6 +19,7 @@
        <result property="loginIp"      column="login_ip"     />
        <result property="loginDate"    column="login_date"   />
        <result property="oaUserId"     column="oa_user_id"   />
        <result property="oaOrderClass" column="oa_order_class" />
        <result property="createBy"     column="create_by"    />
        <result property="createTime"   column="create_time"  />
        <result property="updateBy"     column="update_by"    />
@@ -48,7 +49,7 @@
    </resultMap>
    
    <sql id="selectUserVo">
        select u.user_id, u.dept_id, u.user_name,u.oa_user_id, u.nick_name, u.email, u.avatar, u.phonenumber, u.password, u.sex, u.status, u.del_flag, u.login_ip, u.login_date, u.oa_user_id, u.create_by, u.create_time, u.remark,
        select u.user_id, u.dept_id, u.user_name,u.oa_user_id, u.oa_order_class, u.nick_name, u.email, u.avatar, u.phonenumber, u.password, u.sex, u.status, u.del_flag, u.login_ip, u.login_date, u.oa_user_id, u.create_by, u.create_time, u.remark,
        d.dept_id, d.parent_id, d.ancestors, d.dept_name, d.order_num, d.leader, d.status as dept_status,
        r.role_id, r.role_name, r.role_key, r.role_sort, r.data_scope, r.status as role_status
        from sys_user u
@@ -197,6 +198,7 @@
             <if test="password != null and password != ''">password,</if>
             <if test="status != null and status != ''">status,</if>
             <if test="oaUserId != null">oa_user_id,</if>
             <if test="oaOrderClass != null and oaOrderClass != ''">oa_order_class,</if>
             <if test="createBy != null and createBy != ''">create_by,</if>
             <if test="remark != null and remark != ''">remark,</if>
             create_time
@@ -212,6 +214,7 @@
             <if test="password != null and password != ''">#{password},</if>
             <if test="status != null and status != ''">#{status},</if>
             <if test="oaUserId != null">#{oaUserId},</if>
             <if test="oaOrderClass != null and oaOrderClass != ''">#{oaOrderClass},</if>
             <if test="createBy != null and createBy != ''">#{createBy},</if>
             <if test="remark != null and remark != ''">#{remark},</if>
             sysdate()
@@ -230,6 +233,7 @@
             <if test="password != null and password != ''">password = #{password},</if>
             <if test="status != null and status != ''">status = #{status},</if>
             <if test="oaUserId != null">oa_user_id = #{oaUserId},</if>
             <if test="oaOrderClass != null">oa_order_class = #{oaOrderClass},</if>
             <if test="loginIp != null and loginIp != ''">login_ip = #{loginIp},</if>
             <if test="loginDate != null">login_date = #{loginDate},</if>
             <if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
ruoyi-system/src/main/resources/mapper/system/UserSyncMapper.xml
@@ -12,6 +12,7 @@
        <result property="sex" column="sex" />
        <result property="email" column="email" />
        <result property="phonenumber" column="phonenumber" />
        <result property="oaOrderClass" column="OA_OrderClass" />
    </resultMap>
    <!-- 查询SQL Server中的OA用户列表 -->
@@ -24,7 +25,8 @@
            OA_departmentID AS department_id,
            OA_gender AS sex,
            OA_email AS email,
            OA_mobile AS phonenumber
            OA_mobile AS phonenumber,
            OA_OrderClass AS OA_OrderClass
        FROM OA_User
        WHERE OA_User IS NOT NULL 
          AND OA_Name IS NOT NULL
ruoyi-system/src/main/resources/mapper/system/VehicleInfoMapper.xml
@@ -21,6 +21,13 @@
        <result property="updateBy"       column="update_by"       />
        <result property="updateTime"     column="update_time"     />
        <result property="remark"         column="remark"          />
        <!-- 多个分公司关联 -->
        <collection property="deptIds" ofType="Long"
                    select="selectVehicleDeptIds"
                    column="vehicle_id"/>
        <collection property="deptNames" ofType="String"
                    select="selectVehicleDeptNames"
                    column="vehicle_id"/>
    </resultMap>
    <sql id="selectVehicleInfoVo">
@@ -28,6 +35,19 @@
        from tb_vehicle_info v
        left join sys_dept d on v.dept_id = d.dept_id
    </sql>
    <!-- 查询车辆关联的所有分公司ID -->
    <select id="selectVehicleDeptIds" resultType="Long">
        SELECT dept_id FROM tb_vehicle_dept WHERE vehicle_id = #{vehicle_id}
    </select>
    <!-- 查询车辆关联的所有分公司名称 -->
    <select id="selectVehicleDeptNames" resultType="String">
        SELECT d.dept_name
        FROM tb_vehicle_dept vd
        LEFT JOIN sys_dept d ON vd.dept_id = d.dept_id
        WHERE vd.vehicle_id = #{vehicle_id}
    </select>
    <select id="selectVehicleInfoList" parameterType="VehicleInfo" resultMap="VehicleInfoResult">
        <include refid="selectVehicleInfoVo"/>
@@ -39,23 +59,12 @@
            <if test="vehicleModel != null  and vehicleModel != ''"> and v.vehicle_model = #{vehicleModel}</if>
            <if test="status != null  and status != ''"> and v.status = #{status}</if>
            <if test="platformCode != null  and platformCode != ''"> and v.platform_code = #{platformCode}</if>
            <!-- 部门过滤:自动查找传入部门所属的分公司(parent_id=100) -->
            <!-- 部门过滤:根据分公司ID查询车辆(通过关联表) -->
            <if test="deptId != null">
                and v.dept_id = (
                    <!-- 如果传入的就是分公司(parent_id=100),直接返回 -->
                    select case
                        when exists(select 1 from sys_dept where dept_id = #{deptId} and parent_id = 100) then #{deptId}
                        else (
                            <!-- 否则从 ancestors 中查找分公司ID -->
                            select d.dept_id
                            from sys_dept d
                            where d.parent_id = 100
                            and FIND_IN_SET(d.dept_id, (
                                select ancestors from sys_dept where dept_id = #{deptId}
                            ))
                            limit 1
                        )
                    end
                and EXISTS (
                    SELECT 1 FROM tb_vehicle_dept vd
                    WHERE vd.vehicle_id = v.vehicle_id
                    AND vd.dept_id = #{deptId}
                )
            </if>
            <!-- 任务车辆选择必须过滤:只显示car_id和dept_id都不为空的车辆 -->
@@ -176,4 +185,22 @@
        ORDER BY uv.bind_time DESC
        LIMIT 1
    </select>
    <!-- 批量插入车辆-分公司关联 -->
    <insert id="batchInsertVehicleDept">
        INSERT INTO tb_vehicle_dept (vehicle_id, dept_id, order_class, create_by, create_time)
        VALUES
        <foreach collection="list" item="item" separator=",">
            (#{item.vehicleId}, #{item.deptId}, #{item.orderClass}, #{item.createBy}, NOW())
        </foreach>
        ON DUPLICATE KEY UPDATE
        order_class = VALUES(order_class),
        update_by = VALUES(create_by),
        update_time = NOW()
    </insert>
    <!-- 删除车辆的所有分公司关联 -->
    <delete id="deleteVehicleDeptByVehicleId">
        DELETE FROM tb_vehicle_dept WHERE vehicle_id = #{vehicleId}
    </delete>
</mapper> 
ruoyi-ui/src/api/system/dept.js
@@ -49,4 +49,20 @@
    url: '/system/dept/' + deptId,
    method: 'delete'
  })
}
}
// 按当前登录用户的 OA 权限返回分公司列表
export function listBranchByOa() {
  return request({
    url: '/system/dept/branch/by-oa',
    method: 'get'
  })
}
// 按外部传入的用户ID返回其可管理分公司列表
export function listBranchByUser(userId) {
  return request({
    url: '/system/dept/branch/by-user/' + userId,
    method: 'get'
  })
}
ruoyi-ui/src/views/system/user/index.vue
@@ -59,7 +59,7 @@
            <el-table v-loading="loading" :data="userList" @selection-change="handleSelectionChange">
              <el-table-column type="selection" width="50" align="center" />
              <el-table-column label="用户编号" align="center" key="userId" prop="userId" v-if="columns[0].visible" />
              <!-- OA用户ID列已隐藏,只在编辑表单中显示 -->
              <el-table-column label="用户名称" align="center" key="userName" prop="userName" v-if="columns[2].visible" width="150" />
              <el-table-column label="用户昵称" align="center" key="nickName" prop="nickName" v-if="columns[3].visible" :show-overflow-tooltip="true" />
@@ -75,7 +75,7 @@
                  <span>{{ parseTime(scope.row.createTime) }}</span>
                </template>
              </el-table-column>
              <el-table-column label="操作" align="center" width="160" class-name="small-padding fixed-width">
              <el-table-column label="操作" align="center" width="200" class-name="small-padding fixed-width">
                <template slot-scope="scope" v-if="scope.row.userId !== 1">
                  <el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:user:edit']">修改</el-button>
                  <el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)" v-hasPermi="['system:user:remove']">删除</el-button>
@@ -84,6 +84,8 @@
                    <el-dropdown-menu slot="dropdown">
                      <el-dropdown-item command="handleResetPwd" icon="el-icon-key" v-hasPermi="['system:user:resetPwd']">重置密码</el-dropdown-item>
                      <el-dropdown-item command="handleAuthRole" icon="el-icon-circle-check" v-hasPermi="['system:user:edit']">分配角色</el-dropdown-item>
                      <!-- 新增:管理分公司配置入口 -->
                      <el-dropdown-item command="handleManageBranch" icon="el-icon-office-building" v-hasPermi="['system:user:edit']">管理分公司</el-dropdown-item>
                    </el-dropdown-menu>
                  </el-dropdown>
                </template>
@@ -115,6 +117,16 @@
          <el-col :span="12">
            <el-form-item label="OA用户ID" prop="oaUserId">
              <el-input v-model="form.oaUserId" placeholder="OA系统的用户ID" type="number" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row>
          <el-col :span="24">
            <el-form-item label="可管理分公司">
              <div>
                <el-tag v-for="bc in userBranchCompanies" :key="bc.deptId" type="info" size="small" style="margin-right:6px;margin-bottom:6px">{{ bc.deptName }}</el-tag>
                <span v-if="userBranchCompanies.length === 0" style="color:#999">暂无分公司</span>
              </div>
            </el-form-item>
          </el-col>
        </el-row>
@@ -206,6 +218,21 @@
        <el-button @click="upload.open = false">取 消</el-button>
      </div>
    </el-dialog>
    <!-- 新增:用户管理分公司配置对话框 -->
    <el-dialog title="配置管理分公司" :visible.sync="branchDialog.open" width="500px" append-to-body>
      <el-form label-width="100px">
        <el-form-item label="可管理分公司">
          <div>
            <el-tag v-for="bc in branchDialog.companies" :key="bc.deptId" type="info" size="small" style="margin-right:6px;margin-bottom:6px">{{ bc.deptName }}</el-tag>
            <span v-if="branchDialog.companies.length === 0" style="color:#999">暂无分公司</span>
          </div>
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button @click="branchDialog.open = false">关 闭</el-button>
      </div>
    </el-dialog>
  </div>
</template>
@@ -216,6 +243,8 @@
import "@riophae/vue-treeselect/dist/vue-treeselect.css";
import { Splitpanes, Pane } from "splitpanes";
import "splitpanes/dist/splitpanes.css";
import request from "@/utils/request";
import { listDept, listBranchByUser } from "@/api/system/dept";
export default {
  name: "User",
@@ -308,7 +337,7 @@
        password: [
          { required: true, message: "用户密码不能为空", trigger: "blur" },
          { min: 5, max: 20, message: '用户密码长度必须介于 5 和 20 之间', trigger: 'blur' },
          { pattern: /^[^<>"'|\\]+$/, message: "不能包含非法字符:< > \" ' \\\ |", trigger: "blur" }
          { pattern: /^[^<>"'|\\]+$/, message: "不能包含非法字符:< > \" ' \\\\ |", trigger: "blur" }
        ],
        email: [
          {
@@ -324,7 +353,17 @@
            trigger: "blur"
          }
        ]
      }
      },
      // 新增:分公司配置对话框数据
      branchDialog: {
        open: false,
        userId: null,
        deptOptions: [],
        selectedDeptIds: [],
        companies: []
      },
      // 基于 oa_order_class 计算的分公司列表(只读展示)
      userBranchCompanies: []
    };
  },
  watch: {
@@ -404,6 +443,7 @@
        userName: undefined,
        nickName: undefined,
        oaUserId: undefined,
        oaOrderClass: undefined,
        password: undefined,
        phonenumber: undefined,
        email: undefined,
@@ -443,6 +483,9 @@
        case "handleAuthRole":
          this.handleAuthRole(row);
          break;
        case "handleManageBranch":
          this.handleManageBranch(row);
          break;
        default:
          break;
      }
@@ -471,8 +514,38 @@
        this.open = true;
        this.title = "修改用户";
        this.form.password = "";
        this.loadUserBranchCompanies();
      });
    },
    // 根据 form.oaOrderClass 计算分公司列表(service/dispatch 编码匹配)
    loadUserBranchCompanies() {
      this.userBranchCompanies = [];
      const codesStr = this.form.oaOrderClass || '';
      const codes = codesStr.split(',').map(s => s.trim()).filter(s => s);
      if (codes.length === 0) {
        return;
      }
      listDept({ parentId: 100 }).then(res => {
        const all = res.data || [];
        const codeSet = new Set(codes);
        const list = [];
        const seen = new Set();
        all.forEach(d => {
          const s = d.serviceOrderClass || '';
          const dis = d.dispatchOrderClass || '';
          if ((s && codeSet.has(s)) || (dis && codeSet.has(dis))) {
            if (!seen.has(d.deptId)) {
              seen.add(d.deptId);
              list.push({ deptId: d.deptId, deptName: d.deptName });
            }
          }
        });
        this.userBranchCompanies = list;
      }).catch(() => {
        this.userBranchCompanies = [];
      });
    },
    /** 重置密码按钮操作 */
    handleResetPwd(row) {
      this.$prompt('请输入"' + row.userName + '"的新密码', "提示", {
@@ -483,7 +556,7 @@
        inputErrorMessage: "用户密码长度必须介于 5 和 20 之间",
        inputValidator: (value) => {
          if (/<|>|"|'|\||\\/.test(value)) {
            return "不能包含非法字符:< > \" ' \\\ |"
            return "不能包含非法字符:< > \" ' \\\\ |"
          }
        },
      }).then(({ value }) => {
@@ -497,6 +570,26 @@
      const userId = row.userId;
      this.$router.push("/system/user-auth/role/" + userId);
    },
    /** 管理分公司配置入口 */
    handleManageBranch(row) {
      const userId = row.userId;
      this.branchDialog.userId = userId;
      // 加载分公司列表(OA自动控制,只读展示)
      listBranchByUser(userId).then(res => {
        const list = res.data || [];
        this.branchDialog.companies = (list || []).map(d => ({ deptId: d.deptId, deptName: d.deptName }));
        this.branchDialog.open = true;
      }).catch(() => {
        this.$modal.msgError('加载分公司配置失败');
      });
    },
    /** 保存分公司配置 */
    // 已取消保存,OA自动控制(仅只读展示)
    // submitBranchCompanies() {
    //   const userId = this.branchDialog.userId;
    //   const deptIds = this.branchDialog.selectedDeptIds || [];
    //   // 保留占位,避免误调用
    // },
    /** 提交按钮 */
    submitForm: function() {
      this.$refs["form"].validate(valid => {
@@ -561,4 +654,4 @@
    }
  }
};
</script>
</script>
ruoyi-ui/src/views/system/vehicle/index.vue
@@ -118,13 +118,19 @@
          <dict-tag :options="dict.type.sys_platform" :value="scope.row.platformCode"/>
        </template>
      </el-table-column>
      <el-table-column label="归属部门" align="center" prop="deptName" />
      <el-table-column label="归属分公司" align="center" prop="deptNames" width="200">
        <template slot-scope="scope">
          <span v-if="scope.row.deptNames && scope.row.deptNames.length > 0">
            {{ scope.row.deptNames.join('、') }}
          </span>
          <span v-else>-</span>
        </template>
      </el-table-column>
      <el-table-column label="状态" align="center" prop="status">
        <template slot-scope="scope">
          <dict-tag :options="dict.type.sys_normal_disable" :value="scope.row.status"/>
        </template>
      </el-table-column>
      <el-table-column label="归属部门" align="center" prop="deptName" />
      <el-table-column label="创建时间" align="center" prop="createTime" width="180">
        <template slot-scope="scope">
          <span>{{ parseTime(scope.row.createTime) }}</span>
@@ -190,16 +196,6 @@
            />
          </el-select>
        </el-form-item>
        <el-form-item label="归属部门" prop="deptId">
          <el-select v-model="form.deptId" placeholder="请选择部门" clearable>
            <el-option
              v-for="dept in deptOptions"
              :key="dept.deptId"
              :label="dept.deptName"
              :value="dept.deptId"
            />
          </el-select>
        </el-form-item>
        <el-form-item label="状态" prop="status">
          <el-radio-group v-model="form.status">
            <el-radio
@@ -209,8 +205,8 @@
            >{{dict.label}}</el-radio>
          </el-radio-group>
        </el-form-item>
        <el-form-item label="归属部门" prop="deptId">
          <el-select v-model="form.deptId" placeholder="请选择归属部门" clearable style="width: 100%">
        <el-form-item label="归属分公司" prop="deptIds">
          <el-select v-model="form.deptIds" placeholder="请选择归属分公司" multiple clearable style="width: 100%">
            <el-option
              v-for="dept in deptList"
              :key="dept.deptId"
@@ -282,7 +278,8 @@
        status: "0",
        remark: null,
        platformCode: null,
        deptId: null
        deptId: null,
        deptIds: []  // 多个分公司ID数组
      },
      // 表单校验
      rules: {
@@ -295,8 +292,8 @@
        platformCode: [
          { required: true, message: "平台标识不能为空", trigger: "change" }
        ],
        deptId: [
          { required: true, message: "归属部门不能为空", trigger: "change" }
        deptIds: [
          { required: true, message: "归属分公司不能为空", trigger: "change", type: 'array' }
        ]
      }
    };
@@ -342,7 +339,8 @@
        status: "0",
        remark: null,
        platformCode: null,
        deptId: null
        deptId: null,
        deptIds: []  // 重置为空数组
      };
      this.resetForm("form");
    },
@@ -374,6 +372,14 @@
      const vehicleId = row.vehicleId || this.ids
      getVehicle(vehicleId).then(response => {
        this.form = response.data;
        // 如果没有deptIds,则从 deptId 和 deptName 中预填
        if (!this.form.deptIds || this.form.deptIds.length === 0) {
          if (this.form.deptId) {
            this.form.deptIds = [this.form.deptId];
          } else {
            this.form.deptIds = [];
          }
        }
        this.open = true;
        this.title = "修改车辆信息";
      });
@@ -382,6 +388,11 @@
    submitForm() {
      this.$refs["form"].validate(valid => {
        if (valid) {
          // 如果选择了多个分公司,将第一个设置为deptId(主分公司)
          if (this.form.deptIds && this.form.deptIds.length > 0) {
            this.form.deptId = this.form.deptIds[0];
          }
          if (this.form.vehicleId != null) {
            updateVehicle(this.form).then(response => {
              this.$modal.msgSuccess("修改成功");
sql/vehicle_dept_relation.sql
New file
@@ -0,0 +1,17 @@
-- 车辆-分公司关联表
-- 用于支持一辆车归属多个分公司的多对多关系
CREATE TABLE IF NOT EXISTS `tb_vehicle_dept` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '关联ID',
  `vehicle_id` bigint(20) NOT NULL COMMENT '车辆ID',
  `dept_id` bigint(20) NOT NULL COMMENT '分公司ID(parent_id=100的部门)',
  `order_class` varchar(50) DEFAULT NULL COMMENT '对应的分公司编码(如HB,TI等)',
  `create_by` varchar(64) DEFAULT '' COMMENT '创建者',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  `update_by` varchar(64) DEFAULT '' COMMENT '更新者',
  `update_time` datetime DEFAULT NULL COMMENT '更新时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_vehicle_dept` (`vehicle_id`, `dept_id`),
  KEY `idx_vehicle_id` (`vehicle_id`),
  KEY `idx_dept_id` (`dept_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='车辆-分公司关联表';