董国庆
2026-03-23 af74ba1914068b5237b940330922d2273ba54695
修改
4个文件已添加
6个文件已修改
462 ■■■■■ 已修改文件
.trae/skills/修改表单/SKILL.md 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/element-img/acatar-woman.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/element-img/avatar.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/element-img/man.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/element-img/woman.png 补丁 | 查看 | 原始文档 | blame | 历史
src/service/api/cruise.ts 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/assetManagement/vehicleManagement/inventoryVehicle/components/CompleteVehicleInfoModal.vue 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/businessOperations/cruise/parade-plan/detail.vue 82 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/operations/personnel/personnelManagement/detail.vue 270 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/operations/personnel/personnelManagement/index.vue 98 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.trae/skills/修改表单/SKILL.md
@@ -4,4 +4,8 @@
---
先检查页面的表单,并对表单做以下优化:
1,下拉框-如果有下拉框,需要优化下拉框的插槽arrow。
1,下拉框-如果有下拉框,需要优化下拉框的插槽arrow。
2,如果有参考的图片,需要先参考 图片的布局来调整。
3,一般的页面,都是padding:12px 。如果有块状元素,使用ncard。
4,如果有页面的title,title的样式这些可以参考:src\views\system\config\index.vue。
5,如果页面有保存这些操作,可以结合 src\views\system\config\index.vue 页面,看一下保存的样式这些。
src/assets/element-img/acatar-woman.png
src/assets/element-img/avatar.png
src/assets/element-img/man.png
src/assets/element-img/woman.png
src/service/api/cruise.ts
@@ -42,3 +42,7 @@
}) {
  return request.Post<Service.ResponseResult<any>>('/order/api/v1/cruise-order/pageList', data)
}
export function deleteCruiseOrder(id: number | string) {
  return request.Delete<Service.ResponseResult<any>>(`/order/api/v1/cruise-order/deleteById?id=${id}`)
}
src/views/assetManagement/vehicleManagement/inventoryVehicle/components/CompleteVehicleInfoModal.vue
@@ -393,7 +393,7 @@
// 计算弹窗标题
const modalTitle = computed(() => {
  return props.type === 'complete' ? '完善车辆信息' : '更新信息'
  return props.type === 'complete' ? '完善车辆信息' : '更新车辆信息'
})
</script>
src/views/businessOperations/cruise/parade-plan/detail.vue
@@ -7,11 +7,12 @@
import { useBoolean } from '@/hooks'
import SelectPointModal from '../patrol-points/components/SelectPointModal.vue'
import VehicleSelectModal from '@/views/assetManagement/fleetManagement/components/VehicleSelectModal.vue'
import { editCruisePlan, fetchCruiseOrderPageList, fetchCruisePlanDetail } from '@/service/api/cruise'
import { deleteCruiseOrder, editCruisePlan, fetchCruiseOrderPageList, fetchCruisePlanDetail } from '@/service/api/cruise'
const router = useRouter()
const route = useRoute()
const message = useMessage()
const dialog = useDialog()
interface Point {
  id: number
@@ -255,6 +256,8 @@
const selectedRecords = ref<number[]>([])
const { bool: recordLoading, setTrue: startRecordLoading, setFalse: endRecordLoading } = useBoolean(false)
const isStatusSelectOpen = ref(false)
const statusTextMap: Record<number, string> = {
  1: '进行中',
  2: '已停止',
@@ -324,29 +327,53 @@
const handleRecordDetail = (record: CruiseOrderRow) => {
  message.info(`订单 ${record.id} 详情暂未接入`)
  router.push({ name: 'orderDetail', params: { id: record.id } })
}
const handleDeleteRecord = (record: CruiseOrderRow) => {
  dialog.create({
    title: '是否确认删除该巡游订单?',
    content: '删除后数据不可恢复,请谨慎操作',
    positiveText: '确认',
    negativeText: '取消',
    draggable: true,
    onPositiveClick: async () => {
      const res = await deleteCruiseOrder(record.id)
      if (res.code === 200) {
        message.success('删除成功')
        getRecordList()
      }
    }
  })
}
const recordColumns = computed<DataTableColumns<CruiseOrderRow>>(() => [
  { type: 'selection', width: 50 },
  { title: '订单号', key: 'id', width: 100 },
  { title: '下单时间', key: 'createTime', width: 180 },
  { title: '起点', key: 'startPointName', minWidth: 150 },
  { title: '终点', key: 'endPointName', minWidth: 150 },
  { title: '接单车辆', key: 'plateNumber', width: 140 },
  { title: '归属车队', key: 'fleetName', width: 140 },
  { type: 'selection', width: 50, fixed: 'left' },
  { title: '订单号', key: 'id', width: 200, align: 'left', resizable: true },
  { title: '下单时间', key: 'createTime', width: 180, align: 'left', resizable: true, render: row => row.createTime ? dayjs(row.createTime).format('YYYY/MM/DD HH:mm') : '-' },
  { title: '订单来源', key: 'orderSource', width: 120, align: 'left', resizable: true, render: () => '系统自动创建' },
  { title: '起点', key: 'startPointName', width: 200, align: 'left', resizable: true, ellipsis: { tooltip: true } },
  { title: '终点', key: 'endPointName', width: 200, align: 'left', resizable: true, ellipsis: { tooltip: true } },
  { title: '接单车辆', key: 'plateNumber', width: 140, align: 'left', resizable: true },
  { title: '归属车队', key: 'fleetName', width: 140, align: 'left', resizable: true },
  {
    title: '状态',
    key: 'status',
    width: 100,
    align: 'left',
    resizable: true,
    render: row => <span>{row.status ? statusTextMap[row.status] || row.status : '-'}</span>,
  },
  {
    title: '操作',
    key: 'actions',
    width: 100,
    width: 120,
    fixed: 'right',
    align: 'left',
    render: row => (
      <div class="flex gap-3">
        <a style={{ color: '#0068FF', cursor: 'pointer' }} onClick={() => handleRecordDetail(row)}>详情</a>
        <a style={{ color: '#FF4D4F', cursor: 'pointer' }} onClick={() => handleDeleteRecord(row)}>删除</a>
      </div>
    ),
  },
@@ -366,12 +393,42 @@
  {
    label: '订单状态',
    path: 'status',
    render: () => <n-select v-model:value={recordSearch.value.status} options={statusOptions} placeholder="请选择" clearable />,
    render: () => (
      <n-select
        v-model:value={recordSearch.value.status}
        options={statusOptions}
        placeholder="请选择"
        clearable
        onUpdate:show={(val: boolean) => (isStatusSelectOpen.value = val)}
      >
        {{
          arrow: () => (
            <img
              src="https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/e120f81c-9f15-4e1a-9938-22252898edf9.png"
              class={`arrow-icon ${isStatusSelectOpen.value ? 'arrow-icon--open' : ''}`}
              style={{ width: '16px', height: '16px' }}
              alt="arrow"
            />
          ),
        }}
      </n-select>
    ),
  },
  {
    label: '下单时间',
    path: 'dateRange',
    render: () => <n-date-picker v-model:value={recordSearch.value.dateRange} type="datetimerange" clearable style={{ width: '100%' }} />,
    render: () => (
      <n-date-picker
        v-model:value={recordSearch.value.dateRange}
        type="datetimerange"
        clearable
        style={{ width: '100%' }}
        actions={[]}
        close-on-select={true}
        start-placeholder="开始时间"
        end-placeholder="结束时间"
      />
    ),
  },
])
@@ -523,7 +580,7 @@
                    </div>
                </n-card>
            </n-tab-pane>
            <n-tab-pane name="record" tab="巡游计划记录">
            <n-tab-pane name="record" tab="巡游订单记录">
                <n-card style="border-radius: 6px; margin-bottom: 16px">
                    <n-form :model="recordSearch" label-placement="left" inline :show-feedback="false">
                        <SearchPanel>
@@ -549,6 +606,7 @@
                        :data="records"
                        :loading="recordLoading"
                        :row-key="(row: CruiseOrderRow) => row.id"
                        :scroll-x="1450"
                        :pagination="{
                            page: recordCurrentPage,
                            pageSize: recordPageSize,
src/views/operations/personnel/personnelManagement/detail.vue
@@ -1,132 +1,250 @@
<script setup lang="ts">
import { ref } from 'vue'
import { ref, onMounted } from 'vue'
import { useTabStore } from '@/store'
import { useRouter, useRoute } from 'vue-router'
import { getOperationsUserDetail } from '@/service/api/attendance'
import { getOperationsUserDetail, editOperationsUser } from '@/service/api/attendance'
import { fetchRoleList } from '@/service/api/fleet'
import PersonnelFormModal from './components/PersonnelFormModal.vue'
const router = useRouter()
const route = useRoute()
const tabStore = useTabStore()
const personnelInfo = ref()
const showFormModal = ref(false)
const modalRef = ref()
const roleOptions = ref<any[]>([])
const getDetail = async () => {
  const res = await getOperationsUserDetail(route.params.id)
  if (res.code === 200) {
    personnelInfo.value = res.data
  }
}
const getRoleList = async () => {
  const res = await fetchRoleList()
  if (res.code === 200) {
    roleOptions.value = res.data.map((item: any) => ({
      label: item.roleName,
      value: item.id,
    }))
  }
}
const handleBack = () => {
  tabStore.closeTab(route.fullPath)
  router.back()
}
onMounted(async () => {
  const res = await getOperationsUserDetail(route.params.id)
  if (res.code === 200) {
    personnelInfo.value = res.data
  }
const handleEdit = () => {
  showFormModal.value = true
  modalRef.value?.openModal(personnelInfo.value)
}
const handleConfirmForm = (data: any) => {
  editOperationsUser(data).then(res => {
    if (res.code === 200) {
      showFormModal.value = false
      window.$message.success('编辑成功')
      getDetail()
    }
  })
}
onMounted(() => {
  getDetail()
  getRoleList()
})
</script>
<template>
  <div class="h-full flex flex-col" style="gap: 12px" v-if="personnelInfo?.nickName">
    <n-card style="border-radius: 6px" content-style="padding: 12px">
      <div class="flex items-center justify-between mb-4">
        <div class="text-lg font-bold">员工详情</div>
        <n-button style="width: 84px; height: 32px" @click="handleBack">返回</n-button>
  <div class="h-full flex flex-col p-12px overflow-y-auto" v-if="personnelInfo?.nickName">
    <n-card :bordered="false" class="rounded-6px" content-style="padding: 24px">
      <div class="flex items-center justify-between mb-24px">
        <div class="head">员工详情</div>
        <n-button type="primary" ghost class="w-84px h-32px" @click="handleEdit">编辑</n-button>
      </div>
      <div class="flex items-start gap-4">
        <n-avatar
          round
          :size="64"
          v-if="personnelInfo.avatar"
          :src="JSON.parse(personnelInfo.avatar).url"
        />
        <div class="flex-1">
          <div class="flex items-center gap-3 mb-2">
            <span class="text-lg font-medium">{{ personnelInfo.nickName }}</span>
          </div>
          <div class="text-sm mb-2">
            <span class="text-gray-600">编号:</span>
            <span>{{ personnelInfo.uniqueNumber }}</span>
          </div>
          <div class="grid grid-cols-3 gap-4 text-sm">
            <div>
              <span class="text-gray-600">电话:</span>
              <span>{{ personnelInfo.phoneNumber }}</span>
            </div>
            <div>
              <span class="text-gray-600">性别:</span>
              <span>{{ personnelInfo.sex === 1 ? '男' : '女' }}</span>
            </div>
            <div>
              <span class="text-gray-600">角色:</span>
              <span class="gap-2 inline-flex">
                <n-tag type="info" size="small" v-for="item in personnelInfo.sysRoles">{{ item.roleName }}</n-tag>
              </span>
            </div>
            <div>
              <span class="text-gray-600">备注:</span>
              <span>{{ personnelInfo.remarks || '-' }}</span>
            </div>
      <div class="flex items-center gap-8px mb-16px">
        <div class="relative w-40px h-40px">
          <n-avatar
            round
            :size="40"
            v-if="JSON.parse(personnelInfo.avatar).url"
            :src="JSON.parse(personnelInfo.avatar).url"
          />
          <img src="/src/assets/element-img/avatar.png" style="width:40px;height:40px" v-else-if="!JSON.parse(personnelInfo.avatar).url && personnelInfo.sex === 1" />
          <img src="/src/assets/element-img/acatar-woman.png" style="width:40px;height:40px" v-else />
            <img src="/src/assets/element-img/man.png" class="sex-icon"  v-if="personnelInfo.sex === 1"/>
            <img src="/src/assets/element-img/woman.png" class="sex-icon"  v-else/>
        </div>
        <div class="flex flex-col gap-4px">
            <span class="text-16px text-[#171F26]">{{ personnelInfo.nickName }}</span>
          <div class="text-14px text-[#919191]">
            编号:{{ personnelInfo.uniqueNumber }}
          </div>
        </div>
      </div>
    </n-card>
    <n-card style="border-radius: 6px" content-style="padding: 12px">
      <div class="mb-4">
        <div class="text-sm font-medium">身份证:</div>
        <div class="flex gap-4" v-if="personnelInfo.idCardFront || personnelInfo.idCardBack">
      <div class="grid grid-cols-3 gap-24px mb-16px line-bottom">
        <div class="flex items-center">
          <span class="text-14px label-color w-42px">电话:</span>
          <span class="text-14px text-[#171F26]">{{ personnelInfo.phoneNumber }}</span>
        </div>
        <div class="flex items-center">
          <span class="text-14px label-color w-42px">角色:</span>
          <div class="flex gap-8px">
            <span v-for="item in personnelInfo.sysRoles" :key="item.roleId" class="text-14px text-[#171F26]">{{ item.roleName }}</span>
          </div>
        </div>
        <div class="flex items-center">
          <span class="text-14px label-color w-42px">备注:</span>
          <span class="text-14px text-[#171F26]">{{ personnelInfo.remarks || '-' }}</span>
        </div>
      </div>
      <div class="mb-16px">
        <div class="text-14px font-600 text-[#171F26] mb-12px">身份证:</div>
        <div class="flex gap-16px" v-if="personnelInfo.idCardFront || personnelInfo.idCardBack">
          <n-image
            width="128"
            height="80"
            width="213"
            height="120"
            v-if="personnelInfo.idCardFront"
            :src="JSON.parse(personnelInfo.idCardFront).url"
            object-fit="cover"
            style="border-radius: 4px"
            class="rounded-4px border border-[#E5E5E5]"
          />
          <n-image
            width="128"
            height="80"
            width="213"
            height="120"
            v-if="personnelInfo.idCardBack"
            :src="JSON.parse(personnelInfo.idCardBack).url"
            object-fit="cover"
            style="border-radius: 4px"
            class="rounded-4px border border-[#E5E5E5]"
          />
        </div>
        <div v-else>
          <span>-</span>
        </div>
        <div v-else class="text-14px text-[#919191]">-</div>
      </div>
      <div class="mb-4">
        <div class="text-sm font-medium">驾驶证:</div>
      <div class="mb-16px">
        <div class="text-14px font-600 text-[#171F26] mb-12px">驾驶证:</div>
        <n-image
          width="128"
          height="80"
          width="213"
          height="120"
          v-if="personnelInfo.driverLicense"
          :src="JSON.parse(personnelInfo.driverLicense).url"
          object-fit="cover"
          style="border-radius: 4px"
          class="rounded-4px border border-[#E5E5E5]"
        />
        <div v-else>
          <span>-</span>
        </div>
        <div v-else class="text-14px text-[#919191]">-</div>
      </div>
      <div>
        <div class="text-sm font-medium">无犯罪记录证明:</div>
        <div class="text-14px font-600 text-[#171F26] mb-12px">无犯罪记录证明:</div>
        <n-image
          width="128"
          height="80"
          width="213"
          height="120"
          v-if="personnelInfo.certificateOfNoCriminalRecord"
          :src="JSON.parse(personnelInfo.certificateOfNoCriminalRecord).url"
          object-fit="cover"
          style="border-radius: 4px"
          class="rounded-4px border border-[#E5E5E5]"
        />
        <div v-else>
          <span>-</span>
        </div>
        <div v-else class="text-14px text-[#919191]">-</div>
      </div>
    </n-card>
    <PersonnelFormModal
      v-model:show="showFormModal"
      ref="modalRef"
      mode="edit"
      :roleOptions="roleOptions"
      @confirm="handleConfirmForm"
    />
  </div>
</template>
<style scoped></style>
<style scoped lang="scss">
.p-12px {
  padding: 12px;
}
.rounded-6px {
  border-radius: 6px;
}
.rounded-4px {
  border-radius: 4px;
}
.mb-12px {
  margin-bottom: 12px;
}
.w-84px {
  width: 84px;
}
.h-32px {
  height: 32px;
}
.w-42px {
  width: 42px;
}
.text-16px {
  font-size: 16px;
  line-height: 18px;
  font-weight: bold;
}
.sex-icon{
  position: absolute;
  bottom: 0;
  right: 0;
  // transform: translate(-50%, -50%);
  width: 12px;
  height: 12px;
}
.text-14px {
  font-size: 14px;
  line-height: 18px;
  color: #171F26;
}
.line-bottom {
  border-bottom: 1px solid #E5E5E5;
  padding-bottom: 16px;
}
.label-color{
  color: rgba(23, 31, 38, 0.6);
}
.head {
  font-family: PingFang SC;
  font-size: 16px;
  font-weight: 600;
  line-height: 24px;
  letter-spacing: normal;
  color: #171F26;
  position: relative;
  text-indent: 11px;
  &::before {
    content: '';
    position: absolute;
    top: 50%;
    left: 0;
    transform: translateY(-50%);
    width: 3px;
    height: 14px;
    z-index: 0;
    border-radius: 3px;
    background: #0068FF;
  }
}
</style>
src/views/operations/personnel/personnelManagement/index.vue
@@ -32,6 +32,9 @@
const roleOptions = ref<any[]>([])
const modalRef = ref()
const isRoleSelectOpen = ref(false)
const isStatusSelectOpen = ref(false)
const statusOptions = [
  { label: '启用', value: true },
  { label: '禁用', value: false }
@@ -119,13 +122,6 @@
  showFormModal.value = true
}
const handleEdit = (row: any) => {
  // router.push(`/operations/personnel/personnelManagement/detail/${row.id}`)
  formMode.value = 'edit'
  showFormModal.value = true
  modalRef.value?.openModal(row)
}
const handleConfirmForm = (data: any) => {
  if (data.id) {
    editOperationsUser(data).then(res => {
@@ -147,14 +143,6 @@
}
const moreOptions = (row: any) => [
  {
    label: '编辑',
    key: 'edit',
    show: hasPermission(70115),
    props: {
      onClick: () => handleEdit(row)
    }
  },
  {
    label: row.status ? '禁用' : '启用',
    key: 'toggle',
@@ -223,22 +211,61 @@
  {
    label: '角色',
    path: 'roleId',
    render: () => <n-select v-model:value={searchModel.value.roleId} options={roleOptions.value} placeholder="请选择" clearable />
    render: () => (
      <n-select
        v-model:value={searchModel.value.roleId}
        options={roleOptions.value}
        placeholder="请选择"
        clearable
        onUpdate:show={(val: boolean) => isRoleSelectOpen.value = val}
      >
        {{
          arrow: () => (
            <img
              src="https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/e120f81c-9f15-4e1a-9938-22252898edf9.png"
              class={`arrow-icon ${isRoleSelectOpen.value ? 'arrow-icon--open' : ''}`}
              style={{ width: '16px', height: '16px' }}
              alt="arrow"
            />
          ),
        }}
      </n-select>
    )
  },
  {
    label: '账号状态',
    path: 'status',
    render: () => <n-select v-model:value={searchModel.value.status} options={statusOptions} placeholder="请选择" clearable />
    render: () => (
      <n-select
        v-model:value={searchModel.value.status}
        options={statusOptions}
        placeholder="请选择"
        clearable
        onUpdate:show={(val: boolean) => isStatusSelectOpen.value = val}
      >
        {{
          arrow: () => (
            <img
              src="https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/e120f81c-9f15-4e1a-9938-22252898edf9.png"
              class={`arrow-icon ${isStatusSelectOpen.value ? 'arrow-icon--open' : ''}`}
              style={{ width: '16px', height: '16px' }}
              alt="arrow"
            />
          ),
        }}
      </n-select>
    )
  },
])
const columns = computed<DataTableColumns<any>>(() => [
  { title: '序号', key: 'serialNumber', width: 80, resizable: true },
  { title: '序号', key: 'serialNumber', width: 80, resizable: true, align: 'left' },
  {
    title: '姓名',
    key: 'nickName',
    width: 150,
    resizable: true,
    align: 'left',
    render: (row) => {
      return (
        <div class="flex items-center gap-2">
@@ -254,13 +281,14 @@
      )
    },
  },
  { title: '编号', key: 'uniqueNumber', width: 120, resizable: true  },
  { title: '联系电话', key: 'phoneNumber', width: 150, resizable: true  },
  { title: '编号', key: 'uniqueNumber', width: 120, resizable: true, align: 'left'  },
  { title: '联系电话', key: 'phoneNumber', width: 150, resizable: true, align: 'left'  },
  {
    title: '角色',
    key: 'sysRoles',
    width: 150,
    resizable: true,
    align: 'left',
    render: (row) => (
      <div class="flex items-center gap-1">
        {
@@ -276,17 +304,19 @@
    key: 'status',
    width: 100,
    resizable: true,
    align: 'left',
    render: (row) => (
      row.status  ? <NTag type="primary" size="small" style={{ borderRadius: '4px' }} >启用</NTag> : <NTag type="error" size="small" style={{ borderRadius: '4px' }} >禁用</NTag>
    )
  },
  { title: '创建时间', key: 'createTime', width: 180 , resizable: true, render: (row) => dayjs(row.createTime).format('YYYY-MM-DD HH:mm') },
  { title: '备注', key: 'remarks', width: 150, resizable: true, ellipsis: { tooltip: true }, render: (row) => row.remarks || '-' },
  { title: '创建时间', key: 'createTime', width: 180 , resizable: true, align: 'left', render: (row) => row.createTime ? dayjs(row.createTime).format('YYYY/MM/DD HH:mm') : '-' },
  { title: '备注', key: 'remarks', width: 150, resizable: true, align: 'left', ellipsis: { tooltip: true }, render: (row) => row.remarks || '-' },
  {
    title: '操作',
    key: 'actions',
    width: 120,
    fixed: 'right',
    align: 'left',
    render: (row) => (
      <div class="flex gap-3">
        <a v-permission="70111" style={{ color: '#0068FF', cursor: 'pointer' }} onClick={() => router.push(`/operations/personnel/personnelManagement/detail/${row.id}`)}>详情</a>
@@ -350,7 +380,7 @@
<template>
  <div class="roboTaxi">
    <n-card style="border-radius: 6px" content-style="padding: 12px">
    <div class="roboTaxi-form-card mb-12px">
      <n-form :model="searchModel" label-placement="left" inline :show-feedback="false">
        <SearchPanel>
          <n-gi v-for="item in filterItems" :key="item.path">
@@ -364,9 +394,9 @@
          </template>
        </SearchPanel>
      </n-form>
    </n-card>
    </div>
    <n-card class="flex-1 mt-12px">
    <n-card class="flex-1 robotaxi-table-card">
      <template #header>
        <n-flex justify="flex-end">
          <n-button v-permission="70112" @click="handleExport" style="width: 84px; height: 32px">导出</n-button>
@@ -376,11 +406,19 @@
            @click="handleAdd">新增</n-button>
        </n-flex>
      </template>
      <NSpace vertical>
        <n-data-table :columns="columns" :data="personnelList" :loading="loading" :bordered="false" :header-height="18"
          :row-height="18" />
        <Pagination :count="total" :page="pageParams.pageNum" :page-size="pageParams.pageSize" @change="changePage" />
      </NSpace>
      <div class="flex-1 flex flex-col h-full">
        <n-data-table
          :columns="columns"
          :data="personnelList"
          :loading="loading"
          :bordered="false"
          :scroll-x="1200"
          :header-height="18"
          class="flex-1"
          :row-height="18"
        />
        <Pagination :count="total" class="robotaxi-pagination mt-12px" :page="pageParams.pageNum" :page-size="pageParams.pageSize" @change="changePage" />
      </div>
    </n-card>
    <PersonnelFormModal v-model:show="showFormModal" ref="modalRef" :mode="formMode" :roleOptions="roleOptions"