cloud-web/src/views/etl/task/taskInfo.vue

633 lines
18 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<div class="dashboard-container">
<el-button @click="saveActive" icon="el-icon-position" style="margin-right: 20px;float :right">保存</el-button>
<p>选择节点</p>
<div class="antvBox">
<div class="menu-list">
<div v-for="item in moduleList" :key="item.id"
draggable="true"
@dragend="handleDragEnd($event, item)">
<img :src="item.image" alt="" />
<p>{{ item.name }}</p>
</div>
</div>
<div class="canvas-card">
<div id="container" />
</div>
</div>
<el-dialog title="提示" :visible.sync="dialogVisible" width="30%">
<task v-if="taskOpen" @taskInputForm="findFormValue" :nodes-ids="nodesIds"/>
</el-dialog>
<el-dialog title="提示" :visible.sync="dialogVisible1" width="30%">
<el-table :data="nodeList" style="width: 100%">
<el-table-column prop="databaseId" label="数据库" width="180"></el-table-column>
<el-table-column prop="tableAsName" label="表别名" width="180"></el-table-column>
</el-table>
<div style="margin: 20px;"></div>
<el-form label-width="80px" :model="joinCheck">
<el-form-item label="联查方式">
<el-select v-model="joinCheck.joinId" placeholder="联查方式">
<el-option label="左联查" value="1"></el-option>
<el-option label="右联查" value="2"></el-option>
<el-option label="内联查" value="3"></el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-select v-model="joinCheck.firstCloumn" placeholder="请选择关联字段">
<el-option
v-for="item in options1"
:key="item.nodeMsg"
:label="item.nodeMsg"
:value="item.nodeMsg">
</el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-select v-model="joinCheck.secondCloumn" placeholder="请选择关联字段">
<el-option
v-for="item in options2"
:key="item.nodeMsg"
:label="item.nodeMsg"
:value="item.nodeMsg">
</el-option>
</el-select>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible1 = false">取 消</el-button>
<el-button type="primary" @click="todoSelect">确 定</el-button>
</span>
</el-dialog>
<el-dialog
title="提示"
:visible.sync="dialogVisible2"
width="40%"
>
<el-table ref="multipleTable" :data="fields" tooltip-effect="dark" style="width: 100%"
@selection-change="SelectionChange">
<el-table-column type="selection" width="55"/>
<el-table-column label="字段" width="120"><template slot-scope="scope">{{ scope.row.field }}</template></el-table-column>
<el-table-column label="别名" width="120"><template slot-scope="scope">{{ scope.row.asField }}</template></el-table-column>
<el-table-column label="非空" width="120"><template slot-scope="scope">{{ scope.row.isNull }}</template></el-table-column>
<el-table-column label="注释" width="120"><template slot-scope="scope">{{ scope.row.comment }}</template></el-table-column>
</el-table>
<br>
<el-cascader
v-model="toTable"
:options="tt"
:props="{ expandTrigger: 'hover',value:'name',label:'name',children:'tableNames' }"
@change="handleChange"></el-cascader>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible2 = false">取 消</el-button>
<el-button type="primary" @click="showTableFields">确 定</el-button>
</span>
</el-dialog>
<el-dialog title="提示" :visible.sync="finalVisible" width="30%">
<el-table ref="multipleTable" :data="sqlList" tooltip-effect="dark" style="width: 100%"
@selection-change="SelectionChange">
<el-table-column label="输入字段" width="120">
<template slot-scope="scope">
{{ scope.row.asField }}
</template>
</el-table-column>
<el-table-column label="输出字段" width="120">
<el-select v-model="cloumns" placeholder="请选择">
<el-option
v-for="item in options"
:key="item.id"
:label="item.field"
:value="item.field">
</el-option>
</el-select>
</el-table-column>
</el-table>
<span slot="footer" class="dialog-footer">
<el-button @click="finalVisible = false">取 消</el-button>
<el-button type="primary" @click="addSql"> </el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import { Graph } from "@antv/x6";
import {
addOutPut,
addSelect,
extractDataName, findBySelectId,
seeTableField, selectByAsField,
selectByNodeId,
selectByOne, selectByTableName,
} from "../../../api/etl/etl";
import task from "/src/views/components/task/index.vue"
export default {
name: "antvX6",
components:{
task
},
data() {
return {
sourceField:[],
outPuter:{
nodeId:"",
taskId:"",
tableFields:"",
tableAsFields:"",
targetDatabase:"",
targetTableName:""
},
options:{},
sqlList:[],
toTable:"",
tt:{},
fields:[],
finalVisible:false,
dialogVisible2:false,
options1:{},
options2:{},
nodeList:[],
neighbors:[],
nodesIds:"",
taskOpen:false,
joinCheck:{
nodeId:"",
joinId:"",
leftNodeId:"",
rightNodeId:"",
firstCloumn:"",
secondCloumn:""
},
asFields:[],
tableData:[],
dialogVisible1: false,
tables:{},
tableMsg:{
tableName:"",
tableAsField:""
},
cloumns:'',
labelPosition: 'right',
dialogVisible:false,
curSelectNode: "",
tableFields:[],
tableDataNodeId:[],
moduleList: [
{
id: 1,
name: "开始",
image: require("@/assets/img/1.png"),
},
{
id: 8,
name: "表",
image: require("@/assets/img/2.png"),
},
{
id: 2,
name: "联查",
image: require("@/assets/img/3.png"),
},
{
id: 3,
name: "导出",
image: require("@/assets/img/4.png"),
},
],
graph: null
};
},
created() {
this.getDatabaseMessage()
},
methods: {
showTableFields(){
this.finalVisible = true
let fieldsList = [...new Set(this.tableFields)]
selectByAsField(fieldsList).then(res => {
this.sqlList = res.data
})
selectByTableName(this.toTable).then(res => {
this.options = res.data
})
},
addSql(){
this.outPuter.taskId = this.$route.query.id
let fieldsList = [...new Set(this.tableFields)]
let tableAsFieldList = [...new Set(this.sourceField)]
this.outPuter.tableFields = fieldsList.toString()
this.outPuter.tableAsFields = tableAsFieldList.toString()
this.outPuter.targetTableName = this.toTable
addOutPut(this.outPuter).then(res => {
if (res.code == 200){
this.$message.success("输出节点放置成功")
}else {
this.$message.error("发生异常请联系管理员")
}
})
this.dialogVisible2 = false
this.finalVisible = false
},
handleChange(value){
this.outPuter.targetDatabase = value[0]
this.toTable = value[1]
},
getDatabaseMessage(){
extractDataName().then(res => {
this.tt = res.data
})
},
SelectionChange(val){
val.forEach(res => {
this.tableFields.push(res.asField)
this.sourceField.push(res.field)
})
},
todoSelect(){
addSelect(this.joinCheck).then(res => {
if (res.code == 200){
this.$message.success("操作成功")
}else {
this.$message.error(res.msg)
}
})
this.dialogVisible1 = false
},
findFormValue(){
this.dialogVisible = false
this.taskOpen = false
},
openForm(data){
let textWrap = data.attrs.label.textWrap;
if (textWrap.text == "表"){
console.log("表节点ID=>"+data.id);
this.tableDataNodeId.push(data.id)
this.dialogVisible = true;
this.taskOpen=true;
}
if (textWrap.text == "联查"){
this.dialogVisible1 = true;
this.joinCheck.nodeId = data.id
console.log("联查节点ID=>"+data.id);
const edges = this.graph.getEdges().filter(edge => {
return edge.getSourceNode().id === data.id || edge.getTargetNode().id === data.id;
});
const neighbors = [];
edges.forEach(edge => {
const otherNodeId = edge.getSourceNode().id === data.id ? edge.getTargetNode().id : edge.getSourceNode().id;
if (!neighbors.includes(otherNodeId)) {
neighbors.push(otherNodeId);
}
});
this.neighbors = neighbors;
this.joinCheck.leftNodeId = neighbors[0]
this.joinCheck.rightNodeId = neighbors[1]
selectByOne(neighbors[0]).then(res =>{
this.options1 = res.data
})
selectByOne(neighbors[1]).then(res =>{
this.options2 = res.data
})
selectByNodeId(JSON.stringify(this.neighbors)).then(res => {
this.nodeList = res.data
})
}
if (textWrap.text == "导出"){
this.outPuter.nodeId = data.id
console.log("导出节点ID=>"+data.id);
this.dialogVisible2 = true
findBySelectId(this.neighbors[0]).then(res => {
if (res.data == null){
seeTableField(this.tableDataNodeId).then(res => {
this.fields = res.data
})
}else{
seeTableField(this.neighbors).then(res => {
this.fields = res.data
})
}
})
}
},
//todo 鼠标划过节点在显示连接桩
nodeAddEvent() {
const { graph } = this;
const container = document.getElementById("container");
const changePortsVisible = (visible) => {
const ports = container.querySelectorAll(".x6-port-body");
for (let i = 0, len = ports.length; i < len; i = i + 1) {
ports[i].style.visibility = visible ? "visible" : "hidden";
}
};
this.graph.on("node:mouseenter", () => {
changePortsVisible(true);
});
this.graph.on("node:mouseleave", () => {
changePortsVisible(false);
});
this.graph.on("node:dblclick",(evt) =>{
let data = evt.node.store.data;
this.nodesIds = data.id;
this.openForm(data)
})
//todo 节点绑定点击事件
this.graph.on("node:click", ({ e, x, y, node, view }) => {
// 判断是否有选中过节点
if (this.curSelectNode) {
// 移除选中状态
this.curSelectNode.removeTools();
// 判断两次选中节点是否相同
if (this.curSelectNode !== node) {
node.addTools([
{
name: "boundary",
args: {
attrs: {
fill: "#16B8AA",
stroke: "#2F80EB",
strokeWidth: 1,
fillOpacity: 0.1,
},
},
},
{
name: "button-remove",
args: {
x: "100%",
y: 0,
offset: {
x: 0,
y: 0,
},
},
},
]);
this.curSelectNode = node;
} else {
this.curSelectNode = null;
}
} else {
this.curSelectNode = node;
node.addTools([
{
name: "boundary",
args: {
attrs: {
fill: "#16B8AA",
stroke: "#2F80EB",
strokeWidth: 1,
fillOpacity: 0.1,
},
},
},
{
name: "button-remove",
args: {
x: "100%",
y: 0,
offset: {
x: 0,
y: 0,
},
},
},
]);
}
});
//todo 连线绑定悬浮事件
this.graph.on("cell:mouseenter", ({ cell }) => {
if (cell.shape == "edge") {
cell.addTools([
{
name: "button-remove",
args: {
x: "100%",
y: 0,
offset: {
x: 0,
y: 0,
},
},
},
]);
cell.setAttrs({
line: {
stroke: "#409EFF",
},
});
cell.zIndex = 99; // 保证当前悬停的线在最上层,不会被遮挡
}
});
this.graph.on("cell:mouseleave", ({ cell }) => {
if (cell.shape === "edge") {
cell.removeTools();
cell.setAttrs({
line: {
stroke: "black",
},
});
cell.zIndex = 1; // 保证未悬停的线在下层,不会遮挡悬停的线
}
});
},
// todo 拖动后松开鼠标触发事件
handleDragEnd(e, item) {
// 可以获取到最后拖动后松开鼠标时的坐标和拖动的节点相关信息
this.addHandleNode(
e.pageX - 500,
e.pageY - 200,
new Date().getTime(),
item.image,
item.name
);
},
initGraph() {
const container = document.getElementById("container");
this.graph = new Graph({
container: container, // 画布容器
width: container.offsetWidth, // 画布宽
height: container.offsetHeight, // 画布高
background: false, // 背景(透明)
snapline: true, // 对齐线
// 配置连线规则
connecting: {
snap: true, // 自动吸附
allowBlank: false, // 是否允许连接到画布空白位置的点
allowMulti: true, // 是否允许在相同的起始节点和终止之间创建多条边
allowLoop: true, // 是否允许创建循环连线,即边的起始节点和终止节点为同一节点
highlight: true, // 拖动边时,是否高亮显示所有可用的节点
highlighting: {
magnetAdsorbed: {
name: "stroke",
args: {
attrs: {
fill: "#5F95FF",
stroke: "#5F95FF",
},
},
},
},
router: {
// 对路径添加额外的点
name: "orth",
},
connector: {
// 边渲染到画布后的样式
name: "rounded",
args: {
radius: 8,
},
},
},
panning: {
enabled: false,
},
mousewheel: {
enabled: true, // 支持滚动放大缩小
zoomAtMousePosition: true,
modifiers: "ctrl",
minScale: 0.5,
maxScale: 3,
},
grid: {
type: "dot",
size: 20, // 网格大小 10px
visible: true, // 渲染网格背景
args: {
color: "#a0a0a0", // 网格线/点颜色
thickness: 2, // 网格线宽度/网格点大小
},
},
});
this.nodeAddEvent()
},
//添加节点到画布
addHandleNode(x, y, id, image, name) {
this.graph.addNode({
id: id,
shape: "image", // 指定使用何种图形,默认值为 'rect'
x: x,
y: y,
width: 60,
height: 60,
imageUrl: image,
attrs: {
body: {
stroke: "#ffa940",
fill: "#ffd591",
},
label: {
textWrap: {
width: 90,
text: name,
},
fill: "black",
fontSize: 12,
refX: 0.5,
refY: "100%",
refY2: 4,
textAnchor: "middle",
textVerticalAnchor: "top",
},
},
ports: {
groups: {
group1: {
position: [30, 30],
},
},
items: [
{
group: "group1",
id: "port1",
attrs: {
circle: {
r: 6,
magnet: true,
stroke: "#ffffff",
strokeWidth: 2,
fill: "#5F95FF",
},
},
},
],
},
zIndex: 10,
});
},
//todo 保存画布,并提交
saveActive() {
console.log(this.graph.toJSON(), "graph");
console.log(this.graph.getNodes(), "node");
},
},
//生命周期 - 挂载完成可以访问DOM元素",
mounted() {
this.initGraph()
},
};
</script>
<style lang="scss" scoped>
.dashboard-container {
.antvBox {
display: flex;
width: 100%;
height: 100%;
color: black;
padding-top: 20px;
.menu-list {
height: 100%;
width: 300px;
padding: 0 10px;
box-sizing: border-box;
display: flex;
justify-content: space-between;
align-content: flex-start;
flex-wrap: wrap;
> div {
margin-bottom: 10px;
border-radius: 5px;
padding: 0 10px;
box-sizing: border-box;
cursor: pointer;
color: black;
width: 105px;
display: flex;
flex-wrap: wrap;
justify-content: center;
img {
height: 50px;
width: 50px;
}
P {
width: 90px;
text-align: center;
}
}
}
.canvas-card {
width: 1700px;
height: 750px;
box-sizing: border-box;
> div {
width: 1400px;
height: 750px;
border: 2px dashed #2149ce;
}
}
}
}
</style>