#!/usr/bin/env node /** * Ant Design v6 组件迁移模板生成器 * * 功能:生成符合 v6 最佳实践的组件模板 * 使用方法:node component-template.js <组件类型> <组件名> */ const fs = require('fs'); const path = require('path'); // 组件模板 const templates = { // 基础功能组件 basic: (componentName) => `import React from 'react'; import { Card, Button } from 'antd'; interface ${componentName}Props { // TODO: 添加你的 props 类型 } /** * ${componentName} 组件 * * v4 → v6 迁移说明: * - 已更新到 v6 API * - 使用新的类型定义 */ const ${componentName}: React.FC<${componentName}Props> = (props) => { return ( {/* TODO: 实现你的组件逻辑 */} ); }; export default ${componentName}; `, // 表单组件 form: (componentName) => `import React from 'react'; import { Form, Input, Button, message, App } from 'antd'; import type { FormProps } from 'antd'; interface ${componentName}FormValues { // TODO: 添加表单字段类型 name?: string; } interface ${componentName}Props { onSubmit?: (values: ${componentName}FormValues) => void; } /** * ${componentName} 表单组件 * * v4 → v6 迁移说明: * - ✅ 使用 App.useApp() 获取 message 实例 * - ✅ Form API 已更新到 v6 * - ✅ 使用 TypeScript 类型定义 */ const ${componentName}: React.FC<${componentName}Props> = ({ onSubmit }) => { const [form] = Form.useForm<${componentName}FormValues>(); const { message } = App.useApp(); const handleFinish: FormProps<${componentName}FormValues>['onFinish'] = async (values) => { try { await onSubmit?.(values); message.success('提交成功'); form.resetFields(); } catch (error) { message.error('提交失败'); console.error(error); } }; return (
{/* TODO: 添加更多表单项 */}
); }; export default ${componentName}; `, // 带 Modal 的组件 modal: (componentName) => `import React, { useState } from 'react'; import { Modal, Button, App } from 'antd'; import type { ModalProps } from 'antd'; interface ${componentName}Props { title?: string; onConfirm?: () => Promise; } /** * ${componentName} Modal 组件 * * v4 → v6 迁移说明: * - ✅ visible 改为 open * - ✅ onVisibleChange 改为 onOpenChange * - ✅ 使用 App.useApp() 获取 message */ const ${componentName}: React.FC<${componentName}Props> = ({ title = '${componentName}', onConfirm }) => { const [open, setOpen] = useState(false); const [loading, setLoading] = useState(false); const { message } = App.useApp(); const handleOpen = () => setOpen(true); const handleCancel = () => { if (!loading) { setOpen(false); } }; const handleOk = async () => { setLoading(true); try { await onConfirm?.(); message.success('操作成功'); setOpen(false); } catch (error) { message.error('操作失败'); console.error(error); } finally { setLoading(false); } }; return ( <> {/* TODO: 添加 Modal 内容 */}

Modal 内容

); }; export default ${componentName}; `, // 带 Table 的组件 table: (componentName) => `import React, { useState, useEffect } from 'react'; import { Table, Button, Space, App } from 'antd'; import type { TableProps } from 'antd'; interface DataType { key: string; // TODO: 添加你的数据类型 name: string; } interface ${componentName}Props { // TODO: 添加 props } /** * ${componentName} Table 组件 * * v4 → v6 迁移说明: * - ✅ Table API 已更新到 v6 * - ✅ 使用 TypeScript 类型定义 * - ✅ 分页配置已更新 */ const ${componentName}: React.FC<${componentName}Props> = (props) => { const [loading, setLoading] = useState(false); const [dataSource, setDataSource] = useState([]); const [pagination, setPagination] = useState({ current: 1, pageSize: 10, total: 0, }); const { message } = App.useApp(); const columns: TableProps['columns'] = [ { title: '名称', dataIndex: 'name', key: 'name', }, // TODO: 添加更多列 { title: '操作', key: 'action', render: (_, record) => ( ), }, ]; const fetchData = async (page = 1, pageSize = 10) => { setLoading(true); try { // TODO: 实现数据获取逻辑 const response = { data: [], total: 0 }; setDataSource(response.data); setPagination({ current: page, pageSize, total: response.total, }); } catch (error) { message.error('获取数据失败'); console.error(error); } finally { setLoading(false); } }; useEffect(() => { fetchData(); }, []); const handleTableChange: TableProps['onChange'] = ( pagination, filters, sorter ) => { fetchData(pagination.current, pagination.pageSize); }; return ( ); }; export default ${componentName}; `, // ProTable 组件 proTable: (componentName) => `import React, { useRef } from 'react'; import { ProTable } from '@ant-design/pro-components'; import type { ProColumns, ActionType } from '@ant-design/pro-components'; import { Button, Space, App } from 'antd'; interface DataType { id: string; // TODO: 添加你的数据类型 name: string; createdAt: string; } /** * ${componentName} ProTable 组件 * * v4 → v6 迁移说明: * - ✅ ProTable API 已更新 * - ✅ request 函数签名保持兼容 * - ✅ 使用新的类型定义 */ const ${componentName}: React.FC = () => { const actionRef = useRef(); const { message } = App.useApp(); const columns: ProColumns[] = [ { title: '名称', dataIndex: 'name', key: 'name', }, { title: '创建时间', dataIndex: 'createdAt', key: 'createdAt', valueType: 'dateTime', }, // TODO: 添加更多列 { title: '操作', key: 'action', valueType: 'option', render: (_, record) => [ , , ], }, ]; const handleRequest = async ( params: any, sort: any, filter: any ) => { try { // TODO: 实现数据获取逻辑 const response = { data: [], total: 0, success: true, }; return response; } catch (error) { message.error('获取数据失败'); return { data: [], total: 0, success: false, }; } }; return ( columns={columns} actionRef={actionRef} request={handleRequest} rowKey="id" search={{ labelWidth: 'auto', }} pagination={{ defaultPageSize: 10, showSizeChanger: true, }} dateFormatter="string" headerTitle="${componentName}" toolBarRender={() => [ , ]} /> ); }; export default ${componentName}; `, // 主题切换组件(特殊模板) theme: () => `import React, { useState, useEffect } from 'react'; import { Switch, Space, Typography } from 'antd'; import { MoonOutlined, SunOutlined } from '@ant-design/icons'; const { Text } = Typography; type ThemeMode = 'light' | 'dark'; interface ThemeSwitchProps { onChange?: (theme: ThemeMode) => void; } /** * 主题切换组件 * * v4 → v6 迁移说明: * - ❌ 废弃了 CSS 文件方式 (theme/antd.dark.css) * - ✅ 使用 ConfigProvider theme 配置 * - ✅ 支持动态主题切换 * - ✅ 使用 Design Token * * 使用方法: * 1. 在 App 根组件中使用 ConfigProvider * 2. 根据此组件的状态切换 theme.algorithm */ const ThemeSwitch: React.FC = ({ onChange }) => { const [theme, setTheme] = useState('light'); useEffect(() => { // 从 localStorage 读取保存的主题 const savedTheme = localStorage.getItem('theme') as ThemeMode; if (savedTheme) { setTheme(savedTheme); onChange?.(savedTheme); } }, []); const handleChange = (checked: boolean) => { const newTheme: ThemeMode = checked ? 'dark' : 'light'; setTheme(newTheme); localStorage.setItem('theme', newTheme); onChange?.(newTheme); }; return ( } unCheckedChildren={} /> {theme === 'dark' ? '暗黑' : '明亮'} ); }; export default ThemeSwitch; `, }; // App Provider 模板 const appProviderTemplate = () => `import React, { useState } from 'react'; import { ConfigProvider, App as AntdApp, theme } from 'antd'; import zhCN from 'antd/locale/zh_CN'; import ThemeSwitch from './components/ThemeSwitch'; type ThemeMode = 'light' | 'dark'; /** * App Provider 组件 * * v4 → v6 主题系统迁移: * - ✅ 使用 ConfigProvider 管理全局配置 * - ✅ 使用 theme.algorithm 切换主题 * - ✅ 使用 Design Token 自定义主题 * - ✅ 使用 App 组件提供全局 message/notification/modal */ const AppProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => { const [themeMode, setThemeMode] = useState('light'); return ( {/* 主题切换器(可以放在布局的任意位置) */}
{children}
); }; export default AppProvider; `; // 主题 Token 配置模板 const themeTokensTemplate = () => `import { ThemeConfig } from 'antd'; /** * 自定义主题 Token 配置 * * Design Token 说明: * - token: 全局 token,影响所有组件 * - components: 组件级别的 token,只影响特定组件 * * 常用 Token: * - colorPrimary: 品牌主色 * - colorSuccess: 成功色 * - colorWarning: 警告色 * - colorError: 错误色 * - colorInfo: 信息色 * - colorText: 文本色 * - colorBgContainer: 容器背景色 * - borderRadius: 圆角大小 * - fontSize: 字体大小 * * 完整 Token 列表: * https://ant.design/docs/react/customize-theme#theme */ export const lightTheme: ThemeConfig = { token: { colorPrimary: '#1890ff', colorSuccess: '#52c41a', colorWarning: '#faad14', colorError: '#ff4d4f', colorInfo: '#1890ff', borderRadius: 6, fontSize: 14, // TODO: 添加更多自定义 token }, components: { Button: { controlHeight: 32, borderRadius: 6, }, Card: { borderRadiusLG: 8, }, // TODO: 添加更多组件定制 }, }; export const darkTheme: ThemeConfig = { token: { ...lightTheme.token, // 暗黑模式可以覆盖特定的 token // 注意:使用 theme.darkAlgorithm 会自动处理大部分颜色 }, components: lightTheme.components, }; `; // 生成组件文件 function generateComponent(type, componentName, outputDir = '.') { const template = templates[type]; if (!template) { console.error(`❌ 未知的组件类型: ${type}`); console.log(`可用类型: ${Object.keys(templates).join(', ')}`); process.exit(1); } const content = template(componentName); const filename = `${componentName}.tsx`; const filepath = path.join(outputDir, filename); // 检查文件是否已存在 if (fs.existsSync(filepath)) { console.log(`⚠️ 文件已存在: ${filepath}`); console.log('是否覆盖?(请手动确认)'); return; } fs.writeFileSync(filepath, content); console.log(`✅ 已生成组件: ${filepath}`); } // 生成主题相关文件 function generateThemeFiles(outputDir = '.') { const files = [ { name: 'ThemeSwitch.tsx', content: templates.theme() }, { name: 'AppProvider.tsx', content: appProviderTemplate() }, { name: 'tokens.ts', content: themeTokensTemplate() }, ]; files.forEach(({ name, content }) => { const filepath = path.join(outputDir, name); if (fs.existsSync(filepath)) { console.log(`⚠️ 文件已存在: ${filepath} (跳过)`); return; } fs.writeFileSync(filepath, content); console.log(`✅ 已生成: ${filepath}`); }); console.log('\n📝 使用说明:'); console.log('1. 在你的根组件中引入 AppProvider:'); console.log(' import AppProvider from "./AppProvider";'); console.log(' '); console.log(''); console.log('2. 在任意需要使用 message/modal 的组件中:'); console.log(' const { message } = App.useApp();'); console.log(''); console.log('3. 主题切换器已集成在 AppProvider 中'); console.log(' 你可以根据需要调整其位置\n'); } // 主函数 function main() { const args = process.argv.slice(2); if (args.length === 0 || args.includes('--help') || args.includes('-h')) { console.log(` Ant Design v6 组件模板生成器 用法: node component-template.js <类型> <组件名> [输出目录] node component-template.js --theme [输出目录] 组件类型: basic - 基础组件 form - 表单组件 modal - Modal 组件 table - Table 组件 proTable - ProTable 组件 特殊命令: --theme - 生成完整的主题系统文件(ThemeSwitch + AppProvider + tokens) 示例: # 生成基础组件 node component-template.js basic UserCard ./src/components # 生成表单组件 node component-template.js form UserForm ./src/components # 生成主题系统文件 node component-template.js --theme ./src/theme 可用模板: ${Object.keys(templates).join(', ')} `); process.exit(0); } if (args[0] === '--theme') { const outputDir = args[1] || '.'; console.log(`\n🎨 生成主题系统文件到: ${outputDir}\n`); if (!fs.existsSync(outputDir)) { fs.mkdirSync(outputDir, { recursive: true }); } generateThemeFiles(outputDir); return; } const [type, componentName, outputDir = '.'] = args; if (!componentName) { console.error('❌ 请提供组件名称'); process.exit(1); } console.log(`\n📝 生成 ${type} 类型的组件: ${componentName}\n`); if (!fs.existsSync(outputDir)) { fs.mkdirSync(outputDir, { recursive: true }); } generateComponent(type, componentName, outputDir); } main();