TreeNode.vue 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. <template>
  2. <div class="tree-node-container" :style="containerStyle" ref="node">
  3. <div class="tree-node" :style="cardStyle" @click="nodeClick(id)">
  4. <ElCard class="tree-node-card" shadow="hover">
  5. <template #header>
  6. <div class="card-header">
  7. <span class="node-title">{{title}}</span>
  8. <ElTag class="node-state" :type="getTagTypeFromState(state)">{{state}}</ElTag>
  9. </div>
  10. </template>
  11. </ElCard>
  12. </div>
  13. <div class="node-postfix">
  14. <div class="icon" target="delete" @click="deleteNode(id)"></div>
  15. <template v-if="depth!==4">
  16. <div class="icon" target="add" @click="addChild(id)"></div>
  17. </template>
  18. </div>
  19. </div>
  20. </template>
  21. <script>
  22. const getTagTypeFromState = (state) => {
  23. if (state === '已创建') {
  24. return '';
  25. } if (state === '进行中') {
  26. return 'success';
  27. } if (state === '已完成') {
  28. return 'info';
  29. }
  30. return 'danger';
  31. };
  32. export default {
  33. name: 'TreeNode',
  34. props: {
  35. id: Number,
  36. state: String,
  37. title: String,
  38. positionY: Number,
  39. positionX: Number,
  40. nodeStyle: Object,
  41. depth: Number,
  42. },
  43. computed: {
  44. containerStyle() {
  45. return {
  46. top: `${this.positionY * 130}px`,
  47. left: `${280 * this.positionX}px`,
  48. width: `${this.nodeStyle.width + this.nodeStyle.padding * 2}px`,
  49. height: `${this.nodeStyle.height + this.nodeStyle.padding * 2}px`,
  50. };
  51. },
  52. cardStyle() {
  53. return {
  54. width: `${this.nodeStyle.width}px`,
  55. height: `${this.nodeStyle.height}px`,
  56. };
  57. },
  58. },
  59. methods: {
  60. getTagTypeFromState,
  61. getTreeNodeSizeAndPosition() {
  62. return {
  63. top: this.$refs.node.offsetTop + this.nodeStyle.padding,
  64. left: this.$refs.node.offsetLeft + this.nodeStyle.padding,
  65. height: this.nodeStyle.height,
  66. width: this.nodeStyle.width,
  67. };
  68. },
  69. addChild(id) {
  70. this.$emit('add-child', id);
  71. },
  72. deleteNode(id) {
  73. this.$emit('delete-node', id);
  74. },
  75. nodeClick(id) {
  76. this.$emit('node-click', id);
  77. },
  78. },
  79. };
  80. </script>
  81. <style lang="scss" scoped>
  82. .tree-node-container {
  83. position: absolute;
  84. box-sizing: border-box;
  85. display: flex;
  86. justify-content: center;
  87. align-items: center;
  88. user-select: none;
  89. & .node-postfix {
  90. position: absolute;
  91. bottom: 0px;
  92. right: 25px;
  93. width: 100%;
  94. display: none;
  95. }
  96. &:hover .node-postfix {
  97. display: block;
  98. }
  99. }
  100. .tree-node {
  101. box-sizing: border-box;
  102. }
  103. .card-header {
  104. display: flex;
  105. justify-content: space-between;
  106. }
  107. .icon {
  108. width: 14px;
  109. height: 14px;
  110. float: right;
  111. background-position: center;
  112. background-repeat: no-repeat;
  113. background-size: 100% 100%;
  114. margin-top: 10px;
  115. margin-right: 10px;
  116. transition: all .2s linear;
  117. }
  118. .icon:hover{
  119. cursor: pointer;
  120. transition-duration: 0.2s;
  121. transform: scale(1.1);
  122. transition-timing-function:ease-in-out;
  123. }
  124. .icon[target=add]{
  125. background-image: url("../assets/icon/add.svg");
  126. }
  127. .icon[target=delete]{
  128. background-image: url("../assets/icon/delete.svg");
  129. }
  130. </style>