|
|
@@ -29,9 +29,12 @@
|
|
|
<!-- 流程发起节点 -->
|
|
|
<div class="flow-node start-node">
|
|
|
<div class="node-header">流程发起</div>
|
|
|
- <div class="node-content">
|
|
|
- <span>发起人:所有人</span>
|
|
|
-
|
|
|
+ <div class="node-content" @click="handleEditStartNode" :class="{ 'missing-approver': !initiatorHandlerId }">
|
|
|
+ <div class="node-info">
|
|
|
+ <span class="node-name">发起人</span>
|
|
|
+ <span class="approver-info">{{ initiatorHandlerName || '未设置' }}</span>
|
|
|
+ </div>
|
|
|
+ <el-icon><ArrowRight /></el-icon>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
@@ -146,8 +149,14 @@ const approvalNodes = ref<OrderCustomerFlowNode[]>([]);
|
|
|
const detailLoading = ref(false);
|
|
|
const submitLoading = ref(false);
|
|
|
|
|
|
+// 流程发起人信息
|
|
|
+const initiatorHandlerId = ref('');
|
|
|
+const initiatorHandlerName = ref('');
|
|
|
+const initiatorNodeId = ref<string>(''); // 编辑模式下开始节点的原始ID
|
|
|
+
|
|
|
// 新增的响应式变量
|
|
|
const originalNodes = ref<OrderCustomerFlowNode[]>([]); // 保存原始节点数据用于对比
|
|
|
+const originalInitiatorId = ref(''); // 保存原始发起人ID用于对比
|
|
|
|
|
|
// ==================== 联系人选择弹窗 ====================
|
|
|
const selectDialogVisible = ref(false);
|
|
|
@@ -156,6 +165,7 @@ const contactList = ref<any[]>([]);
|
|
|
const contactSearchKey = ref('');
|
|
|
const selectedContacts = ref<any[]>([]);
|
|
|
const currentEditIndex = ref<number>(-1);
|
|
|
+const currentEditType = ref<'start' | 'approval'>('approval'); // 区分编辑开始节点还是审批节点
|
|
|
|
|
|
const filteredContactList = computed(() => {
|
|
|
const key = contactSearchKey.value.trim().toLowerCase();
|
|
|
@@ -183,16 +193,27 @@ const loadContactList = async () => {
|
|
|
|
|
|
/** 根据 contactList 为已有节点填充 handlerName(编辑模式下后端不返回 handlerName,需前端自行匹配) */
|
|
|
const populateHandlerNames = () => {
|
|
|
- if (contactList.value.length === 0 || approvalNodes.value.length === 0) return;
|
|
|
- approvalNodes.value.forEach((node) => {
|
|
|
- if (node.handlerId && !node.handlerName) {
|
|
|
- const ids = node.handlerId.split(',').filter(Boolean);
|
|
|
- node.handlerName = contactList.value
|
|
|
- .filter((c) => ids.includes(String(c.id)))
|
|
|
- .map((c) => c.contactName || c.name)
|
|
|
- .join(',');
|
|
|
- }
|
|
|
- });
|
|
|
+ if (contactList.value.length === 0) return;
|
|
|
+ // 填充开始节点 handlerName
|
|
|
+ if (initiatorHandlerId.value && !initiatorHandlerName.value) {
|
|
|
+ const ids = initiatorHandlerId.value.split(',').filter(Boolean);
|
|
|
+ initiatorHandlerName.value = contactList.value
|
|
|
+ .filter((c) => ids.includes(String(c.id)))
|
|
|
+ .map((c) => c.contactName || c.name)
|
|
|
+ .join(',');
|
|
|
+ }
|
|
|
+ // 填充审批节点 handlerName
|
|
|
+ if (approvalNodes.value.length > 0) {
|
|
|
+ approvalNodes.value.forEach((node) => {
|
|
|
+ if (node.handlerId && !node.handlerName) {
|
|
|
+ const ids = node.handlerId.split(',').filter(Boolean);
|
|
|
+ node.handlerName = contactList.value
|
|
|
+ .filter((c) => ids.includes(String(c.id)))
|
|
|
+ .map((c) => c.contactName || c.name)
|
|
|
+ .join(',');
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
// 同步更新 originalNodes,避免 handlerName 填充导致误判为"有未保存的更改"
|
|
|
originalNodes.value = JSON.parse(JSON.stringify(approvalNodes.value));
|
|
|
};
|
|
|
@@ -220,12 +241,18 @@ const removeContact = (contact: any) => {
|
|
|
};
|
|
|
|
|
|
const handleConfirmSelect = () => {
|
|
|
- if (currentEditIndex.value < 0) return;
|
|
|
- const currentNode = approvalNodes.value[currentEditIndex.value];
|
|
|
- // 存储处理人ID
|
|
|
- currentNode.handlerId = selectedContacts.value.map((c) => String(c.id)).join(',');
|
|
|
- // 存储处理人姓名(用于显示)
|
|
|
- currentNode.handlerName = selectedContacts.value.map((c) => c.contactName || c.name).join(',');
|
|
|
+ if (currentEditType.value === 'start') {
|
|
|
+ // 保存流程发起人
|
|
|
+ initiatorHandlerId.value = selectedContacts.value.map((c) => String(c.id)).join(',');
|
|
|
+ initiatorHandlerName.value = selectedContacts.value.map((c) => c.contactName || c.name).join(',');
|
|
|
+ } else {
|
|
|
+ if (currentEditIndex.value < 0) return;
|
|
|
+ const currentNode = approvalNodes.value[currentEditIndex.value];
|
|
|
+ // 存储处理人ID
|
|
|
+ currentNode.handlerId = selectedContacts.value.map((c) => String(c.id)).join(',');
|
|
|
+ // 存储处理人姓名(用于显示)
|
|
|
+ currentNode.handlerName = selectedContacts.value.map((c) => c.contactName || c.name).join(',');
|
|
|
+ }
|
|
|
selectDialogVisible.value = false;
|
|
|
};
|
|
|
|
|
|
@@ -238,8 +265,19 @@ const loadFlowDetail = async (id: number) => {
|
|
|
const res = await getOrderFlowDetail(id);
|
|
|
const detail = res.data;
|
|
|
flowName.value = detail?.flowName ?? '';
|
|
|
- // 深拷贝节点数据,保存原始数据用于对比
|
|
|
- const nodes = (detail?.flowNodes ?? []).map((node: OrderCustomerFlowNode) => ({ ...node }));
|
|
|
+ // 分离开始节点和审批节点
|
|
|
+ const allNodes: OrderCustomerFlowNode[] = (detail?.flowNodes ?? []).map((node: OrderCustomerFlowNode) => ({ ...node }));
|
|
|
+ // 开始节点:nodeType 为 0 且 sort 最小(后端返回数字类型,用 == 兼容)
|
|
|
+ const startNode = allNodes.find((n) => Number(n.nodeType) === 0 && Number(n.sort) === 0)
|
|
|
+ || allNodes.find((n) => Number(n.nodeType) === 0);
|
|
|
+ if (startNode) {
|
|
|
+ initiatorHandlerId.value = startNode.handlerId || '';
|
|
|
+ initiatorHandlerName.value = startNode.handlerName || '';
|
|
|
+ initiatorNodeId.value = String(startNode.id || '');
|
|
|
+ originalInitiatorId.value = startNode.handlerId || '';
|
|
|
+ }
|
|
|
+ // 审批节点(nodeType != 0)
|
|
|
+ const nodes = allNodes.filter((n) => Number(n.nodeType) !== 0);
|
|
|
approvalNodes.value = nodes;
|
|
|
originalNodes.value = JSON.parse(JSON.stringify(nodes)); // 深拷贝保存原始数据
|
|
|
} catch (e) {
|
|
|
@@ -321,7 +359,25 @@ const updateNodeOrders = () => {
|
|
|
});
|
|
|
};
|
|
|
|
|
|
+const handleEditStartNode = () => {
|
|
|
+ currentEditType.value = 'start';
|
|
|
+ contactSearchKey.value = '';
|
|
|
+ // 回显已选中的发起人
|
|
|
+ if (initiatorHandlerId.value) {
|
|
|
+ const handlerIds = initiatorHandlerId.value.split(',').filter(Boolean);
|
|
|
+ selectedContacts.value = contactList.value.filter((c) => handlerIds.includes(String(c.id)));
|
|
|
+ } else {
|
|
|
+ selectedContacts.value = [];
|
|
|
+ }
|
|
|
+ selectDialogVisible.value = true;
|
|
|
+ // 若联系人列表未加载,则加载
|
|
|
+ if (contactList.value.length === 0) {
|
|
|
+ loadContactList();
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
const handleEditNode = (index: number) => {
|
|
|
+ currentEditType.value = 'approval';
|
|
|
currentEditIndex.value = index;
|
|
|
contactSearchKey.value = '';
|
|
|
// 回显已选中的联系人(根据 handlerId 匹配)
|
|
|
@@ -383,10 +439,22 @@ const handleSave = async () => {
|
|
|
// 构造保存数据
|
|
|
const saveData: OrderCustomerFlowSaveBo = {
|
|
|
flowName: flowName.value.trim(),
|
|
|
- flowNodes: approvalNodes.value.map((node, index) => ({
|
|
|
- ...node,
|
|
|
- sort: index + 1 // 确保顺序正确
|
|
|
- }))
|
|
|
+ flowNodes: [
|
|
|
+ // 开始节点
|
|
|
+ {
|
|
|
+ ...(initiatorNodeId.value ? { id: initiatorNodeId.value } : {}),
|
|
|
+ nodeName: '流程发起',
|
|
|
+ nodeType: '0',
|
|
|
+ sort: 0,
|
|
|
+ handlerId: initiatorHandlerId.value || '0',
|
|
|
+ handlerName: initiatorHandlerName.value || '所有人'
|
|
|
+ },
|
|
|
+ // 审批节点
|
|
|
+ ...approvalNodes.value.map((node, index) => ({
|
|
|
+ ...node,
|
|
|
+ sort: index + 1
|
|
|
+ }))
|
|
|
+ ]
|
|
|
};
|
|
|
|
|
|
// 编辑模式下添加ID
|
|
|
@@ -414,7 +482,13 @@ const handleSave = async () => {
|
|
|
|
|
|
// 新增:检查是否有未保存的更改
|
|
|
const hasUnsavedChanges = computed(() => {
|
|
|
- if (!isEdit.value) return approvalNodes.value.length > 0;
|
|
|
+ if (!isEdit.value) {
|
|
|
+ // 新建模式下,有任一内容即认为有未保存更改
|
|
|
+ return flowName.value.trim() !== '' || approvalNodes.value.length > 0 || initiatorHandlerId.value !== '';
|
|
|
+ }
|
|
|
+
|
|
|
+ // 编辑模式:检查发起人是否变更
|
|
|
+ if (initiatorHandlerId.value !== originalInitiatorId.value) return true;
|
|
|
|
|
|
// 比较节点数量
|
|
|
if (approvalNodes.value.length !== originalNodes.value.length) return true;
|