0


Vue Element UI 自定义描述列表组件

效果图

写在前面

由于vue使用的版本太低,vue element UI 的描述列表不生效,但是有时候又不想换版本的可以自定义一个描述列表。

实现哪些功能

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

组件设计

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

封装el-descriptions组件

<template>
    <div class="desc" :style="{margin}">
      <!-- 标题 -->
      <h1 v-if="title" class="desc-title" v-html="title"></h1>
      <el-row class="desc-row" ref='elRow'>
        <slot/>
      </el-row>
    </div>
  </template>
  
  <script>
  export default {
    name: 'ElDescriptions',
    // 通过provide提供给子组件
    provide () {
      return {
        labelWidth: this.labelWidth,
        column: this.column,
        size: this.size
      }
    },
    props: {
      // 标题
      title: {
        type: String,
        default: ''
      },
      // 边距
      margin: {
        type: String,
        default: '0'
      },
      // label宽度
      labelWidth: {
        type: String,
        default: '120px',
      },
      column: {
        // 每行显示的项目个数
        type: [Number, String],
        default: 4
      },
      size: {
        // 大小
        type: String,
        default: '0'
      }
    },
    data () {
      return {
        // 监听插槽变化
        observe: new MutationObserver(this.computedSpan)
      }
    },
    mounted () {
      this.$nextTick(() => {
        this.computedSpan()
        this.observe.observe(this.$refs.elRow.$el, { childList: true })
      })
    },
    methods: {
      computedSpan () {
        // 筛选出子组件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
          }
        })
      }
    },
    beforeDestroy () {
      this.observer.disconnect()
    }
  }
  </script>
  
  <style scoped lang="scss">
    .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/>
        </div>
      </div>
    </el-col>
  </template>
  
  <script>
  export default {
    name: 'ElDescriptionsItem',
    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="scss">
    .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,.65);
        font-size: 14px;
        line-height: 1.5;
        width: 100%;
        background-color: #fafafa;
        height: 100%;
        .desc-item-label{
          border-right: 1px solid #EBEEF5;
          display: inline-block;
          padding: 12px 16px;
          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;
          align-items: center;
        }
        .desc-item-value{
          background: #fff;
          padding: 12px 16px;
          flex-grow: 1;
          overflow: hidden;
          word-break: break-all;
          height: 100%;
          display: flex;
          align-items: center;
          color: #444;
          span{
            color: #aaa;
          }
          // 空数据时展示的内容
          &:empty::after {
            content: '--';
          }
        }
        &.small {
          .desc-item-label,
          .desc-item-value {
            padding: 10px 14px;
          }
        }
      }
    }
  </style>
  

使用方式

<template>
  <div class="mod-contracts">
    <el-descriptions
      margin="0 12px"
      label-width="100px"
      v-loading="dataListLoading"
      v-model="userInfo"
      v-if="userInfo"
    >
      <el-descriptions-item label="合同编号" :span="2">{{
        userInfo.num
      }}</el-descriptions-item>
      <el-descriptions-item label="合同名称" :span="2">
        {{ userInfo.title }}</el-descriptions-item
      >
      <el-descriptions-item label="姓名" :span="2">
        {{ userInfo.userName }}</el-descriptions-item
      >
      <el-descriptions-item label="公司名称" :span="2">
        {{ userInfo.companyName }}</el-descriptions-item
      >
      <el-descriptions-item label="合同类型" :span="2">
        <span v-if="userInfo.type == 0">试用期合同</span>
        <span v-if="userInfo.type == 1">正式员工合同</span>
        <span v-if="userInfo.type == 2">外包合同</span>
      </el-descriptions-item>

      <el-descriptions-item label="薪资" :span="2">{{
        userInfo.salary
      }}</el-descriptions-item>
      <el-descriptions-item label="岗位" :span="2">{{
        userInfo.post
      }}</el-descriptions-item>
      <el-descriptions-item label="合同状态" :span="2">
        <template>
          <el-tag
            v-if="userInfo.state === 0 && !isCurrentTimeLater"
            size="small"
            type="info"
            >未确认</el-tag
          >
          <el-tag v-if="userInfo.state === 1" size="small">生效</el-tag>
          <el-tag
            v-if="userInfo.state === 2 || isCurrentTimeLater"
            size="small"
            type="danger"
            >作废</el-tag
          >
        </template>
      </el-descriptions-item>
      <el-descriptions-item label="身份证号码" :span="2">{{
        userInfo.idNum
      }}</el-descriptions-item>
      <el-descriptions-item label="家庭住址" :span="2">{{
        userInfo.address
      }}</el-descriptions-item>
      <el-descriptions-item label="联系电话" :span="2">{{
        userInfo.mobile
      }}</el-descriptions-item>

      <el-descriptions-item label="合同生效时间" :span="2">{{
        userInfo.effectTime
      }}</el-descriptions-item>
      <el-descriptions-item label="合同失效时间" :span="2">{{
        userInfo.lostEffectTime
      }}</el-descriptions-item>
      <el-descriptions-item label="合同人所属部门" :span="2">
        <el-row v-for="item in deptList" :key="item.id">
          <span v-if="item.id == userInfo.deptId">{{ item.label }}</span>
        </el-row>
      </el-descriptions-item>
      <el-descriptions-item label="合同结束时间" :span="4">{{
        userInfo.endTime
      }}</el-descriptions-item>
      <el-descriptions-item label="详情" :span="4" style="height: auto">
        甲乙双方经平等协商。共同決定建立劳务关系。本服务协议属于劳务协议,不在《中华人民共和国芳动法》调整范围内,而在《中华人民共和国民法通则》、
        《中华人民共和国合同法》的调整之中。甲乙双方在充分明确这一法律关系的基础上,根据《民法通则》、《合同法》和其他相关法律、法规,自愿签订本协议,共同道守协议所列条款。
      </el-descriptions-item>
      <el-descriptions-item
        label="操作"
        :span="4"
        v-if="userInfo.state == 0 && !isCurrentTimeLater"
      >
        <template>
          <el-button size="small" type="primary" @click="update(userInfo.id)"
            >确认合同</el-button
          >
        </template>
      </el-descriptions-item>
    </el-descriptions>
<el-descriptions v-else>
  <template>暂无合同,请联系管理员</template>
</el-descriptions>
    <el-button v-if="userInfo"  style="margin-top: 10px;margin-left: 10px;" type="danger" @click="downloadExcel(userInfo.id)">导出合同</el-button>
    <update
      v-if="UpdateVisible"
      ref="updateView"
      @refreshData="getContractsInfo"
    ></update>
  </div>
</template>

<script>
import ElDescriptions from "../../common/e-desc.vue";
import ElDescriptionsItem from "../../common/e-desc-item.vue";
import Update from "./contracts-update.vue";
export default {
  components: {
    ElDescriptions,
    ElDescriptionsItem,
    Update,
  },
  data() {
    return {
      userInfo: {
        id: 0,
        userId: 0,
        roleName: "",
        username: "",
        deptId: "",
        lostEffectTime: "",
      },
      currentTime: new Date(),
      deptList: [],
      dataList:[],
      dataListLoading: false,
      UpdateVisible: false,
    };
  },

  mounted() {
    this.getDeptDataList();
    this.getContractsInfo();
    setInterval(() => {
      this.currentTime = new Date();
    }, 1000);
  },

  computed: {
    isCurrentTimeLater() {
      this.currentTime.setHours(0, 0, 0, 0);
      this.lostEffectTime = new Date(this.userInfo.lostEffectTime);
      return this.currentTime > this.lostEffectTime;
    },
  },

  methods: {
    getContractsInfo() {
      this.dataListLoading = true;
      this.$http({
        url: this.$http.adornUrl(`/contracts/info`),
        method: "get",
        params: this.$http.adornParams(),
      }).then(({ data }) => {
        if (data && data.code === 0) {
          this.userInfo = data.contracts;
          this.dataList.push(data.contracts);
        } else {
          this.userInfo = [];
          this.totalPage = 0;
        }
        this.dataListLoading = false;
      });
    },

    // 获取部门数据列表
    getDeptDataList() {
      this.dataListLoading = true;
      this.$http({
        url: this.$http.adornUrl("/sys/dept/tree"),
        method: "get",
        params: this.$http.adornParams(),
      }).then(({ data }) => {
        this.deptList = data;
      });
    },

    //完善合同
    update(id) {
      this.UpdateVisible = true;
      this.$nextTick(() => {
        this.$refs.updateView.init(id);
      });
    },

    //导出全部
    downloadExcel(id) {
      var ids=this.dataList.map(item => item.id);
      this.$confirm(`确定对[id=${ids.join(",")}]进行导出操作?`, "提示", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning",
      }).then(() => {
        // 发送 HTTP 请求以下载 Excel 文件
        this.$http({
          url: this.$http.adornUrl("/contracts/download"),
          method: "post",
          data: this.$http.adornData(ids, false),
          responseType: "blob", // 设置响应类型为二进制流
        })
          .then((response) => {
            // 创建一个 URL 对象,指向返回的二进制数据
            const url = window.URL.createObjectURL(new Blob([response.data])); // 创建一个 <a> 元素,设置其属性,模拟点击下载

            const link = document.createElement("a");
            link.href = url;
            link.setAttribute("download", "data.xlsx"); // 设置下载文件的默认名称
            document.body.appendChild(link);
            link.click(); // 清理创建的 URL 对象

            window.URL.revokeObjectURL(url);
          })
          .catch((error) => {
            console.error("下载失败", error);
          });
      });
    },
  },
};
</script>

参数说明

至此,代码就写完啦,描述列表不生效的问题应该就解决了

标签: vue.js ui 前端

本文转载自: https://blog.csdn.net/m0_51666421/article/details/132507720
版权归原作者 呜啦啦啊 所有, 如有侵权,请联系我们删除。

“Vue Element UI 自定义描述列表组件”的评论:

还没有评论