TS 和组件绑定深耕(泛型表格)
一、React TS 泛型通用表格GenericTable.tsximport React from react; // 列配置类型 export type TableColumnT { key: keyof T; title: string; width?: number; render?: (val: T[keyof T], record: T) React.ReactNode; }; // 组件入参 interface GenericTablePropsT { columns: TableColumnT[]; dataSource: T[]; loading?: boolean; } // 泛型组件写法 function GenericTableT(props: GenericTablePropsT) { 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; // 业务实体 interface UserItem { id: number; name: string; status: 0 | 1; } function Demo() { const columns: TableColumnUserItem[] [ { key: id, title: ID }, { key: name, title: 姓名 }, { key: status, title: 状态, 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 标准公共组件模板BaseDialog.vuetemplate !-- 遮罩层 -- div classbase-dialog-mask v-ifvisible click.selfhandleCloseMask !-- 弹窗容器 -- div classbase-dialog :styledialogStyle !-- 头部 -- div classdialog-header slot nameheader span classtitle{{ title }}/span /slot span classclose-btn clickhandleClose×/span /div !-- 默认插槽主体内容 -- div classdialog-body slot / /div !-- 底部 -- div classdialog-footer v-ifshowFooter slot namefooter button classbtn cancel-btn clickhandleClose取消/button button classbtn confirm-btn clickhandleConfirm确定/button /slot /div /div /div /template script setup langts // 1. 先定义类型单独抽离不写在行内 type DialogSize small | middle | large; interface BaseDialogProps { // 必传 visible: boolean // 可选 默认值 title?: string size?: DialogSize width?: string showFooter?: boolean closeOnMask?: boolean } // 2. props 定义 精准默认值TS 标准写法 const props withDefaults(definePropsBaseDialogProps(), { title: 提示, size: middle, showFooter: true, closeOnMask: true, width: }) // 3. 严格定义 emits 事件类型 interface DialogEmits { (e: update:visible, val: boolean): void (e: confirm): void (e: close): void } const emit defineEmitsDialogEmits() // 4. 计算弹窗宽度根据 size 适配 const dialogStyle computed(() { const sizeMap: RecordDialogSize, string { small: 400px, middle: 600px, large: 800px } return { width: props.width || sizeMap[props.size] } }) // 5. 内部事件方法 const handleClose () { emit(update:visible, false) emit(close) } const handleConfirm () { emit(confirm) } const handleCloseMask () { if (props.closeOnMask) { handleClose() } } // 6. 对外暴露组件实例方法父组件可 ref 调用 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:visibledialogVisible title编辑内容 sizemiddle :show-footertrue confirmhandleSubmit 这里是弹窗主体内容 /BaseDialog /template script setup langts import { ref } from vue import BaseDialog from /components/BaseDialog.vue const dialogVisible ref(false) const handleSubmit () { console.log(点击确定) } /script重点拆解为什么这是企业高级写法1. 类型规范单独interface定义 Props、Emits用枚举字面量small|middle|large杜绝乱传字符串全程无 any所有参数都有类型约束2. props 默认值用withDefaults给可选属性设默认值TS 识别完美不用自己逻辑判断。3. 事件规范用update:visible支持v-model:visible双向绑定事件参数类型严格约束不会乱传参4. 多插槽规范具名插槽header/footer 默认插槽外部可自定义头部、底部、内容复用性拉满5. defineExpose 暴露实例父组件通过ref可以直接调用组件内部方法适合复杂业务弹窗。6. 自适应 配置化通过size、width灵活控制弹窗大小适配不同业务场景。Vue3 TS 公共组件固定遵守这 6 条所有 Props 先用interface定义绝不写行内对象可选属性统一用withDefaults给默认值固定值选项用字面量联合类型不用 stringEmits 必须用接口约束事件名和参数复杂配置抽Record、computed统一管理需要父组件调用方法必须defineExpose三、配套全局类型规范必加在项目src新建types/global.d.ts// 通用枚举 export type StatusType 0 | 1 | 2; // 通用接口返回格式 export interface ResDataT { code: number; data: T; message: string; } // 常用工具类型复用 export type PartialOptionalT PartialT;