ivue-material-plus
Version:
A high quality UI components Library with Vue.js
1 lines • 18.4 kB
Source Map (JSON)
{"version":3,"file":"node.mjs","sources":["../../../../../src/components/ivue-tree/node.vue"],"sourcesContent":["<template>\r\n <div\r\n tabindex=\"-1\"\r\n role=\"treeItem\"\r\n :class=\"wrapperClasses\"\r\n :data-key=\"getTreeNodeKey(node)\"\r\n :draggable=\"tree.props.draggable\"\r\n @click.stop=\"handleNodeClick\"\r\n v-show=\"node.visible\"\r\n @dragstart.stop=\"handleDragStart\"\r\n @dragover.stop.prevent=\"handleDragOver\"\r\n @dragend.stop.prevent=\"handleDragEnd\"\r\n @drop.stop.prevent\r\n ref=\"nodeRef\"\r\n >\r\n <!-- 标题 -->\r\n <div :class=\"`${prefixCls}--content`\" :style=\"contentStyles\">\r\n <!-- icon -->\r\n <ivue-icon\r\n :class=\"iconClasses\"\r\n @click.stop=\"handleExpandIconClick\"\r\n v-if=\"tree.props.icon && !node.loading\"\r\n >\r\n <component\r\n :is=\"tree.props.icon\"\r\n v-if=\"!isString(tree.props.icon)\"\r\n ></component>\r\n <template v-if=\"isString(tree.props.icon)\">\r\n {{ tree.props.icon }}\r\n </template>\r\n </ivue-icon>\r\n <!-- loading -->\r\n <loading v-if=\"node.loading\"></loading>\r\n <!-- checkbox -->\r\n <ivue-checkbox\r\n :class=\"`${prefixCls}--checkbox`\"\r\n :model-value=\"node.checked\"\r\n :indeterminate=\"node.indeterminate\"\r\n :disabled=\"!!node.disabled\"\r\n @click.stop\r\n @change=\"handleCheckChange\"\r\n v-if=\"showCheckbox\"\r\n ></ivue-checkbox>\r\n\r\n <!-- text -->\r\n <node-content :node=\"node\" :render-content=\"renderContent\" />\r\n </div>\r\n <!-- 子节点 -->\r\n <collapse-transition>\r\n <div\r\n :class=\"`${prefixCls}--children`\"\r\n v-if=\"!renderAfterExpand || childNodeRendered\"\r\n v-show=\"expanded\"\r\n >\r\n <ivue-tree-node\r\n v-for=\"child in node.childNodes\"\r\n :node=\"child\"\r\n :accordion=\"accordion\"\r\n :show-checkbox=\"showCheckbox\"\r\n :render-after-expand=\"renderAfterExpand\"\r\n :key=\"getTreeNodeKey(child)\"\r\n :render-content=\"renderContent\"\r\n :props=\"props\"\r\n @on-node-expand=\"handleChildNodeExpand\"\r\n ></ivue-tree-node>\r\n </div>\r\n </collapse-transition>\r\n </div>\r\n</template>\r\n\r\n<script lang=\"ts\">\r\nimport {\r\n computed,\r\n defineComponent,\r\n inject,\r\n provide,\r\n getCurrentInstance,\r\n ref,\r\n watch,\r\n nextTick,\r\n ComponentInternalInstance,\r\n PropType,\r\n} from 'vue';\r\nimport { getNodeKey, handleCurrentChange } from './utils';\r\nimport { isString, isFunction } from '@vue/shared';\r\nimport CollapseTransition from '../../utils/collapse-transition';\r\nimport { useNodeExpandEventBroadcast } from './hooks/useNodeExpandEventBroadcast';\r\nimport { dragEventsKey } from './hooks/useDragNode';\r\nimport { debugWarn } from '../../utils/error';\r\n\r\n// components\r\nimport IvueIcon from '../ivue-icon/index.vue';\r\nimport IvueCheckbox from '../ivue-checkbox/index.vue';\r\nimport NodeContent from './node-content.vue';\r\nimport Loading from './loading.vue';\r\n\r\n// type\r\nimport type { Props } from './types/node';\r\nimport type { TreeOptionProps } from './types/tree';\r\n\r\nimport Node from './store/node';\r\nimport { TreeContextKey, TreeNodeData } from './types/tree';\r\nimport { TreeNodeContextKey } from './types/node';\r\n\r\nconst prefixCls = 'ivue-tree-node';\r\n\r\nexport default defineComponent({\r\n name: prefixCls,\r\n emits: ['on-node-expand'],\r\n props: {\r\n /**\r\n * tree 节点对象\r\n *\r\n * @type {Node}\r\n */\r\n node: {\r\n type: Node,\r\n default: () => ({}),\r\n },\r\n /**\r\n * 树节点的内容区的渲染方法\r\n *\r\n * @type {Function}\r\n */\r\n renderContent: {\r\n type: Function,\r\n },\r\n /**\r\n * 是否每次只打开一个同级树节点展开\r\n *\r\n * @type {Boolean}\r\n */\r\n accordion: {\r\n type: Boolean,\r\n default: false,\r\n },\r\n /**\r\n * 设置节点是否被选中\r\n *\r\n * @type {Boolean}\r\n */\r\n showCheckbox: {\r\n type: Boolean,\r\n default: false,\r\n },\r\n /**\r\n * 是否在第一次展开某个树节点后才渲染其子节点\r\n *\r\n * @type {Boolean}\r\n */\r\n renderAfterExpand: {\r\n type: Boolean,\r\n default: true,\r\n },\r\n /**\r\n * 配置项\r\n *\r\n * @type {Object}\r\n */\r\n props: {\r\n type: Object as PropType<TreeOptionProps>,\r\n default: () => ({}),\r\n },\r\n },\r\n setup(props: Props, { emit }) {\r\n // vm\r\n const instance = getCurrentInstance();\r\n\r\n // 根树节点数据\r\n const tree = inject(TreeContextKey);\r\n // 拖动事件\r\n const dragEvents = inject(dragEventsKey);\r\n\r\n // dom\r\n const nodeRef = ref<HTMLElement>(null);\r\n\r\n // data\r\n\r\n // 展开子节点\r\n const expanded = ref<boolean>(false);\r\n // 子节点是否可以渲染\r\n const childNodeRendered = ref<boolean>(false);\r\n // 上一个选中状态\r\n const oldChecked = ref<boolean>(null);\r\n // 上一个不确定选中状态\r\n const oldIndeterminate = ref<boolean>(null);\r\n\r\n // 派发事件到所有子节点是否关闭展开\r\n const { broadcastExpanded } = useNodeExpandEventBroadcast(props);\r\n\r\n // computed\r\n\r\n // 外层样式\r\n const wrapperClasses = computed(() => {\r\n return [\r\n prefixCls,\r\n {\r\n ['is-expanded']: expanded.value,\r\n ['is-current']: props.node.isCurrent,\r\n ['is-focusable']: !props.node.disabled,\r\n ['is-checked']: !props.node.disabled && props.node.checked,\r\n },\r\n getNodeClass(props.node),\r\n ];\r\n });\r\n\r\n // 内容样式\r\n const contentStyles = computed(() => {\r\n return {\r\n paddingLeft: (props.node.level - 1) * tree.props.indent + 'px',\r\n };\r\n });\r\n\r\n // icon样式\r\n const iconClasses = computed(() => {\r\n return [\r\n `${prefixCls}--expand-icon`,\r\n {\r\n ['expanded']: expanded.value,\r\n ['is-leaf']: props.node.isLeaf,\r\n },\r\n ];\r\n });\r\n\r\n // methods\r\n\r\n // 初始化数据\r\n const initData = () => {\r\n // 有展开\r\n if (props.node.expanded) {\r\n expanded.value = true;\r\n\r\n // 子节点是否可以渲染\r\n childNodeRendered.value = true;\r\n }\r\n };\r\n\r\n // 获取节点key\r\n const getTreeNodeKey = (node: Node) => {\r\n return getNodeKey(tree.props.nodeKey, node.data);\r\n };\r\n\r\n // 获取节点class\r\n const getNodeClass = (node: Node) => {\r\n const nodeClassFunc = props.props.class;\r\n\r\n if (!nodeClassFunc) {\r\n return {};\r\n }\r\n\r\n let className;\r\n\r\n // 方法\r\n if (isFunction(nodeClassFunc)) {\r\n const { data } = node;\r\n className = nodeClassFunc(data, node);\r\n }\r\n // 其他类型\r\n else {\r\n className = nodeClassFunc;\r\n }\r\n\r\n // 字符串\r\n if (isString(className)) {\r\n return {\r\n [className]: true,\r\n };\r\n }\r\n // 对象\r\n else {\r\n return className;\r\n }\r\n };\r\n\r\n // 点击子节点\r\n const handleNodeClick = (event: MouseEvent) => {\r\n // 设置当前点击的子节点\r\n handleCurrentChange(tree.store, tree.ctx.emit, () =>\r\n tree.store.value.setCurrentNode(props.node)\r\n );\r\n\r\n // 设置当前点击节点\r\n tree.currentNode.value = props.node;\r\n\r\n // 是否在点击节点的时候展开或者收缩节点\r\n if (tree.props.expandOnClickNode) {\r\n handleExpandIconClick();\r\n }\r\n\r\n // 是否在点击节点的时候选中节点\r\n if (tree.props.checkOnClickNode && !props.node.disabled) {\r\n handleCheckChange({\r\n target: { checked: !props.node.checked },\r\n });\r\n }\r\n\r\n // 当节点被点击的时候触发\r\n tree.ctx.emit(\r\n 'on-node-click',\r\n // 点击节点的数据对象\r\n props.node.data,\r\n // 节点对应的 Node\r\n props.node,\r\n // 节点组件本身\r\n instance,\r\n // event\r\n event\r\n );\r\n };\r\n\r\n // 点击展开图标\r\n const handleExpandIconClick = () => {\r\n // 是否是叶子节点\r\n if (props.node.isLeaf) {\r\n return;\r\n }\r\n\r\n // 已经展开\r\n if (expanded.value) {\r\n // 节点被关闭时触发的事件\r\n tree.ctx.emit(\r\n 'on-node-collapse',\r\n // 传递给 data 属性的数组中该节点所对应的对象\r\n props.node.data,\r\n // 节点对应的 Node\r\n props.node,\r\n // 节点组件本身\r\n instance\r\n );\r\n\r\n // 关闭节点\r\n props.node.collapse();\r\n }\r\n // 展开\r\n else {\r\n // 展开节点\r\n props.node.expand();\r\n\r\n // 节点被展开时触发的事件\r\n emit(\r\n 'on-node-expand',\r\n // 传递给 data 属性的数组中该节点所对应的对象\r\n props.node.data,\r\n // 节点对应的 Node\r\n props.node,\r\n // 节点组件本身\r\n instance\r\n );\r\n }\r\n };\r\n\r\n // 节点展开\r\n const handleChildNodeExpand = (\r\n nodeData: TreeNodeData,\r\n node: Node,\r\n instance: ComponentInternalInstance\r\n ) => {\r\n // 派发展开事件\r\n broadcastExpanded(node);\r\n\r\n // 节点被展开时触发的事件\r\n tree.ctx.emit(\r\n 'on-node-expand',\r\n // 传递给 data 属性的数组中该节点所对应的对象\r\n nodeData,\r\n // 节点对应的 Node\r\n node,\r\n // 节点组件本身\r\n instance\r\n );\r\n };\r\n\r\n // 点击多选\r\n const handleCheckChange = (event) => {\r\n props.node.setChecked(event.target.checked, !tree.props.checkBoxStrictly);\r\n\r\n nextTick(() => {\r\n const store = tree.store.value;\r\n\r\n // 点击节点复选框之后触发\r\n tree.ctx.emit('on-check', props.node.data, {\r\n // 获取选中的节点\r\n checkedNodes: store.getCheckedNodes(),\r\n // 获取选中节点的key\r\n checkedKeys: store.getCheckedKeys(),\r\n // 获取半选中节点\r\n halfCheckedNodes: store.getHalfCheckedNodes(),\r\n // 获取半选中节点key\r\n halfCheckedKeys: store.getHalfCheckedKeys(),\r\n });\r\n });\r\n };\r\n\r\n // 多选改变\r\n const handleSelectChange = (checked: boolean, indeterminate: boolean) => {\r\n if (\r\n oldChecked.value !== checked ||\r\n oldIndeterminate.value !== indeterminate\r\n ) {\r\n // 当复选框被点击的时候触发\r\n tree.ctx.emit(\r\n 'on-check-change',\r\n // 传递给 data 属性的数组中该节点所对应的对象\r\n props.node.data,\r\n // 节点本身是否被选中\r\n checked,\r\n // 节点的子树中是否有半选中状态\r\n indeterminate\r\n );\r\n }\r\n\r\n oldChecked.value = checked;\r\n oldIndeterminate.value = indeterminate;\r\n };\r\n\r\n // 开始拖动\r\n const handleDragStart = (event: DragEvent) => {\r\n if (!tree.props.draggable) {\r\n return;\r\n }\r\n\r\n dragEvents.treeNodeDragStart({ event, treeNode: props });\r\n };\r\n\r\n // 拖动到目标\r\n const handleDragOver = (event: DragEvent) => {\r\n if (!tree.props.draggable) {\r\n return;\r\n }\r\n\r\n dragEvents.treeNodeDragOver({\r\n event,\r\n treeNode: {\r\n $el: nodeRef.value,\r\n node: props.node,\r\n },\r\n });\r\n };\r\n\r\n // 拖动结束\r\n const handleDragEnd = (event: DragEvent) => {\r\n if (!tree.props.draggable) {\r\n return;\r\n }\r\n\r\n dragEvents.treeNodeDragEnd(event);\r\n };\r\n\r\n // watch\r\n\r\n // 监听展开子节点\r\n watch(\r\n () => props.node.expanded,\r\n (value) => {\r\n nextTick(() => {\r\n expanded.value = value;\r\n });\r\n\r\n // 子节点是否可以渲染\r\n if (value) {\r\n childNodeRendered.value = true;\r\n }\r\n }\r\n );\r\n\r\n // 监听多选\r\n watch(\r\n () => props.node.checked,\r\n (value) => {\r\n handleSelectChange(value, props.node.indeterminate);\r\n }\r\n );\r\n\r\n // 监听多选半选中状态\r\n watch(\r\n () => props.node.indeterminate,\r\n (value) => {\r\n handleSelectChange(props.node.checked, value);\r\n }\r\n );\r\n\r\n // 监听子节点数据变化\r\n const childrenKey = tree.props['children'] || 'children';\r\n watch(\r\n () => {\r\n const children = props.node.data[childrenKey];\r\n\r\n return children && [...children];\r\n },\r\n () => {\r\n // 更新子节点\r\n props.node.updateChildren();\r\n }\r\n );\r\n\r\n // provide\r\n provide(TreeNodeContextKey, instance);\r\n\r\n // 初始化数据\r\n initData();\r\n\r\n // 没有树节点\r\n if (!tree) {\r\n debugWarn('Ivue Tree', 'Can not find node\\'s tree.');\r\n }\r\n\r\n return {\r\n prefixCls,\r\n\r\n // inject\r\n tree,\r\n\r\n // dom\r\n nodeRef,\r\n\r\n // data\r\n expanded,\r\n childNodeRendered,\r\n\r\n // computed\r\n wrapperClasses,\r\n contentStyles,\r\n iconClasses,\r\n\r\n // methods\r\n isString,\r\n getTreeNodeKey,\r\n handleNodeClick,\r\n handleChildNodeExpand,\r\n handleCheckChange,\r\n handleExpandIconClick,\r\n handleDragStart,\r\n handleDragOver,\r\n handleDragEnd,\r\n };\r\n },\r\n components: {\r\n IvueIcon,\r\n IvueCheckbox,\r\n NodeContent,\r\n CollapseTransition,\r\n Loading,\r\n },\r\n});\r\n</script>\r\n"],"names":["_resolveComponent","_withDirectives","_openBlock","_createElementBlock","_normalizeClass","_withModifiers","_createCommentVNode","_createElementVNode","_normalizeStyle","_withCtx","_createBlock","_resolveDynamicComponent","_Fragment","_createVNode","_renderList"],"mappings":"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"}