从零开始使用vue2+element搭建后台管理系统(框架布局+面包屑+内容页加载实现)

发布时间 2023-09-12 17:58:55作者: 芝麻小仙女

先在components下分别创建侧边栏、顶部、布局等组件,用于全局配置:

CommonAside.vue

<template>
  <el-menu
    default-active="1-4-1"
    class="el-menu-vertical"
    @open="handleOpen"
    @close="handleClose"
    :collapse="isCollapse"
    background-color="#f5f5f5"
    text-color="#555555"
    active-text-color="#409eff"
  >
    <el-menu-item
      @click="clickItem(item)"
      v-for="item in noChildren"
      :key="item.name"
      :index="item.name"
    >
      <!-- 这里是字体图标,用模板字符串拼接,注意要动态绑定 -->
      <i :class="`el-icon-${item.icon}`"></i>
      <span slot="title">{{ item.label }}</span>
    </el-menu-item>
    <el-submenu
      v-for="item in hasChildren"
      :key="item.label"
      :index="item.label"
    >
      <template slot="title">
        <i :class="`el-icon-${item.icon}`"></i>
        <span slot="title">{{ item.label }}</span>
      </template>
      <el-menu-item-group v-for="subItem in item.children" :key="subItem.name">
        <el-menu-item @click="clickItem(subItem)" :index="subItem.name">{{
          subItem.label
        }}</el-menu-item>
      </el-menu-item-group>
    </el-submenu>
  </el-menu>
</template>

<style scoped lang="scss">
.el-menu-vertical:not(.el-menu--collapse) {
  width: 200px;
  min-height: 400px;
}

.el-menu {
  height: 100vh;
  border-right: none;
}
</style>

<script>
export default {
  data() {
    return {};
  },
  methods: {
    handleOpen(key, keyPath) {
      console.log(key, keyPath);
    },
    handleClose(key, keyPath) {
      console.log(key, keyPath);
    },
    clickItem(item) {
      // 防止自己跳自己的报错
      if (
        this.$route.path !== item.path &&
        !(this.$route.path === "/home" && item.path === "/")
      ) {
        this.$router.push(item.path);
      }
      // 面包屑
      this.$store.commit("basic/SelectMenu", item);
    },
  },
  computed: {
    noChildren() {
      // 如果没有children则返回true,会被过滤器留下
      return this.MenuData.filter((item) => !item.children);
    },
    hasChildren() {
      return this.MenuData.filter((item) => item.children);
    },
    // 要放到计算属性,自动计算
    isCollapse() {
      return this.$store.state.basic.isCollapse;
    },
    // 获取菜单
    MenuData() {
      return this.$store.state.basic.menu;
    },
  },
};
</script>

  

CommonHeader.vue

<template>
  <div class="header-container">
    <div class="l-content">
      <i v-if="isCollapse" class="el-icon-s-unfold" @click="handleMenu"></i>
      <i v-else class="el-icon-s-fold" @click="handleMenu"></i>
      <img class="header-icon" src="../assets/logo.png" alt="" />
      <h3>
        {{ isCollapse ? "后台" : "后台管理系统" }}
      </h3>
      <!-- 面包屑 -->
      <el-breadcrumb class="app-breadcrumb" separator="/">
        <transition-group name="breadcrumb">
          <el-breadcrumb-item
            v-for="(item, index) in levelList"
            :key="item.path"
          >
            <span
              v-if="
                item.redirect === 'noRedirect' || index == levelList.length - 1
              "
              class="no-redirect"
              >{{ item.meta.title }}</span
            >
            <a v-else @click.prevent="handleLink(item)">{{
              item.meta.title
            }}</a>
          </el-breadcrumb-item>
        </transition-group>
      </el-breadcrumb>
    </div>
    <div class="r-content">
      <el-dropdown @command="handleClick">
        <span class="el-dropdown-link">
          <img class="user" src="../assets/logo.png" alt="" />
        </span>
        <el-dropdown-menu slot="dropdown">
          <el-dropdown-item>个人信息</el-dropdown-item>
          <el-dropdown-item command="logout">退出</el-dropdown-item>
        </el-dropdown-menu>
      </el-dropdown>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      levelList: null,
    };
  },
  watch: {
    $route() {
      this.getBreadcrumb();
    },
  },
  created() {
    this.getBreadcrumb();
  },
  methods: {
    // 切换菜单伸缩
    handleMenu() {
      this.$store.commit("basic/CollapseMenu");
    },
    // 退出登录
    handleClick(command) {
      if (command === "logout") {
        this.$store.dispatch("user/logout").then(() => {
          this.$router.push({
            name: "/login",
          });
        });
      }
    },
    // 获取面包屑
    getBreadcrumb() {
      // this.$route.matched匹配到一个路由数组
      let matched = this.$route.matched.filter(
        (item) => item.meta && item.meta.title
      );
      const first = matched[0];
      if (!this.isHome(first)) {
        matched = [
          {
            path: "/home",
            meta: { title: "首页", redirect: "/home", path: "/home" },
          },
        ].concat(matched);
      }
      this.levelList = matched.filter(
        (item) => item.meta && item.meta.title && item.meta.breadcrumb !== false
      );
    },
    // 判断是否为主页
    isHome(route) {
      const name = route && route.name;
      if (!name) {
        return false;
      }
      // 对路由进行大小写处理
      return name.trim().toLocaleLowerCase() === "home".toLocaleLowerCase();
    },
    // 转译路由
    pathCompile(path) {
      // 将字符串转化为正则表达式的方法
      var pathToRegexp = require("path-to-regexp");
      // 参阅:https://github.com/PanJiaChen/vue-element-admin/issues/561
      const { params } = this.$route;
      const toPath = pathToRegexp.compile(path);
      return toPath(params);
    },
    // 跳转
    handleLink(item) {
      const { redirect, path } = item;
      if (redirect) {
        this.$router.push(redirect);
        return;
      }
      this.$router.push(this.pathCompile(path));
    },
  },
  computed: {
    isCollapse() {
      return this.$store.state.basic.isCollapse;
    },
  },
};
</script>

<style lang="scss" scoped>
.header-container {
  box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
  height: 60px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 0 20px;

  .el-dropdown-link {
    cursor: pointer;
    color: #409eff;

    .user {
      width: 40px;
      height: 40px;
      border-radius: 50%;
    }
  }
  .header-icon {
    margin: 0 16px;
    height: 48px;
  }
  h3 {
    text-align: center;
    line-height: 48px;
    color: #ffffff;
    font-size: 18px;
    font-weight: 600;
    color: #454545;
  }
}

.l-content {
  display: flex;
  align-items: center;
  cursor: pointer;

  .el-breadcrumb {
    margin-left: 15px;

    .el-breadcrumb__item {
      .el-breadcrumb__inner {
        &.is-link {
          color: #666666;
        }
      }

      &:last-child {
        .el-breadcrumb__inner {
          color: #ffffff;
        }
      }
    }
  }
}
</style>

  

注意头部因为有面包屑,因此需要导入path-to-regexp插件用来处理 url 中地址与参数:yarn add path-to-regexp

 

LayoutView.js

<template>
  <el-container>
    <el-aside width="auto">
    </el-aside>
    <el-container>
      <el-header>
        <common-header />
      </el-header>
      <!-- <common-tags /> -->
      <el-main>
        <div class="page-container-wn">
          <router-view></router-view>
        </div>
      </el-main>
    </el-container>
  </el-container>
</template>

<script>
import CommonAside from "../components/CommonAside.vue";
import CommonHeader from "../components/CommonHeader.vue";
export default {
  data() {
    return {};
  },
  components: {
    CommonAside,
    CommonHeader,
    // CommonTags,
  },
};
</script>

<style lang="scss" scoped>
.el-header {
  padding: 0;
}
</style>

  

就可以对应上一篇博客的router文件夹下的index文件中的路由配置中的父级component

 

同时,我们需要在页面中间的内容部分做一个全局加载的功能,避免element框架loading功能的全屏加载不够友好。在utils文件夹下新建loading.js文件:

import Vue from "vue";
// loading框设置局部刷新,且所有请求完成后关闭loading框
let loading;
let needLoadingRequestCount = 0; // 声明一个对象用于存储请求个数
function startLoading() {
  loading = Vue.prototype.$loading({
    lock: true,
    text: "努力加载中(っ•̀ω•́)っ⁾⁾",
    background: "rgba(255,255,255,.4)",
    target: document.querySelector(".page-container-wn"), // 设置加载动画区域
  });
}
function endLoading() {
  loading.close();
}
export function showPageLoading(target) {
  if (needLoadingRequestCount === 0) {
    startLoading(target);
  }
  needLoadingRequestCount++;
}
export function hidePageLoading() {
  if (needLoadingRequestCount <= 0) return;
  needLoadingRequestCount--;
  if (needLoadingRequestCount === 0) {
    endLoading();
  }
}
export default {
  showPageLoading,
  hidePageLoading,
};

  

然后写一个简单的动态渲染的表单组件,在components文件夹下新增CustomForm.vue,这样就可以用后端返回的表单配置json直接进行渲染啦:

<template>
  <div class="filterPanel">
    <!--是否行内表单-->
    <el-form
      class="form"
      :inline="inline"
      :model="form"
      :rules="rules"
      ref="form"
    >
      <!--标签显示名称-->
      <div class="labelGroup">
        <slot></slot>
        <el-form-item
          v-for="item in formLabel"
          :key="item.model"
          :label="item.label"
          :prop="item.model"
        >
          <!--根据type来显示是什么标签-->
          <!-- 一般输入框 -->
          <el-input
            v-model="form[item.model]"
            :placeholder="item.placeholder || '请输入' + item.label"
            v-if="item.type === 'input'"
          >
          </el-input>
          <!-- 数字输入框 -->
          <el-input
            v-model="form[item.model]"
            :min="0"
            type="number"
            :placeholder="item.placeholder || '请输入' + item.label"
            v-if="item.type === 'number'"
          >
          </el-input>

          <el-autocomplete
            class="inline-input"
            v-model="form[item.model]"
            v-if="item.type === 'searchInput'"
            :fetch-suggestions="
              (queryString, cb) => {
                searchOptionName(queryString, cb, item.opts);
              }
            "
            :placeholder="item.placeholder || '请输入' + item.label"
            :trigger-on-focus="false"
          ></el-autocomplete>

          <el-select
            v-model="form[item.model]"
            :placeholder="item.placeholder || '请选择' + item.label"
            v-if="item.type === 'select'"
          >
            <!--如果是select或者checkbox 、Radio,还需要选项信息-->
            <el-option
              v-for="item in item.opts"
              :key="item.value"
              v-show="item.label"
              :label="item.label"
              :value="item.value"
            ></el-option>
          </el-select>
          <!-- 开关 -->
          <el-switch
            v-model="form[item.model]"
            v-if="item.type === 'switch'"
          ></el-switch>
          <!-- 单个日期选择器 -->
          <el-date-picker
            v-model="form[item.model]"
            type="date"
            placeholder="选择日期"
            v-if="item.type === 'date'"
            value-format="yyyy-MM-dd"
          >
          </el-date-picker>
          <!-- 日期范围选择器 -->
          <el-date-picker
            v-model="form[item.model]"
            range-separator="至"
            start-placeholder="开始日期"
            end-placeholder="结束日期"
            type="datetimerange"
            placeholder="选择日期"
            v-if="item.type === 'dateRange'"
            value-format="yyyy-MM-dd"
          >
          </el-date-picker>
          <!-- 日期时间选择器 -->
          <el-date-picker
            v-model="form[item.model]"
            type="datetimerange"
            range-separator="至"
            v-if="item.type === 'dateTimeRange'"
            start-placeholder="开始日期"
            end-placeholder="结束日期"
            value-format="yyyy-MM-dd HH:mm:ss"
          >
          </el-date-picker>
        </el-form-item>
      </div>
      <div class="btnGroup">
        <el-form-item>
          <el-button type="primary" @click="search">搜索</el-button>
          <el-button @click="reset">重置</el-button>
        </el-form-item>
      </div>
    </el-form>
  </div>
</template>

<script>
//   import Bus from "./formEventBus";
export default {
  name: "CustomForm",
  //inline 属性可以让表单域变为行内的表单域
  //form 表单数据 formLabel 是标签数据
  props: {
    inline: {
      type: Boolean,
      default: true,
    },

    formLabel: Array,
    rules: Object,
  },
  watch: {
    formLabel: {
      handler(newVal) {
        if (newVal) {
          newVal.forEach((item) => {
            this.$set(this.form, item.model, item.default || "");
          });
        }
      },
      immediate: true,
    },
  },
  data() {
    return {
      form: {},
    };
  },
  mounted() {
    let obj = {};
    this.formLabel.forEach(async (item, index) => {
      if (item.optsConfig) {
        //获取动态下拉选项
        let val = await this.getOpts(item);
        this.$set(this.formLabel[index], "opts", val);
        obj[item.model] = val;
        this.$emit("getSelect", obj);
      }
    });
  },
  methods: {
    searchOptionName(queryString, cb, data) {
      var restaurants = data;

      var results = queryString
        ? restaurants.filter(this.createFilter(queryString))
        : restaurants;
      cb(results);
    },

    createFilter(queryString) {
      return (restaurant) => {
        return (
          restaurant.value.toLowerCase().indexOf(queryString.toLowerCase()) !=
          -1
        );
      };
    },

    reset() {
      this.form.pageNum = 1;
      this.$refs["form"].resetFields();
      this.$emit("confirm", this.form);
      // Bus.$emit('getParam', this.form);//给Table传查询参数
    },
    search() {
      this.form.pageNum = 1;

      this.$emit("confirm", this.form);
      // Bus.$emit('getParam', this.form);//给Table传查询参数
    },
    async getOpts(oData) {
      let { api, param, labelKey, valueKey } = oData.optsConfig;
      let opts = [];
      const res = await api(param);
      if (res.code === 1) {
        opts = res.data.map((item) => {
          if (oData.model === "goodsSku" && item["goodsSku"] != "") {
            //SKU特殊处理

            const itemObj = JSON.parse(item.goodsSku);

            return {
              label: itemObj[labelKey].join("-"),
              value: itemObj[valueKey],
              ...item,
            };
          } else {
            return {
              label: item[labelKey],
              value: item[valueKey],
              ...item,
            };
          }
        });
      }
      return opts;
    },
  },
};
</script>
<style lang="scss">
.filterPanel {
  margin-bottom: 20px;
  padding: 20px 20px 0 20px;

  .form {
    display: flex;
    justify-content: space-between;

    .btnGroup {
      min-width: 150px;
      display: flex;
      flex-direction: row;
      flex-wrap: nowrap;
    }
  }

  .el-input__inner {
    background-color: #fff;
    height: 33px;
    line-height: 33px;
  }

  .filterPanel {
    width: 100%;

    border-radius: 4px;
    background: #f7f8fa;

    padding-top: 20px;
    padding-right: 20px;
  }

  .btnContainer {
    margin-bottom: 20px;
  }

  .el-button {
    height: 32px !important;
    padding: 0 16px;
  }

  .el-date-editor .el-range__icon,
  .el-range-separator {
    line-height: 26px;
  }
}
</style>

  

接下来整一个页面看看效果。首先在api文件夹下新增此页面的api文件,我称之为order.js:

/**
 * 订单管理接口列表
 */

export function orderInit() {
  return {
    code: 10000,
    msg: "请求成功",
    data: {
      searchList: [
        {
          label: "",
          model: "time",
          type: "dateTimeRange",
        },
        {
          label: "",
          placeholder: "请选择游戏",
          model: "game",
          type: "select",
          opts: [
            {
              label: "游戏1",
              value: "game1",
            },
            {
              label: "游戏2",
              value: "game2",
            },
          ],
        },
        {
          label: "",
          placeholder: "全部订单",
          model: "order",
          type: "select",
          opts: [
            {
              label: "订单1",
              value: "order1",
            },
            {
              label: "订单2",
              value: "order2",
            },
          ],
        },
        {
          label: "",
          placeholder: "订单类型",
          model: "orderType",
          type: "select",
          opts: [
            {
              label: "平台订单号",
              value: "orderId",
            },
            {
              label: "游戏订单号",
              value: "gameOrderId",
            },
          ],
        },
        {
          label: "",
          placeholder: "输入文本",
          model: "text",
          type: "input",
        },
      ],
      searchRules: {},
      tableHeader: [
        { id: "orderId", name: "平台订单号" },
        { id: "gameName", name: "游戏名称" },
        { id: "userName", name: "用户名" },
        { id: "avaName", name: "区服/角色名" },
        { id: "sum", name: "金额" },
        { id: "createTime", name: "创建时间" },
        { id: "payStatus", name: "支付状态" },
        { id: "noticeStatus", name: "通知状态" },
      ],
    },
  };
}

export function orderList(params) {
  console.log(params, "params");
  return {
    code: 10000,
    msg: "请求成功",
    data: {
      tableData: [
        {
          id: "1212121",
          orderId: "xxxx",
          gameName: "华夏回事路",
          userName: "138xxxx2323",
          avaName: "xxxxxxxxx",
          sum: "648",
          createTime: "2023-05-26 00:00:00",
          payStatus: "成功",
          noticeStatus: "成功",
        },
        {
          id: "12121221",
          orderId: "xxxx",
          gameName: "华夏回事路2",
          userName: "138xxxx2323",
          avaName: "xxxxxxxxx",
          sum: "648",
          createTime: "2023-05-26 00:00:00",
          payStatus: "成功",
          noticeStatus: "成功",
        },
        {
          id: "12121321",
          orderId: "xxxx",
          gameName: "华夏回3事路",
          userName: "138xxxx2323",
          avaName: "xxxxxxxxx",
          sum: "648",
          createTime: "2023-05-26 00:00:00",
          payStatus: "成功",
          noticeStatus: "成功",
        },
      ],
      page: {
        pageSize: 10,
        current: 1,
        total: 200,
      },
    },
  };
}

  

当然接口是假的,接下来写页面,在views文件夹下新增order文件夹,新建index.vue文件:

<template>
  <div>
    <el-card class="box-card search-card">
      <CustomForm
        :formLabel="searchList"
        :rules="searchRules"
        @confirm="handleSearch"
      />
    </el-card>
    <el-card class="box-card">
      <el-table
        :data="tableData"
        border
        v-loading="tableLoading"
        style="width: 100%; margin-bottom: 16px"
      >
        <el-table-column
          v-for="item in tableHeader || []"
          :key="item.id"
          :prop="item.id"
          :label="item.name"
        />
        <el-table-column fixed="right" label="操作" width="120">
          <template slot-scope="scope">
            <el-button @click="handleDetail(scope.row)" type="text"
              >详情</el-button
            >
            <el-button type="text" disabled>补单</el-button>
          </template>
        </el-table-column>
      </el-table>
      <el-pagination
        @size-change="handleSizeChange"
        @current-change="handleCurrentChange"
        :current-page="tablePage.current || 1"
        :page-size="tablePage.current || 10"
        layout="total, sizes, prev, pager, next"
        :total="tablePage.total || total"
      >
        <!-- :page-sizes="[10, 20, 50, 100]" -->
      </el-pagination>
    </el-card>
    <DataDetail
      :rowInfo="rowInfo"
      :visible="drawerVisible"
      @updateVisible="updateVisible"
    />
  </div>
</template>
<script>
import CustomForm from "@/components/CustomForm.vue";
import DataDetail from "./DataDetail";
import { orderInit, orderList } from "@/api/order";
import { showPageLoading, hidePageLoading } from "@/utils/loading";

export default {
  name: "OrderView",
  data() {
    return {
      formInline: {
        user: "",
        region: "",
      },
      tableHeader: [],
      tableData: [],
      searchList: [],
      searchRules: {},
      tablePage: {
        pageSize: 10,
        current: 1,
        total: 400,
      },
      searchData: {},
      rowInfo: {},
      drawerVisible: false,
      tableLoading: false,
    };
  },
  mounted() {
    this.init();
    this.handleSearch();
  },
  methods: {
    // 初始化
    async init() {
      try {
        showPageLoading(); // 开启
        const res = await orderInit();
        if (res.code !== 10000) this.$message.error(res.msg);
        if (res.code === 10000 && res.data) {
          this.searchList = res.data.searchList;
          this.searchRules = res.data.searchRules;
          this.tableHeader = res.data.tableHeader;
        }
      } finally {
        setTimeout(() => {
          hidePageLoading();
        }, 2000);
      }
    },
    // 查询列表数据
    async handleSearch(data) {
      try {
        const tableParams = { ...this.tablePage, ...(data || {}) };
        this.searchData = { ...tableParams };
        this.tableLoading = true;
        const res = await orderList(tableParams);
        if (res.code !== 10000) this.$message.error(res.msg);
        if (res.code === 10000 && res.data) {
          this.tableData = res.data.tableData;
          this.tablePage = { ...res.data.page };
        }
      } finally {
        this.tableLoading = false;
      }
    },
    // 查看详情
    handleDetail(data) {
      this.rowInfo = data;
      this.drawerVisible = true;
    },
    // 切换每页展示条数
    handleSizeChange(val) {
      this.tablePage = { ...this.tablePage, pageSize: val };
      this.handleSearch({
        ...this.searchData,
        ...this.tablePage,
        pageSize: val,
      });
    },
    // 切换页码
    handleCurrentChange(val) {
      this.tablePage = { ...this.tablePage, current: val };
      this.handleSearch({
        ...this.searchData,
        ...this.tablePage,
        current: val,
      });
    },
    updateVisible() {
      this.drawerVisible = !this.drawerVisible;
    },
  },
  components: {
    CustomForm,
    DataDetail,
  },
};
</script>

<style lang="scss" scoped>
.search-card {
  margin-bottom: 16px;
}
</style>

  

可以看到引入了@/utils/loading中导出的两个方法,并在初始化接口中进行了使用(写了个2秒的setTimeOut看效果)

然后新建详情抽屉组件,依旧在order文件夹下,新建DataDetail.vue:

<template>
  <el-drawer :visible.sync="showDrawer" size="45%" title="详情">
    <!-- @tab-click="handleCheckTab" -->
    <el-tabs v-model="activeName" style="margin: 24px">
      <el-tab-pane label="订单详情" name="first">
        <el-descriptions :column="2" :labelStyle="{ fontWeight: 'bold' }">
          <el-descriptions-item
            v-for="item in data.first || []"
            :key="item.key"
            :label="item.label"
            >{{ item.value }}</el-descriptions-item
          >
        </el-descriptions>
      </el-tab-pane>
      <el-tab-pane label="支付回调" name="second">
        <el-table
          :data="data.second?.tableData || []"
          border
          style="width: 100%; margin-bottom: 16px"
        >
          <el-table-column
            v-for="item in data.second?.tableHeader || []"
            :key="item.id"
            :prop="item.id"
            :label="item.name"
          >
          </el-table-column>
        </el-table>
      </el-tab-pane>
      <el-tab-pane label="通知游戏" name="third">
        <el-table
          :data="data.third?.tableData || []"
          border
          style="width: 100%; margin-bottom: 16px"
        >
          <el-table-column
            v-for="item in data.third?.tableHeader || []"
            :key="item.id"
            :prop="item.id"
            :label="item.name"
          >
          </el-table-column>
        </el-table>
      </el-tab-pane>
    </el-tabs>
  </el-drawer>
</template>
<script>
export default {
  name: "DataDetail",
  props: {
    visible: {
      type: Boolean,
      default: false,
    },
    rowInfo: Object,
  },
  data() {
    return {
      activeName: "first",
      data: {},
    };
  },
  computed: {
    showDrawer: {
      get() {
        return this.visible;
      },
      set(val) {
        this.$emit("updateVisible", val);
      },
    },
  },
  mounted() {
    this.data = {
      first: [
        { key: "orderId", label: "平台订单号", value: "xxxxx" },
        { key: "orderId2", label: "支付消息", value: "xxxxx" },
        { key: "orderId3", label: "支付方式", value: "支付宝" },
        { key: "orderId4", label: "游戏名称", value: "xxxxx" },
        { key: "orderId5", label: "支付渠道单号", value: "xxxxxxxxxxxxxxx" },
        { key: "orderId6", label: "订单金额", value: "648.00" },
        { key: "orderId7", label: "支付状态", value: "成功" },
        { key: "orderId8", label: "通知游戏状态", value: "成功" },
        { key: "orderId9", label: "游戏区服", value: "xxx1服" },
        { key: "orderId10", label: "游戏角色名", value: "ABB" },
        { key: "orderId11", label: "游戏订单号", value: "xxxxxxxxxx" },
        { key: "orderId12", label: "商品名称", value: "648礼包" },
        { key: "orderId13", label: "回调地址", value: "https://xxxx.ccc.com" },
      ],
      second: {
        tableHeader: [
          { id: "xxTime", name: "回调时间" },
          { id: "xxStatus", name: "回调状态" },
        ],
        tableData: [
          { id: "12121212", xxTime: "2022-05-21 23-33", xxStatus: "成功" },
          { id: "12134321212", xxTime: "2022-05-21 13-33", xxStatus: "失败" },
          { id: "1212221212", xxTime: "2022-05-21 23-23", xxStatus: "成功" },
        ],
      },
      third: {
        tableHeader: [
          { id: "xxTime", name: "通知时间" },
          { id: "xxStatus", name: "通知状态" },
        ],
        tableData: [
          { id: "12121212", xxTime: "2022-05-21 23-33", xxStatus: "成功" },
          { id: "12121212", xxTime: "2022-05-21 13-33", xxStatus: "失败" },
          { id: "12121212", xxTime: "2022-05-21 23-23", xxStatus: "成功" },
        ],
      },
    };
  },
  methods: {
    // handleCheckTab(val) {
    //   console.log(val);
    // },
  },
};
</script>

  

看看效果:

 

 查看详情: