vue -- 实现MenuTree组件

Published: · LastMod: April 07, 2024 · 1355 words

vue – 实现MenuTree组件 🔗

样例 🔗

image.png

具体实现 🔗

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
<template>
  <div class="Tree-content">
    <div
      v-for="(item, idx) in computedTree"
      :key="idx"
      :class="{
        'Tree-Title': true,
        'Tree-SecondLevelTitle': item.$depth > 0,
        'Tree-FirstLevelTitle': item.$depth == 0,
        active: item.id === computedValue,
      }"
    >
      <div class="Tree-Item" @click="onHandleClick(item)">
        <div class="Tree-ItemTxt">{{ item[computedFieldNames.label] }}</div>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  name: "menu-tree",
  model: {
    prop: "value",
    event: "change",
  },
  props: {
    options: {
      type: Array,
      default: () => [],
    },
    value: [String, Number],
    fieldNames: {
      type: Object,
    },
  },
  computed: {
    computedValue: {
      get() {
        return this.value;
      },
      set(row) {
        this.$emit("change", row.id);
      },
    },
    computedTree() {
      return this.genTree([...this.options]);
    },
    computedFieldNames() {
      return {
        children: "children",
        label: "label",
        key: "key",
        ...(this.fieldNames || {}),
      };
    },
  },
  methods: {
    onHandleClick(row) {
      this.$emit("change", row.id);
    },
    genTree(tree = []) {
      const data = [];
      if (tree.length > 0) {
        this._doLoopTreeWithDepth(tree, 0, data);
      }
      return data;
    },
    _doLoopTreeWithDepth(list, depth = 0, data = []) {
      list.forEach((el, idx) => {
        el.$depth = depth;
        if (!el.id) {
          el.id = `t-${depth}-${idx}`;
        }
        data.push(el);
        if (el[this.computedFieldNames.children]) {
          this._doLoopTreeWithDepth(
            el[this.computedFieldNames.children],
            depth + 1,
            data
          );
        }
      });
    },
  },
};
</script>

<style lang="scss" scoped>
@mixin dot {
  content: " ";
  position: absolute;
  background-color: rgb(133, 144, 166);
  display: inline-block;
  border-radius: 50%;
  top: 12px;
  margin-right: 12px;
}
.Tree {
  &-content {
    box-sizing: border-box;
    margin: 0px;
    min-width: 0px;
    flex-direction: column;
    font-size: 12px;
    display: flex;
  }

  &-Title {
    box-sizing: border-box;
    margin: 0px;
    min-width: 0px;
    flex-shrink: 0;
    display: flex;
    -webkit-box-align: center;
    align-items: center;
    color: rgb(133, 144, 166);
    font-size: 12px;
    padding-left: 30px;
    font-weight: 500;
    position: relative;
    height: 30px;
    line-height: 30px;
  }

  &-FirstLevelTitle::before {
    @include dot;
    left: 16px;
    width: 6px;
    height: 6px;
  }
  &-SecondLevelTitle::before {
    @include dot;
    left: 17px;
    width: 4px;
    height: 4px;
  }
  &-FirstLevelTitle,
  &-SecondLevelTitle {
    &::after {
      content: " ";
      display: block;
      position: absolute;
      left: 19px;
      top: 0px;
      transform: translateX(-50%);
      width: 2px;
      height: 40px;
      margin-top: 12px;
      background: rgba(133, 144, 166, 0.12);
    }
    &.active {
      color: rgb(0, 102, 255);
    }
    &.active::before {
      background-color: rgb(0, 102, 255);
    }
  }
  &-Title:last-child::after {
    height: 0px;
  }
  &-Item {
    box-sizing: border-box;
    margin: 0px;
    min-width: 0px;
    padding-left: 10px;
    padding-right: 10px;
    display: flex;
    cursor: pointer;
    -webkit-box-align: center;
    align-items: center;
    width: 100%;

    &:hover {
      color: rgb(0, 102, 255);
      background: rgb(235, 235, 235);
      border-radius: 4px;
    }
  }

  &-ItemTxt {
    box-sizing: border-box;
    margin: 0px;
    min-width: 0px;
    display: -webkit-box;
    text-overflow: ellipsis;
    overflow: hidden;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 1;
    pointer-events: none;
    word-break: break-all;
    width: calc(100% - 22px);
  }
}
</style>

使用方法 🔗

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
<template>
  <div class="css-box">
    <side-tree v-model="value" :options="tree" @change="onClick"></side-tree>
  </div>
</template>

<script>
import SideTree from "./side-tree.vue";
export default {
  components: {
    SideTree,
  },
  data() {
    return {
      value: 't-1-2',
      tree: [
        {
          label: "零、基础知识",
        },
        {
          label: "一、安装VScode编辑器和MinGW编译器",
          children: [
            {
              label: "1.VScode编辑器",
            },
            {
              label: "1.2.安装VScode扩展插件",
            },
            {
              label: "2.MinGW编译器编译器",
            },
            {
              label: "2.1.下载编译器",
            },
            {
              label: "2.2.安装编译器",
            },
            {
              label: "2.3.编译器验证",
            },
          ],
        },
        {
          label: "二、程序文件的架构",
          children: [
            {
              label: "1.语言学习环境",
            },
            {
              label: "1.1单文件编译运行调试",
            },
            {
              label: "1.2简单多文件编译运行调试",
            },
            {
              label: "2.实际项目开发环境",
            },
          ],
        },
        {
          label: "三、配置一个新项目",
          children: [
            {
              label: "1.创建配置c_cpp_properties.json文件",
            },
            {
              label: "2.创建配置tasks.json文件",
            },
            {
              label: "3.创建配置launch.json文件",
            },
          ],
        },
        {
          label: "四、一些小tips",
          children: [
            {
              label: "1.多文件编译",
            },
            {
              label: "2.中文乱码",
            },
          ],
        },
      ],
    };
  },
  methods: {
    onClick(row) {
      console.log(row);
    },
  },
};
</script>

<style  lang="scss" scoped>
.css-box {
  width: 250px;
  box-sizing: border-box;
  margin: 20px 0px 0px;
  min-width: 0px;
  height: 600px;
  overflow: scroll;
}
</style>

prop 🔗

参数说明类型
v-model值双向绑定string|number
value当前值string|number
options需要展示的树形数据
fieldNames绑定数据的属性值object| {children: “children”,label: “label”,key: “key”, }