fix: 修复设备接入更换接入方式,参数名称未发生变化问题

This commit is contained in:
XieYongHong 2023-05-18 16:20:20 +08:00 committed by GitHub
parent 285d108bbc
commit 73e527f12c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
45 changed files with 735 additions and 160 deletions

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.13",
"jetlinks-ui-components": "^1.0.16",
"js-cookie": "^3.0.1",
"less": "^4.1.3",
"less-loader": "^11.1.0",

Binary file not shown.

View File

@ -2,6 +2,15 @@
@DarkMenuItemColor: #808491 !important;
@font-face {
font-family: AliRegular;
src: url("/fonts/AlibabaPuHuiTi-2-55-Regular.ttf");
}
body {
font-family: 'AliRegular' !important;
}
.ant-form-item-required:before {
position: absolute;
right: -12px;

View File

@ -1,3 +1,4 @@
@import "ant-design-vue/es/style/themes/index.less";
@import 'jetlinks-ui-components/es/style/variable.less';
.ellipsisFn(@num: 1, @width: 100%) {

View File

@ -1,6 +1,6 @@
<template>
<pro-search
class="device-search"
class="device-running-search"
type="simple"
:columns="columns"
target="device-instance-running-events"
@ -45,23 +45,25 @@ const events = defineProps({
});
const instanceStore = useInstanceStore();
const columns = ref<Record<string, any>>([
{
title: '时间',
dataIndex: 'timestamp',
key: 'timestamp',
scopedSlots: true,
search: {
type: 'date',
},
const defaultColumns = [
{
title: '时间',
dataIndex: 'timestamp',
key: 'timestamp',
scopedSlots: true,
search: {
type: 'date',
},
{
title: '操作',
dataIndex: 'action',
key: 'action',
scopedSlots: true,
},
]);
},
{
title: '操作',
dataIndex: 'action',
key: 'action',
scopedSlots: true,
}
]
const columns = ref<Array<Record<string, any>>>([...defaultColumns]);
const params = ref<Record<string, any>>({});
const visible = ref<boolean>(false);
const info = ref<Record<string, any>>({});
@ -70,6 +72,7 @@ const _getEventList = (_params: any) =>
getEventList(instanceStore.current.id || '', events.data.id || '', _params);
watchEffect(() => {
columns.value = [...defaultColumns]
if (events.data?.valueType?.type === 'object') {
(events.data.valueType?.properties || []).reverse().map((i: any) => {
columns.value.splice(0, 0, {
@ -109,8 +112,10 @@ const detail = (_info: any) => {
</script>
<style lang="less">
.device-search {
.device-running-search {
margin: 0 0 24px 0;
padding-top: 0 !important;
padding-bottom: 0 !important;
}
.device-running-event-modal {

View File

@ -61,7 +61,7 @@
<div class="card-item-content-text">
设备类型
</div>
<div>直连设备</div>
<div>{{ slotProps?.deviceType?.text }}</div>
</j-col>
</j-row>
</template>

View File

@ -257,12 +257,15 @@ const findProvidersByProvider = (provider: string) => {
*/
const submitData = async () => {
if (selectedRowKeys.value.length) {
if (checkData.value.channel === 'plugin') {
const resp = await getProductByPluginId(checkData.value.channelId).catch(() => ({ success: false, result: []}))
const metadataResp = await getAccessConfig(props.productId!, checkData.value.id).catch(() => ({ success: false, result: {}}))
emit('submit', {
access: {...checkData.value},
productTypes: resp.result
productTypes: resp.result,
metadata: metadataResp.result
})
} else {
loading.value= true

View File

@ -615,11 +615,13 @@ const checkAccess = async (data: any) => {
productTypes.value = []
productData.id = undefined
productData.metadata = {}
metadata.value = data.metadata?.[0] || {
properties: []
}
if (data.access.channel === 'plugin') { //
markdownToHtml.value = ''
productTypes.value = data.productTypes.map(item => ({ ...item, label: item.name, value: item.id}))
} else {
metadata.value = data.metadata[0]
handleColumns()
markdownToHtml.value = config.value?.document ? marked(config.value.document) : '';
getGuide(!!data.metadata.length); //

View File

@ -20,12 +20,17 @@
>
<template #bodyCell="{ column, text, record, index }">
<template v-if='column.dataIndex === "name"'>
<span class='metadata-title'>{{ text }} ({{ record.id }})</span>
<span class='metadata-title'>
<j-ellipsis>
{{ text }} ({{ record.id }})
</j-ellipsis>
</span>
</template>
<template v-if='column.dataIndex === "plugin"'>
<j-select
v-model:value='record.plugin'
style='width: 100%'
allowClear
@change='(id) => pluginChange(record, id)'
>
<j-select-option
@ -40,26 +45,28 @@
</j-table>
</div>
<div class='right'>
<div class='title'>
功能说明
</div>
<p>
该功能用于将插件中的
<b>物模型属性标识</b>
<b>平台物模型属性标识</b>进行映射,当两方属性标识不一致时可在当前页面直接修改映射管理系统将以映射后的物模型属性进行数据处理
</p>
<p>
未完成映射的属性标识目标属性列数据为空代表该属性值来源以在平台配置的来源为准
</p>
<p>
数据条背景亮起代表<b>标识一致</b><b>已完成映射</b>的属性
</p>
<div class='title'>
功能图示
</div>
<div>
<img :src='getImage("/device/matadataMap.png")' />
</div>
<j-scrollbar>
<div class='title'>
功能说明
</div>
<p>
该功能用于将插件中的
<b>物模型属性标识</b>
<b>平台物模型属性标识</b>进行映射,当两方属性标识不一致时可在当前页面直接修改映射管理系统将以映射后的物模型属性进行数据处理
</p>
<p>
未完成映射的属性标识目标属性列数据为空代表该属性值来源以在平台配置的来源为准
</p>
<p>
数据条背景亮起代表<b>标识一致</b><b>已完成映射</b>的属性
</p>
<div class='title'>
功能图示
</div>
<div>
<img :src='getImage("/device/matadataMap.png")' />
</div>
</j-scrollbar>
</div>
</div>
</template>
@ -87,7 +94,7 @@ const columns = [
{
title: '序号',
dataIndex: 'index',
width: 120
width: 100
},
{
title: '平台属性',
@ -96,6 +103,7 @@ const columns = [
{
title: '目标属性',
dataIndex: 'plugin',
width: 250,
sorter: tableFilter
}
]

View File

@ -470,8 +470,8 @@ const query = reactive({
},
{
title: '接入方式',
key: 'accessName',
dataIndex: 'accessName',
key: 'accessId',
dataIndex: 'accessId',
search: {
type: 'select',
options: async () => {
@ -482,7 +482,7 @@ const query = reactive({
typeList.value = [];
typeList.value = resp.result.map((item: any) => ({
label: item.name,
value: item.name,
value: item.id,
}));
res(typeList.value);
});

View File

@ -20,7 +20,8 @@ watch(
deviceId.value = newId as string;
_control(newId).then((resp: any) => {
if (resp.status === 200) {
const item = `http://${resp.result?.url}/#/login?token=${resp.result.token}`;
const protocol = location.protocol
const item = `${protocol}//${resp.result?.url}/#/login?token=${resp.result.token}`;
url.value = item;
}
});

View File

@ -16,11 +16,13 @@
<template v-if="showType === 'network'">
<Network
v-if="provider.id !== 'plugin_gateway'"
:bindProduct='bindProduct'
:provider="provider"
:data="data"
/>
<Plugin
v-else
:bindProduct='bindProduct'
:provider="provider"
:data="data"
/>
@ -28,21 +30,25 @@
<Media
v-if="showType === 'media'"
:bindProduct='bindProduct'
:provider="provider"
:data="data"
/>
<Channel
v-if="showType === 'channel'"
:bindProduct='bindProduct'
:provider="provider"
:data="data"
/>
<Edge
v-if="showType === 'edge'"
:bindProduct='bindProduct'
:provider="provider"
:data="data"
/>
<Cloud
v-if="showType === 'cloud'"
:bindProduct='bindProduct'
:provider="provider"
:data="data"
/>
@ -63,6 +69,7 @@ import Cloud from '../components/Cloud/index.vue';
import Plugin from '../components/Plugin/index.vue'
import { getProviders, detail } from '@/api/link/accessConfig';
import { accessConfigTypeFilter } from '@/utils/setting';
import { queryProductList } from '@/api/device/product';
const route = useRoute();
const id = route.params.id as string;
@ -73,6 +80,7 @@ const loading = ref(true);
const provider = ref({});
const data = ref({});
const showType: any = ref('');
const bindProduct = ref(false)
const goProviders = (param: any) => {
showType.value = param.type;
@ -188,8 +196,27 @@ const queryProviders = async () => {
}
};
/**
* 检查是否被产品使用
*/
const checkBindProduct = async (_id: string) => {
const resp = await queryProductList({
paging: false,
terms: [{
column: 'accessId',
termType: 'eq',
value: _id
}]
})
console.log(resp.success && resp.result?.total)
if (resp.success && resp.result?.total) {
bindProduct.value = true
}
}
const getProvidersData = async () => {
if (id !== ':id') {
checkBindProduct(id)
getProviders().then((response: any) => {
if (response.status === 200) {
const _data = response.result || [];

View File

@ -22,13 +22,12 @@
}}</j-tooltip>
</div>
<div class="checked-icon">
<div><CheckOutlined /></div>
<div><a-icon type='CheckOutlined' /></div>
</div>
</j-card>
</template>
<script lang="ts" setup name="AccessCard">
import { CheckOutlined } from '@ant-design/icons-vue';
const emit = defineEmits(['checkedChange']);
@ -48,7 +47,9 @@ const props = defineProps({
});
const checkedChange = (id: string) => {
if (!props.disabled) {
emit('checkedChange', id);
}
};
</script>

View File

@ -177,6 +177,7 @@
@search="procotolSearch"
/>
<PermissionButton
v-if='showAddBtn'
type="primary"
@click="addProcotol"
hasPermission="link/Protocol:add"
@ -199,6 +200,7 @@
<AccessCard
@checkedChange="procotolChange"
:checked="procotolCurrent"
:disabled='!showAddBtn'
:data="{ ...item, type: 'protocol' }"
>
</AccessCard>
@ -352,6 +354,10 @@ const props = defineProps({
type: Object,
default: () => {},
},
bindProduct: {
type: Boolean,
default: false
}
});
const formRef1 = ref<FormInstance>();
@ -368,6 +374,10 @@ const formData = ref<Form>({
description: '',
});
const showAddBtn = computed(() => {
return route.query.view === 'false' && !props.bindProduct
})
const current = ref(0);
const stepCurrent = ref(0);
const steps = ref(['接入配置', '消息协议', '完成']);

View File

@ -260,6 +260,7 @@
@search="procotolSearch"
/>
<PermissionButton
v-if='showAddBtn'
type="primary"
@click="addProcotol"
hasPermission="link/Protocol:add"
@ -282,6 +283,7 @@
<AccessCard
@checkedChange="procotolChange"
:checked="procotolCurrent"
:disabled='!showAddBtn'
:data="{ ...item, type: 'protocol' }"
>
</AccessCard>
@ -434,6 +436,10 @@ const props = defineProps({
type: Object,
default: () => {},
},
bindProduct: {
type: Boolean,
default: false
}
});
const route = useRoute();
@ -462,6 +468,10 @@ const procotolList: any = ref([]);
const allProcotolList = ref([]);
const procotolCurrent: any = ref('');
const showAddBtn = computed(() => {
return route.query.view === 'false' && !props.bindProduct
})
const procotolChange = (id: string) => {
procotolCurrent.value = id;
};

View File

@ -3,11 +3,13 @@
<Ctwing
v-if="channel === 'Ctwing'"
:provider="props.provider"
:bindProduct='bindProduct'
:data="props.data"
/>
<OneNet
v-if="channel === 'OneNet'"
:provider="props.provider"
:bindProduct='bindProduct'
:data="props.data"
/>
</div>
@ -26,6 +28,10 @@ const props = defineProps({
type: Object,
default: () => {},
},
bindProduct: {
type: Boolean,
default: false
}
});
const channel = props.provider.channel;

View File

@ -530,7 +530,7 @@ const props = defineProps({
data: {
type: Object,
default: () => {},
},
}
});
const route = useRoute();

View File

@ -17,6 +17,7 @@
@search="networkSearch"
/>
<PermissionButton
type="primary"
@click="addNetwork"
hasPermission="link/Type:add"
@ -112,6 +113,7 @@
@search="procotolSearch"
/>
<PermissionButton
v-if='showAddBtn'
type="primary"
@click="addProcotol"
hasPermission="link/Protocol:add"
@ -134,7 +136,7 @@
<AccessCard
@checkedChange="procotolChange"
:checked="procotolCurrent"
:disabled='id !== ":id"'
:disabled='!showAddBtn'
:data="{ ...item, type: 'protocol' }"
>
</AccessCard>
@ -352,6 +354,10 @@ const props = defineProps({
type: Object,
default: () => {},
},
bindProduct: {
type: Boolean,
default: false
}
});
const clientHeight = document.body.clientHeight;
@ -396,6 +402,10 @@ const { resetFields, validate, validateInfos } = useForm(
}),
);
const showAddBtn = computed(() => {
return route.query.view === 'false' && !props.bindProduct
})
const queryNetworkList = async (id: string, include: string, data = {}) => {
const resp = await getNetworkList(
NetworkTypeMapping.get(id),

View File

@ -14,6 +14,7 @@
@search="pluginSearch"
/>
<PermissionButton
v-if='showAddBtn'
type="primary"
@click="addPlugin"
hasPermission="link/plugin:add"
@ -36,7 +37,7 @@
<AccessCard
@checkedChange="AccessChange"
:checked="AccessCurrent"
:disabled='paramsId !== ":id"'
:disabled='!showAddBtn'
:data="{ ...item, type: 'plugin' }"
>
<template #other>
@ -185,6 +186,10 @@ const props = defineProps({
type: Object,
default: () => {},
},
bindProduct: {
type: Boolean,
default: false
}
});
const route = useRoute();
@ -220,6 +225,10 @@ const queryPlugin = (params = {}) => {
})
}
const showAddBtn = computed(() => {
return route.query.view === 'false' && !props.bindProduct
})
const getRules = (item: any) => {
let typeName = '输入'
let rules: any[] = []

View File

@ -157,7 +157,15 @@ watch(
.dash-board-item {
flex: 1;
//margin: 24px 12px;
min-width: 250px;
min-width: calc(25% - 24px);
}
}
@media (max-width: 1400px) {
.dash-board {
.dash-board-item {
min-width: calc(50% - 24px);
}
}
}
</style>

View File

@ -43,19 +43,27 @@ export default {
data() {
return {
options: {},
myChart: undefined
};
},
beforeDestroy() {
window.removeEventListener('resize', this.resize)
},
methods: {
createChart(val) {
const chart = this.$refs.chartRef;
if (chart && Object.keys(val).length > 0) {
const myChart = echarts.init(chart);
myChart.setOption(val);
window.addEventListener('resize', function () {
myChart.resize();
});
if (chart && Object.keys(val).length > 0 && !this.myChart) {
console.log('createChart')
this.myChart = echarts.init(chart);
this.myChart.setOption(val);
window.addEventListener('resize', this.resize);
} else if (this.myChart) {
this.myChart.setOption(val);
}
},
resize() {
this.myChart?.resize();
},
getOptions(max, formatter, val) {
let formatterCount = 0;
this.options = {

View File

@ -110,17 +110,20 @@ export const Validator = {
),
regIPv6: new RegExp(/^([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$/),
regDomain: new RegExp(
/^https?:\/\/(([a-zA-Z0-9_-])+(\.)?)*(:\d+)?(\/((\.)?(\?)?=?&?[a-zA-Z0-9_-](\?)?)*)*$/i,
// /^https?:\/\/(([a-zA-Z0-9_-])+(\.)?)*(:\d+)?(\/((\.)?(\?)?=?&?[a-zA-Z0-9_-](\?)?)*)*$/i,
/^[a-zA-Z0-9]+([\-\.]{1}[a-zA-Z0-9]+)*\.[a-zA-Z]{2,}$/
),
regOnlyNumber: new RegExp(/^\d+$/),
};
const validateAddress = (_rule: any, value: string): Promise<any> => {
return new Promise(async (resolve, reject) => {
const _domainStr = value
const _domain = _domainStr.replace(/^(https?|ftp):\/\/(www\.)?/i, '')
if (
Validator.regIpv4.test(value) ||
Validator.regIPv6.test(value) ||
Validator.regDomain.test(value)
Validator.regDomain.test(_domain)
) {
return resolve('');
} else {

View File

@ -1,7 +1,7 @@
<template>
<j-upload
name="file"
accept=".jar"
accept=".jar,.zip"
:action="uploadFile"
:headers="{
[TOKEN_KEY]: LocalStore.get(TOKEN_KEY),
@ -16,7 +16,7 @@
>
<div>
<j-button>上传文件</j-button>
<span class='upload-tip'>格式要求{文件名}.jar/{文件名}.zip</span>
<span class='upload-tip'>格式要求.jar .zip</span>
</div>
</j-upload>

View File

@ -207,9 +207,11 @@ watch(
* 部门点击
*/
const onTreeSelect = (keys: any) => {
if (keys.length) {
deptId.value = keys[0];
pageSize.value = 10;
current.value = 1;
}
};
//

View File

@ -271,11 +271,11 @@ const columns = [
},
{
title: '关联场景联动',
dataIndex: 'sceneId',
wdith: 250,
dataIndex: 'scene',
scopedSlots: true,
search: {
type: 'select',
// defaultTermType: 'rule-bind-alarm',
options: async () => {
const res = await getScene(
encodeQuery({
@ -338,7 +338,23 @@ const map = {
other: '其他',
};
const handleSearch = (e: any) => {
params.value = e;
const _terms = (e?.terms || []).map((item: any) => {
item.terms = item.terms.map((i: any) => {
if(i.column === 'scene'){
return {
...i,
termType: 'rule-bind-alarm',
column: 'id'
}
}
return i
})
return item
})
params.value = {
...e,
terms: _terms
}
};
const queryDefaultLevel = () => {
queryLevel().then((res) => {

View File

@ -0,0 +1,127 @@
<template>
<slot></slot>
</template>
<script setup lang='ts' name='CheckItem'>
import { storeToRefs } from 'pinia';
import { useSceneStore } from '@/store/scene'
import { Form } from 'jetlinks-ui-components'
import { queryProductList } from '@/api/device/product'
import { query as deviceQuery } from '@/api/device/instance'
import { getTreeData_api } from '@/api/system/department'
const sceneStore = useSceneStore()
const { data } = storeToRefs(sceneStore)
const formItemContext = Form.useInjectFormItemContext()
const formTouchOff = () => {
formItemContext.onFieldChange()
}
const check = async (): Promise<boolean> => {
const deviceTrigger = data.value.trigger!.device!
const productId = deviceTrigger.productId
//
const proResp = await queryProductList({ terms: [{ terms: [{ column: 'id', termType: 'eq', value: productId }]}]})
if (proResp.success && (proResp.result as any)?.total === 0) {
data.value.trigger!.device!.productId = ''
return false
}
const productDetail = proResp?.result?.data?.[0]
const selectorValues = deviceTrigger.selectorValues?.map(item => item.value)
let metadata = JSON.parse(productDetail?.metadata || '{}') //
//
if (deviceTrigger.selector === 'fixed') { //
const deviceResp = await deviceQuery({ terms: [{ column: 'id', termType: 'in', value: selectorValues?.toString() }]})
if (deviceResp.success && (deviceResp.result as any)?.total !== (selectorValues!.length)) {
data.value.trigger!.device!.selectorValues = undefined
return false
}
if (selectorValues!.length === 1) {
const deviceDetail = deviceResp?.result?.data?.[0]
metadata = JSON.parse(deviceDetail?.metadata || '{}') //
}
} else if (deviceTrigger.selector === 'org') { //
const orgResp = await getTreeData_api({
paging: false,
terms: [{ column: 'id', termType: 'eq', value: selectorValues![0] }]
})
if (orgResp.success && (orgResp.result as any[]).length !== selectorValues!.length) {
data.value.trigger!.device!.selectorValues = undefined
return false
}
}
//
if (['readProperty', 'writeProperty'].includes(deviceTrigger.operation?.operator!)) {
let hasProperties = false
if (metadata.properties.length) {
if (deviceTrigger.operation?.readProperties && deviceTrigger.operation?.readProperties.length) {
hasProperties = metadata.properties.every((item: any) => deviceTrigger.operation!.readProperties!.includes(item.id))
} else if (deviceTrigger.operation?.writeProperties && Object.keys(deviceTrigger.operation?.writeProperties).length) {
const key = Object.keys(deviceTrigger.operation?.writeProperties)[0]
hasProperties = metadata.properties.some((item: any) => key ===item.id)
}
}
if (!hasProperties) {
if (deviceTrigger.operation?.operator === 'readProperty') {
deviceTrigger.operation!.readProperties = []
} else {
deviceTrigger.operation!.writeProperties = {}
}
return false
}
}
if (deviceTrigger.operation?.operator === 'invokeFunction') {
let hasProperties = false
if (metadata.functions.length) {
const functionId = deviceTrigger.operation?.functionId
hasProperties = metadata.functions.some((item: any) => functionId ===item.id)
}
if (!hasProperties) {
deviceTrigger.operation.functionId = undefined
deviceTrigger.operation.functionParameters = []
return false
}
}
if (deviceTrigger.operation?.operator === 'reportEvent') {
let hasProperties = false
if (metadata.events.length) {
const eventId = deviceTrigger.operation.eventId
hasProperties = metadata.events.some((item: any) => eventId ===item.id)
}
if (!hasProperties) {
deviceTrigger.operation.eventId = undefined
return false
}
}
return true
}
const checkInit = async () => {
if (data.value.trigger?.device) {
const checkStatus = await check()
if (!checkStatus) {
formTouchOff()
}
}
}
checkInit()
</script>
<style scoped>
</style>

View File

@ -7,13 +7,15 @@
<template #label>
<TitleComponent data='触发规则' style='font-size: 14px;' />
</template>
<AddButton
style='width: 100%'
@click='visible = true'
>
<Title :options='data.options.trigger' />
</AddButton>
<AddModel v-if='visible' @cancel='visible = false' @save='save' :value='data.trigger.device' :options='data.options.trigger' />
<AddButton
style='width: 100%'
@click='visible = true'
>
<Title :options='data.options.trigger' />
</AddButton>
<AddModel v-if='visible' @cancel='visible = false' @save='save' :value='data.trigger.device' :options='data.options.trigger' />
<CheckItem />
</j-form-item>
<Terms />
</div>
@ -28,6 +30,7 @@ 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'
import CheckItem from './CheckItem.vue'
const sceneStore = useSceneStore()
const { data } = storeToRefs(sceneStore)
@ -38,6 +41,21 @@ const rules = [{
validator(_: any, v: any) {
if (!v) {
return Promise.reject(new Error('请配置设备触发规则'));
} else {
console.log('device-validator', v)
if (
!v.productId ||
(['fixed', 'org'].includes(v.selector) && !v.selectorValues) ||
(v.operation?.operator === 'readProperty' && !v.operation!.readProperties.length) ||
(v.operation?.operator === 'writeProperty' && !Object.keys(v.operation!.writeProperties).length) ||
(v.operation?.operator === 'invokeFunction' && !v.operation.functionId) ||
(v.operation?.operator === 'reportEvent' && !v.operation.eventId)
) {
return Promise.reject(new Error('该数据已发生变更,请重新配置'));
}
//
//
//
}
return Promise.resolve();
},

View File

@ -3,7 +3,7 @@
</template>
<script setup lang='ts' name='ActionCheckItem'>
import { ActionsType } from '@/views/rule-engine/Scene/typings'
import { ActionsType, FormModelType, PlatformRelation } from '@/views/rule-engine/Scene/typings'
import { useSceneStore } from '@/store/scene';
import { storeToRefs } from 'pinia';
import { queryProductList } from '@/api/device/product'
@ -11,7 +11,11 @@ import { query as deviceQuery } from '@/api/device/instance'
import noticeConfig from '@/api/notice/config'
import noticeTemplate from '@/api/notice/template'
import { Form } from 'jetlinks-ui-components'
import { EventEmitter, EventSubscribeKeys } from '@/views/rule-engine/Scene/Save/util'
import { EventEmitter, EventSubscribeKeys, getParams } from '@/views/rule-engine/Scene/Save/util'
import { getOption } from '@/views/rule-engine/Scene/Save/components/DropdownButton/util'
import { getBuildInData, getNotifyVariablesUser } from './util'
import { defineExpose } from 'vue'
const sceneStore = useSceneStore();
const { data: _data } = storeToRefs(sceneStore);
@ -34,21 +38,6 @@ const props = defineProps({
const sub = ref()
const rules = [{
validator(_: any, v?: ActionsType) {
if (v?.executor === 'device') {
if(
!v.device?.productId || //
!v.device?.selectorValues || //
(v.device.source === 'upper' && !v.device?.upperKey)
) {
return Promise.reject(new Error('该数据已发生变更,请重新配置'))
}
}
return Promise.resolve()
}
}]
const formTouchOff = () => {
formItemContext.onFieldChange()
}
@ -64,17 +53,29 @@ const checkDeviceDelete = async () => {
formTouchOff()
return
}
const productDetail = proResp?.result?.data?.[0]
let metadata = JSON.parse(productDetail?.metadata || '{}')
if (item?.selector === 'fixed') {
const deviceList = item!.selectorValues?.map(item => item.value) || []
const deviceResp = await deviceQuery({ terms: [{ terms: [{ column: 'id', termType: 'in', value: deviceList.toString() }]}]})
if (deviceResp.success && (deviceResp.result as any)?.total < (item!.selectorValues?.length || 0)) { //
let hasDevice = false
if (item!.selectorValues) {
const deviceList = item!.selectorValues?.map(item => item.value) || []
const deviceResp = await deviceQuery({ terms: [{ terms: [{ column: 'id', termType: 'in', value: deviceList.toString() }]}]})
hasDevice = deviceResp.success && (deviceResp.result as any)?.total === (item!.selectorValues?.length || 0)
if (item!.selectorValues!.length === 1 && hasDevice) {
const deviceDetail = deviceResp?.result?.data?.[0]
metadata = JSON.parse(deviceDetail?.metadata || '{}') //
}
}
if (!hasDevice) { //
_data.value.branches![props.branchesName].then[props.thenName].actions[props.name].device!.selectorValues = undefined
_data.value.branches![props.branchesName].then[props.thenName].actions[props.name].device!.changeData = true
formTouchOff()
return
}
}
console.log(item!.source, props.name)
if (item!.source === 'upper') { // id
} else if (item!.selector === 'context') { // id
if (props.name === 0) {
_data.value.branches![props.branchesName].then[props.thenName].actions[props.name].device!.upperKey = undefined
formTouchOff()
@ -83,10 +84,99 @@ const checkDeviceDelete = async () => {
const prevItem = _data.value.branches![props.branchesName].then[props.thenName].actions[props.name - 1].device
if (prevItem?.productId !== item?.productId) {
_data.value.branches![props.branchesName].then[props.thenName].actions[props.name].device!.upperKey = undefined
_data.value.branches![props.branchesName].then[props.thenName].actions[props.name].device!.changeData = true
formTouchOff()
return
} else {
const _upperKey = item!.upperKey
const _params = {
branch: props.thenName,
branchGroup: props.branchesName,
action: props.name - 1,
};
const upperData = await getParams(_params, unref(_data.value));
const option = getOption(upperData, _upperKey, 'id')
if (!option) {
_data.value.branches![props.branchesName].then[props.thenName].actions[props.name].device!.upperKey = undefined
_data.value.branches![props.branchesName].then[props.thenName].actions[props.name].device!.changeData = true
formTouchOff()
return
}
}
}
} else if (item!.selector === 'relation') {
const _relationValue = (item!.selectorValues?.[0]?.value as any)?.relation
const relationData = await noticeConfig.getRelationUsers({
paging: false,
sorts: [{ name: 'createTime', order: 'desc' }],
terms: [
{ termType: 'eq', column: 'objectTypeName', value: '设备' },
{ termType: 'eq', column: 'relation', value: _relationValue }
],
}).catch(() => ({ success: false, result: []}))
if (!relationData.result?.length ) {
_data.value.branches![props.branchesName].then[props.thenName].actions[props.name].device!.selectorValues = undefined
_data.value.branches![props.branchesName].then[props.thenName].actions[props.name].device!.changeData = true
formTouchOff()
return
}
} else if (item!.selector === 'tag') {
let hasAllTags = false
if (item!.selectorValues && metadata?.tags?.length) {
const values = (item!.selectorValues?.[0]?.value as any).map((item: any) => item.column)
const tagKeys = new Set(values)
hasAllTags = metadata?.tags?.every((item: any) => tagKeys.has(item.id))
}
if (!hasAllTags) {
_data.value.branches![props.branchesName].then[props.thenName].actions[props.name].device!.selectorValues = undefined
_data.value.branches![props.branchesName].then[props.thenName].actions[props.name].device!.changeData = true
formTouchOff()
return
}
}
if (item!.message!.messageType === 'READ_PROPERTY') {
let hasProperties = false
if (item!.message!.properties && metadata.properties.length) {
const propertiesKey = item!.message!.properties?.[0]
hasProperties = metadata.properties?.some((item: any) => item.id === propertiesKey)
}
if (!hasProperties) {
_data.value.branches![props.branchesName].then[props.thenName].actions[props.name].device!.message!.properties = []
_data.value.branches![props.branchesName].then[props.thenName].actions[props.name].device!.changeData = true
formTouchOff()
return
}
}
if (item!.message!.messageType === 'WRITE_PROPERTY') {
let hasProperties = false
if (item!.message!.properties && metadata.properties.length) {
const propertiesKey = Object.keys(item!.message!.properties!)?.[0]
hasProperties = metadata.properties?.some((item: any) => item.id === propertiesKey)
}
if (!hasProperties) {
_data.value.branches![props.branchesName].then[props.thenName].actions[props.name].device!.message!.properties = undefined
_data.value.branches![props.branchesName].then[props.thenName].actions[props.name].device!.changeData = true
formTouchOff()
return
}
}
if (item!.message!.messageType === 'INVOKE_FUNCTION') {
const functionId = item!.message!.functionId
let hasFunctions = false
if (functionId && metadata.functions.length) {
hasFunctions = metadata.functions?.some((item: any) => item.id === functionId)
}
if (!hasFunctions) {
_data.value.branches![props.branchesName].then[props.thenName].actions[props.name].device!.message!.functionId = undefined
_data.value.branches![props.branchesName].then[props.thenName].actions[props.name].device!.changeData = true
formTouchOff()
return
}
}
}
@ -95,18 +185,114 @@ const checkDeviceDelete = async () => {
*/
const checkNoticeDelete = async () => {
const item = _data.value.branches![props.branchesName].then[props.thenName].actions[props.name].notify
const _triggerType = _data.value.triggerType
const configResp = await noticeConfig.list({ terms: [{ terms: [{ column: 'id', termType: 'eq', value: item!.notifierId }]}]})
if (configResp.success && (configResp.result as any)?.total === 0) {
if (configResp.success && (configResp.result as any)?.total === 0) { //
_data.value.branches![props.branchesName].then[props.thenName].actions[props.name].notify!.notifierId = ''
formTouchOff()
return
}
const templateResp = await noticeTemplate.list({ terms: [{ terms: [{ column: 'id', termType: 'eq', value: item!.templateId }]}]})
if (templateResp.success && (templateResp.result as any)?.total === 0) {
if (templateResp.success && (templateResp.result as any)?.total === 0) { //
_data.value.branches![props.branchesName].then[props.thenName].actions[props.name].notify!.templateId = ''
formTouchOff()
return
}
const templateDetailResp = await noticeTemplate.getTemplateDetail(item!.templateId)
if (templateDetailResp.success) {
const variableDefinitionsMap = new Map()
const itemVariableKeys = Object.keys(item!.variables)
const notifyType = item!.notifyType
templateDetailResp.result.variableDefinitions.map((dItem: any) => {
variableDefinitionsMap.set(dItem.id, dItem.expands?.businessType)
})
//
let BuildInData: any = null
const hasAll = itemVariableKeys.every(async (variableKey: any) => {
if (variableDefinitionsMap.has(variableKey)) {
const itemData = item!.variables[variableKey]
if (itemData.source === 'upper') {
if (!BuildInData) {
const _params = {
branch: props.thenName,
branchGroup: props.branchesName,
action: props.name - 1,
};
BuildInData = await getBuildInData(_params, _data.value)
}
const option = BuildInData?.(itemData.upperKey!, 'id')
return !!option
} else if (itemData.source === 'fixed') {
const itemType = variableDefinitionsMap.get(variableKey)
let hasUser = false
if (itemType === 'user') { //
let resp = undefined;
if (notifyType === 'dingTalk') {
resp = await noticeConfig.queryDingTalkUsers(item!.notifierId);
} else {
resp = await noticeConfig.queryWechatUsers(item!.notifierId);
}
if (resp && resp.success) {
hasUser = resp.result.some((uItem: any) => uItem.id === itemData.value)
}
return hasUser
}
if (itemType === 'tag') { //
const resp = await noticeTemplate.getTags(item!.notifierId)
if (resp && resp.success) {
hasUser = resp.result.some((tag: any) => tag.id === itemData.value)
}
return hasUser
}
if (itemType === 'org') { //
let resp = undefined;
if (notifyType === 'dingTalk') {
resp = await noticeConfig.dingTalkDept(item!.notifierId)
} else {
resp = await noticeConfig.weChatDept(item!.notifierId)
}
if (resp && resp.success) {
hasUser = resp.result.some((org: any) => org.id === itemData.value)
}
return hasUser
}
} else if (itemData.source == 'relation') {
//
const userData = await getNotifyVariablesUser(_triggerType === 'device')
let hasUser = false
if (!hasUser && userData.platform.length) { //
hasUser = userData.platform.some(uItem => {
return uItem.id === (itemData.relation as PlatformRelation)?.objectId
})
}
if (!hasUser && userData.relation.length) { //
hasUser = userData.relation.some(uItem => {
return uItem.id === (itemData.relation as PlatformRelation)?.objectId
})
}
return hasUser
}
} else {
return false
}
return true
})
BuildInData = null
if (!hasAll) {
_data.value.branches![props.branchesName].then[props.thenName].actions[props.name].notify!.changeData! = true
formTouchOff()
return
}
}
}
const check = () => {
@ -133,6 +319,10 @@ subscribe()
check()
defineExpose({
formTouchOff
})
</script>
<style scoped>

View File

@ -5,7 +5,7 @@
:rules='rules'
>
<div class="actions-item">
<CheckItem v-bind='props'>
<CheckItem v-bind='props' ref='checkItemRef'>
<div class="item-options-warp">
<div class="item-options-type" @click="onAdd">
<img
@ -330,7 +330,7 @@
</j-popconfirm>
</CheckItem>
</div>
</j-form-item>
</j-form-item>
<template v-if="!isLast && type === 'serial'">
<div
:class="[
@ -361,7 +361,7 @@
</div>
</div>
</template>
<!-- 编辑 -->
<template v-if="visible">
<Modal
:name="name"
@ -373,6 +373,7 @@
@save="onSave"
/>
</template>
<!-- 编辑 -->
<template>
<ActionTypeComponent
v-bind="props"
@ -405,7 +406,6 @@ import FilterGroup from './FilterGroup.vue';
import { randomString } from '@/utils/utils'
import { EventEmitter, EventEmitterKeys } from '@/views/rule-engine/Scene/Save/util'
import CheckItem from './CheckItem.vue'
import { Form } from 'jetlinks-ui-components'
const sceneStore = useSceneStore();
const { data: _data } = storeToRefs(sceneStore);
@ -441,7 +441,7 @@ const props = defineProps({
});
const emit = defineEmits(['delete', 'update']);
const checkItemRef = ref()
const visible = ref<boolean>(false);
const triggerVisible = ref<boolean>(false);
const actionType = ref('');
@ -450,7 +450,6 @@ const eventEmitterKey = ref(EventEmitterKeys({
branchGroup: props.thenName,
action: props.name
}))
const formItemContext = Form.useInjectFormItemContext()
const termsOptions = computed(() => {
if (!props.parallel) {
//
@ -535,9 +534,8 @@ 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) //
};
@ -559,7 +557,22 @@ const rules = [{
validator(_: any, v?: ActionsType) {
console.log('validator-action-item',v)
if (v?.executor === 'device') {
if(v?.device?.source === 'fixed' && (!v.device?.productId || !v.device?.selectorValues)) {
const _device = v.device!
if (
(_device?.selector === 'fixed' && (!_device?.productId || !_device?.selectorValues?.length )) ||
(_device?.selector === 'context' && !_device?.upperKey) ||
(_device?.selector === 'relation' && !_device?.selectorValues?.length) ||
_device?.changeData === true
) {
return Promise.reject(new Error('该数据已发生变更,请重新配置'))
}
} else if (v?.executor === 'notify') {
const _notify = v.notify
if (
!_notify?.notifierId ||
!_notify?.templateId ||
_notify?.changeData === true
) {
return Promise.reject(new Error('该数据已发生变更,请重新配置'))
}
}

View File

@ -1,4 +1,7 @@
import { getImage } from '@/utils/comm'
import NoticeApi from '@/api/notice/config'
import { getParams } from '@/views/rule-engine/Scene/Save/util'
import { getOption } from '@/views/rule-engine/Scene/Save/components/DropdownButton/util'
export const iconMap = new Map();
iconMap.set('trigger', getImage('/scene/action-bind-icon.png'));
@ -26,3 +29,32 @@ export const typeIconMap = {
INVOKE_FUNCTION: 'icon-zhihangdongzuoxie-1',
WRITE_PROPERTY: 'icon-zhihangdongzuoxie',
};
export const getBuildInData = async (params: any, data: any) => {
const buildInData = await getParams(params, unref(data));
return function(upperKey: string, key: string ) {
return getOption(buildInData, upperKey, key)
}
}
export const getNotifyVariablesUser = (isRelationUser: boolean = false): Promise<{ platform: any[], relation: any[] }> => {
return new Promise(async (resolve) => {
let relationResp = undefined;
const platformResp = await NoticeApi.getPlatformUsers({
paging: false,
sorts: [{ name: 'name', order: 'asc' }],
});
if (isRelationUser) {
relationResp = await NoticeApi.getRelationUsers({
paging: false,
sorts: [{ name: 'name', order: 'asc' }],
});
}
resolve({
platform: platformResp.result || [],
relation: relationResp?.result || []
})
})
}

View File

@ -32,7 +32,7 @@
</j-modal>
</template>
<script lang="ts" setup>
<script lang="ts" setup name='UpdateActionItemModal'>
import { getImage } from '@/utils/comm';
import Delay from '../Delay/index.vue';
import Notify from '../Notify/index.vue';

View File

@ -32,6 +32,7 @@
<j-date-picker
:value="value.value"
allowClear
valueFormat='YYYY-MM-DD HH:mm:ss'
format="YYYY-MM-DD HH:mm:ss"
style="width: calc(100% - 120px)"
v-if="item.type === 'date'"

View File

@ -38,7 +38,7 @@
v-if="['email'].includes(notifyType)"
style="width: calc(100% - 120px)"
placeholder="请选择收信人"
@change="(key, label) => onChange(source, key, false, label)"
@change="(key, label) => onChange(source, key, label)"
:tree-data="treeData"
:multiple="true"
:dropdown-style="{ maxHeight: '400px', overflow: 'auto' }"
@ -66,7 +66,7 @@
style="width: calc(100% - 120px)"
placeholder="请选择收信人"
@change="
(key, label) => onChange(source, key, undefined, label)
(key, label) => onChange(source, key, label)
"
:tree-data="treeData"
:dropdown-style="{ maxHeight: '400px', overflow: 'auto' }"
@ -102,7 +102,6 @@
onChange(
source,
val,
false,
option?.label || option?.name,
)
"
@ -120,7 +119,6 @@
onChange(
source,
val,
false,
Array.isArray(val) ? val.join(',') : val,
)
"
@ -132,7 +130,7 @@
:value="value?.value"
@change="
(e) =>
onChange(source, e.target.value, false, e.target.value)
onChange(source, e.target.value, e.target.value)
"
></j-input>
</template>
@ -183,7 +181,13 @@ const triggerType = computed(() => {
const relationData = computed(() => {
const item = props.value;
if (item?.source === 'relation') {
if(notifyType.value === 'email'){
if(item && Array.isArray(item) && item.length){
if(item[0].source === 'relation'){
return item.map(i => i?.relation?.objectId)
}
}
} else if (item?.source === 'relation') {
const relation = item?.relation;
if (relation) {
if (relation.objectId) {
@ -268,14 +272,15 @@ const getUser = async (_source: string, _triggerType: string) => {
key: 'p2',
selectable: false,
children: relationResp.result.map((item: any) => {
treeDataMap.set(item.id, item)
return {
const obj = {
...item,
value: item.id,
key: item.id,
title: item.name,
isRelation: true,
};
}
treeDataMap.set(item.id, obj)
return obj
}),
});
}
@ -302,7 +307,7 @@ const getObj = (
objectType: 'device',
objectSource: {
source: 'upper',
upperKey: 'deviceId',
upperKey: 'scene.deviceId',
},
related: {
objectType: 'user',
@ -324,7 +329,7 @@ const getObj = (
const onChange = (
_source: string = 'fixed',
_value?: string | string[],
isRelation?: boolean,
// isRelation?: boolean,
_name?: string,
) => {
let _values: any = undefined;
@ -332,16 +337,15 @@ const onChange = (
const _names: string[] = Array.isArray(_name) ? _name : [_name || ''];
if (Array.isArray(_value)) {
if (props?.notify?.notifyType === 'email') {
if (isRelation) {
const arr = _value.map((item) => {
const _item = labelMap.get(item);
_names.push(_item?.name || '');
return getObj('relation', item, _item?.relation);
});
_values = arr;
} else {
_values = getObj(_source, _value, false);
}
_values = _value.map((item) => {
return {
source: "relation",
relation:{
objectType: "user",
objectId: item
}
}
});
}
} else {
const item = treeDataMap.get(_value)

View File

@ -20,7 +20,7 @@ export const getParams = (params: Params, sceneModel: FormModelType): Promise<an
if (resp.success) {
res(resp.result as any[])
}
})
}).catch(() => res([]))
})
}

View File

@ -42,6 +42,7 @@ export enum ActionDeviceSelector {
'fixed' = 'fixed',
'tag' = 'tag',
'relation' = 'relation',
'context' = 'context',
}
export enum ActionDeviceSource {
@ -236,6 +237,7 @@ export interface NotifyProps {
templateId: string;
variables: Record<string, NotifyVariablesType>;
options?: Record<string, any>;
changeData?: Boolean
}
export type SelectorValuesType =
@ -264,6 +266,7 @@ export interface ActionsDeviceProps {
upperKey?: string;
/** 来源为relation时不能为空 */
relation?: any;
changeData?: boolean
}
export interface BranchesThen {
@ -321,6 +324,7 @@ export interface FormModelType {
* ,
*/
options?: Record<string, any>;
triggerType?: 'device' | 'manual' | 'timer'
description?: string;
}

View File

@ -1913,6 +1913,7 @@ function changeBackUpload(info: UploadChangeParam<UploadFile<any>>) {
message.error('logo上传失败请稍后再试');
}
}
function clearNullProp(obj: object) {
if (typeof obj !== 'object') return;
for (const prop in obj) {
@ -1930,15 +1931,19 @@ function clearNullProp(obj: object) {
* @param value
*/
const validateIP = (_rule: Rule, value: string) => {
const ipList = value?.split(/[\n,]/g).filter((i: string) => i && i.trim());
const errorIPList = ipList.filter(
(f: string) => !testIP(f.replace(/\s*/g, '')),
);
return new Promise((resolve, reject) => {
!errorIPList.length
? resolve('')
: reject(`[${errorIPList}]不是正确的IP地址`);
});
if (value) {
const ipList = value?.split(/[\n,]/g).filter((i: string) => i && i.trim());
const errorIPList = ipList?.filter(
(f: string) => !testIP(f.replace(/\s*/g, '')),
);
return new Promise((resolve, reject) => {
!errorIPList?.length
? resolve('')
: reject(`[${errorIPList}]不是正确的IP地址`);
});
} else {
return Promise.resolve()
}
};
</script>

View File

@ -48,7 +48,18 @@
<j-form-item name="base-path">
<template #label>
<span>base-path</span>
<j-tooltip title="系统后台访问的url">
<j-tooltip >
<template #title>
<div style='word-break: break-all;'>
<div>
系统后台访问的url
</div>
<div>
格式{http/https}: //{IP}:{}/api
</div>
</div>
</template>
<img
class="img-style"
:src="
@ -59,7 +70,7 @@
</template>
<j-input
v-model:value="formValue['base-path']"
placeholder="输入base-path"
placeholder="{http/https}: //{前端所在服务器IP地址}:{前端暴露的服务端口}/api"
/>
</j-form-item>
<j-row :gutter="24" :span="24">

View File

@ -2,7 +2,7 @@
<div class="product-container">
<pro-search
:columns="columns"
target="category"
target="category-device"
@search="(params:any)=>queryParams = {...params}"
style='margin-bottom: 0;'
/>

View File

@ -2,7 +2,7 @@
<div class="product-container">
<pro-search
:columns="columns"
target="category"
target="category-product"
@search="(params:any)=>queryParams = {...params}"
style='margin-bottom: 0;'
/>

View File

@ -2,7 +2,7 @@
<div>
<pro-search
:columns="columns"
target="category"
target="category-user"
@search="handleParams"
style='margin-bottom: 0;'
/>

View File

@ -149,7 +149,7 @@
:model="form.data"
class="basic-form permiss-form"
>
<j-form-item name="accessSupport" required>
<j-form-item name="accessSupport" required v-if="isNoCommunity">
<template #label>
<span style="margin-right: 3px">数据权限控制</span>
<j-tooltip title="此菜单页面数据所对应的资产类型">
@ -263,7 +263,6 @@ import { FormInstance } from 'ant-design-vue';
import { message } from 'jetlinks-ui-components';
import ChooseIconDialog from '../components/ChooseIconDialog.vue';
import PermissChoose from '../components/PermissChoose.vue';
import {
getMenuTree_api,
getAssetsType_api,
@ -273,6 +272,7 @@ import {
validCode_api,
} from '@/api/system/menu';
import { Rule } from 'ant-design-vue/lib/form';
import { isNoCommunity } from '@/utils/utils';
const permission = 'system/Menu';
//

View File

@ -38,6 +38,7 @@ import {
import { message } from 'jetlinks-ui-components';
import { modeType } from '../typing';
import { useDepartmentStore } from '@/store/department';
import { onlyMessage } from '@/utils/comm';
const department = useDepartmentStore();
const emits = defineEmits([
@ -136,11 +137,16 @@ const save = async () => {
// emits('refresh');
// });
// fix: bug#10829
removeKeys.length && (await delOperations_api(removeKeys));
const res = await addOperations_api(addKeys);
if (res.success) {
message.success('操作成功');
emits('refresh');
if(addKeys.length || removeKeys.length) {
removeKeys.length && (await delOperations_api(removeKeys));
const res = await addOperations_api(addKeys);
if (res.success) {
message.success('操作成功');
emits('refresh');
}
} else {
onlyMessage('请选择API接口','error')
return
}
} else if (props.mode === 'appManger') {
const removeItems = removeKeys.map((key) => ({

View File

@ -109,6 +109,7 @@ import {
USER_CENTER_MENU_CODE,
MESSAGE_SUBSCRIBE_MENU_CODE
} from '@/utils/consts'
import { isNoCommunity } from '@/utils/utils'
const emits = defineEmits(['update:selectItems']);
const route = useRoute();
@ -117,6 +118,7 @@ const props = defineProps({
});
const treeRef = ref();
let { ctx: that, proxy } = getCurrentInstance();
const columns = [
{
title: '菜单权限',
@ -130,13 +132,16 @@ const columns = [
key: 'action',
width: '260px',
},
{
];
if(isNoCommunity){
columns.push({
title: '数据权限',
dataIndex: 'data',
key: 'data',
width: '50%',
},
];
})
}
const tableData = ref<tableItemType[]>([]);
// -

View File

@ -270,6 +270,7 @@ type modalType = '' | 'add' | 'edit' | 'reset';
const handleParams = (params: any) => {
const newParams = (params?.terms as any[])?.map((item1) => {
let arr: any[] = []
item1.terms = item1.terms.map((item2: any) => {
if (['telephone', 'email'].includes(item2.column)) {
return {
@ -277,8 +278,27 @@ const handleParams = (params: any) => {
value: [item2],
};
}
if (['type'].includes(item2.column) && item2.value === 'other') {
arr = [
{
...item2,
type: 'or',
termType: 'isnull',
value: 1,
},
{
...item2,
type: 'or',
termType: 'empty',
value: 1,
}
]
}
return item2;
});
if(arr.length){
item1.terms = [...item1.terms, ...arr]
}
return item1;
});
queryParams.value = { terms: newParams || [] };

View File

@ -3823,10 +3823,10 @@ jetlinks-store@^0.0.3:
resolved "https://registry.npmjs.org/jetlinks-store/-/jetlinks-store-0.0.3.tgz"
integrity sha512-AZf/soh1hmmwjBZ00fr1emuMEydeReaI6IBTGByQYhTmK1Zd5pQAxC7WLek2snRAn/HHDgJfVz2hjditKThl6Q==
jetlinks-ui-components@^1.0.13:
version "1.0.13"
resolved "https://registry.jetlinks.cn/jetlinks-ui-components/-/jetlinks-ui-components-1.0.13.tgz#9f33fe41d9c453b4db01a3d5e0a86412c13cf652"
integrity sha512-50QFseYmoN1OnvwbMI735q3MlUWW8tASGScfxUKDCD4c7EBr/tgU9exdW8i++ggbjvTfa8d3Lhg+L2gggLtnUQ==
jetlinks-ui-components@^1.0.16:
version "1.0.16"
resolved "https://registry.jetlinks.cn/jetlinks-ui-components/-/jetlinks-ui-components-1.0.16.tgz#bdb65385a30a121065322e5156c13080c8328080"
integrity sha512-R3oE8tpXW4oaNSCeGXRK++paNJiHYDO89Id3YqzIVX6/bWMItOWrEU6JT4iPA9uYkPTfsYHxnG5qZRloLnpiZw==
dependencies:
"@vueuse/core" "^9.12.0"
"@vueuse/router" "^9.13.0"