update: 完善场景联动

This commit is contained in:
xieyonghong 2023-03-21 19:13:14 +08:00
parent 352c4d263b
commit 1f1d81c767
21 changed files with 273 additions and 90 deletions

View File

@ -13,7 +13,7 @@
v-else-if="typeMap.get(itemType) === 'time'"
v-model:value="myValue"
allowClear
format="HH:mm:ss"
valueFormat="HH:mm:ss"
style="width: 100%"
@change='timeChange'
/>
@ -22,8 +22,7 @@
v-model:value="myValue"
allowClear
showTime
lang="cn"
format="YYYY-MM-DD HH:mm:ss"
valueFormat="YYYY-MM-DD HH:mm:ss"
style="width: 100%"
@change='dateChange'
/>

View File

@ -1,5 +1,5 @@
import { defineStore } from 'pinia'
import type { FormModelType, SceneItem } from '@/views/rule-engine/Scene/typings'
import type { FormModelType } from '@/views/rule-engine/Scene/typings'
import { detail } from '@/api/rule-engine/scene'
import { cloneDeep, isArray } from 'lodash-es'
import { randomString } from '@/utils/utils'
@ -63,7 +63,7 @@ const defaultOptions = {
{
terms: [
{
terms: [],
terms: [['','eq','','and']],
},
],
},
@ -71,7 +71,7 @@ const defaultOptions = {
};
export const useSceneStore = defineStore('scene', () => {
const data = reactive<FormModelType>({
const data = ref<FormModelType>({
trigger: { type: ''},
options: defaultOptions,
branches: defaultBranches,
@ -84,7 +84,7 @@ export const useSceneStore = defineStore('scene', () => {
const getDetail = async (id: string) => {
const resp = await detail(id)
if (resp.success) {
const result = resp.result as SceneItem
const result = resp.result as any
const triggerType = result.triggerType
let branches: any[] = result.branches
@ -105,19 +105,30 @@ export const useSceneStore = defineStore('scene', () => {
branches.push(null);
}
}
Object.assign(data, {
data.value = {
...result,
trigger: result.trigger || {},
branches: cloneDeep(assignmentKey(branches)),
options: result.options ? {...defaultOptions, ...result.options } : defaultOptions,
})
}
}
}
const refresh = () => {
data.value = {
trigger: { type: ''},
options: cloneDeep(defaultOptions),
branches: cloneDeep(defaultBranches),
description: '',
name: '',
id: undefined
}
}
return {
data,
productCache,
getDetail
getDetail,
refresh
}
})

View File

@ -5,6 +5,7 @@
:width='820'
@click='save'
@cancel='cancel'
:maskClosable="false"
>
<j-steps :current='addModel.stepNumber' @change='stepChange'>
<j-step>
@ -274,5 +275,10 @@ nextTick(() => {
</script>
<style scoped>
.steps-content {
width: 100%;
max-height: 500px;
overflow-y: auto;
overflow-x: hidden;
}
</style>

View File

@ -26,7 +26,7 @@
:status="slotProps.state?.value"
:statusText="slotProps.state?.text"
:statusNames="{
online: 'success',
online: 'processing',
offline: 'error',
notActive: 'warning',
}"

View File

@ -26,7 +26,7 @@
:active="rowKey === slotProps.id"
:status="slotProps.state"
:statusText="slotProps.state === 1 ? '正常' : '禁用'"
:statusNames="{ 1: 'success', 0: 'error', }"
:statusNames="{ 1: 'processing', 0: 'error', }"
@click="handleClick"
>
<template #img>

View File

@ -44,7 +44,7 @@
<template v-if='showInvokeFunction'>
<InvokeFunction
ref='invokeRef'
v-model:type='formModel.functionId'
v-model:functionId='formModel.functionId'
v-model:functionParameters='formModel.functionParameters'
:functions='functionOptions'
/>
@ -237,9 +237,6 @@ defineExpose({
<style scoped lang='less'>
.type {
max-height: calc(100vh - 350px);
overflow-x: hidden;
overflow-y: auto;
margin-top: 24px;
}
</style>

View File

@ -27,7 +27,7 @@ import AddButton from '../components/AddButton.vue'
import Title from '../components/Title.vue'
import Terms from '../components/Terms'
import type { TriggerDevice } from '@/views/rule-engine/Scene/typings'
import { EventEmitter, DeviceEmitterKey } from '@/views/rule-engine/Scene/Save/util'
const sceneStore = useSceneStore()
const { data } = storeToRefs(sceneStore)
@ -47,6 +47,7 @@ const save = (device: TriggerDevice, options: Record<string, any>) => {
data.value.trigger!.device = device
data.value.options!.trigger = options
visible.value = false
EventEmitter.emit(DeviceEmitterKey, device)
}
</script>

View File

@ -25,7 +25,7 @@
:active="rowKey === slotProps.id"
:status="String(slotProps.state)"
:statusText="slotProps.state === 1 ? '正常' : '禁用'"
:statusNames="{ '1': 'success', '0': 'error' }"
:statusNames="{ '1': 'processing', '0': 'error' }"
@click="handleClick(slotProps)"
>
<template #img>

View File

@ -27,7 +27,7 @@
:status="slotProps.state?.value"
:statusText="slotProps.state?.text"
:statusNames="{
online: 'success',
online: 'processing',
offline: 'error',
notActive: 'warning',
}"

View File

@ -8,6 +8,7 @@
]'
type='type'
v-model:value='paramsValue.type'
@change='typeChange'
/>
</div>
<div
@ -170,13 +171,13 @@ const tabsOptions = ref<Array<TabsOption>>(
const handOptionByColumn = (option: any) => {
if (option) {
termTypeOptions.value = option.termTypes || []
tabsOptions.value[0].component = option.dataType
if (option.dataType === 'boolean') {
tabsOptions.value[0].component = option.type
if (option.type === 'boolean') {
valueOptions.value = [
{ label: '是', value: true },
{ label: '否', value: false },
{ name: '是', id: true },
{ name: '否', id: false },
]
} else if(option.dataType === 'enum') {
} else if(option.type === 'enum') {
valueOptions.value = option.options?.map((item: any) => ({ ...item, label: item.name, value: item.id})) || []
} else{
valueOptions.value = option.options || []
@ -247,9 +248,10 @@ const columnSelect = (e: any) => {
handleOptionsColumnsValue(termsColumns, _options)
emit('update:value', { ...paramsValue })
formItemContext.onFieldChange()
formModel.value.branches![props.branchName].then[props.thenName].actions[props.actionName].options!.terms[props.termsName].terms[props.name][0] = e.name
}
const termsTypeSelect = (e: { key: string }) => {
const termsTypeSelect = (e: { key: string, name: string }) => {
const value = arrayParamsKey.includes(e.key) ? [ undefined, undefined ] : undefined
paramsValue.value = {
source: tabsOptions.value[0].key,
@ -257,11 +259,17 @@ const termsTypeSelect = (e: { key: string }) => {
}
emit('update:value', { ...paramsValue })
formItemContext.onFieldChange()
formModel.value.branches![props.branchName].then[props.thenName].actions[props.actionName].options!.terms[props.termsName].terms[props.name][1] = e.name
}
const valueSelect = () => {
const valueSelect = (_: any, label: string, labelObj: Record<number, any>) => {
emit('update:value', { ...paramsValue })
formItemContext.onFieldChange()
formModel.value.branches![props.branchName].then[props.thenName].actions[props.actionName].options!.terms[props.termsName].terms[props.name][2] = labelObj
}
const typeChange = (e: any) => {
formModel.value.branches![props.branchName].then[props.thenName].actions[props.actionName].options!.terms[props.termsName].terms[props.name][3] = e.label
}
const termAdd = () => {

View File

@ -9,6 +9,7 @@
]'
type='type'
v-model:value='formModel.branches[branchName].then[thenName].actions[actionName].terms[name].type'
@select='typeChange'
/>
</div>
<div
@ -61,7 +62,7 @@ import FilterItem from './FilterCondition.vue'
import { flattenDeep, isArray } from 'lodash-es'
import { provide } from 'vue'
import { randomString } from '@/utils/utils'
import { useParams } from '@/views/rule-engine/Scene/Save/util'
import { getParams, EventEmitter, EventSubscribeKeys } from '@/views/rule-engine/Scene/Save/util'
const sceneStore = useSceneStore()
const { data: formModel } = storeToRefs(sceneStore)
@ -97,14 +98,31 @@ const props = defineProps({
}
})
const { columnOptions } = useParams({
const columnOptions = ref<any[]>([])
const onKeys: string[] = EventSubscribeKeys({
branch: props.branchName,
branchGroup: props.thenName,
action: props.actionName
})
EventEmitter.subscribe(onKeys, (d: any) => {
columnRequest()
})
provide('filter-params', columnOptions)
const columnRequest = () => {
const param = {
branch: props.branchName,
branchGroup: props.thenName,
action: props.actionName
}
getParams(param, formModel.value).then(res => {
columnOptions.value = res
})
}
const termsOptions = computed(() => {
return formModel.value.branches![props.branchName].then[props.thenName].actions[props.actionName].terms?.[props.name].terms
})
@ -152,6 +170,15 @@ const addTerms = () => {
.then[props.thenName]
.actions[props.actionName]
.terms?.push(item)
formModel.value.branches![props.branchName].then[props.thenName].actions[props.actionName].options!.terms.push({
terms: [['','eq','','and']],
termType: '并且'
})
}
const typeChange = (e: any) => {
formModel.value.branches![props.branchName].then[props.thenName].actions[props.actionName].options!.terms[props.name].termType = e.label
}
const onDelete = () => {
@ -167,6 +194,8 @@ const onDelete = () => {
termsColumns.splice(props.name, 1)
handleOptionsColumnsValue(termsColumns, _options)
}
formModel.value.branches![props.branchName].then[props.thenName].actions[props.actionName].options!.terms.splice(props.name, 1)
}
const rules = [
@ -204,11 +233,9 @@ const rules = [
}
]
// watchEffect(() => {
// if (formModel.value.branches![props.branchName].then[props.thenName].actions[props.actionName]) {
// getParams()
// }
// })
nextTick(() => {
columnRequest()
})
</script>

View File

@ -398,6 +398,7 @@ import { storeToRefs } from 'pinia';
import { iconMap, itemNotifyIconMap, typeIconMap } from './util';
import FilterGroup from './FilterGroup.vue';
import { randomString } from '@/utils/utils'
import { EventEmitter, EventEmitterKeys } from '@/views/rule-engine/Scene/Save/util'
const sceneStore = useSceneStore();
const { data: _data } = storeToRefs(sceneStore);
@ -437,6 +438,11 @@ const emit = defineEmits(['delete', 'update']);
const visible = ref<boolean>(false);
const triggerVisible = ref<boolean>(false);
const actionType = ref('');
const eventEmitterKey = EventEmitterKeys({
branch: props.branchesName,
branchGroup: props.thenName,
action: props.name
})
const termsOptions = computed(() => {
if (!props.parallel) {
@ -462,19 +468,9 @@ const onClose = () => {
visible.value = false;
};
const onSave = (data: ActionsType, options: any) => {
const { key, terms } = _data.value.branches![props.branchesName].then?.[props.thenName].actions?.[props.name]
const actionItem: ActionsType = {
...data,
options,
key,
terms
}
_data.value.branches![props.branchesName].then[props.thenName].actions.splice(props.name, 1, actionItem)
visible.value = false;
};
/**
* 添加过滤条件
*/
const addFilterParams = () => {
const item: any = {
type: 'and',
@ -497,6 +493,10 @@ const addFilterParams = () => {
} else {
_data.value.branches![props.branchesName].then[props.thenName].actions[props.name].terms = [item]
}
_data.value.branches![props.branchesName].then[props.thenName].actions[props.name].options!.terms = [{
terms: [['','eq','','and']],
termType: '并且'
}]
}
const onAdd = () => {
@ -507,26 +507,42 @@ const onType = (_type: string) => {
actionType.value = _type;
};
/**
* 添加执行动作
* @param data
* @param options
*/
const onSave = (data: ActionsType, options: any) => {
const { key, terms } = _data.value.branches![props.branchesName].then?.[props.thenName].actions?.[props.name]
const actionItem: ActionsType = {
...data,
options,
key,
terms
}
_data.value.branches![props.branchesName].then[props.thenName].actions.splice(props.name, 1, actionItem)
visible.value = false;
if (props.parallel === false) { //
EventEmitter.emit(eventEmitterKey, data)
}
};
/**
* 直接编辑执行栋数据
* @param data
* @param options
*/
const onPropsOk = (data: ActionsType, options?: any) => {
emit('update', data, options);
// setTimeout(() => {
// getParams();
// }, 10);
actionType.value = '';
onSave(data, options);
actionType.value = '';
};
const onPropsCancel = () => {
actionType.value = '';
};
watch(
() => props.data,
() => {
if (props.data) {
}
},
{ immediate: true, deep: true },
);
</script>
<style lang="less" scoped>

View File

@ -1,7 +1,7 @@
<template>
<j-table
model='TABLE'
:noPagination='true'
:pagination='false'
:data-source='dataSource.value'
:columns='columns'
:bodyStyle='{ padding: 0}'
@ -33,6 +33,7 @@
v-model:modelValue='record.value'
:itemType="record.type"
:options="handleOptions(record)"
@change='valueChange'
/>
</template>
</template>
@ -41,6 +42,7 @@
<script setup lang='ts' name='FunctionCall'>
import type { PropType } from 'vue'
import { debounce } from 'lodash-es'
type Emit = {
(e: 'change', data: Array<{ name: string, value: any}>): void
@ -95,20 +97,20 @@ const handleOptions = (record: any) => {
}
}
watch(() => props.data, () => {
dataSource.value = props.data.map((item: any) => {
const oldValue = props.value.find((oldItem: any) => oldItem.name === item.id)
return oldValue ? { ...item, value: oldValue.value } : item
})
}, { immediate: true })
watch(() => dataSource.value, () => {
const valueChange = debounce(() => {
const _value = dataSource.value.map(item => ({
name: item.id, value: item.value
}))
emit('change', _value)
emit('update:value', _value)
}, { deep: true })
}, 500)
watch(() => props.data, () => {
dataSource.value = props.data.map((item: any) => {
const oldValue = props.value.find((oldItem: any) => oldItem.name === item.id)
return oldValue ? { ...item, value: oldValue.value } : item
})
}, { immediate: true, deep: true })
</script>

View File

@ -9,7 +9,8 @@
:placeholder='placeholder'
:tabs-options='tabsOptions'
:metricOptions='metricOptions'
@select='onSelect'
@select='(v, l) => onSelect(v, l, 0)'
@tabChange='tabChange'
/>
<ParamsDropdown
v-model:value='myValue[1]'
@ -21,7 +22,8 @@
:tabs-options='tabsOptions'
:metricOptions='metricOptions'
:options='options'
@select='onSelect'
@select='(v, l) => onSelect(v, l,1)'
@tabChange='tabChange'
/>
</template>
@ -32,22 +34,32 @@ import { defaultSetting, ValueType } from './typings'
type Emit = {
(e: 'update:value', data: ValueType): void
(e: 'update:source', data: string): void
(e: 'select', data: any): void
(e: 'select', data: any, label?: string, labelObj?: Record<number, any>): void
}
const props = defineProps({
...defaultSetting
})
const label: Record<number, any> = {
0: undefined,
1: undefined
}
const emit = defineEmits<Emit>()
const myValue = ref<ValueType>(props.value)
const mySource = ref<string>(props.source)
const onSelect = () => {
const onSelect = (v: any, _label: string, index: number) => {
emit('update:value', myValue.value)
label[index] = _label
emit('select', myValue.value, _label, label)
}
const tabChange = (e: string) => {
emit('update:source', e)
}
</script>
<style scoped>

View File

@ -88,7 +88,7 @@ import { getOption } from '../DropdownButton/util'
type Emit = {
(e: 'update:value', data: ValueType): void
(e: 'update:source', data: string): void
(e: 'select', data: any): void
(e: 'select', data: any, label?: string, labelObj?: Record<number, any>): void
(e: 'tabChange', data: any): void
}
@ -113,7 +113,8 @@ const tabsChange = (e: string) => {
myValue.value = undefined
emit('update:source', mySource.value)
emit('update:value', undefined)
emit('select', {})
emit('tabChange', e)
emit('select', {}, '', { 0 : undefined })
}
const treeSelect = (v: any, option: any) => {
@ -121,27 +122,27 @@ const treeSelect = (v: any, option: any) => {
visible.value = false
label.value = node.fullname || node.name
emit('update:value', node[props.valueName])
emit('select', node)
emit('select', node, label.value, { 0: label.value })
}
const valueItemChange = (e: string) => {
label.value = e
emit('update:value', e)
emit('select', e)
emit('select', e, label.value, { 0: label.value })
}
const onSelect = (e: string, option: any) => {
visible.value = false
label.value = option.label
emit('update:value', e)
emit('select', e)
emit('select', e, label.value, { 0: label.value })
}
const timeChange = (e: any) => {
label.value = e
visible.value = false
emit('update:value', e)
emit('select', e)
emit('select', e, label.value, { 0: label.value })
}
const visibleChange = (v: boolean) => {

View File

@ -22,7 +22,7 @@
placement="topRight"
@confirm='onDeleteAll'
>
<div class='terms-params-delete' v-show='showDelete'>
<div class='terms-params-delete' v-show='showDelete && whenData.length'>
<AIcon type='CloseOutlined' />
</div>
</j-popconfirm>
@ -110,11 +110,14 @@ const whenData = computed(() => {
})
const onDelete = () => {
FormModel.value.branches?.splice(props.name, 1)
}
const onDeleteAll = () => {
if (FormModel.value.branches) {
FormModel.value.branches.length = props.name
FormModel.value.branches.push(null as any)
}
}
const mouseover = () => {

View File

@ -8,6 +8,7 @@
]'
type='type'
v-model:value='paramsValue.type'
@select='typeSelect'
/>
</div>
<div
@ -223,7 +224,7 @@ const mouseout = () => {
}
}
const columnSelect = () => {
const columnSelect = (option: any) => {
paramsValue.termType = 'eq'
paramsValue.value = {
source: tabsOptions.value[0].key,
@ -231,9 +232,11 @@ const columnSelect = () => {
}
emit('update:value', { ...paramsValue })
formItemContext.onFieldChange()
formModel.value.options!.when[props.whenName].terms[props.termsName].terms[props.name][0] = option.name
formModel.value.options!.when[props.whenName].terms[props.termsName].terms[props.name][1] = paramsValue.termType
}
const termsTypeSelect = (e: { key: string }) => {
const termsTypeSelect = (e: { key: string, name: string }) => {
const value = arrayParamsKey.includes(e.key) ? [ undefined, undefined ] : undefined
paramsValue.value = {
source: tabsOptions.value[0].key,
@ -241,10 +244,16 @@ const termsTypeSelect = (e: { key: string }) => {
}
emit('update:value', { ...paramsValue })
formItemContext.onFieldChange()
formModel.value.options!.when[props.whenName].terms[props.termsName].terms[props.name][1] = e.name
}
const valueSelect = () => {
const valueSelect = (_: any, label: string, labelObj: Record<number, any>) => {
formItemContext.onFieldChange()
formModel.value.options!.when[props.whenName].terms[props.termsName].terms[props.name][2] = labelObj
}
const typeSelect = (e: any) => {
formModel.value.options!.when[props.whenName].terms[props.termsName].terms[props.name][3] = e.label
}
const termAdd = () => {
@ -259,10 +268,12 @@ const termAdd = () => {
key: `params_${new Date().getTime()}`
}
formModel.value.branches?.[props.branchName]?.when?.[props.whenName]?.terms?.push(terms)
formModel.value.options!.when[props.whenName].terms[props.termsName].terms[props.name].push(['', '', '', '并且'])
}
const onDelete = () => {
formModel.value.branches?.[props.branchName]?.when?.[props.whenName]?.terms?.splice(props.name, 1)
formModel.value.options!.when[props.whenName].terms[props.termsName].terms.splice(props.name, 1)
}
nextTick(() => {

View File

@ -63,7 +63,6 @@ const { data } = storeToRefs(sceneStore)
const open = ref(false)
const columnOptions = ref<any>([])
provide(ContextKey, columnOptions)
const change = (e: boolean) => {
@ -97,6 +96,7 @@ const addBranches = () => {
}
const lastIndex = data.value.branches!.length - 1 || 0
data.value.branches?.splice(lastIndex, 1, branchesItem)
data.value.options!.when = []
}
const branchesDelete = (index: number) => {

View File

@ -9,6 +9,7 @@
]'
type='type'
v-model:value='formModel.branches[branchName].when[whenName].type'
@select='typeChange'
/>
</div>
<div
@ -159,6 +160,11 @@ const mouseout = () => {
const onDelete = () => {
formModel.value.branches?.[props.branchName]?.when?.splice(props.name, 1)
formModel.value.options!.when[props.whenName].terms.splice(props.name, 1)
}
const typeChange = (e: any) => {
formModel.value.options!.when[props.whenName].terms[props.name].termType = e.label
}
const addTerms = () => {
@ -179,6 +185,9 @@ const addTerms = () => {
key: `terms_${new Date().getTime()}`
}
formModel.value.branches?.[props.branchName]?.when?.push(terms)
formModel.value.options!.when[props.whenName].push({
terms: [{ termType: '并且', terms: [['','eq','','and']]}]
})
}
</script>

View File

@ -55,7 +55,7 @@ import { message } from 'jetlinks-ui-components'
const sceneStore = useSceneStore()
const menuStore = useMenuStore()
const { data } = storeToRefs(sceneStore)
const { getDetail } = sceneStore
const { getDetail, refresh } = sceneStore
const route = useRoute();
const sceneForm = ref()
@ -76,6 +76,11 @@ const save = async () => {
getDetail(route.query.id as string)
onUnmounted(() => {
console.log('scene-onUnmounted')
refresh?.()
})
</script>
<style scoped lang='less'>

View File

@ -3,6 +3,7 @@ import { handleParamsData } from './components/Terms/util'
import { useSceneStore } from 'store/scene'
import { storeToRefs } from 'pinia'
import type { FormModelType } from '@/views/rule-engine/Scene/typings'
import { isArray } from 'lodash-es'
interface Params {
branch: number
@ -47,3 +48,77 @@ export const useParams = (params: Params) => {
columnOptions
}
}
export const DeviceEmitterKey = 'device_rules'
/**
* keys
* @param params
* @constructor
*/
export const EventEmitterKeys = (params: Params): string => {
const _b = `branches_${params.branch}` // branchesName
const _t = `then_${params.branchGroup}` // thenName
const _a = `then_${params.action}` // actionName
return `${_b}_${_t}_${_a}`
}
/**
* keys
* @param params
* @constructor
*/
export const EventSubscribeKeys = (params: Params): string[] => {
let keys: string[] = []
if (params.action === 0) {
keys.push(DeviceEmitterKey)
}
for (let i = 0; i <= params.action; i++) {
const _b = `branches_${params.branch}` // branchesName
const _t = `then_${params.branchGroup}` // thenName
const _a = `then_${i}` // actionName
keys.push(`${_b}_${_t}_${_a}`)
}
return keys
}
export const EventEmitter = {
list: {},
subscribe: function(events: string[], fn: Function) {
const list = this.list
events.forEach(event => {
(list[event] || (list[event] = [])).push(fn)
})
return this
},
emit: function(events:string, data?: any) {
const list = this.list
const fns: Function[] = list[events] ? [...list[events]] : []
if (!fns.length) return false;
fns.forEach(fn => {
fn(data)
})
return this
},
unSubscribe: function(events:string[], fn: Function) {
const list = this.list
events.forEach(key => {
if (key in list) {
const fns = list[key]
for (let i = 0; i < fns.length; i++) {
if (fns[i] === fn) {
fns.splice(i, 1)
break;
}
}
}
})
return this
}
}