UNPKG

nsgm-cli

Version:

A CLI tool to run Next/Style-components and Graphql/Mysql fullstack project

456 lines (419 loc) 14 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.FileGenerator = void 0; const i18n_generator_1 = require("./i18n-generator"); const fs = __importStar(require("fs")); const path = __importStar(require("path")); /** * 文件生成器 * 负责将生成的内容写入到文件系统 */ class FileGenerator { constructor(projectPath = '.') { this.projectPath = projectPath; } /** * 生成多语言文件 */ generateI18nFiles(controller, action, fields) { const i18nGenerator = new i18n_generator_1.I18nGenerator(controller, action, fields); // 定义支持的语言 const locales = [ { code: 'zh-CN', generator: () => i18nGenerator.generateChineseTranslation() }, { code: 'en-US', generator: () => i18nGenerator.generateEnglishTranslation() }, { code: 'ja-JP', generator: () => i18nGenerator.generateJapaneseTranslation() }, ]; // 为每种语言生成文件 locales.forEach((locale) => { const localeDir = path.join(this.projectPath, 'public', 'locales', locale.code); const filePath = path.join(localeDir, `${controller}.json`); // 确保目录存在 this.ensureDirectoryExists(localeDir); // 生成内容 const content = locale.generator(); // 写入文件 fs.writeFileSync(filePath, content, 'utf8'); console.log(`✅ 生成多语言文件: ${filePath}`); }); } /** * 生成页面组件文件 */ generatePageFile(controller, action, _fields, content) { const pageDir = path.join(this.projectPath, 'pages', controller); const filePath = path.join(pageDir, `${action}.tsx`); // 确保目录存在 this.ensureDirectoryExists(pageDir); // 写入文件 fs.writeFileSync(filePath, content, 'utf8'); console.log(`✅ 生成页面文件: ${filePath}`); } /** * 生成样式文件 */ generateStyleFile(controller, action) { const styleDir = path.join(this.projectPath, 'client', 'styled', controller); const filePath = path.join(styleDir, `${action}.ts`); // 确保目录存在 this.ensureDirectoryExists(styleDir); const content = this.generateStyledComponentsContent(controller, action); // 写入文件 fs.writeFileSync(filePath, content, 'utf8'); console.log(`✅ 生成样式文件: ${filePath}`); } /** * 生成Redux相关文件 */ generateReduxFiles(controller, action, _fields) { const reduxDir = path.join(this.projectPath, 'client', 'redux', controller, action); // 确保目录存在 this.ensureDirectoryExists(reduxDir); // 生成actions文件 const actionsContent = this.generateActionsContent(controller, action); fs.writeFileSync(path.join(reduxDir, 'actions.ts'), actionsContent, 'utf8'); // 生成reducer文件 const reducerContent = this.generateReducerContent(controller, action); fs.writeFileSync(path.join(reduxDir, 'reducer.ts'), reducerContent, 'utf8'); console.log(`✅ 生成Redux文件: ${reduxDir}`); } /** * 生成服务文件 */ generateServiceFile(controller, action) { const serviceDir = path.join(this.projectPath, 'client', 'service', controller); const filePath = path.join(serviceDir, `${action}.ts`); // 确保目录存在 this.ensureDirectoryExists(serviceDir); const content = this.generateServiceContent(controller, action); // 写入文件 fs.writeFileSync(filePath, content, 'utf8'); console.log(`✅ 生成服务文件: ${filePath}`); } /** * 确保目录存在 */ ensureDirectoryExists(dirPath) { if (!fs.existsSync(dirPath)) { fs.mkdirSync(dirPath, { recursive: true }); } } /** * 生成样式组件内容 */ generateStyledComponentsContent(_controller, _action) { return `import styled from 'styled-components' import { Button, Input, Table } from 'antd' export const Container = styled.div\` padding: 24px; background: #fff; min-height: calc(100vh - 64px); .page-title { font-size: 24px; font-weight: 600; color: #1890ff; margin-bottom: 24px; padding-bottom: 12px; border-bottom: 2px solid #f0f0f0; } \` export const SearchRow = styled.div\` margin-bottom: 16px; padding: 16px; background: #fafafa; border-radius: 8px; border: 1px solid #d9d9d9; \` export const ModalContainer = styled.div\` .line { display: flex; align-items: center; margin-bottom: 16px; label { width: 80px; text-align: right; margin-right: 12px; color: #333; font-weight: 500; } .ant-input { flex: 1; } } \` export const StyledButton = styled(Button)<{ $primary?: boolean; $export?: boolean; $import?: boolean; $danger?: boolean }>\` display: flex; align-items: center; border-radius: 6px; box-shadow: 0 2px 0 rgba(0, 0, 0, 0.045); \${(props) => props.$export && \` background-color: #f6ffed; color: #52c41a; border-color: #b7eb8f; box-shadow: 0 2px 0 rgba(0, 0, 0, 0.015); transition: all 0.3s ease; \`} \${(props) => props.$import && \` background-color: #e6f7ff; color: #1890ff; border-color: #91d5ff; box-shadow: 0 2px 0 rgba(0, 0, 0, 0.015); transition: all 0.3s ease; \`} \${(props) => props.$danger && \` background-color: #fff1f0; border-color: #ffa39e; box-shadow: 0 2px 0 rgba(0, 0, 0, 0.015); transition: all 0.3s ease; \`} \` export const StyledInput = styled(Input)\` width: 200px; border-radius: 6px; box-shadow: 0 2px 0 rgba(0, 0, 0, 0.015); \` export const StyledTable = styled(Table)\` margin-top: 16px; border-radius: 8px; overflow: hidden; .styled-pagination { margin-top: 16px; margin-bottom: 16px; } .table-row-light { background-color: #fafafa; } .table-row-dark { background-color: #ffffff; } \` export const ModalTitle = styled.div\` color: #1890ff; font-weight: 500; \` export const ModalInput = styled(Input)\` border-radius: 4px; \` export const IconWrapper = styled.i\` margin-right: 5px; \` export const RoundedButton = styled(Button)\` border-radius: 4px; \` export const GlobalStyle = styled.div\` .rounded-button { border-radius: 4px; } \` `; } /** * 生成Actions内容 */ generateActionsContent(controller, action) { const capitalizedController = controller.charAt(0).toUpperCase() + controller.slice(1); return `import { createAsyncThunk } from '@reduxjs/toolkit' import { get${capitalizedController}Service } from '@/service/${controller}/${action}' // 异步actions export const updateSSR${capitalizedController} = createAsyncThunk( '${controller}${action}/updateSSR${capitalizedController}', async (${controller}: any) => { return ${controller} } ) export const search${capitalizedController} = createAsyncThunk( '${controller}${action}/search${capitalizedController}', async ({ offset, limit, searchData }: { offset: number; limit: number; searchData: any }) => { const response = await get${capitalizedController}Service(offset, limit, searchData) return response.data.${controller} } ) export const add${capitalizedController} = createAsyncThunk( '${controller}${action}/add${capitalizedController}', async (${controller}Data: any) => { // 实现添加逻辑 return ${controller}Data } ) export const mod${capitalizedController} = createAsyncThunk( '${controller}${action}/mod${capitalizedController}', async ({ id, ${controller}Data }: { id: number; ${controller}Data: any }) => { // 实现修改逻辑 return { id, ${controller}Data } } ) export const del${capitalizedController} = createAsyncThunk( '${controller}${action}/del${capitalizedController}', async (id: number) => { // 实现删除逻辑 return id } ) export const batchDel${capitalizedController} = createAsyncThunk( '${controller}${action}/batchDel${capitalizedController}', async (ids: number[]) => { // 实现批量删除逻辑 return ids } ) `; } /** * 生成Reducer内容 */ generateReducerContent(controller, action) { const capitalizedController = controller.charAt(0).toUpperCase() + controller.slice(1); const capitalizedAction = action.charAt(0).toUpperCase() + action.slice(1); return `import { createSlice } from '@reduxjs/toolkit' import { updateSSR${capitalizedController}, search${capitalizedController}, add${capitalizedController}, mod${capitalizedController}, del${capitalizedController}, batchDel${capitalizedController} } from './actions' interface ${capitalizedController}State { ${controller}: any firstLoadFlag: boolean loading: boolean error: string | null } const initialState: ${capitalizedController}State = { ${controller}: { totalCounts: 0, items: [] }, firstLoadFlag: true, loading: false, error: null } const ${controller}${capitalizedAction}Slice = createSlice({ name: '${controller}${capitalizedAction}', initialState, reducers: {}, extraReducers: (builder) => { builder .addCase(updateSSR${capitalizedController}.fulfilled, (state, action) => { state.${controller} = action.payload state.firstLoadFlag = false }) .addCase(search${capitalizedController}.pending, (state) => { state.loading = true state.error = null }) .addCase(search${capitalizedController}.fulfilled, (state, action) => { state.${controller} = action.payload state.loading = false }) .addCase(search${capitalizedController}.rejected, (state, action) => { state.loading = false state.error = action.error.message || 'Search failed' }) .addCase(add${capitalizedController}.fulfilled, (state, action) => { state.${controller}.items.push(action.payload) state.${controller}.totalCounts += 1 }) .addCase(mod${capitalizedController}.fulfilled, (state, action) => { const index = state.${controller}.items.findIndex((item: any) => item.id === action.payload.id) if (index !== -1) { state.${controller}.items[index] = { ...state.${controller}.items[index], ...action.payload.${controller}Data } } }) .addCase(del${capitalizedController}.fulfilled, (state, action) => { state.${controller}.items = state.${controller}.items.filter((item: any) => item.id !== action.payload) state.${controller}.totalCounts -= 1 }) .addCase(batchDel${capitalizedController}.fulfilled, (state, action) => { state.${controller}.items = state.${controller}.items.filter((item: any) => !action.payload.includes(item.id)) state.${controller}.totalCounts -= action.payload.length }) } }) export default ${controller}${capitalizedAction}Slice.reducer `; } /** * 生成Service内容 */ generateServiceContent(controller, _action) { const capitalizedController = controller.charAt(0).toUpperCase() + controller.slice(1); return `import { myFetch } from '@/utils/fetch' /** * ${capitalizedController} 服务 */ export const get${capitalizedController}Service = async (offset: number = 0, limit: number = 100, searchData: any = {}) => { const params = new URLSearchParams({ offset: offset.toString(), limit: limit.toString(), ...searchData }) return await myFetch(\`/rest/${controller}?\${params}\`, { method: 'GET' }) } export const add${capitalizedController}Service = async (data: any) => { return await myFetch(\`/rest/${controller}\`, { method: 'POST', body: JSON.stringify(data) }) } export const update${capitalizedController}Service = async (id: number, data: any) => { return await myFetch(\`/rest/${controller}/\${id}\`, { method: 'PUT', body: JSON.stringify(data) }) } export const delete${capitalizedController}Service = async (id: number) => { return await myFetch(\`/rest/${controller}/\${id}\`, { method: 'DELETE' }) } export const batchDelete${capitalizedController}Service = async (ids: number[]) => { return await myFetch(\`/rest/${controller}/batch\`, { method: 'DELETE', body: JSON.stringify({ ids }) }) } `; } } exports.FileGenerator = FileGenerator;