UNPKG

yonbip-code-gen-mcp

Version:

YonBIP高级版代码生成MCP

598 lines (554 loc) 20.9 kB
# 拉单前端开发步骤 1 引入工具类 2 将片段代码进行整合到你的前端代码中 3 维护对应的页面模板(手工处理) # 拉单前端代码 DEMO ```js import React, { Component } from 'react'; import ReactDOM from 'react-dom'; import {createPage, high, print, output,toast} from 'nc-lightapp-front'; const {NCDiv, NCAffix, NCTabs, NCScrollElement, NCTooltip, NCToggleViewBtn} = base; import {Utils} from './Utils'; const URLS = { // 请求路径 //查询来源单据url querySourceBillUrl: '/nccloud/XXXXX/XXXXX/XXXXXXXAction.do', // 拉单查询"参数审批单"请求路径 //转单URL transferUrl: '/nccloud/XXXXX/XXXXX/XXXXXXAction.do', // 转单请求路径 }; const SHOWMODE_FETCHList = "fetchlist"; // 拉单列表 const SHOWMODE_FETCHCARD = 'fetchcard'; // 拉单卡片 /** * 拉单demo示例 */ class ApplicationPage extends Component { constructor(props) { super(props); this.state = this.createState(); } /** * 创建state */ createState = () => { //基础state let state = { } // 拉单 Utils.apply(state, { toggleViewBtn: { // 主子拉平按钮配置 expand: false, // 默认是否为主子拉平 onClick: () => { // 主子拉平按钮点击处理 this.state.toggleViewBtn.expand = !this.state.toggleViewBtn.expand ; this.setState(this.state, () => this.props.transferTable.changeViewType()); } }, //这个根据你自己的进行定义,可以配置多个拉单,结构类似如下 fetch_DEMO: { // 拉单上游列表配置 srcBilltype: "", // 来源单据类型 queryUrl: URLS.querySourceBillUrl, // 查询请求url headIdName: '', // "上游单据"主表主键名称 bodyIdName: '', // "上游单据"子表主键名称 fetchHead: { // 拉单上游页面头部标题配置 title: 'DEMO标题', // 头部标题 backBtnClick : this.onFetchListBack, // 点击返回 initShowBackBtn: true // 是否显示返回按钮 }, fetchButton: { // 拉单上游页面头部按钮配置 area: 'fetch_table', // 按钮注册中的按钮区域 onButtonClick : (props, key, hotkey, isFromSide) => { // 拉单上游界面按钮点击事件 let fetchConf = this.state.fetch_DEMO; this.onTransferSearch(fetchConf, (data,num) => { // 查询后的回调 toast({color: 'success' , content: '刷新成功'}); }); } }, search: { area: '此编码去模板上找到进行配置', // 查询区组件区域编码 clickSearchBtn: () => { // 点击查询监听事件 let fetchConf = this.state.fetch_DEMO; this.onTransferSearch(fetchConf, (data, num) => { // 查询后的回调 if (num && num > 0) { toast({color: 'success', content: '查询成功,共'+num+'条!'}); } else { toast({color: 'warning', content: '未查询到数据'}); } }); }, onAfterEvent: (key, value) => { // 拉单的查询区编辑后事件处理。 //todo... } }, transferTable: { // 转单表格 dataSource: '自己定义一个key', // 缓存数据的key headTableId: '去模板上找到你注册的对应区域编码', // 表格组件id bodyTableId: '去模板上找到你注册的对应区域编码', // 子表模板id fullTableId: '去模板上找到你注册的对应区域编码', // 主子拉平的id tableType: 'nest', // 表格显示类型 nest:主子表 full:主子拉平 simple:单表 showSeletedBtn: true, // 左下角“查看已选”是否显示 showChangeViewBtn: true, // 显示主子拉平的按钮是否显示 transferBtnText: '转单', // 转单按钮显示文字 containerSelector: '#transferList', // 父组件容器 // 转单按钮监听事件 onTransferBtnClick: () => { let params = { data: Utils.onTransferWrapper({props: this.props, fetchConf: this.state.fetch_1001}), pageCode: this.config.pagecode, formId: '去模板上找到你注册的对应区域编码', formListIds: '去模板上找到你注册的对应区域编码' }; this.onTransfer(params); }, onChangeViewClick: () => { this.state.toggleViewBtn.onClick(); }, componentInitFinished: () => { //缓存数据赋值成功的钩子函数 //若初始化数据后需要对数据做修改,可以在这里处理 } } }, fetchCard: { //拉单卡片界面配置 backMode: "返回的模式编码,这个编码用来操控界面的状态,是你自己进行定义的,这个需要结合你当前编写的代码进行配置", //拉单卡片返回界面 fetchHead: { // 拉单卡片头部配置() title: 'DEMO标题', // 标题 backBtnClick: this.onFetchCardBack // 点击返回 }, fetchButton: { // 按钮区域 area: 'common', // 按钮注册中的按钮区域 onButtonClick: (props, key, hotkey, isFromSide) => this.onBtnClickHead(props,key) // 点击按钮 }, tranlist: { // 转单列表 headcode: '去模板上找到你注册的对应区域编码', // 表头的区域编码 bodycode: [ '去模板上找到你注册的对应区域编码', '去模板上找到你注册的对应区域编码', ], // 表体区域编码 transferListId: '去模板上找到你注册的对应区域编码', // 转单列表 id selectIndex: -1, // 左侧列表选中位置 onTransferItemSelected: Utils.onTransferItemSelectedWrapper(this.onTransferItemSelected) // 转单后卡片左侧缩略图选中点击处理 }, selectIndex: 0 // 左侧列表选中位置 } }); return state; //返回state解构 } /** * 方法功能: * 拉单列表返回 */ onFetchListBack = () => { // 切换 页面模式 this.state.showmode = "你需要切换到的页面"; // 设置不显示标题栏返回按钮 this.state.head.initShowBackBtn = false; this.setState(this.state, () => { //重新查询列表,可以复用已有的查询列表功能,需要自己去看下当前页面是否有,没有的话需要自己写一下 //todo.... }); //路由的方式,可以参考你当前页面的进行开发 } /** * 方法功能: * 点击拉单列表页面的查询 */ onTransferSearch = (param = {}, callback = EMPTY_FN) => { let {queryUrl, headIdName, bodyIdName, search, transferTable} = param; // 解构上游拉单参数 let {headTableId, bodyTableId, tableType} = transferTable; // 获取查询条件 let queryInfo = this.props.search.getQueryInfo(search.area); if (!queryInfo) C // 查询请求 ajax({ url: queryUrl, headers: this.ajaxHeader(), data: { queryTreeFormatVO: queryInfo, // 查询条件 pageCode: this.config.pagecode, formId: headTableId, tableId: [bodyTableId] }, loading: true, success: (res) => { let data = res.data && res.data.data; let num = 0; // 设置拉单列表数据 if (tableType == 'simple') { let fetchListData = data && data[headTableId] || {rows: []}; num = fetchListData.rows.length; this.props.transferTable.setTransferTableValue(headTableId, bodyTableId, fetchListData, headIdName, bodyIdName); } else { num = data ? data.length : 0; // 适配无子表拉单 data && data.forEach(item => { !item.body && (item.body = {[bodyTableId]: {areacode: bodyTableId, rows: []}}) }) this.props.transferTable.setTransferTableValue(headTableId, bodyTableId, data || [], headIdName, bodyIdName); } callback(data, num); } }); } /** * 渲染 */ render() { // 拉单列表渲染方法 const renderFetchListDemo = () => { // 拉单上游列表界面 let {toggleViewBtn,fetch_DEMO} = this.state; // 如果当前显示不是拉单列表界面,则返回空。 if (showmode != SHOWMODE_FETCHList) { return ''; } let { fetchHead, fetchButton, search, transferTable } = fetch_DEMO; let selectedShow = this.props.transferTable.getSelectedListDisplay(transferTable.headTableId); if (selectedShow) { // 查看已选时界面 return ( <div id="transferList" className="nc-bill-list"> <div className="nc-bill-transferTable-area"> {this.props.transferTable.createTransferTable(transferTable)} </div> </div> ); } return ( <div className="nc-bill-list"> {/* 头部 header */} <NCDiv className="nc-bill-header-area" areaCode={NCDiv.config.HEADER}> {/* 标题 title */} <div className="header-title-search-area">{this.props.BillHeadInfo.createBillHeadInfo(fetchHead)}</div> <div className="header-button-area"> {/* 按钮区 btn-group */} {this.props.button.createButtonApp(fetchButton)} {/* 主子拉平的按钮 */} {<NCToggleViewBtn {...toggleViewBtn} />} </div> </NCDiv> {/* 渲染查询区 */} <NCDiv areaCode={NCDiv.config.SEARCH} className="nc-bill-search-area"> {this.props.search.NCCreateSearch(search.area, {...search})} </NCDiv> {/* 渲染列表区 */} <NCDiv className="nc-bill-table-area"> {this.props.transferTable.createTransferTable({...transferTable})} </NCDiv> </div> ); }; //拉单卡片界面渲染 const renderFetchCardDemo = () => { let {fetchCard} = this.state; let {form, formlist, cardPagniation} = card; let {fetchHead, fetchButton, tranlist, selectIndex} = fetchCard; if (showmode != SHOWMODE_FETCHCARD) { return ''; } // cardtable存在bug,拷贝使用的是Object.assign(),会修改当前state let newFormlist = deepClone(formlist); let {'treegridSlave0Sub' : tableConfig, ...other} = newFormlist; return ( <div id="transferCard" className="nc-bill-transferList"> <NCAffix> <NCDiv className='nc-bill-header-area' areaCode={NCDiv.config.HEADER}> <div className='title'> {/*创建头部*/} {this.props.BillHeadInfo.createBillHeadInfo(fetchHead)} </div> <div className="header-button-area"> {/*头部按钮*/} {this.props.button.createButtonApp(fetchButton)} </div> </NCDiv> </NCAffix> <div className="nc-bill-transferList-content"> {this.props.transferTable.createTransferList(tranlist)} <div className="transferList-content-right nc-bill-card"> <div className="nc-bill-form-area"> {/* 主表表单 */} {this.props.form.createForm("表单区域编码,可以复用已有卡片的", "表单区域配置")} </div> <div className="nc-bill-bottom-area"> <NCDiv areaCode={NCDiv.config.TABLE} className="nc-bill-table-area"> {this.props.cardTable.createCardTable(tableConfig.area, {...tableConfig, multiConfig: other})} </NCDiv> </div> </div> </div> </div> ); } return <div> {/* 拉单列表渲染 */} {renderFetchListDemo()} {/* 拉单卡片渲染 */} {renderFetchCardDemo()} </div> } } ``` # 前端工具类 ```js import {toast} from 'nc-lightapp-front'; const EMPTY_FN = function () { }; /** * 对象合并 类似Object.assign(); * @returns 第一个参数对象 */ const apply = function(){ if(!arguments){ throw new ReferenceError("该方法执行时至少传递一个参数对象!"); } //参数的长度 let length = arguments.length; //长度是1 直接返回 if(length == 1){ return arguments[0]; } //源 let target = arguments[0]; //从第二个元素开始 到 最后一个组成sources let sources = [...arguments].slice(1); for(let i = 0;i<sources.length;i++){ //第i个参数 let param = sources[i]; if(!param){ continue; } Object.keys(param).forEach(key=>{ //param原型上有该对象或者属性 if(Object.prototype.hasOwnProperty.call(param,key)){ target[key] = param[key]; } }) } return target; } /** * 加载单据模板 * props: * cfg:{必输项 * pagecode: 必输项 参数模板的参数 * }, * callback(fn): 回调 */ const loadTemplate = function (props, {pagecode,appcode} = cfg) { return new Promise(resolve=>{ props.createUIDom( { pagecode, appcode }, (data) => { resolve({...data}); } ); }) }; /** * 加载多语言 * @param {*} props 必输项 * @param {*} cfg:{ * moduleId: 必输项 【应用编码: 例如 38211902 @attention 这里是应用编码多语文件的名称也要以应用编码命名】 * domainName:必输项 【领域编码:例如 uapbd、fi、gl等等】 * } */ const loadLang = function (props, {moduleId,domainName} = cfg) { return new Promise((resolve)=>{ //请求多语 props.MultiInit.getMultiLang( { moduleId, domainName, callback: (data, success, inlt) => { if (!success){ toast({content: '加载语言包失败', color: 'warning'}); } resolve({lang:data,inlt}); } } ); }) }; /** * 加载参照 多个参照一块加载 * @param {*} urls * 示例:[ * { * url:'uapbd/refer/supplier/SupplierClassTreeRef/index', * name:'uapbd/refer/supplier/SupplierClassTreeRef/index' * }, * { * url:'uapbd/refer/supplier/SupplierClassTreeRef/index', * name:'uapbd/refer/supplier/SupplierClassTreeRef/index' * } * ] * @returns */ const loadRefer = function (urls = []) { if(!urls || urls.length == 0){ return Promise.resolve(true); } let result = {}; //构造 let loads = (urls.filter((refObj,index)=>{ let {url} = refObj; let jsname = url.includes(".")?url.substring(0, url.length - 3):url; if(!window[jsname]){ return refObj; } })||[]).map((refObj)=>{ return new Promise((resolve,reject)=>{ let {url,name} = refObj; let jsname = url.includes(".")?url.substring(0, url.length - 3):url; let script = document.createElement('script'); let suffix = url.includes(".")?'':'.js' script.src = '../../../../' + url+suffix; script.type = 'text/javascript'; script.onload = () => { result[name] = window[jsname].default || EMPTY_FN; resolve(result); }; script.onerror = () => { result[name] = undefined; reject(result); }; document.body.appendChild(script); }) }); //Promise 请求全部参照 return Promise.all(loads); } /** * 请求资源 * @param {*} config * props, 当前应用对象的props * pagecode, 页面编码 * appcode, 应用编码 * moduleId, 建议使用应用编码 * domainName, 领域编码 * referObjs = [], 参照请求路径集合 * callback = EMPTY_FN 回调 */ const loadNCCResource = function (config) { let { props, pagecode, appcode, moduleId, domainName, referObjs = [], callback = EMPTY_FN } = config; //加载参照 loadRefer(referObjs) .then(()=>{ return Promise.all([ //加载模板 loadTemplate(props,{pagecode,appcode}), //加载多语 loadLang(props,{moduleId,domainName}) ]); }).then(res=>{ callback({...res[0],...res[1]}); }) }; const isArray = function(param){ return Object.prototype.toString.call(param).slice(8, -1) === 'Array'; }; const isString = function(param) { return Object.prototype.toString.call(param).slice(8, -1) === 'String'; }; const isObject = function(param){ return Object.prototype.toString.call(param).slice(8, -1) === 'Object'; }; const transferDataWapper = function(datas,grandsonMap){ (isObject(datas)?[datas]:datas || []).forEach((data)=>{ let {head,body,bodys} = data; if(Array.isArray(bodys) && bodys.length){ let bodysWrapper = (bodys || []).reduce((res,b)=>{ let body = Object.values(b); let {areacode} = body[0]; res[areacode] = body[0]; return res; },{}) // head['bodys'] = apply({},bodysWrapper); data['body'] = apply({},(bodys || body || {})); data['bodys'] = apply({},bodysWrapper); }else{ data['body'] = apply({},(bodys || body || {})); } }) grandsonMap && (data['grandsonMap'] = apply({}, grandsonMap)); return datas; }; const onTransferItemSelectedWrapper = function( func ){ const fetchDataUnWapper = function(datas){ ([datas] || []).forEach((data)=>{ let {head,body,bodys} = data; data['body'] = apply({},body || head['bodys'] || bodys); data['bodys'] = apply({},head['bodys'] || bodys); }); debugger; return datas; }; return function(record, isComplete, curActiveIndex,status){ func(fetchDataUnWapper(record), isComplete, curActiveIndex,status); }; }; const onTransferWrapper = function( {props,fetchConf}){ let {srcBilltype,headIdName,bodyIdName,transferTable:{headTableId,bodyTableId}} = fetchConf; let data = props.transferTable.getTransferTableSelectedValue(); let records = (data && data[headTableId] ||[]).map(record=>{ let pk = record[headIdName] ? record[headIdName].value : record.head[headTableId].rows[0].values[headIdName].value; let ts = record["ts"] ? record["ts"].value : record.head[headTableId].rows[0].values["ts"].value; let children = (record.body && bodyTableId && bodyTableId.length>0 && record.body[bodyTableId]) ? (record.body[bodyTableId].rows || []).map(row=>{ return {pk:row.values[bodyIdName].value,ts:row.values["ts"].value} }):[]; return {pk,ts,children}; }); return [{billType:srcBilltype,records}]; }; //float类型数求和 const FloatAdd = function(arg1,arg2){ var r1,r2,m,s1 = arg1.toString(),s2 = arg2.toString(); try{ r1=s1.split(".")[1].length; }catch(e){ r1=0 } try{ r2=s2.split(".")[1].length; }catch(e){ r2=0 } m=Math.pow(10,Math.max(r1,r2)); return (arg1*m+arg2*m)/m; }; //float类型数乘积 const FloatMultiple = function(f1,f2){ var m=0,s1=f1.toString(),s2=f2.toString(); try{ m+=s1.split(".")[1].length; }catch(e){ } try{ m+=s2.split(".")[1].length; }catch(e){ } return Number(s1.replace(".",""))*Number(s2.replace(".",""))/Math.pow(10,m) }; export const Utils = { loadNCCResource, loadTemplate, loadLang, loadRefer, apply, transferDataWapper, onTransferItemSelectedWrapper, onTransferWrapper, FloatAdd, FloatMultiple }; ```