HcTreeData.vue 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. <template>
  2. <ElTree class="hc-tree-node tree-line el-radio-group" :class="[ui,submitCounts?'tree-line1':'']" ref="ElTreeRef" :props="ElTreeProps" :data="datas" highlight-current accordion node-key="primaryKeyId"
  3. :default-expanded-keys="TreeExpandKey" @node-click="ElTreeClick" @node-contextmenu="ElTreeLabelContextMenu" :indent="0" :filter-node-method="filterNode">
  4. <template #default="{ node, data }">
  5. <div class="data-custom-tree-node" :id="`${idPrefix}${data['primaryKeyId']}`">
  6. <!--树组件,节点名称-->
  7. <div class="label" :class="node.level === 1?'level-name':''" style="display: flex;justify-content: space-between;">
  8. <div>
  9. <span :class="data?.colorStatus === 2?'text-blue':data?.colorStatus === 3?'text-orange':data?.colorStatus === 4?'text-green':''" v-if="isColor">{{ node.label }}</span>
  10. <span v-else>{{ node.label }}</span>
  11. </div>
  12. <div class="text-blue submit-counts" v-if="submitCounts">【{{ data.submitCounts ?? 0 }}】</div>
  13. </div>
  14. <!-- <div class="text-blue submit-counts" v-if="isSubmitCounts">【{{ data.submitCounts ?? 0 }}】</div> -->
  15. <!--树组件,操作菜单-->
  16. <div class="menu-icon1" :class="node.showTreeMenu?'show':''" v-if="node.level !== 1 && menusData.length > 0" @click.stop>
  17. <div class="cu-tree-node-popover-menu-icon" @click.prevent.stop="ElTreeLabelContextMenu($event,data,node)">
  18. <HcIcon name="menu" ui="text-2xl"/>
  19. </div>
  20. </div>
  21. <!--树组件,操作菜单 END-->
  22. </div>
  23. </template>
  24. </ElTree>
  25. <!--右键菜单-->
  26. <HcContextMenu ref="contextMenuRef" :datas="menusData" @item-click="handleMenuSelect" v-if="menusData.length > 0">
  27. <template #mark="{item}">
  28. <HcIcon :name="item.icon" :fill="treeRefData?.isFirst" class="menu-item-icon"/>
  29. <span class="menu-item-name">{{treeRefData?.isFirst ? '取消标记为首件' : '标记为首件'}}</span>
  30. </template>
  31. <template #sort="{item}">
  32. <HcIcon :name="item.icon" :line="false" class="menu-item-icon"/>
  33. <span class="menu-item-name">{{item.label}}</span>
  34. </template>
  35. </HcContextMenu>
  36. </template>
  37. <script setup>
  38. import {ref,watch,nextTick} from "vue";
  39. import {getArrValue,getObjValue} from "vue-utils-plus"
  40. //参数
  41. const props = defineProps({
  42. menus: {
  43. type: Array,
  44. default: () => ([])
  45. },
  46. datas: {
  47. type: Array,
  48. default: () => ([])
  49. },
  50. autoExpandKeys: {
  51. type: Array,
  52. default: () => ([])
  53. },
  54. isMark: {
  55. type: Boolean,
  56. default: false
  57. },
  58. idPrefix: {
  59. type: String,
  60. default: 'tree-data-'
  61. },
  62. isAutoKeys: {
  63. type: Boolean,
  64. default: true
  65. },
  66. isAutoClick: {
  67. type: Boolean,
  68. default: true
  69. },
  70. isColor: {
  71. type: Boolean,
  72. default: false
  73. },
  74. submitCounts: {
  75. type: Boolean,
  76. default: false
  77. },
  78. searchTreeVal:{
  79. type: String,
  80. default: '11'
  81. }
  82. })
  83. //变量
  84. const ElTreeRef = ref(null)
  85. const treeRefNode = ref(null)
  86. const treeRefData = ref(null)
  87. const ElTreeProps = ref({
  88. label: 'title',
  89. children: 'children',
  90. isLeaf: 'notExsitChild'
  91. })
  92. const menusData = ref(props.menus)
  93. const menuMark = ref(props.isMark)
  94. const isAutoKeys = ref(props.isAutoKeys)
  95. const TreeExpandKey = ref(props.autoExpandKeys)
  96. const idPrefix = ref(props.idPrefix);
  97. const isSubmitCounts = ref(props.submitCounts);
  98. const searchInfo=ref(props.searchTreeVal)
  99. //监听
  100. watch(() => [
  101. props.menus,
  102. props.isMark,
  103. props.isAutoKeys,
  104. props.autoExpandKeys,
  105. props.idPrefix,
  106. props.submitCounts,
  107. props.searchTreeVal
  108. ], ([menus, isMark, AutoKeys, expandKeys, UserIdPrefix, submitCounts,searchTreeVal]) => {
  109. menusData.value = menus
  110. menuMark.value = isMark
  111. isAutoKeys.value = AutoKeys
  112. TreeExpandKey.value = expandKeys
  113. idPrefix.value = UserIdPrefix
  114. isSubmitCounts.value = submitCounts
  115. searchInfo.value=searchTreeVal
  116. })
  117. watch(searchInfo, (val) => {
  118. if(val){
  119. nextTick(()=> {
  120. ElTreeRef?.value.filter(val)
  121. })
  122. }else{
  123. emit('changeSearch')
  124. }
  125. },
  126. {immediate:true}
  127. )
  128. //事件
  129. const emit = defineEmits(['menuTap','nodeTap','changeSearch'])
  130. //节点被点击
  131. const ElTreeClick = async (data,node) => {
  132. if (isAutoKeys.value) {
  133. let autoKeysArr = []
  134. await getNodeExpandKeys(node, autoKeysArr)
  135. const autoKeys = autoKeysArr.reverse()
  136. emit('nodeTap', {node, data, keys: autoKeys})
  137. } else {
  138. emit('nodeTap', {node, data, keys: []})
  139. }
  140. }
  141. //处理自动展开的节点KEY
  142. // const getNodeExpandKeys = async (node, newKeys) => {
  143. // const parent = getArrValue(node?.parent)
  144. // const nodeData = getObjValue(node?.data);
  145. // console.log(nodeData,'nodeData');
  146. // const primaryKeyId = nodeData?.primaryKeyId ?? ''
  147. // if (primaryKeyId) {
  148. // newKeys.push(primaryKeyId)
  149. // await getNodeExpandKeys(parent, newKeys)
  150. // }
  151. // }
  152. //处理自动展开的节点KEY
  153. const getNodeExpandKeys = async (node, newKeys) => {
  154. const parent = node?.parent ?? []
  155. const primaryKeyId = node?.data?.primaryKeyId ?? ''
  156. if (primaryKeyId) {
  157. newKeys.push(primaryKeyId)
  158. await getNodeExpandKeys(parent, newKeys)
  159. }
  160. }
  161. //鼠标右键事件
  162. const contextMenuRef = ref(null)
  163. const ElTreeLabelContextMenu = (e,data,node) => {
  164. const rows = menusData.value || [];
  165. if (node.level !== 1 && rows.length > 0) {
  166. e.preventDefault();
  167. treeRefNode.value = node;
  168. treeRefData.value = data;
  169. //展开菜单
  170. contextMenuRef.value?.showMenu(e)
  171. }
  172. }
  173. //鼠标右键菜单被点击
  174. const handleMenuSelect = async({key}) => {
  175. const node = treeRefNode.value;
  176. const data = treeRefData.value;
  177. //如果为标记菜单
  178. if (key === 'mark' && menuMark.value) {
  179. if (data.isFirst === true) {
  180. emit('menuTap', {key: 'cancel_mark', node, data})
  181. } else {
  182. emit('menuTap', {key: 'mark', node, data})
  183. }
  184. } else {
  185. // emit('menuTap', {key, node, data})
  186. if (isAutoKeys.value) {
  187. let autoKeysArr = []
  188. await getNodeExpandKeys(node, autoKeysArr)
  189. const autoKeys = autoKeysArr.reverse()
  190. emit('menuTap', {key, node, data,keys:autoKeys})
  191. }
  192. }
  193. }
  194. //设置树菜单的标记数据
  195. const setElTreeMenuMark = (keys,isFirst) => {
  196. keys.forEach(item => {
  197. //根据 data 或者 key 拿到 Tree 组件中的 node
  198. let node = ElTreeRef.value.getNode(item)
  199. if (!!node) node.data.isFirst = isFirst;
  200. })
  201. }
  202. //设置树菜单的标记数据
  203. const removeElTreeNode = (key) => {
  204. //根据 data 或者 key 拿到 Tree 组件中的 node
  205. let node = ElTreeRef.value.getNode(key)
  206. //删除 Tree 中的一个节点,使用此方法必须设置 node-key 属性
  207. ElTreeRef.value.remove(node)
  208. }
  209. const getReturnNode=(node,_array,value)=>{
  210. let isPass = node.data && node.data.title && node.data.title.indexOf(value) !== -1;
  211. isPass?_array.push(isPass):'';
  212. if(!isPass && node.level!=1 && node.parent){
  213. getReturnNode(node.parent,_array,value);
  214. }
  215. }
  216. const filterNode = (value, data,node) => {
  217. if(!value){
  218. return true;
  219. }
  220. let level = node.title;
  221. let _array = [];//这里使用数组存储 只是为了存储值。
  222. getReturnNode(node,_array,value);
  223. let result = false;
  224. _array.forEach((item)=>{
  225. result = result || item;
  226. });
  227. return result;
  228. }
  229. // 暴露出去
  230. defineExpose({
  231. setElTreeMenuMark,
  232. removeElTreeNode,
  233. filterNode
  234. })
  235. </script>
  236. <style lang="scss" scoped>
  237. @import "../../../styles/app/tree.scss";
  238. .el-radio-group {
  239. width: auto;
  240. }
  241. // .data-custom-tree-node {
  242. // .menu-icon {
  243. // position: relative;
  244. // font-size: 20px;
  245. // opacity: 0;
  246. // pointer-events: none;
  247. // transition: opacity 0.2s;
  248. // .cu-tree-node-popover-menu-icon {
  249. // display: flex;
  250. // align-items: center;
  251. // justify-content: center;
  252. // }
  253. // }
  254. // &:hover {
  255. // .menu-icon {
  256. // opacity: 1;
  257. // pointer-events: all;
  258. // cursor: context-menu;
  259. // }
  260. // }
  261. // .menu-icon.show {
  262. // opacity: 1;
  263. // pointer-events: all;
  264. // cursor: context-menu;
  265. // }
  266. // }
  267. .data-custom-tree-node {
  268. .menu-icon1 {
  269. // position: absolute;
  270. vertical-align: bottom;
  271. display:inline-block;
  272. pointer-events: none;
  273. transition: opacity 0.2s;
  274. opacity: 0;
  275. right: 0;
  276. background: rgba(255, 255, 255, 0.25);
  277. border-radius: 2px;
  278. .cu-tree-node-popover-menu-icon {
  279. display: flex;
  280. align-items: center;
  281. justify-content: center;
  282. }
  283. }
  284. &:hover {
  285. .menu-icon1 {
  286. opacity: 1;
  287. pointer-events: all;
  288. cursor: context-menu;
  289. }
  290. }
  291. .menu-icon1.show {
  292. opacity: 1;
  293. pointer-events: all;
  294. cursor: context-menu;
  295. }
  296. }
  297. </style>