当前位置: 首页 > news >正文

last logicflow

<template><div class="logicflow-page"><div class="sidebar"><div class="palette-title">组件面板</div><div class="palette-item" @mousedown="startDrag('custom-rect', '矩形')">矩形</div><div class="palette-item" @mousedown="startDrag('circle', '圆形')">圆形</div><div class="palette-item" @mousedown="startDrag('diamond', '菱形')">菱形</div><div class="palette-item" @mousedown="startDrag('text', '文本')">文本</div><div class="palette-title" style="margin-top: 20px;">关联关系</div><div class="edge-type-selector"><div class="edge-type-item" :class="{ active: selectedEdgeType === 'solid' }"@click="selectEdgeType('solid')"><div class="edge-preview solid-line"></div><span>包含关系</span></div><div class="edge-type-item" :class="{ active: selectedEdgeType === 'dashed' }"@click="selectEdgeType('dashed')"><div class="edge-preview dashed-line"></div><span>关联关系</span></div><div class="edge-type-item" :class="{ active: selectedEdgeType === 'dotted' }"@click="selectEdgeType('dotted')"><div class="edge-preview dotted-line"></div><span>使用关系</span></div></div><div class="palette-title" style="margin-top: 20px;">操作说明</div><div class="help-text"><p>• 点击节点/连接线:编辑文字</p><p>• 右键节点/连接线:删除</p><p>• Delete/Backspace:删除选中元素</p><p>• Ctrl+A:全选</p><p>• Ctrl+C:复制</p><p>• Ctrl+V:粘贴</p></div></div><div class="canvas-wrap"><div ref="lfContainerRef" class="lf-container"></div></div><el-dialog v-model="nodeDialogVisible" title="节点配置" width="600px" append-to-body :close-on-click-modal="false"><el-form label-width="100px"><el-form-item label="节点ID"><el-input v-model="selectedNodeId" disabled /></el-form-item><el-form-item label="显示文本"><el-input v-model="selectedNodeText" placeholder="请输入节点文本" /></el-form-item><el-form-item label="节点类型"><el-input v-model="selectedNodeType" placeholder="请输入节点类型" /></el-form-item><el-form-item label="节点描述"><el-input v-model="selectedNodeDescription" type="textarea" :rows="2"placeholder="请输入节点描述" /></el-form-item><!-- 关联关系配置 --><el-divider content-position="left">关联关系配置</el-divider><el-form-item label="关联节点"><div class="relation-config"><div class="relation-list"><div v-for="(relation, index) in selectedNodeRelations" :key="index"class="relation-item"><el-select v-model="relation.targetNodeId" placeholder="选择关联节点"style="width: 150px; margin-right: 10px;"><el-option v-for="node in getAvailableNodes(selectedNodeId)":key="node.id":label="node.text?.value || node.id":value="node.id"/></el-select><el-select v-model="relation.relationType" placeholder="关系类型"style="width: 120px; margin-right: 10px;"><el-option label="包含" value="contains" /><el-option label="关联" value="associates" /><el-option label="使用" value="uses" /></el-select><el-button type="danger" icon="Delete" size="small"@click="removeRelation(index)"/></div></div><el-button type="primary" icon="Plus" size="small"@click="addRelation"style="margin-top: 10px;">添加关联</el-button></div></el-form-item><!-- 现有关联关系显示 --><el-form-item label="现有关联" v-if="getNodeRelationsSummary(selectedNodeId).length > 0"><div class="existing-relations"><el-tag v-for="(summary, index) in getNodeRelationsSummary(selectedNodeId)":key="index"type="info"size="small"style="margin-right: 8px; margin-bottom: 4px;">{{ summary }}</el-tag></div></el-form-item></el-form><template #footer><div class="dialog-footer"><el-button @click="nodeDialogVisible = false">取 消</el-button><el-button type="primary" @click="applyNodeChange">保 存</el-button><el-button type="info" @click="viewNodeRelations">查看关系图</el-button></div></template></el-dialog><el-dialog v-model="edgeDialogVisible" title="连接线配置" width="500px" append-to-body :close-on-click-modal="false"><el-form label-width="100px"><el-form-item label="连接线ID"><el-input v-model="selectedEdgeId" disabled /></el-form-item><el-form-item label="显示文本"><el-input v-model="selectedEdgeText" placeholder="请输入连接线文本" /></el-form-item><el-form-item label="线条类型"><el-select v-model="selectedEdgeLineType" placeholder="选择线条类型"><el-option label="实线" value="solid" /><el-option label="虚线" value="dashed" /><el-option label="点线" value="dotted" /></el-select></el-form-item><el-form-item label="文字颜色"><div class="color-picker-container"><el-color-picker v-model="selectedEdgeTextColor" :predefine="predefinedColors"show-alpha/><span class="color-preview" :style="{ color: selectedEdgeTextColor }">预览文字效果</span></div></el-form-item><el-form-item label="文字大小"><el-slider v-model="selectedEdgeTextSize" :min="10" :max="24" :step="1"show-inputinput-size="small"/></el-form-item><el-form-item label="箭头样式"><el-select v-model="selectedArrowType" placeholder="选择箭头样式"><el-option label="默认箭头" value="default" /><el-option label="实心箭头" value="filled" /><el-option label="空心箭头" value="hollow" /><el-option label="菱形箭头" value="diamond" /><el-option label="圆形箭头" value="circle" /></el-select></el-form-item><el-form-item label="起始点标记"><el-select v-model="selectedStartMarker" placeholder="选择起始点标记"><el-option label="无标记" value="none" /><el-option label="实心圆" value="filled-circle" /><el-option label="空心圆" value="hollow-circle" /></el-select></el-form-item></el-form><template #footer><div class="dialog-footer"><el-button @click="edgeDialogVisible = false">取 消</el-button><el-button type="primary" @click="applyEdgeChange">保 存</el-button></div></template></el-dialog><!-- 关系图查看对话框 --><el-dialog v-model="relationViewDialogVisible" title="节点关系图" width="800px" append-to-body><div class="relation-view"><div class="relation-header"><h4>节点:{{ selectedNodeText }} ({{ selectedNodeId }})</h4></div><!-- 关系树状图 --><div class="relation-tree"><el-tree:data="relationTreeData":props="{ children: 'children', label: 'label' }"default-expand-allnode-key="id"class="relation-tree-view"><template #default="{ node, data }"><span class="relation-node"><el-icon v-if="data.type === 'node'"><Box /></el-icon><el-icon v-else-if="data.type === 'relation'"><Connection /></el-icon><span>{{ data.label }}</span><el-tag v-if="data.relationType" size="small" type="primary">{{ getRelationTypeLabel(data.relationType) }}</el-tag></span></template></el-tree></div><!-- 关系表格 --><el-divider>关系详情</el-divider><el-table :data="relationTableData" style="width: 100%" size="small"><el-table-column prop="sourceNode" label="源节点" width="150" /><el-table-column prop="relationType" label="关系类型" width="120"><template #default="scope"><el-tag size="small" :type="getRelationTagType(scope.row.relationType)">{{ getRelationTypeLabel(scope.row.relationType) }}</el-tag></template></el-table-column><el-table-column prop="targetNode" label="目标节点" width="150" /><el-table-column prop="description" label="描述" /><el-table-column label="操作" width="120"><template #default="scope"><el-button type="primary" size="small" @click="highlightRelation(scope.row)">高亮</el-button></template></el-table-column></el-table></div><template #footer><div class="dialog-footer"><el-button @click="relationViewDialogVisible = false">关 闭</el-button><el-button type="primary" @click="exportRelations">导出关系</el-button></div></template></el-dialog></div></template><script setup>
import { onMounted, onBeforeUnmount, ref } from 'vue'
import { ElMessage } from 'element-plus'
import LogicFlow from '@logicflow/core'
import { Menu, SelectionSelect, Control, MiniMap, Snapshot } from '@logicflow/extension'
import { RectNode, RectNodeModel, LineEdge, LineEdgeModel, h } from '@logicflow/core'
import '@logicflow/core/dist/index.css'
import '@logicflow/extension/dist/index.css'const lfContainerRef = ref(null)
let lf = nullconst nodeDialogVisible = ref(false)
const selectedNodeId = ref('')
const selectedNodeText = ref('')
const selectedNodeType = ref('')
const selectedNodeDescription = ref('')
const selectedNodeRelations = ref([])// 关系图查看对话框
const relationViewDialogVisible = ref(false)
const relationTreeData = ref([])
const relationTableData = ref([])// 全局关联关系存储
const nodeRelations = ref(new Map())const edgeDialogVisible = ref(false)
const selectedEdgeId = ref('')
const selectedEdgeText = ref('')
const selectedEdgeType = ref('solid') // 当前选中的连线类型
const selectedEdgeLineType = ref('solid') // 编辑对话框中的线条类型
const selectedEdgeTextColor = ref('#374151') // 编辑对话框中的文字颜色
const selectedEdgeTextSize = ref(12) // 编辑对话框中的文字大小
const selectedArrowType = ref('default') // 编辑对话框中的箭头类型
const selectedStartMarker = ref('none') // 编辑对话框中的起始点标记// 预定义颜色选项
const predefinedColors = ['#374151', '#ef4444', '#f59e0b', '#10b981', '#3b82f6', '#8b5cf6', '#ec4899', '#6b7280'
]// 自定义矩形节点
class CustomRectNode extends RectNode {}
class CustomRectNodeModel extends RectNodeModel {constructor(data, graphModel) {super(data, graphModel)// 设置默认尺寸this.width = 180this.height = 40}getNodeStyle() {const style = super.getNodeStyle()return {...style,fill: '#f0f9ff',  // 自定义填充色stroke: '#3b82f6',  // 自定义边框色strokeWidth: 2,  // 自定义边框宽度borderRadius: 8,  // 自定义圆角}}// 重写获取节点尺寸的方法getWidth() {return this.width}getHeight() {return this.height}getTextStyle() {const style = super.getTextStyle()return {...style,fontSize: 14,fontWeight: 'bold',fill: '#1e40af',}}
}// 备用方案:如果自定义getText不行,就用这个简单的类
class SimpleCustomEdge extends LineEdge {// 什么都不重写,让LogicFlow完全处理
}// 完全重写的自定义连接线 - 支持起始点标记和文字居中
class CustomEdge extends LineEdge {getShape() {const { model } = this.propsconst { startMarker } = model.properties || {}// 获取默认的连线形状const shape = super.getShape()// 如果有起始点标记,添加起始点圆形if (startMarker && startMarker !== 'none') {const startPoint = this.getStartPoint()const lineType = model.properties?.lineType || 'solid'const colors = {solid: '#3b82f6',dashed: '#10b981',dotted: '#f59e0b'}const circle = h('circle', {cx: startPoint.x,cy: startPoint.y,r: 4,fill: startMarker === 'filled-circle' ? colors[lineType] : 'white',stroke: colors[lineType],strokeWidth: 2,className: 'lf-edge-start-marker'})// 将起始点圆形添加到形状中if (shape.children) {shape.children.push(circle)} else {return h('g', {}, [shape, circle])}}return shape}getStartPoint() {const { model } = this.props// 尝试多种方式获取起始点if (model.startPoint) {return model.startPoint} else if (model.x1 !== undefined) {return { x: model.x1, y: model.y1 }} else {const sourceNode = model.graphModel.getNodeModelById(model.sourceNodeId)return sourceNode ? { x: sourceNode.x, y: sourceNode.y } : { x: 0, y: 0 }}}getText() {const { model } = this.propsconst text = model.text?.value || ''if (!text.trim()) return null// 调试:输出模型的所有属性console.log('模型属性:', model)console.log('startPoint:', model.startPoint)console.log('endPoint:', model.endPoint)console.log('x1,y1,x2,y2:', model.x1, model.y1, model.x2, model.y2)// 尝试多种方式获取连线坐标let startX, startY, endX, endY// 方法1: 直接从model获取if (model.x1 !== undefined && model.x2 !== undefined) {startX = model.x1startY = model.y1endX = model.x2endY = model.y2}// 方法2: 从startPoint和endPoint获取else if (model.startPoint && model.endPoint) {startX = model.startPoint.xstartY = model.startPoint.yendX = model.endPoint.xendY = model.endPoint.y}// 方法3: 从源节点和目标节点获取else {const sourceNode = model.graphModel.getNodeModelById(model.sourceNodeId)const targetNode = model.graphModel.getNodeModelById(model.targetNodeId)if (sourceNode && targetNode) {startX = sourceNode.xstartY = sourceNode.yendX = targetNode.xendY = targetNode.y} else {console.error('无法获取连线坐标')return null}}// 计算中心点const centerX = (startX + endX) / 2const centerY = (startY + endY) / 2// 获取文字样式const textColor = model.properties?.textColor || '#374151'const textSize = model.properties?.textSize || 12console.log(`连线坐标: (${startX},${startY}) -> (${endX},${endY})`)console.log(`文字渲染: "${text}" 中心位置: (${centerX}, ${centerY})`)// 直接使用绝对坐标,不使用transformreturn h('text', {x: centerX,y: centerY,textAnchor: 'middle',dominantBaseline: 'central',fontSize: textSize,fill: textColor,fontFamily: 'Arial, sans-serif',fontWeight: '500'}, text)}
}// 基础自定义边模型
class CustomEdgeModel extends LineEdgeModel {getEdgeStyle() {const style = super.getEdgeStyle()const lineType = this.properties?.lineType || 'solid'// 根据线条类型设置不同样式const lineStyles = {solid: {stroke: '#3b82f6',strokeWidth: 2,strokeDasharray: '0',},dashed: {stroke: '#10b981',strokeWidth: 2,strokeDasharray: '8,4',},dotted: {stroke: '#f59e0b',strokeWidth: 2,strokeDasharray: '2,2',}}return {...style,...lineStyles[lineType]}}getArrowStyle() {const style = super.getArrowStyle()const lineType = this.properties?.lineType || 'solid'const arrowType = this.properties?.arrowType || 'default'const arrowColors = {solid: '#3b82f6',dashed: '#10b981',dotted: '#f59e0b'}// 根据箭头类型设置不同的样式const baseStyle = {fill: arrowColors[lineType],stroke: arrowColors[lineType],strokeWidth: 1,}switch (arrowType) {case 'filled': // 实心箭头return {...style,...baseStyle,fill: arrowColors[lineType],stroke: arrowColors[lineType]}case 'hollow': // 空心箭头return {...style,...baseStyle,fill: 'white',stroke: arrowColors[lineType],strokeWidth: 2}case 'diamond': // 菱形箭头return {...style,...baseStyle,d: 'M 0 0 L 8 4 L 0 8 L -8 4 Z' // 菱形路径}case 'circle': // 圆形箭头return {...style,...baseStyle,r: 4 // 圆形半径}default: // 默认箭头return {...style,...baseStyle}}}// 配置起始点标记getStartArrowStyle() {const startMarker = this.properties?.startMarker || 'none'const lineType = this.properties?.lineType || 'solid'const colors = {solid: '#3b82f6',dashed: '#10b981',dotted: '#f59e0b'}if (startMarker === 'none') return nullreturn {fill: startMarker === 'filled-circle' ? colors[lineType] : 'white',stroke: colors[lineType],strokeWidth: 2,r: 4}}// 配置终点标记getEndArrowStyle() {return this.getArrowStyle()}// 备用的文字样式方法,如果CustomEdge的getText有问题就用这个getTextStyle() {const style = super.getTextStyle()const textColor = this.properties?.textColor || '#374151'const textSize = this.properties?.textSize || 12return {...style,fontSize: textSize,fill: textColor,fontWeight: '500',fontFamily: 'Arial, sans-serif',textAnchor: 'middle',dominantBaseline: 'central',background: {fill: 'transparent',stroke: 'transparent',strokeWidth: 0}}}
}function startDrag(type, text) {if (!lf) returnlf.dnd.startDrag({ type, text })
}// 选择边类型
function selectEdgeType(edgeType) {selectedEdgeType.value = edgeType// 设置默认的边类型,影响后续创建的连线if (lf) {lf.setDefaultEdgeType('custom-edge')}
}// 根据线型获取对应的关联关系
function getRelationTypeByLineType(lineType) {const mapping = {'solid': 'contains',      // 实线 -> 包含关系'dashed': 'associates',   // 虚线 -> 关联关系'dotted': 'uses'          // 点线 -> 使用关系}return mapping[lineType] || 'contains'
}// 根据关联关系获取对应的线型
function getLineTypeByRelationType(relationType) {const mapping = {'contains': 'solid',      // 包含关系 -> 实线'associates': 'dashed',   // 关联关系 -> 虚线'uses': 'dotted'          // 使用关系 -> 点线}return mapping[relationType] || 'solid'
}// 将连线关系同步到源节点的关联关系配置
function syncEdgeToNodeRelation(sourceNodeId, targetNodeId, relationType) {// 获取源节点现有的关联关系const existingRelations = nodeRelations.value.get(sourceNodeId) || []// 检查是否已存在相同的关系(避免重复)const isDuplicate = existingRelations.some(relation => relation.targetNodeId === targetNodeId && relation.relationType === relationType)if (!isDuplicate) {// 添加新的关联关系const newRelation = {targetNodeId: targetNodeId,relationType: relationType,customType: '',description: `通过连线自动创建的${getRelationTypeLabel(relationType)}关系`}existingRelations.push(newRelation)nodeRelations.value.set(sourceNodeId, existingRelations)console.log(`已自动添加关联关系: ${sourceNodeId} -> ${targetNodeId} (${relationType})`)}
}// 从节点关联关系中移除指定的关系
function removeEdgeFromNodeRelation(sourceNodeId, targetNodeId, relationType) {const existingRelations = nodeRelations.value.get(sourceNodeId) || []const filteredRelations = existingRelations.filter(relation => !(relation.targetNodeId === targetNodeId && relation.relationType === relationType))if (filteredRelations.length !== existingRelations.length) {nodeRelations.value.set(sourceNodeId, filteredRelations)console.log(`已移除关联关系: ${sourceNodeId} -> ${targetNodeId} (${relationType})`)}
}function applyNodeChange() {if (!selectedNodeId.value) return// 更新节点基本信息lf.setProperties(selectedNodeId.value, { text: selectedNodeText.value,nodeType: selectedNodeType.value,description: selectedNodeDescription.value})// 同步 label 文本lf.updateText(selectedNodeId.value, selectedNodeText.value)// 保存关联关系saveNodeRelations(selectedNodeId.value, selectedNodeRelations.value)nodeDialogVisible.value = falseElMessage.success('已更新节点配置')
}// 添加关联关系
function addRelation() {selectedNodeRelations.value.push({targetNodeId: '',relationType: '',customType: '',description: ''})
}// 移除关联关系
function removeRelation(index) {selectedNodeRelations.value.splice(index, 1)
}// 保存节点关联关系
function saveNodeRelations(nodeId, relations) {const validRelations = relations.filter(r => r.targetNodeId && r.relationType)const oldRelations = nodeRelations.value.get(nodeId) || []// 找出被删除的关系,删除对应的自动创建的连线oldRelations.forEach((oldRelation, index) => {const stillExists = validRelations.some(newRelation => newRelation.targetNodeId === oldRelation.targetNodeId && newRelation.relationType === oldRelation.relationType)if (!stillExists) {// 删除对应的自动创建的连线const edgeId = `relation_${nodeId}_${oldRelation.targetNodeId}_${index}`try {const edgeModel = lf.getEdgeModelById(edgeId)if (edgeModel) {lf.deleteEdge(edgeId)console.log(`已删除关联关系对应的连线: ${edgeId}`)}} catch (error) {console.warn('删除关联连线失败:', error)}}})nodeRelations.value.set(nodeId, validRelations)// 创建自动连线(可选)createRelationEdges(nodeId, validRelations)
}// 创建关系连线
function createRelationEdges(sourceNodeId, relations) {relations.forEach((relation, index) => {if (!relation.targetNodeId || !relation.relationType) returnconst edgeId = `relation_${sourceNodeId}_${relation.targetNodeId}_${index}`const relationTypeLabel = getRelationTypeLabel(relation.relationType)// 检查是否已存在连线const existingEdge = lf.getEdgeModelById(edgeId)if (existingEdge) {// 更新现有连线lf.setProperties(edgeId, {relationType: relation.relationType,relationLabel: relationTypeLabel})lf.updateText(edgeId, relationTypeLabel)} else {// 创建新连线try {lf.addEdge({id: edgeId,sourceNodeId: sourceNodeId,targetNodeId: relation.targetNodeId,text: relationTypeLabel,type: 'custom-edge',properties: {lineType: getRelationLineType(relation.relationType),relationType: relation.relationType,relationLabel: relationTypeLabel,textColor: getRelationColor(relation.relationType),textSize: 12,arrowType: 'filled'}})} catch (error) {console.warn('创建关系连线失败:', error)}}})
}// 获取可用节点列表
function getAvailableNodes(currentNodeId) {if (!lf) return []const graphData = lf.getGraphData()return graphData.nodes.filter(node => node.id !== currentNodeId)
}// 获取节点关系摘要
function getNodeRelationsSummary(nodeId) {const relations = nodeRelations.value.get(nodeId) || []const summaries = []relations.forEach(relation => {if (relation.targetNodeId && relation.relationType) {const targetNode = getNodeById(relation.targetNodeId)const targetName = targetNode?.text?.value || relation.targetNodeIdconst relationLabel = getRelationTypeLabel(relation.relationType)summaries.push(`${relationLabel} → ${targetName}`)}})// 查找指向当前节点的关系nodeRelations.value.forEach((relations, sourceNodeId) => {relations.forEach(relation => {if (relation.targetNodeId === nodeId) {const sourceNode = getNodeById(sourceNodeId)const sourceName = sourceNode?.text?.value || sourceNodeIdconst relationLabel = getRelationTypeLabel(relation.relationType)summaries.push(`${sourceName} → ${relationLabel}`)}})})return summaries
}// 查看节点关系
function viewNodeRelations() {buildRelationTreeData(selectedNodeId.value)buildRelationTableData(selectedNodeId.value)relationViewDialogVisible.value = true
}// 构建关系树数据
function buildRelationTreeData(nodeId) {const currentNode = getNodeById(nodeId)const currentNodeName = currentNode?.text?.value || nodeIdconst treeNode = {id: nodeId,label: currentNodeName,type: 'node',children: []}// 添加出去的关系const outgoingRelations = nodeRelations.value.get(nodeId) || []outgoingRelations.forEach((relation, index) => {if (relation.targetNodeId && relation.relationType) {const targetNode = getNodeById(relation.targetNodeId)const targetName = targetNode?.text?.value || relation.targetNodeIdconst relationLabel = getRelationTypeLabel(relation.relationType)treeNode.children.push({id: `${nodeId}_out_${index}`,label: `${relationLabel} → ${targetName}`,type: 'relation',relationType: relation.relationType})}})// 添加进来的关系nodeRelations.value.forEach((relations, sourceNodeId) => {relations.forEach((relation, index) => {if (relation.targetNodeId === nodeId) {const sourceNode = getNodeById(sourceNodeId)const sourceName = sourceNode?.text?.value || sourceNodeIdconst relationLabel = getRelationTypeLabel(relation.relationType)treeNode.children.push({id: `${sourceNodeId}_in_${index}`,label: `${sourceName} → ${relationLabel}`,type: 'relation',relationType: relation.relationType})}})})relationTreeData.value = [treeNode]
}// 构建关系表格数据
function buildRelationTableData(nodeId) {const tableData = []// 出去的关系const outgoingRelations = nodeRelations.value.get(nodeId) || []outgoingRelations.forEach(relation => {if (relation.targetNodeId && relation.relationType) {const targetNode = getNodeById(relation.targetNodeId)const targetName = targetNode?.text?.value || relation.targetNodeIdconst currentNode = getNodeById(nodeId)const currentName = currentNode?.text?.value || nodeIdtableData.push({sourceNode: currentName,relationType: relation.relationType,targetNode: targetName,description: relation.description || `${currentName} ${getRelationTypeLabel(relation.relationType)} ${targetName}`,direction: 'outgoing',sourceNodeId: nodeId,targetNodeId: relation.targetNodeId})}})// 进来的关系nodeRelations.value.forEach((relations, sourceNodeId) => {relations.forEach(relation => {if (relation.targetNodeId === nodeId) {const sourceNode = getNodeById(sourceNodeId)const sourceName = sourceNode?.text?.value || sourceNodeIdconst currentNode = getNodeById(nodeId)const currentName = currentNode?.text?.value || nodeIdtableData.push({sourceNode: sourceName,relationType: relation.relationType,targetNode: currentName,description: relation.description || `${sourceName} ${getRelationTypeLabel(relation.relationType)} ${currentName}`,direction: 'incoming',sourceNodeId: sourceNodeId,targetNodeId: nodeId})}})})relationTableData.value = tableData
}// 获取节点by ID
function getNodeById(nodeId) {if (!lf) return nullconst graphData = lf.getGraphData()return graphData.nodes.find(node => node.id === nodeId)
}// 获取关系类型标签
function getRelationTypeLabel(relationType) {const labels = {'contains': '包含','associates': '关联','uses': '使用'}return labels[relationType] || relationType
}// 获取关系标签类型
function getRelationTagType(relationType) {const types = {'contains': 'success','associates': 'primary','uses': 'warning'}return types[relationType] || 'info'
}// 获取关系线条类型
function getRelationLineType(relationType) {const lineTypes = {'contains': 'solid',      // 包含关系用实线'associates': 'dashed',   // 关联关系用虚线'uses': 'dotted'          // 使用关系用点线}return lineTypes[relationType] || 'solid'
}// 获取关系颜色
function getRelationColor(relationType) {const colors = {'contains': '#3b82f6',    // 包含关系用蓝色(对应实线)'associates': '#10b981',  // 关联关系用绿色(对应虚线)'uses': '#f59e0b'         // 使用关系用橙色(对应点线)}return colors[relationType] || '#374151'
}// 高亮关系
function highlightRelation(relationData) {// 高亮相关节点lf.selectElementById(relationData.sourceNodeId, true)lf.selectElementById(relationData.targetNodeId, true)ElMessage.success(`已高亮关系:${relationData.sourceNode} → ${relationData.targetNode}`)
}// 导出关系
function exportRelations() {const allRelations = []nodeRelations.value.forEach((relations, sourceNodeId) => {const sourceNode = getNodeById(sourceNodeId)const sourceName = sourceNode?.text?.value || sourceNodeIdrelations.forEach(relation => {if (relation.targetNodeId && relation.relationType) {const targetNode = getNodeById(relation.targetNodeId)const targetName = targetNode?.text?.value || relation.targetNodeIdallRelations.push({源节点: sourceName,源节点ID: sourceNodeId,关系类型: getRelationTypeLabel(relation.relationType),目标节点: targetName,目标节点ID: relation.targetNodeId,自定义类型: relation.customType || '',描述: relation.description || ''})}})})// 导出为JSONconst dataStr = JSON.stringify(allRelations, null, 2)const dataBlob = new Blob([dataStr], {type: 'application/json'})const url = URL.createObjectURL(dataBlob)const link = document.createElement('a')link.href = urllink.download = 'node-relations.json'link.click()ElMessage.success('关系数据已导出')
}function applyEdgeChange() {if (!selectedEdgeId.value) return// 获取连线数据const edgeModel = lf.getEdgeModelById(selectedEdgeId.value)if (!edgeModel) returnconst oldRelationType = edgeModel.properties?.relationTypeconst newRelationType = getRelationTypeByLineType(selectedEdgeLineType.value)// 如果关联关系类型发生变化,需要同步更新节点关联关系if (oldRelationType && oldRelationType !== newRelationType) {// 先移除旧的关联关系removeEdgeFromNodeRelation(edgeModel.sourceNodeId, edgeModel.targetNodeId, oldRelationType)// 再添加新的关联关系syncEdgeToNodeRelation(edgeModel.sourceNodeId, edgeModel.targetNodeId, newRelationType)}const relationLabel = getRelationTypeLabel(newRelationType)lf.setProperties(selectedEdgeId.value, {text: selectedEdgeText.value || relationLabel, // 如果没有自定义文本,使用关系标签lineType: selectedEdgeLineType.value,relationType: newRelationType,relationLabel: relationLabel,textColor: selectedEdgeTextColor.value,textSize: selectedEdgeTextSize.value,arrowType: selectedArrowType.value,startMarker: selectedStartMarker.value})// 同步连接线文本const displayText = selectedEdgeText.value || relationLabellf.updateText(selectedEdgeId.value, displayText)edgeDialogVisible.value = falseElMessage.success('已更新连接线样式和关联关系')
}onMounted(() => {lf = new LogicFlow({container: lfContainerRef.value,grid: true,keyboard: {enabled: true},snapline: true,edgeType: 'custom-edge',  // 使用自定义连接线plugins: [Menu, SelectionSelect, Control, MiniMap, Snapshot]})// 注册自定义矩形节点lf.register({type: 'custom-rect',view: CustomRectNode,model: CustomRectNodeModel,})// 注册自定义连接线 - 如果CustomEdge有问题,可以切换到SimpleCustomEdgelf.register({type: 'custom-edge',view: CustomEdge, // 如果有问题,改为 SimpleCustomEdgemodel: CustomEdgeModel,})console.log(lf, '__+++')// 监听点击节点,打开编辑弹窗lf.on('node:click', ({ data }) => {selectedNodeId.value = data.idselectedNodeText.value = data.text?.value || ''selectedNodeType.value = data.properties?.nodeType || ''selectedNodeDescription.value = data.properties?.description || ''// 加载现有关联关系const existingRelations = nodeRelations.value.get(data.id) || []selectedNodeRelations.value = existingRelations.map(r => ({...r})) // 深拷贝nodeDialogVisible.value = true})// 监听点击连接线,打开编辑弹窗lf.on('edge:click', ({ data }) => {selectedEdgeId.value = data.idselectedEdgeText.value = data.text?.value || ''selectedEdgeLineType.value = data.properties?.lineType || 'solid'selectedEdgeTextColor.value = data.properties?.textColor || '#374151'selectedEdgeTextSize.value = data.properties?.textSize || 12selectedArrowType.value = data.properties?.arrowType || 'default'selectedStartMarker.value = data.properties?.startMarker || 'none'edgeDialogVisible.value = true})// 双击连接线添加文本lf.on('edge:dbclick', ({ data }) => {selectedEdgeId.value = data.idselectedEdgeText.value = data.text?.value || ''selectedEdgeLineType.value = data.properties?.lineType || 'solid'selectedEdgeTextColor.value = data.properties?.textColor || '#374151'selectedEdgeTextSize.value = data.properties?.textSize || 12selectedArrowType.value = data.properties?.arrowType || 'default'selectedStartMarker.value = data.properties?.startMarker || 'none'edgeDialogVisible.value = true})// 监听边创建,自动设置当前选择的线条类型和关联关系lf.on('edge:add', ({ data }) => {const lineType = selectedEdgeType.valueconst relationType = getRelationTypeByLineType(lineType)const relationLabel = getRelationTypeLabel(relationType)// 为新创建的边设置线条类型、关联关系和样式lf.setProperties(data.id, {lineType: lineType,relationType: relationType,relationLabel: relationLabel,textColor: getRelationColor(relationType),textSize: 12,arrowType: 'filled'})// 设置连线文本为关联关系标签lf.updateText(data.id, relationLabel)// 自动同步到源节点的关联关系配置syncEdgeToNodeRelation(data.sourceNodeId, data.targetNodeId, relationType)})// 监听边删除事件,同步删除节点关联关系lf.on('edge:delete', ({ data }) => {const properties = data.properties || {}const relationType = properties.relationTypeif (relationType && data.sourceNodeId && data.targetNodeId) {removeEdgeFromNodeRelation(data.sourceNodeId, data.targetNodeId, relationType)}})// 键盘删除事件lf.on('keydown', ({ data }) => {if (data.key === 'Delete' || data.key === 'Backspace') {const selectedElements = lf.getSelectElements()if (selectedElements.nodes.length > 0 || selectedElements.edges.length > 0) {lf.deleteSelectElements()ElMessage.success('已删除选中元素')}}})// 右键菜单删除lf.on('edge:contextmenu', ({ data }) => {lf.showContextMenu({type: 'edge',data: data,callback: (type, data) => {if (type === 'delete') {lf.deleteEdge(data.id)ElMessage.success('已删除连接线')}}})})lf.on('node:contextmenu', ({ data }) => {lf.showContextMenu({type: 'node',data: data,callback: (type, data) => {if (type === 'delete') {lf.deleteNode(data.id)ElMessage.success('已删除节点')}}})})// 初始化一个示例lf.render({nodes: [{ id: 'n1', type: 'custom-rect', x: 200, y: 80, text: '开始' },{ id: 'n2', type: 'custom-rect', x: 420, y: 80, text: '处理' },{ id: 'n3', type: 'custom-rect', x: 640, y: 80, text: '结束' },{ id: 'n4', type: 'custom-rect', x: 420, y: 180, text: '工具' },],edges: [{ id: 'e1', sourceNodeId: 'n1', targetNodeId: 'n2', text: '包含',properties: {lineType: 'solid',relationType: 'contains',relationLabel: '包含',textColor: '#3b82f6',textSize: 14,arrowType: 'filled',startMarker: 'filled-circle'}},{ id: 'e2', sourceNodeId: 'n2', targetNodeId: 'n3', text: '关联',properties: {lineType: 'dashed',relationType: 'associates',relationLabel: '关联',textColor: '#10b981',textSize: 14,arrowType: 'filled',startMarker: 'hollow-circle'}},{ id: 'e3', sourceNodeId: 'n2', targetNodeId: 'n4', text: '使用',properties: {lineType: 'dotted',relationType: 'uses',relationLabel: '使用',textColor: '#f59e0b',textSize: 14,arrowType: 'filled',startMarker: 'none'}}]})
})onBeforeUnmount(() => {if (lf) {lf.destroy()lf = null}
})
</script><style scoped>
.logicflow-page {display: flex;height: calc(100vh - 120px);padding: 12px;box-sizing: border-box;
}
.sidebar {width: 200px;border-right: 1px solid #eee;padding-right: 12px;
}
.palette-title {font-weight: 600;margin-bottom: 8px;
}
.palette-item {background: #f6f7fb;border: 1px dashed #c0c4cc;padding: 8px 10px;margin-bottom: 8px;cursor: grab;border-radius: 4px;user-select: none;
}.help-text {font-size: 12px;color: #666;line-height: 1.5;
}.help-text p {margin: 4px 0;
}
.canvas-wrap {flex: 1;padding-left: 12px;
}
.lf-container {width: 100%;height: 100%;background: #fff;border: 1px solid #eee;border-radius: 4px;
}/* 边类型选择器样式 */
.edge-type-selector {display: flex;flex-direction: column;gap: 8px;margin-bottom: 12px;
}.edge-type-item {display: flex;align-items: center;padding: 8px 10px;border: 1px solid #e5e7eb;border-radius: 4px;cursor: pointer;transition: all 0.2s;background: #f9fafb;
}.edge-type-item:hover {border-color: #3b82f6;background: #eff6ff;
}.edge-type-item.active {border-color: #3b82f6;background: #dbeafe;
}.edge-preview {width: 30px;height: 2px;margin-right: 10px;position: relative;
}.solid-line {background: #3b82f6;
}.dashed-line {background: linear-gradient(to right, #10b981 0%, #10b981 50%, transparent 50%, transparent 100%);background-size: 8px 2px;
}.dotted-line {background: linear-gradient(to right, #f59e0b 0%, #f59e0b 25%, transparent 25%, transparent 50%, #f59e0b 50%, #f59e0b 75%, transparent 75%, transparent 100%);background-size: 4px 2px;
}/* 颜色选择器样式 */
.color-picker-container {display: flex;align-items: center;gap: 12px;
}.color-preview {font-size: 14px;font-weight: 500;padding: 4px 8px;border-radius: 4px;background: #f5f5f5;
}/* LogicFlow 连线文字样式优化 */
:deep(.lf-edge-text) {pointer-events: none;
}/* 隐藏默认的文字背景矩形 */
:deep(.lf-edge-text rect),
:deep(.lf-edge-text-bg),
:deep(.lf-edge .lf-edge-text-bg) {fill: transparent !important;stroke: transparent !important;opacity: 0 !important;display: none !important;
}/* 确保自定义文字样式 */
:deep(.lf-edge text) {text-anchor: middle !important;dominant-baseline: central !important;user-select: none;-webkit-user-select: none;-moz-user-select: none;-ms-user-select: none;
}/* 增强文字可读性 */
:deep(.lf-edge text) {filter: drop-shadow(1px 1px 1px rgba(255, 255, 255, 0.8)) drop-shadow(-1px -1px 1px rgba(255, 255, 255, 0.8));
}/* 关联关系配置样式 */
.relation-config {border: 1px solid #e5e7eb;border-radius: 6px;padding: 12px;background-color: #fafbfc;
}.relation-list {max-height: 200px;overflow-y: auto;
}.relation-item {display: flex;align-items: center;margin-bottom: 8px;padding: 8px;background: white;border-radius: 4px;border: 1px solid #e5e7eb;
}.relation-item:last-child {margin-bottom: 0;
}.existing-relations {max-height: 100px;overflow-y: auto;padding: 8px;background-color: #f9fafb;border-radius: 4px;border: 1px solid #e5e7eb;
}/* 关系查看对话框样式 */
.relation-view {padding: 8px;
}.relation-header h4 {margin: 0 0 16px 0;color: #374151;font-weight: 600;
}.relation-tree {margin-bottom: 20px;border: 1px solid #e5e7eb;border-radius: 6px;padding: 12px;background-color: #fafbfc;
}.relation-tree-view {background: transparent;
}.relation-node {display: flex;align-items: center;gap: 8px;
}.relation-node .el-icon {color: #6b7280;
}.relation-node .el-tag {margin-left: 8px;
}/* 关系连线的不同颜色样式 */
:deep(.lf-edge[data-relation-type="depends"]) .lf-edge-path {stroke: #f59e0b;
}:deep(.lf-edge[data-relation-type="contains"]) .lf-edge-path {stroke: #10b981;
}:deep(.lf-edge[data-relation-type="triggers"]) .lf-edge-path {stroke: #ef4444;
}:deep(.lf-edge[data-relation-type="inherits"]) .lf-edge-path {stroke: #3b82f6;
}:deep(.lf-edge[data-relation-type="references"]) .lf-edge-path {stroke: #8b5cf6;
}:deep(.lf-edge[data-relation-type="calls"]) .lf-edge-path {stroke: #374151;
}
</style>

  

相关文章:

last logicflow

<template><div class="logicflow-page"><div class="sidebar"><div class="palette-title">组件面板</div><div class="palette-item" @mousedown="startDrag(custom-rect, 矩形)">矩…...

老公对我的精神虐待

2025年09月15日早上。使唤我倒垃圾。然后在我往垃圾桶扔垃圾时,他在吐盔。他对我大呼小叫。从来不会好好说话,一言不合应会对我大呼小叫。在和他相处的过程中,感觉不到做为一个人最基本的尊重‘理解’‘包容’。他还喜欢语言暴力。喜欢贬低人。喜欢和你讲一个故事,然后说里…...

用户沉默之日,产品衰亡之时:逃离迭代中的“沉默陷阱”

一、引言 当前,每天有数以千计的新产品上线,但大多很快陨落,究其原因,不是技术不行或功能太差,甚至不是输给对手,而是消无声息的死去。为什么?原因就在于用户不再反馈和交流,没有真实的反馈作为方向盘,产品如同在夜晚行驶的汽车,或早或晚撞向了深渊。 二、“沉默的大…...

华与华是谁?

华与华是谁? “华与华”是由华杉和华楠两兄弟于2002年创立的战略营销咨询公司。他们的核心方法论是 “超级符号”和“超级话语” ,主张用人类文化中固有的、人人都熟悉符号和话语,来降低品牌的传播成本,让品牌一眼就被记住。 他们的著名成功案例包括:蜜雪冰城:“你爱我,…...

从工具到生态:现代Bug管理系统的平台化转型之路

从工具到生态:现代Bug管理系统的平台化转型之路 在DevOps和持续交付成为行业标配的当下,传统独立的Bug追踪系统正面临前所未有的挑战。随着软件开发流程的日益复杂化,仅具备单一缺陷记录功能的传统系统已难以满足高效协作需求。本文将深入分析传统Bug管理工具的局限性,解读…...

PK-CWT 系列罗氏线圈使用指南:操作方法与注意事项

一、概述 普科科技PRBTEK的PK-CWT系列柔性电流探头是一款采用柔性开环结构的专业测量工具,能够精准复现大功率开关全频段电流波形,在多个领域发挥着重要作用。 在性能方面,该系列产品表现出色。其频率响应带宽范围从0.1Hz至16MHz,这意味着它不仅能精准捕捉超低频段的缓变信…...

IDEA Debug 高阶技巧,老手都是这么玩的~~

IDEA Debug 高阶技巧,老手都是这么玩的~~ 小哈学Java2025年09月14日 15:23 安徽来源:juejin.cn/post/7308539123537592357 👉 欢迎加入小哈的星球,你将获得: 专属的项目实战(多个项目) / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论新项目:《Spring AI…...

mysql 创建分区,如何轻松提升海量数据查询效率

你是否遇到过这样的烦恼:随着业务增长,MySQL单表数据量突破千万级别,查询速度越来越慢,甚至影响用户体验?本文将带你深入理解MySQL分区功能,掌握提升大表查询性能的实战技巧。 什么是MySQL表分区 MySQL中的数据以文件形式存储在磁盘上,默认路径可通过my.cnf中的datadir查…...

完整教程:瑞派虹泰环城总院 | 打造“一站式宠物诊疗空间”,定义全国宠物医疗新高度

完整教程:瑞派虹泰环城总院 | 打造“一站式宠物诊疗空间”,定义全国宠物医疗新高度pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", &…...

BOE(京东方)携新能源领域新品亮相2025服贸会 引领绿色转型新动能

9 月 10 日,以“全球服务 互惠共享”为主题的2025中国国际服务贸易交易会(以下简称“服贸会”)在北京拉开帷幕。作为领先的物联网创新企业,BOE(京东方)携十余款全球首发的钙钛矿光伏产品,以及多款综合能源解决方案惊艳亮相。本次参展服贸会,是对BOE(京东方)可持续品牌…...

SpringBoot 集成支付宝支付,看这篇就够了

SpringBoot 集成支付宝支付,看这篇就够了 小哈学Java2025年09月15日 09:31 安徽来源:juejin.cn/post/7269357836026904633👉 欢迎加入小哈的星球,你将获得: 专属的项目实战(多个项目) / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论新项目:《Spring AI …...

工业智能终端赋能自动化生产线建设数字化管理 - 指南

工业智能终端赋能自动化生产线建设数字化管理 - 指南pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", monosp…...

一道模拟赛题

还没打 mx round7 的请勿观看一种我不太会优化的做法。感觉醍醐灌顶了。 链接:https://www.mxoj.net/problem/P130021?contestId=195人话题意:对值域在 \([1,2^n-1]\) 的严格上升序列计数,要求不能存在连续三个位置使得异或和为 \(0\)。\(n\leq 10^6\)。首先注意到,设 \(i…...

Pycharm打包PaddleOCR过程及疑问解决途径

Pycharm打包PaddleOCR过程及疑问解决途径pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", monospace !import…...

uni-app项目支付宝端Input不受控

最近在负责一个多端项目,其中有一个商品数量控制的功能,但是发现在支付宝端踩坑了出现了异常,一起来看看是怎么回事吧?前情 最近又接手一个全新多端项目,包括抖音/快手/微信/支付宝,其中就有支付宝端,需要实现一个SKU选择,同时需要控制选择的商品数量,如下图坑位 既然…...

适合小型企业的项目管理系统推荐:Reddit 用户真实需求

小型企业常遇工具分散、协作低效难题。本文对比5大项目管理系统,解析功能与优势,助你找到合适的项目管理解决方案。原文链接:https://www.nocobase.com/cn/blog/project-management-systems-for-small-businesses。 对于小型企业来说,项目管理系统(Project Management Sys…...

开启研究生学习阶段

人生之路,走走停停,波澜起伏; 没想到又有回到学校继续学习的机会。 小学、中学、大学、职场; 给我不同的人生体验, 其中的喜怒哀乐都像是过眼云烟, 模糊,清晰,历历在目。 当我写下这些文字的时候, 再看以前写的那些博客, 心中感慨万千; 人生如白驹过隙, 最后的结果…...

李航统计学习方法第二版 学习笔记

第一章 统计学习及监督学习概论 主要记录了监督学习内容 1.1 统计学习监督学习输入输出所有可能的取值分别称之为输入空间,输出空间.通常输出空间远小于输入空间(分类问题中 , 输入的是图片特征 , 只输出"是","否) 一个具体的输入为实例由特征向量表示所有可能的…...

如何拥有自己的一台永久免费云主机/云服务器

适用对象:不想花钱就能拥有自己的一台测试服务器,适用于一些大三大四学生和一些手头紧的用户,白嫖党 配置信息:1核1G5M10G 使用感受:虽然配置不是很高,但是满足自己日常的测试使用是足够的,搭建个人网盘,个人博客,用作测试服务器等等都是可以的 地址:阿贝云:https:/…...

第三周训练总结

上周赛时切题情况(含ICPC,附上题目名称和链接)#34.反转DAG图 #34.歪脖子树 #35.矩阵交换 #35.砖块摆放 #35.学习 LIS ICPC J.中位数 ICPC F.景区建设 #36.字符串博弈 #36.闪现数上周订题情况(附上题目名称和链接)#34.倒水问题 #34.树的颜色 #35.战略轰炸上周题解记录情况…...

godot格式化字符串

godot格式化字符串func _handle_rotation(delta):var target_rotation = randf_range(-PI,PI)var current_rotation = transform.basis.get_euler().y#平滑旋转transform.basis = transform.basis.slerp(Basis.from_euler(Vector3(0,target_rotation,0)),rotation_speed*delta)…...

reLeetCode 热题 100-1 两数之和-扩展2 map实现 - MKT

reLeetCode 热题 100-1 两数之和-扩展2 map实现1...

发现一个新的资源论坛 - 小小程序员

3Y论坛页面简约,论坛的资源也很齐全,页面网速也很快。网址:3y论坛 - 纯净的网盘资源分享社区邀请码:266yzo638u...

reLeetCode 热题 100-1 两数之和-扩展3 单向和双向链表实现 - MKT

reLeetCode 热题 100-1 两数之和-扩展3 单向和双向链表实现1...

codeforces1050div4题解

同步更新,但是现在网站的latex还没渲染好 https://happycoding.me/posts/codeforces-round-1050-div4/ A 思路: 当$n$为奇数时,答案为$x$,否则为$0$ B 思路: 显然每条线段都要经过,答案为$n+m$ C 题意: 现有$2$侧:$0$侧和$1$侧,$0$分钟一开始在$0$侧,尽可能地在两侧之…...

深入解析:少儿舞蹈小程序(13)作品播放量累加及点赞

深入解析:少儿舞蹈小程序(13)作品播放量累加及点赞pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", monos…...

Ubuntu 24.04 安装最新版podman@5.6.1

0. 更新系统 sudo apt update && sudo apt upgrade -y 1. 下载并解压官方静态包 cd /tmp curl -L -O https://github.com/containers/podman/releases/download/v5.6.1/podman-remote-static-linux_amd64.tar.gz tar -xzf podman-remote-static-linux_amd64.tar.gz chm…...

深入解析:Unity:XML笔记(二)——Xml序列化、反序列化、IXmlSerializable接口

深入解析:Unity:XML笔记(二)——Xml序列化、反序列化、IXmlSerializable接口pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "C…...

2025.9.15——知识点学习

图 回路 起点和终点相同的路径,也叫“环” 重边 两个顶点中间不只有一条边 自环 自己到自己的边 简单图 没有重边和自环的图 完全图 每对定点之间都恰有一条边相连 稠密图 边数接近完全图,e>=NlogN 稀疏图 边数远少于完全图,e<NlogN...

详细介绍:拉帮结派下的制造麻烦

详细介绍:拉帮结派下的制造麻烦pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", monospace !important; fon…...

C# Avalonia 13- MoreDrawing - CustomPixelShader

C# Avalonia 13- MoreDrawing - CustomPixelShader目前Avalonia无法继承Effect类重写,因为构造函数是internal。我们重写一个GrayscaleImage实现灰化。GrayscaleImage类public class GrayscaleImage : Control{public static readonly StyledProperty<IImage?> SourceP…...

使用标签Tag控制蒙太奇的触发时机-playmontageAndWait-Send GameplayEvent-WaitGameplayEvent

控制蒙太奇的通知,可以在蒙太奇中的通知中发送标签事件,在GA中接收标签事件 在事件通知蓝图中...

sql事务执行

使用上下文管理器from sqlalchemy import create_engine, text from sqlalchemy.orm import sessionmaker from contextlib import contextmanager import logging# 创建数据库连接 engine = create_engine(mysql+pymysql://username:password@localhost/dbname) SessionLocal …...

GAS_Aura-Spawn FireBolt from Event

1将Spawn火球设置为单独的接口,并在触发GA时触发...

在CentOS 7系统上创建SSL/TLS证书以启用HTTPS

第一步:装备准备 首先,确保您的武器库(包管理器)是最新的。咒语(命令)如下: sudo yum update sudo yum install epel-release sudo yum install mod_ssl 这将更新您的包索引,安装EPEL仓库,并让Apache HTTP服务器拥有处理SSL的能力。 第二步:私钥的锻造 SSL/TLS证书需…...

从Craigslist广告到BHIS安全顾问:非科班生的渗透测试求职之路

本文讲述作者通过Craigslist招聘广告应聘Black Hills信息安全公司的经历,涉及文本简历防钓鱼技巧、Linux技术面试、网络端口知识考察以及问题解决能力的实际测试,展现了网络安全行业独特的招聘方式与技术评估要点。ADVISORY: 本博文中提及的技术和工具可能已过时,不适用于当…...

Java 微服务架构中的实践与挑战

一、引言 微服务架构已成为现代分布式系统的主流设计思想。它强调将单体应用拆分为一组小而自治的服务,每个服务聚焦特定业务能力,并通过轻量级通信方式协作。对于以 Java 为核心的企业级系统,微服务架构的落地往往依赖 Spring Boot、Spring Cloud 以及后续兴起的服务网格(…...

Java 与大数据处理:从 Hadoop 到实时计算

一、引言 在大数据时代,数据已经成为企业的战略资产。无论是金融风控、智能推荐,还是智慧城市与医疗健康,背后都依赖海量数据的存储与计算。作为企业开发的主流语言,Java 在大数据生态中扮演着不可替代的角色。从最早的 Hadoop 批处理框架,到 Spark、Flink 的内存与流式计…...

国产IT运维卡壳?乐维智能运维体让运维团队告别“适配难、监控乱”

“刚把3台核心服务器操作系统从CentOS7换成麒麟V10,之前用的Zabbix探针直接无法采集服务器的CPU、磁盘温度;机房中华为交换机、锐捷AC、浪潮存储分散在不同机柜,每台设备都要单独登录后台看告警,上周就因为没有及时发现交换机端口故障,导致研发部门断网整整一天……” 某1…...

ubuntu18安装mysql5.7

环境Os:ubuntu 18.04 desktop桌面版mysql:mysql-5.7.42-linux-glibc2.12 查看操作系统信息root@db:/soft# uname -a Linux db 4.15.0-20-generic #21-Ubuntu SMP Tue Apr 24 06:16:15 UTC 2018 x86_64 x86_64 x86_64 GNU/Linuxroot@db:/soft# cat /etc/os-release NAME="U…...

【IEEE出版 |已连续5届EI稳定检索】第六届计算机工程与智能控制学术会议(ICCEIC 2025)

聚焦计算机工程与智能控制前沿,涵盖网络安全、硬件系统、软件工程、嵌入式创新等多个核心议题及交叉学科研究。ICCEIC 2025将计算机工程和智能控制领域的创新学者和工业专家聚集到一个共同的论坛上,共享最新科研成果,破解关键问题,展望未来发展。第六届计算机工程与智能控制…...

在选择2025年代码托管平台时,Gitee和GitHub作为国内外两大主流平台各有优势。本文将从多个维度进行对比分析,帮助开发者做出更适合自身需求的选择。

在选择2025年代码托管平台时,Gitee和GitHub作为国内外两大主流平台各有优势。本文将从多个维度进行对比分析,帮助开发者做出更适合自身需求的选择。 作为中国本土领先的代码托管平台,Gitee近年来发展迅猛。数据显示,平台已拥有超过500万注册用户和1000万活跃仓库,在国内开…...

android使用socks5的教程

1.安装APP 下载地址忽略 2.使用配置的地址账号信息 少侠,我看你气度不凡天赋异禀,骨骼精奇,这么帅,来了就帮推荐一把吧 我的最近更新 最新发布文章、框架、咨询等,来看看吧...

vue3 自定义指令并实现页面元素平滑上升

基本示例 // 在directives/example.ts中 import type { Directive } from "vue"; /*** 示例*/ const example: Directive = {mounted(el) {console.log("mounted", el);},beforeUnmount(el) {console.log("beforeUnmount", el);} }; export defa…...

abp记录

abp记录abp8.3 : abp new MyCompany.MyProject -t app --no-ui -d ef --database-provider mysql --version 8.3.0...

强化学习(二十):模仿学习

一、概念 1、很多情况下,环境没有明确的奖励,例如聊天,自动驾驶的操作,无法明确定义好坏 2、不知道该怎么定义奖励时,可以收集专家示范 3、模仿学习(imitation learning,IL):智能体通过专家示范来学习,环境没有奖励给智能体二、行为克隆 1、类似于监督学习,专家做什…...

重生之从零开始的神经网络算法学习之路 —— 第七篇 重拾 PyTorch(超分辨率重建和脚本的使用)

重生之从零开始的神经网络算法学习之路 —— 第七篇 重拾 PyTorch(超分辨率重建和脚本的使用)重生之从零开始的神经网络算法学习之路——第七篇 重拾PyTorch(超分辨率重建和脚本的使用) 引言 在前一篇中,我们初步探索了PyTorch框架的使用并体验了GPU加速计算的优势。本篇将…...

从基础到实践(四十五):车载显示屏LCD、OLED、Mini-LED、MicroLED的工作原理、设计差异等说明 - 教程

从基础到实践(四十五):车载显示屏LCD、OLED、Mini-LED、MicroLED的工作原理、设计差异等说明 - 教程pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", &quo…...

国产项目管理工具崛起:Gitee如何以本土化优势重构开发协作生态

国产项目管理工具崛起:Gitee如何以本土化优势重构开发协作生态 在数字化浪潮席卷全球的当下,项目管理工具已成为企业技术栈中不可或缺的基础设施。随着中国科技产业的蓬勃发展,本土化项目管理平台正展现出强大的竞争力。作为国内领先的DevOps全生命周期解决方案提供商,Gite…...

GAS_Aura-Sending Gameplay Events

1讲了增加一个TaskNotify的通知蓝图,及其内部的函数的作用...