@pluve/lego-tree-vue
TypeScript icon, indicating that this package has built-in type declarations

0.1.0 • Public • Published

@pluve/lego-tree-vue

乐高系列之 tree 组件系列,属于强业务组件。

npm (scoped)

@pluve/lego-tree-vue 一期二期规划的功能已上线,目前已在【营销工具 - 一元券】等项目中使用。

安装

# 依赖 vue@3.x/ant-design-vue@3.x/@ant-design/icons-vue@6.x/@pluve/lego-excel-vue@*
yarn add @pluve/lego-tree-vue

公共类型 LegoTreeTypes

LegoTreeTypes.OrgTreeJsonFromEnum

组织机构树 orgTree.json 来源枚举。当走 SSO 方式时,不区分全量 或者 有效 的数据源

export enum OrgTreeJsonFromEnum {
  SSO = 'sso', // SSO(默认,不区分全量 or 有效)
  USER = 'user', // 账号中心
}

LegoTreeTypes.OrgTreeJsonTypeEnum

组织机构树 json 类型:全量(包含已关停等) or 有效的,提供枚举。

export enum OrgTreeJsonTypeEnum {
  ALL = 'all', // 全量的数据
  ENABLE = 'enable', // 有效的数据(默认)
}

LegoTreeTypes.CommonHierarchyEnum

组织机构树原始树结构返回的 hierarchy 字段枚举。

export enum CommonHierarchyEnum {
  COMPANY = '01', // 公司
  CENTER = '02', // 中心
  OPERATING_AREA = '03', // 营运区
  DEPARTMENT = '04', // 部门
  GROUP = '05', // 组
  AREA = '06', // 片区
  STORE = '07', // 门店
}

LegoTreeTypes.IOriginTreeNode

接口返回的原始节点信息,除 [key: string] 外,其他字段都为 通用组织机构树接口 返回的字段。

参数 说明 类型 是否可选
code 节点编码 string
hierarchy (SSO)组织机构级别:01(公司);02(中心);03(营运区);04(部门);05(组);06(片区);07(门店); string
rank (账号中心)组织机构级别:01(公司);02(中心);03(营运区);04(部门);05(组);06(片区);07(门店); LegoTreeTypes.CommonHierarchyEnum
id 唯一 key string
name 名称 string
parentId 父 id string
storeCode 门店编码 string
disabled 是否节点禁用 boolean
selectable 【单选】节点是否可以选择 boolean
sub (SSO)子节点 LegoTreeTypes.IOriginTreeNode[]
children (账号中心)子节点 LegoTreeTypes.IOriginTreeNode[]
[key: string] 其他字段,用于匹配非组织机构树接口的返回字段 any

LegoTreeTypes.ITreeNodesType

接口返回数据转化后的节点信息,必须按照如下类型来传输。

参数 说明 类型 是否可选
key 唯一 key。走默认数据源取数逻辑 && 账号中心全量数据时,key 取值方式:code_storeCode;走默认数据源取数逻辑 && SSO 或 账号中心有效数据时,key 取值方式:code/storeCode; string
value 唯一 keyLegoTreeSelect 组件使用。走默认数据源取数逻辑 && 账号中心全量数据时,value 取值方式:code_storeCode;走默认数据源取数逻辑 && SSO 或 账号中心有效数据时,value 取值方式:code/storeCode; string
title 节点名称 string
hierarchy 组织机构级别:01(公司);02(中心);03(营运区);04(部门);05(组);06(片区);07(门店);若接口返回该字段为空,则组件内部处理为从1开始往下叠加 string
code 原始 code string
storeCode 原始 storeCode,门店节点时才存在该字段 string
pid 上一节点的 'code' 字段 string
trace 父子节点路径,includeAreatrue 时,该字段必须有值 string
disabled 是否节点禁止操作 boolean
selectable 【单选】节点是否可以选择 boolean
children 子节点 LegoTreeTypes.ITreeNodesType[]
originNode 原始树节点信息,keepOriginNode 开启后 该对象不为 undefined,且在 includeAreatruelabelInValuetrue 时返回给调用方 LegoTreeTypes.IOriginTreeNode

LegoTreeTypes.ICheckedAreaItem

可选择父子层级的选中项节点信息

参数 说明 类型 是否可选
key 唯一 key。走默认数据源取数逻辑 && 账号中心全量数据时,key 取值方式:code_storeCode;走默认数据源取数逻辑 && SSO 或 账号中心有效数据时,key 取值方式:code/storeCode; string
title 节点名称 string
hierarchy 组织机构级别:01(公司);02(中心);03(营运区);04(部门);05(组);06(片区);07(门店);若接口返回该字段为空,则组件内部处理为从1开始往下叠加 string
code 原始 code string
storeCode 原始 storeCode,门店节点时才存在该字段 string
originNode 原始树节点信息,keepOriginNode 开启后 该对象不为 undefined,且在 includeAreatrue时返回给调用方 LegoTreeTypes.IOriginTreeNode

LegoTreeTypes.ITreeSelectLabelInValue

LegoTreeSelect 组件 开启 labelInValue 时的 value 类型,即 当前节点的信息 & antd TreeSelect 组件返回的当前节点的信息的并集

export interface ITreeSelectLabelInValue extends Partial<LegoTreeTypes.ITreeNodesType> {
  /**
   * @description 走默认数据源取数逻辑 && 账号中心全量数据时,value 取值方式:code_storeCode
   * 走默认数据源取数逻辑 && SSO 或 账号中心有效数据时,value 取值方式:code/storeCode
   * @author yangwen
   * @type {string}
   * @memberof ITreeSelectLabelInValue
   */
  value: string;
  label?: string;
  disabled?: boolean;
}
参数 说明 类型 是否可选
value 唯一 keyLegoTreeSelect 组件使用。走默认数据源取数逻辑 && 账号中心全量数据时,value 取值方式:code_storeCode;走默认数据源取数逻辑 && SSO 或 账号中心有效数据时,value 取值方式:code/storeCode; string
label 节点名称,与 title 字段取值一致 string
disabled 是否节点禁止操作 boolean

树数据源处理

优先级:treeData > request > orgTree.json

1. treeData

若传值 treeData 不为 undefined,则组件认定是调用方需要自定义数据源,则完全信任调用方传入的数据源,组件内部不对 treeData 做处理。 同时接收 loading 字段 和 dataListMap 字段,作为加载数据源的 加载状态扁平化的数据源。 此时需要区分 2 种情况:

  1. dataListMap 为空对象:则调用 LegoTreeUtils.generateListMap 方法来生成 扁平化数据源 dataListMap 并存储在组件内部,同时给 treeData 子节点增加 trace 字段用作记录父子节点的路径。

  2. dataListMap 不为空对象:则默认调用方已经扁平化了数据源 且给子节点增加了 trace 字段,直接存储在组件内部即可。

注意:

  • trace 字段主要用于组织机构树可以选择任意节点的场景,需要通过 trace 字段来判定父子节点间的关系。如果只需要选择子节点,则可以忽略所有针对 trace 字段的要求。
  • treeData 里面每个节点的 originNode 字段需要自行处理includeAreatruelabelInValuetrue 时,若开启 keepOriginNode,则 返回的值里面会增加 originNode 字段。

2. request

若传值 treeDataundefined,且 request 不为 undefined,则组件认定是调用方需要通过自定义服务来自定义数据源,则完全信任调用方传入自定义服务,组件内部执行调用服务逻辑。 获取数据源后,通过调用 LegoTreeUtils.generateListMap 方法来生成 扁平化数据源 dataListMap 并存储在组件内部,同时给 treeData 子节点增加 trace 字段用作记录父子节点的路径。

注意:

  • trace 字段主要用于组织机构树可以选择任意节点的场景,需要通过 trace 字段来判定父子节点间的关系。如果只需要选择子节点,则可以忽略所有针对 trace 字段的要求。
  • treeData 里面每个节点的 originNode 字段需要自行处理includeAreatruelabelInValuetrue 时,若开启 keepOriginNode,则 返回的值里面会增加 originNode 字段。

3. orgTree.json

若上述 2 个字段都为 undefined,则组件认定调用方没有自定义的需求,则直接走组件默认的数据源取数逻辑:

  1. 根据 envType 字段区分走测试还是生产环境的接口调用——注意:此时获取到的数据没有做任何权限处理
  2. 根据 jsonFrom 字段区分走 SSO 还是走账号中心——注意:SSO 只返回有效的数据源,不包含已关停数据,如需已关停数据,请走账号中心
  3. 根据 jsonType 字段区分返回全量(包含已关停等)还是有效的数据源,获取全部的组织机构树的数据源
  4. 根据 hierarchyList 字段过滤不包含的节点信息,传空或不传则默认取全量数据源
  5. 调用 LegoTreeUtils.recursionTreeData 方法对数据源做处理,转化为 Tree/TreeSelect 组件需要的数据源
  6. 根据 selectHierarchy 字段过滤子节点不包含 selectHierarchy 层级的节点。例如,不包含门店节点的父节点全部过滤掉不展示,则 selectHierarchy 为 '07'。
  7. 调用 LegoTreeUtils.generateListMap 方法来生成 扁平化数据源 dataListMap 并存储在组件内部,同时给 treeData 子节点增加 trace 字段用作记录父子节点的路径,并更新 treeData 且展示在树上。

相关源码

const loadOrgTreeNodeByLevel = async () => {
  if (state.loading) {
    return;
  }

  state.loading = true;

  // 此处 使用 toRaw 针对 props.treeData/props.dataListMap(响应式对象) 做转化
  if (Object.prototype.toString.call(props.treeData) === '[object Array]') {
    // 此处需要确保传的 treeData 已经做了数据处理且包含 trace 字段
    state.treeData = toRaw(props.treeData || []);
    // dataListMap 有值则直接取
    if (Object.values(toRaw(props.dataListMap || {})).length) {
      // 此处需要确保传的 dataListMap 已经做了数据处理
      state.dataListMap = toRaw(props.dataListMap || {});
    } else {
      // dataListMap 没有值则计算再赋值
      const { dataList, dataListMap } = LegoTreeUtils.generateListMap(
        toRaw(props.treeData || [])
      );
      // 此时 treeData 增加了 trace 字段
      state.treeData = dataList;
      state.dataListMap = dataListMap;
    }

    state.loading = false;
    // 接口请求成功后,往外触发 getTreeInfo 事件,获取树相关数据
    emit('getTreeInfo', {
      treeData: toRaw(state.treeData),
      dataListMap: toRaw(state.dataListMap),
    });
    return;
  }

  LegoTreeUtils.loadOrgTreeNodeByLevel(
    {
      request: props.request,
      selectHierarchy: props.selectHierarchy,
      envType: props.envType,
      jsonFrom: props.jsonFrom,
      jsonType: props.jsonType,
      hierarchyList: props.hierarchyList,
      keepOriginNode: props.keepOriginNode ?? false,
    },
    (dataList, dataListMap) => {
      state.treeData = dataList;
      state.dataListMap = dataListMap;
      state.loading = false;
      // 接口请求成功后,往外触发 getTreeInfo 事件,获取树相关数据
      emit('getTreeInfo', {
        treeData: dataList,
        dataListMap: dataListMap,
      });
    }
  );
};
const loadOrgTreeNodeByLevel = async (
  {
    request,
    selectHierarchy = '',
    envType = 'test',
    jsonFrom = LegoTreeTypes.OrgTreeJsonFromEnum.SSO,
    jsonType = LegoTreeTypes.OrgTreeJsonTypeEnum.ENABLE,
    hierarchyList = [],
    enableSelectedHierarchy = [],
    keepOriginNode = false,
  }: {
    request?: () => Promise<LegoTreeTypes.ITreeNodesType[]>; // 自定义 request 接口请求
    selectHierarchy?: string; // 选择的组织机构级别:01(公司);02(中心);03(营运区);04(部门);05(组);06(片区);07(门店);
    envType?: 'test' | 'prod'; // 环境(测试 | 生产)
    jsonFrom?: LegoTreeTypes.OrgTreeJsonFromEnum; // 组织机构树 orgTree.json 来源
    jsonType?: LegoTreeTypes.OrgTreeJsonTypeEnum; // 组织机构树 json 类型:全量(包含已关停等) or 有效的
    hierarchyList?: string[]; // 组织机构级别数组,默认全部
    enableSelectedHierarchy?: string[]; // 【单选】可选择的组织机构级别,不传或为空则都可以选择,传了则包含的级别才可以选择
    keepOriginNode?: boolean; // 是否树节点保留原始对象信息
  },
  callback: (
    treeData: LegoTreeTypes.ITreeNodesType[],
    dataListMap: Record<string, LegoTreeTypes.ITreeNodesType>
  ) => void
) => {
  try {
    let newTreeData: LegoTreeTypes.ITreeNodesType[] = [];
    // 直接传接口进来,则直接调接口,接口中已经对数据做了处理
    if (typeof request !== 'undefined') {
      newTreeData = await request();
    } else {
      // 否则走默认接口,组件中做处理
      const result = await LegoTreeServices.getAllOrgTreeNodesByOss({
        hierarchyList,
        envType,
        jsonFrom,
        jsonType,
      });
      // 将接口返回的数据结构转化为 TreeSelect/Tree 组件需要的数据结构
      newTreeData = LegoTreeUtils.recursionTreeData({
        list:
          result?.[0][jsonFrom === LegoTreeTypes.OrgTreeJsonFromEnum.SSO ? 'sub' : 'children'] ||
          [],
        childrenKey: jsonFrom === LegoTreeTypes.OrgTreeJsonFromEnum.SSO ? 'sub' : 'children',
        hierarchyKey: jsonFrom === LegoTreeTypes.OrgTreeJsonFromEnum.SSO ? 'hierarchy' : 'rank',
        pid: undefined,
        enableSelectedHierarchy, // 【单选】可选择的组织机构级别,不传或为空则都可以选择,传了则包含的级别才可以选择
        keepOriginNode,
        includeDisabledData: LegoTreeUtils.isAllIncludeDisabledData({ jsonFrom, jsonType }),
      });
      // 过滤树底层没有 selectHierarchy 的节点
      LegoTreeUtils.excludeNode(newTreeData, selectHierarchy);
    }

    const { dataList, dataListMap } = LegoTreeUtils.generateListMap(newTreeData);
    callback(dataList, dataListMap);
  } catch (error) {
    callback([], {});
  }
};

LegoTreeSelect

通常用于单选组织架构,例如选择门店、公司、各组织机构等。props 继承自 antd vue TreeSelectProps

export interface ILegoTreeSelectProps
  extends Omit<
    TreeSelectProps,
    | 'defaultValue'
    | 'fieldNames'
    | 'multiple'
    | 'replaceFields'
    | 'searchValue'
    | 'showSearch'
    | 'treeCheckable'
    | 'treeCheckStrictly'
    | 'treeData'
    | 'treeDataSimpleMode'
    | 'virtual'
    | 'onSearch'
    | 'loading'
    | 'value'
    | 'onChange'
  > {
  envType?: 'test' | 'prod';
  jsonFrom?: OrgTreeJsonFromEnum;
  jsonType?: OrgTreeJsonTypeEnum;
  hierarchyList?: string[];
  selectHierarchy?: string;
  enableSelectedHierarchy?: string[];

  treeData?: LegoTreeTypes.ITreeNodesType[];
  loading?: boolean;
  dataListMap?: Record<string, LegoTreeTypes.ITreeNodesType>;
  request?: () => Promise<LegoTreeTypes.ITreeNodesType[]>;

  defaultValue?: string | LegoTreeTypes.ITreeSelectLabelInValue;
  value?: string | LegoTreeTypes.ITreeSelectLabelInValue;

  keepOriginNode?: boolean;
}

API

参数 说明 类型 是否可选 默认值
envType 环境(测试 | 生产)。走组件默认接口请求时使用,其余场景无需传该字段 test | prod test
jsonFrom 组织机构树 json 来源。走组件默认接口请求时使用,其余场景无需传该字段 LegoTreeTypes.OrgTreeJsonFromEnum LegoTreeTypes.OrgTreeJsonFromEnum.SSO
jsonType 组织机构树 json 类型:全量(包含已关停等) or 有效的。走组件默认接口请求时使用,其余场景无需传该字段 LegoTreeTypes.OrgTreeJsonTypeEnum LegoTreeTypes.OrgTreeJsonTypeEnum.ENABLE
hierarchyList 组织机构级别数组,01-07,用于过滤不包含的节点信息,没传或为空则不过滤。走组件默认接口请求时使用,其余场景无需传该字段 string[] -
selectHierarchy 选择的组织机构级别,01-07,用于过滤子节点不包含 selectHierarchy 层级的节点。走组件默认接口请求时使用,其余场景无需传该字段 string -
enableSelectedHierarchy 【单选】可选择的组织机构级别,不传或为空则都可以选择,传了则包含的级别才可以选择,否则 selectablefalse 不可选择 string[] []
treeData antd vue TreeSelect 组件数据源(已经经过转化后的数据源) LegoTreeTypes.ITreeNodesType[] -
loading 外部数据源加载时的 loading 状态 boolean false
dataListMap 扁平化 Map(已经经过转化后的数据源生成的 map) Record<string, LegoTreeTypes.ITreeNodesType> {}
request 自定义服务,用于获取组件树数据源 () => Promise<LegoTreeTypes.ITreeNodesType[]> -
defaultValue 默认选中的值 string | LegoTreeTypes.ITreeSelectLabelInValue -
value(v-model) 当前选中的值 string | LegoTreeTypes.ITreeSelectLabelInValue -
keepOriginNode 是否树节点保留原始对象信息。若开启,配合 labelInValue 字段为 true,则会返回 originNode(原始对象信息) boolean false
title 自定义标题,在原始基础上,增加当前搜索值 searchValue 字段,供自定义判断处理 slot({ key, title, ..., searchValue }) -

事件(emit)

事件名称 说明 回调参数
change 选中树节点时调用此函数 function(string | LegoTreeTypes.ITreeSelectLabelInValue)
getTreeInfo 接口请求完成后,调用此函数,返回当前组件内部的树数据源 function({ treeData: LegoTreeTypes.ITreeNodesType[]; dataListMap: Record<string, LegoTreeTypes.ITreeNodesType>; })

ref

通过 ref 获取组件提供的方法或属性。以下是提供的方法或属性:

方法/属性名称 说明 类型
getTreeInfo 【方法】提供组件内部的树数据源 () => ({ treeData: LegoTreeTypes.ITreeNodesType[]; dataListMap: Record<string, LegoTreeTypes.ITreeNodesType>; })

效果展示

测试环境地址:https://yf-test-oss.yifengx.com/webtest/yangwen/legoTree/index.html#/treeSelect

使用示例

<template>
  <ConfigProvider :locale="zh_CN">
    <div :class="$style.wrap">
      <Form :colon="false" autocomplete="off">
        <Row :gutter="20">
          <!-- 有效数据源 -->
          <Col :span="12">
            <FormItem label="labelInValue true" v-bind="form.validateInfos.rangeCodeObj">
              <LegoTreeSelect
                :hierarchyList="['01', '02', '03', '04', '06', '07']"
                selectHierarchy="07"
                :enable-selected-hierarchy="['01', '07']"
                v-model:value="modelRef.rangeCodeObj"
                :labelInValue="true"
                :default-value="{ value: 'H535' }"
                :keep-origin-node="true"
                ref="treeRef"
                @get-tree-info="getTreeInfo"
              >
                <template #title="{ value: val, title, label, storeCode }">
                  <b v-if="val === '5024'" style="color: #08c">{{ title || label }}</b>
                  <b v-else-if="storeCode === '5500'" style="color: #f90">{{ title || label }}</b>
                  <template v-else>{{ title || label }}</template>
                </template>
              </LegoTreeSelect>
            </FormItem>
          </Col>
          <Col :span="12">
            <FormItem label="labelInValue false" v-bind="form.validateInfos.rangeCode">
              <LegoTreeSelect
                :treeData="state.treeData"
                :loading="state.loading"
                v-model:value="modelRef.rangeCode"
                :labelInValue="false"
              ></LegoTreeSelect>
            </FormItem>
          </Col>

          <!-- 账号中心 && 全部数据源(包含已关停) -->
          <Col :span="24" style="font-weight: bold; color: red; margin-bottom: 24px">
            以下数据源为账号中心全量数据源(包含已关停数据):
          </Col>
          <Col :span="12">
            <FormItem label="labelInValue true" v-bind="form.validateInfos.rangeCodeObjAll">
              <LegoTreeSelect
                :json-from="LegoTreeTypes.OrgTreeJsonFromEnum.USER"
                :json-type="LegoTreeTypes.OrgTreeJsonTypeEnum.ALL"
                :hierarchyList="['01', '02', '03', '04', '06', '07']"
                selectHierarchy="07"
                :enable-selected-hierarchy="['01', '07']"
                v-model:value="modelRef.rangeCodeObjAll"
                :labelInValue="true"
                :default-value="{ value: '10025961_H535' }"
                :keep-origin-node="true"
                ref="treeRefAll"
                @get-tree-info="getTreeInfoAll"
              >
                <template #title="{ value: val, title, label, storeCode }">
                  <b v-if="val === '10004575_5024'" style="color: #08c">{{ title || label }}</b>
                  <b v-else-if="storeCode === '5500'" style="color: #f90">{{ title || label }}</b>
                  <template v-else>{{ title || label }}</template>
                </template>
              </LegoTreeSelect>
            </FormItem>
          </Col>
          <Col :span="12">
            <FormItem label="labelInValue false" v-bind="form.validateInfos.rangeCodeAll">
              <LegoTreeSelect
                :treeData="state.treeDataAll"
                :loading="state.loadingAll"
                v-model:value="modelRef.rangeCodeAll"
                :labelInValue="false"
              ></LegoTreeSelect>
            </FormItem>
          </Col>
          <Col :span="6">
            <Button @click="onQuery" style="margin-left: 10px" type="primary">查询</Button>
          </Col>
        </Row>
      </Form>
    </div>
  </ConfigProvider>
</template>

<script setup lang="ts">
import zh_CN from 'ant-design-vue/es/locale/zh_CN';
import dayjs from 'dayjs';
import type { Rule } from 'ant-design-vue/es/form';
import { ConfigProvider, Form, FormItem, Row, Col, Button } from 'ant-design-vue';
import { reactive, onMounted, ref } from 'vue';
import { LegoTreeSelect, LegoTreeUtils, LegoTreeTypes, LegoTreeServices } from '@pluve/lego-tree-vue';
import 'dayjs/locale/zh-cn';
dayjs.locale('zh-cn');

const treeRef = ref();
const treeRefAll = ref();

// 查询条件 ref
const modelRef = reactive<{
  rangeCodeObj?: LegoTreeTypes.ITreeSelectLabelInValue;
  rangeCodeObjAll?: LegoTreeTypes.ITreeSelectLabelInValue;
  rangeCode?: string;
  rangeCodeAll?: string;
}>({
  rangeCodeObj: undefined,
  rangeCodeObjAll: undefined,
  rangeCode: '5024',
  rangeCodeAll: '10004575_5024', // 全量数据源时:需要使用 code_storeCode 格式
});

const rulesRef: Record<string, Rule[]> = reactive({
  rangeCodeObj: [
    {
      required: true,
      validator: (_rule: Rule, value?: LegoTreeTypes.ITreeSelectLabelInValue) => {
        if (!value) {
          return Promise.reject(new Error('请选择'));
        }
        return Promise.resolve();
      },
    },
  ],
  rangeCodeObjAll: [
    {
      required: false,
    },
  ],
  rangeCode: [
    {
      required: false,
    },
  ],
  rangeCodeAll: [
    {
      required: true,
      validator: (_rule: Rule, value?: string) => {
        if (!value) {
          return Promise.reject(new Error('请选择'));
        }
        return Promise.resolve();
      },
    },
  ],
});

// 查询条件 form
const form = Form.useForm(modelRef, rulesRef);

const state = reactive<{
  treeData: LegoTreeTypes.ITreeNodesType[];
  treeDataAll: LegoTreeTypes.ITreeNodesType[];
  loading: boolean;
  loadingAll: boolean;
}>({
  treeData: [],
  treeDataAll: [],
  loading: false,
  loadingAll: false,
});

// 获取 SSO 有效数据源
const loadOrgTreeNodeByLevel = async () => {
  state.loading = true;
  try {
    const result = await LegoTreeServices.getAllOrgTreeNodesByOss({
      hierarchyList: ['01', '02', '03', '04', '06', '07'],
      envType: 'test',
    });
    const newTreeData = LegoTreeUtils.recursionTreeData({
      list: result?.[0].sub || [],
      pid: undefined,
    });
    LegoTreeUtils.excludeNode(newTreeData, '07');
    state.treeData = newTreeData;
  } catch (error) {
    state.treeData = [];
  } finally {
    state.loading = false;
  }
};

// 获取账号中心全量数据源
const loadOrgTreeNodeByLevelAll = async () => {
  state.loadingAll = true;
  try {
    const result = await LegoTreeServices.getAllOrgTreeNodesByOss({
      hierarchyList: ['01', '02', '03', '04', '06', '07'],
      envType: 'test',
      jsonFrom: LegoTreeTypes.OrgTreeJsonFromEnum.USER,
      jsonType: LegoTreeTypes.OrgTreeJsonTypeEnum.ALL,
    });
    const newTreeData = LegoTreeUtils.recursionTreeData({
      list: result?.[0].children || [],
      pid: undefined,
      childrenKey: 'children',
      hierarchyKey: 'rank',
      includeDisabledData: true,
    });
    LegoTreeUtils.excludeNode(newTreeData, '07');
    state.treeDataAll = newTreeData;
  } catch (error) {
    state.treeDataAll = [];
  } finally {
    state.loadingAll = false;
  }
};

onMounted(() => {
  loadOrgTreeNodeByLevel();
  loadOrgTreeNodeByLevelAll();
});

const onQuery = () => {
  console.log('treeRef.value', treeRef.value?.getTreeInfo());
  console.log('treeRefAll.value', treeRefAll.value?.getTreeInfo());

  form
    .validate()
    .then(() => {
      console.log('查询条件:', modelRef);
    })
    .catch((err) => {
      console.log('err->', err);
    });
};

// 接口请求成功后返回对应的树数据
const getTreeInfo = ({
  treeData,
  dataListMap,
}: {
  treeData: LegoTreeTypes.ITreeNodesType[];
  dataListMap: Record<string, LegoTreeTypes.ITreeNodesType>;
}) => {
  console.log('getTreeInfo', { treeData, dataListMap });
};

// 接口请求成功后返回对应的树数据
const getTreeInfoAll = ({
  treeData,
  dataListMap,
}: {
  treeData: LegoTreeTypes.ITreeNodesType[];
  dataListMap: Record<string, LegoTreeTypes.ITreeNodesType>;
}) => {
  console.log('getTreeInfoAll', { treeData, dataListMap });
};
</script>

<style lang="less" module>
.wrap {
  position: relative;
  background-color: #fff;
  padding: 40px 80px;
  border-radius: 4px;
}
</style>

LegoLeftTree

通常用于页面左右侧布局,左侧展示组织机构树,右侧展示业务内容。左侧操作后右侧同步更新。

export interface ILegoLeftTreeProps
  extends Omit<
    TreeProps,
    | 'blockNode'
    | 'fieldNames'
    | 'multiple'
    | 'treeData'
    | 'virtual'
    | 'checkable'
    | 'selectable'
    | 'checkedKeys'
    | 'selectedKeys'
    | 'autoExpandParent'
    | 'expandedKeys'
    | 'height'
  > {
  envType?: 'test' | 'prod';
  jsonFrom?: OrgTreeJsonFromEnum;
  jsonType?: OrgTreeJsonTypeEnum;
  hierarchyList?: string[];
  selectHierarchy?: string;
  enableSelectedHierarchy?: string[];

  treeData?: LegoTreeTypes.ITreeNodesType[];
  loading?: boolean;
  dataListMap?: Record<string, LegoTreeTypes.ITreeNodesType>;
  request?: () => Promise<LegoTreeTypes.ITreeNodesType[]>;

  value?: string | string[] | LegoTreeTypes.ICheckedAreaItem[];
  headerTitle?: string;
  placeholder?: string;
  disabled?: boolean;
  selectType?: 'single' | 'multiple';
  includeArea?: boolean;
  width?: number | string;
  virtualHeight?: number;
  showSwitch?: boolean;
  defaultIsShow?: boolean;

  keepOriginNode?: boolean;
}

API

参数 说明 类型 是否可选 默认值
envType 环境(测试 | 生产)。走组件默认接口请求时使用,其余场景无需传该字段 test | prod test
jsonFrom 组织机构树 json 来源。走组件默认接口请求时使用,其余场景无需传该字段 LegoTreeTypes.OrgTreeJsonFromEnum LegoTreeTypes.OrgTreeJsonFromEnum.SSO
jsonType 组织机构树 json 类型:全量(包含已关停等) or 有效的。走组件默认接口请求时使用,其余场景无需传该字段 LegoTreeTypes.OrgTreeJsonTypeEnum LegoTreeTypes.OrgTreeJsonTypeEnum.ENABLE
hierarchyList 组织机构级别数组,01-07,用于过滤不包含的节点信息,没传或为空则不过滤。走组件默认接口请求时使用,其余场景无需传该字段 string[] -
selectHierarchy 选择的组织机构级别,01-07,用于过滤子节点不包含 selectHierarchy 层级的节点。走组件默认接口请求时使用,其余场景无需传该字段 string -
enableSelectedHierarchy 【单选】可选择的组织机构级别,不传或为空则都可以选择,传了则包含的级别才可以选择,否则 selectablefalse 不可选择 string[] []
treeData antd vue TreeSelect 组件数据源(已经经过转化后的数据源) LegoTreeTypes.ITreeNodesType[] -
loading 外部数据源加载时的 loading 状态 boolean false
dataListMap 扁平化 Map(已经经过转化后的数据源生成的 map) Record<string, LegoTreeTypes.ITreeNodesType> {}
request 自定义服务,用于获取组件树数据源 () => Promise<LegoTreeTypes.ITreeNodesType[]> -
value(v-model) 当前选中的值,区分单选、多选门店、多选节点三种类型 string | string[] | LegoTreeTypes.ICheckedAreaItem[] -
headerTitle 头部标题文案 string 选择门店
placeholder Input.Search 输入框 placeholder string 搜索门店名称或编码
disabled tree 选择是否禁用 boolean false
selectType 选择类型:单选/多选 single | multiple 'single'
includeArea 【多选】是否包含区域选择,即是否允许选择各层级父子节点 boolean false
width 组件宽度 string | number 280
virtualHeight 组件 Tree 虚拟滚动高度 number 820
showSwitch 是否展示 展开收起功能 开关 boolean true
defaultIsShow 默认是否处于展开状态 boolean true
keepOriginNode 是否树节点保留原始对象信息。若开启,配合 includeAreatrue,则会返回 originNode(原始对象信息) boolean false
title 自定义标题,在原始基础上,增加当前搜索值 searchValue 字段,供自定义判断处理 slot({ key, title, ..., searchValue }) -

事件(emit)

事件名称 说明 回调参数
change 选中/反选树节点时调用此函数 function(string | string[] | LegoTreeTypes.ICheckedAreaItem[])
switch 展开/收起按钮切换时调用此函数 function(boolean)
checkBefore Tree onCheck 触发时优先调用此函数,此函数第三个参数返回 组件内部的树数据源 function(checked: Key[] | { checked: Key[]; halfChecked: Key[]; }, info: CheckInfo, treeInfo: { treeData: LegoTreeTypes.ITreeNodesType[]; dataListMap: Record<string, LegoTreeTypes.ITreeNodesType> })
getTreeInfo 接口请求完成后,调用此函数,返回当前组件内部的树数据源 function({ treeData: LegoTreeTypes.ITreeNodesType[]; dataListMap: Record<string, LegoTreeTypes.ITreeNodesType>; })

ref

通过 ref 获取组件提供的方法或属性。以下是提供的方法或属性:

方法/属性名称 说明 类型
getTreeInfo 【方法】提供组件内部的树数据源 () => ({ treeData: LegoTreeTypes.ITreeNodesType[]; dataListMap: Record<string, LegoTreeTypes.ITreeNodesType>; })

效果展示

测试环境地址:https://yf-test-oss.yifengx.com/webtest/yangwen/legoTree/index.html#/leftTree

使用示例

<template>
  <div :class="$style.top">
    <RadioGroup v-model:value="state.dataType">
      <Radio value="all">全量(包含已关停)</Radio>
      <Radio value="enable">有效</Radio>
    </RadioGroup>
    <Button type="primary" size="small" @click="getTreeInfoWithRef" style="margin-left: 50px">
      点我获取tree数据源
    </Button>
  </div>

  <!-- 有效数据源 -->
  <div :class="$style.wrap" :style="{ display: state.dataType === 'all' ? 'none' : 'flex' }">
    <LegoLeftTree
      env-type="prod"
      :hierarchyList="['01', '02', '03', '04', '06', '07']"
      selectHierarchy="07"
      :enable-selected-hierarchy="['01', '07']"
      header-title="选择范围"
      placeholder="搜索范围名称或编码"
      v-model:value="state.storeCode"
      :show-switch="false"
      ref="treeRef"
      @get-tree-info="getTreeInfo"
      @change="(val) => onChange1(val)"
    >
      <template #title="{ value: val, title, label }">
        <div>
          <span style="margin-right: 10px">{{ title || label }}</span>
          <exclamation-circle-outlined @click="onIconClick1(val)" />
        </div>
      </template>
    </LegoLeftTree>

    <div style="margin-left: 20px; width: auto; height: 100%">
      <LegoLeftTree
        :request="customRequest"
        v-model:value="state.storeCodes"
        header-title="选择渠道"
        placeholder="搜索渠道名称或编码"
        select-type="multiple"
        width="300px"
        :default-is-show="true"
        @switch="onSwitch2"
        @change="(val) => onChange2(val)"
      >
        <template #title="{ value: val, title, label, searchValue }">
          <div>
            <span
              style="margin-right: 10px"
              :style="{ color: searchValue === val ? 'red' : '#222222' }"
            >
              {{ title || label }}
            </span>
            <up-circle-outlined @click="onIconClick2(val)" />
          </div>
        </template>
      </LegoLeftTree>
    </div>

    <div style="margin-left: 20px; width: auto; height: 100%">
      <LegoLeftTree
        :treeData="state.treeData"
        :loading="state.loading"
        v-model:value="state.storeNodes"
        header-title="选择组织机构"
        select-type="multiple"
        include-area
        :virtual-height="600"
        :default-is-show="false"
        :keep-origin-node="true"
        @switch="onSwitch3"
        @change="(val) => onChange3(val)"
        @check-before="onCheckBefore"
      />
    </div>

    <div :class="$style.right">我是右侧业务内容</div>
  </div>

  <!-- 账号中心 && 全部数据源(包含已关停) -->
  <div :class="$style.wrap" :style="{ display: state.dataType === 'all' ? 'flex' : 'none' }">
    <LegoLeftTree
      :json-from="LegoTreeTypes.OrgTreeJsonFromEnum.USER"
      :json-type="LegoTreeTypes.OrgTreeJsonTypeEnum.ALL"
      env-type="test"
      :hierarchyList="['01', '02', '03', '04', '06', '07']"
      selectHierarchy="07"
      :enable-selected-hierarchy="['01', '07']"
      header-title="选择范围"
      placeholder="搜索范围名称或编码"
      v-model:value="state.storeCodeAll"
      :show-switch="false"
      ref="treeRefAll"
      @get-tree-info="getTreeInfoAll"
      @change="(val) => onChange1(val)"
    >
      <template #title="{ value: val, title, label }">
        <div>
          <span style="margin-right: 10px">{{ title || label }}</span>
          <exclamation-circle-outlined @click="onIconClick1(val)" />
        </div>
      </template>
    </LegoLeftTree>

    <div style="margin-left: 20px; width: auto; height: 100%">
      <LegoLeftTree
        :treeData="state.treeDataAll"
        :loading="state.loadingAll"
        v-model:value="state.storeCodesAll"
        header-title="选择渠道"
        placeholder="搜索渠道名称或编码"
        select-type="multiple"
        width="300px"
        :default-is-show="true"
        @switch="onSwitch2"
        @change="(val) => onChange2(val)"
      >
        <template #title="{ value: val, title, label, searchValue }">
          <div>
            <span
              style="margin-right: 10px"
              :style="{ color: searchValue === LegoTreeUtils.getRealKey(val) ? 'red' : '#222222' }"
            >
              {{ title || label }}
            </span>
            <up-circle-outlined @click="onIconClick2(val)" />
          </div>
        </template>
      </LegoLeftTree>
    </div>

    <div style="margin-left: 20px; width: auto; height: 100%">
      <LegoLeftTree
        :treeData="state.treeDataAll"
        :loading="state.loadingAll"
        v-model:value="state.storeNodesAll"
        header-title="选择组织机构"
        select-type="multiple"
        include-area
        :virtual-height="600"
        :default-is-show="false"
        :keep-origin-node="true"
        @switch="onSwitch3"
        @change="(val) => onChange3(val)"
        @check-before="onCheckBeforeAll"
      />
    </div>

    <div :class="$style.right">我是右侧业务内容</div>
  </div>
</template>

<script setup lang="ts">
import { UpCircleOutlined, ExclamationCircleOutlined } from '@ant-design/icons-vue';
import zh_CN from 'ant-design-vue/es/locale/zh_CN';
import dayjs from 'dayjs';
import { ConfigProvider, Button, RadioGroup, Radio } from 'ant-design-vue';
import { reactive, onMounted, ref } from 'vue';
import { CheckInfo } from 'ant-design-vue/es/vc-tree/props';
import { LegoLeftTree, LegoTreeUtils, LegoTreeTypes, LegoTreeServices } from '@pluve/lego-tree-vue';
import 'dayjs/locale/zh-cn';
dayjs.locale('zh-cn');
import { originList } from '@/constant/channel';

const treeRef = ref();
const treeRefAll = ref();

const state = reactive<{
  dataType: 'all' | 'enable';
  treeData: LegoTreeTypes.ITreeNodesType[];
  treeDataAll: LegoTreeTypes.ITreeNodesType[];
  loading: boolean;
  loadingAll: boolean;
  storeCode?: string;
  storeCodeAll?: string;
  storeCodes: string[];
  storeCodesAll: string[];
  storeNodes: LegoTreeTypes.ICheckedAreaItem[];
  storeNodesAll: LegoTreeTypes.ICheckedAreaItem[];
}>({
  dataType: 'enable',
  treeData: [],
  treeDataAll: [],
  loading: false,
  loadingAll: false,
  storeCode: '5500',
  storeCodeAll: '10010357_5500',
  storeCodes: ['5500'],
  storeCodesAll: ['10010357_5500'],
  storeNodes: [{ key: '5500', hierarchy: '07' }],
  storeNodesAll: [{ key: '10010357_5500', code: '10010357', storeCode: '5500', hierarchy: '07' }],
});

const customRequest = async () => {
  try {
    const result: any[] = await new Promise((resolve) => {
      setTimeout(() => {
        resolve(originList);
      }, 800);
    });
    const convertedData = LegoTreeUtils.recursionTreeData({
      list: result ?? [],
      valKey: 'id',
      nameKey: 'text',
      childrenKey: 'children',
    });
    return convertedData;
  } catch (error) {
    return [];
  }
};

const onChange1 = (val: string) => {
  console.log('单选选择范围 val:', val);
};

const onChange2 = (val: string[]) => {
  console.log('多选选择渠道 val:', val);
};

const onChange3 = (val: LegoTreeTypes.ICheckedAreaItem[]) => {
  console.log('多选选择组织机构 val:', val);
};

const onSwitch2 = (bool: boolean) => {
  console.log('多选选择渠道,当前展开收起状态:', bool);
};

const onSwitch3 = (bool: boolean) => {
  console.log('多选选择组织机构,当前展开收起状态:', bool);
};

const onIconClick1 = (val: string) => {
  console.log('单选选择范围,点击图标后获取对应的 value:', val);
};

const onIconClick2 = (val: string) => {
  console.log('多选选择渠道,点击图标后获取对应的 value:', val);
};

const loadOrgTreeNodeByLevel = async () => {
  state.loading = true;
  try {
    const result = await LegoTreeServices.getAllOrgTreeNodesByOss({
      hierarchyList: ['01', '02', '03', '04', '06', '07'],
      envType: 'test',
    });
    const newTreeData = LegoTreeUtils.recursionTreeData({
      list: result?.[0].sub || [],
      pid: undefined,
      keepOriginNode: true,
    });
    LegoTreeUtils.excludeNode(newTreeData, '07');
    state.treeData = newTreeData;
  } catch (error) {
    state.treeData = [];
  } finally {
    state.loading = false;
  }
};

const loadOrgTreeNodeByLevelAll = async () => {
  state.loadingAll = true;
  try {
    const result = await LegoTreeServices.getAllOrgTreeNodesByOss({
      hierarchyList: ['01', '02', '03', '04', '06', '07'],
      envType: 'test',
      jsonFrom: LegoTreeTypes.OrgTreeJsonFromEnum.USER,
      jsonType: LegoTreeTypes.OrgTreeJsonTypeEnum.ALL,
    });
    const newTreeData = LegoTreeUtils.recursionTreeData({
      list: result?.[0].children || [],
      pid: undefined,
      childrenKey: 'children',
      hierarchyKey: 'rank',
      keepOriginNode: true,
      includeDisabledData: true,
    });
    LegoTreeUtils.excludeNode(newTreeData, '07');
    state.treeDataAll = newTreeData;
  } catch (error) {
    state.treeDataAll = [];
  } finally {
    state.loadingAll = false;
  }
};

onMounted(() => {
  loadOrgTreeNodeByLevel();
  loadOrgTreeNodeByLevelAll();
});

// 接口请求成功后返回对应的树数据
const getTreeInfo = ({
  treeData,
  dataListMap,
}: {
  treeData: LegoTreeTypes.ITreeNodesType[];
  dataListMap: Record<string, LegoTreeTypes.ITreeNodesType>;
}) => {
  console.log('getTreeInfo', { treeData, dataListMap });
};

// 接口请求成功后返回对应的树数据
const getTreeInfoAll = ({
  treeData,
  dataListMap,
}: {
  treeData: LegoTreeTypes.ITreeNodesType[];
  dataListMap: Record<string, LegoTreeTypes.ITreeNodesType>;
}) => {
  console.log('getTreeInfoAll', { treeData, dataListMap });
};

const getTreeInfoWithRef = () => {
  if (state.dataType === 'enable') {
    console.log('treeRef.value', treeRef.value?.getTreeInfo());
  } else {
    console.log('treeRefAll.value', treeRefAll.value?.getTreeInfo());
  }
};

const onCheckBefore = (
  _: any,
  e: CheckInfo,
  treeInfo: {
    treeData: LegoTreeTypes.ITreeNodesType[];
    dataListMap: Record<string, LegoTreeTypes.ITreeNodesType>;
  }
) => {
  console.log('treeInfo', treeInfo);
};

const onCheckBeforeAll = (
  _: any,
  e: CheckInfo,
  treeInfo: {
    treeData: LegoTreeTypes.ITreeNodesType[];
    dataListMap: Record<string, LegoTreeTypes.ITreeNodesType>;
  }
) => {
  console.log('treeInfoAll', treeInfo);
};
</script>

<style lang="less" module>
body {
  background-color: #f1f4f5;
  width: 100%;
  height: 100%;
}

.top {
  position: relative;
  padding: 10px 50px 0;
}

.wrap {
  position: relative;
  min-height: 52px;
  background-color: #f1f4f5;
  padding: 10px 50px;
  border-radius: 4px;
  height: calc(100vh - 120px);
  display: flex;
  align-items: center;
  justify-content: flex-start;
}

.right {
  width: 300px;
  height: 100%;
  background: #fff;
  margin-left: 20px;
  display: flex;
  align-items: center;
  justify-content: center;
}
</style>

LegoTreeTransfer

通常作为最底层组件向外提供,可以嵌在 tab 内、Modal 弹窗内等任何场景,左右侧布局展示。props 继承自 antd vue TreeProps

export interface ILegoTreeTransferProps
  extends Omit<
    TreeProps,
    | 'blockNode'
    | 'fieldNames'
    | 'multiple'
    | 'treeData'
    | 'virtual'
    | 'checkable'
    | 'selectable'
    | 'checkedKeys'
    | 'selectedKeys'
    | 'onSelect'
    | 'autoExpandParent'
    | 'expandedKeys'
    | 'height'
  > {
  envType?: 'test' | 'prod';
  jsonFrom?: OrgTreeJsonFromEnum;
  jsonType?: OrgTreeJsonTypeEnum;
  hierarchyList?: string[];
  selectHierarchy?: string;

  treeData?: LegoTreeTypes.ITreeNodesType[];
  loading?: boolean;
  dataListMap?: Record<string, LegoTreeTypes.ITreeNodesType>;
  request?: () => Promise<LegoTreeTypes.ITreeNodesType[]>;

  value: string[] | LegoTreeTypes.ICheckedAreaItem[];
  placeholder?: string;
  disabled?: boolean;
  includeArea?: boolean;
  searchDomHeight?: number;
  customFormatName?: (
    value: (LegoTreeTypes.ICheckedAreaItem | string)[],
    type: 'input' | 'modal'
  ) => string | undefined;

  keepOriginNode?: boolean;
}

API

参数 说明 类型 是否可选 默认值
envType 环境(测试 | 生产)。走组件默认接口请求时使用,其余场景无需传该字段 test | prod test
jsonFrom 组织机构树 json 来源。走组件默认接口请求时使用,其余场景无需传该字段 LegoTreeTypes.OrgTreeJsonFromEnum LegoTreeTypes.OrgTreeJsonFromEnum.SSO
jsonType 组织机构树 json 类型:全量(包含已关停等) or 有效的。走组件默认接口请求时使用,其余场景无需传该字段 LegoTreeTypes.OrgTreeJsonTypeEnum LegoTreeTypes.OrgTreeJsonTypeEnum.ENABLE
hierarchyList 组织机构级别数组,01-07,用于过滤不包含的节点信息,没传或为空则不过滤。走组件默认接口请求时使用,其余场景无需传该字段 string[] -
selectHierarchy 选择的组织机构级别,01-07,用于过滤子节点不包含 selectHierarchy 层级的节点。走组件默认接口请求时使用,其余场景无需传该字段 string -
treeData antd vue TreeSelect 组件数据源(已经经过转化后的数据源,若 includeAreatrue,则子节点需要包含 trace 字段) LegoTreeTypes.ITreeNodesType[] -
loading 外部数据源加载时的 loading 状态 boolean false
dataListMap 扁平化 map(已经经过转化后的数据源生成的 map) Record<string, LegoTreeTypes.ITreeNodesType> {}
request 自定义服务,用于获取组件树数据源 () => Promise<LegoTreeTypes.ITreeNodesType[]> -
value(v-model) 当前选中值,区分选择父子节点和子节点,有 2 种类型 string[] | LegoTreeTypes.ICheckedAreaItem[] []
placeholder Input.Search 输入框的 placeholder string '搜索关键词'
disabled tree 选择是否禁用 boolean false
includeArea 是否包含区域选择,即是否允许选择各层级父子节点 boolean false
searchDomHeight 实际业务中搜索输入框的高度,不同的样式主题高度会有差别 number 46
customFormatName 自定义选中项内容展示,包含 input 输入框的回显和 Tree 选中后的汇总 (value: (LegoTreeTypes.ICheckedAreaItem | string)[], type: 'input' | 'modal') => string | undefined -
keepOriginNode 是否树节点保留原始对象信息。若开启,配合 includeAreatrue,则会返回 originNode(原始对象信息) boolean false
title 自定义标题,在原始基础上,增加当前搜索值 searchValue 字段,供自定义判断处理 slot({ key, title, ..., searchValue }) -

事件(emit)

事件名称 说明 回调参数
change 选中/反选树节点时调用此函数 function(string[] | LegoTreeTypes.ICheckedAreaItem[])
checkBefore Tree onCheck 触发时优先调用此函数,此函数第三个参数返回 组件内部的树数据源 function(checked: Key[] | { checked: Key[]; halfChecked: Key[]; }, info: CheckInfo, treeInfo: { treeData: LegoTreeTypes.ITreeNodesType[]; dataListMap: Record<string, LegoTreeTypes.ITreeNodesType> })
getTreeInfo 接口请求完成后,调用此函数,返回当前组件内部的树数据源 function({ treeData: LegoTreeTypes.ITreeNodesType[]; dataListMap: Record<string, LegoTreeTypes.ITreeNodesType>; })

ref

通过 ref 获取组件提供的方法或属性。以下是提供的方法或属性:

方法/属性名称 说明 类型
getTreeInfo 【方法】提供组件内部的树数据源 () => ({ treeData: LegoTreeTypes.ITreeNodesType[]; dataListMap: Record<string, LegoTreeTypes.ITreeNodesType>; })

效果展示

测试环境地址:https://yf-test-oss.yifengx.com/webtest/yangwen/legoTree/index.html#/treeTransfer

使用示例

<template>
  <ConfigProvider :locale="zh_CN">
    <div :class="$style.wrap">
      <!-- 有效数据源 -->
      <h4
        style="margin: 30px 50px 0 0; word-break: break-word; max-height: 100px; overflow-y: auto"
      >
        LegoTreeTransfer demo:{{ JSON.stringify(state.storeCodes) }}
      </h4>
      <div style="width: 536px; margin: 30px 0 0">
        <LegoTreeTransfer
          :treeData="state.treeData"
          :loading="state.loading"
          :include-area="true"
          :keep-origin-node="true"
          v-model:value="state.storeCodes"
        />
      </div>
      <h4
        style="margin: 30px 50px 0 0; word-break: break-word; max-height: 100px; overflow-y: auto"
      >
        LegoTreeTransfer demo:{{ JSON.stringify(state.storeCodes1) }}
      </h4>
      <Button type="primary" @click="getTreeInfoWithRef" style="margin-top: 10px">
        点我获取tree数据源
      </Button>
      <div style="width: 536px; margin: 30px 0 0">
        <LegoTreeTransfer
          :include-area="false"
          v-model:value="state.storeCodes1"
          :custom-format-name="customFormatName"
          :request="customRequest"
          ref="treeRef"
          @get-tree-info="getTreeInfo"
          @check-before="onCheckBefore"
        />
      </div>

      <!-- 账号中心 && 全部数据源(包含已关停) -->
      <div style="font-weight: bold; color: red; margin-top: 10px; margin-bottom: 10px">
        以下数据源为账号中心全量数据源(包含已关停数据):
      </div>
      <h4
        style="margin: 30px 50px 0 0; word-break: break-word; max-height: 100px; overflow-y: auto"
      >
        LegoTreeTransfer demo:{{ JSON.stringify(state.storeCodesAll) }}
      </h4>
      <div style="width: 536px; margin: 30px 0 0">
        <LegoTreeTransfer
          :treeData="state.treeDataAll"
          :loading="state.loadingAll"
          :include-area="true"
          :keep-origin-node="true"
          v-model:value="state.storeCodesAll"
        />
      </div>
      <h4
        style="margin: 30px 50px 0 0; word-break: break-word; max-height: 100px; overflow-y: auto"
      >
        LegoTreeTransfer demo:{{ JSON.stringify(state.storeCodes1All) }}
      </h4>
      <Button type="primary" @click="getTreeInfoWithRefAll" style="margin-top: 10px">
        点我获取tree数据源
      </Button>
      <div style="width: 536px; margin: 30px 0 0">
        <LegoTreeTransfer
          :json-from="LegoTreeTypes.OrgTreeJsonFromEnum.USER"
          :json-type="LegoTreeTypes.OrgTreeJsonTypeEnum.ALL"
          env-type="test"
          :hierarchyList="['01', '02', '03', '04', '06', '07']"
          selectHierarchy="07"
          :include-area="false"
          v-model:value="state.storeCodes1All"
          ref="treeRefAll"
          @get-tree-info="getTreeInfoAll"
          @check-before="onCheckBeforeAll"
        />
      </div>
    </div>
  </ConfigProvider>
</template>

<script setup lang="ts">
import zh_CN from 'ant-design-vue/es/locale/zh_CN';
import dayjs from 'dayjs';
import { ConfigProvider, Button } from 'ant-design-vue';
import { reactive, onMounted, ref } from 'vue';
import { CheckInfo } from 'ant-design-vue/es/vc-tree/props';
import { LegoTreeTransfer, LegoTreeUtils, LegoTreeTypes, LegoTreeServices } from '@pluve/lego-tree-vue';
import 'dayjs/locale/zh-cn';
dayjs.locale('zh-cn');
import { originList } from '@/constant/channel';

const treeRef = ref();
const treeRefAll = ref();

const state = reactive<{
  treeData: LegoTreeTypes.ITreeNodesType[];
  treeDataAll: LegoTreeTypes.ITreeNodesType[];
  loading: boolean;
  loadingAll: boolean;
  storeCodes: LegoTreeTypes.ICheckedAreaItem[];
  storeCodesAll: LegoTreeTypes.ICheckedAreaItem[];
  storeCodes1: string[];
  storeCodes1All: string[];
}>({
  treeData: [],
  treeDataAll: [],
  loading: false,
  loadingAll: false,
  storeCodes: [],
  storeCodesAll: [],
  storeCodes1: [],
  storeCodes1All: [],
});

const loadOrgTreeNodeByLevel = async () => {
  state.loading = true;
  try {
    const result = await LegoTreeServices.getAllOrgTreeNodesByOss({
      hierarchyList: ['01', '02', '03', '04', '06', '07'],
      envType: 'test',
    });
    const newTreeData = LegoTreeUtils.recursionTreeData({
      list: result?.[0].sub || [],
      pid: undefined,
      keepOriginNode: true,
    });
    LegoTreeUtils.excludeNode(newTreeData, '07');
    state.treeData = newTreeData;
  } catch (error) {
    state.treeData = [];
  } finally {
    state.loading = false;
  }
};

const loadOrgTreeNodeByLevelAll = async () => {
  state.loadingAll = true;
  try {
    const result = await LegoTreeServices.getAllOrgTreeNodesByOss({
      hierarchyList: ['01', '02', '03', '04', '06', '07'],
      envType: 'test',
      jsonFrom: LegoTreeTypes.OrgTreeJsonFromEnum.USER,
      jsonType: LegoTreeTypes.OrgTreeJsonTypeEnum.ALL,
    });
    const newTreeData = LegoTreeUtils.recursionTreeData({
      list: result?.[0].children || [],
      pid: undefined,
      childrenKey: 'children',
      hierarchyKey: 'rank',
      keepOriginNode: true,
      includeDisabledData: true,
    });
    LegoTreeUtils.excludeNode(newTreeData, '07');
    state.treeDataAll = newTreeData;
  } catch (error) {
    state.treeDataAll = [];
  } finally {
    state.loadingAll = false;
  }
};

onMounted(() => {
  loadOrgTreeNodeByLevel();
  loadOrgTreeNodeByLevelAll();
});

const customRequest = async () => {
  try {
    const result: any[] = await new Promise((resolve) => {
      setTimeout(() => {
        resolve(originList);
      }, 800);
    });
    const convertedData = LegoTreeUtils.recursionTreeData({
      list: result ?? [],
      valKey: 'id',
      nameKey: 'text',
      childrenKey: 'children',
    });
    return convertedData;
  } catch (error) {
    return [];
  }
};

const customFormatName = (
  values: (LegoTreeTypes.ICheckedAreaItem | string)[],
  type: 'input' | 'modal'
) => {
  const checkedStr = (values as LegoTreeTypes.ICheckedAreaItem[])
    .map((item) => item.title)
    .join('');

  if (type === 'input') {
    return checkedStr ? `${checkedStr}被选择` : undefined;
  }

  return checkedStr ? `已选:${checkedStr}` : '已选:0条数据';
};

// 接口请求成功后返回对应的树数据
const getTreeInfo = ({
  treeData,
  dataListMap,
}: {
  treeData: LegoTreeTypes.ITreeNodesType[];
  dataListMap: Record<string, LegoTreeTypes.ITreeNodesType>;
}) => {
  console.log('getTreeInfo', { treeData, dataListMap });
};

// 接口请求成功后返回对应的树数据
const getTreeInfoAll = ({
  treeData,
  dataListMap,
}: {
  treeData: LegoTreeTypes.ITreeNodesType[];
  dataListMap: Record<string, LegoTreeTypes.ITreeNodesType>;
}) => {
  console.log('getTreeInfoAll', { treeData, dataListMap });
};

const onCheckBefore = (
  _: any,
  e: CheckInfo,
  treeInfo: {
    treeData: LegoTreeTypes.ITreeNodesType[];
    dataListMap: Record<string, LegoTreeTypes.ITreeNodesType>;
  }
) => {
  console.log('treeInfo', treeInfo);
};

const onCheckBeforeAll = (
  _: any,
  e: CheckInfo,
  treeInfo: {
    treeData: LegoTreeTypes.ITreeNodesType[];
    dataListMap: Record<string, LegoTreeTypes.ITreeNodesType>;
  }
) => {
  console.log('treeInfoAll', treeInfo);
};

const getTreeInfoWithRef = () => {
  console.log('treeRef.value', treeRef.value?.getTreeInfo());
};

const getTreeInfoWithRefAll = () => {
  console.log('treeRefAll.value', treeRefAll.value?.getTreeInfo());
};
</script>

<style lang="less" module>
.wrap {
  position: relative;
  min-height: 52px;
  background-color: #fff;
  padding: 0 80px 40px;
  border-radius: 4px;
}
</style>

LegoTreeModal

通常作为 选择组织机构树的弹窗使用,Modal 内部引用 LegoTreeTransfer 组件来实现。故部分属性可以参考 LegoTreeTransfer 组件。

export interface ILegoTreeModalProps {
  title?: string;
  visible?: boolean;
  value: string[] | LegoTreeTypes.ICheckedAreaItem[];
  okLoading?: boolean;
  onOk?: (value: string[] | LegoTreeTypes.ICheckedAreaItem[]) => void;
  onCancel?: () => void;
  disabled?: boolean;
  showStoreUploadBtn?: boolean;
  modalProps?: Omit<
    ModalProps,
    'onOk' | 'onCancel' | 'visible' | 'destroyOnClose' | 'closable' | 'keyboard' | 'maskClosable'
  >;
  transferProps?: Omit<ILegoTreeTransferProps, 'value' | 'disabled'>;
}

API

参数 说明 类型 是否可选 默认值
title Modal 弹窗的 title,优先级:title[slot] > modalProps.title > title[string] string | slot 选择区域
visible 控制 Modal 弹窗的显隐 boolean false
value(v-model) 当前选中值,区分选择父子节点和子节点,有 2 种类型 string[] | LegoTreeTypes.ICheckedAreaItem[] []
okLoading 点击弹窗确定按钮的 loading boolean false
onOk 点击弹窗确定按钮触发 (value: string[] | LegoTreeTypes.ICheckedAreaItem[]) => void -
onCancel 点击弹窗取消按钮触发 () => void -
disabled Modal 弹窗 和 tree 选择是否禁用 boolean false
showStoreUploadBtn 是否展示默认的导入门店按钮,导入任意节点,做全量覆盖处理。
注意:
1. 如果走全量数据源(包含已关停数据),则不能使用默认的导入功能;
2. 如果开启 includeArea,则不区分导入的数据之间是否存在父子层级依赖关系,一并去重导入;
boolean false
modalProps antd vue Modal 弹窗组件的额外的属性 Omit<ModalProps, 'onOk' | 'onCancel' | 'visible' | 'destroyOnClose' | 'closable' | 'keyboard' | 'maskClosable'> -
transferProps LegoTreeTransfer 组件的额外的属性 Omit<ILegoTreeTransferProps, 'value' | 'disabled'> {}
footer Modal 弹窗的 footer,优先级最高 slot -
renderFooterLeft Modal 弹窗底部左侧的自定义区域 slot -
renderUpload Modal 弹窗底部右侧的自定义导入功能,disabledfalse 时展示,优先级高于默认的导入功能 slot -
bodyTop Modal 弹窗 LegoTreeTransfer 组件上方的自定义区域 slot -
bodyBottom Modal 弹窗 LegoTreeTransfer 组件下方的自定义区域 slot -
treeTitle 用于传递给 LegoTreeTransfer 组件的 title 插槽(避免插槽名字冲突) slot -

事件(emit)

事件名称 说明 回调参数
getTreeInfo 接口请求完成后,调用此函数,返回当前组件内部的树数据源 function({ treeData: LegoTreeTypes.ITreeNodesType[]; dataListMap: Record<string, LegoTreeTypes.ITreeNodesType>; })

ref

通过 ref 获取组件提供的方法或属性。以下是提供的方法或属性:

方法/属性名称 说明 类型
getTreeInfo 【方法】提供组件内部的树数据源 () => ({ treeData: LegoTreeTypes.ITreeNodesType[]; dataListMap: Record<string, LegoTreeTypes.ITreeNodesType>; })

效果展示

测试环境地址:https://yf-test-oss.yifengx.com/webtest/yangwen/legoTree/index.html#/treeModal

使用示例

<template>
  <ConfigProvider :locale="zh_CN">
    <div :class="$style.wrap">
      <!-- 有效数据源 -->
      <h4
        style="margin: 30px 50px 0 0; word-break: break-word; max-height: 100px; overflow-y: auto"
      >
        LegoTreeModal demo:{{ JSON.stringify(state.storeCodes) }}
      </h4>
      <Button style="margin-top: 20px" type="primary" @click="showTreeModal">
        点我打开组织机构树弹窗
      </Button>
      <LegoTreeModal
        :visible="state.treeModalVisible"
        v-model:value="state.storeCodes"
        @cancel="
          () => {
            state.treeModalVisible = false;
          }
        "
        @ok="
          (value) => {
            state.treeModalVisible = false;
          }
        "
        :showStoreUploadBtn="true"
        :transfer-props="{
          hierarchyList: ['01', '02', '03', '04', '06', '07'],
          selectHierarchy: '07',
          includeArea: false,
          onCheck: onTreeCheck,
        }"
        ref="treeRef"
        @get-tree-info="getTreeInfo"
      >
        <template #title>我是 LegoTreeModal 弹窗组件的自定义 title</template>
        <template #bodyTop>我是 LegoTreeModal 弹窗组件的自定义 bodyTop</template>
        <template #bodyBottom>我是 LegoTreeModal 弹窗组件的自定义 bodyBottom</template>
        <template #renderFooterLeft>我是 LegoTreeModal 弹窗组件的自定义 renderFooterLeft</template>
      </LegoTreeModal>

      <!-- 账号中心 && 全部数据源(包含已关停) -->
      <div style="font-weight: bold; color: red; margin-top: 40px; margin-bottom: 0px">
        以下数据源为账号中心全量数据源(包含已关停数据):
      </div>
      <h4
        style="margin: 30px 50px 0 0; word-break: break-word; max-height: 100px; overflow-y: auto"
      >
        LegoTreeModal demo:{{ JSON.stringify(state.storeCodesAll) }}
      </h4>
      <Button style="margin-top: 20px" type="primary" @click="showTreeModalAll">
        点我打开组织机构树弹窗
      </Button>
      <LegoTreeModal
        :visible="state.treeModalVisibleAll"
        v-model:value="state.storeCodesAll"
        @cancel="
          () => {
            state.treeModalVisibleAll = false;
          }
        "
        @ok="
          (value) => {
            state.treeModalVisibleAll = false;
          }
        "
        :showStoreUploadBtn="false"
        :transfer-props="{
          jsonFrom: LegoTreeTypes.OrgTreeJsonFromEnum.USER,
          jsonType: LegoTreeTypes.OrgTreeJsonTypeEnum.ALL,
          hierarchyList: ['01', '02', '03', '04', '06', '07'],
          selectHierarchy: '07',
          includeArea: false,
          onCheck: onTreeCheckAll,
        }"
        ref="treeRefAll"
        @get-tree-info="getTreeInfoAll"
      >
        <template #title>我是 LegoTreeModal 弹窗组件的自定义 title</template>
        <template #bodyTop>我是 LegoTreeModal 弹窗组件的自定义 bodyTop</template>
        <template #bodyBottom>我是 LegoTreeModal 弹窗组件的自定义 bodyBottom</template>
        <template #renderFooterLeft>我是 LegoTreeModal 弹窗组件的自定义 renderFooterLeft</template>
      </LegoTreeModal>
    </div>
  </ConfigProvider>
</template>

<script setup lang="ts">
import zh_CN from 'ant-design-vue/es/locale/zh_CN';
import dayjs from 'dayjs';
import { ConfigProvider, Button } from 'ant-design-vue';
import { reactive, ref } from 'vue';
import { LegoTreeModal, LegoTreeTypes, LegoTreeUtils, LegoTreeServices } from '@pluve/lego-tree-vue';
import 'dayjs/locale/zh-cn';
dayjs.locale('zh-cn');

const treeRef = ref();
const treeRefAll = ref();

const state = reactive<{
  storeCodes: string[];
  storeCodesAll: string[];
  treeModalVisible: boolean;
  treeModalVisibleAll: boolean;
}>({
  storeCodes: [],
  storeCodesAll: [],
  treeModalVisible: false,
  treeModalVisibleAll: false,
});

const showTreeModal = () => {
  state.treeModalVisible = true;
};

const showTreeModalAll = () => {
  state.treeModalVisibleAll = true;
};

// 接口请求成功后返回对应的树数据
const getTreeInfo = ({
  treeData,
  dataListMap,
}: {
  treeData: LegoTreeTypes.ITreeNodesType[];
  dataListMap: Record<string, LegoTreeTypes.ITreeNodesType>;
}) => {
  console.log('getTreeInfo', { treeData, dataListMap });
};

// 接口请求成功后返回对应的树数据
const getTreeInfoAll = ({
  treeData,
  dataListMap,
}: {
  treeData: LegoTreeTypes.ITreeNodesType[];
  dataListMap: Record<string, LegoTreeTypes.ITreeNodesType>;
}) => {
  console.log('getTreeInfoAll', { treeData, dataListMap });
};

const onTreeCheck = () => {
  console.log('treeRef.value', treeRef.value?.getTreeInfo());
};

const onTreeCheckAll = () => {
  console.log('treeRefAll.value', treeRefAll.value?.getTreeInfo());
};
</script>

<style lang="less" module>
.wrap {
  position: relative;
  min-height: 52px;
  background-color: #fff;
  padding: 20px 80px;
  border-radius: 4px;
}
</style>

LegoTree

通常用于多选组织架构,与 FormItem 进行绑定,内部引用 LegoTreeModal 组件来实现。故部分属性可以参考 LegoTreeModal 组件。

export interface ILegoTreeProps
  extends Omit<
    ILegoTreeModalProps,
    'title' | 'visible' | 'value' | 'onOk' | 'onCancel' | 'disabled'
  > {
  title?: string;
  value: string[] | LegoTreeTypes.ICheckedAreaItem[];
  disabled?: boolean;
  placeholder?: string;
  limitSelectedNum?: number;
  onlyStoreCodeInDisabled?: boolean;
  showModalInDisabled?: boolean;
  customValidate?: (value: string[] | LegoTreeTypes.ICheckedAreaItem[]) => Promise<boolean>;
  customFormatName?: (
    value: (LegoTreeTypes.ICheckedAreaItem | string)[],
    type: 'input' | 'modal'
  ) => string | undefined;
}

API

参数 说明 类型 是否可选 默认值
title Modal 弹窗的 title string | slot 选择区域
value(v-model) 当前选中值,区分选择父子节点和子节点,有 2 种类型 string[] | LegoTreeTypes.ICheckedAreaItem[] []
disabled InputModal 弹窗 和 tree 选择是否禁用 boolean false
placeholder Input 输入框的 placeholder string '请选择区域'
limitSelectedNum 限制最大门店勾选数量,仅限 transferProps.includeArea 为 false 时 number -
onlyStoreCodeInDisabled 禁用时,是否只展示门店编码,仅限输入框中展示用 boolean false
showModalInDisabled 禁用时,是否可以展示弹窗 boolean true
customValidate 点击弹窗确定按钮,触发自定义校验,优先级:customValidate > limitSelectedNum (value: string[] | ICheckedAreaItem[]) => Promise<boolean> -
customFormatName 自定义选中项内容展示,包含 input 输入框的回显和 Tree 选中后的汇总 (value: (LegoTreeTypes.ICheckedAreaItem | string)[], type: 'input' | 'modal') => string | undefined -
default 默认插槽,用于自定义展示形式,默认为 Input slot({ show }) -

事件(emit)

事件名称 说明 回调参数
change 弹窗选中/反选树节点后,点击【确定】按钮时调用此函数 function(string[] | LegoTreeTypes.ICheckedAreaItem[])

效果展示

测试环境地址:https://yf-test-oss.yifengx.com/webtest/yangwen/legoTree/index.html#/tree

使用示例

<template>
  <ConfigProvider :locale="zh_CN">
    <div :class="$style.wrap">
      <Form :colon="false" autocomplete="off">
        <Row>
          <!-- 有效数据源 -->
          <Col :span="12">
            <FormItem label="Tree自定义request">
              <LegoTree
                title="选择渠道"
                placeholder="请选择"
                v-model:value="modelRef.storeCodes"
                :showStoreUploadBtn="true"
                :transfer-props="{
                  request: customRequest,
                  includeArea: false,
                }"
                :custom-format-name="customFormatName"
                :disabled="true"
                :show-modal-in-disabled="true"
              ></LegoTree>
            </FormItem>
          </Col>
          <Col :span="12"></Col>
          <Col :span="12">
            <FormItem label="Tree使用默认json">
              <LegoTree
                title="选择门店"
                placeholder="选择门店"
                v-model:value="modelRef.channelList"
                :limit-selected-num="100"
                :showStoreUploadBtn="true"
                :transfer-props="{
                  jsonFrom: LegoTreeTypes.OrgTreeJsonFromEnum.USER,
                  jsonType: LegoTreeTypes.OrgTreeJsonTypeEnum.ENABLE,
                  hierarchyList: ['01', '02', '03', '04', '06', '07'],
                  selectHierarchy: '07',
                  includeArea: true,
                  keepOriginNode: true,
                  onCheckBefore,
                }"
                :custom-validate="customValidate"
                :disabled="false"
                :show-modal-in-disabled="true"
                @get-tree-info="getTreeInfo"
              >
                <template #treeTitle="{ key: val, title, label }">
                  <b v-if="val === '5024'" style="color: #08c">{{ title || label }}</b>
                  <template v-else>{{ title || label }}</template>
                </template>
              </LegoTree>
            </FormItem>
          </Col>
          <Col :span="12"></Col>

          <!-- 账号中心 && 全部数据源(包含已关停) -->
          <Col :span="24" style="font-weight: bold; color: red; margin-bottom: 24px">
            以下数据源为账号中心全量数据源(包含已关停数据):
          </Col>
          <Col :span="12">
            <FormItem label="Tree自定义treeData">
              <LegoTree
                title="选择渠道"
                placeholder="请选择"
                v-model:value="modelRef.storeCodesAll"
                :limit-selected-num="100"
                :showStoreUploadBtn="false"
                :transfer-props="{
                  treeData: state.treeDataAll,
                  loading: state.loadingAll,
                  includeArea: false,
                  keepOriginNode: true,
                  onCheckBefore: onCheckBeforeAll,
                }"
                :custom-validate="customValidate"
                :disabled="false"
                @get-tree-info="getTreeInfoAll"
              >
                <template #treeTitle="{ key: val, title, label, storeCode }">
                  <b v-if="val === '10004575_5024'" style="color: #08c">{{ title || label }}</b>
                  <b v-else-if="storeCode === '5500'" style="color: #f90">{{ title || label }}</b>
                  <template v-else>{{ title || label }}</template>
                </template>
              </LegoTree>
            </FormItem>
          </Col>
          <Col :span="12"></Col>
          <Col :span="12">
            <FormItem label="Tree使用默认json">
              <LegoTree
                title="选择门店"
                placeholder="选择门店"
                v-model:value="modelRef.channelListAll"
                :showStoreUploadBtn="false"
                :transfer-props="{
                  jsonFrom: LegoTreeTypes.OrgTreeJsonFromEnum.USER,
                  jsonType: LegoTreeTypes.OrgTreeJsonTypeEnum.ALL,
                  hierarchyList: ['01', '02', '03', '04', '06', '07'],
                  selectHierarchy: '07',
                  includeArea: true,
                  keepOriginNode: true,
                }"
                :disabled="true"
                :show-modal-in-disabled="true"
                :only-store-code-in-disabled="true"
              ></LegoTree>
            </FormItem>
          </Col>
          <Col :span="12"></Col>

          <Col :span="6">
            <Button @click="onQuery" style="margin-left: 10px" type="primary">查询</Button>
          </Col>
        </Row>
      </Form>
    </div>
  </ConfigProvider>
</template>

<script setup lang="ts">
import zh_CN from 'ant-design-vue/es/locale/zh_CN';
import dayjs from 'dayjs';
import { ConfigProvider, Form, FormItem, Row, Col, Button } from 'ant-design-vue';
import { reactive, onMounted } from 'vue';
import { CheckInfo } from 'ant-design-vue/es/vc-tree/props';
import { LegoTree, LegoTreeUtils, LegoTreeTypes, LegoTreeServices } from '@pluve/lego-tree-vue';
import 'dayjs/locale/zh-cn';
dayjs.locale('zh-cn');
import { originList } from '@/constant/channel';

// 查询条件 ref
const modelRef = reactive<{
  storeCodes: string[];
  storeCodesAll: string[];
  channelList: LegoTreeTypes.ICheckedAreaItem[];
  channelListAll: LegoTreeTypes.ICheckedAreaItem[];
}>({
  storeCodes: ['5024', '5032', '5979', 'E520'],
  storeCodesAll: [],
  channelList: [],
  channelListAll: [
    {
      key: '10014212_7971',
      code: '10014212',
      storeCode: '7971',
      hierarchy: '07',
      title: '7971-南昌怡园路店',
    },
  ],
});

const state = reactive<{
  treeDataAll: LegoTreeTypes.ITreeNodesType[];
  loadingAll: boolean;
}>({
  treeDataAll: [],
  loadingAll: false,
});

// 查询条件 form
const form = Form.useForm(modelRef, undefined);

const customValidate = async (e: string[] | LegoTreeTypes.ICheckedAreaItem[]) => {
  await new Promise((resolve) => {
    setTimeout(() => {
      console.log('1秒的自定义校验');
      resolve(true);
    }, 1000);
  });
  return true;
};

const customRequest = async () => {
  try {
    const result: any[] = await new Promise((resolve) => {
      setTimeout(() => {
        resolve(originList);
      }, 800);
    });
    const convertedData = LegoTreeUtils.recursionTreeData({
      list: result ?? [],
      valKey: 'id',
      nameKey: 'text',
      childrenKey: 'children',
    });
    return convertedData;
  } catch (error) {
    return [];
  }
};

const customFormatName = (
  values: (LegoTreeTypes.ICheckedAreaItem | string)[],
  type: 'input' | 'modal'
) => {
  if (type === 'input') {
    const checkedStr = (values as string[]).join('');
    return checkedStr ? `${checkedStr}被选择` : undefined;
  }

  const checkedStr = (values as LegoTreeTypes.ICheckedAreaItem[])
    .map((item) => item.title)
    .join('');
  return checkedStr ? `已选:${checkedStr}` : '已选:0条数据';
};

const loadOrgTreeNodeByLevelAll = async () => {
  state.loadingAll = true;
  try {
    const result = await LegoTreeServices.getAllOrgTreeNodesByOss({
      hierarchyList: ['01', '02', '03', '04', '06', '07'],
      envType: 'test',
      jsonFrom: LegoTreeTypes.OrgTreeJsonFromEnum.USER,
      jsonType: LegoTreeTypes.OrgTreeJsonTypeEnum.ALL,
    });
    const newTreeData = LegoTreeUtils.recursionTreeData({
      list: result?.[0].children || [],
      pid: undefined,
      childrenKey: 'children',
      hierarchyKey: 'rank',
      keepOriginNode: true,
      includeDisabledData: true,
    });
    LegoTreeUtils.excludeNode(newTreeData, '07');
    state.treeDataAll = newTreeData;
  } catch (error) {
    state.treeDataAll = [];
  } finally {
    state.loadingAll = false;
  }
};

onMounted(() => {
  loadOrgTreeNodeByLevelAll();
});

const onQuery = () => {
  console.log('查询条件:', modelRef);
};

// 接口请求成功后返回对应的树数据
const getTreeInfo = ({
  treeData,
  dataListMap,
}: {
  treeData: LegoTreeTypes.ITreeNodesType[];
  dataListMap: Record<string, LegoTreeTypes.ITreeNodesType>;
}) => {
  console.log('getTreeInfo', { treeData, dataListMap });
};

// 接口请求成功后返回对应的树数据
const getTreeInfoAll = ({
  treeData,
  dataListMap,
}: {
  treeData: LegoTreeTypes.ITreeNodesType[];
  dataListMap: Record<string, LegoTreeTypes.ITreeNodesType>;
}) => {
  console.log('getTreeInfoAll', { treeData, dataListMap });
};

const onCheckBefore = (
  _: any,
  e: CheckInfo,
  treeInfo: {
    treeData: LegoTreeTypes.ITreeNodesType[];
    dataListMap: Record<string, LegoTreeTypes.ITreeNodesType>;
  }
) => {
  console.log('treeInfo', treeInfo);
};

const onCheckBeforeAll = (
  _: any,
  e: CheckInfo,
  treeInfo: {
    treeData: LegoTreeTypes.ITreeNodesType[];
    dataListMap: Record<string, LegoTreeTypes.ITreeNodesType>;
  }
) => {
  console.log('treeInfoAll', treeInfo);
};
</script>

<style lang="less" module>
.wrap {
  position: relative;
  min-height: 52px;
  background-color: #fff;
  margin: 20px 0 0 100px;
  border-radius: 4px;
}
</style>

LegoTreeUtils

提供一系列 Tree 相关方法

HIERARCHY_LIST

提供 hierarchy 的枚举数组,枚举值:LegoTreeTypes.CommonHierarchyEnum

示例

import { LegoTreeUtils } from '@pluve/lego-tree-vue';

const aaa = LegoTreeUtils.HIERARCHY_LIST;

excludeNode

过滤树底层没有 primaryKey 的节点

Params

excludeNode(list: LegoTreeTypes.ITreeNodesType[], primaryKey?: string)

参数 说明 类型 是否可选 默认值
list Tree/TreeSelect 组件的 treeData 数据源 LegoTreeTypes.ITreeNodesType[] -
primaryKey 过滤依据 string -

示例

import { LegoTreeUtils } from '@pluve/lego-tree-vue';

const newTreeData = []; 
LegoTreeUtils.excludeNode(newTreeData, '07');

recursionTreeData

将接口返回的数据结构转化为 Tree/TreeSelect 组件需要的数据结构

Params

recursionTreeData({ list, valKey = 'code', nameKey = 'name', childrenKey = 'sub', hierarchyKey = 'hierarchy', pid, enableSelectedHierarchy = [], hierarchyIndex = 1, keepOriginNode = false, includeDisabledData = false })

参数 说明 类型 是否可选 默认值
list 接口返回的原始数据 LegoTreeTypes.IOriginTreeNode[] -
valKey 需要转化为 key 的原始字段名称 string 'code'
nameKey 需要转化为 title 的原始字段名称 string 'name'
childrenKey 需要转化为 children 的原始字段名称 string 'sub'
hierarchyKey 需要转化为 hierarchy 的原始字段名称 string 'hierarchy'
pid 上一节点的 'code' 字段,此处 'code' 取 valKey 值 string -
enableSelectedHierarchy 【单选】可选择的组织机构级别,不传或为空则都可以选择,传了则包含的级别才可以选择,否则 selectable 为 false 不可选择 string[] []
hierarchyIndex 若接口未返回 hierarchy 字段,则默认从 1 开始赋值 number 1
keepOriginNode includeAreatruelabelInValuetrue 时,若开启 keepOriginNode,则 返回的值里面会增加 originNode 字段。 boolean false
includeDisabledData 走默认数据源时,是否包含已关停的门店数据 boolean false

示例

import { LegoTreeUtils } from '@pluve/lego-tree-vue';

const newTreeData = LegoTreeUtils.recursionTreeData({
  list: [],
  valKey: 'id',
  nameKey: 'text',
  childrenKey: 'children',
  hierarchyKey: 'rank',
  keepOriginNode: true,
  includeDisabledData: false,
});

filterTreeNodesByHierarchyList

过滤接口返回的源数据中 hierarchyList 中不包含 hierarchy 的节点

Params

filterTreeNodesByHierarchyList(list, hierarchyList, jsonFrom)

参数 说明 类型 是否可选 默认值
list 组织机构树接口返回的源数据 LegoTreeTypes.IOriginTreeNode[] -
hierarchyList 允许留下的 hierarchy 集合 string[] -
jsonFrom 组织机构树 json 来源 LegoTreeTypes.OrgTreeJsonFromEnum LegoTreeTypes.OrgTreeJsonFromEnum.SSO

示例

import { LegoTreeUtils } from '@pluve/lego-tree-vue';

const newTreeData = LegoTreeUtils.filterTreeNodesByHierarchyList(decodeRes, hierarchyList, LegoTreeTypes.OrgTreeJsonFromEnum.USER)

formatValueToName

数据选中后的展示,用于 LegoTree Input 输入框的回显,以及 LegoTreeTransfer 组件右侧选中数据后的展示。

Params

formatValueToName({ includeArea, value, disabled, type, onlyStoreCodeInDisabled })

参数 说明 类型 是否可选 默认值
includeArea 是否包含区域选择,即是否允许选择各层级父子节点 boolean false
value 当前选中项 (LegoTreeTypes.ICheckedAreaItem | string)[] -
disabled tree 是否禁用 boolean false
type 在输入框中展示 or 在弹窗右侧展示选中节点信息 input | modal input
onlyStoreCodeInDisabled 禁用时,是否只展示门店编码,仅限输入框中展示用 boolean false

示例

import { LegoTreeUtils } from '@pluve/lego-tree-vue';

LegoTreeUtils.formatValueToName({
  includeArea: props.includeArea ?? false,
  value: state.checkedNodes as LegoTreeTypes.ICheckedAreaItem[],
  disabled: props.disabled || false,
  type: 'modal',
})

getParentKey

由父级到子级,获取父节点集合

Params

getParentKey(key: string, tree: LegoTreeTypes.ITreeNodesType[])

参数 说明 类型 是否可选 默认值
key 需要获取父节点的节点 key string -
tree Tree/TreeSelect 组件的 treeData 数据源 LegoTreeTypes.ITreeNodesType[] -

示例

import { LegoTreeUtils } from '@pluve/lego-tree-vue';

LegoTreeUtils.getParentKey('5024', []);

getNodesByKeys

根据 keys 获取对应的 nodes 节点信息

Params

getNodesByKeys(keys, dataListMap)

参数 说明 类型 是否可选 默认值
keys 需要获取节点信息的 keys string[] -
dataListMap 扁平化 Map(已经经过转化后的数据源生成的 map) Record<string, LegoTreeTypes.ITreeNodesType> -

示例

import { LegoTreeUtils } from '@pluve/lego-tree-vue';

LegoTreeUtils.getNodesByKeys(['5024'], state.dataListMap);

generateListMap

获取 treeData 的 Map 结构。此方法会修改源数据,增加 trace 字段。

Params

generateListMap(data: LegoTreeTypes.ITreeNodesType[])

参数 说明 类型 是否可选 默认值
data Tree/TreeSelect 组件的 treeData 数据源 LegoTreeTypes.ITreeNodesType[] -

示例

import { LegoTreeUtils } from '@pluve/lego-tree-vue';

const { dataList, dataListMap } = LegoTreeUtils.generateListMap([]);

loadOrgTreeNodeByLevel

接口数据处理,传入回调函数,返回最终的 treeDatadataListMap

Params

loadOrgTreeNodeByLevel({ request, selectHierarchy, envType, jsonFrom, jsonType, hierarchyList, enableSelectedHierarchy, keepOriginNode }, function(treeData, dataListMap))

参数 说明 类型 是否可选 默认值
request 自定义 request 接口请求 () => Promise<LegoTreeTypes.ITreeNodesType[]> -
selectHierarchy 选择的组织机构级别,01-07,用于过滤子节点不包含 selectHierarchy 层级的节点 string ''
envType 环境(测试 | 生产) test | prod test
jsonFrom 组织机构树 json 来源 LegoTreeTypes.OrgTreeJsonFromEnum LegoTreeTypes.OrgTreeJsonFromEnum.SSO
jsonType 组织机构树 json 类型:全量(包含已关停等) or 有效的 LegoTreeTypes.OrgTreeJsonTypeEnum LegoTreeTypes.OrgTreeJsonTypeEnum.ENABLE
hierarchyList 组织机构级别数组,01-07,用于过滤不包含的节点信息,没传或为空则不过滤。 string[] []
enableSelectedHierarchy 【单选】可选择的组织机构级别,不传或为空则都可以选择,传了则包含的级别才可以选择,否则 selectablefalse 不可选择 string[] []
keepOriginNode includeAreatruelabelInValuetrue 时,若开启 keepOriginNode,则 返回的值里面会增加 originNode 字段。 boolean false

示例

import { LegoTreeUtils } from '@pluve/lego-tree-vue';

LegoTreeUtils.loadOrgTreeNodeByLevel(
  {
    request: props.request,
    selectHierarchy: props.selectHierarchy,
    envType: props.envType,
    jsonFrom: props.jsonFrom,
    jsonType: props.jsonType,
    hierarchyList: props.hierarchyList,
    enableSelectedHierarchy: props.enableSelectedHierarchy,
    keepOriginNode: props.keepOriginNode ?? false,
  },
  (dataList, dataListMap) => {
    state.treeData = dataList;
    state.dataListMap = dataListMap;
  }
);

LegoTreeServices

接口服务,用于获取 组织机构树 orgTree.json

getAllOrgTreeNodesByOss

根据 hierarchyList 匹配对应权限的 组织机构树 orgTree.json

Params

getAllOrgTreeNodesByOss({ hierarchyList, envType, jsonFrom, jsonType })

参数 说明 类型 是否可选 默认值
hierarchyList 根据 hierarchyList 字段过滤不包含的节点信息,传空或不传则默认取全量数据源 string[] -
envType 环境(测试 | 生产) test | prod test
jsonFrom 组织机构树 json 来源 LegoTreeTypes.OrgTreeJsonFromEnum LegoTreeTypes.OrgTreeJsonFromEnum.SSO
jsonType 组织机构树 json 类型:全量(包含已关停等) or 有效的 LegoTreeTypes.OrgTreeJsonTypeEnum LegoTreeTypes.OrgTreeJsonTypeEnum.ENABLE

示例

import { LegoTreeServices } from '@pluve/lego-tree-vue';

const result = await LegoTreeServices.getAllOrgTreeNodesByOss({
  hierarchyList: ['01', '02', '03', '04', '06', '07'],
  envType: 'test',
  jsonFrom: LegoTreeTypes.OrgTreeJsonFromEnum.USER,
  jsonType: LegoTreeTypes.OrgTreeJsonTypeEnum.ENABLE,
});

TODO

  1. LegoTree 组件,如何通过传值 footer 插槽,来自定义 LegoTreeModal 组件的插槽,且需要暴露对应的方法?
  2. attrs(多余的属性) 如何分配给不同组件?
  3. 如果部分参数传的有问题,应该要给出警告提示
  4. 页面展示3个 LegoLeftTree 组件,交互受影响,去掉 ConfigProvider 包裹后正常,暂时没找到原因。

Package Sidebar

Install

npm i @pluve/lego-tree-vue

Weekly Downloads

34

Version

0.1.0

License

MIT

Unpacked Size

635 kB

Total Files

32

Last publish

Collaborators

  • fuqiting
  • zhaoyajie
  • annan1220
  • pengfeng365
  • plutolove
  • vdfor
  • ddg-dany
  • yangwend
  • yaqin8023
  • damonchen
  • lee2545
  • abel0222
  • stevenluo
  • xiongyan
  • deng_cheng