旧接口字段更新BUG修复

This commit is contained in:
MHW
2026-04-20 09:38:58 +08:00
parent 39b04d8b73
commit ae01a2aa01
9 changed files with 509 additions and 36 deletions

View File

@@ -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;

View File

@@ -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');
};

View File

@@ -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;

View File

@@ -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[],
}