一、条件层级效果图
二、代码
<template>
<ContentWrap>
<!-- 添加条件分支:level1 -->
<div class="btnBox" v-if="isEdit">
<el-button type="primary" @click="add">添加条件分支</el-button>
</div>
<div v-if="tableList.length > 0" class="boxOne">
<div class="title" v-for="(level1, index) in tableList" :key="level1.accountName">
<!-- 返回科目:level2 -->
<el-form-item :label="`${level1.accountName}`" label-width="80px">
<el-select
v-model="level1.conditionName"
placeholder="请选择数据"
:disabled="!isEdit"
filterable
>
<el-option
v-for="(item, index1) in titleList"
:key="index1"
:label="item.label"
:value="item.value"
/>
</el-select>
<el-button
link
:icon="Delete"
@click="handleDelete(index)"
type="danger"
class="ml-10px"
v-if="isEdit"
/>
<el-button link :icon="Plus" @click="handleAdd(level1)" v-if="isEdit" />
</el-form-item>
<!-- 条件设置:level3 -->
<div class="boxTwo" v-if="level1.children?.length > 0">
<!-- 此处可不展示 style="display: none" -->
<div class="left">
<div class="round" @click="handleRelation(level1)">
{{ level1.relation }}
</div>
<div class="border"></div>
</div>
<div class="right">
<div
class="level1Right"
v-for="(level2, level2Index) in level1.children"
:key="level2Index"
>
<!-- 符号:or/AND -->
<div class="left" v-if="level2.children?.length > 0">
<div class="round" @click="handleLevelRelation(level2)">
{{ level2.relation }}
</div>
<div class="border"></div>
</div>
<div class="levelRight" v-if="level2.children?.length > 0">
<div
class="mb-20px"
v-for="(level3, level3Index) in level2.children"
:key="level3Index"
>
<el-row :gutter="20">
<!-- 字典表 -->
<el-col :span="5">
<el-form-item>
<el-select
v-model="level3.pName"
placeholder="请选择数据"
:disabled="!isEdit"
>
<el-option
v-for="(operator, index1) in operatorList"
:key="index1"
:label="operator.label"
:value="operator.value"
/>
</el-select>
</el-form-item>
</el-col>
<!-- 数值类型 -->
<el-col :span="5">
<el-form-item>
<el-select
v-model="level3.pType"
:placeholder="t('common.inputText')"
:disabled="!isEdit"
>
<el-option
v-for="(accountType, index1) in accountTypeList"
:key="index1"
:label="accountType.label"
:value="accountType.value"
/>
</el-select>
</el-form-item>
</el-col>
<!-- 运算符,根据数值类型显示 -->
<el-col :span="5">
<el-form-item>
<el-select
v-model="level3.pOption"
placeholder="请选择数据"
:disabled="level3.pType === '' || !isEdit"
>
<el-option
v-for="(kind, index1) in typeMap[level3.pType]"
:key="index1"
:label="kind.label"
:value="kind.value"
/>
</el-select>
</el-form-item>
</el-col>
<!-- 值 -->
<el-col :span="6">
<el-form-item>
<el-input
placeholder="请输入"
v-model="level3.pValue"
:disabled="!isEdit"
/>
<!-- <el-date-picker
v-if="level3.pType === '3'"
v-model="level3.pValue"
clearable
:placeholder="t('common.selectText')"
type="date"
value-format="YYYY-MM-DD"
:disabled="!isEdit"
/>
<el-input
v-else
placeholder="请输入"
v-model="level3.pValue"
:disabled="!isEdit"
/> -->
</el-form-item>
</el-col>
<!-- 操作 -->
<el-col :span="3">
<el-form-item>
<el-button
link
:icon="Delete"
@click="handleRowDelete(level2, level3Index)"
type="danger"
class="ml-10px"
v-if="isEdit"
/>
<el-button
link
:icon="Plus"
@click="handleRowAdd(level2, index)"
v-if="isEdit"
/>
</el-form-item>
</el-col>
</el-row>
</div>
<div>
<el-button
link
:icon="Delete"
@click="handleNodeDelete(level1, level2Index)"
type="danger"
class="ml-10px"
v-if="isEdit"
/>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</ContentWrap>
</template>
<script lang="ts" setup>
import { Delete, Plus } from '@element-plus/icons-vue'
import * as apiType from '@/api/system/dict/dict.data.ts'
defineOptions({ name: 'SettingTable' })
const { t } = useI18n() // 国际化
const message = useMessage() // 消息弹窗
const emits = defineEmits(['success'])
const titleList = [
{ label: 'lemon', value: 1 },
{ label: 'orange', value: 2 },
{ label: 'apple', value: 3 },
{ label: 'banana', value: 4 },
{ label: 'mango', value: 5 },
{ label: 'cherry', value: 6 }
]
const operatorList = [
{ label: 'doudou1', value: 1 },
{ label: 'doudou2', value: 2 },
{ label: 'doudou3', value: 3 },
{ label: 'doudou4', value: 4 },
{ label: 'doudou5', value: 5 },
{ label: 'doudou6', value: 6 }
]
const accountTypeList = [
{
value: 1,
label: '数值'
},
{
value: 2,
label: '字符串'
},
{
value: 3,
label: '日期'
}
]
// 条件配置(数据类型:数值)
const numberConfig = [
{
value: 1,
label: '>'
},
{
value: 2,
label: '>='
},
{
value: 3,
label: '<= '
},
{
value: 4,
label: '<'
},
{
value: 5,
label: '='
},
{
value: 6,
label: '!='
}
]
// 条件配置(数据类型:字符串)
const stringConfig = [
{
value: 7,
label: 'equals'
},
{
value: 8,
label: 'contains'
},
{
value: 9,
label: 'startsWith'
},
{
value: 10,
label: 'endsWith'
},
{
value: 11,
label: 'equals'
},
{
value: 12,
label: 'before'
},
{
value: 13,
label: 'after'
}
]
// 条件配置(数据类型:日期)
const dateConfig = [
{
value: 11,
label: 'YYYY-MM-DD'
},
{
value: 12,
label: ' YYYY-MM-DD HH:mm:ss'
},
{
value: 13,
label: 'YYYY-MM'
}
]
interface AccountItem {
accountCode?: string
accountName?: string
label?: string
value?: string
}
interface ConditionItem {
conditionName?: string
children?: childrenItems[]
accountName?: string
relation?: string
}
interface itemProps {
pName: string
pType: string
pOption: string
pValue: string
}
interface childrenItems {
relation?: string
children?: itemProps[]
}
const props = defineProps({
accountList: {
type: Array as () => AccountItem[],
required: true
},
chooseList: {
type: Array as () => ConditionItem[],
default: () => []
},
isEdit: {
type: Boolean,
default: false
}
})
const chooseList = computed(() => {
return props.chooseList
})
const tableList = ref<ConditionItem[]>([])
// 运算符
const typeMap = {
'1': numberConfig,
'2': stringConfig,
'3': dateConfig
}
/** 第一步:【level1】添加条件分支:新增 */
const add = () => {
console.log('第一步:')
// 1、判断上面一条数据是否填写
const lastItem = tableList.value[tableList.value.length - 1]
if (lastItem !== undefined) {
if (lastItem?.conditionName === '') {
message.error(`返回科目规则需配置完整`)
return
}
const hasAllValues = checkValues(lastItem.children)
if (!hasAllValues) {
message.error('请填写完整条件')
return
}
}
// 2、添加数据
const result: ConditionItem = {
accountName: '返回科目',
conditionName: '',
relation: 'AND',
children: []
}
tableList.value.push(result)
console.log('添加条件分支', tableList.value)
emits('success', tableList.value)
}
// 外层:条件
const handleRelation = (item) => {
console.log('外层', item)
if (!props.isEdit) return
item.relation = item.relation === 'AND' ? 'OR' : 'AND'
console.log('????', tableList.value)
emits('success', tableList.value)
}
function checkValues(data) {
for (const item of data) {
if (item.pName === '' || item.pType === '' || item.pOption === '' || item.pValue === '') {
return false
}
if (item.children && item.children.length > 0) {
const hasAllFields = checkValues(item.children)
if (!hasAllFields) {
return false
}
}
}
return true
}
/** 第二步:【level2】返回科目:新增、删除、条件关系 */
// 新增
const handleAdd = (item) => {
console.log('第二步:', item)
// 1、判断上面一条数据是否填写
if (item?.conditionName === '') {
message.error('请选择提成科目!')
return
}
// 2、判断上条数据是否填写完整
const lastItem = item.children[item.children.length - 1]
if (lastItem !== undefined) {
if (lastItem.children?.length > 0) {
const hasAllValues = checkFields(lastItem)
if (!hasAllValues) {
message.error('请填写完整条件')
return
}
}
} else {
if (lastItem?.children?.length == 0) {
message.error('请填写完整条件')
return
}
}
// 3、添加数据
const result: itemProps = {
pName: '',
pType: '',
pOption: '',
pValue: ''
}
item.children.push({
relation: 'AND',
children: [result]
})
console.log('科目添加条件:新增', tableList.value)
emits('success', tableList.value)
}
// 删除
const handleDelete = (index) => {
console.log('第一步:科目添加条件:删除', tableList.value)
tableList.value.splice(index, 1)
emits('success', tableList.value)
}
// 条件关系
const handleLevelRelation = (item) => {
console.log('里层')
if (!props.isEdit) return
item.relation = item.relation === 'AND' ? 'OR' : 'AND'
emits('success', tableList.value)
}
// 判断数组中的每个对象的 pName、pType、pOption、pValue 字段是否都有值
function checkFields(data) {
for (const item of data.children) {
if (item.pName === '' || item.pType === '' || item.pOption === '' || item.pValue === '') {
return false
}
}
return true
}
/** 第三步:行内添加 */
const handleRowAdd = (item, index: number) => {
console.log('第三步:新增行', item, index)
// 1、判断上面一条数据是否填写
const arr = item.children
const lastItem = arr.length > 0 ? arr[arr.length - 1] : null
console.log('lastItem', lastItem)
if (
lastItem.pName === '' ||
lastItem.pType === '' ||
lastItem.pOption === '' ||
lastItem.pValue === ''
) {
message.error(`返回科目规则需配置完整!`)
return
}
// 2、添加数据
const result: itemProps = {
pName: '',
pType: '',
pOption: '',
pValue: ''
}
item.children.push(result)
console.log('tableList', tableList.value)
emits('success', tableList.value)
}
const handleRowDelete = (item, index?: number) => {
item.children.splice(index, 1)
console.log('????', tableList.value)
emits('success', tableList.value)
}
/** 第四步:删除节点 */
const handleNodeDelete = (item, index) => {
item.children.splice(index, 1)
emits('success', tableList.value)
}
onMounted(() => {
tableList.value = chooseList.value // 初始化赋值
})
watch(
() => props.chooseList,
(val) => {
tableList.value = val
}
)
</script>
<style lang="scss" scoped>
.title {
padding: 10px 0;
}
.btnBox {
display: flex;
justify-content: flex-end;
}
.boxTwo {
display: flex;
justify-content: space-between;
margin-top: 10px;
.left {
display: flex;
align-items: center;
position: relative;
.round {
width: 30px;
height: 30px;
border-radius: 50%;
background-color: white;
border: 1px solid black;
font-size: 14px;
color: black;
margin: auto;
position: relative;
z-index: 3;
text-align: center;
}
.border {
position: absolute;
left: 50%;
top: 0;
bottom: 0;
border-left: 1px solid black;
}
}
.right {
flex: 1;
padding: 10px;
}
.level1Right {
display: flex;
margin-bottom: 10px;
}
.levelRight {
flex: 1;
padding: 10px 10px 0 10px;
display: block;
}
}
</style>