diff --git a/packages/canvas/render/src/builtin/CanvasRouterView.vue b/packages/canvas/render/src/builtin/CanvasRouterView.vue
new file mode 100644
index 000000000..96beea7c2
--- /dev/null
+++ b/packages/canvas/render/src/builtin/CanvasRouterView.vue
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
diff --git a/packages/canvas/render/src/builtin/builtin.json b/packages/canvas/render/src/builtin/builtin.json
index ad56e67ab..4d2e487c1 100644
--- a/packages/canvas/render/src/builtin/builtin.json
+++ b/packages/canvas/render/src/builtin/builtin.json
@@ -2,6 +2,63 @@
"data": {
"materials": {
"components": [
+ {
+ "icon": "Box",
+ "name": {
+ "zh_CN": "RouterView"
+ },
+ "component": "RouterView",
+ "schema": {
+ "slots": {},
+ "properties": [
+ {
+ "label": {
+ "zh_CN": "基础信息"
+ },
+ "description": {
+ "zh_CN": "基础信息"
+ },
+ "collapse": {
+ "number": 6,
+ "text": {
+ "zh_CN": "显示更多"
+ }
+ },
+ "content": []
+ }
+ ],
+ "events": {
+ "onClick": {
+ "label": {
+ "zh_CN": "点击事件"
+ },
+ "description": {
+ "zh_CN": "点击时触发的回调函数"
+ },
+ "type": "event",
+ "functionInfo": {
+ "params": [],
+ "returns": {}
+ },
+ "defaultValue": ""
+ }
+ },
+ "shortcuts": {
+ "properties": []
+ },
+ "contentMenu": {
+ "actions": []
+ }
+ },
+ "configure": {
+ "loop": true,
+ "isContainer": true,
+ "nestingRule": {
+ "childWhitelist": [],
+ "descendantBlacklist": []
+ }
+ }
+ },
{
"icon": "Box",
"name": {
@@ -458,6 +515,17 @@
"zh_CN": "基础元素"
},
"children": [
+ {
+ "name": {
+ "zh_CN": "RouterView"
+ },
+ "icon": "Box",
+ "screenshot": "",
+ "snippetName": "RouterView",
+ "schema": {
+ "componentName": "RouterView"
+ }
+ },
{
"name": {
"zh_CN": "文本"
diff --git a/packages/canvas/render/src/builtin/index.js b/packages/canvas/render/src/builtin/index.js
index f857ff1a1..3ab3b562b 100644
--- a/packages/canvas/render/src/builtin/index.js
+++ b/packages/canvas/render/src/builtin/index.js
@@ -17,5 +17,15 @@ import CanvasIcon from './CanvasIcon.vue'
import CanvasSlot from './CanvasSlot.vue'
import CanvasImg from './CanvasImg.vue'
import CanvasPlaceholder from './CanvasPlaceholder.vue'
+import CanvasRouterView from './CanvasRouterView.vue'
-export { CanvasText, CanvasBox, CanvasCollection, CanvasIcon, CanvasSlot, CanvasImg, CanvasPlaceholder }
+export {
+ CanvasText,
+ CanvasBox,
+ CanvasCollection,
+ CanvasIcon,
+ CanvasSlot,
+ CanvasImg,
+ CanvasPlaceholder,
+ CanvasRouterView
+}
diff --git a/packages/canvas/render/src/render.js b/packages/canvas/render/src/render.js
index 1c5c4a36c..a90307e17 100644
--- a/packages/canvas/render/src/render.js
+++ b/packages/canvas/render/src/render.js
@@ -28,7 +28,8 @@ import {
CanvasText,
CanvasSlot,
CanvasImg,
- CanvasPlaceholder
+ CanvasPlaceholder,
+ CanvasRouterView
} from './builtin'
const { BROADCAST_CHANNEL } = constants
@@ -69,7 +70,8 @@ const Mapper = {
CanvasRow,
CanvasCol,
CanvasRowColContainer,
- CanvasPlaceholder
+ CanvasPlaceholder,
+ CanvasRouterView
}
const { post } = useBroadcastChannel({ name: BROADCAST_CHANNEL.Notify })
diff --git a/packages/vue-generator/src/plugins/genRouterPlugin.js b/packages/vue-generator/src/plugins/genRouterPlugin.js
index bccc98eae..869e8bf40 100644
--- a/packages/vue-generator/src/plugins/genRouterPlugin.js
+++ b/packages/vue-generator/src/plugins/genRouterPlugin.js
@@ -5,30 +5,98 @@ const defaultOption = {
path: './src/router'
}
-const parseSchema = (schema) => {
- const { pageSchema } = schema
+const flattenRoutes = (routes, parentPath = '') => {
+ return routes.reduce((acc, route) => {
+ const fullPath = `${parentPath}${route.path}`
+
+ if (route.path !== '/') {
+ if (route.component) {
+ // 如果存在 component,则直接添加路由
+ const newRoute = {
+ path: fullPath,
+ component: route.component,
+ children: flattenRoutes(route.children)
+ }
+ const redirectChild = route.children.find((item) => item.isDefault)
+
+ if (route.children && redirectChild) {
+ newRoute.redirect = `${fullPath}/${redirectChild.path}`
+ }
+
+ acc.push(newRoute)
+ } else if (route.children && route.children.length > 0) {
+ // 如果不存在 component 但有 children,则递归处理 children
+ const children = flattenRoutes(route.children, fullPath + '/')
+ // 将处理后的 children 合并到上一层存在 component 的路由中
+ acc.push(...children)
+ }
+ // 如果既没有 component 也没有 children,则不做任何处理
+ } else {
+ acc.push(route)
+ }
+
+ return acc
+ }, [])
+}
+
+const convertToNestedRoutes = (schema) => {
+ const pageSchema = schema.pageSchema?.sort((a, b) => a.meta?.router?.length - b.meta?.router?.length)
+ const result = []
+ let home = {}
+ let isGetHome = false
+
+ pageSchema.forEach((item) => {
+ if ((item.meta.isHome || item.meta.isDefault) && !isGetHome) {
+ home = {
+ path: '/',
+ redirect: `/${item.meta.router}`
+ }
+ isGetHome = true
+ }
+
+ const parts = item.meta?.router?.split('/').filter(Boolean)
+ let curretnLevel = result
- const routes = pageSchema.map(({ meta: { isHome = false, router = '' } = {}, fileName, path }) => ({
- filePath: `@/views${path ? `/${path}` : ''}/${fileName}.vue`,
- fileName,
- isHome,
- path: router?.startsWith?.('/') ? router : `/${router}`
- }))
+ parts.forEach((part, index) => {
+ let found = false
- const hasRoot = routes.some(({ path }) => path === '/')
+ for (let i = 0; i < curretnLevel.length; i++) {
+ if (curretnLevel[i].path === part) {
+ // 如果已经存在该路径部分,则进入下一层级
+ curretnLevel = curretnLevel[i].children
+ found = true
+ break
+ }
+ }
+
+ if (!found) {
+ // 如果不存在该路径部分,创建一个新节点
+ const newNode = {
+ path: part,
+ children: []
+ }
+ // 如果路径是最后一步,则设置组件和属性
+ if (index === parts.length - 1) {
+ newNode.component = `() => import('@/views${item.path ? `/${item.path}` : ''}/${item.fileName}.vue')`
+ newNode.isDefault = item.meta.isDefault
+ }
- if (!hasRoot && routes.length) {
- const { path: homePath } = routes.find(({ isHome }) => isHome) || { path: routes[0].path }
+ curretnLevel.push(newNode)
+ curretnLevel = newNode.children
+ }
+ })
+ })
- routes.unshift({ path: '/', redirect: homePath })
+ if (home.path) {
+ result.unshift(home)
}
- return routes
+ return flattenRoutes(result)
}
+// 示例路由数组
function genRouterPlugin(options = {}) {
const realOptions = mergeOptions(defaultOption, options)
-
const { path, fileName } = realOptions
return {
@@ -40,34 +108,21 @@ function genRouterPlugin(options = {}) {
* @returns
*/
run(schema) {
- const routesList = parseSchema(schema)
+ const routesList = convertToNestedRoutes(schema)
+ const resultStr = JSON.stringify(routesList, null, 2).replace(
+ /("component":\s*)"(.*?)"/g,
+ (match, p1, p2) => p1 + p2
+ )
// TODO: 支持 hash 模式、history 模式
const importSnippet = "import { createRouter, createWebHashHistory } from 'vue-router'"
const exportSnippet = `
-export default createRouter({
- history: createWebHashHistory(),
- routes
-})`
- const routes = routesList.map(({ fileName, path, redirect, filePath }) => {
- let pathAttr = `path: '${path}'`
- let redirectAttr = ''
- let componentAttr = ''
-
- if (redirect) {
- redirectAttr = `redirect: '${redirect}'`
- }
-
- if (fileName) {
- componentAttr = `component: () => import('${filePath}')`
- }
-
- const res = [pathAttr, redirectAttr, componentAttr].filter((item) => Boolean(item)).join(',')
-
- return `{${res}}`
- })
+ export default createRouter({
+ history: createWebHashHistory(),
+ routes
+ })`
- const routeSnippets = `const routes = [${routes.join(',')}]`
+ const routeSnippets = `const routes = ${resultStr}`
const res = {
fileType: 'js',