feat: 拓扑编辑数据存储功能

This commit is contained in:
TsMask
2024-01-04 15:54:22 +08:00
parent 2ff313e575
commit 7e2f404ba7
8 changed files with 436 additions and 2523 deletions

View File

@@ -0,0 +1,40 @@
import { request } from '@/plugins/http-fetch';
/**图组名称信息 */
export function getGraphGroups() {
return request({
url: '/chart/graph/groups',
method: 'get',
});
}
/**获取图数据 */
export function getGraphData(group: string) {
return request({
url: '/chart/graph',
method: 'get',
params: {
group,
},
});
}
/**保存图数据 */
export function saveGraphData(group: string, data: Record<string, any>) {
return request({
url: '/chart/graph',
method: 'post',
data: {
group,
data,
},
});
}
/**删除图组数据 */
export function delGraphData(group: string) {
return request({
url: `/chart/graph/${group}`,
method: 'delete',
});
}

File diff suppressed because it is too large Load Diff

View File

@@ -43,7 +43,7 @@ const {
watch(graphEvent, v => {
if (!v) return;
const { type, target, item } = v;
console.log(type, target, item);
// console.log(type, target, item);
//
if (type === 'canvas-create-edge') {

View File

@@ -39,7 +39,7 @@ export const selectSourceTargetOptions = ref<Record<string, any>[]>([]);
export const selectComboOptions = ref<Record<string, any>[]>([]);
/**图模式选择项 */
export const graphMode = ref<string>('default');
export const graphMode = ref<'default' | 'edit'>('default');
export default function useGraph() {
//实例化i18n
@@ -51,7 +51,6 @@ export default function useGraph() {
offseY: 10,
itemTypes: ['canvas'],
getContent(evt) {
console.log(evt);
if (!evt) return '无';
const edit = graphMode.value === 'edit';
return `
@@ -125,26 +124,32 @@ export default function useGraph() {
offseY: 10,
itemTypes: ['combo'],
getContent(evt) {
console.log(evt);
if (!evt) return '无';
const item = evt.item?.getModel();
const edit = graphMode.value === 'edit';
return `
<div
style="
display: flex;
flex-direction: column;
width: 140px;
background: #e6f7ff;
"
>
<h3></h3>
<div id="edit" style="cursor: pointer; margin-bottom: 2px">
1.
</div>
<div id="delete" style="cursor: pointer; margin-bottom: 2px">
2.
</div>
<h3 style="margin-bottom: 8px">分组:${item?.label || item?.id}</h3>
<div id="hide" style="cursor: pointer; margin-bottom: 2px">
2.
</div>
<div id="edit" style="cursor: pointer; margin-bottom: 2px;display: ${
edit ? 'black' : 'none'
}">
</div>
<div id="delete" style="cursor: pointer; margin-bottom: 2px; display: ${
edit ? 'black' : 'none'
}">
</div>
</div>
`;
},
@@ -172,7 +177,6 @@ export default function useGraph() {
offseY: 10,
itemTypes: ['node'],
getContent(evt) {
console.log(evt);
if (!evt) return '无';
const item = evt.item?.getModel();
const edit = graphMode.value === 'edit';
@@ -182,19 +186,18 @@ export default function useGraph() {
display: flex;
flex-direction: column;
width: 140px;
background: #e6f7ff;
"
>
<h3>节点:${item?.label || '无标签'}</h3>
<div id="hide" style="cursor: pointer; margin-bottom: 2px">
<h3 style="margin-bottom: 8px">节点:${item?.label || item?.id}</h3>
<div id="hide" style="cursor: pointer; margin-bottom: 4px">
</div>
<div id="edit" style="cursor: pointer; margin-bottom: 2px; display: ${
<div id="edit" style="cursor: pointer; margin-bottom: 4px; display: ${
edit ? 'black' : 'none'
}">
</div>
<div id="delete" style="cursor: pointer; margin-bottom: 2px; display: ${
<div id="delete" style="cursor: pointer; margin-bottom: 4px; display: ${
edit ? 'black' : 'none'
}">
@@ -225,23 +228,17 @@ export default function useGraph() {
offsetX: 10,
offsetY: 20,
getContent(evt) {
console.log(evt);
if (!evt) return '无';
const item = evt.item?.getModel();
if (!item?.label) {
return '无标签';
}
console.log(item);
return `
<div
style="
display: flex;
flex-direction: column;
width: 140px;
background: #e6f7ff;
"
>
<h3> ${item?.label}</h3>
<h3>节点:${item?.label || item?.id}</h3>
</div>
`;
},
@@ -254,7 +251,6 @@ export default function useGraph() {
offseY: 10,
itemTypes: ['edge'],
getContent(evt) {
console.log(evt);
if (!evt) return '无';
const item = evt.item?.getModel();
const edit = graphMode.value === 'edit';
@@ -264,19 +260,18 @@ export default function useGraph() {
display: flex;
flex-direction: column;
width: 140px;
background: #e6f7ff;
"
>
<strong>:${item?.label || '无标签'}</strong>
<div id="hide" style="cursor: pointer; margin-bottom: 2px">
<h3 style="margin-bottom: 8px">:${item?.label || '无标签'}</h3>
<div id="hide" style="cursor: pointer; margin-bottom: 4px">
</div>
<div id="edit" style="cursor: pointer; margin-bottom: 2px; display: ${
<div id="edit" style="cursor: pointer; margin-bottom: 4px; display: ${
edit ? 'black' : 'none'
}">
</div>
<div id="delete" style="cursor: pointer; margin-bottom: 2px; display: ${
<div id="delete" style="cursor: pointer; margin-bottom: 4px; display: ${
edit ? 'black' : 'none'
}">
@@ -306,22 +301,17 @@ export default function useGraph() {
offsetX: 10,
offsetY: 20,
getContent(evt) {
console.log(evt);
if (!evt) return '无';
const item = evt.item?.getModel();
if (!item?.label) {
return '无标签';
}
return `
<div
style="
display: flex;
flex-direction: column;
width: 140px;
background: #e6f7ff;
"
>
<h3> ${item?.label}</h3>
<h3> ${item?.label || '无标签'}</h3>
</div>
`;
},
@@ -420,7 +410,10 @@ export default function useGraph() {
}
/**图数据渲染 */
function handleRanderGraph(container: HTMLElement, data: GraphData) {
function handleRanderGraph(
container: HTMLElement | undefined,
data: GraphData
) {
if (!container) return;
const { clientHeight, clientWidth } = container;
@@ -569,7 +562,7 @@ export default function useGraph() {
/**图模式改变 default | edit */
function handleChangeMode(value: any) {
console.log(value, JSON.parse(JSON.stringify(graphG6.value.save())));
// console.log(value, JSON.parse(JSON.stringify(graphG6.value.save())));
graphG6.value.setMode(value);
graphMode.value = graphG6.value.getCurrentMode();
}

View File

@@ -33,10 +33,10 @@ export const nodeTypeOptions = [
value: 'image',
label: '图片',
},
{
value: 'donut',
label: '面包圈',
},
// {
// value: 'donut',
// label: '面包圈',
// },
];
/**图节点标签文本位置 */

View File

@@ -0,0 +1,359 @@
<script setup lang="ts">
import { reactive, onMounted, ref, toRaw } from 'vue';
import { PageContainer } from 'antdv-pro-layout';
import useI18n from '@/hooks/useI18n';
import GraphEditModal from './components/GraphEditModal.vue';
import useGraph, {
graphG6,
graphMode,
graphModeOptions,
} from './/hooks/useGraph';
import {
delGraphData,
getGraphData,
getGraphGroups,
saveGraphData,
} from '@/api/monitor/topology';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import { Form, Modal, message } from 'ant-design-vue/lib';
const { t } = useI18n();
const { handleRanderGraph, handleChangeMode } = useGraph();
/**图DOM节点实例对象 */
const graphG6Dom = ref<HTMLElement | undefined>(undefined);
/**图状态 */
const graphState = reactive<Record<string, any>>({
/**当前图组名 */
group: '',
groupOptions: [],
/**图数据 */
data: {
combos: [],
edges: [],
nodes: [],
},
});
/**
* 图组变更
* @param value 变更值
*/
function fnGraphGroupChange(value: any) {
if (value) {
fnGraphDataLoad(true);
}
}
/**
* 获取图组名称数据
* @param reload 是否重载数据
*/
function fnGraphDataGroups(reload: boolean = false) {
getGraphGroups()
.then(res => {
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
graphState.groupOptions = res.data.map(str => {
return { label: str, value: str };
});
// 非重载
if (!reload) {
graphState.group = res.data[0];
fnGraphDataLoad(false);
}
}
})
.finally(() => {
if (!graphState.group) {
handleRanderGraph(graphG6Dom.value, graphState.data);
message.warning({
content: '暂无图组数据,可使用【图模式-编辑】进行新增图组',
duration: 5,
});
}
});
}
/**
* 获取图组数据渲染到画布
* @param reload 是否重载数据
*/
function fnGraphDataLoad(reload: boolean = false) {
getGraphData(graphState.group)
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
graphState.data = res.data;
}
})
.finally(() => {
// 重载数据
if (reload) {
graphG6.value.read(graphState.data);
} else {
handleRanderGraph(graphG6Dom.value, graphState.data);
}
});
}
/**对话框对象信息状态类型 */
type ModalStateType = {
/**框是否显示 */
visible: boolean;
/**标题 */
title: string;
/**表单数据 */
form: Record<string, any>;
/**操作类型 */
type: 'save' | 'delete';
/**确定按钮 loading */
confirmLoading: boolean;
};
/**对话框对象信息状态 */
let modalState: ModalStateType = reactive({
visible: false,
title: '图组',
form: {
group: '',
},
type: 'save',
confirmLoading: false,
});
/**对话框内表单属性和校验规则 */
const modalStateFrom = Form.useForm(
modalState.form,
reactive({
group: [
{
required: true,
message: '图组不能为空',
},
],
})
);
/**
* 对话框弹出确认执行函数
* 进行表达规则校验
*/
function fnModalOk() {
const from = toRaw(modalState.form);
modalStateFrom
.validate()
.then(e => {
modalState.confirmLoading = true;
const hide = message.loading({ content: t('common.loading') });
// 根据类型选择函数
saveGraphData(from.group, graphG6.value.save())
.then((res: any) => {
if (res.code === RESULT_CODE_SUCCESS) {
message.success({
content: `保存图组【${from.group}】成功`,
duration: 3,
});
fnGraphDataGroups(true);
graphState.group = from.group;
} else {
message.error({
content: `保存图组【${from.group}】失败`,
duration: 3,
});
}
})
.finally(() => {
hide();
fnModalCancel();
modalState.confirmLoading = false;
});
})
.catch(e => {
message.error(t('common.errorFields', { num: e.errorFields.length }), 3);
});
}
/**
* 对话框弹出关闭执行函数
* 进行表达规则校验
*/
function fnModalCancel() {
modalState.type = 'save';
modalState.visible = false;
modalStateFrom.resetFields();
}
/**图组数据保存 */
function fnGraphDataSave() {
modalState.form.group = graphState.group;
modalState.type = 'save';
modalState.title = '图组信息保存';
modalState.visible = true;
}
/**图组数据删除 */
function fnGraphDataDelete() {
if (!graphState.group) {
handleRanderGraph(graphG6Dom.value, graphState.data);
message.warning({
content: '暂无图组数据,可使用【图模式-编辑】进行新增图组',
duration: 5,
});
}
Modal.confirm({
title: t('common.tipTitle'),
content: `确认要删除图组名为【${graphState.group}】的数据吗?`,
onOk() {
const hide = message.loading({ content: t('common.loading') });
delGraphData(graphState.group)
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
message.success({
content: t('common.msgSuccess', {
msg: t('common.deleteText'),
}),
duration: 3,
});
// 重加载图组数据
fnGraphDataGroups(true);
if (graphState.groupOptions.length > 0) {
graphState.group = graphState.groupOptions[0].value;
fnGraphDataLoad(true);
}
} else {
message.error({
content: `${res.msg}`,
duration: 3,
});
}
})
.finally(() => {
hide();
});
},
});
}
onMounted(() => {
fnGraphDataGroups();
});
</script>
<template>
<PageContainer>
<a-card
:bordered="false"
:body-style="{ marginBottom: '24px', paddingBottom: 0 }"
>
<!-- 表格搜索栏 -->
<a-form :model="graphState" name="graphState" layout="horizontal">
<a-row :gutter="16">
<a-col :lg="4" :md="12" :xs="24">
<a-form-item label="图模式" name="graphMode">
<a-select
:value="graphMode"
:options="graphModeOptions"
@change="handleChangeMode"
>
</a-select>
</a-form-item>
</a-col>
<a-col :lg="6" :md="12" :xs="24">
<a-form-item label="图组" name="group ">
<a-select
v-model:value="graphState.group"
:options="graphState.groupOptions"
:placeholder="t('common.selectPlease')"
@change="fnGraphGroupChange"
/>
</a-form-item>
</a-col>
</a-row>
</a-form>
</a-card>
<a-card :bordered="false" :body-style="{ padding: '0px' }">
<!-- 插槽-卡片左侧侧 -->
<template #title>
<div class="button-container" style="margin-bottom: -12px">
<span>图组名{{ graphState.group }}</span>
</div>
</template>
<!-- 插槽-卡片右侧 -->
<template #extra>
<div class="button-container" style="margin-bottom: -12px">
<template v-if="graphMode === 'edit'">
<a-button type="primary" size="small" @click="fnGraphDataSave">
<template #icon>
<SaveOutlined />
</template>
保存
</a-button>
<a-button
type="ghost"
danger
size="small"
@click="fnGraphDataDelete"
>
<template #icon>
<DeleteOutlined />
</template>
删除
</a-button>
</template>
</div>
</template>
<div ref="graphG6Dom" class="chart"></div>
</a-card>
<!-- 图元素修改框 -->
<GraphEditModal></GraphEditModal>
<!-- 图保存图组名修改框 -->
<a-modal
width="500px"
:keyboard="false"
:mask-closable="false"
:visible="modalState.visible"
:title="modalState.title"
:confirm-loading="modalState.confirmLoading"
@ok="fnModalOk"
@cancel="fnModalCancel"
>
<a-form
name="modalStateFrom"
layout="horizontal"
:label-col="{ span: 6 }"
:labelWrap="true"
>
<a-row :gutter="16">
<a-col :lg="24" :md="24" :xs="24">
<a-form-item
label="图组名"
name="group"
v-bind="modalStateFrom.validateInfos.group"
help="已存在图组名会更新图数据,不存在则新增图组"
>
<a-auto-complete
v-model:value="modalState.form.group"
:options="graphState.groupOptions"
allow-clear
placeholder="图组名"
/>
</a-form-item>
</a-col>
</a-row>
</a-form>
</a-modal>
</PageContainer>
</template>
<style lang="less" scoped>
.chart {
width: 100%;
height: calc(100vh - 300px);
background-color: rgb(43, 47, 51);
}
</style>