Browse Source

关系图谱布局

CzRger 7 months ago
parent
commit
2db1ce84a4

+ 1 - 0
src/stores/dictionary-define.ts

@@ -19,6 +19,7 @@ export const dictionaryDefine = {
 	sort_type: ['sortTypeList', 'sortTypeMap'], //  排序类型
 	date_type: ['dateTypeList', 'dateTypeMap'], //  日期类型
 	search_status: ['searchStatusList', 'searchStatusMap'], //  搜索状态
+	relation_chart_layout: ['relationChartLayoutList', 'relationChartLayoutMap'], //  关系图谱布局
 }
 
 const stateMap = {}

+ 12 - 1
src/views/manage/theme/relation-detail.vue

@@ -88,7 +88,17 @@
           limitNoUpload
           :delRule="(file) => true"
         />
-        <el-col :span="12"/>
+        <template v-if="state.form.isMain == 1">
+          <CusFormColumn
+            :span="12"
+            required
+            label="关系图谱默认布局"
+            v-model:param="state.form.chartLayout"
+            link="select"
+            :options="DictionaryStore.relationChartLayoutList"
+          />
+        </template>
+        <el-col v-else :span="12"/>
         <el-col :span="12">
           <div class="__cus-title_1">索引条件</div>
           <CusTable
@@ -325,6 +335,7 @@ const initDictionary = () => {
   DictionaryStore.initDict('condition_type')
   DictionaryStore.initDict('is_main_index')
   DictionaryStore.initDict('sort_type')
+  DictionaryStore.initDict('relation_chart_layout')
 }
 </script>
 

+ 22 - 3
src/views/web/archive/index.vue

@@ -52,9 +52,17 @@
           <div class="archive-list-block">
             <div class="archive-list-block-title">
               <SvgIcon class="flag" name="flag_1" color="var(--cus-main-color)"/>关系图谱
+              <div class="layout-select">
+                <CusFormColumn
+                  :span="24"
+                  v-model:param="state.chartData.layout"
+                  link="select"
+                  :options="DictionaryStore.relationChartLayoutList"
+                />
+              </div>
             </div>
             <div class="archive-list-block-page" v-loading="state.chartData.loading">
-              <RelationChart :data="state.chartData.data" @chartPage="onChartPage"/>
+              <RelationChart :data="state.chartData.data" :layout="state.chartData.layout" @chartPage="onChartPage"/>
             </div>
           </div>
           <div class="archive-list-block" v-if="state.chartTable.chartId">
@@ -104,11 +112,12 @@
 import {computed, getCurrentInstance, onMounted, reactive, watch} from "vue";
 import RelationChart from "./relation-chart.vue";
 import {ElLoading, ElMessage} from "element-plus";
-import {useAppStore, useThemeStore} from "@/stores";
+import {useAppStore, useDictionaryStore, useThemeStore} from "@/stores";
 import {frontGetThemeByThemeId} from "@/api/modules/web/list";
 import {useRoute} from "vue-router";
 
 const ThemeStore = useThemeStore()
+const DictionaryStore = useDictionaryStore()
 const AppStore = useAppStore()
 const route = useRoute()
 const {proxy} = getCurrentInstance()
@@ -144,7 +153,8 @@ const state: any = reactive({
     data: {
       nodes: [],
       edges: []
-    }
+    },
+    layout: 'radial'
   },
   ws: {
     instance: null,
@@ -205,6 +215,7 @@ const themeConfigCpt = computed(() => {
   }
   state.themeDetail.indexDtos?.forEach(v => {
     if (v.isMain == 1) {
+      state.chartData.layout = v.chartLayout
       res.main = {
         indexTableName: v.indexTableName,
         indexStyle: v.indexStyle ? JSON.parse(v.indexStyle) : '',
@@ -526,6 +537,7 @@ const initWS = () => {
 }
 onMounted(() => {
   initTheme()
+  DictionaryStore.initDict('relation_chart_layout')
 })
 </script>
 
@@ -682,6 +694,13 @@ onMounted(() => {
           .svg-icon {
             margin-right: 8px;
           }
+          :deep(.layout-select) {
+            margin-left: auto;
+            .el-form-item {
+              width: 150px;
+              margin-bottom: 0;
+            }
+          }
         }
         .archive-list-block-page {
           flex: 1;

+ 83 - 37
src/views/web/archive/relation-chart.vue

@@ -11,10 +11,12 @@ import {Graph} from "@antv/g6";
 const emit = defineEmits(['chartPage'])
 const {proxy} = getCurrentInstance()
 const props = defineProps({
-  data: {}
+  data: {},
+  layout: {default: 'radial'}
 })
 const state: any = reactive({
   resizeObserver: <any>null,
+  chart: null
 })
 const ref_main = ref()
 const setNodes = (data) => {
@@ -38,27 +40,94 @@ const initChart = () => {
     const data = JSON.parse(JSON.stringify(props.data))
     setNodes(data)
     setEdges(data)
-    console.log(data)
+    if (state.chart) {
+      state.chart.destroy()
+      state.chart = null
+    }
     const mainColor = getComputedStyle(document.documentElement).getPropertyValue('--cus-main-color')
+    const options = {
+      radial: {
+        layout: {
+          type: 'radial',
+          focusNode: '3',
+          linkDistance: 200,
+          preventOverlap: true,
+          unitRadius: 250,
+          maxPreventOverlapIteration: 100,
+          strictRadial: false
+        },
+        edge: {
+          style: {
+            endArrow: true,
+            endArrowType: 'triangle',
+            endArrowSize: 8,
+            stroke: mainColor,
+            labelAutoRotate: false,
+            labelFill: '#ffffff',
+            labelPadding: [4, 8, 2, 8],
+            labelBackground: true,
+            labelBackgroundFill: mainColor,
+            labelBackgroundRadius: 20,
+            labelFontSize: 12,
+            halo: true,
+            haloLineDash: 4,
+            haloLineWidth: 2,
+          },
+          state: {
+            selected: {
+              labelFontSize: 12,
+              labelFontWeight: 'normal'
+            }
+          }
+        },
+      },
+      dagre: {
+        layout: {
+          type: 'dagre',
+          nodesep: 200,
+          ranksep: 200,
+        },
+        edge: {
+          type: 'polyline',
+          style: {
+            router: {
+              type: 'orth',
+            },
+            endArrow: true,
+            endArrowType: 'triangle',
+            endArrowSize: 8,
+            stroke: mainColor,
+            labelAutoRotate: false,
+            labelFill: '#ffffff',
+            labelPadding: [4, 8, 2, 8],
+            labelBackground: true,
+            labelBackgroundFill: mainColor,
+            labelBackgroundRadius: 20,
+            labelFontSize: 12,
+            halo: true,
+            haloLineDash: 4,
+            haloLineWidth: 2,
+          },
+          state: {
+            selected: {
+              labelFontSize: 12,
+              labelFontWeight: 'normal'
+            }
+          }
+        },
+      }
+    }
     const graph = new Graph({
       container: 'container',
       data,
       autoResize: true,
-      autoFit: data.nodes.length < 10 ? 'center' : 'view',
+      autoFit: 'view',
       behaviors: ['drag-canvas', 'drag-element', 'zoom-canvas', {
         type: 'click-select',
         degree: 1,
         enable: (event) => event.targetType === "node",
       }],
-      layout: {
-        type: 'radial',
-        focusNode: '3',
-        linkDistance: 200,
-        preventOverlap: true,
-        unitRadius: 250,
-        maxPreventOverlapIteration: 100,
-        strictRadial: false
-      },
+      ...options[props.layout],
       node: {
         style: {
           // circle
@@ -95,35 +164,12 @@ const initChart = () => {
           }
         }
       },
-      edge: {
-        style: {
-          endArrow: true,
-          endArrowType: 'triangle',
-          endArrowSize: 8,
-          stroke: mainColor,
-          labelAutoRotate: false,
-          labelFill: '#ffffff',
-          labelPadding: [4, 8, 2, 8],
-          labelBackground: true,
-          labelBackgroundFill: mainColor,
-          labelBackgroundRadius: 20,
-          labelFontSize: 12,
-          halo: true,
-          haloLineDash: 4,
-          haloLineWidth: 2,
-        },
-        state: {
-          selected: {
-            labelFontSize: 12,
-            labelFontWeight: 'normal'
-          }
-        }
-      },
     } as any);
     graph.render();
     graph.on('node:click', (e) => {
       emit('chartPage', e.target.id)
     })
+    state.chart = graph
     state.resizeObserver = new ResizeObserver((entries) => {
       for (const entry of entries) {
         setTimeout(() => {
@@ -134,7 +180,7 @@ const initChart = () => {
     state.resizeObserver.observe(ref_main.value);
   }
 }
-watch(() => props.data, () => {
+watch(() => [props.data, props.layout], () => {
   initChart()
 })
 onMounted(() => {