zjk
2026-06-02 b2b5dc5114dbeed1c45ffc6a1190166daf159f5d
Merge branch 'deploy-1.0' into feat/deploy-1.0
5个文件已修改
76 ■■■■■ 已修改文件
src/views/businessOperations/openCity/components/TableModal.vue 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/businessOperations/operationWorkbench/map.vue 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/transaction/directServiceManagement/add.vue 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/userContent/active/activityManagement/components/ActivityOrderList.vue 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/userContent/whitelistManagement/whitelist/components/WhitelistModal.vue 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/businessOperations/openCity/components/TableModal.vue
@@ -1,6 +1,6 @@
<script setup lang="tsx">
import { useBoolean } from '@/hooks'
import { addOpenCity, editOpenCity, getRegionCascader } from '@/service/api/operations'
import { addOpenCity, editOpenCity, getRegionCascader, getRegionCascaderNoArea } from '@/service/api/operations'
interface Props {
  modalName?: string
@@ -190,7 +190,7 @@
}
async function fetchCascaderData() {
  const res = await getRegionCascader()
  const res = await getRegionCascaderNoArea()
  if (res.code === 200 && res.data) {
    pcaOptions.value = formatCascaderData(res.data)
  }
@@ -198,22 +198,29 @@
</script>
<template>
  <n-modal v-model:show="modalVisible" :segmented="{
    content: true,
    action: true,
  }" :content-style="{
  <n-modal
    v-model:show="modalVisible" :segmented="{
      content: true,
      action: true,
    }" :content-style="{
      padding: '16px 24px 0px 24px',
    }" :mask-closable="false" preset="card" title="开通城市" class="w-400px table-modal">
    <n-form ref="formRef" :rules="rules" :model="formModel" :disabled="modalType === 'view'"
      require-mark-placement="left">
    }" :mask-closable="false" preset="card" title="开通城市" class="w-400px table-modal"
  >
    <n-form
      ref="formRef" :rules="rules" :model="formModel" :disabled="modalType === 'view'"
      require-mark-placement="left"
    >
      <n-form-item label="开通城市" path="pca">
        <n-cascader v-model:value="formModel.pca" :options="pcaOptions" value-field="code" label-field="name"
          check-strategy="all" @update:value="handlePcaChange" @update:show="isRoleSelectOpen = $event">
        <n-cascader
          v-model:value="formModel.pca" :options="pcaOptions" value-field="code" label-field="name"
          check-strategy="all" @update:value="handlePcaChange" @update:show="isRoleSelectOpen = $event"
        >
          <template #arrow>
            <img
              src="https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/e120f81c-9f15-4e1a-9938-22252898edf9.png"
              class="arrow-icon" :class="{ 'arrow-icon--open': isRoleSelectOpen }" style="width: 16px;height: 16px;"
              alt="arrow" @error="(e: any) => { e.target.src = 'https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/9bed493b-a6aa-4ef6-ac10-7f67f5c6adc5.png' }">
              alt="arrow" @error="(e: any) => { e.target.src = 'https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/9bed493b-a6aa-4ef6-ac10-7f67f5c6adc5.png' }"
            >
          </template>
        </n-cascader>
      </n-form-item>
src/views/businessOperations/operationWorkbench/map.vue
@@ -1215,6 +1215,8 @@
  () => !fenceCreateMode.value && Number(fenceDetailData.value?.status) === 2
);
// 围栏保存前的统一阻断原因。
// 这里不直接执行 message,而是返回首个阻断文案,供按钮禁用态和保存提示复用同一套校验结果。
const fenceSaveBlockReason = computed(() => {
  if (!isFenceFormMode.value) return "";
  if (isLockedFenceStatusEdit.value) return "";
@@ -1239,6 +1241,8 @@
    if (Number(strategy.type) === 3 && !strategy.typeStr.length)
      return `请为 ${strategy.name || "策略组"} 选择生效星期`;
    let typeKey = "";
    // 非自定义策略按固定类型去重;自定义策略按“星期组合”去重。
    // 这样既能防止出现两个“工作日”策略,也允许不同星期组合并存。
    if (Number(strategy.type) === 0) typeKey = "workday";
    else if (Number(strategy.type) === 1) typeKey = "statutoryHoliday";
    else if (Number(strategy.type) === 2) typeKey = "holiday";
@@ -1249,6 +1253,7 @@
    if (typeKey && strategyTypeKeys.has(typeKey))
      return `运营策略存在重复:${strategy.name || "策略组"} 的类型已存在`;
    if (typeKey) strategyTypeKeys.add(typeKey);
    // “每天”覆盖全部日期范围,因此不允许再和其他策略组并存。
    if (typeKey.includes("custom-0") && fenceStrategyList.value.length > 1)
      return "选择“每天”时,不能添加其他运营策略";
    if (!strategy.time.length)
@@ -1280,6 +1285,7 @@
  }
  for (const area of fenceRestrictedAreas.value) {
    if (!area.areaName.trim()) return "请填写禁行区名称";
    // 禁行区只要开始绘制,就必须形成完整 polygon,避免保存半成品区域。
    if (area.points.length > 0 && area.points.length < 3)
      return `${area.areaName || "禁行区"} 还未完成绘制`;
    if (!area.points.length) return `${area.areaName || "禁行区"} 还未完成绘制`;
@@ -1287,6 +1293,8 @@
  return "";
});
// 场站在“自定义时间”模式下的时间段校验。
// 这里和围栏策略校验拆开,便于场站表单保存、联动校验及错误提示单独复用。
function getStationCustomTimeErrorMessage() {
  if (stationForm.value.operationType !== "2") return "";
  const ranges = Array.isArray(stationForm.value.customTimeRanges)
@@ -1315,6 +1323,7 @@
  return "";
}
// 编辑态下通过快照判断场站表单是否被修改,用于离开编辑态时提示是否放弃更改。
const stationEditPayloadSnapshot = computed(() => {
  return JSON.stringify({
    stationName: stationForm.value.stationName.trim(),
@@ -1333,6 +1342,8 @@
  });
});
// 场站保存前的统一阻断原因。
// 与围栏类似,返回首个错误文案,供按钮禁用态、保存前提示、局部联动共用。
const stationSaveBlockReason = computed(() => {
  if (!isStationFormMode.value) return "";
  if (!stationForm.value.stationName.trim()) return "请输入场站名称";
@@ -1341,6 +1352,8 @@
    allowOutsideOperationArea: isStationEditWorkflow.value,
  });
  if (geometryError) return geometryError;
  // 编辑已有场站时允许它暂时落在围栏外,方便修复历史数据或承接围栏变更后的残留场站;
  // 但只要当前没有匹配到任何运营区,就不能把状态切回“启用”。
  if (
    isStationEditWorkflow.value &&
    !resolveStationOperationAreaByPolygon(stationDraftPoints.value) &&
@@ -1596,6 +1609,9 @@
    .filter((item) => item.points.length >= 3);
}
// 根据整个场站 polygon 反查所属运营区。
// 这里不使用中心点命中,而是要求“场站整体完全落在某个运营区内”,
// 以便和保存校验、围栏解绑逻辑保持一致。
function resolveStationOperationAreaByPolygon(points: LngLatTuple[]) {
  if (points.length < 3) return null;
  return (
@@ -1604,6 +1620,11 @@
  );
}
// 统一处理场站几何合法性校验:
// 1. 必须构成合法 polygon
// 2. 新建时必须在运营区内,编辑时允许临时落在围栏外
// 3. 不允许与其他场站重叠/包含
// 4. 不允许与启用中的禁行区重叠
function getStationGeometryInvalidReason(
  points: LngLatTuple[],
  options: {
@@ -1618,6 +1639,7 @@
  const otherStations = getOtherStations();
  const geometryUtil = (window as any).AMap?.GeometryUtil;
  if (geometryUtil) {
    // 先拦截相交,再拦截包含/被包含,提示更符合用户的实际绘制问题。
    const crossStation = otherStations.find((station) =>
      geometryUtil.doesRingRingIntersect(points, station.points)
    );
@@ -1641,6 +1663,8 @@
  return "";
}
// 地图交互层的场站几何校验封装。
// 与纯函数校验分开,专门负责在用户绘制/拖拽时弹出 warning。
function validateStationGeometry(points: LngLatTuple[]) {
  const errorMessage = getStationGeometryInvalidReason(points, {
    allowOutsideOperationArea: isStationEditWorkflow.value,
@@ -1652,6 +1676,8 @@
  return true;
}
// 按场站 polygon 自动同步框选到的点位列表。
// 保留已匹配点位的原有顺序,只把新增命中的点位追加到末尾,避免用户编辑时列表频繁跳动。
function syncStationPointListByPolygon(path: LngLatTuple[]) {
  const nextMatchedList = getPointItemsWithinPolygon(path).map(
    mapListItemToStationPoint
@@ -1709,6 +1735,8 @@
  });
}
// 基于当前草稿点位重建地图上的场站 polygon。
// 只负责地图图形本身,不负责同步表单/点位列表,避免图形重建时产生额外副作用。
function createStationDraftPolygon(points: LngLatTuple[]) {
  if (!map.value || points.length < 3) return;
  if (stationDraftPolygon.value) map.value.remove(stationDraftPolygon.value);
@@ -1730,6 +1758,8 @@
  map.value.add(stationDraftPolygon.value);
}
// 首次应用一组完整的场站草稿几何信息。
// 用于详情回显、新建绘制完成后落稿,同时按最新 polygon 自动同步场站内点位。
function applyStationDraftGeometry(points: LngLatTuple[], fitView = false) {
  stationDraftPoints.value = clonePolygonPath(points);
  stationForm.value.polygonPath = clonePolygonPath(points);
@@ -1739,12 +1769,16 @@
    map.value.setFitView([stationDraftPolygon.value], false, [100, 100, 100, 100]);
}
// 编辑器拖拽/加点/删点过程中的轻量同步。
// 这里只更新内存态,不重建 polygon,避免编辑过程出现闪烁。
function syncStationDraftGeometryState(points: LngLatTuple[]) {
  stationDraftPoints.value = clonePolygonPath(points);
  stationForm.value.polygonPath = clonePolygonPath(points);
  syncStationPointListByPolygon(points);
}
// 当编辑后的几何不合法时,回滚到上一次合法草稿,并重新挂回编辑器。
// 这里通过 rollbacking 标记和 suppress 标记避免 close/open 编辑器时触发重复 end 事件。
async function rollbackStationDraftGeometry() {
  if (!map.value || stationDraftPoints.value.length < 3 || stationEditorRollbacking)
    return;
@@ -1763,6 +1797,8 @@
  enableStationDraftEdit();
}
// 打开场站 polygon 的可编辑态,并把 AMap 编辑事件同步回表单草稿。
// adjust/addnode/removenode 过程中先实时校验,若非法则立即回滚,确保草稿始终保持可保存状态。
function enableStationDraftEdit() {
  if (!map.value || !stationDraftPolygon.value || stationEditorRollbacking) return;
  if (stationPolyEditor.value) suppressStationEditorEnd.value = true;
@@ -1790,6 +1826,7 @@
      editOptions: { fillColor: "#A034FF" },
    }
  );
  // 统一从 polygon 实例回读最新 path,避免不同编辑事件各自维护一套取值逻辑。
  const syncStationGeometry = () => {
    if (!stationDraftPolygon.value || stationEditorRollbacking) return;
    const points = stationDraftPolygon.value
@@ -1801,6 +1838,7 @@
    }
    syncStationDraftGeometryState(points);
  };
  // 主动 close 编辑器时会触发 end,这里用 suppress 标记过滤掉非用户操作的结束事件。
  const handleStationGeometryEditEnd = () => {
    if (suppressStationEditorEnd.value) {
      suppressStationEditorEnd.value = false;
src/views/transaction/directServiceManagement/add.vue
@@ -3,7 +3,7 @@
import { computed, onMounted, ref } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { NButton, NCascader, NCheckbox, NDataTable, NDatePicker, NForm, NGrid, NInput, NSpace, NTag, useDialog, useMessage } from 'naive-ui'
import { getRegionCascader } from '@/service/api/operations'
import { getRegionCascader, getRegionCascaderNoArea } from '@/service/api/operations'
import { addDirectService, editDirectService, getDirectServiceDetail, getTargetedVehiclePageList } from '@/service/api/directService'
import { useTabStore } from '@/store'
import { useBoolean } from '@/hooks'
@@ -128,7 +128,7 @@
}
async function fetchCascaderData() {
  const res = await getRegionCascader()
  const res = await getRegionCascaderNoArea()
  if (res.code === 200 && res.data) {
    pcaOptions.value = formatCascaderData(res.data)
  }
src/views/userContent/active/activityManagement/components/ActivityOrderList.vue
@@ -451,6 +451,7 @@
  let params: any = {}
  if (checkedRowKeys.value.length > 0) {
    params.ids = checkedRowKeys.value
    params.activityId = props.activityId
  }
  else {
    params = {
src/views/userContent/whitelistManagement/whitelist/components/WhitelistModal.vue
@@ -269,7 +269,7 @@
      <!-- 编辑模式:单行输入 -->
      <NFormItem v-if="mode === 'edit'" label="邀请手机号" path="account">
        <NInput v-model:value="formModel.account" placeholder="请输入手机号" />
        <NInput v-model:value="formModel.account" disabled placeholder="请输入手机号" />
      </NFormItem>
      <!-- 编辑模式:用户名称 -->