From 2e331658c5a934b4269975b2ee5d0e94c94a5ce0 Mon Sep 17 00:00:00 2001 From: FatttSnake Date: Tue, 23 Jan 2024 15:32:15 +0800 Subject: [PATCH] Add compile to ToolBase management page --- src/assets/css/pages/system/tools/base.scss | 2 +- .../Playground/Output/Preview/index.tsx | 3 +- src/components/Playground/compiler.ts | 4 +- src/global.d.ts | 7 +- src/pages/System/Settings/Mail.tsx | 6 +- src/pages/System/Tools/Base.tsx | 328 ++++++++++++++---- src/pages/System/Tools/Template.tsx | 13 +- src/pages/System/User.tsx | 4 +- 8 files changed, 276 insertions(+), 91 deletions(-) diff --git a/src/assets/css/pages/system/tools/base.scss b/src/assets/css/pages/system/tools/base.scss index 50ab0fb..358c38b 100644 --- a/src/assets/css/pages/system/tools/base.scss +++ b/src/assets/css/pages/system/tools/base.scss @@ -19,7 +19,7 @@ > *:nth-child(2) { position: sticky; top: 20px; - height: calc(100vh - 40px); + height: calc(100vh - 60px); } .close-editor-btn { diff --git a/src/components/Playground/Output/Preview/index.tsx b/src/components/Playground/Output/Preview/index.tsx index f0b4424..8620120 100644 --- a/src/components/Playground/Output/Preview/index.tsx +++ b/src/components/Playground/Output/Preview/index.tsx @@ -2,6 +2,7 @@ import '@/components/Playground/Output/Preview/preview.scss' import { IFiles, IImportMap } from '@/components/Playground/shared' import Compiler from '@/components/Playground/compiler' import iframeRaw from '@/components/Playground/Output/Preview/iframe.html?raw' +import { ENTRY_FILE_NAME } from '@/components/Playground/files.ts' interface PreviewProps { iframeKey: string @@ -60,7 +61,7 @@ const Preview = ({ iframeKey, files, importMap }: PreviewProps) => { }, []) useEffect(() => { - Compiler.compile(files, importMap) + Compiler.compile(files, importMap, [ENTRY_FILE_NAME]) .then((result) => { if (loaded) { iframeRef.current?.contentWindow?.postMessage({ diff --git a/src/components/Playground/compiler.ts b/src/components/Playground/compiler.ts index 1862e24..244c435 100644 --- a/src/components/Playground/compiler.ts +++ b/src/components/Playground/compiler.ts @@ -39,7 +39,7 @@ class Compiler { return esbuild.transform(code, { loader }) }) - compile = (files: IFiles, importMap: IImportMap) => + compile = (files: IFiles, importMap: IImportMap, entryPoints: string[]) => new Promise((resolve) => { if (this.init) { resolve() @@ -54,7 +54,7 @@ class Compiler { }).then(() => { return esbuild.build({ bundle: true, - entryPoints: [ENTRY_FILE_NAME], + entryPoints: entryPoints, format: 'esm', metafile: true, write: false, diff --git a/src/global.d.ts b/src/global.d.ts index 5972364..a950bd8 100644 --- a/src/global.d.ts +++ b/src/global.d.ts @@ -481,9 +481,9 @@ interface ToolCategoryAddEditParam { interface ToolDataVo { id: string - data: string - createTime: string - updateTime: string + data?: string + createTime?: string + updateTime?: string } interface ToolBaseVo { @@ -491,6 +491,7 @@ interface ToolBaseVo { name: string source: ToolDataVo dist: ToolDataVo + compiled: boolean enable: boolean createTime: string updateTime: string diff --git a/src/pages/System/Settings/Mail.tsx b/src/pages/System/Settings/Mail.tsx index d540030..935f91c 100644 --- a/src/pages/System/Settings/Mail.tsx +++ b/src/pages/System/Settings/Mail.tsx @@ -37,7 +37,7 @@ const Mail = () => { onOk: () => mailSendForm.validateFields().then( () => { - return new Promise((resolve) => { + return new Promise((resolve) => { void r_sys_settings_mail_send({ to: mailSendForm.getFieldValue('to') as string }).then((res) => { @@ -45,10 +45,10 @@ const Mail = () => { if (response.success) { void message.success('发送成功') - resolve(true) + resolve() } else { void message.error('发送失败,请检查配置后重试') - resolve(true) + resolve() } }) }) diff --git a/src/pages/System/Tools/Base.tsx b/src/pages/System/Tools/Base.tsx index 0990a33..64dd690 100644 --- a/src/pages/System/Tools/Base.tsx +++ b/src/pages/System/Tools/Base.tsx @@ -16,12 +16,14 @@ import { r_sys_tool_base_get, r_sys_tool_base_update } from '@/services/system' -import { IFile, IFiles, ITsconfig } from '@/components/Playground/shared' +import { IFile, IFiles, IImportMap, ITsconfig } from '@/components/Playground/shared' import { base64ToFiles, fileNameToLanguage, filesToBase64, getFilesSize, + IMPORT_MAP_FILE_NAME, + strToBase64, TS_CONFIG_FILE_NAME } from '@/components/Playground/files' import FitFullscreen from '@/components/common/FitFullscreen' @@ -30,6 +32,8 @@ import HideScrollbar from '@/components/common/HideScrollbar' import Card from '@/components/common/Card' import CodeEditor from '@/components/Playground/CodeEditor' import Permission from '@/components/common/Permission' +import { useState } from 'react' +import compiler from '@/components/Playground/compiler.ts' const Base = () => { const blocker = useBlocker( @@ -55,6 +59,8 @@ const Base = () => { const [baseDetailData, setBaseDetailData] = useState>({}) const [baseDetailLoading, setBaseDetailLoading] = useState>({}) const [tsconfig, setTsconfig] = useState() + const [compiling, setCompiling] = useState(false) + const [compileForm] = AntdForm.useForm<{ entryFileName: string }>() useBeforeUnload( useCallback( @@ -70,10 +76,6 @@ const Base = () => { ) const handleOnAddBtnClick = () => { - if (Object.keys(hasEdited).length) { - void message.warning('新增前请保存修改') - return - } setIsDrawerEdit(false) setIsDrawerOpen(true) form.setFieldValue('id', undefined) @@ -93,41 +95,62 @@ const Base = () => { { title: '创建时间', dataIndex: 'createTime', - width: '20%', + width: '15em', align: 'center', render: (value: string) => utcToLocalTime(value) }, { title: '修改时间', dataIndex: 'updateTime', - width: '20%', + width: '15em', align: 'center', render: (value: string) => utcToLocalTime(value) }, { title: '状态', - dataIndex: 'enable', - width: '5%', + width: '10em', align: 'center', - render: (value) => - value ? 启用 : 禁用 + render: (_, record) => ( + <> + {record.enable ? ( + 启用 + ) : ( + 禁用 + )} + {!record.compiled && 未编译} + + ) }, { title: ( <> - 操作 ( - - 新增 - - ) + 操作 + {!Object.keys(hasEdited).length && ( + <> + {' '} + ( + + 新增 + + ) + + )} ), dataIndex: 'enable', - width: '15em', + width: '12em', align: 'center', render: (_, record) => ( <> + {!record.compiled && !Object.keys(hasEdited).length && ( + + 编译 + + )} {hasEdited[record.id] && ( { )} - - - 编辑 - - + {!Object.keys(hasEdited).length && ( + + + 编辑 + + + )} { } ] + const handleOnCompileBtnClick = (value: ToolBaseVo) => { + return () => { + if (compiling || isLoading) { + return + } + setCompiling(true) + setIsLoading(true) + void message.loading({ content: '加载文件中', key: 'compile-loading', duration: 0 }) + + if (!baseDetailLoading[value.id]) { + getBaseDetail(value) + } + + void new Promise((resolve, reject) => { + const timer = setInterval(() => { + let loading + let data + setBaseDetailLoading((prevState) => { + loading = prevState[value.id] + return prevState + }) + setBaseDetailData((prevState) => { + data = prevState[value.id] + return prevState + }) + if (!loading && data) { + clearInterval(timer) + resolve() + } + if (loading !== undefined && !loading && !data) { + clearInterval(timer) + reject() + } + }, 100) + }) + .then(() => { + let baseDetail: ToolBaseVo + setBaseDetailData((prevState) => { + baseDetail = prevState[value.id] + return prevState + }) + message.destroy('compile-loading') + const files = base64ToFiles(baseDetail!.source.data!) + if (!Object.keys(files).includes(IMPORT_MAP_FILE_NAME)) { + void message.warning(`编译中止:未包含 ${IMPORT_MAP_FILE_NAME} 文件`) + setCompiling(false) + setIsLoading(false) + return + } + let importMap: IImportMap + try { + importMap = JSON.parse(files[IMPORT_MAP_FILE_NAME].value) as IImportMap + } catch (e) { + void message.warning(`编译中止:Import Map 文件转换失败`) + setCompiling(false) + setIsLoading(false) + return + } + + compileForm.setFieldValue('entryFileName', undefined) + void modal.confirm({ + title: '编译', + content: ( + <> + + + + ![ + IMPORT_MAP_FILE_NAME, + TS_CONFIG_FILE_NAME + ].includes(value) + ) + .map((value) => ({ value, label: value }))} + /> + + + + ), + onOk: () => + compileForm.validateFields().then( + () => { + return new Promise((resolve) => { + resolve() + void message.loading({ + content: '编译中', + key: 'compiling', + duration: 0 + }) + void compiler + .compile(files, importMap, [ + compileForm.getFieldValue('entryFileName') as string + ]) + .then((result) => { + void message.destroy('compiling') + void message.loading({ + content: '上传中', + key: 'uploading', + duration: 0 + }) + console.debug(result.outputFiles[0].text) + void r_sys_tool_base_update({ + id: value.id, + dist: strToBase64(result.outputFiles[0].text) + }) + .then((res) => { + const response = res.data + switch (response.code) { + case DATABASE_UPDATE_SUCCESS: + void message.success('编译成功') + getBase() + break + default: + void message.error('上传失败') + } + }) + .finally(() => { + void message.destroy('uploading') + setCompiling(false) + setIsLoading(false) + }) + }) + .catch((e: Error) => { + void message.error(`编译失败:${e.message}`) + void message.destroy('compiling') + setCompiling(false) + setIsLoading(false) + }) + }) + }, + () => { + return new Promise((_, reject) => { + reject('请选择入口文件') + }) + } + ), + onCancel: () => { + setCompiling(false) + setIsLoading(false) + } + }) + }) + .catch(() => { + setCompiling(false) + setIsLoading(false) + message.destroy('compile-loading') + }) + } + } + const handleOnSaveBtnClick = (value: ToolBaseVo) => { return () => { if (isLoading) { @@ -191,11 +373,6 @@ const Base = () => { const handleOnEditBtnClick = (value: ToolBaseVo) => { return () => { - if (Object.keys(hasEdited).length) { - void message.warning('编辑前请保存修改') - return - } - setIsDrawerEdit(true) setIsDrawerOpen(true) form.setFieldValue('id', value.id) @@ -340,6 +517,17 @@ const Base = () => { switch (response.code) { case DATABASE_SELECT_SUCCESS: setBaseDetailData({ ...baseDetailData, [record.id]: response.data! }) + setBaseData( + baseData.map((value) => + value.id === response.data!.id + ? { + ...response.data!, + source: { id: response.data!.source.id }, + dist: { id: response.data!.dist.id } + } + : value + ) + ) break default: void message.error(`获取基板 ${record.name} 文件内容失败,请稍后重试`) @@ -355,16 +543,11 @@ const Base = () => { let sourceFiles: IFiles | undefined = undefined let sourceFileList: IFile[] = [] if (baseDetailVo) { - sourceFiles = base64ToFiles(baseDetailVo.source.data) + sourceFiles = base64ToFiles(baseDetailVo.source.data!) sourceFileList = Object.values(sourceFiles) } const handleOnAddFile = () => { - if (Object.keys(hasEdited).length) { - void message.warning('新建文件前请先保存更改') - return - } - void modal.confirm({ title: '新建文件', content: ( @@ -401,7 +584,7 @@ const Base = () => { onOk: () => addFileForm.validateFields().then( () => { - return new Promise((resolve) => { + return new Promise((resolve) => { const newFileName = addFileForm.getFieldValue('fileName') as string setBaseDetailLoading({ ...baseDetailLoading, [record.id]: true }) @@ -428,11 +611,11 @@ const Base = () => { setTimeout(() => { getBaseDetail(record) }) - resolve(true) + resolve() break default: void message.error('添加失败,请稍后重试') - resolve(true) + resolve() } }) .finally(() => { @@ -468,11 +651,17 @@ const Base = () => { { title: ( <> - 操作 ( - - 新增 - - ) + 操作 + {!Object.keys(hasEdited).length && ( + <> + {' '} + ( + + 新增 + + ) + + )} ), dataIndex: 'enable', @@ -489,22 +678,26 @@ const Base = () => { 编辑 - - - 重命名 - - - - - 删除 - - + {!Object.keys(hasEdited).length && ( + + + 重命名 + + + )} + {!Object.keys(hasEdited).length && ( + + + 删除 + + + )} ) @@ -526,10 +719,6 @@ const Base = () => { const handleOnRenameFile = (fileName: string) => { return () => { - if (Object.keys(hasEdited).length) { - void message.warning('重命名文件前请先保存更改') - return - } renameFileForm.setFieldValue('fileName', fileName) void modal.confirm({ title: '重命名文件', @@ -570,7 +759,7 @@ const Base = () => { onOk: () => renameFileForm.validateFields().then( () => { - return new Promise((resolve) => { + return new Promise((resolve) => { const newFileName = renameFileForm.getFieldValue( 'fileName' ) as string @@ -615,8 +804,7 @@ const Base = () => { [record.id]: false }) }) - - resolve(true) + resolve() }) }, () => { @@ -631,11 +819,6 @@ const Base = () => { const handleOnDeleteFile = (fileName: string) => { return () => { - if (Object.keys(hasEdited).length) { - void message.warning('删除文件前请先保存更改') - return - } - modal .confirm({ title: '确定删除', @@ -794,6 +977,7 @@ const Base = () => { onChangeFileContent={handleOnChangeFileContent} showFileSelector={false} tsconfig={tsconfig} + readonly={isLoading || baseDetailLoading[editingBaseId]} />
diff --git a/src/pages/System/Tools/Template.tsx b/src/pages/System/Tools/Template.tsx index febaa7a..882f445 100644 --- a/src/pages/System/Tools/Template.tsx +++ b/src/pages/System/Tools/Template.tsx @@ -377,7 +377,7 @@ const Template = () => { let sourceFiles: IFiles | undefined = undefined let sourceFileList: IFile[] = [] if (templateDetailVo) { - sourceFiles = base64ToFiles(templateDetailVo.source.data) + sourceFiles = base64ToFiles(templateDetailVo.source.data!) sourceFileList = Object.values(sourceFiles) } @@ -423,7 +423,7 @@ const Template = () => { onOk: () => addFileForm.validateFields().then( () => { - return new Promise((resolve) => { + return new Promise((resolve) => { const newFileName = addFileForm.getFieldValue('fileName') as string setTemplateDetailLoading({ @@ -453,11 +453,11 @@ const Template = () => { setTimeout(() => { getTemplateDetail(record) }) - resolve(true) + resolve() break default: void message.error('添加失败,请稍后重试') - resolve(true) + resolve() } }) .finally(() => { @@ -595,7 +595,7 @@ const Template = () => { onOk: () => renameFileForm.validateFields().then( () => { - return new Promise((resolve) => { + return new Promise((resolve) => { const newFileName = renameFileForm.getFieldValue( 'fileName' ) as string @@ -640,8 +640,7 @@ const Template = () => { [record.id]: false }) }) - - resolve(true) + resolve() }) }, () => { diff --git a/src/pages/System/User.tsx b/src/pages/System/User.tsx index 076c758..281adde 100644 --- a/src/pages/System/User.tsx +++ b/src/pages/System/User.tsx @@ -392,7 +392,7 @@ const User = () => { .validateFields() .then( () => { - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { void r_sys_user_change_password({ id: changePasswordForm.getFieldValue('id') as string, password: changePasswordForm.getFieldValue( @@ -407,7 +407,7 @@ const User = () => { const response = res.data if (response.success) { void message.success('修改密码成功') - resolve(true) + resolve() } else { reject(response.msg) }