zjk
2026-04-28 26e731a36ae5c70c40cb40f705b6f4fe1b34c812
fix: 修复巡游打卡规则、系统配置及活动管理的多处问题

- 修正路由标题为“新增巡游打卡规则”以明确功能范围
- 优化人员管理表格操作列宽度从250px调整为230px
- 修正定价管理详情页车型显示字段为vehicleModelName
- 修复打卡地址弹窗标题动态化,支持传入自定义标题
- 将巡游打卡详情现场照片展示从img标签替换为n-image-group组件
- 完善巡游打卡规则点位选择弹窗,支持编辑时数据回填
- 为巡游打卡地列表增加启用/禁用开关功能
- 修复巡游打卡规则新增页点位编辑功能,支持修改已添加点位
- 优化巡游打卡规则详情弹窗的布局和字段显示
- 调整活动管理新增页默认激活标签页为第一个
- 修复系统配置页面表单校验和输入处理逻辑
- 修正行程中修改目的地策略表单的字段绑定和单位显示
- 统一下单及点位吸附策略的表单布局和单位标签
13个文件已修改
386 ■■■■■ 已修改文件
src/router/routes.static.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/operations/attendance/address/components/AddressFormModal.vue 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/operations/attendance/address/index.vue 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/operations/cruiseCheckIn/index.vue 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/operations/cruiseCheckInDetails/detail.vue 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/operations/cruiseCheckInRule/add.vue 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/operations/cruiseCheckInRule/components/DetailModal.vue 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/operations/cruiseCheckInRule/components/SelectPointModal.vue 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/operations/personnel/personnelManagement/index.vue 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/system/config/index.vue 175 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/transaction/pricingManagement/components/TableModal.vue 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/transaction/pricingManagement/detail.vue 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/userContent/active/activityManagement/add.vue 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/router/routes.static.ts
@@ -1379,7 +1379,7 @@
  {
    name: 'addCruiseCheckInRule',
    path: '/operations/cruiseCheckInManagement/cruiseCheckInRule/add',
    title: '新增打卡规则',
    title: '新增巡游打卡规则',
    requiresAuth: true,
    icon: 'icon-park-outline:list-view',
    menuType: 'page',
src/views/operations/attendance/address/components/AddressFormModal.vue
@@ -8,6 +8,7 @@
const props = defineProps<{
  show: boolean
  mode?: 'add' | 'edit'
  title?: string
  type?: number
}>()
@@ -316,7 +317,7 @@
<template>
  <n-modal
    v-model:show="show" :mask-closable="false" preset="card" :title="mode === 'edit' ? '编辑打卡地址' : '新增打卡地址'"
    v-model:show="show" :mask-closable="false" preset="card" :title="mode === 'edit' ? `编辑${title}` : `新增${title}`"
    class="w-600px" :auto-focus="false" :segmented="{
      content: true,
      action: true,
src/views/operations/attendance/address/index.vue
@@ -312,7 +312,7 @@
        />
      </div>
      <AddressFormModal ref="modalRef" v-model:show="showFormModal" :mode="formMode" @confirm="handleConfirmForm" />
      <AddressFormModal ref="modalRef" v-model:show="showFormModal" title="打卡地" :mode="formMode" @confirm="handleConfirmForm" />
    </n-card>
  </div>
</template>
src/views/operations/cruiseCheckIn/index.vue
@@ -1,8 +1,8 @@
<script setup lang="tsx">
import type { DataTableColumns } from 'naive-ui'
import { useBoolean } from '@/hooks'
import { NButton, NTag } from 'naive-ui'
import { addCheckLocation, deleteCheckLocation, editCheckLocation, getCheckLocationList } from '@/service/api/attendance'
import { NButton, NSwitch, NTag } from 'naive-ui'
import { addCheckLocation, deleteCheckLocation, editCheckLocation, getCheckLocationList, updateCheckLocationStatus } from '@/service/api/attendance'
import SearchPanel from '@/components/custom/SearchPanel.vue'
import AddressFormModal from '@/views/operations/attendance/address/components/AddressFormModal.vue'
import dayjs from 'dayjs'
@@ -31,6 +31,28 @@
function handleSearch() {
  pageParams.value.pageNum = 1
  getList()
}
async function handleSwitchChange(row: any) {
  dialog.warning({
    title: `是否确认${row.status === 1 ? '禁用' : '启用'}打卡地?`,
    content: '',
    positiveText: '确认',
    negativeText: '取消',
    maskClosable: false,
    autoFocus: false,
    onPositiveClick: async () => {
      updateCheckLocationStatus({ id: row.id, status: row.status === 1 ? 0 : 1 }).then((res) => {
        if (res.code === 200) {
          window.$message.success('操作成功')
          getList()
        }
      })
    },
    onNegativeClick: () => {
      getList()
    },
  })
}
const tableScrollX = computed(() => Math.min(1600, 70 + 180 + 360 + 120 + 200))
@@ -89,7 +111,7 @@
  {
    title: '操作',
    key: 'actions',
    width: 70,
    width: 140,
    align: 'left',
    render: (row) => {
      return (
@@ -108,6 +130,18 @@
          >
            删除
          </span>
          <NSwitch
            v-permission="70214"
            value={row.status}
            checked-value={1}
            unchecked-value={0}
            onUpdateValue={() => handleSwitchChange(row)}
          >
            {{
              checked: () => '启用',
              unchecked: () => '禁用',
            }}
          </NSwitch>
        </div>
      )
    },
@@ -253,7 +287,7 @@
        />
      </div>
      <AddressFormModal ref="modalRef" v-model:show="showFormModal" :mode="formMode" :type="2" @confirm="handleConfirmForm" />
      <AddressFormModal ref="modalRef" v-model:show="showFormModal" title="巡游打卡地" :mode="formMode" :type="2" @confirm="handleConfirmForm" />
    </n-card>
  </div>
</template>
src/views/operations/cruiseCheckInDetails/detail.vue
@@ -154,10 +154,23 @@
            <div class="col-span-1 md:col-span-3 flex items-start">
              <span class="label">现场照片:</span>
              <div v-if="detailInfo?.scenePhoto" class="flex gap-16px flex-wrap">
                <img
                <!-- <img
                  v-for="(item, idx) in JSON.parse(detailInfo.scenePhoto)" :key="idx"
                  :src="item.url" class="w-182px h-120px" @error="(e: any) => { e.target.src = 'https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/9bed493b-a6aa-4ef6-ac10-7f67f5c6adc5.png' }"
                >
                > -->
                <n-image-group>
                  <div class="flex flex-wrap gap-2">
                    <n-image
                      v-for="item in JSON.parse(detailInfo.scenePhoto)"
                      :key="item.id || item.url"
                      :src="item.url"
                      width="182"
                      height="120"
                      class="rounded-lg"
                      fallback-src="https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/9bed493b-a6aa-4ef6-ac10-7f67f5c6adc5.png"
                    />
                  </div>
                </n-image-group>
              </div>
              <span v-else class="value">-</span>
            </div>
src/views/operations/cruiseCheckInRule/add.vue
@@ -47,6 +47,7 @@
}
const checkInPoints = ref<CheckInPoint[]>([])
const editingPointId = ref<number | null>(null)
const draggedPointId = ref<number | null>(null)
const executorOptions = ref<Array<{ label: string, value: number }>>([])
@@ -74,18 +75,17 @@
    title: '打卡地',
    key: 'name',
    ellipsis: true,
    width: 300,
  },
  {
    title: '打卡范围',
    key: 'range',
    width: 160,
    ellipsis: true,
  },
  {
    title: '打卡限时',
    key: 'timeLimit',
    width: 180,
    ellipsis: true,
  },
  {
    title: '操作',
@@ -165,6 +165,7 @@
}
function handleAddPoint() {
  editingPointId.value = null
  showSelectModal.value = true
}
@@ -174,23 +175,33 @@
    message.warning('打卡点数据异常,请重试')
    return
  }
  const existed = checkInPoints.value.some(item => item.pointId === pointId)
  const currentEditId = Number(pointData?.id ?? 0) || null
  const existed = checkInPoints.value.some(item => item.pointId === pointId && item.id !== currentEditId)
  if (existed) {
    message.warning('该打卡点已添加')
    return
  }
  checkInPoints.value.push({
  const nextPoint: CheckInPoint = {
    id: Number(pointData?.id ?? Date.now()),
    pointId,
    name: pointData?.name || '-',
    range: pointData?.range || '-',
    timeLimit: pointData?.timeLimit || '-',
  })
  }
  const editIndex = checkInPoints.value.findIndex(item => item.id === nextPoint.id)
  if (editIndex > -1)
    checkInPoints.value.splice(editIndex, 1, nextPoint)
  else
    checkInPoints.value.push(nextPoint)
  editingPointId.value = null
}
function handleEditPoint(point: CheckInPoint) {
  message.info(`点击了编辑点位:${point.name}`)
  editingPointId.value = point.id
  showSelectModal.value = true
}
function handleRemovePoint(point: CheckInPoint) {
@@ -302,7 +313,7 @@
            <n-form-item label="任务起止日期" path="taskDateRange" class="mb-0">
              <n-date-picker
                v-model:value="basicFormModel.taskDateRange" type="daterange" clearable class="flex-1"
                placeholder="请选择起止日期" format="yyyy/MM/dd" :actions="[]" :close-on-select="true"
                start-placeholder="开始时间" end-placeholder="结束时间" format="yyyy/MM/dd" :actions="[]" :close-on-select="true"
              />
            </n-form-item>
@@ -334,7 +345,7 @@
            打卡点位
          </div>
          <div class="flex gap-2">
            <n-button @click="handleResetPoints">
            <n-button class="w-58px" @click="handleResetPoints">
              重置
            </n-button>
            <n-button secondary type="primary" @click="handleAddPoint">
@@ -369,7 +380,11 @@
        </NButton>
      </NSpace>
    </div>
    <SelectPointModal v-model:show="showSelectModal" @confirm="handleConfirmSelect" />
    <SelectPointModal
      v-model:show="showSelectModal"
      :edit-point="checkInPoints.find(item => item.id === editingPointId) || null"
      @confirm="handleConfirmSelect"
    />
  </div>
</template>
src/views/operations/cruiseCheckInRule/components/DetailModal.vue
@@ -42,7 +42,7 @@
function formatDateRange(startTime?: string, endTime?: string) {
  const start = startTime ? dayjs(startTime).format('YYYY/MM/DD') : '-'
  const end = endTime ? dayjs(endTime).format('YYYY/MM/DD') : '-'
  return `${start} ~ ${end}`
  return `${start}~${end}`
}
function formatCycleMode(cyclicMode?: number) {
@@ -70,7 +70,7 @@
      detailInfo.value = {
        baseInfo: {
          ruleName: data.name || '-',
          associatedPerson: data.createBy || '-',
          associatedPerson: data.operationsUserName || '-',
          dateRange: formatDateRange(data.startTime, data.endTime),
          cycleMode: formatCycleMode(data.cyclicMode),
        },
@@ -116,32 +116,32 @@
          <div class="head mb-16px">
            基础信息
          </div>
          <div class="grid grid-cols-2 gap-y-16px gap-x-24px px-12px">
            <div class="flex">
              <span class="label-color flex-shrink-0 whitespace-nowrap">打卡规则名称:</span>
              <span class="value-color break-all">{{ detailInfo.baseInfo.ruleName }}</span>
          <div class="grid grid-cols-2 gap-y-16px gap-x-24px">
            <div class="flex flex-col">
              <span class="label-color flex-shrink-0 whitespace-nowrap">打卡规则名称</span>
              <span class="value-color break-all mt-4px">{{ detailInfo.baseInfo.ruleName }}</span>
            </div>
            <div class="flex">
              <span class="label-color flex-shrink-0 whitespace-nowrap">关联人员:</span>
              <span class="value-color break-all">{{ detailInfo.baseInfo.associatedPerson }}</span>
            <div class="flex flex-col">
              <span class="label-color flex-shrink-0 whitespace-nowrap">关联人员</span>
              <span class="value-color break-all mt-4px">{{ detailInfo.baseInfo.associatedPerson }}</span>
            </div>
            <div class="flex">
              <span class="label-color flex-shrink-0 whitespace-nowrap">任务起止日期:</span>
              <span class="value-color break-all">{{ detailInfo.baseInfo.dateRange }}</span>
            <div class="flex flex-col">
              <span class="label-color flex-shrink-0 whitespace-nowrap">任务起止日期</span>
              <span class="value-color break-all mt-4px">{{ detailInfo.baseInfo.dateRange }}</span>
            </div>
            <div class="flex">
              <span class="label-color flex-shrink-0 whitespace-nowrap">循环方式:</span>
              <span class="value-color break-all">{{ detailInfo.baseInfo.cycleMode }}</span>
            <div class="flex flex-col">
              <span class="label-color flex-shrink-0 whitespace-nowrap">循环方式</span>
              <span class="value-color break-all mt-4px">{{ detailInfo.baseInfo.cycleMode }}</span>
            </div>
          </div>
        </div>
        <!-- 打卡点位信息 -->
        <div>
          <div class="head mb-24px">
          <div class="head mb-16px">
            打卡点位信息
          </div>
          <div class="px-12px">
          <div>
            <NDataTable
              :columns="columns"
              :data="detailInfo.pointInfoList"
src/views/operations/cruiseCheckInRule/components/SelectPointModal.vue
@@ -5,6 +5,13 @@
const props = defineProps<{
  show: boolean
  editPoint?: {
    id: string
    pointId: number
    timeLimit: string
    name?: string
    range?: string
  } | null
}>()
const emit = defineEmits<{
@@ -118,6 +125,34 @@
  }
})
watch(show, async (visible) => {
  if (!visible)
    return
  // 每次打开都刷新选项,避免编辑时 value 已有但 options 尚未加载导致不回显
  await getPointOptions()
  // 编辑场景:打开弹窗时回填点位和时间段
  if (props.editPoint) {
    const editPointId = props.editPoint.pointId || null
    pointId.value = editPointId
    // 当前点位不在下拉选项中时,补一个临时选项用于回显名称
    if (editPointId && !pointOptions.value.some(item => item.value === editPointId)) {
      pointOptions.value.unshift({
        label: props.editPoint.name || `点位${editPointId}`,
        value: editPointId,
        range: String(props.editPoint.range || '-'),
      })
    }
    const timeRangeMatch = String(props.editPoint.timeLimit || '').match(/^(\d{2}:\d{2})\s*~\s*(\d{2}:\d{2})$/)
    startTime.value = timeRangeMatch?.[1] ?? null
    endTime.value = timeRangeMatch?.[2] ?? null
    return
  }
  // 新增场景:每次打开重置表单
  pointId.value = null
  startTime.value = null
  endTime.value = null
})
function handleConfirm() {
  if (!pointId.value) {
    window.$message.warning('请选择打卡点')
@@ -140,7 +175,7 @@
  }
  emit('confirm', {
    id: Date.now(), // Generate a temp ID for the row
    id: props.editPoint?.id,
    pointId: pointId.value,
    name: selectedPoint?.label,
    range: selectedPoint?.range,
src/views/operations/personnel/personnelManagement/index.vue
@@ -363,7 +363,7 @@
  {
    title: '操作',
    key: 'actions',
    width: 250,
    width: 230,
    fixed: 'right',
    align: 'left',
    render: row => (
src/views/system/config/index.vue
@@ -9,18 +9,20 @@
  minOrderDistance: any
  adsorptionDistance: any
  arriveTime: any
  xczSyDistance: number
  xczSyTime: number
  xczCaCount: number
  xczHmSpeedPerHour: number
  xczHmDuration: number
  headShowCount: number
  xczSyDistance: string
  xczSyTime: string
  xczCaCount: string
  xczHmSpeedPerHour: string
  xczHmDuration: string
  headShowCount: string
  nearPointDistance: any
  operateFree: boolean
  temperatureUnit: string
}
const formRef = ref<FormInst | null>(null)
const strategyFormRef = ref<FormInst | null>(null)
const orderStrategyFormRef = ref<FormInst | null>(null)
const formModel = ref<FormModel>({
  customerServicePhone: '',
@@ -28,12 +30,12 @@
  minOrderDistance: '',
  adsorptionDistance: '',
  arriveTime: '',
  xczSyDistance: 0,
  xczSyTime: 0,
  xczCaCount: 0,
  xczHmSpeedPerHour: 0,
  xczHmDuration: 0,
  headShowCount: 0,
  xczSyDistance: '',
  xczSyTime: '',
  xczCaCount: '',
  xczHmSpeedPerHour: '',
  xczHmDuration: '',
  headShowCount: '',
  nearPointDistance: '',
  operateFree: true,
  temperatureUnit: '℃',
@@ -41,6 +43,18 @@
const onlyAllowNumberAndDash = (value: string) => !value || /^[\d-]+$/.test(value)
const isWeatherUnitOpen = ref(false)
function handleHeadShowCountChange(value: string) {
  // 仅保留数字,并在输入阶段直接截断为最多3位,确保不超过999
  const normalizedValue = value.replace(/\D/g, '').slice(0, 3)
  formModel.value.headShowCount = normalizedValue
}
function handleNearPointDistanceChange(value: string) {
  // 仅保留数字,并在输入阶段直接截断为最多5位,确保不超过99999
  const normalizedValue = value.replace(/\D/g, '').slice(0, 5)
  formModel.value.nearPointDistance = normalizedValue
}
const rules: FormRules = {
  customerServicePhone: [
@@ -60,28 +74,26 @@
    { required: true, min: 1, message: '请输入正整数', trigger: 'blur' },
  ],
  xczSyDistance: [
    { required: true, type: 'number', min: 0, message: '请输入正整数', trigger: 'blur' },
    { required: true, message: '请输入正整数', trigger: 'blur' },
  ],
  xczSyTime: [
    { required: true, type: 'number', min: 0, message: '请输入正整数', trigger: 'blur' },
    { required: true, message: '请输入正整数', trigger: 'blur' },
  ],
  xczCaCount: [
    { required: true, type: 'number', min: 0, message: '请输入正整数', trigger: 'blur' },
    { required: true, message: '请输入正整数', trigger: 'blur' },
  ],
  xczHmSpeedPerHour: [
    { required: true, type: 'number', min: 0, message: '请输入正整数', trigger: 'blur' },
    { required: true, message: '请输入正整数', trigger: 'blur' },
  ],
  xczHmDuration: [
    { required: true, type: 'number', min: 0, message: '请输入正整数', trigger: 'blur' },
    { required: true, message: '请输入正整数', trigger: 'blur' },
  ],
  headShowCount: [
    { required: true, type: 'number', min: 0, message: '请输入正整数', trigger: 'blur' },
    { type: 'number', max: 999, message: '最大999', trigger: 'blur' },
    { required: true, message: '请输入正整数', trigger: 'blur' },
  ],
  // nearPointDistance: [
  //   { required: true, type: 'number', min: 0, message: '请输入正整数', trigger: 'blur' },
  //   { type: 'number', max: 99999, message: '最大99999m', trigger: 'blur' },
  // ],
  nearPointDistance: [
    { required: true, message: '请输入正整数', trigger: 'blur' },
  ],
  temperatureUnit: [
    { required: true, message: '请选择', trigger: 'change' },
  ],
@@ -91,7 +103,21 @@
async function handleSave() {
  try {
    await formRef.value?.validate()
    // 三块表单统一校验,任意一块不通过都不允许保存
    await Promise.all([
      formRef.value?.validate(),
      strategyFormRef.value?.validate(),
      orderStrategyFormRef.value?.validate(),
    ])
    // 这两个字段未单独挂载 path,补充保存前兜底校验
    if (!String(formModel.value.xczSyTime ?? '').trim()) {
      window.$message?.warning('请输入正整数')
      return
    }
    if (!String(formModel.value.xczHmDuration ?? '').trim()) {
      window.$message?.warning('请输入正整数')
      return
    }
    const base = originalSetting.value || {}
    const payload: any = {
      ...base,
@@ -131,19 +157,19 @@
      const d = res.data
      originalSetting.value = d
      formModel.value.customerServicePhone = d.customerServicePhone ?? ''
      formModel.value.orderDistance = Number(d.orderDistance ?? 0)
      formModel.value.minOrderDistance = Number(d.minOrderDistance ?? 0)
      formModel.value.adsorptionDistance = Number(d.adsorptionDistance ?? 0)
      formModel.value.arriveTime = Number(d.arriveTime ?? 0)
      formModel.value.headShowCount = Math.min(999, Number(d.headShowCount ?? 0))
      formModel.value.nearPointDistance = Math.min(99999, Number(d.nearPointDistance ?? 0))
      formModel.value.orderDistance = d.orderDistance ? d.orderDistance.toString() : ''
      formModel.value.minOrderDistance = d.minOrderDistance ? d.minOrderDistance.toString() : ''
      formModel.value.adsorptionDistance = d.adsorptionDistance ? d.adsorptionDistance.toString() : ''
      formModel.value.arriveTime = d.arriveTime ? d.arriveTime.toString() : ''
      formModel.value.headShowCount = d.headShowCount ? d.headShowCount.toString() : ''
      formModel.value.nearPointDistance = d.nearPointDistance ? d.nearPointDistance.toString() : ''
      formModel.value.operateFree = d.operateFree
      formModel.value.temperatureUnit = d.temperatureUnit || '℃'
      formModel.value.xczSyDistance = Number(d.xczSyDistance ?? 0)
      formModel.value.xczSyTime = Number(d.xczSyTime ?? 0)
      formModel.value.xczCaCount = Number(d.xczCaCount ?? 0)
      formModel.value.xczHmSpeedPerHour = Number(d.xczHmSpeedPerHour ?? 0)
      formModel.value.xczHmDuration = Number(d.xczHmDuration ?? 0)
      formModel.value.xczSyDistance = d.xczSyDistance ? d.xczSyDistance.toString() : ''
      formModel.value.xczSyTime = d.xczSyTime ? d.xczSyTime.toString() : ''
      formModel.value.xczCaCount = d.xczCaCount ? d.xczCaCount.toString() : ''
      formModel.value.xczHmSpeedPerHour = d.xczHmSpeedPerHour ? d.xczHmSpeedPerHour.toString() : ''
      formModel.value.xczHmDuration = d.xczHmDuration ? d.xczHmDuration.toString() : ''
    }
    else {
      window.$message?.warning('获取系统配置失败')
@@ -171,7 +197,7 @@
          ref="formRef" :model="formModel" :rules="rules" label-placement="left"
          require-mark-placement="left"
        >
          <div class="form-row">
          <div class="form-row gap-x-40px!">
            <n-form-item label="客服电话" path="customerServicePhone" required class="row-item">
              <n-input
                v-model:value="formModel.customerServicePhone" placeholder="请输入" style="width: 339px;"
@@ -179,9 +205,10 @@
              />
            </n-form-item>
            <n-form-item label="常见问题首页展示数" path="headShowCount" required class="row-item">
              <n-input-number
                v-model:value="formModel.headShowCount" placeholder="请输入" :min="0" :max="999"
                :show-button="false" style="width: 339px;"
              <n-input
                :value="formModel.headShowCount" placeholder="请输入"
                :allow-input="onlyAllowNumberAndDash" style="width: 339px;"
                @update:value="handleHeadShowCountChange"
              />
            </n-form-item>
            <n-form-item label="是否开启运营期免收费" path="operateFree" required class="row-item">
@@ -189,13 +216,14 @@
            </n-form-item>
          </div>
          <div class="form-row">
          <div class="form-row gap-x-20px!">
            <n-form-item label="小程序附近热门站点推荐距离" path="nearPointDistance" required class="row-item">
              <div class="inline-input">
                <n-input-group style="width: 222px;">
                  <n-input
                    v-model:value="formModel.nearPointDistance" placeholder="请输入"
                    :value="formModel.nearPointDistance" placeholder="请输入"
                    :allow-input="onlyAllowNumberAndDash"
                    @update:value="handleNearPointDistanceChange"
                  />
                  <n-input-group-label>m</n-input-group-label>
                </n-input-group>
@@ -207,7 +235,7 @@
                v-model:value="formModel.temperatureUnit"
                :options="[{ label: '℃', value: '℃' }, { label: '℉', value: '℉' }]"
                placeholder="请选择"
                style="width: 280px;"
                style="width: 362px;"
                @update:show="isWeatherUnitOpen = $event"
              >
                <template #arrow>
@@ -229,28 +257,23 @@
          行程中修改目的地策略
        </div>
        <n-form
          ref="strategyFormRef"
          :model="formModel" :rules="rules" label-placement="left"
          require-mark-placement="left"
        >
          <div class="form-row single-line-row">
            <n-form-item label="当前剩余里程<" path="xczSyDistance" required>
              <div class="inline-input">
                <n-input-number
                  v-model:value="formModel.xczSyDistance"
                  placeholder="请输入"
                  :min="0"
                  :precision="0"
                  :show-button="false"
                <n-input
                  v-model:value="formModel.xczSyDistance" placeholder="请输入"
                  style="width: 115px;"
                  :allow-input="onlyAllowNumberAndDash"
                />
                <span>米,或剩余时长&lt;</span>
                <n-input-number
                  v-model:value="formModel.xczSyTime"
                  placeholder="请输入"
                  :min="0"
                  :precision="0"
                  :show-button="false"
                <n-input
                  v-model:value="formModel.xczSyTime" placeholder="请输入"
                  style="width: 115px;"
                  :allow-input="onlyAllowNumberAndDash"
                />
                <span>分钟,禁止修改</span>
              </div>
@@ -260,13 +283,10 @@
          <div class="form-row single-line-row">
            <n-form-item label="单次行程最多允许修改" path="xczCaCount" required>
              <div class="inline-input">
                <n-input-number
                  v-model:value="formModel.xczCaCount"
                  placeholder="请输入"
                  :min="0"
                  :precision="0"
                  :show-button="false"
                <n-input
                  v-model:value="formModel.xczCaCount" placeholder="请输入"
                  style="width: 115px;"
                  :allow-input="onlyAllowNumberAndDash"
                />
                <span>次目的地,防恶意高频调用</span>
              </div>
@@ -274,25 +294,19 @@
          </div>
          <div class="form-row single-line-row">
            <n-form-item label="特殊豁免:" required label-placement="top">
            <n-form-item label="特殊豁免:" required path="xczHmSpeedPerHour" label-placement="top">
              <div class="inline-input break-line block!">
                <span>若系统检测到车辆在当前坐标时速&lt;</span>
                <n-input-number
                  v-model:value="formModel.xczHmSpeedPerHour"
                  placeholder="请输入"
                  :min="0"
                  :precision="0"
                  :show-button="false"
                <n-input
                  v-model:value="formModel.xczHmSpeedPerHour" placeholder="请输入"
                  style="width: 115px; display: inline-block;margin: 0 8px;"
                  :allow-input="onlyAllowNumberAndDash"
                />
                <span>km/h 且持续时长&gt;</span>
                <n-input-number
                  v-model:value="formModel.xczHmDuration"
                  placeholder="请输入"
                  :min="0"
                  :precision="0"
                  :show-button="false"
                <n-input
                  v-model:value="formModel.xczHmDuration" placeholder="请输入"
                  style="width: 115px; display: inline-block;margin: 0 8px;"
                  :allow-input="onlyAllowNumberAndDash"
                />
                <span>分钟<span class="inline-note">(判定为极度拥堵或异常滞留)</span>,此时解除限制,允许用户修改目的地新增途经点以寻找更近的下车位置</span>
              </div>
@@ -306,13 +320,14 @@
          下单及点位吸附策略
        </div>
        <n-form
          ref="orderStrategyFormRef"
          :model="formModel" :rules="rules" label-placement="left"
          require-mark-placement="left"
        >
          <div class="form-row single-line-row">
            <n-form-item label="距点位" path="orderDistance" required>
              <div class="inline-input">
                <n-input-group class="w-326px">
                <n-input-group class="w-352px">
                  <n-input
                    v-model:value="formModel.orderDistance" placeholder="请输入" :allow-input="onlyAllowNumberAndDash"
                  />
@@ -326,11 +341,11 @@
          <div class="form-row single-line-row">
            <n-form-item label="起终点之间" path="minOrderDistance" required>
              <div class="inline-input">
                <n-input-group>
                <n-input-group class="w-326px">
                  <n-input
                    v-model:value="formModel.minOrderDistance" placeholder="请输入" :allow-input="onlyAllowNumberAndDash"
                  />
                  <n-input-group-label>分钟</n-input-group-label>
                  <n-input-group-label>m</n-input-group-label>
                </n-input-group>
                <span class="inline-note">内不可下单</span>
              </div>
@@ -340,7 +355,7 @@
          <div class="form-row single-line-row">
            <n-form-item label="距点位" path="adsorptionDistance" required>
              <div class="inline-input">
                <n-input-group class="w-326px">
                <n-input-group class="w-352px">
                  <n-input
                    v-model:value="formModel.adsorptionDistance" placeholder="请输入" :allow-input="onlyAllowNumberAndDash"
                  />
@@ -354,13 +369,13 @@
          <div class="form-row single-line-row">
            <n-form-item label="到达目的地后" path="arriveTime" required>
              <div class="inline-input">
                <n-input-group class="w-326px">
                <n-input-group class="w-313px">
                  <n-input
                    v-model:value="formModel.arriveTime" placeholder="请输入" :allow-input="onlyAllowNumberAndDash"
                  />
                  <n-input-group-label>m</n-input-group-label>
                  <n-input-group-label>分钟</n-input-group-label>
                </n-input-group>
                <span class="inline-note">内结束行程</span>
                <span class="inline-note">自动结束行程</span>
              </div>
            </n-form-item>
          </div>
src/views/transaction/pricingManagement/components/TableModal.vue
@@ -237,7 +237,7 @@
  }
}
// 获取车辆型号列表(预留接口)
// 获取车辆型号列表
async function getVehicleModelList() {
  try {
    const { data } = await queryVehicleModelNoPageList() // TODO: 对接获取车辆型号接口
src/views/transaction/pricingManagement/detail.vue
@@ -503,7 +503,7 @@
                        车型:
                      </div>
                      <div class="value">
                        {{ detail?.vehicleModel || '-' }}
                        {{ detail?.vehicleModelName || '-' }}
                      </div>
                    </div>
                  </n-grid-item>
src/views/userContent/active/activityManagement/add.vue
@@ -132,7 +132,7 @@
  message.success(`成功导入了 ${newUsers.length} 名用户`)
}
const activeTab = ref(4)
const activeTab = ref(1)
const isEditMode = computed(() => route.query.mode === 'edit' && !!route.query.id)
interface UsagePeriodItem {
@@ -995,6 +995,12 @@
  userGroupList.value.splice(index, 1)
}
function closeTimePicker() {
  if (document.activeElement) {
    (document.activeElement as HTMLElement).blur()
  }
}
onMounted(() => {
  getDetail()
})
@@ -1032,7 +1038,7 @@
            <div class="flex items-center gap-12px">
              <NDatePicker
                v-model:value="formModel.timeRange" format="yyyy/MM/dd" type="daterange" clearable
                start-placeholder="请选择日期" end-placeholder="请选择日期" close-on-select :actions="null"
                start-placeholder="开始时间" end-placeholder="结束时间" close-on-select :actions="null"
                style="width: 400px;"
              />
            </div>
@@ -1085,7 +1091,7 @@
            class="mb-12px p-24px pb-12px border border-[#EEE9E9] last:mb-0 rounded-6px"
          >
            <NForm :model="strategy" :rules="strategyRules" label-placement="left" require-mark-placement="left">
              <div class="flex items-center justify-between mb-16px">
              <div class="flex items-center justify-between mb-8px">
                <!-- 策略名称 -->
                <NFormItem label="策略名称" path="name">
                  <NInput
@@ -1104,13 +1110,12 @@
                  </NButton>
                </div>
              </NFormItem>
              <div class="mt-4px min-h-[262px]">
              <div class="mt-4px">
                <NDataTable
                  :columns="createCouponColumns(strategyIndex, 'normal')"
                  :data="strategy.couponList"
                  :bordered="false"
                  style="min-height: 263px"
                  flex-height
                  :style="strategy.couponList.length === 0 ? { minHeight: '263px' } : {}"
                  :pagination="false"
                >
                  <template #empty>
@@ -1160,8 +1165,7 @@
                          :columns="createCouponColumns(strategyIndex, 'afterUse')"
                          :data="strategy.distributionRulesAfterUse.couponList"
                          :bordered="false"
                          style="min-height: 263px"
                          flex-height
                          :style="strategy.distributionRulesAfterUse.couponList.length === 0 ? { minHeight: '263px' } : {}"
                          :pagination="false"
                        >
                          <template #empty>
@@ -1244,12 +1248,20 @@
                  >
                    <NTimePicker
                      v-model:formatted-value="timeLimit.startTime" format="HH:mm" value-format="HH:mm"
                      placeholder="请选择开始时间" style="width: 240px"
                      placeholder="请选择开始时间" style="width: 240px" :actions="['now']" @update:value="
                        () => {
                          closeTimePicker();
                        }
                      "
                    />
                    <span class="text-[#171f26cc]">至</span>
                    <NTimePicker
                      v-model:formatted-value="timeLimit.endTime" format="HH:mm" value-format="HH:mm"
                      placeholder="请选择结束时间" style="width: 240px"
                      placeholder="请选择结束时间" style="width: 240px" :actions="['now']" @update:value="
                        () => {
                          closeTimePicker();
                        }
                      "
                    />
                    <img v-if="index > 0" :src="deleteIcon" alt="删除" style="width: 16px;height: 16px;cursor: pointer;" @click="handleRemoveTimeLimit(strategyIndex, index)">
                  </div>
@@ -1347,8 +1359,7 @@
                  :columns="createUserColumns(groupIndex)"
                  :data="group.userList"
                  :bordered="false"
                  flex-height
                  style="min-height: 263px"
                  :style="group.userList.length === 0 ? { minHeight: '263px' } : {}"
                  :pagination="false"
                >
                  <template #empty>