diff --git a/packages/plugins/page/src/Main.vue b/packages/plugins/page/src/Main.vue index 2101637cf0..aeb2217d76 100644 --- a/packages/plugins/page/src/Main.vue +++ b/packages/plugins/page/src/Main.vue @@ -23,6 +23,8 @@ :isFolder="state.isFolder" @add="createNewPage('publicPages')" @openSettingPanel="openSettingPanel" + @createPage="createNewPage" + @createFolder="createNewFolder" > @@ -79,12 +81,12 @@ export default { isFolder: false }) - const createNewPage = (group) => { + const createNewPage = (group, parentId = ROOT_ID) => { closeFolderSettingPanel() pageSettingState.isNew = true pageSettingState.currentPageData = { ...DEFAULT_PAGE, - parentId: ROOT_ID, + parentId, route: '', name: 'Untitled', page_content: { @@ -97,10 +99,10 @@ export default { openPageSettingPanel() } - const createNewFolder = () => { + const createNewFolder = (parentId = ROOT_ID) => { closePageSettingPanel() pageSettingState.isNew = true - pageSettingState.currentPageData = { parentId: ROOT_ID, route: '', name: 'untitled' } + pageSettingState.currentPageData = { parentId, route: '', name: 'untitled' } pageSettingState.currentPageDataCopy = extend(true, {}, pageSettingState.currentPageData) state.isFolder = true openFolderSettingPanel() @@ -112,11 +114,11 @@ export default { } }) - const openSettingPanel = async (node) => { - state.isFolder = !node.data.isPage + const openSettingPanel = async (pageData) => { + state.isFolder = !pageData.isPage pageSettingState.isNew = false - const isPageChange = node.data.id !== pageSettingState.currentPageData.id + const isPageChange = pageData.id !== pageSettingState.currentPageData.id if (state.isFolder) { isPageChange && closePageSettingPanel() @@ -125,7 +127,7 @@ export default { isPageChange && closeFolderSettingPanel() openPageSettingPanel() } - const pageDetail = await fetchPageDetail(node.data?.id) + const pageDetail = await fetchPageDetail(pageData?.id) initCurrentPageData(pageDetail) } diff --git a/packages/plugins/page/src/PageGeneral.vue b/packages/plugins/page/src/PageGeneral.vue index c141518e1d..8ac9e11fa9 100644 --- a/packages/plugins/page/src/PageGeneral.vue +++ b/packages/plugins/page/src/PageGeneral.vue @@ -26,14 +26,18 @@ > - + @@ -154,39 +158,50 @@ export default { group: [{ required: true, message: '必须选择页面类型' }] } - const getFolders = (pages) => { - const list = [] + const pageToTreeData = (page) => { + const { id, name, isPage, children } = page - pages.forEach((page) => { - if (!page.isPage && page.id !== pageSettingState.currentPageData.id) { - list.push(page) - if (!page.children) { - page.children = [] - } - page.children = getFolders(page.children) - } - }) + if (!Array.isArray(children)) { + return { id, name, isPage } + } + + return { + id, + name, + isPage, + children: children + .filter((page) => page.id !== pageSettingState.currentPageData.id) + .map((page) => pageToTreeData(page)) + } + } + + // TODO 遗留问题:无法收缩下拉面板的树目录。如果使用tree自带的icon,无法动态设置expandIcon和shrinkIcon + const getNodeIcon = (data) => { + if (data.id === ROOT_ID) { + return null + } + + if (data.isPage) { + return + } - return list + return } const treeFolderOp = computed(() => { - const expandIcon = - const shrinkIcon = const staticPages = pageSettingState.pages[STATIC_PAGE_GROUP_ID]?.data || [] - const data = [{ name: '无', id: ROOT_ID }, ...getFolders(JSON.parse(JSON.stringify(staticPages)))] + const dummyRoot = pageToTreeData({ children: [{ name: '无', id: ROOT_ID }].concat(staticPages) }) + const data = dummyRoot.children const options = { data: data, - shrinkIcon: shrinkIcon, - expandIcon: expandIcon, + shrinkIcon: null, + expandIcon: null, renderContent: (_h, { node, data }) => { return ( - - {node.isLeaf && data.id !== ROOT_ID ? ( - - ) : null} - {node.label} - + <> + {getNodeIcon(data)} + + ) } } @@ -277,22 +292,44 @@ export default { diff --git a/packages/plugins/page/src/PageHome.vue b/packages/plugins/page/src/PageHome.vue index e431e54ec3..31311d4c99 100644 --- a/packages/plugins/page/src/PageHome.vue +++ b/packages/plugins/page/src/PageHome.vue @@ -67,7 +67,7 @@ export default { handleRouteHomeUpdate(id, params) .then(() => { pageSettingState.updateTreeData() - openSettingPanel({ data: pageSettingState.currentPageData }) + openSettingPanel(pageSettingState.currentPageData) pageSettingState.isNew = false if (isVsCodeEnv) { generateRouter({ diff --git a/packages/plugins/page/src/PageSetting.vue b/packages/plugins/page/src/PageSetting.vue index 2578c1d19a..d10da1eb0a 100644 --- a/packages/plugins/page/src/PageSetting.vue +++ b/packages/plugins/page/src/PageSetting.vue @@ -339,6 +339,15 @@ export default { } const deletePage = () => { + if (pageSettingState.treeDataMapping[pageSettingState.currentPageData.id]?.children?.length) { + useNotify({ + type: 'error', + message: '此页面存在子页面或子文件夹,不能删除!' + }) + + return + } + confirm({ title: '提示', message: '您是否要删除页面?', diff --git a/packages/plugins/page/src/PageTree.vue b/packages/plugins/page/src/PageTree.vue index 6ac0762799..25674d4630 100644 --- a/packages/plugins/page/src/PageTree.vue +++ b/packages/plugins/page/src/PageTree.vue @@ -13,29 +13,46 @@ {{ groupItem.groupName }}
- + + label-key="name" + :active="state.currentNodeData.id" + @click-row="handleClickRow" + > + +
+ + diff --git a/packages/plugins/page/src/composable/usePage.js b/packages/plugins/page/src/composable/usePage.js index c99072cc4c..634d2e4f98 100644 --- a/packages/plugins/page/src/composable/usePage.js +++ b/packages/plugins/page/src/composable/usePage.js @@ -12,6 +12,11 @@ import { reactive, ref } from 'vue' import { extend, isEqual } from '@opentiny/vue-renderless/common/object' +import { constants } from '@opentiny/tiny-engine-utils' +import { getMetaApi, META_SERVICE } from '@opentiny/tiny-engine-meta-register' +import http from '../http' + +const { ELEMENT_TAG } = constants const DEFAULT_PAGE = { app: '', @@ -132,6 +137,113 @@ const isChangePageData = () => !isEqual(pageSettingState.currentPageData, pageSe const STATIC_PAGE_GROUP_ID = 0 const COMMON_PAGE_GROUP_ID = 1 +/** + * + * @typedef {Object} PageData + * @property {string | number} id + * @property {string | number} parentId + * + * @typedef {Object} PageNode + * @property {string | number} id + * @property {string | number} parentId + * @property {PageNode[] | undefined} children + * + * @param {PageData[]} data + * @returns + */ +const generateTree = (data) => { + const { ROOT_ID } = pageSettingState + + /** @type {Record} */ + const treeDataMapping = { [ROOT_ID]: { id: ROOT_ID } } + + data.forEach((item) => { + treeDataMapping[item.id] = item + }) + + data.forEach((item) => { + const parentNode = treeDataMapping[item.parentId] + + if (!parentNode) { + return + } + + parentNode.children = parentNode.children || [] + parentNode.children.push(item) + }) + + return treeDataMapping +} + +const getPageList = async (appId) => { + const pagesData = await http.fetchPageList(appId) + + const firstGroupData = { groupName: '静态页面', groupId: STATIC_PAGE_GROUP_ID, data: [] } + const secondGroupData = { groupName: '公共页面', groupId: COMMON_PAGE_GROUP_ID, data: [] } + + pagesData.forEach((item) => { + const namedNode = item.name ? item : { ...item, name: item.folderName, group: 'staticPages' } + const node = item.meta + ? { + ...item, + ...item.meta, + name: item.fileName, + isPage: true, + isBody: item.meta.rootElement === ELEMENT_TAG.Body + } + : namedNode + + const { children, ...other } = node + + if (node.group === 'staticPages') { + firstGroupData.data.push(other) + } else { + secondGroupData.data.push(other) + } + }) + + const firstGroupTreeData = generateTree(firstGroupData.data) + pageSettingState.treeDataMapping = firstGroupTreeData + firstGroupData.data = firstGroupTreeData[pageSettingState.ROOT_ID].children + pageSettingState.pages = [firstGroupData, secondGroupData] + return pageSettingState.pages +} + +/** + * @param {string | number} id page Id + * @param {boolean} withFolders default `false` + * @returns {(string | number)[]} + */ +const getAncestors = async (id, withFolders) => { + if (pageSettingState.pages.length === 0) { + const appId = getMetaApi(META_SERVICE.GlobalService).getBaseInfo().id + await getPageList(appId) + } + + /** + * @param {string | number} id + * @param {(string | number)[]} ancestors + * @returns {(string | number)[]} + */ + const getAncestorsRecursively = (id) => { + const pageNode = pageSettingState.treeDataMapping[id] + + if (pageNode.id === pageSettingState.ROOT_ID) { + return [] + } + + return [pageNode].concat(getAncestorsRecursively(pageNode.parentId)) + } + + const ancestorsWithSelf = getAncestorsRecursively(id) + const ancestors = ancestorsWithSelf.slice(1).reverse() + + if (withFolders) { + return ancestors.map((item) => item.id) + } + return ancestors.filter((item) => item.isPage).map((item) => item.id) +} + export default () => { return { DEFAULT_PAGE, @@ -144,6 +256,8 @@ export default () => { resetPageData, initCurrentPageData, isChangePageData, + getPageList, + getAncestors, STATIC_PAGE_GROUP_ID, COMMON_PAGE_GROUP_ID }