This repository has been archived on 2025-09-14. You can view files and clone it, but cannot push or open issues or pull requests.
Files
cyber-narrator-cn-ui/src/views/setting/sources/SourcesForm.vue
2024-11-15 18:45:58 +08:00

480 lines
17 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<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>