//定价格式化(来自配置) const inputFormatPercent = Object.freeze({ precision: 2, append: '%' }); const inputFormatPercentNegative = Object.freeze({ precision: 2, append: '%', negative: true }); const inputFormatNegative = Object.freeze({ precision: 2, negative: true }); 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; //允许配置期限档的 ETF 子类(SpanConfig.EtfKind 值,取值=数据字典"ETF 子类"), //与后端 ConsMarginTerm.TermTierEnabledEtfKinds 保持一致;其余子类与"不区分"的基金行不分档 const TermTierEnabledEtfKinds = ['可转债 ETF', '科创债 ETF']; //利率债区块固定展开的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 () { //区间追保结构:加载时按 资产类型+期限 排序一次,再重建资产类型区块(利率债区块补齐固定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)与分档 ETF 子类 //(ut=基金 + EtfKind∈可转债/科创债 ETF)区块固定展开4个期限档段(每段一条 detail),其他区块单段(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 isTieredEtf = ut === FundTypeMask && TermTierEnabledEtfKinds.indexOf(kind) >= 0; var isTiered = isTBond || isTieredEtf; var sections = []; if (isTiered) { 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, isTieredEtf: isTieredEtf, 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.marginTemplate.Name) { main.message("模板名称不能为空"); return; } this.marginTemplate.TradeTypes = $("#tradeTypes").val().join(","); 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 → 区块展开固定4档(缺档补行、异常档行删除); //选其他子类/不区分 → 收敛为单行(BondTerm 置空,多余档位行删除,首行承载 EtfKind) onEtfKindChange(blk) { var head = blk.sections[0].detail; var kind = (head.SpanConfig && head.SpanConfig.EtfKind) || ''; var tiered = TermTierEnabledEtfKinds.indexOf(kind) >= 0; var details = this.marginTemplate.Details; var bk = head._bk; var rows = details.filter(function (d) { return d._bk === bk; }); if (tiered) { var byTerm = {}; rows.forEach(function (r) { var bt = (r.SpanConfig && r.SpanConfig.BondTerm) || ''; if (byTerm[bt] === undefined) byTerm[bt] = r; }); var that = this; var merged = []; SpanBondTerms.forEach(function (t) { var r = byTerm[t[0]]; if (!r) r = that.newSpanDetail(FundTypeMask, t[0], kind); r.UnderlyingType = FundTypeMask; r.SpanConfig.EtfKind = kind; r.SpanConfig.BondTerm = t[0]; Vue.set(r, '_bk', bk); merged.push(r); var idx = details.indexOf(r); if (idx >= 0 && r !== head) details.splice(idx, 1); }); //异常档/重复行全部移除后按档序回插到区块首行位置 var headIdx = details.indexOf(head); if (headIdx < 0) headIdx = details.length; details.splice.apply(details, [headIdx + 1, 0].concat(merged.filter(function (r) { return r !== head; }))); } else { 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';基金 + 分档 ETF 子类 → 'etf:子类'(同一子类的连续行归一个区块) spanTierKey(d) { var ut = d.UnderlyingType || 0; var kind = (d.SpanConfig && d.SpanConfig.EtfKind) || ''; if (ut === TermTierEnabledUnderlyingMask) return 'tbond'; if (ut === FundTypeMask && TermTierEnabledEtfKinds.indexOf(kind) >= 0) return 'etf:' + kind; return null; }, //存量回显重建资产类型区块(分配区块键 _bk):连续的纯利率债行、基金+分档ETF子类行各自归为区块并按固定4档补齐(缺档补空行); //同档重复/异常档的分档行、非分档行(含基金+不分档子类、基金通配行)均各自独立成块展示,不丢数据 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); var utv = tk === 'tbond' ? TermTierEnabledUnderlyingMask : FundTypeMask; var kind = tk === 'tbond' ? '' : tk.substring(4); termValues.forEach(function (t) { var r = byTerm[t]; if (!r) r = that.newSpanDetail(utv, t, kind); r.UnderlyingType = utv; if (kind) r.SpanConfig.EtfKind = kind; 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 资产类型切换计价口径。 //债券类(利率债16/信用债32/其它债券64)及其他类型(默认债券口径):期初净价/当前净价,金额=×期初全价×券面总额; //基金及基金专户(32768)/债券指数(536870912):参考标的期初净价/参考标的当前收盘价(空头各层统一收盘价,BUG-11 修正), //金额=×参考标的期初价格×参考标的名义份额。 spanText(detail) { var ut = (detail && detail.UnderlyingType) || 0; if ((ut & 32768) > 0 || (ut & 536870912) > 0) { 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); }); SetAceDropDown(); });