在阿里低代码DEMO上扩展多页面管理插件
效果如下: 能够进行页面的添加、编辑和删除
页面结构是根据当前选择的系统查询并处理的,使用了消息订阅与发布pubsub 来监听系统的改变。
系统管理请看这一篇: https://blog.csdn.net/qq_45868390/article/details/125539342
(1)新建一个react文件
import React, { Component } from 'react'
import { Nav, Button, Dialog, Message } from '@alifd/next';
const { Item, SubNav } = Nav;
import { nanoid } from "nanoid";
import {
project,
config,
} from '@alilc/lowcode-engine';
// 网络请求
import { getPage, addPage, updatePage, delPage } from 'src/network/data'
import PMForm from './child-components/PM-form';
import { delPropInObject, getEmptySchema, getPackages } from 'src/universal/utils'
import _, { find } from 'lodash';
import PubSub from 'pubsub-js'
import './index.scss'
export default class PageManage extends Component {
token = null
token1 = null
state = {
isNoSystem: false, // 是否没有选择系统
isDisabledAddBtn: false, // 是否禁用添加按钮
isDisabledDelBtn: true,
currentPageId: undefined, // 当前选中的page的id
currentPagePId: 0, // 当前选中的page的pid,默认为根节点 0
page: [], // 页面列表(处理过层级结构后后)
originPage: [], // 页面列表,未处理层级结构
currentSchema: null, // 当前刚打开的schema
isShowDialog: false, // 是否显示添加页面弹框
footerActions: [], // 消除页面自带默认按钮
formData: {}, // 当前的页面数据
isAdd: undefined, // 是否为新增页面
}
componentDidMount() {
console.log(process.env.NODE_ENV, 'NODE_ENV');
console.log(process.env, 'NODE_ENV');
// 判断当前是否已经全局选择了系统
// 若选择,直接拿页面数据,
// 若不做该处的处理,在关闭插件面板,或者固定左侧面板时,状态会刷新,页面会丢失
this.judgeIsChooseSystem()
// 订阅系统变化消息
this.token = PubSub.subscribe('currentSystem', (_: any, data: any) => {
console.log("当前的系统是", data);
console.log(process.env.NODE_ENV, 'NODE_ENV', process.env.REACT_APP_ENV);
this.setState({
isNoSystem: false,
currentPageId: undefined,
currentPagePId: 0,
currentSchema: null
})
// 查询接口
// 拿到该系统对应的page数据处理后赋值给state
this.getPageAndSchemaData()
})
// 订阅页面修改消息
this.token1 = PubSub.subscribe('updatePage', (_: any) => {
this.getPageAndSchemaData()
// 注意要重新设置当前的schema,否则切换时仍然会弹出修改提示
this.setState({
currentSchema: project.exportSchema().componentsTree[0],
})
})
// 默认第一次加载选择第一个页面
setTimeout(()=>{
// this.clickFirstNodeAfterFirstLoadedOrDel()
},200)
}
componentWillUnmount() {
PubSub.unsubscribe(this.token)
PubSub.unsubscribe(this.token1)
}
// 判断是否选择了系统
judgeIsChooseSystem = () => {
let currentSystem = config.get('currentSystem')
if (currentSystem !== undefined) {
this.setState({
isNoSystem: false
})
this.getPageAndSchemaData()
} else {
this.setState({
isNoSystem: true
})
}
console.log(currentSystem, 'config');
}
// 查询系统对应的页面
getPageAndSchemaData = () => {
// 网络请求
let params = {
page: 1,
pageNum: 10,
param: {
system_id: config.get('currentSystem').systemId
}
}
getPage(params).then(res => {
res.data.list.reverse()
// console.log("查询的页面", res.data.list.reverse());
this.setState({
originPage: JSON.parse(JSON.stringify(res.data.list))
})
this.dealData(JSON.parse(JSON.stringify(res.data.list)))
}).catch(err => {
})
}
// 处理数据
dealData = (data: Array<object>) => {
console.log("传入的", data);
// let newData: Array<object> = []
data.forEach((item: any) => {
let children: Array<object> = []
item.key = item.row_id
data.forEach((c: any) => {
if (item.row_id == c.parent_id) {
children.push(c)
// 把找到的添加给children,同时要删除该项
let index = data.findIndex((d: any) => {
return c.row_id === d.row_id
})
data.splice(index, 1)
}
})
if (children.length > 0) {
item['children'] = children
}
// newData.push(item)
})
console.log("处理过后的页面结构", data);
if (data.length == 0) this.setState({ isDisabledDelBtn: true })
this.setState({
page: data
})
}
// 切换页面
handleSelect = async (selectedKeys: any, item: any) => {
// const packages = await getPackages()
// console.log(packages, '..');
// let currentSchema = delPropInObject(project.exportSchema().componentsTree[0],'docId')
// 判断页面是否修改
this.pageSchemaChangeWillTip(item).then(res => {
// 改变
// console.log('res', res);
// 调用接口修改
this.handleUpdatePage().then(res => {
Message.success('保存成功!')
console.log(res, '修改成功!');
this.switchPageSchema(item)
this.getPageAndSchemaData()
this.setState({
currentPageId: item.props.row_id,
currentPagePId: item.props.parent_id == 0 ? item.props.row_id : item.props.parent_id,
isDisabledDelBtn: false, // 解禁删除按钮
})
// config.set('currentPageId',item.props.row_id)
}).catch(err => {
Message.error('修改失败!')
})
}).catch(err => {
this.switchPageSchema(item)
// 没改变
this.setState({
currentPageId: item.props.row_id,
currentPagePId: item.props.parent_id == 0 ? item.props.row_id : item.props.parent_id,
isDisabledDelBtn: false, // 解禁删除按钮
})
})
// 设置当前页面的id
config.set('currentPageId', item.props.row_id)
// 处理
};
// 切换不同的页面schema
switchPageSchema = (page: any) => {
project.removeDocument(project.currentDocument as any)
let info = this.state.originPage.find((item: any) => {
return item.row_id === page.props.row_id
}) as any
// console.log(info,'查到的');
project.openDocument(JSON.parse(info.schema_info).schema)
// 保存当前打开的schema
// 需要使用低代码的api进行导出,在加载schema时,会给组件加上docId等属性
// 若不使用api,会导致判断是否修改出问题,一直是已修改状态
this.setState({
currentSchema: project.exportSchema().componentsTree[0],
formData: {
name: page.props.name,
parent_id: page.props.parent_id,
icon: page.props.icon_
}
})
}
// 对比页面内容是否改变并提示是否保存
pageSchemaChangeWillTip = (item: any) => {
let currentSchema = project.exportSchema().componentsTree[0]
console.log("当前的schema", currentSchema);
console.log("原来的schema", this.state.currentSchema);
// 使用lodash工具类
// let isSame = _.isEqual(currentSchema, this.state.currentSchema)
let isSame = JSON.stringify(currentSchema) == JSON.stringify(this.state.currentSchema)
console.log('是否一样', isSame);
return new Promise((resolve, reject) => {
// 切换页面前判断是否已经选择了某个页面
// 若选了,且改变内容了,切换给提示
// 若没有选择任何的页面,则直接打开schema
if (this.state.currentPageId !== undefined && !isSame) {
Dialog.confirm({
v2: true,
title: "温馨提示",
className: 'is-save-schame',
content: '内容已修改,是否要保存该页面?',
onOk: () => {
// 调用保存接口
// 删除当前的文档
// 再根据schema加载新的文档
resolve(true)
// this.switchPageSchema(item)
},
onCancel: () => {
// this.switchPageSchema(item)
console.log('不保存!');
reject(false)
},
onClose: () => {
// this.switchPageSchema(item)
reject(false)
console.log('不保存!');
}
})
} else {
reject(false)
// this.switchPageSchema(item)
}
})
}
// 显示添加页面
handleShowAddPage = async () => {
// let news = await getEmptySchema()
// console.log('新的schema', news);
this.setState({
isShowDialog: true,
isAdd: true
})
}
// 添加新页面
handleAddPage = async (data: object) => {
const { componentsMap, componentsTree } = await getEmptySchema()
let params = {
...data,
system_id: config.get('currentSystem').systemId,
schema_info: JSON.stringify({
componentsMap: componentsMap,
schema: componentsTree[0]
})
}
addPage(params).then(res => {
console.log(res, '添加成功!');
this.getPageAndSchemaData()
// 强制渲染组件
// this.forceUpdate();
Message.success("添加成功!");
this.setState({ isShowDialog: false })
}).catch(err => {
Message.error(err);
})
}
// 显示修改页面
handleModifyPage = () => {
this.setState({
isShowDialog: true,
isAdd: false
})
}
// 删除页面
handleDelPage = () => {
Dialog.confirm({
v2: true,
title: "温馨提示",
className: 'del-page',
content: '确定要删除该页面?',
onOk: () => {
// 调用保存接口
// 删除当前的文档
// 再根据schema加载新的文档
let params = {
row_id: this.state.currentPageId
}
delPage(params).then(res => {
Message.success("删除成功!");
this.getPageAndSchemaData()
// 强制渲染组件
// this.forceUpdate();
this.setState({
currentPageId: undefined,
currentPagePId: 0,
isDisabledDelBtn: true, // 禁用删除按钮
})
this.clickFirstNodeAfterFirstLoadedOrDel()
}).catch(err => {
Message.error(err);
})
},
onCancel: () => {
console.log('不保存!');
},
onClose: () => {
console.log('不保存!');
}
})
}
// 封装修改页面内容 返回promise
handleUpdatePage = (data: object = {}) => {
const { componentsMap, componentsTree } = project.exportSchema()
let schema = {
componentsMap: componentsMap,
schema: componentsTree[0]
}
let params = {
data: {
...data,
schema_info: JSON.stringify(schema)
},
row_id: this.state.currentPageId
}
return updatePage(params)
}
// 修改页面
handleUpdatePageInfo = (data: object) => {
this.handleUpdatePage(data).then(res => {
console.log(res, '修改成功!');
this.getPageAndSchemaData()
// 强制渲染组件
// this.forceUpdate();
Message.success("修改成功!");
this.setState({ isShowDialog: false })
}).catch(err => {
Message.error(err);
})
}
// 关闭弹框
onClose = (e: any) => {
console.log(e);
this.setState({ isShowDialog: false })
};
// 刪除节点后默认选中第一个
clickFirstNodeAfterFirstLoadedOrDel = () => {
let elementNode = document.getElementsByClassName('next-nav-item')
console.log(elementNode, '节点');
elementNode[0].click()
}
render() {
const { page, formData, isAdd, currentPagePId, isDisabledAddBtn, isDisabledDelBtn, isNoSystem, footerActions, isShowDialog } = this.state
// const pageCopy = JSON.parse(JSON.stringify(page))
return (
<div className="page-manage">
{
isNoSystem ? (
<div className="no-system">
<p >请先选择系统!</p>
</div>
) : null
}
<div className="operation-page" style={{ display: isNoSystem ? 'none' : 'flex' }}>
<Button className='btn' type="secondary" onClick={this.handleShowAddPage} disabled={isDisabledAddBtn}>添加页面</Button>
<Button className='btn' type="secondary" onClick={this.handleModifyPage} disabled={isDisabledDelBtn}>编辑页面</Button>
<Button className='btn' type="secondary" onClick={this.handleDelPage} disabled={isDisabledDelBtn}>删除页面</Button>
</div>
<Nav type='line' mode='inline' onSelect={this.handleSelect} defaultOpenAll>
{/* <SubNav label={page.name}> */}
{
page.map((page: any) => {
if (page.children && page.children.length !== 0) {
return (
<SubNav icon={page.icon} label={page.name} key={page.key}>
{
page.children.map((c: any) =>
// Item本身就有 icon属性,需要再声明一个自定义的
// 才能在之后拿到该属性
<Item
icon={c.icon}
icon_={c.icon}
row_id={c.row_id}
parent_id={c.parent_id}
name={c.name}
key={c.key}
>
{c.name}
</Item>)
}
</SubNav>
)
} else {
return (
<Item
icon={page.icon}
icon_={page.icon}
row_id={page.row_id}
parent_id={page.parent_id}
name={page.name}
key={page.key}
>
{page.name}
</Item>
)
}
})
}
{/* </SubNav> */}
</Nav>
{/* 添加页面弹框 */}
<Dialog
className='page-manage-dialog'
v2
title={this.state.isAdd ? "添加页面" : "编辑页面"}
visible={isShowDialog}
onClose={this.onClose}
footerActions={footerActions}
>
<div className="page-content">
<PMForm
pageData={page}
pId={currentPagePId}
handleAddPage={this.handleAddPage}
handleUpdatePageInfo={this.handleUpdatePageInfo}
onCancel={this.onClose}
formData={formData}
isAdd={isAdd}
/>
</div>
</Dialog>
</div>
)
}
}
(2)在plugin.tsx文件进行插件的注册。值得注意的是配置项中有个index,内置插件默认都是0,-1就可以排序在最上面,具体看个人需求。