import Icon from '@ant-design/icons' import '@/assets/css/pages/tools/create.scss' import { DATABASE_DUPLICATE_KEY, DATABASE_INSERT_SUCCESS, DATABASE_SELECT_SUCCESS } from '@/constants/common.constants' import { navigateToEdit } from '@/util/navigation' import { r_tool_category_get, r_tool_create, r_tool_template_get, r_tool_template_get_one } from '@/services/tool' import compiler from '@/components/Playground/compiler' import { IImportMap } from '@/components/Playground/shared' import { base64ToFiles, base64ToStr, IMPORT_MAP_FILE_NAME } from '@/components/Playground/files' import FlexBox from '@/components/common/FlexBox' import Card from '@/components/common/Card' import FitFullscreen from '@/components/common/FitFullscreen' import HideScrollbar from '@/components/common/HideScrollbar' import Playground from '@/components/Playground' const Create = () => { const navigate = useNavigate() const [form] = AntdForm.useForm() const formValues = AntdForm.useWatch([], form) const [templateData, setTemplateData] = useState() const [categoryData, setCategoryData] = useState() const [templateDetailData, setTemplateDetailData] = useState>({}) const [previewTemplate, setPreviewTemplate] = useState('') const [isLoadingTemplate, setIsLoadingTemplate] = useState(false) const [isLoadingCategory, setIsLoadingCategory] = useState(false) const [isCreating, setIsCreating] = useState(false) const [compiledCode, setCompiledCode] = useState('') const handleOnFinish = (toolAddParam: ToolCreateParam) => { setIsCreating(true) void r_tool_create(toolAddParam) .then((res) => { const response = res.data switch (response.code) { case DATABASE_INSERT_SUCCESS: void message.success( `创建工具 ${response.data!.name}<${response.data!.toolId}:${response.data!.platform.slice(0, 1)}${response.data!.platform.slice(1).toLowerCase()}:${response.data!.ver}> 成功` ) navigateToEdit(navigate, response.data!.toolId, response.data!.platform) break case DATABASE_DUPLICATE_KEY: void message.warning('已存在相同 ID 的应用') setIsCreating(false) break default: void message.error('创建失败,请稍后重试') setIsCreating(false) } }) .catch(() => { setIsCreating(false) }) } const handleOnIconBeforeUpload = ( file: Parameters<_GetProp<_UploadProps, 'beforeUpload'>>[0] ) => { if (file.type !== 'image/svg+xml') { void message.error('仅支持 svg 文件') return false } if (file.size / 1024 / 1024 > 2) { void message.error('文件大小不能大于2MiB') } const reader = new FileReader() reader.addEventListener('load', () => { // eslint-disable-next-line @typescript-eslint/no-base-to-string form.setFieldValue('icon', reader.result!.toString().split(',')[1]) void form.validateFields(['icon']) }) reader.readAsDataURL(file) return false } const handleOnPlatformChange = (value: string) => { setIsLoadingTemplate(true) void r_tool_template_get({ platform: value }) .then((res) => { const response = res.data switch (response.code) { case DATABASE_SELECT_SUCCESS: setTemplateData(response.data!) response.data?.length ? form.setFieldValue('templateId', response.data?.[0].id) : form.setFieldValue('templateId', null) response.data?.length && handleOnTemplateChange(response.data?.[0].id) break default: void message.error('获取模板列表失败,请稍后重试') } }) .finally(() => { setIsLoadingTemplate(false) }) } const handleOnTemplateChange = (value: string) => { setPreviewTemplate(value) if (templateDetailData[value]) { return } setIsLoadingTemplate(true) void r_tool_template_get_one(value) .then((res) => { const response = res.data switch (response.code) { case DATABASE_SELECT_SUCCESS: setTemplateDetailData({ ...templateDetailData, [value]: response.data! }) break default: void message.error('获取模板信息失败') } }) .finally(() => { setIsLoadingTemplate(false) }) } useEffect(() => { const template = templateDetailData[previewTemplate] if (!template) { return } setCompiledCode('') try { const baseDist = base64ToStr(template.base.dist.data!) const files = base64ToFiles(template.source.data!) const importMap = JSON.parse(files[IMPORT_MAP_FILE_NAME].value) as IImportMap void compiler .compile(files, importMap, template.entryPoint) .then((result) => { const output = result.outputFiles[0].text setCompiledCode(`(() => {${output}})();\n(() => {${baseDist}})();`) }) .catch((reason) => { void message.error(`编译失败:${reason}`) setCompiledCode(baseDist) }) } catch (e) { void message.error(`载入模板 ${templateDetailData[previewTemplate].name} 失败`) } }, [templateDetailData, previewTemplate]) useEffect(() => { const temp: string[] = [] formValues?.keywords.forEach((item) => { if (item.length <= 10) { temp.push(item) } }) form.setFieldValue('keyword', temp) }, [form, formValues?.keywords]) useEffect(() => { setIsLoadingCategory(true) void r_tool_category_get() .then((res) => { const response = res.data switch (response.code) { case DATABASE_SELECT_SUCCESS: setCategoryData(response.data!) break default: void message.error('获取类别列表失败,请稍后重试') } }) .finally(() => { setIsLoadingCategory(false) }) }, []) return ( 配置
({ validator() { if (!getFieldValue('icon')) { return Promise.reject( new Error('请选择图标') ) } return Promise.resolve() } }) ]} getValueFromEvent={() => {}} > {formValues?.icon ? ( {'icon'} ) : ( )} Web Desktop Android .<数字>.<数字>', eg. 1.0.3` } ]} > ({ value: value.id, label: value.name }))} loading={isLoadingTemplate} disabled={isLoadingTemplate} onChange={handleOnTemplateChange} placeholder={'请选择模板'} /> ({ value: value.id, label: value.name }))} loading={isLoadingCategory} disabled={isLoadingCategory} placeholder={'请选择类别'} /> 创建
预览 {compiledCode ? ( ) : ( 暂无预览 )}
) } export default Create