vue中手写一个树形表格(可动态添加行数)

90 阅读4分钟

树形组件(子组件)

<template>
  <div class="tree-table">
    <div class="table-header">
      <div class="table-row">
        <div class="table-cell">标题</div>
        <div class="table-cell">项目进度</div>
        <div class="table-cell">开始时间</div>
        <div class="table-cell">截至时间</div>
        <div class="table-cell">所属模块</div>
        <div class="table-cell">负责人</div>
        <div class="table-cell">优先级</div>
        <div class="table-cell">状态</div>
      </div>
    </div>
    <!-- <div class="table-body">
      <TreeNode v-for="item in data" :key="item.id" :node="item" :parentNode="data" />
    </div> -->
    <div class="table-body" v-for="item in data" :key="item.id">
      <TreeNode :parentNode="data" :node="item" />
    </div>
  </div>
</template>

<script>
import TreeNode from './components/TreeNode.vue'; // 递归组件

export default {
  components: {
    TreeNode
  },
  data() {
    return {
      data: [
        {
          id: 1,
          level: 1,
          title: '节点1',
          progress: 0.3,
          startTime: '2024-06-30',
          endTime: '2024-09-30',
          module: '热电偶',
          director: '文歆',
          priority: 1,
          status: 0,
          expanded: false,
          children: [
            {
              id: 3,
              level: 2,
              title: '子节点1-1',
              progress: 0.3,
              startTime: '2024-06-30',
              endTime: '2024-09-30',
              module: '热电偶',
              director: '文歆',
              priority: 1,
              status: 0,
              expanded: false,
              children: [
                {
                  id: 5,
                  level: 3,
                  title: '孙节点1-1-1',
                  progress: 0.3,
                  startTime: '2024-06-30',
                  endTime: '2024-09-30',
                  module: '热电偶',
                  director: '文歆',
                  priority: 1,
                  status: 0,
                  expanded: true,
                  children: [{
                    id: 6,
                    title: '孙孙节点1-1-1-1',
                    progress: 0.3,
                    startTime: '2024-06-30',
                    endTime: '2024-09-30',
                    module: '热电偶',
                    director: '文歆',
                    priority: 1,
                    status: 0,
                    children: [], //一定要有这个字段,否则子级不会显示出来
                    // expanded: true,
                  }]
                },
              ],
            },
            {
              id: 4,
              level: 2,
              title: '子节点1-2',
              progress: 0.3,
              startTime: '2024-06-30',
              endTime: '2024-09-30',
              module: '热电偶',
              director: '文歆',
              priority: 1,
              status: 0,
              expanded: false,
              children: [],
            },
          ],
        },
        {
          id: 1,
          level: 1,
          title: '节点1',
          progress: 0.3,
          startTime: '2024-06-30',
          endTime: '2024-09-30',
          module: '热电偶',
          director: '文歆',
          priority: 1,
          status: 0,
          expanded: false,
          children: [
            {
              id: 3,
              level: 2,
              title: '子节点1-1',
              progress: 0.3,
              startTime: '2024-06-30',
              endTime: '2024-09-30',
              module: '热电偶',
              director: '文歆',
              priority: 1,
              status: 0,
              expanded: false,
              children: [
                {
                  id: 5,
                  level: 3,
                  title: '孙节点1-1-1',
                  progress: 0.3,
                  startTime: '2024-06-30',
                  endTime: '2024-09-30',
                  module: '热电偶',
                  director: '文歆',
                  priority: 1,
                  status: 0,
                  expanded: true,
                  children: [{
                    id: 6,
                    title: '孙孙节点1-1-1-1',
                    progress: 0.3,
                    startTime: '2024-06-30',
                    endTime: '2024-09-30',
                    module: '热电偶',
                    director: '文歆',
                    priority: 1,
                    status: 0,
                    children: [], //一定要有这个字段,否则子级不会显示出来
                    // expanded: true,
                  }]
                },
              ],
            },
            {
              id: 4,
              level: 2,
              title: '子节点1-2',
              progress: 0.3,
              startTime: '2024-06-30',
              endTime: '2024-09-30',
              module: '热电偶',
              director: '文歆',
              priority: 1,
              status: 0,
              expanded: false,
              children: [],
            },
          ],
        },
        {
          id: 1,
          level: 1,
          title: '节点1',
          progress: 0.3,
          startTime: '2024-06-30',
          endTime: '2024-09-30',
          module: '热电偶',
          director: '文歆',
          priority: 1,
          status: 0,
          expanded: false,
          children: [
            {
              id: 3,
              level: 2,
              title: '子节点1-1',
              progress: 0.3,
              startTime: '2024-06-30',
              endTime: '2024-09-30',
              module: '热电偶',
              director: '文歆',
              priority: 1,
              status: 0,
              expanded: false,
              children: [
                {
                  id: 5,
                  level: 3,
                  title: '孙节点1-1-1',
                  progress: 0.3,
                  startTime: '2024-06-30',
                  endTime: '2024-09-30',
                  module: '热电偶',
                  director: '文歆',
                  priority: 1,
                  status: 0,
                  expanded: true,
                  children: [{
                    id: 6,
                    title: '孙孙节点1-1-1-1',
                    progress: 0.3,
                    startTime: '2024-06-30',
                    endTime: '2024-09-30',
                    module: '热电偶',
                    director: '文歆',
                    priority: 1,
                    status: 0,
                    children: [], //一定要有这个字段,否则子级不会显示出来
                    // expanded: true,
                  }]
                },
              ],
            },
            {
              id: 4,
              level: 2,
              title: '子节点1-2',
              progress: 0.3,
              startTime: '2024-06-30',
              endTime: '2024-09-30',
              module: '热电偶',
              director: '文歆',
              priority: 1,
              status: 0,
              expanded: false,
              children: [],
            },
          ],
        }
      ],
    };
  },
};
</script>

<style>
.tree-table {
  border: 1px solid #ccc;
  width: 100%;
}

.table-header {
  background: #f1f1f1;
}

.table-row {
  display: flex;
  /* padding: 8px; */
  cursor: pointer;
}

.table-cell {
  display: flex;
  border-top: 1px solid #ccc;
  border-right: 1px solid #ccc;
  flex: 1;
  height: 48px;
  line-height: 48px;
  /* text-align: center; */
}

.children {
  /* padding-left: 20px; */
}

.toggle-icon {
  /* margin-right: 5px; */
}
</style>

父组件中引入树形表格

<template>
    <div>
        <div class="table-row" v-if="node.level == 1">
            <div class="table-cell">
                <!-- <span @click="toggleExpand"
                    style="display: inline-block;width: 48px;height:48px;line-height:48px;text-align: center;"
                    class="el-icon-arrow-down toggle-icon" v-if="node.children.length && node.expanded"></span>
                <span @click="toggleExpand"
                    style="display: inline-block;width: 48px;height:48px;line-height:48px;text-align: center;"
                    class="el-icon-arrow-right toggle-icon" v-if="node.children.length && !node.expanded"></span> -->
                <span @click="toggleExpand"
                    style="display: inline-block;width: 48px;height:48px;line-height:48px;text-align: center;"
                    :class="['toggle-icon', node.expanded ? 'el-icon-arrow-down' : 'el-icon-arrow-right']"></span>
                <span @click="textToInput(node.title)" v-if="oneLevel">{{ node.title }}</span>
                <el-input @blur="closeOneLevelInput" v-model="oneInput" v-else style="width: 120px;"></el-input>
            </div>
        </div>


        <div class="table-row" v-else>
            <div class="table-cell">
                <span
                    style="border-right:1px solid #ccc;display: inline-block;width: 48px;height: 48px;line-height: 48px;text-align: center;">{{
            node.id }}</span>
                <div :style="{ paddingLeft: `${depth * 20}px` }">
                    <!-- <span style="display: inline-block;width: 20px;text-align: center;"
                        class="el-icon-arrow-down toggle-icon" v-if="node.children.length && node.expanded"></span>
                    <span style="display: inline-block;width: 20px;text-align: center;"
                        class="el-icon-arrow-right toggle-icon" v-if="node.children.length && !node.expanded"></span> -->
                    <span v-if="node.children.length" @click="toggleExpand"
                        style="display: inline-block;width: 20px;text-align: center;"
                        :class="['toggle-icon', node.expanded ? 'el-icon-arrow-down' : 'el-icon-arrow-right']"></span>

                    <span>{{ node.title }}</span>
                </div>
            </div>
            <div class="table-cell">{{ node.progress }}</div>
            <div class="table-cell">
                <span @click="textToSDatePicker(node.startTime)" v-if="!isShowStartTimePicker">{{ node.startTime
                    }}</span>
                <el-date-picker @blur="closeSDatePicker" v-model="startTime" type="date" placeholder="选择日期"
                    v-if="isShowStartTimePicker">
                </el-date-picker>
            </div>
            <div class="table-cell">
                <span @click="textToEDatePicker(node.endTime)" v-if="!isShowEndTimePicker">{{ node.endTime }}</span>
                <el-date-picker @blur="closeEDatePicker" v-model="endTime" type="date" placeholder="选择日期"
                    v-if="isShowEndTimePicker">
                </el-date-picker>
            </div>
            <div class="table-cell">{{ node.module }}</div>
            <div class="table-cell">{{ node.director }}</div>
            <div class="table-cell">{{ node.priority }}</div>
            <div class="table-cell">{{ node.status }}</div>
        </div>

        <div v-if="node.expanded && node.children.length" class="children">
            <TreeNode v-for="child in node.children" :key="child.id" :node="child" :parentNode="node.children"
                :depth="depth + 1" />
        </div>
        <div v-if="node.level == 2 && isLastChild(node, parentNode)"
            style="height: 48px;border-top: 1px solid #ccc;padding-left:10px;display: flex;align-items: center;">
            <span @click="addRow(node, parentNode)" class="el-icon-circle-plus-outline" style="font-size: 22px;"></span>
        </div>
    </div>
</template>

<script>
export default {
    name: 'TreeNode',
    props: {
        parentNode: {
            type: Array,
            required: true,
        },
        node: {
            type: Object,
            required: true,
        },
        depth: {
            type: Number,
            default: 0
        }
    },
    data() {
        return {
            startTime: '',
            endTime: '',
            oneLevel: true,
            isShowStartTimePicker: false,
            isShowEndTimePicker: false,
            oneInput: '',
        }
    },
    methods: {
        addRow(node, parentNode) {
            this.parentNode.push({
                id: 7,
                level: 2,
                title: '子节点1-3',
                progress: 0.3,
                startTime: '2024-06-30',
                endTime: '2024-09-30',
                module: '热电偶',
                director: '文歆',
                priority: 1,
                status: 0,
                expanded: false,
                children: [],
            })
        },
        closeSDatePicker() {
            console.log("执行了");
            this.isShowStartTimePicker = false;
        },
        closeEDatePicker() {
            this.isShowEndTimePicker = false;
        },
        textToSDatePicker(date) {
            this.isShowStartTimePicker = true;
        },
        textToEDatePicker(date) {
            this.isShowEndTimePicker = true;
        },
        textToInput(title) {
            this.oneLevel = false;
            this.oneInput = title;
        },
        closeOneLevelInput() {
            console.log("执行了");
            this.oneLevel = true;
        },
        isLastChild(node, parentNode) {
            // console.log("node", node);
            // console.log("parentNode", parentNode);
            //判断是否是最后一个子节点
            return parentNode[parentNode.length - 1] === node;
        },
        toggleExpand() {
            this.node.expanded = !this.node.expanded;
        },
    },
};
</script>

<style scoped>
.children {
    /* padding-left: 20px; */
}

.toggle-icon {
    /* margin-right: 5px; */
}
</style>