赞
踩
1、从数据局出发
2、从表单出发
3、拖动布局
grid布局还是flex布局实现呢?grid布局整体上更明快,flex方便一步步推进,2种方式也许都实现一遍,进行比对后再下结论。
前端形成模板后 怎么保存
保存整个模板?还是把结构分解描述为一个个字段保存到数据库?
从表单出发的页面设计器
数据可以和表单元素绑定:
(元数据来源:)
既然需要和数据元素绑定,必须有一套元数据描述体系作为支撑。
元数据描述怎么来:
1、手工维护(不动态生成数据库table)
2、反射取得数据库表信息(能维护吗)
拖动到右边画布的动作?
可能涉及如下操作dom方法?
1、html5 antd
2、react 操作dom
3、js 操作dom
4、es6 操作dom
5、react jquery 混用(最好不这样)
用flex布局,Row的主轴设成列 Col的主轴设置成行?
行列交叉进行
整个布局为,左边设置工具栏,中间是画布,右边是属性栏目
整个动态布局背后是个树形结构,render方法,即使根据这个结构形成
全页面采取flex布局,层层嵌套(行、列交互嵌套)
每一个元素都是flex: 行元素必然放在列元素当中
列元素 必然放到行元素当中
每个元素都是flex元素 flex display
row 元素
column 元素:
flex-direction: row
初始化的时候,只能放row,画布director 方向为column
(整个布局为,左边设置工具栏,中间是画布,右边是属性栏目)
初始化的时候,只留一个div,这个div不可删除
- <Button type="primary" shape="circle"
- style={{display: node.id=="1"? "none": "" }}
- size="smaill" onClick={()=>removeNode(_id)}>
- D
- </Button>
工具栏布局后期可能需要改进,按grid 或者flex进行修正?
- <div style={ tool_area }>
- <PageHeader
- className="site-page-header"
- title="工具"
- />
- <div style={textAlign}>
-
-
- <IconFont id="rowIcon" type="icon-rows" onDragStart={onDrop} draggable="true"></IconFont>
- </div>
-
- <div style={textAlign}>
- <IconFont id="colIcon" type="icon-column" onDragStart={onDrop} draggable="true"></IconFont>
- </div>
-
-
- <div style={textAlign}>
- <IconFont id="input" type="icon-input"></IconFont>
- </div>
-
-
- <div style={textAlign}>
- <IconFont id="input1" type="icon-input1"></IconFont>
- </div>
- </div>

拖放工具栏的图标,放在目标元素上的时候:
- const drop=(event)=>
- {
- let _id=event.currentTarget.id
- // 根据id找到树种的节点
- //给这个节点赋予新的子节点
- let newLayoutTree = deepCopy(layoutTree);
- let n=queryNode(newLayoutTree,_id);
- if(n==null || n==undefined){
- return;
- }
- if(n.layoutType==null || n.layoutType==undefined){
- return;
- }
- if(!(n.layoutType==1 || n.layoutType==2)){
- return;
- }
- let newNode=null;
- let _nodeId = newGuid();
- if(n.layoutType==1){
- newNode = new LayoutTreeNode(_nodeId,_nodeId,n.id,_nodeId,2,1,[]); //布局树
- }else if(n.layoutType==2){
- newNode = new LayoutTreeNode(_nodeId,_nodeId,n.id,_nodeId,1,1,[]); //布局树
- }
- n.children.push({...newNode});
- //设置
- console.log(newLayoutTree)
- setLayoutTree(newLayoutTree);
- stopBubbling(event)
- }

这里需要注意的是,必须对原来的存放在state中的数据。进行deep copy,否则,render的时候,感知不到数据的变化。删除元素的时候,也面临似的问题,同样需要deep copy。
完整代码如下:
- //动态表单设计器
-
- import React, {useState} from 'react';
- import {Button, Form, Input, message, PageHeader} from 'antd';
- import {createFromIconfontCN} from '@ant-design/icons';
-
- const ROW_TYPE=1; //行类型
- const COL_TYPE=2; //列类型
-
-
-
- //export default class Dynamic_Form_Designer2 extends Component {
- export default function Dynamic_Form_Designer2(prop) {
- const [form] = Form.useForm();
-
-
- /**flexGrow :flex grow 值**/
- const getRowLayoutStyle=(flexGrow)=>{
- return {flex: flexGrow+" 1 auto",
- // border: "1px solid #cccccc",
- paddingTop:"1em",paddingLeft:"0.3em",paddingRight:"1em",paddingBottom:"1em",
- margin:"0.5em",backgroundColor:"RGB(172,216,230)",boxSizing: "border-box",
- display:"flex",
- flexDirection:"row"
- }
- }
-
- const getColLayoutStyle=(flexGrow)=>{
- return {
- // border: "1px solid #cccccc",
- flex: flexGrow+" 1 auto",
- paddingTop:"1em",paddingLeft:"0.3em",paddingRight:"1em",paddingBottom:"1.5em",
- margin:"0.3em",backgroundColor:"RGB(135,206,235)",
- display: "flex",
- flexDirection:"column"
- }
- }
-
- const flexContentStyle={
- display: "flex",
- flexFlow:"column nowrap",
- border: "1px solid #cccccc",
- padding:"1em"
- }
-
- const titleStyle={
- height:"30px",
- width:"100%",
- background: "#999999"
- }
-
- const rowTool={
- display: "flex",
- flexDirection: "column",
- boxSizing: "border-box",
- border: "2px solid #999999"
- }
-
-
- const colTool={
- display: "flex",
- flexDirection: "row",
- boxSizing: "border-box",
- border: "2px solid #999999",
- justifyContent:"flex-end"
- }
-
- let _children=[];
- //id,key,parentId,name,layoutType,flexGrow,children,colSpan,showVale
- let layoutTreeNood1 = new LayoutTreeNode("1",1,"0","row1",1,3,[]); //布局树
- let layoutTreeNood2 = new LayoutTreeNode("2",2,"0","row2",1,1,[]); //布局树
- let layoutTreeNood3 = new LayoutTreeNode("3",3,"0","row3",1,2,[]); //布局树
- let layoutTreeRoot = new LayoutTreeNode("0",0,"-1","root",0,1,_children); //布局树
- _children.push(layoutTreeNood1);
- _children.push(layoutTreeNood2);
- _children.push(layoutTreeNood3);
- // id,key,parentId,name,layoutType,flexGrow,children
- let layoutTreeNood1_col1 = new LayoutTreeNode("11",11,"1","col11",2,1,[],6); //布局树
- let layoutTreeNood1_col2 = new LayoutTreeNode("12",12,"1","col12",2,5,[],6); //布局树
- layoutTreeNood1.getChildren().push(layoutTreeNood1_col1);
- layoutTreeNood1.getChildren().push(layoutTreeNood1_col2);
- //
- let layoutTreeNood1col2row1 = new LayoutTreeNode("111",111,"11","ttt",1,1,[]);
- let layoutTreeNood1col2row2 = new LayoutTreeNode("112",112,"11","ttt2",1,1,[]);
- layoutTreeNood1_col1.getChildren().push(layoutTreeNood1col2row1);
- layoutTreeNood1_col1.getChildren().push(layoutTreeNood1col2row2);
-
- const [layoutTree, setLayoutTree] = useState(layoutTreeRoot);
- const [flexGrowVal, setFlexGrowVal] = useState(1);
- const [flexShrinkVal, setFlexShrinkVal] = useState(1);
- const [flexBasisVal, setFlexBasisVal] = useState("auto");
- const [idVal, setIdVal] = useState("-1");
-
-
- // 编辑flex
- const modifyFlex = (id) => {
- //根据info 设置state相关值
- //let _id=info.node.id
- let _id=id
- // 根据id查找节点
- let n=queryNode(layoutTree,_id);
- console.log("n:"+n)
- if(n!=null){
- //setItemId(_id);
- let _flexGrow=n.flexGrow;
- let _flexShrink=n.flexGrow;
- let _flexBasis=n.flexGrow;
- setFlexGrowVal(_flexGrow);
- setFlexShrinkVal(_flexShrink);
- setFlexBasisVal(_flexBasis);
- setIdVal(_id)
- form.setFieldsValue({
- "flexGrowItem":_flexGrow,
- "flexShrinkItem":_flexShrink,
- "flexBasisItem":_flexBasis,
- "idItem": _id
- })
- }else{
- message.error("未查询到对应的节点")
- }
- }
-
- const renderLayout=(nodeObj)=>{
- console.log(nodeObj);
- let ns=nodeObj.children
- let _cells = [];
- let i=0
- if(ns==null) return '' ;
- for(let node of ns){
- console.log(node);
- if(node.layoutType==1){ //ROW
-
- let _flexGrow= node.flexGrow;
- let _id= node.id;
- console.log("_flexGrow:"+_flexGrow);
- _cells.push(<div id={node.id} style={getRowLayoutStyle(node.flexGrow)} key={i} onDrop={drop} onDragOver={allowDrop}>
- <div style={rowTool}>
- <Button type="primary" shape="circle" size="smaill"
- onClick={()=>modifyFlex(_id)}>
- E
- </Button>
- <Button type="primary" shape="circle"
- style={{display: node.id=="1"? "none": "" }}
- size="smaill" onClick={()=>removeNode(_id)}>
- D
- </Button>
- </div>
- {renderLayout(node)}
- </div>)
-
- } else if(node.layoutType==2) { //COL
- let _flexGrowForCol= node.flexGrow;
- let _idForCol= node.id;
-
- _cells.push(<div id={node.id} key={node.id} style={getColLayoutStyle(node.flexGrow)} onDrop={drop} onDragOver={allowDrop} >
- <div style={colTool}>
- <Button type="primary" shape="circle" size="smaill"
- onClick={()=>modifyFlex(_idForCol)}>
- E
- </Button>
- <Button type="primary" shape="circle" size="smaill" onClick={()=>removeNode(_idForCol)}>
- D
- </Button>
- </div>
-
- {/*<div style={this.titleStyle}>col</div>*/}
- {renderLayout(node)}
- </div>)
- };
- i++;
- }
- return _cells;
- }
-
- //设置树形节点属性
- const setNodeProperty=(rootNode,currentNode)=>{
-
- let ns=rootNode.children
- let _cells = [];
- let i=0
- if(ns==null) return '' ;
- for(let node of ns){
- if(currentNode.id==node.id){ //当前节点
- //进行相关处理
- //node.flexGrow=currentNode.flexGrow
- let _g=node.flexGrow=currentNode.flexGrow
- node.flexGrow=+_g; //通过+转换为数字
- return ;
- }else{ //判断是否有子节点? 有则进行递归调用
- //判断是否有子节点 如果有子节点 则递归 否则返回
- if(node.children==null || node.children.length==0){
- continue;
- }else{
- setNodeProperty(node,currentNode)
- }
- }
- }
- return;
- }
-
- const allowDrop=(event)=>{
- event.preventDefault();
- };
- //根据id查找node
- const queryNode=(root,nodeId)=>{
- let nodeObj=null;
- // 根节点的情况
- if (nodeId=='0'){
- return root;
- }
- for(var currentNode of root.children){
- if(nodeId==currentNode.id){ //当前节点
- nodeObj= currentNode;
- break;
- }else{ //判断是否有子节点? 有则进行递归调用
- //判断是否有子节点 如果有子节点 则递归 否则返回
- if(currentNode.children==null || currentNode.children.length==0){
- continue;
- }else{
- nodeObj=queryNode(currentNode,nodeId)
- if(nodeObj!=null) break;
- }
- }
- }
- return nodeObj;
- }
-
- function deepCopy(obj1) {
- let _obj = JSON.stringify(obj1);
- let obj2 = JSON.parse(_obj);
- return obj2;
- }
-
- const IconFont =createFromIconfontCN({
- scriptUrl: '/iconfont.js'
- })
-
- const myLayout={
- flex: "1 1 0%",
- boxSizing: "border-box",
- display: 'flex',
- flexDirection: 'row',
- }
-
- const myLayout_1={
- backgroundColor: "#cc99sc"
- }
-
-
- // 列布局
- const flex_Col={
- display: 'flex',
- flexDirection: 'row'
- }
-
- const tool_area={
- flex: "0 0 80px",
- boxSizing: "border-box",
- border:"2px solid #999999",
- margin:"5px",
- padding: "2px",
- display:"flex",
- flexDirection: "column"
- }
-
- const content_area={
- flex: "1 1 0%",
- boxSizing: "border-box",
- border:"2px solid #999999",
- margin:"5px",
- display:"flex",
- flexDirection:"column"
- }
-
- const property_area={
- flex: "0 0 150px",
-
- boxSizing: "border-box"
- }
-
- const textAlign={
- textAlign:"center"
- }
- // //删除节点
- // 查找本节点 找到parent Node
- // 从父节点child集合当中删除子节点
- const removeNode=(nodeId)=>{
- let rootNodeCopy = deepCopy(layoutTree);
-
- let n=queryNode(rootNodeCopy,nodeId);
- let _parentId=n.parentId;
- let parentNode=queryNode(rootNodeCopy,_parentId);
- parentNode.children.map((item,index)=>{
- if(item.id==n.id){
- parentNode.children.splice(index,1)
- }
- })
- setLayoutTree(rootNodeCopy)
- }
-
- const onDrop = (event) => {
- console.log("begin drop:"+event)
- // event.event.dataTransfer.setData('memberName', event.node.text);
- // event.event.dataTransfer.setData('memberId', event.node.id);
- }
-
-
- const drop=(event)=>
- {
- let _id=event.currentTarget.id
- // 根据id找到树种的节点
- //给这个节点赋予新的子节点
- let newLayoutTree = deepCopy(layoutTree);
- let n=queryNode(newLayoutTree,_id);
- if(n==null || n==undefined){
- return;
- }
- if(n.layoutType==null || n.layoutType==undefined){
- return;
- }
- if(!(n.layoutType==1 || n.layoutType==2)){
- return;
- }
- let newNode=null;
- let _nodeId = newGuid();
- if(n.layoutType==1){
- newNode = new LayoutTreeNode(_nodeId,_nodeId,n.id,_nodeId,2,1,[]); //布局树
- }else if(n.layoutType==2){
- newNode = new LayoutTreeNode(_nodeId,_nodeId,n.id,_nodeId,1,1,[]); //布局树
- }
- n.children.push({...newNode});
- //设置
- console.log(newLayoutTree)
- setLayoutTree(newLayoutTree);
- stopBubbling(event)
- }
-
-
-
-
- //stop Bubbling
- const stopBubbling =(e)=>
- {
- if (e && e.stopPropagation){
- e.stopPropagation()
- }else{
- window.event.cancelBubble=true;
- }
- }
-
-
- function newGuid(){
- var guid = "";
- for (var i=1; i<=32; i++){
- var n = Math.floor(Math.random()*16.0).toString(16);
- guid += n;
- if((i==8)||(i==12)||(i==16)||(i==20))
- guid += "-";
- }
- return guid+"";
- }
-
- const FlexItemProperty = () => {
-
- const onFinish = (values) => {
- console.log('Success:', values);
- let _Itemid=values.idItem;
- let _flexGrow=values.flexGrowItem;
- let _flexShrink=values.flexShrinkItem;
- let _flexBasis=values.flexBasisItem;
- //找到对应节点 进行设置 根据id 找到所有的已恶点 遍历树 进行设置
- if(values.idItem==='-1'){
- message.info("请选中相应节点")
- return
- }
- let currentNode=new LayoutTreeNode(_Itemid,-1,-1,"",-1,_flexGrow,[]);
- console.log("test0:"+layoutTree)
- let rootNodeCopy = deepCopy(layoutTree);
- setNodeProperty(rootNodeCopy,currentNode);
- console.log("===================================================="+layoutTreeRoot)
- //setLayoutTree(layoutTreeRoot);
- setLayoutTree(rootNodeCopy);
- console.log("test1:"+layoutTree)
- };
-
- const onFinishFailed = (errorInfo) => {
- console.log('Failed:', errorInfo);
- };
-
-
-
- const onReset = () => {
- form.resetFields();
- };
-
- return (
- <Form
- name="basic"
- form={form}
- labelCol={{
- span: 12,
- }}
- wrapperCol={{
- span: 12,
- }}
- initialValues={{
- idItem:idVal,
- flexGrowItem:flexGrowVal,
- flexShrinkItem:flexShrinkVal,
- flexBasisItem:flexBasisVal
- }}
- onFinish={onFinish}
- onFinishFailed={onFinishFailed}
- >
-
- <Form.Item
- label="id"
- name="idItem"
- >
- <Input />
- </Form.Item>
-
- <Form.Item
- label="grow"
- name="flexGrowItem"
- rules={[
- {
- required: true,
- message: 'Please input the flex-grow!',
- },
- ]}
- >
- <Input />
- </Form.Item>
-
-
- <Form.Item
- label="Shrink"
- name="flexShrinkItem"
- rules={[
- {
- required: true,
- message: 'Please input the flex-shrink!',
- },
- ]}
- >
- <Input />
- </Form.Item>
-
- <Form.Item
- label="basis"
- name="flexBasisItem"
- rules={[
- {
- required: true,
- message: 'Please input flex-basis!',
- },
- ]}
- >
- <Input />
- </Form.Item>
-
- <Form.Item
- wrapperCol={{
- offset: 8,
- span: 16,
- }}
- >
- <Button type="primary" htmlType="submit">
- Submit
- </Button>
-
- {/*<Button htmlType="button" onClick={onReset}>*/}
- {/*Reset*/}
- {/*</Button>*/}
-
- </Form.Item>
- </Form>
- );
- }
-
-
- return (
- <div style={myLayout}>
- <div style={ tool_area }>
- <PageHeader
- className="site-page-header"
- title="工具"
- />
- <div style={textAlign}>
-
-
- <IconFont id="rowIcon" type="icon-rows" onDragStart={onDrop} draggable="true"></IconFont>
- </div>
-
- <div style={textAlign}>
- <IconFont id="colIcon" type="icon-column" onDragStart={onDrop} draggable="true"></IconFont>
- </div>
-
-
- <div style={textAlign}>
- <IconFont id="rowIcon" type="icon-rows"></IconFont>
- </div>
-
-
- <div style={textAlign}>
- <IconFont id="rowIcon" type="icon-rows"></IconFont>
- </div>
- </div>
- <div id = 'rootPanel' style={ content_area } onDrop={drop} onDragOver={allowDrop}>
- {renderLayout(layoutTree)}
- </div>
- <div style={ property_area }>
-
- {FlexItemProperty()}
-
- </div>
- </div>
- )
-
- }
-
- //type 0 是根 1 是row 2 是col
- class LayoutTreeNode{
- constructor(id,key,parentId,name,layoutType,flexGrow,children,colSpan,showVale,position)
- {
- this.id=id; // 数值
- this.key=key; // 数值
- this.parentId=parentId; // 数值
- this.name=name;
- this.layoutType=layoutType;
- this.children=children; // 用数组的方式保存子节点,适合更多业务场景
- //this.flexValue="1 1 auto";
- this.flexGrow=flexGrow;
- this.colSpan=colSpan;
- this.position=position;
- }
-
- setId(id){
- this.id=id;
- }
- getId(){
- return this.id
- }
- getKey(){
- return this.key
- }
- getName(){
- return this.name
- }
- getParentId(){
- return this.parentId
- }
- getLayoutType(){
- return this.layoutType
- }
- getChildren(){
- return this.children
- }
- getFlexGrow(){
- return this.flexGrow
- }
- }
-

点击画布中元素工具图标的时候,右边出现相应的属性面板,我通过路由实现这个功能:
路由的使用:A Complete Beginner's Guide to React Router (Including Router Hooks)
通过路由link获取面板。同时传递点击元素的id
首先传递元素的id及layoutTree, 属性 页面根据元素的属性,初始化页面。
layoutTree如何传递呢,是vuex还是直接通过prop传递呢,考虑到多个页面共用这个,用一下React Redux,是否完全合理,没有深入思考。
ps:React Redux 中文文档 | React Redux 中文文档
React Components:在组件中要“做什么”,把需要做的事情告诉Action Creators(“Action的创建者们”,)行为创建器。
Action Creators:将需要做的事情包装成一个动作对象,可以省去,自己直接创建一个动作对象。action在redux中被称为动作对象(js中的Object一般对象),action包括type:动作类型、data:动作值。
dispatch(分发)为一个函数,生成action动作对象后,需要一个函数将动作对象继续传递下去,否则这个动作就此终止,即deispatch将Action继续“分发”下去,交给Store
Store:为redux的核心,可以理解为一个调用者,负责对全局的掌控。Store本身并不执行操作,也不加工状态,只是一个调用者,Action对象交给Store后不停留,会被Store交给加工者Reducers
Reducers:为redux的实际加工者,可以有多个。Store将previousState和Action对象交给Reducers,previousState表示之前的状态,要在之前的状态上进行action操作。Reducers操作完毕后要向Store返回一个新的状态即newState
getState():组件通过getState()方法拿到操作后的最新的结果,state变化重新渲染组件
注意:状态初始化时没有previousState参数,只会在此位置传递undefined,只有在第二次及之后的加工中才会有
ps:React -- redux详解
ps:调试react,有个浏览器插件,react-devTools,根据github上构建这个插件,安装在chromel浏览器报错:Manifest version 2 is deprecated, and support will be removed in 2023. See https://developer.chrome.com/blog/mv2-transition/ for more details.
好像是新的chromel浏览器不支持react-devTools的某种协议了?
那就用firefox调试react!
前端:(排除字体 对齐 颜色等CSS属性)
第一期出现动态布局三个元素:
label: 增加 删除 修改
增加:在pallet中单击label tag,添加label
删除:点击remove 图标
修改:修改属性:
动态文本元素:绑定数据源、字段
修改属性方面 ,要指定数据字段(最好采取下拉框选择?需要实现)
Table来说,
table层上,要做什么:
table加标尺:调整列宽、行高度
追加列的实现:
遍历header、detail、footer三个区域,追加最后一列的元素
追加行:
分区域 ,坐标参考系:row插入,
行坐标系+append;
插入一个整行 就是行数
列只要遍历行既可
如果是行头,行头追加 则把后面的明细区 表脚区单元格的startrow+1, endrow+1
如果是明细区,行头追加 则把后面的叶脚区 表脚区单元格 的startrow+1, endrow+1
如果是表脚区,则追 startrow+1, endrow+1
准备数据:
追加:
保存:
追加行:
- // 表头有几行 表明细有几行 表脚有几行
- let _headerRowHeightArray= theTable.rowHeights.slice(0,theTable.headerRowNum-1);
- let _detailRowHeightArray= theTable.rowHeights.slice(theTable.headerRowNum,
- theTable.headerRowNum+theTable.detailRowNum-1);
- let _footerRowHeightArray= theTable.rowHeights.slice(theTable.headerRowNum+theTable.detailRowNum,
- theTable.rowHeights.length-1);
测试:
ps:Loadrunner11--输入license后提示违反许可证安全,禁止操作
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。