数据源知识沉淀

1、下拉框与Lov的注意点:
下拉框

{
	name: 'intertradeType',
	lookupCode: "HCMS.INTERTRADE_TYPE",
	label: intl.get(`${prefix}.table.intertradeType`).d('贸易性质'),
	required: true,
},
{
	name: 'intertradeTypeMeaning',
	label: intl.get(`${prefix}.table.intertradeType`).d('贸易性质'),
},

下拉框 lookupCode,后端需要配置对应的值集。填写时,可直接使用ID intertradeType,但是编辑预览时,无MC字端,但是后端会返回MC字端,需要预览intertradeTypeMeaning字段;—— 如何在修改时,将下拉数据中的meaning赋值给其他字端;
Lov字段

{
	name: 'declareCustomsObj’, —— 不可与后端返回的字段相同,否则,新增保存后,再次修改其他表单,保存后,数据将丢失;
	label: intl.get(`${prefix}.table.declareCustoms`).d('申报海关'),
	lovCode: 'HWMS.CUSTOMS',
	type: FieldType.object,
	ignore: FieldIgnore.always,
},
{
	name: 'declareCustoms', //* 构建提交数据
	bind: 'declareCustomsObj.value' //* 对应lov数据的ID值
},
{
	name: 'declareCustomsMeaning', //* 构建提交数据
	bind: 'declareCustomsObj.meaning'//* 对应lov数据的MC值
},
后端需要配置对应的值集视图配置,且需要注意正确填写【值字段名】(对应lov数据的ID值)、【显示字段名】(对应lov数据的MC值)字段;否则需要前端手动配置textField: 'meaning';
提交时需要ignore这个declareCustomsObj对象,且需要将Lov数据中的值(value)绑定到declareCustoms 来保存ID值,需要将Lov数据中的展示字端(meaning)绑定到declareCustomsMeaning 来保存MC值,以便组织数据、编辑和预览;
  • 填写时,bind会将用户选中的数据(declareCustomsObj)中的value、meaning分别赋值给declareCustoms字端和declareCustomsMeaning字端;
  • 编辑与预览时,bind会将后端数据中declareCustoms字端和declareCustomsMeaning字端 组装为declareCustomsObj,用于页面的渲染。
    2、查询一般都是get请求,查询条件有输入框,当有空格时,会自动以【+】号拼接;
    get 请求参数重空格的处理方式(以+号拼接,后端会自动处理),查询时,query字段会自动拼接,但是如果是手动拼接queryString需要我们自己拼接
const invalidSubmit = async (res?: any, adjustSubmit?: string) => { //* 作废提交
  const _urlParams = urlParams || '?'; //? 接口参数
  if (adjustSubmit) { //* 有变更时间的提交
    const adjustDate = InvalidDs.current?.get('adjustDate');
    const adjustDateStr = adjustDate?.format('YYYY-MM-DD+HH:mm:ss') ?? ''; //* 手动将空格转成【+】,方便后端转换
    tableDS['_urlParams'] += `&adjustDate=${adjustDateStr}`;
  }
  tableDS['_urlParams'] = _urlParams;
  const result = await otherSubmit(tableDS, 'invalid'); //* 进行【作废】提交
  result && closeModal(res);
};

3、非勾选的汇总值(所有页面数据的总和),可编辑的分页表格,当表格有变更汇总字段的数据,然后进行分页时,会提示有变更“有未保存的数据,是否继续?”,点击【确认】会重新查询,但是汇总字段依旧是修改后的数据;要求应该是初始的数据,使用reCompute将计算当前页面的总和,需要在load后,保存汇总值的数据,重新查询后,再次赋值上一次赋值的汇总值

const forceUpdateSummary = (dataSet) => { //* 强制刷新行的汇总字段
    //* zhy added hzrl-0001-8738【采购发货】采购发货行结算数量汇总显示问题
    const parentDataSet = dataSet.parent;
    const summaryLineDS = summaryHeader?.current?.summaryLineDS;
    setTimeout(() => {
      summaryLineDS?.current?.init('40Sum', parentDataSet?.current?.get('totalPackingQuantity_origin') || parentDataSet?.current?.get('totalPackingQuantity'));
      summaryLineDS?.current?.init('numberSum', parentDataSet?.current?.get('totalNumber_origin') || parentDataSet?.current?.get('totalNumber'));
      summaryLineDS?.current?.init('amountSum', parentDataSet?.current?.get('totalAmount_origin') || parentDataSet?.current?.get('totalAmount'));
      parentDataSet?.current?.init('totalNumber', parentDataSet?.current?.get('totalNumber_origin') || parentDataSet?.current?.get('totalNumber'));
      parentDataSet?.current?.init('totalAmount', parentDataSet?.current?.get('totalAmount_origin') || parentDataSet?.current?.get('totalAmount'));
      summaryHeader.current?.forceUpdate({});
    }, 50);
  }

  useDataSetEvent(lineDS, 'load', ({ dataSet }) => {
    const { records = [] } = dataSet;
    records.forEach((res: any, index: number) => {
      // ! 被引用或者被锁货都不可勾选
      if (res.get('isQuote') || res.get('isLock')) {
        const first = dataSet.get(index);
        if (first) first.selectable = false;
      }
    });
    setIsMoreLine(!!records.length);
    if (!dataSet.getState('noNeedForceUpdateSummary')) { //* 需要“强制刷新行的汇总字段”
      forceUpdateSummary(dataSet);
    } //* 删除时已经处理过了,不需要再次“强制刷新行的汇总字段”
    dataSet.setState('noNeedForceUpdateSummary', false);

  });

4、一个Lov值集视图,多选,值字段为Nubmer类型,参数既有string,又有Number[],后端返回给前端的也是string + Number[]类型的两个字段。—— 我们组织数据源的数据时,必须把类型为string的字段放在类型为数组的字段的上面;以保证构建虚拟对象时,值字段为Nubmer类型!!!

{
  name: 'deliveryStartPortObj',
  label: `${intl.get(`cice.cms.in.contract.manage.table.data.deliveryEndPortName`).d('起运港')}`,
  type: 'object' as FieldType,
  ignore: 'always' as FieldIgnore,
  lovCode: 'HMDM.PORT_COUNTRY',
  multiple: true,
  dynamicProps: {
    required({ record }) {
      return record.get('isStartPortRequire') === 1
    }
  }
},
{
  name: 'deliveryStartPort', //* zhy 自动组织字符串数据,且必须放在deliveryStartPortList的上面,保证load后,ptId为Number类型
  bind: 'deliveryStartPortObj.ptId',
  type: 'string' as FieldType,
  multiple: ',',
},
{
  name: 'deliveryStartPortList',
  bind: 'deliveryStartPortObj.ptId'
},

5、数据源dataSet中events的submitSuccess和feedback的submitSuccess的使用:

events: { //* 事件
  submitSuccess: ({ dataSet, data}) => {
    data.firstStep = dataSet.getState('firstStep');
    dataSet.setState('firstStep', false);
  },
},
feedback: { //* 查询和提交数据的反馈配置
  submitSuccess: (res: any) => {
    const content = res && res.content;
      if (content.length && content[0].saveMsg && res.firstStep) { //* 如果后端返回提示语,并且是是第一步骤的保存操作
      notification.success({
        message: intl.get(`cicemon.massage.tip`).d('操作成功!'),
        description: content[0].saveMsg,
      });
    } else {
      notification.success({
        message: intl.get(`cicemon.massage.tip`).d('提示'),
        description: '操作成功!',
      });
    }
  },
},
  • 参数不同,events的可以获取到dataSet,feedback的获取不到;events的data是响应数据(将相应到feedback的数据),feedback的resp 是 响应值;
  • 需要时可以配合使用,先执行events的submitSuccess,后执行feedback的submitSuccess;
  • 在events的submitSuccess执行修改data(响应数据),执行feedback的submitSuccess时可以获取到修改后的响应值;

6、数据源,必录字段的隐藏,一定要设置为隐藏时非必录
7、光改common包可能不会触发编译,需要同时改动引用该common包的文件,才会触发common的再次变异
8、分页查询接口,参数page为空,size为空,后端默认page:0, size: 20,返回20条第一页的数据;
前端若是想查找全量的数据,需要设置page为-1,后端会将size置为总量;—— 返回的格式是一样的
9、useEffect与dataSet的load事件,请求基础配置的注意事项
// 页面初始化函数
useEffect(() => {
initPage(); //* 初始化页面数据
}, [foreignChainId]);
1、useEffect,是在初始化时以及依赖项变更时执行的,当编辑单据,修改后保存,useEffect的依赖项不会变更,也就不会触发里面的方法;—— 新建保存时,bug不会体现,但是再次编辑就会体现出来这个问题;
2、但是dataSet的load事件,会在数据源数据变更后始终执行;
qimingFlag标识丢失,getQmFlag是写在useEffect中初始化加载完毕后执行的,但是保存后刷新页面不会执行;
需要将getQmFlag写在对应dataSet的load事件中;
10、数据源重新请求后,会重置每一条record,dataSet.setState设置的值依旧存在,而record.setState、record.set、record.init设置的值丢失;
函数组件的useState的setState
类组件的setState
dataSet.setState —— 常用于保存基础配置
record.setState —— 常用于行内编辑。
record.set 会触发数据变更(dirty变为true),update时使用,不可在初始化时设置,组织提交数据时使用
record.init 不会触发数据变更()的差异 —— 初始化时设置,组织提交数据时使用
初始化设置默认数据、组织提交数据等,不要用set,否则会触发变更,go.back()提示有变更,提交时提示有变更需要先保存数据
11、&& 的优先级 大于 ||:
true && false || true,因为&& 的优先级大于 ||,所以相当于(true && false) || true;

Javascript中运算符的优先级

在js中存在很多的运算符,如何区分它们之间的优先级,今天总结常用的运算符,从上往下依次顺序:

1. 小括号()

有括号先算括号里面的;

2. 一元运算符

加加(++);  减减(--);  非(!3. 算数运算符

加(+;减(-;乘(*;除(/;取于(%);这里是先乘(*)除(/)取于(%)后加(+)减(-)。

4. 关系运算符

大于(>;大于等于(>=;小于(<;小于等于(<=)。

5. 相等运算符

等于(==);不等于(!=;全等于(===;不全等于(!==)。

6. 逻辑运算符

先且(&&)后或(||)。

7. 赋值运算符(=)。

8. 逗号运算符(,)。

12、无论何时,计算后的结果都需要圆整 round

13、Lov选择弹窗的问题,一般都是值集视图的值字段的配置有问题

  • Lov选择弹窗,切换到第二页时,Lov的查询结果表格展示超过当前页数的数据;
  • 新增保存后,Lov弹窗上有缓存数据(小眼睛图标);
    14、axios设置了返回拦截器,接口200,返回的信息中res.failed等于true时,抛出异常,所以,我们要提示错误信息时,需要用catch来捕获错误信息并提示;
    axios拦截器设置的源码:
  withTokenAxios.interceptors.response.use(function (response) {
    var status = response.status,
        data = response.data;

    if (status === 204) {
      return undefined;
    }

    if (data && data.failed) {
      // notification.error({
      //   message: data.message,
      // });
      throw data;
    } else {
      return data;
    }
  });
  withTokenAxios._HZERO_AXIOS_IS_CONFIGED = true;
} // axios.defaults.headersmon.Authorization = `bearer ${getAccessToken()}`;

15、提交之前要求保存,保存需要有变更,提交无需变更(有变更不可提交)
提交的两种形式:
1、dataSet.submit()进行提交,会自动进行提交前的校验,触发接口,以及接口调用成功之后,刷新页面(autoQueryAfterSubmit为true时)
2、使用axios.post()进行提交,使用通用组件axiosSubmit模拟dataSet.submit()上面的三个步骤;

16、【操作-编辑】按钮的权限,是在其父级按钮【操作】权限基础上的,如果【操作】按钮都没有权限,【操作-编辑】设置再多的权限也没有用;
17、上游单据与下游单据,”含税金额“相差一分钱(0.01)的问题:
原因分析:
背景:上游单据,数量、单价、金额均是可以修改的,下游单据数量是可以修改的;(公式:数量 * 单价 = 金额)会根据公式自动计算
上游单据,修改单价会自动计算金额,当修改金额时又会自动计算单价;
下游单据,会自动计算金额,金额 = 数量 * 上游单据自动计算后的单价;
即,数量固定,根据用户手动录入的金额计算出来的单价 * 数量 与用户手动录入的金额相差0.01,甚至更大;

解决方案:
初始化场景,下游单据的金额,不再自动计算,展示上游单据的金额值;
编辑场景,需要在初始化时,保存用户可能会修改的字段,当字段修改为上游单据的数据时,相当于没有修改,金额赋值为上游单据的金额值;
18、window系统,改动了本地日期后,页面的当前日期有延迟,还是修改之前的日期,在缓冲时间内与本地时间不一致;mac系统无此问题

19、数据源的配置对象,建议使用函数形式来返回,执行函数的时候再去返回这个对象,而不是页面加载后就确定了这个对象;

20、默认当前日期,有两种方式:
1、直接使用dynamicProps属性,
例如:dynamicProps: {
defaultValue() {
return moment(new Date()).format(‘YYYY-MM-DD’)
},
};
2、使用defaultValue属性,但是该数据源的配置需要使用函数形式!!!
数据源的配置为对象时,直接使用defaultValue属性,会有日期会有问题:昨天打开查询页面,今天去新建时,日期会默认为昨天的时间。
例如:defaultValue: moment().format('YYYY-MM-DD’),
21、Lodash.multiply(11, undefined); //* 11
Lodash.sum([undefined]); //* undefined
Lodash.round(undefined, 2); //* NaN
解决方案:
Loadsh.multiply(11, undefined || 0); //* 11
Lodash.sum([undefined || 0]); //* 0
Lodash.round(undefined || 0, 2); //* 0
22、如何只选择表格当前操作的数据:

	tableDS.unSelectAll(); //* 取消全选当前页
	tableDS.clearCachedSelected(); //* 清除缓存的选中记录	
	tableDS.select(record);

23、日期列,重新渲染renderer时,有值的时候再去调用monent(value);因为moment()、moment(undefined)返回的都是当前日期;

{
  name: 'invoiceDate',
  width: 140,
  align: ColumnAlign.center,
  editor: false,
  sortable: true,
  renderer: ({ value }) => {
    return moment(value).format('YYYY-MM-DD');
  },
},

当value为空时,日期列会渲染当前日期,原因是因为moment()、moment(undefined)返回的都是当前日期;

解决方案如下:

{
  name: 'invoiceDate',
  width: 140,
  align: ColumnAlign.center,
  editor: false,
  sortable: true,
  renderer: ({ value }) => {
    return value ? moment(value).format('YYYY-MM-DD') : '';
  },
},

24、当数据源有关联的子级数据源,请求父数据源,会自动请求子数据源,调用两个接口;当关联的子数据源多时,会调用大量的接口;那如何只请求父数据源,而不请求关联的子数据源呢?解决方案如下:

const children: any = basicInfoHeader.children;
basicInfoHeader.children = {};
await basicInfoHeader.query(); //* zhy hzrl-0001-5059【采购合同】合同数据版本不一致的两个场景 —— 删除后,后端会变更头信息中的合同版本号,所以需要更新头信息
basicInfoHeader.children = children;
  • dataSet.query()是异步的;
    25、查询字段的联动,需要使用表格组件属性的onQueryChange属性;
    26、触发单个字段的校验 record?.getField(‘paymentCategoryName’)?.checkValidity();

27、编辑的场景,默认值不生效,
28、表单字段的提示,help结合showHelp同时使用,showHelp有三种形式,newLine(字段下面新增一行提示信息),tooltip(表单框后面添加【?】,悬浮框提示),lable(在lable后面添加【?】,悬浮提示框);表格字段的提示,tooltip: TableColumnTooltip.overflow,内容超出,会自动展示省略号,悬浮自动展示悬浮窗;
29、tableDS.delete(),删除的数据如果不是新增的数据,会直接调用接口进行删除;
tableDS.remove(),临时删除,不会直接调用接口

30、tableDS.selected(),和勾选先后顺序没有关系;

31、方法some() 方法测试数组中是不是至少有 1 个元素通过了被提供的函数测试。它返回的是一个 Boolean 类型的值。
some()是在数组中找是否有符合条件的元素

如果有一个元素满足条件,则表达式返回true , 剩余的元素不会再执行检测。
如果没有满足条件的元素,则返回false。
find()是在数组中找第一个符合条件的元素

当数组中的元素在测试条件时返回 true 时, find() 返回符合条件的元素,之后的值不会再调用执行函数。
如果没有符合条件的元素返回 undefined。
find()与some()的性能比较

32、dataSet.dataToJSON属性

用于配置 DataSet 数据转化规则,主要作用于 toJSONData 方法的执行
基本使用

  const ds = new DataSet({
    dataToJSON: 'dirty', // 只转换变更的数据,包括本身无变更但级联有变更的数据
  });

实现逻辑
DataSet 在执行 toJSONData 方法时, 会以传入的 DataToJSON 值作为条件,仅对满足条件的数据集进行转换,具体规则如下

在有级联的数据源下,dataToJSON均默认为dirty,只组织有修改的数据,当没有变更又想按照上面的规则来组织提交数据时,需要对相应的数据源设置dataToJSON为对应的类型;

注意事项

注意根据实际需求,判断是否需要转换级联数据和附加字段等情况,选择合适的转换规则进行使用

关于级联的注意事项:

一个头dataSet(chainBasicInfoDS),有一个子级联的行dataSet(chainLineDS),现在想【提交审批】,如果有变更则需要先【保存】,没有变更时,则需要调用接口;提交时,需要提交子级联的行数据(根据用户选择的来,或者是所有的数据)

如何提交呢,首先需要设置勾选当前的头信息chainBasicInfoDS.select(chainBasicInfoDS.current),并设置chainBasicInfoDS.dataToJSON = ‘selected’,组织勾选的数据,但是发现,其子级联的行是没有数据,这是因为dataToJSON默认是dirty,只有更改后才会组织提交数据,头已经设置过了,行也需要设置,如果是根据用户选择的来,也需要设置chainLineDS.dataToJSON为selected,如果是默认所有的 和用户勾选无关的,chainLineDS.dataToJSON为selected;

54、光改common包可能不会触发编译,需要同时改动引用该common包的文件,才会触发common的再次变异
55、数据源,必录字段的隐藏,一定要设置为隐藏时非必录
56、events的submitSuccess和feedback的submitSuccess的使用:
events: { //* 事件
submitSuccess: ({ dataSet, data}) => {
data.firstStep = dataSet.getState(‘firstStep’);
dataSet.setState(‘firstStep’, false);
},
},
feedback: { //* 查询和提交数据的反馈配置
submitSuccess: (res: any) => {
const content = res && res.content;
if (content.length && content[0].saveMsg && res.firstStep) { //* 如果后端返回提示语,并且是是第一步骤的保存操作
notification.success({
message: intl.get(cicemon.massage.tip).d(‘操作成功!’),
description: content[0].saveMsg,
});
} else {
notification.success({
message: intl.get(cicemon.massage.tip).d(‘提示’),
description: ‘操作成功!’,
});
}
},
},
参数不同,events的可以获取到dataSet,feedback的获取不到;events的data是响应数据(将相应到feedback的数据),feedback的resp 是 响应值;
需要时可以配合使用,先执行events的submitSuccess,后执行feedback的submitSuccess;
在events的submitSuccess执行修改data(响应数据),执行feedback的submitSuccess时可以获取到修改后的响应值;
57、删除方法一般是使用commonDelete(utils/ciecUtils.js)(基于固定的dataSet.totalCount,为load后数组的长度),但是有时会发现dataSet.totalCount会随着新增而变更,导致大于两条时的删除功能是正常的,但是等于两条进行删除时有问题,点击删除confirm弹窗的确认按钮,不会正常执行回调函数,但是点击取消,却执行了回调函数;
—— 解决方案:已修改了通用方法,const totalCount = dataSet.getState(‘totalCount’) || dataSet.totalCount;,
totalCount 优先取dataSet.getState('totalCount’);所有我们需要在初始化时设置dataSet.setState(‘ totalCount’, 0)(因为新建时不会执行load事件); load时设置dataSet.setState(‘ totalCount’, dataSet.length);
58、表单字段,超长溢出,步骤组件的表单处理过了,没有使用步骤组件的需要单独处理,一般在外层加上className={commonStyle['ciec-steps-item’]},即可;
import commonStyle from '@common/utils/ciecUtils.less’;
使用了通用样式,
.ciec-steps-item {
// margin-bottom: 0.4rem;
:global {
.c7n-pro-output {
word-break: break-all!important;
}
}
}
59、一个Lov值集视图,多选,值字段为Nubmer类型,参数既有string,又有Number[],后端返回给前端的也是string + Number[]类型的两个字段。—— 我们组织数据源的数据时,必须把类型为string的字段放在类型为数组的字段的上面;以保证构建虚拟对象时,值字段为Nubmer类型!!!
{
name: ‘deliveryStartPortObj’,
label: ${intl.get(cice.cms.in.contract.manage.table.data.deliveryEndPortName).d('起运港')},
type: ‘object’ as FieldType,
ignore: ‘always’ as FieldIgnore,
lovCode: ‘HMDM.PORT_COUNTRY’,
multiple: true,
dynamicProps: {
required({ record }) {
return record.get(‘isStartPortRequire’) === 1
}
}
},
{
name: ‘deliveryStartPort’, //* zhy 自动组织字符串数据,且必须放在deliveryStartPortList的上面,保证load后,ptId为Number类型
bind: ‘deliveryStartPortObj.ptId’,
type: ‘string’ as FieldType,
multiple: ‘,’,
},
{
name: ‘deliveryStartPortList’,
bind: ‘deliveryStartPortObj.ptId’
},
60、非勾选的汇总值(所有页面数据的总和),可编辑的分页表格,当表格有变更汇总字段的数据,然后进行分页时,会提示有变更“有未保存的数据,是否继续?”,点击【确认】会重新查询,但是汇总字段依旧是修改后的数据;要求应该是初始的数据,使用reCompute将计算当前页面的总和,需要在load后,保存汇总值的数据,重新查询后,再次赋值上一次赋值的汇总值。—— hzrl-0001-8738【采购发货】采购发货行结算数量汇总显示问题
const forceUpdateSummary = (dataSet) => { //* 强制刷新行的汇总字段
//* zhy added hzrl-0001-8738【采购发货】采购发货行结算数量汇总显示问题
const parentDataSet = dataSet.parent;
const summaryLineDS = summaryHeader?.current?.summaryLineDS;
setTimeout(() => {
summaryLineDS?.current?.init(‘40Sum’, parentDataSet?.current?.get(‘totalPackingQuantity_origin’) || parentDataSet?.current?.get(‘totalPackingQuantity’));
summaryLineDS?.current?.init(‘numberSum’, parentDataSet?.current?.get(‘totalNumber_origin’) || parentDataSet?.current?.get(‘totalNumber’));
summaryLineDS?.current?.init(‘amountSum’, parentDataSet?.current?.get(‘totalAmount_origin’) || parentDataSet?.current?.get(‘totalAmount’));
parentDataSet?.current?.init(‘totalNumber’, parentDataSet?.current?.get(‘totalNumber_origin’) || parentDataSet?.current?.get(‘totalNumber’));
parentDataSet?.current?.init(‘totalAmount’, parentDataSet?.current?.get(‘totalAmount_origin’) || parentDataSet?.current?.get(‘totalAmount’));
summaryHeader.current?.forceUpdate({});
}, 50);
}

useDataSetEvent(lineDS, ‘load’, ({ dataSet }) => {
const { records = [] } = dataSet;
records.forEach((res: any, index: number) => {
// ! 被引用或者被锁货都不可勾选
if (res.get(‘isQuote’) || res.get(‘isLock’)) {
const first = dataSet.get(index);
if (first) first.selectable = false;
}
});
setIsMoreLine(!!records.length);
if (!dataSet.getState(‘noNeedForceUpdateSummary’)) { //* 需要“强制刷新行的汇总字段”
forceUpdateSummary(dataSet);
} //* 删除时已经处理过了,不需要再次“强制刷新行的汇总字段”
dataSet.setState(‘noNeedForceUpdateSummary’, false);

});
61、get 请求参数重空格的处理方式(以+号拼接,后端会自动处理),查询时,query字段会自动拼接,但是如果是手动拼接queryString需要我们自己拼接
const invalidSubmit = async (res?: any, adjustSubmit?: string) => { //* 作废提交
const _urlParams = urlParams || ‘?’; //? 接口参数
if (adjustSubmit) { //* 有变更时间的提交
const adjustDate = InvalidDs.current?.get(‘adjustDate’);
const adjustDateStr = adjustDate?.format(‘YYYY-MM-DD+HH:mm:ss’) ?? ‘’; //* 手动将空格转成【+】,方便后端转换
tableDS[‘_urlParams’] += &adjustDate=${adjustDateStr};
}
tableDS[‘_urlParams’] = _urlParams;
const result = await otherSubmit(tableDS, ‘invalid’); //* 进行【作废】提交
result && closeModal(res);
};
62、Lov值集视图中,后端返回了两个重复的数据(值字段一样的),
导致1、lov弹窗重新查询或者切换到下一页时,始终都会多一条之前重复的数据;
2、lov字段直接编辑后自动查询,会默认带出数条Lov弹窗中重复的数据,每次打开lov弹窗后,就会多搜到1条数据;
—— 值字段不可重复,或者说后端的数据中值字段不能重复;
63、通过bind将一个字段绑定到一个对象上,当对象update的时候,该字段不会触发update,如果该lov对象update的时候想要触发该字段的update时,首先需要解除bind绑定,然后使用set触发即可
64、dataSet.cacheSelection属性:用于设置 缓存选中记录,使切换分页时仍保留选中状态。但是只有 当设置了 primaryKey 或有字段设置了 unique 才起作用。

65、cascadeParams的级别大于我们请求时设置的入参,数据源有级联时,并且设置了级联参数cascadeParams,我们再去commonQuery子数据源,并设置cascadeParams中相同的入参,但是不同值,会发现查询请求的入参,不是我们设置后的值而是父级数据源中的值;
—— hzrl-0001-9825【四大合同】销售合同有头有行,做了变更新增行再删除变更合同操作后,原来版本合同行没了 —— 合同变更成功后,修改合同编号,防止快速跳转到货物行页面时,通过老的合同编号查询老数据,导致的后续问题
66、进入步骤二修改步骤一的字段,应考虑该字段是否有联动的关系,并制定解决方案;
—— hzrl-0001-9852【销售合同】销售合同锁现有量反写货物行保存报错 —— 场景未考虑全, 本次采用的是数据源关联一个属性,仅步骤一时需要联动,其他步骤均不需要联动(改成非必录);
67、数学运算的时候,一定要考虑被除数为0的场景,被除数为0,2/0 === infinity,divide(2, 0) === NaN
—— hzrl-0001-9850【采购合同】复制采购合同行 利润预测中的平均毛利显示英文
if (averagePriceCount !== 0) {
averageGrossProfitDS?.current?.set(‘averageGross’, round(divide(averagePrice, averagePriceCount), 4));
}
68、可编辑字段的汇总值,分页的表格修改后,不保存直接切换下一页导致行的汇总数据有问题,—— hzrl-0001-8738【采购发货】采购发货行结算数量汇总显示问题
可编辑字段的汇总值,保存后的数据与编辑前不一致,需要强制更新汇总字段 —— hzrl-0001-9724【采购发货登记】修改采购发货登记单行含税金额 保存后弹窗提示点击确认 汇总含税金额未更新

69、父级数据源与子级数据源,属性是否有关联
dataToJson属性,没有关联,各司其职,不相互影响;
dirty属性,有关联,子级dirty,父级也变成dirty;
status属性,没有关联,各司其职,不相互影响;

70、status
dataSet.status
dataSet 在初始化或者查询的时候会改变 status 为 loading, 在 submit 期间会改变为 submiting ,当所有操作处理完毕时状态会变为 ready。

Table、Form 或 Button 等组件关联了 dataSet 后,在 dataSet 处理过程中,会显示 loading 状态。

Record.status
record 对象有一个值是 status,用于标记记录的状态,可选值有 add | update | delete | sync

loadData 方法等同于手动将 query 拿到的数据加载进 dataSet 对象,均为 sync 状态。
使用 dataSet 的 create 方法和 Table 的 add 按钮新建的记录状态为 add。
sync 状态的记录修改后变为 update 状态。
sync 状态的记录执行 dataSet 的 remove 方法后状态变为 delete(还未向后端提交,仅是前端删除)。
71、dataSet 或 record 对象在执行 validate 校验方法时,只会校验内部 status 状态不为 sync 的 record 记录。
其中,使用 dataSet.loadData 方法加载的数据状态为 sync。

当 dataSet 的 forceValidate 属性设置为 true 后,status 状态为 sync 的记录也会被校验,但是移除的记录(调用 remove 方法的记录)不会校验。

72、var timer = setTimeout(() => {

//todo sth
}, 20000)
clearTimeout(timer); //* 清除计时器之后,timer依旧是计时器ID
73、合同非最新版本(maxVersionNum > contractVersion),不允许编辑;

74、先请求头信息,子数据源不会立即有loading的效果,在子数据源页面,请求头信息时,子数据源会有明显的延迟感;

—— 解决方案,请求头信息之前,添加goodsInfoDS.status = DataSetStatus.loading;

75、有级联的数据源,当只刷新子数据源时,勾选状态不会被清空,但是当刷新父级数据源时,子级的数据源的勾选状体啊会被清空;

—— fix(cms)🐛:hzrl-0001-10359【采购/销售合同】审批通过的合同,先勾选货物行,点击变更后,切换到货物行页面,发现有货物行有缓存勾选(小眼睛),导致再勾选当前行进行复制,能复制出多行;

git相关问题记录

1、解决代码冲突要注意的问题:—— 红线
1. 不要简单的全部采用传入的更改,而是要一个一个的去检查,看看到底是采用传入的更改,还是需要我们保留二者的变更(可能需要适当的变更),尤其是解决master的冲突;
2. 解决冲突时,遇到他人代码,不确定如何解决时,要找对应的开发同学;
3. 合并”解决代码冲突“代码的时候,开发同学最好能够检查一下;

2、daily/dev打包时,会自动将master的代码merge到daily/dev,当有冲突时,需要重新以origin/daily为基准拉取一个分支,Merge origin/master into daily,手动解决冲突后,提交 merge到 origin/daily;
3、直接在gitLab上解决冲突,会将daily上的代码Merge到自己的分支上,将来合master,会将daily上无需合到master的代码合到master; 万一这么操作了,该怎么办呢?
1. 在工作分支上找到错误的Merge记录(Merge daily into 当前分支),然后在该记录之前重新拉取一个中转分支;
2. 若工作分支的这个错误的Merge记录的上面有其他的提交记录,需要cherry pick到新的分支;
3. 然后通过比对工具(文件夹比较与合并,推荐App Beyong Compare),全量比对工作分支和中转分支,在差异文件中,将中转分支的代码 全量复制到工作分支上;—— 此时获取到相当于revert Merge 的变更;
4. 然后在工作分支上将全部的变更提交(revert Merge);
5. 最后将工作分支Merge到master;

4、解决Merge冲突有两个方案:
1. 以daily为基准去解决冲突;使用最新的daily拉取副本分支,然后将我们的分支代码Merge到daily的副本,手动解决冲突,提交后,去gitLab上提MR;—— 适合解决复杂冲突,需要保存两者,甚至是保存两者之后需要修改的冲突;
2. 以我们自己的分支为基准去解决冲突,使用我们自己的分支代码拉取副本分支,直接去gitLab上提交MR,有冲突直接在gitLab上解决即可;—— 更简单,适合解决简单明了的冲突;
均需要delete source Branch
5、切记不要在gitLab上解决daily/dev的冲突,不然会将daily/dev Merge到我们的分支,后面合生产时就会有「将别人代码合到生产」的问题;解决冲突有两个方案,一个是以daily为基准去解决冲突,一个是以我们自己的分支为基准去解决冲突;
Merge到master的冲突,可以直接在gitLab上解决;
6、问题现象:自己建的分支,只有自己修改,然后提交,提示需要先pull拉取一下,为啥?
原因分析:从gitLab新建一个分支(远程),想从vscode切换到该分支时,使用了第一种方案,vsCode切换到master,但是没有拉取最新的代码(本地master分支 版本落后),然后新建一个重名的分支,导致这两个重名分支后面自动关联时,版本不一致

7、从gitLab新建一个分支,想从vscode切换到该分支时,发现没有搜到远程的该分支origin/,解决方案目前有两种:
1. vsCode切换到master(本地),拉取最新的代码,然后新建一个重名的本地分支;相当于新建了两个重名分支,后面git会自动关联
2. 使用fetch抓取所有分支,就可以搜到该远程分支,就可以从远程的该分支来创建本地分支 ——推荐用法
8、git remote -v 查看当前的远程仓库地址 git remote set-url origin 新地址 , 重设远程仓库地址

其他沉淀

1、前端根据后端的fileUrl预览PDF时,文件名是根据fileUrl来的,但是下载时,也可以默认其他的文件名,即在document类型的请求中,response headers中 content-disposition的filename属性来设置文件名
response.setHeader(“Content-disposition”, “attachment;filename=” + fileName)。

Content-disposition为属性名。一般有两种方式:

  • inline:直接在页面显示
  • attchment:以附件形式下载

attachment表示以附件方式下载。如果要在页面中打开,则改为inline。

filename如果为中文,则会出现乱码。解决办法有两种:

  • 1、使用fileName = new String(fileName.getBytes(), “ISO8859-1”)语句

  • 2、使用fileName = HttpUtility.UrlEncode(filename, System.Text.Encoding.UTF8)语句

  • Content-type 指示响应内容的格式

更多推荐

猪齿鱼知识沉淀