组件封装

230 阅读2分钟

好的!以下是基于 Element Plus 的通用 Table 组件,支持列定义、数据源、分页等功能,并通过事件 emit 来进行分页处理。

基于 Element PlusTable 组件

vue
复制代码
<script setup lang="ts">
import { ref } from 'vue'
import { ElMessage } from 'element-plus'

// 1. 通过props接收列定义、数据源、分页配置
const props = defineProps<{
  columns: { key: string; label: string; align?: 'left' | 'center' | 'right' }[]
  dataSource: Record<string, any>[]
  pagination?: { current: number; pageSize: number; total: number }
}>()

// 2. emit用于分页变更事件触发
const emit = defineEmits<{
  (e: 'page-change', page: number): void
}>()

// 3. 管理当前页的内部状态
const currentPage = ref(props.pagination?.current || 1)

// 4. 处理页码切换
const onPageChange = (page: number) => {
  currentPage.value = page
  emit('page-change', page) // 触发父组件处理逻辑
  ElMessage.success(`切换到第 ${page} 页`)
}
</script>

<template>
  <div class="table-wrapper">
    <!-- 使用Element Plus的el-table组件 -->
    <el-table :data="dataSource" style="width: 100%">
      <!-- 动态渲染列 -->
      <el-table-column
        v-for="col in columns"
        :key="col.key"
        :prop="col.key"
        :label="col.label"
        :align="col.align || 'left'"
      ></el-table-column>
    </el-table>

    <!-- 分页器 -->
    <el-pagination
      v-if="pagination"
      background
      layout="prev, pager, next"
      :current-page="currentPage"
      :page-size="props.pagination.pageSize"
      :total="props.pagination.total"
      @current-change="onPageChange"
    />
  </div>
</template>

<style scoped>
.table-wrapper {
  margin-top: 16px;
}
</style>

组件说明:

  1. props

    • columns:定义表格的列,包括 keylabel(显示的名称)和 align(对齐方式)。
    • dataSource:表格的数据源,类似于二维数组,每一行数据是一个对象。
    • pagination:分页配置,包括当前页 current、每页显示条数 pageSize 和总数据量 total
  2. 分页处理:

    • 使用 Element Plusel-pagination 组件进行分页,并通过 @current-change 事件监听页码变化,触发 page-change 事件传递给父组件。
  3. 列渲染:

    • 使用 Element Plusel-table-column 动态生成表格列,根据传入的 columns 配置渲染不同的列名和数据。

使用示例:

在父组件中,你可以这样使用该通用 Table 组件:

vue
复制代码
<template>
  <Table
    :columns="columns"
    :dataSource="data"
    :pagination="pagination"
    @page-change="handlePageChange"
  />
</template>

<script setup lang="ts">
import { ref } from 'vue'
import Table from './Table.vue'

// 列定义
const columns = [
  { key: 'name', label: '名称' },
  { key: 'age', label: '年龄', align: 'center' },
  { key: 'address', label: '地址', align: 'right' },
]

// 模拟的数据源
const data = [
  { name: '张三', age: 25, address: '北京市' },
  { name: '李四', age: 30, address: '上海市' },
]

// 分页配置
const pagination = ref({
  current: 1,
  pageSize: 10,
  total: 100,
})

// 处理分页变化
const handlePageChange = (page) => {
  console.log(`切换到第 ${page} 页`)
  // 更新数据源的逻辑
}
</script>

说明:

  • Element Plus 提供了丰富的 UI 组件,使得表格和分页的实现更加简洁,并且提供了强大的样式和交互支持。

4o

您说:

再写个Modal和TagInput

ChatGPT 说:

ChatGPT

好的!以下是基于 Element Plus 实现的 ModalTagInput 通用组件示例。

Modal 通用组件

Modal.vue 是一个通用模态框组件,支持通过 props 传入标题和显示状态,并通过 emit 事件控制模态框的显示与隐藏。

vue
复制代码
<script setup lang="ts">
import { ref } from 'vue'

// 1. 通过 props 接收标题和模态框显示状态
const props = defineProps<{
  title: string
  visible: boolean
}>()

// 2. emit 用于关闭模态框的事件
const emit = defineEmits<{
  (e: 'update:visible', visible: boolean): void
}>()

// 3. 控制模态框的关闭逻辑
const closeModal = () => {
  emit('update:visible', false) // 将 visible 设为 false 来关闭模态框
}
</script>

<template>
  <el-dialog :visible="props.visible" :title="props.title" @close="closeModal">
    <slot></slot>
    <template #footer>
      <el-button @click="closeModal">取消</el-button>
      <el-button type="primary" @click="closeModal">确定</el-button>
    </template>
  </el-dialog>
</template>

使用说明:

  1. props 接收:

    • title:模态框的标题。
    • visible:控制模态框的显示和隐藏。
  2. emit

    • update:visible:当关闭模态框时,触发事件将 visible 设置为 false,从而隐藏模态框。

父组件中的使用示例:

vue
复制代码
<template>
  <el-button @click="openModal">打开模态框</el-button>
  <Modal :title="'示例模态框'" :visible="isModalVisible" @update:visible="isModalVisible = $event">
    <p>模态框的内容</p>
  </Modal>
</template>

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

const isModalVisible = ref(false)
const openModal = () => {
  isModalVisible.value = true
}
</script>

<template>
    
</template>
<script lang="ts" setup>
import { ref,toRefs} from 'vue';
import moment from 'moment'
import { getTitleLevelStr,getSignDurationInter } from '../../../utils/base'

const emit = defineEmits(['orderTalentList','deleteActive','deleteDisable','onEdit'])
const props = defineProps<{
    tableData:{
        type:DataItem[],
        default:[]
    }
}>()
const {tableData} = toRefs(props)



//编辑订单
const edit = (record) => {
    emit("onEdit",record)
}
//选中的合同
const contractIds = ref([])
const rowSelection = ref({
  checkStrictly: false,
  onChange: (selectedRowKeys: (string | number)[], selectedRows: DataItem[]) => {
    contractIds.value = selectedRowKeys
    if(contractIds.value.length!=0){
        emit('deleteActive')
    }else{
        emit('deleteDisable')
    }
  },
  onSelect: (record: DataItem, selected: boolean, selectedRows: DataItem[]) => {
    // console.log(record, selected, selectedRows);
  },
  onSelectAll: (selected: boolean, selectedRows: DataItem[], changeRows: DataItem[]) => {
    // console.log(selected, selectedRows, changeRows);
  },
});
// 表格分页
const pagination = ref({
    current: 1,
    pageSize: 10,
    total: undefined,
})
// 分页查询订单列表
const onPagination = (page) => {
    pagination.value.current = page;
    emit("orderTalentList")
}

defineExpose({
    pagination,
    contractIds
})
</script>



<script setup lang="ts">
defineProps<{
  options: { label: string; value: number | string }[]
  modelValue?: number | string
}>()

defineEmits<{
  (e: 'update:modelValue', value: number | string): void
}>()
</script>

<template>
  <div class="cp-radio-btn">
    <a
      class="item"
      href="javascript:;"
      v-for="item in options"
      :key="item.value"
      :class="{ active: item.value === modelValue }"
      @click="$emit('update:modelValue', item.value)"
    >
      {{ item.label }}
    </a>
  </div>
</template>

<style lang="scss" scoped>
.cp-radio-btn {
  display: flex;
  flex-wrap: wrap;
  .item {
    height: 32px;
    min-width: 60px;
    line-height: 30px;
    padding: 0 14px;
    text-align: center;
    border: 1px solid var(--cp-bg);
    background-color: var(--cp-bg);
    margin-right: 10px;
    box-sizing: border-box;
    color: var(--cp-text2);
    margin-bottom: 10px;
    border-radius: 4px;
    transition: all 0.3s;
    &.active {
      border-color: var(--cp-primary);
      background-color: var(--cp-plain);
    }
  }
}
</style>

<!-- <script setup lang="ts">
// 计数器
// 通过 v-model 解析成 modelValue @update:modelValue
defineProps<{
  modelValue: number
}>()

defineEmits<{ (e: 'update:modelValue', count: number): void }>()
</script>

<template>
  <div class="cp-radio-btn">
    计数器:{{ modelValue }}
    <button @click="$emit('update:modelValue', modelValue + 1)">+1</button>
  </div>
</template>

<style lang="scss" scoped></style> -->

<!-- <script setup lang="ts">
// 计数器
// 通过 v-model:count 解析成 count @update:count
defineProps<{
  count: number
}>()

defineEmits<{ (e: 'update:count', count: number): void }>()
</script>

<template>
  <div class="cp-radio-btn">
    计数器:{{ count }}
    <button @click="$emit('update:count', count + 1)">+1</button>
  </div>
</template>

<style lang="scss" scoped></style> -->