123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498 |
- <template>
- <view>
- <template v-if="showLoading">
- <view class="ly-loader ly-flex-center">
- <view class="ly-loader-inner">加载中...</view>
- </view>
- </template>
- <template v-else>
- <view v-if="isEmpty" class="ly-empty bg-white padding" style="margin-top:0px">{{emptyText}}</view>
- <view class="ly-tree " :class="{ 'ly-tree--highlight-current': highlightCurrent }" role="tree" name="LyTreeExpand">
- <ly-tree-node class="text-black" v-for="nodeId in childNodesId"
- :nodeId="nodeId"
- :render-after-expand="renderAfterExpand"
- :show-checkbox="showCheckbox"
- :show-radio="showRadio"
- :check-only-leaf="checkOnlyLeaf"
- :key="getNodeKey(nodeId)"
- :indent="indent"
- :icon-class="iconClass">
- </ly-tree-node>
- </view>
- </template>
- </view>
- </template>
- <script>
- import TreeStore from './model/tree-store.js';
- import {getNodeKey} from './tool/util.js';
- import LyTreeNode from './ly-tree-node.vue';
- export default {
- name: 'LyTree',
- componentName: 'LyTree',
- components: {
- LyTreeNode
- },
- data() {
- return {
- elId: `ly_${Math.ceil(Math.random() * 10e5).toString(36)}`,
- store: {
- ready: false
- },
- currentNode: null,
- childNodesId: []
- };
- },
- provide() {
- return {
- tree: this
- }
- },
- props: {
- // 展示数据
- treeData: Array,
- // 自主控制loading加载,避免数据还没获取到的空档出现“暂无数据”字样
- ready: {
- type: Boolean,
- default: true
- },
- // 内容为空的时候展示的文本
- emptyText: {
- type: String,
- default: '暂无数据'
- },
- // 是否在第一次展开某个树节点后才渲染其子节点
- renderAfterExpand: {
- type: Boolean,
- default: true
- },
- // 每个树节点用来作为唯一标识的属性,整棵树应该是唯一的
- nodeKey: String,
- // 在显示复选框的情况下,是否严格的遵循父子不互相关联的做法,默认为 false
- checkStrictly: Boolean,
- // 是否默认展开所有节点
- defaultExpandAll: Boolean,
- // 切换全部展开、全部折叠
- toggleExpendAll: Boolean,
- // 是否在点击节点的时候展开或者收缩节点, 默认值为 true,如果为 false,则只有点箭头图标的时候才会展开或者收缩节点
- expandOnClickNode: {
- type: Boolean,
- default: true
- },
- // 选中的时候展开节点
- expandOnCheckNode: {
- type: Boolean,
- default: false
- },
- // 是否在点击节点的时候选中节点,默认值为 false,即只有在点击复选框时才会选中节点
- checkOnClickNode: Boolean,
- checkDescendants: {
- type: Boolean,
- default: false
- },
- // 展开子节点的时候是否自动展开父节点
- autoExpandParent: {
- type: Boolean,
- default: true
- },
- // 默认勾选的节点的 key 的数组
- defaultCheckedKeys: Array,
- // 默认展开的节点的 key 的数组
- defaultExpandedKeys: Array,
- // 当前选中的节点
- currentNodeKey: [String, Number],
- // 是否最后一层叶子节点才显示单选/多选框
- checkOnlyLeaf: {
- type: Boolean,
- default: false
- },
- // 节点是否可被选择
- showCheckbox: {
- type: Boolean,
- default: false
- },
- // 节点单选
- showRadio: {
- type: Boolean,
- default: false
- },
- // 配置选项
- props: {
- type: [Object, Function],
- default () {
- return {
- children: 'children', // 指定子树为节点对象的某个属性值
- label: 'name', // 指定节点标签为节点对象的某个属性值
- disabled: 'disabled' // 指定节点选择框是否禁用为节点对象的某个属性值
- };
- }
- },
- // 是否懒加载子节点,需与 load 方法结合使用
- lazy: {
- type: Boolean,
- default: false
- },
- // 是否高亮当前选中节点,默认值是 false
- highlightCurrent: Boolean,
- // 加载子树数据的方法,仅当 lazy 属性为true 时生效
- load: Function,
- // 对树节点进行筛选时执行的方法,返回 true 表示这个节点可以显示,返回 false 则表示这个节点会被隐藏
- filterNodeMethod: Function,
- // 搜索时是否展示匹配项的所有子节点
- childVisibleForFilterNode: {
- type: Boolean,
- default: false
- },
- // 是否每次只打开一个同级树节点展开
- accordion: Boolean,
- // 相邻级节点间的水平缩进,单位为像素
- indent: {
- type: Number,
- default: 18
- },
- // 自定义树节点的展开图标
- iconClass: String,
- // 是否显示节点图标,如果配置为true,需要配置props中对应的图标属性名称
- showNodeIcon: {
- type: Boolean,
- default: false
- }
- },
- computed: {
- children: {
- set(value) {
- this.treeData = value;
- },
- get() {
- return this.treeData;
- }
- },
- isEmpty() {
- if (this.store.root) {
- const childNodes = this.store.root.getChildNodes(this.childNodesId);
- return !childNodes || childNodes.length === 0 || childNodes.every(({visible}) => !visible);
- }
- return true;
- },
- showLoading() {
- return !(this.store.ready && this.ready);
- }
- },
- watch: {
- toggleExpendAll(newVal) {
- this.store.toggleExpendAll(newVal);
- },
- defaultCheckedKeys(newVal) {
- this.store.setDefaultCheckedKey(newVal);
- },
- defaultExpandedKeys(newVal) {
- this.store.defaultExpandedKeys = newVal;
- this.store.setDefaultExpandedKeys(newVal);
- },
- treeData(newVal) {
- this.store.setData(newVal);
- },
- checkStrictly(newVal) {
- this.store.checkStrictly = newVal || this.checkOnlyLeaf;
- },
- 'store.root.childNodesId'(newVal) {
- this.childNodesId = newVal;
- },
- childNodesId(){
- this.$nextTick(() => {
- this.$emit('ly-tree-render-completed');
- });
- }
- },
- methods: {
- /*
- * @description 对树节点进行筛选操作
- * @method filter
- * @param {all} value 在 filter-node-method 中作为第一个参数
- * @param {Object} data 搜索指定节点的节点数据,不传代表搜索所有节点,假如要搜索A节点下面的数据,那么nodeData代表treeData中A节点的数据
- */
- filter(value, data) {
- if (!this.filterNodeMethod) throw new Error('[Tree] filterNodeMethod is required when filter');
- this.store.filter(value, data);
- },
- /*
- * @description 获取节点的唯一标识符
- * @method getNodeKey
- * @param {String, Number} nodeId
- * @return {String, Number} 匹配到的数据中的某一项数据
- */
- getNodeKey(nodeId) {
- let node = this.store.root.getChildNodes([nodeId])[0];
- return getNodeKey(this.nodeKey, node.data);
- },
- /*
- * @description 获取节点路径
- * @method getNodePath
- * @param {Object} data 节点数据
- * @return {Array} 路径数组
- */
- getNodePath(data) {
- return this.store.getNodePath(data);
- },
- /*
- * @description 若节点可被选择(即 show-checkbox 为 true),则返回目前被选中的节点所组成的数组
- * @method getCheckedNodes
- * @param {Boolean} leafOnly 是否只是叶子节点,默认false
- * @param {Boolean} includeHalfChecked 是否包含半选节点,默认false
- * @return {Array} 目前被选中的节点所组成的数组
- */
- getCheckedNodes(leafOnly, includeHalfChecked) {
- return this.store.getCheckedNodes(leafOnly, includeHalfChecked);
- },
- /*
- * @description 若节点可被选择(即 show-checkbox 为 true),则返回目前被选中的节点的 key 所组成的数组
- * @method getCheckedKeys
- * @param {Boolean} leafOnly 是否只是叶子节点,默认false,若为 true 则仅返回被选中的叶子节点的 keys
- * @return {Array} 目前被选中的节点所组成的数组
- */
- getCheckedKeys(leafOnly) {
- return this.store.getCheckedKeys(leafOnly);
- },
- /*
- * @description 获取当前被选中节点的 data,若没有节点被选中则返回 null
- * @method getCurrentNode
- * @return {Object} 当前被选中节点的 data,若没有节点被选中则返回 null
- */
- getCurrentNode() {
- const currentNode = this.store.getCurrentNode();
- return currentNode ? currentNode.data : null;
- },
- /*
- * @description 获取当前被选中节点的 key,若没有节点被选中则返回 null
- * @method getCurrentKey
- * @return {all} 当前被选中节点的 key, 若没有节点被选中则返回 null
- */
- getCurrentKey() {
- const currentNode = this.getCurrentNode();
- return currentNode ? currentNode[this.nodeKey] : null;
- },
- /*
- * @description 设置全选/取消全选
- * @method setCheckAll
- * @param {Boolean} isCheckAll 选中状态,默认为true
- */
- setCheckAll(isCheckAll = true) {
- if (this.showRadio) throw new Error('You set the "show-radio" property, so you cannot select all nodes');
- if (!this.showCheckbox) console.warn('You have not set the property "show-checkbox". Please check your settings');
- this.store.setCheckAll(isCheckAll);
- },
- /*
- * @description 设置目前勾选的节点
- * @method setCheckedNodes
- * @param {Array} nodes 接收勾选节点数据的数组
- * @param {Boolean} leafOnly 是否只是叶子节点, 若为 true 则仅设置叶子节点的选中状态,默认值为 false
- */
- setCheckedNodes(nodes, leafOnly) {
- this.store.setCheckedNodes(nodes, leafOnly);
- },
- /*
- * @description 通过 keys 设置目前勾选的节点
- * @method setCheckedKeys
- * @param {Array} keys 勾选节点的 key 的数组
- * @param {Boolean} leafOnly 是否只是叶子节点, 若为 true 则仅设置叶子节点的选中状态,默认值为 false
- */
- setCheckedKeys(keys, leafOnly) {
- if (!this.nodeKey) throw new Error('[Tree] nodeKey is required in setCheckedKeys');
- this.store.setCheckedKeys(keys, leafOnly);
- },
- /*
- * @description 通过 key / data 设置某个节点的勾选状态
- * @method setChecked
- * @param {all} data 勾选节点的 key 或者 data
- * @param {Boolean} checked 节点是否选中
- * @param {Boolean} deep 是否设置子节点 ,默认为 false
- */
- setChecked(data, checked, deep) {
- this.store.setChecked(data, checked, deep);
- },
- /*
- * @description 若节点可被选择(即 show-checkbox 为 true),则返回目前半选中的节点所组成的数组
- * @method getHalfCheckedNodes
- * @return {Array} 目前半选中的节点所组成的数组
- */
- getHalfCheckedNodes() {
- return this.store.getHalfCheckedNodes();
- },
- /*
- * @description 若节点可被选择(即 show-checkbox 为 true),则返回目前半选中的节点的 key 所组成的数组
- * @method getHalfCheckedKeys
- * @return {Array} 目前半选中的节点的 key 所组成的数组
- */
- getHalfCheckedKeys() {
- return this.store.getHalfCheckedKeys();
- },
- /*
- * @description 通过 node 设置某个节点的当前选中状态
- * @method setCurrentNode
- * @param {Object} node 待被选节点的 node
- */
- setCurrentNode(node) {
- if (!this.nodeKey) throw new Error('[Tree] nodeKey is required in setCurrentNode');
- this.store.setUserCurrentNode(node);
- },
- /*
- * @description 通过 key 设置某个节点的当前选中状态
- * @method setCurrentKey
- * @param {all} key 待被选节点的 key,若为 null 则取消当前高亮的节点
- */
- setCurrentKey(key) {
- if (!this.nodeKey) throw new Error('[Tree] nodeKey is required in setCurrentKey');
- this.store.setCurrentNodeKey(key);
- },
- /*
- * @description 根据 data 或者 key 拿到 Tree 组件中的 node
- * @method getNode
- * @param {all} data 要获得 node 的 key 或者 data
- */
- getNode(data) {
- return this.store.getNode(data);
- },
- /*
- * @description 删除 Tree 中的一个节点
- * @method remove
- * @param {all} data 要删除的节点的 data 或者 node
- */
- remove(data) {
- this.store.remove(data);
- },
- /*
- * @description 为 Tree 中的一个节点追加一个子节点
- * @method append
- * @param {Object} data 要追加的子节点的 data
- * @param {Object} parentNode 子节点的 parent 的 data、key 或者 node
- */
- append(data, parentNode) {
- this.store.append(data, parentNode);
- },
- /*
- * @description 为 Tree 的一个节点的前面增加一个节点
- * @method insertBefore
- * @param {Object} data 要增加的节点的 data
- * @param {all} refNode 要增加的节点的后一个节点的 data、key 或者 node
- */
- insertBefore(data, refNode) {
- this.store.insertBefore(data, refNode);
- },
- /*
- * @description 为 Tree 的一个节点的后面增加一个节点
- * @method insertAfter
- * @param {Object} data 要增加的节点的 data
- * @param {all} refNode 要增加的节点的前一个节点的 data、key 或者 node
- */
- insertAfter(data, refNode) {
- this.store.insertAfter(data, refNode);
- },
- /*
- * @description 通过 keys 设置节点子元素
- * @method updateKeyChildren
- * @param {String, Number} key 节点 key
- * @param {Object} data 节点数据的数组
- */
- updateKeyChildren(key, data) {
- if (!this.nodeKey) throw new Error('[Tree] nodeKey is required in updateKeyChild');
- this.store.updateChildren(key, data);
- }
- },
- created() {
- this.isTree = true;
- let props = this.props;
- if (typeof this.props === 'function') props = this.props();
- if (typeof props !== 'object') throw new Error('props must be of object type.');
- this.store = new TreeStore({
- key: this.nodeKey,
- data: this.treeData,
- lazy: this.lazy,
- props: props,
- load: this.load,
- showCheckbox: this.showCheckbox,
- showRadio: this.showRadio,
- currentNodeKey: this.currentNodeKey,
- checkStrictly: this.checkStrictly || this.checkOnlyLeaf,
- checkDescendants: this.checkDescendants,
- defaultCheckedKeys: this.defaultCheckedKeys,
- defaultExpandedKeys: this.defaultExpandedKeys,
- autoExpandParent: this.autoExpandParent,
- defaultExpandAll: this.defaultExpandAll,
- filterNodeMethod: this.filterNodeMethod,
- childVisibleForFilterNode: this.childVisibleForFilterNode,
- showNodeIcon: this.showNodeIcon
- });
- this.childNodesId = this.store.root.childNodesId;
- },
- beforeDestroy() {
- if (this.accordion) {
- uni.$off(`${this.elId}-tree-node-expand`)
- }
- }
- };
- </script>
|