CN-1733 fix: Source管理页面开发

This commit is contained in:
刘洪洪
2024-11-15 18:45:58 +08:00
parent fac1c82c96
commit b47e72ef3e
15 changed files with 2717 additions and 5 deletions

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-search, .top-tool-search1 {
.top-tool-search__display { .top-tool-search__display {
display: flex; display: flex;
} }
@@ -83,6 +83,13 @@
} }
} }
.top-tool-search1 {
width: 100%;
.el-input {
width: 100% !important;
}
}
.top-tool-right { .top-tool-right {
display: flex; display: flex;
} }

View File

@@ -89,3 +89,6 @@
@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

@@ -0,0 +1,306 @@
$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;
font-size: 12px;
color: var(--el-text-color-primary);
font-weight: bold;
margin-bottom: 10px;
div:nth-child(1) {
width: 288px;
}
}
.el-form {
display: flex;
flex-direction: column;
}
.block-body {
width: 596px;
display: flex;
.el-form-item {
margin-bottom: 10px;
}
.block-body__select {
width: 260px;
}
.block-body-equal {
margin: 0 10px;
}
.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: 312px;
margin: 0 10px;
}
}
}
.form-setting-type {
width: 620px;
border: 1px solid $border-color-light;
padding: 15px 12px;
margin-bottom: 10px;
.setting-type__text {
font-size: 14px;
margin-bottom: 10px;
}
}
.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;
}
}

View File

@@ -0,0 +1,189 @@
$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;
}
}
}

View File

@@ -199,6 +199,114 @@
} }
} }
} }
.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

@@ -138,6 +138,7 @@
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"> <div class="cn-menu__left" v-if="otherMenu">
<div class="left-menu" v-for="menu in otherMenu" :key="menu.id" @click="jumpOther(menu.route,'','',0)"> <div class="left-menu" v-for="menu in otherMenu" :key="menu.id" @click="jumpOther(menu.route,'','',0)">
@@ -146,7 +147,7 @@
<i class="cn-icon cn-icon-right"></i> <i class="cn-icon cn-icon-right"></i>
</div> </div>
</div> </div>
<div class="cn-menu__middle" > <div class="cn-menu__middle" v-if="!isShowSetting">
<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">
@@ -186,6 +187,27 @@
</div> </div>
</div> </div>
</div> </div>
<div class="cn-menu__middle" v-if="isShowSetting">
<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>
<!-- 改密码 --> <!-- 改密码 -->
@@ -336,16 +358,21 @@ export default {
wholeScreenRouterMapping, wholeScreenRouterMapping,
logo: 'images/logo-header.svg', logo: 'images/logo-header.svg',
ZH, ZH,
EN EN,
isShowSetting: false
} }
}, },
computed: { computed: {
networkAnalyticsMenu () { networkAnalyticsMenu () {
console.log('-------', this.$store.getters.menuList)
return this.$store.getters.menuList.find(menu => menu.code === 'networkAnalytics') return this.$store.getters.menuList.find(menu => menu.code === 'networkAnalytics')
}, },
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)
@@ -755,6 +782,11 @@ export default {
}, },
// 仅处理除panel外的相关路径的导航 // 仅处理除panel外的相关路径的导航
async jumpOther (route, index) { async jumpOther (route, index) {
console.log('route', 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) { // 当前只有一级菜单时,点击不进行刷新,重新跳转
@@ -893,6 +925,12 @@ 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)
} }
} }
} }

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"> <div class="top-tool-left" :style="{width: showInputWidth ? showInputWidth : 'auto'}">
<slot name="top-tool-left"></slot> <slot name="top-tool-left"></slot>
<div v-if="showLayout.indexOf('search') > -1" class="top-tool-search margin-r-20"> <div v-if="showLayout.indexOf('search') > -1" :class="showInputWidth ? 'top-tool-search1' : '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,6 +72,10 @@ export default {
layout: { layout: {
type: Array, type: Array,
default () { return [] } default () { return [] }
},
inputWidth: {
type: String,
default () { return '' }
} }
}, },
data () { data () {
@@ -82,6 +86,7 @@ export default {
showCustomTableTitle: false // 自定义列弹框是否显示 showCustomTableTitle: false // 自定义列弹框是否显示
}, },
showLayout: [], showLayout: [],
showInputWidth: '',
loading: true loading: true
} }
}, },
@@ -103,6 +108,13 @@ export default {
handler (n) { handler (n) {
this.showLayout = [...n] this.showLayout = [...n]
} }
},
inputWidth: {
immediate: true,
deep: true,
handler (n) {
this.showInputWidth = n
}
} }
} }
} }

View File

@@ -0,0 +1,124 @@
<template>
<el-table
id="userTable"
ref="dataTable"
:data="tableData"
tooltip-effect="light"
empty-text=" "
@header-dragend="dragend"
@sort-change="tableDataSort"
@selection-change="selectionChange"
>
<el-table-column
:resizable="false"
align="center"
type="selection"
:selectable="checkSelectable"
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 === 'createdTime' || item.prop === 'updateTime'">
<template v-if="scope.row[item.prop]">
{{ dateFormatByAppearance(scope.row[item.prop]) || '-' }}
</template>
<template v-else><span>-</span></template>
</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>
</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: 'source',
show: true,
sortable: 'custom',
minWidth: 200
},
{
label: this.$t('setting.entityTypes'),
prop: 'entityTypes',
show: true,
minWidth: 200
},
{
label: this.$t('setting.relationTypes'),
prop: 'relationTypes',
show: true,
minWidth: 200
},
{
label: this.$t('config.user.createTime'),
prop: 'createTime',
show: true,
minWidth: 200
},
{
label: this.$t('overall.updateTime'),
prop: 'updateTime',
show: true
}
]
}
},
computed: {
uploadParams () {
return {
indicatorType: 'IP'
}
}
},
methods: {
dateFormatByAppearance,
// 禁止勾选buildIn为1的项即禁止修改、删除admin的账号
checkSelectable (row) {
return row.buildIn !== 1
}
}
}
</script>

View File

@@ -0,0 +1,308 @@
<template>
<el-table
id="userTable"
ref="dataTable"
:data="tableData"
tooltip-effect="light"
empty-text=" "
@header-dragend="dragend"
@sort-change="tableDataSort"
@selection-change="selectionChange"
>
<el-table-column
:resizable="false"
align="center"
type="selection"
:selectable="checkSelectable"
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 === 'createdTime'">
<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="dialogVisible=true"></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 { itemListHeight, 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'
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('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: 'createdTime',
show: true,
minWidth: 150
},
{
label: this.$t('overall.option'),
prop: 'option',
show: true
}
],
dialogVisible: false,
uploadUrl: api.setting.source.sourceUpload,
uploadHeaders: {
'Cn-Authorization': localStorage.getItem(storageKey.token)
},
fileList: [],
fileTypeLimit: '.csv',
language: localStorage.getItem(storageKey.language) || EN,
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 服务器的信息,请联系您的管理员。' }
]
}
}
},
computed: {
uploadParams () {
return {
indicatorType: 'IP'
}
}
},
methods: {
dateFormatByAppearance,
// 禁止勾选buildIn为1的项即禁止修改、删除admin的账号
checkSelectable (row) {
return row.buildIn !== 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 (response) {
if (response.code === 200) {
this.uploaded = true
// 上传成功后去掉upload和preview的错误提示
this.uploadErrorTip = ''
this.previewErrorTip = ''
this.importedType = this.editObject.indicatorType
const originalImportedData = _.cloneDeep(response.data.list)
this.importedDataNoData = originalImportedData.length === 0
if (originalImportedData.length > 0) {
originalImportedData.forEach(data => {
if (data.isValid === 1) {
data.msg = this.$t('overall.success')
} else if (data.isValid === 0) {
if (data.errorAttribute === 'entityType') {
data.msg = this.$t('validate.wrongType')
} else if (data.errorAttribute === 'entityValue') {
data.msg = this.$t('validate.wrongFormat')
}
}
})
}
this.originalImportInfo = {
total: originalImportedData.length,
succeeded: originalImportedData.filter(d => d.isValid === 1).length,
failed: originalImportedData.filter(d => d.isValid !== 1).length
}
this.isLoad = false
originalImportedData.sort((a, b) => b.isValid - a.isValid)
this.importedData = this.handleSpeticalTypeData(originalImportedData)
this.addItemList = _.cloneDeep(this.importedData).filter(item => { return item.isValid === 1 })
this.updateItemList = []
this.deleteItemIds = this.oldItemIds
this.handleShowImportedData()
this.addEditFlag = false
this.editTagErrorTip = ''
this.editIndex = -1
this.isPreviewChange = true
this.stepHeights[2] = itemListHeight.hasData
this.stepHeightConstant.third = itemListHeight.hasData
} else {
this.uploadLoading = false
this.$message.error(this.$t('tip.uploadFailed', { msg: response.message }))
}
},
beforeUpload (file) {
return new Promise((resolve, reject) => {
// 判断后缀,仅支持.csv
if (!_.endsWith(file.name, '.csv')) {
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 }))
},
downloadTemplate () {
window.open('/assets/tagTemplate.csv', '_blank')
}
},
setup () {
// 没上传过文件的提示
const uploadErrorTip = ref('')
return {
uploadErrorTip
}
}
}
</script>

View File

@@ -197,6 +197,16 @@ export function handleComponent (code) {
case 'locationMap': case 'locationMap':
case 'traceTracking': case 'traceTracking':
return () => import('@/views/location/Index') return () => import('@/views/location/Index')
case 'source':
return () => import('@/views/setting/sources/Sources')
case 'createSource':
case 'editSource':
return () => import('@/views/setting/sources/SourcesForm')
case 'entitySetting':
return () => import('@/views/setting/entitySetting/EntitySetting')
case 'createEntitySetting':
case 'editEntitySetting':
return () => import('@/views/setting/entitySetting/EntitySettingForm')
default: default:
return null return null
} }

View File

@@ -386,6 +386,15 @@ 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/profiles' // get列表查询delete删除post新增put修改
}
} }
} }

View File

@@ -0,0 +1,255 @@
<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="editTag">
<i class="cn-icon-edit cn-icon"></i>
<span>{{ $t('overall.edit') }}</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 { profilesList } from '@/utils/static-data'
import axios from '_axios@0.21.4@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) {
this.name = params.q
params = { name: this.name }
this.pageObj.pageNo = 1
this.getTableData(params)
this.$refs.dataTable.expandedIds = []
},
add () {
this.$router.push({
path: '/setting/entitySetting/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) {
// this.tableData = profilesList
// this.pageObj.total = profilesList.length
// this.isNoData = !this.tableData || this.tableData.length === 0
// this.toggleLoading(false)
console.log('params', this.searchLabel)
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 + '?id=' + ids).then(response => {
if (response.status === 200) {
this.delFlag = true
this.$message({
duration: 2000,
type: 'success',
message: this.$t('tip.deleteSuccess')
})
let params = null
if (this.intent) {
params = { intent: this.intent }
}
if (this.category) {
params = {
...params,
category: this.category
}
}
if (this.name) {
params = {
...params,
name: this.name
}
}
this.getTableData(params)
} 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(() => {})
}
},
editTag () {
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)
}
},
jumpToEditPage (id) {
const pageNo = this.$router.currentRoute.value.query.pageNo
this.$router.push({
path: '/setting/entitySetting/edit',
query: {
t: +new Date(),
pageNoForTable: pageNo || 1,
id: id
}
})
}
}
}
</script>

View File

@@ -0,0 +1,612 @@
<template>
<div class="es-form">
<loading :loading="myLoading"></loading>
<div class="es-form-header">
<!-- {{ ruleId ? $t('detection.editEventPolicies') : $t('overall.entitySetting') }}-->
{{ $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[0]==='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="source_id" class="form-setting__block margin-b-20">
<el-select v-model="editObj.source_id" class="form-setting__select" placeholder=" ">
<el-option
v-for="item in sourceOption"
:key="item.source_id"
:label="item.label"
:value="item.source_id"
/>
</el-select>
</el-form-item>
</el-form>
<div class="form-content__title">{{ $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>{{ item.name }}</div>
<div>{{ $t('setting.source') }}</div>
</div>
<div class="block-body" v-for="(ite, ind) in item.list" :key="index+'-'+ind">
<el-form-item :prop="`data.${index}.list.${ind}.field`" :rules="mappingRules.type">
<el-select v-model="ite.field" class="block-body__select" placeholder="">
<el-option
v-for="obj in mappingFieldOption"
:key="obj.value"
:label="obj.label"
:value="obj.value"
/>
</el-select>
</el-form-item>
<div class="block-body-equal">=</div>
<el-form-item :prop="`data.${index}.list.${ind}.source`" :rules="mappingRules.source">
<el-select v-model="ite.source" class="block-body__select" placeholder="">
<el-option
v-for="obj in mappingSourceOption"
:key="obj.value"
:label="obj.label"
:value="obj.value"
/>
</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" v-if="isCloseMappingItem" @click="deleteMappingItem(index, ind)"></i>
</div>
</div>
</el-form>
<div class="form-setting-type" v-if="showMappingType">
<div class="setting-type__text">{{ $t('setting.selectMappingType') }}</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">
<!--新增按钮-->
<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[0]==='2' ? '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-body" 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"
:disabled="item.fromDisabled"
:class="item.fromDisabled ? 'relation-field__select-disabled' : 'relation-field__select'"
placeholder=""
@visible-change="visibleFromEntity(index)"
@change="changeFromEntity(index)">
<el-option
v-for="obj in editObj.schemaMappingData.data"
:key="obj.index"
:label="obj.name"
:value="obj.index"
/>
</el-select>
</el-form-item>
<div class="block-body-equal">=</div>
<el-form-item :prop="`data.${index}.to_entity_index`" :rules="relationRules">
<el-select
v-model="item.to_entity_index"
:disabled="item.toDisabled"
:class="item.toDisabled ? 'relation-field__select-disabled' : 'relation-field__select'"
placeholder=""
@change="changeToEntity(index)">
<el-option
v-for="obj in editObj.schemaMappingData.data"
:key="obj.index"
:label="obj.name"
:value="obj.index"
/>
</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">{{ $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" @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'
export default {
name: 'EntitySettingForm',
data () {
return {
activeNames: ['1'],
intervalList: [],
sourceRules: {
source_id: [{ required: true, message: this.$t('validate.required'), trigger: 'change' }]
},
mappingRules: {
type: [{ required: true, message: this.$t('validate.required'), trigger: 'change' }],
source: [{ required: true, message: this.$t('validate.required'), trigger: 'change' }]
},
relationRules: { required: true, message: this.$t('validate.required'), trigger: 'change' },
sourceOption: [],
mappingFieldOption: [
{ value: 'IP_address', label: 'IP_address' },
{ value: 'Port', label: 'Port' },
{ value: 'ASN', label: 'ASN' }
],
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' }
],
isCloseMappingItem: false, // 删除mapping标识
showMappingType: false,
mappingItemType: '', // 添加mapping映射类型
typeList: [
{ value: 'ip', label: 'ip' },
{ value: 'domain', label: 'domain' },
{ value: 'app', label: 'app' },
{ value: 'subscriber_id', label: 'subscriber' },
{ value: 'cell_id', 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' }
]
}
},
components: {
Loading
},
mounted () {
this.initSourceData()
if (this.ruleId) {
this.getDetailInfo()
}
},
setup () {
const { query } = 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: '',
source_id: '',
entities: [],
schemaMappingData: {
data: [
{
index: 1,
type: 'ip',
name: 'IP',
list: [
{ field: '', source: '' }
],
mapping: {}
}
]
},
relations: [],
relationData: {
data: [{ from_entity_index: '', to_entity_index: '', type: '', fromDisabled: false, toDisabled: false }]
},
enable: 1
})
return {
ruleId,
myLoading,
pageNoForTable,
editObj,
ruleObj
}
},
methods: {
switchStatus,
initSourceData () {
this.sourceOption = [
{ source_id: '1', value: 'ip_metric1', label: 'IP metric1' },
{ source_id: '2', value: 'ip_metric2', label: 'IP metric2' },
{ source_id: '3', value: 'ip_metric3', label: 'IP metric3' },
{ source_id: '4', value: 'ip_metric4', label: 'IP metric4' }
]
},
/** 编辑时获取详情 */
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 = {
...response.data.data,
ruleId: this.ruleId
}
this.ruleObj = this.$_.cloneDeep(this.editObj.ruleConfigObj)
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/entitySetting',
query: {
pageNo: this.pageNoForTable ? Number(this.pageNoForTable) : 1,
t: +new Date()
}
})
})
},
onContinue () {
this.$refs.mappingForm.validate(valid => {
if (valid) {
// this.editObj.schemaMappingData.data[index].list.push({ field: '', source: '' })
// this.isCloseMappingItem = true
}
})
},
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: [{ field: '', source: '' }],
mapping: {}
})
this.isCloseMappingItem = true
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: '', fromDisabled: false, toDisabled: false })
}
})
},
/** 添加schema mapping某一类型下的字段 **/
async addMappingListItem (index, ind) {
const valid1 = await this.$refs.mappingForm.validateField(`data.${index}.list.${ind}.field`, (valid) => {
return valid
})
const valid2 = await this.$refs.mappingForm.validateField(`data.${index}.list.${ind}.source`, (valid) => {
return valid
})
if (valid1 && valid2) {
this.editObj.schemaMappingData.data[index].list.push({ field: '', source: '' })
this.isCloseMappingItem = true
}
},
/** 删除mapping下的子项 **/
deleteMappingItem (index, ind) {
const type = this.editObj.schemaMappingData.data[index].type
if (this.editObj.schemaMappingData.data[index].list.length > 1) {
this.editObj.schemaMappingData.data[index].list.splice(ind, 1)
} else {
this.editObj.schemaMappingData.data.splice(index, 1)
}
this.editObj.schemaMappingData.data.forEach((item, index) => {
item.index = index + 1
})
// 删除同类型的name需要重新排序
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)
})
}
}
if (this.editObj.schemaMappingData.data.length > 1) {
this.isCloseMappingItem = true
} else {
this.isCloseMappingItem = this.editObj.schemaMappingData.data[0].list.length > 1
}
console.log('shuzu', this.editObj.schemaMappingData.data)
},
/** 删除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
},
visibleFromEntity (index) {
console.log('visible---from', this.editObj.relationData.data, index)
},
changeFromEntity (index) {
console.log('from---data', this.editObj.relationData.data)
this.editObj.relationData.data[index].fromDisabled = true
},
changeToEntity (index) {
console.log('to---data', this.editObj.relationData.data)
this.editObj.relationData.data[index].toDisabled = true
},
handleToEntityData (index) {
console.log('index', index)
if (index && index >= 0) {
const obj = this.editObj.relationData.data[index]
console.log('obj', obj)
}
return this.editObj.schemaMappingData.data
},
/** 获取Rule Definition折叠板form数据 */
getRuleObj (data) {
if (data.dataSource && data.knowledgeId && data.level) {
data.editFlag = false
data.saveFlag = true
this.handleActiveNames('2', this.activeNames, data.ruleNoContinue)
}
this.ruleObj = JSON.parse(JSON.stringify(data))
},
/** 自动展开收起折叠板 */
handleActiveNames (name, arr, flag) {
if (!flag) {
const list = arr
list.splice(list.indexOf(name), 1)
if (name === '1' && list.indexOf('2') < 0) {
list.push('2')
}
if (name === '2' && list.indexOf('3') < 0) {
list.push('3')
}
this.activeNames = []
list.forEach(t => {
this.activeNames.push(t)
})
}
},
/** 创建entity */
async saveEntity () {
const valid1 = await this.$refs.sourceForm.validate((valid) => {
return valid
})
const valid2 = await this.$refs.mappingForm.validate((valid) => {
return valid
})
const valid3 = await this.$refs.relationForm.validate((valid) => {
return valid
})
console.log('obj', this.editObj)
const formObj = this.$_.cloneDeep(this.editObj)
formObj.schemaMappingData.data.forEach((item) => {
const obj = {}
item.list.forEach(ite => {
obj[ite.field] = ite.source
})
item.mapping = this.$_.cloneDeep(obj)
})
console.log('formObj', formObj)
if (valid1 && valid2 && valid3) {
this.myLoading = true
if (!this.ruleId) {
const formObj = this.$_.cloneDeep(this.editObj)
formObj.schemaMappingData.data.forEach((item) => {
const obj = {}
item.list.forEach(ite => {
obj[ite.field] = ite.source
})
item.mapping = this.$_.cloneDeep(obj)
})
// 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/entitySetting',
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/entitySetting',
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/entitySetting',
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/entitySetting',
query: queryInfo
})
}).catch(() => {
})
}
}
}
</script>

View File

@@ -0,0 +1,252 @@
<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="editTag">
<i class="cn-icon-edit cn-icon"></i>
<span>{{ $t('overall.edit') }}</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="delItem"
@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) {
this.name = params.q
params = { name: this.name }
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')
})
let params = null
if (this.intent) {
params = { intent: this.intent }
}
if (this.category) {
params = {
...params,
category: this.category
}
}
if (this.name) {
params = {
...params,
name: this.name
}
}
this.getTableData(params)
} 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/source/create',
query: {
t: +new Date()
}
})
},
edit (u) {
this.object = u
this.rightBox.show = true
},
editTag () {
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)
}
},
jumpToEditPage (id) {
const pageNo = this.$router.currentRoute.value.query.pageNo
this.$router.push({
path: '/setting/source/edit',
query: {
t: +new Date(),
pageNoForTable: pageNo || 1,
id: id
}
})
}
}
}
</script>

View File

@@ -0,0 +1,479 @@
<template>
<div class="sources-form">
<loading :loading="myLoading"></loading>
<div class="sources-form__header">{{ sourceObj.id ? $t('sources.editSource') : $t('sources.createSource') }}</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="">
<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">{{ $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" v-if="sourceObj.fieldsData.data.length > 1" @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" v-if="sourceObj.lookupsData.data.length > 1" @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">{{ $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="blockOperation.save" 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 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' }
],
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: ''
}
}
},
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)
} else {
console.error(response.data)
}
}).catch(e => {
console.error(e)
this.$message.error(this.errorMsgHandler(e))
this.$router.push({
path: '/setting/source',
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
})
const valid3 = await this.$refs.lookupsForm.validate((valid) => {
return valid
})
if (valid1 && valid2 && valid3) {
this.myLoading = true
this.sourceObj.fields = JSON.stringify(this.sourceObj.fieldsData.data)
this.sourceObj.lookups = JSON.stringify(this.sourceObj.lookupsData.data)
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/source',
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/source',
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/source',
query: {
pageNo: self.pageNoForTable ? Number(self.pageNoForTable) : 1,
t: +new Date()
}
})
}).catch(() => {})
} else {
this.$router.push({
path: '/setting/source',
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) {
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')
}
},
setup () {
const { query } = 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: [{ function: '', lookup_field: '', output_type: '', output_field: '' }]
},
description: '',
enable: 1
}
const sourceObj = ref(_.cloneDeep(blankObject))
return {
sourceId,
pageNoForTable,
myLoading,
sourceObj
}
}
}
</script>