UNPKG

song-ui-u

Version:

vue3 + js的PC前端组件库

313 lines (276 loc) 10.8 kB
'use strict'; var vue = require('vue'); var index = require('../../../hook/use-namespace/index.cjs'); var icons = require('song-ui-pro-icon'); require('../../../hook/use-zindex/index.cjs'); require('../../button/index.cjs'); require('../../buttonGroup/index.cjs'); var index$1 = require('../../icon/index.cjs'); require('../../input/index.cjs'); require('../../textarea/index.cjs'); require('../../row/index.cjs'); require('../../col/index.cjs'); require('../../container/index.cjs'); var index$2 = require('../../checkbox/index.cjs'); require('../../switch/index.cjs'); require('../../form/index.cjs'); require('../../message/index.cjs'); require('../../mask/src/mask.cjs'); require('../../modal/index.cjs'); require('../../messageBox/index.cjs'); require('../../drawer/index.cjs'); require('../../badge/index.cjs'); require('../../space/index.cjs'); require('../../image/index.cjs'); var index$3 = require('../../radio/index.cjs'); require('../../divider/index.cjs'); require('../../chat/index.cjs'); require('../../progress/index.cjs'); require('../../upload/index.cjs'); require('../index.cjs'); require('../../table/index.cjs'); require('../../tabs/index.cjs'); require('../../menu/index.cjs'); require('../../steps/index.cjs'); require('../../header/index.cjs'); require('../../breadcrumble/index.cjs'); require('../../datePicker/index.cjs'); require('../../tooltip/index.cjs'); require('../../popover/index.cjs'); require('../../timePicker/index.cjs'); require('../../select/index.cjs'); require('../../collapse/index.cjs'); require('../../card/index.cjs'); require('../../timeline/index.cjs'); require('../../tag/index.cjs'); require('../../result/index.cjs'); require('../../sender/index.cjs'); var _pluginVue_exportHelper = require('../../../_virtual/_plugin-vue_export-helper.cjs'); const itemHeight = 30; const visibleCount = 10; // 扁平化树结构 const _sfc_main = /*#__PURE__*/Object.assign({ name: "x-vtree", }, { __name: 'index', props: { data: { type: Array, required: true, default: () => [], }, selectionMode: { type: String, default: "single", validator: (value) => ["single", "multiple"].includes(value), }, // label label: { type: String, default: "label", }, // value // value: { // type: String, // default: "id", // }, }, emits: ["change", "expand", "collapse"], setup(__props, { expose: __expose, emit: __emit }) { __expose(); const ns = index.useNamespace("vtree"); const props = __props; const emits = __emit; const islabel = vue.computed(() => props.label); // const isvalue = computed(() => props.value); const container = vue.ref(null); const scrollTop = vue.ref(0); const flatTree = vue.computed(() => { const result = []; function traverse(node, depth = 0) { if (!node) return; // 确保节点有 expanded 属性 if (node.expanded === undefined) { node.expanded = false; } node.depth = depth; result.push(node); if (node.children && node.expanded) { node.children.forEach((child) => traverse(child, depth + 1)); } } props.data.forEach((node) => traverse(node)); return result; }); const totalHeight = vue.computed(() => flatTree.value.length * itemHeight); const visibleNodes = vue.computed(() => { // 根据滚动位置计算起始索引: // scrollTop.value 是当前滚动的像素值 // itemHeight 是每个节点的高度(30px) // 除法结果向下取整,得到应该从第几个节点开始显示 const start = Math.floor(scrollTop.value / itemHeight); // 计算结束索引: // start + visibleCount 是理论上需要显示的节点数量 // 使用 Math.min 确保不会超出实际节点总数 const end = Math.min(start + visibleCount, flatTree.value.length); // 从扁平化的树结构中截取需要显示的部分 return flatTree.value.slice(start, end); }); const getNodeStyle = (index) => ({ position: "absolute", top: `${scrollTop.value + index * itemHeight}px`, }); const handleScroll = () => { if (container.value) { scrollTop.value = container.value.scrollTop; } }; const selectedNodes = vue.ref([]); // 对于多选 const selectedNode = vue.ref(null); // 对于单选 const toggleNode = (node) => { if (props.selectionMode === "single") { selectedNode.value = node.id; // 选中 emits("change", selectedNode.value); } else if (props.selectionMode === "multiple") { const index = selectedNodes.value.indexOf(node.id); if (index > -1) { selectedNodes.value.splice(index, 1); // 取消选中 if (node.children && node.expanded) { deselectChildren(node.children); // 取消选中子节点 } } else { selectedNodes.value.push(node.id); // 选中 if (node.children && node.expanded) { selectChildren(node.children); // 选中子节点 } } emits("change", selectedNodes.value); } }; const selectChildren = (children) => { children.forEach((child) => { if (!selectedNodes.value.includes(child.id)) { selectedNodes.value.push(child.id); } if (child.children && child.expanded) { selectChildren(child.children); } }); }; const deselectChildren = (children) => { children.forEach((child) => { const index = selectedNodes.value.indexOf(child.id); if (index > -1) { selectedNodes.value.splice(index, 1); } if (child.children && child.expanded) { deselectChildren(child.children); } }); }; const isSelected = (node) => { if (props.selectionMode === "single") { return selectedNode.value === node.id; } else if (props.selectionMode === "multiple") { return selectedNodes.value.includes(node.id); } return false; }; const toggleExpand = (node) => { if (!node.children) return; if (node.expanded) { emits("collapse", node); } else { emits("expand", node); } node.expanded = !node.expanded; }; const __returned__ = { ns, props, emits, islabel, container, scrollTop, itemHeight, visibleCount, flatTree, totalHeight, visibleNodes, getNodeStyle, handleScroll, selectedNodes, selectedNode, toggleNode, selectChildren, deselectChildren, isSelected, toggleExpand, ref: vue.ref, computed: vue.computed, get useNamespace() { return index.useNamespace }, get XIcon() { return index$1.XIcon }, get XCheckbox() { return index$2.XCheckbox }, get XRadio() { return index$3.XRadio }, get PlusSquare() { return icons.PlusSquare }, get MinusSquare() { return icons.MinusSquare } }; Object.defineProperty(__returned__, '__isScriptSetup', { enumerable: false, value: true }); return __returned__ } }); const _hoisted_1 = ["onClick"]; const _hoisted_2 = ["onClick"]; function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) { return (vue.openBlock(), vue.createElementBlock("div", { class: vue.normalizeClass($setup.ns.b()), onScroll: $setup.handleScroll, ref: "container" }, [ vue.createElementVNode("div", { style: vue.normalizeStyle({ height: $setup.totalHeight + 'px' }), class: vue.normalizeClass($setup.ns.e('spacer')) }, null, 6 /* CLASS, STYLE */), (vue.openBlock(true), vue.createElementBlock(vue.Fragment, null, vue.renderList($setup.visibleNodes, (node, index) => { return (vue.openBlock(), vue.createElementBlock("div", { key: node.id, style: vue.normalizeStyle($setup.getNodeStyle(index)), class: vue.normalizeClass([$setup.ns.e('node'), $setup.ns.is('selected', $setup.isSelected(node))]), onClick: vue.withModifiers($event => ($setup.toggleNode(node)), ["stop"]) }, [ vue.createElementVNode("span", { class: vue.normalizeClass($setup.ns.e('node-inner')), style: vue.normalizeStyle({ paddingLeft: `${node.depth * 20}px` }) }, [ (node.children) ? (vue.openBlock(), vue.createElementBlock("span", { key: 0, class: vue.normalizeClass($setup.ns.e('expand-icon')), onClick: vue.withModifiers($event => ($setup.toggleExpand(node)), ["stop"]) }, [ (node.expanded) ? (vue.openBlock(), vue.createBlock($setup["XIcon"], { key: 0, color: "#abb1bf" }, { default: vue.withCtx(() => [ vue.createVNode($setup["MinusSquare"]) ]), _: 1 /* STABLE */ })) : (vue.openBlock(), vue.createBlock($setup["XIcon"], { key: 1, color: "#abb1bf" }, { default: vue.withCtx(() => [ vue.createVNode($setup["PlusSquare"]) ]), _: 1 /* STABLE */ })) ], 10 /* CLASS, PROPS */, _hoisted_2)) : vue.createCommentVNode("v-if", true), ($setup.props.selectionMode === 'multiple') ? (vue.openBlock(), vue.createBlock($setup["XCheckbox"], { key: 1, size: "small", checked: $setup.isSelected(node), onClick: vue.withModifiers($event => ($setup.toggleNode(node)), ["stop"]) }, { default: vue.withCtx(() => [ vue.createTextVNode(vue.toDisplayString(node[$setup.islabel]), 1 /* TEXT */) ]), _: 2 /* DYNAMIC */ }, 1032 /* PROPS, DYNAMIC_SLOTS */, ["checked", "onClick"])) : (vue.openBlock(), vue.createBlock($setup["XRadio"], { key: 2, type: "radio", size: "small", name: $setup.props.selectionMode, value: node.id, checked: $setup.isSelected(node), onClick: vue.withModifiers($event => ($setup.toggleNode(node)), ["stop"]) }, { default: vue.withCtx(() => [ vue.createTextVNode(vue.toDisplayString(node[$setup.islabel]), 1 /* TEXT */) ]), _: 2 /* DYNAMIC */ }, 1032 /* PROPS, DYNAMIC_SLOTS */, ["name", "value", "checked", "onClick"])) ], 6 /* CLASS, STYLE */) ], 14 /* CLASS, STYLE, PROPS */, _hoisted_1)) }), 128 /* KEYED_FRAGMENT */)) ], 34 /* CLASS, NEED_HYDRATION */)) } var vtree = /*#__PURE__*/_pluginVue_exportHelper(_sfc_main, [['render',_sfc_render],['__file',"E:\\code\\my-code\\song-ui-ultra\\packages\\components\\vTree\\src\\index.vue"]]); module.exports = vtree; //# sourceMappingURL=index.vue.cjs.map