Bläddra i källkod

新增TreeTable组件

Yue 3 år sedan
förälder
incheckning
28d22287ca

+ 1 - 3
auto-imports.d.ts

@@ -4,9 +4,7 @@
 // Generated by unplugin-auto-import
 export {}
 declare global {
-
-}
-onst EffectScope: typeof import('vue')['EffectScope']
+  const EffectScope: typeof import('vue')['EffectScope']
   const computed: typeof import('vue')['computed']
   const createApp: typeof import('vue')['createApp']
   const customRef: typeof import('vue')['customRef']

+ 3 - 0
components.d.ts

@@ -72,7 +72,10 @@ declare module '@vue/runtime-core' {
     TableItemsPerPageSelect: typeof import('./src/components/Table/table-partials/table-content/table-footer/TableItemsPerPageSelect.vue')['default']
     TablePagination: typeof import('./src/components/Table/table-partials/table-content/table-footer/TablePagination.vue')['default']
     TableRightFixed: typeof import('./src/components/Table/table-partials/table-content/table-fixed/TableRightFixed.vue')['default']
+    TableTreeChildrenRow: typeof import('./src/components/Table/table-partials/table-content/table-body/TableTreeChildrenRow.vue')['default']
+    TableTreeRow: typeof import('./src/components/Table/table-partials/table-content/table-body/TableTreeRow.vue')['default']
     TextFormatting: typeof import('./src/components/devs/TextFormatting.vue')['default']
     VbDataTable: typeof import('./src/components/Table/VbDataTable.vue')['default']
+    VbTreeTable: typeof import('./src/components/Table/VbTreeTable.vue')['default']
   }
 }

+ 9 - 9
src/App.vue

@@ -48,19 +48,19 @@ export default defineComponent({
 </script>
 
 <style lang="scss">
-@import "bootstrap-icons/font/bootstrap-icons.css";
-@import "apexcharts/dist/apexcharts.css";
-@import "quill/dist/quill.snow.css";
+//@import "bootstrap-icons/font/bootstrap-icons.css";
+//@import "apexcharts/dist/apexcharts.css";
+//@import "quill/dist/quill.snow.css";
 @import "animate.css";
 @import "sweetalert2/dist/sweetalert2.css";
-@import "nouislider/distribute/nouislider.css";
+//@import "nouislider/distribute/nouislider.css";
 @import "@fortawesome/fontawesome-free/css/all.min.css";
-@import "socicon/css/socicon.css";
-@import "line-awesome/dist/line-awesome/css/line-awesome.css";
-@import "dropzone/dist/dropzone.css";
-@import "@vueform/multiselect/themes/default.css";
+//@import "socicon/css/socicon.css";
+//@import "line-awesome/dist/line-awesome/css/line-awesome.css";
+//@import "dropzone/dist/dropzone.css";
+//@import "@vueform/multiselect/themes/default.css";
 @import "prism-themes/themes/prism-shades-of-purple.css";
-@import "element-plus/dist/index.css";
+//@import "element-plus/dist/index.css";
 
 // Main demo style scss
 @import "assets/fonticon/fonticon.css";

+ 10 - 1
src/assets/sass/_common.scss

@@ -1,3 +1,12 @@
+// .app-page{
+//   --bs-app-header-base-border-bottom:1px solid #eee;
+
+//   .app-main{
+//     background-color: #f5f5f5;
+//   }
+// }
+
+
 dl{
   margin-bottom: 8px;
   dt{
@@ -6,4 +15,4 @@ dl{
   dd{
     margin: 0;
   }
-}
+}

+ 119 - 37
src/assets/sass/_table.scss

@@ -1,67 +1,149 @@
 .vb-table{
-  position: relative;
   --table-footer-height:50px;
-  table.table{
-    table-layout: fixed;
-    width: 100%;
+  --table-radius:10px;
+  --table-bg:#fefefe;
+  width: 100%;
+  position: relative;
+  background-color: var(--table-bg);
+  border-radius: var(--table-radius);
+  margin-top: 10px;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
 
+  .table-tool{
+    width: 100%;
+    padding: 15px ;
+    border-radius: var(--table-radius) var(--table-radius) 0 0;
+  }
+  .table-box{
+    position: relative;
+    width: 100%;
+    min-height: 90px;
+    
+    .fixed-columns, .fixed-columns-right {
+      position: absolute;
+      top: 0;
+      height:calc(100% -  10px);
+      box-sizing: border-box;
+      background-color: var(--table-bg);
+      z-index: 1;
+      overflow: hidden!important;
+      &.no-shadow{
+        box-shadow: none;
+      }
+    }
+    .fixed-columns{
+      left:0;
+      box-shadow: 20px -2px 10px -13px #ddd  ;
+     
+    }
+    .fixed-columns-right{
+      right: 0;
+      box-shadow: -20px -2px 10px -13px #ddd  ;
+    }
+    &.radius{
+      border-radius: var(--table-radius) var(--table-radius) 0 0;
+      .fixed-columns, .table-responsive,.fixed-columns-right{
+        border-radius: var(--table-radius) var(--table-radius) 0 0;
+      }
+    }
+    table{
+      table-layout: fixed;
+    }
     thead{
       tr {
         th {
           height: 52px;
-          background-image: linear-gradient(rgba(0,0,0,.02),rgba(0,0,0,.02));
+          background-color: #fff;
+          font-size: 110%;
+          vertical-align: middle;
+          // background-image: linear-gradient(rgba(0,0,0,.02),rgba(0,0,0,.02));
          }
       }
-      &.fixed{
-        tr {
-          th {
-            position:sticky;
-            top:0;
-           }
-        }
-      }
     }
     tbody{
       tr{
         td{
-          padding-top: 6px;
-          padding-bottom: 6px;
+          padding-top: 0px;
+          padding-bottom: 0px;
+          height: 40px;
+          line-height: 40px;
+          
+          .table-action{
+            padding: 2px 4px;
+            margin: 0 4px;
+            color: var(--bs-text-primary);
+            cursor: pointer;
+            &:hover{
+              border-bottom: 2px solid;
+            }
+          }
         }
       }
     }
+
+
   }
 
   .table-footer{
+    width: 100%;
     height: var(--table-footer-height);
     align-items:center;
+    border-radius: 0 0 var(--table-radius) var(--table-radius);
   }
 
-  .fixed-columns, .fixed-columns-right {
-
-    position: absolute;
-    top: 0;
-    height:calc(100% - var(--table-footer-height) - 10px);
-    background-color: #fff;
-    box-sizing: border-box;
-    z-index: 1;
-    overflow: hidden!important;
-    &.no-shadow{
-      box-shadow: none;
-    }
-  }
-  .fixed-columns{
-    left:0;
-    box-shadow: 20px -2px 10px -13px #ddd  ;
-   
-  }
   
-  .fixed-columns-right{
-    right: 0;
-    box-shadow: -20px -2px 10px -13px #ddd  ;
-  }
   tr.text-center{
     th,td{
       text-align: center;
     }
   }
+
+  // &.pagination{
+  //   .fixed-columns,   .fixed-columns-right{
+  //     height:calc(100% - var(--table-footer-height) - 10px);
+  //   }
+  // }
+}
+.vb-table.vb-tree-table{
+  tr{
+    &.hide{
+      display: none;
+    }
+    &.show{
+      display: table-row;
+    }
+    .tree-icon{
+      display: flex;
+      align-items: center;
+
+      .icon{
+        --w:20px;
+        width: var(--w);
+        height:  var(--w);
+        margin-right:8px ;
+        background-color: #3F4254;
+        -webkit-mask-image: url("media/icons/duotune/files/fil012.svg") ;
+        mask-image: url("media/icons/duotune/files/fil012.svg") ;
+        -webkit-mask-size:var(--w);
+        mask-size:var(--w);
+      }
+    }
+    
+    &.hasChildren {
+      &.tr-expand{
+      &>td.tree-icon> .icon{
+        -webkit-mask-image: url("media/icons/duotune/files/fil014.svg");
+        mask-image: url("media/icons/duotune/files/fil014.svg");
+        }
+      }
+      &.tr-collapse{
+        &>td.tree-icon> .icon{
+          -webkit-mask-image: url("media/icons/duotune/files/fil013.svg");
+          mask-image: url("media/icons/duotune/files/fil013.svg");
+        }
+      }
+    }
+  }
 }

+ 32 - 8
src/components/Table/VbDataTable.vue

@@ -12,7 +12,7 @@ const props = withDefaults(
     url?: string
     method?: string
     configs?: AxiosRequestConfig
-    queryParms?: any
+    queryParams?: any
     data?: Array<any>
     total?: number //总数
     selectedItems?: Array<any>
@@ -40,6 +40,12 @@ const props = withDefaults(
     thTrClass?: string
     tdTrClass?: string
     rowSpanSuffix?: string //rowsapn 的字段后缀 后台应该组成 `${字段}${后缀}`
+    isTree?: boolean
+    expandDepth?: number
+    intervalLeft?: number
+    keyField?: string
+    parentField?: string
+    childrenField?: string
   }>(),
   {
     currentPage: 1,
@@ -62,16 +68,19 @@ const props = withDefaults(
     fixedNumber: 0,
     fixedRightNumber: 0,
     emptyTableText: "未查询到数据",
-    tableBoxClass: "bg-white w-100",
+    tableBoxClass: "w-100",
     rowSpanSuffix: "_rowSpan",
+    isTree: false,
+    childrenField: "children",
   }
 )
 const emits = defineEmits<{
   (e: "update:selectedItems", v: Array<any>): void
+  (e: "update:currentPage", v: number): void
   (e: "page-change", v: number): void
   (e: "on-items-per-page-change", v: number): void
   (e: "on-sort", v: Sort): void
-  (e: "on-items-change", isChecked: boolean, row: any): void
+  (e: "on-items-change", isChecked: boolean, v: any, row: any): void
   (e: "on-items-select", row: any): void
   (e: "on-items-all-change", isChecked: boolean, rows: Array<any>): void
 }>()
@@ -83,7 +92,7 @@ const itemsInTable = ref(props.pagination ? props.pageArray[0] : 1000)
 const selectedItems = ref(props.selectedItems)
 const remoteTotal = ref<number>(0)
 const remoteData = ref<Array<any>>([])
-const { queryParms } = toRefs(props)
+const { queryParams: queryParms } = toRefs(props)
 const table = ref()
 const dataToDisplay = computed(() => {
   let data = []
@@ -113,6 +122,7 @@ const totalItems = computed(() => {
 })
 
 const pageChange = (page: number) => {
+  emits("update:currentPage", page)
   emits("page-change", page)
   remote()
 }
@@ -124,12 +134,15 @@ const onSelectAll = (isChecked: boolean, rows: Array<any>) => {
   emits("on-items-all-change", isChecked, rows)
 }
 const onItemChange = (isChecked: boolean, v: any, row: any) => {
-  emits("update:selectedItems", v)
-  emits("on-items-change", isChecked, row)
+  emits("update:selectedItems", selectedItems.value)
+  emits("on-items-change", isChecked, v, row)
   if (isChecked) {
     emits("on-items-select", row)
   }
 }
+const onItemsPerPageChange = (v: number) => {
+  emits("on-items-per-page-change", v)
+}
 // const onItemSelect = (selectedItems: any) => {
 //   emits("on-items-select", selectedItems)
 // }
@@ -191,7 +204,7 @@ watch(
 )
 
 watch(
-  () => props.queryParms,
+  () => props.queryParams,
   (val) => {
     console.log("WATCH_Q")
 
@@ -223,8 +236,12 @@ defineExpose({ search, remote })
 </script>
 
 <template>
-  <div class="vb-table" :class="tableBoxClass">
+  <div class="vb-table" :class="`${isTree ? `vb-tree-table` : ``} ${tableBoxClass} ${pagination ? `pagination` : ``}`">
+    <div v-if="$slots[`table-tool`]" class="table-tool">
+      <slot name="table-tool" />
+    </div>
     <TableContent
+      :class="`${$slots[`table-tool`] ? `` : `radius`}`"
       ref="table"
       @on-items-change="onItemChange"
       @on-items-all-change="onSelectAll"
@@ -249,6 +266,12 @@ defineExpose({ search, remote })
       :th-tr-class="thTrClass"
       :td-tr-class="tdTrClass"
       :row-span-suffix="rowSpanSuffix"
+      :is-tree="isTree"
+      :interval-left="intervalLeft"
+      :key-field="keyField"
+      :parent-field="parentField"
+      :children-field="childrenField"
+      :expand-depth="expandDepth"
     >
       <template v-for="(_, name) in $slots" #[name]="{ row: item }">
         <slot :name="name" :row="item" />
@@ -256,6 +279,7 @@ defineExpose({ search, remote })
     </TableContent>
     <TableFooter
       @page-change="pageChange"
+      @on-items-per-page-change="onItemsPerPageChange"
       v-model:current-page="currentPage"
       v-model:itemsPerPage="itemsInTable"
       :pagination="pagination"

+ 141 - 0
src/components/Table/VbTreeTable.vue

@@ -0,0 +1,141 @@
+<script setup lang="ts">
+import { ref, defineProps, withDefaults, defineEmits, onMounted } from "vue"
+import VbDataTable from "./VbDataTable.vue"
+import type { Sort, Header, Scroll } from "@/components/Table/table-partials/models"
+import type { AxiosRequestConfig } from "axios"
+const props = withDefaults(
+  defineProps<{
+    expandDepth?: number
+    intervalLeft?: number
+    keyField?: string
+    parentField?: string
+    childrenField?: string
+    header: Array<Header>
+    url?: string
+    method?: string
+    configs?: AxiosRequestConfig
+    queryParams?: any
+    data?: Array<any>
+    total?: number //总数
+    selectedItems?: Array<any>
+    scroll?: Scroll
+    pagination?: boolean //是否分页
+    currentPage?: number //当前页数
+    //itemsPerPage?: number //每页数量
+    pageArray?: Array<number> //下拉选择每页数量
+    pageDropdown?: boolean //是否下拉选择
+    maxVisibleButtons?: number // 显示分页按钮数量
+    hasCheckbox?: boolean //是否有选择框
+    checkboxField?: string //选择框字段
+    checkMultiple?: boolean //是否多选
+    sortField?: string //排序字段
+    sortOrder?: "asc" | "desc"
+    loading?: boolean //加载中
+    fixedColumn?: boolean //固定列
+    fixedNumber?: number //左边固定列数
+    fixedRightNumber?: number //右边固定列数
+    emptyTableText?: string
+    tableBoxClass?: string
+    tableClass?: string
+    headerClass?: string
+    bodyClass?: string
+    thTrClass?: string
+    tdTrClass?: string
+    rowSpanSuffix?: string //rowsapn 的字段后缀 后台应该组成 `${字段}${后缀}`
+  }>(),
+  {
+    currentPage: 1,
+    method: "get",
+    selectedItems: () => {
+      return []
+    },
+
+    hasCheckbox: false,
+    checkMultiple: false,
+    sortOrder: "asc",
+    pagination: false,
+  }
+)
+const emits = defineEmits<{
+  (e: "update:selectedItems", v: Array<any>): void
+  (e: "update:currentPage", v: number): void
+  (e: "page-change", v: number): void
+  (e: "on-items-per-page-change", v: number): void
+  (e: "on-sort", v: Sort): void
+  (e: "on-items-change", isChecked: boolean, v: any, row: any): void
+  (e: "on-items-select", row: any): void
+  (e: "on-items-all-change", isChecked: boolean, rows: Array<any>): void
+}>()
+const selectedItems = ref(props.selectedItems)
+const currentPage = ref(props.currentPage)
+
+const pageChange = (page: number) => {
+  emits("update:currentPage", page)
+  emits("page-change", page)
+}
+const onSort = (sort: Sort) => {
+  emits("on-sort", sort)
+}
+const onSelectAll = (isChecked: boolean, rows: Array<any>) => {
+  emits("on-items-all-change", isChecked, rows)
+}
+const onItemChange = (isChecked: boolean, v: any, row: any) => {
+  emits("update:selectedItems", selectedItems.value)
+  emits("on-items-change", isChecked, v, row)
+}
+const onItemSelect = (row: any) => {
+  emits("on-items-select", row)
+}
+const onItemsPerPageChange = (v: number) => {
+  emits("on-items-per-page-change", v)
+}
+</script>
+<template>
+  <VbDataTable
+    v-model:selected-items="selectedItems"
+    :isTree="true"
+    :header="header"
+    :interval-left="intervalLeft"
+    :key-field="keyField"
+    :children-field="childrenField"
+    :parent-field="parentField"
+    :expand-depth="expandDepth"
+    :url="url"
+    :configs="configs"
+    :method="method"
+    :query-params="queryParams"
+    :scroll="scroll"
+    :loading="loading"
+    :pagination="pagination"
+    v-model:current-page="currentPage"
+    :max-visible-buttons="maxVisibleButtons"
+    :items-per-page-dropdown-enabled="pageDropdown"
+    :items-per-page-dropdown-array="pageArray"
+    :hasCheckbox="hasCheckbox"
+    :checkboxField="checkboxField"
+    :checkMultiple="checkMultiple"
+    :sortField="sortField"
+    :sortOrder="sortOrder"
+    :fixedColumn="fixedColumn"
+    :fixedNumber="fixedNumber"
+    :fixedRightNumber="fixedRightNumber"
+    :emptyTableText="emptyTableText"
+    :tableBoxClass="tableBoxClass"
+    :tableClass="tableClass"
+    :headerClass="headerClass"
+    :bodyClass="bodyClass"
+    :thTrClass="thTrClass"
+    :tdTrClass="tdTrClass"
+    :rowSpanSuffix="rowSpanSuffix"
+    @page-change="pageChange"
+    @on-items-change="onItemChange"
+    @on-items-all-change="onSelectAll"
+    @on-sort="onSort"
+    @on-items-select="onItemSelect"
+    @on-items-per-page-change="onItemsPerPageChange"
+  >
+    <template v-for="(_, name) in $slots" #[name]="{ row: item }">
+      <slot :name="name" :row="item" />
+    </template>
+  </VbDataTable>
+</template>

+ 129 - 103
src/components/Table/table-partials/table-content/TableContent.vue

@@ -29,6 +29,12 @@ const props = withDefaults(
     thTrClass?: string
     tdTrClass?: string
     rowSpanSuffix?: string //rowsapn 的字段后缀 后台应该组成 `${字段}${后缀}`
+    isTree?: boolean
+    expandDepth?: number
+    intervalLeft?: number
+    keyField?: string
+    parentField?: string
+    childrenField?: string
   }>(),
   {
     selectedItems: () => {
@@ -44,7 +50,7 @@ const props = withDefaults(
     fixedNumber: 0,
     fixedRightNumber: 0,
     emptyTableText: "未查询到数据",
-    tableClass: "table-bordered table-rounded table-striped  table-row-dashed",
+    tableClass: "table-bordered table-rounded",
     rowSpanSuffix: "_rowSpan",
   }
 )
@@ -131,109 +137,129 @@ defineExpose({
 })
 </script>
 <template>
-  <TableFixed
-    v-if="props.fixedColumn && props.fixedNumber > 0"
-    @onSort="onSort"
-    @on-select-all="onSelectAll"
-    @on-change="onSelectItemsChange"
-    :table="table"
-    :header="header"
-    :data="data"
-    v-model:selected-items="selectedItems"
-    :fixed-number="fixedNumber"
-    :checkbox-all-value="check"
-    :checkbox-field="checkboxField"
-    :has-checkbox="hasCheckbox"
-    :check-multiple="checkMultiple"
-    :multiple-page-check="multiplePageCheck"
-    :sort-field="sortField"
-    :sort-order="sortOrder"
-    :table-class="tableClass"
-    :header-class="headerClass"
-    :th-tr-class="thTrClass"
-    :row-span-suffix="rowSpanSuffix"
-    :body-class="bodyClass"
-    :td-tr-class="tdTrClass"
-  >
-    <template v-for="(_, name) in $slots" v-slot:[name]="{ row: item }">
-      <slot :name="name" :row="item" />
-    </template>
-  </TableFixed>
-
-  <div class="table-responsive position-relative">
-    <table
-      ref="table"
-      class="table align-middle fs-6 gy-5 no-footer"
-      :class="loading ? `overlay overlay-block ${tableClass}` : `${tableClass}`"
-      :style="tableStyle"
+  <div class="table-box">
+    <TableFixed
+      v-if="props.fixedColumn && props.fixedNumber > 0"
+      @onSort="onSort"
+      @on-select-all="onSelectAll"
+      @on-change="onSelectItemsChange"
+      :table="table"
+      :header="header"
+      :data="data"
+      v-model:selected-items="selectedItems"
+      :fixed-number="fixedNumber"
+      :checkbox-all-value="check"
+      :checkbox-field="checkboxField"
+      :has-checkbox="hasCheckbox"
+      :check-multiple="checkMultiple"
+      :multiple-page-check="multiplePageCheck"
+      :sort-field="sortField"
+      :sort-order="sortOrder"
+      :table-class="tableClass"
+      :header-class="headerClass"
+      :th-tr-class="thTrClass"
+      :row-span-suffix="rowSpanSuffix"
+      :body-class="bodyClass"
+      :td-tr-class="tdTrClass"
+      :is-tree="isTree"
+      :interval-left="intervalLeft"
+      :key-field="keyField"
+      :children-field="childrenField"
+      :parent-field="parentField"
+      :expand-depth="expandDepth"
     >
-      <TableHeadRow
-        @onSort="onSort"
-        @on-select-all="onSelectAll"
-        :checkbox-all-value="check"
-        :has-checkbox="hasCheckbox"
-        :check-multiple="checkMultiple"
-        :sort-field="sortField"
-        :sort-order="sortOrder"
-        :header="header"
-        :fixed-column="fixedColumn"
-        :fixed-number="fixedNumber"
-        :fixed-right-number="fixedRightNumber"
-        :header-class="headerClass"
-        :th-tr-class="thTrClass"
-      >
-        <template v-for="(_, name) in $slots" #[name]="{ row: item }">
-          <slot v-if="name.toString().endsWith('_header')" :name="name" :row="item" />
-        </template>
-      </TableHeadRow>
-      <TableBodyRow
-        v-if="data.length !== 0"
-        @on-change="onSelectItemsChange"
-        v-model:currently-selected-items="selectedItems"
-        :check-multiple="checkMultiple"
-        :data="data"
-        :header="header"
-        :has-checkbox="hasCheckbox"
-        :checkbox-field="checkboxField"
-        :fixed-column="fixedColumn"
-        :fixed-number="fixedNumber"
-        :fixed-right-number="fixedRightNumber"
-        :body-class="bodyClass"
-        :td-tr-class="tdTrClass"
-        :row-span-suffix="rowSpanSuffix"
+      <template v-for="(_, name) in $slots" v-slot:[name]="{ row: item }">
+        <slot :name="name" :row="item" />
+      </template>
+    </TableFixed>
+
+    <div class="table-responsive">
+      <table
+        ref="table"
+        class="table align-middle fs-6 gy-5 no-footer"
+        :class="loading ? `overlay overlay-block ${tableClass}` : `${tableClass}`"
+        :style="tableStyle"
       >
-        <template v-for="(_, name) in $slots" v-slot:[name]="{ row: item }">
-          <slot v-if="!name.toString().endsWith('_header')" :name="name" :row="item" />
-        </template>
-      </TableBodyRow>
-      <tr v-if="loading" style="height: 50px"></tr>
-    </table>
-    <template v-if="data.length == 0 && !loading">
-      <div class="text-center text-primary mt-10 mb-5 fs-4">
-        {{ emptyTableText }}
-      </div>
-    </template>
-    <Loading v-if="loading" />
+        <TableHeadRow
+          @onSort="onSort"
+          @on-select-all="onSelectAll"
+          :checkbox-all-value="check"
+          :has-checkbox="hasCheckbox"
+          :check-multiple="checkMultiple"
+          :sort-field="sortField"
+          :sort-order="sortOrder"
+          :header="header"
+          :fixed-column="fixedColumn"
+          :fixed-number="fixedNumber"
+          :fixed-right-number="fixedRightNumber"
+          :header-class="headerClass"
+          :th-tr-class="thTrClass"
+        >
+          <template v-for="(_, name) in $slots" #[name]="{ row: item }">
+            <slot v-if="name.toString().endsWith('_header')" :name="name" :row="item" />
+          </template>
+        </TableHeadRow>
+        <TableBodyRow
+          v-if="data.length !== 0"
+          @on-change="onSelectItemsChange"
+          v-model:currently-selected-items="selectedItems"
+          :check-multiple="checkMultiple"
+          :data="data"
+          :header="header"
+          :has-checkbox="hasCheckbox"
+          :checkbox-field="checkboxField"
+          :fixed-column="fixedColumn"
+          :fixed-number="fixedNumber"
+          :fixed-right-number="fixedRightNumber"
+          :body-class="bodyClass"
+          :td-tr-class="tdTrClass"
+          :row-span-suffix="rowSpanSuffix"
+          :is-tree="isTree"
+          :interval-left="intervalLeft"
+          :key-field="keyField"
+          :parent-field="parentField"
+          :children-field="childrenField"
+          :expand-depth="expandDepth"
+        >
+          <template v-for="(_, name) in $slots" v-slot:[name]="{ row: item }">
+            <slot v-if="!name.toString().endsWith('_header')" :name="name" :row="item" />
+          </template>
+        </TableBodyRow>
+        <tr v-if="loading" style="height: 50px"></tr>
+      </table>
+      <template v-if="data.length == 0 && !loading">
+        <div class="text-center text-primary mt-10 mb-5 fs-4">
+          {{ emptyTableText }}
+        </div>
+      </template>
+      <Loading v-if="loading" />
+    </div>
+    <TableFixedRight
+      v-if="props.fixedColumn && props.fixedRightNumber > 0"
+      @onSort="onSort"
+      @on-select-all="onSelectAll"
+      :table="table"
+      :fixed-number="fixedRightNumber"
+      :header="header"
+      :data="data"
+      :sort-field="sortField"
+      :sort-order="sortOrder"
+      :table-class="tableClass"
+      :header-class="headerClass"
+      :th-tr-class="thTrClass"
+      :body-class="bodyClass"
+      :td-tr-class="tdTrClass"
+      :row-span-suffix="rowSpanSuffix"
+      :is-tree="isTree"
+      :interval-left="intervalLeft"
+      :key-field="keyField"
+      :parent-field="parentField"
+      :children-field="childrenField"
+      :expand-depth="expandDepth"
+    >
+      <template v-for="(_, name) in $slots" #[name]="{ row: item }">
+        <slot :name="name" :row="item" />
+      </template>
+    </TableFixedRight>
   </div>
-  <TableFixedRight
-    v-if="props.fixedColumn && props.fixedRightNumber > 0"
-    @onSort="onSort"
-    @on-select-all="onSelectAll"
-    :table="table"
-    :fixed-number="fixedRightNumber"
-    :header="header"
-    :data="data"
-    :sort-field="sortField"
-    :sort-order="sortOrder"
-    :table-class="tableClass"
-    :header-class="headerClass"
-    :th-tr-class="thTrClass"
-    :body-class="bodyClass"
-    :td-tr-class="tdTrClass"
-    :row-span-suffix="rowSpanSuffix"
-  >
-    <template v-for="(_, name) in $slots" #[name]="{ row: item }">
-      <slot :name="name" :row="item" />
-    </template>
-  </TableFixedRight>
 </template>

+ 73 - 3
src/components/Table/table-partials/table-content/table-body/TableBodyRow.vue

@@ -1,6 +1,8 @@
 <script setup lang="ts">
-import { ref, defineProps, withDefaults, defineEmits, watch } from "vue"
+import { ref, defineProps, withDefaults, defineEmits, watch, onMounted } from "vue"
 import type { Header } from "@/components/Table/table-partials/models"
+import TableTreeRow from "./TableTreeRow.vue"
+
 const props = withDefaults(
   defineProps<{
     header: Array<Header>
@@ -14,6 +16,12 @@ const props = withDefaults(
     fixedRightNumber?: number
     tdTrClass?: string
     rowSpanSuffix?: string //rowsapn 的字段后缀 后台应该组成 `${字段}${后缀}`
+    isTree?: boolean
+    expandDepth?: number
+    intervalLeft?: number
+    keyField?: string
+    parentField?: string
+    childrenField?: string
   }>(),
   {
     currentlySelectedItems: () => {
@@ -27,6 +35,7 @@ const props = withDefaults(
     fixedRightNumber: 0,
     tdTrClass: "text-center fs-7 ",
     rowSpanSuffix: "_rowSpan",
+    childrenField: "children",
   }
 )
 const emits = defineEmits<{
@@ -70,12 +79,73 @@ const tdClass = (column: Header) => {
   }
   return classStr
 }
+const tbody = ref<HTMLElement>()
+function init() {
+  if (props.isTree && tbody.value) {
+    tbody.value.querySelectorAll("td.tree-icon>.icon")?.forEach((v) => {
+      v.addEventListener("click", () => {
+        const parent = v.parentElement?.parentElement
+        const id = parent?.id
+        if (parent == null || !id) {
+          return
+        }
+        if (parent.className.search("tr-expand") >= 0) {
+          parent.className = parent.className.replace("tr-expand", "tr-collapse")
+          hideChildren(id)
+        } else if (parent.className.search("tr-collapse") >= 0) {
+          parent.className = parent.className.replace("tr-collapse", "tr-expand")
+          showChildren(id)
+        }
+      })
+    })
+  }
+  function hideChildren(parentId: string) {
+    const children = document.querySelectorAll(`[data-parent="${parentId}"]`)
+    if (children && children.length) {
+      children.forEach((v) => {
+        v.className = v.className.replace("show", "hide")
+        v.className = v.className.replace("tr-expand", "tr-collapse")
+        hideChildren(v.id)
+      })
+    }
+  }
+  function showChildren(parentId: string) {
+    const children = document.querySelectorAll(`[data-parent="${parentId}"]`)
+    if (children && children.length) {
+      children.forEach((v) => {
+        v.className = v.className.replace("hide", "show")
+      })
+    }
+  }
+}
+onMounted(init)
 </script>
 
 <template>
-  <tbody class="fw-semibold text-gray-600">
+  <tbody ref="tbody" class="fw-semibold text-gray-600">
     <template v-for="(row, i) in data" :key="i">
-      <tr :class="tdTrClass">
+      <template v-if="isTree">
+        <TableTreeRow
+          :key="i"
+          :row="row"
+          :header="header"
+          :interval-left="intervalLeft"
+          :key-field="keyField"
+          :parent-field="parentField"
+          :children-field="childrenField"
+          :expand-depth="expandDepth"
+          :currently-selected-items="currentlySelectedItems"
+          :has-checkbox="hasCheckbox"
+          :checkbox-field="checkboxField"
+          :td-tr-class="tdTrClass"
+          :row-span-suffix="rowSpanSuffix"
+        >
+          <template v-for="(_, name) in $slots" #[name]="{ row: item }">
+            <slot :name="name" :row="item" />
+          </template>
+        </TableTreeRow>
+      </template>
+      <tr v-else :class="tdTrClass">
         <td v-if="hasCheckbox">
           <div class="form-check form-check-sm form-check-custom form-check-solid">
             <input

+ 165 - 0
src/components/Table/table-partials/table-content/table-body/TableTreeRow.vue

@@ -0,0 +1,165 @@
+<script setup lang="ts">
+import { ref, defineProps, withDefaults, defineEmits, onMounted, computed } from "vue"
+import type { Header } from "@/components/Table/table-partials/models"
+
+const props = withDefaults(
+  defineProps<{
+    header: Array<Header>
+    row: any
+    depth?: number
+    expandDepth?: number
+    currentlySelectedItems?: Array<any>
+    hasCheckbox?: boolean //是否有选择框
+    checkMultiple?: boolean //是否多选
+    checkboxField?: string //选择框字段
+    tdTrClass?: string
+    rowSpanSuffix?: string //rowsapn 的字段后缀 后台应该组成 `${字段}${后缀}`
+    intervalLeft?: number
+    iconField?: string
+    keyField?: string
+    parentField?: string
+    childrenField?: string
+  }>(),
+  {
+    checkboxField: "id",
+    keyField: "id",
+    parentField: "parent_id",
+    childrenField: "children",
+    iconField: "name",
+    depth: 0,
+    expandDepth: 0,
+    intervalLeft: 10,
+  }
+)
+const emits = defineEmits<{
+  (e: "on-change", isChecked: boolean, v: any, row: any): void
+  (e: "update:currentlySelectedItems", v: any): void
+}>()
+const selectedItems = ref<Array<any>>([])
+const isExpand = computed(() => {
+  return props.expandDepth > props.depth
+})
+const hasChildren = computed(() => {
+  return props.row[props.childrenField] && props.row[props.childrenField].length
+})
+
+const trClass = computed(() => {
+  let _trClass = ` ${props.tdTrClass} `
+  if (hasChildren.value) {
+    _trClass += `hasChildren ${isExpand.value ? "tr-expand" : "tr-collapse"} `
+  }
+  _trClass += props.depth > props.expandDepth ? "hide" : "show"
+  return _trClass
+})
+
+const tdStyle = (column: Header) => {
+  const style: any = {}
+  if (column.align) {
+    style.textAlign = column.align
+  }
+
+  return style
+}
+const tdClass = (column: Header) => {
+  let classStr = ""
+  if (column.tdClass) {
+    classStr += column.tdClass
+  }
+  if (column.field == props.iconField) {
+    classStr += " tree-icon"
+  }
+  return classStr
+}
+const onChange = (event: Event) => {
+  const target = event.target as HTMLInputElement
+  selectedItems.value = props.checkMultiple ? selectedItems.value : target.checked ? [target.value] : []
+  // const row = props.data.find((v: any) => {
+  //   return v[props.checkboxField] == target.value
+  // })
+  emits("on-change", target.checked, selectedItems.value, props.row)
+  emits("update:currentlySelectedItems", selectedItems.value)
+}
+
+function init() {
+  //console.log("TREE_ROW")
+}
+
+onMounted(() => {
+  init()
+})
+</script>
+<template>
+  <tr :class="trClass" :id="`tr_${row[keyField]}`" :data-parent="`tr_${row[parentField]}`">
+    <td v-if="hasCheckbox">
+      <div class="form-check form-check-sm form-check-custom form-check-solid">
+        <input
+          class="form-check-input"
+          type="checkbox"
+          :value="row[checkboxField]"
+          v-model="selectedItems"
+          @change="onChange"
+        />
+      </div>
+    </td>
+    <template v-for="(column, i) in header" :key="i">
+      <td
+        v-if="row[column.field + props.rowSpanSuffix]"
+        :style="tdStyle(column)"
+        :class="tdClass(column)"
+        :rowspan="`${row[column.field + props.rowSpanSuffix] ? row[column.field + props.rowSpanSuffix] : ''}`"
+      >
+        <span
+          v-if="column.field == iconField"
+          class="icon"
+          :style="`margin-left: ${intervalLeft * depth}px!important`"
+        ></span>
+        <template v-if="$slots[column.field]">
+          <slot :name="`${column.field}`" :row="row">
+            {{ row }}
+          </slot>
+        </template>
+        <template v-else>
+          {{ row[column.field] }}
+        </template>
+      </td>
+      <td v-else-if="row[column.field + props.rowSpanSuffix] != 0" :style="tdStyle(column)" :class="tdClass(column)">
+        <span
+          v-if="column.field == iconField"
+          class="icon"
+          :style="`margin-left: ${intervalLeft * depth}px!important`"
+        ></span>
+        <template v-if="$slots[column.field]">
+          <slot :name="`${column.field}`" :row="row">
+            {{ row }}
+          </slot>
+        </template>
+        <template v-else>
+          {{ row[column.field] }}
+        </template>
+      </td>
+    </template>
+  </tr>
+  <template v-if="hasChildren">
+    <TableTreeRow
+      v-for="(child, i) in row[props.childrenField]"
+      :key="i"
+      :row="child"
+      :header="header"
+      :depth="depth + 1"
+      :expand-depth="expandDepth"
+      :currently-selected-items="currentlySelectedItems"
+      :has-checkbox="hasCheckbox"
+      :checkbox-field="checkboxField"
+      :td-tr-class="tdTrClass"
+      :row-span-suffix="rowSpanSuffix"
+      :icon-field="iconField"
+      :key-field="keyField"
+      :parent-field="parentField"
+      :children-field="childrenField"
+    >
+      <template v-for="(_, name) in $slots" #[name]="{ row: item }">
+        <slot :name="name" :row="item" />
+      </template>
+    </TableTreeRow>
+  </template>
+</template>

+ 13 - 1
src/components/Table/table-partials/table-content/table-fixed/TableFixed.vue

@@ -24,6 +24,12 @@ const props = withDefaults(
     thTrClass?: string
     tdTrClass?: string
     rowSpanSuffix?: string //rowsapn 的字段后缀 后台应该组成 `${字段}${后缀}`
+    isTree?: boolean
+    expandDepth?: number
+    intervalLeft?: number
+    keyField?: string
+    parentField?: string
+    childrenField?: string
   }>(),
   {
     hasCheckbox: true,
@@ -31,7 +37,7 @@ const props = withDefaults(
     multiplePageCheck: false,
     checkboxField: "",
     sortOrder: "asc",
-    tableBoxClass: "bg-white",
+    tableBoxClass: "",
     rowSpanSuffix: "_rowSpan",
   }
 )
@@ -151,6 +157,12 @@ onBeforeUnmount(() => {
         :body-class="bodyClass"
         :td-tr-class="tdTrClass"
         :row-span-suffix="rowSpanSuffix"
+        :is-tree="isTree"
+        :interval-left="intervalLeft"
+        :key-field="keyField"
+        :children-field="childrenField"
+        :parent-field="parentField"
+        :expand-depth="expandDepth"
       >
         <template v-for="(_, name) in $slots" v-slot:[name]="{ row: item }">
           <slot v-if="!name.toString().endsWith('_header')" :name="name" :row="item" />

+ 16 - 1
src/components/Table/table-partials/table-content/table-fixed/TableRightFixed.vue

@@ -19,12 +19,18 @@ const props = withDefaults(
     thTrClass?: string
     tdTrClass?: string
     rowSpanSuffix?: string //rowsapn 的字段后缀 后台应该组成 `${字段}${后缀}`
+    isTree?: boolean
+    expandDepth?: number
+    intervalLeft?: number
+    keyField?: string
+    parentField?: string
+    childrenField?: string
   }>(),
   {
     hasCheckbox: true,
     checkMultiple: false,
     sortOrder: "asc",
-    tableBoxClass: "bg-white",
+    tableBoxClass: "",
     rowSpanSuffix: "_rowSpan",
   }
 )
@@ -62,6 +68,9 @@ watch(
       tableBox.value.firstElementChild.style.width = val.clientWidth + "px"
       tableBox.value.firstElementChild.style.transform = `translateX(-${val.clientWidth - width}px)`
     }
+    if (props.table.clientWidth <= props.table.parentElement.clientWidth) {
+      tableBox.value.className += " no-shadow"
+    }
     if (props.table.parentElement) {
       props.table.parentElement.addEventListener("scroll", onScroll, true)
     }
@@ -97,6 +106,12 @@ onBeforeUnmount(() => {
         :body-class="bodyClass"
         :td-tr-class="tdTrClass"
         :row-span-suffix="rowSpanSuffix"
+        :is-tree="isTree"
+        :interval-left="intervalLeft"
+        :key-field="keyField"
+        :children-field="childrenField"
+        :parent-field="parentField"
+        :expand-depth="expandDepth"
       >
         <template v-for="(_, name) in $slots" v-slot:[name]="{ row: item }">
           <slot v-if="!name.toString().endsWith('_header')" :name="name" :row="item" />

+ 62 - 57
src/views/goLineData/timeData.vue

@@ -279,73 +279,76 @@ onMounted(() => {
       </div>
     </div>
   </div>
-  <el-form class="my-5 align-items-center" :inline="true">
-    <el-form-item class="mb-0 me-5 align-items-center" label="商户名称">
-      <el-input class="" style="width: 180px" v-model="companyName" placeholder="请输入商户名称" :size="size" />
-    </el-form-item>
-    <el-form-item class="mb-0 me-5 align-items-center" label="区域">
-      <DySelectTree
-        v-model:value="orgId"
-        style="width: 180px"
-        class="full-input"
-        :url="'sys/dict/getOrgList?type=1'"
-        :defaultExpandLevel="1"
-        :option-map="{ id: 'value', label: 'label', children: 'children' }"
-        placeholder="请选择区域..."
-      />
-    </el-form-item>
-    <el-form-item class="mb-0 me-5 align-items-center" label="菜系">
-      <DySelect
-        v-model="cateringStyle"
-        :formatRemoteData="(v:any)=>{return v?.list}"
-        :url="'sys/dict/getList?code=000010001&key=temp'"
-        :style="dySearchSelectStyle"
-        placeholder="请选择菜系"
-      ></DySelect>
-    </el-form-item>
-    <el-form-item class="mb-0 me-5 align-items-center" label="营业规模">
-      <DySelect
-        v-model="cateringScale"
-        :formatRemoteData="(v:any)=>{return v?.list}"
-        :url="'sys/dict/getList?code=000090001&key=temp'"
-        :style="dySearchSelectStyle"
-        placeholder="请选择营业规模"
-      ></DySelect>
-    </el-form-item>
-    <el-form-item class="mb-0 me-5 align-items-center" label="类型">
-      <DySelect
-        v-model="type"
-        :formatRemoteData="(v:any)=>{return v?.list}"
-        :url="'sys/dict/getList?code=000200000&key=temp'"
-        :style="dySearchSelectStyle"
-        placeholder="请选择类型"
-      ></DySelect>
-    </el-form-item>
-    <el-form-item class="mb-0 me-5 align-items-center" label="营业状态">
-      <el-select v-model="operateStatus" style="width: 150px" placeholder="请选择营业状态" :clearable="true">
-        <el-option value="0" label="营业"></el-option>
-        <el-option value="1" label="间休"></el-option>
-        <el-option value="2" label="停业"></el-option>
-      </el-select>
-    </el-form-item>
-    <el-form-item class="mb-0 me-0 align-items-center">
-      <el-button class="ms-3 mt-0 btn btn-sm btn-primary" @click="query">查询</el-button>
-      <el-button class="ms-3 mt-0 btn btn-sm btn-light-primary btn-outline" @click="reset">重置</el-button>
-      <el-button class="ms-3 mt-0 btn btn-sm btn-light-info btn-outline" @click="exported">导出</el-button>
-    </el-form-item>
-  </el-form>
+
   <VbDataTable
     ref="table"
     :header="cols"
     url="sys/realTimeData/getRealTimeDataForPage"
     method="post"
-    :query-parms="queryParams"
+    :query-params="queryParams"
     :has-checkbox="false"
     :fixed-column="true"
     :fixed-number="2"
     :fixed-right-number="1"
     :scroll="{ x: 2000 }"
   >
+    <template v-slot:table-tool="">
+      <el-form class="align-items-center" :inline="true">
+        <el-form-item class="mb-0 me-5 align-items-center" label="商户名称">
+          <el-input class="" style="width: 180px" v-model="companyName" placeholder="请输入商户名称" :size="size" />
+        </el-form-item>
+        <el-form-item class="mb-0 me-5 align-items-center" label="区域">
+          <DySelectTree
+            v-model:value="orgId"
+            style="width: 180px"
+            class="full-input"
+            :url="'sys/dict/getOrgList?type=1'"
+            :defaultExpandLevel="1"
+            :option-map="{ id: 'value', label: 'label', children: 'children' }"
+            placeholder="请选择区域..."
+          />
+        </el-form-item>
+        <el-form-item class="mb-0 me-5 align-items-center" label="菜系">
+          <DySelect
+            v-model="cateringStyle"
+            :formatRemoteData="(v:any)=>{return v?.list}"
+            :url="'sys/dict/getList?code=000010001&key=temp'"
+            :style="dySearchSelectStyle"
+            placeholder="请选择菜系"
+          ></DySelect>
+        </el-form-item>
+        <el-form-item class="mb-0 me-5 align-items-center" label="营业规模">
+          <DySelect
+            v-model="cateringScale"
+            :formatRemoteData="(v:any)=>{return v?.list}"
+            :url="'sys/dict/getList?code=000090001&key=temp'"
+            :style="dySearchSelectStyle"
+            placeholder="请选择营业规模"
+          ></DySelect>
+        </el-form-item>
+        <el-form-item class="mb-0 me-5 align-items-center" label="类型">
+          <DySelect
+            v-model="type"
+            :formatRemoteData="(v:any)=>{return v?.list}"
+            :url="'sys/dict/getList?code=000200000&key=temp'"
+            :style="dySearchSelectStyle"
+            placeholder="请选择类型"
+          ></DySelect>
+        </el-form-item>
+        <el-form-item class="mb-0 me-5 align-items-center" label="营业状态">
+          <el-select v-model="operateStatus" style="width: 150px" placeholder="请选择营业状态" :clearable="true">
+            <el-option value="0" label="营业"></el-option>
+            <el-option value="1" label="间休"></el-option>
+            <el-option value="2" label="停业"></el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item class="mb-0 me-0 align-items-center">
+          <el-button class="ms-3 mt-0 btn btn-sm btn-primary" @click="query">查询</el-button>
+          <el-button class="ms-3 mt-0 btn btn-sm btn-light-primary btn-outline" @click="reset">重置</el-button>
+          <el-button class="ms-3 mt-0 btn btn-sm btn-light-info btn-outline" @click="exported">导出</el-button>
+        </el-form-item>
+      </el-form>
+    </template>
     <template #company_name="{ row }">
       <span class="text-primary" @click="jump(row)" style="cursor: pointer">{{ row["company_name"] }}</span>
     </template>
@@ -372,7 +375,9 @@ onMounted(() => {
       <div v-if="row['fan_online_state'] == 4">-</div>
     </template>
     <template #action="{ row }">
-      <el-button data-bs-toggle="modal" data-bs-target="#detail_modal" @click="detail(row)">查看详情</el-button>
+      <span class="table-action" data-bs-toggle="modal" data-bs-target="#detail_modal" @click="detail(row)">
+        查看详情
+      </span>
     </template>
   </VbDataTable>
 

+ 36 - 2
src/views/overAnalysis/overParam.vue

@@ -1,7 +1,41 @@
 <script setup lang="ts">
-import { defineProps, reactive, ref, toRefs } from "vue"
+import { ref } from "vue"
+import type { Header } from "@/components/Table/table-partials/models"
+const cols = ref<Array<Header>>([
+  { name: "名称", field: "name", align: "left", width: "200px" },
+  { name: "简称", field: "simple_name" },
+  { name: "父级", field: "parent_id" },
+  { name: "code", field: "code" },
+  { name: "省/市/区", field: "province_city_district" },
+  { name: "描述", field: "description" },
+  {
+    name: "操作",
+    field: "action",
+    width: "150px",
+  },
+])
+
+function edit(row: any) {
+  //
+}
 </script>
 
 <template>
-  <div>overParam</div>
+  <VbTreeTable
+    url="sys/organizational/selectForPage"
+    method="post"
+    :header="cols"
+    key-field="org_id"
+    children-field="children"
+    :expand-depth="1"
+    :interval-left="10"
+    :fixed-column="true"
+    :fixed-number="1"
+    :fixed-right-number="1"
+  >
+    <template #action="{ row }">
+      <a class="text-primary table-action" @click="edit(row)">编辑</a>
+      <a class="text-danger table-action" @click="edit(row)">删除</a>
+    </template>
+  </VbTreeTable>
 </template>

+ 19 - 16
src/views/overAnalysis/overTime.vue

@@ -147,20 +147,7 @@ const detail = function (v: any) {
       类型
     </button>
   </div>
-  <el-form class="mb-3 align-items-center" :inline="true" label-width="90px">
-    <DateRangeSelect
-      v-model:date-value="dateValue"
-      v-model:select-value="selectValue"
-      :select-class="'mb-0 align-items-center'"
-      :date-class="'mb-0 align-items-center'"
-      @change="changeDate"
-    />
-    <el-form-item class="mb-0 align-items-center">
-      <el-button class="ms-3 mt-0 btn btn-sm btn-primary" @click="query">查询</el-button>
-      <el-button class="ms-3 mt-0 btn btn-sm btn-light-primary btn-outline" @click="reset">重置</el-button>
-      <el-button class="ms-3 mt-0 btn btn-sm btn-light-info btn-outline" @click="exported">导出</el-button>
-    </el-form-item>
-  </el-form>
+
   <!-- 
     :fixed-column="true"
     :fixed-number="2"
@@ -174,11 +161,27 @@ const detail = function (v: any) {
     v-model:selected-items="tableSelect"
     url="sys/overStandardAnalysis/queryTabel"
     method="post"
-    :query-parms="queryParams"
+    :query-params="queryParams"
     :pagination="false"
     :has-checkbox="false"
     checkbox-field="name_code"
   >
+    <template v-slot:table-tool="">
+      <el-form class="align-items-center" :inline="true" label-width="90px">
+        <DateRangeSelect
+          v-model:date-value="dateValue"
+          v-model:select-value="selectValue"
+          :select-class="'mb-0 align-items-center'"
+          :date-class="'mb-0 align-items-center'"
+          @change="changeDate"
+        />
+        <el-form-item class="mb-0 align-items-center">
+          <el-button class="ms-3 mt-0 btn btn-sm btn-primary" @click="query">查询</el-button>
+          <el-button class="ms-3 mt-0 btn btn-sm btn-light-primary btn-outline" @click="reset">重置</el-button>
+          <el-button class="ms-3 mt-0 btn btn-sm btn-light-info btn-outline" @click="exported">导出</el-button>
+        </el-form-item>
+      </el-form>
+    </template>
     <template v-slot:name_header="">
       <span class="text-danger text-center">{{ name }}</span>
     </template>
@@ -187,7 +190,7 @@ const detail = function (v: any) {
     </template>
     <template #action="{ row }">
       <div class="text-danger text-center">
-        <el-button @click="detail(row)">查看详情</el-button>
+        <span class="table-action" @click="detail(row)">查看详情</span>
       </div>
     </template>
   </VbDataTable>