Feat router page embedded view toggle (#1010)

* feat(canvas, toolbar/view-setting): add router view setting view mode switcher, support embedded and standalone mode in canvas

* fix(canvas/render): fix switch locale failed

* feat(canvas): add window.onpopstate listener

* fix(toolbars/view-setting): fix init view mode value no fallback cause next view mode label error

* fix: using enum instead of multiple time literal string declaration, add fallback for cache value getter in toolbar, fix typo mistake

* fix: simplify return value using enum, remove unnecessary style code

* fix(toolbars/view-setting): fix package.json wrong repository directory
This commit is contained in:
rhlin 2025-01-10 16:48:53 +08:00 committed by GitHub
parent 4c1c4c49f4
commit ab4e7dc2ed
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 301 additions and 16 deletions

View File

@ -14,6 +14,7 @@ import {
Breadcrumb,
Fullscreen,
Lang,
ViewSetting,
Logo,
Lock,
Media,
@ -70,7 +71,8 @@ export default {
collapse: [
['engine.toolbars.collaboration'],
['engine.toolbars.refresh', 'engine.toolbars.fullscreen'],
['engine.toolbars.lang']
['engine.toolbars.lang'],
['engine.toolbars.viewSetting']
]
}
}
@ -96,7 +98,8 @@ export default {
GenerateCode,
Save,
Fullscreen,
Lang
Lang,
ViewSetting
],
plugins: [Materials, Tree, Page, Block, Datasource, Bridge, I18n, Script, State, Schema, Help, Robot],
dsls: [{ id: 'engine.dsls.dslvue' }],

View File

@ -23,6 +23,7 @@
"@opentiny/tiny-engine-toolbar-breadcrumb": ["packages/toolbars/breadcrumb/index"],
"@opentiny/tiny-engine-toolbar-fullscreen": ["packages/toolbars/fullscreen/index"],
"@opentiny/tiny-engine-toolbar-lang": ["packages/toolbars/lang/index"],
"@opentiny/tiny-engine-toolbar-view-setting": ["packages/toolbars/view-setting/index"],
"@opentiny/tiny-engine-toolbar-layout": ["packages/toolbars/layout/index"],
"@opentiny/tiny-engine-toolbar-lock": ["packages/toolbars/lock/index"],
"@opentiny/tiny-engine-toolbar-logo": ["packages/toolbars/logo/index"],
@ -51,6 +52,7 @@
"@opentiny/tiny-engine-toolbar-breadcrumb/*": ["packages/toolbars/breadcrumb/*"],
"@opentiny/tiny-engine-toolbar-fullscreen/*": ["packages/toolbars/fullscreen/*"],
"@opentiny/tiny-engine-toolbar-lang/*": ["packages/toolbars/lang/*"],
"@opentiny/tiny-engine-toolbar-view-setting/*": ["packages/toolbars/view-setting/*"],
"@opentiny/tiny-engine-toolbar-layout/*": ["packages/toolbars/layout/*"],
"@opentiny/tiny-engine-toolbar-lock/*": ["packages/toolbars/lock/*"],
"@opentiny/tiny-engine-toolbar-logo/*": ["packages/toolbars/logo/*"],

View File

@ -36,6 +36,7 @@ const getDevAlias = (useSourceAlias) => {
'@opentiny/tiny-engine-toolbar-breadcrumb': path.resolve(basePath, 'packages/toolbars/breadcrumb/index.js'),
'@opentiny/tiny-engine-toolbar-fullscreen': path.resolve(basePath, 'packages/toolbars/fullscreen/index.js'),
'@opentiny/tiny-engine-toolbar-lang': path.resolve(basePath, 'packages/toolbars/lang/index.js'),
'@opentiny/tiny-engine-toolbar-view-setting': path.resolve(basePath, 'packages/toolbars/view-setting/index.js'),
'@opentiny/tiny-engine-toolbar-layout': path.resolve(basePath, 'packages/toolbars/layout/index.js'),
'@opentiny/tiny-engine-toolbar-lock': path.resolve(basePath, 'packages/toolbars/lock/index.js'),
'@opentiny/tiny-engine-toolbar-logo': path.resolve(basePath, 'packages/toolbars/logo/index.js'),

View File

@ -12,7 +12,8 @@
:canvas-src-doc="canvasSrcDoc"
@remove="removeNode"
@selected="nodeSelected"
></component>
>
</component>
</template>
<template #footer>
<component :is="CanvasBreadcrumb" :data="footData"></component>
@ -209,6 +210,17 @@ export default {
}
})()
// TODO: getBaseInfo
const postUrlChanged = () => {
usePage().postLocationHistoryChanged(Object.fromEntries(new URLSearchParams(window.location.search)))
}
onMounted(() => {
window.addEventListener('popstate', postUrlChanged)
})
onUnmounted(() => {
window.removeEventListener('popstate', postUrlChanged)
})
return {
removeNode,
canvasSrc,

View File

@ -11,7 +11,15 @@
*/
import { provide, watch, defineComponent, PropType, ref, inject, onUnmounted, h, Ref } from 'vue'
import { getDesignMode, setDesignMode, setController, useCustomRenderer, getController } from './canvas-function'
import {
getDesignMode,
setDesignMode,
setController,
useCustomRenderer,
getController,
useRouterViewSetting,
useLocale
} from './canvas-function'
import { removeBlockCompsCache, setConfigure } from './material-function'
import { useUtils, useBridge, useDataSourceMap, useGlobalState } from './application-function'
import { IPageSchema, useContext, usePageContext, useSchema } from './page-block-function'
@ -79,6 +87,9 @@ const throttleUpdateSchema = useThrottleFn(
true
)
const pageRenderer = getRenderer()
const { routerViewSetting } = useRouterViewSetting()
export default defineComponent({
props: {
entry: {
@ -120,23 +131,39 @@ export default defineComponent({
pageContext.pageId = props.pageId || pageIdFromPath
pageContext.active = props.active || !pageIdFromPath
pageContext.setCssScopeId(props.cssScopeId || (props.entry ? null : `data-te-page-${pageContext.pageId}`))
if (props.entry) {
provide('page-ancestors', pageAncestors)
getPageAncestors(pageContext.pageId).then((value) => {
pageAncestors.value = value
})
const updatePageAncestor = () => {
if (routerViewSetting.viewMode === 'standalone') {
pageAncestors.value = []
return
}
getPageAncestors(pageContext.pageId).then((value) => {
pageAncestors.value = value
})
}
updatePageAncestor()
const cancel = getController().addHistoryDataChangedCallback(() => {
const pageIdFromPath = getController().getBaseInfo().pageId
pageContext.pageId = props.pageId || pageIdFromPath
pageContext.active = props.active || !pageIdFromPath
getPageAncestors(pageContext.pageId).then((value) => {
pageAncestors.value = value
})
updatePageAncestor()
})
onUnmounted(() => {
cancel()
})
watch(
() => routerViewSetting.viewMode,
() => {
updatePageAncestor()
}
)
useLocale()
window.host.subscribe({
topic: 'schemaChange',
subscriber: 'canvasRenderer',
@ -245,11 +272,16 @@ export default defineComponent({
)
}
const renderer = getRenderer()
return () =>
pageAncestors.value === null
? h(CanvasEmpty, { placeholderText: '页面分析加载中' })
: renderer(schema, refreshKey, props.entry, pageContext.active, !!pageContext.pageId)
: pageRenderer(
schema,
refreshKey,
props.entry,
pageContext.active,
!!pageContext.pageId && pageAncestors.value.length
)
}
})

View File

@ -3,3 +3,4 @@ export * from './design-mode'
export * from './global-notify'
export * from './custom-renderer'
export * from './locale'
export * from './router-view-setting'

View File

@ -0,0 +1,39 @@
import { reactive, watch } from 'vue'
import { useBroadcastChannel } from '@vueuse/core'
import { constants } from '@opentiny/tiny-engine-utils'
const { BROADCAST_CHANNEL, CANVAS_ROUTER_VIEW_SETTING_VIEW_MODE_KEY } = constants
export enum ViewMode {
EMBEDDED = 'embedded',
STANDALONE = 'standalone'
}
export interface IRouterViewSetting {
viewMode: ViewMode
}
function getCacheValue() {
const value = localStorage.getItem(CANVAS_ROUTER_VIEW_SETTING_VIEW_MODE_KEY)
if (!(Object.values(ViewMode) as string[]).includes(value)) {
return ViewMode.EMBEDDED
}
return value as ViewMode
}
export function useRouterViewSetting() {
const routerViewSetting = reactive<IRouterViewSetting>({
viewMode: getCacheValue()
})
const { data } = useBroadcastChannel<IRouterViewSetting, IRouterViewSetting>({
name: BROADCAST_CHANNEL.CanvasRouterViewSetting
})
watch(data, () => {
routerViewSetting.viewMode = data.value.viewMode
})
return {
routerViewSetting
}
}

View File

@ -4,6 +4,7 @@ export { init } from './src/init'
export { default as Breadcrumb, BreadcrumbService } from '@opentiny/tiny-engine-toolbar-breadcrumb'
export { default as Fullscreen } from '@opentiny/tiny-engine-toolbar-fullscreen'
export { default as Lang } from '@opentiny/tiny-engine-toolbar-lang'
export { default as ViewSetting } from '@opentiny/tiny-engine-toolbar-view-setting'
export { default as Logo } from '@opentiny/tiny-engine-toolbar-logo'
export { default as Lock } from '@opentiny/tiny-engine-toolbar-lock'
export { default as Media } from '@opentiny/tiny-engine-toolbar-media'

View File

@ -76,6 +76,7 @@
"@opentiny/tiny-engine-toolbar-fullscreen": "workspace:*",
"@opentiny/tiny-engine-toolbar-generate-code": "workspace:*",
"@opentiny/tiny-engine-toolbar-lang": "workspace:*",
"@opentiny/tiny-engine-toolbar-view-setting": "workspace:*",
"@opentiny/tiny-engine-toolbar-layout": "workspace:*",
"@opentiny/tiny-engine-toolbar-lock": "workspace:*",
"@opentiny/tiny-engine-toolbar-logo": "workspace:*",

View File

@ -14,6 +14,7 @@ import {
Breadcrumb,
Fullscreen,
Lang,
ViewSetting,
Logo,
Lock,
Media,
@ -70,7 +71,8 @@ export default {
collapse: [
['engine.toolbars.collaboration'],
['engine.toolbars.refresh', 'engine.toolbars.fullscreen'],
['engine.toolbars.lang']
['engine.toolbars.lang'],
['engine.toolbars.viewSetting']
]
}
}
@ -96,7 +98,8 @@ export default {
GenerateCode,
Save,
Fullscreen,
Lang
Lang,
ViewSetting
],
plugins: [Materials, Tree, Page, Block, Datasource, Bridge, I18n, Script, State, Schema, Help, Robot],
dsls: [{ id: 'engine.dsls.dslvue' }],

View File

@ -23,7 +23,8 @@ export default {
collapse: [
['engine.toolbars.collaboration'],
['engine.toolbars.refresh', 'engine.toolbars.fullscreen'],
['engine.toolbars.lang']
['engine.toolbars.lang'],
['engine.toolbars.viewSetting']
]
}
},

View File

@ -0,0 +1,19 @@
/**
* 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 entry from './src/Main.vue'
import metaData from './meta'
export default {
...metaData,
entry
}

View File

@ -0,0 +1,12 @@
export default {
id: 'engine.toolbars.viewSetting',
type: 'toolbars',
title: 'viewSetting',
options: {
icon: {
default: 'routerview'
},
renderType: 'icon',
collapsed: true
}
}

View File

@ -0,0 +1,42 @@
{
"name": "@opentiny/tiny-engine-toolbar-view-setting",
"version": "2.1.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/view-setting"
},
"bugs": {
"url": "https://github.com/opentiny/tiny-engine/issues"
},
"author": "OpenTiny Team",
"license": "MIT",
"homepage": "https://opentiny.design/tiny-engine",
"dependencies": {
"@opentiny/tiny-engine-common": "workspace:*",
"@opentiny/tiny-engine-utils": "workspace:*",
"@vueuse/core": "^9.6.0"
},
"devDependencies": {
"@opentiny/tiny-engine-vite-plugin-meta-comments": "workspace:*",
"@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"
}
}

View File

@ -0,0 +1,76 @@
<template>
<toolbar-base
:content="`切换到${nextViewMode.label}`"
:icon="options.icon.default || options.icon"
:options="options"
@click-api="changeViewMode"
>
</toolbar-base>
</template>
<script>
import { computed, ref, watch } from 'vue'
import { useBroadcastChannel } from '@vueuse/core'
import { ToolbarBase } from '@opentiny/tiny-engine-common'
import { constants } from '@opentiny/tiny-engine-utils'
const { BROADCAST_CHANNEL, CANVAS_ROUTER_VIEW_SETTING_VIEW_MODE_KEY } = constants
function getCacheValue() {
const value = localStorage.getItem(CANVAS_ROUTER_VIEW_SETTING_VIEW_MODE_KEY)
if (!['embedded', 'standalone'].includes(value)) {
return 'embedded'
}
return value
}
function setCacheValue(value) {
localStorage.setItem(CANVAS_ROUTER_VIEW_SETTING_VIEW_MODE_KEY, value)
}
export default {
components: {
ToolbarBase
},
props: {
options: {
type: Object,
default: () => ({})
}
},
setup() {
const viewMode = ref(getCacheValue())
const { post, data } = useBroadcastChannel({ name: BROADCAST_CHANNEL.CanvasRouterViewSetting })
watch(data, () => {
viewMode.value = data.value
})
const viewModeOptions = [
{
value: 'embedded',
label: '嵌套视图'
},
{
value: 'standalone',
label: '单页视图'
}
]
const nextViewMode = computed(
() => viewModeOptions.find(({ value }) => value !== viewMode.value) || viewModeOptions[1]
)
const changeViewMode = () => {
viewMode.value = viewMode.value === viewModeOptions[0].value ? viewModeOptions[1].value : viewModeOptions[0].value
setCacheValue(viewMode.value)
post({ viewMode: viewMode.value })
}
return {
viewModeOptions,
viewMode,
changeViewMode,
nextViewMode
}
}
}
</script>

View File

@ -0,0 +1,38 @@
/**
* 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 { defineConfig } from 'vite'
import path from 'path'
import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx'
import generateComment from '@opentiny/tiny-engine-vite-plugin-meta-comments'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [generateComment(), vue(), vueJsx()],
publicDir: false,
resolve: {},
build: {
lib: {
entry: path.resolve(__dirname, './index.js'),
name: 'toolbar-view-setting',
fileName: () => 'index.js',
formats: ['es']
},
rollupOptions: {
output: {
banner: 'import "./style.css"'
},
external: ['vue', /@opentiny\/tiny-engine.*/, /@opentiny\/vue.*/]
}
}
})

View File

@ -93,10 +93,12 @@ if (typeof sessionStorage !== 'undefined') {
export const BROADCAST_CHANNEL = {
CanvasLang: `tiny-lowcode-canvas-lang-${CHANNEL_UID}`,
Notify: `global-notify-${CHANNEL_UID}`
Notify: `global-notify-${CHANNEL_UID}`,
CanvasRouterViewSetting: `tiny-lowcode-canvas-router-view-setting-${CHANNEL_UID}`
}
export const STORAGE_KEY_FIXED_PANELS = `tiny-engine-fixed-panels-${CHANNEL_UID}`
export const CANVAS_ROUTER_VIEW_SETTING_VIEW_MODE_KEY = `tiny-engine-canvas-router-view-setting-view-mode`
export const TYPES = {
ErrorType: 'error',