目錄
效果圖?
組件封裝
?parseTime函數(shù)
debounce?函數(shù)
render通用渲染模版
頁(yè)面使用
【擴(kuò)展】vue 函數(shù)式組件
函數(shù)式組件特點(diǎn):
函數(shù)式組件的優(yōu)點(diǎn):
【擴(kuò)展】vue中的render函數(shù)
一、初步認(rèn)識(shí)render函數(shù)
二、為什么使用render函數(shù)
三、render函數(shù)的解析
【擴(kuò)展】添加操作欄顯示權(quán)限
結(jié)構(gòu)改動(dòng)
邏輯新增
組件引入使用?
【擴(kuò)展?】?props與attrs
props(Properties):
attrs(Attributes):
attrs的屬性?
應(yīng)用場(chǎng)景:
使用組件需先下載 element ui? vue2
引入全局注冊(cè)自定義table組件
效果圖?
組件封裝
這段代碼是一個(gè)封裝的分頁(yè)表格組件。它使用了Vue.js框架,并結(jié)合了Element UI中的el-table和el-pagination組件來(lái)實(shí)現(xiàn)。該組件接受一系列屬性作為數(shù)據(jù)源,包括列表數(shù)據(jù)(list)、表格列(columns)、操作按鈕列(operates)、總數(shù)(total)、分頁(yè)參數(shù)(pagination)等。
在模板中,使用el-table組件來(lái)展示表格數(shù)據(jù),使用el-pagination組件來(lái)實(shí)現(xiàn)分頁(yè)功能。el-table-column用來(lái)定義每列的樣式和展示內(nèi)容。組件中還包括其他的一些方法和事件處理函數(shù),如handleSizeChange用于切換每頁(yè)顯示的數(shù)量,handleIndexChange用于切換頁(yè)碼,handleSelectionChange處理多行選中等。
此外,還定義了一些樣式規(guī)則來(lái)美化表格的顯示效果,如設(shè)置表頭顏色、調(diào)整列的樣式、設(shè)置操作按鈕組的布局、設(shè)置篩選彈窗和表格操作彈窗的樣式等。
最后,使用了scss來(lái)編寫(xiě)樣式表,通過(guò)設(shè)置類(lèi)名來(lái)控制樣式的展示。
-
在
<template>
標(biāo)簽中,定義了一個(gè)表格組件,包括表格本身,列定義,按鈕操作組,和分頁(yè)控件。 -
在
<script>
標(biāo)簽中,導(dǎo)入了必要的依賴(lài),并定義了組件的屬性(props)、數(shù)據(jù)(data)、生命周期鉤子(activated、beforeDestroy、deactivated、created、mounted)和方法(methods)。 -
props
屬性用于接受從父組件傳遞進(jìn)來(lái)的數(shù)據(jù),如數(shù)據(jù)列表?list
、列定義?columns
、按鈕操作?operates
、總數(shù)?total
、分頁(yè)參數(shù)?pagination
?等。 -
data
數(shù)據(jù)屬性包括一些內(nèi)部狀態(tài),如表格高度?height
、監(jiān)聽(tīng)窗口調(diào)整大小的回調(diào)函數(shù)?$_resizeHandler
、當(dāng)前頁(yè)碼?pageIndex
、表格分頁(yè)信息?tableCurrentPagination
、多行選中數(shù)據(jù)?multipleSelection
?等。 -
在生命周期鉤子中,組件監(jiān)聽(tīng)窗口大小調(diào)整事件,自動(dòng)調(diào)整表格高度,并在適當(dāng)?shù)纳芷谥谐跏蓟弯N(xiāo)毀事件監(jiān)聽(tīng)器。
-
methods
包括一系列方法,如計(jì)算表頭寬度的方法?headSpanFit
、初始化事件監(jiān)聽(tīng)器的方法?initListener
、銷(xiāo)毀事件監(jiān)聽(tīng)器的方法?destroyListener
、調(diào)整表格高度的方法?resize
、處理每頁(yè)顯示數(shù)量變化的方法?handleSizeChange
、處理頁(yè)碼變化的方法?handleIndexChange
、處理多行選中的方法?handleSelectionChange
?等。 -
最后,在
<style>
標(biāo)簽中定義了一些樣式,包括表格樣式、分頁(yè)樣式、按鈕樣式、篩選和操作彈窗樣式等。
<!--region 封裝的分頁(yè) table-->
<!--region 封裝的分頁(yè) table-->
<template>
<div class="table">
<el-table
id="iTable"
ref="mutipleTable"
:row-class-name="tableRowClassName"
:header-cell-style="getRowClass"
v-loading="options.loading"
:border="options.border"
:data="list"
:height="noStatic ? customHeight : height"
:max-height="noStatic ? customHeight : height"
v-bind="options"
@selection-change="handleSelectionChange"
@sort-change="sortChange"
>
<!--region 選擇框-->
<el-table-column
v-if="options.mutiSelect"
type="selection"
style="width: 60px"
:selectable="options.selectable"
>
</el-table-column>
<!--endregion-->
<!--region 序號(hào)-->
<el-table-column
v-if="options.numbers"
width="60"
type="index"
label="序號(hào)"
align="center"
></el-table-column>
<!--endregion-->
<!--region 數(shù)據(jù)列-->
<template v-for="(column, index) in columns">
<el-table-column
v-if="isShowColumn(column)"
:min-width="headSpanFit(column)"
:key="index"
:prop="column.prop"
:label="column.label"
:align="column.align"
:width="column.width"
v-bind="column.el"
>
<template slot-scope="scope">
<template v-if="!column.render">
<template v-if="column.formatter">
<span v-html="column.formatter(scope.row, column)"></span>
</template>
<template v-else>
<span>{{
scope.row[column.prop] === 0 ? 0 : scope.row[column.prop] || "--"
}}</span>
</template>
</template>
<template v-else>
<expand-dom
:column="column"
:row="scope.row"
:render="column.render"
:index="index"
></expand-dom>
</template>
</template>
</el-table-column>
</template>
<!--endregion-->
<!--region 按鈕操作組-->
<el-table-column
ref="fixedColumn"
label="操作"
align="center"
:width="operates && operates.width"
:fixed="operates && operates.fixed"
v-if="operates && operates.list.length > 0"
>
<template slot-scope="scope">
<expand-dom
:row="scope.row"
:render="renderOperates"
:index="scope.$index"
></expand-dom>
</template>
</el-table-column>
<!--endregion-->
</el-table>
<div style="height: 12px"></div>
<!--region 分頁(yè)-->
<el-pagination
style="float: none; text-align: right"
v-if="pagination"
:pager-count="5"
@size-change="handleSizeChange"
@current-change="handleIndexChange"
:page-size.sync="pagination.pageSize"
:page-sizes="pagination.pageSizes || pageArray"
:current-page.sync="pagination.pageIndex"
layout="total,sizes, prev, pager, next,jumper"
:total="total"
></el-pagination>
<!--endregion-->
</div>
</template>
<!--endregion-->
<script>
import { debounce } from "@/utils";
import { checkPermi } from "@/utils/permission.js";
const _pageArray = [10, 20, 30, 50]; // 每頁(yè)展示條數(shù)的控制集合
export default {
props: {
list: {
type: Array,
default: [], // prop:表頭綁定的地段,label:表頭名稱(chēng),align:每列數(shù)據(jù)展示形式(left, center, right),width:列寬
}, // 數(shù)據(jù)列表
columns: {
type: Array,
default: [], // 需要展示的列 === prop:列數(shù)據(jù)對(duì)應(yīng)的屬性,label:列名,align:對(duì)齊方式,width:列寬
},
operates: {
type: Object,
defaultt: () => {}, // width:按鈕列寬,fixed:是否固定(left,right),按鈕集合 === label: 文本,type :類(lèi)型(primary / success / warning / danger / info / text),show:是否顯示,icon:按鈕圖標(biāo),plain:是否樸素按鈕,disabled:是否禁用,method:回調(diào)方法
},
total: {
type: Number,
default: 0,
}, // 總數(shù)
pagination: {
type: Object,
default: null, // 分頁(yè)參數(shù) === pageSize:每頁(yè)展示的條數(shù),pageIndex:當(dāng)前頁(yè),pageArray: 每頁(yè)展示條數(shù)的控制集合,默認(rèn) _page_array
},
noStatic: false, // 是否計(jì)算表格高度
customHeight: {
//與noStatic一起使用
type: Number,
default: 320,
},
otherHeight: {
type: Number,
default: 180,
}, // 計(jì)算表格的高度
options: {
type: Object,
default: {
stripe: false, // 是否為斑馬紋 table
loading: false, // 是否添加表格loading加載動(dòng)畫(huà)
highlightCurrentRow: false, // 是否支持當(dāng)前行高亮顯示
mutiSelect: false, // 是否支持列表項(xiàng)選中功能
border: false, //是否顯示邊框
selectable: () => {
//是否可以選中
return false;
},
},
}, // table 表格的控制參數(shù)
},
components: {
expandDom: {
functional: true,
props: {
row: Object,
render: Function,
index: Number,
column: {
type: Object,
default: null,
},
},
render: (h, ctx) => {
const params = {
row: ctx.props.row,
index: ctx.props.index,
};
if (ctx.props.column) params.column = ctx.props.column;
return ctx.props.render(h, params);
},
},
},
data() {
return {
height: 250,
$_resizeHandler: null,
pageIndex: 1,
pageArray: _pageArray,
multipleSelection: [], // 多行選中
};
},
activated() {
// 通常是在使用 Vue.js 的 <keep-alive> 包裝時(shí),組件會(huì)被緩存并在重新激活時(shí)調(diào)用這個(gè)鉤子函數(shù)。
if (!this.$_resizeHandler) {
// avoid duplication init
this.initListener();
}
// when keep-alive chart activated, auto resize
this.resize();
},
//當(dāng)組件即將被銷(xiāo)毀(beforeDestroy)時(shí),會(huì)調(diào)用這個(gè)鉤子函數(shù)。
beforeDestroy() {
this.destroyListener();
},
//當(dāng)組件被停用(deactivated),通常也是在 <keep-alive> 包裝下,組件會(huì)調(diào)用這個(gè)鉤子函數(shù)。
deactivated() {
this.destroyListener();
},
created() {},
mounted() {
this.initListener();
},
methods: {
//計(jì)算小列寬
headSpanFit(column) {
let labelLong = column.label.length; // 表頭label長(zhǎng)度
let size = 20; // 根據(jù)需要定義標(biāo)尺,直接使用字體大小確定就行,也可以根據(jù)需要定義
let minWidth = labelLong * size < 100 ? 100 : labelLong * size; // 根據(jù)label長(zhǎng)度計(jì)算該表頭最終寬度
return minWidth;
},
// 初始化監(jiān)聽(tīng)器的方法。
initListener() {
this.$_resizeHandler = debounce(() => {
this.resize();
}, 200);
window.addEventListener("resize", this.$_resizeHandler);
this.$nextTick(() => {
this.resize();
});
},
//這是一個(gè)銷(xiāo)毀監(jiān)聽(tīng)器的方法。
destroyListener() {
window.removeEventListener("resize", this.$_resizeHandler);
this.$_resizeHandler = null;
},
//計(jì)算表格的高度
resize() {
// 不用計(jì)算
if (this.noStatic) {
return;
}
const { mutipleTable } = this.$refs;
let staticHeight =
window.innerHeight - this.$refs.mutipleTable.$el.offsetTop - this.otherHeight;
this.height = staticHeight < 250 ? 250 : staticHeight;
console.log(this.height);
//保表格的布局在高度調(diào)整后得以更新。
mutipleTable && mutipleTable.doLayout();
},
// 切換每頁(yè)顯示的數(shù)量
handleSizeChange(size) {
if (this.pagination) {
this.$emit("handleSizeChange", { ...this.pagination });
}
},
// 切換頁(yè)碼
handleIndexChange(currnet) {
if (this.pagination) {
this.$emit("handleIndexChange", { ...this.pagination });
}
},
// 多行選中
handleSelectionChange(val) {
this.multipleSelection = val;
this.$emit("handleSelectionChange", val);
},
// 用于渲染操作按鈕的方法
renderOperates(h, params) {
const endArr = this.rebuildList(params); //權(quán)限驗(yàn)證后最終按鈕數(shù)組
let outSideBtnArr = endArr.slice(0, 2); //外部按鈕 默認(rèn)前兩個(gè)
let insideArr = endArr.slice(2); //下拉菜單按鈕數(shù)組
const buttonArr = []; //最終渲染數(shù)組
outSideBtnArr.forEach((item) => {
buttonArr.push(this.renderOutsideButton(h, item, params));
});
if (insideArr.length > 0) {
buttonArr.push(this.renderDropdownButton(h, insideArr, params));
}
return h("div", { attrs: { class: "operate-group" } }, buttonArr);
},
// 渲染外部按鈕
renderOutsideButton(h, item, params) {
return h(
"el-button",
{
// 組件的屬性(數(shù)據(jù))
props: {
type: item.type || "text", //類(lèi)型(primary / success / warning / danger / info / text)
icon: item.icon || "", //icon:按鈕圖標(biāo)
plain: item.plain || false, //plain:是否樸素按鈕
size: item.size || "mini", //大小
},
// 組件的屬性(html屬性)
attrs: {
title: item.label, //label: 文本
},
// 樣式
style: {
color: item.color,
},
// 按鈕的點(diǎn)擊事件處理函數(shù)
on: {
click: () => {
item.method(params.index, params.row);
},
},
},
item.label //label: 文本
);
},
// 渲染下拉按鈕
renderDropdownButton(h, insideArr, params) {
return h(
// 創(chuàng)建了一個(gè) "el-dropdown" 組件
"el-dropdown",
// dropdown 樣式
{
class: ["custom-dropdown"],
// 組件的屬性(數(shù)據(jù)):
props: {
trigger: "click", //下拉框點(diǎn)擊觸發(fā)
},
},
[
// "el-dropdown" 組件中的button
h(
"el-button",
{
// 按鈕樣式
style: {
fontSize: "18px",
},
// 按鈕類(lèi)名
class: ["custom-text"],
props: {
type: "text",
plain: false,
size: "mini",
},
},
// 在按鈕的內(nèi)容中,使用了一個(gè) "i" 標(biāo)簽,其 class 屬性為 "el-icon-more",顯示一個(gè)圖標(biāo)。
[h("i", { class: "el-icon-more" })]
),
// 創(chuàng)建了一個(gè) "el-dropdown-menu" 組件
h(
"el-dropdown-menu",
// 傳入slot
{
slot: "dropdown",
},
// 對(duì)傳入的按鈕循環(huán)渲染出el-dropdown-item"
insideArr.map((item) => {
return h(
"el-dropdown-item",
{
nativeOn: {
click: () => {
item.method(params.index, params.row);
},
},
},
item.label
);
})
),
]
);
},
// 校驗(yàn)權(quán)限
rebuildList({ row }) {
// 檢查 operates 是否為空對(duì)象
if (!this.operates || !this.operates.list || !Array.isArray(this.operates.list)) {
return [];
}
// 過(guò)濾操作項(xiàng)
return this.operates.list.filter((item) => {
// 如果定義了 show 函數(shù),則根據(jù)該函數(shù)的返回值來(lái)決定是否顯示
if (typeof item.show === "function") {
return item.show(row);
}
// 如果 show 是布爾值且為 true,則顯示
if (typeof item.show === "boolean" && item.show) {
return true;
}
// 如果定義了權(quán)限要求,則檢查權(quán)限
if (item.haspermission) {
return checkPermi(item.haspermission);
}
// 默認(rèn)允許顯示
return true;
});
},
// 判斷列的數(shù)據(jù)是否顯示
isShowColumn(column) {
if (column.show === undefined) {
return true;
}
if (typeof column.show !== "function") {
return column.show;
}
return column.show(column);
},
// 排序
sortChange({ column, prop, order }) {
this.$emit("sortChange", { column, prop, order });
},
// 每一行的樣式
tableRowClassName({ rowIndex }) {
if (rowIndex % 2 === 0) {
return "warning-row";
} else if (rowIndex % 2 === 1) {
return "success-row";
}
return "";
},
// 表頭樣式
getRowClass({ rowIndex }) {
if (rowIndex == 0) {
return this.options.headerCellStyle || "";
} else {
return " ";
}
},
},
};
</script>
<style lang="scss">
.table {
height: 100%;
.el-pagination {
float: right;
margin: 20px;
}
.el-table__header-wrapper,
.el-table__fixed-header-wrapper {
thead {
tr {
th {
color: #333333;
}
}
}
}
.el-table-column--selection .cell {
padding: 0;
text-align: center;
}
.el-table__fixed-right {
bottom: 0 !important;
right: 6px !important;
z-index: 1004;
}
.operate-group {
display: flex;
flex-wrap: wrap;
.item {
margin-top: 4px;
margin-bottom: 4px;
display: block;
flex: 0 0 50%;
}
}
.filter-data {
top: e("calc((100% - 100px) / 3)");
background-color: rgba(0, 0, 0, 0.7);
}
.table-action {
top: e("calc((100% - 100px) / 2)");
background-color: rgba(0, 0, 0, 0.7);
}
.fix-right {
position: absolute;
right: 0;
height: 100px;
color: #ffffff;
width: 30px;
display: block;
z-index: 1005;
writing-mode: vertical-rl;
text-align: center;
line-height: 28px;
border-bottom-left-radius: 6px;
border-top-left-radius: 6px;
cursor: pointer;
}
.custom-dropdown {
color: #666;
&:hover {
color: inherit;
}
}
.operate-group {
display: flex;
flex-wrap: wrap;
flex-direction: row;
align-items: center;
justify-content: center;
height: 100%;
.item {
margin-right: 2px;
display: block;
flex: 0 1 auto;
}
}
.el-button--text.custom-text {
span {
vertical-align: middle;
}
color: #666;
&:hover {
color: #66b1ff;
}
}
}
</style>
?parseTime函數(shù) ?
用于頁(yè)面使用格式化時(shí)間對(duì)象
// 日期格式化
export function parseTime(time, pattern) {
if (arguments.length === 0 || !time) {
return null
}
const format = pattern || '{y}-{m}-n5n3t3z {h}:{i}:{s}'
let date
if (typeof time === 'object') {
date = time
} else {
if ((typeof time === 'string') && (/^[0-9]+$/.test(time))) {
time = parseInt(time)
} else if (typeof time === 'string') {
time = time.replace(new RegExp(/-/gm), '/').replace('T', ' ').replace(new RegExp(/\.[\d]{3}/gm), '');
}
if ((typeof time === 'number') && (time.toString().length === 10)) {
time = time * 1000
}
date = new Date(time)
}
const formatObj = {
y: date.getFullYear(),
m: date.getMonth() + 1,
d: date.getDate(),
h: date.getHours(),
i: date.getMinutes(),
s: date.getSeconds(),
a: date.getDay()
}
const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => {
let value = formatObj[key]
// Note: getDay() returns 0 on Sunday
if (key === 'a') { return ['日', '一', '二', '三', '四', '五', '六'][value] }
if (result.length > 0 && value < 10) {
value = '0' + value
}
return value || 0
})
return time_str
}
debounce?函數(shù) ?
用在封裝監(jiān)聽(tīng)窗口變化的節(jié)流函數(shù)
export function debounce(func, wait, immediate) {
let timeout, args, context, timestamp, result
const later = function() {
// 據(jù)上一次觸發(fā)時(shí)間間隔
const last = +new Date() - timestamp
// 上次被包裝函數(shù)被調(diào)用時(shí)間間隔 last 小于設(shè)定時(shí)間間隔 wait
if (last < wait && last > 0) {
timeout = setTimeout(later, wait - last)
} else {
timeout = null
// 如果設(shè)定為immediate===true,因?yàn)殚_(kāi)始邊界已經(jīng)調(diào)用過(guò)了此處無(wú)需調(diào)用
if (!immediate) {
result = func.apply(context, args)
if (!timeout) context = args = null
}
}
}
return function(...args) {
context = this
timestamp = +new Date()
const callNow = immediate && !timeout
// 如果延時(shí)不存在,重新設(shè)定延時(shí)
if (!timeout) timeout = setTimeout(later, wait)
if (callNow) {
result = func.apply(context, args)
context = args = null
}
return result
}
}
render通用渲染模版
豐富表格的展現(xiàn)形式
// renderUtils.js
// 渲染標(biāo)簽
export function customRender({ h, params, fieldToCheck, textMapping }) {
const fieldValue = params.row[fieldToCheck];
const type = textMapping.hasOwnProperty(fieldValue)
? textMapping[fieldValue].type
: "default"; // 默認(rèn)類(lèi)型,可以根據(jù)需要修改
const labelText = textMapping.hasOwnProperty(fieldValue)
? textMapping[fieldValue].text
: '未知'; // 默認(rèn)顯示字段值,可以根據(jù)需要修改
return h("el-tag", { props: { type } }, labelText);
}
// 渲染按鈕
export function renderSwitch({ h, params, size, fieldToCheck }) {
return h("el-switch", {
props: {
size: size || "medium",
value: params.row[fieldToCheck],
},
on: {
change: (events) => {
this.$set(params.row, fieldToCheck, events);
this.changeMsgStatus(events, params);
},
},
});
}
// link
export function renderLink(h, params, title) {
return h(
"el-link",
{
props: {
type: "primary",
underline: false,
},
on: {
click: (e) => {
this.handleDetail(params);
},
},
},
title
);
}
// 頭像
export function avatarElement({ h, params, fieldToCheck, size }) {
return h(
"el-avatar",
{
props: {
size: size || 44,
src: params.row[fieldToCheck],
},
// 圖片加載失敗展示默認(rèn)圖片
on: {
error: (e) => {
return true;
},
},
},
[
h("img", {
attrs: {
src: require("@/assets/images/default_avatar.png"),
},
}),
]
);
}
頁(yè)面使用
<template>
<div class="table-page">
<!--region table 表格-->
<app-table :list="list" :total="total" :otherHeight="otherHeight" :options="options" :pagination="pagination"
:columns="columns" :operates="operates" @handleSizeChange="handleSizeChange" @handleIndexChange="handleIndexChange"
@handleSelectionChange="handleSelectionChange" @sortChange="sortChange">
</app-table>
<!--endregion-->
</div>
</template>
<script>
import { parseTime } from "@/utils/ruoyi";
import {
customRender,
renderSwitch,
renderLink,
avatarElement,
} from "@/utils/renderUtils.js";
export default {
data() {
return {
total: 0,
list: [
{
id: 1,
title: "標(biāo)題",
state: 2,
author: "張三",
phone: "12346788901",
email: "1234556778@qq.com",
createDate: "2023-04-23 16:11:38",
zero: null,
isOpend: false,
headimgurl: 'https://cube.elemecdn.com/e/fd/0fc7d20532fdaf769a25683617711png.png'
},
],
otherHeight: 208,
columns: [
{
prop: "id",
label: "編號(hào)",
align: "center",
el: {
// element ui的一些props...
sortable: true, //開(kāi)啟排序
},
},
{
prop: "title",
label: "標(biāo)題",
align: "center",
formatter: (row, column, cellValue) => {
return `<span style="white-space: nowrap;color: dodgerblue;">${row.title}</span>`;
},
},
{
prop: "state",
label: "狀態(tài)",
align: "center",
width: "160",
render: (h, params) => {
const fieldToCheck = "state";
const textMapping = {
0: { type: "success", text: "上架" },
1: { type: "info", text: "下架" },
2: { type: "danger", text: "審核中" },
};
let data = { h, params, fieldToCheck, textMapping }
return customRender.call(this, data);
},
},
{
prop: "switch",
label: "開(kāi)關(guān)",
align: "center",
width: "160",
render: (h, params) => {
let data = {
h, params,
fieldToCheck: 'isOpend',
size: 'medium'
}
return renderSwitch.call(this, data);
},
},
{
prop: "headimgurl",
label: "頭像",
align: "center",
render: (h, params) => {
let data = {
h, params,
fieldToCheck: 'headimgurl',
size: 44
}
return avatarElement.call(this, data);
},
},
{
prop: "author",
label: "作者",
align: "center",
width: 120,
},
{
prop: "phone",
label: "聯(lián)系方式",
align: "center",
width: 160,
show: false, //控制這一列是否展示
},
{
prop: "zero",
label: "郵箱",
align: "center",
width: 240,
},
{
prop: "link",
label: "查看",
align: "center",
width: "160",
render: (h, params) => {
return renderLink.call(this, h, params, '查看');
},
},
{
prop: "createDate",
label: "發(fā)布時(shí)間",
align: "center",
width: 180,
formatter: (row, column, cellValue) => {
return parseTime(row.createDate);
},
},
], // 需要展示的列
operates: {
width: 200,
fixed: "right",
list: [
{
label: "編輯",
type: "text",
show: (index, row) => {
return true;
},
icon: "el-icon-edit",
disabled: false,
method: (index, row) => {
this.handleEdit(index, row);
},
},
{
label: "刪除",
type: "text",
icon: "el-icon-delete",
show: true,
disabled: (index, row) => {
return false;
},
method: (index, row) => {
this.handleDel(index, row);
},
},
{
label: "測(cè)試下拉",
type: "text",
icon: "el-icon-delete",
haspermission: ["agent:del"], //顯示權(quán)限
show: true,
disabled: (index, row) => {
return false;
},
method: (index, row) => {
this.handleDel(index, row);
},
},
],
}, // 操作按鈕組
pagination: {
pageIndex: 1,
pageSize: 20,
}, // 分頁(yè)參數(shù)
options: {
stripe: true, // 是否為斑馬紋 table
loading: false, // 是否添加表格loading加載動(dòng)畫(huà)
highlightCurrentRow: true, // 是否支持當(dāng)前行高亮顯示
mutiSelect: true, // 是否支持列表項(xiàng)選中功能
border: true, //是否顯示邊框
numbers: true, //是否顯示序號(hào)
selectable() {
//禁用選中
return false;
},
headerCellStyle: "background-color:#fff", //表頭顏色
}, // table 的參數(shù)
};
},
mounted() { },
methods: {
// 切換每頁(yè)顯示的數(shù)量
handleSizeChange(pagination) {
console.log("pagination", pagination);
},
// 切換頁(yè)碼
handleIndexChange(pagination) {
console.log("pagination", pagination);
},
// 選中行
handleSelectionChange(val) {
console.log("val:", val);
},
// 編輯
handleEdit(index, row) {
console.log(" index:", index);
console.log(" row:", row);
},
// 刪除
handleDel(index, row) {
console.log(" index:", index);
console.log(" row:", row);
},
// 排序
sortChange(data) {
console.log(data);
},
// 開(kāi)關(guān)按鈕
changeMsgStatus(ev, params) {
console.log(ev, params);
},
handleDetail(params) {
console.log(params);
},
},
};
</script>
【擴(kuò)展】vue 函數(shù)式組件
函數(shù)式組件特點(diǎn):
- 沒(méi)有管理任何狀態(tài)
- 沒(méi)有監(jiān)聽(tīng)任何傳遞給它的狀態(tài)
- 沒(méi)有生命周期方法
- 它只是接收一些
prop
的函
我們將這樣的組件標(biāo)記為functional
:
- 無(wú)狀態(tài) == 無(wú)響應(yīng)式數(shù)據(jù)
- 無(wú)實(shí)例 == 無(wú)
this
上下文
函數(shù)式組件的優(yōu)點(diǎn):
- 渲染開(kāi)銷(xiāo)低,因?yàn)楹瘮?shù)式組件只是函數(shù);
{
functional: true,
// Props 是可選的
props: {
// ...
},
// 為了彌補(bǔ)缺少的實(shí)例
// 提供第二個(gè)參數(shù)作為上下文
render: function (createElement, context) {
// ...
}
}
props
: 提供所有prop
的對(duì)象children:VNode
?子節(jié)點(diǎn)的數(shù)組slots
: 一個(gè)函數(shù),返回了包含所有插槽的對(duì)象scoptedSlots
:(2.6.0) 一個(gè)暴露傳入的作用域插槽的對(duì)象,也以函數(shù)形式暴露普通插槽data
:傳遞個(gè)組件的整個(gè)數(shù)據(jù)對(duì)象,作為createElement
的第二個(gè)參數(shù)傳入組件parent
:對(duì)父組件的引用listeners
:(2.3.0+) 一個(gè)包含了:所有父組件為當(dāng)前組件祖冊(cè)的事件監(jiān)聽(tīng)器對(duì)象,是data.on
的一個(gè)別名injections
:(2.3.0+) 如果使用了inject
選項(xiàng),則改對(duì)象包含了:應(yīng)當(dāng)被注入的屬性;
【擴(kuò)展】vue中的render函數(shù)
一、初步認(rèn)識(shí)render函數(shù)
import Vue from 'vue'
import App from './App'
Vue.config.productionTip = false
new Vue({
el: '#app',
render: h => h(App)
})
在使用腳手架創(chuàng)建vue項(xiàng)目的過(guò)程,我們很容易看到render這個(gè)函數(shù),相對(duì)于其他標(biāo)簽,我們對(duì)于render還是比較陌生的,因此寫(xiě)下這篇文章你我共同理解。
二、為什么使用render函數(shù)
VUE推薦在絕大多數(shù)情況下使用template來(lái)創(chuàng)建我們的HTML。然而在一些場(chǎng)景中,我們真的需要JavaScript的完全編程的能力,這就是render函數(shù),它比template更接近編譯器。(這是官方的話(huà))
簡(jiǎn)單來(lái)說(shuō),我們?yōu)槭裁匆褂胷ender函數(shù)呢?? 便是因?yàn)槲覀冏罱?jīng)常使用的一個(gè)引入。
import Vue from "vue";
這一個(gè)引入你看似沒(méi)有任何問(wèn)題,但問(wèn)題恰恰就是出在這。在不同版本的vue中,有vue.js和vue.runtime.xxx.js這兩種js文件。其中
(1)vue.js是完整版的vue,包含核心功能+模板解析器。
(2)vue.runtime.xxx.js是運(yùn)行版vue,只包含核心功能,沒(méi)有模板解析器。
VUE開(kāi)發(fā)者為了讓我們打包的文件能盡可能小一點(diǎn),在上述引入的是運(yùn)行版vue。因?yàn)関ue.runtime.xxx.js沒(méi)有模板解析器,所以不能使用template配置項(xiàng),這時(shí)候就需要使用render函數(shù)去接收到的createElement函數(shù)去指定具體內(nèi)容,創(chuàng)建html模板。
三、render函數(shù)的解析
render 函數(shù)即渲染函數(shù),它是個(gè)函數(shù),它的參數(shù) createElement 也是個(gè)函數(shù)。
上邊的代碼中 render: h => h(App) ,這是 ES6的箭頭函數(shù)的寫(xiě)法,可以把 h 當(dāng)作 createElement 的別名。所以這段代碼其實(shí)相當(dāng)于
render: function (createElement) {
return createElement(App)
}
這個(gè)函數(shù)的作用就是生成一個(gè) VNode節(jié)點(diǎn),render 函數(shù)得到這個(gè) VNode 節(jié)點(diǎn)之后,返回給 Vue.js 的 mount 函數(shù),渲染成真實(shí) DOM 節(jié)點(diǎn),并掛載到根節(jié)點(diǎn)上。
createElement 函數(shù)的返回值是 VNode(即:虛擬節(jié)點(diǎn))
createElement 函數(shù)的3個(gè)參數(shù)
- 一個(gè) HTML 標(biāo)簽字符串,組件選項(xiàng)對(duì)象,或者解析上述任何一種的一個(gè) async 異步函數(shù)。類(lèi)型:String | Object | Function。必需。
- 一個(gè)包含模板相關(guān)屬性的數(shù)據(jù)對(duì)象,你可以在 template 中使用這些特性。類(lèi)型:Object??蛇x。
- 子虛擬節(jié)點(diǎn) (VNodes),由 createElement() 構(gòu)建而成,也可以使用字符串來(lái)生成“文本虛擬節(jié)點(diǎn)”。類(lèi)型:String | Array。可選
new Vue({
el: '#app',
render:function (createElement) {
//1.普通用法
// createElement(標(biāo)簽,{屬性},[內(nèi)容])
return createElement("h2",{class:"box"},['hello',createElement("button",["按鈕"])])
}
})
同時(shí)createElement也可以傳進(jìn)去一個(gè)組件,因此?
render: h => h(App)
等同于?
render:function (createElement) {
return createElement(App)
}
【擴(kuò)展】添加操作欄顯示權(quán)限
結(jié)構(gòu)改動(dòng)
通過(guò)函數(shù)式組件渲染 操作按鈕部分
<el-table-column
ref="fixedColumn"
label="操作"
align="center"
:width="operates.width"
:fixed="operates.fixed"
v-if="operates.list.length > 0"
>
<template slot-scope="scope">
<expand-dom
:row="scope.row"
:render="renderOperates"
:index="scope.$index"
></expand-dom>
</template>
</el-table-column>
邏輯新增
這段代碼是一個(gè)Vue.js組件方法,用于渲染操作按鈕。讓我逐步解釋代碼的主要部分:
1. `renderOperates` 方法:這是一個(gè)渲染操作按鈕的方法。它接受兩個(gè)參數(shù) `h` 和 `params`,其中 `h` 是Vue的createElement函數(shù),用于創(chuàng)建虛擬DOM,而 `params` 包含一些參數(shù)數(shù)據(jù)。
2. `const endArr = this.rebuildList(params);`:這一行調(diào)用了 `rebuildList` 方法,它會(huì)根據(jù)權(quán)限驗(yàn)證后的結(jié)果生成最終的按鈕數(shù)組,然后將這個(gè)數(shù)組存儲(chǔ)在 `endArr` 變量中。
3. `let outSideBtnArr = endArr.slice(0, 2);` 和 `let insideArr = endArr.slice(2);`:這兩行將 `endArr` 分為兩個(gè)部分,前兩個(gè)按鈕存儲(chǔ)在 `outSideBtnArr` 中,其余的按鈕存儲(chǔ)在 `insideArr` 中。
4. `const buttonArr = [];`:創(chuàng)建一個(gè)空數(shù)組 `buttonArr`,用于存儲(chǔ)最終要渲染的按鈕。
5. `outSideBtnArr.forEach((item) => { ... });`:這是一個(gè)循環(huán)遍歷 `outSideBtnArr` 的循環(huán),對(duì)每個(gè)按鈕調(diào)用 `renderOutsideButton` 方法進(jìn)行渲染,然后將渲染結(jié)果添加到 `buttonArr` 中。
6. `if (insideArr.length > 0) { ... }`:這是一個(gè)條件判斷,如果 `insideArr` 中有按鈕,則調(diào)用 `renderDropdownButton` 方法進(jìn)行渲染,然后將渲染結(jié)果添加到 `buttonArr` 中。
7. 最后,使用 `h("div", { attrs: { class: "operate-group" } }, buttonArr)` 創(chuàng)建一個(gè) `<div>` 元素,設(shè)置其類(lèi)名為 "operate-group",并將 `buttonArr` 中的按鈕渲染到這個(gè) `<div>` 元素中,最終返回這個(gè) `<div>` 元素的虛擬DOM。
接下來(lái),代碼中還包含了兩個(gè)方法 `renderOutsideButton` 和 `renderDropdownButton`,它們分別用于渲染外部按鈕和下拉按鈕。這兩個(gè)方法的主要作用是創(chuàng)建相應(yīng)的按鈕元素,并設(shè)置按鈕的屬性、樣式和點(diǎn)擊事件處理函數(shù)。
最后,代碼中還包括一個(gè) `rebuildList` 方法,用于根據(jù)權(quán)限驗(yàn)證結(jié)果來(lái)生成最終的按鈕數(shù)組。它會(huì)遍歷操作按鈕列表,根據(jù)按鈕的顯示權(quán)限和角色身份權(quán)限來(lái)判斷是否允許顯示該按鈕,然后返回允許顯示的按鈕數(shù)組。
總的來(lái)說(shuō),這段代碼用于動(dòng)態(tài)生成操作按鈕組件,根據(jù)權(quán)限和角色身份權(quán)限來(lái)控制按鈕的顯示和行為。
// 用于渲染操作按鈕的方法
renderOperates(h, params) {
const endArr = this.rebuildList(params); //權(quán)限驗(yàn)證后最終按鈕數(shù)組
let outSideBtnArr = endArr.slice(0, 2); //外部按鈕 默認(rèn)前兩個(gè)
let insideArr = endArr.slice(2); //下拉菜單按鈕數(shù)組
const buttonArr = []; //最終渲染數(shù)組
outSideBtnArr.forEach((item) => {
buttonArr.push(this.renderOutsideButton(h, item, params));
});
if (insideArr.length > 0) {
buttonArr.push(this.renderDropdownButton(h, insideArr, params));
}
return h("div", { attrs: { class: "operate-group" } }, buttonArr);
},
// 渲染外部按鈕
renderOutsideButton(h, item, params) {
return h(
"el-button",
{
// 組件的屬性(數(shù)據(jù))
props: {
type: item.type || "text", //類(lèi)型(primary / success / warning / danger / info / text)
icon: item.icon || "", //icon:按鈕圖標(biāo)
plain: item.plain || false, //plain:是否樸素按鈕
size: item.size || "mini", //大小
},
// 組件的屬性(html屬性)
attrs: {
title: item.label, //label: 文本
},
// 樣式
style: {
color: item.label == "刪除" ? "#ff4057" : "",
},
// 按鈕的點(diǎn)擊事件處理函數(shù)
on: {
click: () => {
item.method(params.index, params.row);
},
},
},
item.label //label: 文本
);
},
// 渲染下拉按鈕
renderDropdownButton(h, insideArr, params) {
return h(
// 創(chuàng)建了一個(gè) "el-dropdown" 組件
"el-dropdown",
// dropdown 樣式
{
class: ["custom-dropdown"],
// 組件的屬性(數(shù)據(jù)):
props: {
trigger: "click", //下拉框點(diǎn)擊觸發(fā)
},
},
[
// "el-dropdown" 組件中的button
h(
"el-button",
{
// 按鈕樣式
style: {
fontSize: "18px",
},
// 按鈕類(lèi)名
class: ["custom-text"],
props: {
type: "text",
plain: false,
size: "mini",
},
},
// 在按鈕的內(nèi)容中,使用了一個(gè) "i" 標(biāo)簽,其 class 屬性為 "el-icon-more",顯示一個(gè)圖標(biāo)。
[h("i", { class: "el-icon-more" })]
),
// 創(chuàng)建了一個(gè) "el-dropdown-menu" 組件
h(
"el-dropdown-menu",
// 傳入slot
{
slot: "dropdown",
},
// 對(duì)傳入的按鈕循環(huán)渲染出el-dropdown-item"
insideArr.map((item) => {
return h(
"el-dropdown-item",
{
nativeOn: {
click: () => {
item.method(params.index, params.row);
},
},
},
item.label
);
})
),
]
);
},
// 校驗(yàn)權(quán)限
rebuildList({ row }) {
// 驗(yàn)證是否顯示權(quán)限和角色身份權(quán)限
return this.operates.list.filter((item) => {
if (typeof item.show === "function") {
return item.show(row);
} else if (typeof item.show === "boolean" && item.show) {
return true;
}
// 如果沒(méi)有顯示權(quán)限要求,則繼續(xù)驗(yàn)證角色身份權(quán)限
if (item.haspermission) {
return checkPermi(item.haspermission);
}
// 如果沒(méi)有角色身份權(quán)限要求,則默認(rèn)允許顯示
return true;
});
},
組件引入使用?
只需添加按鈕對(duì)應(yīng)的權(quán)限標(biāo)識(shí)符字段即可
operates: {
fixed: "right",
list: [
{
label: "編輯",
type: "text",
haspermission: ["agent:del"],
icon: "el-icon-edit",
plain: false,
disabled: false,
method: (index, row) => {
this.handleEdit(index, row);
},
},
{
label: "刪除",
show: true,
plain: false,
disabled: (index, row) => {
return false;
},
method: (index, row) => {
this.handleDel(index, row);
},
},
],
}, // 操作按鈕組
本文用的是若依框架 上文中添加權(quán)限部分?checkPermi 函數(shù) 為若伊提供的校驗(yàn)按鈕權(quán)限方法(組件引入haspermission字段同理 可以根據(jù)業(yè)務(wù)場(chǎng)景自行修改,傳入haspermission權(quán)限標(biāo)識(shí)符即可)
【擴(kuò)展?】?props與attrs
Vue.js是一個(gè)流行的前端JavaScript框架,它提供了一種便捷的方式來(lái)構(gòu)建交互性的用戶(hù)界面。在Vue.js中,attrs
和props
是兩個(gè)重要的概念,它們用于組件之間的數(shù)據(jù)傳遞和交互。讓我們來(lái)探討一下它們的區(qū)別和應(yīng)用場(chǎng)景。
1、props 要先聲明才能取值,attrs 不用先聲明
2、props 聲明過(guò)的屬性,attrs 里不會(huì)再出現(xiàn)
3、props 不包含事件,attrs 包含
4、props 支持 string 以外的類(lèi)型,attrs 只有 string 類(lèi)型
props(Properties):
-
用途:
props
是用來(lái)從父組件向子組件傳遞數(shù)據(jù)的一種方式。父組件可以將數(shù)據(jù)作為屬性傳遞給子組件,子組件可以在其模板中使用這些屬性。 -
數(shù)據(jù)流向:
數(shù)據(jù)流是單向的,從父組件到子組件。父組件通過(guò)屬性設(shè)置子組件的數(shù)據(jù)。 -
定義方式:
在子組件中,需要明確聲明接收哪些props
,通常在組件的選項(xiàng)中使用props
屬性進(jìn)行定義。
// 父組件
<template>
<child-component :message="parentMessage"></child-component>
</template>
<script>
export default {
data() {
return {
parentMessage: "Hello from parent!",
};
},
};
</script>
// 子組件
<template>
<div>{{ message }}</div>
</template>
<script>
export default {
props: ['message'],
};
</script>
attrs(Attributes):
-
用途:
attrs
用于從父組件向子組件傳遞HTML屬性,而不是組件的數(shù)據(jù)。這對(duì)于將原生HTML屬性傳遞給子組件非常有用。 -
數(shù)據(jù)流向:
同樣是單向的,從父組件到子組件。但是,attrs
傳遞的是HTML屬性,而不是組件的屬性。 -
定義方式:
attrs
不需要在子組件中明確聲明,而是在子組件的模板中直接使用。
// 父組件
<template>
<child-component title="Child Component"></child-component>
</template>
// 子組件
<template>
<div :title="title">This is a child component</div>
</template>
attrs的屬性?
HTML 中的屬性可以分為多個(gè)類(lèi)別,以下是一些常見(jiàn)的 HTML 屬性及其分類(lèi):
-
全局屬性 (Global Attributes):?這些屬性可以在 HTML 元素中的任何地方使用,它們不受特定元素的限制。例如:
-
id
: 元素的唯一標(biāo)識(shí)符 -
class
: 元素的類(lèi)名 -
style
: 元素的 CSS 樣式 -
data-*
: 自定義數(shù)據(jù)屬性 -
title
: 元素的標(biāo)題(通常顯示為工具提示)
-
-
鏈接屬性 (Link Attributes):?用于鏈接元素的屬性,如?
<a>
?和?<link>
:-
href
: 指定鏈接的目標(biāo) URL -
target
: 定義鏈接如何在瀏覽器中打開(kāi) -
rel
: 定義當(dāng)前文檔與鏈接資源之間的關(guān)系
-
-
表單屬性 (Form Attributes):?用于表單元素(如?
<input>
,?<form>
)的屬性:-
name
: 表單元素的名稱(chēng) -
type
: 輸入字段的類(lèi)型(文本、密碼、單選框等) -
value
: 輸入字段的默認(rèn)值 -
action
: 表單提交的目標(biāo) URL -
method
: 表單提交的 HTTP 方法(GET 或 POST) -
required
: 指示字段是否必填
-
-
圖像屬性 (Image Attributes):?用于?
<img>
?元素的屬性:-
src
: 圖像的源 URL -
alt
: 圖像的替代文本 -
width
?和?height
: 圖像的寬度和高度
-
-
多媒體屬性 (Media Attributes):?用于多媒體元素,如?
<audio>
?和?<video>
:-
controls
: 顯示多媒體控件(播放、暫停、音量等) -
autoplay
: 指示多媒體是否自動(dòng)播放 -
loop
: 指示多媒體是否循環(huán)播放 -
poster
: 預(yù)覽圖像的 URL
-
-
元信息屬性 (Meta Information Attributes):?用于?
<meta>
?元素,通常用于定義文檔的元信息:-
charset
: 文檔字符編碼 -
name
?和?content
: 定義各種元信息,如頁(yè)面描述、關(guān)鍵詞、作者等
-
-
腳本屬性 (Script Attributes):?用于?
<script>
?元素,用于加載和執(zhí)行腳本:-
src
: 腳本文件的源 URL -
type
: 指定腳本的 MIME 類(lèi)型 -
async
?和?defer
: 控制腳本的加載和執(zhí)行方式
-
-
框架屬性 (Frame Attributes):?用于?
<iframe>
?元素,用于嵌套其他文檔:-
src
: 嵌套文檔的源 URL -
width
?和?height
: 框架的寬度和高度
-
這只是一些常見(jiàn)的 HTML 屬性分類(lèi),還有其他特定元素的屬性。不同的 HTML 元素可能具有不同的屬性,具體取決于元素的類(lèi)型和用途。
應(yīng)用場(chǎng)景:
-
使用
props
:- 當(dāng)你需要在父組件和子組件之間傳遞數(shù)據(jù),以便子組件可以根據(jù)這些數(shù)據(jù)渲染不同的內(nèi)容或執(zhí)行不同的操作時(shí),使用
props
是最常見(jiàn)的做法。
- 當(dāng)你需要在父組件和子組件之間傳遞數(shù)據(jù),以便子組件可以根據(jù)這些數(shù)據(jù)渲染不同的內(nèi)容或執(zhí)行不同的操作時(shí),使用
-
使用
attrs
:文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-706935.html- 當(dāng)你需要將原生HTML屬性傳遞給子組件,例如
title
、class
、style
等,而不需要在子組件中顯式聲明它們時(shí),可以使用attrs
。 - 也可以用于將自定義屬性傳遞給子組件,這些屬性在子組件內(nèi)部沒(méi)有定義
props
,但仍然需要在子組件的模板中使用。
- 當(dāng)你需要將原生HTML屬性傳遞給子組件,例如
總之,props
用于傳遞組件之間的數(shù)據(jù),而attrs
用于傳遞HTML屬性。根據(jù)你的需求,選擇適當(dāng)?shù)姆绞絹?lái)實(shí)現(xiàn)組件之間的通信。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-706935.html
到了這里,關(guān)于element ui 表格組件與分頁(yè)組件的二次封裝 【擴(kuò)展】vue中的render函數(shù)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!