|
@@ -44,7 +44,7 @@
|
|
|
:key="nodeType.typeCode"
|
|
:key="nodeType.typeCode"
|
|
|
draggable="true"
|
|
draggable="true"
|
|
|
@dragstart="onDragStart($event, nodeType)"
|
|
@dragstart="onDragStart($event, nodeType)"
|
|
|
- :style="{ borderColor: nodeType.typeColor }"
|
|
|
|
|
|
|
+ :style="{ borderColor: nodeType.typeColor, '--node-color': nodeType.typeColor }"
|
|
|
>
|
|
>
|
|
|
<i :class="nodeType.typeIcon" :style="{ color: nodeType.typeColor }"></i>
|
|
<i :class="nodeType.typeIcon" :style="{ color: nodeType.typeColor }"></i>
|
|
|
<span>{{ nodeType.typeName }}</span>
|
|
<span>{{ nodeType.typeName }}</span>
|
|
@@ -64,6 +64,11 @@
|
|
|
@mousedown="onCanvasMouseDown"
|
|
@mousedown="onCanvasMouseDown"
|
|
|
@keydown.delete="handleDelete"
|
|
@keydown.delete="handleDelete"
|
|
|
@keydown.backspace="handleDelete"
|
|
@keydown.backspace="handleDelete"
|
|
|
|
|
+ @keydown.ctrl.c="copyNode"
|
|
|
|
|
+ @keydown.meta.c="copyNode"
|
|
|
|
|
+ @keydown.ctrl.v="pasteNode"
|
|
|
|
|
+ @keydown.meta.v="pasteNode"
|
|
|
|
|
+ @contextmenu.prevent="showContextMenu"
|
|
|
>
|
|
>
|
|
|
<svg class="canvas-svg" ref="canvasSvg" :width="canvasSize.width" :height="canvasSize.height"
|
|
<svg class="canvas-svg" ref="canvasSvg" :width="canvasSize.width" :height="canvasSize.height"
|
|
|
:class="{ 'dragging-canvas': isDraggingCanvas }">
|
|
:class="{ 'dragging-canvas': isDraggingCanvas }">
|
|
@@ -78,11 +83,11 @@
|
|
|
markerWidth="10"
|
|
markerWidth="10"
|
|
|
markerHeight="10"
|
|
markerHeight="10"
|
|
|
refX="9"
|
|
refX="9"
|
|
|
- refY="3"
|
|
|
|
|
|
|
+ refY="5"
|
|
|
orient="auto"
|
|
orient="auto"
|
|
|
- markerUnits="strokeWidth"
|
|
|
|
|
|
|
+ markerUnits="userSpaceOnUse"
|
|
|
>
|
|
>
|
|
|
- <path d="M0,0 L0,6 L9,3 z" fill="#999" />
|
|
|
|
|
|
|
+ <path d="M0,1 L0,9 L9,5 z" fill="#999" />
|
|
|
</marker>
|
|
</marker>
|
|
|
<!-- 选中状态的箭头 -->
|
|
<!-- 选中状态的箭头 -->
|
|
|
<marker
|
|
<marker
|
|
@@ -90,11 +95,11 @@
|
|
|
markerWidth="10"
|
|
markerWidth="10"
|
|
|
markerHeight="10"
|
|
markerHeight="10"
|
|
|
refX="9"
|
|
refX="9"
|
|
|
- refY="3"
|
|
|
|
|
|
|
+ refY="5"
|
|
|
orient="auto"
|
|
orient="auto"
|
|
|
- markerUnits="strokeWidth"
|
|
|
|
|
|
|
+ markerUnits="userSpaceOnUse"
|
|
|
>
|
|
>
|
|
|
- <path d="M0,0 L0,6 L9,3 z" fill="#1890ff" />
|
|
|
|
|
|
|
+ <path d="M0,1 L0,9 L9,5 z" fill="#666" />
|
|
|
</marker>
|
|
</marker>
|
|
|
</defs>
|
|
</defs>
|
|
|
<rect :width="canvasSize.width" :height="canvasSize.height" fill="url(#grid)" />
|
|
<rect :width="canvasSize.width" :height="canvasSize.height" fill="url(#grid)" />
|
|
@@ -145,6 +150,7 @@
|
|
|
v-for="node in nodes"
|
|
v-for="node in nodes"
|
|
|
:key="node.nodeKey"
|
|
:key="node.nodeKey"
|
|
|
class="node-group"
|
|
class="node-group"
|
|
|
|
|
+ :class="{ selected: selectedNode === node }"
|
|
|
:transform="`translate(${node.posX}, ${node.posY})`"
|
|
:transform="`translate(${node.posX}, ${node.posY})`"
|
|
|
@mousedown="onNodeMouseDown($event, node)"
|
|
@mousedown="onNodeMouseDown($event, node)"
|
|
|
@click.stop="selectNode(node)"
|
|
@click.stop="selectNode(node)"
|
|
@@ -177,111 +183,290 @@
|
|
|
</g>
|
|
</g>
|
|
|
</g>
|
|
</g>
|
|
|
</svg>
|
|
</svg>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 右键菜单 -->
|
|
|
|
|
+ <div
|
|
|
|
|
+ v-show="contextMenu.visible"
|
|
|
|
|
+ class="context-menu"
|
|
|
|
|
+ :style="{ left: contextMenu.x + 'px', top: contextMenu.y + 'px' }"
|
|
|
|
|
+ @click.stop
|
|
|
|
|
+ >
|
|
|
|
|
+ <div
|
|
|
|
|
+ v-if="contextMenu.node"
|
|
|
|
|
+ class="menu-item"
|
|
|
|
|
+ @click="copyNodeFromMenu"
|
|
|
|
|
+ >
|
|
|
|
|
+ <i class="el-icon-document-copy"></i>
|
|
|
|
|
+ <span>复制节点</span>
|
|
|
|
|
+ <span class="shortcut">Ctrl+C</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div
|
|
|
|
|
+ v-if="copiedNode"
|
|
|
|
|
+ class="menu-item"
|
|
|
|
|
+ @click="pasteNodeFromMenu"
|
|
|
|
|
+ >
|
|
|
|
|
+ <i class="el-icon-document-add"></i>
|
|
|
|
|
+ <span>粘贴节点</span>
|
|
|
|
|
+ <span class="shortcut">Ctrl+V</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="menu-divider" v-if="contextMenu.node && copiedNode"></div>
|
|
|
|
|
+ <div
|
|
|
|
|
+ v-if="contextMenu.node"
|
|
|
|
|
+ class="menu-item danger"
|
|
|
|
|
+ @click="deleteNodeFromMenu"
|
|
|
|
|
+ >
|
|
|
|
|
+ <i class="el-icon-delete"></i>
|
|
|
|
|
+ <span>删除节点</span>
|
|
|
|
|
+ <span class="shortcut">Delete</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div
|
|
|
|
|
+ v-if="!contextMenu.node"
|
|
|
|
|
+ class="menu-item disabled"
|
|
|
|
|
+ >
|
|
|
|
|
+ <i class="el-icon-info"></i>
|
|
|
|
|
+ <span>请选中节点</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
<!-- 右侧属性面板 -->
|
|
<!-- 右侧属性面板 -->
|
|
|
<div class="property-panel" v-if="selectedNode || selectedEdge">
|
|
<div class="property-panel" v-if="selectedNode || selectedEdge">
|
|
|
<div class="panel-title">
|
|
<div class="panel-title">
|
|
|
- {{ selectedNode ? '节点属性' : '连线属性' }}
|
|
|
|
|
|
|
+ <span>
|
|
|
|
|
+ <i :class="selectedNode ? 'el-icon-setting' : 'el-icon-share'"></i>
|
|
|
|
|
+ {{ selectedNode ? '节点属性' : '连线属性' }}
|
|
|
|
|
+ </span>
|
|
|
<i class="el-icon-close" @click="clearSelection"></i>
|
|
<i class="el-icon-close" @click="clearSelection"></i>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
<!-- 节点属性 -->
|
|
<!-- 节点属性 -->
|
|
|
- <el-form v-if="selectedNode" label-width="80px" size="small">
|
|
|
|
|
- <el-form-item label="节点内容">
|
|
|
|
|
- <el-input
|
|
|
|
|
- v-model="selectedNode.nodeName"
|
|
|
|
|
- type="textarea"
|
|
|
|
|
- :autosize="{ minRows: 1, maxRows: 6 }"
|
|
|
|
|
- />
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
- <div v-if="selectedNode.nodeType == 'http'">
|
|
|
|
|
- <el-form-item label="URL地址">
|
|
|
|
|
- <el-input v-model="selectedNode.nodeConfig.url" />
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
- </div>
|
|
|
|
|
- <el-form-item label="节点类型">
|
|
|
|
|
- <el-input :value="getNodeTypeName(selectedNode.nodeType)" disabled />
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
- <el-form-item label="节点颜色">
|
|
|
|
|
- <el-color-picker v-model="selectedNode.nodeColor" />
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
- <el-form-item label="X坐标">
|
|
|
|
|
- <el-input-number v-model="selectedNode.posX" :min="0" />
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
- <el-form-item label="Y坐标">
|
|
|
|
|
- <el-input-number v-model="selectedNode.posY" :min="0" />
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
- <el-form-item>
|
|
|
|
|
- <el-button type="danger" size="mini" @click="deleteNode">删除节点</el-button>
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
- </el-form>
|
|
|
|
|
-
|
|
|
|
|
- <!-- 连线属性 -->
|
|
|
|
|
- <el-form v-if="selectedEdge" label-width="80px" size="small">
|
|
|
|
|
- <el-form-item label="备注">
|
|
|
|
|
- <el-input v-model="selectedEdge.edgeLabel" />
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
- <el-form-item label="连线颜色">
|
|
|
|
|
- <el-color-picker v-model="selectedEdge.edgeColor" />
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
- <el-form-item v-if="edgeSourceNode.nodeType == 'AI_CALL_TASK' || edgeSourceNode.nodeType == 'AI_SEND_MSG_TASK' || edgeSourceNode.nodeType == 'AI_ADD_WX_TASK'">
|
|
|
|
|
- <el-button type="success" @click="addCondition">新增条件判断</el-button>
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
- <div v-if="edgeSourceNode.nodeType == 'AI_CALL_TASK'">
|
|
|
|
|
- <div v-for="(item, index) in selectedEdge.conditionExprObj">
|
|
|
|
|
- <el-form-item label="是否拨通">
|
|
|
|
|
- <div style="display: flex;justify-content: space-between;">
|
|
|
|
|
- <el-select v-model="item.callConnected">
|
|
|
|
|
- <el-option :value="false" label="否" />
|
|
|
|
|
- <el-option :value="true" label="是" />
|
|
|
|
|
- </el-select>
|
|
|
|
|
- <el-button type="danger" size="mini" round @click="removeCondition(index)">删除</el-button>
|
|
|
|
|
|
|
+ <div v-if="selectedNode" class="property-content">
|
|
|
|
|
+ <el-form label-width="90px" size="small" label-position="left">
|
|
|
|
|
+ <!-- 基本信息 -->
|
|
|
|
|
+ <div class="property-section">
|
|
|
|
|
+ <div class="section-title">
|
|
|
|
|
+ <i class="el-icon-document"></i>基本信息
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <el-form-item label="节点内容">
|
|
|
|
|
+ <el-input
|
|
|
|
|
+ v-model="selectedNode.nodeName"
|
|
|
|
|
+ type="textarea"
|
|
|
|
|
+ :autosize="{ minRows: 2, maxRows: 4 }"
|
|
|
|
|
+ placeholder="请输入节点内容"
|
|
|
|
|
+ />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="节点类型">
|
|
|
|
|
+ <div class="node-type-badge">
|
|
|
|
|
+ <i :class="selectedNode.nodeIcon" :style="{ color: selectedNode.nodeColor }"></i>
|
|
|
|
|
+ <span>{{ getNodeTypeName(selectedNode.nodeType) }}</span>
|
|
|
</div>
|
|
</div>
|
|
|
</el-form-item>
|
|
</el-form-item>
|
|
|
- <el-form-item label="延迟时间" v-if="item.callConnected == false">
|
|
|
|
|
- <el-input v-model="item.callTime" style="width: 180px">
|
|
|
|
|
- <template slot="append">分钟</template>
|
|
|
|
|
- </el-input>
|
|
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- AI外呼配置 -->
|
|
|
|
|
+ <div v-if="selectedNode.nodeType == 'AI_CALL_TASK'" class="property-section">
|
|
|
|
|
+ <div class="section-title">
|
|
|
|
|
+ <i class="el-icon-phone"></i>外呼配置
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <el-form-item label="机器人">
|
|
|
|
|
+ <el-select v-model="selectedNode.nodeConfig.robot" filterable placeholder="请选择机器人">
|
|
|
|
|
+ <el-option v-for="item in robotList" :key="item.id" :label="item.name + '('+item.num+')'" :value="item.id"/>
|
|
|
|
|
+ </el-select>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="话术">
|
|
|
|
|
+ <el-select v-model="selectedNode.nodeConfig.dialogId" filterable placeholder="请选择话术">
|
|
|
|
|
+ <el-option v-for="item in dialogList" :key="item.id" :label="item.name" :value="item.id"/>
|
|
|
|
|
+ </el-select>
|
|
|
</el-form-item>
|
|
</el-form-item>
|
|
|
- <el-form-item label="意向度">
|
|
|
|
|
- <el-select v-model="item.intention" placeholder="意向等级" filterable clearable>
|
|
|
|
|
- <el-option v-for="item in levelList" :label="item.dictLabel" :value="item.dictValue"/>
|
|
|
|
|
|
|
+ <el-form-item label="主叫分组">
|
|
|
|
|
+ <el-select v-model="selectedNode.nodeConfig.cidGroupId" filterable placeholder="请选择主叫分组">
|
|
|
|
|
+ <el-option v-for="item in CIDGroupList" :key="item.id" :label="item.name" :value="item.id"/>
|
|
|
</el-select>
|
|
</el-select>
|
|
|
</el-form-item>
|
|
</el-form-item>
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
- <div v-if="edgeSourceNode.nodeType == 'AI_SEND_MSG_TASK'">
|
|
|
|
|
- <div v-for="(item, index) in selectedEdge.conditionExprObj">
|
|
|
|
|
- <el-form-item label="添加状态">
|
|
|
|
|
- <div style="display: flex;justify-content: space-between;">
|
|
|
|
|
- <el-select v-model="item.isAdd">
|
|
|
|
|
- <el-option :value="false" label="未同意" />
|
|
|
|
|
- <el-option :value="true" label="已同意" />
|
|
|
|
|
|
|
+ <el-form-item label="模式">
|
|
|
|
|
+ <el-select v-model="selectedNode.nodeConfig.mode" placeholder="请选择模式">
|
|
|
|
|
+ <el-option label="呼叫机器人后挂断" :value="7"/>
|
|
|
|
|
+ </el-select>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+
|
|
|
|
|
+ <div class="form-row">
|
|
|
|
|
+ <el-form-item label="呼叫倍率" class="half-width">
|
|
|
|
|
+ <el-select v-model="selectedNode.nodeConfig.multiplier" @change="handleConfigChange" placeholder="选择倍率">
|
|
|
|
|
+ <el-option label="1" :value="1"/>
|
|
|
|
|
+ <el-option label="2" :value="2"/>
|
|
|
|
|
+ <el-option label="3" :value="3"/>
|
|
|
|
|
+ <el-option label="4" :value="4"/>
|
|
|
|
|
+ <el-option label="5" :value="5"/>
|
|
|
</el-select>
|
|
</el-select>
|
|
|
- <el-button type="danger" size="mini" round @click="removeCondition(index)">删除</el-button>
|
|
|
|
|
- </div>
|
|
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <el-form-item label="自动重呼">
|
|
|
|
|
+ <el-radio-group v-model="selectedNode.nodeConfig.autoRecall" @change="handleConfigChange">
|
|
|
|
|
+ <el-radio :label="0">否</el-radio>
|
|
|
|
|
+ <el-radio :label="1">是</el-radio>
|
|
|
|
|
+ </el-radio-group>
|
|
|
</el-form-item>
|
|
</el-form-item>
|
|
|
- <el-form-item label="超时时间" v-if="item.isAdd == false">
|
|
|
|
|
- <el-input v-model="item.addTime" style="width: 180px">
|
|
|
|
|
- <template slot="append">分钟</template>
|
|
|
|
|
- </el-input>
|
|
|
|
|
|
|
+
|
|
|
|
|
+ <el-form-item label="重呼次数" v-if="selectedNode.nodeConfig.autoRecall == 1">
|
|
|
|
|
+ <el-select v-model="selectedNode.nodeConfig.recallTimes" @change="handleConfigChange" placeholder="选择重呼次数">
|
|
|
|
|
+ <el-option label="不自动重呼" :value="0"/>
|
|
|
|
|
+ <el-option label="1次" :value="1"/>
|
|
|
|
|
+ <el-option label="2次" :value="2"/>
|
|
|
|
|
+ <el-option label="3次" :value="3"/>
|
|
|
|
|
+ <el-option label="4次" :value="4"/>
|
|
|
|
|
+ <el-option label="5次" :value="5"/>
|
|
|
|
|
+ </el-select>
|
|
|
</el-form-item>
|
|
</el-form-item>
|
|
|
</div>
|
|
</div>
|
|
|
- </div>
|
|
|
|
|
- <div v-if="edgeSourceNode.nodeType == 'AI_ADD_WX_TASK'">
|
|
|
|
|
|
|
|
|
|
- </div>
|
|
|
|
|
- <el-form-item>
|
|
|
|
|
- <el-button type="danger" size="mini" @click="deleteEdge">删除连线</el-button>
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
- </el-form>
|
|
|
|
|
|
|
+ <!-- 样式设置 -->
|
|
|
|
|
+ <div class="property-section">
|
|
|
|
|
+ <div class="section-title">
|
|
|
|
|
+ <i class="el-icon-brush"></i>样式设置
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <el-form-item label="节点颜色">
|
|
|
|
|
+ <el-color-picker v-model="selectedNode.nodeColor" show-alpha />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <!-- <div class="form-row">
|
|
|
|
|
+ <el-form-item label="X坐标" class="half-width">
|
|
|
|
|
+ <el-input-number v-model="selectedNode.posX" :min="0" controls-position="right" />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="Y坐标" class="half-width">
|
|
|
|
|
+ <el-input-number v-model="selectedNode.posY" :min="0" controls-position="right" />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </div> -->
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 操作按钮 -->
|
|
|
|
|
+ <div class="property-actions">
|
|
|
|
|
+ <el-button type="primary" size="small" icon="el-icon-document-copy" @click="copyNode">复制节点</el-button>
|
|
|
|
|
+ <el-button type="danger" size="small" icon="el-icon-delete" @click="deleteNode">删除节点</el-button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </el-form>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 连线属性 -->
|
|
|
|
|
+ <div v-if="selectedEdge" class="property-content">
|
|
|
|
|
+ <el-form label-width="90px" size="small" label-position="left">
|
|
|
|
|
+ <!-- 基本信息 -->
|
|
|
|
|
+ <div class="property-section">
|
|
|
|
|
+ <div class="section-title">
|
|
|
|
|
+ <i class="el-icon-share"></i>连线信息
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <el-form-item label="备注">
|
|
|
|
|
+ <el-input v-model="selectedEdge.edgeLabel" placeholder="请输入备注" />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="连线颜色">
|
|
|
|
|
+ <el-color-picker v-model="selectedEdge.edgeColor" show-alpha />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 条件判断 -->
|
|
|
|
|
+ <div v-if="edgeSourceNode.nodeType == 'AI_CALL_TASK' || edgeSourceNode.nodeType == 'AI_SEND_MSG_TASK' || edgeSourceNode.nodeType == 'AI_ADD_WX_TASK'" class="property-section">
|
|
|
|
|
+ <div class="section-title">
|
|
|
|
|
+ <i class="el-icon-set-up"></i>条件判断
|
|
|
|
|
+ <el-button type="success" size="mini" icon="el-icon-plus" @click="addCondition" class="add-condition-btn">新增条件</el-button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- AI外呼任务条件 -->
|
|
|
|
|
+ <div v-if="edgeSourceNode.nodeType == 'AI_CALL_TASK'" class="conditions-container">
|
|
|
|
|
+ <div v-for="(item, index) in selectedEdge.conditionExprObj" :key="index" class="condition-item">
|
|
|
|
|
+ <div class="condition-header">
|
|
|
|
|
+ <span class="condition-number">条件 {{ index + 1 }}</span>
|
|
|
|
|
+ <el-button type="danger" size="mini" icon="el-icon-delete" circle @click="removeCondition(index)"></el-button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="condition-content">
|
|
|
|
|
+ <el-form-item label="是否拨通">
|
|
|
|
|
+ <el-select v-model="item.callConnected" placeholder="请选择">
|
|
|
|
|
+ <el-option :value="false" label="否" />
|
|
|
|
|
+ <el-option :value="true" label="是" />
|
|
|
|
|
+ </el-select>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="延迟时间" v-if="item.callConnected == false">
|
|
|
|
|
+ <el-input v-model="item.callTime" placeholder="请输入时间">
|
|
|
|
|
+ <template slot="append">分钟</template>
|
|
|
|
|
+ </el-input>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="意向度">
|
|
|
|
|
+ <el-select v-model="item.intention" placeholder="请选择意向等级" filterable clearable>
|
|
|
|
|
+ <el-option v-for="level in levelList" :key="level.dictValue" :label="level.dictLabel" :value="level.dictValue"/>
|
|
|
|
|
+ </el-select>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div v-if="selectedEdge.conditionExprObj.length === 0" class="empty-condition">
|
|
|
|
|
+ <i class="el-icon-info"></i>
|
|
|
|
|
+ <span>暂无条件,请点击上方按钮添加</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 加微信任务条件 -->
|
|
|
|
|
+ <div v-if="edgeSourceNode.nodeType == 'AI_SEND_MSG_TASK'" class="conditions-container">
|
|
|
|
|
+ <div v-for="(item, index) in selectedEdge.conditionExprObj" :key="index" class="condition-item">
|
|
|
|
|
+ <div class="condition-header">
|
|
|
|
|
+ <span class="condition-number">条件 {{ index + 1 }}</span>
|
|
|
|
|
+ <el-button type="danger" size="mini" icon="el-icon-delete" circle @click="removeCondition(index)"></el-button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="condition-content">
|
|
|
|
|
+ <el-form-item label="添加状态">
|
|
|
|
|
+ <el-select v-model="item.isAdd" placeholder="请选择">
|
|
|
|
|
+ <el-option :value="false" label="未同意" />
|
|
|
|
|
+ <el-option :value="true" label="已同意" />
|
|
|
|
|
+ </el-select>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="超时时间" v-if="item.isAdd == false">
|
|
|
|
|
+ <el-input v-model="item.addTime" placeholder="请输入时间">
|
|
|
|
|
+ <template slot="append">分钟</template>
|
|
|
|
|
+ </el-input>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div v-if="selectedEdge.conditionExprObj.length === 0" class="empty-condition">
|
|
|
|
|
+ <i class="el-icon-info"></i>
|
|
|
|
|
+ <span>暂无条件,请点击上方按钮添加</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 其他任务类型条件 -->
|
|
|
|
|
+ <div v-if="edgeSourceNode.nodeType == 'AI_ADD_WX_TASK'" class="conditions-container">
|
|
|
|
|
+ <div class="empty-condition">
|
|
|
|
|
+ <i class="el-icon-info"></i>
|
|
|
|
|
+ <span>此类型暂不支持条件设置</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 操作按钮 -->
|
|
|
|
|
+ <div class="property-actions">
|
|
|
|
|
+ <el-button type="danger" size="small" icon="el-icon-delete" @click="deleteEdge">删除连线</el-button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </el-form>
|
|
|
|
|
+ </div>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
</template>
|
|
</template>
|
|
|
|
|
|
|
|
<script>
|
|
<script>
|
|
|
|
|
+import {
|
|
|
|
|
+ listRobotic,
|
|
|
|
|
+ getRobotic,
|
|
|
|
|
+ delRobotic,
|
|
|
|
|
+ addRobotic,
|
|
|
|
|
+ updateRobotic,
|
|
|
|
|
+ exportRobotic,
|
|
|
|
|
+ calleesList,
|
|
|
|
|
+ statusList,
|
|
|
|
|
+ startRobotic,
|
|
|
|
|
+ stopRobotic,
|
|
|
|
|
+ companyUserList,
|
|
|
|
|
+ wxList,
|
|
|
|
|
+ taskRun,
|
|
|
|
|
+ getTypes,
|
|
|
|
|
+ getSmsTempList,
|
|
|
|
|
+ getCIDGroupList
|
|
|
|
|
+} from "@/api/company/companyVoiceRobotic";
|
|
|
import { getWorkflow, addWorkflow, updateWorkflow, getNodeTypes } from '@/api/company/companyWorkflow'
|
|
import { getWorkflow, addWorkflow, updateWorkflow, getNodeTypes } from '@/api/company/companyWorkflow'
|
|
|
import {getDicts} from "@/api/system/dict/data";
|
|
import {getDicts} from "@/api/system/dict/data";
|
|
|
|
|
|
|
@@ -334,6 +519,15 @@ export default {
|
|
|
isDraggingCanvas: false,
|
|
isDraggingCanvas: false,
|
|
|
// 画布拖动起始点
|
|
// 画布拖动起始点
|
|
|
canvasDragStart: { x: 0, y: 0 },
|
|
canvasDragStart: { x: 0, y: 0 },
|
|
|
|
|
+ // 复制的节点
|
|
|
|
|
+ copiedNode: null,
|
|
|
|
|
+ // 右键菜单
|
|
|
|
|
+ contextMenu: {
|
|
|
|
|
+ visible: false,
|
|
|
|
|
+ x: 0,
|
|
|
|
|
+ y: 0,
|
|
|
|
|
+ node: null
|
|
|
|
|
+ },
|
|
|
// 工作流类型字典
|
|
// 工作流类型字典
|
|
|
workflowTypeOptions: [
|
|
workflowTypeOptions: [
|
|
|
{ dictValue: '1', dictLabel: '对话流程' },
|
|
{ dictValue: '1', dictLabel: '对话流程' },
|
|
@@ -342,6 +536,9 @@ export default {
|
|
|
],
|
|
],
|
|
|
conditionExprTemp:{
|
|
conditionExprTemp:{
|
|
|
},
|
|
},
|
|
|
|
|
+ CIDGroupList:[],
|
|
|
|
|
+ robotList: [],
|
|
|
|
|
+ dialogList: [],
|
|
|
}
|
|
}
|
|
|
},
|
|
},
|
|
|
created() {
|
|
created() {
|
|
@@ -361,11 +558,14 @@ export default {
|
|
|
// window.addEventListener('keydown', this.handleGlobalKeydown)
|
|
// window.addEventListener('keydown', this.handleGlobalKeydown)
|
|
|
document.addEventListener('mousemove', this.onMouseMove)
|
|
document.addEventListener('mousemove', this.onMouseMove)
|
|
|
document.addEventListener('mouseup', this.onMouseUp)
|
|
document.addEventListener('mouseup', this.onMouseUp)
|
|
|
|
|
+ // 点击任意地方隐藏右键菜单
|
|
|
|
|
+ document.addEventListener('click', this.hideContextMenu)
|
|
|
},
|
|
},
|
|
|
beforeDestroy() {
|
|
beforeDestroy() {
|
|
|
// window.removeEventListener('keydown', this.handleGlobalKeydown)
|
|
// window.removeEventListener('keydown', this.handleGlobalKeydown)
|
|
|
document.removeEventListener('mousemove', this.onMouseMove)
|
|
document.removeEventListener('mousemove', this.onMouseMove)
|
|
|
document.removeEventListener('mouseup', this.onMouseUp)
|
|
document.removeEventListener('mouseup', this.onMouseUp)
|
|
|
|
|
+ document.removeEventListener('click', this.hideContextMenu)
|
|
|
},
|
|
},
|
|
|
methods: {
|
|
methods: {
|
|
|
// 聚焦画布容器
|
|
// 聚焦画布容器
|
|
@@ -373,6 +573,12 @@ export default {
|
|
|
this.$refs.canvasContainer.focus()
|
|
this.$refs.canvasContainer.focus()
|
|
|
},
|
|
},
|
|
|
|
|
|
|
|
|
|
+ // 处理配置变化,强制更新视图
|
|
|
|
|
+ handleConfigChange() {
|
|
|
|
|
+ // 强制触发Vue响应式更新
|
|
|
|
|
+ this.$forceUpdate()
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
// 点击画布时聚焦
|
|
// 点击画布时聚焦
|
|
|
onCanvasClick() {
|
|
onCanvasClick() {
|
|
|
this.clearSelection()
|
|
this.clearSelection()
|
|
@@ -382,9 +588,46 @@ export default {
|
|
|
// 点击节点时聚焦
|
|
// 点击节点时聚焦
|
|
|
selectNode(node) {
|
|
selectNode(node) {
|
|
|
this.selectedNode = node
|
|
this.selectedNode = node
|
|
|
- // this.selectedNode.nodeConfig = JSON.parse(node.nodeConfig)
|
|
|
|
|
|
|
+ // 将nodeConfig从字符串解析为对象
|
|
|
|
|
+ if (typeof this.selectedNode.nodeConfig === 'string') {
|
|
|
|
|
+ try {
|
|
|
|
|
+ this.selectedNode.nodeConfig = JSON.parse(this.selectedNode.nodeConfig)
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ this.selectedNode.nodeConfig = {}
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ // 确保nodeConfig是对象
|
|
|
|
|
+ if (!this.selectedNode.nodeConfig || typeof this.selectedNode.nodeConfig !== 'object') {
|
|
|
|
|
+ this.selectedNode.nodeConfig = {}
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
this.selectedEdge = null
|
|
this.selectedEdge = null
|
|
|
this.edgeSourceNode = null
|
|
this.edgeSourceNode = null
|
|
|
|
|
+ if(this.selectedNode.nodeType == "AI_CALL_TASK"){
|
|
|
|
|
+ // 使用$set初始化默认值,确保响应式
|
|
|
|
|
+ if (!this.selectedNode.nodeConfig.mode) {
|
|
|
|
|
+ this.$set(this.selectedNode.nodeConfig, 'mode', 7)
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!this.selectedNode.nodeConfig.multiplier) {
|
|
|
|
|
+ this.$set(this.selectedNode.nodeConfig, 'multiplier', 1)
|
|
|
|
|
+ }
|
|
|
|
|
+ if (this.selectedNode.nodeConfig.autoRecall === undefined) {
|
|
|
|
|
+ this.$set(this.selectedNode.nodeConfig, 'autoRecall', 0)
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!this.selectedNode.nodeConfig.recallTimes && this.selectedNode.nodeConfig.recallTimes !== 0) {
|
|
|
|
|
+ this.$set(this.selectedNode.nodeConfig, 'recallTimes', 0)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ getTypes().then(e => {
|
|
|
|
|
+ this.robotList = e.robot;
|
|
|
|
|
+ this.dialogList = e.dialog;
|
|
|
|
|
+ })
|
|
|
|
|
+ getCIDGroupList().then(res=>{
|
|
|
|
|
+ this.CIDGroupList = res.data;
|
|
|
|
|
+ }).catch(res=>{
|
|
|
|
|
+ console.log(res);
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
this.focusCanvasContainer()
|
|
this.focusCanvasContainer()
|
|
|
},
|
|
},
|
|
|
|
|
|
|
@@ -518,6 +761,23 @@ export default {
|
|
|
console.info(edge)
|
|
console.info(edge)
|
|
|
})
|
|
})
|
|
|
}
|
|
}
|
|
|
|
|
+ // 处理nodes,将nodeConfig从字符串转为对象
|
|
|
|
|
+ if(data.nodes){
|
|
|
|
|
+ data.nodes.forEach(node => {
|
|
|
|
|
+ if (typeof node.nodeConfig === 'string') {
|
|
|
|
|
+ try {
|
|
|
|
|
+ // 使用Object.assign创建新对象,确保响应式
|
|
|
|
|
+ const parsedConfig = JSON.parse(node.nodeConfig)
|
|
|
|
|
+ node.nodeConfig = Object.assign({}, parsedConfig)
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ node.nodeConfig = {}
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!node.nodeConfig || typeof node.nodeConfig !== 'object') {
|
|
|
|
|
+ node.nodeConfig = {}
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
this.nodes = data.nodes || []
|
|
this.nodes = data.nodes || []
|
|
|
this.edges = data.edges || []
|
|
this.edges = data.edges || []
|
|
|
})
|
|
})
|
|
@@ -544,6 +804,96 @@ export default {
|
|
|
generateKey() {
|
|
generateKey() {
|
|
|
return 'node_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9)
|
|
return 'node_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9)
|
|
|
},
|
|
},
|
|
|
|
|
+ /** 复制节点 */
|
|
|
|
|
+ copyNode(e) {
|
|
|
|
|
+ if (!this.selectedNode) {
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ e.preventDefault()
|
|
|
|
|
+ // 深拷贝选中的节点,包括所有属性
|
|
|
|
|
+ this.copiedNode = JSON.parse(JSON.stringify(this.selectedNode))
|
|
|
|
|
+ this.$message.success('节点已复制')
|
|
|
|
|
+ },
|
|
|
|
|
+ /** 粘贴节点 */
|
|
|
|
|
+ pasteNode(e) {
|
|
|
|
|
+ if (!this.copiedNode) {
|
|
|
|
|
+ this.$message.warning('没有可粘贴的节点')
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ e.preventDefault()
|
|
|
|
|
+
|
|
|
|
|
+ // 创建新节点,复制所有属性
|
|
|
|
|
+ const newNode = {
|
|
|
|
|
+ ...this.copiedNode,
|
|
|
|
|
+ nodeKey: this.generateKey(),
|
|
|
|
|
+ // 在原位置偏移一定距离
|
|
|
|
|
+ posX: this.copiedNode.posX + 30,
|
|
|
|
|
+ posY: this.copiedNode.posY + 30,
|
|
|
|
|
+ // 深拷贝 nodeConfig
|
|
|
|
|
+ nodeConfig: this.copiedNode.nodeConfig ? JSON.parse(JSON.stringify(this.copiedNode.nodeConfig)) : {}
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ this.nodes.push(newNode)
|
|
|
|
|
+ // 选中新节点
|
|
|
|
|
+ this.selectedNode = newNode
|
|
|
|
|
+ this.selectedEdge = null
|
|
|
|
|
+
|
|
|
|
|
+ // 检查是否需要扩展画布
|
|
|
|
|
+ this.checkAndExpandCanvas(newNode)
|
|
|
|
|
+
|
|
|
|
|
+ this.$message.success('节点已粘贴')
|
|
|
|
|
+ },
|
|
|
|
|
+ /** 显示右键菜单 */
|
|
|
|
|
+ showContextMenu(e) {
|
|
|
|
|
+ // 获取鼠标相对于画布容器的位置
|
|
|
|
|
+ const rect = this.$refs.canvasContainer.getBoundingClientRect()
|
|
|
|
|
+ this.contextMenu.x = e.clientX - rect.left
|
|
|
|
|
+ this.contextMenu.y = e.clientY - rect.top
|
|
|
|
|
+ this.contextMenu.node = this.selectedNode
|
|
|
|
|
+ this.contextMenu.visible = true
|
|
|
|
|
+ },
|
|
|
|
|
+ /** 隐藏右键菜单 */
|
|
|
|
|
+ hideContextMenu() {
|
|
|
|
|
+ this.contextMenu.visible = false
|
|
|
|
|
+ },
|
|
|
|
|
+ /** 从菜单复制节点 */
|
|
|
|
|
+ copyNodeFromMenu() {
|
|
|
|
|
+ if (this.contextMenu.node) {
|
|
|
|
|
+ this.copiedNode = JSON.parse(JSON.stringify(this.contextMenu.node))
|
|
|
|
|
+ this.$message.success('节点已复制')
|
|
|
|
|
+ }
|
|
|
|
|
+ this.hideContextMenu()
|
|
|
|
|
+ },
|
|
|
|
|
+ /** 从菜单粘贴节点 */
|
|
|
|
|
+ pasteNodeFromMenu() {
|
|
|
|
|
+ if (!this.copiedNode) {
|
|
|
|
|
+ this.$message.warning('没有可粘贴的节点')
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const newNode = {
|
|
|
|
|
+ ...this.copiedNode,
|
|
|
|
|
+ nodeKey: this.generateKey(),
|
|
|
|
|
+ posX: this.copiedNode.posX + 30,
|
|
|
|
|
+ posY: this.copiedNode.posY + 30,
|
|
|
|
|
+ nodeConfig: this.copiedNode.nodeConfig ? JSON.parse(JSON.stringify(this.copiedNode.nodeConfig)) : {}
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ this.nodes.push(newNode)
|
|
|
|
|
+ this.selectedNode = newNode
|
|
|
|
|
+ this.selectedEdge = null
|
|
|
|
|
+ this.checkAndExpandCanvas(newNode)
|
|
|
|
|
+
|
|
|
|
|
+ this.$message.success('节点已粘贴')
|
|
|
|
|
+ this.hideContextMenu()
|
|
|
|
|
+ },
|
|
|
|
|
+ /** 从菜单删除节点 */
|
|
|
|
|
+ deleteNodeFromMenu() {
|
|
|
|
|
+ if (this.contextMenu.node) {
|
|
|
|
|
+ this.deleteNode()
|
|
|
|
|
+ }
|
|
|
|
|
+ this.hideContextMenu()
|
|
|
|
|
+ },
|
|
|
/** 拖拽开始 */
|
|
/** 拖拽开始 */
|
|
|
onDragStart(e, nodeType) {
|
|
onDragStart(e, nodeType) {
|
|
|
e.dataTransfer.setData('nodeType', JSON.stringify(nodeType))
|
|
e.dataTransfer.setData('nodeType', JSON.stringify(nodeType))
|
|
@@ -566,7 +916,7 @@ export default {
|
|
|
posX: Math.round(x - 50),
|
|
posX: Math.round(x - 50),
|
|
|
posY: Math.round(y - 20),
|
|
posY: Math.round(y - 20),
|
|
|
height: 40,
|
|
height: 40,
|
|
|
- nodeConfig: nodeType.defaultConfig || '{}'
|
|
|
|
|
|
|
+ nodeConfig: {} // 初始化为空对象
|
|
|
}
|
|
}
|
|
|
this.nodes.push(newNode)
|
|
this.nodes.push(newNode)
|
|
|
this.selectNode(newNode)
|
|
this.selectNode(newNode)
|
|
@@ -626,8 +976,8 @@ export default {
|
|
|
const scrollTop = this.$refs.canvasContainer.scrollTop
|
|
const scrollTop = this.$refs.canvasContainer.scrollTop
|
|
|
const x = (e.clientX - rect.left + scrollLeft - this.canvasOffset.x) / this.scale - this.dragOffset.x
|
|
const x = (e.clientX - rect.left + scrollLeft - this.canvasOffset.x) / this.scale - this.dragOffset.x
|
|
|
const y = (e.clientY - rect.top + scrollTop - this.canvasOffset.y) / this.scale - this.dragOffset.y
|
|
const y = (e.clientY - rect.top + scrollTop - this.canvasOffset.y) / this.scale - this.dragOffset.y
|
|
|
- this.draggingNode.posX = Math.max(0, Math.round(x))
|
|
|
|
|
- this.draggingNode.posY = Math.max(0, Math.round(y))
|
|
|
|
|
|
|
+ this.draggingNode.posX = Math.max(0, x)
|
|
|
|
|
+ this.draggingNode.posY = Math.max(0, y)
|
|
|
// 检测并扩展画布
|
|
// 检测并扩展画布
|
|
|
this.checkAndExpandCanvas(this.draggingNode)
|
|
this.checkAndExpandCanvas(this.draggingNode)
|
|
|
}
|
|
}
|
|
@@ -751,49 +1101,59 @@ export default {
|
|
|
const dx = x2 - x1
|
|
const dx = x2 - x1
|
|
|
const dy = y2 - y1
|
|
const dy = y2 - y1
|
|
|
const distance = Math.sqrt(dx * dx + dy * dy)
|
|
const distance = Math.sqrt(dx * dx + dy * dy)
|
|
|
-
|
|
|
|
|
- // 根据锚点位置计算控制点偏移量
|
|
|
|
|
- let offset = Math.min(distance * 0.5, 100)
|
|
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
|
|
+ // 根据锚点位置计算控制点偏移量,动态调整
|
|
|
|
|
+ let offset = Math.min(distance * 0.4, 80)
|
|
|
|
|
+
|
|
|
let cp1x = x1, cp1y = y1
|
|
let cp1x = x1, cp1y = y1
|
|
|
let cp2x = x2, cp2y = y2
|
|
let cp2x = x2, cp2y = y2
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
// 根据源节点锚点方向设置第一个控制点
|
|
// 根据源节点锚点方向设置第一个控制点
|
|
|
switch(sourceAnchor) {
|
|
switch(sourceAnchor) {
|
|
|
case 'top':
|
|
case 'top':
|
|
|
cp1y = y1 - offset
|
|
cp1y = y1 - offset
|
|
|
|
|
+ cp1x = x1
|
|
|
break
|
|
break
|
|
|
case 'bottom':
|
|
case 'bottom':
|
|
|
cp1y = y1 + offset
|
|
cp1y = y1 + offset
|
|
|
|
|
+ cp1x = x1
|
|
|
break
|
|
break
|
|
|
case 'left':
|
|
case 'left':
|
|
|
cp1x = x1 - offset
|
|
cp1x = x1 - offset
|
|
|
|
|
+ cp1y = y1
|
|
|
break
|
|
break
|
|
|
case 'right':
|
|
case 'right':
|
|
|
cp1x = x1 + offset
|
|
cp1x = x1 + offset
|
|
|
|
|
+ cp1y = y1
|
|
|
break
|
|
break
|
|
|
default:
|
|
default:
|
|
|
cp1y = y1 + offset
|
|
cp1y = y1 + offset
|
|
|
|
|
+ cp1x = x1
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// 根据目标节点锚点方向设置第二个控制点
|
|
// 根据目标节点锚点方向设置第二个控制点
|
|
|
switch(targetAnchor) {
|
|
switch(targetAnchor) {
|
|
|
case 'top':
|
|
case 'top':
|
|
|
cp2y = y2 - offset
|
|
cp2y = y2 - offset
|
|
|
|
|
+ cp2x = x2
|
|
|
break
|
|
break
|
|
|
case 'bottom':
|
|
case 'bottom':
|
|
|
cp2y = y2 + offset
|
|
cp2y = y2 + offset
|
|
|
|
|
+ cp2x = x2
|
|
|
break
|
|
break
|
|
|
case 'left':
|
|
case 'left':
|
|
|
cp2x = x2 - offset
|
|
cp2x = x2 - offset
|
|
|
|
|
+ cp2y = y2
|
|
|
break
|
|
break
|
|
|
case 'right':
|
|
case 'right':
|
|
|
cp2x = x2 + offset
|
|
cp2x = x2 + offset
|
|
|
|
|
+ cp2y = y2
|
|
|
break
|
|
break
|
|
|
default:
|
|
default:
|
|
|
cp2y = y2 - offset
|
|
cp2y = y2 - offset
|
|
|
|
|
+ cp2x = x2
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
return `M ${x1} ${y1} C ${cp1x} ${cp1y}, ${cp2x} ${cp2y}, ${x2} ${y2}`
|
|
return `M ${x1} ${y1} C ${cp1x} ${cp1y}, ${cp2x} ${cp2y}, ${x2} ${y2}`
|
|
|
},
|
|
},
|
|
|
/** 获取连线标签位置 */
|
|
/** 获取连线标签位置 */
|
|
@@ -808,6 +1168,28 @@ export default {
|
|
|
/** 获取节点背景色 */
|
|
/** 获取节点背景色 */
|
|
|
getNodeBgColor(node) {
|
|
getNodeBgColor(node) {
|
|
|
const color = node.nodeColor || '#1890ff'
|
|
const color = node.nodeColor || '#1890ff'
|
|
|
|
|
+ // 处理各种颜色格式
|
|
|
|
|
+ if (color.startsWith('rgba')) {
|
|
|
|
|
+ // rgba 格式,替换透明度
|
|
|
|
|
+ return color.replace(/[\d.]+\)$/g, '0.08)')
|
|
|
|
|
+ } else if (color.startsWith('rgb')) {
|
|
|
|
|
+ // rgb 格式,转为 rgba
|
|
|
|
|
+ return color.replace('rgb', 'rgba').replace(')', ', 0.08)')
|
|
|
|
|
+ } else if (color.startsWith('#')) {
|
|
|
|
|
+ // 十六进制格式
|
|
|
|
|
+ // 处理 3 位和 6 位十六进制
|
|
|
|
|
+ let hex = color.slice(1)
|
|
|
|
|
+ if (hex.length === 3) {
|
|
|
|
|
+ hex = hex.split('').map(c => c + c).join('')
|
|
|
|
|
+ }
|
|
|
|
|
+ if (hex.length === 6) {
|
|
|
|
|
+ return `#${hex}15`
|
|
|
|
|
+ } else if (hex.length === 8) {
|
|
|
|
|
+ // 已经带透明度,替换为 15
|
|
|
|
|
+ return `#${hex.slice(0, 6)}15`
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ // 默认返回原颜色 + 15
|
|
|
return color + '15'
|
|
return color + '15'
|
|
|
},
|
|
},
|
|
|
|
|
|