fix: 物模型属性开发
This commit is contained in:
parent
11e9d6faf9
commit
0b090cf818
|
@ -166,3 +166,13 @@ export const saveDevice = (data:any) => server.post('/device-product',data)
|
|||
* 更新选择设备(设备接入)
|
||||
*/
|
||||
export const updateDevice = (data:any) => server.patch('/device-product',data)
|
||||
|
||||
/**
|
||||
* 获取操作符
|
||||
*/
|
||||
export const getOperator = () => server.get<OperatorItem>('/property-calculate-rule/description')
|
||||
|
||||
/**
|
||||
* 获取聚合函数列表
|
||||
*/
|
||||
export const getStreamingAggType = () => server.get<Record<string, string>[]>('/dictionary/streaming-agg-type/items')
|
|
@ -3,6 +3,7 @@
|
|||
<div class="advance-box">
|
||||
<div class="left">
|
||||
<Editor
|
||||
ref="editor"
|
||||
mode="advance"
|
||||
key="advance"
|
||||
v-model:value="_value"
|
||||
|
@ -16,7 +17,7 @@
|
|||
/>
|
||||
</div>
|
||||
<div class="right">
|
||||
<Operator :id="id" />
|
||||
<Operator :id="id" @add-operator-value="addOperatorValue"/>
|
||||
</div>
|
||||
</div>
|
||||
</a-modal>
|
||||
|
@ -44,10 +45,15 @@ const handleCancel = () => {
|
|||
emit('change', 'simple')
|
||||
}
|
||||
const handleOk = () => {
|
||||
console.log(_value.value)
|
||||
emit('update:value', _value.value)
|
||||
emit('change', 'simple')
|
||||
}
|
||||
|
||||
const editor = ref()
|
||||
const addOperatorValue = (val: string) => {
|
||||
editor.value.addOperatorValue(val)
|
||||
}
|
||||
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<div class="top">
|
||||
<div class="left">
|
||||
<span v-for="item in symbolList.filter((t: SymbolType, i: number) => i <= 3)" :key="item.key"
|
||||
@click="handleInsertCode(item.value)">
|
||||
@click="addOperatorValue(item.value)">
|
||||
{{ item.value }}
|
||||
</span>
|
||||
<span>
|
||||
|
@ -12,7 +12,7 @@
|
|||
<template #overlay>
|
||||
<a-menu>
|
||||
<a-menu-item v-for="item in symbolList.filter((t: SymbolType, i: number) => i > 6)" :key="item.key"
|
||||
@click="handleInsertCode(item.value)">
|
||||
@click="addOperatorValue(item.value)">
|
||||
{{ item.value }}
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
|
@ -149,7 +149,7 @@ onMounted(() => {
|
|||
}, 100);
|
||||
})
|
||||
|
||||
const handleInsertCode = (val: string) => {
|
||||
const addOperatorValue = (val: string) => {
|
||||
editor.value?.insert(val)
|
||||
}
|
||||
|
||||
|
@ -159,6 +159,10 @@ const fullscreenClick = () => {
|
|||
}
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
addOperatorValue
|
||||
})
|
||||
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.editor-box {
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
<template>
|
||||
<div class="operator-box">
|
||||
<a-input-search @search="search" allow-clear placeholder="搜索关键字" />
|
||||
<a-tree class="tree" @select="selectTree" :field-names="{ title: 'name', key: 'id', }" auto-expand-parent
|
||||
<div class="tree">
|
||||
<a-tree @select="selectTree" :field-names="{ title: 'name', key: 'id', }" auto-expand-parent
|
||||
:tree-data="data">
|
||||
<template #title="node">
|
||||
<div class="node">
|
||||
<div>{{ node.name }}</div>
|
||||
<div :class="node.children?.length > 0 ? 'parent' : 'add'">
|
||||
<a-popover v-if="node.type === 'property'" placement="right" title="请选择使用值" @visibleChange="setVisible">
|
||||
<a-popover v-if="node.type === 'property'" placement="right" title="请选择使用值">
|
||||
<template #content>
|
||||
<a-space direction="vertical">
|
||||
<a-tooltip placement="right" title="实时值为空时获取上一有效值补齐,实时值不为空则使用实时值">
|
||||
|
@ -22,7 +23,7 @@
|
|||
</a-tooltip>
|
||||
</a-space>
|
||||
</template>
|
||||
<a @click="setVisible(true)">添加</a>
|
||||
<a>添加</a>
|
||||
</a-popover>
|
||||
|
||||
<a v-else @click="addClick(node)">
|
||||
|
@ -32,20 +33,32 @@
|
|||
</div>
|
||||
</template>
|
||||
</a-tree>
|
||||
</div>
|
||||
<div class="explain">
|
||||
<ReactMarkdown>{{ item?.description || '' }}</ReactMarkdown>
|
||||
<Markdown :source="item?.description || ''"></Markdown>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts" name="Operator">
|
||||
import { useProductStore } from '@/store/product';
|
||||
import type { OperatorItem } from './typings';
|
||||
import { treeFilter } from '@/utils/tree'
|
||||
import { Store } from 'jetlinks-store';
|
||||
import { PropertyMetadata } from '@/views/device/Product/typings';
|
||||
import { getOperator } from '@/api/device/product'
|
||||
import Markdown from 'vue3-markdown-it'
|
||||
|
||||
const props = defineProps({
|
||||
id: String
|
||||
})
|
||||
|
||||
interface Emits {
|
||||
(e: 'addOperatorValue', data: string): void;
|
||||
}
|
||||
const emit = defineEmits<Emits>();
|
||||
|
||||
const item = ref<Partial<OperatorItem>>()
|
||||
const data = ref<OperatorItem[]>([])
|
||||
const dataRef = ref<OperatorItem[]>([])
|
||||
const visible = ref(false)
|
||||
|
||||
const search = (value: string) => {
|
||||
if (value) {
|
||||
|
@ -60,22 +73,52 @@ const selectTree = (k: any, info: any) => {
|
|||
item.value = info.node as unknown as OperatorItem;
|
||||
}
|
||||
|
||||
const setVisible = (_visible: boolean) => {
|
||||
visible.value = !!visible
|
||||
}
|
||||
|
||||
const recentClick = (node: OperatorItem) => {
|
||||
Store.set('add-operator-value', `$recent("${node.id}")`);
|
||||
setVisible(!visible.value);
|
||||
emit('addOperatorValue', `$recent("${node.id}")`)
|
||||
}
|
||||
const lastClick = (node: OperatorItem) => {
|
||||
Store.set('add-operator-value', `$lastState("${node.id}")`);
|
||||
setVisible(!visible.value);
|
||||
emit('addOperatorValue', `$lastState("${node.id}")`)
|
||||
}
|
||||
const addClick = (node: OperatorItem) => {
|
||||
Store.set('add-operator-value', node.code);
|
||||
setVisible(true);
|
||||
emit('addOperatorValue', node.code)
|
||||
}
|
||||
|
||||
const productStore = useProductStore()
|
||||
|
||||
const getData = async (id?: string) => {
|
||||
const metadata = productStore.current.metadata || '{}';
|
||||
console.log('metadata', metadata)
|
||||
const _properties = JSON.parse(metadata).properties || [] as PropertyMetadata[]
|
||||
const properties = {
|
||||
id: 'property',
|
||||
name: '属性',
|
||||
description: '',
|
||||
code: '',
|
||||
children: _properties
|
||||
.filter((p: PropertyMetadata) => p.id !== id)
|
||||
.map((p: PropertyMetadata) => ({
|
||||
id: p.id,
|
||||
name: p.name,
|
||||
description: `### ${p.name}
|
||||
\n 数据类型: ${p.valueType?.type}
|
||||
\n 是否只读: ${p.expands?.readOnly || 'false'}
|
||||
\n 可写数值范围: `,
|
||||
type: 'property',
|
||||
})),
|
||||
};
|
||||
const response = await getOperator();
|
||||
if (response.status === 200) {
|
||||
data.value = [properties, ...response.result];
|
||||
dataRef.value = [properties, ...response.result];
|
||||
}
|
||||
};
|
||||
|
||||
watch(() => props.id,
|
||||
(val) => {
|
||||
getData(val)
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.border {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<Editor key="simple" @change="change" v-model:value="_value" :id="id" />
|
||||
{{ ruleEditorStore.state.model }}
|
||||
<Advance v-if="ruleEditorStore.state.model === 'advance'" :model="ruleEditorStore.state.model"
|
||||
{{ _value }}
|
||||
<Advance v-if="ruleEditorStore.state.model === 'advance'" v-model:value="_value" :model="ruleEditorStore.state.model"
|
||||
:virtualRule="virtualRule" :id="id" @change="change" />
|
||||
</template>
|
||||
<script setup lang="ts" name="FRuleEditor">
|
||||
|
|
|
@ -87,7 +87,7 @@ const handleEdit = (index: number) => {
|
|||
}
|
||||
const handleDelete = (index: number) => {
|
||||
editIndex.value = -1
|
||||
_value.value.slice(index, 1)
|
||||
_value.value.splice(index, 1)
|
||||
}
|
||||
const handleClose = () => {
|
||||
editIndex.value = -1
|
||||
|
|
|
@ -2,10 +2,39 @@
|
|||
<a-form-item :name="name.concat(['script'])">
|
||||
<f-rule-editor v-model:value="value.script" :id="id"></f-rule-editor>
|
||||
</a-form-item>
|
||||
<template v-if="showWindow">
|
||||
<a-form-item label="规则配置" :name="name.concat(['isVirtualRule'])">
|
||||
<a-switch v-model:checked="value.isVirtualRule" :checked-value="true" :un-checked-value="false"
|
||||
@change="changeWindow"></a-switch>
|
||||
</a-form-item>
|
||||
<template v-if="value.isVirtualRule">
|
||||
<a-form-item label="窗口" :name="name.concat(['windowType'])" :rules="[
|
||||
{ required: true, message: '请选择窗口' },
|
||||
]">
|
||||
<a-select v-model:value="value.windowType" :options="windowTypeEnum" size="small" allow-clear></a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label="聚合函数" :name="name.concat(['aggType'])" :rules="[
|
||||
{ required: true, message: '请选择聚合函数' },
|
||||
]">
|
||||
<a-select v-model:value="value.aggType" :options="aggTypeOptions" size="small" allow-clear></a-select>
|
||||
</a-form-item>
|
||||
<a-form-item :label="spanLabel" :name="name.concat(['window', 'span'])" :rules="[
|
||||
{ required: true, message: '请输入窗口长度' },
|
||||
]">
|
||||
<a-input-number v-model:value="value.window.span" size="small" style="width: 100%;"></a-input-number>
|
||||
</a-form-item>
|
||||
<a-form-item :label="everyLabel" :name="name.concat(['window', 'every'])" :rules="[
|
||||
{ required: true, message: '请输入步长' },
|
||||
]">
|
||||
<a-input-number v-model:value="value.window.every" size="small" style="width: 100%;"></a-input-number>
|
||||
</a-form-item>
|
||||
</template>
|
||||
</template>
|
||||
</template>
|
||||
<script setup lang="ts" name="VirtualRuleParam">
|
||||
import { PropType } from 'vue';
|
||||
import FRuleEditor from '@/components/FRuleEditor/index.vue'
|
||||
import { getStreamingAggType } from '@/api/device/product'
|
||||
|
||||
const props = defineProps({
|
||||
value: {
|
||||
|
@ -18,7 +47,11 @@ const props = defineProps({
|
|||
type: Array as PropType<string[]>,
|
||||
default: () => ([])
|
||||
},
|
||||
id: String
|
||||
id: String,
|
||||
showWindow: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
|
||||
interface Emits {
|
||||
|
@ -32,5 +65,54 @@ onMounted(() => {
|
|||
type: 'script'
|
||||
})
|
||||
})
|
||||
|
||||
const aggTypeOptions = ref()
|
||||
const getAggTypeList = async () => {
|
||||
aggTypeOptions.value = await getStreamingAggType().then((resp) =>
|
||||
resp.result.map((item: any) => ({
|
||||
label: `${item.value}(${item.text})`,
|
||||
value: item.value,
|
||||
})),
|
||||
);
|
||||
}
|
||||
getAggTypeList()
|
||||
|
||||
const changeWindow = (val: boolean | string | number) => {
|
||||
if (val as boolean) {
|
||||
props.value.type = 'window'
|
||||
if (!props.value.window) {
|
||||
props.value.window = {}
|
||||
}
|
||||
} else {
|
||||
delete props.value.type
|
||||
}
|
||||
}
|
||||
|
||||
const windowTypeEnum = [
|
||||
{ label: '时间窗口', value: 'time' },
|
||||
{ label: '次数窗口', value: 'num' },
|
||||
]
|
||||
|
||||
const spanLabel = computed(() => {
|
||||
switch(props.value.windowType) {
|
||||
case 'time':
|
||||
return '窗口长度(秒)';
|
||||
case 'num':
|
||||
return '窗口长度(次)';
|
||||
default:
|
||||
return '窗口长度'
|
||||
}
|
||||
})
|
||||
|
||||
const everyLabel = computed(() => {
|
||||
switch(props.value.windowType) {
|
||||
case 'time':
|
||||
return '步长(秒)';
|
||||
case 'num':
|
||||
return '步长(次)';
|
||||
default:
|
||||
return '步长'
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<style lang="less" scoped></style>
|
|
@ -4,7 +4,7 @@
|
|||
]">
|
||||
<a-select v-model:value="_value.source" :options="PropertySource" size="small" :disabled="metadataStore.model.action === 'edit'"></a-select>
|
||||
</a-form-item>
|
||||
<virtual-rule-param v-if="_value.source === 'rule'" v-model:value="_value.virtualRule" :name="name.concat(['virtualRule'])" :id="id"></virtual-rule-param>
|
||||
<virtual-rule-param v-if="_value.source === 'rule'" v-model:value="_value.virtualRule" :name="name.concat(['virtualRule'])" :id="id" :showWindow="_value.source === 'rule'"></virtual-rule-param>
|
||||
<a-form-item label="读写类型" :name="name.concat(['type'])" :rules="[
|
||||
{ required: true, message: '请选择读写类型' },
|
||||
]">
|
||||
|
|
Loading…
Reference in New Issue