-
8README.md
-
52mock/index.js
-
91mock/mock-server.js
-
682mock/peoplePool.js
-
25mock/utils.js
-
5package.json
-
7src/App.vue
-
26src/api/peoplePool.js
-
BINsrc/assets/image/3.png
-
BINsrc/assets/image/a1.png
-
BINsrc/assets/image/a2.png
-
BINsrc/assets/image/a3.png
-
23src/assets/image/default.svg
-
BINsrc/assets/image/down.png
-
17src/assets/image/down.svg
-
21src/assets/image/tag1.svg
-
21src/assets/image/tag2.svg
-
159src/components/PeoplePoolPage/basic-info.vue
-
4src/components/PeoplePoolPage/breadcrumb.vue
-
125src/components/PeoplePoolPage/detailCom.vue
-
459src/components/PeoplePoolPage/itemCard.vue
-
153src/components/PeoplePoolPage/itemGroup.vue
-
66src/components/PeoplePoolPage/nav-left.vue
-
336src/components/PeoplePoolPage/news.vue
-
8src/components/PeoplePoolPage/people-descriptive.vue
-
8src/components/PeoplePoolPage/people-lifetime.vue
-
208src/components/PeoplePoolPage/people-profile.vue
-
98src/components/PeoplePoolPage/person-information.vue
-
161src/components/PeoplePoolPage/photo-video.vue
-
129src/components/PeoplePoolPage/platform-dynamics.vue
-
457src/components/PeoplePoolPage/recommendation.vue
-
32src/components/PeoplePoolPage/relationship.vue
-
670src/components/PeoplePoolPage/search.vue
-
56src/components/PeoplePoolPage/social-analysis.vue
-
34src/components/PeoplePoolPage/tracing-point.vue
-
1065src/components/graphContainer/Graph.vue
-
111src/components/graphContainer/GraphIndex.vue
-
347src/components/graphContainer/Menu.vue
-
128src/components/graphContainer/README.md
-
2src/components/graphContainer/eventBus.js
-
337src/components/graphContainer/extend-dialog.vue
-
214src/components/graphContainer/graph/bindListener.js
-
641src/components/graphContainer/graph/common.js
-
66src/components/graphContainer/graph/dialogData.js
-
61src/components/graphContainer/graph/directive.js
-
24src/components/graphContainer/graph/elementPlugin.js
-
90src/components/graphContainer/graph/globalData.js
-
163src/components/graphContainer/graph/graph.js
-
639src/components/graphContainer/graph/menu.js
-
65src/components/graphContainer/graph/publicFunction.js
-
486src/components/graphContainer/graph/registerNodeEdge.js
-
148src/components/graphContainer/graph/request.js
-
46src/components/graphContainer/graph/snapshot.js
-
BINsrc/components/graphContainer/images/empty-graph.png
-
2src/components/graphContainer/images/graphSearch.svg
-
344src/components/graphContainer/search.vue
-
54src/components/graphContainer/store/menu.js
-
383src/components/graphContainer/styles/g6-contextmenu.less
-
35src/layout/components/domain.vue
-
27src/layout/index.vue
-
1src/layout/layout/appMain.vue
-
2src/layout/layout/horizontal.vue
-
2src/layout/layout/vertical.vue
-
10src/main.js
-
45src/router/index.js
-
3src/settings.js
-
1src/store/getters.js
-
12src/store/modules/app.js
-
103src/store/modules/graphAside.js
-
54src/store/modules/graphMenu.js
-
2src/store/modules/settings.js
-
1src/styles/global.less
-
16src/styles/variable.less
-
2src/styles/winbox-ui/css/black.css
-
2src/styles/winbox-ui/css/blue.css
-
2src/styles/winbox-ui/css/cyan.css
-
2src/styles/winbox-ui/css/default.css
-
BINsrc/styles/winbox-ui/css/iconfont.ttf
-
BINsrc/styles/winbox-ui/css/iconfont.woff
-
BINsrc/styles/winbox-ui/css/iconfont.woff2
-
2src/styles/winbox-ui/css/red.css
-
2src/styles/winbox-ui/css/white.css
-
34src/utils/common.js
-
1src/views/demo/form/index.vue
-
128src/views/detail/index.vue
-
70src/views/icons/index.vue
-
181src/views/peoplePool/detail.vue
-
162src/views/peoplePool/index.vue
-
3vue.config.js
@ -0,0 +1,52 @@ |
|||
const Mock = require('mockjs') |
|||
const { param2Obj } = require('./utils') |
|||
|
|||
const peoplePool = require('./peoplePool') |
|||
|
|||
const mocks = [ |
|||
...peoplePool |
|||
] |
|||
|
|||
|
|||
|
|||
|
|||
function mockXHR() { |
|||
Mock.XHR.prototype.proxy_send = Mock.XHR.prototype.send |
|||
Mock.XHR.prototype.send = function() { |
|||
if (this.custom.xhr) { |
|||
this.custom.xhr.withCredentials = this.withCredentials || false |
|||
|
|||
if (this.responseType) { |
|||
this.custom.xhr.responseType = this.responseType |
|||
} |
|||
} |
|||
this.proxy_send(...arguments) |
|||
} |
|||
|
|||
function XHR2ExpressReqWrap(respond) { |
|||
return function(options) { |
|||
let result = null |
|||
if (respond instanceof Function) { |
|||
const { body, type, url } = options |
|||
// https://expressjs.com/en/4x/api.html#req
|
|||
result = respond({ |
|||
method: type, |
|||
body: JSON.parse(body), |
|||
query: param2Obj(url) |
|||
}) |
|||
} else { |
|||
result = respond |
|||
} |
|||
return Mock.mock(result) |
|||
} |
|||
} |
|||
|
|||
for (const i of mocks) { |
|||
Mock.mock(new RegExp(i.url), i.type || 'get', XHR2ExpressReqWrap(i.response)) |
|||
} |
|||
} |
|||
|
|||
module.exports = { |
|||
mocks, |
|||
mockXHR |
|||
} |
@ -0,0 +1,91 @@ |
|||
const chokidar = require('chokidar'); |
|||
const bodyParser = require('body-parser'); |
|||
const chalk = require('chalk'); |
|||
const path = require('path'); |
|||
const Mock = require('mockjs'); |
|||
|
|||
const mockDir = path.join(process.cwd(), 'mock'); |
|||
|
|||
function registerRoutes(app) { |
|||
let mockLastIndex; |
|||
const { mocks } = require('./index.js'); |
|||
const mocksForServer = mocks.map(route => { |
|||
return responseFake(route.url, route.type, route.response); |
|||
}); |
|||
for (const mock of mocksForServer) { |
|||
app[mock.type](mock.url, mock.response); |
|||
mockLastIndex = app._router.stack.length; |
|||
} |
|||
const mockRoutesLength = Object.keys(mocksForServer).length; |
|||
return { |
|||
mockRoutesLength: mockRoutesLength, |
|||
mockStartIndex: mockLastIndex - mockRoutesLength |
|||
}; |
|||
} |
|||
|
|||
function unregisterRoutes() { |
|||
Object.keys(require.cache).forEach(i => { |
|||
if (i.includes(mockDir)) { |
|||
delete require.cache[require.resolve(i)]; |
|||
} |
|||
}); |
|||
} |
|||
function randomInRange(min, max) { |
|||
const differ = max - min; |
|||
return Math.trunc(min + differ * Math.random()); |
|||
} |
|||
// for mock server
|
|||
const responseFake = (url, type, respond) => { |
|||
return { |
|||
url: new RegExp(`${process.env.VUE_APP_BASE_API}${url}`), |
|||
type: type || 'get', |
|||
response(req, res) { |
|||
console.log('request invoke:' + req.path); |
|||
setTimeout( |
|||
() => { |
|||
res.json(Mock.mock(respond instanceof Function ? respond(req, res) : respond)); |
|||
}, |
|||
(function () { |
|||
return randomInRange(500, 1000); // 注意最大值不能超过 request.js 中设置的最长等待时间
|
|||
})() |
|||
); |
|||
} |
|||
}; |
|||
}; |
|||
|
|||
module.exports = app => { |
|||
// parse app.body
|
|||
// https://expressjs.com/en/4x/api.html#req.body
|
|||
app.use(bodyParser.json()); |
|||
app.use(bodyParser.urlencoded({ |
|||
extended: true |
|||
})); |
|||
|
|||
const mockRoutes = registerRoutes(app); |
|||
let mockRoutesLength = mockRoutes.mockRoutesLength; |
|||
let mockStartIndex = mockRoutes.mockStartIndex; |
|||
|
|||
// watch files, hot reload mock server
|
|||
chokidar.watch(mockDir, { |
|||
ignored: /mock-server/, |
|||
ignoreInitial: true |
|||
}).on('all', (event, path) => { |
|||
if (event === 'change' || event === 'add') { |
|||
try { |
|||
// remove mock routes stack
|
|||
app._router.stack.splice(mockStartIndex, mockRoutesLength); |
|||
|
|||
// clear routes cache
|
|||
unregisterRoutes(); |
|||
|
|||
const mockRoutes = registerRoutes(app); |
|||
mockRoutesLength = mockRoutes.mockRoutesLength; |
|||
mockStartIndex = mockRoutes.mockStartIndex; |
|||
|
|||
console.log(chalk.magentaBright(`\n > Mock Server hot reload success! changed ${path}`)); |
|||
} catch (error) { |
|||
console.log(chalk.redBright(error)); |
|||
} |
|||
} |
|||
}); |
|||
}; |
@ -0,0 +1,682 @@ |
|||
const Mock = require('mockjs') |
|||
|
|||
const analysisFilterData = Mock.mock({ |
|||
'items|3': [{ |
|||
"image": "https://tilake.wenge.com/wos/graphlake/entity-icon/gzdx/%E5%BC%A0%E5%86%9B32.glk", |
|||
"简介": "张军 (1956年10月-),男,汉族,山东博兴人,中华人民共和国政治人物,副国级领导人。吉林大学法律系毕业,中国人民大学刑法学硕士,武汉大学刑法学博士。中共第十七届中央纪委委员、常委、副书记(2012年11月增选)。第十八届中央纪委委员、常委、副书记,第十九届中央委员。曾任中华人民共和国司法部副部长,最高人民法院副院长(一级大法官),中国共产党第十八届中央纪律检查委员会副书记(正部长级),中华人民共和国司法部部长,党组书记。现任中华人民共和国最高人民检察院检察长,首席大检察官。", |
|||
"手机号码": null, |
|||
"人物性格": null, |
|||
"出生地": "山东博兴", |
|||
"对华态度": null, |
|||
"活跃等级": null, |
|||
"国家地区": "中国", |
|||
"性别": "男", |
|||
"死亡日期": null, |
|||
"现任职务": "中共十九届中央委员,最高人民检察院检察长、党组书记。", |
|||
"民族": "汉族", |
|||
"所属组织": null, |
|||
"出生日期": null, |
|||
"毕业院校": "吉林大学法律系(学士)\n中国人民大学刑法学(硕士)\n武汉大学刑法学(博士)", |
|||
"name": "张军", |
|||
"id": "72860f221138d46fa16e9d6f99af9b38", |
|||
"居住地址": null, |
|||
"政党派系": "中国共产党" |
|||
}, |
|||
{ |
|||
"image": "https://tilake.wenge.com/wos/graphlake/entity-icon/gzdx/%E5%BC%A0%E6%98%A5%E8%B4%A414.glk", |
|||
"简介": "张春贤(1953年5月12日-),河南禹州人,中华人民共和国政治人物,副国级领导人,中共第十六、十七、十八、十九届中央委员,第十八届中央政治局委员[1][2]。曾任交通部部长、中共湖南省委书记、中共新疆维吾尔自治区党委书记,现任第十三届全国人大常委会副委员长。", |
|||
"手机号码": null, |
|||
"人物性格": null, |
|||
"出生地": "中华人民共和国河南省禹县", |
|||
"对华态度": null, |
|||
"活跃等级": null, |
|||
"国家地区": "中国", |
|||
"性别": "男", |
|||
"死亡日期": null, |
|||
"现任职务": "全国人民代表大会常务委员会副委员长", |
|||
"民族": "汉族", |
|||
"所属组织": null, |
|||
"出生日期": null, |
|||
"毕业院校": "东北重型机械学院、哈尔滨工业大学", |
|||
"name": "张春贤", |
|||
"id": "faa1329e90c205b9c7e87d9dde4ef990", |
|||
"居住地址": null, |
|||
"政党派系": "中国共产党" |
|||
}, |
|||
{ |
|||
"image": "https://tilake.wenge.com/wos/graphlake/entity-icon/gzdx/%E5%BC%A0%E5%8F%88%E4%BE%A029.glk", |
|||
"简介": "张又侠(1950年7月-),男,陕西渭南人,生于北京,中国人民解放军上将,副国级领导人,大专学历。中共十八届中央委员、中央军事委员会委员、原中国人民解放军总装备部部长、中央军委装备发展部原部长。2017年10月25日,当选为中共第十九届中央委员、中央政治局委员、中共中央军委副主席。2018年3月18日,当选为国家军委副主席。", |
|||
"手机号码": null, |
|||
"人物性格": null, |
|||
"出生地": "中国北京市", |
|||
"对华态度": null, |
|||
"活跃等级": null, |
|||
"国家地区": "中国", |
|||
"性别": "男", |
|||
"死亡日期": null, |
|||
"现任职务": "中央政治局委员,中共中央军事委员会副主席,中华人民共和国中央军事委员会副主席,陆军上将军衔", |
|||
"民族": "汉族", |
|||
"所属组织": null, |
|||
"出生日期": null, |
|||
"毕业院校": "中国人民解放军军事学院", |
|||
"name": "张又侠", |
|||
"id": "26ef870b0d6aa11360df494af8415b1f", |
|||
"居住地址": null, |
|||
"政党派系": "中国共产党" |
|||
}], |
|||
}) |
|||
|
|||
|
|||
|
|||
const entityInfoData = Mock.mock({ |
|||
'items|1': [{ |
|||
"__oid":"faa1329e90c205b9c7e87d9dde4ef990", |
|||
"id":"faa1329e90c205b9c7e87d9dde4ef990", |
|||
"name":"张春贤", |
|||
"image":"https://tilake.wenge.com/wos/graphlake/entity-icon/gzdx/%E5%BC%A0%E6%98%A5%E8%B4%A414.glk", |
|||
"data_status":"NORMAL", |
|||
"source_name":"3", |
|||
"concept":[ |
|||
"64082742e4b0f6e105e07aab" |
|||
], |
|||
"propertyList": [ |
|||
{ |
|||
"id": "64084189e4b047daf59229c7", |
|||
"name": "别名", |
|||
"alias": null, |
|||
"parentid": null, |
|||
"groupKey": "", |
|||
"group": null, |
|||
"data_type": "STRING", |
|||
"data_format": null, |
|||
"data_unit": null, |
|||
"dataRange": null, |
|||
"multi": false, |
|||
"propValue": null, |
|||
"childrenList": null |
|||
}, |
|||
{ |
|||
"id": "64082b3be4b047daf5922998", |
|||
"name": "简介", |
|||
"alias": null, |
|||
"parentid": null, |
|||
"groupKey": "", |
|||
"group": null, |
|||
"data_type": "STRING", |
|||
"data_format": null, |
|||
"data_unit": null, |
|||
"dataRange": null, |
|||
"multi": false, |
|||
"propValue": "张春贤(1953年5月12日-),河南禹州人,中华人民共和国政治人物,副国级领导人,中共第十六、十七、十八、十九届中央委员,第十八届中央政治局委员[1][2]。曾任交通部部长、中共湖南省委书记、中共新疆维吾尔自治区党委书记,现任第十三届全国人大常委会副委员长。", |
|||
"childrenList": null |
|||
}, |
|||
{ |
|||
"id": "640843d2e4b047daf59229da", |
|||
"name": "人物经历", |
|||
"alias": null, |
|||
"parentid": null, |
|||
"groupKey": "", |
|||
"group": null, |
|||
"data_type": "STRING", |
|||
"data_format": null, |
|||
"data_unit": null, |
|||
"dataRange": null, |
|||
"multi": false, |
|||
"propValue": "1953年出生在河南省禹县(今禹州市)城关镇一个普通家庭[3]。1970年17岁时,张春贤参加中国人民解放军,任中国人民解放军八一九二部队战士。1973年11月加入中国共产党。1975年,回到河南家乡,任河南省禹县城关公社东关大队干部。1976年12月进入东北重型机械学院(现为燕山大学)机械制造系锻压工艺及设备专业学习。1980年9月毕业后,被分配到第三机械工业部一一六厂十五车间担任技术员[4][5]。\n\n张春贤从一名普通技术员做起。1982年至1985年,历任机械工业部第十设计研究院计划科计划员、助理工程师,设计院党委组织部副部长。1985年至1988年,任机械工业部、国家机械工业委员会、机械电子工业部第十设计研究院党委书记。1988年到1991年,任机械电子工业部第十设计研究院党委书记兼副院长[4][5]。\n\n1991年10月,作为机械电子工业部选拔的优秀年轻干部,张春贤调入中央部委工作。1991年至1992年,任监察部驻机械电子工业部监察局副局长兼机械电子工业部纠风办主任。1992年至1993年,任中国包装和食品机械总公司副总经理。1993年至1995年,任中国包装和食品机械总公司总经理兼党委书记[4][5]。在他出任副总经理时,该公司管理混乱,而在他以总经理兼党委书记的身份离开该公司时,它已变成部里较好的企业[5]。", |
|||
"childrenList": null |
|||
}, |
|||
{ |
|||
"id": "640843ece4b047daf59229db", |
|||
"name": "家庭地址", |
|||
"alias": null, |
|||
"parentid": null, |
|||
"groupKey": "", |
|||
"group": null, |
|||
"data_type": "TEXT", |
|||
"data_format": null, |
|||
"data_unit": null, |
|||
"dataRange": null, |
|||
"multi": false, |
|||
"propValue": null, |
|||
"childrenList": null |
|||
}, |
|||
{ |
|||
"id": "640843fee4b047daf59229dc", |
|||
"name": "个人履历", |
|||
"alias": null, |
|||
"parentid": null, |
|||
"groupKey": "", |
|||
"group": null, |
|||
"data_type": "TEXT", |
|||
"data_format": null, |
|||
"data_unit": null, |
|||
"dataRange": null, |
|||
"multi": false, |
|||
"propValue": null, |
|||
"childrenList": null |
|||
}, |
|||
{ |
|||
"id": "64084c01e4b047daf5922a09", |
|||
"name": "描述信息", |
|||
"alias": null, |
|||
"parentid": null, |
|||
"groupKey": "", |
|||
"group": null, |
|||
"data_type": "TEXT", |
|||
"data_format": null, |
|||
"data_unit": null, |
|||
"dataRange": null, |
|||
"multi": false, |
|||
"propValue": null, |
|||
"childrenList": null |
|||
}, |
|||
{ |
|||
"id": "64084572e4b047daf59229ed", |
|||
"name": "活跃等级", |
|||
"alias": null, |
|||
"parentid": null, |
|||
"groupKey": "", |
|||
"group": null, |
|||
"data_type": "STRING", |
|||
"data_format": null, |
|||
"data_unit": null, |
|||
"dataRange": null, |
|||
"multi": false, |
|||
"propValue": null, |
|||
"childrenList": null |
|||
}, |
|||
{ |
|||
"id": "64084564e4b047daf59229ec", |
|||
"name": "人物现状", |
|||
"alias": null, |
|||
"parentid": null, |
|||
"groupKey": "", |
|||
"group": null, |
|||
"data_type": "LONG", |
|||
"data_format": null, |
|||
"data_unit": null, |
|||
"dataRange": null, |
|||
"multi": false, |
|||
"propValue": null, |
|||
"childrenList": null |
|||
}, |
|||
{ |
|||
"id": "64084578e4b047daf59229ee", |
|||
"name": "代表作品", |
|||
"alias": null, |
|||
"parentid": null, |
|||
"groupKey": "", |
|||
"group": null, |
|||
"data_type": "TEXT", |
|||
"data_format": null, |
|||
"data_unit": null, |
|||
"dataRange": null, |
|||
"multi": false, |
|||
"propValue": null, |
|||
"childrenList": null |
|||
} |
|||
], |
|||
"parentPropertyList": [ |
|||
{ |
|||
"id": "64082b8ae4b047daf5922999", |
|||
"name": "人物基本信息", |
|||
"alias": null, |
|||
"parentid": null, |
|||
"groupKey": "", |
|||
"group": null, |
|||
"data_type": "PARENT", |
|||
"data_format": null, |
|||
"data_unit": null, |
|||
"dataRange": null, |
|||
"multi": false, |
|||
"propValue": null, |
|||
"childrenList": [ |
|||
[ |
|||
{ |
|||
"id": "64082bd1e4b047daf592299a", |
|||
"name": "性别", |
|||
"alias": null, |
|||
"parentid": "64082b8ae4b047daf5922999", |
|||
"groupKey": "64082b8ae4b047daf5922999", |
|||
"group": "8d7b96eb1ddf46d8ad95381477d68f03", |
|||
"data_type": "STRING", |
|||
"data_format": null, |
|||
"data_unit": null, |
|||
"dataRange": null, |
|||
"multi": false, |
|||
"propValue": "男", |
|||
"childrenList": null |
|||
}, |
|||
{ |
|||
"id": "640841d4e4b047daf59229c9", |
|||
"name": "出生日期", |
|||
"alias": null, |
|||
"parentid": "64082b8ae4b047daf5922999", |
|||
"groupKey": "64082b8ae4b047daf5922999", |
|||
"group": "8d7b96eb1ddf46d8ad95381477d68f03", |
|||
"data_type": "DATETIME", |
|||
"data_format": "yyyy-MM-dd HH:mm:ss", |
|||
"data_unit": null, |
|||
"dataRange": null, |
|||
"multi": false, |
|||
"propValue": null, |
|||
"childrenList": null |
|||
}, |
|||
{ |
|||
"id": "64084205e4b047daf59229ca", |
|||
"name": "出生地", |
|||
"alias": null, |
|||
"parentid": "64082b8ae4b047daf5922999", |
|||
"groupKey": "64082b8ae4b047daf5922999", |
|||
"group": "8d7b96eb1ddf46d8ad95381477d68f03", |
|||
"data_type": "STRING", |
|||
"data_format": null, |
|||
"data_unit": null, |
|||
"dataRange": null, |
|||
"multi": false, |
|||
"propValue": "中华人民共和国河南省禹县", |
|||
"childrenList": null |
|||
}, |
|||
{ |
|||
"id": "64084224e4b047daf59229cc", |
|||
"name": "国家地区", |
|||
"alias": null, |
|||
"parentid": "64082b8ae4b047daf5922999", |
|||
"groupKey": "64082b8ae4b047daf5922999", |
|||
"group": "8d7b96eb1ddf46d8ad95381477d68f03", |
|||
"data_type": "STRING", |
|||
"data_format": null, |
|||
"data_unit": null, |
|||
"dataRange": null, |
|||
"multi": false, |
|||
"propValue": "中国", |
|||
"childrenList": null |
|||
}, |
|||
{ |
|||
"id": "640842dee4b047daf59229d1", |
|||
"name": "毕业院校", |
|||
"alias": null, |
|||
"parentid": "64082b8ae4b047daf5922999", |
|||
"groupKey": "64082b8ae4b047daf5922999", |
|||
"group": "8d7b96eb1ddf46d8ad95381477d68f03", |
|||
"data_type": "STRING", |
|||
"data_format": null, |
|||
"data_unit": null, |
|||
"dataRange": null, |
|||
"multi": false, |
|||
"propValue": "东北重型机械学院、哈尔滨工业大学", |
|||
"childrenList": null |
|||
}, |
|||
{ |
|||
"id": "640842efe4b047daf59229d2", |
|||
"name": "民族", |
|||
"alias": null, |
|||
"parentid": "64082b8ae4b047daf5922999", |
|||
"groupKey": "64082b8ae4b047daf5922999", |
|||
"group": "8d7b96eb1ddf46d8ad95381477d68f03", |
|||
"data_type": "STRING", |
|||
"data_format": null, |
|||
"data_unit": null, |
|||
"dataRange": null, |
|||
"multi": false, |
|||
"propValue": "汉族", |
|||
"childrenList": null |
|||
}, |
|||
{ |
|||
"id": "64084325e4b047daf59229d4", |
|||
"name": "现任职务", |
|||
"alias": null, |
|||
"parentid": "64082b8ae4b047daf5922999", |
|||
"groupKey": "64082b8ae4b047daf5922999", |
|||
"group": "8d7b96eb1ddf46d8ad95381477d68f03", |
|||
"data_type": "STRING", |
|||
"data_format": null, |
|||
"data_unit": null, |
|||
"dataRange": null, |
|||
"multi": false, |
|||
"propValue": "全国人民代表大会常务委员会副委员长", |
|||
"childrenList": null |
|||
}, |
|||
{ |
|||
"id": "64084310e4b047daf59229d3", |
|||
"name": "政党派系", |
|||
"alias": null, |
|||
"parentid": "64082b8ae4b047daf5922999", |
|||
"groupKey": "64082b8ae4b047daf5922999", |
|||
"group": "8d7b96eb1ddf46d8ad95381477d68f03", |
|||
"data_type": "STRING", |
|||
"data_format": null, |
|||
"data_unit": null, |
|||
"dataRange": null, |
|||
"multi": false, |
|||
"propValue": "中国共产党", |
|||
"childrenList": null |
|||
} |
|||
] |
|||
] |
|||
} |
|||
], |
|||
"associatedSpecialLibrary": [ |
|||
{ |
|||
"id": "7", |
|||
"specialCode": "ZYK_SR", |
|||
"specialName": "涉日专用库" |
|||
}, |
|||
{ |
|||
"id": "8", |
|||
"specialCode": "ZYK_ST", |
|||
"specialName": "涉台专用库" |
|||
} |
|||
] |
|||
}], |
|||
}) |
|||
|
|||
const anlysisData = Mock.mock({ |
|||
'items|1': [{ |
|||
"d3data": { |
|||
"nodes": [ |
|||
{ |
|||
"shape": null, |
|||
"shapecolor": null, |
|||
"border": null, |
|||
"bordercolor": null, |
|||
"image": "https://tilake.wenge.com/wos/graphlake/entity-icon/gzdx/%E5%BC%A0%E5%86%9B32.glk", |
|||
"imageprtcl": null, |
|||
"confirm": false, |
|||
"concept": "64082742e4b0f6e105e07aab", |
|||
"layer": 10, |
|||
"name": "张军", |
|||
"id": "72860f221138d46fa16e9d6f99af9b38", |
|||
"nickName": null, |
|||
"domain": "9fdfed3888c1cd3c", |
|||
"positionX": null, |
|||
"positionY": null, |
|||
"source_name": null |
|||
}, |
|||
{ |
|||
"shape": null, |
|||
"shapecolor": null, |
|||
"border": null, |
|||
"bordercolor": null, |
|||
"image": "https://tilake.wenge.com/wos/graphlake/entity-icon/gzdx/%E5%BC%A0%E6%98%A5%E8%B4%A414.glk", |
|||
"imageprtcl": null, |
|||
"confirm": false, |
|||
"concept": "64082742e4b0f6e105e07aab", |
|||
"layer": 10, |
|||
"name": "张春贤", |
|||
"id": "faa1329e90c205b9c7e87d9dde4ef990", |
|||
"nickName": null, |
|||
"domain": "9fdfed3888c1cd3c", |
|||
"positionX": null, |
|||
"positionY": null, |
|||
"source_name": null |
|||
}, |
|||
{ |
|||
"shape": null, |
|||
"shapecolor": null, |
|||
"border": null, |
|||
"bordercolor": null, |
|||
"image": "https://tilake.wenge.com/wos/graphlake/entity-icon/gzdx/%E6%9D%A8%E6%99%93%E6%B8%A130.glk", |
|||
"imageprtcl": null, |
|||
"confirm": false, |
|||
"concept": "64082742e4b0f6e105e07aab", |
|||
"layer": 10, |
|||
"name": "杨晓渡", |
|||
"id": "f18ff88ea3a328d71fd68acabbbce824", |
|||
"nickName": null, |
|||
"domain": "9fdfed3888c1cd3c", |
|||
"positionX": null, |
|||
"positionY": null, |
|||
"source_name": null |
|||
}, |
|||
{ |
|||
"shape": null, |
|||
"shapecolor": null, |
|||
"border": null, |
|||
"bordercolor": null, |
|||
"image": "https://tilake.wenge.com/wos/graphlake/entity-icon/gzdx/%E6%9D%8E%E5%85%8B%E5%BC%BA1.glk", |
|||
"imageprtcl": null, |
|||
"confirm": false, |
|||
"concept": "64082742e4b0f6e105e07aab", |
|||
"layer": 10, |
|||
"name": "李克强", |
|||
"id": "3dddc2f34bc6d52be97b990e6af59ee5", |
|||
"nickName": null, |
|||
"domain": "9fdfed3888c1cd3c", |
|||
"positionX": null, |
|||
"positionY": null, |
|||
"source_name": null |
|||
}, |
|||
{ |
|||
"shape": null, |
|||
"shapecolor": null, |
|||
"border": null, |
|||
"bordercolor": null, |
|||
"image": "https://tilake.wenge.com/wos/graphlake/entity-icon/gzdx/%E5%BC%A0%E5%8F%88%E4%BE%A029.glk", |
|||
"imageprtcl": null, |
|||
"confirm": false, |
|||
"concept": "64082742e4b0f6e105e07aab", |
|||
"layer": 10, |
|||
"name": "张又侠", |
|||
"id": "26ef870b0d6aa11360df494af8415b1f", |
|||
"nickName": null, |
|||
"domain": "9fdfed3888c1cd3c", |
|||
"positionX": null, |
|||
"positionY": null, |
|||
"source_name": null |
|||
}, |
|||
{ |
|||
"shape": null, |
|||
"shapecolor": null, |
|||
"border": null, |
|||
"bordercolor": null, |
|||
"image": "https://tilake.wenge.com/wos/graphlake/entity-icon/gzdx/%E7%8E%8B%E4%B8%9C%E6%98%8E20.glk", |
|||
"imageprtcl": null, |
|||
"confirm": false, |
|||
"concept": "64082742e4b0f6e105e07aab", |
|||
"layer": 10, |
|||
"name": "王东明", |
|||
"id": "0ae44f4010a1f461964316dbaef2f136", |
|||
"nickName": null, |
|||
"domain": "9fdfed3888c1cd3c", |
|||
"positionX": null, |
|||
"positionY": null, |
|||
"source_name": null |
|||
} |
|||
], |
|||
"edges": [ |
|||
{ |
|||
"source": 0, |
|||
"target": 1, |
|||
"relation_id": "64083d5ee4b047daf59229a6", |
|||
"relation": "同事", |
|||
"rid": "64185382e4b0e0eb28ce53ac", |
|||
"rids": [ |
|||
{ |
|||
"id": "64185382e4b0e0eb28ce53ac", |
|||
"name": "同事", |
|||
"source": "MANUAL" |
|||
} |
|||
], |
|||
"start": null, |
|||
"end": null, |
|||
"icon": { |
|||
"shape": null, |
|||
"shapecolor": null, |
|||
"border": null, |
|||
"bordercolor": null, |
|||
"image": null, |
|||
"imageprtcl": null |
|||
} |
|||
}, |
|||
{ |
|||
"source": 0, |
|||
"target": 2, |
|||
"relation_id": "64083d5ee4b047daf59229a6", |
|||
"relation": "同事", |
|||
"rid": "641853a9e4b0e0eb28ce53ad", |
|||
"rids": [ |
|||
{ |
|||
"id": "641853a9e4b0e0eb28ce53ad", |
|||
"name": "同事", |
|||
"source": "MANUAL" |
|||
} |
|||
], |
|||
"start": null, |
|||
"end": null, |
|||
"icon": { |
|||
"shape": null, |
|||
"shapecolor": null, |
|||
"border": null, |
|||
"bordercolor": null, |
|||
"image": null, |
|||
"imageprtcl": null |
|||
} |
|||
}, |
|||
{ |
|||
"source": 0, |
|||
"target": 3, |
|||
"relation_id": "64083d5ee4b047daf59229a6", |
|||
"relation": "同事", |
|||
"rid": "64185319e4b0e0eb28ce53aa", |
|||
"rids": [ |
|||
{ |
|||
"id": "64185319e4b0e0eb28ce53aa", |
|||
"name": "同事", |
|||
"source": "MANUAL" |
|||
} |
|||
], |
|||
"start": null, |
|||
"end": null, |
|||
"icon": { |
|||
"shape": null, |
|||
"shapecolor": null, |
|||
"border": null, |
|||
"bordercolor": null, |
|||
"image": null, |
|||
"imageprtcl": null |
|||
} |
|||
}, |
|||
{ |
|||
"source": 0, |
|||
"target": 4, |
|||
"relation_id": "64083d5ee4b047daf59229a6", |
|||
"relation": "同事", |
|||
"rid": "64185046e4b0e0eb28ce53a9", |
|||
"rids": [ |
|||
{ |
|||
"id": "64185046e4b0e0eb28ce53a9", |
|||
"name": "同事", |
|||
"source": "MANUAL" |
|||
} |
|||
], |
|||
"start": null, |
|||
"end": null, |
|||
"icon": { |
|||
"shape": null, |
|||
"shapecolor": null, |
|||
"border": null, |
|||
"bordercolor": null, |
|||
"image": null, |
|||
"imageprtcl": null |
|||
} |
|||
}, |
|||
{ |
|||
"source": 0, |
|||
"target": 5, |
|||
"relation_id": "64083d5ee4b047daf59229a6", |
|||
"relation": "同事", |
|||
"rid": "64185340e4b0e0eb28ce53ab", |
|||
"rids": [ |
|||
{ |
|||
"id": "64185340e4b0e0eb28ce53ab", |
|||
"name": "同事", |
|||
"source": "MANUAL" |
|||
} |
|||
], |
|||
"start": null, |
|||
"end": null, |
|||
"icon": { |
|||
"shape": null, |
|||
"shapecolor": null, |
|||
"border": null, |
|||
"bordercolor": null, |
|||
"image": null, |
|||
"imageprtcl": null |
|||
} |
|||
} |
|||
], |
|||
"ccount": [ |
|||
{ |
|||
"cid": "64082742e4b0f6e105e07aab", |
|||
"cname": "人物", |
|||
"ccount": 6 |
|||
} |
|||
], |
|||
"concepts": { |
|||
"64082742e4b0f6e105e07aab": null |
|||
}, |
|||
"conceptsName": {}, |
|||
"relations": {}, |
|||
"relationInstance": [], |
|||
"rids": [] |
|||
}}], |
|||
}) |
|||
module.exports = [ |
|||
{ |
|||
url: '/knowledge-manage/thematicSearch/analysisFilter', |
|||
type: 'get', |
|||
response: config => { |
|||
const items = analysisFilterData.items |
|||
return { |
|||
code: 200, |
|||
success: true, |
|||
meta: { |
|||
success: true, |
|||
message: "操作成功" |
|||
}, |
|||
total: items.length, |
|||
data: { |
|||
data: items, |
|||
entityData: [], |
|||
instances: [] |
|||
} |
|||
} |
|||
} |
|||
}, |
|||
{ |
|||
url: '/knowledge-manage/thematicSearch/entityInfo', |
|||
type: 'post', |
|||
response: config => { |
|||
const items = entityInfoData.items |
|||
return { |
|||
code: 200, |
|||
success: true, |
|||
meta: { |
|||
success: true, |
|||
message: "操作成功" |
|||
}, |
|||
total: items.length, |
|||
data: items |
|||
} |
|||
} |
|||
}, |
|||
{ |
|||
url: '/knowledge-manage/thematicSearch/anlysis', |
|||
type: 'post', |
|||
response: config => { |
|||
const items = anlysisData.items |
|||
return { |
|||
code: 200, |
|||
success: true, |
|||
meta: { |
|||
success: true, |
|||
message: "操作成功" |
|||
}, |
|||
total: items.length, |
|||
data: items |
|||
} |
|||
} |
|||
} |
|||
|
|||
] |
@ -0,0 +1,25 @@ |
|||
/** |
|||
* @param {string} url |
|||
* @returns {Object} |
|||
*/ |
|||
function param2Obj(url) { |
|||
const search = decodeURIComponent(url.split('?')[1]).replace(/\+/g, ' ') |
|||
if (!search) { |
|||
return {} |
|||
} |
|||
const obj = {} |
|||
const searchArr = search.split('&') |
|||
searchArr.forEach(v => { |
|||
const index = v.indexOf('=') |
|||
if (index !== -1) { |
|||
const name = v.substring(0, index) |
|||
const val = v.substring(index + 1, v.length) |
|||
obj[name] = val |
|||
} |
|||
}) |
|||
return obj |
|||
} |
|||
|
|||
module.exports = { |
|||
param2Obj |
|||
} |
@ -0,0 +1,26 @@ |
|||
import request from '@/utils/request'; |
|||
|
|||
export function getList(params) { |
|||
return request({ |
|||
url: '/knowledge-manage/thematicSearch/analysisFilter', |
|||
method: 'get', |
|||
params |
|||
}); |
|||
} |
|||
|
|||
export function getDetails(data) { |
|||
return request({ |
|||
url: '/knowledge-manage/thematicSearch/entityInfo', |
|||
method: 'post', |
|||
data |
|||
}); |
|||
} |
|||
|
|||
export function getAnlysis(data) { |
|||
return request({ |
|||
url: '/knowledge-manage/thematicSearch/anlysis', |
|||
method: 'post', |
|||
data |
|||
}); |
|||
} |
|||
|
After Width: 1441 | Height: 1124 | Size: 242 KiB |
After Width: 220 | Height: 297 | Size: 126 KiB |
After Width: 812 | Height: 456 | Size: 682 KiB |
After Width: 536 | Height: 589 | Size: 394 KiB |
@ -0,0 +1,23 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<svg width="130px" height="130px" viewBox="0 0 130 130" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> |
|||
<title>默认头像</title> |
|||
<g id="优化-0322" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"> |
|||
<g id="全球人物库-展开搜索条件" transform="translate(-271.000000, -713.000000)"> |
|||
<g id="默认头像" transform="translate(271.000000, 713.000000)"> |
|||
<path d="M5.19999994,0 L124.8,0 C128.266667,0 130,1.73319466 130,5.19958397 L130,124.790017 C130,128.256406 128.266667,129.989601 124.8,129.989601 L5.19999994,129.989601 C1.73333331,129.989601 0,128.256406 0,124.790017 L0,5.19958397 C0,1.73319466 1.73333331,0 5.19999994,0 Z" id="路径" fill="#EDEEF5" fill-rule="nonzero"></path> |
|||
<path d="M50.704056,70.85 C50.873056,71.3231622 54.604368,78.5863687 54.604368,81.25 C54.604368,84.5 53.339,88.387729 53.339,88.387729 C53.339,88.387729 60.6502,97.5467962 64.2356,98.786897 C64.1784,98.8388929 65.156,98.5295176 65.2418,98.6907048 C65.2453687,98.7007978 65.2453687,98.7118092 65.2418,98.7219022 C73.6475999,95.5345573 81.906552,89.05 81.906552,89.05 C81.906552,89.05 79.061996,81.1041517 79.956396,78 C80.588196,75.8109751 84.187012,69.3912047 85.156812,66.95 C86.001812,64.8259699 49.1700559,66.6357372 50.704056,70.85 Z" id="路径" fill="#E3DCDB" fill-rule="nonzero"></path> |
|||
<path d="M87.9000314,53.5652 C87.7960231,59.3528 86.781942,64.3292 85.0554039,68.393 C94.5019595,65.7514 96.7355382,44.7278 87.9000314,53.5652 L87.9000314,53.5652 Z" id="路径" fill="#E3DCDB" fill-rule="nonzero"></path> |
|||
<path d="M47.066365,52.2652 C47.1703733,58.0528 48.1844544,63.0292 49.9109925,67.093 C40.4644369,64.4514 38.2308582,43.4278 47.066365,52.2652 L47.066365,52.2652 Z" id="路径" fill="#E3DCDB" fill-rule="nonzero"></path> |
|||
<path d="M64.6152,99.1612671 C64.5944,99.1014719 65.3354,98.5893128 65.3848,98.4229261 L65.2157999,98.4853212 C65.2157999,98.6153108 64.6151999,99.0312775 64.6152,99.1612671 L64.6152,99.1612671 Z" id="路径" fill="#BDBDBD" fill-rule="nonzero"></path> |
|||
<polygon id="路径-2" fill="#EDEEF5" points="89.707176 94.25 83.206656 88.4 81.906552 88.4 64.355148 98.8 53.304264 87.75 50.054004 89.05 49.403952 91 48.7539 92.95 48.7539 130 92.307384 130"></polygon> |
|||
<path d="M49.933,89.0948725 L49.933,90.7925366 C50.3828,96.0935126 51.0458,100.445564 50.7988,106.042917 C50.4504001,113.925486 49.6574,122.437205 50.8975999,129.992201 L5.19999994,129.989601 C4.72679992,129.989601 4.2692,129.927206 3.83239997,129.807615 C6.23219999,118.529718 4.37839998,114.853612 9.31320003,110.280578 C17.0222001,103.141549 34.6320001,96.8032557 49.933,89.0948725 Z" id="路径" fill="#86909C" fill-rule="nonzero"></path> |
|||
<path d="M63.1618,99.1170706 C63.2294,99.2288617 63.7624001,99.12487 63.7624,99.2652588 C64.48,99.3432526 65.1534,99.3432526 65.8112,99.3172546 L67.7664,99.2132629 C68.4242,99.1872649 69.095,99.1872649 69.8126,99.2652588 C72.1188,99.5174386 71.5442,100.708143 71.5442,100.957723 L67.223,108.585513 C69.173,115.082393 71.2166001,122.187625 72.4984,129.989601 L54.5948,129.989601 C55.0628,123.900888 56.0274,119.340853 57.6862,114.479242 C57.8837999,113.896888 58.422,113.059755 59.0121999,112.199224 L59.527,111.463483 C60.4708,110.12199 61.3522,108.884489 61.074,108.645308 C58.11,106.113111 57.486,103.37813 56.8464,100.957723 C58.9992001,99.9828013 61.4094,99.2808576 62.8966,99.2678586 C63.0838,99.2652588 63.2216,99.3094553 63.1618,99.1170706 L63.1618,99.1170706 Z" id="路径" fill="#86909C" fill-rule="nonzero"></path> |
|||
<path d="M85.046156,89.4822414 C85.3997559,89.9294056 85.5219559,89.9190065 85.8911559,90.3141748 C96.590156,98.0095592 111.802756,102.689185 120.614156,110.311775 C124.456956,113.636909 124.859956,120.544557 125.970156,129.989601 L76.8769559,130 C78.800956,116.436885 81.208556,101.49588 82.973956,88.3929286 C83.616156,88.3929286 84.598956,88.9128869 85.046156,89.4822414 Z" id="路径" fill="#86909C" fill-rule="nonzero"></path> |
|||
<polygon id="路径-2" fill="#FFFFFF" points="83.206656 91 83.206656 88.4 81.906552 88.4 64.355148 98.8 64.355148 100.1 67.605408 100.1 73.455876 109.85 81.906552 95.55"></polygon> |
|||
<polygon id="路径-2备份" fill="#FFFFFF" transform="translate(56.879550, 98.475000) scale(-1, 1) translate(-56.879550, -98.475000) " points="64.355148 89.05 60.454836 87.75 49.403952 98.8 49.403952 100.1 52.00416 99.45 61.104888 109.2 64.355148 92.3"></polygon> |
|||
<path d="M83.1260495,39.8528 C64.742579,44.018 57.1239695,35.5888 53.3640688,33.0486 C47.3731895,37.8222 48.7955033,48.893 46.8635488,50.9236 C46.5593244,50.518 46.2551001,50.1098 45.9508758,49.6028 C44.3257458,95.5084 88.3056638,92.3598 87.9000314,52.0416 C83.9373144,45.9472 84.6497714,45.8458 83.1260495,39.8528 Z" id="路径" fill="#F7EDEB" fill-rule="nonzero"></path> |
|||
<path d="M53.304264,35.75 C57.0641647,38.2876 64.7399788,43.9166 83.1260495,39.7514 C83.9325528,42.7758678 84.0282238,44.8545562 84.4414829,46.9757628 C84.8609543,49.1288558 87.106968,51.35 87.75702,54.6 C88.0776797,54.7139444 88.9091277,52.2120388 89.3125433,51.6708075 C92.8673638,46.901582 97.9132912,19.340691 74.105928,15.6 C60.5220851,13.5068373 50.5891683,18.3225086 46.153692,24.7 C40.4737627,32.8668117 41.7063798,49.7183132 46.153692,55.25 C46.7536132,54.6509342 48.6433677,51.1551207 48.7539,48.75 C48.99932,43.4097967 50.054004,39.65 53.304264,35.75 Z" id="路径" fill="#86909C" fill-rule="nonzero"></path> |
|||
</g> |
|||
</g> |
|||
</g> |
|||
</svg> |
Before Width: 22 | Height: 22 | Size: 601 B |
@ -0,0 +1,17 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<svg width="22px" height="22px" viewBox="0 0 22 22" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> |
|||
<title>标题ICON</title> |
|||
<g id="优化-0322" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"> |
|||
<g id="标题ICON"> |
|||
<rect id="矩形" x="0" y="0" width="22" height="22"></rect> |
|||
<g id="组-2880" transform="translate(2.444444, 0.000000)" fill="#2055F4" fill-rule="nonzero"> |
|||
<g id="编组" transform="translate(4.715580, 10.677568) rotate(-79.000000) translate(-4.715580, -10.677568) translate(-5.618525, 7.882474)"> |
|||
<path d="M-1.38950579e-13,0 C-1.38950579e-13,0 15.9232672,1.54989997 15.9232672,1.54989997 C18.2683776,1.77816219 20.3927845,3.58706327 20.6682109,5.59018768 C20.6682109,5.59018768 4.74499204,4.04029993 4.74499204,4.04029993 C2.39988161,3.81203771 0.27547588,2.00313663 -1.38950579e-13,0 C-1.38950579e-13,0 -1.38950579e-13,0 -1.38950579e-13,0 Z" id="路径"></path> |
|||
</g> |
|||
<g id="编组" transform="translate(11.622068, 10.677568) rotate(-79.000000) translate(-11.622068, -10.677568) translate(1.287963, 7.882474)"> |
|||
<path d="M0,0 C0,0 15.9232672,1.54989997 15.9232672,1.54989997 C18.2683776,1.77816219 20.3927845,3.58706327 20.6682109,5.59018768 C20.6682109,5.59018768 4.74499204,4.04029993 4.74499204,4.04029993 C2.39988161,3.81203771 0.27547588,2.00313663 0,0 C0,0 0,0 0,0 Z" id="路径"></path> |
|||
</g> |
|||
</g> |
|||
</g> |
|||
</g> |
|||
</svg> |
@ -0,0 +1,21 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<svg width="22px" height="22px" viewBox="0 0 22 22" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> |
|||
<title>涉H备份</title> |
|||
<defs> |
|||
<linearGradient x1="50%" y1="1.98897782%" x2="50%" y2="100%" id="linearGradient-1"> |
|||
<stop stop-color="#FF9090" offset="0%"></stop> |
|||
<stop stop-color="#FF0000" offset="100%"></stop> |
|||
</linearGradient> |
|||
<linearGradient x1="50%" y1="1.98897782%" x2="50%" y2="100%" id="linearGradient-2"> |
|||
<stop stop-color="#FFFFFF" offset="0%"></stop> |
|||
<stop stop-color="#FFBDBD" offset="100%"></stop> |
|||
</linearGradient> |
|||
</defs> |
|||
<g id="优化-0322" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"> |
|||
<g id="涉H备份"> |
|||
<polygon id="路径" points="0 0 22 0 22 22 0 22"></polygon> |
|||
<path d="M3.71775,2.67383333 L11.25,1 L18.78225,2.67383333 C19.2015714,2.76704721 19.5,3.13894294 19.5,3.5685 L19.5,12.72325 C19.5,14.5621295 18.580748,16.2792848 17.0506667,17.29925 L11.25,21.1666667 L5.44933333,17.29925 C3.91952217,16.279465 3.00044003,14.5627214 3,12.7241667 L3,3.5685 C3,3.13894294 3.29842859,2.76704721 3.71775,2.67383333 Z" id="路径" fill="url(#linearGradient-1)" fill-rule="nonzero"></path> |
|||
<polygon id="路径" fill="url(#linearGradient-2)" fill-rule="nonzero" points="11.2056385 13.2107863 14.4231719 14.9022083 13.8090051 11.3201159 16.411277 8.78243555 12.8138578 8.25913477 11.2056385 5 9.59632439 8.25913477 6 8.78243555 8.60227186 11.3201159 7.98919982 14.9022083"></polygon> |
|||
</g> |
|||
</g> |
|||
</svg> |
@ -0,0 +1,21 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<svg width="22px" height="22px" viewBox="0 0 22 22" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> |
|||
<title>重点任务备份</title> |
|||
<defs> |
|||
<linearGradient x1="50%" y1="0%" x2="50%" y2="100%" id="linearGradient-1"> |
|||
<stop stop-color="#FFE4A9" offset="0%"></stop> |
|||
<stop stop-color="#FFBB2D" offset="100%"></stop> |
|||
</linearGradient> |
|||
<linearGradient x1="50%" y1="0%" x2="50%" y2="100%" id="linearGradient-2"> |
|||
<stop stop-color="#FFFFFF" offset="0%"></stop> |
|||
<stop stop-color="#FFEABD" offset="100%"></stop> |
|||
</linearGradient> |
|||
</defs> |
|||
<g id="优化-0322" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"> |
|||
<g id="重点任务备份"> |
|||
<polygon id="路径" points="0 0 22 0 22 22 0 22"></polygon> |
|||
<path d="M3.71775,2.67383333 L11.25,1 L18.78225,2.67383333 C19.2015714,2.76704721 19.5,3.13894294 19.5,3.5685 L19.5,12.72325 C19.5,14.5621295 18.580748,16.2792848 17.0506667,17.29925 L11.25,21.1666667 L5.44933333,17.29925 C3.91952217,16.279465 3.00044003,14.5627214 3,12.7241667 L3,3.5685 C3,3.13894294 3.29842859,2.76704721 3.71775,2.67383333 Z" id="路径" fill="url(#linearGradient-1)" fill-rule="nonzero"></path> |
|||
<path d="M11.2209945,4.8 C8.32044199,4.8 6,7.21379311 6,10.2310345 C6,12.1017241 6.98618784,13.912069 8.55248618,14.8775862 L9.13259668,15.2396552 L10.4088398,11.8603448 L10.0607735,11.6189655 C9.65469613,11.2568965 9.48066298,10.7741379 9.48066298,10.2310345 C9.48066298,9.20517242 10.2348066,8.42068966 11.2209945,8.42068966 C12.2071823,8.42068966 12.961326,9.20517242 12.961326,10.2310345 C12.961326,10.7741379 12.7292818,11.2568965 12.3812155,11.6189655 L12.0911602,11.9206897 L13.3674033,15.3 L13.9475138,14.937931 C15.5138121,13.9724138 16.5,12.162069 16.5,10.2913793 C16.4419889,7.21379311 14.121547,4.8 11.2209945,4.8 Z" id="路径" fill="url(#linearGradient-2)" fill-rule="nonzero"></path> |
|||
</g> |
|||
</g> |
|||
</svg> |
@ -0,0 +1,159 @@ |
|||
<template> |
|||
<div class="basic"> |
|||
<div class="top"> |
|||
<div style="display: flex;align-items: center;"> |
|||
<div class="image"> |
|||
<img src="../../assets/image/down.svg" alt=""> |
|||
</div> |
|||
<div class="title"> |
|||
人物简介 |
|||
</div> |
|||
</div> |
|||
<div class="edit"> |
|||
<w-button style="width: 80px;color:#303133;border: 1px solid #DDDFE8; border-radius: 2px;"> |
|||
<template #default> |
|||
<CoolEditLine size="15" color="#303133" style="margin-right: 3px;" />编辑 |
|||
</template> |
|||
</w-button> |
|||
</div> |
|||
|
|||
</div> |
|||
<div class="content"> |
|||
<div class="avatar"> |
|||
<img v-if="basicInfo.image" :src="basicInfo.image" alt=""> |
|||
<img v-else src="../../assets/image/default.svg" alt="" /> |
|||
<div class="" style="color: #909399; text-align: center; line-height: 24px;margin-top: 8px;"> |
|||
蔡英文相关概述图 |
|||
<div> |
|||
(2张) |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="introduction"> |
|||
<div class="peo_row"> |
|||
<div class="item"> |
|||
<span class="left">姓名</span> |
|||
<span class="right">{{ basicInfo.name }}</span> |
|||
</div> |
|||
|
|||
</div> |
|||
<div v-for="(item, index) in parentPropertyList" :key="index" class="peo_row"> |
|||
<div class="item"> |
|||
<span class="left">{{ item.name }}</span> |
|||
<!-- <span class="right">{{ item.propValue }}</span> --> |
|||
<span class="right"> |
|||
<w-text-ellipsis :text="item.propValue" more="..." :height="40" :width="220" use-tooltip placement="bottom"> |
|||
</w-text-ellipsis> |
|||
</span> |
|||
</div> |
|||
|
|||
</div> |
|||
|
|||
</div> |
|||
|
|||
</div> |
|||
</div> |
|||
</template> |
|||
<script> |
|||
// import bus from '@/utils/EventBus'; |
|||
// import { getDetails } from '@/api/peoplePool'; |
|||
export default { |
|||
name: 'BasicInfo', |
|||
props: { |
|||
parentPropertyList: Array, |
|||
basicInfo: Object |
|||
}, |
|||
data() { |
|||
return { |
|||
}; |
|||
}, |
|||
methods: { |
|||
}, |
|||
created() { |
|||
|
|||
}, |
|||
mounted() { |
|||
} |
|||
}; |
|||
</script> |
|||
<style lang='less' scoped> |
|||
.basic { |
|||
width: 100%; |
|||
margin-top: 16px; |
|||
color: #303133; |
|||
font-size: var(--font-size-default); |
|||
background: #fff; |
|||
padding: 24px; |
|||
border-radius: 4px; |
|||
|
|||
} |
|||
|
|||
.top { |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: space-between; |
|||
|
|||
.image { |
|||
width: 18px; |
|||
height: 18px; |
|||
margin-right: 9px; |
|||
|
|||
img { |
|||
width: 100%; |
|||
} |
|||
} |
|||
|
|||
.title { |
|||
font-size: var(--font-size-medium); |
|||
line-height: 26px; |
|||
font-weight: 500; |
|||
} |
|||
} |
|||
|
|||
.content { |
|||
padding-top: 16px; |
|||
display: flex; |
|||
|
|||
.avatar { |
|||
width: 130px; |
|||
|
|||
img { |
|||
width: 130px; |
|||
height: 156px; |
|||
object-fit: cover; |
|||
} |
|||
} |
|||
|
|||
.introduction { |
|||
margin-left: 25px; |
|||
flex: 1; |
|||
display: grid; |
|||
grid-template-columns: repeat(3, minmax(0, 1fr)); |
|||
grid-template-rows: 25px 25px; |
|||
grid-gap: 20px 20px; |
|||
|
|||
.peo_row { |
|||
width: 100%; |
|||
height: 24px; |
|||
line-height: 24px; |
|||
|
|||
.item { |
|||
display: flex; |
|||
|
|||
.left { |
|||
display: block; |
|||
width: 90px; |
|||
color: #909399; |
|||
margin-right: 0px; |
|||
} |
|||
|
|||
.right { |
|||
width: 70%; |
|||
display: block; |
|||
} |
|||
} |
|||
} |
|||
|
|||
} |
|||
} |
|||
</style> |
@ -0,0 +1,125 @@ |
|||
<template> |
|||
<div> |
|||
<div id="content-item0" class="part" style="background: transparent;border-radius: 0px;"> |
|||
<PersonInformation :offsettop="offsettop" :title="name" :job="job" :introduction="introduction"></PersonInformation> |
|||
<basicInfo :parentPropertyList="parentPropertyList" :basicInfo="basicInfo"></basicInfo> |
|||
</div> |
|||
<PhotoVideo id="content-item1" class="part"></PhotoVideo> |
|||
<News id="content-item2" class="part"></News> |
|||
<PeopleDescriptive id="content-item3" class="part"></PeopleDescriptive> |
|||
<PeopleLifetime id="content-item4" class="part"></PeopleLifetime> |
|||
<SocialAnalysis id="content-item5" class="part"></SocialAnalysis> |
|||
<PlatformDynamics id="content-item6" class="part"></PlatformDynamics> |
|||
</div> |
|||
</template> |
|||
<script> |
|||
import bus from '@/utils/EventBus'; |
|||
import PhotoVideo from './photo-video.vue'; |
|||
import News from './news.vue'; |
|||
import PeopleDescriptive from './people-descriptive.vue'; |
|||
import PeopleLifetime from './people-lifetime.vue'; |
|||
import SocialAnalysis from './social-analysis.vue'; |
|||
import PlatformDynamics from './platform-dynamics.vue'; |
|||
import basicInfo from './basic-info.vue'; |
|||
import PersonInformation from './person-information.vue'; |
|||
import { getDetails } from '@/api/peoplePool'; |
|||
export default { |
|||
name: 'Detail', |
|||
components: { |
|||
PersonInformation, |
|||
basicInfo, |
|||
PhotoVideo, |
|||
News, |
|||
PeopleDescriptive, |
|||
PeopleLifetime, |
|||
SocialAnalysis, |
|||
PlatformDynamics |
|||
}, |
|||
props: { |
|||
offsettop: Number |
|||
}, |
|||
data() { |
|||
return { |
|||
loading: true, |
|||
isShow1: false, |
|||
job: '', |
|||
basicInfo: {}, |
|||
name: '', |
|||
introduction: '', |
|||
parentPropertyList: [] |
|||
}; |
|||
}, |
|||
methods: { |
|||
getdeatilInfo() { |
|||
getDetails().then(res => { |
|||
this.basicInfo = res.data; |
|||
res.data.parentPropertyList.forEach(item => { |
|||
if (item.name === '人物基本信息') { |
|||
this.parentPropertyList = item.childrenList[0]; |
|||
} |
|||
}); |
|||
this.parentPropertyList.forEach(m => { |
|||
if (m.name === '现任职务') { |
|||
// bus.$emit('job', m.propValue); |
|||
this.job = m.propValue; |
|||
} else if (m.name === '政党派系') { |
|||
// bus.$emit('introduction', m.propValue); |
|||
this.introduction = m.propValue; |
|||
} |
|||
}); |
|||
this.name = this.basicInfo.name; |
|||
bus.$emit('load2', false); |
|||
// bus.$emit('show', true); |
|||
this.$emit('show', true); |
|||
|
|||
}); |
|||
|
|||
} |
|||
|
|||
}, |
|||
watch: { |
|||
|
|||
// screenWidth() { |
|||
// if (this.screenWidth < 1480) { |
|||
// document.querySelector('.detailGlobal').style.marginRight = '160px'; |
|||
// document.querySelector('.detailGlobal').style.marginLeft = '160px'; |
|||
|
|||
// } else { |
|||
// // document.querySelector('.tabList').style.display = 'block'; |
|||
// document.querySelector('.detailGlobal').style.marginRight = 'calc((100vw - 1200px) / 2)'; |
|||
// document.querySelector('.detailGlobal').style.marginLeft = 'calc(50vw - 600px)'; |
|||
|
|||
// } |
|||
// if (this.screenWidth < 1300) { |
|||
// document.querySelector('.tabList').style.display = 'none'; |
|||
|
|||
// } else { |
|||
// document.querySelector('.tabList').style.display = 'block'; |
|||
|
|||
// } |
|||
// } |
|||
}, |
|||
created() { |
|||
|
|||
}, |
|||
mounted() { |
|||
this.getdeatilInfo(); |
|||
// this.target = document.documentElement; |
|||
this.screenWidth = document.body.clientWidth; |
|||
window.onresize = () => { |
|||
return (() => { |
|||
this.screenWidth = document.body.clientWidth; |
|||
})(); |
|||
}; |
|||
|
|||
} |
|||
}; |
|||
</script> |
|||
<style lang="less" scoped> |
|||
.part { |
|||
width: 100%; |
|||
border-radius: 4px; |
|||
background: #fff; |
|||
margin-bottom: 16px; |
|||
} |
|||
</style> |
@ -1,208 +1,32 @@ |
|||
<template> |
|||
<div class="profile"> |
|||
<div class="info"> |
|||
<div class="name"> |
|||
<div style="display: flex;align-items: center;margin-bottom: 8px;"> |
|||
<div class="title">蔡英文</div> |
|||
<div class="address">(台湾地区领导人)</div> |
|||
</div> |
|||
<div class="identity"> |
|||
台湾地区政治人物,民主进步党籍,现任台湾地区领导人 |
|||
</div> |
|||
|
|||
</div> |
|||
<div class="edit"> |
|||
<w-button style="margin-right: 16px;"> |
|||
<template #default> <CoolEditLine size="15" color="#303133" style="margin-right: 5px;"/>编辑</template> |
|||
</w-button> |
|||
<w-button> |
|||
<template #icon> |
|||
<icon-delete /> |
|||
</template> |
|||
<template #default><CoolArrowLeftSLine size="15" color="#303133" style="margin-right: 5px;"/>返回</template> |
|||
</w-button> |
|||
|
|||
</div> |
|||
|
|||
</div> |
|||
<div class="content"> |
|||
<div class="avart"> |
|||
<w-avatar shape="square" :size="130" fit="contain" |
|||
src="http://winbox.wenge.com/static/external_images/212516-15666531161ade.jpg"></w-avatar> |
|||
<div class="" style="color: #909399; text-align: center; line-height: 24px;margin-top: 8px;"> |
|||
蔡英文相关概述图 |
|||
<div> |
|||
(2张) |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="introduction"> |
|||
<div class="peo_row"> |
|||
<div class="item"> |
|||
<span class="left">姓名</span> |
|||
<span class="right">蔡英文</span> |
|||
</div> |
|||
<div class="item"> |
|||
<span class="left">外文名</span> |
|||
<span class="right">Tsai Ing-wen</span> |
|||
</div> |
|||
<div class="item"> |
|||
<span class="left">出生年月</span> |
|||
<span class="right">1956年8月31日</span> |
|||
</div> |
|||
<div class="item"> |
|||
<span class="left">出生地</span> |
|||
<span class="right">台北市中山区</span> |
|||
</div> |
|||
|
|||
</div> |
|||
<div class="peo_row"> |
|||
<div class="item"> |
|||
<span class="left">国家/地区</span> |
|||
<span class="right">中国</span> |
|||
</div> |
|||
<div class="item"> |
|||
<span class="left">民族</span> |
|||
<span class="right">汉族</span> |
|||
</div> |
|||
<div class="item"> |
|||
<span class="left">所属组织</span> |
|||
<span class="right">民主进步党</span> |
|||
</div> |
|||
<div class="item"> |
|||
<span class="left">组织职位</span> |
|||
<span class="right">前党主席</span> |
|||
</div> |
|||
|
|||
</div> |
|||
<div class="peo_row"> |
|||
<div class="item" style="width: 50%;"> |
|||
<span class="left">毕业院校</span> |
|||
<span class="right">台湾大学,康乃尔大学,伦敦政治经济学院</span> |
|||
</div> |
|||
<div class="item" style="width: 50%;"> |
|||
<span class="left">代表作品</span> |
|||
<span class="right">《英派:点亮台湾的这一哩路》</span> |
|||
</div> |
|||
|
|||
</div> |
|||
<div class="peo_row"> |
|||
<div class="item" style="width: 100%;"> |
|||
<span class="left">社交账号</span> |
|||
<span class="right"> |
|||
<span style="margin-right: 30px;">Facebook: xxxxxxxxxx</span> |
|||
<span style="margin-right: 30px;">Twitter:xxxxxxxxxxxx</span> |
|||
<span style="margin-right: 30px;">Youtube:xxxXXXX</span> |
|||
<span style="margin-right: 30dpx;">Instagram:XXXXXXXX</span> |
|||
<span>TikTok:xXXXXXXX</span> |
|||
</span> |
|||
</div> |
|||
|
|||
</div> |
|||
<div class="peo_row"> |
|||
<div class="item" style="width: 100%;"> |
|||
<span class="left">人物履历</span> |
|||
<div class="right"> |
|||
<div style="margin-bottom: 8px;">2020年1月11日,蔡英文当选连任台湾地区领导人。</div> |
|||
<div style="margin-bottom: 8px;">2020年1月11日,蔡英文当选连任台湾地区领导人。</div> |
|||
<div>2020年1月11日,蔡英文当选连任台湾地区领导人。</div> |
|||
|
|||
</div> |
|||
</div> |
|||
|
|||
</div> |
|||
|
|||
</div> |
|||
|
|||
</div> |
|||
|
|||
<PersonInformation :offsettop="56"></PersonInformation> |
|||
<basicInfo></basicInfo> |
|||
</div> |
|||
</template> |
|||
<script> |
|||
import bus from '@/utils/EventBus'; |
|||
import basicInfo from './basic-info.vue'; |
|||
import PersonInformation from './person-information.vue'; |
|||
export default { |
|||
name: 'Profile' |
|||
name: 'Profile', |
|||
components: { |
|||
PersonInformation, |
|||
basicInfo |
|||
}, |
|||
methods: { |
|||
goBack() { |
|||
bus.$emit('detail', true); |
|||
} |
|||
} |
|||
}; |
|||
</script> |
|||
<style lang='less' scoped> |
|||
.profile { |
|||
width: 100%; |
|||
// min-height: 350px !important; |
|||
margin-top: 16px; |
|||
color: #303133; |
|||
font-size: 16px; |
|||
|
|||
.info { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
width: 100%; |
|||
padding: 24px; |
|||
background: url(../../assets/image/detail_title_bg.a6a1e552.png) no-repeat; |
|||
|
|||
.name { |
|||
.title { |
|||
font-size: 30px; |
|||
width: 90px; |
|||
height: 38px; |
|||
line-height: 38px; |
|||
margin-right: 8px; |
|||
} |
|||
|
|||
.address { |
|||
width: 198px; |
|||
height: 24px; |
|||
font-size: 22px; |
|||
line-height: 24px; |
|||
color: #8A909A; |
|||
} |
|||
|
|||
.identity { |
|||
width: 400px; |
|||
height: 24px; |
|||
line-height: 24px; |
|||
color: #272A31; |
|||
} |
|||
} |
|||
|
|||
.edit {} |
|||
} |
|||
|
|||
.content { |
|||
padding: 24px; |
|||
display: flex; |
|||
|
|||
.avart {} |
|||
|
|||
.introduction { |
|||
margin-left: 25px; |
|||
flex: 1; |
|||
|
|||
.peo_row { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
// min-height: 36px; |
|||
line-height: 24px; |
|||
margin-bottom: 16px; |
|||
|
|||
.item { |
|||
width: 25%; |
|||
display: flex; |
|||
|
|||
.left { |
|||
display: block; |
|||
width: 100px; |
|||
color: #909399; |
|||
} |
|||
|
|||
.right { |
|||
width: 70%; |
|||
display: block; |
|||
} |
|||
} |
|||
} |
|||
|
|||
} |
|||
} |
|||
font-size: var(--font-size-default); |
|||
} |
|||
</style> |
|||
|
@ -0,0 +1,98 @@ |
|||
<template> |
|||
<div> |
|||
<w-affix :offset-top="offsettop"> |
|||
<div class="info"> |
|||
<div class="name"> |
|||
<div style="display: flex;align-items: center;margin-bottom: 8px;"> |
|||
<div class="title">{{ title }}</div> |
|||
<div class="address">({{ job }})</div> |
|||
</div> |
|||
<div class="identity"> |
|||
{{ introduction }},现任{{ job }} |
|||
</div> |
|||
|
|||
</div> |
|||
|
|||
</div> |
|||
</w-affix> |
|||
|
|||
</div> |
|||
</template> |
|||
<script> |
|||
export default { |
|||
name: 'PersonInformation', |
|||
props: { |
|||
offsettop: Number, |
|||
title: String, |
|||
job: String, |
|||
introduction: String |
|||
}, |
|||
data() { |
|||
return { |
|||
}; |
|||
}, |
|||
methods: { |
|||
|
|||
}, |
|||
created() { |
|||
}, |
|||
mounted() { |
|||
// bus.$on('name', val => { |
|||
// this.name = val; |
|||
// }); |
|||
// bus.$on('job', val => { |
|||
// this.job = val; |
|||
// }); |
|||
// bus.$on('introduction', val => { |
|||
// this.introduction = val; |
|||
// }); |
|||
|
|||
} |
|||
|
|||
}; |
|||
</script> |
|||
<style lang='less' scoped> |
|||
.info { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
width: 100%; |
|||
padding: 14px 24px; |
|||
background: url(../../assets/image/detail_title_bg.a6a1e552.png) no-repeat; |
|||
border-radius: 4px; |
|||
|
|||
.name { |
|||
.title { |
|||
font-size: 30px; |
|||
height: 38px; |
|||
line-height: 38px; |
|||
margin-right: 8px; |
|||
color: #303133; |
|||
} |
|||
|
|||
.address { |
|||
width: 600px; |
|||
height: 24px; |
|||
font-size: 22px; |
|||
line-height: 24px; |
|||
color: #8A909A; |
|||
overflow: hidden; |
|||
text-overflow: ellipsis; |
|||
white-space: nowrap; |
|||
} |
|||
|
|||
.identity { |
|||
width: 600px; |
|||
height: 24px; |
|||
line-height: 24px; |
|||
color: #272A31; |
|||
overflow: hidden; |
|||
text-overflow: ellipsis; |
|||
white-space: nowrap; |
|||
font-size: var(--font-size-default); |
|||
} |
|||
} |
|||
} |
|||
.w-affix__fixed .info{ |
|||
box-shadow: 0 2px 10px 0 rgb(0 0 0 / 12%); |
|||
} |
|||
</style> |
@ -0,0 +1,457 @@ |
|||
<template> |
|||
<div class="recommendation"> |
|||
<!-- <div class="top"> |
|||
{{ content }} |
|||
</div> --> |
|||
<w-tabs v-model="content" type="card" class="tablist"> |
|||
<w-tab-pane label="智能推荐" name="智能推荐"> |
|||
<div class="intelligent"> |
|||
<w-card class="card"> |
|||
<div class="card-content"> |
|||
<div class="name"> |
|||
<w-avatar src='http://winbox.wenge.com/static/external_images/212516-15666531161ade.jpg' |
|||
:size="32"></w-avatar> |
|||
<span style="color:#2055F4; line-height: 24px;margin-left: 10px;">阿利娅·加尼姆</span> |
|||
</div> |
|||
<div style="margin-bottom: 16px;"> |
|||
<span style="color:#909399;margin-right: 15px;">扩线依据</span> |
|||
<span>基于组织</span> |
|||
</div> |
|||
<div class="cause"> |
|||
<span style="color:#909399;margin-right: 15px;">原因分析</span> |
|||
<div>阿利娅·加尼姆与当前目标同属一个组织“<br><span style="color:#2055F4;">基地组织</span>”。</div> |
|||
</div> |
|||
</div> |
|||
</w-card> |
|||
<w-card class="card"> |
|||
<div class="card-content"> |
|||
<div class="name"> |
|||
<w-avatar src='http://winbox.wenge.com/static/external_images/212516-15666531161ade.jpg' |
|||
:size="32"></w-avatar> |
|||
<span style="color:#2055F4; line-height: 24px;margin-left: 10px;">法赫德·库索</span> |
|||
</div> |
|||
<div style="margin-bottom: 16px;"> |
|||
<span style="color:#909399;margin-right: 15px;">扩线依据</span> |
|||
<span>基于事件</span> |
|||
</div> |
|||
<div class="cause"> |
|||
<span style="color:#909399;margin-right: 15px;">原因分析</span> |
|||
<div>法赫德·库索与当前目标共同参与过事件:<br><span style="color:#2055F4;">拉子市恐怖袭击事件</span></div> |
|||
</div> |
|||
</div> |
|||
</w-card> |
|||
</div> |
|||
</w-tab-pane> |
|||
<w-tab-pane label="实体统计" name="实体统计"> |
|||
<div class="thing"> |
|||
<w-card class="card" style="margin-bottom: 16px;"> |
|||
<div class="content"> |
|||
<div class="title"> |
|||
<CoolArrowDownSLine size="15" color="#5A606B " style="margin-right: 8px;" /> |
|||
<div class="name">实体</div> |
|||
</div> |
|||
<div class="num"> |
|||
85个 |
|||
</div> |
|||
</div> |
|||
<div class="item"> |
|||
<div class="title"> |
|||
<CoolEyeLine size="15" color="#5A606B " style="margin-right: 8px;" /> |
|||
<div class="name">人物</div> |
|||
</div> |
|||
<div class="num"> |
|||
50个 |
|||
</div> |
|||
</div> |
|||
<div class="item"> |
|||
<div class="title"> |
|||
<CoolEyeLine size="15" color="#5A606B " style="margin-right: 8px;" /> |
|||
<div class="name">组织机构</div> |
|||
</div> |
|||
<div class="num"> |
|||
15个 |
|||
</div> |
|||
</div> |
|||
<div class="item"> |
|||
<div class="title"> |
|||
<CoolEyeLine size="15" color="#5A606B " style="margin-right: 8px;" /> |
|||
<div class="name" style="color: #909399;">事件</div> |
|||
</div> |
|||
<div class="num"> |
|||
10个 |
|||
</div> |
|||
</div> |
|||
<div class="content"> |
|||
<div class="title"> |
|||
<CoolEyeLine size="15" color="#5A606B " style="margin-right: 8px;" /> |
|||
<div class="name" style="color: #909399;">社交账号</div> |
|||
</div> |
|||
<div class="num"> |
|||
10个 |
|||
</div> |
|||
</div> |
|||
</w-card> |
|||
<w-card class="card"> |
|||
<div class="content"> |
|||
<div class="title"> |
|||
<CoolArrowDownSLine size="15" color="#5A606B " style="margin-right: 8px;" /> |
|||
<div class="name">实体</div> |
|||
</div> |
|||
<div class="num"> |
|||
85个 |
|||
</div> |
|||
</div> |
|||
<div class="item"> |
|||
<div class="title"> |
|||
<CoolEyeLine size="15" color="#5A606B " style="margin-right: 8px;" /> |
|||
<div class="name">人物</div> |
|||
</div> |
|||
<div class="num"> |
|||
50个 |
|||
</div> |
|||
</div> |
|||
<div class="item"> |
|||
<div class="title"> |
|||
<CoolEyeLine size="15" color="#5A606B " style="margin-right: 8px;" /> |
|||
<div class="name">组织机构</div> |
|||
</div> |
|||
<div class="num"> |
|||
15个 |
|||
</div> |
|||
</div> |
|||
<div class="item"> |
|||
<div class="title"> |
|||
<CoolEyeLine size="15" color="#5A606B " style="margin-right: 8px;" /> |
|||
<div class="name" style="color: #909399;">事件</div> |
|||
</div> |
|||
<div class="num"> |
|||
10个 |
|||
</div> |
|||
</div> |
|||
<div class="content"> |
|||
<div class="title"> |
|||
<CoolEyeLine size="15" color="#5A606B " style="margin-right: 8px;" /> |
|||
<div class="name" style="color: #909399;">社交账号</div> |
|||
</div> |
|||
<div class="num"> |
|||
10个 |
|||
</div> |
|||
</div> |
|||
</w-card> |
|||
</div> |
|||
|
|||
</w-tab-pane> |
|||
<w-tab-pane label="节点信息" name="节点信息"> |
|||
<div class="information"> |
|||
<w-card class="card"> |
|||
<div class="card-content"> |
|||
<div class="name"> |
|||
<w-avatar src='http://winbox.wenge.com/static/external_images/212516-15666531161ade.jpg' |
|||
:size="32"></w-avatar> |
|||
<span style="color:#2055F4; line-height: 24px;margin-left: 10px;">阿利娅·加尼姆</span> |
|||
</div> |
|||
<div v-for="(item, index) in infoList" :key="index" class="item"> |
|||
<div class="title">{{ item.title }}</div> |
|||
<div class="info" :style="{ color: item.title === 'KB组织' ? '#2055F4' : '#303133 ' }">{{ item.contenet }} |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</w-card> |
|||
</div> |
|||
</w-tab-pane> |
|||
</w-tabs> |
|||
|
|||
</div> |
|||
</template> |
|||
<script> |
|||
export default { |
|||
name: 'Recommendation', |
|||
components: { |
|||
}, |
|||
data() { |
|||
return { |
|||
content: '智能推荐', |
|||
infoList: [ |
|||
{ |
|||
title: '扩线依据', |
|||
contenet: '基于组织' |
|||
}, |
|||
{ |
|||
title: '姓名', |
|||
contenet: '阿尔祖玛' |
|||
}, |
|||
{ |
|||
title: '外文名', |
|||
contenet: 'Oaeda.*.:' |
|||
}, |
|||
{ |
|||
title: '别名', |
|||
contenet: '别名' |
|||
}, |
|||
{ |
|||
title: '信仰', |
|||
contenet: '伊斯兰教-逊尼派-瓦哈比派' |
|||
}, |
|||
{ |
|||
title: '国籍', |
|||
contenet: '美国' |
|||
}, |
|||
{ |
|||
title: 'KB组织', |
|||
contenet: '基地组织' |
|||
}, |
|||
{ |
|||
title: '出生日期', |
|||
contenet: '1959年4月9日' |
|||
}, |
|||
{ |
|||
title: '出生地', |
|||
contenet: '伊拉克' |
|||
} |
|||
] |
|||
|
|||
}; |
|||
} |
|||
}; |
|||
</script> |
|||
<style lang='less' scoped> |
|||
.recommendation { |
|||
width: 100%; |
|||
// border: 1px solid #DDDFE8; |
|||
min-height: 570px; |
|||
border-right: none; |
|||
background-color: #F9FAFB; |
|||
padding: 12px; |
|||
|
|||
.tablist { |
|||
width: 100%; |
|||
.intelligent { |
|||
.card { |
|||
margin-bottom: 16px; |
|||
|
|||
.card-content { |
|||
font-size: var(--font-size-small); |
|||
color: #303133; |
|||
|
|||
.name { |
|||
display: flex; |
|||
align-items: center; |
|||
margin-bottom: 16px; |
|||
} |
|||
|
|||
.cause { |
|||
display: flex; |
|||
line-height: 24px; |
|||
|
|||
} |
|||
} |
|||
|
|||
} |
|||
} |
|||
|
|||
.thing { |
|||
font-size: var(--font-size-small); |
|||
|
|||
.content { |
|||
display: flex; |
|||
align-items: center; |
|||
margin-bottom: 16px; |
|||
|
|||
.title { |
|||
display: flex; |
|||
align-items: center; |
|||
margin-right: 16px; |
|||
|
|||
.name { |
|||
width: 56px; |
|||
height: 24px; |
|||
line-height: 24px; |
|||
} |
|||
|
|||
} |
|||
|
|||
.num { |
|||
width: 56px; |
|||
height: 24px; |
|||
line-height: 24px; |
|||
text-align: center; |
|||
border-radius: 2px; |
|||
border: 1px solid #DDDFE8; |
|||
background-color: #FFFFFF; |
|||
color: #272A31; |
|||
} |
|||
} |
|||
|
|||
.item { |
|||
display: flex; |
|||
align-items: center; |
|||
margin-bottom: 16px; |
|||
|
|||
.title { |
|||
display: flex; |
|||
align-items: center; |
|||
margin-right: 16px; |
|||
|
|||
.name { |
|||
width: 56px; |
|||
height: 24px; |
|||
line-height: 24px; |
|||
color: #909399; |
|||
} |
|||
|
|||
} |
|||
|
|||
.num { |
|||
width: 56px; |
|||
height: 24px; |
|||
line-height: 24px; |
|||
text-align: center; |
|||
border-radius: 2px; |
|||
border: 1px solid #DDDFE8; |
|||
background-color: #FFFFFF; |
|||
color: #272A31; |
|||
} |
|||
} |
|||
|
|||
} |
|||
|
|||
.information { |
|||
font-size: var(--font-size-small); |
|||
|
|||
.card-content { |
|||
color: #303133; |
|||
|
|||
.name { |
|||
display: flex; |
|||
align-items: center; |
|||
margin-bottom: 16px; |
|||
} |
|||
|
|||
.item { |
|||
display: flex; |
|||
align-items: center; |
|||
margin-bottom: 16px; |
|||
|
|||
.title { |
|||
color: #909399; |
|||
width: 56px; |
|||
height: 24px; |
|||
line-height: 24px; |
|||
margin-right: 15px; |
|||
} |
|||
|
|||
.info { |
|||
height: 24px; |
|||
line-height: 24px; |
|||
} |
|||
} |
|||
|
|||
.cause { |
|||
display: flex; |
|||
line-height: 24px; |
|||
|
|||
} |
|||
} |
|||
|
|||
} |
|||
|
|||
::v-deep.w-tabs__nav { |
|||
width: 100%; |
|||
display: flex; |
|||
align-items: center; |
|||
} |
|||
|
|||
// // ::v-deep.w-tabs__item { |
|||
// // writing-mode: vertical-lr; |
|||
// // min-height: 184px; |
|||
// // padding: 0 !important; |
|||
// // text-align: center; |
|||
// // background-color: #FFFFFF; |
|||
// font-size: var(--font-size-default); |
|||
// // } |
|||
|
|||
// ::v-deep.w-tabs__content { |
|||
// padding: 16px 0px 16px 16px; |
|||
// overflow-y: auto; |
|||
// max-height: 550px; |
|||
// } |
|||
|
|||
} |
|||
} |
|||
|
|||
::v-deep.w-tabs--card>.w-tabs__header .w-tabs__item { |
|||
border: none !important; |
|||
width: 100%; |
|||
min-width: 25%; |
|||
max-width: 100%; |
|||
text-align: center; |
|||
background: #F0F6FF; |
|||
color: #2055F4; |
|||
margin-left: 0px; |
|||
border-radius:0; |
|||
font-size: var(--font-size-default); |
|||
} |
|||
::v-deep.w-tabs--card>.w-tabs__header .w-tabs__item:first-child{ |
|||
border-radius: 2px 0 0 2px; |
|||
} |
|||
::v-deep.w-tabs--card>.w-tabs__header .w-tabs__item:last-child{ |
|||
border-radius: 0px 2px 2px 0px; |
|||
} |
|||
::v-deep.w-tabs--card>.w-tabs__header .w-tabs__item.is-active{ |
|||
background-color: #2055F4; |
|||
color: #FFFFFF; |
|||
} |
|||
|
|||
// ::v-deep.w-tabs--right .w-tabs__header.is-right{ |
|||
// margin-left: 16px; |
|||
// } |
|||
// ::v-deep.w-tabs--card>.w-tabs__header .w-tabs__item { |
|||
// margin-left: 0px; |
|||
// border-radius: 0px !important; |
|||
// } |
|||
|
|||
// ::v-deep.w-tabs--right.w-tabs--card .w-tabs__nav { |
|||
// border-bottom: none; |
|||
// } |
|||
|
|||
::v-deep.w-tabs--card>.w-tabs__header { |
|||
border-bottom: none; |
|||
} |
|||
|
|||
// ::v-deep.w-tabs--right.w-tabs--card .w-tabs__item.is-right { |
|||
// border: 1px solid #DDDFE8; |
|||
// border-bottom: 1px solid #DDDFE8; |
|||
// border-top: none; |
|||
// } |
|||
|
|||
// ::v-deep.w-tabs--right.w-tabs--card .w-tabs__item.is-right:last-child { |
|||
// border-bottom: none; |
|||
|
|||
// } |
|||
|
|||
// ::v-deep.w-tabs--right.w-tabs--card .w-tabs__item.is-right:first-child { |
|||
// border-left: 1px solid #DDDFE8; |
|||
|
|||
// } |
|||
|
|||
// ::v-deep.w-tabs--right.w-tabs--card .w-tabs__item.is-right.is-active:first-child { |
|||
// border: 1px solid #2055F4; |
|||
// } |
|||
|
|||
// ::v-deep.w-tabs--right.w-tabs--card .w-tabs__item.is-right.is-active { |
|||
// border: 1px solid #2055F4; |
|||
// border-bottom: 1px solid #2055F4; |
|||
// background-color: #F0F6FF; |
|||
// } |
|||
|
|||
// ::v-deep.w-tabs--right.w-tabs--card .w-tabs__item.is-right.is-active:last-child { |
|||
// border-bottom: 1px solid #2055F4; |
|||
// // background-color: #F0F6FF; |
|||
// } |
|||
|
|||
::v-deep.w-card { |
|||
border-radius: 4px; |
|||
border: none; |
|||
// box-shadow: 0 2px 10px 0 rgb(0 0 0 / 12%); |
|||
} |
|||
</style> |
@ -0,0 +1,32 @@ |
|||
<template> |
|||
<div> |
|||
<GraphIndex :d3-data="d3data" |
|||
:graph-type="['gForce', 'dagreLR', 'dagreRL', 'dagreTB', 'grid', 'gridRows', 'gridCols', 'concentric']" |
|||
:select-type="['brushSelect', 'lassoSelect']" /> |
|||
</div> |
|||
</template> |
|||
<script> |
|||
import GraphIndex from '@/components/graphContainer/GraphIndex.vue'; |
|||
import { getAnlysis } from '@/api/peoplePool'; |
|||
export default { |
|||
components: { |
|||
GraphIndex |
|||
}, |
|||
data() { |
|||
return { |
|||
d3data: {} |
|||
}; |
|||
}, |
|||
methods: { |
|||
getData(){ |
|||
getAnlysis().then(res => { |
|||
this.d3data = res.data.d3data; |
|||
}); |
|||
} |
|||
|
|||
}, |
|||
mounted() { |
|||
this.getData(); |
|||
} |
|||
}; |
|||
</script> |
@ -0,0 +1,34 @@ |
|||
<template> |
|||
<w-anchor :offset-top="size" :get-container='getContainer' :target-offset="targetOffset" @click="handleClick"> |
|||
<w-anchor-link href="#content-item0" title="人物简介"></w-anchor-link> |
|||
<w-anchor-link href="#content-item1" title="图册视频"></w-anchor-link> |
|||
<w-anchor-link href="#content-item2" title="新闻资讯"></w-anchor-link> |
|||
<w-anchor-link href="#content-item3" title="描述"></w-anchor-link> |
|||
<w-anchor-link href="#content-item4" title="人物生平"></w-anchor-link> |
|||
<w-anchor-link href="#content-item5" title="社交分析与扩线"></w-anchor-link> |
|||
<w-anchor-link href="#content-item6" title="社交平台发文动态"></w-anchor-link> |
|||
</w-anchor> |
|||
</template> |
|||
<script> |
|||
export default { |
|||
name: 'TracingPoint', |
|||
props: { |
|||
size: Number, |
|||
getContainer: Function |
|||
}, |
|||
data() { |
|||
return { |
|||
targetOffset: undefined |
|||
}; |
|||
}, |
|||
methods: { |
|||
|
|||
handleClick(e, link) { |
|||
e.preventDefault(); |
|||
} |
|||
}, |
|||
mounted() { |
|||
this.targetOffset = window.innerHeight / 2.15; |
|||
} |
|||
}; |
|||
</script> |
1065
src/components/graphContainer/Graph.vue
File diff suppressed because it is too large
View File
@ -0,0 +1,111 @@ |
|||
<template> |
|||
<div class="graphOuter"> |
|||
<!-- top menu --> |
|||
<graph-menu :graph-type="graphType" :select-type="selectType" @downloadFullImage="downloadFullImage" /> |
|||
<!-- graph content --> |
|||
<graph ref="graph" /> |
|||
</div> |
|||
</template> |
|||
<script> |
|||
import { EventBus } from './eventBus'; |
|||
import GraphMenu from './Menu'; |
|||
import Graph from './Graph'; |
|||
export default { |
|||
components: { GraphMenu, Graph }, |
|||
props: { |
|||
graphType: { |
|||
type: Array, |
|||
default: function () { |
|||
return ['gForce']; |
|||
} |
|||
}, |
|||
selectType: { |
|||
type: Array, |
|||
default: function () { |
|||
return []; |
|||
} |
|||
}, |
|||
alllowExportPNG: { |
|||
type: Boolean, |
|||
default: true |
|||
}, |
|||
allowDelSelect: { |
|||
type: Boolean, |
|||
default: true |
|||
}, |
|||
allowMinimap: { |
|||
type: Boolean, |
|||
default: true |
|||
}, |
|||
allowSearch: { |
|||
type: Boolean, |
|||
default: true |
|||
}, |
|||
allowZoom: { |
|||
type: Boolean, |
|||
default: true |
|||
}, |
|||
allowFullScreen: { |
|||
type: Boolean, |
|||
default: true |
|||
}, |
|||
d3Data: { |
|||
type: Object, |
|||
default: function () { |
|||
return {}; |
|||
} |
|||
}, |
|||
graphStyle: { |
|||
type: Object, |
|||
default: function () { |
|||
return { |
|||
labelTextColor: '#E8E9EA', |
|||
backgroundColor: '#F5F6F7', |
|||
borderColor: '#4E596C' |
|||
}; |
|||
} |
|||
} |
|||
}, |
|||
watch: { |
|||
graphStyle: { |
|||
handler: function (val, oldVal) { |
|||
if (val) { |
|||
const mewData = JSON.parse(JSON.stringify(val)); |
|||
this.$refs.graph.getStyle(mewData); |
|||
} |
|||
}, |
|||
deep: true |
|||
}, |
|||
d3Data: { |
|||
handler: function (val, oldVal) { |
|||
if (val) { |
|||
const mewData = JSON.parse(JSON.stringify(val)); |
|||
this.$nextTick(() => { |
|||
this.$refs.graph.beginDraw(mewData); |
|||
}); |
|||
} |
|||
}, |
|||
deep: true |
|||
} |
|||
}, |
|||
mounted() { |
|||
EventBus.$off('clickNodes'); |
|||
EventBus.$on('clickNodes', (obj) => { |
|||
this.$emit('clickNode', obj); |
|||
}); |
|||
}, |
|||
methods: { |
|||
// 导出png图片 |
|||
downloadFullImage(type) { |
|||
this.$refs.graph.downloadFullImage(type); |
|||
} |
|||
} |
|||
}; |
|||
</script> |
|||
<style lang="less" scoped> |
|||
.graphOuter { |
|||
// background: #f4f7fc; |
|||
width: 100%; |
|||
height: 100%; |
|||
} |
|||
</style> |
@ -0,0 +1,347 @@ |
|||
<template> |
|||
<div class="menu"> |
|||
<!-- 左侧操作菜单栏 --> |
|||
<div class="left-handle-menu"> |
|||
<w-tooltip v-if="graphType.includes('gForce')" effect="dark" content="力导向布局" placement="bottom"> |
|||
<!-- <i class="iconfont icon-a-1" @click="switchLayout('gForce')" /> --> |
|||
<w-button type="text" size="small" @click="switchLayout('gForce')"> |
|||
<CoolFlowChart v-if="allowZoom" size="24" color="#909399" /> |
|||
</w-button> |
|||
</w-tooltip> |
|||
<!-- <el-dropdown placement="bottom" popper-class="menuPopOverStyle"> |
|||
<span class="el-dropdown-link"> |
|||
<i class="iconfont icon-shangxiashuxing" /> |
|||
</span> |
|||
<el-dropdown-menu slot="dropdown" class="layout-dropdown"> |
|||
<el-dropdown-item v-if="graphType.includes('dagreTB')" :class="{'active': dagreType === 'TB'}" @click.native="switchLayout('dagre', 'TB')"><i class="iconfont icon-shangxiashuxing" />上下树形</el-dropdown-item> |
|||
<el-dropdown-item v-if="graphType.includes('dagreLR')" :class="{'active': dagreType === 'LR'}" @click.native="switchLayout('dagre', 'LR')"><i class="iconfont icon-zuoceshuxing" />左侧树形</el-dropdown-item> |
|||
<el-dropdown-item v-if="graphType.includes('dagreRL')" :class="{'active': dagreType === 'RL'}" @click.native="switchLayout('dagre', 'RL')"><i class="iconfont icon-youceshuxing" />右侧树形</el-dropdown-item> |
|||
</el-dropdown-menu> |
|||
</el-dropdown> --> |
|||
<w-tooltip v-if="graphType.includes('dagreTB')" effect="dark" content="上下树形" placement="bottom"> |
|||
<!-- <i class="iconfont icon-shangxiashuxing" @click="switchLayout('dagre', 'TB')" /> --> |
|||
<!-- <w-button @click="switchLayout('dagre', 'TB')">111</w-button> --> |
|||
<w-button type="text" size="small" @click="switchLayout('dagre', 'TB')"> |
|||
<CoolOrganizationChart v-if="allowZoom" size="24" color="#909399" /> |
|||
</w-button> |
|||
</w-tooltip> |
|||
<w-tooltip v-if="graphType.includes('grid')" effect="dark" content="网格布局" placement="bottom"> |
|||
<!-- <i class="iconfont icon-a-31" @click="switchLayout('grid')" /> --> |
|||
<w-button type="text" size="small" @click="switchLayout('grid')"> |
|||
<CoolGridFill v-if="allowZoom" size="24" color="#909399" /> |
|||
</w-button> |
|||
</w-tooltip> |
|||
<w-tooltip v-if="graphType.includes('concentric')" effect="dark" content="同心圆布局" placement="bottom"> |
|||
<!-- <i class="iconfont icon-a-4" @click="switchLayout('concentric')" /> --> |
|||
<w-button type="text" size="small" @click="switchLayout('concentric')"> |
|||
<CoolMovie_2Fill v-if="allowZoom" size="24" color="#909399" /> |
|||
</w-button> |
|||
</w-tooltip> |
|||
<!-- <w-tooltip v-if="graphType.includes('gridRows')" effect="dark" content="横向布局" placement="bottom" popper-class="menuPopOverStyle"> |
|||
<i class="iconfont icon-hengxiang" @click="switchLayout('grid', 'rows')" /> |
|||
</w-tooltip> |
|||
<w-tooltip v-if="graphType.includes('gridCols')" effect="dark" content="纵向布局" placement="bottom" popper-class="menuPopOverStyle"> |
|||
<i class="iconfont icon-zongxiang" @click="switchLayout('grid', 'cols')" /> |
|||
</w-tooltip> --> |
|||
<span class="split-line" /> |
|||
<!-- <w-divider direction="vertical"></w-divider> --> |
|||
<!-- <w-tooltip v-if="selectType.includes('brushSelect')" effect="dark" content="框选" placement="bottom" popper-class="menuPopOverStyle"> |
|||
<i class="iconfont icon-kuangxuan" @click="setMode('brushSelect')" /> |
|||
</w-tooltip> |
|||
<w-tooltip v-if="selectType.includes('lassoSelect')" effect="dark" content="不规则选择" placement="bottom" popper-class="menuPopOverStyle"> |
|||
<i class="iconfont icon-buguizexuanzhong" @click="setMode('lassoSelect')" /> |
|||
</w-tooltip> |
|||
<i class="iconfont icon-buquanguanxi disabled" /> |
|||
<span class="split-line" /> --> |
|||
<!-- <i class="iconfont icon-xiazai disabled" /> --> |
|||
<!-- <el-dropdown placement="bottom" popper-class="menuPopOverStyle"> |
|||
<i class="iconfont icon-jietu" /> |
|||
<el-dropdown-menu slot="dropdown" class="layout-dropdown"> |
|||
<el-dropdown-item @click.native="downloadFullImage(2)"> |
|||
<span style="font-size:12px;">全屏快照</span> |
|||
</el-dropdown-item> |
|||
<el-dropdown-item @click.native="downloadFullImage(3)"> |
|||
<span style="font-size:12px;">选取快照</span> |
|||
</el-dropdown-item> |
|||
</el-dropdown-menu> |
|||
</el-dropdown> --> |
|||
<w-tooltip v-if="alllowExportPNG" effect="dark" content="导出图片" placement="bottom" popper-class="menuPopOverStyle"> |
|||
<!-- <iconpark-icon name="image-add-line" size="22" @click="downloadFullImage(1)" /> --> |
|||
<w-button type="text" size="small" @click="downloadFullImage(1)"> |
|||
<CoolImageLine v-if="allowZoom" size="24" color="#909399" /> |
|||
</w-button> |
|||
</w-tooltip> |
|||
<w-tooltip v-if="allowDelSelect" effect="dark" content="删除选中" placement="bottom" popper-class="menuPopOverStyle"> |
|||
<!-- <i :class="{ disabled: focusNodesArr && focusNodesArr.length === 0 }" class="iconfont icon-shanchu" @click="deleteSelected" /> --> |
|||
<w-button type="text" size="small" :disabled="focusNodesArr && focusNodesArr.length === 0 " @click="deleteSelected"> |
|||
<CoolDeleteBinThreeFill v-if="allowZoom" size="24" color="#909399" /> |
|||
</w-button> |
|||
</w-tooltip> |
|||
<!-- <w-tooltip v-if="allowMinimap" effect="dark" content="缩略图" placement="bottom" popper-class="menuPopOverStyle"> |
|||
<i class="iconfont icon-suolvetu" @click="handleMinimap" /> |
|||
</w-tooltip> --> |
|||
<!-- <span class="split-line" /> |
|||
<i class="iconfont icon-shangyibu1 disabled" /> |
|||
<i class="iconfont icon-xiayibu1 disabled" /> --> |
|||
</div> |
|||
<div class="footer"> |
|||
<div class="right-menu"> |
|||
<search v-if="allowSearch" /> |
|||
<span class="split-line" /> |
|||
<w-tooltip effect="dark" content="缩小" placement="bottom"> |
|||
<w-button type="text" size="small" @click="handleZoom('small')"> |
|||
<CoolSubtractLine v-if="allowZoom" size="24" color="#909399" /> |
|||
</w-button> |
|||
</w-tooltip> |
|||
<span class="percentage">{{ parseInt(zoom) }}%</span> |
|||
<w-tooltip effect="dark" content="放大" placement="bottom"> |
|||
<w-button type="text" size="small" @click="handleZoom('big')"> |
|||
<CoolAddLine v-if="allowZoom" size="24" color="#909399" /> |
|||
</w-button> |
|||
</w-tooltip> |
|||
<!-- <span class="split-line" /> --> |
|||
<!-- <w-tooltip v-if="allowFullScreen" effect="dark" content="全屏" placement="top"> |
|||
<i class="iconfont icon-quanjufangda disabled" /> |
|||
</w-tooltip> --> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
import { mapState } from 'vuex'; |
|||
import { EventBus } from './eventBus'; |
|||
import search from './search.vue'; |
|||
|
|||
export default { |
|||
name: 'GraphMenu', |
|||
components: { search }, |
|||
props: { |
|||
graphType: { |
|||
type: Array, |
|||
default: ['gForce'] |
|||
}, |
|||
selectType: { |
|||
type: Array, |
|||
default: [] |
|||
}, |
|||
alllowExportPNG: { |
|||
type: Boolean, |
|||
default: true |
|||
}, |
|||
allowDelSelect: { |
|||
type: Boolean, |
|||
default: true |
|||
}, |
|||
allowMinimap: { |
|||
type: Boolean, |
|||
default: true |
|||
}, |
|||
allowSearch: { |
|||
type: Boolean, |
|||
default: true |
|||
}, |
|||
allowZoom: { |
|||
type: Boolean, |
|||
default: true |
|||
}, |
|||
allowFullScreen: { |
|||
type: Boolean, |
|||
default: true |
|||
} |
|||
}, |
|||
data() { |
|||
return { |
|||
zoom: 100, |
|||
dagreType: '' |
|||
}; |
|||
}, |
|||
created() { |
|||
}, |
|||
computed: { |
|||
...mapState({ |
|||
focusNodesArr: (state) => state.graphMenu.focusNodesArr, |
|||
curZoom: (state) => state.graphMenu.zoom |
|||
}) |
|||
}, |
|||
watch: { |
|||
curZoom(val) { |
|||
const newVal = val * 100; |
|||
this.zoom = newVal; |
|||
} |
|||
}, |
|||
methods: { |
|||
fullSnapShot() { |
|||
const data = graph.toDataURL(); |
|||
}, |
|||
partSnapShot() { |
|||
const data = graph.toDataURL(); |
|||
}, |
|||
handleZoom(type) { |
|||
EventBus.$emit('zoom', type); |
|||
}, |
|||
switchLayout(type, direction) { |
|||
if (direction === 'TB' || direction === 'LR' || direction === 'RL') { |
|||
this.dagreType = direction; |
|||
} else { |
|||
this.dagreType = ''; |
|||
} |
|||
EventBus.$emit('switchLayout', type, direction || ''); |
|||
}, |
|||
downloadFullImage(type) { |
|||
this.$emit('downloadFullImage', type); |
|||
}, |
|||
deleteSelected() { |
|||
EventBus.$emit('deleteSelected'); |
|||
}, |
|||
// 缩略图的显示隐藏 |
|||
handleMinimap() { |
|||
const dom = document.getElementById('minimap'); |
|||
if (dom.style.display === 'none') { |
|||
document.getElementById('minimap').style.display = 'block'; |
|||
} else { |
|||
document.getElementById('minimap').style.display = 'none'; |
|||
} |
|||
}, |
|||
setMode(mode) { |
|||
EventBus.$emit('setMode', mode); |
|||
} |
|||
} |
|||
}; |
|||
</script> |
|||
<style lang="less"> |
|||
.menuPopOverStyle[x-placement^='bottom'] { |
|||
// color: #000 !important; |
|||
// background: rgba(255, 255, 255, 0.8) !important; |
|||
} |
|||
.menuPopOverStyle[x-placement^='bottom'] .popper__arrow::after { |
|||
border-bottom-color: rgba(255, 255, 255, 0.8); |
|||
} |
|||
.menuPopOverStyle[x-placement^='bottom'] .popper__arrow { |
|||
border-bottom-color: rgba(255, 255, 255, 0.8); |
|||
} |
|||
</style> |
|||
<style lang="less" scoped> |
|||
::v-deep.w-button{ |
|||
background-color:#F3F4F5; |
|||
|
|||
} |
|||
.layout-dropdown.el-dropdown-menu { |
|||
background: #2d3648; |
|||
border-radius: 2px; |
|||
border: none; |
|||
top: 68px !important; |
|||
padding: 4px 0 !important; |
|||
.el-dropdown-menu__item { |
|||
padding: 0 12px; |
|||
line-height: 32px; |
|||
color: #ffffff; |
|||
&:hover { |
|||
color: #0081ff; |
|||
} |
|||
&:hover, |
|||
&.active { |
|||
background: rgba(26, 34, 50, 0.5); |
|||
} |
|||
i { |
|||
margin-right: 7px; |
|||
} |
|||
.iconfont { |
|||
font-size: 14px; |
|||
} |
|||
} |
|||
::v-deep .popper__arrow { |
|||
display: none; |
|||
} |
|||
} |
|||
.menu { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
background: #F3F4F5; |
|||
padding: 0 12px; |
|||
overflow: hidden; |
|||
margin-bottom: 2px; |
|||
iconpark-icon { |
|||
cursor: pointer; |
|||
margin-left: 6px; |
|||
position: relative; |
|||
top: 8px; |
|||
color: #b7c9df; |
|||
&.disabled { |
|||
cursor: not-allowed; |
|||
color: rgba(152, 167, 185, 0.5); |
|||
} |
|||
&:hover { |
|||
color: #0081ff; |
|||
} |
|||
} |
|||
.iconfont { |
|||
display: inline-block; |
|||
font-size: 20px; |
|||
color: #b7c9df !important; |
|||
margin: 11.5px 12px; |
|||
vertical-align: middle; |
|||
cursor: pointer; |
|||
&.disabled { |
|||
cursor: not-allowed; |
|||
color: rgba(152, 167, 185, 0.5); |
|||
} |
|||
} |
|||
.left-handle-menu { |
|||
height: 48px; |
|||
display: flex; |
|||
align-items: center; |
|||
i { |
|||
color: #b7c9df; |
|||
&:hover { |
|||
color: #0081ff; |
|||
} |
|||
} |
|||
.split-line { |
|||
width: 1px; |
|||
height: 20px; |
|||
display: inline-block; |
|||
background: rgba(152, 167, 185, 0.3); |
|||
vertical-align: middle; |
|||
margin: 0 12px; |
|||
} |
|||
} |
|||
} |
|||
.footer { |
|||
height: 48px; |
|||
.iconfont { |
|||
display: inline-block; |
|||
font-size: 20px; |
|||
color: #b7c9df; |
|||
margin: 11.5px 12px; |
|||
vertical-align: middle; |
|||
cursor: pointer; |
|||
&.disabled { |
|||
cursor: not-allowed; |
|||
color: rgba(152, 167, 185, 0.5); |
|||
} |
|||
} |
|||
.right-menu { |
|||
width: fit-content; |
|||
height: 100%; |
|||
display: flex; |
|||
align-items: center; |
|||
.percentage { |
|||
font-size: 14px; |
|||
color: #98a7b9; |
|||
line-height: 48px; |
|||
display: inline-block; |
|||
width: 46px; |
|||
text-align: center; |
|||
} |
|||
.split-line { |
|||
width: 1px; |
|||
height: 20px; |
|||
display: inline-block; |
|||
background: rgba(152, 167, 185, 0.3); |
|||
vertical-align: middle; |
|||
margin: 0 12px; |
|||
} |
|||
} |
|||
} |
|||
</style> |
@ -0,0 +1,128 @@ |
|||
|
|||
|
|||
|
|||
|
|||
## 绘图组件 |
|||
|
|||
# 依赖 @antv/g6 vuex qs sass element-ui |
|||
|
|||
|
|||
# 配合Vuex modules 使用, 需在store/index => import chartExplore\component\store\menu注入 |
|||
|
|||
|
|||
# 安装axios |
|||
|
|||
|
|||
## 功能配置项 |
|||
|
|||
<!-- Attributes start --> |
|||
|
|||
# d3Data 绘图数据 必填 |
|||
默认值:{} |
|||
类型:object |
|||
|
|||
# graphType 绘图类型可选项 可选 |
|||
默认值:['gForce'] |
|||
类型:array |
|||
可选值: |
|||
gForce(力导向布局) |
|||
dagreTB(层级布局-上下树形) |
|||
dagreLR(层级布局-左侧树形) |
|||
dagreRL(层级布局-右侧树形) |
|||
grid(网格布局) |
|||
gridRows(横向布局) |
|||
gridCols(纵向布局) |
|||
concentric(同心圆布局) |
|||
|
|||
# selectType 选择类型可选项 可选 |
|||
默认值:无 |
|||
类型:array |
|||
可选值: |
|||
brushSelect(框选) |
|||
lassoSelect(不规则选择) |
|||
|
|||
|
|||
# alllowExportPNG 是否允许导出图片 可选 |
|||
默认值:true |
|||
类型:boolean |
|||
可选值: |
|||
true |
|||
false |
|||
|
|||
|
|||
# allowDelSelect 是否允许选中删除 可选 |
|||
默认值:true |
|||
类型:boolean |
|||
可选值: |
|||
true |
|||
false |
|||
|
|||
|
|||
# allowMinimap 是否允许显示缩略图 可选 |
|||
默认值:true |
|||
类型:boolean |
|||
可选值: |
|||
true |
|||
false |
|||
|
|||
|
|||
# allowSearch 搜索功能 可选 |
|||
默认值:true |
|||
类型:boolean |
|||
可选值: |
|||
true |
|||
false |
|||
|
|||
|
|||
# allowZoom 是否允许缩放 可选 |
|||
默认值:true |
|||
类型:boolean |
|||
可选值: |
|||
true |
|||
false |
|||
|
|||
|
|||
# allowFullScreen 是否允许全屏 可选 |
|||
默认值:true |
|||
类型:boolean |
|||
可选值: |
|||
true |
|||
false |
|||
|
|||
|
|||
# graphStyle 样式配置 可选 |
|||
|
|||
# labelTextColor 节点文本颜色 |
|||
# backgroundColor 背景色 |
|||
# borderColor 链接线颜色 |
|||
默认值:{ |
|||
'labelTextColor':'#E8E9EA', |
|||
'backgroundColor':'#1A2232', |
|||
'borderColor':'#4E596C', |
|||
} |
|||
类型:Object |
|||
|
|||
|
|||
<!-- Attributes end --> |
|||
|
|||
|
|||
|
|||
|
|||
|
|||
|
|||
<!-- Events start --> |
|||
|
|||
# 事件名称 clickNode |
|||
# 回调参数 类型:(object) |
|||
# 参数描述:节点信息 |
|||
|
|||
|
|||
<!-- 方法 end --> |
|||
|
|||
|
|||
|
|||
|
|||
|
|||
|
|||
|
|||
|
@ -0,0 +1,2 @@ |
|||
import Vue from 'vue' |
|||
export const EventBus = new Vue() |
@ -0,0 +1,337 @@ |
|||
<template> |
|||
<div class="extend-dialog"> |
|||
<el-dialog |
|||
v-if="nodeExtendD" |
|||
:close-on-click-modal="false" |
|||
:title="nodeExtendD.title" |
|||
:visible.sync="dialogVisible" |
|||
:width="nodeExtendD.width ? nodeExtendD.width : '30%'" |
|||
@close="handleCancel('form')" |
|||
> |
|||
<el-form ref="form" :model="nodeExtendD.formData" :rules="nodeExtendD.rules" label-width="80px"> |
|||
<el-form-item v-for="(item, index) in nodeExtendD.data" :key="index" :label="item.label" :prop="item.field"> |
|||
<el-select |
|||
v-if="item.inputType === 'select'" |
|||
v-model="nodeExtendD.formData[item.field]" |
|||
:filterable="item.filterable ? item.filterable : false" |
|||
:placeholder="item.placeholder" |
|||
:popper-append-to-body="false" |
|||
:multiple="item.multiple ? item.multiple : false" |
|||
no-data-text="暂无数据" |
|||
:class="item.width" |
|||
> |
|||
<el-option |
|||
v-for="option in item.options" |
|||
:key="item.optionType ? option[item.optionType.value] : option" |
|||
:label="item.optionType ? option[item.optionType.label] : option" |
|||
:value="item.optionType ? option[item.optionType.value] : option" |
|||
/> |
|||
</el-select> |
|||
<w-tree-select |
|||
v-if="item.inputType === 'tree' && dialogVisible" |
|||
v-model="nodeExtendD.formData[item.field]" |
|||
lazy |
|||
:load="loadNode" |
|||
:tree-props="{ label: 'name' }" |
|||
:popper-append-to-body="false" |
|||
value-key="id" |
|||
clearable |
|||
collapse-tags |
|||
:tree-attributes="{ accordion: true, checkOnClickNode: true }" |
|||
empty-text="暂无数据" |
|||
:class="item.width" |
|||
:multiple="item.multiple ? item.multiple : false" |
|||
:placeholder="item.placeholder" |
|||
:tree-data="nodeExtendD.data[1].options" |
|||
/> |
|||
</el-form-item> |
|||
</el-form> |
|||
<span slot="footer" class="dialog-footer"> |
|||
<el-button @click="handleCancel('form')">取 消</el-button> |
|||
<el-button type="primary" @click="handleOk('form')">确 定</el-button> |
|||
</span> |
|||
</el-dialog> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
import { getOntConceptList, getOntRelationList, getRelationType, getOntRelationQuery } from './graph/graph'; |
|||
import { EventBus } from './eventBus'; |
|||
import { globalV } from './graph/globalData'; |
|||
import commonUtils from './graph/common'; |
|||
import menuHandle from './graph/menu'; |
|||
export default { |
|||
data() { |
|||
return { |
|||
dialogVisible: false, |
|||
nodeExtendD: null, |
|||
nodeId: null, |
|||
graphNodeInfo: null |
|||
}; |
|||
}, |
|||
mounted() { |
|||
EventBus.$on('currentNodeInfo', (obj) => { |
|||
this.dialogVisible = obj.dialogVisible; |
|||
this.$nextTick(() => { |
|||
this.nodeExtendD = obj.extendData; |
|||
this.nodeId = obj.nodeId; |
|||
this.graphNodeInfo = obj; |
|||
if (obj.type === 'extend') { |
|||
this.getOntConceptList(obj.expandType); |
|||
} else if (obj.type === 'path') { |
|||
this.pathExplorer(); |
|||
} |
|||
}); |
|||
}); |
|||
// 清空表单和关闭弹窗 |
|||
EventBus.$on('resetForm', () => { |
|||
this.resetForm('form'); |
|||
this.dialogVisible = false; |
|||
}); |
|||
}, |
|||
beforeDestroy() { |
|||
EventBus.$off(['currentNodeInfo', 'resetForm']); |
|||
}, |
|||
methods: { |
|||
// 关系扩展和节点扩展 |
|||
getOntConceptList(type) { |
|||
const params = { |
|||
graphId: this.$route.query.graphId, |
|||
domain: this.$route.query.domain, |
|||
maxdist: 1, |
|||
eid: this.nodeId, |
|||
force: false |
|||
}; |
|||
if (type === 'node') { |
|||
getOntConceptList(params).then((res) => { |
|||
this.nodeExtendD.data[0].options = res; |
|||
this.nodeExtendD.data[0].optionType = { |
|||
label: 'name', |
|||
value: 'id' |
|||
}; |
|||
this.$forceUpdate(); |
|||
}); |
|||
} else if (type === 'edge') { |
|||
getOntRelationList(params).then((res) => { |
|||
this.nodeExtendD.data[0].options = res; |
|||
this.nodeExtendD.data[0].optionType = { |
|||
label: 'name', |
|||
value: 'id' |
|||
}; |
|||
this.$forceUpdate(); |
|||
}); |
|||
} |
|||
}, |
|||
// 扩展功能的表单提交 |
|||
extendFormSubmit(formName) { |
|||
menuHandle |
|||
.getNeighborMixedGraph( |
|||
this.graphNodeInfo.model, |
|||
1, |
|||
this.graphNodeInfo.data, |
|||
this.graphNodeInfo.clusteredData, |
|||
this.graphNodeInfo.currentUnproccessedData, |
|||
globalV.nodeMap, |
|||
globalV.aggregatedNodeMap, |
|||
10, |
|||
this.graphNodeInfo.expandType, |
|||
this.nodeExtendD.formData |
|||
) |
|||
.then((res) => { |
|||
this.resetForm(formName); |
|||
this.dialogVisible = false; |
|||
const mixedGraphData = res; |
|||
commonUtils.updateDataStatistics(res); |
|||
if (mixedGraphData) { |
|||
globalV.cachePositions = commonUtils.cacheNodePositions(this.graphNodeInfo.graph.getNodes()); |
|||
this.graphNodeInfo.currentUnproccessedData = mixedGraphData; |
|||
commonUtils.handleRefreshGraph( |
|||
this.graphNodeInfo.graph, |
|||
this.graphNodeInfo.currentUnproccessedData, |
|||
globalV.CANVAS_WIDTH, |
|||
globalV.CANVAS_HEIGHT, |
|||
globalV.largeGraphMode, |
|||
true, |
|||
false, |
|||
true |
|||
); |
|||
} |
|||
commonUtils.setFocusNodes(this.graphNodeInfo.graph); |
|||
}); |
|||
}, |
|||
// 自定义路径探寻 |
|||
pathExplorer() { |
|||
const data = { |
|||
graphId: this.$route.query.graphId, |
|||
domain: this.$route.query.domain, |
|||
projectId: this.$route.query.projectId, |
|||
pageNo: 1, |
|||
pageSize: 100 |
|||
}; |
|||
getRelationType(data).then((res) => { |
|||
this.nodeExtendD.data[1].options = res.instances ? res.instances : []; |
|||
}); |
|||
}, |
|||
loadNode(node, resolve) { |
|||
if (node.level === 1) { |
|||
const data = { |
|||
domain: this.$route.query.domain, |
|||
pageNo: 1, |
|||
pageSize: 1000, |
|||
id: node.data.id |
|||
}; |
|||
getOntRelationQuery(data).then((res) => { |
|||
resolve(res.instances ? res.instances : []); |
|||
}); |
|||
} else { |
|||
resolve([]); |
|||
} |
|||
}, |
|||
resetForm(formName) { |
|||
this.$refs[formName].resetFields(); |
|||
}, |
|||
handleCancel(formName) { |
|||
this.resetForm(formName); |
|||
this.dialogVisible = false; |
|||
}, |
|||
handleOk(formName) { |
|||
this.$refs[formName].validate((valid) => { |
|||
if (valid) { |
|||
if (this.graphNodeInfo.type === 'extend') { |
|||
this.extendFormSubmit(formName); |
|||
} else if (this.graphNodeInfo.type === 'path') { |
|||
EventBus.$emit('shortestPath', this.graphNodeInfo); |
|||
} |
|||
} else { |
|||
console.log('error submit!!'); |
|||
} |
|||
}); |
|||
} |
|||
} |
|||
}; |
|||
</script> |
|||
|
|||
<style lang="less" scoped> |
|||
::v-deep.el-select-dropdown__list { |
|||
background-color: #2d3648; |
|||
} |
|||
.extend-dialog { |
|||
::v-deep.el-dialog__header { |
|||
background-color: #2d3648; |
|||
.el-dialog__title { |
|||
color: #fff; |
|||
} |
|||
} |
|||
::v-deep.el-dialog__body { |
|||
background-color: #222c3f; |
|||
border-bottom: 1px solid rgba(255, 255, 255, 0.05); |
|||
.el-form-item__label { |
|||
color: #fff; |
|||
} |
|||
.el-input__inner { |
|||
background-color: #1a2232; |
|||
border: 1px solid #333e51; |
|||
color: #ffffff; |
|||
&:hover { |
|||
border-color: #0081ff; |
|||
} |
|||
} |
|||
.w-tree-select { |
|||
.w-tag { |
|||
background: #0081ff; |
|||
color: #fff; |
|||
} |
|||
.w-tree-select-dropdown { |
|||
border: 1px solid #333e51; |
|||
} |
|||
.w-checkbox__input.is-checked .w-checkbox__inner { |
|||
background-color: #0081ff; |
|||
border-color: #0081ff; |
|||
} |
|||
.w-checkbox__inner:hover { |
|||
border-color: #0081ff; |
|||
} |
|||
.w-tree-node__expand-icon { |
|||
color: #fff; |
|||
} |
|||
.w-scrollbar { |
|||
background-color: #1a2232; |
|||
.w-tree { |
|||
background-color: #1a2232; |
|||
.w-tree-node__content { |
|||
background-color: #1a2232; |
|||
color: #fff; |
|||
&:hover { |
|||
background-color: #5caeff; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
.w-input__inner { |
|||
background-color: #1a2232; |
|||
border: 1px solid #333e51; |
|||
color: #ffffff; |
|||
} |
|||
&.width90 { |
|||
width: 90%; |
|||
} |
|||
} |
|||
.el-select { |
|||
width: 100%; |
|||
&.width50 { |
|||
width: 50%; |
|||
} |
|||
&.width90 { |
|||
width: 90%; |
|||
} |
|||
.el-select__input { |
|||
color: #ffffff; |
|||
} |
|||
.el-tag.el-tag--info { |
|||
background-color: #272f3e; |
|||
border-color: #272f3e; |
|||
color: #ffffff; |
|||
.el-tag__close.el-icon-close { |
|||
background-color: transparent; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
::v-deep.el-dialog__footer { |
|||
background-color: #222c3f; |
|||
.el-button--default { |
|||
background-color: #384152; |
|||
color: #fff; |
|||
border-color: #384152; |
|||
} |
|||
} |
|||
::v-deep .el-select-dropdown { |
|||
background-color: #2d3648; |
|||
border: none; |
|||
border-radius: 2px; |
|||
.el-scrollbar__thumb { |
|||
background: rgb(74, 82, 96); |
|||
&:hover { |
|||
background: rgb(74, 82, 96); |
|||
} |
|||
} |
|||
.el-select-dropdown__item { |
|||
height: 28px; |
|||
line-height: 28px; |
|||
color: #ffffff; |
|||
font-weight: normal; |
|||
} |
|||
.el-select-dropdown__item.selected { |
|||
color: #ffffff; |
|||
background: #0081ff; |
|||
} |
|||
.el-select-dropdown__item.hover, |
|||
.el-select-dropdown__item:hover { |
|||
background: rgba(0, 129, 255, 1); |
|||
} |
|||
} |
|||
::v-deep .el-popper .popper__arrow { |
|||
display: none; |
|||
} |
|||
} |
|||
</style> |
@ -0,0 +1,214 @@ |
|||
import { globalV } from './globalData'; |
|||
import commonUtils from './common'; |
|||
import menuHandle from './menu'; |
|||
import store from '@/store'; |
|||
import { EventBus } from '../eventBus'; |
|||
const BindListener = class BindListener { |
|||
constructor(shiftKeydown, largeGraphMode) { |
|||
this.shiftKeydown = shiftKeydown; |
|||
this.largeGraphMode = largeGraphMode; |
|||
} |
|||
bindEvent(graph, data, clusteredData, currentUnproccessedData, nodeMap) { |
|||
graph.on('keydown', (evt) => { |
|||
const code = evt.key; |
|||
if (!code) { |
|||
return; |
|||
} |
|||
if (code.toLowerCase() === 'shift') { |
|||
this.shiftKeydown = true; |
|||
} else { |
|||
this.shiftKeydown = false; |
|||
} |
|||
// ctrl+A全选功能 暂时注释
|
|||
// if (evt.ctrlKey && code.toLowerCase() === 'a') {// ctrl+A全选节点
|
|||
// menuHandle.selectAll(graph)
|
|||
// }
|
|||
}); |
|||
graph.on('keyup', (evt) => { |
|||
const code = evt.key; |
|||
if (!code) { |
|||
return; |
|||
} |
|||
if (code.toLowerCase() === 'shift') { |
|||
this.shiftKeydown = false; |
|||
} |
|||
if (code.toLowerCase() === 'delete') { |
|||
// menuHandle.deleteFocusNodes(graph)
|
|||
} |
|||
}); |
|||
graph.on('contextmenu', (evt) => { |
|||
const g6ContextMenu = document.getElementsByClassName('g6-component-contextmenu')[0]; |
|||
g6ContextMenu.style.cssText += ';padding: 10px 8px;border: 1px solid #e2e2e2'; |
|||
const { item } = evt; |
|||
if (!item) return; |
|||
const itemType = item.getType(); |
|||
const model = item.getModel(); |
|||
if (itemType && model) { |
|||
if (itemType === 'node' && model.level === 0) { |
|||
const { x, y } = model; // 获得该节点的位置,对应 pointX/pointY 坐标
|
|||
const canvasXY = graph.getCanvasByPoint(x, y); |
|||
g6ContextMenu.style.cssText += `;position: absolute;left: ${canvasXY.x}px;top: ${ |
|||
canvasXY.y |
|||
}px;transform: scale(${evt.currentTarget.getZoom()});transform-origin: left center;padding: 0;border: none;`;
|
|||
} |
|||
} |
|||
}); |
|||
graph.on('wheelzoom', (e) => { |
|||
e.stopPropagation(); |
|||
// 这里的 className 根据实际情况而定,默认是 g6-component-tooltip
|
|||
const contextmenu = Array.from(document.getElementsByClassName('g6-component-contextmenu')); |
|||
contextmenu.forEach((contextmenu) => { |
|||
if (contextmenu && contextmenu.style) { |
|||
globalV.zoom = graph.getZoom(); |
|||
store.commit('graphMenu/SET_ZOOM', globalV.zoom); |
|||
contextmenu.style.transform = `scale(${graph.getZoom()})`; |
|||
} |
|||
}); |
|||
const g6ContextMenu = document.getElementsByClassName('g6-component-contextmenu')[0]; |
|||
g6ContextMenu.style.cssText += 'visibility: hidden'; |
|||
}); |
|||
graph.on('node:mouseenter', (evt) => { |
|||
const { item } = evt; |
|||
graph.setItemState(item, 'hover', true); |
|||
item.toFront(); |
|||
}); |
|||
|
|||
graph.on('node:mouseleave', (evt) => { |
|||
const { item } = evt; |
|||
graph.setItemState(item, 'hover', false); |
|||
}); |
|||
|
|||
graph.on('edge:mouseenter', (evt) => { |
|||
const { item } = evt; |
|||
const model = item.getModel(); |
|||
const currentLabel = model.label; |
|||
item.update({ |
|||
label: model.oriLabel |
|||
}); |
|||
model.oriLabel = currentLabel; |
|||
// item.toFront()
|
|||
// item.getSource().toFront()
|
|||
// item.getTarget().toFront()
|
|||
}); |
|||
|
|||
graph.on('edge:mouseleave', (evt) => { |
|||
const { item } = evt; |
|||
const model = item.getModel(); |
|||
const currentLabel = model.label; |
|||
item.update({ |
|||
label: model.oriLabel |
|||
}); |
|||
model.oriLabel = currentLabel; |
|||
}); |
|||
// click node to show the detail drawer
|
|||
graph.on('node:click', (evt) => { |
|||
commonUtils.stopLayout(); |
|||
if (!this.shiftKeydown) commonUtils.clearFocusItemState(graph); |
|||
else commonUtils.clearFocusEdgeState(graph); |
|||
const { item } = evt; |
|||
|
|||
// highlight the clicked node, it is down by click-select
|
|||
if (item.hasState('focus')) { |
|||
graph.setItemState(item, 'focus', false); |
|||
} else { |
|||
graph.setItemState(item, 'focus', true); |
|||
} |
|||
|
|||
if (!this.shiftKeydown) { |
|||
// 将相关边也高亮
|
|||
const relatedEdges = item.getEdges(); |
|||
relatedEdges.forEach((edge) => { |
|||
graph.setItemState(edge, 'focus', true); |
|||
}); |
|||
} |
|||
|
|||
// 获取当前被点击节点的基本信息
|
|||
const model = item.getModel(); |
|||
EventBus.$emit('clickNodes', model); |
|||
if (model.isReal) { |
|||
// 真实节点才显示当前节点信息
|
|||
// 点击节点,将右侧弹窗信息切换到节点信息
|
|||
commonUtils.dispatchDetail(model, true); |
|||
} |
|||
commonUtils.setFocusNodes(graph); |
|||
EventBus.$emit('GraphEvent', { graph, evt, model, type: 'node:click' }); |
|||
}); |
|||
// 双击
|
|||
graph.on('node:dblclick', (evt) => { |
|||
const model = evt.item && evt.item.getModel(); |
|||
if (!model.isReal) return false; |
|||
menuHandle |
|||
.getNeighborMixedGraph( |
|||
model, |
|||
1, |
|||
globalV.basicData, |
|||
globalV.clusteredData, |
|||
globalV.currentUnproccessedData, |
|||
globalV.nodeMap, |
|||
globalV.aggregatedNodeMap, |
|||
10 |
|||
) |
|||
.then((res) => { |
|||
const mixedGraphData = res; |
|||
commonUtils.updateDataStatistics(res); |
|||
if (mixedGraphData) { |
|||
globalV.cachePositions = commonUtils.cacheNodePositions(graph.getNodes()); |
|||
globalV.currentUnproccessedData = mixedGraphData; |
|||
commonUtils.handleRefreshGraph( |
|||
graph, |
|||
globalV.currentUnproccessedData, |
|||
globalV.CANVAS_WIDTH, |
|||
globalV.CANVAS_HEIGHT, |
|||
globalV.largeGraphMode, |
|||
true, |
|||
false, |
|||
true |
|||
); |
|||
} |
|||
commonUtils.setFocusNodes(graph); |
|||
EventBus.$emit('GraphEvent', { graph, evt, model, type: 'node:dblclick' }); |
|||
}); |
|||
}); |
|||
// click edge to show the detail of integrated edge drawer
|
|||
graph.on('edge:click', (evt) => { |
|||
commonUtils.stopLayout(); |
|||
if (!this.shiftKeydown) commonUtils.clearFocusItemState(graph); |
|||
const { item } = evt; |
|||
// highlight the clicked edge
|
|||
graph.setItemState(item, 'focus', true); |
|||
const model = item.getModel(); |
|||
if (model.isReal) { |
|||
// 真实关系才显示当前关系信息
|
|||
// 点击关系,将右侧弹窗信息切换到关系信息
|
|||
commonUtils.dispatchDetail(model, false); |
|||
} |
|||
commonUtils.setFocusNodes(graph); |
|||
EventBus.$emit('clickEdges', model); |
|||
EventBus.$emit('GraphEvent', { graph, evt, model, type: 'edge:click' }); |
|||
}); |
|||
|
|||
// click canvas to cancel all the focus state
|
|||
graph.on('canvas:click', (evt) => { |
|||
commonUtils.clearFocusItemState(graph); |
|||
// console.log(graph.getGroup(), graph.getGroup().getBBox(), graph.getGroup().getCanvasBBox())
|
|||
commonUtils.setFocusNodes(graph); |
|||
EventBus.$emit('GraphEvent', { graph, evt, type: 'canvas:click' }); |
|||
EventBus.$emit('clickCavans', true); |
|||
}); |
|||
|
|||
graph.on('canvas:dragstart', (evt) => { |
|||
const mode = graph.getCurrentMode(); |
|||
if (mode === 'brushSelect' || mode === 'lassoSelect') { |
|||
commonUtils.clearFocusItemState(graph); |
|||
commonUtils.setFocusNodes(graph); |
|||
} |
|||
}); |
|||
|
|||
graph.on('nodeselectchange', (evt) => { |
|||
graph.setMode('default'); |
|||
commonUtils.setFocusNodes(graph); |
|||
}); |
|||
} |
|||
}; |
|||
const bindListener = new BindListener(false, globalV.largeGraphMode); |
|||
export default bindListener; |
@ -0,0 +1,641 @@ |
|||
import G6 from '@antv/g6'; |
|||
import qs from 'qs'; |
|||
import { globalV } from './globalData'; |
|||
import store from '@/store'; |
|||
import { getQueryString, sub } from './publicFunction'; |
|||
const { labelPropagation } = G6.Algorithm; // 图算法
|
|||
const colorSets = G6.Util.getColorSetsBySubjectColors(globalV.subjectColors, globalV.darkBackColor, globalV.theme, globalV.disableColor); |
|||
const { uniqueId } = G6.Util; |
|||
const CommonUtils = class CommonUtils { |
|||
cloneObjectFn(obj) { |
|||
return JSON.parse(JSON.stringify(obj)); |
|||
} |
|||
// 截断长文本。length 为文本截断后长度,elipsis 是后缀
|
|||
formatText(text, length = 5, elipsis = '...') { |
|||
if (!text) return ''; |
|||
if (text.length > length) { |
|||
return `${text.substr(0, length)}${elipsis}`; |
|||
} |
|||
return text; |
|||
} |
|||
labelFormatter(text, minLength = 10) { |
|||
if (text && text.split('').length > minLength) return `${text.substr(0, minLength)}...`; |
|||
return text; |
|||
} |
|||
descendCompare(p) { |
|||
// 这是比较函数
|
|||
return function (m, n) { |
|||
const a = m[p]; |
|||
const b = n[p]; |
|||
return b - a; // 降序
|
|||
}; |
|||
} |
|||
// 格式化nodes和edges的数据(处理成所需要的格式)
|
|||
formatData(data) { |
|||
const g6Data = data; |
|||
if (g6Data.edges && g6Data.edges.length) { |
|||
g6Data.edges = g6Data.edges.map((item) => { |
|||
item.sourceNum = item.source; |
|||
item.targetNum = item.target; |
|||
item.source = g6Data.nodes[item.source].id; |
|||
item.target = g6Data.nodes[item.target].id; |
|||
return item; |
|||
}); |
|||
} |
|||
if (g6Data.nodes && g6Data.nodes.length) { |
|||
g6Data.nodes = g6Data.nodes.map((item) => { |
|||
item.x = item.positionX ? Number(item.positionX) : null; |
|||
item.y = item.positionY ? Number(item.positionY) : null; |
|||
item.name = item.name || ''; |
|||
delete item.layer; |
|||
return item; |
|||
}); |
|||
} |
|||
return g6Data; |
|||
} |
|||
// 停止布局
|
|||
stopLayout() { |
|||
globalV.layout.instance.stop(); |
|||
} |
|||
// 隐藏hiddenItemIds里的元素
|
|||
hideItems(graph) { |
|||
globalV.hiddenItemIds.forEach((id) => { |
|||
graph.hideItem(id); |
|||
graph.clearItemStates(id); |
|||
}); |
|||
} |
|||
showItems(graph) { |
|||
graph.getNodes().forEach((node) => { |
|||
if (!node.isVisible()) graph.showItem(node); |
|||
}); |
|||
graph.getEdges().forEach((edge) => { |
|||
if (!edge.isVisible()) edge.show(); |
|||
}); |
|||
globalV.hiddenItemIds = []; |
|||
this.updateDataStatistics(); |
|||
} |
|||
// 清除图元素focus状态及相应样式
|
|||
clearFocusItemState(graph) { |
|||
if (!graph) return; |
|||
this.clearFocusNodeState(graph); |
|||
this.clearFocusEdgeState(graph); |
|||
} |
|||
// 清除图上所有节点的 focus 状态及相应样式
|
|||
clearFocusNodeState(graph) { |
|||
const focusNodes = graph.findAllByState('node', 'focus'); |
|||
focusNodes.forEach((fnode) => { |
|||
graph.setItemState(fnode, 'focus', false); // false
|
|||
}); |
|||
} |
|||
// 清除图上所有边的 focus 状态及相应样式
|
|||
clearFocusEdgeState(graph) { |
|||
const focusEdges = graph.findAllByState('edge', 'focus'); |
|||
focusEdges.forEach((fedge) => { |
|||
graph.setItemState(fedge, 'focus', false); |
|||
}); |
|||
} |
|||
// 存入当前状态为focus的节点
|
|||
setFocusNodes(graph) { |
|||
const nodes = graph.findAllByState('node', 'focus'); |
|||
store.commit('graphMenu/SET_FOCUSNODESARR', nodes); |
|||
} |
|||
getForceLayoutConfig(graph, largeGraphMode, configSettings) { |
|||
const { linkDistance, edgeStrength, nodeStrength, nodeSpacing, preventOverlap, nodeSize, collideStrength, alpha, alphaDecay, alphaMin } = |
|||
configSettings || { preventOverlap: true }; |
|||
// if (!linkDistance && linkDistance !== 0) linkDistance = 225
|
|||
// if (!edgeStrength && edgeStrength !== 0) edgeStrength = 50
|
|||
// if (!nodeStrength && nodeStrength !== 0) nodeStrength = 200
|
|||
// if (!nodeSpacing && nodeSpacing !== 0) nodeSpacing = 5
|
|||
const config = { |
|||
type: 'gForce', |
|||
// kr: 50,
|
|||
// mode: 'linlog',
|
|||
// minMovement: 0.01,
|
|||
maxIteration: 3, |
|||
maxSpeed: 10000, |
|||
preventOverlap, |
|||
// barnesHut: true,
|
|||
// damping: 0.99,
|
|||
// // 边长度
|
|||
// linkDistance: (d) => {
|
|||
// let dist = linkDistance
|
|||
// const sourceNode = globalV.nodeMap[d.source] || globalV.aggregatedNodeMap[d.source]
|
|||
// const targetNode = globalV.nodeMap[d.target] || globalV.aggregatedNodeMap[d.target]
|
|||
// // // 两端都是聚合点
|
|||
// // if (sourceNode.level && targetNode.level) dist = linkDistance * 3;
|
|||
// // // 一端是聚合点,一端是真实节点
|
|||
// // else if (sourceNode.level || targetNode.level) dist = linkDistance * 1.5;
|
|||
// if (!sourceNode.level && !targetNode.level) dist = linkDistance * 0.3
|
|||
// return dist
|
|||
// },
|
|||
// // 边的作用力(引力)大小
|
|||
// edgeStrength: (d) => {
|
|||
// const sourceNode = globalV.nodeMap[d.source] || globalV.aggregatedNodeMap[d.source]
|
|||
// const targetNode = globalV.nodeMap[d.target] || globalV.aggregatedNodeMap[d.target]
|
|||
// // 聚合节点之间的引力小
|
|||
// if (sourceNode.level && targetNode.level) return edgeStrength / 2
|
|||
// // 聚合节点与真实节点之间引力大
|
|||
// if (sourceNode.level || targetNode.level) return edgeStrength
|
|||
// return edgeStrength
|
|||
// },
|
|||
// // 节点作用力
|
|||
// nodeStrength: (d) => {
|
|||
// // 给离散点引力,让它们聚集
|
|||
// if (d.degree === 0) return 30
|
|||
// // 聚合点的斥力大
|
|||
// if (d.level) return nodeStrength * 2
|
|||
// return nodeStrength
|
|||
// },
|
|||
nodeSize: (d) => { |
|||
if (!nodeSize && d.size) return d.size; |
|||
return 50; |
|||
}, |
|||
// nodeSpacing: (d) => {
|
|||
// if (d.degree === 0) return nodeSpacing * 2
|
|||
// if (d.level) return nodeSpacing
|
|||
// return nodeSpacing
|
|||
// },
|
|||
onLayoutEnd: () => { |
|||
graph.fitCenter(); |
|||
// if (largeGraphMode) {
|
|||
// graph.getEdges().forEach((edge) => {
|
|||
// if (!edge.oriLabel) return
|
|||
// edge.update({
|
|||
// label: this.labelFormatter(edge.oriLabel, globalV.labelMaxLength)
|
|||
// })
|
|||
// })
|
|||
// }
|
|||
}, |
|||
tick: () => { |
|||
graph.refreshPositions(); |
|||
} |
|||
}; |
|||
|
|||
// if (nodeSize) config['nodeSize'] = nodeSize
|
|||
// if (collideStrength) config['collideStrength'] = collideStrength
|
|||
// if (alpha) config['alpha'] = alpha
|
|||
// if (alphaDecay) config['alphaDecay'] = alphaDecay
|
|||
// if (alphaMin) config['alphaMin'] = alphaMin
|
|||
|
|||
return config; |
|||
} |
|||
cacheNodePositions(nodes) { |
|||
const positionMap = {}; |
|||
const nodeLength = nodes.length; |
|||
for (let i = 0; i < nodeLength; i++) { |
|||
const node = nodes[i].getModel(); |
|||
positionMap[node.id] = { |
|||
x: node.x, |
|||
y: node.y, |
|||
level: node.level |
|||
}; |
|||
} |
|||
return positionMap; |
|||
} |
|||
handleRefreshGraph(graph, graphData, width, height, largeGraphMode, edgeLabelVisible, isNewGraph, isExtendHighlight) { |
|||
if (!graphData || !graph) return; |
|||
this.clearFocusItemState(graph); |
|||
// reset the filtering
|
|||
graph.getNodes().forEach((node) => { |
|||
if (!node.isVisible()) node.show(); |
|||
}); |
|||
graph.getEdges().forEach((edge) => { |
|||
if (!edge.isVisible()) edge.show(); |
|||
}); |
|||
|
|||
let nodes = []; |
|||
let edges = []; |
|||
|
|||
nodes = graphData.nodes; |
|||
const processRes = this.processNodesEdges(nodes, graphData.edges || [], width, height, largeGraphMode, edgeLabelVisible, isNewGraph); |
|||
|
|||
edges = processRes.edges; |
|||
graph.changeData({ nodes, edges }); |
|||
|
|||
this.hideItems(graph); |
|||
graph.getNodes().forEach((node) => { |
|||
node.toFront(); |
|||
}); |
|||
|
|||
// layout.instance.stop();
|
|||
// force 需要使用不同 id 的对象才能进行全新的布局,否则会使用原来的引用。因此复制一份节点和边作为 force 的布局数据
|
|||
globalV.layout.instance.init({ |
|||
nodes: graphData.nodes, |
|||
edges |
|||
}); |
|||
|
|||
// globalV.layout.instance.minMovement = 0.0001
|
|||
// layout.instance.getCenter = d => {
|
|||
// const cachePosition = cachePositions[d.id];
|
|||
// if (!cachePosition && (d.x || d.y)) return [d.x, d.y, 10];
|
|||
// else if (cachePosition) return [cachePosition.x, cachePosition.y, 10];
|
|||
// return [width / 2, height / 2, 10];
|
|||
// }
|
|||
globalV.layout.instance.getMass = (d) => { |
|||
const cachePosition = globalV.cachePositions[d.id]; |
|||
if (cachePosition) return 5; |
|||
return 1; |
|||
}; |
|||
globalV.layout.instance.execute(); |
|||
if (isExtendHighlight) { |
|||
// 将扩展出来的节点和边进行高亮
|
|||
const extendArr = store.state.menu.extendData; |
|||
extendArr.nodes.forEach((node) => { |
|||
graph.setItemState(node.id, 'focus', true); |
|||
}); |
|||
extendArr.edges.forEach((edge) => { |
|||
graph.setItemState(edge.id, 'focus', true); |
|||
}); |
|||
store.commit('graphMenu/SET_EXTENDDATA', { nodes: [], edges: [] }); |
|||
} |
|||
return { nodes, edges }; |
|||
} |
|||
processNodesEdges(nodes, edges, width, height, largeGraphMode, edgeLabelVisible, isNewGraph = false) { |
|||
if (!nodes || nodes.length === 0) return {}; |
|||
const currentNodeMap = {}; // 当前节点的map集合,key为节点id,value为节点对象
|
|||
let maxNodeCount = -Infinity; |
|||
const paddingRatio = 0.3; |
|||
const paddingLeft = paddingRatio * width; |
|||
const paddingTop = paddingRatio * height; |
|||
nodes.forEach((node) => { |
|||
node.type = node.level === 0 ? 'real-node' : 'aggregated-node'; |
|||
node.isReal = node.level === 0; |
|||
if (node.isReal) { |
|||
const concepts = globalV.basicData.concepts; |
|||
// 节点的形状、中央图片等显示逻辑(优先级:nodes > concepts > 默认)
|
|||
if (node.image) { |
|||
// 节点有图片
|
|||
if (node.imageprtcl == 'file' || node.imageprtcl == 'fs') { |
|||
// node.img = `${process.env.VUE_APP_BASE_API}/media/${node['imageprtcl']}/${node.image}`
|
|||
node.img = `${process.env.VUE_APP_BASE_API}/media/file/${node.image}`; |
|||
} else if (node.imageprtcl == 'https' || node.imageprtcl == 'http' || node.imageprtcl == 'ftp' || node.imageprtcl == 'base64') { |
|||
node.img = node.image; |
|||
} else { |
|||
node.img = node.image; |
|||
} |
|||
} else if (concepts[node.concept] && concepts[node.concept].image) { |
|||
// 关系有图片
|
|||
if (concepts[node.concept].imageprtcl == 'file' || concepts[node.concept].imageprtcl == 'fs') { |
|||
// node.img = `${process.env.VUE_APP_BASE_API}/media/${concepts[node.concept]['imageprtcl']}/${concepts[node.concept].image}`
|
|||
node.img = `${process.env.VUE_APP_BASE_API}/media/file/${concepts[node.concept].image}`; |
|||
} else if ( |
|||
concepts[node.concept].imageprtcl == 'https' || |
|||
concepts[node.concept].imageprtcl == 'http' || |
|||
concepts[node.concept].imageprtcl == 'ftp' || |
|||
concepts[node.concept].imageprtcl == 'base64' |
|||
) { |
|||
node.img = concepts[node.concept].image; |
|||
} |
|||
} else { |
|||
// 默认图片
|
|||
node.img = './static/defaultNode.png'; |
|||
} |
|||
node.shapeStyle = { |
|||
// border、shape优先级逻辑
|
|||
border: node.border || (concepts[node.concept] && concepts[node.concept].border) || globalV.defaultShapeStyle.border, |
|||
shape: node.shape || (concepts[node.concept] && concepts[node.concept].shape) || globalV.defaultShapeStyle.shape, |
|||
shapecolor: node.shapecolor || (concepts[node.concept] && concepts[node.concept].shapecolor) || globalV.defaultShapeStyle.shapecolor, |
|||
bordercolor: node.bordercolor || (concepts[node.concept] && concepts[node.concept].bordercolor) || globalV.defaultShapeStyle.bordercolor |
|||
}; |
|||
} |
|||
const nodeArr = globalV.basicData.nodes.filter((item) => { |
|||
return item.id === node.id; |
|||
}); |
|||
node.label = nodeArr.length ? nodeArr[0].name : node.id; |
|||
node.oriLabel = node.label; |
|||
// node.label = this.formatText(node.label, globalV.labelMaxLength, '...')
|
|||
node.label = sub(node.label, globalV.labelMaxLength); |
|||
node.degree = 0; |
|||
node.inDegree = 0; |
|||
node.outDegree = 0; |
|||
if (currentNodeMap[node.id]) { |
|||
console.warn('node exists already!', node.id); |
|||
node.id = `${node.id}${Math.random()}`; |
|||
} |
|||
currentNodeMap[node.id] = node; |
|||
if (node.count > maxNodeCount) maxNodeCount = node.count; |
|||
const cachePosition = globalV.cachePositions ? globalV.cachePositions[node.id] : undefined; |
|||
if (cachePosition) { |
|||
node.x = cachePosition.x; |
|||
node.y = cachePosition.y; |
|||
node.new = false; |
|||
} else { |
|||
node.new = !isNewGraph; |
|||
if (globalV.manipulatePosition && !node.x && !node.y) { |
|||
node.x = globalV.manipulatePosition.x + 30 * Math.cos(Math.random() * Math.PI * 2); |
|||
node.y = globalV.manipulatePosition.y + 30 * Math.sin(Math.random() * Math.PI * 2); |
|||
} |
|||
} |
|||
}); |
|||
|
|||
let maxCount = -Infinity; |
|||
let minCount = Infinity; |
|||
// let maxCount = 0;
|
|||
edges.forEach((edge) => { |
|||
// to avoid the dulplicated id to nodes
|
|||
if (!edge.id) edge.id = `edge-${uniqueId()}`; |
|||
// else if (edge.id.split('-')[0] !== 'edge') edge.id = `edge-${edge.id}`;
|
|||
// TODO: delete the following line after the queried data is correct
|
|||
if (!currentNodeMap[edge.source] || !currentNodeMap[edge.target]) { |
|||
console.warn('edge source target does not exist', edge.source, edge.target, edge.id); |
|||
return; |
|||
} |
|||
const sourceNode = currentNodeMap[edge.source]; |
|||
const targetNode = currentNodeMap[edge.target]; |
|||
|
|||
if (!sourceNode || !targetNode) { |
|||
console.warn('source or target is not defined!!!', edge, sourceNode, targetNode); |
|||
} |
|||
|
|||
// calculate the degree
|
|||
sourceNode.degree++; |
|||
targetNode.degree++; |
|||
sourceNode.outDegree++; |
|||
targetNode.inDegree++; |
|||
|
|||
if (edge.count > maxCount) maxCount = edge.count; |
|||
if (edge.count < minCount) minCount = edge.count; |
|||
}); |
|||
|
|||
nodes.sort(this.descendCompare(globalV.NODESIZEMAPPING)); |
|||
const maxDegree = nodes[0].degree || 1; |
|||
|
|||
const descreteNodes = []; |
|||
nodes.forEach((node, i) => { |
|||
// assign the size mapping to the outDegree
|
|||
const countRatio = node.count / maxNodeCount; |
|||
const isRealNode = node.level === 0; |
|||
node.size = isRealNode ? globalV.DEFAULTNODESIZE : globalV.DEFAULTAGGREGATEDNODESIZE; |
|||
node.isReal = isRealNode; |
|||
node.labelCfg = { |
|||
position: 'bottom', |
|||
offset: 5, |
|||
style: { |
|||
fill: globalV.global.node.labelCfg.style.fill, |
|||
// fontSize: 6 + countRatio * 6 || 12
|
|||
fontSize: 12 |
|||
} |
|||
}; |
|||
|
|||
if (!node.degree) { |
|||
descreteNodes.push(node); |
|||
} |
|||
}); |
|||
|
|||
const countRange = maxCount - minCount; |
|||
const minEdgeSize = 1; |
|||
const maxEdgeSize = 7; |
|||
const edgeSizeRange = maxEdgeSize - minEdgeSize; |
|||
edges.forEach((edge) => { |
|||
// set edges' style
|
|||
const targetNode = currentNodeMap[edge.target]; |
|||
|
|||
const size = ((edge.count - minCount) / countRange) * edgeSizeRange + minEdgeSize || 1; |
|||
edge.size = size; |
|||
|
|||
const arrowWidth = Math.max(size / 2 + 2, 3); |
|||
const arrowLength = 10; |
|||
const arrowBeging = targetNode.size + arrowLength; |
|||
let arrowPath = `M ${arrowBeging},0 L ${arrowBeging + arrowLength},-${arrowWidth} L ${arrowBeging + arrowLength},${arrowWidth} Z`; |
|||
const d = targetNode.size / 2 + arrowLength; |
|||
if (edge.source === edge.target) { |
|||
edge.type = 'loop'; |
|||
arrowPath = undefined; |
|||
} |
|||
const sourceNode = currentNodeMap[edge.source]; |
|||
const isRealEdge = targetNode.isReal && sourceNode.isReal; |
|||
edge.isReal = isRealEdge; |
|||
const stroke = isRealEdge ? globalV.global.edge.style.realEdgeStroke : globalV.global.edge.style.stroke; |
|||
const opacity = isRealEdge ? globalV.global.edge.style.realEdgeOpacity : globalV.global.edge.style.strokeOpacity; |
|||
const dash = Math.max(size, 2); |
|||
const lineDash = isRealEdge ? undefined : [dash, dash]; |
|||
edge.style = { |
|||
lineWidth: 1, |
|||
stroke, |
|||
strokeOpacity: opacity, |
|||
cursor: 'pointer', |
|||
lineAppendWidth: Math.max(edge.size || 5, 5), |
|||
fillOpacity: 1, |
|||
lineDash, |
|||
endArrow: arrowPath |
|||
? { |
|||
path: arrowPath, |
|||
d, |
|||
fill: stroke, |
|||
strokeOpacity: 0 |
|||
} |
|||
: false |
|||
}; |
|||
edge.labelCfg = { |
|||
autoRotate: true, |
|||
style: { |
|||
fill: globalV.global.edge.labelCfg.style.fill, |
|||
fontSize: 10, |
|||
lineAppendWidth: 10 |
|||
// 边上的文本背景
|
|||
// background: {
|
|||
// fill: 'rgb(26, 34, 50)',
|
|||
// padding: [2, 5, 2, 5]
|
|||
// }
|
|||
} |
|||
}; |
|||
if (!edge.oriLabel) edge.oriLabel = edge.label; |
|||
if (largeGraphMode || !edgeLabelVisible) edge.label = ''; |
|||
else { |
|||
// edge.label = this.labelFormatter(edge.label, globalV.labelMaxLength)
|
|||
edge.label = sub(edge.label, globalV.labelMaxLength); |
|||
} |
|||
|
|||
// arrange the other nodes around the hub
|
|||
const sourceDis = sourceNode.size / 2 + 20; |
|||
const targetDis = targetNode.size / 2 + 20; |
|||
if (sourceNode.x && !targetNode.x) { |
|||
targetNode.x = sourceNode.x + sourceDis * Math.cos(Math.random() * Math.PI * 2); |
|||
} |
|||
if (sourceNode.y && !targetNode.y) { |
|||
targetNode.y = sourceNode.y + sourceDis * Math.sin(Math.random() * Math.PI * 2); |
|||
} |
|||
if (targetNode.x && !sourceNode.x) { |
|||
sourceNode.x = targetNode.x + targetDis * Math.cos(Math.random() * Math.PI * 2); |
|||
} |
|||
if (targetNode.y && !sourceNode.y) { |
|||
sourceNode.y = targetNode.y + targetDis * Math.sin(Math.random() * Math.PI * 2); |
|||
} |
|||
|
|||
if (!sourceNode.x && !sourceNode.y && globalV.manipulatePosition) { |
|||
sourceNode.x = globalV.manipulatePosition.x + 30 * Math.cos(Math.random() * Math.PI * 2); |
|||
sourceNode.y = globalV.manipulatePosition.y + 30 * Math.sin(Math.random() * Math.PI * 2); |
|||
} |
|||
if (!targetNode.x && !targetNode.y && globalV.manipulatePosition) { |
|||
targetNode.x = globalV.manipulatePosition.x + 30 * Math.cos(Math.random() * Math.PI * 2); |
|||
targetNode.y = globalV.manipulatePosition.y + 30 * Math.sin(Math.random() * Math.PI * 2); |
|||
} |
|||
}); |
|||
|
|||
const descreteNodeCenter = { |
|||
x: width - paddingLeft, |
|||
y: height - paddingTop |
|||
}; |
|||
descreteNodes.forEach((node) => { |
|||
if (!node.x && !node.y) { |
|||
node.x = descreteNodeCenter.x + 30 * Math.cos(Math.random() * Math.PI * 2); |
|||
node.y = descreteNodeCenter.y + 30 * Math.sin(Math.random() * Math.PI * 2); |
|||
} |
|||
}); |
|||
|
|||
G6.Util.processParallelEdges(edges, 12.5, 'custom-quadratic', 'custom-line'); |
|||
return { |
|||
maxDegree, |
|||
edges |
|||
}; |
|||
} |
|||
dispatchDetail(model, isNode) { |
|||
if (isNode) { |
|||
const params = { |
|||
eid: model.id, |
|||
domain: getQueryString('domain') |
|||
}; |
|||
store.dispatch('graphAside/NODE_DETAIL_ACTIONS', qs.stringify(params)); |
|||
} else { |
|||
const params = { |
|||
rids: model.id, |
|||
domain: getQueryString('domain') |
|||
}; |
|||
store.dispatch('graphAside/EDGE_DETAIL_ACTIONS', qs.stringify(params)); |
|||
} |
|||
} |
|||
graphDataActions(res) { |
|||
// 临时用来去重
|
|||
let newData = res.nodes.concat(); // 深拷贝
|
|||
const set = new Set(); |
|||
newData = newData.filter((item) => { |
|||
if (set.has(item.concept)) { |
|||
return false; |
|||
} else { |
|||
set.add(item.concept); |
|||
return true; |
|||
} |
|||
}); |
|||
const arr = newData.map((item) => { |
|||
return item.concept; |
|||
}); |
|||
const params2 = { |
|||
concepts: arr.join(), |
|||
graphId: getQueryString('graphId'), |
|||
domain: getQueryString('domain') |
|||
}; |
|||
store.dispatch('graphAside/SET_GRAPH_DATA_ACTIONS', qs.stringify(params2)); |
|||
} |
|||
// 更新右侧统计
|
|||
updateDataStatistics(data) { |
|||
store.commit('graphAside/SET_GRAPH_DATA', data || globalV.basicData); |
|||
this.graphDataActions(data || globalV.basicData); |
|||
} |
|||
processClusteredData(data, isNewGraph, graph) { |
|||
globalV.basicData = data; |
|||
globalV.clusteredData = labelPropagation(globalV.basicData, false, 'weight'); |
|||
const aggregatedData = { nodes: [], edges: [] }; // 聚合节点数据
|
|||
const realData = []; |
|||
globalV.clusteredData.clusters.forEach((cluster, i) => { |
|||
// 真实节点
|
|||
cluster.nodes.forEach((node) => { |
|||
node.level = 0; |
|||
node.type = ''; |
|||
node.colorSet = colorSets[i]; |
|||
globalV.nodeMap[node.id] = node; |
|||
realData.push(node); |
|||
}); |
|||
// 聚合节点
|
|||
const cnode = { |
|||
id: cluster.id, |
|||
type: 'aggregated-node', |
|||
count: cluster.nodes.length, |
|||
level: 1, |
|||
label: cluster.id, |
|||
colorSet: colorSets[i], |
|||
idx: i |
|||
}; |
|||
globalV.aggregatedNodeMap[cluster.id] = cnode; |
|||
aggregatedData.nodes.push(cnode); |
|||
}); |
|||
// 聚合节点的连线
|
|||
globalV.clusteredData.clusterEdges.forEach((clusterEdge) => { |
|||
const cedge = { |
|||
...clusterEdge, |
|||
size: Math.log(clusterEdge.count), |
|||
label: '', |
|||
id: `edge-${uniqueId()}` |
|||
}; |
|||
if (cedge.source === cedge.target) { |
|||
cedge.type = 'loop'; |
|||
cedge.loopCfg = { |
|||
dist: 20 |
|||
}; |
|||
} else cedge.type = 'line'; |
|||
aggregatedData.edges.push(cedge); |
|||
}); |
|||
// 真实节点的连线
|
|||
globalV.basicData.edges.forEach((edge) => { |
|||
edge.label = `${edge.relation || edge.label}`; |
|||
edge.id = `${edge.rid || edge.id}`; |
|||
edge.relation_id = `${edge.relation_id}`; |
|||
}); |
|||
globalV.currentUnproccessedData = { |
|||
nodes: realData, |
|||
edges: globalV.basicData.edges |
|||
}; |
|||
if (!isNewGraph) { |
|||
globalV.cachePositions = commonUtils.cacheNodePositions(graph.getNodes()); |
|||
} |
|||
const { edges: processedRealEdges } = this.processNodesEdges( |
|||
realData, |
|||
globalV.basicData.edges, |
|||
globalV.CANVAS_WIDTH, |
|||
globalV.CANVAS_HEIGHT, |
|||
globalV.largeGraphMode, |
|||
true, |
|||
isNewGraph |
|||
); |
|||
return { realData, processedRealEdges }; |
|||
} |
|||
// 子图布局
|
|||
subLayout(focusNodes, configObj, graph, type) { |
|||
const edges = globalV.basicData.edges; |
|||
const newNodes = []; |
|||
const newEdges = []; |
|||
const newNodeMap = new Map(); |
|||
// add the nodes which should be re-layout
|
|||
focusNodes.forEach((item, i) => { |
|||
const model = item.getModel(); |
|||
newNodes.push(model); |
|||
newNodeMap.set(model.id, i); |
|||
}); |
|||
// add related edges
|
|||
edges.forEach(function (edge) { |
|||
const sourceId = edge.source; |
|||
const targetId = edge.target; |
|||
if (newNodeMap.get(sourceId) !== undefined && newNodeMap.get(targetId) !== undefined) { |
|||
newEdges.push(edge); |
|||
} |
|||
}); |
|||
const subForceLayout = new G6.Layout[type]({ |
|||
...configObj, |
|||
center: [newNodes[0].x, newNodes[0].y], // 力导向和同心圆布局使用center
|
|||
begin: [newNodes[0].x, newNodes[0].y], // 层级和网格和横纵向使用begin
|
|||
onLayoutEnd: () => { |
|||
graph.refreshPositions(); |
|||
}, |
|||
tick: function tick() { |
|||
// the tick function to show the animation of layout process
|
|||
graph.refreshPositions(); |
|||
} |
|||
}); |
|||
subForceLayout.init({ |
|||
nodes: newNodes, |
|||
edges: newEdges |
|||
}); |
|||
subForceLayout.execute(); |
|||
} |
|||
}; |
|||
const commonUtils = new CommonUtils(); |
|||
export default commonUtils; |
@ -0,0 +1,66 @@ |
|||
// 节点扩展
|
|||
export const nodeExtendData = { |
|||
title: '实体扩展', |
|||
data: [{ |
|||
label: '实体类型', |
|||
inputType: 'select', |
|||
placeholder: '请选择实体类型', |
|||
field: 'conceptId', |
|||
multiple: true, |
|||
filterable: true |
|||
}], |
|||
formData: { |
|||
conceptId: [] |
|||
}, |
|||
rules: { |
|||
conceptId: { required: true, message: '请选择实体类型', trigger: 'change' } |
|||
} |
|||
} |
|||
// 边扩展
|
|||
export const edgeExtendData = { |
|||
title: '关系扩展', |
|||
data: [{ |
|||
label: '关系定义', |
|||
inputType: 'select', |
|||
placeholder: '请选择关系定义', |
|||
field: 'relationId', |
|||
multiple: true, |
|||
filterable: true |
|||
}], |
|||
formData: { |
|||
relationId: [] |
|||
}, |
|||
rules: { |
|||
relationId: { required: true, message: '请选择关系定义', trigger: 'change' } |
|||
} |
|||
} |
|||
// 最短路径探寻
|
|||
export const customPathData = { |
|||
width: '50%', |
|||
title: '自定义路径探寻', |
|||
data: [{ |
|||
label: '路径度数', |
|||
inputType: 'select', |
|||
placeholder: '请选择', |
|||
field: 'parthDegree', |
|||
options: [2, 3, 4, 5, 6], |
|||
width: 'width50' |
|||
}, { |
|||
label: '关系定义', |
|||
inputType: 'tree', |
|||
placeholder: '请选择', |
|||
field: 'relType', |
|||
multiple: true, |
|||
filterable: true, |
|||
options: [], |
|||
width: 'width90' |
|||
}], |
|||
formData: { |
|||
parthDegree: '', |
|||
relType: [] |
|||
}, |
|||
rules: { |
|||
parthDegree: { required: true, message: '请选择路径度数', trigger: 'change' } |
|||
// relType: { required: true, message: '请选择关系类型', trigger: 'change' }
|
|||
} |
|||
} |
@ -0,0 +1,61 @@ |
|||
/** |
|||
* 鼠标拖拽指令 |
|||
* 1.v-drag 该方式将移动作用在指令绑定的dom元素上 |
|||
* 2.v-drag="{dom: '需要移动的dom元素的id'}" 该方式将移动所传参数指定的dom元素 |
|||
*/ |
|||
export const drag = { |
|||
// 当被绑定的元素插入到 DOM 中时……
|
|||
inserted(el, binding) { |
|||
const customDom = binding.value ? binding.value.dom : null |
|||
let customDomEl |
|||
if (customDom) customDomEl = document.getElementById(customDom) |
|||
const gap = 10 // 缝隙:小于缝隙时,则吸附
|
|||
const parent = customDom ? document.querySelector('#graph-layout') : el.offsetParent |
|||
const container = document.querySelector('#graph-layout') |
|||
el.onmousedown = function(event) { |
|||
const eleEvent = event || window.event // event的兼容,同时得到clientX,clientY的值
|
|||
const x = customDom ? eleEvent.clientX - customDomEl.offsetLeft : eleEvent.clientX - el.offsetLeft |
|||
const y = customDom ? eleEvent.clientY - customDomEl.offsetTop : eleEvent.clientY - el.offsetTop |
|||
let left = 0 |
|||
let top = 0 |
|||
const elParentW = customDom ? customDomEl.offsetWidth : el.offsetWidth |
|||
const elParentH = customDom ? customDomEl.offsetHeight : el.offsetHeight |
|||
container.onmousemove = function(eve) { |
|||
left = eve.clientX - x |
|||
top = eve.clientY - y |
|||
// 左
|
|||
if (left <= gap) { |
|||
left = 0 |
|||
} |
|||
// 右
|
|||
if (left >= parent.offsetWidth - elParentW - gap) { // 大于整个盒子的宽度-小盒子的宽度
|
|||
left = parent.offsetWidth - elParentW |
|||
} |
|||
// 上
|
|||
if (top <= gap) { |
|||
top = 0 |
|||
} |
|||
// 下
|
|||
if (top >= parent.offsetHeight - elParentH - gap) { |
|||
top = parent.offsetHeight - elParentH |
|||
} |
|||
if (customDom) { |
|||
customDomEl.style.left = left + 'px' |
|||
customDomEl.style.top = top + 'px' |
|||
} else { |
|||
el.style.left = left + 'px' |
|||
el.style.top = top + 'px' |
|||
} |
|||
} |
|||
container.onmouseup = function(e) { |
|||
container.onmousemove = null |
|||
container.onmouseup = null |
|||
} |
|||
document.onmouseup = function() { |
|||
container.onmousemove = null |
|||
container.onmouseup = null |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
@ -0,0 +1,24 @@ |
|||
import Vue from 'vue' |
|||
let loadingInstance |
|||
/** |
|||
* element的loading组件(开启loading) |
|||
* @param {target: 作用的dom元素} opt |
|||
*/ |
|||
export const openLoading = (opt) => { |
|||
const { target } = opt |
|||
loadingInstance = Vue.prototype.$loading({ |
|||
lock: true, |
|||
target, |
|||
text: '拼命加载中', |
|||
spinner: 'el-icon-loading', |
|||
background: 'rgba(0, 0, 0, 0.8)' |
|||
}) |
|||
} |
|||
/** |
|||
* element的loading组件(关闭loading) |
|||
*/ |
|||
export const closeLoading = () => { |
|||
if (loadingInstance) { |
|||
loadingInstance.close() |
|||
} |
|||
} |
@ -0,0 +1,90 @@ |
|||
class GlobalGraphData { |
|||
constructor() { |
|||
this.NODESIZEMAPPING = 'degree' // 节点大小匹配条件
|
|||
this.labelMaxLength = 10 // label文本显示的最大长度
|
|||
this.DEFAULTNODESIZE = 30 // 真实节点的默认大小
|
|||
this.DEFAULTAGGREGATEDNODESIZE = 53 // 聚合节点的默认大小
|
|||
this.NODE_LIMIT = 40 // TODO: find a proper number for maximum node number on the canvas(画布上显示真实节点的最大数量限制)
|
|||
// 默认节点的样式
|
|||
this.defaultShapeStyle = { |
|||
border: 'solid', |
|||
bordercolor: '#5F95FF', |
|||
shape: 'circle', |
|||
shapecolor: '#2B384E' |
|||
} |
|||
this.duration = 2000 |
|||
this.animateOpacity = 1 // 连线focus状态下线的透明度
|
|||
this.virtualEdgeOpacity = 1 // 虚拟边的透明度
|
|||
this.realEdgeOpacity = 1 // 真实边的透明度
|
|||
this.darkBackColor = 'rgb(43, 47, 51)' |
|||
this.disableColor = '#777' |
|||
this.theme = 'dark' |
|||
this.subjectColors = [ |
|||
'#5F95FF', // blue
|
|||
'#61DDAA', |
|||
'#65789B', |
|||
'#F6BD16', |
|||
'#7262FD', |
|||
'#78D3F8', |
|||
'#9661BC', |
|||
'#F6903D', |
|||
'#008685', |
|||
'#F08BB4' |
|||
] |
|||
// 图元素的全局样式
|
|||
this.global = { |
|||
node: { |
|||
style: { |
|||
fill: '#2B384E' |
|||
}, |
|||
labelCfg: { |
|||
style: { |
|||
fill: '#000'// label颜色
|
|||
} |
|||
}, |
|||
stateStyles: { |
|||
focus: { |
|||
fill: '#2B384E' |
|||
} |
|||
} |
|||
}, |
|||
edge: { |
|||
style: { |
|||
stroke: '#8A909A', // 聚合节点的线的颜色
|
|||
realEdgeStroke: '#8A909A', // 真实节点的线的颜色
|
|||
realEdgeOpacity: this.realEdgeOpacity, |
|||
strokeOpacity: this.realEdgeOpacity |
|||
}, |
|||
labelCfg: { |
|||
style: { |
|||
fill: '#8A909A' |
|||
} |
|||
}, |
|||
stateStyles: { |
|||
focus: { |
|||
stroke: '#fff' |
|||
} |
|||
} |
|||
} |
|||
} |
|||
this.largeGraphMode = false // 边上文字的显隐,true:默认不显示;false:默认显示;
|
|||
this.aggregatedNodeMap = {} |
|||
this.clusteredData = {} |
|||
this.currentUnproccessedData = { nodes: [], edges: [] } |
|||
this.cachePositions = {} |
|||
this.CANVAS_WIDTH = 800 // canvas的宽度
|
|||
this.CANVAS_HEIGHT = 800 // canvas的高度
|
|||
this.zoom = 1 // canvas缩放比例
|
|||
this.layout = { |
|||
type: '', |
|||
instance: null, |
|||
destroyed: true |
|||
} |
|||
this.hiddenItemIds = [] // 隐藏的元素 id 数组
|
|||
this.nodeMap = {} |
|||
this.manipulatePosition = null |
|||
this.basicData = [] // 接口返回的原始数据(该变量会根据操作变化)
|
|||
} |
|||
} |
|||
|
|||
export const globalV = new GlobalGraphData() |
@ -0,0 +1,163 @@ |
|||
import request from './request' |
|||
|
|||
// 关系图接口
|
|||
export function getGraphData(data) { |
|||
return request({ |
|||
url: '/graphApi/anlysis/analytic', |
|||
method: 'post', |
|||
data |
|||
}) |
|||
} |
|||
|
|||
// 获取中心节点
|
|||
export function getCenterGraphData(params) { |
|||
return request({ |
|||
url: '/graphApi/anlysis/getNodeDatasByGraphId', |
|||
method: 'get', |
|||
params |
|||
}) |
|||
} |
|||
|
|||
// 保存中心节点
|
|||
export function graphCentraNodeSave(params) { |
|||
return request({ |
|||
url: '/graphApi/anlysis/graphCentraNodeSave', |
|||
method: 'get', |
|||
params |
|||
}) |
|||
} |
|||
|
|||
// 根据关键词搜索实体
|
|||
export function searchEntityByKeyword(params) { |
|||
return request({ |
|||
url: '/graphApi/anlysis/ir', |
|||
method: 'get', |
|||
params |
|||
}) |
|||
} |
|||
|
|||
// 节点扩展
|
|||
export function getOntConceptList(params) { |
|||
return request({ |
|||
url: '/graphApi/anlysis/getOntConceptList', |
|||
method: 'get', |
|||
params |
|||
}) |
|||
} |
|||
|
|||
// 边扩展
|
|||
export function getOntRelationList(params) { |
|||
return request({ |
|||
url: '/graphApi/anlysis/getOntRelationList', |
|||
method: 'get', |
|||
params |
|||
}) |
|||
} |
|||
|
|||
// 根据节点的节点类型获取一度关联数据
|
|||
export function expandByNodeType(params) { |
|||
return request({ |
|||
url: '/graphApi/anlysis/expandByNodeType', |
|||
method: 'get', |
|||
params |
|||
}) |
|||
} |
|||
|
|||
// 根据节点的边类型获取一度关联数据
|
|||
export function expandByEdgeType(params) { |
|||
return request({ |
|||
url: '/graphApi/anlysis/expandByEdgeType', |
|||
method: 'get', |
|||
params |
|||
}) |
|||
} |
|||
|
|||
// 路径探寻
|
|||
export function pathToExplore(data) { |
|||
return request({ |
|||
url: '/graphApi/anlysis/pathToExplore', |
|||
method: 'post', |
|||
data |
|||
}) |
|||
} |
|||
|
|||
// 自定义路径探寻中的关系类型列表
|
|||
export function categoryRelTree(data) { |
|||
return request({ |
|||
url: '/graphApi/anlysis/categoryRelTree', |
|||
method: 'post', |
|||
data |
|||
}) |
|||
} |
|||
|
|||
// 自定义路径探寻中的关系父级类型列表
|
|||
export function getRelationType(data) { |
|||
return request({ |
|||
url: '/graphApi/anlysis/getRelationType', |
|||
method: 'post', |
|||
data |
|||
}) |
|||
} |
|||
|
|||
// 自定义路径探寻中的关系子级类型列表
|
|||
export function getOntRelationQuery(data) { |
|||
return request({ |
|||
url: '/graphApi/anlysis/getOntRelationQuery', |
|||
method: 'post', |
|||
data |
|||
}) |
|||
} |
|||
|
|||
// 时序分析
|
|||
export function timingAnalysis(data) { |
|||
return request({ |
|||
url: '/graphApi/anlysis/timingAnalysis', |
|||
method: 'post', |
|||
data |
|||
}) |
|||
} |
|||
|
|||
// 删除节点
|
|||
export function deleteEntityByGraphId(data) { |
|||
return request({ |
|||
url: '/graphApi/anlysis/deleteEntityByGraphId', |
|||
method: 'post', |
|||
data |
|||
}) |
|||
} |
|||
|
|||
// 删除关系
|
|||
export function deleteEntityRelation(data) { |
|||
return request({ |
|||
url: '/graphApi/project/deleteEntityRelation', |
|||
method: 'post', |
|||
data |
|||
}) |
|||
} |
|||
|
|||
// 概念id和name的映射接口
|
|||
export function conceptAnalytic(data) { |
|||
return request({ |
|||
url: '/graphApi/anlysis/conceptAnalytic', |
|||
method: 'post', |
|||
data |
|||
}) |
|||
} |
|||
|
|||
// 获取单个节点详情的接口
|
|||
export function entityInfo(data) { |
|||
return request({ |
|||
url: '/graphApi/anlysis/entityInfo', |
|||
method: 'post', |
|||
data |
|||
}) |
|||
} |
|||
|
|||
// 获取单个关系详情的接口
|
|||
export function entity_relation_byrids(data) { |
|||
return request({ |
|||
url: '/graphApi/anlysis/entityRelationByrids', |
|||
method: 'post', |
|||
data |
|||
}) |
|||
} |
@ -0,0 +1,639 @@ |
|||
import { Message } from 'winbox-ui'; |
|||
import qs from 'qs'; |
|||
import { globalV } from './globalData'; |
|||
import { getGraphData, expandByNodeType, expandByEdgeType } from './graph'; |
|||
import commonUtils from './common'; |
|||
import store from '@/store'; |
|||
import { EventBus } from '../eventBus'; |
|||
import { getQueryString } from './publicFunction'; |
|||
import { openLoading, closeLoading } from './elementPlugin'; |
|||
import { deleteEntityByGraphId, deleteEntityRelation } from './graph'; |
|||
import G6 from '@antv/g6'; |
|||
const { labelPropagation } = G6.Algorithm; // 图算法
|
|||
const colorSets = G6.Util.getColorSetsBySubjectColors(globalV.subjectColors, globalV.darkBackColor, globalV.theme, globalV.disableColor); |
|||
const { uniqueId } = G6.Util; |
|||
|
|||
class MenuHandle { |
|||
async getNeighborMixedGraph( |
|||
centerNodeModel, |
|||
step, |
|||
originData, |
|||
clusteredData, |
|||
currentData, |
|||
nodeMap, |
|||
aggregatedNodeMap, |
|||
maxNeighborNumPerNode = 5, |
|||
expandType, |
|||
formData |
|||
) { |
|||
try { |
|||
// update the manipulate position for center gravity of the new nodes
|
|||
globalV.manipulatePosition = { x: centerNodeModel.x, y: centerNodeModel.y }; |
|||
|
|||
// the neighborSubGraph does not include the centerNodeModel. the elements are all generated new nodes and edges
|
|||
// const neighborSubGraph = generateNeighbors(centerNodeModel, step, maxNeighborNumPerNode);
|
|||
const neighborSubGraph = { |
|||
nodes: [], |
|||
edges: [] |
|||
}; |
|||
const tempNeighborSubGraph = await this.generateNeighbors(centerNodeModel, step, maxNeighborNumPerNode, expandType, formData); |
|||
setTimeout(() => { |
|||
EventBus.$emit('timeaxis', true); |
|||
}, 200); |
|||
tempNeighborSubGraph.nodes.forEach((item) => { |
|||
const nodeArr = originData.nodes.filter((oriItem) => { |
|||
return oriItem.id === item.id; |
|||
}); |
|||
if (!nodeArr.length) neighborSubGraph.nodes.push(item); |
|||
}); |
|||
tempNeighborSubGraph.edges.forEach((item) => { |
|||
const edgeArr = originData.edges.filter((oriItem) => { |
|||
return item.source === oriItem.source && item.target === oriItem.target; |
|||
}); |
|||
if (!edgeArr.length) neighborSubGraph.edges.push(item); |
|||
}); |
|||
// update the origin data
|
|||
originData.nodes = originData.nodes.concat(neighborSubGraph.nodes); |
|||
originData.edges = originData.edges.concat(neighborSubGraph.edges); |
|||
// update the origin nodeMap
|
|||
neighborSubGraph.nodes.forEach((node) => { |
|||
nodeMap[node.id] = node; |
|||
}); |
|||
// 扩展如果没有新的数据,提示无数据
|
|||
if (currentData.nodes.length === globalV.basicData.nodes.length && currentData.edges.length === globalV.basicData.edges.length) { |
|||
// 将扩展新增的节点和边存储起来,用于扩展后高亮这些节点和边
|
|||
store.commit('graphMenu/SET_EXTENDDATA', tempNeighborSubGraph); |
|||
Message({ |
|||
message: '当前节点没有可扩展的数据!', |
|||
type: 'warning', |
|||
duration: 5000 |
|||
}); |
|||
return currentData; |
|||
} else { |
|||
// 将扩展新增的节点和边存储起来,用于扩展后高亮这些节点和边
|
|||
store.commit('graphMenu/SET_EXTENDDATA', neighborSubGraph); |
|||
} |
|||
// update the clusteredData
|
|||
const clusterId = centerNodeModel.clusterId; |
|||
clusteredData.clusters.forEach((cluster) => { |
|||
if (cluster.id !== clusterId) return; |
|||
cluster.nodes = cluster.nodes.concat(neighborSubGraph.nodes); |
|||
cluster.sumTot += neighborSubGraph.edges.length; |
|||
}); |
|||
// update the count
|
|||
aggregatedNodeMap[clusterId].count += neighborSubGraph.nodes.length; |
|||
|
|||
currentData.nodes = currentData.nodes.concat(neighborSubGraph.nodes); |
|||
currentData.edges = currentData.edges.concat(neighborSubGraph.edges); |
|||
return currentData; |
|||
} catch (error) { |
|||
console.log(error); |
|||
} |
|||
} |
|||
generateNeighbors(centerNodeModel, step, maxNeighborNumPerNode = 5, expandType, formData) { |
|||
if (step <= 0) return undefined; |
|||
const nodes = []; |
|||
const edges = []; |
|||
const clusterId = centerNodeModel.clusterId; |
|||
const centerId = centerNodeModel.id; |
|||
|
|||
return new Promise((resolve, reject) => { |
|||
openLoading({ target: '.chart-explore-container' }); |
|||
if (expandType === 'node') { |
|||
const params = { |
|||
graphId: getQueryString('graphId'), |
|||
domain: getQueryString('domain'), |
|||
conceptId: formData.conceptId ? formData.conceptId.join() : '', |
|||
maxdist: 1, |
|||
eid: centerId, |
|||
force: false, |
|||
direction: formData.direction ? formData.direction : '', |
|||
flag: formData.flag |
|||
}; |
|||
if (!params.flag) { |
|||
delete params.flag; |
|||
} |
|||
expandByNodeType(params).then((res) => { |
|||
const obj = this.processNeighbors(res, clusterId, centerNodeModel, nodes, edges, centerId); |
|||
resolve(obj); |
|||
}); |
|||
} else if (expandType === 'edge') { |
|||
const params = { |
|||
graphId: getQueryString('graphId'), |
|||
domain: getQueryString('domain'), |
|||
relationId: formData.relationId.join(), |
|||
maxdist: 1, |
|||
eid: centerId, |
|||
force: false |
|||
}; |
|||
expandByEdgeType(params).then((res) => { |
|||
const obj = this.processNeighbors(res, clusterId, centerNodeModel, nodes, edges, centerId); |
|||
resolve(obj); |
|||
}); |
|||
} else { |
|||
const params = { |
|||
graphId: getQueryString('graphId'), |
|||
domain: getQueryString('domain'), |
|||
eid: centerId, |
|||
maxdist: 1, |
|||
force: false |
|||
}; |
|||
getGraphData(qs.stringify(params)).then((data) => { |
|||
const obj = this.processNeighbors(data, clusterId, centerNodeModel, nodes, edges, centerId); |
|||
// resolve({ nodes, edges })
|
|||
resolve(obj); |
|||
}); |
|||
} |
|||
}); |
|||
} |
|||
processNeighbors(data, clusterId, centerNodeModel, nodes, edges, centerId) { |
|||
closeLoading(); |
|||
let g6Data = { |
|||
nodes: [], |
|||
edges: [] |
|||
}; |
|||
if (JSON.stringify(data) !== '{}') g6Data = data.d3data; |
|||
g6Data.nodes.forEach((item) => { |
|||
const neighborNode = { |
|||
id: item.id, |
|||
clusterId, |
|||
level: 0, |
|||
name: item.name, |
|||
colorSet: centerNodeModel.colorSet, |
|||
concept: item.concept |
|||
}; |
|||
nodes.push(neighborNode); |
|||
}); |
|||
g6Data.edges.forEach((item) => { |
|||
const source = g6Data.nodes[item.source].id === centerId ? centerId : g6Data.nodes[item.source].id; |
|||
const target = g6Data.nodes[item.target].id === centerId ? centerId : g6Data.nodes[item.target].id; |
|||
const neighborEdge = { |
|||
id: item.rid, |
|||
source, |
|||
target, |
|||
label: `${item.relation}`, |
|||
relation_id: item.relation_id, |
|||
relation: item.relation, |
|||
rid: item.rid, |
|||
rids: item.rids, |
|||
sourceNum: item.source, |
|||
targetNum: item.target |
|||
}; |
|||
edges.push(neighborEdge); |
|||
}); |
|||
for (const key in g6Data.concepts) { |
|||
if (globalV.basicData.concepts[key] === undefined) { |
|||
globalV.basicData.concepts[key] = g6Data.concepts[key]; |
|||
} |
|||
} |
|||
return { nodes, edges }; |
|||
} |
|||
examAncestors(model, expandedArray, length, keepTags) { |
|||
for (let i = 0; i < length; i++) { |
|||
const expandedNode = expandedArray[i]; |
|||
if (!keepTags[i] && model.parentId === expandedNode.id) { |
|||
keepTags[i] = true; // 需要被保留
|
|||
this.examAncestors(expandedNode, expandedArray, length, keepTags); |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
manageExpandCollapseArray(nodeNumber, model, collapseArray, expandArray, graph) { |
|||
globalV.manipulatePosition = { x: model.x, y: model.y }; |
|||
|
|||
// 维护 expandArray,若当前画布节点数高于上限,移出 expandedArray 中非 model 祖先的节点)
|
|||
if (nodeNumber > globalV.NODE_LIMIT) { |
|||
// 若 keepTags[i] 为 true,则 expandedArray 的第 i 个节点需要被保留
|
|||
const keepTags = {}; |
|||
const expandLen = expandArray.length; |
|||
// 检查 X 的所有祖先并标记 keepTags
|
|||
this.examAncestors(model, expandArray, expandLen, keepTags); |
|||
// 寻找 expandedArray 中第一个 keepTags 不为 true 的点
|
|||
let shiftNodeIdx = -1; |
|||
for (let i = 0; i < expandLen; i++) { |
|||
if (!keepTags[i]) { |
|||
shiftNodeIdx = i; |
|||
break; |
|||
} |
|||
} |
|||
// 如果有符合条件的节点,将其从 expandedArray 中移除
|
|||
if (shiftNodeIdx !== -1) { |
|||
let foundNode = expandArray[shiftNodeIdx]; |
|||
if (foundNode.level === 2) { |
|||
let foundLevel1 = false; |
|||
// 找到 expandedArray 中 parentId = foundNode.id 且 level = 1 的第一个节点
|
|||
for (let i = 0; i < expandLen; i++) { |
|||
const eNode = expandArray[i]; |
|||
if (eNode.parentId === foundNode.id && eNode.level === 1) { |
|||
foundLevel1 = true; |
|||
foundNode = eNode; |
|||
expandArray.splice(i, 1); |
|||
break; |
|||
} |
|||
} |
|||
// 若未找到,则 foundNode 不变, 直接删去 foundNode
|
|||
if (!foundLevel1) expandArray.splice(shiftNodeIdx, 1); |
|||
} else { |
|||
// 直接删去 foundNode
|
|||
expandArray.splice(shiftNodeIdx, 1); |
|||
} |
|||
// const removedNode = expandedArray.splice(shiftNodeIdx, 1); // splice returns an array
|
|||
const idSplits = foundNode.id.split('-'); |
|||
let collapseNodeId; |
|||
// 去掉最后一个后缀
|
|||
for (let i = 0; i < idSplits.length - 1; i++) { |
|||
const str = idSplits[i]; |
|||
if (collapseNodeId) collapseNodeId = `${collapseNodeId}-${str}`; |
|||
else collapseNodeId = str; |
|||
} |
|||
const collapseNode = { |
|||
id: collapseNodeId, |
|||
parentId: foundNode.id, |
|||
level: foundNode.level - 1 |
|||
}; |
|||
collapseArray.push(collapseNode); |
|||
} |
|||
} |
|||
|
|||
const currentNode = { |
|||
id: model.id, |
|||
level: model.level, |
|||
parentId: model.parentId |
|||
}; |
|||
|
|||
// 加入当前需要展开的节点
|
|||
expandArray.push(currentNode); |
|||
graph.get('canvas').setCursor('default'); |
|||
return { expandArray, collapseArray }; |
|||
} |
|||
// 反选
|
|||
selectInvert(graph, model) { |
|||
graph.getNodes().forEach((node) => { |
|||
const nodeModel = node.getModel(); |
|||
if (!node.hasState('focus') && nodeModel.id !== model.id) { |
|||
graph.setItemState(node, 'focus', true); |
|||
} else { |
|||
graph.setItemState(node, 'focus', false); |
|||
} |
|||
}); |
|||
commonUtils.setFocusNodes(graph); |
|||
} |
|||
// 反选删除(支持单个节点和多节点)
|
|||
selectInvertDelete(graph, item, model) { |
|||
const nodes = []; |
|||
graph.getNodes().forEach((node) => { |
|||
const nodeModel = node.getModel(); |
|||
if ((nodeModel.id !== model.id && !item.hasState('focus')) || (item.hasState('focus') && !node.hasState('focus'))) { |
|||
nodes.push(node); |
|||
} |
|||
}); |
|||
this.deleteNodes(nodes, graph); |
|||
} |
|||
// 选择一度
|
|||
selectOneDegree(graph, item) { |
|||
const itemHaveFocus = item.hasState('focus'); |
|||
commonUtils.clearFocusItemState(graph); |
|||
const data = { |
|||
nodes: [], |
|||
edges: [] |
|||
}; |
|||
item.getEdges().forEach((edge) => { |
|||
data.edges.push(edge.getModel()); |
|||
}); |
|||
var neighborNodes; |
|||
|
|||
if (itemHaveFocus) { |
|||
// 选中状态
|
|||
neighborNodes = item.getNeighbors().concat(item); |
|||
} else { |
|||
neighborNodes = item.getNeighbors(); |
|||
} |
|||
neighborNodes.forEach((node) => { |
|||
graph.setItemState(node, 'focus', true); |
|||
data.nodes.push(node.getModel()); |
|||
}); |
|||
// store.commit('aside/SET_GRAPH_DATA', data)
|
|||
commonUtils.setFocusNodes(graph); |
|||
} |
|||
// 全选节点
|
|||
selectAll(graph) { |
|||
graph.getNodes().forEach((node) => { |
|||
if (!node.hasState('focus')) { |
|||
graph.setItemState(node, 'focus', true); |
|||
} |
|||
}); |
|||
commonUtils.setFocusNodes(graph); |
|||
} |
|||
// 高亮节点
|
|||
highlightSomeNodes(graph, ids) { |
|||
graph.getNodes().forEach((node) => { |
|||
const nodeModel = node.getModel(); |
|||
if (ids.indexOf(nodeModel.id) > -1) { |
|||
graph.setItemState(node, 'focus', true); |
|||
} else { |
|||
graph.setItemState(node, 'focus', false); |
|||
} |
|||
}); |
|||
commonUtils.setFocusNodes(graph); |
|||
} |
|||
// 高亮边
|
|||
highlightSomeEdges(graph, ids) { |
|||
graph.getEdges().forEach((edge) => { |
|||
const edgeModel = edge.getModel(); |
|||
if (ids.indexOf(edgeModel.id) > -1) { |
|||
graph.setItemState(edge, 'focus', true); |
|||
} else { |
|||
graph.setItemState(edge, 'focus', false); |
|||
} |
|||
}); |
|||
} |
|||
// 清除所有节点和边的状态
|
|||
clearNodesEdgesState(graph) { |
|||
graph.getNodes().forEach((node) => { |
|||
graph.clearItemStates(node); |
|||
}); |
|||
graph.getEdges().forEach((edge) => { |
|||
graph.clearItemStates(edge); |
|||
}); |
|||
} |
|||
|
|||
// 清除多个节点和边的状态
|
|||
clearMultipleNodesEdgesState(graph, nodes) { |
|||
(nodes || []).forEach((node) => { |
|||
const nodeModel = graph.findById(node.id); |
|||
graph.setItemState(nodeModel, 'focus', false); |
|||
const relatedEdges = nodeModel.getEdges(); |
|||
relatedEdges.forEach((edge) => { |
|||
graph.setItemState(edge, 'focus', false); |
|||
}); |
|||
}); |
|||
} |
|||
// 清除多个边状态
|
|||
clearMultipleEdgesState(graph, edges) { |
|||
(edges || []).forEach((edge) => { |
|||
const edgeModel = graph.findById(edge.id); |
|||
graph.setItemState(edgeModel, 'focus', false); |
|||
}); |
|||
} |
|||
// 删除关系
|
|||
deleteEdges(graph, data) { |
|||
openLoading({ target: '.chart-explore-container' }); |
|||
deleteEntityRelation(data).then((res) => { |
|||
closeLoading(); |
|||
if (res && res.status == 'SUCCESS') { |
|||
EventBus.$emit('deleteRelation', false); |
|||
store.commit('graphAside/SET_RELATION_SHIP', ''); |
|||
Message({ |
|||
message: '删除成功!', |
|||
type: 'success', |
|||
offset: 100, |
|||
duration: 2000 |
|||
}); |
|||
globalV.basicData.edges.forEach((edge, index) => { |
|||
if (data.id == edge.id) { |
|||
globalV.basicData.edges.splice(index, 1); |
|||
} |
|||
}); |
|||
globalV.currentUnproccessedData.edges.forEach((edge, index) => { |
|||
if (data.id == edge.id) { |
|||
globalV.currentUnproccessedData.edges.splice(index, 1); |
|||
} |
|||
}); |
|||
const edgeModel = graph.findById(data.id); |
|||
graph.removeItem(edgeModel); |
|||
globalV.clusteredData = labelPropagation(globalV.basicData, false, 'weight'); |
|||
globalV.clusteredData.clusters.forEach((cluster, i) => { |
|||
// 聚合节点
|
|||
const cnode = { |
|||
id: cluster.id, |
|||
type: 'aggregated-node', |
|||
count: cluster.nodes.length, |
|||
level: 1, |
|||
label: cluster.id, |
|||
colorSet: colorSets[i], |
|||
idx: i |
|||
}; |
|||
globalV.aggregatedNodeMap[cluster.id] = cnode; |
|||
}); |
|||
// 聚合节点的连线
|
|||
globalV.clusteredData.clusterEdges.forEach((clusterEdge) => { |
|||
const cedge = { |
|||
...clusterEdge, |
|||
size: Math.log(clusterEdge.count), |
|||
label: '', |
|||
id: `edge-${uniqueId()}` |
|||
}; |
|||
if (cedge.source === cedge.target) { |
|||
cedge.type = 'loop'; |
|||
cedge.loopCfg = { |
|||
dist: 20 |
|||
}; |
|||
} else cedge.type = 'line'; |
|||
}); |
|||
commonUtils.setFocusNodes(graph); |
|||
commonUtils.updateDataStatistics(); |
|||
} else { |
|||
EventBus.$emit('deleteRelation', false); |
|||
Message({ |
|||
message: '删除失败!', |
|||
type: 'error' |
|||
}); |
|||
} |
|||
}); |
|||
} |
|||
// 删除节点
|
|||
deleteNodes(nodes, graph) { |
|||
openLoading({ target: '.chart-explore-container' }); |
|||
const delIds = nodes.map((item) => { |
|||
const model = item.getModel(); |
|||
return model.id; |
|||
}); |
|||
const delEdges = []; |
|||
deleteEntityByGraphId({ eids: delIds, graphId: getQueryString('graphId'), projectId: getQueryString('projectId') }).then((res) => { |
|||
closeLoading(); |
|||
if (!res) { |
|||
Message({ |
|||
message: '删除成功!', |
|||
type: 'success', |
|||
offset: 100, |
|||
duration: 2000 |
|||
}); |
|||
nodes.forEach((node) => { |
|||
const edges = node.getEdges(); |
|||
const nodeModel = node.getModel(); |
|||
if (globalV.nodeMap[nodeModel.id]) { |
|||
delete globalV.nodeMap[nodeModel.id]; |
|||
} |
|||
edges.forEach((edge) => { |
|||
const edgeModel = edge.getModel(); |
|||
delEdges.push(edgeModel.id); |
|||
}); |
|||
graph.removeItem(node); |
|||
}); |
|||
globalV.basicData.nodes = globalV.basicData.nodes.filter((node) => { |
|||
return node && delIds.indexOf(node.id) === -1; |
|||
}); |
|||
globalV.basicData.edges = globalV.basicData.edges.filter((edge) => { |
|||
return edge && delEdges.indexOf(edge.id) === -1; |
|||
}); |
|||
globalV.currentUnproccessedData.nodes = globalV.currentUnproccessedData.nodes.filter((node) => { |
|||
return node && delIds.indexOf(node.id) === -1; |
|||
}); |
|||
globalV.currentUnproccessedData.edges = globalV.currentUnproccessedData.edges.filter((edge) => { |
|||
return edge && delEdges.indexOf(edge.id) === -1; |
|||
}); |
|||
globalV.clusteredData = labelPropagation(globalV.basicData, false, 'weight'); |
|||
globalV.clusteredData.clusters.forEach((cluster, i) => { |
|||
// 聚合节点
|
|||
const cnode = { |
|||
id: cluster.id, |
|||
type: 'aggregated-node', |
|||
count: cluster.nodes.length, |
|||
level: 1, |
|||
label: cluster.id, |
|||
colorSet: colorSets[i], |
|||
idx: i |
|||
}; |
|||
globalV.aggregatedNodeMap[cluster.id] = cnode; |
|||
}); |
|||
// 聚合节点的连线
|
|||
globalV.clusteredData.clusterEdges.forEach((clusterEdge) => { |
|||
const cedge = { |
|||
...clusterEdge, |
|||
size: Math.log(clusterEdge.count), |
|||
label: '', |
|||
id: `edge-${uniqueId()}` |
|||
}; |
|||
if (cedge.source === cedge.target) { |
|||
cedge.type = 'loop'; |
|||
cedge.loopCfg = { |
|||
dist: 20 |
|||
}; |
|||
} else cedge.type = 'line'; |
|||
}); |
|||
commonUtils.setFocusNodes(graph); |
|||
commonUtils.updateDataStatistics(); |
|||
} else { |
|||
Message({ |
|||
message: '删除失败!', |
|||
type: 'error' |
|||
}); |
|||
} |
|||
}); |
|||
} |
|||
// 删除选中的节点
|
|||
deleteFocusNodes(graph) { |
|||
const nodes = graph.findAllByState('node', 'focus'); |
|||
if (!nodes.length) { |
|||
Message({ |
|||
message: '当前没有选中的节点!', |
|||
type: 'warning', |
|||
duration: 2000 |
|||
}); |
|||
return false; |
|||
} |
|||
this.deleteNodes(nodes, graph); |
|||
} |
|||
// 节点隐藏
|
|||
hideNodes(graph, nodes) { |
|||
(nodes || []).forEach((node) => { |
|||
graph.hideItem(node.id); |
|||
graph.clearItemStates(node.id); |
|||
}); |
|||
} |
|||
// 边隐藏
|
|||
hideEdges(graph, nodes) { |
|||
(nodes || []).forEach((node) => { |
|||
const nodeModel = graph.findById(node.id); |
|||
nodeModel.hide(nodeModel); |
|||
}); |
|||
} |
|||
// 边、节点取消高亮
|
|||
cancleHighLightEdgesNodes(graph, nodes) { |
|||
(nodes || []).forEach((node) => { |
|||
const nodeModel = graph.findById(node.id); |
|||
graph.setItemState(nodeModel, 'focus', false); |
|||
}); |
|||
} |
|||
// 节点显示
|
|||
showFocusNodesAndShowEdges(graph, nodes) { |
|||
(nodes || []).forEach((node) => { |
|||
const nodeModel = graph.findById(node.id); |
|||
graph.showItem(node.id); |
|||
graph.setItemState(nodeModel, 'focus', true); |
|||
}); |
|||
} |
|||
// 节点显示
|
|||
showNodes(graph, nodes) { |
|||
(nodes || []).forEach((node) => { |
|||
const nodeModel = graph.findById(node.id); |
|||
graph.showItem(node.id); |
|||
graph.setItemState(nodeModel, 'focus', true); |
|||
const relatedEdges = nodeModel.getEdges(); |
|||
relatedEdges.forEach((edge) => { |
|||
graph.setItemState(edge, 'focus', true); |
|||
}); |
|||
}); |
|||
} |
|||
// 边显示
|
|||
showEdges(graph, nodes) { |
|||
(nodes || []).forEach((node) => { |
|||
const nodeModel = graph.findById(node.id); |
|||
nodeModel.show(nodeModel); |
|||
graph.setItemState(nodeModel, 'focus', true); |
|||
}); |
|||
} |
|||
// 查看节点或边的显示或隐藏状态
|
|||
edgeOrNodesIsVisible(type, graph, nodes) { |
|||
var resultStatus; |
|||
(nodes || []).forEach((node) => { |
|||
const nodeModel = graph.findById(node.id); |
|||
if (type == 1) { |
|||
// 判断是否全显示
|
|||
if (!nodeModel.isVisible(nodeModel)) { |
|||
resultStatus = false; |
|||
} else { |
|||
resultStatus = true; |
|||
} |
|||
} else if (type == 2) { |
|||
// 判断全隐藏
|
|||
if (!nodeModel.isVisible(nodeModel)) { |
|||
resultStatus = false; |
|||
} else { |
|||
resultStatus = true; |
|||
} |
|||
} |
|||
}); |
|||
return resultStatus; |
|||
} |
|||
// 缩放
|
|||
zoom(graph, type) { |
|||
const ratio = type === 'big' ? 0.1 : -0.1; |
|||
if ((globalV.zoom >= 0.2 && type === 'small') || (globalV.zoom <= 9.9 && type === 'big')) { |
|||
// 使用+和-进行缩放时,比例是10的倍数
|
|||
const fixedOneBit = Math.round(globalV['zoom'] * 10) / 10; // 四舍五入
|
|||
globalV.zoom = Number((fixedOneBit + ratio).toFixed(1)); |
|||
graph.zoomTo(globalV.zoom, { x: globalV.CANVAS_WIDTH / 2, y: globalV.CANVAS_HEIGHT / 2 }); |
|||
store.commit('graphMenu/SET_ZOOM', globalV.zoom); |
|||
} |
|||
} |
|||
// 通过搜索框添加节点使该节点高亮并且移动到画布中心
|
|||
moveNodeCenter(graph, addNodeId) { |
|||
graph.setItemState(addNodeId, 'focus', true); |
|||
graph.focusItem(addNodeId, true); |
|||
} |
|||
// 显示并高亮元素(param:元素的id)
|
|||
showHighItem(arr, graph) { |
|||
(arr || []).forEach((id) => { |
|||
const model = graph.findById(id); |
|||
if (!model.isVisible()) { |
|||
model.show(); |
|||
} |
|||
if (!model.hasState('focus')) { |
|||
graph.setItemState(id, 'focus', true); |
|||
} |
|||
}); |
|||
} |
|||
} |
|||
const menuHandle = new MenuHandle(); |
|||
export default menuHandle; |
@ -0,0 +1,65 @@ |
|||
export const getQueryString = function(name){//获取带#的url参数
|
|||
let reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)", "i"); |
|||
if(window.location.hash.indexOf("?") < 0){ |
|||
return null; |
|||
} |
|||
let r = window.location.hash.split("?")[1].match(reg); |
|||
if (r != null) return decodeURIComponent(r[2]); |
|||
return null; |
|||
} |
|||
/** |
|||
* 截取字符串(通过substring实现并支持中英文混合) |
|||
* @param str |
|||
* @param n 需要截取的长度 |
|||
* @returns {*} |
|||
*/ |
|||
export const sub = function (str, n) { |
|||
if (!str) return str |
|||
const r = /[^\x00-\xff]/g |
|||
if (str.replace(r, '**').length <= n) { return str } |
|||
const m = Math.floor(n / 2) |
|||
for (let i = m; i < str.length; i++) { |
|||
if (str.substr(0, i).replace(r, '**').length >= n) { |
|||
return str.substr(0, i) + '...' |
|||
} |
|||
} |
|||
return str |
|||
} |
|||
|
|||
// 防抖
|
|||
export const _debounce = function(fn, delay) { |
|||
var delay = delay || 200 |
|||
var timer |
|||
return function() { |
|||
var th = this |
|||
var args = arguments |
|||
if (timer) { |
|||
clearTimeout(timer) |
|||
} |
|||
timer = setTimeout(function() { |
|||
timer = null |
|||
fn.apply(th, args) |
|||
}, delay) |
|||
} |
|||
} |
|||
// 节流
|
|||
export const _throttle = function(fn, interval) { |
|||
var last |
|||
var timer |
|||
var interval = interval || 200 |
|||
return function() { |
|||
var th = this |
|||
var args = arguments |
|||
var now = +new Date() |
|||
if (last && now - last < interval) { |
|||
clearTimeout(timer) |
|||
timer = setTimeout(function() { |
|||
last = now |
|||
fn.apply(th, args) |
|||
}, interval) |
|||
} else { |
|||
last = now |
|||
fn.apply(th, args) |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,486 @@ |
|||
import G6 from '@antv/g6' |
|||
import { globalV } from './globalData' |
|||
const ExploreGraph = class ExploreGraph { |
|||
constructor() { |
|||
this.colorSets = G6.Util.getColorSetsBySubjectColors( |
|||
globalV.subjectColors, |
|||
globalV.darkBackColor, |
|||
globalV.theme, |
|||
globalV.disableColor |
|||
) |
|||
} |
|||
// 自定义节点和边
|
|||
registerNodeEdge() { |
|||
this.registerAggregatedNode() |
|||
this.registerRealNode() |
|||
this.registerQuadraticEdge() |
|||
this.registerCustomEdge() |
|||
} |
|||
registerAggregatedNode() { |
|||
const that = this |
|||
// Custom super node
|
|||
// 自定义聚合节点
|
|||
G6.registerNode( |
|||
'aggregated-node', |
|||
{ |
|||
draw(cfg, group) { |
|||
const width = 53 |
|||
const height = 27 |
|||
const style = cfg.style || {} |
|||
const colorSet = cfg.colorSet || that.colorSets[0] |
|||
// halo for hover(hover状态时的光晕)
|
|||
group.addShape('rect', { |
|||
attrs: { |
|||
x: -width * 0.55, |
|||
y: -height * 0.6, |
|||
width: width * 1.1, |
|||
height: height * 1.2, |
|||
fill: colorSet.mainFill, |
|||
opacity: 0.9, |
|||
lineWidth: 0, |
|||
radius: (height / 2 || 13) * 1.2 |
|||
}, |
|||
name: 'halo-shape', |
|||
visible: false |
|||
}) |
|||
// focus stroke for hover(hover状态时的描边)
|
|||
group.addShape('rect', { |
|||
attrs: { |
|||
x: -width * 0.55, |
|||
y: -height * 0.6, |
|||
width: width * 1.1, |
|||
height: height * 1.2, |
|||
fill: colorSet.mainFill, // '#3B4043',
|
|||
stroke: '#AAB7C4', |
|||
lineWidth: 1, |
|||
lineOpacty: 0.85, |
|||
radius: (height / 2 || 13) * 1.2 |
|||
}, |
|||
name: 'stroke-shape', |
|||
visible: false |
|||
}) |
|||
const keyShape = group.addShape('rect', { |
|||
attrs: { |
|||
...style, |
|||
x: -width / 2, |
|||
y: -height / 2, |
|||
width, |
|||
height, |
|||
fill: colorSet.mainFill, // || '#3B4043',
|
|||
stroke: colorSet.mainStroke, |
|||
lineWidth: 2, |
|||
cursor: 'pointer', |
|||
radius: height / 2 || 13, |
|||
lineDash: [2, 2] |
|||
}, |
|||
name: 'aggregated-node-keyShape' |
|||
}) |
|||
let labelStyle = {} // eslint-disable-line no-unused-vars
|
|||
if (cfg.labelCfg) { |
|||
labelStyle = Object.assign(labelStyle, cfg.labelCfg.style) |
|||
} |
|||
// 聚合节点包含的真实节点的数量text
|
|||
group.addShape('text', { |
|||
attrs: { |
|||
text: `${cfg.count}`, |
|||
x: 0, |
|||
y: 0, |
|||
textAlign: 'center', |
|||
textBaseline: 'middle', |
|||
cursor: 'pointer', |
|||
fontSize: 12, |
|||
fill: '#fff', |
|||
opacity: 0.85, |
|||
fontWeight: 400 |
|||
}, |
|||
name: 'count-shape', |
|||
className: 'count-shape', |
|||
draggable: true |
|||
}) |
|||
return keyShape |
|||
}, |
|||
setState: (name, value, item) => { |
|||
const group = item.get('group') |
|||
if (name === 'layoutEnd' && value) { |
|||
const labelShape = group.find((e) => e.get('name') === 'text-shape') |
|||
if (labelShape) labelShape.set('visible', true) |
|||
} else if (name === 'hover') { |
|||
if (item.hasState('focus')) { |
|||
return |
|||
} |
|||
const halo = group.find((e) => e.get('name') === 'halo-shape') |
|||
const keyShape = item.getKeyShape() |
|||
const colorSet = item.getModel().colorSet || that.colorSets[0] |
|||
if (value) { |
|||
halo && halo.show() |
|||
keyShape.attr('fill', colorSet.activeFill) |
|||
} else { |
|||
halo && halo.hide() |
|||
keyShape.attr('fill', colorSet.mainFill) |
|||
} |
|||
} else if (name === 'focus') { |
|||
const stroke = group.find((e) => e.get('name') === 'stroke-shape') |
|||
const keyShape = item.getKeyShape() |
|||
const colorSet = item.getModel().colorSet || that.colorSets[0] |
|||
if (value) { |
|||
stroke && stroke.show() |
|||
keyShape.attr('fill', colorSet.selectedFill) |
|||
} else { |
|||
stroke && stroke.hide() |
|||
keyShape.attr('fill', colorSet.mainFill) |
|||
} |
|||
} |
|||
}, |
|||
update: undefined |
|||
}, |
|||
'single-node' |
|||
) |
|||
} |
|||
registerRealNode() { |
|||
// Custom real node
|
|||
// 自定义真实节点
|
|||
G6.registerNode( |
|||
'real-node', |
|||
{ |
|||
draw(cfg, group) { |
|||
const style = cfg.style || {} |
|||
// 选中节点的边框
|
|||
group.addShape('circle', { |
|||
attrs: { |
|||
x: 0, |
|||
y: 0, |
|||
r: (cfg.size / 2) * 1.8, |
|||
fill: '#rgba(255,255,255,.4)', |
|||
// stroke: cfg.shapeStyle.bordercolor,
|
|||
opacity: 1, |
|||
lineWidth: 6 |
|||
// lineDash:[2,2,2,2],//边框虚线
|
|||
}, |
|||
name: 'selected-shape', |
|||
visible: false |
|||
}) |
|||
const keyShape = group.addShape('circle', { |
|||
attrs: { |
|||
...style, |
|||
width: cfg.size, |
|||
height: cfg.size, |
|||
r: cfg.size / 2, |
|||
fill: cfg.shapeStyle.bordercolor, |
|||
// stroke: cfg.shapeStyle.bordercolor,
|
|||
// strokeOpacity: 0.8,
|
|||
lineWidth: 2, |
|||
cursor: 'pointer' |
|||
}, |
|||
name: 'bg-node-keyShape', |
|||
draggable: true |
|||
}) |
|||
let point = -cfg.size * 7 / 20 |
|||
let wh = cfg.size * 7 / 10 |
|||
if (cfg.img.includes('/static/defaultNode.png')) { |
|||
point = -cfg.size / 2 |
|||
wh = cfg.size |
|||
} |
|||
group.addShape('image', { |
|||
attrs: { |
|||
...style, |
|||
x: point, |
|||
y: point, |
|||
img: cfg.img, |
|||
width: wh, |
|||
height: wh, |
|||
cursor: 'pointer' |
|||
}, |
|||
name: 'aggregated-node-keyShape', |
|||
draggable: true |
|||
}) |
|||
let labelStyle = {} // eslint-disable-line no-unused-vars
|
|||
if (cfg.labelCfg) { |
|||
labelStyle = Object.assign(labelStyle, cfg.labelCfg.style) |
|||
} |
|||
if (cfg.label) { |
|||
const text = cfg.label |
|||
let labelStyle = {} |
|||
if (cfg.labelCfg) { |
|||
labelStyle = Object.assign(labelStyle, cfg.labelCfg.style) |
|||
} |
|||
const fontSize = labelStyle.fontSize < 8 ? 8 : labelStyle.fontSize |
|||
group.addShape('text', { |
|||
attrs: { |
|||
text, |
|||
// x: cfg.size / 2,
|
|||
// y: cfg.size + cfg.labelCfg.style.fontSize + 5,
|
|||
y: cfg.size / 2 + cfg.labelCfg.style.fontSize + 5, |
|||
textAlign: 'center', |
|||
textBaseLine: 'alphabetic', |
|||
cursor: 'pointer', |
|||
fontsize:20, |
|||
fill: cfg.labelCfg.style.fill, |
|||
fontWeight: 100, |
|||
fontFamily: 'Microsoft YaHei' |
|||
}, |
|||
name: 'text-shape', |
|||
className: 'text-shape' |
|||
}) |
|||
} |
|||
return keyShape |
|||
}, |
|||
setState: (name, value, item) => { |
|||
const model = item.getModel() |
|||
const group = item.get('group') |
|||
const textShape = group.find((ele) => ele.get('name') === 'text-shape') |
|||
if (name === 'layoutEnd' && value) { |
|||
const labelShape = group.find((e) => e.get('name') === 'text-shape') |
|||
if (labelShape) labelShape.set('visible', true) |
|||
} else if (name === 'hover') { |
|||
// if (item.hasState('focus')) {
|
|||
// return
|
|||
// }
|
|||
const labelShape = group.find((e) => e.get('name') === 'text-shape') |
|||
// if (value) {
|
|||
// labelShape.attr('text', item.getModel().oriLabel)
|
|||
// } else {
|
|||
// labelShape.attr('text', item.getModel().label)
|
|||
// }
|
|||
item.getModel().label && labelShape.attr('text', item.getModel().label) |
|||
} else if (name === 'focus') { |
|||
const selected = group.find((e) => e.get('name') === 'selected-shape') |
|||
// const label = group.find((e) => e.get('name') === 'text-shape')
|
|||
const bgNode = group.find((e) => e.get('name') === 'bg-node-keyShape') |
|||
if (value) { |
|||
if (item.getModel().label) { |
|||
textShape.attr({ |
|||
fill: '#000', |
|||
fontWeight: 'bold', |
|||
fontsize:20, |
|||
}) |
|||
} |
|||
selected && selected.show() |
|||
// label && label.attr('fontWeight', 800)
|
|||
bgNode && bgNode.attr('lineWidth', 0) |
|||
} else { |
|||
if (item.getModel().label) { |
|||
textShape.attr({ |
|||
fill: '#000', |
|||
fontWeight: 100 |
|||
}) |
|||
} |
|||
selected && selected.hide() |
|||
// label && label.attr('fontWeight', 400)
|
|||
bgNode && bgNode.attr('lineWidth', 2) |
|||
} |
|||
} |
|||
}, |
|||
afterDraw(cfg, group) { |
|||
const shape = group.get('children')[0] |
|||
shape.animate( |
|||
{ |
|||
opacity: 0 |
|||
}, |
|||
{ |
|||
repeat: true, |
|||
duration: 2000, |
|||
easing: 'easeCubic' |
|||
} |
|||
) |
|||
shape.animate( |
|||
{ |
|||
fill: '#2F6BFF' |
|||
}, |
|||
{ |
|||
repeat: true, |
|||
duration: 2000, |
|||
easing: 'easeCubic' |
|||
} |
|||
) |
|||
shape.animate( |
|||
(ratio) => { |
|||
const diff = ratio <= 0.5 ? ratio * 25 : (1 - ratio) * 25 |
|||
let radius = cfg.size |
|||
if (isNaN(radius)) radius = radius[0] |
|||
return { |
|||
r: radius / 2 + diff |
|||
} |
|||
}, |
|||
{ |
|||
repeat: true, |
|||
duration: 2000, |
|||
easing: 'easeCubic' |
|||
} |
|||
) |
|||
}, |
|||
update: undefined |
|||
}, |
|||
'aggregated-node' |
|||
) // 这样可以继承 aggregated-node 的 setState
|
|||
} |
|||
registerQuadraticEdge() { |
|||
// Custom the quadratic edge for multiple edges between one node pair
|
|||
G6.registerEdge( |
|||
'custom-quadratic', |
|||
{ |
|||
setState: (name, value, item) => { |
|||
const group = item.get('group') |
|||
const model = item.getModel() |
|||
if (name === 'focus') { |
|||
const keyShape = group.find((ele) => ele.get('name') === 'edge-shape') |
|||
const textShape = group.find((ele) => ele.get('name') === 'text-shape') |
|||
const arrow = model.style.endArrow |
|||
textShape.attr({ |
|||
fill: '#0081ff', |
|||
fontWeight: 'bold' |
|||
}) |
|||
if (value) { |
|||
keyShape.attr({ |
|||
strokeOpacity: globalV.animateOpacity, |
|||
opacity: globalV.animateOpacity, |
|||
stroke: '#0081ff', |
|||
endArrow: { |
|||
...arrow, |
|||
stroke: '#0081ff', |
|||
fill: '#0081ff' |
|||
} |
|||
}) |
|||
} else { |
|||
textShape.attr({ |
|||
fill: '#98A7B9', |
|||
fontWeight: 100 |
|||
}) |
|||
const stroke = '#4E596C' |
|||
const opacity = model.isReal ? globalV.realEdgeOpacity : globalV.virtualEdgeOpacity |
|||
keyShape.attr({ |
|||
stroke, |
|||
strokeOpacity: opacity, |
|||
opacity, |
|||
endArrow: { |
|||
...arrow, |
|||
stroke, |
|||
fill: stroke |
|||
} |
|||
}) |
|||
} |
|||
} else { |
|||
textShape.attr({ |
|||
fill: '#98A7B9', |
|||
fontWeight: 100 |
|||
}) |
|||
} |
|||
// const lineDash = [4, 2, 1, 2];
|
|||
// // 获得该边的第一个图形,这里是边的 path
|
|||
// const shape = group.get('children')[0];
|
|||
// if(shape.cfg&&shape.cfg.animating==true){
|
|||
// shape.stopAnimate();
|
|||
// shape.attr('lineDash', null);
|
|||
// }else{
|
|||
// let index = 0;
|
|||
// // 边 path 图形的动画
|
|||
// shape.animate(
|
|||
// () => {
|
|||
// index++;
|
|||
// if (index > 9) {
|
|||
// index = 0;
|
|||
// }
|
|||
// const res = {
|
|||
// lineDash,
|
|||
// lineDashOffset: -index,
|
|||
// };
|
|||
// // 返回需要修改的参数集,这里修改了 lineDash,lineDashOffset
|
|||
// return res;
|
|||
// },
|
|||
// {
|
|||
// repeat: true, // 动画重复
|
|||
// duration: 3000, // 一次动画的时长为 3000
|
|||
// },
|
|||
// );
|
|||
// }
|
|||
} |
|||
}, |
|||
'quadratic' |
|||
) |
|||
} |
|||
registerCustomEdge() { |
|||
// Custom the line edge for single edge between one node pair
|
|||
G6.registerEdge( |
|||
'custom-line', |
|||
{ |
|||
setState: (name, value, item) => { |
|||
const group = item.get('group') |
|||
const model = item.getModel() |
|||
if (name === 'focus') { |
|||
const keyShape = group.find((ele) => ele.get('name') === 'edge-shape') |
|||
const textShape = group.find((ele) => ele.get('name') === 'text-shape') |
|||
textShape.attr({ |
|||
fill: '#0081ff', |
|||
fontWeight: 'bold' |
|||
}) |
|||
const arrow = model.style.endArrow |
|||
if (value) { |
|||
keyShape.attr({ |
|||
strokeOpacity: globalV.animateOpacity, |
|||
opacity: globalV.animateOpacity, |
|||
stroke: '#0081ff', |
|||
endArrow: { |
|||
...arrow, |
|||
stroke: '#0081ff', |
|||
fill: '#0081ff' |
|||
} |
|||
}) |
|||
} else { |
|||
textShape.attr({ |
|||
fill: '#98A7B9', |
|||
fontWeight: 100 |
|||
}) |
|||
const stroke = '#4E596C' |
|||
const opacity = model.isReal ? globalV.realEdgeOpacity : globalV.virtualEdgeOpacity |
|||
keyShape.attr({ |
|||
stroke, |
|||
strokeOpacity: opacity, |
|||
opacity, |
|||
endArrow: { |
|||
...arrow, |
|||
stroke, |
|||
fill: stroke |
|||
} |
|||
}) |
|||
} |
|||
} else { |
|||
textShape.attr({ |
|||
fill: '#98A7B9', |
|||
fontWeight: 100 |
|||
}) |
|||
} |
|||
// const lineDash = [4, 2, 1, 2];
|
|||
// // 获得该边的第一个图形,这里是边的 path
|
|||
// const shape = group.get('children')[0];
|
|||
// if(shape.cfg&&shape.cfg.animating==true){
|
|||
// shape.stopAnimate();
|
|||
// shape.attr('lineDash', null);
|
|||
// }else{
|
|||
// let index = 0;
|
|||
// // 边 path 图形的动画
|
|||
// shape.animate(
|
|||
// () => {
|
|||
// index++;
|
|||
// if (index > 9) {
|
|||
// index = 0;
|
|||
// }
|
|||
// const res = {
|
|||
// lineDash,
|
|||
// lineDashOffset: -index,
|
|||
// };
|
|||
// // 返回需要修改的参数集,这里修改了 lineDash,lineDashOffset
|
|||
// return res;
|
|||
// },
|
|||
// {
|
|||
// repeat: true, // 动画重复
|
|||
// duration: 3000, // 一次动画的时长为 3000
|
|||
// },
|
|||
// );
|
|||
// }
|
|||
} |
|||
}, |
|||
'single-edge' |
|||
) |
|||
} |
|||
} |
|||
const exploreGraph = new ExploreGraph() |
|||
export default exploreGraph |
@ -0,0 +1,148 @@ |
|||
import axios from 'axios'; |
|||
import { |
|||
MessageBox, |
|||
Message |
|||
// Loading
|
|||
} from 'winbox-ui'; |
|||
import store from '@/store'; |
|||
|
|||
// let loading
|
|||
// const option = {
|
|||
// lock: true,
|
|||
// text: '加载中……',
|
|||
// background: 'rgba(251, 251, 251, 0.8)'
|
|||
// }
|
|||
|
|||
// create an axios instance
|
|||
const service = axios.create({ |
|||
// baseURL: process.env.VUE_APP_BASE_API, // url = base url + request url
|
|||
baseURL: '', |
|||
// withCredentials: true, // send cookies when cross-domain requests
|
|||
timeout: 900000 // request timeout
|
|||
}); |
|||
|
|||
// request interceptor
|
|||
service.interceptors.request.use( |
|||
(config) => { |
|||
// do something before request is sent
|
|||
// loading = Loading.service(option)
|
|||
if (config.url.includes('/auth/api/')) { |
|||
config.baseURL = ''; |
|||
} else { |
|||
config.baseURL = process.env.VUE_APP_BASE_API; |
|||
} |
|||
if (store.getters.token) { |
|||
// let each request carry token
|
|||
// ['X-Token'] is a custom headers key
|
|||
// please modify it according to the actual situation
|
|||
} |
|||
return config; |
|||
}, |
|||
(error) => { |
|||
// do something with request error
|
|||
console.log(error); // for debug
|
|||
return Promise.reject(error); |
|||
} |
|||
); |
|||
|
|||
// response interceptor
|
|||
function arrayOrObjNotHaveValue(params) { |
|||
if (params === null || params === undefined) return false; |
|||
if (typeof params === 'object') { |
|||
if (Object.keys(params).length) { |
|||
return true; |
|||
} else { |
|||
return false; |
|||
} |
|||
} else if (Array.isArray(params)) { |
|||
if (params.length) { |
|||
return true; |
|||
} else { |
|||
return false; |
|||
} |
|||
} else if (typeof params === 'string' || typeof params === 'number' || typeof params === 'boolean') { |
|||
return true; |
|||
} else if (typeof params === 'function') { |
|||
return true; |
|||
} else { |
|||
return false; |
|||
} |
|||
} |
|||
service.interceptors.response.use( |
|||
/** |
|||
* If you want to get http information such as headers or status |
|||
* Please return response => response |
|||
*/ |
|||
|
|||
/** |
|||
* Determine the request status by custom code |
|||
* Here is just an example |
|||
* You can also judge the status by HTTP Status Code |
|||
*/ |
|||
(response) => { |
|||
if (response.data.code == '000400') { |
|||
// 请求未认证跳转登录
|
|||
// Message({
|
|||
// type: 'warning',
|
|||
// message: response.data.msg
|
|||
// })
|
|||
window.localStorage.removeItem('userInfo'); |
|||
if (getCookie('redirectUrl') && process.env.NODE_ENV === 'production') { |
|||
// window.location.href = getCookie('redirectUrl')
|
|||
} |
|||
} else if (response.data.code == '-1') { |
|||
return response.data; |
|||
} else { |
|||
if (response.config.url.includes('/auth/api/')) { |
|||
return response; |
|||
} else { |
|||
return response.data.data; |
|||
} |
|||
} |
|||
// const res = response.data
|
|||
// loading.close()
|
|||
// if the custom code is not 20000, it is judged as an error.
|
|||
// if (response.status !== 200) {
|
|||
// Message({
|
|||
// message: res.message || 'Error',
|
|||
// type: 'error',
|
|||
// duration: 5 * 1000
|
|||
// })
|
|||
|
|||
// 50008: Illegal token; 50012: Other clients logged in; 50014: Token expired;
|
|||
// if (res.code === 50008 || res.code === 50012 || res.code === 50014) {
|
|||
// // to re-login
|
|||
// MessageBox.confirm('You have been logged out, you can cancel to stay on this page, or log in again', 'Confirm logout', {
|
|||
// confirmButtonText: 'Re-Login',
|
|||
// cancelButtonText: 'Cancel',
|
|||
// type: 'warning'
|
|||
// }).then(() => {
|
|||
// store.dispatch('user/resetToken').then(() => {
|
|||
// location.reload()
|
|||
// })
|
|||
// })
|
|||
// }
|
|||
// NProgress.done()
|
|||
// return Promise.reject(new Error(res.message || 'Error'))
|
|||
// } else {
|
|||
// NProgress.done()
|
|||
// if (res.code === '-1') {
|
|||
// return res
|
|||
// } else {
|
|||
// return res.data
|
|||
// }
|
|||
// }
|
|||
}, |
|||
(error) => { |
|||
// loading.close()
|
|||
console.log('err' + error); // for debug
|
|||
Message({ |
|||
message: error.message, |
|||
type: 'error', |
|||
duration: 5 * 1000 |
|||
}); |
|||
return Promise.reject(error); |
|||
} |
|||
); |
|||
|
|||
export default service; |
@ -0,0 +1,46 @@ |
|||
import request from './request' |
|||
|
|||
// 创建快照
|
|||
export function createSnapShot(data) { |
|||
return request({ |
|||
url: '/anlysis/createGraphSnapshot', |
|||
method: 'post', |
|||
data |
|||
}) |
|||
} |
|||
// 删除快照
|
|||
export function delSnapshot(data) { |
|||
return request({ |
|||
url: '/anlysis/delGraphSnapshot', |
|||
method: 'post', |
|||
data |
|||
}) |
|||
} |
|||
// 查询快照数据
|
|||
export function findSnapShot(data) { |
|||
return request({ |
|||
url: '/anlysis/findGraphSnapshot', |
|||
method: 'post', |
|||
data |
|||
}) |
|||
} |
|||
|
|||
// 恢复快照数据到d3data
|
|||
export function recoverSnapshot(data) { |
|||
return request({ |
|||
url: '/anlysis/recoverSnapshot', |
|||
method: 'post', |
|||
data |
|||
}) |
|||
} |
|||
|
|||
// 快照数据对比
|
|||
export function recoverSnapshotData(data) { |
|||
return request({ |
|||
url: '/anlysis/snapshotComparison', |
|||
method: 'post', |
|||
data |
|||
}) |
|||
} |
|||
|
|||
|
After Width: 742 | Height: 378 | Size: 52 KiB |
@ -0,0 +1,2 @@ |
|||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1652778936406" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4027" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css">@font-face { font-family: feedback-iconfont; src: url("//at.alicdn.com/t/font_1031158_u69w8yhxdu.woff2?t=1630033759944") format("woff2"), url("//at.alicdn.com/t/font_1031158_u69w8yhxdu.woff?t=1630033759944") format("woff"), url("//at.alicdn.com/t/font_1031158_u69w8yhxdu.ttf?t=1630033759944") format("truetype"); } |
|||
</style></defs><path d="M469.3504 85.3504c211.968 0 384 172.032 384 384 0 211.968-172.032 384-384 384-211.968 0-384-172.032-384-384 0-211.968 172.032-384 384-384z m0 682.6496A298.5984 298.5984 0 0 0 768 469.3504a298.5984 298.5984 0 0 0-298.6496-298.7008 298.5472 298.5472 0 0 0-298.7008 298.7008A298.5984 298.5984 0 0 0 469.3504 768z m361.984 3.072l120.7296 120.6272-60.416 60.3648-120.6272-120.6784 60.3648-60.3648z" fill="#909399" p-id="4028"></path></svg> |
@ -0,0 +1,344 @@ |
|||
<template> |
|||
<div class="menu-search"> |
|||
<w-autocomplete |
|||
v-model="keyword" |
|||
v-scrollLoad="loadMore" |
|||
clearable |
|||
popper-class="graph-autocomplete" |
|||
:fetch-suggestions="querySearch" |
|||
placeholder="请输入" |
|||
:trigger-on-focus="false" |
|||
:popper-append-to-body="false" |
|||
@select="queryChange" |
|||
> |
|||
<!-- <i slot="prefix" class="iconfont icon-sousuo" /> --> |
|||
<template slot-scope="{ item }"> |
|||
<div class="name" v-html="highlightKeyword(item.name, keyword)" /> |
|||
<w-tag v-show="item.concept" size="mini">{{ item.concept }}</w-tag> |
|||
<div v-show="item.contentKey" class="content">{{ item.contentKey }}:{{ item.contentVal }}</div> |
|||
</template> |
|||
</w-autocomplete> |
|||
<img class="iconfont" src="./images/graphSearch.svg" @click="showSearch" /> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
import { EventBus } from './eventBus'; |
|||
import { searchEntityByKeyword, graphCentraNodeSave } from './graph/graph'; |
|||
import { globalV } from './graph/globalData'; |
|||
import { _debounce } from './graph/publicFunction'; |
|||
export default { |
|||
data() { |
|||
return { |
|||
searchShow: false, |
|||
keyword: '', |
|||
graph: {}, |
|||
pageNum: 1, |
|||
callbackFun: '' |
|||
}; |
|||
}, |
|||
mounted() { |
|||
EventBus.$off('Graph'); |
|||
EventBus.$on('Graph', (object) => { |
|||
this.graph = object; |
|||
}); |
|||
}, |
|||
beforeDestroy() { |
|||
EventBus.$off('Graph'); |
|||
}, |
|||
directives: { |
|||
scrollLoad: { |
|||
bind(el, binding, vnode) { |
|||
let wrapDom = el.querySelector('.w-autocomplete-suggestion__wrap'); |
|||
let listDom = el.querySelector('.w-autocomplete-suggestion__wrap .w-autocomplete-suggestion__list'); |
|||
wrapDom.addEventListener( |
|||
'scroll', |
|||
_debounce((e) => { |
|||
let condition = wrapDom.offsetHeight + wrapDom.scrollTop + 20 - listDom.offsetHeight; |
|||
if (condition > 0 && !vnode.context.loading) { |
|||
binding.value(); |
|||
} |
|||
}, false), |
|||
500 |
|||
); |
|||
} |
|||
} |
|||
}, |
|||
methods: { |
|||
loadMore() { |
|||
this.pageNum = this.pageNum + 1; |
|||
this.querySearch(); |
|||
}, |
|||
highlightKeyword(name, keyword) { |
|||
if (!keyword) return name; |
|||
const result = name.replace(new RegExp(keyword, 'g'), `<span class="primary_title">${keyword}</span>`); |
|||
return result; |
|||
}, |
|||
showSearch() { |
|||
this.searchShow = !this.searchShow; |
|||
this.keyword = ''; |
|||
if (!this.searchShow) { |
|||
document.querySelector('.menu-search .w-autocomplete .w-input .w-input__inner').style.width = '0'; |
|||
} else { |
|||
document.querySelector('.menu-search .w-autocomplete .w-input .w-input__inner').style.width = '170px'; |
|||
} |
|||
}, |
|||
querySearch(queryString, cb) { |
|||
if (cb) { |
|||
this.callbackFun = cb; |
|||
} |
|||
const results = []; |
|||
const params = { |
|||
max: this.pageNum * 10, |
|||
keyword: this.keyword, |
|||
domain: this.$route.query.domain, |
|||
pageNum: 1 |
|||
}; |
|||
searchEntityByKeyword(params).then((res) => { |
|||
(res || []).forEach((item) => { |
|||
const obj = { |
|||
name: item.name, |
|||
id: item.id |
|||
}; |
|||
if (item.description) { |
|||
obj.contentKey = '描述'; |
|||
obj.contentVal = item.description; |
|||
} else if (item.property.length) { |
|||
const name = item.property[0].name; |
|||
const valueKey = 'value_' + item.property[0].data_type.toLowerCase(); |
|||
if (name && item.property[0][valueKey]) { |
|||
obj.contentKey = name; |
|||
obj.contentVal = item.property[0][valueKey].join(); |
|||
} else { |
|||
obj.contentKey = ''; |
|||
obj.contentVal = ''; |
|||
} |
|||
} else { |
|||
obj.contentKey = ''; |
|||
obj.contentVal = ''; |
|||
} |
|||
if (JSON.stringify(item.concepts_) !== '{}') { |
|||
const tempConcept = Object.values(item.concepts_); |
|||
obj.concept = tempConcept[0]; |
|||
} |
|||
results.push(obj); |
|||
}); |
|||
this.callbackFun(results); |
|||
}); |
|||
}, |
|||
queryChange(item) { |
|||
let nodeisExist = false; |
|||
if (typeof globalV.basicData === 'object') { |
|||
globalV.basicData.nodes.forEach((ele) => { |
|||
if (ele.id === item.id) { |
|||
this.$message({ |
|||
message: `【${item.name}】在当前图谱已存在!`, |
|||
type: 'warning' |
|||
}); |
|||
nodeisExist = true; |
|||
this.graph.setItemState(item.id, 'focus', true); |
|||
return; |
|||
} |
|||
}); |
|||
if (!nodeisExist) { |
|||
const params = { |
|||
graphId: this.$route.query.graphId, |
|||
entityId: item.id |
|||
}; |
|||
graphCentraNodeSave(params).then((res) => { |
|||
this.$nextTick(() => { |
|||
// EventBus.$emit('RefreshGraph') |
|||
EventBus.$emit('subGraph', res); |
|||
}); |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
}; |
|||
</script> |
|||
|
|||
<style lang="less" scoped> |
|||
.menu-search { |
|||
position: relative; |
|||
display: inline-block; |
|||
// background: #fff; |
|||
margin: 4px 8px 4px 0; |
|||
vertical-align: middle; |
|||
.iconfont { |
|||
width: 20px; |
|||
height: 20px; |
|||
position: absolute; |
|||
top: 6px; |
|||
right: 0; |
|||
cursor: pointer; |
|||
} |
|||
::v-deep .w-input__inner { |
|||
padding: 0; |
|||
background-color: transparent; |
|||
border: none; |
|||
border-bottom: 1px solid #fff; |
|||
height: 34px; |
|||
border-radius: 0; |
|||
width: 200px; |
|||
font-size: 14px; |
|||
color: #8a909a; |
|||
} |
|||
::v-deep .w-input__prefix { |
|||
left: 16px; |
|||
.w-input__icon { |
|||
width: 16px; |
|||
// line-height: 40px; |
|||
position: absolute; |
|||
top: 0; |
|||
right: 5px; |
|||
} |
|||
.w-icon-search:before { |
|||
font-size: 16px; |
|||
} |
|||
} |
|||
::v-deep .w-input__suffix { |
|||
top: -1px; |
|||
right: 25px; |
|||
.w-input__icon { |
|||
width: 16px; |
|||
line-height: 34px; |
|||
} |
|||
.w-icon-circle-close:before { |
|||
font-size: 16px; |
|||
font-family: 'iconfont'; |
|||
content: '\e6f3'; |
|||
color: rgba(152, 167, 185, 0.5); |
|||
} |
|||
} |
|||
/* - Chrome ≤56, |
|||
- Safari 5-10.0 |
|||
- iOS Safari 4.2-10.2 |
|||
- Opera 15-43 |
|||
- Opera Mobile >12 |
|||
- Android Browser 2.1-4.4.4 |
|||
- Samsung Internet |
|||
- UC Browser for Android |
|||
- QQ Browser */ |
|||
::-webkit-input-placeholder { |
|||
color: #98a7b9; |
|||
font-weight: 400; |
|||
} |
|||
|
|||
/* Firefox 4-18 */ |
|||
:-moz-placeholder { |
|||
color: #98a7b9; |
|||
font-weight: 400; |
|||
} |
|||
|
|||
/* Firefox 19-50 */ |
|||
::-moz-placeholder { |
|||
color: #98a7b9; |
|||
font-weight: 400; |
|||
} |
|||
|
|||
/* - Internet Explorer 10–11 |
|||
- Internet Explorer Mobile 10-11 */ |
|||
:-ms-input-placeholder { |
|||
color: #98a7b9 !important; |
|||
font-weight: 400 !important; |
|||
} |
|||
|
|||
/* Edge (also supports ::-webkit-input-placeholder) */ |
|||
::-ms-input-placeholder { |
|||
color: #98a7b9; |
|||
font-weight: 400; |
|||
} |
|||
|
|||
/* CSS Working Draft */ |
|||
::placeholder { |
|||
color: #98a7b9; |
|||
font-weight: 400; |
|||
} |
|||
|
|||
.w-autocomplete { |
|||
display: flex; |
|||
align-items: center; |
|||
} |
|||
} |
|||
</style> |
|||
|
|||
<style lang="less"> |
|||
.menu-search { |
|||
.w-autocomplete { |
|||
.w-input { |
|||
.w-input__inner { |
|||
width: 0; |
|||
transition: 1s width ease-out; |
|||
border-bottom: 1px solid #8a909a; |
|||
} |
|||
} |
|||
} |
|||
.graph-autocomplete { |
|||
background: #fff; |
|||
border: none; |
|||
width: 170px !important; |
|||
border-radius: 2px; |
|||
top: 26px !important; |
|||
left: 0 !important; |
|||
li { |
|||
padding: 6px 20px; |
|||
line-height: 1; |
|||
.name, |
|||
.content { |
|||
color: #ffffff; |
|||
line-height: 1.6; |
|||
white-space: nowrap; |
|||
overflow: hidden; |
|||
text-overflow: ellipsis; |
|||
} |
|||
.name { |
|||
color: #0081ff; |
|||
display: inline-block; |
|||
vertical-align: middle; |
|||
max-width: 58%; |
|||
margin-right: 6px; |
|||
.primary_title { |
|||
color: #0081ff; |
|||
} |
|||
} |
|||
.content { |
|||
font-size: 12px; |
|||
} |
|||
.w-tag { |
|||
background: rgba(0, 129, 255, 0.1); |
|||
border-radius: 2px; |
|||
border: 1px solid rgba(0, 129, 255, 0.2); |
|||
max-width: 36%; |
|||
white-space: nowrap; |
|||
overflow: hidden; |
|||
text-overflow: ellipsis; |
|||
vertical-align: middle; |
|||
color: #0081ff; |
|||
} |
|||
&.highlighted, |
|||
&:hover { |
|||
background: rgba(26, 34, 50, 0.5); |
|||
.primary_title { |
|||
color: #f00; |
|||
} |
|||
} |
|||
} |
|||
.popper__arrow { |
|||
display: none; |
|||
} |
|||
.w-autocomplete-suggestion__wrap { |
|||
padding: 0; |
|||
} |
|||
.w-scrollbar__bar.is-vertical { |
|||
width: 4px; |
|||
} |
|||
.w-scrollbar__thumb { |
|||
background: rgba(0, 0, 0, 0.1); |
|||
&:hover { |
|||
background: #1a2232; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
</style> |
@ -0,0 +1,54 @@ |
|||
const getDefaultState = () => { |
|||
return { |
|||
zoom: '', |
|||
graph: null, |
|||
focusNodesArr: [], |
|||
graphId: null, |
|||
extendData: {nodes: [], edges: []} |
|||
} |
|||
} |
|||
|
|||
const state = getDefaultState() |
|||
|
|||
const mutations = { |
|||
SET_ZOOM: (state, zoom) => { |
|||
state.zoom = zoom |
|||
}, |
|||
SET_GRAPH: (state, graph) => { |
|||
state.graph = graph |
|||
}, |
|||
SET_FOCUSNODESARR: (state, focusNodesArr) => { |
|||
state.focusNodesArr = focusNodesArr |
|||
}, |
|||
SET_GRAPHID: (state, graphId) => { |
|||
state.graphId = graphId |
|||
}, |
|||
SET_EXTENDDATA: (state, extendData) => { |
|||
state.extendData = extendData |
|||
} |
|||
} |
|||
|
|||
const actions = { |
|||
SET_ZOOM_ACTIONS(context, payload) { |
|||
context.commit('SET_ZOOM', payload) |
|||
}, |
|||
SET_GRAPH_ACTIONS(context, payload) { |
|||
context.commit('SET_GRAPH', payload) |
|||
}, |
|||
SET_FOCUSNODESARR_ACTIONS(context, payload) { |
|||
context.commit('SET_FOCUSNODESARR', payload) |
|||
}, |
|||
SET_GRAPHID_ACTIONS(context, payload) { |
|||
context.commit('SET_GRAPHID', payload) |
|||
}, |
|||
SET_EXTENDDATA_ACTIONS(context, payload) { |
|||
context.commit('SET_EXTENDDATA', payload) |
|||
} |
|||
} |
|||
|
|||
export default { |
|||
namespaced: true, |
|||
state, |
|||
mutations, |
|||
actions |
|||
} |
@ -0,0 +1,383 @@ |
|||
.graph-layout { |
|||
.g6-component-contextmenu { |
|||
position: absolute; |
|||
z-index: 2; |
|||
list-style-type: none; |
|||
background-color: gray; |
|||
box-shadow: none; |
|||
border-radius: 6px; |
|||
font-size: 14px; |
|||
color: hsla(0, 0%, 100%, 0.85); |
|||
width: fit-content; |
|||
transition: opacity 0.2s; |
|||
text-align: center; |
|||
border: 0px; |
|||
|
|||
ul { |
|||
padding-left: 0px; |
|||
margin: 0; |
|||
li { |
|||
cursor: pointer; |
|||
list-style-type: none; |
|||
list-style: none; |
|||
margin-left: 0; |
|||
line-height: 38px; |
|||
&:hover { |
|||
color: #aaaaaa; |
|||
} |
|||
} |
|||
} |
|||
|
|||
// 菜单第一圈 |
|||
.menu-layout { |
|||
width: 180px; |
|||
height: 180px; |
|||
position: absolute; |
|||
left: -90px; |
|||
top: -90px; |
|||
clip-path: circle(50%); |
|||
-webkit-clip-path: circle(50%); |
|||
z-index: 999; |
|||
&::before { |
|||
content: ''; |
|||
width: 100%; |
|||
height: 100%; |
|||
position: absolute; |
|||
z-index: -1; |
|||
right: 0; |
|||
background: linear-gradient(180deg, rgba(30, 36, 43, 0.68) 0%, rgba(90, 109, 137, 0.27) 100%); |
|||
box-shadow: inset -2px -2px 10px 0px rgba(255, 255, 255, 0.1); |
|||
border-image: linear-gradient(137deg, rgba(255, 255, 255, 0.28), rgba(253, 255, 232, 0.26), rgba(255, 255, 255, 0)) 2 2; |
|||
backdrop-filter: blur(15px); |
|||
} |
|||
// 菜单中心的节点样式 |
|||
.node-default { |
|||
position: absolute; |
|||
border-radius: 50%; |
|||
z-index: 99; |
|||
border: 15px solid #232C3F; |
|||
box-sizing: content-box; |
|||
background: #6093FF; |
|||
.node-in { |
|||
width: 100%; |
|||
height: 100%; |
|||
background: #2B384E; |
|||
border-radius: 50%; |
|||
} |
|||
} |
|||
.node { |
|||
position: absolute; |
|||
border-radius: 50%; |
|||
z-index: 99; |
|||
border: 15px solid #232C3F; |
|||
box-sizing: content-box; |
|||
.node-in { |
|||
width: 100%; |
|||
height: 100%; |
|||
background: #2B384E; |
|||
border-radius: 50%; |
|||
overflow: hidden; |
|||
img { |
|||
width: 70%; |
|||
height: 70%; |
|||
display: block; |
|||
transform: translate(-50%, -50%); |
|||
margin-top: 50%; |
|||
margin-left: 50%; |
|||
} |
|||
} |
|||
} |
|||
.item-list { |
|||
width: 100%; |
|||
height: 100%; |
|||
clip-path: circle(50%); |
|||
-webkit-clip-path: circle(50%); |
|||
cursor: pointer; |
|||
.item { |
|||
width: 50%; |
|||
height: 50%; |
|||
position: absolute; |
|||
transform-origin: 100% 100%; |
|||
left: 0px; |
|||
top: 0; |
|||
&.item-1 { |
|||
transform: rotate(60deg) skew(30deg); |
|||
} |
|||
&.item-2 { |
|||
transform: rotate(120deg) skew(30deg); |
|||
} |
|||
&.item-3 { |
|||
transform: rotate(180deg) skew(30deg); |
|||
} |
|||
&.item-4 { |
|||
transform: rotate(240deg) skew(30deg); |
|||
} |
|||
&.item-5 { |
|||
transform: rotate(300deg) skew(30deg); |
|||
} |
|||
&.item-6 { |
|||
transform: rotate(0deg) skew(30deg); |
|||
} |
|||
&:hover { |
|||
background: linear-gradient(117deg, #1357FF 0%, #4E93FF 100%); |
|||
opacity: 0.89; |
|||
} |
|||
} |
|||
.item-text { |
|||
position: absolute; |
|||
font-size: 14px; |
|||
color: #000; |
|||
transform: scale(0.8); |
|||
pointer-events: none; |
|||
&.disabled { |
|||
color: rgba(255, 255, 255, .3); |
|||
} |
|||
.iconfont { |
|||
display: block; |
|||
} |
|||
&.item-text-1 { |
|||
left: 35%; |
|||
top: 6%; |
|||
} |
|||
&.item-text-2 { |
|||
left: 72%; |
|||
top: 22%; |
|||
} |
|||
&.item-text-3 { |
|||
left: 64%; |
|||
top: 55%; |
|||
} |
|||
&.item-text-4 { |
|||
left: 42%; |
|||
top: 73%; |
|||
} |
|||
&.item-text-5 { |
|||
left: 14%; |
|||
top: 56%; |
|||
} |
|||
&.item-text-6 { |
|||
left: 5%; |
|||
top: 22%; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
// 不可点击的选项 |
|||
.text-disabled { |
|||
color: rgba(0,0,0,.5) !important; |
|||
cursor: not-allowed; |
|||
} |
|||
// 菜单第二圈 |
|||
#second-menu-wrapper, #second-menu-wrapper-1, #second-menu-wrapper-2, #second-menu-wrapper-3 { |
|||
width: 300px; |
|||
height: 300px; |
|||
position: absolute; |
|||
left: -150px; |
|||
top: -150px; |
|||
opacity: 1; |
|||
clip-path: circle(50%); |
|||
-webkit-clip-path: circle(50%); |
|||
display: none; |
|||
.sector { |
|||
position: absolute; |
|||
top: 50%; |
|||
left: 50%; |
|||
overflow: hidden; |
|||
margin-top: -150px; |
|||
margin-left: -150px; |
|||
width: 150px; |
|||
height: 150px; |
|||
transform: rotate(105deg); |
|||
transform-origin: 100% 100%; |
|||
background: linear-gradient(180deg, rgba(176, 196, 222, 0.68) 0%, rgba(176, 196, 222, 0.27) 100%); |
|||
backdrop-filter: blur(15px); |
|||
} |
|||
.text { |
|||
color: #000; |
|||
font-size: 14px; |
|||
position: absolute; |
|||
width: 30px; |
|||
transform: scale(0.8); |
|||
cursor: pointer; |
|||
pointer-events: none; |
|||
} |
|||
.text-1 { |
|||
right: 25%; |
|||
top: 9% |
|||
} |
|||
.text-2 { |
|||
right: 11%; |
|||
top: 24%; |
|||
} |
|||
.text-3 { |
|||
right: 5%; |
|||
top: 44%; |
|||
} |
|||
.arc { |
|||
position: absolute; |
|||
top: 50%; |
|||
left: 50%; |
|||
overflow: hidden; |
|||
margin-top: -150px; |
|||
margin-left: -150px; |
|||
width: 150px; |
|||
height: 150px; |
|||
transform: rotate(137deg) skew(60deg); |
|||
transform-origin: 100% 100%; |
|||
cursor: pointer; |
|||
background: transparent; |
|||
&.active { |
|||
background: linear-gradient(117deg, #1357FF 0%, #4E93FF 100%); |
|||
backdrop-filter: none; |
|||
} |
|||
&:hover { |
|||
background: linear-gradient(117deg, #1357FF 0%, #4E93FF 100%); |
|||
backdrop-filter: none; |
|||
} |
|||
} |
|||
.arc-1 { |
|||
transform: rotate(104.8deg) skew(60deg); |
|||
} |
|||
.arc-2 { |
|||
transform: rotate(134.8deg) skew(60deg); |
|||
} |
|||
.arc-3 { |
|||
transform: rotate(164.8deg) skew(60deg); |
|||
} |
|||
} |
|||
#second-menu-wrapper-1 { |
|||
.arc-4 { |
|||
transform: rotate(255deg) skew(60deg); |
|||
background: linear-gradient(180deg, rgba(70, 130, 180, 0.68) 0%, rgba(70, 130, 180, 0.27) 100%); |
|||
backdrop-filter: blur(15px); |
|||
} |
|||
.text-4 { |
|||
width: auto; |
|||
right: 40%; |
|||
bottom: 8%; |
|||
} |
|||
} |
|||
#second-menu-wrapper-2 { |
|||
.sector { |
|||
transform: rotate(60deg) skew(30deg); |
|||
} |
|||
.arc-5 { |
|||
transform: rotate(60deg) skew(60deg); |
|||
} |
|||
.arc-6 { |
|||
transform: rotate(90deg) skew(60deg); |
|||
} |
|||
.text-5 { |
|||
top: 6%; |
|||
right: 55%; |
|||
} |
|||
.text-6 { |
|||
top: 6%; |
|||
right: 30%; |
|||
width: 18%; |
|||
} |
|||
} |
|||
#second-menu-wrapper-3 { |
|||
.sector { |
|||
transform: rotate(-60deg) skew(30deg); |
|||
} |
|||
.arc-7 { |
|||
transform: rotate(-30deg) skew(60deg); |
|||
} |
|||
.arc-8 { |
|||
transform: rotate(-60deg) skew(60deg); |
|||
} |
|||
.text-7 { |
|||
bottom: 37%; |
|||
left: 7%; |
|||
} |
|||
.text-8 { |
|||
bottom: 19%; |
|||
left: 16%; |
|||
} |
|||
} |
|||
#second-menu-mask-wrapper, #second-menu-mask-wrapper-1, #second-menu-mask-wrapper-2, #second-menu-mask-wrapper-3 { |
|||
width: 180px; |
|||
height: 180px; |
|||
position: absolute; |
|||
left: -90px; |
|||
top: -90px; |
|||
opacity: 1; |
|||
clip-path: circle(50%); |
|||
-webkit-clip-path: circle(50%); |
|||
display: none; |
|||
.arc-mask { |
|||
position: absolute; |
|||
top: 50%; |
|||
left: 50%; |
|||
overflow: hidden; |
|||
margin-top: -150px; |
|||
margin-left: -150px; |
|||
width: 150px; |
|||
height: 150px; |
|||
font-size: 1.5em; |
|||
transform-origin: 100% 100%; |
|||
background: rgba(30, 36, 43, 0.9); |
|||
} |
|||
.arc-mask-1 { |
|||
transform: rotate(104deg) skew(60deg); |
|||
} |
|||
.arc-mask-2 { |
|||
transform: rotate(135deg) skew(60deg); |
|||
} |
|||
.arc-mask-3 { |
|||
transform: rotate(165deg) skew(60deg); |
|||
} |
|||
.arc-mask-4 { |
|||
transform: rotate(255deg) skew(60deg); |
|||
} |
|||
.arc-mask-5 { |
|||
transform: rotate(60deg) skew(60deg); |
|||
} |
|||
.arc-mask-6 { |
|||
transform: rotate(90deg) skew(60deg); |
|||
} |
|||
.arc-mask-7 { |
|||
transform: rotate(-30deg) skew(60deg); |
|||
} |
|||
.arc-mask-8 { |
|||
transform: rotate(-60deg) skew(60deg); |
|||
} |
|||
} |
|||
} |
|||
#minimap { |
|||
width: 200px; |
|||
height: 150px; |
|||
position: absolute; |
|||
right: 0; |
|||
bottom: 0; |
|||
border-radius: 8px; |
|||
border: 1px solid rgba(0, 129, 255, 0.5); |
|||
background-color: #222D3D; |
|||
.icon-guanbi { |
|||
font-size: 16px; |
|||
color: #5F6A7A; |
|||
position: absolute; |
|||
right: 6px; |
|||
top: 6px; |
|||
z-index: 1; |
|||
cursor: pointer; |
|||
} |
|||
.rect { |
|||
width: 25px; |
|||
height: 25px; |
|||
position: absolute; |
|||
left: 0; |
|||
top: 0; |
|||
cursor: pointer; |
|||
background: #FFFFFF; |
|||
z-index: 1; |
|||
img { |
|||
display: block; |
|||
width: 100%; |
|||
height: 100%; |
|||
pointer-events: none; |
|||
} |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,103 @@ |
|||
// import { conceptAnalytic, entityInfo, entity_relation_byrids } from '@/api/graph';
|
|||
// import { EventBus } from '@/components/graphContainer/eventBus.js';
|
|||
const getDefaultState = () => { |
|||
return { |
|||
toggleShowState: true, |
|||
graphData: {}, // 真实名称
|
|||
graphIds: [], // 真实姓名id
|
|||
conceptName: [], // 概念名称
|
|||
showNode: null, // 展示节点/ 边
|
|||
nodeDetail: {}, // 节点详情
|
|||
relationShip: {} // 关系详情
|
|||
}; |
|||
}; |
|||
|
|||
const state = getDefaultState(); |
|||
|
|||
const mutations = { |
|||
SET_SHOW_STATE: (state, payload) => { |
|||
state.toggleShowState = payload; |
|||
}, |
|||
// 真实名称
|
|||
SET_GRAPH_DATA: (state, payload) => { |
|||
// state.graphIds = payload.graphIds
|
|||
state.graphData = payload; |
|||
}, |
|||
// 概念名称 - 对应名称
|
|||
SET_CONCEPT_ANALYTIC: (state, payload) => { |
|||
state.conceptName = payload; |
|||
}, |
|||
// 节点详情
|
|||
SET_NODE_DETAIL: (state, payload) => { |
|||
state.showNode = true; |
|||
state.nodeDetail = payload; |
|||
}, |
|||
// 关系详情
|
|||
SET_RELATION_SHIP: (state, payload) => { |
|||
state.showNode = false; |
|||
state.relationShip = payload; |
|||
}, |
|||
// 单独修改showNode
|
|||
SET_SHOW_NODE: (state, payload) => { |
|||
state.showNode = payload; |
|||
}, |
|||
RESET_STATE: (state) => { |
|||
// Object.assign(state, getDefaultState())
|
|||
state.graphData = { nodes: [], edges: [], ccount: [], concepts: {}, relationInstance: null, rids: null }; |
|||
} |
|||
}; |
|||
|
|||
const actions = { |
|||
conceptAnalyticAction(context, payload) { |
|||
context.commit('SET_GRAPH_DATA', payload); |
|||
}, |
|||
SET_GRAPH_DATA_ACTIONS(context, payload) { |
|||
// 初始化name ,然后提交
|
|||
console.log('SET_GRAPH_DATA_ACTIONS:', payload); |
|||
// conceptAnalytic(payload).then((res) => {
|
|||
// const arr = (res || []).map((item) => {
|
|||
// // 返回来的无法序列化,使用循环处理
|
|||
// return item;
|
|||
// });
|
|||
// context.commit('SET_CONCEPT_ANALYTIC', arr);
|
|||
// });
|
|||
}, |
|||
// 单个节点详情
|
|||
NODE_DETAIL_ACTIONS(context, payload) { |
|||
// 初始化name ,
|
|||
const eid = payload.split('&eid=')[1]; |
|||
if (eid && context.state.nodeDetail.entity) { |
|||
if (eid === context.state.nodeDetail.entity.id) { |
|||
context.state.showNode = true; |
|||
return; |
|||
} |
|||
} |
|||
console.log(('单个节点详情:', payload)); |
|||
// entityInfo(payload).then((res) => {
|
|||
// context.commit('SET_NODE_DETAIL', res);
|
|||
// });
|
|||
}, |
|||
// 单个边详情
|
|||
EDGE_DETAIL_ACTIONS(context, payload) { |
|||
// 初始化name
|
|||
const rids = payload.split('rids=')[1]; |
|||
if (rids && context.state.relationShip) { |
|||
if (Number(rids) === context.state.relationShip.id) { |
|||
context.state.showNode = false; |
|||
return; |
|||
} |
|||
} |
|||
console.log('单个边详情:', payload); |
|||
// entity_relation_byrids(payload).then((res) => {
|
|||
// EventBus.$emit('relationDyData', res && res[0] ? res[0] : '');
|
|||
// context.commit('SET_RELATION_SHIP', res && res[0] ? res[0] : '');
|
|||
// });
|
|||
} |
|||
}; |
|||
|
|||
export default { |
|||
namespaced: true, |
|||
state, |
|||
mutations, |
|||
actions |
|||
}; |
@ -0,0 +1,54 @@ |
|||
const getDefaultState = () => { |
|||
return { |
|||
zoom: '', |
|||
graph: null, |
|||
focusNodesArr: [], |
|||
graphId: null, |
|||
extendData: { nodes: [], edges: [] } |
|||
}; |
|||
}; |
|||
|
|||
const state = getDefaultState(); |
|||
|
|||
const mutations = { |
|||
SET_ZOOM: (state, zoom) => { |
|||
state.zoom = zoom; |
|||
}, |
|||
SET_GRAPH: (state, graph) => { |
|||
state.graph = graph; |
|||
}, |
|||
SET_FOCUSNODESARR: (state, focusNodesArr) => { |
|||
state.focusNodesArr = focusNodesArr; |
|||
}, |
|||
SET_GRAPHID: (state, graphId) => { |
|||
state.graphId = graphId; |
|||
}, |
|||
SET_EXTENDDATA: (state, extendData) => { |
|||
state.extendData = extendData; |
|||
} |
|||
}; |
|||
|
|||
const actions = { |
|||
SET_ZOOM_ACTIONS(context, payload) { |
|||
context.commit('SET_ZOOM', payload); |
|||
}, |
|||
SET_GRAPH_ACTIONS(context, payload) { |
|||
context.commit('SET_GRAPH', payload); |
|||
}, |
|||
SET_FOCUSNODESARR_ACTIONS(context, payload) { |
|||
context.commit('SET_FOCUSNODESARR', payload); |
|||
}, |
|||
SET_GRAPHID_ACTIONS(context, payload) { |
|||
context.commit('SET_GRAPHID', payload); |
|||
}, |
|||
SET_EXTENDDATA_ACTIONS(context, payload) { |
|||
context.commit('SET_EXTENDDATA', payload); |
|||
} |
|||
}; |
|||
|
|||
export default { |
|||
namespaced: true, |
|||
state, |
|||
mutations, |
|||
actions |
|||
}; |
2
src/styles/winbox-ui/css/black.css
File diff suppressed because it is too large
View File
2
src/styles/winbox-ui/css/blue.css
File diff suppressed because it is too large
View File
2
src/styles/winbox-ui/css/cyan.css
File diff suppressed because it is too large
View File
2
src/styles/winbox-ui/css/default.css
File diff suppressed because it is too large
View File
2
src/styles/winbox-ui/css/red.css
File diff suppressed because it is too large
View File
2
src/styles/winbox-ui/css/white.css
File diff suppressed because it is too large
View File
@ -1,128 +0,0 @@ |
|||
<template> |
|||
<div class="peoplePool"> |
|||
<w-row :gutter="16" type="flex"> |
|||
<w-col :span="3"> |
|||
<NavLeft></NavLeft> |
|||
</w-col> |
|||
<w-col :span="21"> |
|||
<div v-if="isShow" class="con-right"> |
|||
<BreadCrumb></BreadCrumb> |
|||
<Search></Search> |
|||
<!-- <ItemGroup></ItemGroup> --> |
|||
</div> |
|||
<div v-else class="detailGlobal"> |
|||
<div id="detailContent" class="detailContent"> |
|||
<BreadCrumb content="人物分析"></BreadCrumb> |
|||
<Profile id="content-item0" class="part"></Profile> |
|||
<PhotoVideo id="content-item1" class="part"></PhotoVideo> |
|||
<News id="content-item2" class="part"></News> |
|||
<PeopleDescriptive id="content-item3" class="part"></PeopleDescriptive> |
|||
<PeopleLifetime id="content-item4" class="part"></PeopleLifetime> |
|||
<SocialAnalysis id="content-item5" class="part"></SocialAnalysis> |
|||
<PlatformDynamics id="content-item6" class="part"></PlatformDynamics> |
|||
</div> |
|||
<div class="tabList"> |
|||
<w-anchor :offset-top="10" :get-container='getContainer' |
|||
@click="handleClick"> |
|||
<w-anchor-link href="#content-item0" title="基本信息"></w-anchor-link> |
|||
<w-anchor-link href="#content-item1" title="图册视频"></w-anchor-link> |
|||
<w-anchor-link href="#content-item2" title="新闻资讯"></w-anchor-link> |
|||
<w-anchor-link href="#content-item3" title="描述"></w-anchor-link> |
|||
<w-anchor-link href="#content-item4" title="人物生平"></w-anchor-link> |
|||
<w-anchor-link href="#content-item5" title="社交分析与扩线"></w-anchor-link> |
|||
<w-anchor-link href="#content-item6" title="社交平台发文动态"></w-anchor-link> |
|||
</w-anchor> |
|||
</div> |
|||
|
|||
</div> |
|||
</w-col> |
|||
|
|||
</w-row> |
|||
</div> |
|||
</template> |
|||
<script> |
|||
import bus from '@/utils/EventBus'; |
|||
import Search from '@/components/PeoplePoolPage/search'; |
|||
import NavLeft from '@/components/PeoplePoolPage/nav-left.vue'; |
|||
import BreadCrumb from '@/components/PeoplePoolPage/breadcrumb.vue'; |
|||
import ItemGroup from '@/components/PeoplePoolPage/itemGroup.vue'; |
|||
import Profile from '@/components/PeoplePoolPage/people-profile.vue'; |
|||
import PhotoVideo from '@/components/PeoplePoolPage/photo-video.vue'; |
|||
import News from '@/components/PeoplePoolPage/news.vue'; |
|||
import PeopleDescriptive from '@/components/PeoplePoolPage/people-descriptive.vue'; |
|||
import PeopleLifetime from '@/components/PeoplePoolPage/people-lifetime.vue'; |
|||
import SocialAnalysis from '@/components/PeoplePoolPage/social-analysis.vue'; |
|||
import PlatformDynamics from '@/components/PeoplePoolPage/platform-dynamics.vue'; |
|||
|
|||
export default { |
|||
name: 'PeoplePool', |
|||
components: { |
|||
Search, |
|||
NavLeft, |
|||
BreadCrumb, |
|||
ItemGroup, |
|||
Profile, |
|||
PhotoVideo, |
|||
News, |
|||
PeopleDescriptive, |
|||
PeopleLifetime, |
|||
SocialAnalysis, |
|||
PlatformDynamics |
|||
}, |
|||
data() { |
|||
return { |
|||
isShow: true, |
|||
list: ['基本信息', '图册视频', '新闻资讯', '描述', '人物生平', '社交分析与扩线', '社交平台发文动态'] |
|||
}; |
|||
}, |
|||
methods: { |
|||
getContainer() { |
|||
return this.scrollContainer; |
|||
}, |
|||
handleClick(e, link) { |
|||
e.preventDefault(); |
|||
} |
|||
}, |
|||
mounted() { |
|||
this.scrollContainer = document.getElementsByClassName('w-main')[0]; |
|||
bus.$on('detail', val => { |
|||
this.isShow = val; |
|||
}); |
|||
} |
|||
|
|||
}; |
|||
</script> |
|||
<style lang='less' scoped> |
|||
.peoplePool { |
|||
color: @fontColor; |
|||
background-color: @mainBg; |
|||
|
|||
.detailGlobal { |
|||
display: flex; |
|||
|
|||
.detailContent { |
|||
width: 87.5%; |
|||
margin-top: 16px; |
|||
margin-right: 1%; |
|||
// overflow-y: auto; |
|||
height: 845px; |
|||
padding-right: 5px; |
|||
|
|||
.part { |
|||
width: 100%; |
|||
border-radius: 4px; |
|||
background: #fff; |
|||
margin-bottom: 16px; |
|||
} |
|||
} |
|||
|
|||
.tabList { |
|||
width: 10%; |
|||
margin-top: 50px; |
|||
::v-deep.w-anchor-wrapper { |
|||
background-color: transparent !important; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
</style> |
@ -0,0 +1,70 @@ |
|||
<template> |
|||
<div class="icons-container"> |
|||
<w-card> |
|||
<w-tabs v-model="activeName" type="card"> |
|||
<w-tab-pane v-for="item of iconsList" :key="item.name + item.id" :label="item.name" :name="item.name + item.id"> |
|||
<w-row> |
|||
<w-col v-for="o in item.icons" :key="item.id + o.name" :span="3"> |
|||
<div class="icon-item"> |
|||
<component :is="`Cool${PascalName(o.name)}`" :size="30"></component> |
|||
<span style="font-size: 14px; color: #666">{{ o.name_zh }}</span> |
|||
<span style="font-size: 14px"> |
|||
{{ generateIconCode(o.name) }} |
|||
</span> |
|||
</div> |
|||
</w-col> |
|||
</w-row> |
|||
</w-tab-pane> |
|||
</w-tabs> |
|||
</w-card> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
import iconsList from '@coolbox/we-design/icons.json'; |
|||
export default { |
|||
name: 'Icons', |
|||
data() { |
|||
return { |
|||
iconsList, |
|||
activeName: iconsList[0].name + iconsList[0].id |
|||
}; |
|||
}, |
|||
methods: { |
|||
PascalName(str) { |
|||
const strArr = str.split('-'); |
|||
const reg = /\d/; |
|||
for (let i = 0; i < strArr.length; i++) { |
|||
if (reg.test(strArr[i].charAt(0))) { |
|||
strArr[i] = '_' + strArr[i]; |
|||
} else { |
|||
strArr[i] = strArr[i].charAt(0).toUpperCase() + strArr[i].substring(1); |
|||
} |
|||
} |
|||
return strArr.join(''); |
|||
}, |
|||
generateIconCode(symbol) { |
|||
return `<Cool${this.PascalName(symbol)} />`; |
|||
} |
|||
} |
|||
}; |
|||
</script> |
|||
|
|||
<style lang="less" scoped> |
|||
.icons-container { |
|||
overflow: hidden; |
|||
|
|||
.icon-item { |
|||
height: 100px; |
|||
text-align: center; |
|||
font-size: 30px; |
|||
color: #24292e; |
|||
} |
|||
|
|||
span { |
|||
display: block; |
|||
font-size: 16px; |
|||
margin-top: 10px; |
|||
} |
|||
} |
|||
</style> |
@ -0,0 +1,181 @@ |
|||
<template> |
|||
<div v-loading="loading" style="min-height: 100vh;"> |
|||
<div v-if="isShow1" class="deatil"> |
|||
<div style="width: 100%;height: 100%;"> |
|||
<div class="detailGlobal"> |
|||
<div id="detailContent" class="detailContent"> |
|||
<detailCom ref="mySon" :offsettop="0"></detailCom> |
|||
</div> |
|||
<div class="tabList"> |
|||
<TracingPoint :size="10"></TracingPoint> |
|||
<div> |
|||
<w-backtop style="right: initial;"> |
|||
<div class="goback"> |
|||
<CoolUploadLine size="16" color="#ffffff" /> |
|||
<div style="font-size: 14px; color: #ffffff; margin-top: 2px">回顶</div> |
|||
</div> |
|||
</w-backtop> |
|||
</div> |
|||
|
|||
</div> |
|||
|
|||
</div> |
|||
</div> |
|||
|
|||
</div> |
|||
</div> |
|||
</template> |
|||
<script> |
|||
import bus from '@/utils/EventBus'; |
|||
import TracingPoint from '@/components/PeoplePoolPage/tracing-point.vue'; |
|||
import detailCom from '@/components/PeoplePoolPage/detailCom.vue'; |
|||
export default { |
|||
name: 'Detail', |
|||
components: { |
|||
TracingPoint, |
|||
detailCom |
|||
}, |
|||
data() { |
|||
return { |
|||
isShow: true, |
|||
list: ['人物简介', '图册视频', '新闻资讯', '描述', '人物生平', '社交分析与扩线', '社交平台发文动态'], |
|||
screenWidth: '', |
|||
loading: true, |
|||
isShow1: true |
|||
}; |
|||
}, |
|||
methods: { |
|||
}, |
|||
watch: { |
|||
// screenWidth() { |
|||
// if (this.screenWidth < 1480) { |
|||
// document.querySelector('.detailGlobal').style.marginRight = '160px'; |
|||
// document.querySelector('.detailGlobal').style.marginLeft = '160px'; |
|||
|
|||
// } else { |
|||
// // document.querySelector('.tabList').style.display = 'block'; |
|||
// document.querySelector('.detailGlobal').style.marginRight = 'calc((100vw - 1200px) / 2)'; |
|||
// document.querySelector('.detailGlobal').style.marginLeft = 'calc(50vw - 600px)'; |
|||
|
|||
// } |
|||
// if (this.screenWidth < 1300) { |
|||
// document.querySelector('.tabList').style.display = 'none'; |
|||
|
|||
// } else { |
|||
// document.querySelector('.tabList').style.display = 'block'; |
|||
|
|||
// } |
|||
// } |
|||
}, |
|||
created() { |
|||
// this.getdeatilInfo(); |
|||
bus.$on('load2', val => { |
|||
this.loading = val; |
|||
}); |
|||
this.$refs.mySon.$on('show', this.ShowName); |
|||
|
|||
}, |
|||
mounted() { |
|||
// bus.$on('load2', val => { |
|||
// this.loading = val; |
|||
// }); |
|||
this.screenWidth = document.body.clientWidth; |
|||
window.onresize = () => { |
|||
return (() => { |
|||
this.screenWidth = document.body.clientWidth; |
|||
})(); |
|||
}; |
|||
|
|||
} |
|||
}; |
|||
</script> |
|||
<style lang='less' scoped> |
|||
.deatil { |
|||
background-color: @mainBg; |
|||
width: 100%; |
|||
height: 100%; |
|||
|
|||
// position: relative; |
|||
.detailGlobal { |
|||
position: relative; |
|||
height: 100%; |
|||
width: 1200px; |
|||
margin-left: auto; |
|||
margin-right: auto; |
|||
padding-bottom: 16px; |
|||
// min-width: 1000px; |
|||
// margin: 0 calc(50vw - 600px) 0 calc((100vw - 1200px) / 2); |
|||
// background-color: red; |
|||
|
|||
.detailContent { |
|||
width: 100%; |
|||
margin-top: 16px; |
|||
margin-right: 1%; |
|||
// overflow-y: auto; |
|||
padding-right: 5px; |
|||
|
|||
.part { |
|||
width: 100%; |
|||
border-radius: 4px; |
|||
background: #fff; |
|||
margin-bottom: 16px; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.tabList { |
|||
margin-top: 50px; |
|||
margin-left: 50px; |
|||
position: absolute; |
|||
right: -154px; |
|||
top: -49px; |
|||
z-index: 1000; |
|||
|
|||
::v-deep.w-anchor-wrapper { |
|||
background-color: transparent !important; |
|||
} |
|||
} |
|||
|
|||
.goback { |
|||
text-align: center; |
|||
height: 55px; |
|||
display: flex; |
|||
flex-direction: column; |
|||
justify-content: center; |
|||
} |
|||
|
|||
::v-deep .w-backtop { |
|||
width: 55px; |
|||
height: 55px; |
|||
background-color: #2055f4; |
|||
border-radius: 8px; |
|||
} |
|||
|
|||
::v-deep .w-backtop:hover { |
|||
background-color: #2055f4; |
|||
} |
|||
|
|||
.w-anchor-link { |
|||
>a { |
|||
overflow: hidden; |
|||
white-space: nowrap; |
|||
text-overflow: ellipsis; |
|||
} |
|||
} |
|||
} |
|||
</style> |
|||
<style lang="less"> |
|||
.deatil { |
|||
.w-anchor-link-title { |
|||
color: #000000; |
|||
} |
|||
|
|||
.w-anchor-link { |
|||
>a { |
|||
overflow: hidden; |
|||
white-space: nowrap; |
|||
text-overflow: ellipsis; |
|||
} |
|||
} |
|||
} |
|||
</style> |