480 lines
17 KiB
Vue
480 lines
17 KiB
Vue
<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>
|