feat(input): 数字输入整数位数限制功能
- 在 fastVue.base.js 中实现整数位数配置选项 integerDigits - 添加 checkIntegerDigitsInput 函数验证整数位数输入限制 - 实现 limitIntegerDigits 函数控制数值字符串中的整数位数 - 在键盘处理和粘贴事件中集成整数位数限制逻辑 - 更新掉期交易价格精度配置支持 quantityIntegerDigits 选项 - 扩展精度辅助函数支持整数位数配置和验证规则 - 添加完整的单元测试覆盖整数位数限制功能
This commit is contained in:
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user