Compare commits

..

3 Commits

Author SHA1 Message Date
陈劲松
a5afea11e4 Merge branch 'cherry-pick-bbdd93a0' into 'dev-24.11'
fix: 删除假数据

See merge request cyber-narrator/cn-ui!85
2024-11-19 12:04:46 +00:00
陈劲松
4b2fd5df9a fix: 删除假数据
(cherry picked from commit bbdd93a043)

Co-authored-by: chenjinsong <523037378@qq.com>
2024-11-19 12:04:40 +00:00
chenjinsong
5f621784ff fix: 修复自动测试不通过的内容 2024-11-15 18:28:50 +08:00
25 changed files with 20 additions and 3157 deletions

View File

@@ -1,8 +1,7 @@
const BASE_CONFIG = { const BASE_CONFIG = {
baseUrl: 'http://192.168.44.54:8090/', baseUrl: 'http://192.168.44.54:8090/',
version: '24.04', version: '24.04',
apiVersion: 'v1', apiVersion: 'v1'
apiVersion2: 'v2'
} }
// 默认时间过滤条件,单位分钟. 0表示请求接口时不传时间参数 // 默认时间过滤条件,单位分钟. 0表示请求接口时不传时间参数
const DEFAULT_TIME_FILTER_RANGE = { const DEFAULT_TIME_FILTER_RANGE = {

View File

@@ -150,7 +150,3 @@ body {
} }
} }
} }
.el-table .table-disabled-row {
--el-table-tr-bg-color: var(--el-fill-color-light);
}

View File

@@ -53,7 +53,7 @@
border: 1px solid var(--el-border-color-light); border: 1px solid var(--el-border-color-light);
} }
.top-tool-search, .top-tool-search1 { .top-tool-search {
.top-tool-search__display { .top-tool-search__display {
display: flex; display: flex;
} }
@@ -83,13 +83,6 @@
} }
} }
.top-tool-search1 {
width: 100%;
.el-input {
width: 100% !important;
}
}
.top-tool-right { .top-tool-right {
display: flex; display: flex;
} }
@@ -128,12 +121,6 @@
} }
} }
.tool-btn-view {
i {
font-size: 13px;
}
}
.btn-customize { .btn-customize {
color: var(--el-color-primary); color: var(--el-color-primary);
font-size: 12px; font-size: 12px;

View File

@@ -88,21 +88,20 @@
} }
.cn-menu__left { .cn-menu__left {
flex: 1; flex: 1;
padding: 12px 0 10px 0; padding: 12px 10px 10px 20px;
max-width: 290px; max-width: 290px;
border-right: 1px solid var(--el-border-color); border-right: 1px solid var(--el-border-color);
.left-menu { .left-menu {
padding: 8px 0 8px 20px; padding: 8px 0;
font-size: 14px; font-size: 14px;
color: var(--el-text-color-primary); color: var(--el-text-color-primary);
cursor: pointer; cursor: pointer;
&:hover { &:hover {
color: var(--el-color-primary); span {
} text-decoration: underline;
&.left-menu--setting { }
background-color: #f0f0f0;
} }
.cn-icon:first-of-type { .cn-icon:first-of-type {
font-size: 17px; font-size: 17px;
@@ -117,10 +116,6 @@
padding-left: 10px; padding-left: 10px;
} }
} }
.empty-menu {
border-top: 1px rgba(0,0,0,0) solid;
}
} }
.cn-menu__middle { .cn-menu__middle {
display: flex; display: flex;

View File

@@ -89,6 +89,3 @@
@import "./components/common/simple-loading"; @import "./components/common/simple-loading";
@import "views/location/location"; @import "views/location/location";
@import "views/setting/sourcesForm";
@import "views/setting/entitySettingForm";

View File

@@ -1,352 +0,0 @@
$text-color-primary: var(--el-text-color-primary);
$bg-color-page: var(--el-bg-color-page);
$border-color-light: var(--el-border-color-light);
// es为entity setting简写
.es-form {
padding: 20px;
.el-input {
height: 24px;
line-height: 24px;
}
.el-select__wrapper {
min-height: 24px;
//padding: 2px 8px;
}
.es-form-header {
font-size: 24px;
color: $text-color-primary;
font-weight: bold;
margin-top: 10px;
}
.es-form-content {
height: calc(100% - 92px);
overflow: scroll;
padding-bottom: 20px;
.es-form-collapse {
margin-top: 20px;
width: 1200px;
border: 1px $border-color-light solid;
.rule-definition {
position: relative;
}
.el-collapse-item__header {
width: 1200px !important;
height: 56px !important;
background: var(--el-fill-color-blank) !important;
border-left: 1px solid $bg-color-page;
border-right: 1px solid $border-color-light;
border-radius: 4px !important;
display: flex;
justify-content: space-between;
flex-direction: inherit;
position: relative;
padding: 0 20px;
i {
position: absolute;
left: 20px;
}
}
.el-collapse-item__wrap {
//border: 1px solid $bg-color-page;
border-top: none;
}
.form-collapse-header {
margin-left: 26px;
display: flex;
align-items: center;
.form-collapse-header-no, .form-collapse-header-no-active {
width: 24px;
height: 24px;
line-height: 24px;
text-align: center;
font-size: 16px;
border-radius: 50%;
margin-right: 10px;
transition: 0.5s all;
}
.form-collapse-header-no {
background: $border-color-light;
color: $text-color-primary;
}
.form-collapse-header-no-active {
background: var(--el-color-business);
color: var(--el-color-white);
}
.form-collapse-header-title {
font-size: 16px;
color: $text-color-primary;
}
}
.form-collapse-content {
padding: 0 20px 0 46px;
.form-setting__block {
width: 620px;
margin-top: 0;
.form-setting__select {
width: 620px !important;
}
}
.form-content__title {
line-height: 16px;
font-size: 16px;
color: var(--el-text-color-primary);
font-weight: 500;
margin-bottom: 15px;
}
.form-content__block {
width: 620px;
padding: 15px 12px 5px 12px;
border: 1px $border-color-light solid;
margin-bottom: 10px;
.block-header {
width: 596px;
line-height: 12px;
display: flex;
flex-direction: column;
font-size: 12px;
color: var(--el-text-color-primary);
margin-bottom: 10px;
.block-header__title {
display: flex;
justify-content: space-between;
div:nth-child(1) {
font-weight: bold;
}
i {
color: var(--el-text-color-regular);
font-size: 10px;
cursor: pointer;
}
}
.block-header__menu {
display: flex;
padding: 10px 10px 0 10px;
font-weight: bold;
div:nth-child(1) {
width: 278px;
}
}
}
.el-form {
display: flex;
flex-direction: column;
}
.block-body, .block-body1 {
width: 576px;
display: flex;
.el-form-item {
margin-bottom: 10px;
}
.block-body__select {
width: 255px;
}
.block-body-equal {
margin: 0 10px;
color: var(--el-text-color-disabled);
}
.mapping-item-add {
color: var(--el-text-color-regular);
font-size: 10px;
line-height: 24px;
margin: 0 10px;
cursor: pointer;
}
.mapping-item-close {
height: 24px;
line-height: 24px;
color: var(--el-color-error);
font-size: 10px;
cursor: pointer;
}
.relation-field__select {
width: 112px;
}
.relation-field__select-disabled {
width: 112px;
.el-select__selected-item {
color: var(--el-text-color-primary);
}
.el-select__suffix {
display: none;
}
}
.relation-type__select {
width: 302px;
margin: 0 10px;
}
}
.block-body {
padding-left: 10px;
}
.block-body1 {
width: 596px;
}
}
.form-setting-type {
width: 620px;
border: 1px solid $border-color-light;
padding: 15px 12px;
margin-bottom: 10px;
.setting-type__header {
font-size: 14px;
margin-bottom: 10px;
display: flex;
justify-content: space-between;
i {
height: 24px;
line-height: 24px;
color: var(--el-text-color-regular);
font-size: 10px;
cursor: pointer;
}
}
}
.block__footer {
cursor: pointer;
.addFieldBtn {
height: 24px;
min-height: 24px;
font-size: 12px;
color: var(--el-text-color-primary);
font-weight: 500;
width: 620px;
background: var(--el-color-business-light-9);
border: 1px $border-color-light solid;
padding: 0;
box-shadow: 0 2px 4px 0 rgba(51, 51, 51, 0.02);
border-radius: 2px;
.add-field-btn {
color: var(--el-text-color-regular) !important;
font-size: 9px !important;
margin: 0 8px 2px 8px;
}
&:hover {
i {
color: var(--el-color-business) !important;
}
}
}
}
}
.margin-20 {
margin: 20px 0;
}
.el-collapse-item__content {
padding-bottom: 20px;
}
.es-form-trigger {
.el-input__wrapper, .el-select__wrapper {
width: 112px;
}
.el-input__inner {
padding: 0 15px !important;
}
}
}
.el-input--small, .el-input--small .el-input__inner {
border-radius: 2px !important;
}
.switch__block {
margin: 20px 0;
.block-title {
font-size: 14px;
line-height: 14px;
color: $text-color-primary;
margin-bottom: 12px;
}
}
}
.es-form__footer {
width: 100%;
height: 60px;
box-shadow: 0 -1px 4px 0 rgba(0, 0, 0, 0.10);
display: flex;
align-items: center;
justify-content: center;
.el-form-item__label {
color: var(--el-text-color-primary);
}
.tag__btn {
margin: 0 10px;
height: 30px;
min-width: 74px;
}
}
.el-overlay-message-box, .el-message-box {
padding: 0 !important;
}
.el-overlay-message-box {
text-align: center !important;
}
.is-message-box .el-overlay-message-box {
display: flex;
justify-content: center;
align-items: center;
}
.from-dot {
font-size: 14px;
color: var(--el-color-danger);
margin-right: 4px;
}
}

View File

@@ -1,195 +0,0 @@
$color-text-primary: var(--el-text-color-primary);
.sources-form {
height: 100%;
.el-input {
height: 24px;
line-height: 24px;
}
.el-select__wrapper {
min-height: 24px;
padding: 2px 8px;
}
.el-input__wrapper {
padding: 1px 6px;
}
.sources-form__header {
padding: 30px 0 30px 20px;
font-size: 24px;
line-height: 24px;
font-weight: bold;
color: $color-text-primary;
}
.sources-form__body {
display: flex;
flex-direction: column;
height: calc(100% - 147px);
padding-left: 40px;
overflow: auto;
.el-form-item__label {
color: var(--el-text-color-primary);
}
.form__body__label {
font-family: NotoSansHans-Medium;
font-size: 14px;
color: var(--el-text-color-primary);
font-weight: 500;
}
.form-fields__block {
padding: 10px;
width: 620px;
border: 1px solid var(--el-border-color-light);
border-radius: 2px;
margin-top: 10px;
margin-bottom: 20px;
.block__header {
display: flex;
font-family: NotoSansHans-Medium;
font-size: 12px;
color: var(--el-text-color-primary);
font-weight: bold;
margin-bottom: 10px;
.block__header-name {
width: 262px;
margin-right: 10px;
}
.block__header-type {
width: 162px;
margin-right: 10px;
}
}
.block__body {
display: flex;
flex-direction: column;
.block__body-container {
//height: 24px;
//line-height: 24px;
display: flex;
//margin-bottom: 8px;
.block__body-name, .block__body-type, .block__body-default, .block__body-function, .block__body-field, .block__body-field-name {
width: 262px;
height: 24px;
margin-right: 10px;
}
.block__body-type {
width: 162px;
}
.block__body-default {
width: 132px;
}
.block__body-function {
width: 160px;
}
.block__body-field {
width: 124px;
}
.block__body-field-name {
width: 138px;
}
.remove-btn {
height: 24px;
line-height: 24px;
color: var(--el-text-color-primary);
font-size: 9px;
cursor: pointer;
}
.el-form-item {
margin-bottom: 12px;
.el-form-item__content {
line-height: 24px;
}
}
}
}
.block__footer {
margin-top: 8px;
cursor: pointer;
.addFieldBtn {
height: 24px;
min-height: 24px;
font-size: 12px;
color: $color-text-primary;
font-weight: 500;
width: 598px;
background: var(--el-color-business-light-9);
border: 1px var(--el-border-color-light) solid;
padding: 0;
box-shadow: 0 2px 4px 0 rgba(51, 51, 51, 0.02);
border-radius: 2px;
font-family: NotoSansHans-Medium !important;
.add-field-btn {
color: var(--el-text-color-regular) !important;
font-size: 9px !important;
margin: 0 8px 2px 8px;
}
&:hover {
i {
color: var(--el-color-business) !important;
}
}
span {
font-family: NotoSansHans-Medium !important;
}
}
}
}
}
.form-description {
width: 620px;
margin-top: 12px;
margin-bottom: 20px;
}
.sources-form__footer {
display: flex;
align-items: center;
justify-content: center;
height: 60px;
margin-top: 3px;
box-shadow: 0 -1px 4px 0 rgba(0, 0, 0, 0.10);
.el-form-item__label {
color: var(--el-text-color-primary);
}
.tag__btn {
margin: 0 10px;
height: 30px;
min-width: 74px;
}
}
.from-dot {
font-size: 14px;
color: var(--el-color-danger);
margin-right: 4px;
}
}

View File

@@ -199,114 +199,6 @@
} }
} }
} }
.sources-dialog {
.el-dialog__header {
background-color: var(--cn-bg-color-lighter);
box-shadow: 0 1px 0 0 rgba(53,54,54,0.08);
padding: 0;
height: 42px;
line-height: 42px;
.sources-dialog__header {
font-family: NotoSansSChineseRegular;
font-size: 14px;
color: var(--el-text-color-primary);
letter-spacing: 0;
font-weight: 400;
padding-left: 10px;
}
.el-dialog__headerbtn {
width: 42px;
height: 42px;
.el-dialog__close {
color: var(--el-text-color-primary);
}
}
}
.sources-dialog__body {
padding: 20px 30px 10px 30px;
display: flex;
flex-direction: column;
.dialog__body-upload {
width: 420px;
height: 124px;
background: var(--cn-bg-color-lighter);
border: 1px solid var(--el-border-color-light);
border-radius: 4px;
margin-bottom: 10px;
.el-upload-dragger {
height: 124px;
border: 1px solid var(--el-border-color-light);
}
.upload-icon {
font-size: 24px;
color: var(--el-text-color-primary);
}
.el-upload__text {
color: var(--el-text-color-primary);
}
}
.dialog__body-tip {
width: 420px;
height: 142px;
background: var(--el-color-business-light-9);
border-radius: 4px;
padding: 10px;
.tip__text {
font-family: NotoSansSChineseRegular;
font-size: 12px;
color: var(--el-text-color-regular);
letter-spacing: 0;
font-weight: 400;
}
}
}
.el-dialog__footer {
padding: 0;
.sources-dialog__footer {
height: 52px;
line-height: 52px;
border-top: 1px solid var(--el-border-color-light);
.cancel-btn {
width: 80px;
height: 28px;
margin-right: 20px;
color: var(--el-text-color-primary);
background: var(--el-fill-color-light);
border: 1px solid var(--el-border-color-dark);
border-radius: 2px;
cursor: pointer;
}
.upload-btn {
width: 80px;
height: 28px;
margin-right: 20px;
color: var(--el-color-white);
margin-left: 0 !important;
background-color: var(--el-color-business);
border: none;
outline: none;
border-radius: 2px;
cursor: pointer;
}
}
}
}
} }
.type-tag { .type-tag {
display: inline-block; display: inline-block;

View File

@@ -1,8 +1,8 @@
@font-face { @font-face {
font-family: "cn-icon"; /* Project id 2614877 */ font-family: "cn-icon"; /* Project id 2614877 */
src: url('iconfont.woff2?t=1731894091336') format('woff2'), src: url('iconfont.woff2?t=1730282688072') format('woff2'),
url('iconfont.woff?t=1731894091336') format('woff'), url('iconfont.woff?t=1730282688072') format('woff'),
url('iconfont.ttf?t=1731894091336') format('truetype'); url('iconfont.ttf?t=1730282688072') format('truetype');
} }
.cn-icon { .cn-icon {
@@ -13,14 +13,6 @@
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
} }
.cn-icon-System:before {
content: "\e830";
}
.cn-icon-mutual:before {
content: "\ec8d";
}
.cn-icon-breached-human-black:before { .cn-icon-breached-human-black:before {
content: "\e824"; content: "\e824";
} }

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

@@ -138,23 +138,15 @@
class="cn-menu" class="cn-menu"
:with-header="false" :with-header="false"
:show-close="false" :show-close="false"
@close="closeDrawer"
> >
<div class="cn-menu__left" v-if="otherMenu" @mouseleave="mouseleaveLeftMenu"> <div class="cn-menu__left" v-if="otherMenu">
<div <div class="left-menu" v-for="menu in otherMenu" :key="menu.id" @click="jumpOther(menu.route,'','',0)">
class="left-menu"
:class="isShowSetting && menu.code==='setting' ? 'left-menu--setting' : ''"
v-for="menu in otherMenu"
:key="menu.id"
@click="jumpOther(menu.route,'','',0)"
@mouseenter="mouseleaveItemMenu(menu.code)">
<i :class="menu.icon"></i> <i :class="menu.icon"></i>
<span>{{ $t(menu.i18n || menu.name) }}</span> <span>{{ $t(menu.i18n || menu.name) }}</span>
<i class="cn-icon cn-icon-right" v-show="menu.code==='setting'"></i> <i class="cn-icon cn-icon-right"></i>
</div> </div>
<div class="empty-menu" @mouseleave="mouseleaveItemMenu(null)"></div>
</div> </div>
<div class="cn-menu__middle" v-if="!isShowSetting"> <div class="cn-menu__middle" >
<div class="middle-menus middle-menus--network-analytics"> <div class="middle-menus middle-menus--network-analytics">
<div class="middle-menus__header">{{ $t('overall.networkAnalytics') }}</div> <div class="middle-menus__header">{{ $t('overall.networkAnalytics') }}</div>
<div class="middle-menus__body" v-if="networkAnalyticsMenu && networkAnalyticsMenu.children"> <div class="middle-menus__body" v-if="networkAnalyticsMenu && networkAnalyticsMenu.children">
@@ -194,27 +186,6 @@
</div> </div>
</div> </div>
</div> </div>
<div class="cn-menu__middle" v-else @mouseenter="mouseleaveItemMenu('setting')">
<div class="middle-menus middle-menus--network-analytics">
<div class="middle-menus__header">{{ $t('overall.setting') }}</div>
<div class="middle-menus__body" v-if="settingMenu && settingMenu.children">
<div style="width: 260px;">
<template v-for="(menu, index) in settingMenu.children" :key="menu.name">
<div class="middle-menu" v-if="index < 5" @click="jump(menu.route,'','',2)">
{{ $t(menu.i18n || menu.name) }}
</div>
</template>
</div>
<div>
<template v-for="(menu, index) in settingMenu.children" :key="menu.name">
<div class="middle-menu" v-if="index >= 5 && index < 10" @click="jump(menu.route,'','',2)">
{{ $t(menu.i18n || menu.name) }}
</div>
</template>
</div>
</div>
</div>
</div>
</el-drawer> </el-drawer>
<!-- 改密码 --> <!-- 改密码 -->
@@ -365,8 +336,7 @@ export default {
wholeScreenRouterMapping, wholeScreenRouterMapping,
logo: 'images/logo-header.svg', logo: 'images/logo-header.svg',
ZH, ZH,
EN, EN
isShowSetting: false
} }
}, },
computed: { computed: {
@@ -376,9 +346,6 @@ export default {
locationIntelligenceMenu () { locationIntelligenceMenu () {
return this.$store.getters.menuList.find(menu => menu.code === 'locationIntelligence') return this.$store.getters.menuList.find(menu => menu.code === 'locationIntelligence')
}, },
settingMenu () {
return this.$store.getters.menuList.find(menu => menu.code === 'setting')
},
otherMenu () { otherMenu () {
return this.$store.getters.menuList.filter(menu => ['locationIntelligence', 'networkAnalytics', 'I18N', 'entityDetail', 'entityGraph', 'detectionPolicy'].indexOf(menu.code) === -1) return this.$store.getters.menuList.filter(menu => ['locationIntelligence', 'networkAnalytics', 'I18N', 'entityDetail', 'entityGraph', 'detectionPolicy'].indexOf(menu.code) === -1)
@@ -501,10 +468,8 @@ export default {
const newQuery = query const newQuery = query
// 重刷界面后url里参数带空格的部分会被转为+,此处将+再转为空格。后续观察是否存在原本参数带+被误转的 // 重刷界面后url里参数带空格的部分会被转为+,此处将+再转为空格。后续观察是否存在原本参数带+被误转的
Object.keys(newQuery).forEach(item => { Object.keys(newQuery).forEach(item => {
if (newQuery[item] && _.isString(newQuery[item])) { if (newQuery[item] && newQuery[item].indexOf('+') > -1) {
if (newQuery[item].indexOf('+') > -1) { newQuery[item] = newQuery[item].replaceAll('+', ' ')
newQuery[item] = newQuery[item].replaceAll('+', ' ')
}
} }
}) })
const newUrl = urlParamsHandler(window.location.href, query, newQuery) const newUrl = urlParamsHandler(window.location.href, query, newQuery)
@@ -790,10 +755,6 @@ export default {
}, },
// 仅处理除panel外的相关路径的导航 // 仅处理除panel外的相关路径的导航
async jumpOther (route, index) { async jumpOther (route, index) {
if (route === '/setting') {
this.isShowSetting = true
return true
}
route = route.replace('redirect:', '') route = route.replace('redirect:', '')
this.showMenu = false this.showMenu = false
if (route === this.route && index > 0) { // 当前只有一级菜单时,点击不进行刷新,重新跳转 if (route === this.route && index > 0) { // 当前只有一级菜单时,点击不进行刷新,重新跳转
@@ -932,18 +893,6 @@ export default {
if (obj.scrollTop + obj.clientHeight === obj.scrollHeight) { if (obj.scrollTop + obj.clientHeight === obj.scrollHeight) {
this.initDropdownList() this.initDropdownList()
} }
},
closeDrawer () {
const timer = setTimeout(() => {
this.isShowSetting = false
clearTimeout(timer)
}, 400)
},
mouseleaveLeftMenu () {
this.isShowSetting = false
},
mouseleaveItemMenu (code) {
this.isShowSetting = code === 'setting'
} }
} }
} }

View File

@@ -5,9 +5,9 @@
<div class="main-container"> <div class="main-container">
<!-- 顶部工具栏 --> <!-- 顶部工具栏 -->
<div class="top-tools"> <div class="top-tools">
<div class="top-tool-left" :style="{width: showInputWidth ? showInputWidth : 'auto'}"> <div class="top-tool-left">
<slot name="top-tool-left"></slot> <slot name="top-tool-left"></slot>
<div v-if="showLayout.indexOf('search') > -1" :class="showInputWidth ? 'top-tool-search1' : 'top-tool-search margin-r-20'"> <div v-if="showLayout.indexOf('search') > -1" class="top-tool-search margin-r-20">
<div class="top-tool-search__display"> <div class="top-tool-search__display">
<el-input v-model="keyWord" @keyup.enter="onSearch"></el-input> <el-input v-model="keyWord" @keyup.enter="onSearch"></el-input>
<button class="business-button business-button--light top-tool-btn--search" @click="onSearch"> <button class="business-button business-button--light top-tool-btn--search" @click="onSearch">
@@ -72,10 +72,6 @@ export default {
layout: { layout: {
type: Array, type: Array,
default () { return [] } default () { return [] }
},
inputWidth: {
type: String,
default () { return '' }
} }
}, },
data () { data () {
@@ -86,7 +82,6 @@ export default {
showCustomTableTitle: false // 自定义列弹框是否显示 showCustomTableTitle: false // 自定义列弹框是否显示
}, },
showLayout: [], showLayout: [],
showInputWidth: '',
loading: true loading: true
} }
}, },
@@ -108,13 +103,6 @@ export default {
handler (n) { handler (n) {
this.showLayout = [...n] this.showLayout = [...n]
} }
},
inputWidth: {
immediate: true,
deep: true,
handler (n) {
this.showInputWidth = n
}
} }
} }
} }

View File

@@ -1,145 +0,0 @@
<template>
<el-table
id="userTable"
ref="dataTable"
:data="tableData"
tooltip-effect="light"
empty-text=" "
@header-dragend="dragend"
@sort-change="tableDataSort"
@selection-change="selectionChange"
:row-class-name="tableRowClassName"
>
<el-table-column
:resizable="false"
align="center"
type="selection"
width="55">
</el-table-column>
<el-table-column
v-for="(item, index) in customTableTitles"
:key="item.prop+index"
:fixed="item.fixed"
:label="item.label"
:min-width="`${item.minWidth}`"
:prop="item.prop"
:resizable="true"
:sort-orders="['ascending', 'descending']"
:sortable="item.sortable"
:width="`${item.width}`"
>
<template #header>
<span class="data-column__span">{{ item.label }}</span>
<div class="col-resize-area"></div>
</template>
<template #default="scope" :column="item">
<template v-if="item.prop === 'ctime' || item.prop === 'utime'">
<template v-if="scope.row[item.prop]">
{{ dateFormatByAppearance(scope.row[item.prop]) || '-' }}
</template>
<template v-else><span>-</span></template>
</template>
<template v-if="item.prop === 'entitySource'">
{{ scope.row[item.prop]?.name || '-' }}
</template>
<template v-if="item.prop === 'entities' || item.prop === 'relations'">
{{ handleListTypes(scope.row[item.prop]) }}
</template>
</template>
</el-table-column>
<template v-slot:empty>
<div class="table-no-data" v-if="isNoData">
<div class="table-no-data__title">{{ $t('npm.noData') }}</div>
</div>
</template>
</el-table>
</template>
<script>
import table from '@/mixins/table'
import { dateFormatByAppearance } from '@/utils/date-util'
export default {
name: 'SourcesTable',
props: {
isNoData: {
type: Boolean,
default: false
}
},
mixins: [table],
data () {
return {
tableTitle: [ // 原始table列
{
label: 'ID',
prop: 'id',
show: true,
minWidth: 50,
sortable: 'custom'
},
{
label: this.$t('setting.source'),
prop: 'entitySource',
show: true,
sortable: 'custom',
minWidth: 100
},
{
label: this.$t('setting.entityTypes'),
prop: 'entities',
show: true,
minWidth: 150
},
{
label: this.$t('setting.relationTypes'),
prop: 'relations',
show: true,
minWidth: 200
},
{
label: this.$t('config.user.createTime'),
prop: 'ctime',
show: true,
minWidth: 150
},
{
label: this.$t('overall.updateTime'),
prop: 'utime',
show: true,
minWidth: 150
}
]
}
},
methods: {
dateFormatByAppearance,
// 禁止勾选buildIn为1的项即禁止修改、删除admin的账号
checkSelectable (row) {
return row.isBuiltIn !== 1
},
handleListTypes (data) {
let str = ''
if (data && typeof data === 'string') {
data = JSON.parse(data)
data.forEach(item => {
if (!str) {
str = item.type + ','
} else if (str.indexOf(item.type) < 0) {
str += item.type + ','
}
})
str = str.slice(0, -1) || '-'
} else {
str = '-'
}
return str
},
tableRowClassName (row) {
if (row.row.isBuiltIn === 1) {
return 'table-disabled-row'
}
}
}
}
</script>

View File

@@ -1,282 +0,0 @@
<template>
<el-table
id="userTable"
ref="dataTable"
:data="tableData"
tooltip-effect="light"
empty-text=" "
@header-dragend="dragend"
@sort-change="tableDataSort"
@selection-change="selectionChange"
:row-class-name="tableRowClassName"
>
<el-table-column
:resizable="false"
align="center"
type="selection"
width="55">
</el-table-column>
<el-table-column
v-for="(item, index) in customTableTitles"
:key="item.prop+index"
:fixed="item.fixed"
:label="item.label"
:min-width="`${item.minWidth}`"
:prop="item.prop"
:resizable="true"
:sort-orders="['ascending', 'descending']"
:sortable="item.sortable"
:width="`${item.width}`"
>
<template #header>
<span class="data-column__span">{{ item.label }}</span>
<div class="col-resize-area"></div>
</template>
<template #default="scope" :column="item">
<template v-if="item.prop === 'ctime'">
<template v-if="scope.row[item.prop]">
{{ dateFormatByAppearance(scope.row[item.prop]) || '-' }}
</template>
<template v-else><span>-</span></template>
</template>
<template v-else-if="item.prop === 'option'">
<i class="cn-icon cn-icon-upload" style="cursor: pointer;" @click="clickOption(scope.row)"></i>
</template>
<span v-else>{{ scope.row[item.prop] || '-' }}</span>
</template>
</el-table-column>
<template v-slot:empty>
<div class="table-no-data" v-if="isNoData">
<div class="table-no-data__title">{{ $t('npm.noData') }}</div>
</div>
</template>
</el-table>
<el-dialog class="sources-dialog" v-model="dialogVisible" width="480">
<template #header>
<div class="sources-dialog__header">{{ $t('overall.upload') }}</div>
</template>
<div class="sources-dialog__body">
<div class="dialog__body-upload">
<loading :loading="uploadLoading"></loading>
<el-upload :action="uploadUrl"
:headers="uploadHeaders"
:data="uploadParams"
:multiple="false"
:file-list="fileList"
:on-change="fileChange"
:on-success="uploadSuccess"
:before-upload="beforeUpload"
:on-progress="onUpload"
:on-error="uploadError"
:class="uploadErrorTip ? 'el-upload--error' : ''"
drag
:accept="fileTypeLimit"
ref="upload"
>
<i class="cn-icon cn-icon-upload2 upload-icon"></i>
<div class="el-upload__text">
<div ref="uploadButton">{{ $t('sources.dragFile') }}</div>
</div>
</el-upload>
</div>
<div class="dialog__body-tip">
<div class="tip__text"
v-for="(item, index) in tipsInfo[language]"
:key="item.value"
:style="{color: index===0 || index===3 ? 'var(--el-color-business)' : 'var(--el-text-color-regular)'}">
{{ item.label }}
</div>
</div>
</div>
<template #footer>
<div class="sources-dialog__footer">
<button class="cancel-btn" @click="dialogVisible=false">{{ $t('overall.cancel') }}</button>
<button class="upload-btn">{{ $t('overall.upload') }}</button>
</div>
</template>
</el-dialog>
</template>
<script>
import table from '@/mixins/table'
import { dateFormatByAppearance } from '@/utils/date-util'
import { storageKey, unitTypes, EN } from '@/utils/constants'
import _ from 'lodash'
import unitConvert from '@/utils/unit-convert'
import { ElMessageBox } from 'element-plus'
import { ref } from 'vue'
import { api } from '@/utils/api'
import Loading from '@/components/common/Loading'
export default {
name: 'SourcesTable',
props: {
isNoData: {
type: Boolean,
default: false
}
},
mixins: [table],
data () {
return {
sourceId: '',
tableTitle: [ // 原始table列
{
label: 'ID',
prop: 'id',
show: true,
minWidth: 50,
sortable: 'custom'
},
{
label: this.$t('config.user.name'),
prop: 'name',
show: true,
sortable: 'custom',
minWidth: 150
},
{
label: this.$t('overall.remark'),
prop: 'description',
show: true,
minWidth: 350
},
{
label: this.$t('config.user.createTime'),
prop: 'ctime',
show: true,
minWidth: 150
},
{
label: this.$t('overall.option'),
prop: 'option',
show: true
}
],
dialogVisible: false,
uploadUrl: BASE_CONFIG.baseUrl + api.setting.source.sourceUpload,
uploadHeaders: {
'Cn-Authorization': localStorage.getItem(storageKey.token)
},
fileList: [],
fileTypeLimit: '.csv',
language: localStorage.getItem(storageKey.language) || EN,
uploadLoading: false,
tipsInfo: {
en: [
{ value: 0, label: 'You can now integrate data to our platform using two methods:' },
{ value: 1, label: '1. File Upload: Access the upload feature on our website to submit your files directly.' },
{ value: 2, label: '2. Kafka Direct Transmission: Send your data to a specific Kafka topic on our servers, ensuring each message includes a header with the source_id.' },
{ value: 3, label: 'For Kafka server information, please contact your administrator.' }
],
zh: [
{ value: 0, label: '您现在可以使用两种方法将数据集成到我们的平台:' },
{ value: 1, label: '1. 文件上传:访问我们网站的上传功能直接提交您的文件。' },
{ value: 2, label: '2. Kafka 直接传输:将您的数据发送到我们服务器上的特定 Kafka 主题,确保每条消息都包含带有 source_id 的标头。' },
{ value: 3, label: '有关 Kafka 服务器的信息,请联系您的管理员。' }
]
},
uploadFileSizeLimit: 1024 * 1024 * 1024,
isClick: true
}
},
components: {
Loading
},
computed: {
uploadParams () {
return {
sourceId: this.sourceId
}
}
},
methods: {
dateFormatByAppearance,
clickOption (item) {
this.sourceId = item.id
this.dialogVisible = true
this.fileTypeLimit = `.${item.dataFormat}`
},
// 禁止勾选buildIn为1的项即禁止修改、删除admin的账号
checkSelectable (row) {
return row.isBuiltIn !== 1
},
fileChange (files, fileList) {
if (this.fileList.length > 0 && this.fileList[0].status === 'success') {
this.fileListBack = this.fileList[0]
}
this.fileList = fileList.slice(-1)
},
uploadSuccess () {
this.uploaded = false
this.dialogVisible = false
this.$message.success(this.$t('tip.success'))
},
beforeUpload (file) {
return new Promise((resolve, reject) => {
// 判断后缀,仅支持.csv
if (!_.endsWith(file.name, this.fileTypeLimit)) {
this.$message.error(this.$t('validate.fileTypeLimit', { types: this.fileTypeLimit }))
this.fileList = []
reject(new Error(this.$t('validate.fileTypeLimit', { types: this.fileTypeLimit })))
} else if (file.size > this.uploadFileSizeLimit) { // 判断文件大小
this.$message.error(this.$t('validate.fileSizeLimit', { size: unitConvert(this.uploadFileSizeLimit, unitTypes.byte).join('') }))
this.fileList = []
reject(new Error(this.$t('validate.fileSizeLimit', { size: unitConvert(this.uploadFileSizeLimit, unitTypes.byte).join('') })))
} else {
if (!this.isClick) {
if (this.importedData.length > 0) {
ElMessageBox.confirm(this.$t('tip.uploadFile'), {
confirmButtonText: this.$t('tip.confirm'),
cancelButtonText: this.$t('overall.cancel'),
message: this.$t('tip.uploadFileTips'),
title: this.$t('tip.uploadFile'),
// type: 'warning',
iconClass: 'width:0px;height:0px;',
customClass: 'del-model'
}).then(() => {
resolve()
}).catch(e => {
reject(e)
})
} else {
resolve()
}
} else {
resolve()
}
}
})
},
onUpload () {
this.uploadLoading = true
this.typeSelectDisable = true
this.isClick = false
},
uploadError (error) {
let errorMsg
if (error.message) {
errorMsg = JSON.parse(error.message).message
} else {
errorMsg = 'error'
}
this.uploadLoading = false
this.$message.error(this.$t('tip.uploadFailed', { msg: errorMsg }))
},
tableRowClassName (row) {
if (row.row.isBuiltIn === 1) {
return 'table-disabled-row'
}
}
},
setup () {
// 没上传过文件的提示
const uploadErrorTip = ref('')
return {
uploadErrorTip
}
}
}
</script>

View File

@@ -42,7 +42,6 @@ export default {
delFlag: false, delFlag: false,
disableEdit: true, // 编辑按钮是否不可用,当选择多条记录的时候,编辑按钮不可用 disableEdit: true, // 编辑按钮是否不可用,当选择多条记录的时候,编辑按钮不可用
disableDelete: true, disableDelete: true,
disableView: true,
operationWidth: '165', // 操作列宽 operationWidth: '165', // 操作列宽
loading: true, loading: true,
isNoData: false isNoData: false
@@ -92,18 +91,7 @@ export default {
} }
}) })
this.disableEdit = this.batchDeleteObjs.length !== 1 this.disableEdit = this.batchDeleteObjs.length !== 1
this.disableView = this.batchDeleteObjs.length !== 1
this.disableDelete = this.batchDeleteObjs.length < 1 this.disableDelete = this.batchDeleteObjs.length < 1
const obj = objs.find(d => d.usage > 0)
if (obj) {
this.disableDelete = true
this.disableEdit = true
}
const obj1 = objs.find(d => d.isBuiltIn > 0)
if (obj1) {
this.disableDelete = true
this.disableEdit = true
}
}, },
getTableData (params, isAll, isClearType) { getTableData (params, isAll, isClearType) {
if (isAll) { if (isAll) {

View File

@@ -197,18 +197,6 @@ export function handleComponent (code) {
case 'locationMap': case 'locationMap':
case 'traceTracking': case 'traceTracking':
return () => import('@/views/location/Index') return () => import('@/views/location/Index')
case 'dataSource':
return () => import('@/views/setting/sources/Sources')
case 'createDataSource':
case 'editDataSource':
case 'viewDataSource':
return () => import('@/views/setting/sources/SourcesForm')
case 'entityIntegration':
return () => import('@/views/setting/entitySetting/EntitySetting')
case 'createEntityIntegration':
case 'editEntityIntegration':
case 'viewEntityIntegration':
return () => import('@/views/setting/entitySetting/EntitySettingForm')
default: default:
return null return null
} }

View File

@@ -386,15 +386,6 @@ export const api = {
tracking: apiVersion + '/locationIntelligence/trace/tracking', tracking: apiVersion + '/locationIntelligence/trace/tracking',
follow: apiVersion + '/locationIntelligence/follow', follow: apiVersion + '/locationIntelligence/follow',
geoLocation: apiVersion + 'locationIntelligence/geolocation' geoLocation: apiVersion + 'locationIntelligence/geolocation'
},
setting: {
source: {
source: apiVersion + '/entity/sources', // get列表查询delete删除post新增put修改
sourceUpload: apiVersion + '/entity/sources/upload' // 文件上传post
},
profiles: {
profiles: apiVersion + '/entity/integrations' // get列表查询delete删除post新增put修改
}
} }
} }

File diff suppressed because one or more lines are too long

View File

@@ -1,249 +0,0 @@
<template>
<div class="cn-tag">
<div class="cn-tag-right">
<cn-data-list
ref="dataList"
:tableId="tableId"
v-model:custom-table-title="tools.customTableTitle"
:api="url"
:from="fromRoute.tag"
:layout="['search']"
:input-width="'100%'"
@search="search"
>
<template #top-tool-left>
<button id="account-add" class="business-button tag__btn margin-r-10" @click="add">
<i class="cn-icon-xinjian cn-icon"></i>
<span>{{ $t('overall.create') }}</span>
</button>
<button id="tag-edit" class="business-button business-button--light tag__btn margin-r-10"
:disabled="disableEdit"
@click="editEntity">
<i class="cn-icon-edit cn-icon"></i>
<span>{{ $t('overall.edit') }}</span>
</button>
<button id="tag-view" class="business-button business-button--light tag__btn margin-r-10 tool-btn-view"
:disabled="disableView"
@click="viewEntity">
<i class="cn-icon-preview cn-icon"></i>
<span>{{ $t('overall.view') }}</span>
</button>
<button id="tag-delete" class="business-button business-button--light tag__btn margin-r-10"
:disabled="disableDelete"
@click="delBatch">
<i class="cn-icon-delete cn-icon"></i>
<span>{{ $t('overall.delete') }}</span>
</button>
</template>
<template #default>
<loading :loading="loading"></loading>
<profiles-table
ref="dataTable"
:api="url"
:isNoData="isNoData"
:custom-table-title="tools.customTableTitle"
:height="mainTableHeight"
:table-data="tableData"
@delete="del"
@edit="edit"
@download="download"
@preview="preview"
@reload="getTableData"
@selectionChange="selectionChange"
/>
</template>
<template #pagination>
<pagination ref="pagination" :page-obj="pageObj" :tableData="tableData" :table-id="tableId" @pageNo='pageNo'
@pageSize='pageSize'></pagination>
</template>
</cn-data-list>
</div>
</div>
</template>
<script>
import { api } from '@/utils/api'
import { tagCategoryOptions, tagIntentOptions, tagSourceOptions } from '@/utils/constants'
import { useRoute } from 'vue-router'
import { ref } from 'vue'
import dataListMixin from '@/mixins/data-list'
import Loading from '@/components/common/Loading'
import cnDataList from '@/components/table/CnDataList'
import ProfilesTable from '@/components/table/setting/ProfilesTable'
import axios from 'axios'
export default {
name: 'EntitySetting',
data () {
return {
builtinColor: false,
// url: 'api.sourcesList',
url: api.setting.profiles.profiles,
tagIntentOptions,
tagCategoryOptions,
tagSourceOptions,
tableId: 'ProfilesTable',
builtinLeftLoading: false,
isInit: true,
intent: '',
category: '',
source: '',
name: ''
}
},
setup () {
const { query } = useRoute()
const urlPageNo = ref(parseInt(query.pageNo) || 1)
return {
urlPageNo
}
},
mixins: [dataListMixin],
components: {
Loading,
cnDataList,
ProfilesTable
},
mounted () {
this.$nextTick(() => {
this.getTableData()
})
},
methods: {
search (params) {
if (this.$_.isNumber(params.q) || params.q.indexOf(',') > -1) {
params = { ids: params.q }
} else {
params = { name: params.q }
}
this.pageObj.pageNo = 1
this.getTableData(params)
this.$refs.dataTable.expandedIds = []
},
add () {
this.$router.push({
path: '/setting/entityIntegration/create',
query: {
t: +new Date()
}
})
},
getTableData (params) {
this.searchLabel = null
if (params) {
this.searchLabel = { ...this.searchLabel, ...params }
}
this.searchLabel = { ...this.searchLabel, ...this.pageObj }
this.isNoData = false
this.toggleLoading(true)
// delete this.searchLabel.total
let listUrl = this.url
if (this.listUrl) {
listUrl = this.listUrl
}
if (!this.isInit) {
axios.get(listUrl, { params: this.searchLabel }).then(response => {
if (response.status === 200) {
this.$nextTick(() => {
this.tableData = response.data.data.list.map(item => {
return {
...item,
config: item.config ? JSON.parse(item.config) : {}
}
})
this.pageObj.total = response.data.data.total
this.isNoData = !this.tableData || this.tableData.length === 0
})
// TODO 回到顶部
} else {
this.isNoData = true
}
}).finally(() => {
this.toggleLoading(false)
this.$refs.dataTable.expandedIds = []
})
}
this.isInit = false
},
delBatch () {
const ids = []
if (this.batchDeleteObjs && this.batchDeleteObjs.length > 0) {
this.batchDeleteObjs.forEach(item => {
ids.push(item.id)
})
}
if (ids.length === 0) {
this.$alert(this.$t('tip.pleaseSelect'), {
confirmButtonText: this.$t('tip.yes'),
type: 'warning'
}).catch(() => {
})
} else {
this.$confirm(this.$t('tip.confirmDelete'), {
confirmButtonText: this.$t('tip.yes'),
cancelButtonText: this.$t('tip.no'),
customClass: 'del-model-message-box',
type: 'warning'
}).then(() => {
this.toggleLoading(true)
axios.delete(api.setting.profiles.profiles + '?ids=' + ids).then(response => {
if (response.status === 200) {
this.delFlag = true
this.$message({ duration: 2000, type: 'success', message: this.$t('tip.deleteSuccess') })
this.getTableData()
} else {
this.$message.error(response.data.message)
}
}).catch(e => {
this.$message.error(e.response.data.message)
}).finally(() => {
this.toggleLoading(false)
})
}).finally(() => {
if (this.isSelectedStatus !== undefined) {
this.isSelectedStatus = false
this.disableDelete = true
this.batchDeleteObjs = []
this.$refs.dataTable.expandedIds = []
}
}).catch(() => {})
}
},
editEntity () {
if (this.batchDeleteObjs.length === 0) {
this.$alert(this.$t('tip.pleaseSelectForEdit'), {
confirmButtonText: this.$t('tip.yes'),
type: 'warning'
}).catch(() => {
})
} else {
this.jumpToEditPage(this.batchDeleteObjs[0].id)
}
},
viewEntity () {
const pageNo = this.$router.currentRoute.value.query.pageNo
this.$router.push({
path: '/setting/entityIntegration/view',
query: {
t: +new Date(),
pageNoForTable: pageNo || 1,
id: this.batchDeleteObjs[0].id
}
})
},
jumpToEditPage (id) {
const pageNo = this.$router.currentRoute.value.query.pageNo
this.$router.push({
path: '/setting/entityIntegration/edit',
query: {
t: +new Date(),
pageNoForTable: pageNo || 1,
id: id
}
})
}
}
}
</script>

View File

@@ -1,799 +0,0 @@
<template>
<div class="es-form">
<loading :loading="myLoading"></loading>
<div class="es-form-header">{{ $t('overall.entitySetting') }}</div>
<div class="es-form-content">
<!--第一步General Settings-->
<div class="es-form-collapse">
<el-collapse v-model="activeNames">
<el-collapse-item name="1">
<template #title>
<div class="form-collapse-header">
<div :class="activeNames.indexOf('1')>-1 ? 'form-collapse-header-no-active' : 'form-collapse-header-no'">1</div>
<div class="form-collapse-header-title">{{ $t('detection.create.generalSettings') }}</div>
</div>
</template>
<div class="form-collapse-content">
<el-form ref="sourceForm" :model="editObj" label-position="top" :rules="sourceRules">
<el-form-item :label="$t('setting.source')" prop="sourceId" class="form-setting__block margin-b-20">
<el-select v-model="editObj.sourceId" filterable class="form-setting__select" placeholder="" @change="onChangeSource" @visible-change="visibleSource($event)">
<el-option
v-for="item in sourceOption"
:key="item.id"
:label="item.name"
:value="item.id"
:disabled="item.disabled"
/>
</el-select>
</el-form-item>
</el-form>
<div class="form-content__title"><span class="from-dot">*</span>{{ $t('setting.schemaMapping') }}</div>
<el-form :model="editObj.schemaMappingData" ref="mappingForm">
<div class="form-content__block" v-for="(item, index) in editObj.schemaMappingData.data" :key="index">
<div class="block-header">
<div class="block-header__title">
<div>{{ item.name }}</div>
<i class="cn-icon cn-icon-close" @click="deleteMappingItem(index)"></i>
</div>
<div class="block-header__menu">
<div>{{ $t('setting.entityField') }}</div>
<div>{{ $t('setting.sourceField') }}</div>
</div>
</div>
<div class="block-body" v-for="(ite, ind) in item.list" :key="index+'-'+ind">
<el-form-item :prop="`data.${index}.list.${ind}.entityField`" :rules="mappingRules.entityField">
<el-select
v-model="ite.entityField"
class="block-body__select"
placeholder=""
filterable
@visible-change="visibleEntityFiled($event, index, ind)"
:disabled="ind===0">
<el-option
v-for="obj in settingFields[item.type]"
:key="obj.fieldName"
:label="obj.fieldName"
:value="obj.fieldName"
:disabled="obj.disabled"
/>
</el-select>
</el-form-item>
<div class="block-body-equal">=</div>
<el-form-item :prop="`data.${index}.list.${ind}.sourceField`" :rules="mappingRules.sourceField">
<el-select v-model="ite.sourceField" filterable class="block-body__select" placeholder="">
<el-option
v-for="obj in mappingFieldOption"
:key="obj.name"
:label="obj.name"
:value="obj.name"
/>
</el-select>
</el-form-item>
<i class="cn-icon cn-icon-add mapping-item-add" @click="addMappingListItem(index, ind)"></i>
<i class="cn-icon cn-icon-close mapping-item-close" @click="deleteMappingItem(index, ind)" v-if="ind > 0"></i>
</div>
</div>
</el-form>
<div class="form-setting-type" v-if="showMappingType">
<div class="setting-type__header">
<div>{{ $t('setting.selectMappingType') }}</div>
<i class="cn-icon cn-icon-close" @click="showMappingType=false"></i>
</div>
<el-select v-model="mappingItemType" placeholder="" @change="changeMappingType">
<el-option
v-for="obj in typeList"
:key="obj.value"
:label="obj.label"
:value="obj.value"
/>
</el-select>
</div>
<div class="block__footer">
<!--新增mapping类型按钮-->
<el-button class="addFieldBtn" @click="addSchemaMappingItem">
<i class="cn-icon cn-icon-add add-field-btn"></i>
{{ $t('overall.add') }}
</el-button>
</div>
</div>
<div class="form-setting__btn">
<button @click="onContinue" class="business-button">{{ $t('detection.create.continue') }}</button>
</div>
</el-collapse-item>
</el-collapse>
</div>
<!--第二步Relation-->
<div class="es-form-collapse">
<el-collapse v-model="activeNames">
<el-collapse-item name="2">
<template #title>
<div class="form-collapse-header">
<div :class="activeNames.indexOf('2')>-1 ? 'form-collapse-header-no-active' : 'form-collapse-header-no'">2
</div>
<div class="form-collapse-header-title">{{ $t('setting.relation') }}</div>
</div>
</template>
<div class="form-collapse-content">
<el-form :model="editObj.relationData" ref="relationForm">
<div class="form-content__block">
<div class="block-body1" v-for="(item, index) in editObj.relationData.data" :key="index">
<el-form-item :prop="`data.${index}.from_entity_index`" :rules="relationRules">
<el-select
v-model="item.from_entity_index"
class="relation-field__select"
placeholder=""
@visible-change="visibleFromEntity($event, index)">
<el-option
v-for="obj in editObj.schemaMappingData.data"
:key="obj.index"
:label="obj.name"
:value="obj.index"
:disabled="obj.disabled"
/>
</el-select>
</el-form-item>
<div class="block-body-equal">
<i class="cn-icon cn-icon-mutual"></i>
</div>
<el-form-item :prop="`data.${index}.to_entity_index`" :rules="relationRules">
<el-select
v-model="item.to_entity_index"
class="relation-field__select"
placeholder=""
@visible-change="visibleToEntity($event, index)">
<el-option
v-for="obj in editObj.schemaMappingData.data"
:key="obj.index"
:label="obj.name"
:value="obj.index"
:disabled="obj.disabled"
/>
</el-select>
</el-form-item>
<el-form-item :prop="`data.${index}.type`" :rules="relationRules">
<el-select v-model="item.type" class="relation-type__select" :placeholder="$t('setting.relationType')">
<el-option
v-for="obj in relationTypeOption"
:key="obj.value"
:label="obj.label"
:value="obj.value"
/>
</el-select>
</el-form-item>
<i class="cn-icon cn-icon-close mapping-item-close" @click="deleteRelationItem(index)"></i>
</div>
</div>
</el-form>
<div class="block__footer">
<!--新增按钮-->
<el-button class="addFieldBtn" @click="addRelationItem">
<i class="cn-icon cn-icon-add add-field-btn"></i>
{{ $t('overall.add') }}
</el-button>
</div>
</div>
</el-collapse-item>
</el-collapse>
</div>
<div class="switch__block margin-20">
<div class="block-title"><span class="from-dot">*</span>{{ $t('overall.status') }}</div>
<el-switch
v-model="editObj.enable"
:active-value="1"
:inactive-value="0"
:active-text="$t(switchStatus(editObj.enable))"/>
</div>
</div>
<div class="es-form__footer">
<button class="business-button business-button--light tag__btn" @click="cancel">
<span>{{ $t('overall.cancel') }}</span>
</button>
<button style="position: relative;" class="business-button tag__btn" :disabled="disabledSave" @click="saveEntity">
<!-- <loading :loading="blockOperation.save"></loading>-->
<span>{{ $t('overall.save') }}</span>
</button>
</div>
</div>
</template>
<script>
import { api } from '@/utils/api'
import axios from 'axios'
import { useRoute } from 'vue-router'
import { ref } from 'vue'
import Loading from '@/components/common/Loading'
import { ElMessageBox } from 'element-plus'
import { toUpperCaseByString, switchStatus } from '@/utils/tools'
import { settingFields } from '@/utils/constants'
export default {
name: 'EntitySettingForm',
data () {
return {
activeNames: ['1'],
intervalList: [],
sourceRules: {
sourceId: [{ required: true, message: this.$t('validate.required'), trigger: 'change' }]
},
mappingRules: {
entityField: [{ required: true, message: this.$t('validate.required'), trigger: 'change' }],
sourceField: [{ required: true, message: this.$t('validate.required'), trigger: 'change' }]
},
relationRules: { required: true, message: this.$t('validate.required'), trigger: 'change' },
sourceOption: [],
mappingFieldOption: [],
mappingSourceOption: [
{ value: 'dns_server_role', label: 'dns_server_role' },
{ value: 'ip_addr', label: 'ip_addr' },
{ value: 'category_name', label: 'category_name' }
],
typeOption: [
{ label: 'IP', value: 'IP' },
{ label: 'Domain', value: 'Domain' },
{ label: 'APP', value: 'APP' }
],
showMappingType: false,
mappingItemType: '', // 添加mapping映射类型
typeList: [
{ value: 'ip', label: 'ip' },
{ value: 'domain', label: 'domain' },
{ value: 'app', label: 'app' },
{ value: 'subscriber', label: 'subscriber' },
{ value: 'cell', label: 'cell' }
],
relationTypeOption: [
{ value: 'RESOLVE_DOMAIN_IP', label: 'RESOLVE_DOMAIN_IP' },
{ value: 'CARRY_APP_IP', label: 'CARRY_APP_IP' },
{ value: 'CARRY_APP_DOMAIN', label: 'CARRY_APP_DOMAIN' },
{ value: 'VISIT_SUBSCRIBER_APP', label: 'VISIT_SUBSCRIBER_APP' },
{ value: 'CALL_SUBSCRIBER_SUBSCRIBER', label: 'CALL_SUBSCRIBER_SUBSCRIBER' }
],
settingFields
}
},
components: {
Loading
},
computed: {
disabledSave () {
return this.editObj.isBuiltIn > 0 || this.isView
}
},
mounted () {
this.initSourceData()
if (this.ruleId) {
this.getDetailInfo()
}
},
setup () {
const { query, name } = useRoute()
const ruleId = ref(query.id || '')
const pageNoForTable = ref(query.pageNoForTable || 1)
const myLoading = ref(!!ruleId.value)
// 第二步的form表单信息
const ruleObj = ref({})
const editObj = ref({
id: '',
sourceId: '',
entities: [],
schemaMappingData: {
data: [
{
index: 1,
type: 'ip',
name: 'IP',
list: [
{ entityField: 'ip', sourceField: '' }
],
mapping: {}
}
]
},
relations: [],
relationData: {
data: []
},
enable: 1
})
const isView = name === 'viewEntityIntegration'
return {
ruleId,
myLoading,
pageNoForTable,
editObj,
ruleObj,
isView
}
},
methods: {
switchStatus,
initSourceData () {
axios.get(api.setting.source.source).then(response => {
if (response.status === 200) {
if (!response.data.data) {
throw new Error('No data found, id: ' + this.ruleId)
}
this.sourceOption = response.data.data.list
if (this.sourceOption && this.sourceOption.length > 0) {
this.sourceOption.forEach((item, index) => {
if (typeof item.fields === 'string') {
this.sourceOption[index].fieldsList = JSON.parse(item.fields)
}
if (typeof item.lookups === 'string') {
this.sourceOption[index].lookupsList = JSON.parse(item.lookups)
}
})
}
} else {
console.error(response.data)
}
}).catch(e => {
console.error(e)
this.$message.error(this.errorMsgHandler(e))
this.$router.push({
path: '/setting/entityIntegration',
query: {
pageNo: this.pageNoForTable ? Number(this.pageNoForTable) : 1,
t: +new Date()
}
})
})
},
/** 编辑时获取详情 */
getDetailInfo () {
axios.get(`${api.setting.profiles.profiles}/${this.ruleId}`).then(response => {
if (response.status === 200) {
if (!response.data.data) {
throw new Error('No data found, id: ' + this.ruleId)
}
this.myLoading = false
this.editObj = { ...this.editObj, ...response.data.data, ruleId: this.ruleId }
if (this.editObj.entities && typeof this.editObj.entities === 'string') {
const entities = JSON.parse(this.editObj.entities)
entities.forEach(item => {
const list = []
for (const [key, value] of Object.entries(item.mapping)) {
list.push({ entityField: key, sourceField: value.toString() })
}
item.list = list
})
this.editObj.schemaMappingData.data = entities
}
if (this.editObj.relations && typeof this.editObj.relations === 'string') {
this.editObj.relationData.data = JSON.parse(this.editObj.relations)
}
this.onChangeSource(this.editObj.sourceId)
this.activeNames = ['1', '2']
} else {
console.error(response.data)
}
}).catch(e => {
console.error(e)
this.$message.error(this.errorMsgHandler(e))
this.$router.push({
path: '/setting/entityIntegration',
query: {
pageNo: this.pageNoForTable ? Number(this.pageNoForTable) : 1,
t: +new Date()
}
})
})
},
onContinue () {
this.$refs.mappingForm.validate(valid => {
if (valid) {
this.activeNames = ['1', '2']
}
})
},
addSchemaMappingItem () {
this.showMappingType = true
},
changeMappingType () {
const mappingList = this.editObj.schemaMappingData.data.filter(d => d.type === this.mappingItemType)
let name = ''
// 添加item后按顺序给name添加名字
if (mappingList.length === 0) {
name = this.handleMappingName(this.mappingItemType)
} else if (mappingList.length === 1) {
mappingList[0].name = mappingList[0].name + 1
name = this.handleMappingName(this.mappingItemType, mappingList.length)
} else {
name = this.handleMappingName(this.mappingItemType, mappingList.length)
}
this.editObj.schemaMappingData.data.push({
index: this.editObj.schemaMappingData.data.length + 1,
type: this.mappingItemType,
name: name,
list: [{ entityField: settingFields[this.mappingItemType][0].fieldName, sourceField: '' }],
mapping: {}
})
this.showMappingType = false
this.mappingItemType = ''
},
/** 添加relation **/
addRelationItem () {
this.$refs.relationForm.validate(valid => {
if (valid) {
this.editObj.relationData.data.push({ from_entity_index: '', to_entity_index: '', type: '' })
}
})
},
/** 添加schema mapping某一类型下的字段 **/
async addMappingListItem (index, ind) {
const valid1 = await this.$refs.mappingForm.validateField(`data.${index}.list.${ind}.entityField`, (valid) => {
return valid
})
const valid2 = await this.$refs.mappingForm.validateField(`data.${index}.list.${ind}.sourceField`, (valid) => {
return valid
})
if (valid1 && valid2) {
const emptyIndex = this.editObj.schemaMappingData.data[index].list.findIndex(d => !d.entityField || !d.sourceField)
if (emptyIndex === -1) {
this.editObj.schemaMappingData.data[index].list.push({ entityField: '', sourceField: '' })
} else {
this.$refs.mappingForm.validateField(`data.${index}.list.${emptyIndex}.entityField`, () => null)
this.$refs.mappingForm.validateField(`data.${index}.list.${emptyIndex}.sourceField`, () => null)
}
}
},
/** 删除mapping下的子项 **/
deleteMappingItem (index, ind) {
const currentIndex = this.editObj.schemaMappingData.data[index].index
const obj = this.editObj.relationData.data.find(d => d.from_entity_index === currentIndex || d.to_entity_index === currentIndex)
if (obj) {
// 在relation中有关联的删除子项不提示删除块级mapping才提示
if (ind >= 0) {
this.editObj.schemaMappingData.data[index].list.splice(ind, 1)
} else {
const name = this.editObj.schemaMappingData.data[index].name
ElMessageBox.alert(`${name} ${this.$t('setting.deleteMappingTip')}`, this.$t('overall.tip'), {
confirmButtonText: 'OK',
callback: () => {}
})
}
} else {
// 删除mapping同类型的name需要重新排序先获取type避免删除后获取不到type
const type = this.editObj.schemaMappingData.data[index].type
// 删除mapping的大块或者其子项删除后排序index
if (ind >= 0) {
this.editObj.schemaMappingData.data[index].list.splice(ind, 1)
} else {
this.editObj.schemaMappingData.data.splice(index, 1)
this.editObj.schemaMappingData.data.forEach((item, i) => {
item.index = i + 1
})
}
if (this.editObj.schemaMappingData.data.length === 0) {
this.sourceOption.forEach(item => {
item.disabled = false
})
return true
}
const list = this.editObj.schemaMappingData.data.filter(d => d.type === type)
if (list && list.length > 0) {
if (list.length === 1) {
list[0].name = this.handleMappingName(list[0].type)
} else {
list.forEach((item, i) => {
item.name = this.handleMappingName(item.type, i)
})
}
}
// 判断sourceFiled即mapping第二列是否引用了source如果引用删除mapping之后source下拉框的禁选解除可正常选择source
let usedFlag = false
if (this.editObj.schemaMappingData.data.length > 0) {
this.editObj.schemaMappingData.data.forEach(item => {
const obj1 = item.list.find(d => d.sourceField)
if (obj1 && !usedFlag) {
usedFlag = true
}
})
} else {
usedFlag = false
}
if (usedFlag && this.editObj.sourceId) {
this.sourceOption.forEach(item => {
item.disabled = item.id !== this.editObj.sourceId
})
} else if (!usedFlag) {
this.sourceOption.forEach(item => {
item.disabled = false
})
}
}
},
/** 删除relation的子项 **/
deleteRelationItem (index) {
this.editObj.relationData.data.splice(index, 1)
},
/** 修改mapping的模块名 **/
handleMappingName (type, index) {
let name = ''
if (type === 'ip' || type === 'app') {
name = index >= 0 ? type.toUpperCase() + (index + 1) : type.toUpperCase()
} else {
name = index >= 0 ? toUpperCaseByString(type) + (index + 1) : toUpperCaseByString(type)
}
return name
},
/** relation第一个下拉框出现时的操作 */
visibleFromEntity (callback, index) {
if (callback) {
// true出现下拉框false隐藏下拉框
const item = this.editObj.relationData.data[index]
if (!item.from_entity_index && !item.to_entity_index) {
// from和to都为空的话不禁选
this.editObj.schemaMappingData.data.forEach(ite => {
ite.disabled = false
})
} else if (item.to_entity_index) {
// to已经选择则禁选to本身的index和to相关联的index
const obj = this.editObj.schemaMappingData.data.find(d => d.index === item.to_entity_index)
obj.disabled = true
const filterList = this.editObj.relationData.data.filter(d => d.from_entity_index === item.to_entity_index || d.to_entity_index === item.to_entity_index)
filterList.forEach(ite => {
const obj1 = this.editObj.schemaMappingData.data.find(d => d.index === ite.to_entity_index)
const obj2 = this.editObj.schemaMappingData.data.find(d => d.index === ite.from_entity_index)
if (obj1) {
obj1.disabled = true
}
if (obj2) {
obj2.disabled = true
}
})
}
} else {
this.editObj.schemaMappingData.data.forEach(ite => {
ite.disabled = false
})
}
},
/** relation第二个下拉框出现时的操作 */
visibleToEntity (callback, index) {
if (callback) {
// true出现下拉框false隐藏下拉框
const item = this.editObj.relationData.data[index]
if (!item.from_entity_index && !item.to_entity_index) {
// from和to都为空的话不禁选
this.editObj.schemaMappingData.data.forEach(ite => {
ite.disabled = false
})
} else if (item.from_entity_index) {
// from已经选择则禁选from本身的index和from相关联的index
const obj = this.editObj.schemaMappingData.data.find(d => d.index === item.from_entity_index)
obj.disabled = true
const filterList = this.editObj.relationData.data.filter(d => d.from_entity_index === item.from_entity_index || d.to_entity_index === item.from_entity_index)
filterList.forEach(ite => {
const obj1 = this.editObj.schemaMappingData.data.find(d => d.index === ite.from_entity_index)
const obj2 = this.editObj.schemaMappingData.data.find(d => d.index === ite.to_entity_index)
if (obj1) {
obj1.disabled = true
}
if (obj2) {
obj2.disabled = true
}
})
}
} else {
this.editObj.schemaMappingData.data.forEach(ite => {
ite.disabled = false
})
}
},
/** 创建entity */
async saveEntity () {
const valid1 = await this.$refs.sourceForm.validate((valid) => {
return valid
})
const valid2 = await this.$refs.mappingForm.validate((valid) => {
return valid
})
if (valid1 && valid2) {
const formObj = this.$_.cloneDeep(this.editObj)
formObj.schemaMappingData.data.forEach((item) => {
const obj = {}
item.list.forEach(ite => {
obj[ite.entityField] = ite.sourceField
})
item.mapping = this.$_.cloneDeep(obj)
})
formObj.entities = JSON.stringify(formObj.schemaMappingData.data)
formObj.relations = JSON.stringify(formObj.relationData.data)
if (formObj.schemaMappingData.data.length === 0) {
this.activeNames = ['1', '2']
ElMessageBox.alert(this.$t('detection.create.informationFilled'), this.$t('overall.tip'), {
confirmButtonText: 'OK',
callback: () => {}
})
return true
}
if (formObj.relationData.data.length > 0) {
const obj = formObj.relationData.data.find(d => !d.from_entity_index || !d.to_entity_index || !d.type)
if (obj) {
this.activeNames = ['1', '2']
this.$refs.relationForm.validate(() => null)
ElMessageBox.alert(this.$t('detection.create.informationFilled'), this.$t('overall.tip'), {
confirmButtonText: 'OK',
callback: () => {}
})
return true
}
}
this.myLoading = true
if (!this.ruleId) {
// post调用是新增put是编辑
axios.post(api.setting.profiles.profiles, formObj).then(response => {
if (response.status === 200) {
this.$message({ duration: 2000, type: 'success', message: this.$t('tip.saveSuccess') })
this.$router.push({
path: '/setting/entityIntegration',
query: {
t: +new Date()
}
})
} else {
console.error(response.data.message)
this.$message.error(this.errorMsgHandler(response))
}
}).catch(e => {
console.error(e)
this.$message.error(this.errorMsgHandler(e))
}).finally(() => {
this.myLoading = false
})
} else {
axios.put(api.setting.profiles.profiles, formObj).then(response => {
if (response.status === 200) {
this.$message({ duration: 2000, type: 'success', message: this.$t('tip.saveSuccess') })
const { query } = this.$route
const queryInfo = {
pageNo: self.pageNoForTable ? Number(self.pageNoForTable) : 1,
t: +new Date()
}
if (query.name && query.id) {
queryInfo.ruleId = query.id
queryInfo.name = this.settingObj.name
}
this.$router.push({
path: '/setting/entityIntegration',
query: queryInfo
})
} else {
console.error(response.data.message)
this.$message.error(this.errorMsgHandler(response))
}
}).catch(e => {
console.error(e)
this.$message.error(this.errorMsgHandler(e))
}).finally(() => {
this.myLoading = false
})
}
}
},
/** 底部返回按钮 */
cancel () {
const { query } = this.$route
const queryInfo = {
pageNo: this.pageNoForTable ? Number(this.pageNoForTable) : 1,
t: +new Date()
}
if (query.name && query.id) {
queryInfo.ruleId = query.id
queryInfo.name = this.settingObj.name
}
// if (this.settingObj.editFlag || this.ruleObj.editFlag) {
// this.confirmMessage(queryInfo)
// } else {
// }
this.$router.push({
path: '/setting/entityIntegration',
query: queryInfo
})
},
confirmMessage (queryInfo) {
ElMessageBox.confirm(this.$t('tip.leavePage'), {
confirmButtonText: this.$t('tip.confirm'),
cancelButtonText: this.$t('overall.cancel'),
message: this.$t('tip.leavePageTips'),
title: this.$t('tip.leavePage'),
// type: 'warning',
iconClass: 'width:0px;height:0px;',
customClass: 'del-model'
}).then(() => {
this.$router.push({
path: '/setting/entityIntegration',
query: queryInfo
})
}).catch(() => {
})
},
/** 切换source数据源 */
onChangeSource (id) {
const obj = this.sourceOption.find(d => d.id === id)
if (obj) {
const fieldList = obj.fieldsList
const myLookupsList = []
obj.lookupsList.forEach(item => {
myLookupsList.push({ name: item.output_field })
})
this.mappingFieldOption = fieldList.concat(myLookupsList)
} else {
this.mappingFieldOption = []
}
},
visibleSource (callback) {
if (callback) {
// 如果relation中有数据则禁选source
const obj = this.editObj.relationData.data.find(d => d.from_entity_index || d.to_entity_index)
if (obj && this.editObj.sourceId) {
this.sourceOption.forEach(item => {
item.disabled = item.id !== this.editObj.sourceId
})
}
let usedFlag = false
if (this.editObj.schemaMappingData.data.length > 0) {
this.editObj.schemaMappingData.data.forEach(item => {
const obj1 = item.list.find(d => d.sourceField)
if (obj1 && !usedFlag) {
usedFlag = true
}
})
} else {
usedFlag = false
}
if (usedFlag && this.editObj.sourceId) {
this.sourceOption.forEach(item => {
item.disabled = item.id !== this.editObj.sourceId
})
} else if (!usedFlag) {
this.sourceOption.forEach(item => {
item.disabled = false
})
}
}
},
visibleEntityFiled (callback, index, i) {
const item = this.editObj.schemaMappingData.data[index]
if (callback) {
const fieldNameList = []
item.list.forEach(ite => {
fieldNameList.push(ite.entityField)
})
this.settingFields[item.type].forEach(obj => {
if (fieldNameList.indexOf(obj.fieldName) > -1) {
obj.disabled = true
}
})
} else {
this.settingFields[item.type].forEach(obj => {
obj.disabled = false
})
}
}
}
}
</script>

View File

@@ -1,256 +0,0 @@
<template>
<div class="cn-tag">
<div class="cn-tag-right">
<cn-data-list
ref="dataList"
:tableId="tableId"
v-model:custom-table-title="tools.customTableTitle"
:api="url"
:from="fromRoute.tag"
:layout="['search']"
:input-width="'100%'"
@search="search"
>
<template #top-tool-left>
<button id="account-add" class="business-button tag__btn margin-r-10" @click="add">
<i class="cn-icon-xinjian cn-icon"></i>
<span>{{ $t('overall.create') }}</span>
</button>
<button id="tag-edit" class="business-button business-button--light tag__btn margin-r-10"
:disabled="disableEdit"
@click="editSource">
<i class="cn-icon-edit cn-icon"></i>
<span>{{ $t('overall.edit') }}</span>
</button>
<button id="tag-view" class="business-button business-button--light tag__btn margin-r-10 tool-btn-view"
:disabled="disableView"
@click="viewSource">
<i class="cn-icon-preview cn-icon"></i>
<span>{{ $t('overall.view') }}</span>
</button>
<button id="tag-delete" class="business-button business-button--light tag__btn margin-r-10"
:disabled="disableDelete"
@click="delBatch">
<i class="cn-icon-delete cn-icon"></i>
<span>{{ $t('overall.delete') }}</span>
</button>
</template>
<template #default>
<loading :loading="loading"></loading>
<sources-table
ref="dataTable"
:api="url"
:isNoData="isNoData"
:custom-table-title="tools.customTableTitle"
:height="mainTableHeight"
:table-data="tableData"
@delete="del"
@edit="edit"
@download="download"
@preview="preview"
@reload="getTableData"
@selectionChange="selectionChange"
/>
</template>
<template #pagination>
<pagination ref="pagination" :page-obj="pageObj" :tableData="tableData" :table-id="tableId" @pageNo='pageNo'
@pageSize='pageSize'></pagination>
</template>
</cn-data-list>
</div>
</div>
</template>
<script>
import axios from 'axios'
import SourcesTable from '@/components/table/setting/SourcesTable'
import cnDataList from '@/components/table/CnDataList'
import dataListMixin from '@/mixins/data-list'
import { api } from '@/utils/api'
import { tagIntentOptions, tagCategoryOptions, tagSourceOptions } from '@/utils/constants'
import Loading from '@/components/common/Loading'
import { ref } from 'vue'
import { useRoute } from 'vue-router'
export default {
name: 'Sources',
data () {
return {
builtinColor: false,
url: api.setting.source.source,
tagIntentOptions,
tagCategoryOptions,
tagSourceOptions,
tableId: 'sourcesTable',
builtinLeftLoading: false,
isInit: true,
intent: '',
category: '',
source: '',
name: ''
}
},
setup () {
const { query } = useRoute()
const urlPageNo = ref(parseInt(query.pageNo) || 1)
return {
urlPageNo
}
},
mixins: [dataListMixin],
components: {
Loading,
cnDataList,
SourcesTable
},
mounted () {
this.$nextTick(() => {
this.getTableData()
})
},
methods: {
search (params) {
if (this.$_.isNumber(params.q) || params.q.indexOf(',') > -1) {
params = { ids: params.q }
} else {
params = { name: params.q }
}
this.pageObj.pageNo = 1
this.getTableData(params)
this.$refs.dataTable.expandedIds = []
},
delBatch () {
const ids = []
if (this.batchDeleteObjs && this.batchDeleteObjs.length > 0) {
this.batchDeleteObjs.forEach(item => {
ids.push(item.id)
})
}
if (ids.length === 0) {
this.$alert(this.$t('tip.pleaseSelect'), {
confirmButtonText: this.$t('tip.yes'),
type: 'warning'
}).catch(() => {
})
} else {
this.$confirm(this.$t('tip.confirmDelete'), {
confirmButtonText: this.$t('tip.yes'),
cancelButtonText: this.$t('tip.no'),
customClass: 'del-model-message-box',
type: 'warning'
}).then(() => {
this.toggleLoading(true)
axios.delete(this.url + '?ids=' + ids).then(response => {
if (response.status === 200) {
this.delFlag = true
this.$message({
duration: 2000,
type: 'success',
message: this.$t('tip.deleteSuccess')
})
this.getTableData()
} else {
this.$message.error(response.data.message)
}
}).catch(e => {
this.$message.error(e.response.data.message)
}).finally(() => {
this.toggleLoading(false)
})
}).finally(() => {
if (this.isSelectedStatus !== undefined) {
this.isSelectedStatus = false
this.disableDelete = true
this.batchDeleteObjs = []
this.$refs.dataTable.expandedIds = []
}
}).catch(() => {})
}
},
getTableData (params) {
this.searchLabel = null
if (params) {
this.searchLabel = { ...this.searchLabel, ...params }
}
this.searchLabel = { ...this.searchLabel, ...this.pageObj }
this.isNoData = false
this.toggleLoading(true)
// delete this.searchLabel.total
let listUrl = this.url
if (this.listUrl) {
listUrl = this.listUrl
}
if (!this.isInit) {
axios.get(listUrl, { params: this.searchLabel }).then(response => {
if (response.status === 200) {
this.$nextTick(() => {
this.tableData = response.data.data.list.map(item => {
return {
...item,
config: item.config ? JSON.parse(item.config) : {}
}
})
this.pageObj.total = response.data.data.total
this.isNoData = !this.tableData || this.tableData.length === 0
})
// TODO 回到顶部
} else {
this.isNoData = true
}
}).finally(() => {
this.toggleLoading(false)
this.$refs.dataTable.expandedIds = []
})
}
this.isInit = false
},
add () {
this.$router.push({
path: '/setting/dataSource/create',
query: {
t: +new Date()
}
})
},
edit (u) {
this.object = u
this.rightBox.show = true
},
editSource () {
if (this.batchDeleteObjs.length === 0) {
this.$alert(this.$t('tip.pleaseSelectForEdit'), {
confirmButtonText: this.$t('tip.yes'),
type: 'warning'
}).catch(() => {
})
} else {
this.jumpToEditPage(this.batchDeleteObjs[0].id)
}
},
viewSource () {
const pageNo = this.$router.currentRoute.value.query.pageNo
this.$router.push({
path: '/setting/dataSource/view',
query: {
t: +new Date(),
pageNoForTable: pageNo || 1,
id: this.batchDeleteObjs[0].id
}
})
},
jumpToEditPage (id) {
const pageNo = this.$router.currentRoute.value.query.pageNo
this.$router.push({
path: '/setting/dataSource/edit',
query: {
t: +new Date(),
pageNoForTable: pageNo || 1,
id: id
}
})
}
}
}
</script>

View File

@@ -1,542 +0,0 @@
<template>
<div class="sources-form">
<loading :loading="myLoading"></loading>
<div class="sources-form__header">{{ pageHeaderName }}</div>
<div class="sources-form__body">
<el-form ref="baseForm" :model="sourceObj" label-position="top" style="width: 620px" :rules="rules">
<el-form-item :label="$t('overall.name')" prop="name">
<el-input v-model="sourceObj.name" />
</el-form-item>
<el-form-item :label="$t('sources.dataFormat')" prop="dataFormat">
<el-select v-model="sourceObj.dataFormat" placeholder="" :disabled="sourceObj.id !== ''">
<el-option
v-for="item in formatOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
</el-form>
<div class="form__body__label"><span class="from-dot">*</span>{{ $t('overall.fields') }}</div>
<el-form ref="fieldsForm" :model="sourceObj.fieldsData">
<div class="form-fields__block">
<div class="block__header">
<div class="block__header-name">{{ $t('overall.name') }}</div>
<div class="block__header-type">{{ $t('overall.type') }}</div>
<div class="block__header-default">{{ $t('overall.default') }}</div>
</div>
<div class="block__body">
<div class="block__body-container" v-for="(item, index) in sourceObj.fieldsData.data" :key="index">
<el-form-item :prop="`data.${index}.name`" :rules="fieldsRules.name">
<div class="block__body-name">
<el-input :ref="`name-${index}`" v-model="item.name" />
</div>
</el-form-item>
<el-form-item :prop="`data.${index}.type`" :rules="fieldsRules.type">
<div class="block__body-type">
<el-select ref="field-type" v-model="item.type" placeholder="">
<el-option
v-for="ite in typeOption"
:key="ite.value"
:label="ite.label"
:value="ite.value"
/>
</el-select>
</div>
</el-form-item>
<el-form-item :prop="`data.${index}.default`">
<div class="block__body-default">
<el-input :ref="`default-${index}`" v-model="item.default" />
</div>
</el-form-item>
<i class="cn-icon cn-icon-close remove-btn" @click="removeFieldItem(index)"></i>
</div>
</div>
<div class="block__footer">
<!--新增按钮-->
<el-button class="addFieldBtn" @click="addFieldItem">
<i class="cn-icon cn-icon-add add-field-btn"></i>
{{$t('overall.add')}}
</el-button>
</div>
</div>
</el-form>
<div class="form__body__label">{{ $t('setting.lookups') }}</div>
<el-form ref="lookupsForm" :model="sourceObj.lookupsData">
<div class="form-fields__block">
<div class="block__body">
<div class="block__body-container" v-for="(item, index) in sourceObj.lookupsData.data" :key="index">
<el-form-item :prop="`data.${index}.function`" :rules="lookupRules.function">
<div class="block__body-function">
<el-select v-model="item.function" :placeholder="$t('setting.functionName')" @change="onChangeFunction(index)">
<el-option
v-for="ite in lookupsObj.functionOption"
:key="ite.value"
:label="ite.label"
:value="ite.value"
/>
</el-select>
</div>
</el-form-item>
<el-form-item :prop="`data.${index}.lookup_field`" :rules="lookupRules.lookup_field">
<div class="block__body-field">
<el-select v-model="item.lookup_field" :placeholder="$t('setting.lookupField')">
<el-option
v-for="ite in lookupsObj.lookupFieldsOption"
:key="ite.name"
:label="ite.name"
:value="ite.name"
/>
</el-select>
</div>
</el-form-item>
<el-form-item :prop="`data.${index}.output_type`" :rules="lookupRules.output_type">
<div class="block__body-field">
<el-select v-model="item.output_type" :placeholder="$t('setting.outputField')">
<el-option
v-for="ite in lookupsObj.outputFieldsOption[item.function]"
:key="ite.value"
:label="ite.label"
:value="ite.value"
/>
</el-select>
</div>
</el-form-item>
<el-form-item :prop="`data.${index}.output_field`" :rules="lookupRules.output_field">
<div class="block__body-field-name">
<el-input v-model="item.output_field" :placeholder="$t('setting.outputFieldName')" />
</div>
</el-form-item>
<i class="cn-icon cn-icon-close remove-btn" @click="removeLookupItem(index)"></i>
</div>
</div>
<div class="block__footer">
<!--新增按钮-->
<el-button class="addFieldBtn" @click="addLookupItem">
<i class="cn-icon cn-icon-add add-field-btn"></i>
{{$t('overall.add')}}
</el-button>
</div>
</div>
</el-form>
<div class="form__body__label">{{ $t('overall.remark') }}</div>
<el-input
class="form-description"
maxlength="255"
show-word-limit
:rows="5"
size='mini'
type="textarea"
resize='none'
v-model="sourceObj.description"
id="role-box-input-remark"/>
<!-- <div class="form-setting__block margin-b-20">-->
<!-- <div class="block-title"><span class="from-dot">*</span>{{ $t('overall.status') }}</div>-->
<!-- <el-switch-->
<!-- v-model="sourceObj.enable"-->
<!-- :active-value="1"-->
<!-- :inactive-value="0"-->
<!-- :active-text="$t(switchStatus(sourceObj.enable))"/>-->
<!-- </div>-->
</div>
<div class="sources-form__footer">
<button class="business-button business-button--light tag__btn" @click="cancel">
<span>{{ $t('overall.cancel') }}</span>
</button>
<button style="position: relative;" :class="{'disabled': blockOperation.save}"
:disabled="disabledSave" class="business-button tag__btn" @click="saveSource">
<loading :loading="blockOperation.save"></loading>
<span>{{ $t('overall.save') }}</span>
</button>
</div>
</div>
</template>
<script>
import { ref } from 'vue'
import _ from 'lodash'
import Loading from '@/components/common/Loading'
import { ElMessageBox } from 'element-plus'
import i18n from '@/i18n'
import { useRoute } from 'vue-router'
import axios from 'axios'
import { api } from '@/utils/api'
import { switchStatus } from '@/utils/tools'
export default {
name: 'SourceForm',
components: {
Loading
},
data () {
const nameDuplicateValidator = async (rule, value, callback) => {
let validate = true
if (!this.sourceObj.id) {
const response = await this.getSourceList()
if (response.status === 200) {
const find = response.data.data.list.find(d => d.name === value)
if (find) {
validate = false
callback(new Error())
}
}
}
return validate
}
const nameValidator = (rule, value, callback) => {
// 校验value包含字母、数字和下划线并且不是全数字、全下划线和连续下划线
const regex = /^(?=.*[a-zA-Z])(?=.*\d|^(?!\d+$))[a-zA-Z\d]+(?:_[a-zA-Z\d]+)*$/
return regex.test(value)
}
const nameRepeat = (rule, value, callback) => {
// 字段重复校验
let validate = true
const repeatList = this.sourceObj.fieldsData.data.filter(d => d.name === value)
if (repeatList.length > 1) {
validate = false
}
return validate
}
const outputFieldRepeat = (rule, value, callback) => {
// 输出字段不能和输入字段重名
let validate = true
const repeatList = this.sourceObj.fieldsData.data.filter(d => d.name === value)
if (repeatList.length > 0) {
validate = false
}
return validate
}
return {
rules: {
name: [
{ required: true, message: this.$t('validate.required'), trigger: 'blur' },
{ validator: nameDuplicateValidator, message: this.$t('validate.duplicateRecord', { columns: `(${this.$t('config.roles.name')})` }), trigger: 'blur' }
],
dataFormat: [
{ required: true, message: this.$t('validate.required'), trigger: 'change' }
]
},
fieldsRules: {
name: [
{ required: true, message: this.$t('validate.required'), trigger: 'blur' },
{ validator: nameValidator, message: i18n.global.t('validate.onlyAllowNumberLetter_'), trigger: 'blur' },
{ validator: nameRepeat, message: i18n.global.t('validate.fieldDuplicate'), trigger: 'blur' }
],
type: [
{ required: true, message: this.$t('validate.required'), trigger: 'change' }
]
},
lookupRules: {
function: [{ required: true, message: this.$t('validate.required'), trigger: 'change' }],
lookup_field: [{ required: true, message: this.$t('validate.required'), trigger: 'change' }],
output_type: [{ required: true, message: this.$t('validate.required'), trigger: 'change' }],
output_field: [
{ required: true, message: this.$t('validate.required'), trigger: 'blur' },
{ validator: nameValidator, message: i18n.global.t('validate.onlyAllowNumberLetter_'), trigger: 'blur' },
{ validator: outputFieldRepeat, message: i18n.global.t('validate.outputFieldNotLookupField'), trigger: 'blur' }
]
},
// format下拉框数据
formatOptions: [
{ label: 'json', value: 'json' },
{ label: 'csv', value: 'csv' }
],
// field的type下拉框
typeOption: [
{ label: 'string', value: 'string' },
{ label: 'int', value: 'int' },
{ label: 'float', value: 'float' },
{ label: 'long', value: 'long' },
{ label: 'double', value: 'double' }
],
blockOperation: { save: false },
addEditFlag: false, // 新增最后一个field标识
lookupsObj: {
// lookup的function name下拉列表
functionOption: [
{ label: 'GEOIP_LOOKUP', value: 'GEOIP_LOOKUP' },
{ label: 'ASN_LOOKUP', value: 'ASN_LOOKUP' }
],
// lookup的字段下拉列表
lookupFieldsOption: this.sourceObj.fieldsData.data,
// lookup的output字段下拉列表
outputFieldsOption: {
GEOIP_LOOKUP: [
{ label: 'COUNTRY', value: 'COUNTRY' },
{ label: 'PROVINCE', value: 'PROVINCE' },
{ label: 'CITY', value: 'CITY' },
{ label: 'LONGITUDE', value: 'LONGITUDE' },
{ label: 'LATITUDE', value: 'LATITUDE' },
{ label: 'ISP', value: 'ISP' },
{ label: 'ORGANIZATION', value: 'ORGANIZATION' }
],
ASN_LOOKUP: [
{ label: 'ASN', value: 'ASN' }
]
}
},
lookupsIndex: -1,
lookupsForm: {
function: '',
lookup_field: '',
output_type: '',
output_field: ''
}
}
},
computed: {
pageHeaderName () {
let name = this.$t('sources.createSource')
if (this.sourceObj.id) {
name = this.isView ? this.$t('setting.viewSource') : this.$t('sources.editSource')
}
return name
},
disabledSave () {
return this.sourceObj.usage > 0 || this.sourceObj.isBuiltIn > 0 || this.isView
}
},
mounted () {
if (this.sourceId) {
this.initDetailInfo()
}
},
methods: {
switchStatus,
/**
* 编辑时初始化source详情
*/
initDetailInfo () {
axios.get(`${api.setting.source.source}/${this.sourceId}`).then(response => {
if (response.status === 200) {
if (!response.data.data) {
throw new Error('No data found, id: ' + this.sourceId)
}
this.myLoading = false
this.sourceObj = { ...this.sourceObj, ...response.data.data, sourceId: this.sourceId }
this.sourceObj.fieldsData.data = JSON.parse(this.sourceObj.fields)
this.sourceObj.lookupsData.data = JSON.parse(this.sourceObj.lookups)
this.lookupsObj.lookupFieldsOption = this.sourceObj.fieldsData.data
} else {
console.error(response.data)
}
}).catch(e => {
console.error(e)
this.$message.error(this.errorMsgHandler(e))
this.$router.push({
path: '/setting/dataSource',
query: {
pageNo: this.pageNoForTable ? Number(this.pageNoForTable) : 1,
t: +new Date()
}
})
})
},
async saveSource () {
const valid1 = await this.$refs.baseForm.validate((valid) => {
return valid
})
const valid2 = await this.$refs.fieldsForm.validate((valid) => {
return valid
})
if (valid1 && valid2) {
this.sourceObj.fields = JSON.stringify(this.sourceObj.fieldsData.data)
this.sourceObj.lookups = JSON.stringify(this.sourceObj.lookupsData.data)
if (this.sourceObj.fieldsData.data.length === 0) {
ElMessageBox.alert(this.$t('detection.create.informationFilled'), this.$t('overall.tip'), {
confirmButtonText: 'OK',
callback: () => {}
})
return true
}
if (this.sourceObj.lookupsData.data.length > 0) {
const valid3 = await this.$refs.lookupsForm.validate((valid) => {
return valid
})
if (!valid3) {
ElMessageBox.alert(this.$t('detection.create.informationFilled'), this.$t('overall.tip'), {
confirmButtonText: 'OK',
callback: () => {}
})
return true
}
}
delete this.sourceObj.enable
this.myLoading = true
if (!this.sourceId) {
// post调用是新增put是编辑
axios.post(api.setting.source.source, this.sourceObj).then(response => {
if (response.status === 200) {
this.$message({
duration: 2000,
type: 'success',
message: this.$t('tip.saveSuccess')
})
this.$router.push({
path: '/setting/dataSource',
query: {
t: +new Date()
}
})
} else {
console.error(response.data.message)
this.$message.error(this.errorMsgHandler(response))
}
}).catch(e => {
console.error(e)
this.$message.error(this.errorMsgHandler(e))
}).finally(() => {
this.myLoading = false
})
} else {
axios.put(api.setting.source.source, this.sourceObj).then(response => {
if (response.status === 200) {
this.$message({ duration: 2000, type: 'success', message: this.$t('tip.saveSuccess') })
const { query } = this.$route
const queryInfo = {
pageNo: self.pageNoForTable ? Number(self.pageNoForTable) : 1,
t: +new Date()
}
if (query.name && query.id) {
queryInfo.sourceId = query.id
queryInfo.name = this.settingObj.name
}
this.$router.push({
path: '/setting/dataSource',
query: queryInfo
})
} else {
console.error(response.data.message)
this.$message.error(this.errorMsgHandler(response))
}
}).catch(e => {
console.error(e)
this.$message.error(this.errorMsgHandler(e))
}).finally(() => {
this.myLoading = false
})
}
}
},
cancel () {
const self = this
if (this.isPreviewChange) {
ElMessageBox.confirm(this.$t('tip.leavePage'), {
confirmButtonText: this.$t('tip.confirm'),
cancelButtonText: this.$t('overall.cancel'),
message: this.$t('tip.leavePageTips'),
title: this.$t('tip.leavePage'),
// type: 'warning',
iconClass: 'width:0px;height:0px;',
customClass: 'del-model'
}).then(() => {
this.$router.push({
path: '/setting/dataSource',
query: {
pageNo: self.pageNoForTable ? Number(self.pageNoForTable) : 1,
t: +new Date()
}
})
}).catch(() => {})
} else {
this.$router.push({
path: '/setting/dataSource',
query: {
pageNo: self.pageNoForTable ? Number(self.pageNoForTable) : 1,
t: +new Date()
}
})
}
},
addFieldItem () {
this.$refs.fieldsForm.validate(valid => {
if (valid) {
this.addEditFlag = true
this.sourceObj.fieldsData.data.push({ name: '', type: '', default: '' })
}
})
},
addLookupItem () {
this.$refs.lookupsForm.validate(valid => {
if (valid) {
this.lookupsIndex = -1
this.addEditFlag = true
this.sourceObj.lookupsData.data.push({ function: '', lookup_field: '', output_type: '', output_field: '' })
}
})
},
removeFieldItem (i) {
// 如果lookups里存在filed的字段则进行提示
const name = this.sourceObj.fieldsData.data[i].name
const obj = this.sourceObj.lookupsData.data.find(d => d.lookup_field === name && name)
if (obj) {
ElMessageBox.alert(`${name} ${this.$t('setting.deleteFieldTip')}`, this.$t('overall.tip'), {
confirmButtonText: 'OK',
callback: () => {}
})
} else {
this.addEditFlag = false
this.sourceObj.fieldsData.data.splice(i, 1)
}
},
removeLookupItem (i) {
this.addEditFlag = false
this.sourceObj.lookupsData.data.splice(i, 1)
},
// lookups的function切换重置outputField
onChangeFunction (index) {
this.sourceObj.lookupsData.data[index].output_type = ''
// this.$refs.lookupsForm.clearValidate('output_type')
},
async getSourceList () {
return await axios.get(api.setting.source.source, { params: { pageSize: 999 } }).catch(e => {
console.error(e)
this.$message.error(this.errorMsgHandler(e))
})
}
},
setup () {
const { query, name } = useRoute()
const sourceId = ref(query.id || '')
const pageNoForTable = ref(query.pageNoForTable || 1)
const myLoading = ref(!!sourceId.value)
const blankObject = {
id: '',
name: '',
dataFormat: '',
fields: [],
// fields列表数据
fieldsData: {
data: [{ name: '', type: '', default: '' }]
},
lookups: [],
// lookups列表数据
lookupsData: {
data: []
},
description: ''
// enable: 1
}
const sourceObj = ref(_.cloneDeep(blankObject))
const isView = name === 'viewDataSource'
return {
sourceId,
pageNoForTable,
myLoading,
sourceObj,
isView
}
}
}
</script>