fix: 修复设备接入更换接入方式,参数名称未发生变化问题
This commit is contained in:
parent
285d108bbc
commit
73e527f12c
|
@ -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.
|
@ -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;
|
||||
|
|
|
@ -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%) {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<pro-search
|
||||
class="device-search"
|
||||
class="device-running-search"
|
||||
type="simple"
|
||||
:columns="columns"
|
||||
target="device-instance-running-events"
|
||||
|
@ -45,7 +45,7 @@ const events = defineProps({
|
|||
});
|
||||
const instanceStore = useInstanceStore();
|
||||
|
||||
const columns = ref<Record<string, any>>([
|
||||
const defaultColumns = [
|
||||
{
|
||||
title: '时间',
|
||||
dataIndex: 'timestamp',
|
||||
|
@ -60,8 +60,10 @@ const columns = ref<Record<string, any>>([
|
|||
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 {
|
||||
|
|
|
@ -61,7 +61,7 @@
|
|||
<div class="card-item-content-text">
|
||||
设备类型
|
||||
</div>
|
||||
<div>直连设备</div>
|
||||
<div>{{ slotProps?.deviceType?.text }}</div>
|
||||
</j-col>
|
||||
</j-row>
|
||||
</template>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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); //
|
||||
|
|
|
@ -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,6 +45,7 @@
|
|||
</j-table>
|
||||
</div>
|
||||
<div class='right'>
|
||||
<j-scrollbar>
|
||||
<div class='title'>
|
||||
功能说明
|
||||
</div>
|
||||
|
@ -60,6 +66,7 @@
|
|||
<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
|
||||
}
|
||||
]
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
});
|
||||
|
|
|
@ -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 || [];
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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(['接入配置', '消息协议', '完成']);
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -530,7 +530,7 @@ const props = defineProps({
|
|||
data: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
const route = useRoute();
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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[] = []
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -207,9 +207,11 @@ watch(
|
|||
* 部门点击
|
||||
*/
|
||||
const onTreeSelect = (keys: any) => {
|
||||
if (keys.length) {
|
||||
deptId.value = keys[0];
|
||||
pageSize.value = 10;
|
||||
current.value = 1;
|
||||
}
|
||||
};
|
||||
|
||||
// 右侧表格
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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>
|
|
@ -7,6 +7,7 @@
|
|||
<template #label>
|
||||
<TitleComponent data='触发规则' style='font-size: 14px;' />
|
||||
</template>
|
||||
|
||||
<AddButton
|
||||
style='width: 100%'
|
||||
@click='visible = true'
|
||||
|
@ -14,6 +15,7 @@
|
|||
<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();
|
||||
},
|
||||
|
|
|
@ -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') {
|
||||
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() }]}]})
|
||||
if (deviceResp.success && (deviceResp.result as any)?.total < (item!.selectorValues?.length || 0)) { // 某一个设备被删除
|
||||
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,11 +84,100 @@ 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>
|
||||
|
|
|
@ -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
|
||||
|
@ -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('该数据已发生变更,请重新配置'))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 || []
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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'"
|
||||
|
|
|
@ -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,17 +337,16 @@ 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)
|
||||
const _isRelation = item?.isRelation
|
||||
|
|
|
@ -20,7 +20,7 @@ export const getParams = (params: Params, sceneModel: FormModelType): Promise<an
|
|||
if (resp.success) {
|
||||
res(resp.result as any[])
|
||||
}
|
||||
})
|
||||
}).catch(() => res([]))
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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) => {
|
||||
if (value) {
|
||||
const ipList = value?.split(/[\n,]/g).filter((i: string) => i && i.trim());
|
||||
const errorIPList = ipList.filter(
|
||||
const errorIPList = ipList?.filter(
|
||||
(f: string) => !testIP(f.replace(/\s*/g, '')),
|
||||
);
|
||||
return new Promise((resolve, reject) => {
|
||||
!errorIPList.length
|
||||
!errorIPList?.length
|
||||
? resolve('')
|
||||
: reject(`[${errorIPList}]不是正确的IP地址`);
|
||||
});
|
||||
} else {
|
||||
return Promise.resolve()
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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;'
|
||||
/>
|
||||
|
|
|
@ -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;'
|
||||
/>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<div>
|
||||
<pro-search
|
||||
:columns="columns"
|
||||
target="category"
|
||||
target="category-user"
|
||||
@search="handleParams"
|
||||
style='margin-bottom: 0;'
|
||||
/>
|
||||
|
|
|
@ -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';
|
||||
// 路由
|
||||
|
|
|
@ -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,12 +137,17 @@ const save = async () => {
|
|||
// emits('refresh');
|
||||
// });
|
||||
// fix: bug#10829
|
||||
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) => ({
|
||||
id: key,
|
||||
|
|
|
@ -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[]>([]);
|
||||
|
||||
// 表头-全选
|
||||
|
|
|
@ -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 || [] };
|
||||
|
|
|
@ -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"
|
||||
|
|
Loading…
Reference in New Issue