ai-coding
2026-07-23 cc2172689d8a339139a99506284a3f72216c420e
feat: 更新路径管理列表页和菜单顺序

Co-Authored-By: 祝美莲 <zhuml4@xiaopeng.com>
2个文件已修改
165 ■■■■■ 已修改文件
src/router/routes.static.ts 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/businessOperations/pathManagement/index.vue 141 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/router/routes.static.ts
@@ -637,6 +637,18 @@
    activeMenu: '/businessOperations/operationWorkbench/map',
  },
  {
    name: 'pathManagement',
    path: '/businessOperations/pathManagement',
    title: '路径管理',
    requiresAuth: true,
    icon: 'icon-park-outline:guide-board',
    menuType: 'page',
    componentPath: '/businessOperations/pathManagement/index.vue',
    id: 2026072301,
    keepAlive: true,
    pid: 4,
  },
  {
    name: 'siteInfoList',
    path: '/businessOperations/siteInfoList',
    title: '点位现场信息',
@@ -660,18 +672,6 @@
    keepAlive: false,
    hide: true,
    activeMenu: '/businessOperations/siteInfoList',
    pid: 4,
  },
  {
    name: 'pathManagement',
    path: '/businessOperations/pathManagement',
    title: '路径管理',
    requiresAuth: true,
    icon: 'icon-park-outline:guide-board',
    menuType: 'page',
    componentPath: '/businessOperations/pathManagement/index.vue',
    id: 2026072301,
    keepAlive: true,
    pid: 4,
  },
  {
src/views/businessOperations/pathManagement/index.vue
@@ -2,21 +2,18 @@
import noDataImg from '@/assets/element-img/no-data.png'
import type { DataTableColumns } from 'naive-ui'
import { useBoolean } from '@/hooks'
import { NButton, NDatePicker, NInput, NSelect, NSpace, NSwitch } from 'naive-ui'
import { NButton, NDatePicker, NInput, NSelect, NSwitch } from 'naive-ui'
import SearchPanel from '@/components/custom/SearchPanel.vue'
import Pagination from '@/components/common/Pagination.vue'
import OperationLogModal from '@/components/custom/OperationLogModal.vue'
import {
  addUturnConfig,
  batchImportUturnConfig,
  deleteUturnConfig,
  downloadUturnConfigTemplate,
  editUturnConfig,
  enableSwitchUturnConfig,
  exportUturnConfigExcel,
  fetchUturnConfigDetail,
  fetchUturnConfigPageList,
} from '@/service/api/uturnAvoidConfig'
import { fetchPointList } from '@/service/api/point'
import { downloadFile } from '@/utils/common'
import { getMultiSelectRowClassName, shouldIgnoreTableRowClick, toggleMultiSelectRowKey } from '@/utils/table'
import dayjs from 'dayjs'
@@ -49,6 +46,37 @@
const model = ref({ ...initialModel })
const isStatusSelectOpen = ref(false)
// 点位选项(用于「站点附近起步」Tab 的点位名称筛选与点位经纬度展示)
interface PointOption {
  label: string
  value: string
  id: number
  lon?: number
  lat?: number
}
const pointOptions = ref<PointOption[]>([])
const pointMap = ref<Record<number, { name: string, lon?: number, lat?: number }>>({})
async function loadPointOptions() {
  try {
    const res = await fetchPointList({})
    if (res.code === 200 && Array.isArray(res.data)) {
      const map: Record<number, { name: string, lon?: number, lat?: number }> = {}
      const opts: PointOption[] = []
      res.data.forEach((p) => {
        if (p.id == null)
          return
        const name = p.pointName || ''
        map[p.id] = { name, lon: p.pointLon, lat: p.pointLat }
        opts.push({ label: name, value: name, id: p.id, lon: p.pointLon, lat: p.pointLat })
      })
      pointMap.value = map
      pointOptions.value = opts
    }
  }
  catch {}
}
const statusOptions = [
  { label: '启用中', value: true },
  { label: '已停用', value: false },
@@ -63,7 +91,17 @@
  {
    label: activeTab.value === 'NARROW_UTURN' ? '位置名称' : '点位名称',
    path: 'name',
    render: () => <NInput v-model:value={model.value.name} placeholder={activeTab.value === 'NARROW_UTURN' ? '请输入位置名称' : '请输入点位名称'} clearable />,
    render: () => activeTab.value === 'NARROW_UTURN'
      ? <NInput v-model:value={model.value.name} placeholder="请输入位置名称" clearable />
      : (
          <NSelect
            v-model:value={model.value.name}
            options={pointOptions.value}
            placeholder="请选择点位名称"
            filterable
            clearable
          />
        ),
  },
  {
    label: '创建时间',
@@ -99,7 +137,6 @@
// ==================== 列表数据 ====================
const listData = ref<any[]>([])
const checkedRowKeys = ref<Array<string | number>>([])
const showOperationLogModal = ref(false)
const TABLE_ROW_CLICK_IGNORE_SELECTORS = ['.table-row-link']
const pagination = reactive({
@@ -192,36 +229,44 @@
  if (activeTab.value === 'STATION_START') {
    cols.push({
      title: '点位ID',
      key: 'startPointId',
      width: 100,
      title: '点位经纬度',
      key: 'startPointCoord',
      width: 200,
      align: 'left',
      ellipsis: true,
      ellipsis: { tooltip: true },
      resizable: true,
      render(row) {
        return row.startPointId || '-'
        const info = row.startPointId ? pointMap.value[row.startPointId] : undefined
        if (info && info.lon != null && info.lat != null)
          return `(${info.lon},${info.lat})`
        // 兼容后端可能直接下发的经纬度字段
        const lon = row.startPointLon ?? row.pointLon
        const lat = row.startPointLat ?? row.pointLat
        if (lon != null && lat != null)
          return `(${lon},${lat})`
        return '-'
      },
    })
  }
  cols.push(
    {
      title: '路径点经纬度',
      title: activeTab.value === 'NARROW_UTURN' ? '路径经纬度' : '路径点经纬度',
      key: 'uturnPointsJson',
      width: 240,
      align: 'left',
      ellipsis: true,
      ellipsis: { tooltip: true },
      resizable: true,
      render(row) {
        return formatPointsJson(row.uturnPointsJson)
      },
    },
    {
      title: '途径点经纬度',
      title: activeTab.value === 'NARROW_UTURN' ? '途径点经纬度' : '途径点',
      key: 'detourWaypointsJson',
      width: 240,
      align: 'left',
      ellipsis: true,
      ellipsis: { tooltip: true },
      resizable: true,
      render(row) {
        return formatPointsJson(row.detourWaypointsJson)
@@ -249,18 +294,15 @@
    {
      title: '操作',
      key: 'actions',
      width: 120,
      width: 90,
      align: 'left',
      fixed: 'right',
      render(row) {
        const isEnabled = row.enabled === true || row.enabled === 1
        return (
          <NSpace wrap={false}>
            <NButton text type="primary" onClick={() => handleEdit(row)}>编辑</NButton>
            <NButton text type="primary" onClick={() => handleEnableSwitch(row)}>
              {isEnabled ? '停用' : '启用'}
            </NButton>
          </NSpace>
          <NButton text type={isEnabled ? 'error' : 'primary'} onClick={() => handleEnableSwitch(row)}>
            {isEnabled ? '停用' : '启用'}
          </NButton>
        )
      },
    },
@@ -293,11 +335,9 @@
  })
}
// ==================== 新增/编辑弹窗 ====================
// ==================== 新增弹窗 ====================
const showFormModal = ref(false)
const isEditMode = ref(false)
const formModel = ref({
  id: null as number | null,
  name: '',
  uturnPointsText: '',
  detourWaypointsText: '',
@@ -307,9 +347,7 @@
})
function handleAdd() {
  isEditMode.value = false
  formModel.value = {
    id: null,
    name: '',
    uturnPointsText: '',
    detourWaypointsText: '',
@@ -318,24 +356,6 @@
    startPointId: null,
  }
  showFormModal.value = true
}
async function handleEdit(row: any) {
  isEditMode.value = true
  const res = await fetchUturnConfigDetail(row.id)
  if (res.code === 200 && res.data) {
    const data = res.data
    formModel.value = {
      id: data.id,
      name: data.name || '',
      uturnPointsText: formatPointsJson(data.uturnPointsJson).replace(/; /g, ';'),
      detourWaypointsText: formatPointsJson(data.detourWaypointsJson).replace(/; /g, ';'),
      radius: data.radius,
      enabled: data.enabled === true || data.enabled === 1,
      startPointId: data.startPointId,
    }
    showFormModal.value = true
  }
}
function parsePointsText(text: string): number[][] | null {
@@ -385,16 +405,9 @@
  }
  try {
    let res
    if (isEditMode.value) {
      payload.id = formModel.value.id
      res = await editUturnConfig(payload)
    }
    else {
      res = await addUturnConfig(payload)
    }
    const res = await addUturnConfig(payload)
    if (res.code === 200) {
      message.success(isEditMode.value ? '编辑成功' : '新增成功')
      message.success('新增成功')
      showFormModal.value = false
      getList()
    }
@@ -490,11 +503,8 @@
  getList()
}
function handleOpenOperationLog() {
  showOperationLogModal.value = true
}
onMounted(() => {
  loadPointOptions()
  getList()
})
</script>
@@ -545,6 +555,8 @@
            <NButton :disabled="checkedRowKeys.length === 0" @click="handleBatchExport">
              批量导出
            </NButton>
          </n-flex>
          <n-flex>
            <NButton @click="handleDownloadTemplate">
              下载模板
            </NButton>
@@ -558,11 +570,6 @@
              style="display: none;"
              @change="handleFileChange"
            >
          </n-flex>
          <n-flex>
            <NButton @click="handleOpenOperationLog">
              操作日志
            </NButton>
            <NButton type="primary" @click="handleAdd">
              新增
            </NButton>
@@ -603,10 +610,10 @@
      </div>
    </n-card>
    <!-- 新增/编辑弹窗 -->
    <!-- 新增弹窗 -->
    <n-modal
      v-model:show="showFormModal"
      :title="isEditMode ? '编辑' : '新增'"
      title="新增"
      preset="dialog"
      :mask-closable="false"
      style="width: 560px;"
@@ -669,8 +676,6 @@
        </NButton>
      </template>
    </n-modal>
    <OperationLogModal v-model:show="showOperationLogModal" />
  </div>
</template>