目录
使用组件需先下载 element ui vue2
引入全局注册自定义table组件

这段代码是一个封装的分页表格组件。它使用了Vue.js框架,并结合了Element UI中的el-table和el-pagination组件来实现。该组件接受一系列属性作为数据源,包括列表数据(list)、表格列(columns)、操作按钮列(operates)、总数(total)、分页参数(pagination)等。
在模板中,使用el-table组件来展示表格数据,使用el-pagination组件来实现分页功能。el-table-column用来定义每列的样式和展示内容。组件中还包括其他的一些方法和事件处理函数,如handleSizeChange用于切换每页显示的数量,handleIndexChange用于切换页码,handleSelectionChange处理多行选中等。
此外,还定义了一些样式规则来美化表格的显示效果,如设置表头颜色、调整列的样式、设置操作按钮组的布局、设置筛选弹窗和表格操作弹窗的样式等。
最后,使用了scss来编写样式表,通过设置类名来控制样式的展示。
在标签中,定义了一个表格组件,包括表格本身,列定义,按钮操作组,和分页控件。
在标签中,导入了必要的依赖,并定义了组件的属性(props)、数据(data)、生命周期钩子(activated、beforeDestroy、deactivated、created、mounted)和方法(methods)。
props属性用于接受从父组件传递进来的数据,如数据列表 list、列定义 columns、按钮操作 operates、总数 total、分页参数 pagination 等。
data数据属性包括一些内部状态,如表格高度 height、监听窗口调整大小的回调函数 $_resizeHandler、当前页码 pageIndex、表格分页信息 tableCurrentPagination、多行选中数据 multipleSelection 等。
在生命周期钩子中,组件监听窗口大小调整事件,自动调整表格高度,并在适当的生命周期中初始化和销毁事件监听器。
methods包括一系列方法,如计算表头宽度的方法 headSpanFit、初始化事件监听器的方法 initListener、销毁事件监听器的方法 destroyListener、调整表格高度的方法 resize、处理每页显示数量变化的方法 handleSizeChange、处理页码变化的方法 handleIndexChange、处理多行选中的方法 handleSelectionChange 等。
最后,在标签中定义了一些样式,包括表格样式、分页样式、按钮样式、筛选和操作弹窗样式等。
- <!--region 封装的分页 table-->
- <!--region 封装的分页 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 序号-->
- <el-table-column
- v-if="options.numbers"
- width="60"
- type="index"
- label="序号"
- align="center"
- ></el-table-column>
- <!--endregion-->
- <!--region 数据列-->
- <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 分页-->
- <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]; // 每页展示条数的控制集合
- export default {
- props: {
- list: {
- type: Array,
- default: [], // prop:表头绑定的地段,label:表头名称,align:每列数据展示形式(left, center, right),width:列宽
- }, // 数据列表
- columns: {
- type: Array,
- default: [], // 需要展示的列 === prop:列数据对应的属性,label:列名,align:对齐方式,width:列宽
- },
- operates: {
- type: Object,
- defaultt: () => {}, // width:按钮列宽,fixed:是否固定(left,right),按钮集合 === label: 文本,type :类型(primary / success / warning / danger / info / text),show:是否显示,icon:按钮图标,plain:是否朴素按钮,disabled:是否禁用,method:回调方法
- },
- total: {
- type: Number,
- default: 0,
- }, // 总数
- pagination: {
- type: Object,
- default: null, // 分页参数 === pageSize:每页展示的条数,pageIndex:当前页,pageArray: 每页展示条数的控制集合,默认 _page_array
- },
- noStatic: false, // 是否计算表格高度
- customHeight: {
- //与noStatic一起使用
- type: Number,
- default: 320,
- },
- otherHeight: {
- type: Number,
- default: 180,
- }, // 计算表格的高度
- options: {
- type: Object,
- default: {
- stripe: false, // 是否为斑马纹 table
- loading: false, // 是否添加表格loading加载动画
- highlightCurrentRow: false, // 是否支持当前行高亮显示
- mutiSelect: false, // 是否支持列表项选中功能
- border: false, //是否显示边框
- selectable: () => {
- //是否可以选中
- return false;
- },
- },
- }, // table 表格的控制参数
- },
- 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> 包装时,组件会被缓存并在重新激活时调用这个钩子函数。
- if (!this.$_resizeHandler) {
- // avoid duplication init
- this.initListener();
- }
-
- // when keep-alive chart activated, auto resize
- this.resize();
- },
- //当组件即将被销毁(beforeDestroy)时,会调用这个钩子函数。
- beforeDestroy() {
- this.destroyListener();
- },
- //当组件被停用(deactivated),通常也是在 <keep-alive> 包装下,组件会调用这个钩子函数。
- deactivated() {
- this.destroyListener();
- },
- created() {},
- mounted() {
- this.initListener();
- },
- methods: {
- //计算小列宽
- headSpanFit(column) {
- let labelLong = column.label.length; // 表头label长度
- let size = 20; // 根据需要定义标尺,直接使用字体大小确定就行,也可以根据需要定义
- let minWidth = labelLong * size < 100 ? 100 : labelLong * size; // 根据label长度计算该表头最终宽度
- return minWidth;
- },
- // 初始化监听器的方法。
- initListener() {
- this.$_resizeHandler = debounce(() => {
- this.resize();
- }, 200);
- window.addEventListener("resize", this.$_resizeHandler);
- this.$nextTick(() => {
- this.resize();
- });
- },
- //这是一个销毁监听器的方法。
- destroyListener() {
- window.removeEventListener("resize", this.$_resizeHandler);
- this.$_resizeHandler = null;
- },
- //计算表格的高度
- resize() {
- // 不用计算
- 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);
- //保表格的布局在高度调整后得以更新。
- mutipleTable && mutipleTable.doLayout();
- },
- // 切换每页显示的数量
- handleSizeChange(size) {
- if (this.pagination) {
- this.$emit("handleSizeChange", { ...this.pagination });
- }
- },
- // 切换页码
- 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); //权限验证后最终按钮数组
- let outSideBtnArr = endArr.slice(0, 2); //外部按钮 默认前两个
- let insideArr = endArr.slice(2); //下拉菜单按钮数组
- const buttonArr = []; //最终渲染数组
-
- 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",
- {
- // 组件的属性(数据)
- props: {
- type: item.type || "text", //类型(primary / success / warning / danger / info / text)
- icon: item.icon || "", //icon:按钮图标
- plain: item.plain || false, //plain:是否朴素按钮
- size: item.size || "mini", //大小
- },
- // 组件的属性(html属性)
- attrs: {
- title: item.label, //label: 文本
- },
- // 样式
- style: {
- color: item.color,
- },
- // 按钮的点击事件处理函数
- on: {
- click: () => {
- item.method(params.index, params.row);
- },
- },
- },
- item.label //label: 文本
- );
- },
- // 渲染下拉按钮
- renderDropdownButton(h, insideArr, params) {
- return h(
- // 创建了一个 "el-dropdown" 组件
- "el-dropdown",
- // dropdown 样式
- {
- class: ["custom-dropdown"],
- // 组件的属性(数据):
- props: {
- trigger: "click", //下拉框点击触发
- },
- },
- [
- // "el-dropdown" 组件中的button
- h(
- "el-button",
- {
- // 按钮样式
- style: {
- fontSize: "18px",
- },
- // 按钮类名
- class: ["custom-text"],
- props: {
- type: "text",
- plain: false,
- size: "mini",
- },
- },
- // 在按钮的内容中,使用了一个 "i" 标签,其 class 属性为 "el-icon-more",显示一个图标。
- [h("i", { class: "el-icon-more" })]
- ),
- // 创建了一个 "el-dropdown-menu" 组件
- h(
- "el-dropdown-menu",
- // 传入slot
- {
- slot: "dropdown",
- },
- // 对传入的按钮循环渲染出el-dropdown-item"
- insideArr.map((item) => {
- return h(
- "el-dropdown-item",
- {
- nativeOn: {
- click: () => {
- item.method(params.index, params.row);
- },
- },
- },
- item.label
- );
- })
- ),
- ]
- );
- },
- // 校验权限
- rebuildList({ row }) {
- // 检查 operates 是否为空对象
- if (!this.operates || !this.operates.list || !Array.isArray(this.operates.list)) {
- return [];
- }
- // 过滤操作项
- return this.operates.list.filter((item) => {
- // 如果定义了 show 函数,则根据该函数的返回值来决定是否显示
- if (typeof item.show === "function") {
- return item.show(row);
- }
- // 如果 show 是布尔值且为 true,则显示
- if (typeof item.show === "boolean" && item.show) {
- return true;
- }
- // 如果定义了权限要求,则检查权限
- if (item.haspermission) {
- return checkPermi(item.haspermission);
- }
- // 默认允许显示
- return true;
- });
- },
- // 判断列的数据是否显示
- 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 " ";
- }
- },
- },
- };
用于页面使用格式化时间对象
- // 日期格式化
- export function parseTime(time, pattern) {
- if (arguments.length === 0 || !time) {
- return null
- }
- const format = pattern || '{y}-{m}-{d} {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
- }
用在封装监听窗口变化的节流函数
- export function debounce(func, wait, immediate) {
- let timeout, args, context, timestamp, result
-
- const later = function() {
- // 据上一次触发时间间隔
- const last = +new Date() - timestamp
-
- // 上次被包装函数被调用时间间隔 last 小于设定时间间隔 wait
- if (last < wait && last > 0) {
- timeout = setTimeout(later, wait - last)
- } else {
- timeout = null
- // 如果设定为immediate===true,因为开始边界已经调用过了此处无需调用
- if (!immediate) {
- result = func.apply(context, args)
- if (!timeout) context = args = null
- }
- }
- }
-
- return function(...args) {
- context = this
- timestamp = +new Date()
- const callNow = immediate && !timeout
- // 如果延时不存在,重新设定延时
- if (!timeout) timeout = setTimeout(later, wait)
- if (callNow) {
- result = func.apply(context, args)
- context = args = null
- }
-
- return result
- }
- }
丰富表格的展现形式
- // renderUtils.js
- // 渲染标签
- export function customRender({ h, params, fieldToCheck, textMapping }) {
- const fieldValue = params.row[fieldToCheck];
- const type = textMapping.hasOwnProperty(fieldValue)
- ? textMapping[fieldValue].type
- : "default"; // 默认类型,可以根据需要修改
-
- const labelText = textMapping.hasOwnProperty(fieldValue)
- ? textMapping[fieldValue].text
- : '未知'; // 默认显示字段值,可以根据需要修改
-
- 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],
- },
- // 图片加载失败展示默认图片
- on: {
- error: (e) => {
- return true;
- },
- },
- },
- [
- h("img", {
- attrs: {
- src: require("@/assets/images/default_avatar.png"),
- },
- }),
- ]
- );
- }
- <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: "标题",
- 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: "编号",
- align: "center",
- el: {
- // element ui的一些props...
- sortable: true, //开启排序
- },
- },
- {
- prop: "title",
- label: "标题",
- align: "center",
- formatter: (row, column, cellValue) => {
- return `<span style="white-space: nowrap;color: dodgerblue;">${row.title}</span>`;
- },
- },
- {
- prop: "state",
- label: "状态",
- 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: "开关",
- 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: "联系方式",
- 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: "发布时间",
- 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: "测试下拉",
- type: "text",
- icon: "el-icon-delete",
- haspermission: ["agent:del"], //显示权限
- show: true,
- disabled: (index, row) => {
- return false;
- },
- method: (index, row) => {
- this.handleDel(index, row);
- },
- },
- ],
- }, // 操作按钮组
- pagination: {
- pageIndex: 1,
- pageSize: 20,
- }, // 分页参数
- options: {
- stripe: true, // 是否为斑马纹 table
- loading: false, // 是否添加表格loading加载动画
- highlightCurrentRow: true, // 是否支持当前行高亮显示
- mutiSelect: true, // 是否支持列表项选中功能
- border: true, //是否显示边框
- numbers: true, //是否显示序号
- selectable() {
- //禁用选中
- return false;
- },
- headerCellStyle: "background-color:#fff", //表头颜色
- }, // table 的参数
- };
- },
-
- mounted() { },
- methods: {
- // 切换每页显示的数量
- handleSizeChange(pagination) {
- console.log("pagination", pagination);
- },
- // 切换页码
- 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);
- },
- // 开关按钮
- changeMsgStatus(ev, params) {
- console.log(ev, params);
- },
- handleDetail(params) {
- console.log(params);
- },
- },
- };
- </script>
prop的函我们将这样的组件标记为functional:
this上下文- {
- functional: true,
- // Props 是可选的
- props: {
- // ...
- },
- // 为了弥补缺少的实例
- // 提供第二个参数作为上下文
- render: function (createElement, context) {
- // ...
- }
- }
props: 提供所有prop的对象children:VNode 子节点的数组slots: 一个函数,返回了包含所有插槽的对象scoptedSlots:(2.6.0) 一个暴露传入的作用域插槽的对象,也以函数形式暴露普通插槽data:传递个组件的整个数据对象,作为createElement的第二个参数传入组件parent:对父组件的引用listeners:(2.3.0+) 一个包含了:所有父组件为当前组件祖册的事件监听器对象,是data.on的一个别名injections:(2.3.0+) 如果使用了inject选项,则改对象包含了:应当被注入的属性;
- import Vue from 'vue'
- import App from './App'
-
- Vue.config.productionTip = false
-
- new Vue({
- el: '#app',
- render: h => h(App)
- })
在使用脚手架创建vue项目的过程,我们很容易看到render这个函数,相对于其他标签,我们对于render还是比较陌生的,因此写下这篇文章你我共同理解。
VUE推荐在绝大多数情况下使用template来创建我们的HTML。然而在一些场景中,我们真的需要JavaScript的完全编程的能力,这就是render函数,它比template更接近编译器。(这是官方的话)
简单来说,我们为什么要使用render函数呢?? 便是因为我们最经常使用的一个引入。
import Vue from "vue";
这一个引入你看似没有任何问题,但问题恰恰就是出在这。在不同版本的vue中,有vue.js和vue.runtime.xxx.js这两种js文件。其中
(1)vue.js是完整版的vue,包含核心功能+模板解析器。
(2)vue.runtime.xxx.js是运行版vue,只包含核心功能,没有模板解析器。

VUE开发者为了让我们打包的文件能尽可能小一点,在上述引入的是运行版vue。因为vue.runtime.xxx.js没有模板解析器,所以不能使用template配置项,这时候就需要使用render函数去接收到的createElement函数去指定具体内容,创建html模板。
render 函数即渲染函数,它是个函数,它的参数 createElement 也是个函数。
上边的代码中 render: h => h(App) ,这是 ES6的箭头函数的写法,可以把 h 当作 createElement 的别名。所以这段代码其实相当于
- render: function (createElement) {
- return createElement(App)
- }
这个函数的作用就是生成一个 VNode节点,render 函数得到这个 VNode 节点之后,返回给 Vue.js 的 mount 函数,渲染成真实 DOM 节点,并挂载到根节点上。
createElement 函数的返回值是 VNode(即:虚拟节点)
createElement 函数的3个参数
- new Vue({
- el: '#app',
- render:function (createElement) {
- //1.普通用法
- // createElement(标签,{属性},[内容])
- return createElement("h2",{class:"box"},['hello',createElement("button",["按钮"])])
- }
- })

同时createElement也可以传进去一个组件,因此
render: h => h(App)
等同于
- render:function (createElement) {
- return createElement(App)
- }
通过函数式组件渲染 操作按钮部分
- <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>
这段代码是一个Vue.js组件方法,用于渲染操作按钮。让我逐步解释代码的主要部分:
1. `renderOperates` 方法:这是一个渲染操作按钮的方法。它接受两个参数 `h` 和 `params`,其中 `h` 是Vue的createElement函数,用于创建虚拟DOM,而 `params` 包含一些参数数据。
2. `const endArr = this.rebuildList(params);`:这一行调用了 `rebuildList` 方法,它会根据权限验证后的结果生成最终的按钮数组,然后将这个数组存储在 `endArr` 变量中。
3. `let outSideBtnArr = endArr.slice(0, 2);` 和 `let insideArr = endArr.slice(2);`:这两行将 `endArr` 分为两个部分,前两个按钮存储在 `outSideBtnArr` 中,其余的按钮存储在 `insideArr` 中。
4. `const buttonArr = [];`:创建一个空数组 `buttonArr`,用于存储最终要渲染的按钮。
5. `outSideBtnArr.forEach((item) => { ... });`:这是一个循环遍历 `outSideBtnArr` 的循环,对每个按钮调用 `renderOutsideButton` 方法进行渲染,然后将渲染结果添加到 `buttonArr` 中。
6. `if (insideArr.length > 0) { ... }`:这是一个条件判断,如果 `insideArr` 中有按钮,则调用 `renderDropdownButton` 方法进行渲染,然后将渲染结果添加到 `buttonArr` 中。
7. 最后,使用 `h("div", { attrs: { class: "operate-group" } }, buttonArr)` 创建一个 `
接下来,代码中还包含了两个方法 `renderOutsideButton` 和 `renderDropdownButton`,它们分别用于渲染外部按钮和下拉按钮。这两个方法的主要作用是创建相应的按钮元素,并设置按钮的属性、样式和点击事件处理函数。
最后,代码中还包括一个 `rebuildList` 方法,用于根据权限验证结果来生成最终的按钮数组。它会遍历操作按钮列表,根据按钮的显示权限和角色身份权限来判断是否允许显示该按钮,然后返回允许显示的按钮数组。
总的来说,这段代码用于动态生成操作按钮组件,根据权限和角色身份权限来控制按钮的显示和行为。
- // 用于渲染操作按钮的方法
- renderOperates(h, params) {
- const endArr = this.rebuildList(params); //权限验证后最终按钮数组
- let outSideBtnArr = endArr.slice(0, 2); //外部按钮 默认前两个
- let insideArr = endArr.slice(2); //下拉菜单按钮数组
- const buttonArr = []; //最终渲染数组
-
- 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",
- {
- // 组件的属性(数据)
- props: {
- type: item.type || "text", //类型(primary / success / warning / danger / info / text)
- icon: item.icon || "", //icon:按钮图标
- plain: item.plain || false, //plain:是否朴素按钮
- size: item.size || "mini", //大小
- },
- // 组件的属性(html属性)
- attrs: {
- title: item.label, //label: 文本
- },
- // 样式
- style: {
- color: item.label == "删除" ? "#ff4057" : "",
- },
- // 按钮的点击事件处理函数
- on: {
- click: () => {
- item.method(params.index, params.row);
- },
- },
- },
- item.label //label: 文本
- );
- },
- // 渲染下拉按钮
- renderDropdownButton(h, insideArr, params) {
- return h(
- // 创建了一个 "el-dropdown" 组件
- "el-dropdown",
- // dropdown 样式
- {
- class: ["custom-dropdown"],
- // 组件的属性(数据):
- props: {
- trigger: "click", //下拉框点击触发
- },
- },
- [
- // "el-dropdown" 组件中的button
- h(
- "el-button",
- {
- // 按钮样式
- style: {
- fontSize: "18px",
- },
- // 按钮类名
- class: ["custom-text"],
- props: {
- type: "text",
- plain: false,
- size: "mini",
- },
- },
- // 在按钮的内容中,使用了一个 "i" 标签,其 class 属性为 "el-icon-more",显示一个图标。
- [h("i", { class: "el-icon-more" })]
- ),
- // 创建了一个 "el-dropdown-menu" 组件
- h(
- "el-dropdown-menu",
- // 传入slot
- {
- slot: "dropdown",
- },
- // 对传入的按钮循环渲染出el-dropdown-item"
- insideArr.map((item) => {
- return h(
- "el-dropdown-item",
- {
- nativeOn: {
- click: () => {
- item.method(params.index, params.row);
- },
- },
- },
- item.label
- );
- })
- ),
- ]
- );
- },
- // 校验权限
- rebuildList({ row }) {
- // 验证是否显示权限和角色身份权限
- 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;
- }
- // 如果没有显示权限要求,则继续验证角色身份权限
- if (item.haspermission) {
- return checkPermi(item.haspermission);
- }
- // 如果没有角色身份权限要求,则默认允许显示
- return true;
- });
- },
只需添加按钮对应的权限标识符字段即可
- 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);
- },
- },
- ],
- }, // 操作按钮组
本文用的是若依框架 上文中添加权限部分 checkPermi 函数 为若伊提供的校验按钮权限方法(组件引入haspermission字段同理 可以根据业务场景自行修改,传入haspermission权限标识符即可)
Vue.js是一个流行的前端JavaScript框架,它提供了一种便捷的方式来构建交互性的用户界面。在Vue.js中,attrs和props是两个重要的概念,它们用于组件之间的数据传递和交互。让我们来探讨一下它们的区别和应用场景。
1、props 要先声明才能取值,attrs 不用先声明
2、props 声明过的属性,attrs 里不会再出现
3、props 不包含事件,attrs 包含
4、props 支持 string 以外的类型,attrs 只有 string 类型
props(Properties):用途:
props是用来从父组件向子组件传递数据的一种方式。父组件可以将数据作为属性传递给子组件,子组件可以在其模板中使用这些属性。数据流向:
数据流是单向的,从父组件到子组件。父组件通过属性设置子组件的数据。定义方式:
在子组件中,需要明确声明接收哪些props,通常在组件的选项中使用props属性进行定义。- // 父组件
- <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属性,而不是组件的数据。这对于将原生HTML属性传递给子组件非常有用。数据流向:
同样是单向的,从父组件到子组件。但是,attrs传递的是HTML属性,而不是组件的属性。定义方式:
attrs不需要在子组件中明确声明,而是在子组件的模板中直接使用。- // 父组件
- <template>
- <child-component title="Child Component"></child-component>
- </template>
-
- // 子组件
- <template>
- <div :title="title">This is a child component</div>
- </template>
HTML 中的属性可以分为多个类别,以下是一些常见的 HTML 属性及其分类:
全局属性 (Global Attributes): 这些属性可以在 HTML 元素中的任何地方使用,它们不受特定元素的限制。例如:
id: 元素的唯一标识符class: 元素的类名style: 元素的 CSS 样式data-*: 自定义数据属性title: 元素的标题(通常显示为工具提示)链接属性 (Link Attributes): 用于链接元素的属性,如 和 :
href: 指定链接的目标 URLtarget: 定义链接如何在浏览器中打开rel: 定义当前文档与链接资源之间的关系表单属性 (Form Attributes): 用于表单元素(如 , )的属性:
name: 表单元素的名称type: 输入字段的类型(文本、密码、单选框等)value: 输入字段的默认值action: 表单提交的目标 URLmethod: 表单提交的 HTTP 方法(GET 或 POST)required: 指示字段是否必填图像属性 (Image Attributes): 用于 元素的属性:
src: 图像的源 URLalt: 图像的替代文本width 和 height: 图像的宽度和高度多媒体属性 (Media Attributes): 用于多媒体元素,如 和 :
controls: 显示多媒体控件(播放、暂停、音量等)autoplay: 指示多媒体是否自动播放loop: 指示多媒体是否循环播放poster: 预览图像的 URL元信息属性 (Meta Information Attributes): 用于 元素,通常用于定义文档的元信息:
charset: 文档字符编码name 和 content: 定义各种元信息,如页面描述、关键词、作者等脚本属性 (Script Attributes): 用于 元素,用于加载和执行脚本:
src: 脚本文件的源 URLtype: 指定脚本的 MIME 类型async 和 defer: 控制脚本的加载和执行方式框架属性 (Frame Attributes): 用于 元素,用于嵌套其他文档:
src: 嵌套文档的源 URLwidth 和 height: 框架的宽度和高度这只是一些常见的 HTML 属性分类,还有其他特定元素的属性。不同的 HTML 元素可能具有不同的属性,具体取决于元素的类型和用途。
使用props:
props是最常见的做法。使用attrs:
title、class、style等,而不需要在子组件中显式声明它们时,可以使用attrs。props,但仍然需要在子组件的模板中使用。总之,props用于传递组件之间的数据,而attrs用于传递HTML属性。根据你的需求,选择适当的方式来实现组件之间的通信。