yonbip-code-gen-mcp
Version:
YonBIP高级版代码生成MCP
598 lines (554 loc) • 20.9 kB
Markdown
# 拉单前端开发步骤
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
};
```