//定价格式化(来自配置) const inputFormatPercent = Object.freeze({ precision: 2, append: '%' }); const inputFormatPercentNegative = Object.freeze({ precision: 2, append: '%', negative: true }); const inputFormatNegative = Object.freeze({ precision: 2, negative: true }); function normalizeBookIds(values) { var ids = Array.isArray(values) ? values : (values ? [values] : []); var normalized = []; ids.forEach(function (id) { id = (id || '').toString().trim(); if (id && normalized.indexOf(id) < 0) { normalized.push(id); } }); return normalized.length ? normalized.join(',') : null; } const MarginRuleTypeEnum = { 无预付金: "0", 按固定利率: "1", 按浮动盈亏: "2", 类香草预付金规则: "3", 自动赎回买入预付金规则: "4", 触发条件预付金规则1: "5", 触发条件预付金规则2: "6", 保底预付金规则: "7", 含赔付预付金计算规则: "8", 触发条件预付金规则3: "9", 交易所基本规则: "10", 标准SPAN: "11", 买入自动赎回规则1: "12", 卖出自动赎回规则1: "13", 区间追保结构: "15", 默认预付金规则: "99" }; const UnderlyingSeperateTypeEnum = { None: "0",//不区分 CustomStock: "1",//按自定义权益分类 CustomInstrumentType: "2"//按资产类型分类 }; //允许配置期限档(SpanConfig.BondTerm)的标的资产类型标志位掩码, //与后端 ConsMarginTerm.TermTierEnabledUnderlyingTypes 保持一致(本期仅 利率债 TBonds=1<<4=16) const TermTierEnabledUnderlyingMask = 16; //基金及基金专户标志位(ETF 子类行的 UnderlyingType,不加新枚举位) const FundTypeMask = 32768; //利率债区块固定展开的4个期限档([BondTerm值, 展示文案]),与 docx 原型 4.3.1 一致 const SpanBondTerms = [['<5y', '≤5y'], ['5y-10y', '(5y-10y]'], ['10y-30y', '(10y-30y]'], ['>30y', '>30y']]; //区块键自增序号(仅前端分组用,不持久化) var spanBlockSeq = 0; //标的资产类型标志位 -> 名称(与明细表格 chosen 选项一致,用于规则15录入区块标题) const UnderlyingTypeNames = [ [16, "利率债"], [32, "信用债"], [64, "其它债券"], [128, "股票"], [256, "股指"], [512, "股指期货"], [1024, "商品期货"], [2048, "商品现货"], [4096, "新三板挂牌股票"], [8192, "香港股票"], [16384, "香港股指"], [32768, "基金及基金专户"], [65536, "黄金期货"], [131072, "国债期货"], [262144, "其他期货"], [524288, "黄金现货"], [1048576, "其他现货"], [2097152, "境外期货"], [4194304, "境外现货"], [8388608, "境外股票"], [16777216, "境外股指"], [33554432, "汇率"], [67108864, "Shibor"], [134217728, "银行间回购定盘"], [268435456, "利率收益率"], [536870912, "债券指数"] ]; const vue = new Vue({ el: '#marginTemplateV2Form', data: { isAdd: page.isAdd, marginTemplate: page.marginTemplate, etfSubtypeItems: page.etfSubtypeItems || [] }, created: function () { //留档进入页面时的生效状态:v-model 改写的就是 page.marginTemplate 同一对象(data 浅拷贝的是引用), //原始值须在用户交互前记下,供保存时判断"生效中规则被改为否"弹失效确认; //select 的 v-model 取值是字符串,统一 String 化比较 this.originalIsValid = String(this.marginTemplate.IsValid); //区间追保结构:加载时按 资产类型+期限 排序一次,再重建资产类型区块(利率债区块补齐固定4档), //编辑过程中不再重排,避免 chosen/选择器失序 if (this.marginTemplate.RuleType == MarginRuleTypeEnum.区间追保结构 && this.marginTemplate.Details) { var termOrder = ['', '<5y', '5y-10y', '10y-30y', '>30y']; var that = this; this.marginTemplate.Details.forEach(function (d) { //兜底:历史数据可能没有 SpanConfig / 区间录入结构,补齐避免录入区块不渲染 that.ensureSpanDetail(d); }); this.marginTemplate.Details.sort(function (a, b) { var at = a.UnderlyingType || 0, bt = b.UnderlyingType || 0; if (at !== bt) return at - bt; var ai = termOrder.indexOf((a.SpanConfig && a.SpanConfig.BondTerm) || ''); var bi = termOrder.indexOf((b.SpanConfig && b.SpanConfig.BondTerm) || ''); return (ai < 0 ? termOrder.length : ai) - (bi < 0 ? termOrder.length : bi); }); this.marginTemplate.Details = this.rebuildSpanBlocks(this.marginTemplate.Details); } this.changeRuleType(true); }, computed: { //区间追保结构:参数组按资产类型区块展示,相邻且区块键(_bk)相同的行合并(rowspan)。 //_bk 由 加载重建/新增/切换资产类型 时维护:利率债区块含固定4档行,其他区块单行。 ruleRangeDetailRows() { var details = this.marginTemplate.Details || []; var keyOf = function (d, i) { return d._bk || ('idx_' + i); }; var rows = []; for (var i = 0; i < details.length; i++) { var key = keyOf(details[i], i); if (i > 0 && keyOf(details[i - 1], i - 1) === key) { rows.push({ detail: details[i], index: i, rowspan: 0 }); continue; } var span = 1; for (var j = i + 1; j < details.length; j++) { if (keyOf(details[j], j) !== key) break; span++; } rows.push({ detail: details[i], index: i, rowspan: span }); } return rows; }, //区间追保结构录入区块:每个资产类型区块一个;纯利率债(ut=16)区块固定展开4个期限档段(每段一条 detail), //其他区块(含全部 ETF 子类,2026-08-27 裁定 ETF 无期限概念不再分档)单段(BondTerm 为空)。 //段的 detail 缺失时模板侧 v-if 兜底不渲染该段 ruleRangeBlocks() { if (this.marginTemplate.RuleType != MarginRuleTypeEnum.区间追保结构) return []; var blocks = []; var rows = this.ruleRangeDetailRows; var details = this.marginTemplate.Details || []; for (var i = 0; i < rows.length; i++) { if (rows[i].rowspan <= 0) continue; var head = rows[i]; var ut = head.detail.UnderlyingType || 0; var kind = (head.detail.SpanConfig && head.detail.SpanConfig.EtfKind) || ''; var isTBond = ut === 16; var sections = []; if (isTBond) { for (var t = 0; t < SpanBondTerms.length; t++) { var found = null; for (var j = head.index; j < head.index + head.rowspan; j++) { var bt = (details[j].SpanConfig && details[j].SpanConfig.BondTerm) || ''; if (bt === SpanBondTerms[t][0]) { found = details[j]; break; } } sections.push({ key: SpanBondTerms[t][0], termLabel: SpanBondTerms[t][1], detail: found }); } } else { sections.push({ key: 'single', termLabel: '', detail: head.detail }); } blocks.push({ key: head.detail._bk || ('i' + head.index), no: blocks.length + 1, headIndex: head.index, ut: ut, etfKind: kind, isTBond: isTBond, isFundBlock: ut === FundTypeMask, sections: sections }); } return blocks; } }, methods: { changeRuleType(isInitial) { switch (this.marginTemplate.RuleType.toString()) { case MarginRuleTypeEnum.无预付金: this.marginTemplate.UnderlyingSeperateType = UnderlyingSeperateTypeEnum.None; this.marginTemplate.Comments = "预付金占用为零。"; break; case MarginRuleTypeEnum.按固定利率: this.marginTemplate.Comments = "预付金占用为成交/实时名义本金的固定比例。"; break; case MarginRuleTypeEnum.按浮动盈亏: this.marginTemplate.UnderlyingSeperateType = UnderlyingSeperateTypeEnum.None; this.marginTemplate.Comments = "预付金占用为成交/实时名义本金的固定比例,加上对手方交易浮动亏损,扣除对手方交易浮动盈利。"; break; case MarginRuleTypeEnum.类香草预付金规则: this.marginTemplate.UnderlyingSeperateType = UnderlyingSeperateTypeEnum.None; this.marginTemplate.Comments = "预付金占用为为成交/实时名义本金的浮动比例,加上对手方交易浮动亏损。浮动比例依据标的涨跌停情况判断。"; break; case MarginRuleTypeEnum.自动赎回买入预付金规则: this.marginTemplate.UnderlyingSeperateType = UnderlyingSeperateTypeEnum.None; this.marginTemplate.Comments = "预付金占用为n+2(≤N)个票息之和,N为合约期内敲出观察日总数,n为当前经过的敲出观察日个数(当n+2>N时,n = N - 2)。"; break; case MarginRuleTypeEnum.触发条件预付金规则1: this.marginTemplate.UnderlyingSeperateType = UnderlyingSeperateTypeEnum.None; this.marginTemplate.Comments = "预付金占用为成交名义本金的固定比例,加标的跌幅的市值(跌幅达到一定条件触发)"; break; case MarginRuleTypeEnum.触发条件预付金规则2: this.marginTemplate.UnderlyingSeperateType = UnderlyingSeperateTypeEnum.None; this.marginTemplate.Comments = "预付金占用为成交/实时名义本金的固定比例,加标的跌幅的市值(跌幅达到一定条件触发)"; break; case MarginRuleTypeEnum.触发条件预付金规则3: this.marginTemplate.UnderlyingSeperateType = UnderlyingSeperateTypeEnum.None; this.marginTemplate.Comments = "预付金占用为成交/实时名义本金的固定比例,加上对手方分段式交易浮动亏损(涨跌幅达到一定条件触发)"; break; case MarginRuleTypeEnum.保底预付金规则: this.marginTemplate.UnderlyingSeperateType = UnderlyingSeperateTypeEnum.None; this.marginTemplate.Comments = "预付金占用为对手方最大亏损金额。"; break; case MarginRuleTypeEnum.含赔付预付金计算规则: this.marginTemplate.UnderlyingSeperateType = UnderlyingSeperateTypeEnum.None; this.marginTemplate.Comments = "预付金占用为对手方最大赔付金额。"; break; case MarginRuleTypeEnum.交易所基本规则: this.marginTemplate.UnderlyingSeperateType = UnderlyingSeperateTypeEnum.None; this.marginTemplate.Comments = "交易所基本规则"; break; case MarginRuleTypeEnum.标准SPAN: this.marginTemplate.Comments = "计算标的价格和波动率上涨下跌4个场景下的对手方最大亏损"; break; case MarginRuleTypeEnum.买入自动赎回规则1: this.marginTemplate.UnderlyingSeperateType = UnderlyingSeperateTypeEnum.None; this.marginTemplate.Comments = "Min(x*首次票息金额+y*Max(0,Pv),合约期间全部票息金额);\n若勾选了“取与保底金额孰小”,则还要与取较小的一个;\n保底金额 =ABS(执行价格 - 保底价格)* ABS(交易数量)。"; break; case MarginRuleTypeEnum.卖出自动赎回规则1: this.marginTemplate.Comments = "名义本金*Max(x,y-0.5*虚值比例)+ Flag*Max(0,PV);\n其中虚值比例 = max(0, P - 执行价)/期初价格,当计算日为成交日时,Flag = 0,P = 期初价格,当计算日不为成交日时,Flag = 1,P = 标的现价;\n若勾选了“取与保底金额孰小”,则取较小的一个;\n保底金额 =ABS(执行价格 - 保底价格)* ABS(交易数量)。"; break; case MarginRuleTypeEnum.区间追保结构: this.marginTemplate.Comments = "预付金占用为名义本金的固定比例。按利率债期限(<5y/5y-10y/10y-30y/>30y)分别设置预警线/平仓线与多空各4层追保价格区间及追保金额比例,支持再按标的资产类型(利率债/信用债/其它债券/股票/股指等全部资产类型)分组;以期初净价为基准,标的价格落入录入的追保区间时按对应比例追保,触及预警线/平仓线时触发预警或平仓。"; break; case MarginRuleTypeEnum.默认预付金规则: this.marginTemplate.Comments = "考虑了14种场景下的Span算法与Delta算法比较的通用预付金规则,该规则可以调整Span涨跌幅度"; break; default: this.marginTemplate.Comments = ""; break; } this.changeUnderlyingSeperateType(isInitial); }, changeUnderlyingSeperateType(isInitial) { if (isInitial && this.marginTemplate.Details.length >= 1) { return; } if (this.marginTemplate.UnderlyingSeperateType == UnderlyingSeperateTypeEnum.None) { this.marginTemplate.UnderlyingSeperateComments = "无论挂钩标的代码为何,都采用同一套参数"; } else if (this.marginTemplate.UnderlyingSeperateType == UnderlyingSeperateTypeEnum.CustomInstrumentType) { this.marginTemplate.UnderlyingSeperateComments = "按标的的资产类型分类:在每组参数中选择适用的资产类型(利率债/信用债/其它债券/股票/股指/商品期货等全部资产类型),按标的资产类型匹配对应参数组的区间追保参数。"; } else { this.marginTemplate.UnderlyingSeperateComments = "把所有标的根据代码所属区间段自动分为4类:\n【1-指数】沪深指数;\n【2-个股/ETF-核准制发行】主板股票、非科创版ETF;\n【3-个股/ETF-非核准制发行】科创版股票、科创版ETF、创业板股票;\n【4-其他】所有不满足以上分类的。"; } let that = this new Promise((reslove) => { that.marginTemplate.Details = []; reslove(); }).then(() => { var needDetailRules = []; needDetailRules.push(MarginRuleTypeEnum.按固定利率); needDetailRules.push(MarginRuleTypeEnum.按浮动盈亏); needDetailRules.push(MarginRuleTypeEnum.类香草预付金规则); needDetailRules.push(MarginRuleTypeEnum.触发条件预付金规则1); needDetailRules.push(MarginRuleTypeEnum.触发条件预付金规则2); needDetailRules.push(MarginRuleTypeEnum.触发条件预付金规则3); needDetailRules.push(MarginRuleTypeEnum.标准SPAN); needDetailRules.push(MarginRuleTypeEnum.买入自动赎回规则1); needDetailRules.push(MarginRuleTypeEnum.卖出自动赎回规则1); needDetailRules.push(MarginRuleTypeEnum.区间追保结构); needDetailRules.push(MarginRuleTypeEnum.默认预付金规则); if (needDetailRules.indexOf(that.marginTemplate.RuleType.toString()) >= 0) { new Promise((reslove) => { //规则15:新增的是"资产类型区块"(含区间录入结构与独立区块键);其他规则维持原样 if (that.marginTemplate.RuleType == MarginRuleTypeEnum.区间追保结构) { var d = that.newSpanDetail(0, ''); Vue.set(d, '_bk', 'bk_' + (++spanBlockSeq)); that.marginTemplate.Details.push(d); } else { that.marginTemplate.Details.push(_.cloneDeep(page.detail)); } reslove(); }).then(() => { SetAceDropDown(); }) } }) }, saveMarginTemplateV2(isForClient) { //失效确认:编辑中把"生效中"规则改为否时先弹窗提示降级影响,确认后才真正提交; //新增规则尚未生效、生效状态未翻转,不弹。自定义规则编辑页无"是否生效"开关,不经过此逻辑 if (!this.isAdd && this.originalIsValid === 'true' && String(this.marginTemplate.IsValid) === 'false') { var that = this; main.confirm("该规则失效后,适用范围内的持仓将降级匹配下一优先级规则(客户→全局),确认继续?", function () { that.doSaveMarginTemplateV2(isForClient); }); return; } this.doSaveMarginTemplateV2(isForClient); }, doSaveMarginTemplateV2(isForClient) { if (!this.marginTemplate.Name) { main.message("模板名称不能为空"); return; } this.marginTemplate.TradeTypes = $("#tradeTypes").val().join(","); this.marginTemplate.BookIds = normalizeBookIds($("#BookIds").val()); this.marginTemplate.IsDefault = true; this.marginTemplate.IsForClient = isForClient; this.marginTemplate.BuySellType = this.sum($("#BuySellType").val()); this.marginTemplate.MarginScenarios = this.sum($("#MarginScenarios").val()); var that = this; this.marginTemplate.Details.forEach((detail, index) => { if (that.marginTemplate.UnderlyingSeperateType == 0) { detail.UnderlyingType = 15; } detail.SpanConfigJson = JSON.stringify(detail.SpanConfig); }); if (that.marginTemplate.UnderlyingSeperateType != 0) { if (that.marginTemplate.RuleType == MarginRuleTypeEnum.区间追保结构) { //区间追保结构表格:按区块取值——区块首行读选择器,被合并的档位行跟随本区块(区块内资产类型一致) var groupValue = 0; that.ruleRangeDetailRows.forEach((row) => { if (row.rowspan > 0) { var ut = $("#UnderlyingType" + row.index).val(); groupValue = ut ? that.sum(ut) : (row.detail.UnderlyingType || 0); //与 onUnderlyingTypeChange 同口径兜底:含利率债(位16)时只能单选利率债 if ((groupValue & TermTierEnabledUnderlyingMask) > 0) groupValue = TermTierEnabledUnderlyingMask; } row.detail.UnderlyingType = groupValue; }); } else { that.marginTemplate.Details.forEach((detail, index) => { detail.UnderlyingType = that.sum($("#UnderlyingType" + index).val()); }); } } main.post("/margin_template_v2/saveMarginTemplateV2", { marginTemplate: this.marginTemplate }).done(function (resp) { try { window.parent.reloadmargin_template(); (parent || window).layer.closeAll(); } catch (e) { } }); }, addDetail() { let that = this new Promise((reslove) => { //规则15:新增参数组=新增一个资产类型区块(默认未选资产类型,单行;选成纯利率债时自动展开4档) if (that.marginTemplate.RuleType == MarginRuleTypeEnum.区间追保结构) { var d = that.newSpanDetail(0, ''); Vue.set(d, '_bk', 'bk_' + (++spanBlockSeq)); that.marginTemplate.Details.push(d); } else { that.marginTemplate.Details.push(_.cloneDeep(page.detail)); } reslove(); }).then(() => { that.refreshChosen(); }) }, deleteDetail(index) { //规则15:删除区块=删除该区块全部 detail 行(利率债区块4行、其他区块1行) if (this.marginTemplate.RuleType == MarginRuleTypeEnum.区间追保结构) { var rows = this.ruleRangeDetailRows; for (var i = 0; i < rows.length; i++) { if (rows[i].index === index) { this.marginTemplate.Details.splice(index, Math.max(rows[i].rowspan, 1)); break; } } } else { this.marginTemplate.Details.splice(index, 1); } this.refreshChosen(); }, //资产类型 chosen 变更(区块级):含利率债(位16)时强制单选利率债; //纯利率债区块改为不含利率债时先弹确认(4档收缩为1条,其余3档录入数据将丢弃); //反向(非利率债 → 利率债展开4档)保留已有数据,无需确认 onUnderlyingTypeChange(index) { var ut = this.sum($("#UnderlyingType" + index).val()); if ((ut & TermTierEnabledUnderlyingMask) > 0) ut = TermTierEnabledUnderlyingMask; var rows = this.ruleRangeDetailRows; var head = null; for (var i = 0; i < rows.length; i++) { if (rows[i].index === index) { head = rows[i]; break; } } if (!head) return; if ((head.detail.UnderlyingType || 0) === TermTierEnabledUnderlyingMask && ut !== TermTierEnabledUnderlyingMask) { var that = this; main.confirm("该区块当前为利率债分档配置,改为其他资产类型后其余期限档的录入内容将被清除,确认修改?", function () { that.applyUnderlyingTypeChange(index, ut); }, function () { //取消:Vue 数据未动,翻转一次 UnderlyingType 强制重渲染 select,再由 chosen 重读选中态回退为纯利率债 head.detail.UnderlyingType = 0; Vue.nextTick(function () { head.detail.UnderlyingType = TermTierEnabledUnderlyingMask; that.refreshChosen(); }); }); return; } this.applyUnderlyingTypeChange(index, ut); }, //区块资产类型变更的实际落地(onUnderlyingTypeChange 确认后调用): //ut=纯利率债 → 区块固定展开4个期限档 detail(保留已有档位行数据,缺档补新行); //否则 → 区块收敛为单条 detail(BondTerm 置空,保留首行数据) applyUnderlyingTypeChange(index, ut) { var rows = this.ruleRangeDetailRows; var head = null; for (var i = 0; i < rows.length; i++) { if (rows[i].index === index) { head = rows[i]; break; } } if (!head) return; var details = this.marginTemplate.Details; var headDetail = head.detail; var bk = headDetail._bk; if (!bk) { bk = 'bk_' + (++spanBlockSeq); Vue.set(headDetail, '_bk', bk); } var oldCount = Math.max(head.rowspan, 1); if (ut === TermTierEnabledUnderlyingMask) { var byTerm = {}; details.slice(index, index + oldCount).forEach(function (r) { var bt = (r.SpanConfig && r.SpanConfig.BondTerm) || ''; if (!byTerm[bt]) byTerm[bt] = r; }); var merged = []; for (var t = 0; t < SpanBondTerms.length; t++) { var r = byTerm[SpanBondTerms[t][0]] || (t === 0 ? headDetail : null); if (!r) r = this.newSpanDetail(TermTierEnabledUnderlyingMask, SpanBondTerms[t][0]); r.UnderlyingType = TermTierEnabledUnderlyingMask; r.SpanConfig.BondTerm = SpanBondTerms[t][0]; Vue.set(r, '_bk', bk); merged.push(r); } details.splice.apply(details, [index, oldCount].concat(merged)); } else { headDetail.UnderlyingType = ut; if (headDetail.SpanConfig) { headDetail.SpanConfig.BondTerm = ''; //离开基金类型时清 ETF 子类(服务端校验:配置子类的行必须纯基金) if (ut !== FundTypeMask) headDetail.SpanConfig.EtfKind = null; } if (oldCount > 1) details.splice(index + 1, oldCount - 1); } this.refreshChosen(); }, //行合并重组/删除后重渲染:SetAceDropDown 只增强新出现的 select(已初始化的会跳过,见 MyJs.js:137-138), //chosen:updated 让被 Vue 复用的 select 按最新 :selected 重读选中态,两者需同时执行 refreshChosen() { Vue.nextTick(function () { SetAceDropDown(); $('.chosen-select').trigger('chosen:updated'); }); }, //新建一条规则15 detail:克隆 page.detail 模板行,设置资产类型/期限档/ETF子类,并补齐区间录入结构 newSpanDetail(ut, bondTerm, etfKind) { var d = _.cloneDeep(page.detail); d.UnderlyingType = ut; this.ensureSpanDetail(d); d.SpanConfig.BondTerm = bondTerm || ''; d.SpanConfig.EtfKind = etfKind || null; return d; }, //ETF 子类选择器变更(仅纯基金区块渲染):子类仅用于行区分(取数侧子类行优先), //ETF 无期限概念不分档——任何子类切换后区块始终收敛为单行(BondTerm 置空,多余档位行删除,首行承载 EtfKind; //存量 4 档子类数据在这里被收敛时仅保留首行录入内容) onEtfKindChange(blk) { var head = blk.sections[0].detail; var kind = (head.SpanConfig && head.SpanConfig.EtfKind) || ''; var details = this.marginTemplate.Details; var bk = head._bk; var rows = details.filter(function (d) { return d._bk === bk; }); rows.forEach(function (r) { if (r !== head) { var idx = details.indexOf(r); if (idx >= 0) details.splice(idx, 1); } }); head.UnderlyingType = FundTypeMask; head.SpanConfig.BondTerm = ''; head.SpanConfig.EtfKind = kind || null; this.refreshChosen(); }, //分档行判定与分档键:纯利率债 → 'tbond'(连续行归一个区块按固定4档补齐);ETF 子类不分档,基金行一律独立成块 spanTierKey(d) { var ut = d.UnderlyingType || 0; if (ut === TermTierEnabledUnderlyingMask) return 'tbond'; return null; }, //存量回显重建资产类型区块(分配区块键 _bk):连续的纯利率债行归为区块并按固定4档补齐(缺档补空行); //同档重复/异常档的分档行、其余全部行(含基金+子类行、基金通配行)均各自独立成块展示,不丢数据—— //存量 ETF 子类 4 档行会以多个单行区块出现,保存时由服务端"ETF 子类不分期限档"校验拦截提示清理 rebuildSpanBlocks(details) { var termValues = SpanBondTerms.map(function (t) { return t[0]; }); var result = []; var that = this; var i = 0; while (i < details.length) { var d = details[i]; var tk = this.spanTierKey(d); if (tk) { var rows = []; while (i < details.length && this.spanTierKey(details[i]) === tk) { rows.push(details[i]); i++; } var byTerm = {}; var anomalies = []; rows.forEach(function (r) { var bt = (r.SpanConfig && r.SpanConfig.BondTerm) || ''; if (termValues.indexOf(bt) >= 0 && !byTerm[bt]) byTerm[bt] = r; else anomalies.push(r); }); var bk = 'bk_' + (++spanBlockSeq); termValues.forEach(function (t) { var r = byTerm[t]; if (!r) r = that.newSpanDetail(TermTierEnabledUnderlyingMask, t); r.UnderlyingType = TermTierEnabledUnderlyingMask; r.SpanConfig.BondTerm = t; Vue.set(r, '_bk', bk); result.push(r); }); anomalies.forEach(function (r) { Vue.set(r, '_bk', 'bk_' + (++spanBlockSeq)); result.push(r); }); } else { Vue.set(d, '_bk', 'bk_' + (++spanBlockSeq)); result.push(d); i++; } } return result; }, underlyingTypeNames(mask) { mask = parseInt(mask) || 0; if (mask === 0) return "全部"; var names = []; for (var i = 0; i < UnderlyingTypeNames.length; i++) { if ((mask & UnderlyingTypeNames[i][0]) > 0) names.push(UnderlyingTypeNames[i][1]); } return names.length > 0 ? names.join("、") : "全部"; }, bondTermLabel(detail) { var v = (detail.SpanConfig && detail.SpanConfig.BondTerm) || ''; var map = { '': '全部', '<5y': '≤5y', '5y-10y': '(5y-10y]', '10y-30y': '(10y-30y]', '>30y': '>30y' }; return map[v] !== undefined ? map[v] : v; }, //规则15录入区块文案:按 detail 资产类型切换计价口径。 //比较价格口径与计算侧对齐(MarginCalculationBase.CalcSwapSpanMaintenanceMargin:IsBond→中债估值净价,其余→收盘价): //纯债券类(利率债16/信用债32/其它债券64,可组合):期初净价/当前净价,金额=×期初全价×券面总额; //其余全部类型(基金/债券指数/股票/股指等):参考标的期初净价/参考标的当前收盘价(多空各层统一收盘价), //金额=×参考标的期初价格×参考标的名义份额。 spanText(detail) { var ut = (detail && detail.UnderlyingType) || 0; //非空且标志位全部落在债券三类内才按债券口径;混合行(债券|非债券)按收盘价口径显示,与取数侧标的实际类型判定方向一致 var bondMask = 16 | 32 | 64; var isBond = ut !== 0 && (ut & ~bondMask) === 0; if (!isBond) { return { priceInit: '参考标的期初净价', priceCur: '参考标的当前收盘价', priceCurShort1: '参考标的当前收盘价', priceCurShort: '参考标的当前收盘价', amountBase: '参考标的期初价格 × 参考标的名义份额' }; } return { priceInit: '期初净价', priceCur: '当前净价', priceCurShort1: '当前净价', priceCurShort: '当前净价', amountBase: '期初全价 × 券面总额' }; }, //4个空追保区间 tier(区间边界/金额比例均存小数,null=未填) emptySpanTiers() { var tiers = []; for (var i = 0; i < 4; i++) tiers.push({ Lower: null, Upper: null, AmountRate: null }); return tiers; }, //规则15:补齐区间录入结构——SpanConfig 缺省时补空对象,WarnLine/CloseLine/EtfKind 缺键时补 null, //LongSpans/ShortSpans 非数组或为空时初始化为4个空 tier,tier 缺键补齐(全程 Vue.set 保证响应式,v-model 绑定点必须预先存在) ensureSpanDetail(detail) { if (!detail.SpanConfig) Vue.set(detail, 'SpanConfig', { BondTerm: '' }); var sc = detail.SpanConfig; if (sc.WarnLine === undefined) Vue.set(sc, 'WarnLine', null); if (sc.CloseLine === undefined) Vue.set(sc, 'CloseLine', null); if (sc.EtfKind === undefined) Vue.set(sc, 'EtfKind', null); var that = this; ['LongSpans', 'ShortSpans'].forEach(function (key) { if (!Array.isArray(sc[key]) || sc[key].length === 0) Vue.set(sc, key, that.emptySpanTiers()); for (var i = 0; i < 4; i++) { if (!sc[key][i]) Vue.set(sc[key], i, { Lower: null, Upper: null, AmountRate: null }); if (sc[key][i].Lower === undefined) Vue.set(sc[key][i], 'Lower', null); if (sc[key][i].Upper === undefined) Vue.set(sc[key][i], 'Upper', null); if (sc[key][i].AmountRate === undefined) Vue.set(sc[key][i], 'AmountRate', null); } }); }, sum(items) { var total = 0; if (items) { for (var index = 0; index < items.length; index++) { total = total + parseFloat(items[index]); } } return total; } }, mounted: function () { /*console.log(this.marginTemplate.Details);*/ }, components: { 'vue-datepicker': FastVue.vueDatePicker(), 'vue-number-input': FastVue.vueNumberInput() } }); $(function () { $(".datepicker").change(function () { var dateVal = $(this).val(); if (!dateVal) return; dateVal = dateVal.replace(/\D/g, '').padStart(4, '0'); switch (dateVal.length) { case 4: dateVal = new Date().getFullYear() + dateVal; break; case 6: dateVal = '20' + dateVal; break; case 8: break; default: dateVal = dateVal.length > 8 ? dateVal.substring(0, 8) : ''; break; } if (dateVal) { dateVal = moment(dateVal).format('YYYY-MM-DD'); !/^\d+/.test(dateVal) && (dateVal = ''); } $(this).datepicker("setDate", dateVal); }); //chosen 选中变化通过 jQuery trigger("change") 通知(chosen.jquery.js trigger_form_field_change), //Vue 的 v-on:change(原生监听)收不到,这里用 jQuery 委托监听规则15区块的资产类型选择器 $(document).on('change', 'select.chosen-select[id^="UnderlyingType"]', function () { var idx = parseInt(this.id.substring('UnderlyingType'.length)); if (!isNaN(idx)) vue.onUnderlyingTypeChange(idx); }); //多选 chosen:已有选中项后隐藏搜索框——空框没有输入内容却占一行,放不下时还会换行留白 //(本页多选均为点选场景,无需键盘过滤);全部取消后恢复,显示"请选择…"占位。 //勾选变化会触发原 select 的 jQuery change(trigger_form_field_change), //加载重建/增删区块后的 refreshChosen 会触发 chosen:updated,两处都走这里的委托统一刷新 function refreshChosenSearchField(select) { var $sel = $(select); var $container = $sel.next('.chosen-container-multi'); if ($container.length === 0) return; var noneSelected = $sel.find('option:selected').length === 0; $container.find('li.search-field').toggle(noneSelected); } $(document).on('change chosen:updated', 'select.chosen-select[multiple]', function () { refreshChosenSearchField(this); }); SetAceDropDown(); $('select.chosen-select[multiple]').each(function () { refreshChosenSearchField(this); }); });