wanshenmean
2026-03-24 78e153a3bec824964a52c3a0c2744ef0191f44ac
feat: 新增缺失前端页面并接入路由

改动内容:补齐 Sys_Tenant、taskHty、scheduler 三个页面,新增对应 extension 文件并在 viewGird.js 注册路由入口。

改动原因:后端已存在 tenant/task_hty/scheduler 接口,但前端缺少页面与入口,导致功能不可达。

影响范围:WIDESEAWCS_Client/src/views、WIDESEAWCS_Client/src/extension、WIDESEAWCS_Client/src/router/viewGird.js。
已添加5个文件
已修改1个文件
800 ■■■■■ 文件已修改
Code/WCS/WIDESEAWCS_Client/src/extension/system/Sys_Tenant.js 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Client/src/extension/taskinfo/taskHty.js 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Client/src/router/viewGird.js 46 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Client/src/views/quartzJob/scheduler.vue 136 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Client/src/views/system/Sys_Tenant.vue 238 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Client/src/views/taskinfo/taskHty.vue 242 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Client/src/extension/system/Sys_Tenant.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,69 @@
// è¯¥æ‰©å±•文件用于租户页面的前端扩展。
// æ–¹æ³•目的:在不修改通用 view-grid ç»„件的前提下,预留查询前后、保存前后的业务扩展入口。
// å‚数说明:各方法参数由框架回调传入(如搜索参数、表单数据、行数据)。
// è¿”回值说明:返回 true è¡¨ç¤ºç»§ç»­æ‰§è¡Œæ¡†æž¶é»˜è®¤æµç¨‹ï¼Œè¿”回 false è¡¨ç¤ºä¸­æ–­é»˜è®¤æµç¨‹ã€‚
// å¼‚常处理说明:当前扩展不抛出异常,若后续增加网络调用,请在对应方法内使用 try-catch å¹¶è®°å½•关键上下文。
let extension = {
  components: {
    gridHeader: "",
    gridBody: "",
    gridFooter: "",
    modelHeader: "",
    modelBody: "",
    modelFooter: "",
  },
  tableAction: "",
  buttons: { view: [], box: [], detail: [] },
  methods: {
    // æ–¹æ³•目的:页面初始化前执行,可用于调整按钮、列配置。
    // å‚数含义:无。
    // è¿”回值:无。
    onInit() {},
    // æ–¹æ³•目的:页面初始化完成后执行,可用于加载附加数据。
    // å‚数含义:无。
    // è¿”回值:无。
    onInited() {},
    // æ–¹æ³•目的:查询前参数预处理。
    // å‚数含义:param ä¸ºå½“前查询条件对象。
    // è¿”回值:true ç»§ç»­æŸ¥è¯¢ï¼Œfalse ç»ˆæ­¢æŸ¥è¯¢ã€‚
    searchBefore(param) {
      return true;
    },
    // æ–¹æ³•目的:查询结果后处理。
    // å‚数含义:result ä¸ºåŽç«¯è¿”回结果。
    // è¿”回值:true ç»§ç»­æ¸²æŸ“,false å¯ç”¨äºŽé˜»æ–­åŽç»­å¤„理。
    searchAfter(result) {
      return true;
    },
    // æ–¹æ³•目的:新增保存前校验或补充字段。
    // å‚数含义:formData ä¸ºå¾…提交表单对象。
    // è¿”回值:true ç»§ç»­ä¿å­˜ï¼Œfalse ç»ˆæ­¢ä¿å­˜ã€‚
    addBefore(formData) {
      return true;
    },
    // æ–¹æ³•目的:编辑保存前校验或补充字段。
    // å‚数含义:formData ä¸ºå¾…提交表单对象。
    // è¿”回值:true ç»§ç»­ä¿å­˜ï¼Œfalse ç»ˆæ­¢ä¿å­˜ã€‚
    updateBefore(formData) {
      return true;
    },
    // æ–¹æ³•目的:表格行点击事件扩展。
    // å‚数含义:row ä¸ºè¡Œæ•°æ®ï¼Œcolumn ä¸ºåˆ—配置,event ä¸ºåŽŸå§‹äº‹ä»¶å¯¹è±¡ã€‚
    // è¿”回值:无。
    rowClick({ row, column, event }) {},
    // æ–¹æ³•目的:新增/编辑弹窗打开后扩展处理。
    // å‚数含义:row ä¸ºå½“前行数据;新增时可能为空对象。
    // è¿”回值:无。
    modelOpenAfter(row) {},
  },
};
export default extension;
Code/WCS/WIDESEAWCS_Client/src/extension/taskinfo/taskHty.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,69 @@
// è¯¥æ‰©å±•文件用于任务历史页面的业务扩展。
// æ–¹æ³•目的:统一预留列表查询、保存前后和弹窗打开时的扩展入口,保持与项目内 task é¡µé¢ä¸€è‡´çš„æ‰©å±•点。
// å‚数含义:由框架按生命周期传入,包含查询参数、表单数据、行数据等上下文。
// è¿”回值说明:返回 true ä»£è¡¨ç»§ç»­é»˜è®¤è¡Œä¸ºï¼Œè¿”回 false ä»£è¡¨ä¸­æ–­é»˜è®¤è¡Œä¸ºã€‚
// å¼‚常处理说明:当前未引入外部调用;若后续新增接口调用,需在方法内补充 try-catch å¹¶è®°å½•关键业务参数。
let extension = {
  components: {
    gridHeader: "",
    gridBody: "",
    gridFooter: "",
    modelHeader: "",
    modelBody: "",
    modelFooter: "",
  },
  tableAction: "",
  buttons: { view: [], box: [], detail: [] },
  methods: {
    // æ–¹æ³•目的:页面初始化时执行自定义逻辑。
    // å‚数:无。
    // è¿”回值:无。
    onInit() {},
    // æ–¹æ³•目的:页面初始化完成后执行自定义逻辑。
    // å‚数:无。
    // è¿”回值:无。
    onInited() {},
    // æ–¹æ³•目的:查询前处理查询条件。
    // å‚数:param æŸ¥è¯¢å‚数对象。
    // è¿”回值:true ç»§ç»­æŸ¥è¯¢ï¼Œfalse ç»ˆæ­¢æŸ¥è¯¢ã€‚
    searchBefore(param) {
      return true;
    },
    // æ–¹æ³•目的:查询后处理返回结果。
    // å‚数:result åŽç«¯è¿”回数据。
    // è¿”回值:true ç»§ç»­æ¸²æŸ“,false ç»ˆæ­¢åŽç»­å¤„理。
    searchAfter(result) {
      return true;
    },
    // æ–¹æ³•目的:新增前校验或补充提交字段。
    // å‚数:formData è¡¨å•提交对象。
    // è¿”回值:true ç»§ç»­æäº¤ï¼Œfalse ç»ˆæ­¢æäº¤ã€‚
    addBefore(formData) {
      return true;
    },
    // æ–¹æ³•目的:编辑前校验或补充提交字段。
    // å‚数:formData è¡¨å•提交对象。
    // è¿”回值:true ç»§ç»­æäº¤ï¼Œfalse ç»ˆæ­¢æäº¤ã€‚
    updateBefore(formData) {
      return true;
    },
    // æ–¹æ³•目的:点击行时扩展处理。
    // å‚数:row è¡Œæ•°æ®ï¼Œcolumn åˆ—配置,event åŽŸå§‹äº‹ä»¶ã€‚
    // è¿”回值:无。
    rowClick({ row, column, event }) {},
    // æ–¹æ³•目的:编辑弹窗打开后执行扩展逻辑。
    // å‚数:row å½“前行数据。
    // è¿”回值:无。
    modelOpenAfter(row) {},
  },
};
export default extension;
Code/WCS/WIDESEAWCS_Client/src/router/viewGird.js
@@ -1,4 +1,3 @@
let viewgird = [
  {
    path: '/Sys_Log',
@@ -15,7 +14,6 @@
    name: 'permission',
    component: () => import('@/views/system/Permission.vue')
  },
  {
    path: '/Sys_Dictionary',
    name: 'Sys_Dictionary',
@@ -25,38 +23,62 @@
    path: '/Sys_Role',
    name: 'Sys_Role',
    component: () => import('@/views/system/Sys_Role.vue')
  }, {
  },
  {
    path: '/Sys_Role1',
    name: 'Sys_Role1',
    component: () => import('@/views/system/Sys_Role1.vue')
  }, {
  },
  {
    path: '/Sys_DictionaryList',
    name: 'Sys_DictionaryList',
    component: () => import('@/views/system/Sys_DictionaryList.vue')
  }, {
  },
  {
    path: '/Sys_Tenant',
    name: 'Sys_Tenant',
    component: () => import('@/views/system/Sys_Tenant.vue')
  },
  {
    path: '/deviceInfo',
    name: 'deviceInfo',
    component: () => import('@/views/quartzJob/deviceInfo.vue')
  }, {
  },
  {
    path: '/deviceProtocol',
    name: 'deviceProtocol',
    component: () => import('@/views/quartzJob/deviceProtocol.vue')
  }, {
  },
  {
    path: '/deviceProtocolDetail',
    name: 'deviceProtocolDetail',
    component: () => import('@/views/quartzJob/deviceProtocolDetail.vue')
  }, {
  },
  {
    path: '/dispatchInfo',
    name: 'dispatchInfo',
    component: () => import('@/views/quartzJob/dispatchInfo.vue')
  }, {
  },
  {
    path: '/scheduler',
    name: 'scheduler',
    component: () => import('@/views/quartzJob/scheduler.vue')
  },
  {
    path: '/task',
    name: 'task',
    component: () => import('@/views/taskinfo/task.vue')
  }, {
  },
  {
    path: '/taskHty',
    name: 'taskHty',
    component: () => import('@/views/taskinfo/taskHty.vue')
  },
  {
    path: '/router',
    name: 'router',
    component: () => import('@/views/basicinfo/router.vue')
  }]
  }
];
export default viewgird
export default viewgird;
Code/WCS/WIDESEAWCS_Client/src/views/quartzJob/scheduler.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,136 @@
<template>
  <div class="scheduler-page">
    <el-card shadow="never" class="card-item">
      <template #header>
        <span>调度服务控制</span>
      </template>
      <div class="button-row">
        <el-button type="success" @click="handleSchedulerAction('StartSchedule')">启动调度服务</el-button>
        <el-button type="warning" @click="handleSchedulerAction('StopSchedule')">停止调度服务</el-button>
      </div>
    </el-card>
    <el-card shadow="never" class="card-item">
      <template #header>
        <span>任务调度操作</span>
      </template>
      <el-form :model="jobForm" label-width="110px" class="form-wrap">
        <el-row :gutter="16">
          <el-col :span="8">
            <el-form-item label="任务名称">
              <el-input v-model="jobForm.name" placeholder="请输入任务名称" />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="任务分组">
              <el-input v-model="jobForm.jobGroup" placeholder="请输入任务分组" />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="执行类名">
              <el-input v-model="jobForm.className" placeholder="请输入执行类名" />
            </el-form-item>
          </el-col>
        </el-row>
      </el-form>
      <div class="button-row">
        <el-button type="primary" @click="handleSchedulerAction('PauseJob')">暂停任务</el-button>
        <el-button type="primary" @click="handleSchedulerAction('ResumeJob')">恢复任务</el-button>
        <el-button type="primary" @click="handleSchedulerAction('ExecuteJob')">立即执行</el-button>
        <el-button type="danger" @click="handleSchedulerAction('DeleteScheduleJob')">删除任务</el-button>
      </div>
    </el-card>
  </div>
</template>
<script>
import { defineComponent, ref } from "vue";
import { ElMessage } from "element-plus";
import http from "@/api/http";
export default defineComponent({
  setup() {
    const jobForm = ref({
      name: "",
      jobGroup: "",
      className: "",
      assemblyName: "",
      intervalSecond: 0,
      beginTime: "",
      endTime: "",
      remark: "",
    });
    /**
     * æ–¹æ³•目的:校验任务级调度操作所需参数,避免提交无效请求。
     * å‚数含义:无(直接读取当前 jobForm)。
     * è¿”回值:true è¡¨ç¤ºæ ¡éªŒé€šè¿‡ï¼›false è¡¨ç¤ºæ ¡éªŒå¤±è´¥å¹¶å¼¹å‡ºæç¤ºã€‚
     * å¼‚常处理:本地校验不抛异常,通过消息提示引导用户补全关键字段。
     */
    const validateJobPayload = () => {
      if (!jobForm.value.name || !jobForm.value.jobGroup || !jobForm.value.className) {
        ElMessage.error("请先填写任务名称、任务分组、执行类名");
        return false;
      }
      return true;
    };
    /**
     * æ–¹æ³•目的:统一处理调度控制请求,兼容 GET ä¸Ž POST ä¸¤ç±»æŽ¥å£ã€‚
     * å‚数含义:action ä¸ºè°ƒåº¦åŠ¨ä½œåç§°ï¼Œå¯¹åº”åŽç«¯ api/Scheduler/{action}。
     * è¿”回值:Promise<void>,执行完成后通过消息提示结果。
     * å…³é”®é€»è¾‘:
     * 1. å…¨å±€åŠ¨ä½œï¼ˆå¯åŠ¨/停止)走 GET。
     * 2. ä»»åŠ¡åŠ¨ä½œï¼ˆæš‚åœ/恢复/执行/删除)走 POST,并提交 jobForm。
     * 3. æŽ¥å£è¿”回 status=false æ—¶æç¤ºåŽç«¯ message。
     * å¼‚常处理:使用 try-catch æ•获请求异常并保留错误上下文输出。
     */
    const handleSchedulerAction = async (action) => {
      try {
        const isGlobalAction = action === "StartSchedule" || action === "StopSchedule";
        if (!isGlobalAction && !validateJobPayload()) {
          return;
        }
        let result;
        if (isGlobalAction) {
          result = await http.get(`/api/Scheduler/${action}`, {}, true);
        } else {
          result = await http.post(`/api/Scheduler/${action}`, jobForm.value, true);
        }
        if (result && result.status) {
          ElMessage.success(result.message || "操作成功");
          return;
        }
        ElMessage.error((result && result.message) || "操作失败");
      } catch (error) {
        console.error("调度请求异常", { action, jobForm: jobForm.value, error });
        ElMessage.error("调度请求失败,请稍后重试");
      }
    };
    return {
      jobForm,
      handleSchedulerAction,
    };
  },
});
</script>
<style scoped>
.scheduler-page {
  padding: 12px;
}
.card-item {
  margin-bottom: 12px;
}
.button-row {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
}
</style>
Code/WCS/WIDESEAWCS_Client/src/views/system/Sys_Tenant.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,238 @@
<template>
  <view-grid
    ref="grid"
    :columns="columns"
    :detail="detail"
    :editFormFields="editFormFields"
    :editFormOptions="editFormOptions"
    :searchFormFields="searchFormFields"
    :searchFormOptions="searchFormOptions"
    :table="table"
    :extend="extend"
  >
  </view-grid>
</template>
<script>
import extend from "@/extension/system/Sys_Tenant.js";
import { ref, defineComponent } from "vue";
export default defineComponent({
  setup() {
    const tenantTypeOptions = [
      { key: 0, value: "默认租户" },
      { key: 1, value: "企业租户" },
      { key: 2, value: "平台租户" },
    ];
    const dbTypeOptions = [
      { key: 0, value: "SqlServer" },
      { key: 1, value: "MySql" },
      { key: 2, value: "PostgreSQL" },
      { key: 3, value: "Oracle" },
      { key: 4, value: "Sqlite" },
    ];
    const statusOptions = [
      { key: 1, value: "启用" },
      { key: 0, value: "禁用" },
    ];
    const table = ref({
      key: "tenantId",
      footer: "Foots",
      cnName: "租户管理",
      name: "Sys_Tenant",
      url: "/tenant/",
      sortName: "tenantId",
    });
    const editFormFields = ref({
      tenantName: "",
      tenantType: 0,
      dbType: 0,
      connectionString: "",
      status: 1,
      remark: "",
    });
    const editFormOptions = ref([
      [
        { title: "租户名称", required: true, field: "tenantName", type: "string" },
        {
          title: "租户类型",
          required: true,
          field: "tenantType",
          type: "select",
          data: tenantTypeOptions,
        },
      ],
      [
        {
          title: "数据库类型",
          required: true,
          field: "dbType",
          type: "select",
          data: dbTypeOptions,
        },
        {
          title: "状态",
          required: true,
          field: "status",
          type: "select",
          data: statusOptions,
        },
      ],
      [
        {
          title: "连接字符串",
          required: true,
          field: "connectionString",
          colSize: 12,
          type: "textarea",
        },
      ],
      [{ title: "备注", field: "remark", colSize: 12, type: "textarea" }],
    ]);
    const searchFormFields = ref({
      tenantName: "",
      tenantType: "",
      dbType: "",
      status: "",
    });
    const searchFormOptions = ref([
      [
        { title: "租户名称", field: "tenantName", type: "like" },
        {
          title: "租户类型",
          field: "tenantType",
          type: "select",
          data: tenantTypeOptions,
        },
      ],
      [
        {
          title: "数据库类型",
          field: "dbType",
          type: "select",
          data: dbTypeOptions,
        },
        {
          title: "状态",
          field: "status",
          type: "select",
          data: statusOptions,
        },
      ],
    ]);
    const columns = ref([
      {
        field: "tenantId",
        title: "租户ID",
        type: "int",
        width: 110,
        readonly: true,
        require: true,
        align: "left",
      },
      {
        field: "tenantName",
        title: "租户名称",
        type: "string",
        width: 180,
        require: true,
        align: "left",
      },
      {
        field: "tenantType",
        title: "租户类型",
        type: "int",
        width: 120,
        align: "left",
        bind: { key: "tenantTypeLocal", data: tenantTypeOptions },
      },
      {
        field: "dbType",
        title: "数据库类型",
        type: "int",
        width: 140,
        align: "left",
        bind: { key: "dbTypeLocal", data: dbTypeOptions },
      },
      {
        field: "status",
        title: "状态",
        type: "int",
        width: 100,
        align: "left",
        bind: { key: "tenantStatusLocal", data: statusOptions },
      },
      {
        field: "connectionString",
        title: "连接字符串",
        type: "string",
        width: 320,
        align: "left",
      },
      {
        field: "creater",
        title: "创建人",
        type: "string",
        width: 100,
        align: "left",
      },
      {
        field: "createDate",
        title: "创建时间",
        type: "datetime",
        width: 160,
        align: "left",
        sort: true,
      },
      {
        field: "modifier",
        title: "修改人",
        type: "string",
        width: 100,
        align: "left",
      },
      {
        field: "modifyDate",
        title: "修改时间",
        type: "datetime",
        width: 160,
        align: "left",
        sort: true,
      },
      {
        field: "remark",
        title: "备注",
        type: "string",
        width: 180,
        align: "left",
      },
    ]);
    const detail = ref({
      cnName: "",
      table: "",
      columns: [],
      sortName: "",
      key: "",
    });
    return {
      table,
      extend,
      editFormFields,
      editFormOptions,
      searchFormFields,
      searchFormOptions,
      columns,
      detail,
    };
  },
});
</script>
Code/WCS/WIDESEAWCS_Client/src/views/taskinfo/taskHty.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,242 @@
<template>
  <view-grid
    ref="grid"
    :columns="columns"
    :detail="detail"
    :editFormFields="editFormFields"
    :editFormOptions="editFormOptions"
    :searchFormFields="searchFormFields"
    :searchFormOptions="searchFormOptions"
    :table="table"
    :extend="extend"
  >
  </view-grid>
</template>
<script>
import extend from "@/extension/taskinfo/taskHty.js";
import { ref, defineComponent } from "vue";
export default defineComponent({
  setup() {
    const table = ref({
      key: "taskId",
      footer: "Foots",
      cnName: "任务历史",
      name: "taskHty",
      url: "/Task_Hty/",
      sortName: "insertTime",
    });
    const editFormFields = ref({});
    const editFormOptions = ref([]);
    const searchFormFields = ref({
      taskNum: "",
      palletCode: "",
      roadway: "",
      sourceAddress: "",
      targetAddress: "",
      currentAddress: "",
      nextAddress: "",
      creater: "",
      createDate: "",
      operateType: "",
      insertTime: "",
    });
    const searchFormOptions = ref([
      [
        { title: "任务号", field: "taskNum", type: "int" },
        { title: "托盘编码", field: "palletCode", type: "like" },
        {
          title: "任务类型",
          field: "taskType",
          type: "selectList",
          dataKey: "taskType",
          data: [],
        },
        {
          title: "任务状态",
          field: "taskStatus",
          type: "selectList",
          dataKey: "taskState",
          data: [],
        },
      ],
      [
        { title: "起始地址", field: "sourceAddress", type: "like" },
        { title: "目标地址", field: "targetAddress", type: "like" },
        { title: "当前位置", field: "currentAddress", type: "like" },
        { title: "下一位置", field: "nextAddress", type: "like" },
      ],
      [
        { title: "巷道号", field: "roadway", type: "like" },
        { title: "创建人", field: "creater", type: "like" },
        { title: "操作类型", field: "operateType", type: "like" },
        { title: "移入历史时间", field: "insertTime", type: "datetime" },
      ],
    ]);
    const columns = ref([
      {
        field: "taskId",
        title: "TaskId",
        type: "int",
        width: 90,
        hidden: true,
        readonly: true,
        require: true,
        align: "left",
      },
      {
        field: "taskNum",
        title: "任务号",
        type: "int",
        width: 100,
        align: "left",
      },
      {
        field: "palletCode",
        title: "托盘编码",
        type: "string",
        width: 150,
        align: "left",
      },
      {
        field: "roadway",
        title: "巷道号",
        type: "string",
        width: 90,
        align: "left",
      },
      {
        field: "taskType",
        title: "任务类型",
        type: "int",
        width: 90,
        align: "left",
        bind: { key: "taskType", data: [] },
      },
      {
        field: "taskStatus",
        title: "任务状态",
        type: "int",
        width: 120,
        align: "left",
        bind: { key: "taskState", data: [] },
      },
      {
        field: "sourceAddress",
        title: "起始地址",
        type: "string",
        width: 120,
        align: "left",
      },
      {
        field: "targetAddress",
        title: "目标地址",
        type: "string",
        width: 120,
        align: "left",
      },
      {
        field: "currentAddress",
        title: "当前位置",
        type: "string",
        width: 120,
        align: "left",
      },
      {
        field: "nextAddress",
        title: "下一位置",
        type: "string",
        width: 120,
        align: "left",
      },
      {
        field: "grade",
        title: "优先级",
        type: "int",
        width: 90,
        align: "left",
      },
      {
        field: "sourceId",
        title: "原表主键",
        type: "int",
        width: 100,
        align: "left",
      },
      {
        field: "operateType",
        title: "操作类型",
        type: "string",
        width: 120,
        align: "left",
      },
      {
        field: "insertTime",
        title: "移入历史时间",
        type: "datetime",
        width: 170,
        align: "left",
        sort: true,
      },
      {
        field: "creater",
        title: "创建人",
        type: "string",
        width: 100,
        align: "left",
      },
      {
        field: "createDate",
        title: "创建时间",
        type: "datetime",
        width: 160,
        align: "left",
      },
      {
        field: "modifier",
        title: "修改人",
        type: "string",
        width: 100,
        align: "left",
      },
      {
        field: "modifyDate",
        title: "修改时间",
        type: "datetime",
        width: 160,
        align: "left",
      },
      {
        field: "remark",
        title: "备注",
        type: "string",
        width: 120,
        align: "left",
      },
    ]);
    const detail = ref({
      cnName: "",
      table: "",
      columns: [],
      sortName: "",
      key: "",
    });
    return {
      table,
      extend,
      editFormFields,
      editFormOptions,
      searchFormFields,
      searchFormOptions,
      columns,
      detail,
    };
  },
});
</script>