diff --git a/src/components/Playground/Output/Preview/iframe.html b/src/components/Playground/Output/Preview/iframe.html index 95c6c95..b485bf9 100644 --- a/src/components/Playground/Output/Preview/iframe.html +++ b/src/components/Playground/Output/Preview/iframe.html @@ -48,6 +48,11 @@ }); -
+
+
+ Loading... +
+
diff --git a/src/constants/urls.constants.ts b/src/constants/urls.constants.ts index 84eb783..266a0d3 100644 --- a/src/constants/urls.constants.ts +++ b/src/constants/urls.constants.ts @@ -30,5 +30,9 @@ export const URL_SYS_TOOL_CATEGORY = `${URL_SYS_TOOL}/category` export const URL_SYS_TOOL_BASE = `${URL_SYS_TOOL}/base` export const URL_SYS_TOOL_TEMPLATE = `${URL_SYS_TOOL}/template` +export const URL_TOOL = '/tool' +export const URL_TOOL_TEMPLATE = `${URL_TOOL}/template` +export const URL_TOOL_CATEGORY = `${URL_TOOL}/category` + export const URL_API_V1 = '/api/v1' export const URL_API_V1_AVATAR_RANDOM_BASE64 = `${URL_API_V1}/avatar/base64` diff --git a/src/global.d.ts b/src/global.d.ts index f3e3bef..9740e02 100644 --- a/src/global.d.ts +++ b/src/global.d.ts @@ -509,6 +509,7 @@ interface ToolTemplateVo { name: string baseId: string source: ToolDataVo + entryPoint: string enable: boolean createTime: string updateTime: string @@ -520,6 +521,7 @@ interface ToolTemplateAddEditParam { name?: string baseId?: string source?: string + entryPoint?: string enable?: boolean } @@ -541,3 +543,14 @@ interface ToolVo { createTime: string updateTime: string } + +interface ToolCreateParam { + name: string + toolId: string + description: string + ver: string + templateId: string + privately: boolean + keywords: string[] + categories: string[] +} diff --git a/src/pages/System/Tools/Template.tsx b/src/pages/System/Tools/Template.tsx index f5ae678..28f4c78 100644 --- a/src/pages/System/Tools/Template.tsx +++ b/src/pages/System/Tools/Template.tsx @@ -79,6 +79,7 @@ const Template = () => { form.setFieldValue('id', undefined) form.setFieldValue('name', newFormValues?.name) form.setFieldValue('baseId', newFormValues?.baseId) + form.setFieldValue('entryPoint', newFormValues?.entryPoint) form.setFieldValue('enable', newFormValues?.enable ?? true) if (!baseData || !baseData.length) { getBaseData() @@ -98,6 +99,10 @@ const Template = () => { title: '基板', dataIndex: ['base', 'name'] }, + { + title: '入口', + dataIndex: 'entryPoint' + }, { title: '创建时间', dataIndex: 'createTime', @@ -211,6 +216,7 @@ const Template = () => { form.setFieldValue('id', value.id) form.setFieldValue('name', value.name) form.setFieldValue('baseId', value.base.id) + form.setFieldValue('entryPoint', value.entryPoint) form.setFieldValue('enable', value.enable) if (!baseData || !baseData.length) { getBaseData() @@ -787,6 +793,7 @@ const Template = () => { setNewFormValues({ name: formValues.name, baseId: formValues.baseId, + entryPoint: formValues.entryPoint, enable: formValues.enable }) } @@ -839,6 +846,9 @@ const Template = () => { }))} /> + + + @@ -857,6 +867,7 @@ const Template = () => { rowKey={(record) => record.id} loading={isLoading} pagination={false} + scroll={{ x: true }} expandable={{ expandedRowRender, onExpand: handleOnExpand diff --git a/src/pages/Tools/Create.tsx b/src/pages/Tools/Create.tsx index 46cc42b..eccc362 100644 --- a/src/pages/Tools/Create.tsx +++ b/src/pages/Tools/Create.tsx @@ -1,39 +1,151 @@ import '@/assets/css/pages/tools/create.scss' -import FlexBox from '@/components/common/FlexBox.tsx' -import Card from '@/components/common/Card.tsx' -import FitFullscreen from '@/components/common/FitFullscreen.tsx' -import Preview from '@/components/Playground/Output/Preview' -import templates from '@/components/Playground/templates.ts' -import { useEffect } from 'react' -import HideScrollbar from '@/components/common/HideScrollbar.tsx' +import { + DATABASE_DUPLICATE_KEY, + DATABASE_INSERT_SUCCESS, + DATABASE_SELECT_SUCCESS +} from '@/constants/common.constants' +import { + r_tool_category_get, + r_tool_create, + r_tool_template_get, + r_tool_template_get_one +} from '@/services/tool' +import { IImportMap } from '@/components/Playground/shared' +import { base64ToFiles, base64ToStr, IMPORT_MAP_FILE_NAME } from '@/components/Playground/files' +import compiler from '@/components/Playground/compiler' +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 [form] = AntdForm.useForm<{ - name: string - toolId: string - desc: string - version: string - template: string - private: boolean - keyword: string[] - category: string[] - }>() + const [form] = AntdForm.useForm() const formValues = AntdForm.useWatch([], form) - const [template, setTemplate] = useState(templates['demo']) + const [templateData, setTemplateData] = useState() + const [categoryData, setCategoryData] = useState() + const [templateDetailData, setTemplateDetailData] = useState>({}) + const [previewTemplate, setPreviewTemplate] = useState('') + const [loadingTemplate, setLoadingTemplate] = useState(false) + const [loadingCategory, setLoadingCategory] = useState(false) + const [creating, setCreating] = useState(false) + const [compiledCode, setCompiledCode] = useState('') + + const handleOnFinish = (toolAddParam: ToolCreateParam) => { + setCreating(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!.ver} 成功` + ) + break + case DATABASE_DUPLICATE_KEY: + void message.warning('已存在相同 ID 相同版本的应用') + setCreating(false) + break + default: + void message.error('创建失败,请稍后重试') + setCreating(false) + } + }) + .catch(() => { + setCreating(false) + }) + } + + const handleOnTemplateChange = (value: string) => { + setPreviewTemplate(value) + if (templateDetailData[value]) { + return + } + + setLoadingTemplate(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(() => { + setLoadingTemplate(false) + }) + } useEffect(() => { - formValues?.template && setTemplate(templates[formValues?.template]) - }, [formValues?.template]) + const template = templateDetailData[previewTemplate] + if (!template) { + return + } + 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}`) + }) + } catch (e) { + void message.error(`载入模板 ${templateDetailData[previewTemplate].name} 失败`) + } + }, [templateDetailData, previewTemplate]) useEffect(() => { const temp: string[] = [] - formValues?.keyword.forEach((item) => { + formValues?.keywords.forEach((item) => { if (item.length <= 10) { temp.push(item) } }) form.setFieldValue('keyword', temp) - }, [form, formValues?.keyword]) + }, [form, formValues?.keywords]) + + useEffect(() => { + setLoadingTemplate(true) + setLoadingCategory(true) + void r_tool_template_get() + .then((res) => { + const response = res.data + switch (response.code) { + case DATABASE_SELECT_SUCCESS: + setTemplateData(response.data!) + break + default: + void message.error('获取模板列表失败,请稍后重试') + } + }) + .finally(() => { + setLoadingTemplate(false) + }) + 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(() => { + setLoadingCategory(false) + }) + }, []) return ( @@ -45,7 +157,12 @@ const Create = () => {
- + { placeholder={'请输入工具 ID'} /> - + { { - - {Object.keys(templates).map((item) => ( - - {templates[item].name} - - ))} - + ({ + value: value.id, + label: value.name + }))} + loading={loadingTemplate} + disabled={loadingTemplate} + onChange={handleOnTemplateChange} + /> { - + - + ({ + value: value.id, + label: value.name + }))} + loading={loadingCategory} + disabled={loadingCategory} + /> - + 创建 @@ -155,11 +292,12 @@ const Create = () => { 预览 - + {compiledCode && ( + + )} diff --git a/src/services/index.tsx b/src/services/index.tsx index 2b8aeae..c4652b7 100644 --- a/src/services/index.tsx +++ b/src/services/index.tsx @@ -109,11 +109,18 @@ service.interceptors.response.use( return response }, async (error: AxiosError) => { - void message.error( - <> - 服务器出错,请稍后重试 - - ) + if ( + error.code === 'ETIMEDOUT' || + (error.code === 'ECONNABORTED' && error.message.includes('timeout')) + ) { + void message.error('请求超时,请稍后重试') + } else { + void message.error( + <> + 服务器出错,请稍后重试 + + ) + } return await Promise.reject(error?.response?.data) } ) diff --git a/src/services/tool.tsx b/src/services/tool.tsx new file mode 100644 index 0000000..7621345 --- /dev/null +++ b/src/services/tool.tsx @@ -0,0 +1,11 @@ +import request from '@/services/index' +import { URL_TOOL, URL_TOOL_CATEGORY, URL_TOOL_TEMPLATE } from '@/constants/urls.constants' + +export const r_tool_template_get = () => request.get(URL_TOOL_TEMPLATE) + +export const r_tool_template_get_one = (id: string) => + request.get(`${URL_TOOL_TEMPLATE}/${id}`) + +export const r_tool_category_get = () => request.get(URL_TOOL_CATEGORY) + +export const r_tool_create = (param: ToolCreateParam) => request.post(URL_TOOL, param)