feat(input): 数字输入整数位数限制功能

- 在 fastVue.base.js 中实现整数位数配置选项 integerDigits
- 添加 checkIntegerDigitsInput 函数验证整数位数输入限制
- 实现 limitIntegerDigits 函数控制数值字符串中的整数位数
- 在键盘处理和粘贴事件中集成整数位数限制逻辑
- 更新掉期交易价格精度配置支持 quantityIntegerDigits 选项
- 扩展精度辅助函数支持整数位数配置和验证规则
- 添加完整的单元测试覆盖整数位数限制功能
This commit is contained in:
张名锐
2026-08-06 16:30:59 +08:00
parent 9f6224f44e
commit 40b4e2e697
5 changed files with 349 additions and 57 deletions
+28 -27
View File
@@ -3,53 +3,54 @@ window.main = window.main || {};
window.main.swapPricePrecision = {
common: {
amount: { precision: 2, grouping: true },
quantity: { precision: 2, grouping: true },
quantity: { integerDigits: 16, precision: 2, grouping: true },
rate: { precision: 4 }
},
Stock: { integerDigits: 7, precision: 2, quantityPrecision: 2 },
StockIndex: { integerDigits: 7, precision: 2, quantityPrecision: 2 },
StockIF: { integerDigits: 7, precision: 4, quantityPrecision: 2 },
CommodityFutures: { integerDigits: 7, precision: 4, quantityPrecision: 2 },
CommoditySpot: { integerDigits: 7, precision: 4, quantityPrecision: 2 },
NewOtcStock: { integerDigits: 7, precision: 4, quantityPrecision: 2 },
HKStock: { integerDigits: 7, precision: 4, quantityPrecision: 2 },
HKStockIndex: { integerDigits: 7, precision: 4, quantityPrecision: 2 },
Fund: { integerDigits: 7, precision: 4, quantityPrecision: 4 },
Stock: { integerDigits: 7, precision: 2, quantityPrecision: 2, quantityIntegerDigits: 12 },
StockIndex: { integerDigits: 7, precision: 2, quantityPrecision: 2, quantityIntegerDigits: 12 },
StockIF: { integerDigits: 7, precision: 4, quantityPrecision: 2, quantityIntegerDigits: 12 },
CommodityFutures: { integerDigits: 7, precision: 4, quantityPrecision: 2, quantityIntegerDigits: 12 },
CommoditySpot: { integerDigits: 7, precision: 4, quantityPrecision: 2, quantityIntegerDigits: 12 },
NewOtcStock: { integerDigits: 7, precision: 4, quantityPrecision: 2, quantityIntegerDigits: 12 },
HKStock: { integerDigits: 7, precision: 4, quantityPrecision: 2, quantityIntegerDigits: 12 },
HKStockIndex: { integerDigits: 7, precision: 4, quantityPrecision: 2, quantityIntegerDigits: 12 },
Fund: { integerDigits: 7, precision: 4, quantityPrecision: 4, quantityIntegerDigits: 12 },
Bond: {
quantityPrecision: 0,
quantityPrecision: 0, quantityIntegerDigits: 16,
grossPrice: { integerDigits: 6, precision: 9 },
netPrice: { integerDigits: 6, precision: 9 },
yield: { integerDigits: 2, precision: 4 }
},
TBonds: {
quantityPrecision: 0,
quantityPrecision: 0, quantityIntegerDigits: 16,
grossPrice: { integerDigits: 6, precision: 9 },
netPrice: { integerDigits: 6, precision: 9 },
yield: { integerDigits: 2, precision: 4 }
},
CreditBonds: {
quantityPrecision: 0,
quantityPrecision: 0, quantityIntegerDigits: 16,
grossPrice: { integerDigits: 6, precision: 9 },
netPrice: { integerDigits: 6, precision: 9 },
yield: { integerDigits: 2, precision: 4 }
},
OtherBonds: {
quantityPrecision: 0,
quantityPrecision: 0, quantityIntegerDigits: 16,
grossPrice: { integerDigits: 6, precision: 9 },
netPrice: { integerDigits: 6, precision: 9 },
yield: { integerDigits: 2, precision: 4 }
},
TBFutures: { integerDigits: 8, precision: 4, quantityPrecision: 2 },
OtherFutures: { integerDigits: 8, precision: 4, quantityPrecision: 2 },
GoldSpot: { integerDigits: 8, precision: 4, quantityPrecision: 2 },
OtherSpot: { integerDigits: 8, precision: 4, quantityPrecision: 2 },
AbroadFutures: { integerDigits: 8, precision: 4, quantityPrecision: 2 },
AbroadSpot: { integerDigits: 8, precision: 4, quantityPrecision: 2 },
AbroadStock: { integerDigits: 8, precision: 2, quantityPrecision: 2 },
AbroadStockIndex: { integerDigits: 8, precision: 4, quantityPrecision: 2 },
ExRate: { integerDigits: 2, precision: 8, quantityPrecision: 8 },
Shibor: { integerDigits: 2, precision: 4, quantityPrecision: 2 },
FixingRepoRate: { integerDigits: 2, precision: 4, quantityPrecision: 2 },
RateYield: {integerDigits: 6, precision: 8, quantityPrecision: 2},
BondIndex: {integerDigits: 6, precision: 4, quantityPrecision: 2},
TBFutures: { integerDigits: 8, precision: 4, quantityPrecision: 2, quantityIntegerDigits: 12 },
OtherFutures: { integerDigits: 8, precision: 4, quantityPrecision: 2, quantityIntegerDigits: 12 },
GoldFutures: { quantityIntegerDigits: 12 },
GoldSpot: { integerDigits: 8, precision: 4, quantityPrecision: 2, quantityIntegerDigits: 12 },
OtherSpot: { integerDigits: 8, precision: 4, quantityPrecision: 2, quantityIntegerDigits: 12 },
AbroadFutures: { integerDigits: 8, precision: 4, quantityPrecision: 2, quantityIntegerDigits: 12 },
AbroadSpot: { integerDigits: 8, precision: 4, quantityPrecision: 2, quantityIntegerDigits: 12 },
AbroadStock: { integerDigits: 8, precision: 2, quantityPrecision: 2, quantityIntegerDigits: 12 },
AbroadStockIndex: { integerDigits: 8, precision: 4, quantityPrecision: 2, quantityIntegerDigits: 12 },
ExRate: { integerDigits: 2, precision: 8, quantityPrecision: 8, quantityIntegerDigits: 16 },
Shibor: { integerDigits: 2, precision: 4, quantityPrecision: 2, quantityIntegerDigits: 12 },
FixingRepoRate: { integerDigits: 2, precision: 4, quantityPrecision: 2, quantityIntegerDigits: 12 },
RateYield: {integerDigits: 6, precision: 8, quantityPrecision: 2, quantityIntegerDigits: 12},
BondIndex: {integerDigits: 6, precision: 4, quantityPrecision: 2, quantityIntegerDigits: 12},
};
@@ -0,0 +1,202 @@
const fs = require('fs');
const path = require('path');
const vm = require('vm');
const ROOT = path.resolve(__dirname, '..');
const read = (relativePath) => fs.readFileSync(path.join(ROOT, relativePath), 'utf8');
const precisionConfigSrc = read('App_Data/Config/swappriceprecision.js');
const precisionHelperSrc = read('wwwroot/Scripts/app/swaptrade/swapPricePrecisionHelper.js');
function loadConfiguredPrecision() {
const context = { window: {} };
vm.runInNewContext(precisionConfigSrc, context);
return context.window.main.swapPricePrecision;
}
function loadHelper(config) {
const context = { window: { main: { swapPricePrecision: config } } };
vm.runInNewContext(precisionHelperSrc, context);
return context.swapPricePrecision;
}
function activeViewSource(source) {
return source.replace(/@\*[\s\S]*?\*@/g, '');
}
describe('swap price precision common wiring', () => {
test('configured and fallback common precision expose 2/2/4', () => {
const configured = loadConfiguredPrecision();
expect(configured.common).toEqual({
amount: { precision: 2, grouping: true },
quantity: { integerDigits: 16, precision: 2, grouping: true },
rate: { precision: 4 }
});
const helper = loadHelper(configured);
expect(helper.getCommonPrecision('amount')).toBe(2);
expect(helper.getCommonPrecision('quantity')).toBe(2);
expect(helper.getCommonPrecision('rate')).toBe(4);
const expectedQuantityPrecisions = {
Stock: 2,
StockIndex: 2,
StockIF: 2,
CommodityFutures: 2,
CommoditySpot: 2,
NewOtcStock: 2,
HKStock: 2,
HKStockIndex: 2,
Fund: 4,
Bond: 0,
TBonds: 0,
CreditBonds: 0,
OtherBonds: 0,
TBFutures: 2,
OtherFutures: 2,
GoldSpot: 2,
OtherSpot: 2,
AbroadFutures: 2,
AbroadSpot: 2,
AbroadStock: 2,
AbroadStockIndex: 2,
ExRate: 8,
Shibor: 2,
FixingRepoRate: 2,
RateYield: 2,
BondIndex: 2
};
Object.entries(expectedQuantityPrecisions).forEach(([instrumentType, precision]) => {
expect(configured[instrumentType].quantityPrecision).toBe(precision);
expect(helper.getCommonPrecision('quantity', instrumentType)).toBe(precision);
expect(helper.getCommonInputFormat('quantity', { append: '' }, instrumentType).precision).toBe(precision);
});
const expectedQuantityIntegerDigits = {
Stock: 12, StockIndex: 12, StockIF: 12, CommodityFutures: 12, CommoditySpot: 12,
NewOtcStock: 12, HKStock: 12, HKStockIndex: 12, Fund: 12, TBFutures: 12,
OtherFutures: 12, GoldFutures: 12, GoldSpot: 12, OtherSpot: 12, AbroadFutures: 12,
AbroadSpot: 12, AbroadStock: 12, AbroadStockIndex: 12, Shibor: 12,
FixingRepoRate: 12, RateYield: 12, BondIndex: 12,
Bond: 16, TBonds: 16, CreditBonds: 16, OtherBonds: 16, ExRate: 16
};
Object.entries(expectedQuantityIntegerDigits).forEach(([instrumentType, integerDigits]) => {
expect(configured[instrumentType].quantityIntegerDigits).toBe(integerDigits);
expect(helper.getCommonInputFormat('quantity', { append: '' }, instrumentType).integerDigits).toBe(integerDigits);
});
expect(helper.getCommonPrecision('quantity', 'OtherRate')).toBe(2);
expect(helper.getCommonInputFormat('quantity', { append: '' }, 'Fund').precision).toBe(4);
expect(helper.getCommonInputFormat('quantity', { append: '' }, 'Bond').precision).toBe(0);
expect(helper.getCommonInputFormat('quantity', { append: '' }, 'OtherRate').integerDigits).toBe(16);
const input = helper.getCommonInputFormat('amount', { append: '', trimTailZeros: true });
expect(input).toEqual(expect.objectContaining({ precision: 2, grouping: true, trimTailZeros: false }));
expect(helper.getCommonInputFormat('rate', { append: '%' })).toEqual(
expect.objectContaining({ precision: 4, grouping: false }));
});
test('invalid common precision falls back and formatted values keep trailing zeros', () => {
const helper = loadHelper({
common: {
amount: { precision: -1 },
quantity: { precision: 14 },
rate: { precision: 'invalid' }
}
});
expect(helper.getCommonPrecision('amount')).toBe(2);
expect(helper.getCommonPrecision('quantity')).toBe(2);
expect(helper.getCommonPrecision('rate')).toBe(4);
const configured = loadHelper({
common: {
amount: { precision: 3 },
quantity: { precision: 1 },
rate: { precision: 2 }
}
});
expect(configured.formatCommon('amount', '12.5')).toBe('12.500');
expect(configured.formatCommon('quantity', 1)).toBe('1.0');
expect(configured.formatCommon('rate', '0.0123')).toBe('1.23%');
expect(configured.formatCommon('amount', '1234.5')).toBe('1,234.500');
expect(configured.normalizeCommon('amount', '1234.5')).toBe('1234.500');
expect(configured.formatCommon('amount', NaN)).toBe('');
expect(configured.formatCommon('amount', Infinity)).toBe('');
});
test('common grouping can be disabled independently from precision', () => {
const helper = loadHelper({
common: {
amount: { precision: 2, grouping: false },
quantity: { precision: 2, grouping: false },
rate: { precision: 4, grouping: false }
}
});
expect(helper.formatCommon('amount', '1234.5')).toBe('1234.50');
expect(helper.getCommonInputFormat('quantity', { append: '' }).grouping).toBe(false);
});
test('missing or invalid asset quantity precision falls back to common quantity precision', () => {
const helper = loadHelper({
common: { quantity: { precision: 3, grouping: true } },
Stock: { quantityPrecision: 'invalid' },
Bond: {}
});
expect(helper.getCommonPrecision('quantity', 'Stock')).toBe(3);
expect(helper.getCommonPrecision('quantity', 'Bond')).toBe(3);
expect(helper.getCommonPrecision('quantity', 'OtherRate')).toBe(3);
expect(helper.getCommonInputFormat('quantity', { append: '' }, 'Stock').integerDigits).toBe(16);
expect(loadHelper({
common: { quantity: { integerDigits: 0, precision: 3 } },
Stock: { quantityIntegerDigits: 'invalid' }
}).getCommonInputFormat('quantity', { append: '' }, 'Stock').integerDigits).toBe(16);
});
test('SwapTrade2 common fields use the price helper without changing price responsibilities', () => {
const scripts = {
edit: read('wwwroot/Scripts/app/swaptrade/swapTradeEdit.js'),
income: read('wwwroot/Scripts/app/swaptrade/incomeSwapTrade.js'),
unwind: read('wwwroot/Scripts/app/swaptrade/unwindSwapTrade.js'),
longShort: read('wwwroot/Scripts/app/swaptrade/swapLongShort.js'),
unwindLongShort: read('wwwroot/Scripts/app/swaptrade/unwindLongShort.js'),
view: read('wwwroot/Scripts/app/swaptrade/swapTradeView.js'),
flow: read('wwwroot/Scripts/app/swaptrade/SwapflowList.js'),
helper: precisionHelperSrc
};
const view = read('Views/SwapTrade2/TradeView.cshtml');
const activeView = activeViewSource(view);
for (const script of Object.values(scripts)) {
expect(script).not.toMatch(/otcformat\.trading\.swap(?:Amount|Quantity|RateP)/);
}
expect(scripts.edit).toMatch(/getCommonInputFormat\('amount'/);
expect(scripts.edit).toMatch(/getCommonInputFormat\(\s*['"]quantity['"]/);
expect(scripts.edit).toMatch(/getCommonInputFormat\('rate'/);
expect(scripts.view).toContain("swapPricePrecision.formatCommon('rate', item.Rate)");
expect(scripts.flow).toMatch(/name:\s*'ytm'[\s\S]*formatter:\s*otcformat\.trading\.marginRateP/);
expect(scripts.flow).not.toMatch(/name:\s*'ytm'[\s\S]*formatCommon\('rate'/);
expect(scripts.unwind).toContain('ClosePercent = otcformat.fixed6');
expect(scripts.unwind).toContain('_.round(tradingFee, 2)');
expect(scripts.unwind).toContain('.toFixed(2)');
expect(scripts.edit).toContain('observationRatePrecision = 12');
expect(scripts.edit).toMatch(/roundObservationRate[\s\S]*swapPricePrecision\.roundDecimal/);
expect(scripts.helper).toContain('getRule: getRule');
expect(scripts.helper).toContain('getInputFormat: function');
expect(scripts.helper).toContain('roundForSubmit: function');
expect(scripts.helper).toContain('normalizeCommon: function');
expect(activeView).not.toContain('OtcFormatHelper');
expect(activeView).not.toMatch(/OtcFormatMoney|OtcFormatNotional/);
expect(activeView).toContain('SwapCommonData');
expect(activeView).toMatch(/js-swap-common[\s\S]*data-kind="amount"/);
expect(activeView).toMatch(/js-swap-common[\s\S]*data-kind="quantity"/);
expect(activeView).toMatch(/js-swap-common[\s\S]*data-kind="rate"/);
expect(scripts.view).toContain('formatSwapCommonElements');
expect(scripts.edit).toContain('getQuantityInputFormat');
expect(scripts.unwind).toContain('getQuantityInputFormat');
expect(scripts.flow).toContain('getQuantityInputFormat');
expect(scripts.flow).toMatch(/formatSwapQuantity = function \(value, options, rowObject\)/);
expect(scripts.flow).toMatch(/row\.UnderlyingInstrumentType[\s\S]*row\.InstrumentType[\s\S]*row\.position[\s\S]*UnderlyingInstrumentType/);
expect(scripts.flow).toContain("formatCommon('quantity', value, instrumentType)");
expect(activeView).toMatch(/data-kind="quantity"[^>]*data-instrument-type=/);
});
});
@@ -2,55 +2,56 @@ var swapPricePrecision = (function (global) {
const defaults = Object.freeze({
common: {
amount: { precision: 2, grouping: true },
quantity: { precision: 2, grouping: true },
quantity: { integerDigits: 16, precision: 2, grouping: true },
rate: { precision: 4 }
},
Stock: { integerDigits: 7, precision: 2, quantityPrecision: 2 },
StockIndex: { integerDigits: 7, precision: 2, quantityPrecision: 2 },
StockIF: { integerDigits: 7, precision: 4, quantityPrecision: 2 },
CommodityFutures: { integerDigits: 7, precision: 4, quantityPrecision: 2 },
CommoditySpot: { integerDigits: 7, precision: 4, quantityPrecision: 2 },
NewOtcStock: { integerDigits: 7, precision: 4, quantityPrecision: 2 },
HKStock: { integerDigits: 7, precision: 4, quantityPrecision: 2 },
HKStockIndex: { integerDigits: 7, precision: 4, quantityPrecision: 2 },
Fund: { integerDigits: 7, precision: 4, quantityPrecision: 4 },
Stock: { integerDigits: 7, precision: 2, quantityPrecision: 2, quantityIntegerDigits: 12 },
StockIndex: { integerDigits: 7, precision: 2, quantityPrecision: 2, quantityIntegerDigits: 12 },
StockIF: { integerDigits: 7, precision: 4, quantityPrecision: 2, quantityIntegerDigits: 12 },
CommodityFutures: { integerDigits: 7, precision: 4, quantityPrecision: 2, quantityIntegerDigits: 12 },
CommoditySpot: { integerDigits: 7, precision: 4, quantityPrecision: 2, quantityIntegerDigits: 12 },
NewOtcStock: { integerDigits: 7, precision: 4, quantityPrecision: 2, quantityIntegerDigits: 12 },
HKStock: { integerDigits: 7, precision: 4, quantityPrecision: 2, quantityIntegerDigits: 12 },
HKStockIndex: { integerDigits: 7, precision: 4, quantityPrecision: 2, quantityIntegerDigits: 12 },
Fund: { integerDigits: 7, precision: 4, quantityPrecision: 4, quantityIntegerDigits: 12 },
Bond: {
quantityPrecision: 0,
quantityPrecision: 0, quantityIntegerDigits: 16,
grossPrice: { integerDigits: 6, precision: 9 },
netPrice: { integerDigits: 6, precision: 9 },
yield: { integerDigits: 2, precision: 4 }
},
TBonds: {
quantityPrecision: 0,
quantityPrecision: 0, quantityIntegerDigits: 16,
grossPrice: { integerDigits: 6, precision: 9 },
netPrice: { integerDigits: 6, precision: 9 },
yield: { integerDigits: 2, precision: 4 }
},
CreditBonds: {
quantityPrecision: 0,
quantityPrecision: 0, quantityIntegerDigits: 16,
grossPrice: { integerDigits: 6, precision: 9 },
netPrice: { integerDigits: 6, precision: 9 },
yield: { integerDigits: 2, precision: 4 }
},
OtherBonds: {
quantityPrecision: 0,
quantityPrecision: 0, quantityIntegerDigits: 16,
grossPrice: { integerDigits: 6, precision: 9 },
netPrice: { integerDigits: 6, precision: 9 },
yield: { integerDigits: 2, precision: 4 }
},
TBFutures: { integerDigits: 8, precision: 4, quantityPrecision: 2 },
OtherFutures: { integerDigits: 8, precision: 4, quantityPrecision: 2 },
GoldSpot: { integerDigits: 8, precision: 4, quantityPrecision: 2 },
OtherSpot: { integerDigits: 8, precision: 4, quantityPrecision: 2 },
AbroadFutures: { integerDigits: 8, precision: 4, quantityPrecision: 2 },
AbroadSpot: { integerDigits: 8, precision: 4, quantityPrecision: 2 },
AbroadStock: { integerDigits: 8, precision: 2, quantityPrecision: 2 },
AbroadStockIndex: { integerDigits: 8, precision: 4, quantityPrecision: 2 },
ExRate: { integerDigits: 2, precision: 8, quantityPrecision: 8 },
Shibor: { integerDigits: 2, precision: 4, quantityPrecision: 2 },
FixingRepoRate: { integerDigits: 2, precision: 4, quantityPrecision: 2 },
RateYield: {integerDigits: 6, precision: 8, quantityPrecision: 2},
BondIndex: {integerDigits: 6, precision: 4, quantityPrecision: 2},
TBFutures: { integerDigits: 8, precision: 4, quantityPrecision: 2, quantityIntegerDigits: 12 },
OtherFutures: { integerDigits: 8, precision: 4, quantityPrecision: 2, quantityIntegerDigits: 12 },
GoldFutures: { quantityIntegerDigits: 12 },
GoldSpot: { integerDigits: 8, precision: 4, quantityPrecision: 2, quantityIntegerDigits: 12 },
OtherSpot: { integerDigits: 8, precision: 4, quantityPrecision: 2, quantityIntegerDigits: 12 },
AbroadFutures: { integerDigits: 8, precision: 4, quantityPrecision: 2, quantityIntegerDigits: 12 },
AbroadSpot: { integerDigits: 8, precision: 4, quantityPrecision: 2, quantityIntegerDigits: 12 },
AbroadStock: { integerDigits: 8, precision: 2, quantityPrecision: 2, quantityIntegerDigits: 12 },
AbroadStockIndex: { integerDigits: 8, precision: 4, quantityPrecision: 2, quantityIntegerDigits: 12 },
ExRate: { integerDigits: 2, precision: 8, quantityPrecision: 8, quantityIntegerDigits: 16 },
Shibor: { integerDigits: 2, precision: 4, quantityPrecision: 2, quantityIntegerDigits: 12 },
FixingRepoRate: { integerDigits: 2, precision: 4, quantityPrecision: 2, quantityIntegerDigits: 12 },
RateYield: {integerDigits: 6, precision: 8, quantityPrecision: 2, quantityIntegerDigits: 12},
BondIndex: {integerDigits: 6, precision: 4, quantityPrecision: 2, quantityIntegerDigits: 12},
});
@@ -134,7 +135,12 @@ var swapPricePrecision = (function (global) {
if (!rule || typeof rule !== 'object') return null;
const precision = Number(rule.precision);
if (!Number.isInteger(precision) || precision < 0 || precision > 13) return null;
return { precision: precision, grouping: typeof rule.grouping === 'boolean' ? rule.grouping : undefined };
const integerDigits = Number(rule.integerDigits);
return {
precision: precision,
integerDigits: Number.isInteger(integerDigits) && integerDigits >= 1 && integerDigits <= 18 ? integerDigits : undefined,
grouping: typeof rule.grouping === 'boolean' ? rule.grouping : undefined
};
}
function findRule(source, instrumentType, field) {
@@ -176,6 +182,15 @@ var swapPricePrecision = (function (global) {
return Number.isInteger(precision) && precision >= 0 && precision <= 13 ? precision : fallback;
}
function getQuantityIntegerDigits(instrumentType, fallback) {
if (!instrumentType) return fallback;
const configured = global.main && global.main.swapPricePrecision;
const typeRule = (configured || defaults)[instrumentType];
if (!typeRule || !Object.prototype.hasOwnProperty.call(typeRule, 'quantityIntegerDigits')) return fallback;
const integerDigits = Number(typeRule.quantityIntegerDigits);
return Number.isInteger(integerDigits) && integerDigits >= 1 && integerDigits <= 18 ? integerDigits : fallback;
}
function getCommonRule(kind, instrumentType) {
const fallback = normalizeCommonRule(defaults.common[kind]) || { precision: 2, grouping: false };
const configured = global.main && global.main.swapPricePrecision
@@ -183,7 +198,11 @@ var swapPricePrecision = (function (global) {
const configuredRule = normalizeCommonRule(configured && configured[kind]);
const rule = configuredRule || fallback;
if (rule.grouping === undefined) rule.grouping = fallback.grouping;
if (kind === 'quantity') rule.precision = getQuantityPrecision(instrumentType, rule.precision);
if (rule.integerDigits === undefined) rule.integerDigits = fallback.integerDigits;
if (kind === 'quantity') {
rule.precision = getQuantityPrecision(instrumentType, rule.precision);
rule.integerDigits = getQuantityIntegerDigits(instrumentType, rule.integerDigits);
}
return rule;
}
@@ -338,11 +357,13 @@ var swapPricePrecision = (function (global) {
const type = instrumentType || inputOptions.instrumentType;
delete inputOptions.instrumentType;
const rule = getCommonRule(kind, type);
return Object.assign(inputOptions, {
const result = Object.assign(inputOptions, {
precision: rule.precision,
grouping: inputOptions.grouping === undefined ? !!rule.grouping : !!inputOptions.grouping,
trimTailZeros: false
});
if (rule.integerDigits !== undefined) result.integerDigits = rule.integerDigits;
return result;
},
formatCommon: formatCommon,
normalizeCommon: function (kind, value, instrumentType) {
@@ -297,6 +297,8 @@
!options.append && (options.append = '');
let precision = parseInt(options.precision) || 0;
options.precision = precision < 0 ? 0 : precision;
let integerDigits = parseInt(options.integerDigits) || 0;
options.integerDigits = integerDigits > 0 ? integerDigits : 0;
options.negative = !!options.negative;
_options = options;
setValue(getValue());
@@ -336,6 +338,35 @@
return this.value.substring(0, this.selectionStart).indexOf('.') < 0 && this.value.substring(this.selectionEnd, this.value.length).indexOf('.') < 0;
}
function checkIntegerDigitsInput() {
if (!_options.integerDigits) return true;
let valueEnd = this.value.length;
if (_options.append && this.value.endsWith(_options.append)) valueEnd -= _options.append.length;
const dotIndex = this.value.indexOf('.');
const integerEnd = dotIndex < 0 ? valueEnd : dotIndex;
if (this.selectionStart > integerEnd) return true;
const selectionEnd = Math.min(this.selectionEnd, integerEnd);
const integerText = this.value.substring(0, this.selectionStart) + this.value.substring(selectionEnd, integerEnd);
return integerText.replace(/\D/g, '').length < _options.integerDigits;
}
function limitIntegerDigits(value) {
if (!_options.integerDigits) return value;
let digits = 0;
let hasDot = false;
let result = '';
for (let index = 0; index < value.length; index++) {
const ch = value.charAt(index);
if (ch === '.' || ch === '。') hasDot = true;
if (ch >= '0' && ch <= '9' && !hasDot) {
if (digits >= _options.integerDigits) continue;
digits++;
}
result += ch;
}
return result;
}
function __keyHandle(event) {
if (!event) return false;
@@ -385,6 +416,7 @@
if (charCode < 48 || charCode > 57 && (charCode < 96 || charCode > 105)) {
return false;
}
if (!checkIntegerDigitsInput.call(this)) return false;
if (this.selectionStart === 0) {
return !this.value || this.value.charAt(this.selectionEnd) !== '-';
}
@@ -445,6 +477,7 @@
// 粘贴进来的是显示值(可能带 %/‱ 后缀),先按 __change 同款口径解析为模型值再回显,
// 避免 setValue 把显示值再乘以 100/10000#EQD-5914 债券价格粘贴 ×100
let f = '';
this.value = limitIntegerDigits(this.value);
if (this.value && this.value !== _options.append) {
f = parseFloat(this.value.replaceAll(",", "")) || 0;
if (_options.append === '%' || _options.percent == true) f /= 100;
@@ -455,6 +488,7 @@
if (_chnInput >= 0) {
__onChineseInput.call(this, _chnInput);
}
this.value = limitIntegerDigits(this.value);
if (!_options.append || !this.value) return;
let appended = true;
if (this.value !== _options.append) {
@@ -14354,6 +14354,8 @@ $.fn.selectpicker.Constructor.DEFAULTS = Object.assign($.fn.selectpicker.Constru
!options.append && (options.append = '');
let precision = parseInt(options.precision) || 0;
options.precision = precision < 0 ? 0 : precision;
let integerDigits = parseInt(options.integerDigits) || 0;
options.integerDigits = integerDigits > 0 ? integerDigits : 0;
options.negative = !!options.negative;
_options = options;
setValue(getValue());
@@ -14393,6 +14395,35 @@ $.fn.selectpicker.Constructor.DEFAULTS = Object.assign($.fn.selectpicker.Constru
return this.value.substring(0, this.selectionStart).indexOf('.') < 0 && this.value.substring(this.selectionEnd, this.value.length).indexOf('.') < 0;
}
function checkIntegerDigitsInput() {
if (!_options.integerDigits) return true;
let valueEnd = this.value.length;
if (_options.append && this.value.endsWith(_options.append)) valueEnd -= _options.append.length;
const dotIndex = this.value.indexOf('.');
const integerEnd = dotIndex < 0 ? valueEnd : dotIndex;
if (this.selectionStart > integerEnd) return true;
const selectionEnd = Math.min(this.selectionEnd, integerEnd);
const integerText = this.value.substring(0, this.selectionStart) + this.value.substring(selectionEnd, integerEnd);
return integerText.replace(/\D/g, '').length < _options.integerDigits;
}
function limitIntegerDigits(value) {
if (!_options.integerDigits) return value;
let digits = 0;
let hasDot = false;
let result = '';
for (let index = 0; index < value.length; index++) {
const ch = value.charAt(index);
if (ch === '.' || ch === '。') hasDot = true;
if (ch >= '0' && ch <= '9' && !hasDot) {
if (digits >= _options.integerDigits) continue;
digits++;
}
result += ch;
}
return result;
}
function __keyHandle(event) {
if (!event) return false;
@@ -14442,6 +14473,7 @@ $.fn.selectpicker.Constructor.DEFAULTS = Object.assign($.fn.selectpicker.Constru
if (charCode < 48 || charCode > 57 && (charCode < 96 || charCode > 105)) {
return false;
}
if (!checkIntegerDigitsInput.call(this)) return false;
if (this.selectionStart === 0) {
return !this.value || this.value.charAt(this.selectionEnd) !== '-';
}
@@ -14502,6 +14534,7 @@ $.fn.selectpicker.Constructor.DEFAULTS = Object.assign($.fn.selectpicker.Constru
// 粘贴进来的是显示值(可能带 %/‱ 后缀),先按 __change 同款口径解析为模型值再回显,
// 避免 setValue 把显示值再乘以 100/10000#EQD-5914 债券价格粘贴 ×100
let f = '';
this.value = limitIntegerDigits(this.value);
if (this.value && this.value !== _options.append) {
f = parseFloat(this.value.replaceAll(",", "")) || 0;
if (_options.append === '%' || _options.percent == true) f /= 100;
@@ -14512,6 +14545,7 @@ $.fn.selectpicker.Constructor.DEFAULTS = Object.assign($.fn.selectpicker.Constru
if (_chnInput >= 0) {
__onChineseInput.call(this, _chnInput);
}
this.value = limitIntegerDigits(this.value);
if (!_options.append || !this.value) return;
let appended = true;
if (this.value !== _options.append) {