最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

TS和組件綁定過(guò)程(泛型表格)

 更新時(shí)間:2026年05月15日 09:13:43   作者:淑子啦  
文章介紹了使用React+TS和Vue3;TS實(shí)現(xiàn)泛型通用表格和標(biāo)準(zhǔn)公共組件的方法,重點(diǎn)在于類型規(guī)范、事件規(guī)范、插槽規(guī)范等,強(qiáng)調(diào)了全程無(wú)`null`、類型約束等,并提供了配套的全局類型規(guī)范文件,這些實(shí)踐有助于提升代碼質(zhì)量和可復(fù)性性

一、React + TS 泛型通用表格

GenericTable.tsx

import React from 'react';

// 列配置類型
export type TableColumn<T> = {
  key: keyof T;
  title: string;
  width?: number;
  render?: (val: T[keyof T], record: T) => React.ReactNode;
};

// 組件入?yún)?
interface GenericTableProps<T> {
  columns: TableColumn<T>[];
  dataSource: T[];
  loading?: boolean;
}

// 泛型組件寫法
function GenericTable<T>(props: GenericTableProps<T>) {
  const { columns, dataSource, loading = false } = props;

  if (loading) return <div>加載中...</div>;

  return (
    <table border={1} cellPadding={6} cellSpacing={0}>
      <thead>
        <tr>
          {columns.map((col) => (
            <th key={String(col.key)} style={{ width: col.width }}>
              {col.title}
            </th>
          ))}
        </tr>
      </thead>
      <tbody>
        {dataSource.map((record, idx) => (
          <tr key={idx}>
            {columns.map((col) => {
              const val = record[col.key];
              return (
                <td key={String(col.key)}>
                  {col.render ? col.render(val, record) : String(val)}
                </td>
              );
            })}
          </tr>
        ))}
      </tbody>
    </table>
  );
}

export default GenericTable;

使用示例

import GenericTable from './GenericTable';

// 業(yè)務(wù)實(shí)體
interface UserItem {
  id: number;
  name: string;
  status: 0 | 1;
}

function Demo() {
  const columns: TableColumn<UserItem>[] = [
    { key: 'id', title: 'ID' },
    { key: 'name', title: '姓名' },
    {
      key: 'status',
      title: '狀態(tài)',
      render: (val) => (val === 1 ? '啟用' : '禁用')
    }
  ];

  const data: UserItem[] = [
    { id: 1, name: '張三', status: 1 },
    { id: 2, name: '李四', status: 0 }
  ];

  return <GenericTable columns={columns} dataSource={data} />;
}

二、Vue3 + TS 標(biāo)準(zhǔn)公共組件模板

BaseDialog.vue

<template>
  <!-- 遮罩層 -->
  <div 
    class="base-dialog-mask" 
    v-if="visible"
    @click.self="handleCloseMask"
  >
    <!-- 彈窗容器 -->
    <div class="base-dialog" :style="dialogStyle">
      <!-- 頭部 -->
      <div class="dialog-header">
        <slot name="header">
          <span class="title">{{ title }}</span>
        </slot>
        <span class="close-btn" @click="handleClose">×</span>
      </div>

      <!-- 默認(rèn)插槽:主體內(nèi)容 -->
      <div class="dialog-body">
        <slot />
      </div>

      <!-- 底部 -->
      <div class="dialog-footer" v-if="showFooter">
        <slot name="footer">
          <button class="btn cancel-btn" @click="handleClose">取消</button>
          <button class="btn confirm-btn" @click="handleConfirm">確定</button>
        </slot>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
// ?? 1. 先定義類型:?jiǎn)为?dú)抽離,不寫在行內(nèi)
type DialogSize = 'small' | 'middle' | 'large';

interface BaseDialogProps {
  // 必傳
  visible: boolean
  // 可選 + 默認(rèn)值
  title?: string
  size?: DialogSize
  width?: string
  showFooter?: boolean
  closeOnMask?: boolean
}

// ?? 2. props 定義 + 精準(zhǔn)默認(rèn)值(TS 標(biāo)準(zhǔn)寫法)
const props = withDefaults(defineProps<BaseDialogProps>(), {
  title: '提示',
  size: 'middle',
  showFooter: true,
  closeOnMask: true,
  width: ''
})

// ?? 3. 嚴(yán)格定義 emits 事件類型
interface DialogEmits {
  (e: 'update:visible', val: boolean): void
  (e: 'confirm'): void
  (e: 'close'): void
}
const emit = defineEmits<DialogEmits>()

// ?? 4. 計(jì)算彈窗寬度(根據(jù) size 適配)
const dialogStyle = computed(() => {
  const sizeMap: Record<DialogSize, string> = {
    small: '400px',
    middle: '600px',
    large: '800px'
  }
  return {
    width: props.width || sizeMap[props.size]
  }
})

// ?? 5. 內(nèi)部事件方法
const handleClose = () => {
  emit('update:visible', false)
  emit('close')
}

const handleConfirm = () => {
  emit('confirm')
}

const handleCloseMask = () => {
  if (props.closeOnMask) {
    handleClose()
  }
}

// ?? 6. 對(duì)外暴露組件實(shí)例方法(父組件可 ref 調(diào)用)
defineExpose({
  handleClose,
  handleConfirm
})
</script>

<style scoped>
.base-dialog-mask {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: rgba(0, 0, 0, 0.5);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 999;
}
.base-dialog {
  background: #fff;
  border-radius: 8px;
  overflow: hidden;
}
.dialog-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 16px 20px;
  border-bottom: 1px solid #eee;
}
.title {
  font-size: 16px;
  font-weight: 600;
}
.close-btn {
  cursor: pointer;
  font-size: 20px;
  color: #999;
}
.dialog-body {
  padding: 20px;
}
.dialog-footer {
  padding: 12px 20px;
  border-top: 1px solid #eee;
  text-align: right;
}
.btn {
  padding: 6px 16px;
  margin-left: 8px;
  border-radius: 4px;
  border: none;
  cursor: pointer;
}
.cancel-btn {
  background: #f5f5f5;
}
.confirm-btn {
  background: #1677ff;
  color: #fff;
}
</style>

使用:

<template>
  <BaseDialog
    v-model:visible="dialogVisible"
    title="編輯內(nèi)容"
    size="middle"
    :show-footer="true"
    @confirm="handleSubmit"
  >
    這里是彈窗主體內(nèi)容
  </BaseDialog>
</template>

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

const dialogVisible = ref(false)
const handleSubmit = () => {
  console.log('點(diǎn)擊確定')
}
</script>

重點(diǎn)拆解:為什么這是企業(yè)高級(jí)寫法

1. 類型規(guī)范

  • 單獨(dú) interface 定義 Props、Emits
  • 枚舉字面量 'small'|'middle'|'large',杜絕亂傳字符串
  • 全程無(wú) any,所有參數(shù)都有類型約束

2. props 默認(rèn)值

withDefaults 給可選屬性設(shè)默認(rèn)值,TS 識(shí)別完美,不用自己邏輯判斷。

3. 事件規(guī)范

  • update:visible 支持 v-model:visible 雙向綁定
  • 事件參數(shù)類型嚴(yán)格約束,不會(huì)亂傳參

4. 多插槽規(guī)范

  • 具名插槽 header / footer + 默認(rèn)插槽
  • 外部可自定義頭部、底部、內(nèi)容,復(fù)用性拉滿

5. defineExpose 暴露實(shí)例

父組件通過(guò) ref 可以直接調(diào)用組件內(nèi)部方法,適合復(fù)雜業(yè)務(wù)彈窗。

6. 自適應(yīng) + 配置化

通過(guò) size、width 靈活控制彈窗大小,適配不同業(yè)務(wù)場(chǎng)景。

Vue3 + TS 公共組件,固定遵守這 6 條

  1. 所有 Props 先用 interface 定義,絕不寫行內(nèi)對(duì)象
  2. 可選屬性統(tǒng)一用 withDefaults 給默認(rèn)值
  3. 固定值選項(xiàng)用字面量聯(lián)合類型,不用 string
  4. Emits 必須用接口約束事件名和參數(shù)
  5. 復(fù)雜配置抽 Record、computed 統(tǒng)一管理
  6. 需要父組件調(diào)用方法,必須 defineExpose

三、配套全局類型規(guī)范(必加)

在項(xiàng)目 src 新建 types/global.d.ts

// 通用枚舉
export type StatusType = 0 | 1 | 2;

// 通用接口返回格式
export interface ResData<T> {
  code: number;
  data: T;
  message: string;
}

// 常用工具類型復(fù)用
export type PartialOptional<T> = Partial<T>;

總結(jié)

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

最新評(píng)論

光山县| 隆子县| 眉山市| 宁陕县| 和静县| 栾城县| 司法| 香河县| 尚义县| 收藏| 亚东县| 陆良县| 大关县| 茂名市| 宝山区| 郓城县| 辽宁省| 平谷区| 石泉县| 襄城县| 东乡| 西青区| 云安县| 新昌县| 克拉玛依市| 钦州市| 舞钢市| 南宫市| 丹棱县| 浦城县| 双柏县| 含山县| 兰州市| 松滋市| 阿克陶县| 九龙县| 博野县| 三穗县| 鄂托克前旗| 昭平县| 武山县|