From 2c94dfe3e64abd6d40a592cc3adf46b4cc4f934a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Mar 2024 21:34:58 +0000 Subject: [PATCH 1/6] chore: bump @vercel/analytics from 1.0.2 to 1.2.2 Bumps [@vercel/analytics](https://github.com/vercel/analytics/tree/HEAD/packages/web) from 1.0.2 to 1.2.2. - [Release notes](https://github.com/vercel/analytics/releases) - [Commits](https://github.com/vercel/analytics/commits/1.2.2/packages/web) --- updated-dependencies: - dependency-name: "@vercel/analytics" dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- package-lock.json | 28 ++++++++++++++++++++++++---- package.json | 2 +- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8d577d7..3570d31 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,7 +19,7 @@ "@uiw/codemirror-extensions-langs": "^4.21.9", "@uiw/codemirror-themes": "^4.21.9", "@uiw/react-codemirror": "^4.21.20", - "@vercel/analytics": "^1.0.2", + "@vercel/analytics": "^1.2.2", "autoprefixer": "10.4.16", "emoji-picker-react": "^4.5.3", "eslint": "8.47.0", @@ -2307,9 +2307,24 @@ } }, "node_modules/@vercel/analytics": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@vercel/analytics/-/analytics-1.0.2.tgz", - "integrity": "sha512-BZFxVrv24VbNNl5xMxqUojQIegEeXMI6rX3rg1uVLYUEXsuKNBSAEQf4BWEcjQDp/8aYJOj6m8V4PUA3x/cxgg==" + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@vercel/analytics/-/analytics-1.2.2.tgz", + "integrity": "sha512-X0rctVWkQV1e5Y300ehVNqpOfSOufo7ieA5PIdna8yX/U7Vjz0GFsGf4qvAhxV02uQ2CVt7GYcrFfddXXK2Y4A==", + "dependencies": { + "server-only": "^0.0.1" + }, + "peerDependencies": { + "next": ">= 13", + "react": "^18 || ^19" + }, + "peerDependenciesMeta": { + "next": { + "optional": true + }, + "react": { + "optional": true + } + } }, "node_modules/@webassemblyjs/ast": { "version": "1.11.5", @@ -8081,6 +8096,11 @@ "randombytes": "^2.1.0" } }, + "node_modules/server-only": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/server-only/-/server-only-0.0.1.tgz", + "integrity": "sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA==" + }, "node_modules/set-function-name": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.1.tgz", diff --git a/package.json b/package.json index b3216cf..2cb0387 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "@uiw/codemirror-extensions-langs": "^4.21.9", "@uiw/codemirror-themes": "^4.21.9", "@uiw/react-codemirror": "^4.21.20", - "@vercel/analytics": "^1.0.2", + "@vercel/analytics": "^1.2.2", "autoprefixer": "10.4.16", "emoji-picker-react": "^4.5.3", "eslint": "8.47.0", From 40d59c354d010070ebf5215387477a151ffb31fb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Mar 2024 21:37:21 +0000 Subject: [PATCH 2/6] chore: bump webpack from 5.88.2 to 5.90.3 Bumps [webpack](https://github.com/webpack/webpack) from 5.88.2 to 5.90.3. - [Release notes](https://github.com/webpack/webpack/releases) - [Commits](https://github.com/webpack/webpack/compare/v5.88.2...v5.90.3) --- updated-dependencies: - dependency-name: webpack dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- package-lock.json | 73 ++++++++++++++++++++++------------------------- package.json | 2 +- 2 files changed, 35 insertions(+), 40 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8d577d7..0842e32 100644 --- a/package-lock.json +++ b/package-lock.json @@ -49,7 +49,7 @@ "cypress": "^12.17.4", "mini-css-extract-plugin": "^2.7.6", "typescript": "^5.1.6", - "webpack": "^5.88.2" + "webpack": "^5.90.3" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -1512,9 +1512,9 @@ } }, "node_modules/@jridgewell/source-map": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.3.tgz", - "integrity": "sha512-b+fsZXeLYi9fEULmfBrhxn4IrPlINf8fiNarzTof004v3lFdntdwa9PF7vFJqm3mg7s+ScJMxXaE3Acp1irZcg==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz", + "integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==", "dependencies": { "@jridgewell/gen-mapping": "^0.3.0", "@jridgewell/trace-mapping": "^0.3.9" @@ -1526,19 +1526,14 @@ "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.18", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz", - "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==", + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", "dependencies": { - "@jridgewell/resolve-uri": "3.1.0", - "@jridgewell/sourcemap-codec": "1.4.14" + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@jridgewell/trace-mapping/node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==" - }, "node_modules/@lezer/common": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.0.2.tgz", @@ -2008,9 +2003,9 @@ } }, "node_modules/@types/estree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.1.tgz", - "integrity": "sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==" + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==" }, "node_modules/@types/hoist-non-react-statics": { "version": "3.3.1", @@ -8074,9 +8069,9 @@ } }, "node_modules/serialize-javascript": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz", - "integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", "dependencies": { "randombytes": "^2.1.0" } @@ -8529,12 +8524,12 @@ } }, "node_modules/terser": { - "version": "5.17.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.17.1.tgz", - "integrity": "sha512-hVl35zClmpisy6oaoKALOpS0rDYLxRFLHhRuDlEGTKey9qHjS1w9GMORjuwIMt70Wan4lwsLYyWDVnWgF+KUEw==", + "version": "5.28.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.28.1.tgz", + "integrity": "sha512-wM+bZp54v/E9eRRGXb5ZFDvinrJIOaTapx3WUokyVGZu5ucVCK55zEgGd5Dl2fSr3jUo5sDiERErUWLY6QPFyA==", "dependencies": { - "@jridgewell/source-map": "^0.3.2", - "acorn": "^8.5.0", + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", "commander": "^2.20.0", "source-map-support": "~0.5.20" }, @@ -8546,15 +8541,15 @@ } }, "node_modules/terser-webpack-plugin": { - "version": "5.3.7", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.7.tgz", - "integrity": "sha512-AfKwIktyP7Cu50xNjXF/6Qb5lBNzYaWpU6YfoX3uZicTx0zTy0stDDCsvjDapKsSDvOeWo5MEq4TmdBy2cNoHw==", + "version": "5.3.10", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz", + "integrity": "sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==", "dependencies": { - "@jridgewell/trace-mapping": "^0.3.17", + "@jridgewell/trace-mapping": "^0.3.20", "jest-worker": "^27.4.5", "schema-utils": "^3.1.1", "serialize-javascript": "^6.0.1", - "terser": "^5.16.5" + "terser": "^5.26.0" }, "engines": { "node": ">= 10.13.0" @@ -8579,9 +8574,9 @@ } }, "node_modules/terser-webpack-plugin/node_modules/schema-utils": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.2.tgz", - "integrity": "sha512-pvjEHOgWc9OWA/f/DE3ohBWTD6EleVLf7iFUkoSwAxttdBhB9QUebQgxER2kWueOvRJXPHNnyrvvh9eZINB8Eg==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", "dependencies": { "@types/json-schema": "^7.0.8", "ajv": "^6.12.5", @@ -9032,18 +9027,18 @@ } }, "node_modules/webpack": { - "version": "5.88.2", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.88.2.tgz", - "integrity": "sha512-JmcgNZ1iKj+aiR0OvTYtWQqJwq37Pf683dY9bVORwVbUrDhLhdn/PlO2sHsFHPkj7sHNQF3JwaAkp49V+Sq1tQ==", + "version": "5.90.3", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.90.3.tgz", + "integrity": "sha512-h6uDYlWCctQRuXBs1oYpVe6sFcWedl0dpcVaTf/YF67J9bKvwJajFulMVSYKHrksMB3I/pIagRzDxwxkebuzKA==", "dependencies": { "@types/eslint-scope": "^3.7.3", - "@types/estree": "^1.0.0", + "@types/estree": "^1.0.5", "@webassemblyjs/ast": "^1.11.5", "@webassemblyjs/wasm-edit": "^1.11.5", "@webassemblyjs/wasm-parser": "^1.11.5", "acorn": "^8.7.1", "acorn-import-assertions": "^1.9.0", - "browserslist": "^4.14.5", + "browserslist": "^4.21.10", "chrome-trace-event": "^1.0.2", "enhanced-resolve": "^5.15.0", "es-module-lexer": "^1.2.1", @@ -9057,7 +9052,7 @@ "neo-async": "^2.6.2", "schema-utils": "^3.2.0", "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.3.7", + "terser-webpack-plugin": "^5.3.10", "watchpack": "^2.4.0", "webpack-sources": "^3.2.3" }, diff --git a/package.json b/package.json index b3216cf..111cc16 100644 --- a/package.json +++ b/package.json @@ -53,6 +53,6 @@ "cypress": "^12.17.4", "mini-css-extract-plugin": "^2.7.6", "typescript": "^5.1.6", - "webpack": "^5.88.2" + "webpack": "^5.90.3" } } From f1decb3a9f86535881ff0df0e18872ca2822ece1 Mon Sep 17 00:00:00 2001 From: Zach Wang Date: Mon, 11 Mar 2024 23:52:12 +1300 Subject: [PATCH 3/6] feat: add Typescript generator (#265) --- scripts/addNewFormatter.ts | 2 +- src/components/Utils/src/OptionsInput.tsx | 21 ++- src/constants/enums.ts | 5 +- src/core/formatters/CSharp/CSharp.tsx | 26 ++-- src/core/formatters/JavaScript/Javascript.tsx | 27 +--- src/core/formatters/Typescript/Typescript.tsx | 131 ++++++++++++++++++ src/core/formatters/Typescript/index.ts | 13 ++ src/core/formatters/index.ts | 2 + src/core/generators/Url/Url.tsx | 36 +++-- src/locale/translations/en.ts | 10 +- src/locale/translations/zhCN.ts | 63 ++++++--- src/pages/workspace/index.tsx | 2 +- src/utils/formatterUtils.ts | 49 ++++++- 13 files changed, 300 insertions(+), 87 deletions(-) create mode 100644 src/core/formatters/Typescript/Typescript.tsx create mode 100644 src/core/formatters/Typescript/index.ts diff --git a/scripts/addNewFormatter.ts b/scripts/addNewFormatter.ts index 23c6e12..7516ac0 100644 --- a/scripts/addNewFormatter.ts +++ b/scripts/addNewFormatter.ts @@ -25,7 +25,7 @@ const createFormatter = (formatterName: string, fileExtension: string) => { const path = `./src/core/formatters/${formatterName}`; // add formatter enum option - enumUtils('./src/constants/enums.ts', 'ExportFormat', formatterName.toUpperCase(), formatterName.toLowerCase()); + enumUtils('./src/constants/enums.ts', 'ExportFormat', formatterName.toUpperCase(), formatterName); // create directory fs.mkdirSync(path, {recursive: true}); diff --git a/src/components/Utils/src/OptionsInput.tsx b/src/components/Utils/src/OptionsInput.tsx index c54aeb4..8d96707 100644 --- a/src/components/Utils/src/OptionsInput.tsx +++ b/src/components/Utils/src/OptionsInput.tsx @@ -11,10 +11,23 @@ export interface OptionsInputProps { value: string; onChange: (value: any) => void; style?: React.CSSProperties; + required?: boolean; // Add this line for the new required prop } export const OptionsInput: React.FunctionComponent = ({...props}) => { - const {label, infoTooltip, errorMessage, value, style, suffix, onChange} = props; + const {label, infoTooltip, errorMessage, value, style, suffix, onChange, required} = props; + + // Add a new useState to manage the validation error message + const [validationError, setValidationError] = React.useState(); + + // Add effect to validate value when it changes or when required status changes + React.useEffect(() => { + if (required && isNullOrWhiteSpace(value)) { + setValidationError('This field is required.'); // Set default required error message or use props.errorMessage + } else { + setValidationError(undefined); // Clear error message when input is valid + } + }, [value, required]); return (
@@ -24,15 +37,15 @@ export const OptionsInput: React.FunctionComponent = ({...pro {infoTooltip} }
- + onChange(value)} value={value} style={style} suffix={suffix} - validateStatus={!isNullOrWhiteSpace(errorMessage) ? 'error' : 'default'} + validateStatus={!isNullOrWhiteSpace(validationError || errorMessage) ? 'error' : 'default'} /> ) -} \ No newline at end of file +} diff --git a/src/constants/enums.ts b/src/constants/enums.ts index f5b2bc5..918d080 100644 --- a/src/constants/enums.ts +++ b/src/constants/enums.ts @@ -1,12 +1,13 @@ // export format export enum ExportFormatCategory { - FILE_TYPES = "fileTypes", + FILE_TYPES = "file_types", DATABASES = "databases", - PROGRAMMING_LANGUAGES = "programmingLanguages", + PROGRAMMING_LANGUAGES = "programming_languages", } export enum ExportFormat { + TYPESCRIPT = "Typescript", CSHARP = "C#", SQL = "SQL", CSV = "CSV", diff --git a/src/core/formatters/CSharp/CSharp.tsx b/src/core/formatters/CSharp/CSharp.tsx index 774bf00..cff70b9 100644 --- a/src/core/formatters/CSharp/CSharp.tsx +++ b/src/core/formatters/CSharp/CSharp.tsx @@ -4,8 +4,6 @@ import {OptionsInput, OptionsSelect, SelectOption} from "@/components/Utils"; import {FormattedMessage} from "@/locale"; import {OptionsSwitch} from "@/components/Utils/src/OptionsSwitch"; import {ValueType} from "@/constants/enums"; -import {GenerateResult} from "@/types/generator"; -import {hasValue} from "@/utils/typeUtils"; import {formatValueForCSharp} from "@/core/formatters/CSharp/CSharpFormatterUtils"; // ------------------------------------------------------------------------------------------------------------- @@ -66,13 +64,13 @@ export const format = (request: FormatRequest): string => { fieldType = `long${field.emptyRate !== 0 ? "?" : ""}`; break; case ValueType.INT_LIST: - fieldType = 'List' + fieldType = 'List'; break; case ValueType.STRING_LIST: - fieldType = 'List' + fieldType = 'List'; break; case ValueType.ONE_BIT: - fieldType = `int${field.emptyRate !== 0 ? "?" : ""}` + fieldType = `int${field.emptyRate !== 0 ? "?" : ""}`; break; // Add more cases as necessary } @@ -110,20 +108,16 @@ export const format = (request: FormatRequest): string => { // Populate collection with values values.forEach((value, index) => { + let fieldAssignments = sortedFieldIds.map(id => { + return ` ${fields[id].fieldName} = ${formatValueForCSharp(value[id], fields[id].valueType)}`; + }).join(',\n'); // Ensure each field assignment is on a new line + if (config.collectionType === CSharpCollectionType.ARRAY) { // Array value assignment - csharpCode += `${config.collectionName}[${index}] = new ${itemType} { `; + csharpCode += `${config.collectionName}[${index}] = new ${itemType} {\n${fieldAssignments}\n };\n`; } else { // Other collections value addition - csharpCode += `${config.collectionName}.Add(new ${itemType} { `; - } - - csharpCode += sortedFieldIds.map(id => `${fields[id].fieldName} = ${formatValueForCSharp(value[id], fields[id].valueType)}`).join(', '); - - if (config.collectionType === CSharpCollectionType.ARRAY) { - csharpCode += ' };\n'; - } else { - csharpCode += ' });\n'; + csharpCode += `${config.collectionName}.Add(new ${itemType}\n{\n${fieldAssignments}\n});\n`; } }); @@ -156,6 +150,7 @@ export const CSharpConfigComponent: React.FC /> } value={config.collectionName} onChange={(v) => handleValueChange("collectionName", v)} @@ -170,6 +165,7 @@ export const CSharpConfigComponent: React.FC { config.dtoClass && } value={config.dtoClassName} onChange={(v) => handleValueChange("dtoClassName", v)} diff --git a/src/core/formatters/JavaScript/Javascript.tsx b/src/core/formatters/JavaScript/Javascript.tsx index 3957712..f7b3087 100644 --- a/src/core/formatters/JavaScript/Javascript.tsx +++ b/src/core/formatters/JavaScript/Javascript.tsx @@ -3,6 +3,7 @@ import {FormatRequest, FormatterConfigComponentInterface} from "@/types/formatte import {FormattedMessage, useIntl} from "@/locale"; import {OptionsInput, OptionsSelect, SelectOption} from "@/components/Utils"; import {isNullOrWhiteSpace} from "@/utils/stringUtils"; +import {toJsonListStringWithoutQuotes} from "@/utils/formatterUtils"; // ------------------------------------------------------------------------------------------------------------- // types @@ -38,30 +39,12 @@ export const format = (request: FormatRequest): string => { return ''; } - const jsonData = values.map(item => { - const row: Record = {}; - for (const column of sortedFieldIds) { - const field = fields[column]; - const {isDraft, fieldName} = field; - const itemValue = item[column]; - let {value} = itemValue; - - if (!isDraft && value !== null) { - if (typeof value === 'bigint') { - value = value.toString(); - } - row[fieldName] = value; - } - } - return row; - }); - - let output = JSON.stringify(jsonData, null, 3); + let output = toJsonListStringWithoutQuotes(fields, sortedFieldIds, values); return formatOutput(formatType, module, declarationKeyword, variableName, output); }; -function formatOutput(formatType, module, declarationKeyword, variableName, output) { +function formatOutput(formatType: JavascriptFormatterFormat, module: JavascriptModuleType, declarationKeyword: JavascriptDeclarationKeyword, variableName: string, output: string) { switch (formatType) { case JavascriptFormatterFormat.VARIABLE: return formatVariableOutput(declarationKeyword, variableName, output); @@ -72,14 +55,14 @@ function formatOutput(formatType, module, declarationKeyword, variableName, outp } } -function formatVariableOutput(declarationKeyword, variableName, output) { +function formatVariableOutput(declarationKeyword: JavascriptDeclarationKeyword, variableName: string, output: string) { if (isNullOrWhiteSpace(variableName)) { return ''; } return `${declarationKeyword} ${variableName} = ${output};`; } -function formatExportOutput(module, output) { +function formatExportOutput(module: JavascriptModuleType, output: string) { switch (module) { case JavascriptModuleType.ES6: return `export default ${output};`; diff --git a/src/core/formatters/Typescript/Typescript.tsx b/src/core/formatters/Typescript/Typescript.tsx new file mode 100644 index 0000000..0022548 --- /dev/null +++ b/src/core/formatters/Typescript/Typescript.tsx @@ -0,0 +1,131 @@ +import React from "react"; +import {FormatRequest, FormatterConfigComponentInterface} from "@/types/formatter"; +import {OptionsInput} from "@/components/Utils"; +import {OptionsRadio, RadioOption} from "@/components/Utils/src/OptionsRadio"; +import {FormattedMessage} from "@/locale"; +import {toJsonListStringWithoutQuotes} from "@/utils/formatterUtils"; +import {ValueType} from "@/constants/enums"; + +// ------------------------------------------------------------------------------------------------------------- +// types + +export enum TypescriptDeclaration { + INTERFACE = "interface", + TYPE = "type" +} + +export type TypescriptFormatterConfig = { + declaration: TypescriptDeclaration, + declarationName: string, + variableName: string +} + +// ------------------------------------------------------------------------------------------------------------- +// default options + +export const defaultTypescriptFormatterConfig: TypescriptFormatterConfig = { + declaration: TypescriptDeclaration.TYPE, + declarationName: "MyRecord", + variableName: "myData" +} + +// ------------------------------------------------------------------------------------------------------------- +// format method + +export const format = (request: FormatRequest): string => { + const {fields, values, sortedFieldIds, config} = request; + const {declaration, declarationName, variableName} = config as TypescriptFormatterConfig; + + if (sortedFieldIds.length === 0 || values.length === 0) { + return ''; + } + + let output = ''; + + // type & interface + output += `export ${declaration} ${declarationName}${declaration === TypescriptDeclaration.TYPE ? " = " : ""} {\n`; + sortedFieldIds.forEach(id => { + const field = fields[id]; + let fieldType = 'string'; // Default field type + switch (field.valueType) { + case ValueType.BIGINT: + case ValueType.DOUBLE: + case ValueType.ONE_BIT: + case ValueType.INT: + fieldType = `number`; + break; + case ValueType.BOOLEAN: + fieldType = 'boolean'; + break; + case ValueType.INT_LIST: + fieldType = 'number[]' + break; + case ValueType.STRING_LIST: + fieldType = 'string[]' + break; + } + output+= ` ${field.fieldName}${field.emptyRate !== 0 ? "?" : ""}: ${fieldType};\n`; + }); + output+="}; \n\n" + + // values + output += `export const ${variableName}: ${declarationName}[] = ${toJsonListStringWithoutQuotes(fields, sortedFieldIds, values)};`; + + return output; +} + +// ------------------------------------------------------------------------------------------------------------- +// config component + +export const TypescriptConfigComponent: React.FC = ({...props}) => { + const {config, onConfigChange} = props as { + config: TypescriptFormatterConfig + onConfigChange: typeof props.onConfigChange + } + + // action + const handleValueChange = (field: string, value: any) => { + onConfigChange({...config, [field]: value}) + } + + // TODO: implement your own configs component here + return ( +
+ } + radioOptions={typescriptDeclarationTypeRadioOptions} + type={"button"} + value={config.declaration} + onChange={(v) => handleValueChange("declaration", v)} + style={{"width": "160px"}} + /> + + } + value={config.declarationName} + onChange={(v) => handleValueChange("declarationName", v)} + style={{"width": "155px"}} + /> + + } + value={config.variableName} + onChange={(v) => handleValueChange("variableName", v)} + style={{"width": "160px"}} + /> + +
+ ); +} + +const typescriptDeclarationTypeRadioOptions: RadioOption[] = [ + { + label: "type", + value: TypescriptDeclaration.TYPE + }, + { + label: "interface", + value: TypescriptDeclaration.INTERFACE + } +] \ No newline at end of file diff --git a/src/core/formatters/Typescript/index.ts b/src/core/formatters/Typescript/index.ts new file mode 100644 index 0000000..54414d3 --- /dev/null +++ b/src/core/formatters/Typescript/index.ts @@ -0,0 +1,13 @@ +import {Formatter} from "@/types/formatter"; +import {ExportFormat, ExportFormatCategory} from "@/constants/enums"; +import {TypescriptConfigComponent, format, defaultTypescriptFormatterConfig} from "./Typescript"; + + +export const TypescriptFormatter: Formatter = { + type: ExportFormat.TYPESCRIPT, + category: ExportFormatCategory.PROGRAMMING_LANGUAGES, + format: format, + fileExtension: 'ts', + configComponent: TypescriptConfigComponent, + defaultConfig: defaultTypescriptFormatterConfig, +} \ No newline at end of file diff --git a/src/core/formatters/index.ts b/src/core/formatters/index.ts index 977032e..6833b12 100644 --- a/src/core/formatters/index.ts +++ b/src/core/formatters/index.ts @@ -1,3 +1,4 @@ +import {TypescriptFormatter} from "@/core/formatters/Typescript"; import {CSharpFormatter} from "@/core/formatters/CSharp"; import {SqlFormatter} from "@/core/formatters/Sql"; import {CsvFormatter} from "@/core/formatters/Csv"; @@ -8,6 +9,7 @@ import {XmlFormatter} from "@/core/formatters/Xml"; export const formatters = { + [ExportFormat.TYPESCRIPT]: TypescriptFormatter, [ExportFormat.CSHARP]: CSharpFormatter, [ExportFormat.SQL]: SqlFormatter, [ExportFormat.CSV]: CsvFormatter, diff --git a/src/core/generators/Url/Url.tsx b/src/core/generators/Url/Url.tsx index a8e88bc..d5aa7f8 100644 --- a/src/core/generators/Url/Url.tsx +++ b/src/core/generators/Url/Url.tsx @@ -10,20 +10,22 @@ import {OptionsSwitch} from "@/components/Utils/src/OptionsSwitch"; // ------------------------------------------------------------------------------------------------------------- // types type HTTPProtocolType = 'http' | 'https'; -export enum ProtocolGeneratorFormat{ + +export enum ProtocolGeneratorFormat { HTTP_PROTOCOL = 'http', HTTPS_PROTOCOL = 'https' } + export interface UrlGeneratorOptions { - appendSlash: boolean; - protocol:HTTPProtocolType; + appendSlash: boolean; + protocol: HTTPProtocolType; } // ------------------------------------------------------------------------------------------------------------- // default options -export const UrlGeneratorDefaultOptions:UrlGeneratorOptions = { - appendSlash:false, - protocol:ProtocolGeneratorFormat.HTTPS_PROTOCOL +export const UrlGeneratorDefaultOptions: UrlGeneratorOptions = { + appendSlash: false, + protocol: ProtocolGeneratorFormat.HTTPS_PROTOCOL } @@ -31,7 +33,7 @@ export const UrlGeneratorDefaultOptions:UrlGeneratorOptions = { // generate method export const generate = (options: any): GenerateResult => { // TODO: implement your own generate method here - const { appendSlash, protocol } = options; + const {appendSlash, protocol} = options; const domain = faker.internet.domainName(); // Construct URL let value = `${protocol}://${domain}`; @@ -43,7 +45,7 @@ export const generate = (options: any): GenerateResult => { value: value, stringValue: value, } - + } // ------------------------------------------------------------------------------------------------------------- @@ -56,15 +58,12 @@ export const UrlGeneratorOptionsComponent: React.FunctionComponent { handleOptionValueChange("protocol", format); } - - + return ( <> - - } + label={} value={options.appendSlash} onChange={(v) => { handleOptionValueChange('appendSlash', v); @@ -72,25 +71,24 @@ export const UrlGeneratorOptionsComponent: React.FunctionComponent - } + label={} selectOptions={protocolOptions} value={options.protocol} onChange={handleFormatChange} // This directly passes the selected value to handleFormatChange - style={{ width: '210px' }} -/> + style={{width: '110px'}} + /> ); } const protocolOptions: SelectOption[] = [ { - value:ProtocolGeneratorFormat.HTTPS_PROTOCOL, + value: ProtocolGeneratorFormat.HTTPS_PROTOCOL, label: <>https }, { - value:ProtocolGeneratorFormat.HTTP_PROTOCOL, + value: ProtocolGeneratorFormat.HTTP_PROTOCOL, label: <>http } ] \ No newline at end of file diff --git a/src/locale/translations/en.ts b/src/locale/translations/en.ts index 3241d1e..48343b8 100644 --- a/src/locale/translations/en.ts +++ b/src/locale/translations/en.ts @@ -4,9 +4,9 @@ export const en = { // export // export category - "export.category.fileTypes": "General file types", + "export.category.file_types": "General file types", "export.category.databases" : "Databases", - "export.category.programmingLanguages": "Programming languages", + "export.category.programming_languages": "Programming languages", // export format modal "export.configurator.modal.title": "Format", @@ -76,6 +76,12 @@ export const en = { "export.configurator.csharp.dtoClass": "Create DTO class", "export.configurator.csharp.dtoClassName": "Class name", + // typescript + "export.configurator.typescript.declarationType":"Declaration type", + "export.configurator.typescript.declarationType.interface.name":"Interface name", + "export.configurator.typescript.declarationType.type.name":"Type name", + "export.configurator.typescript.variableName":"Variable name", + // ------------------------------------------------------------------------------------------------------------- // data types diff --git a/src/locale/translations/zhCN.ts b/src/locale/translations/zhCN.ts index c252c7c..65eb611 100644 --- a/src/locale/translations/zhCN.ts +++ b/src/locale/translations/zhCN.ts @@ -3,9 +3,9 @@ export const zhCN = { // export // export category - "export.category.fileTypes": "常见格式", - "export.category.databases" : "数据库", - "export.category.programmingLanguages": "编程语言", + "export.category.file_types": "常见格式", + "export.category.databases": "数据库", + "export.category.programming_languages": "编程语言", // export format modal "export.configurator.modal.title": "生成格式", @@ -34,7 +34,7 @@ export const zhCN = { // csv "export.configurator.csv.delimiter": "分隔符", - "export.configurator.csv.delimiter.required" : "分隔符不可为空", + "export.configurator.csv.delimiter.required": "分隔符不可为空", "export.configurator.csv.includeHeader": "包含表头", "export.configurator.csv.endLineChar": "行结束符", @@ -57,14 +57,37 @@ export const zhCN = { "export.configurator.javascript.declarationKeyword": "声明类型", "export.configurator.javascript.module": "模块", + // sql + "export.configurator.sql.type": "数据库类型", + "export.configurator.sql.tableName": "表名", + "export.configurator.sql.tableName.required": "表名不能为空", + "export.configurator.sql.statement": "SQL语句", + "export.configurator.sql.batchSize": "批量大小", + "export.configurator.sql.includeDropTable": "包含 `DROP TABLE`", + "export.configurator.sql.includeCreateTable": "包含 `CREATE TABLE`", + "export.configurator.sql.includePrimaryKey": "包含主键", + "export.configurator.sql.primaryKeyColumnName": "主键列名", + + // c# + "export.configurator.csharp.collectionType": "集合类型", + "export.configurator.csharp.collectionName": "集合变量名", + "export.configurator.csharp.dtoClass": "创建DTO类", + "export.configurator.csharp.dtoClassName": "类名", + + // typescript + "export.configurator.typescript.declarationType": "结构声明类型", + "export.configurator.typescript.declarationType.interface.name": "Interface命名", + "export.configurator.typescript.declarationType.type.name": "Type命名", + "export.configurator.typescript.variableName": "变量名", + // ------------------------------------------------------------------------------------------------------------- // data types - - + + // url "dataType.url": "网址", - "dataType.url.appendSlash.label":"斜线", - "dataType.url.protocol.label":"传输协议", + "dataType.url.appendSlash.label": "斜线", + "dataType.url.protocol.label": "传输协议", // domainsuffix "dataType.domainsuffix": "域名后缀", @@ -83,7 +106,7 @@ export const zhCN = { // color "dataType.color": "颜色", "dataType.color.kind.label": "类型", - "dataType.color.format.humanWord":"词语", + "dataType.color.format.humanWord": "词语", "dataType.color.format.label": "格式", // phone @@ -96,15 +119,15 @@ export const zhCN = { "dataType.emoji.type": "类型", "dataType.emoji.type.all": "全部", "dataType.emoji.type.smiley": "笑脸", - "dataType.emoji.type.body" : "人体", - "dataType.emoji.type.person" : "人物", - "dataType.emoji.type.nature" : "自然", - "dataType.emoji.type.food" : "食物", - "dataType.emoji.type.travel" : "旅行", - "dataType.emoji.type.activity" : "活动", - "dataType.emoji.type.object" : "物品", - "dataType.emoji.type.symbol" : "符号", - "dataType.emoji.type.flag" : "国旗", + "dataType.emoji.type.body": "人体", + "dataType.emoji.type.person": "人物", + "dataType.emoji.type.nature": "自然", + "dataType.emoji.type.food": "食物", + "dataType.emoji.type.travel": "旅行", + "dataType.emoji.type.activity": "活动", + "dataType.emoji.type.object": "物品", + "dataType.emoji.type.symbol": "符号", + "dataType.emoji.type.flag": "国旗", // persontitle "dataType.persontitle": "人物称谓", @@ -126,11 +149,11 @@ export const zhCN = { "dataType.number.kind.label": "种类", "dataType.number.precision.label": "精度", "dataType.number.min.label": "最小值", - "dataType.number.min.tooltip":"生成数据的最小值", + "dataType.number.min.tooltip": "生成数据的最小值", "dataType.number.min.errorMessage.empty": "最小值不能为空", "dataType.number.min.errorMessage.greaterThanMax": "最小值不能大于最大值", "dataType.number.max.label": "最大值", - "dataType.number.max.tooltip":"生成数据的最大值", + "dataType.number.max.tooltip": "生成数据的最大值", "dataType.number.max.errorMessage.empty": "最大值不能为空", "dataType.number.max.errorMessage.lessThanMin": "最大值不能小于最小值", diff --git a/src/pages/workspace/index.tsx b/src/pages/workspace/index.tsx index baf1dc7..6a2b6d7 100644 --- a/src/pages/workspace/index.tsx +++ b/src/pages/workspace/index.tsx @@ -54,7 +54,7 @@ export default function Workspace() { return ( <> - {intl.formatMessage({id: "nav.item.workspace"})} - Duymmi + Dummyi - Mock Data Generator diff --git a/src/utils/formatterUtils.ts b/src/utils/formatterUtils.ts index 999bbcd..ad475d2 100644 --- a/src/utils/formatterUtils.ts +++ b/src/utils/formatterUtils.ts @@ -2,6 +2,7 @@ import {FormatRequest, Formatter} from "@/types/formatter"; import {ExportFormat} from "@/constants/enums"; import {formatters} from "@/core/formatters"; import {langs} from '@uiw/codemirror-extensions-langs'; +import {DataFieldList} from "@/types/generator"; // format data export const formatData = (request: FormatRequest): string => { @@ -86,7 +87,53 @@ export const getCodemirrorLanguagePluginByFormat = (format: ExportFormat): any = return langs.sql(); case ExportFormat.CSHARP: return langs.csharp(); + case ExportFormat.TYPESCRIPT: + return langs.typescript(); default: return langs.mathematica(); } -} \ No newline at end of file +} + +// Get json string without quotes in field names +export function toJsonListStringWithoutQuotes(fields: DataFieldList, sortedFieldIds: string[], values: any[]): string { + const convert = (value: any, indent = 2): string => { // 改为直接处理任意值 + const indentSpace = ' '.repeat(indent); + const nextIndentSpace = ' '.repeat(indent + 2); + + if (Array.isArray(value)) { // 处理数组格式 + const elements = value.map(element => convert(element, indent + 2)); + return `[\n${nextIndentSpace}${elements.join(`,\n${nextIndentSpace}`)}\n${indentSpace}]`; + } else if (typeof value === 'object' && value !== null) { // 处理对象格式 + const entries = Object.entries(value).map(([key, val]) => { + const formattedValue = convert(val, indent + 2); + return `${nextIndentSpace}${key}: ${formattedValue}`; + }); + return `{\n${entries.join(',\n')}\n${indentSpace}}`; + } else { // 处理基础类型 + return JSON.stringify(value); + } + }; + + let output = "[\n"; // 开始数组并添加换行符 + values.forEach((item, index) => { + const row: Record = {}; // 允许任何类型的值 + for (const column of sortedFieldIds) { + const field = fields[column]; + const {isDraft, fieldName} = field; + const itemValue = item[column]; + let {value} = itemValue; + + if (!isDraft && value !== null) { + if (typeof value === 'bigint') { + value = value.toString(); // 处理 bigint 为字符串 + } + row[fieldName] = value; + } + } + // 添加对象到数组,确保正确的缩进和换行 + output += ` ${convert(row)}${index < values.length - 1 ? ',\n' : '\n'}`; + }); + + output += "]"; // 结束数组 + return output; +} From 522b22e5c8d11b1d04603fc9f017771d115d4f05 Mon Sep 17 00:00:00 2001 From: Zach Wang Date: Tue, 12 Mar 2024 00:26:33 +1300 Subject: [PATCH 4/6] feat: add required prop to OptionsInput --- src/components/Utils/src/OptionsInput.tsx | 4 +++- src/core/formatters/Sql/Sql.tsx | 21 +++-------------- src/core/formatters/Typescript/Typescript.tsx | 6 +++-- src/locale/index.tsx | 1 + src/locale/translations/en.ts | 23 ++++++++++--------- src/locale/translations/zhCN.ts | 1 + 6 files changed, 24 insertions(+), 32 deletions(-) diff --git a/src/components/Utils/src/OptionsInput.tsx b/src/components/Utils/src/OptionsInput.tsx index 8d96707..61ff424 100644 --- a/src/components/Utils/src/OptionsInput.tsx +++ b/src/components/Utils/src/OptionsInput.tsx @@ -2,6 +2,7 @@ import React from 'react'; import {ErrorTooltip, InfoTooltip} from "@/components/Utils"; import {Input} from "@douyinfe/semi-ui"; import {isNullOrWhiteSpace} from "@/utils/stringUtils"; +import {useIntl} from "@/locale"; export interface OptionsInputProps { label: string | React.ReactNode; @@ -16,6 +17,7 @@ export interface OptionsInputProps { export const OptionsInput: React.FunctionComponent = ({...props}) => { const {label, infoTooltip, errorMessage, value, style, suffix, onChange, required} = props; + const intl = useIntl(); // Add a new useState to manage the validation error message const [validationError, setValidationError] = React.useState(); @@ -23,7 +25,7 @@ export const OptionsInput: React.FunctionComponent = ({...pro // Add effect to validate value when it changes or when required status changes React.useEffect(() => { if (required && isNullOrWhiteSpace(value)) { - setValidationError('This field is required.'); // Set default required error message or use props.errorMessage + setValidationError(intl.formatMessage({id: 'error.input.isRequired'})); // Set default required error message or use props.errorMessage } else { setValidationError(undefined); // Clear error message when input is valid } diff --git a/src/core/formatters/Sql/Sql.tsx b/src/core/formatters/Sql/Sql.tsx index d5fdd17..161922c 100644 --- a/src/core/formatters/Sql/Sql.tsx +++ b/src/core/formatters/Sql/Sql.tsx @@ -1,8 +1,7 @@ import React from "react"; import {FormatRequest, FormatterConfigComponentInterface} from "@/types/formatter"; import {OptionsInput, OptionsNumberInput, OptionsSelect, SelectOption} from "@/components/Utils"; -import {FormattedMessage, useIntl} from "@/locale"; -import {isNullOrWhiteSpace} from "@/utils/stringUtils"; +import {FormattedMessage} from "@/locale"; import {OptionsSwitch} from "@/components/Utils/src/OptionsSwitch"; import {Divider} from "@douyinfe/semi-ui"; import {DataField} from "@/types/generator"; @@ -149,8 +148,6 @@ const generateInsertStatements = (sqlType: SqlType, tableName: string, sortedFie // Modify the format function to adapt to different SQL dialects export const format = (request: FormatRequest): string => { - console.log(request) - const {fields, values, config, sortedFieldIds} = request; const {type, tableName, batchSize, dropTable, createTable, primaryKey, primaryKeyColumnName} = config; @@ -201,24 +198,12 @@ export const format = (request: FormatRequest): string => { export const SqlConfigComponent: React.FC = ({...props}) => { const {config, onConfigChange} = props; - const intl = useIntl(); // action const handleValueChange = (field: string, value: any) => { onConfigChange({...config, [field]: value}) } - const [errorMessages, setErrorMessages] = React.useState({tableName: ''}); - React.useEffect(() => { - const newErrorMessages = {...errorMessages}; - if (isNullOrWhiteSpace(config.tableName)) { - newErrorMessages.tableName = intl.formatMessage({id: 'export.configurator.sql.tableName.required'}); - } else { - newErrorMessages.tableName = ''; - } - setErrorMessages(newErrorMessages); - }, [config.tableName]); - return (
= ( handleValueChange('tableName', value) }} style={{width: '150px'}} - errorMessage={errorMessages.tableName} + required /> = ( handleValueChange('primaryKeyColumnName', value) }} style={{width: '80px'}} - errorMessage={errorMessages.tableName} + required />}
} diff --git a/src/core/formatters/Typescript/Typescript.tsx b/src/core/formatters/Typescript/Typescript.tsx index 0022548..5d5f286 100644 --- a/src/core/formatters/Typescript/Typescript.tsx +++ b/src/core/formatters/Typescript/Typescript.tsx @@ -64,9 +64,9 @@ export const format = (request: FormatRequest): string => { fieldType = 'string[]' break; } - output+= ` ${field.fieldName}${field.emptyRate !== 0 ? "?" : ""}: ${fieldType};\n`; + output += ` ${field.fieldName}${field.emptyRate !== 0 ? "?" : ""}: ${fieldType};\n`; }); - output+="}; \n\n" + output += "}; \n\n" // values output += `export const ${variableName}: ${declarationName}[] = ${toJsonListStringWithoutQuotes(fields, sortedFieldIds, values)};`; @@ -101,6 +101,7 @@ export const TypescriptConfigComponent: React.FC } value={config.declarationName} @@ -109,6 +110,7 @@ export const TypescriptConfigComponent: React.FC } value={config.variableName} onChange={(v) => handleValueChange("variableName", v)} diff --git a/src/locale/index.tsx b/src/locale/index.tsx index 0e53a80..06e9400 100644 --- a/src/locale/index.tsx +++ b/src/locale/index.tsx @@ -58,6 +58,7 @@ export type FormattedMessageProps = ReactIntlFormattedMessageProps; } \ No newline at end of file diff --git a/src/locale/translations/en.ts b/src/locale/translations/en.ts index 48343b8..bcbef4e 100644 --- a/src/locale/translations/en.ts +++ b/src/locale/translations/en.ts @@ -5,7 +5,7 @@ export const en = { // export category "export.category.file_types": "General file types", - "export.category.databases" : "Databases", + "export.category.databases": "Databases", "export.category.programming_languages": "Programming languages", // export format modal @@ -35,7 +35,7 @@ export const en = { // csv "export.configurator.csv.delimiter": "Delimiter", - "export.configurator.csv.delimiter.required" : "Delimiter cannot be empty", + "export.configurator.csv.delimiter.required": "Delimiter cannot be empty", "export.configurator.csv.includeHeader": "Include header", "export.configurator.csv.endLineChar": "End of line characters", @@ -77,26 +77,26 @@ export const en = { "export.configurator.csharp.dtoClassName": "Class name", // typescript - "export.configurator.typescript.declarationType":"Declaration type", - "export.configurator.typescript.declarationType.interface.name":"Interface name", - "export.configurator.typescript.declarationType.type.name":"Type name", - "export.configurator.typescript.variableName":"Variable name", + "export.configurator.typescript.declarationType": "Declaration type", + "export.configurator.typescript.declarationType.interface.name": "Interface name", + "export.configurator.typescript.declarationType.type.name": "Type name", + "export.configurator.typescript.variableName": "Variable name", // ------------------------------------------------------------------------------------------------------------- // data types - - + + // url "dataType.url": "Url", "dataType.url.appendSlash.label": "Slash", - "dataType.url.protocol.label":"Protocol", + "dataType.url.protocol.label": "Protocol", // domainsuffix "dataType.domainsuffix": "Domain Suffix", // domainname "dataType.domainname": "Domain Name", - + // accountnumber "dataType.accountnumber": "Account Number", "dataType.accountnumber.length": "Length", @@ -109,7 +109,7 @@ export const en = { "dataType.color": "Color", "dataType.color.kind.label": "Kind", "dataType.color.format.label": "Format", - "dataType.color.format.humanWord":"Human Word", + "dataType.color.format.humanWord": "Human Word", // phone "dataType.phone": "Phone Number", @@ -244,6 +244,7 @@ export const en = { "dataFields.type.modal.search.placeholder": "Search data type...", // error pages + "error.input.isRequired": "This field is required", "error.404.description": "Page does not exist", "error.404.button.text": "Home page", "error.general.description": "Oops! An error has occurred!", diff --git a/src/locale/translations/zhCN.ts b/src/locale/translations/zhCN.ts index 65eb611..2cdb78d 100644 --- a/src/locale/translations/zhCN.ts +++ b/src/locale/translations/zhCN.ts @@ -242,6 +242,7 @@ export const zhCN = { "dataFields.type.modal.search.placeholder": "搜索类型...", // error pages + "error.input.isRequired": "此项不能为空", "error.404.description": "页面不存在", "error.404.button.text": "首页", "error.general.description": "Oops! 出错了!", From eff5477f7eaed666087ccc920c8038c4a823f714 Mon Sep 17 00:00:00 2001 From: Zach Wang Date: Tue, 12 Mar 2024 00:40:46 +1300 Subject: [PATCH 5/6] local: update jaJP --- src/locale/translations/jaJP.ts | 251 ++++++++++++++++++++++---------- 1 file changed, 176 insertions(+), 75 deletions(-) diff --git a/src/locale/translations/jaJP.ts b/src/locale/translations/jaJP.ts index 3653901..5a73c9f 100644 --- a/src/locale/translations/jaJP.ts +++ b/src/locale/translations/jaJP.ts @@ -1,106 +1,200 @@ -// Translated by ChatGPT export const jaJP = { - // ------------------------------------------------------------------------------------------------------------- // export // export category - "export.category.fileTypes": "一般的なファイルタイプ", - "export.category.databases" : "Databases", - "export.category.programmingLanguages": "プログラミング言語", + "export.category.file_types": "一般的な形式", + "export.category.databases": "データベース", + "export.category.programming_languages": "プログラミング言語", // export format modal - "export.configurator.modal.title": "エクスポート形式", - "export.configurator.modal.closeButton.text": "閉じる", + "export.configurator.modal.title": "形式を生成", + "export.configurator.modal.confirmButton.text": "確認", "export.configurator.config.label": "設定", - "export.configurator.config.empty": "現在、このエクスポート形式の設定は利用できません。", + "export.configurator.config.empty": "この出力形式の設定は現在利用できません", + + // export modal + "export.modal.title": "データを一括生成", + "export.modal.exportNumOfRows.label": "生成するデータ行数", + "export.modal.exportNumOfRows.empty": "データ行数を空にすることはできません", + "export.modal.exportFormat.label": "生成形式", + "export.modal.estimatedSize.label": "推定サイズ", + "export.modal.estimatedTime.label": "推定時間", + "export.modal.exportFileName.label": "ファイル名", + "export.modal.exportFileName.empty": "ファイル名は空にできません", + "export.modal.cancel.button.text": "キャンセル", + "export.modal.generate.button.text": "生成", + "export.modal.hide.button.text": "隠す", + "export.modal.terminate.button.text": "終了", + "export.modal.generating.rows.text": "行数", + "export.modal.generating.time.text": "時間", + "export.modal.generating.done.text": "完了", + "export.modal.toast.details.button.text": "詳細", + "export.modal.toast.download.button.text": "ダウンロード", // csv "export.configurator.csv.delimiter": "区切り文字", - "export.configurator.csv.includeHeader": "ヘッダーを含める", - "export.configurator.csv.endLineChar": "行末文字", + "export.configurator.csv.delimiter.required": "区切り文字を空にすることはできません", + "export.configurator.csv.includeHeader": "ヘッダを含む", + "export.configurator.csv.endLineChar": "行終了文字", + + // xml + "export.configurator.xml.rootNodeName": "ルートノード", + "export.configurator.xml.childNodeName": "子ノード", + "export.configurator.xml.encoding": "エンコーディング", + "export.configurator.xml.indentSize": "インデントのサイズ", + + // json + "export.configurator.json.insideArray": "配列内", + "export.configurator.json.includeNullValues": "null値を含む", + + // javascript + "export.configurator.javascript.format": "形式", + "export.configurator.javascript.format.variable": "変数", + "export.configurator.javascript.format.export": "エクスポート", + "export.configurator.javascript.varName": "変数名", + "export.configurator.javascript.varName.required": "変数名を空にすることはできません", + "export.configurator.javascript.declarationKeyword": "宣言タイプ", + "export.configurator.javascript.module": "モジュール", + + // sql + "export.configurator.sql.type": "データベースのタイプ", + "export.configurator.sql.tableName": "テーブル名", + "export.configurator.sql.tableName.required": "テーブル名を空にすることはできません", + "export.configurator.sql.statement": "SQLステートメント", + "export.configurator.sql.batchSize": "バッチサイズ", + "export.configurator.sql.includeDropTable": "`DROP TABLE` を含む", + "export.configurator.sql.includeCreateTable": "`CREATE TABLE` を含む", + "export.configurator.sql.includePrimaryKey": "主キーを含む", + "export.configurator.sql.primaryKeyColumnName": "主キーカラム名", + + // c# + "export.configurator.csharp.collectionType": "コレクションタイプ", + "export.configurator.csharp.collectionName": "コレクションの変数名", + "export.configurator.csharp.dtoClass": "DTOクラスを作成", + "export.configurator.csharp.dtoClassName": "クラス名", + + // typescript + + + "export.configurator.typescript.declarationType": "宣言のタイプ", + "export.configurator.typescript.declarationType.interface.name": "Interfaceの名前", + "export.configurator.typescript.declarationType.type.name": "Typeの名前", + "export.configurator.typescript.variableName": "変数名", // ------------------------------------------------------------------------------------------------------------- // data types - - + + // url - "dataType.url": "Url", + "dataType.url": "URL", + "dataType.url.appendSlash.label": "スラッシュ", + "dataType.url.protocol.label": "プロトコル", // domainsuffix - "dataType.domainsuffix": "DomainSuffix", + "dataType.domainsuffix": "ドメインサフィックス", // domainname - "dataType.domainname": "DomainName", + "dataType.domainname": "ドメイン名", // accountnumber - "dataType.accountnumber": "AccountNumber", + "dataType.accountnumber": "銀行口座番号", + "dataType.accountnumber.length": "長さ", + "dataType.accountnumber.empty": "長さは空にできません", // accountname - "dataType.accountname": "AccountName", + "dataType.accountname": "アカウント名", // color - "dataType.color": "Color", + "dataType.color": "色", + "dataType.color.kind.label": "タイプ", + "dataType.color.format.humanWord": "言葉での形式", + "dataType.color.format.label": "形式", // phone - "dataType.phone": "Phone Number", - "dataType.phone.formats.label": "Formats", - "dataType.phone.formats.tooltips": "Phone number format, please use \"#\" to represent digits, press Enter key to confirm", + "dataType.phone": "電話番号", + "dataType.phone.formats.label": "形式", + "dataType.phone.formats.tooltips": "電話番号の形式は、「#」を数字の代わりに使用してください。エンターキーで確定", // emoji - "dataType.emoji": "Emoji", - "dataType.emoji.type": "Type", - "dataType.emoji.type.all": "All", - "dataType.emoji.type.smiley": "Smiley", - "dataType.emoji.type.body" : "Body", - "dataType.emoji.type.person" : "Person", - "dataType.emoji.type.nature" : "Nature", - "dataType.emoji.type.food" : "Food", - "dataType.emoji.type.travel" : "Travel", - "dataType.emoji.type.activity" : "Activity", - "dataType.emoji.type.object" : "Object", - "dataType.emoji.type.symbol" : "Symbol", - "dataType.emoji.type.flag" : "Flag", + "dataType.emoji": "絵文字", + "dataType.emoji.type": "タイプ", + "dataType.emoji.type.all": "全て", + "dataType.emoji.type.smiley": "スマイリー", + "dataType.emoji.type.body": "ボディ", + "dataType.emoji.type.person": "人", + "dataType.emoji.type.nature": "自然", + "dataType.emoji.type.food": "食べ物", + "dataType.emoji.type.travel": "旅行", + "dataType.emoji.type.activity": "アクティビティ", + "dataType.emoji.type.object": "オブジェクト", + "dataType.emoji.type.symbol": "シンボル", + "dataType.emoji.type.flag": "フラグ", // persontitle - "dataType.persontitle": "PersonTitle", + "dataType.persontitle": "称号", // middlename - "dataType.middlename": "MiddleName", + "dataType.middlename": "ミドルネーム", // lastname - "dataType.lastname": "LastName", + "dataType.lastname": "姓", // firstname - "dataType.firstname": "FirstName", + "dataType.firstname": "名", // sex - "dataType.sex": "Sex", - - // data type category - "dataType.category.all": "全て", - "dataType.category.basic": "基本", - "dataType.category.person": "個人", - "dataType.category.commerce": "商業", + "dataType.sex": "性別", // number - "dataType.number": "数値", + "dataType.number": "数字", "dataType.number.kind.label": "種類", "dataType.number.precision.label": "精度", "dataType.number.min.label": "最小値", + "dataType.number.min.tooltip": "生成するデータの最小値", + "dataType.number.min.errorMessage.empty": "最小値は空にできません", + "dataType.number.min.errorMessage.greaterThanMax": "最小値は最大値より大きくできません", "dataType.number.max.label": "最大値", + "dataType.number.max.tooltip": "生成するデータの最大値", + "dataType.number.max.errorMessage.empty": "最大値は空にできません", + "dataType.number.max.errorMessage.lessThanMin": "最大値は最小値より小さくできません", // dateTime "dataType.dateTime": "日時", // boolean "dataType.boolean": "ブール値", + "dataType.boolean.true.label": "真の確率", + "dataType.boolean.true.tooltip": "真(true)を生成する確率", + "dataType.boolean.true.errorMessage.empty": "真の確率を空にすることはできません", + "dataType.boolean.format.label": "形式", + + // full name + "dataType.fullName": "フルネーム", + "dataType.fullName.sex.label": "性別", + "dataType.fullName.sex.selectOptions.all": "男性、女性", + "dataType.fullName.sex.selectOptions.male": "男性", + "dataType.fullName.sex.selectOptions.female": "女性", + "dataType.fullName.firstName.label": "名", + "dataType.fullName.lastName.label": "姓", + + //email + "dataType.email": "メールアドレス", + "dataType.email.provider.label": "メールプロバイダー", + "dataType.email.allowSpecialCharacters.label": "特殊文字を許可", + + // company name + "dataType.companyName": "会社名", + + // account number + "dataType.accountNumber": "アカウント番号", - // person name - "dataType.personName": "氏名", - - // account mi,ner - "dataType.accountNumber": "口座番号", + // data type category + "dataType.category.all": "全て", + "dataType.category.basic": "基本", + "dataType.category.person": "人物", + "dataType.category.commerce": "商業", + "dataType.category.network": "ネットワーク", // ------------------------------------------------------------------------------------------------------------- // pages @@ -108,45 +202,52 @@ export const jaJP = { // nav bar "nav.item.home": "ホーム", "nav.item.workspace": "ワークスペース", - "nav.item.about": "について", + "nav.item.about": "紹介", "nav.colorModeSwitchButton.switchToDarkMode.text": "ダークモードに切り替え", "nav.colorModeSwitchButton.switchToLightMode.text": "ライトモードに切り替え", "nav.languageSwitchButton.tooltip": "言語", - "nav.languageSwitchModal.title": "言語の選択", - "nav.languageSwitchModal.footer.chatGPT.text": "多言語翻訳はChatGPTによって生成されました", + "nav.languageSwitchModal.title": "言語を選択", + "nav.languageSwitchModal.footer.chatGPT.text": "多言語翻訳はChatGPTによって生成されます", // workspace "toolbar.numOfRowInput.suffix": "行", - "toolbar.generateButton.text": "批量生成", - "toolbar.panelsOrientationButton.tooltip.switchToColumn": "パネルを列に切り替え", - "toolbar.panelsOrientationButton.tooltip.switchToRow": "パネルを行に切り替え", - "toolbar.emptyPageButton.tooltip": "ワークスペースを空にする", - "toolbar.emptyPageButton.confirmation.title": "ワークスペースを空にする", - "toolbar.emptyPageButton.confirmation.text": "本当にワークスペースを空にしますか?設定したワークスペースの構成が失われます。", - "toolbar.exportSchemaButton.tooltip": "スキーマのエクスポート", - "toolbar.importSchemaButton.tooltip": "スキーマのインポート", + "toolbar.generateButton.text": "一括生成", + "toolbar.panelsOrientationButton.tooltip.switchToColumn": "パネルを横向きに切り替え", + "toolbar.panelsOrientationButton.tooltip.switchToRow": "パネルを縦向きに切り替え", + "toolbar.emptyPageButton.tooltip": "ワークスペースをクリア", + "toolbar.emptyPageButton.confirmation.title": "ワークスペースをクリアしますか?", + "toolbar.emptyPageButton.confirmation.text": "ワークスペースをクリアしますか?すべての設定が削除されます。", + "toolbar.exportSchemaButton.tooltip": "スキーマをエクスポート", + "toolbar.importSchemaButton.tooltip": "スキーマをインポート", "preview.setting.regenerateButton.tooltip": "再生成", "preview.setting.lineNumberSwitch.tooltip.show": "行番号を表示", "preview.setting.lineNumberSwitch.tooltip.hide": "行番号を非表示", - "preview.setting.lineWarpSwitch.tooltip.enable": "行の折り返しを有効にする", - "preview.setting.lineWarpSwitch.tooltip.disable": "行の折り返しを無効にする", + "preview.setting.lineWarpSwitch.tooltip.enable": "自動改行を有効にする", + "preview.setting.lineWarpSwitch.tooltip.disable": "自動改行を無効にする", "preview.setting.copyToClipboard.tooltip": "クリップボードにコピー", "preview.setting.copyToClipboard.notification.success": "成功", - "preview.setting.copyToClipboard.notification.content": "内容がクリップボードにコピーされました。", - "preview.setting.rawView.text": "生データ", - "preview.setting.tableView.text": "表形式", - "dataFields.list.addNewFieldButton.text": "フィールドを追加", + "preview.setting.copyToClipboard.notification.content": "クリップボードにコピーされました", + "preview.setting.rawView.text": "原文", + "preview.setting.tableView.text": "テーブル", + "dataFields.list.addNewFieldButton.text": "新しいフィールドを追加", + "dataFields.list.noDataFields.text": "フィールドがまだありません", + "dataFields.list.createFirstField.text": "最初のフィールドを作成しましょう!", "dataFields.input.fieldName.label": "フィールド名", - "dataFields.input.type.label": "タイプ", - "dataFields.input.type.placeholder": "タイプを選択", - "dataFields.input.emptyRate.label": "空の確率", + "dataFields.input.fieldName.errorMessage.empty": "フィールド名は空にできません", + "dataFields.input.type.label": "データタイプ", + "dataFields.input.type.placeholder": "タイプを選択...", + "dataFields.input.emptyRate.label": "空値の確率", + "dataFields.input.emptyRate.tooltip": "空値(null)を生成する確率", + "dataFields.input.emptyRate.errorMessage.empty": "空値の確率は空にできません", "dataFields.input.options.label": "オプション", - "dataFields.type.modal.title": "データタイプ", - "dataFields.type.modal.search.placeholder": "データタイプを検索...", + "dataFields.type.modal.title": "タイプを選択", + "dataFields.type.modal.search.placeholder": "タイプを検索...", // error pages + "error.input.isRequired": "この項目は必須です", "error.404.description": "ページが存在しません", "error.404.button.text": "ホームページ", - "error.general.description": "おっと!エラーが発生しました。", + "error.general.description": "おっと!エラーが発生しました!", "error.general.button.text": "ホームページ", -}; \ No newline at end of file + +} From a5045c6880b82ea54f3d2fbb2eaea116f5c37538 Mon Sep 17 00:00:00 2001 From: Zach Wang Date: Thu, 14 Mar 2024 13:56:02 +1300 Subject: [PATCH 6/6] feat: add DateTime, Birthday, Weekday, Month generators (#269) --- package-lock.json | 4 +- package.json | 2 +- scripts/addNewGenerator.ts | 6 +- .../DevTools/src/GenerateResultsPreviewer.tsx | 2 +- src/components/InputPanel/src/InputPanel.tsx | 2 +- .../src/components/DataTypeSelectModal.tsx | 8 +- .../Utils/src/OptionsDatetimePicker.tsx | 55 +++++ src/components/Utils/src/OptionsSwitch.tsx | 2 +- src/constants/enums.ts | 23 ++- src/core/formatters/CSharp/CSharp.tsx | 3 + .../formatters/CSharp/CSharpFormatterUtils.ts | 13 ++ src/core/formatters/Sql/Sql.tsx | 42 +++- src/core/formatters/Typescript/Typescript.tsx | 3 + src/core/generators/Birthday/Birthday.tsx | 143 +++++++++++++ src/core/generators/Birthday/index.ts | 14 ++ src/core/generators/DateTime/DateTime.tsx | 189 ++++++++++++++++++ src/core/generators/DateTime/index.ts | 14 ++ src/core/generators/Month/Month.tsx | 49 +++++ src/core/generators/Month/index.ts | 14 ++ src/core/generators/Number/Number.tsx | 8 +- src/core/generators/Weekday/Weekday.tsx | 47 +++++ src/core/generators/Weekday/index.ts | 14 ++ src/core/generators/index.ts | 8 + src/locale/translations/en.ts | 43 +++- src/locale/translations/jaJP.ts | 24 +++ src/locale/translations/zhCN.ts | 39 +++- src/utils/formatterUtils.ts | 20 +- src/utils/typeUtils.ts | 15 ++ 28 files changed, 766 insertions(+), 40 deletions(-) create mode 100644 src/components/Utils/src/OptionsDatetimePicker.tsx create mode 100644 src/core/generators/Birthday/Birthday.tsx create mode 100644 src/core/generators/Birthday/index.ts create mode 100644 src/core/generators/DateTime/DateTime.tsx create mode 100644 src/core/generators/DateTime/index.ts create mode 100644 src/core/generators/Month/Month.tsx create mode 100644 src/core/generators/Month/index.ts create mode 100644 src/core/generators/Weekday/Weekday.tsx create mode 100644 src/core/generators/Weekday/index.ts diff --git a/package-lock.json b/package-lock.json index 8916a54..9de3fe5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "dummyi", - "version": "0.1.0", + "version": "0.2.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "dummyi", - "version": "0.1.0", + "version": "0.2.2", "dependencies": { "@douyinfe/semi-next": "^2.51.0", "@douyinfe/semi-ui": "^2.51.0", diff --git a/package.json b/package.json index d7ada64..e0590dc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "dummyi", - "version": "0.2.2", + "version": "0.2.3", "private": true, "scripts": { "dev": "next dev", diff --git a/scripts/addNewGenerator.ts b/scripts/addNewGenerator.ts index e81710c..5403898 100644 --- a/scripts/addNewGenerator.ts +++ b/scripts/addNewGenerator.ts @@ -106,7 +106,7 @@ export const ${generatorName}GeneratorDefaultOptions:${generatorName}GeneratorOp // ------------------------------------------------------------------------------------------------------------- // generate method -export const generate = (options: any): GenerateResult => { +export const generate = (options: ${generatorName}GeneratorOptions): GenerateResult => { // TODO: implement your own generate method here return { @@ -125,9 +125,9 @@ export const ${generatorName}GeneratorOptionsComponent: React.FunctionComponent< // TODO: implement your own options component here return ( -
+ <> NOT IMPLEMENTED -
+ ); }`; } diff --git a/src/components/DevTools/src/GenerateResultsPreviewer.tsx b/src/components/DevTools/src/GenerateResultsPreviewer.tsx index 595aa5a..5c90700 100644 --- a/src/components/DevTools/src/GenerateResultsPreviewer.tsx +++ b/src/components/DevTools/src/GenerateResultsPreviewer.tsx @@ -77,7 +77,7 @@ export const GenerateResultsPreviewer: React.FunctionComponent - + {formatType} {isError && ERROR} diff --git a/src/components/InputPanel/src/InputPanel.tsx b/src/components/InputPanel/src/InputPanel.tsx index 3f66bee..bdc375e 100644 --- a/src/components/InputPanel/src/InputPanel.tsx +++ b/src/components/InputPanel/src/InputPanel.tsx @@ -23,7 +23,7 @@ export const InputPanel: React.FunctionComponent = () => { setPanelHeight(containerHeight); - if (containerWidth > 1000) { + if (containerWidth > 900) { setComponentSize(ComponentSize.LARGE); } else if (containerWidth > 550) { setComponentSize(ComponentSize.MEDIUM); diff --git a/src/components/InputPanel/src/components/DataTypeSelectModal.tsx b/src/components/InputPanel/src/components/DataTypeSelectModal.tsx index 72a0037..2200c79 100644 --- a/src/components/InputPanel/src/components/DataTypeSelectModal.tsx +++ b/src/components/InputPanel/src/components/DataTypeSelectModal.tsx @@ -21,7 +21,7 @@ export interface DataTypeSelectModalProps { export const DataTypeSelectModal: React.FunctionComponent = ({...props}) => { const intl = useIntl(); - const {Title} = Typography; + const {Title, Text} = Typography; const dispatch = useDispatch(); const [searchText, setSearchText] = React.useState(null); const data = useMemo(() => getGeneratorList(searchText, intl), [intl, searchText]); @@ -34,7 +34,7 @@ export const DataTypeSelectModal: React.FunctionComponent { - dispatch(doChangeDataType(currentTargetDataFieldId,item.type)); + dispatch(doChangeDataType(currentTargetDataFieldId, item.type)); onCancel(); } @@ -106,7 +106,9 @@ export const DataTypeSelectModal: React.FunctionComponent (
- {example} + + {example} +
)) } diff --git a/src/components/Utils/src/OptionsDatetimePicker.tsx b/src/components/Utils/src/OptionsDatetimePicker.tsx new file mode 100644 index 0000000..cf6a92e --- /dev/null +++ b/src/components/Utils/src/OptionsDatetimePicker.tsx @@ -0,0 +1,55 @@ +import React from "react"; +import {ErrorTooltip, InfoTooltip} from "@/components/Utils"; +import {useIntl} from "@/locale"; +import {isNullOrWhiteSpace} from "@/utils/stringUtils"; +import {DatePicker} from "@douyinfe/semi-ui"; +import {hasValue} from "@/utils/typeUtils"; + +export interface OptionsDatetimePickerProps { + label: string | React.ReactNode; + type?: "date" | "dateTime" | "dateRange" | "dateTimeRange"; + infoTooltip?: string | React.ReactNode; + errorMessage?: string; + value: string | number | Date | string[] | number[] | Date[]; + onChange: (value: any) => void; + style?: React.CSSProperties; + required?: boolean; +} + +export const OptionsDatetimePicker: React.FunctionComponent = ({...props}) => { + const {label, type, infoTooltip, errorMessage, value, style, onChange, required} = props; + const intl = useIntl(); + + // Add a new useState to manage the validation error message + const [validationError, setValidationError] = React.useState(); + + // Add effect to validate value when it changes or when required status changes + React.useEffect(() => { + if (required && !hasValue(value)) { + setValidationError(intl.formatMessage({id: 'error.input.isRequired'})); // Set default required error message or use props.errorMessage + } else { + setValidationError(undefined); // Clear error message when input is valid + } + }, [value, required]); + + return ( +
+
+ {label} + {infoTooltip && + {infoTooltip} + } +
+ + onChange(dateString)} + value={value} + style={style} + validateStatus={!isNullOrWhiteSpace(validationError || errorMessage) ? 'error' : 'default'} + /> + +
+ ) +} \ No newline at end of file diff --git a/src/components/Utils/src/OptionsSwitch.tsx b/src/components/Utils/src/OptionsSwitch.tsx index 05eb65d..9c4acd7 100644 --- a/src/components/Utils/src/OptionsSwitch.tsx +++ b/src/components/Utils/src/OptionsSwitch.tsx @@ -27,7 +27,7 @@ export const OptionsSwitch: React.FunctionComponent = ({...p diff --git a/src/constants/enums.ts b/src/constants/enums.ts index 918d080..f0a0ab6 100644 --- a/src/constants/enums.ts +++ b/src/constants/enums.ts @@ -1,4 +1,3 @@ - // export format export enum ExportFormatCategory { FILE_TYPES = "file_types", @@ -18,17 +17,18 @@ export enum ExportFormat { export enum ValueType { STRING = "string", - TEXT= "text", + TEXT = "text", ONE_BIT = "1bit", INT = "integer", BIGINT = "bigint", DOUBLE = "double", BOOLEAN = "boolean", INT_LIST = "int_list", - STRING_LIST = "string_list" + STRING_LIST = "string_list", + DATE_TIME = "date_time" } -export enum ExportProcessStage{ +export enum ExportProcessStage { PREVIEW = "preview", GENERATING = "generating", COMPLETED = "completed", @@ -36,14 +36,19 @@ export enum ExportProcessStage{ // data types export enum DataTypeCategory { - ALL= "all", + ALL = "all", BASIC = "basic", PERSON = "person", NETWORK = "network", COMMERCE = "commerce", + DATETIME = "datetime" } export enum DataType { + BIRTHDAY = "birthday", + MONTH = "month", + WEEKDAY = "weekday", + DATETIME = "datetime", URL = "url", DOMAINSUFFIX = "domainsuffix", DOMAINNAME = "domainname", @@ -81,18 +86,18 @@ export enum Locales { JA_JP = "ja-JP", } -export enum ComponentSize{ +export enum ComponentSize { SMALL = "small", MEDIUM = "medium", LARGE = "large", } -export enum PreviewType{ +export enum PreviewType { TABLE = "table", RAW = "raw" } -export enum CollectionNodeType{ +export enum CollectionNodeType { COLLECTION = "collection", SCHEMA = "schema" } @@ -102,7 +107,7 @@ export enum EndOfLineChars { CRLF = '\r\n' } -export enum Sex{ +export enum Sex { ALL = "all", MALE = "male", FEMALE = "female" diff --git a/src/core/formatters/CSharp/CSharp.tsx b/src/core/formatters/CSharp/CSharp.tsx index cff70b9..ed4d198 100644 --- a/src/core/formatters/CSharp/CSharp.tsx +++ b/src/core/formatters/CSharp/CSharp.tsx @@ -72,6 +72,9 @@ export const format = (request: FormatRequest): string => { case ValueType.ONE_BIT: fieldType = `int${field.emptyRate !== 0 ? "?" : ""}`; break; + case ValueType.DATE_TIME: + fieldType = `DateTime${field.emptyRate !== 0 ? "?" : ""}` + break; // Add more cases as necessary } csharpCode += ` public ${fieldType} ${field.fieldName} { get; set; }\n`; diff --git a/src/core/formatters/CSharp/CSharpFormatterUtils.ts b/src/core/formatters/CSharp/CSharpFormatterUtils.ts index f3cfed2..8eea719 100644 --- a/src/core/formatters/CSharp/CSharpFormatterUtils.ts +++ b/src/core/formatters/CSharp/CSharpFormatterUtils.ts @@ -26,7 +26,20 @@ export function formatValueForCSharp(generateResult: GenerateResult, valueType: return `new List { ${value.join(", ")} }` case ValueType.STRING_LIST: return `new List { ${value.map(item => `"${item}"`).join(', ')})} }` + case ValueType.DATE_TIME: + return `DateTime.Parse("${toCSharpDateTimeFormat(value)}")` default: return 'null'; // Or some other default case } +} + +export function toCSharpDateTimeFormat(date: Date): string { + const year = date.getFullYear(); + const month = String(date.getMonth() + 1).padStart(2, '0'); // 月份是从0开始的 + const day = String(date.getDate()).padStart(2, '0'); + + const hours = String(date.getHours()).padStart(2, '0'); + const minutes = String(date.getMinutes()).padStart(2, '0'); + + return `${year}-${month}-${day} ${hours}:${minutes}`; } \ No newline at end of file diff --git a/src/core/formatters/Sql/Sql.tsx b/src/core/formatters/Sql/Sql.tsx index 161922c..86d10df 100644 --- a/src/core/formatters/Sql/Sql.tsx +++ b/src/core/formatters/Sql/Sql.tsx @@ -61,14 +61,16 @@ const addCreateTableColumn = (field: DataField, sqlType: SqlType) => { fieldType = (sqlType === SqlType.ORACLE || sqlType === SqlType.IBMDB2) ? "CLOB" : "TEXT"; break; case ValueType.ONE_BIT: - fieldType = "TINYINT(1)"; - break; case ValueType.BOOLEAN: fieldType = "TINYINT(1)"; break; case ValueType.BIGINT: fieldType = "BIGINT"; break; + case ValueType.DATE_TIME: // Add this line + // Define default DATETIME format, then override per SQL type if necessary + fieldType = "DATETIME"; + break; default: fieldType = "VARCHAR(255)"; break; @@ -77,23 +79,29 @@ const addCreateTableColumn = (field: DataField, sqlType: SqlType) => { // Apply SQL type-specific modifications switch (sqlType) { case SqlType.ORACLE: - // Oracle-specific adaptations, e.g., use NUMBER instead of INT + // Oracle-specific adaptations if (fieldType === "INT") { fieldType = "NUMBER"; } else if (fieldType === "TINYINT(1)") { fieldType = "NUMBER(1)"; + } else if (fieldType === "DATETIME") { // Add this line + fieldType = "TIMESTAMP"; // Or DATE, depending on your needs } break; case SqlType.POSTGRES: - // Postgres-specific adaptations, e.g., use BOOLEAN instead of TINYINT(1) + // Postgres-specific adaptations if (fieldType === "TINYINT(1)") { fieldType = "BOOLEAN"; + } else if (fieldType === "DATETIME") { // Add this line + fieldType = "TIMESTAMP"; // Or TIMESTAMP WITHOUT TIME ZONE, depending on your needs } break; case SqlType.SQLITE: - // SQLite uses a more dynamic type system + // SQLite adaptations if (fieldType === "TINYINT(1)") { fieldType = "INTEGER"; + } else if (fieldType === "DATETIME") { // Add this line + fieldType = "TEXT"; // SQLite uses TEXT for date and time types } break; // Add cases for other SQL types as necessary @@ -116,7 +124,29 @@ const formatValueForSQL = (value: any, sqlType: SqlType, valueType: ValueType): case ValueType.INT_LIST: return `'${value.join(", ")}'` case ValueType.STRING_LIST: - return `'${value.map(item => `"${item}"`).join(', ')}'` + return `'${value.map((item: string): string => `"${item}"`).join(', ')}'` + case ValueType.DATE_TIME: + let formattedDate: string; + // Assuming value is a JavaScript Date object for simplicity + const date = (value instanceof Date) ? value : new Date(value); + + switch (sqlType) { + case SqlType.ORACLE: + // Oracle format: 'YYYY-MM-DD HH24:MI:SS' + formattedDate = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')} ${String(date.getHours()).padStart(2, '0')}:${String(date.getMinutes()).padStart(2, '0')}:${String(date.getSeconds()).padStart(2, '0')}`; + break; + case SqlType.POSTGRES: + // PostgreSQL format: 'YYYY-MM-DD HH:MM:SS' + formattedDate = date.toISOString().slice(0, 19).replace('T', ' '); + break; + // Add additional cases for different SQL types as needed + default: + // Default to ISO format 'YYYY-MM-DD HH:MM:SS' + formattedDate = date.toISOString().slice(0, 19).replace('T', ' '); + break; + } + + return `'${formattedDate}'`; default: return value; } diff --git a/src/core/formatters/Typescript/Typescript.tsx b/src/core/formatters/Typescript/Typescript.tsx index 5d5f286..7a645d1 100644 --- a/src/core/formatters/Typescript/Typescript.tsx +++ b/src/core/formatters/Typescript/Typescript.tsx @@ -63,6 +63,9 @@ export const format = (request: FormatRequest): string => { case ValueType.STRING_LIST: fieldType = 'string[]' break; + case ValueType.DATE_TIME: + fieldType = "Date" + break; } output += ` ${field.fieldName}${field.emptyRate !== 0 ? "?" : ""}: ${fieldType};\n`; }); diff --git a/src/core/generators/Birthday/Birthday.tsx b/src/core/generators/Birthday/Birthday.tsx new file mode 100644 index 0000000..d7912d1 --- /dev/null +++ b/src/core/generators/Birthday/Birthday.tsx @@ -0,0 +1,143 @@ +import React from "react"; +import {GenerateResult, GeneratorOptionsComponentInterface} from "@/types/generator"; +import {DateTimeFormat, DateTimeFormatSelectOptions, formatDateTime} from "@/core/generators/DateTime/DateTime"; +import {OptionsNumberInput, OptionsSelect, SelectOption} from "@/components/Utils"; +import {FormattedMessage} from "@/locale"; +import {faker} from "@faker-js/faker"; +import {ValueType} from "@/constants/enums"; + +// ------------------------------------------------------------------------------------------------------------- +// types + +export enum BirthdayGeneratorMode { + AGE = "age", + YEAR = "year" +} + +export interface BirthdayGeneratorOptions { + format: DateTimeFormat; + mode: BirthdayGeneratorMode; + minAge: number; + maxAge: number; + fromYear: number; + toYear: number; +} + +// ------------------------------------------------------------------------------------------------------------- +// default options +export const BirthdayGeneratorDefaultOptions: BirthdayGeneratorOptions = { + format: DateTimeFormat.DATETIME, + mode: BirthdayGeneratorMode.AGE, + minAge: 1, + maxAge: 80, + fromYear: 1960, + toYear: 2000, +} + +// ------------------------------------------------------------------------------------------------------------- +// generate method +export const generate = (options: BirthdayGeneratorOptions): GenerateResult => { + + const value = faker.date.birthdate({ + mode: options.mode, + min: options.mode === BirthdayGeneratorMode.AGE ? options.minAge : options.fromYear, + max: options.mode === BirthdayGeneratorMode.AGE ? options.maxAge : options.toYear, + refDate: Date.now() + }) + const output = formatDateTime(value, options.format); + + return { + value: output, + stringValue: (output instanceof Date) ? output.toISOString() : output.toString() + }; +} + +// ------------------------------------------------------------------------------------------------------------- +// options component +export const BirthdayGeneratorOptionsComponent: React.FunctionComponent = ({...props}) => { + const {options, handleOptionValueChange} = props as { + options: BirthdayGeneratorOptions, + handleOptionValueChange: typeof props.handleOptionValueChange + }; + + const handleChangeFormat = (format: DateTimeFormat) => { + if (format === DateTimeFormat.TIMESTAMP) { + handleOptionValueChange("format", format, ValueType.INT); + } else if (format === DateTimeFormat.TEXT) { + handleOptionValueChange("format", format, ValueType.STRING); + } else if (format === DateTimeFormat.DATETIME) { + handleOptionValueChange("format", format, ValueType.DATE_TIME); + } + } + + return ( + <> + } + selectOptions={BirthdayGeneratorModeSelectOptions} + value={options.mode} + onChange={(v) => handleOptionValueChange("mode", v)} + /> + + {options.mode === BirthdayGeneratorMode.AGE && <> + } + value={options.minAge} + onChange={(v) => handleOptionValueChange("minAge", v)} + style={{width: "70px"}} + min={0} + max={options.maxAge} + /> + + } + value={options.maxAge} + onChange={(v) => handleOptionValueChange("maxAge", v)} + style={{width: "70px"}} + min={options.minAge} + max={150} + /> + } + + + {options.mode === BirthdayGeneratorMode.YEAR && <> + } + value={options.fromYear} + onChange={(v) => handleOptionValueChange("fromYear", v)} + style={{width: "90px"}} + min={1000} + max={options.toYear} + /> + + } + value={options.toYear} + onChange={(v) => handleOptionValueChange("toYear", v)} + style={{width: "100px"}} + min={options.toYear} + max={2500} + /> + } + + } + selectOptions={DateTimeFormatSelectOptions} + value={options.format} + onChange={handleChangeFormat} + style={{width: "200px"}} + /> + + + ); +} + +const BirthdayGeneratorModeSelectOptions: SelectOption[] = [ + { + label: , + value: BirthdayGeneratorMode.AGE + }, { + label: , + value: BirthdayGeneratorMode.YEAR + } +] \ No newline at end of file diff --git a/src/core/generators/Birthday/index.ts b/src/core/generators/Birthday/index.ts new file mode 100644 index 0000000..f915ab9 --- /dev/null +++ b/src/core/generators/Birthday/index.ts @@ -0,0 +1,14 @@ +import {Generator} from "@/types/generator"; +import {DataType, DataTypeCategory, ValueType} from "@/constants/enums"; +import {BirthdayGeneratorDefaultOptions, BirthdayGeneratorOptionsComponent, generate} from "./Birthday"; + +export const BirthdayGenerator: Generator = { + type: DataType.BIRTHDAY, + category: DataTypeCategory.PERSON, + generate: generate, + optionsComponent: BirthdayGeneratorOptionsComponent, + defaultOptions: BirthdayGeneratorDefaultOptions, + defaultValueType: ValueType.DATE_TIME, + exampleLines: ["1949-08-11T20:00:14.636Z", "-640186506999", "18-02-1968 20:14:50"] +} + \ No newline at end of file diff --git a/src/core/generators/DateTime/DateTime.tsx b/src/core/generators/DateTime/DateTime.tsx new file mode 100644 index 0000000..ed66fe4 --- /dev/null +++ b/src/core/generators/DateTime/DateTime.tsx @@ -0,0 +1,189 @@ +import React from "react"; +import {GenerateResult, GeneratorOptionsComponentInterface} from "@/types/generator"; +import {OptionsSelect, SelectOption} from "@/components/Utils"; +import {FormattedMessage} from "@/locale"; +import {OptionsDatetimePicker} from "@/components/Utils/src/OptionsDatetimePicker"; +import {faker} from "@faker-js/faker"; +import {ValueType} from "@/constants/enums"; +import style from "@/core/generators/Boolean/Boolean.module.scss"; +import {Tag} from "@douyinfe/semi-ui"; +import {toDateTimeString} from "@/utils/typeUtils"; + +// ------------------------------------------------------------------------------------------------------------- +// types +export enum DateTimeTerms { + ANYTIME = "anytime", + BETWEEN = "between", + FUTURE = "future", + PAST = "past", + RECENT = "recent", + SOON = "soon" +} + +export enum DateTimeFormat { + TEXT = "text", + TIMESTAMP = "timestamp", + DATETIME = "datetime" +} + +export interface DateTimeGeneratorOptions { + terms: DateTimeTerms; + ref: Date | number | string; + format: DateTimeFormat; + timeRange: Date[] | number[] | string[]; +} + +// ------------------------------------------------------------------------------------------------------------- +// default options +export const DateTimeGeneratorDefaultOptions: DateTimeGeneratorOptions = { + terms: DateTimeTerms.ANYTIME, + ref: Date.now(), + format: DateTimeFormat.DATETIME, + timeRange: [new Date(2023, 0, 1), new Date()] +} + +// ------------------------------------------------------------------------------------------------------------- +// generate method +export const generate = (options: DateTimeGeneratorOptions): GenerateResult => { + const {terms, ref, format} = options; + + let value: Date; + const fakerOptions = {refDate: ref} + + switch (terms) { + case DateTimeTerms.FUTURE: + value = faker.date.future(fakerOptions); + break; + case DateTimeTerms.PAST: + value = faker.date.past(fakerOptions); + break; + case DateTimeTerms.RECENT: + value = faker.date.recent(fakerOptions); + break; + case DateTimeTerms.SOON: + value = faker.date.soon(fakerOptions); + break; + case DateTimeTerms.BETWEEN: + value = faker.date.between({from: options.timeRange[0], to: options.timeRange[1]}); + break; + default: + value = faker.date.anytime(fakerOptions); + } + + const output = formatDateTime(value, format); + + return { + value: output, + stringValue: (output instanceof Date) ? output.toISOString() : output.toString() + }; +} + +// ------------------------------------------------------------------------------------------------------------- +// options component +export const DateTimeGeneratorOptionsComponent: React.FunctionComponent = ({...props}) => { + + const {options, handleOptionValueChange} = props as { + options: DateTimeGeneratorOptions, + handleOptionValueChange: typeof props.handleOptionValueChange + }; + + const handleChangeFormat = (format: DateTimeFormat) => { + if (format === DateTimeFormat.TIMESTAMP) { + handleOptionValueChange("format", format, ValueType.INT); + } else if (format === DateTimeFormat.TEXT) { + handleOptionValueChange("format", format, ValueType.STRING); + } else if (format === DateTimeFormat.DATETIME) { + handleOptionValueChange("format", format, ValueType.DATE_TIME); + } + } + + return ( + <> + } + selectOptions={DateTimeTermsSelectOptions} + value={options.terms} + onChange={(v) => handleOptionValueChange("terms", v)} + style={{width: "130px"}} + /> + + { + options.terms === DateTimeTerms.BETWEEN ? } + type={'dateTimeRange'} + value={options.timeRange} + onChange={(v) => handleOptionValueChange("timeRange", v)} + style={{width: "260px"}} + required + /> + : + } + infoTooltip={} + type={'dateTime'} + value={options.ref} + onChange={(v) => handleOptionValueChange("ref", v)} + style={{width: "185px"}} + required + /> + } + + } + selectOptions={DateTimeFormatSelectOptions} + value={options.format} + onChange={handleChangeFormat} + style={{width: "200px"}} + /> + + ); +} + +export const formatDateTime = (date: Date, format: DateTimeFormat) => { + switch (format) { + case DateTimeFormat.DATETIME: + return date; + case DateTimeFormat.TIMESTAMP: + return date.getTime(); + case DateTimeFormat.TEXT: + return toDateTimeString(date); + } +} + +export const DateTimeTermsSelectOptions: SelectOption[] = Object.values(DateTimeTerms).map((term) => { + return { + value: term, + label: + }; +}) + +export const DateTimeFormatSelectOptions: SelectOption[] = [ + { + value: DateTimeFormat.DATETIME, + label: + <> + Date + + + }, + { + value: DateTimeFormat.TIMESTAMP, + label: + <> + number + + + }, + { + value: DateTimeFormat.TEXT, + label: + <> + string + + + }, + +] \ No newline at end of file diff --git a/src/core/generators/DateTime/index.ts b/src/core/generators/DateTime/index.ts new file mode 100644 index 0000000..1251e07 --- /dev/null +++ b/src/core/generators/DateTime/index.ts @@ -0,0 +1,14 @@ +import {Generator} from "@/types/generator"; +import {DataType, DataTypeCategory, ValueType} from "@/constants/enums"; +import {DateTimeGeneratorDefaultOptions, DateTimeGeneratorOptionsComponent, generate} from "./DateTime"; + +export const DateTimeGenerator: Generator = { + type: DataType.DATETIME, + category: DataTypeCategory.DATETIME, + generate: generate, + optionsComponent: DateTimeGeneratorOptionsComponent, + defaultOptions: DateTimeGeneratorDefaultOptions, + defaultValueType: ValueType.DATE_TIME, + exampleLines: ["1725842513046", "29-01-2025 20:15:40", "13-06-2023 08:16:09"] +} + \ No newline at end of file diff --git a/src/core/generators/Month/Month.tsx b/src/core/generators/Month/Month.tsx new file mode 100644 index 0000000..314b1b1 --- /dev/null +++ b/src/core/generators/Month/Month.tsx @@ -0,0 +1,49 @@ +import React from "react"; +import {GenerateResult, GeneratorOptionsComponentInterface} from "@/types/generator"; +import {OptionsSwitch} from "@/components/Utils/src/OptionsSwitch"; +import {FormattedMessage} from "@/locale"; +import {faker} from "@faker-js/faker"; + +// ------------------------------------------------------------------------------------------------------------- +// types +export interface MonthGeneratorOptions { + abbreviated: boolean; +} + +// ------------------------------------------------------------------------------------------------------------- +// default options +export const MonthGeneratorDefaultOptions: MonthGeneratorOptions = { + abbreviated: false +} + +// ------------------------------------------------------------------------------------------------------------- +// generate method +export const generate = (options: MonthGeneratorOptions): GenerateResult => { + const {abbreviated} = options; + const value = faker.date.month({abbreviated:abbreviated}) + return { + value: value, + stringValue: value + }; +} + +// ------------------------------------------------------------------------------------------------------------- +// options component +export const MonthGeneratorOptionsComponent: React.FunctionComponent = ({...props}) => { + const {options, handleOptionValueChange} = props as { + options: MonthGeneratorOptions, + handleOptionValueChange: typeof props.handleOptionValueChange + }; + + // TODO: implement your own options component here + return ( + <> + } + value={options.abbreviated} + onChange={(v) => handleOptionValueChange("abbreviated", v)} + /> + + + ); +} \ No newline at end of file diff --git a/src/core/generators/Month/index.ts b/src/core/generators/Month/index.ts new file mode 100644 index 0000000..825940f --- /dev/null +++ b/src/core/generators/Month/index.ts @@ -0,0 +1,14 @@ +import {Generator} from "@/types/generator"; +import {DataType, DataTypeCategory, ValueType} from "@/constants/enums"; +import {MonthGeneratorDefaultOptions, MonthGeneratorOptionsComponent, generate} from "./Month"; + +export const MonthGenerator: Generator = { + type: DataType.MONTH, + category: DataTypeCategory.DATETIME, + generate: generate, + optionsComponent: MonthGeneratorOptionsComponent, + defaultOptions: MonthGeneratorDefaultOptions, + defaultValueType: ValueType.STRING, + exampleLines: ["September", "Feb", "December"] +} + \ No newline at end of file diff --git a/src/core/generators/Number/Number.tsx b/src/core/generators/Number/Number.tsx index 29a6119..f975519 100644 --- a/src/core/generators/Number/Number.tsx +++ b/src/core/generators/Number/Number.tsx @@ -95,18 +95,14 @@ export const NumberGeneratorOptionsComponent: React.FunctionComponent { const newErrorMessages = {...errorMessages}; // min - if (isNullOrWhiteSpace(options.min.toString())) { - newErrorMessages.min = intl.formatMessage({id: 'dataType.number.min.errorMessage.empty'}) - } else if (options.min > options.max) { + if (options.min > options.max) { newErrorMessages.min = intl.formatMessage({id: 'dataType.number.min.errorMessage.greaterThanMax'}) } else { newErrorMessages.min = ''; } // max - if (isNullOrWhiteSpace(options.max.toString())) { - newErrorMessages.max = intl.formatMessage({id: 'dataType.number.max.errorMessage.empty'}) - } else if (options.max < options.min) { + if (options.max < options.min) { newErrorMessages.max = intl.formatMessage({id: 'dataType.number.max.errorMessage.lessThanMin'}) } else { newErrorMessages.max = ''; diff --git a/src/core/generators/Weekday/Weekday.tsx b/src/core/generators/Weekday/Weekday.tsx new file mode 100644 index 0000000..a9ed699 --- /dev/null +++ b/src/core/generators/Weekday/Weekday.tsx @@ -0,0 +1,47 @@ +import React from "react"; +import {GenerateResult, GeneratorOptionsComponentInterface} from "@/types/generator"; +import {OptionsSwitch} from "@/components/Utils/src/OptionsSwitch"; +import {FormattedMessage} from "@/locale"; +import {faker} from "@faker-js/faker"; + +// ------------------------------------------------------------------------------------------------------------- +// types +export interface WeekdayGeneratorOptions { + abbreviated: boolean; +} + +// ------------------------------------------------------------------------------------------------------------- +// default options +export const WeekdayGeneratorDefaultOptions: WeekdayGeneratorOptions = { + abbreviated: false +} + +// ------------------------------------------------------------------------------------------------------------- +// generate method +export const generate = (options: WeekdayGeneratorOptions): GenerateResult => { + const {abbreviated} = options; + const value = faker.date.weekday({abbreviated: abbreviated}) + return { + value: value, + stringValue: value + }; +} + +// ------------------------------------------------------------------------------------------------------------- +// options component +export const WeekdayGeneratorOptionsComponent: React.FunctionComponent = ({...props}) => { + const {options, handleOptionValueChange} = props as { + options: WeekdayGeneratorOptions, + handleOptionValueChange: typeof props.handleOptionValueChange + }; + + return ( + <> + } + value={options.abbreviated} + onChange={(v) => handleOptionValueChange("abbreviated", v)} + /> + + ); +} \ No newline at end of file diff --git a/src/core/generators/Weekday/index.ts b/src/core/generators/Weekday/index.ts new file mode 100644 index 0000000..8fe15f1 --- /dev/null +++ b/src/core/generators/Weekday/index.ts @@ -0,0 +1,14 @@ +import {Generator} from "@/types/generator"; +import {DataType, DataTypeCategory, ValueType} from "@/constants/enums"; +import {WeekdayGeneratorDefaultOptions, WeekdayGeneratorOptionsComponent, generate} from "./Weekday"; + +export const WeekdayGenerator: Generator = { + type: DataType.WEEKDAY, + category: DataTypeCategory.DATETIME, + generate: generate, + optionsComponent: WeekdayGeneratorOptionsComponent, + defaultOptions: WeekdayGeneratorDefaultOptions, + defaultValueType: ValueType.STRING, + exampleLines: ["Wednesday", "Wed", "Thursday"] +} + \ No newline at end of file diff --git a/src/core/generators/index.ts b/src/core/generators/index.ts index 8d6ffd1..7d98b22 100644 --- a/src/core/generators/index.ts +++ b/src/core/generators/index.ts @@ -1,3 +1,7 @@ +import {BirthdayGenerator} from "@/core/generators/Birthday"; +import {MonthGenerator} from "@/core/generators/Month"; +import {WeekdayGenerator} from "@/core/generators/Weekday"; +import {DateTimeGenerator} from "@/core/generators/DateTime"; import {UrlGenerator} from "@/core/generators/Url"; import {DomainSuffixGenerator} from "@/core/generators/DomainSuffix"; import {DomainNameGenerator} from "@/core/generators/DomainName"; @@ -19,6 +23,10 @@ import {CompanyNameGenerator} from "@/core/generators/CompanyName"; import {DataType} from "@/constants/enums"; export const generators = { + [DataType.BIRTHDAY]: BirthdayGenerator, + [DataType.MONTH]: MonthGenerator, + [DataType.WEEKDAY]: WeekdayGenerator, + [DataType.DATETIME]: DateTimeGenerator, [DataType.URL]: UrlGenerator, [DataType.DOMAINSUFFIX]: DomainSuffixGenerator, [DataType.DOMAINNAME]: DomainNameGenerator, diff --git a/src/locale/translations/en.ts b/src/locale/translations/en.ts index bcbef4e..a298c96 100644 --- a/src/locale/translations/en.ts +++ b/src/locale/translations/en.ts @@ -86,6 +86,45 @@ export const en = { // data types + + // birthday + "dataType.birthday": "Birthday", + "dataType.birthday.mode":"Mode", + "dataType.birthday.mode.age":"Age", + "dataType.birthday.mode.year":"Year", + "dataType.birthday.mode.minAge":"Min. Age", + "dataType.birthday.mode.maxAge":"Max. Age", + "dataType.birthday.mode.fromYear":"From year", + "dataType.birthday.mode.toYear":"To year", + + // month + "dataType.month": "Month", + "dataType.month.abbreviated": "Abbreviated", + + // weekday + "dataType.weekday": "Weekday", + "dataType.weekday.abbreviated": "Abbreviated", + + // datetime + "dataType.datetime": "DateTime", + "dataType.datetime.terms": "Terms", + "dataType.datetime.terms.anytime": "Anytime", + "dataType.datetime.terms.between": "Between", + "dataType.datetime.terms.future": "Future", + "dataType.datetime.terms.past": "Past", + "dataType.datetime.terms.recent": "Recent past", + "dataType.datetime.terms.soon": "Soon", + "dataType.datetime.ref": "Reference date", + "dataType.datetime.ref.tooltip": "The date to use as reference point", + "dataType.datetime.format": "Format", + "dataType.datetime.format.text": "Text", + "dataType.datetime.format.timestamp": "Timestamp", + "dataType.datetime.format.datetime": "DateTime", + "dataType.datetime.timeRange": "Range", + + // anytime + "dataType.anytime": "Anytime", + // url "dataType.url": "Url", "dataType.url.appendSlash.label": "Slash", @@ -159,9 +198,6 @@ export const en = { "dataType.number.max.errorMessage.empty": "Max. value cannot be empty", "dataType.number.max.errorMessage.lessThanMin": "Max. value cannot be less than min. value", - // dateTime - "dataType.dateTime": "Date time", - // boolean "dataType.boolean": "Boolean", "dataType.boolean.true.label": "Prob of True", @@ -195,6 +231,7 @@ export const en = { "dataType.category.person": "Person", "dataType.category.commerce": "Commerce", "dataType.category.network": "Network", + "dataType.category.datetime": "Datetime", // ------------------------------------------------------------------------------------------------------------- // pages diff --git a/src/locale/translations/jaJP.ts b/src/locale/translations/jaJP.ts index 5a73c9f..4800825 100644 --- a/src/locale/translations/jaJP.ts +++ b/src/locale/translations/jaJP.ts @@ -86,6 +86,30 @@ export const jaJP = { // data types + + + + + + + // birthday + "dataType.birthday": "Birthday", + + // month + "dataType.month": "Month", + + // weekday + "dataType.weekday": "Weekday", + + // datetimerange + "dataType.datetimerange": "DateTimeRange", + + // datetime + "dataType.datetime": "DateTime", + + // anytime + "dataType.anytime": "Anytime", + // url "dataType.url": "URL", "dataType.url.appendSlash.label": "スラッシュ", diff --git a/src/locale/translations/zhCN.ts b/src/locale/translations/zhCN.ts index 2cdb78d..7438930 100644 --- a/src/locale/translations/zhCN.ts +++ b/src/locale/translations/zhCN.ts @@ -83,6 +83,41 @@ export const zhCN = { // ------------------------------------------------------------------------------------------------------------- // data types + + // birthday + "dataType.birthday": "生日", + "dataType.birthday.mode":"模式", + "dataType.birthday.mode.age":"年龄", + "dataType.birthday.mode.year":"出生年份", + "dataType.birthday.mode.minAge":"最小年龄", + "dataType.birthday.mode.maxAge":"最大年龄", + "dataType.birthday.mode.fromYear":"最小年份", + "dataType.birthday.mode.toYear":"最大年份", + + // month + "dataType.month": "月份", + "dataType.month.abbreviated": "缩写", + + // weekday + "dataType.weekday": "星期", + "dataType.weekday.abbreviated": "缩写", + + // datetime + "dataType.datetime": "日期时间", + "dataType.datetime.terms": "条件", + "dataType.datetime.terms.anytime": "任意时间", + "dataType.datetime.terms.between": "区间", + "dataType.datetime.terms.future": "未来", + "dataType.datetime.terms.past": "过去", + "dataType.datetime.terms.recent": "不久之前", + "dataType.datetime.terms.soon": "不久之后", + "dataType.datetime.ref": "参考日期", + "dataType.datetime.ref.tooltip": "用于生成数据的参考日期", + "dataType.datetime.format": "格式", + "dataType.datetime.format.text": "文本", + "dataType.datetime.format.timestamp": "时间戳", + "dataType.datetime.format.datetime": "Date对象", + "dataType.datetime.timeRange": "区间", // url "dataType.url": "网址", @@ -157,9 +192,6 @@ export const zhCN = { "dataType.number.max.errorMessage.empty": "最大值不能为空", "dataType.number.max.errorMessage.lessThanMin": "最大值不能小于最小值", - // dateTime - "dataType.dateTime": "日期时间", - // boolean "dataType.boolean": "布尔值", "dataType.boolean.true.label": "真值概率", @@ -193,6 +225,7 @@ export const zhCN = { "dataType.category.person": "人物", "dataType.category.commerce": "商业", "dataType.category.network": "网络", + "dataType.category.datetime": "时间", // ------------------------------------------------------------------------------------------------------------- // pages diff --git a/src/utils/formatterUtils.ts b/src/utils/formatterUtils.ts index ad475d2..4e1f73b 100644 --- a/src/utils/formatterUtils.ts +++ b/src/utils/formatterUtils.ts @@ -1,5 +1,5 @@ import {FormatRequest, Formatter} from "@/types/formatter"; -import {ExportFormat} from "@/constants/enums"; +import {ExportFormat, ValueType} from "@/constants/enums"; import {formatters} from "@/core/formatters"; import {langs} from '@uiw/codemirror-extensions-langs'; import {DataFieldList} from "@/types/generator"; @@ -103,6 +103,8 @@ export function toJsonListStringWithoutQuotes(fields: DataFieldList, sortedField if (Array.isArray(value)) { // 处理数组格式 const elements = value.map(element => convert(element, indent + 2)); return `[\n${nextIndentSpace}${elements.join(`,\n${nextIndentSpace}`)}\n${indentSpace}]`; + } else if (value instanceof Date) { + return `new Date("${toISOString(value)}")` } else if (typeof value === 'object' && value !== null) { // 处理对象格式 const entries = Object.entries(value).map(([key, val]) => { const formattedValue = convert(val, indent + 2); @@ -137,3 +139,19 @@ export function toJsonListStringWithoutQuotes(fields: DataFieldList, sortedField output += "]"; // 结束数组 return output; } + + +function toISOString(date) { + // 获取日期部分 + const year = date.getFullYear(); + const month = String(date.getMonth() + 1).padStart(2, '0'); // 月份是从0开始的 + const day = String(date.getDate()).padStart(2, '0'); + + // 获取时间部分 + const hours = String(date.getHours()).padStart(2, '0'); + const minutes = String(date.getMinutes()).padStart(2, '0'); + const seconds = String(date.getSeconds()).padStart(2, '0'); + + // 拼接成 YYYY-MM-DDTHH:mm:ss 格式 + return `${year}-${month}-${day}T${hours}:${minutes}:${seconds}`; +} \ No newline at end of file diff --git a/src/utils/typeUtils.ts b/src/utils/typeUtils.ts index b6f247c..1c50988 100644 --- a/src/utils/typeUtils.ts +++ b/src/utils/typeUtils.ts @@ -13,4 +13,19 @@ export const calculateByteSize = (str: string) => { // check variable has value export function hasValue(variable: any): boolean { return variable !== null && variable !== undefined; +} + +export function toDateString(date: Date): string { + const day = date.getDate().toString().padStart(2, '0'); + const month = (date.getMonth() + 1).toString().padStart(2, '0'); + const year = date.getFullYear(); + return `${day}-${month}-${year}`; +} + +export function toDateTimeString(date: Date): string { + const formattedDate = toDateString(date); // Reuse formatDate function + const hours = date.getHours().toString().padStart(2, '0'); + const minutes = date.getMinutes().toString().padStart(2, '0'); + const seconds = date.getSeconds().toString().padStart(2, '0'); + return `${formattedDate} ${hours}:${minutes}:${seconds}`; } \ No newline at end of file