From ead2c01f0d9073b3fd0ed200fe54d6547fadb525 Mon Sep 17 00:00:00 2001 From: hjhan Date: Thu, 30 Jul 2026 10:06:42 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E5=80=BA=E5=88=B8=E4=B8=89=E5=AD=97?= =?UTF-8?q?=E6=AE=B5=20keydown=20=E5=AE=88=E5=8D=AB=E6=94=B9=E7=94=A8?= =?UTF-8?q?=E6=AD=A3=E5=90=91=E7=99=BD=E5=90=8D=E5=8D=95=EF=BC=8C=E5=A0=B5?= =?UTF-8?q?=E4=BD=8F"=E6=B2=A1=E6=94=B9=E5=80=BC=E5=8D=B4=E6=A0=87REV"?= =?UTF-8?q?=E7=9A=84=E7=BC=BA=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 根因:onBondPriceKeydown 的按键排除列表只排除回车/Ctrl-Alt-Meta/Tab/Home/End/←→/F5, 漏掉 ↑(38)/↓(40)/Insert(45)/PageUp(33)/PageDown(34)/F1-F4,F6-F12/纯Shift。 这些键不改变数值却会触发 applyBondManualEdit 标 REV——正是用户观察到的 "鼠标放输入框没改值、只按了方向键/功能键却有时出现REV"。 修复: - swapCalc.js 新增纯函数 isBondPriceValueKey(event):正向白名单(数字/小数点/减号/Backspace/Delete) 才返回 true,其余(回车/修饰键/导航键/功能键/纯Shift)返回 false,jest 可测。 - swapTradeEdit.js onBondPriceKeydown 改用该函数判定,并移除两行每次按键的 console.log 噪音。 - 纯鼠标聚焦(无按键)本就不会标REV(无 focus/click 处理;vue-number-input 的 input 仅在值变化(失焦/回车)触发), 本次仅修正按键守卫缺口。 - 同步补 9 个单测覆盖各类按键(响应"最好单测阶段解决")。 约定1/约定2/约定3 累加REV 行为不受任何影响(仅改判定是否标REV的触发条件)。 测试:bondCalc 全套 81 例通过。 --- YLErpWeb/fe-tests/bondCalc.test.js | 52 +++++++++++++++++++ .../wwwroot/Scripts/app/swaptrade/swapCalc.js | 33 ++++++++++++ .../Scripts/app/swaptrade/swapTradeEdit.js | 15 ++---- 3 files changed, 90 insertions(+), 10 deletions(-) diff --git a/YLErpWeb/fe-tests/bondCalc.test.js b/YLErpWeb/fe-tests/bondCalc.test.js index 44eca626..6595bdab 100644 --- a/YLErpWeb/fe-tests/bondCalc.test.js +++ b/YLErpWeb/fe-tests/bondCalc.test.js @@ -276,3 +276,55 @@ describe('单位换算边界(前端↔债券计算器 存储态小数 ↔ 展 expect(modelYtm * 100).toBeCloseTo(6.37, 4); }); }); + +describe('isBondPriceValueKey:只有真正改值的按键才标REV(防止"没改值却REV")', () => { + const K = (keyCode, extra) => Object.assign({ which: keyCode, keyCode: keyCode }, extra || {}); + test('数字键(主键盘/小键盘) → 改值', () => { + expect(SwapCalc.isBondPriceValueKey(K(49))).toBe(true); // 主键盘 1 + expect(SwapCalc.isBondPriceValueKey(K(97))).toBe(true); // 小键盘 1 + }); + test('小数点/减号(主键盘/小键盘) → 改值', () => { + expect(SwapCalc.isBondPriceValueKey(K(190))).toBe(true); // 主键盘 . + expect(SwapCalc.isBondPriceValueKey(K(110))).toBe(true); // 小键盘 . + expect(SwapCalc.isBondPriceValueKey(K(189))).toBe(true); // 主键盘 - + expect(SwapCalc.isBondPriceValueKey(K(109))).toBe(true); // 小键盘 - + }); + test('Backspace / Delete → 改值', () => { + expect(SwapCalc.isBondPriceValueKey(K(8))).toBe(true); + expect(SwapCalc.isBondPriceValueKey(K(46))).toBe(true); + }); + test('回车 → 不改标识(由 enter 事件处理)', () => { + expect(SwapCalc.isBondPriceValueKey(K(13))).toBe(false); + expect(SwapCalc.isBondPriceValueKey(K(108))).toBe(false); + }); + test('方向键 ←↑→↓ → 不改标识(修复:此前漏掉 ↑↓ 会误标REV)', () => { + expect(SwapCalc.isBondPriceValueKey(K(37))).toBe(false); + expect(SwapCalc.isBondPriceValueKey(K(38))).toBe(false); + expect(SwapCalc.isBondPriceValueKey(K(39))).toBe(false); + expect(SwapCalc.isBondPriceValueKey(K(40))).toBe(false); + }); + test('Home/End/Tab/Insert/PageUp/PageDown → 不改标识(修复:此前漏掉 Insert/PageUp/PageDown)', () => { + expect(SwapCalc.isBondPriceValueKey(K(35))).toBe(false); + expect(SwapCalc.isBondPriceValueKey(K(36))).toBe(false); + expect(SwapCalc.isBondPriceValueKey(K(9))).toBe(false); + expect(SwapCalc.isBondPriceValueKey(K(45))).toBe(false); + expect(SwapCalc.isBondPriceValueKey(K(33))).toBe(false); + expect(SwapCalc.isBondPriceValueKey(K(34))).toBe(false); + }); + test('F1-F12 → 不改标识(修复:此前漏掉除 F5 外的功能键)', () => { + [112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123].forEach(function (kc) { + expect(SwapCalc.isBondPriceValueKey(K(kc))).toBe(false); + }); + }); + test('纯 Shift / Ctrl / Alt / Meta 组合键 → 不改标识', () => { + expect(SwapCalc.isBondPriceValueKey(K(16))).toBe(false); // 纯 Shift + expect(SwapCalc.isBondPriceValueKey(K(65, { ctrlKey: true }))).toBe(false); // Ctrl+A + expect(SwapCalc.isBondPriceValueKey(K(86, { ctrlKey: true }))).toBe(false); // Ctrl+V + expect(SwapCalc.isBondPriceValueKey(K(67, { altKey: true }))).toBe(false); + expect(SwapCalc.isBondPriceValueKey(K(67, { metaKey: true }))).toBe(false); + }); + test('无事件对象 → 安全返回 false', () => { + expect(SwapCalc.isBondPriceValueKey(null)).toBe(false); + expect(SwapCalc.isBondPriceValueKey(undefined)).toBe(false); + }); +}); diff --git a/YLErpWeb/wwwroot/Scripts/app/swaptrade/swapCalc.js b/YLErpWeb/wwwroot/Scripts/app/swaptrade/swapCalc.js index df90bb43..dfcea9a7 100644 --- a/YLErpWeb/wwwroot/Scripts/app/swaptrade/swapCalc.js +++ b/YLErpWeb/wwwroot/Scripts/app/swaptrade/swapCalc.js @@ -386,8 +386,41 @@ "若暂不需要计算,可手动填写净价/全价/收益率三项数值"; } + /** + * 判断按键是否会【改变】债券三字段的数值(纯函数,jest 可测)。 + * 用于 onBondPriceKeydown:仅"会改变数值"的按键才清 源/AUTO 标识并标本字段 REV; + * 其余(回车/修饰键/导航键/功能键/纯 Shift)一律不改标识,避免"没改值却出现 REV"。 + * 正向白名单(最稳,不会漏掉未来新增的导航键): + * - 数字键:主键盘 48-57 / 小键盘 96-105 + * - 小数点:主键盘 190 / 小键盘 110 + * - 减号: 主键盘 189 / 小键盘 109 + * - Backspace(8) / Delete(46) + * 明确排除:回车(13/108,由 v-on:enter 单独处理)、Ctrl/Alt/Meta 组合键、 + * Tab(9)/Home(36)/End(35)/←(37)/→(39)/↑(38)/↓(40)/Insert(45)/PageUp(33)/PageDown(34)、 + * F1-F12(112-123)、纯 Shift(16)。这些键不改数值,不应清标识/标 REV。 + */ + function isBondPriceValueKey(event) { + if (!event) return false; + if (event.ctrlKey || event.altKey || event.metaKey) return false; // 组合键(如 Ctrl+C/V)不改数值 + var kc = event.which || event.keyCode; + if (kc === 13 || kc === 108) return false; // 回车由 enter 事件处理 + // 纯 Shift 单独按下不改数值(Shift+数字在数值框不产生数字,亦不改数值) + if (kc === 16) return false; + // 导航/功能键不改数值 + if (kc >= 33 && kc <= 40) return false; // PageUp/PageDown/End/Home/←/↑/→/↓ + if (kc === 45) return false; // Insert + if (kc >= 112 && kc <= 123) return false; // F1-F12 + if (kc === 9) return false; // Tab + // 以下为会改变数值的键 + var valueKeys = [8, 46, 48,49,50,51,52,53,54,55,56,57, + 96,97,98,99,100,101,102,103,104,105, + 109,110,189,190]; + return valueKeys.indexOf(kc) !== -1; + } + return { applyBondCalcResult: applyBondCalcResult, + isBondPriceValueKey: isBondPriceValueKey, getBondCalcErrorMessage: getBondCalcErrorMessage, bondPriceToCalc: bondPriceToCalc, bondCalcPriceToStorage: bondCalcPriceToStorage, diff --git a/YLErpWeb/wwwroot/Scripts/app/swaptrade/swapTradeEdit.js b/YLErpWeb/wwwroot/Scripts/app/swaptrade/swapTradeEdit.js index 421dd26b..22914749 100644 --- a/YLErpWeb/wwwroot/Scripts/app/swaptrade/swapTradeEdit.js +++ b/YLErpWeb/wwwroot/Scripts/app/swaptrade/swapTradeEdit.js @@ -360,18 +360,13 @@ const vue = new Vue({ // 修饰键(Shift/Ctrl/Alt/Tab/方向键/F键等)除外——这些不改变数值,不应清标识。 onBondPriceKeydown(item, type, event) { if (!item.isBond) return; - var kc = event.which || event.keyCode; - // 回车由 v-on:enter 处理,不在此清标识 - if (kc === 13 || kc === 108) return; - // 修饰键/导航键/功能键不改变数值,不清标识 - if (event.ctrlKey || event.altKey || event.metaKey) return; - if (kc === 9 || kc === 35 || kc === 36 || kc === 37 || kc === 39 || kc === 116) return; - // Backspace/Delete 改变数值,需清标识 - // 数字键/小键盘/小数点/减号 改变数值,需清标识 - console.log('[onBondPriceKeydown] type=' + type + ' kc=' + kc + ' before: driver=' + item.bondDriverType); + // 仅"会改变数值"的按键才清 源/AUTO 标识并标本字段 REV; + // 回车/修饰键/导航键/功能键/纯 Shift 不改标识(见 SwapCalc.isBondPriceValueKey)。 + // 修复:此前守卫漏掉 ↑(38)/↓(40)/Insert(45)/PageUp(33)/PageDown(34)/F键/Shift(16), + // 导致"鼠标放进去没改值、只按了方向键/功能键"也会误标 REV。 + if (!SwapCalc.isBondPriceValueKey(event)) return; SwapCalc.applyBondManualEdit(item, type); this.syncBondFlags(item); - console.log('[onBondPriceKeydown] after: driver=' + item.bondDriverType + ' rev=' + JSON.stringify(item.bondRev)); }, //确保 bondDriverType/bondAuto/bondRev 的变更触发 Vue 响应式更新。 //swapCalc.js 的纯函数直接对 state 赋值,但这些属性不在后端数据中(非响应式),