antecedentes
Debe implementar un flujo de trabajo que admita arrastrar y soltar nodos para generar un flujo de trabajo.
Realización empresarial
- Zoom de diseño de página de soporte
- Nodo de soporte
- Apoyar si más
- Soporta múltiples ramas
Punto técnico
- Fondo de cuadrícula
- Zoom del flujo de trabajo
- Realización de tecnología de flujo de trabajo
- Arrastre de nodo
Selección de tecnología
- vista
- jsplumb
- sortablejs (vue-draggable)
Dificultades para romper
Fondo de cuadrícula
Utilizando principalmente el CSS linear-gradient
y la background-size
implementación.
<div class="flow-layout">
<div class="flow-editor">
<div class="canvas-container">
</div>
</div>
</div>
.flow-layout {
display: flex;
flex-direction: column;
}
.flow-editor {
position: relative;
display: flex;
flex-direction: row;
flex: 1;
overflow: hidden;
}
.canvas-container {
flex: 1;
overflow: auto;
z-index: 0;
}
.canvas-container:before {
content: "";
height: 10px;
width: 100%;
display: block;
background-repeat-y: no-repeat;
position: absolute;
background-image: linear-gradient(90deg, #ccc 1px, transparent 0), linear-gradient(90deg, #ddd 1px, transparent 0);
background-size: 75px 10px, 5px 5px;
}
.canvas-container:after {
content: "";
height: 100%;
width: 10px;
display: block;
background-repeat-x: no-repeat;
position: absolute;
top: 0;
background-image: linear-gradient(#ccc 1px, transparent 0), linear-gradient(#ddd 1px, transparent 0);
background-size: 10px 75px, 5px 5px;
}
Zoom del flujo de trabajo
Principalmente combinado con el selector de atributos de CSS E[att="val"]
, la función de zoom se realiza modificando el valor de zoom.
<div class="flow-zoom" :data-zoom="canvasDataRoom + '%'">
<div class="zoom-btn">
<el-button size="mini" :class="{
'el-button--primary':canvasRoomMinusEnable}" icon="el-icon-minus"
circle
@click="handleMinusCanvas"></el-button>
</div>
<div class="zoom-btn">
<el-button size="mini" :class="{
'el-button--primary':canvasRoomPlusEnable}" icon="el-icon-plus"
circle
@click="handlePlusCanvas"></el-button>
</div>
</div>
<div class="canvas-container" :data-zoom="canvasDataRoom">
<div class="campaignCanvas"></div>
</div>
.canvas-container[data-zoom="100"] {
background-image: linear-gradient(#eee 1px, transparent 0), linear-gradient(90deg, #eee 1px, transparent 0), linear-gradient(#f5f5f5 1px, transparent 0), linear-gradient(90deg, #f5f5f5 1px, transparent 0);
background-size: 75px 75px, 75px 75px, 15px 15px, 15px 15px;
}
.canvas-container[data-zoom="90"] {
background-image: linear-gradient(#eee 1px, transparent 0), linear-gradient(90deg, #eee 1px, transparent 0), linear-gradient(#f5f5f5 1px, transparent 0), linear-gradient(90deg, #f5f5f5 1px, transparent 0);
background-size: 70px 70px, 70px 70px, 14px 14px, 14px 14px;
}
.canvas-container[data-zoom="80"] {
background-image: linear-gradient(#eee 1px, transparent 0), linear-gradient(90deg, #eee 1px, transparent 0), linear-gradient(#f5f5f5 1px, transparent 0), linear-gradient(90deg, #f5f5f5 1px, transparent 0);
background-size: 60px 60px, 60px 60px, 12px 12px, 12px 12px;
}
.canvas-container[data-zoom="70"] {
background-image: linear-gradient(#eee 1px, transparent 0), linear-gradient(90deg, #eee 1px, transparent 0), linear-gradient(#f5f5f5 1px, transparent 0), linear-gradient(90deg, #f5f5f5 1px, transparent 0);
background-size: 55px 55px, 55px 55px, 11px 11px, 11px 11px;
}
.canvas-container[data-zoom="60"] {
background-image: linear-gradient(#eee 1px, transparent 0), linear-gradient(90deg, #eee 1px, transparent 0), linear-gradient(#f5f5f5 1px, transparent 0), linear-gradient(90deg, #f5f5f5 1px, transparent 0);
background-size: 45px 45px, 45px 45px, 9px 9px, 9px 9px;
}
.canvas-container[data-zoom="50"] {
background-image: linear-gradient(#eee 1px, transparent 0), linear-gradient(90deg, #eee 1px, transparent 0), linear-gradient(#f5f5f5 1px, transparent 0), linear-gradient(90deg, #f5f5f5 1px, transparent 0);
background-size: 40px 40px, 40px 40px, 8px 8px, 8px 8px;
}
Realización de tecnología de flujo de trabajo
Confíe principalmente en jsplumb para lograrlo.
Utilice principalmente jsplumb para realizar la conexión de dos nodos.
Deje que el nodo dom se convierta en un nodo arrastrable jsplumb
<div id="_uuid">
</div>
jsPlumb.draggable(_uuid, {
});
Conexión de dos nodos.
jsPlumb.connect({
source: source,
target: target,
endpoint: 'Dot',
// 连接线的样式
connectorStyle: {
strokeStyle: "#ccc", joinStyle: "round", outlineColor: "#ccc"}, // 链接 style
// 连接线配置,起点可用
connector: ["Flowchart", {
stub: [10, 20],
gap: 1,
cornerRadius: 2,
alwaysRespectStubs: true
}], // 链接
//
endpointStyle: {
fill: 'transparent', outlineStroke: 'transparent', outlineWidth: 2},
// 线的样式
paintStyle: {
stroke: 'lightgray', strokeWidth: 2},
// 锚点的位置
anchor: ['BottomCenter', 'TopCenter'],
// 遮罩层-设置箭头
overlays: [
['PlainArrow', {
width: 10, length: 10, location: 1}],
['Custom', {
location: .5,
id: 'nodeTempSmall',
create: function () {
let $el = that.$refs[target][0].$el;
$el.dataset.target = target;
$el.dataset.source = source;
return $el;
},
visible: false
}],
['Label', {
location: 1, id: "flowItemDesc", cssClass: "node-item-label", visible: true}] //
]
});
Eliminar un nodo
jsPlumb.removeAllEndpoints(uuid);
Crea un nodo entre dos nodos
function createFlowConnectionLabel(sourceList, target) {
if (!Array.isArray(sourceList)) {
sourceList = [sourceList];
}
sourceList.forEach((source) => {
//
let lines = this.$options.jsPlumb.getConnections({
source: source,
target: target
});
//
lines.forEach((line) => {
line.getOverlay('nodeTempSmall').setVisible(true);
line.bind('click', this.handleFlowLabelClick);
});
});
}
Redacción publicitaria entre dos nodos
function createFlowItemLabel(source, target, label) {
this.$nextTick(() => {
let lines = this.$options.jsPlumb.getConnections({
source: source,
target: target
});
if (lines.length > 0) {
lines[0].getOverlay("flowItemDesc").setLabel(`<span class="node-item-title" title="${
label}">${
label}</span>`);
}
});
}
Arrastre de nodo
Confíe principalmente en sortablejs para lograr
Utilice principalmente componentes encapsulados que se pueden arrastrar mediante vue para lograr la función de arrastrar y soltar. Código de núcleo
El área de destino arrastrada.
<draggable class="flow-item node-temp node-temp-img"
ref="tempNode"
:id="flowItem.uuid"
:group="{name:'sortable', pull:false, put: true }">
</draggable>
El objeto de destino que se está arrastrando.
<draggable class="items-box"
:key="index"
:list="flowItem.children"
:group="{name:'sortable', pull: 'clone', put: false }"
v-bind="dragConfig"
:move="handleFlowMoveItem"
@start="handleFlowMoveStart"
@end="handleFlowMoveEnd"
:sort="false"
:ref="flowItem.ref">
<div class="node-temp-img"></div>
</template>
</draggable>
Captura de pantalla del proyecto
dirección del proyecto
github: https://github.com/bosscheng/vue-draggable-workflow
demostración: https://bosscheng.github.io/vue-draggable-workflow