Files
railwaysystem/build_ui/src/views/graph/nebula_query.vue
T
2026-09-10 15:20:14 +08:00

1448 lines
55 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="all">
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px" style="margin-left:10px;">
<el-form-item label="图谱名称" prop="kgId">
<el-select v-model="queryParams.kgId" placeholder="请选择">
<el-option
v-for="(mItem, mIndex) in kgList"
:key="mItem.kgId"
:label="mItem.kgName"
:value="mItem.kgId"
/>
</el-select>
</el-form-item>
<el-form-item label="实体名称" prop="entity">
<el-input
v-model="queryParams.entity"
placeholder="请输入实体名称"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="关系名称" prop="relation">
<el-input
v-model="queryParams.relation"
placeholder="请输入关系名称"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="属性名称" prop="property" v-show="false">
<el-input
v-model="queryParams.property"
placeholder="请输入属性名称"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
</div>
<div class="antv-content" v-loading="loading">
<el-col :span="18">
<div class="antv-wrapper">
<div ref="graphContent" id="wrapper"></div>
</div>
</el-col>
<el-col :span="6" v-show="editShow">
<div style="margin-top: 10px;">
<div>
<el-tabs type="border-card" v-model="activeName" @tab-click="handleClick">
<el-tab-pane name="nodeTab">
<span slot="label"><i class="el-icon-eleme"></i> 节点</span>
<el-form ref="form" :model="nodeProp" label-width="100px">
<div v-show="1">
<el-row :gutter="20">
<el-col :span="24" style="margin-bottom: 5px;">
<!-- <el-input v-model="nodeProp.label" placeholder="节点Label"></el-input>-->
<el-select v-model="nodeProp.label" placeholder="节点Label" clearable :disabled="labelDisabled">
<el-option
v-for="(item, index) in labelList"
:key="item.key"
:label="item.key"
:value="item.value"
/>
</el-select>
</el-col>
<el-col :span="24" style="margin-bottom: 5px;">
<el-input v-model="nodeProp.name" placeholder="节点名称" :disabled="labelDisabled"></el-input>
</el-col>
</el-row>
<el-row :gutter="20" v-for="(item, index) in nodeProp.props">
<el-col :span="10" style="margin-bottom: 5px;">
<el-input v-model="item.key" placeholder="属性名" readonly="true"></el-input>
</el-col>
<el-col :span="14">
<el-input v-model="item.value" placeholder="属性值" readonly="true"></el-input>
</el-col>
<!-- <el-col :span="4">
<el-button
@click="addOrDel(index)"
v-bind:type="index>0?'warning':'primary'"
v-bind:icon="index>0?'el-icon-minus':'el-icon-plus'"
circle
>
</el-button>
</el-col>-->
</el-row>
<div style="margin-top: 5px">
<el-input type="hidden" v-model="nodeProp.id"></el-input>
<el-button type="primary" icon="el-icon-add" size="mini" v-show="!btnDeleteNode" @click="submitEditNode">新增</el-button>
<el-button type="primary" icon="el-icon-add" size="mini" v-show="!btnDeleteNode" @click="resetEditNode">清空</el-button>
<!-- <el-button type="primary" icon="el-icon-add" size="mini" v-show="btnDeleteNode" @click="submitEditProp">修改属性</el-button>-->
<el-button type="warning" icon="el-icon-add" size="mini" v-show="btnDeleteNode" @click="submitDeleteNode">删除此节点</el-button>
</div>
</div>
</el-form>
</el-tab-pane>
<el-tab-pane name="edgeTab">
<span slot="label"><i class="el-icon-share"></i> 关系</span>
<el-form ref="form" :model="edgeProp" label-width="100px">
<div v-show="1">
<el-row :gutter="20">
<el-col :span="24" style="margin-bottom: 5px;">
<h5>请在图中“点击”选择两个节点</h5>
<!-- <el-input v-model="nodeProp.label" placeholder="节点Label"></el-input>-->
<el-select v-model="edgeProp.label1" placeholder="节点1Label" clearable :disabled="true">
<el-option
v-for="(item, index) in labelList"
:key="item.key"
:label="item.key"
:value="item.value"
/>
</el-select>
</el-col>
<el-col :span="24" style="margin-bottom: 5px;">
<el-input v-model="edgeProp.name1" placeholder="节点1名称" :disabled="true"></el-input>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="24" style="margin-bottom: 5px;">
<el-select v-model="edgeProp.label2" placeholder="节点2Label" clearable :disabled="true">
<el-option
v-for="(item, index) in labelList"
:key="item.key"
:label="item.key"
:value="item.value"
/>
</el-select>
</el-col>
<el-col :span="24" style="margin-bottom: 5px;">
<el-input v-model="edgeProp.name2" placeholder="节点2名称" :disabled="true"></el-input>
</el-col>
</el-row>
<el-col :span="24" style="margin-bottom: 5px;">
<el-radio-group v-model="edgeProp.opType" @input="chgRelOp">
<el-radio label="新增关系"></el-radio>
<el-radio label="删除关系"></el-radio>
</el-radio-group>
</el-col>
<el-col :span="24" v-show="relOpFlag" style="margin-bottom: 5px;">
<el-input v-model="edgeProp.newRel" placeholder="新增关系名称"></el-input>
</el-col>
<el-col :span="24" v-show="!relOpFlag" style="margin-bottom: 5px;">
<el-select v-model="edgeProp.deleteRel" placeholder="请选择需删除的关系" clearable :disabled="labelDisabled">
<el-option
v-for="(item, index) in edgeList"
:key="item.key"
:label="item.key"
:value="item.value"
/>
</el-select>
</el-col>
<div style="margin-top: 5px">
<el-input type="hidden" v-model="edgeProp.id1"></el-input>
<el-input type="hidden" v-model="edgeProp.id2"></el-input>
<el-button type="primary" icon="el-icon-add" size="mini" @click="submitEditEdge">确定</el-button>
<el-button type="primary" icon="el-icon-add" size="mini" @click="resetEditEdge">清空</el-button>
</div>
</div>
</el-form>
</el-tab-pane>
</el-tabs>
</div>
</div>
</el-col>
</div>
</div>
</template>
<script>
import G6 from '@antv/g6';
import insertCss from 'insert-css';
import { ForceLayout } from '@antv/layout'
import { configSetting, configNodeShape, configNodePorts, configEdgeLabel, graphBindKey } from '/static/js/antvSetting.js'
import axios from 'axios'
import { getToken } from '@/utils/auth'
import { setting } from '@/config'
import Vue from 'vue'
import { delKgModal } from '@/api/build/modal'
import { listKgBase } from '@/api/build/kgmgr'
Vue.prototype.$axios = axios
axios.defaults.baseURL = setting.apiUrl;
insertCss(`
.g6-component-tooltip {
border: 1px solid #e2e2e2;
border-radius: 4px;
font-size: 12px;
color: #000;
background-color: rgba(255, 255, 255, 0.9);
padding: 10px 8px;
box-shadow: rgb(174, 174, 174) 0px 0px 10px;
}
#contextMenu {
position: absolute;
list-style-type: none;
padding: 10px 8px;
left: -150px;
background-color: rgba(255, 255, 255, 0.9);
border: 1px solid #e2e2e2;
border-radius: 4px;
font-size: 12px;
color: #545454;
}
#contextMenu li {
cursor: pointer;
list-style-type:none;
list-style: none;
margin-left: 0px;
}
#contextMenu li:hover {
color: #aaa;
}
.g6-component-toolbar li {
list-style-type: none !important;
}
.g6-minimap-container {
border: 1px solid #e2e2e2;
}
.g6-minimap-viewport {
border: 2px solid rgb(25, 128, 255);
}
`);
export default {
name: "AntVG6",
/**
* 这个是作为子组件分别接受了两个数据一个是高度height,一个是反显图表数据tempGroupJson
* 作为子组件例子 <AntVXSix v-model="tempGroupJson" height="720px" />
*
*/
props: {
height: {
type: String,
default: '100vh' //'720px'
},
value: {
type: String,
default: ''
}
},
data() {
return {
graph: null,
isChange: false,
isPortsShow: false,
menuItem: '',
selectCell: '',
editDrawer: false,
editTitle: '',
form:{},
labelForm: {
fontColor: '#333',
fill: '#FFF',
stroke: '#555'
},
kgId: '',
kgName: '',
nodeData : {},
nodeProp: {"id":"","name":"","label":"","props":[{"key":"","value":""},{"key":"","value":""}]},
edgeProp: {"opType":"1"},
editShow: true,
labelList: [],
labelDisabled: false,
btnDeleteNode: false,
relOpFlag: true,
activeName: "nodeTab",
edgeList: [],
// 查询参数
queryParams: {
kgId: undefined,
kgName: undefined,
entity: undefined,
property: undefined,
relation: undefined
},
showSearch: true,
// 图谱表格数据
kgList: [],
nebulaEdgeList: [],
// 遮罩层
loading: false,
}
},
created() {
},
watch:{
value:{
handler: function(){
if(this.graph){
this.isChange = false
this.isPortsShow = false
this.menuItem = ''
this.selectCell = ''
this.editDrawer = false
//this.graph.dispose()
this.graph.destroy();
this.initGraph()
}
},
deep: true,
immediate: true
}
},
mounted() {
let paramKgId = this.$route.query.kgId ? this.$route.query.kgId : "";
this.kgId = paramKgId;
//this.queryParams.kgId = paramKgId;
//如果url中没带参数,则取数据表中第一个
//this.$set(this.queryParams,"kgId",parseInt(paramKgId, 10));
listKgBase(this.queryParams).then(response => {
console.log(this.queryParams);
console.log("listKgBase response:");
console.log(response);
this.kgList = response.rows;
for(let i=0; i<this.kgList.length; i++){
if(i == 0){
//如果url中没带参数,则取数据表中第一个,2025-07-24
if(paramKgId.length == 0){
this.$set(this.queryParams,"kgId",parseInt(this.kgList[i].kgId, 10));
this.$set(this.queryParams,"kgName",this.kgList[i].kgName);
}
}
if(parseInt(this.kgId, 10) === parseInt(this.kgList[i].kgId, 10)){
this.$set(this.queryParams,"kgId",parseInt(paramKgId, 10));
this.$set(this.queryParams,"kgName",this.kgList[i].kgName);
}
}
//初始化画布和图谱,相当于点击搜索
this.handleQuery();
});
},
beforeDestroy() {
//this.graph.dispose();
if(this.graph){
this.graph.destroy();
}
},
methods: {
// 链接桩的显示与隐藏,主要是照顾菱形
changePortsShow(val){
const container = document.getElementById('wrapper')
const ports = container.querySelectorAll('.x6-port-body')
for (let i = 0, len = ports.length; i < len; i = i + 1) {
ports[i].style.visibility = val ? 'visible' : 'hidden'
}
},
// 初始化渲染画布
initGraph(){
const colors = [
'#BDD2FD',
'#BDEFDB',
'#C2C8D5',
'#FBE5A2',
'#F6C3B7',
'#B6E3F5',
'#D3C6EA',
'#FFD8B8',
'#AAD8D8',
'#FFD6E7',
];
const strokes = [
'#5B8FF9',
'#5AD8A6',
'#5D7092',
'#F6BD16',
'#E8684A',
'#6DC8EC',
'#9270CA',
'#FF9D4D',
'#269A99',
'#FF99C3',
];
//小地图
const minimap = new G6.Minimap({
size: [150, 100],
});
//鱼眼
let fisheye = new G6.Fisheye({
r: 200,
showLabel: true,
});
/*const toolbar = new G6.ToolBar({
position: { x: 10, y: 10 },
});*/
const animateCfg = { duration: 200, easing: 'easeCubic' };
//自定义工具栏
const toolbar = new G6.ToolBar({
position: { x: 10, y: 10 },
getContent: () => `
<ul class='g6-component-toolbar'>
<li code='zoomOut' title="放大">
<svg class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" width="24" height="24">
<path d="M658.432 428.736a33.216 33.216 0 0 1-33.152 33.152H525.824v99.456a33.216 33.216 0 0 1-66.304 0V461.888H360.064a33.152 33.152 0 0 1 0-66.304H459.52V296.128a33.152 33.152 0 0 1 66.304 0V395.52H625.28c18.24 0 33.152 14.848 33.152 33.152z m299.776 521.792a43.328 43.328 0 0 1-60.864-6.912l-189.248-220.992a362.368 362.368 0 0 1-215.36 70.848 364.8 364.8 0 1 1 364.8-364.736 363.072 363.072 0 0 1-86.912 235.968l192.384 224.64a43.392 43.392 0 0 1-4.8 61.184z m-465.536-223.36a298.816 298.816 0 0 0 298.432-298.432 298.816 298.816 0 0 0-298.432-298.432A298.816 298.816 0 0 0 194.24 428.8a298.816 298.816 0 0 0 298.432 298.432z"></path>
</svg>
</li>
<li code='zoomIn' title="缩小">
<svg class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" width="24" height="24">
<path d="M639.936 416a32 32 0 0 1-32 32h-256a32 32 0 0 1 0-64h256a32 32 0 0 1 32 32z m289.28 503.552a41.792 41.792 0 0 1-58.752-6.656l-182.656-213.248A349.76 349.76 0 0 1 480 768 352 352 0 1 1 832 416a350.4 350.4 0 0 1-83.84 227.712l185.664 216.768a41.856 41.856 0 0 1-4.608 59.072zM479.936 704c158.784 0 288-129.216 288-288S638.72 128 479.936 128a288.32 288.32 0 0 0-288 288c0 158.784 129.216 288 288 288z" p-id="3853"></path>
</svg>
</li>
<li code='realZoom' title="还原">
<svg class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" width="20" height="24">
<path d="M384 320v384H320V320h64z m256 0v384H576V320h64zM512 576v64H448V576h64z m0-192v64H448V384h64z m355.968 576H92.032A28.16 28.16 0 0 1 64 931.968V28.032C64 12.608 76.608 0 95.168 0h610.368L896 192v739.968a28.16 28.16 0 0 1-28.032 28.032zM704 64v128h128l-128-128z m128 192h-190.464V64H128v832h704V256z"></path>
</svg>
</li>
<li code='autoZoom' title="中心">
<svg class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" width="20" height="24">
<path d="M684.288 305.28l0.128-0.64-0.128-0.64V99.712c0-19.84 15.552-35.904 34.496-35.712a35.072 35.072 0 0 1 34.56 35.776v171.008h170.944c19.648 0 35.84 15.488 35.712 34.432a35.072 35.072 0 0 1-35.84 34.496h-204.16l-0.64-0.128a32.768 32.768 0 0 1-20.864-7.552c-1.344-1.024-2.816-1.664-3.968-2.816-0.384-0.32-0.512-0.768-0.832-1.088a33.472 33.472 0 0 1-9.408-22.848zM305.28 64a35.072 35.072 0 0 0-34.56 35.776v171.008H99.776A35.072 35.072 0 0 0 64 305.216c0 18.944 15.872 34.496 35.84 34.496h204.16l0.64-0.128a32.896 32.896 0 0 0 20.864-7.552c1.344-1.024 2.816-1.664 3.904-2.816 0.384-0.32 0.512-0.768 0.768-1.088a33.024 33.024 0 0 0 9.536-22.848l-0.128-0.64 0.128-0.704V99.712A35.008 35.008 0 0 0 305.216 64z m618.944 620.288h-204.16l-0.64 0.128-0.512-0.128c-7.808 0-14.72 3.2-20.48 7.68-1.28 1.024-2.752 1.664-3.84 2.752-0.384 0.32-0.512 0.768-0.832 1.088a33.664 33.664 0 0 0-9.408 22.912l0.128 0.64-0.128 0.704v204.288c0 19.712 15.552 35.904 34.496 35.712a35.072 35.072 0 0 0 34.56-35.776V753.28h170.944c19.648 0 35.84-15.488 35.712-34.432a35.072 35.072 0 0 0-35.84-34.496z m-593.92 11.52c-0.256-0.32-0.384-0.768-0.768-1.088-1.088-1.088-2.56-1.728-3.84-2.688a33.088 33.088 0 0 0-20.48-7.68l-0.512 0.064-0.64-0.128H99.84a35.072 35.072 0 0 0-35.84 34.496 35.072 35.072 0 0 0 35.712 34.432H270.72v171.008c0 19.84 15.552 35.84 34.56 35.776a35.008 35.008 0 0 0 34.432-35.712V720l-0.128-0.64 0.128-0.704a33.344 33.344 0 0 0-9.472-22.848zM512 374.144a137.92 137.92 0 1 0 0.128 275.84A137.92 137.92 0 0 0 512 374.08z"></path>
</svg>
</li>
<!--<li code='addNode' title="添加节点">
<svg class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" width="20" height="24">
<path d="M684.288 305.28l0.128-0.64-0.128-0.64V99.712c0-19.84 15.552-35.904 34.496-35.712a35.072 35.072 0 0 1 34.56 35.776v171.008h170.944c19.648 0 35.84 15.488 35.712 34.432a35.072 35.072 0 0 1-35.84 34.496h-204.16l-0.64-0.128a32.768 32.768 0 0 1-20.864-7.552c-1.344-1.024-2.816-1.664-3.968-2.816-0.384-0.32-0.512-0.768-0.832-1.088a33.472 33.472 0 0 1-9.408-22.848zM305.28 64a35.072 35.072 0 0 0-34.56 35.776v171.008H99.776A35.072 35.072 0 0 0 64 305.216c0 18.944 15.872 34.496 35.84 34.496h204.16l0.64-0.128a32.896 32.896 0 0 0 20.864-7.552c1.344-1.024 2.816-1.664 3.904-2.816 0.384-0.32 0.512-0.768 0.768-1.088a33.024 33.024 0 0 0 9.536-22.848l-0.128-0.64 0.128-0.704V99.712A35.008 35.008 0 0 0 305.216 64z m618.944 620.288h-204.16l-0.64 0.128-0.512-0.128c-7.808 0-14.72 3.2-20.48 7.68-1.28 1.024-2.752 1.664-3.84 2.752-0.384 0.32-0.512 0.768-0.832 1.088a33.664 33.664 0 0 0-9.408 22.912l0.128 0.64-0.128 0.704v204.288c0 19.712 15.552 35.904 34.496 35.712a35.072 35.072 0 0 0 34.56-35.776V753.28h170.944c19.648 0 35.84-15.488 35.712-34.432a35.072 35.072 0 0 0-35.84-34.496z m-593.92 11.52c-0.256-0.32-0.384-0.768-0.768-1.088-1.088-1.088-2.56-1.728-3.84-2.688a33.088 33.088 0 0 0-20.48-7.68l-0.512 0.064-0.64-0.128H99.84a35.072 35.072 0 0 0-35.84 34.496 35.072 35.072 0 0 0 35.712 34.432H270.72v171.008c0 19.84 15.552 35.84 34.56 35.776a35.008 35.008 0 0 0 34.432-35.712V720l-0.128-0.64 0.128-0.704a33.344 33.344 0 0 0-9.472-22.848zM512 374.144a137.92 137.92 0 1 0 0.128 275.84A137.92 137.92 0 0 0 512 374.08z"></path>
</svg>
</li>
<li code='addEdge' title="添加边">
<svg class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" width="20" height="24">
<path d="M684.288 305.28l0.128-0.64-0.128-0.64V99.712c0-19.84 15.552-35.904 34.496-35.712a35.072 35.072 0 0 1 34.56 35.776v171.008h170.944c19.648 0 35.84 15.488 35.712 34.432a35.072 35.072 0 0 1-35.84 34.496h-204.16l-0.64-0.128a32.768 32.768 0 0 1-20.864-7.552c-1.344-1.024-2.816-1.664-3.968-2.816-0.384-0.32-0.512-0.768-0.832-1.088a33.472 33.472 0 0 1-9.408-22.848zM305.28 64a35.072 35.072 0 0 0-34.56 35.776v171.008H99.776A35.072 35.072 0 0 0 64 305.216c0 18.944 15.872 34.496 35.84 34.496h204.16l0.64-0.128a32.896 32.896 0 0 0 20.864-7.552c1.344-1.024 2.816-1.664 3.904-2.816 0.384-0.32 0.512-0.768 0.768-1.088a33.024 33.024 0 0 0 9.536-22.848l-0.128-0.64 0.128-0.704V99.712A35.008 35.008 0 0 0 305.216 64z m618.944 620.288h-204.16l-0.64 0.128-0.512-0.128c-7.808 0-14.72 3.2-20.48 7.68-1.28 1.024-2.752 1.664-3.84 2.752-0.384 0.32-0.512 0.768-0.832 1.088a33.664 33.664 0 0 0-9.408 22.912l0.128 0.64-0.128 0.704v204.288c0 19.712 15.552 35.904 34.496 35.712a35.072 35.072 0 0 0 34.56-35.776V753.28h170.944c19.648 0 35.84-15.488 35.712-34.432a35.072 35.072 0 0 0-35.84-34.496z m-593.92 11.52c-0.256-0.32-0.384-0.768-0.768-1.088-1.088-1.088-2.56-1.728-3.84-2.688a33.088 33.088 0 0 0-20.48-7.68l-0.512 0.064-0.64-0.128H99.84a35.072 35.072 0 0 0-35.84 34.496 35.072 35.072 0 0 0 35.712 34.432H270.72v171.008c0 19.84 15.552 35.84 34.56 35.776a35.008 35.008 0 0 0 34.432-35.712V720l-0.128-0.64 0.128-0.704a33.344 33.344 0 0 0-9.472-22.848zM512 374.144a137.92 137.92 0 1 0 0.128 275.84A137.92 137.92 0 0 0 512 374.08z"></path>
</svg>
</li>-->
</ul>`,
handleClick: (code, graph) => {
switch (code) {
case 'zoomOut':
graph.zoom(1.2, undefined, true, animateCfg);
break;
case 'zoomIn':
graph.zoom(0.8, undefined, true, animateCfg);
break;
case 'realZoom':
graph.zoomTo(1, undefined, true, animateCfg);
break;
case 'autoZoom':
graph.fitView(20, undefined, true, animateCfg);
break;
/*case 'addNode':
console.log("addNode");
break;
case 'addEdge':
console.log("addEdge");
break;*/
}
}
});
//悬浮框
const tooltip = new G6.Tooltip({
offsetX: 10,
offsetY: 10,
fixToNode: [1, 0.5],
// the types of items that allow the tooltip show up
// 允许出现 tooltip 的 item 类型
itemTypes: ['node', 'edge'],
// custom the tooltip's content
// 自定义 tooltip 内容
getContent: (e) => {
const outDiv = document.createElement('div');
outDiv.style.width = 'fit-content';
outDiv.style.height = 'fit-content';
const model = e.item.getModel();
//console.log("model=================");
//console.log(model);
if (e.item.getType() === 'node') {
outDiv.innerHTML = `${model.label}`;
} else {
const source = e.item.getSource();
const target = e.item.getTarget();
outDiv.innerHTML = `来源:${source.getModel().label}<br/>去向:${target.getModel().label}`;
}
return outDiv;
},
});
//右键菜单
const contextMenu = new G6.Menu({
getContent(evt) {
let header;
let id;
let label;
//console.log("evt============");
//console.log(evt);
if(evt.targetMarker && evt.isCanvas && evt.target.isCanvas()){
header = "Canvas ContextMenu";
}else if (evt.item) {
const itemType = evt.item.getType();
header = `${itemType.toUpperCase()} ContextMenu`;
id = evt.item.getModel().id;
label = evt.item.getModel().label;
}
if(header === undefined){
return `
<h3>操作</h3>
<ul>
<li style="cursor: pointer" onclick="editNodeItem()">添加节点</li>
<li style="cursor: pointer" onclick="editEdgeItem()">添加边</li>
</ul>
`;
}else{
return `
<h3>${header}</h3>
<ul>
<li>id:${id}</li>
<li>标签:${label}</li>
</ul>
`;
}
},
handleMenuClick: ((target, item) => {
console.log(target, item);
}),
// offsetX and offsetY include the padding of the parent container
// 需要加上父级容器的 padding-left 16 与自身偏移量 10
offsetX: 16 + 10,
// 需要加上父级容器的 padding-top 24 、画布兄弟元素高度、与自身偏移量 10
offsetY: 0,
// the types of items that allow the menu show up
// 在哪些类型的元素上响应
itemTypes: ['node', 'edge', 'canvas'],
});
console.log("handleQuery______________22222");
console.log("queryParams");
console.log(this.queryParams);
//return false;
axios.post("kg/base/getnebuladata",
{
"kgId":this.kgId,
"entity": this.queryParams.entity?this.queryParams.entity:"",
"property": this.queryParams.property?this.queryParams.property:"",
"relation": this.queryParams.relation?this.queryParams.relation:""
},
{
headers: {
'Authorization': 'Bearer ' + getToken()
}
},
)
.then(res => {
console.log("res_getnebuladata================");
console.log(res);
if(res.status === 200){
const nodes = res.data.nodeList;
const edges = res.data.edgeList;
this.nebulaEdgeList = res.data.edgeList;
let legendData = res.data.legend;
let labelList = res.data.labelList;
this.labelList = labelList;
const clusterMap = new Map();
let clusterId = 0;
//console.log(nodes);
nodes.forEach(function(node) {
//cluster
if(node.cluster && clusterMap.get(node.cluster) === undefined){
clusterMap.set(node.cluster, clusterId);
clusterId++;
}
const cid = clusterMap.get(node.cluster);
if(!node.style){
node.style = {};
}
node.style.fill = colors[cid % colors.length];
node.style.stroke = strokes[cid % strokes.length];
});
edges.forEach(edge => {
//cluster
if(edge.cluster && clusterMap.get(edge.cluster) === undefined){
clusterMap.set(edge.cluster, clusterId);
clusterId++;
}
const cid = clusterMap.get(edge.cluster);
if(!edge.style){
edge.style = {};
}
edge.style = {
stroke: colors[cid % colors.length]
}
});
//console.log(clusterMap);
//console.log(clusterMap.get("Film"));
legendData.nodes.forEach(node => {
//console.log(node);
//console.log("legend.nodes====="+node.id);
const cid = clusterMap.get(node.id);
//console.log(cid);
node.style = {
fill: colors[cid % colors.length],
stroke: strokes[cid % strokes.length]
};
});
legendData.edges.forEach(node => {
//console.log(node);
//console.log("legend.nodes====="+node.id);
const cid = clusterMap.get(node.id);
//console.log("legend.cid====="+cid);
node.style = {
fill: colors[cid % colors.length],
stroke: strokes[cid % strokes.length]
};
});
const legend = new G6.Legend({
data: legendData,
align: 'center',
layout: 'horizontal', // vertical
position: 'bottom-right',
vertiSep: 12,
horiSep: 24,
offsetY: -24,
padding: [4, 16, 8, 16],
containerStyle: {
fill: '#ccc',
lineWidth: 1
},
title: 'Legend',
titleConfig: {
position: 'center',
offsetX: 0,
offsetY: 12,
},
filter: {
enable: true,
multiple: true,
trigger: 'click',
graphActiveState: 'activeByLegend',
graphInactiveState: 'inactiveByLegend',
/*filterFunctions: legendData.nodes.forEach(node=>{
console.log("node0202020===========");
console.log(node);
if(node.id === 'Film') return true;
return false;
}),*/
filterFunctions: {
'Film': (d) => {
if(d.legendType === 'Film') return true;
return false;
},
'Person': (d) => {
if(d.legendType === 'Person') return true;
return false;
},
'导演': (d) => {
if(d.legendType === '导演') return true;
return false;
},
'主演': (d) => {
if(d.legendType === '主演') return true;
return false;
},
}
}
});
const width = document.getElementById('wrapper').scrollWidth;
const height = document.getElementById('wrapper').scrollHeight || 560;
const graph = new G6.Graph({
container: document.getElementById('wrapper'),
width,
height,
layout: {
type: 'forceAtlas2',//force2 防重叠
//type: 'force2',//force2 防重叠
preventOverlap: true,
linkDistance: d => {
if(d.source.id === 'node0'){
return 100;
}
return 30;
},
nodeStrength: d => {
if(d.isLeaf){
return -50;
}
return -10;
},
edgeStrength: d => {
if(d.source.id === 'node1' || d.source.id === 'node2' || d.source.id === 'node3'){
return 0.7;
}
return 0.1;
},
},
//划过弹出提示栏,右键菜单
plugins: [tooltip, contextMenu, legend, toolbar, minimap],//fisheye 鱼眼
animate: true,
defaultNode: {
//color: '#5B8FF9',
style: {
lineWidth: 2,
//fill: '#C6E5FF',
fill: '#DEE9FF',
stroke: '#5B8FF9',
}
},
defaultEdge: {
//type: 'arc',
//type: 'quadratic',//指定边的形状为二阶贝塞尔曲线,同方向的多条边还是重合
size: 1,
color: '#E2E2E2'
},
/** 自定义高亮显示 start **/
modes: {
//default: ['drag-canvas', 'activate-relations']
default: ['drag-canvas', 'drag-node']
},
nodeStateStyles: {
highlight: {
opacity: 1,
},
dark: {
opacity: 0.2
},
activeByLegend: {
lineWidth: 10,
strokeOpacity: 0.5
},
inactiveByLegend: {
opacity: 0.5
}
},
edgeStateStyles: {
highlight: {
stroke: '#999'
},
activeByLegend: {
lineWidth: 3
},
inactiveByLegend: {
opacity: 0.5
}
},
/** 自定义高亮显示 end **/
});
//防止同方向边重合,2023-11-30
G6.Util.processParallelEdges(res.data.edgeList);
graph.data({
nodes: nodes.map(function(node, j){
//console.log("node=========");
//console.log(node);
//node.color = "#A2B1C3";
node.legendType = node.legendType;
node.cluster = node.legendType;
//console.log("legendType========="+node.legendType);
//return Object.assign(node,{...typeConfigs[node.legendType]});
return Object.assign(node,{});
}),
edges: res.data.edgeList.map(function(edge, i){
edge.id = 'edge' + i;
/*edge.attrs("line/stroke",'#A2B1C3');
edge.attrs("line.strokeWidth",2);
edge.attrs("line.targetMarker",null);*/
return Object.assign({}, edge);
}),
});
graph.render();
graph.on('node:dragstart', function(e) {
graph.layout();
refreshDragedNodePosition(e);
});
graph.on('node:drag', function(e) {
refreshDragedNodePosition(e);
});
graph.on('node:dragend', function(e) {
e.item.get('model').fx = null;
e.item.get('model').fy = null;
});
let that = this;
graph.on('node:click', function(e) {
let activeName = that.activeName;
//console.log("activeName=======" + that.activeName);
//console.log("id1==="+that.edgeProp.id1);
if(activeName === "nodeTab"){
const item = e.item.getModel();
//console.log(item);
//获取当前节点数据信息
let CellId = e.item._cfg.id;
let ChildId = e.item._cfg.model.cellid;
let NodeState = e.item._cfg.model.state;
//console.log("click a node named: " + item.id);
//console.log("show====="+that.editShow);
//console.log(item);
let tmpLabel = item.label;
that.$set(that.nodeProp, "name", tmpLabel);
that.$set(that.nodeProp, "id", item.id);
that.$set(that.nodeProp, "label", item.cluster);
that.$set(that.nodeProp, "props", item.props);
//console.log(e);
that.labelDisabled = true;
that.btnDeleteNode = true;
}else if(activeName === "edgeTab"){
const item = e.item.getModel();
let edgeProp = that.edgeProp;
//console.log("that.edgeProp================");
//console.log(that.edgeProp);
if(that.edgeProp.id1 === "" || that.edgeProp.id1 === undefined){
that.$set(that.edgeProp, "name1", item.label);
that.$set(that.edgeProp, "id1", item.id);
that.$set(that.edgeProp, "label1", item.cluster);
}else if(that.edgeProp.id2 === "" || that.edgeProp.id2 === undefined){
that.$set(that.edgeProp, "name2", item.label);
that.$set(that.edgeProp, "id2", item.id);
that.$set(that.edgeProp, "label2", item.cluster);
}else {
that.$modal.msgSuccess("已经选中了两个节点,如需重选请先“清空”!");
}
this.chgRelOp("新增关系");
}
//console.log("that.nodeProp=======================");
//console.log(that.nodeProp);
});
function refreshDragedNodePosition(e) {
const model = e.item.get('model');
model.fx = e.x;
model.fy = e.y;
}
/** 自定义高亮显示 start **/
function clearAllStats() {
graph.setAutoPaint(false);
graph.getNodes().forEach(function (node) {
graph.clearItemStates(node);
});
graph.getEdges().forEach(function (edge) {
graph.clearItemStates(edge);
});
graph.paint();
graph.setAutoPaint(true);
}
graph.on('node:mouseenter', function (e) {
const item = e.item;
graph.setAutoPaint(false);
graph.getNodes().forEach(function (node) {
graph.clearItemStates(node);
graph.setItemState(node, 'dark', true);
});
graph.setItemState(item, 'dark', false);
graph.setItemState(item, 'highlight', true);
graph.getEdges().forEach(function (edge) {
if (edge.getSource() === item) {
graph.setItemState(edge.getTarget(), 'dark', false);
graph.setItemState(edge.getTarget(), 'highlight', true);
graph.setItemState(edge, 'highlight', true);
edge.toFront();
} else if (edge.getTarget() === item) {
graph.setItemState(edge.getSource(), 'dark', false);
graph.setItemState(edge.getSource(), 'highlight', true);
graph.setItemState(edge, 'highlight', true);
edge.toFront();
} else {
graph.setItemState(edge, 'highlight', false);
}
});
graph.paint();
graph.setAutoPaint(true);
});
graph.on('node:mouseleave', clearAllStats);
graph.on('canvas:click', clearAllStats);
/** 自定义高亮显示 end **/
this.graph = graph;
}
})
.catch(function (error) {
console.log(error)
});
},
addOrDel(index){
let obj = this.nodeProp.props;
let newObj = {};
if(index==0){//add
for(let k1 in obj){
if(k1==0){
for(let k2 in obj[k1]){
newObj[k2] = "";
}
}
}
obj.push(newObj)
}else{//delete
obj.splice(index,1);
}
this.nodeProp.props = obj;
},
editNodeItem(){
this.editShow = true;
},
editEdgeItem(){
this.editShow = true;
},
submitEditEdge(){
//console.log(this.edgeProp);
//console.log("addEdge");
console.log("kgId="+this.kgId);
console.log(this.kgList);
for(let i=0; i<this.kgList.length; i++){
if(parseInt(this.kgId, 10) === parseInt(this.kgList[i].kgId, 10)){
this.$set(this.queryParams,"kgId",parseInt(this.kgId, 10));
this.$set(this.queryParams,"kgName",this.kgList[i].kgName);
this.$set(this.queryParams,"kgCode",this.kgList[i].kgCode);
}
}
this.$set(this.edgeProp, "space", this.queryParams.kgCode);
this.$set(this.edgeProp, "type", "edge");
this.$set(this.edgeProp, "tagEdgeName", this.edgeProp.newRel);
this.$set(this.edgeProp, "tagEdgeComment", this.edgeProp.newRel);
this.$set(this.edgeProp, "color", "string");
this.$set(this.edgeProp, "srcVid", this.edgeProp.id1);
this.$set(this.edgeProp, "dstVid", this.edgeProp.id2);
this.$set(this.edgeProp, "propertyType", "string");
this.$set(this.edgeProp, "isNull", "NULL");
this.$set(this.edgeProp, "defaultValue", "");
let propertyList = [
{
"propertyName": "name",
"propertyType": "string",
"propertyComment": this.edgeProp.newRel,
"isNull": "NULL",
"defaultValue": "NULL"
}
]
this.$set(this.edgeProp, "propertyList", propertyList);
console.log("this.edgeProp==================================");
console.log(this.edgeProp);
//return false;
this.loading = true;
if(this.edgeProp.opType === "新增关系"){
axios.post("kg/base/editnebulaedge",
{
"edgeProp":this.edgeProp,
},
{
headers: {
'Authorization': 'Bearer ' + getToken()
}
},
)
.then(res => {
//console.log("res_editneo4jedge================");
//console.log(res);
if (res.status === 200) {
//console.log(res.data);
this.$modal.msgSuccess("新增关系成功!");
this.$set(this.nodeProp, "id", "");
this.$set(this.nodeProp, "name", "");
this.$set(this.nodeProp, "label", "");
this.$set(this.nodeProp, "props", [{}]);
this.labelDisabled = false;
this.btnDeleteNode = false;
this.$set(this.edgeProp, "id1", "");
this.$set(this.edgeProp, "label1", "");
this.$set(this.edgeProp, "name1", "");
this.$set(this.edgeProp, "id2", "");
this.$set(this.edgeProp, "label2", "");
this.$set(this.edgeProp, "name2", "");
this.$set(this.edgeProp, "newRel", "");
this.$set(this.edgeProp, "deleteRel", "");
this.$set(this.edgeProp, "opType", "新增关系");
//操作完成,刷新页面,载入新数据
//window.location.reload();
const url = new URL(window.location.href);
url.searchParams.set("kgId", this.kgId);
window.location.href = url.toString();
console.log("href======="+window.location.href);
//this.loading = false;
}
});
}else{
let modalIds = this.edgeProp.deleteRel;
console.log("modalIds========="+modalIds);
console.log(this.edgeProp);
let that = this;
this.$modal.confirm('是否确认删除关系为"' + modalIds + '"的数据项?').then(function() {
console.log("after confirm=====");
axios.post("kg/base/editnebulaedge",
{
"edgeProp":that.edgeProp,
},
{
headers: {
'Authorization': 'Bearer ' + getToken()
}
},
)
.then(res => {
//console.log("res_editneo4jedge================");
//console.log(res);
if (res.status === 200) {
console.log(res.data);
that.$modal.msgSuccess("删除关系成功!");
that.$set(that.nodeProp, "id", "");
that.$set(that.nodeProp, "name", "");
that.$set(that.nodeProp, "label", "");
that.$set(that.nodeProp, "props", [{}]);
that.labelDisabled = false;
that.btnDeleteNode = false;
that.$set(that.edgeProp, "id1", "");
that.$set(that.edgeProp, "label1", "");
that.$set(that.edgeProp, "name1", "");
that.$set(that.edgeProp, "id2", "");
that.$set(that.edgeProp, "label2", "");
that.$set(that.edgeProp, "name2", "");
that.$set(that.edgeProp, "newRel", "");
that.$set(that.edgeProp, "deleteRel", "");
that.$set(that.edgeProp, "opType", "新增关系");
//操作完成,刷新页面,载入新数据
//window.location.reload();
const url = new URL(window.location.href);
url.searchParams.set("kgId", that.kgId);
window.location.href = url.toString();
//that.loading = false;
}
});
}).then(() => {
//this.getList();
//that.$modal.msgSuccess("删除关系成功");
}).catch(() => {});
}
},
submitEditNode(){
console.log(this.nodeProp);
console.log(this.kgList);
for(let i=0; i<this.kgList.length; i++){
if(parseInt(this.kgId, 10) === parseInt(this.kgList[i].kgId, 10)){
this.$set(this.queryParams,"kgId",parseInt(this.kgId, 10));
this.$set(this.queryParams,"kgName",this.kgList[i].kgName);
this.$set(this.queryParams,"kgCode",this.kgList[i].kgCode);
}
}
this.$set(this.nodeProp, "space", this.queryParams.kgCode);
this.$set(this.nodeProp, "tagName", this.nodeProp.label);
this.$set(this.nodeProp, "pointName", this.nodeProp.name);
//return false;
let id = this.nodeProp.id;
console.log("add");
this.loading = true;
axios.post("kg/base/addnebulanode",
{
"nodeProp":this.nodeProp,
},
{
headers: {
'Authorization': 'Bearer ' + getToken()
}
},
)
.then(res => {
//console.log("res_addneo4jnode================");
//console.log(res);
if (res.status === 200) {
//console.log(res.data);
this.$modal.msgSuccess("添加成功!");
this.$set(this.nodeProp, "id", "");
this.$set(this.nodeProp, "name", "");
this.$set(this.nodeProp, "label", "");
this.$set(this.nodeProp, "props", [{}]);
this.labelDisabled = false;
this.btnDeleteNode = false;
//操作完成,刷新页面,载入新数据
//window.location.reload();
const url = new URL(window.location.href);
url.searchParams.set("kgId", this.kgId);
window.location.href = url.toString();
//this.loading = false;
}
});
},
submitDeleteNode() {
const modalIds = this.nodeProp.id;
const modalName = this.nodeProp.name;
console.log(this.nodeProp);
for(let i=0; i<this.kgList.length; i++){
if(parseInt(this.kgId, 10) === parseInt(this.kgList[i].kgId, 10)){
this.$set(this.queryParams,"kgId",parseInt(this.kgId, 10));
this.$set(this.queryParams,"kgName",this.kgList[i].kgName);
this.$set(this.queryParams,"kgCode",this.kgList[i].kgCode);
}
}
this.$set(this.nodeProp, "space", this.queryParams.kgCode);
this.$set(this.nodeProp, "vid", this.nodeProp.id);
console.log("nodeProp===============");
console.log(this.nodeProp);
//return false;
/*axios.post("kg/base/deletenebulanode",
{
"nodeProp":this.nodeProp,
},
{
headers: {
'Authorization': 'Bearer ' + getToken()
}
},
)
.then(res => {
console.log("res_deleteneo4jnode================");
console.log(res);
if (res.status === 200) {
//console.log(res.data);
this.$modal.msgSuccess("删除成功!");
this.$set(this.nodeProp, "id", "");
this.$set(this.nodeProp, "name", "");
this.$set(this.nodeProp, "label", "");
this.$set(this.nodeProp, "props", [{}]);
this.labelDisabled = false;
this.btnDeleteNode = false;
//操作完成,刷新页面,载入新数据
//window.location.reload();
}
});*/
let that = this;
this.$modal.confirm('是否确认删除编号为"' + modalIds + '('+modalName+')"的数据项?').then(function() {
console.log("in confirm");
console.log(that.nodeProp);
axios.post("kg/base/deletenebulanode",
{
"nodeProp":that.nodeProp,
},
{
headers: {
'Authorization': 'Bearer ' + getToken()
}
},
)
.then(res => {
console.log("res_deleteneo4jnode================");
console.log(res);
if (res.status === 200) {
//console.log(res.data);
that.$modal.msgSuccess("删除成功!");
that.$set(that.nodeProp, "id", "");
that.$set(that.nodeProp, "name", "");
that.$set(that.nodeProp, "label", "");
that.$set(that.nodeProp, "props", [{}]);
that.labelDisabled = false;
that.btnDeleteNode = false;
//操作完成,刷新页面,载入新数据
//window.location.reload();
const url = new URL(window.location.href);
url.searchParams.set("kgId", that.kgId);
window.location.href = url.toString();
}
});
}).then(() => {
//this.getList();
//this.$modal.msgSuccess("删除成功");
}).catch(() => {});
},
chgRelOp(chkName) {
//console.log("chkName=========="+chkName)
if(chkName === "新增关系"){
this.relOpFlag = true;
}else{
console.log("this.nebulaEdgeList=================");
console.log(this.nebulaEdgeList);
/*axios.post("kg/base/getnebulaedge",
{
"edgeProp":this.edgeProp,
},
{
headers: {
'Authorization': 'Bearer ' + getToken()
}
},
)
.then(res => {
//console.log("res_getneo4jedge================");
//console.log(res);
if (res.status === 200) {
//console.log(res.data);
let edges = res.data.edges;
this.edgeList = edges;
}
});*/
let edgeNodeArr = [];
edgeNodeArr.push(this.edgeProp.id1);
edgeNodeArr.push(this.edgeProp.id2);
console.log("edgeNodeArr============");
console.log(edgeNodeArr);
let edgeTypeArr = [];
for(let k=0; k<this.nebulaEdgeList.length; k++){
let tmpEdge = this.nebulaEdgeList[k];
console.log("tmpEdge["+k+"]:");
console.log(tmpEdge);
console.log(tmpEdge.source + "--" + tmpEdge.target);
let tmpSource = tmpEdge.source;
let tmpTarget = tmpEdge.target;
if(edgeNodeArr.indexOf(tmpSource) !== -1 && edgeNodeArr.indexOf(tmpTarget) !== -1){
let tmpObj = {};
this.$set(tmpObj, "key", tmpEdge.label);
this.$set(tmpObj, "value", tmpEdge.label);
edgeTypeArr.push(tmpObj);
}
this.edgeList = edgeTypeArr;
}
this.relOpFlag = false;
}
},
handleClick(tab, event) {
//console.log(this.activeName);
this.nodeProp = {"id":"","name":"","label":"","props":[{"key":"","value":""},{"key":"","value":""}]};
this.edgeProp = {"opType":"新增关系"};
},
resetEditEdge() {
this.edgeProp = {"opType":"新增关系"};
},
resetEditNode() {
this.nodeProp = {"id":"","name":"","label":"","props":[{"key":"","value":""},{"key":"","value":""}]};
this.btnDeleteNode = false;
this.labelDisabled = false;
},
/**
* 按图谱名称(kgId)可视化图谱
*/
handleQuery() {
//清除画布
if(this.graph){
this.graph.destroy();
}
console.log("handleQuery______________00000");
//重置参数
this.$route.query.kgId = this.queryParams.kgId;
this.kgId = this.queryParams.kgId;
console.log("handleQuery______________11111");
//绘制关系图
this.initGraph();
},
resetQuery() {
this.queryParams.kgName = undefined;
this.queryParams.entity = undefined;
this.queryParams.relation = undefined;
this.queryParams.property = undefined;
}
}
}
</script>
<style lang="scss">
@keyframes ant-line {
to {
stroke-dashoffset: -1000
}
}
</style>
<style lang="scss" scoped="scoped">
.all{
border-radius: 8px;
overflow: hidden;
}
.antv-content{
//background: #0A2052;
display: flex;
overflow: hidden;
position: relative;
.antv-menu{
width: 200px;
//border-right: 1px solid #196AA1;
padding: 10px;
h3{
padding: 10px;
};
li{
padding: 10px;
border-radius: 8px;
border: 1px solid #555;
background: #196AA1;
margin: 5px 10px;
font-size: 12px;
display: flex;
align-items: center;
cursor: pointer;
transition: all 0.5s ease;
&:hover{
box-shadow: 0 0 5px rgba($color: #000000, $alpha: 0.3);
}
i{
font-size: 18px;
margin-right: 10px;
}
strong{
flex: 1;
}
}
}
.antv-wrapper{
flex: 1;
position: relative;
.wrapper-canvas{
position: relative;
height: 100vh;
min-height: 720px;
}
.wrapper-tips{
padding: 10px;
display: flex;
align-items: center;
position: absolute;
top: 0;
left: 0;
.wrapper-tips-item{
span{
padding-left: 10px;
font-size: 12px;
}
}
}
}
}
i.icon-oval{
display: inline-block;
width: 16px;
height: 10px;
border-radius: 10px;
border: 2px solid #eee;
}
i.icon-square{
display: inline-block;
width: 16px;
height: 10px;
border: 2px solid #eee;
}
i.icon-ysquare{
display: inline-block;
width: 16px;
height: 10px;
border-radius: 4px;
border: 2px solid #eee;
}
i.icon-rhombus{
display: inline-block;
width: 10px;
height: 10px;
border: 2px solid #eee;
transform: rotate(45deg);
}
i.icon-rhomboid{
display: inline-block;
width: 10px;
height: 10px;
border: 2px solid #eee;
transform: skew(-30deg);
}
i.icon-circle{
display: inline-block;
width: 16px;
height: 16px;
border-radius: 16px;
border: 2px solid #eee;
}
.edit-main{
position: absolute;
right: 0;
top: 0;
height: 100%;
width: 280px;
border-left: 1px solid #196AA1;
box-shadow: 0 -10px 10px rgba($color: #000000, $alpha: 0.3);
padding: 20px;
//background: #fff;
background: #0A2052;
box-sizing: border-box;
.edit-main-title{
display: flex;
justify-content: space-between;
align-items: center;
h3{
flex: 1;
}
i{
cursor: pointer;
font-size: 20px;
opacity: 0.7;
&:hover{
opacity: 1;
}
}
}
.form-main{
padding: 20px 0;
.label-style{
background: #f2f2f2;
padding: 0 10px;
p{
display: flex;
align-items: center;
font-size: 12px;
}
}
}
.edit-btn{
}
.see-box{
padding: 20px;
background: #0A2052;
h5{
padding-bottom: 10px;
}
.see-item{
padding: 10px 30px;
border: 2px solid #333;
text-align: center;
}
}
}
.wrapper-btn{
text-align: center;
padding: 20px;
button{
width: 100%;
}
}
</style>