宅男在线永久免费观看网直播,亚洲欧洲日产国码无码久久99,野花社区在线观看视频,亚洲人交乣女bbw,一本一本久久a久久精品综合不卡

全部
常見(jiàn)問(wèn)題
產(chǎn)品動(dòng)態(tài)
精選推薦

Vue3 進(jìn)階教程:一文詳解 el-table 組件的二次封裝技巧!

管理 管理 編輯 刪除

開(kāi)發(fā)后臺(tái)管理系統(tǒng),在業(yè)務(wù)上接觸的最多就是表單(輸入)和表格(輸出)了。對(duì)于使用 Vue 框架進(jìn)行開(kāi)發(fā)的同學(xué)來(lái)說(shuō),組件庫(kù) Element 是肯定會(huì)接觸的,而其中的 el-table 和 el-form 更是管理系統(tǒng)中的???。

然而,一旦項(xiàng)目的表格或表單多起來(lái),每個(gè)不同的配置,以及多一個(gè)字段少一個(gè)字段,都要在 template 中重新寫(xiě)一大段組件代碼,顯得非常麻煩?;蛟S你會(huì)考慮將這些代碼封裝起來(lái),可是又會(huì)發(fā)現(xiàn),封裝的表格、表單大多數(shù)只在一處地方使用,還不如不封裝呢。到底要如何封裝,可以讓每處使用 el-table 或 el-form, 都可以復(fù)用相同的組件,減少代碼量的同時(shí)又具備高度的可定制性?

本文章將會(huì)按照從無(wú)到有的步驟,按照封裝組件常用的思路來(lái)封裝 el-table,并且實(shí)現(xiàn)封裝完成的組件支持 el-table 的全配置。在封裝的過(guò)程中,你將會(huì)看到:

  • 如何抽取組件。
  • 巧用屬性透?jìng)鳌?/li>
  • v-html、component 組件、h 函數(shù)、動(dòng)態(tài)組件的應(yīng)用。
  • 具名插槽、作用域插槽。
  • v-bind 的妙用。
  • 實(shí)現(xiàn)插槽透?jìng)鞯姆椒ā?/li>

一般的組件封裝思路

以下是 el-table 在項(xiàng)目中常用的寫(xiě)法:el-table 接受一個(gè)數(shù)組 data 作為數(shù)據(jù),在 el-table 元素中插入多個(gè) el-table-column 組件,用于定義列的名稱(label),數(shù)據(jù)來(lái)源(prop),以及其它列的定制配置(width 等)。在實(shí)際項(xiàng)目中,往往不止幾行 column,甚至三四十行都有可能(不過(guò)一般超過(guò)十行,最好考慮把次要的信息放在詳情中展示,而不是全部列在表格上,除非是業(yè)務(wù)需要在表格上瀏覽所有數(shù)據(jù)),而且每個(gè) column 都可能會(huì)有不同的配置,比如排序、fix、不同寬度、插入增刪改按鈕等,這就使得一個(gè)表格的代碼會(huì)變得又長(zhǎng)又復(fù)雜,如果還要寫(xiě)其它業(yè)務(wù)功能,會(huì)大大地降低模板代碼的可讀性。

<template>
  <el-table :data="tableData" style="width: 100%">
    <el-table-column prop="a" label="aName" width="180" />
    <el-table-column prop="b" label="bName" width="180" />
    <el-table-column prop="c" label="cName" width="180" />
    <el-table-column prop="d" label="dName" width="180" />
    <el-table-column prop="e" label="eName" width="180" />
    <el-table-column prop="f" label="fName" width="180" />
    <el-table-column prop="g" label="gName" width="180" />
    <el-table-column prop="h" label="hName" width="180" />
    <el-table-column prop="i" label="iName" width="180" />
  </el-table>
</template>

<script lang="ts" setup>
const tableData = new Array(9).fill({
  a: "2016-05-03",
  b: "Tom",
  c: "No. 189, Grove St, Los Angeles",
  d: "No. 189, Grove St, Los Angeles",
  e: "2016-05-03",
  f: "Tom",
  g: "No. 189, Grove St, Los Angeles",
  h: "2016-05-03",
  i: "Tom",
});
</script>

當(dāng)然,這種情況,一般都會(huì)將它抽取為組件:

// CustomTable.vue
<template>
  <el-table :data="tableData" style="width: 100%">
    <el-table-column prop="a" label="aName" width="180" />
    <el-table-column prop="b" label="bName" width="180" />
    <el-table-column prop="c" label="cName" width="180" />
    <el-table-column prop="d" label="dName" width="180" />
    <el-table-column prop="e" label="eName" width="180" />
    <el-table-column prop="f" label="fName" width="180" />
    <el-table-column prop="g" label="gName" width="180" />
    <el-table-column prop="h" label="hName" width="180" />
    <el-table-column prop="i" label="iName" width="180" />
  </el-table>
</template>

<script lang="ts" setup>
defineProps<{
  tableData: Array<any>;
}>();
</script>

然后在頁(yè)面中使用:

// App.vue
<template>
  <CustomTable :tableData="tableData"></CustomTable>
</template>

<script lang="ts" setup>
import CustomTable from "./CustomTable.vue";
const tableData = new Array(9).fill({
  a: "2016-05-03",
  b: "Tom",
  c: "No. 189, Grove St, Los Angeles",
  d: "No. 189, Grove St, Los Angeles",
  e: "2016-05-03",
  f: "Tom",
  g: "No. 189, Grove St, Los Angeles",
  h: "2016-05-03",
  i: "Tom",
});
</script>

一般封裝的過(guò)程到這里就結(jié)束了。可見(jiàn),這種封裝既將表格從頁(yè)面中抽取出來(lái)方便單獨(dú)維護(hù),又提高了頁(yè)面代碼的可讀性。然而,這種封裝方式并沒(méi)有解決開(kāi)篇提到的書(shū)寫(xiě)重復(fù)代碼的問(wèn)題,而且還比沒(méi)有封裝多了一些操作,其實(shí)仍然是一種“體力活”。

封裝表格數(shù)據(jù)Api

為了后面演示方便,先將表格數(shù)據(jù)的 api 封裝起來(lái)。

// src/api/index.ts
export function getData() {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve({
        data: new Array(9).fill({
          a: "2016-05-03",
          b: "Tom",
          c: "No. 189, Grove St, Los Angeles",
          d: "No. 189, Grove St, Los Angeles",
          e: "2016-05-03",
          f: "Tom",
          g: "No. 189, Grove St, Los Angeles",
          h: "2016-05-03",
          i: "Tom",
        }),
      });
    }, 200);
  });
}

同時(shí),組件的位置也改為 src/components/custom-table/index.vue。

// App.vue
<template>
  <CustomTable :tableData="tableData"></CustomTable>
</template>

<script lang="ts" setup>
import CustomTable from "./components/custom-table/index.vue";
import { getData } from "./api/index";
import { onMounted, ref } from "vue";
const tableData = ref<any>([]);
onMounted(() => {
  getData().then((res: any) => {
    tableData.value = res.data;
  });
});
</script>

v-for 復(fù)用 el-table-column

先回到最初的代碼,來(lái)解決 el-table-column 復(fù)用的問(wèn)題。首先暫時(shí)不考慮 el-table-column 定制化屬性的需求,先把下面的代碼量減少,如何實(shí)現(xiàn)?很簡(jiǎn)單,使用 v-for:

// src\components\custom-table\index.vue
<template>
  <el-table :data="tableData" style="width: 100%">
    <el-table-column
      v-for="column in tableHeaders"
      :key="column"
      :prop="column"
      :label="column + 'Name'"
      width="180"
    ></el-table-column>
  </el-table>
</template>

<script lang="ts" setup>
import { computed } from "vue";
const prop = defineProps<{
  tableData: Array<any>;
}>();
const tableHeaders = computed(() => Object.keys(prop.tableData[0] || {}));
</script>

這里,我們使用數(shù)據(jù)的 key 作為列名,數(shù)據(jù)有多少個(gè)字段,就可以顯示多少列,當(dāng)數(shù)據(jù)的列數(shù)發(fā)生改變時(shí),還不需要修改任何代碼。

自定義列名和列的屬性

自定義列名

然而,數(shù)據(jù)的 key 作為列名的情況很少(至少在我們這里,一般是使用中文作為列名的),這就需要我們使用可定制的列名,并且,如果我們不想展示某些字段,上面的寫(xiě)法也是做不到的(它會(huì)顯示數(shù)據(jù)的所有字段)。

這時(shí)候,我們只需要一個(gè)映射(mapper)就可以解決這些問(wèn)題。該對(duì)象的每一個(gè)屬性對(duì)應(yīng)每一列的 prop、key,值對(duì)應(yīng)列的列名 label。

// App.vue
// 定義新的Header結(jié)構(gòu),key為column的prop/key,value為column的label
const tableHeaderMapper = {
  a: "列a",
  b: "列b",
  c: "列c",
  d: "列d",
  e: "列e",
  f: "列f",
  g: "列g(shù)",
  h: "列h",
  i: "列i",
};

遍歷并綁定:

// src\components\custom-table\index.vue
<template>
  <el-table :data="tableData" style="width: 100%">
    <el-table-column
      v-for="(value, key) in tableHeaders"
      :key="key"
      :prop="key"
      :label="value"
    ></el-table-column>
  </el-table>
</template>

<script lang="ts" setup>
export type Mapper<T> = {
  [P in keyof T as string]?: string;
};
defineProps<{
  tableData: Array<any>;
  tableHeaders: Mapper<any>;
}>();
</script>

使用:

  <CustomTable :tableData="tableData" :table-headers="tableHeaderMapper"></CustomTable>

v-bind 綁定所有屬性

在 Vue 中,可以使用 v-bind 直接綁定對(duì)象,簡(jiǎn)化很多代碼:

<el-table-column
  :key="key"
  :prop="key"
  :label="value"
></el-table-column>

等同:

<el-table-column
  v-bind="{key:key,prop:key,label:value}"
></el-table-column>

所以,我們其實(shí)可以這樣定義 tableHeaders!

const tableHeaderMapper = {
  a: {
	  label: "列a",
	  key: "a",
	  prop: "a"
  },
  ...
};

改寫(xiě)組件代碼:

// src\components\custom-table\index.vue
// v-bind 直接綁定一個(gè)對(duì)象
<template>
  <el-table :data="tableData" style="width: 100%">
    <el-table-column
      v-for="column in tableHeaders"
      v-bind="column"
    ></el-table-column>
  </el-table>
</template>

<script lang="ts" setup>
export type Mapper<T> = {
  [P in keyof T as string]?: object; // 從string類型改為object類型
};
defineProps<{
  tableData: Array<any>;
  tableHeaders: Mapper<any>;
}>();
</script>

如果覺(jué)得重復(fù)寫(xiě) key、prop 太冗余,又或者想兼容之前自定義列名的寫(xiě)法,可以在自定義組件中對(duì) tableHeaders 進(jìn)行處理,生成 newTableHeaders。

// src\components\custom-table\index.vue
<template>
  <el-table :data="tableData" style="width: 100%">
    <el-table-column
      v-for="column in newTableHeader"
      v-bind="column"
    ></el-table-column>
  </el-table>
</template>

<script lang="ts" setup>
import { ref, onMounted, onBeforeUpdate } from "vue";
export type Mapper<T> = {
  [P in keyof T as string]?: string | object;
};
const prop = defineProps<{
  tableData: Array<any>;
  tableHeaders: Mapper<any>;
}>();
const newTableHeader = ref<any>({});
const genNewTableHeader = () => {
  newTableHeader.value = { ...prop.tableHeaders };
  const rawAttr = prop.tableHeaders;
  for (let key in rawAttr) {
    let column = rawAttr[key];
    if (typeof column === "string") {
      Reflect.set(newTableHeader.value, key, {
        key: key,
        prop: key,
        label: column,
      });
    }

    // 其實(shí)此時(shí)一定是對(duì)象了,此處判斷是用于ts類型收窄
    if (typeof column === "object") {
      // 設(shè)置默認(rèn)的key
      if (!Reflect.has(column, "key")) {
        Reflect.set(column, "key", key);
      }
      if (!Reflect.has(column, "label")) {
        Reflect.set(column, "label", key);
      }
      // 設(shè)置默認(rèn)的prop,如果該列是多選項(xiàng),則不需要prop
      if (
        !Reflect.has(column, "prop") &&
        !(
          Reflect.has(column, "type") &&
          Reflect.get(column, "type") == "selection"
        )
      ) {
        Reflect.set(column, "prop", key);
      }
    }
  }
  console.log(newTableHeader.value);
};
onMounted(genNewTableHeader);
onBeforeUpdate(genNewTableHeader);
</script>

使用:

// App.vue
const tableHeaderMapper = {
  a: {
    label: "列a",
    width: "200",
  },
  b: "列b",
  c: "列c",
  d: "列d",
  e: "列e",
  f: "列f",
  g: "列g(shù)",
  h: "列h",
  i: "列i",
};

到這里,對(duì) el-tabl 的封裝已經(jīng)相對(duì)完善了,我們不需要書(shū)寫(xiě)復(fù)雜的 el-table-column,只需傳入 tableData 和 tableHeaders,就可以自由定制我們的表格列了。

表格透?jìng)?/h2>

我們解決了表格列屬性問(wèn)題,現(xiàn)在,如果我們還想要表格屬性和表格列屬性一樣可以自由傳入,如何實(shí)現(xiàn)?

屬性、樣式、事件透?jìng)?/h3>

Vue 天然支持屬性、樣式、事件透?jìng)?,我們直接?CustomeTable 上書(shū)寫(xiě)我們想要的 el-table 屬性或事件即可!

// App.vue
<template>
  <CustomTable
    :tableData="tableData"
    :table-headers="tableHeaderMapper"
    v-loading="loading"
  ></CustomTable>
</template>

<script lang="ts" setup>
...
const loading = ref(false);
onMounted(() => {
  loading.value = true;
  getData().then((res: any) => {
    tableData.value = res.data;
    loading.value = false;
  });
});
</script>

插槽透?jìng)?/h3>

仔細(xì)觀察 el-table 屬性,發(fā)現(xiàn)它還支持三個(gè)插槽:默認(rèn)插槽、append、empty。我們的 CustomTable 也要支持!

先回顧以下我們的 CustomTable。在 custom-table/index. vue 中,我們并沒(méi)有書(shū)寫(xiě) slot 標(biāo)簽,所以在 App. vue 中往 CustomTable 插入標(biāo)簽是不會(huì)被渲染的,所以我們需要給 CustomTable 添加 slot 插槽,要添加什么樣的插槽呢?和 el-table 提供同名的插槽,并在 custom-table/index. vue 中的 el-table 中插入。即,CustomTable 提供插槽,App. vue 寫(xiě)入插槽,CustomTable 讀取到插槽,并把插槽的內(nèi)容寫(xiě)入 el-table 中。插槽的內(nèi)容是這樣傳遞的:App. vue -> CustomTable -> el-table。

在 CustomTable 中開(kāi)始寫(xiě)插槽前,會(huì)發(fā)現(xiàn),我們已經(jīng)使用了 el-table 的插槽,將我們 v-for 生成的 column 插入到 el-table 的默認(rèn)插槽中了。這個(gè)時(shí)候,我們需要改變我們的寫(xiě)法:將 column 的生成也拆分為組件!然后傳入給 CustomTable,而 CustomTable 的職責(zé)則變?yōu)椋褐回?fù)責(zé)從 App. vue 傳遞插槽給 el-table。符合單一職責(zé)的封裝原則!

// 新的CustomTable,只負(fù)責(zé)傳遞插槽
<template>
  <el-table :data="tableData" style="width: 100%">
    <slot></slot>
  </el-table>
</template>

<script lang="ts" setup>
defineProps<{
  tableData: Array<any>;
}>();
</script>

新建 src/components/custom-column/index. vue,修改少許代碼。

// 抽取為CustomColumn
<template>
  <el-table-column
    v-for="column in newTableHeader"
    v-bind="column"
  ></el-table-column>
</template>

<script lang="ts" setup>
import { ref, onMounted, onBeforeUpdate } from "vue";
export type Mapper<T> = {
  [P in keyof T as string]?: string | object;
};
const prop = defineProps<{
  tableHeaders: Mapper<any>;
}>();
const newTableHeader = ref<any>({});
const genNewTableHeader = () => {
  newTableHeader.value = { ...prop.tableHeaders };
  const rawAttr = prop.tableHeaders;
  for (let key in rawAttr) {
    let column = rawAttr[key];
    if (typeof column === "string") {
      Reflect.set(newTableHeader.value, key, {
        key: key,
        prop: key,
        label: column,
      });
    }

    // 其實(shí)此時(shí)一定是對(duì)象了,此處判斷是用于ts類型收窄
    if (typeof column === "object") {
      // 設(shè)置默認(rèn)的key
      if (!Reflect.has(column, "key")) {
        Reflect.set(column, "key", key);
      }
      if (!Reflect.has(column, "label")) {
        Reflect.set(column, "label", key);
      }
      // 設(shè)置默認(rèn)的prop,如果該列是多選項(xiàng),則不需要prop
      if (
        !Reflect.has(column, "prop") &&
        !(
          Reflect.has(column, "type") &&
          Reflect.get(column, "type") == "selection"
        )
      ) {
        Reflect.set(column, "prop", key);
      }
    }
  }
  console.log(newTableHeader.value);
};
onMounted(genNewTableHeader);
onBeforeUpdate(genNewTableHeader);
</script>

使用:

// App.vue
<template>
  <CustomTable :tableData="tableData" v-loading="loading">
    <CustomColumn :table-headers="tableHeaderMapper"></CustomColumn>
  </CustomTable>
</template>

<script lang="ts" setup>
import CustomColumn from "./components/custom-column/index.vue";
...

上面我們只是支持傳遞默認(rèn)插槽,現(xiàn)在我們讓 CustomTable 將所有插槽原封不動(dòng)傳給 el-table,實(shí)現(xiàn)“插槽透?jìng)鳌埃?/p>

// src\components\custom-table\index.vue
<template>
  <el-table :data="tableData" style="width: 100%">
    <template v-for="slot in Object.keys($slots)" #[slot]>
      <slot :name="slot"></slot>
    </template>
  </el-table>
</template>

<script setup lang="ts">
defineProps<{
  tableData: Array<any>;
}>();
</script>

Vue 中并沒(méi)有實(shí)現(xiàn)插槽透?jìng)鞯姆椒ǎ覀冏龅钠鋵?shí)是插槽傳遞,而非透?jìng)?,但?duì)于組件的使用者來(lái)說(shuō),這種傳遞方法看起來(lái)和透?jìng)鳠o(wú)異!

到了這一步,我們的 CustomTable 的基礎(chǔ)功能就算封裝完成啦!支持 el-tabled 的所有屬性和插槽。如有需要,可以在 CustomTable 的基礎(chǔ)上進(jìn)行修改,設(shè)計(jì)出我們自己的專屬 Table!

在列中插入子元素

表格列中重要的一個(gè)內(nèi)容,就是插入各種按鈕(增刪改)、圖片、進(jìn)度條以及其它元素,來(lái)實(shí)現(xiàn)表格的可操作性、數(shù)據(jù)可視化。在 el-table-column 中,我們想插入元素,直接使用插槽即可。然而,這里并不能完全照搬表格的插槽傳遞的方法,因?yàn)槟愫芸炀蜁?huì)發(fā)現(xiàn),CustomColumn 只有一個(gè),而 el-table-column 卻有很多個(gè),我怎么知道插入 CustomColumn 的插槽是屬于哪個(gè) el-table-column 的!

非插槽寫(xiě)法 v-html

如何在不同的 column 插入元素?很快我們就會(huì)想起我們是如何在不同 column 中定義不同的列名,對(duì),tableHeaders!我們可以給 tableHeaders 定義一個(gè)屬性,只要寫(xiě)到這個(gè)屬性中,就插入到對(duì)應(yīng)的列的位置上!我們暫且稱這個(gè)屬性為 inner。

const tableHeaderMapper = {
  a: {
    label: "列a",
    width: "200",
    inner: xxx
  },

接下來(lái)就是如何讀取 inner 并插入了,我們很容易就想起 v-html:

// CustomColumn
<template>
  <el-table-column v-for="column in newTableHeader" v-bind="column">
    <div v-if="column.inner" v-html="column.inner"></div>
  </el-table-column>
</template>

inner 的值為字符串:

const tableHeaderMapper = {
  a: {
    label: "列a",
    inner: "<h1>hello</h1>",
  },

在字符串中寫(xiě) html,顯然不是很方便,而一說(shuō)到字符串中寫(xiě) html,我們自然而然就會(huì)想到 jsx!那么這里我們能否接受 jsx 呢?顯然不能,但是可以接受一個(gè)返回組件的函數(shù)嗎?可以一試!

const tableHeaderMapper = {
  a: {
    label: "列a",
    inner: h('h1','function component!'),
  },

直接運(yùn)行,可以看到對(duì)應(yīng)列顯示:
b9be8202403111113493938.png
其實(shí)就是輸出一個(gè)組件對(duì)象。如何應(yīng)用這個(gè)對(duì)象?不妨試試 component。

// CustomColumn
<template>
  <el-table-column v-for="column in newTableHeader" v-bind="column">
    <div v-if="typeof column.inner == 'string'" v-html="column.inner"></div>
    <component v-else :is="column.inner"></component>
  </el-table-column>
</template>

瀏覽頁(yè)面:

12611202403111113577740.png

好極了。

既然支持 h 渲染函數(shù),能否支持導(dǎo)入的組件?

const tableHeaderMapper = {
  a: {
    label: "列a",
    width: "200",
    inner: CustomButton,

component 也是支持的:
dbcdb202403111114052361.png

插槽寫(xiě)法

如何在 CustomColumn 的插槽中區(qū)分不同的插槽?其實(shí)也很簡(jiǎn)單,規(guī)定一個(gè)格式,使用不同的后綴即可。比如,a 列的插槽為 default-a,b 列的插槽為 default-b。我們可以使用 useSlots 讀取傳入 CustomColumn 中的所有插槽,并使用正則進(jìn)行匹配。

const slots = useSlots();
const slotKeys = Object.keys(slots);
for(let key of keys){
	const res = key.match(/^(\S+)-(\S+)/);
	console.log(res);
}

假設(shè)傳入:

    <CustomColumn :table-header="tableHeaderMapper">
      <template #default-a> I am slot of a </template>
    </CustomColumn>

輸出:

const slots = useSlots() // { 'default-a': V_node }
const slotKeys = Object.keys(slots); // ['default-a']
for(let key of keys){
	const res = key.match(/^(\S+)-(\S+)/);
	console.log(res); // ['default-a','default','a']
}

根據(jù) res 的值,我們可以很快確定插槽是屬于哪一列的、哪一種類型的插槽。

插槽能夠區(qū)分和讀取了,接下來(lái)如何插入?

試想一下我們之前是如何插入列的 inner 屬性的,很快就有思路了。

// CustomColumn
<template>
  <el-table-column v-for="column in newTableHeader" v-bind="column">
    <template v-for="(value, key) in column.slot" #[key]>
      <slot :name="value">
        <div v-if="column.inner && String(key) == 'default'">
          <div
            v-if="typeof column.inner == 'string'"
            v-html="column.inner"
          ></div>
          <component v-else :is="column.inner"></component>
        </div>
      </slot>
    </template>
    <!-- <div v-if="typeof column.inner == 'string'" v-html="column.inner"></div> -->
    <!-- <component v-else :is="column.inner"></component> -->
  </el-table-column>
</template>

<script lang="ts" setup>
import { ref, onMounted, onBeforeUpdate, useSlots } from "vue";
...
const slots = useSlots();
const newTableHeader = ref<any>({});
const genNewTableHeader = () => {
  newTableHeader.value = { ...prop.tableHeaders };
  const rawAttr = prop.tableHeaders;
  for (let key in rawAttr) {
	...
    if (typeof column === "object") {
      // 設(shè)置默認(rèn)的key
      ...
      // 設(shè)置默認(rèn)的prop,如果該列是多選項(xiàng),則不需要prop
      ...
      // 處理插槽
      const slotKeys = Object.keys(slots);
      for (let key of slotKeys) {
        const res = key.match(/^(\S+)-(\S+)/);
        // 查找不到則res為null
        if (res && res[2] == Reflect.get(column, "key")) {
          if (!Reflect.has(column, "slot")) {
            Reflect.set(column, "slot", {});
          }
          Reflect.set(Reflect.get(column, "slot"), res[1], res[0]);
        }
      }
    }
  }
  console.log(newTableHeader.value);
};
...
</script>

這里我們預(yù)先處理 slots,和 inner 屬性類似,在渲染 column 時(shí),如果有 inner 就渲染 inner。我們?cè)?column 對(duì)象(column 對(duì)象位于 newTableHeaders 上)上綁定了一個(gè) slot 屬性,在渲染 column 時(shí),如果有 slot 就渲染 slot。

兼容 inner 和 slot:因?yàn)?inner 也是渲染在 default 上,所以上面的代碼需要將 inner 注釋掉,以免沖突。這里我選擇在 slot 存在的情況下,忽略 inner(你也可以選擇其它處理方式,比如二者存在時(shí),都渲染出來(lái),或者存在 inner,就忽略slot)。

// CustomColumn
<template>
  <el-table-column v-for="column in newTableHeader" v-bind="column">
    <template v-for="(value, key) in column.slot" #[key]>
      <slot :name="value">
        <div v-if="column.inner && String(key) == 'default'">
          <div
            v-if="typeof column.inner == 'string'"
            v-html="column.inner"
          ></div>
          <component v-else :is="column.inner"></component>
        </div>
      </slot>
    </template>
    <template v-if="!column.slot" #default>
      <div v-if="column.inner">
        <div v-if="typeof column.inner == 'string'" v-html="column.inner"></div>
        <component v-else :is="column.inner"></component>
      </div>
    </template>
  </el-table-column>
</template>

插槽作用域

在點(diǎn)擊按鈕時(shí)(修改列、刪除列),我們需要得到點(diǎn)擊對(duì)應(yīng)列的信息,該信息是通過(guò)插槽作用域?qū)崿F(xiàn)的。

// CustomColumn
<template>
  <el-table-column v-for="column in newTableHeader" v-bind="column">
    <template v-for="(value, key) in column.slot" #[key]="scope">
      <slot :name="value" v-bind="scope">
        <div v-if="column.inner && String(key) == 'default'">
          <div
            v-if="typeof column.inner == 'string'"
            v-html="column.inner"
          ></div>
          <component v-else :is="column.inner"></component>
        </div>
      </slot>
    </template>
    <template v-if="!column.slot" #default>
      <div v-if="column.inner">
        <div v-if="typeof column.inner == 'string'" v-html="column.inner"></div>
        <component v-else :is="column.inner"></component>
      </div>
    </template>
  </el-table-column>
</template>

這里使用 template 的 #插槽 =“xxx”讀取了 el-table-column 提供的列信息并保存 xxx 為 scope,然后又使用 v-bind 將 scope 通過(guò) slot 的屬性傳遞給 CustomColumn 的使用者。

// App.vue 使用scope可以讀取對(duì)應(yīng)的列的信息
<template>
  <CustomTable :tableData="tableData" v-loading="loading">
    <template #empty>暫無(wú)數(shù)據(jù)</template>
    <CustomColumn :table-headers="tableHeaderMapper">
    // 使用scope.$index讀取該列在表格中的索引
      <template #default-a="scope"> I am slot of a {{ scope.$index }}</template>
    </CustomColumn>
  </CustomTable>
</template>

關(guān)于 scope 對(duì)象的取值可以查閱 Element 的文檔(Table 表格 | Element Plus

c4dd9202403111114297553.png

總結(jié)

本文章通過(guò)通俗易懂、循序漸進(jìn)的方法,介紹了如何對(duì) el-table 進(jìn)行基礎(chǔ)性的二次封裝,讓我們使用表格的代碼量減到最少的同時(shí),又具備極高的可定制性和可維護(hù)性。同時(shí),又在封裝的過(guò)程中掌握了 v-for、v-if、v-else、v-html、v-slot 等內(nèi)置指令的用法、屬性透?jìng)骱筒宀鄣母拍睢?/p>

作者:天氣好


請(qǐng)登錄后查看

CRMEB-慕白寒窗雪 最后編輯于2024-03-11 11:15:27

快捷回復(fù)
回復(fù)
回復(fù)
回復(fù)({{post_count}}) {{!is_user ? '我的回復(fù)' :'全部回復(fù)'}}
排序 默認(rèn)正序 回復(fù)倒序 點(diǎn)贊倒序

{{item.user_info.nickname ? item.user_info.nickname : item.user_name}} LV.{{ item.user_info.bbs_level || item.bbs_level }}

作者 管理員 企業(yè)

{{item.floor}}# 同步到gitee 已同步到gitee {{item.is_suggest == 1? '取消推薦': '推薦'}}
{{item.is_suggest == 1? '取消推薦': '推薦'}}
沙發(fā) 板凳 地板 {{item.floor}}#
{{item.user_info.title || '暫無(wú)簡(jiǎn)介'}}
附件

{{itemf.name}}

{{item.created_at}}  {{item.ip_address}}
打賞
已打賞¥{{item.reward_price}}
{{item.like_count}}
{{item.showReply ? '取消回復(fù)' : '回復(fù)'}}
刪除
回復(fù)
回復(fù)

{{itemc.user_info.nickname}}

{{itemc.user_name}}

回復(fù) {{itemc.comment_user_info.nickname}}

附件

{{itemf.name}}

{{itemc.created_at}}
打賞
已打賞¥{{itemc.reward_price}}
{{itemc.like_count}}
{{itemc.showReply ? '取消回復(fù)' : '回復(fù)'}}
刪除
回復(fù)
回復(fù)
查看更多
打賞
已打賞¥{{reward_price}}
5060
{{like_count}}
{{collect_count}}
添加回復(fù) ({{post_count}})

相關(guān)推薦

快速安全登錄

使用微信掃碼登錄
{{item.label}} 加精
{{item.label}} {{item.label}} 板塊推薦 常見(jiàn)問(wèn)題 產(chǎn)品動(dòng)態(tài) 精選推薦 首頁(yè)頭條 首頁(yè)動(dòng)態(tài) 首頁(yè)推薦
取 消 確 定
回復(fù)
回復(fù)
問(wèn)題:
問(wèn)題自動(dòng)獲取的帖子內(nèi)容,不準(zhǔn)確時(shí)需要手動(dòng)修改. [獲取答案]
答案:
提交
bug 需求 取 消 確 定
打賞金額
當(dāng)前余額:¥{{rewardUserInfo.reward_price}}
{{item.price}}元
請(qǐng)輸入 0.1-{{reward_max_price}} 范圍內(nèi)的數(shù)值
打賞成功
¥{{price}}
完成 確認(rèn)打賞

微信登錄/注冊(cè)

切換手機(jī)號(hào)登錄

{{ bind_phone ? '綁定手機(jī)' : '手機(jī)登錄'}}

{{codeText}}
切換微信登錄/注冊(cè)
暫不綁定
CRMEB客服

CRMEB咨詢熱線 咨詢熱線

400-8888-794

微信掃碼咨詢

CRMEB開(kāi)源商城下載 源碼下載 CRMEB幫助文檔 幫助文檔
返回頂部 返回頂部
CRMEB客服