董国庆
2026-06-20 59ac2890e021dd4b91565cce24c8866e287b223f
修改:场站在编辑时-所属围栏停用,操作启用禁用switch给出提示语
5个文件已修改
197 ■■■■■ 已修改文件
src/service/api/testOperationWorkbench.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/businessOperations/operationWorkbench/map.vue 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/businessOperations/operationWorkbench/workbench.payload.ts 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/testDemo/operationWorkbench/map.vue 126 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/testDemo/operationWorkbench/workbench.payload.ts 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/service/api/testOperationWorkbench.ts
@@ -84,7 +84,7 @@
    type: number
    name: string
    ids: number | string
  }>>>('/map/api/v1/electronicFence/findListFenceAndStationDemo', { params })
  }>>>('/map/api/v1/electronicFence/findListFenceAndStation', { params })
}
export function exportPointExcel(data: any) {
src/views/businessOperations/operationWorkbench/map.vue
@@ -3475,6 +3475,21 @@
  resolveStationOperationFenceId(stationOperationArea.value),
)
// 场站当前若匹配不到运营区,保存时优先沿用详情接口里原有的围栏归属字段,
// 避免仅因地图默认隐藏停用围栏,就把 electronicAreaId / electronicId 意外清空。
function getStationOperationAreaFieldFallback() {
  const source = stationDetailData.value || selectedItem.value?.raw || null
  return {
    electronicAreaId:
      source?.electronicAreaId ?? source?.raw?.electronicAreaId ?? null,
    electronicId:
      source?.electronicId
      ?? source?.raw?.electronicId
      ?? resolveStationOperationFenceId(source)
      ?? null,
  }
}
async function ensureStationOperationLifecycleFallback() {
  if (activeTab.value !== 'station') {
    stationOperationLifecycleFallback.value = null
@@ -4092,6 +4107,7 @@
      : undefined
    const currentSelectedStation = selectedItem.value
    const operationArea = resolveStationOperationAreaByPolygon(polygonPath)
    const operationAreaFieldFallback = getStationOperationAreaFieldFallback()
    const stationArea = Number(calcFenceArea(polygonPath).toFixed(2))
    const detail = {
      id:
@@ -4121,6 +4137,8 @@
      areaStatus,
      pointStop,
      operationArea,
      fallbackElectronicAreaId: operationAreaFieldFallback.electronicAreaId,
      fallbackElectronicId: operationAreaFieldFallback.electronicId,
      polygonPath,
      operationType: stationForm.value.operationType,
      parkingTimeLimit: stationForm.value.parkingTimeLimit,
@@ -4202,9 +4220,18 @@
watch(
  [isStationEditWorkflow, stationOperationArea, () => stationForm.value.areaStatus],
  ([editing, operationArea, areaStatus]) => {
  ([editing, operationArea, areaStatus], previousValue) => {
    if (!editing || operationArea || Number(areaStatus) !== 1)
      return
    const [previousEditing, , previousAreaStatus] = Array.isArray(previousValue)
      ? previousValue
      : [false, null, undefined]
    // 仅在用户主动把开关切到“启用”时提示原因;如果是进入编辑态或几何联动回退,则静默兜底。
    if (previousEditing && Number(previousAreaStatus) !== 1) {
      window.$message?.warning(
        '当前场站所属运营围栏已停用或不可用,无法匹配运营区域,不允许启用',
      )
    }
    stationForm.value.areaStatus = 2
  },
)
src/views/businessOperations/operationWorkbench/workbench.payload.ts
@@ -302,6 +302,8 @@
  areaStatus: number;
  pointStop?: boolean;
  operationArea: any;
  fallbackElectronicAreaId?: number | string | null;
  fallbackElectronicId?: number | string | null;
  polygonPath: LngLatTuple[];
  operationType: "1" | "2";
  parkingTimeLimit: string;
@@ -320,6 +322,8 @@
    areaStatus,
    pointStop,
    operationArea,
    fallbackElectronicAreaId,
    fallbackElectronicId,
    polygonPath,
    operationType,
    parkingTimeLimit,
@@ -337,7 +341,15 @@
  );
  // 场站保存状态码。
  const statusCode = getStationStatusCode(saveType, areaStatus);
  const shouldClearOperationArea = !isCreatingStation && !operationArea;
  const resolvedElectronicAreaId
    = operationArea?.id ?? fallbackElectronicAreaId ?? null;
  const resolvedElectronicId
    = operationArea?.electronicId ?? fallbackElectronicId ?? null;
  const shouldClearOperationArea
    = !isCreatingStation
      && !operationArea
      && resolvedElectronicAreaId === null
      && resolvedElectronicId === null;
  return omitUndefinedFields({
    ...(isCreatingStation ? {} : { id: Number(detailId) }),
@@ -346,15 +358,14 @@
    effectivePeriod: 1,
    effectiveTimePeriod: effectiveTimePeriod || undefined,
    electronicAreaId:
      operationArea?.id !== undefined && operationArea?.id !== null
        ? Number(operationArea.id)
      resolvedElectronicAreaId !== undefined && resolvedElectronicAreaId !== null
        ? Number(resolvedElectronicAreaId)
        : shouldClearOperationArea
        ? null
        : undefined,
    electronicId:
      operationArea?.electronicId !== undefined &&
      operationArea?.electronicId !== null
        ? Number(operationArea.electronicId)
      resolvedElectronicId !== undefined && resolvedElectronicId !== null
        ? Number(resolvedElectronicId)
        : shouldClearOperationArea
        ? null
        : undefined,
src/views/testDemo/operationWorkbench/map.vue
@@ -1844,9 +1844,20 @@
    return '请输入场站名称'
  if (stationDraftPoints.value.length < 3)
    return '请先在运营区内绘制场站'
  const geometryError = getStationGeometryInvalidReason(stationDraftPoints.value)
  const geometryError = getStationGeometryInvalidReason(stationDraftPoints.value, {
    allowOutsideOperationArea: isStationEditWorkflow.value,
  })
  if (geometryError)
    return geometryError
  // 编辑已有场站时允许它暂时落在围栏外,方便修复历史数据或承接围栏变更后的残留场站;
  // 但只要当前没有匹配到任何运营区,就不能把状态切回“启用”。
  if (
    isStationEditWorkflow.value
    && !resolveStationOperationAreaByPolygon(stationDraftPoints.value)
    && Number(stationForm.value.areaStatus || 1) === 1
  ) {
    return '当前场站不在围栏区域内,不允许启用'
  }
  if (!stationForm.value.parkingTimeLimit)
    return '请输入限停时长'
  return getStationCustomTimeErrorMessage()
@@ -2042,6 +2053,18 @@
    .filter(item => item.points.length >= 3)
}
// 根据整个场站 polygon 反查所属运营区。
// 这里不使用中心点命中,而是要求“场站整体完全落在某个运营区内”,
// 以便和保存校验、围栏解绑逻辑保持一致。
function resolveStationOperationAreaByPolygon(points: LngLatTuple[]) {
  if (points.length < 3)
    return null
  return (
    getStationOperationAreas().find(area => isRingInRing(points, area.points))
    || null
  )
}
function getStationRestrictedAreas() {
  return fenceAreaData.value
    .filter((item: any) => Number(item.areaType) === 2)
@@ -2071,16 +2094,18 @@
    .filter(item => item.points.length >= 3)
}
function getStationGeometryInvalidReason(points: LngLatTuple[]) {
function getStationGeometryInvalidReason(
  points: LngLatTuple[],
  options: {
    allowOutsideOperationArea?: boolean
  } = {},
) {
  if (points.length < 3)
    return '请绘制至少3个点的场站'
  if (isFencePolygonSelfIntersect(points))
    return '场站区域不允许自相交'
  const operationAreas = getStationOperationAreas()
  const isInsideOperationArea = operationAreas.some(area =>
    isRingInRing(points, area.points),
  )
  if (!isInsideOperationArea)
  const matchedOperationArea = resolveStationOperationAreaByPolygon(points)
  if (!matchedOperationArea && !options.allowOutsideOperationArea)
    return '场站需完全处于运营区内'
  const otherStations = getOtherStations()
  const geometryUtil = (window as any).AMap?.GeometryUtil
@@ -3483,9 +3508,55 @@
const stationLifecycleCache = ref<Record<string, Record<string, any> | null>>({})
const stationOperationLifecycleFallback = ref<Record<string, any> | null>(null)
const stationOperationArea = computed(() => resolveStationOperationArea())
function resolveStationOperationFenceId(source?: Record<string, any> | null) {
  if (!source)
    return ''
  const fenceId
    = source.electronicId
      ?? source.electronicFenceId
      ?? source.fenceId
      ?? source.raw?.electronicId
      ?? source.raw?.electronicFenceId
      ?? source.raw?.fenceId
  return fenceId !== undefined && fenceId !== null ? String(fenceId) : ''
}
// testDemo 场站启用限制需要直接读取所属围栏状态,
// 避免只在保存时才因为“找不到运营区”间接报错,导致交互反馈滞后。
function isStationParentFenceDisabled(source?: Record<string, any> | null) {
  const fenceId = resolveStationOperationFenceId(source)
  if (!fenceId)
    return false
  const matchedFence = fenceData.value.find(
    item => String(item.id || '') === fenceId,
  )
  if (!matchedFence)
    return false
  const rawStatus = matchedFence.raw?.status
  if (rawStatus !== undefined && rawStatus !== null)
    return Number(rawStatus) === 3
  return String(matchedFence.status || '').trim() === '已停用'
}
const stationOperationFenceId = computed(() =>
  String(stationOperationArea.value?.electronicId || ''),
  resolveStationOperationFenceId(stationOperationArea.value),
)
// 场站当前若匹配不到运营区,保存时优先沿用详情接口里原有的围栏归属字段,
// 避免仅因地图默认隐藏停用围栏,就把 electronicAreaId / electronicId 意外清空。
function getStationOperationAreaFieldFallback() {
  const source = stationDetailData.value || selectedItem.value?.raw || null
  return {
    electronicAreaId:
      source?.electronicAreaId ?? source?.raw?.electronicAreaId ?? null,
    electronicId:
      source?.electronicId
      ?? source?.raw?.electronicId
      ?? resolveStationOperationFenceId(source)
      ?? null,
  }
}
async function ensureStationOperationLifecycleFallback() {
  if (activeTab.value !== 'station') {
@@ -3995,14 +4066,8 @@
      ? Number(polygonCenter?.[1])
      : undefined
    const currentSelectedStation = selectedItem.value
    const operationArea
      = polygonCenter && fenceAreaData.value.length
        ? resolveOperationArea(
            fenceAreaData.value,
            Number(polygonCenter[0]),
            Number(polygonCenter[1]),
          )
        : null
    const operationArea = resolveStationOperationAreaByPolygon(polygonPath)
    const operationAreaFieldFallback = getStationOperationAreaFieldFallback()
    const stationArea = Number(calcFenceArea(polygonPath).toFixed(2))
    const detail = {
      id:
@@ -4031,6 +4096,8 @@
      saveType,
      areaStatus,
      operationArea,
      fallbackElectronicAreaId: operationAreaFieldFallback.electronicAreaId,
      fallbackElectronicId: operationAreaFieldFallback.electronicId,
      polygonPath,
      operationType: stationForm.value.operationType,
      parkingTimeLimit: stationForm.value.parkingTimeLimit,
@@ -4367,6 +4434,33 @@
)
watch(
  [isStationEditWorkflow, stationOperationArea, () => stationForm.value.areaStatus],
  ([editing, operationArea, areaStatus], previousValue) => {
    if (!editing || Number(areaStatus) !== 1)
      return
    const [previousEditing, , previousAreaStatus] = Array.isArray(previousValue)
      ? previousValue
      : [false, null, undefined]
    const enableTriggeredByUser
      = previousEditing && Number(previousAreaStatus) !== 1
    const parentFenceDisabled = isStationParentFenceDisabled(
      stationDetailData.value || selectedItem.value?.raw || null,
    )
    if (!parentFenceDisabled && operationArea)
      return
    // 仅在用户主动把开关切到“启用”时提示原因;如果是进入编辑态或几何联动回退,则静默兜底。
    if (enableTriggeredByUser) {
      window.$message?.warning(
        parentFenceDisabled
          ? '当前场站所属围栏已停用,不允许启用'
          : '当前场站所属运营围栏已停用或不可用,无法匹配运营区域,不允许启用',
      )
    }
    stationForm.value.areaStatus = 2
  },
)
watch(
  () => [route.name, route.fullPath],
  async () => {
    if (route.name !== 'testDemoOperationWorkbenchMap')
src/views/testDemo/operationWorkbench/workbench.payload.ts
@@ -293,6 +293,8 @@
  saveType: 0 | 1;
  areaStatus: number;
  operationArea: any;
  fallbackElectronicAreaId?: number | string | null;
  fallbackElectronicId?: number | string | null;
  polygonPath: LngLatTuple[];
  operationType: "1" | "2";
  parkingTimeLimit: string;
@@ -310,6 +312,8 @@
    saveType,
    areaStatus,
    operationArea,
    fallbackElectronicAreaId,
    fallbackElectronicId,
    polygonPath,
    operationType,
    parkingTimeLimit,
@@ -327,6 +331,10 @@
  );
  // 场站保存状态码。
  const statusCode = getStationStatusCode(saveType, areaStatus);
  const resolvedElectronicAreaId
    = operationArea?.id ?? fallbackElectronicAreaId ?? null;
  const resolvedElectronicId
    = operationArea?.electronicId ?? fallbackElectronicId ?? null;
  return omitUndefinedFields({
    ...(isCreatingStation ? {} : { id: Number(detailId) }),
@@ -335,13 +343,12 @@
    effectivePeriod: 1,
    effectiveTimePeriod: effectiveTimePeriod || undefined,
    electronicAreaId:
      operationArea?.id !== undefined && operationArea?.id !== null
        ? Number(operationArea.id)
      resolvedElectronicAreaId !== undefined && resolvedElectronicAreaId !== null
        ? Number(resolvedElectronicAreaId)
        : undefined,
    electronicId:
      operationArea?.electronicId !== undefined &&
      operationArea?.electronicId !== null
        ? Number(operationArea.electronicId)
      resolvedElectronicId !== undefined && resolvedElectronicId !== null
        ? Number(resolvedElectronicId)
        : undefined,
    fencePoints: stringifyFencePoints(polygonPath) || undefined,
    operationType: Number(operationType || 1),