diff --git a/src/assets/css/components/views/entityExplorer/entity-graph.scss b/src/assets/css/components/views/entityExplorer/entity-graph.scss index 61cdb7d6..d1d2b805 100644 --- a/src/assets/css/components/views/entityExplorer/entity-graph.scss +++ b/src/assets/css/components/views/entityExplorer/entity-graph.scss @@ -18,6 +18,13 @@ color: #575757; font-size: 18px; } + + &.toolbar--unactivated { + cursor: default; + i { + opacity: .4; + } + } } } } diff --git a/src/assets/css/font/iconfont.css b/src/assets/css/font/iconfont.css index 378e85db..998a6b74 100644 --- a/src/assets/css/font/iconfont.css +++ b/src/assets/css/font/iconfont.css @@ -1,8 +1,8 @@ @font-face { font-family: "cn-icon"; /* Project id 2614877 */ - src: url('iconfont.woff2?t=1689317280458') format('woff2'), - url('iconfont.woff?t=1689317280458') format('woff'), - url('iconfont.ttf?t=1689317280458') format('truetype'); + src: url('iconfont.woff2?t=1690879635451') format('woff2'), + url('iconfont.woff?t=1690879635451') format('woff'), + url('iconfont.ttf?t=1690879635451') format('truetype'); } .cn-icon { @@ -13,6 +13,30 @@ -moz-osx-font-smoothing: grayscale; } +.cn-icon-next-step:before { + content: "\e7fd"; +} + +.cn-icon-zoom-out:before { + content: "\e808"; +} + +.cn-icon-reset:before { + content: "\e809"; +} + +.cn-icon-to-default:before { + content: "\e805"; +} + +.cn-icon-pre-step:before { + content: "\e806"; +} + +.cn-icon-zoom-in:before { + content: "\e807"; +} + .cn-icon-add-knowledge-base:before { content: "\e802"; } @@ -21,30 +45,6 @@ content: "\e803"; } -.cn-icon-zoom-out:before { - content: "\e7fd"; -} - -.cn-icon-to-default:before { - content: "\e7fe"; -} - -.cn-icon-reset:before { - content: "\e7ff"; -} - -.cn-icon-next-step:before { - content: "\e800"; -} - -.cn-icon-pre-step:before { - content: "\e801"; -} - -.cn-icon-zoom-in:before { - content: "\e7f"; -} - .cn-icon-expand-continue:before { content: "\e7fc"; } @@ -321,7 +321,7 @@ content: "\e7ab"; } -.cn-icon-serach:before { +.cn-icon-search:before { content: "\e7ac"; } diff --git a/src/assets/css/font/iconfont.js b/src/assets/css/font/iconfont.js index 1f1b6e01..db732242 100644 --- a/src/assets/css/font/iconfont.js +++ b/src/assets/css/font/iconfont.js @@ -1 +1 @@ -window._iconfont_svg_string_2614877='',function(l){var a=(a=document.getElementsByTagName("script"))[a.length-1],c=a.getAttribute("data-injectcss"),a=a.getAttribute("data-disable-injectsvg");if(!a){var h,o,i,m,v,z=function(a,c){c.parentNode.insertBefore(a,c)};if(c&&!l.__iconfont__svg__cssinject__){l.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(a){console&&console.log(a)}}h=function(){var a,c=document.createElement("div");c.innerHTML=l._iconfont_svg_string_2614877,(c=c.getElementsByTagName("svg")[0])&&(c.setAttribute("aria-hidden","true"),c.style.position="absolute",c.style.width=0,c.style.height=0,c.style.overflow="hidden",c=c,(a=document.body).firstChild?z(c,a.firstChild):a.appendChild(c))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(h,0):(o=function(){document.removeEventListener("DOMContentLoaded",o,!1),h()},document.addEventListener("DOMContentLoaded",o,!1)):document.attachEvent&&(i=h,m=l.document,v=!1,s(),m.onreadystatechange=function(){"complete"==m.readyState&&(m.onreadystatechange=null,t())})}function t(){v||(v=!0,i())}function s(){try{m.documentElement.doScroll("left")}catch(a){return void setTimeout(s,50)}t()}}(window); +window._iconfont_svg_string_2614877='',function(l){var a=(a=document.getElementsByTagName("script"))[a.length-1],c=a.getAttribute("data-injectcss"),a=a.getAttribute("data-disable-injectsvg");if(!a){var h,o,i,m,v,z=function(a,c){c.parentNode.insertBefore(a,c)};if(c&&!l.__iconfont__svg__cssinject__){l.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(a){console&&console.log(a)}}h=function(){var a,c=document.createElement("div");c.innerHTML=l._iconfont_svg_string_2614877,(c=c.getElementsByTagName("svg")[0])&&(c.setAttribute("aria-hidden","true"),c.style.position="absolute",c.style.width=0,c.style.height=0,c.style.overflow="hidden",c=c,(a=document.body).firstChild?z(c,a.firstChild):a.appendChild(c))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(h,0):(o=function(){document.removeEventListener("DOMContentLoaded",o,!1),h()},document.addEventListener("DOMContentLoaded",o,!1)):document.attachEvent&&(i=h,m=l.document,v=!1,s(),m.onreadystatechange=function(){"complete"==m.readyState&&(m.onreadystatechange=null,t())})}function t(){v||(v=!0,i())}function s(){try{m.documentElement.doScroll("left")}catch(a){return void setTimeout(s,50)}t()}}(window); \ No newline at end of file diff --git a/src/assets/css/font/iconfont.ttf b/src/assets/css/font/iconfont.ttf index d19823cf..15338513 100644 Binary files a/src/assets/css/font/iconfont.ttf and b/src/assets/css/font/iconfont.ttf differ diff --git a/src/assets/css/font/iconfont.woff b/src/assets/css/font/iconfont.woff index 972a5dcc..cbbbf612 100644 Binary files a/src/assets/css/font/iconfont.woff and b/src/assets/css/font/iconfont.woff differ diff --git a/src/assets/css/font/iconfont.woff2 b/src/assets/css/font/iconfont.woff2 index 162b078f..afb4d85c 100644 Binary files a/src/assets/css/font/iconfont.woff2 and b/src/assets/css/font/iconfont.woff2 differ diff --git a/src/components/layout/Header.vue b/src/components/layout/Header.vue index d6489a9d..f7619998 100644 --- a/src/components/layout/Header.vue +++ b/src/components/layout/Header.vue @@ -627,7 +627,7 @@ export default { if (e.response.data && e.response.data.message) { this.$message.error(e.response.data.message) } else { - this.$message.error('Something went wrong...') + this.$message.error(this.$t('tip.somethingWentWrong')) } }) } else { diff --git a/src/mixins/data-list.js b/src/mixins/data-list.js index c4f02b38..941349b5 100644 --- a/src/mixins/data-list.js +++ b/src/mixins/data-list.js @@ -133,7 +133,7 @@ export default { if (response.message) { this.$message.error(response.message) } else { - this.$message.error('Something went wrong...') + this.$message.error(this.$t('tip.somethingWentWrong')) } } }).catch(() => { diff --git a/src/mixins/table.js b/src/mixins/table.js index f614994b..706a36d5 100644 --- a/src/mixins/table.js +++ b/src/mixins/table.js @@ -122,7 +122,7 @@ export default { if (response.message) { this.$message.error(response.message) } else { - this.$message.error('Something went wrong...') + this.$message.error(this.$t('tip.somethingWentWrong')) } } this.$emit('reload') diff --git a/src/views/administration/Appearance.vue b/src/views/administration/Appearance.vue index 6971eade..07f0e615 100644 --- a/src/views/administration/Appearance.vue +++ b/src/views/administration/Appearance.vue @@ -181,7 +181,7 @@ export default { if (e.response && e.response.message) { this.$message.error(e.response.message) } else { - this.$message.error('Something went wrong...') + this.$message.error(this.$t('tip.somethingWentWrong')) } }).finally(() => { this.blockOperation.save = false diff --git a/src/views/administration/User.vue b/src/views/administration/User.vue index 4d274805..b22f4f2d 100644 --- a/src/views/administration/User.vue +++ b/src/views/administration/User.vue @@ -126,16 +126,12 @@ export default { if (response.message) { this.$message.error(response.message) } else { - this.$message.error('Something went wrong...') + this.$message.error(this.$t('tip.somethingWentWrong')) } } }).finally(() => { this.toggleLoading(false) - if (!this.tableData || this.tableData.length === 0) { - this.isNoData = true - } else { - this.isNoData = false - } + this.isNoData = !this.tableData || this.tableData.length === 0 }) } }, diff --git a/src/views/entityExplorer/EntityGraph2.vue b/src/views/entityExplorer/EntityGraph2.vue index d3e6a426..2effdcfe 100644 --- a/src/views/entityExplorer/EntityGraph2.vue +++ b/src/views/entityExplorer/EntityGraph2.vue @@ -50,6 +50,7 @@ export default { }, data () { return { + debounceFunc: null, chartOption: { container: 'entityGraph', layout: { @@ -64,6 +65,8 @@ export default { nodeStrength: (d) => { if (d.type === nodeType.rootNode || d.type === nodeType.listNode) { return -100 + } else if (d.type === nodeType.tempNode) { + return -300 } return -10 }, @@ -77,7 +80,13 @@ export default { modes: { default: ['drag-canvas', 'drag-nodes', 'click-select', 'zoom-canvas'] } - } + }, + /* 自己实现stack操作 */ + stackData: { + undo: [], // 后退 + redo: [] // 前进 + }, + initialData: null // 初始化数据,用于重置 } }, methods: { @@ -85,19 +94,26 @@ export default { this.registerElements() // 注册自定义node const tooltip = this.buildTooltip() // tooltip组件 const toolbar = this.buildToolbar() // 工具栏组装件 - this.chartOption.plugins = [tooltip] // 注册组件 + this.chartOption.plugins = [tooltip, toolbar] // 注册组件 this.graph = new G6.Graph(this.chartOption) - const initialData = await this.generateInitialData()// 初始化数据 - this.graph.data(initialData) - this.graph.render() - const rootNode = this.graph.findById(this.entity.entityName) - this.bindEvents() // 绑定事件 - this.graph.emit('node:click', { item: rootNode, target: rootNode.getKeyShape() }) // 手动触发rootNode的点击事件 + try { + const initialData = await this.generateInitialData() // 备份初始数据 + this.initialData = _.cloneDeep(initialData) // 初始化数据 + this.graph.data(initialData) + this.graph.render() + const rootNode = this.graph.findById(this.entity.entityName) + this.bindEvents() // 绑定事件 + this.graph.emit('node:click', { item: rootNode, target: rootNode.getKeyShape() }) // 手动触发rootNode的点击事件 + } catch (e) { + this.$message.error(this.errorMsgHandler(e)) + } finally { + this.rightBox.loading = false + } }, registerElements () { const _this = this G6.registerNode( - 'rootNode', + nodeType.rootNode, { draw (cfg, group) { group.addShape('circle', { @@ -214,7 +230,7 @@ export default { 'single-node' ) G6.registerNode( - 'listNode', + nodeType.listNode, { draw (cfg, group) { group.addShape('circle', { @@ -324,7 +340,7 @@ export default { 'single-node' ) G6.registerNode( - 'entityNode', + nodeType.entityNode, { draw (cfg, group) { group.addShape('circle', { @@ -382,14 +398,14 @@ export default { 'single-node' ) G6.registerNode( - 'tempNode', + nodeType.tempNode, { draw (cfg, group) { group.addShape('text', { attrs: { text: cfg.label, x: 0, - y: 25, + y: 22, fontSize: 12, textAlign: 'center', textBaseline: 'middle', @@ -534,7 +550,7 @@ export default { } } function getIconUrl (entityType, colored, isRoot) { - let suffix = '' + let suffix if (entityType === 'domain' && isRoot) { suffix = '-colored2' } else { @@ -560,45 +576,54 @@ export default { const node = e.item const nodeModel = node.get('model') if (nodeModel.type !== 'tempNode') { - cleanTempNodesAndTempEdges() + node.setState('mySelected', true) + _this.cleanTempNodesAndTempEdges() // 点击entityNode,查询数据,并根据数据生成tempNode if (nodeModel.type === nodeType.entityNode) { - _this.rightBox.mode = 'detail' - // 若已查过数据,不重复查询 - if (!nodeModel.myData.relatedEntity) { - await nodeModel.queryDetailData() - } - let change = false - Object.keys(nodeModel.myData.relatedEntity).forEach(k => { - if (nodeModel.myData.relatedEntity[k].total) { - // 若已有同级同类型的listNode,不生成此tempNode - const neighbors = _this.graph.getNeighbors(nodeModel.id, 'target') - const hasListNode = neighbors.some(b => b.get('model').myData.entityType === k) - if (!hasListNode) { - change = true - const tempNode = new Node( - nodeType.tempNode, - `${nodeModel.id}__${k}__temp`, - { - entityType: k, - // TODO k2-k1=1+k1k2 - ...generateTempNodeCoordinate(nodeModel.sourceNode, e) - }, - nodeModel - ) - const tempEdge = new Edge(nodeModel, tempNode, 'temp') - _this.graph.addItem('node', tempNode) - _this.graph.addItem('edge', tempEdge) - } + _this.rightBox.loading = true + try { + // 若已查过数据,不重复查询 + if (!nodeModel.myData.relatedEntity) { + await nodeModel.queryDetailData() } - }) - change && _this.graph.layout() + let change = false + Object.keys(nodeModel.myData.relatedEntity).forEach(k => { + if (nodeModel.myData.relatedEntity[k].total) { + // 若已有同级同类型的listNode,不生成此tempNode + const neighbors = _this.graph.getNeighbors(nodeModel.id, 'target') + const hasListNode = neighbors.some(b => b.get('model').myData.entityType === k) + if (!hasListNode) { + change = true + const tempNode = new Node( + nodeType.tempNode, + `${nodeModel.id}__${k}__temp`, + { + entityType: k, + ...generateTempNodeCoordinate(nodeModel.sourceNode, e) + }, + nodeModel + ) + const tempEdge = new Edge(nodeModel, tempNode, 'temp') + _this.graph.addItem('node', tempNode) + _this.graph.addItem('edge', tempEdge) + } + } + }) + change && _this.graph.layout() + _this.rightBox.node = _.cloneDeep(nodeModel) + _this.rightBox.mode = 'detail' + } catch (e) { + _this.$message.error(_this.errorMsgHandler(e)) + } finally { + _this.rightBox.loading = false + } } else if (nodeModel.type === nodeType.listNode) { + _this.rightBox.node = _.cloneDeep(nodeModel) _this.rightBox.mode = 'list' } else if (nodeModel.type === nodeType.rootNode) { + _this.rightBox.node = _.cloneDeep(nodeModel) _this.rightBox.mode = 'detail' } - _this.rightBox.node = _.cloneDeep(nodeModel) } else { // 点击tempNode,根据source生成listNode和entityNode以及对应的edge。查完entityNode的接口再删除临时node和edge。 // 若已达第六层,则只生成listNode,不再展开entityNode @@ -620,35 +645,35 @@ export default { // 判断listNode的sourceNode层级,若大于等于10(即第6层listNode),则不继续拓展entity node,并给用户提示。否则拓展entity node const level = _this.getNodeLevel(listNode.sourceNode.id) if (level < 10) { - const entities = await queryRelatedEntity(nodeModel.sourceNode, listNode.myData.entityType) - nodeModel.sourceNode.myData.relatedEntity[listNode.myData.entityType].list.push(...entities.list) - entities.list.forEach(entity => { - const entityNode = new Node(nodeType.entityNode, entity.vertex, { - entityType: listNode.myData.entityType, - entityName: entity.vertex, - x: e.x + Math.random() * 100 - 50, - y: e.y + Math.random() * 100 - 50 - }, listNode) - nodes.push(entityNode) - edges.push(new Edge(listNode, entityNode)) - }) + _this.rightBox.loading = true + try { + const entities = await queryRelatedEntity(nodeModel.sourceNode, listNode.myData.entityType) + nodeModel.sourceNode.myData.relatedEntity[listNode.myData.entityType].list.push(...entities.list) + entities.list.forEach(entity => { + const entityNode = new Node(nodeType.entityNode, entity.vertex, { + entityType: listNode.myData.entityType, + entityName: entity.vertex, + x: e.x + Math.random() * 100 - 50, + y: e.y + Math.random() * 100 - 50 + }, listNode) + nodes.push(entityNode) + edges.push(new Edge(listNode, entityNode)) + }) + } catch (e) { + _this.$message.error(_this.errorMsgHandler(e)) + } finally { + _this.rightBox.loading = false + } } else { // TODO 提示大于5层 } - nodes.forEach(n => { - _this.graph.addItem('node', n) - }) - edges.forEach(edge => { - _this.graph.addItem('edge', edge) - }) - cleanTempNodesAndTempEdges() + _this.addItems(nodes, edges) + _this.cleanTempNodesAndTempEdges() _this.graph.layout() // 手动高亮listNode const _listNode = _this.graph.findById(listNode.id) - _this.graph.emit('node:click', { item: _listNode, target: _listNode.getKeyShape() }) // 手动触发rootNode的点击事件 + _this.graph.emit('node:click', { item: _listNode, target: _listNode.getKeyShape() }) } - - node.setState('mySelected', true) }) this.graph.on('node:mouseenter', function (e) { e.item.setState('hover', true) @@ -661,17 +686,6 @@ export default { model.fx = e.x model.fy = e.y } - function cleanTempNodesAndTempEdges () { - // 清除现有tempNode和tempEdge - const tempNodes = _this.graph.findAll('node', node => node.get('model').type === nodeType.tempNode) - tempNodes.forEach(n => { - _this.graph.removeItem(n, false) - }) - const tempEdges = _this.graph.findAll('edge', edge => edge.get('model').isTemp) - tempEdges.forEach(n => { - _this.graph.removeItem(n, false) - }) - } function generateTempNodeCoordinate (sourceNode, event) { const sx = sourceNode.x const sy = sourceNode.y @@ -683,6 +697,37 @@ export default { } } }, + addItems (nodes = [], edges = [], stack = true) { + // 过滤掉已经存在的node + const _nodes = nodes.filter(n => { + return !this.graph.findById(n.id) + }) + _nodes.forEach(n => { + this.graph.addItem('node', n) + }) + edges.forEach(e => { + this.graph.addItem('edge', e) + }) + if (stack) { + this.stackData.undo.push({ nodes: _nodes, edges }) + } + }, + setItemsStata (models = [], stateName, state) { + models.forEach(m => { + this.graph.setItemState(this.graph.findById(m.id), stateName, state) + }) + }, + cleanTempNodesAndTempEdges () { + // 清除现有tempNode和tempEdge + const tempNodes = this.graph.findAll('node', node => node.get('model').type === nodeType.tempNode) + tempNodes.forEach(n => { + this.graph.removeItem(n) + }) + const tempEdges = this.graph.findAll('edge', edge => edge.get('model').isTemp) + tempEdges.forEach(n => { + this.graph.removeItem(n) + }) + }, async generateInitialData () { const nodes = [] const edges = [] @@ -829,15 +874,59 @@ export default { className: 'toolbar__tools', getContent: () => { return `` }, handleClick: (code, graph) => { - toolbar.handleDefaultOperator(code) + if (code === 'undo') { + const data = this.stackData.undo.pop() + data.nodes.forEach(n => { + if (n.type === nodeType.listNode) { + const listNode = this.graph.findById(n.id) + const listNodeEdges = listNode.getEdges() + listNodeEdges.forEach(e => { + e.setState('mySelected', false) + }) + listNode.setState('mySelected', false) + } + this.graph.removeItem(n.id) + }) + data.edges.forEach(e => { + this.graph.removeItem(e.id) + }) + this.stackData.redo.push(data) + this.cleanTempNodesAndTempEdges() + this.graph.layout() + this.onCloseBlock() + } else if (code === 'redo') { + const data = this.stackData.redo.pop() + this.addItems(data.nodes, data.edges, false) + this.stackData.undo.push(data) + this.cleanTempNodesAndTempEdges() + this.graph.layout() + this.onCloseBlock() + } else if (code === 'autoZoom') { + this.graph.zoomTo(1) + this.graph.fitCenter() + } else if (code === 'zoomOut') { + const { x, y } = this.graph.getViewPortCenterPoint() + this.graph.zoomTo(this.graph.getZoom() + 0.2, { x, y }) + } else if (code === 'zoomIn') { + const { x, y } = this.graph.getViewPortCenterPoint() + const currentZoom = this.graph.getZoom() + this.graph.zoomTo(currentZoom - 0.2, { x, y }) + } else { + this.graph.clear() + this.graph.data(this.initialData) + this.graph.render() + const rootNode = this.graph.findById(this.entity.entityName) + this.graph.emit('node:click', { item: rootNode, target: rootNode.getKeyShape() }) // 手动触发rootNode的点击事件 + } } }) return toolbar @@ -849,22 +938,33 @@ export default { const sourceModel = sourceNode.getModel() const expandType = model.myData.entityType if (sourceModel.myData.relatedEntity[expandType].list.length < 50) { - const entities = await queryRelatedEntity(sourceModel, expandType) - sourceModel.myData.relatedEntity[expandType].list.push(...entities.list) - entities.list.forEach(entity => { - const entityNodeModel = new Node(nodeType.entityNode, entity.vertex, { - entityType: expandType, - entityName: entity.vertex, - x: model.x + Math.random() * 500 - 250, - y: model.y + Math.random() * 500 - 250 - }, model) - this.graph.addItem('node', entityNodeModel) - const edge = new Edge(model, entityNodeModel) - this.graph.addItem('edge', edge) + this.rightBox.loading = true + try { + const entities = await queryRelatedEntity(sourceModel, expandType) + sourceModel.myData.relatedEntity[expandType].list.push(...entities.list) + const entityNodeModels = [] + const edgeModels = [] + entities.list.forEach(entity => { + const entityNodeModel = new Node(nodeType.entityNode, entity.vertex, { + entityType: expandType, + entityName: entity.vertex, + x: model.x + Math.random() * 500 - 250, + y: model.y + Math.random() * 500 - 250 + }, model) + entityNodeModels.push(entityNodeModel) + + const edge = new Edge(model, entityNodeModel) + edgeModels.push(edge) + }) + this.addItems(entityNodeModels, edgeModels) + this.setItemsStata(edgeModels, 'mySelected', true) this.graph.layout() - this.graph.setItemState(this.graph.findById(edge.id), 'mySelected', true) - }) - this.rightBox.node = _.cloneDeep(model) + this.rightBox.node = _.cloneDeep(model) + } catch (e) { + this.$message.error(this.errorMsgHandler(e)) + } finally { + this.rightBox.loading = false + } } else { // TODO 提示超过50 } @@ -874,48 +974,91 @@ export default { if (node) { const nodeModel = node.getModel() if (nodeModel.myData.relatedEntity[expandType].list.length < 50) { - const entities = await queryRelatedEntity(nodeModel, expandType) - nodeModel.myData.relatedEntity[expandType].list.push(...entities.list) - const neighbors = node.getNeighbors('target') - const listNode = neighbors.find(n => n.getModel().myData.entityType === expandType) - let listNodeModel = listNode.getModel() - // 如果listNode是tempNode,移除,并新建listNode - if (listNodeModel.type === nodeType.tempNode) { - // 移除tempNode和tempEdge - const tempEdges = listNode.getInEdges() - tempEdges.forEach(edge => { - this.graph.removeItem(edge) - }) - this.graph.removeItem(listNode) + const toAddNodeModels = [] + const toAddEdgeModels = [] - listNodeModel = new Node(nodeType.listNode, `${nodeModel.id}__${expandType}-list`, { entityType: expandType, x: listNodeModel.x, y: listNodeModel.y }, nodeModel) - const edge = new Edge(nodeModel, listNodeModel) - this.graph.addItem('node', listNodeModel) - this.graph.addItem('edge', edge) - this.graph.setItemState(this.graph.findById(edge.id), 'mySelected', true) - } - entities.list.forEach(entity => { - const entityNodeModel = new Node(nodeType.entityNode, entity.vertex, { - entityType: expandType, - entityName: entity.vertex, - x: listNodeModel.x + Math.random() * 500 - 250, - y: listNodeModel.y + Math.random() * 500 - 250 - }, listNodeModel) - this.graph.addItem('node', entityNodeModel) - this.graph.addItem('edge', new Edge(listNodeModel, entityNodeModel)) + this.rightBox.loading = true + try { + const entities = await queryRelatedEntity(nodeModel, expandType) + nodeModel.myData.relatedEntity[expandType].list.push(...entities.list) + const neighbors = node.getNeighbors('target') + const listNode = neighbors.find(n => n.getModel().myData.entityType === expandType) + let listNodeModel = listNode.getModel() + let listEdgeModel + // 如果listNode是tempNode,移除,并新建listNode + if (listNodeModel.type === nodeType.tempNode) { + // 移除tempNode和tempEdge + const tempEdges = listNode.getInEdges() + tempEdges.forEach(edge => { + this.graph.removeItem(edge) + }) + this.graph.removeItem(listNode) + + listNodeModel = new Node(nodeType.listNode, `${nodeModel.id}__${expandType}-list`, { entityType: expandType, x: listNodeModel.x, y: listNodeModel.y }, nodeModel) + listEdgeModel = new Edge(nodeModel, listNodeModel) + toAddNodeModels.push(listNodeModel) + toAddEdgeModels.push(listEdgeModel) + } + entities.list.forEach(entity => { + const entityNodeModel = new Node(nodeType.entityNode, entity.vertex, { + entityType: expandType, + entityName: entity.vertex, + x: listNodeModel.x + Math.random() * 500 - 250, + y: listNodeModel.y + Math.random() * 500 - 250 + }, listNodeModel) + toAddNodeModels.push(entityNodeModel) + toAddEdgeModels.push(new Edge(listNodeModel, entityNodeModel)) + }) + this.addItems(toAddNodeModels, toAddEdgeModels) + if (listEdgeModel) { + this.graph.setItemState(this.graph.findById(listEdgeModel.id), 'mySelected', true) + } this.graph.layout() - }) - this.rightBox.node = _.cloneDeep(nodeModel) + this.rightBox.node = _.cloneDeep(nodeModel) + } catch (e) { + this.$message.error(this.errorMsgHandler(e)) + } finally { + this.rightBox.loading = false + } } else { // TODO 提示超过50 } } + }, + resize () { + const container = document.getElementById('entityGraph') + this.graph.changeSize(container.offsetWidth, container.offsetHeight) + } + }, + watch: { + stackData: { + deep: true, + handler (n) { + if (n) { + if (n.undo.length > 0) { + document.getElementById('preStep').classList.remove('toolbar--unactivated') + console.info(n, document.getElementById('preStep').classList) + } else { + document.getElementById('preStep').classList.add('toolbar--unactivated') + } + if (n.redo.length > 0) { + document.getElementById('nextStep').classList.remove('toolbar--unactivated') + } else { + document.getElementById('nextStep').classList.add('toolbar--unactivated') + } + } + } } }, async mounted () { if (this.entity.entityType && this.entity.entityName) { await this.init() } + this.debounceFunc = this.$_.debounce(this.resize, 300) + window.addEventListener('resize', this.debounceFunc) + }, + unmounted () { + window.removeEventListener('resize', this.debounceFunc) }, setup () { const route = useRoute() @@ -931,7 +1074,7 @@ export default { mode: 'detail', // list | detail show: true, node: null, - loading: false + loading: true }) return { entity, diff --git a/src/views/entityExplorer/entityGraph/GraphEntityList.vue b/src/views/entityExplorer/entityGraph/GraphEntityList.vue index bd859aea..639bf8a2 100644 --- a/src/views/entityExplorer/entityGraph/GraphEntityList.vue +++ b/src/views/entityExplorer/entityGraph/GraphEntityList.vue @@ -114,6 +114,9 @@ export default { props: { node: { type: Object + }, + loading: { + type: Boolean } }, components: { diff --git a/src/views/setting/KnowledgeBase.vue b/src/views/setting/KnowledgeBase.vue index ea516946..aba3a871 100644 --- a/src/views/setting/KnowledgeBase.vue +++ b/src/views/setting/KnowledgeBase.vue @@ -315,7 +315,7 @@ export default { if (e.response.data && e.response.data.message) { this.$message.error(e.response.data.message) } else { - this.$message.error('Something went wrong...') + this.$message.error(this.$t('tip.somethingWentWrong')) } }) }, @@ -402,7 +402,7 @@ export default { if (e.response.data && e.response.data.message) { this.$message.error(e.response.data.message) } else { - this.$message.error('Something went wrong...') + this.$message.error(this.$t('tip.somethingWentWrong')) } }).finally(() => { this.toggleLoading(false)