fix(EQD-6838): 修复约定2/3标识不更新的Vue响应式问题 + 组件内emit keydown + 去掉重算按钮

根因1(约定3按键不触发):
- .native 修饰符在 vue-number-input 上不可靠(jQuery在mounted中重新绑定了keydown)
- 改为在组件内部 addEventListener('keydown') + $emit('keydown')
- cshtml 从 v-on:keydown.native 改为 v-on:keydown

根因2(约定2/3标识不更新):
- bondDriverType/bondAuto/bondRev 不在后端数据中,直接赋值不触发Vue2响应式更新
- 成功分支之前用 $forceUpdate 兜底,但失败/编辑/按键分支没有
- 新增 syncBondFlags 方法,用 $set 强制触发响应式更新
- 在所有状态变更后调用:onBondPriceEdit/onBondPriceKeydown/onDpPriceInput/
  calcBondForItem成功/失败/setUnderlyingCode/加载路径

其他改动:
- 去掉"重算"按钮(用户确认不再需要)
- calcBondForItem 成功分支用 syncBondFlags 替代 $forceUpdate
- 重建 bundle.js(fastVue.base.js 是 bundle 输入)
This commit is contained in:
hjhan
2026-07-29 19:20:13 +08:00
parent d662399ab4
commit cee69a02eb
4 changed files with 36 additions and 8 deletions
+3 -3
View File
@@ -474,13 +474,13 @@
</a>
</td>
<td v-if="trade.StructureType!='普通收益互换'">
<vue-number-input :key="getPosiPriceFormatKey(item,'PosiGrossPrice')" v-model="item.PosiGrossPrice" v-bind:format="inputFormatSwapBondDeliveryPrice" v-on:input="onDpPriceInput(item)" v-on:enter="onBondPriceEnter(item,'DP')" v-on:keydown.native="onBondPriceKeydown(item,'DP',$event)"></vue-number-input><span style="color:#2e7d32;font-size:11px;margin-left:4px;" v-if="item.isBond && item.bondDriverType==='DP'">源</span><span style="color:#1565c0;font-size:11px;margin-left:4px;" v-else-if="item.isBond && item.bondAuto && item.bondAuto.DP">AUTO</span><span style="color:#ef6c00;font-size:11px;margin-left:4px;" v-if="item.isBond && item.bondRev && item.bondRev.DP" title="人工输入:未回车触发计算器">REV</span>
<vue-number-input :key="getPosiPriceFormatKey(item,'PosiGrossPrice')" v-model="item.PosiGrossPrice" v-bind:format="inputFormatSwapBondDeliveryPrice" v-on:input="onDpPriceInput(item)" v-on:enter="onBondPriceEnter(item,'DP')" v-on:keydown="onBondPriceKeydown(item,'DP',$event)"></vue-number-input><span style="color:#2e7d32;font-size:11px;margin-left:4px;" v-if="item.isBond && item.bondDriverType==='DP'">源</span><span style="color:#1565c0;font-size:11px;margin-left:4px;" v-else-if="item.isBond && item.bondAuto && item.bondAuto.DP">AUTO</span><span style="color:#ef6c00;font-size:11px;margin-left:4px;" v-if="item.isBond && item.bondRev && item.bondRev.DP" title="人工输入:未回车触发计算器">REV</span>
</td>
<td v-if="trade.StructureType!='普通收益互换'">
<vue-number-input :key="getPosiPriceFormatKey(item,'PosiNetNoFeePrice')" v-model="item.PosiNetNoFeePrice" v-bind:format="inputFormatSwapBondNetPriceAndYtm" v-on:input="onBondPriceEdit(item,'CP')" v-on:enter="onBondPriceEnter(item,'CP')" v-on:keydown.native="onBondPriceKeydown(item,'CP',$event)"></vue-number-input><span style="color:#2e7d32;font-size:11px;margin-left:4px;" v-if="item.isBond && item.bondDriverType==='CP'">源</span><span style="color:#1565c0;font-size:11px;margin-left:4px;" v-else-if="item.isBond && item.bondAuto && item.bondAuto.CP">AUTO</span><span style="color:#ef6c00;font-size:11px;margin-left:4px;" v-if="item.isBond && item.bondRev && item.bondRev.CP" title="人工输入:未回车触发计算器">REV</span>
<vue-number-input :key="getPosiPriceFormatKey(item,'PosiNetNoFeePrice')" v-model="item.PosiNetNoFeePrice" v-bind:format="inputFormatSwapBondNetPriceAndYtm" v-on:input="onBondPriceEdit(item,'CP')" v-on:enter="onBondPriceEnter(item,'CP')" v-on:keydown="onBondPriceKeydown(item,'CP',$event)"></vue-number-input><span style="color:#2e7d32;font-size:11px;margin-left:4px;" v-if="item.isBond && item.bondDriverType==='CP'">源</span><span style="color:#1565c0;font-size:11px;margin-left:4px;" v-else-if="item.isBond && item.bondAuto && item.bondAuto.CP">AUTO</span><span style="color:#ef6c00;font-size:11px;margin-left:4px;" v-if="item.isBond && item.bondRev && item.bondRev.CP" title="人工输入:未回车触发计算器">REV</span>
</td>
<td v-if="trade.StructureType!='普通收益互换'">
<vue-number-input :key="getPosiPriceFormatKey(item,'InitYtm')" v-model="item.InitYtm" v-bind:format="inputFormatSwapBondNetPriceAndYtm" v-on:input="onBondPriceEdit(item,'YD')" v-on:enter="onBondPriceEnter(item,'YD')" v-on:keydown.native="onBondPriceKeydown(item,'YD',$event)"></vue-number-input><span style="color:#2e7d32;font-size:11px;margin-left:4px;" v-if="item.isBond && item.bondDriverType==='YD'">源</span><span style="color:#1565c0;font-size:11px;margin-left:4px;" v-else-if="item.isBond && item.bondAuto && item.bondAuto.YD">AUTO</span><span style="color:#ef6c00;font-size:11px;margin-left:4px;" v-if="item.isBond && item.bondRev && item.bondRev.YD" title="人工输入:未回车触发计算器">REV</span><a href="javascript:void(0);" v-on:click="resetBondCalc(item)" v-if="item.isBond" style="margin-left:6px;font-size:11px;color:#1565c0;">重算</a>
<vue-number-input :key="getPosiPriceFormatKey(item,'InitYtm')" v-model="item.InitYtm" v-bind:format="inputFormatSwapBondNetPriceAndYtm" v-on:input="onBondPriceEdit(item,'YD')" v-on:enter="onBondPriceEnter(item,'YD')" v-on:keydown="onBondPriceKeydown(item,'YD',$event)"></vue-number-input><span style="color:#2e7d32;font-size:11px;margin-left:4px;" v-if="item.isBond && item.bondDriverType==='YD'">源</span><span style="color:#1565c0;font-size:11px;margin-left:4px;" v-else-if="item.isBond && item.bondAuto && item.bondAuto.YD">AUTO</span><span style="color:#ef6c00;font-size:11px;margin-left:4px;" v-if="item.isBond && item.bondRev && item.bondRev.YD" title="人工输入:未回车触发计算器">REV</span>
</td>
<td v-if="trade.StructureType=='普通收益互换'">
<vue-number-input :key="getPosiPriceFormatKey(item,'normalPosiGrossPrice')" v-model="item.PosiGrossPrice" v-bind:format="inputFormatSwapDeliveryPrice" v-on:input="changeSpotPrice(item)"></vue-number-input>
@@ -348,10 +348,11 @@ const vue = new Vue({
onBondPriceEdit(item, type) {
if (!item.isBond) return;
SwapCalc.applyBondManualEdit(item, type);
this.syncBondFlags(item);
},
//交互约定3(按键实时触发):vue-number-input 的 input 事件只在失焦/回车时触发,
// 按键输入过程中不触发,导致用户输入时 源/AUTO 标识不会消失。
// 此方法绑在 v-on:keydown.native 上,按键即清 源/AUTO 标识、标本字段 REV。
// 此方法绑在 v-on:keydown 上(组件内部 addEventListener + $emit,按键即清 源/AUTO 标识、标本字段 REV。
// 回车键(13/108)除外——回车由 v-on:enter 驱动 calcBondForItem,会把标识修正为 源/AUTO。
// 修饰键(Shift/Ctrl/Alt/Tab/方向键/F键等)除外——这些不改变数值,不应清标识。
onBondPriceKeydown(item, type, event) {
@@ -365,12 +366,24 @@ const vue = new Vue({
// Backspace/Delete 改变数值,需清标识
// 数字键/小键盘/小数点/减号 改变数值,需清标识
SwapCalc.applyBondManualEdit(item, type);
this.syncBondFlags(item);
},
//确保 bondDriverType/bondAuto/bondRev 的变更触发 Vue 响应式更新。
//swapCalc.js 的纯函数直接对 state 赋值,但这些属性不在后端数据中(非响应式),
//直接赋值不触发视图更新。用 $set 强制触发,无论属性是否已存在都能正确工作。
syncBondFlags(item) {
this.$set(item, 'bondDriverType', item.bondDriverType);
this.$set(item, 'bondAuto', item.bondAuto);
this.$set(item, 'bondRev', item.bondRev);
},
//全价列:普通标的重算名义本金;债券标的按交互约定3 标 REV(不联动)。
// 回车计算由独立的 v-on:enter="onBondPriceEnter(item,'DP')" 负责。
onDpPriceInput(item) {
this.changeSpotPrice(item); // 名义本金随全价变化(非债券标的的既有逻辑)
if (item.isBond) SwapCalc.applyBondManualEdit(item, 'DP'); // 交互约定3:失焦未回车→REV
if (item.isBond) {
SwapCalc.applyBondManualEdit(item, 'DP'); // 交互约定3:失焦未回车→REV
this.syncBondFlags(item);
}
},
//放弃手动标记、以当前某字段为源重新自动反算("重算"按钮)。
resetBondCalc(item) {
@@ -435,6 +448,7 @@ const vue = new Vue({
// 同一错误文案连续出现只弹一次(shouldShowBondErr 维护 item._lastBondErr),抑制噪声、不影响任何计算逻辑。
if (SwapCalc.shouldShowBondErr(item, err)) main.message(err);
SwapCalc.applyBondCalcFailure(item, driver); // 交互约定2:失败→清另两字段+标识
self.syncBondFlags(item);
return;
}
item._lastBondErr = null; // 成功则清标记,便于下次真出不同错误时仍能提示
@@ -455,10 +469,10 @@ const vue = new Vue({
item.PosiGrossPrice = SwapCalc.bondCalcPriceToStorage(proxy.dirtyPrice);
item.InitYtm = SwapCalc.bondCalcPriceToStorage(proxy.ytm);
SwapCalc.applyBondCalcSuccess(item, driver); // 交互约定2:成功→标 源/AUTO
self.syncBondFlags(item);
// 名义本金依赖全价(PosiGrossPrice):以净价/收益率为源反算出的全价被回写后,
// 直接赋值不会触发组件 input 事件,需在此显式重算,保持名义本金与全价一致。
if (self.calcNotional) self.calcNotional();
if (self.$forceUpdate) self.$forceUpdate();
});
},
//变更收费基本单位
@@ -814,6 +828,7 @@ const vue = new Vue({
this.$set(item, 'isBond', tradeHelper.IsBond(data.InstrumentType));
// 切换标的时清空债券三字段互算的手动/源标志(D1 修复:避免旧债券手填状态污染新债券)
SwapCalc.clearBondCalcFlags(item);
this.syncBondFlags(item);
// 簿记基础逻辑1:不再自动调 getSpotPrice 回填价格,由用户手填或回车调计算器
// 续发(renewal)场景的初始价恢复由 refreshInitialUnderlyingPrices 独立处理
//this.initMarginRate();
@@ -1560,6 +1575,7 @@ const vue = new Vue({
var isBond = val.isBond || tradeHelper.IsBond(val.UnderlyingInstrumentType);
if (isBond && hasV(val.PosiNetNoFeePrice) && hasV(val.PosiGrossPrice) && hasV(val.InitYtm)) {
SwapCalc.clearBondCalcMarksOnLoad(arr[num]);
thisObj.syncBondFlags(arr[num]);
}
});
if (thisObj.paySwapList.length > 0) {
@@ -544,7 +544,13 @@
let self = this;
let options = FastVue.extend({ onchange: this.onchange }, this.format);
this.inputctrl = FastVue.numberInput(this.$el, options);
this.inputctrl.setValue(this.value)
this.inputctrl.setValue(this.value);
// 按键时 emit 'keydown' 事件,供父组件监听按键输入(交互约定3)
// .native 修饰符在此组件上不可靠(jQuery 在 mounted 中重新绑定了 keydown),
// 改为在组件内部 addEventListener 并 emit,确保父组件能收到按键事件
this.$el.addEventListener('keydown', function (e) {
self.$emit('keydown', e);
});
},
methods: {
onchange(value, text, isEnter) {
+7 -1
View File
@@ -14601,7 +14601,13 @@ $.fn.selectpicker.Constructor.DEFAULTS = Object.assign($.fn.selectpicker.Constru
let self = this;
let options = FastVue.extend({ onchange: this.onchange }, this.format);
this.inputctrl = FastVue.numberInput(this.$el, options);
this.inputctrl.setValue(this.value)
this.inputctrl.setValue(this.value);
// 按键时 emit 'keydown' 事件,供父组件监听按键输入(交互约定3)
// .native 修饰符在此组件上不可靠(jQuery 在 mounted 中重新绑定了 keydown),
// 改为在组件内部 addEventListener 并 emit,确保父组件能收到按键事件
this.$el.addEventListener('keydown', function (e) {
self.$emit('keydown', e);
});
},
methods: {
onchange(value, text, isEnter) {