commit
11f2d2845a
10
package.json
10
package.json
|
@ -1,8 +1,9 @@
|
||||||
{
|
{
|
||||||
"name": "muyu",
|
"name": "muyu",
|
||||||
"version": "3.6.3",
|
"version": "3.6.3",
|
||||||
"description": "若依管理系统",
|
"description": "张腾管理系统",
|
||||||
"author": "若依",
|
"author": "张腾",
|
||||||
|
"vuedraggable": "2.23.0",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vue-cli-service serve",
|
"dev": "vue-cli-service serve",
|
||||||
|
@ -11,6 +12,7 @@
|
||||||
"preview": "node build/index.js --preview",
|
"preview": "node build/index.js --preview",
|
||||||
"lint": "eslint --ext .js,.vue src"
|
"lint": "eslint --ext .js,.vue src"
|
||||||
},
|
},
|
||||||
|
|
||||||
"husky": {
|
"husky": {
|
||||||
"hooks": {
|
"hooks": {
|
||||||
"pre-commit": "lint-staged"
|
"pre-commit": "lint-staged"
|
||||||
|
@ -58,7 +60,9 @@
|
||||||
"vue-meta": "2.4.0",
|
"vue-meta": "2.4.0",
|
||||||
"vue-router": "3.4.9",
|
"vue-router": "3.4.9",
|
||||||
"vuedraggable": "2.24.3",
|
"vuedraggable": "2.24.3",
|
||||||
"vuex": "3.6.0"
|
"vuex": "3.6.0",
|
||||||
|
"lodash": "4.17.15",
|
||||||
|
"vue-codemirror": "^4.0.6"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vue/cli-plugin-babel": "4.4.6",
|
"@vue/cli-plugin-babel": "4.4.6",
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
import request from "@/utils/request";
|
||||||
|
|
||||||
|
export function showTask() {
|
||||||
|
return request({
|
||||||
|
url: '/integration/task/selectAll',
|
||||||
|
method: 'get',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function addTask(params) {
|
||||||
|
return request({
|
||||||
|
url: '/integration/task/addTask',
|
||||||
|
method: 'post',
|
||||||
|
params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function delTask(id) {
|
||||||
|
return request({
|
||||||
|
url: '/integration/task/delTask?id='+id,
|
||||||
|
method: 'delete',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function updTask(params) {
|
||||||
|
return request({
|
||||||
|
url: '/integration/task/delTask',
|
||||||
|
method: 'put',
|
||||||
|
params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
let dataA = {
|
||||||
|
name: '流程A',
|
||||||
|
nodeList: [
|
||||||
|
{
|
||||||
|
id: 'nodeA',
|
||||||
|
name: '流程A-节点A',
|
||||||
|
type: 'task',
|
||||||
|
left: '26px',
|
||||||
|
top: '161px',
|
||||||
|
ico: 'el-icon-user-solid'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'nodeB',
|
||||||
|
name: '流程A-节点B',
|
||||||
|
type: 'task',
|
||||||
|
left: '340px',
|
||||||
|
top: '161px',
|
||||||
|
ico: 'el-icon-goods'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'nodeC',
|
||||||
|
name: '流程A-节点C',
|
||||||
|
type: 'task',
|
||||||
|
left: '739px',
|
||||||
|
top: '161px',
|
||||||
|
ico: 'el-icon-present'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
lineList: [{
|
||||||
|
from: 'nodeA',
|
||||||
|
to: 'nodeB'
|
||||||
|
}, {
|
||||||
|
from: 'nodeB',
|
||||||
|
to: 'nodeC'
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getDataA () {
|
||||||
|
return dataA
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
let dataB = {
|
||||||
|
name: '流程B',
|
||||||
|
nodeList: [
|
||||||
|
{
|
||||||
|
id: 'nodeA',
|
||||||
|
name: '节点A-不可拖拽',
|
||||||
|
type: 'task',
|
||||||
|
left: '18px',
|
||||||
|
top: '223px',
|
||||||
|
ico: 'el-icon-user-solid',
|
||||||
|
state: 'success',
|
||||||
|
viewOnly: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'nodeB',
|
||||||
|
type: 'task',
|
||||||
|
name: '流程B-节点B',
|
||||||
|
left: '351px',
|
||||||
|
top: '96px',
|
||||||
|
ico: 'el-icon-goods',
|
||||||
|
state: 'error'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'nodeC',
|
||||||
|
name: '流程B-节点C',
|
||||||
|
type: 'task',
|
||||||
|
left: '354px',
|
||||||
|
top: '351px',
|
||||||
|
ico: 'el-icon-present',
|
||||||
|
state: 'warning'
|
||||||
|
}, {
|
||||||
|
id: 'nodeD',
|
||||||
|
name: '流程B-节点D',
|
||||||
|
type: 'task',
|
||||||
|
left: '723px',
|
||||||
|
top: '215px',
|
||||||
|
ico: 'el-icon-present',
|
||||||
|
state: 'running'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
lineList: [{
|
||||||
|
from: 'nodeA',
|
||||||
|
to: 'nodeB',
|
||||||
|
label: '条件A'
|
||||||
|
}, {
|
||||||
|
from: 'nodeA',
|
||||||
|
to: 'nodeC',
|
||||||
|
label: '条件B'
|
||||||
|
}, {
|
||||||
|
from: 'nodeB',
|
||||||
|
to: 'nodeD'
|
||||||
|
}, {
|
||||||
|
from: 'nodeC',
|
||||||
|
to: 'nodeD'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getDataB () {
|
||||||
|
return dataB
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
let dataC = {
|
||||||
|
name: '流程C',
|
||||||
|
nodeList: [
|
||||||
|
{
|
||||||
|
id: 'nodeA',
|
||||||
|
name: '流程C-节点A',
|
||||||
|
type: 'task',
|
||||||
|
left: '400px',
|
||||||
|
top: '15px',
|
||||||
|
ico: 'el-icon-user-solid'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'nodeB',
|
||||||
|
name: '流程C-节点B',
|
||||||
|
type: 'task',
|
||||||
|
left: '400px',
|
||||||
|
top: '200px',
|
||||||
|
ico: 'el-icon-goods'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'nodeC',
|
||||||
|
name: '流程C-节点C',
|
||||||
|
type: 'task',
|
||||||
|
left: '400px',
|
||||||
|
top: '378px',
|
||||||
|
ico: 'el-icon-present'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
lineList: [
|
||||||
|
{
|
||||||
|
from: 'nodeA',
|
||||||
|
to: 'nodeB'
|
||||||
|
}, {
|
||||||
|
from: 'nodeB',
|
||||||
|
to: 'nodeC'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getDataC () {
|
||||||
|
return dataC
|
||||||
|
}
|
|
@ -0,0 +1,71 @@
|
||||||
|
var dataD = {
|
||||||
|
name: '流程D',
|
||||||
|
nodeList: [
|
||||||
|
{
|
||||||
|
id: 'nodeA',
|
||||||
|
name: '流程D-节点A',
|
||||||
|
type: 'task',
|
||||||
|
left: '18px',
|
||||||
|
top: '223px',
|
||||||
|
ico: 'el-icon-user-solid',
|
||||||
|
state: 'success'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'nodeB',
|
||||||
|
type: 'task',
|
||||||
|
name: '流程D-节点B',
|
||||||
|
left: '351px',
|
||||||
|
top: '96px',
|
||||||
|
ico: 'el-icon-goods',
|
||||||
|
state: 'error'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'nodeC',
|
||||||
|
name: '流程D-节点C',
|
||||||
|
type: 'task',
|
||||||
|
left: '354px',
|
||||||
|
top: '351px',
|
||||||
|
ico: 'el-icon-present',
|
||||||
|
state: 'warning'
|
||||||
|
}, {
|
||||||
|
id: 'nodeD',
|
||||||
|
name: '流程D-节点D',
|
||||||
|
type: 'task',
|
||||||
|
left: '723px',
|
||||||
|
top: '215px',
|
||||||
|
ico: 'el-icon-present',
|
||||||
|
state: 'running'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
lineList: [{
|
||||||
|
from: 'nodeA',
|
||||||
|
to: 'nodeB',
|
||||||
|
label: '直线,自定义线样式,固定锚点',
|
||||||
|
connector: 'Straight',
|
||||||
|
anchors: ['Top', 'Bottom'],
|
||||||
|
paintStyle: {strokeWidth: 2, stroke: '#1879FF'}
|
||||||
|
}, {
|
||||||
|
from: 'nodeA',
|
||||||
|
to: 'nodeC',
|
||||||
|
label: '贝塞尔曲线,固定锚点',
|
||||||
|
connector: 'Bezier',
|
||||||
|
anchors: ['Bottom', 'Left']
|
||||||
|
}, {
|
||||||
|
from: 'nodeB',
|
||||||
|
to: 'nodeD',
|
||||||
|
label: '默认连线样式,动态锚点'
|
||||||
|
}, {
|
||||||
|
from: 'nodeC',
|
||||||
|
to: 'nodeD',
|
||||||
|
label: '默认连线样式,动态锚点'
|
||||||
|
}, {
|
||||||
|
from: 'nodeC',
|
||||||
|
to: 'nodeC',
|
||||||
|
label: '自连接'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getDataD() {
|
||||||
|
return dataD
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
var dataE = {
|
||||||
|
name: '流程E,力导图',
|
||||||
|
nodeList: [
|
||||||
|
{
|
||||||
|
id: 'nodeA',
|
||||||
|
name: '流程D-节点A',
|
||||||
|
type: 'task',
|
||||||
|
ico: 'el-icon-user-solid',
|
||||||
|
state: 'success'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'nodeB',
|
||||||
|
type: 'task',
|
||||||
|
name: '流程D-节点B',
|
||||||
|
ico: 'el-icon-goods',
|
||||||
|
state: 'error'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'nodeC',
|
||||||
|
name: '流程D-节点C',
|
||||||
|
type: 'task',
|
||||||
|
ico: 'el-icon-present',
|
||||||
|
state: 'warning'
|
||||||
|
}, {
|
||||||
|
id: 'nodeD',
|
||||||
|
name: '流程D-节点D',
|
||||||
|
type: 'task',
|
||||||
|
ico: 'el-icon-present',
|
||||||
|
state: 'running'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
lineList: [{
|
||||||
|
from: 'nodeA',
|
||||||
|
to: 'nodeB'
|
||||||
|
}, {
|
||||||
|
from: 'nodeA',
|
||||||
|
to: 'nodeC',
|
||||||
|
label: 'hello'
|
||||||
|
}, {
|
||||||
|
from: 'nodeB',
|
||||||
|
to: 'nodeD'
|
||||||
|
}, {
|
||||||
|
from: 'nodeC',
|
||||||
|
to: 'nodeD'
|
||||||
|
}, {
|
||||||
|
from: 'nodeC',
|
||||||
|
to: 'nodeC'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getDataE() {
|
||||||
|
return dataE
|
||||||
|
}
|
|
@ -0,0 +1,182 @@
|
||||||
|
/**
|
||||||
|
* 感谢 https://github.com/chaangliu/ForceDirectedLayout/blob/master/javascript/force-directed.js
|
||||||
|
* A force directed graph layout implementation by liuchang on 2018/05/10.
|
||||||
|
*/
|
||||||
|
const CANVAS_WIDTH = 1000
|
||||||
|
const CANVAS_HEIGHT = 1000
|
||||||
|
let k
|
||||||
|
let mNodeList = []
|
||||||
|
let mEdgeList = []
|
||||||
|
let mDxMap = {}
|
||||||
|
let mDyMap = {}
|
||||||
|
let mNodeMap = {}
|
||||||
|
|
||||||
|
export function ForceDirected(data = {}) {
|
||||||
|
// generate nodes and edges
|
||||||
|
// for (let i = 0; i < 20; i++) {
|
||||||
|
// mNodeList.push(new Node(i))
|
||||||
|
// }
|
||||||
|
k = 0
|
||||||
|
mNodeList = []
|
||||||
|
mEdgeList = []
|
||||||
|
mDxMap = {}
|
||||||
|
mDyMap = {}
|
||||||
|
mNodeMap = {}
|
||||||
|
|
||||||
|
let nodeList = data.nodeList
|
||||||
|
for (let i = 0; i < nodeList.length; i++) {
|
||||||
|
let node = nodeList[i]
|
||||||
|
mNodeList.push(node)
|
||||||
|
}
|
||||||
|
|
||||||
|
// for (let i = 0; i < 20; i++) {
|
||||||
|
// let edgeCount = Math.random() * 8 + 1
|
||||||
|
// for (let j = 0; j < edgeCount; j++) {
|
||||||
|
// let targetId = Math.floor(Math.random() * 20)
|
||||||
|
// let edge = new Edge(i, targetId)
|
||||||
|
// mEdgeList.push(edge)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// line 转 edge
|
||||||
|
let lineList = data.lineList
|
||||||
|
for (let i = 0; i < lineList.length; i++) {
|
||||||
|
let line = lineList[i]
|
||||||
|
let edge = new Edge(line.from, line.to)
|
||||||
|
mEdgeList.push(edge)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mNodeList && mEdgeList) {
|
||||||
|
k = Math.sqrt(CANVAS_WIDTH * CANVAS_HEIGHT / mNodeList.length)
|
||||||
|
}
|
||||||
|
for (let i = 0; i < mNodeList.length; i++) {
|
||||||
|
let node = mNodeList[i]
|
||||||
|
if (node) {
|
||||||
|
mNodeMap[node.id] = node
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 随机生成坐标. Generate coordinates randomly.
|
||||||
|
let initialX, initialY, initialSize = 40.0
|
||||||
|
for (let i in mNodeList) {
|
||||||
|
initialX = CANVAS_WIDTH * 0.5
|
||||||
|
initialY = CANVAS_HEIGHT * 0.5
|
||||||
|
mNodeList[i].x = initialX + initialSize * (Math.random() - 0.5)
|
||||||
|
mNodeList[i].y = initialY + initialSize * (Math.random() - 0.5)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 迭代200次. Iterate 200 times.
|
||||||
|
for (let i = 0; i < 200; i++) {
|
||||||
|
calculateRepulsive()
|
||||||
|
calculateTraction()
|
||||||
|
updateCoordinates()
|
||||||
|
}
|
||||||
|
// console.log(JSON.stringify(new Result(mNodeList, mEdgeList)))
|
||||||
|
// 坐标添加px
|
||||||
|
for (let i = 0; i < mNodeList.length; i++) {
|
||||||
|
let node = mNodeList[i]
|
||||||
|
node.left = node.x + 'px'
|
||||||
|
node.top = node.y + 'px'
|
||||||
|
node.x = undefined
|
||||||
|
node.y = undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
data.nodeList = mNodeList
|
||||||
|
|
||||||
|
// console.log(data)
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
function Node(id = null) {
|
||||||
|
this.id = id
|
||||||
|
this.x = 22
|
||||||
|
this.y = null
|
||||||
|
}
|
||||||
|
|
||||||
|
function Edge(source = null, target = null) {
|
||||||
|
this.source = source
|
||||||
|
this.target = target
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算两个Node的斥力产生的单位位移。
|
||||||
|
* Calculate the displacement generated by the repulsive force between two nodes.*
|
||||||
|
*/
|
||||||
|
function calculateRepulsive() {
|
||||||
|
let ejectFactor = 6
|
||||||
|
let distX, distY, dist
|
||||||
|
for (let i = 0; i < mNodeList.length; i++) {
|
||||||
|
mDxMap[mNodeList[i].id] = 0.0
|
||||||
|
mDyMap[mNodeList[i].id] = 0.0
|
||||||
|
for (let j = 0; j < mNodeList.length; j++) {
|
||||||
|
if (i !== j) {
|
||||||
|
distX = mNodeList[i].x - mNodeList[j].x
|
||||||
|
distY = mNodeList[i].y - mNodeList[j].y
|
||||||
|
dist = Math.sqrt(distX * distX + distY * distY)
|
||||||
|
}
|
||||||
|
if (dist < 30) {
|
||||||
|
ejectFactor = 5
|
||||||
|
}
|
||||||
|
if (dist > 0 && dist < 250) {
|
||||||
|
let id = mNodeList[i].id
|
||||||
|
mDxMap[id] = mDxMap[id] + distX / dist * k * k / dist * ejectFactor
|
||||||
|
mDyMap[id] = mDyMap[id] + distY / dist * k * k / dist * ejectFactor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算Edge的引力对两端Node产生的引力。
|
||||||
|
* Calculate the traction force generated by the edge acted on the two nodes of its two ends.
|
||||||
|
*/
|
||||||
|
function calculateTraction() {
|
||||||
|
let condenseFactor = 3
|
||||||
|
let startNode, endNode
|
||||||
|
for (let e = 0; e < mEdgeList.length; e++) {
|
||||||
|
let eStartID = mEdgeList[e].source
|
||||||
|
let eEndID = mEdgeList[e].target
|
||||||
|
startNode = mNodeMap[eStartID]
|
||||||
|
endNode = mNodeMap[eEndID]
|
||||||
|
if (!startNode) {
|
||||||
|
console.log('Cannot find start node id: ' + eStartID + ', please check it out.')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!endNode) {
|
||||||
|
console.log('Cannot find end node id: ' + eEndID + ', please check it out.')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let distX, distY, dist
|
||||||
|
distX = startNode.x - endNode.x
|
||||||
|
distY = startNode.y - endNode.y
|
||||||
|
dist = Math.sqrt(distX * distX + distY * distY)
|
||||||
|
mDxMap[eStartID] = mDxMap[eStartID] - distX * dist / k * condenseFactor
|
||||||
|
mDyMap[eStartID] = mDyMap[eStartID] - distY * dist / k * condenseFactor
|
||||||
|
mDxMap[eEndID] = mDxMap[eEndID] + distX * dist / k * condenseFactor
|
||||||
|
mDyMap[eEndID] = mDyMap[eEndID] + distY * dist / k * condenseFactor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新坐标。
|
||||||
|
* update the coordinates.
|
||||||
|
*/
|
||||||
|
function updateCoordinates() {
|
||||||
|
let maxt = 4, maxty = 3 // Additional coefficients.
|
||||||
|
for (let v = 0; v < mNodeList.length; v++) {
|
||||||
|
let node = mNodeList[v]
|
||||||
|
let dx = Math.floor(mDxMap[node.id])
|
||||||
|
let dy = Math.floor(mDyMap[node.id])
|
||||||
|
|
||||||
|
if (dx < -maxt) dx = -maxt
|
||||||
|
if (dx > maxt) dx = maxt
|
||||||
|
if (dy < -maxty) dy = -maxty
|
||||||
|
if (dy > maxty) dy = maxty
|
||||||
|
node.x = node.x + dx >= CANVAS_WIDTH || node.x + dx <= 0 ? node.x - dx : node.x + dx
|
||||||
|
node.y = node.y + dy >= CANVAS_HEIGHT || node.y + dy <= 0 ? node.y - dy : node.y + dy
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Result(nodes = null, links = null) {
|
||||||
|
this.nodes = nodes
|
||||||
|
this.links = links
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
<template>
|
||||||
|
<el-dialog
|
||||||
|
title="帮助"
|
||||||
|
:visible.sync="dialogVisible"
|
||||||
|
width="70%"
|
||||||
|
customClass="flowHelp"
|
||||||
|
>
|
||||||
|
<el-tabs tab-position="left">
|
||||||
|
<el-tab-pane label="如何新增">
|
||||||
|
<el-divider content-position="left">如何新增</el-divider>
|
||||||
|
<div>按住鼠标拖拽左侧组件到中间画布中松开鼠标即可</div>
|
||||||
|
</el-tab-pane>
|
||||||
|
<el-tab-pane label="如何删除">
|
||||||
|
<el-divider content-position="left">页面删除</el-divider>
|
||||||
|
<div>
|
||||||
|
鼠标点中需要删除的节点,点击左上角的删除图标
|
||||||
|
</div>
|
||||||
|
<el-divider content-position="left">通过代码删除</el-divider>
|
||||||
|
<pre>this.deleteNode(nodeId)</pre>
|
||||||
|
</el-tab-pane>
|
||||||
|
<el-tab-pane label="如何移动">
|
||||||
|
<el-divider content-position="left">如何移动</el-divider>
|
||||||
|
<div>鼠标移动到节点中,当鼠标变为可拖拽的图标时按下鼠标移动到新的位置松开鼠标</div>
|
||||||
|
</el-tab-pane>
|
||||||
|
<el-tab-pane label="如何连线">
|
||||||
|
<el-divider content-position="left">如何连线</el-divider>
|
||||||
|
<div>鼠标移动到节点中左侧的图标上,当鼠标变为+时按下鼠标移动到另一个节点中松开鼠标</div>
|
||||||
|
</el-tab-pane>
|
||||||
|
<el-tab-pane label="如何添加条件">
|
||||||
|
<el-divider content-position="left">如何添加条件</el-divider>
|
||||||
|
<div>点击画布中的连线,在页面右侧会出现一个表单,输入新的条件,点击【保存】</div>
|
||||||
|
</el-tab-pane>
|
||||||
|
<el-tab-pane label="如何进行后端交互存储">
|
||||||
|
<el-divider content-position="left">如何进行后端交互存储</el-divider>
|
||||||
|
<div>参考: https://gitee.com/xiaoka2017/easy-flow-sdk</div>
|
||||||
|
</el-tab-pane>
|
||||||
|
</el-tabs>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
dialogVisible: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
components: {},
|
||||||
|
methods: {
|
||||||
|
init() {
|
||||||
|
this.dialogVisible = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.flowHelp {
|
||||||
|
height: 80%;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,225 @@
|
||||||
|
/*画布容器*/
|
||||||
|
#efContainer {
|
||||||
|
position: relative;
|
||||||
|
overflow: scroll;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*顶部工具栏*/
|
||||||
|
.ef-tooltar {
|
||||||
|
padding-left: 10px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
height: 42px;
|
||||||
|
line-height: 42px;
|
||||||
|
z-index: 3;
|
||||||
|
border-bottom: 1px solid #DADCE0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.jtk-overlay {
|
||||||
|
cursor: pointer;
|
||||||
|
color: #4A4A4A;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*节点菜单*/
|
||||||
|
.ef-node-pmenu {
|
||||||
|
cursor: pointer;
|
||||||
|
height: 32px;
|
||||||
|
line-height: 32px;
|
||||||
|
width: 225px;
|
||||||
|
display: block;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #4A4A4A;
|
||||||
|
padding-left: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ef-node-pmenu:hover {
|
||||||
|
background-color: #E0E0E0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ef-node-menu-li {
|
||||||
|
color: #565758;
|
||||||
|
width: 150px;
|
||||||
|
border: 1px dashed #E0E3E7;
|
||||||
|
margin: 5px 0 5px 0;
|
||||||
|
padding: 5px;
|
||||||
|
border-radius: 5px;
|
||||||
|
padding-left: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ef-node-menu-li:hover {
|
||||||
|
/* 设置移动样式*/
|
||||||
|
cursor: move;
|
||||||
|
background-color: #F0F7FF;
|
||||||
|
border: 1px dashed #1879FF;
|
||||||
|
border-left: 4px solid #1879FF;
|
||||||
|
padding-left: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ef-node-menu-ul {
|
||||||
|
list-style: none;
|
||||||
|
padding-left: 20px
|
||||||
|
}
|
||||||
|
|
||||||
|
/*节点的最外层容器*/
|
||||||
|
.ef-node-container {
|
||||||
|
position: absolute;
|
||||||
|
display: flex;
|
||||||
|
width: 170px;
|
||||||
|
height: 32px;
|
||||||
|
border: 1px solid #E0E3E7;
|
||||||
|
border-radius: 5px;
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ef-node-container:hover {
|
||||||
|
/* 设置移动样式*/
|
||||||
|
cursor: move;
|
||||||
|
background-color: #F0F7FF;
|
||||||
|
/*box-shadow: #1879FF 0px 0px 12px 0px;*/
|
||||||
|
background-color: #F0F7FF;
|
||||||
|
border: 1px dashed #1879FF;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*节点激活样式*/
|
||||||
|
.ef-node-active {
|
||||||
|
background-color: #F0F7FF;
|
||||||
|
/*box-shadow: #1879FF 0px 0px 12px 0px;*/
|
||||||
|
background-color: #F0F7FF;
|
||||||
|
border: 1px solid #1879FF;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*节点左侧的竖线*/
|
||||||
|
.ef-node-left {
|
||||||
|
width: 4px;
|
||||||
|
background-color: #1879FF;
|
||||||
|
border-radius: 4px 0 0 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*节点左侧的图标*/
|
||||||
|
.ef-node-left-ico {
|
||||||
|
line-height: 32px;
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ef-node-left-ico:hover {
|
||||||
|
/* 设置拖拽的样式 */
|
||||||
|
cursor: crosshair;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*节点显示的文字*/
|
||||||
|
.ef-node-text {
|
||||||
|
color: #565758;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 32px;
|
||||||
|
margin-left: 8px;
|
||||||
|
width: 100px;
|
||||||
|
/* 设置超出宽度文本显示方式*/
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*节点右侧的图标*/
|
||||||
|
.ef-node-right-ico {
|
||||||
|
line-height: 32px;
|
||||||
|
position: absolute;
|
||||||
|
right: 5px;
|
||||||
|
color: #84CF65;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*节点的几种状态样式*/
|
||||||
|
.el-node-state-success {
|
||||||
|
line-height: 32px;
|
||||||
|
position: absolute;
|
||||||
|
right: 5px;
|
||||||
|
color: #84CF65;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-node-state-error {
|
||||||
|
line-height: 32px;
|
||||||
|
position: absolute;
|
||||||
|
right: 5px;
|
||||||
|
color: #F56C6C;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-node-state-warning {
|
||||||
|
line-height: 32px;
|
||||||
|
position: absolute;
|
||||||
|
right: 5px;
|
||||||
|
color: #E6A23C;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-node-state-running {
|
||||||
|
line-height: 32px;
|
||||||
|
position: absolute;
|
||||||
|
right: 5px;
|
||||||
|
color: #84CF65;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*node-form*/
|
||||||
|
.ef-node-form-header {
|
||||||
|
height: 32px;
|
||||||
|
border-top: 1px solid #dce3e8;
|
||||||
|
border-bottom: 1px solid #dce3e8;
|
||||||
|
background: #F1F3F4;
|
||||||
|
color: #000;
|
||||||
|
line-height: 32px;
|
||||||
|
padding-left: 12px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ef-node-form-body {
|
||||||
|
margin-top: 10px;
|
||||||
|
padding-right: 10px;
|
||||||
|
padding-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 连线中的label 样式*/
|
||||||
|
.jtk-overlay.flowLabel:not(.aLabel) {
|
||||||
|
padding: 4px 10px;
|
||||||
|
background-color: white;
|
||||||
|
color: #565758 !important;
|
||||||
|
border: 1px solid #E0E3E7;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* label 为空的样式 */
|
||||||
|
.emptyFlowLabel {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.ef-dot {
|
||||||
|
background-color: #1879FF;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ef-dot-hover {
|
||||||
|
background-color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ef-rectangle {
|
||||||
|
background-color: #1879FF;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ef-rectangle-hover {
|
||||||
|
background-color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ef-img {
|
||||||
|
}
|
||||||
|
|
||||||
|
.ef-img-hover {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.ef-drop-hover{
|
||||||
|
border: 1px dashed #1879FF;
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
<template>
|
||||||
|
<el-dialog
|
||||||
|
title="流程数据信息"
|
||||||
|
:visible.sync="dialogVisible"
|
||||||
|
width="70%"
|
||||||
|
>
|
||||||
|
<el-alert
|
||||||
|
title="使用说明"
|
||||||
|
type="warning"
|
||||||
|
description="以下流程信息可以被存储起来,方便下一次流程加载"
|
||||||
|
show-icon
|
||||||
|
close-text="知道了"
|
||||||
|
>
|
||||||
|
</el-alert>
|
||||||
|
<br/>
|
||||||
|
<!--一个高亮显示的插件-->
|
||||||
|
<codemirror
|
||||||
|
:value="flowJsonData"
|
||||||
|
:options="options"
|
||||||
|
class="code"
|
||||||
|
></codemirror>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import 'codemirror/lib/codemirror.css'
|
||||||
|
import { codemirror } from 'vue-codemirror'
|
||||||
|
|
||||||
|
require("codemirror/mode/javascript/javascript.js")
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
data: Object,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
dialogVisible: false,
|
||||||
|
flowJsonData: {},
|
||||||
|
options: {
|
||||||
|
mode: {name: "javascript", json: true},
|
||||||
|
lineNumbers: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
codemirror
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
init() {
|
||||||
|
this.dialogVisible = true
|
||||||
|
this.flowJsonData = JSON.stringify(this.data, null, 4).toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,157 @@
|
||||||
|
export const easyFlowMixin = {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
jsplumbSetting: {
|
||||||
|
// 动态锚点、位置自适应
|
||||||
|
Anchors: ['Top', 'TopCenter', 'TopRight', 'TopLeft', 'Right', 'RightMiddle', 'Bottom', 'BottomCenter', 'BottomRight', 'BottomLeft', 'Left', 'LeftMiddle'],
|
||||||
|
// 容器ID
|
||||||
|
Container: 'efContainer',
|
||||||
|
// 连线的样式,直线或者曲线等,可选值: StateMachine、Flowchart,Bezier、Straight
|
||||||
|
Connector: ['Bezier', {curviness: 100}],
|
||||||
|
// Connector: ['Straight', {stub: 20, gap: 1}],
|
||||||
|
// Connector: ['Flowchart', {stub: 30, gap: 1, alwaysRespectStubs: false, midpoint: 0.5, cornerRadius: 10}],
|
||||||
|
// Connector: ['StateMachine', {margin: 5, curviness: 10, proximityLimit: 80}],
|
||||||
|
// 鼠标不能拖动删除线
|
||||||
|
ConnectionsDetachable: false,
|
||||||
|
// 删除线的时候节点不删除
|
||||||
|
DeleteEndpointsOnDetach: false,
|
||||||
|
/**
|
||||||
|
* 连线的两端端点类型:圆形
|
||||||
|
* radius: 圆的半径,越大圆越大
|
||||||
|
*/
|
||||||
|
// Endpoint: ['Dot', {radius: 5, cssClass: 'ef-dot', hoverClass: 'ef-dot-hover'}],
|
||||||
|
/**
|
||||||
|
* 连线的两端端点类型:矩形
|
||||||
|
* height: 矩形的高
|
||||||
|
* width: 矩形的宽
|
||||||
|
*/
|
||||||
|
// Endpoint: ['Rectangle', {height: 20, width: 20, cssClass: 'ef-rectangle', hoverClass: 'ef-rectangle-hover'}],
|
||||||
|
/**
|
||||||
|
* 图像端点
|
||||||
|
*/
|
||||||
|
// Endpoint: ['Image', {src: 'https://www.easyicon.net/api/resizeApi.php?id=1181776&size=32', cssClass: 'ef-img', hoverClass: 'ef-img-hover'}],
|
||||||
|
/**
|
||||||
|
* 空白端点
|
||||||
|
*/
|
||||||
|
Endpoint: ['Blank', {Overlays: ''}],
|
||||||
|
// Endpoints: [['Dot', {radius: 5, cssClass: 'ef-dot', hoverClass: 'ef-dot-hover'}], ['Rectangle', {height: 20, width: 20, cssClass: 'ef-rectangle', hoverClass: 'ef-rectangle-hover'}]],
|
||||||
|
/**
|
||||||
|
* 连线的两端端点样式
|
||||||
|
* fill: 颜色值,如:#12aabb,为空不显示
|
||||||
|
* outlineWidth: 外边线宽度
|
||||||
|
*/
|
||||||
|
EndpointStyle: {fill: '#1879ffa1', outlineWidth: 1},
|
||||||
|
// 是否打开jsPlumb的内部日志记录
|
||||||
|
LogEnabled: true,
|
||||||
|
/**
|
||||||
|
* 连线的样式
|
||||||
|
*/
|
||||||
|
PaintStyle: {
|
||||||
|
// 线的颜色
|
||||||
|
stroke: '#E0E3E7',
|
||||||
|
// 线的粗细,值越大线越粗
|
||||||
|
strokeWidth: 1,
|
||||||
|
// 设置外边线的颜色,默认设置透明,这样别人就看不见了,点击线的时候可以不用精确点击,参考 https://blog.csdn.net/roymno2/article/details/72717101
|
||||||
|
outlineStroke: 'transparent',
|
||||||
|
// 线外边的宽,值越大,线的点击范围越大
|
||||||
|
outlineWidth: 10
|
||||||
|
},
|
||||||
|
DragOptions: {cursor: 'pointer', zIndex: 2000},
|
||||||
|
/**
|
||||||
|
* 叠加 参考: https://www.jianshu.com/p/d9e9918fd928
|
||||||
|
*/
|
||||||
|
Overlays: [
|
||||||
|
// 箭头叠加
|
||||||
|
['Arrow', {
|
||||||
|
width: 10, // 箭头尾部的宽度
|
||||||
|
length: 8, // 从箭头的尾部到头部的距离
|
||||||
|
location: 1, // 位置,建议使用0~1之间
|
||||||
|
direction: 1, // 方向,默认值为1(表示向前),可选-1(表示向后)
|
||||||
|
foldback: 0.623 // 折回,也就是尾翼的角度,默认0.623,当为1时,为正三角
|
||||||
|
}],
|
||||||
|
// ['Diamond', {
|
||||||
|
// events: {
|
||||||
|
// dblclick: function (diamondOverlay, originalEvent) {
|
||||||
|
// console.log('double click on diamond overlay for : ' + diamondOverlay.component)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }],
|
||||||
|
['Label', {
|
||||||
|
label: '',
|
||||||
|
location: 0.1,
|
||||||
|
cssClass: 'aLabel'
|
||||||
|
}]
|
||||||
|
],
|
||||||
|
// 绘制图的模式 svg、canvas
|
||||||
|
RenderMode: 'svg',
|
||||||
|
// 鼠标滑过线的样式
|
||||||
|
HoverPaintStyle: {stroke: '#b0b2b5', strokeWidth: 1},
|
||||||
|
// 滑过锚点效果
|
||||||
|
// EndpointHoverStyle: {fill: 'red'}
|
||||||
|
Scope: 'jsPlumb_DefaultScope' // 范围,具有相同scope的点才可连接
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 连线参数
|
||||||
|
*/
|
||||||
|
jsplumbConnectOptions: {
|
||||||
|
isSource: true,
|
||||||
|
isTarget: true,
|
||||||
|
// 动态锚点、提供了4个方向 Continuous、AutoDefault
|
||||||
|
anchor: 'Continuous',
|
||||||
|
// 设置连线上面的label样式
|
||||||
|
labelStyle: {
|
||||||
|
cssClass: 'flowLabel'
|
||||||
|
},
|
||||||
|
// 修改了jsplumb 源码,支持label 为空传入自定义style
|
||||||
|
emptyLabelStyle: {
|
||||||
|
cssClass: 'emptyFlowLabel'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 源点配置参数
|
||||||
|
*/
|
||||||
|
jsplumbSourceOptions: {
|
||||||
|
// 设置可以拖拽的类名,只要鼠标移动到该类名上的DOM,就可以拖拽连线
|
||||||
|
filter: '.flow-node-drag',
|
||||||
|
filterExclude: false,
|
||||||
|
anchor: 'Continuous',
|
||||||
|
// 是否允许自己连接自己
|
||||||
|
allowLoopback: true,
|
||||||
|
maxConnections: -1,
|
||||||
|
onMaxConnections: function (info, e) {
|
||||||
|
console.log(`超过了最大值连线: ${info.maxConnections}`)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 参考 https://www.cnblogs.com/mq0036/p/7942139.html
|
||||||
|
jsplumbSourceOptions2: {
|
||||||
|
// 设置可以拖拽的类名,只要鼠标移动到该类名上的DOM,就可以拖拽连线
|
||||||
|
filter: '.flow-node-drag',
|
||||||
|
filterExclude: false,
|
||||||
|
// anchor: 'Continuous',
|
||||||
|
// 是否允许自己连接自己
|
||||||
|
allowLoopback: true,
|
||||||
|
connector: ['Flowchart', {curviness: 50}],
|
||||||
|
connectorStyle: {
|
||||||
|
// 线的颜色
|
||||||
|
stroke: 'red',
|
||||||
|
// 线的粗细,值越大线越粗
|
||||||
|
strokeWidth: 1,
|
||||||
|
// 设置外边线的颜色,默认设置透明,这样别人就看不见了,点击线的时候可以不用精确点击,参考 https://blog.csdn.net/roymno2/article/details/72717101
|
||||||
|
outlineStroke: 'transparent',
|
||||||
|
// 线外边的宽,值越大,线的点击范围越大
|
||||||
|
outlineWidth: 10
|
||||||
|
},
|
||||||
|
connectorHoverStyle: {stroke: 'red', strokeWidth: 2}
|
||||||
|
},
|
||||||
|
jsplumbTargetOptions: {
|
||||||
|
// 设置可以拖拽的类名,只要鼠标移动到该类名上的DOM,就可以拖拽连线
|
||||||
|
filter: '.flow-node-drag',
|
||||||
|
filterExclude: false,
|
||||||
|
// 是否允许自己连接自己
|
||||||
|
anchor: 'Continuous',
|
||||||
|
allowLoopback: true,
|
||||||
|
dropOptions: {hoverClass: 'ef-drop-hover'}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,79 @@
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
ref="node"
|
||||||
|
:style="nodeContainerStyle"
|
||||||
|
@click="clickNode"
|
||||||
|
@mouseup="changeNodeSite"
|
||||||
|
:class="nodeContainerClass"
|
||||||
|
>
|
||||||
|
<!-- 最左侧的那条竖线 -->
|
||||||
|
<div class="ef-node-left"></div>
|
||||||
|
<!-- 节点类型的图标 -->
|
||||||
|
<div class="ef-node-left-ico flow-node-drag">
|
||||||
|
<i :class="nodeIcoClass"></i>
|
||||||
|
</div>
|
||||||
|
<!-- 节点名称 -->
|
||||||
|
<div class="ef-node-text" :show-overflow-tooltip="true">
|
||||||
|
{{node.name}}
|
||||||
|
</div>
|
||||||
|
<!-- 节点状态图标 -->
|
||||||
|
<div class="ef-node-right-ico">
|
||||||
|
<i class="el-icon-circle-check el-node-state-success" v-show="node.state === 'success'"></i>
|
||||||
|
<i class="el-icon-circle-close el-node-state-error" v-show="node.state === 'error'"></i>
|
||||||
|
<i class="el-icon-warning-outline el-node-state-warning" v-show="node.state === 'warning'"></i>
|
||||||
|
<i class="el-icon-loading el-node-state-running" v-show="node.state === 'running'"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
node: Object,
|
||||||
|
activeElement: Object
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
nodeContainerClass() {
|
||||||
|
return {
|
||||||
|
'ef-node-container': true,
|
||||||
|
'ef-node-active': this.activeElement.type == 'node' ? this.activeElement.nodeId === this.node.id : false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 节点容器样式
|
||||||
|
nodeContainerStyle() {
|
||||||
|
return {
|
||||||
|
top: this.node.top,
|
||||||
|
left: this.node.left
|
||||||
|
}
|
||||||
|
},
|
||||||
|
nodeIcoClass() {
|
||||||
|
var nodeIcoClass = {}
|
||||||
|
nodeIcoClass[this.node.ico] = true
|
||||||
|
// 添加该class可以推拽连线出来,viewOnly 可以控制节点是否运行编辑
|
||||||
|
nodeIcoClass['flow-node-drag'] = this.node.viewOnly ? false : true
|
||||||
|
return nodeIcoClass
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
// 点击节点
|
||||||
|
clickNode() {
|
||||||
|
this.$emit('clickNode', this.node.id)
|
||||||
|
},
|
||||||
|
// 鼠标移动后抬起
|
||||||
|
changeNodeSite() {
|
||||||
|
// 避免抖动
|
||||||
|
if (this.node.left == this.$refs.node.style.left && this.node.top == this.$refs.node.style.top) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.$emit('changeNodeSite', {
|
||||||
|
nodeId: this.node.id,
|
||||||
|
left: this.$refs.node.style.left,
|
||||||
|
top: this.$refs.node.style.top,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -0,0 +1,134 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="ef-node-form">
|
||||||
|
<div class="ef-node-form-header">
|
||||||
|
编辑
|
||||||
|
</div>
|
||||||
|
<div class="ef-node-form-body">
|
||||||
|
<el-form :model="node" ref="dataForm" label-width="80px" v-show="type === 'node'">
|
||||||
|
<el-form-item label="类型">
|
||||||
|
<el-input v-model="node.type" :disabled="true"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="名称">
|
||||||
|
<el-input v-model="node.name"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="left坐标">
|
||||||
|
<el-input v-model="node.left" :disabled="true"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="top坐标">
|
||||||
|
<el-input v-model="node.top" :disabled="true"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="ico图标">
|
||||||
|
<el-input v-model="node.ico"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="状态">
|
||||||
|
<el-select v-model="node.state" placeholder="请选择">
|
||||||
|
<el-option
|
||||||
|
v-for="item in stateList"
|
||||||
|
:key="item.state"
|
||||||
|
:label="item.label"
|
||||||
|
:value="item.state">
|
||||||
|
</el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button icon="el-icon-close">重置</el-button>
|
||||||
|
<el-button type="primary" icon="el-icon-check" @click="save">保存</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<el-form :model="line" ref="dataForm" label-width="80px" v-show="type === 'line'">
|
||||||
|
<el-form-item label="条件">
|
||||||
|
<el-input v-model="line.label"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button icon="el-icon-close">重置</el-button>
|
||||||
|
<el-button type="primary" icon="el-icon-check" @click="saveLine">保存</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
<!-- <div class="el-node-form-tag"></div>-->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { cloneDeep } from 'lodash'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
visible: true,
|
||||||
|
// node 或 line
|
||||||
|
type: 'node',
|
||||||
|
node: {},
|
||||||
|
line: {},
|
||||||
|
data: {},
|
||||||
|
stateList: [{
|
||||||
|
state: 'success',
|
||||||
|
label: '成功'
|
||||||
|
}, {
|
||||||
|
state: 'warning',
|
||||||
|
label: '警告'
|
||||||
|
}, {
|
||||||
|
state: 'error',
|
||||||
|
label: '错误'
|
||||||
|
}, {
|
||||||
|
state: 'running',
|
||||||
|
label: '运行中'
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
/**
|
||||||
|
* 表单修改,这里可以根据传入的ID进行业务信息获取
|
||||||
|
* @param data
|
||||||
|
* @param id
|
||||||
|
*/
|
||||||
|
nodeInit(data, id) {
|
||||||
|
this.type = 'node'
|
||||||
|
this.data = data
|
||||||
|
data.nodeList.filter((node) => {
|
||||||
|
if (node.id === id) {
|
||||||
|
this.node = cloneDeep(node)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
lineInit(line) {
|
||||||
|
this.type = 'line'
|
||||||
|
this.line = line
|
||||||
|
},
|
||||||
|
// 修改连线
|
||||||
|
saveLine() {
|
||||||
|
this.$emit('setLineLabel', this.line.from, this.line.to, this.line.label)
|
||||||
|
},
|
||||||
|
save() {
|
||||||
|
this.data.nodeList.filter((node) => {
|
||||||
|
if (node.id === this.node.id) {
|
||||||
|
node.name = this.node.name
|
||||||
|
node.left = this.node.left
|
||||||
|
node.top = this.node.top
|
||||||
|
node.ico = this.node.ico
|
||||||
|
node.state = this.node.state
|
||||||
|
this.$emit('repaintEverything')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.el-node-form-tag {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
margin-left: -15px;
|
||||||
|
height: 40px;
|
||||||
|
width: 15px;
|
||||||
|
background-color: #fbfbfb;
|
||||||
|
border: 1px solid rgb(220, 227, 232);
|
||||||
|
border-right: none;
|
||||||
|
z-index: 0;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,142 @@
|
||||||
|
<template>
|
||||||
|
<div class="flow-menu" ref="tool">
|
||||||
|
<div v-for="menu in menuList" :key="menu.id">
|
||||||
|
<span class="ef-node-pmenu" @click="menu.open = !menu.open"><i :class="{'el-icon-caret-bottom': menu.open,'el-icon-caret-right': !menu.open}"></i> {{menu.name}}</span>
|
||||||
|
<ul v-show="menu.open" class="ef-node-menu-ul">
|
||||||
|
<draggable @end="end" @start="move" v-model="menu.children" :options="draggableOptions">
|
||||||
|
<li v-for="subMenu in menu.children" class="ef-node-menu-li" :key="subMenu.id" :type="subMenu.type">
|
||||||
|
<i :class="subMenu.ico"></i> {{subMenu.name}}
|
||||||
|
</li>
|
||||||
|
</draggable>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import draggable from 'vuedraggable'
|
||||||
|
|
||||||
|
var mousePosition = {
|
||||||
|
left: -1,
|
||||||
|
top: -1
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
activeNames: '1',
|
||||||
|
// draggable配置参数参考 https://www.cnblogs.com/weixin186/p/10108679.html
|
||||||
|
draggableOptions: {
|
||||||
|
preventOnFilter: false,
|
||||||
|
sort: false,
|
||||||
|
disabled: false,
|
||||||
|
ghostClass: 'tt',
|
||||||
|
// 不使用H5原生的配置
|
||||||
|
forceFallback: true,
|
||||||
|
// 拖拽的时候样式
|
||||||
|
// fallbackClass: 'flow-node-draggable'
|
||||||
|
},
|
||||||
|
// 默认打开的左侧菜单的id
|
||||||
|
defaultOpeneds: ['1', '2'],
|
||||||
|
menuList: [
|
||||||
|
{
|
||||||
|
id: '1',
|
||||||
|
type: 'group',
|
||||||
|
name: '开始节点',
|
||||||
|
ico: 'el-icon-video-play',
|
||||||
|
open: true,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
id: '11',
|
||||||
|
type: 'timer',
|
||||||
|
name: '数据接入',
|
||||||
|
ico: 'el-icon-time',
|
||||||
|
// 自定义覆盖样式
|
||||||
|
style: {}
|
||||||
|
}, {
|
||||||
|
id: '12',
|
||||||
|
type: 'task',
|
||||||
|
name: '接口调用',
|
||||||
|
ico: 'el-icon-odometer',
|
||||||
|
// 自定义覆盖样式
|
||||||
|
style: {}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '2',
|
||||||
|
type: 'group',
|
||||||
|
name: '结束节点',
|
||||||
|
ico: 'el-icon-video-pause',
|
||||||
|
open: true,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
id: '21',
|
||||||
|
type: 'end',
|
||||||
|
name: '流程结束',
|
||||||
|
ico: 'el-icon-caret-right',
|
||||||
|
// 自定义覆盖样式
|
||||||
|
style: {}
|
||||||
|
}, {
|
||||||
|
id: '22',
|
||||||
|
type: 'over',
|
||||||
|
name: '数据清理',
|
||||||
|
ico: 'el-icon-shopping-cart-full',
|
||||||
|
// 自定义覆盖样式
|
||||||
|
style: {}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
nodeMenu: {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
draggable
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
/**
|
||||||
|
* 以下是为了解决在火狐浏览器上推拽时弹出tab页到搜索问题
|
||||||
|
* @param event
|
||||||
|
*/
|
||||||
|
if (this.isFirefox()) {
|
||||||
|
document.body.ondrop = function (event) {
|
||||||
|
// 解决火狐浏览器无法获取鼠标拖拽结束的坐标问题
|
||||||
|
mousePosition.left = event.layerX
|
||||||
|
mousePosition.top = event.clientY - 50
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
// 根据类型获取左侧菜单对象
|
||||||
|
getMenuByType(type) {
|
||||||
|
for (let i = 0; i < this.menuList.length; i++) {
|
||||||
|
let children = this.menuList[i].children;
|
||||||
|
for (let j = 0; j < children.length; j++) {
|
||||||
|
if (children[j].type === type) {
|
||||||
|
return children[j]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 拖拽开始时触发
|
||||||
|
move(evt, a, b, c) {
|
||||||
|
var type = evt.item.attributes.type.nodeValue
|
||||||
|
this.nodeMenu = this.getMenuByType(type)
|
||||||
|
},
|
||||||
|
// 拖拽结束时触发
|
||||||
|
end(evt, e) {
|
||||||
|
this.$emit('addNode', evt, this.nodeMenu, mousePosition)
|
||||||
|
},
|
||||||
|
// 是否是火狐浏览器
|
||||||
|
isFirefox() {
|
||||||
|
var userAgent = navigator.userAgent
|
||||||
|
if (userAgent.indexOf("Firefox") > -1) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -0,0 +1,546 @@
|
||||||
|
<template>
|
||||||
|
<div v-if="easyFlowVisible" style="height: calc(100vh);">
|
||||||
|
<el-row>
|
||||||
|
<!--顶部工具菜单-->
|
||||||
|
<el-col :span="24">
|
||||||
|
<div class="ef-tooltar">
|
||||||
|
<el-link type="primary" :underline="false">{{data.name}}</el-link>
|
||||||
|
<el-divider direction="vertical"></el-divider>
|
||||||
|
<el-button type="text" icon="el-icon-delete" size="large" @click="deleteElement" :disabled="!this.activeElement.type"></el-button>
|
||||||
|
<el-divider direction="vertical"></el-divider>
|
||||||
|
<el-button type="text" icon="el-icon-download" size="large" @click="downloadData"></el-button>
|
||||||
|
<el-divider direction="vertical"></el-divider>
|
||||||
|
<el-button type="text" icon="el-icon-plus" size="large" @click="zoomAdd"></el-button>
|
||||||
|
<el-divider direction="vertical"></el-divider>
|
||||||
|
<el-button type="text" icon="el-icon-minus" size="large" @click="zoomSub"></el-button>
|
||||||
|
<div style="float: right;margin-right: 5px">
|
||||||
|
<el-button type="info" plain round icon="el-icon-document" @click="dataInfo" size="mini">流程信息</el-button>
|
||||||
|
<el-button type="primary" plain round @click="dataReloadA" icon="el-icon-refresh" size="mini">切换流程A</el-button>
|
||||||
|
<el-button type="primary" plain round @click="dataReloadB" icon="el-icon-refresh" size="mini">切换流程B</el-button>
|
||||||
|
<el-button type="primary" plain round @click="dataReloadC" icon="el-icon-refresh" size="mini">切换流程C</el-button>
|
||||||
|
<el-button type="primary" plain round @click="dataReloadD" icon="el-icon-refresh" size="mini">自定义样式</el-button>
|
||||||
|
<el-button type="primary" plain round @click="dataReloadE" icon="el-icon-refresh" size="mini">力导图</el-button>
|
||||||
|
<el-button type="info" plain round icon="el-icon-document" @click="openHelp" size="mini">帮助</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<div style="display: flex;height: calc(100% - 47px);">
|
||||||
|
<div style="width: 230px;border-right: 1px solid #dce3e8;">
|
||||||
|
<node-menu @addNode="addNode" ref="nodeMenu"></node-menu>
|
||||||
|
</div>
|
||||||
|
<div id="efContainer" ref="efContainer" class="container" v-flowDrag>
|
||||||
|
<template v-for="node in data.nodeList">
|
||||||
|
<flow-node
|
||||||
|
:id="node.id"
|
||||||
|
:key="node.id"
|
||||||
|
:node="node"
|
||||||
|
:activeElement="activeElement"
|
||||||
|
@changeNodeSite="changeNodeSite"
|
||||||
|
@nodeRightMenu="nodeRightMenu"
|
||||||
|
@clickNode="clickNode"
|
||||||
|
>
|
||||||
|
</flow-node>
|
||||||
|
</template>
|
||||||
|
<!-- 给画布一个默认的宽度和高度 -->
|
||||||
|
<div style="position:absolute;top: 2000px;left: 2000px;"> </div>
|
||||||
|
</div>
|
||||||
|
<!-- 右侧表单 -->
|
||||||
|
<div style="width: 300px;border-left: 1px solid #dce3e8;background-color: #FBFBFB">
|
||||||
|
<flow-node-form ref="nodeForm" @setLineLabel="setLineLabel" @repaintEverything="repaintEverything"></flow-node-form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- 流程数据详情 -->
|
||||||
|
<flow-info v-if="flowInfoVisible" ref="flowInfo" :data="data"></flow-info>
|
||||||
|
<flow-help v-if="flowHelpVisible" ref="flowHelp"></flow-help>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import draggable from 'vuedraggable'
|
||||||
|
// import { jsPlumb } from 'jsplumb'
|
||||||
|
// 使用修改后的jsplumb
|
||||||
|
import './jsplumb'
|
||||||
|
import { easyFlowMixin } from '@/components/ef/mixins'
|
||||||
|
import flowNode from '@/components/ef/node'
|
||||||
|
import nodeMenu from '@/components/ef/node_menu'
|
||||||
|
import FlowInfo from '@/components/ef/info'
|
||||||
|
import FlowHelp from '@/components/ef/help'
|
||||||
|
import FlowNodeForm from './node_form.vue'
|
||||||
|
import lodash from 'lodash'
|
||||||
|
import { getDataA } from './data_A'
|
||||||
|
import { getDataB } from './data_B'
|
||||||
|
import { getDataC } from './data_C'
|
||||||
|
import { getDataD } from './data_D'
|
||||||
|
import { getDataE } from './data_E'
|
||||||
|
import { ForceDirected } from './force-directed'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
// jsPlumb 实例
|
||||||
|
jsPlumb: null,
|
||||||
|
// 控制画布销毁
|
||||||
|
easyFlowVisible: true,
|
||||||
|
// 控制流程数据显示与隐藏
|
||||||
|
flowInfoVisible: false,
|
||||||
|
// 是否加载完毕标志位
|
||||||
|
loadEasyFlowFinish: false,
|
||||||
|
flowHelpVisible: false,
|
||||||
|
// 数据
|
||||||
|
data: {},
|
||||||
|
// 激活的元素、可能是节点、可能是连线
|
||||||
|
activeElement: {
|
||||||
|
// 可选值 node 、line
|
||||||
|
type: undefined,
|
||||||
|
// 节点ID
|
||||||
|
nodeId: undefined,
|
||||||
|
// 连线ID
|
||||||
|
sourceId: undefined,
|
||||||
|
targetId: undefined
|
||||||
|
},
|
||||||
|
zoom: 0.5
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 一些基础配置移动该文件中
|
||||||
|
mixins: [easyFlowMixin],
|
||||||
|
components: {
|
||||||
|
draggable, flowNode, nodeMenu, FlowInfo, FlowNodeForm, FlowHelp
|
||||||
|
},
|
||||||
|
directives: {
|
||||||
|
'flowDrag': {
|
||||||
|
bind(el, binding, vnode, oldNode) {
|
||||||
|
if (!binding) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
el.onmousedown = (e) => {
|
||||||
|
if (e.button == 2) {
|
||||||
|
// 右键不管
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 鼠标按下,计算当前原始距离可视区的高度
|
||||||
|
let disX = e.clientX
|
||||||
|
let disY = e.clientY
|
||||||
|
el.style.cursor = 'move'
|
||||||
|
|
||||||
|
document.onmousemove = function (e) {
|
||||||
|
// 移动时禁止默认事件
|
||||||
|
e.preventDefault()
|
||||||
|
const left = e.clientX - disX
|
||||||
|
disX = e.clientX
|
||||||
|
el.scrollLeft += -left
|
||||||
|
|
||||||
|
const top = e.clientY - disY
|
||||||
|
disY = e.clientY
|
||||||
|
el.scrollTop += -top
|
||||||
|
}
|
||||||
|
|
||||||
|
document.onmouseup = function (e) {
|
||||||
|
el.style.cursor = 'auto'
|
||||||
|
document.onmousemove = null
|
||||||
|
document.onmouseup = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.jsPlumb = jsPlumb.getInstance()
|
||||||
|
this.$nextTick(() => {
|
||||||
|
// 默认加载流程A的数据、在这里可以根据具体的业务返回符合流程数据格式的数据即可
|
||||||
|
this.dataReload(getDataB())
|
||||||
|
})
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
// 返回唯一标识
|
||||||
|
getUUID() {
|
||||||
|
return Math.random().toString(36).substr(3, 10)
|
||||||
|
},
|
||||||
|
jsPlumbInit() {
|
||||||
|
this.jsPlumb.ready(() => {
|
||||||
|
// 导入默认配置
|
||||||
|
this.jsPlumb.importDefaults(this.jsplumbSetting)
|
||||||
|
// 会使整个jsPlumb立即重绘。
|
||||||
|
this.jsPlumb.setSuspendDrawing(false, true);
|
||||||
|
// 初始化节点
|
||||||
|
this.loadEasyFlow()
|
||||||
|
// 单点击了连接线, https://www.cnblogs.com/ysx215/p/7615677.html
|
||||||
|
this.jsPlumb.bind('click', (conn, originalEvent) => {
|
||||||
|
this.activeElement.type = 'line'
|
||||||
|
this.activeElement.sourceId = conn.sourceId
|
||||||
|
this.activeElement.targetId = conn.targetId
|
||||||
|
this.$refs.nodeForm.lineInit({
|
||||||
|
from: conn.sourceId,
|
||||||
|
to: conn.targetId,
|
||||||
|
label: conn.getLabel()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
// 连线
|
||||||
|
this.jsPlumb.bind("connection", (evt) => {
|
||||||
|
let from = evt.source.id
|
||||||
|
let to = evt.target.id
|
||||||
|
if (this.loadEasyFlowFinish) {
|
||||||
|
this.data.lineList.push({from: from, to: to})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 删除连线回调
|
||||||
|
this.jsPlumb.bind("connectionDetached", (evt) => {
|
||||||
|
this.deleteLine(evt.sourceId, evt.targetId)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 改变线的连接节点
|
||||||
|
this.jsPlumb.bind("connectionMoved", (evt) => {
|
||||||
|
this.changeLine(evt.originalSourceId, evt.originalTargetId)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 连线右击
|
||||||
|
this.jsPlumb.bind("contextmenu", (evt) => {
|
||||||
|
console.log('contextmenu', evt)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 连线
|
||||||
|
this.jsPlumb.bind("beforeDrop", (evt) => {
|
||||||
|
let from = evt.sourceId
|
||||||
|
let to = evt.targetId
|
||||||
|
if (from === to) {
|
||||||
|
this.$message.error('节点不支持连接自己')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (this.hasLine(from, to)) {
|
||||||
|
this.$message.error('该关系已存在,不允许重复创建')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (this.hashOppositeLine(from, to)) {
|
||||||
|
this.$message.error('不支持两个节点之间连线回环');
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
this.$message.success('连接成功')
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
|
// beforeDetach
|
||||||
|
this.jsPlumb.bind("beforeDetach", (evt) => {
|
||||||
|
console.log('beforeDetach', evt)
|
||||||
|
})
|
||||||
|
this.jsPlumb.setContainer(this.$refs.efContainer)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
// 加载流程图
|
||||||
|
loadEasyFlow() {
|
||||||
|
// 初始化节点
|
||||||
|
for (var i = 0; i < this.data.nodeList.length; i++) {
|
||||||
|
let node = this.data.nodeList[i]
|
||||||
|
// 设置源点,可以拖出线连接其他节点
|
||||||
|
this.jsPlumb.makeSource(node.id, lodash.merge(this.jsplumbSourceOptions, {}))
|
||||||
|
// // 设置目标点,其他源点拖出的线可以连接该节点
|
||||||
|
this.jsPlumb.makeTarget(node.id, this.jsplumbTargetOptions)
|
||||||
|
if (!node.viewOnly) {
|
||||||
|
this.jsPlumb.draggable(node.id, {
|
||||||
|
containment: 'parent',
|
||||||
|
stop: function (el) {
|
||||||
|
// 拖拽节点结束后的对调
|
||||||
|
console.log('拖拽结束: ', el)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 初始化连线
|
||||||
|
for (var i = 0; i < this.data.lineList.length; i++) {
|
||||||
|
let line = this.data.lineList[i]
|
||||||
|
var connParam = {
|
||||||
|
source: line.from,
|
||||||
|
target: line.to,
|
||||||
|
label: line.label ? line.label : '',
|
||||||
|
connector: line.connector ? line.connector : '',
|
||||||
|
anchors: line.anchors ? line.anchors : undefined,
|
||||||
|
paintStyle: line.paintStyle ? line.paintStyle : undefined,
|
||||||
|
}
|
||||||
|
this.jsPlumb.connect(connParam, this.jsplumbConnectOptions)
|
||||||
|
}
|
||||||
|
this.$nextTick(function () {
|
||||||
|
this.loadEasyFlowFinish = true
|
||||||
|
})
|
||||||
|
},
|
||||||
|
// 设置连线条件
|
||||||
|
setLineLabel(from, to, label) {
|
||||||
|
var conn = this.jsPlumb.getConnections({
|
||||||
|
source: from,
|
||||||
|
target: to
|
||||||
|
})[0]
|
||||||
|
if (!label || label === '') {
|
||||||
|
conn.removeClass('flowLabel')
|
||||||
|
conn.addClass('emptyFlowLabel')
|
||||||
|
} else {
|
||||||
|
conn.addClass('flowLabel')
|
||||||
|
}
|
||||||
|
conn.setLabel({
|
||||||
|
label: label,
|
||||||
|
})
|
||||||
|
this.data.lineList.forEach(function (line) {
|
||||||
|
if (line.from == from && line.to == to) {
|
||||||
|
line.label = label
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
},
|
||||||
|
// 删除激活的元素
|
||||||
|
deleteElement() {
|
||||||
|
if (this.activeElement.type === 'node') {
|
||||||
|
this.deleteNode(this.activeElement.nodeId)
|
||||||
|
} else if (this.activeElement.type === 'line') {
|
||||||
|
this.$confirm('确定删除所点击的线吗?', '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
}).then(() => {
|
||||||
|
var conn = this.jsPlumb.getConnections({
|
||||||
|
source: this.activeElement.sourceId,
|
||||||
|
target: this.activeElement.targetId
|
||||||
|
})[0]
|
||||||
|
this.jsPlumb.deleteConnection(conn)
|
||||||
|
}).catch(() => {
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 删除线
|
||||||
|
deleteLine(from, to) {
|
||||||
|
this.data.lineList = this.data.lineList.filter(function (line) {
|
||||||
|
if (line.from == from && line.to == to) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
},
|
||||||
|
// 改变连线
|
||||||
|
changeLine(oldFrom, oldTo) {
|
||||||
|
this.deleteLine(oldFrom, oldTo)
|
||||||
|
},
|
||||||
|
// 改变节点的位置
|
||||||
|
changeNodeSite(data) {
|
||||||
|
for (var i = 0; i < this.data.nodeList.length; i++) {
|
||||||
|
let node = this.data.nodeList[i]
|
||||||
|
if (node.id === data.nodeId) {
|
||||||
|
node.left = data.left
|
||||||
|
node.top = data.top
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 拖拽结束后添加新的节点
|
||||||
|
* @param evt
|
||||||
|
* @param nodeMenu 被添加的节点对象
|
||||||
|
* @param mousePosition 鼠标拖拽结束的坐标
|
||||||
|
*/
|
||||||
|
addNode(evt, nodeMenu, mousePosition) {
|
||||||
|
var screenX = evt.originalEvent.clientX, screenY = evt.originalEvent.clientY
|
||||||
|
let efContainer = this.$refs.efContainer
|
||||||
|
var containerRect = efContainer.getBoundingClientRect()
|
||||||
|
var left = screenX, top = screenY
|
||||||
|
// 计算是否拖入到容器中
|
||||||
|
if (left < containerRect.x || left > containerRect.width + containerRect.x || top < containerRect.y || containerRect.y > containerRect.y + containerRect.height) {
|
||||||
|
this.$message.error("请把节点拖入到画布中")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
left = left - containerRect.x + efContainer.scrollLeft
|
||||||
|
top = top - containerRect.y + efContainer.scrollTop
|
||||||
|
// 居中
|
||||||
|
left -= 85
|
||||||
|
top -= 16
|
||||||
|
var nodeId = this.getUUID()
|
||||||
|
// 动态生成名字
|
||||||
|
var origName = nodeMenu.name
|
||||||
|
var nodeName = origName
|
||||||
|
var index = 1
|
||||||
|
while (index < 10000) {
|
||||||
|
var repeat = false
|
||||||
|
for (var i = 0; i < this.data.nodeList.length; i++) {
|
||||||
|
let node = this.data.nodeList[i]
|
||||||
|
if (node.name === nodeName) {
|
||||||
|
nodeName = origName + index
|
||||||
|
repeat = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (repeat) {
|
||||||
|
index++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
var node = {
|
||||||
|
id: nodeId,
|
||||||
|
name: nodeName,
|
||||||
|
type: nodeMenu.type,
|
||||||
|
left: left + 'px',
|
||||||
|
top: top + 'px',
|
||||||
|
ico: nodeMenu.ico,
|
||||||
|
state: 'success'
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 这里可以进行业务判断、是否能够添加该节点
|
||||||
|
*/
|
||||||
|
this.data.nodeList.push(node)
|
||||||
|
this.$nextTick(function () {
|
||||||
|
this.jsPlumb.makeSource(nodeId, this.jsplumbSourceOptions)
|
||||||
|
this.jsPlumb.makeTarget(nodeId, this.jsplumbTargetOptions)
|
||||||
|
this.jsPlumb.draggable(nodeId, {
|
||||||
|
containment: 'parent',
|
||||||
|
stop: function (el) {
|
||||||
|
// 拖拽节点结束后的对调
|
||||||
|
console.log('拖拽结束: ', el)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 删除节点
|
||||||
|
* @param nodeId 被删除节点的ID
|
||||||
|
*/
|
||||||
|
deleteNode(nodeId) {
|
||||||
|
this.$confirm('确定要删除节点' + nodeId + '?', '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning',
|
||||||
|
closeOnClickModal: false
|
||||||
|
}).then(() => {
|
||||||
|
/**
|
||||||
|
* 这里需要进行业务判断,是否可以删除
|
||||||
|
*/
|
||||||
|
this.data.nodeList = this.data.nodeList.filter(function (node) {
|
||||||
|
if (node.id === nodeId) {
|
||||||
|
// 伪删除,将节点隐藏,否则会导致位置错位
|
||||||
|
// node.show = false
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
this.$nextTick(function () {
|
||||||
|
this.jsPlumb.removeAllEndpoints(nodeId);
|
||||||
|
})
|
||||||
|
}).catch(() => {
|
||||||
|
})
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
clickNode(nodeId) {
|
||||||
|
this.activeElement.type = 'node'
|
||||||
|
this.activeElement.nodeId = nodeId
|
||||||
|
this.$refs.nodeForm.nodeInit(this.data, nodeId)
|
||||||
|
},
|
||||||
|
// 是否具有该线
|
||||||
|
hasLine(from, to) {
|
||||||
|
for (var i = 0; i < this.data.lineList.length; i++) {
|
||||||
|
var line = this.data.lineList[i]
|
||||||
|
if (line.from === from && line.to === to) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
},
|
||||||
|
// 是否含有相反的线
|
||||||
|
hashOppositeLine(from, to) {
|
||||||
|
return this.hasLine(to, from)
|
||||||
|
},
|
||||||
|
nodeRightMenu(nodeId, evt) {
|
||||||
|
this.menu.show = true
|
||||||
|
this.menu.curNodeId = nodeId
|
||||||
|
this.menu.left = evt.x + 'px'
|
||||||
|
this.menu.top = evt.y + 'px'
|
||||||
|
},
|
||||||
|
repaintEverything() {
|
||||||
|
this.jsPlumb.repaint()
|
||||||
|
},
|
||||||
|
// 流程数据信息
|
||||||
|
dataInfo() {
|
||||||
|
this.flowInfoVisible = true
|
||||||
|
this.$nextTick(function () {
|
||||||
|
this.$refs.flowInfo.init()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
// 加载流程图
|
||||||
|
dataReload(data) {
|
||||||
|
this.easyFlowVisible = false
|
||||||
|
this.data.nodeList = []
|
||||||
|
this.data.lineList = []
|
||||||
|
this.$nextTick(() => {
|
||||||
|
data = lodash.cloneDeep(data)
|
||||||
|
this.easyFlowVisible = true
|
||||||
|
this.data = data
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.jsPlumb = jsPlumb.getInstance()
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.jsPlumbInit()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
// 模拟载入数据dataA
|
||||||
|
dataReloadA() {
|
||||||
|
this.dataReload(getDataA())
|
||||||
|
},
|
||||||
|
// 模拟载入数据dataB
|
||||||
|
dataReloadB() {
|
||||||
|
this.dataReload(getDataB())
|
||||||
|
},
|
||||||
|
// 模拟载入数据dataC
|
||||||
|
dataReloadC() {
|
||||||
|
this.dataReload(getDataC())
|
||||||
|
},
|
||||||
|
// 模拟载入数据dataD
|
||||||
|
dataReloadD() {
|
||||||
|
this.dataReload(getDataD())
|
||||||
|
},
|
||||||
|
// 模拟加载数据dataE,自适应创建坐标
|
||||||
|
dataReloadE() {
|
||||||
|
let dataE = getDataE()
|
||||||
|
let tempData = lodash.cloneDeep(dataE)
|
||||||
|
let data = ForceDirected(tempData)
|
||||||
|
this.dataReload(data)
|
||||||
|
this.$message({
|
||||||
|
message: '力导图每次产生的布局是不一样的',
|
||||||
|
type: 'warning'
|
||||||
|
});
|
||||||
|
},
|
||||||
|
zoomAdd() {
|
||||||
|
if (this.zoom >= 1) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.zoom = this.zoom + 0.1
|
||||||
|
this.$refs.efContainer.style.transform = `scale(${this.zoom})`
|
||||||
|
this.jsPlumb.setZoom(this.zoom)
|
||||||
|
},
|
||||||
|
zoomSub() {
|
||||||
|
if (this.zoom <= 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.zoom = this.zoom - 0.1
|
||||||
|
this.$refs.efContainer.style.transform = `scale(${this.zoom})`
|
||||||
|
this.jsPlumb.setZoom(this.zoom)
|
||||||
|
},
|
||||||
|
// 下载数据
|
||||||
|
downloadData() {
|
||||||
|
this.$confirm('确定要下载该流程数据吗?', '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning',
|
||||||
|
closeOnClickModal: false
|
||||||
|
}).then(() => {
|
||||||
|
var datastr = "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(this.data, null, '\t'));
|
||||||
|
var downloadAnchorNode = document.createElement('a')
|
||||||
|
downloadAnchorNode.setAttribute("href", datastr);
|
||||||
|
downloadAnchorNode.setAttribute("download", 'data.json')
|
||||||
|
downloadAnchorNode.click();
|
||||||
|
downloadAnchorNode.remove();
|
||||||
|
this.$message.success("正在下载中,请稍后...")
|
||||||
|
}).catch(() => {
|
||||||
|
})
|
||||||
|
},
|
||||||
|
openHelp() {
|
||||||
|
this.flowHelpVisible = true
|
||||||
|
this.$nextTick(function () {
|
||||||
|
this.$refs.flowHelp.init()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -0,0 +1,29 @@
|
||||||
|
// 是否具有该线
|
||||||
|
export function hasLine(data, from, to) {
|
||||||
|
for (let i = 0; i < data.lineList.length; i++) {
|
||||||
|
let line = data.lineList[i]
|
||||||
|
if (line.from === from && line.to === to) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 是否含有相反的线
|
||||||
|
export function hashOppositeLine(data, from, to) {
|
||||||
|
return hasLine(data, to, from)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取连线
|
||||||
|
export function getConnector(jsp, from, to) {
|
||||||
|
let connection = jsp.getConnections({
|
||||||
|
source: from,
|
||||||
|
target: to
|
||||||
|
})[0]
|
||||||
|
return connection
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取唯一标识
|
||||||
|
export function uuid() {
|
||||||
|
return Math.random().toString(36).substr(3, 10)
|
||||||
|
}
|
|
@ -1,5 +1,10 @@
|
||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
|
|
||||||
|
import ElementUI from 'element-ui'
|
||||||
|
import 'element-ui/lib/theme-chalk/index.css'
|
||||||
|
import '@/components/ef/index.css'
|
||||||
|
Vue.use(ElementUI, {size: 'small'})
|
||||||
|
|
||||||
import Cookies from 'js-cookie'
|
import Cookies from 'js-cookie'
|
||||||
|
|
||||||
import Element from 'element-ui'
|
import Element from 'element-ui'
|
||||||
|
|
|
@ -45,6 +45,11 @@ export const constantRoutes = [
|
||||||
component: () => import('@/views/login'),
|
component: () => import('@/views/login'),
|
||||||
hidden: true
|
hidden: true
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/easyFlow',
|
||||||
|
component: () => import('@/components/ef/panel'),
|
||||||
|
hidden: true
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/register',
|
path: '/register',
|
||||||
component: () => import('@/views/register'),
|
component: () => import('@/views/register'),
|
||||||
|
|
|
@ -1,309 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="app-container">
|
|
||||||
<el-form v-show="showSearch" ref="queryForm" :inline="true" :model="queryParams" label-width="68px" size="small">
|
|
||||||
<el-form-item label="数据来源名称" prop="name">
|
|
||||||
<el-input
|
|
||||||
v-model="queryParams.name"
|
|
||||||
clearable
|
|
||||||
placeholder="请输入数据来源名称"
|
|
||||||
@keyup.enter.native="handleQuery"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="存放数据库名称" prop="databaseName">
|
|
||||||
<el-input
|
|
||||||
v-model="queryParams.databaseName"
|
|
||||||
clearable
|
|
||||||
placeholder="请输入存放数据库名称"
|
|
||||||
@keyup.enter.native="handleQuery"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item>
|
|
||||||
<el-button icon="el-icon-search" size="mini" type="primary" @click="handleQuery">搜索</el-button>
|
|
||||||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
|
|
||||||
<el-row :gutter="10" class="mb8">
|
|
||||||
<el-col :span="1.5">
|
|
||||||
<el-button
|
|
||||||
v-hasPermi="['system:post:add']"
|
|
||||||
icon="el-icon-plus"
|
|
||||||
plain
|
|
||||||
size="mini"
|
|
||||||
type="primary"
|
|
||||||
@click="handleAdd"
|
|
||||||
>新增
|
|
||||||
</el-button>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="1.5">
|
|
||||||
<el-button
|
|
||||||
v-hasPermi="['system:post:edit']"
|
|
||||||
:disabled="single"
|
|
||||||
icon="el-icon-edit"
|
|
||||||
plain
|
|
||||||
size="mini"
|
|
||||||
type="success"
|
|
||||||
@click="handleUpdate"
|
|
||||||
>修改
|
|
||||||
</el-button>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="1.5">
|
|
||||||
<el-button
|
|
||||||
v-hasPermi="['system:post:remove']"
|
|
||||||
:disabled="multiple"
|
|
||||||
icon="el-icon-delete"
|
|
||||||
plain
|
|
||||||
size="mini"
|
|
||||||
type="danger"
|
|
||||||
@click="handleDelete"
|
|
||||||
>删除
|
|
||||||
</el-button>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="1.5">
|
|
||||||
<el-button
|
|
||||||
v-hasPermi="['system:post:export']"
|
|
||||||
icon="el-icon-download"
|
|
||||||
plain
|
|
||||||
size="mini"
|
|
||||||
type="warning"
|
|
||||||
@click="handleExport"
|
|
||||||
>导出
|
|
||||||
</el-button>
|
|
||||||
</el-col>
|
|
||||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
|
||||||
</el-row>
|
|
||||||
|
|
||||||
<el-table v-loading="loading" :data="dataList" @selection-change="handleSelectionChange">
|
|
||||||
<el-table-column align="center" type="selection" width="55"/>
|
|
||||||
<el-table-column align="name" label="数据来源名称" prop="postId"/>
|
|
||||||
<el-table-column align="url" label="数据来源地址" prop="postCode"/>
|
|
||||||
<el-table-column align="port" label="来源地址端口号" prop="postName"/>
|
|
||||||
<el-table-column align="dataTypeId" label="数据来源类型" prop="postName"/>
|
|
||||||
<el-table-column align="databaseName" label="存放数据库名称" prop="postSort"/>
|
|
||||||
<el-table-column align="createTime" label="存放数据库名称" prop="postSort"/>
|
|
||||||
<el-table-column align="center" label="状态" prop="status">
|
|
||||||
<template slot-scope="scope">
|
|
||||||
<dict-tag :options="dict.type.sys_normal_disable" :value="scope.row.status"/>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column align="center" label="创建时间" prop="createTime" width="180">
|
|
||||||
<template slot-scope="scope">
|
|
||||||
<span>{{ parseTime(scope.row.createTime) }}</span>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column align="center" class-name="small-padding fixed-width" label="操作">
|
|
||||||
<template slot-scope="scope">
|
|
||||||
<el-button
|
|
||||||
v-hasPermi="['system:post:edit']"
|
|
||||||
icon="el-icon-edit"
|
|
||||||
size="mini"
|
|
||||||
type="text"
|
|
||||||
@click="handleUpdate(scope.row)"
|
|
||||||
>修改
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
v-hasPermi="['system:post:remove']"
|
|
||||||
icon="el-icon-delete"
|
|
||||||
size="mini"
|
|
||||||
type="text"
|
|
||||||
@click="handleDelete(scope.row)"
|
|
||||||
>删除
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
|
|
||||||
<pagination
|
|
||||||
v-show="total>0"
|
|
||||||
:limit.sync="queryParams.pageSize"
|
|
||||||
:page.sync="queryParams.pageNum"
|
|
||||||
:total="total"
|
|
||||||
@pagination="getList"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- 添加或修改岗位对话框 -->
|
|
||||||
<el-dialog :title="title" :visible.sync="open" append-to-body width="500px">
|
|
||||||
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
|
|
||||||
<el-form-item label="岗位名称" prop="postName">
|
|
||||||
<el-input v-model="form.postName" placeholder="请输入岗位名称"/>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="岗位编码" prop="postCode">
|
|
||||||
<el-input v-model="form.postCode" placeholder="请输入编码名称"/>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="岗位顺序" prop="postSort">
|
|
||||||
<el-input-number v-model="form.postSort" :min="0" controls-position="right"/>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="岗位状态" prop="status">
|
|
||||||
<el-radio-group v-model="form.status">
|
|
||||||
<el-radio
|
|
||||||
v-for="dict in dict.type.sys_normal_disable"
|
|
||||||
:key="dict.value"
|
|
||||||
:label="dict.value"
|
|
||||||
>{{ dict.label }}
|
|
||||||
</el-radio>
|
|
||||||
</el-radio-group>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="备注" prop="remark">
|
|
||||||
<el-input v-model="form.remark" placeholder="请输入内容" type="textarea"/>
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
<div slot="footer" class="dialog-footer">
|
|
||||||
<el-button type="primary" @click="submitForm">确 定</el-button>
|
|
||||||
<el-button @click="cancel">取 消</el-button>
|
|
||||||
</div>
|
|
||||||
</el-dialog>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import {addPost, delPost, getPost, listPost, updatePost} from "@/api/system/post";
|
|
||||||
import {getList} from "@/api/dataSources/dataSources";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: "DataSources",
|
|
||||||
dicts: ['sys_normal_disable'],
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
// 遮罩层
|
|
||||||
loading: true,
|
|
||||||
// 选中数组
|
|
||||||
ids: [],
|
|
||||||
// 非单个禁用
|
|
||||||
single: true,
|
|
||||||
// 非多个禁用
|
|
||||||
multiple: true,
|
|
||||||
// 显示搜索条件
|
|
||||||
showSearch: true,
|
|
||||||
// 总条数
|
|
||||||
total: 0,
|
|
||||||
// 岗位表格数据
|
|
||||||
dataList: [],
|
|
||||||
// 弹出层标题
|
|
||||||
title: "",
|
|
||||||
// 是否显示弹出层
|
|
||||||
open: false,
|
|
||||||
// 查询参数
|
|
||||||
queryParams: {
|
|
||||||
pageNum: 1,
|
|
||||||
pageSize: 10,
|
|
||||||
name: undefined,
|
|
||||||
databaseName: undefined
|
|
||||||
},
|
|
||||||
// 表单参数
|
|
||||||
form: {},
|
|
||||||
// 表单校验
|
|
||||||
rules: {
|
|
||||||
pageNum: [
|
|
||||||
{required: true, message: "岗位名称不能为空", trigger: "blur"}
|
|
||||||
],
|
|
||||||
pageSize: [
|
|
||||||
{required: true, message: "岗位编码不能为空", trigger: "blur"}
|
|
||||||
],
|
|
||||||
postSort: [
|
|
||||||
{required: true, message: "岗位顺序不能为空", trigger: "blur"}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
};
|
|
||||||
},
|
|
||||||
created() {
|
|
||||||
this.getList();
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
/** 查询岗位列表 */
|
|
||||||
getList() {
|
|
||||||
this.loading = true;
|
|
||||||
getList(this.queryParams).then(response => {
|
|
||||||
this.dataList = response.data.records;
|
|
||||||
this.total = response.data.total;
|
|
||||||
this.loading = false;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
// 取消按钮
|
|
||||||
cancel() {
|
|
||||||
this.open = false;
|
|
||||||
this.reset();
|
|
||||||
},
|
|
||||||
// 表单重置
|
|
||||||
reset() {
|
|
||||||
this.form = {
|
|
||||||
postId: undefined,
|
|
||||||
postCode: undefined,
|
|
||||||
postName: undefined,
|
|
||||||
postSort: 0,
|
|
||||||
status: "0",
|
|
||||||
remark: undefined
|
|
||||||
};
|
|
||||||
this.resetForm("form");
|
|
||||||
},
|
|
||||||
/** 搜索按钮操作 */
|
|
||||||
handleQuery() {
|
|
||||||
this.queryParams.pageNum = 1;
|
|
||||||
this.getList();
|
|
||||||
},
|
|
||||||
/** 重置按钮操作 */
|
|
||||||
resetQuery() {
|
|
||||||
this.resetForm("queryForm");
|
|
||||||
this.handleQuery();
|
|
||||||
},
|
|
||||||
// 多选框选中数据
|
|
||||||
handleSelectionChange(selection) {
|
|
||||||
this.ids = selection.map(item => item.postId)
|
|
||||||
this.single = selection.length != 1
|
|
||||||
this.multiple = !selection.length
|
|
||||||
},
|
|
||||||
/** 新增按钮操作 */
|
|
||||||
handleAdd() {
|
|
||||||
this.reset();
|
|
||||||
this.open = true;
|
|
||||||
this.title = "添加岗位";
|
|
||||||
},
|
|
||||||
/** 修改按钮操作 */
|
|
||||||
handleUpdate(row) {
|
|
||||||
this.reset();
|
|
||||||
const postId = row.postId || this.ids
|
|
||||||
getPost(postId).then(response => {
|
|
||||||
this.form = response.data;
|
|
||||||
this.open = true;
|
|
||||||
this.title = "修改岗位";
|
|
||||||
});
|
|
||||||
},
|
|
||||||
/** 提交按钮 */
|
|
||||||
submitForm: function () {
|
|
||||||
this.$refs["form"].validate(valid => {
|
|
||||||
if (valid) {
|
|
||||||
if (this.form.postId != undefined) {
|
|
||||||
updatePost(this.form).then(response => {
|
|
||||||
this.$modal.msgSuccess("修改成功");
|
|
||||||
this.open = false;
|
|
||||||
this.getList();
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
addPost(this.form).then(response => {
|
|
||||||
this.$modal.msgSuccess("新增成功");
|
|
||||||
this.open = false;
|
|
||||||
this.getList();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
/** 删除按钮操作 */
|
|
||||||
handleDelete(row) {
|
|
||||||
const postIds = row.postId || this.ids;
|
|
||||||
this.$modal.confirm('是否确认删除岗位编号为"' + postIds + '"的数据项?').then(function () {
|
|
||||||
return delPost(postIds);
|
|
||||||
}).then(() => {
|
|
||||||
this.getList();
|
|
||||||
this.$modal.msgSuccess("删除成功");
|
|
||||||
}).catch(() => {
|
|
||||||
});
|
|
||||||
},
|
|
||||||
/** 导出按钮操作 */
|
|
||||||
handleExport() {
|
|
||||||
this.download('system/post/export', {
|
|
||||||
...this.queryParams
|
|
||||||
}, `post_${new Date().getTime()}.xlsx`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
|
@ -0,0 +1,183 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<h1 style="color: #00afff" align="center">发布任务</h1>
|
||||||
|
<el-table :data="tableData" style="width: 100%">
|
||||||
|
<el-table-column label="编号" width="180">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span style="margin-left: 10px">{{ scope.row.id }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="任务名称" width="180">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span style="margin-left: 10px">{{ scope.row.name }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="任务权重级别" width="180">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span style="margin-left: 10px">{{ scope.row.weight }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="任务执行状态" width="180">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span style="margin-left: 10px">{{ scope.row.processTotal }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="最终执行结果" width="180">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span style="margin-left: 10px">{{ scope.row.status }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="总处理条数" width="180">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span style="margin-left: 10px">{{ scope.row.total }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="创建人" width="180">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span style="margin-left: 10px">{{ scope.row.createBy }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="创建时间" width="180">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span style="margin-left: 10px">{{ scope.row.createTime }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="修改人" width="180">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span style="margin-left: 10px">{{ scope.row.updateBy }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="修改时间" width="180">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span style="margin-left: 10px">{{ scope.row.updateTime }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
|
||||||
|
<el-table-column label="操作">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-button
|
||||||
|
size="mini"
|
||||||
|
@click="handleEdit(scope.$index, scope.row)">编辑</el-button>
|
||||||
|
<el-button
|
||||||
|
size="mini"
|
||||||
|
type="danger"
|
||||||
|
@click="handleDelete(scope.$index, scope.row)">删除</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<el-button @click="toAdd">添加任务</el-button>
|
||||||
|
<el-dialog
|
||||||
|
title="提示"
|
||||||
|
:visible.sync="dialogVisible"
|
||||||
|
width="30%"
|
||||||
|
>
|
||||||
|
<el-radio-group v-model="labelPosition" size="small">
|
||||||
|
<el-radio-button label="left">左对齐</el-radio-button>
|
||||||
|
<el-radio-button label="right">右对齐</el-radio-button>
|
||||||
|
<el-radio-button label="top">顶部对齐</el-radio-button>
|
||||||
|
</el-radio-group>
|
||||||
|
<div style="margin: 20px;"></div>
|
||||||
|
<el-form :label-position="labelPosition" label-width="80px" :model="formLabelAlign">
|
||||||
|
<el-form-item label="任务名称">
|
||||||
|
<el-input v-model="formLabelAlign.name"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="任务权重">
|
||||||
|
<el-input v-model="formLabelAlign.weight"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<span slot="footer" class="dialog-footer">
|
||||||
|
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||||
|
<el-button type="primary" @click="doAdd()">确 定</el-button>
|
||||||
|
</span>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
//这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等),
|
||||||
|
//例如:import 《组件名称》 from '《组件路径》,
|
||||||
|
import {addTask, delTask, showTask} from "../../../api/etl/etl";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "Task",
|
||||||
|
//import引入的组件需要注入到对象中才能使用"
|
||||||
|
components: {},
|
||||||
|
props: {},
|
||||||
|
data() {
|
||||||
|
//这里存放数据"
|
||||||
|
|
||||||
|
return {
|
||||||
|
formLabelAlign:{},
|
||||||
|
labelPosition: 'right',
|
||||||
|
tableData:[],
|
||||||
|
task:{},
|
||||||
|
dialogVisible: false
|
||||||
|
};
|
||||||
|
},
|
||||||
|
//计算属性 类似于data概念",
|
||||||
|
computed: {},
|
||||||
|
//监控data中的数据变化",
|
||||||
|
watch: {},
|
||||||
|
//方法集合",
|
||||||
|
methods: {
|
||||||
|
toAdd(){
|
||||||
|
this.dialogVisible = true
|
||||||
|
},
|
||||||
|
doAdd(){
|
||||||
|
this.dialogVisible = false
|
||||||
|
addTask(this.task).then(res =>{
|
||||||
|
if (res.code == 200){
|
||||||
|
this.$message.success("添加任务成功")
|
||||||
|
location.reload()
|
||||||
|
}else{
|
||||||
|
this.$message.error("异常")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
getData(){
|
||||||
|
showTask().then(res => {
|
||||||
|
this.tableData = res.data
|
||||||
|
})
|
||||||
|
},
|
||||||
|
handleEdit(index, row) {
|
||||||
|
console.log(index, row);
|
||||||
|
},
|
||||||
|
handleDelete(index, row) {
|
||||||
|
console.log(index, row);
|
||||||
|
delTask(row.id).then(res =>{
|
||||||
|
if (res.code == 200){
|
||||||
|
this.$message.success(res.msg)
|
||||||
|
location.reload()
|
||||||
|
}else{
|
||||||
|
this.$message.error(res.msg)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
//生命周期 - 创建完成(可以访问当前this实例)",
|
||||||
|
created() {
|
||||||
|
this.getData()
|
||||||
|
},
|
||||||
|
//生命周期 - 挂载完成(可以访问DOM元素)",
|
||||||
|
mounted() {
|
||||||
|
},
|
||||||
|
beforeCreate() {
|
||||||
|
}, //生命周期 - 创建之前",
|
||||||
|
beforeMount() {
|
||||||
|
}, //生命周期 - 挂载之前",
|
||||||
|
beforeUpdate() {
|
||||||
|
}, //生命周期 - 更新之前",
|
||||||
|
updated() {
|
||||||
|
}, //生命周期 - 更新之后",
|
||||||
|
beforeDestroy() {
|
||||||
|
}, //生命周期 - 销毁之前",
|
||||||
|
destroyed() {
|
||||||
|
}, //生命周期 - 销毁完成",
|
||||||
|
activated() {
|
||||||
|
} //如果页面有keep-alive缓存功能,这个函数会触发",
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
Loading…
Reference in New Issue