258 lines
6.6 KiB
Vue
258 lines
6.6 KiB
Vue
<template>
|
||
<j-modal
|
||
title="选择处理方式"
|
||
visible
|
||
width="900px"
|
||
okText="确定"
|
||
cancelText="取消"
|
||
:confirmLoading='loading'
|
||
@ok="submitData"
|
||
@cancel="cancel"
|
||
>
|
||
<div class='tip'>
|
||
<a-icon type='ExclamationCircleOutlined'/>
|
||
平台
|
||
<span style='font-weight: bold;padding:0 4px;'>物模型</span>
|
||
中已有数据,请选择处理方式。
|
||
<j-tooltip title='默认采用覆盖的方式处理功能、事件、标签下的数据'>
|
||
<a-icon type='QuestionCircleOutlined' />
|
||
</j-tooltip>
|
||
</div>
|
||
<j-form :layout="'vertical'" ref='formRef' :model='handleData'>
|
||
<j-form-item label='处理方式' :rules='[{ required: true, message: "请选择处理方式"}]' >
|
||
<j-card-select
|
||
v-model:value="handleData.type"
|
||
:column='4'
|
||
:options="options"
|
||
>
|
||
<template #image='{image}'>
|
||
<img :src='image' />
|
||
</template>
|
||
</j-card-select>
|
||
</j-form-item>
|
||
</j-form>
|
||
</j-modal>
|
||
</template>
|
||
|
||
<script lang='ts' setup name='MetadataModal'>
|
||
import { useProductStore } from '@/store/product';
|
||
import { getImage, onlyMessage } from '@/utils/comm'
|
||
import { storeToRefs } from 'pinia'
|
||
import { modify, updateDevice } from '@/api/device/product'
|
||
import { savePluginData } from '@/api/link/plugin'
|
||
|
||
type Emit = {
|
||
(e: 'submit'): void
|
||
(e: 'cancel'): void
|
||
}
|
||
|
||
const emit = defineEmits<Emit>()
|
||
|
||
const props = defineProps({
|
||
metadata: {
|
||
type: Object,
|
||
default: () => ({})
|
||
},
|
||
access: {
|
||
type: Object,
|
||
default: () => ({})
|
||
},
|
||
data: {
|
||
type: Object,
|
||
default: () => ({})
|
||
}
|
||
})
|
||
|
||
const productStore = useProductStore();
|
||
const { current: productDetail } = storeToRefs(productStore)
|
||
const formRef = ref()
|
||
const handleData = reactive({
|
||
type: undefined
|
||
})
|
||
const loading = ref(false)
|
||
const options = [
|
||
{
|
||
value: 'intersection',
|
||
label: '取交集',
|
||
subLabel: '仅保留标识一致的属性',
|
||
iconUrl: getImage('/device/intersection.png'),
|
||
},
|
||
{
|
||
value: 'union',
|
||
label: '取并集',
|
||
subLabel: '保留平台、插件中的所有属性',
|
||
iconUrl: getImage('/device/union.png'),
|
||
},
|
||
{
|
||
value: 'ignore',
|
||
label: '忽略',
|
||
subLabel: '仅保留平台中的属性',
|
||
iconUrl: getImage('/device/ignore.png'),
|
||
},
|
||
{
|
||
value: 'cover',
|
||
label: '覆盖',
|
||
subLabel: '仅保留插件中的属性',
|
||
iconUrl: getImage('/device/cover.png'),
|
||
}
|
||
]
|
||
|
||
|
||
const flatObj = (obj: any, result: any) => {
|
||
Object.keys(obj).forEach((key: string) => {
|
||
if (typeof obj[key] === 'string') {
|
||
result[key] = obj[key];
|
||
} else {
|
||
flatObj(obj[key], result);
|
||
}
|
||
});
|
||
};
|
||
|
||
|
||
const updateAccessData = async (id: string, values: any, metadata: string) => {
|
||
const result: any = {};
|
||
flatObj(values, result);
|
||
const { storePolicy, ...extra } = result;
|
||
// 更新选择设备(设备接入)
|
||
const accessObj = {
|
||
...productDetail.value,
|
||
metadata: JSON.stringify(metadata),
|
||
transportProtocol: props.access?.transport,
|
||
protocolName: props.access?.protocolDetail?.name,
|
||
accessId: props.access?.id,
|
||
accessName: props.access?.name,
|
||
accessProvider: props.access?.provider,
|
||
messageProtocol: props.access?.protocol,
|
||
}
|
||
loading.value = true
|
||
const updateDeviceResp = await updateDevice(accessObj)
|
||
|
||
if (!updateDeviceResp.success) {
|
||
loading.value = false
|
||
return
|
||
}
|
||
|
||
if (props.access?.provider === 'plugin_gateway' && props.data.productTypeId) {
|
||
await savePluginData(
|
||
'product',
|
||
props.access.id,
|
||
props.data.id,
|
||
props.data.productTypeId
|
||
).catch(() => ({}))
|
||
}
|
||
|
||
// 更新产品配置信息
|
||
const resp = await modify(id || '', {
|
||
id: id,
|
||
configuration: { ...extra },
|
||
storePolicy: storePolicy,
|
||
});
|
||
loading.value = false
|
||
if (resp.status === 200) {
|
||
onlyMessage('操作成功!');
|
||
productStore.current!.storePolicy = storePolicy;
|
||
if ((window as any).onTabSaveSuccess) {
|
||
if (resp.result) {
|
||
(window as any).onTabSaveSuccess(resp);
|
||
setTimeout(() => window.close(), 300);
|
||
}
|
||
} else {
|
||
await productStore.getDetail(productDetail.value.id)
|
||
emit('submit')
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
const submitData = () => {
|
||
formRef.value.validate().then((res) => {
|
||
if (res) {
|
||
let metadata = JSON.parse(productDetail.value?.metadata || '{}') // 产品物模型
|
||
switch (handleData.type![0]) {
|
||
case 'intersection': // 交集
|
||
metadata.properties = IntersectionFn(metadata.properties, props.metadata.properties)
|
||
metadata.events = IntersectionFn(metadata.events, props.metadata.events)
|
||
metadata.functions = IntersectionFn(metadata.functions, props.metadata.functions)
|
||
metadata.tags = IntersectionFn(metadata.tags, props.metadata.tags)
|
||
break;
|
||
case 'union': // 并集
|
||
metadata.properties = UnionFn(metadata.properties, props.metadata.properties)
|
||
metadata.functions = UnionFn(metadata.functions, props.metadata.functions)
|
||
metadata.events = UnionFn(metadata.events, props.metadata.events)
|
||
metadata.tags = UnionFn(metadata.tags, props.metadata.tags)
|
||
break;
|
||
case 'cover': // 覆盖
|
||
metadata = props.metadata
|
||
break;
|
||
default:
|
||
break
|
||
}
|
||
updateAccessData(
|
||
props.data.id,
|
||
props.data.values,
|
||
metadata
|
||
)
|
||
}
|
||
}).catch(() => {
|
||
|
||
})
|
||
}
|
||
|
||
const cancel = () => {
|
||
emit('cancel')
|
||
}
|
||
|
||
/**
|
||
* 交集处理函数, 只保留来自插件中的属性
|
||
* @param DataA 产品物模型
|
||
* @param DataB 插件物模型
|
||
* @constructor
|
||
*/
|
||
const IntersectionFn = (DataA: any[] = [], DataB: any[] = []): any[] => {
|
||
const newData: any[] = []
|
||
if (!DataA.length) return []
|
||
DataB.forEach((item) => {
|
||
console.log(item, item.id)
|
||
if (DataA.some((aItem) => aItem.id === item.id)) {
|
||
newData.push(item)
|
||
}
|
||
})
|
||
return newData
|
||
}
|
||
|
||
/**
|
||
* 并集函数处理,保留平台、插件中的所有属性,ID重复时,只保留来自插件中的1条属性。
|
||
* @param DataA 产品物模型
|
||
* @param DataB 插件物模型
|
||
* @constructor
|
||
*/
|
||
const UnionFn = (DataA: any[] = [], DataB: any[] = []): any[] => {
|
||
const dataMap = new Map()
|
||
|
||
DataB.forEach((item) => {
|
||
dataMap.set(item.id, item)
|
||
})
|
||
|
||
DataA.forEach((item) => {
|
||
if (!dataMap.has(item.id)) {
|
||
dataMap.set(item.id, item)
|
||
}
|
||
})
|
||
console.log(DataA, DataB, [...dataMap.values()])
|
||
return [...dataMap.values()]
|
||
}
|
||
|
||
|
||
</script>
|
||
|
||
<style scoped lang='less'>
|
||
.tip {
|
||
background: #F6F6F6;
|
||
color: #999;
|
||
padding: 10px 26px;
|
||
margin-bottom: 24px;
|
||
}
|
||
:deep(.j-card-item) {
|
||
padding: 16px !important;
|
||
}
|
||
</style> |