feat: 搜索框(部分内容)

This commit is contained in:
chenjinsong
2022-01-23 23:34:51 +08:00
parent 581896bcb8
commit ba341cde24
10 changed files with 1053 additions and 8 deletions

View File

@@ -0,0 +1,80 @@
<template>
<div
class="advanced-search"
>
<text-mode
v-if="searchMode === 'text'"
@changeMode="changeMode"
@search="search"
></text-mode>
<tag-mode
v-if="searchMode === 'tag'"
:column-list="columnList"
:operator-list="showOperatorList"
:connection-list="showConnectionList"
@changeMode="changeMode"
@search="search"
></tag-mode>
</div>
</template>
<script>
import TagMode from '@/components/advancedSearch/TagMode'
import TextMode from '@/components/advancedSearch/TextMode'
import { defaultOperatorList, defaultConnectionList } from '@/components/advancedSearch/meta/meta'
import _ from 'lodash'
import { ref } from 'vue'
export default {
name: 'Index',
components: {
TagMode,
TextMode
},
props: {
// 默认模式tag | text
defaultMode: String,
// 使用全文检索
fullText: {
type: Boolean,
default: true
},
// 条件字段列表
columnList: {
type: Array,
required: true
},
// 操作符列表
operatorList: Array,
// 连接符列表
connectionList: Array
},
methods: {
search () {
},
changeMode (mode) {
this.searchMode = mode
}
},
setup (props) {
// 默认为文本模式 TODO 改回text
let searchMode = ref('tag')
if (props.defaultMode) {
switch (props.defaultMode) {
case 'tag': {
searchMode = 'tag'
break
}
}
}
// 如果参数中的operatorList、connectionList为空使用默认值
const showOperatorList = _.isEmpty(props.operatorList) ? defaultOperatorList : [...props.operatorList]
const showConnectionList = _.isEmpty(props.connectionList) ? defaultConnectionList : [...props.connectionList]
return {
searchMode,
showOperatorList,
showConnectionList
}
}
}
</script>

View File

@@ -0,0 +1,226 @@
<template>
<div class="tag-search">
<div class="tag-search__meta"
v-for="(meta, index) in metaList"
:key="index"
:class="`tag-search__meta--${meta.meta}`"
>
<template v-if="meta.meta === condition">
<!-- 删除按钮 -->
<div class="condition__delete" @click="removeCondition(index)"><i class="el-icon-error"></i></div>
<!-- 字段选择 -->
<div class="condition__column">
<div v-show="meta.column.isEditing">
<el-select
allow-create
filterable
size="mini"
v-model="meta.column.name"
ref="columnSelect"
:placeholder="meta.column.name || ''"
@blur="columnBlur(meta, index)"
@change="(value) => selectColumn(value, meta)"
>
<el-option
v-for="(column, index) in columnList"
:key="index"
:label="column.label"
:value="column.name"
></el-option>
</el-select>
</div>
<span v-show="!meta.column.isEditing" @click="columnClick(meta)">{{meta.column.label}}</span>
</div>
<!-- 已选操作符显示 -->
<div class="condition__operator" @click="operatorClick(meta)" v-if="meta.operator.show">{{meta.operator.value}}</div>
<!-- -->
<div class="condition__value">
<div v-if="meta.value.isEditing">
<el-input v-model="meta.value.value" size="mini" @blur="valueBlur(meta)" ref="valueInput"></el-input>
</div>
<span v-else @click="valueClick(meta)">{{meta.value.label}}</span>
</div>
<!-- 操作符选择器 -->
<div class="condition__operation-select" v-if="meta.operator.show && meta.operator.isEditing">
<div
class="condition__operation"
v-for="(operator, index) in operatorList"
:key="index"
@click.stop="selectOperator(operator, meta)"
>{{operator}}</div>
</div>
</template>
<!-- 条件连接符 -->
<template v-else-if="meta.meta === connection">
<div class="connection__value">
<div v-show="meta.isEditing">
<el-select
:placeholder="meta.value"
v-model="meta.value"
size="mini"
@change="(value) => selectConnection(value, meta)"
>
<el-option
v-for="(connection, index) in connectionList"
:key="index"
:label="connection.label"
:value="connection.value"
></el-option>
</el-select>
</div>
<span v-show="!meta.isEditing" @click="connectionClick(meta)">{{meta.value}}</span>
</div>
</template>
</div>
<div class="tag-search__add" @click="addCondition">{{$t('entities.advancedSearch.add')}}</div>
<div class="search__suffixes search__suffixes--tag-mode">
<div class="search__suffix" @click="changeMode">
<i class="cn-icon cn-icon-search-advance"></i>
</div>
<div class="search__suffix" @click="search">
<i class="el-icon-search"></i>
</div>
</div>
</div>
</template>
<script>
import Meta, { connection, condition, columnType } from './meta/meta'
import _ from 'lodash'
export default {
name: 'TagMode',
props: {
columnList: Array,
operatorList: Array,
connectionList: Array
},
data () {
return {
condition,
connection,
metaList: []
}
},
methods: {
// 新增条件
addCondition () {
this.metaList.forEach(meta => {
meta.cancelEditing()
})
// 先判断上一个condition是否已填写完整没有则删除之后将当前所有meta的内容的isEditing置为false
if (this.metaList.length > 0) {
const prevMeta = this.metaList[this.metaList.length - 1]
if (!prevMeta.isCompleteCondition()) {
this.metaList.splice(this.metaList.length - (this.metaList.length === 1 ? 1 : 2), this.metaList.length === 1 ? 1 : 2)
}
}
// 不是首个condition时先增加一个connection再增加一个新condition
if (!_.isEmpty(this.metaList)) {
this.addConnection()
}
const newCondition = new Meta(condition)
newCondition.isEditing = true
newCondition.column.isEditing = true
this.metaList.push(newCondition)
this.$nextTick(() => {
this.$refs.columnSelect.focus()
})
},
addConnection () {
this.metaList.push(new Meta(connection))
},
removeCondition (index) {
// 删除首个条件时,连带删除之后的第一个连接符;否则删除之前的第一个连接符
if (index === 0) {
if (this.metaList.length > 2) {
this.metaList = this.metaList.slice(0, 2)
} else {
this.metaList = []
}
} else {
this.metaList.splice(index - 1, 2)
}
},
// 选择搜索条件的事件
selectColumn (value, meta) {
// 处理column。如果是用户自己新增的column视为全文搜索
if (this.isCustomized(value)) {
meta.column.type = columnType.fullText
meta.column.label = value
} else {
const selectedColumn = this.columnList.find(column => {
return column.name === value
})
meta.column.label = selectedColumn.label
meta.column.type = selectedColumn.type
}
setTimeout(() => {
meta.column.isEditing = false
// 处理操作符
if (!meta.operator.value) {
meta.operator.isEditing = true
meta.operator.show = true
}
}, 200)
},
selectConnection (value, meta) {
meta.isEditing = false
},
columnClick (meta) {
meta.column.isEditing = true
this.$nextTick(() => {
this.$refs.columnSelect.focus()
})
},
columnBlur (meta, index) {
setTimeout(() => {
meta.column.isEditing = false
if (meta.isEmpty()) {
this.metaList.splice(index, 1)
}
}, 200)
},
connectionClick (meta) {
meta.isEditing = true
},
// 选择操作符的事件
selectOperator (operator, meta) {
meta.operator.value = operator
meta.operator.isEditing = false
// 处理搜索值
meta.value.isEditing = true
meta.value.show = true
this.$nextTick(() => {
this.$refs.valueInput.focus()
})
},
operatorClick (meta) {
meta.operator.isEditing = true
},
valueBlur (meta) {
meta.value.label = meta.value.value // label是显示value是实际值目前的需求label和value是相等的
meta.value.isEditing = !meta.isCompleteCondition()
},
valueClick (meta) {
meta.value.isEditing = true
this.$nextTick(() => {
this.$refs.valueInput.focus()
})
},
// 判断是否是用户自己添加的内容,用于判断是否是全局搜索
isCustomized (value) {
return !this.columnList.some(meta => {
return meta.name === value
})
},
search () {
this.$emit('search', this.metaList)
},
changeMode () {
this.$emit('changeMode', 'text')
}
}
}
</script>

View File

@@ -0,0 +1,43 @@
<template>
<textarea
ref="textSearch"
></textarea>
<div class="search__suffixes search__suffixes--text-mode">
<div class="search__suffix" @click="changeMode">
<i class="cn-icon cn-icon-search-advance"></i>
</div>
<div class="search__suffix">
<i class="el-icon-search"></i>
</div>
</div>
</template>
<script>
import 'codemirror/theme/ambiance.css'
import 'codemirror/addon/hint/show-hint'
import 'codemirror/addon/hint/show-hint.css'
import 'codemirror/mode/sql/sql'
const CodeMirror = require('codemirror')
export default {
name: 'TextMode',
methods: {
initCodeMirror () {
CodeMirror.fromTextArea(this.$refs.textSearch, {
mode: {
name: 'sql'
},
lineNumbers: false
})
},
search () {
this.$emit('search', this.metaList)
},
changeMode () {
this.$emit('changeMode', 'tag')
}
},
mounted () {
this.initCodeMirror()
}
}
</script>

View File

@@ -0,0 +1,88 @@
import _ from 'lodash'
export const connection = 'connection'
export const condition = 'condition'
export const columnType = {
fullText: 'fullText',
string: 'string',
long: 'long'
}
export const defaultOperatorList = ['=', '!=', '>', '<', '>=', '<=', 'IN', 'NOT IN', 'LIKE', 'NOT LIKE']
export const defaultConnectionList = [
{
value: 'AND',
label: 'AND'
},
{
value: 'OR',
label: 'OR'
}
]
export default class Meta {
// meta元数据有两种一是condition表示字段、操作符、值的组合二是connection是condition之间的连接符AND | OR
constructor (type) {
switch (type) {
case connection: {
this.newConnection()
break
}
default: {
this.newCondition()
break
}
}
}
newConnection () {
this.meta = connection
this.value = 'AND'
this.isEditing = false
}
newCondition () {
this.meta = condition
this.column = {
name: '',
type: '', // fullText | string | long ...
label: '',
isEditing: false
}
this.operator = {
value: '',
isEditing: false,
show: false
}
this.value = {
value: '',
label: '',
isEditing: false,
show: false
}
}
isEmpty () {
if (this.meta === condition) {
return _.isEmpty(this.column.name)
} else {
return true
}
}
// 是否是完整的condition
isCompleteCondition () {
return (this.column.type === columnType.fullText)
? !_.isEmpty(this.column.name)
: !_.isEmpty(this.column.name) && !_.isEmpty(this.operator.value) && !_.isEmpty(this.value.value)
}
// 取消editing状态
cancelEditing () {
if (this.meta === condition) {
this.column.isEditing = false
this.operator.isEditing = false
this.value.isEditing = false
}
if (this.meta === connection) {
this.isEditing = false
}
}
}