song-ui-u
Version:
vue3 + js的PC前端组件库
313 lines (276 loc) • 10.8 kB
JavaScript
'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