diff --git a/mockServer/package.json b/mockServer/package.json index 01ebc83c0..a798f89ce 100644 --- a/mockServer/package.json +++ b/mockServer/package.json @@ -25,7 +25,7 @@ "lint": "eslint --fix ." }, "dependencies": { - "@opentiny/tiny-engine-dsl-vue": "^1.0.6", + "@opentiny/tiny-engine-dsl-vue": "workspace:*", "@seald-io/nedb": "^4.0.2", "fs-extra": "^11.1.1", "glob": "^10.3.4", diff --git a/packages/vue-generator/src/utils/parseRequiredBlocks.js b/packages/controller/js/block.js similarity index 100% rename from packages/vue-generator/src/utils/parseRequiredBlocks.js rename to packages/controller/js/block.js diff --git a/packages/controller/js/generate-files/http.js b/packages/controller/js/generate-files/http.js new file mode 100644 index 000000000..4ff89efe6 --- /dev/null +++ b/packages/controller/js/generate-files/http.js @@ -0,0 +1,41 @@ +/** + * Copyright (c) 2023 - present TinyEngine Authors. + * Copyright (c) 2023 - present Huawei Cloud Computing Technologies Co., Ltd. + * + * Use of this source code is governed by an MIT-style license. + * + * THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, + * BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR + * A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS. + * + */ + +import { useHttp } from '@opentiny/tiny-engine-http' + +const http = useHttp() + +const HEADER_LOWCODE_ORG = 'x-lowcode-org' + +// 获取页面/区块预览的代码 +export const fetchCode = async ({ platform, app, pageInfo, tenant } = {}) => + http.post( + '/app-center/api/schema2code', + { platform, app, pageInfo }, + { + headers: { [HEADER_LOWCODE_ORG]: tenant } + } + ) + +// 获取页面依赖的关联应用数据: i18n/dataSource等 +export const fetchMetaData = async ({ platform, app, type, id, history, tenant } = {}) => + id + ? http.get('/app-center/api/preview/metadata', { + headers: { [HEADER_LOWCODE_ORG]: tenant }, + params: { platform, app, type, id, history } + }) + : {} + +// 获取页面列表 +export const fetchPageList = (appId) => http.get(`/app-center/api/pages/list/${appId}`) + +export const fetchBlockSchema = async (blockName) => http.get(`/material-center/api/block?label=${blockName}`) diff --git a/packages/controller/js/generate-files/index.js b/packages/controller/js/generate-files/index.js new file mode 100644 index 000000000..c138cf318 --- /dev/null +++ b/packages/controller/js/generate-files/index.js @@ -0,0 +1,177 @@ +/** + * Copyright (c) 2023 - present TinyEngine Authors. + * Copyright (c) 2023 - present Huawei Cloud Computing Technologies Co., Ltd. + * + * Use of this source code is governed by an MIT-style license. + * + * THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, + * BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR + * A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS. + * + */ + +import { constants } from '@opentiny/tiny-engine-utils' +import { useHttp } from '@opentiny/tiny-engine-http' +import useCanvas from '../../src/useCanvas' +import useBlock from '../../src/useBlock' +import useLayout from '../../src/useLayout' +import useEditorInfo from '../../src/useEditorInfo' +import { parseRequiredBlocks } from '../block' +import { getGlobalConfig } from '../../src/globalConfig' +import { fetchMetaData, fetchPageList, fetchBlockSchema } from './http' + +const { PREVIEW_SANDBOX } = constants + +const getBlocksSchema = async (pageSchema, blockSet = new Set()) => { + let res = [] + + const blockNames = parseRequiredBlocks(pageSchema) + const promiseList = blockNames + .filter((name) => { + if (blockSet.has(name)) { + return false + } + + blockSet.add(name) + + return true + }) + .map((name) => fetchBlockSchema(name)) + const schemaList = await Promise.allSettled(promiseList) + const extraList = [] + + schemaList.forEach((item) => { + if (item.status === 'fulfilled' && item.value?.[0]?.content) { + res.push(item.value[0].content) + extraList.push(getBlocksSchema(item.value[0].content, blockSet)) + } + }) + ;(await Promise.allSettled(extraList)).forEach((item) => { + if (item.status === 'fulfilled' && item.value) { + res.push(...item.value) + } + }) + + return res +} + +const getAllPageDetails = async (pageList) => { + const detailPromise = pageList.map(({ id }) => useLayout().getPluginApi('AppManage').getPageById(id)) + const detailList = await Promise.allSettled(detailPromise) + + return detailList + .map((item) => { + if (item.status === 'fulfilled' && item.value) { + return item.value + } + }) + .filter((item) => Boolean(item)) +} + +const getParams = () => { + const { isBlock, getCurrentPage, canvasApi } = useCanvas() + const { getCurrentBlock } = useBlock() + + const { getSchema } = canvasApi.value + const params = { + framework: getGlobalConfig()?.dslMode, + platform: getGlobalConfig()?.platformId, + pageInfo: { + schema: getSchema() + } + } + const paramsMap = new URLSearchParams(location.search) + params.app = paramsMap.get('id') + params.tenant = paramsMap.get('tenant') + + if (isBlock()) { + const block = getCurrentBlock() + params.id = block?.id + params.pageInfo.name = block?.label + params.type = 'Block' + } else { + const page = getCurrentPage() + params.id = page?.id + params.pageInfo.name = page?.name + params.type = 'Page' + } + + return params +} + +export const getPreGenerateInfo = async (instance, sandbox = PREVIEW_SANDBOX.Web) => { + const params = getParams() + const { id, pageId } = useEditorInfo().useInfo() + const currentPage = await useLayout().getPluginApi('AppManage').getPageById(pageId) + const promises = [ + useHttp().get(`/app-center/v1/api/apps/schema/${id}`), + fetchMetaData(params), + fetchPageList(params.app) + ] + + const [appData, metaData, pageList] = await Promise.all(promises) + const pageDetailList = await getAllPageDetails(pageList) + + const blockSet = new Set() + const list = pageDetailList.map((page) => getBlocksSchema(page.page_content, blockSet)) + const blocks = await Promise.allSettled(list) + + const blockSchema = [] + blocks.forEach((item) => { + if (item.status === 'fulfilled' && Array.isArray(item.value)) { + blockSchema.push(...item.value) + } + }) + + const appSchema = { + // metaData 包含dataSource、utils、i18n、globalState + ...metaData, + // 页面 schema + pageSchema: pageDetailList.map((item) => { + const { page_content, ...meta } = item + return { + ...page_content, + meta: { + ...meta, + isHome: sandbox === PREVIEW_SANDBOX.Web ? meta.isHome : meta.id === currentPage?.id, + router: meta.route + } + } + }), + blockSchema, + // 物料数据 + componentsMap: [...(appData.componentsMap || [])], + + meta: { + ...(appData.meta || {}) + } + } + + const res = await instance.generate(appSchema) + + const { genResult = [] } = res || {} + const fileRes = genResult.map(({ fileContent, fileName, path, fileType }) => { + const slash = path.endsWith('/') || path === '.' ? '' : '/' + let filePath = `${path}${slash}` + if (filePath.startsWith('./')) { + filePath = filePath.slice(2) + } + if (filePath.startsWith('.')) { + filePath = filePath.slice(1) + } + + if (filePath.startsWith('/')) { + filePath = filePath.slice(1) + } + + return { + fileContent, + filePath: `${filePath}${fileName}`, + fileType + } + }) + + return fileRes +} + +export default getPreGenerateInfo diff --git a/packages/design-core/src/preview/src/App.vue b/packages/design-core/src/preview/src/App.vue index 6c851e7dc..92dadfb57 100644 --- a/packages/design-core/src/preview/src/App.vue +++ b/packages/design-core/src/preview/src/App.vue @@ -77,8 +77,8 @@ export default { version: '0.0.0', private: true, scripts: { + dev: 'vite', build: 'vite build', - dev: 'vite --host', lint: 'pnpm run lint:js && pnpm run lint:style', 'lint:fix': 'pnpm run lint:js -- --fix && pnpm run lint:style -- --fix', 'lint:js': 'eslint --ext .js,.vue --ignore-path .gitignore src', diff --git a/packages/design-core/src/preview/src/preview/codesandboxFiles.js b/packages/design-core/src/preview/src/preview/codesandboxFiles.js index 913e912ec..eda0c78bc 100644 --- a/packages/design-core/src/preview/src/preview/codesandboxFiles.js +++ b/packages/design-core/src/preview/src/preview/codesandboxFiles.js @@ -18,10 +18,7 @@ export const codesandboxFiles = { "start-app": { "name": "Run Dev Server", "command": "pnpm run dev", - "runAtStart": true, - "preview": { - "port": 3000 - } + "runAtStart": true } } } diff --git a/packages/design-core/src/preview/src/preview/usePreview.js b/packages/design-core/src/preview/src/preview/usePreview.js index 265999b9c..a8b9b41c3 100644 --- a/packages/design-core/src/preview/src/preview/usePreview.js +++ b/packages/design-core/src/preview/src/preview/usePreview.js @@ -14,7 +14,8 @@ import { provide, inject } from 'vue' import { ReplStore } from '@vue/repl' import vueJsx from '@vue/babel-plugin-jsx' import { transformSync } from '@babel/core' -import { genSFCWithDefaultPlugin, parseRequiredBlocks } from '@opentiny/tiny-engine-dsl-vue' +import { parseRequiredBlocks } from '@opentiny/tiny-engine-controller/js/block' +import { genSFCWithDefaultPlugin } from '@opentiny/tiny-engine-dsl-vue' import importMap from './importMap' import srcFiles from './srcFiles' import generateMetaFiles, { processAppJsCode } from './generate' diff --git a/packages/toolbars/codesandbox/package.json b/packages/toolbars/codesandbox/package.json index 49251ddcb..883ebf7b2 100644 --- a/packages/toolbars/codesandbox/package.json +++ b/packages/toolbars/codesandbox/package.json @@ -27,7 +27,9 @@ "@opentiny/tiny-engine-canvas": "workspace:*", "@opentiny/tiny-engine-common": "workspace:*", "@opentiny/tiny-engine-controller": "workspace:*", - "@opentiny/tiny-engine-utils": "workspace:*" + "@opentiny/tiny-engine-dsl-vue": "workspace:*", + "@opentiny/tiny-engine-utils": "workspace:*", + "codesandbox": "^2.2.3" }, "devDependencies": { "@vitejs/plugin-vue": "^4.2.3", diff --git a/packages/toolbars/codesandbox/src/Main.vue b/packages/toolbars/codesandbox/src/Main.vue index e815ac4c6..a1e7fdd44 100644 --- a/packages/toolbars/codesandbox/src/Main.vue +++ b/packages/toolbars/codesandbox/src/Main.vue @@ -15,10 +15,15 @@