You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在多个React组件中复用模态框与搜索功能以减少代码冗余

复用标签页搜索与Modal功能的实战方案

1. 抽离公共基础组件

把搜索栏、Modal这类和业务数据无关的通用功能拆成独立组件,只通过props传递个性化参数:

  • 搜索组件:负责输入交互、关键词双向绑定,接收占位符、搜索触发回调等props。
  • Modal组件:封装弹窗的基础样式、显示/隐藏逻辑,通过插槽传递不同标签页的弹窗内容。
  • 表格容器组件:统一表格渲染逻辑,接收数据集、列配置、行点击回调等props。

示例代码(以Vue为例)

搜索组件 SearchBar.vue

<template>
  <div class="search-bar">
    <input 
      v-model="localKeyword"
      @input="triggerSearch"
      :placeholder="placeholder"
      class="search-input"
    />
  </div>
</template>

<script setup>
import { ref, watch } from 'vue'
const props = defineProps({
  modelValue: String,
  placeholder: {
    type: String,
    default: '输入关键词搜索'
  }
})
const emit = defineEmits(['update:modelValue', 'search'])

const localKeyword = ref(props.modelValue)
watch(localKeyword, val => emit('update:modelValue', val))

const triggerSearch = () => emit('search', localKeyword.value)
</script>

<style scoped>
.search-bar { margin-bottom: 16px; }
.search-input { padding: 8px 12px; width: 100%; border-radius: 4px; border: 1px solid #ddd; }
</style>

Modal组件 BaseModal.vue

<template>
  <div class="modal-mask" v-if="visible" @click.self="close">
    <div class="modal-box">
      <div class="modal-header">
        <span>{{ title }}</span>
        <button class="close-btn" @click="close">×</button>
      </div>
      <div class="modal-body">
        <slot></slot>
      </div>
    </div>
  </div>
</template>

<script setup>
const props = defineProps({
  visible: Boolean,
  title: String
})
const emit = defineEmits(['close'])

const close = () => emit('close')
</script>

<style scoped>
.modal-mask { position: fixed; top:0; left:0; width:100%; height:100%; background:rgba(0,0,0,0.5); display:flex; align-items:center; justify-content:center; }
.modal-box { background:#fff; border-radius:8px; width:80%; max-width:500px; }
.modal-header { padding:16px; border-bottom:1px solid #eee; display:flex; justify-content:space-between; align-items:center; }
.close-btn { border:none; background:transparent; font-size:20px; cursor:pointer; }
.modal-body { padding:16px; }
</style>

2. 封装标签页内容组合组件

创建一个TabContent.vue,把搜索、表格、Modal组合起来,接收每个标签页独有的数据集、列配置和名称:

<template>
  <div class="tab-content">
    <SearchBar 
      v-model="searchKey"
      @search="filterData"
      :placeholder="`搜索${tabTitle}数据`"
    />
    <table class="data-table">
      <thead>
        <tr>
          <th v-for="col in columns" :key="col.key">{{ col.label }}</th>
        </tr>
      </thead>
      <tbody>
        <tr v-for="row in displayedData" :key="row.id" @click="openModal(row)">
          <td v-for="col in columns" :key="col.key">{{ row[col.key] }}</td>
        </tr>
      </tbody>
    </table>
    <BaseModal 
      :visible="modalShow"
      :title="`${tabTitle}详情`"
      @close="modalShow = false"
    >
      <div v-if="selectedRow">
        <p v-for="col in columns" :key="col.key">
          <span class="label">{{ col.label }}:</span>
          <span>{{ selectedRow[col.key] }}</span>
        </p>
      </div>
    </BaseModal>
  </div>
</template>

<script setup>
import { ref, computed } from 'vue'
import SearchBar from './SearchBar.vue'
import BaseModal from './BaseModal.vue'

const props = defineProps({
  rawData: Array,
  columns: Array,
  tabTitle: String
})

const searchKey = ref('')
const modalShow = ref(false)
const selectedRow = ref(null)

const displayedData = computed(() => {
  if (!searchKey.value) return props.rawData
  return props.rawData.filter(row => {
    // 根据实际字段调整匹配逻辑,这里示例匹配所有字符串类型字段
    return Object.values(row).some(val => 
      typeof val === 'string' && val.includes(searchKey.value)
    )
  })
})

const filterData = () => {} // 如需额外搜索逻辑可扩展
const openModal = (row) => {
  selectedRow.value = row
  modalShow.value = true
}
</script>

<style scoped>
.data-table { width:100%; border-collapse:collapse; }
.data-table th, .data-table td { border:1px solid #eee; padding:8px; text-align:left; }
.data-table th { background:#f5f5f5; }
.label { font-weight:600; margin-right:8px; }
</style>

3. 主页面中复用组合组件

在标签页容器里,只需要给每个标签页传入对应的数据集、列配置和标题即可:

<template>
  <div class="tabs-wrap">
    <div class="tab-nav">
      <button 
        v-for="(tab, idx) in tabs" 
        :key="idx"
        :class="{ active: activeTab === idx }"
        @click="activeTab = idx"
      >
        {{ tab.title }}
      </button>
    </div>
    <div class="tab-panels">
      <TabContent 
        v-for="(tab, idx) in tabs" 
        :key="idx"
        v-show="activeTab === idx"
        :raw-data="tab.data"
        :columns="tab.columns"
        :tab-title="tab.title"
      />
    </div>
  </div>
</template>

<script setup>
import { ref } from 'vue'
import TabContent from './TabContent.vue'

// 模拟四个标签页的配置数据
const tabs = ref([
  {
    title: '用户数据',
    data: [{id:1, name:'张三', age:25}, {id:2, name:'李四', age:30}],
    columns: [{key:'id', label:'ID'}, {key:'name', label:'姓名'}, {key:'age', label:'年龄'}]
  },
  {
    title: '订单数据',
    data: [{id:101, orderNo:'OD2024001', amount:199}, {id:102, orderNo:'OD2024002', amount:399}],
    columns: [{key:'id', label:'序号'}, {key:'orderNo', label:'订单号'}, {key:'amount', label:'金额'}]
  },
  {
    title: '商品数据',
    data: [{id:201, goodsName:'手机', price:2999}, {id:202, goodsName:'耳机', price:399}],
    columns: [{key:'id', label:'ID'}, {key:'goodsName', label:'商品名'}, {key:'price', label:'价格'}]
  },
  {
    title: '日志数据',
    data: [{id:301, operator:'admin', time:'2024-05-01'}, {id:302, operator:'user', time:'2024-05-02'}],
    columns: [{key:'id', label:'ID'}, {key:'operator', label:'操作人'}, {key:'time', label:'操作时间'}]
  }
])

const activeTab = ref(0)
</script>

<style scoped>
.tabs-wrap { max-width:1200px; margin:0 auto; }
.tab-nav { margin-bottom:16px; border-bottom:1px solid #eee; }
.tab-nav button { border:none; background:transparent; padding:8px 16px; cursor:pointer; margin-right:8px; }
.tab-nav button.active { border-bottom:2px solid #1677ff; color:#1677ff; }
</style>

4. 额外优化建议

  • 复杂搜索逻辑(比如防抖、多字段精准匹配)可以抽成独立工具函数,在组件中引入复用。
  • 如果多个标签页需要共享状态(比如搜索历史),可以用状态管理工具(如Pinia)集中维护。
  • 表格样式、间距等公共样式可以放到全局CSS文件,避免重复编写。

内容的提问来源于stack exchange,提问作者Prajul Aaditya

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 11:36:30