优化审批流程逻辑,支持复杂分支条件

This commit is contained in:
锦麟 王
2026-07-10 17:00:13 +08:00
parent fb59c6180a
commit c4293ed523
9 changed files with 207 additions and 320 deletions
@@ -712,25 +712,7 @@ namespace YLErp.Modules.SystemModule
InitGroupId = UserBLL.GetApprovalProcessGroup(userId)
};
// ===== 临时诊断日志(定位触发条件是否生效,验证后删除)=====
try
{
var tn = trade?.TradeNumber ?? "?";
var initNotional = trade?.OriginalStockEqvNotional ?? 0;
System.Diagnostics.Debug.WriteLine($"[ApplyTriggerOnStart] trade={tn}, 期初名义本金={initNotional}, 起点order={start.order}, 起点triggerCondition={start.triggerCondition ?? "()"}, 节点数={tradeProcess.Count}");
foreach (var n in tradeProcess)
{
System.Diagnostics.Debug.WriteLine($" 节点 order={n.order}, node={n.node}, roleId={n.roleId}, triggerCondition={n.triggerCondition ?? "()"}");
}
}
catch { }
var target = ConditionEvaluator.FindFirstTriggeredNode(tradeProcess, start, ctx);
try
{
System.Diagnostics.Debug.WriteLine($"[ApplyTriggerOnStart] FindFirstTriggeredNode 结果 = {(target == null ? "null(,)" : "order=" + target.order)}");
}
catch { }
if (target == null)
{
@@ -126,8 +126,8 @@ namespace YLErp.Modules.TradeModule.DealModule
td.CheckStatus = Convert.ToInt32(TradeCheckEnum.StatusOfOld);
//检查是否有交易审批流程
if (valuedateBLL.SystemDate.CloseReApprove == 1 && hasTradeProcess)
//检查是否有交易审批流程(需求②:平仓/行权按交易状态查对应流程,而非只查开仓流程)
if (valuedateBLL.SystemDate.CloseReApprove == 1 && TradeProcessCountByCategory(td) > 0)
{
// 如果有审批组
InitTradeProcessOrder(td, UserId);
@@ -107,7 +107,8 @@ namespace YLErp.Modules.TradeModule.DealModule
td.OptDate = DateTime.Now;
td.TradeStatus = ConsTrade.;
td.CheckStatus = Convert.ToInt32(TradeCheckEnum.StatusOfOld);
if (valuedateBLL.SystemDate.CloseReApprove == 1 && hasTradeProcess)
// 需求②:行权按交易状态查对应流程,而非只查开仓流程
if (valuedateBLL.SystemDate.CloseReApprove == 1 && TradeProcessCountByCategory(td) > 0)
{
//如果有审批组
InitTradeProcessOrder(td,UserId);
@@ -294,8 +294,8 @@ namespace YLErp.Modules.TradeModule.DealModule
}
var result = new TradeUnwindResultModel(td);
//存在审批流程时,走审批成功流程
if (valuedateBLL.SystemDate.CloseReApprove == 1 && HasTradeProcess())
//存在审批流程时,走审批成功流程(需求②:按交易状态查对应流程)
if (valuedateBLL.SystemDate.CloseReApprove == 1 && TradeProcessCountByCategory(td) > 0)
{
result.ApprovalProcess = true;
@@ -462,8 +462,8 @@ namespace YLErp.Modules.TradeModule.DealModule
throw new ServiceException("平仓费不一致!复核不通过");
}
}
//存在审批流程时,走审批成功流程
if (valuedateBLL.SystemDate.CloseReApprove == 1 && HasTradeProcess())
//存在审批流程时,走审批成功流程(需求②:按交易状态查对应流程)
if (valuedateBLL.SystemDate.CloseReApprove == 1 && TradeProcessCountByCategory(td) > 0)
{
var reqModel = new TradeOpenReqModel
{
+9 -7
View File
@@ -3081,6 +3081,8 @@ namespace YLErp.BLL
//如果是审批组
var approvalprocessQuery = db.approvalprocess.Where(a => a.processType == "TradeProcess");
var tradeOpenProcessOrder = approvalprocessQuery.Count();
// 需求②:了结流程节点数(用于平仓/行权/互换交易展示正确的审批进度)
var closeProcessOrder = db.approvalprocess.Count(a => a.processType == "CloseProcess");
var branch = approvalprocessQuery.FirstOrDefault(x => x.approvalGroupId != 0);//审批流程有分支情况
var firstBranch = approvalprocessQuery.Where(x => (x.node == 1 && x.approvalGroupId == 0) || x.node == 0);//分支一总流程
var secondBranch = approvalprocessQuery.Where(x => (x.node == 2 && x.approvalGroupId == 0) || x.node == 0);//分支二总流程
@@ -3288,7 +3290,7 @@ namespace YLErp.BLL
IQueryable<TradeLinq> query = null;
if (branch == null)
{
query = GetTradeLinqQuery(predicate, approvalprocessQuery, tradeOpenProcessOrder, 0);
query = GetTradeLinqQuery(predicate, approvalprocessQuery, tradeOpenProcessOrder, 0, closeProcessOrder);
}
else
{
@@ -3304,8 +3306,8 @@ namespace YLErp.BLL
{
nodeArr.Add(approvalConditionSecend.node);
}
query = GetTradeLinqQuery(predicate, firstBranch, firstCount, 1, branchIndex, nodeArr);
var secondQuery = GetTradeLinqQuery(predicate, secondBranch, secondCount, 2, branchIndex, nodeArr);
query = GetTradeLinqQuery(predicate, firstBranch, firstCount, 1, branchIndex, nodeArr, closeProcessOrder);
var secondQuery = GetTradeLinqQuery(predicate, secondBranch, secondCount, 2, branchIndex, nodeArr, closeProcessOrder);
query = query.Union(secondQuery);
}
@@ -3319,7 +3321,7 @@ namespace YLErp.BLL
query = query.OrderByDescending(s => s.OptDate);
return query;
}
private IQueryable<TradeLinq> GetTradeLinqQuery(Expression<Func<trade, bool>> predicate, IQueryable<approvalprocess> approvalprocessQuery, int tradeOpenProcessOrder, int branchOrder)
private IQueryable<TradeLinq> GetTradeLinqQuery(Expression<Func<trade, bool>> predicate, IQueryable<approvalprocess> approvalprocessQuery, int tradeOpenProcessOrder, int branchOrder, int closeProcessOrder = 0)
{
var query = from source in db.trade.Where(predicate)
join process in approvalprocessQuery on source.ProcessOrderId equals process.order into pro
@@ -3371,7 +3373,7 @@ namespace YLErp.BLL
OptId = source.OptId,
OptName = source.OptName,
OptDate = source.OptDate,
ProcessStatus = source.ProcessStatus == "审批中" ? source.ProcessStatus + " 流程" + (branchOrder != 0 && source.ProcessOrderId > branchOrder ? source.ProcessOrderId - 2 : source.ProcessOrderId - 1) + "/" + tradeOpenProcessOrder : source.ProcessStatus,
ProcessStatus = source.ProcessStatus == "审批中" ? source.ProcessStatus + " 流程" + (branchOrder != 0 && source.ProcessOrderId > branchOrder ? source.ProcessOrderId - 2 : source.ProcessOrderId - 1) + "/" + (source.TradeStatus == "平仓待复核" || source.TradeStatus == "行权待复核" || source.TradeStatus == "互换待复核" ? (closeProcessOrder > 0 ? closeProcessOrder : tradeOpenProcessOrder) : tradeOpenProcessOrder) : source.ProcessStatus,
ProcessOrderId = source.ProcessOrderId,
ProcessOrderBranch = source.ProcessOrderBranch,
ProcessRoleId = proce.roleId,
@@ -3393,7 +3395,7 @@ namespace YLErp.BLL
};
return query;
}
private IQueryable<TradeLinq> GetTradeLinqQuery(Expression<Func<trade, bool>> predicate, IQueryable<approvalprocess> approvalprocessQuery, int tradeOpenProcessOrder, int node, int branchOrder, List<int?> nodeArr)
private IQueryable<TradeLinq> GetTradeLinqQuery(Expression<Func<trade, bool>> predicate, IQueryable<approvalprocess> approvalprocessQuery, int tradeOpenProcessOrder, int node, int branchOrder, List<int?> nodeArr, int closeProcessOrder = 0)
{
var tradeStatus = new string[] { "平仓待复核", "行权待复核", "互换待复核" };
var query = from source in db.trade.Where(predicate)
@@ -3447,7 +3449,7 @@ namespace YLErp.BLL
OptId = source.OptId,
OptName = source.OptName,
OptDate = source.OptDate,
ProcessStatus = source.ProcessStatus == "审批中" ? source.ProcessStatus + " 流程" + (branchOrder != 0 && source.ProcessOrderId > branchOrder ? source.ProcessOrderId - 2 : source.ProcessOrderId - 1) + "/" + tradeOpenProcessOrder : source.ProcessStatus,
ProcessStatus = source.ProcessStatus == "审批中" ? source.ProcessStatus + " 流程" + (branchOrder != 0 && source.ProcessOrderId > branchOrder ? source.ProcessOrderId - 2 : source.ProcessOrderId - 1) + "/" + (source.TradeStatus == "平仓待复核" || source.TradeStatus == "行权待复核" || source.TradeStatus == "互换待复核" ? (closeProcessOrder > 0 ? closeProcessOrder : tradeOpenProcessOrder) : tradeOpenProcessOrder) : source.ProcessStatus,
ProcessOrderId = source.ProcessOrderId,
ProcessOrderBranch = source.ProcessOrderBranch,
ProcessRoleId = proce.roleId,
@@ -57,18 +57,13 @@ namespace YLErp.Modules.TradeModule
/// <summary>
/// 按交易推断流程类别(开仓/了结)获取审批节点(需求②)。
/// <para>了结类(平仓待复核/行权待复核/互换待复核)优先取 CloseProcess若未配置则回退 TradeProcess,避免卡单。</para>
/// <para>了结类(平仓待复核/行权待复核/互换待复核)取 CloseProcess开仓类取 TradeProcess。</para>
/// <para>未配置对应流程时返回空列表(由调用方决定是否直接通过)。</para>
/// </summary>
public List<approvalprocess> TradeProcessByCategory(trade td)
{
var category = ResolveProcessCategory(td);
var orders = DbContext.approvalprocess.Where(t => t.processType == category).OrderBy(o => o.order).ToList();
// 兼容回退:了结流程未配置时,回退到开仓流程。
if (category == ProcessCategoryConst.Close && orders.Count == 0)
{
return TradeProcess();
}
return orders;
return DbContext.approvalprocess.Where(t => t.processType == category).OrderBy(o => o.order).ToList();
}
/// <summary>
@@ -85,9 +80,17 @@ namespace YLErp.Modules.TradeModule
/// <param name="td"></param>
public void InitTradeProcessOrder(trade td,int userId)
{
var tradeProcess = TradeProcessByCategory(td);
// 该交易类别未配置审批流程 → 直接审批通过,无需审核
if (tradeProcess == null || tradeProcess.Count == 0)
{
td.ProcessOrderId = ProcessTradeLog.;
td.ProcessStatus = ProcessTradeStatus..ToString();
td.ProcessOptDate = DateTime.Now;
return;
}
td.ProcessOrderId = ProcessTradeLog.;
td.ProcessStatus = ProcessTradeStatus..ToString();
var tradeProcess = TradeProcessByCategory(td);
var groupId = UserBLL.GetApprovalProcessGroup(userId);
bool approvalBranch = tradeProcess.Any(x => x.approvalGroupId != 0);//审批流程有分支情况
if (td.ProcessOrderId == 1)
@@ -196,16 +199,16 @@ namespace YLErp.Modules.TradeModule
ProcessCategory = ResolveProcessCategory(td),
InitGroupId = UserBLL.GetApprovalProcessGroup(userId)
};
// 了结场景:取本次影响的名义本金(trade_cash.UnwindStockEqvNotional 绝对值
// 了结场景:当次平仓名义本金 = 期初名义本金 × 平仓比例(UnwindPercentRate),取绝对值
if (td != null && ctx.ProcessCategory == ProcessCategoryConst.Close)
{
var tc = DbContext.trade_cash
.Where(t => t.TradeId == td.id && t.ValidState == ConsGlobal.InValid && !t.IsDeleted)
.OrderByDescending(t => t.id)
.FirstOrDefault();
if (tc != null && tc.UnwindStockEqvNotional.HasValue)
if (tc != null && tc.UnwindPercentRate.HasValue && td.OriginalStockEqvNotional.HasValue)
{
ctx.CurrentNotional = Math.Abs(tc.UnwindStockEqvNotional.Value);
ctx.CurrentNotional = Math.Abs(td.OriginalStockEqvNotional.Value * tc.UnwindPercentRate.Value);
}
}
return ctx;
@@ -84,7 +84,7 @@
<div class="branch-wrap">
<div class="branch-box-wrap">
<div class="branch-box">
<span class="add-branch" title="添加条件">添加条件</span>
<span class="add-branch" title="添加条件" v-on:click="addCondition(item)">添加条件</span>
<div class="col-box" v-for="child in openFilterBranch">
<div class="condition-node">
<div class="condition-node-box">
@@ -214,7 +214,7 @@
<div class="branch-wrap">
<div class="branch-box-wrap">
<div class="branch-box">
<span class="add-branch" title="添加条件">添加条件</span>
<span class="add-branch" title="添加条件" v-on:click="addCondition(item)">添加条件</span>
<div class="col-box" v-for="child in clientFilterBranch">
<div class="condition-node">
<div class="condition-node-box">
@@ -344,7 +344,7 @@
</div>
</div>
<div class="trigger-token-list">
<div v-for="(tok,ti) in item._trigger.tokens" class="trigger-token-row">
<div v-for="(tok,ti) in (item._trigger && item._trigger.tokens) || []" class="trigger-token-row">
<span v-if="tok.type=='operator'" class="trigger-connector" v-on:click="toggleTriggerConnector(item,ti)">{{tok.connector=='or'?'或':'且'}}</span>
<span v-if="tok.type=='lparen'" class="trigger-paren">(&nbsp;&nbsp;<i class="glyphicon glyphicon-remove trigger-remove" v-on:click="removeTriggerToken(item,ti)"></i></span>
<span v-if="tok.type=='rparen'" class="trigger-paren">)<i class="glyphicon glyphicon-remove trigger-remove" v-on:click="removeTriggerToken(item,ti)"></i></span>
@@ -375,7 +375,7 @@
<div class="branch-wrap">
<div class="branch-box-wrap">
<div class="branch-box">
<span class="add-branch" title="添加条件">添加条件</span>
<span class="add-branch" title="添加条件" v-on:click="addCondition(item)">添加条件</span>
<div class="col-box" v-for="child in filterBranch">
<div class="condition-node">
<div class="condition-node-box">
@@ -436,7 +436,7 @@
</div>
</div>
<div class="trigger-token-list">
<div v-for="(tok,ti) in childSecond._trigger.tokens" class="trigger-token-row">
<div v-for="(tok,ti) in (childSecond._trigger && childSecond._trigger.tokens) || []" class="trigger-token-row">
<span v-if="tok.type=='operator'" class="trigger-connector" v-on:click="toggleTriggerConnector(childSecond,ti)">{{tok.connector=='or'?'或':'且'}}</span>
<span v-if="tok.type=='lparen'" class="trigger-paren">(&nbsp;&nbsp;<i class="glyphicon glyphicon-remove trigger-remove" v-on:click="removeTriggerToken(childSecond,ti)"></i></span>
<span v-if="tok.type=='rparen'" class="trigger-paren">)<i class="glyphicon glyphicon-remove trigger-remove" v-on:click="removeTriggerToken(childSecond,ti)"></i></span>
@@ -513,7 +513,7 @@
<div class="node-wrap-box start-node " style="width:340px;">
<div class="title" style="background: rgb(255, 148, 62);">
<span class="userEdit">审核节点</span>
<i class="glyphicon glyphicon-remove btnRemove" v-on:click="closeDelProcess(item)"></i>
<i class="glyphicon glyphicon-remove btnRemove" v-on:click="delProcess(item)"></i>
</div>
<div>
<span>审核角色</span>
@@ -537,7 +537,7 @@
</div>
</div>
<div class="trigger-token-list">
<div v-for="(tok,ti) in item._trigger.tokens" class="trigger-token-row">
<div v-for="(tok,ti) in (item._trigger && item._trigger.tokens) || []" class="trigger-token-row">
<span v-if="tok.type=='operator'" class="trigger-connector" v-on:click="toggleTriggerConnector(item,ti)">{{tok.connector=='or'?'或':'且'}}</span>
<span v-if="tok.type=='lparen'" class="trigger-paren">(&nbsp;&nbsp;<i class="glyphicon glyphicon-remove trigger-remove" v-on:click="removeTriggerToken(item,ti)"></i></span>
<span v-if="tok.type=='rparen'" class="trigger-paren">)<i class="glyphicon glyphicon-remove trigger-remove" v-on:click="removeTriggerToken(item,ti)"></i></span>
@@ -568,7 +568,7 @@
<div class="branch-wrap">
<div class="branch-box-wrap">
<div class="branch-box">
<span class="add-branch" title="添加条件">添加条件</span>
<span class="add-branch" title="添加条件" v-on:click="addCondition(item)">添加条件</span>
<div class="col-box" v-for="child in closeFilterBranch()">
<div class="condition-node">
<div class="condition-node-box">
@@ -576,7 +576,7 @@
<div class="node-wrap-box start-node " style="width: 282px;">
<div class="title" style="background: rgb(255, 148, 62);">
<span class="userEdit">分支{{child.node}}条件</span>
<i class="glyphicon glyphicon-remove btnRemove" v-on:click="closeDelProcess(item)"></i>
<i class="glyphicon glyphicon-remove btnRemove" v-on:click="delProcess(item)"></i>
</div>
<div>
<span>申请人</span>
@@ -605,7 +605,7 @@
<div class="node-wrap-box start-node " style="width:340px;">
<div class="title" style="background: rgb(255, 148, 62);">
<span class="userEdit">审核节点</span>
<i class="glyphicon glyphicon-remove btnRemove" v-on:click="closeDelProcess(childSecond)"></i>
<i class="glyphicon glyphicon-remove btnRemove" v-on:click="delProcess(childSecond)"></i>
</div>
<div>
<span>审核角色</span>
@@ -629,7 +629,7 @@
</div>
</div>
<div class="trigger-token-list">
<div v-for="(tok,ti) in childSecond._trigger.tokens" class="trigger-token-row">
<div v-for="(tok,ti) in (childSecond._trigger && childSecond._trigger.tokens) || []" class="trigger-token-row">
<span v-if="tok.type=='operator'" class="trigger-connector" v-on:click="toggleTriggerConnector(childSecond,ti)">{{tok.connector=='or'?'或':'且'}}</span>
<span v-if="tok.type=='lparen'" class="trigger-paren">(&nbsp;&nbsp;<i class="glyphicon glyphicon-remove trigger-remove" v-on:click="removeTriggerToken(childSecond,ti)"></i></span>
<span v-if="tok.type=='rparen'" class="trigger-paren">)<i class="glyphicon glyphicon-remove trigger-remove" v-on:click="removeTriggerToken(childSecond,ti)"></i></span>
@@ -70,12 +70,12 @@ var app = new Vue({
// 需求①:节点触发条件字段(期初名义本金 / 本次交易名义本金)
// 开仓流程只能选期初名义本金;了结流程两个都可选
conditionFields: [
{ value: 'initialNotional', text: '交易的期初名义本金' },
{ value: 'initialNotional', text: '期初名义本金' },
{ value: 'currentNotional', text: '本次交易名义本金' }
],
// 开仓流程可选字段(仅期初)
conditionFieldsOpen: [
{ value: 'initialNotional', text: '交易的期初名义本金' }
{ value: 'initialNotional', text: '期初名义本金' }
],
conditionOps: [
{ value: 'gt', text: '>' },
@@ -128,6 +128,7 @@ var app = new Vue({
if (thisObj.selected === '1') {
thisObj.isOpen = true;
thisObj.isTrade = false;
thisObj.isClose = false;
thisObj.isCredit = false;
thisObj.isOutCash = false;
thisObj.isClient = false;
@@ -148,6 +149,7 @@ var app = new Vue({
} else if (thisObj.selected === '3') {
thisObj.isOpen = false;
thisObj.isTrade = false;
thisObj.isClose = false;
thisObj.isCredit = true;
thisObj.isOutCash = false;
thisObj.isClient = false;
@@ -155,6 +157,7 @@ var app = new Vue({
else if (thisObj.selected === '4') {
thisObj.isOpen = false;
thisObj.isTrade = false;
thisObj.isClose = false;
thisObj.isCredit = false;
thisObj.isOutCash = true;
thisObj.isClient = false;
@@ -162,6 +165,7 @@ var app = new Vue({
else if (thisObj.selected === '5') {
thisObj.isOpen = false;
thisObj.isTrade = false;
thisObj.isClose = false;
thisObj.isCredit = false;
thisObj.isOutCash = false;
thisObj.isClient = true;
@@ -169,6 +173,7 @@ var app = new Vue({
else {
thisObj.isOpen = false;
thisObj.isTrade = false;
thisObj.isClose = false;
thisObj.isCredit = false;
thisObj.isOutCash = false;
thisObj.isClient = false;
@@ -273,7 +278,10 @@ var app = new Vue({
approvalCondition: 1,
node: 1,
parentNode: thisObj.node,
approvalGroupId: 0
approvalGroupId: 0,
conditionConfig: '',
triggerCondition: '',
_trigger: { tokens: [] }
}
var item2 = {
id: 0,
@@ -283,7 +291,10 @@ var app = new Vue({
approvalCondition: 1,
node: 2,
parentNode: thisObj.node,
approvalGroupId: 0
approvalGroupId: 0,
conditionConfig: '',
triggerCondition: '',
_trigger: { tokens: [] }
}
if (selectType == "2") { //交易
thisObj.tradeItems.splice(thisObj.node, 0, item, item2);
@@ -376,12 +387,9 @@ var app = new Vue({
return;
}
if (selectType === "1") { //客户开户
thisObj.delOpenProcess(openItem);
return;
}
else if (selectType === "2") { //交易
thisObj.delTradeProcess(openItem);
if (selectType === "1" || selectType === "2" || selectType === "5" || selectType === "6") {
// 客户开户/交易/客户信息/交易了结:统一走通用删除(支持多分支)
thisObj.commonDeleteProcess(openItem);
return;
}
@@ -401,89 +409,6 @@ var app = new Vue({
});
return;
}
else if (selectType === "5") { //客户信息
thisObj.delClientProcess(openItem);
return;
}
},
delOpenProcess(openItem) {
var thisObj = this;
if (openItem.approvalCondition != 0) {//删除分支下,所有东西
var childNodes = thisObj.openItems.filter(x => x.node != 0);
thisObj.openItems = thisObj.openItems.concat(childNodes).filter(function (v) {
return thisObj.openItems.indexOf(v) === -1 || childNodes.indexOf(v) === -1
});
thisObj.initOpenItemIndex();
} else if (openItem.node != 0) {//删除分支下某节点
var index = thisObj.openItems.indexOf(openItem);
thisObj.openItems.splice(index, 1);
var childNodes = thisObj.openItems.filter(x => x.node == openItem.node && x.Index >= openItem.Index);
childNodes.forEach(item => {
item.parentNode = item.parentNode - 1;
item.Index = item.Index - 1;
});
} else {//删除非分支下某节点
var index = thisObj.openItems.indexOf(openItem);
thisObj.openItems.splice(index, 1);
var nodes = thisObj.openItems.filter(x => x.Index >= openItem.Index);
nodes.forEach(item => {
item.Index = item.Index - 1;
});
thisObj.initOpenItemIndex();
}
},
delTradeProcess(tradeItem) {
var thisObj = this;
thisObj.getRuleType();
if (tradeItem.approvalCondition != 0) {//删除分支下,所有东西
var childNodes = thisObj.tradeItems.filter(x => x.node != 0);
thisObj.tradeItems = thisObj.tradeItems.concat(childNodes).filter(function (v) {
return thisObj.tradeItems.indexOf(v) === -1 || childNodes.indexOf(v) === -1
});
thisObj.initTradeItemIndex();
} else if (tradeItem.node != 0) {//删除分支下某节点
var index = thisObj.tradeItems.indexOf(tradeItem);
thisObj.tradeItems.splice(index, 1);
var childNodes = thisObj.tradeItems.filter(x => x.node == tradeItem.node && x.Index >= tradeItem.Index);
childNodes.forEach(item => {
item.parentNode = item.parentNode - 1;
item.Index = item.Index - 1;
});
} else {//删除非分支下某节点
var index = thisObj.tradeItems.indexOf(tradeItem);
thisObj.tradeItems.splice(index, 1);
var nodes = thisObj.tradeItems.filter(x => x.Index >= tradeItem.Index);
nodes.forEach(item => {
item.Index = item.Index - 1;
});
thisObj.initTradeItemIndex();
}
},
delClientProcess(clientItem) {
var thisObj = this;
if (clientItem.approvalCondition != 0) {//删除分支下,所有东西
var childNodes = thisObj.clientItems.filter(x => x.node != 0);
thisObj.clientItems = thisObj.clientItems.concat(childNodes).filter(function (v) {
return thisObj.clientItems.indexOf(v) === -1 || childNodes.indexOf(v) === -1
});
thisObj.initClientItemIndex();
} else if (clientItem.node != 0) {//删除分支下某节点
var index = thisObj.clientItems.indexOf(clientItem);
thisObj.clientItems.splice(index, 1);
var childNodes = thisObj.clientItems.filter(x => x.node == clientItem.node && x.Index >= clientItem.Index);
childNodes.forEach(item => {
item.parentNode = item.parentNode - 1;
item.Index = item.Index - 1;
});
} else {//删除非分支下某节点
var index = thisObj.clientItems.indexOf(clientItem);
thisObj.clientItems.splice(index, 1);
var nodes = thisObj.clientItems.filter(x => x.Index >= clientItem.Index);
nodes.forEach(item => {
item.Index = item.Index - 1;
});
thisObj.initClientItemIndex();
}
},
addOpenProcess(index, child, node) {
var thisObj = this;
@@ -494,7 +419,10 @@ var app = new Vue({
approvalCondition: 0,
node: node,
parentNode: child ? index : 0,
approvalGroupId: 0
approvalGroupId: 0,
conditionConfig: '',
triggerCondition: '',
_trigger: { tokens: [] }
}
var childNodes = thisObj.openItems.filter(x => x.Index > index);
childNodes.forEach(item => {
@@ -542,7 +470,10 @@ var app = new Vue({
approvalCondition: 0,
node: node,
parentNode: child ? index : 0,
approvalGroupId: 0
approvalGroupId: 0,
conditionConfig: '',
triggerCondition: '',
_trigger: { tokens: [] }
}
var childNodes = thisObj.clientItems.filter(x => x.Index > index);
childNodes.forEach(item => {
@@ -1032,44 +963,134 @@ var app = new Vue({
clientFilterChild(node) {
return this.clientItems.filter(x => x.node == node && x.approvalCondition == 0);
},
initTradeItemIndex() {
var thisObj = this;
var newSortTradeItems = [];
var mainTradeItems = thisObj.tradeItems.filter(x => x.node == 0).sort((a, b) => a.Index - b.Index);
var childTradeItem = thisObj.tradeItems.filter(x => x.node != 0);
var childTradeItemSort = childTradeItem.sort((a, b) => a.Index - b.Index);
var firstChildIndex = childTradeItemSort.length > 0 ? childTradeItemSort[0].Index : -1;
var j = 1;
for (var i = 0; i < mainTradeItems.length; i++) {
if (mainTradeItems[i].Index >= firstChildIndex) {
if (childTradeItemSort.length > 0) {
mainTradeItems[i].Index = childTradeItemSort[childTradeItemSort.length - 1].Index + j;
// ===== 需求③:多分支支持(参考中粮版)通用方法 =====
// 通用分支重编号:动态把分支节点编号为 1,2,3...N(不再写死 1/2
commonInitIndex(targetProp) {
const that = this;
const newSortedItems = [];
const mainItems = that[targetProp].filter(x => x.node == 0).sort((a, b) => a.Index - b.Index);
const childItems = that[targetProp].filter(x => x.node != 0);
const sortedChildItems = childItems.sort((a, b) => a.Index - b.Index);
const firstChildIndex = sortedChildItems.length > 0 ? sortedChildItems[0].Index : -1;
let j = 1;
for (let i = 0; i < mainItems.length; i++) {
if (mainItems[i].Index >= firstChildIndex) {
if (sortedChildItems.length > 0) {
mainItems[i].Index = sortedChildItems[sortedChildItems.length - 1].Index + j;
j++;
} else {
mainTradeItems[i].Index = i + 1;
mainItems[i].Index = i + 1;
}
}
newSortTradeItems.push(mainTradeItems[i]);
newSortedItems.push(mainItems[i]);
}
var firstChildTradeItems = childTradeItem.filter(x => x.node == 1);
var secondChildTradeItems = childTradeItem.filter(x => x.node == 2);
firstChildTradeItems.forEach((x, index) => { //分支index不用再排序
if (x.approvalCondition == 0) {
x.Index = firstChildIndex + index;
x.parentNode = firstChildIndex + index - 1;
// 为分支条件的 node 重新排序(1,2,3...N)——多分支的核心
let tempNode = 1;
childItems.forEach((i) => {
if (i.approvalCondition != 0) {
i.node = tempNode++;
}
newSortTradeItems.push(x);
});
secondChildTradeItems.forEach((x, index) => {
if (x.approvalCondition == 0) {
x.Index = firstChildIndex + index;
x.parentNode = firstChildIndex + index - 1;
}
newSortTradeItems.push(x);
// 按分支 node 分组,重建每个分支下的子节点 Index/parentNode
for (let i = 0; i < childItems.length; i++) {
var filteredChildItems = childItems.filter(x => x.node == i + 1);
filteredChildItems.forEach((i, index) => {
if (i.approvalCondition == 0) {
i.Index = firstChildIndex + index;
i.parentNode = firstChildIndex + index - 1;
}
newSortedItems.push(i);
});
}
that[targetProp] = newSortedItems.sort((a, b) => a.Index - b.Index);
},
// 通用添加分支条件:push 一个 node:999 占位的分支节点,再重编号
commonAddCondition(item) {
const that = this;
const typeToPropMap = {
'OpenProcess': 'openItems',
'ClientProcess': 'clientItems',
'TradeProcess': 'tradeItems',
'CloseProcess': 'closeItems'
};
const typeToInitFnMap = {
'OpenProcess': that.initOpenItemIndex,
'ClientProcess': that.initClientItemIndex,
'TradeProcess': that.initTradeItemIndex,
'CloseProcess': that.initCloseItemIndex
};
const prop = typeToPropMap[item.Type];
const initFn = typeToInitFnMap[item.Type];
that[prop].push({
id: 0,
Type: item.Type,
Index: item.Index,
SelectValue: 0,
approvalCondition: 1,
node: 999,
parentNode: item.Index,
ApprovalRules: '',
approvalGroupId: 0,
conditionConfig: '',
triggerCondition: '',
_trigger: { tokens: [] }
});
thisObj.tradeItems = newSortTradeItems.sort((a, b) => a.Index - b.Index);
thisObj.initRuleType();
initFn();
},
// 添加条件入口("添加条件"按钮调用)
addCondition(item) {
this.commonAddCondition(item);
},
// 通用删除分支/节点
commonDeleteProcess(item) {
const that = this;
const typeToPropMap = {
'OpenProcess': 'openItems',
'ClientProcess': 'clientItems',
'TradeProcess': 'tradeItems',
'CloseProcess': 'closeItems'
};
const typeToInitFnMap = {
'OpenProcess': that.initOpenItemIndex,
'ClientProcess': that.initClientItemIndex,
'TradeProcess': that.initTradeItemIndex,
'CloseProcess': that.initCloseItemIndex
};
const prop = typeToPropMap[item.Type];
const initFn = typeToInitFnMap[item.Type];
// 删除的不是分支条件节点(普通审批节点)
if (item.approvalCondition == 0) {
const index = that[prop].indexOf(item);
that[prop].splice(index, 1);
const nodes = that[prop].filter(x => x.Index >= item.Index);
nodes.forEach(item => {
item.Index = item.Index - 1;
});
initFn();
return;
}
// 分支条件数量<=2 时,删除整个分支组
var conditionBranchesLen = that[prop].filter(i => i.approvalCondition != 0 && i.node != 0).length;
if (item.approvalCondition != 0 && conditionBranchesLen <= 2) {
var childNodes = that[prop].filter(x => x.node != 0);
that[prop] = that[prop].filter(x => childNodes.indexOf(x) === -1);
initFn();
return;
}
// 分支条件数量>2 时,只删除该分支(同 node 的全部节点)
if (item.node != 0 && item.approvalCondition != 0) {
that[prop] = that[prop].filter(x => x.node != item.node);
initFn();
}
},
initTradeItemIndex() {
this.commonInitIndex('tradeItems');
this.initRuleType();
},
// ===== 需求①:节点触发条件 结构化编辑器(token 数组,支持每行独立且/或 + 括号)=====
// _trigger = { tokens: [ {type:'condition',condition:{field,op,value}}, {type:'operator',connector:'and'|'or'}, {type:'lparen'|'rparen'} ] }
@@ -1247,42 +1268,8 @@ var app = new Vue({
return this.closeItems.filter(x => x.node == node && x.approvalCondition == 0);
},
initCloseItemIndex() {
var thisObj = this;
var newSortItems = [];
var mainItems = thisObj.closeItems.filter(x => x.node == 0).sort((a, b) => a.Index - b.Index);
var childItem = thisObj.closeItems.filter(x => x.node != 0);
var childItemSort = childItem.sort((a, b) => a.Index - b.Index);
var firstChildIndex = childItemSort.length > 0 ? childItemSort[0].Index : -1;
var j = 1;
for (var i = 0; i < mainItems.length; i++) {
if (mainItems[i].Index >= firstChildIndex) {
if (childItemSort.length > 0) {
mainItems[i].Index = childItemSort[childItemSort.length - 1].Index + j;
j++;
} else {
mainItems[i].Index = i + 1;
}
}
newSortItems.push(mainItems[i]);
}
var firstChildItems = childItem.filter(x => x.node == 1);
var secondChildItems = childItem.filter(x => x.node == 2);
firstChildItems.forEach((x, index) => {
if (x.approvalCondition == 0) {
x.Index = firstChildIndex + index;
x.parentNode = firstChildIndex + index - 1;
}
newSortItems.push(x);
});
secondChildItems.forEach((x, index) => {
if (x.approvalCondition == 0) {
x.Index = firstChildIndex + index;
x.parentNode = firstChildIndex + index - 1;
}
newSortItems.push(x);
});
thisObj.closeItems = newSortItems.sort((a, b) => a.Index - b.Index);
thisObj.initRuleType();
this.commonInitIndex('closeItems');
this.initRuleType();
},
closeShowProcess(index, child, obj) {
var thisObj = this;
@@ -1329,33 +1316,6 @@ var app = new Vue({
thisObj.closeItems.splice(index, 0, item);
thisObj.initCloseItemIndex();
},
closeDelProcess(closeItem) {
var thisObj = this;
thisObj.getCloseRuleType();
if (closeItem.approvalCondition != 0) {//删除分支下,所有东西
var childNodes = thisObj.closeItems.filter(x => x.node != 0);
thisObj.closeItems = thisObj.closeItems.concat(childNodes).filter(function (v) {
return thisObj.closeItems.indexOf(v) === -1 || childNodes.indexOf(v) === -1
});
thisObj.initCloseItemIndex();
} else if (closeItem.node != 0) {//删除分支下某节点
var index = thisObj.closeItems.indexOf(closeItem);
thisObj.closeItems.splice(index, 1);
var childNodes = thisObj.closeItems.filter(x => x.node == closeItem.node && x.Index >= closeItem.Index);
childNodes.forEach(item => {
item.parentNode = item.parentNode - 1;
item.Index = item.Index - 1;
});
} else {//删除非分支下某节点
var index = thisObj.closeItems.indexOf(closeItem);
thisObj.closeItems.splice(index, 1);
var nodes = thisObj.closeItems.filter(x => x.Index >= closeItem.Index);
nodes.forEach(item => {
item.Index = item.Index - 1;
});
thisObj.initCloseItemIndex();
}
},
closeSelectChangeType(index, value) {
var thisObj = this;
for (var j = 0; j < thisObj.closeItems.length; j++) {
@@ -1442,92 +1402,23 @@ var app = new Vue({
}
},
initOpenItemIndex() {
var thisObj = this;
var newSortOpenItems = [];
var mainOpenItems = thisObj.openItems.filter(x => x.node == 0).sort((a, b) => a.Index - b.Index);
var childOpenItem = thisObj.openItems.filter(x => x.node != 0);
var childOpenItemSort = childOpenItem.sort((a, b) => a.Index - b.Index);
var firstChildIndex = childOpenItemSort.length > 0 ? childOpenItemSort[0].Index : -1;
var j = 1;
for (var i = 0; i < mainOpenItems.length; i++) {
if (mainOpenItems[i].Index >= firstChildIndex) {
if (childOpenItemSort.length > 0) {
mainOpenItems[i].Index = childOpenItemSort[childOpenItemSort.length - 1].Index + j;
j++;
} else {
mainOpenItems[i].Index = i + 1;
}
}
newSortOpenItems.push(mainOpenItems[i]);
}
var firstChildOpenItems = childOpenItem.filter(x => x.node == 1);
var secondChildOpenItems = childOpenItem.filter(x => x.node == 2);
firstChildOpenItems.forEach((x, index) => { //分支index不用再排序
if (x.approvalCondition == 0) {
x.Index = firstChildIndex + index;
x.parentNode = firstChildIndex + index - 1;
}
newSortOpenItems.push(x);
});
secondChildOpenItems.forEach((x, index) => {
if (x.approvalCondition == 0) {
x.Index = firstChildIndex + index;
x.parentNode = firstChildIndex + index - 1;
}
newSortOpenItems.push(x);
});
thisObj.openItems = newSortOpenItems.sort((a, b) => a.Index - b.Index);
this.commonInitIndex('openItems');
},
initClientItemIndex() {
var thisObj = this;
var newSortClientItems = [];
var mainClientItems = thisObj.clientItems.filter(x => x.node == 0).sort((a, b) => a.Index - b.Index);
var childClientItem = thisObj.clientItems.filter(x => x.node != 0);
var childClientItemSort = childClientItem.sort((a, b) => a.Index - b.Index);
var firstChildIndex = childClientItemSort.length > 0 ? childClientItemSort[0].Index : -1;
var j = 1;
for (var i = 0; i < mainClientItems.length; i++) {
if (mainClientItems[i].Index >= firstChildIndex) {
if (childClientItemSort.length > 0) {
mainClientItems[i].Index = childClientItemSort[childClientItemSort.length - 1].Index + j;
j++;
} else {
mainClientItems[i].Index = i + 1;
}
}
newSortClientItems.push(mainClientItems[i]);
}
var firstChildClientItems = childClientItem.filter(x => x.node == 1);
var secondChildClientItems = childClientItem.filter(x => x.node == 2);
firstChildClientItems.forEach((x, index) => { //分支index不用再排序
if (x.approvalCondition == 0) {
x.Index = firstChildIndex + index;
x.parentNode = firstChildIndex + index - 1;
}
newSortClientItems.push(x);
});
secondChildClientItems.forEach((x, index) => {
if (x.approvalCondition == 0) {
x.Index = firstChildIndex + index;
x.parentNode = firstChildIndex + index - 1;
}
newSortClientItems.push(x);
});
thisObj.clientItems = newSortClientItems.sort((a, b) => a.Index - b.Index);
this.commonInitIndex('clientItems');
},
initRuleTypeFor(items, prefix) {
var thisObj = this;
var length = items.length;
for (var i = 0; i < length; i++) {
var ruleId = "#" + prefix + items[i].Index + items[i].node;
var $selectize = $(ruleId).get(0);
var el = $(ruleId).get(0);
if (!el || !el.selectize) continue; // DOM 尚未渲染或未初始化 selectize,跳过
if (items[i].ApprovalRules != null && items[i].ApprovalRules.length > 0) {
var ruleArr = items[i].ApprovalRules.split(',');
$selectize.selectize.setValue(ruleArr);
} else if ($selectize) {
$selectize.selectize.setValue([]);
el.selectize.setValue(ruleArr);
} else {
el.selectize.setValue([]);
}
}
},
@@ -50,7 +50,7 @@ html {
flex-direction: column;
align-items: center;
width: 100%;
padding: 0 50px;
/*padding: 0 50px;*/
position: relative;
font-size: 12px;
}
@@ -568,3 +568,11 @@ html {
color: #ff7875 !important;
opacity: 1;
}
/* 需求③:多分支连线。多个 .col-box 时,中间列(非首非尾)的右覆盖线宽度置 0,避免连线断裂 */
.col-box:not(:first-child):not(:last-child) .top-right-cover-line {
width: 0;
}
.col-box:not(:first-child):not(:last-child) .bottom-right-cover-line {
width: 0;
}