Element UI自定义描述列表组件

​​​​​​​ 

 

功能

1、每行的高度根据改行中某一列的最大高度自动撑开
2、列宽度自动补全,避免最后一列出现残缺的情况
3、支持纯文本与HTML插槽
4、支持每行几列的设置
5、支持每列宽度自定义
6、支持动态数据重绘

组件设计

1、使用父子组件嵌套实现,父组件为 e-desc, 子组件为 e-desc-item 。
2、e-desc-item传递props的label 和 插槽的value,使用插槽 $slots.content来显示DOM
3、利用 el-row 和 el-col 来实现整体组件布局

封装e-desc组件

<template>
  <div class="desc" :style="{ margin }">
    <!-- 标题 -->
    <h1 v-if="title" class="desc-title" v-html="title"></h1>
    <el-row class="desc-row">
      <slot />
    </el-row>
  </div>
</template>
 
<script>
export default {
  name: "EDesc",
  // 通过provide提供给子组件
  provide() {
    return {
      labelWidth: this.labelWidth,
      column: this.column,
      size: this.size,
    };
  },
  props: {
    // 数据源,监听数据重绘
    data: {
      type: Object,
      required: true,
      default() {
        return {};
      },
    },
    // 标题
    title: {
      type: String,
      default: "",
    },
    // 边距
    margin: {
      type: String,
      default: "0",
    },
    // label宽度
    labelWidth: {
      type: String,
      default: "120px",
    },
    column: {
      // 每行显示的项目个数
      type: [Number, String],
      default: 3,
    },
    size: {
      // 大小
      type: String,
      default: "",
    },
  },
  watch: {
    data: {
      handler() {
        this.$nextTick(() => {
          // 筛选出子组件e-desc-item
          const dataSource = this.$slots.default;
          const dataList = [];
          dataSource.forEach((item) => {
            if (
              item.componentOptions &&
              item.componentOptions.tag === "e-desc-item"
            ) {
              dataList.push(item.componentInstance);
            }
          });
          // 剩余span
          let leftSpan = this.column;
          const len = dataList.length;
          dataList.forEach((item, index) => {
            // 处理column与span之间的关系
            // 剩余的列数小于设置的span数
            const hasLeft = leftSpan <= (item.span || 1);
            // 当前列的下一列大于了剩余span
            const nextColumnSpan =
              index < len - 1 && dataList[index + 1].span >= leftSpan;
            // 是最后一行的最后一列
            const isLast = index === len - 1;
            if (hasLeft || nextColumnSpan || isLast) {
              // 满足以上条件,需要自动补全span,避免最后一列出现残缺的情况
              item.selfSpan = leftSpan;
              leftSpan = this.column;
            } else {
              leftSpan -= item.span || 1;
            }
          });
        });
      },
      deep: true,
      immediate: true,
    },
  },
};
</script>
 
<style scoped lang="less">
.desc {
  .desc-title {
    margin-bottom: 10px;
    color: #333;
    font-weight: 700;
    font-size: 16px;
    line-height: 1.5715;
  }
  .desc-row {
    display: flex;
    flex-wrap: wrap;
    border-radius: 2px;
    border: 1px solid #ebeef5;
    border-bottom: 0;
    border-right: 0;
    width: 100%;
  }
}
</style>

封装e-desc-item组件

<template>
  <el-col :span="computedSpan" class="desc-item">
    <div class="desc-item-content" :class="size">
      <label
        class="desc-item-label"
        :style="{ width: labelWidth }"
        v-html="label"
      ></label>
      <div class="desc-item-value" v-if="$slots">
        <!-- 纯文本 -->
        <slot v-if="$slots.default && $slots.default[0].text" />
        <!-- HTML -->
        <slot name="content" v-else-if="$slots.content" />
        <span v-else>暂无数据</span>
      </div>
    </div>
  </el-col>
</template>
 
<script>
export default {
  name: "EDescItem",
  inject: ["labelWidth", "column", "size"],
  props: {
    span: {
      type: [Number, String],
      required: false,
      default: 0,
    },
    label: {
      type: String,
      required: false,
      default: "",
    },
  },
  data() {
    return {
      // 子组件自己的span
      selfSpan: 0,
    };
  },
  computed: {
    computedSpan() {
      // 子组件自己的span,用于父组件计算修改span
      if (this.selfSpan) {
        return (24 / this.column) * this.selfSpan;
      } else if (this.span) {
        // props传递的span
        return (24 / this.column) * this.span;
      } else {
        // 未传递span时,取column
        return 24 / this.column;
      }
    },
  },
};
</script>
 
<style scoped lang="less">
.desc-item {
  border-right: 1px solid #ebeef5;
  border-bottom: 1px solid #ebeef5;
  .desc-item-content {
    display: flex;
    justify-content: flex-start;
    align-items: center;
    color: rgba(0, 0, 0, 0.65);
    font-size: 14px;
    line-height: 1.5;
    width: 100%;
    background-color: #fafafa;
    height: 100%;
    box-sizing: border-box;
    .desc-item-label {
      box-sizing: border-box;
      border-right: 1px solid #ebeef5;
      display: inline-block;
      padding: 10px 5px;
      flex-grow: 0;
      flex-shrink: 0;
      color: rgba(0, 0, 0, 0.6);
      font-weight: 400;
      font-size: 14px;
      line-height: 1.5;
      height: 100%;
      display: flex;
      font-weight: bold;
      align-items: center;
    }
    .desc-item-value {
      box-sizing: border-box;
      background: #fff;
      padding: 10px 5px;
      flex-grow: 1;
      overflow: hidden;
      word-break: break-all;
      height: 100%;
      display: flex;
      align-items: center;
      color: #000;
      span {
        color: #aaa;
      }
    }
    &.small {
      .desc-item-label,
      .desc-item-value {
        padding: 10px 14px;
      }
    }
  }
}
</style>

使用方式

<template>
  <e-desc :data='info' margin='0 12px' label-width='100px'>
    <e-desc-item label="姓名">{{info.name}}</e-desc-item>
    <e-desc-item label="年龄">{{ info.age }}岁</e-desc-item>
    <e-desc-item label="性别">{{ info.sex }}</e-desc-item>
    <e-desc-item label="学校">{{ info.school }}</e-desc-item>
    <e-desc-item label="专业">{{ info.major }}</e-desc-item>
    <e-desc-item label="爱好">{{ info.hobby }}</e-desc-item>
    <e-desc-item label="手机号">{{ info.phone }}</e-desc-item>
    <e-desc-item label="微信">{{ info.wx }}</e-desc-item>
    <e-desc-item label="QQ">{{ info.qq }}</e-desc-item>
    <e-desc-item label="住址">{{ info.address }}</e-desc-item>
    <e-desc-item label="描述" :span='2'>{{ info.intro }}</e-desc-item>
    <e-desc-item label="操作" :span='3'>
      <template slot="content">
        <el-button size="small" type="primary">修改</el-button>
        <el-button size="small" type="danger">删除</el-button>
      </template>
    </e-desc-item>
  </e-desc>
</template>
 
<script>
import EDesc from './e-desc'
import EDescItem from './e-desc-item'
export default {
  components: {
    EDesc, EDescItem
  },
  data () {
    return {
      info: {
        name: 'jeken',
        age: 26,
        sex: '男',
        school: '**大学',
        major: '**专业',
        address: '**省**市',
        hobby: '搬砖、前端',
        phone: 18888888888,
        wx: '151145454',
        qq: 332983810,
        intro: 'asdaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaafdddddddddddddddddddddddddd'
      }
    }
  }
}
</script>

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值