diff --git a/designer-demo/registry.js b/designer-demo/registry.js index 2e2fcacb1a..61389d4f70 100644 --- a/designer-demo/registry.js +++ b/designer-demo/registry.js @@ -22,6 +22,7 @@ import { Save, Clean, Preview, + Codesandbox, GenerateCode, Refresh, Collaboration, @@ -65,7 +66,7 @@ export default { center: ['engine.toolbars.media'], right: [ ['engine.toolbars.redoundo', 'engine.toolbars.clean'], - ['engine.toolbars.preview'], + ['engine.toolbars.preview', 'engine.toolbars.codesandbox'], ['engine.toolbars.generate-code', 'engine.toolbars.save'] ], collapse: [ @@ -94,6 +95,7 @@ export default { Collaboration, Clean, Preview, + Codesandbox, Refresh, GenerateCode, Save, diff --git a/designer-demo/vite.config.js b/designer-demo/vite.config.js index e608817972..520b37c1c6 100644 --- a/designer-demo/vite.config.js +++ b/designer-demo/vite.config.js @@ -16,6 +16,9 @@ export default defineConfig((configEnv) => { publicDir: path.resolve(__dirname, './public'), server: { port: 8090 + }, + define: { + process: {} } } diff --git a/jsconfig.json b/jsconfig.json index 96b5fe1aaa..8e82a201cc 100644 --- a/jsconfig.json +++ b/jsconfig.json @@ -29,6 +29,7 @@ "@opentiny/tiny-engine-toolbar-logo": ["packages/toolbars/logo/index"], "@opentiny/tiny-engine-toolbar-media": ["packages/toolbars/media/index"], "@opentiny/tiny-engine-toolbar-preview": ["packages/toolbars/preview/index"], + "@opentiny/tiny-engine-toolbar-codesandbox": ["packages/toolbars/codesandbox/index"], "@opentiny/tiny-engine-toolbar-generate-code": ["packages/toolbars/generate-code/index"], "@opentiny/tiny-engine-toolbar-clean": ["packages/toolbars/clean/index"], "@opentiny/tiny-engine-toolbar-save": ["packages/toolbars/save/index"], @@ -58,6 +59,7 @@ "@opentiny/tiny-engine-toolbar-logo/*": ["packages/toolbars/logo/*"], "@opentiny/tiny-engine-toolbar-media/*": ["packages/toolbars/media/*"], "@opentiny/tiny-engine-toolbar-preview/*": ["packages/toolbars/preview/*"], + "@opentiny/tiny-engine-toolbar-codesandbox/*": ["packages/toolbars/codesandbox/*"], "@opentiny/tiny-engine-toolbar-clean/*": ["packages/toolbars/clean/*"], "@opentiny/tiny-engine-toolbar-save/*": ["packages/toolbars/save/*"], "@opentiny/tiny-engine-theme-dark/*": ["packages/theme/dark/*"], diff --git a/packages/build/vite-config/src/vite-plugins/devAliasPlugin.js b/packages/build/vite-config/src/vite-plugins/devAliasPlugin.js index 0679556480..68eb9f24f6 100644 --- a/packages/build/vite-config/src/vite-plugins/devAliasPlugin.js +++ b/packages/build/vite-config/src/vite-plugins/devAliasPlugin.js @@ -43,6 +43,7 @@ const getDevAlias = (useSourceAlias) => { '@opentiny/tiny-engine-toolbar-logout': path.resolve(basePath, 'packages/toolbars/logout/index.js'), '@opentiny/tiny-engine-toolbar-media': path.resolve(basePath, 'packages/toolbars/media/index.js'), '@opentiny/tiny-engine-toolbar-preview': path.resolve(basePath, 'packages/toolbars/preview/index.js'), + '@opentiny/tiny-engine-toolbar-codesandbox': path.resolve(basePath, 'packages/toolbars/codesandbox/index.js'), '@opentiny/tiny-engine-toolbar-generate-code': path.resolve(basePath, 'packages/toolbars/generate-code/index.js'), '@opentiny/tiny-engine-toolbar-refresh': path.resolve(basePath, 'packages/toolbars/refresh/index.js'), '@opentiny/tiny-engine-toolbar-redoundo': path.resolve(basePath, 'packages/toolbars/redoundo/index.js'), diff --git a/packages/design-core/assets/code-sandbox.svg b/packages/design-core/assets/code-sandbox.svg new file mode 100644 index 0000000000..838d755ca7 --- /dev/null +++ b/packages/design-core/assets/code-sandbox.svg @@ -0,0 +1,4 @@ + \ No newline at end of file diff --git a/packages/design-core/index.js b/packages/design-core/index.js index e3bff183f8..302cc81237 100644 --- a/packages/design-core/index.js +++ b/packages/design-core/index.js @@ -12,6 +12,7 @@ export { default as Redoundo, HistoryService } from '@opentiny/tiny-engine-toolb export { default as Save } from '@opentiny/tiny-engine-toolbar-save' export { default as Clean } from '@opentiny/tiny-engine-toolbar-clean' export { default as Preview } from '@opentiny/tiny-engine-toolbar-preview' +export { default as Codesandbox } from '@opentiny/tiny-engine-toolbar-codesandbox' export { default as GenerateCode, SaveLocalService } from '@opentiny/tiny-engine-toolbar-generate-code' export { default as Refresh } from '@opentiny/tiny-engine-toolbar-refresh' export { default as Collaboration } from '@opentiny/tiny-engine-toolbar-collaboration' diff --git a/packages/design-core/package.json b/packages/design-core/package.json index 92f2fc982f..cb1e831213 100644 --- a/packages/design-core/package.json +++ b/packages/design-core/package.json @@ -45,6 +45,7 @@ "@opentiny/tiny-engine-canvas": "workspace:*", "@opentiny/tiny-engine-common": "workspace:*", "@opentiny/tiny-engine-configurator": "workspace:*", + "@opentiny/tiny-engine-dsl-vue": "workspace:^", "@opentiny/tiny-engine-i18n-host": "workspace:*", "@opentiny/tiny-engine-layout": "workspace:~", "@opentiny/tiny-engine-meta-register": "workspace:*", @@ -72,6 +73,7 @@ "@opentiny/tiny-engine-theme-light": "workspace:*", "@opentiny/tiny-engine-toolbar-breadcrumb": "workspace:*", "@opentiny/tiny-engine-toolbar-clean": "workspace:*", + "@opentiny/tiny-engine-toolbar-codesandbox": "workspace:*", "@opentiny/tiny-engine-toolbar-collaboration": "workspace:*", "@opentiny/tiny-engine-toolbar-fullscreen": "workspace:*", "@opentiny/tiny-engine-toolbar-generate-code": "workspace:*", @@ -93,6 +95,7 @@ "@vue/babel-plugin-jsx": "^1.2.5", "@vue/repl": "^2.9.0", "@vueuse/core": "^9.6.0", + "codesandbox": "^2.2.3", "element-resize-detector": "^1.2.4", "eslint-linter-browserify": "8.57.0", "file-saver": "^2.0.5", diff --git a/packages/toolbars/codesandbox/assets/test.png b/packages/toolbars/codesandbox/assets/test.png new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/toolbars/codesandbox/index.js b/packages/toolbars/codesandbox/index.js new file mode 100644 index 0000000000..6f941d9f2f --- /dev/null +++ b/packages/toolbars/codesandbox/index.js @@ -0,0 +1,7 @@ +import entry from './src/Main.vue' +import metaData from './meta' + +export default { + ...metaData, + entry +} diff --git a/packages/toolbars/codesandbox/meta.js b/packages/toolbars/codesandbox/meta.js new file mode 100644 index 0000000000..87aac5d91f --- /dev/null +++ b/packages/toolbars/codesandbox/meta.js @@ -0,0 +1,11 @@ +export default { + id: 'engine.toolbars.codesandbox', + type: 'toolbars', + title: 'codesandbox', + options: { + icon: { + default: 'code-sandbox' + }, + renderType: 'icon' + } +} diff --git a/packages/toolbars/codesandbox/mock/test.js b/packages/toolbars/codesandbox/mock/test.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/toolbars/codesandbox/package.json b/packages/toolbars/codesandbox/package.json new file mode 100644 index 0000000000..95f83e81ff --- /dev/null +++ b/packages/toolbars/codesandbox/package.json @@ -0,0 +1,42 @@ +{ + "name": "@opentiny/tiny-engine-toolbar-codesandbox", + "version": "2.0.0", + "publishConfig": { + "access": "public" + }, + "scripts": { + "build": "vite build" + }, + "type": "module", + "main": "dist/index.js", + "module": "dist/index.js", + "files": [ + "dist" + ], + "repository": { + "type": "git", + "url": "https://github.com/opentiny/tiny-engine", + "directory": "packages/toolbars/codesandbox" + }, + "bugs": { + "url": "https://github.com/opentiny/tiny-engine/issues" + }, + "author": "OpenTiny Team", + "license": "MIT", + "homepage": "https://opentiny.design/tiny-engine", + "dependencies": { + "@opentiny/tiny-engine-canvas": "workspace:*", + "@opentiny/tiny-engine-common": "workspace:*", + "@opentiny/tiny-engine-utils": "workspace:*", + "codesandbox": "^2.2.3" + }, + "devDependencies": { + "@vitejs/plugin-vue": "^5.1.2", + "@vitejs/plugin-vue-jsx": "^4.0.1", + "vite": "^5.4.2" + }, + "peerDependencies": { + "@opentiny/vue": "^3.14.0", + "vue": "^3.4.15" + } +} diff --git a/packages/toolbars/codesandbox/src/Main.vue b/packages/toolbars/codesandbox/src/Main.vue new file mode 100644 index 0000000000..9b7b79889f --- /dev/null +++ b/packages/toolbars/codesandbox/src/Main.vue @@ -0,0 +1,93 @@ + + + diff --git a/packages/toolbars/codesandbox/src/codesandboxFiles.js b/packages/toolbars/codesandbox/src/codesandboxFiles.js new file mode 100644 index 0000000000..9d145184f7 --- /dev/null +++ b/packages/toolbars/codesandbox/src/codesandboxFiles.js @@ -0,0 +1,27 @@ +export const codesandboxFiles = { + '.codesandbox/tasks.json': { + content: String.raw`{ + // These tasks will run in order when initializing your CodeSandbox project. + "setupTasks": [ + { + "name": "pnpm install", + "command": "pnpm install" + } + ], + + // These tasks can be run from CodeSandbox. Running one will open a log in the app. + "tasks": { + "install-dependencies": { + "name": "Install Dependencies", + "command": "pnpm install" + }, + "start-app": { + "name": "Run Dev Server", + "command": "pnpm run dev", + "runAtStart": true + } + } + } + ` + } +} diff --git a/packages/toolbars/codesandbox/vite.config.js b/packages/toolbars/codesandbox/vite.config.js new file mode 100644 index 0000000000..93c6222ab7 --- /dev/null +++ b/packages/toolbars/codesandbox/vite.config.js @@ -0,0 +1,22 @@ +import { defineConfig } from 'vite' +import path from 'path' +import vue from '@vitejs/plugin-vue' +import vueJsx from '@vitejs/plugin-vue-jsx' + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [vue(), vueJsx()], + publicDir: false, + resolve: {}, + build: { + lib: { + entry: path.resolve(__dirname, './index.js'), + name: 'toolbar-codesandbox', + fileName: () => 'index.js', + formats: ['es'] + }, + rollupOptions: { + external: ['vue', /@opentiny\/tiny-engine.*/, /@opentiny\/vue.*/] + } + } +}) diff --git a/packages/toolbars/generate-code/src/composable/useSaveLocal.js b/packages/toolbars/generate-code/src/composable/useSaveLocal.js index 6e7216844a..67d9cf9789 100644 --- a/packages/toolbars/generate-code/src/composable/useSaveLocal.js +++ b/packages/toolbars/generate-code/src/composable/useSaveLocal.js @@ -11,7 +11,16 @@ */ import { Modal } from '@opentiny/vue' -import { useCanvas, getMergeMeta, useEnv } from '@opentiny/tiny-engine-meta-register' +import { fetchMetaData, fetchPageList, fetchBlockSchema } from '../http' +import { + useBlock, + useCanvas, + getMergeMeta, + getMetaApi, + useEnv, + META_SERVICE, + META_APP +} from '@opentiny/tiny-engine-meta-register' // 获取当前页面的全量信息 @@ -76,9 +85,133 @@ const savePageLocal = async () => { }) } +const getParams = () => { + const { getSchema } = useCanvas() + const { isBlock, getCurrentPage } = useCanvas() + const { getCurrentBlock } = useBlock() + + const params = { + framework: getMergeMeta('engine.config')?.dslMode, + platform: getMergeMeta('engine.config')?.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 +} + +const getAllPageDetails = async (pageList) => { + const detailPromise = pageList.map(({ id }) => getMetaApi(META_APP.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 getPreGenerateInfo = async () => { + const params = getParams() + const { id } = getMetaApi(META_SERVICE.GlobalService).getBaseInfo() + const { getAllNestedBlocksSchema, generateAppCode } = getMetaApi('engine.service.generateCode') + + const promises = [ + getMetaApi(META_SERVICE.Http).get(`/app-center/v1/api/apps/schema/${id}`), + fetchMetaData(params), + fetchPageList(params.app) + ] + + const [appData, metaData, pageList, dirHandle] = await Promise.all(promises) + const pageDetailList = await getAllPageDetails(pageList) + + // 这里需要手动传入 blockSet 的原因是多页面可能会存在重复的区块 + const blockSet = new Set() + const list = pageDetailList.map((page) => getAllNestedBlocksSchema(page.page_content, fetchBlockSchema, 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, + router: meta.route + } + } + }), + blockSchema, + // 物料数据 + componentsMap: [...(appData.componentsMap || [])], + + meta: { + ...(appData.meta || {}) + } + } + + const res = await generateAppCode(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 [dirHandle, fileRes] +} + export default () => { return { + getPreGenerateInfo, confirmSaveLocal, - savePageLocal + savePageLocal, + getParams } } diff --git a/packages/utils/src/constants/index.js b/packages/utils/src/constants/index.js index e75dcb1f92..905c9fd8a9 100644 --- a/packages/utils/src/constants/index.js +++ b/packages/utils/src/constants/index.js @@ -75,6 +75,12 @@ export const BLOCK_OPENNESS = { Special: 2 } + +export const PREVIEW_SANDBOX = { + Web: 'Web', + CodeSandbox: 'CodeSandbox' +} + export const OPEN_DELAY = { Default: 500 } diff --git a/packages/vue-generator/src/templates/vue-template/templateFiles/genViteConfig.js b/packages/vue-generator/src/templates/vue-template/templateFiles/genViteConfig.js index da9563182d..6cc8b8b6f8 100644 --- a/packages/vue-generator/src/templates/vue-template/templateFiles/genViteConfig.js +++ b/packages/vue-generator/src/templates/vue-template/templateFiles/genViteConfig.js @@ -9,6 +9,9 @@ export default () => { import vueJsx from '@vitejs/plugin-vue-jsx' export default defineConfig({ + server: { + allowedHosts: ['localhost', '.csb.app'] + }, resolve: { alias: { '@': path.resolve(__dirname, 'src')