CN-1479 fix: 搜索组件补全模糊搜索与tag模式关于枚举in操作的处理

This commit is contained in:
刘洪洪
2023-12-12 10:06:21 +08:00
parent e1a26b60ae
commit 3d5c69d87b
7 changed files with 202 additions and 47 deletions

View File

@@ -40,7 +40,8 @@
<div v-if="meta.operator.value === 'has'" class="condition__operator" style="color: #000C18">({{meta.column.label}},</div> <div v-if="meta.operator.value === 'has'" class="condition__operator" style="color: #000C18">({{meta.column.label}},</div>
<!-- --> <!-- -->
<div class="condition__value"> <div class="condition__value">
<div v-if="meta.value.isEditing && meta.doc"> <!--点击=操作符时单个选择枚举值-->
<div v-if="meta.value.isEditing && meta.doc && meta.column.type!==columnType.array">
<el-select <el-select
allow-create allow-create
filterable filterable
@@ -48,6 +49,7 @@
v-model="meta.value.value" v-model="meta.value.value"
ref="columnValue" ref="columnValue"
:placeholder="meta.value.value || ' '" :placeholder="meta.value.value || ' '"
@blur="valueBlur1(meta, index)"
@change="(value) => selectValue(value, meta)" @change="(value) => selectValue(value, meta)"
> >
<el-option <el-option
@@ -58,6 +60,37 @@
></el-option> ></el-option>
</el-select> </el-select>
</div> </div>
<!--点击 in 操作符时多个选择枚举值-->
<div v-if="meta.value.isEditing && meta.doc && meta.column.type===columnType.array">
<el-select
v-model="myCheckboxList"
ref="valuesSelect"
multiple
size="mini"
collapse-tags
collapse-tags-tooltip
:placeholder="meta.value.value || ' '"
@blur="valuesBlur(meta, index)"
@focus="valuesFocus(meta)"
@change="(value) => selectValues(value, meta)"
@visible-change="(value) => selectVisibleValues(value, meta)"
popper-class="my-select-class"
>
<template #label>
{{ meta.value.value }}
</template>
<template #default>
<el-option
v-for="item in meta.doc.data"
:key="item.code"
:label="item.code"
:value="item.code"
/>
</template>
</el-select>
</div>
<div v-if="meta.value.isEditing && !meta.doc"> <div v-if="meta.value.isEditing && !meta.doc">
<!--避免blur事件和keyup.enter重复执行--> <!--避免blur事件和keyup.enter重复执行-->
<el-input <el-input
@@ -151,6 +184,8 @@ export default {
condition, condition,
connection, connection,
metaList: [], metaList: [],
columnType,
myCheckboxList: [],
operatorList: [] // 操作符列表根据所选columnList的label来确定一般为=,INtags操作符为has operatorList: [] // 操作符列表根据所选columnList的label来确定一般为=,INtags操作符为has
} }
}, },
@@ -250,15 +285,29 @@ export default {
meta.value.isEditing = false meta.value.isEditing = false
}, 100) }, 100)
}, },
selectVisibleValues (value, meta) {
if (!value) {
meta.value.isEditing = false
this.myCheckboxList = []
}
},
selectValues (value, meta) {
if (value.length > 0) {
let str = ''
value.forEach(item => {
str += `'${item}',`
})
str = str.substring(0, str.length - 1)
str = `(${str})`
meta.value.value = str
meta.value.label = str
} else {
meta.value.value = ''
}
},
selectConnection (value, meta) { selectConnection (value, meta) {
meta.isEditing = false meta.isEditing = false
}, },
columnClick (meta) {
meta.column.isEditing = true
this.$nextTick(() => {
this.$refs.columnSelect[this.$refs.columnSelect.length - 1].focus()
})
},
columnBlur (meta, index) { columnBlur (meta, index) {
setTimeout(() => { setTimeout(() => {
const parser = new Parser(this.columnList) const parser = new Parser(this.columnList)
@@ -276,27 +325,60 @@ export default {
this.operatorList = obj ? obj.doc.constraints.operator_functions.split(',') : ['=', 'IN'] this.operatorList = obj ? obj.doc.constraints.operator_functions.split(',') : ['=', 'IN']
if (meta.column && meta.column.type === 'fullText') { if (meta.column && meta.column.type === 'fullText') {
meta.operator.value = '=' meta.operator.value = '='
meta.column.show = false meta.column.show = true
meta.column.isFullText = true meta.column.isFullText = true
meta.operator.show = false meta.operator.show = true
const label = JSON.parse(JSON.stringify(meta.column.label)) const label = JSON.parse(JSON.stringify(meta.column.label))
meta.column.label = parser.getEntityTypeByValue(meta.column.label) meta.column.label = parser.getEntityTypeByValue(meta.column.label)
meta.value.value = label meta.value.value = label
meta.value.label = label meta.value.label = label
// if (meta.column.label === 'domain') { if (meta.column.label === 'domain') {
// meta.operator.value = 'like' meta.operator.value = 'like'
// meta.value.value = `%${this.delSingleQuote(label)}` meta.value.value = `%${this.delSingleQuote(label)}`
// meta.value.label = `${this.delSingleQuote(label)}` meta.value.label = `%${this.delSingleQuote(label)}`
// } else if (meta.column.label === 'app') { } else if (meta.column.label === 'app') {
// meta.operator.value = 'like' meta.operator.value = 'like'
// meta.value.value = `%${this.delSingleQuote(label)}%` meta.value.value = `%${this.delSingleQuote(label)}%`
// meta.value.label = `${this.delSingleQuote(label)}` meta.value.label = `%${this.delSingleQuote(label)}%`
// } }
meta.column.type = 'string' meta.column.type = 'string'
} }
}, 200) }, 200)
}, },
valueBlur1 (meta) {
setTimeout(() => {
meta.value.isEditing = false
}, 200)
},
valuesBlur (meta) {
this.$nextTick(() => {
})
},
valuesFocus (meta) {
this.$nextTick(() => {
meta.value.isEditing = true
setTimeout(() => {
if (meta.value.value && this.myCheckboxList.length === 0) {
let valueArr = []
if (!_.isArray(meta.value.value)) {
let value = meta.value.value
if (value.indexOf('(') === 0 && value.indexOf(')') === value.length - 1) {
value = value.substring(1, value.length)
value = value.substring(0, value.length - 1)
}
valueArr = value.split(',')
valueArr.forEach((item, index) => {
if (item[0] === "'" && item[item.length - 1] === "'") {
item = item.substring(1, item.length - 1)
this.myCheckboxList.push(item)
}
})
}
}
}, 100)
})
},
connectionClick (meta) { connectionClick (meta) {
meta.isEditing = true meta.isEditing = true
}, },
@@ -314,7 +396,7 @@ export default {
// 若是in或not incolumn的type要改成array否则是string // 若是in或not incolumn的type要改成array否则是string
if (operator.toLowerCase().indexOf('in') > -1) { if (operator.toLowerCase().indexOf('in') > -1) {
meta.column.type = columnType.array meta.column.type = columnType.array
meta.value.value = [] // meta.value.value = []
} else if (['>', '<', '>=', '<='].indexOf(operator) > -1) { } else if (['>', '<', '>=', '<='].indexOf(operator) > -1) {
meta.column.type = columnType.number meta.column.type = columnType.number
} else { } else {
@@ -480,10 +562,26 @@ export default {
} }
meta.value.isEditing = !meta.isCompleteCondition() meta.value.isEditing = !meta.isCompleteCondition()
}, },
columnClick (meta) {
meta.column.isEditing = true
this.$nextTick(() => {
this.$refs.columnSelect[this.$refs.columnSelect.length - 1].focus()
})
},
valueClick (meta) { valueClick (meta) {
meta.value.isEditing = true meta.value.isEditing = true
const obj = enumerateData.find(d => d.name === meta.column.label)
if (obj) {
meta.doc = obj
}
this.$nextTick(() => { this.$nextTick(() => {
if (this.$refs.valueInput) {
this.$refs.valueInput[0].focus() this.$refs.valueInput[0].focus()
}
if (this.$refs.valuesSelect) {
// 触发focus后select弹窗并没有生效
this.$refs.valuesSelect[0].focus(meta)
}
}) })
}, },
// 判断是否是用户自己添加的内容,用于判断是否是全局搜索 // 判断是否是用户自己添加的内容,用于判断是否是全局搜索
@@ -515,7 +613,8 @@ export default {
const str = strObj.str ? strObj.str : strObj const str = strObj.str ? strObj.str : strObj
const str2 = strObj.str2 ? strObj.str2 : strObj const str2 = strObj.str2 ? strObj.str2 : strObj
// str为将metaList转成字符串的值str2为地址栏展示的值 // str为将metaList转成字符串的值str2为地址栏展示的值
const key = parser.handleEntityTypeByStr(str) let key = parser.handleEntityTypeByStr(str)
key = parser.conversionEnum(key)
this.$emit('search', { ...parser.parseStr(key), str: str2, keywordList: keywordList }) this.$emit('search', { ...parser.parseStr(key), str: str2, keywordList: keywordList })
} else { } else {
this.$message.error(handleErrorTip(errorList[0])) this.$message.error(handleErrorTip(errorList[0]))

View File

@@ -178,9 +178,13 @@ export default {
} }
}) })
if (keyInfo.isKey) { if (keyInfo.isKey) {
const errorList = parser.validateStr(keyInfo.key) // 检查是否包含枚举字段,包含的话进行替换
const enumKey = parser.conversionEnum(keyInfo.key)
const errorList = parser.validateStr(newKey)
if (_.isEmpty(errorList)) { if (_.isEmpty(errorList)) {
this.$emit('search', { ...parser.parseStr(keyInfo.key), str: str, keywordList: keywordList }) // 补全模糊搜索
toRaw(this.codeMirror).setValue(parser.handleEntityTypeByStr(str))
this.$emit('search', { ...parser.parseStr(enumKey), str: parser.handleEntityTypeByStr(str), keywordList: keywordList })
} else { } else {
this.$message.error(handleErrorTip(errorList[0])) this.$message.error(handleErrorTip(errorList[0]))
} }

View File

@@ -12,6 +12,7 @@ const strReg = {
value: /^[\da-zA-Z\u4E00-\u9FA5\u3040-\u309F\u0800-\u4e00\u0400-\u04FF\u2000-\u206F\s.'-_%]$/ value: /^[\da-zA-Z\u4E00-\u9FA5\u3040-\u309F\u0800-\u4e00\u0400-\u04FF\u2000-\u206F\s.'-_%]$/
} }
const operatorList = ['=', ' in ', ' IN ', ' like ', ' LIKE ', 'HAS(', 'has('] const operatorList = ['=', ' in ', ' IN ', ' like ', ' LIKE ', 'HAS(', 'has(']
const enumList = ['status', 'eventType', 'severity']
export default class Parser { export default class Parser {
constructor (columnList) { constructor (columnList) {
@@ -1476,6 +1477,47 @@ export default class Parser {
return this.columnList.find(t => t.label.toLowerCase() === label.toLowerCase()) return this.columnList.find(t => t.label.toLowerCase() === label.toLowerCase())
} }
/**
* 检测str是否包含枚举字段包含的话进行替换
* @param str
* @returns {string|*}
*/
conversionEnum (str) {
if (str) {
let enumFlag = false // 判断字符串是否包含枚举类型的key不包含则直接返回
enumList.forEach(item => {
if (str.toLocaleLowerCase().indexOf(item.toLocaleLowerCase()) > -1) {
enumFlag = true
}
})
if (enumFlag) {
let key = _.cloneDeep(str)
let searchList = [] // 将字符串按AND分割成单独的搜索条件
if (key.indexOf(' AND ') > -1) {
searchList = key.split(' AND ')
} else {
searchList = [key]
}
searchList.forEach((item, index) => {
const obj = this.columnList.find(d => item.indexOf(d.label) > -1)
if (obj && obj.doc.data) {
obj.doc.data.forEach(item1 => {
if (item.indexOf(item1.code) > -1) {
searchList[index] = searchList[index].replace(new RegExp(item1.code, 'g'), item1.value)
}
})
}
})
key = searchList.join(' AND ')
return key
} else {
return str
}
} else {
return str
}
}
} }
// 使用单引号包裹 // 使用单引号包裹

View File

@@ -57,17 +57,17 @@ export default {
filter.showIndex >= (filter.data.length - 1) && (filter.showDisabled = true) filter.showIndex >= (filter.data.length - 1) && (filter.showDisabled = true)
}, },
clickFilterItem (name, data, index) { clickFilterItem (name, data, index) {
if (index === 0) { // if (index === 0) {
let status = 0 // let status = 0
if (name === this.$t('detections.active')) { // if (name === this.$t('detections.active')) {
status = '0' // status = '0'
} else if (name === this.$t('detections.ended')) { // } else if (name === this.$t('detections.ended')) {
status = '1' // status = '1'
} // }
this.$emit('filter', status, data) // this.$emit('filter', status, data)
} else { // } else {
// }
this.$emit('filter', name, data) this.$emit('filter', name, data)
}
}, },
handleMouse (id) { handleMouse (id) {
const dom = document.getElementById(id) const dom = document.getElementById(id)

View File

@@ -181,7 +181,7 @@ export default {
} }
localStorage.setItem(storageKey.detectionSearchHistory, JSON.stringify(arr)) localStorage.setItem(storageKey.detectionSearchHistory, JSON.stringify(arr))
} }
this.$emit('search', { q, metaList }) this.$emit('search', { q, metaList, str })
}, },
changeParams (params) { changeParams (params) {
this.$refs.search.addParams(params) this.$refs.search.addParams(params)

View File

@@ -140,6 +140,8 @@ import ChartTabs from '@/components/common/ChartTabs'
import { useStore } from 'vuex' import { useStore } from 'vuex'
import { tooLongFormatter } from '@/views/charts/charts/tools' import { tooLongFormatter } from '@/views/charts/charts/tools'
import { format } from 'echarts' import { format } from 'echarts'
import Parser from '@/components/advancedSearch/meta/parser'
import { schemaDetectionSecurity } from '@/utils/static-data'
export default { export default {
name: 'Index', name: 'Index',
@@ -763,11 +765,11 @@ export default {
// 参数q避免切换页码时地址栏参数q为空 // 参数q避免切换页码时地址栏参数q为空
let urlQ = '' let urlQ = ''
if (param && param.str) { if (param && param.str) {
// urlQ = encodeURI(param.str) urlQ = param.str.indexOf('%') > -1 ? encodeURI(param.str) : param.str
urlQ = param.str this.str = param.str
} else if (this.q) { } else if (this.q) {
// urlQ = encodeURI(this.q) // urlQ = encodeURI(this.q)
urlQ = this.q urlQ = this.q.indexOf('%') > -1 ? encodeURI(this.q) : this.q
} }
const mode = this.$route.query.mode || 'text' const mode = this.$route.query.mode || 'text'
const newUrl = urlParamsHandler(window.location.href, this.$route.query, { const newUrl = urlParamsHandler(window.location.href, this.$route.query, {
@@ -778,8 +780,8 @@ export default {
mode: mode mode: mode
}) })
overwriteUrl(newUrl) overwriteUrl(newUrl)
this.queryFilter(urlQ) this.queryFilter(this.q)
this.queryList(urlQ) this.queryList(this.q)
}, },
resetFilterData () { resetFilterData () {
this.filterData.securityEvent.forEach(d => { this.filterData.securityEvent.forEach(d => {
@@ -921,10 +923,12 @@ export default {
} }
// %位置不为0即内容包含非英文时 // %位置不为0即内容包含非英文时
const str1 = q.substring(q.indexOf('%'), q.indexOf('%') + 3) const str1 = q.substring(q.indexOf('%'), q.indexOf('%') + 3)
if (q && q.indexOf('%') > 0 && (str1 !== '%20' || str1 === '%25')) { if (q && q.indexOf('%') > 0 && (str1 === '%20' || str1 === '%25')) {
q = decodeURI(q) q = decodeURI(q)
} }
} }
const parser = new Parser(schemaDetectionSecurity)
q = parser.conversionEnum(q)
this.queryFilter(q) this.queryFilter(q)
if (this.initFlag) { if (this.initFlag) {
this.timer = setTimeout(() => { this.timer = setTimeout(() => {

View File

@@ -282,6 +282,7 @@ export default {
], ],
listData: [], listData: [],
q: '', q: '',
str: '',
metaList: [], metaList: [],
listLoading: false, listLoading: false,
// 实体详情搜索页面 底部列表 // 实体详情搜索页面 底部列表
@@ -387,9 +388,12 @@ export default {
// 参数q避免切换页码时地址栏参数q为空 // 参数q避免切换页码时地址栏参数q为空
let urlQ = '' let urlQ = ''
if (param.str) { if (param.str) {
urlQ = encodeURI(param.str) // urlQ = encodeURI(param.str)
urlQ = param.str.indexOf('%') > -1 ? encodeURI(param.str) : param.str
this.str = param.str
} else if (this.q) { } else if (this.q) {
urlQ = encodeURI(this.q) // urlQ = encodeURI(this.q)
urlQ = this.q.indexOf('%') > -1 ? encodeURI(this.q) : this.q
} else if (!this.q) { } else if (!this.q) {
this.isHideRelatedEntities = false this.isHideRelatedEntities = false
} }
@@ -444,10 +448,10 @@ export default {
const keywordList = this.getKeywordListByMetaList(this.metaList) const keywordList = this.getKeywordListByMetaList(this.metaList)
if (this.initFlag) { if (this.initFlag) {
if (val !== 20) { if (val !== 20) {
this.search({ metaList: this.metaList, q: this.q, keywordList: keywordList }) this.search({ metaList: this.metaList, q: this.q, str: this.str, keywordList: keywordList })
} }
} else { } else {
this.search({ metaList: this.metaList, q: this.q, keywordList: keywordList }) this.search({ metaList: this.metaList, q: this.q, str: this.str, keywordList: keywordList })
} }
}, },
pageNo (val) { pageNo (val) {
@@ -455,7 +459,7 @@ export default {
this.pageObj.pageNo = val this.pageObj.pageNo = val
this.pageObj.resetPageNo = false this.pageObj.resetPageNo = false
const keywordList = this.getKeywordListByMetaList(this.metaList) const keywordList = this.getKeywordListByMetaList(this.metaList)
this.search({ metaList: this.metaList, q: this.q, keywordList: keywordList }) this.search({ metaList: this.metaList, q: this.q, str: this.str, keywordList: keywordList })
} }
}, },
// 点击上一页箭头 // 点击上一页箭头
@@ -827,6 +831,8 @@ export default {
if (q && q.indexOf('%') > 0 && (str1 === '%20' || str1 === '%25')) { if (q && q.indexOf('%') > 0 && (str1 === '%20' || str1 === '%25')) {
q = decodeURI(q) q = decodeURI(q)
} }
const parser = new Parser(columnList)
q = parser.conversionEnum(q)
this.initSearch(q) this.initSearch(q)
this.listMode = listMode this.listMode = listMode
// 查询评分基准 // 查询评分基准
@@ -840,7 +846,7 @@ export default {
}, },
watch: { watch: {
timeFilter () { timeFilter () {
this.search({ metaList: this.metaList, q: this.q }) this.search({ metaList: this.metaList, q: this.q, str: this.str })
} }
}, },
setup () { setup () {