vuejs - - - - - 递归组件的实现

1. 需求描述:

  1. 点击添加行,增加一级目录结构
  2. 当类型为object or array时,点击右侧➕,增加子集
  3. 点击右侧🚮,删除对应子集

2. 效果图:

在这里插入图片描述

3. 代码

3.1 封装组件代码

<template>
  <template v-if="!!currentLevelData.length">
    <div class="mt10" v-for="(item, index) in currentLevelData" :key="`${deep}-${index}`">
      <div class="flex flex-align-center">
        <!-- key -->
        <div class="common mr10 border-box" :style="{ paddingLeft: (deep - 1) * 10 + 'px' }">
          <a-input v-model:value="item.key" placeholder="请输入key" />
        </div>

        <!-- type -->
        <div class="type mr10">
          <a-select
            ref="select"
            v-model:value="item.type"
            class="full-width"
            @change="handleChange($event, item)"
          >
            <a-select-option v-for="t in dataType" :value="t" :key="t">{{ t }}</a-select-option>
          </a-select>
        </div>

        <!-- value -->
        <div class="common mr10">
          <a-textarea
            :rows="1"
            placeholder="请输入参考值"
            v-model:value="item.value"
            :disabled="objectFile.includes(item.type)"
          />
        </div>

        <!-- desc -->
        <div class="common mr10">
          <a-textarea :rows="1" placeholder="请输入备注" v-model:value="item.desc" />
        </div>

        <!-- 删除按钮 -->
        <div class="flex">
          <delete-outlined class="ml5" @click="deleteTarget(index)" />
          <!-- 添加子集 -->
          <plus-outlined
            class="ml5"
            v-show="objectFile.includes(item.type)"
            @click="addSubset(item)"
          />
        </div>
      </div>

      <template v-if="!!item.child?.length">
        <!-- 组件递归 -->
        <CustomInputGroup :deep="deep + 1" :list="item.child" />
      </template>
    </div>
  </template>
</template>
<script lang="ts" setup>
import CustomInputGroup from './index.vue';
import { DeleteOutlined, PlusOutlined } from '@ant-design/icons-vue';
import { message } from 'ant-design-vue';

const dataType = ['string', 'number', 'boolean', 'object', 'array', 'file']; // 所有的类型

const props = defineProps({
  list: {
    type: Array,
    default: () => [],
  },
  deep: {
    type: Number,
    default: 1,
  },
});

const objectFile = ['object', 'array']; // 可以有下一级结构的类型

interface paramsItem {
  key: string;
  type: string;
  value: string;
  desc: string;
  child?: any;
}

// currentLevelData:永远是当前层次的数据 - 数据源来自于组件调用时传递的
// 监听props变化
const currentLevelData: any = computed(() => {
  return props.list;
});

/**
 * 切换类型
 */
function handleChange(type: string, item: any) {
  if (objectFile.includes(type)) {
    item.value = '';
    item.child = [];
  } else {
    delete item.child;
  }
}

/**
 * 添(追)加子集
 */
function addSubset(item: any) {
  const lastDeep = props.deep;
  if (lastDeep == 5) return message.info('最多支持5层结构', 2);
  item.value = '';
  item.value = '';
  item.child.push({
    key: `params${props.deep + 1}-${item.child.length + 1}`,
    type: 'string',
    value: '',
    desc: '',
  });
}

/**
 * 删除
 */
function deleteTarget(index: number) {
  currentLevelData.value.splice(index, 1);
}

/**
 * 获取数据
 */
function getChildParams() {
  return currentLevelData.value;
}
/**
 * 将子组件方法暴露给父组件
 */
defineExpose({
  addSubset,
  getChildParams,
});
</script>
<style lang="less" scoped>
.common {
  width: 135px;
}
.type {
  width: 100px !important;
}
</style>

3.2 父组件使用

<template>
	 <CustomInputGroup ref="paramRef" :list="formState.param" :deep="1" />
     <a-button class="mt10" type="primary" @click="addLineParam('param')"> 添加行 </a-button>
</template>



<script>
const formState = ({
	param:[]
})

/**
 * 添加行(headersParam、requestParam)
 */
function addLineParam(formStateKey: string) {
  formState[formStateKey].push({
    key: `params${formState[formStateKey].length + 1}`,
    type: 'string',
    value: '',
    desc: '',
  });
}
</script>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是使用递归实现多级目录的el-menu示例代码: ```html <template> <el-menu :default-active="activeIndex" class="el-menu-vertical-demo" @open="handleOpen" @close="handleClose" router> <template v-for="item in menuData"> <template v-if="item.children"> <el-submenu :index="item.path"> <template slot="title"> <i :class="item.icon"></i> <span slot="title">{{ item.name }}</span> </template> <el-menu-item-group> <template v-for="child in item.children"> <menu-item :item="child" :key="child.path"></menu-item> </template> </el-menu-item-group> </el-submenu> </template> <template v-else> <menu-item :item="item" :key="item.path"></menu-item> </template> </template> </el-menu> </template> <script> import MenuItem from './MenuItem.vue' export default { name: 'Menu', components: { MenuItem }, props: { menuData: { type: Array, required: true } }, data() { return { activeIndex: '' } }, methods: { handleOpen(key, keyPath) { this.activeIndex = keyPath[keyPath.length - 1] }, handleClose(key, keyPath) { this.activeIndex = keyPath[keyPath.length - 1] } } } </script> ``` 在上述代码中,我们使用了递归的方式来实现多级目录。具体来说,我们在el-menu组件中使用了v-for指令来遍历menuData数组中的每一个菜单项。对于每一个菜单项,我们首先判断它是否有子菜单,如果有,我们就使用el-submenu组件来包裹它,并在其中递归调用自己,直到没有子菜单为止。如果没有子菜单,我们就使用自定义的MenuItem组件来渲染它。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值