当前位置:   article > 正文

用react实现页面动态表单设计器_react 表单设计器

react 表单设计器

 表单设计器整体思路


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 元素 

  • flex-direction:   column

column 元素:

flex-direction:   row

初始化的时候,只能放row,画布director 方向为column

 (整个布局为,左边设置工具栏,中间是画布,右边是属性栏目)

初始化的时候,只留一个div,这个div不可删除

  1. <Button type="primary" shape="circle"
  2. style={{display: node.id=="1"? "none": "" }}
  3. size="smaill" onClick={()=>removeNode(_id)}>
  4. D
  5. </Button>

工具栏:


     工具栏布局后期可能需要改进,按grid 或者flex进行修正?

  1. <div style={ tool_area }>
  2. <PageHeader
  3. className="site-page-header"
  4. title="工具"
  5. />
  6. <div style={textAlign}>
  7. <IconFont id="rowIcon" type="icon-rows" onDragStart={onDrop} draggable="true"></IconFont>
  8. </div>
  9. <div style={textAlign}>
  10. <IconFont id="colIcon" type="icon-column" onDragStart={onDrop} draggable="true"></IconFont>
  11. </div>
  12. <div style={textAlign}>
  13. <IconFont id="input" type="icon-input"></IconFont>
  14. </div>
  15. <div style={textAlign}>
  16. <IconFont id="input1" type="icon-input1"></IconFont>
  17. </div>
  18. </div>
拖放工具栏的图标,放在目标元素上的时候:
  1. const drop=(event)=>
  2. {
  3. let _id=event.currentTarget.id
  4. // 根据id找到树种的节点
  5. //给这个节点赋予新的子节点
  6. let newLayoutTree = deepCopy(layoutTree);
  7. let n=queryNode(newLayoutTree,_id);
  8. if(n==null || n==undefined){
  9. return;
  10. }
  11. if(n.layoutType==null || n.layoutType==undefined){
  12. return;
  13. }
  14. if(!(n.layoutType==1 || n.layoutType==2)){
  15. return;
  16. }
  17. let newNode=null;
  18. let _nodeId = newGuid();
  19. if(n.layoutType==1){
  20. newNode = new LayoutTreeNode(_nodeId,_nodeId,n.id,_nodeId,2,1,[]); //布局树
  21. }else if(n.layoutType==2){
  22. newNode = new LayoutTreeNode(_nodeId,_nodeId,n.id,_nodeId,1,1,[]); //布局树
  23. }
  24. n.children.push({...newNode});
  25. //设置
  26. console.log(newLayoutTree)
  27. setLayoutTree(newLayoutTree);
  28. stopBubbling(event)
  29. }

      这里需要注意的是,必须对原来的存放在state中的数据。进行deep copy,否则,render的时候,感知不到数据的变化。删除元素的时候,也面临似的问题,同样需要deep copy。

完整代码如下:

  1. //动态表单设计器
  2. import React, {useState} from 'react';
  3. import {Button, Form, Input, message, PageHeader} from 'antd';
  4. import {createFromIconfontCN} from '@ant-design/icons';
  5. const ROW_TYPE=1; //行类型
  6. const COL_TYPE=2; //列类型
  7. //export default class Dynamic_Form_Designer2 extends Component {
  8. export default function Dynamic_Form_Designer2(prop) {
  9. const [form] = Form.useForm();
  10. /**flexGrow :flex grow 值**/
  11. const getRowLayoutStyle=(flexGrow)=>{
  12. return {flex: flexGrow+" 1 auto",
  13. // border: "1px solid #cccccc",
  14. paddingTop:"1em",paddingLeft:"0.3em",paddingRight:"1em",paddingBottom:"1em",
  15. margin:"0.5em",backgroundColor:"RGB(172,216,230)",boxSizing: "border-box",
  16. display:"flex",
  17. flexDirection:"row"
  18. }
  19. }
  20. const getColLayoutStyle=(flexGrow)=>{
  21. return {
  22. // border: "1px solid #cccccc",
  23. flex: flexGrow+" 1 auto",
  24. paddingTop:"1em",paddingLeft:"0.3em",paddingRight:"1em",paddingBottom:"1.5em",
  25. margin:"0.3em",backgroundColor:"RGB(135,206,235)",
  26. display: "flex",
  27. flexDirection:"column"
  28. }
  29. }
  30. const flexContentStyle={
  31. display: "flex",
  32. flexFlow:"column nowrap",
  33. border: "1px solid #cccccc",
  34. padding:"1em"
  35. }
  36. const titleStyle={
  37. height:"30px",
  38. width:"100%",
  39. background: "#999999"
  40. }
  41. const rowTool={
  42. display: "flex",
  43. flexDirection: "column",
  44. boxSizing: "border-box",
  45. border: "2px solid #999999"
  46. }
  47. const colTool={
  48. display: "flex",
  49. flexDirection: "row",
  50. boxSizing: "border-box",
  51. border: "2px solid #999999",
  52. justifyContent:"flex-end"
  53. }
  54. let _children=[];
  55. //id,key,parentId,name,layoutType,flexGrow,children,colSpan,showVale
  56. let layoutTreeNood1 = new LayoutTreeNode("1",1,"0","row1",1,3,[]); //布局树
  57. let layoutTreeNood2 = new LayoutTreeNode("2",2,"0","row2",1,1,[]); //布局树
  58. let layoutTreeNood3 = new LayoutTreeNode("3",3,"0","row3",1,2,[]); //布局树
  59. let layoutTreeRoot = new LayoutTreeNode("0",0,"-1","root",0,1,_children); //布局树
  60. _children.push(layoutTreeNood1);
  61. _children.push(layoutTreeNood2);
  62. _children.push(layoutTreeNood3);
  63. // id,key,parentId,name,layoutType,flexGrow,children
  64. let layoutTreeNood1_col1 = new LayoutTreeNode("11",11,"1","col11",2,1,[],6); //布局树
  65. let layoutTreeNood1_col2 = new LayoutTreeNode("12",12,"1","col12",2,5,[],6); //布局树
  66. layoutTreeNood1.getChildren().push(layoutTreeNood1_col1);
  67. layoutTreeNood1.getChildren().push(layoutTreeNood1_col2);
  68. //
  69. let layoutTreeNood1col2row1 = new LayoutTreeNode("111",111,"11","ttt",1,1,[]);
  70. let layoutTreeNood1col2row2 = new LayoutTreeNode("112",112,"11","ttt2",1,1,[]);
  71. layoutTreeNood1_col1.getChildren().push(layoutTreeNood1col2row1);
  72. layoutTreeNood1_col1.getChildren().push(layoutTreeNood1col2row2);
  73. const [layoutTree, setLayoutTree] = useState(layoutTreeRoot);
  74. const [flexGrowVal, setFlexGrowVal] = useState(1);
  75. const [flexShrinkVal, setFlexShrinkVal] = useState(1);
  76. const [flexBasisVal, setFlexBasisVal] = useState("auto");
  77. const [idVal, setIdVal] = useState("-1");
  78. // 编辑flex
  79. const modifyFlex = (id) => {
  80. //根据info 设置state相关值
  81. //let _id=info.node.id
  82. let _id=id
  83. // 根据id查找节点
  84. let n=queryNode(layoutTree,_id);
  85. console.log("n:"+n)
  86. if(n!=null){
  87. //setItemId(_id);
  88. let _flexGrow=n.flexGrow;
  89. let _flexShrink=n.flexGrow;
  90. let _flexBasis=n.flexGrow;
  91. setFlexGrowVal(_flexGrow);
  92. setFlexShrinkVal(_flexShrink);
  93. setFlexBasisVal(_flexBasis);
  94. setIdVal(_id)
  95. form.setFieldsValue({
  96. "flexGrowItem":_flexGrow,
  97. "flexShrinkItem":_flexShrink,
  98. "flexBasisItem":_flexBasis,
  99. "idItem": _id
  100. })
  101. }else{
  102. message.error("未查询到对应的节点")
  103. }
  104. }
  105. const renderLayout=(nodeObj)=>{
  106. console.log(nodeObj);
  107. let ns=nodeObj.children
  108. let _cells = [];
  109. let i=0
  110. if(ns==null) return '' ;
  111. for(let node of ns){
  112. console.log(node);
  113. if(node.layoutType==1){ //ROW
  114. let _flexGrow= node.flexGrow;
  115. let _id= node.id;
  116. console.log("_flexGrow:"+_flexGrow);
  117. _cells.push(<div id={node.id} style={getRowLayoutStyle(node.flexGrow)} key={i} onDrop={drop} onDragOver={allowDrop}>
  118. <div style={rowTool}>
  119. <Button type="primary" shape="circle" size="smaill"
  120. onClick={()=>modifyFlex(_id)}>
  121. E
  122. </Button>
  123. <Button type="primary" shape="circle"
  124. style={{display: node.id=="1"? "none": "" }}
  125. size="smaill" onClick={()=>removeNode(_id)}>
  126. D
  127. </Button>
  128. </div>
  129. {renderLayout(node)}
  130. </div>)
  131. } else if(node.layoutType==2) { //COL
  132. let _flexGrowForCol= node.flexGrow;
  133. let _idForCol= node.id;
  134. _cells.push(<div id={node.id} key={node.id} style={getColLayoutStyle(node.flexGrow)} onDrop={drop} onDragOver={allowDrop} >
  135. <div style={colTool}>
  136. <Button type="primary" shape="circle" size="smaill"
  137. onClick={()=>modifyFlex(_idForCol)}>
  138. E
  139. </Button>
  140. <Button type="primary" shape="circle" size="smaill" onClick={()=>removeNode(_idForCol)}>
  141. D
  142. </Button>
  143. </div>
  144. {/*<div style={this.titleStyle}>col</div>*/}
  145. {renderLayout(node)}
  146. </div>)
  147. };
  148. i++;
  149. }
  150. return _cells;
  151. }
  152. //设置树形节点属性
  153. const setNodeProperty=(rootNode,currentNode)=>{
  154. let ns=rootNode.children
  155. let _cells = [];
  156. let i=0
  157. if(ns==null) return '' ;
  158. for(let node of ns){
  159. if(currentNode.id==node.id){ //当前节点
  160. //进行相关处理
  161. //node.flexGrow=currentNode.flexGrow
  162. let _g=node.flexGrow=currentNode.flexGrow
  163. node.flexGrow=+_g; //通过+转换为数字
  164. return ;
  165. }else{ //判断是否有子节点? 有则进行递归调用
  166. //判断是否有子节点 如果有子节点 则递归 否则返回
  167. if(node.children==null || node.children.length==0){
  168. continue;
  169. }else{
  170. setNodeProperty(node,currentNode)
  171. }
  172. }
  173. }
  174. return;
  175. }
  176. const allowDrop=(event)=>{
  177. event.preventDefault();
  178. };
  179. //根据id查找node
  180. const queryNode=(root,nodeId)=>{
  181. let nodeObj=null;
  182. // 根节点的情况
  183. if (nodeId=='0'){
  184. return root;
  185. }
  186. for(var currentNode of root.children){
  187. if(nodeId==currentNode.id){ //当前节点
  188. nodeObj= currentNode;
  189. break;
  190. }else{ //判断是否有子节点? 有则进行递归调用
  191. //判断是否有子节点 如果有子节点 则递归 否则返回
  192. if(currentNode.children==null || currentNode.children.length==0){
  193. continue;
  194. }else{
  195. nodeObj=queryNode(currentNode,nodeId)
  196. if(nodeObj!=null) break;
  197. }
  198. }
  199. }
  200. return nodeObj;
  201. }
  202. function deepCopy(obj1) {
  203. let _obj = JSON.stringify(obj1);
  204. let obj2 = JSON.parse(_obj);
  205. return obj2;
  206. }
  207. const IconFont =createFromIconfontCN({
  208. scriptUrl: '/iconfont.js'
  209. })
  210. const myLayout={
  211. flex: "1 1 0%",
  212. boxSizing: "border-box",
  213. display: 'flex',
  214. flexDirection: 'row',
  215. }
  216. const myLayout_1={
  217. backgroundColor: "#cc99sc"
  218. }
  219. // 列布局
  220. const flex_Col={
  221. display: 'flex',
  222. flexDirection: 'row'
  223. }
  224. const tool_area={
  225. flex: "0 0 80px",
  226. boxSizing: "border-box",
  227. border:"2px solid #999999",
  228. margin:"5px",
  229. padding: "2px",
  230. display:"flex",
  231. flexDirection: "column"
  232. }
  233. const content_area={
  234. flex: "1 1 0%",
  235. boxSizing: "border-box",
  236. border:"2px solid #999999",
  237. margin:"5px",
  238. display:"flex",
  239. flexDirection:"column"
  240. }
  241. const property_area={
  242. flex: "0 0 150px",
  243. boxSizing: "border-box"
  244. }
  245. const textAlign={
  246. textAlign:"center"
  247. }
  248. // //删除节点
  249. // 查找本节点 找到parent Node
  250. // 从父节点child集合当中删除子节点
  251. const removeNode=(nodeId)=>{
  252. let rootNodeCopy = deepCopy(layoutTree);
  253. let n=queryNode(rootNodeCopy,nodeId);
  254. let _parentId=n.parentId;
  255. let parentNode=queryNode(rootNodeCopy,_parentId);
  256. parentNode.children.map((item,index)=>{
  257. if(item.id==n.id){
  258. parentNode.children.splice(index,1)
  259. }
  260. })
  261. setLayoutTree(rootNodeCopy)
  262. }
  263. const onDrop = (event) => {
  264. console.log("begin drop:"+event)
  265. // event.event.dataTransfer.setData('memberName', event.node.text);
  266. // event.event.dataTransfer.setData('memberId', event.node.id);
  267. }
  268. const drop=(event)=>
  269. {
  270. let _id=event.currentTarget.id
  271. // 根据id找到树种的节点
  272. //给这个节点赋予新的子节点
  273. let newLayoutTree = deepCopy(layoutTree);
  274. let n=queryNode(newLayoutTree,_id);
  275. if(n==null || n==undefined){
  276. return;
  277. }
  278. if(n.layoutType==null || n.layoutType==undefined){
  279. return;
  280. }
  281. if(!(n.layoutType==1 || n.layoutType==2)){
  282. return;
  283. }
  284. let newNode=null;
  285. let _nodeId = newGuid();
  286. if(n.layoutType==1){
  287. newNode = new LayoutTreeNode(_nodeId,_nodeId,n.id,_nodeId,2,1,[]); //布局树
  288. }else if(n.layoutType==2){
  289. newNode = new LayoutTreeNode(_nodeId,_nodeId,n.id,_nodeId,1,1,[]); //布局树
  290. }
  291. n.children.push({...newNode});
  292. //设置
  293. console.log(newLayoutTree)
  294. setLayoutTree(newLayoutTree);
  295. stopBubbling(event)
  296. }
  297. //stop Bubbling
  298. const stopBubbling =(e)=>
  299. {
  300. if (e && e.stopPropagation){
  301. e.stopPropagation()
  302. }else{
  303. window.event.cancelBubble=true;
  304. }
  305. }
  306. function newGuid(){
  307. var guid = "";
  308. for (var i=1; i<=32; i++){
  309. var n = Math.floor(Math.random()*16.0).toString(16);
  310. guid += n;
  311. if((i==8)||(i==12)||(i==16)||(i==20))
  312. guid += "-";
  313. }
  314. return guid+"";
  315. }
  316. const FlexItemProperty = () => {
  317. const onFinish = (values) => {
  318. console.log('Success:', values);
  319. let _Itemid=values.idItem;
  320. let _flexGrow=values.flexGrowItem;
  321. let _flexShrink=values.flexShrinkItem;
  322. let _flexBasis=values.flexBasisItem;
  323. //找到对应节点 进行设置 根据id 找到所有的已恶点 遍历树 进行设置
  324. if(values.idItem==='-1'){
  325. message.info("请选中相应节点")
  326. return
  327. }
  328. let currentNode=new LayoutTreeNode(_Itemid,-1,-1,"",-1,_flexGrow,[]);
  329. console.log("test0:"+layoutTree)
  330. let rootNodeCopy = deepCopy(layoutTree);
  331. setNodeProperty(rootNodeCopy,currentNode);
  332. console.log("===================================================="+layoutTreeRoot)
  333. //setLayoutTree(layoutTreeRoot);
  334. setLayoutTree(rootNodeCopy);
  335. console.log("test1:"+layoutTree)
  336. };
  337. const onFinishFailed = (errorInfo) => {
  338. console.log('Failed:', errorInfo);
  339. };
  340. const onReset = () => {
  341. form.resetFields();
  342. };
  343. return (
  344. <Form
  345. name="basic"
  346. form={form}
  347. labelCol={{
  348. span: 12,
  349. }}
  350. wrapperCol={{
  351. span: 12,
  352. }}
  353. initialValues={{
  354. idItem:idVal,
  355. flexGrowItem:flexGrowVal,
  356. flexShrinkItem:flexShrinkVal,
  357. flexBasisItem:flexBasisVal
  358. }}
  359. onFinish={onFinish}
  360. onFinishFailed={onFinishFailed}
  361. >
  362. <Form.Item
  363. label="id"
  364. name="idItem"
  365. >
  366. <Input />
  367. </Form.Item>
  368. <Form.Item
  369. label="grow"
  370. name="flexGrowItem"
  371. rules={[
  372. {
  373. required: true,
  374. message: 'Please input the flex-grow!',
  375. },
  376. ]}
  377. >
  378. <Input />
  379. </Form.Item>
  380. <Form.Item
  381. label="Shrink"
  382. name="flexShrinkItem"
  383. rules={[
  384. {
  385. required: true,
  386. message: 'Please input the flex-shrink!',
  387. },
  388. ]}
  389. >
  390. <Input />
  391. </Form.Item>
  392. <Form.Item
  393. label="basis"
  394. name="flexBasisItem"
  395. rules={[
  396. {
  397. required: true,
  398. message: 'Please input flex-basis!',
  399. },
  400. ]}
  401. >
  402. <Input />
  403. </Form.Item>
  404. <Form.Item
  405. wrapperCol={{
  406. offset: 8,
  407. span: 16,
  408. }}
  409. >
  410. <Button type="primary" htmlType="submit">
  411. Submit
  412. </Button>
  413. {/*<Button htmlType="button" onClick={onReset}>*/}
  414. {/*Reset*/}
  415. {/*</Button>*/}
  416. </Form.Item>
  417. </Form>
  418. );
  419. }
  420. return (
  421. <div style={myLayout}>
  422. <div style={ tool_area }>
  423. <PageHeader
  424. className="site-page-header"
  425. title="工具"
  426. />
  427. <div style={textAlign}>
  428. <IconFont id="rowIcon" type="icon-rows" onDragStart={onDrop} draggable="true"></IconFont>
  429. </div>
  430. <div style={textAlign}>
  431. <IconFont id="colIcon" type="icon-column" onDragStart={onDrop} draggable="true"></IconFont>
  432. </div>
  433. <div style={textAlign}>
  434. <IconFont id="rowIcon" type="icon-rows"></IconFont>
  435. </div>
  436. <div style={textAlign}>
  437. <IconFont id="rowIcon" type="icon-rows"></IconFont>
  438. </div>
  439. </div>
  440. <div id = 'rootPanel' style={ content_area } onDrop={drop} onDragOver={allowDrop}>
  441. {renderLayout(layoutTree)}
  442. </div>
  443. <div style={ property_area }>
  444. {FlexItemProperty()}
  445. </div>
  446. </div>
  447. )
  448. }
  449. //type 0 是根 1 是row 2 是col
  450. class LayoutTreeNode{
  451. constructor(id,key,parentId,name,layoutType,flexGrow,children,colSpan,showVale,position)
  452. {
  453. this.id=id; // 数值
  454. this.key=key; // 数值
  455. this.parentId=parentId; // 数值
  456. this.name=name;
  457. this.layoutType=layoutType;
  458. this.children=children; // 用数组的方式保存子节点,适合更多业务场景
  459. //this.flexValue="1 1 auto";
  460. this.flexGrow=flexGrow;
  461. this.colSpan=colSpan;
  462. this.position=position;
  463. }
  464. setId(id){
  465. this.id=id;
  466. }
  467. getId(){
  468. return this.id
  469. }
  470. getKey(){
  471. return this.key
  472. }
  473. getName(){
  474. return this.name
  475. }
  476. getParentId(){
  477. return this.parentId
  478. }
  479. getLayoutType(){
  480. return this.layoutType
  481. }
  482. getChildren(){
  483. return this.children
  484. }
  485. getFlexGrow(){
  486. return this.flexGrow
  487. }
  488. }

属性面板:


点击画布中元素工具图标的时候,右边出现相应的属性面板,我通过路由实现这个功能:

路由的使用: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 中文文档

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元素:
  • 动态文本元素:(针对主表字段)和后端数据绑定
  • table

label: 增加 删除 修改

增加:在pallet中单击label tag,添加label

删除:点击remove 图标

修改:修改属性:

  1. text、
  2. 位置信息 
  3. 宽高信息

动态文本元素:绑定数据源、字段

修改属性方面 ,要指定数据字段(最好采取下拉框选择?需要实现)

Table来说,

table层上,要做什么:

  1. 删除行列
  2.  追加行列
  3. 插入行列
  4.  调整列宽、行高度(ok)

table加标尺:调整列宽、行高度

追加列的实现:

遍历header、detail、footer三个区域,追加最后一列的元素

追加行:

分区域 ,坐标参考系:row插入,

行坐标系+append;

插入一个整行  就是行数

列只要遍历行既可

如果是行头,行头追加 则把后面的明细区 表脚区单元格的startrow+1, endrow+1

如果是明细区,行头追加 则把后面的叶脚区 表脚区单元格 的startrow+1, endrow+1

如果是表脚区,则追 startrow+1, endrow+1

准备数据:

追加:

保存:

追加行:

  1. // 表头有几行 表明细有几行 表脚有几行
  2. let _headerRowHeightArray= theTable.rowHeights.slice(0,theTable.headerRowNum-1);
  3. let _detailRowHeightArray= theTable.rowHeights.slice(theTable.headerRowNum,
  4. theTable.headerRowNum+theTable.detailRowNum-1);
  5. let _footerRowHeightArray= theTable.rowHeights.slice(theTable.headerRowNum+theTable.detailRowNum,
  6. theTable.rowHeights.length-1);

测试:

ps:Loadrunner11--输入license后提示违反许可证安全,禁止操作


 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小桥流水78/article/detail/864520
推荐阅读
相关标签
  

闽ICP备14008679号