fix: bug#16459、16434、16453、16452、10936

* fix: bug#16459、16434、16453、16452

* feat: 数据解析添加代码提示

* fix: bug#16451

* fix: 修改子设备绑定

* fix: 北向输出bug修复

* fix: 修改北向输出

* fix: 兼容物模型旧版数据

* fix: 修改订阅通知样式

* fix: 优化物模型规则窗口长度

* fix: 修改告警配置

* fix: providerName

* fix: bug#10936

* feat: docker:2.1.0-SNAPSHOT
This commit is contained in:
XieYongHong 2023-07-18 18:11:44 +08:00 committed by GitHub
parent 7a8848360c
commit 9170030d25
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 401 additions and 255 deletions

View File

@ -1,3 +1,3 @@
#!/usr/bin/env bash
docker build -t registry.cn-shenzhen.aliyuncs.com/jetlinks/jetlinks-ui-vue:2.1.0-TEST .
docker push registry.cn-shenzhen.aliyuncs.com/jetlinks/jetlinks-ui-vue:2.1.0-TEST
docker build -t registry.cn-shenzhen.aliyuncs.com/jetlinks/jetlinks-ui-vue:2.1.0-SNAPSHOT .
docker push registry.cn-shenzhen.aliyuncs.com/jetlinks/jetlinks-ui-vue:2.1.0-SNAPSHOT

View File

@ -25,7 +25,7 @@
"event-source-polyfill": "^1.0.31",
"global": "^4.4.0",
"jetlinks-store": "^0.0.3",
"jetlinks-ui-components": "^1.0.24",
"jetlinks-ui-components": "^1.0.25",
"js-cookie": "^3.0.1",
"jsencrypt": "^3.3.2",
"less": "^4.1.3",

View File

@ -607,3 +607,6 @@ export const saveDeviceVirtualProperty = (productId: string, deviceId: string, d
export const queryDeviceVirtualProperty = (productId: string, deviceId: string, propertyId: string) => server.get(`/virtual/property/device/${productId}/${deviceId}/${propertyId}`)
export const queryByParent = (deviceId: string) => server.get(`/device/gateway/${deviceId}/parent`)
export const queryCodeTips = (productId: string, deviceId: string) => server.get(`/device/transparent-codec/${productId}/${deviceId}.d.ts`)
export const queryProductCodeTips = (productId: string) => server.get(`/device/transparent-codec/${productId}.d.ts`)

View File

@ -8,9 +8,6 @@
<div class="box-item" v-for="item in getData" :key="item.id">
<slot name="card" v-bind="item"></slot>
</div>
<div class="box-item">
<slot name="add"></slot>
</div>
<div class="box-btn" v-if="(pageIndex + 1) * showLength < data.length">
<div class="box-item-action" @click="onRight">
<AIcon type="RightOutlined" />
@ -63,14 +60,13 @@ const onLeft = () => {
.box {
display: flex;
align-items: center;
margin: 5px 0;
margin: 8px 0;
.box-item {
margin: 0 6px;
max-width: 48px;
margin: 0 12px;
max-width: 60px;
}
.box-btn {
margin-right: 12px;
.box-item-action {
width: 12px;
background-color: #F7F8FA;
@ -79,6 +75,7 @@ const onLeft = () => {
font-size: 12px;
color: #666666;
cursor: pointer;
&:hover {
background-color: #EFF2FE;
color: @primary-color;

View File

@ -617,7 +617,9 @@ const onPlatError = (val: any) => {
const _validator = (_rule: any, value: string): Promise<any> =>
new Promise((resolve, reject) => {
const _item = productList.value.find((item) => item.id === value);
if (!_item) {
if(!modelRef.id || modelRef.id === ':id') {
return resolve('');
} else if (!_item && value) {
return reject('关联产品已被删除,请重新选择');
}
return resolve('');

View File

@ -779,18 +779,19 @@ const onActiveProduct = () => {
const _validator = (_rule: any, value: string): Promise<any> =>
new Promise((resolve, reject) => {
const _item = productList.value.find((item) => item.id === value);
if (!_item) {
if(!modelRef.id || modelRef.id === ':id') {
return resolve('');
} else if (!_item && value) {
productChange(value);
return reject('关联产品已被删除,请重新选择');
} else {
if (!_item?.state) {
_error.value = `当前选择的${_item.name}产品为禁用状态`;
// return reject(`${_item.name}`)
} else {
_error.value = '';
}
return resolve('');
}
return resolve('');
});
const saveBtn = async () => {

View File

@ -105,7 +105,7 @@ const handleSearch = async () => {
);
});
if (_item) {
info.value = _item?.providerName
info.value = _item?.providerName || _item?.thirdPartyUserId
}
}
};

View File

@ -147,15 +147,19 @@ const onSave = (dt: any) => {
<style lang="less" scoped>
.child-item {
padding: 5px 20px 0 20px;
display: flex;
justify-content: space-between;
align-items: center;
box-shadow: 0px 1px 0px 0px #e2e2e2;
height: 68px;
background: linear-gradient(270deg, #ffffff 0%, #f1f6ff 99%);
border-radius: 4px;
border: 1px solid #ebeef3;
margin-bottom: 10px;
.child-item-left {
display: flex;
align-items: center;
margin-left: 24px;
div {
display: flex;
@ -172,6 +176,7 @@ const onSave = (dt: any) => {
.child-item-right {
display: flex;
margin-right: 16px;
}
}
</style>

View File

@ -21,10 +21,9 @@
<j-collapse-panel
v-for="item in dataSource"
:key="item.provider"
class="custom"
:header="item.name"
>
<div class="child">
<div>
<template
v-for="child in item.children"
:key="child.id"
@ -110,26 +109,32 @@ onMounted(() => {
<style lang="less" scoped>
.alert {
height: 40px;
padding-left: 10px;
margin-bottom: 10px;
color: #999999;
line-height: 40px;
}
.custom {
background: #F7F8FA;
border: 0;
overflow: hidden;
color: #333333;
}
.child {
background-color: white;
padding-bottom: 24px;
margin-bottom: 16px;
}
.content-collapse {
:deep(.ant-collapse-content > .ant-collapse-content-box) {
padding: 0;
:deep(.ant-collapse) {
border-color: #EBEEF3;
background-color: #fff;
.ant-collapse-item {
border: 1px solid #EBEEF3;
margin-bottom: 24px;
}
.ant-collapse-header {
background-color: #F7F8FA;
height: 42px;
}
.ant-collapse-content {
padding: 17px 21px 7px 21px;
}
.ant-collapse-content-box {
padding: 0;
}
}
}
</style>

View File

@ -194,13 +194,22 @@ const handleOk = () => {
if (instanceStore.current.accessProvider === 'official-edge-gateway') { //
queryDeviceMapping(instanceStore.current.id)
.then(res => {
const arr = bindDeviceRef.value?._dataSource.filter(item => {
return !res.result?.[0]?.find(val => val.deviceId === item.id) && _selectedRowKeys.value.includes(item.id);
}).map(item => {
return {
deviceId: item.id,
deviceName: item.name
}
const arr = bindDeviceRef.value?._dataSource.filter((item: any) => {
return _selectedRowKeys.value.includes(item.id);
}).map((item: any) => {
const _item = res.result?.[0]?.find((val: any) => val.deviceId === item.id)
if(_item){
return {
id: _item.id,
deviceId: _item.deviceId,
deviceName: _item.deviceName
}
}else {
return {
deviceId: item.id,
deviceName: item.name
}
}
})
return saveDeviceMapping(instanceStore.current.id, {info: arr})
}).then(res => {

View File

@ -11,35 +11,27 @@
编辑
</PermissionButton>
</template>
<j-descriptions-item label="设备ID">{{
instanceStore.current?.id
}}</j-descriptions-item>
<j-descriptions-item v-if='instanceStore.current?.accessProvider === "plugin_gateway"'>
<template #label>
<div>
第三方系统设备ID
<j-tooltip>
<template #title>
<p>通过调用SDK或HTTP请求的方式接入第三方系统设备数据时第三方系统与平台当前设备对应的设备ID</p>
如双方ID值一致则无需填写
</template>
<a-icon type='QuestionCircleOutlined' />
</j-tooltip>
</div>
</template>
<j-button v-if='!inklingDeviceId' type="link" @click='giveAnInkling'>映射</j-button>
<div v-else style='display: flex;justify-content: space-between;align-items: center;'>
<div style='flex: 1 1 auto;'>
<j-ellipsis>{{ inklingDeviceId }}</j-ellipsis>
</div>
<j-button type='link'>
<a-icon
type='EditOutlined'
@click='inkingVisible = true'
/>
</j-button>
<j-descriptions-item label="设备ID">
<div style="display: flex">
<div style="flex: 1">
<j-ellipsis> {{ instanceStore.current?.id }} </j-ellipsis>
</div>
<div v-if='instanceStore.current?.accessProvider === "plugin_gateway"'>
<j-tooltip>
<template #title>
<p>通过调用SDK或HTTP请求的方式接入第三方系统设备数据时第三方系统与平台当前设备对应的设备ID</p>
如双方ID值一致则无需填写
</template>
<a v-if="!inklingDeviceId" type='link' @click='giveAnInkling'>
未映射
</a>
<a v-else type='link' @click='inkingVisible = true'>
已映射
</a>
</j-tooltip>
</div>
</div>
</j-descriptions-item>
<j-descriptions-item label="产品名称">{{
instanceStore.current?.productName

View File

@ -68,6 +68,7 @@
style="height: 100%"
theme="vs"
v-model:modelValue="editorValue"
:registrationTypescript="typescriptTip"
/>
</div>
<div class="bottom">
@ -151,11 +152,11 @@ import PermissionButton from '@/components/PermissionButton/index.vue';
import { useFullscreen } from '@vueuse/core';
import { useInstanceStore } from '@/store/instance';
import {
deviceCode,
getProtocal,
testCode,
saveDeviceCode,
delDeviceCode,
deviceCode,
getProtocal,
testCode,
saveDeviceCode,
delDeviceCode, queryCodeTips, queryProductCodeTips,
} from '@/api/device/instance';
import { message } from 'jetlinks-ui-components';
import { isBoolean } from 'lodash';
@ -179,6 +180,11 @@ const loading = ref<boolean>(false);
const isTest = ref<boolean>(false);
const editorValue = ref<string>('');
const typescriptTip = reactive({
typescript: ''
})
const color = computed(() => ({
color: readOnly.value ? '#415ed1' : '#a6a6a6',
}));
@ -228,12 +234,24 @@ const getTopic = async () => {
topicList.value = item;
}
};
const queryCode = () => {
queryCodeTips(instanceStore.current.productId,
instanceStore.current.id,).then(res => {
if (res.success) {
typescriptTip.typescript = res.result
}
})
}
//
const getDeviceCode = async () => {
const res: any = await deviceCode(
instanceStore.current.productId,
instanceStore.current.id,
);
if (res.status === 200) {
const item = res.result?.configuration?.script
? res.result?.configuration?.script
@ -261,6 +279,7 @@ const test = async (dataTest: any) => {
}
};
//
const save = async () => {
const item = {
@ -328,6 +347,7 @@ watch(() => instanceStore.current?.id, () => {
if (instanceStore.current?.id) {
getDeviceCode();
getTopic();
queryCode()
}
}, { immediate: true })
</script>

View File

@ -25,6 +25,7 @@
style="height: 100%"
theme="vs"
v-model:modelValue="editorValue"
:registrationTypescript="typescriptTip"
/>
</div>
<div class="bottom">
@ -112,10 +113,10 @@ import PermissionButton from '@/components/PermissionButton/index.vue';
import { useFullscreen } from '@vueuse/core';
import { useProductStore } from '@/store/product';
import {
productCode,
getProtocal,
testCode,
saveProductCode,
productCode,
getProtocal,
testCode,
saveProductCode, queryProductCodeTips,
} from '@/api/device/instance';
import { isBoolean } from 'lodash';
import { onlyMessage } from '@/utils/comm';
@ -135,6 +136,9 @@ const resultValue = ref<any>({});
const loading = ref<boolean>(false);
const isTest = ref<boolean>(false);
const editorValue = ref<string>('');
const typescriptTip = reactive({
typescript: ''
})
const resStyle = computed(() =>
isBoolean(resultValue.value.success)
@ -168,6 +172,16 @@ const getTopic = async () => {
topicList.value = item;
}
};
const queryCodeTips = () => {
queryProductCodeTips(productStore.current.id).then(res => {
if (res.success) {
typescriptTip.typescript = res.result
}
})
}
//
const getProductCode = async () => {
const res: any = await productCode(productStore.current.id);
@ -248,6 +262,7 @@ const debug = () => {
onMounted(() => {
getProductCode();
getTopic();
queryCodeTips()
});
</script>

View File

@ -395,19 +395,22 @@ const handleSaveClick = async (next?: Function) => {
const virtual: any[] = [];
const arr = resp.map((item: any) => {
if(item.expands?.virtualRule) {
const triggerProperties = item.expands.virtualRule.triggerProperties
const rule = omit(item.expands.virtualRule, ['triggerProperties'])
virtual.push({
...item.expands.virtualRule,
triggerProperties,
rule,
type: rule.type,
propertyId: item.id
})
}
// return {
// ...item,
// expands: {
// ...item.expands,
// virtualRule: undefined
// }
// }
return item
return {
...item,
expands: {
...omit(item.expands, ['virtualRule'])
}
}
// return item
})
//
if(virtual.length) {

View File

@ -433,26 +433,46 @@ export const useColumns = (type?: MetadataType, target?: 'device' | 'product', n
required: true,
rules: target !== 'device' ? [
{
validator: async (_: Record<string, any>, value: any) => {
callback: async (rule: any, value: any, dataSource: any[]) => {
console.log('value', value)
if (value.source) {
if(value.source !== 'rule') {
if(value.type?.length) {
return Promise.resolve();
} else {
return Promise.reject('请选择读写类型');
}
} else {
if(value.virtualRule?.rule?.script) {
return Promise.resolve();
}else {
return Promise.reject('请配置规则');
}
const field = rule.field.split('.')
const fieldIndex = Number(field[1])
const values = dataSource.find((item, index) => index === fieldIndex)
const virtualRule = values.elements?.virtualRule
const source = value.source
const ids = (noEdit?.value?.id || []) as any[]
if (source) {
if (source !== 'rule' && !value.type?.length) {
return Promise.reject('请选择读写类型');
} else if(!ids.includes(values.id) && virtualRule){
return Promise.reject('请配置规则');
}
} else {
return Promise.reject('请选择属性来源');
return Promise.resolve()
}
return Promise.reject('请选择属性来源');
}
// if (value.source) {
// if(value.source !== 'rule') {
// if(value.type?.length) {
// return Promise.resolve();
// } else {
// return Promise.reject('请选择读写类型');
// }
// } else {
// if(value.virtualRule?.script) {
// return Promise.resolve();
// }else {
// return Promise.reject('请配置规则');
// }
// }
// } else {
// return Promise.reject('请选择属性来源');
// }
// }
},
]: []
},

View File

@ -0,0 +1,39 @@
<template>
<j-select
v-model:value="myValue"
:options="[
{ label: '固定值', value: 'false' },
{ label: '范围值', value: 'true' },
]"
@select="select"
/>
</template>
<script name="BooleanSelect" setup>
const props = defineProps({
value: {
type: Object,
default: () => ({})
}
})
const emit = defineEmits(['update:value'])
const myValue = ref()
const select = (e) => {
emit('update:value', {
...props.value,
range: e === 'true',
value: e === 'true' ? [undefined, undefined] : undefined
})
}
watch(() => JSON.stringify(props.value), () => {
myValue.value = `${props.value?.range}`
}, { immediate: true })
</script>
<style scoped>
</style>

View File

@ -33,6 +33,7 @@
import { defineExpose, provide } from 'vue'
import MetricValueItem from './ValueItem.vue'
import {validatorConfig} from "@/views/device/components/Metadata/Base/columns";
import BooleanSelect from "@/views/device/components/Metadata/Base/components/Properties/Metrics/BooleanSelect.vue";
const props = defineProps({
value: {
@ -107,7 +108,6 @@ const columns: any = [
rules: [
{
validator(_: any, value: any) {
console.log('指标配置', value)
if (!value) {
return Promise.reject('请配置指标')
}
@ -132,17 +132,11 @@ const newColumns = computed(() => {
title: '指标值',
dataIndex: 'range',
width: 120,
type: 'booleanSelect',
type: 'components',
components: {
props: {
trueText: '范围值',
trueValue: true,
falseText: '固定值',
falseValue: false,
}
name: BooleanSelect
}
})
console.log(data);
return data
}
return columns

View File

@ -10,18 +10,18 @@
>
<template #content>
<j-form ref="formRef" :model="formData">
<j-form-item v-if="value.range === 'false'" name="value" :rule="[{ required: true, message: '请输入指标值'}]">
<j-form-item v-if="value.range === false" :rules="[{ required: true, message: '请输入指标值'}]" name="value">
<Item v-model:value="formData.value" />
</j-form-item>
<div v-else class="data-table-boolean-item">
<div class="data-table-boolean-item--value">
<j-form-item :name="['rangeValue', 0]" :rule="[{ required: true, message: '请输入指标值'}]">
<j-form-item :name="['rangeValue', 0]" :rules="[{ required: true, message: '请输入指标值'}]">
<Item v-model:value="formData.rangeValue[0]" />
</j-form-item>
</div>
<div>-</div>
<div class="data-table-boolean-item--value">
<j-form-item :name="['rangeValue', 1]" :rule="[{ required: true, message: '请输入指标值'}]">
<j-form-item :name="['rangeValue', 1]" :rules="[{ required: true, message: '请输入指标值'}, { validator: validator}]">
<Item v-model:value="formData.rangeValue[1]" />
</j-form-item>
</div>
@ -38,6 +38,7 @@ import { reactive } from 'vue';
import type { PropType } from 'vue';
import Item from './item.vue'
import {Form} from "jetlinks-ui-components";
import {cloneDeep} from "lodash";
type ValueType = number | Array<number | undefined> | undefined;
@ -56,13 +57,14 @@ const emit = defineEmits<Emit>();
const formItemContext = Form.useInjectFormItemContext();
const formData = reactive<{
value: ValueType;
rangeValue: ValueType;
}>({
value: props.value?.range === false ? props.value?.value : undefined,
rangeValue: props.value?.range === true
? props.value?.value || [undefined, undefined]
? cloneDeep(props.value?.value) || [undefined, undefined]
: [undefined, undefined],
});
@ -76,6 +78,13 @@ const showText = computed(() => {
}
})
const validator = (_: any, value: any) => {
if (props.value.range && formData.rangeValue![0] >= formData.rangeValue![1]) {
return Promise.reject('需大于左侧数值')
}
return Promise.resolve()
}
const confirm = () => {
return new Promise((resolve, reject) => {
formRef.value.validate().then(() => {

View File

@ -10,6 +10,8 @@
v-else-if="['int', 'long', 'float', 'double'].includes(type)"
v-model:value="myValue"
:precision="0"
:max="2147483647"
:min="-2147483647"
style="width: 100%"
placeholder="请输入"
@change="change"
@ -31,6 +33,7 @@
/>
</template>
<script setup name="MetricValueItem">
import { Form } from 'jetlinks-ui-components'
const props = defineProps({
value: {
@ -40,11 +43,13 @@ const props = defineProps({
})
const emit = defineEmits(['update:value'])
const formItemContext = Form.useInjectFormItemContext();
const type = inject('metricsType')
const myValue = ref(props.value)
const change = () => {
// formItemContext.onFieldChange()
emit('update:value', myValue.value)
}

View File

@ -55,8 +55,8 @@
required
>
<Rule
v-model:value="formData.virtualRule.rule.script"
:virtualRule="_virtualRule.virtualRule.rule"
v-model:value="formData.virtualRule.script"
:virtualRule="_virtualRule.virtualRule"
:id="value.id"
:aggList="aggList"
/>
@ -67,7 +67,7 @@
required
>
<j-select
v-model:value="formData.virtualRule.rule.windowType"
v-model:value="formData.virtualRule.windowType"
:options="[
{ label: '无', value: 'undefined' },
{ label: '时间窗口', value: 'time' },
@ -79,26 +79,26 @@
/>
</j-form-item>
<template
v-if="formData.virtualRule?.rule?.windowType !== 'undefined'"
v-if="formData.virtualRule?.windowType !== 'undefined'"
>
<j-form-item
label="聚合函数"
:name="['virtualRule', 'rule', 'aggType']"
:name="['virtualRule', 'aggType']"
required
>
<j-select
v-model:value="formData.virtualRule.rule.aggType"
v-model:value="formData.virtualRule.aggType"
:options="aggList"
placeholder="请选择聚合函数"
/>
</j-form-item>
<j-form-item
:label="
formData.virtualRule?.rule?.windowType === 'time'
formData.virtualRule?.windowType === 'time'
? '窗口长度(s)'
: '窗口长度(次)'
"
:name="['virtualRule', 'rule', 'window', 'span']"
:name="['virtualRule', 'window', 'span']"
required
:rules="[
{
@ -112,20 +112,20 @@
]"
>
<j-input-number
v-model:value="formData.virtualRule.rule.window.span"
placeholder="请输入窗口长度"
style="width: 100%"
v-model:value="formData.virtualRule.window.span"
:max="999999"
:min="1"
placeholder="请输入窗口长度"
style="width: 100%"
/>
</j-form-item>
<j-form-item
:label="
formData.virtualRule?.rule?.windowType === 'time'
formData.virtualRule?.windowType === 'time'
? '步长(s)'
: '步长(次)'
"
:name="['virtualRule', 'rule', 'window', 'every']"
:name="['virtualRule', 'window', 'every']"
required
:rules="[
{
@ -140,7 +140,7 @@
>
<j-input-number
style="width: 100%"
v-model:value="formData.virtualRule.rule.window.every"
v-model:value="formData.virtualRule.window.every"
placeholder="请输入步长"
:max="999999"
:min="1"
@ -182,16 +182,14 @@ const props = defineProps({
const initData = {
triggerProperties: ['*'],
rule: {
type: undefined,
script: '',
isVirtualRule: false,
windowType: 'undefined',
aggType: undefined,
window: {
span: undefined,
every: undefined,
},
type: undefined,
script: '',
isVirtualRule: false,
windowType: 'undefined',
aggType: undefined,
window: {
span: undefined,
every: undefined,
},
};
@ -208,16 +206,14 @@ const formData = reactive<{
type: string[];
virtualRule?: {
triggerProperties: string[];
rule: {
type: 'script' | 'window' | undefined;
script: string | undefined;
isVirtualRule: boolean;
windowType: string;
aggType: string | undefined;
window: {
span: number | undefined;
every: number | undefined;
};
type: 'script' | 'window' | undefined;
script: string | undefined;
isVirtualRule: boolean;
windowType: string;
aggType: string | undefined;
window: {
span: number | undefined;
every: number | undefined;
};
};
}>({
@ -228,7 +224,7 @@ const formData = reactive<{
const dataSource = inject<Ref<any[]>>('_dataSource')
const windowTypeChange = () => {
formData.virtualRule!.rule.window = {
formData.virtualRule!.window = {
span: undefined,
every: undefined
}
@ -253,9 +249,11 @@ const options = computed(() => {
});
const setInitVirtualRule = () => {
console.log(props.value?.expands?.virtualRule);
formData.virtualRule = {
...initData,
...(props.value?.expands?.virtualRule || {}),
triggerProperties: props.value?.expands?.virtualRule?.triggerProperties || ['*'],
rule: props.value?.expands?.virtualRule?.rule || initData.rule
}
}
@ -277,7 +275,7 @@ const handleSearch = async () => {
if (resp && resp.status === 200 && resp.result) {
formData.virtualRule = {
triggerProperties: resp.result.triggerProperties,
rule: resp.result.rule,
...resp.result.rule,
}
} else {
setInitVirtualRule()
@ -308,8 +306,6 @@ watch(
() => props.value,
() => {
formData.type = props.value.expands?.type;
if (props.value.virtualRule) {
}
},
{ immediate: true, deep: true },
);
@ -319,7 +315,7 @@ watch(
(newVal: SourceType) => {
if (newVal === 'rule') {
formData.virtualRule = initData;
console.log(formData.virtualRule);
handleSearch();
} else {
formData.virtualRule = undefined;
@ -332,16 +328,13 @@ watch(
);
const _virtualRule = computed(() => {
const flag = formData?.virtualRule?.rule?.windowType !== 'undefined';
const flag = formData?.virtualRule?.windowType !== 'undefined';
return {
type: formData?.type,
virtualRule: {
type: flag ? 'window' : 'script',
rule: {
...formData?.virtualRule?.rule,
isVirtualRule: flag,
type: flag ? 'window' : 'script',
},
...formData?.virtualRule,
isVirtualRule: flag,
triggerProperties: formData?.virtualRule?.triggerProperties.includes('*')
? []
: formData?.virtualRule?.triggerProperties,

View File

@ -92,7 +92,8 @@ const getCPUEcharts = async (val: any) => {
const _cpuXAxis = new Set();
if (res.result?.length) {
//
const filterArray = props.isNoCommunity ? res.result.filter((item : any) => item.data?.clusterNodeId === props.serviceId) : res.result
// const filterArray = props.isNoCommunity ? res.result.filter((item : any) => item.data?.clusterNodeId === props.serviceId) : res.result
const filterArray = res.result
filterArray.forEach((item: any) => {
const value = item.data.value;
const nodeID = item.data.clusterNodeId;

View File

@ -90,7 +90,8 @@ const getJVMEcharts = async (val: any) => {
const _jvmOptions = {};
const _jvmXAxis = new Set();
if (res.result?.length) {
const filterArray = props.isNoCommunity ? res.result.filter((item : any) => item.data?.clusterNodeId === props.serviceId) : res.result
// const filterArray = props.isNoCommunity ? res.result.filter((item : any) => item.data?.clusterNodeId === props.serviceId) : res.result
const filterArray = res.result
filterArray.forEach((item: any) => {
const value = item.data.value;
const memoryJvmHeapFree = value.memoryJvmHeapFree;

View File

@ -11,7 +11,27 @@
></j-input> </j-form-item
></j-col>
<j-col :span="12">
<j-form-item label="类型" name="targetType">
<j-form-item name="targetType">
<template #label>
<j-space>
类型
<j-tooltip>
<template #title>
<div>产品以产品维度告警某产品下的多个设备异常仅发送一条告警</div>
<div>设备以设备维度告警任何设备异常即发送一条告警</div>
<div>组织以组织维度告警某组织下的多个设备异常仅发送一条告警</div>
<div>其他以场景联动维度告警某场景下的多个设备异常仅发送一条告警</div>
</template>
<AIcon
type="QuestionCircleOutlined"
style="
color: rgb(136, 136, 136);
font-size: 12px;
"
/>
</j-tooltip>
</j-space>
</template>
<j-select
:options="options"
v-model:value="form.targetType"
@ -91,13 +111,13 @@ let selectDisable = ref(false);
const alarmConfigurationStore = useAlarmConfigurationStore();
let { configurationData } = storeToRefs(alarmConfigurationStore);
const emit = defineEmits(['change'])
const emit = defineEmits(['change']);
const queryData = () => {
if (route.query?.id) {
detail(route.query?.id).then((res) => {
if (res.status === 200) {
emit('change', res?.result?.targetType)
emit('change', res?.result?.targetType);
form.value = res?.result;
// form.level = res?.result?.level;
// form.name = res?.result?.name;
@ -209,7 +229,7 @@ const handleSave = async () => {
if (res.status === 200) {
onlyMessage('操作成功,请配置关联的场景联动');
loading.value = false;
emit('change', form.value.targetType)
emit('change', form.value.targetType);
if (res.result?.id) {
menuStory.jumpPage(
'rule-engine/Alarm/Configuration/Save',

View File

@ -7,6 +7,7 @@
:width="1000"
@cancel="closeModal"
@ok="saveCorrelation"
:maskClosable="false"
>
<pro-search :columns="columns" @search="handleSearch" />
<div style="height: 500px; overflow-y: auto">

View File

@ -250,6 +250,7 @@ const getDashBoard = () => {
},
grid: {
top: '2%',
left: 40,
bottom: 0,
},
tooltip: {
@ -428,7 +429,7 @@ const selectChange = () => {
.filter((item: any) => item.group === 'alarmTrend')
.forEach((item: any) => {
xData.push(item.data.timeString);
sData.push(item.data.value);
sData.push(item.data.value * 100000);
});
const maxY = sData.sort((a,b)=>{
return b-a
@ -451,7 +452,7 @@ const selectChange = () => {
grid: {
top: '2%',
bottom: '5%',
left: maxY > 10000 ? '50px' : '40px',
left: maxY < 1000 ? '40px' : maxY.toString().length * 10,
right: '48px',
},
series: [

View File

@ -268,7 +268,8 @@ watch(
() => props.productDetail,
(newVal) => {
if (newVal?.id) {
if (props.values?.selector === 'fixed') {
console.log(props.values)
if (props.values?.selector === 'fixed' && props.values?.selectorValues?.length === 1) {
const id = props.values?.selectorValues?.[0]?.value;
if (id) {
detail(id).then((resp) => {
@ -276,6 +277,7 @@ watch(
metadata.value = JSON.parse(
resp.result?.metadata || '{}',
);
console.log(metadata.value, resp.result?.metadata)
}
});
}

View File

@ -67,7 +67,7 @@ const checkDeviceDelete = async () => {
if (item!.selectorValues!.length === 1 && hasDevice) {
const deviceDetail = deviceResp?.result?.data?.[0]
metadata = JSON.parse(deviceDetail?.deriveMetadata || '{}') //
metadata = JSON.parse(deviceDetail?.deriveMetadata || productDetail?.metadata || '{}') //
}
}
if (!hasDevice) { //
@ -139,9 +139,8 @@ const checkDeviceDelete = async () => {
}
if (item!.message!.messageType === 'READ_PROPERTY' && item!.message!.properties && metadata.properties) {
if (item!.message!.messageType === 'READ_PROPERTY') {
let hasProperties = false
console.log('checkDeviceDelete',item!.message!.properties, metadata)
if (item!.message!.properties && metadata.properties?.length) {
const propertiesKey = item!.message!.properties?.[0]
hasProperties = metadata.properties?.some((item: any) => item.id === propertiesKey)
@ -154,8 +153,7 @@ const checkDeviceDelete = async () => {
}
}
console.log('WRITE_PROPERTY',item, metadata)
if (item!.message!.messageType === 'WRITE_PROPERTY' && item!.message!.properties && metadata.properties) {
if (item!.message!.messageType === 'WRITE_PROPERTY') {
let hasProperties = false
const propertiesKey = Object.keys(item!.message!.properties!)?.[0]
if (item!.message!.properties && metadata.properties?.length) {
@ -169,7 +167,7 @@ const checkDeviceDelete = async () => {
}
// -
const _value = item!.message!.properties?.[propertiesKey]
console.log('WRITE_PROPERTY',_value)
if(_value.source === 'upper') {
const _params = {
branch: props.thenName,
@ -248,7 +246,6 @@ const checkNoticeDelete = async () => {
const itemType = variableDefinitionsMap.get(variableKey)
let hasUser = false
console.log(itemType, notifyType)
if (itemType === 'user') { //
let resp = undefined;
if (['dingTalk', 'weixin'].includes(notifyType)) {
@ -319,8 +316,7 @@ const checkNoticeDelete = async () => {
}
const check = () => {
const _executor = _data.value.branches![props.branchesName].then[props.thenName].actions[props.name]?.executor
console.log('check', _executor)
const _executor = _data.value.branches![props.branchesName].then[props.thenName].actions?.[props.name]?.executor
if (_executor === 'device' && _data.value.branches![props.branchesName].then[props.thenName].actions[props.name]?.device) { //
checkDeviceDelete()
} else if (_executor === 'notify' && _data.value.branches![props.branchesName].then[props.thenName].actions[props.name]?.notify) {

View File

@ -536,7 +536,7 @@ const onSave = (data: ActionsType, options: any) => {
_data.value.branches![props.branchesName].then[props.thenName].actions.splice(props.name, 1, actionItem)
checkItemRef.value?.formTouchOff?.()
visible.value = false;
EventEmitter.emit(eventEmitterKey.value, data) //
EventEmitter.emit(key!, data) //
};
/**

View File

@ -1,6 +1,6 @@
<template>
<j-spin :spinning="spinning">
<div class="child-item" :class="{ border: !isLast }">
<div class="child-item">
<div class="child-item-left">
<div style="color: #333333">
{{ data?.name }}
@ -14,7 +14,10 @@
/>
</j-tooltip>
</div>
<div class="child-item-left-auth" :class="{ disabled: !checked }">
<div
class="child-item-left-auth"
:class="{ disabled: !checked }"
>
<j-tooltip>
<template #title>
<span v-if="!update">暂无权限请联系管理员</span>
@ -27,9 +30,17 @@
type="text"
@click="onAuth"
>
<span v-if="!auth.length" class="child-item-left-auth-lock" ><AIcon type="LockFilled" /></span>
<span v-else class="child-item-left-auth-key"><AIcon type="KeyOutlined" /></span>
<span class="child-item-left-auth-text">权限控制</span>
<span
v-if="!auth.length"
class="child-item-left-auth-lock"
><AIcon type="LockFilled"
/></span>
<span v-else class="child-item-left-auth-key"
><AIcon type="KeyOutlined"
/></span>
<span class="child-item-left-auth-text"
>权限控制</span
>
</j-button>
</j-tooltip>
</div>
@ -99,25 +110,15 @@
</template>
</MCarousel>
<div class="box-item-add">
<div class="box-item-img">
<j-tooltip
:title="!add ? '暂无权限,请联系管理员' : ''"
>
<j-button
:disabled="!add"
type="text"
@click="onAdd"
>
<AIcon
style="font-size: 20px"
type="PlusOutlined"
/>
</j-button>
</j-tooltip>
</div>
<div class="box-item-text"></div>
</div>
<j-tooltip :title="!add ? '暂无权限,请联系管理员' : ''">
<j-button
class="box-item-add"
:disabled="!add"
@click="onAdd"
>
<AIcon type="PlusOutlined" />
</j-button>
</j-tooltip>
</div>
</div>
</j-spin>
@ -304,12 +305,15 @@ const onAction = (e: boolean) => {
role: {
idList: [],
},
permissions: props.provider === 'alarm' ? [
{
id: 'alarm-config',
actions: ['query'],
},
] : [],
permissions:
props.provider === 'alarm'
? [
{
id: 'alarm-config',
actions: ['query'],
},
]
: [],
},
},
],
@ -406,15 +410,19 @@ const onSave = (_data: any) => {
<style lang="less" scoped>
.child-item {
padding: 0 20px;
display: flex;
justify-content: space-between;
align-items: center;
height: 68px;
background: linear-gradient(270deg, #ffffff 0%, #f1f6ff 99%);
border-radius: 4px;
border: 1px solid #ebeef3;
margin-bottom: 10px;
.child-item-left {
display: flex;
align-items: center;
height: 80px;
margin-left: 24px;
div {
display: flex;
@ -435,19 +443,20 @@ const onSave = (_data: any) => {
color: #666666;
}
.child-item-left-auth-key {
color: #00C800;
color: #00c800;
font-size: 18px;
margin-right: 10px;
}
.child-item-left-auth-lock{
.child-item-left-auth-lock {
color: @primary-color;
font-size: 18px;
margin-right: 10px;
}
&.disabled {
.child-item-left-auth-key, .child-item-left-auth-lock {
.child-item-left-auth-key,
.child-item-left-auth-lock {
color: #666666 !important;
}
}
@ -460,19 +469,14 @@ const onSave = (_data: any) => {
.box-item {
cursor: pointer;
width: 48px;
margin: 0 2px;
width: 100%;
display: flex;
justify-content: space-between;
flex-direction: column;
align-items: center;
.box-item-img {
background-color: #fff;
width: 32px;
height: 32px;
display: flex;
justify-content: center;
align-items: center;
}
.box-item-text {
@ -485,21 +489,27 @@ const onSave = (_data: any) => {
.box-item-add {
cursor: pointer;
background-color: #f8f9fc;
// width: 54px;
background-color: #F7F8FA;
width: 54px;
height: 54px;
display: flex;
align-items: center;
margin-left: 12px;
justify-content: center;
color: #666666;
border: none;
padding: 0;
border-radius: 0;
margin: 0 16px;
&:hover {
background-color: #eff2fe;
color: #2f54eb;
}
}
&.disabled {
filter: grayscale(100%);
}
}
&.border {
box-shadow: 0px 1px 0px 0px #e2e2e2;
}
}
</style>

View File

@ -2,7 +2,7 @@
<page-container>
<FullPage>
<div class="content">
<div>
<div style="margin-bottom: 15px;">
<div class="alert">
<AIcon type="InfoCircleOutlined" />
启用通知类型后你可以为每种通知类型配置不同的通知方式通知模板接收人
@ -10,7 +10,7 @@
</div>
<div class="content-collapse">
<j-collapse :bordered="false" v-model:activeKey="activeKey" expand-icon-position="right">
<!-- <template #expandIcon="{ isActive }">
<template #expandIcon="{ isActive }">
<AIcon
type="CaretRightOutlined"
:style="{
@ -19,11 +19,10 @@
}deg)`,
}"
/>
</template> -->
</template>
<j-collapse-panel
v-for="item in dataSource"
:key="item.provider"
class="custom"
>
<template #header>
<div>
@ -31,7 +30,7 @@
<span style="margin-left: 10px;" class="alert" v-if="item.provider === 'alarm'">注意接收人需要有告警配置页面查询权限才能收到告警类通知</span>
</div>
</template>
<div class="child">
<div>
<template
v-for="(child, index) in item.children"
:key="child.provider"
@ -142,7 +141,6 @@ onMounted(() => {
padding: 24px;
display: flex;
flex-direction: column;
// height: 100%;
box-sizing: border-box;
justify-content: space-between;
@ -153,27 +151,31 @@ onMounted(() => {
}
}
.alert {
height: 40px;
padding-left: 10px;
margin-bottom: 10px;
color: rgba(0, 0, 0, 0.55);
line-height: 40px;
// background-color: #f6f6f6;
}
.custom {
background: #F7F8FA;
border: 0;
overflow: hidden;
color: #333333;
}
.child {
background-color: white;
padding-bottom: 24px;
}
.content-collapse {
:deep(.ant-collapse-content > .ant-collapse-content-box) {
padding: 0;
:deep(.ant-collapse) {
border-color: #EBEEF3;
background-color: #fff;
.ant-collapse-item {
border: 1px solid #EBEEF3;
margin-bottom: 24px;
}
.ant-collapse-header {
background-color: #F7F8FA;
height: 42px;
}
.ant-collapse-content {
padding: 17px 21px 7px 21px;
}
.ant-collapse-content-box {
padding: 0;
}
}
}
</style>

View File

@ -3835,10 +3835,10 @@ jetlinks-ui-components@^1.0.23:
lodash-es "^4.17.21"
monaco-editor "^0.35.0"
jetlinks-ui-components@^1.0.24:
version "1.0.24"
resolved "http://registry.jetlinks.cn/jetlinks-ui-components/-/jetlinks-ui-components-1.0.24.tgz#e15551fb114e4cb8752db962423b3ca17d9389c1"
integrity sha512-TGyQ9SmdkoNkZScfYvfBF1/P1oBzXkxMuzR+DiNnof6CbSlYfPrkRsJIpOIZUjqg2o/6dyKLDK8gOxbxWm8wjA==
jetlinks-ui-components@^1.0.25:
version "1.0.25"
resolved "http://registry.jetlinks.cn/jetlinks-ui-components/-/jetlinks-ui-components-1.0.25.tgz#b783da3fe05c1420b2ee5707868a67baa90559f7"
integrity sha512-4HJM9Wi8gFfBgYFPjCO7JpmEAXVtDUf5u5lnXTQUt1QkGh0QCzFSBGBIStFs6HE5oY3oIIS3ZWoAjvXElpnNZg==
dependencies:
"@vueuse/core" "^9.12.0"
"@vueuse/router" "^9.13.0"