Files
HTFX-CRM-APP/pages/components/baseTree/treeNode.vue
2025-07-07 15:55:44 +08:00

158 lines
3.9 KiB
Vue

<template>
<view v-if="!table" class="tree-node" :class="{ 'tree-node--with-tree-line': treeLine }" :style="indentStyle" ref="el">
<template v-if="treeLine">
<view v-for="line in vLines" class="tree-line tree-vline" :style="line.style"></view>
<view v-if="stat.level > 1" class="tree-line tree-hline" :style="hLineStyle"></view>
</template>
<view class="tree-node-inner">
<slot :indentStyle="indentStyle"></slot>
</view>
</view>
<tr v-else class="tree-node" ref="el">
<slot :indentStyle="indentStyle"></slot>
</tr>
</template>
<script lang="ts">
import { defineComponent, computed, watch } from "./vue"
let justToggleOpen = false
const afterToggleOpen = () => {
justToggleOpen = true
setTimeout(() => {
justToggleOpen = false
}, 100)
}
const cpt = defineComponent({
// components: {},
props: ["stat", "rtl", "btt", "indent", "table", "treeLine", "treeLineOffset", "processor"],
emits: ["open", "close", "check"],
setup(props, { emit }) {
const indentStyle = computed(() => {
return {
[!props.rtl ? "paddingLeft" : "paddingRight"]:
props.indent * (props.stat.level - 1) + "px",
};
});
// watch checked
watch(
() => props.stat.checked,
(checked) => {
// fix issue: https://github.com/phphe/he-tree/issues/98
// when open/close above node, the after nodes' states 'checked' and 'open' will be updated. It should be caused by Vue's key. We don't use Vue's key prop.
if (justToggleOpen) {
return
}
if (props.processor.afterOneCheckChanged(props.stat)) {
emit("check", props.stat);
}
}
);
// watch open
watch(
() => props.stat.open,
(open) => {
if (justToggleOpen) {
return
}
if (open) {
emit("open", props.stat);
} else {
emit("close", props.stat);
}
afterToggleOpen()
}
);
// tree lines
const vLines = computed(() => {
const lines: { style: object }[] = [];
const hasNextVisibleNode = (stat) => {
if (stat.parent) {
let i = stat.parent?.children.indexOf(stat);
do {
i++
let next = stat.parent.children[i]
if (next) {
if (!next.hidden) {
return true
}
} else {
break
}
} while (true);
}
return false
}
const leftOrRight = props.rtl ? 'right' : 'left'
const bottomOrTop = props.btt ? 'top' : 'bottom'
let current = props.stat
while (current) {
let left = (current.level - 2) * props.indent + props.treeLineOffset
const hasNext = hasNextVisibleNode(current)
const addLine = () => {
lines.push({
style: {
[leftOrRight]: left + 'px',
[bottomOrTop]: hasNext ? 0 : '50%',
}
})
}
if (current === props.stat) {
if (current.level > 1) {
addLine()
}
} else if (hasNext) {
addLine()
}
current = current.parent
}
return lines
})
const hLineStyle = computed(() => {
let left = (props.stat.level - 2) * props.indent + props.treeLineOffset
const leftOrRight = props.rtl ? 'right' : 'left'
return {
[leftOrRight]: left + 'px',
}
})
return { indentStyle, vLines, hLineStyle, }
},
// data() {
// return {}
// },
// computed: {},
// watch: {},
// methods: {},
// created() {},
// mounted() {}
});
export default cpt;
export type TreeNodeType = InstanceType<typeof cpt>;
</script>
<style>
/* tree line start */
.tree-node--with-tree-line {
position: relative;
}
.tree-line {
position: absolute;
background-color: #bbbbbb;
}
.tree-vline {
width: 1px;
top: 0;
bottom: 0;
}
.tree-hline {
height: 1px;
top: 50%;
width: 10px;
}
/* tree line end */
</style>