Compare commits

...

75 Commits

Author SHA1 Message Date
陈劲松
80130e046b Merge branch 'cherry-pick-3f450020' into 'dev-23.07'
fix: 修复实体下拉预览跳转url参数不对的问题

See merge request cyber-narrator/cn-ui!33
2023-07-17 12:08:09 +00:00
chenjinsong
bddc366f05 fix: 修复实体下拉预览跳转url参数不对的问题
(cherry picked from commit 3f45002047)
2023-07-17 20:08:03 +08:00
chenjinsong
4b04433194 fix: 修复关系图有时节点label不对的问题 2023-07-17 11:09:03 +08:00
chenjinsong
f46ec514f0 Merge remote-tracking branch 'origin/dev' into dev 2023-07-17 10:48:22 +08:00
chenjinsong
f915c51254 fix: 修复关系图有时无法拓展的问题 2023-07-17 10:48:13 +08:00
刘洪洪
4290ef2ceb fix: 实体详情tabs入参去除时间参数 2023-07-17 09:50:21 +08:00
刘洪洪
766b777bc6 fix: 修复实体详情底部tab的’相关实体‘active的数值与实际不符的问题 2023-07-16 22:04:23 +08:00
hyx
14125d549d 1.实体详情tab(相关实体、开放端口)没超过100条的话,more按钮隐藏;2.i18n的新增,后端校验失败时没弹提示。用户、角色新增的name重复校验失败时没弹提示;3.下拉框背景色调为白色;4.修改密码增加at least five的校验; 2023-07-15 12:50:03 +08:00
chenjinsong
ee338b247d CN-1066 feat: 增加知识库格式提示 2023-07-14 20:32:18 +08:00
chenjinsong
d640c656fa CN-1160 fix: 调整样式细节 2023-07-14 19:38:30 +08:00
刘洪洪
5edd569148 fix: 实体列表信誉等级图标更换 2023-07-14 18:56:03 +08:00
刘洪洪
f486c53945 fix: 实体详情底部tab的数值添加接口报错时置0处理 2023-07-14 18:43:52 +08:00
chenjinsong
95dc133dac fix: 更新实体列表下拉中地图下钻url 2023-07-14 18:38:47 +08:00
刘洪洪
01c393fee2 fix: 实体关系首页布局调整 2023-07-14 17:43:39 +08:00
chenjinsong
f334746a70 CN-1087 feat: 实体关系图完善 2023-07-14 16:50:30 +08:00
hyx
19ca35b738 CN-1149 一开始就没超过100条的话,把more按钮隐藏掉 2023-07-14 15:56:28 +08:00
hyx
96e7a935ee fix : 详情页"相关实体"展示交互优化(去掉界面多余显示的内容);知识库内新建Tag页,下拉框可用时,背景色为白色; 2023-07-14 15:05:42 +08:00
刘洪洪
dce3921beb fix: 实体关系右侧弹窗列表宽度调整,以及修改实体类型字段取值 2023-07-14 11:08:41 +08:00
刘洪洪
ae7f72a72b fix: 修改实体搜索框的操作符列表范围 2023-07-13 18:06:21 +08:00
刘洪洪
3823ea3dc4 fix: 实体列表吞吐量和展开吞吐量保持保持一致 2023-07-13 17:40:54 +08:00
hyx
4b7a559af7 CN-1149 详情页"相关实体"展示交互优化 2023-07-13 17:16:22 +08:00
hyx
ba8dfaf0d4 CN-1149 详情页"相关实体"展示交互优化 2023-07-13 17:11:00 +08:00
刘洪洪
e489e568e9 fix: 修复实体列表吞吐量字段取错问题 2023-07-13 16:41:31 +08:00
刘洪洪
c0f6a47da4 fix: 实体列表的吞吐量数值为0展示以及展开详情为'-'时去除单位 2023-07-12 18:05:47 +08:00
chenjinsong
7b4070f06a CN-1087 feat: 实体关系图完善 2023-07-12 17:23:42 +08:00
hyx
97ae64943b CN-1128 fix:知识库内新建Tag页,类型一栏背景颜色和其他栏不一致 2023-07-12 15:51:53 +08:00
刘洪洪
4855944e44 fix: 实体基数统计字段名称更改 2023-07-12 15:47:22 +08:00
刘洪洪
22b805791a fix: 实体列表展开详情的关系模块无数据时添加-提示 2023-07-12 14:32:08 +08:00
hyx
80992f0b30 Merge remote-tracking branch 'origin/dev' into dev 2023-07-12 11:52:35 +08:00
hyx
1405d6b3fe CN-1159 fix:userbox、rolebox表单校验提示内容不合理 2023-07-12 11:51:23 +08:00
刘洪洪
607ab78fc1 fix: 修复实体列表搜索框在清除内容后,切换模式内容还存在的问题 2023-07-12 11:46:37 +08:00
刘洪洪
5fcfdc4244 fix: 调整实体列表文字和图表距离 2023-07-12 11:18:25 +08:00
刘洪洪
86c14ae560 fix: 1、删除获取实体列表多余入参;2、左侧filter隐藏空数据项以及添加nodata 2023-07-11 16:37:53 +08:00
刘洪洪
c558bdb952 fix: 实体列表搜索将name更改为label 2023-07-11 15:54:42 +08:00
刘洪洪
d7b6a4f735 fix: 实体列表以及展开详情的loading调整 2023-07-10 18:54:43 +08:00
刘洪洪
4114c3fa36 fix: 实体搜索为advanced模式时,调整关联下拉列表的值 2023-07-10 17:47:38 +08:00
刘洪洪
049622fd4b CN-1111 fix: 鼠标悬浮在搜索框中的模式按钮上时增加提示信息 2023-07-10 16:59:09 +08:00
刘洪洪
d0c4565eef CN-1111: 鼠标悬浮在搜索框中的模式按钮上时增加提示信息 2023-07-10 16:57:20 +08:00
chenjinsong
446bd4431e CN-1087 feat: 实体关系 2023-07-09 21:51:05 +08:00
刘洪洪
9c46e1af47 CN-1150: 实体列表接口对接 2023-07-07 17:22:51 +08:00
hyx
87cd43dde2 CN-1114 fix:手动输入时间范围当开始时间和结束时间的时间点一样时,日期无法输入为同一个月的不同日期 2023-07-05 14:31:34 +08:00
chenjinsong
53b4085111 CN-1156 fix: 修复s3=0时无法下载和预览的问题 2023-07-05 11:28:06 +08:00
hyx
697cc1da2d CN-1118 fix:修改密码时输入错误的当前密码,更新时没有提示 2023-07-03 16:14:09 +08:00
hyx
d841118ad9 CN-1134 fix:知识库切换展示记录数由20/页切换为50/页时,展示No Date 2023-07-03 15:54:32 +08:00
刘洪洪
07156f9e03 fix: 实体关系右侧详情的行间距调整 2023-07-03 14:51:59 +08:00
刘洪洪
35004f419a CN-1143: Network & APP Performance和DNS service insights页面均无法在趋势图中框选时间范围 2023-07-03 14:07:28 +08:00
刘洪洪
5da5f55b80 CN-1144: Dashboard下钻后顶部的模糊查询时间条件错误 2023-07-03 11:21:21 +08:00
hyx
dff5135d88 fix:1、CN-1133 增加IP段格式验证;2、情报分析无数据时样式错误 2023-07-03 08:42:33 +08:00
chenjinsong
f724477934 CN-1087 feat: 关系图部分实现 2023-07-02 22:38:59 +08:00
chenjinsong
24864ca1be Merge remote-tracking branch 'origin/dev' into dev 2023-06-30 18:43:11 +08:00
chenjinsong
0789dbcbfb CN-1087 feat: 关系图部分实现 2023-06-30 18:43:02 +08:00
刘洪洪
17f0701c27 Merge remote-tracking branch 'origin/dev' into dev 2023-06-30 18:08:06 +08:00
刘洪洪
617ee131ec CN-1135: 统一各页面环形图样式 2023-06-30 18:07:46 +08:00
chenjinsong
6d6f863ae7 CN-1130 fix: save增加报错信息提示 2023-06-30 17:24:28 +08:00
chenjinsong
bdee548bc1 CN-1112 fix: 隐藏explore按钮 2023-06-30 16:55:36 +08:00
刘洪洪
5eed8baac1 fix: 调整实体关系的标签文字距离 2023-06-30 15:51:10 +08:00
刘洪洪
e474003376 fix: 修复实体关系ip列表的按钮宽度异常的问题 2023-06-30 15:33:00 +08:00
刘洪洪
76b409d95d fix: 修改实体列表跳转到实体关系的参数名 2023-06-30 14:29:44 +08:00
刘洪洪
09c83e215c fix: 修复实体列表选择时间后刷新界面,时间选择器被重置为1hour的问题 2023-06-30 14:17:52 +08:00
刘洪洪
e8e8bd3462 fix: 修复自动引入element-plus版本导致的提交报错 2023-06-30 11:04:48 +08:00
刘洪洪
0e752cb3a3 fix: 实体关系模块右侧列表详情样式调整 2023-06-30 10:41:29 +08:00
chenjinsong
30883802cb CN-1087 feat: 关系图基本实现 2023-06-29 14:40:50 +08:00
刘洪洪
4c38f1c913 CN-1136: 实体左侧筛选弹框时间参数错误 2023-06-29 14:25:05 +08:00
刘洪洪
a0fe66089f fix: 更新icon图标 2023-06-29 11:56:30 +08:00
刘洪洪
57609406be CN-1093 fix: 添加实体类型初始化 2023-06-29 11:15:15 +08:00
刘洪洪
f38b5c7947 CN-1093 fix: 修改axios自动引入时的问题 2023-06-29 10:59:56 +08:00
刘洪洪
09b37512c9 1093: 实体关系探索--右侧详情信息静态页面开发 2023-06-29 10:46:00 +08:00
刘洪洪
7f15139a38 fix: 修复实体列表左侧filter弹窗因时间戳转换导致偶现noData的问题 2023-06-29 10:31:18 +08:00
刘洪洪
5ee3351ba7 CN-1119: 网络&APP性能页面查看具体ip对应的网络性能时,鼠标悬浮在趋势图上后与鼠标未悬浮时的趋势图不一致 2023-06-28 16:31:29 +08:00
hyx
df89c61a65 CN-1075 table自定义列缓存功能有时会因字段变化导致缓存数据使用报错 2023-06-28 11:23:29 +08:00
hyx
ab1b551642 CN-1075 table自定义列缓存功能有时会因字段变化导致缓存数据使用报错 2023-06-27 10:54:36 +08:00
刘洪洪
38bc1ec729 CN-1115: 实体首页使用全量搜索时报错 2023-06-27 10:41:00 +08:00
刘洪洪
dee401c9f2 CN-1113: 实体详情--tabs增加数字提示 2023-06-26 15:01:53 +08:00
刘洪洪
eb611bdac9 fix: 1、修复customize只保留最后一项点击空白处,再打开customize新增选项,上次禁用的项还存在的问题;2、表格某一列数据为空时,添加-避免界面展示空白 2023-06-25 15:49:54 +08:00
chenjinsong
468026b3fa fix: 修复link monitor有些图没数据时没显示nodata的问题 2023-06-25 15:07:24 +08:00
103 changed files with 8342 additions and 1816 deletions

View File

@@ -12,6 +12,7 @@
"dependencies": {
"@amcharts/amcharts4": "^4.10.24",
"@amcharts/amcharts4-geodata": "^4.1.20",
"@antv/g6": "^4.8.17",
"axios": "^0.21.1",
"babel-plugin-lodash": "^3.3.4",
"codemirror": "^5.65.1",
@@ -40,7 +41,9 @@
"@babel/cli": "^7.12.1",
"@babel/core": "^7.11.4",
"@babel/plugin-proposal-class-properties": "^7.12.1",
"@babel/plugin-proposal-private-methods": "^7.12.1",
"@babel/plugin-transform-runtime": "^7.12.1",
"@babel/plugin-proposal-private-property-in-object": "^7.12.1",
"@babel/preset-env": "^7.11.5",
"@babel/preset-typescript": "^7.10.4",
"@commitlint/cli": "^9.1.2",

View File

@@ -1,302 +1,304 @@
.el-drawer.rtl {
width: 700px !important;
}
.el-drawer__body {
.common-right-box {
height: 100%;
}
.right-box, .right-sub-box {
display: flex;
flex-direction: column;
padding: 0;
height: 100%;
width: 100%;
.el-date-editor {
.el-input__inner {
padding-left: 32px;
}
.el-drawer__body {
height: 100%;
}
}
.right-box__header {
display: flex;
justify-content: space-between;
align-items: center;
height: 60px;
padding: 0 20px;
border-bottom: 1px solid $--right-box-border-color;
.right-box, .right-sub-box {
display: flex;
flex-direction: column;
padding: 0;
height: 100%;
width: 100%;
.header__title {
font-size: 16px;
font-weight: bold;
color: #333;
}
.header__operation {
i {
color: #999;
font-size: 14px;
}
}
}
.right-box__container {
height: calc(100% - 130px);
padding: 0 30px;
overflow-x: hidden;
overflow-y: auto;
.el-textarea__inner {
padding: 5px 70px 4px 15px;
}
.container__form-width.container__form{
.input-box {
.el-textarea {
.el-textarea__inner {
width: 530px;
height: 32px;
padding: 5px 70px 4px 10px;
}
.el-input__count {
right: -40px;
line-height: 29px;
height: 25px;
}
.el-date-editor {
.el-input__inner {
padding-left: 32px;
}
}
}
.el-form-item__content {
.input-box {
.el-textarea {
.el-textarea__inner {
width: 517px;
height: 32px;
padding: 5px 70px 4px 10px;
}
.el-input__count {
right: -40px;
line-height: 29px;
height: 25px;
}
.right-box__header {
display: flex;
justify-content: space-between;
align-items: center;
height: 60px;
padding: 0 20px;
border-bottom: 1px solid $--right-box-border-color;
.header__title {
font-size: 16px;
font-weight: bold;
color: #333;
}
.header__operation {
i {
color: #999;
font-size: 14px;
}
}
}
.form-row-item {
.input-box {
.el-textarea {
.el-textarea__inner {
width: 466px;
height: 32px;
padding: 5px 70px 4px 10px;
}
.el-input__count {
right: 0;
.right-box__container {
height: calc(100% - 130px);
padding: 0 30px;
overflow-x: hidden;
overflow-y: auto;
.el-textarea__inner {
padding: 5px 70px 4px 15px;
}
.container__form-width.container__form{
.input-box {
.el-textarea {
.el-textarea__inner {
width: 530px;
height: 32px;
padding: 5px 70px 4px 10px;
}
.el-input__count {
right: -40px;
line-height: 29px;
height: 25px;
}
}
}
}
}
.el-form-item {
.el-input__count {
line-height: 29px;
height: 25px;
.el-form-item__content {
.input-box {
.el-textarea {
.el-textarea__inner {
width: 517px;
height: 32px;
padding: 5px 70px 4px 10px;
}
.el-input__count {
right: -40px;
line-height: 29px;
height: 25px;
}
}
}
}
}
.el-form-item {
.el-input--small.not-fixed-height {
height: 32px;
.form-row-item {
.input-box {
.el-textarea {
.el-textarea__inner {
width: 466px;
height: 32px;
padding: 5px 70px 4px 10px;
}
.el-input__count {
right: 0;
}
}
}
}
.el-form-item {
.el-input__count {
line-height: 29px;
height: 25px;
}
}
}
.el-input__inner, .el-textarea__inner {
padding: 0 10px;
border-radius: $--border-radius-primary;
border: 1px solid $--right-box-border-color;
}
.el-textarea__inner {
padding: 5px 70px 4px 15px;
}
.el-form {
padding-top: 20px;
.el-form-item {
margin-bottom: 16px;
.el-form-item__label{
padding-bottom: 6px;
font-size: 14px;
line-height: 16px;
color: #666;
}
.el-input__inner:hover {
border-color: darken($--right-box-border-color, 10%);
}
.el-input__inner:focus {
border-color: darken($--right-box-border-color, 20%);
.el-input--small.not-fixed-height {
height: 32px;
.el-input__count {
line-height: 29px;
height: 25px;
}
}
}
.el-form-item.is-error .el-input__inner, .el-form-item.is-error .el-input__inner:focus, .el-form-item.is-error .el-textarea__inner, .el-form-item.is-error .el-textarea__inner:focus, .el-message-box__input input.invalid, .el-message-box__input input.invalid:focus {
border-color: #F56C6C
}
.form__sub-title {
display: flex;
justify-content: space-between;
.el-input__inner, .el-textarea__inner {
padding: 0 10px;
margin-bottom: 20px;
line-height: 32px;
font-size: 14px;
font-weight: bold;
color: #555;
background-color: #F6F6F6;
}
/* 虚线框类型的form-item */
.form__dotted-item {
padding: 10px 10px 6px 10px;
margin-bottom: 10px;
border: 1px dashed $--border-color-primary;
border-radius: $--border-radius-primary;
border: 1px solid $--right-box-border-color;
}
.el-textarea__inner {
padding: 5px 70px 4px 15px;
}
.el-form {
padding-top: 20px;
.el-form-item {
margin-bottom: 0;
.el-form-item__label {
width: 100%;
margin-bottom: 16px;
.el-form-item__label{
padding-bottom: 6px;
font-size: 14px;
line-height: 16px;
color: #666;
}
.form__labels-label {
display: flex;
justify-content: space-between;
.el-input__inner:hover {
border-color: darken($--right-box-border-color, 10%);
}
.el-input__inner:focus {
border-color: darken($--right-box-border-color, 20%);
}
}
.el-form-item.is-error .el-input__inner, .el-form-item.is-error .el-input__inner:focus, .el-form-item.is-error .el-textarea__inner, .el-form-item.is-error .el-textarea__inner:focus, .el-message-box__input input.invalid, .el-message-box__input input.invalid:focus {
border-color: #F56C6C
}
.form__sub-title {
display: flex;
justify-content: space-between;
padding: 0 10px;
margin-bottom: 20px;
line-height: 32px;
font-size: 14px;
font-weight: bold;
color: #555;
background-color: #F6F6F6;
}
/* 虚线框类型的form-item */
.form__dotted-item {
padding: 10px 10px 6px 10px;
margin-bottom: 10px;
border: 1px dashed $--border-color-primary;
border-radius: $--border-radius-primary;
.el-form-item {
margin-bottom: 0;
.el-form-item__label {
width: 100%;
}
.form__labels-label {
display: flex;
justify-content: space-between;
}
}
}
.form__create-btn {
margin-bottom: 20px;
width: 300px;
height: 28px;
border: 1px solid lighten($--color-primary, 60%);
border-radius: $--border-radius-primary;
background-color: lighten($--color-primary, 95%);
i {
color: $--color-primary;
}
}
.form__flex-container {
display: flex;
justify-content: center;
align-items: center;
}
.one-third-form-item-left{
display: inline-block;
width: calc(50% - 5px);
}
.one-third-form-item-right{
display: inline-block;
width: calc(50% - 5px);
}
.form-item--half-width-other-two{
display: inline-block;
width: calc(50% - 10px);
}
.form-item--half-width-other{
display: inline-block;
width: calc(50% - 10px);
}
}
.form__create-btn {
margin-bottom: 20px;
}
.right-box__footer {
display: flex;
align-items: center;
justify-content: center;
height: 70px;
box-shadow: -3px 0 8px -3px rgba(205,205,205,0.77);
.footer__btn {
margin: 0 15px;
height: 30px;
min-width: 74px;
padding: 0 15px;
color: white;
background-color: $--color-primary;
border: none;
border-radius: 4px;
outline: none;
font-size: 14px;
cursor: pointer;
transition: background-color linear .2s, color linear .1s;
}
.footer__btn:hover:not(.footer__btn--disabled) {
background-color: lighten($--color-primary, 10%);
}
.footer__btn--light {
background-color: white;
border: 1px solid $--border-color-primary;
color: #333;
}
.footer__btn.footer__btn--light:hover:not(.footer__btn--disabled) {
background-color: white;
border-color: lighten($--color-primary, 40%);
color: $--color-primary;
}
.footer__btn--disabled {
opacity: .6;
cursor: default;
}
}
/* 隐藏label新增按钮处级联选择器的input */
.hide-casc-input {
position: relative;
.hide-input {
position: absolute;
top: 0;
width: 300px;
height: 28px;
border: 1px solid lighten($--color-primary, 60%);
border-radius: $--border-radius-primary;
background-color: lighten($--color-primary, 95%);
opacity: 0;
}
}
.label__multi-text {
display: flex;
justify-content: space-between;
}
.right-box__select {
width: 100%;
}
.el-select-last.right-box-select-dropdown {
left: 1698px;
}
.limit-height .el-cascader-menu {
max-height: 200px;
overflow: auto;
}
i {
color: $--color-primary;
.form-items--half-width-group {
display: flex;
justify-content: space-between;
flex-wrap: wrap;
.form-item--half-width {
width: calc(50% - 10px);
.el-select {
width: 100%;
}
}
.form__flex-container {
display: flex;
justify-content: center;
align-items: center;
}
.one-third-form-item-left{
display: inline-block;
width: calc(50% - 5px);
}
.one-third-form-item-right{
display: inline-block;
width: calc(50% - 5px);
}
.form-item--half-width-other-two{
display: inline-block;
width: calc(50% - 10px);
}
.form-item--half-width-other{
display: inline-block;
width: calc(50% - 10px);
}
}
}
.right-box__footer {
display: flex;
align-items: center;
justify-content: center;
height: 70px;
box-shadow: -3px 0 8px -3px rgba(205,205,205,0.77);
.footer__btn {
margin: 0 15px;
height: 30px;
min-width: 74px;
padding: 0 15px;
color: white;
background-color: $--color-primary;
border: none;
border-radius: 4px;
outline: none;
font-size: 14px;
cursor: pointer;
transition: background-color linear .2s, color linear .1s;
}
.footer__btn:hover:not(.footer__btn--disabled) {
background-color: lighten($--color-primary, 10%);
}
.footer__btn--light {
background-color: white;
border: 1px solid $--border-color-primary;
color: #333;
}
.footer__btn.footer__btn--light:hover:not(.footer__btn--disabled) {
background-color: white;
border-color: lighten($--color-primary, 40%);
color: $--color-primary;
}
.footer__btn--disabled {
opacity: .6;
cursor: default;
}
}
/* 隐藏label新增按钮处级联选择器的input */
.hide-casc-input {
position: relative;
.hide-input {
.cn-icon-minus-position {
display: inline-flex;
flex-direction: column;
position: absolute;
top: 0;
width: 300px;
opacity: 0;
right: 0;
top: 50%;
height: 100%;
transform: translateY(-50%);
justify-content: space-between;
}
.form-item--end-with-btn { // 末尾留出btn宽度空间的form item
}
.el-form-item__content .el-autocomplete .el-input-group {
vertical-align: unset;
}
}
.label__multi-text {
display: flex;
justify-content: space-between;
}
.right-box__select {
width: 100%;
}
.el-select-last.right-box-select-dropdown {
left: 1698px;
}
.limit-height .el-cascader-menu {
max-height: 200px;
overflow: auto;
}
.form-items--half-width-group {
display: flex;
justify-content: space-between;
flex-wrap: wrap;
.form-item--half-width {
width: calc(50% - 10px);
.el-select {
width: 100%;
}
}
}
.cn-icon-minus-position {
display: inline-flex;
flex-direction: column;
position: absolute;
right: 0;
top: 50%;
height: 100%;
transform: translateY(-50%);
justify-content: space-between;
}
.form-item--end-with-btn { // 末尾留出btn宽度空间的form item
}
.el-form-item__content .el-autocomplete .el-input-group {
vertical-align: unset;
}

View File

@@ -10,10 +10,9 @@
right: 10px;
.search__suffix {
margin-left: 8px; // 新版实体列表改版,后续记得解开
// margin-left: 8px;
.cn-icon-search-advance, .cn-icon-search-normal {
//.cn-icon-search-advance, .cn-icon-search-normal, .cn-icon-filter {
.cn-icon-search-advance, .cn-icon-search-normal, .cn-icon-filter {
color: #A6AAAE;
font-size: 18px;
}

View File

@@ -85,3 +85,6 @@
@import 'views/charts2/entityDetailTabs';
@import 'views/charts2/digitalCertificate';
@import 'views/charts2/entityDetailBasicInfo';
@import "views/charts2/graphRightListBlock";
@import "views/charts2/graphRightDetailBlock";

View File

@@ -33,8 +33,15 @@
}
}
.more{
margin-bottom:20px;
.button {
color:#909399;
cursor: pointer;
}
}
.type-content {
margin-bottom:15px;
margin-bottom:0px;
display:flex;
flex-flow: row wrap;
width:100%;

View File

@@ -24,11 +24,12 @@
display: flex;
height: 134px;
align-items: center;
justify-content: space-around;
justify-content: space-evenly;
.analysis-entry-item {
display: flex;
flex-direction: column;
min-width: 70px;
align-items: center;
cursor: pointer;
@@ -69,7 +70,7 @@
.dividing-line {
height: 1px;
width: calc(100% - 60px);
width: 100%;
margin-top: 21px;
background-color: #EFF2F5;
}

View File

@@ -0,0 +1,205 @@
$font-size: 12px;
.graph-detail-basic-info {
position: relative;
padding-bottom: 12px;
display: flex;
justify-content: space-between;
//height: 100%;
.graph-detail-header {
display: flex;
flex-direction: column;
.entity-graph-type {
font-size: 12px;
color: #717171;
font-weight: 400;
}
}
.entity-type {
color: #717171;
}
.graph-basic-info {
display: flex;
align-items: center;
justify-content: space-between;
.graph-basic-info-name__block {
display: flex;
align-items: center;
.graph-basic-info-name {
padding-right: 10px;
max-width: 260px;
font-size: 20px;
color: #353636;
font-weight: 700;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.graph-basic-info-icon {
display: flex;
justify-content: center;
align-items: center;
height: 18px;
width: 18px;
border-radius: 50%;
background-color: #EFF1F4;
cursor: pointer;
flex-shrink: 0;
i {
color: #717171;
font-size: 10px;
-webkit-transform: scale(0.8); // 强制给文字进行缩放达到12px以下小字体的效果
}
}
}
}
.graph-detail__icon {
width: 50px;
height: 50px;
overflow: hidden;
display: flex;
justify-content: center;
justify-items: center;
align-items: center;
margin-right: 10px;
border-radius: 50%;
background-color: #F3F7FA;
i {
font-size: 26px;
color: #4E84B4;
}
}
}
.graph-close {
color: #575757;
font-size: 8px;
cursor: pointer;
}
.graph-basic-info__block {
margin-top: 12px;
margin-bottom: 18px;
.graph-header__icon {
width: 3px !important;
height: 14px !important;
}
.graph-basic-info__block-content {
.graph-content-item, .graph-content-relationship-item {
display: flex;
line-height: 24px;
}
.graph-content-item {
.graph-content-item-label, .graph-content-item-value {
width: 130px;
font-family: NotoSansSChineseRegular;
font-size: $font-size;
color: #717171;
font-weight: 400;
padding-right: 10px;
flex-shrink: 0;
}
.graph-content-item-value {
width: 230px;
color: #353636;
font-weight: 400;
overflow-wrap: break-word;
line-height: normal;
margin-top: 0.15rem;
}
}
.graph-content-relationship-item {
justify-content: space-between;
line-height: 24px;
.graph-relationship-item-label, .graph-relationship-item-value {
font-size: $font-size;
color: #353636;
font-weight: 400;
//height: 40px;
display: flex;
align-items: center !important;
padding-top: 1px;
.graph-relationship-item-label-icon {
font-size: 14px;
}
}
.graph-relationship-item-value {
color: #717171;
i {
cursor: pointer;
}
}
}
}
.graph-tag-list {
display: flex;
align-items: flex-start;
flex-wrap: wrap;
margin: 6px 0;
.graph-tag-item {
margin-bottom: 10px;
margin-right: 9px;
padding: 0 8px;
height: 24px;
line-height: 24px;
font-size: 12px;
span {
margin-top: -1px;
}
}
}
}
.padding-b-10 {
padding-bottom: 10px;
}
.padding-b-4 {
padding-bottom: 4px;
}
//修改popover样式
.graph-popover {
width: auto !important;
background: #303133 !important;
color: #fff !important;
font-size: 12px !important;
padding: 10px !important;
}
.graph-popover .el-popper__arrow::before {
background: #303133 !important;
}
.graph-expand-relationship__icon {
font-size: 13px;
}
.graph-basic-info__block-title {
font-size: 13px;
color: #353636;
font-weight: 600;
}

View File

@@ -0,0 +1,154 @@
$font-size: 12px;
.graph-list-header {
display: flex;
justify-content: space-between;
.graph-list-header-title {
display: flex;
align-items: center;
margin-bottom: 10px;
span {
font-size: 16px;
color: #353636;
line-height: 21px;
font-weight: 600;
}
.graph-list-header-icon {
font-size: 21px;
color: #717171;
margin-right: 9px;
}
}
.graph-list-header-number {
font-size: 12px;
color: #717171;
font-weight: 400;
span {
font-weight: bold;
}
}
}
.graph-list-expand-btn-block {
margin-top: 16px;
margin-bottom: 24px;
.graph-list-expand-btn {
height: 28px;
line-height: 28px;
background: #38ACD2;
border-radius: 3px;
font-size: 12px;
color: #FFFFFF;
display: flex;
align-items: center;
justify-content: center;
font-weight: 500;
padding: 14px 10px;
cursor: pointer;
border: 1px solid rgba(46,136,166,0.85);
i {
font-size: 16px;
margin-right: 7px;
}
&.graph-list-expand-btn--disabled {
opacity: .4;
cursor: default;
}
}
}
.graph-list-content-header {
font-size: 14px;
color: #353636;
font-weight: 500;
}
.graph-list-content {
padding: 0 10px;
.graph-list-item-ip {
margin-bottom: 10px;
font-size: $font-size;
color: #353636;
font-weight: 400;
}
.graph-list-item-block {
width: 100%;
background: rgba(247, 247, 247, 1);
border: 1px solid rgba(226, 229, 236, 1);
border-radius: 2px;
padding: 10px 15px;
.graph-list-item, .graph-list-item__app {
display: flex;
.graph-list-item-label, .graph-list-item-label__app {
width: 72px;
//margin-right: 15px;
font-size: $font-size;
color: #717171;
font-weight: 400;
flex-shrink: 0;
}
.graph-list-item-label__app {
width: 83px;
display: flex;
align-items: flex-start;
line-height: 22px;
}
.graph-list-country-flag {
width: 16px;
height: 14px;
margin-right: 5px;
}
.graph-list-item-value {
font-size: $font-size;
color: #353636;
font-weight: 400;
line-height: 18px;
display: flex;
align-items: center;
margin-top: 0.15rem;
}
.graph-list-item-value1 {
display: flex;
align-content: center;
}
}
.graph-list-item {
align-items: center;
}
}
}
.padding-b-20 {
padding-bottom: 20px;
}
.padding-b-16 {
padding-bottom: 16px;
}
.padding-b-12 {
padding-bottom: 12px;
}
.graph-list-dividing-line {
width: 300px;
height: 1px;
background: #ECECEC;
margin: 11px 0;
}

View File

@@ -1,19 +1,13 @@
.information-aggregation__table {
.intelligence-content {
padding-top:10px !important;
padding-bottom:4px !important;
display: flex;
flex-direction: column;
justify-content: center;
align-items: flex-start;
.information-aggregation-tags {
display: flex;
flex-direction: row;
justify-content: center;
justify-content: left;
align-items: flex-start;
margin-bottom:6px;
padding-top:10px !important;
padding-bottom:4px !important;
}
}
.information-aggregation__valid {
background: #eff3e9;
border-radius: 10px;

View File

@@ -71,7 +71,7 @@
.el-input__inner {
font-size: 14px;
color: #353636;
background-color: #F5F8FA;
background-color: #FFFFFF;
}
.common-select {
top: 32px !important;

View File

@@ -222,3 +222,10 @@
padding: 0 4px;
//color: white;
}
.performance-event-remark {
font-family: NotoSansSChineseRegular;
font-size: 12px;
color: #353636;
font-weight: 400;
}

View File

@@ -117,9 +117,9 @@
.overview-left {
display: flex;
flex-direction: column;
//flex-direction: column;
align-items: center;
padding: 0 30px;
padding: 0 0 0 30px;
.overview-left-span {
font-size: 16px;
@@ -141,12 +141,14 @@
.overview-right {
display: flex;
flex-direction: column;
padding: 0 30px;
justify-content: center;
padding: 0 15px;
.right-row {
display: flex;
height: 30px;
height: 18px;
align-items: center;
color: #666666;
.right-label-loading {
position: relative;
@@ -168,7 +170,7 @@
font-weight: bold;
}
i {
padding-right: 10px;
padding-right: 4px;
font-size: 18px;
}
.cn-icon-increase {

View File

@@ -1,104 +1,100 @@
.entity-filter-case {
display: flex;
flex-direction: column;
width: 280px;
margin-right: 10px;
width: 320px;
margin-right: 20px;
overflow: auto;
z-index: 1;
border: 1px solid rgba(226, 229, 236, 1) !important;
border-radius: 4px !important;
.filter-case__header {
background-color: #E1E6ED;
margin-bottom: 10px;
padding-left: 8px;
height: 36px;
line-height: 36px;
color: #666;
font-size: 14px;
}
.entity-filter {
display: flex;
flex-direction: column;
border: 1px solid #E7EAED;
margin-bottom: 10px;
background-color: white;
.filter__header {
height: 46px;
margin: 0 15px;
line-height: 46px;
border-bottom: 1px solid #EFF2F5;
padding-left: 8px;
height: 36px;
line-height: 36px;
color: #666;
font-size: 14px;
color: #333;
background: #F7F7F7;
box-shadow: 0 1px 0 0 rgba(226,229,236,1);
border-radius: 4px 4px 0 0;
}
.filter__body {
padding: 11px 0 21px 0;
.filter__row {
padding: 0 15px;
.filter__header {
height: 46px;
line-height: 46px;
margin: 0 20px;
font-size: 14px;
color: #353636;
font-weight: 500;
}
.filter__body {
width: calc(100% - 40px);
margin: 0 20px;
.filter-hr {
width: calc(100% + 20px);
margin-left: -10px;
margin-top: 10px;
height: 1px;
background: #EFF2F5;
//background: #000;
}
.filter__body-item {
height: 26px;
line-height: 26px;
display: flex;
align-items: center;
justify-content: space-between;
cursor: pointer;
.filter__body-item-left {
display: flex;
align-items: center;
cursor: pointer;
transition: all linear .2s;
.filter__row-popover {
display: flex;
line-height: 26px;
font-size: 14px;
color: #353636;
font-weight: 400;
.filter-country-flag {
width: 18px;
height: 12px;
}
&:hover, &.filter__row--active {
background-color: #F3F7FA;
}
.row__label {
font-size: 14px;
flex: 8;
display: flex;
i {
color: #8FA1BE;
}
span {
padding-left: 6px;
color: #333;
}
}
.row__value {
color: #666;
position: relative;
display: inline-block;
.filter__body-item-left-index {
width: 16px;
height: 16px;
text-align: center;
flex: 1;
.chart__loading img {
left: unset;
right: 0;
}
}
}
}
}
}
.filter__row-popover {
.pop-title {
i {
margin-right: 6px;
}
}
.entity-pop-custom {
.filter-top-box {
.chart__loading {
height: calc(100% - 65px);
top: 64px;
}
.top-table-percent{
display:grid;
grid-template-columns: 50% auto;
grid-template-rows: 100%;
grid-row-gap: 0px;
grid-column-gap: 0px;
.top-table-progress{
align-content: center;
padding-top: 8px;
}
}
.customer-no-border-table {
.el-table__body-wrapper {
height: calc(100% - 36px);
background: #EFF2F5;
border-radius: 2px;
margin-right: 6px;
font-family: NotoSansHans-Black;
font-size: 9px;
color: #96A2B0;
font-weight: 900;
display: flex;
align-items: center;
justify-content: center;
}
.filter__body-item-left-label {
max-width: 180px;
font-family: NotoSansSChineseRegular;
font-size: 14px;
color: #353636;
font-weight: 400;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
}
.filter__body-item-right {
flex-shrink: 0;
font-family: NotoSansSChineseRegular;
font-size: 12px;
color: #717171;
font-weight: 400;
}
}
}

View File

@@ -5,7 +5,7 @@
background: #FFFFFF;
border-radius: 2px;
transition: all .2s;
//border: 1px solid #E2E5EC; 新版实体列表改版,后续记得解开
border: 1px solid #E2E5EC;
&:hover .cn-entity__header .header__content {

View File

@@ -34,6 +34,7 @@
.overview__title {
color: #333;
font-size: 14px;
font-weight: 600;
}
.overview__content-loading.overview__content {
position: relative;
@@ -130,6 +131,9 @@
background-color: #F6C738;
}
}
&.row__content--width90 {
width: 90px;
}
&.row__content--width200 {
width: 200px;
}
@@ -144,7 +148,7 @@
}
.row__contents {
display: flex;
flex-direction: column;
//flex-direction: column;
.row__content {
padding: 2px 0;
@@ -156,15 +160,26 @@
&:last-of-type {
padding-bottom: 0;
}
.el-popper {
min-width: 90px !important;
}
}
.row__content-accept {
margin-left: 39px;
}
.row__charts-msg {
width: auto;
padding-right: 20px;
padding-right: 10px;
}
.new-row__charts-msg {
width: auto;
padding-right: 10px;
}
.row__charts {
height: 20px;
width: 60px;
padding-left: 5px;
//padding-left: 5px;
}
}
.row__charts {
@@ -176,6 +191,12 @@
color: #666666;
}
}
.overview__row-related {
display: flex;
align-items: center;
margin-bottom: 8px;
}
}
.overview__content.domain__content {
@@ -292,3 +313,11 @@
}
}
}
.margin-l-140 {
margin-left: 140px;
}
.line-center {
display: flex;
align-items: center;
}

View File

@@ -1,5 +1,5 @@
.entity-list {
width: calc(100% - 290px);
width: 100%;
height: calc(100% - 42px);
flex: 1;
position: relative;

View File

@@ -1,9 +1,8 @@
.cn-entity--list {
display: flex;
//border: 1px #E2E5EC solid;
//margin-bottom: 10px;
//border-radius: 4px;
// 新版实体列表改版,后续记得解开
border: 1px #E2E5EC solid;
margin-bottom: 10px;
border-radius: 4px;
.cn-entity__collapse {
margin-bottom: 1px;
@@ -13,8 +12,8 @@
justify-content: center;
align-items: flex-start;
background-color: #F3F7FA;
//border-radius: 4px 0 0 4px;
// 新版实体列表改版,后续记得解开
border-radius: 4px 0 0 4px;
span {
transform: rotate(0);
transition: all linear .2s;
@@ -41,12 +40,12 @@
overflow: hidden;
display: flex;
flex-wrap: wrap;
//align-content: center;
align-content: center;
padding: 16px 0;
margin-bottom: 1px;
background-color: white;
//border-radius: 0 4px 4px 0;
// 新版实体列表改版,后续记得解开
border-radius: 0 4px 4px 0;
.cn-entity__icon {
margin-left: 26px;
margin-right: 10px;
@@ -77,13 +76,12 @@
font-size: 16px;
padding-bottom: 3px;
color: #333333;
//.cn-entity__header-title {
// margin-right: 10px;
//}
//.cn-entity__header-tag {
//
//}
// 新版实体列表改版,后续记得解开
.cn-entity__header-title {
margin-right: 10px;
}
.cn-entity__header-tag {
}
}
.cn-entity__body {
@@ -140,7 +138,7 @@
.row__charts {
height: 19px;
width: 60px;
padding-left: 5px;
margin-left: 4px;
}
}
@@ -162,31 +160,46 @@
color: #666;
}
}
}
.show-detail {
flex-shrink: 0;
padding: 0 30px;
font-size: 12px;
color: #3976CB;
//color: #2C72C6;
//font-weight: 400;
//margin-top: -17px;
// 新版实体列表改版,后续记得解开
&:hover {
cursor: pointer;
.row-item-label {
font-family: NotoSansSChineseRegular;
font-size: 14px;
color: #717171;
font-weight: 400;
}
//i {
// font-size: 12px;
// margin-right: 5px;
//}
.row-item-value {
font-family: NotoSansSChineseRegular;
font-size: 14px;
color: #353636;
font-weight: 400;
}
}
}
}
}
.new-show-detail {
flex-shrink: 0;
padding: 0 30px;
font-size: 12px;
color: #2C72C6;
font-weight: 400;
display: flex;
flex-direction: column;
justify-content: space-around;
align-items: center;
&:hover {
cursor: pointer;
}
i {
font-size: 12px;
margin-right: 5px;
}
}
.cn-entity__detail-overview {
flex-basis: 100%;
padding: 0 10px;

View File

@@ -682,8 +682,9 @@
}
.el-collapse {
width: 655px;
width: 855px;
margin-left: 5px;
padding-right: 200px;
border: none;
.el-collapse-item.upload-collapse {
@@ -721,6 +722,7 @@
.el-collapse-item__wrap {
padding-left: 35px;
border: none;
overflow: visible;
}
.el-collapse-item__content {
position: relative;
@@ -814,13 +816,20 @@
padding-left: 8px;
}
.form-select {
.form-select__disable {
width: 100%;
.el-input__inner {
background-color: #F5F8FA;
}
}
.form-select__enable {
width: 100%;
.el-input__inner {
background-color: white !important;
}
}
}
}
.skeleton-border {
@@ -866,6 +875,22 @@
&.imported-table-box--error {
border-color: $--color-danger;
}
.entity-format-tip {
position: absolute;
left: 100%;
padding-left: 10px;
width: 200px;
color: #353636;
div {
display: flex;
}
span {
padding-right: 10px;
color: #909399;
}
}
.imported-table {
padding: 0 12px;
width: 100%;

View File

@@ -1,8 +1,8 @@
@font-face {
font-family: "cn-icon"; /* Project id 2614877 */
src: url('iconfont.woff2?t=1687167070241') format('woff2'),
url('iconfont.woff?t=1687167070241') format('woff'),
url('iconfont.ttf?t=1687167070241') format('truetype');
src: url('iconfont.woff2?t=1689317280458') format('woff2'),
url('iconfont.woff?t=1689317280458') format('woff'),
url('iconfont.ttf?t=1689317280458') format('truetype');
}
.cn-icon {
@@ -13,6 +13,46 @@
-moz-osx-font-smoothing: grayscale;
}
.cn-icon-add-knowledge-base:before {
content: "\e802";
}
.cn-icon-update-knowledge-base:before {
content: "\e803";
}
.cn-icon-zoom-out:before {
content: "\e7fd";
}
.cn-icon-to-default:before {
content: "\e7fe";
}
.cn-icon-reset:before {
content: "\e7ff";
}
.cn-icon-next-step:before {
content: "\e800";
}
.cn-icon-pre-step:before {
content: "\e801";
}
.cn-icon-zoom-in:before {
content: "\e7f";
}
.cn-icon-expand-continue:before {
content: "\e7fc";
}
.cn-icon-domain1:before {
content: "\e7fb";
}
.cn-icon-expand-relationship:before {
content: "\e7f8";
}

File diff suppressed because one or more lines are too long

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1 @@
<?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="1687771200531" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7765" width="36" height="36" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M723.84487531 71.54166698c60.46291698 0 110.11458302 49.45145802 110.11458302 110.11458302v12.21270802h10.05045802c27.58870802 0 49.77179198 22.30320802 49.77179198 49.73175v256.78720896c0 27.42854198-22.34325 49.73175-49.77179198 49.73175h-10.05045802v292.22408302c0 60.50295802-49.4915 110.11458302-110.11458302 110.11458302H310.89516635a110.394875 110.394875 0 0 1-110.11458302-110.11458302v-292.22408302h-10.0905A49.81183302 49.81183302 0 0 1 140.95833333 500.38791698V243.60070802c0-27.42854198 22.30320802-49.73175 49.73175-49.73175h10.0905V181.65625C200.78058333 121.15329198 250.23204135 71.54166698 310.89516635 71.54166698z m54.97720802 688.31625H255.75779135v82.48583302a55.2575 55.2575 0 0 0 55.137375 55.137375h412.94970896a55.2575 55.2575 0 0 0 55.09733302-55.137375l-0.120125-82.48583302z m-261.47208302 27.508625c22.02291698 0 41.24291698 19.22 41.24291604 41.28295802 0 22.02291698-19.22 41.24291698-41.24291604 41.24291698-21.90279198 0-41.24291698-19.22-41.24291698-41.24291698s19.22-41.24291698 41.24291698-41.24291698z m261.59220802-237.246875H255.87791635v154.56083302h523.06429198v-154.56083302z m61.06354198-302.47475H194.73429135v248.65875h645.27145896v-248.65875z m-518.53958396 23.78475c12.41291698 0 23.5445 7.52783302 28.10925 18.97975l60.903375 150.55666604a24.865875 24.865875 0 0 1-23.10404104 34.27566698 25.106125 25.106125 0 0 1-23.26420896-15.8565l-6.56683302-16.697375H284.78800031l-6.32658396 16.53720802a25.14616698 25.14616698 0 1 1-46.76866604-18.41916604l61.70420802-150.55666698a30.43166698 30.43166698 0 0 1 28.10925-18.81958302z m217.82666698 0c40.722375 0 71.63454198 31.47275 71.63454198 69.6725 0 38.15970802-31.19245802 69.071875-74.4775 69.071875h-20.70154198v40.32195802a24.66566698 24.66566698 0 0 1-24.74575 24.74575 24.82583302 24.82583302 0 0 1-24.74575-24.74575V296.17541698a24.66566698 24.66566698 0 0 1 24.74575-24.74575z m190.878625 0c40.722375 0 71.67458302 31.47275 71.67458302 69.6725 0 38.15970802-31.2325 69.071875-74.4775 69.071875h-20.74158302v40.32195802a24.66566698 24.66566698 0 0 1-24.74575 24.74575 24.66566698 24.66566698 0 0 1-24.70570802-24.74575V296.17541698a24.66566698 24.66566698 0 0 1 24.70570802-24.74575zM321.10579135 348.30966698l-20.181 52.05416604H341.16666635l-20.02083302-52.05416604z m215.46420896-27.3885h-20.70154198v47.16908302h18.17891698c18.65941698 0 27.3885-11.57204198 27.3885-25.14616698 0-11.01145802-6.68695802-22.02291698-24.82583396-22.02291604z m190.7585 0h-20.70154198v47.16908302h18.138875c18.69945802 0 27.42854198-11.57204198 27.42854198-25.14616698 0-11.01145802-6.727-22.02291698-24.82583396-22.02291604z m-3.3635-194.40229198H311.01529135A55.2575 55.2575 0 0 0 255.87791635 181.65625v12.21270802h523.22445896V181.65625a55.2575 55.2575 0 0 0-55.137375-55.137375z" p-id="7766" fill="#E5A219"></path></svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@@ -0,0 +1 @@
<?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="1688290120782" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7911" xmlns:xlink="http://www.w3.org/1999/xlink" width="36" height="36"><path d="M723.84487531 71.54166698c60.46291698 0 110.11458302 49.45145802 110.11458302 110.11458302v12.21270802h10.05045802c27.58870802 0 49.77179198 22.30320802 49.77179198 49.73175v256.78720896c0 27.42854198-22.34325 49.73175-49.77179198 49.73175h-10.05045802v292.22408302c0 60.50295802-49.4915 110.11458302-110.11458302 110.11458302H310.89516635a110.394875 110.394875 0 0 1-110.11458302-110.11458302v-292.22408302h-10.0905A49.81183302 49.81183302 0 0 1 140.95833333 500.38791698V243.60070802c0-27.42854198 22.30320802-49.73175 49.73175-49.73175h10.0905V181.65625C200.78058333 121.15329198 250.23204135 71.54166698 310.89516635 71.54166698z m54.97720802 688.31625H255.75779135v82.48583302a55.2575 55.2575 0 0 0 55.137375 55.137375h412.94970896a55.2575 55.2575 0 0 0 55.09733302-55.137375l-0.120125-82.48583302z m-261.47208302 27.508625c22.02291698 0 41.24291698 19.22 41.24291604 41.28295802 0 22.02291698-19.22 41.24291698-41.24291604 41.24291698-21.90279198 0-41.24291698-19.22-41.24291698-41.24291698s19.22-41.24291698 41.24291698-41.24291698z m261.59220802-237.246875H255.87791635v154.56083302h523.06429198v-154.56083302z m61.06354198-302.47475H194.73429135v248.65875h645.27145896v-248.65875z m-518.53958396 23.78475c12.41291698 0 23.5445 7.52783302 28.10925 18.97975l60.903375 150.55666604a24.865875 24.865875 0 0 1-23.10404104 34.27566698 25.106125 25.106125 0 0 1-23.26420896-15.8565l-6.56683302-16.697375H284.78800031l-6.32658396 16.53720802a25.14616698 25.14616698 0 1 1-46.76866604-18.41916604l61.70420802-150.55666698a30.43166698 30.43166698 0 0 1 28.10925-18.81958302z m217.82666698 0c40.722375 0 71.63454198 31.47275 71.63454198 69.6725 0 38.15970802-31.19245802 69.071875-74.4775 69.071875h-20.70154198v40.32195802a24.66566698 24.66566698 0 0 1-24.74575 24.74575 24.82583302 24.82583302 0 0 1-24.74575-24.74575V296.17541698a24.66566698 24.66566698 0 0 1 24.74575-24.74575z m190.878625 0c40.722375 0 71.67458302 31.47275 71.67458302 69.6725 0 38.15970802-31.2325 69.071875-74.4775 69.071875h-20.74158302v40.32195802a24.66566698 24.66566698 0 0 1-24.74575 24.74575 24.66566698 24.66566698 0 0 1-24.70570802-24.74575V296.17541698a24.66566698 24.66566698 0 0 1 24.70570802-24.74575zM321.10579135 348.30966698l-20.181 52.05416604H341.16666635l-20.02083302-52.05416604z m215.46420896-27.3885h-20.70154198v47.16908302h18.17891698c18.65941698 0 27.3885-11.57204198 27.3885-25.14616698 0-11.01145802-6.68695802-22.02291698-24.82583396-22.02291604z m190.7585 0h-20.70154198v47.16908302h18.138875c18.69945802 0 27.42854198-11.57204198 27.42854198-25.14616698 0-11.01145802-6.727-22.02291698-24.82583396-22.02291604z m-3.3635-194.40229198H311.01529135A55.2575 55.2575 0 0 0 255.87791635 181.65625v12.21270802h523.22445896V181.65625a55.2575 55.2575 0 0 0-55.137375-55.137375z" p-id="7912" fill="#778391"></path></svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@@ -0,0 +1 @@
<?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="1688298522321" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7364" xmlns:xlink="http://www.w3.org/1999/xlink" width="36" height="36"><path d="M512 61.06201172a450.93798828 450.93798828 0 1 1 0 901.87597656 450.93798828 450.93798828 0 0 1 0-901.87597656z m0 45.09379883a405.84418945 405.84418945 0 1 0 0 811.6883789 405.84418945 405.84418945 0 0 0 0-811.6883789z" p-id="7365" fill="#38ACD2"></path><path d="M229.64344446 586.23133044l15.95626722-52.586307c4.64812962-15.19314186 8.18625866-30.6637832 10.54501134-46.41192491h0.69375048c3.1912538 18.3150199 6.17438209 31.77378466 10.12876124 46.48129978l15.05439124 52.51693213h46.82817591l49.11755284-149.36453686h-48.77067761l-13.73626514 59.24631408c-3.60750391 17.06626868-6.93750745 33.855037-9.22688438 50.99068056h-0.69375136a864.41343565 864.41343565 0 0 0-11.79376257-50.64380531l-15.95626721-59.59318933h-39.33566772l-17.06626869 61.39694129c-3.88500427 14.63814112-8.46375903 31.70440892-11.79376256 48.84005335h-0.69375049A890.56783897 890.56783897 0 0 0 199.81216237 497.84748474l-12.7650143-61.05006603h-50.78255506l46.55067554 149.36453685h46.82817591zM570.27506324 496.04373279c-3.53812904 17.20501843-6.86813258 33.99378674-9.1575104 51.06005543h-0.69375047c-3.1912538-17.13564355-7.49250818-33.855037-11.79376257-50.64380531l-15.95626721-59.59318933H493.26872999l-17.06626869 61.39694129c-3.88500427 14.63814112-8.46375903 31.77378466-11.79376256 48.84005335h-0.69375137a904.30410413 904.30410413 0 0 0-9.08813463-49.18692861l-12.83438917-61.05006603h-50.78255417l46.48129978 149.29516199h46.89755078l16.09501783-52.51693213c4.57875475-15.26251673 8.11688379-30.73315807 10.40626074-46.48130068h0.69375136c3.33000354 18.38439477 6.24375697 31.84315955 10.19813611 46.48130068l15.05439124 52.51693212h46.82817502l49.11755373-149.29516198H584.15007813l-13.73626514 59.24631408zM825.22846423 496.04373279c-3.60750391 17.20501843-6.93750745 33.99378674-9.15751039 51.06005543h-0.69375048c-3.26062867-17.13564355-7.56188305-33.855037-11.79376256-50.64380531l-16.02564208-59.59318933h-39.33566773l-16.99689381 61.39694129a710.40076917 710.40076917 0 0 0-11.79376257 48.84005335h-0.69375049a893.96721827 893.96721827 0 0 0-9.15751038-49.18692861l-12.76501342-61.05006603H645.89389464l46.55067554 149.29516199h46.82817591l16.09501784-52.51693213a328.14410573 328.14410573 0 0 0 10.47563559-46.48130068h0.69375136c3.26062867 18.38439477 6.24375697 31.84315955 10.12876036 46.48130068l15.05439124 52.51693212h46.82817591l49.1869286-149.29516198h-48.84005337l-13.73626426 59.24631408z" p-id="7366" fill="#38ACD2"></path><path d="M320.17791736 643.11889184c28.65190576 164.34955323 107.87824195 274.72529762 191.82208264 274.72529761 81.30758763 0 158.24454601-103.7157373 189.04707984-259.80965612l2.42812756-12.83438916 44.40004835 7.90875828C715.4077206 835.28784972 623.34699522 962.93798828 512 962.93798828c-109.26574291 0-200.07771705-123.00200805-233.93275317-299.56157396l-2.28937782-12.48751393 44.40004835-7.70063368zM512 61.06201172c109.40449353 0 200.14709193 123.14075868 234.00212804 299.83907432l2.28937782 12.48751393-44.40004834 7.70063369C675.10080202 216.67030556 595.94384069 106.15581055 512 106.15581055c-82.07071387 0-159.56267298 105.58886413-189.81020519 263.62528552l-2.28937782 12.69563854-44.40004834-7.63125792C307.06602784 190.65465198 399.7511288 61.06201172 512 61.06201172z" p-id="7367" fill="#38ACD2"></path><path d="M549.60129065 647.28139648V962.93798828h-60.14819006V647.28139648h60.14819006z m0-586.21938476v315.6565918h-60.14819006V61.06201172h60.14819006z" p-id="7368" fill="#38ACD2"></path><path d="M151.24960938 241.43720703h721.50078124v45.09379883H151.24960938V241.43720703z m0 496.03178711h721.50078124v45.09379883H151.24960938v-45.09379883z" p-id="7369" fill="#38ACD2"></path></svg>

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

@@ -0,0 +1 @@
<?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="1687771150463" class="icon" viewBox="0 0 1228 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7479" width="35.9765625" height="30" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M1099.86729167 250.4559668V115.17457031H125.84123698v135.28139649h974.02605469z m0 54.11255859H125.84123698v595.23814453h974.02605469V304.56852539zM71.72867839 61.06201172h1082.25117187v892.8572168H71.72867839V61.06201172z m324.31460117 504.74992185v44.34223492c0.1803752 10.31144837 2.1344401 18.12770712 5.89225608 23.50890074 3.72775374 5.38119362 11.15319987 8.05675843 22.27633663 8.05675844h99.65729541v44.34223581h-119.73906657c-20.14189651 0-34.15103728-4.65969284-41.99735827-13.9490154-7.81625876-9.28932256-11.7243877-23.32852556-11.72438769-42.14767035v-82.61183945c0-18.81914567 3.90812893-32.85834779 11.72438769-42.14767034 7.81625876-9.31938479 21.82539863-13.9490154 41.99735827-13.9490154h119.73906657v44.34223581h-99.65729541c-11.12313675 0-18.54858288 2.70562793-22.27633663 8.05675842-3.75781686 5.38119362-5.71188089 12.7465135-5.89225608 22.1560868z m327.98223044 64.15344416c0 18.12770712-3.72775374 32.01659717-11.21332523 41.66667012-7.4855706 9.62001071-21.40452288 14.43001562-41.7267946 14.43001563h-66.76888539c-20.17195963 0-34.18109951-4.65969284-41.99735738-13.9490154-7.81625876-9.28932256-11.7243877-23.32852556-11.7243877-42.14767035v-82.61183945c0-18.81914567 3.90812893-32.85834779 11.7243877-42.14767034 7.81625876-9.31938479 21.82539863-13.9490154 41.99735738-13.9490154h66.73882227c20.35233483 0 34.27128711 4.81000491 41.75685772 14.43001562 7.4855706 9.62001071 11.21332435 23.53896299 11.21332523 41.66667012v82.61183945zM678.39058559 611.20635742v-44.67292307c0-10.76238636-1.74362659-18.60870736-5.23088067-23.50890076-3.48725407-4.93025564-11.21332435-7.39538301-23.20827572-7.395383h-25.55315237c-11.12313675 0-18.57864512 2.70562793-22.30639886 8.05675842-3.72775374 5.38119362-5.6216933 12.98701406-5.62169418 22.84752534v44.31217268c0 9.86051038 1.89393956 17.4663317 5.62169418 22.84752444 3.72775374 5.38119362 11.18326211 8.05675843 22.30639886 8.05675932h25.55315237c11.99495049 0 19.63083405-2.46512737 22.93771293-7.39538301 3.30687888-4.90019251 5.14069306-12.62626367 5.50144346-23.14815036z m241.37207356-119.91944264c20.32227171 0 34.27128711 4.81000491 41.72679548 14.43001562 7.4855706 9.62001071 11.21332435 23.53896299 11.21332434 41.66667012v138.7085252h-45.63492441v-119.55869137c0-10.76238636-1.74362659-18.60870736-5.23088066-23.50890076-3.48725407-4.93025564-11.21332435-7.39538301-23.20827484-7.395383h-14.61039082v150.46297513h-46.17605l-0.24050055-150.46297513h-13.04713943c-11.12313675 0-18.54858288 2.70562793-22.30639886 8.05675842-3.72775374 5.38119362-5.59163105 12.98701406-5.59163105 22.84752534v119.55869137h-45.90548721v-138.7085252c0-18.81914567 3.90812893-32.85834779 11.72438769-42.14767035 7.81625876-9.31938479 21.82539863-13.9490154 41.99735828-13.94901539h115.28981204zM244.88886589 637.36076074h48.70130273v48.70130274H244.88886589v-48.70130274z" p-id="7480" fill="#38ACD2"></path></svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@@ -0,0 +1 @@
<?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="1688290376101" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8107" xmlns:xlink="http://www.w3.org/1999/xlink" width="36" height="36"><path d="M512 61.06201172a450.93798828 450.93798828 0 1 1 0 901.87597656 450.93798828 450.93798828 0 0 1 0-901.87597656z m0 45.09379883a405.84418945 405.84418945 0 1 0 0 811.6883789 405.84418945 405.84418945 0 0 0 0-811.6883789z" p-id="8108" fill="#778391"></path><path d="M229.64344446 586.23133044l15.95626722-52.586307c4.64812962-15.19314186 8.18625866-30.6637832 10.54501134-46.41192491h0.69375048c3.1912538 18.3150199 6.17438209 31.77378466 10.12876124 46.48129978l15.05439124 52.51693213h46.82817591l49.11755284-149.36453686h-48.77067761l-13.73626514 59.24631408c-3.60750391 17.06626868-6.93750745 33.855037-9.22688438 50.99068056h-0.69375136a864.41343565 864.41343565 0 0 0-11.79376257-50.64380531l-15.95626721-59.59318933h-39.33566772l-17.06626869 61.39694129c-3.88500427 14.63814112-8.46375903 31.70440892-11.79376256 48.84005335h-0.69375049A890.56783897 890.56783897 0 0 0 199.81216237 497.84748474l-12.7650143-61.05006603h-50.78255506l46.55067554 149.36453685h46.82817591zM570.27506324 496.04373279c-3.53812904 17.20501843-6.86813258 33.99378674-9.1575104 51.06005543h-0.69375047c-3.1912538-17.13564355-7.49250818-33.855037-11.79376257-50.64380531l-15.95626721-59.59318933H493.26872999l-17.06626869 61.39694129c-3.88500427 14.63814112-8.46375903 31.77378466-11.79376256 48.84005335h-0.69375137a904.30410413 904.30410413 0 0 0-9.08813463-49.18692861l-12.83438917-61.05006603h-50.78255417l46.48129978 149.29516199h46.89755078l16.09501783-52.51693213c4.57875475-15.26251673 8.11688379-30.73315807 10.40626074-46.48130068h0.69375136c3.33000354 18.38439477 6.24375697 31.84315955 10.19813611 46.48130068l15.05439124 52.51693212h46.82817502l49.11755373-149.29516198H584.15007813l-13.73626514 59.24631408zM825.22846423 496.04373279c-3.60750391 17.20501843-6.93750745 33.99378674-9.15751039 51.06005543h-0.69375048c-3.26062867-17.13564355-7.56188305-33.855037-11.79376256-50.64380531l-16.02564208-59.59318933h-39.33566773l-16.99689381 61.39694129a710.40076917 710.40076917 0 0 0-11.79376257 48.84005335h-0.69375049a893.96721827 893.96721827 0 0 0-9.15751038-49.18692861l-12.76501342-61.05006603H645.89389464l46.55067554 149.29516199h46.82817591l16.09501784-52.51693213a328.14410573 328.14410573 0 0 0 10.47563559-46.48130068h0.69375136c3.26062867 18.38439477 6.24375697 31.84315955 10.12876036 46.48130068l15.05439124 52.51693212h46.82817591l49.1869286-149.29516198h-48.84005337l-13.73626426 59.24631408z" p-id="8109" fill="#778391"></path><path d="M320.17791736 643.11889184c28.65190576 164.34955323 107.87824195 274.72529762 191.82208264 274.72529761 81.30758763 0 158.24454601-103.7157373 189.04707984-259.80965612l2.42812756-12.83438916 44.40004835 7.90875828C715.4077206 835.28784972 623.34699522 962.93798828 512 962.93798828c-109.26574291 0-200.07771705-123.00200805-233.93275317-299.56157396l-2.28937782-12.48751393 44.40004835-7.70063368zM512 61.06201172c109.40449353 0 200.14709193 123.14075868 234.00212804 299.83907432l2.28937782 12.48751393-44.40004834 7.70063369C675.10080202 216.67030556 595.94384069 106.15581055 512 106.15581055c-82.07071387 0-159.56267298 105.58886413-189.81020519 263.62528552l-2.28937782 12.69563854-44.40004834-7.63125792C307.06602784 190.65465198 399.7511288 61.06201172 512 61.06201172z" p-id="8110" fill="#778391"></path><path d="M549.60129065 647.28139648V962.93798828h-60.14819006V647.28139648h60.14819006z m0-586.21938476v315.6565918h-60.14819006V61.06201172h60.14819006z" p-id="8111" fill="#778391"></path><path d="M151.24960938 241.43720703h721.50078124v45.09379883H151.24960938V241.43720703z m0 496.03178711h721.50078124v45.09379883H151.24960938v-45.09379883z" p-id="8112" fill="#778391"></path></svg>

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

@@ -0,0 +1 @@
<?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="1687770782238" class="icon" viewBox="0 0 1109 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7446" width="35.7392578125" height="33" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M306.07165232 286.53100586h88.45900173v402.31184159H306.07165232V286.53100586z m254.70480646 258.08684225h92.9683825c25.1773713 0 46.59692575-3.68265995 64.25866333-10.97282467 17.66173758-7.36532077 32.01659717-16.9853306 43.06457788-29.01034421 11.12313675-12.02501272 19.1648645-25.85377829 24.27549445-41.48629492a152.94313406 152.94313406 0 0 0 0-95.44854056 111.6071521 111.6071521 0 0 0-24.27549445-41.18566989 120.25012991 120.25012991 0 0 0-43.06457788-29.01034333A167.59861869 167.59861869 0 0 0 653.74484128 286.53100586H472.31745703v402.31184159h88.45900175v-144.30015625z m0-189.39395508h68.7680432c10.14610474 0 19.84127148 0.75156302 29.31096924 2.25468995a68.39226126 68.39226126 0 0 1 24.80158936 8.79329077 47.72427072 47.72427072 0 0 1 17.13564355 18.33814456 64.48413233 64.48413233 0 0 1 6.46344479 30.96440824c0 12.77657663-2.1795339 23.07299344-6.46344479 30.96440911a47.6491138 47.6491138 0 0 1-17.21079959 18.33814456 68.09163623 68.09163623 0 0 1-24.80158936 8.71813474c-9.39454172 1.50312692-19.08970846 2.25468994-29.31096923 2.25468994H560.85161569V355.29904907z" p-id="7447" fill="#7E9F54"></path><path d="M997.43474439 61.06201172H101.72158671C79.2498442 61.06201172 61.06201172 80.60265817 61.06201172 104.65268362v814.69463276c0 24.05002634 18.18783249 43.5906719 40.65957499 43.5906719h895.71315768c22.47174337 0 40.65957499-19.54064646 40.65957497-43.5906719V104.65268362c0-24.05002634-18.18783249-43.5906719-40.65957497-43.5906719z m-20.36736551 835.73840524H122.0889531V126.44802001h854.97842578v770.35239695z" p-id="7448" fill="#7E9F54"></path></svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -0,0 +1 @@
<?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="1688290403407" class="icon" viewBox="0 0 1109 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8307" xmlns:xlink="http://www.w3.org/1999/xlink" width="38.98828125" height="36"><path d="M306.07165232 286.53100586h88.45900173v402.31184159H306.07165232V286.53100586z m254.70480646 258.08684225h92.9683825c25.1773713 0 46.59692575-3.68265995 64.25866333-10.97282467 17.66173758-7.36532077 32.01659717-16.9853306 43.06457788-29.01034421 11.12313675-12.02501272 19.1648645-25.85377829 24.27549445-41.48629492a152.94313406 152.94313406 0 0 0 0-95.44854056 111.6071521 111.6071521 0 0 0-24.27549445-41.18566989 120.25012991 120.25012991 0 0 0-43.06457788-29.01034333A167.59861869 167.59861869 0 0 0 653.74484128 286.53100586H472.31745703v402.31184159h88.45900175v-144.30015625z m0-189.39395508h68.7680432c10.14610474 0 19.84127148 0.75156302 29.31096924 2.25468995a68.39226126 68.39226126 0 0 1 24.80158936 8.79329077 47.72427072 47.72427072 0 0 1 17.13564355 18.33814456 64.48413233 64.48413233 0 0 1 6.46344479 30.96440824c0 12.77657663-2.1795339 23.07299344-6.46344479 30.96440911a47.6491138 47.6491138 0 0 1-17.21079959 18.33814456 68.09163623 68.09163623 0 0 1-24.80158936 8.71813474c-9.39454172 1.50312692-19.08970846 2.25468994-29.31096923 2.25468994H560.85161569V355.29904907z" p-id="8308" fill="#778391"></path><path d="M997.43474439 61.06201172H101.72158671C79.2498442 61.06201172 61.06201172 80.60265817 61.06201172 104.65268362v814.69463276c0 24.05002634 18.18783249 43.5906719 40.65957499 43.5906719h895.71315768c22.47174337 0 40.65957499-19.54064646 40.65957497-43.5906719V104.65268362c0-24.05002634-18.18783249-43.5906719-40.65957497-43.5906719z m-20.36736551 835.73840524H122.0889531V126.44802001h854.97842578v770.35239695z" p-id="8309" fill="#778391"></path></svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -15,9 +15,9 @@
allow-create
filterable
size="mini"
v-model="meta.column.name"
v-model="meta.column.label"
ref="columnSelect"
:placeholder="meta.column.name || ''"
:placeholder="meta.column.label || ''"
@blur="columnBlur(meta, index)"
@change="(value) => selectColumn(value, meta)"
>
@@ -25,7 +25,7 @@
v-for="(column, index) in columnList"
:key="index"
:label="column.label"
:value="column.name"
:value="column.label"
></el-option>
</el-select>
</div>
@@ -75,13 +75,20 @@
</div>
<div class="tag-search__add" @click="addCondition">{{$t('entities.advancedSearch.add')}}</div>
<div class="search__suffixes search__suffixes--tag-mode">
<div class="search__suffix" @click="changeMode">
<!-- 新版实体列表改版后续记得解开-->
<!-- <div class="search__suffix" style="margin-right: 12px" @click="changeMode">-->
<i class="cn-icon cn-icon-search-normal"></i>
<div class="search__suffix" style="margin-right: 12px">
<el-popover
popper-class="my-popper-class"
placement="top"
:popper-style="{border: '1px red solid'}"
trigger="hover"
:content="$t('entity.switchToBasicSearch')"
>
<template #reference>
<i class="cn-icon cn-icon-search-normal" @click="changeMode"></i>
</template>
</el-popover>
</div>
<div class="search__suffix" @click="search">
<!-- <div class="search__suffix new-search__suffix" @click="search">-->
<div class="search__suffix new-search__suffix" @click="search">
<i class="el-icon-search"></i>
</div>
</div>
@@ -157,6 +164,18 @@ export default {
} else {
this.metaList.splice(index - 1, 2)
}
if (this.metaList.length > 0) {
const parser = new Parser(this.columnList)
const errorList = parser.validateMeta(this.metaList)
if (_.isEmpty(errorList)) {
this.reloadUrl({ q: parser.parseMeta(this.metaList).q })
}
} else {
const routeQuery = this.$route.query
delete routeQuery.q
this.reloadUrl(routeQuery, 'cleanOldParams')
}
},
// 选择搜索条件的事件
selectColumn (value, meta) {
@@ -168,7 +187,7 @@ export default {
meta.resetValue()
} else {
const selectedColumn = this.columnList.find(column => {
return column.name === value
return column.label === value
})
meta.column.label = selectedColumn.label
meta.column.type = selectedColumn.type
@@ -194,7 +213,7 @@ export default {
},
columnBlur (meta, index) {
setTimeout(() => {
meta.column.name = meta.column.name.replace(/"/g, '')
meta.column.label = meta.column.label.replace(/"/g, '')
meta.column.isEditing = false
if (meta.isEmpty()) {
if (this.metaList.length > 1) {
@@ -295,7 +314,7 @@ export default {
// 判断是否是用户自己添加的内容,用于判断是否是全局搜索
isCustomized (value) {
return !this.columnList.some(meta => {
return meta.name === value
return meta.label === value
})
},
enterSearch () {
@@ -352,10 +371,10 @@ export default {
addParams (params) {
params.forEach(param => {
const column = this.columnList.find(column => {
return column.name === param.column
return column.label === param.column
})
const meta = new Meta()
meta.column.name = param.column
meta.column.label = param.column
meta.column.type = column ? column.type : columnType.string
meta.column.label = column ? column.label : param.column
meta.operator.value = '='
@@ -369,16 +388,16 @@ export default {
changeParams (params) {
params.forEach(param => {
const oldColumn = this.columnList.find(column => {
return column.name === param.oldParam.column
return column.label === param.oldParam.column
})
const newColumn = this.columnList.find(column => {
return column.name === param.newParam.column
return column.label === param.newParam.column
})
const meta = this.metaList.find(m => m.column && m.column.name === oldColumn.name && m.operator.value === param.oldParam.operator && m.value.value === this.handleValue(param.oldParam.value, oldColumn, param.oldParam.operator))
const meta = this.metaList.find(m => m.column && m.column.label === oldColumn.label && m.operator.value === param.oldParam.operator && m.value.value === this.handleValue(param.oldParam.value, oldColumn, param.oldParam.operator))
if (meta) {
meta.column.name = newColumn.name
meta.column.label = newColumn.label
meta.column.type = newColumn.type
meta.column.label = newColumn.label ? newColumn.label : newColumn.name
meta.column.label = newColumn.label ? newColumn.label : newColumn.label
meta.operator.value = param.newParam.operator
meta.value.value = this.handleValue(param.newParam.value, newColumn, param.newParam.operator)
meta.value.label = meta.value.value
@@ -388,9 +407,9 @@ export default {
removeParams (params) {
params.forEach(param => {
const column = this.columnList.find(c => {
return c.name === param.column
return c.label === param.column
})
const metaIndex = this.metaList.findIndex(m => m.column && m.column.name === param.column && m.operator.value === param.operator && m.value.value === this.handleValue(param.value, column, param.operator))
const metaIndex = this.metaList.findIndex(m => m.column && m.column.label === param.column && m.operator.value === param.operator && m.value.value === this.handleValue(param.value, column, param.operator))
// 不是在首位则删除时顺带删除前一个indexand或or否则顺带删除后一个index
if (metaIndex > 0) {
this.metaList.splice(metaIndex - 1, 2)
@@ -440,20 +459,22 @@ export default {
}
</script>
<!--// 新版实体列表改版,后续记得解开-->
<!--<style lang="scss">-->
<!--.new-search__suffix {-->
<!-- width: 41px;-->
<!-- height: 41px;-->
<!-- line-height: 41px;-->
<!-- background: #38ACD2;-->
<!-- text-align: center;-->
<!-- margin-top: -10px;-->
<!-- margin-right: -10px;-->
<style lang="scss">
.new-search__suffix {
width: 41px;
height: 41px;
line-height: 41px;
background: #38ACD2;
text-align: center;
margin-top: -10px;
margin-right: -10px;
<!-- .el-icon-search {-->
<!-- color: #fff !important;-->
<!-- margin-top: 9px !important;-->
<!-- }-->
<!--}-->
<!--</style>-->
.el-icon-search {
color: #fff !important;
margin-top: 9px !important;
}
}
.my-popper-class .el-popper__arrow {
display: none;
}
</style>

View File

@@ -3,14 +3,22 @@
ref="textSearch"
></textarea>
<div class="search__suffixes search__suffixes--text-mode">
<div class="search__suffix" @click="changeMode">
<i class="cn-icon cn-icon-search-advance"></i>
<div class="search__suffix">
<el-popover
popper-class="my-popper-class"
placement="top"
trigger="hover"
:content="$t('entity.switchToAdvancedSearch')"
>
<template #reference>
<i class="cn-icon cn-icon-filter" @click="changeMode"></i>
</template>
</el-popover>
</div>
<!-- <div class="search__suffix-close" @click="cleanParams">-->
<!-- <i class="el-icon-error"></i>-->
<!-- </div>-->
<!-- <div class="search__suffix new-search__suffix" @click="search">-->
<div class="search__suffix" @click="search">
<div class="search__suffix-close" @click="cleanParams">
<i class="el-icon-error"></i>
</div>
<div class="search__suffix new-search__suffix" @click="search">
<i class="el-icon-search"></i>
</div>
</div>
@@ -43,9 +51,12 @@ export default {
},
emits: ['changeMode', 'search'],
methods: {
// cleanParams () {
// toRaw(this.codeMirror).setValue('')
// },
cleanParams () {
toRaw(this.codeMirror).setValue('')
const routeQuery = this.$route.query
delete routeQuery.q
this.reloadUrl(routeQuery, 'cleanOldParams')
},
initCodeMirror () {
this.codeMirror = CodeMirror.fromTextArea(this.$refs.textSearch, {
mode: {
@@ -110,7 +121,7 @@ export default {
addParams (params) {
let current = this.codeMirror.getValue()
params.forEach(param => {
const column = this.columnList.find(c => c.name === param.column)
const column = this.columnList.find(c => c.label === param.column)
current = `${current ? current + ' AND ' : ''}${param.column}${handleOperatorSpace(param.operator)}${this.handleValue(param.value, column, param.operator)}`
})
toRaw(this.codeMirror).setValue(current.trim())
@@ -118,7 +129,7 @@ export default {
removeParams (params) {
let current = this.codeMirror.getValue()
params.forEach(param => {
const column = this.columnList.find(c => c.name === param.column)
const column = this.columnList.find(c => c.label === param.column)
// 将对应内容替换为空串
const sqlPiece = `${param.column}${handleOperatorSpace(param.operator)}${this.handleValue(param.value, column, param.operator)}`.trim()
const sqlPieceWithConnection = [` AND ${sqlPiece}`, ` OR ${sqlPiece}`, `${sqlPiece} AND `, `${sqlPiece} OR `, sqlPiece]
@@ -131,8 +142,8 @@ export default {
changeParams (params) {
let current = this.codeMirror.getValue()
params.forEach(param => {
const oldColumn = this.columnList.find(c => c.name === param.oldParam.column)
const newColumn = this.columnList.find(c => c.name === param.newParam.column)
const oldColumn = this.columnList.find(c => c.label === param.oldParam.column)
const newColumn = this.columnList.find(c => c.label === param.newParam.column)
// 将oldParam内容替换为newParam
const oldSqlPiece = `${param.oldParam.column}${handleOperatorSpace(param.oldParam.operator)}${this.handleValue(param.oldParam.value, oldColumn, param.oldParam.operator)}`.trim()
const newSqlPiece = `${param.newParam.column}${handleOperatorSpace(param.newParam.operator)}${this.handleValue(param.newParam.value, newColumn, param.newParam.operator)}`.trim()
@@ -166,9 +177,18 @@ export default {
},
mounted () {
// 如果地址栏包含参数q则将参数q回显到搜索栏内
const { q } = this.$route.query
let { q } = this.$route.query
this.initCodeMirror()
if (q) {
// 为避免地址栏任意输入导致全查询的q带QUERY解析时不识别导致的语法错误
// 如地址栏输入116.178.222.171此时的q很长刷新界面时需要把q里的116.178.222.171拿出来进行搜索
if (q.indexOf('QUERY') > -1) {
const strList = q.split(' ')
if (strList.length > 0) {
// 此时strList[1]为ip_addr:116.178.222.171获取116.178.222.171
q = strList[1].slice(8)
}
}
toRaw(this.codeMirror).setValue(q)
}
@@ -180,28 +200,28 @@ export default {
}
</script>
<!--<style lang="scss">-->
<!--.search__suffix-close {-->
<!-- .el-icon-error {-->
<!-- font-size: 17px;-->
<!-- color: #C4C4C4;-->
<!-- margin: 0 12px;-->
<!-- cursor: pointer;-->
<!-- }-->
<!--}-->
<style lang="scss">
.search__suffix-close {
.el-icon-error {
font-size: 17px;
color: #C4C4C4;
margin: 0 12px;
cursor: pointer;
}
}
<!--.new-search__suffix {-->
<!-- width: 41px;-->
<!-- height: 41px;-->
<!-- line-height: 41px;-->
<!-- background: #38ACD2;-->
<!-- text-align: center;-->
<!-- margin-top: -10px;-->
<!-- margin-right: -10px;-->
.new-search__suffix {
width: 41px;
height: 41px;
line-height: 41px;
background: #38ACD2;
text-align: center;
margin-top: -10px;
margin-right: -10px;
<!-- .el-icon-search {-->
<!-- color: #fff !important;-->
<!-- margin-top: 9px !important;-->
<!-- }-->
<!--}-->
<!--</style>-->
.el-icon-search {
color: #fff !important;
margin-top: 9px !important;
}
}
</style>

View File

@@ -63,7 +63,7 @@ export default class Meta {
isEmpty () {
if (this.meta === condition) {
return _.isEmpty(this.column.name)
return _.isEmpty(this.column.label)
} else {
return true
}
@@ -73,8 +73,8 @@ export default class Meta {
isCompleteCondition () {
if (this.meta === condition) {
return (this.column.type === columnType.fullText)
? !_.isEmpty(this.column.name)
: !_.isEmpty(this.column.name) && !_.isEmpty(this.operator.value) && (!_.isEmpty(this.value.value) || (_.isNumber(this.value.value) && !_.isNaN(this.value.value)))
? !_.isEmpty(this.column.label)
: !_.isEmpty(this.column.label) && !_.isEmpty(this.operator.value) && (!_.isEmpty(this.value.value) || (_.isNumber(this.value.value) && !_.isNaN(this.value.value)))
} else if (this.meta === connection) {
return !!this.value
}

View File

@@ -80,9 +80,9 @@ export default class Parser {
str += `${meta.value.toUpperCase()} `
} else if (meta.meta === condition) {
if (meta.column.type === columnType.fullText) {
str += `'${meta.column.name}' `
str += `'${meta.column.label}' `
} else if (meta.column.type === columnType.array) {
str += `${meta.column.name} ${meta.operator.value} (`
str += `${meta.column.label} ${meta.operator.value} (`
meta.value.value.forEach((s, j) => {
str += `'${s}'`
if (j < meta.value.value.length) {
@@ -93,13 +93,13 @@ export default class Parser {
str += ') '
} else if (meta.column.type === columnType.string) {
if (meta.operator.value.toLowerCase().indexOf('like') > -1 || meta.operator.value.toLowerCase().indexOf('in') > -1) {
str += `${meta.column.name} ${meta.operator.value} '${meta.value.value}' `
str += `${meta.column.label} ${meta.operator.value} '${meta.value.value}' `
} else {
str += `${meta.column.name}${meta.operator.value}'${meta.value.value}' `
str += `${meta.column.label}${meta.operator.value}'${meta.value.value}' `
}
} else if (meta.column.type === columnType.number) {
if (_.isNumber(Number(meta.value.value))) {
str += `${meta.column.name}${meta.operator.value}${meta.value.value} `
str += `${meta.column.label}${meta.operator.value}${meta.value.value} `
} else {
this.errorList.push(new ParserError(i, errorTypes.typeError, errorDesc.typeError.number))
return
@@ -125,11 +125,11 @@ export default class Parser {
if (meta.column.type === columnType.fullText) {
str += "QUERY('"
this.columnList.forEach(column => {
str += `${column.name}:${meta.column.name} `
str += `${column.label}:${meta.column.label} `
})
str += "') "
} else if (meta.column.type === columnType.array) {
str += `${meta.column.name} ${meta.operator.value} (`
str += `${meta.column.label} ${meta.operator.value} (`
meta.value.value.forEach((s, j) => {
str += `'${s}'`
if (j < meta.value.value.length) {
@@ -140,13 +140,13 @@ export default class Parser {
str += ') '
} else if (meta.column.type === columnType.string) {
if (meta.operator.value.toLowerCase().indexOf('like') > -1 || meta.operator.value.toLowerCase().indexOf('in') > -1) {
str += `${meta.column.name} ${meta.operator.value} '${meta.value.value}' `
str += `${meta.column.label} ${meta.operator.value} '${meta.value.value}' `
} else {
str += `${meta.column.name}${meta.operator.value}'${meta.value.value}' `
str += `${meta.column.label}${meta.operator.value}'${meta.value.value}' `
}
} else if (meta.column.type === columnType.number) {
if (_.isNumber(Number(meta.value.value))) {
str += `${meta.column.name}${meta.operator.value}${meta.value.value} `
str += `${meta.column.label}${meta.operator.value}${meta.value.value} `
}
}
}
@@ -566,12 +566,12 @@ export default class Parser {
// 前面是连接符或空后面是操作符不在单引号内则是key
// 前面是连接符或操作符或空后面是连接符或空或在单引号内是value
if (isInApostrophe) {
if (meta.column.name) {
if (meta.column.label) {
meta.value.value = token.value
meta.column.type = columnType.string
} else {
meta.column.type = columnType.fullText
meta.column.name = token.value
meta.column.label = token.value
}
} else {
let isColumn = true
@@ -579,14 +579,14 @@ export default class Parser {
if (prevToken) {
if (prevToken.type === types.connection && [types.commonOperator, types.letterOperator].indexOf(nextToken.type) > -1) {
meta.column.type = columnType.string
meta.column.name = token.value
meta.column.label = token.value
} else {
isColumn = false
}
} else {
if ([types.commonOperator, types.letterOperator].indexOf(nextToken.type) > -1) {
meta.column.type = columnType.string
meta.column.name = token.value
meta.column.label = token.value
} else {
isColumn = false
}
@@ -604,7 +604,7 @@ export default class Parser {
meta.column.type = columnType.string
} else if (prevToken && (!prevToken.prevToken || prevToken.prevToken.type === types.connection)) {
meta.column.type = columnType.fullText
meta.column.name = token.value
meta.column.label = token.value
} else {
errorList.push(new ParserError(token.end, errorTypes.syntaxError, errorDesc.syntaxError.unexpectedString))
break
@@ -631,14 +631,14 @@ export default class Parser {
meta.value.value = token.value
}
} else if (prevToken.type === types.connection) {
meta.column.name = token.value
meta.column.label = token.value
meta.column.type = columnType.fullText
} else {
errorList.push(new ParserError(token.end, errorTypes.syntaxError, errorDesc.syntaxError.unexpectedString))
break
}
} else {
meta.column.name = token.value
meta.column.label = token.value
meta.column.type = columnType.fullText
}
} else {
@@ -707,10 +707,10 @@ export default class Parser {
if (meta.column.type === columnType.fullText) {
meta.operator.show = false
meta.value.show = false
meta.column.label = meta.column.name
// meta.column.label = meta.column.name
metaList.push(meta)
} else {
const column = this.columnList.find(c => c.name === meta.column.name)
const column = this.columnList.find(c => c.label === meta.column.label)
if (column) {
meta.operator.show = true
meta.value.show = true

View File

@@ -26,6 +26,7 @@
style="position: absolute;top: -53px;left: -536px;"
:clearable="false"
:default-time="defaultTime"
:unlink-panels="true"
type="datetimerange"
@change="timeArrChange"
/>
@@ -251,6 +252,7 @@ export default {
* 重置时间,将时间存入缓存,并触发方法请求接口刷新界面
*/
const returnValue = () => {
store.commit('setTimeFilter', { startTime: myStartTime.value, endTime: myEndTime.value, range: dateRangeValue.value })
cancelHttp()
rangeHistory.value.unshift({
start: myStartTime.value,

View File

@@ -20,10 +20,7 @@ export default {
entityDetectionStyle () {
const route = this.$route.name !== undefined ? this.$route.name : this.$route
if (listScrollPath.indexOf(route.path) > -1) {
// 新版实体列表改版,后续记得解开
const style = route.path === listScrollPath[0] ? 'overflow:auto;background-color: #EFF2F5;' : 'overflow:auto;'
// const style = 'overflow:auto;'
return style
return 'overflow:auto;'
} else {
return ''
}

View File

@@ -192,26 +192,27 @@ import { get, put } from '@/utils/http'
import {
curTabState,
entityType,
fromRoute,
networkTable,
operationType,
storageKey,
wholeScreenRouterMapping,
fromRoute
wholeScreenRouterMapping
} from '@/utils/constants'
import { api } from '@/utils/api'
import { ref } from 'vue'
import {
combineDrilldownTableWithUserConfig,
combineTabList,
getDefaultCurTab,
getTabList,
overwriteUrl,
urlParamsHandler,
combineDrilldownTableWithUserConfig,
getDnsMapData,
handleSpecialValue
getTabList,
handleSpecialValue,
overwriteUrl,
urlParamsHandler
} from '@/utils/tools'
import { getNowTime, getSecond } from '@/utils/date-util'
import _ from 'lodash'
import { useRoute } from 'vue-router'
export default {
name: 'Header',
@@ -223,6 +224,13 @@ export default {
callback()
}
}
const validateFiveLength = (rule, value, callback) => {
if (value.length < 5) {
callback(new Error(this.$t('validate.atLeastFive')))
} else {
callback()
}
}
return {
username: localStorage.getItem(storageKey.username),
language: localStorage.getItem(storageKey.language) ? localStorage.getItem(storageKey.language) : 'en',
@@ -243,11 +251,17 @@ export default {
required: true,
message: this.$t('validate.required'),
trigger: 'blur'
}, {
validator: validateFiveLength,
trigger: 'change'
}],
newPwd2: [{
required: true,
message: this.$t('validate.required'),
trigger: 'blur'
}, {
validator: validateFiveLength,
trigger: 'change'
}, {
validator: passwordComparison,
trigger: 'blur'
@@ -359,6 +373,11 @@ export default {
this.dnsRcodeMapData = await getDnsMapData('dnsRcode')
}
}
},
'$store.getters.timeFilter': function (newVal) {
if (newVal && Object.keys(newVal).length > 0) {
this.chartTimeFilter = newVal
}
}
},
async mounted () {
@@ -375,16 +394,24 @@ export default {
this.initDropdownList()
},
setup () {
const dateRangeValue = 60
const {
startTime,
endTime
} = getNowTime(dateRangeValue)
const chartTimeFilter = ref({
startTime,
endTime,
dateRangeValue
})
const { query } = useRoute()
// 获取url携带的range、startTime、endTime
const rangeParam = query.range
const startTimeParam = query.startTime
const endTimeParam = query.endTime
// 若url携带了使用携带的值否则使用默认值。
const dateRangeValue = rangeParam ? parseInt(query.range) : 60
const chartTimeFilter = ref({ dateRangeValue })
if (!startTimeParam || !endTimeParam) {
const { startTime, endTime } = getNowTime(60)
chartTimeFilter.value.startTime = startTime
chartTimeFilter.value.endTime = endTime
} else {
chartTimeFilter.value.startTime = parseInt(startTimeParam)
chartTimeFilter.value.endTime = parseInt(endTimeParam)
}
return {
chartTimeFilter,
entityType // 所有entity类型用于header下拉框选择
@@ -592,6 +619,15 @@ export default {
this.showChangePin = false
} else if (res.code === 518005) {
this.$message.error('密码错误')
} else {
this.$message.error(res.message)
}
}).catch(e => {
console.error(e)
if (e.response.data && e.response.data.message) {
this.$message.error(e.response.data.message)
} else {
this.$message.error('Something went wrong...')
}
})
} else {

View File

@@ -376,7 +376,7 @@ export default {
this.$message({ duration: 2000, type: 'success', message: this.$t('tip.saveSuccess') })
this.esc(true)
} else {
this.$message.error(res.msg)
this.$message.error(res.msg || res.message)
}
})
} else {
@@ -386,7 +386,7 @@ export default {
this.$message({ duration: 2000, type: 'success', message: this.$t('tip.saveSuccess') })
this.esc(true)
} else {
this.$message.error(res.msg)
this.$message.error(res.msg || res.message)
}
})
}

View File

@@ -112,7 +112,7 @@ export default {
this.$message({ duration: 2000, type: 'success', message: this.$t('tip.saveSuccess') })
this.esc(true)
} else {
this.$message.error(res.msg)
this.$message.error(res.msg || res.message)
}
})
} else {
@@ -122,7 +122,7 @@ export default {
this.$message({ duration: 2000, type: 'success', message: this.$t('tip.saveSuccess') })
this.esc(true)
} else {
this.$message.error(res.msg)
this.$message.error(res.msg || res.message)
}
})
}

View File

@@ -64,6 +64,18 @@ export default {
detail: Boolean
},
data () {
const validateName = (rule, value, callback) => {
let validate = true
const reg = /^[a-zA-Z0-9\u4e00-\u9fa5\u30a1-\u30f6\u3041-\u3093\uFF00-\uFFFF\u4e00-\u9fa5\u0400-\u04FF\s]{2,64}$/
validate = reg.test(value)
if (value.length < 2) {
callback(new Error(this.$t('validate.atLeastTwo')))
} else if (!validate) {
callback(new Error(this.$t('validate.name')))
} else {
callback()
}
}
return {
editRole: {},
url: api.role,
@@ -71,7 +83,7 @@ export default {
rules: { // 表单校验规则
name: [
{ required: true, message: this.$t('validate.required'), trigger: 'blur' },
{ pattern: /^[a-zA-Z0-9]{2,64}$/, message: this.$t('validate.atLeastTwo') } // 目前仅对长度要求在2-64之间后续有需求再添加
{ validator: validateName, trigger: 'change' }
]
},
menus: [],
@@ -174,7 +186,7 @@ export default {
this.$message({ duration: 2000, type: 'success', message: this.$t('tip.saveSuccess') })
this.esc(true)
} else {
this.$message.error(res.msg)
this.$message.error(res.msg || res.message)
}
})
} else {
@@ -184,7 +196,7 @@ export default {
this.$message({ duration: 2000, type: 'success', message: this.$t('tip.saveSuccess') })
this.esc(true)
} else {
this.$message.error(res.msg)
this.$message.error(res.msg || res.message)
}
})
}

View File

@@ -132,17 +132,41 @@ export default {
callback()
}
}
const validateName = (rule, value, callback) => {
let validate = true
const reg = /^[a-zA-Z0-9\u4e00-\u9fa5\u30a1-\u30f6\u3041-\u3093\uFF00-\uFFFF\u4e00-\u9fa5\u0400-\u04FF\s]{2,64}$/
validate = reg.test(value)
if (value.length < 2) {
callback(new Error(this.$t('validate.atLeastTwo')))
} else if (!validate) {
callback(new Error(this.$t('validate.name')))
} else {
callback()
}
}
const validateUserName = (rule, value, callback) => {
let validate = true
const reg = /^[a-zA-Z0-9_]{2,64}$/
validate = reg.test(value)
if (value.length < 2) {
callback(new Error(this.$t('validate.atLeastTwo')))
} else if (!validate) {
callback(new Error(this.$t('validate.userName')))
} else {
callback()
}
}
return {
url: api.user,
loginName: localStorage.getItem(storageKey.username),
rules: { // 表单校验规则
name: [
{ required: true, message: this.$t('validate.required'), trigger: 'blur' },
{ pattern: /^[a-zA-Z0-9]{2,64}$/, message: this.$t('validate.atLeastTwo') }
{ validator: validateName, trigger: 'change' }
],
username: [
{ required: true, message: this.$t('validate.required'), trigger: 'blur' },
{ pattern: /^[a-zA-Z0-9]{2,64}$/, message: this.$t('validate.atLeastTwo') }
{ validator: validateUserName, trigger: 'change' }
],
pin: [
{ required: true, message: this.$t('validate.required'), trigger: 'blur' },
@@ -217,7 +241,7 @@ export default {
this.$message({ duration: 2000, type: 'success', message: this.$t('tip.saveSuccess') })
this.esc(true)
} else {
this.$message.error(res.msg)
this.$message.error(res.msg || res.message)
}
})
} else {
@@ -227,7 +251,7 @@ export default {
this.$message({ duration: 2000, type: 'success', message: this.$t('tip.saveSuccess') })
this.esc(true)
} else {
this.$message.error(res.msg)
this.$message.error(res.msg || res.message)
}
})
}

View File

@@ -58,7 +58,9 @@
</template>
<script>
import { storageKey } from '@/utils/constants'
import indexedDBUtils from '@/indexedDB'
import { storageKey, dbTableColumnCustomizeConfigPre } from '@/utils/constants'
import { get } from '@/utils/http'
export default {
props: {
customTableTitle: Array, // 自定义的title
@@ -70,15 +72,15 @@ export default {
return {
custom: [],
dragIndex: -1,
selectList: [],
lastIndex: -1
selectList: []
}
},
created () {
/*
const localStorageTitle = JSON.parse(localStorage.getItem(storageKey.tableTitle + '-' + localStorage.getItem(storageKey.username) + '-' + this.tableId))
if (localStorageTitle) {
localStorage.setItem(storageKey.tableTitle + '-' + localStorage.getItem(storageKey.username) + '-' + this.tableId, JSON.stringify(localStorageTitle))
}
} */
},
watch: {
customTableTitle: {
@@ -95,19 +97,13 @@ export default {
this.selectList = this.custom.filter(item => item.show)
// 最少保留一个选项
if (this.selectList.length === 1) {
let index = -1
this.custom.find((item, i) => {
index = i
return item.prop === this.selectList[0].prop
})
this.lastIndex = index
this.custom[index].disabled = true
} else if (this.lastIndex > -1) {
const obj = this.custom.find(item => item.prop === this.selectList[0].prop)
obj.disabled = true
} else if (this.selectList.length > 1) {
this.custom.forEach(item => {
// 该方案仅用于原始table列表无禁用的情况目前无原始列表禁用的情况后续有原始列表禁用的情况再修改
item.disabled = false
})
this.lastIndex = -1
}
this.save()
},
@@ -154,10 +150,30 @@ export default {
}
},
// 点击第二个cancel
save () {
async save () {
this.$emit('update', this.custom)
localStorage.setItem(storageKey.tableTitle + '-' + localStorage.getItem(storageKey.username) + '-' + this.tableId, JSON.stringify(this.custom))
// this.esc()
const userId = localStorage.getItem(storageKey.userId)
const tableName = dbTableColumnCustomizeConfigPre + '-' + this.tableId
const defaultConfigInDb = await indexedDBUtils.selectTable(tableName).get({ id: userId })
let fullVersion = ''
if (defaultConfigInDb && defaultConfigInDb.version) {
const oldVersion = defaultConfigInDb.version
if (oldVersion.startsWith(BASE_CONFIG.version)) {
const realVersion = Number(oldVersion.substring(BASE_CONFIG.version.length + 1))
fullVersion = BASE_CONFIG.version + '.' + (realVersion + 1)
} else {
fullVersion = BASE_CONFIG.version + '.1'
}
} else {
fullVersion = BASE_CONFIG.version + '.1'
}
await indexedDBUtils.selectTable(tableName).put({
id: userId,
version: fullVersion,
config: _.cloneDeep(this.custom)
})
}
},
computed: {

View File

@@ -33,7 +33,7 @@
<div class="col-resize-area"></div>
</template>
<template #default="scope" :column="item">
<span>{{scope.row[item.prop]}}</span>
<span>{{scope.row[item.prop] || '-'}}</span>
</template>
</el-table-column><template v-slot:empty >
<div class="table-no-data" v-if="isNoData">

View File

@@ -43,15 +43,15 @@
{{$t('operationLog.state.fail')}}
</template>
<template v-else>
{{scope.row[item.prop]}}
{{scope.row[item.prop] || '-'}}
</template>
</span>
<span v-else-if="item.prop === 'username'">{{formatUsername(scope.row)}}</span>
<span v-else-if="item.prop === 'ctime'">{{dateFormatByAppearance(scope.row[item.prop])}}</span>
<template v-else-if="item.prop === 'params' || item.prop === 'response'">
<span>{{scope.row[item.prop]}}</span>
<span>{{scope.row[item.prop] || '-'}}</span>
</template>
<span v-else>{{scope.row[item.prop]}}</span>
<span v-else>{{scope.row[item.prop] || '-'}}</span>
</template>
</el-table-column>
<template v-slot:empty >

View File

@@ -47,7 +47,7 @@
<span>-</span>
</template>
</template>
<span v-else>{{scope.row[item.prop]}}</span>
<span v-else>{{scope.row[item.prop] || '-'}}</span>
</template>
</el-table-column>
<template v-slot:empty >

View File

@@ -44,7 +44,7 @@
</template>
<template v-else-if="item.prop === 'lastLoginTime'">
<template v-if="scope.row[item.prop]">
{{dateFormatByAppearance(scope.row[item.prop])}}
{{dateFormatByAppearance(scope.row[item.prop]) || '-'}}
</template>
<template v-else>
<span>-</span>
@@ -60,7 +60,7 @@
@change="()=>{statusChange(scope.row)}">
</el-switch>
</template>
<span v-else>{{scope.row[item.prop]}}</span>
<span v-else>{{scope.row[item.prop] || '-'}}</span>
</template>
</el-table-column>
<template v-slot:empty >

View File

@@ -117,9 +117,7 @@
{{ handleTimeRange(scope.row) }}
</span>
<span v-else-if="item.prop === 'categoryId'">
<span v-for="(item, i) in categoryList" :key="i">
<span v-if="scope.row.categoryId === item.id">{{ item.name }}</span>
</span>
<span>{{ getCategoryName(scope.row.categoryId) }}</span>
</span>
<span v-else-if="item.prop === 'timePlan'">
<template v-if="scope.row.config && scope.row.config.isScheduler === 0">
@@ -768,6 +766,16 @@ export default {
}
}
this.configCustom = str
},
getCategoryName (id) {
let name = '-'
for (let i = 0; i < this.categoryList.length; i++) {
if (id === this.categoryList[i].id) {
name = this.categoryList[i].name
break
}
}
return name
}
},
beforeUnmount () {

View File

@@ -1,11 +1,18 @@
import { dbName, dbGeoDataTableName, dbDrilldownTableConfig } from '@/utils/constants'
import { dbName, dbGeoDataTableName, dbDrilldownTableConfig, dbUserTableColumnCustomizeConfig, dbRoleTableColumnCustomizeConfig, dbOperationLogTableColumnCustomizeConfig, dbChartTableColumnCustomizeConfig, dbI18nTableColumnCustomizeConfig, dbReportTableColumnCustomizeConfig, dbGalaxySettingTableColumnCustomizeConfig } from '@/utils/constants'
import Dexie from 'dexie'
/* https://dexie.org/ */
const db = new Dexie(dbName)
db.version(3).stores({
db.version(4).stores({
[dbGeoDataTableName]: '++name, geo',
[dbDrilldownTableConfig]: '++id, config',
[dbUserTableColumnCustomizeConfig]: '++id, config',
[dbRoleTableColumnCustomizeConfig]: '++id, config',
[dbOperationLogTableColumnCustomizeConfig]: '++id, config',
[dbChartTableColumnCustomizeConfig]: '++id, config',
[dbI18nTableColumnCustomizeConfig]: '++id, config',
[dbReportTableColumnCustomizeConfig]: '++id, config',
[dbGalaxySettingTableColumnCustomizeConfig]: '++id, config',
test: '++id, name'
})
function selectTable (tableName) {

View File

@@ -1,11 +1,12 @@
import { tableSort } from '@/utils/tools'
import { defaultPageSize, fromRoute, position, storageKey } from '@/utils/constants'
import { defaultPageSize, fromRoute, position, storageKey, dbTableColumnCustomizeConfigPre } from '@/utils/constants'
import { get, del } from '@/utils/http'
import { ref } from 'vue'
import pagination from '@/components/common/Pagination'
import axios from 'axios'
import { api } from '@/utils/api'
import Loading from '@/components/common/Loading'
import indexedDBUtils from '@/indexedDB'
export default {
components: {
@@ -104,14 +105,12 @@ export default {
if (isAll) {
this.searchLabel = null
} else if (isClearType) {
// this.searchLabel.tagType = ''
this.searchLabel.type = ''// 换新接口需要修改的属性名称
}
if (params) {
this.searchLabel = { ...this.searchLabel, ...params }
}
this.searchLabel = { ...this.searchLabel, ...this.pageObj }
//this.tableData = []
this.isNoData = false
this.toggleLoading(true)
delete this.searchLabel.total
@@ -123,6 +122,11 @@ export default {
if (response.code === 200) {
this.tableData = response.data.list
this.pageObj.total = response.data.total
if (!this.tableData || this.tableData.length === 0) {
this.isNoData = true
} else {
this.isNoData = false
}
} else {
console.error(response)
this.isNoData = true
@@ -132,13 +136,10 @@ export default {
this.$message.error('Something went wrong...')
}
}
}).catch(() => {
this.isNoData = true
}).finally(() => {
this.toggleLoading(false)
if (!this.tableData || this.tableData.length === 0) {
this.isNoData = true
} else {
this.isNoData = false
}
})
},
del (row) {
@@ -216,6 +217,7 @@ export default {
},
pageSize (val) {
this.pageObj.pageSize = val
this.pageObj.pageNo = 1
localStorage.setItem(storageKey.pageSize + '-' + localStorage.getItem(storageKey.username) + '-' + this.tableId, val)
this.getTableData()
},
@@ -261,8 +263,14 @@ export default {
if (this.$refs.dataTable.loadingTableId === u.id) { // 列表单个下载
return
}
if (u.state !== 1 || u.upload !== 1) {
return
if (localStorage.getItem(storageKey.s3Enable) == 1) {
if (u.state !== 1 || u.upload !== 1) {
return
}
} else {
if (u.state !== 1) {
return
}
}
let fileName = ''
let url = ''
@@ -323,8 +331,14 @@ export default {
if (this.$refs.dataTable.loadingPreviewId === u.id) { // 列表单个下载
return
}
if (u.state !== 1 || u.upload !== 1) {
return
if (localStorage.getItem(storageKey.s3Enable) == 1) {
if (u.state !== 1 || u.upload !== 1) {
return
}
} else {
if (u.state !== 1) {
return
}
}
const params = {
id: u.id
@@ -395,13 +409,21 @@ export default {
}
}
},
mounted () {
async mounted () {
const pageSize = localStorage.getItem(storageKey.pageSize + '-' + localStorage.getItem(storageKey.username) + '-' + this.tableId)
if (pageSize && pageSize !== 'undefined') {
this.pageObj.pageSize = pageSize
}
let localStorageTableTitle = localStorage.getItem(storageKey.tableTitle + '-' + localStorage.getItem(storageKey.username) + '-' + this.tableId)
localStorageTableTitle = localStorageTableTitle ? JSON.parse(localStorageTableTitle) : this.$refs.dataTable.tableTitle
const userId = localStorage.getItem(storageKey.userId)
const tableName = dbTableColumnCustomizeConfigPre + '-' + this.tableId
let localStorageTableTitle = []
if (indexedDBUtils.selectTable(tableName)) {
localStorageTableTitle = await indexedDBUtils.selectTable(tableName).get({ id: userId })
}
localStorageTableTitle = localStorageTableTitle && localStorageTableTitle.config
? localStorageTableTitle.config
: (this.$refs.dataTable && this.$refs.dataTable.tableTitle ? this.$refs.dataTable.tableTitle : [])
// this.tools.customTableTitle = this.$refs.dataTable.tableTitle.map((item, index) => { // 修复切换中英文的问题
// if (localStorageTableTitle[index]) {
// item.show = localStorageTableTitle[index].show

View File

@@ -19,7 +19,12 @@ export default {
this.loadingRelationshipOne = true
get(relationshipUrlOne, this.getQueryParams()).then(response => {
if (response.code === 200) {
const relationshipDataOne = response.data.result
const relationshipDataOne = []
if (response.data.result.length > 0) {
response.data.result.forEach(item => {
relationshipDataOne.push({ value: item, show: true })
})
}
// 将请求数据 传入方法中
this.relatedServerWidth(relationshipDataOne, refOne, 1)
}
@@ -30,7 +35,12 @@ export default {
this.loadingRelationshipTwo = true
get(relationshipUrlTow, this.getQueryParams()).then(response => {
if (response.code === 200) {
const relationshipDataTwo = response.data.result
const relationshipDataTwo = []
if (response.data.result.length > 0) {
response.data.result.forEach(item => {
relationshipDataTwo.push({ value: item, show: true })
})
}
// 将请求数据 传入方法中
this.relatedServerWidth(relationshipDataTwo, refTow, 2)
}
@@ -45,7 +55,7 @@ export default {
let flag = true
data.forEach((item) => {
// 每条数据的宽度
const width = getTextRect(item.appName || item.domain || item.ip).width + 67
const width = getTextRect(item.value).width + 67
if (width > 67 && width !== 0) {
sum += width
if (flag && sum >= relatedServerWidth && num === 1) {

View File

@@ -33,7 +33,7 @@ export default {
this.$message({ duration: 2000, type: 'success', message: this.$t('tip.saveSuccess') })
this.esc(true)
} else {
this.$message.error(res.msg)
this.$message.error(res.msg || res.message)
}
})
} else {
@@ -43,7 +43,7 @@ export default {
this.$message({ duration: 2000, type: 'success', message: this.$t('tip.saveSuccess') })
this.esc(true)
} else {
this.$message.error(res.msg)
this.$message.error(res.msg || res.message)
}
})
}

View File

@@ -451,6 +451,300 @@ if (openMock) {
}
}
})
Mock.mock(new RegExp(`${urlAndVersion}/entity/explorer/query/list.*`), 'get', function (requestObj) {
const result = {
pageNo: 1,
pageSize: 10,
total: 3,
list: [
{ entityValue: '192.168.12.34', entityType: 'ip' },
{ entityValue: 'gdbzkz.com', entityType: 'domain' },
{ entityValue: 'qqvideo', entityType: 'app' }
]
}
return {
msg: 'success',
code: 200,
data: result
}
})
Mock.mock(new RegExp(`${urlAndVersion}/entity/explorer/detail/basic.*`), 'get', function (requestObj) {
const entityType = getEntityType(requestObj.url)
let result = {}
switch (entityType) {
case ('domain'): {
result = {
whois: {
registrarName: 'Beijing Baidu Company',
registrantOrg: 'Beijing Baidu Netcom Science Technology Co., Ltd.',
registrantCountry: 'China',
email: '信息已设置隐私保护',
createDate: 1685329698,
expireDate: 1685329698
},
icp: {
icpSiteLicense: '京ICP证030173号',
icpCompanyName: '北京百度网讯科技有限公司',
icpCompanyType: '企业'
},
category: {
name: '门户网站',
group: '互联网',
reputationLevel: 'Trustworthy'
}
}
break
}
case ('ip'): {
result = {
asn: { id: 2, asn: '14061', organization: 'DIGITALOCEAN-ASN - DigitalOcean, LLC, US' },
location: {
continent: 'North America',
country: 'United States',
province: 'New York',
city: '',
lngwgs: '-74.006',
latwgs: '40.713',
isp: 'dba Omsoft',
owner: 'tie net'
}
}
break
}
case ('app'): {
result = {
category: {
appName: 'QQ',
appId: 333,
appCategory: '娱乐',
appSubcategory: '聊天',
appRisk: '1',
appDescription: '聊天社交软件',
appLongname: 'Tencent qq',
appTechnology: 'socket',
appCompany: 'tencent',
appCompanyCategory: '互联网'
}
}
break
}
}
return {
msg: 'success',
code: 200,
data: result
}
})
Mock.mock(new RegExp(`${urlAndVersion}/entity/explorer/detail/kb/intelligence/tag.*`), 'get', function (requestObj) {
const entityType = getEntityType(requestObj.url)
let result = {}
switch (entityType) {
case ('domain'): {
result = {
malware: {
threatType: 'command and control',
malwareName: '情报攻击',
malwareAlias: '攻击'
},
darkweb: {
nodeType: 'mtproxy'
},
userDefinedTags: [
{
id: 1,
tagValue: '门户网站'
}
]
}
break
}
case ('ip'): {
result = {
malware: {
threatType: 'command and control',
malwareName: 'IcedID',
malwareAlias: 'BokBot,IceID'
},
darkweb: {
nodeType: '12p'
},
psiphon3Ip: {
type: 'high',
method: 'passive_ml',
confidence: 88,
confidenceLevel: 'confirmed'
},
userDefinedTags: [
{
id: 1,
tagValue: '门户网站'
}
]
}
break
}
case ('app'): {
result = {
userDefinedTags: [
{
id: 1,
tagValue: '门户网站'
},
{
id: 2,
tagValue: '新闻软件'
}
]
}
break
}
}
return {
msg: 'success',
code: 200,
data: result
}
})
Mock.mock(new RegExp(`${urlAndVersion}/entity/explorer/detail/domain/relate.*`), 'get', function (requestObj) {
const relateType = getRelateType(requestObj.url)
let result = {}
switch (relateType) {
case ('ip'): {
result = {
total: 5,
result: ['bittorrent', 'qq_web', 'wechat', 'tencent', 'outlook']
}
break
}
case ('app'): {
result = {
total: 5,
result: ['192.107.175.180', '192.107.175.180', '192.107.175.180', '192.107.175.180', '192.107.175.180']
}
break
}
}
return {
msg: 'success',
code: 200,
data: result
}
})
Mock.mock(new RegExp(`${urlAndVersion}/entity/explorer/detail/ip/relate.*`), 'get', function (requestObj) {
const relateType = getRelateType(requestObj.url)
let result = {}
switch (relateType) {
case ('domain'): {
result = {
total: 5,
result: ['bittorrent', 'qq_web', 'wechat', 'tencent', 'outlook']
}
break
}
case ('app'): {
result = {
total: 5,
result: ['gdbzkz.com', 'gdbzkz.com', 'gdbzkz.com', 'gdbzkz.com', 'gdbzkz.com']
}
break
}
}
return {
msg: 'success',
code: 200,
data: result
}
})
Mock.mock(new RegExp(`${urlAndVersion}/entity/explorer/detail/app/relate.*`), 'get', function (requestObj) {
const relateType = getRelateType(requestObj.url)
let result = {}
switch (relateType) {
case ('ip'): {
result = {
total: 5,
result: ['gdbzkz.com', 'gdbzkz.com', 'gdbzkz.com', 'gdbzkz.com', 'gdbzkz.com']
}
break
}
case ('domain'): {
result = {
total: 5,
result: ['192.107.175.180', '192.107.175.180', '192.107.175.180', '192.107.175.180', '192.107.175.180']
}
break
}
}
return {
msg: 'success',
code: 200,
data: result
}
})
Mock.mock(new RegExp(`${urlAndVersion}/entity/explorer/detail/traffic/performance.*`), 'get', function (requestObj) {
const result = {
resultType: 'object',
result: {
httpResponseLatencyValue: null,
httpResponseLatencyP50: null,
httpResponseLatencyP90: null,
httpResponseLatencyP99: null,
sslConLatencyValue: 191,
sslConLatencyP50: 137,
sslConLatencyP90: 311,
sslConLatencyP99: 1610,
establishLatencyValue: 42,
establishLatencyP50: 33,
establishLatencyP90: 54,
establishLatencyP99: 177,
sequenceGapLossPercentValue: 0.001,
sequenceGapLossPercentP50: 0,
sequenceGapLossPercentP90: 0.0038,
sequenceGapLossPercentP99: 0.0087,
pktRetransPercentValue: 0.0124,
pktRetransPercentP50: 0.0096,
pktRetransPercentP90: 0.0183,
pktRetransPercentP99: 0.0769
}
}
return {
msg: 'success',
code: 200,
data: result
}
})
Mock.mock(new RegExp(`${urlAndVersion}/entity/explorer/overview/active.*`), 'get', function (requestObj) {
const data = { domainCount: 755, ipCount: 8373, appCount: 263 }
return {
msg: 'success',
code: 200,
data: data
}
})
Mock.mock(new RegExp(`${urlAndVersion}/entity/explorer/overview/new.*`), 'get', function (requestObj) {
const data = { domainCount: 262, ipCount: 4201, appCount: 43 }
return {
msg: 'success',
code: 200,
data: data
}
})
Mock.mock(new RegExp(`${urlAndVersion}/entity/explorer/overview/total.*`), 'get', function (requestObj) {
const data = { domainCount: 7686274, ipCount: 2169957, appCount: 856 }
return {
msg: 'success',
code: 200,
data: data
}
})
}
const getQuery = (url) => {
@@ -467,3 +761,31 @@ const getQuery = (url) => {
}
return result
}
const getEntityType = (url) => {
let entityType = ''
if (url.indexOf('/domain?') > -1) {
entityType = 'domain'
}
if (url.indexOf('/ip?') > -1) {
entityType = 'ip'
}
if (url.indexOf('/app?') > -1) {
entityType = 'app'
}
return entityType
}
const getRelateType = (url) => {
let entityType = ''
if (url.indexOf('/domains?') > -1) {
entityType = 'domain'
}
if (url.indexOf('/ips?') > -1) {
entityType = 'ip'
}
if (url.indexOf('/apps?') > -1) {
entityType = 'app'
}
return entityType
}

View File

@@ -13,7 +13,8 @@ const user = {
menuList: [],
buttonList: [],
roleList: [],
drilldownTableConfigList: []
drilldownTableConfigList: [],
timeFilter: {}
}
},
mutations: {
@@ -33,6 +34,9 @@ const user = {
state.menuList = []
state.buttonList = []
state.roleList = []
},
setTimeFilter (state, data) {
state.timeFilter = data
}
},
getters: {
@@ -47,6 +51,9 @@ const user = {
},
drilldownTableConfigList (state) {
return state.drilldownTableConfigList
},
timeFilter (state) {
return state.timeFilter
}
},
actions: {

View File

@@ -252,7 +252,58 @@ export const api = {
openPortOfApp: apiVersion + '/entity/detail/app/relate/ports',
basicInfo: apiVersion + '/entity/detail/basic',
tags: apiVersion + '/entity/detail/kb/intelligence/tag',
informationAggregation: apiVersion + '/entity/detail/kb/intelligence/list'
informationAggregation: apiVersion + '/entity/detail/kb/intelligence/list',
// 实体关系
entityGraph: {
basicInfo: apiVersion + '/entity/graph/relation/basic',
tags: apiVersion + '/entity/graph/relation/kb/intelligence/tag',
relatedEntityCount: apiVersion + '/entity/graph/relation/summaryCount',
domainRelatedIp: apiVersion + '/entity/graph/relation/domain/relate/ips',
domainRelatedApp: apiVersion + '/entity/graph/relation/domain/relate/apps',
domainRelatedSubdomain: apiVersion + '/entity/graph/relation/domain/relate/subdomains',
ipRelatedDomain: apiVersion + '/entity/graph/relation/ip/relate/domains',
ipRelatedApp: apiVersion + '/entity/graph/relation/ip/relate/apps',
appRelatedIp: apiVersion + '/entity/graph/relation/app/relate/ips',
appRelatedDomain: apiVersion + '/entity/graph/relation/app/relate/domains'
},
entityList: {
list: apiVersion + '/entity/explorer/query/list', // 实体列表
domainBasicInfo: apiVersion + '/entity/explorer/detail/basic/domain', // Domain实体响应结果
ipBasicInfo: apiVersion + '/entity/explorer/detail/basic/ip', // ip实体响应
appBasicInfo: apiVersion + '/entity/explorer/detail/basic/app', // app实体响应
domainTags: apiVersion + '/entity/explorer/detail/kb/intelligence/tag/domain', // Domain实体标签响应结果
ipTags: apiVersion + '/entity/explorer/detail/kb/intelligence/tag/ip', // ip实体标签响应结果
appTags: apiVersion + '/entity/explorer/detail/kb/intelligence/tag/app', // app实体标签响应结果
domainThroughput: apiVersion + '/entity/explorer/detail/traffic/throughput/domain', // 实体流量信息
ipThroughput: apiVersion + '/entity/explorer/detail/traffic/throughput/ip', // 实体流量信息
appThroughput: apiVersion + '/entity/explorer/detail/traffic/throughput/app', // 实体流量信息
domainPerformance: apiVersion + '/entity/explorer/detail/traffic/performance/domain', // domain网络质量
ipPerformance: apiVersion + '/entity/explorer/detail/traffic/performance/ip', // ip网络质量
appPerformance: apiVersion + '/entity/explorer/detail/traffic/performance/app', // app网络质量
domainRelatedApp: apiVersion + '/entity/explorer/detail/domain/relate/apps', // 域名相关app
domainRelatedIp: apiVersion + '/entity/explorer/detail/domain/relate/ips', // 域名相关ip
appRelatedDomain: apiVersion + '/entity/explorer/detail/app/relate/domains', // app相关域名
appRelatedIp: apiVersion + '/entity/explorer/detail/app/relate/ips', // app相关ip
ipRelatedApp: apiVersion + '/entity/explorer/detail/ip/relate/apps', // ip相关app
ipRelatedDomain: apiVersion + '/entity/explorer/detail/ip/relate/domains', // ip相关域名
ipRelatedPort: apiVersion + '/entity/explorer/detail/ip/relate/ports', // ip开放端口
domainTrafficMap: apiVersion + '/entity/explorer/detail/traffic/map/domain', // domain流量地图
ipTrafficMap: apiVersion + '/entity/explorer/detail/traffic/map/ip', // ip流量地图
appTrafficMap: apiVersion + '/entity/explorer/detail/traffic/map/app', // app流量地图
summaryCount: apiVersion + '/entity/explorer/query/summaryCount', // 实体基数统计
aggCountry: apiVersion + '/entity/explorer/top/aggCountry', // 国家实体基数统计
aggAsn: apiVersion + '/entity/explorer/top/aggAsn', // ASN实体基数统计
aggCity: apiVersion + '/entity/explorer/top/aggCity', // 城市实体基数统计
domainSecurity: apiVersion + '/entity/explorer/detail/event/security/domain', // domain安全事件详情
ipSecurity: apiVersion + '/entity/explorer/detail/event/security/domain', // ip安全事件详情
appSecurity: apiVersion + '/entity/explorer/detail/event/security/domain', // app安全事件详情
domainEventPerformance: apiVersion + '/entity/explorer/detail/event/performance/domain', // domain服务质量详情
ipEventPerformance: apiVersion + '/entity/explorer/detail/event/performance/ip', // ip服务质量详情
appEventPerformance: apiVersion + '/entity/explorer/detail/event/performance/app', // app服务质量详情
entityActive: apiVersion + '/entity/explorer/overview/active', // entity首页active数据概览
entityNew: apiVersion + '/entity/explorer/overview/new', // entity首页new数据概览
entityTotal: apiVersion + '/entity/explorer/overview/total' // entity首页total数据概览
}
}
}

View File

@@ -5,6 +5,14 @@ export const dbName = 'cn-db'
// indexedDB表名
export const dbGeoDataTableName = 'geodata'
export const dbDrilldownTableConfig = 'cn-drilldown-table-config'
export const dbTableColumnCustomizeConfigPre = 'cn-table-column-customize-config'
export const dbUserTableColumnCustomizeConfig = 'cn-table-column-customize-config-userTable'
export const dbRoleTableColumnCustomizeConfig = 'cn-table-column-customize-config-rolesTable'
export const dbOperationLogTableColumnCustomizeConfig = 'cn-table-column-customize-config-operationLogTable'
export const dbChartTableColumnCustomizeConfig = 'cn-table-column-customize-config-chartTable'
export const dbI18nTableColumnCustomizeConfig = 'cn-table-column-customize-config-i18nTable'
export const dbReportTableColumnCustomizeConfig = 'cn-table-column-customize-config-reportTable'
export const dbGalaxySettingTableColumnCustomizeConfig = 'cn-table-column-customize-config-galaxySettingTable'
export const storageKey = {
iso36112Capital: 'cn-iso3611-2-capital',
iso36112WorldLow: 'cn-iso3611-2-world-low',
@@ -97,6 +105,8 @@ export const entityDetailTabsName = {
performanceEvent: 'performanceEvent'
}
export const entityDetailRelatedEntitiesShowSize = 100
export const echartsFontSize = {
legendFirstFontSize: 12, // <1920
legendSecondFontSize: 14, // >=1920 && <2560

View File

@@ -272,179 +272,66 @@ export const dataForNpmNetworkQuantity = {
}
export const columnList = [
{
name: 'entity_type',
name: 'ip',
type: 'string',
label: 'Entity type'
label: 'IP',
doc: {
constraints: {
type: 'ip',
operator_functions: '=,in'
}
}
},
{
name: 'ip_addr',
name: 'fqdn',
type: 'string',
label: 'IP.Address'
},
{
name: 'ip_location_country',
type: 'string',
label: 'IP.Country'
},
{
name: 'ip_location_province',
type: 'string',
label: 'IP.Province'
},
{
name: 'ip_location_city',
type: 'string',
label: 'IP.City'
},
{
name: 'ip_asn',
type: 'string',
label: 'IP.ASN'
},
{
name: 'dns_server_role',
type: 'string',
label: 'IP.DNS server role'
},
{
name: 'dns_server_org',
type: 'string',
label: 'IP.DNS server organization'
},
{
name: 'dns_server_os',
type: 'string',
label: 'IP.Operating system'
},
{
name: 'dns_server_software',
type: 'string',
label: 'IP.DNS server software'
},
{
name: 'domain_name',
type: 'string',
label: 'Domain.Name'
},
{
name: 'domain_category',
type: 'string',
label: 'Domain.Category'
},
{
name: 'domain_category_group',
type: 'string',
label: 'Domain.Category group'
},
{
name: 'domain_reputation_level',
type: 'string',
label: 'Domain.Reputation'
},
{
name: 'domain_whois_email',
type: 'string',
label: 'Domain.Whois email'
},
{
name: 'domain_whois_name_servers',
type: 'string',
label: 'Domain.Whois nameserver'
},
{
name: 'domain_whois_registrar',
type: 'string',
label: 'Domain.Whois registrar'
},
{
name: 'domain_whois_org',
type: 'string',
label: 'Domain.Whois organization'
},
{
name: 'domain_whois_address',
type: 'string',
label: 'Domain.Whois address'
},
{
name: 'domain_whois_city',
type: 'string',
label: 'Domain.Whois city'
},
{
name: 'domain_whois_state',
type: 'string',
label: 'Domain.Whois state'
},
{
name: 'domain_whois_country',
type: 'string',
label: 'Domain.Whois country'
},
{
name: 'domain_icp_owner',
type: 'string',
label: 'Domain.ICP owner'
},
{
name: 'domain_icp_company_name',
type: 'string',
label: 'Domain.ICP company'
},
{
name: 'domain_icp_company_type',
type: 'string',
label: 'Domain.ICP company type'
},
{
name: 'domain_icp_site_license',
type: 'string',
label: 'Domain.ICP site license'
},
{
name: 'domain_icp_site_name',
type: 'string',
label: 'Domain.ICP site'
label: 'Domain',
doc: {
constraints: {
type: 'domain',
operator_functions: '=,in'
}
}
},
{
name: 'app_name',
type: 'string',
label: 'APP.Name'
label: 'App',
doc: {
constraints: {
operator_functions: '=,in'
}
}
},
{
name: 'app_id',
name: 'region',
type: 'string',
label: 'APP.ID'
label: 'City',
doc: {
constraints: {
operator_functions: '=,in'
}
}
},
{
name: 'app_category',
name: 'country',
type: 'string',
label: 'APP.Category'
label: 'Country',
doc: {
constraints: {
operator_functions: '=,in'
}
}
},
{
name: 'app_subcategory',
name: 'asn',
type: 'string',
label: 'APP.Subcategory'
},
{
name: 'app_risk',
type: 'string',
label: 'APP.Risk'
},
{
name: 'app_description',
type: 'string',
label: 'APP.Description'
},
{
name: 'app_longname',
type: 'string',
label: 'APP.Long name'
},
{
name: 'app_technology',
type: 'string',
label: 'APP.Technology'
label: 'ASN',
doc: {
constraints: {
operator_functions: '=,in'
}
}
}
]
export const operatorList = ['=', '!=', /* '>', '<', '>=', '<=', */'IN', 'NOT IN', 'LIKE', 'NOT LIKE']

View File

@@ -741,34 +741,38 @@ export function truncateText (text, limitWidth, fontSize = 12, ellipsis = '...')
}
export function scrollToTop (dom, toTop, duration, direction) {
const clientHeight = dom.clientHeight
const currentTop = dom.scrollTop
const totalScrollDistance = Math.abs(currentTop - toTop)
let scrollY = currentTop
let oldTimestamp = null
function step (newTimestamp) {
if (oldTimestamp !== null) {
if (direction === 'up') {
scrollY -= totalScrollDistance * (newTimestamp - oldTimestamp) / duration
if (scrollY < 0) {
dom.scrollTop = 0
return
if (toTop && duration && direction) {
const clientHeight = dom.clientHeight
const currentTop = dom.scrollTop
const totalScrollDistance = Math.abs(currentTop - toTop)
let scrollY = currentTop
let oldTimestamp = null
function step (newTimestamp) {
if (oldTimestamp !== null) {
if (direction === 'up') {
scrollY -= totalScrollDistance * (newTimestamp - oldTimestamp) / duration
if (scrollY < 0) {
dom.scrollTop = 0
return
}
dom.scrollTop = scrollY
} else if (direction === 'down') {
scrollY += totalScrollDistance * (newTimestamp - oldTimestamp) / duration
if (scrollY > clientHeight) {
dom.scrollTop = clientHeight
return
}
dom.scrollTop = scrollY
}
dom.scrollTop = scrollY
} else if (direction === 'down') {
scrollY += totalScrollDistance * (newTimestamp - oldTimestamp) / duration
if (scrollY > clientHeight) {
dom.scrollTop = clientHeight
return
}
dom.scrollTop = scrollY
}
oldTimestamp = newTimestamp
window.requestAnimationFrame(step)
}
oldTimestamp = newTimestamp
window.requestAnimationFrame(step)
} else {
const wraps = document.querySelector('.entity-graph__detail')
wraps.scrollTop = 0
}
window.requestAnimationFrame(step)
}
export function getChainRatio (current, prev) {

View File

@@ -50,6 +50,8 @@
<el-drawer
v-model="rightBox.show"
direction="rtl"
custom-class="common-right-box"
:size="700"
:with-header="false"
destroy-on-close>
<chart-box

View File

@@ -74,6 +74,8 @@
<el-drawer
v-model="rightBox.show"
direction="rtl"
custom-class="common-right-box"
:size="700"
:with-header="false"
destroy-on-close>
<galaxy-proxy-box :object="object" @close="closeRightBox"></galaxy-proxy-box>

View File

@@ -51,6 +51,8 @@
<el-drawer
v-model="rightBox.show"
direction="rtl"
custom-class="common-right-box"
:size="700"
:with-header="false"
destroy-on-close>
<i18n-box

View File

@@ -52,7 +52,9 @@
<el-drawer
v-model="rightBox.show"
direction="rtl"
custom-class="common-right-box"
:with-header="false"
:size="700"
destroy-on-close>
<role-box :object="object" @close="closeRightBox"></role-box>
</el-drawer>

View File

@@ -49,6 +49,8 @@
<el-drawer
v-model="rightBox.show"
direction="rtl"
custom-class="common-right-box"
:size="700"
:with-header="false"
destroy-on-close>
<user-box
@@ -104,7 +106,7 @@ export default {
}
this.searchLabel = { ...this.searchLabel, ...this.pageObj }
this.isNoData = false
//this.tableData = []
// this.tableData = []
this.toggleLoading(true)
delete this.searchLabel.total
let listUrl = this.url

View File

@@ -37,25 +37,15 @@ export default {
location () {
let location = ''
if (this.chartInfo) {
if (this.chartInfo.country) {
location = this.chartInfo.country
if (this.chartInfo.province) {
location += ', '
location += this.chartInfo.province
if (this.chartInfo.city) {
location += ', '
location += this.chartInfo.city
}
}
} else if (this.chartInfo.province) {
location = this.chartInfo.province
if (this.chartInfo.city) {
location += ', '
location += this.chartInfo.city
}
} else if (this.chartInfo.city) {
if (this.chartInfo.city) {
location = this.chartInfo.city
}
if (this.chartInfo.province) {
location = location ? `${this.chartInfo.province}, ${location}` : this.chartInfo.province
}
if (this.chartInfo.country) {
location = location ? `${this.chartInfo.country}, ${location}` : this.chartInfo.country
}
}
return location
},

View File

@@ -75,7 +75,7 @@ export default {
selector: '.entity-detail-info',
correctionHeight: 161,
chartType: typeMapping.entityDetail.basicInfo
}*/
} */
]
}
},

View File

@@ -1,3 +1,5 @@
import { entityDetailRelatedEntitiesShowSize } from '@/utils/constants'
export default {
props: {
chart: Object,
@@ -8,13 +10,30 @@ export default {
return {
isNoData: false,
showError: false,
errorMsg: ''
errorMsg: '',
entityDetailRelatedEntitiesShowSize: entityDetailRelatedEntitiesShowSize
}
},
emits: ['toggleLoading'],
methods: {
toggleLoading (loading) {
this.$emit('toggleLoading', loading)
},
showMore (showListInfo, allList) {
const showNum = showListInfo.num
if (showNum < allList.length) {
showListInfo.num = showNum + entityDetailRelatedEntitiesShowSize
if (showNum > allList.length) {
showListInfo.num = allList.length
}
}
},
handleShowDataNum (showListInfo, allList) {
if (allList.length <= entityDetailRelatedEntitiesShowSize) {
showListInfo.num = allList.length
} else {
showListInfo.num = entityDetailRelatedEntitiesShowSize
}
}
}
}

View File

@@ -288,6 +288,18 @@ export default {
}
this.myChart = echarts.init(document.getElementById('dnsLineChart'))
this.myChart.setOption(this.chartOption)
this.myChart.dispatchAction({
type: 'takeGlobalCursor',
key: 'brush',
brushOption: {
brushType: 'lineX',
xAxisIndex: 'all',
brushMode: 'single',
throttleType: 'debounce'
}
})
this.myChart.on('brushEnd', this.brushEcharts)
})
}
})
@@ -470,6 +482,29 @@ export default {
this.echartsInit(this.tabs, show)
if (!this.lineRefer) this.lineRefer = 'Average'
}
},
/**
* echarts框选
* @param params
*/
brushEcharts (params) {
this.myChart.dispatchAction({
type: 'brush',
areas: [] // 删除选框
})
// 避免点击空白区域报错
if (params.areas && params.areas.length > 0) {
const rangeObj = {
startTime: Math.ceil(params.areas[0].coordRange[0]),
endTime: Math.ceil(params.areas[0].coordRange[1])
}
// 暂定框选最小范围为5分钟后续可能会变动
if (rangeObj.endTime - rangeObj.startTime < 5 * 60 * 1000) {
rangeObj.startTime = rangeObj.endTime - 5 * 60 * 1000
}
this.$store.commit('setRangeEchartsData', rangeObj)
}
}
},
mounted () {

View File

@@ -9,7 +9,7 @@
</div>
<el-popover
placement="bottom-end"
:width="390"
:width="450"
trigger="click"
popper-class="analysis-popper"
@show="analysisPopSwitch(true)"
@@ -254,6 +254,17 @@ export default {
label: i18n.global.t('dns.dnsInsights'),
path: '/panel/dnsServiceInsights'
}
const graphItem = {
icon: 'cn-icon cn-icon-graph',
label: i18n.global.t('entities.graph'),
url: resolvePath({
path: '/entityGraph',
query: {
entityType: props.entity.entityType,
entityName: props.entity.entityName
}
})
}
switch (props.entity.entityType) {
case 'ip': {
const queryCondition = `common_client_ip='${props.entity.entityName}' OR common_server_ip='${props.entity.entityName}'`
@@ -379,6 +390,7 @@ export default {
break
}
}
analysisItems.value.push(graphItem)
// 底部各属性
const detailCards = ref([])
switch (props.entity.entityType) {

View File

@@ -10,14 +10,16 @@
<template #label>
<!--<div class="el-tabs__active-bar is-top" style="width: 80.9998px; transform: translateX(177px);"></div>-->
<i :class="tab.icon"></i>{{tab.label}}
<i v-if="tab.warnFlag" :class="tab.warnIcon" class="tab-pane-warn--icon"></i>
<el-tag size="small" style="margin-left: 6px" :color="tab.name === activeTab ? 'rgb(223,237,248)' : 'rgb(237,237,237)'" round>
<span :style="{color: tab.name === activeTab ? '#046ECA' : '#717171'}">{{ tab.tag }}</span>
</el-tag>
</template>
<information-aggregation v-if="tab.name === entityDetailTabsName.informationAggregation && tab.name === activeTab" @toggleLoading="setLoading" :entity="entity"></information-aggregation>
<domain-name-resolution v-else-if="tab.name === entityDetailTabsName.relatedEntity && tab.name === activeTab" @toggleLoading="setLoading" :entity="entity" :timeFilter="oneDayTimeFilter"></domain-name-resolution>
<digital-certificate v-else-if="tab.name === entityDetailTabsName.digitalCertificate && tab.name === activeTab" @toggleLoading="setLoading" :timeFilter="oneDayTimeFilter" />
<security-event v-else-if="tab.name === entityDetailTabsName.securityEvent && tab.name === activeTab" @toggleLoading="setLoading" :timeFilter="oneDayTimeFilter" @checkWarn="setWarn" />
<performance-event v-else-if="tab.name === entityDetailTabsName.performanceEvent && tab.name === activeTab" @toggleLoading="setLoading" :timeFilter="oneDayTimeFilter" @checkWarn="setWarn" />
<open-port v-else-if="tab.name === entityDetailTabsName.openPort && tab.name === activeTab" @toggleLoading="setLoading" :entity="entity" :timeFilter="oneDayTimeFilter"></open-port>
<information-aggregation v-if="tab.name === entityDetailTabsName.informationAggregation && tab.name === activeTab" @toggleLoading="setLoading" :entity="entity" @checkTag="setTag"></information-aggregation>
<domain-name-resolution v-else-if="tab.name === entityDetailTabsName.relatedEntity && tab.name === activeTab" @toggleLoading="setLoading" :entity="entity" :timeFilter="oneDayTimeFilter" @checkTag="setTag"></domain-name-resolution>
<digital-certificate v-else-if="tab.name === entityDetailTabsName.digitalCertificate && tab.name === activeTab" @toggleLoading="setLoading" :timeFilter="oneDayTimeFilter" @checkTag="setTag" />
<security-event v-else-if="tab.name === entityDetailTabsName.securityEvent && tab.name === activeTab" @toggleLoading="setLoading" :timeFilter="oneDayTimeFilter" @checkTag="setTag" />
<performance-event v-else-if="tab.name === entityDetailTabsName.performanceEvent && tab.name === activeTab" @toggleLoading="setLoading" :timeFilter="oneDayTimeFilter" @checkTag="setTag" />
<open-port v-else-if="tab.name === entityDetailTabsName.openPort && tab.name === activeTab" @toggleLoading="setLoading" :entity="entity" :timeFilter="oneDayTimeFilter" @checkTag="setTag"></open-port>
</el-tab-pane>
</el-tabs>
</div>
@@ -26,7 +28,7 @@
<script>
import chartMixin from '@/views/charts2/chart-mixin'
import i18n from '@/i18n'
import { entityDetailTabsName } from '@/utils/constants'
import { entityDetailTabsName, entityDetailTags, psiphon3IpType } from '@/utils/constants'
import { reactive, ref } from 'vue'
import InformationAggregation from '@/views/charts2/charts/entityDetail/tabs/InformationAggregation'
import DomainNameResolution from '@/views/charts2/charts/entityDetail/tabs/DomainNameResolution'
@@ -36,7 +38,6 @@ import OpenPort from '@/views/charts2/charts/entityDetail/tabs/OpenPort'
import DigitalCertificate from '@/views/charts2/charts/entityDetail/tabs/DigitalCertificate'
import { overwriteUrl, urlParamsHandler } from '@/utils/tools'
import { useRoute } from 'vue-router'
import { getSecond } from '@/utils/date-util'
import axios from 'axios'
import { api } from '@/utils/api'
@@ -80,14 +81,14 @@ export default {
const entityType = props.entity.entityType
const tabs = reactive([
{ name: entityDetailTabsName.relatedEntity, label: i18n.global.t('entities.relatedEntity'), icon: 'cn-icon cn-icon-domain-name-resolution' },
{ name: entityDetailTabsName.openPort, label: i18n.global.t('entities.openPort'), icon: 'cn-icon cn-icon-open-port' },
// { name: entityDetailTabsName.digitalCertificate, label: i18n.global.t('entities.digitalCertificate'), icon: 'cn-icon cn-icon-digital-certificate' },
{ name: entityDetailTabsName.securityEvent, label: i18n.global.t('overall.securityEvent'), icon: 'cn-icon cn-icon-security-event', warnIcon: 'cn-icon cn-icon-warn', warnFlag: false },
{ name: entityDetailTabsName.performanceEvent, label: i18n.global.t('overall.performanceEvent'), icon: 'cn-icon cn-icon-a-PerformanceEvent', warnIcon: 'cn-icon cn-icon-warn', warnFlag: false }
{ name: entityDetailTabsName.relatedEntity, label: i18n.global.t('entities.relatedEntity'), icon: 'cn-icon cn-icon-domain-name-resolution', tag: 0 },
{ name: entityDetailTabsName.openPort, label: i18n.global.t('entities.openPort'), icon: 'cn-icon cn-icon-open-port', tag: 0 },
// { name: entityDetailTabsName.digitalCertificate, label: i18n.global.t('entities.digitalCertificate'), icon: 'cn-icon cn-icon-digital-certificate', tag: 0 },
{ name: entityDetailTabsName.securityEvent, label: i18n.global.t('overall.securityEvent'), icon: 'cn-icon cn-icon-security-event', tag: 30 },
{ name: entityDetailTabsName.performanceEvent, label: i18n.global.t('overall.performanceEvent'), icon: 'cn-icon cn-icon-a-PerformanceEvent', tag: 0 }
])
if (entityType !== 'app') {
tabs.unshift({ name: entityDetailTabsName.informationAggregation, label: i18n.global.t('entities.informationAggregation'), icon: 'cn-icon cn-icon-information-aggregation' })
tabs.unshift({ name: entityDetailTabsName.informationAggregation, label: i18n.global.t('entities.informationAggregation'), icon: 'cn-icon cn-icon-information-aggregation', tag: 0 })
}
const activeTab = ref(tabs[0].name)
@@ -107,32 +108,107 @@ export default {
methods: {
initData () {
const params = {
resource: this.entity.entityName,
startTime: getSecond(this.oneDayTimeFilter.startTime),
endTime: getSecond(this.oneDayTimeFilter.endTime)
resource: this.entity.entityName
// startTime: getSecond(this.oneDayTimeFilter.startTime),
// endTime: getSecond(this.oneDayTimeFilter.endTime)
}
const url = this.getUrlByEntityType(this.entity.entityType)
const informationAggregation = axios.get(`${api.entity.informationAggregation}/${this.entity.entityType}?resource=${this.entity.entityName}&pageSize=100&pageNo=1`, { params: params })
const openPort = axios.get(url, { params: params })
const security = axios.get(`${api.entity.security}/${this.entity.entityType}`, { params: params })
const performance = axios.get(`${api.entity.performance}/${this.entityType}`, { params: params })
Promise.all([security, performance]).then(response => {
if (response[0].data.code === 200 && response[1].data.code === 200) {
const name1 = entityDetailTabsName.securityEvent
const name2 = entityDetailTabsName.performanceEvent
Promise.all([informationAggregation, openPort, security, performance]).then(response => {
if (response[0].data.code === 200) {
const list = []
response[0].data.data.result.forEach(r => {
Object.keys(r).forEach(k => {
const aggregation = {
createTime: r[k].createTime,
updateTime: r[k].updateTime,
status: r[k].isValid,
intelligenceContent: []
}
if (k === 'userDefinedTag') {
aggregation.intelligenceContent.push({ key: k, value: r[k].tagValue, type: 'normal' })
} else {
Object.keys(r[k]).forEach(k2 => {
const find = entityDetailTags[this.entity.entityType].find(t => t.name === k2)
if (find) {
aggregation.intelligenceContent.push({ key: k2, value: this.tagValueHandler(k, k2, r[k][k2]), type: find.type })
}
})
}
if (aggregation.intelligenceContent.length > 0) {
list.push(aggregation)
}
})
})
// 为了避免tabs其中某项被注释或删除使用name查找到该项进行更改避免icon显示错误
const obj1 = this.tabs.find(t => t.name === name1)
const obj2 = this.tabs.find(t => t.name === name2)
if (obj1) {
obj1.warnFlag = response[0].data.data.result.length > 0
this.initSetTag(entityDetailTabsName.informationAggregation, list.length)
}
if (response[1].data.code === 200) {
this.initSetTag(entityDetailTabsName.openPort, response[1].data.data.result.length)
}
if (response[2].data.code === 200) {
this.initSetTag(entityDetailTabsName.securityEvent, response[2].data.data.result.length)
}
if (response[3].data.code === 200) {
this.initSetTag(entityDetailTabsName.performanceEvent, response[3].data.data.result.length)
}
})
// 域名解析
if (this.entity.entityType === 'app') {
const ipsOfApp = axios.get(api.entity.domainNameResolutionAboutIpsOfApp, { params: params })
const domainsOfApp = axios.get(api.entity.domainNameResolutionAboutDomainsOfApp, { params: params })
this.promiseData(ipsOfApp, domainsOfApp)
}
if (this.entity.entityType === 'ip') {
const appsOfIp = axios.get(api.entity.domainNameResolutionAboutAppsOfIp, { params: params })
const domainsOfIp = axios.get(api.entity.domainNameResolutionAboutDomainsOfIp, { params: params })
this.promiseData(appsOfIp, domainsOfIp)
}
if (this.entity.entityType === 'domain') {
const appsOfDomain = axios.get(api.entity.domainNameResolutionAboutAppsOfDomain, { params: params })
const ipsOfDomain = axios.get(api.entity.domainNameResolutionAboutIpsOfDomain, { params: params })
const fqdnsOfDomain = axios.get(api.entity.domainNameResolutionAboutFQDNsOfDomain, { params: params })
this.promiseData(appsOfDomain, ipsOfDomain, fqdnsOfDomain)
}
},
promiseData (data1, data2, data3) {
Promise.all([data1, data2, data3]).then(res => {
const res0 = res[0].data
const res1 = res[1].data
switch (this.entity.entityType) {
case 'ip': {
const len1 = res0.code === 200 ? res0.data.result.length : 0
const len2 = res1.code === 200 ? res1.data.result.length : 0
this.initSetTag(entityDetailTabsName.relatedEntity, len1 + len2)
break
}
if (obj2) {
obj2.warnFlag = response[1].data.data.result.length > 0
case 'domain': {
const res2 = res[2].data
const len1 = res0.code === 200 ? res0.data.result.length : 0
const len2 = res1.code === 200 ? res1.data.result.length : 0
const len3 = res2.code === 200 ? res2.data.result.length : 0
this.initSetTag(entityDetailTabsName.relatedEntity, len1 + len2 + len3)
break
}
case 'app': {
const len1 = res0.code === 200 ? res0.data.result.length : 0
const len2 = res1.code === 200 ? res1.data.result.length : 0
this.initSetTag(entityDetailTabsName.relatedEntity, len1 + len2)
break
}
}
})
},
handleActiveBar (name, change) {
handleActiveBar (name, num) {
const tabDom = document.getElementById('tab-' + name)
if (tabDom) {
const { query } = this.$route
@@ -143,26 +219,46 @@ export default {
const clientWidth = tabDom.clientWidth
const clientLeft = tabDom.clientLeft
const activeBar = document.querySelector('.entity-detail-tabs .entity-detail-tabs__active-bar')
const addWidth = change === 'add' ? 30 : 0
const reduceWidth = change === 'reduce' ? -30 : 0
activeBar.style.cssText += `width: ${clientWidth + 2 + addWidth + reduceWidth}px; left: ${offsetLeft + clientLeft - 1}px;`
const newWidth = num ? num * 6 : 0
activeBar.style.cssText += `width: ${clientWidth + 2 + newWidth}px; left: ${offsetLeft + clientLeft - 1}px;`
}
},
setLoading (loading) {
this.toggleLoading(loading)
},
setWarn (name, flag) {
setTag (name, num) {
const obj = this.tabs.find(t => t.name === name)
if (obj) {
const oldFlag = JSON.parse(JSON.stringify(obj.warnFlag))
obj.warnFlag = flag
if (oldFlag && !flag) {
this.handleActiveBar(name, 'reduce') // 之前有warn再次切换无数据
}
if (!oldFlag && flag) {
this.handleActiveBar(name, 'add') // 之前无warn再次切换有数据
const oldNum = JSON.parse(JSON.stringify(obj.tag))
obj.tag = num
// 根据前后位数的变化来增减状态栏的长短,
// 如初始是0位数是1点击tab变成10条位数是2所以状态栏宽度增加(2-1)*6px
this.handleActiveBar(name, JSON.stringify(num).length - JSON.stringify(oldNum).length)
}
},
initSetTag (name, num) {
const obj = this.tabs.find(t => t.name === name)
if (obj) {
obj.tag = num
}
},
getUrlByEntityType (type) {
switch (type) {
case 'ip': return api.entity.openPortOfIp
case 'domain': return api.entity.openPortOfDomain
case 'app': return api.entity.openPortOfApp
}
},
tagValueHandler (k, k2, value) {
if (k === 'psiphon3Ip') {
if (k2 === 'type') {
const find = psiphon3IpType.find(t => t.value === value)
if (find) {
return find.name
}
}
}
return value
}
},
beforeUnmount () {
@@ -170,3 +266,10 @@ export default {
}
}
</script>
<style scoped>
.el-tag {
font-size: 12px !important;
border-radius: 20px !important;
}
</style>

View File

@@ -1,4 +1,4 @@
<template>
<template xmlns="http://www.w3.org/1999/html">
<div>
<chart-no-data v-if="isNoData"></chart-no-data>
@@ -8,11 +8,16 @@
<span class="title-mark"></span>
{{ $t('entities.tab.relatedApp') }}&nbsp;&nbsp;({{ relatedAppList.length }})
</div>
<div class="type-content" v-if="!showError0">
<div v-for="(entity, index) in relatedAppList" class="data-item" :key="index">
{{ entity.appName ? entity.appName : entity }}
<template v-if="!showError0">
<div class="type-content" >
<div v-for="(entity, index) in relatedAppList.slice(0,showRelatedAppListInfo.num)" class="data-item" :key="index">
{{ entity.appName ? entity.appName : entity }}
</div>
</div>
</div>
<div class="more" v-if="relatedAppList.length > entityDetailRelatedEntitiesShowSize">
<span class="button" :style="{'opacity': relatedAppList.length > showRelatedAppListInfo.num ? 1 : 0.6}" @click="showMore(showRelatedAppListInfo,relatedAppList)">{{ $t('overall.more') }} > </span>
</div>
</template>
<div class="type-error-content" v-else>
<chart-error class="type-error-content__block" :content="errorMsg0"></chart-error>
</div>
@@ -23,11 +28,16 @@
<span class="title-mark"></span>
{{ $t('entities.tab.relatedIp') }}&nbsp;&nbsp;({{ relatedIpList.length }})
</div>
<div class="type-content" v-if="!showError1">
<div v-for="(entity, index) in relatedIpList" :key="index" class="data-item">
{{ entity.ip ? entity.ip : entity }}
<template v-if="!showError1">
<div class="type-content" >
<div v-for="(entity, index) in relatedIpList.slice(0,showRelatedIpListInfo.num)" :key="index" class="data-item">
{{ entity.ip ? entity.ip : entity }}
</div>
</div>
</div>
<div class="more" v-if="relatedIpList.length > entityDetailRelatedEntitiesShowSize">
<span class="button" :style="{'opacity': relatedIpList.length > showRelatedIpListInfo.num ? 1 : 0.6}" @click="showMore(showRelatedIpListInfo,relatedIpList)">{{ $t('overall.more') }} > </span>
</div>
</template>
<div class="type-error-content" v-else>
<chart-error class="type-error-content__block" :content="errorMsg1"></chart-error>
</div>
@@ -38,11 +48,16 @@
<span class="title-mark"></span>
{{ $t('entities.tab.relatedDomain') }}&nbsp;&nbsp;({{ relatedDomainList.length }})
</div>
<div class="type-content" v-if="!showError2">
<div v-for="(entity, index) in relatedDomainList" class="data-item" :key="index">
{{ entity.domain ? entity.domain : entity }}
<template v-if="!showError2">
<div class="type-content">
<div v-for="(entity, index) in relatedDomainList.slice(0,showRelatedDomainListInfo.num)" class="data-item" :key="index">
{{ entity.domain ? entity.domain : entity }}
</div>
</div>
</div>
<div class="more" v-if="relatedDomainList.length > entityDetailRelatedEntitiesShowSize">
<span class="button" :style="{'opacity': relatedDomainList.length > showRelatedDomainListInfo.num ? 1 : 0.6}" @click="showMore(showRelatedDomainListInfo,relatedDomainList)">{{ $t('overall.more') }} > </span>
</div>
</template>
<div class="type-error-content" v-else>
<chart-error class="type-error-content__block" :content="errorMsg2"></chart-error>
</div>
@@ -56,8 +71,8 @@
import axios from 'axios'
import { api } from '@/utils/api'
import chartMixin from '@/views/charts2/chart-mixin'
import { getSecond } from '@/utils/date-util'
import chartNoData from '@/views/charts/charts/ChartNoData'
import { entityDetailTabsName } from '@/utils/constants'
export default {
name: 'DomainNameResolution',
@@ -69,9 +84,18 @@ export default {
},
data () {
return {
relatedAppList: [],
relatedIpList: [],
relatedDomainList: [],
relatedAppList: [], // 所有数据
showRelatedAppListInfo: {
num: 0
}, // 界面展示的数量
relatedIpList: [], // 所有数据
showRelatedIpListInfo: {
num: 0
}, // 界面展示的数量
relatedDomainList: [], // 所有数据
showRelatedDomainListInfo: {
num: 0
}, // 界面展示的数量
initFlag: false, // 初始化标识,请求接口之后再显示,避免标题初始化会闪一下
showError0: false,
errorMsg0: '',
@@ -87,9 +111,9 @@ export default {
methods: {
initData () {
const params = {
resource: this.entity.entityName,
startTime: getSecond(this.timeFilter.startTime),
endTime: getSecond(this.timeFilter.endTime)
resource: this.entity.entityName
// startTime: getSecond(this.timeFilter.startTime),
// endTime: getSecond(this.timeFilter.endTime)
}
if (this.entity.entityType === 'app') {
const ipsOfApp = axios.get(api.entity.domainNameResolutionAboutIpsOfApp, { params: params })
@@ -126,6 +150,7 @@ export default {
if (this.entity.entityType === 'app' && !this.isNoData) {
if (res0.code === 200) {
this.relatedIpList = res0.data.result
this.handleShowDataNum(this.showRelatedIpListInfo, this.relatedIpList)
} else {
this.showError1 = true
this.errorMsg1 = this.errorMsgHandler(res0)
@@ -133,26 +158,35 @@ export default {
if (res1.code === 200) {
this.relatedDomainList = res1.data.result
this.handleShowDataNum(this.showRelatedDomainListInfo, this.relatedDomainList)
} else {
this.showError2 = true
this.errorMsg2 = this.errorMsgHandler(res1)
}
this.$emit('checkTag', entityDetailTabsName.relatedEntity, res0.data.result.length + res1.data.result.length)
} else {
this.$emit('checkTag', entityDetailTabsName.relatedEntity, 0)
}
// ip相关显示appdomain
if (this.entity.entityType === 'ip' && !this.isNoData) {
if (res0.code === 200) {
this.relatedAppList = res0.data.result
this.handleShowDataNum(this.showRelatedAppListInfo, this.relatedAppList)
} else {
this.showError0 = true
this.errorMsg0 = this.errorMsgHandler(res0)
}
if (res1.code === 200) {
this.relatedDomainList = res1.data.result
this.handleShowDataNum(this.showRelatedDomainListInfo, this.relatedDomainList)
} else {
this.showError2 = true
this.errorMsg2 = this.errorMsgHandler(res1)
}
this.$emit('checkTag', entityDetailTabsName.relatedEntity, res0.data.result.length + res1.data.result.length)
} else {
this.$emit('checkTag', entityDetailTabsName.relatedEntity, 0)
}
// domain相关显示appipdomain
@@ -164,6 +198,7 @@ export default {
if (res0.code === 200) {
this.relatedAppList = res0.data.result
this.handleShowDataNum(this.showRelatedAppListInfo, this.relatedAppList)
} else {
this.showError0 = true
this.errorMsg0 = this.errorMsgHandler(res0)
@@ -171,6 +206,7 @@ export default {
if (res1.code === 200) {
this.relatedIpList = res1.data.result
this.handleShowDataNum(this.showRelatedIpListInfo, this.relatedIpList)
} else {
this.showError1 = true
this.errorMsg1 = this.errorMsgHandler(res1)
@@ -178,13 +214,16 @@ export default {
if (res2.code === 200) {
this.relatedDomainList = res2.data.result
this.handleShowDataNum(this.showRelatedDomainListInfo, this.relatedDomainList)
} else {
this.showError2 = true
this.errorMsg2 = this.errorMsgHandler(res2)
}
this.$emit('checkTag', entityDetailTabsName.relatedEntity, res0.data.result.length + res1.data.result.length + res2.data.result.length)
}
}).catch(e => {
console.log(e)
this.$emit('checkTag', entityDetailTabsName.relatedEntity, 0)
this.showError0 = true
this.showError1 = true
this.showError2 = true

View File

@@ -3,7 +3,6 @@
<!--初步方案error提示替换table后续若改为table内error提示记得在此修改-->
<chart-error v-if="showError" :content="errorMsg" class="entity-detail-event-error"></chart-error>
<chart-no-data v-if="isNoData && !showError" test-id="no-data"></chart-no-data>
<div v-if="!isNoData && !showError && !loading" class="information-aggregation__table">
<el-table
style="width: 100%"
@@ -40,32 +39,12 @@
prop="intelligenceContent"
>
<template #default="scope">
<div class="intelligence-content">
<!-- <div class="information-aggregation-tags">
<template v-for="ic in scope.row.intelligenceContent" >
<template v-if="ic.type === 0" >
<div v-if="ic.threatLevel === -1" class="entity-tag entity-tag--small entity-tag--level-one-negative margin-r-6">
{{ic.value}}
</div>
<div v-else-if="ic.threatLevel === 0" class="entity-tag entity-tag--small entity-tag--level-one-normal margin-r-6">
{{ic.value}}
</div>
<div v-else-if="ic.threatLevel === 1" class="entity-tag entity-tag--small entity-tag--level-one-positive margin-r-6">
{{ic.value}}
</div>
</template>
</template>
</div>-->
<div class="information-aggregation-tags">
<div v-for="ic in scope.row.intelligenceContent" :key="ic.key">
<div>
<div class="entity-tag entity-tag--small margin-r-6" :test-id="`entity-tag${scope.$index}`" :class="`entity-tag--level-two-${ic.type}`">
{{ic.value}}
</div>
</div>
<div v-for="ic in scope.row.intelligenceContent" :key="ic.key"
class="entity-tag entity-tag--small margin-r-6" :test-id="`entity-tag${scope.$index}`" :class="`entity-tag--level-two-${ic.type}`">
{{ic.value}}
</div>
</div>
</div>
</template>
</el-table-column>
<el-table-column
@@ -92,7 +71,7 @@
import chartMixin from '@/views/charts2/chart-mixin'
import axios from 'axios'
import { api } from '@/utils/api'
import { entityDetailTags, psiphon3IpType } from '@/utils/constants'
import { entityDetailTabsName, entityDetailTags, psiphon3IpType } from '@/utils/constants'
import { dateFormatByAppearance } from '@/utils/date-util'
import chartNoData from '@/views/charts/charts/ChartNoData'
@@ -126,14 +105,15 @@ export default {
},
getData () {
this.loading = true
this.showError = false
this.toggleLoading(true)
this.informationAggregationList = []
axios.get(`${api.entity.informationAggregation}/${this.entity.entityType}?resource=${this.entity.entityName}&pageSize=100&pageNo=1`).then(response => {
const res = response.data
if (res.code === 200) {
this.isNoData = res.data.result.length === 0
// this.isNoData = res.data.result.length === 0
this.showError = false
if (!this.isNoData) {
if (res.data.result.length > 0) {
res.data.result.forEach(r => {
Object.keys(r).forEach(k => {
const aggregation = {
@@ -157,18 +137,26 @@ export default {
}
})
})
this.$emit('checkTag', entityDetailTabsName.informationAggregation, this.informationAggregationList.length)
} else {
this.$emit('checkTag', entityDetailTabsName.informationAggregation, 0)
}
} else {
this.showError = true
this.isNoData = false
this.errorMsg = this.errorMsgHandler(res)
this.$emit('checkTag', entityDetailTabsName.informationAggregation, 0)
}
}).catch(e => {
console.error(e)
this.showError = true
this.isNoData = false
this.errorMsg = this.errorMsgHandler(e)
this.$emit('checkTag', entityDetailTabsName.informationAggregation, 0)
}).finally(() => {
if (this.informationAggregationList.length > 0) {
this.isNoData = false
} else {
this.isNoData = true
}
this.loading = false
this.toggleLoading(false)
})

View File

@@ -10,10 +10,13 @@
<span class="type-title-word">{{ $t('entities.tab.currentDevelopmentPortsAndServices') }}</span>({{ openPortList.length }})
</div>
<div class="type-content">
<div v-for="(openPort, index) in openPortList" :key="index" class="data-item">
<div v-for="(openPort, index) in openPortList.slice(0,showOpenPortListInfo.num)" :key="index" class="data-item">
{{ openPort.port }}/{{ openPort.l7Protocol }}
</div>
</div>
<div class="more" v-if="openPortList.length > entityDetailRelatedEntitiesShowSize">
<span class="button" :style="{'opacity': openPortList.length > showOpenPortListInfo.num ? 1 : 0.6}" @click="showMore(showOpenPortListInfo,openPortList)">{{ $t('overall.more') }} > </span>
</div>
</div>
</div>
</div>
@@ -22,8 +25,8 @@
import axios from 'axios'
import chartMixin from '@/views/charts2/chart-mixin'
import { api } from '@/utils/api'
import { getSecond } from '@/utils/date-util'
import chartNoData from '@/views/charts/charts/ChartNoData'
import { entityDetailTabsName } from '@/utils/constants'
export default {
name: 'OpenPort',
@@ -33,6 +36,9 @@ export default {
data () {
return {
openPortList: [],
showOpenPortListInfo: {
num: 0
},
showError: false,
errorMsg: '',
initFlag: false // 初始化标识,请求接口之后再显示,避免标题初始化会闪一下
@@ -49,9 +55,9 @@ export default {
methods: {
initData () {
const params = {
resource: this.entity.entityName,
startTime: getSecond(this.timeFilter.startTime),
endTime: getSecond(this.timeFilter.endTime)
resource: this.entity.entityName
// startTime: getSecond(this.timeFilter.startTime),
// endTime: getSecond(this.timeFilter.endTime)
}
this.toggleLoading(true)
@@ -60,9 +66,11 @@ export default {
const res = response.data
if (res.code === 200) {
this.isNoData = res.data.result.length === 0
this.$emit('checkTag', entityDetailTabsName.openPort, res.data.result.length)
this.showError = false
if (!this.isNoData) {
this.openPortList = res.data.result
this.handleShowDataNum(this.showOpenPortListInfo, this.openPortList)
}
} else {
this.httpError(res)
@@ -76,6 +84,7 @@ export default {
})
},
httpError (e) {
this.$emit('checkTag', entityDetailTabsName.openPort, 0)
this.isNoData = false
this.showError = true
this.errorMsg = this.errorMsgHandler(e)

View File

@@ -50,7 +50,7 @@
</template>
<script>
import { getSecond, dateFormatByAppearance } from '@/utils/date-util'
import { dateFormatByAppearance } from '@/utils/date-util'
import { eventSeverityColor, entityDetailTabsName } from '@/utils/constants'
import unitConvert from '@/utils/unit-convert'
import axios from 'axios'
@@ -76,7 +76,7 @@ export default {
setup () {
const { query } = useRoute()
const entityType = query.entityType
const entityName = query.name || query.entityName
const entityName = query.entityName
return {
entityType,
@@ -92,9 +92,9 @@ export default {
dateFormatByAppearance,
initData () {
const params = {
resource: this.entityName,
startTime: getSecond(this.timeFilter.startTime),
endTime: getSecond(this.timeFilter.endTime)
resource: this.entityName
// startTime: getSecond(this.timeFilter.startTime),
// endTime: getSecond(this.timeFilter.endTime)
}
this.toggleLoading(true)
@@ -103,7 +103,7 @@ export default {
if (res.code === 200) {
this.isNoData = res.data.result.length === 0
this.$emit('checkWarn', entityDetailTabsName.performanceEvent, !this.isNoData)
this.$emit('checkTag', entityDetailTabsName.performanceEvent, res.data.result.length)
this.showError = false
if (!this.isNoData) {
this.eventList = res.data.result
@@ -122,6 +122,7 @@ export default {
this.isNoData = false
this.showError = true
this.errorMsg = this.errorMsgHandler(e)
this.$emit('checkTag', entityDetailTabsName.performanceEvent, 0)
}
}
}

View File

@@ -76,7 +76,7 @@
</template>
<script>
import { getSecond, dateFormatByAppearance } from '@/utils/date-util'
import { dateFormatByAppearance } from '@/utils/date-util'
import { eventSeverityColor, entityDetailTabsName } from '@/utils/constants'
import unitConvert from '@/utils/unit-convert'
import axios from 'axios'
@@ -101,7 +101,7 @@ export default {
setup () {
const { query } = useRoute()
const entityType = query.entityType
const entityName = query.name
const entityName = query.entityName
return {
entityType,
@@ -117,9 +117,9 @@ export default {
dateFormatByAppearance,
initData () {
const params = {
resource: this.entityName,
startTime: getSecond(this.timeFilter.startTime),
endTime: getSecond(this.timeFilter.endTime)
resource: this.entityName
// startTime: getSecond(this.timeFilter.startTime),
// endTime: getSecond(this.timeFilter.endTime)
}
this.toggleLoading(true)
@@ -128,7 +128,7 @@ export default {
if (res.code === 200) {
this.isNoData = res.data.result.length === 0
this.$emit('checkWarn', entityDetailTabsName.securityEvent, !this.isNoData)
this.$emit('checkTag', entityDetailTabsName.securityEvent, res.data.result.length)
this.showError = false
if (!this.isNoData) {
this.eventList = res.data.result
@@ -144,6 +144,7 @@ export default {
})
},
httpError (e) {
this.$emit('checkTag', entityDetailTabsName.securityEvent, 0)
this.isNoData = false
this.showError = true
this.errorMsg = this.errorMsgHandler(e)

View File

@@ -231,6 +231,7 @@ export default {
}
}
})
this.linkNoData = data.length === 0
data.forEach((item) => {
item.totalBitsRate = item.egressBitsRate + item.ingressBitsRate
})
@@ -246,7 +247,7 @@ export default {
this.linkData = sorted
}
} else {
this.linkNoData = false
this.linkNoData = true
this.showError1 = true
this.errorMsg1 = this.errorMsgHandler(res[0])
}
@@ -267,7 +268,7 @@ export default {
directionArr = Array.from(new Set(directionArr))
const newNextHopData = []
this.nextHopNoData = directionArr.length === 0
directionArr.forEach((item1) => {
const newObj = { egressBitsRate: 0, ingressBitsRate: 0, totalBitsRate: 0, linkDirection: item1 }
nextHopData.forEach((item2) => {
@@ -309,7 +310,7 @@ export default {
}
} else {
this.showError2 = true
this.nextHopNoData = false
this.nextHopNoData = true
this.errorMsg2 = this.errorMsgHandler(res[1])
}
}

View File

@@ -138,10 +138,11 @@ export default {
}
}
})
this.isLinkNoData = linkGridData.length === 0
this.linkGridData = linkGridData
}
} else {
this.isLinkNoData = false
this.isLinkNoData = true
this.isLinkShowError = true
this.linkErrorMsg = this.errorMsgHandler(res[0])
}
@@ -227,10 +228,11 @@ export default {
}
}
})
this.isNextNoData = nextGridData.length === 0
this.nextGridData = nextGridData
}
} else {
this.isNextNoData = false
this.isNextNoData = true
this.isNextShowError = true
this.nextErrorMsg = this.errorMsgHandler(res[1])
}

View File

@@ -259,6 +259,18 @@ export default {
}
this.myChart = echarts.init(document.getElementById('linkTrafficLineChart'))
this.myChart.setOption(this.chartOption)
this.myChart.dispatchAction({
type: 'takeGlobalCursor',
key: 'brush',
brushOption: {
brushType: 'lineX',
xAxisIndex: 'all',
brushMode: 'single',
throttleType: 'debounce'
}
})
this.myChart.on('brushEnd', this.brushEcharts)
})
}
},
@@ -401,6 +413,29 @@ export default {
if (!this.lineRefer) this.lineRefer = 'Average'
})
}
},
/**
* echarts框选
* @param params
*/
brushEcharts (params) {
this.myChart.dispatchAction({
type: 'brush',
areas: [] // 删除选框
})
// 避免点击空白区域报错
if (params.areas && params.areas.length > 0) {
const rangeObj = {
startTime: Math.ceil(params.areas[0].coordRange[0]),
endTime: Math.ceil(params.areas[0].coordRange[1])
}
// 暂定框选最小范围为5分钟后续可能会变动
if (rangeObj.endTime - rangeObj.startTime < 5 * 60 * 1000) {
rangeObj.startTime = rangeObj.endTime - 5 * 60 * 1000
}
this.$store.commit('setRangeEchartsData', rangeObj)
}
}
},
mounted () {

View File

@@ -32,7 +32,7 @@
import * as echarts from 'echarts'
import { trafficLineChartOption } from '@/views/charts2/charts/options/echartOption'
import unitConvert from '@/utils/unit-convert'
import { chartColor3, unitTypes } from '@/utils/constants.js'
import { chartColor3, chartColor4, unitTypes } from '@/utils/constants.js'
import { ref, shallowRef } from 'vue'
import { stackedLineTooltipFormatter } from '@/views/charts/charts/tools'
import axios from 'axios'
@@ -84,7 +84,8 @@ export default {
npmQuantity: dataForNpmTrafficLine.npmQuantity,
metricOptions: dataForNpmTrafficLine.metricOptions,
showError: false,
errorMsg: ''
errorMsg: '',
sizes: [3, 4, 6, 8, 9, 10]
}
},
watch: {
@@ -238,6 +239,14 @@ export default {
width: 1
},
stack: t.name !== this.$t('network.total') ? this.$t('network.total') : '',
emphasis: {
itemStyle: {
borderColor: chartColor4[t.positioning],
borderWidth: 2,
shadowColor: chartColor4[t.positioning],
shadowBlur: this.sizes[t.positioning] + 2
}
},
areaStyle: {
opacity: 0.1,
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
@@ -283,6 +292,18 @@ export default {
this.myChart = echarts.init(dom)
this.myChart.setOption(this.chartOption, true)
this.myChart.on('legendselectchanged', this.selectLegend)
this.myChart.dispatchAction({
type: 'takeGlobalCursor',
key: 'brush',
brushOption: {
brushType: 'lineX',
xAxisIndex: 'all',
brushMode: 'single',
throttleType: 'debounce'
}
})
this.myChart.on('brushEnd', this.brushEcharts)
})
}
})
@@ -426,6 +447,29 @@ export default {
this.isNoData = false
this.showError = true
this.errorMsg = this.errorMsgHandler(e)
},
/**
* echarts框选
* @param params
*/
brushEcharts (params) {
this.myChart.dispatchAction({
type: 'brush',
areas: [] // 删除选框
})
// 避免点击空白区域报错
if (params.areas && params.areas.length > 0) {
const rangeObj = {
startTime: Math.ceil(params.areas[0].coordRange[0]),
endTime: Math.ceil(params.areas[0].coordRange[1])
}
// 暂定框选最小范围为5分钟后续可能会变动
if (rangeObj.endTime - rangeObj.startTime < 5 * 60 * 1000) {
rangeObj.startTime = rangeObj.endTime - 5 * 60 * 1000
}
this.$store.commit('setRangeEchartsData', rangeObj)
}
}
},
mounted () {

View File

@@ -49,8 +49,7 @@ export const pieChartOption1 = {
}
})
name = name.length > 9 ? name.substr(0, 9) + '...' : name
const arr = ['{a|' + ' ' + value + '}' + '{b|' + name + '}']
return arr
return ['{a|' + ' ' + value + '}' + '{b|' + name + '}']
}
},
series: [
@@ -108,8 +107,7 @@ export const pieChartOption2 = {
}
})
name = name.length > 9 ? name.substr(0, 9) + '...' : name
const arr = ['{a|' + ' ' + value + '}' + '{b|' + name + '}']
return arr
return ['{a|' + ' ' + value + '}' + '{b|' + name + '}']
}
},
color: chartColor2,
@@ -136,7 +134,7 @@ export const pieChartOption3 = {
{
name: 'Access From',
type: 'pie',
radius: ['45%', '50%'],
radius: ['40%', '50%'],
avoidLabelOverlap: false,
emphasis: {
label: {
@@ -229,6 +227,12 @@ export const linkTrafficLineChartOption = {
legend: {
show: false
},
brush: {
toolbox: ['lineX'],
xAxisIndex: 'all',
throttleType: 'debounce',
transformable: false
},
grid: {
top: '12%',
left: '2%',
@@ -393,6 +397,12 @@ export const trafficLineChartOption = {
fontFamily: 'NotoSansSChineseRegular'
}
},
brush: {
toolbox: ['lineX'],
xAxisIndex: 'all',
throttleType: 'debounce',
transformable: false
},
grid: {
top: '21%',
left: '2%',

View File

@@ -180,7 +180,7 @@ export const pieForSeverity = {
appendToBody: true
},
color: activeAttackColor,
animation: false,
animation: true,
legend: {
orient: 'vertical',
type: 'plain',
@@ -199,7 +199,7 @@ export const pieForSeverity = {
{
type: 'pie',
selectedMode: 'single',
radius: ['42%', '65%'],
radius: ['43%', '65%'],
center: ['30%', '50%'],
data: [],
label: {
@@ -212,13 +212,6 @@ export const pieForSeverity = {
formatter: function (param, index, callback) {
return `${param.name}: ${param.value}`
}
},
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}
]

View File

@@ -244,7 +244,7 @@ export default {
path: '/entityDetail',
query: {
entityType: type,
name: name
entityName: name
}
})
window.open(href, '_blank')

View File

@@ -275,7 +275,7 @@ export default {
path: '/entityDetail',
query: {
entityType: type,
name: name
entityName: name
}
})
window.open(href, '_blank')

View File

@@ -236,7 +236,7 @@ export default {
path: '/entityDetail',
query: {
entityType: type,
name: name
entityName: name
}
})
window.open(href, '_blank')

View File

@@ -379,7 +379,7 @@ export default {
path: '/entityDetail',
query: {
entityType: type,
name: name
entityName: name
}
})
window.open(href, '_blank')

View File

@@ -22,21 +22,21 @@ export default {
setup (props) {
const { query } = useRoute()
let panelType
const entityData = { entityType: query.entityType, entityName: query.name }
const entityData = { entityType: query.entityType, entityName: query.entityName }
switch (query.entityType) {
case 'ip': {
panelType = panelTypeAndRouteMapping.ipEntityDetail
entityData.ip = query.name
entityData.ip = query.entityName
break
}
case 'domain': {
panelType = panelTypeAndRouteMapping.domainEntityDetail
entityData.domain = query.name
entityData.domain = query.entityName
break
}
case 'app': {
panelType = panelTypeAndRouteMapping.appEntityDetail
entityData.appName = query.name
entityData.appName = query.entityName
break
}
default: {

View File

@@ -3,60 +3,60 @@
class="entity-explorer"
:class="{'entity-explorer--show-list': showList}">
<!-- 顶部工具栏在列表页显示 -->
<!-- <div class="explorer-top-tools explorer-top-tools-new" v-show="showList">-->
<!-- <div class="explorer-detection-top-tools">-->
<!-- <div class="explorer-top-tools-title">{{$t('network.entity')}}</div>-->
<!-- </div>-->
<!-- <div class="explorer-top-tools">-->
<!-- <DateTimeRange :start-time="timeFilter.startTime" :end-time="timeFilter.endTime" :date-range="timeFilter.dateRangeValue" ref="dateTimeRange" @change="reload"/>-->
<!-- <TimeRefresh class="date-time-range" @change="timeRefreshChange" :end-time="timeFilter.endTime"/>-->
<!-- <el-button-group size="mini">-->
<!-- <el-button size="mini" @click="listMode = 'list'" :class="{'active': listMode === 'list'}"><i class="cn-icon cn-icon-list"></i></el-button>-->
<!-- <el-button size="mini" @click="listMode = 'block'" :class="{'active': listMode === 'block'}"><i class="cn-icon cn-icon-blocks"></i></el-button>-->
<!-- </el-button-group>-->
<div class="explorer-top-tools" v-show="showList">
<DateTimeRange :start-time="timeFilter.startTime" :end-time="timeFilter.endTime" :date-range="timeFilter.dateRangeValue" ref="dateTimeRange" @change="reload"/>
<TimeRefresh class="date-time-range" @change="timeRefreshChange" :end-time="timeFilter.endTime"/>
<el-button-group size="mini">
<el-button size="mini" @click="setListMode('list')" :class="{'active': listMode === 'list'}"><i class="cn-icon cn-icon-list"></i></el-button>
<el-button size="mini" @click="setListMode('block')" :class="{'active': listMode === 'block'}"><i class="cn-icon cn-icon-blocks"></i></el-button>
</el-button-group>
<div class="explorer-top-tools explorer-top-tools-new" style="margin: 2px 0;" v-show="showList">
<div class="explorer-detection-top-tools">
<div class="explorer-top-tools-title" style="padding: 0;margin-left: -10px;">{{$t('network.entity')}}</div>
</div>
<!-- </div>-->
</div>
<!-- 搜索组件 -->
<explorer-search
v-if="!showList"
ref="search"
:class="{'explorer-search--show-list': showList}"
:show-list="showList"
@search="search"
></explorer-search>
<!--todo静态数据之后记得修改-->
<!-- <div class="explorer-result" v-if="showList">-->
<!-- 8,866 resultsIP 2666Domain 3200APP 3000-->
<!-- </div>-->
<!-- 内容区 -->
<div class="explorer-container" v-if="showList" style="height: calc(100% - 20px); flex-direction: column">
<div style="display: flex; height: auto;">
<entity-filter
:filter-data="filterData"
:loading-left="loadingLeft"
:q="q"
:time-filter="timeFilter"
@filter="filter"
></entity-filter>
<entity-list
:list-data="listData"
:list-mode="listMode"
:pageObj="pageObj"
:time-filter="timeFilter"
@pageSize="pageSize"
@pageNo="pageNo"
:loading="listLoading"
></entity-list>
<div v-if="showList" style="display: flex;flex-direction: row;">
<entity-filter
:filter-data="newFilterData"
:loading-left="loadingLeft"
:q="q"
:time-filter="timeFilter"
@filter="filter"
></entity-filter>
<div class="explorer-container" style="height: calc(100% - 62px);flex-direction: column;width: 100%;">
<explorer-search
ref="search"
:class="{'explorer-search--show-list': showList}"
:show-list="showList"
@search="search"
></explorer-search>
<div style="display: flex;flex-direction: column;height: calc(100% - 42px);">
<div class="explorer-result" v-if="showList" style="position: relative;">
<loading :loading="loadingCount" style="width: 240px"></loading>
<span>{{ summaryCount.total }}&nbsp;</span>resultsIP
<span>{{ summaryCount.ipCount }}</span>Domain
<span>{{ summaryCount.domainCount }}</span>APP
<span>{{ summaryCount.appCount }}</span>
</div>
<entity-list
style="width: 100%;"
:list-data="listData"
:list-mode="listMode"
:pageObj="pageObj"
:time-filter="timeFilter"
@pageSize="pageSize"
@pageNo="pageNo"
:loading="listLoading"
></entity-list>
</div>
</div>
</div>
<div class="explorer-foot" v-else>
<div class="explorer-foot" v-if="!showList">
<div>
<el-divider direction="vertical"></el-divider>
<div class="entity-overview">
@@ -65,16 +65,10 @@
<loading :loading="loadingApp"></loading>
<span class="overview-left-loading-span">{{ numberWithCommas(entityAppTotal) }}</span>
</span>
<span class="overview-left-span">APP</span>
</div>
<div class="overview-right">
<div class="right-row">
<i class="cn-icon cn-icon-increase"></i>
<div class="right-label">New</div>
<div class="right-label-loading">
<loading :loading="loadingAppNew" size="small"></loading>
<div class="right-value">{{ numberWithCommas(entityAppNew) }}</div>
</div>
APP
</div>
<div class="right-row">
<i class="cn-icon cn-icon-active"></i>
@@ -93,16 +87,10 @@
<loading :loading="loadingDomain"></loading>
<span class="overview-left-loading-span">{{ numberWithCommas(entityDomainTotal) }}</span>
</span>
<span class="overview-left-span">DOMAIN</span>
</div>
<div class="overview-right">
<div class="right-row">
<i class="cn-icon cn-icon-increase"></i>
<div class="right-label">New</div>
<div class="right-label-loading">
<loading :loading="loadingDomainNew" size="small"></loading>
<div class="right-value">{{ numberWithCommas(entityDomainNew) }}</div>
</div>
DOMAIN
</div>
<div class="right-row">
<i class="cn-icon cn-icon-active"></i>
@@ -121,16 +109,10 @@
<loading :loading="loadingIp"></loading>
<span class="overview-left-loading-span">{{ numberWithCommas(entityIpTotal) }}</span>
</span>
<span class="overview-left-span">IP</span>
</div>
<div class="overview-right">
<div class="right-row">
<i class="cn-icon cn-icon-increase"></i>
<div class="right-label">New</div>
<div class="right-label-loading">
<loading :loading="loadingIpNew" size="small"></loading>
<div class="right-value">{{ numberWithCommas(entityIpNew) }}</div>
</div>
IP
</div>
<div class="right-row">
<i class="cn-icon cn-icon-active"></i>
@@ -150,8 +132,6 @@
<script>
import ExplorerSearch from '@/views/entityExplorer/search/ExplorerSearch'
import DateTimeRange from '@/components/common/TimeRange/DateTimeRange'
import TimeRefresh from '@/components/common/TimeRange/TimeRefresh'
import EntityFilter from '@/views/entityExplorer/EntityFilter'
import EntityList from '@/views/entityExplorer/entityList/EntityList'
import { entityType, defaultPageSize, riskLevelMapping } from '@/utils/constants'
@@ -172,8 +152,6 @@ export default {
components: {
Loading,
ExplorerSearch,
DateTimeRange,
TimeRefresh,
EntityFilter,
EntityList
},
@@ -210,23 +188,15 @@ export default {
{
label: this.$t('overall.country'),
column: 'countryDistinctCount',
topColumn: 'ip_location_country', // top弹框查询字段
topColumn: 'Country', // top弹框查询字段
icon: 'cn-icon cn-icon-country',
showTopTen: false,
value: 0
},
{
label: this.$t('overall.province'),
column: 'provinceDistinctCount',
topColumn: 'ip_location_province', // top弹框查询字段
icon: 'cn-icon cn-icon-position',
showTopTen: false,
value: 0
},
{
label: this.$t('overall.city'),
column: 'cityDistinctCount',
topColumn: 'ip_location_city', // top弹框查询字段
topColumn: 'City', // top弹框查询字段
icon: 'cn-icon cn-icon-city',
showTopTen: false,
value: 0
@@ -234,7 +204,7 @@ export default {
{
label: this.$t('entities.asn'),
column: 'asnDistinctCount',
topColumn: 'ip_asn', // top弹框查询字段
topColumn: 'ASN', // top弹框查询字段
icon: 'cn-icon cn-icon-cloud',
showTopTen: false,
value: 0
@@ -346,6 +316,26 @@ export default {
]
}
],
newFilterData: [
{
icon: 'cn-icon cn-icon-registration-country',
title: 'Top Countries',
totalCount: 0,
data: []
},
{
icon: 'cn-icon cn-icon-city',
title: 'Top Cities',
totalCount: 0,
data: []
},
{
icon: 'cn-icon cn-icon-as',
title: 'Top ASNs',
totalCount: 0,
data: []
}
],
listData: [],
q: '',
metaList: [],
@@ -367,7 +357,14 @@ export default {
// 实体详情列表页面 左侧筛选条件
loadingLeft: false,
initFlag: false, // 初始化标志避免初始化时pageSize和pageNo会调用搜索
timer: null // 初始化标志的延时器,需要销毁
timer: null, // 初始化标志的延时器,需要销毁
summaryCount: {
total: 0,
domainCount: 0,
ipCount: 0,
appCount: 0
},
loadingCount: false // 实体基数统计的loading
}
},
methods: {
@@ -420,6 +417,12 @@ export default {
return result
},
search (param) {
// todo 下版本08版本删除 ---- start
if (param && param.q.indexOf("QUERY('") > -1) {
this.$message.error(this.$t('overall.versionNotSupportThisFormat'))
return true
}
// 下版本08版本删除 ---- end
let q
let metaList
if (param) {
@@ -442,7 +445,8 @@ export default {
q: q,
mode: mode,
startTime: getSecond(this.timeFilter.startTime),
endTime: getSecond(this.timeFilter.endTime)
endTime: getSecond(this.timeFilter.endTime),
range: this.timeFilter.dateRangeValue
})
if (!this.showList) {
@@ -454,7 +458,8 @@ export default {
q: q,
mode: mode,
startTime: getSecond(this.timeFilter.startTime),
endTime: getSecond(this.timeFilter.endTime)
endTime: getSecond(this.timeFilter.endTime),
range: this.timeFilter.dateRangeValue
}
})
this.showList = true
@@ -487,29 +492,35 @@ export default {
} else {
this.limitFilterType = false
}
this.queryFilter({ entityType: entityType, q: this.q, ...this.timeFilter })
if (entityType === 'ip') {
this.queryFilter({ entityType: 'dns', q: this.q, ...this.timeFilter })
}
// this.queryFilter({ entityType: entityType, q: this.q, ...this.timeFilter })
// if (entityType === 'ip') {
// this.queryFilter({ entityType: 'dns', q: this.q, ...this.timeFilter })
// }
this.queryFilterNew({ q: this.q, ...this.pageObj, ...this.timeFilter })
this.queryList({ q: this.q, ...this.pageObj, ...this.timeFilter })
this.queryListTotal({ q: this.q, ...this.timeFilter })
this.queryCount({ q: this.q, ...this.pageObj, ...this.timeFilter })
// this.queryListTotal({ q: this.q, ...this.timeFilter })
} else {
this.limitFilterType = false
this.queryFilter({ entityType: 'ip', q: this.q, ...this.timeFilter })
this.queryFilter({ entityType: 'domain', q: this.q, ...this.timeFilter })
this.queryFilter({ entityType: 'app', q: this.q, ...this.timeFilter })
this.queryFilter({ entityType: 'dns', q: this.q, ...this.timeFilter })
// this.queryFilter({ entityType: 'ip', q: this.q, ...this.timeFilter })
// this.queryFilter({ entityType: 'domain', q: this.q, ...this.timeFilter })
// this.queryFilter({ entityType: 'app', q: this.q, ...this.timeFilter })
// this.queryFilter({ entityType: 'dns', q: this.q, ...this.timeFilter })
this.queryFilterNew({ q: this.q, ...this.pageObj, ...this.timeFilter })
this.queryList({ q: this.q, ...this.pageObj, ...this.timeFilter })
this.queryListTotal({ q: this.q, ...this.timeFilter })
this.queryCount({ q: this.q, ...this.pageObj, ...this.timeFilter })
// this.queryListTotal({ q: this.q, ...this.timeFilter })
}
} else {
this.limitFilterType = false
this.queryFilter({ entityType: 'ip', ...this.timeFilter })
this.queryFilter({ entityType: 'app', ...this.timeFilter })
this.queryFilter({ entityType: 'domain', ...this.timeFilter })
this.queryFilter({ entityType: 'dns', ...this.timeFilter })
// this.queryFilter({ entityType: 'ip', ...this.timeFilter })
// this.queryFilter({ entityType: 'app', ...this.timeFilter })
// this.queryFilter({ entityType: 'domain', ...this.timeFilter })
// this.queryFilter({ entityType: 'dns', ...this.timeFilter })
this.queryFilterNew({ ...this.pageObj, ...this.timeFilter })
this.queryList({ ...this.pageObj, ...this.timeFilter })
this.queryListTotal({ ...this.timeFilter })
this.queryCount({ ...this.pageObj, ...this.timeFilter })
// this.queryListTotal({ ...this.timeFilter })
// 延时一秒避免初始化时pageSize为20pageNo为1也会调用“搜索”的情况
if (!this.initFlag) {
@@ -634,27 +645,88 @@ export default {
}
})
},
/** 新版查询filter数据 */
queryFilterNew (params) {
const queryParams = {
// startTime: getSecond(params.startTime),
// endTime: getSecond(params.endTime),
resource: params.q || ''
}
this.loadingLeft = true
const aggCountry = get(api.entity.entityList.aggCountry, queryParams)
const aggCity = get(api.entity.entityList.aggCity, queryParams)
const aggAsn = get(api.entity.entityList.aggAsn, queryParams)
Promise.all([aggCountry, aggCity, aggAsn]).then(response => {
response.forEach((item, index) => {
if (item.code === 200 && item.data.list) {
this.newFilterData[index].data = []
item.data.list.forEach(item => {
const obj = { label: item.value, flag: '011-china', topColumn: 'Country', value: item.uniqueEntities }
if (index === 0) {
obj.flag = item.uniqueEntities // 接口字段名称为'China'目前svg名称为'011-china',后续再指定方案调整
}
if (index === 1) {
obj.topColumn = 'City'
}
if (index === 2) {
obj.topColumn = 'ASN'
}
this.newFilterData[index].data.push(obj)
})
}
})
}).catch(e => {
// e
}).finally(() => {
this.loadingLeft = false
})
},
/** 实体列表查询 */
queryList (params) {
this.listLoading = true
const queryParams = {
...params,
startTime: getSecond(params.startTime),
endTime: getSecond(params.endTime)
pageSize: params.pageSize,
pageNo: params.pageNo,
// startTime: getSecond(params.startTime),
// endTime: getSecond(params.endTime),
resource: params.q || ''
}
get(api.entityList, queryParams).then(response => {
get(api.entity.entityList.list, queryParams).then(response => {
if (response.code === 200) {
this.listData = []
this.$nextTick(() => {
this.listData = response.data.result
this.listData = response.data.list
this.pageObj.total = response.data.total
})
} else {
console.error(response.message)
this.$message.error(response.message)
}
}).finally(() => {
this.listLoading = false
})
},
/** 实体基数统计 */
queryCount (params) {
this.loadingCount = true
const queryParams = {
// startTime: getSecond(params.startTime),
// endTime: getSecond(params.endTime),
resource: params.q || '' // 目前版本搜索不支持实体名称搜索,下版本改进
}
get(api.entity.entityList.summaryCount, queryParams).then(response => {
if (response.code === 200) {
this.summaryCount = response.data
} else {
this.summaryCount = { total: 0, domainCount: 0, ipCount: 0, appCount: 0 }
}
}).catch(e => {
console.log(e)
this.summaryCount = { total: 0, domainCount: 0, ipCount: 0, appCount: 0 }
}).finally(() => {
this.loadingCount = false
})
},
queryListTotal (params) {
const queryParams = {
@@ -675,11 +747,6 @@ export default {
},
getEntityIndexData () {
const now = window.$dayJs.tz().valueOf()
const timeFilter = {
startTime: parseInt(now / 1000 - 3600),
endTime: parseInt(now / 1000)
}
// Total
this.loadingApp = true
this.loadingDomain = true
@@ -693,61 +760,37 @@ export default {
this.loadingDomainActive = true
this.loadingIpActive = true
get(api.entityTotal, { entityType: 'app' }).then(response => {
get(api.entity.entityList.entityActive).then(response => {
if (response.code === 200) {
this.entityAppTotal = response.data.result
}
this.loadingApp = false
})
get(api.entityTotal, { entityType: 'domain' }).then(response => {
if (response.code === 200) {
this.entityDomainTotal = response.data.result
this.entityDomainTotal = response.data.domainCount
this.entityIpTotal = response.data.ipCount
this.entityAppTotal = response.data.appCount
}
this.loadingDomain = false
})
get(api.entityTotal, { entityType: 'ip' }).then(response => {
if (response.code === 200) {
this.entityIpTotal = response.data.result
}
this.loadingIp = false
this.loadingApp = false
})
// New
get(api.entityNew, { entityType: 'app', ...timeFilter }).then(response => {
get(api.entity.entityList.entityNew).then(response => {
if (response.code === 200) {
this.entityAppNew = response.data.result
}
this.loadingAppNew = false
})
get(api.entityNew, { entityType: 'domain', ...timeFilter }).then(response => {
if (response.code === 200) {
this.entityDomainNew = response.data.result
this.entityDomainNew = response.data.domainCount
this.entityIpNew = response.data.ipCount
this.entityAppNew = response.data.appCount
}
this.loadingDomainNew = false
})
get(api.entityNew, { entityType: 'ip', ...timeFilter }).then(response => {
if (response.code === 200) {
this.entityIpNew = response.data.result
}
this.loadingIpNew = false
this.loadingAppNew = false
})
// Active
get(api.entityActive, { entityType: 'app', ...timeFilter }).then(response => {
get(api.entity.entityList.entityActive).then(response => {
if (response.code === 200) {
this.entityAppActive = response.data.result
}
this.loadingAppActive = false
})
get(api.entityActive, { entityType: 'domain', ...timeFilter }).then(response => {
if (response.code === 200) {
this.entityDomainActive = response.data.result
this.entityDomainActive = response.data.domainCount
this.entityIpActive = response.data.ipCount
this.entityAppActive = response.data.appCount
}
this.loadingDomainActive = false
})
get(api.entityActive, { entityType: 'ip', ...timeFilter }).then(response => {
if (response.code === 200) {
this.entityIpActive = response.data.result
}
this.loadingIpActive = false
this.loadingAppActive = false
})
},
cleanFilterData (index) {
@@ -779,10 +822,19 @@ export default {
* @param q
*/
initSearch (q) {
const str = q
let str = q
// 此处的mode不做text和tag区分是因为text和tag构造搜索参数过程不一样但结果的参数一致
// 故采用text的参数形式进行搜索tag形式在tagMode.vue的mounted里根据地址栏的参数q构造metaList
if (str) {
// 为避免地址栏任意输入导致全查询的q带QUERY解析时不识别导致的语法错误
// 如地址栏输入116.178.222.171此时的q很长刷新界面时需要把q里的116.178.222.171拿出来进行搜索
if (str.indexOf('QUERY') > -1) {
const strList = str.split(' ')
if (strList.length > 0) {
// 此时strList[1]为ip_addr:116.178.222.171获取116.178.222.171
str = strList[1].slice(8)
}
}
const parser = new Parser(columnList)
const errorList = parser.validateStr(str)
if (_.isEmpty(errorList)) {
@@ -828,7 +880,6 @@ export default {
timeFilter.value.startTime = parseInt(startTimeParam)
timeFilter.value.endTime = parseInt(endTimeParam)
}
timeFilter.value.dateRangeValue = 60
return {
timeFilter
}

View File

@@ -1,150 +1,94 @@
<template >
<div class="entity-filter-case">
<div class="filter-case__header">{{$t('entities.filter')}}</div>
<div
class="entity-filter"
v-for="(filters, index) in filterData"
:key="index"
>
<div class="filter__header">{{filters.title}}</div>
<div class="filter__body">
<template>
<div class="entity-filter-case" style="position: relative">
<div class="filter-case__header">{{ $t('entities.filter1') }}</div>
<div class="filter__row" v-for="(item, i) in filters.data" :key="i">
<el-popover popper-class="filter__row-popover" placement="right-start" :width="440" v-model:visible="item.showTopTen">
<template #reference>
<div class="filter__row-popover" @click="showTopDialog(i, item, filters)">
<div class="row__label">
<i :class="item.icon"></i>
<span>{{item.label}}</span>
</div>
<div class="row__value">
<loading :loading="loadingLeft" size="small"></loading>
<span>{{item.value}}</span>
<div v-if="filterDataLength>0">
<div class="entity-filter" v-for="(item, index) in filterData" :key="index">
<div v-if="item.data.length>0">
<div class="filter__header">
<i :class="item.icon"></i>
{{ item.title }}
</div>
<div class="filter__body" style="position: relative">
<loading :loading="loadingLeft" style="top: -5px;"></loading>
<div class="filter__body-item" v-for="(data, i) in item.data" :key="i" @click="filter(data.label, data)">
<div class="filter__body-item-left">
<!--当前无更好方案匹配国旗后续解决-->
<!--<div v-if="data.flag">-->
<!-- <img :src="require(`../../../public/images/flag/${data.flag}.svg`)" class="filter-country-flag"/>-->
<!--</div>-->
<div class="filter__body-item-left-index">{{ i+1 }}</div>
<div class="filter__body-item-left-label">
<el-tooltip :content="data.label" placement="top" effect="light" :disabled="disabledLabel">
<span @mouseenter="handleMouse(`filter${index}${i}`)" :id="`filter${index}${i}`">{{ data.label }}</span>
</el-tooltip>
</div>
</div>
</template>
<entity-top
ref="entityTopTenPop"
:loading="loading"
:popover-data="popoverData"
:item-data="itemData"
:total-count="totalCount"
:top-column="item.topColumn"
@filter="filter"
></entity-top>
</el-popover>
<div class="filter__body-item-right">{{ data.value }}</div>
</div>
<div class="filter-hr"></div>
</div>
</div>
</div>
</div>
<chart-no-data v-else style="padding-top: 40px"></chart-no-data>
</div>
</template>
<script>
import EntityTop from '@/views/entityExplorer/EntityTop'
import { get } from '@/utils/http'
import { api } from '@/utils/api'
import Loading from '@/components/common/Loading'
import ChartNoData from '@/views/charts/charts/ChartNoData'
export default {
name: 'EntityFilter',
components: {
Loading,
EntityTop
},
components: { ChartNoData, Loading },
props: {
filterData: Array,
q: String,
timeFilter: Object,
loadingLeft: Boolean
filterData: {
type: Object
},
loadingLeft: {
type: Boolean
}
},
computed: {
filterDataLength () {
let length = 0
this.filterData.forEach(item => {
length += item.data.length
})
return length
}
},
data () {
return {
topList: 'list',
topData: [],
entityTopTenData: [],
currentColumn: {},
totalCount: 0,
loading: false,
popoverData: [],
itemData: {}
}
},
watch: {
currentColumn (n, o) {
if (n.column === 'dnsServerOrgCount') {
this.totalCount = this.filterData[3].orgTotalCount
} else if (n.column === 'dnsServerSoftwareCount') {
this.totalCount = this.filterData[3].softwareTotalCount
} else if (n.column === 'dnsServerOsCount') {
this.totalCount = this.filterData[3].osTotalCount
} else if (n.column === 'categoryDistinctCount' && n.type === 'app') {
this.totalCount = this.filterData[1].totalCount
} else {
let count = 0
this.filterData.forEach(f => {
const filter = f.data.some(d => d.column === n.column)
if (filter) {
count = f.totalCount
}
})
this.totalCount = count
}
disabledLabel: true
}
},
methods: {
showTopDialog (i, item, filter) {
if (this.currentColumn.column === item.column && item.showTopTen) {
item.showTopTen = false
return
/**
* 判断文字是否溢出超出则鼠标移入tooltip显示否则鼠标移入不显示
* @param id
*/
handleMouse (id) {
const dom = document.getElementById(id)
if (dom) {
const width = document.getElementById(id).offsetWidth
this.disabledLabel = width < 180
} else {
this.disabledLabel = true
}
this.filterData.forEach(f => {
f.data.forEach(ff => {
ff.showTopTen = false
})
})
item.showTopTen = true
this.currentColumn = {
column: item.column,
type: filter.type
}
const queryParams = {
q: this.q,
entityType: filter.type,
column: item.topColumn,
top: 10,
startTime: parseInt(this.timeFilter.startTime / 1000),
endTime: parseInt(this.timeFilter.endTime / 1000)
}
this.loading = true
this.popoverData = []
this.itemData = {}
get(api.filterTop, queryParams).then(response => {
if (response.code === 200) {
if (this.currentColumn.column === item.column) {
if (filter.type === 'dns') {
this.popoverData = response.data.result.filter(f => {
return f.count > 0
})
} else {
this.popoverData = response.data.result
}
this.itemData = item
}
} else {
this.popoverData = []
this.itemData = item
}
this.loading = false
}).catch(e => {
this.popoverData = []
this.itemData = item
this.loading = false
})
},
filter (name, topData) {
this.showTopDialog('', topData)
console.log('打印数据', name, topData)
this.$emit('filter', name, topData)
filter (name, data) {
this.$emit('filter', name, data)
}
}
}
</script>
<style lang="scss" scoped>
</style>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,119 @@
<template>
<div>
<loading :loading="entity.loading" size="small"></loading>
<div class="graph-list-header">
<div>
<div class="graph-list-header-title">
<i class="graph-list-header-icon cn-icon cn-icon-app-name"></i>
<span>{{ $t('entities.tab.relatedApp') }}</span>
</div>
<div class="graph-list-header-number">
{{ $t('entity.graph.associatedCount') }}:&nbsp;<span>{{entity.count}}</span>
</div>
</div>
<i class="cn-icon cn-icon-close graph-close" @click="closeBlock"></i>
</div>
<div class="graph-list-expand-btn-block">
<div class="graph-list-expand-btn" :class="{ 'graph-list-expand-btn--disabled': expandBtnDisable }" style="display: inline-flex;" @click="expandList">
<i class="cn-icon cn-icon-expand-continue graph-expand-continue"></i>
{{ $t('entity.graph.continueToExpand') }}
</div>
</div>
<div>
<div class="digital-certificate">
<div class="digital-certificate-header padding-b-12">
<div class="digital-certificate-header__icon graph-header__icon"></div>
<div class="graph-list-content-header ">
{{ $t('entity.graph.expandedEntityCount') }}:&nbsp;&nbsp;
<span>{{ entity.listData ? entity.listData.length : 0 }}</span>
</div>
</div>
<div class="graph-list-content">
<div v-for="(item, index) in entity.listData" :key="index" @mouseenter="onMouseenter(item)">
<div class="graph-list-item-ip" @click="expandDetail">{{ item.vertex }}</div>
<div class="graph-list-item-block">
<div class="graph-list-item__app">
<div class="graph-list-item-label__app">APP ID:</div>
<div class="graph-list-item-value">{{ $_.get(item.detail, 'category.appId', '-') }}</div>
</div>
<div class="graph-list-item__app">
<div class="graph-list-item-label__app">{{$t('entities.category')}}:</div>
<div class="graph-list-item-value">{{ $_.get(item.detail, 'category.appCategory', '-') }}</div>
</div>
<div class="graph-list-item__app">
<div class="graph-list-item-label__app">{{$t('entities.subcategory')}}:</div>
<div class="graph-list-item-value">{{ $_.get(item.detail, 'category.appSubcategory', '-') }}</div>
</div>
<div class="graph-list-item__app">
<div class="graph-list-item-label__app">{{$t('entities.riskLevel')}}:</div>
<div class="graph-list-item-value">{{ appRisk($_.get(item.detail, 'category.appRisk', '-')) }}</div>
</div>
<div class="graph-list-item__app">
<div class="graph-list-item-label__app">{{$t('config.dataSource.description')}}:</div>
<div class="graph-list-item-value">{{ $_.get(item.detail, 'category.appDescription', '-') }}</div>
</div>
</div>
<div class="graph-list-dividing-line" v-if="index !== entity.listData.length - 1"></div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import { riskLevelMapping } from '@/utils/constants'
import Loading from '@/components/common/Loading'
import { scrollToTop } from '@/utils/tools'
export default {
name: 'AppList',
props: {
entity: {
type: Object
}
},
components: {
Loading
},
computed: {
appRisk () {
return function (level) {
const m = riskLevelMapping.find(mapping => {
return mapping.value == level
})
return (m && m.name) || level
}
},
expandBtnDisable () {
return !((this.entity.listData ? this.entity.listData.length : 0) < this.entity.count && this.entity.count > 10 && (this.entity.listData && this.entity.listData.length < 50))
}
},
methods: {
closeBlock () {
this.$emit('closeBlock')
},
expandDetail () {
if (this.entity.entityType === 'app') {
this.$emit('expandDetail', 'appDetail')
} else if (this.entity.entityType === 'domain') {
this.$emit('expandDetail', 'domainDetail')
}
},
onMouseenter (val) {
// 鼠标移动过graph列表名称时graph图的分支图形会变大一点
this.$emit('mouseenter', val)
},
expandList () {
// 继续拓展ip列表传递信息调用接口
this.$emit('expandList', this.entity.sourceName, this.entity.nodeId)
}
},
mounted () {
this.$nextTick(() => {
scrollToTop()
})
}
}
</script>

View File

@@ -0,0 +1,115 @@
<template>
<div>
<loading :loading="entity.loading" size="small"></loading>
<div class="graph-list-header">
<div>
<div class="graph-list-header-title">
<i class="graph-list-header-icon cn-icon cn-icon-subdomain"></i>
<span>{{ entity.isSubdomain ? $t('entities.subdomain') : (entity.sourceType === 'ip' ? $t('entity.graph.resolveDomain') : $t('entities.relatedDomain')) }}</span>
</div>
<div class="graph-list-header-number">
{{ $t('entity.graph.associatedCount') }}:&nbsp;<span>{{entity.count}}</span>
</div>
</div>
<i class="cn-icon cn-icon-close graph-close" @click="closeBlock"></i>
</div>
<div class="graph-list-expand-btn-block">
<div class="graph-list-expand-btn" :class="{ 'graph-list-expand-btn--disabled': expandBtnDisable }" style="display: inline-flex;" @click="expandList">
<i class="cn-icon cn-icon-expand-continue graph-expand-continue"></i>
{{ $t('entity.graph.continueToExpand') }}
</div>
</div>
<div>
<div class="digital-certificate">
<div class="digital-certificate-header padding-b-12">
<div class="digital-certificate-header__icon graph-header__icon"></div>
<div class="graph-list-content-header ">
{{ $t('entity.graph.expandedEntityCount') }}:&nbsp;&nbsp;
<span>{{ entity.listData ? entity.listData.length : 0 }}</span>
</div>
</div>
<div class="graph-list-content">
<div v-for="(item, index) in entity.listData" :key="index" @mouseenter="onMouseenter(item)">
<div class="graph-list-item-ip" @click="expandDetail">{{ item.vertex }}</div>
<div class="graph-list-item-block">
<div class="graph-list-item__app">
<div class="graph-list-item-label__app">{{$t('entities.category')}}:</div>
<div class="graph-list-item-value">{{ $_.get(item.detail, 'category.name', '-') }}</div>
</div>
<div class="graph-list-item__app">
<div class="graph-list-item-label__app">{{$t('entities.group')}}:</div>
<div class="graph-list-item-value">{{ $_.get(item.detail, 'category.group', '-') }}</div>
</div>
<div class="graph-list-item__app">
<div class="graph-list-item-label__app">{{$t('entities.registration')}}:</div>
<div class="graph-list-item-value">{{ $_.get(item.detail, 'whois.registrantCountry', '-') }}</div>
</div>
<div class="graph-list-item__app">
<div class="graph-list-item-label__app">{{$t('entities.registry')}}:</div>
<div class="graph-list-item-value">{{ $_.get(item.detail, 'whois.registrantOrg', '-') }}</div>
</div>
</div>
<div class="graph-list-dividing-line" v-if="index !== entity.listData.length - 1"></div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import { riskLevelMapping } from '@/utils/constants'
import Loading from '@/components/common/Loading'
import { scrollToTop } from '@/utils/tools'
export default {
name: 'DomainList',
props: {
entity: {
type: Object
}
},
components: {
Loading
},
computed: {
appRisk () {
return function (level) {
const m = riskLevelMapping.find(mapping => {
return mapping.value == level
})
return (m && m.name) || level
}
},
expandBtnDisable () {
return !((this.entity.listData ? this.entity.listData.length : 0) < this.entity.count && this.entity.count > 10 && (this.entity.listData && this.entity.listData.length < 50))
}
},
methods: {
closeBlock () {
this.$emit('closeBlock')
},
expandDetail () {
if (this.entity.entityType === 'app') {
this.$emit('expandDetail', 'appDetail')
} else if (this.entity.entityType === 'domain') {
this.$emit('expandDetail', 'domainDetail')
}
},
onMouseenter (val) {
// 鼠标移动过graph列表名称时graph图的分支图形会变大一点
this.$emit('mouseenter', val)
},
expandList () {
// 继续拓展ip列表传递信息调用接口
this.$emit('expandList', this.entity.sourceName, this.entity.nodeId)
}
},
mounted () {
this.$nextTick(() => {
scrollToTop()
})
}
}
</script>

View File

@@ -0,0 +1,493 @@
<template>
<!--title-->
<div class="graph-detail-basic-info">
<div style="display: flex">
<div class="graph-detail__icon"><i :class="iconClass"></i></div>
<div class="graph-detail-header">
<div class="entity-graph-type">{{ entityType[entity.type || entity.entityType] }}</div>
<div class="graph-basic-info">
<div class="graph-basic-info-name__block">
<div class="graph-basic-info-name" :title="$_.get(entity, 'detailData.vertex', '')" id="entityName">{{ $_.get(entity, 'detailData.vertex', '') }}</div>
<div class="graph-basic-info-icon" @click="copyEntityName">
<i class="cn-icon cn-icon-copy"></i>
</div>
</div>
</div>
</div>
</div>
<i class="cn-icon cn-icon-close graph-close" @click="closeBlock"></i>
</div>
<!--basic info-->
<div class="digital-certificate graph-basic-info__block">
<div class="digital-certificate-header padding-b-10">
<div class="digital-certificate-header__icon graph-header__icon"></div>
<div class="graph-basic-info__block-title">
{{ $t('overall.basicInfo') }}
</div>
</div>
<div class="graph-basic-info__block-content">
<template v-if="entity.type === 'ip'">
<div class="graph-content-item" >
<div class="graph-content-item-label">{{ $t('entities.asNumber') }}:</div>
<div class="graph-content-item-value">{{ $_.get(entity.detailData, 'detail.asn.asn', '-') }}</div>
</div>
<div class="graph-content-item" >
<div class="graph-content-item-label">{{ $t('entities.asOrg') }}:</div>
<div class="graph-content-item-value">{{ $_.get(entity.detailData, 'detail.asn.organization', '-') }}</div>
</div>
<div class="graph-content-item" >
<div class="graph-content-item-label">{{ $t('entities.graph.isp') }}:</div>
<div class="graph-content-item-value">{{ $_.get(entity.detailData, 'detail.location.isp', '-') }}</div>
</div>
<div class="graph-content-item" >
<div class="graph-content-item-label">{{ $t('overall.location') }}:</div>
<div class="graph-content-item-value">{{ location(entity.detailData) }}</div>
</div>
</template>
<template v-else-if="entity.type === 'domain'">
<div class="graph-content-item" >
<div class="graph-content-item-label">{{ $t('entities.category') }}:</div>
<div class="graph-content-item-value">{{ $_.get(entity, 'detailData.detail.category.name', '-') }}</div>
</div>
<div class="graph-content-item" >
<div class="graph-content-item-label">{{ $t('entities.group') }}:</div>
<div class="graph-content-item-value">{{ $_.get(entity, 'detailData.detail.category.group', '-') }}</div>
</div>
<div class="graph-content-item" >
<div class="graph-content-item-label">{{ $t('entities.creditLevel2') }}:</div>
<div class="graph-content-item-value">{{ $_.get(entity, 'detailData.detail.category.reputationLevel', '-') }}</div>
</div>
<div class="graph-content-item" >
<div class="graph-content-item-label">{{ $t('entities.graph.expirationDate') }}:</div>
<div class="graph-content-item-value">{{ handleDate('detailData.detail.whois.expireDate') }}</div>
</div>
<div class="graph-content-item" >
<div class="graph-content-item-label">{{ $t('entities.registrar') }}:</div>
<div class="graph-content-item-value">{{ $_.get(entity, 'detailData.detail.whois.registrarName', '-') }}</div>
</div>
<div class="graph-content-item" >
<div class="graph-content-item-label">{{ $t('entities.registry') }}:</div>
<div class="graph-content-item-value">{{ $_.get(entity, 'detailData.detail.whois.registrantOrg', '-') }}</div>
</div>
<div class="graph-content-item" >
<div class="graph-content-item-label">{{ $t('entities.registrationCountry') }}:</div>
<div class="graph-content-item-value">{{ $_.get(entity, 'detailData.detail.whois.registrantCountry', '-') }}</div>
</div>
<div class="graph-content-item" >
<div class="graph-content-item-label">{{ $t('entities.registrationDate') }}:</div>
<div class="graph-content-item-value">{{ handleDate('detailData.detail.whois.createDate') }}</div>
</div>
<div class="graph-content-item" >
<div class="graph-content-item-label">{{ $t('entities.registryEmail') }}:</div>
<div class="graph-content-item-value">{{ $_.get(entity, 'detailData.detail.whois.email', '-') }}</div>
</div>
</template>
<template v-else-if="entity.type === 'app'">
<div class="graph-content-item" >
<div class="graph-content-item-label">{{ $t('entities.category') }}:</div>
<div class="graph-content-item-value">{{ $_.get(entity, 'detailData.detail.category.appCategory', '-') }}</div>
</div>
<div class="graph-content-item" >
<div class="graph-content-item-label">{{ $t('entities.subcategory') }}:</div>
<div class="graph-content-item-value">{{ $_.get(entity, 'detailData.detail.category.appSubcategory', '-') }}</div>
</div>
<div class="graph-content-item" >
<div class="graph-content-item-label">{{ $t('entities.riskLevel') }}:</div>
<div class="graph-content-item-value">{{ appRisk($_.get(entity, 'detailData.detail.category.appRisk', '-')) }}</div>
</div>
<div class="graph-content-item" >
<div class="graph-content-item-label">{{ $t('overall.technology') }}:</div>
<div class="graph-content-item-value">{{ $_.get(entity, 'detailData.detail.category.appTechnology', '-') }}</div>
</div>
<div class="graph-content-item" >
<div class="graph-content-item-label">{{ $t('overall.appFullName') }}:</div>
<div class="graph-content-item-value">{{ $_.get(entity, 'detailData.detail.category.appLongname', '-') }}</div>
</div>
<div class="graph-content-item" >
<div class="graph-content-item-label">{{ $t('config.dataSource.description') }}:</div>
<div class="graph-content-item-value">{{ $_.get(entity, 'detailData.detail.category.appDescription', '-') }}</div>
</div>
</template>
</div>
</div>
<!--关系拓展-->
<div class="digital-certificate graph-basic-info__block">
<loading :loading="entity.loading" size="small"></loading>
<div class="digital-certificate-header padding-b-10">
<div class="digital-certificate-header__icon graph-header__icon"></div>
<div class="graph-basic-info__block-title">
{{ $t('entity.graph.relationshipExpansion') }}
</div>
</div>
<div class="graph-basic-info__block-content" style="margin-top: -4px;">
<div v-for="item in relationList" :key="item.name">
<div class="graph-content-item graph-content-relationship-item" v-if="item.value === item.total">
<div class="graph-relationship-item-label">
<i class="margin-r-6" :class="item.icon"></i>
<span>{{ item.label }}</span>
</div>
<div class="graph-relationship-item-value">
<span class="margin-r-6">{{ item.value }}/{{ item.total }}</span>
<i class="cn-icon cn-icon-expand-relationship" :style="{color: iconColor(item.value, item.total)}"></i>
</div>
</div>
<div v-else class="graph-content-item graph-content-relationship-item">
<div class="graph-relationship-item-label">
<i class="graph-relationship-item-label-icon margin-r-6" :class="item.icon"></i>
<span>{{ item.label }}</span>
</div>
<div class="graph-relationship-item-value">
<span class="margin-r-6">{{ item.value }}/{{ item.total }}</span>
<i class="cn-icon cn-icon-expand-relationship graph-expand-relationship__icon" :style="{color: iconColor(item.value, item.total)}" @click="expandRelation(item.name)"></i>
</div>
</div>
</div>
</div>
</div>
<!--标签-->
<div v-if="entity.tags && entity.tags.length > 1" class="digital-certificate graph-basic-info__block">
<div class="digital-certificate-header padding-b-10">
<div class="digital-certificate-header__icon graph-header__icon"></div>
<div class="graph-basic-info__block-title">
{{ $t('entity.graph.tags') }}
</div>
</div>
<div class="entity-detail graph-basic-info__block-content">
<div class="graph-tag-list">
<div v-for="ic in entity.tags" :key="ic.value">
<div class="entity-tag graph-tag-item" :class="`entity-tag--level-two-${ic.type}`">
<span>{{ic.value}}</span>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import { copySelectionText, selectElementText } from '@/utils/tools'
import { entityType, riskLevelMapping } from '@/utils/constants'
import chartMixin from '@/views/charts2/chart-mixin'
import { dateFormatByAppearance } from '@/utils/date-util'
import { ref } from 'vue'
import _ from 'lodash'
import Loading from '@/components/common/Loading'
export default {
name: 'DomainDetail',
props: {
entity: {
type: Object
}
},
mixins: [chartMixin],
components: {
Loading
},
data () {
return {
entityType
}
},
computed: {
appRisk () {
return function (level) {
const m = riskLevelMapping.find(mapping => {
return mapping.value == level
})
return (m && m.name) || level
}
},
iconClass () {
let className
switch (this.entity.type) {
case ('ip'): {
className = 'cn-icon cn-icon-ip2'
break
}
case ('domain'): {
className = 'cn-icon cn-icon-domain2'
break
}
case ('app'): {
className = 'cn-icon cn-icon-app2'
break
}
default:
break
}
return className
},
handleDate () {
return function (key) {
const date = _.get(this.entity, key, '')
return date ? dateFormatByAppearance(date) : '-'
}
}
},
watch: {
entity: {
deep: true,
handler (n) {
this.handleDetailData(n)
}
}
},
mounted () {
this.handleDetailData(this.entity)
},
setup (props) {
const detailCards = ref([])
const relationList = ref([])
const tagList = ref([])
return {
detailCards,
relationList,
tagList
}
},
methods: {
/** 复制实体名称 */
copyEntityName () {
selectElementText(document.getElementById('entityName'))
if (copySelectionText()) {
this.$message.success(this.$t('tip.copySuccess'))
} else {
this.$message.error('Unknown error')
}
},
/** 修改关系拓展图标颜色,全部拓展浅灰色,否则深灰色 */
iconColor (length, total) {
if (length < total) {
if (length === 50) {
return 'rgba(57, 57, 57, 0.5)'
} else {
return 'rgba(57, 57, 57, 1)'
}
} else {
return 'rgba(57, 57, 57, 0.5)'
}
},
// 关闭右侧详情栏
closeBlock () {
this.$emit('closeBlock')
},
/** 构造地址,国-省市-市 */
handleLocation (data) {
const location = []
if (data.country) {
location.push(data.country)
}
if (data.province) {
location.push(data.province)
}
if (data.city) {
location.push(data.city)
}
return location.join(' - ')
},
/** 关系拓展 */
expandRelation (name) {
this.$emit('expandDetailList', name === 'subDomain' ? 'domain' : name, this.entity.name)
},
httpError (e) {
this.isNoData = false
this.showError = true
this.errorMsg = this.errorMsgHandler(e)
},
location (detailData) {
let location = ''
if (detailData) {
const data = detailData.detail
if (data) {
if (data.city) {
location = data.city
}
if (data.province) {
location = location ? `${data.province}, ${location}` : data.province
}
if (data.country) {
location = location ? `${data.country}, ${location}` : data.country
}
}
}
return location || '-'
},
handleDetailData (entity) {
const n = entity
const type = n.type || n.entityType
switch (type) {
case 'ip': {
this.detailCards = [
{ name: 'asn', label: this.$t('entities.asNumber'), value: _.get(n.detailData, 'detail.asn.asn', '-') },
{
name: 'asOrg',
label: this.$t('entities.asOrg'),
value: _.get(n.detailData, 'detail.asn.organization', '-')
},
{
name: 'isp',
label: this.$t('entities.graph.isp'),
value: _.get(n.detailData, 'detail.location.isp', '-')
},
{ name: 'location', label: this.$t('overall.location'), value: this.location(n.detailData) }
]
this.relationList = [
{
icon: 'cn-icon cn-icon-subdomain',
name: 'domain',
label: this.$t('entity.graph.resolveDomain'),
value: _.get(n.relatedEntityCount, 'domain.current', '0') || 0,
total: _.get(n.relatedEntityCount, 'domain.total', '0') || 0
},
{
icon: 'cn-icon cn-icon-app-name',
name: 'app',
label: this.$t('entities.tab.relatedApp'),
value: _.get(n.relatedEntityCount, 'app.current', '0') || 0,
total: _.get(n.relatedEntityCount, 'app.total', '0') || 0
}
]
break
}
case 'domain': {
const expireDate = _.get(n.detailData, 'detail.whois.expireDate', '')
const createDate = _.get(n.detailData, 'detail.whois.createDate', '')
this.detailCards = [
{
name: 'categoryName',
label: this.$t('entities.category'),
value: _.get(n.detailData, 'detail.category.name', '-')
},
{
name: 'categoryGroup',
label: this.$t('entities.group'),
value: _.get(n.detailData, 'detail.category.group', '-')
},
{
name: 'reputationLevel',
label: this.$t('entities.creditLevel2'),
value: _.get(n.detailData, 'detail.category.reputationLevel', '-')
},
{
name: 'expireDate',
label: this.$t('entities.graph.expirationDate'),
value: expireDate ? dateFormatByAppearance(expireDate) : '-'
},
{
name: 'registrarName',
label: this.$t('entities.registrar'),
value: _.get(n.detailData, 'detail.whois.registrarName', '-')
},
{
name: 'registrantOrg',
label: this.$t('entities.registry'),
value: _.get(n.detailData, 'detail.whois.registrantOrg', '-')
},
{
name: 'registrantCountry',
label: this.$t('entities.registrationCountry'),
value: _.get(n.detailData, 'detail.whois.registrantCountry', '-')
},
{
name: 'createDate',
label: this.$t('entities.registrationDate'),
value: createDate ? dateFormatByAppearance(createDate) : '-'
},
{
name: 'email',
label: this.$t('entities.registryEmail'),
value: _.get(n.detailData, 'detail.whois.email', '-')
}
]
this.relationList = [
{
icon: 'cn-icon cn-icon-resolve-ip',
name: 'ip',
label: this.$t('entities.graph.resolveIp'),
value: _.get(n.relatedEntityCount, 'ip.current', '0') || 0,
total: _.get(n.relatedEntityCount, 'ip.total', '0') || 0
},
{
icon: 'cn-icon cn-icon-subdomain',
name: 'subDomain',
label: this.$t('entities.subdomain'),
value: _.get(n.relatedEntityCount, 'subDomain.current', '0') || 0,
total: _.get(n.relatedEntityCount, 'subDomain.total', '0') || 0
},
{
icon: 'cn-icon cn-icon-app-name',
name: 'app',
label: this.$t('entities.tab.relatedApp'),
value: _.get(n.relatedEntityCount, 'app.current', 0) || 0,
total: _.get(n.relatedEntityCount, 'app.total', 0) || 0
}
]
break
}
case 'app': {
this.detailCards = [
{
name: 'appCategory',
label: this.$t('entities.category'),
value: _.get(n.detailData, 'detail.category.appCategory', '-')
},
{
name: 'appSubcategory',
label: this.$t('entities.subcategory'),
value: _.get(n.detailData, 'detail.category.appSubcategory', '-')
},
{
name: 'appRisk',
label: this.$t('entities.riskLevel'),
value: _.get(n.detailData, 'detail.category.appRisk', '-')
},
{
name: 'appTechnology',
label: this.$t('overall.technology'),
value: _.get(n.detailData, 'detail.category.appTechnology', '-')
},
{
name: 'appLongname',
label: this.$t('overall.appFullName'),
value: _.get(n.detailData, 'detail.category.appLongname', '-')
},
{
name: 'appDescription',
label: this.$t('config.dataSource.description'),
value: _.get(n.detailData, 'detail.category.appDescription', '-')
}
]
this.relationList = [
{
icon: 'cn-icon cn-icon-resolve-ip',
name: 'ip',
label: this.$t('entities.tab.relatedIp'),
value: _.get(n.relatedEntityCount, 'ip.current', '0') || 0,
total: _.get(n.relatedEntityCount, 'ip.total', '0') || 0
},
{
icon: 'cn-icon cn-icon-subdomain',
name: 'domain',
label: this.$t('entities.graph.relatedDomain'),
value: _.get(n.relatedEntityCount, 'domain.current', '0') || 0,
total: _.get(n.relatedEntityCount, 'domain.total', '0') || 0
}
]
}
}
}
}
}
</script>

View File

@@ -1,13 +1,101 @@
<template>
<div>
<loading :loading="entity.loading" size="small"></loading>
<div class="graph-list-header">
<div>
<div class="graph-list-header-title">
<i class="cn-icon cn-icon-resolve-ip graph-list-header-icon"></i>
<span>{{ entity.sourceType === 'domain' ? $t('entities.graph.resolveIp') : $t('entities.tab.relatedIp') }}</span>
</div>
<div class="graph-list-header-number">
{{ $t('entity.graph.associatedCount') }}:&nbsp;<span>{{entity.count}}</span>
</div>
</div>
<i class="cn-icon cn-icon-close graph-close" @click="closeBlock"></i>
</div>
<div class="graph-list-expand-btn-block">
<div class="graph-list-expand-btn" :class="{ 'graph-list-expand-btn--disabled': expandBtnDisable }" style="display: inline-flex;" @click="expandList">
<i class="cn-icon cn-icon-expand-continue graph-expand-continue"></i>
{{ $t('entity.graph.continueToExpand') }}
</div>
</div>
<div>
<div class="digital-certificate">
<div class="digital-certificate-header padding-b-16">
<div class="digital-certificate-header__icon graph-header__icon"></div>
<div class="graph-list-content-header ">
{{ $t('entity.graph.expandedEntityCount') }}:&nbsp;&nbsp;
<span>{{ entity.listData ? entity.listData.length : 0 }}</span>
</div>
</div>
<div class="graph-list-content">
<div v-for="(item, index) in entity.listData" :key="index" @mouseenter="onMouseenter(item)">
<div class="graph-list-item-ip"><span @click="expandDetail">{{ item.vertex }}</span></div>
<div class="graph-list-item-block">
<div class="graph-list-item padding-b-4">
<div class="graph-list-item-label">{{ $t('overall.location') }}:</div>
<div class="graph-list-item-value graph-list-item-value1" style="display: flex;align-items: center;">
<!-- <img :src="require(`../../../../public/images/flag/${item.flag}.svg`)" class="graph-list-country-flag"/>-->
<span>{{ $_.get(item.detail, 'location.city', '-') }}</span>
</div>
</div>
<div class="graph-list-item">
<div class="graph-list-item-label">ASN:</div>
<div class="graph-list-item-value">{{ $_.get(item.detail, 'asn.asn', '-') }}</div>
</div>
</div>
<div class="graph-list-dividing-line" v-if="index !== entity.listData.length - 1"></div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import Loading from '@/components/common/Loading'
import { scrollToTop } from '@/utils/tools'
export default {
name: 'IpList'
name: 'IpList',
props: {
entity: {
type: Object
}
},
components: {
Loading
},
computed: {
expandBtnDisable () {
return !((this.entity.listData ? this.entity.listData.length : 0) < this.entity.count && this.entity.count > 10 && (this.entity.listData && this.entity.listData.length < 50))
}
},
methods: {
closeBlock () {
this.$emit('closeBlock')
},
expandDetail () {
this.$emit('expandDetail', 'ipDetail')
},
onMouseenter (val) {
// 鼠标移动过graph列表名称时graph图的分支图形会变大一点
this.$emit('mouseenter', val)
},
expandList () {
// 继续拓展ip列表传递信息调用接口
this.$emit('expandList', this.entity.sourceName, this.entity.nodeId)
}
},
mounted () {
this.$nextTick(() => {
scrollToTop()
})
}
}
</script>
<style scoped>
</style>

View File

@@ -9,77 +9,78 @@
<div class="cn-entity__case">
<div class="cn-entity__icon"><i :class="iconClass"></i></div>
<div class="cn-entity__row">
<div class="cn-entity__header">
{{ entityData.ipAddr || entityData.domainName || entityData.appName || 'Unknown' }}
<!-- <div class="cn-entity__header" style="display: flex">-->
<!-- <span class="cn-entity__header-title">{{ entityData.ipAddr || entityData.domainName || entityData.appName || 'Unknown' }}</span>-->
<!-- <span class="entity-detail" style="display: flex">-->
<!-- <span style="width: 62px;" class="entity-tag entity-tag&#45;&#45;small entity-tag&#45;&#45;level-two-positive margin-r-6">信息技术</span>-->
<!-- <span style="width: 50px;" class="entity-tag entity-tag&#45;&#45;small entity-tag&#45;&#45;level-two-normal margin-r-6">互联网</span>-->
<!-- </span>-->
<!--标签-->
<div class="cn-entity__header" style="display: flex;align-items: center">
<span class="cn-entity__header-title">{{ entityData.entityValue || 'Unknown' }}</span>
<span class="entity-detail" style="display: flex;margin-left: 6px;margin-top: 1px;">
<span v-for="(item, index) in levelTwoTags" :key="index" class="entity-tag entity-tag--small margin-r-10" :class="`entity-tag--level-two-${item.type}`">
{{ item.value }}
</span>
</span>
</div>
<div class="cn-entity__body">
<div class="body__basic-info">
<div class="basic-info">
<template v-if="entityData.entityType === 'ip'">
<div class="basic-info__item">
<i class="cn-icon cn-icon-country"></i>
<span>{{ $t('overall.country') }}&nbsp;:&nbsp;&nbsp;</span>
<span>{{ entityData.ipLocationCountry || '-' }}</span>
<span class="row-item-label">{{ $t('overall.country') }}&nbsp;:&nbsp;&nbsp;</span>
<span class="row-item-value">{{ entityData.location ? entityData.location.country : '-' }}</span>
</div>
<div class="basic-info__item">
<i class="cn-icon cn-icon-position"></i>
<span>{{ $t('overall.region') }}&nbsp;:&nbsp;&nbsp;</span>
<span>{{ ipLocationRegion(entityData) }}</span>
<span class="row-item-label">{{ $t('overall.region') }}&nbsp;:&nbsp;&nbsp;</span>
<span class="row-item-value">{{ entityData.location ? ipLocationRegion(entityData.location) : '-' }}</span>
</div>
<div class="basic-info__item">
<i class="cn-icon cn-icon-cloud"></i>
<span>{{ $t('entities.asn') }}&nbsp;:&nbsp;&nbsp;</span>
<span>{{ entityData.ipAsn || '-' }}</span>
<span class="row-item-label">{{ $t('entities.asn') }}&nbsp;:&nbsp;&nbsp;</span>
<span class="row-item-value">{{ entityData.asn ? entityData.asn.asn : '-' }}</span>
</div>
</template>
<template v-else-if="entityData.entityType === 'domain'">
<div class="basic-info__item">
<i class="cn-icon cn-icon-category"></i>
<span>{{ $t('entities.domainDetail.categoryGroup') }}&nbsp;:&nbsp;&nbsp;</span>
<span>{{ entityData.domainCategoryGroup || '-' }}</span>
</div>
<div class="basic-info__item">
<i class="cn-icon cn-icon-sub-category"></i>
<span>{{ $t('entities.category') }}&nbsp;:&nbsp;&nbsp;</span>
<span>{{ entityData.domainCategory || '-' }}</span>
<span class="row-item-label">{{ $t('entities.category') }}&nbsp;:&nbsp;&nbsp;</span>
<span class="row-item-value">{{ entityData.category ? entityData.category.name : '-' }}</span>
</div>
<div class="basic-info__item">
<i class="cn-icon cn-icon-credit"></i>
<span>{{ $t('entities.reputationLevel') }}&nbsp;:&nbsp;&nbsp;</span>
<span>{{ entityData.domainReputationScore || '-' }}</span>
<i class="cn-icon cn-icon-category"></i>
<span class="row-item-label">{{ $t('entities.subcategory') }}&nbsp;:&nbsp;&nbsp;</span>
<span class="row-item-value">{{ entityData.category ? entityData.category.group : '-' }}</span>
</div>
<div class="basic-info__item">
<i class="cn-icon cn-icon-credit-rating"></i>
<span class="row-item-label">{{ $t('entities.reputationLevel') }}&nbsp;:&nbsp;&nbsp;</span>
<span class="row-item-value">{{ entityData.category ? entityData.category.reputationLevel : '-' }}</span>
</div>
</template>
<template v-else-if="entityData.entityType === 'app'">
<div class="basic-info__item">
<i class="cn-icon cn-icon-id"></i>
<span>{{ $t('entities.category') }}&nbsp;:&nbsp;&nbsp;</span>
<span>{{ entityData.appCategory || '-' }}</span>
<span class="row-item-label">{{ $t('entities.category') }}&nbsp;:&nbsp;&nbsp;</span>
<span class="row-item-value">{{ entityData.category ? entityData.category.appCategory : '-' }}</span>
</div>
<div class="basic-info__item">
<i class="cn-icon cn-icon-category"></i>
<span>{{ $t('entities.subcategory') }}&nbsp;:&nbsp;&nbsp;</span>
<span>{{ entityData.appSubcategory || '-' }}</span>
<span class="row-item-label">{{ $t('entities.subcategory') }}&nbsp;:&nbsp;&nbsp;</span>
<span class="row-item-value">{{ entityData.category ? entityData.category.appSubcategory : '-' }}</span>
</div>
<div class="basic-info__item">
<i class="cn-icon cn-icon-sub-category"></i>
<span>{{ $t('entities.risk') }}&nbsp;:&nbsp;&nbsp;</span>
<span>{{ appRisk(entityData.appRisk) || '-' }}</span>
<span class="row-item-label">{{ $t('entities.risk') }}&nbsp;:&nbsp;&nbsp;</span>
<span class="row-item-value">{{ entityData.category ? appRisk(entityData.category.appRisk) : '-' }}</span>
</div>
</template>
<!-- 通用字段 -->
<div class="basic-info__item">
<div class="item__box">
<i class="cn-icon cn-icon-rise"></i>
<span>{{ $t('entities.sentThroughput') }}&nbsp;:&nbsp;&nbsp;</span>
<span>
<span class="row-item-label">{{ $t('entities.sentThroughput') }}&nbsp;:&nbsp;&nbsp;</span>
<span class="row-item-value">
{{
entityData.bytesSentRate ? unitConvert(entityData.bytesSentRate, unitTypes.byte).join(' ') + 'ps' : '-'
unitConvert(entityData.bytesSentRate, unitTypes.byte).join(' ') !=='- ' ? unitConvert(entityData.bytesSentRate, unitTypes.byte).join(' ') + 'ps' : '-'
}}
</span>
<!-- 曲线-->
@@ -107,11 +108,9 @@
<div class="basic-info__item">
<div class="item__box">
<i class="cn-icon cn-icon-fall"></i>
<span>{{ $t('entities.receivedThroughput') }}&nbsp;:&nbsp;&nbsp;</span>
<span>
{{
entityData.bytesReceivedRate ? unitConvert(entityData.bytesReceivedRate, unitTypes.byte).join(' ') + 'ps' : '-'
}}
<span class="row-item-label">{{ $t('entities.receivedThroughput') }}&nbsp;:&nbsp;&nbsp;</span>
<span class="row-item-value">
{{ unitConvert(entityData.bytesReceivedRate, unitTypes.byte).join(' ') !== '- ' ? unitConvert(entityData.bytesReceivedRate, unitTypes.byte).join(' ') + 'ps' : '-' }}
</span>
<div class="item-box-loading">
<loading :loading="loading" size="small"></loading>
@@ -133,34 +132,18 @@
</div>
</div>
</div>
<!--新版实体列表改版去除这一段-->
<div class="basic-info__item">
<i class="cn-icon cn-icon-entity-alert"></i>
<span>{{ $t('entities.recentAlert') }}&nbsp;:&nbsp;&nbsp;</span>
<span>{{ entityData.performanceCount }}</span>
</div>
<div class="basic-info__item">
<i class="cn-icon cn-icon-safe"></i>
<span>{{ $t('entities.recentSecurity') }}&nbsp;:&nbsp;&nbsp;</span>
<span>{{ entityData.securityCount }}</span>
</div>
<!--新版实体列表改版去除这一段-->
</div>
<div class="show-detail" @click="showDetail">
{{ $t('overall.detail') }}>
</div>
<!-- 新版实体列表改版后续记得解开-->
<!-- <div class="show-detail">-->
<!-- <div @click="showDetail"><i class="cn-icon cn-icon-detail"></i>{{ $t('overall.detail') }} ></div>-->
<!-- <div><i class="cn-icon cn-icon-graph"></i>{{ $t('entities.graph') }} ></div>-->
<!-- </div>-->
</div>
</div>
</div>
<div class="new-show-detail">
<div @click="showDetail"><i class="cn-icon cn-icon-detail"></i>{{ $t('overall.detail') }} ></div>
<div @click="showGraph"><i class="cn-icon cn-icon-graph"></i>{{ $t('entities.graph') }} ></div>
</div>
<el-collapse-transition>
<div class="cn-entity__detail-overview" v-if="!isCollapse">
<el-divider></el-divider>
<detail-overview :entity="entityData" :time-filter="timeFilter"/>
<detail-overview :entity="entityData" :time-filter="timeFilter" @reloadEntity="getEntity" />
</div>
</el-collapse-transition>
</div>
@@ -172,6 +155,10 @@ import DetailOverview from '@/views/entityExplorer/entityList/detailOverview/Det
import entityListMixin from './entityListMixin'
import relatedServer from '@/mixins/relatedServer'
import Loading from '@/components/common/Loading'
import axios from 'axios'
import { api } from '@/utils/api'
import { entityDetailTags, psiphon3IpType } from '@/utils/constants'
import _ from 'lodash'
export default {
name: 'Row',
@@ -188,35 +175,109 @@ export default {
data () {
return {
loading: false,
isCollapse: true // 是否是折叠状态
isCollapse: true, // 是否是折叠状态
levelTwoTags: []
}
},
computed: {
ipLocationRegion () {
return function (entityData) {
const hasProvinceAndCity =
entityData.ipLocationProvince &&
entityData.ipLocationCity &&
entityData.ipLocationProvince !== 'null' &&
entityData.ipLocationCity !== 'null'
entityData.province &&
entityData.city &&
entityData.province !== 'null' &&
entityData.city !== 'null'
const hasProvince =
entityData.ipLocationProvince &&
entityData.ipLocationProvince !== 'null'
entityData.province &&
entityData.province !== 'null'
const hasCity =
entityData.ipLocationCity && entityData.ipLocationCity !== 'null'
entityData.city && entityData.city !== 'null'
if (hasProvinceAndCity) {
return `${entityData.ipLocationProvince}, ${entityData.ipLocationCity}`
return `${entityData.province}, ${entityData.city}`
} else if (hasProvince) {
return entityData.ipLocationProvince
return entityData.province
} else if (hasCity) {
return entityData.ipLocationCity
return entityData.city
} else {
return '-'
}
}
}
},
mounted () {
this.initData()
this.initTagsData()
},
methods: {
initData () {
let url = ''
switch (this.entity.entityType) {
case ('domain'): {
url = api.entity.entityList.domainBasicInfo
break
}
case ('ip'): {
url = api.entity.entityList.ipBasicInfo
break
}
case ('app'): {
url = api.entity.entityList.appBasicInfo
break
}
}
axios.get(`${url}?resource=${this.entity.entityValue}`).then(response => {
this.$nextTick(() => {
this.entityData = { ...response.data.data, ...this.entity }
})
})
},
initTagsData () {
let url = ''
switch (this.entity.entityType) {
case ('domain'): {
url = api.entity.entityList.domainTags
break
}
case ('ip'): {
url = api.entity.entityList.ipTags
break
}
case ('app'): {
url = api.entity.entityList.appTags
break
}
}
axios.get(`${url}?resource=${this.entity.entityValue}`).then(responese => {
const res = responese.data
if (res.code === 200) {
Object.keys(res.data).forEach(k => {
if (k !== 'userDefinedTags' && res.data[k]) {
Object.keys(res.data[k]).forEach(k2 => {
const find = entityDetailTags[this.entity.entityType].find(t => t.name === k2)
if (find) {
this.levelTwoTags.push({ key: k2, value: this.tagValueHandler(k, k2, res.data[k][k2]), type: find.type })
}
})
}
})
if (_.isArray(res.data.userDefinedTags)) {
this.levelTwoTags = _.concat(this.levelTwoTags, res.data.userDefinedTags.map(tag => ({ value: tag.tagValue, type: 'normal' })))
}
this.hideTagArea = _.isEmpty(this.levelTwoTags)
}
})
},
tagValueHandler (k, k2, value) {
if (k === 'psiphon3Ip') {
if (k2 === 'type') {
const find = psiphon3IpType.find(t => t.value === value)
if (find) {
return find.name
}
}
}
return value
},
/* 切换折叠状态 */
switchCollapse () {
this.isCollapse = !this.isCollapse
@@ -225,6 +286,9 @@ export default {
/* 设为折叠状态 */
collapse () {
this.isCollapse = true
},
getEntity (data) {
this.entityData = { ...data }
}
}
}

View File

@@ -4,23 +4,23 @@
<div class="overview__content">
<div class="overview__row">
<div class="row__label row__label--width130">APP ID</div>
<div class="row__content">{{entity.appId|| '-'}}</div>
<div class="row__content">{{entity.category.appId || '-'}}</div>
</div>
<div class="overview__row">
<div class="row__label row__label--width130">{{$t('entities.category')}}</div>
<div class="row__content">{{entity.appCategory|| '-'}}</div>
<div class="row__content">{{entity.category.appCategory || '-'}}</div>
</div>
<div class="overview__row">
<div class="row__label row__label--width130">{{$t('entities.subcategory')}}</div>
<div class="row__content">{{entity.appSubcategory || '-'}}</div>
<div class="row__content">{{entity.category.appSubcategory || '-'}}</div>
</div>
<div class="overview__row">
<div class="row__label row__label--width130">{{$t('entities.riskLevel')}}</div>
<div class="row__content">{{appRisk(entity.appRisk) || '-'}}</div>
<div class="row__content">{{appRisk(parseInt(entity.category.appRisk)) || '-'}}</div>
</div>
<div class="overview__row">
<div class="row__label row__label--width130">{{$t('overall.remark')}}</div>
<div class="row__content">{{entity.appDescription || '-'}}</div>
<div class="row__content">{{entity.category.appDescription || '-'}}</div>
</div>
</div>
</div>
@@ -30,116 +30,110 @@
<loading :loading="loadingTraffic" size="small" inner-style="left: 8.75rem;" style="width: 50%;"></loading>
<div class="overview__row">
<div class="row__label row__label--width130">{{$t('overall.peak')}}</div>
<div class="row__content">{{unitConvert(entityData.max, unitTypes.byte).join(' ')}}/s</div>
<div class="row__content">
{{unitConvert(entityData.max, unitTypes.byte).join(' ') !== '- ' ? unitConvert(entityData.max, unitTypes.byte).join(' ') + '/s' : '-'}}
</div>
</div>
<div class="overview__row">
<div class="row__label row__label--width130">{{$t('overall.mean')}}</div>
<div class="row__content">{{unitConvert(entityData.avg, unitTypes.byte).join(' ')}}/s</div>
<div class="row__content">
{{unitConvert(entityData.avg, unitTypes.byte).join(' ') !== '- ' ? unitConvert(entityData.avg, unitTypes.byte).join(' ') + '/s' : '-'}}
</div>
</div>
<div class="overview__row">
<div class="row__label row__label--width130">{{$t('overall.throughput')}}</div>
<div class="row__contents">
<div class="row__content">
<div class="row__charts-msg">{{$t('overall.sent')}}{{unitConvert(entityData.bytesSentRate, unitTypes.byte).join(' ')}}ps</div>
<div class="row__charts-msg">{{$t('overall.sent')}}
{{unitConvert(entityData.bytesSentRate, unitTypes.byte).join(' ') !== '- ' ? unitConvert(entityData.bytesSentRate, unitTypes.byte).join(' ') + 'ps' : '-'}}
</div>
<!-- 曲线-->
<div class="row__content-loading">
<div class="row__charts" :id="`entityDetailSend${entity.appName}`" ></div>
<div class="row__charts" :id="`entityDetailSend${entity.entityValue}`" ></div>
</div>
</div>
<div class="row__content">
<div class="row__charts-msg">{{$t('overall.received')}}{{unitConvert(entityData.bytesReceivedRate, unitTypes.byte).join(' ')}}ps</div>
<div class="row__content row__content-accept">
<div class="row__charts-msg">{{$t('overall.received')}}
{{unitConvert(entityData.bytesReceivedRate, unitTypes.byte).join(' ') !== '- ' ? unitConvert(entityData.bytesReceivedRate, unitTypes.byte).join(' ') + 'ps' : '-'}}
</div>
<!-- 曲线-->
<div class="row__content-loading">
<div class="row__charts" :id="`entityDetailReceived${entity.appName}`" ></div>
<div class="row__charts" :id="`entityDetailReceived${entity.entityValue}`" ></div>
</div>
</div>
</div>
</div>
<div class="overview__row">
<div class="row__label row__label--width130">{{$t('entities.networkQualityRating')}}</div>
<div style="position: relative;">
<div class="entity-score" v-if="!loadingNetworkQuality">
<div class="circle-icon" v-if="score <= 2 || score === '-'" :class="{'data-score-red': score <= 2 || score === '-'}" ></div>
<div class="circle-icon" v-else-if="score <= 4" :class="{'data-score-yellow': score <= 4}" ></div>
<div class="circle-icon" v-else-if="score <= 6" :class="{'data-score-green': score <= 6}" ></div>
Score:{{score}}
</div>
<loading :loading="loadingNetworkQuality" size="small" style="left: 1rem;width: 50%;"></loading>
</div>
</div>
</div>
</div>
<div class="overview-item">
<div class="overview__title">{{$t('overall.relationship')}}</div>
<div class="overview__content domain__content">
<div class="overview__tags domain__tags" ref="relationship">
<div class="overview__domain-tabs overview__domain-tabs-loading">
<loading :loading="loadingRelationshipOne" size="small" inner-style="left: 1rem;" style="width: 50%;"></loading>
<div class="overview__domain-tab">
<div class="overview__tag domain__tag">
<span class="tag__value">{{relationshipDataOne.length}}</span>
<span class="tag__desc">{{$t('entities.relatedDomains')}}</span>
</div>
<div class="overview__tag domain__tag-list" v-show="item.show" v-for="(item, index) in relationshipDataOne" :key="index">
<span class="tag__desc">{{item.domain}}</span>
</div>
<div class="overview__tags domain__tags" ref="relationship"></div>
<div class="overview__row">
<div class="row__label row__label--width130">{{$t('entities.tab.relatedDomain')}}</div>
<div class="row__content overview__row-related">
<div v-if="loadingRelationshipOne" style="position: relative;width: 450px;">
<loading :loading="loadingRelationshipOne" size="small" style="left: 1rem;"></loading>
</div>
<div class="overview__domain-btn">
<div class="overview__domain-more" @click="more(relationshipDataOne, 1)" v-if="relationshipShowOne">...</div>
<div class="overview__domain-more-tabs show-more-list" v-if="relationshipShowMoreOne" v-ele-click-outside="mouseout">
<div class="domain-more-tab" v-for="item in relationshipMoreDataOne" :key="item">
<span v-if="item.domain" :title="item.domain">{{item.domain}}</span>
</div>
</div>
<div class="data-item" v-show="item.show" v-for="(item, index) in relationshipDataOne" :key="index">
{{item.value}}
</div>
<div v-if="relationshipDataOne.length===0 && !loadingRelationshipOne">-</div>
<div v-if="relationshipShowOne">
<el-popover placement="right-end" trigger="click" show-arrow="false" offset="20">
<template #reference>
<div class="data-item show-more-related">...</div>
</template>
<div v-for="(item, index) in relationshipDataOne" :key="index">{{item.value}}</div>
</el-popover>
</div>
</div>
<div class="overview__domain-tabs overview__domain-tabs-loading">
<loading :loading="loadingRelationshipTwo" size="small" inner-style="left: 1rem;" style="width: 50%;"></loading>
<div class="overview__domain-tab">
<div class="overview__tag domain__tag">
<span class="tag__value">{{relationshipDataTwo.length}}</span>
<span class="tag__desc">{{$t('entities.relatedServerIp')}}</span>
</div>
<div class="overview__tag domain__tag-list" v-show="item.show" v-for="(item, index) in relationshipDataTwo" :key="index">
<span class="tag__desc">{{item.ip}}</span>
</div>
</div>
<div class="overview__row overview__row-related">
<div class="row__label row__label--width130">{{$t('entities.tab.relatedIp')}}</div>
<div class="row__content">
<div v-if="loadingRelationshipTwo" style="position: relative;width: 450px;">
<loading :loading="loadingRelationshipTwo" size="small" style="left: 1rem;"></loading>
</div>
<div class="overview__domain-btn">
<div class="overview__domain-more" @click="more(relationshipDataTwo, 2)" v-if="relationshipShowTwo">...</div>
<div class="overview__domain-more-tabs show-more-list" v-if="relationshipShowMoreTwo" v-ele-click-outside="mouseout">
<div class="domain-more-tab" v-for="item in relationshipMoreDataTwo" :key="item">
<span v-if="item.ip" :title="item.ip">{{item.ip}}</span>
</div>
</div>
<div class="data-item" v-show="item.show" v-for="(item, index) in relationshipDataTwo" :key="index">
{{item.value}}
</div>
<div v-if="relationshipDataTwo.length===0 && !loadingRelationshipTwo">-</div>
<div v-if="relationshipShowTwo">
<el-popover class="entity-expand-detail" placement="right-end" trigger="click" show-arrow="false" offset="20">
<template #reference>
<div class="data-item show-more-related">...</div>
</template>
<div v-for="(item, index) in relationshipDataTwo" :key="index">{{item.value}}</div>
</el-popover>
</div>
</div>
</div>
</div>
</div>
<div class="overview-item">
<div class="overview__title">{{$t('overall.networkQuality')}}</div>
<div class="overview__content overview__content-loading-net">
<loading :loading="loadingNetworkQuality" size="small"></loading>
<div class="overview__row overview__row--single-value">
<chart-single-value
v-for="(chartInfo, i) in singleValues.chartInfos"
:chart-info="chartInfo"
:chart-data="singleValues.chartDatas[i]"
:key="i"
class="cn-chart__single-value--detail-overview"
></chart-single-value>
</div>
</div>
</div>
<div class="overview-item">
<div class="overview__title">{{$t('entities.accessLink')}}</div>
<div class="overview__content">
<div class="overview__tags">
<div class="overview__tag overview__tag-loading">
<loading :loading="loadingOut" size="small"></loading>
<span class="tag__desc">{{$t('entities.outLinkTrafficPercentage')}}</span>
<span class="tag__value">{{entityData.linkOutId ? entityData.linkOutId : '-'}},</span>
<span class="tag__desc">{{$t('entities.percentage')}}</span>
<span class="tag__value">{{entityData.linkOutPercent ? unitConvert(entityData.linkOutPercent, unitTypes.percent).join(' ') : '-'}}</span>
</div>
<div class="overview__tag overview__tag-loading">
<loading :loading="loadingIn" size="small"></loading>
<span class="tag__desc">{{$t('entities.inLinkTrafficPercentage')}}</span>
<span class="tag__value">{{entityData.linkInId ? entityData.linkInId : '-'}},</span>
<span class="tag__desc">{{$t('entities.percentage')}}</span>
<span class="tag__value">{{entityData.linkInPercent ? unitConvert(entityData.linkInPercent, unitTypes.percent).join(' ') : '-'}}</span>
</div>
</div>
</div>
</div>
<div class="overview-item">
<div class="overview__title">{{$t('overall.alert')}}</div>
<div class="overview__content overview__content-loading">
@@ -148,20 +142,17 @@
<span class="no-recent-alerts"><i class="el-icon-success"></i>{{$t('relationShip.noRecentAlerts')}}</span>
</div>
<div class="overview__row" v-if="performanceData.length > 0">
<div class="row__label">{{$t('entities.recentAlert')}}</div>
<div class="row__label row__label--width130">{{$t('entities.recentAlert')}}</div>
<div class="row__content">{{entityData.performanceNum}}</div>
</div>
<div class="overview__row overview__row--small-font" v-for="(performance, index) in entityData.performanceList" :key="index">
<div class="row__label row__label--width160">{{dateFormatByAppearance(performance.startTime) || '-'}}</div>
<div class="row__content row__content--width200">
<div class="overview__row overview__row--small-font" v-for="(performance, index) in entityData.performanceList" :key="index">
<div class="row__label row__label--width130">{{dateFormatByAppearance(performance.startTime) || '-'}}</div>
<div class="row__content row__content--width90">
<div class="alert-level-tag alert-level-tag--high" :class="iconClass(performance)">{{performance.eventSeverity}}</div>
<div>{{performance.eventType}}</div>
</div>
<div class="row__content-loading" style="position: relative;" >
<loading :loading="!loadingAlert && loadingPerformance[index]?loadingPerformance[index]:false" :id="`loading${entity.ipAddr}_${index}`" size="small"></loading>
<div class="row__charts" :id="`entityPerformanceChart${entity.appName}_${index}`"></div>
<div class="row__content-loading" style="position: relative;">
<div class="performance-event-remark">{{performance.eventType}}</div>
</div>
<div class="row__desc"></div>
</div>
<div class="overview__row overview__row--small-font" v-if="performanceData && performanceData.length > 5">
@@ -177,15 +168,26 @@
<span class="no-recent-alerts"><i class="el-icon-success"></i>{{$t('relationShip.noRecentAlerts')}}</span>
</div>
<div class="overview__row" v-if="securityData.length > 0">
<div class="row__label">{{$t('entities.recentSecurity')}}</div>
<div class="row__label row__label--width130">{{$t('entities.recentSecurity')}}</div>
<div class="row__content">{{entityData.securityNum}}</div>
</div>
<div class="overview__row overview__row--small-font" v-for="(security, index) in entityData.securityList" :key="index">
<div class="row__label row__label--width160">{{dateFormatByAppearance(security.startTime) || '-'}}</div>
<div class="row__content row__content--width200">
<div class="overview__row overview__row--small-font" v-for="(security, index) in entityData.securityList" :key="index">
<div class="row__label row__label--width130">{{dateFormatByAppearance(security.startTime) || '-'}}</div>
<div class="row__content row__content--width90">
<div class="alert-level-tag alert-level-tag--high" :class="iconClass(security)">{{security.eventSeverity}}</div>
<div>{{security.securityType}}</div>
</div>
<div class="cn-detection__header">
<i class="cn-icon cn-icon-attacker"></i>
<span>{{ security.offenderIp }}</span>
<div class="domain" v-if="security.offenderIp===security.serverIp">{{ security.domain }}</div>
<span class="line">-------</span>
<span class="circle"></span>
<i class="cn-icon cn-icon-attacked"></i>
<span>{{ security.victimIp }}</span>
<div class="domain" v-if="security.victimIp===security.serverIp">{{ security.domain }}</div>
</div>
<div class="row__desc"></div>
</div>
<div class="overview__row overview__row--small-font" v-if="securityData && securityData.length > 5">
@@ -213,34 +215,38 @@ import { unitTypes } from '@/utils/constants'
import unitConvert from '@/utils/unit-convert'
import Chart from '@/views/charts/Chart'
import _ from 'lodash'
import ChartSingleValue from '@/views/charts/charts/ChartSingleValue'
import { get } from '@/utils/http'
import relatedServer from '@/mixins/relatedServer'
import { getSecond, getMillisecond } from '@/utils/date-util'
import { dateFormatByAppearance, getMillisecond, getSecond } from '@/utils/date-util'
import Loading from '@/components/common/Loading'
import { ref } from 'vue'
export default {
name: 'App',
mixins: [entityDetailMixin, relatedServer],
components: {
Chart,
Loading,
ChartSingleValue
Loading
},
data () {
return {
// entityData: {}
entityType: 'app',
trafficUrl: api.entityAppDetailTraffic,
// trafficUrl: api.entityAppDetailTraffic,
trafficUrl: api.entity.entityList.appThroughput,
relationUrl: api.entityAppDetailRelation,
networkQuantityUrl: api.entityAppDetailNetworkQuantity,
// networkQuantityUrl: api.entityAppDetailNetworkQuantity,
networkQuantityUrl: api.entity.entityList.appPerformance,
linkInUrl: api.entityAppDetailLinkIn,
linkOutUrl: api.entityAppDetailLinkOut,
performanceUrl: api.entityAppDetailPerformance,
securityUrl: api.entityAppDetailSecurity,
trafficUrlMap: api.entityAppDetailTrafficMap,
relatedServerDomainUrl: api.entityAppRelatedServerDomain,
relatedServerIpUrl: api.entityAppRelatedServerIp,
// performanceUrl: api.entityAppDetailPerformance,
performanceUrl: api.entity.entityList.appEventPerformance,
securityUrl: api.entity.entityList.appSecurity,
// securityUrl: api.entityAppDetailSecurity,
// trafficUrlMap: api.entityAppDetailTrafficMap,
trafficUrlMap: api.entity.entityList.appTrafficMap,
relatedServerDomainUrl: api.entity.entityList.appRelatedDomain,
relatedServerIpUrl: api.entity.entityList.appRelatedIp,
chartData: null,
listMode: 'list',
singleValues: {
@@ -296,34 +302,33 @@ export default {
i18n: 'entities.pktRetransPercent'
}
],
chartDatas: [null, null, null, null, null],
loadingTraffic: false,
loadingRelationshipOne: false,
loadingRelationshipTwo: false,
loadingNetworkQuality: false,
loadingOut: false,
loadingIn: false,
loadingAlert: false,
loadingSecurityEvents: false,
loadingMap: false
}
chartDatas: [null, null, null, null, null]
},
loadingTraffic: false,
loadingRelationshipOne: false,
loadingRelationshipTwo: false,
loadingNetworkQuality: false,
loadingOut: false,
loadingIn: false,
loadingAlert: false,
loadingSecurityEvents: false,
loadingMap: false
}
},
methods: {
getMillisecond,
dateFormatByAppearance,
getQueryParams () {
const queryParams = {
startTime: getSecond(this.timeFilter.startTime),
endTime: getSecond(this.timeFilter.endTime),
appName: this.entity.appName
return {
// startTime: getSecond(this.timeFilter.startTime),
// endTime: getSecond(this.timeFilter.endTime),
resource: this.entity.entityValue
}
return queryParams
},
getPerformanceQueryParams () {
const queryParams = {
appName: this.entity.appName
return {
appName: this.entity.entityValue
}
return queryParams
},
handleRelationData (result) {
this.entityData.domainCount = result.domainCount
@@ -353,10 +358,11 @@ export default {
})
},
setup (props) {
const entityData = ref({ ...props.entity })
return {
chart: {
params: {
url: '/interface/entity/detail/app/trafficMap?startTime={{startTime}}&endTime={{endTime}}&country={{country}}&appName={{appName}}',
url: `${api.entity.entityList.appTrafficMap}?resource={{resource}}&country={{country}}`,
unitType: 'number',
valueColumn: 'sessions'
},
@@ -366,7 +372,8 @@ export default {
entityCopy: {
..._.cloneDeep(props.entity)
},
unitConvert
unitConvert,
entityData
}
}
}

View File

@@ -1,13 +1,13 @@
<template>
<div class="entity-detail-overview">
<template v-if="entity.entityType === 'ip'">
<ip-overview :entity="entity" :time-filter="timeFilter"></ip-overview>
<ip-overview :entity="entity" :time-filter="timeFilter" @reloadEntity="getEntity"></ip-overview>
</template>
<template v-else-if="entity.entityType === 'domain'">
<domain-overview :entity="entity" :time-filter="timeFilter"></domain-overview>
<domain-overview :entity="entity" :time-filter="timeFilter" @reloadEntity="getEntity"></domain-overview>
</template>
<template v-else-if="entity.entityType === 'app'">
<app-overview :entity="entity" :time-filter="timeFilter"></app-overview>
<app-overview :entity="entity" :time-filter="timeFilter" @reloadEntity="getEntity"></app-overview>
</template>
</div>
</template>
@@ -28,6 +28,11 @@ export default {
'domain-overview': Domain,
'app-overview': App,
'ip-overview': Ip
},
methods: {
getEntity (data) {
this.$emit('reloadEntity', data)
}
}
}
</script>

View File

@@ -4,36 +4,32 @@
<div class="overview__content">
<div class="overview__row">
<div class="row__label row__label--width130">{{$t('entities.category')}}</div>
<div class="row__content">{{entityData.domainCategory || '-'}}</div>
<div class="row__content">{{entityData.category ? entityData.category.categoryName : '-'}}</div>
</div>
<div class="overview__row">
<div class="row__label row__label--width130">{{$t('entities.domainDetail.categoryGroup')}}</div>
<div class="row__content">{{entityData.domainCategoryGroup || '-'}}</div>
<div class="row__content">{{entityData.category ? entityData.category.categoryGroup : '-'}}</div>
</div>
<div class="overview__row">
<div class="row__label row__label--width130">{{$t('entities.reputationLevel')}}</div>
<div class="row__content">{{entityData.domainReputationScore || '-'}}</div>
<div class="row__content">{{entityData.category ? entityData.category.reputationLevel : '-'}}</div>
</div>
<div class="overview__row">
<div class="row__label row__label--width130">{{$t('entities.registration')}}</div>
<div class="row__content">{{entityData.domainWhoisAddress || '-'}}</div>
<div class="row__content">{{entityData.whois ? entityData.whois.registrantCountry : '-'}}</div>
</div>
<div class="overview__row">
<div class="row__label row__label--width130">{{$t('entities.org')}}</div>
<div class="row__content">{{entityData.domainWhoisOrg || '-'}}</div>
<div class="row__content">{{entityData.whois ? entityData.whois.registrantOrg : '-'}}</div>
</div>
<div class="overview__row">
<div class="row__label row__label--width130">{{$t('entities.icpCompanyName')}}</div>
<div class="row__content">{{entityData.domainIcpCompanyName || '-'}}</div>
<div class="row__content">{{entityData.icp ? entityData.icp.icpCompanyName : '-'}}</div>
</div>
<div class="overview__row">
<div class="row__label row__label--width130">{{$t('entities.icpLicense')}}</div>
<div class="row__content">{{entityData.domainIcpSiteLicense || '-'}}</div>
<div class="row__content">{{entityData.icp ? entityData.icp.icpSiteLicense : '-'}}</div>
</div>
<!-- <div class="overview__row">
<div class="row__label row__label&#45;&#45;width130">{{$t('overall.remark')}}</div>
<div class="row__content">{{entityData.domainDescription || '-'}}</div>
</div>-->
</div>
</div>
<div class="overview-item">
@@ -42,116 +38,107 @@
<loading :loading="loadingTraffic" size="small" inner-style="left: 8.75rem;" style="width: 50%;"></loading>
<div class="overview__row">
<div class="row__label row__label--width130">{{$t('overall.peak')}}</div>
<div class="row__content">{{unitConvert(entityData.max, unitTypes.byte).join(' ')}}/s</div>
<div class="row__content">
{{unitConvert(entityData.max, unitTypes.byte).join(' ') !== '- ' ? unitConvert(entityData.max, unitTypes.byte).join(' ') + '/s' : '-'}}
</div>
</div>
<div class="overview__row">
<div class="row__label row__label--width130">{{$t('overall.mean')}}</div>
<div class="row__content">{{unitConvert(entityData.avg, unitTypes.byte).join(' ')}}/s</div>
<div class="row__content">
{{unitConvert(entityData.avg, unitTypes.byte).join(' ') !== '- ' ? unitConvert(entityData.avg, unitTypes.byte).join(' ') + '/s' : '-'}}
</div>
</div>
<div class="overview__row">
<div class="row__label row__label--width130">{{$t('overall.throughput')}}</div>
<div class="row__contents">
<div class="row__content">
<div class="row__charts-msg">{{$t('overall.sent')}}{{unitConvert(entityData.bytesSentRate, unitTypes.byte).join(' ')}}ps</div>
<div class="row__charts-msg">{{$t('overall.sent')}}
{{unitConvert(entityData.bytesSentRate, unitTypes.byte).join(' ') !== '- ' ? unitConvert(entityData.bytesSentRate, unitTypes.byte).join(' ') + 'ps' : '-'}}
</div>
<!-- 曲线-->
<div class="row__content-loading">
<div class="row__charts" :id="`entityDetailSend${entity.domainName}`" >{</div>
<div class="row__charts" :id="`entityDetailSend${entity.entityValue}`" >{</div>
</div>
</div>
<div class="row__content">
<div class="row__charts-msg">{{$t('overall.received')}}{{unitConvert(entityData.bytesReceivedRate, unitTypes.byte).join(' ')}}ps</div>
<div class="row__content row__content-accept">
<div class="row__charts-msg">{{$t('overall.received')}}
{{unitConvert(entityData.bytesReceivedRate, unitTypes.byte).join(' ') !== '- ' ? unitConvert(entityData.bytesReceivedRate, unitTypes.byte).join(' ') + 'ps' : '-'}}
</div>
<!-- 曲线-->
<div class="row__content-loading">
<div class="row__charts" :id="`entityDetailReceived${entity.domainName}`" ></div>
<div class="row__charts" :id="`entityDetailReceived${entity.entityValue}`" ></div>
</div>
</div>
</div>
</div>
<div class="overview__row">
<div class="row__label row__label--width130">{{$t('entities.networkQualityRating')}}</div>
<div style="position: relative;">
<div class="entity-score" v-if="!loadingNetworkQuality">
<div class="circle-icon" v-if="score <= 2 || score === '-'" :class="{'data-score-red': score <= 2 || score === '-'}" ></div>
<div class="circle-icon" v-else-if="score <= 4" :class="{'data-score-yellow': score <= 4}" ></div>
<div class="circle-icon" v-else-if="score <= 6" :class="{'data-score-green': score <= 6}" ></div>
Score:{{score}}
</div>
<loading :loading="loadingNetworkQuality" size="small" style="left: 1rem;width: 50%;"></loading>
</div>
</div>
</div>
</div>
<div class="overview-item">
<div class="overview__title">{{$t('overall.relationship')}}</div>
<div class="overview__content domain__content">
<div class="overview__tags domain__tags" ref="relationship">
<div class="overview__domain-tabs overview__domain-tabs-loading">
<loading :loading="loadingRelationshipOne" size="small" inner-style="left: 1rem;" style="width: 50%;"></loading>
<div class="overview__domain-tab">
<div class="overview__tag domain__tag">
<span class="tag__value">{{relationshipDataOne.length}}</span>
<span class="tag__desc">{{$t('entities.relatedApp')}}</span>
</div>
<div class="overview__tag domain__tag-list" v-show="item.show" v-for="(item, index) in relationshipDataOne" :key="index">
<span class="tag__desc">{{item.appName}}</span>
</div>
<div class="overview__tags domain__tags" ref="relationship"></div>
<div class="overview__row">
<div class="row__label row__label--width130">{{$t('entities.tab.relatedApp')}}</div>
<div class="row__content overview__row-related">
<div v-if="loadingRelationshipOne" style="position: relative;width: 450px;">
<loading :loading="loadingRelationshipOne" size="small" style="left: 1rem;"></loading>
</div>
<div class="overview__domain-btn">
<div class="overview__domain-more" @click="more(relationshipDataOne, 1)" v-if="relationshipShowOne">...</div>
<div class="overview__domain-more-tabs show-more-list" v-if="relationshipShowMoreOne" v-ele-click-outside="mouseout">
<div class="domain-more-tab" v-for="item in relationshipMoreDataOne" :key="item">
<span v-if="item.appName" :title="item.appName">{{item.appName}}</span>
</div>
</div>
<div class="data-item" v-show="item.show" v-for="(item, index) in relationshipDataOne" :key="index">
{{item.value}}
</div>
<div v-if="relationshipDataOne.length===0 && !loadingRelationshipOne">-</div>
<div v-if="relationshipShowOne">
<el-popover placement="right-end" trigger="click" show-arrow="false" offset="20">
<template #reference>
<div class="data-item show-more-related">...</div>
</template>
<div v-for="(item, index) in relationshipDataOne" :key="index">{{item.value}}</div>
</el-popover>
</div>
</div>
<div class="overview__domain-tabs overview__domain-tabs-loading">
<loading :loading="loadingRelationshipTwo" size="small" inner-style="left: 1rem;" style="width: 50%;"></loading>
<div class="overview__domain-tab">
<div class="overview__tag domain__tag">
<span class="tag__value">{{relationshipDataTwo.length}}</span>
<span class="tag__desc">{{$t('entities.relatedServerIp')}}</span>
</div>
<div class="overview__tag domain__tag-list" v-show="item.show" v-for="(item, index) in relationshipDataTwo" :key="index">
<span class="tag__desc">{{item.ip}}</span>
</div>
</div>
<div class="overview__row overview__row-related">
<div class="row__label row__label--width130">{{$t('entities.tab.relatedIp')}}</div>
<div class="row__content">
<div v-if="loadingRelationshipTwo" style="position: relative;width: 450px;">
<loading :loading="loadingRelationshipTwo" size="small" style="left: 1rem;"></loading>
</div>
<div class="overview__domain-btn">
<div class="overview__domain-more" @click="more(relationshipDataTwo, 2)" v-if="relationshipShowTwo">...</div>
<div class="overview__domain-more-tabs show-more-list" v-if="relationshipShowMoreTwo" v-ele-click-outside="mouseout">
<div class="domain-more-tab" v-for="item in relationshipMoreDataTwo" :key="item">
<span v-if="item.ip" :title="item.ip">{{item.ip}}</span>
</div>
</div>
<div class="data-item" v-show="item.show" v-for="(item, index) in relationshipDataTwo" :key="index">
{{item.value}}
</div>
<div v-if="relationshipDataTwo.length===0 && !loadingRelationshipTwo">-</div>
<div v-if="relationshipShowTwo">
<el-popover class="entity-expand-detail" placement="right-end" trigger="click" show-arrow="false" offset="20">
<template #reference>
<div class="data-item show-more-related">...</div>
</template>
<div v-for="(item, index) in relationshipDataTwo" :key="index">{{item.value}}</div>
</el-popover>
</div>
</div>
</div>
</div>
</div>
<div class="overview-item">
<div class="overview__title">{{$t('overall.networkQuality')}}</div>
<div class="overview__content overview__content-loading-net">
<loading :loading="loadingNetworkQuality" size="small"></loading>
<div class="overview__row overview__row--single-value">
<chart-single-value
v-for="(chartInfo, i) in singleValues.chartInfos"
:chart-info="chartInfo"
:chart-data="singleValues.chartDatas[i]"
:key="i"
class="cn-chart__single-value--detail-overview"
></chart-single-value>
</div>
</div>
</div>
<div class="overview-item">
<div class="overview__title">{{$t('entities.accessLink')}}</div>
<div class="overview__content">
<div class="overview__tags">
<div class="overview__tag overview__tag-loading">
<loading :loading="loadingOut" size="small"></loading>
<span class="tag__desc">{{$t('entities.outLinkTrafficPercentage')}}</span>
<span class="tag__value">{{entityData.linkOutId ? entityData.linkOutId : '-'}},</span>
<span class="tag__desc">{{$t('entities.percentage')}}</span>
<span class="tag__value">{{entityData.linkOutPercent ? unitConvert(entityData.linkOutPercent, unitTypes.percent).join(' ') : '-'}}</span>
</div>
<div class="overview__tag overview__tag-loading">
<loading :loading="loadingIn" size="small"></loading>
<span class="tag__desc">{{$t('entities.inLinkTrafficPercentage')}}</span>
<span class="tag__value">{{entityData.linkInId ? entityData.linkInId : '-'}},</span>
<span class="tag__desc">{{$t('entities.percentage')}}</span>
<span class="tag__value">{{entityData.linkInPercent ? unitConvert(entityData.linkInPercent, unitTypes.percent).join(' ') : '-'}}</span>
</div>
</div>
</div>
</div>
<div class="overview-item">
<div class="overview__title">{{$t('overall.alert')}}</div>
<div class="overview__content overview__content-loading">
@@ -160,18 +147,16 @@
<span class="no-recent-alerts"><i class="el-icon-success"></i>{{$t('relationShip.noRecentAlerts')}}</span>
</div>
<div class="overview__row" v-if="performanceData.length > 0">
<div class="row__label">{{$t('entities.recentAlert')}}</div>
<div class="row__label row__label--width130">{{$t('entities.recentAlert')}}</div>
<div class="row__content">{{entityData.performanceNum}}</div>
</div>
<div class="overview__row overview__row--small-font" v-for="(performance, index) in entityData.performanceList" :key="index">
<div class="row__label row__label--width160">{{dateFormatByAppearance(performance.startTime) || '-'}}</div>
<div class="row__content row__content--width200">
<div class="row__label row__label--width130">{{dateFormatByAppearance(performance.startTime) || '-'}}</div>
<div class="row__content row__content--width90">
<div class="alert-level-tag alert-level-tag--high" :class="iconClass(performance)">{{performance.eventSeverity}}</div>
<div>{{performance.eventType}}</div>
</div>
<div class="row__content-loading" style="position: relative;" >
<loading :loading="!loadingAlert && loadingPerformance[index]?loadingPerformance[index]:false" :id="`loading${entity.ipAddr}_${index}`" size="small"></loading>
<div class="row__charts" :id="`entityPerformanceChart${entity.domainName}_${index}`"></div>
<div class="row__content-loading" style="position: relative;">
<div class="performance-event-remark">{{performance.eventType}}</div>
</div>
<div class="row__desc"></div>
</div>
@@ -188,14 +173,24 @@
<span class="no-recent-alerts"><i class="el-icon-success"></i>{{$t('relationShip.noRecentAlerts')}}</span>
</div>
<div class="overview__row" v-if="securityData.length > 0">
<div class="row__label">{{$t('entities.recentSecurity')}}</div>
<div class="row__label row__label--width130">{{$t('entities.recentSecurity')}}</div>
<div class="row__content">{{entityData.securityNum}}</div>
</div>
<div class="overview__row overview__row--small-font" v-for="(security, i) in entityData.securityList" :key="i">
<div class="row__label row__label--width160">{{dateFormatByAppearance(getMillisecond(security.startTime)) || '-'}}</div>
<div class="row__content row__content--width200">
<div class="row__label row__label--width130">{{dateFormatByAppearance(getMillisecond(security.startTime)) || '-'}}</div>
<div class="row__content row__content--width90">
<div class="alert-level-tag alert-level-tag--high" :class="iconClass(security)">{{security.eventSeverity}}</div>
<div>{{security.securityType}}</div>
</div>
<div class="cn-detection__header">
<i class="cn-icon cn-icon-attacker"></i>
<span>{{ security.offenderIp }}</span>
<div class="domain" v-if="security.offenderIp===security.serverIp">{{ security.domain }}</div>
<span class="line">-------</span>
<span class="circle"></span>
<i class="cn-icon cn-icon-attacked"></i>
<span>{{ security.victimIp }}</span>
<div class="domain" v-if="security.victimIp===security.serverIp">{{ security.domain }}</div>
</div>
<div class="row__desc"></div>
</div>
@@ -218,7 +213,6 @@
</template>
<script>
import ChartSingleValue from '@/views/charts/charts/ChartSingleValue'
import { api } from '@/utils/api'
import entityDetailMixin from './entityDetailMixin'
import { unitTypes } from '@/utils/constants'
@@ -227,13 +221,13 @@ import Chart from '@/views/charts/Chart'
import _ from 'lodash'
import { get } from '@/utils/http'
import relatedServer from '@/mixins/relatedServer'
import { getSecond, getMillisecond } from '@/utils/date-util'
import { dateFormatByAppearance, getMillisecond, getSecond } from '@/utils/date-util'
import Loading from '@/components/common/Loading'
import { ref } from 'vue'
export default {
name: 'Domain',
components: {
ChartSingleValue,
Loading,
Chart
},
@@ -242,16 +236,21 @@ export default {
return {
// entityData: {},
entityType: 'domain',
trafficUrl: api.entityDomainDetailTraffic,
// trafficUrl: api.entityDomainDetailTraffic,
trafficUrl: api.entity.entityList.domainThroughput,
relationUrl: api.entityDomainDetailRelation,
networkQuantityUrl: api.entityDomainDetailNetworkQuantity,
networkQuantityUrl: api.entity.entityList.domainPerformance,
// networkQuantityUrl: api.entityDomainDetailNetworkQuantity,
linkInUrl: api.entityDomainDetailLinkIn,
linkOutUrl: api.entityDomainDetailLinkOut,
performanceUrl: api.entityDomainDetailPerformance,
securityUrl: api.entityDomainDetailSecurity,
trafficUrlMap: api.entityDomainDetailTrafficMap,
relatedServerIpUrl: api.entityDomainRelatedServerIp,
relatedServerAppUrl: api.entityDomainRelatedServerApp,
// performanceUrl: api.entityDomainDetailPerformance,
performanceUrl: api.entity.entityList.domainEventPerformance,
// securityUrl: api.entityDomainDetailSecurity,
securityUrl: api.entity.entityList.domainSecurity,
// trafficUrlMap: api.entityDomainDetailTrafficMap,
trafficUrlMap: api.entity.entityList.domainTrafficMap,
relatedServerIpUrl: api.entity.entityList.domainRelatedIp,
relatedServerAppUrl: api.entity.entityList.domainRelatedApp,
basicProperties: api.entityDomainDetailBasic,
chartData: null,
listMode: 'list',
@@ -308,34 +307,33 @@ export default {
i18n: 'entities.pktRetransPercent'
}
],
chartDatas: [null, null, null, null, null],
loadingTraffic: false,
loadingRelationshipOne: false,
loadingRelationshipTwo: false,
loadingNetworkQuality: false,
loadingOut: false,
loadingIn: false,
loadingAlert: false,
loadingSecurityEvents: false,
loadingMap: false
}
chartDatas: [null, null, null, null, null]
},
loadingTraffic: false,
loadingRelationshipOne: false,
loadingRelationshipTwo: false,
loadingNetworkQuality: false,
loadingOut: false,
loadingIn: false,
loadingAlert: false,
loadingSecurityEvents: false,
loadingMap: false
}
},
methods: {
getMillisecond,
dateFormatByAppearance,
getQueryParams () {
const queryParams = {
startTime: getSecond(this.timeFilter.startTime),
endTime: getSecond(this.timeFilter.endTime),
domain: this.entity.domainName
return {
// startTime: getSecond(this.timeFilter.startTime),
// endTime: getSecond(this.timeFilter.endTime),
resource: this.entity.entityValue
}
return queryParams
},
getPerformanceQueryParams () {
const queryParams = {
domain: this.entity.domainName
return {
domain: this.entity.entityValue
}
return queryParams
},
handleRelationData (result) {
this.entityData.appCount = result.appCount
@@ -383,14 +381,15 @@ export default {
})
},
setup (props) {
const entityData = ref({ ...props.entity })
const entityCopy = {
..._.cloneDeep(props.entity),
domain: props.entity.domainName
domain: props.entity.entityValue
}
return {
chart: {
params: {
url: '/interface/entity/detail/domain/trafficMap?startTime={{startTime}}&endTime={{endTime}}&country={{country}}&domain={{domain}}',
url: `${api.entity.entityList.domainTrafficMap}?resource={{resource}}&country={{country}}`,
unitType: 'number',
valueColumn: 'sessions'
},
@@ -398,7 +397,8 @@ export default {
},
entityCopy,
unitTypes,
unitConvert
unitConvert,
entityData
}
}
}

View File

@@ -4,11 +4,15 @@
<div class="overview__content">
<div class="overview__row">
<div class="row__label row__label--width130">{{$t('overall.location')}}</div>
<div class="row__content">{{ipLocationRegion(entity)}}</div>
<div class="row__content">{{ipLocationRegion(entity.location)}}</div>
</div>
<div class="overview__row">
<div class="row__label row__label--width130">ASN</div>
<div class="row__content">{{entity.ipAsn || '-'}}</div>
<div class="row__content">{{entity.asn ? entity.asn.asn : '-'}}</div>
</div>
<div class="overview__row">
<div class="row__label row__label--width130">{{$t('entities.openPort')}}</div>
<div class="row__content">{{ openPort }}</div>
</div>
</div>
</div>
@@ -44,7 +48,7 @@
<!-- 曲线-->
<div class="row__content-loading">
<loading :loading="!loadingDns && loading" size="small"></loading>
<div class="row__charts" :id="`entityDnsServerInfo${entity.ipAddr}`"></div>
<div class="row__charts" :id="`entityDnsServerInfo${entity.entityValue}`"></div>
</div>
</div>
</div>
@@ -57,116 +61,107 @@
<loading :loading="loadingTraffic" size="small" inner-style="left: 8.75rem;" style="width: 50%;"></loading>
<div class="overview__row">
<div class="row__label row__label--width130">{{$t('overall.peak')}}</div>
<div class="row__content">{{unitConvert(entityData.max, unitTypes.byte).join(' ')}}/s</div>
<div class="row__content">
{{unitConvert(entityData.max, unitTypes.byte).join(' ') !== '- ' ? unitConvert(entityData.max, unitTypes.byte).join(' ') + '/s' : '-'}}
</div>
</div>
<div class="overview__row">
<div class="row__label row__label--width130">{{$t('overall.mean')}}</div>
<div class="row__content">{{unitConvert(entityData.avg, unitTypes.byte).join(' ')}}/s</div>
<div class="row__content">
{{unitConvert(entityData.avg, unitTypes.byte).join(' ') !== '- ' ? unitConvert(entityData.avg, unitTypes.byte).join(' ') + '/s' : '-'}}
</div>
</div>
<div class="overview__row">
<div class="row__label row__label--width130">{{$t('overall.throughput')}}</div>
<div class="row__contents">
<div class="row__content">
<div class="row__charts-msg">{{$t('overall.sent')}}{{unitConvert(entityData.bytesSentRate, unitTypes.byte).join(' ')}}ps</div>
<div class="row__charts-msg">{{$t('overall.sent')}}
{{unitConvert(entityData.bytesSentRate, unitTypes.byte).join(' ') !== '- ' ? unitConvert(entityData.bytesSentRate, unitTypes.byte).join(' ') + 'ps' : '-'}}
</div>
<!-- 曲线-->
<div class="row__content-loading">
<div class="row__charts" :id="`entityDetailSend${entity.ipAddr}`"></div>
<div class="row__charts" :id="`entityDetailSend${entity.entityValue}`"></div>
</div>
</div>
<div class="row__content">
<div class="row__charts-msg">{{$t('overall.received')}}{{unitConvert(entityData.bytesReceivedRate, unitTypes.byte).join(' ')}}ps</div>
<div class="row__content row__content-accept">
<div class="row__charts-msg">{{$t('overall.received')}}
{{unitConvert(entityData.bytesReceivedRate, unitTypes.byte).join(' ') !== '- ' ? unitConvert(entityData.bytesReceivedRate, unitTypes.byte).join(' ') + 'ps' : '-'}}
</div>
<!-- 曲线-->
<div class="row__content-loading">
<div class="row__charts" :id="`entityDetailReceived${entity.ipAddr}`"></div>
<div class="row__charts" :id="`entityDetailReceived${entity.entityValue}`"></div>
</div>
</div>
</div>
</div>
<div class="overview__row">
<div class="row__label row__label--width130">{{$t('entities.networkQualityRating')}}</div>
<div style="position: relative;">
<div class="entity-score" v-if="!loadingNetworkQuality">
<div class="circle-icon" v-if="score <= 2 || score === '-'" :class="{'data-score-red': score <= 2 || score === '-'}" ></div>
<div class="circle-icon" v-else-if="score <= 4" :class="{'data-score-yellow': score <= 4}" ></div>
<div class="circle-icon" v-else-if="score <= 6" :class="{'data-score-green': score <= 6}" ></div>
Score:{{score}}
</div>
<loading :loading="loadingNetworkQuality" size="small" style="left: 1rem;width: 50%;"></loading>
</div>
</div>
</div>
</div>
<div class="overview-item">
<div class="overview__title">{{$t('overall.relationship')}}</div>
<div class="overview__content domain__content">
<div class="overview__tags domain__tags" ref="relationship">
<div class="overview__domain-tabs overview__domain-tabs-loading">
<loading :loading="loadingRelationshipOne" size="small" inner-style="left: 1rem;" style="width: 50%;"></loading>
<div class="overview__domain-tab">
<div class="overview__tag domain__tag">
<span class="tag__value">{{relationshipDataOne.length}}</span>
<span class="tag__desc">{{$t('entities.relatedDomains')}}</span>
</div>
<div class="overview__tag domain__tag-list" v-show="item.show" v-for="(item, index) in relationshipDataOne" :key="index">
<span class="tag__desc">{{item.domain}}</span>
</div>
<div class="overview__tags domain__tags" ref="relationship"></div>
<div class="overview__row overview__row-related">
<div class="row__label row__label--width130">{{$t('entities.tab.relatedApp')}}</div>
<div class="row__content">
<div v-if="loadingRelationshipOne" style="position: relative;width: 450px;">
<loading :loading="loadingRelationshipOne" size="small" style="left: 1rem;"></loading>
</div>
<div class="overview__domain-btn">
<div class="overview__domain-more" @click="more(relationshipDataOne, 1)" v-if="relationshipShowOne">...</div>
<div class="overview__domain-more-tabs show-more-list" v-if="relationshipShowMoreOne" v-ele-click-outside="mouseout">
<div class="domain-more-tab" v-for="item in relationshipMoreDataOne" :key="item">
<span v-if="item.domain" :title="item.domain">{{item.domain}}</span>
</div>
</div>
<div class="data-item" v-show="item.show" v-for="(item, index) in relationshipDataOne" :key="index">
{{item.value}}
</div>
<div v-if="relationshipDataOne.length===0 && !loadingRelationshipOne">-</div>
<div v-if="relationshipShowOne">
<el-popover placement="right-end" trigger="click" show-arrow="false" offset="20">
<template #reference>
<div class="data-item show-more-related">...</div>
</template>
<div class="popover-content" v-for="(item, index) in relationshipDataOne" :key="index">{{item.value}}</div>
</el-popover>
</div>
</div>
<div class="overview__domain-tabs overview__domain-tabs-loading">
<loading :loading="loadingRelationshipTwo" size="small" inner-style="left: 1rem;" style="width: 50%;"></loading>
<div class="overview__domain-tab">
<div class="overview__tag domain__tag">
<span class="tag__value">{{relationshipDataTwo.length}}</span>
<span class="tag__desc">{{$t('entities.relatedApp')}}</span>
</div>
<div class="overview__tag domain__tag-list" v-show="item.show" v-for="(item, index) in relationshipDataTwo" :key="index">
<span class="tag__desc">{{item.appName}}</span>
</div>
</div>
<div class="overview__row overview__row-related">
<div class="row__label row__label--width130">{{$t('entities.relatedDomain')}}</div>
<div class="row__content">
<div v-if="loadingRelationshipTwo" style="position: relative;width: 450px;">
<loading :loading="loadingRelationshipTwo" size="small" style="left: 1rem;"></loading>
</div>
<div class="overview__domain-btn">
<div class="overview__domain-more" @click="more(relationshipDataTwo, 2)" v-if="relationshipShowTwo">...</div>
<div class="overview__domain-more-tabs show-more-list" v-if="relationshipShowMoreTwo" v-ele-click-outside="mouseout">
<div class="domain-more-tab" v-for="item in relationshipMoreDataTwo" :key="item">
<span v-if="item.appName" :title="item.appName">{{item.appName}}</span>
</div>
</div>
<div class="data-item" v-show="item.show" v-for="(item, index) in relationshipDataTwo" :key="index">
{{item.value}}
</div>
<div v-if="relationshipDataTwo.length===0 && !loadingRelationshipTwo">-</div>
<div v-if="relationshipShowTwo">
<el-popover class="entity-expand-detail" placement="right-end" trigger="click" show-arrow="false" offset="20">
<template #reference>
<div class="data-item show-more-related">...</div>
</template>
<div v-for="(item, index) in relationshipDataTwo" :key="index">{{item.value}}</div>
</el-popover>
</div>
</div>
</div>
</div>
</div>
<div class="overview-item">
<div class="overview__title">{{$t('overall.networkQuality')}}</div>
<div class="overview__content overview__content-loading-net">
<loading :loading="loadingNetworkQuality" size="small"></loading>
<div class="overview__row overview__row--single-value">
<chart-single-value
v-for="(chartInfo, i) in singleValues.chartInfos"
:chart-info="chartInfo"
:chart-data="singleValues.chartDatas[i]"
:key="i"
class="cn-chart__single-value--detail-overview"
></chart-single-value>
</div>
</div>
</div>
<div class="overview-item">
<div class="overview__title">{{$t('entities.accessLink')}}</div>
<div class="overview__content">
<div class="overview__tags">
<div class="overview__tag overview__tag-loading">
<loading :loading="loadingOut" size="small"></loading>
<span class="tag__desc">{{$t('entities.outLinkTrafficPercentage')}}</span>
<span class="tag__value">{{entityData.linkOutId ? entityData.linkOutId : '-'}},&nbsp;</span>
<span class="tag__desc">{{$t('entities.percentage')}}</span>
<span class="tag__value">{{entityData.linkOutPercent ? unitConvert(entityData.linkOutPercent, unitTypes.percent).join(' ') : '-'}}</span>
</div>
<div class="overview__tag overview__tag-loading">
<loading :loading="loadingIn" size="small"></loading>
<span class="tag__desc">{{$t('entities.inLinkTrafficPercentage')}}</span>
<span class="tag__value">{{entityData.linkInId ? entityData.linkInId : '-'}},&nbsp;</span>
<span class="tag__desc">{{$t('entities.percentage')}}</span>
<span class="tag__value">{{entityData.linkInPercent ? unitConvert(entityData.linkInPercent, unitTypes.percent).join(' ') : '-'}}</span>
</div>
</div>
</div>
</div>
<div class="overview-item">
<div class="overview__title">{{$t('overall.alert')}}</div>
<div class="overview__content overview__content-loading">
@@ -175,18 +170,16 @@
<span class="no-recent-alerts"><i class="el-icon-success"></i>{{$t('relationShip.noRecentAlerts')}}</span>
</div>
<div class="overview__row" v-if="performanceData.length > 0">
<div class="row__label">{{$t('entities.recentAlert')}}</div>
<div class="row__label row__label--width130">{{$t('entities.recentAlert')}}</div>
<div class="row__content">{{entityData.performanceNum}}</div>
</div>
<div class="overview__row overview__row--small-font" v-for="(performance, index) in entityData.performanceList" :key="index">
<div class="row__label row__label--width160">{{dateFormatByAppearance(performance.startTime) || '-'}}</div>
<div class="row__content">
<div class="row__label row__label--width130">{{dateFormatByAppearance(performance.startTime) || '-'}}</div>
<div class="row__content row__content--width90">
<div class="alert-level-tag alert-level-tag--high" :class="iconClass(performance)">{{performance.eventSeverity}}</div>
<div>{{performance.eventType}}</div>
</div>
<div class="row__content-loading" style="position: relative; padding-left: 10px;">
<loading :loading="!loadingAlert && loadingPerformance[index]?loadingPerformance[index]:false" :id="`loading${entity.ipAddr}_${index}`" size="small"></loading>
<div class="row__charts" :id="`entityPerformanceChart${entity.ipAddr}_${index}`"></div>
<div class="row__content-loading" style="position: relative;">
<div class="performance-event-remark">{{performance.eventType}}</div>
</div>
<div class="row__desc"></div>
</div>
@@ -203,15 +196,26 @@
<span class="no-recent-alerts"><i class="el-icon-success"></i>{{$t('relationShip.noRecentAlerts')}}</span>
</div>
<div class="overview__row" v-if="securityData.length > 0">
<div class="row__label">{{$t('entities.recentSecurity')}}</div>
<div class="row__label row__label--width130">{{$t('entities.recentSecurity')}}</div>
<div class="row__content">{{entityData.securityNum}}</div>
</div>
<div class="overview__row overview__row--small-font" v-for="(security, index) in entityData.securityList" :key="index">
<div class="row__label row__label--width160">{{dateFormatByAppearance(security.startTime) || '-'}}</div>
<div class="row__content row__content--width200">
<div class="row__label row__label--width130">{{dateFormatByAppearance(security.startTime) || '-'}}</div>
<div class="row__content row__content--width90">
<div class="alert-level-tag alert-level-tag--high" :class="iconClass(security)">{{security.eventSeverity}}</div>
<div>{{security.securityType}}</div>
</div>
<div class="cn-detection__header" >
<i class="cn-icon cn-icon-attacker"></i>
<span>{{ security.offenderIp }}</span>
<div class="domain" v-if="security.offenderIp===security.serverIp">{{ security.domain }}</div>
<span class="line">-------</span>
<span class="circle"></span>
<i class="cn-icon cn-icon-attacked"></i>
<span>{{ security.victimIp }}</span>
<div class="domain" v-if="security.victimIp===security.serverIp">{{ security.domain }}</div>
</div>
<div class="row__desc"></div>
</div>
<div class="overview__row overview__row--small-font" v-if="securityData && securityData.length > 5">
@@ -234,7 +238,6 @@
<script>
import entityDetailMixin from './entityDetailMixin'
import ChartSingleValue from '@/views/charts/charts/ChartSingleValue'
import { api } from '@/utils/api'
import { unitTypes } from '@/utils/constants'
import unitConvert from '@/utils/unit-convert'
@@ -242,30 +245,35 @@ import Chart from '@/views/charts/Chart'
import _ from 'lodash'
import { get } from '@/utils/http'
import relatedServer from '@/mixins/relatedServer'
import { getSecond, getMillisecond } from '@/utils/date-util'
import { dateFormatByAppearance, getMillisecond, getSecond } from '@/utils/date-util'
import Loading from '@/components/common/Loading'
import axios from 'axios'
export default {
name: 'Ip',
mixins: [entityDetailMixin, relatedServer],
components: {
Loading,
Chart,
ChartSingleValue
Chart
},
data () {
return {
entityType: 'ip',
trafficUrl: api.entityIpDetailTraffic,
trafficUrlMap: api.entityIpDetailTrafficMap,
// trafficUrl: api.entityIpDetailTraffic,
trafficUrl: api.entity.entityList.ipThroughput,
// trafficUrlMap: api.entityIpDetailTrafficMap,
trafficUrlMap: api.entity.entityList.ipTrafficMap,
relationUrl: api.entityIpDetailRelation,
networkQuantityUrl: api.entityIpDetailNetworkQuantity,
// networkQuantityUrl: api.entityIpDetailNetworkQuantity,
networkQuantityUrl: api.entity.entityList.ipPerformance,
linkInUrl: api.entityIpDetailLinkIn,
linkOutUrl: api.entityIpDetailLinkOut,
performanceUrl: api.entityIpDetailPerformance,
securityUrl: api.entityIpDetailSecurity,
relatedServerDomainUrl: api.entityIpRelatedServerDomain,
relatedServerAppUrl: api.entityIpRelatedServerApp,
performanceUrl: api.entity.entityList.ipEventPerformance,
// performanceUrl: api.entityIpDetailPerformance,
// securityUrl: api.entityIpDetailSecurity,
securityUrl: api.entity.entityList.ipSecurity,
relatedServerDomainUrl: api.entity.entityList.ipRelatedDomain,
relatedServerAppUrl: api.entity.entityList.ipRelatedApp,
entityDetectionsIpUrl: api.entityDetectionsIp,
entityDetectionsIpQueryRateUrl: api.entityDetectionsIpQueryRate,
listMode: 'list',
@@ -336,21 +344,22 @@ export default {
loadingIn: false,
loadingAlert: false,
loadingSecurityEvents: false,
loadingMap: false
loadingMap: false,
openPort: '-'
}
},
computed: {
ipLocationRegion () {
return function (entityData) {
let result = ''
if (entityData.ipLocationCountry) {
result += `${entityData.ipLocationCountry},`
if (entityData.country) {
result += `${entityData.country},`
}
if (entityData.ipLocationProvince) {
result += `${entityData.ipLocationProvince},`
if (entityData.province) {
result += `${entityData.province},`
}
if (entityData.ipLocationCity) {
result += `${entityData.ipLocationCity},`
if (entityData.city) {
result += `${entityData.city},`
}
result = result.substr(0, result.length - 1)
if (!result) {
@@ -381,19 +390,18 @@ export default {
},
methods: {
getMillisecond,
dateFormatByAppearance,
getQueryParams () {
const queryParams = {
startTime: getSecond(this.timeFilter.startTime),
endTime: getSecond(this.timeFilter.endTime),
ip: this.entity.ipAddr
return {
// startTime: getSecond(this.timeFilter.startTime),
// endTime: getSecond(this.timeFilter.endTime),
resource: this.entity.entityValue
}
return queryParams
},
getPerformanceQueryParams () {
const queryParams = {
serverIp: this.entity.ipAddr
return {
serverIp: this.entity.entityValue
}
return queryParams
},
handleRelationData (result) {
this.entityData.appCount = result.appCount
@@ -411,11 +419,29 @@ export default {
queryRelated () {
this.getRelatedServerDataOne(this.relatedServerDomainUrl)
this.getRelatedServerDataTwo(this.relatedServerAppUrl)
},
getOpenPort () {
const params = {
startTime: getSecond(this.timeFilter.startTime),
endTime: getSecond(this.timeFilter.endTime),
resource: this.entity.entityValue
}
axios.get(api.entity.entityList.ipRelatedPort, { params: params }).then(res => {
if (res.data.code === 200 && res.data.data.result.length) {
this.openPort = ''
res.data.data.result.forEach(item => {
this.openPort += item.port + '/' + item.l7Protocol + ','
})
this.openPort = this.openPort.slice(0, -1)
}
})
}
},
mounted () {
this.queryParams = this.getQueryParams()
this.chartGetMap()
this.getOpenPort()
this.$nextTick(() => {
setTimeout(() => {
this.queryRelated()
@@ -425,16 +451,16 @@ export default {
setup (props) {
const entityCopy = {
..._.cloneDeep(props.entity),
ip: props.entity.ipAddr
ip: props.entity.entityValue
}
return {
chart: {
params: {
url: '/interface/entity/detail/ip/trafficMap?startTime={{startTime}}&endTime={{endTime}}&country={{country}}&ip={{ip}}',
url: `${api.entity.entityList.ipTrafficMap}?resource={{resource}}&country={{country}}`,
unitType: 'number',
valueColumn: 'sessions'
},
id: props.entity.ipAddr,
id: props.entity.entityValue,
type: 2
},
entityCopy,
@@ -444,3 +470,110 @@ export default {
}
}
</script>
<style lang="scss">
//.type-content {
// margin-bottom:15px;
// display:flex;
// flex-flow: row wrap;
// width:100%;
.data-item {
display: flex;
justify-content: center;
align-items: center;
background: rgba(119,131,145,0.06);
border: 1px solid rgba(119,131,145,0.36);
border-radius: 2px;
height:28px;
padding:8px 15px;
margin-right:10px;
//margin-bottom:15px;
font-size: 12px;
color: #353636;
font-weight: 400;
white-space: nowrap;
}
.show-more-related {
height: 28px;
line-height: 28px;
padding-bottom: 12px;
cursor: pointer;
}
//}
.entity-score {
.circle-icon {
border-radius: 3px;
width: 6px;
height: 6px;
margin-right: 4px;
}
.data-score-red {
background: #E26154;
}
.data-score-yellow {
background: #E5A219;
}
.data-score-green {
background: #749F4D;
}
height:24px;
font-size: 14px;
color: #046ECA;
font-weight: 500;
display:flex;
align-items: center;
justify-content: center;
}
.cn-detection__header {
display: flex;
flex-direction: row;
flex-wrap: nowrap;
padding-bottom: 3px;
color: #333333;
align-items: center;
font-size: 12px;
i {
color: #7b8fa2;
margin-right: 5px;
font-size: 18px;
}
.line {
color: #da5656;
margin-left: 12px;
font-size: xx-small;
font-weight: bold;
}
.circle {
width: 10px;
height: 10px;
border: 2px solid #da5656;
border-radius: 10px;
margin-top: 4px;
margin-right: 12px;
}
.domain {
background: #EFF2F5;
border-radius: 2px;
font-size: 14px;
color: #333333;
letter-spacing: 0;
line-height: 14px;
margin-left: 5px;
font-style: italic;
padding: 0 2px;
font-weight: 500;
}
}
.el-popper {
min-width: 90px !important;
width: auto !important;
max-width: 300px !important;
max-height: 180px !important;
overflow: scroll !important;
line-height: 24px !important;
}
</style>

View File

@@ -6,7 +6,7 @@ import { riskLevelMapping, unitTypes } from '@/utils/constants'
import unitConvert from '@/utils/unit-convert'
import { shallowRef, markRaw } from 'vue'
import { metricOption } from '@/views/detections/options/detectionOptions'
import { sortBy, reverseSortBy } from '@/utils/tools'
import { sortBy, reverseSortBy, computeScore } from '@/utils/tools'
import { getSecond } from '@/utils/date-util'
import { api } from '@/utils/api'
@@ -45,29 +45,13 @@ export default {
metricChart: null,
performanceChartList: [],
loadingPerformance: [],
score: '-', // 网络质量评分
performanceMetricEndTimeInterval: 3600 // 服务质量事件指标的结束时间与开始时间的秒间隔
}
},
computed: {
entityName () {
let name
switch (this.entity.entityType) {
case ('ip'): {
name = this.entity.ipAddr
break
}
case ('domain'): {
name = this.entity.domainName
break
}
case ('app'): {
name = this.entity.appName
break
}
default: break
}
return name
return this.entity.entityValue
},
appRisk () {
return function (level) {
@@ -172,6 +156,7 @@ export default {
this.sentChart.setOption(this.chartOptionSent)
this.receivedChart.setOption(this.chartOptionReceived)
this.loadingTraffic = false
this.$emit('reloadEntity', this.entityData)
} else {
this.loadingTraffic = false
}
@@ -234,7 +219,7 @@ export default {
this.performanceChartList.push(metricChart)
this.echartsArray.push(shallowRef(metricChart))
} else {
const chartDom = document.getElementById(`entityPerformanceChart${this.entityName}${index}`)
const chartDom = document.getElementById(`entityPerformanceChart${this.entityName}_${index}`)
chartDom.innerHTML = '<span style="padding-left:5px;">-</span>'
}
})
@@ -272,6 +257,14 @@ export default {
if (this.networkQuantityUrl) {
get(this.networkQuantityUrl, this.getQueryParams()).then(response => {
if (response.code === 200) {
const data = {
establishLatencyMs: response.data.result.establishLatencyValue || null,
httpResponseLatency: response.data.result.httpResponseLatencyValue || null,
sslConLatency: response.data.result.sslConLatencyValue || null,
tcpLostlenPercent: response.data.result.sequenceGapLossPercentValue || null,
pktRetransPercent: response.data.result.pktRetransPercentValue || null
}
this.score = computeScore(data)
this.entityData.establishLatencyValue = response.data.result.establishLatencyValue
this.entityData.establishLatencyP50 = response.data.result.establishLatencyP50
this.entityData.establishLatencyP90 = response.data.result.establishLatencyP90
@@ -389,11 +382,6 @@ export default {
this.entityData.performanceNum = response.data.result.length
this.performanceData = response.data.result
this.entityData.performanceList = this.getTargetPageData(1, this.showMore.performancePageSize, this.performanceData)
this.$nextTick(() => {
setTimeout(() => {
this.queryEntityDetailPerformanceChart(this.entityData.performanceList, 0)
}, 200)
})
}
this.loadingAlert = false
})
@@ -412,16 +400,16 @@ export default {
},
performanceShowMore (num) {
const startIndex = this.showMore.performancePageSize
// const startIndex = this.showMore.performancePageSize
this.showMore.performancePageSize += num
this.entityData.performanceList = this.getTargetPageData(this.showMore.pageNo, this.showMore.performancePageSize, this.performanceData)
this.$nextTick(() => {
setTimeout(() => {
if (this.entityData.performanceList && this.entityData.performanceList.length > 0) {
this.queryEntityDetailPerformanceChart(this.entityData.performanceList.slice(startIndex, this.entityData.performanceList.length), startIndex)
}
}, 200)
})
// this.$nextTick(() => {
// setTimeout(() => lltext
// if (this.entityData.performanceList && this.entityData.performanceList.length > 0) {
// this.queryEntityDetailPerformanceChart(this.entityData.performanceList.slice(startIndex, this.entityData.performanceList.length), startIndex)
// }
// }, 200)
// })
},
securityShowMore (num) {
@@ -520,6 +508,7 @@ export default {
this.chartOption = _.cloneDeep(entityListLineOption)
setTimeout(() => { this.queryEntityDetail() })
const _this = this
this.entityData = { ...this.entityData, ...this.entity }
this.emitter.on('switch-collapse', function () {
setTimeout(() => { _this.queryEntityDetail() }, 200)
})

View File

@@ -45,43 +45,10 @@ export default {
return className
},
entityType () {
let type
switch (this.entityData.entityType) {
case 'ip': {
type = this.entityData.ipAddr
break
}
case 'domain': {
type = this.entityData.domainName
break
}
case 'app': {
type = this.entityData.appName
break
}
default:
break
}
return type
return this.entity.entityValue
},
entityName () {
let name
switch (this.entityData.entityType) {
case ('ip'): {
name = this.entity.ipAddr
break
}
case ('domain'): {
name = this.entity.domainName
break
}
case ('app'): {
name = this.entity.appName
break
}
default: break
}
return name
return this.entity.entityValue
},
appRisk () {
return function (level) {
@@ -94,32 +61,11 @@ export default {
queryParams () {
let params
const now = window.$dayJs.tz().valueOf()
switch (this.entityData.entityType) {
case ('ip'): {
params = {
startTime: this.timeFilter.startTime ? parseInt(this.timeFilter.startTime / 1000) : Math.floor(now / 1000 - 3600),
endTime: this.timeFilter.endTime ? parseInt(this.timeFilter.endTime / 1000) : Math.floor(now / 1000),
ip: this.entityData.ipAddr
}
break
}
case ('domain'): {
params = {
startTime: this.timeFilter.startTime ? parseInt(this.timeFilter.startTime / 1000) : Math.floor(now / 1000 - 3600),
endTime: this.timeFilter.endTime ? parseInt(this.timeFilter.endTime / 1000) : Math.floor(now / 1000),
domain: this.entityData.domainName
}
break
}
case ('app'): {
params = {
startTime: this.timeFilter.startTime ? parseInt(this.timeFilter.startTime / 1000) : Math.floor(now / 1000 - 3600),
endTime: this.timeFilter.endTime ? parseInt(this.timeFilter.endTime / 1000) : Math.floor(now / 1000),
appName: this.entityData.appName
}
break
}
default: break
// eslint-disable-next-line prefer-const
params = {
startTime: this.timeFilter.startTime ? getSecond(this.timeFilter.startTime) : Math.floor(now / 1000 - 3600),
endTime: this.timeFilter.endTime ? getSecond(this.timeFilter.endTime) : Math.floor(now / 1000),
resource: this.entityData.entityValue
}
return params
}
@@ -130,7 +76,17 @@ export default {
path: '/entityDetail',
query: {
entityType: this.entityData.entityType,
name: this.entityData.ipAddr || this.entityData.domainName || this.entityData.appName
entityName: this.entityData.entityValue
}
})
window.open(href, '_blank')
},
showGraph () {
const { href } = this.$router.resolve({
path: '/entityGraph',
query: {
entityType: this.entityData.entityType,
entityName: this.entityData.entityValue
}
})
window.open(href, '_blank')
@@ -197,11 +153,9 @@ export default {
},
getQueryParams () {
return {
startTime: getSecond(this.timeFilter.startTime),
endTime: getSecond(this.timeFilter.endTime),
appName: this.entityType,
domain: this.entityType,
ip: this.entityType
// startTime: getSecond(this.timeFilter.startTime),
// endTime: getSecond(this.timeFilter.endTime),
resource: this.entityType
}
},
queryEntityDetailTraffic () {
@@ -285,30 +239,36 @@ export default {
},
resize () {
this.echartsArray.forEach(item => { item.value.resize() })
},
initUrl () {
if (this.entity.entityType) {
switch (this.entity.entityType) {
case 'ip': {
// this.trafficUrl = api.entityIpDetailTraffic
this.trafficUrl = api.entity.entityList.ipThroughput
break
}
case 'domain': {
// this.trafficUrl = api.entityDomainDetailTraffic
this.trafficUrl = api.entity.entityList.domainThroughput
break
}
case 'app': {
// this.trafficUrl = api.entityAppDetailTraffic
this.trafficUrl = api.entity.entityList.appThroughput
break
}
default:
break
}
}
}
},
watch: {
entityData: {
deep: true,
handler (n) {
if (n.entityType) {
switch (n.entityType) {
case 'ip': {
this.trafficUrl = api.entityIpDetailTraffic
break
}
case 'domain': {
this.trafficUrl = api.entityDomainDetailTraffic
break
}
case 'app': {
this.trafficUrl = api.entityAppDetailTraffic
break
}
default:
break
}
}
this.initUrl()
}
}
},
@@ -316,7 +276,9 @@ export default {
this.debounceFunc = this.$_.debounce(this.resize, 200)
window.addEventListener('resize', this.debounceFunc)
this.chartOption = _.cloneDeep(entityListLineOption)
this.entityData = _.cloneDeep(this.entity)
// this.entityData = _.cloneDeep(this.entity)
this.entityData = { ...this.entityData, ...this.entity }
this.initUrl()
setTimeout(() => {
this.querySecurity()
this.queryEntityDetailTraffic()

View File

@@ -2,9 +2,7 @@
<div class="explorer-search">
<div class="explorer-search__title" v-show="!showList">{{$t('search.title')}}</div>
<div class="explorer-search__input-case" :class="{'explorer-search__input-case--question-mark-in-line': showList}">
<div class="explorer-search__input">
<!--新版实体列表改版后续记得解开-->
<!--<div class="explorer-search__input" style="border: 1px #DEDEDE solid;height: 42px;">-->
<div class="explorer-search__input" style="border: 1px #DEDEDE solid;height: 42px;">
<advanced-search
ref="search"
:column-list="columnList"
@@ -25,8 +23,8 @@
<span>{{$t('search.searchHistory')}}</span>
</div>
<div class="foot__item">
<span @click="search">{{$t('overall.explore')}}</span>
<!-- <el-divider direction="vertical"></el-divider>
<!-- <span @click="search">{{$t('overall.explore')}}</span>
<el-divider direction="vertical"></el-divider>
<span>{{$t('overall.help')}}</span>-->
</div>
<transition name="el-zoom-in-top">
@@ -69,7 +67,8 @@ export default {
data () {
return {
columnList: columnList,
operatorList: ['=', '!=', /* '>', '<', '>=', '<=', */'IN', 'NOT IN', 'LIKE', 'NOT LIKE'],
// operatorList: ['=', '!=', /* '>', '<', '>=', '<=', */'IN', 'NOT IN', 'LIKE', 'NOT LIKE'],
operatorList: ['=', 'IN'],
connectionList: [
{
value: 'AND',

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More