
<template>
<el-form ref="conditionForm" :model="formData" :rules="validateRules" label-width="0px" class="addCondition-main">
<el-button type="primary" @click="addGroup">添加条件组</el-button>
<div class="filter-container">
<div v-for="(group, groupIndex) in formData.filterGroups" :key="groupIndex" class="filter-group">
<div class="group-header">
<span class="group-title">过滤条件组{{ groupIndex + 1 }}</span>
<el-button type="text" icon="el-icon-error" class="group-delete-btn"
@click="deleteGroup(groupIndex)" size="mini"></el-button>
</div>
<div class="condition-row">
<div v-for="(condition, condIndex) in group.conditions" :key="condIndex" class="condition-item">
<el-form-item :prop="`filterGroups[${groupIndex}].conditions[${condIndex}].field`"
:rules="validateRules.field">
<el-select v-model="condition.field" placeholder="請選擇字段名稱" class="field-select" size="mini"
@change="changeField(groupIndex, condIndex, condition.field)">
<el-option :label="item.tabelFieldName || item.tabelField" :value="item.tabelField"
v-for="(item, index) in selectData" :key="item.tabelField"></el-option>
</el-select>
</el-form-item>
<el-form-item :prop="`filterGroups[${groupIndex}].conditions[${condIndex}].operator`"
:rules="validateRules.operator">
<el-select v-model="condition.operator" placeholder="請選擇條件" class="operator-select"
size="mini" @change="handleOperatorChange(condition.operator, groupIndex, condIndex)">
<el-option v-for="dict in dict.type.ind_operator" :key="dict.value" :label="dict.label"
:value="dict.value" />
</el-select>
</el-form-item>
<el-form-item :prop="`filterGroups[${groupIndex}].conditions[${condIndex}].value`"
:rules="validateRules.value">
<el-input v-model="condition.value" placeholder="請輸入內容" class="value-input" size="mini"
prefix-icon="el-icon-search" :maxlength="getMaxLength(condition.operator)"
:disabled="isValueDisabled(condition.operator)" :type="getType(condition.operator)"
v-if="condition.operator !== 'between'"></el-input>
<NumberRangeInput
v-if="condition.operator === 'between' && (!fieldTypeArray.includes(condition.fieldType))"
v-model="condition.value" input-width="200px" :clearable="false" ref="priceRangeRef">
</NumberRangeInput>
<DateRangePicker
v-if="condition.operator === 'between' && fieldTypeArray.includes(condition.fieldType)"
v-model="condition.value"></DateRangePicker>
</el-form-item>
<el-button type="text" icon="el-icon-remove" @click="deleteCondition(groupIndex, condIndex)"
size="mini" class="condition-delete-btn" v-if="group.conditions.length > 1"></el-button>
</div>
<el-button type="text" icon="el-icon-circle-plus" @click="addCondition(groupIndex)" size="mini"
class="add-condition-btn" v-if="group.conditions.length < 10"></el-button>
<el-form-item v-if="group.conditions.length > 1" :prop="`filterGroups[${groupIndex}].logic`"
:rules="validateRules.logic">
<el-select v-model="group.logic" class="group-logic-select" size="mini"
v-if="group.conditions.length > 1">
<el-option v-for="dict in dict.type.ind_groups_logic" :key="dict.value" :label="dict.label"
:value="dict.value" />
</el-select>
</el-form-item>
</div>
</div>
<el-form-item v-if="formData.filterGroups.length > 1" prop="groupsLogic" :rules="validateRules.groupsLogic"
class="groups-logic-select">
<el-select v-model="formData.groupsLogic" size="mini" v-if="formData.filterGroups.length > 1">
<el-option v-for="dict in dict.type.ind_groups_logic" :key="dict.value" :label="dict.label"
:value="dict.value" />
</el-select>
</el-form-item>
</div>
</el-form>
</template>
<script>
import NumberRangeInput from '@/components/NumberRangeInput/NumberRangeInput.vue';
import DateRangePicker from '@/components/DateRangePicker/DateRangePicker.vue';
export default {
dicts: ['ind_operator', 'ind_groups_logic'],
name: 'addCondition',
inject: ['grandchildMethods', 'parentChildMethods'],
props: {
selectData: {
type: Array,
default: () => []
},
initFilterGroups: {
type: Object,
default: () => { }
}
},
watch: {
selectData: {
handler(val) {
if (val) {
console.log('selectData', val)
}
},
immediate: true,
deep: true
},
initFilterGroups: {
handler(val) {
console.log("initFilterGroups===>", Object.keys(val))
if (!this.isEmptyObjStrict(val)) {
console.log("initFilterGroups", val)
this.formData = JSON.parse(JSON.stringify(val))
} else if (this.$route.query.formType === 'edit') {
this.formData = {
filterGroups: [],
groupsLogic: ''
}
}
},
immediate: true,
deep: true
}
},
data() {
const validateGroupLogic = (rule, value, callback) => {
const prop = rule.field;
const groupIndex = prop.match(/filterGroups\[(\d+)\].logic/)[1];
const group = this.formData.filterGroups[groupIndex];
console.log("組group", group)
if (group.conditions.length > 1 && !value) {
callback(new Error('请选择關係'));
} else {
callback();
}
};
const validateGroupsLogic = (rule, value, callback) => {
if (this.formData.filterGroups.length > 1 && !value) {
callback(new Error('请选择關係'));
} else {
callback();
}
};
return {
formData: {
filterGroups: [
{
logic: '',
conditions: [
{ field: '', operator: '', value: '', fieldType: '' }
]
}
],
groupsLogic: ''
},
validateRules: {
field: [
{ required: true, message: '请选择字段名称', trigger: 'change' }
],
operator: [
{ required: true, message: '请选择操作符', trigger: 'change' }
],
value: [
{
validator: this.validateValue,
trigger: ['blur', 'change']
}
],
logic: [
{ validator: validateGroupLogic, trigger: 'change' }
],
groupsLogic: [
{ validator: validateGroupsLogic, trigger: 'change' }
],
},
fieldTypeArray: ['DATE', 'TIME', 'DATETIME', 'TIMESTAMP']
}
},
components: { NumberRangeInput, DateRangePicker },
created() { },
mounted() {
this.grandchildMethods.validateFun = this.validateFun
this.grandchildMethods.resetForm = this.resetForm
this.parentChildMethods.resetForm = this.resetForm
},
methods: {
addGroup() {
if (this.formData.filterGroups.length == 10) {
this.$message.warning('至多只能添加10條')
return
}
this.formData.filterGroups.push({
logic: '',
conditions: [
{ field: '', operator: '', value: '', fieldType: '' }
]
})
},
deleteGroup(index) {
this.formData.filterGroups.splice(index, 1)
this.clearValidate()
},
addCondition(groupIndex) {
this.formData.filterGroups[groupIndex].conditions.push({
field: '', operator: '', value: '', fieldType: ''
})
console.log("addCondition", this.formData.filterGroups)
},
deleteCondition(groupIndex, condIndex) {
this.formData.filterGroups[groupIndex].conditions.splice(condIndex, 1)
this.clearValidate()
},
isEmptyObjStrict(obj) {
if (typeof obj !== 'object' || obj === null || Array.isArray(obj)) {
return false;
}
return Object.keys(obj).length === 0;
},
validateFun() {
console.log("校驗條件")
return new Promise((resolve, reject) => {
if (!this.$refs.conditionForm) {
reject('表单实例未找到')
return
}
this.$refs.conditionForm.validate((valid, errorFields) => {
if (valid) {
if (this.formData.filterGroups.length !== 0) {
resolve(JSON.parse(JSON.stringify(this.formData)))
} else {
resolve()
}
} else {
reject(errorFields)
}
})
})
},
resetForm() {
this.formData = {
filterGroups: [
{
logic: '',
conditions: [
{ field: '', operator: '', value: '', fieldType: '' }
]
}
],
groupsLogic: ''
}
this.clearValidate()
},
clearValidate() {
if (this.$refs.conditionForm) {
this.$refs.conditionForm.clearValidate()
}
},
validateField(groupIndex, condIndex, field) {
const prop = `filterGroups[${groupIndex}].conditions[${condIndex}].${field}`
this.$refs.conditionForm.validateField(prop, (error) => {
if (error) {
console.log(`校验失败:${error}`)
}
})
},
handleOperatorChange(val, groupIndex, condIndex) {
let installVal = ['isnull', 'isnotnull']
if (installVal.includes(val)) {
this.formData.filterGroups[groupIndex].conditions[condIndex].value = ''
}
this.validateField(groupIndex, condIndex, 'value');
},
validateValue(rule, value, callback) {
console.log("value", value)
const prop = rule.field;
const match = prop.match(/filterGroups\[(\d+)\].conditions\[(\d+)\].value/);
if (!match) {
callback();
return;
}
const [, groupIndex, condIndex] = match;
const condition = this.formData.filterGroups[groupIndex].conditions[condIndex];
const { operator } = condition;
const numberOperators = ['gt', 'ge', 'lt', 'le'];
const disabledOperators = ['isnull', 'isnotnull'];
if (disabledOperators.includes(operator)) {
callback();
return;
}
if ((!disabledOperators.includes(operator)) && (!value || value.trim() === '')) {
callback(new Error('請輸入值'));
return;
}
if (numberOperators.includes(operator) && value && !/^-?[0-9]+(\.[0-9]+)?$/.test(value.trim())) {
callback(new Error('僅允許輸入數字'));
return;
}
if (value && value.length > 50) {
callback(new Error('輸入內容不能超過50個字符'));
return;
}
callback();
},
getMaxLength(operator) {
const noLimitOperators = ['isnull', 'isnotnull'];
if (noLimitOperators.includes(operator)) return null;
return 50;
},
isValueDisabled(operator) {
return ['isnull', 'isnotnull'].includes(operator);
},
getType(operator) {
const numberOperators = ['gt', 'ge', 'lt', 'le',];
if (numberOperators.includes(operator)) {
return "number"
} else {
return "text"
}
},
changeField(groupIndex, condIndex, field) {
const selectData = this.selectData
let obj = selectData.find(item => item.tabelField === field)
this.formData.filterGroups[groupIndex].conditions[condIndex].fieldType = obj.fieldType
console.log(" this.formData.filterGroups", this.formData)
},
}
}
</script>
<style scoped lang="scss">
// 样式部分不变,保留之前的优化
.addCondition-main {
.el-form-item {
margin-bottom: 0;
}
}
.filter-container {
background: #fff;
border-radius: 4px;
padding: 15px 0;
position: relative;
}
.filter-group {
width: calc(100% - 80px);
margin-bottom: 15px;
padding: 10px;
border: 1px solid #e6e6e6;
}
.group-header {
display: flex;
align-items: center;
margin-bottom: 10px;
position: relative;
}
.group-title {
font-weight: 500;
color: #333;
}
.group-delete-btn {
position: absolute;
top: -20px;
right: -20px;
font-size: 20px;
padding: 0px !important;
}
.condition-row {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 8px;
}
.condition-item {
display: flex;
align-items: center;
gap: 5px;
.el-form-item__error {
position: absolute;
top: 100%;
left: 0;
font-size: 12px;
padding-top: 2px;
}
}
.field-select {
width: 100px;
}
.operator-select {
width: 80px;
}
.value-input {
width: 200px;
}
.condition-delete-btn {
color: #999;
font-size: 20px;
padding: 0px !important;
}
.add-condition-btn {
color: #409eff;
font-size: 20px;
padding: 0px !important;
}
.group-logic-select {
width: 75px;
::v-deep .el-input__inner {
padding-left: 5px;
padding-right: 10px;
}
::v-deep .el-input__suffix {
right: 0 !important;
}
}
.groups-logic-select {
width: 80px;
position: absolute;
right: 0px;
bottom: 31px;
::v-deep .el-form-item__error {
z-index: 10;
white-space: nowrap;
}
}
::v-deep .el-form-item__error {
z-index: 10;
white-space: nowrap;
}
::v-deep .el-date-editor {
width: 200px !important;
height: 28px !important;
line-height: 28px !important;
padding:0 10px !important;
// .el-input__icon {
// display: none !important;
// }
.el-range-separator {
padding: 0 !important;
}
.el-range-input {
width: 50%;
}
.el-input__inner {
// height: 28px !important;
// line-height: 28px !important;
}
.el-range__icon{
line-height: 28px !important;
}
}
</style>