旧接口字段更新BUG修复
This commit is contained in:
@@ -7,42 +7,73 @@
|
||||
- that was distributed with this source code.
|
||||
-->
|
||||
<template>
|
||||
<div class="rule-knowledge-graph">
|
||||
<div
|
||||
ref="graphShellRef"
|
||||
class="rule-knowledge-graph"
|
||||
:class="{
|
||||
'rule-knowledge-graph--four-blocks': density === 'four-blocks',
|
||||
'rule-knowledge-graph--fullscreen': isFullscreen,
|
||||
}"
|
||||
>
|
||||
<div class="rule-knowledge-graph__toolbar">
|
||||
<a-radio-group v-model:value="density" size="small" button-style="solid">
|
||||
<a-radio-button value="overview">简要结构</a-radio-button>
|
||||
<a-radio-button value="full">完整</a-radio-button>
|
||||
<a-radio-button value="four-blocks">四块分区</a-radio-button>
|
||||
</a-radio-group>
|
||||
<span class="rule-knowledge-graph__hint">
|
||||
简要:仅层级→种类→模块→规则;完整:含参数、任务类型与执行顺序边
|
||||
<span v-if="density !== 'four-blocks'" class="rule-knowledge-graph__hint">
|
||||
简要:仅层级→种类→模块→规则;完整:含参数、任务类型与执行顺序边;四块:业务运算步骤 + 规则项 + 参数(与 globalParams 一致)
|
||||
</span>
|
||||
<span v-else class="rule-knowledge-graph__hint rule-knowledge-graph__hint--compact">
|
||||
四宫格 · 拖拽画布 / 滚轮缩放
|
||||
</span>
|
||||
<a-button type="default" size="small" class="rule-knowledge-graph__fullscreen-btn" @click="toggleFullscreen">
|
||||
<template #icon>
|
||||
<FullscreenExitOutlined v-if="isFullscreen" />
|
||||
<FullscreenOutlined v-else />
|
||||
</template>
|
||||
{{ isFullscreen ? '退出全屏' : '全屏' }}
|
||||
</a-button>
|
||||
</div>
|
||||
<div v-if="errorMsg" class="rule-knowledge-graph__banner rule-knowledge-graph__banner--error">
|
||||
{{ errorMsg }}
|
||||
</div>
|
||||
<div v-else-if="emptyHint" class="rule-knowledge-graph__banner">
|
||||
{{ emptyHint }}
|
||||
</div>
|
||||
<div v-show="!errorMsg && !emptyHint" ref="hostRef" class="rule-knowledge-graph__host" />
|
||||
<RuleFourBlocksPanel v-if="density === 'four-blocks'" :refresh-key="refreshKey" />
|
||||
<template v-else>
|
||||
<div v-if="errorMsg" class="rule-knowledge-graph__banner rule-knowledge-graph__banner--error">
|
||||
{{ errorMsg }}
|
||||
</div>
|
||||
<div v-else-if="emptyHint" class="rule-knowledge-graph__banner">
|
||||
{{ emptyHint }}
|
||||
</div>
|
||||
<div v-show="!errorMsg && !emptyHint" ref="hostRef" class="rule-knowledge-graph__host" />
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { FullscreenExitOutlined, FullscreenOutlined } from '@ant-design/icons-vue';
|
||||
import { Graph } from '@antv/g6';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { nextTick, onBeforeUnmount, ref, watch } from 'vue';
|
||||
import { nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue';
|
||||
import { findRuleConfigGraph } from './api';
|
||||
import RuleFourBlocksPanel from './RuleFourBlocksPanel.vue';
|
||||
import type { RuleConfigRequest, RuleGraphEdge, RuleGraphNode, RuleGraphPayload } from './types';
|
||||
|
||||
type RuleGraphDensityMode = 'overview' | 'full' | 'four-blocks';
|
||||
|
||||
const emit = defineEmits<{
|
||||
densityChange: [RuleGraphDensityMode],
|
||||
}>();
|
||||
|
||||
const props = defineProps<{
|
||||
query: RuleConfigRequest,
|
||||
refreshKey: number,
|
||||
}>();
|
||||
|
||||
const graphShellRef = ref<HTMLElement | null>(null);
|
||||
const hostRef = ref<HTMLDivElement | null>(null);
|
||||
const isFullscreen = ref(false);
|
||||
const errorMsg = ref<string | null>(null);
|
||||
const emptyHint = ref<string | null>(null);
|
||||
const density = ref<'overview' | 'full'>('overview');
|
||||
const density = ref<RuleGraphDensityMode>('overview');
|
||||
const lastPayload = ref<RuleGraphPayload | null>(null);
|
||||
|
||||
let graph: Graph | null = null;
|
||||
@@ -127,6 +158,48 @@ const toGraphData = (payload: RuleGraphPayload) => ({
|
||||
})),
|
||||
});
|
||||
|
||||
const syncFullscreenState = () => {
|
||||
const el = graphShellRef.value;
|
||||
isFullscreen.value = Boolean(el && document.fullscreenElement === el);
|
||||
};
|
||||
|
||||
const refitMainGraphAfterLayout = () => {
|
||||
if (!graph || !hostRef.value) {
|
||||
return;
|
||||
}
|
||||
const w = Math.max(hostRef.value.clientWidth, 280);
|
||||
const h = Math.max(hostRef.value.clientHeight, 240);
|
||||
graph.setSize(w, h);
|
||||
void graph.fitView();
|
||||
};
|
||||
|
||||
const onFullscreenChange = () => {
|
||||
syncFullscreenState();
|
||||
void nextTick(() => {
|
||||
refitMainGraphAfterLayout();
|
||||
window.dispatchEvent(new Event('resize'));
|
||||
window.setTimeout(() => {
|
||||
refitMainGraphAfterLayout();
|
||||
}, 120);
|
||||
});
|
||||
};
|
||||
|
||||
const toggleFullscreen = async () => {
|
||||
const el = graphShellRef.value;
|
||||
if (!el) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (document.fullscreenElement === el) {
|
||||
await document.exitFullscreen();
|
||||
} else {
|
||||
await el.requestFullscreen();
|
||||
}
|
||||
} catch {
|
||||
message.warning('无法切换全屏,请检查浏览器权限或使用 Chrome / Edge');
|
||||
}
|
||||
};
|
||||
|
||||
const disposeGraph = async () => {
|
||||
resizeObserver?.disconnect();
|
||||
resizeObserver = null;
|
||||
@@ -229,6 +302,9 @@ const buildGraph = async (payload: RuleGraphPayload, mode: 'overview' | 'full')
|
||||
};
|
||||
|
||||
const renderFromCache = async () => {
|
||||
if (density.value === 'four-blocks') {
|
||||
return;
|
||||
}
|
||||
if (!lastPayload.value) {
|
||||
return;
|
||||
}
|
||||
@@ -236,7 +312,8 @@ const renderFromCache = async () => {
|
||||
return;
|
||||
}
|
||||
await nextTick();
|
||||
await buildGraph(lastPayload.value, density.value);
|
||||
const mode: 'overview' | 'full' = density.value === 'full' ? 'full' : 'overview';
|
||||
await buildGraph(lastPayload.value, mode);
|
||||
};
|
||||
|
||||
const load = async () => {
|
||||
@@ -278,16 +355,34 @@ const load = async () => {
|
||||
watch(
|
||||
() => [props.query.pageNum, props.query.pageSize, props.refreshKey],
|
||||
() => {
|
||||
if (density.value === 'four-blocks') {
|
||||
return;
|
||||
}
|
||||
void load();
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
watch(density, () => {
|
||||
void renderFromCache();
|
||||
watch(density, async (mode, prev) => {
|
||||
emit('densityChange', mode);
|
||||
if (mode === 'four-blocks') {
|
||||
await disposeGraph();
|
||||
return;
|
||||
}
|
||||
if (prev === 'four-blocks') {
|
||||
await load();
|
||||
return;
|
||||
}
|
||||
await renderFromCache();
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
syncFullscreenState();
|
||||
document.addEventListener('fullscreenchange', onFullscreenChange);
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
document.removeEventListener('fullscreenchange', onFullscreenChange);
|
||||
void disposeGraph();
|
||||
});
|
||||
</script>
|
||||
@@ -311,6 +406,14 @@ onBeforeUnmount(() => {
|
||||
padding: 0 2px 4px;
|
||||
}
|
||||
|
||||
.rule-knowledge-graph__fullscreen-btn {
|
||||
margin-left: auto;
|
||||
flex-shrink: 0;
|
||||
color: #b8ccd6;
|
||||
border-color: rgba(120, 170, 200, 0.45);
|
||||
background: rgba(10, 28, 40, 0.65);
|
||||
}
|
||||
|
||||
.rule-knowledge-graph__hint {
|
||||
font-size: 11px;
|
||||
color: #7a8d96;
|
||||
@@ -318,6 +421,35 @@ onBeforeUnmount(() => {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.rule-knowledge-graph__hint--compact {
|
||||
font-size: 10px;
|
||||
color: #6d8290;
|
||||
}
|
||||
|
||||
.rule-knowledge-graph--four-blocks {
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.rule-knowledge-graph--fullscreen {
|
||||
box-sizing: border-box;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
max-height: 100vh;
|
||||
padding: 10px 12px;
|
||||
gap: 10px;
|
||||
background: #0d1f2c;
|
||||
}
|
||||
|
||||
.rule-knowledge-graph--fullscreen .rule-knowledge-graph__host {
|
||||
min-height: 0;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.rule-knowledge-graph--fullscreen :deep(.rule-four-blocks) {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.rule-knowledge-graph__host {
|
||||
flex: 1;
|
||||
min-height: 200px;
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
import { HttpRequestClient } from '@/utils/request';
|
||||
import type { ApiDataResponse, BasicResponse } from '@/types';
|
||||
import type { RuleConfig, RuleConfigPageableResponse, RuleConfigRequest, RuleDictItem, RuleGraphPayload, RuleParamMeta } from './types';
|
||||
import type { RuleConfig, RuleConfigPageableResponse, RuleConfigRequest, RuleDictItem, RuleFourBlocksPayload, RuleGraphPayload, RuleParamMeta } from './types';
|
||||
|
||||
const req = HttpRequestClient.create<BasicResponse>({
|
||||
baseURL: '/api',
|
||||
@@ -46,3 +46,7 @@ export const findRuleParamMeta = (): Promise<ApiDataResponse<RuleParamMeta[]>> =
|
||||
export const findRuleConfigGraph = (query: Partial<RuleConfigRequest> = {}): Promise<ApiDataResponse<RuleGraphPayload>> => {
|
||||
return req.get('/system/rule/config/graph', query);
|
||||
};
|
||||
|
||||
export const findRuleFourBlocksGraph = (): Promise<ApiDataResponse<RuleFourBlocksPayload>> => {
|
||||
return req.get('/system/rule/config/graph/four-blocks');
|
||||
};
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
maxWidth: '78%',
|
||||
}"
|
||||
>
|
||||
<RuleKnowledgeGraph :query="query" :refresh-key="graphRevision" />
|
||||
<RuleKnowledgeGraph :query="query" :refresh-key="graphRevision" @density-change="onRuleGraphDensityChange" />
|
||||
</div>
|
||||
<div
|
||||
class="rule-config-main-split-resizer"
|
||||
@@ -251,12 +251,27 @@ const GRAPH_PANE_STORAGE_KEY = 'rule-config-graph-pane-percent';
|
||||
const GRAPH_PANE_MIN = 18;
|
||||
const GRAPH_PANE_MAX = 70;
|
||||
const graphPanePercent = ref(32);
|
||||
const graphPanePercentBeforeFourBlocks = ref<number | null>(null);
|
||||
const splitRootRef = ref<HTMLElement | null>(null);
|
||||
const graphRevision = ref(0);
|
||||
|
||||
const clampGraphPercent = (n: number) =>
|
||||
Math.min(GRAPH_PANE_MAX, Math.max(GRAPH_PANE_MIN, Math.round(n)));
|
||||
|
||||
const onRuleGraphDensityChange = (mode: 'overview' | 'full' | 'four-blocks') => {
|
||||
if (mode === 'four-blocks') {
|
||||
if (graphPanePercentBeforeFourBlocks.value === null) {
|
||||
graphPanePercentBeforeFourBlocks.value = graphPanePercent.value;
|
||||
}
|
||||
graphPanePercent.value = clampGraphPercent(62);
|
||||
return;
|
||||
}
|
||||
if (graphPanePercentBeforeFourBlocks.value !== null) {
|
||||
graphPanePercent.value = clampGraphPercent(graphPanePercentBeforeFourBlocks.value);
|
||||
graphPanePercentBeforeFourBlocks.value = null;
|
||||
}
|
||||
};
|
||||
|
||||
const onGraphSplitMouseDown = (e: MouseEvent) => {
|
||||
e.preventDefault();
|
||||
const root = splitRootRef.value;
|
||||
|
||||
@@ -105,4 +105,33 @@ export interface RuleGraphEdge {
|
||||
export interface RuleGraphPayload {
|
||||
nodes: RuleGraphNode[],
|
||||
edges: RuleGraphEdge[],
|
||||
layoutHint?: string | null,
|
||||
focusNodeId?: string | null,
|
||||
}
|
||||
|
||||
export interface RuleFourBlockParamRow {
|
||||
ruleCode: NullableString,
|
||||
ruleName: NullableString,
|
||||
paramKey: NullableString,
|
||||
paramName: NullableString,
|
||||
whenText: NullableString,
|
||||
thenText: NullableString,
|
||||
outputText: NullableString,
|
||||
}
|
||||
|
||||
export interface RuleFourBlockCluster {
|
||||
blockId: string,
|
||||
moduleCode: NullableString,
|
||||
title: NullableString,
|
||||
droolsRuleName: NullableString,
|
||||
salience: number | null,
|
||||
whenExpr: NullableString,
|
||||
thenAction: NullableString,
|
||||
graph: RuleGraphPayload,
|
||||
paramRows?: RuleFourBlockParamRow[] | null,
|
||||
}
|
||||
|
||||
export interface RuleFourBlocksPayload {
|
||||
globalParamsPreview: Record<string, unknown>,
|
||||
blocks: RuleFourBlockCluster[],
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user