1、先上代码,data.ts

import type { VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';

import { DICT_TYPE } from '@vben/constants';
import { getDictOptions } from '@vben/hooks';
import { useUserStore } from '@vben/stores';

import dayjs from 'dayjs';

import { getSimpleUserList } from '#/api/system/user';
import { getRangePickerFutureProps } from '#/utils';

const userStore = useUserStore();

/** 列表的搜索表单 */
export function useGridFormSchema(tabType: number): VbenFormSchema[] {
  return [
    {
      fieldName: 'sn',
      label: '设备型号',
      component: 'Input',
      componentProps: {
        allowClear: true,
        placeholder: '请输入设备型号',
      },
    },
    {
      fieldName: 'firmwareStatus',
      label: '升级状态',
      component: 'Select',
      componentProps: {
        options: getDictOptions(
          DICT_TYPE.DJ_SK2_EQUIPMENT_FIRMWARE_STATUS_ENUM,
          'number',
        ),
        allowClear: true,
        mode: 'multiple',
        placeholder: '请选择升级状态',
      },
      // 👇 直接在这里控制显隐,最稳定
      hide: tabType === 1,
    },
    {
      fieldName: 'deviceOnlineStatus',
      label: '在线状态',
      component: 'Select',
      componentProps: {
        options: getDictOptions(
          DICT_TYPE.DJ_SK2_EQUIPMENT_ONLINE_STATUS_SELECT,
          'string',
        ),
        allowClear: true,
        placeholder: '请选择在线状态',
      },
      // 👇 直接在这里控制显隐,最稳定
      hide: tabType === 0,
    },
    {
      fieldName: 'deviceWorkingStatus',
      label: '工作状态',
      component: 'Select',
      componentProps: {
        allowClear: true,
        options: getDictOptions(
          DICT_TYPE.DJ_SK2_EQUIPMENT_WORK_STATUS_ENUM,
          'string',
        ),
        placeholder: '请选择工作状态',
      },
      // 👇 直接在这里控制显隐,最稳定
      hide: tabType === 1,
    },
    // {
    //   fieldName: 'deptId',
    //   label: '所属项目',
    //   component: 'ApiTreeSelect',
    //   componentProps: {
    //     api: async () => {
    //       const data = await getDeptList();
    //       return handleTree(data);
    //     },
    //     labelField: 'name',
    //     valueField: 'id',
    //     childrenField: 'children',
    //     placeholder: '请选择所属项目',
    //     treeDefaultExpandAll: true,
    //     allowClear: true,
    //   },
    // },
    {
      fieldName: 'travelApplication',
      label: '设备型号',
      component: 'Select',
      componentProps: {
        allowClear: true,
        options: getDictOptions(DICT_TYPE.APP_FLY_ZERO_NO_ONE_YES, 'number'),
        placeholder: '请选择设备型号',
      },
    },
  ];
}

/** 列表的字段 */
export function useGridColumns(
  tabType: number,
): VxeTableGridOptions['columns'] {
  return [
    {
      type: 'seq',
      title: '序号',
      minWidth: 50,
    },
    {
      field: 'deviceModel',
      title: '设备型号',
      minWidth: 150,
      align: 'left', // 👈 左对齐
      slots: { default: 'device_model' },
    },
    {
      field: 'deviceSn',
      title: '设备SN',
      minWidth: 180,
      align: 'left', // 👈 左对齐
      slots: { default: 'device_sn' },
    },
    {
      field: 'deviceCallsign',
      title: '设备名称',
      minWidth: 120,
      align: 'left', // 👈 左对齐
      slots: { default: 'device_callsign' },
    },
    {
      field: 'deviceFirmwareVersion',
      title: '固件版本',
      minWidth: 110,
      align: 'left', // 👈 左对齐
      slots: { default: 'device_firmware_version' },
    },
    {
      field: 'deviceFirmwareStatus',
      title: '固件升级',
      minWidth: 120,
      align: 'left', // 👈 左对齐
      formatter: ({ cellValue }) => {
        return cellValue === 0
          ? '要看司空2'
          : cellValue === 1
            ? '无需升级'
            : cellValue === 1
              ? '升级中'
              : '—';
      },
    },
    {
      field: 'deviceWorkingStatus',
      title: '工作状态',
      minWidth: 90,
      visible: tabType === 0,
      align: 'left', // 👈 左对齐
      slots: { default: 'device_working_status' },
    },
    {
      field: 'deviceOnlineStatus',
      title: '在线状态',
      minWidth: 90,
      visible: tabType === 1,
      cellRender: {
        name: 'CellDict',
        props: { type: DICT_TYPE.DJ_SK2_EQUIPMENT_ONLINE_STATUS_ENUM },
      },
    },
    {
      field: 'deviceActiveProjectName',
      title: '所属项目',
      minWidth: 120,
    },
    {
      field: 'deviceAddTime',
      title: '加入组织时间',
      minWidth: 150,
      align: 'left', // 👈 左对齐
      slots: { default: 'device_add_time' },
      formatter: ({ cellValue }) => {
        return cellValue
          ? dayjs(Number(cellValue) * 1000).format('YYYY-MM-DD HH:mm:ss')
          : '—';
      },
    },
    {
      field: 'deviceLastOnlineTime',
      title: '在线时间',
      minWidth: 150,
      align: 'left', // 👈 左对齐
      slots: { default: 'device_last_online_time' },
      formatter: ({ cellValue }) => {
        return cellValue
          ? dayjs(Number(cellValue) * 1000).format('YYYY-MM-DD HH:mm:ss')
          : '—';
      },
    },
    // {
    //   title: '操作',
    //   width: 250,
    //   fixed: 'right',
    //   slots: { default: 'actions' },
    // },
  ];
}

/** 新增/修改的表单 */
export function useFormSchema(automaticAssignment: number): VbenFormSchema[] {
  return [
    {
      fieldName: 'id',
      component: 'Input',
      dependencies: {
        triggerFields: [''],
        show: () => false,
      },
    },
    {
      fieldName: 'userId',
      label:
        automaticAssignment === 1 ? '处理人员(不选则系统自动指派)' : '处理人员',
      component: 'ApiSelect',
      componentProps: {
        api: getSimpleUserList,
        labelField: 'nickname',
        valueField: 'id',
        placeholder: '请选择处理人员',
        allowClear: true,
        showSearch: true,
        disabled: automaticAssignment === 0,
        filterOption: (inputValue: any, option: any) => {
          console.warn(automaticAssignment);
          // 确保 option 存在且 nickname 字段可被检索
          if (!option || !option.label) return false;
          // 忽略大小写的模糊匹配
          return option.label.toLowerCase().includes(inputValue.toLowerCase());
        },
      },
      formItemClass: 'col-span-12',
      // 是否属于自动分配任务 0-否;1-是不能为空
      defaultValue: automaticAssignment === 0 ? userStore.userInfo?.id : null,
    },
    {
      fieldName: 'type',
      label: '事务类型',
      component: 'Select',
      componentProps: {
        options:
          automaticAssignment === 1
            ? getDictOptions(DICT_TYPE.TASK_TYPE, 'string').filter((v: any) => {
                return v.value !== 'leave_task';
              })
            : getDictOptions(DICT_TYPE.TASK_TYPE, 'string'),
        placeholder: '请选择事务类型',
      },
      rules: 'required',
      formItemClass: 'col-span-12',
    },
    {
      fieldName: 'inspectionTime',
      label: '事务时间',
      component: 'RangePicker',
      componentProps: {
        ...getRangePickerFutureProps(),
        allowClear: true,
      },
      rules: 'required',
      formItemClass: 'col-span-12',
    },
    {
      fieldName: 'estimatedTotalTime',
      label: '预估总用时(只能输入0.5的倍数)',
      component: 'InputNumber',
      componentProps: {
        placeholder: '请输入预估总用时,最小值为0.5,只能输入0.5的倍数',
        min: 0.5, // 最小值设为0.5
        step: 0.5, // 步长设为0.5,控制点击增减按钮的幅度
        precision: 1, // 保留1位小数
        stringMode: true,
        defaultValue: 0.5, // 默认值为0.5
        // 核心:格式化输入值为0.5的倍数
        formatter: (value) => {
          // 处理空值/非数字情况
          if (!value || isNaN(Number(value))) {
            return 0.5;
          }
          const numValue = Number(value);
          // 核心逻辑:计算最近的0.5倍数(向下取整)
          const formattedValue = Math.floor(numValue * 2) / 2;
          // 确保不小于最小值0.5
          const finalValue = Math.max(formattedValue, 0.5);
          return finalValue;
        },
        // 解析函数:确保提交的值是正确的数值
        parser: (value) => {
          if (!value || isNaN(Number(value))) {
            return 0.5;
          }
          const numValue = Number(value);
          const formattedValue = Math.floor(numValue * 2) / 2;
          return Math.max(formattedValue, 0.5);
        },
        // 失焦时再次校验,防止手动输入不符合规则的值
        onBlur: (e) => {
          const value = e.target.value;
          if (!value || isNaN(Number(value))) {
            e.target.value = 0.5;
            return;
          }
          const numValue = Number(value);
          const formattedValue = Math.floor(numValue * 2) / 2;
          e.target.value = Math.max(formattedValue, 0.5);
        },
      },
      formItemClass: 'col-span-12',
    },
    {
      fieldName: 'deviceSn',
      label: '事务内容',
      component: 'Textarea',
      componentProps: {
        placeholder: '请输入事务内容',
        rows: 2,
        maxLength: 100, // 直接限制输入框最大输入长度,提升用户体验
        showCount: true,
      },
      formItemClass: 'col-span-12',
      rules: 'required',
    },
    {
      fieldName: 'travelApplication',
      label: '是否外出',
      component: 'RadioGroup',
      dependencies: {
        triggerFields: ['type'],
        show: (values) => {
          return !['leave_task'].includes(values.type);
        },
      },
      componentProps: {
        options: getDictOptions(DICT_TYPE.APP_FLY_ZERO_NO_ONE_YES, 'number'),
        buttonStyle: 'solid',
        optionType: 'button',
      },
      rules: 'required',
      defaultValue: 0,
    },
    {
      fieldName: 'outTime',
      label: '外出时间',
      component: 'RangePicker',
      componentProps: {
        ...getRangePickerFutureProps(),
        allowClear: true,
      },
      dependencies: {
        triggerFields: ['travelApplication'],
        show: (values) => {
          return values.travelApplication === 1;
        },
      },
      rules: 'required',
      formItemClass: 'col-span-12',
    },
  ];
}

/** 处置任务 */
export function dealFormSchema(): VbenFormSchema[] {
  return [
    {
      component: 'Input',
      fieldName: 'id',
      dependencies: {
        triggerFields: [''],
        show: () => false,
      },
    },
    {
      fieldName: 'satisfactionScore',
      label: '客户评分',
      component: 'InputNumber',
      componentProps: {
        placeholder: '请输入客户评分,1~100分',
        min: 1, // 组件层面限制最小值
        max: 100, // 组件层面限制最大值
        precision: 0, // 核心:精度0 → 只能输入整数(组件原生属性,无类型问题)
        step: 1, // 步长1 → 增减按钮只变整数
        // 组件内置的输入格式化,避免手动输入小数
        formatter: (value) => {
          return value ? Number.parseInt(value.toString(), 10) : '';
        },
      },
      formItemClass: 'col-span-12',
    },
    {
      component: 'Textarea',
      fieldName: 'remark',
      label: '处置说明',
      componentProps: {
        placeholder: '请输入处置说明',
        maxLength: 200, // 直接限制输入框最大输入长度,提升用户体验
        showCount: true,
      },
      formItemClass: 'col-span-12',
    },
    {
      fieldName: 'satisfactionAttachmentUrl',
      label: '上传图片',
      component: 'ImageUpload',
      componentProps: {
        maxNumber: 9,
        maxCount: 9, // 新增主流属性(核心修复)
        multiple: true,
        maxSize: 30,
        // 可选:手动添加 onChange 校验(兜底方案)
        onChange: (files: any) => {
          if (files.length > 9) {
            // 截断超出的图片(强制限制)
            return files.slice(0, 9);
          }
          return files;
        },
      },
    },
  ];
}

2、index.vue

<script lang="ts" setup>
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { FlyTaskApi } from '#/api/fly/flyTask';

import { computed, ref } from 'vue';
import { useRouter } from 'vue-router';

import { Page, useVbenModal } from '@vben/common-ui';
import { DICT_TYPE } from '@vben/constants';
import { useUserStore } from '@vben/stores';

import { message, Tabs, Tag } from 'ant-design-vue';
import dayjs from 'dayjs';

import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
// import { deleteFlyTask, getFlyTaskPage } from '#/api/fly/flyTasks';
import { getFlyDevicesPageManage } from '#/api/fly/flyDevices';
import { getConfigKey } from '#/api/infra/config';
import { DictTag } from '#/components/dict-tag';
import { $t } from '#/locales';

import { useGridColumns, useGridFormSchema } from './data';
import DealForm from './modules/deal-form.vue';
import Form from './modules/form.vue';

const { push } = useRouter();
const userStore = useUserStore();
const tabType = ref(0);
const tabsData = ref([
  {
    name: '机场',
    type: 0,
    value: 'airport',
  },
  {
    name: '飞行器',
    type: 1,
    value: 'drone',
  },
]);
const [FormModal, formModalApi] = useVbenModal({
  connectedComponent: Form,
  destroyOnClose: true,
  fullscreen: false,
  fullscreenButton: false,
  class: 'w-1/2',
});
const [DealFormModal, dealFormModalApi] = useVbenModal({
  connectedComponent: DealForm,
  destroyOnClose: true,
  fullscreen: false,
  fullscreenButton: false,
  class: 'w-1/2',
});

// 👇 响应式 schema
const formSchema = computed(() => useGridFormSchema(tabType.value));

/** 刷新表格 */
function handleRefresh() {
  gridApi.query();
}
/** 创建事务管理 */
function handleCreate() {
  formModalApi.setData({ automaticAssignment: 0 }).open();
}
/** 创建事务管理 */
function handleCreateAuto() {
  formModalApi.setData({ automaticAssignment: 1 }).open();
}
/** 编辑事务管理 */
function handleEdit(row: FlyTaskApi.Task) {
  formModalApi.setData(row).open();
}

/** 查看事务管理详情 */
function handleDetail(row: FlyTaskApi.Task) {
  push({ path: `/flytask/${row.id}` });
}

function onChangeTab(key: any) {
  tabType.value = Number(key);
  gridApi.setGridOptions({
    columns: useGridColumns(tabType.value),
  });
  // gridApi.query();
}

/** 事务管理 删除 */
async function handleDelete(row: FlyTaskApi.Task) {
  const hideLoading = message.loading({
    content: $t('ui.actionMessage.deleting'),
    duration: 0,
  });
  try {
    await deleteFlyTask(row.id);
    message.success($t('ui.actionMessage.deleteSuccess'));
    handleRefresh();
  } finally {
    hideLoading();
  }
}

/** 变更事务管理状态为已完成 */
async function handleCompleted(row: FlyTaskApi.Task) {
  dealFormModalApi.setData(row).open();
}

const [Grid, gridApi] = useVbenVxeGrid({
  formOptions: {
    schema: formSchema,
  },
  gridOptions: {
    columns: useGridColumns(),
    cellConfig: {
      height: 90,
    },
    height: 'auto',
    keepSource: true,
    proxyConfig: {
      ajax: {
        query: async ({ page }, formValues) => {
          const data = await getConfigKey('org_uuid');
          const params = {
            pageNo: page.currentPage,
            pageSize: page.pageSize,
            deviceModelClass: 'airport',
            orgUuid: data,
            ...formValues,
          };
          if (
            formValues.deviceWorkingStatus &&
            Number.parseInt(formValues.deviceWorkingStatus) === 100
          ) {
            params.deviceOnlineStatus = false;
            delete params.deviceWorkingStatus;
          }
          if (tabType.value === 1) {
            params.deviceModelClass = 'drone';
          }
          return await getFlyDevicesPageManage(params);
        },
      },
    },
    rowConfig: {
      keyField: 'id',
      isHover: true,
    },
    toolbarConfig: {
      refresh: true,
      search: true,
    },
  } as VxeTableGridOptions<FlyTaskApi.Task>,
});
</script>

<template>
  <Page auto-content-height>
    <FormModal @success="handleRefresh" />
    <DealFormModal @success="handleRefresh" />
    <Grid :key="tabType">
      <!-- <Grid> -->
      <!-- 顶部切换 -->
      <template #toolbar-actions>
        <Tabs @change="onChangeTab" class="w-full" v-model:active-key="tabType">
          <Tabs.TabPane
            v-for="item in tabsData"
            :key="item.type"
            :tab="`${item.name}`"
          />
        </Tabs>
      </template>
      <!-- 设备型号 -->
      <template #device_model="{ row }">
        <div class="flex">
          <div class="">
            <div style="">
              {{ row.deviceModel.name }}
            </div>
            <div
              class="justify-left mt-3 flex items-center"
              v-if="
                row.associateDroneDeviceInfo &&
                row.associateDroneDeviceInfo.deviceModel
              "
            >
              <div class="l-xing mr-1"></div>
              <div>
                {{ row.associateDroneDeviceInfo.deviceModel.name }}
              </div>
            </div>
          </div>
        </div>
      </template>
      <!-- 设备SN -->
      <template #device_sn="{ row }">
        <div class="flex">
          <div class="">
            <div>
              {{ row.deviceSn }}
            </div>
            <div v-if="row.associateDroneDeviceInfo" class="mt-3">
              {{ row.associateDroneDeviceInfo.deviceSn }}
            </div>
          </div>
        </div>
      </template>
      <!-- 设备名称 -->
      <template #device_callsign="{ row }">
        <div class="flex">
          <div class="">
            <div>
              {{ row.deviceCallsign }}
            </div>
            <div
              class="mt-3"
              v-if="
                row.associateDroneDeviceInfo &&
                row.associateDroneDeviceInfo.deviceCallsign
              "
            >
              {{
                (row.associateDroneDeviceInfo &&
                  row.associateDroneDeviceInfo.deviceCallsign) ||
                '-'
              }}
            </div>
          </div>
        </div>
      </template>
      <!-- 固件版本 -->
      <template #device_firmware_version="{ row }">
        <div class="flex">
          <div class="">
            <div>
              {{ row.deviceFirmwareVersion }}
            </div>
            <div v-if="row.associateDroneDeviceInfo" class="mt-3">
              {{ row.associateDroneDeviceInfo.deviceFirmwareVersion }}
            </div>
          </div>
        </div>
      </template>
      <!-- 工作状态 -->
      <template #device_working_status="{ row }">
        <div class="flex">
          <div class="">
            <div>
              <DictTag
                :type="DICT_TYPE.DJ_SK2_EQUIPMENT_WORK_STATUS_ENUM"
                :value="row.deviceWorkingStatus"
                v-if="row.deviceOnlineStatus"
              />
              <DictTag
                :type="DICT_TYPE.DJ_SK2_EQUIPMENT_WORK_STATUS_ENUM"
                :value="100"
                v-else
              />
            </div>
            <div v-if="row.associateDroneDeviceInfo" class="mt-3">
              <DictTag
                :type="DICT_TYPE.DJ_SK2_EQUIPMENT_WORK_STATUS_ENUM"
                :value="row.associateDroneDeviceInfo.deviceWorkingStatus"
                v-if="row.associateDroneDeviceInfo.deviceOnlineStatus"
              />
              <!-- <DictTag
                :type="DICT_TYPE.DJ_SK2_EQUIPMENT_WORK_STATUS_ENUM"
                :value="100"
                v-else
              /> -->
              <Tag v-else type="info"> 飞行器未连接 </Tag>
            </div>
          </div>
        </div>
      </template>
      <!-- 加入组织时间 -->
      <template #device_add_time="{ row }">
        <div>
          {{
            row.deviceAddTime
              ? dayjs(Number(row.deviceAddTime) * 1000).format(
                  'YYYY-MM-DD HH:mm:ss',
                )
              : '—'
          }}
        </div>
        <div v-if="row.associateDroneDeviceInfo" class="mt-3">
          {{
            row.associateDroneDeviceInfo &&
            row.associateDroneDeviceInfo.deviceAddTime
              ? dayjs(
                  Number(row.associateDroneDeviceInfo.deviceAddTime) * 1000,
                ).format('YYYY-MM-DD HH:mm:ss')
              : '—'
          }}
        </div>
      </template>
      <!-- 在线时间 -->
      <template #device_last_online_time="{ row }">
        <div>
          {{
            row.deviceLastOnlineTime
              ? dayjs(Number(row.deviceLastOnlineTime) * 1000).format(
                  'YYYY-MM-DD HH:mm:ss',
                )
              : '—'
          }}
        </div>
        <div v-if="row.associateDroneDeviceInfo" class="mt-3">
          {{
            row.associateDroneDeviceInfo &&
            row.associateDroneDeviceInfo.deviceLastOnlineTime
              ? dayjs(
                  Number(row.associateDroneDeviceInfo.deviceLastOnlineTime) *
                    1000,
                ).format('YYYY-MM-DD HH:mm:ss')
              : '—'
          }}
        </div>
      </template>
      <!-- 操作栏 -->
      <template #toolbar-tools>
        <TableAction
          :actions="[
            // {
            //   label: '如何绑定机场?',
            //   type: 'text',
            //   onClick: handleCreateAuto,
            // },
            {
              label: $t('ui.actionTitle.create', ['事务']),
              type: 'primary',
              icon: ACTION_ICON.ADD,
              auth: ['starpivot:star-pivot-task:create'],
              onClick: handleCreate,
            },
            {
              label: $t('ui.actionTitle.create', ['指派事务']),
              type: 'primary',
              icon: ACTION_ICON.ADD,
              auth: ['starpivot:star-pivot-task:dispatch'],
              onClick: handleCreateAuto,
            },
            {
              label: $t('ui.actionTitle.create', ['日志反馈']),
              type: 'primary',
              icon: ACTION_ICON.ADD,
              onClick: handleCreateAuto,
            },
            {
              label: $t('ui.actionTitle.create', ['固件管理']),
              type: 'primary',
              icon: ACTION_ICON.ADD,
              onClick: handleCreateAuto,
            },
            {
              label: $t('ui.actionTitle.create', ['绑定设备码']),
              type: 'primary',
              icon: ACTION_ICON.ADD,
              onClick: handleCreateAuto,
            },
          ]"
        />
      </template>
      <template #actions="{ row, seq }">
        <TableAction
          :key="tabType"
          :actions="[
            {
              label: $t('common.edit'),
              type: 'link',
              icon: ACTION_ICON.EDIT,
              auth: ['starpivot:star-pivot-task:update'],
              onClick: handleEdit.bind(null, row),
              // 状态 1-待完成;2-已完成
              // 1-待审核;2-已通过;3-已驳回
              ifShow:
                [1].includes(row.status) &&
                ![2].includes(row.outApprovalResult) &&
                (parseInt(row.userId) === userStore.userInfo?.id ||
                  parseInt(row.creator) === userStore.userInfo?.id),
            },
            {
              label: $t('common.completed'),
              type: 'link',
              icon: ACTION_ICON.CIRCLECHECK,
              auth: ['starpivot:star-pivot-task:update'],
              onClick: handleCompleted.bind(null, row),
              // 状态 1-待完成;2-已完成
              // 1-待审核;2-已通过;3-已驳回
              ifShow:
                !['leave_task'].includes(row.type) &&
                [1].includes(row.status) &&
                [0, 2].includes(row.outApprovalResult) &&
                parseInt(row.userId) === userStore.userInfo?.id,
            },
            {
              label: $t('common.detail'),
              type: 'link',
              icon: ACTION_ICON.VIEW,
              auth: ['starpivot:star-pivot-task:query'],
              onClick: handleDetail.bind(null, row),
            },
            {
              label: $t('common.delete'),
              type: 'link',
              icon: ACTION_ICON.DELETE,
              auth: ['starpivot:star-pivot-task:delete'],
              popConfirm: {
                title: $t('ui.actionMessage.deleteConfirm'),
                confirm: handleDelete.bind(null, row),
              },
              ifShow:
                [1].includes(row.status) &&
                parseInt(row.userId) === userStore.userInfo?.id,
            },
          ]"
        />
      </template>
    </Grid>
  </Page>
</template>
<style lang="css" scoped>
.l-xing {
  width: 5px;
  height: 5px;
  background: #e9e3e3;
  clip-path: polygon(0 0, 100% 0, 100% 40%, 40% 40%, 40% 100%, 0 100%);
  transform: rotate(270deg); /* 旋转90度,方向正确 */
  transition: 0.3s;
}
</style>

3、核心方法


      // 👇 直接在这里控制显隐,最稳定
      hide: tabType === 0,


    // 第二处
    <Grid :key="tabType">

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐