fix: 修改北向输出
This commit is contained in:
parent
23aff419a3
commit
d70956e1e1
|
@ -29,7 +29,7 @@
|
|||
v-model:value="myValue"
|
||||
>
|
||||
<template #addonAfter>
|
||||
<form-outlined @click="modalVis = true" />
|
||||
<AIcon type="FormOutlined" @click="modalVis = true" />
|
||||
</template>
|
||||
</j-input>
|
||||
<GeoComponent
|
||||
|
@ -50,7 +50,7 @@
|
|||
:showUploadList="false"
|
||||
@change="handleFileChange"
|
||||
>
|
||||
<cloud-upload-outlined />
|
||||
<AIcon type="CloudUploadOutlined" />
|
||||
</j-upload>
|
||||
</template>
|
||||
</j-input>
|
||||
|
|
|
@ -15,6 +15,7 @@ import Ellipsis from './Ellipsis/index.vue'
|
|||
import JEmpty from './Empty/index.vue'
|
||||
import AMapComponent from './AMapComponent/index.vue'
|
||||
import PathSimplifier from './AMapComponent/PathSimplifier.vue'
|
||||
import ValueItem from './ValueItem/index.vue'
|
||||
|
||||
export default {
|
||||
install(app: App) {
|
||||
|
@ -35,5 +36,6 @@ export default {
|
|||
.component('JEmpty', JEmpty)
|
||||
.component('AMapComponent', AMapComponent)
|
||||
.component('PathSimplifier', PathSimplifier)
|
||||
.component('ValueItem', ValueItem)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
在特定场景下,设备无法直接接入阿里云物联网平台时,您可先将设备接入物联网平台,再使用阿里云“云云对接SDK”,快速构建桥接服务,搭建物联网平台与阿里云物联网平台的双向数据通道。
|
||||
</div>
|
||||
<div class="image">
|
||||
<a-image width="100%" :src="getImage('/northbound/aliyun2.png')" />
|
||||
<j-image width="100%" :src="getImage('/northbound/aliyun2.png')" />
|
||||
</div>
|
||||
<h1>2.配置说明</h1>
|
||||
<div>
|
||||
|
@ -26,14 +26,14 @@
|
|||
</div>
|
||||
<div>获取路径:“阿里云物联网平台”--“服务地址”</div>
|
||||
<div class="image">
|
||||
<a-image width="100%" :src="getImage('/northbound/aliyun3.png')" />
|
||||
<j-image width="100%" :src="getImage('/northbound/aliyun3.png')" />
|
||||
</div>
|
||||
<h2> 2、AccesskeyID/Secret</h2>
|
||||
<div>
|
||||
用于程序通知方式调用云服务费API的用户标识和秘钥获取路径:“阿里云管理控制台”--“用户头像”--“”--“AccessKey管理”--“查看”
|
||||
</div>
|
||||
<div class="image">
|
||||
<a-image width="100%" :src="getImage('/northbound/aliyun1.jpg')" />
|
||||
<j-image width="100%" :src="getImage('/northbound/aliyun1.jpg')" />
|
||||
</div>
|
||||
<h2> 3. 网桥产品</h2>
|
||||
<div>
|
||||
|
@ -44,7 +44,7 @@
|
|||
将阿里云物联网平台中的产品实例与物联网平台的产品实例进行关联。关联后需要进入该产品下的每一个设备的实例信息页,填入对应的阿里云物联网平台设备的DeviceName、DeviceSecret进行一对一绑定。
|
||||
</div>
|
||||
<div class="image">
|
||||
<a-image width="100%" :src="getImage('/northbound/aliyun4.png')" />
|
||||
<j-image width="100%" :src="getImage('/northbound/aliyun4.png')" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
<template>
|
||||
<page-container>
|
||||
<a-card>
|
||||
<a-row :gutter="24">
|
||||
<a-col :span="16">
|
||||
<j-card>
|
||||
<j-row :gutter="24">
|
||||
<j-col :span="16">
|
||||
<TitleComponent data="基本信息" />
|
||||
<a-form
|
||||
<j-form
|
||||
:layout="'vertical'"
|
||||
ref="formRef"
|
||||
:model="modelRef"
|
||||
>
|
||||
<a-row :gutter="24">
|
||||
<a-col :span="24">
|
||||
<a-form-item
|
||||
<j-row :gutter="24">
|
||||
<j-col :span="24">
|
||||
<j-form-item
|
||||
label="名称"
|
||||
name="name"
|
||||
:rules="[
|
||||
|
@ -25,14 +25,14 @@
|
|||
},
|
||||
]"
|
||||
>
|
||||
<a-input
|
||||
<j-input
|
||||
placeholder="请输入名称"
|
||||
v-model:value="modelRef.name"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="24">
|
||||
<a-form-item
|
||||
</j-form-item>
|
||||
</j-col>
|
||||
<j-col :span="24">
|
||||
<j-form-item
|
||||
:name="['accessConfig', 'regionId']"
|
||||
:rules="[
|
||||
{
|
||||
|
@ -44,63 +44,62 @@
|
|||
<template #label>
|
||||
<span>
|
||||
服务地址
|
||||
<a-tooltip
|
||||
<j-tooltip
|
||||
title="阿里云内部给每台机器设置的唯一编号"
|
||||
>
|
||||
<AIcon
|
||||
type="QuestionCircleOutlined"
|
||||
style="margin-left: 2px"
|
||||
/>
|
||||
</a-tooltip>
|
||||
</j-tooltip>
|
||||
</span>
|
||||
</template>
|
||||
<a-select
|
||||
<j-select
|
||||
placeholder="请选择服务地址"
|
||||
v-model:value="
|
||||
modelRef.accessConfig.regionId
|
||||
"
|
||||
show-search
|
||||
:filter-option="filterOption"
|
||||
@blur="productChange"
|
||||
>
|
||||
<a-select-option
|
||||
<j-select-option
|
||||
v-for="item in regionsList"
|
||||
:key="item.id"
|
||||
:value="item.id"
|
||||
:label="item.name"
|
||||
>{{ item.name }}</a-select-option
|
||||
>{{ item.name }}</j-select-option
|
||||
>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="24">
|
||||
<a-form-item
|
||||
</j-select>
|
||||
</j-form-item>
|
||||
</j-col>
|
||||
<j-col :span="24">
|
||||
<j-form-item
|
||||
:name="['accessConfig', 'instanceId']"
|
||||
>
|
||||
<template #label>
|
||||
<span>
|
||||
实例ID
|
||||
<a-tooltip
|
||||
<j-tooltip
|
||||
title="阿里云物联网平台中的实例ID,没有则不填"
|
||||
>
|
||||
<AIcon
|
||||
type="QuestionCircleOutlined"
|
||||
style="margin-left: 2px"
|
||||
/>
|
||||
</a-tooltip>
|
||||
</j-tooltip>
|
||||
</span>
|
||||
</template>
|
||||
<a-input
|
||||
<j-input
|
||||
placeholder="请输入实例ID"
|
||||
v-model:value="
|
||||
modelRef.accessConfig.instanceId
|
||||
"
|
||||
@blur="productChange"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="24">
|
||||
<a-form-item
|
||||
</j-form-item>
|
||||
</j-col>
|
||||
<j-col :span="24">
|
||||
<j-form-item
|
||||
:name="['accessConfig', 'accessKeyId']"
|
||||
:rules="[
|
||||
{
|
||||
|
@ -116,27 +115,27 @@
|
|||
<template #label>
|
||||
<span>
|
||||
accessKey
|
||||
<a-tooltip
|
||||
<j-tooltip
|
||||
title="用于程序通知方式调用云服务API的用户标识"
|
||||
>
|
||||
<AIcon
|
||||
type="QuestionCircleOutlined"
|
||||
style="margin-left: 2px"
|
||||
/>
|
||||
</a-tooltip>
|
||||
</j-tooltip>
|
||||
</span>
|
||||
</template>
|
||||
<a-input
|
||||
<j-input
|
||||
placeholder="请输入accessKey"
|
||||
v-model:value="
|
||||
modelRef.accessConfig.accessKeyId
|
||||
"
|
||||
@blur="productChange"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="24">
|
||||
<a-form-item
|
||||
</j-form-item>
|
||||
</j-col>
|
||||
<j-col :span="24">
|
||||
<j-form-item
|
||||
:name="['accessConfig', 'accessSecret']"
|
||||
:rules="[
|
||||
{
|
||||
|
@ -152,27 +151,27 @@
|
|||
<template #label>
|
||||
<span>
|
||||
accessSecret
|
||||
<a-tooltip
|
||||
<j-tooltip
|
||||
title="用于程序通知方式调用云服务费API的秘钥标识"
|
||||
>
|
||||
<AIcon
|
||||
type="QuestionCircleOutlined"
|
||||
style="margin-left: 2px"
|
||||
/>
|
||||
</a-tooltip>
|
||||
</j-tooltip>
|
||||
</span>
|
||||
</template>
|
||||
<a-input
|
||||
<j-input
|
||||
placeholder="请输入accessSecret"
|
||||
v-model:value="
|
||||
modelRef.accessConfig.accessSecret
|
||||
"
|
||||
@blur="productChange"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="24">
|
||||
<a-form-item
|
||||
</j-form-item>
|
||||
</j-col>
|
||||
<j-col :span="24">
|
||||
<j-form-item
|
||||
name="bridgeProductKey"
|
||||
:rules="{
|
||||
required: true,
|
||||
|
@ -182,44 +181,43 @@
|
|||
<template #label>
|
||||
<span>
|
||||
网桥产品
|
||||
<a-tooltip
|
||||
<j-tooltip
|
||||
title="物联网平台对应的阿里云产品"
|
||||
>
|
||||
<AIcon
|
||||
type="QuestionCircleOutlined"
|
||||
style="margin-left: 2px"
|
||||
/>
|
||||
</a-tooltip>
|
||||
</j-tooltip>
|
||||
</span>
|
||||
</template>
|
||||
<a-select
|
||||
<j-select
|
||||
placeholder="请选择网桥产品"
|
||||
v-model:value="
|
||||
modelRef.bridgeProductKey
|
||||
"
|
||||
show-search
|
||||
:filter-option="filterOption"
|
||||
>
|
||||
<a-select-option
|
||||
<j-select-option
|
||||
v-for="item in aliyunProductList"
|
||||
:key="item.productKey"
|
||||
:value="item.productKey"
|
||||
:label="item.productName"
|
||||
>{{
|
||||
item.productName
|
||||
}}</a-select-option
|
||||
}}</j-select-option
|
||||
>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="24">
|
||||
</j-select>
|
||||
</j-form-item>
|
||||
</j-col>
|
||||
<j-col :span="24">
|
||||
<p>产品映射</p>
|
||||
<a-collapse
|
||||
<j-collapse
|
||||
v-if="modelRef.mappings.length"
|
||||
:activeKey="activeKey"
|
||||
@change="onCollChange"
|
||||
>
|
||||
<a-collapse-panel
|
||||
<j-collapse-panel
|
||||
v-for="(
|
||||
item, index
|
||||
) in modelRef.mappings"
|
||||
|
@ -239,9 +237,9 @@
|
|||
type="DeleteOutlined"
|
||||
@click="delItem(index)"
|
||||
/></template>
|
||||
<a-row :gutter="24">
|
||||
<a-col :span="12">
|
||||
<a-form-item
|
||||
<j-row :gutter="24">
|
||||
<j-col :span="12">
|
||||
<j-form-item
|
||||
label="阿里云产品"
|
||||
:name="[
|
||||
'mappings',
|
||||
|
@ -254,19 +252,16 @@
|
|||
'请选择阿里云产品',
|
||||
}"
|
||||
>
|
||||
<a-select
|
||||
<j-select
|
||||
placeholder="请选择阿里云产品"
|
||||
v-model:value="
|
||||
item.productKey
|
||||
"
|
||||
show-search
|
||||
:filter-option="
|
||||
filterOption
|
||||
"
|
||||
>
|
||||
<a-select-option
|
||||
<j-select-option
|
||||
v-for="i in getAliyunProductList(
|
||||
item.productKey,
|
||||
item?.productKey || ''
|
||||
)"
|
||||
:key="i.productKey"
|
||||
:value="
|
||||
|
@ -277,13 +272,13 @@
|
|||
"
|
||||
>{{
|
||||
i.productName
|
||||
}}</a-select-option
|
||||
}}</j-select-option
|
||||
>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item
|
||||
</j-select>
|
||||
</j-form-item>
|
||||
</j-col>
|
||||
<j-col :span="12">
|
||||
<j-form-item
|
||||
label="平台产品"
|
||||
:name="[
|
||||
'mappings',
|
||||
|
@ -296,36 +291,36 @@
|
|||
'请选择平台产品',
|
||||
}"
|
||||
>
|
||||
<a-select
|
||||
<j-select
|
||||
placeholder="请选择平台产品"
|
||||
v-model:value="
|
||||
item.productId
|
||||
"
|
||||
show-search
|
||||
:filter-option="
|
||||
filterOption
|
||||
"
|
||||
>
|
||||
<a-select-option
|
||||
<j-select-option
|
||||
v-for="i in getPlatProduct(
|
||||
item.productId,
|
||||
item.productId || ''
|
||||
)"
|
||||
:key="i.id"
|
||||
:value="item.id"
|
||||
:value="i?.id"
|
||||
:label="i.name"
|
||||
>{{
|
||||
i.name
|
||||
}}</a-select-option
|
||||
}}</j-select-option
|
||||
>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-collapse-panel>
|
||||
</a-collapse>
|
||||
</a-col>
|
||||
<a-col :span="24">
|
||||
<a-button
|
||||
</j-select>
|
||||
</j-form-item>
|
||||
</j-col>
|
||||
</j-row>
|
||||
</j-collapse-panel>
|
||||
</j-collapse>
|
||||
<j-card v-else>
|
||||
<j-empty />
|
||||
</j-card>
|
||||
</j-col>
|
||||
<j-col :span="24">
|
||||
<j-button
|
||||
type="dashed"
|
||||
style="width: 100%; margin-top: 10px"
|
||||
@click="addItem"
|
||||
|
@ -334,10 +329,10 @@
|
|||
type="PlusOutlined"
|
||||
style="margin-left: 2px"
|
||||
/>添加
|
||||
</a-button>
|
||||
</a-col>
|
||||
<a-col :span="24" style="margin-top: 20px">
|
||||
<a-form-item
|
||||
</j-button>
|
||||
</j-col>
|
||||
<j-col :span="24" style="margin-top: 20px">
|
||||
<j-form-item
|
||||
label="说明"
|
||||
name="description"
|
||||
:rules="{
|
||||
|
@ -345,16 +340,16 @@
|
|||
message: '最多输入200个字符',
|
||||
}"
|
||||
>
|
||||
<a-textarea
|
||||
<j-textarea
|
||||
v-model:value="modelRef.description"
|
||||
placeholder="请输入说明"
|
||||
showCount
|
||||
:maxlength="200"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-form>
|
||||
</j-form-item>
|
||||
</j-col>
|
||||
</j-row>
|
||||
</j-form>
|
||||
<div v-if="type === 'edit'">
|
||||
<PermissionButton
|
||||
type="primary"
|
||||
|
@ -365,12 +360,12 @@
|
|||
保存
|
||||
</PermissionButton>
|
||||
</div>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
</j-col>
|
||||
<j-col :span="8">
|
||||
<Doc />
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-card>
|
||||
</j-col>
|
||||
</j-row>
|
||||
</j-card>
|
||||
</page-container>
|
||||
</template>
|
||||
|
||||
|
@ -384,7 +379,7 @@ import {
|
|||
queryProductList,
|
||||
} from '@/api/northbound/alicloud';
|
||||
import _ from 'lodash';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { message } from 'jetlinks-ui-components';
|
||||
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
|
@ -430,10 +425,6 @@ const loading = ref<boolean>(false);
|
|||
const type = ref<'edit' | 'view'>('edit');
|
||||
const activeKey = ref<string[]>(['0']);
|
||||
|
||||
const filterOption = (input: string, option: any) => {
|
||||
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0;
|
||||
};
|
||||
|
||||
const queryRegionsList = async () => {
|
||||
const resp = await getRegionsList();
|
||||
if (resp.status === 200) {
|
||||
|
@ -503,8 +494,9 @@ const saveBtn = () => {
|
|||
);
|
||||
data.bridgeProductName = product?.productName || '';
|
||||
loading.value = true;
|
||||
const resp = await savePatch({...toRaw(modelRef), ...data});
|
||||
loading.value = false;
|
||||
const resp = await savePatch({...toRaw(modelRef), ...data}).finally(() => {
|
||||
loading.value = false;
|
||||
})
|
||||
if (resp.status === 200) {
|
||||
message.success('操作成功!');
|
||||
formRef.value.resetFields();
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
<template>
|
||||
<page-container>
|
||||
<Search
|
||||
<j-advanced-search
|
||||
:columns="columns"
|
||||
target="northbound-aliyun"
|
||||
@search="handleSearch"
|
||||
/>
|
||||
<JTable
|
||||
<JProTable
|
||||
ref="instanceRef"
|
||||
:columns="columns"
|
||||
:request="query"
|
||||
|
@ -13,7 +13,7 @@
|
|||
:params="params"
|
||||
>
|
||||
<template #headerTitle>
|
||||
<a-space>
|
||||
<j-space>
|
||||
<PermissionButton
|
||||
type="primary"
|
||||
@click="handleAdd"
|
||||
|
@ -22,7 +22,7 @@
|
|||
<template #icon><AIcon type="PlusOutlined" /></template>
|
||||
新增
|
||||
</PermissionButton>
|
||||
</a-space>
|
||||
</j-space>
|
||||
</template>
|
||||
<template #card="slotProps">
|
||||
<CardBox
|
||||
|
@ -45,20 +45,20 @@
|
|||
>
|
||||
{{ slotProps.name }}
|
||||
</h3>
|
||||
<a-row>
|
||||
<a-col :span="12">
|
||||
<j-row>
|
||||
<j-col :span="12">
|
||||
<div class="card-item-content-text">
|
||||
网桥产品
|
||||
</div>
|
||||
<div>{{ slotProps?.bridgeProductName }}</div>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
</j-col>
|
||||
<j-col :span="12">
|
||||
<div class="card-item-content-text">
|
||||
<label>说明</label>
|
||||
</div>
|
||||
<div>{{ slotProps?.description }}</div>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</j-col>
|
||||
</j-row>
|
||||
</template>
|
||||
<template #actions="item">
|
||||
<PermissionButton
|
||||
|
@ -81,13 +81,13 @@
|
|||
</CardBox>
|
||||
</template>
|
||||
<template #state="slotProps">
|
||||
<a-badge
|
||||
<j-badge
|
||||
:text="slotProps.state?.text"
|
||||
:status="statusMap.get(slotProps.state?.value)"
|
||||
/>
|
||||
</template>
|
||||
<template #action="slotProps">
|
||||
<a-space>
|
||||
<j-space>
|
||||
<template
|
||||
v-for="i in getActions(slotProps, 'table')"
|
||||
:key="i.key"
|
||||
|
@ -104,23 +104,21 @@
|
|||
<template #icon><AIcon :type="i.icon" /></template>
|
||||
</PermissionButton>
|
||||
</template>
|
||||
</a-space>
|
||||
</j-space>
|
||||
</template>
|
||||
</JTable>
|
||||
</JProTable>
|
||||
</page-container>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { query, _undeploy, _deploy, _delete } from '@/api/northbound/alicloud';
|
||||
import type { ActionsType } from '@/components/Table/index.vue';
|
||||
import type { ActionsType } from '@/views/device/Instance/typings'
|
||||
import { getImage } from '@/utils/comm';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { message } from 'jetlinks-ui-components';
|
||||
import { useMenuStore } from 'store/menu';
|
||||
|
||||
const router = useRouter();
|
||||
const instanceRef = ref<Record<string, any>>({});
|
||||
const params = ref<Record<string, any>>({});
|
||||
const current = ref<Record<string, any>>({});
|
||||
|
||||
const menuStory = useMenuStore();
|
||||
|
||||
|
@ -149,6 +147,9 @@ const columns = [
|
|||
title: '说明',
|
||||
dataIndex: 'describe',
|
||||
key: 'describe',
|
||||
search: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
|
|
|
@ -40,7 +40,7 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { PropType } from "vue-demi";
|
||||
import { PropType } from "vue";
|
||||
|
||||
|
||||
type Emits = {
|
||||
|
|
|
@ -9,8 +9,8 @@
|
|||
<j-form-item name="messageType" label="指令类型" :rules="{
|
||||
required: true,
|
||||
message: '请选择指令类型',
|
||||
}">
|
||||
<j-select placeholder="请选择指令类型" v-model:value="modelRef.messageType" show-search :filter-option="filterOption">
|
||||
}" class="other">
|
||||
<j-select placeholder="请选择指令类型" v-model:value="modelRef.messageType" show-search>
|
||||
<j-select-option value="READ_PROPERTY">读取属性</j-select-option>
|
||||
<j-select-option value="WRITE_PROPERTY">修改属性</j-select-option>
|
||||
<j-select-option value="INVOKE_FUNCTION">调用功能</j-select-option>
|
||||
|
@ -22,7 +22,7 @@
|
|||
required: true,
|
||||
message: '请选择属性',
|
||||
}">
|
||||
<j-select placeholder="请选择属性" v-model:value="modelRef.message.properties" show-search :filter-option="filterOption">
|
||||
<j-select placeholder="请选择属性" v-model:value="modelRef.message.properties" show-search @change="onPropertyChange">
|
||||
<j-select-option v-for="i in (metadata?.properties) || []" :key="i.id" :value="i.id" :label="i.name">{{i.name}}</j-select-option>
|
||||
</j-select>
|
||||
</j-form-item>
|
||||
|
@ -32,7 +32,27 @@
|
|||
required: true,
|
||||
message: '请输入值',
|
||||
}">
|
||||
<j-input />
|
||||
<ValueItem
|
||||
v-model:modelValue="modelRef.message.value"
|
||||
:itemType="property.type || property.valueType?.type || 'int'"
|
||||
:options="
|
||||
property.valueType?.type === 'enum'
|
||||
? (property?.dataType?.elements || []).map(
|
||||
(item) => {
|
||||
return {
|
||||
label: item?.text,
|
||||
value: item?.value,
|
||||
};
|
||||
},
|
||||
)
|
||||
: property.valueType?.type === 'boolean'
|
||||
? [
|
||||
{ label: '是', value: true },
|
||||
{ label: '否', value: false },
|
||||
]
|
||||
: undefined
|
||||
"
|
||||
/>
|
||||
</j-form-item>
|
||||
</j-col>
|
||||
<j-col :span="24" v-if="modelRef.messageType === 'INVOKE_FUNCTION'">
|
||||
|
@ -40,7 +60,7 @@
|
|||
required: true,
|
||||
message: '请选择功能',
|
||||
}">
|
||||
<j-select placeholder="请选择功能" v-model:value="modelRef.message.functionId" show-search :filter-option="filterOption" @change="funcChange">
|
||||
<j-select placeholder="请选择功能" v-model:value="modelRef.message.functionId" show-search @change="funcChange">
|
||||
<j-select-option v-for="i in (metadata?.functions) || []" :key="i.id" :value="i.id" :label="i.name">{{i.name}}</j-select-option>
|
||||
</j-select>
|
||||
</j-form-item>
|
||||
|
@ -62,10 +82,6 @@ import EditTable from './EditTable.vue'
|
|||
|
||||
const formRef = ref();
|
||||
|
||||
const filterOption = (input: string, option: any) => {
|
||||
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0;
|
||||
};
|
||||
|
||||
const props = defineProps({
|
||||
actionType: {
|
||||
type: String,
|
||||
|
@ -89,10 +105,12 @@ const props = defineProps({
|
|||
type Emits = {
|
||||
(e: 'update:modelValue', data: any): void;
|
||||
};
|
||||
|
||||
const emit = defineEmits<Emits>();
|
||||
|
||||
const modelRef = computed({
|
||||
get: () => {
|
||||
onPropertyChange(props.modelValue?.message?.properties)
|
||||
return props.modelValue || {
|
||||
messageType: undefined,
|
||||
message: {
|
||||
|
@ -107,6 +125,8 @@ const modelRef = computed({
|
|||
}
|
||||
})
|
||||
|
||||
const property = ref<any>({})
|
||||
|
||||
const funcChange = (val: string) => {
|
||||
if(val){
|
||||
const arr = props.metadata?.functions.find((item: any) => item.id === val)?.inputs || []
|
||||
|
@ -122,6 +142,13 @@ const funcChange = (val: string) => {
|
|||
}
|
||||
}
|
||||
|
||||
const onPropertyChange = (val: string) => {
|
||||
if(val){
|
||||
const _item = props.metadata?.properties.find((item: any) => item.id === val)
|
||||
property.value = _item?.[0] || {}
|
||||
}
|
||||
}
|
||||
|
||||
const saveBtn = () => new Promise((resolve) => {
|
||||
formRef.value.validate()
|
||||
.then(() => {
|
||||
|
@ -139,4 +166,13 @@ const saveBtn = () => new Promise((resolve) => {
|
|||
|
||||
defineExpose({ saveBtn })
|
||||
|
||||
</script>
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
:deep(.ant-form-item){
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.other {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
</style>
|
|
@ -51,7 +51,6 @@
|
|||
placeholder="请选择产品"
|
||||
v-model:value="modelRef.id"
|
||||
show-search
|
||||
:filter-option="filterOption"
|
||||
@change="productChange"
|
||||
>
|
||||
<j-select-option
|
||||
|
@ -89,7 +88,6 @@
|
|||
placeholder="请选择设备类型"
|
||||
v-model:value="modelRef.applianceType"
|
||||
show-search
|
||||
:filter-option="filterOption"
|
||||
@change="typeChange"
|
||||
>
|
||||
<j-select-option
|
||||
|
@ -170,13 +168,10 @@
|
|||
item.action
|
||||
"
|
||||
show-search
|
||||
:filter-option="
|
||||
filterOption
|
||||
"
|
||||
>
|
||||
<j-select-option
|
||||
v-for="i in getTypesActions(
|
||||
item.action,
|
||||
item.action || ''
|
||||
)"
|
||||
:key="i.id"
|
||||
:value="i.id"
|
||||
|
@ -218,9 +213,6 @@
|
|||
item.actionType
|
||||
"
|
||||
show-search
|
||||
:filter-option="
|
||||
filterOption
|
||||
"
|
||||
>
|
||||
<j-select-option
|
||||
value="command"
|
||||
|
@ -261,6 +253,9 @@
|
|||
</j-row>
|
||||
</j-collapse-panel>
|
||||
</j-collapse>
|
||||
<j-card v-else>
|
||||
<j-empty />
|
||||
</j-card>
|
||||
</j-col>
|
||||
<j-col :span="24">
|
||||
<j-button
|
||||
|
@ -323,13 +318,10 @@
|
|||
item.source
|
||||
"
|
||||
show-search
|
||||
:filter-option="
|
||||
filterOption
|
||||
"
|
||||
>
|
||||
<j-select-option
|
||||
v-for="i in getDuerOSProperties(
|
||||
item.source,
|
||||
item.source || '',
|
||||
)"
|
||||
:key="i.id"
|
||||
:value="i.id"
|
||||
|
@ -361,16 +353,13 @@
|
|||
"
|
||||
mode="tags"
|
||||
show-search
|
||||
:filter-option="
|
||||
filterOption
|
||||
"
|
||||
>
|
||||
<j-select-option
|
||||
v-for="i in getProductProperties(
|
||||
item.target,
|
||||
)"
|
||||
:key="i.id"
|
||||
:value="item.id"
|
||||
:value="i.id"
|
||||
>{{
|
||||
i.name
|
||||
}}</j-select-option
|
||||
|
@ -381,6 +370,9 @@
|
|||
</j-row>
|
||||
</j-collapse-panel>
|
||||
</j-collapse>
|
||||
<j-card v-else>
|
||||
<j-empty />
|
||||
</j-card>
|
||||
</j-col>
|
||||
<j-col :span="24">
|
||||
<j-button
|
||||
|
@ -494,10 +486,6 @@ const onActionCollChange = (_key: string[]) => {
|
|||
actionActiveKey.value = _key;
|
||||
};
|
||||
|
||||
const filterOption = (input: string, option: any) => {
|
||||
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0;
|
||||
};
|
||||
|
||||
const addItem = () => {
|
||||
actionActiveKey.value.push(String(modelRef.actionMappings.length));
|
||||
modelRef.actionMappings.push({
|
||||
|
@ -636,8 +624,9 @@ const saveBtn = async () => {
|
|||
.then(async (data: any) => {
|
||||
if (tasks.every((item) => item) && data) {
|
||||
loading.value = true;
|
||||
const resp = await savePatch(data);
|
||||
loading.value = false;
|
||||
const resp = await savePatch(data).finally(() => {
|
||||
loading.value = false;
|
||||
})
|
||||
if (resp.status === 200) {
|
||||
message.success('操作成功!');
|
||||
formRef.value.resetFields();
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
selectedRowKeys: _selectedRowKeys,
|
||||
onChange: onSelectChange,
|
||||
}"
|
||||
@cancelSelect="cancelSelect"
|
||||
:params="params"
|
||||
>
|
||||
<template #headerTitle>
|
||||
|
@ -643,10 +642,6 @@ const onSelectChange = (keys: string[]) => {
|
|||
_selectedRowKeys.value = [...keys];
|
||||
};
|
||||
|
||||
const cancelSelect = () => {
|
||||
_selectedRowKeys.value = [];
|
||||
};
|
||||
|
||||
const handleClick = (dt: any) => {
|
||||
if (_selectedRowKeys.value.includes(dt.id)) {
|
||||
const _index = _selectedRowKeys.value.findIndex((i) => i === dt.id);
|
||||
|
|
|
@ -1,261 +1,278 @@
|
|||
<template>
|
||||
<Search
|
||||
:columns="columns"
|
||||
type='simple'
|
||||
@search="handleSearch"
|
||||
class='search'
|
||||
target="scene-triggrt-device-device"
|
||||
<j-advanced-search
|
||||
:columns="columns"
|
||||
type="simple"
|
||||
@search="handleSearch"
|
||||
class="search"
|
||||
target="scene-trigger-device-product"
|
||||
/>
|
||||
<a-divider style='margin: 0' />
|
||||
<a-divider style="margin: 0" />
|
||||
<j-pro-table
|
||||
ref='actionRef'
|
||||
model='CARD'
|
||||
:columns='columns'
|
||||
:params='params'
|
||||
:request='productQuery'
|
||||
:gridColumn='2'
|
||||
:gridColumns='[2,2,2]'
|
||||
:bodyStyle='{
|
||||
paddingRight: 0,
|
||||
paddingLeft: 0
|
||||
}'
|
||||
ref="actionRef"
|
||||
model="CARD"
|
||||
:columns="columns"
|
||||
:params="params"
|
||||
:request="productQuery"
|
||||
:gridColumn="2"
|
||||
:bodyStyle="{
|
||||
paddingRight: 0,
|
||||
paddingLeft: 0,
|
||||
}"
|
||||
>
|
||||
<template #card="slotProps">
|
||||
<CardBox
|
||||
:value='slotProps'
|
||||
:active="rowKey === slotProps.id"
|
||||
:status="slotProps.state"
|
||||
:statusText="slotProps.state === 1 ? '正常' : '禁用'"
|
||||
:statusNames="{ 1: 'success', 0: 'error', }"
|
||||
@click="handleClick"
|
||||
>
|
||||
<template #img>
|
||||
<slot name="img">
|
||||
<img width='88' height='88' :src="slotProps.photoUrl || getImage('/device-product.png')" />
|
||||
</slot>
|
||||
</template>
|
||||
<template #content>
|
||||
<div style='width: calc(100% - 100px)'>
|
||||
<Ellipsis>
|
||||
<span style="font-size: 16px;font-weight: 600" >
|
||||
{{ slotProps.name }}
|
||||
</span>
|
||||
</Ellipsis>
|
||||
</div>
|
||||
<a-row>
|
||||
<a-col :span="12">
|
||||
<div class="card-item-content-text">
|
||||
设备类型
|
||||
</div>
|
||||
<div>直连设备</div>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</template>
|
||||
</CardBox>
|
||||
</template>
|
||||
<template #card="slotProps">
|
||||
<CardBox
|
||||
:value="slotProps"
|
||||
:active="rowKey === slotProps.id"
|
||||
:status="String(slotProps.state)"
|
||||
:statusText="slotProps.state === 1 ? '正常' : '禁用'"
|
||||
:statusNames="{ '1': 'success', '0': 'error' }"
|
||||
@click="handleClick(slotProps)"
|
||||
>
|
||||
<template #img>
|
||||
<slot name="img">
|
||||
<img
|
||||
:width="88"
|
||||
:height="88"
|
||||
:src="
|
||||
slotProps.photoUrl ||
|
||||
getImage('/device-product.png')
|
||||
"
|
||||
/>
|
||||
</slot>
|
||||
</template>
|
||||
<template #content>
|
||||
<div style="width: calc(100% - 100px)">
|
||||
<Ellipsis>
|
||||
<span style="font-size: 16px; font-weight: 600">
|
||||
{{ slotProps.name }}
|
||||
</span>
|
||||
</Ellipsis>
|
||||
</div>
|
||||
<a-row>
|
||||
<a-col :span="12">
|
||||
<div class="card-item-content-text">设备类型</div>
|
||||
<Ellipsis>{{ slotProps.deviceType?.text }}</Ellipsis>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<div class="card-item-content-text">接入方式</div>
|
||||
<Ellipsis>{{ slotProps?.accessName || '未接入' }}</Ellipsis>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</template>
|
||||
</CardBox>
|
||||
</template>
|
||||
</j-pro-table>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<script setup lang='ts' name='Product'>
|
||||
import { getProviders, queryGatewayList, queryProductList } from '@/api/device/product'
|
||||
import { queryTree } from '@/api/device/category'
|
||||
import { getTreeData_api } from '@/api/system/department'
|
||||
import { isNoCommunity } from '@/utils/utils'
|
||||
import { getImage } from '@/utils/comm'
|
||||
|
||||
type Emit = {
|
||||
(e: 'update:rowKey', data: string): void
|
||||
(e: 'update:detail', data: string): void
|
||||
(e: 'change', data: string): void
|
||||
}
|
||||
|
||||
const actionRef = ref()
|
||||
const params = ref({})
|
||||
const props = defineProps({
|
||||
import {
|
||||
getProviders,
|
||||
queryGatewayList,
|
||||
queryProductList,
|
||||
} from '@/api/device/product';
|
||||
import { queryTree } from '@/api/device/category';
|
||||
import { getTreeData_api } from '@/api/system/department';
|
||||
import { isNoCommunity } from '@/utils/utils';
|
||||
import { getImage } from '@/utils/comm';
|
||||
|
||||
type Emit = {
|
||||
(e: 'update:rowKey', data: string): void;
|
||||
(e: 'update:detail', data: string): void;
|
||||
(e: 'change', data: string): void;
|
||||
};
|
||||
|
||||
const actionRef = ref();
|
||||
const params = ref({});
|
||||
const props = defineProps({
|
||||
rowKey: {
|
||||
type: String,
|
||||
default: ''
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
detail: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits<Emit>()
|
||||
|
||||
const columns = [
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits<Emit>();
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: 'ID',
|
||||
dataIndex: 'id',
|
||||
width: 300,
|
||||
ellipsis: true,
|
||||
fixed: 'left',
|
||||
search: {
|
||||
type: 'string',
|
||||
},
|
||||
title: 'ID',
|
||||
dataIndex: 'id',
|
||||
width: 300,
|
||||
ellipsis: true,
|
||||
fixed: 'left',
|
||||
search: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '名称',
|
||||
dataIndex: 'name',
|
||||
width: 200,
|
||||
ellipsis: true,
|
||||
search: {
|
||||
type: 'string',
|
||||
first: true
|
||||
}
|
||||
title: '名称',
|
||||
dataIndex: 'name',
|
||||
width: 200,
|
||||
ellipsis: true,
|
||||
search: {
|
||||
type: 'string',
|
||||
first: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '网关类型',
|
||||
dataIndex: 'accessProvider',
|
||||
width: 150,
|
||||
ellipsis: true,
|
||||
hideInTable: true,
|
||||
search: {
|
||||
type: 'select',
|
||||
options: () => getProviders().then((resp: any) => {
|
||||
if (isNoCommunity) {
|
||||
return (resp?.result || []).map((item: any) => ({
|
||||
label: item.name,
|
||||
value: item.id
|
||||
}))
|
||||
} else {
|
||||
return (resp?.result || []).filter((item: any) => [
|
||||
'mqtt-server-gateway',
|
||||
'http-server-gateway',
|
||||
'mqtt-client-gateway',
|
||||
'tcp-server-gateway',
|
||||
].includes(item.id))
|
||||
.map((item: any) => ({
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
}))
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '接入方式',
|
||||
dataIndex: 'accessName',
|
||||
width: 150,
|
||||
ellipsis: true,
|
||||
search: {
|
||||
type: 'select',
|
||||
options: () => queryGatewayList().then((resp: any) =>
|
||||
resp.result.map((item: any) => ({
|
||||
label: item.name, value: item.id
|
||||
}))
|
||||
)
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '设备类型',
|
||||
dataIndex: 'deviceType',
|
||||
width: 150,
|
||||
search: {
|
||||
type: 'select',
|
||||
options: [
|
||||
{ label: '直连设备', value: 'device' },
|
||||
{ label: '网关子设备', value: 'childrenDevice' },
|
||||
{ label: '网关设备', value: 'gateway' },
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'state',
|
||||
width: '90px',
|
||||
search: {
|
||||
type: 'select',
|
||||
options: [
|
||||
{ label: '禁用', value: 0 },
|
||||
{ label: '正常', value: 1 },
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '说明',
|
||||
dataIndex: 'describe',
|
||||
ellipsis: true,
|
||||
width: 300,
|
||||
},
|
||||
{
|
||||
dataIndex: 'classifiedId',
|
||||
title: '分类',
|
||||
hideInTable: true,
|
||||
search: {
|
||||
type: 'treeSelect',
|
||||
options: queryTree({ paging: false }).then(resp => resp.result),
|
||||
componentProps: {
|
||||
fieldNames: {
|
||||
label: 'name',
|
||||
value: 'id',
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
dataIndex: 'id$dim-assets',
|
||||
title: '所属组织',
|
||||
hideInTable: true,
|
||||
search: {
|
||||
type: 'treeSelect',
|
||||
options: getTreeData_api({ paging: false }).then((resp: any) => {
|
||||
const formatValue = (list: any[]) => {
|
||||
return list.map((item: any) => {
|
||||
if (item.children) {
|
||||
item.children = formatValue(item.children);
|
||||
}
|
||||
return {
|
||||
...item,
|
||||
value: JSON.stringify({
|
||||
assetType: 'product',
|
||||
targets: [
|
||||
{
|
||||
type: 'org',
|
||||
id: item.id,
|
||||
},
|
||||
],
|
||||
title: '网关类型',
|
||||
dataIndex: 'accessProvider',
|
||||
width: 150,
|
||||
ellipsis: true,
|
||||
hideInTable: true,
|
||||
search: {
|
||||
type: 'select',
|
||||
options: () =>
|
||||
getProviders().then((resp: any) => {
|
||||
if (isNoCommunity) {
|
||||
return (resp?.result || []).map((item: any) => ({
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
}));
|
||||
} else {
|
||||
return (resp?.result || [])
|
||||
.filter((item: any) =>
|
||||
[
|
||||
'mqtt-server-gateway',
|
||||
'http-server-gateway',
|
||||
'mqtt-client-gateway',
|
||||
'tcp-server-gateway',
|
||||
].includes(item.id),
|
||||
)
|
||||
.map((item: any) => ({
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
}));
|
||||
}
|
||||
}),
|
||||
}
|
||||
})
|
||||
}
|
||||
return formatValue(resp.result)
|
||||
}),
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
const handleSearch = (p: any) => {
|
||||
params.value = p
|
||||
}
|
||||
|
||||
const productQuery = (p: any) => {
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '接入方式',
|
||||
dataIndex: 'accessName',
|
||||
width: 150,
|
||||
ellipsis: true,
|
||||
search: {
|
||||
type: 'select',
|
||||
options: () =>
|
||||
queryGatewayList().then((resp: any) =>
|
||||
resp.result.map((item: any) => ({
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
})),
|
||||
),
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '设备类型',
|
||||
dataIndex: 'deviceType',
|
||||
width: 150,
|
||||
search: {
|
||||
type: 'select',
|
||||
options: [
|
||||
{ label: '直连设备', value: 'device' },
|
||||
{ label: '网关子设备', value: 'childrenDevice' },
|
||||
{ label: '网关设备', value: 'gateway' },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'state',
|
||||
width: '90px',
|
||||
search: {
|
||||
type: 'select',
|
||||
options: [
|
||||
{ label: '禁用', value: 0 },
|
||||
{ label: '正常', value: 1 },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '说明',
|
||||
dataIndex: 'describe',
|
||||
ellipsis: true,
|
||||
width: 300,
|
||||
},
|
||||
{
|
||||
dataIndex: 'classifiedId',
|
||||
title: '分类',
|
||||
hideInTable: true,
|
||||
search: {
|
||||
type: 'treeSelect',
|
||||
options: queryTree({ paging: false }).then((resp) => resp.result),
|
||||
componentProps: {
|
||||
fieldNames: {
|
||||
label: 'name',
|
||||
value: 'id',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
dataIndex: 'id$dim-assets',
|
||||
title: '所属组织',
|
||||
hideInTable: true,
|
||||
search: {
|
||||
type: 'treeSelect',
|
||||
options: getTreeData_api({ paging: false }).then((resp: any) => {
|
||||
const formatValue = (list: any[]) => {
|
||||
return list.map((item: any) => {
|
||||
if (item.children) {
|
||||
item.children = formatValue(item.children);
|
||||
}
|
||||
return {
|
||||
...item,
|
||||
value: JSON.stringify({
|
||||
assetType: 'product',
|
||||
targets: [
|
||||
{
|
||||
type: 'org',
|
||||
id: item.id,
|
||||
},
|
||||
],
|
||||
}),
|
||||
};
|
||||
});
|
||||
};
|
||||
return formatValue(resp.result);
|
||||
}),
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const handleSearch = (p: any) => {
|
||||
params.value = p;
|
||||
};
|
||||
|
||||
const productQuery = (p: any) => {
|
||||
const sorts: any = [];
|
||||
|
||||
|
||||
if (props.rowKey) {
|
||||
sorts.push({
|
||||
name: 'id',
|
||||
value: props.rowKey,
|
||||
});
|
||||
sorts.push({
|
||||
name: 'id',
|
||||
value: props.rowKey,
|
||||
});
|
||||
}
|
||||
sorts.push({ name: 'createTime', order: 'desc' });
|
||||
p.sorts = sorts
|
||||
return queryProductList(p)
|
||||
}
|
||||
|
||||
const handleClick = (detail: any) => {
|
||||
emit('update:rowKey', detail.id)
|
||||
emit('update:detail', detail)
|
||||
emit('change', detail)
|
||||
}
|
||||
|
||||
</script>
|
||||
p.sorts = sorts;
|
||||
return queryProductList(p);
|
||||
};
|
||||
|
||||
const handleClick = (detail: any) => {
|
||||
emit('update:rowKey', detail.id);
|
||||
emit('update:detail', detail);
|
||||
emit('change', detail);
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang='less'>
|
||||
.search {
|
||||
.search {
|
||||
margin-bottom: 0;
|
||||
padding-right: 0px;
|
||||
padding-left: 0px;
|
||||
}
|
||||
</style>
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,64 @@
|
|||
<template>
|
||||
<j-table
|
||||
rowKey="id"
|
||||
:columns="columns"
|
||||
:data-source="dataSource"
|
||||
bordered
|
||||
:pagination="false"
|
||||
>
|
||||
<template #bodyCell="{ column, text, record }">
|
||||
<div>
|
||||
<template v-if="['valueType', 'name'].includes(column.dataIndex)">
|
||||
<span>{{ text }}</span>
|
||||
</template>
|
||||
<template v-else>
|
||||
<j-input />
|
||||
<!-- TODO -->
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
</j-table>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { PropType } from "vue";
|
||||
|
||||
type Emits = {
|
||||
(e: 'update:modelValue', data: Record<string, any>[]): void;
|
||||
};
|
||||
const _emit = defineEmits<Emits>();
|
||||
|
||||
const _props = defineProps({
|
||||
modelValue: {
|
||||
type: Array as PropType<Record<string, any>[]>,
|
||||
default: '',
|
||||
}
|
||||
});
|
||||
const columns = [
|
||||
{
|
||||
title: '参数名称',
|
||||
dataIndex: 'name',
|
||||
with: '33%',
|
||||
},
|
||||
{
|
||||
title: '类型',
|
||||
dataIndex: 'valueType',
|
||||
with: '33%',
|
||||
},
|
||||
{
|
||||
title: '值',
|
||||
dataIndex: 'value',
|
||||
with: '34%',
|
||||
},
|
||||
];
|
||||
|
||||
const dataSource = computed({
|
||||
get: () => {
|
||||
return _props.modelValue || []
|
||||
},
|
||||
set: (val: any) => {
|
||||
_emit('update:modelValue', val);
|
||||
}
|
||||
})
|
||||
|
||||
</script>
|
|
@ -0,0 +1,215 @@
|
|||
<template>
|
||||
<div>
|
||||
<a-form :layout="'vertical'" ref="formRef" :model="modelRef">
|
||||
<j-form-item
|
||||
:name="['message', 'messageType']"
|
||||
label="动作类型"
|
||||
:rules="[{ required: true, message: '请选择动作类型' }]"
|
||||
>
|
||||
<TopCard
|
||||
:typeList="TypeList"
|
||||
v-model:value="modelRef.message.messageType"
|
||||
/>
|
||||
</j-form-item>
|
||||
<template v-if="deviceMessageType === 'INVOKE_FUNCTION'">
|
||||
<j-form-item
|
||||
:name="['message', 'functionId']"
|
||||
label="功能调用"
|
||||
:rules="[{ required: true, message: '请选择功能' }]"
|
||||
>
|
||||
<j-select
|
||||
showSearch
|
||||
placeholder="请选择功能"
|
||||
v-model:value="modelRef.message.functionId"
|
||||
@change="onFunctionChange"
|
||||
>
|
||||
<j-select-option
|
||||
v-for="item in metadata?.functions || []"
|
||||
:value="item?.id"
|
||||
:key="item?.id"
|
||||
>{{ item?.name }}</j-select-option
|
||||
>
|
||||
</j-select>
|
||||
</j-form-item>
|
||||
<j-form-item
|
||||
v-if="modelRef.message.functionId"
|
||||
:name="['message', 'inputs']"
|
||||
:rules="[{ required: true, message: '请输入功能值' }]"
|
||||
>
|
||||
<EditTable v-model="modelRef.message.inputs" />
|
||||
</j-form-item>
|
||||
</template>
|
||||
<template v-else-if="deviceMessageType === 'READ_PROPERTY'">
|
||||
<j-form-item
|
||||
:name="['message', 'properties']"
|
||||
label="读取属性"
|
||||
:rules="[{ required: true, message: '请选择读取属性' }]"
|
||||
>
|
||||
<j-select
|
||||
showSearch
|
||||
placeholder="请选择属性"
|
||||
v-model:value="modelRef.message.properties"
|
||||
>
|
||||
<j-select-option
|
||||
v-for="item in metadata?.properties || []"
|
||||
:value="item?.id"
|
||||
:key="item?.id"
|
||||
>{{ item?.name }}</j-select-option
|
||||
>
|
||||
</j-select>
|
||||
</j-form-item>
|
||||
</template>
|
||||
<template v-else-if="deviceMessageType === 'WRITE_PROPERTY'">
|
||||
<j-row>
|
||||
<j-col :span="11">
|
||||
<j-form-item
|
||||
:name="['message', 'properties']"
|
||||
label="读取属性"
|
||||
:rules="[
|
||||
{ required: true, message: '请选择读取属性' },
|
||||
]"
|
||||
>
|
||||
<j-select
|
||||
showSearch
|
||||
placeholder="请选择属性"
|
||||
v-model:value="modelRef.message.properties"
|
||||
>
|
||||
<j-select-option
|
||||
v-for="item in metadata?.properties || []"
|
||||
:value="item?.id"
|
||||
:key="item?.id"
|
||||
>{{ item?.name }}</j-select-option
|
||||
>
|
||||
</j-select>
|
||||
</j-form-item>
|
||||
</j-col>
|
||||
<j-col :span="2"></j-col>
|
||||
<j-col :span="11">
|
||||
<j-form-item
|
||||
:name="['message', 'properties']"
|
||||
label="读取属性"
|
||||
:rules="[
|
||||
{ required: true, message: '请选择读取属性' },
|
||||
]"
|
||||
>
|
||||
<j-select placeholder="请选择">
|
||||
<!-- TODO -->
|
||||
</j-select>
|
||||
</j-form-item>
|
||||
</j-col>
|
||||
</j-row>
|
||||
</template>
|
||||
</a-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { getImage } from '@/utils/comm';
|
||||
import TopCard from '../device/TopCard.vue';
|
||||
import { detail } from '@/api/device/instance';
|
||||
import EditTable from './EditTable.vue';
|
||||
|
||||
const TypeList = [
|
||||
{
|
||||
label: '功能调用',
|
||||
value: 'INVOKE_FUNCTION',
|
||||
image: getImage('/scene/invoke-function.png'),
|
||||
tip: '',
|
||||
},
|
||||
{
|
||||
label: '读取属性',
|
||||
value: 'READ_PROPERTY',
|
||||
image: getImage('/scene/read-property.png'),
|
||||
tip: '',
|
||||
},
|
||||
{
|
||||
label: '设置属性',
|
||||
value: 'WRITE_PROPERTY',
|
||||
image: getImage('/scene/write-property.png'),
|
||||
tip: '',
|
||||
},
|
||||
];
|
||||
|
||||
const props = defineProps({
|
||||
values: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
name: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
thenName: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
branchGroup: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
});
|
||||
|
||||
const formRef = ref();
|
||||
|
||||
const modelRef = reactive({
|
||||
message: {
|
||||
messageType: undefined,
|
||||
functionId: undefined,
|
||||
properties: undefined,
|
||||
inputs: [],
|
||||
},
|
||||
});
|
||||
|
||||
const metadata = ref<{
|
||||
functions: any[];
|
||||
properties: any[];
|
||||
}>({
|
||||
functions: [],
|
||||
properties: [],
|
||||
});
|
||||
|
||||
const deviceMessageType = computed(() => {
|
||||
return modelRef.message.messageType;
|
||||
});
|
||||
|
||||
const onFunctionChange = (val: string) => {
|
||||
const _item = (metadata.value?.functions || []).find((item: any) => {
|
||||
return val === item.id;
|
||||
});
|
||||
const list = (_item?.inputs || []).map((item: any) => {
|
||||
return {
|
||||
id: item.id,
|
||||
name: item.name,
|
||||
value: undefined,
|
||||
valueType: item?.valueType?.type,
|
||||
};
|
||||
});
|
||||
modelRef.message.inputs = list;
|
||||
};
|
||||
|
||||
watchEffect(() => {
|
||||
// console.log(props.values)
|
||||
// console.log(metadata.value)
|
||||
// Object.assign()
|
||||
});
|
||||
|
||||
watch(
|
||||
() => [props.values?.productDetail, props.values.deviceDetail],
|
||||
([newVal1, newVal2]) => {
|
||||
if (newVal1) {
|
||||
if (props.values?.selector === 'fixed') {
|
||||
detail(newVal2.id).then((resp) => {
|
||||
if (resp.status === 200) {
|
||||
metadata.value = JSON.parse(
|
||||
resp.result?.metadata || '{}',
|
||||
);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
metadata.value = JSON.parse(newVal1?.metadata || '{}');
|
||||
}
|
||||
}
|
||||
},
|
||||
{ deep: true, immediate: true },
|
||||
);
|
||||
</script>
|
|
@ -0,0 +1,186 @@
|
|||
<template>
|
||||
<!-- <j-advanced-search
|
||||
:columns="columns"
|
||||
type="simple"
|
||||
@search="handleSearch"
|
||||
class="search"
|
||||
target="scene-trigger-device-device"
|
||||
/> -->
|
||||
<a-divider style="margin: 0" />
|
||||
<j-pro-table
|
||||
ref="actionRef"
|
||||
model="CARD"
|
||||
:columns="columns"
|
||||
:params="params"
|
||||
:request="deviceQuery"
|
||||
:gridColumn="2"
|
||||
:bodyStyle="{
|
||||
paddingRight: 0,
|
||||
paddingLeft: 0,
|
||||
}"
|
||||
>
|
||||
<template #card="slotProps">
|
||||
<CardBox
|
||||
:value="slotProps"
|
||||
@click="handleClick"
|
||||
:active="value[0]?.value === slotProps.id"
|
||||
:status="slotProps.state?.value"
|
||||
:statusText="slotProps.state?.text"
|
||||
:statusNames="{
|
||||
online: 'success',
|
||||
offline: 'error',
|
||||
notActive: 'warning',
|
||||
}"
|
||||
>
|
||||
<template #img>
|
||||
<slot name="img">
|
||||
<img
|
||||
:src="getImage('/device/instance/device-card.png')"
|
||||
/>
|
||||
</slot>
|
||||
</template>
|
||||
<template #content>
|
||||
<div style="width: calc(100% - 100px)">
|
||||
<Ellipsis>
|
||||
<span style="font-size: 16px; font-weight: 600">
|
||||
{{ slotProps.name }}
|
||||
</span>
|
||||
</Ellipsis>
|
||||
</div>
|
||||
<j-row style="margin-top: 20px">
|
||||
<j-col :span="12">
|
||||
<div class="card-item-content-text">设备类型</div>
|
||||
<div>{{ slotProps.deviceType?.text }}</div>
|
||||
</j-col>
|
||||
<j-col :span="12">
|
||||
<div class="card-item-content-text">产品名称</div>
|
||||
<Ellipsis style="width: 100%">
|
||||
{{ slotProps.productName }}
|
||||
</Ellipsis>
|
||||
</j-col>
|
||||
</j-row>
|
||||
</template>
|
||||
</CardBox>
|
||||
</template>
|
||||
</j-pro-table>
|
||||
</template>
|
||||
|
||||
<script setup lang='ts' name='Product'>
|
||||
import { query } from '@/api/device/instance';
|
||||
import { getImage } from '@/utils/comm';
|
||||
import { PropType } from 'vue';
|
||||
|
||||
type Emit = {
|
||||
(e: 'update:value', data: any): void;
|
||||
(e: 'change', data: any): void;
|
||||
};
|
||||
|
||||
const actionRef = ref();
|
||||
const params = ref({
|
||||
terms: []
|
||||
});
|
||||
const props = defineProps({
|
||||
value: {
|
||||
type: Array as PropType<any>,
|
||||
default: []
|
||||
},
|
||||
detail: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
productId: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
});
|
||||
|
||||
const emit = defineEmits<Emit>();
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: 'ID',
|
||||
dataIndex: 'id',
|
||||
width: 300,
|
||||
search: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '设备名称',
|
||||
dataIndex: 'name',
|
||||
search: {
|
||||
type: 'string',
|
||||
first: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
dataIndex: 'createTime',
|
||||
key: 'createTime',
|
||||
scopedSlots: true,
|
||||
search: {
|
||||
type: 'date',
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'state',
|
||||
key: 'state',
|
||||
scopedSlots: true,
|
||||
search: {
|
||||
type: 'select',
|
||||
options: [
|
||||
{ label: '禁用', value: 'notActive' },
|
||||
{ label: '离线', value: 'offline' },
|
||||
{ label: '在线', value: 'online' },
|
||||
],
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const handleSearch = (p: any) => {
|
||||
params.value = {
|
||||
...p,
|
||||
terms: [
|
||||
...p.terms,
|
||||
{terms: [{ column: 'productId', value: props?.productId }]}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
const deviceQuery = (p: any) => {
|
||||
const sorts: any = [];
|
||||
if (props.value) {
|
||||
sorts.push({
|
||||
name: 'id',
|
||||
value: props.value,
|
||||
});
|
||||
}
|
||||
sorts.push({ name: 'createTime', order: 'desc' });
|
||||
p.sorts = sorts;
|
||||
return query(p);
|
||||
};
|
||||
|
||||
const handleClick = (detail: any) => {
|
||||
emit('update:value', [{ value: detail.id, name: detail.name }]);
|
||||
emit('change', detail);
|
||||
};
|
||||
|
||||
watchEffect(() => {
|
||||
params.value = {
|
||||
...params.value,
|
||||
terms: params.value?.terms ? [
|
||||
...(params.value.terms || []),
|
||||
{terms: [{ column: 'productId', value: props?.productId }]}
|
||||
] : [{terms: [{ column: 'productId', value: props?.productId }]}]
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang='less'>
|
||||
.search {
|
||||
margin-bottom: 0;
|
||||
padding-right: 0px;
|
||||
padding-left: 0px;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,206 @@
|
|||
<template>
|
||||
<div>
|
||||
<template v-for="(item, index) in tagList" :key="item.id">
|
||||
<j-row :gutter="24" style="margin-bottom: 12px">
|
||||
<j-col :span="4">
|
||||
<span v-if="index === 0" class="tagName">标签选择</span>
|
||||
<j-select
|
||||
:options="[
|
||||
{ label: '并且', value: 'and' },
|
||||
{ label: '或者', value: 'or' },
|
||||
]"
|
||||
v-else
|
||||
:value="item?.type"
|
||||
style="width: 100%"
|
||||
@select="(key) => onTypeSelect(key, index)"
|
||||
/>
|
||||
</j-col>
|
||||
<j-col :span="16">
|
||||
<j-row :gutter="24">
|
||||
<j-col flex="120px">
|
||||
<j-select
|
||||
style="width: 120px"
|
||||
:value="item.id"
|
||||
placeholder="请选择标签"
|
||||
:options="options"
|
||||
@select="
|
||||
(_, _data) => onTagSelect(_data, index)
|
||||
"
|
||||
/>
|
||||
</j-col>
|
||||
<j-col flex="auto">
|
||||
<ValueItem
|
||||
v-model:modelValue="item.value"
|
||||
:itemType="item.valueType"
|
||||
@change="onValueChange"
|
||||
:options="
|
||||
item.valueType === 'enum'
|
||||
? (item?.dataType?.elements || []).map(
|
||||
(i) => {
|
||||
return {
|
||||
label: i.text,
|
||||
value: i.value,
|
||||
};
|
||||
},
|
||||
)
|
||||
: item.valueType === 'boolean'
|
||||
? [
|
||||
{ label: '是', value: true },
|
||||
{ label: '否', value: false },
|
||||
]
|
||||
: undefined
|
||||
"
|
||||
/>
|
||||
</j-col>
|
||||
</j-row>
|
||||
</j-col>
|
||||
<j-col :span="4">
|
||||
<j-space>
|
||||
<j-button style="padding: 0 8px" @click="addItem">
|
||||
<AIcon type="PlusOutlined" />
|
||||
</j-button>
|
||||
<j-button
|
||||
danger
|
||||
v-if="tagData.length > 1"
|
||||
style="padding: 0 8px"
|
||||
@click="deleteItem(index)"
|
||||
>
|
||||
<AIcon type="DeleteOutlined" />
|
||||
</j-button>
|
||||
</j-space>
|
||||
</j-col>
|
||||
</j-row>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { PropType } from 'vue';
|
||||
|
||||
const props = defineProps({
|
||||
value: {
|
||||
type: Array as PropType<any>,
|
||||
default: () => [],
|
||||
},
|
||||
tagData: {
|
||||
type: Array as PropType<any[]>,
|
||||
default: () => [],
|
||||
},
|
||||
});
|
||||
|
||||
const emits = defineEmits(['update:value', 'change']);
|
||||
|
||||
const options = ref<any[]>([]);
|
||||
const tagList = ref<any[]>([]);
|
||||
|
||||
const handleItem = (data: any) => {
|
||||
return {
|
||||
...data,
|
||||
valueType: data.valueType ? data.valueType.type : '-',
|
||||
format: data.valueType ? data.valueType.format : undefined,
|
||||
options: data.valueType ? data.valueType.elements : undefined,
|
||||
value: data.value,
|
||||
};
|
||||
};
|
||||
|
||||
const addItem = () => {
|
||||
tagList.value.push({ type: 'and' });
|
||||
};
|
||||
|
||||
const deleteItem = (_index: number) => {
|
||||
tagList.value.splice(_index, 1);
|
||||
};
|
||||
|
||||
const onTypeSelect = (key: any, _index: number) => {
|
||||
const indexItem = tagList[_index];
|
||||
indexItem.type = key;
|
||||
tagList.value[_index] = indexItem;
|
||||
};
|
||||
|
||||
const onTagSelect = (_data: any, _index: number) => {
|
||||
const newList = [...unref(tagList)];
|
||||
const indexType = newList[_index].type;
|
||||
newList.splice(
|
||||
_index,
|
||||
1,
|
||||
handleItem({ ..._data, value: undefined, type: indexType }),
|
||||
);
|
||||
tagList.value = newList;
|
||||
};
|
||||
|
||||
watch(
|
||||
() => [props.tagData, props.value],
|
||||
([newTag, newVal]) => {
|
||||
options.value = newTag.map((item: any) => {
|
||||
return { label: item.name, value: item.id, ...item };
|
||||
});
|
||||
if (newVal && newVal[0] && newVal[0].name && newTag && newTag.length) {
|
||||
const names: string[] = [];
|
||||
const newTagList = newVal[0].value
|
||||
.filter((valueItem: any) => {
|
||||
return newTag.some(
|
||||
(item: any) => valueItem.column === item.id,
|
||||
);
|
||||
})
|
||||
.map((valueItem: any) => {
|
||||
const oldItem = newTag.find(
|
||||
(item: any) => item.id === valueItem.column,
|
||||
);
|
||||
if (oldItem) {
|
||||
names.push(oldItem.name);
|
||||
return {
|
||||
...handleItem(oldItem),
|
||||
value: valueItem.value,
|
||||
type: valueItem.type,
|
||||
};
|
||||
}
|
||||
return valueItem;
|
||||
});
|
||||
tagList.value = newTagList;
|
||||
} else {
|
||||
tagList.value = [{}];
|
||||
}
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
deep: true,
|
||||
},
|
||||
);
|
||||
|
||||
const onValueChange = () => {
|
||||
const newValue = tagList.value
|
||||
.filter((item) => !!item.value)
|
||||
.map((item: any) => {
|
||||
return {
|
||||
column: item.id,
|
||||
type: item.type,
|
||||
value: item.value,
|
||||
};
|
||||
});
|
||||
const arr = newValue
|
||||
.filter((item) => !!item.value)
|
||||
.map((item: any) => {
|
||||
return {
|
||||
column: item.name,
|
||||
type: item.type,
|
||||
value: item.value,
|
||||
};
|
||||
});
|
||||
emits('update:value', [{ value: newValue, name: '标签' }]);
|
||||
emits('change', [{ value: newValue, name: '标签' }], undefined);
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.tagName::before {
|
||||
position: relative;
|
||||
left: 70px;
|
||||
display: inline-block;
|
||||
margin-right: 4px;
|
||||
color: #ff4d4f;
|
||||
font-size: 14px;
|
||||
font-family: SimSun, sans-serif;
|
||||
line-height: 1;
|
||||
content: '*';
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,149 @@
|
|||
<template>
|
||||
<div class="trigger-way-warp" :class="{ disabled: disabled }">
|
||||
<div
|
||||
v-for="item in typeList"
|
||||
:key="item.value"
|
||||
class="trigger-way-item"
|
||||
:class="{
|
||||
active: _value === item.value,
|
||||
labelBottom: labelBottom,
|
||||
}"
|
||||
@click="onSelect(item.value)"
|
||||
>
|
||||
<div class="'way-item-title">
|
||||
<span class="way-item-label">{{ item.label }}</span>
|
||||
<j-popover v-if="item.tip">
|
||||
<AIcon
|
||||
type="QuestionCircleOutlined"
|
||||
class="way-item-icon"
|
||||
/>
|
||||
</j-popover>
|
||||
</div>
|
||||
<div class="way-item-image">
|
||||
<img :width="48" :src="item.image" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { PropType } from 'vue';
|
||||
|
||||
const props = defineProps({
|
||||
typeList: {
|
||||
type: Array as PropType<any>,
|
||||
default: () => [],
|
||||
},
|
||||
value: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
labelBottom: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
|
||||
const emits = defineEmits(['update:value', 'change']);
|
||||
const _value = ref(props?.value || '');
|
||||
|
||||
watch(
|
||||
() => props.value,
|
||||
(newValue) => {
|
||||
_value.value = newValue || ""
|
||||
},
|
||||
{ immediate: true, deep: true },
|
||||
);
|
||||
|
||||
const onSelect = (_type: string) => {
|
||||
if (!props.disabled) {
|
||||
_value.value = _type;
|
||||
emits('update:value', _type);
|
||||
emits('change', _type)
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.trigger-way-warp {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 16px 24px;
|
||||
width: 100%;
|
||||
|
||||
.trigger-way-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
width: 237px;
|
||||
padding: 12px 16px;
|
||||
border: 1px solid #e0e4e8;
|
||||
border-radius: 2px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
|
||||
.way-item-title {
|
||||
span {
|
||||
font-size: 16px;
|
||||
}
|
||||
.way-item-label {
|
||||
padding-right: 6px;
|
||||
color: rgba(#000, 0.64);
|
||||
}
|
||||
|
||||
.way-item-icon {
|
||||
color: rgba(#000, 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
.way-item-image {
|
||||
margin: 0 !important;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.way-item-image {
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
&.active {
|
||||
border-color: @primary-color-active;
|
||||
.way-item-image {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
&.labelBottom {
|
||||
flex-direction: column-reverse;
|
||||
grid-gap: 16px;
|
||||
gap: 0;
|
||||
align-items: center;
|
||||
width: auto;
|
||||
padding: 8px 16px;
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
.trigger-way-item {
|
||||
cursor: not-allowed;
|
||||
|
||||
&:hover {
|
||||
color: initial;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
&.active {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -1,14 +1,354 @@
|
|||
|
||||
<template>
|
||||
<div>
|
||||
|
||||
</div>
|
||||
<div>
|
||||
<a-form :layout="'vertical'" ref="formRef" :model="modelRef">
|
||||
<j-form-item
|
||||
name="selector"
|
||||
label="选择方式"
|
||||
v-show="!(list.length === 1)"
|
||||
:rules="[{ required: true, message: '请选择方式' }]"
|
||||
>
|
||||
<TopCard
|
||||
:typeList="list"
|
||||
v-model:value="modelRef.selector"
|
||||
@change="onSelectorChange"
|
||||
/>
|
||||
</j-form-item>
|
||||
<j-form-item
|
||||
v-if="modelRef.selector === 'fixed'"
|
||||
name="selectorValues"
|
||||
:rules="[{ required: true, message: '请选择设备' }]"
|
||||
>
|
||||
<Device
|
||||
:productId="values.productDetail.id"
|
||||
v-model:value="modelRef.selectorValues"
|
||||
@change="onDeviceChange"
|
||||
/>
|
||||
</j-form-item>
|
||||
<j-form-item
|
||||
v-else-if="modelRef.selector === 'relation'"
|
||||
label="关系"
|
||||
name="selectorValues"
|
||||
:rules="[{ required: true, message: '请选择关系' }]"
|
||||
>
|
||||
<j-select
|
||||
placeholder="请选择关系"
|
||||
v-model:value="modelRef.selectorValues"
|
||||
:options="relationList"
|
||||
show-search
|
||||
@change="onRelationChange"
|
||||
/>
|
||||
</j-form-item>
|
||||
<j-form-item
|
||||
v-else-if="modelRef.selector === 'tag'"
|
||||
name="selectorValues"
|
||||
:rules="[{ required: true, message: '请选择标签' }]"
|
||||
>
|
||||
<Tag
|
||||
v-model:value="modelRef.selectorValues"
|
||||
:tagData="tagList"
|
||||
@change="onTagChange"
|
||||
/>
|
||||
</j-form-item>
|
||||
<j-form-item
|
||||
v-else
|
||||
name="selectorValues"
|
||||
label="变量"
|
||||
:rules="[{ required: true, message: '请选择' }]"
|
||||
>
|
||||
<j-tree-select
|
||||
style="width: 100%; height: 100%"
|
||||
:tree-data="builtInList"
|
||||
v-model:value="modelRef.selectorValues"
|
||||
placeholder="请选择参数"
|
||||
@select="onVariableChange"
|
||||
>
|
||||
<template #title="{ name, description }">
|
||||
<a-space>
|
||||
{{ name }}
|
||||
<span style="color: grey; margin-left: 5px">{{
|
||||
description
|
||||
}}</span>
|
||||
</a-space>
|
||||
</template>
|
||||
</j-tree-select>
|
||||
</j-form-item>
|
||||
</a-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang='ts' name="Device">
|
||||
import { useSceneStore } from '@/store/scene';
|
||||
import TopCard from './TopCard.vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { queryBuiltInParams } from '@/api/rule-engine/scene';
|
||||
import { getImage } from '@/utils/comm';
|
||||
import NoticeApi from '@/api/notice/config';
|
||||
import Device from './Device.vue';
|
||||
import Tag from './Tag.vue';
|
||||
|
||||
const props = defineProps({
|
||||
values: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
name: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
thenName: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
branchGroup: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
parallel: {
|
||||
type: Boolean,
|
||||
},
|
||||
});
|
||||
|
||||
const emits = defineEmits(['save', 'cancel']);
|
||||
|
||||
const sceneStore = useSceneStore();
|
||||
const { data } = storeToRefs(sceneStore);
|
||||
|
||||
const formRef = ref();
|
||||
|
||||
const modelRef = reactive({
|
||||
selector: 'fixed',
|
||||
selectorValues: undefined,
|
||||
deviceId: '',
|
||||
source: '',
|
||||
relationName: '',
|
||||
upperKey: '',
|
||||
});
|
||||
|
||||
const list = ref<any[]>([]);
|
||||
const builtInList = ref<any[]>([]);
|
||||
const tagList = ref<any[]>([]);
|
||||
const relationList = ref<any[]>([]);
|
||||
|
||||
const TypeList = [
|
||||
{
|
||||
label: '自定义',
|
||||
value: 'fixed',
|
||||
image: getImage('/scene/device-custom.png'),
|
||||
tip: '自定义选择当前产品下的任意设备',
|
||||
},
|
||||
{
|
||||
label: '按关系',
|
||||
value: 'relation',
|
||||
image: getImage('/scene/device-relation.png'),
|
||||
tip: '选择与触发设备具有相同关系的设备',
|
||||
},
|
||||
{
|
||||
label: '按标签',
|
||||
value: 'tag',
|
||||
image: getImage('/scene/device-tag.png'),
|
||||
tip: '按标签选择产品下具有特定标签的设备',
|
||||
},
|
||||
{
|
||||
label: '按变量',
|
||||
value: 'variable',
|
||||
image: getImage('/scene/device-variable.png'),
|
||||
tip: '选择设备ID为上游变量值的设备',
|
||||
},
|
||||
];
|
||||
|
||||
const filterTree = (nodes: any[]) => {
|
||||
if (!nodes?.length) {
|
||||
return nodes;
|
||||
}
|
||||
return nodes.filter((it) => {
|
||||
if (
|
||||
it.children.find(
|
||||
(item: any) =>
|
||||
item.id.indexOf('deviceId' || 'device_id' || 'device_Id') >
|
||||
-1,
|
||||
) &&
|
||||
!it.children.find((item: any) => item.id.indexOf('boolean') > -1)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
};
|
||||
|
||||
const treeDataFilter = (arr: any[]) => {
|
||||
if (Array.isArray(arr) && arr.length) {
|
||||
const list: any[] = [];
|
||||
arr.map((item: any) => {
|
||||
if (item.children) {
|
||||
const children = treeDataFilter(item.children);
|
||||
if (children.length) {
|
||||
list.push({
|
||||
...item,
|
||||
title: item.name,
|
||||
value: item.id,
|
||||
disabled: true,
|
||||
children,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if (
|
||||
item.children.find(
|
||||
(item: any) =>
|
||||
item.id.indexOf(
|
||||
'deviceId' || 'device_id' || 'device_Id',
|
||||
) > -1,
|
||||
) &&
|
||||
!item.children.find(
|
||||
(item: any) => item.id.indexOf('bolaen') > -1,
|
||||
)
|
||||
) {
|
||||
list.push(item);
|
||||
}
|
||||
}
|
||||
});
|
||||
return list;
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
const sourceChangeEvent = async () => {
|
||||
const _params = {
|
||||
branch: props.thenName,
|
||||
branchGroup: props.branchGroup,
|
||||
action: props.name - 1,
|
||||
};
|
||||
const resp = await queryBuiltInParams(unref(data), _params);
|
||||
if (resp.status === 200) {
|
||||
// const array = filterTree(resp.result as any[]);
|
||||
//判断相同产品才有按变量
|
||||
// if (props.formProductId === DeviceModel.productId)// TODO
|
||||
console.log(array);
|
||||
const arr = treeDataFilter(resp.result as any[]);
|
||||
builtInList.value = array;
|
||||
}
|
||||
};
|
||||
|
||||
const queryRelationList = () => {
|
||||
NoticeApi.getRelationUsers({
|
||||
paging: false,
|
||||
sorts: [{ name: 'createTime', order: 'desc' }],
|
||||
terms: [{ termType: 'eq', column: 'objectTypeName', value: '设备' }],
|
||||
}).then((resp) => {
|
||||
if (resp.status === 200) {
|
||||
relationList.value = (resp.result as any[]).map((item) => {
|
||||
return {
|
||||
label: item.name,
|
||||
value: item.relation,
|
||||
};
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const filterType = async () => {
|
||||
const _list = TypeList.filter((item) => item.value === 'fixed');
|
||||
if (unref(data)?.trigger?.type === 'device') {
|
||||
//关系
|
||||
const res = await NoticeApi.getRelationUsers({
|
||||
paging: false,
|
||||
sorts: [{ name: 'createTime', order: 'desc' }],
|
||||
terms: [
|
||||
{ termType: 'eq', column: 'objectTypeName', value: '设备' },
|
||||
],
|
||||
});
|
||||
if (res.status === 200 && res.result.length !== 0) {
|
||||
const array = TypeList.filter((item) => item.value === 'relation');
|
||||
_list.push(...array);
|
||||
}
|
||||
//标签
|
||||
const tag = JSON.parse(
|
||||
props.values.productDetail?.metadata || '{}',
|
||||
)?.tags;
|
||||
if (tag && tag.length !== 0) {
|
||||
const array = TypeList.filter((item) => item.value === 'tag');
|
||||
_list.push(...array);
|
||||
}
|
||||
//变量
|
||||
if (
|
||||
builtInList.value.length !== 0 &&
|
||||
!props.parallel &&
|
||||
props.name !== 0
|
||||
) {
|
||||
const array = TypeList.filter((item) => item.value === 'variable');
|
||||
_list.push(...array);
|
||||
}
|
||||
list.value = _list;
|
||||
} else {
|
||||
if (
|
||||
builtInList.value.length !== 0 &&
|
||||
!props.parallel &&
|
||||
props.name !== 0
|
||||
) {
|
||||
const array = TypeList.filter((item) => item.value === 'variable');
|
||||
_list.push(...array);
|
||||
}
|
||||
list.value = _list;
|
||||
}
|
||||
};
|
||||
|
||||
const onSelectorChange = (val: string) => {
|
||||
if (val === 'relation') {
|
||||
queryRelationList();
|
||||
}
|
||||
};
|
||||
|
||||
const onDeviceChange = (_detail: any) => {
|
||||
if (_detail) {
|
||||
emits('save', modelRef, _detail);
|
||||
}
|
||||
};
|
||||
|
||||
const onRelationChange = (val: any, options: any) => {
|
||||
modelRef.deviceId = 'deviceId';
|
||||
modelRef.source = 'upper';
|
||||
modelRef.selectorValues = val;
|
||||
modelRef.upperKey = 'scene.deviceId';
|
||||
modelRef.relationName = options.label;
|
||||
emits('save', modelRef, {});
|
||||
};
|
||||
|
||||
const onTagChange = (val: any[], arr: any[]) => {
|
||||
if (val) {
|
||||
modelRef.deviceId = 'deviceId';
|
||||
modelRef.source = 'fixed';
|
||||
}
|
||||
if (arr) {
|
||||
tagList.value = arr;
|
||||
}
|
||||
};
|
||||
|
||||
const onVariableChange = (val: any, node: any) => {
|
||||
modelRef.deviceId = val;
|
||||
// modelRef.deviceDetail = node;
|
||||
modelRef.selectorValues = [{ value: val, name: node.description }] as any;
|
||||
};
|
||||
|
||||
watchEffect(() => {
|
||||
Object.assign(modelRef, props.values);
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.values.productDetail,
|
||||
async (newVal) => {
|
||||
await sourceChangeEvent();
|
||||
if (newVal) {
|
||||
const metadata = JSON.parse(newVal?.metadata || '{}');
|
||||
tagList.value = metadata?.tags || [];
|
||||
filterType();
|
||||
}
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
deep: true,
|
||||
},
|
||||
);
|
||||
</script>
|
||||
|
||||
<style scoped lang='less'>
|
||||
|
||||
</style>
|
|
@ -1,6 +1,13 @@
|
|||
<template>
|
||||
<j-modal title="执行动作" visible :width="860" @cancel="onCancel" @ok="save" :maskClosable="false">
|
||||
<j-steps :current='DeviceModel.current' @change='stepChange'>
|
||||
<j-modal
|
||||
title="执行动作"
|
||||
visible
|
||||
:width="860"
|
||||
@cancel="onCancel"
|
||||
@ok="save"
|
||||
:maskClosable="false"
|
||||
>
|
||||
<j-steps :current="current" @change="stepChange">
|
||||
<j-step>
|
||||
<template #title>选择产品</template>
|
||||
</j-step>
|
||||
|
@ -11,36 +18,82 @@
|
|||
<template #title>执行动作</template>
|
||||
</j-step>
|
||||
</j-steps>
|
||||
<j-divider style='margin-bottom: 10px;' />
|
||||
<div class='steps-content'>
|
||||
<Product v-if='DeviceModel.current === 0' v-model:rowKey='DeviceModel.productId'
|
||||
v-model:detail='DeviceModel.productDetail' />
|
||||
<j-divider style="margin-bottom: 10px" />
|
||||
<div class="steps-content">
|
||||
<Product
|
||||
v-if="current === 0"
|
||||
v-model:rowKey="DeviceModel.productId"
|
||||
v-model:detail="DeviceModel.productDetail"
|
||||
/>
|
||||
<Device
|
||||
v-else-if="current === 1"
|
||||
:name="name"
|
||||
:parallel="parallel"
|
||||
:branchGroup="branchGroup"
|
||||
:thenName="thenName"
|
||||
:values="DeviceModel"
|
||||
@save="onDeviceSave"
|
||||
/>
|
||||
<Action v-else-if="current === 2"
|
||||
:name="name"
|
||||
:branchGroup="branchGroup"
|
||||
:thenName="thenName"
|
||||
:values="DeviceModel"
|
||||
/>
|
||||
</div>
|
||||
<template #footer>
|
||||
<div class='steps-action'>
|
||||
<j-button v-if='DeviceModel.current === 0' @click='cancel'>取消</j-button>
|
||||
<j-button v-else @click='prev'>上一步</j-button>
|
||||
<j-button type='primary' v-if='DeviceModel.current < 2' @click='saveClick'>下一步</j-button>
|
||||
<j-button type='primary' v-else @click='saveClick'>确定</j-button>
|
||||
</div>
|
||||
</template>
|
||||
<div class="steps-action">
|
||||
<j-button v-if="current === 0" @click="onCancel">取消</j-button>
|
||||
<j-button v-else @click="prev">上一步</j-button>
|
||||
<j-button type="primary" v-if="current < 2" @click="saveClick"
|
||||
>下一步</j-button
|
||||
>
|
||||
<j-button type="primary" v-else @click="saveClick"
|
||||
>确定</j-button
|
||||
>
|
||||
</div>
|
||||
</template>
|
||||
</j-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { DeviceModelType } from './typings';
|
||||
import Product from './Product.vue';
|
||||
import Device from './device/index.vue';
|
||||
import Action from './actions/index.vue';
|
||||
import { onlyMessage } from '@/utils/comm';
|
||||
import { detail } from '@/api/device/product'
|
||||
|
||||
type Emit = {
|
||||
(e: 'cancel'): void
|
||||
(e: 'save', data: any, options: Record<string, any>): void
|
||||
}
|
||||
(e: 'cancel'): void;
|
||||
(e: 'save', data: any, options: Record<string, any>): void;
|
||||
};
|
||||
|
||||
const props = defineProps({
|
||||
value: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
name: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
thenName: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
branchGroup: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
parallel: {
|
||||
type: Boolean,
|
||||
},
|
||||
});
|
||||
|
||||
const current = ref<number>(0);
|
||||
|
||||
const DeviceModel = reactive<DeviceModelType>({
|
||||
steps: [],
|
||||
current: 0,
|
||||
productId: '',
|
||||
deviceId: '',
|
||||
productDetail: {},
|
||||
|
@ -58,38 +111,64 @@ const DeviceModel = reactive<DeviceModelType>({
|
|||
columns: [],
|
||||
actionName: '',
|
||||
tagList: [],
|
||||
})
|
||||
});
|
||||
|
||||
const emit = defineEmits<Emit>()
|
||||
const cancel = () => {
|
||||
const emit = defineEmits<Emit>();
|
||||
|
||||
const onCancel = () => {
|
||||
emit('cancel');
|
||||
};
|
||||
const save = async(step?: number) => {
|
||||
let _step = step !== undefined ? step : DeviceModel.current
|
||||
if (_step === 0) {
|
||||
DeviceModel.productId ? DeviceModel.current = 1 : onlyMessage('请选择产品', 'error')
|
||||
} else if (_step === 1) {
|
||||
|
||||
} else {
|
||||
|
||||
|
||||
const save = async (step?: number) => {
|
||||
let _step = step !== undefined ? step : current.value;
|
||||
if (_step === 0) {
|
||||
DeviceModel.productId
|
||||
? (current.value = 1)
|
||||
: onlyMessage('请选择产品', 'error');
|
||||
} else if (_step === 1) {
|
||||
DeviceModel.deviceId
|
||||
? (current.value = 2)
|
||||
: onlyMessage('请选择设备', 'error');
|
||||
} else {
|
||||
}
|
||||
};
|
||||
|
||||
const stepChange = (step: number) => {
|
||||
if (step !== 0) {
|
||||
save(step - 1)
|
||||
save(step - 1);
|
||||
} else {
|
||||
DeviceModel.current = 0
|
||||
current.value = 0;
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
const prev = () => {
|
||||
DeviceModel.current = DeviceModel.current - 1
|
||||
}
|
||||
const saveClick = () => save()
|
||||
current.value -= 1;
|
||||
};
|
||||
|
||||
const saveClick = () => save();
|
||||
|
||||
const onDeviceSave = (_data: any, _detail: any) => {
|
||||
Object.assign(DeviceModel, _data)
|
||||
DeviceModel.deviceId = _detail.id
|
||||
DeviceModel.deviceDetail = _detail
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.value,
|
||||
(newValue) => {
|
||||
Object.assign(DeviceModel, newValue);
|
||||
if(newValue?.productId){
|
||||
detail(newValue.productId).then(resp => {
|
||||
if(resp.status === 200){
|
||||
DeviceModel.productDetail = resp.result
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
{ immediate: true, deep: true },
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.steps-steps {
|
||||
width: 100%;
|
||||
|
@ -100,5 +179,8 @@ const saveClick = () => save()
|
|||
|
||||
.steps-content {
|
||||
width: 100%;
|
||||
max-height: 500px;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
</style>
|
|
@ -2,12 +2,12 @@ import { ProductItem } from '@/views/device/Product/typings';
|
|||
import { ActionsDeviceProps } from '../../../typings';
|
||||
|
||||
type DeviceModelType = {
|
||||
steps: {
|
||||
key: string;
|
||||
title: string;
|
||||
content: React.ReactNode;
|
||||
}[];
|
||||
current: number;
|
||||
// steps: {
|
||||
// key: string;
|
||||
// title: string;
|
||||
// content: React.ReactNode;
|
||||
// }[];
|
||||
// current: number;
|
||||
productId: string;
|
||||
deviceId: string;
|
||||
productDetail: ProductItem | any;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<div>
|
||||
<template v-if="actionType === 'device'">
|
||||
<Device v-bind="props" :value="data?.device" @cancel="onCancel" @save="onPropsOk" />
|
||||
<Device v-bind="props" :value="data?.device" @cancel="onCancel" @save="onPropsOk" :thenName="branchesName" />
|
||||
</template>
|
||||
<template v-else-if="actionType === 'notify'">
|
||||
<Notify v-bind="props" :value="data?.notify" @cancel="onCancel" @save="onPropsOk" />
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
<template #title="{ name, description }">
|
||||
<a-space>
|
||||
{{ name }}
|
||||
<spn style="color: grey; margin-left: 5px">{{ description }}</spn>
|
||||
<span style="color: grey; margin-left: 5px">{{ description }}</span>
|
||||
</a-space>
|
||||
</template>
|
||||
</a-tree-select>
|
||||
|
|
Loading…
Reference in New Issue