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')