Skip to content

树组件(懒加载)使用示例

注意事项:

  • 树组件需要设置container-height属性
  • 需要需要对懒加载树组件进行过滤,需要给关键字输入框加上防抖,避免段时间大量请求服务器
  • loadData需要重点掌握
  • 取数接口一定需要和后端同事提前约定好,如果业务上有通过关键字过滤或者通过查询条件过滤树组件的需求,则需要取数接口在设计上支持关键字过滤,如果接口没有传查询条件,则接口通过传入的parentCode逐级返回数据,如果有有传递关键字,则该接口
  • 组件渲染之后,可调用updateNodeValue方法更新树组件节点属性值
  • 如果功能从设计上有关键字过滤功能,则需要根据有无关键字调用组件init与filter方法

效果图

image.png

代码示例:

<!-- 侧边栏单位 -->
<template>
  <!-- 容器 -->
  <div>
    <!-- 关键字过滤 -->
    <el-input v-model="keyword" size="small" placeholder="输入关键字过滤" prefix-icon="el-icon-search" @input="debounceOnInput" style="width: 100%;" />
    <!-- 懒加载树组件-不含有过滤单位数据时显示 -->
    <!-- TODO: 树组件需要设置container-height属性 -->
    <v-lazy-tree ref="layTree" :load="loadData" display="CODE_NAME" :props="treeProps" :container-height="treeHeight" @node-click="onNodeClick">
      <!-- 可通过默认插槽自定义节点显示 -->
      <template v-slot="{node}">
        <i class="el-icon-location mr5 f16" v-if="isActive(node)"></i>
        <span :class="statusColor(node)">{{node.agencyCode}} {{node.agencyName}}</span>
      </template>
    </v-lazy-tree>
    <!-- 状态标示 -->
    <div class="pfs-gfa-audit-manage-sidebar-agency-status">
      <span class="pfs-gfa-audit-manage-sidebar-agency-status-item no-submit">未上报</span>
      <span class="pfs-gfa-audit-manage-sidebar-agency-status-item submit">已上报</span>
      <span class="pfs-gfa-audit-manage-sidebar-agency-status-item reject">已退回</span>
      <span class="pfs-gfa-audit-manage-sidebar-agency-status-item finished">完成审核</span>
    </div>
  </div>
</template>
<script>
import fetch from '@/config/fetch';
import util from '@/assets/js/util';
import {
  PAGE_TYPE,
  AGENCY_STATE,
  AUDITED_STATUS,
  TOPIC,
} from '@/modules/pfs/pfs-gfa/pfs-gfa-audit-manage/constant';

export default {
  props: {
    contextInfo: Object, //上下文数据
    agencyInfo: Object, //当前选的单位信息
    agencyFilter: Array, //已过滤的单位数据
    getHeight: Function, //获取高度
    select: Function, //处理选择数据
  },
  data() {
    return {
      keyword: '', //关键字
      treeProps: {
        id: 'agencyCode',
        label: 'agencyName',
        pid: 'parentCode',
        disabled: 'disabled',
        leaf: 'isLeaf',
      }, //树组件绑定的字段
      treeHeight: 0, //树组件高度
    };
  },
  created() {
    // TODO:需要需要对懒加载树组件进行过滤,需要给关键字输入框加上防抖,避免段时间大量请求服务器
    this.debounceOnInput = _.debounce(this.onInput, 500);
    this.subscribe();
  },
  methods: {
    /**
     * 加载数据的方法(TODO:loadData需要重点掌握)
     * 1、如果关键字为空
     *    1、判断有无node节点数据,如果节点数据为空,则需要生成首节点数据,并自动展开
     *    2、如果node节点数据不为空,则需要获取单位节点数据,如果node节点单位数据与首节点单位数据一致,则需要自动点击第一个节点(因为默认的首节点不能被选中)
     * 2、如果关键字不为空,则通过关键字获取单位节点数据
     */
    async loadData(node = {}, resolve) {
      let data;
      try {
        if (util.isEmpty(this.keyword)) {
          if (util.isEmptyObject(node)) {
            data = this.generateFirstNode();
            _.defer(() => {
              this.$refs.layTree.onExpand(0);
            });
          } else {
            data = await this.getData(
              node.agencyCode,
              node.mofDivCode,
              node.dwType
            );
            if (
              util.isNotEmpty(data) &&
              this.contextInfo.agencyCode === node.agencyCode
            ) {
              _.defer(() => {
                this.$refs.layTree.onClick(1);
              });
            }
          }
        } else {
          data = await this.getData(
            this.contextInfo.agencyCode,
            this.contextInfo.mofDivCode,
            this.contextInfo.dwType
          );
        }
      } catch (err) {
        console.error(err);
        this.$message({
          type: 'error',
          message: err.msg,
        });
        data = [];
      } finally {
        resolve(data);
      }
    },
    /**
     * 生成第一个节点数据
     * 1、初始化加载时,需要自己从当前页面上下文环境中生成第一个单位节点数据,当前单位节点不可以被选中
     */
    generateFirstNode() {
      let { agencyCode, agencyName, mofDivCode, reportFormStatus, dwType } =
        this.contextInfo;
      return [
        {
          agencyCode,
          agencyName,
          mofDivCode,
          parentCode: '',
          status: reportFormStatus,
          dwType,
          disabled: true,
          isLeaf: false,
        },
      ];
    },
    /**
     * 获取单位数据(如果关键字不为空,则返回所有的数据都应该平铺)
     * TODO:取数接口一定需要和后端同事提前约定好,如果业务上有通过关键字过滤或者通过查询条件过滤树组件的需求,则需要取数接口在设计上支持关键字过滤,如果接口没有传查询条件,则接口通过传入的parentCode逐级返回数据,如果有有传递关键字,则该接口
     *
     */
    getData(parentCode, mofDivCode, dwType) {
      return new Promise((resolve, reject) => {
        let { transType, taskId, fiscalYear, fiscal } = this.contextInfo;
        fetch
          .post('/gfa/fr/gfa/org/query/reportAgyTree', {
            transType,
            groupId: taskId,
            mofDivCode,
            fiscalYear: fiscalYear || fiscal,
            fiscal: fiscalYear || fiscal,
            parentCode,
            keyWord: this.keyword,
            dwType,
            receiveDwType: this.contextInfo.dwType,
          })
          .then(({ data }) => resolve(data))
          .catch(reject);
      });
    },
    /**
     * 设置树组件高度
     */
    setTreeHeight() {
      this.treeHeight = this.getHeight() - 70;
    },
    /**
     * 消息订阅
     * 1、订阅单位上报状态变化消息
     * 2、订阅单位完成审核状态变化消息
     */
    subscribe() {
      this.$observable.subscribe(
        TOPIC.AGENCY_STATE_CHANGE,
        this.onAgencyStateChange
      );
      this.$observable.subscribe(
        TOPIC.AUDITED_STATUS_CHANGE,
        this.onAuditedStatusChange
      );
    },
    /**
     * 消息取消订阅
     * 1、订阅单位上报状态变化消息
     * 2、订阅单位完成审核状态变化消息
     */
    unsubscribe() {
      this.$observable.unsubscribe(
        TOPIC.AGENCY_STATE_CHANGE,
        this.onAgencyStateChange
      );
      this.$observable.unsubscribe(
        TOPIC.AUDITED_STATUS_CHANGE,
        this.onAuditedStatusChange
      );
    },
    /**
     * 处理单位上报状态变化
     * 1、如果不包含单位过滤数据,则需要获取懒加载树组件,并将指定单位的数据状态进行改变
     */
    onAgencyStateChange(agencyCode, status) {
      let ref = this.$refs.layTree;
      if (ref) {
        // TODO:组件渲染之后,可调用updateNodeValue方法更新树组件节点属性值
        ref.updateNodeValue(agencyCode, { status });
      }
    },
    /**
     * 处理单位完成审核状态变化
     * 1、如果不包含单位过滤数据,则需要获取懒加载树组件,并遍历已选单位,更新单位审核完成状态
     * @param {*} auditedStatus 审核完成状态
     * @param {*} agencyCodes 已选的单位code
     */
    onAuditedStatusChange(auditedStatus, agencyCodes) {
      let ref = this.$refs.layTree;
      if (ref) {
        agencyCodes.forEach((e) => {
          ref.updateNodeValue(e, { auditedStatus });
        });
      }
    },
    /**
     * 处理关键字输入
     * 1、如果不包含单位过滤数据,则需要获取懒加载树组件,并对树组件数据进行过滤
     */
    onInput() {
      // TODO:如果功能从设计上有关键字过滤功能,则需要根据有无关键字调用组件init与filter方法
      if (util.isEmpty(this.keyword)) {
        this.$refs.layTree.init();
      } else {
        this.$refs.layTree.filter();
      }
    },
    /**
     * 处理节点点击
     */
    onNodeClick(data) {
      this.select(PAGE_TYPE.AGENCY, data);
    },
    /**
     * 节点状态颜色
     * 1、优先判断单位完成审核状态,已完成,标识绿色
     * 2、单位未完成审核,标识单位上报状态
     */
    statusColor({ auditedStatus, status }) {
      if (auditedStatus === AUDITED_STATUS.FINISHED) {
        return 'bs-color-green';
      } else {
        switch (status) {
          case AGENCY_STATE.NO_SUBMIT:
            return 'bs-text-color';
          case AGENCY_STATE.SUBMIT:
            return 'bs-color-blue';
          case AGENCY_STATE.RETURN:
            return 'bs-color-red';
          default:
            return '';
        }
      }
    },
    /**
     * 节点是否激活
     */
    isActive(nodeInfo) {
      return nodeInfo.agencyCode === this.agencyInfo.agencyCode;
    },
  },
  computed: {
    /**
     * 是否含有过滤单位数据
     */
    hasAgencyFilter() {
      return util.isNotEmpty(this.agencyFilter);
    },
  },
  /**
   * 1、设置树组件高度
   */
  mounted() {
    this.setTreeHeight();
  },
  beforeDestroy() {
    this.debounceOnInput.cancel();
    this.unsubscribe();
  },
};
</script>
<style lang="scss" scoped>
@import '~@/assets/style/variables.scss';
.pfs-gfa-audit-manage-sidebar-agency-status {
  height: 30px;
  border-top: $-bs-border;
  display: flex;
  justify-content: space-around;
  align-items: flex-end;
  .pfs-gfa-audit-manage-sidebar-agency-status-item {
    position: relative;
    color: $-bs-text-color-light;
    font-size: 15px;
    &::before {
      position: absolute;
      content: ' ';
      top: 6px;
      left: -20px;
      width: 15px;
      height: 12px;
      border-radius: $-bs-border-radius;
    }
    &.no-submit::before {
      background-color: $-bs-text-color;
    }
    &.submit::before {
      background-color: $-bs-color-blue;
    }
    &.reject::before {
      background-color: $-bs-color-red;
    }
    &.finished::before {
      background-color: $-bs-color-green;
    }
  }
}
</style>