feat: 场景联动新增过滤条件组件
This commit is contained in:
parent
a28f46e01c
commit
2cd4d7ddb1
|
@ -0,0 +1,254 @@
|
|||
<template>
|
||||
<div class='terms-params-item'>
|
||||
<div v-if='!isFirst' class='term-type-warp'>
|
||||
<DropdownButton
|
||||
:options='[
|
||||
{ label: "并且", value: "and" },
|
||||
{ label: "或者", value: "or" },
|
||||
]'
|
||||
type='type'
|
||||
v-model:value='paramsValue.type'
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class='params-item_button'
|
||||
@mouseover='mouseover'
|
||||
@mouseout='mouseout'
|
||||
>
|
||||
<DropdownButton
|
||||
:options='columnOptions'
|
||||
icon='icon-zhihangdongzuoxie-1'
|
||||
type='column'
|
||||
value-name='column'
|
||||
label-name='fullName'
|
||||
placeholder='请选择参数'
|
||||
v-model:value='paramsValue.column'
|
||||
component='treeSelect'
|
||||
@select='columnSelect'
|
||||
/>
|
||||
<DropdownButton
|
||||
:options='termTypeOptions'
|
||||
type="termType"
|
||||
value-name='id'
|
||||
label-name='name'
|
||||
placeholder="操作符"
|
||||
v-model:value='paramsValue.termType'
|
||||
@select='termsTypeSelect'
|
||||
/>
|
||||
<DoubleParamsDropdown
|
||||
v-if='showDouble'
|
||||
icon='icon-canshu'
|
||||
placeholder='参数值'
|
||||
:options='valueOptions'
|
||||
:metricOptions='metricOption'
|
||||
:tabsOptions='tabsOptions'
|
||||
v-model:value='paramsValue.value.value'
|
||||
v-model:source='paramsValue.value.source'
|
||||
/>
|
||||
<ParamsDropdown
|
||||
v-else
|
||||
icon='icon-canshu'
|
||||
placeholder='参数值'
|
||||
:options='valueOptions'
|
||||
:metricOptions='metricOption'
|
||||
:tabsOptions='tabsOptions'
|
||||
v-model:value='paramsValue.value.value'
|
||||
v-model:source='paramsValue.value.source'
|
||||
/>
|
||||
<j-popconfirm title='确认删除?' @confirm='onDelete'>
|
||||
<div v-show='showDelete' class='button-delete'> <AIcon type='CloseOutlined' /></div>
|
||||
</j-popconfirm>
|
||||
</div>
|
||||
<div class='term-add' @click.stop='termAdd' v-if='isLast'>
|
||||
<div class='terms-content'>
|
||||
<AIcon type='PlusOutlined' style='font-size: 12px' />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang='ts' name='FilterCondition'>
|
||||
import type { PropType } from 'vue'
|
||||
import type { TermsType } from '@/views/rule-engine/Scene/typings'
|
||||
import DropdownButton from '../../components/DropdownButton'
|
||||
import { getOption } from '../../components/DropdownButton/util'
|
||||
import ParamsDropdown, { DoubleParamsDropdown } from '../../components/ParamsDropdown'
|
||||
import { inject } from 'vue'
|
||||
import { ContextKey } from '../../components/Terms/util'
|
||||
import { useSceneStore } from 'store/scene'
|
||||
import { storeToRefs } from 'pinia';
|
||||
|
||||
const sceneStore = useSceneStore()
|
||||
const { data: formModel } = storeToRefs(sceneStore)
|
||||
|
||||
type Emit = {
|
||||
(e: 'update:value', data: TermsType): void
|
||||
}
|
||||
|
||||
type TabsOption = {
|
||||
label: string;
|
||||
key: string;
|
||||
component: string
|
||||
}
|
||||
|
||||
const props = defineProps({
|
||||
isFirst: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
isLast: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
showDeleteBtn: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
name: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
termsName: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
branchName: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
thenName: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
value: {
|
||||
type: Object as PropType<TermsType>,
|
||||
default: () => ({
|
||||
column: '',
|
||||
type: '',
|
||||
termType: 'eq',
|
||||
value: {
|
||||
source: 'fixed',
|
||||
value: undefined
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits<Emit>()
|
||||
|
||||
const paramsValue = reactive<TermsType>({
|
||||
column: props.value.column,
|
||||
type: props.value.type,
|
||||
termType: props.value.termType,
|
||||
value: props.value.value
|
||||
})
|
||||
|
||||
const showDelete = ref(false)
|
||||
const columnOptions: any = inject(ContextKey) //
|
||||
const termTypeOptions = ref<Array<{ id: string, name: string}>>([]) // 条件值
|
||||
const valueOptions = ref<any[]>([]) // 默认手动输入下拉
|
||||
const metricOption = ref<any[]>([]) //
|
||||
const tabsOptions = ref<Array<TabsOption>>([{ label: '内置参数', key: 'upper', component: 'tree' }])
|
||||
// { label: '手动输入', key: 'fixed', component: 'string' },
|
||||
|
||||
|
||||
const handOptionByColumn = (option: any) => {
|
||||
if (option) {
|
||||
termTypeOptions.value = option.termTypes || []
|
||||
metricOption.value = option.metrics || []
|
||||
tabsOptions.value.length = 1
|
||||
tabsOptions.value[0].component = option.dataType
|
||||
|
||||
if (option.metrics && option.metrics.length) {
|
||||
tabsOptions.value.push(
|
||||
{ label: '指标值', key: 'metric', component: 'select' }
|
||||
)
|
||||
}
|
||||
|
||||
if (option.dataType === 'boolean') {
|
||||
valueOptions.value = [
|
||||
{ label: '是', value: true },
|
||||
{ label: '否', value: false },
|
||||
]
|
||||
} else if(option.dataType === 'enum') {
|
||||
valueOptions.value = option.options?.map((item: any) => ({ ...item, label: item.name, value: item.id})) || []
|
||||
} else{
|
||||
valueOptions.value = option.options || []
|
||||
}
|
||||
} else {
|
||||
termTypeOptions.value = []
|
||||
metricOption.value = []
|
||||
valueOptions.value = []
|
||||
}
|
||||
}
|
||||
|
||||
watchEffect(() => {
|
||||
const option = getOption(columnOptions.value, paramsValue.column, 'column')
|
||||
handOptionByColumn(option)
|
||||
})
|
||||
|
||||
const showDouble = computed(() => {
|
||||
const isRange = paramsValue.termType ? ['nbtw', 'btw', 'in', 'nin'].includes(paramsValue.termType) : false
|
||||
if (metricOption.value.length) {
|
||||
metricOption.value = metricOption.value.filter(item => isRange ? item.range : !item.range)
|
||||
} else {
|
||||
metricOption.value = []
|
||||
}
|
||||
return isRange
|
||||
})
|
||||
|
||||
const mouseover = () => {
|
||||
if (props.showDeleteBtn){
|
||||
showDelete.value = true
|
||||
}
|
||||
}
|
||||
|
||||
const mouseout = () => {
|
||||
if (props.showDeleteBtn){
|
||||
showDelete.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const columnSelect = () => {
|
||||
paramsValue.termType = 'eq'
|
||||
paramsValue.value = {
|
||||
source: tabsOptions.value[0].key,
|
||||
value: undefined
|
||||
}
|
||||
emit('update:value', { ...paramsValue })
|
||||
}
|
||||
|
||||
const termsTypeSelect = () => {
|
||||
paramsValue.value = {
|
||||
source: tabsOptions.value[0].key,
|
||||
value: undefined
|
||||
}
|
||||
emit('update:value', { ...paramsValue })
|
||||
}
|
||||
|
||||
const termAdd = () => {
|
||||
const terms = {
|
||||
column: undefined,
|
||||
value: {
|
||||
source: 'fixed',
|
||||
value: undefined
|
||||
},
|
||||
termType: undefined,
|
||||
type: 'and',
|
||||
key: `params_${new Date().getTime()}`
|
||||
}
|
||||
formModel.value.branches?.[props.branchName]?.then?.[props.thenName]?.actions?.[props.name].terms?.push(terms)
|
||||
}
|
||||
|
||||
const onDelete = () => {
|
||||
formModel.value.branches?.[props.branchName]?.then?.[props.thenName]?.actions?.[props.name].terms?.splice(props.name, 1)
|
||||
}
|
||||
|
||||
nextTick(() => {
|
||||
Object.assign(paramsValue, props.value)
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
|
@ -0,0 +1,13 @@
|
|||
<template>
|
||||
|
||||
</template>
|
||||
|
||||
<script setup lang='ts' name='FilterGroup'>
|
||||
export default {
|
||||
name: 'FilterGroup'
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
|
@ -318,16 +318,26 @@
|
|||
</j-popconfirm>
|
||||
</div>
|
||||
<template v-if="!isLast && type === 'serial'">
|
||||
<div class="actions-item-filter-warp">
|
||||
<!-- filter-border -->
|
||||
满足此条件后执行后续动作
|
||||
<div :class='["actions-item-filter-warp", termsOptions.length ? "filter-border" : ""]'>
|
||||
<template v-if='termsOptions.length'>
|
||||
<div class='actions-item-filter-warp-tip'>
|
||||
满足此条件后执行后续动作
|
||||
</div>
|
||||
<div class='actions-item-filter-overflow'>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
<div v-else class='filter-add-button'>
|
||||
<AIcon type='PlusOutlined' style='padding-right: 4px;' />
|
||||
<span>添加过滤条件</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<!-- 编辑 -->
|
||||
<template v-if="visible">
|
||||
<Modal
|
||||
:name="name"
|
||||
:branchGroup="branchGroup"
|
||||
:branchGroup="thenName"
|
||||
:branchesName="branchesName"
|
||||
:data="data"
|
||||
@cancel="onClose"
|
||||
|
@ -352,7 +362,6 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { getImage } from '@/utils/comm';
|
||||
import { isBoolean } from 'lodash-es';
|
||||
import { PropType } from 'vue';
|
||||
import { ActionsType, ParallelType } from '../../../typings';
|
||||
|
@ -361,6 +370,7 @@ import ActionTypeComponent from '../Modal/ActionTypeComponent.vue';
|
|||
import TriggerAlarm from '../TriggerAlarm/index.vue';
|
||||
import { useSceneStore } from '@/store/scene';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { iconMap, itemNotifyIconMap, typeIconMap } from './util'
|
||||
|
||||
const sceneStore = useSceneStore();
|
||||
const { data: _data } = storeToRefs(sceneStore);
|
||||
|
@ -370,9 +380,9 @@ const props = defineProps({
|
|||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
branchGroup: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
thenName: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
name: {
|
||||
type: Number,
|
||||
|
@ -397,37 +407,17 @@ const props = defineProps({
|
|||
|
||||
const emit = defineEmits(['delete', 'update']);
|
||||
|
||||
const iconMap = new Map();
|
||||
iconMap.set('trigger', getImage('/scene/action-bind-icon.png'));
|
||||
iconMap.set('notify', getImage('/scene/action-notify-icon.png'));
|
||||
iconMap.set('device', getImage('/scene/action-device-icon.png'));
|
||||
iconMap.set('relieve', getImage('/scene/action-unbind-icon.png'));
|
||||
iconMap.set('delay', getImage('/scene/action-delay-icon.png'));
|
||||
|
||||
const itemNotifyIconMap = new Map();
|
||||
itemNotifyIconMap.set(
|
||||
'dingTalk',
|
||||
getImage('/scene/notify-item-img/dingtalk.png'),
|
||||
);
|
||||
itemNotifyIconMap.set('weixin', getImage('/scene/notify-item-img/weixin.png'));
|
||||
itemNotifyIconMap.set('email', getImage('/scene/notify-item-img/email.png'));
|
||||
itemNotifyIconMap.set('voice', getImage('/scene/notify-item-img/voice.png'));
|
||||
itemNotifyIconMap.set('sms', getImage('/scene/notify-item-img/sms.png'));
|
||||
itemNotifyIconMap.set(
|
||||
'webhook',
|
||||
getImage('/scene/notify-item-img/webhook.png'),
|
||||
);
|
||||
|
||||
const typeIconMap = {
|
||||
READ_PROPERTY: 'icon-zhihangdongzuodu',
|
||||
INVOKE_FUNCTION: 'icon-zhihangdongzuoxie-1',
|
||||
WRITE_PROPERTY: 'icon-zhihangdongzuoxie',
|
||||
};
|
||||
|
||||
const visible = ref<boolean>(false);
|
||||
const triggerVisible = ref<boolean>(false);
|
||||
const actionType = ref('');
|
||||
|
||||
const termsOptions = computed(() => {
|
||||
if (!props.parallel) { // 串行
|
||||
return _data.value.branches![props.branchesName].then?.[props.thenName].actions?.[props.name].terms || []
|
||||
}
|
||||
return []
|
||||
})
|
||||
|
||||
const onDelete = () => {
|
||||
emit('delete');
|
||||
};
|
||||
|
@ -463,6 +453,7 @@ const onPropsOk = (data: ActionsType, options?: any) => {
|
|||
const onPropsCancel = () => {
|
||||
actionType.value = '';
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
@ -582,6 +573,19 @@ const onPropsCancel = () => {
|
|||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.actions-item-filter-warp-tip {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 16px;
|
||||
z-index: 2;
|
||||
color: rgba(0, 0, 0, 0.55);
|
||||
font-weight: 800;
|
||||
font-size: 14px;
|
||||
line-height: 1;
|
||||
background-color: #fff;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
.actions-item-filter-overflow {
|
||||
display: flex;
|
||||
padding-top: 4px;
|
||||
|
@ -590,6 +594,13 @@ const onPropsCancel = () => {
|
|||
row-gap: 16px;
|
||||
}
|
||||
|
||||
.filter-add-button{
|
||||
width: 100%;
|
||||
color: rgba(0, 0, 0, 0.3);
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.terms-params {
|
||||
// display: inline-block;
|
||||
display: flex;
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
:parallel="parallel"
|
||||
:data="item"
|
||||
:branchesName="branchesName"
|
||||
:branchGroup="parallel ? 1 : 0"
|
||||
:thenName="thenName"
|
||||
:name="index"
|
||||
:type="type"
|
||||
:isLast="index === actions.length - 1"
|
||||
|
@ -25,7 +25,7 @@
|
|||
@cancel="onCancel"
|
||||
:parallel="parallel"
|
||||
:name="actions.length"
|
||||
:branchGroup="parallel ? 1 : 0"
|
||||
:branchGroup="thenName"
|
||||
@save="onSave"
|
||||
:branchesName="branchesName"
|
||||
/>
|
||||
|
@ -38,6 +38,11 @@ import { ActionsType, ParallelType } from '../../../typings';
|
|||
import Modal from '../Modal/index.vue';
|
||||
import Item from './Item.vue';
|
||||
import { pick } from 'lodash';
|
||||
import { useSceneStore } from '@/store/scene';
|
||||
import { storeToRefs } from 'pinia';
|
||||
|
||||
const sceneStore = useSceneStore();
|
||||
const { data: _data } = storeToRefs(sceneStore);
|
||||
|
||||
interface ListProps {
|
||||
branchesName: number;
|
||||
|
@ -47,7 +52,10 @@ interface ListProps {
|
|||
}
|
||||
|
||||
const props = defineProps({
|
||||
branchesName: Number,
|
||||
branchesName: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
type: {
|
||||
type: String as PropType<ListProps['type']>,
|
||||
default: 'serial',
|
||||
|
@ -63,6 +71,10 @@ const emit = defineEmits(['delete', 'add']);
|
|||
|
||||
const visible = ref<boolean>(false);
|
||||
|
||||
const thenName = computed(() => {
|
||||
return _data.value.branches![props.branchesName].then.findIndex(item => item.parallel === props.parallel)
|
||||
})
|
||||
|
||||
const onAdd = () => {
|
||||
visible.value = true;
|
||||
};
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
import { getImage } from '@/utils/comm'
|
||||
|
||||
export const iconMap = new Map();
|
||||
iconMap.set('trigger', getImage('/scene/action-bind-icon.png'));
|
||||
iconMap.set('notify', getImage('/scene/action-notify-icon.png'));
|
||||
iconMap.set('device', getImage('/scene/action-device-icon.png'));
|
||||
iconMap.set('relieve', getImage('/scene/action-unbind-icon.png'));
|
||||
iconMap.set('delay', getImage('/scene/action-delay-icon.png'));
|
||||
|
||||
export const itemNotifyIconMap = new Map();
|
||||
itemNotifyIconMap.set(
|
||||
'dingTalk',
|
||||
getImage('/scene/notify-item-img/dingtalk.png'),
|
||||
);
|
||||
itemNotifyIconMap.set('weixin', getImage('/scene/notify-item-img/weixin.png'));
|
||||
itemNotifyIconMap.set('email', getImage('/scene/notify-item-img/email.png'));
|
||||
itemNotifyIconMap.set('voice', getImage('/scene/notify-item-img/voice.png'));
|
||||
itemNotifyIconMap.set('sms', getImage('/scene/notify-item-img/sms.png'));
|
||||
itemNotifyIconMap.set(
|
||||
'webhook',
|
||||
getImage('/scene/notify-item-img/webhook.png'),
|
||||
);
|
||||
|
||||
export const typeIconMap = {
|
||||
READ_PROPERTY: 'icon-zhihangdongzuodu',
|
||||
INVOKE_FUNCTION: 'icon-zhihangdongzuoxie-1',
|
||||
WRITE_PROPERTY: 'icon-zhihangdongzuoxie',
|
||||
};
|
Loading…
Reference in New Issue