feat: 搜索框(部分内容)
This commit is contained in:
80
src/components/advancedSearch/Index.vue
Normal file
80
src/components/advancedSearch/Index.vue
Normal 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>
|
||||
226
src/components/advancedSearch/TagMode.vue
Normal file
226
src/components/advancedSearch/TagMode.vue
Normal 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>
|
||||
43
src/components/advancedSearch/TextMode.vue
Normal file
43
src/components/advancedSearch/TextMode.vue
Normal 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>
|
||||
88
src/components/advancedSearch/meta/meta.js
Normal file
88
src/components/advancedSearch/meta/meta.js
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user