Skip to content

Commit

Permalink
Ccf 2024/codesandbox preview (#896)
Browse files Browse the repository at this point in the history
* fix(preview): fix missing handle `Block` in Toolbar

* feat: add `codesandbox` plugin

* feat: add `pageid` to url when app init

* feat(codesandbox): support preview both `Block` and `Page`

* fix: fix missing change generating
  • Loading branch information
xiejay97 authored Nov 11, 2024
1 parent d90f0f2 commit 6c233c3
Show file tree
Hide file tree
Showing 29 changed files with 1,197 additions and 395 deletions.
2 changes: 2 additions & 0 deletions jsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,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-vue": ["packages/toolbars/generate-vue/index"],
"@opentiny/tiny-engine-toolbar-clean": ["packages/toolbars/clean/index"],
"@opentiny/tiny-engine-toolbar-save": ["packages/toolbars/save/index"],
Expand Down Expand Up @@ -57,6 +58,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/*"],
Expand Down
2 changes: 1 addition & 1 deletion mockServer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
File renamed without changes.
41 changes: 41 additions & 0 deletions packages/controller/js/generate-files/http.js
Original file line number Diff line number Diff line change
@@ -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}`)
177 changes: 177 additions & 0 deletions packages/controller/js/generate-files/index.js
Original file line number Diff line number Diff line change
@@ -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
18 changes: 10 additions & 8 deletions packages/controller/js/preview.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,14 @@ import { isDevelopEnv } from './environments'
import useResource from '../src/useResource'
// prefer old unicode hacks for backward compatibility

const { COMPONENT_NAME } = constants
const { COMPONENT_NAME, PREVIEW_SANDBOX } = constants

export const utoa = (string) => btoa(unescape(encodeURIComponent(string)))

export const atou = (base64) => decodeURIComponent(escape(atob(base64)))

const open = (params = {}) => {
const getParams = (_params, type, sandbox) => {
const params = Object.assign({ type, sandbox }, _params)
const paramsMap = new URLSearchParams(location.search)
params.app = paramsMap.get('id')
params.tenant = paramsMap.get('tenant')
Expand All @@ -30,7 +31,10 @@ const open = (params = {}) => {
.filter((item) => item.script)
.reduce((pre, cur) => ({ ...pre, [cur.package]: cur.script }), {})
params.styles = [...styles]
return params
}

const open = (params = {}) => {
const href = window.location.href.split('?')[0] || './'
const tenant = new URLSearchParams(location.search).get('tenant') || ''
let openUrl = ''
Expand All @@ -46,12 +50,10 @@ const open = (params = {}) => {
aTag.click()
}

export const previewPage = (params = {}) => {
params.type = COMPONENT_NAME.Page
open(params)
export const previewPage = (params = {}, sandbox = PREVIEW_SANDBOX.Web) => {
open(getParams(params, COMPONENT_NAME.Page, sandbox))
}

export const previewBlock = (params = {}) => {
params.type = COMPONENT_NAME.Block
open(params)
export const previewBlock = (params = {}, sandbox = PREVIEW_SANDBOX.Web) => {
open(getParams(params, COMPONENT_NAME.Block, sandbox))
}
8 changes: 8 additions & 0 deletions packages/controller/src/useResource.js
Original file line number Diff line number Diff line change
Expand Up @@ -325,13 +325,21 @@ const initPageOrBlock = async () => {
return
}

const updateUrlPageId = (id) => {
const url = new URL(window.location)

url.searchParams.delete('blockid')
url.searchParams.set('pageid', id)
window.history.replaceState({}, '', url)
}
// url 没有 pageid 或 blockid,到页面首页或第一页
const pageInfo = resState.pageTree.find((page) => page?.meta?.isHome) ||
resState.pageTree.find(
(page) => page.componentName === COMPONENT_NAME.Page && page?.meta?.group !== 'publicPages'
) || {
componentName: COMPONENT_NAME.Page
}
updateUrlPageId(pageInfo.meta.id)
initPage(pageInfo)
}

Expand Down
1 change: 1 addition & 0 deletions packages/design-core/assets/code-sandbox.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions packages/design-core/config/addons.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import Redoundo from '@opentiny/tiny-engine-toolbar-redoundo'
import Save from '@opentiny/tiny-engine-toolbar-save'
import Clean from '@opentiny/tiny-engine-toolbar-clean'
import Preview from '@opentiny/tiny-engine-toolbar-preview'
import CodeSandbox from '@opentiny/tiny-engine-toolbar-codesandbox'
import GenerateVue from '@opentiny/tiny-engine-toolbar-generate-vue'
import Refresh from '@opentiny/tiny-engine-toolbar-refresh'
import Collaboration from '@opentiny/tiny-engine-toolbar-collaboration'
Expand Down Expand Up @@ -56,6 +57,7 @@ const addons = {
Save,
GenerateVue,
Preview,
CodeSandbox,
Redoundo,
Fullscreen,
Checkinout,
Expand Down
2 changes: 2 additions & 0 deletions packages/design-core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@
"@opentiny/tiny-engine-toolbar-logout": "workspace:*",
"@opentiny/tiny-engine-toolbar-media": "workspace:*",
"@opentiny/tiny-engine-toolbar-preview": "workspace:*",
"@opentiny/tiny-engine-toolbar-codesandbox": "workspace:*",
"@opentiny/tiny-engine-toolbar-redoundo": "workspace:*",
"@opentiny/tiny-engine-toolbar-refresh": "workspace:*",
"@opentiny/tiny-engine-toolbar-save": "workspace:*",
Expand All @@ -85,6 +86,7 @@
"@vue/babel-plugin-jsx": "1.1.1",
"@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",
Expand Down
Loading

0 comments on commit 6c233c3

Please sign in to comment.