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

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