You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
462 lines
15 KiB
462 lines
15 KiB
<template>
|
|
<el-dialog
|
|
:visible.sync="visible"
|
|
title="高级检索"
|
|
width="1000px"
|
|
:close-on-click-modal="false"
|
|
:modal-append-to-body="false"
|
|
append-to-body
|
|
:before-close="handleClose"
|
|
>
|
|
<div class="advanced-search-modal">
|
|
<el-form ref="form" inline :model="form" :rules="rules" size="small" label-width="70px">
|
|
<el-form-item label="字段名" prop="field">
|
|
<el-select v-model="form.field" value-key="id" style="width: 200px;">
|
|
<el-option v-for="item in fieldOptions" :key="item.id" :label="item.label" :value="item" />
|
|
</el-select>
|
|
</el-form-item>
|
|
<el-form-item label="运算符" prop="symbol">
|
|
<el-select v-model="form.symbol" value-key="value" placeholder="请选择" style="width: 200px;">
|
|
<el-option
|
|
v-for="item in symbolOptions"
|
|
:key="item.value"
|
|
:label="item.label"
|
|
:value="item"
|
|
/>
|
|
</el-select>
|
|
</el-form-item>
|
|
<el-form-item label="检索值" prop="keyWord" :rules="getKeywordRules">
|
|
<el-input v-model="form.keyWord" :type="inputType" style="width: 200px;" />
|
|
</el-form-item>
|
|
</el-form>
|
|
<div class="advanced-btn">
|
|
<el-button size="mini" @click="addConditionData"><i class="iconfont icon-xinzeng" />新增</el-button>
|
|
<el-button class="filter-refresh" size="mini" icon="el-icon-refresh-left" @click="resetQuery">重置</el-button>
|
|
</div>
|
|
<div class="search-condition">
|
|
<h4> 检索条件</h4>
|
|
<div class="condition-main">
|
|
<div class="condition-left">
|
|
<el-button size="mini" :disabled="currentIndex===null" @click="deltCurrent(currentIndex)"><i class="iconfont icon-shanchu" />删除</el-button>
|
|
<el-button size="mini" icon="el-icon-top" :disabled="currentIndex === 0" @click="moveUp(currentIndex)">上移</el-button>
|
|
<el-button size="mini" icon="el-icon-bottom" :disabled="currentIndex === conditionData.length - 1" @click="moveDown(currentIndex)">下移</el-button>
|
|
</div>
|
|
<ul id="condition-container-modal" class="condition-content">
|
|
<li v-for="(item, index) in conditionData" :id="'modal-element-id-' + index" :key="index" :class="currentIndex===index ? 'active': ''" @click="selectCurrent(index)">
|
|
<span style="color:#0348F3">{{ item.field }}</span>
|
|
<span style="color:#ED4A41; margin:0 4px">{{ item.symbol }}</span>
|
|
<span v-if="item.symbol && (item.symbol === '包含'|| item.symbol === '不包含')" class="keyword-style"><i>'%</i>{{ item.keyWord }}<i>%'</i></span>
|
|
<span v-else-if="item.keyWord && isNaN(parseInt(item.keyWord))" class="keyword-style"><i>'</i>{{ item.keyWord }}<i>'</i></span>
|
|
<span v-else class="keyword-style">{{ item.keyWord }}</span>
|
|
<span>{{ item.connector }}</span>
|
|
<span>{{ item.bracket }}</span>
|
|
</li>
|
|
</ul>
|
|
<div class="condition-right">
|
|
<el-button v-for="(item,index) in connectorList" :key="index" type="primary" size="mini" @click="addConnector(item)">{{ item }}</el-button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div slot="footer" class="dialog-footer">
|
|
<el-button @click="handleClose">取消</el-button>
|
|
<el-button type="primary" @click="handleSearch">检索</el-button>
|
|
</div>
|
|
</el-dialog>
|
|
</template>
|
|
|
|
<script>
|
|
import { FetchInitCategoryInputFieldByPid } from '@/api/system/category/category'
|
|
|
|
export default {
|
|
name: 'AdvancedSearchModal',
|
|
props: {
|
|
visible: {
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
selectedCategory: {
|
|
type: Object,
|
|
default: () => ({})
|
|
},
|
|
collectLevel: {
|
|
type: Number,
|
|
default: 0
|
|
},
|
|
initialConditions: {
|
|
type: Array,
|
|
default: () => []
|
|
}
|
|
},
|
|
data() {
|
|
return {
|
|
form: {
|
|
field: null,
|
|
symbol: null,
|
|
keyWord: null
|
|
},
|
|
fieldOptions: [],
|
|
symbolOptions: [
|
|
{
|
|
label: '包含',
|
|
value: 'like'
|
|
},
|
|
{
|
|
label: '不包含',
|
|
value: 'not like'
|
|
},
|
|
{
|
|
label: '等于',
|
|
value: '='
|
|
},
|
|
{
|
|
label: '不等于',
|
|
value: '!='
|
|
},
|
|
{
|
|
label: '为空',
|
|
value: 'is null'
|
|
},
|
|
{
|
|
label: '不为空',
|
|
value: 'is not null'
|
|
},
|
|
{
|
|
label: '大于',
|
|
value: '>'
|
|
},
|
|
{
|
|
label: '大于等于',
|
|
value: '>='
|
|
},
|
|
{
|
|
label: '小于',
|
|
value: '<'
|
|
},
|
|
{
|
|
label: '小于等于',
|
|
value: '<='
|
|
}
|
|
],
|
|
rules: {
|
|
field: [
|
|
{ required: true, message: '请选择字段名', trigger: 'change' }
|
|
],
|
|
symbol: [
|
|
{ required: true, message: '请选择运算符', trigger: 'change' }
|
|
]
|
|
},
|
|
conditionData: [],
|
|
currentIndex: null,
|
|
connectorList: ['并且', '或者', '(', ')']
|
|
}
|
|
},
|
|
computed: {
|
|
getKeywordRules() {
|
|
if ((this.form.symbol && this.form.symbol.label === '为空') || (this.form.symbol && this.form.symbol.label === '不为空')) {
|
|
return []
|
|
} else {
|
|
return [{ required: true, message: '请输入检索值', trigger: 'blur' }]
|
|
}
|
|
},
|
|
inputType() {
|
|
if (
|
|
this.form.symbol &&
|
|
(this.form.symbol.label === '大于' ||
|
|
this.form.symbol.label === '大于等于' ||
|
|
this.form.symbol.label === '小于' ||
|
|
this.form.symbol.label === '小于等于')
|
|
) {
|
|
return 'number'
|
|
} else {
|
|
return 'text'
|
|
}
|
|
}
|
|
},
|
|
watch: {
|
|
visible(newVal) {
|
|
if (!newVal) {
|
|
this.resetQuery()
|
|
this.conditionData = []
|
|
this.currentIndex = null
|
|
} else if (this.initialConditions && this.initialConditions.length > 0) {
|
|
this.conditionData = JSON.parse(JSON.stringify(this.initialConditions))
|
|
}
|
|
}
|
|
},
|
|
mounted() {
|
|
this.getFieldCommon()
|
|
},
|
|
methods: {
|
|
resetQuery() {
|
|
if (this.$refs.form) {
|
|
this.$refs.form.resetFields()
|
|
}
|
|
},
|
|
addConditionData() {
|
|
this.$refs.form.validate((valid) => {
|
|
if (valid) {
|
|
const newConditionData = {}
|
|
newConditionData.field = this.form.field.label
|
|
newConditionData.fieldName = this.form.field.value
|
|
newConditionData.symbol = this.form.symbol.label
|
|
newConditionData.symbolCode = this.form.symbol.value
|
|
newConditionData.keyWord = this.form.keyWord
|
|
this.conditionData.push(newConditionData)
|
|
this.$nextTick(() => {
|
|
const container = document.getElementById('condition-container-modal')
|
|
container.scrollTop = container.scrollHeight
|
|
})
|
|
}
|
|
})
|
|
},
|
|
moveUp(index) {
|
|
if (index > 0) {
|
|
const temp = this.conditionData[index]
|
|
this.conditionData[index] = this.conditionData[index - 1]
|
|
this.conditionData[index - 1] = temp
|
|
this.currentIndex = index - 1
|
|
}
|
|
const targetElement = document.getElementById('modal-element-id-' + this.currentIndex)
|
|
if (targetElement) {
|
|
targetElement.scrollIntoView({ behavior: 'smooth', block: 'nearest' })
|
|
}
|
|
},
|
|
moveDown(index) {
|
|
if (index < this.conditionData.length - 1) {
|
|
const temp = this.conditionData[index]
|
|
this.conditionData[index] = this.conditionData[index + 1]
|
|
this.conditionData[index + 1] = temp
|
|
this.currentIndex = index + 1
|
|
}
|
|
const targetElement = document.getElementById('modal-element-id-' + this.currentIndex)
|
|
if (targetElement) {
|
|
targetElement.scrollIntoView({ behavior: 'smooth', block: 'nearest' })
|
|
}
|
|
},
|
|
deltCurrent(index) {
|
|
this.conditionData.splice(index, 1)
|
|
this.currentIndex = null
|
|
},
|
|
selectCurrent(index) {
|
|
if (this.currentIndex === index) {
|
|
this.currentIndex = null
|
|
} else {
|
|
this.currentIndex = index
|
|
}
|
|
},
|
|
addConnector(item) {
|
|
const newConditionData = {}
|
|
if (item === '并且' || item === '或者') {
|
|
newConditionData.connector = item
|
|
} else {
|
|
newConditionData.bracket = item
|
|
}
|
|
this.conditionData.push(newConditionData)
|
|
this.$nextTick(() => {
|
|
const container = document.getElementById('condition-container-modal')
|
|
container.scrollTop = container.scrollHeight
|
|
})
|
|
},
|
|
getFieldCommon() {
|
|
const params = {
|
|
'categoryId': this.selectedCategory.id,
|
|
'categoryLevel': this.collectLevel
|
|
}
|
|
// console.log('params', params)
|
|
FetchInitCategoryInputFieldByPid(params).then((data) => {
|
|
if (data && data.length > 0) {
|
|
this.fieldOptions = data
|
|
.filter(item => item.fieldName !== 'fonds_no' && item.fieldName !== 'fonds_name')
|
|
.map(item => {
|
|
return {
|
|
id: item.id,
|
|
label: item.fieldCnName,
|
|
value: item.fieldName
|
|
}
|
|
})
|
|
}
|
|
})
|
|
},
|
|
checkConditions(conditionData) {
|
|
let brackets = 0
|
|
let fields = 0
|
|
let connectors = 0
|
|
let previousTokenType = null
|
|
let hasValidConditionBetweenBrackets = false
|
|
|
|
for (var i = 0; i < conditionData.length; i++) {
|
|
const condition = conditionData[i]
|
|
let currentTokenType = ''
|
|
|
|
if (condition.hasOwnProperty('bracket')) {
|
|
currentTokenType = 'bracket'
|
|
brackets++
|
|
} else if (condition.hasOwnProperty('field')) {
|
|
currentTokenType = 'field'
|
|
fields++
|
|
if (brackets > 0) {
|
|
hasValidConditionBetweenBrackets = true
|
|
}
|
|
} else if (condition.hasOwnProperty('connector')) {
|
|
currentTokenType = 'connector'
|
|
connectors++
|
|
}
|
|
|
|
if (previousTokenType && currentTokenType) {
|
|
if ((previousTokenType === 'field' && currentTokenType === 'field') ||
|
|
(previousTokenType === 'connector' && currentTokenType === 'connector')) {
|
|
this.$message({ message: '条件之间缺少或且连接符', type: 'error', offset: 8 })
|
|
return null
|
|
}
|
|
}
|
|
previousTokenType = currentTokenType
|
|
}
|
|
|
|
if (brackets > 0 && !hasValidConditionBetweenBrackets) {
|
|
this.$message({ message: '请输入有效条件', type: 'error', offset: 8 })
|
|
return null
|
|
} else if (brackets > 0 && brackets % 2 !== 0) {
|
|
this.$message({ message: '括号不对称', type: 'error', offset: 8 })
|
|
return null
|
|
} else if (fields === 0) {
|
|
this.$message({ message: '请输入有效条件', type: 'error', offset: 8 })
|
|
return null
|
|
} else if (fields === 1 || connectors === fields - 1) {
|
|
const wheresql = this.conditionData.map(obj => {
|
|
if (obj.field) {
|
|
if (obj.symbol === '包含' || obj.symbol === '不包含') {
|
|
return obj.fieldName + ' ' + obj.symbolCode + " '%" + obj.keyWord + "%'"
|
|
} else if (obj.keyWord && isNaN(parseInt(obj.keyWord))) {
|
|
return obj.fieldName + ' ' + obj.symbolCode + " '" + obj.keyWord + "'"
|
|
} else {
|
|
return obj.fieldName + ' ' + obj.symbolCode + ' ' + obj.keyWord
|
|
}
|
|
} else if (obj.connector === '并且') {
|
|
return 'and'
|
|
} else if (obj.connector === '或者') {
|
|
return 'or'
|
|
} else {
|
|
return obj.bracket
|
|
}
|
|
}).join(' ')
|
|
return wheresql
|
|
} else {
|
|
this.$message({ message: '条件之间缺少或且连接符', type: 'error', offset: 8 })
|
|
return null
|
|
}
|
|
},
|
|
handleSearch() {
|
|
const wheresql = this.checkConditions(this.conditionData)
|
|
// console.log('wheresql', wheresql)
|
|
if (wheresql) {
|
|
const conditions = JSON.parse(JSON.stringify(this.conditionData))
|
|
// 保存到 localStorage
|
|
localStorage.setItem('advancedSearchConditions', JSON.stringify(conditions))
|
|
localStorage.setItem('advancedSearchSql', wheresql)
|
|
// 发送 SQL 条件和原始条件数据(用于显示文案)
|
|
this.$emit('search', {
|
|
sql: wheresql,
|
|
conditions: conditions
|
|
})
|
|
}
|
|
},
|
|
handleClose() {
|
|
this.$emit('update:visible', false)
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style lang='scss' scoped>
|
|
.advanced-search-modal {
|
|
.el-form--inline .el-form-item {
|
|
margin-right: 20px !important;
|
|
}
|
|
.advanced-btn {
|
|
display: flex;
|
|
justify-content: center;
|
|
margin-top: 15px;
|
|
.el-button {
|
|
margin-right: 10px;
|
|
}
|
|
}
|
|
.search-condition {
|
|
padding: 18px;
|
|
margin-top: 20px;
|
|
background: #F6F9FF;
|
|
border-radius: 3px;
|
|
border: 1px dashed #DCDFE6;
|
|
h4 {
|
|
margin: 0 0 17px 0;
|
|
padding-left: 25px;
|
|
color: #0C0E1E;
|
|
font-size: 14px;
|
|
font-weight: bold;
|
|
}
|
|
}
|
|
.condition-main {
|
|
display: flex;
|
|
justify-content: center;
|
|
flex-wrap: nowrap;
|
|
.condition-left {
|
|
display: flex;
|
|
flex-direction: column;
|
|
justify-content: center;
|
|
.el-button {
|
|
width: 76px;
|
|
margin: 5px 0;
|
|
::v-deep i.el-icon-top,
|
|
::v-deep i.el-icon-bottom {
|
|
font-size: 16px;
|
|
font-weight: bold;
|
|
}
|
|
}
|
|
}
|
|
.condition-content {
|
|
width: 500px;
|
|
height: 160px;
|
|
margin: 0 10px;
|
|
padding: 0;
|
|
background: #E6E8ED;
|
|
overflow: hidden;
|
|
overflow-y: scroll;
|
|
li {
|
|
display: flex;
|
|
justify-content: center;
|
|
flex-wrap: nowrap;
|
|
height: 32px;
|
|
line-height: 32px;
|
|
font-size: 14px;
|
|
text-align: center;
|
|
background-color: #fff;
|
|
border-bottom: 1px solid #E6E8ED;
|
|
cursor: default;
|
|
&:hover,
|
|
&:focus,
|
|
&.active {
|
|
background-color: #E8F2FF;
|
|
}
|
|
.keyword-style {
|
|
color: #2ECAAC;
|
|
i {
|
|
font-style: normal;
|
|
color: #545B65;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
.condition-right {
|
|
display: flex;
|
|
flex-direction: column;
|
|
justify-content: center;
|
|
.el-button {
|
|
width: 64px;
|
|
margin: 5px 0;
|
|
background-color: #0348F3;
|
|
color: #fff;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
</style>
|