vue3 封装 el-table 支持嵌套 + render + json 配置

index.vue

<script setup>
import { onMounted, ref, nextTick, onUnmounted } from "vue";
import TableTemplate from "./tableTemplate.vue";

const props = defineProps({
  tableData: {
    type: Array,
    default: () => [],
  },
  tableConfig: {
    type: Object,
    default: () => {},
  },
  tableColumnConfig: {
    type: Array,
    require: true,
    default: () => [],
  },
  minusPart: {
    type: Number,
    default: () => 0,
  },
  minTableHeight: {
    type: Number,
    default: () => 0,
  },
});

// 表格高度
const tableHeight = ref(0);
const getTableHeight = () => {
  nextTick(() => {
    tableHeight.value = window.innerHeight - props.minusPart;
  });
};
const xTable = ref();
// 导出子组件Ref
defineExpose({
  xTable,
});
// 组件加载完成
onMounted(() => {
  getTableHeight();
  window.addEventListener("resize", getTableHeight);
});
// 组件移除
onUnmounted(() => {
  window.removeEventListener("resize", getTableHeight);
});
</script>
<template>
  <div
    class="x-table"
    :style="minTableHeight ? `height: calc(100% - ${minTableHeight}px)` : ''"
  >
    <el-table
      ref="xTable"
      :data="tableData"
      v-bind="$attrs"
      :max-height="tableHeight"
    >
      <!-- 多选列 -->
      <template
        v-if="tableConfig.selection"
        :align="selectionAlign"
        :header-align="selectionHeaderAlign"
      >
        <el-table-column
          type="selection"
          :width="tableConfig.selectionWidth || 40"
          :fixed="tableConfig.selectionFixed || null"
        ></el-table-column>
      </template>
      <!-- 序号列 -->
      <template
        v-if="tableConfig.index"
        :align="indexAlign"
        :header-align="indexHeaderAlign"
      >
        <el-table-column
          type="index"
          :width="tableConfig.indexWidth || 50"
          :label="tableConfig.indexLabel || '序号'"
          :fixed="tableConfig.indexFixed || null"
        ></el-table-column>
      </template>
      <!-- 循环列 -->
      <template v-for="(item, index) in tableColumnConfig">
        <!-- 试验插槽 -->
        <el-table-column
          v-if="item.scope"
          :label="item.label || ''"
          :width="item.width || ''"
          :min-width="item.minWidth || ''"
          :align="item.align || 'left'"
          :header-align="item.headerAlign || 'left'"
        >
          <template #default="scope">
            <slot
              :name="item.scope"
              :data="{ scope: scope.row, index: scope.$index }"
            ></slot>
          </template>
        </el-table-column>
        <TableTemplate
          :tableColumConfig="item"
          v-bind="$attrs"
          v-else="!item.scope"
        ></TableTemplate>
      </template>
    </el-table>
  </div>
</template>
<style lang="scss">
.x-table {
  .cell {
    padding: 0 10px !important;
    line-height: 35px !important;
  }
  th {
    background: rgb(240, 242, 245) !important;
    font-size: 13px;
    color: #333333;
    padding: 6px 0 !important;
  }
  .x-table-template {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    justify-content: flex-start;
    .x-table-input {
      input {
        text-align: center;
      }
    }
  }
}
</style>

tableTemplate.vue

<script setup>
import { onMounted, ref, computed } from "vue";
import xTableRender from "./xTableRender.vue";
import { useRoute } from "vue-router";

const props = defineProps({
  tableColumConfig: {
    type: Object,
    default: () => {},
  },
});
const exData = computed(() => {
  return window.localStorage.getItem("exData") || "";
});

const route = useRoute();

const renderItem = computed(() => {
  return function (templateList, row) {
    if (!Array.isArray(templateList)) {
      return [];
    }
    return templateList.filter((i) => {
      return i.showItem
        ? i.showItem({
            row: row,
            route: route,
            exData: exData.value,
          })
        : true;
    });
  };
});

const emit = defineEmits();

const emitFunc = (method, row) => {
  emit(method, row);
};

onMounted(() => {});
</script>
<template>
  <el-table-column
    :label="tableColumConfig.label || ''"
    :width="tableColumConfig.width || ''"
    :min-width="tableColumConfig.minWidth || ''"
    :prop="tableColumConfig.prop"
    :align="tableColumConfig.align || 'left'"
    :header-align="tableColumConfig.headerAlign || 'left'"
    :fixed="tableColumConfig.fixed || null"
  >
    <template #default="scope">
      <!-- 循环可能存在的template数组 -->
      <div
        class="x-table-template"
        v-if="
          tableColumConfig.template &&
          Array.isArray(tableColumConfig.template) &&
          !tableColumConfig.children
        "
        :style="tableColumConfig.alignStyle || ''"
      >
        <div
          v-for="(i, _index) in renderItem(
            tableColumConfig.template,
            scope.row
          )"
          :key="_index"
        >
          <!-- 按钮项 -->
          <template v-if="i.type === 'button'">
            <el-button
              :type="i.assemblyType || 'primary'"
              :disabled="
                i.disabled
                  ? i.disabled({
                      row: scope.row,
                      route: route,
                      exData: exData,
                    })
                  : false
              "
              @click="
                emitFunc(i.method || 'defaultMethod', {
                  row: scope.row,
                  index: scope.$index,
                  additionalParams: i.addParams || '',
                })
              "
            >
              {{ i.text }}
            </el-button>
          </template>
          <!-- icon项 -->
          <template v-else-if="i.type === 'icon'">
            <el-link
              :type="i.assemblyType || 'primary'"
              :disabled="
                i.disabled
                  ? i.disabled({
                      row: scope.row,
                      route: route,
                      exData: exData,
                    })
                  : false
              "
              @click="
                emitFunc(i.method || 'defaultMethod', {
                  row: scope.row,
                  index: scope.$index,
                  additionalParams: i.addParams || '',
                })
              "
            >
              <!-- icon的样式 具体看 https://element.eleme.cn/#/zh-CN/component/icon -->
              <i :class="i.iconClass"></i>
            </el-link>
          </template>
          <!-- input输入框 -->
          <template v-else-if="i.type === 'input'">
            <el-input
              class="x-table-input"
              v-model="scope.row[`${tableColumConfig.prop}`]"
              clearable
              :type="i.assemblyType || 'text'"
              :placeholder="i.placeholder || '请输入'"
              :disabled="
                i.disabled
                  ? i.disabled({
                      row: scope.row,
                      route: route,
                      exData: exData,
                    })
                  : false
              "
              @blur="
                emitFunc(i.method || 'defaultMethod', {
                  row: scope.row,
                  index: scope.$index,
                  additionalParams: i.addParams || '',
                })
              "
            ></el-input>
          </template>
          <!-- 文字方面的 -->
          <template v-else-if="i.type === 'link'">
            <el-link
              :type="i.assemblyType || 'primary'"
              :disabled="
                i.disabled
                  ? i.disabled({
                      row: scope.row,
                      route: route,
                      exData: exData,
                    })
                  : false
              "
              @click="
                emitFunc(i.method || 'defaultMethod', {
                  row: scope.row,
                  index: scope.$index,
                  additionalParams: i.addParams || '',
                })
              "
            >
              {{ i.text }}
            </el-link>
          </template>
          <!-- 非超链接 -->
          <template v-else-if="i.type === 'span'">
            <span
              @click="
                emitFunc(i.method || 'defaultMethod', {
                  row: scope.row,
                  index: scope.$index,
                  additionalParams: i.addParams || '',
                })
              "
              :style="i.spanStyle ? i.spanStyle(scope.row) : ''"
            >
              {{ i.span(scope.row) }}
            </span>
          </template>
          <template v-else-if="i.type === 'render'">
            <xTableRender
              v-bind="$attrs"
              :sc="scope"
              :row="scope.row"
              :render="i.render(scope.row, that)"
              :rederStyle="i.style(scope.row)"
            ></xTableRender>
          </template>
          <template v-else-if="i.type === 'renderLink'">
            <span
              v-for="(_x, _xindex) in i.renderLink(scope.row)"
              :key="_xindex"
            >
              <el-link
                @click="
                  emitFunc(i.method || 'defaultMethod', {
                    row: scope.row,
                    index: scope.$index,
                    additionalParams: i.addParams || '',
                    linkIndex: _xindex,
                  })
                "
                :disabled="
                  i.disabled
                    ? i.disabled({
                        row: scope.row,
                        route: route,
                        exData: exData,
                      })
                    : false
                "
                :type="i.assemblyType ? i.assemblyType() : 'primary'"
                :style="i.linkStyle"
              >
                {{ _x }}
                {{
                  _xindex != i.renderLink(scope.row).length - 1
                    ? i.renderFill || ";"
                    : ""
                }}
              </el-link>
            </span>
          </template>
          <!-- 自定义值 -->
          <el-link
            @click="
              emitFunc(i.method || 'defaultMethod', {
                row: scope.row,
                index: scope.$index,
                additionalParams: i.addParams || '',
              })
            "
            v-if="i.supplement"
            :type="i.suppleType ? i.suppleType(scope.row) : ' '"
            :disabled="
              i.disabled
                ? i.disabled({
                    row: scope.row,
                    route: route,
                    exData: exData,
                  })
                : false
            "
          >
            {{ i.supplement(scope.row) }}
          </el-link>
        </div>
      </div>

      <template v-else>
        <el-tooltip
          placement="top-start"
          :disabled="
            scope.row[`${tableColumConfig.prop}`] == 0 ||
            (scope.row[`${tableColumConfig.prop}`] &&
              scope.row[`${tableColumConfig.prop}`].toString().length < 20) ||
            !scope.row[`${tableColumConfig.prop}`]
          "
        >
          <div slot="content">
            <span style="max-width: 800px">{{
              scope.row[`${tableColumConfig.prop}`]
            }}</span>
          </div>
          <div>
            {{ scope.row[`${tableColumConfig.prop}`] }}
          </div>
        </el-tooltip>
      </template>

      <!-- 可能存在的子集 -->
      <template v-if="tableColumConfig.children">
        <tableTemplate
          v-for="(i, __index) in tableColumConfig.children"
          :key="__index"
          :tableColumConfig="i"
          v-bind="$attrs"
        ></tableTemplate>
      </template>
    </template>
  </el-table-column>
</template>
<style lang="scss"></style>

xTableReder.vue

<script>
export default {
  functional: true,
  props: {
    row: {
      type: Object,
      required: true,
    },
    render: {
      type: Function,
      required: true,
    },
    sc: {
      type: Object,
      required: true,
    },
    rederStyle: {
      type: String,
      require: true,
    },
  },
  render: (h, ctx) => {
    const arr = [];
    const params = {
      row: ctx.props.row,
      index: ctx.props.sc.$index,
    };
    const VNode = ctx.props.render(h, params);
    arr.push(VNode);
    return h("div", { class: ctx.props.rederStyle }, arr);
  },
};
</script>

例子:
vue 文件

	const { tableHeaderConfig, tableColumnConfig } = useConfig()
	const { tableData, tableConfig, tableHeaderForm, pageInfo, dialogTypeObj, reset, commonSubmit } = useTableCommon()

    <XTable
      :table-column-config="tableColumnConfig"
      :table-data="tableData"
      :table-config="tableConfig"
      :min-table-height="100"
      :minus-part="100"
      @publicOne="publicOne"
      @dele="dele"
      @edit="edit"
      class="mb-2"
    >
      <template #testScope="{ data }">{{ data }}</template>
    </XTable>

useConfig.js hooks

export default () => {
  const tableHeaderConfig = [
    {
      type: 'input',
      prop: 'vehicleBrand',
      label: '品牌:',
      labelWidth: '50',
      width: 160
    },
    {
      type: 'input',
      prop: 'registrationNo',
      label: '车牌号:',
      labelWidth: '60',
      width: 160
    },
    {
      type: 'button',
      text: '查询',
      method: 'search'
    },
    {
      type: 'button',
      text: '重置',
      btnType: 'default',
      method: 'reset'
    }
  ]

  const tableColumnConfig = [
    {
      label: '车牌号',
      prop: 'licensePlate'
    },
    {
      label: '设备SN码',
      prop: 'sn'
    },
    {
      label: '车辆VIN',
      prop: 'vin'
    },
    {
      scope: 'testScope',
      label: 'testScope'
    },
    {
      label: '在线状态',
      template: [
        {
          type: 'span',
          span: (row) => {
            const list = ['离线', '在线']
            return list[row.a]
          }
        }
      ]
    },
    {
      label: '操作',
      template: [
        {
          type: 'link',
          method: 'publicOne',
          text: '发布'
        },
        {
          type: 'link',
          method: 'dele',
          assemblyType: 'danger',
          text: '删除'
        },
        {
          type: 'link',
          method: 'edit',
          text: '编辑'
        }
      ]
    }
  ]

  return {
    tableHeaderConfig,
    tableColumnConfig
  }
}

useTableCommon.js hooks 代码

import { ElMessage, ElMessageBox } from 'element-plus'
export default function () {
  // 表格数组对象
  const tableData = ref([{ a: 1 }])
  // 表格配置
  const tableConfig = ref({})

  // 表头对象
  const tableHeaderForm = ref({})

  // 分页对象
  const pageInfo = reactive({
    pageNum: 1,
    total: 100,
    pageSize: 20
  })

  // 表头重置
  const reset = () => {
    tableHeaderForm.value = {}
    pageInfo.pageNum = 1
  }

  // 弹窗控制器
  const dialogVisible = ref(false)

  // 弹窗综合对象
  const dialogTypeObj = reactive({
    visible: false,
    type: 0,
    form: {}
  })

  const commonSubmit = ({
    text = '提示信息',
    title = '提示',
    confirmBtn = '确认',
    cancelBtn = '取消',
    status = 'warning'
  }) => {
    return new Promise((resolve) => {
      ElMessageBox.confirm(text, title, {
        confirmButtonText: confirmBtn,
        cancelButtonText: cancelBtn,
        type: status
      }).then(() => {
        resolve()
      })
    })
  }

  return {
    tableData,
    tableConfig,
    tableHeaderForm,
    pageInfo,
    dialogVisible,
    dialogTypeObj,
    reset,
    commonSubmit
  }
}

你可能感兴趣的:(json,vue.js,javascript)