|
@@ -1,58 +1,214 @@
|
|
|
<template>
|
|
|
- <m-table
|
|
|
- v-loading="loading"
|
|
|
- row-key="uuid"
|
|
|
- card-title="组织机构"
|
|
|
- :items="organizationTree"
|
|
|
- :headers="headers"
|
|
|
- :page-size="total"
|
|
|
- :page-current="1"
|
|
|
- :total="total"
|
|
|
- :expand-row-keys="expandRowKeys"
|
|
|
- lazy
|
|
|
- :load="load"
|
|
|
- :tree-props="{children: 'child'}"
|
|
|
- :default-sort="{ prop: 'sort', order: 'ascending' }"
|
|
|
- >
|
|
|
- </m-table>
|
|
|
+ <div class="fullBox white pa-3">
|
|
|
+ <div ref="graphRef" v-loading="loading" class="fullBox"></div>
|
|
|
+ <!-- <RelationGraph ref="graphRef" :options="graphOptions" :on-node-expand="onNodeExpand" :on-node-collapse="onNodeCollapse" v-loading="loading"></RelationGraph> -->
|
|
|
+ </div>
|
|
|
</template>
|
|
|
|
|
|
<script>
|
|
|
+// import RelationGraph from 'relation-graph'
|
|
|
+
|
|
|
+import {
|
|
|
+ CollapseExpandTree,
|
|
|
+ MindMapNode
|
|
|
+} from '@/utils/antvG6'
|
|
|
+import {
|
|
|
+ Graph,
|
|
|
+ register,
|
|
|
+ ExtensionCategory
|
|
|
+} from '@antv/g6'
|
|
|
import {
|
|
|
- organizationDrill
|
|
|
+ getOrganizationAtlas,
|
|
|
+ getOrganizationAtlasEmployee
|
|
|
} from '@/api/system'
|
|
|
import { mapGetters } from 'vuex'
|
|
|
|
|
|
+const NODE_TYPE = {
|
|
|
+ type: 'MindMapNode',
|
|
|
+ style: function (d) {
|
|
|
+ return {
|
|
|
+ label: true,
|
|
|
+ labelFontSize: 24,
|
|
|
+ labelLineHeight: 48,
|
|
|
+ labelPlacement: 'right',
|
|
|
+ labelPadding: [0, 20],
|
|
|
+ labelText: d.name,
|
|
|
+ labelOffsetX: d.depth === 3 ? 40 : 80,
|
|
|
+ labelBackground: true,
|
|
|
+ labelBackgroundFill: '#EFF0F0',
|
|
|
+ labelBackgroundRadius: 8,
|
|
|
+ port: true,
|
|
|
+ ports: [{ placement: 'right' }, { placement: 'left' }]
|
|
|
+ // badges: [
|
|
|
+ // { text: d.tag || '', placement: 'right-bottom', fontSize: 16 }
|
|
|
+ // ]
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
export default {
|
|
|
name: 'organization-structure',
|
|
|
data () {
|
|
|
return {
|
|
|
- expandRowKeys: [],
|
|
|
- loading: false,
|
|
|
- total: 0,
|
|
|
- headers: [
|
|
|
- { label: '机构名称', prop: 'organizationName' }
|
|
|
- ]
|
|
|
+ loading: false
|
|
|
}
|
|
|
},
|
|
|
computed: {
|
|
|
...mapGetters(['organizationTree'])
|
|
|
},
|
|
|
- created () {
|
|
|
- this.init()
|
|
|
+ mounted () {
|
|
|
+ register(ExtensionCategory.BEHAVIOR, 'collapse-expand-tree', CollapseExpandTree)
|
|
|
+ register(ExtensionCategory.NODE, 'MindMapNode', MindMapNode)
|
|
|
+ // this.renderGraph(treeToGraphData(this.assignTree(this.organizationTree)[0]))
|
|
|
+ this.onInit()
|
|
|
},
|
|
|
methods: {
|
|
|
- async init () {
|
|
|
- if (!this.organizationTree.length) {
|
|
|
- return
|
|
|
+ async getChildren (organizationNo) {
|
|
|
+ try {
|
|
|
+ const { data } = await getOrganizationAtlasEmployee({ organizationNo })
|
|
|
+ return {
|
|
|
+ nodes: data.nodes.map(e => {
|
|
|
+ const { labelOffsetX, ...obj } = NODE_TYPE
|
|
|
+ return {
|
|
|
+ id: e.id,
|
|
|
+ name: e.text,
|
|
|
+ hasChildren: false,
|
|
|
+ depth: 3,
|
|
|
+ ...obj
|
|
|
+ }
|
|
|
+ }),
|
|
|
+ edges: data.lines.map(e => {
|
|
|
+ return {
|
|
|
+ source: e.from,
|
|
|
+ target: e.to
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ this.$message.error(error)
|
|
|
}
|
|
|
- this.expandRowKeys = [this.organizationTree[0].uuid]
|
|
|
- this.total = this.organizationTree.length
|
|
|
},
|
|
|
- async load (tree, treeNode, resolve) {
|
|
|
+ assignTree (tree) {
|
|
|
+ return tree.map(e => {
|
|
|
+ const { child, organizationNo, organizationName, ...rest } = e
|
|
|
+ if (e.child && e.child.length) {
|
|
|
+ rest.children = this.assignTree(e.child)
|
|
|
+ }
|
|
|
+ return {
|
|
|
+ id: organizationNo,
|
|
|
+ name: organizationName,
|
|
|
+ getChildren: this.getChildren,
|
|
|
+ ...rest
|
|
|
+ }
|
|
|
+ })
|
|
|
+ },
|
|
|
+ renderGraph (data) {
|
|
|
+ const graph = new Graph({
|
|
|
+ container: this.$refs.graphRef,
|
|
|
+ width: this.$refs.graphRef.clientWidth,
|
|
|
+ height: this.$refs.graphRef.clientHeight,
|
|
|
+ data,
|
|
|
+ autoFit: 'center',
|
|
|
+ autoResize: false,
|
|
|
+ enable: false,
|
|
|
+ plugins: [
|
|
|
+ 'minimap',
|
|
|
+ 'contextmenu',
|
|
|
+ {
|
|
|
+ className: 'toolbar',
|
|
|
+ position: 'right-top',
|
|
|
+ type: 'toolbar',
|
|
|
+ getItems: () => [
|
|
|
+ { id: 'zoom-in', value: 'zoom-in' },
|
|
|
+ { id: 'zoom-out', value: 'zoom-out' },
|
|
|
+ { id: 'auto-fit', value: 'auto-fit' }
|
|
|
+ // { id: 'export', value: 'export' },
|
|
|
+ // { id: 'request-fullscreen', value: 'request-fullscreen' },
|
|
|
+ // { id: 'exit-fullscreen', value: 'exit-fullscreen' }
|
|
|
+ ],
|
|
|
+ onClick: (value) => {
|
|
|
+ const zoom = graph.getZoom()
|
|
|
+ console.log(zoom)
|
|
|
+ // 处理按钮点击事件
|
|
|
+ if (value === 'zoom-in') {
|
|
|
+ if (zoom > 2) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ graph.zoomTo(zoom + 0.1)
|
|
|
+ } else if (value === 'zoom-out') {
|
|
|
+ if (zoom < 0.5) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ graph.zoomTo(zoom - 0.1)
|
|
|
+ } else if (value === 'auto-fit') {
|
|
|
+ graph.fitView()
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ behaviors: [
|
|
|
+ 'drag-canvas',
|
|
|
+ // 'zoom-canvas',
|
|
|
+ 'scroll-canvas',
|
|
|
+ 'drag-element',
|
|
|
+ 'collapse-expand-tree'
|
|
|
+ ],
|
|
|
+ transforms: ['assign-color-by-branch'],
|
|
|
+ animation: false,
|
|
|
+ layout: {
|
|
|
+ type: 'compact-box',
|
|
|
+ getHeight: function getHeight () {
|
|
|
+ return 32
|
|
|
+ },
|
|
|
+ getWidth: function getWidth () {
|
|
|
+ return 32
|
|
|
+ },
|
|
|
+ getVGap: function getVGap () {
|
|
|
+ return 10
|
|
|
+ },
|
|
|
+ getHGap: function getHGap () {
|
|
|
+ return 200
|
|
|
+ },
|
|
|
+ preventOverlap: true, // 防止节点重叠
|
|
|
+ iterations: 200, // 迭代次数
|
|
|
+ animation: true, // 启用布局动画
|
|
|
+ direction: 'LR',
|
|
|
+ nodeSep: 100,
|
|
|
+ rankSep: 500, // 层间距(px)
|
|
|
+ ranker: 'tight-tree', // 布局的模式 'network-simplex' | 'tight-tree' | 'longest-path'
|
|
|
+ rankdir: 'LR', // 布局的方向
|
|
|
+ nodeSize: 100, // 节点大小(直径)
|
|
|
+ radial: false
|
|
|
+ },
|
|
|
+ node: NODE_TYPE,
|
|
|
+ edge: {
|
|
|
+ type: 'cubic-horizontal',
|
|
|
+ style: function (d) {
|
|
|
+ return {
|
|
|
+ endArrow: true,
|
|
|
+ lineWidth: 2,
|
|
|
+ stroke: '#999'
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ console.log(graph)
|
|
|
+ graph.render()
|
|
|
+ },
|
|
|
+ async onInit () {
|
|
|
try {
|
|
|
- const { data } = await organizationDrill({ deptName: tree.organizationName })
|
|
|
- resolve(data)
|
|
|
+ const { data } = await getOrganizationAtlas()
|
|
|
+ const { nodes, edges } = data
|
|
|
+ this.renderGraph({
|
|
|
+ nodes: nodes.map(e => {
|
|
|
+ return {
|
|
|
+ ...e,
|
|
|
+ getChildren: this.getChildren
|
|
|
+ }
|
|
|
+ }),
|
|
|
+ edges
|
|
|
+ })
|
|
|
} catch (error) {
|
|
|
this.$message.error(error)
|
|
|
}
|
|
@@ -62,5 +218,21 @@ export default {
|
|
|
</script>
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
+.fullBox {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ box-sizing: border-box;
|
|
|
+}
|
|
|
+
|
|
|
+::v-deep .toolbar {
|
|
|
+ width: 50px;
|
|
|
+ padding: 20px 0;
|
|
|
+ border: 1px solid #ccc;
|
|
|
+ .g6-toolbar-item {
|
|
|
+ width: 100%;
|
|
|
+ height: 20px;
|
|
|
+ padding: 10px 0;
|
|
|
+ }
|
|
|
+}
|
|
|
|
|
|
</style>
|