fix: 物模型属性开发

This commit is contained in:
wangshuaiswim 2023-02-23 20:02:19 +08:00
parent 11e9d6faf9
commit 0b090cf818
8 changed files with 197 additions and 52 deletions

View File

@ -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 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')

View File

@ -3,6 +3,7 @@
<div class="advance-box"> <div class="advance-box">
<div class="left"> <div class="left">
<Editor <Editor
ref="editor"
mode="advance" mode="advance"
key="advance" key="advance"
v-model:value="_value" v-model:value="_value"
@ -16,7 +17,7 @@
/> />
</div> </div>
<div class="right"> <div class="right">
<Operator :id="id" /> <Operator :id="id" @add-operator-value="addOperatorValue"/>
</div> </div>
</div> </div>
</a-modal> </a-modal>
@ -44,10 +45,15 @@ const handleCancel = () => {
emit('change', 'simple') emit('change', 'simple')
} }
const handleOk = () => { const handleOk = () => {
console.log(_value.value)
emit('update:value', _value.value) emit('update:value', _value.value)
emit('change', 'simple') emit('change', 'simple')
} }
const editor = ref()
const addOperatorValue = (val: string) => {
editor.value.addOperatorValue(val)
}
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>

View File

@ -3,7 +3,7 @@
<div class="top"> <div class="top">
<div class="left"> <div class="left">
<span v-for="item in symbolList.filter((t: SymbolType, i: number) => i <= 3)" :key="item.key" <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 }} {{ item.value }}
</span> </span>
<span> <span>
@ -12,7 +12,7 @@
<template #overlay> <template #overlay>
<a-menu> <a-menu>
<a-menu-item v-for="item in symbolList.filter((t: SymbolType, i: number) => i > 6)" :key="item.key" <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 }} {{ item.value }}
</a-menu-item> </a-menu-item>
</a-menu> </a-menu>
@ -149,7 +149,7 @@ onMounted(() => {
}, 100); }, 100);
}) })
const handleInsertCode = (val: string) => { const addOperatorValue = (val: string) => {
editor.value?.insert(val) editor.value?.insert(val)
} }
@ -159,6 +159,10 @@ const fullscreenClick = () => {
} }
} }
defineExpose({
addOperatorValue
})
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
.editor-box { .editor-box {

View File

@ -1,51 +1,64 @@
<template> <template>
<div class="operator-box"> <div class="operator-box">
<a-input-search @search="search" allow-clear placeholder="搜索关键字" /> <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">
:tree-data="data"> <a-tree @select="selectTree" :field-names="{ title: 'name', key: 'id', }" auto-expand-parent
<template #title="node"> :tree-data="data">
<div class="node"> <template #title="node">
<div>{{ node.name }}</div> <div class="node">
<div :class="node.children?.length > 0 ? 'parent' : 'add'"> <div>{{ node.name }}</div>
<a-popover v-if="node.type === 'property'" placement="right" title="请选择使用值" @visibleChange="setVisible"> <div :class="node.children?.length > 0 ? 'parent' : 'add'">
<template #content> <a-popover v-if="node.type === 'property'" placement="right" title="请选择使用值">
<a-space direction="vertical"> <template #content>
<a-tooltip placement="right" title="实时值为空时获取上一有效值补齐,实时值不为空则使用实时值"> <a-space direction="vertical">
<a-button type="text" @click="recentClick(node)"> <a-tooltip placement="right" title="实时值为空时获取上一有效值补齐,实时值不为空则使用实时值">
$recent实时值 <a-button type="text" @click="recentClick(node)">
</a-button> $recent实时值
</a-tooltip> </a-button>
<a-tooltip placement="right" title="实时值的上一有效值"> </a-tooltip>
<a-button @click="lastClick(node)" type="text"> <a-tooltip placement="right" title="实时值的上一有效值">
上一值 <a-button @click="lastClick(node)" type="text">
</a-button> 上一值
</a-tooltip> </a-button>
</a-space> </a-tooltip>
</template> </a-space>
<a @click="setVisible(true)">添加</a> </template>
</a-popover> <a>添加</a>
</a-popover>
<a v-else @click="addClick(node)"> <a v-else @click="addClick(node)">
添加 添加
</a> </a>
</div>
</div> </div>
</div> </template>
</template> </a-tree>
</a-tree> </div>
<div class="explain"> <div class="explain">
<ReactMarkdown>{{ item?.description || '' }}</ReactMarkdown> <Markdown :source="item?.description || ''"></Markdown>
</div> </div>
</div> </div>
</template> </template>
<script setup lang="ts" name="Operator"> <script setup lang="ts" name="Operator">
import { useProductStore } from '@/store/product';
import type { OperatorItem } from './typings'; import type { OperatorItem } from './typings';
import { treeFilter } from '@/utils/tree' 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 item = ref<Partial<OperatorItem>>()
const data = ref<OperatorItem[]>([]) const data = ref<OperatorItem[]>([])
const dataRef = ref<OperatorItem[]>([]) const dataRef = ref<OperatorItem[]>([])
const visible = ref(false)
const search = (value: string) => { const search = (value: string) => {
if (value) { if (value) {
@ -60,22 +73,52 @@ const selectTree = (k: any, info: any) => {
item.value = info.node as unknown as OperatorItem; item.value = info.node as unknown as OperatorItem;
} }
const setVisible = (_visible: boolean) => {
visible.value = !!visible
}
const recentClick = (node: OperatorItem) => { const recentClick = (node: OperatorItem) => {
Store.set('add-operator-value', `$recent("${node.id}")`); emit('addOperatorValue', `$recent("${node.id}")`)
setVisible(!visible.value);
} }
const lastClick = (node: OperatorItem) => { const lastClick = (node: OperatorItem) => {
Store.set('add-operator-value', `$lastState("${node.id}")`); emit('addOperatorValue', `$lastState("${node.id}")`)
setVisible(!visible.value);
} }
const addClick = (node: OperatorItem) => { const addClick = (node: OperatorItem) => {
Store.set('add-operator-value', node.code); emit('addOperatorValue', node.code)
setVisible(true);
} }
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> </script>
<style lang="less" scoped> <style lang="less" scoped>
.border { .border {

View File

@ -1,7 +1,7 @@
<template> <template>
<Editor key="simple" @change="change" v-model:value="_value" :id="id" /> <Editor key="simple" @change="change" v-model:value="_value" :id="id" />
{{ ruleEditorStore.state.model }} {{ _value }}
<Advance v-if="ruleEditorStore.state.model === 'advance'" :model="ruleEditorStore.state.model" <Advance v-if="ruleEditorStore.state.model === 'advance'" v-model:value="_value" :model="ruleEditorStore.state.model"
:virtualRule="virtualRule" :id="id" @change="change" /> :virtualRule="virtualRule" :id="id" @change="change" />
</template> </template>
<script setup lang="ts" name="FRuleEditor"> <script setup lang="ts" name="FRuleEditor">

View File

@ -87,7 +87,7 @@ const handleEdit = (index: number) => {
} }
const handleDelete = (index: number) => { const handleDelete = (index: number) => {
editIndex.value = -1 editIndex.value = -1
_value.value.slice(index, 1) _value.value.splice(index, 1)
} }
const handleClose = () => { const handleClose = () => {
editIndex.value = -1 editIndex.value = -1

View File

@ -1,11 +1,40 @@
<template> <template>
<a-form-item :name="name.concat(['script'])"> <a-form-item :name="name.concat(['script'])">
<f-rule-editor v-model:value="value.script" :id="id" ></f-rule-editor> <f-rule-editor v-model:value="value.script" :id="id"></f-rule-editor>
</a-form-item> </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> </template>
<script setup lang="ts" name="VirtualRuleParam"> <script setup lang="ts" name="VirtualRuleParam">
import { PropType } from 'vue'; import { PropType } from 'vue';
import FRuleEditor from '@/components/FRuleEditor/index.vue' import FRuleEditor from '@/components/FRuleEditor/index.vue'
import { getStreamingAggType } from '@/api/device/product'
const props = defineProps({ const props = defineProps({
value: { value: {
@ -18,7 +47,11 @@ const props = defineProps({
type: Array as PropType<string[]>, type: Array as PropType<string[]>,
default: () => ([]) default: () => ([])
}, },
id: String id: String,
showWindow: {
type: Boolean,
default: false
}
}) })
interface Emits { interface Emits {
@ -32,5 +65,54 @@ onMounted(() => {
type: 'script' 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> </script>
<style lang="less" scoped></style> <style lang="less" scoped></style>

View File

@ -4,7 +4,7 @@
]"> ]">
<a-select v-model:value="_value.source" :options="PropertySource" size="small" :disabled="metadataStore.model.action === 'edit'"></a-select> <a-select v-model:value="_value.source" :options="PropertySource" size="small" :disabled="metadataStore.model.action === 'edit'"></a-select>
</a-form-item> </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="[ <a-form-item label="读写类型" :name="name.concat(['type'])" :rules="[
{ required: true, message: '请选择读写类型' }, { required: true, message: '请选择读写类型' },
]"> ]">