layui/src/module/tree/index.vue

203 lines
5.0 KiB
Vue
Raw Normal View History

<script setup lang="ts">
2021-10-11 08:17:55 +00:00
import { VNode, VNodeChild } from 'vue'
2021-10-09 14:19:17 +00:00
import TreeEntity from './TreeEntity.vue'
2021-10-11 08:17:55 +00:00
import { useTreeData } from '/@src/module/tree/useTreeData'
import { TreeNode } from '/@src/module/tree/tree.type'
2021-10-11 10:09:38 +00:00
import { getEmitNode } from '/@src/module/tree/treeHelper'
2021-10-11 08:17:55 +00:00
type EditAction = 'add' | 'update' | 'del'
type EditType = boolean | EditAction[]
2021-10-11 10:09:38 +00:00
interface TreeData {
/**
* 节点唯一索引值用于对指定节点进行各类操作
*/
id: string | number
/**
* 节点标题
*/
title: string | (() => string)
/**
* 节点字段名
*/
field: string | (() => string)
/**
* 子节点支持设定选项同父节点
*/
children: TreeData[]
/**
* 点击节点弹出新窗口对应的 url需开启 isJump 参数
2021-10-09 14:19:17 +00:00
* 废弃通过 on-click事件用户控制
*/
href?: string | URL
/**
* 节点是否初始展开默认 false
2021-10-09 14:19:17 +00:00
* 废弃设置 v-model:spreadKeys
*/
spread?: boolean
/**
* 节点是否初始为选中状态如果开启复选框的话默认 false
2021-10-09 14:19:17 +00:00
* 废弃设置 v-model:checkedKeys
*/
checked?: boolean
/**
* 节点是否为禁用状态默认 false
*/
disabled?: boolean
}
interface TreeProps {
2021-10-09 14:19:17 +00:00
/**
* 指定唯一的id
*/
// eslint-disable-next-line vue/require-default-prop
2021-10-11 08:17:55 +00:00
key?: string
2021-10-09 14:19:17 +00:00
/**
* 选中的节点
*/
// eslint-disable-next-line vue/require-default-prop
checkedKeys?: NonNullable<(string | number)[]>
2021-10-09 14:19:17 +00:00
/**
* 展开的节点
*/
// eslint-disable-next-line vue/require-default-prop
2021-10-11 08:17:55 +00:00
spreadKeys?: (string | number)[]
/**
* 数据源
*/
// eslint-disable-next-line vue/require-default-prop
2021-10-11 08:17:55 +00:00
data?: TreeData[]
/**
* 是否显示复选框 默认 false
*/
showCheckbox?: boolean
/**
* 是否开启节点的操作图标默认 false
若为 true则默认显示改删图标
若为 数组则可自由配置操作图标的显示状态和顺序目前支持的操作图标有addupdatedel
edit: ['add', 'update', 'del']
*/
edit?: EditType
/**
* 是否开启手风琴模式默认 false
*/
accordion?: boolean
/**
* 是否仅允许节点左侧图标控制展开收缩默认 false即点击节点本身也可控制若为 true则只能通过节点左侧图标来展开收缩
*/
onlyIconControl?: boolean
/**
* 是否允许点击节点时弹出新窗口跳转默认 false若开启需在节点数据中设定 link 参数值为 url 格式
2021-10-09 14:19:17 +00:00
* 废弃能过事件用户自行控制
*/
isJump?: boolean
/**
* 是否开启连接线默认 true若设为 false则节点左侧出现三角图标
*/
showLine?: boolean
/**
* 自定义各类默认文本目前支持以下设定
*/
// eslint-disable-next-line vue/require-default-prop
text?: {
/**
* 节点默认名称
*/
defaultNodeName?: () => string | string
/**
* 数据为空时的提示文本
*/
2021-10-11 08:17:55 +00:00
none?: (() => string) | string | VNode | Element
}
}
2021-10-11 10:09:38 +00:00
interface EmitData {
/**
* 当前点击的节点数据
*/
data: TreeData
/**
* 节点的展开状态
2021-10-11 10:09:38 +00:00
* remove
*/
2021-10-11 10:09:38 +00:00
state?: 'open' | 'close' | 'normal'
/**
* 当前节点元素
2021-10-11 10:09:38 +00:00
* remove
*/
2021-10-11 10:09:38 +00:00
elem?: Element | VNode | VNodeChild
}
2021-10-11 10:09:38 +00:00
interface TreeEmits {
/**
* 节点被点击后触发
* @param e 事件
* @param treeNode
*/
2021-10-11 10:09:38 +00:00
(e: 'node-click', treeNode: EmitData): void
/**
* 点击复选框时触发
* @param e 事件
* @param treeNode
*/
2021-10-11 10:09:38 +00:00
(e: 'node-check', treeNode: EmitData): void
// /**
// * 操作节点的回调
// * @param e 事件
// * @param treeNode
// */
// (e: 'node-operate', treeNode: EmitData): void
(e: 'update:spreadKeys', spreadKeys: (string | number)[]): void
(e: 'update:checkedKeys', checkedKeys: (string | number)[]): void
}
const props = withDefaults(defineProps<TreeProps>(), {
showCheckbox: false,
accordion: false,
onlyIconControl: false,
isJump: false,
showLine: true,
2021-10-11 08:17:55 +00:00
edit: () => true,
2021-10-09 14:19:17 +00:00
})
2021-10-11 08:17:55 +00:00
const emit = defineEmits<TreeEmits>()
2021-10-09 14:19:17 +00:00
const {
innerTreeData,
updateInnerTreeData,
treeWrapperClass,
updateCheckedByNode,
} = useTreeData(props, emit)
2021-10-09 14:19:17 +00:00
function handleNodeClick(node: TreeNode, type: 'node' | 'icon') {
// 是否只通过icon控制展开收起
if (props.onlyIconControl) {
type === 'icon' && updateInnerTreeData(innerTreeData.value, node)
} else {
updateInnerTreeData(innerTreeData.value, node)
}
// icon 点击不emit出事件
2021-10-11 10:09:38 +00:00
const emitNode = getEmitNode(props.data!, node)
type !== 'icon' && emit('node-click', { data: emitNode! })
2021-10-11 08:17:55 +00:00
}
</script>
<script lang="ts">
export default {
name: 'LayTree',
}
</script>
<template>
2021-10-09 14:19:17 +00:00
<div :class="treeWrapperClass">
<TreeEntity
v-for="(node, index) in innerTreeData"
:key="node.id || index"
2021-10-09 14:19:17 +00:00
:node="node"
:show-checkbox="showCheckbox"
2021-10-11 08:17:55 +00:00
@node-click="handleNodeClick"
:updateCheckedByNode="updateCheckedByNode"
/>
2021-10-09 14:19:17 +00:00
</div>
</template>
<style scoped></style>