|
@@ -1,11 +1,67 @@
|
|
|
<template>
|
|
|
<el-tree-v2
|
|
|
- ref="ElTreeRef" :class="[ui]" :data="datas" :props="ElTreeProps"
|
|
|
- class="hc-tree-node" :filter-method="filterMethod" :height="treeHeight"></el-tree-v2>
|
|
|
+ ref="ElTreeRef"
|
|
|
+ :class="[ui,submitCounts?'tree-line1':'']"
|
|
|
+ :data="datas"
|
|
|
+ :props="ElTreeProps"
|
|
|
+ class="hc-tree-node tree-line el-radio-group"
|
|
|
+ :filter-method="filterMethod"
|
|
|
+ :height="treeHeight"
|
|
|
+ @node-click="ElTreeClick"
|
|
|
+ accordion
|
|
|
+ highlight-current
|
|
|
+ :default-expand-all="false"
|
|
|
+ node-key="primaryKeyId"
|
|
|
+
|
|
|
+ @node-contextmenu="ElTreeLabelContextMenu"
|
|
|
+ >
|
|
|
+ <template #default="{ node, data }">
|
|
|
+ <div :id="`${idPrefix}${data['primaryKeyId']}`" class="data-custom-tree-node">
|
|
|
+ <!--树组件,节点名称-->
|
|
|
+ <div :class="node.level === 1?'level-name':''" class="label">
|
|
|
+ <span
|
|
|
+ v-if="(data['type'] > 0 && data['majorDataType'] > 0) || (data['type'] > 0 && data['majorDataType'] <= 0)"
|
|
|
+ class="hc-tree-node-type">{{
|
|
|
+ getTreeNodeType(data['type'], data['majorDataType'])
|
|
|
+ }}</span>
|
|
|
+ <span
|
|
|
+ v-if="isColor"
|
|
|
+ :class="data?.colorStatus === 2?'text-blue':data?.colorStatus === 3?'text-orange':data?.colorStatus === 4?'text-green':''">{{
|
|
|
+ node.label
|
|
|
+ }}</span>
|
|
|
+ <span v-else>{{ node.label }}</span>
|
|
|
+ </div>
|
|
|
+ <div v-if="isSubmitCounts" class="text-blue submit-counts">【{{ data.submitCounts ?? 0 }}】</div>
|
|
|
+ <!--树组件,操作菜单-->
|
|
|
+ <div v-if="node.level !== 1 && menusData.length > 0" :class="node.showTreeMenu?'show':''"
|
|
|
+ class="menu-icon1" @click.stop>
|
|
|
+ <div class="cu-tree-node-popover-menu-icon"
|
|
|
+ @click.prevent.stop="ElTreeLabelContextMenu($event,data,node)">
|
|
|
+ <HcIcon name="apps" ui="text-2xl"/>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <!--树组件,操作菜单 END-->
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-tree-v2>
|
|
|
+ <!--右键菜单-->
|
|
|
+ <!--右键菜单-->
|
|
|
+ <!-- <HcContextMenu v-if="menusData.length > 0" ref="contextMenuRef" :datas="menusData" @item-click="handleMenuSelect">
|
|
|
+ <template #mark="{item}">
|
|
|
+ <HcIcon :fill="treeRefData?.isFirst" :name="item.icon" class="menu-item-icon"/>
|
|
|
+ <span class="menu-item-name">{{ treeRefData?.isFirst ? '取消标记为首件' : '标记为首件' }}</span>
|
|
|
+ </template>
|
|
|
+ <template #sort="{item}">
|
|
|
+ <HcIcon :line="false" :name="item.icon" class="menu-item-icon"/>
|
|
|
+ <span class="menu-item-name">{{ item.label }}</span>
|
|
|
+ </template>
|
|
|
+ </HcContextMenu> -->
|
|
|
+
|
|
|
</template>
|
|
|
|
|
|
<script setup>
|
|
|
import {ref, watch, nextTick} from "vue";
|
|
|
+import {getTreeNodeType} from '~uti/utils';
|
|
|
//参数
|
|
|
const props = defineProps({
|
|
|
ui: {
|
|
@@ -28,6 +84,22 @@ const props = defineProps({
|
|
|
type: String,
|
|
|
default: ''
|
|
|
},
|
|
|
+ ElTreeLoadNode: {
|
|
|
+ type: Boolean,
|
|
|
+ default: true
|
|
|
+ },
|
|
|
+ idPrefix: {
|
|
|
+ type: String,
|
|
|
+ default: 'tree-data-'
|
|
|
+ },
|
|
|
+ isColor: {
|
|
|
+ type: Boolean,
|
|
|
+ default: false
|
|
|
+ },
|
|
|
+ isAutoKeys: {
|
|
|
+ type: Boolean,
|
|
|
+ default: true
|
|
|
+ },
|
|
|
})
|
|
|
|
|
|
//变量
|
|
@@ -41,44 +113,206 @@ const treeHeight = ref(0)
|
|
|
|
|
|
const isSubmitCounts = ref(props.submitCounts);
|
|
|
const searchInfo = ref(props.searchTreeVal)
|
|
|
+const idPrefix = ref(props.idPrefix);
|
|
|
+const menusData = ref(props.menus)
|
|
|
+const isAutoKeys = ref(props.isAutoKeys)
|
|
|
+const treeRefData = ref(null)
|
|
|
+
|
|
|
|
|
|
//监听
|
|
|
watch(() => [
|
|
|
props.height,
|
|
|
props.submitCounts,
|
|
|
props.searchTreeVal,
|
|
|
-], ([height, submitCounts, searchTreeVal]) => {
|
|
|
+ props.datas,
|
|
|
+ props.ElTreeLoadNode,
|
|
|
+ props.idPrefix,
|
|
|
+ props.menus,
|
|
|
+ props.isAutoKeys,
|
|
|
+], ([height, submitCounts, searchTreeVal,UserIdPrefix,menus,AutoKeys]) => {
|
|
|
console.log('height', height)
|
|
|
treeHeight.value = height
|
|
|
isSubmitCounts.value = submitCounts
|
|
|
searchInfo.value = searchTreeVal
|
|
|
+ idPrefix.value = UserIdPrefix
|
|
|
+ menusData.value = menus
|
|
|
+ isAutoKeys.value = AutoKeys
|
|
|
})
|
|
|
|
|
|
+watch(searchInfo, (val) => {
|
|
|
+ if (val) {
|
|
|
+ nextTick(() => {
|
|
|
+
|
|
|
+ ElTreeRef?.value.filter(val)
|
|
|
+ })
|
|
|
+
|
|
|
+ } else {
|
|
|
+ emit('changeSearch')
|
|
|
+ }
|
|
|
+
|
|
|
+ },
|
|
|
+
|
|
|
+ {immediate: true}
|
|
|
+)
|
|
|
//事件
|
|
|
const emit = defineEmits(['menuTap', 'nodeTap', 'changeSearch', 'changetreelaod'])
|
|
|
|
|
|
+//节点被点击
|
|
|
+//处理自动展开的节点KEY
|
|
|
+const getNodeExpandKeys = async (node, newKeys) => {
|
|
|
+ const parent = node?.parent ?? []
|
|
|
+ const primaryKeyId = node?.data?.primaryKeyId ?? ''
|
|
|
+ if (primaryKeyId) {
|
|
|
+ newKeys.push(primaryKeyId)
|
|
|
+ await getNodeExpandKeys(parent, newKeys)
|
|
|
+ }
|
|
|
+}
|
|
|
//节点被点击
|
|
|
const ElTreeClick = async (data, node) => {
|
|
|
- console.log('node', node)
|
|
|
- console.log('data', data)
|
|
|
+ if (isAutoKeys.value) {
|
|
|
+ let autoKeysArr = []
|
|
|
+ await getNodeExpandKeys(node, autoKeysArr)
|
|
|
+ const autoKeys = autoKeysArr.reverse()
|
|
|
+ emit('nodeTap', {node, data, keys: autoKeys})
|
|
|
+ } else {
|
|
|
+ emit('nodeTap', {node, data, keys: []})
|
|
|
+ }
|
|
|
+}
|
|
|
+const getReturnNode = (node, _array, value) => {
|
|
|
+ let isPass = node && node.title && node.title.indexOf(value) !== -1;
|
|
|
+
|
|
|
+ isPass ? _array.push(isPass) : '';
|
|
|
+ if (!isPass && node.level != 1 && node.parent) {
|
|
|
+ getReturnNode(node.parent, _array, value);
|
|
|
+ }
|
|
|
}
|
|
|
-
|
|
|
//筛选树节点
|
|
|
const filterMethod = (query, node) => {
|
|
|
- console.log('query',query)
|
|
|
- return node.label?.includes(query)
|
|
|
+ if (!query) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ let level = node.title;
|
|
|
+ let _array = [];//这里使用数组存储 只是为了存储值。
|
|
|
+ getReturnNode(node, _array, query);
|
|
|
+ let result = false;
|
|
|
+ _array.forEach((item) => {
|
|
|
+ result = result || item;
|
|
|
+ });
|
|
|
+ setTimeout(() => {
|
|
|
+ emit('changetreelaod', false)
|
|
|
+ }, 1000)
|
|
|
+ return result;
|
|
|
+
|
|
|
+}
|
|
|
+//鼠标右键事件
|
|
|
+const contextMenuRef = ref(null)
|
|
|
+const ElTreeLabelContextMenu = (e, data, node) => {
|
|
|
+ const rows = menusData.value || [];
|
|
|
+ if (node.level !== 1 && rows.length > 0) {
|
|
|
+ e.preventDefault();
|
|
|
+ treeRefNode.value = node;
|
|
|
+ treeRefData.value = data;
|
|
|
+ //展开菜单
|
|
|
+ contextMenuRef.value?.showMenu(e)
|
|
|
+ }
|
|
|
+}
|
|
|
+//鼠标右键菜单被点击
|
|
|
+const handleMenuSelect = async ({key}) => {
|
|
|
+ const node = treeRefNode.value;
|
|
|
+ const data = treeRefData.value;
|
|
|
+ //如果为标记菜单
|
|
|
+ if (key === 'mark' && menuMark.value) {
|
|
|
+ if (data.isFirst === true) {
|
|
|
+ emit('menuTap', {key: 'cancel_mark', node, data})
|
|
|
+ } else {
|
|
|
+ emit('menuTap', {key: 'mark', node, data})
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // emit('menuTap', {key, node, data})
|
|
|
+ if (isAutoKeys.value) {
|
|
|
+ let autoKeysArr = []
|
|
|
+ await getNodeExpandKeys(node, autoKeysArr)
|
|
|
+ const autoKeys = autoKeysArr.reverse()
|
|
|
+ emit('menuTap', {key, node, data, keys: autoKeys})
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+//设置树菜单的标记数据
|
|
|
+const setElTreeMenuMark = (keys, isFirst) => {
|
|
|
+ keys.forEach(item => {
|
|
|
+ //根据 data 或者 key 拿到 Tree 组件中的 node
|
|
|
+ let node = ElTreeRef.value.getNode(item)
|
|
|
+ if (!!node) node.data.isFirst = isFirst;
|
|
|
+ })
|
|
|
+}
|
|
|
+//设置树菜单的标记数据
|
|
|
+const removeElTreeNode = (key) => {
|
|
|
+ //根据 data 或者 key 拿到 Tree 组件中的 node
|
|
|
+ let node = ElTreeRef.value.getNode(key)
|
|
|
+ //删除 Tree 中的一个节点,使用此方法必须设置 node-key 属性
|
|
|
+ ElTreeRef.value.remove(node)
|
|
|
}
|
|
|
-
|
|
|
// 暴露出去
|
|
|
defineExpose({
|
|
|
-
|
|
|
+ setElTreeMenuMark,
|
|
|
+ removeElTreeNode,
|
|
|
+ filterMethod
|
|
|
})
|
|
|
</script>
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
@import "../../../styles/app/tree.scss";
|
|
|
+.el-radio-group {
|
|
|
+ width: 100% !important;
|
|
|
+ display: inline-grid;
|
|
|
+}
|
|
|
+.data-custom-tree-node {
|
|
|
+ flex: 1;
|
|
|
+ white-space: nowrap;
|
|
|
+ overflow: hidden;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+ .label {
|
|
|
+ flex: 1;
|
|
|
+ white-space: nowrap;
|
|
|
+ overflow: hidden;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+ }
|
|
|
+ .submit-counts {
|
|
|
+ position: unset;
|
|
|
+ font-size: 14px;
|
|
|
+ }
|
|
|
+ .menu-icon1 {
|
|
|
+ position: unset;
|
|
|
+ vertical-align: bottom;
|
|
|
+ display: inline-block;
|
|
|
+ pointer-events: none;
|
|
|
+ transition: opacity 0.2s;
|
|
|
+ opacity: 0;
|
|
|
+ right: 0;
|
|
|
+ background: rgba(255, 255, 255, 0.25);
|
|
|
+ border-radius: 2px;
|
|
|
+ .cu-tree-node-popover-menu-icon {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ &:hover {
|
|
|
+ .menu-icon1 {
|
|
|
+ opacity: 1;
|
|
|
+ pointer-events: all;
|
|
|
+ cursor: context-menu;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .menu-icon1.show {
|
|
|
+ opacity: 1;
|
|
|
+ pointer-events: all;
|
|
|
+ cursor: context-menu;
|
|
|
+ }
|
|
|
+}
|
|
|
</style>
|
|
|
<style lang="scss">
|
|
|
+
|
|
|
.el-tree.hc-tree-node .el-tree-node {
|
|
|
white-space: nowrap;
|
|
|
overflow: hidden;
|