iot-ui-vue/src/components/FRuleEditor/Debug/index.vue

399 lines
12 KiB
Vue

<template>
<div class="debug-container">
<div class="top">
<div class="header">
<div>
<div class="title">
属性赋值
<div class="description">
请对上方规则使用的属性进行赋值
</div>
</div>
</div>
</div>
<div class="top-bottom">
<j-table
:columns="columns"
:data-source="property"
:pagination="false"
bordered
size="small"
:scroll="{ y: 200 }"
>
<template #bodyCell="{ column, record, index }">
<template v-if="column.key === 'id'">
<j-select
showSearch
:options="options"
v-model:value="record.id"
size="small"
style="width: 100%; z-index: 1400 !important"
/>
</template>
<template v-if="column.key === 'current'">
<j-input
v-model:value="record.current"
size="small"
></j-input>
</template>
<template v-if="column.key === 'last'">
<j-input
v-model:value="record.last"
size="small"
></j-input>
</template>
<template v-if="column.key === 'action'">
<AIcon
type="DeleteOutlined"
@click="deleteItem(index)"
/>
</template>
</template>
</j-table>
<j-button
type="dashed"
block
style="margin-top: 5px"
@click="addItem"
>
<template #icon>
<AIcon type="PlusOutlined" />
</template>
添加条目
</j-button>
</div>
</div>
<div class="bottom">
<div class="header">
<div class="title">
<div>运行结果</div>
<div v-if="virtualRule?.script && !isBeginning">
正在运行......
</div>
</div>
<div class="action">
<div
v-if="!isBeginning && virtualRule?.type === 'window'"
class="action"
@click="runScriptAgain"
>
<a style="margin-left: 75px">发送数据</a>
</div>
<div v-if="virtualRule?.script">
<a v-if="isBeginning" @click="beginAction">
开始运行
</a>
<a v-else @click="stopAction"> 停止运行 </a>
</div>
<div>
<a @click="clearAction"> 清空 </a>
</div>
</div>
</div>
<div class="log">
<j-descriptions>
<j-descriptions-item
v-for="(item, index) in ruleEditorStore.state.log"
:key="item.time"
:span="3"
>
<template #label>
<template v-if="!!runningState(index + 1, item._time)">
{{ runningState(index + 1, item._time) }}
</template>
<template v-else>{{
moment(item.time).format('HH:mm:ss')
}}</template>
</template>
<div v-if="!!runningState(index + 1, item._time)">
{{ moment(item.time).format('HH:mm:ss') }}
</div>
<j-tooltip placement="top" :title="item.content">
{{ item.content }}
</j-tooltip>
</j-descriptions-item>
</j-descriptions>
</div>
</div>
</div>
</template>
<script setup lang="ts" name="Debug">
import { PropType, Ref } from 'vue';
import { useProductStore } from '@/store/product';
import { useRuleEditorStore } from '@/store/ruleEditor';
import moment from 'moment';
import { getWebSocket } from '@/utils/websocket';
import { PropertyMetadata } from '@/views/device/Product/typings';
import { onlyMessage } from '@/utils/comm';
const props = defineProps({
virtualRule: Object as PropType<Record<any, any>>,
id: String,
});
const emits = defineEmits(['success']);
const isBeginning = ref(true);
type propertyType = {
id?: string;
current?: string;
last?: string;
};
const property = ref<propertyType[]>([]);
const columns = [
{
title: '属性名称',
dataIndex: 'id',
key: 'id',
},
{
title: '当前值',
dataIndex: 'current',
key: 'current',
},
{
title: '上一值',
dataIndex: 'last',
key: 'last',
},
{
title: '操作',
key: 'action',
width: 50,
},
];
const addItem = () => {
property.value.push({});
};
const deleteItem = (index: number) => {
property.value.splice(index, 1);
};
const ws = ref();
const virtualIdRef = ref(new Date().getTime());
const medataSource = inject<Ref<any[]>>('_dataSource');
const productStore = useProductStore();
const ruleEditorStore = useRuleEditorStore();
const time = ref<number>(0);
const timer = ref<any>(null);
const runScript = () => {
const metadata = productStore.current.metadata || '{}';
const propertiesList = JSON.parse(metadata).properties || [];
const _properties = property.value.map((item: any) => {
const _item = propertiesList.find((i: any) => i.id === item.id);
return { ...item, type: _item?.valueType?.type };
});
if (ws.value) {
ws.value.unsubscribe?.();
}
if (!props.virtualRule?.script) {
isBeginning.value = true;
onlyMessage('请编辑规则', 'warning');
return;
}
ws.value = getWebSocket(
`virtual-property-debug-${props.id}-${new Date().getTime()}`,
'/virtual-property-debug',
{
virtualId: `${virtualIdRef.value}-virtual-id`,
property: props.id,
virtualRule: {
...props.virtualRule,
},
properties: _properties || [],
},
).subscribe((data: any) => {
ruleEditorStore.state.log.push({
time: new Date().getTime(),
content: JSON.stringify(data.payload),
_time: unref(time.value)
});
emits('success', false);
if (props.virtualRule?.type !== 'window') {
stopAction();
}
});
};
const runningState = (_index: number, _time: number) => {
if (props.virtualRule?.windowType === 'time') {
return `已运行${_time}`;
}
if (props.virtualRule?.windowType === 'num') {
return `${_index}次运行`;
}
return false;
};
const wsAgain = ref<any>();
const runScriptAgain = async () => {
if (wsAgain.value) {
wsAgain.value.unsubscribe?.();
}
const metadata = productStore.current.metadata || '{}';
const propertiesList = JSON.parse(metadata).properties || [];
const _properties = property.value.map((item: any) => {
const _item = propertiesList.find((i: any) => i.id === item.id);
return { ...item, type: _item?.valueType?.type };
});
wsAgain.value = getWebSocket(
`virtual-property-debug-${props.id}-${new Date().getTime()}`,
'/virtual-property-debug',
{
virtualId: `${virtualIdRef.value}-virtual-id`,
property: props.id,
virtualRule: {
...props.virtualRule,
},
properties: _properties || [],
},
).subscribe((data: any) => {});
};
const getTime = () => {
time.value = 0;
timer.value = setInterval(() => {
time.value += 1;
}, 1000);
};
const beginAction = () => {
isBeginning.value = false;
runScript();
getTime();
};
const stopAction = () => {
isBeginning.value = true;
if (ws.value) {
ws.value.unsubscribe?.();
}
window.clearInterval(timer.value)
timer.value = null
};
const clearAction = () => {
ruleEditorStore.set('log', []);
};
onUnmounted(() => {
if (ws.value) {
ws.value.unsubscribe?.();
}
clearAction();
window.clearInterval(timer.value)
timer.value = null
});
const options = computed(() => {
return (medataSource.value || [])
.filter((p) => p.id !== props.id)
.map((item) => ({
label: item.name,
value: item.id,
}));
});
// const getProperty = () => {
// // const metadata = productStore.current.metadata || '{}';
// // const _p: PropertyMetadata[] = JSON.parse(metadata).properties || [];
// console.log(medataSource.value)
// options.value =
// };
// getProperty();
</script>
<style lang="less" scoped>
.debug-container {
// display: flex;
// width: 100%;
// height: 340px;
// margin-top: 20px;
.top {
// min-width: 0;
// max-width: 550px;
// overflow-y: auto;
height: 350px;
border: 1px solid lightgray;
margin-bottom: 10px;
.header {
display: flex;
align-items: center;
width: 100%;
height: 40px;
border-bottom: 1px solid lightgray;
//justify-content: space-around;
div {
display: flex;
//width: 100%;
align-items: center;
justify-content: flex-start;
height: 100%;
.title {
margin: 0 10px;
font-weight: 600;
font-size: 16px;
}
.description {
margin-left: 10px;
color: lightgray;
font-size: 12px;
}
}
.action {
width: 150px;
font-size: 14px;
}
}
.top-bottom {
padding: 10px;
}
}
.bottom {
border: 1px solid lightgray;
.header {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
height: 40px;
border-bottom: 1px solid lightgray;
.title {
display: flex;
div {
margin: 0 10px;
}
}
.action {
display: flex;
div {
margin: 0 10px;
}
}
}
.log {
height: 300px;
padding: 5px;
overflow: auto;
}
}
}
</style>